タグ「C言語」を含む投稿[75件](2ページ目)
しばらく前に書いた Art-Net を扱うコードです。
今だったらこうするのにってところは多いですが、これをたたき台にしようかなと。
表示の都合で一部の文字を全角にしています。コピペしてお使いになる際はご注意ください。オレオレライブラリーやオレオレヘッダーが必要なのでこのソースだけでは動きませんけど、要点の参考になれば幸いかと。
/* --------------
Art-Net Engine
ae_main.c
-------------- */
// 汎用ライブラリをインクルード
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/shm.h>
//#include <netinet/in.h>
//#include <sys/ipc.h>
// 自作ライブラリをインクルード
#include "ae_main.h"
#include "std_inkey.h"
#include "std_dump.h"
// Main
int main( int argc, char *argv〔〕 ) {
// 汎用変数
// int i ;
// int ret = 0 ;
// 共有メモリ変数
ae_sharemem_t *ae_sharemem ;
int ae_sharememsize = sizeof( ae_sharemem_t ) ;
int ae_sharememID ;
//【開発用】共有メモリ領域の確保 上位モジュールが出来たらそちらで作る
ae_sharememID = shmget( IPC_PRIVATE, ae_sharememsize, SHM_R|SHM_W ) ;
// 共有メモリ定義
ae_sharemem = shmat( ae_sharememID, 0, SHM_R|SHM_W ) ;
// nsec時変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int start_nsec ; // 処理開始nsec時を保存
//【開発用】処理時間変数
unsigned long long int lap_nsec ; // 処理終了nsec時
static unsigned long long int max_nsec = 0 ; // 経過したnsecの最大値
// Art-Net 受信変数
int an_rx ; // 受信socket番号
struct sockaddr_in an_rx_addr ; // 受信先指定構造体
int an_rx_length ; // recvfromで得たデータ長
char an_rx_nic〔〕 = { "eth0" } ; // 受信NICの名称
char an_rx_buf〔 ADMX_BUF_SIZE 〕 ; // 受信バッファ ArtDMXの最大バイト長で設定
socklen_t an_rx_sin_size = ADMX_BUF_SIZE ; // recvfromに渡す受信バッファサイズ
struct sockaddr_in an_rx_from_addr ; // 送信元情報の構造体
artdmx_t artdmx_rx ; // 受信したArtDMXを保存する構造体
// Art-Net 送信変数
int an_tx ; // 送信socket番号
struct sockaddr_in an_tx_addr ; // 送信先指定構造体
char an_tx_nic〔〕 = { "eth1" } ; // 受信NICの名称
int yes = 1 ; // 送信をブロードキャストに設定する際のフラグ変数
artdmx_t artdmx_tx〔 ADMX_TX_UNIVRESES 〕 ; // 送信するArtDMXを保存する構造体配列
char an_tx_buf〔 ADMX_BUF_SIZE 〕 ; // 送信バッファ Art-DMXの最大バイト長で設定
int an_tx_count = 0 ; // 送信ユニバースカウンタ
unsigned long long int an_tx_nsec = 0 ; // 送信nsec時を保存
float an_tx_fps = 29.97 ; // 送信フレームレート
unsigned int an_tx_interval =
( unsigned int )( 1e9 / ADMX_TX_UNIVRESES / an_tx_fps ) ;
// Art-Netの受信ソケットを作成
an_rx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 受信ソケットのインスタンスを作成
setsockopt( an_rx, SOL_SOCKET, SO_BINDTODEVICE, // 受信NICを指定する
&an_rx_nic, sizeof( an_rx_nic ) );
an_rx_addr.sin_family = AF_INET ; // 受信をUDPに設定
an_rx_addr.sin_port = htons( AN_PORT ) ; // 受信ポート番号を設定
an_rx_addr.sin_addr.s_addr = INADDR_ANY ; // 送信元アドレスを無指定にする
bind( an_rx, ( struct sockaddr * ) &an_rx_addr, // 上記をインスタンスに設定する
sizeof( an_rx_addr ) ) ;
// Art-Netの送信ソケットを作成
an_tx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 送信ソケットのインスタンスを作成
an_tx_addr.sin_family = AF_INET ; // 送信をUDPに設定
an_tx_addr.sin_port = htons( AN_PORT ) ; // 送信ポート番号を設定
an_tx_addr.sin_addr.s_addr = inet_addr( "255.255.255.255" ) ; // 送信先アドレスを無指定にする
setsockopt( an_tx, SOL_SOCKET, SO_BINDTODEVICE, // 送信NICを指定する
&an_tx_nic, sizeof( an_tx_nic ) ) ;
setsockopt( an_tx, SOL_SOCKET, SO_BROADCAST, // 送信をブロードキャストに設定
( char * )&yes, sizeof( yes ) ) ;
// std_inkey 用変数
int key_code_length = 0 ;
int key_code_length_st = 0 ;
char key_code〔 32 〕 = { 0x00 } ;
char key_code_st〔 32 〕 = { 0x00 } ;
// std_inkey を開始
set_inkey( SET ) ;
// ***** Art-Netの受送信と表示 *****
printf( "\e〔2J\e〔?25l" ) ; // 画面を消去 カーソル非表示 \e〔?25l カーソル非表示
while( strcmp( key_code, _KEY_F12 ) != 0 ) {
// ------ 開始nsec時を取得 ------
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
start_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
// --------- キー入力 ----------
key_code_length = get_inkey( key_code ) ; // キー入力を取得
if( strcmp( key_code, "\x20" ) == 0 ) max_nsec = 0 ; //【開発用】キー入力が〔SP〕なら実行時間計測の最大値をクリア
if( key_code_length > 0 ) { // キー入力を別関数に渡すためのスタック
key_code_length_st = key_code_length ;
memcpy( key_code_st, key_code, sizeof( key_code ) ) ;
}
// --------- 受 信 -----------
memset( an_rx_buf, 0, ADMX_BUF_SIZE ) ; // 受信バッファのクリア
an_rx_length = recvfrom( an_rx, an_rx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // an_rx_from_addrには送信元情報が入る
( struct sockaddr * )&an_rx_from_addr, &an_rx_sin_size ) ;
// 受信がある場合
if( an_rx_length > 0 ) { // an_rx_lengthは受信値のバイト長
// 送信元IPアドレスとポートを抽出し、構造体artdmx_rxに保存
inet_ntop( AF_INET, &an_rx_from_addr.sin_addr, // 受信データから送信元IPアドレスを抽出保存
artdmx_rx.senderip, sizeof( artdmx_rx.senderip ) ) ;
strcpy( artdmx_rx.senderip, inet_ntoa( an_rx_from_addr.sin_addr ) ) ;
artdmx_rx.senderport = ntohs( an_rx_from_addr.sin_port ) ; // 受信データから送信元ポートを抽出保存
artdmx_rx.recv_nsec = start_nsec ; // 受信日時を保存
// 受信したArtDMXをデコード
if( artdmx_decode( an_rx_buf, &artdmx_rx ) >= 0 ) // ArtDMXなら送信スタックに保存
memcpy( &artdmx_tx〔 artdmx_rx.universe 〕, // ArtDMX以外なら読み飛ばし
&artdmx_rx, sizeof( artdmx_t ) ) ;
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
}
// --------- 送 信 -----------
else if( start_nsec - an_tx_nsec > ( unsigned int )( an_tx_interval ) ) {
artdmx_encode( an_tx_buf, &artdmx_tx〔 an_tx_count 〕 ) ; // 送信データをエンコード
sendto( an_tx, an_tx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // 送信実行
( struct sockaddr * )&an_tx_addr, sizeof( an_tx_addr ) ) ;
/*
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
*/
//【開発用】受信データの表示
artdmx_check_disp( &artdmx_tx〔 an_tx_count 〕, // 送信データの仮表示
key_code_st, &key_code_length_st, &lap_nsec, &max_nsec ) ;
// 送信事後処理
an_tx_nsec = start_nsec ; // 送信インターバル・次回用のnsec取得
if( ++an_tx_count > 7 ) an_tx_count = 0 ; // 送信ルートカウンタインクリメント
}
// 待ち時間 無駄にループしても処理負荷が増えるだけなので少しお休み
usleep( 1 ) ;
}
// --------- 終 了 ----------
set_inkey( RESET ) ; // std_inkeyを終了
close( an_rx ) ; // 受信ソケットをクローズ
close( an_tx ) ; // 送信ソケットをクローズ
printf( "\e〔?25h\e〔0m\n" ) ; // コンソール画面を戻す \e〔?25h カーソルの表示
shmdt( ae_sharemem ) ; // 共有メモリの切り離し
//【開発用】上位モジュールが出来たらそちらで処理する
shmctl( ae_sharememID, IPC_RMID, 0) ; // 共有メモリ領域開放
return 0 ;
}
// ArtDMXのデコード関数
int artdmx_decode( char *an_rx_buf, artdmx_t *artdmx_rx ) {
int i ;
char int_st〔 sizeof( int ) 〕 ;
// IDを抽出
for( i = 0; i < 8; i++ ) { // bufの先頭から8バイト
artdmx_rx->id〔 i 〕 = an_rx_buf〔 i 〕 ;
}
// OpCodeを抽出
int_st〔 0 〕 = an_rx_buf〔 8 〕 ;
int_st〔 1 〕 = an_rx_buf〔 9 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->opcode = *( ( int * )&int_st〔 0 〕) ;
if( ! (strcmp( artdmx_rx->id, "Art-Net" ) == 0 // ArtDMXかチェックし、そうでなければエラーを返して終了
&& artdmx_rx->opcode == 0x5000 ) ) {
return -1 ;
}
// ProVerを抽出
int_st〔 0 〕 = an_rx_buf〔 11 〕 ;
int_st〔 1 〕 = an_rx_buf〔 10 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->prover = *( ( int * )&int_st〔 0 〕) ;
// Sequenceを抽出
artdmx_rx->sequence = an_rx_buf〔 12 〕 ;
// Physicalを抽出
artdmx_rx->physical = an_rx_buf〔 13 〕 ;
// Universeを抽出
artdmx_rx->subnet = an_rx_buf〔 14 〕 / 0x10 ;
artdmx_rx->universe = an_rx_buf〔 14 〕 % 0x10 ;
artdmx_rx->net = an_rx_buf〔 15 〕 ;
// Lengthを抽出
int_st〔 0 〕 = an_rx_buf〔 17 〕 ;
int_st〔 1 〕 = an_rx_buf〔 16 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->length = *( ( int * )&int_st〔0〕) ;
// Dataを抽出
for( i = 0; i < artdmx_rx->length; i++ ) {
artdmx_rx->data〔 i 〕 = an_rx_buf〔 i + 18 〕 ;
}
if( artdmx_rx->length < 512 ) {
for( i = artdmx_rx->length; i < 512; i++ ) {
artdmx_rx->data〔 i 〕 = 0x00 ;
}
}
return 0 ;
}
// ArtDMXのエンコード関数
int artdmx_encode( char *an_tx_buf, artdmx_t *artdmx_tx ) {
int i ;
// IDを設定
for( i = 0; i < 8; i++ ) {
an_tx_buf〔 i 〕 = artdmx_tx->id〔 i 〕 ;
}
// OpCodeを設定
an_tx_buf〔 8 〕 = (char)( artdmx_tx->opcode % 0x100 ) ;
an_tx_buf〔 9 〕 = (char)( artdmx_tx->opcode / 0x100 ) ;
// ProVerを設定
an_tx_buf〔 10 〕 = (char)( artdmx_tx->prover / 0x100 ) ;
an_tx_buf〔 11 〕 = (char)( artdmx_tx->prover % 0x100 ) ;
// Sequenceを設定
an_tx_buf〔 12 〕 = (char) artdmx_tx->sequence ;
// Physicalを設定
an_tx_buf〔 13 〕 = (char) artdmx_tx->physical ;
// SubUniを設定
an_tx_buf〔 14 〕 = (char)( artdmx_tx->subnet * 0x100 + artdmx_tx->universe ) ;
// Netを設定
an_tx_buf〔 15 〕 = (char)artdmx_tx->net ;
// Lengthを設定
an_tx_buf〔 16 〕 = (char)( artdmx_tx->length / 0x100 ) ;
an_tx_buf〔 17 〕 = (char)( artdmx_tx->length % 0x100 ) ;
// Dataを設定
memcpy( an_tx_buf + 18, artdmx_tx->data, 512 ) ;
return 0 ;
}
// ArtDMXのチェック用画面表示
int artdmx_check_disp( artdmx_t *artdmx, char *key_code_st, int *key_code_length_st,
unsigned long long int *val1, unsigned long long int *val2 ) {
// 汎用変数
//int i ;
// タイムカウント変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int now_nsec ; // 現在nsecを保存
static unsigned long long int prev_nsec = 0 ; // 過去nsecを保存
// データ管理用
static artdmx_t artdmx_disp ;
// std_dump 用変数
static int first_line = 0 ; // 表示開始行数
// 表示制御変数
struct winsize ws ;
static int col = 20 ;
static int row = 100 ;
static int net = 0 ;
static int subnet = 0 ;
static int universe = 0 ;
// カーソルキー入力
if( *key_code_length_st > 0 ) {
if( strcmp( key_code_st, _KEY_RIGHT ) == 0 ) {
if( ++universe > 7 ) universe = 7 ;
}
if( strcmp( key_code_st, _KEY_LEFT ) == 0 ) {
if( --universe < 0 ) universe = 0 ;
}
if( strcmp( key_code_st, _KEY_UP ) == 0 ) {
first_line-- ;
}
if( strcmp( key_code_st, _KEY_DOWN ) == 0 ) {
first_line++ ;
}
*key_code_length_st = 0 ;
}
// 対象ユニバースのデータをスタック
if( ( artdmx->net == net ) && ( artdmx->subnet == subnet ) && ( artdmx->universe == universe ) ) {
memcpy( &artdmx_disp, artdmx, sizeof( artdmx_t ) ) ;
}
// タイムカウント
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
now_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
if( now_nsec - prev_nsec < 5e7) return -1 ; // 経過時間が不足なら何もせずリターン
prev_nsec = now_nsec ; // 過去nsecを現在値としておく
// 画面のテキスト表示サイズを取得
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) ; // 画面サイズを取得 幅=ws.ws_col 高=ws.ws_row
if( row + 4 > ws.ws_row ) row = ws.ws_row - 4 ; // dumpの行数制限
// artdmxを表示
printf( "\e〔0;0H" ) ; // カーソルを左上に
printf( "Sender: %s:%d", artdmx_disp.senderip, artdmx_disp.senderport ) ;
printf( "\e〔28GID: %s", artdmx_disp.id ) ;
printf( "\e〔41GOpCode: 0x%04X", artdmx_disp.opcode ) ;
printf( "\e〔57GProVer: 0x%04X", artdmx_disp.prover ) ;
printf( "\e〔0K\nSequence: 0x%04X", artdmx_disp.sequence ) ;
printf( "\e〔19GPhysical: 0x%04X", artdmx_disp.physical ) ;
printf( "\e〔37GLength: %d", artdmx_disp.length ) ;
printf( "\e〔50G< Net:%d SubNet:%d Universe:%d >\e〔0K\n", artdmx_disp.net, artdmx_disp.subnet, artdmx_disp.universe ) ;
dump_print( artdmx_disp.data, artdmx_disp.length, col, row, &first_line, 0 ) ;
printf( "%8lld /%8lld nsec", *val1, *val2 ) ;
//fflush( stdout ) ;
// 終了
return 0 ;
}
#[Art-Net] #C言語
今だったらこうするのにってところは多いですが、これをたたき台にしようかなと。
表示の都合で一部の文字を全角にしています。コピペしてお使いになる際はご注意ください。オレオレライブラリーやオレオレヘッダーが必要なのでこのソースだけでは動きませんけど、要点の参考になれば幸いかと。
/* --------------
Art-Net Engine
ae_main.c
-------------- */
// 汎用ライブラリをインクルード
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/shm.h>
//#include <netinet/in.h>
//#include <sys/ipc.h>
// 自作ライブラリをインクルード
#include "ae_main.h"
#include "std_inkey.h"
#include "std_dump.h"
// Main
int main( int argc, char *argv〔〕 ) {
// 汎用変数
// int i ;
// int ret = 0 ;
// 共有メモリ変数
ae_sharemem_t *ae_sharemem ;
int ae_sharememsize = sizeof( ae_sharemem_t ) ;
int ae_sharememID ;
//【開発用】共有メモリ領域の確保 上位モジュールが出来たらそちらで作る
ae_sharememID = shmget( IPC_PRIVATE, ae_sharememsize, SHM_R|SHM_W ) ;
// 共有メモリ定義
ae_sharemem = shmat( ae_sharememID, 0, SHM_R|SHM_W ) ;
// nsec時変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int start_nsec ; // 処理開始nsec時を保存
//【開発用】処理時間変数
unsigned long long int lap_nsec ; // 処理終了nsec時
static unsigned long long int max_nsec = 0 ; // 経過したnsecの最大値
// Art-Net 受信変数
int an_rx ; // 受信socket番号
struct sockaddr_in an_rx_addr ; // 受信先指定構造体
int an_rx_length ; // recvfromで得たデータ長
char an_rx_nic〔〕 = { "eth0" } ; // 受信NICの名称
char an_rx_buf〔 ADMX_BUF_SIZE 〕 ; // 受信バッファ ArtDMXの最大バイト長で設定
socklen_t an_rx_sin_size = ADMX_BUF_SIZE ; // recvfromに渡す受信バッファサイズ
struct sockaddr_in an_rx_from_addr ; // 送信元情報の構造体
artdmx_t artdmx_rx ; // 受信したArtDMXを保存する構造体
// Art-Net 送信変数
int an_tx ; // 送信socket番号
struct sockaddr_in an_tx_addr ; // 送信先指定構造体
char an_tx_nic〔〕 = { "eth1" } ; // 受信NICの名称
int yes = 1 ; // 送信をブロードキャストに設定する際のフラグ変数
artdmx_t artdmx_tx〔 ADMX_TX_UNIVRESES 〕 ; // 送信するArtDMXを保存する構造体配列
char an_tx_buf〔 ADMX_BUF_SIZE 〕 ; // 送信バッファ Art-DMXの最大バイト長で設定
int an_tx_count = 0 ; // 送信ユニバースカウンタ
unsigned long long int an_tx_nsec = 0 ; // 送信nsec時を保存
float an_tx_fps = 29.97 ; // 送信フレームレート
unsigned int an_tx_interval =
( unsigned int )( 1e9 / ADMX_TX_UNIVRESES / an_tx_fps ) ;
// Art-Netの受信ソケットを作成
an_rx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 受信ソケットのインスタンスを作成
setsockopt( an_rx, SOL_SOCKET, SO_BINDTODEVICE, // 受信NICを指定する
&an_rx_nic, sizeof( an_rx_nic ) );
an_rx_addr.sin_family = AF_INET ; // 受信をUDPに設定
an_rx_addr.sin_port = htons( AN_PORT ) ; // 受信ポート番号を設定
an_rx_addr.sin_addr.s_addr = INADDR_ANY ; // 送信元アドレスを無指定にする
bind( an_rx, ( struct sockaddr * ) &an_rx_addr, // 上記をインスタンスに設定する
sizeof( an_rx_addr ) ) ;
// Art-Netの送信ソケットを作成
an_tx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 送信ソケットのインスタンスを作成
an_tx_addr.sin_family = AF_INET ; // 送信をUDPに設定
an_tx_addr.sin_port = htons( AN_PORT ) ; // 送信ポート番号を設定
an_tx_addr.sin_addr.s_addr = inet_addr( "255.255.255.255" ) ; // 送信先アドレスを無指定にする
setsockopt( an_tx, SOL_SOCKET, SO_BINDTODEVICE, // 送信NICを指定する
&an_tx_nic, sizeof( an_tx_nic ) ) ;
setsockopt( an_tx, SOL_SOCKET, SO_BROADCAST, // 送信をブロードキャストに設定
( char * )&yes, sizeof( yes ) ) ;
// std_inkey 用変数
int key_code_length = 0 ;
int key_code_length_st = 0 ;
char key_code〔 32 〕 = { 0x00 } ;
char key_code_st〔 32 〕 = { 0x00 } ;
// std_inkey を開始
set_inkey( SET ) ;
// ***** Art-Netの受送信と表示 *****
printf( "\e〔2J\e〔?25l" ) ; // 画面を消去 カーソル非表示 \e〔?25l カーソル非表示
while( strcmp( key_code, _KEY_F12 ) != 0 ) {
// ------ 開始nsec時を取得 ------
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
start_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
// --------- キー入力 ----------
key_code_length = get_inkey( key_code ) ; // キー入力を取得
if( strcmp( key_code, "\x20" ) == 0 ) max_nsec = 0 ; //【開発用】キー入力が〔SP〕なら実行時間計測の最大値をクリア
if( key_code_length > 0 ) { // キー入力を別関数に渡すためのスタック
key_code_length_st = key_code_length ;
memcpy( key_code_st, key_code, sizeof( key_code ) ) ;
}
// --------- 受 信 -----------
memset( an_rx_buf, 0, ADMX_BUF_SIZE ) ; // 受信バッファのクリア
an_rx_length = recvfrom( an_rx, an_rx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // an_rx_from_addrには送信元情報が入る
( struct sockaddr * )&an_rx_from_addr, &an_rx_sin_size ) ;
// 受信がある場合
if( an_rx_length > 0 ) { // an_rx_lengthは受信値のバイト長
// 送信元IPアドレスとポートを抽出し、構造体artdmx_rxに保存
inet_ntop( AF_INET, &an_rx_from_addr.sin_addr, // 受信データから送信元IPアドレスを抽出保存
artdmx_rx.senderip, sizeof( artdmx_rx.senderip ) ) ;
strcpy( artdmx_rx.senderip, inet_ntoa( an_rx_from_addr.sin_addr ) ) ;
artdmx_rx.senderport = ntohs( an_rx_from_addr.sin_port ) ; // 受信データから送信元ポートを抽出保存
artdmx_rx.recv_nsec = start_nsec ; // 受信日時を保存
// 受信したArtDMXをデコード
if( artdmx_decode( an_rx_buf, &artdmx_rx ) >= 0 ) // ArtDMXなら送信スタックに保存
memcpy( &artdmx_tx〔 artdmx_rx.universe 〕, // ArtDMX以外なら読み飛ばし
&artdmx_rx, sizeof( artdmx_t ) ) ;
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
}
// --------- 送 信 -----------
else if( start_nsec - an_tx_nsec > ( unsigned int )( an_tx_interval ) ) {
artdmx_encode( an_tx_buf, &artdmx_tx〔 an_tx_count 〕 ) ; // 送信データをエンコード
sendto( an_tx, an_tx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // 送信実行
( struct sockaddr * )&an_tx_addr, sizeof( an_tx_addr ) ) ;
/*
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
*/
//【開発用】受信データの表示
artdmx_check_disp( &artdmx_tx〔 an_tx_count 〕, // 送信データの仮表示
key_code_st, &key_code_length_st, &lap_nsec, &max_nsec ) ;
// 送信事後処理
an_tx_nsec = start_nsec ; // 送信インターバル・次回用のnsec取得
if( ++an_tx_count > 7 ) an_tx_count = 0 ; // 送信ルートカウンタインクリメント
}
// 待ち時間 無駄にループしても処理負荷が増えるだけなので少しお休み
usleep( 1 ) ;
}
// --------- 終 了 ----------
set_inkey( RESET ) ; // std_inkeyを終了
close( an_rx ) ; // 受信ソケットをクローズ
close( an_tx ) ; // 送信ソケットをクローズ
printf( "\e〔?25h\e〔0m\n" ) ; // コンソール画面を戻す \e〔?25h カーソルの表示
shmdt( ae_sharemem ) ; // 共有メモリの切り離し
//【開発用】上位モジュールが出来たらそちらで処理する
shmctl( ae_sharememID, IPC_RMID, 0) ; // 共有メモリ領域開放
return 0 ;
}
// ArtDMXのデコード関数
int artdmx_decode( char *an_rx_buf, artdmx_t *artdmx_rx ) {
int i ;
char int_st〔 sizeof( int ) 〕 ;
// IDを抽出
for( i = 0; i < 8; i++ ) { // bufの先頭から8バイト
artdmx_rx->id〔 i 〕 = an_rx_buf〔 i 〕 ;
}
// OpCodeを抽出
int_st〔 0 〕 = an_rx_buf〔 8 〕 ;
int_st〔 1 〕 = an_rx_buf〔 9 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->opcode = *( ( int * )&int_st〔 0 〕) ;
if( ! (strcmp( artdmx_rx->id, "Art-Net" ) == 0 // ArtDMXかチェックし、そうでなければエラーを返して終了
&& artdmx_rx->opcode == 0x5000 ) ) {
return -1 ;
}
// ProVerを抽出
int_st〔 0 〕 = an_rx_buf〔 11 〕 ;
int_st〔 1 〕 = an_rx_buf〔 10 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->prover = *( ( int * )&int_st〔 0 〕) ;
// Sequenceを抽出
artdmx_rx->sequence = an_rx_buf〔 12 〕 ;
// Physicalを抽出
artdmx_rx->physical = an_rx_buf〔 13 〕 ;
// Universeを抽出
artdmx_rx->subnet = an_rx_buf〔 14 〕 / 0x10 ;
artdmx_rx->universe = an_rx_buf〔 14 〕 % 0x10 ;
artdmx_rx->net = an_rx_buf〔 15 〕 ;
// Lengthを抽出
int_st〔 0 〕 = an_rx_buf〔 17 〕 ;
int_st〔 1 〕 = an_rx_buf〔 16 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->length = *( ( int * )&int_st〔0〕) ;
// Dataを抽出
for( i = 0; i < artdmx_rx->length; i++ ) {
artdmx_rx->data〔 i 〕 = an_rx_buf〔 i + 18 〕 ;
}
if( artdmx_rx->length < 512 ) {
for( i = artdmx_rx->length; i < 512; i++ ) {
artdmx_rx->data〔 i 〕 = 0x00 ;
}
}
return 0 ;
}
// ArtDMXのエンコード関数
int artdmx_encode( char *an_tx_buf, artdmx_t *artdmx_tx ) {
int i ;
// IDを設定
for( i = 0; i < 8; i++ ) {
an_tx_buf〔 i 〕 = artdmx_tx->id〔 i 〕 ;
}
// OpCodeを設定
an_tx_buf〔 8 〕 = (char)( artdmx_tx->opcode % 0x100 ) ;
an_tx_buf〔 9 〕 = (char)( artdmx_tx->opcode / 0x100 ) ;
// ProVerを設定
an_tx_buf〔 10 〕 = (char)( artdmx_tx->prover / 0x100 ) ;
an_tx_buf〔 11 〕 = (char)( artdmx_tx->prover % 0x100 ) ;
// Sequenceを設定
an_tx_buf〔 12 〕 = (char) artdmx_tx->sequence ;
// Physicalを設定
an_tx_buf〔 13 〕 = (char) artdmx_tx->physical ;
// SubUniを設定
an_tx_buf〔 14 〕 = (char)( artdmx_tx->subnet * 0x100 + artdmx_tx->universe ) ;
// Netを設定
an_tx_buf〔 15 〕 = (char)artdmx_tx->net ;
// Lengthを設定
an_tx_buf〔 16 〕 = (char)( artdmx_tx->length / 0x100 ) ;
an_tx_buf〔 17 〕 = (char)( artdmx_tx->length % 0x100 ) ;
// Dataを設定
memcpy( an_tx_buf + 18, artdmx_tx->data, 512 ) ;
return 0 ;
}
// ArtDMXのチェック用画面表示
int artdmx_check_disp( artdmx_t *artdmx, char *key_code_st, int *key_code_length_st,
unsigned long long int *val1, unsigned long long int *val2 ) {
// 汎用変数
//int i ;
// タイムカウント変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int now_nsec ; // 現在nsecを保存
static unsigned long long int prev_nsec = 0 ; // 過去nsecを保存
// データ管理用
static artdmx_t artdmx_disp ;
// std_dump 用変数
static int first_line = 0 ; // 表示開始行数
// 表示制御変数
struct winsize ws ;
static int col = 20 ;
static int row = 100 ;
static int net = 0 ;
static int subnet = 0 ;
static int universe = 0 ;
// カーソルキー入力
if( *key_code_length_st > 0 ) {
if( strcmp( key_code_st, _KEY_RIGHT ) == 0 ) {
if( ++universe > 7 ) universe = 7 ;
}
if( strcmp( key_code_st, _KEY_LEFT ) == 0 ) {
if( --universe < 0 ) universe = 0 ;
}
if( strcmp( key_code_st, _KEY_UP ) == 0 ) {
first_line-- ;
}
if( strcmp( key_code_st, _KEY_DOWN ) == 0 ) {
first_line++ ;
}
*key_code_length_st = 0 ;
}
// 対象ユニバースのデータをスタック
if( ( artdmx->net == net ) && ( artdmx->subnet == subnet ) && ( artdmx->universe == universe ) ) {
memcpy( &artdmx_disp, artdmx, sizeof( artdmx_t ) ) ;
}
// タイムカウント
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
now_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
if( now_nsec - prev_nsec < 5e7) return -1 ; // 経過時間が不足なら何もせずリターン
prev_nsec = now_nsec ; // 過去nsecを現在値としておく
// 画面のテキスト表示サイズを取得
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) ; // 画面サイズを取得 幅=ws.ws_col 高=ws.ws_row
if( row + 4 > ws.ws_row ) row = ws.ws_row - 4 ; // dumpの行数制限
// artdmxを表示
printf( "\e〔0;0H" ) ; // カーソルを左上に
printf( "Sender: %s:%d", artdmx_disp.senderip, artdmx_disp.senderport ) ;
printf( "\e〔28GID: %s", artdmx_disp.id ) ;
printf( "\e〔41GOpCode: 0x%04X", artdmx_disp.opcode ) ;
printf( "\e〔57GProVer: 0x%04X", artdmx_disp.prover ) ;
printf( "\e〔0K\nSequence: 0x%04X", artdmx_disp.sequence ) ;
printf( "\e〔19GPhysical: 0x%04X", artdmx_disp.physical ) ;
printf( "\e〔37GLength: %d", artdmx_disp.length ) ;
printf( "\e〔50G< Net:%d SubNet:%d Universe:%d >\e〔0K\n", artdmx_disp.net, artdmx_disp.subnet, artdmx_disp.universe ) ;
dump_print( artdmx_disp.data, artdmx_disp.length, col, row, &first_line, 0 ) ;
printf( "%8lld /%8lld nsec", *val1, *val2 ) ;
//fflush( stdout ) ;
// 終了
return 0 ;
}
#[Art-Net] #C言語
今年も師走です。
現場は現地照明をもう1件やれば終わり。今期の年末年始は人並みにユックリ出来そうです。
そんな時は机上の勉強を進めたいものです。物理的な加工や開発・設計ではなくお勉強。特にC言語の勉強です。
一応書けるのですが、ポインタや構造体についてボンヤリしているところを整理したい。Art-Net関連には特に重要な基礎ですから抑えないといけません。
#C言語
現場は現地照明をもう1件やれば終わり。今期の年末年始は人並みにユックリ出来そうです。
そんな時は机上の勉強を進めたいものです。物理的な加工や開発・設計ではなくお勉強。特にC言語の勉強です。
一応書けるのですが、ポインタや構造体についてボンヤリしているところを整理したい。Art-Net関連には特に重要な基礎ですから抑えないといけません。
#C言語
セマフォの使い方が見えたので構造の重要な要素は獲得出来ました。
構成ですが、起動し mmap や pipe を定義し子プロセスの状態を監視する親プロセス「ArtNetPatch」、ユーザーの操作の相手やデータを画面表示をする子プロセス「ArtNetConsole」、Art-Netを送受信しパッチ処理やプロファイルカーブ処理を行う子プロセス「ArtNetEngine」の3本柱にしようと思います。
子プロセスの生成は fork を使います。fork ならば mmap、pipe、semaqhoe を子プロセスに渡すのが楽です。メモリの消費は多くなるようですが、実体の複製ですからポインタ祭りにならずに済みます。メモリの消費が多いと言っても、私が作るソフトウェアの規模などたかが知れていますので RaspberryPiCM4 の8GBモデルなら楽勝でしょう。
こういった構造は面倒な気もしますが、プロセス構造をシッカリ作っておけば開発が楽になると思いますし、何よりも「ArtNetEngine」の処理にリソースを集中したいのです。状態監視やユーザー相手は100msec毎で十分ですが、Art-Net の処理は5msec毎が欲しいのです。分散処理というより、求めるラップが違う処理を明確に分けたいのです。
処理時間を意識してしまうのは PIC マイコンからのクセですが、RaspberryPiCM4 で8ユニバースのパッチ処理をするならダイエットが大事かと。
#[Art-net] #C言語
構成ですが、起動し mmap や pipe を定義し子プロセスの状態を監視する親プロセス「ArtNetPatch」、ユーザーの操作の相手やデータを画面表示をする子プロセス「ArtNetConsole」、Art-Netを送受信しパッチ処理やプロファイルカーブ処理を行う子プロセス「ArtNetEngine」の3本柱にしようと思います。
子プロセスの生成は fork を使います。fork ならば mmap、pipe、semaqhoe を子プロセスに渡すのが楽です。メモリの消費は多くなるようですが、実体の複製ですからポインタ祭りにならずに済みます。メモリの消費が多いと言っても、私が作るソフトウェアの規模などたかが知れていますので RaspberryPiCM4 の8GBモデルなら楽勝でしょう。
こういった構造は面倒な気もしますが、プロセス構造をシッカリ作っておけば開発が楽になると思いますし、何よりも「ArtNetEngine」の処理にリソースを集中したいのです。状態監視やユーザー相手は100msec毎で十分ですが、Art-Net の処理は5msec毎が欲しいのです。分散処理というより、求めるラップが違う処理を明確に分けたいのです。
処理時間を意識してしまうのは PIC マイコンからのクセですが、RaspberryPiCM4 で8ユニバースのパッチ処理をするならダイエットが大事かと。
#[Art-net] #C言語
ArtNet-Patch で共有メモリの mmap を使うために セマフォ を勉強しています。mmap へのアクセスを排他的にするためです。
ただ、プロセス間のセマフォを使うような人は上級者が多いのか解説ページを見てもわかりにくい。
POSIX系のセマフォは物凄くシンプルな感じがしますが、とどのつまりがわからない。
man もわかっている人向けなのか引数の記述があるだけ。
もっと勉強です。
追記
なんて愚痴っていたらよい記事がありました。
開始が sem_init だったり sem_open だったりするけど、同じことみたいですからヘッダファイルのプロトタイプ記述を読めば良さそう。
「sem_overview - POSIX セマフォの概要」
「man-pages-ja sem_init(3)」
次の記事は、パーミッションにおける umask のことが主題ですが、セマフォの使い方がよくわかる。シンプルな記述を示してくれるこんな記事が嬉しい。
「sem_openで、Permission denied になる」
わかった気になった。
#[Art-net] #C言語
ただ、プロセス間のセマフォを使うような人は上級者が多いのか解説ページを見てもわかりにくい。
POSIX系のセマフォは物凄くシンプルな感じがしますが、とどのつまりがわからない。
man もわかっている人向けなのか引数の記述があるだけ。
もっと勉強です。
追記
なんて愚痴っていたらよい記事がありました。
開始が sem_init だったり sem_open だったりするけど、同じことみたいですからヘッダファイルのプロトタイプ記述を読めば良さそう。
「sem_overview - POSIX セマフォの概要」
「man-pages-ja sem_init(3)」
次の記事は、パーミッションにおける umask のことが主題ですが、セマフォの使い方がよくわかる。シンプルな記述を示してくれるこんな記事が嬉しい。
「sem_openで、Permission denied になる」
わかった気になった。
#[Art-net] #C言語
少し時間が空いたので ArtNet-Patch を考えていました。
部分はなんとなく出来ているので、全体の構造をどうするか。
まずプロセスを分けます。シングルプロセスではたぶんダメ。画面やキーボード操作を親プロセスにし、Art-Net の受信、パッチ処理、送信などを子プロセスとしようかと。子プロセスはタイムアウトする様にし、親プロセスからのリフレッシュが一定時間無ければ自動的に閉じることにします。こうしておけば親プロセスが飛んでも子プロセスがゾンビにならずいいかなと。操作が飛んでも Art-Net は生きている方がいいとする考え方もありますが、タイムアウト処理をコメントにすればそうなります。少なくとも開発中はタイムアウトした方がいいでしょう。
プロセス間は mmap や Pipe で繋ぎます。データには mmap を用い、コマンドには Pipe を用いるイメージです。
子プロセスの Art-Net 処理をイメージしながら表示画面やキーボード操作を考えてみましょう。
#[Art-net] #C言語
部分はなんとなく出来ているので、全体の構造をどうするか。
まずプロセスを分けます。シングルプロセスではたぶんダメ。画面やキーボード操作を親プロセスにし、Art-Net の受信、パッチ処理、送信などを子プロセスとしようかと。子プロセスはタイムアウトする様にし、親プロセスからのリフレッシュが一定時間無ければ自動的に閉じることにします。こうしておけば親プロセスが飛んでも子プロセスがゾンビにならずいいかなと。操作が飛んでも Art-Net は生きている方がいいとする考え方もありますが、タイムアウト処理をコメントにすればそうなります。少なくとも開発中はタイムアウトした方がいいでしょう。
プロセス間は mmap や Pipe で繋ぎます。データには mmap を用い、コマンドには Pipe を用いるイメージです。
子プロセスの Art-Net 処理をイメージしながら表示画面やキーボード操作を考えてみましょう。
#[Art-net] #C言語
ncurses はウィンドウも定義出来ます。
今時のウィンドウとは違い、画面の中に子領域を作るイメージです。この領域内で文字位置が指定出来ます。子領域の左上を文字位置の 0,0 として扱えます。
ダイアログウィンドウを描くことも出来ますし、画面の区分けにも使えます。
今ネタになっている ArtNet-Patch では、すべてのレイアウトに共通する要素をメイン画面に描き、機能ごとの要素をウィンドウにして描こうかなと。表示位置のオフセット管理が楽になりますし、複数のレイアウトで共通する要素は使いまわしがしやすくなります。
ncurses はキー入力も受け取れますので、ユーザーの相手にする部分は ncurses で全て作れそうです。
後は string の使い方を頭に刷り込めば画面は作れるかなと。C言語の欠点は文字列の扱い難さですが、文字列は char の配列であることを頭に染み込ませてから string を勉強しなおしましょう。
アホかと思うくらいC言語について検索してきたので、これまで ncurses を知らなかったのは何故だろう。
#[Art-net] #C言語
今時のウィンドウとは違い、画面の中に子領域を作るイメージです。この領域内で文字位置が指定出来ます。子領域の左上を文字位置の 0,0 として扱えます。
ダイアログウィンドウを描くことも出来ますし、画面の区分けにも使えます。
今ネタになっている ArtNet-Patch では、すべてのレイアウトに共通する要素をメイン画面に描き、機能ごとの要素をウィンドウにして描こうかなと。表示位置のオフセット管理が楽になりますし、複数のレイアウトで共通する要素は使いまわしがしやすくなります。
ncurses はキー入力も受け取れますので、ユーザーの相手にする部分は ncurses で全て作れそうです。
後は string の使い方を頭に刷り込めば画面は作れるかなと。C言語の欠点は文字列の扱い難さですが、文字列は char の配列であることを頭に染み込ませてから string を勉強しなおしましょう。
アホかと思うくらいC言語について検索してきたので、これまで ncurses を知らなかったのは何故だろう。
#[Art-net] #C言語
C言語のライブラリである ncurses を勉強しています。ANSIエスケープシーケンスを簡単に使える様にしたライブラリです。
コマンドで全て描画するので今時の画面作りツールに比べたら面倒ですが、ANSIエスケープシーケンスを直接触ると思えば超簡単。歴史の長いライブラリのためか、奇妙な変化球がなくシンプルなのが私好み。
x-windowベースのグラフィカルな画面にしてもいいのですが、リソースの節約をしたいし、あえてレガシーな風味がいいかなと思っております。
Art-Netを受信して処理して送信する機能は一応出来ているので、まずは画面作りのお勉強をしましょう。データのモニターが出来ないと見えない部分の動作のチェックもし難いですしね。
#[Art-net] #C言語
コマンドで全て描画するので今時の画面作りツールに比べたら面倒ですが、ANSIエスケープシーケンスを直接触ると思えば超簡単。歴史の長いライブラリのためか、奇妙な変化球がなくシンプルなのが私好み。
x-windowベースのグラフィカルな画面にしてもいいのですが、リソースの節約をしたいし、あえてレガシーな風味がいいかなと思っております。
Art-Netを受信して処理して送信する機能は一応出来ているので、まずは画面作りのお勉強をしましょう。データのモニターが出来ないと見えない部分の動作のチェックもし難いですしね。
#[Art-net] #C言語
こんなん欲しいなぁ~と思っていた RaspberryPiCM4 用のマザーボードを発見!

中華電機のサイト
EtherNet が2系統、RTC、電源入力がUSB-Cでなく裏側にある、etc.
PCI-Ex1 もあれば満点ですが、USB-3.0 が3系統あるので十分です。
これが入荷したら ArtNet-Patch の開発を再開しようかと。晩酌の肴ですけどね。
画面作りに ncurses を使えばと思っていましたが、ふと思いついて調べ直したらこれは便利。ネットにも man にもわかり易く整理された情報がないので、お試ししながらオレオレマニュアルを作成して本体ページにアップしましょう。自分の為にですけどね。
最近、RaspberryPi の価格も下がってきました。CM4 が 中華電機で1万円せずに買えます。
#RaspberryPi #C言語 #[Art-Net]

中華電機のサイト
EtherNet が2系統、RTC、電源入力がUSB-Cでなく裏側にある、etc.
PCI-Ex1 もあれば満点ですが、USB-3.0 が3系統あるので十分です。
これが入荷したら ArtNet-Patch の開発を再開しようかと。晩酌の肴ですけどね。
画面作りに ncurses を使えばと思っていましたが、ふと思いついて調べ直したらこれは便利。ネットにも man にもわかり易く整理された情報がないので、お試ししながらオレオレマニュアルを作成して本体ページにアップしましょう。自分の為にですけどね。
最近、RaspberryPi の価格も下がってきました。CM4 が 中華電機で1万円せずに買えます。
#RaspberryPi #C言語 #[Art-Net]
C言語ネタです。
セマフォを使ったプロセス間での排他制御
セマフォは異なるプロセスが共有メモリへ同時に読み書きをしないために使おうを思っています。この記事だけではどう使ったらいいか見えませんが、セマフォの機能についてはシンプルに要点を突いて分かりやすい。こういう蛇足が無い説明は好きです。
ここでは他のプロセスの挙動は説明されていませんが、同じ名前のセマフォ(この記事では"/unko")を開いて同じ様に使います。同じセマフォを持った二つのプロセスがあるとして、後から sem_wait(sem) を実行した側は先に sem_wait(sem) を実行した側が sem_post(sem) を実行するまでブロック(動作を一時停止)します。sem_trywait(sem) を用いればブロックせずに戻りますので戻り値を見て処理を続行出来ます。
自力でセマフォを作ろうと思っていましたが、この方法でいいんでないかなと。
セマフォの方針が決まれば必要なことが一通りまとまったことになるので書き始められそうです。
まずは Art-Net を丁寧に受信する処理から進めます。ネットワーク上のすべての ArtDMX(スロットデータが列挙されたパケット) と存在する送信機の一覧を取得し、別プロセスから読み出して階層的に表示することを当面の目標とします。一種の Art-Net モニタでしょうか。画面を横に3分割し、左に送信機の一覧、中間に選択された送信機が扱うユニバース、右にスロットデータといった構成です。送信機とユニバースをキーボード(カーソル)操作で選択することも習作の一部となります。
これが作れなければ ArtNetPatch など作れませんし、最終的なパッケージでも欲しい機能です。
#C言語
セマフォを使ったプロセス間での排他制御
セマフォは異なるプロセスが共有メモリへ同時に読み書きをしないために使おうを思っています。この記事だけではどう使ったらいいか見えませんが、セマフォの機能についてはシンプルに要点を突いて分かりやすい。こういう蛇足が無い説明は好きです。
ここでは他のプロセスの挙動は説明されていませんが、同じ名前のセマフォ(この記事では"/unko")を開いて同じ様に使います。同じセマフォを持った二つのプロセスがあるとして、後から sem_wait(sem) を実行した側は先に sem_wait(sem) を実行した側が sem_post(sem) を実行するまでブロック(動作を一時停止)します。sem_trywait(sem) を用いればブロックせずに戻りますので戻り値を見て処理を続行出来ます。
自力でセマフォを作ろうと思っていましたが、この方法でいいんでないかなと。
セマフォの方針が決まれば必要なことが一通りまとまったことになるので書き始められそうです。
まずは Art-Net を丁寧に受信する処理から進めます。ネットワーク上のすべての ArtDMX(スロットデータが列挙されたパケット) と存在する送信機の一覧を取得し、別プロセスから読み出して階層的に表示することを当面の目標とします。一種の Art-Net モニタでしょうか。画面を横に3分割し、左に送信機の一覧、中間に選択された送信機が扱うユニバース、右にスロットデータといった構成です。送信機とユニバースをキーボード(カーソル)操作で選択することも習作の一部となります。
これが作れなければ ArtNetPatch など作れませんし、最終的なパッケージでも欲しい機能です。
#C言語
コンソールの画面操作では「ANSIエスケープシーケンス」を使うイメージでいましたが、「ncurses」と呼ばれる便利なライブラリがあることを今更知りました。これを使えばコンソール画面作りが楽になりそうです。
キー操作の扱いを調べていたのですが、「C言語で矢印キーの入力を完璧に検出する5つのステップ」を読んでいたら「ncurses」が便利とのこと。ここから調べを進めたところナルホドが連発。「cursesライブラリの超てきとー解説」を読むと概要がわかるような。
#C言語
キー操作の扱いを調べていたのですが、「C言語で矢印キーの入力を完璧に検出する5つのステップ」を読んでいたら「ncurses」が便利とのこと。ここから調べを進めたところナルホドが連発。「cursesライブラリの超てきとー解説」を読むと概要がわかるような。
#C言語