2025年6月 この範囲を時系列順で読む この範囲をファイルに出力する
現場ですが、簡単なひな壇を組むだけの現地道具なので終演までヒマです。
ArtNetPatch の ap_transmitter の構造を構造体配列を元に整理したのですが、割り切って構成したら案外軽い処理になりました。メモリの節約など考えず、条件分岐や計算を出来るだけ減らし繰り返しをヒトまとめにする方針だからでしょうか。
実際に組んで実行時間を計測しなければなりませんが、パッチ処理の後にもディレイやプロファイルカーブの処理を入れられそうな予感がします。
receive(受信)、bind(入力ユニバースを内部Bus(ユニバース)にパッチ、ここで数値をマージしてHTPミキサーとします)、pre-delay、pre-profile-curve、patch、post-delay、post-profile-curve、transmit といった流れで考えています。
ついてはモジュール構成を少し変えます。
ap_transmitter の中の数値操作と Art-net の出力を分割します。
fps はともかく、すべてのユニバースを連続して送るのは避けた方がいいかなと思うので、ユニバース毎にインターバルを持たせるためです。
1)アプリの起動部とし、共有物を設定して以下のモジュールを呼ぶ「ap_main」
2)画面表示やユーザー操作を司る「ap_console」
3)Art-Netを受信する「ap_receiver」
4)受信値や設定値から出力値をまとめる「ap_effect」
5)Art-Net を出力する「ap_transmitter」
6)データのタイムアウト管理をする「ap_timeout」
今後はタイムラグを減らす工夫を考えてみましょう。
出力の目標は 30fps 以上、出来れば 36fps ですが、1/36秒以内に処理を一巡出来るなら遅れても1フレームとなります。無理ならスレッド的なアプローチで考えて出来るだけ遅れを少なくしましょう。
何にしても、試作をして処理時間と処理負荷の計測が必要です。
こんな複雑なシステムは10回くらい書き直すつもりで試すしかありません。素人の私が結果を完全に予測するなんで無理ですもん。
#[Art-Net] #器具の製作 #C言語
ArtNetPatch の ap_transmitter の構造を構造体配列を元に整理したのですが、割り切って構成したら案外軽い処理になりました。メモリの節約など考えず、条件分岐や計算を出来るだけ減らし繰り返しをヒトまとめにする方針だからでしょうか。
実際に組んで実行時間を計測しなければなりませんが、パッチ処理の後にもディレイやプロファイルカーブの処理を入れられそうな予感がします。
receive(受信)、bind(入力ユニバースを内部Bus(ユニバース)にパッチ、ここで数値をマージしてHTPミキサーとします)、pre-delay、pre-profile-curve、patch、post-delay、post-profile-curve、transmit といった流れで考えています。
ついてはモジュール構成を少し変えます。
ap_transmitter の中の数値操作と Art-net の出力を分割します。
fps はともかく、すべてのユニバースを連続して送るのは避けた方がいいかなと思うので、ユニバース毎にインターバルを持たせるためです。
1)アプリの起動部とし、共有物を設定して以下のモジュールを呼ぶ「ap_main」
2)画面表示やユーザー操作を司る「ap_console」
3)Art-Netを受信する「ap_receiver」
4)受信値や設定値から出力値をまとめる「ap_effect」
5)Art-Net を出力する「ap_transmitter」
6)データのタイムアウト管理をする「ap_timeout」
今後はタイムラグを減らす工夫を考えてみましょう。
出力の目標は 30fps 以上、出来れば 36fps ですが、1/36秒以内に処理を一巡出来るなら遅れても1フレームとなります。無理ならスレッド的なアプローチで考えて出来るだけ遅れを少なくしましょう。
何にしても、試作をして処理時間と処理負荷の計測が必要です。
こんな複雑なシステムは10回くらい書き直すつもりで試すしかありません。素人の私が結果を完全に予測するなんで無理ですもん。
#[Art-Net] #器具の製作 #C言語
子プロセスを使う場合は親プロセスが落ちたらこれらも落とさないといけません。ゾンビとして残ってしまうからです。
一般的にはメインから「終わりにしなさい」って指示を受けて落としますが、開発途中ではそうもいかないことがあります。
ならば、「続けなさい」って指令が無ければタイムアウトすることにしましょう。親の存在確認をする方法もありますが、この方法なら管理が楽かも。
#C言語
一般的にはメインから「終わりにしなさい」って指示を受けて落としますが、開発途中ではそうもいかないことがあります。
ならば、「続けなさい」って指令が無ければタイムアウトすることにしましょう。親の存在確認をする方法もありますが、この方法なら管理が楽かも。
#C言語
ArtNetPatch の構成イメージが見えてきました。
この後は「ap_transmitter」の具体化でしょうか。これが ArtNetPatch の核であり一番重い処理です。
モジュール間を繋ぐ構造体配列が見えればその他のモジュールを作ることは可能ですし、画面表示とか作って手ごたえを感じたいってのはありますが、最終的にデータを取りまとめる「ap_transmitter」が見えませんと構造体配列を決めることが出来ません。
#[Art-Net] #器具の製作
この後は「ap_transmitter」の具体化でしょうか。これが ArtNetPatch の核であり一番重い処理です。
モジュール間を繋ぐ構造体配列が見えればその他のモジュールを作ることは可能ですし、画面表示とか作って手ごたえを感じたいってのはありますが、最終的にデータを取りまとめる「ap_transmitter」が見えませんと構造体配列を決めることが出来ません。
#[Art-Net] #器具の製作
と、なると、って書き出しは私のアタマの中の言葉のままですが、プロセスかスレッドかはともかく、処理のスジを次の様に分けようかと。
1)アプリの起動部で、共有物を設定して以下のモジュールを呼ぶ「ap_main」
2)画面表示やユーザー操作を司る「ap_console」
3)Art-Netを受信する「ap_receiver」
4)受信値や設定値から出力値まとめ、Art-Net を出力する「ap_transmitter」
5)データのタイムアウト管理をする「ap_timeout」
大きなデータは共有メモリ「mmap」と「semaphore」でやりとりし、指示や返答は「queue」で繋げます。自分ナリに得手不得手を検討した結果です。
タイムアウトを別枠で勝手にやらせる発想が出たら役割分担が楽になりました。
これが出来るのも共有メモリとセマフォのオカゲです。
まだまだ決定ではありませんが、タイミングがラフなところはOS に任せてしまえ!って思えたら気が楽になったかも。
メモリ管理も処理タイミングの管理も OS に頼った方が間違いないのです。
この役割分担をしたら RaspberryPi でも処理しきれそうな気になってきたかも。
追記
成り行き任せで後回しにしても構わない処理はプロセスやスレッドを別にして区切りのいいところに短い「sleep」を入れればいい。こうすると OS のジャッジで急ぎと思われるプロセスやスレッドを優先的に処理をしてくれるようです。
厳密なタイミング管理が必要なら「RTOS」ベースのカーネルを使ってガチガチに管理するのが良いと思いますが、そこまででない処理のため導入の敷居が高い RTOS を使うのはどうかと。
#[Art-Net] #器具の製作
1)アプリの起動部で、共有物を設定して以下のモジュールを呼ぶ「ap_main」
2)画面表示やユーザー操作を司る「ap_console」
3)Art-Netを受信する「ap_receiver」
4)受信値や設定値から出力値まとめ、Art-Net を出力する「ap_transmitter」
5)データのタイムアウト管理をする「ap_timeout」
大きなデータは共有メモリ「mmap」と「semaphore」でやりとりし、指示や返答は「queue」で繋げます。自分ナリに得手不得手を検討した結果です。
タイムアウトを別枠で勝手にやらせる発想が出たら役割分担が楽になりました。
これが出来るのも共有メモリとセマフォのオカゲです。
まだまだ決定ではありませんが、タイミングがラフなところはOS に任せてしまえ!って思えたら気が楽になったかも。
メモリ管理も処理タイミングの管理も OS に頼った方が間違いないのです。
この役割分担をしたら RaspberryPi でも処理しきれそうな気になってきたかも。
追記
成り行き任せで後回しにしても構わない処理はプロセスやスレッドを別にして区切りのいいところに短い「sleep」を入れればいい。こうすると OS のジャッジで急ぎと思われるプロセスやスレッドを優先的に処理をしてくれるようです。
厳密なタイミング管理が必要なら「RTOS」ベースのカーネルを使ってガチガチに管理するのが良いと思いますが、そこまででない処理のため導入の敷居が高い RTOS を使うのはどうかと。
#[Art-Net] #器具の製作
Art-Net の処理についてタイミングをイロイロ検討してみました。
まず、全受信のスタックと受信の次の工程に送るスタックは完全に別物にした方が良さそうです。先日の書き込みと真逆のことですが、Art-Net 全体のモニターをするためのスタックとパッチの前工程のスタックは別物にするってことです。細かい理由は割愛しますが、同じ結果を得られるならメモリを大食いしても処理は軽い方がいい。DMXの処理で使うメモリ量など増えても数KBですから、GBクラスのメモリを持った RaspberryPi で気にするこっちゃありません。また、全受信のスタックを元にすべてを行うと最低でも2秒分の履歴を残さなければなりませんが、モニターするだけなら最新値のスタックだけで済みますし、Delay では深い履歴が必要でもパッチにおけるユニバース数だけあればいいので、想定される入力のすべてを数秒分スタックするほどのメモリは不要です。結果、処理が軽くなってメモリの使用量が減るなら御の字ってことです。あとは、タイムアウトの処理も大きな理由です。パケット毎に受信時刻のチェックをすることになりますが、チェックするパケットの数が減り、モニターならチェック頻度を落としてタイムアウトの実効値が2秒とか3秒でも大丈夫です。
データをどのように取り込んでスタックして処理するかのイメージを整理して処理時間や工数が最も少ないであろう構成を決めてからソースコードを書こうと思っています。
追記
上記の考え方にするなら、パッチの前工程の処理をする位置や共有メモリの扱いを明確にしておけばソースを書き始められるかも。完全に Art-Net モニタです。受信の後の各種処理や画面表示を別プロセスにするのでまだまだ考えることはありますけどね。
今思い付いたのですが、タイムアウトの処理を別プロセスや別スレッドにしたら受信プロセスが凄く簡単になるかも。socket の受信をタイムアウトせずに何かを受信するまでブロックするってことです。シングルプロセスの PIC マイコンに慣れ過ぎて手作業のマルチスレッド処理がアタマの中で前提になっているようです。ブロックすることが一般的な処理をあえてタイムアウトさせてエラー処理するくらいならプロセスを分けてタイムアウトさせずに待たせればいいのです。OSが得意な部分はOSにやってもらえばいいのです。
#[Art-Net] #器具の製作
まず、全受信のスタックと受信の次の工程に送るスタックは完全に別物にした方が良さそうです。先日の書き込みと真逆のことですが、Art-Net 全体のモニターをするためのスタックとパッチの前工程のスタックは別物にするってことです。細かい理由は割愛しますが、同じ結果を得られるならメモリを大食いしても処理は軽い方がいい。DMXの処理で使うメモリ量など増えても数KBですから、GBクラスのメモリを持った RaspberryPi で気にするこっちゃありません。また、全受信のスタックを元にすべてを行うと最低でも2秒分の履歴を残さなければなりませんが、モニターするだけなら最新値のスタックだけで済みますし、Delay では深い履歴が必要でもパッチにおけるユニバース数だけあればいいので、想定される入力のすべてを数秒分スタックするほどのメモリは不要です。結果、処理が軽くなってメモリの使用量が減るなら御の字ってことです。あとは、タイムアウトの処理も大きな理由です。パケット毎に受信時刻のチェックをすることになりますが、チェックするパケットの数が減り、モニターならチェック頻度を落としてタイムアウトの実効値が2秒とか3秒でも大丈夫です。
データをどのように取り込んでスタックして処理するかのイメージを整理して処理時間や工数が最も少ないであろう構成を決めてからソースコードを書こうと思っています。
追記
上記の考え方にするなら、パッチの前工程の処理をする位置や共有メモリの扱いを明確にしておけばソースを書き始められるかも。完全に Art-Net モニタです。受信の後の各種処理や画面表示を別プロセスにするのでまだまだ考えることはありますけどね。
今思い付いたのですが、タイムアウトの処理を別プロセスや別スレッドにしたら受信プロセスが凄く簡単になるかも。socket の受信をタイムアウトせずに何かを受信するまでブロックするってことです。シングルプロセスの PIC マイコンに慣れ過ぎて手作業のマルチスレッド処理がアタマの中で前提になっているようです。ブロックすることが一般的な処理をあえてタイムアウトさせてエラー処理するくらいならプロセスを分けてタイムアウトさせずに待たせればいいのです。OSが得意な部分はOSにやってもらえばいいのです。
#[Art-Net] #器具の製作
Art-Net の扱いを送信元8、ユニバース各8、44fpsにしますと毎秒の最大受信パケット数は2,816です。1パケットあたり355usec以下で捌かないといけません。実用においてここまでの量になることは無いと思いますし、確認する環境を整えるのも現実的ではありませんが、一度は想定最大負荷をかけてみたいものです。
実作業を始められるのがいつになるかわかりませんが基本設計は進めます。アタマが空いてる時のヒマ潰しにはちょうどいいですし。
#器具の製作
実作業を始められるのがいつになるかわかりませんが基本設計は進めます。アタマが空いてる時のヒマ潰しにはちょうどいいですし。
#器具の製作
Art-Netの扱いにアイデアを一つ。
送信元を識別するにはIPv4アドレスがキーになると思います。運が悪くなければ送信元毎にユニークなハズです。これの扱い。
IPv4アドレスは4バイト長(32bit)で構成され、文字列で表しても7~15文字です。
これを一つの整数にまとめてしまうアイデアです。一般的にint型は4バイト長ですから、丸めてしまえばint型にしても情報は欠落しません。
こうすれば、4つの数字や7~15文字のテキストよりも扱いが簡単で軽くなると思うのです。IPアドレスとしての情報は別途残すとして、識別IDにこれを使うのです。
ユニバースも同じ考え方でいいでしょう。15bit長ですからshort型でも収まりますが、噂に聞くところではint型の方が処理が軽いらしいのでこの型にしておこうかなと。
この両者を合わせた8バイトのlong型もしくはLongLong型を併記しておいてもいいかもしれません。なぜこの様なことを考えるかと言いますと、複数のユニバースを保管する配列から特定のものを探し出す処理を軽くしたいからです。処理の方針にもよりますが、受信したものを一旦スタックして一定の時間間隔で取り出そうというのが今の考え方ですので、クエリに相当するインデックスを作るのは当然としてもキーワードが簡素なことは重要だと思うのです。一定の時間間隔で作れらたLoop配列ならばその並びが時間情報となりますので、Delay を求めても受信日時と現在日時を比較する必要がありません。
Art-Netのモニターも兼ねたいので、使う使わないはともかく、受信したデータを全て一時保管するつもりです。
かといってモニターのための保管とパッチのための保管を別々にするのは気に入らないので、受信をすべて保管してそこから必要なモノを取り出したいのです。
仮に、送信元8、それぞれ8ユニバースとするなら、最大44fpsとして1秒間に2,816件のスタックをしなければなりません。1パケット当たりのデータ長は540バイトくらいですから1.5MB/秒くらいです。2秒分のスタックをしても3MBです。動画の1フレーム当たりのデータ長は640x480の16bitカラーで1.8MBくらいですので55MB/秒です。動画に比べたらArt-Netの情報量は余裕っしょ。
#[Art-Net] #C言語
送信元を識別するにはIPv4アドレスがキーになると思います。運が悪くなければ送信元毎にユニークなハズです。これの扱い。
IPv4アドレスは4バイト長(32bit)で構成され、文字列で表しても7~15文字です。
これを一つの整数にまとめてしまうアイデアです。一般的にint型は4バイト長ですから、丸めてしまえばint型にしても情報は欠落しません。
こうすれば、4つの数字や7~15文字のテキストよりも扱いが簡単で軽くなると思うのです。IPアドレスとしての情報は別途残すとして、識別IDにこれを使うのです。
ユニバースも同じ考え方でいいでしょう。15bit長ですからshort型でも収まりますが、噂に聞くところではint型の方が処理が軽いらしいのでこの型にしておこうかなと。
この両者を合わせた8バイトのlong型もしくはLongLong型を併記しておいてもいいかもしれません。なぜこの様なことを考えるかと言いますと、複数のユニバースを保管する配列から特定のものを探し出す処理を軽くしたいからです。処理の方針にもよりますが、受信したものを一旦スタックして一定の時間間隔で取り出そうというのが今の考え方ですので、クエリに相当するインデックスを作るのは当然としてもキーワードが簡素なことは重要だと思うのです。一定の時間間隔で作れらたLoop配列ならばその並びが時間情報となりますので、Delay を求めても受信日時と現在日時を比較する必要がありません。
Art-Netのモニターも兼ねたいので、使う使わないはともかく、受信したデータを全て一時保管するつもりです。
かといってモニターのための保管とパッチのための保管を別々にするのは気に入らないので、受信をすべて保管してそこから必要なモノを取り出したいのです。
仮に、送信元8、それぞれ8ユニバースとするなら、最大44fpsとして1秒間に2,816件のスタックをしなければなりません。1パケット当たりのデータ長は540バイトくらいですから1.5MB/秒くらいです。2秒分のスタックをしても3MBです。動画の1フレーム当たりのデータ長は640x480の16bitカラーで1.8MBくらいですので55MB/秒です。動画に比べたらArt-Netの情報量は余裕っしょ。
#[Art-Net] #C言語
しばらく前に書いた 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言語
TaitanやETCでは正常なのにMA系の卓からの出力を正常に受信出来ないノードがあります。MAの卓は値が変化している最中は30fpsくらいですが、値の変化が無いと10fps位に落ちるのでコレが原因かなと妄想しています。DoctorMXでの計測ですが、レガシーDMXだけでなくArt-Netもしかりです。
原因は定かではありませんが繋がらないのは困ります。ArtNetPatchではどうしましょう。
fpsを一定にすることは外さないとして、ArtNetPatchの処理フェーズにも関係しそうな気がしてます。
#[Art-Net]
原因は定かではありませんが繋がらないのは困ります。ArtNetPatchではどうしましょう。
fpsを一定にすることは外さないとして、ArtNetPatchの処理フェーズにも関係しそうな気がしてます。
#[Art-Net]
ArtNetPatch は一時スタックに格納した後にどう処理するかが課題です。
一定以上の処理速度を確保しなければなりません。DMX512 は最大44fps ですので1フェーズあたり23msec.以下で動かす必要があります。4卓、各8ユニバースと想定するなら、受信1パケットあたりの総処理時間を710usec.以下にしなければなりません。RaspberryPi でも一時スタックに格納するまでの所要時間は150usec.くらいかと予想しますが、全体の処理が納まるかは感覚として微妙です。
Delay の扱いをどうするが鍵です。Delay を構成するには FIFO と呼ばれるループ型のスタックを使いますが、Art-Net を受信する度に FIFO に持って行くか、一旦一時スタックに留めて一定間隔で FIFO に持って行くか悩んでいます。前者ならソースコードは簡単ですがパケットの受信時刻を評価する回数が多く重くなり、後者ならソースコードが複雑になりますがスタックポインタの現在値からの単純なオフセットで処理出来るので軽くなります。簡単で重いか、複雑で軽いか、この両者の良し悪しは簡単には決められません。
後工程を考えずに前工程を決めてしまうと難儀することがあるので、工程全体をある程度イメージしてから進めなければなりません。
#[Art-Net]
一定以上の処理速度を確保しなければなりません。DMX512 は最大44fps ですので1フェーズあたり23msec.以下で動かす必要があります。4卓、各8ユニバースと想定するなら、受信1パケットあたりの総処理時間を710usec.以下にしなければなりません。RaspberryPi でも一時スタックに格納するまでの所要時間は150usec.くらいかと予想しますが、全体の処理が納まるかは感覚として微妙です。
Delay の扱いをどうするが鍵です。Delay を構成するには FIFO と呼ばれるループ型のスタックを使いますが、Art-Net を受信する度に FIFO に持って行くか、一旦一時スタックに留めて一定間隔で FIFO に持って行くか悩んでいます。前者ならソースコードは簡単ですがパケットの受信時刻を評価する回数が多く重くなり、後者ならソースコードが複雑になりますがスタックポインタの現在値からの単純なオフセットで処理出来るので軽くなります。簡単で重いか、複雑で軽いか、この両者の良し悪しは簡単には決められません。
後工程を考えずに前工程を決めてしまうと難儀することがあるので、工程全体をある程度イメージしてから進めなければなりません。
#[Art-Net]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131