HOME 戻る

TK0040Aを使いIP通信を知るシンプルプログラム例

 
 
サンプルプログラムを用いてリモートI/O装置と簡単な通信を行い、 通信とその周辺ソフトに関する知識を習得します。
リモートI/O装置には、I/Oの種類が多様な TK0040A を用います。
TK0040A には デジタル (入出)、アナログ (入出)、カウンタ (入)、PWM (出)が複数チャネル用意されています。
但し、サンプルプログラムは勉強用であり解説をしやすくするため、I/Oそれぞれ 1 チャネルずつ定義(下段プログラム中※B))して用いています。
装置へのIPアドレスとポート番号も 192.168.0.200 と 20000 に(下段プログラム中※A)取り敢えずプログラム固定としています。
装置のIPアドレス等を変更している場合には、このアドレス等変更してお使いください。
※BK1682A,AK0822A,IOB30RTAの方は、コマンド等を装置仕様に合わせるなどしてご使用ください。
 
 
1. ダウンロードする勉強用サンプルプログラム
 
  本ページ下段枠内に示すサンプルプログラムを、弊社HPからダウンロードしてください。
  このプログラムは、弊社HP上「通信プログラム作成練習用サンプル」とタイトル付けされています。
  (HP→ダウンロード→4.新KaracrixBuilder用プログラム→kcx_com_study_1_xxx.prg)
  そして、プログラムをインポートしてください。
  また、プログラムをコンパイルしておいてください。(TK0040AのIPアドレスに注意(プログラム中※A))
 
2. 実行前の確認
 
  ホスト(CentOS等)のWebブラウザ画面を利用して、TK0040A のWeb画面が見えて(繋がって)いますか ?
  見えない場合、ping コマンドを使用して接続を再確認してください。
  接続の確認が出来ないと先に進めません。
  接続出来ない場合の多くの原因がネットワークアドレス(ネットマスク)の違い等にあります。
  先ずは、TK0040A がWebブラウザ画面で見える状態にしてください。
 
3. プログラム実行
 
  TK0040AのI/Oに以下を準備できる方は取り付けるなどしてください。
    a. デジタル入力1CH → スイッチ
    b. デジタル出力1CH → ランプ、LED等
    c. アナログ入力1CH → 電圧 (1.5V電池等)
    d. アナログ出力1CH → メータ (テスター等)
  では、サンプルプログラムを実行してください。
  TK0040Aの装置のLANのランプが点滅すれば通信始めています。
  次に、I/O情報が取り込まれ、また操作できるか確認します。
  サンプルプログラムで管理しているポイントは、勉強用のdi001,pi001,do001,ai001,ao001(プログラム中※B)のみです。
  KaracrixBuilder24A→監視パネル→14)リモートI/Oテスト(画面)に移ります。
  入力I/Oとなるものの状態を変化させられる方は変化させてください。画面に反映されます。
  続いて、TR&リレーとD/A出力を操作してください。リモート装置のI/Oに反映されるはずです。
  以上確認できた方は、動作正常ですので、サンプルプログラムの仕組を解析してみてください。
 
4. 使用しているソフトウエアライブラリ
 
  プログラムに使用しているソフトウエアは、以下の2つです。
  以下、プログラムと照らし合わせてください。
    a. ソケットライブラリ (プログラム中※S)(IP通信を行うC言語関数)
    b. KCXライブラリ (KaracrixBuilderのオブジェクトメモリへの読み書き)
     
kcxobj_open(); (プログラム中※C)ポイントオブジェクトIDを開く(使う宣言をする)
kcxobj_stat_iwt() (プログラム中※D)整数型データをオブジェクトメモリに書き込む
kcxobj_stat_fwt() (プログラム中※E)実数型データをオブジェクトメモリに書き込む
kcxobj_scale_frd() (プログラム中※F)ポイント登録のアナログ・スケールを得て比の計算に用いる
kcxobj_sndstat_fromkcx() (プログラム中※G)マニュアル(監視画面等)操作送信スタックデータの取得
kcxtim_tsleep() (プログラム中※H)ウエイト(プログラムにCPU時間食われないよう少し待たせるため)
 
5. プログラム要素
 
  通信計測制御に必要な下記基本要素がプログラムに記述されています。
  大きなプログラムもこれら要素が基礎となっています。
    1. ポイントオブジェクトをオープンしIDを得る。 (プログラムではこのIDを用いてポイントを操作します)
    2. ポイントオブジェクトメモリに対する読み書きを行う。
    3. 画面からのマニュアル操作等によるポイント操作データを得る。 (リモートI/Oを操作するデータです)
    4. TK0040AのI/O状態を得るためにコマンドを送り応答データをシステムに反映します。(プログラム中※J)
    5. TK0040AのI/Oを操作するためにコマンドを送ります。(プログラム中※K)
 
6. 通信リトライ用タイムアウト値の注意
 
  下段プログラム中※Tの値には注意してください。
  これはTK0040Aにコマンド送信後、装置からの応答データを待つ最大時間(秒)です。
  最大時間待っても応答データが来なかった場合、コマンドを再送信させ通信エラー回復を図ります。
  そして、この待ち時間の値設定には次の場合で注意が必要となります。
  それは、通信経路上に電波を使用した経路(無線LAN等)が含まれる場合です。
  電波を使用した通信経路では電波条件が悪くなるとデータパケットが著しく滞ってしまう場合があります。
  この滞ってしまう時間が、上記待ち時間を越すとコマンドが再送されて通信のやり取りが狂ってしまいます。
  この状況でも対応できる通信プログラム組んでおけば問題無いと思いますが、
  下記プログラムの場合、勉強用含めシンプルに記述しており対応していません。
  従いまして、電波機器を使用している場合の待ち時間は、十分長く設定して下さい。
 
 
 
 
「通信プログラム作成練習用サンプル」(kcx_com_study_1_xxx.prg)
#include  <karacrix.h>
main(argc,argv)
int   argc;
char *argv[];
{
  int    sockid,len,adval,pwmval;       /*先頭部では変数宣言*/
  struct sockaddr_in sndaddr,rcvaddr;
  char   sndbuff[512],rcvbuff[512];
  char   seq[64],ans[64];
  char   di_s[16],dti_s[16],do_s[8];
  int    pi[6],ai[4],ao[2];
  int    objid,di001,do001;
  int    objid_di001,objid_do001;
  int    objid_pi001,objid_ai001,objid_ao001;
  double fval,hi,low;
  KcxIntFlt_t  udata;

    /* 初期設定 */
    kcxinit( argc, argv );  /*←KaracrixBuilder初期必須宣言*/
    /* ソケットの作成 */
    sockid = socket( AF_INET, SOCK_DGRAM, 0 ); ※S 以下
    memset( (void *)&sndaddr, 0, sizeof(sndaddr) );
    sndaddr.sin_family      = AF_INET;
    sndaddr.sin_port        = htons    ( 20000           ); /*リモートIOのポート番号*/ ※A 
    sndaddr.sin_addr.s_addr = inet_addr( "192.168.0.200" ); /*リモートIOのIPアドレス*/ ※A 
    memset( (void *)&rcvaddr, 0, sizeof(rcvaddr) );
    rcvaddr.sin_family      = AF_INET;
    rcvaddr.sin_addr.s_addr = htonl( INADDR_ANY    );
    rcvaddr.sin_port        = htons( 30000 );                /*自分(PC)のポート番号*/
    bind( sockid, (struct sockaddr *)&rcvaddr, sizeof(rcvaddr) ); ※S 以上

    /* ポイント・オブジェクトID取得 */
    objid_di001 = kcxobj_open( "di001" ); ※B ※C
    objid_do001 = kcxobj_open( "do001" ); ※B ※C
    objid_pi001 = kcxobj_open( "pi001" ); ※B ※C
    objid_ai001 = kcxobj_open( "ai001" ); ※B ※C
    objid_ao001 = kcxobj_open( "ao001" ); ※B ※C

    while( 1 ){   /*←無限ループの意味*/

      /* == TK0040A状態監視 == */
      /* コマンド送信&応答受信 */
      strcpy( sndbuff, "123A mix" );   /*←状態読取コマンド*/
      len =  karacrix_com( sockid, &sndaddr, &rcvaddr, 
			   sndbuff, strlen(sndbuff), rcvbuff, sizeof(rcvbuff) ); ※J
      if( len <= 0 ){ sleep(1); continue; }   /*←エラー時戻り*/

      /* 応答パケット展開&データ取得 */
      rcvbuff[len] = (char)0;
      sscanf( rcvbuff, "%s%s%s%s%d%d%d%d%d%d%s%d%d%d%d%d%d", 
                       seq, ans, di_s, dti_s,
                       &pi[0],&pi[1],&pi[2],&pi[3],&pi[4],&pi[5],
                       do_s, &ai[0],&ai[1],&ai[2],&ai[3], &ao[0],&ao[1] );

      /* ポイント・オブジェクト値設定 */
      if( di_s[0] == (int)'1' )  di001 = 1;               /* Di01→ON 判定    */
      else                       di001 = 0;               /* Di01→OFF判定    */
      if( do_s[0] == (int)'1' )  do001 = 1;               /* Do01→ON 判定    */
      else                       do001 = 0;               /* Do01→OFF判定    */
      kcxobj_stat_iwt( objid_di001, di001  );              /* Di01値設定       */ ※D
      kcxobj_stat_iwt( objid_do001, do001  );              /* Do01値設定       */ ※D
      kcxobj_stat_iwt( objid_pi001, pi[0] );              /* Pi01値設定       */ ※D
      kcxobj_scale_frd( objid_ai001, &hi, &low );         /* Ai01スケール取得 */ ※F
      fval = ( hi - low ) * (double)ai[0] / 1024.0 + low; /* AD値→単位値変換 */
      kcxobj_stat_fwt( objid_ai001, fval );               /* Ai01値設定       */ ※E
      kcxobj_scale_frd( objid_ao001, &hi, &low );         /* Ao01スケール取得 */ ※F
      fval = ( hi - low ) * (double)ao[0] / 256.0 + low;  /* DA値→単位値変換 */
      kcxobj_stat_fwt( objid_ao001, fval );               /* Ao01値設定       */ ※E

      /* == TK0040A操作 == */
      /* 送信データ蓄積検査と取得(オブジェクトIDとデータ(整数or実数)) */
      switch( kcxobj_sndstat_fromkcx( &objid, &udata ) ){ ※G
      case KcxINTEGER: /* デジタル型→Do型 */
           if( objid == objid_do001 ){
             /* do001のID検査パス、そしてdo001制御データのみ(他は現状維持)送信 */
             if( udata.i == 1 ) sprintf( sndbuff, "123A dout 1---" );  /*←操作コマンド*/
             else               sprintf( sndbuff, "123A dout 0---" );  /*←操作コマンド*/
             karacrix_com( sockid, &sndaddr, &rcvaddr, 
			   sndbuff, strlen(sndbuff), rcvbuff, sizeof(rcvbuff) ); ※K
           }  
           break;
      case KcxFLOAT:   /* アナログ型→Ao型 */
           if( objid == objid_ao001 ){
             /* ao001のID検査パス、そしてao001制御データ(DA値)のみ(他は現状維持)送信 */
             kcxobj_scale_frd( objid_ao001, &hi, &low ); ※F
             adval = (int)( 256.0 * ( udata.f - low ) / ( hi - low ) );
             if( adval <   0 ) adval = 0;   /*制限チェック*/
             if( adval > 255 ) adval = 255; /*制限チェック*/
             sprintf( sndbuff, "123A aout %d -1", adval );   /*←操作コマンド*/
             karacrix_com( sockid, &sndaddr, &rcvaddr, 
			   sndbuff, strlen(sndbuff), rcvbuff, sizeof(rcvbuff) ); ※K
             /** 以下アナログ出力をRC/PWMにも連動出力させています **/
             pwmval = (int)( 10000.0 * ( udata.f - low ) / ( hi - low ) );
             if( pwmval <     0 ) pwmval = 0;     /*制限チェック*/
             if( pwmval > 10000 ) pwmval = 10000; /*制限チェック*/
             sprintf( sndbuff, "123A pwmout %d -1 -1", pwmval );   /*←操作コマンド*/
             karacrix_com( sockid, &sndaddr, &rcvaddr, 
			   sndbuff, strlen(sndbuff), rcvbuff, sizeof(rcvbuff) ); ※K
           }
           break;
      default: break;
      }
      /* == ウエイト == */
      kcxtim_tsleep( 100000 );  /* CPU負荷和らげるため必ず必要(100msec) */ ※H
    }
}

/*以下興味ない方→karacrix_com()関数を汎用的にご使用下さい*/
/*以下興味ある方→C言語ソケットライブラリ参照後に参照下さい*/
/* IPデータ送信し応答受けるプログラム(以下興味有る方はBSDソケットライブラリ参照) */
karacrix_com( sockid,sndaddr,rcvaddr,sndbuff,sndlen,rcvbuff,rcvlen )
int		 sockid;        /* ソケットID           */
struct sockaddr *sndaddr;       /* 送信アドレス         */
struct sockaddr *rcvaddr;       /* 受信アドレス         */
char		 sndbuff[];     /* 送信データバッファ   */
int		 sndlen;        /* 送信データ長         */
char		 rcvbuff[];     /* 受信データバッファ   */
int		 rcvlen;        /* 受信データバッファ長 */
{
  int   len,retry;
  char  snd_frameidname[128];
  char  rcv_frameidname[128];
    sscanf( sndbuff, "%s", snd_frameidname );
    for( retry = 0; retry < (5); retry ++ ){
      rcv_frameidname[0] = (char)0;
      rcvbuff        [0] = (char)0;
      len = karacrix2_com( sockid,sndaddr,rcvaddr,sndbuff,sndlen,rcvbuff,rcvlen );
      if( len <= 0 ){ sleep( 1 ); continue; }
      rcvbuff[len] = (char)0;
      sscanf( rcvbuff, "%s", rcv_frameidname );
      if( strcmp( snd_frameidname, rcv_frameidname ) == 0 ){
        return  len;  /*正常終了*/
      }else{ 
        sleep( 1 ); continue;
      }
    }
    return  (-1); /*失敗終了*/
}

karacrix2_com( sockid,sndaddr,rcvaddr,sndbuff,sndlen,rcvbuff,rcvlen )
int		 sockid;
struct sockaddr *sndaddr;
struct sockaddr *rcvaddr;
char		 sndbuff[];
int		 sndlen;
char		 rcvbuff[];
int		 rcvlen;
{
  int	 i,len,fds,addrlen;
  fd_set fdset;
  struct timeval tm;
    /*受信バッファクリア*/
    for(i=0;i<100;i++){  
      fds = 1 + sockid;
      FD_ZERO( &fdset         );
      FD_SET ( sockid, &fdset );
      tm.tv_sec  = tm.tv_usec = (0); 
      if( select( fds,&fdset,(fd_set *)NULL,(fd_set *)NULL,&tm ) <= 0 ){
        break;
      }
      addrlen = sizeof(struct sockaddr); 
      (void)recvfrom( sockid,(void *)rcvbuff,rcvlen,0,rcvaddr,&addrlen );
    }
    /* 送信チェック */
    fds = 1 + sockid;
    FD_ZERO( &fdset         );
    FD_SET ( sockid, &fdset );
    tm.tv_sec  = (1); 
    tm.tv_usec = (0);
    if( select( fds,(fd_set *)NULL,&fdset,(fd_set *)NULL,&tm ) <= 0 ){
      return 0;
    }
    /* データ送信 */
    addrlen = sizeof(struct sockaddr);
    (void)sendto( sockid,(void *)sndbuff,sndlen,0,sndaddr,addrlen );
    /* 受信チェック */
    FD_ZERO( &fdset         );
    FD_SET ( sockid, &fdset );
    tm.tv_sec  = (5);  /*要調整(ハング回避:無線LAN介入時等遅延可能性大の場合は20〜30)*/  ※T
    tm.tv_usec = (0);
    if( select( fds,&fdset,(fd_set *)NULL,(fd_set *)NULL,&tm ) <= 0 ){
      return 0;
    }
    /* データ受信 */
    if(( len = recvfrom( sockid,(void *)rcvbuff,rcvlen,0,rcvaddr,&addrlen )) < 0 ){
      return 0;
    }
    return  len; /*受信データ長*/
}
 
 

HOME 戻る