From 7ed283b61b4fa2b6fa8f1ba8c33cd98ed737914e Mon Sep 17 00:00:00 2001 From: KusaReMKN <48670724+KusaReMKN@users.noreply.github.com> Date: Tue, 29 Nov 2022 12:47:47 +0900 Subject: [PATCH] Latest version --- Makefile | 20 +++ denkino.ms | 495 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 515 insertions(+) create mode 100644 Makefile create mode 100644 denkino.ms diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..51513ea --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +TARGET= denkino.pdf + +GROFF= groff +GROFFFLAGS= -dpaper=a4 -P-pa4 -ept -ma4 -ms -mpspic -mwww +PS2PDF= ps2pdf +PS2PDFFLAGS= -sPAPERSIZE=a4 +RM= rm -f +SHELL= /bin/sh + +all: $(TARGET) + +.SUFFIXES: +.SUFFIXES: .ms .pdf + +.ms.pdf: + $(GROFF) -Tps $(GROFFFLAGS) $< | $(PS2PDF) $(PS2PDFFLAGS) - $@ + +.PHONY: clean +clean: + $(RM) $(TARGET) diff --git a/denkino.ms b/denkino.ms new file mode 100644 index 0000000..547dd46 --- /dev/null +++ b/denkino.ms @@ -0,0 +1,495 @@ +.ll 21c-2i +.nr LL \n(.lu +.nr PI 1m +.ds FAM P +.ds ABSTRACT \\fB概要\\fP +.DA 2022\-09\-12 +.TL +電気系-情報系間の通信方式について(案) +.AU +情報二課 +.NH 1 +今までの通信方式(リモコン) +.PP +今までの通信方式(電気系-リモコン間通信)は、 +ボーレート(通信速度)115200 のシリアル通信であり、 +その通信手順は次のようであった。 +.IP (1) 5n +電気系から命令要求(同期信号)として何か一文字送信する +.IP (2) +リモコンは命令要求を受けて制御信号(現在のボタンの押下状態)を送信する +.IP (3) +電気系は制御信号を受けて実際にアクチュエータを制御する +.IP (4) +始めにもどる +.PP +.PS +"\[u30EA]\[u30E2]\[u30B3]\[u30F3]"; +right; move 4; +"\[u96FB]\[u6C17]\[u7CFB]"; +left; move 4; +down; move 0.1; line 3.6; move 0.1; line 0.1; move 0.1; line 0.1; +up; move 4; +right; move 4; +down; line 3.6; move 0.1; line 0.1; move 0.1; line 0.1; +up; move 3.7; +left; arrow 4 "\[u547D]\[u4EE4]\[u8981]\[u6C42]: \fC`.'\fP\[u300C]\[u547D]\[u4EE4]\[u3092]\[u3088]\[u3053]\[u305B]\[u300D]" ""; +down; move 0.5; +right; arrow 4 "\[u5236]\[u5FA1]\[u4FE1]\[u53F7]: \fC`lRFb\\n'\fP\[u300C]\[u53F3]\[u65CB]\[u56DE]\[u304B]\[u3064]\[u524D]\[u9032]\[u305B]\[u3088]\[u300D]" ""; +down; move 0.3; +spline right up 0.15 then down .7 then left up 0.15 -> "\[u5236]\[u5FA1]"; +down; move 0.3; +left; arrow 4 "\[u547D]\[u4EE4]\[u8981]\[u6C42]: \fC`.'\fP\[u300C]\[u547D]\[u4EE4]\[u3092]\[u3088]\[u3053]\[u305B]\[u300D]" ""; +down; move 0.5; +right; arrow 4 "\[u5236]\[u5FA1]\[u4FE1]\[u53F7]: \fC`lrFb\\n'\fP\[u300C]\[u524D]\[u9032]\[u305B]\[u3088]\[u300D]" ""; +down; move 0.3; +spline right up 0.15 then down .7 then left up 0.15 -> "\[u5236]\[u5FA1]"; +down; move 0.3; +left; arrow 4 "\[u547D]\[u4EE4]\[u8981]\[u6C42]: \fC`.'\fP\[u300C]\[u547D]\[u4EE4]\[u3092]\[u3088]\[u3053]\[u305B]\[u300D]" ""; +.PE +.ce +\fBFigure 1:\fP 今までの通信方式 +.bp +.NH 1 +今までの通信方式の問題点 +.PP +例えば次のようなものが挙げられる。 +.IP \fB制御周期の決定権が電気系にある\fP 5n +制御信号は電気系から送信される同期信号を発端に送信される。 +そのため情報系(リモコン)から自発的に信号を発生させることができない。 +.IP \fBボーレートが速すぎる\fP +単純な実装では容易に同期信号を取りこぼすほか、 +通信障害によって同期信号が消失した場合に単純な実装では制御不能に陥る。 +現にリモコン-木箱車間の通信では容易に制御不能になる。 +もっと低速な通信(ボーレート 9600 程度)でも充分実用に耐え得るだろう。 +.IP \fB電気系からバッテリ残量などの付加情報を転送できない\fP +電気系から生成される信号を同期信号として利用しているので、 +意味のある情報を転送できない。 +.PP +これらの問題点を克服するために通信方式を改善する。 +.bp +.NH 1 +新しい通信方式(案) +.PP +新しい通信方式はボーレート 9600 のシリアル通信であり、 +次のような通信手順で行う。 +.IP (1) 5n +情報系は定期的に制御信号(リクエスト)を送信する +.IP (2) +電気系は制御信号を受けて実際にアクチュエータを制御する +.IP (3) +電気系は制御状態(レスポンス)を送信する +.IP (4) +始めにもどる +.PP +さらに、次のような暴走抑制機能を搭載する。 +.IP \(bu 3n +情報系は、リクエストを送信すれどレスポンスを得られずに一定時間経過する場合、 +制御不能状態であると判断し、管理システム(サーバ)にその旨通告する。 +通告した後にはリクエストとして「制御停止(halt)」を送信する。 +.IP \(bu +電気系は、前回のリクエストを受信してから新たなリクエストを受信することなく +一定時間経過する場合、制御管理状態から外れたと判断して車体を安全に停止させる。 +停止した後にリクエストを受信した場合、 +レスポンスとして「制御停止(halt)」を送信する。 +.PP +.PS +"\[u60C5]\[u5831]\[u7CFB]"; +right; move 4; +"\[u96FB]\[u6C17]\[u7CFB]"; +left; move 4; +down; move 0.1; line 3.6; move 0.1; line 0.1; move 0.1; line 0.1; +up; move 4; +right; move 4; +down; line 3.6; move 0.1; line 0.1; move 0.1; line 0.1; +up; move 3.7; +left; move 4; +right; arrow 4 "\[u30EA]\[u30AF]\[u30A8]\[u30B9]\[u30C8]: \fC`RF\\n'\fP\[u300C]\[u53F3]\[u65CB]\[u56DE]\[u304B]\[u3064]\[u524D]\[u9032]\[u305B]\[u3088]\[u300D]" ""; +down; move 0.3; +spline right up 0.15 then down .7 then left up 0.15 -> "\[u5236]\[u5FA1]"; +down; move 0.3; +left; arrow 4 "\[u30EC]\[u30B9]\[u30DD]\[u30F3]\[u30B9]: \fC`FR\\n'\fP\[u300C]\[u524D]\[u9032]\[u304B]\[u3064]\[u53F3]\[u65CB]\[u56DE]\[u3057]\[u3066]\[u3044]\[u308B]\[u300D]" ""; +down; move 0.5; +right; arrow 4 "\[u30EA]\[u30AF]\[u30A8]\[u30B9]\[u30C8]: \fC`FQ\\n'\fP\[u300C]\[u524D]\[u9032]\[u304B]\[u3064]\[u30D0]\[u30C3]\[u30C6]\[u30EA]\[u5831]\[u544A]\[u305B]\[u3088]\[u300D]" ""; +down; move 0.3; +spline right up 0.15 then down .7 then left up 0.15 -> "\[u5236]\[u5FA1]"; +down; move 0.3; +left; arrow 4 "\[u30EC]\[u30B9]\[u30DD]\[u30F3]\[u30B9]: \fC`FQ065\\n'\fP\[u300C]\[u524D]\[u9032]\[u304B]\[u3064]\[u30D0]\[u30C3]\[u30C6]\[u30EA] 65% \[u3067]\[u3042]\[u308B]\[u300D]" ""; +down; move 0.5; +right; arrow 4 "\[u30EA]\[u30AF]\[u30A8]\[u30B9]\[u30C8]: \fC`RF\\n'\fP\[u300C]\[u53F3]\[u65CB]\[u56DE]\[u304B]\[u3064]\[u524D]\[u9032]\[u305B]\[u3088]\[u300D]" ""; +left; move 4.5; +up; move 0.1; line dotted <-> 1.3 "\[u4E00]\[u5B9A]\[u6642]\[u9593]\[u4EE5]\[u5185]"; move 0.1; +up; move 0.1; line dotted <-> 1.3 "\[u4E00]\[u5B9A]\[u6642]\[u9593]\[u4EE5]\[u5185]"; move 0.1; +right; move 5.5; +down; move 0.1; line dotted <-> .8 "\[u4E00]\[u5B9A]\[u6642]\[u9593]\[u4EE5]\[u5185]"; move 0.6; +down; move 0.1; line dotted <-> .8 "\[u4E00]\[u5B9A]\[u6642]\[u9593]\[u4EE5]\[u5185]"; move 0.6; +.PE +.ce +\fBFigure 2:\fP 新しい通信方式 +.bp +.NH 1 +リクエスト及びレスポンスの書式 +.PP +リクエスト及びレスポンス(メッセージ)の書式は次のようにする。 +.IP \(bu 3n +それぞれのメッセージはデリミタ文字 \fC`\\n'\fP (0x0A; NL) で区切られる。 +.IP \(bu +ひとつのメッセージは 72 文字以内である。 +ただし、デリミタ文字はこれに含まれない。 +.NH 2 +リクエストの書式 +.PP +形式的な定義では次の通り(たぶんね): +.DS +.fam C + + : + ; + + + : + | + ; + + + : + | + | + | + | + | + ; + + + : + | + ; + + + : + | + ; + + : `B' ; /* 後退せよ */ + : `Q' ; /* バッテリ報告せよ */ + : `\\n'; /* デリミタ文字 */ + : `F' ; /* 前進せよ */ + : `H' ; /* 制御停止せよ */ + : `L' ; /* 左旋回せよ */ + : `R' ; /* 右旋回せよ */ + : `Z' ; /* 停車せよ */ + + /* 意味を持たない */ + : { 、 + 及び のいづれでもない文字 } +.fam +.DE +.bp +.NH 2 +レスポンスの定義 +.PP +形式的な定義では次の通り(たぶんね): +.DS +.fam C + + : + ; + + + : + | + ; + + + : + | + | + | + | + | + ; + + + | + ; + + + | + ; + + + : + | + ; + + + : + | + ; + + : `B' ; /* 後退している */ + : `Q' ; /* バッテリ報告 */ + : `\\n'; /* デリミタ文字 */ + : `F' ; /* 前進している */ + : `H' ; /* 制御停止している */ + : `L' ; /* 左旋回している */ + : `R' ; /* 右旋回している */ + : `Z' ; /* 停車している */ + + /* 意味を持たない */ + : { 、 + 及び のいづれでもない文字 } + + : `0' | `1' | `2' | `3' | `4' | `5' | `6' | `7' | `8' | `9' + ; +.fam +.DE +.NH 2 +メッセージの意味 +.PP +それぞれのメッセージ文字は次のような意味を持つ。 +.IP \fC\fP 5n +リクエストならば「後退せよ」、 +レスポンスならば「後退している」。 +リクエストに \fC\fP と同時に出現する場合は互いに打ち消し合う。 +すなわち前進も後退もしない。 +レスポンスに \fC\fP と同時に出現しては\fBいけない\fP。 +.IP \fC\fP +リクエストならば「バッテリ残量を報告せよ」、 +レスポンスならば \fC\fP の形式で「バッテリ残量は XXX% である」。 +.IP \fC\fP +リクエストならば「前進せよ」、 +レスポンスならば「前進している」。 +リクエストに \fC\fP と同時に出現する場合は互いに打ち消し合う。 +すなわち前進も後退もしない。 +レスポンスに \fC\fP と同時に出現しては\fBいけない\fP。 +.IP \fC\fP +リクエストならば「制御を停止せよ」、 +レスポンスならば「制御を停止している」。 +制御停止状態から復帰するには本体のリセットが必要である。 +.IP \fC\fP +リクエストならば「左旋回せよ」、 +レスポンスならば「左旋回している」。 +リクエストに \fC\fP と同時に出現する場合は互いに打ち消し合う。 +すなわち旋回しない。 +レスポンスに \fC\fP と同時に出現しては\fBいけない\fP。 +.IP \fC\fP +リクエストならば「右旋回せよ」、 +レスポンスならば「右旋回している」。 +リクエストに \fC\fP と同時に出現する場合は互いに打ち消し合う。 +すなわち旋回しない。 +レスポンスに \fC\fP と同時に出現しては\fBいけない\fP。 +.IP \fC\fP +リクエストならば「停車せよ」、 +レスポンスならば「停車している」。 +レスポンスに \fC\fP、\fC\fP、 +\fC\fP あるいは \fC\fP と同時に出現しては\fBいけない\fP。 +.IP \fC\fP +リクエストとレスポンスのいづれにおいても無視される。 +開発者が巫山戯るためだけにある。 +どんなときであっても遊び心を忘れてはいけない。 +.NH 2 +メッセージの優先順位 +.NH 3 +リクエストの優先順位 +.PP +リクエストの優先順位は次の通りである。 +メッセージの意味が相反する場合、より上にあるものが勝つ。 +.IP 1. 5n +\fC\fP +.IP 2. +\fC\fP +.IP 3. +\fC\fP +.IP 4. +\fC\fP、\fC\fP +.IP 5. +\fC\fP、\fC\fP +.IP 6. +\fC\fP +.NH 3 +レスポンスの優先順位 +.PP +レスポンスは状況を説明しているだけであるので全てのメッセージ文字は対等である。 +.bp +.NH 2 +リクエスト評価の例 +.PP +リクエストを評価する例を示す。 +.IP \fC`F\en'\fP 5n +前進する。 +.IP \fC`FR\en'\fP +右旋回しながら前進する。 +.IP \fC`RF\en'\fP +上と同じ。メッセージは順不同である。 +.IP \fC`BF\en'\fP +停止する。前進と後退は互いに打ち消し合う。 +.IP \fC`FFFFFFFFFF\en'\fP +前進する。重複するメッセージはひとつにまとめられる。 +.IP \fC`FBFFBFB\en'\fP +停止する。重複するメッセージがまとめられた後に打ち消し合う。 +.IP \fC`RLF\en'\fP +前進する。 +.IP \fC`Z\en'\fP +停止する。 +.IP \fC`\en'\fP +停止する。メッセージが無い場合の規定の動作は停止である。 +.IP \fC`Zzz...\en'\fP +停止する。無効な文字は単に無視される。 +.IP \fC`SLEEP!\en'\fP +左旋回する。 +.IP "\fC`BBQ! BBQ!\en'\fP" +後退しながらバッテリ報告する。もちろん一回だけ報告すれば良い。 +.IP \fC`BATTERY\en' +右旋回しながら後退する。 +.IP \fC`HZQLRBF\en' +制御停止する。制御停止は他のどんな動作よりも優先する。 +.bp +.NH 1 +実装のヒント +.PP +ここでは実装のヒントとなるようなコード片をいくつか紹介する。 +\fBここに紹介しているコード片は正しいと思われるが、 +実際に動作を試していないので注意すること\fP。 +機種依存の処理のいくつかは +\(lahttps://blog.goo.ne.jp/lm324/e/c7c8705f1f110a74a251b3abf254024c\(ra +に紹介されている関数を利用している。 +.NH 2 +メッセージを受けとる +.PP +関数 \fCgetMessage\fP() はメッセージを引数 \fCbuf\fP の指す領域に格納する。 +引数 \fCbuf\fP が指す領域は caller(呼び出し側)によって +適切に 73 バイト以上(80 バイト程度)確保されている必要がある。 +.PP +関数 \fCgetMessage\fP() は成功すると 0 を、失敗した場合 +(例えば、72 バイト以上のメッセージを受信した場合など)に \-1 を返す。 +.DS +.fam C +#define DELIM \(aq\\n\(aq /* デリミタ文字 */ +#define MSGLEN 72 /* メッセージの最大長 */ + +int +getMessage(char *buf) +{ + int c, i, res; + + res = 0; + for (i = 0; i < MSGLEN; i++) { + c = SCI_get(); /* 一文字受信する */ + if (c == DELIM) /* デリミタ文字(メッセージの終わり)か? */ + break; /* 受信終了 */ + buf[i] = c; /* バッファに格納 */ + } + buf[i] = \(aq\\0\(aq; /* 文字列終端 */ + if (i == MSGLEN) { /* メッセージが最大長を越えているか? */ + res = -1; + while (SCI_get() != DELIM) + (void)0; /* デリミタ文字まで読み飛ばす */ + } + + return res; +} +.fam +.DE +.bp +.NH 2 +リクエストを解析する +.PP +関数 \fCparseRequest\fP() は引数 \fCreq\fP で指定されるリクエストを解析する。 +解析された結果を返り値として返す。 +.DS +.fam C +#define BACKWARD \(aqB\(aq +#define BATTERY \(aqQ\(aq +#define FORWARD \(aqF\(aq +#define HALT \(aqH\(aq +#define LTURN \(aqL\(aq +#define RTURN \(aqR\(aq +#define SLEEP \(aqZ\(aq + +#define F_BACK 0x01 +#define F_FORW 0x02 +#define F_LTRN 0x04 +#define F_RTRN 0x08 +#define F_BTRY 0x10 +#define F_SLEP 0x20 +#define F_HALT 0x40 + +int +parseRequest(const char *req) +{ + int reqtab[256]; + int i, ret; + + /* リクエストテーブルを初期化する(本来はハードコードしたい) */ + for (i = 0; i < sizeof(reqtab)/sizeof(reqtab[0]); i++) + reqtab[i] = 0; + reqtab[BACKWARD] = F_BACK; + reqtab[BATTERY] = F_BTRY; + reqtab[FORWARD] = F_FORW; + reqtab[HALT] = F_HALT; + reqtab[LTURN] = F_LTRN; + reqtab[RTURN] = F_RTRN; + reqtab[SLEEP] = F_SLEP; + + /* リクエストメッセージを舐めてフラグをセットする */ + ret = 0; + while (*req) + ret |= reqtab[*req++]; + + return ret; +} +.fam +.DE +.bp +.NH 2 +解析した結果を用いて実際に制御する +.PP +関数 \fCdrive\fP() は引数 \fCarg\fP に格納された解析結果を用いて +実際に車体を制御する。 +制御する部分のコードを記述することは\Z'\(an\(an\(an\(an\(an'したくないできないので +コメントに代えてある。 +.DS +.fam C +/* 各種 F_* の定義は 5.2. と同様 */ + +int +drive(int arg) +{ + /* 同時に前進と後退が指示されているなら打ち消す */ + if (arg & F_BACK && arg & F_FORW) + arg ^= F_BACK | F_FORW; + + /* 同時に左旋回と右旋回が指示されているなら打ち消す */ + if (arg & F_LTRN && arg & F_RTRN) + arg ^= F_LTRN | F_RTRN; + + /* 優先順位の上の方から順に指示を調べる */ + if (arg & F_HALT) { + /* 制御停止 */ + return 0; + } + if (arg & F_SLEP) { + /* 停車 */ + return 0; + } + if (arg & F_BTRY) { + /* バッテリ報告 */ + } + if (arg & F_LTRN) { + /* 左旋回 */ + } + if (arg & F_RTRN) { + /* 右旋回 */ + } + if (arg & F_BACK) { + /* 後退 */ + } + if (arg & F_FORW) { + /* 前進 */ + } + + return 0; +} +.fam +.DE