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


// =============================================================================
//
// CallbackIo : Test and sample program
//
//   Respond to received messages using Callback function. Also, the
//   user-defined trace output function is used to perform trace output by
//   itself.
//
//   (Note) In this sample, when making an HSMS connection, it is assumed that
//	the HOST side executes Passive connection and the EQUIP side executes
//	Active connection.
//
//
// Starting method
//
//   CallbackIo {h|e} [option [send_wait [send_sleep]]]
//   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//   h    : Refer to [HOST]  section of Sample.ini to determine operation
//   e    : Refer to [EQUIP] section of Sample.ini to determine operation
//
//   option : Options for setting the details of operation (optional)
//	      Specify as a decimal number or as a hexadecimal number with 0x or
//	      x as a prefix.
//	      The details of the specified value for each bit are shown below.
//    F       8 7654 3210
//   +----+----+----+----+
//    |       | |||| |||+-- Function to use
//    |       | |||| |||    =0: _TDSCommXxxxx()		1:_TDSUDrvXxxx()
//    |       | |||| ||+--- SECS Header Byte order
//    |       | |||| ||     =0: System order		1: Network order
//    |       | |||| |+---- Format for displaying messages in SECS list format
//    |       | |||| |	    =0: TDS Format		1: SML Format
//    |       | |||| +----- Whether to use MssgNextL() for SECS message display
//    |       | ||||	    =0: Not use			1: Use
//    |       | |||+------- Whether to use the SECS message definition file
//    |       | |||	    =0: Not use			1: Use
//    |       | ||+-------- Whether to execute SECS message reception in a
//    |       | ||	    dedicated thread
//    |       | ||	    =0: No			1: Yes
//    |       | |+--------- Whether to use the Callback function for receiving
//    |       | |	    transmission results
//    |       | |	    =0: Not use			1: Use
//    |       | +---------- Whether user-specified trace output function is used
//    |       |		    =0: Not use			1: Use
//    |       +------------ Suppress Message structure output for testing
//    |			    =0: No			1: Yes
//    +-------------------- Synchronize the connection status with communication
//			    control processing thread
//			    =0: No			1: Yes
//
//   send_wait   : Transmission interval when sending continuous messages (ms).
//   send_sleep  : Time to rest for a short time after sending a given number
//                 of times (SEND_NO) when sending continuous messages (ms).
//
//   Normally, "CallbackIo h" and "CallbackIo e" both operate on same machine or
//   different machines to communicate with each other.
//
//   After startup, refer to the menu display, enter request code (0,2,3) etc.
//   and execute functions such as sending a message to the other side.
//
//   The received message and various event information complete processing in
//   Callback function. When following "MSSG_USE_FILE" is set to 0x80, secondary
//   message corresponding to received primary message is automatically sent
//   according to contents of message definition file Sample.sml set in
//   Sample.ini.
//   When MSSG_USE_FILE=0x80 (using a message definition file), message is sent
//   out by specifying name of any message defined in message definition file.
//
//   Continuous sending and receiving is performed at a fixed interval
//   (specified by SEND_WAIT) according to request code.
//   Continuous sending and receiving is terminated by input of CTRL-C.
//   In this case, sending of primary message and sending of secondary message
//   may occur simultaneously. When creating a send message using a message
//   definition file, in this sample, only one descriptor for message analysis
//   is prepared. Therefore, it is necessary to perform lock control so that
//   message construction in sending processing and reply processing does not
//   collide. See top of SunFunctions.h for details.
// 
//   This sample omits some of abnormal processing.
//
//   The following "SECS_MODE" is defined to use _TDSCommXxxx() as a processing
//   function and to judge whether or not connection processing to Passive side
//   in case of Active connection is performed.
//   (Note) Changing this value alone does not make TDS aware of the change in
//      SECS1 or HSMS.  TDS determines SECS1 or HSMS based on the setting of
//      SECSMODE in the configuration file (.ini).  To change this value, you
//      must change the setting of SECSMODE in .ini.
//
//   If the second parameter (option) at startup is not specified, the details
//   of operation are determined by the following constants.
//
//   The following "FUNC_TYPE" is defined to select type of processing function
//   to be used.
//   When using _TDSUDrvXxxx(), there is no need to perform connection
//   processing for HSMS-SS Active connection, so there is no need to set
//   "SECS_MODE" in principle.
//   TDS switches SECS-1 connection or HSMS connection in setting file (.ini).
//   When _TDSUDrvXxxx() is used, user AP is not necessary to know connection
//   method (SECS-1 or HSMS and  Passive or Active).
//   However, when using _TDSUDrvXxxx(), Callback processing needs to be
//   performed by itself.
//
//   By changing values of "MSSG_USE_FILE" and "MSSG_DISP_TYPE" defined below,
//   you can change output format of SECS message displayed by this AP.
//   By changing value of "MSSG_USE_NEXTL", it is possible to change whether to
//   acquire field values as data or display list format when analyzing message
//   contents.
//   Similarly, when "USE_RECVTHREAD" is set to =1, a thread for performing
//   receiving process is activated, and display of received message and output
//   of the secondary message are automatically performed in the thread.
//   When using the receiving thread, keep in mind that prompt message may be
//   distorted.
//   Changing value of "USE_SEND_CB" can change use of Callback for Sending
//   result.
//   Changing value of "USE_USER_TRACE" can change use of Callback for User
//   Trace output.
//   If "HEADER_BO" is set to 1, Byte Order of SECS message header obtained at
//   the time of Receive and Send can be obtained as the original network Byte
//   Order of the SECS message.
//
//
// =============================================================================
//
// CallbackIo : テスト 及び サンプル・プログラム
//
//   Callback 関数を使用して受信メッセージに対する応答処理を行う。また、ユーザ
//   定義トレース出力関数を使用し、トレース出力を自力で行う。
//
//   (注) 本サンプルでは HSMS 接続する場合、HOST 側が Passive 接続、EQUIP 側が
//	Active 接続を実行すると仮定している。
//
//
// 起動方法
//
//   CallbackIo {h|e} [option [send_wait [send_sleep]]]
//   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//   h    : Sample.ini の [HOST]  セクションを参照し動作を決定する
//   e    : Sample.ini の [EQUIP]      :              :
//
//   option : 動作の詳細を設定するオプション (省略可能)
//	      10進数 もしくは 接頭文字として 0x もしくは x を付与した 16進数
//	      で指定する。
//	      以下に指定値のビット毎の詳細を以下に示す。
//    F       8 7654 3210
//   +----+----+----+----+
//    |	      | |||| |||+-- 使用する関数
//    |	      | |||| |||    =0: _TDSCommXxxxx()		1:_TDSUDrvXxxx()
//    |	      | |||| ||+--- SECS Header Byte order
//    |	      | |||| ||	    =0: System order		1: Network order
//    |	      | |||| |+---- SECS リスト形式でメッセージを表示する場合の形式
//    |	      | |||| |	    =0: TDS 形式		1: SML 形式
//    |	      | |||| +----- SECS メッセージ表示に MssgNextL() を使用するか否か
//    |	      | ||||	    =0: 使用しない		1: 使用する
//    |	      | |||+------- SECS メッセージ定義ファイル使用の有無
//    |	      | |||	    =0: 使用しない		1: 使用する
//    |	      | ||+-------- SECS メッセージ受信を専用スレッドで実行するか否か
//    |	      | ||	    =0: No			1: Yes
//    |	      | |+--------- 送信結果受信用 Callback 関数使用の有無
//    |	      | |	    =0: 使用しない		1: 使用する
//    |	      | +---------- ユーザ指定のトレース出力関数使用の有無
//    |	      |		    =0: 使用しない		1: 使用する
//    |	      +------------ 試験のため Message 構造の出力を抑止する
//    |			    =0: No			1: Yes
//    +-------------------- 接続状態を通信制御部に同期する
//			    =0: No			1: Yes
//
//   send_wait   : 連続メッセージ送信時の送信間隔 (ms)
//   send_sleep  : 連続メッセージ送信時に所定の回数 (SEND_NO) 送信した後に少し
//                 送信をお休みする時間 (ms)
//
//   通常、CallbackIo h 及び CallbackIo e の両方を、同一マシン、もしくは異なる
//   マシン上で動作させて、相互通信を行う。
//
//   起動後、メニュ表示を参照し、要求コード (0,2,3) 等を入力して、相手側にメッ
//   セージを送出する等の機能を実行する。
//   受信したメッセージ、及び各種イベント情報は Callback 関数の中で処理を完結
//   する。下記 "MSSG_USE_FILE" を 0x80 に設定すると、Sample.ini 中で設定した
//   メッセージ定義ファイル Sample.sml の内容により、受信１次メッセージに対応
//   する２次メッセージを自動送出する。
//   MSSG_USE_FILE=0x80（メッセージ定義ファイルを使用する）場合は、メッセージ
//   定義ファイルに定義した任意のメッセージの名称を指定することにより、その
//   メッセージを送出する。
// 
//   要求コードにより、一定間隔（SEND_WAIT にて指定）での連続送受信を行う。
//   連続送受信は、CTRL-C の入力により終了する。
//   この場合、１次メッセージの送信と、２次メッセージの送信が同時に発生する可
//   能性がある。メッセージ定義ファイルを使用して送信メッセージを作成する場合、
//   本サンプルでは、メッセージ解析用のデスクリプタは１つしか用意していないの
//   で、送信処理、返信処理でのメッセージ構築がバッティングしないようにロック
//   制御をする必要がある。詳細は SunFunctions.h の先頭部分を参照すること。
// 
//   本サンプルは、異常時処理を一部省略している。
//
//   以下で定義する "SECS_MODE" は、処理関数として _TDSCommXxxx() を使用する
//   場合で Active 接続の場合の Passive 側への接続処理の実行の有無を判断する
//   ために定義する。
//   (注) この値の変更だけで TDS が SECS1 or HSMS の変更を認識するわけではない。 
//      TDS は SECS1 or HSMS を判断するのは設定ファイル (.ini) の SECSMODE の
//      設定によるので、この値を変更する場合は .ini の SECSMODE の設定変更を
//      しなければならない。
//
//   起動時の2番目のパラメータ (option) を指定しない場合は、以下の定数により
//   動作の詳細を決定する。
//
//   "FUNC_TYPE" は、使用する処理関数の種別を選択するために定義する。
//   _TDSUDrvXxxx() を使用する場合は、HSMS-SS Active 接続の場合の接続処理を行
//   う必要がないので、本来であれば "SECS_MODE" の設定も必要がない。
//   TDS は、SECS-1 接続 or HSMS-SS 接続の切替は、設定ファイル (.ini) にて行う
//   ため、処理関数として _TDSUDrvXxxx() を使用する限りは、ユーザＡＰでは、
//   接続方式（SECS-1/HSMS-SS そして HSMS-SS時の Passive/Active）を知る必要は
//   ない。ただし _TDSUDrvXxxx() を使用する場合は、Callback 処理は自力で行う
//   必要がある。
//
//   以下で定義する "MSSG_USE_FILE"、"MSSG_DISP_TYPE" の値を変更すると、本ＡＰ
//   にて表示する SECS メッセージの出力形式を変更できる。
//   "MSSG_USE_NEXTL" の値を変更すると、メッセージ内容を解析する際に、項目値を
//   データとして取得するか、表示リスト形式で取得するかを変更できる。
//   同様に "USE_RECVTHREAD" を =1 とすると、受信処理を行うスレッドを起動し、そ
//   のスレッド内で受信メッセージの表示、２次メッセージの出力を自動で行う。
//   受信スレッドを使用する場合、入力促進用メッセージに乱れが生じる事に留意
//   すること。
//   "USE_SEND_CB" の値を変更すると、送信結果取得用 Callback の使用の有無を変更
//   できる。
//   "USE_USER_TRACE" の値を変更すると、User Trace 出力用 Callback の使用の有無
//   を変更できる。
//   "HEADER_BO" を 1 とすると、Receive、Send 時に取得する SECS メッセージ・
//   ヘッダのバイトオーダーを SECS メッセージ本来のネットワーク・バイトオーダー
//   として取得できる。
//
// =============================================================================

#include	"TDS.h"


#define		PO		fprintf(stdout
#define		PE		fprintf(stderr

#define		SECS_MODE	1		// SECS/HSMS mode
						// 0    : SECS-1
						// 1    : HSMS
						// (Note) See comments above
						// (注) 上部コメント参照
#define		UDRV_MASK  	0x8383ffff	// Mask value for UDrvOpen()
						// 0          : Set =0x49
						// 0x8383ffff : All event

#define		FUNC_TYPE	1		// Type of function used
						// 通信に使用する関数の種別
						// 0    : _TDSCommXxxxx
						// 1    : _TDSUDrvXxxxx
#define		HEADER_BO	0		// Header byte order
						// 取得するヘッダの Byte Order
						// 0    : System  Order
						// 1    : Network Order
#define		MSSG_DISP_TYPE	0x20		// SECS Message display format
						// 0x00 : TDS Format
						// 0x20 : SML Format
#define		MSSG_USE_NEXTL	1		// Use MssgNextL() or not
						// 0    : Not use
						// 1    : Use

#define		MSSG_USE_FILE	0x00		// Message definition file
						// 0x00 : Not use
						// 0x80 : Use to display item
						//	  names
						//	  使用して項目名表示する
#define		USE_RECVTHREAD	0		// Execute receiving process on
						// its own thread
						// 受信処理を自前スレッドで実行
						// 0	: No
						// 1	: Yes
#define		USE_SEND_CB	0		// Use CB for sending result
						// 送信結果受信用 Callback 使用
						// 0	: Not Use
						// 1	: Use
#define		USE_USER_TRACE	0		// Use User trace call back
						// function or not
						// UserTrace用CB を使用するか否か
						// 0    : Not use
						// 1    : Use

#define		SYNC_STATUS	1		// Synchronize connection status
						// 接続状態の同期
						// 0    : No
						// 1    : Yes

#define		SEND_WAIT	200		// Message sending interval(ms)
						// メッセージ送信間隔      (m秒)
#define		SEND_NO		10		// Number of times until message
						// sending off
						// メッセージ送信休みまでの回数
#define		SEND_SLEEP	1000		// Wait time		   (ms)
						// お休み時間              (m秒)
#define		ERROR_SLEEP	2000		// Wait time in error case (ms)
						// エラー発生時お休み時間  (m秒)

#define		PARAMFILE	"Sample.ini"


static int	FuncType	=FUNC_TYPE;		// argv[2]&0x0001
static int	HeaderBO	=HEADER_BO;		// argv[2]&0x0002
static int	MssgDispType	=MSSG_DISP_TYPE;	// argv[2]&0x0004
static int	MssgUseNextL	=MSSG_USE_NEXTL;	// argv[2]&0x0008
static int	MssgUseFile	=MSSG_USE_FILE;		// argv[2]&0x0010
static int	UseRecvThrd	=USE_RECVTHREAD;	// argv[2]&0x0020
static int	UseSendCB	=USE_SEND_CB;		// argv[2]&0x0040
static int	UseUserTrace	=USE_USER_TRACE;	// argv[2]&0x0080
static int	SyncStatus	=SYNC_STATUS;		// argv[2]&0x8000
static int	NoListOut	=0;			// argv[2]&0x0100

static int	SendWait	=SEND_WAIT;		// argv[3]
static int	SendSleep	=SEND_SLEEP;		// argv[4]

static int	Finish=0;	// End instruction to thread
				// スレッドへの終了指示


static void	Host();
static void	Equip();



// =============================================================================
// Common functions ============================================================

#include	"SubFunctions.h"



// =============================================================================
// Receive processing thread ---------------------------------------------------
// 受信処理スレッド ------------------------------------------------------------

#if defined	WINDOWS
static void
#else
static void*
#endif
RecvProcThread(
void			*par)
{
  TDSCBData		cbd;
  char			msg[4096];
  int			msz,cmd=0;

  cbd.vec=1;		cbd.msg=msg;
  msz=sizeof(msg);

  for(;Finish==0;){
    // Header Byte Order (HBO) conversion is performed by CBRecvProc() in the
    // same way as when TDSCommOpen() instructs use of Callback for receiving,
    // so treat it in the same way. (If Callback is called from TDS, do not
    // convert HBO.)  Therefore, HBO conversion is not instructed here.
    // ヘッダ・バイトオーダー変換は、_TDSCommOpen() で受信に Callback 使用を
    // 指示した場合と同様（Callback の Call 時は、ヘッダ・バイトオーダー変換を
    // する訳ではなく）CBRecvProc() で行うので、それと同様の扱いとなるよう、ここ
    // ではヘッダ・バイトオーダー変換を指示しない。
    //if(HeaderBO!=0)	cmd=0x2000;
    //else		cmd=0x0000;
    if(FuncType	 ==0){
      cbd.rtn=
	_TDSCommRecv(Fd,cmd,&cbd.devid,&cbd.sf,&cbd.xid,cbd.msg,msz,&cbd.thd);
    }else{
      cbd.rtn=
	_TDSUDrvRecv(Fd,cmd,&cbd.devid,&cbd.sf,&cbd.xid,cbd.msg,msz,&cbd.thd);
    }
    if(cbd.rtn	== -E_NODATA)	Sleep(100);
    else{
      if((-1000)<cbd.rtn && cbd.rtn<(-959))	cbd.req=(-cbd.rtn)-900;
      else					cbd.req=0;
      CBRecvProc(par,&cbd);
      if(cbd.rtn== -E_ACCES)	Break=1;
  } }
}



// =============================================================================
// Main process ----------------------------------------------------------------

int
main(
int			argc,
char			*argv[])
{
  int			opt=0;

  // ---------------------------------------------------------------------------
  { TDSVersion		inf;	// Display TDS information
    int			bits;	// This code has nothing to do with running TDS.
    bits=_TDSGetVersion(&inf);
    PO,"Version = %-20s %-20s\n"	,inf.version[0],inf.version[1]);
    PO,"Date    = %-20s %-20s\n"	,inf.verdate[0],inf.verdate[1]);
    PO,"Compile = %-20s %-20s\n"	,inf.compile[0],inf.compile[1]);
    PO,"#of Bit = %d\n"			,bits);
  }
  // ---------------------------------------------------------------------------

  // ---------------------------------------------------------------------------
  if(FuncType		!=0)		opt|=0x0001;
  if(HeaderBO		!=0)		opt|=0x0002;
  if(MssgDispType	!=0x00)		opt|=0x0004;
  if(MssgUseNextL	!=0)		opt|=0x0008;
  if(MssgUseFile	!=0x00)		opt|=0x0010;
  if(UseRecvThrd	!=0)		opt|=0x0020;
  if(UseSendCB		!=0)		opt|=0x0040;
  if(UseUserTrace	!=0)		opt|=0x0080;
  if(NoListOut		!=0)		opt|=0x0100;
  if(SyncStatus		!=0)		opt|=0x8000;
  if(argc>2){			// Analysis of running parameters
    if	   (argv[2][0]=='x' || argv[2][0]=='X')	 sscanf(&argv[2][1],"%x",&opt);
    else if(argv[2][0]=='0' &&
	   (argv[2][1]=='x' || argv[2][1]=='X')) sscanf(&argv[2][2],"%x",&opt);
    else					 sscanf(&argv[2][0],"%d",&opt);
    if(opt&0x0001) FuncType    =1;	else FuncType    =0;
    if(opt&0x0002) HeaderBO    =1;	else HeaderBO    =0;
    if(opt&0x0004) MssgDispType=0x20;	else MssgDispType=0x00;
    if(opt&0x0008) MssgUseNextL=1;	else MssgUseNextL=0;
    if(opt&0x0010) MssgUseFile =0x80;	else MssgUseFile =0x00;
    if(opt&0x0020) UseRecvThrd =1;	else UseRecvThrd =0;
    if(opt&0x0040) UseSendCB   =1;	else UseSendCB   =0;
    if(opt&0x0080) UseUserTrace=1;	else UseUserTrace=0;
    if(opt&0x0100) NoListOut   =1;	else NoListOut   =0;
    if(opt&0x8000) SyncStatus  =1;	else SyncStatus	 =0;
  }
  if(argc>3)	sscanf(argv[3],"%d"	,&SendWait);
  if(argc>4)	sscanf(argv[4],"%d"	,&SendSleep);

  PO,"option      =0x%02x,%d\n"		,opt,opt);
  PO,"FuncType    =%d\n"		,FuncType);
  PO,"HeaderBO    =%d\n"		,HeaderBO);
  PO,"MssgDispType=0x%02x\n"		,MssgDispType);
  PO,"MssgUseNextL=%d\n"		,MssgUseNextL);
  PO,"MssgUseFile =0x%02x\n"		,MssgUseFile);
  PO,"UseRecvThrd =%d\n"		,UseRecvThrd);
  PO,"UseSendCB   =%d\n"		,UseSendCB);
  PO,"UseUserTrace=%d\n"		,UseUserTrace);
  PO,"NoListOut   =%d\n"		,NoListOut);
  PO,"SyncStatus  =%d\n"		,SyncStatus);
  PO,"SendWait    =%d\n"		,SendWait);
  PO,"SendSleep   =%d\n"		,SendSleep);
  // ---------------------------------------------------------------------------

  if(argc<2 || (argv[1][0]!='h' && argv[1][0]!='e')){
    PE,"\nUsage: %s {host|equip} [option [send_wait [send_sleep]]]\n",argv[0]);
    PE,"\n  option\n");
    PE,  "   F       8 7654 3210\n");
    PE,  "  +----+----+----+----+\n");
    PE,  "   |       | |||| |||+-- Function to use\n");
    PE,  "   |       | |||| |||    =0: _TDSCommXxxxx()           1:_TDSUDrvXxxx()\n");
    PE,  "   |       | |||| ||+--- SECS Header Byte order\n");
    PE,  "   |       | |||| ||     =0: System order              1: Network order\n");
    PE,  "   |       | |||| |+---- Format for displaying messages in SECS list format\n");
    PE,  "   |       | |||| |      =0: TDS Format                1: SML Format\n");
    PE,  "   |       | |||| +----- Whether to use MssgNextL() for SECS message display\n");
    PE,  "   |       | ||||        =0: Not use                   1: Use\n");
    PE,  "   |       | |||+------- Whether to use the SECS message definition file\n");
    PE,  "   |       | |||         =0: Not use                   1: Use\n");
    PE,  "   |       | ||+-------- Whether to execute SECS message reception in a dedicated thread\n");
    PE,  "   |       | ||          =0: No                        1: Yes\n");
    PE,  "   |       | |+--------- Whether to use the Callback function for receiving transmission results\n");
    PE,  "   |       | |           =0: Not use                   1: Use\n");
    PE,  "   |       | +---------- Whether user-specified trace output function is used\n");
    PE,  "   |       |             =0: Not use                   1: Use\n");
    PE,  "   |       +------------ Suppress Message structure output for testing\n");
    PE,  "   |                     =0: No                        1: Yes\n");
    PE,  "   +-------------------- Synchronize connection status\n");
    PE,  "                         =0: No                        1: Yes\n");
    PE,  "  send_wait   : Message sending interval     (%5dms)\n",SendWait);
    PE,  "  send_sleep  : Wait time                    (%5dms)\n",SendSleep);
    PE,"\n");
    exit(1);
  }

  signal(SIGINT,CBSignal);
//InitializeCriticalSection(&Cs0);
  InitializeCriticalSection(&Cs1);

  if(argv[1][0]=='h')	Host ();
  else			Equip();

//DeleteCriticalSection(&Cs0);
  DeleteCriticalSection(&Cs1);
  exit(0);
}



// =============================================================================
// Host side process -----------------------------------------------------------

static void
Host()
{
  int			(*cbrecv )(void*,TDSCBData*)=0;
  int			(*cbsend )(void*,TDSCBData*)=0;
  int			(*cbtrace)(void*,TDSTRData*)=0;
  char			mname[64];
  int			req,rtn,omd,mno,stat,i;

  if(UseRecvThrd ==0)	cbrecv	= CBRecvProc;
  if(UseSendCB   !=0)	cbsend	= CBSend;
  if(UseUserTrace!=0)	cbtrace	= CBTrace;
  if(SyncStatus  ==0)	omd	= 0x0002;
  else			omd	= 0x1002;
  if(FuncType	 ==0){
    Fd=_TDSCommOpen(omd,PARAMFILE,"HOST",cbrecv,0,cbsend,0,cbtrace,0);
  }else{
    Fd=_TDSUDrvOpen(omd,PARAMFILE,"HOST",UDRV_MASK);
  }
  if(Fd								< 0) goto Exit;
  PO,"(H) Opened (%d)\n",Fd);	OType=0;
  if((MssgUseFile&0x80)!=0) Md=_TDSMDMssgInitialize(0x4000,Fd,0);

	// When _TDSUDrvXxxx() is used and when receiving process is specified
	// in the receiving thread, start the receiving process thread.
  	// _TDSUDrvXxxx() 使用の場合 及び 受信用スレッドでの受信処理指定の場合
	// は受信処理スレッドを起動する
  if(FuncType	 !=0 || UseRecvThrd!=0){
    _beginthread(RecvProcThread,0,0);
  }

  if(SECS_MODE!=0){
	// In case of HSMS, wait for Select state.
	// For SECS-1 connection, the following connection status confirmation
	// processing may be performed.
	// (Note 1) If connection status is not synchronized with (omd&0x1000)==0
	// at CommOpen() or UDrvOpen(), connection status change report must be
	// received with CommRecv() or UDrvRecv().  (The processing is not
	// described here.)
	// If you are using a receive thread with UseRecvThrd!=0, you don't need,
	// because that thread will receive connection state change reports.
	// (Note 2) In the first place, even if it is not in Selected state, if
	// you think that "_TDSCommSend() etc. will only result in an error
	// (E_ILSEQ)", at least in this AP, this "waiting for Selected state"
	// operation is not necessary.
	//
	// HSMS 接続の場合、Select 状態になるのを待つ。
	// SECS-1 接続の場合も以下の接続ステータス確認処理は行ってもかまわない。
	// (注1) CommOpen()、UDrvOpen() 時に (omd&0x1000)==0 として接続ステータス
	// の同期を行わない場合は、CommRecv()、UDrvRecv() で、接続状態変化報告を
	// 受信しなければならない。
	// UseRecvThrd!=0 で受信スレッドを使用している場合は、そのスレッドにより
	// 接続状態変化報告を受信するので、その必要はない。
	// (注2) そもそも、仮に Selected 状態になっていなくても、「以降の
	// _TDSCommSend() 等がエラー (E_ILSEQ) になるだけ」と考えれば、少なくとも
	// この AP では、この「Selected 状態になるのを待つ」という動作自体が必要
	// ない。
    for(;;){
	// CallbackIo always uses the receive CB, so there is no need to call
	// Recv() below.
	// CallbackIo では、常に受信 CB を使用するので、以下の Recv() の Call は
	// 必要ない。
    //if(SyncStatus==0 && UseRecvThrd==0){
    //  if(FuncType==0)	_TDSCommRecv(Fd,0x0000,&did,&sf,&xid,msg,msz,&hd);
    //  else		_TDSUDrvRecv(Fd,0x0000,&did,&sf,&xid,msg,msz,&hd);
    //}
      if(FuncType==0)	rtn=_TDSCommStatus(Fd,0x0000);
      else		rtn=_TDSUDrvStatus(Fd,0x0000);
      if(rtn							< 0) goto Exit;
      if(rtn							==3) break;
      Sleep(100);
    }
    printf("(H) Selected\n");
  }

// (Note 3) In other words, when SyncStatus!=0, the following code is OK for
//    both SECS-1 and HSMS.
// (注3) 即ち SyncStatus!=0 の場合、SECS-1、HSMS 共に以下のコードでＯＫ。
//for(;;){
//  if(FuncType==0)	rtn=_TDSCommStatus(Fd,0x0000)
//  else		rtn=_TDSUDrvStatus(Fd,0x0000)
//  if(rtn							< 0) goto Exit;
//  if(rtn							==3) break;
//  Sleep(100);
//}

  for(;;){
    req=99;	rtn=0;
    PE,"Req (0:Exit 2:Send 3:Continuous Send 4:Statistic Report) : ");
    scanf("%d",&req);
    if      (req==0){						break;

    }else if(req==2){
      char			*frm0="1:S1F1 2:S1F21 3:S2F49";
      if((MssgUseFile&0x80)!=0)  frm0="1:S1F1 2:S1F21 3:S2F49   9:Any";
      PE,"Message(%s) : "	,frm0);			scanf("%d",&mno);
      switch(mno){
	case 1: rtn=SendS1F1 ();				break;
	case 2: rtn=SendS1F21();				break;
	case 3: rtn=SendS2F49();				break;
	case 9: if((MssgUseFile&0x80)!=0){
		  PE,"Message Name : ");		scanf("%s",mname);
		  rtn=SendNamedMssg(0,0,mname);
		}						break;
      }

    }else if(req==3){
      for(Break=i=0;Break==0;i++){
	Sleep(SendWait);
	if(FuncType==0)	stat=_TDSCommStatus(Fd,0);
	else		stat=_TDSUDrvStatus(Fd,0);
	if(stat==3){
	  if((rtn=SendS2F49())	< 0){
	    PE,"(H) Send error (%d)\n"	,rtn);	Sleep(ERROR_SLEEP);
	    if(rtn		==-E_ACCES)	Break=1;
	  }
	  if((i%SEND_NO)		==0)	Sleep(SendSleep);
      } }
      rtn=0;

    }else if(req==4){
      TDSSInfoSP	sp;
      int		noe,nort,i,s,f,d;
      for(noe=i=0;i<2;i++){
	if(i==0) PO,"\n[Sent Primary Message Table]\n");
	else	 PO,"\n[Received Primary Message Table]\n");
	PO, "   SC  FC: Primary: Second.:Async T3TO"
	    ": S9F1   F3   F5   F7   F9  F11  F13:  Total.ms (   Ave. ms)\n");
	for(s=0;s<128;s++){	for(f=0;f<256;f++){
	  if(FuncType	==0)	rtn=_TDSCommSInfoGet(Fd,i,(s<<8)|f,&sp);
	  else			rtn=_TDSUDrvSInfoGet(Fd,i,(s<<8)|f,&sp);
	  if(rtn	==0){	noe++;
	    if((d=f)	!=0)	f++;
	    if((nort=sp.nort)==0) nort=1;
	    PO,	"  %3d %3d:%8d:%8d:%5d%5d"
		":%5d%5d%5d%5d%5d%5d%5d:%8lldms (%8.1fms)\n"
		  ,s,d  ,sp.nox  ,sp.nort ,sp.norf,sp.not3
			,sp.nof1 ,sp.nof3 ,sp.nof5,sp.nof7,sp.nof9
			,sp.nof11,sp.nof13,sp.trtok,(float)sp.trtok/(float)nort);
	  }else{
	    if(f	!=0)	f++;
      } }}}
      if(noe==0){
	PO,"\nSet STATISINFO=1 in the .ini file when using this function.\n\n");
      }else	PO,"\n");
      rtn=0;
    }

    if(rtn<(-999) || ((-900)<rtn && rtn<0) ){
      PE,"(H) Send error (%d)\n"	,rtn);
  } }

Exit:
  Finish=1;
  if(Fd> 0){
    if(FuncType	==0)	_TDSCommClose(Fd,0);
    else		_TDSUDrvClose(Fd,0);
  }else			PE,"(H) Error (%d)\n",Fd);
  if(Md> 0)		_TDSMDMssgTerminate(Md,0);
}



// =============================================================================
// Equipment side process ------------------------------------------------------

static void
Equip()
{
  int			(*cbrecv )(void*,TDSCBData*)=0;
  int			(*cbsend) (void*,TDSCBData*)=0;
  int			(*cbtrace)(void*,TDSTRData*)=0;
  char			mname[64];
  int			req,rtn,omd,mno,stat,i;

  if(UseRecvThrd ==0)	cbrecv	= CBRecvProc;
  if(UseSendCB   !=0)	cbsend	= CBSend;
  if(UseUserTrace!=0)	cbtrace	= CBTrace;
  if(SyncStatus  ==0)	omd	= 0x0002;
  else			omd	= 0x1002;
  if(FuncType	 ==0){
    Fd=_TDSCommOpen(omd,PARAMFILE,"EQUIP",cbrecv,0,cbsend,0,cbtrace,0);
  }else{
    Fd=_TDSUDrvOpen(omd,PARAMFILE,"EQUIP",UDRV_MASK);
  }
  if(Fd								< 0) goto Exit;

	// When _TDSUDrvXxxx() is used and when receiving process is specified
	// in the receiving thread, start the receiving process thread.
  	// _TDSUDrvXxxx() 使用の場合 及び 受信用スレッドでの受信処理指定の場合
	// は受信処理スレッドを起動する
  if(FuncType	 !=0 || UseRecvThrd!=0){
    _beginthread(RecvProcThread,0,0);
  }

  PO,"(E) Opened (%d)\n",Fd);	OType=1;
  if((MssgUseFile&0x80)!=0) Md=_TDSMDMssgInitialize(0x4000,Fd,0);

  if(SECS_MODE !=0){	// In case of HSMS
    if(FuncType==0){	// In case of use TDSCommXxxxx()
      if(_TDSCommSend(Fd,0x0100,0,0,0,0,0,0)			< 0) goto Exit;
      printf("(E) Connected\n");
      if(_TDSCommSend(Fd,0x0200,0,0,0,0,0,0)			< 0) goto Exit;
      printf("(E) Select request\n");
    }

    for(;;){	// Wait for Select state. See the description for Host().
    		// Select 状態になるのを待つ。 Host() での説明を参照すること。
    //if(SyncStatus==0 && UseRecvThrd==0){
    //  if(FuncType==0)	_TDSCommRecv(Fd,0x0000,&did,&sf,&xid,msg,msz,&hd);
    //  else		_TDSUDrvRecv(Fd,0x0000,&did,&sf,&xid,msg,msz,&hd);
    //}
      if(FuncType==0)	rtn=_TDSCommStatus(Fd,0x0000);
      else		rtn=_TDSUDrvStatus(Fd,0x0000);
      if(rtn							< 0) goto Exit;
      if(rtn							==3) break;
      Sleep(100);
    }
    printf("(E) Selected\n");
  }

  for(;;){
    req=99;	rtn=0;
    PE,"Req (0:Exit 2:Send 3:Continuous Send 4:Statistic Report) : ");
    scanf("%d",&req);
    if      (req==0){						break;

    }else if(req==2){
      char			*frm0="1:S1F1 2:S1F21 3:S6F11";
      if((MssgUseFile&0x80)!=0)  frm0="1:S1F1 2:S1F21 3:S6F11   9:Any";
      PE,"Message(%s) : "	,frm0);			scanf("%d",&mno);
      switch(mno){
	case 1: rtn=SendS1F1 ();				break;
	case 2: rtn=SendS1F21();				break;
	case 3: rtn=SendS6F11();				break;
	case 9: if((MssgUseFile&0x80)!=0){
		  PE,"Message Name : ");		scanf("%s",mname);
		  rtn=SendNamedMssg(0,0,mname);
		}						break;
      }

    }else if(req==3){
      for(Break=i=0;Break==0;i++){
	Sleep(SendWait);
	if(FuncType==0)	stat=_TDSCommStatus(Fd,0);
	else		stat=_TDSUDrvStatus(Fd,0);
	if(stat==3){
	  if((rtn=SendS6F11())	< 0){
	    PE,"(E) Send error (%d)\n"	,rtn);	Sleep(ERROR_SLEEP);
	    if(rtn		==-E_ACCES)	Break=1;
	  }
	  if((i%SEND_NO)		==0)	Sleep(SendSleep);
      } }
      rtn=0;

    }else if(req==4){
      TDSSInfoSP	sp;
      int		noe,nort,i,s,f,d;
      for(noe=i=0;i<2;i++){
	if(i==0) PO,"\n[Sent Primary Message Table]\n");
	else	 PO,"\n[Received Primary Message Table]\n");
	PO, "   SC  FC: Primary: Second.:Async T3TO"
	    ": S9F1   F3   F5   F7   F9  F11  F13:  Total.ms (   Ave. ms)\n");
	for(s=0;s<128;s++){	for(f=0;f<256;f++){
	  if(FuncType	==0)	rtn=_TDSCommSInfoGet(Fd,i,(s<<8)|f,&sp);
	  else			rtn=_TDSUDrvSInfoGet(Fd,i,(s<<8)|f,&sp);
	  if(rtn	==0){	noe++;
	    if((d=f)	!=0)	f++;
	    if((nort=sp.nort)==0) nort=1;
	    PO,	"  %3d %3d:%8d:%8d:%5d%5d"
		":%5d%5d%5d%5d%5d%5d%5d:%8lldms (%8.1fms)\n"
		  ,s,d  ,sp.nox  ,sp.nort ,sp.norf,sp.not3
			,sp.nof1 ,sp.nof3 ,sp.nof5,sp.nof7,sp.nof9
			,sp.nof11,sp.nof13,sp.trtok,(float)sp.trtok/(float)nort);
	  }else{
	    if(f	!=0)	f++;
      } }}}
      if(noe==0){
	PO,"\nSet STATISINFO=1 in the .ini file when using this function.\n\n");
      }else	PO,"\n");
      rtn=0;
    }

    if(rtn<(-999) || ((-900)<rtn && rtn<0)){
      PE,"(E) Send error (%d)\n"	,rtn);
  } }

  // If you want to terminate (call _TDSCommClose() or _TDSUDrvClose()) like
  // this AP, you don't need the following "Separate request" processing.
  // この AP の様に、このまま終了する (_TDSCommClose() もしくは _TDSUDrvClose()
  // を Call する) のであれば、以下の "Separate 要求" 処理は必要ない。
  if(SECS_MODE	!=0){			// In case of HSMS, Shutdown process
					// HSMS の場合、切断処理
    if(FuncType	==0)	rtn=_TDSCommStatus(Fd,0);
    else		rtn=_TDSUDrvStatus(Fd,0);
    if(rtn	==3){
    // Deselect request is not performed. (Of course you may go. However SEMI
    // claims that HSMS-SS does not perform Deselect request.)
    // Deselect request は行わない。 (もちろん行ってもよい。SEMI では HSMS-SS
    // において、Deselect request は行わない、としている。)
    //if(FuncType==0)	rtn=_TDSCommSend(Fd,0x0800,0,0,0,0,0,0);
    //else		rtn=_TDSUDrvSend(Fd,0x0800,0,0,0,0,0,0);
    //if(rtn							< 0) goto Exit;
    //PO,"(E) Deselected\n");
      if(FuncType==0)	rtn=_TDSCommSend(Fd,0x0900,0,0,0,0,0,0);
      else		rtn=_TDSUDrvSend(Fd,0x0900,0,0,0,0,0,0);
      if(rtn							< 0) goto Exit;
      PO,"(E) Separated\n");
  } }

Exit:
  Finish=1;
  if(Fd> 0){
    if(FuncType	==0)	_TDSCommClose(Fd,0);
    else		_TDSUDrvClose(Fd,0);
  }else			PE,"(E) Error (%d)\n",Fd);
  if(Md> 0)		_TDSMDMssgTerminate(Md,0);
}
