
//
// Trust Design LLC : SECS/HSMS Communication library
//
// (c) Copyright Trust Design LLC.  2010-2019.  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.
//
//
// Starting method
//
//   CallbackIo {h|e}
//   ~~~~~~~~~~~~~~~~
//   h    : Refer to [HOST]  section of Sample.ini to determine operation
//   e    : Refer to [EQUIP] section of Sample.ini to determine operation
//
//
//   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.
//   In this sample, operation on eqipment side is assumed to be  Active
//   operation.
//
//   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, changing value of "USE_USER_TRACE" can change use of Callback
//   for User Trace output.
//
//
// =============================================================================
//
// CallbackIo : テスト 及び サンプル・プログラム
//
//  Callback 関数を使用して受信メッセージに対する応答処理を行う。また、ユーザ
//  定義トレース出力関数を使用し、トレース出力を自力で行う。
//
//
// 起動方法
//
//   CallbackIo {h|e}
//   ~~~~~~~~~~~~~~~~
//   h    : Sample.ini の [HOST]  セクションを参照し動作を決定する
//   e    : Sample.ini の [EQUIP]      :              :
//
//
//   通常、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 側への接続処理の実行の有無を判断する
//   ために定義する。
//   なお、本サンプルは、装置側での動作を Active 動作と仮定している。
//
//   "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_USER_TRACE" の値を変更すると、User Trace 出力用 Callback の
//   使用の有無を変更できる。
//
// =============================================================================

#include	"TDS.h"


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

#define		SECS_MODE	1		// SECS/HSMS mode
						// 0    : SECS-1
						// 1    : HSMS
#define		FUNC_TYPE	0		// Type of function used
						// 通信に使用する関数の種別
						// 0    : _TDSCommXxxxx
						// 1    : _TDSUDrvXxxxx
#define		UDRV_MASK  	0x8383ffff	// Mask value for UDrvOpen()
						// 0          : Set =0x49
						// 0x8383ffff : All event

#define		MSSG_USE_FILE	0x80		// Message definition file
						// 0x00 : Not use
						// 0x80 : Use to display item
						//	  names
						//	  使用して項目名表示する
#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		USE_USER_TRACE	0		// Use User trace call back
						// function or not
						// UserTrace用CB を使用するか否か
						// 0    : Not use
						// 1    : use

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

#define		PARAMFILE	"Sample.ini"

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


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



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

#include	"SubFunctions.h"



// =============================================================================
// Receive Callback processing thread (for _TDSUDrvRecv()) ---------------------
// 受信 Callback 処理用スレッド (_TDSUDrvRecv() 用) ----------------------------

#if defined	WINDOWS
static void
#else
static void*
#endif
RecvProcThread(
void			*par)
{
  TDSCBData		cbd;
  char			msg[1024];
  int			msz;

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

  for(;Finish==0;){
    cbd.rtn=_TDSUDrvRecv(Fd,0,&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;
      CBRecv(par,&cbd);
  } }
}



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

int
main(
int			argc,
char			*argv[])
{
  if(argc<2 || (argv[1][0]!='h' && argv[1][0]!='e')){
    PE,"Usage: %s {host|equip}\n"	,argv[0]);		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			(*cbtrace)(void*,TDSTRData*)=0;
  char			mname[64];
  int			req,rtn,mno,stat,i;

  if(USE_USER_TRACE!=0)	cbtrace=CBTrace;
  if(FUNC_TYPE==0){
    Fd=_TDSCommOpen(0x02,PARAMFILE,"HOST",CBRecv,0,CBSend,0,cbtrace,0);
  }else{
    Fd=_TDSUDrvOpen(0x02,PARAMFILE,"HOST",UDRV_MASK);
  }
  if(Fd								< 0) goto Exit;

	// Execute Callback processing by itself when using _TDSUDrvXxxx()
  	// _TDSUDrvXxxx() 使用の場合は Callback 処理を自力で実行する
  if(FUNC_TYPE!=0) _beginthread(RecvProcThread,0,0);

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

  for(;;){
    Sleep(500);
    req=99;	rtn=0;
    PE,"Req (0:Exit 2:Send 3:Continuous Send) : ");	scanf("%d",&req);
    if      (req==0){						break;
    }else if(req==2){
      PE,"Message(1:S1F1 2:S2F49   9:Any) : ");		scanf("%d",&mno);
      switch(mno){
	case 1: rtn=SendS1F1 ();				break;
	case 2: rtn=SendS2F49();				break;
	case 9: if((MSSG_USE_FILE&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(SEND_WAIT);
	if(FUNC_TYPE==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((i%SEND_NO)		==0)	Sleep(SEND_SLEEP);
      } }
      rtn=0;
    }
    if(rtn<(-999) || ((-900)<rtn && rtn<0) ){
      PE,"(H) Send error (%d)\n"	,rtn);
  } }

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



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

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

  if(USE_USER_TRACE!=0)	cbtrace=CBTrace;
  if(FUNC_TYPE==0){
    Fd=_TDSCommOpen(0x02,PARAMFILE,"EQUIP",CBRecv,0,CBSend,0,cbtrace,0);
  }else{
    Fd=_TDSUDrvOpen(0x02,PARAMFILE,"EQUIP",UDRV_MASK);
  }
  if(Fd								< 0) goto Exit;

	// Execute Callback processing by itself when using _TDSUDrvXxxx()
  	// _TDSUDrvXxxx() 使用の場合は Callback 処理を自力で実行する
  if(FUNC_TYPE!=0) _beginthread(RecvProcThread,0,0);

  PO,"(E) Opened (%d)\n",Fd);	OType=1;
  if((MSSG_USE_FILE&0x80)!=0) Md=_TDSMDMssgInitialize(0x4000,Fd,0);
  if(SECS_MODE!=0 && FUNC_TYPE==0){	// In case of HSMS and use TDSCommXxxxx()
					// HSMS で TDSComm 使用の場合
    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) Selected\n");
  }

  for(;;){
    Sleep(500);
    req=99;	rtn=0;
    PE,"Req (0:Exit 2:Send 3:Continuous Send) : ");	scanf("%d",&req);
    if      (req==0){						break;
    }else if(req==2){
      PE,"Message(1:S1F1 2:S6F11   9:Any) : ");		scanf("%d",&mno);
      switch(mno){
	case 1: rtn=SendS1F1 ();				break;
	case 2: rtn=SendS6F11();				break;
	case 9: if((MSSG_USE_FILE&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(SEND_WAIT);
	if(FUNC_TYPE==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((i%SEND_NO)		==0)	Sleep(SEND_SLEEP);
      } }
      rtn=0;
    }
    if(rtn<(-999) || ((-900)<rtn && rtn<0)){
      PE,"(E) Send error (%d)\n"	,rtn);
  } }

  if(SECS_MODE!=0){			// In case of HSMS, Shutdown process
					// HSMS の場合、切断処理
  // 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(FUNC_TYPE==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(FUNC_TYPE==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;		Sleep(1000);
  if(Md>0)		_TDSMDMssgTerminate(Md,0);
  if(Fd>0){
    if(FUNC_TYPE==0)	_TDSCommClose(Fd,0);
    else		_TDSUDrvClose(Fd,0);
  } else		PE,"(E) Error (%d)\n",Fd);
}
