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


// =============================================================================
//
// SimpleIo : Test and sample program
//
//  The simplest use case of constructing a message in AP and simply repeating
//  sending and receiving of SECS message.
//
//  Execute the following flow.
// 
//  (Note) In the case of HSMS communication, this AP assumes that the equipment
//  side is Active connection and the host side is Passive connection.
//
//   Host side (Simple h)   |          | Equipment side (Simple e)
//   ---------------------------------------------------------------------------
//   Waiting for connection |          |
//                          |          |
//        Accept connection |<---------| Connect
//            Accept Select |<---------| Select request
//                          |          |
//                          |          |
//        +--> S1F1 Receive | <------- | S1F1 Send <--+
//        |    S1F2 Send    | -------> | S1F2 Receive | Repeat
//        |    Wait 2 sec.  |          |              | Exit with CTRL-C
//        |    S1F1 Send    | -------> | S1F1 Receive | Transmission interval
//        +--- S1F2 Receive | <------- | S1F2 Send    | is determined by WAIT1
//                          |          | Wait 2 sec. -+ macro constant
//                          |          |
//          Accept Deselect |<---------| Deselect request  <Omission>
//          Accept Separate |<---------| Separate request
//                          |          |
//                 Shutdown |          | Shutdown                                 
//                          |          |
//
//
// Starting method
//
//   SimpleIo {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, "SimpleIo h" and "SimpleIo e" both operate on the same
//   machine or different machines to communicate with each other.
//   Entering CTRL-C terminates the next action.
//
//   This sample omits some of the abnormal processing.
//
//
// =============================================================================
//
// SimpleIo : テスト 及び サンプル・プログラム
//
//  ＡＰ内でメッセージを構築し、単純に SECS メッセージの送受信を繰り返す、最も
//  単純な使用例。
//
//  以下の流れを実行する。
//
//  (注) 本ＡＰは HSMS 通信においては、設備側を Active 接続、ホスト側を Passive
//  接続と仮定している。
//
//   ホスト側（Simple h） |          | 設備側（Simple e）
//   --------------------------------------------------------------------------
//            接続 を待つ |          |
//                        |          |
//       接続を受け付ける |<---------| Connect
//    Select を受け付ける |<---------| Select 要求
//                        |          |
//                        |          |
//         +--> S1F1 受信 | <------- | S1F1 送信 <--+
//         |    S1F2 送信 | -------> | S1F2 受信    | 繰り返す
//         |    ２秒待つ  |          |              | CTRL-C で終了
//         |    S1F1 送信 | -------> | S1F1 受信    | 送信間隔は WAIT1 マクロ
//         +--- S1F2 受信 | <------- | S1F2 送信    | 定数で決める
//                        |          | ２秒待つ  ---+
//                        |          |
//   Deselectを受け付ける |<---------| Deselect 要求  <省略>
//   Separateを受け付ける |<---------| Separate 要求
//                        |          |
//                   切断 |          | 切断                                 
//                        |          |
//
//
//
// 起動方法
//
//   SimpleIo {h|e}
//   ~~~~~~~~~~~~~~
//   h    : Sample.ini の [HOST]  セクションを参照し動作を決定する
//   e    : Sample.ini の [EQUIP]      :              :
//
//
//   通常、Simple h 及び Simple e の両方を、同一マシン、もしくは異なる
//   マシン上で動作させて、相互通信を行う。
//   CTRL-C を入力すると次にアクションがあった時点で終了する。
//
//   本サンプルは、異常時処理を省略している。
//
// =============================================================================

#include	"TDS.h"
#include	<time.h>
#include	<string.h>
#include	<signal.h>


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

#define		PARAMFILE	"Sample.ini"

#define		WAIT0		100	// Monitoring interval   (ms)
#define		WAIT1		2000	// Transmission interval (ms)


static int	Fd=0;			// Communication identifier
static int	Break=0;		// Signal reception status

static void	Host();			// Host side processing function
static void	Equip();		// Equipment side processing function



// =============================================================================
// UNIX Sleep ------------------------------------------------------------------

#if !defined (WINDOWS)
static void
Sleep		(
int		msec)
{
  struct timespec	itimer,otimer;

  itimer.tv_sec =(msec/1000);
  itimer.tv_nsec=(msec%1000)*1000*1000;
  nanosleep(&itimer,&otimer);
}
#endif



// =============================================================================
// Signal handler ==============================================================

static void
CBSignal(
int		vec)
{
  signal(SIGINT,CBSignal);
  Break=1;
}



// =============================================================================
// Message display =============================================================

static void
DispData(
int		tp,		// i  : Message type
				//	=1 : Received message
				//	 2 : Send message
TDSECSHead	*hd,		// i  : SECS Message header
void		*msg,		// i  : SECS Message body
int		len,		// i  : Byte length of 'msg'
int		rtn)		// i  : I/O return value
{
  static char	*ctp[]={"SRES","RECV","SEND"};
  char		sfcode[12],rbit,wbit;

  if(rtn<0){
    switch(-rtn){
      case ENODEV:		PO," : No such device ID\n");	break;
      case E2BIG:		PO," : Data size to large\n");	break;
				// Not display "NODATA"
      case E_NODATA:		/*PO,"No data\n");*/		break;
      case E_ILLBLOCK:		PO,"Illegal block#\n");		break;
      case E_T1TIMEDOUT:	PO,"T1 Timeout occur\n");	break;
      case E_T2TIMEDOUT:	PO,"T2 Timeout occur\n");	break;
      case E_T3TIMEDOUT:	PO,"T3 Timeout occur\n");	break;
      case E_T4TIMEDOUT:	PO,"T4 Timeout occur\n");	break;
      case E_T5TIMEDOUT:	PO,"T5 Timeout occur\n");	break;
      case E_T6TIMEDOUT:	PO,"T6 Timeout occur\n");	break;
      case E_T7TIMEDOUT:	PO,"T7 Timeout occur\n");	break;
      case E_T8TIMEDOUT:	PO,"T8 Timeout occur\n");	break;
      case E_RETRYOVER:		PO,"Retry over\n");		break;
      case E_CONNECT:		PO,"Connected\n");		break;
      case E_SELECT:		PO,"Selected   (0x%04x)\n"
					,hd->did);		break;
      case E_REJECT:		PO,"Rejected XId=0x%04x.0x%04x\n"
					,hd->sid,hd->xid);	break;
      case E_DESELECT:		PO,"Deselected (0x%04x)\n"
					,hd->did);		break;
      case E_NOTCONNECT:	PO,"Not connected\n");		break;
      default:			PO,"Error [%d]\n",rtn);		break;
    }

  }else{
    rbit=' ';	if((hd->did&0x8000)!=0)	rbit='R';
    wbit=' ';	if((hd->scd&  0x80)!=0)	wbit='W';
    sprintf(sfcode,"S%dF%d",hd->scd&0x7f,hd->fcd);
    PO,"[%s]  Dev=0x%04x  %-8s  %c%c  XId=0x%04x.0x%04x  Len=%4d\n"
		,ctp[tp],hd->did&0x7fff,sfcode,rbit,wbit,hd->sid,hd->xid,len);
  }
}



// =============================================================================
// Message construction ========================================================

// -----------------------------------------------------------------------------
// S1F1 message construction and sending ---------------------------------------

static int
SendS1F1()
{
  TDSECSHead	hd;
  char		msg[16];
  int		rtn,sf=0x8101,len=0;

  		// Refer to the (note) in SendS1F1() of SubFunction.h.
  		// SubFunction.h の SendS1F1() 内 (注) を参照すること。
  if((rtn=_TDSCommSend(Fd,0x0000,0,sf,0,msg,len,&hd))	< 0)	Break=1;
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


// -----------------------------------------------------------------------------
// S1F2 message (Host) construction and sending --------------------------------

static int
SendS1F2H(
int		did,
unsigned int	xid)
{
  TDSECSHead	hd;
  char		msg[256];
  int		rtn,md,sf=0x0102,len;
  
  md =_TDSMssgInit   (   0,msg,sizeof(msg),Fd);			// S1F2
      _TDSMssgBuild  (md,0,msg,000,  0,  0);			// L0
  len=_TDSMssgEnd    (md,0,msg);

  if(len < 0)	rtn=len;
  else		rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  if(rtn < 0)							Break=1;
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


// -----------------------------------------------------------------------------
// S1F2 message (Equipment) construction and sending ---------------------------

static int
SendS1F2E(
int		did,
unsigned int	xid)
{
  TDSECSHead	hd;
  char		msg[256];
  int		rtn,md,sf=0x0102,len;
  
  md= _TDSMssgInit   (   0,msg,sizeof(msg),Fd);			// S1F2
      _TDSMssgBuild  (md,0,msg,000,  2,  0);			// L2
      _TDSMssgBuild  (md,0,msg,020,  6,"EQUIP1");		//  MDLN
      _TDSMssgBuild  (md,0,msg,020,  6,"01.000");		//  SOFTREV
  len=_TDSMssgEnd    (md,0,msg);

  if(len < 0)	rtn=len;
  else		rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  if(rtn < 0)							Break=1;
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}




// =============================================================================
// 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);
  if(argv[1][0]=='h')	Host ();
  else			Equip();

  exit(0);
}



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

static void
Host()
{
  TDSECSHead		hd;
  char			msg[1024];
  unsigned int		xid;
  int			rtn,msz,did;

  msz=sizeof(msg);
  if((Fd=_TDSCommOpen(0x02,PARAMFILE,"HOST",0,0,0,0,0,0))	< 0) goto Exit;
  PO,"(H) Opened (%d)\n",Fd);

  for(Break=0;Break==0;){
    rtn=_TDSCommRecv(Fd,0,&did,0,&xid,msg,msz,&hd);
    DispData(1,&hd,msg,rtn,rtn);
    if(rtn	< 0){
      Sleep(WAIT0);	// Wait if there is no data received including NODATA
			// NODATA を含めデータ受信でなかったらならチョット待つ
    }else{
      if(hd.scd==0x81 && hd.fcd==0x01){		// S1F1 received ...
	SendS1F2H(did,xid);			// S1F1 受信したら・・
	Sleep(WAIT1);
	SendS1F1();
  } } }

Exit:
  if(Fd>0) _TDSCommClose(Fd,0);
  else	   PE,"(H) Error (%d)\n",Fd);
}



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

static void
Equip()
{
  TDSECSHead		hd;
  char			msg[1024];
  unsigned int		xid;
  int			rtn,msz,did;

  msz=sizeof(msg);
  if((Fd=_TDSCommOpen(0x02,PARAMFILE,"EQUIP",0,0,0,0,0,0))	< 0) goto Exit;
  PO,"(E) Opened (%d)\n",Fd);
// For HSMS-SS, do the following. Not required for SECS-1.
// HSMS-SS の場合は、以下を実行。SECS-1 の場合は必要なし。
  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");

  Sleep(WAIT1);
  SendS1F1();
  for(Break=0;Break==0;){
    rtn=_TDSCommRecv(Fd,0,&did,0,&xid,msg,msz,&hd);
    DispData(1,&hd,msg,rtn,rtn);
    if(rtn	< 0){
      Sleep(WAIT0);	// Wait if there is no data received including NODATA
			// NODATA を含めデータ受信でなかったらならチョット待つ
    }else{
      if(hd.scd==0x81 && hd.fcd==0x01){		// S1F1 recieved ...
	SendS1F2E(did,xid);			// S1F1 受信したら・・
	Sleep(WAIT1);
	SendS1F1();
  } } }

  if(_TDSCommStatus(Fd,0)	==3){
  // Deselect request is not performed. (Of course you may go. However SEMI
  // claims that HSMS-SS does not perform Deselect request.)
  // Deselect 要求は省略する。 (もちろん行ってもよい。但し SEMI では HSMS-SS
  // では Deselect 要求は行はない、としている。)
  //if(_TDSCommSend(Fd,0x0800,0,0,0,0,0,0)			< 0) goto Exit;
  //PO,"(E) Deselected\n");
    if(_TDSCommSend(Fd,0x0900,0,0,0,0,0,0)			< 0) goto Exit;
    PO,"(E) Separated\n");
  }

Exit:
  if(Fd>0) _TDSCommClose(Fd,0);
  else	   PE,"(E) Error (%d)\n",Fd);
}
