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


// =============================================================================
//
// FirstSimpleIo : Test and sample program
//
//  The simplest use case of constructing a message in AP and simply repeating
//  sending and receiving of SECS message.
//
//  The first sample is a simpler version of "Sample0/SimpleIo.c".
//  See the comment at beginning of "Sample0/SimpleIo.c" for the flow of
//  processing.
// 
//  (Note) In the case of HSMS communication, this AP assumes that the equipment
//  side is Active connection and the host side is Passive connection.
//
// 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.
//
//
// =============================================================================
//
// FirstSimpleIo : テスト 及び サンプル・プログラム
//
//  ＡＰ内でメッセージを構築し、単純に SECS メッセージの送受信を繰り返す、最も
//  単純な使用例。
//
//  最初に見るサンプルとして "Sample0/SimpleIo.c" の内容をもっと単純にしたもの。
//  処理の流れ等は "Sample0/SimpleIo.c" の冒頭のコメントを参照すること。
//
//  (注) 本ＡＰは HSMS 通信においては、設備側を Active 接続、ホスト側を Passive
//  接続と仮定している。
//
// 起動方法
//
//   FirstSimpleIo {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		UDRV_MASK	0x8383ffff	// Mask value for UDrvOpen()
						// 0          : Set =0x49
						// 0x8383ffff : All event

#define 	WAIT0		100	// Monitoring interval   (ms)
#define 	WAIT1		300	// 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"};
  unsigned char	*hh;
  char		sfcode[12],rbit,wbit;

  if(rtn<0){
    switch(-rtn){
      case E_NODATA:		/*PO,"No data\n");*/		break;
      case E_CONNECT:		PO,"Connected\n");		break;
      case E_SELECT:		PO,"Selected   (0x%04x)\n"
					,hd->did);		break;
      case E_SEPARATE:		PO,"Separated  (0x%04x)\n"
					,hd->did);		break;
      case E_NOTCONNECT:	PO,"Not connected\n");		break;
      default:			PO,"Error [%d]\n",rtn);		break;
    }

  }else{
    hh  =(unsigned char*)hd;
    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"
       "  Head=0x%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n"
		,ctp[tp],hd->did,sfcode,rbit,wbit,hd->sid,hd->xid,len
		,hh[0],hh[1],hh[2],hh[3],hh[4],hh[5],hh[6],hh[7],hh[8],hh[9]);
  }
}



// =============================================================================
// 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 FirstSubFunction.h.
  		// FirstSubFunction.h の SendS1F1() 内 (注) を参照すること。
  if((rtn=_TDSUDrvSend(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=_TDSUDrvSend (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=_TDSUDrvSend (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=_TDSUDrvOpen(0x1002,PARAMFILE,"HOST",UDRV_MASK))	< 0) goto Exit;
  PO,"(H) Opened (%d)\n",Fd);

  for(Break=0;Break==0;){
    rtn=_TDSUDrvRecv(Fd,0x0000,&did,0,&xid,msg,msz,&hd);
    DispData(1,&hd,msg,rtn,rtn);
    if	   (rtn	==-E_NODATA)	Sleep(WAIT0);
    else if(rtn	< 0)		Sleep(WAIT1);
    else{
      if(hd.scd==0x81 && hd.fcd==0x01){		// S1F1 received ...
	SendS1F2H(did,xid);			// S1F1 受信したら・・
	Sleep(WAIT1);
	SendS1F1();
  } } }

Exit:
  if(Fd>0) _TDSUDrvClose(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=_TDSUDrvOpen(0x1002,PARAMFILE,"EQUIP",UDRV_MASK))	< 0) goto Exit;
  PO,"(E) Opened (%d)\n",Fd);

// Wait for Select state. Not required for SECS-1, but fine to do.
// This AP issues S1F1 from Equip side when it becomes "Selected" state, so
// check Selected state at this position.  (It's usually OK to simply wait for
// a few seconds)
// Select 状態になるのを待つ。SECS-1 の場合は必要ないが、行っても問題ない。
// 本 AP は、Select 状態になったら Equip 側から S1F1 を発行するので、この位置で
// Select 状態を確認する (簡易的に数秒間の「待ち」でも通常はＯＫ) 。
  for(;;){
    if((rtn=_TDSUDrvStatus(Fd,0x0000))				< 0) goto Exit;
    if( rtn							==3) break;
    Sleep(100);
  }
  PO,"(E) Selected\n");

  SendS1F1();

  for(Break=0;Break==0;){
    rtn=_TDSUDrvRecv(Fd,0x0000,&did,0,&xid,msg,msz,&hd);
    DispData(1,&hd,msg,rtn,rtn);
    if	   (rtn	==-E_NODATA)	Sleep(WAIT0);
    else if(rtn	< 0)		Sleep(WAIT1);
    else{
      if(hd.scd==0x81 && hd.fcd==0x01){		// S1F1 recieved ...
	SendS1F2E(did,xid);			// S1F1 受信したら・・
	Sleep(WAIT1);
	SendS1F1();
  } } }

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