🗐 電装工芸日記 - 舞台照明機器の製作とか -

今年は開発案件を進めたい

or 管理画面へ

2025年6月6日の投稿[1件]

Icon of admin
 しばらく前に書いた 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言語

■思ってみた

社屋を囲む田んぼの田植えが終わり季節を感じます。

編集

■全文検索:

複合検索窓に切り替える

■複合検索:

  • 投稿者名:
  • 投稿年月:
  • #タグ:
  • カテゴリ:
  • 出力順序:

■日付検索:

■カレンダー:

2025年6月
1234567
891011121314
15161718192021
22232425262728
2930

■カテゴリ:

■最近の投稿:

最終更新日時:
2025年6月22日(日) 15時27分49秒