// -------------------------------------------------- // Global Versatile Controler http://www.gvc-on.net/ // -------------------------------------------------- // -------------------------------------------------- // Revision Memo (Y.M.D Editor/Memo) // -------------------------------------------------- // // 2013.06.12 メッセージキュー部分の実装(移植)をする // // 2013.06.10 T.Kabu // 汎用制御装置 Grobal Versatile Controller CLI (gvc_cmd) // // コンパイルの際には -lpcre をつけること // sync;gcc -O2 -Wall -lm -lpcre ./gvc_cmd.c -o gvc_cmd // // 参考URLいろいろ // http://pinka99.ddo.jp/nanao/work/daemon.html // http://d.hatena.ne.jp/rero/20041002/p1 // http://linuxmag.sourceforge.jp/Japanese/March2003/article287.shtml // http://www.geocities.co.jp/Athlete-Samos/7760/study/msgkyu1.html // http://www.geocities.jp/taka_owl2005/job/UNIX/kernel/ipc.html // http://d.hatena.ne.jp/ka2yan/20090327 // // ------------------------------ // BASE // ------------------------------ // 2012.02.06 T.Kabu gvc_cmd gvcdに対してコマンド送信をする // 2012.03.09 T.Kabu gvc_cmd2 暫定的にスイッチON対応(GVC側はタイマーONを想定) // 2013.06.13 T.Kabu gvcd_20130610 Rev.2用に色々修正 // 2013.07.18 T.Kabu gvcd_20130717 赤外線データ(つまりリモコン)の送受信保存再送が出来るようになったのでいったんFix // 2013.12.20 T.Kabu 清書と、赤外線データのサイズの関係で扱えるデータサイズをヘッダ込みで最大1600バイトに統一する //--------------------------------------------------- // include //--------------------------------------------------- #include <stdio.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <syslog.h> #include <termios.h> #include <time.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <sys/stat.h> #include <sys/time.h> //#include <linux/ipc.h> //#include <linux/msg.h> #include <errno.h> // VineLinuxでは<pcre/pcre.h>、Raspbianなど、Debian系?では<pcre.h> #include <pcre/pcre.h> //#include <pcre.h> #include "gvcd.h" // -------------------------------------------------- // Const Define // -------------------------------------------------- // -------------------------------------------------- // Structure // -------------------------------------------------- // -------------------------------- // グローバル変数定義 // -------------------------------- int gvc_cmd = 0; // 0:通常モード -1:終了モード int dev_num = 1; // 対象GVCモジュール番号(1=マスターコントローラー) // -------------------------------------------------- // Sub Routine // -------------------------------------------------- // -------------------------------------------------- // Main Routine // -------------------------------------------------- int main(int argc, char *argv[]) { int argnum; // 引数カウント用 GVC_MESSAGE_QUEUE_t rcv_message_queue; // 受信メッセージキュー GVC_MESSAGE_QUEUE_t send_message_queue; // 送信メッセージキュー int message_qid; // メッセージキューID int msgq_length; // メッセージの長さ key_t msgq_key; // メッセージキューのキー int msgq_result; // メッセージキュー送受信結果 pcre *re[16]; // 検索パターンの内部処理用データ、パターンが増えたら配列数も増やすこと const char *errptr; int erroffset = 0; int capture_count; int *matches = NULL; int n_matches = 0; int offset = 0; int pcount = 0; FILE *datafp = NULL; // データ格納用ファイル struct stat irdatastat; // 赤外線データファイル情報 // 引数検索パターン const char *pattern[16] = { "^end$", // 0 "^ver", // 1 "^list$", // 2 "^res", // 3 "^stop$", // 4 "^sw1on$", // 5 "^sw2on$", // 6 "^sw1off$", // 7 "^sw2off$", // 8 "^irrx$", // 9 "^irtx$", // 10 "^irget$", // 11 "^irset$", // 12 "^irdel$", // 13 "^irclear$", // 14 ""}; // 検索パターンが存在する限りループ while(strlen(pattern[pcount])) { // 検索パターンを内部形式にコンパイル(大文字小文字判別せず、UTF-8で) re[pcount] = pcre_compile(pattern[pcount], PCRE_CASELESS | PCRE_UTF8, &errptr, &erroffset, NULL); // エラーなら if (re[pcount] == NULL) { // errptrに可読エラーメッセージ、erroffsetに場所(先頭からn文字目)が入る fprintf(stderr, "%s at %d\n", errptr, erroffset); // 終わり exit(1); } // 結果を受け取る配列(matches)のサイズを計算する pcre_fullinfo(re[pcount], NULL, PCRE_INFO_CAPTURECOUNT, &capture_count); // 一時領域も含めて3倍必要らしい matches = malloc(sizeof(int) * (capture_count + 1) * 3); // 次の検索パターンに pcount ++; } // ---------------- // 引数確認 // ---------------- // 引数を検査 for (argnum = 1; argnum < argc; argnum++) { // 引数の中にhelpがあるなら if (strcasecmp("help", argv[argnum]) == 0 || strcasecmp("-h", argv[argnum]) == 0 || strcasecmp("-?", argv[argnum]) == 0) { // ソフト名と使い方を表示 printf("%s %s\n\n", SOFTNAME, VERSION); printf("%s [version|list|reset|stop|end|sw?on|sw?off|irrx|irtx|irget (filename)|irset filename|irdel]\n\n", argv[0]); printf(" * MAX IR DATA LENGTH = %d\n\n", GVC_IR_DATALEN_MAX); // 終わり exit(1); } } // 検索パターン番号初期化 pcount = 0; // 検索パターンが存在する限りループ while(strlen(pattern[pcount])) { // 検索実行 n_matches = pcre_exec(re[pcount], NULL, argv[1], strlen(argv[1]), offset, 0, matches, (capture_count + 1) * 3); // 検索結果があるなら if (n_matches > 0) { // 引数の中にend(0番目)があるなら if (pcount == 0) { // 命令コマンドを終了(0xff)にする gvc_cmd = 0xff; } // 引数の中にver(1番目)があるなら if (pcount == 1) { // 命令コマンドをバージョン要求(0x01)にする gvc_cmd = 0x01; } // 引数の中にlist(2番目)があるなら if (pcount == 2) { // 命令コマンドを終了(0x02)にする gvc_cmd = 0x02; } // 引数の中にres(3番目)があるなら if (pcount == 3) { // 命令コマンドをリセット/リスタート要求(0x7e)にする gvc_cmd = 0x7e; } // 引数の中にstop(4番目)があるなら if (pcount == 4) { // 命令コマンドをGVC停止(0x7f)にする gvc_cmd = 0x7f; } // 引数の中にsw1on(5番目)があるなら if (pcount == 5) { // dev_numで指定したモジュールのスイッチをONにする dev_num = 0x30; gvc_cmd = 0x21; } // 引数の中にsw2on(6番目)があるなら if (pcount == 6) { // dev_numで指定したモジュールのスイッチをONにする dev_num = 0x38; gvc_cmd = 0x21; } // 引数の中にsw1off(7番目)があるなら if (pcount == 7) { // dev_numで指定したモジュールのスイッチをONにする dev_num = 0x30; gvc_cmd = 0x20; } // 引数の中にsw2off(8番目)があるなら if (pcount == 8) { // dev_numで指定したモジュールのスイッチをONにする dev_num = 0x38; gvc_cmd = 0x20; } // 引数の中にirrx(9番目)があるなら if (pcount == 9) { // dev_numで指定したモジュールで赤外線データを受信する dev_num = 0x10; gvc_cmd = 0x92; } // 引数の中にirtx(10番目)があるなら if (pcount == 10) { // dev_numで指定したモジュールで赤外線データを送信する dev_num = 0x10; gvc_cmd = 0x91; } // 引数の中にirget(11番目)があるなら if (pcount == 11) { // dev_numで指定したモジュールから赤外線データを取得する dev_num = 0x10; gvc_cmd = 0x94; // 何らかのオプションがもうひとつついているなら if (argc == 3) { // そのファイルが開けるか試してみる datafp = fopen(argv[2], "wb"); // ファイルが開けなかったら if (datafp == NULL) { // エラー吐いて終わり fprintf(stderr, "%s : %s\n", argv[2], strerror(errno)); // 終わり exit(EXIT_FAILURE); } // ファイルが普通に開けたなら else { // ファイルを閉じる fclose(datafp); } } } // 引数の中にirset(12番目)があるなら if (pcount == 12) { // 何らかのオプションがもうひとつついているなら if (argc == 3) { // dev_numで指定したモジュールに赤外線データを設定する dev_num = 0x10; gvc_cmd = 0x93; // 赤外線データファイルがないなら if (stat(argv[2], &irdatastat) == -1) { // エラー吐いて終わり fprintf(stderr, "%s : %s\n", argv[2], strerror(errno)); // 終わり exit(EXIT_FAILURE); } // 赤外線データファイルのサイズがGVC_IR_DATALEN_MAXを超えていたら if (irdatastat.st_size > GVC_IR_DATALEN_MAX) { // エラー吐いて終わり fprintf(stderr, "%s : size over\n", argv[2]); // 終わり exit(EXIT_FAILURE); } // そのファイルが開けるか試してみる datafp = fopen(argv[2], "rb"); // ファイルが開けなかったら if (datafp == NULL) { // エラー吐いて終わり fprintf(stderr, "%s : %s\n", argv[2], strerror(errno)); // 終わり exit(EXIT_FAILURE); } // ファイルが普通に開けたなら else { // 実際にはこの後↓で処理 } } } // 引数の中にirdel(13番目)かirclear(14番目)があるなら if (pcount == 13 || pcount == 14) { // dev_numで指定したモジュールで赤外線データを受信する dev_num = 0x10; gvc_cmd = 0x95; } } // 次の検索パターンに pcount ++; } // もしgvc_cmdが0のままなら if (gvc_cmd == 0) { // ソフト名と使い方を表示 printf("%s %s\n\n", SOFTNAME, VERSION); printf("%s [version|list|reset|stop|end|sw?on|sw?off|irrx|irtx|irget (filename)|irset filename|irdel]\n\n", argv[0]); printf(" * MAX IR DATA LENGTH = %d\n\n", GVC_IR_DATALEN_MAX); // 終わり exit(1); } // 乱数初期化 srand(time(0)); // メッセージキューのメッセージの長さを設定 msgq_length = sizeof(GVC_MESSAGE_QUEUE_t) - sizeof(long); // パス名とプロジェクト識別子を System V IPC キーに変換する(命令キュー) // gvcdが動いていることが前提なので、gvcdのPIDファイルから生成すればOK。 msgq_key = ftok(GVC_PID_FILENAME, 'w'); // メッセージキューIDを取得 /// message_qid = msgget(msgq_key, 0660); message_qid = msgget(msgq_key, 0666); // メッセージキューIDが取得できたなら(message_qid!=-1) if (message_qid != -1) { // 作成したキューIDを出力 printf("QID = %d\n", message_qid); } // メッセージキューIDが取得できなかったら(message_qid=-1) else { // エラーを出力 fprintf(stderr, "Message Queue make error : %s\n", strerror(errno)); // 終わり exit(EXIT_FAILURE); } // なんらかの命令があるなら if (gvc_cmd != 0) { // メッセージの初期化 memset((void *)&send_message_queue.q, sizeof(GVC_QUEUE_MESSAGE_t), 0x00); // メッセージ設定…ここでは命令を送信する send_message_queue.qtype = COMMAND_Q; // メッセージキュータイプ設定(要求) send_message_queue.q.gvc_num = 0x01; // 対象GVC番号設定(1=とりあえず…TBD) send_message_queue.q.msg_type = GVC_MSG_ENQ; // メッセージタイプ設定(GVCへの各種問い合わせ) send_message_queue.q.dev_num = dev_num; // 接続GVC番号設定(1=マスターコントローラー) send_message_queue.q.format = 0x01; // コマンドフォーマット設定 send_message_queue.q.cmd = gvc_cmd; // コマンド設定 // dev_numで指定したモジュールから赤外線データを取得する、という命令で、かつ保存先ファイル名の指定があるなら if (gvc_cmd == 0x94 && argc == 3) { // 保存先ファイル名をコピー(引数確認のところでファイルが開けるか調べているのでコピーするだけでOK) strcpy((char *)send_message_queue.q.data, argv[2]); // データ長設定 send_message_queue.q.data_len = strlen((char *)send_message_queue.q.data); } // dev_numで指定したモジュールへ赤外線データを設定する、という命令で、かつデータファイル名の指定があって、ファイルが開けているなら else if (gvc_cmd == 0x93 && argc == 3 && datafp != NULL) { // データファイルからデータを読出、かつデータ長設定 send_message_queue.q.data_len = fread(send_message_queue.q.data, sizeof(char), BUFF_SIZE, datafp); // データファイルを閉じる fclose(datafp); } else { // それ以外は今のところ特にデータはないので send_message_queue.q.data_len = 0; // データ長設定 } // CRCの設定は… // メッセージキュー、というかLinuxレベルになるとテーブル構造体のアラインメントが行われるから実質できない。 // メッセージ送信 msgq_result = msgsnd(message_qid, &send_message_queue, msgq_length, 0); // メッセージが送信できたなら(=0) if (msgq_result == 0) { // 画面に出力 printf("COMMAND SEND ... \n"); printf(" qtype = 0x%02lx\n", send_message_queue.qtype); printf(" q.gvc_num = 0x%02x\n", send_message_queue.q.gvc_num); printf(" q.msg_type = 0x%02x\n", send_message_queue.q.msg_type); printf(" q.dev_num = 0x%02x\n", send_message_queue.q.dev_num); printf(" q.format = 0x%02x\n", send_message_queue.q.format); printf(" q.cmd = 0x%02x\n", send_message_queue.q.cmd); printf(" q.data_len = %0d\n", send_message_queue.q.data_len); printf(" q.data = %s\n", send_message_queue.q.data); // 終わり exit(EXIT_SUCCESS); } // メッセージが送信できなかったら(!=0) else { // エラーを出力 fprintf(stderr, "COMMAND SEND ERROR : %s(ERRNO=%d, message_qid=%d, msgq_length=%d)\n", strerror(errno), errno, message_qid, msgq_length); // 終わり exit(EXIT_FAILURE); } sleep(1); } // メッセージ受信 // 「求めているメッセージがない」こともある.msgflgとしてIPC_NOWAITが指定されてい // る場合にはすぐにエラーを返すが,そうでない場合には求めるメッセージが得られるか, // キューが消えるか,シグナルで捕獲されるまで待つ. msgq_result = msgrcv(message_qid, &rcv_message_queue, msgq_length, RESULT_Q, IPC_NOWAIT); // メッセージが受信できたなら(!=-1) if (msgq_result != -1) { // データを出力 printf("MESSAGE RECEIVED ... qtype = %ld\n", rcv_message_queue.qtype); } // メッセージが受信できなかったら(=-1) else { // とりあえずコマンドを送るだけなので何もしない // // エラーを出力 // fprintf(stderr, "Message Queue receieved error : %s\n", strerror(errno)); // // 終わり // exit(EXIT_FAILURE); } // 終わり exit(EXIT_SUCCESS); }