2018年6月21日木曜日

[VX2] Movidius Neural Compute Stick (Myriad2) NCSDK2の導入方法

Movidius Neural Compute Stickは、VPUと呼ばれる、ディープラーニング推論のためのアクセラレータです。
本製品は、Movidius Myriad2 VPUを使用したもので、開発用としてUSBスティックタイプのものが用意されています。

今回の記事は、本製品のSDKである、NCSDKの最新バージョンである、V2.04をOpenBlocks IoT VX2(VX1)で利用するためのTipsとなります。
( 2018/7/11 時点では、2.0.5がリリースされており、Bug Fixに記述がありませんが、アプリケーション実行後の再起動時にdevice Stalledの状態になった場合、デバイスが復帰するようになりました。
2018/7/15 追記 2.05においては、別の致命的なエラーが出ることを確認しています。現状では1.x系を使った方がいいかもしれません。
)

Intel® Movidius | Neural Compute Stick | AI Programming



OBDN技術ブログによる動作検証は、該当するデバイスやソフトウェアの動作について、保証およびサポートを行うものではありません。
内容に関するご指摘などありましたら、ブログ記事の担当までご連絡下さい。

<検証環境>
OpenBlocks IoT VX2 FW3.1



1. ncsdk2のインストール

ncsdk v2のブランチを利用するには、以下のように指定します。

git clone -b ncsdk2 https://github.com/movidius/ncsdk.git

先に、ncsdkディレクトリ下で

./install.sh

とするのですが、NCSDKは、Ubuntu16.04またはRasbian9に対応していますが、debian9などの他のディストリビューションには対応していません。
そのため、今回はRasbian9と認識させてインストールさせます。
install.shの以下の部分(30行目以降)に変更を加えます。

#    OS_DISTRO="${DISTRO:-INVALID}"
#    OS_VERSION="${VERSION:-255}"
    OS_DISTRO="raspbian"
    OS_VERSION="91"

このスクリプトが終了したら、api/src の下で、

make install

として、sdkをインストールしてください。

・tensorflowについて

インストールは一通り終わりますが、インストールされたpython用のtensorflowはno gpuバージョンであっても、現在配布されているバージョンのモジュールはAVX命令をenableとしてビルドしているため、そのままでは使用できません。
tensorflowは別途SSE2のみでビルドする必要があります。
(VX2でセルフコンパイルをするのはリソース的に厳しいため、他のdebian/amd64のPCなどでwhlファイルを作成することをおすすめします)

bazelを使ったインストールはtensorflowのサイトのfrom Sourcesに従って行います。

Installing TensorFlow from Sources

bazelをAPT repositoryよりインストールした場合は、0.14.1がインストールされますが、tensorflowのサイトサンプルのようにgit checkout r1.0のように指定すると、古いリリースのものがcheckoutされてbazelの古いバージョンのものが必要となります。

git clone https://github.com/tensorflow/tensorflow
cd tensorflow
git checkout v1.9.0

tensorflow-hubなども使えるよう、新しいリリースのものをcheckoutします。
bazelのオプションは今回は以下のように設定しました。

bazel build -c opt --copt=-msse4.2 //tensorflow/tools/pip_package:build_pip_package

ビルド、パッケージ作成、インストールが終わったら、以下のスクリプトがエラーなく動作することを確認してください。

# Python3
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

ここで、warningが出る場合は以下のモジュールアップデートが必要です。

pip3 install --upgrade h5py

・OpenCVについて

opencvもそのままでは不完全にインストールされるため、別途ソースコードからインストールしなおすか、暫定でpython対応だけでよいならば、

pip3 install opencv-python

としてインストールしてください。

opencvをビルドする場合のcmakeのオプションのサンプルは以下の通りです。

cmake -G "Unix Makefiles" --build . -D BUILD_CUDA_STUBS=OFF -D BUILD_DOCS=OFF \
-D BUILD_EXAMPLES=OFF -D BUILD_JASPER=OFF -D BUILD_JPEG=OFF -D BUILD_OPENEXR=OFF \
-D BUILD_PACKAGE=ON -D BUILD_PERF_TESTS=OFF -D BUILD_PNG=OFF -D BUILD_SHARED_LIBS=ON \
-D BUILD_TBB=OFF -D BUILD_TESTS=OFF -D BUILD_TIFF=OFF -D BUILD_WITH_DEBUG_INFO=ON \
-D BUILD_ZLIB=OFF -D BUILD_WEBP=OFF -D BUILD_opencv_apps=ON -D BUILD_opencv_calib3d=ON \
-D BUILD_opencv_core=ON -D BUILD_opencv_cudaarithm=OFF -D BUILD_opencv_cudabgsegm=OFF \
-D BUILD_opencv_cudacodec=OFF -D BUILD_opencv_cudafeatures2d=OFF -D BUILD_opencv_cudafilters=OFF \
-D BUILD_opencv_cudaimgproc=OFF -D BUILD_opencv_cudalegacy=OFF -D BUILD_opencv_cudaobjdetect=OFF \
-D BUILD_opencv_cudaoptflow=OFF -D BUILD_opencv_cudastereo=OFF -D BUILD_opencv_cudawarping=OFF \
-D BUILD_opencv_cudev=OFF -D BUILD_opencv_features2d=ON -D BUILD_opencv_flann=ON \
-D BUILD_opencv_highgui=ON -D BUILD_opencv_imgcodecs=ON -D BUILD_opencv_imgproc=ON \
-D BUILD_opencv_java=OFF -D BUILD_opencv_ml=ON -D BUILD_opencv_objdetect=ON \
-D BUILD_opencv_photo=ON -D BUILD_opencv_python2=OFF -D BUILD_opencv_python3=ON \
-D BUILD_opencv_shape=ON -D BUILD_opencv_stitching=ON -D BUILD_opencv_superres=ON \
-D BUILD_opencv_ts=ON -D BUILD_opencv_video=ON -D BUILD_opencv_videoio=ON \
-D BUILD_opencv_videostab=ON -D BUILD_opencv_viz=OFF -D BUILD_opencv_world=OFF \
-D CMAKE_BUILD_TYPE=RELEASE -D WITH_1394=ON -D WITH_CUBLAS=OFF -D WITH_CUDA=OFF \
-D WITH_CUFFT=OFF -D WITH_EIGEN=ON -D WITH_FFMPEG=ON -D WITH_GDAL=OFF -D WITH_GPHOTO2=OFF \
-D WITH_GIGEAPI=ON -D WITH_GSTREAMER=OFF -D WITH_GTK=ON -D WITH_INTELPERC=OFF -D WITH_IPP=ON \
-D WITH_IPP_A=OFF -D WITH_JASPER=ON -D WITH_JPEG=ON -D WITH_LIBV4L=ON -D WITH_OPENCL=ON \
-D WITH_OPENCLAMDBLAS=OFF -D WITH_OPENCLAMDFFT=OFF -D WITH_OPENCL_SVM=OFF -D WITH_OPENEXR=ON \
-D WITH_OPENGL=ON -D WITH_OPENMP=OFF -D WITH_OPENNI=OFF -D WITH_PNG=ON -D WITH_PTHREADS_PF=OFF \
-D WITH_PVAPI=OFF -D WITH_QT=ON -D WITH_TBB=ON -D WITH_TIFF=ON -D WITH_UNICAP=OFF \
-D WITH_V4L=ON -D WITH_VTK=OFF -D WITH_WEBP=ON -D WITH_XIMEA=OFF -D WITH_XINE=OFF \
-D WITH_LAPACKE=ON -D WITH_MATLAB=OFF ..


・NCSの動作確認

NCSデバイスを認識すると、hello_ncs.pyの結果が以下のように表示されます。

#cd ~/ncsdk/examples/apps/hello_ncs_py
# python3 hello_ncs.py
D: [         0] ncDeviceCreate:221      ncDeviceCreate index 0

D: [         0] ncDeviceCreate:221      ncDeviceCreate index 1

D: [         0] ncDeviceOpen:415        File path /usr/local/lib/mvnc/MvNCAPI-ma2450.mvcmd

I: [         0] ncDeviceOpen:421        ncDeviceOpen() XLinkBootRemote returned success 0

I: [         0] ncDeviceOpen:450        XLinkConnect done - link Id 0

D: [         0] ncDeviceOpen:464        done

I: [         0] ncDeviceOpen:466        Booted 1.1-ma2450 -> VSC

I: [         0] getDevAttributes:287    Device attributes

I: [         0] getDevAttributes:290    Device FW version: 2.4.2450.e4

I: [         0] getDevAttributes:292    mvTensorVersion 2.4

I: [         0] getDevAttributes:293    Maximum graphs: 10

I: [         0] getDevAttributes:294    Maximum fifos: 20

I: [         0] getDevAttributes:296    Maximum graph option class: 1

I: [         0] getDevAttributes:298    Maximum device option class: 1

I: [         0] getDevAttributes:299    Device memory capacity: 522081584

Hello NCS! Device opened normally.
I: [         0] ncDeviceClose:656       closing device

Goodbye NCS! Device closed normally.

NCS device working.



2.ncappzooのv2用ブランチの入手

ncappzooもv2用のブランチが用意されています。
以下のオプションにてgit cloneしてください。

git clone -b ncsdk2 https://github.com/movidius/ncappzoo.git

READMEに従って必要なgraphファイルを作成するとともに、gstreamerなど、要求されるモジュールは別途インストールしてください。

また、opencvは、linuxで利用する場合は、ディスプレイとしてXを使います。
OpenBlocks IoT VX2(VX1)にはディスプレイヘッドはありませんので、Xサーバを別に用意する必要があります。


stream_inferの実行例
( 液晶モニタがmonitorとして認識されている )


3.最後に

ストレージ(eMMC)の制約から、VX2でなくVX1を使う場合は、外付けのUSB HDDなどを使用して環境のビルドを行ってください。
また、セルフビルドを行う場合、以下のようにTMPDIRの設定が必要な場合があります。

export TMPDIR=/var/tmp

なお、本SDKおよびサンプルアプリケーションであるncappzooをインストールしたdockerコンテナを作成しておりますので、別途公開する予定です。

2018年4月25日水曜日

OpenBlocks IoT FW3.xシリーズで下流方向の制御を行うハンドラの設定とテンプレート (C言語)

OpenBlocks IoT VX2およびVX1では、基本ソフトウェアとしてFW3.xが用意されます。
FW3.xシリーズでは、2.xシリーズの各機能に加え、以下のようなアップデートが行われています。

・メッセージの完全な双方向化
・Dockerに対応。エッジ処理モジュールをDockerコンテナで提供可能
・エッジ処理のみをNode-REDでも記述可能
・Luaによるデバイスハンドラ記述が可能
・Node-REDの最新環境を提供し、カスタマイズを容易に

Firmware 3.0 Architecture スタック

今回は、FW3.xで実装された、メッセージの双方向化における、下流方向のデータを用いたデバイスハンドラについて、まずは、C言語による記述と、設定方法について簡単に解説します。

メッセージの完全な双方向化に関しての詳細な情報は以下のドキュメントをご覧ください。

OpenBlocks IoT Family データハンドリングガイド

OpenBlocks IoT Family データハンドリング設定リファレンスガイド

先にご理解いただきたいのですが、メッセージの双方向化に関しては、現在は、
MQTT系の接続サービス、TCP接続、当社PD Exchangeとの接続、当社独自仕様のWebサーバーのみでご利用いただけます。対応の詳細に関しましては、上記ドキュメントを参照してください。

OBDN技術ブログによる動作検証は、該当するデバイスやソフトウェアの動作について、保証およびサポートを行うものではありません。
内容に関するご指摘などありましたら、ブログ記事の担当までご連絡下さい。


1.Userデバイスの作成と送受信設定


Userデバイスの作成
 サービスメニューより、基本機能、Userデバイスタブ、の順に選択します。
ユーザーメモを入力し、保存すると、一覧に作成したUserデバイスが表示されます。

送受信設定
サービスメニューより、IoTデータ、送受信設定の順に選択します。
今回は、例としてAzure IoT Hubへの送受信設定を行っています。


 上記、送受信の設定を行った後、Userデバイスに対してのデバイス設定を行います。
デバイス設定メニュー内の送受信設定にて、iothubにチェックを入れ、接続に必要なデバイスIDおよびデバイスキーを入力します。
なお、Azure IoT Hubへの接続方法に関しては、以下のOBDNマガジンの記事にて解説を行っています。

OpenBlocks IoT シリーズをMicrosoft Azure IoT Hubへ接続し、Stream Analyticsを経由してPowerBIへデータを受け渡す手順について

下流方向への制御を行うには、「受信設定」を「有効」にします。
制御に使用する、Unixドメインソケットは、作成したデバイス番号より生成されます。

2.送受信に使用するUnixドメインソケット

制御メッセージの送受信に使用するUnixドメインソケットは、データハンドリングガイドにて解説しています。

PD Repeaterからの下流方向

¥0/pd_handler/<デバイス番号>.sock

PD Repeaterへの上流方向


¥0/pd_repeater/<デバイス番号>.sock

今回の設定例では、デバイス番号はuserdev_0000001となります。

3.Cで記述した下流方向制御アプリケーションのサンプル

本サンプルは、Cで記述した下流方向制御アプリケーションのサンプルです。
PD Repeater側がクライアントとなり、下流方向の制御アプリケーションはUnixドメインソケットを使うサーバ側の実装となります。
本プログラムは下流制御機能の評価用に作成されたもので、Userデバイスとして定義されるUnixドメインソケットのuserdev_0000000からuserdev_0000031までをポーリングして受信したデータを表示します。

# apt-get update
# apt-get install libssl-dev

インストールされていないならば、libssl-devパッケージをインストールし、

# make rd_header

シンプルにmakeしてください。

/*
 *  Copyright (c) 2018
 *       Plat'Home CO., LTD. <support@plathome.co.jp>. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Plat'Home CO., LTD. nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <openssl/md5.h>
#ifdef __linux__
#include <sys/epoll.h>
#endif

#define N_DEVICE 32
#ifdef __linux__
#define ROOT_PATH "/pd_handler"
#else
#define ROOT_PATH "/tmp/pd_handler"
#endif
#define LOCALNAME "userdev_"

#define SOCKET_BUF 4096

#ifdef __linux__
#define MAX_SOCKET_CONN 512
#else
#define MAX_SOCKET_CONN 64
#endif

#define SELECT_TIMEOUT 0
#define SELECT_UTIMEOUT 1
#define EPOLL_WAIT 1

struct s_socket_t {
int fd;
struct sockaddr_un addr;
};

struct c_socket_t {
unsigned char id;
int fd;
struct sockaddr_un addr;
};

int main(int argc,char *argv[])
{
int i,j,k,m,n,rc;
char *buf, *socket_buf, *msg_buf;

#ifdef __linux__
int num_fd, epfd;
struct epoll_event ev;
struct epoll_event events[MAX_SOCKET_CONN];
int fcntl_flag;
#else
struct timeval timeout;
fd_set rd_set, rd_set_orig;
#endif
struct timeval tv;
socklen_t len;
struct s_socket_t *ss[N_DEVICE];
struct c_socket_t *cs[MAX_SOCKET_CONN - N_DEVICE];
u_int16_t topic_size;
char *p, *topic, *topic_p, *msg_buf_p;
unsigned char pl;
unsigned char ph;
unsigned char cloud_id, sub_id;
unsigned char md5[MD5_DIGEST_LENGTH];
char hash[MD5_DIGEST_LENGTH * 2 + 1];

buf = (char *)malloc(sizeof(char)*BUFSIZ);
if(!buf) {
perror("buf = malloc()");
exit(1);
}

socket_buf = (unsigned char *)malloc(sizeof(unsigned char)*SOCKET_BUF);
if(!socket_buf) {
perror("socket_buf = malloc()");
exit(1);
}

msg_buf = (unsigned char *)malloc(sizeof(unsigned char)*SOCKET_BUF);
if(!msg_buf) {
perror("msg_buf = malloc()");
exit(1);
}

topic = (char *)malloc(sizeof(char)*BUFSIZ);
if(!topic) {
perror("socket_buf = malloc()");
exit(1);
}

#ifdef __linux__
if ((epfd = epoll_create(MAX_SOCKET_CONN)) < 0) {
perror("epfd = epoll_create()");
exit(1);
}
#else
FD_ZERO(&rd_set_orig);
#endif

for(i=0;i<N_DEVICE;i++) {
ss[i] = (struct s_socket_t *)malloc(sizeof(struct s_socket_t));
if(!ss[i]) {
perror("ss[] = malloc()");
exit(1);
}
memset(ss[i], 0, sizeof(struct s_socket_t));

ss[i]->fd = socket(AF_UNIX, SOCK_STREAM, 0);
ss[i]->addr.sun_family = AF_UNIX;
#ifdef __linux__
snprintf(ss[i]->addr.sun_path + 1,sizeof(ss[i]->addr.sun_path) - 1,"%s/%s%07d.sock",
ROOT_PATH, LOCALNAME, i);
unlink(ss[i]->addr.sun_path);

rc = bind(ss[i]->fd, (struct sockaddr *)&ss[i]->addr, sizeof(sa_family_t) + strlen(ss[i]->
addr.sun_path + 1) + 1);
#else
snprintf(ss[i]->addr.sun_path,sizeof(ss[i]->addr.sun_path),"%s/%s%07d.sock",
ROOT_PATH, LOCALNAME, i);
unlink(ss[i]->addr.sun_path);

rc = bind(ss[i]->fd, (struct sockaddr *)&ss[i]->addr, sizeof(ss[i]->addr));
#endif
if (rc) {
snprintf(buf,sizeof(char)*BUFSIZ,"failed to bind(%s)",ss[i]->addr.sun_path);
perror(buf);
exit(1);
}

rc = listen(ss[i]->fd, N_DEVICE);
if (rc) {
snprintf(buf,sizeof(char)*BUFSIZ,"failed to listen(%s)",ss[i]->addr.sun_path);
perror(buf);
exit(1);
}

#ifdef __linux__
memset(&ev, 0, sizeof(struct epoll_event));
ev.events = EPOLLIN;
ev.data.fd = ss[i]->fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, ss[i]->fd, &ev);
#else
FD_SET(ss[i]->fd,&rd_set_orig);
#endif
printf("listen %s/%s%07d.sock\n", ROOT_PATH, LOCALNAME, i);
}

for(i=0;i<(MAX_SOCKET_CONN - N_DEVICE);i++) {
cs[i] = (struct c_socket_t *)malloc(sizeof(struct c_socket_t));
if(!cs[i]) {
perror("cs[] = malloc()");
exit(1);
}
memset(cs[i], 0, sizeof(struct c_socket_t));
cs[i]->fd = -1;
}

while(1) {
#ifdef __linux__
num_fd = epoll_wait(epfd, events, MAX_SOCKET_CONN, EPOLL_WAIT);
for(i=0;i<num_fd;i++) {
n=1;
for (j = 0; j < N_DEVICE; j++) {
if (events[i].data.fd == ss[j]->fd) {
for(k=0;k<(MAX_SOCKET_CONN - N_DEVICE);k++) {
if(cs[k]->fd == -1) {
break;
}
}
if(k == (MAX_SOCKET_CONN - N_DEVICE)) {
printf("exceeded maximum fd(%d) for accept()", MAX_SOCKET_CONN - N_DEVICE);
}
else {
len = sizeof(cs[k]->addr);
cs[k]->fd = accept(ss[j]->fd, (struct sockaddr *)&cs[k]->addr, &len);
if (cs[k]->fd != -1 ) {
cs[k]->id = j;
fcntl_flag = fcntl(cs[k]->fd, F_GETFL, 0);
fcntl(cs[k]->fd, F_SETFL, fcntl_flag | O_NONBLOCK);
memset(&ev, 0, sizeof(struct epoll_event));
ev.events = EPOLLIN;
ev.data.fd = cs[k]->fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, cs[k]->fd, &ev);
}
}
n = 0;
}
}
if(n) {
for(j=0;j<(MAX_SOCKET_CONN - N_DEVICE);j++) {
if ( events[i].data.fd == cs[j]->fd ) {
memset(socket_buf, 0, sizeof(char)*SOCKET_BUF);
rc = read(cs[j]->fd,(char *)socket_buf,SOCKET_BUF);
if(rc > 0) {
p = socket_buf;
cloud_id = *p++;
sub_id = *p++;
pl = (unsigned char)*p++;
ph = (unsigned char)*p++;
for(m=0;m<MD5_DIGEST_LENGTH;m++)
md5[m] = (unsigned char)*p++;
for(m=0; m<MD5_DIGEST_LENGTH;m++)
snprintf(hash + m + m, sizeof(char)*3, "%.2x", md5[m]);
topic_size = (u_int16_t)((ph & 0x00ff) * 256 + (pl & 0x00ff));
topic_p = topic;
for(m=0;m<topic_size;m++)
*topic_p++ = *p++;
*topic_p = 0x00;
msg_buf_p = msg_buf;
while(*p != 0x00)
*msg_buf_p++ = *p++;
*msg_buf_p = 0x0;
printf("subscrib: topic_size=%d topic=%s payload=%s md5=%s\n",
topic_size, topic, msg_buf, hash);
}
epoll_ctl(epfd, EPOLL_CTL_DEL, cs[j]->fd, &ev);
shutdown(cs[j]->fd,SHUT_RDWR);
close(cs[j]->fd);
cs[j]->fd = -1;
}
}
}
}
#else
timeout.tv_sec = SELECT_TIMEOUT;
timeout.tv_usec = SELECT_UTIMEOUT;

memcpy(&rd_set,&rd_set_orig,sizeof(rd_set_orig));

if(select(FD_SETSIZE,&rd_set,(fd_set *)NULL,(fd_set *)NULL,&timeout) == -1) {
perror("select()");
exit(1);
}

for(i=0;i<FD_SETSIZE;i++) {
if(FD_ISSET(i, &rd_set)) {
n=1;
for(j=0;j<N_DEVICE;j++) {
if ( i == ss[j]->fd ) {
for(k=0;k<(MAX_SOCKET_CONN - N_DEVICE);k++) {
if(cs[k]->fd == -1) {
break;
}
}
if(k == (MAX_SOCKET_CONN - N_DEVICE)) {
printf("exceeded maximum fd(%d) for accept()", MAX_SOCKET_CONN);
}
else {
len = sizeof(cs[k]->addr);
cs[k]->fd = accept(i, (struct sockaddr *)&cs[k]->addr, &len);
if (cs[k]->fd != -1 ) {
setsockopt(cs[k]->fd, SOL_SOCKET, SO_RCVTIMEO,(char*)&timeout, sizeof(struct timeval));
FD_SET(cs[k]->fd, &rd_set_orig);
cs[k]->id = j;
}
}
n = 0;
}
}
if(n) {
for(j=0;j<(MAX_SOCKET_CONN - N_DEVICE);j++) {
if (i == cs[j]->fd ) {
memset(socket_buf, 0, sizeof(char)*SOCKET_BUF);
rc = read(i,(char *)socket_buf,SOCKET_BUF);
if(rc > 0) {
p = socket_buf;
cloud_id = *p++;
sub_id = *p++;
pl = (unsigned char)*p++;
ph = (unsigned char)*p++;
for(m=0;m<MD5_DIGEST_LENGTH;m++)
md5[m] = (unsigned char)*p++;
for(m=0; m<MD5_DIGEST_LENGTH;m++)
snprintf(hash + m + m, sizeof(char)*3, "%.2x", md5[m]);
topic_size = (u_int16_t)((ph & 0x00ff) * 256 + (pl & 0x00ff));
topic_p = topic;
for(m=0;m<topic_size;m++)
*topic_p++ = *p++;
*topic_p = 0x00;
msg_buf_p = msg_buf;
while(*p != 0x00)
*msg_buf_p++ = *p++;
*msg_buf_p = 0x0;
printf("subscrib: topic_size=%d topic=%s payload=%s md5=%s\n",
topic_size, topic, msg_buf, hash);
}
shutdown(i,SHUT_RDWR);
close(i);
FD_CLR(i,&rd_set_orig);
cs[j]->fd = -1;
}
}
}
}
}
#endif
}
}




4.[補足] Cで記述したPD Repeaterへの上流方向へのデータ送信

他の記事で上流方向へデータを送信するカスタムハンドラについて記述していますが、FW3.xでの書き込み方法としては、以下のコードを参考にしてください。

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

/*
write data to unix domain socket */
write_uds( uint8_t *jbuf ) {
    int uds, ret;
    socklen_t socklen;
    struct sockaddr_un addr;

    uds = socket(AF_UNIX, SOCK_STREAM, 0);
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path+1, "/pd_repeater/userdev_0000001.sock");
    socklen =  sizeof(sa_family_t) + strlen(addr.sun_path+1)+1;
    ret = connect(uds, (struct sockaddr *)&addr, socklen);

    if (ret < 0) {
        int err = errno;
        printf("connect NG (errno: %d)\n", err);
       close(uds);
       return 1;
    }
    else {
        printf("connect OK\n");
    }
    write(uds, jbuf, strlen(jbuf));
   
    close( uds );
    return 0;
}

5.さいごに

下流方向の制御に関して、PD Repeaterがクライアントであり、制御アプリケーションはサーバとしての実装が必要になります。

また、WebUIに連動して、起動、停止を行うためには、プロセスを外部から制御する実装を行ってください。