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

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


#if !defined	PO
#define		PO		fprintf(stdout
#endif
#if !defined	PE
#define		PE		fprintf(stderr
#endif

#if defined	WINDOWS
#include	<process.h>
#else
#include	<pthread.h>
#define		CRITICAL_SECTION	pthread_mutex_t
#endif

				// Refer to the comment at the beginning of
				// BasicIo.c and CallbackIo.c for following
				// macro constants.
				// 以下のマクロ定数に関しては、BasicIo.c、
				// CallbackIo.c の先頭部分のコメント参照する事。

#if !defined	MSSG_USE_FILE
#define		MSSG_USE_FILE	0x80	// Message definition file
					// 0x00 : Not use
					// 0x80 : Use to display item names
#endif

#if !defined	MSSG_DISP_TYPE
#define		MSSG_DISP_TYPE	0x00	// SECS Message display format
					// 0x00 : TDS Format
					// 0x20 : SML Format
#endif

#if !defined	MSSG_USE_NEXTL
#define		MSSG_USE_NEXTL	0	// Use User trace call back function or
					// not
					// UserTrace用CB を使用するか否か
					// 0    : Not use
					// 1    : use
#endif


static int	Fd=0;		// Communication identification
static int	Md=0;		// Message analysis identifier
static int	OType=0;	// Operation type	(0:Host	  1:Equipment)
static int	Break=0;	// Signal reception status
				// シグナル受信状態

static CRITICAL_SECTION	Cs0;	// For message construction processing lock
				// メッセージ構築処理ロック用
	// When continuous sending and receiving processing is performed
	// using a message definition file, there is a possibility that the
	// construction of primary message and construction of secondary message
	// for Receive may conflict in message construction processing
	// identified by Md. 
	// Lock control to avoid it.
	// Of course, there is no need for lock control when preparing separate
	// Mds for sending and receiving.
	// Also, from V12.030 on, when (mode&0x4000)!=0 is specified in message
	// definition initialization _TDSMDMssgInitialize(), lock control is
	// performed inside library, so there is no need for AP to perform lock
	// control.
	// In this sample, this lock control is set to be performed on the
	// library side, and then conventional lock control portion is commented
	// out and left.
	//
	// 連続送受信処理をメッセージ定義ファイルを使用して行う場合、上記 Md で
	// 識別するメッセージ構築処理において、１次メッセージの構築と、Receive
	// に対する２次メッセージの構築とがバッティングする可能性があるので、そ
	// れを避けるためにロック制御する。
	// もちろん、送信用と受信用とで別の Md を用意する場合には、ロック制御の
	// 必要はない。
	// また、V12.030 以降、メッセージ定義の初期化 _TDSMDMssgInitialize() に
	// おいて、(mode&0x4000)!=0 を指定するとロック制御をライブラリ内部で行う
	// ので、ＡＰがロック制御を行う必要はない。
	// 本サンプルでは、このロック制御をライブラリ側で行うよう設定した上で、
	// 従来のロック制御の部分をコメント化して残す。

static CRITICAL_SECTION	Cs1;	// For message display processing lock
				// メッセージ表示処理ロック用	
	// When displaying send/receive message structure in following
	// DispData(), since CallbackIo.exe executes sending and receiving in
	// different threads, the message may be displayed at same time, and
	// display may be disturbed.
	// Lock control is performed to avoid it.
	//
	// 以下の DispData() で送受信メッセージ構造を表示する場合、
	// CallbackIo.exe では、送信と受信を異なるスレッドで実行するため、同時に
	// メッセージ表示を行う場合が発生し、表示が乱れる可能性がある。
	// それを避けるため、ロック制御を行う。


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

#if !defined	WINDOWS
static void		
InitializeCriticalSection(
CRITICAL_SECTION	*cs)
{
  pthread_mutex_init(cs,0);
}

static void
DeleteCriticalSection(
CRITICAL_SECTION	*cs)
{
  pthread_mutex_destroy(cs);
}

static void
EnterCriticalSection(
CRITICAL_SECTION	*cs)
{
  pthread_mutex_lock(cs);
}

static void
LeaveCriticalSection(
CRITICAL_SECTION	*cs)
{
  pthread_mutex_unlock(cs);
}


static void*
_beginthread(
void*			(*func)(void*),
int			stacksz,
void			*par)
{
  pthread_t		th;
  pthread_attr_t	ta;

  if(pthread_attr_init(&ta)				==0){
    pthread_attr_setdetachstate(&ta,PTHREAD_CREATE_DETACHED);
    if(stacksz>0) pthread_attr_setstacksize(&ta,stacksz);
    pthread_attr_destroy(&ta);
  }
  return((void*)pthread_create(&th,&ta,func,par));
}


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



// -----------------------------------------------------------------------------

static void
DispSECSMssg(			//
int		tp,		// i  : Message type
				//	=0 : Transmission result
				//	 1 : Received message
				//	 2 : Send message
TDSECSHead	*hd,		// i  : SECS Message header
void		*msg,		// i  : SECS Message storage area
				//	     (Header not included)
int		len)		// i  : SECS Message byte length
{
  static char	*ctp[]={"SRES","RECV","SEND"};
  TDSAllType	*itm;
  char		item[64],str[512],sfcode[12],rbit,wbit;
  int		rtn,md=0,dp=MSSG_USE_FILE|MSSG_DISP_TYPE,fm,form,sz,noi,la,i;
  	// [Note] In CallbackIo, DispData() is called simultaneously from both
	// sending thread and the thread on which receiving Callback operates.
	//
	// In DispData(), when lock control (in this sample, it is performed in
	// EnterCriticalSection() and LeaveCriticalSection()) is not performed,
	// if message name, message item name as message definition are
	// specify to displayed by setting (mode&0x8000)!=0 in _TDSMssgFind()
	// and (mode&0x0080)!=0 in _TDSMssgNextL(), calling DispData()
	// simultaneously from each thread of sending and receiving may
	// cause a conflict in _TDSMssgFind() and _TDSMssgNextL(), and
	// unexpected results may occur.
	// In this case, change the setting of mode and do not refer to 
	// message definition file.
	// Refer to notes in Section 3.3(2)(c) of TDSE.pdf for the lower
	// _TDSMssgNextL() mode setting value 'dp'.
	//
	// [注意] CallbackIo においては、送信スレッドと、受信 Callback が動作す
	// るスレッドの両方から同時に DispData() が Call される。
	// 仮に DispData() において、ロック制御（本 Sample では、EnterCritical
	// Section()、LeaveCriticalSection() にて行っている）を実施しない場合、
	// _TDSMssgFind() で (mode&0x8000)!=0、_TDSMssgNextL() で (mode&0x0080)
	// !=0 として、メッセージ名称、メッセージ項目名称を、メッセージ定義ファ
	// イルを参照して表示するように指定した場合、送受信の各スレッドから同時
	// に DispData() を Call することにより、_TDSMssgFind()、_TDSMssgNextL()
	// において競合が発生し、予期せぬ結果となる可能性がある。
	// この場合 mode の設定を変更し、メッセージ定義ファイルを参照しない設定
	// とすること。
	// 下方の _TDSMssgNextL() の mode 設定値である dp に関して TDS.pdf の
	// 3.3 (2)(c) の注記を参照すること。

  itm =(TDSAllType*)item;
  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);

  if((MSSG_USE_FILE&0x80)!=0)	fm =0x8000;	// Use message definition file
  else				fm =0x0000;	// Not use
	// Refer to above [Note] for 'fm' setting value (setting value of 'mode'
	// value in _TDSMssgFind()).
	//
  	// fm の設定値 (_TDSMssgFind() における mode 値の設定値) に関して、上述
	// の [注意] を参照すること。

  if(msg!=0){
    if(tp==1){	dp|=0x3000;	fm|=0x3000;}	// In case of receiving
    else{	dp|=0x2000;	fm|=0x2000;}	// In case of sending

    if((md=_TDSMssgFind(fm,msg,len,Fd,hd,str))	> 0){
      if(str[0]!='\0')	PO,"[%s]\n",str);	// Message name

      for(la=0;;){
	if(MSSG_USE_NEXTL==0){	// Get item value
	  rtn=_TDSMssgNext (md,0,msg,&form,&sz,&noi,item,sizeof(item));
	  if(rtn					< 0)	break;
	  printf("%03o:%d*%2d:"		,form,sz,noi);
	  switch(form){		// Display field value
				// The second and subsequent numbers are omitted
				// 数値の２番目以降は省略
	    case 000: PO,"L[%d]\n"	,noi);			break;
	    case 010: PO,"B[%d]=%d\n"	,noi,itm->uc[0]);	break;
	    case 011: PO,"T[%d]=%d\n"	,noi,itm->uc[0]);	break;
	    case 020: PO,"A[%d]=%s\n"	,noi,itm->sc);		break;
	    case 021: PO,"J[%d]=%s\n"	,noi,itm->sc);		break;
	    case 022: PO,"K[%d]=%s\n"	,noi,itm->sc);		break;
	    case 030: PO,"I8[%d]=%lld\n",noi,itm->sl[0]);	break;
	    case 031: PO,"I1[%d]=%d\n"	,noi,itm->sc[0]);	break;
	    case 032: PO,"I2[%d]=%d\n"	,noi,itm->ss[0]);	break;
	    case 034: PO,"I4[%d]=%d\n"	,noi,itm->si[0]);	break;
	    case 040: PO,"F8[%d]=%le\n"	,noi,itm->fl[0]);	break;
	    case 044: PO,"F4[%d]=%e\n"	,noi,itm->fs[0]);	break;
	    case 050: PO,"U8[%d]=%llu\n",noi,itm->ul[0]);	break;
	    case 051: PO,"U1[%d]=%u\n"	,noi,itm->uc[0]);	break;
	    case 052: PO,"U2[%d]=%u\n"	,noi,itm->us[0]);	break;
	    case 054: PO,"U4[%d]=%u\n"	,noi,itm->ui[0]);	break;
	    default:  PO,"\n");					break;

	  }

	}else{					// Get in list format
						// リスト表示形式で取得
	  rtn=_TDSMssgNextL(md,dp,msg,&form,&noi,str,sizeof(str));
	  if(rtn					< 0)	break;
	  if((dp&0x70)>0x10){			// In case of SML
	    for(i=la;i>rtn;i--) PO,"%*s>\n",i*2,"");	// Display '>' 
	  }
	  la=rtn;				// Save current hierarchy
						// 現在の階層を保存
	  PO,"  %s\n",str);			// Display acquired field value
						// 取得した項目値を表示
	  if((dp&0x70)>0x10 && form==000 && noi==0) PO,"%*s>\n",(la+1)*2,"");
						// '>' Processing for L0
      } }					// L0 の場合の '>' 処理
      if(MSSG_USE_NEXTL!=0){
        if((dp&0x70)>0x10){
	   for(i=la;i>0;i--) PO,"%*s>\n",i*2,"");	// Show remaining '>'
      } }						// 残りの '>' 表示
      _TDSMssgExit(md,0,msg);
  } }
}



// -----------------------------------------------------------------------------
// Display sent and received data ----------------------------------------------

static int
DispData(
int		tp,		// i  : Message type
				//	=0 : Transmission result
				//	 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
{
  int		err,pos,ok=0;

  EnterCriticalSection(&Cs1);
	// This lock is important! See DispSECSMssg() for details
	// このロックは重要！ 詳細は DispSECSMssg() 参照

  if(rtn<0){					ok=1;
    if((rtn<(-E_NOTCONNECT) || (-E_ILLBLOCK)<rtn) && rtn!=(-E_NODATA)){
      _TDSErrorStatus(Fd,&err,&pos);
      PO,"ERROR  [%d.%d.%d]",rtn,err,pos);
      if      (rtn==(-ENODEV)){
	PO," : No such device ID\n");	len=ok=0;
      }else if(rtn==(-E2BIG )){
	PO," : Data size to large\n");	len=ok=0;
      }else			PO,"\n");
    }else{
      PO,"STATUS = %d : ",-rtn);
      switch(-rtn){
	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,"\n");			break;
  } } }

  if(ok==0) DispSECSMssg(tp,hd,msg,len);
  LeaveCriticalSection(&Cs1);

  return(0);
}



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

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

	// (Note) When trace output of rimary message is executed after
	// _TDSCommSend(), if secondary message is acquired by another thread
	// (using Callback, for example), there is a possibility that secondary
	// lmessage trace output may precede primary message trace output.
	// To avoid this phenomenon, trace output of primary message should be
	// done before _TDSCommSend(). In that case, since TransactionID at the
	// time of sending is not known at that time (TransactionID is returned
	// to 'hd' by Call of _TDSCommSend()), TransactionID of primary message
	// can not be displayed.
	// It is necessary to determine trace output position and content on
	// user side in consideration of above.
	//
	// (注) １次メッセージのトレース出力を _TDSCommSend() の後に実行する場
	// 合、送信１次メッセージに対する相手からの２次メッセージの取得を（例え
	// ば Callback を使用して）別スレッドで行うと、その２次メッセージのト
	// レース出力の方が、ここでの１次メッセージのトレース出力よりも先に出力
	// されるケースが発生する可能性がある。
	// この現象を回避するためには、１次メッセージのトレース出力を
	// _TDSCommSend() の前に行えばいいのだが、その場合、送信時の
	// TransactionID がその時点ではわからない (TransactionID は
	// _TDSCommSend() の Call により、hd に戻って来る) ので、１次メッセ−ジ
	// の TransactionID の表示はできないことになる。
	// 以上を考慮してユーザ側でのトレース出力位置、内容を決定する必要がある。

  if(FUNC_TYPE==0)	rtn=_TDSCommSend(Fd,0x0000,0,sf,  0,msg,len,&hd);
  else			rtn=_TDSUDrvSend(Fd,0x0000,0,sf,  0,msg,len,&hd);
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


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

static int
SendS1F2H(
unsigned int	did,
unsigned int	xid)
{
  TDSECSHead	hd;
  char		msg[256];
  int		rtn,md,sf=0x0102,len;
  
  if((MSSG_USE_FILE&0x80)==0){			// Do not use message definition
    md =_TDSMssgInit   (   0,msg,sizeof(msg),Fd);		// S1F2
	_TDSMssgBuild  (md,0,msg,000,  0,  0);			// L0
    len=_TDSMssgEnd    (md,0,msg);

  }else{					// Use message definition
    //EnterCriticalSection(&Cs0);
	_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S1F2_H");
    len=_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  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;
  
  if((MSSG_USE_FILE&0x80)==0){			// Do not use message definition
    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);

  }else{					// Use message definition
    //EnterCriticalSection(&Cs0);
	_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S1F2_E");
    len=_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


// =============================================================================
// S2F49 message construction and sending --------------------------------------

static int
SendS2F49()
{
  static int	cnt=0;
  TDSECSHead	hd;
  char		msg[1024];
  char		vb[4],str[128],itm[32];
  int		rtn,md,sf=0x8200+49,no1,no2,len,i,j;

  sprintf(str,"LOTID (%4d)",++cnt);	no1=(cnt%2)+1;	no2=(cnt%10)+1;
  
  if((MSSG_USE_FILE&0x80)==0){			// Do not use message definition
    md=		_TDSMssgInit   (   0,msg,sizeof(msg),Fd);	// S2F49
  		_TDSMssgBuild  (md,0,msg,000,  3, 0);		// L3
    vb[0]=0;	_TDSMssgBuild  (md,0,msg,010,  1,vb);		//  DATAIDB
  		_TDSMssgBuild  (md,0,msg,020,  0,"LOAD");	//  RCMD
  		_TDSMssgBuild  (md,0,msg,000,  4, 0);		//  L4
    vb[0]=1;	_TDSMssgBuild  (md,0,msg,010,  1,vb);		//   STID
    vb[0]=0;	_TDSMssgBuild  (md,0,msg,010,  1,vb);		//   MTKD
  		_TDSMssgBuild  (md,0,msg,020, 20,str);		//   LOTID
    		_TDSMssgBuild  (md,0,msg,000,no1, 0);		//   L[no1]
    for(i=0;i<no1;i++){
    		_TDSMssgBuild  (md,0,msg,000,no2, 0);		//    L[no2]
      for(j=0;j<no2;j++){
    		_TDSMssgBuild  (md,0,msg,000,  2, 0);		//     L[2]
	sprintf(str,"WAFER(%04d-%d-%02d)",cnt,i+1,j+1);
  		_TDSMssgBuild  (md,0,msg,020, 20,str);		//      WAFERID
	sprintf(str,"PPID (%04d-%d-%02d)",cnt,i+1,j+1);
  		_TDSMssgBuild  (md,0,msg,020, 16,str);		//      PPID
    } }
    len=	_TDSMssgEnd    (md,0,msg);

  }else{					// Use message definition
    //EnterCriticalSection(&Cs0);
		_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S2F49");
  		_TDSMDMssgBuild(Md,0,msg,"LOTID",0,str);	//   LOTID
  		_TDSMDMssgBuild(Md,0,msg,"NOI1"	,1,&no1);	//   L[no1]
    for(i=0;i<no1;i++){
      sprintf(itm,"NOI2:%d"			,i+1);
  		_TDSMDMssgBuild(Md,0,msg,itm	,1,&no2);	//    L[no2]
      for(j=0;j<no2;j++){
	sprintf(itm,"WAFERID:%d:%d"		,i+1,j+1);
	sprintf(str,"WAFER[%04d-%d-%02d]",cnt	,i+1,j+1);
  		_TDSMDMssgBuild(Md,0,msg,itm	,0,str);	//      WAFERID
	sprintf(itm,"PPID:%d:%d"		,i+1,j+1);
	sprintf(str,"PPID [%04d-%d-%02d]",cnt	,i+1,j+1);
  		_TDSMDMssgBuild(Md,0,msg,itm	,0,str);	//      PPID
    } }
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;	// See (Note) in SendS1F1().
  else{					// SendS1F1() の (注) を参照すること。
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,0,sf,  0,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,0,sf,  0,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


// -----------------------------------------------------------------------------
// S2F50 message construction and sending --------------------------------------

static int
SendS2F50(
int		did,
unsigned int	xid)
{
  static int	cnt=0;
  TDSECSHead	hd;
  char		msg[256];
  char		vb[4],str[128];
  int		rtn,md,sf=0x0200+50,len;
  
  sprintf(str,"LOTID (%4d)",++cnt);
  
  if((MSSG_USE_FILE&0x80)==0){			// Do not use message definition
    md=		_TDSMssgInit   (   0,msg,sizeof(msg),Fd);	// S2F50
		_TDSMssgBuild  (md,0,msg,000,  2, 0);		// L2
    vb[0]=0;	_TDSMssgBuild  (md,0,msg,010,  1,vb);		//  HCACK
		_TDSMssgBuild  (md,0,msg,000,  2, 0);		//  L2
  		_TDSMssgBuild  (md,0,msg,020, 16,"PPID");	//   PPID
  		_TDSMssgBuild  (md,0,msg,020, 20,str);		//   LOTID
    len=	_TDSMssgEnd    (md,0,msg);

  }else{					// Use message definition
    //EnterCriticalSection(&Cs0);
		_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S2F50");
  		_TDSMDMssgBuild(Md,0,msg,"LOTID",  0,str);	//   LOTID
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}


// =============================================================================
// S6F11 message construction and sending --------------------------------------
// S6F11 メッセージ構築及び送信 ------------------------------------------------

static int
SendS6F11()
{
  static int	cnt=0;
  TDSECSHead	hd;
  char		msg[256];
  unsigned short vs[4];
  int		rtn,md,sf=0x8600+11,len;

  if((++cnt)==65536) cnt=0;

  if((MSSG_USE_FILE&0x80)==0){			// Do not use message definition
    md=		_TDSMssgInit   (   0,msg,sizeof(msg),Fd);	// S6F11
  		_TDSMssgBuild  (md,0,msg,000,  3, 0);		// L3
    vs[0]=cnt;	_TDSMssgBuild  (md,0,msg,052,  1,vs);		//  DATAID
    vs[0]=8;	_TDSMssgBuild  (md,0,msg,052,  1,vs);		//  CEID
  		_TDSMssgBuild  (md,0,msg,000,  3, 0);		//  L3
  		_TDSMssgBuild  (md,0,msg,020, 16,"DATA1 です");	//   DATA1
  		_TDSMssgBuild  (md,0,msg,020, 16,"DATA2 です");	//   DATA2
  		_TDSMssgBuild  (md,0,msg,020, 14,"YYYYMMDDhhmmss");
			// @TIME Actually I set current time, but omitted.
			// @TIME 本当は現在時刻を設定するのだが、省略。
    len=	_TDSMssgEnd    (md,0,msg);

  }else{					// Use message definition
    //EnterCriticalSection(&Cs0);
		_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S6F11_0");
    vs[0]=cnt;	_TDSMDMssgBuild(Md,0,msg,"DATAID",1,vs);	//  DATAID
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;	// See (Note) in SendS1F1().
  else{					// SendS1F1() の (注) を参照すること。
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,0,sf,  0,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,0,sf,  0,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// S6F12 message construction and sending --------------------------------------

static int
SendS6F12(
int		did,
unsigned int	xid)
{
  TDSECSHead	hd;
  char		msg[256];
  char		vb[4];
  int		rtn,md,sf=0x0600+12,len;

  if((MSSG_USE_FILE&0x80)==0){			// Do not use message definition
    md=		_TDSMssgInit (   0,msg,sizeof(msg),Fd);		// S6F12
    vb[0]=0;	_TDSMssgBuild(md,0,msg,010,  1,vb);		// ACKC
    len=	_TDSMssgEnd  (md,0,msg);

  }else{					// Use message definition
    //EnterCriticalSection(&Cs0);
		_TDSMDMssgInit (Md,0,msg,sizeof(msg),"S6F12");
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

  return(rtn);
}



// -----------------------------------------------------------------------------
// Construct and send message with specified name using message definition file
// 指定名称のメッセージをメッセージ定義ファイルを使用して構築し送信する --------

static int
SendNamedMssg(
int		did,
unsigned int	xid,
char		*mname)
{
  TDSECSHead	hd;
  char		msg[4096];			// Up to 4096 Bytes
						// 最大 4096Bytes までとする
  int		rtn=(-1),sf,scode,fcode,len;

						// Do not use message definition
  if((MSSG_USE_FILE&0x80)				==0)	goto Exit;

	// In order to reflect changes in message definition file at this point,
	// reinitialization processing is performed here. Naturally, this
	// process is not necessary if there is no change in message definition
	// file.
	// In addition, reinitialization here is related to the subsequent
	// processing such as _TDSMssgInit(), and since trace display is
	// performed by already initialized information, keep in mind to the
	// latest message information is not necessarily used.
	//
	// メッセージ定義ファイルの変更をこの時点で反映するため、ここで再度初期
	// 化処理を行う。もとより、メッセージ定義ファイルの変更がないのであれば、
	// この処理は必要ない。
	// また、ここで再初期化するのは、その後の _TDSMssgInit() 等の処理に関す
	// るものであり、トレース表示は既初期化済みの情報により行われるので、必
	// ずしも最新メッセージ情報が使用される訳ではないことに留意すること。

  if(Md>0)	_TDSMDMssgTerminate (Md,0);
  Md=		_TDSMDMssgInitialize(0x4000,Fd,0);

  sscanf(mname,"S%dF%d"	,&scode,&fcode);
  sf = scode*0x0100 + fcode;
  if(scode!=9 && (fcode%2)!=0)	sf|=0x8000;

  //EnterCriticalSection(&Cs0);
  if(	_TDSMDMssgInit(Md,0,msg,sizeof(msg),mname)	< 0)	goto Exit;
  len=	_TDSMDMssgEnd (Md,0,msg);
  //LeaveCriticalSection(&Cs0);

  if(len<0)		rtn=len;	// See (Note) in SendS1F1().
  else{					// SendS1F1() の (注) を参照すること。
    if(FUNC_TYPE==0)	rtn=_TDSCommSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,0x0000,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,msg,len,rtn);

Exit:
  return(rtn);
}



// =============================================================================
// Callback function ===========================================================

// -----------------------------------------------------------------------------
// Callback for receiving SECS message from the other side ---------------------
// 相手からの SECS メッセージ受信用 Callback -----------------------------------

static int
CBRecv(
void		*par,
TDSCBData	*cbd)
{
  TDSECSHead	hd;
  char		msg[1024],rname[32],sname[32];
  int		rtn,len,sf;

  DispData(1,&cbd->thd,cbd->msg,cbd->rtn,cbd->rtn);

  if      (cbd->req== 0 && cbd->rtn>=0){
    if((MSSG_USE_FILE&0x80)==0){
	// If you do not use message definition file, check SF-Code on your own,
	// determine necessity of sending secondary message, and send it if
	// necessary.
	// メッセージ定義ファイルを使用しない場合は、自力で SF-Code を調べ、２
	// 次メッセージの送出の必要性を判断し、必要な場合は、送出する。
      if     (cbd->sf==(0x8100+ 1)&&OType==0)	SendS1F2H (cbd->devid,cbd->xid);
      else if(cbd->sf==(0x8100+ 1)&&OType==1)	SendS1F2E (cbd->devid,cbd->xid);
      else if(cbd->sf==(0x8200+49))		SendS2F50 (cbd->devid,cbd->xid);
      else if(cbd->sf==(0x8600+11))		SendS6F12 (cbd->devid,cbd->xid);

    }else if((cbd->sf&0x8000)!=0){
	// If you use message definition file, use automatic reply function. 
	// (It does not have to be used.)
	// メッセージ定義ファイルを使用する場合は、自動返信機能を使用する。
	//（使用しなくてはいけない訳ではない。）
      memcpy(msg,cbd->msg,cbd->rtn);
      //EnterCriticalSection(&Cs0);
      len=_TDSMDMssgAutoRes(Md,0,&cbd->thd
		,msg,cbd->rtn,sizeof(msg),rname,sname,&sf);
      //LeaveCriticalSection(&Cs0);
      if(len							>=0){
	PO,"RECV %s ..  Auto respond %s [S%dF%d]\n"
		,rname,sname,sf/0x100,sf&0xff);
	if(FUNC_TYPE==0){
	  rtn=_TDSCommSend (Fd,0x0000,cbd->devid,sf,cbd->xid,msg,len,&hd);
	}else{
	  rtn=_TDSUDrvSend (Fd,0x0000,cbd->devid,sf,cbd->xid,msg,len,&hd);
	}
	DispData(2,&hd,msg,len,rtn);
      }else{
	if(len!=(-930) && len!=(-931)){
	  PE,"RECV Auto response error (%d)\n",len);
  } } } }

  return(0);
}



// -----------------------------------------------------------------------------
// Callback for receiving sending result ---------------------------------------
// 送信結果受信用 Callback -----------------------------------------------------

static int
CBSend(
void		*par,
TDSCBData	*cbd)
{
  DispData(0,&cbd->thd,cbd->msg,cbd->rtn,cbd->rtn);
  return(0);
}



// -----------------------------------------------------------------------------
// User trace output function --------------------------------------------------

static int
CBTrace(
void		*par,
TDSTRData	*trd)
{
  PO,"UserTrace [%08d.%06d.%03d] (%4d) : %s\n"
	,trd->date,trd->time,trd->msec,trd->proc,trd->msg);
  return(0);
}



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

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