// --------------------------------------------------
// 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);
}