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


// =============================================================================
//
// BasicIo : Test and sample program
//
//   Construct a message in AP and simply send and receive SECS messages.
//
//   (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
//
//   BasicIo {h|e} [option]
//   ~~~~~~~~~~~~~~~~~~~~~~
//   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		   4 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
//    +-------------------- Synchronize the connection status with communication
//			    control processing thread
//			    =0: No			1: Yes
//
//   Normally, "BasicIo h" and "BasicIo e" both operate on same machine or
//   different machines to communicate with each other.
//
//   After startup, it refers to the menu display, inputs request codes (0,1,2),
//   etc., and executes functions such as receiving a message from the other
//   side and sending a message to the other side.
//
//   Execute functions such as receiving a message or sending a message to the
//   other side. Host side and equipment side do not use Callback function but
//   perform Receive processing according to request code.
//   When using a message definition file, message is sent out by defining it in
//   message definition file and specifying an arbitrary message name.
// 
//   When host starts up, it waits for a connection from equipment.
//   When equipment side is started, connection request and Select request are
//   automatically issued to host side. On host side, repeat "1:Recv" processing,
//   and confirm that display is "STATUS = 951: No data".  After that, exchange
//   any message. On receiving side, do not forget to reap received data until
//   "STATUS = 951".
//
//   The transaction ID used when sending secondary message uses that of primary
//   message received immediately before.
//
//   This sample omits some of abnormal processing.
//
//   The following "SECS_MODE" is defined to use _TDSCommXxxx() as 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.
//   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.
//
//
// =============================================================================
//
// BasicIo : テスト 及び サンプル・プログラム
//
//   ＡＰ内でメッセージを構築し、単純に SECS メッセージの送受信を行う。
// 
//   (注) 本サンプルでは HSMS 接続する場合、HOST 側が Passive 接続、EQUIP 側が
//	Active 接続を実行すると仮定している。
//
//
// 起動方法
//
//   BasicIo {h|e} [option]
//   ~~~~~~~~~~~~~~~~~~~~~~
//   h      : Sample.ini の [HOST]  セクションを参照し動作を決定する
//   e      : Sample.ini の [EQUIP]      :              :
//
//   option : 動作の詳細を設定するオプション (省略可能)
//	      10進数 もしくは 接頭文字として 0x もしくは x を付与した 16進数
//	      で指定する。
//	      以下に指定値のビット毎の詳細を以下に示す。
//    F		   4 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: 使用する
//    +-------------------- 接続状態を通信制御部に同期する
//			    =0: No			1: Yes
//
//   通常、BasicIo h 及び BasicIo e の両方を、同一マシン、もしくは異なる
//   マシン上で動作させて、相互通信を行う。
//
//   起動後、メニュ表示を参照し、要求コード (0,1,2) 等を入力し、相手側からの
//   メッセージを受信したり、相手側へメッセージを送出する等の機能を実行する。
//   ホスト側、装置側共に Callback 関数を使用せず、Receive 処理を要求コード
//   に応じて行う。
//   メッセージ定義ファイルを使用する場合は、メッセージ定義ファイルに定義した
//   任意のメッセージ名称を指定することにより、そのメッセージをを送出する。
// 
//   ホスト側は起動すると、装置側からの接続を待つ。装置側を起動すると、自動的
//   にホスト側に接続要求、Select 要求を発行する。ホスト側では、1:Recv 処理を
//   繰り返し行い、表示が「STATUS=951 : No data」となるのを確認する。その後、
//   任意のメッセージのやりとりを行う。受信側では、受信データの刈り取りを「
//   STATUS=951」になるまで行うことを忘れぬこと。
//
//   ２次メッセージを送信する際のトランザクションＩＤは、直前に受信した１次
//   メッセージのものを使用する。
//
//   本サンプルは、異常時処理を一部省略している。
//
//   以下で定義する "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/HSMSS そして HSMS-SS時の Passive/Active）を知る必要は
//   ない。ただし _TDSUDrvXxxx() を使用する場合は、Callback 処理は自力で行う
//   必要がある。
//
//   以下で定義する "MSSG_USE_FILE"、"MSSG_DISP_TYPE" の値を変更すると、本ＡＰ
//   にて表示する SECS メッセージの出力形式を変更できる。
//   "MSSG_USE_NEXTL" の値を変更すると、メッセージ内容を解析する際に、項目値を
//   データとして取得するか、表示リスト形式で取得するかを変更できる。
//   "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    : SECS1
						// 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	1		// 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		SYNC_STATUS	1		// Synchronize connection status
						// 接続状態の同期
						// 0    : No
						// 1    : Yes

#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	=0;
static int	SyncStatus	=SYNC_STATUS;		// argv[2]&0x8000
static int	NoListOut	=0;


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



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

#include	"SubFunctions.h"



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

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

  // ---------------------------------------------------------------------------
  { TDSInform		inf;	// Display TDS information
    int			bits;	// This code has nothing to do with running TDS.
    bits=_TDSGetInform(&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(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&0x8000) SyncStatus  =1;	else SyncStatus	 =0;
  }
  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,"SyncStatus  =%d\n"		,SyncStatus);
  // ---------------------------------------------------------------------------

  if(argc<2 || (argv[1][0]!='h' && argv[1][0]!='e')){
    PE,"\nUsage: %s {host|equip} [option]\n"	,argv[0]);
    PE,"\n  option\n");
    PE,  "   F            4 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,  "   +-------------------- Synchronize connection status\n");
    PE,  "                         =0: No                        1: Yes\n");
    PE,  "\n");
    exit(1);
  }

//InitializeCriticalSection(&Cs0);
  InitializeCriticalSection(&Cs1);

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

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



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

static void
Host()
{
  TDSECSHead		hd;
  char			msg[1024],mname[64];
  unsigned int		xid,xids=0;
  int			did,dids=0;
  int			req,rtn,omd,sf,cmd,msz,mno;

  msz=sizeof(msg);
  if(SyncStatus	==0)	omd=0x0002;
  else			omd=0x1002;
  if(FuncType	==0)	Fd=_TDSCommOpen(omd,PARAMFILE,"HOST",0,0,0,0,0,0);
  else			Fd=_TDSUDrvOpen(omd,PARAMFILE,"HOST",UDRV_MASK);
  if(Fd								< 0) goto Exit;
  PO,"(H) Opened (%d)\n",Fd);
  if((MssgUseFile&0x80)!=0) Md=_TDSMDMssgInitialize(0x4000,Fd,0);

  for(;;){
    PE,"Req (0:Exit 1:Recv 2:Send) : ");	scanf("%d",&req);
    if      (req==0){						break;

    }else if(req==1){
      if(HeaderBO !=0)	cmd=0x4000;
      else		cmd=0x0000;
      if(FuncType==0)	rtn=_TDSCommRecv(Fd,cmd,&did,&sf,&xid,msg,msz,&hd);
      else		rtn=_TDSUDrvRecv(Fd,cmd,&did,&sf,&xid,msg,msz,&hd);
      if(rtn	  >=0){	dids=did;	xids=xid;}
      DispData(1,&hd,did,sf,xid,msg,rtn,rtn);

    }else if(req==2){
      char	*frm0="1:S1F1 2:S1F21 3:S2F49   6:S1F2 7:S1F22 8:S6F12";
      if((MssgUseFile&0x80)!=0)
		 frm0="1:S1F1 2:S1F21 3:S2F49   6:S1F2 7:S1F22 8:S6F12   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 6: rtn=SendS1F2H(dids,xids);		break;
	case 7: rtn=SendS1F22(dids,xids);		break;
	case 8: rtn=SendS6F12(dids,xids);		break;
	case 9: if((MssgUseFile&0x80)!=0){
		  PE,"Message Name : ");	scanf("%s",mname);
		  rtn=SendNamedMssg(dids,xids,mname);
		}					break;
    } }
    if(rtn<(-999) || ((-900)<rtn && rtn<0)){
      PE,"(H) I/O Error (%d)\n",	rtn);
  } }

Exit:
  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()
{
  TDSECSHead		hd;
  char			msg[1024],mname[64];
  unsigned int		xid,xids=0;
  int			did,dids=0;
  int			req,rtn,omd,sf,cmd,msz,mno;

  msz=sizeof(msg);
  if(SyncStatus	==0)	omd=0x0002;
  else			omd=0x1002;
  if(FuncType	==0)	Fd=_TDSCommOpen(omd,PARAMFILE,"EQUIP",0,0,0,0,0,0);
  else			Fd=_TDSUDrvOpen(omd,PARAMFILE,"EQUIP",UDRV_MASK);
  if(Fd								< 0) goto Exit;
  PO,"(E) Opened (%d)\n",Fd);
  if((MssgUseFile&0x80)!=0) Md=_TDSMDMssgInitialize(0x4000,Fd,0);
  if(SECS_MODE!=0 && FuncType==0){	// In case of HSMS and use TDSCommXxxxx()
					// HSMS で TDSComm 使用の場合
    if(_TDSCommSend(Fd,0x0100,0,0,0,0,0,0)			< 0) goto Exit;
    PO,"(E) Connected\n");
    if(_TDSCommSend(Fd,0x0200,0,0,0,0,0,0)			< 0) goto Exit;
    PO,"(E) Selected\n");
  }

  for(;;){		rtn=0;
    PE,"Req (0:Exit 1:Recv 2:Send) : ");	scanf("%d",&req);
    if      (req==0){						 break;

    }else if(req==1){
      if(HeaderBO !=0)	cmd=0x4000;
      else		cmd=0x0000;
      if(FuncType==0)	rtn=_TDSCommRecv(Fd,cmd,&did,&sf,&xid,msg,msz,&hd);
      else		rtn=_TDSUDrvRecv(Fd,cmd,&did,&sf,&xid,msg,msz,&hd);
      if(rtn	>=0){	dids=did; xids=xid;}
      DispData(1,&hd,did,sf,xid,msg,rtn,rtn);

    }else if(req==2){
      char	*frm0="1:S1F1 2:S1F21 3:S6F11   6:S1F2 7:S1F22 8:S2F50";
      if((MssgUseFile&0x80)!=0)
		 frm0="1:S1F1 2:S1F21 3:S6F11   6:S1F2 7:S1F22 8:S2F50   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 6: rtn=SendS1F2E(dids,xids);		break;
	case 7: rtn=SendS1F22(dids,xids);		break;
	case 8: rtn=SendS2F50(dids,xids);		break;
	case 9: if((MssgUseFile&0x80)!=0){
		  PE,"Message Name : ");	scanf("%s",mname);
		  rtn=SendNamedMssg(dids,xids,mname);
		}					break;
    } }
    if(rtn<(-999) || ((-900)<rtn && rtn<0)){
      PE,"(E) I/O Error (%d)\n",	rtn);
  } }

  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) Separate\n");
  } }

Exit:
  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);
}
