タグ「C言語」を含む投稿[64件](4ページ目)
Art-Netのデコード/エンコードも出来ました。受信した1配列のバイナリデータを分類して構造体に取り込むのがデコード、項目別の構造体から送信用のバイナリデータに変換するのがエンコードです。
実験は受信値をデコードして再エンコードして送信です。照明器具としては何の芸も無い状態ですが、これが出来なきゃ先へは進めません。
topコマンドで処理負荷を見ますと、ダンプ表示ありで23%、ダンプ表示無しで7%くらいです。Art-Netのコア処理は思った以上に軽いですが、画面表示が重いっすね。完成イメージの画面表示はもっと重くなりますので、Art-Netのコア処理は画面表示とは別プロセスにしたいですね。出来ることならArt-Netのコア処理でCPUスレッドを1つ占有したいくらいです。ここは余裕をもって確実にしたい処理ですから。
今のところ製作は順調です。3月末までに1日平均3-4時間程度使えますので、「ArtNet-Engine」と呼ぶコア機能はまとめられそうです。
#[Art-Net] #C言語
実験は受信値をデコードして再エンコードして送信です。照明器具としては何の芸も無い状態ですが、これが出来なきゃ先へは進めません。
topコマンドで処理負荷を見ますと、ダンプ表示ありで23%、ダンプ表示無しで7%くらいです。Art-Netのコア処理は思った以上に軽いですが、画面表示が重いっすね。完成イメージの画面表示はもっと重くなりますので、Art-Netのコア処理は画面表示とは別プロセスにしたいですね。出来ることならArt-Netのコア処理でCPUスレッドを1つ占有したいくらいです。ここは余裕をもって確実にしたい処理ですから。
今のところ製作は順調です。3月末までに1日平均3-4時間程度使えますので、「ArtNet-Engine」と呼ぶコア機能はまとめられそうです。
#[Art-Net] #C言語
Art-Netの送信にも成功しました。
受信値を転送するだけですが、Art-Netデコーダから正常と思われる値が出力されています。
今日は終わりにしますが、大きな課題がクリア出来て大満足です。
ただ、8ユニバースを出力しているdot2が一杯いっぱいの様子。発熱も凄いし画面もコマ送りです。
ちなみにですが、recvfrom()の4番目のパラメータを「MSG_DONTWAIT」にすると受信待ちしません。先達の例では待ちアリの「0」を定義してioctl()に待ち無し(ノンブロッキング)を設定していることが多いのですが、「MSG_DONTWAIT」を使った方がストレスが無い感じ。
で、気付いたのですが、C言語は速い。速いが故の対策が必要になる始末。受送信テストでは終了のためのキー入力やタイムアウトを入れるのが面倒だったのでfor文による一定回数の繰り返しで試したのですが、受信待ちを無くした後はPythonでの実験の際に使った回数では一瞬で終わってしまいます。プログラムが間違っているのかと思う程でした。てことは、あまりにも無意味な回数recvfrom()を呼んでいることになりますので、受信が無い場合は100~500usecくらい待ちを入れた方がいいみたいです。バッファを読みに行っているだけなので気にしなくていいって話もありますが、ANSIエスケープシーケンスを用いた画面表示も適度な待ちを入れないと画面がフリッカーを起こす程です。数か月かかりましたが、C言語を勉強して良かったと思います。遅いのを対策するのは大変ですが、速いのを抑え込むのは比較的簡単ですからね。
ここまで速いとマルチプロセスを使わなくてもいいのではないか?って気持ちも芽生えます。使った方が速さ以外に都合が良いこともあるので使いますケド。
基本的な受送信が確認出来ましたので、関数化しつつArt-Net(正しくはArt-DMX)のデコード/エンコードも書きましょう。
#[Art-Net] #C言語
受信値を転送するだけですが、Art-Netデコーダから正常と思われる値が出力されています。
今日は終わりにしますが、大きな課題がクリア出来て大満足です。
ただ、8ユニバースを出力しているdot2が一杯いっぱいの様子。発熱も凄いし画面もコマ送りです。
ちなみにですが、recvfrom()の4番目のパラメータを「MSG_DONTWAIT」にすると受信待ちしません。先達の例では待ちアリの「0」を定義してioctl()に待ち無し(ノンブロッキング)を設定していることが多いのですが、「MSG_DONTWAIT」を使った方がストレスが無い感じ。
で、気付いたのですが、C言語は速い。速いが故の対策が必要になる始末。受送信テストでは終了のためのキー入力やタイムアウトを入れるのが面倒だったのでfor文による一定回数の繰り返しで試したのですが、受信待ちを無くした後はPythonでの実験の際に使った回数では一瞬で終わってしまいます。プログラムが間違っているのかと思う程でした。てことは、あまりにも無意味な回数recvfrom()を呼んでいることになりますので、受信が無い場合は100~500usecくらい待ちを入れた方がいいみたいです。バッファを読みに行っているだけなので気にしなくていいって話もありますが、ANSIエスケープシーケンスを用いた画面表示も適度な待ちを入れないと画面がフリッカーを起こす程です。数か月かかりましたが、C言語を勉強して良かったと思います。遅いのを対策するのは大変ですが、速いのを抑え込むのは比較的簡単ですからね。
ここまで速いとマルチプロセスを使わなくてもいいのではないか?って気持ちも芽生えます。使った方が速さ以外に都合が良いこともあるので使いますケド。
基本的な受送信が確認出来ましたので、関数化しつつArt-Net(正しくはArt-DMX)のデコード/エンコードも書きましょう。
#[Art-Net] #C言語
自作のライブラリをincludeしてコンパイルするには少し面倒があります。
例えば、
製作するアプリケーションファイル test_inkey
メインのソースファイル test_inkey.c
ライブラリのソースファイル std_inkey.c
とした場合、
$ gcc -Wall -lm -o test_inkey -I ./ test_inkey.c std_inkey.c
などとコンソールからコマンドを打たないといけません。
Cのソースファイルが一つならVSCodeからショートカットで一発なんですけどね。
この場合、makefileを作っておけばmakeコマンドで一発です。
次がわかりやすい。
Makefile の書き方 (C 言語)
makeコマンドの使い方も丁寧に説明してくれています。
#C言語
例えば、
製作するアプリケーションファイル test_inkey
メインのソースファイル test_inkey.c
ライブラリのソースファイル std_inkey.c
とした場合、
$ gcc -Wall -lm -o test_inkey -I ./ test_inkey.c std_inkey.c
などとコンソールからコマンドを打たないといけません。
Cのソースファイルが一つならVSCodeからショートカットで一発なんですけどね。
この場合、makefileを作っておけばmakeコマンドで一発です。
次がわかりやすい。
Makefile の書き方 (C 言語)
makeコマンドの使い方も丁寧に説明してくれています。
#C言語
てなわけで、socketの実験に入る下準備が整いました。Pythonで出来たことですからC言語(gcc)で出来ないことではないでしょう。
Art-Netエンジンを構成するにはFIFOというか循環配列というかリングキャッシュというかQueueも勉強しないとけません。例えば1024個の配列を作ったとして、これがリング状に繋がったイメージで使います。1024で折り返すカウンタを使って配列の基点位置を表すだけですが、カウンタの計算モジュールだけでもライブラリ化しないと面倒かなと。
これはDelayで必要な機能です。一定の時間間隔でDMXのデータを保存し続ければ配列サイズが許す範囲で過去情報を取り出せます。つまりDelayになります。受信毎の保存でないことが肝ですが、往年のテープエコーと要領は同じです。
C言語はボチボチ書けるようになってきましたし、先達の情報も読み取れるようになってきました。パッチマシンとしての完成は先としても、この閑散期にArt-Netエンジンだけでも完成させたいです。
あとは、コマンド入力と処理の方法も考えないといけません。
ルールマップに基づいた入力制限とか、入力されたコマンドや数値のスタック方法とか、それの表示とかです。入力値はコマンドと数値の文字列で処理関数に渡すつもりですが、それをどの様に解析して実行するかも案外難しい。
#[Art-Net] #C言語
Art-Netエンジンを構成するにはFIFOというか循環配列というかリングキャッシュというかQueueも勉強しないとけません。例えば1024個の配列を作ったとして、これがリング状に繋がったイメージで使います。1024で折り返すカウンタを使って配列の基点位置を表すだけですが、カウンタの計算モジュールだけでもライブラリ化しないと面倒かなと。
これはDelayで必要な機能です。一定の時間間隔でDMXのデータを保存し続ければ配列サイズが許す範囲で過去情報を取り出せます。つまりDelayになります。受信毎の保存でないことが肝ですが、往年のテープエコーと要領は同じです。
C言語はボチボチ書けるようになってきましたし、先達の情報も読み取れるようになってきました。パッチマシンとしての完成は先としても、この閑散期にArt-Netエンジンだけでも完成させたいです。
あとは、コマンド入力と処理の方法も考えないといけません。
ルールマップに基づいた入力制限とか、入力されたコマンドや数値のスタック方法とか、それの表示とかです。入力値はコマンドと数値の文字列で処理関数に渡すつもりですが、それをどの様に解析して実行するかも案外難しい。
#[Art-Net] #C言語
キー入力の処理を書き直してみました。
ファンクションキーやカーソルキーなどの複数バイトでキーコードを出すキーも扱えます。shiftやAltなどの装飾キーは拾えませんケド。
入力の待ち時間はありません。get_inkey()はすぐ戻ってきます。戻り値はキーコードの長さを表しますが、0なら入力が無かったと判別出来ます。
得られるのは標準入力からのASCIIコードです。read()はキャッシュを読むだけでタイミング次第では取得値に複数のキーコードが混じることがありますが、これをキー単体のコードとして仕分けています。キャッシュの一番前のキーコードだけを取り出す単純な仕分けで、想定外のキーコードは読み飛ばしていますが十分でした。
キーコードで何かをするにはswitch文やif文でそのコードを仕分ける必要があります。
正常に動かすなら、100msec(1/10秒)毎以下でget_inkey()を読みに行く必要があります。
定数設定とプロトタイプ宣言をヘッダーファイルに書けばincludeしてライブラリとして使えます。mainはコメント化して使った方がいいですけど。
Raspberry Pi 4B / Rasbian11_32bit(blueseye) / OS標準gcc
/* ---------------------------------
リアルタイムにキー入力をチェックする
--------------------------------- */
/* getcharはキー入力のキャッシュが空だと入力があるまで待つ。
キャッシュが無いなら無いでそのまま抜けたいが、タイムアウトを設定しても
抜けない。環境に寄るのかもしれないが、ioctl.hを用いることで
タイムアウトが実現できた。
get_inkeyの呼び出しは100msec(1/10秒)毎以下で繰り返し行うこと。短い方がいい。
*/
/* ライブラリ読み込み */
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
/* 定数設定 */
#define SET 1 // ioctl.readのモードをRawにするフラグ
#define RESET 0 // ipctl.readのモードをCanonicalに戻すフラグ
#define LENGTH_STACK 32 // キー入力のスタック長 状況によるが、長めにしないと取り出しが半端になる
#define ESC 0x1B // ASCIIコード ESC
#define BRACKET 0x5B // ASCIIコード [
/* プロトタイプ宣言 */
int get_inkey( char *get_char ) ; // キー入力を取得する関数
// 戻り値 // -1=エラー、0>=取得したキーコードの長さ
// char *get_char // 取得したキーコードを返すポインタ
int set_inkey( int mode_flag ) ; // キー入力のモードを設定する関数
// int mode_flag // モードの設定フラグ 1(SET)=Rawモードにする / 0(CLR)=モードを戻す
/* テスト用main */
int main(void) {
int i ; // for文用カウンタ
char get_char[ LENGTH_STACK ] ; // 入力されたキーコードを保存する変数
int ret_inkey ; // get_inkeyからの戻り値を保存する変数
/* コンソールの設定を初期化 */
set_inkey( SET ) ;
/* 無限ループで入力したキーを表示する */
for(;;) {
usleep( 1e5 ) ;
ret_inkey = get_inkey( get_char ) ; // get_inkeyから文字取得
/* get_inkeyの戻り値を処理 */
if( ret_inkey == -1 ) break ; // 戻り値が-1ならエラーなのでループから抜けて終了
if( get_char[0] == 0x0A ) break ; // エンターキーが押されたならループを抜けて終了
/* 入力された文字を出力する */
if( ret_inkey > 0) {
for( i = 0; i < ret_inkey; i++ ) {
if( get_char[i] >= 0x20 && get_char[i] <= 0x7E ) { // 入力された文字を出力する・・・表示可能な文字
printf( " %c<%02X>", get_char[i], get_char[i] ) ;
} else if( get_char[i] >= 0x00 && get_char[i] <= 0xFF ) { // 入力された文字を出力する・・・表示不可能な文字
printf( " <%02X>", get_char[i] ) ;
}
}
printf( "\n" ) ;
}
fflush( stdout ) ; // 画面表示のスタックを吐き出す
}
/* 正常終了 */
printf( "\n" ) ; // 念のための改行
set_inkey( RESET ) ; // コンソールの設定を元に戻す
return 0 ;
}
/* キー入力を取得する */
int get_inkey( char *get_char) {
// char *get_char // キーコードを返すためのchar配列のポインタ
int i ; // for用変数
char in_char[ LENGTH_STACK ] = { 0x00 }; // 入力されたキーを取得
int read_length ; // read()戻り値 キーコードのバイト数
int put_length = 0 ; // 戻り値 キーコードのバイト数
/* get_charの初期化 */
for( i = 0; i < LENGTH_STACK; i++ ) { // すべての値を0x00にする
get_char[ i ] = 0x00 ;
}
/* キー入力を読み取る */
read_length = read( 0, &in_char, LENGTH_STACK ) ; // read_lengthに取得バイト数、in_charにキーコードを得る
/* 取得値の仕分け */
if( read_length < 0 ) { // 取得エラーなのでエラーを返して終了
return -1 ;
} else if( read_length == 0 ) { // キーコードを得られなかったのでゼロを返す
return 0 ;
} else if( read_length == 1 // 取得が1バイトか先頭バイトがESC(0x1B)以外なら先頭バイトを返す
|| in_char[ 0 ] != ESC ) {
get_char[ 0 ] = in_char[ 0 ] ; // 値コピー
return 1 ;
} else if( read_length > 2 // 3バイト以上で先頭2バイトが0x1B(ESC)、0x5B([)なら
&& in_char[ 0 ] == ESC // ファンクションキーまたはカーソルキー
&& in_char[ 1 ] == BRACKET ) {
get_char[ 0 ] = ESC ; // 0バイト目
get_char[ 1 ] = BRACKET ; // 1バイト目
put_length = 2; // 戻り値設定
for( i = 2; i < read_length; i++ ) { // 2バイト目以降をコピー
if( in_char[ i ] == ESC ) break ; // 末尾にESCがあれば終了
get_char[ i ] = in_char[ i ] ; // 値コピー
put_length++ ; // 戻り値インクリメント
}
return put_length ; // 戻り値送って終了
}
return 0 ;
}
/* キー入力のモードを設定する */
int set_inkey( int mode_flag ) {
static struct termio tty_backup ; // 変更前の設定を保持
static struct termio tty_change ; // 変更後の設定を保持
/* モードを設定する */
if( mode_flag == SET ) {
ioctl( 0, TCGETA, &tty_backup ) ; // 現在の設定を読み出し
tty_change = tty_backup ; // 現在設定のスタック
tty_change.c_lflag &= ~( ECHO | ICANON ) ; // エコーを止め、RAW モードへ変更
tty_change.c_cc[ VMIN ]= 0 ; // 0文字入力された時点で入力を受け取る
tty_change.c_cc[ VTIME ] = 0.01 ; // 何も入力がない場合、1msec待つ (1 = 1/10sec)
ioctl( 0, TCSETAF, &tty_change ) ; // ここで設定を反映
/* モードを戻す */
} else if( mode_flag == 0 ) {
ioctl( 0, TCSETAF, &tty_backup ) ; // スタックしていた設定に戻す
}
return 0;
}
※ このブログシステムでは#や[が機能文字扱いなので、上記ではこれらを全角文字で書いています。
#C言語
ファンクションキーやカーソルキーなどの複数バイトでキーコードを出すキーも扱えます。shiftやAltなどの装飾キーは拾えませんケド。
入力の待ち時間はありません。get_inkey()はすぐ戻ってきます。戻り値はキーコードの長さを表しますが、0なら入力が無かったと判別出来ます。
得られるのは標準入力からのASCIIコードです。read()はキャッシュを読むだけでタイミング次第では取得値に複数のキーコードが混じることがありますが、これをキー単体のコードとして仕分けています。キャッシュの一番前のキーコードだけを取り出す単純な仕分けで、想定外のキーコードは読み飛ばしていますが十分でした。
キーコードで何かをするにはswitch文やif文でそのコードを仕分ける必要があります。
正常に動かすなら、100msec(1/10秒)毎以下でget_inkey()を読みに行く必要があります。
定数設定とプロトタイプ宣言をヘッダーファイルに書けばincludeしてライブラリとして使えます。mainはコメント化して使った方がいいですけど。
Raspberry Pi 4B / Rasbian11_32bit(blueseye) / OS標準gcc
/* ---------------------------------
リアルタイムにキー入力をチェックする
--------------------------------- */
/* getcharはキー入力のキャッシュが空だと入力があるまで待つ。
キャッシュが無いなら無いでそのまま抜けたいが、タイムアウトを設定しても
抜けない。環境に寄るのかもしれないが、ioctl.hを用いることで
タイムアウトが実現できた。
get_inkeyの呼び出しは100msec(1/10秒)毎以下で繰り返し行うこと。短い方がいい。
*/
/* ライブラリ読み込み */
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
/* 定数設定 */
#define SET 1 // ioctl.readのモードをRawにするフラグ
#define RESET 0 // ipctl.readのモードをCanonicalに戻すフラグ
#define LENGTH_STACK 32 // キー入力のスタック長 状況によるが、長めにしないと取り出しが半端になる
#define ESC 0x1B // ASCIIコード ESC
#define BRACKET 0x5B // ASCIIコード [
/* プロトタイプ宣言 */
int get_inkey( char *get_char ) ; // キー入力を取得する関数
// 戻り値 // -1=エラー、0>=取得したキーコードの長さ
// char *get_char // 取得したキーコードを返すポインタ
int set_inkey( int mode_flag ) ; // キー入力のモードを設定する関数
// int mode_flag // モードの設定フラグ 1(SET)=Rawモードにする / 0(CLR)=モードを戻す
/* テスト用main */
int main(void) {
int i ; // for文用カウンタ
char get_char[ LENGTH_STACK ] ; // 入力されたキーコードを保存する変数
int ret_inkey ; // get_inkeyからの戻り値を保存する変数
/* コンソールの設定を初期化 */
set_inkey( SET ) ;
/* 無限ループで入力したキーを表示する */
for(;;) {
usleep( 1e5 ) ;
ret_inkey = get_inkey( get_char ) ; // get_inkeyから文字取得
/* get_inkeyの戻り値を処理 */
if( ret_inkey == -1 ) break ; // 戻り値が-1ならエラーなのでループから抜けて終了
if( get_char[0] == 0x0A ) break ; // エンターキーが押されたならループを抜けて終了
/* 入力された文字を出力する */
if( ret_inkey > 0) {
for( i = 0; i < ret_inkey; i++ ) {
if( get_char[i] >= 0x20 && get_char[i] <= 0x7E ) { // 入力された文字を出力する・・・表示可能な文字
printf( " %c<%02X>", get_char[i], get_char[i] ) ;
} else if( get_char[i] >= 0x00 && get_char[i] <= 0xFF ) { // 入力された文字を出力する・・・表示不可能な文字
printf( " <%02X>", get_char[i] ) ;
}
}
printf( "\n" ) ;
}
fflush( stdout ) ; // 画面表示のスタックを吐き出す
}
/* 正常終了 */
printf( "\n" ) ; // 念のための改行
set_inkey( RESET ) ; // コンソールの設定を元に戻す
return 0 ;
}
/* キー入力を取得する */
int get_inkey( char *get_char) {
// char *get_char // キーコードを返すためのchar配列のポインタ
int i ; // for用変数
char in_char[ LENGTH_STACK ] = { 0x00 }; // 入力されたキーを取得
int read_length ; // read()戻り値 キーコードのバイト数
int put_length = 0 ; // 戻り値 キーコードのバイト数
/* get_charの初期化 */
for( i = 0; i < LENGTH_STACK; i++ ) { // すべての値を0x00にする
get_char[ i ] = 0x00 ;
}
/* キー入力を読み取る */
read_length = read( 0, &in_char, LENGTH_STACK ) ; // read_lengthに取得バイト数、in_charにキーコードを得る
/* 取得値の仕分け */
if( read_length < 0 ) { // 取得エラーなのでエラーを返して終了
return -1 ;
} else if( read_length == 0 ) { // キーコードを得られなかったのでゼロを返す
return 0 ;
} else if( read_length == 1 // 取得が1バイトか先頭バイトがESC(0x1B)以外なら先頭バイトを返す
|| in_char[ 0 ] != ESC ) {
get_char[ 0 ] = in_char[ 0 ] ; // 値コピー
return 1 ;
} else if( read_length > 2 // 3バイト以上で先頭2バイトが0x1B(ESC)、0x5B([)なら
&& in_char[ 0 ] == ESC // ファンクションキーまたはカーソルキー
&& in_char[ 1 ] == BRACKET ) {
get_char[ 0 ] = ESC ; // 0バイト目
get_char[ 1 ] = BRACKET ; // 1バイト目
put_length = 2; // 戻り値設定
for( i = 2; i < read_length; i++ ) { // 2バイト目以降をコピー
if( in_char[ i ] == ESC ) break ; // 末尾にESCがあれば終了
get_char[ i ] = in_char[ i ] ; // 値コピー
put_length++ ; // 戻り値インクリメント
}
return put_length ; // 戻り値送って終了
}
return 0 ;
}
/* キー入力のモードを設定する */
int set_inkey( int mode_flag ) {
static struct termio tty_backup ; // 変更前の設定を保持
static struct termio tty_change ; // 変更後の設定を保持
/* モードを設定する */
if( mode_flag == SET ) {
ioctl( 0, TCGETA, &tty_backup ) ; // 現在の設定を読み出し
tty_change = tty_backup ; // 現在設定のスタック
tty_change.c_lflag &= ~( ECHO | ICANON ) ; // エコーを止め、RAW モードへ変更
tty_change.c_cc[ VMIN ]= 0 ; // 0文字入力された時点で入力を受け取る
tty_change.c_cc[ VTIME ] = 0.01 ; // 何も入力がない場合、1msec待つ (1 = 1/10sec)
ioctl( 0, TCSETAF, &tty_change ) ; // ここで設定を反映
/* モードを戻す */
} else if( mode_flag == 0 ) {
ioctl( 0, TCSETAF, &tty_backup ) ; // スタックしていた設定に戻す
}
return 0;
}
※ このブログシステムでは#や[が機能文字扱いなので、上記ではこれらを全角文字で書いています。
#C言語
本業は当面の課題を一通りやっつけました。
追われる感じが無いって幸せ。
そんぢゃ今夜はC言語の勉強!!、といきたいところですが、ここ数日、朝から晩まで10時間以上図面書きをやりますと帰宅してからヤル気はおきません。今夜は脳味噌を休ませてアタマを切替えます。
明日と明後日は急ぎの要件が無いので、キー入力の実験、これまでに実験したネタの整理、socketによるArt-Net処理の実験に手を付けましょう。
キー入力はioctl.hとtermios.hを用いて設定しread()で読み込む方法を用いていますが、これってデバイスドライバを書く基本なんだそうです。
宛先を設定し、read()で取り込み、write()で送り出すという極めてシンプルな構成ですが、合理主義者の神達がローレベルの制御に対し案件毎に違ったやり方を定義するとは思えませんので納得です。
肝心なところは先達のサンプルプログラムをコピペしただけなので何が何だかよくわかりませんが、デバイスドライバが書けたら守備範囲が広くなりそうです。
#C言語
追われる感じが無いって幸せ。
そんぢゃ今夜はC言語の勉強!!、といきたいところですが、ここ数日、朝から晩まで10時間以上図面書きをやりますと帰宅してからヤル気はおきません。今夜は脳味噌を休ませてアタマを切替えます。
明日と明後日は急ぎの要件が無いので、キー入力の実験、これまでに実験したネタの整理、socketによるArt-Net処理の実験に手を付けましょう。
キー入力はioctl.hとtermios.hを用いて設定しread()で読み込む方法を用いていますが、これってデバイスドライバを書く基本なんだそうです。
宛先を設定し、read()で取り込み、write()で送り出すという極めてシンプルな構成ですが、合理主義者の神達がローレベルの制御に対し案件毎に違ったやり方を定義するとは思えませんので納得です。
肝心なところは先達のサンプルプログラムをコピペしただけなので何が何だかよくわかりませんが、デバイスドライバが書けたら守備範囲が広くなりそうです。
#C言語
キー入力はファンクションキーや矢印キーが複数バイトのデータなので、先のプログラムではこれらを正しく判別出来ません。0x1Bを得てもESCキーなのか矢印キーなのか区別できないからです。
これらのデータが一度にキャッシュに入って連続した読み出しとなる(タイムアウト無しに連続して読み出せる)ならいいですが、そうでないと面倒です。いずれにしてもこの辺りの挙動をチェックする必要があります。今は1バイトのcharとして処理していますが、8バイトくらいのchar配列、つまり文字列として処理することになりそうです。
#C言語
これらのデータが一度にキャッシュに入って連続した読み出しとなる(タイムアウト無しに連続して読み出せる)ならいいですが、そうでないと面倒です。いずれにしてもこの辺りの挙動をチェックする必要があります。今は1バイトのcharとして処理していますが、8バイトくらいのchar配列、つまり文字列として処理することになりそうです。
#C言語
キー入力の処理が出来ましたが、願わくば押し下げと解放も拾いたいところではあります。
ですが、ハードウェアが汎用ですし、キーボードが押されたことを検知しているのではなく、入力された文字コードを得る処理ですので、これ以上細かいことはもう少し勉強しないとダメかな。
socketの検証は場所も時間も潤沢に必要なので、もう少し本業を終わらせないと手を付けられませんが、キー入力によるコマンド処理をどうしようか考えています。
入力されたアルファベットをコマンドショートカットとし、数字は数字として扱うことを基本にしようと思います。キーに対して処理を割り付けるのではなく、得たASCIIテキストに対して処理を割り付ければ標準入力だろうがUARTだろうがsocketだろうが何でも良く、ハードウェアに合わせた入口さえ作ればその先の処理は同じでいけます。こうするのは専用入力端末が壊れた時に汎用のキーボードでも対応したいからです。
では、得たASCIIテキストをどう処理するかです。
入力されたASCIIテキストは文字列として普通に扱えばいいでしょう。画面にコマンド列を表示したいならそれを変換すればいいし、コマンドを実行するなら関数に文字列として投げればいい。
せめて、入力に制限をかけてありえない文字列にならないようにした方がいいでしょう。「コレの後にありえる入力はコレ」といった制限です。一種のルールマップを作り、条件に合致しない入力は応答しないかエラーを出すのです。数値に最大値の制限もかけたいですが、これはコマンド実行関数側の仕事であって入力時にチェックするものでもないかな?
#[Art-Net] #C言語
ですが、ハードウェアが汎用ですし、キーボードが押されたことを検知しているのではなく、入力された文字コードを得る処理ですので、これ以上細かいことはもう少し勉強しないとダメかな。
socketの検証は場所も時間も潤沢に必要なので、もう少し本業を終わらせないと手を付けられませんが、キー入力によるコマンド処理をどうしようか考えています。
入力されたアルファベットをコマンドショートカットとし、数字は数字として扱うことを基本にしようと思います。キーに対して処理を割り付けるのではなく、得たASCIIテキストに対して処理を割り付ければ標準入力だろうがUARTだろうがsocketだろうが何でも良く、ハードウェアに合わせた入口さえ作ればその先の処理は同じでいけます。こうするのは専用入力端末が壊れた時に汎用のキーボードでも対応したいからです。
では、得たASCIIテキストをどう処理するかです。
入力されたASCIIテキストは文字列として普通に扱えばいいでしょう。画面にコマンド列を表示したいならそれを変換すればいいし、コマンドを実行するなら関数に文字列として投げればいい。
せめて、入力に制限をかけてありえない文字列にならないようにした方がいいでしょう。「コレの後にありえる入力はコレ」といった制限です。一種のルールマップを作り、条件に合致しない入力は応答しないかエラーを出すのです。数値に最大値の制限もかけたいですが、これはコマンド実行関数側の仕事であって入力時にチェックするものでもないかな?
#[Art-Net] #C言語
ANSI-CというかLinuxのC言語ではキー入力は「待ち」が基本です。何かキーが入力されるとか、Enterを押されるまで処理が一時停止します。それでも構わないこともありますが、ダメな時もあります。
WindowsならDxLib.hを使って様々な入力を直に得ることが出来ますが、ANSI-Cの標準ライブラリには同様の物がありません。
そこで、キー入力があれば受け付け、無いなら飛ばして次に行く処理を作ってみました。キー入力はOSがバッファしているので、これを覗く形です。
Raspberry Pi 4B / Rasbian11_32bit(blueseye) / OS標準gcc
/* ---------------------------------
リアルタイムにキー入力をチェックする
--------------------------------- */
/* getcharはキー入力のキャッシュが空だと入力があるまで待つ。
キャッシュが無いなら無いでそのまま抜けたいが、タイムアウトを設定しても
抜けない。環境に寄るのかもしれないが、ioctl.hを用いることで
タイムアウトが実現できた。
*/
/* ライブラリ読み込み */
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
/* 定数設定 */
#define SET 1 // ioctl.readのモードをRawにするフラグ
#define RESET 0 // ipctl.readのモードをCanonicalに戻すフラグ
/* プロトタイプ宣言 */
int get_inkey( char *get_char ) ; // キー入力を取得する関数
// char *get_char 取得したキーコードを返すポインタ
int set_inkey( int mode_flag ) ; // キー入力のモードを設定する関数
// int mode_flag モードの設定フラグ 1(SET)=Rawモードにする / 0(CLR)=モードを戻す
/* テスト用main */
int main(void) {
char in_char = 0 ; // 入力されたキーコードを保存する変数
int ret_inkey = 0 ; // get_inkeyからの戻り値を保存する変数
/* コンソールの設定を初期化 */
set_inkey( SET ) ;
/* 無限ループで入力したキーを表示する */
for(;;) {
// get_inkeyから1文字取得
in_char = 0x00 ; // 事前初期化
ret_inkey = get_inkey( &in_char ) ; // キー取得
/* get_inkeyの戻り値を処理 */
// 戻り値が-1ならエラーなのでループから抜けて終了
if( ret_inkey == -1 ) {
break ;
// エンターキーが押されたならループを抜けて終了
} else if( in_char == 0x0A ) {
break ;
// 入力された文字を出力する・・・表示可能な文字
} else if( in_char >= 0x20 && in_char <= 0x7F ) {
printf( " %c<%02X>", in_char, in_char ) ;
// 入力された文字を出力する・・・表示不可能な文字
} else if( in_char > 0x00 && in_char <= 0xFF ) {
printf( " <%02X>", in_char ) ;
}
fflush( stdout ) ; // 画面表示のスタックを吐き出す
}
/* 正常終了 */
printf( "\n" ) ; // 念のための改行
set_inkey( RESET ) ; // コンソールの設定を元に戻す
return 0 ;
}
/* キー入力を取得する */
int get_inkey( char *get_char) {
char in_char = 0 ; // 入力されたキーを保持
char read_length = 0 ; // 読み込んだバイト数
// readを使って標準入力から1文字取得
read_length = read( 0, &in_char, 1 ) ;
// read_lengthが-1ならエラー、それ以外なら正常
if( read_length != -1 ) { // 正常なので取得値を返す
*get_char = in_char ;
} else { // エラーなので以上通知
*get_char = 0x00 ;
set_inkey( RESET ) ;
return -1 ;
}
return 0;
}
/* キー入力のモードを設定する */
int set_inkey( int mode_flag ) {
static struct termio tty_backup; // 変更前の設定を保持
static struct termio tty_change; // 変更後の設定を保持
// モードを設定する
if( mode_flag == SET ) {
// 現在の設定をスタック
ioctl(0, TCGETA, &tty_backup);
tty_change = tty_backup;
// 設定を変更
tty_change.c_lflag &= ~( ECHO | ICANON ) ; // エコーを止め、RAW モードへ変更
tty_change.c_cc[VMIN]= 0 ; // 0文字入力された時点で入力を受け取る
tty_change.c_cc[VTIME] = 0.01 ; // 何も入力がなくても1msec待つ (1 = 1/10sec)
ioctl( 0, TCSETAF, &tty_change ) ; // ここで設定を反映
// モードを戻す
} else if( mode_flag == 0 ) {
// スタックしていた設定に戻す
ioctl( 0, TCSETAF, &tty_backup ) ;
}
return 0;
}
同じ感じでgetcharを使ってもいいような情報が多かったのですが、RaspberryPiのblueseyeではこの方法でないとキー入力があるまで一時停止してしまいます。
Pythonでも同様の処理を書いたことがありますが、C言語の方が圧倒的にレスポンスがいいです。
矢印キーがANSIエスケープシーケンスのコードで取得できたことには驚きましたが、ファンクションキーも含め、getcharでは取得できないこともあるキーも取得出来て満足。
ただし、シフトキーなどの装飾キーを単体で取得することは出来ません。
#C言語
WindowsならDxLib.hを使って様々な入力を直に得ることが出来ますが、ANSI-Cの標準ライブラリには同様の物がありません。
そこで、キー入力があれば受け付け、無いなら飛ばして次に行く処理を作ってみました。キー入力はOSがバッファしているので、これを覗く形です。
Raspberry Pi 4B / Rasbian11_32bit(blueseye) / OS標準gcc
/* ---------------------------------
リアルタイムにキー入力をチェックする
--------------------------------- */
/* getcharはキー入力のキャッシュが空だと入力があるまで待つ。
キャッシュが無いなら無いでそのまま抜けたいが、タイムアウトを設定しても
抜けない。環境に寄るのかもしれないが、ioctl.hを用いることで
タイムアウトが実現できた。
*/
/* ライブラリ読み込み */
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
/* 定数設定 */
#define SET 1 // ioctl.readのモードをRawにするフラグ
#define RESET 0 // ipctl.readのモードをCanonicalに戻すフラグ
/* プロトタイプ宣言 */
int get_inkey( char *get_char ) ; // キー入力を取得する関数
// char *get_char 取得したキーコードを返すポインタ
int set_inkey( int mode_flag ) ; // キー入力のモードを設定する関数
// int mode_flag モードの設定フラグ 1(SET)=Rawモードにする / 0(CLR)=モードを戻す
/* テスト用main */
int main(void) {
char in_char = 0 ; // 入力されたキーコードを保存する変数
int ret_inkey = 0 ; // get_inkeyからの戻り値を保存する変数
/* コンソールの設定を初期化 */
set_inkey( SET ) ;
/* 無限ループで入力したキーを表示する */
for(;;) {
// get_inkeyから1文字取得
in_char = 0x00 ; // 事前初期化
ret_inkey = get_inkey( &in_char ) ; // キー取得
/* get_inkeyの戻り値を処理 */
// 戻り値が-1ならエラーなのでループから抜けて終了
if( ret_inkey == -1 ) {
break ;
// エンターキーが押されたならループを抜けて終了
} else if( in_char == 0x0A ) {
break ;
// 入力された文字を出力する・・・表示可能な文字
} else if( in_char >= 0x20 && in_char <= 0x7F ) {
printf( " %c<%02X>", in_char, in_char ) ;
// 入力された文字を出力する・・・表示不可能な文字
} else if( in_char > 0x00 && in_char <= 0xFF ) {
printf( " <%02X>", in_char ) ;
}
fflush( stdout ) ; // 画面表示のスタックを吐き出す
}
/* 正常終了 */
printf( "\n" ) ; // 念のための改行
set_inkey( RESET ) ; // コンソールの設定を元に戻す
return 0 ;
}
/* キー入力を取得する */
int get_inkey( char *get_char) {
char in_char = 0 ; // 入力されたキーを保持
char read_length = 0 ; // 読み込んだバイト数
// readを使って標準入力から1文字取得
read_length = read( 0, &in_char, 1 ) ;
// read_lengthが-1ならエラー、それ以外なら正常
if( read_length != -1 ) { // 正常なので取得値を返す
*get_char = in_char ;
} else { // エラーなので以上通知
*get_char = 0x00 ;
set_inkey( RESET ) ;
return -1 ;
}
return 0;
}
/* キー入力のモードを設定する */
int set_inkey( int mode_flag ) {
static struct termio tty_backup; // 変更前の設定を保持
static struct termio tty_change; // 変更後の設定を保持
// モードを設定する
if( mode_flag == SET ) {
// 現在の設定をスタック
ioctl(0, TCGETA, &tty_backup);
tty_change = tty_backup;
// 設定を変更
tty_change.c_lflag &= ~( ECHO | ICANON ) ; // エコーを止め、RAW モードへ変更
tty_change.c_cc[VMIN]= 0 ; // 0文字入力された時点で入力を受け取る
tty_change.c_cc[VTIME] = 0.01 ; // 何も入力がなくても1msec待つ (1 = 1/10sec)
ioctl( 0, TCSETAF, &tty_change ) ; // ここで設定を反映
// モードを戻す
} else if( mode_flag == 0 ) {
// スタックしていた設定に戻す
ioctl( 0, TCSETAF, &tty_backup ) ;
}
return 0;
}
同じ感じでgetcharを使ってもいいような情報が多かったのですが、RaspberryPiのblueseyeではこの方法でないとキー入力があるまで一時停止してしまいます。
Pythonでも同様の処理を書いたことがありますが、C言語の方が圧倒的にレスポンスがいいです。
矢印キーがANSIエスケープシーケンスのコードで取得できたことには驚きましたが、ファンクションキーも含め、getcharでは取得できないこともあるキーも取得出来て満足。
ただし、シフトキーなどの装飾キーを単体で取得することは出来ません。
#C言語
棚上げしていた本業に取り組んでおりますが、ここ10日ほどは残業することも精神的に追われることもない日常を送っているので心身共に回復しております。
VSCodeのオカゲですが、Pythonと同じ感覚でC言語を使えています。覚えなきゃならないことはまだまだ沢山ありますが、実行ファイルの完成まで1クリックですし、コンパイルエラーの説明がとても丁寧でわかりやすいし、コンパイルの時間もPythonのインタプリタ翻訳と大差ありませんので、方言違いのPythonを書いている様な使い勝手です。相当昔の記憶によりますが、リンカーの処理を勝手にやってくれるだけでも楽ってもんです。
C言語の取り扱いにはローレベルのコンピュータの知識と摩訶不思議な記述の習得が不可欠ですが、慣れてくるとPythonよりも書いてて楽しいですね。コンピュータを動かしているって実感がPICのアセンブラ並に強いからでしょうか。何をしているかも所要時間もわからない便利なだけのコマンドでハードウェアの制御をするとすべてが遠くに思えて面白くありません。この感覚が変人たる所以かもしれませんが、私の基本はハードウェアを直に動かすPICのアセンブラなんだと改めて思います。
この先数日は棚上げしていた本業(某劇場の舞台資料)の仕上げをせねばなりませんが、これが終われば半月くらいは自由研究に没頭できそうです。新規のご相談も頂いているので進捗次第ではわかりませんが。。。
もちろん、息抜きにC言語の学習と試作はしますよ。
#C言語
VSCodeのオカゲですが、Pythonと同じ感覚でC言語を使えています。覚えなきゃならないことはまだまだ沢山ありますが、実行ファイルの完成まで1クリックですし、コンパイルエラーの説明がとても丁寧でわかりやすいし、コンパイルの時間もPythonのインタプリタ翻訳と大差ありませんので、方言違いのPythonを書いている様な使い勝手です。相当昔の記憶によりますが、リンカーの処理を勝手にやってくれるだけでも楽ってもんです。
C言語の取り扱いにはローレベルのコンピュータの知識と摩訶不思議な記述の習得が不可欠ですが、慣れてくるとPythonよりも書いてて楽しいですね。コンピュータを動かしているって実感がPICのアセンブラ並に強いからでしょうか。何をしているかも所要時間もわからない便利なだけのコマンドでハードウェアの制御をするとすべてが遠くに思えて面白くありません。この感覚が変人たる所以かもしれませんが、私の基本はハードウェアを直に動かすPICのアセンブラなんだと改めて思います。
この先数日は棚上げしていた本業(某劇場の舞台資料)の仕上げをせねばなりませんが、これが終われば半月くらいは自由研究に没頭できそうです。新規のご相談も頂いているので進捗次第ではわかりませんが。。。
もちろん、息抜きにC言語の学習と試作はしますよ。
#C言語