
/*
 * Trust Design LLC : SECS/HSMS Communication library
 *
 * (c) Copyright Trust Design LLC.  2010-2015.  All rights reserved.
 */


/* ========================================================================== */
/*
 * CSIo : テスト 及び サンプル・プログラム
 *
 *  ＡＰ内でメッセージを構築し、単純に SECS メッセージの送受信を行う。
 *
 *  (注) 本サンプルは、MSVCRT を使用する。
 *
 *
 * 起動方法
 *
 *   CSIo {h|e}
 *   ~~~~~~~~~~
 *   h    : Sample.ini の [HOST]  セクションを参照し動作を決定する
 *   e    : Sample.ini の [EQUIP]      :              :
 *
 *
 *   通常、CSIo h 及び CSIo e の両方を、同一マシン、もしくは異なるマシン上で
 *   動作させて、相互通信を行う。通信相手は、BasicIo.exe、Callbackio.exe でも
 *   よい。
 *
 *   起動後、メニュ表示を参照し、要求コード等を入力し、機能を実行する。
 * 
 *   ホスト側は起動すると、装置側からの接続を待つ。装置側を起動すると、自動的
 *   にホスト側に接続要求、Select 要求を発行する。ホスト側では、1:Recv 処理を
 *   繰り返し行い、表示が「STATUS=951 : No data」となるのを確認する。その後、
 *   任意のメッセージのやりとりを行う。受信側では、受信データの刈り取りを「
 *   STATUS=951」になるまで行うことを忘れぬこと。
 *
 *   ２次メッセージを送信する際のトランザクションＩＤは、直前に受信した１次
 *   メッセージのものを使用する。
 *
 *   本サンプルは、異常時処理を省略している。
 *   以下で定義する "MSSG_USE_FILE"、"MSSG_DISP_TYPE" の値を変更すると、本ＡＰ
 *   にて表示する SECS メッセージの出力形式を変更できる。
 *   また "MSSG_USE_NEXTL" の値を変更すると、メッセージ内容を解析する際に、項目
 *   値をデータとして取得するか、表示リスト形式で取得するかを変更できる。
 *   同様に "USE_CALLBACK" を =1 とすると、受信処理を行う Callback 関数を使用
 *   し、受信メッセージの表示、２次メッセージの出力を自動で行う。
 *   Callback 関数を使用する場合、入力促進用メッセージに乱れが生じる事に留意
 *   すること。
 */
/* ========================================================================== */

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;

using TDCSL;


namespace TDCSSTest{

unsafe class CSIo{
[DllImport( "msvcrt", CallingConvention=CallingConvention.Cdecl)]
extern static int printf(string form);
[DllImport( "msvcrt", CallingConvention=CallingConvention.Cdecl)]
extern static int printf(string form, __arglist);
[DllImport( "msvcrt", CallingConvention=CallingConvention.Cdecl)]
extern static int sprintf(byte[] str, string form, __arglist);
[DllImport( "msvcrt", CallingConvention=CallingConvention.Cdecl)]
extern static int scanf(string form, __arglist);


/* -------------------------------------------------------------------------- */

private const int	EBADF		=9;
private const int	EBUSY		=16;
private const int	ENOMEM		=12;
private const int	ENODEV		=19;
private const int	E2BIG		=7;

private const int	E_NOTCONNECT	=999;
private const int	E_DESELECT	=998;
private const int	E_REJECT	=997;
private const int	E_SELECT	=992;
private const int	E_CONNECT	=991;
private const int	E_RETRYOVER	=989;
private const int	E_T8TIMEDOUT	=988;
private const int	E_T7TIMEDOUT	=987;
private const int	E_T6TIMEDOUT	=986;
private const int	E_T5TIMEDOUT	=985;
private const int	E_T4TIMEDOUT	=984;
private const int	E_T3TIMEDOUT	=983;
private const int	E_T2TIMEDOUT	=982;
private const int	E_T1TIMEDOUT	=981;
private const int	E_ILLBLOCK	=980;
private const int	E_NODATA	=951;


/* -------------------------------------------------------------------------- */

private static int	MSSG_USE_FILE	=0x80;	// メッセージ定義ファイル
						// 0x00 : 使用しない
						// 0x80 : 使用して項目名表示する
private static int	MSSG_DISP_TYPE	=0x20;	// SECS メッセージ表示形式
						// 0x00 : 通常形式
						// 0x10 : ????形式
						// 0x20 : SML 形式
						// 0x40 : NSG/TS300 形式
private static int	MSSG_USE_NEXTL	=1;	// MssgNextL を使用するか否か
						// 0    : 使用しない
						// 1    : 使用する

private static int	USE_CALLBACK	=1;	// Callback 関数使用の有無
						// 0    : 使用しない
						// 1    : 使用する

private static string	PARAMFILE	="Sample.ini";


/* -------------------------------------------------------------------------- */

private static int	Fd=0;		// 通信用識別子
private static int	Md=0;		// メッセージ解析用識別子
private static int	Break=0;	// スレッド終了指示
private static int	OType=0;	// 動作タイプ (0:Host 1:Equip)
private static int	Status=0;	// 動作状態
					// (0:未接続 1:未Select 2:Select)

private static int	cnt249=0;
private static int	cnt250=0;
private static int	cnt611=0;


/* ========================================================================== */
/* 共通関数 ================================================================= */


/* -------------------------------------------------------------------------- */
/* SECS メッセージを標準出力に表示する。 ------------------------------------ */

private static void
DispSECSMssg(			//
int		tp,		// i  : メッセージ種別
				//	=0 : 送信結果
				//	 1 : 受信メッセージ
				//	 2 : 送信メッセージ
byte[]		hd,		// i  : SECS メッセージ・ヘッダ
int		did,		// i  : SECS メッセージのデバイスＩＤ
int		sf,		// i  : SECS メッセージのＳＦコード
uint		xid,		// i  : SECS メッセージのトランザクションＩＤ
byte[]		msg,		// i  : SECS メッセージ格納領域（ヘッダ含まず）
int		len)		// i  : SECS メッセージのバイト長
{
  string[]	ctp={"SRES","RECV","SEND"};
  string	mname,sitem;
  byte[]	itm=new byte[64];
  short[]	vi2=new short[1];
  ushort[]	vu2=new ushort[1];
  int[]		vi4=new int[1];
  uint[]	vu4=new uint[1];
  long[]	vi8=new long[1];
  ulong[]	vu8=new ulong[1];
  float[]	vf4=new float[1];
  double[]	vf8=new double[1];
  string	str,rbit,wbit;
  byte[]	sfcode=new byte[16];
  int		rtn,md=0,dp=MSSG_USE_FILE|MSSG_DISP_TYPE,fm,form,sz,noi,la,i;

  rbit=" ";	if((did&0x8000)!=0)	rbit="R";
  wbit=" ";	if((sf &0x8000)!=0)	wbit="W";
  sprintf(sfcode,"S%dF%d",__arglist((sf&0x7f00)/0x0100,sf&0xff));
  printf("[%s]  Dev=0x%04x  %-8s  %s%s  XId=0x%04x  Len=%4d\n"
		,__arglist(ctp[tp],did&0x7fff,sfcode,rbit,wbit,xid,len));

  if((MSSG_USE_FILE&0x80)!=0)	fm =0x8000;		// メッセージ定義を使用
  else				fm =0x0000;		// 使用せず

  if(len>0){
    if(tp==1){	dp|=0x3000;	fm|=0x3000;}		// 受信の場合
    else{	dp|=0x2000;	fm|=0x2000;}		// 送信の場合

    if((md=TDS._MssgFind(fm,msg,len,Fd,hd,out mname))	> 0){
      if(mname!="") printf("[%s]\n",__arglist(mname));	// メッセージ名称

      for(la=0;;){
	if(MSSG_USE_NEXTL==0){				// 項目値を取得
	  fixed(byte* pm=itm){
	    rtn=TDS._MssgNext(md,0,msg,out form,out sz,out noi,pm,64,out sitem);
	    if(rtn					< 0)	break;
	    printf("%03o:%d*%2d:",__arglist(form,sz,noi));
	    switch(form){				// 項目値の表示
							// 数値の２番目以降は省略
	      case 0x00: Console.WriteLine("L[{0}]"	    ,noi);	   break;
	      case 0x08: Console.WriteLine("B[{0}]={1,0:x2}",noi,itm[0]);  break;
	      case 0x09: Console.WriteLine("T[{0}]={1,0}"   ,noi,itm[0]);  break;
	      case 0x10: Console.WriteLine("A[{0}]={1}"	    ,noi,sitem);   break;
	      case 0x11: Console.WriteLine("J[{0}]={1}"	    ,noi,sitem);   break;
	      case 0x12: Console.WriteLine("K[{0}]={1}"	    ,noi,sitem);   break;
	      case 0x18: Buffer.BlockCopy(itm,0,vi8,0,sz);
			 Console.WriteLine("I8[{0}]={1}"    ,noi,vi8[0]);  break;
	      case 0x19: Console.WriteLine("I1[{0}]={1}"    ,noi,itm[0]);  break;
	      case 0x1a: Buffer.BlockCopy(itm,0,vi2,0,sz);
			 Console.WriteLine("I2[{0}]={1}"    ,noi,vi2[0]);  break;
	      case 0x1c: Buffer.BlockCopy(itm,0,vi4,0,sz);
			 Console.WriteLine("I4[{0}]={1}"    ,noi,vi4[0]);  break;
	      case 0x20: Buffer.BlockCopy(itm,0,vf8,0,sz);
			 Console.WriteLine("F8[{0}]={1}"    ,noi,vf8[0]);  break;
	      case 0x24: Buffer.BlockCopy(itm,0,vf4,0,sz);
			 Console.WriteLine("F4[{0}]={1}"    ,noi,vf4[0]);  break;
	      case 0x28: Buffer.BlockCopy(itm,0,vu8,0,sz);
			 Console.WriteLine("U8[{0}]={1}"    ,noi,vu8[0]);  break;
	      case 0x29: Console.WriteLine("U1[{0}]={1}"    ,noi,itm[0]);  break;
	      case 0x2a: Buffer.BlockCopy(itm,0,vu2,0,sz);
			 Console.WriteLine("U2[{0}]={1}"    ,noi,vu2[0]);  break;
	      case 0x2c: Buffer.BlockCopy(itm,0,vu4,0,sz);
			 Console.WriteLine("U4[{0}]={1}"    ,noi,vu4[0]);  break;
	      default:	 Console.WriteLine("");				   break;
	  } }

	}else{						// リスト表示形式で取得
	  rtn=TDS._MssgNextL(md,dp,msg,out form,out noi,out str);
	  if(rtn					< 0)	break;
	  if((dp&0x70)>0x10){				// SML、NSG/TS300 の場合
	    for(i=la;i>rtn;i--){
	      printf("%*s>\n"	,__arglist(i*2,""));	// '>' 表示
	  } }
	  la=rtn;					// 現在の階層を保存
	  printf("  %s\n",__arglist(str));		// 取得した項目値を表示
	  if((dp&0x70)>0x10 && form==000 && noi==0){
	    printf("%*s>\n"	,__arglist((la+1)*2,""));// L0 の場合の '>' 処理
      } } }
      if(MSSG_USE_NEXTL!=0){
	if((dp&0x70)>0x10){
	   for(i=la;i>0;i--){
	     printf("%*s>\n"	,__arglist(i*2,""));	// 残りの '>' 表示
      } } }
      TDS._MssgExit(md,0,msg);
  } }
}



/* -------------------------------------------------------------------------- */
/* 送受信データを表示する。-------------------------------------------------- */

private static int
DispData(
int		tp,		// i  : メッセージ種別
				//	=0 : 送信結果
				//	 1 : 受信メッセージ
				//	 2 : 送信メッセージ
byte[]		hd,		// i  : SECS メッセージ・ヘッダ
int		did,		// i  : SECS メッセージのデバイスＩＤ
int		sf,		// i  : SECS メッセージのＳＦコード
uint		xid,		// i  : SECS メッセージのトランザクションＩＤ
byte[]		msg,		// i  : SECS メッセージ本体
int		len,		// i  : msg の長さ
int		rtn)		// i  : I/O の戻り値
{
  int		ok=0;

  if(rtn<0){							 ok=1;
    if((rtn<(-E_NOTCONNECT) || (-E_ILLBLOCK)<rtn) && rtn!=(-E_NODATA)){
      printf("ERROR  [%d]"			,__arglist(rtn));
      if      (rtn==(-ENODEV)){
	printf(" : No such device ID\n");	len=ok=0;
      }else if(rtn==(-E2BIG )){
	printf(" : Data size to large\n");	len=ok=0;
      }else			printf("\n");
    }else{
      printf("STATUS = %d,%d : "		,__arglist(-rtn,Status));
      switch(-rtn){
	case E_NODATA:		printf("No data\n");			break;
	case E_ILLBLOCK:	printf("Illegal block#\n");		break;
	case E_T1TIMEDOUT:	printf("T1 Timeout occur\n");		break;
	case E_T2TIMEDOUT:	printf("T2 Timeout occur\n");		break;
	case E_T3TIMEDOUT:	printf("T3 Timeout occur\n");		break;
	case E_T4TIMEDOUT:	printf("T4 Timeout occur\n");		break;
	case E_T5TIMEDOUT:	printf("T5 Timeout occur\n");		break;
	case E_T6TIMEDOUT:	printf("T6 Timeout occur\n");		break;
	case E_T7TIMEDOUT:	printf("T7 Timeout occur\n");		break;
	case E_T8TIMEDOUT:	printf("T8 Timeout occur\n");		break;
	case E_RETRYOVER:	printf("Retry over\n");			break;
	case E_CONNECT:		printf("Connected\n");			break;
	case E_SELECT:		printf("Selected\n");			break;
	case E_REJECT:		printf("Rejected XId=0x%04x\n"
						,__arglist(xid));	break;
	case E_DESELECT:	printf("Deselected\n");			break;
	case E_NOTCONNECT:	printf("Not connected\n");		break;
	default:		printf("\n");				break;
  } } }

  if(ok==0) DispSECSMssg(tp,hd,did,sf,xid,msg,len);

  return(0);
}



/* ========================================================================== */
/* S1F1 メッセージ構築及び送信 ---------------------------------------------- */

private static int
SendS1F1()
{
  byte[]	hd=new byte[12],msg=new byte[16];
  int		rtn,sf=0x8101,len=0;

  		// SubFunction.h の SendS1F1() 内 (注) を参照すること。
  rtn=TDS._CommSend(Fd,0x0000,0,sf,0,msg,len,ref hd);
  DispData(2,hd,0x00,sf,(uint)rtn,msg,len,rtn);

  return(rtn);
}


/* ---------------------------------------------------------------------------*/
/* S1F2 メッセージ (Host) 構築及び送信 -------------------------------------- */

private static int
SendS1F2H(
int		did,
uint		xid)
{
  byte[]	hd=new byte[12],msg=new byte[256];
  byte[]	vb=new byte[32];
  int		rtn,md,sf=0x0102,len;
  
  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		TDS._MssgInit   (   0,ref msg, 256,Fd);		// S1F2
		TDS._MssgBuild  (md,0,ref msg,0x00,  0,vb);	// L0
    len=	TDS._MssgEnd    (md,0,ref msg);

  }else{					// メッセージ定義を使用する
		TDS._MDMssgInit (Md,0,ref msg, 256,"S1F2_H");
    len=	TDS._MDMssgEnd  (Md,0,ref msg);
  }

  if(len<0)	rtn=len;
  else		rtn=TDS._CommSend (Fd,0x0000,0,sf,xid,msg,len,ref hd);
  DispData(2,hd,did,sf,(uint)rtn,msg,len,rtn);

  return(rtn);
}


/* ---------------------------------------------------------------------------*/
/* S1F2 メッセージ (Equip) 構築及び送信 ------------------------------------- */

private static int
SendS1F2E(
int		did,
uint		xid)
{
  byte[]	hd=new byte[12],msg=new byte[256];
  byte[]	vb=new byte[32];
  int		rtn,md,sf=0x0102,len;
  
  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		TDS._MssgInit   (   0,ref msg, 256,Fd);		// S1F2
		TDS._MssgBuild  (md,0,ref msg,0x00, 2,vb);	// L2
		TDS._MssgBuild  (md,0,ref msg,0x10, 6,"EQUIP1");//  MDLN
		TDS._MssgBuild  (md,0,ref msg,0x10, 6,"01.000");//  SOFTREV
    len=	TDS._MssgEnd    (md,0,ref msg);

  }else{					// メッセージ定義を使用する
		TDS._MDMssgInit (Md,0,ref msg, 256,"S1F2_E");
    len=	TDS._MDMssgEnd  (Md,0,ref msg);
  }

  if(len<0)	rtn=len;
  else		rtn=TDS._CommSend (Fd,0x0000,0,sf,xid,msg,len,ref hd);
  DispData(2,hd,did,sf,(uint)rtn,msg,len,rtn);

  return(rtn);
}


/* ========================================================================== */
/* S2F49 メッセージ構築及び送信 --------------------------------------------- */

private static int
SendS2F49()
{
  byte[]	hd=new byte[12],msg=new byte[1024];
  byte[]	vb=new byte[32];
  int[]		vi=new int[4];
  string	str,itm;
  int		rtn,md,sf=0x8200+49,no1,no2,len,i,j;

  cnt249++;	no1=(cnt249%2)+1;	no2=(cnt249%10)+1;
  str=string.Format("LOTID ({0,0:d4})",cnt249);
  
  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		TDS._MssgInit   (   0,ref msg,1024,Fd);		// S2F49
  		TDS._MssgBuild  (md,0,ref msg,0x00,  3,vb);	// L3
    vb[0]=0;	TDS._MssgBuild  (md,0,ref msg,0x08,  1,vb);	//  DATAIDB
  		TDS._MssgBuild  (md,0,ref msg,0x10,  0,"LOAD");	//  RCMD
  		TDS._MssgBuild  (md,0,ref msg,0x00,  4,vb);	//  L4
    vb[0]=1;	TDS._MssgBuild  (md,0,ref msg,0x08,  1,vb);	//   STID
    vb[0]=0;	TDS._MssgBuild  (md,0,ref msg,0x08,  1,vb);	//   MTKD
  		TDS._MssgBuild  (md,0,ref msg,0x10, 20,str);	//   LOTID
		TDS._MssgBuild  (md,0,ref msg,0x00,no1,vb);	//   L[no1]
    for(i=0;i<no1;i++){
		TDS._MssgBuild  (md,0,ref msg,0x00,no2,vb);	//    L[no2]
      for(j=0;j<no2;j++){
		TDS._MssgBuild  (md,0,ref msg,0x00,  2,vb);	//     L[2]
	str=string.Format("WAFER({0,0:d4}-{1,0:d1}-{2,0:d2})",cnt249,i+1,i+2);
  		TDS._MssgBuild  (md,0,ref msg,0x10, 20,str);	//      WAFERID
	str=string.Format("PPID ({0,0:d4}-{1,0:d1}-{2,0:d2})",cnt249,i+1,i+2);
  		TDS._MssgBuild  (md,0,ref msg,0x10, 16,str);	//      PPID
    } }
    len=	TDS._MssgEnd    (md,0,ref msg);

  }else{					// メッセージ定義を使用する
		TDS._MDMssgInit (Md,0,ref msg,1024,"S2F49");
  		TDS._MDMssgBuild(Md,0,ref msg,"LOTID",0,str);	//   LOTID
    vi[0]=no1;	TDS._MDMssgBuild(Md,0,ref msg,"NOI1" ,1,vi);	//   L[no1]
    for(i=0;i<no1;i++){
      itm  =string.Format("NOI2:{0,0:d1}"		,i+1);
      vi[0]=no2;TDS._MDMssgBuild(Md,0,ref msg,itm   ,1,vi);	//    L[no2]
      for(j=0;j<no2;j++){
	itm=string.Format("WAFERID:{0,0:d1}:{1,0:d1}"	,i+1,j+1);
	str=string.Format("WAFERID[{0,0:d4}-{1,0:d1}-{1,0:d2}]"
						,cnt249	,i+1,j+1);
  		TDS._MDMssgBuild(Md,0,ref msg,itm    ,0,str);	//     WAFERID
	itm=string.Format("PPID:{0,0:d1}:{1,0:d1}"	,i+1,j+1);
	str=string.Format("PPID [{0,0:d4}-{1,0:d1}-{1,0:d2}]"
						,cnt249	,i+1,j+1);
  		TDS._MDMssgBuild(Md,0,ref msg,itm    ,0,str);	//     PPID
    } }
    len=	TDS._MDMssgEnd  (Md,0,ref msg);
  }

  if(len<0)	rtn=len;	// SubFunction.h の SendS1F1() 内 (注) を参照。
  else		rtn=TDS._CommSend (Fd,0x0000,0,sf,0,msg,len,ref hd);
  DispData(2,hd,0x00,sf,(uint)rtn,msg,len,rtn);

  return(rtn);
}


/* ---------------------------------------------------------------------------*/
/* S2F50 メッセージ構築及び送信 --------------------------------------------- */

private static int
SendS2F50(
int		did,
uint		xid)
{
  byte[]	hd=new byte[12],msg=new byte[256];
  byte[]	vb=new byte[32];
  string	str;
  int		rtn,md,sf=0x0200+50,len;
  
  str=string.Format("LOTID ({0,0:d4})"	,++cnt250);

  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		TDS._MssgInit   (   0,ref msg, 256,Fd);		// S2F50
		TDS._MssgBuild  (md,0,ref msg,0x00,  2,vb);	// L2
    vb[0]=0;	TDS._MssgBuild  (md,0,ref msg,0x08,  1,vb);	//  HCACK
		TDS._MssgBuild  (md,0,ref msg,0x00,  2,vb);	//  L2
  		TDS._MssgBuild  (md,0,ref msg,0x10,  5,"PODID");//   PODID
  		TDS._MssgBuild  (md,0,ref msg,0x10, 20,str);	//   LOTID
    len=	TDS._MssgEnd    (md,0,ref msg);

  }else{					// メッセージ定義を使用する
		TDS._MDMssgInit (Md,0,ref msg, 256,"S2F50");
		TDS._MDMssgBuild(Md,0,ref msg,"LOTID",  0,str);	//   LOTID
    len=	TDS._MDMssgEnd  (Md,0,ref msg);
  }

  if(len<0)	rtn=len;
  else		rtn=TDS._CommSend (Fd,0x0000,0,sf,xid,msg,len,ref hd);
  DispData(2,hd,did,sf,(uint)rtn,msg,len,rtn);

  return(rtn);
}


/* ========================================================================== */
/* S6F11 メッセージ構築及び送信 --------------------------------------------- */

private static int
SendS6F11()
{
  byte[]	hd=new byte[12],msg=new byte[256];
  ushort[]	vs=new ushort[4];
  int		rtn,md,sf=0x8600+11,len;

  if((++cnt611)==65536) cnt611=0;

  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		TDS._MssgInit   (   0,ref msg, 256,Fd);		// S6F11
  		TDS._MssgBuild  (md,0,ref msg,0x00,  3,vs);	// L3
    vs[0]=(ushort)cnt611;
		TDS._MssgBuild	(md,0,ref msg,0x2a,  1,vs);	//  DATAID
    vs[0]=8;	TDS._MssgBuild  (md,0,ref msg,0x2a,  1,vs);	//  CEID
  		TDS._MssgBuild  (md,0,ref msg,0x00,  3,vs);	//  L3
  		TDS._MssgBuild  (md,0,ref msg,0x10, 16,"DATA1");//   DATA1
  		TDS._MssgBuild  (md,0,ref msg,0x10, 16,"DATA2");//   DATA2
  		TDS._MssgBuild  (md,0,ref msg,0x10, 14,"YYYYMMDDhhmmss");
						// @TIME (本当は現在時刻を設定
						// するのだが、省略。
    len=	TDS._MssgEnd    (md,0,ref msg);

  }else{					// メッセージ定義を使用する
		TDS._MDMssgInit (Md,0,ref msg, 256,"S6F11");
    vs[0]=(ushort)cnt611;
		TDS._MDMssgBuild(Md,0,ref msg,"DATAID",1,vs);	//  DATAID
    len=	TDS._MDMssgEnd  (Md,0,ref msg);
  }

  if(len<0)	rtn=len;	// SubFunction.h の SendS1F1() 内 (注) を参照。
  else		rtn=TDS._CommSend (Fd,0x0000,0,sf,0,msg,len,ref hd);
  DispData(2,hd,0x00,sf,(uint)rtn,msg,len,rtn);

  return(rtn);
}



/* ---------------------------------------------------------------------------*/
/* S6F12 メッセージ構築及び送信 --------------------------------------------- */

private static int
SendS6F12(
int		did,
uint		xid)
{
  byte[]	hd=new byte[12],msg=new byte[256];
  byte[]	vb=new byte[32];
  int		rtn,md,sf=0x0600+12,len;

  if((MSSG_USE_FILE&0x80)==0){			// メッセージ定義を使用しない
    md=		TDS._MssgInit	(   0,ref msg, 256,Fd);		// S6F12
    vb[0]=0;	TDS._MssgBuild	(md,0,ref msg,0x08,  1,vb);	// ACKC
    len=	TDS._MssgEnd 	(md,0,ref msg);

  }else{					// メッセージ定義を使用する
		TDS._MDMssgInit (Md,0,ref msg, 256,"S6F12");
    len=	TDS._MDMssgEnd  (Md,0,ref msg);
  }

  if(len<0)	rtn=len;
  else		rtn=TDS._CommSend (Fd,0x0000,0,sf,xid,msg,len,ref hd);
  DispData(2,hd,did,sf,(uint)rtn,msg,len,rtn);

  return(rtn);
}



/* ========================================================================== */
/* Callback 関数 ============================================================ */

private static int
CBRecvProc(
int		req,		// i  : ライブラリへの要求コード
int		rtn,		// i  : ライブラリからの戻り値
int		did,		// i  : SECS メッセージのデバイスＩＤ
int		xsf,		// i  : SECS メッセージのＳＦコード
uint		xid,		// i  : SECS メッセージのトランザクションＩＤ
byte[]		xhd,		// i  : SECS メッセージ・ヘッダ
byte[]		xmsg)		// i  : SECS メッセージ
{
  byte[]	hd=new byte[12],msg=new byte[1024];
  string	rname,sname;
  int		len,sf;

  DispData(1,xhd,did,xsf,xid,xmsg,rtn,rtn);
  switch(rtn){
    case -E_CONNECT:	Status=1;	break;		// Connected
    case -E_SELECT:	Status=2;	break;		// Selected
    case -E_DESELECT:	Status=1;	break;		// Deselected
    case -E_NOTCONNECT:	Status=0;	break;		// Not connected
  }

  if      (req== 0 && rtn>=0){
    if((MSSG_USE_FILE&0x80)==0){	// メッセージ定義ファイルを使用しない
					// 場合は、自力で SF-Code を調べ、２次
					// メッセージの送出の必要性を判断し、
					// 必要な場合は、送出する。
      if     (xsf==(0x8100+ 1)&&OType==0)	SendS1F2H (did,xid);
      else if(xsf==(0x8100+ 1)&&OType==1)	SendS1F2E (did,xid);
      else if(xsf==(0x8200+49))			SendS2F50 (did,xid);
      else if(xsf==(0x8600+11))			SendS6F12 (did,xid);

    }else if((xsf&0x8000)!=0){		// メッセージ定義ファイルを使用する
					// 場合は、本サンプルでは、自動返信機能
					// を使用する。
      msg=xmsg;
      if((len=TDS._MDMssgAutoRes(Md,0,xhd
		,ref msg,rtn,1024,out rname,out sname,out sf))	>=0){
	printf("RECV %s ..  Auto respond %s [S%dF%d]\n"
		,__arglist(rname,sname,sf/0x100,sf&0xff));
	rtn=TDS._CommSend(Fd,0x0000,0,sf,xid,msg,len,ref hd);
	DispData(2,hd,did,sf,xid,msg,len,rtn);
      }else{
	if(len!=(-930) && len!=(-931)){
	  printf("RECV Auto response error (%d)\n",__arglist(len));
  } } } }

  return(0);
}



/* -------------------------------------------------------------------------- */

private static void
RecvProcThread(
object		param)
{
  byte[]	msg=new byte[1024],hd=new byte[12];
  uint		xid;
  int		rtn,req,devid,sf;

  for(;Break==0;){
    req=0;
    rtn=TDS._CommRecv(Fd,0,out devid,out sf,out xid,ref msg,1024,ref hd);
    if(rtn==(-951)){
      Thread.Sleep(100);
    }else{
      if((-1000)<rtn && rtn<(-979))	req=(-rtn)-900;
      CBRecvProc(req,rtn,devid,sf,xid,hd,msg);
  } }
}



/* ========================================================================== */
/* Main 処理 ---------------------------------------------------------------- */

static int
Main(
string[]		argv)
{
  if(argv.Length<1 || (argv[0]!="h" && argv[0]!="e")){
    printf("CSIo {h|e}\n");

  }else{
    if(argv[0]=="h")	Host ();
    else		Equip();
  }

  return(0);
}



/* ========================================================================== */
/* ホスト側処理 ------------------------------------------------------------- */

private static void
Host()
{
  Thread		th;
  byte[]		hd=new byte[12],msg=new byte[1024];
  uint			xid,xids=0;
  int			req,rtn,mno,did=0,sf=0;

  OType=0;	Break=0;
  if((Fd=TDS._CommOpen(0x02,PARAMFILE,"HOST"))			< 0) goto Exit;
  printf("(H) Opened (%d)\n",__arglist(Fd));
  if((MSSG_USE_FILE&0x80)!=0) Md=TDS._MDMssgInitialize(0x4000,Fd,"");

  if(USE_CALLBACK!=0){
    th=new Thread(new ParameterizedThreadStart(RecvProcThread));
    th.Start("");
  }

  for(;;){		rtn=0;
    if(USE_CALLBACK==0)	printf("Req (0:Exit 1:Recv 2:Send) : ");
    else		printf("Req (0:Exit 2:Send) : ");
    scanf ("%d",__arglist(out req));
    if      (req==0){						break;

    }else if(req==1){
      if((rtn=TDS._CommRecv(Fd,0,out did,out sf,out xid
					,ref msg,1024,ref hd))	>=0) xids=xid;
      DispData(1,hd,did,sf,xid,msg,rtn,rtn);

    }else if(req==2){
      if(USE_CALLBACK==0) printf("Message(1:S1F1 2:S2F49  6:S1F2 7:S6F12) : ");
      else		  printf("Message(1:S1F1 2:S2F49) : ");
      scanf("%d",__arglist(out mno));
      switch(mno){
	case 1: rtn=SendS1F1 ();		break;
	case 2: rtn=SendS2F49();		break;
	case 6: rtn=SendS1F2H(did,xids);	break;
	case 7: rtn=SendS6F12(did,xids);	break;
    } }
    if(rtn<(-999) || ((-900)<rtn && rtn<0)){
      printf("(H) I/O Error (%d)\n"	,__arglist(rtn));
  } }

Exit:
  Break=1;
  if(Md>0) TDS._MDMssgTerminate(Md,0);
  if(Fd>0) TDS._CommClose(Fd,0);
  else	   printf("(H) Error (%d)\n"	,__arglist(Fd));
}



/* ========================================================================== */
/* 装置側処理 --------------------------------------------------------------- */

private static void
Equip()
{
  Thread		th;
  byte[]		hd=new byte[12],msg=new byte[1024];
  uint			xid,xids=0;
  int			req,rtn,mno,did=0,sf=0;

  OType=1;	Break=0;
  if((Fd=TDS._CommOpen(0x02,PARAMFILE,"EQUIP"))			< 0) goto Exit;
  printf("(E) Opened (%d)\n",__arglist(Fd));
  if((MSSG_USE_FILE&0x80)!=0) Md=TDS._MDMssgInitialize(0x4000,Fd,"");

  if(USE_CALLBACK!=0){
    th=new Thread(new ParameterizedThreadStart(RecvProcThread));
    th.Start("");
  }

  if(TDS._CommSend(Fd,0x0100,0,0,0,msg,0,ref hd)		< 0) goto Exit;
  printf("(E) Connected\n");
  if(TDS._CommSend(Fd,0x0200,0,0,0,msg,0,ref hd)		< 0) goto Exit;
  printf("(E) Selected\n");

  for(;;){		rtn=0;
    if(USE_CALLBACK==0)	printf("Req (0:Exit 1:Recv 2:Send) : ");
    else		printf("Req (0:Exit 2:Send) : ");
    scanf ("%d",__arglist(out req));
    if      (req==0){						 break;

    }else if(req==1){
      if((rtn=TDS._CommRecv(Fd,0,out did,out sf,out xid
					,ref msg,1024,ref hd))	>=0) xids=xid;
      DispData(1,hd,did,sf,xid,msg,rtn,rtn);

    }else if(req==2){
      if(USE_CALLBACK==0) printf("Message(1:S1F1 2:S6F11  6:S1F2 7:S2F50) : ");
      else		  printf("Message(1:S1F1 2:S6F11) : ");
      scanf("%d",__arglist(out mno));
      switch(mno){
	case 1: rtn=SendS1F1 ();		break;
	case 2: rtn=SendS6F11();		break;
	case 6: rtn=SendS1F2E(did,xids);	break;
	case 7: rtn=SendS2F50(did,xids);	break;
    } }
    if(rtn<(-999) || ((-900)<rtn && rtn<0)){
      printf("(E) I/O Error (%d)\n"	,__arglist(rtn));
  } }

// Deselect request は行わない。（SEMI では HSMS-SS において、Deselect request
// は行ってはならない、としている。）
//if(TDS._CommSend(Fd,0x0800,0,0,0,msg,0,ref hd)		< 0) goto Exit;
//printf("(E) Deselected\n");
  if(TDS._CommSend(Fd,0x0900,0,0,0,msg,0,ref hd)		< 0) goto Exit;
  printf("(E) Separate\n");

Exit:
  Break=1;
  if(Md>0) TDS._MDMssgTerminate(Md,0);
  if(Fd>0) TDS._CommClose(Fd,0);
  else	   printf("(E) Error (%d)\n"	,__arglist(Fd));
}

}
}
