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


/* ========================================================================== */
/*
 * 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		// 監視間隔 (ms)
#define		WAIT1		2000		// 送信間隔 (ms)


static int	Fd=0;				// 通信用識別子
static int	Break=0;			// シグナル受信状態

static void	Host();				// ホスト側処理関数
static void	Equip();			// 設備側処理関数



/* ========================================================================== */
/* 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;
}



/* ========================================================================== */
/* メッセージ表示 =========================================================== */

static void
DispData(
int		tp,		// i  : メッセージ種別
				//	=1 : 受信メッセージ
				//	 2 : 送信メッセージ
TDSECSHead	*hd,		// i  : SECS メッセージ・ヘッダ
void		*msg,		// i  : SECS メッセージ本体
int		len,		// i  : msg の長さ
int		rtn)		// i  : I/O の戻り値
{
  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;
				// "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);
  }
}



/* ========================================================================== */
/* メッセージ構築 =========================================================== */

/* ---------------------------------------------------------------------------*/
/* S1F1 メッセージ構築及び送信 ---------------------------------------------- */

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

  		// SubFunction.h の SendS1F1() 内 (注) を参照すること。
  rtn=_TDSCommSend(Fd,0x0000,0,sf,0,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


/* ---------------------------------------------------------------------------*/
/* S1F2 メッセージ (Host) 構築及び送信 -------------------------------------- */

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);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


/* ---------------------------------------------------------------------------*/
/* S1F2 メッセージ (Equip) 構築及び送信 ------------------------------------- */

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);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}




/* ========================================================================== */
/* Main 処理 ---------------------------------------------------------------- */

int
main(
int			argc,
char			*argv[])
{
  if(argc<2 || (argv[1][0]!='h' && argv[1][0]!='e')){
    PE,"%s {host|equip}\n"	,argv[0]);			exit(1);
  }

  signal(SIGINT,CBSignal);
  if(argv[1][0]=='h')	Host ();
  else			Equip();

  exit(0);
}



/* ========================================================================== */
/* ホスト側処理 ------------------------------------------------------------- */

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);	// NODATA を含めデータ受信でなかったらならチョット待つ
    }else{
      if(hd.scd==0x81 && hd.fcd==0x01){		// S1F1 受信したら・・
	SendS1F2H(did,xid);
	Sleep(WAIT1);
	SendS1F1();
  } } }

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



/* ========================================================================== */
/* 装置側処理 --------------------------------------------------------------- */

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);
// 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);	// NODATA を含めデータ受信でなかったらならチョット待つ
    }else{
      if(hd.scd==0x81 && hd.fcd==0x01){		// S1F1 受信したら・・
	SendS1F2E(did,xid);
	Sleep(WAIT1);
	SendS1F1();
  } } }

// 省略する。（もちろん行ってもよい。但し SEMI では 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) Separate\n");

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