
//
// Trust Design LLC : SECS/HSMS Communication library
//
// (c) Copyright Trust Design LLC.  2010-2021.  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


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;
  int			rtn;

  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);
  }
  if((rtn=pthread_create(&th,&ta,func,par))	==0)	return( 0);
  else							return((void*)(-1));
}


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
int		did,		// i  : DeciceID
int		sf,		// i  : SF-Code
int		xid,		// i  : TransactionID
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;
  unsigned char	*hh;
  char		item[4096],str[512],adsf[128],sfcode[12],rbit,wbit;
  char		mnam[32],inam[32],sdid[32];
  int		rtn,md=0,dp=MssgUseFile|MssgDispType,fm,form,sz,noi,la;
  int		no0,no1,no2,i,j;
  	// [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((did&0x8000)!=0)	 rbit='R';
  wbit=' ';	if((sf &0x8000)!=0)	 wbit='W';
  hh  =(unsigned char*)hd;
  sprintf(sfcode,"S%dF%d",(sf&0x7f00)>>8,sf&0xff);
  if		  ((sf &0x7f00)==0x0600) sprintf(adsf,"  %s",sfcode);
  else if	  ((sf &0x7f00)==0x0200) sprintf(adsf,"          %s",sfcode);
  else						 adsf[0]='\0';
  if		  ( did		<0)	 sprintf(sdid,"%6d"	,did);
  else					 sprintf(sdid,"0x%04x"	,did);
  PO,"[%s]  Dev=%s  %-8s  %c%c  XId=0x%04x.%04x  Len=%4d"
     "  Head=0x%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x%s\n"
		,ctp[tp],sdid,sfcode,rbit,wbit
		,(xid>>16)&0xffff,xid&0xffff,len
		,hh[0],hh[1],hh[2],hh[3],hh[4],hh[5],hh[6],hh[7],hh[8],hh[9]
		,adsf);

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

	}else{					// Get in list format
						// リスト表示形式で取得
	  rtn=_TDSMssgNextL(md,dp,msg,&form,&noi,str,sizeof(str));
	  form&=077;
	  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(MssgUseNextL!=0){
        if((dp&0x70)>0x10){
	   for(i=la;i>0;i--) PO,"%*s>\n",i*2,"");	// Show remaining '>'
      } }						// 残りの '>' 表示
      _TDSMssgExit(md,0,msg);
    }

    if((MssgUseFile&0x80)!=0  &&  MssgUseNextL	!=0){
		// When the message definition file is used.
		// And when the list structure is displayed using _TDSMssgNextL().  (Therefore, when
		// message item acquisition by _TDSMssgNext() is not performed.)
		// The following is an example how to use _TDSMDMssgFind(), MDMssgNext() method to get
		// the item value by specifying item name only for items that need to be obtained by
		// using the message structure definition file.
		//
		// メッセージ定義ファイルを使用する指定で、_TDSMssgNextL() を使用してリスト構造表示を
		// している場合（従って、_TDSMssgNext() によるメッセージ項目取得をしていない場合）に、
		// 以下を例示する。
		// _TDSMDMssgFind(), MDMssgNext() を使用してメッセージ構造定義を利用して取得する必要
		// のある項目のみ、項目名称を指定して項目値を取得する方法。
      sf&=0x7fff;
      if((rtn=_TDSMDMssgFind(Md,0,msg,len,sf,mnam))>0){
	PO,"Message Definition Name = '%s'\n"		,mnam);

	if	(sf	==0x0115){		// S1F21
	  if((no0=_TDSMDMssgNext(Md,0,msg,"VB"		, 0,itm))>=0){
	    PO,"VB          [%2d]=0x"	,no0);	for(i=0;i<no0;i++) PO,"%02x ",itm->uc[i]);	PO,"\n");
	  }
	  if((no0=_TDSMDMssgNext(Md,0,msg,"MDLN"	, 0,itm))>=0){
	    PO,"MDLN        [%2d]='%s'\n",no0				     ,itm->sc);
	  }
	  if((no0=_TDSMDMssgNext(Md,0,msg,"VI4"		, 0,itm))>=0){
	    PO,"VI4         [%2d]="	,no0);	for(i=0;i<no0;i++) PO,"%d "  ,itm->si[i]);	PO,"\n");
	  }
	  if((no0=_TDSMDMssgNext(Md,0,msg,"VF4"		, 0,itm))>=0){
	    PO,"VF4         [%2d]="	,no0);	for(i=0;i<no0;i++) PO,"%e "  ,itm->fs[i]);	PO,"\n");
	  }

	}else if(sf	==0x0116){		// S1F22
	  if((no0=_TDSMDMssgNext(Md,0,msg,"VT"		, 0,itm))>=0){
	    PO,"VT          [%2d]=0x"	,no0);	for(i=0;i<no0;i++) PO,"%02x ",itm->uc[i]);	PO,"\n");
	  }
	  if((no0=_TDSMDMssgNext(Md,0,msg,"COMMENT"	, 0,itm))>=0){
	    PO,"COMMENT     [%2d]='%s'\n",no0				     ,itm->sc);
	  }
	  if((no0=_TDSMDMssgNext(Md,0,msg,"AU8"		, 0,itm))>=0){
	    PO,"AU8         [%2d]="	,no0);	for(i=0;i<no0;i++) PO,"%llu ",itm->ul[i]);	PO,"\n");
	  }
	  if((no0=_TDSMDMssgNext(Md,0,msg,"VF8"		, 0,itm))>=0){
	    PO,"VF8         [%2d]="	,no0);	for(i=0;i<no0;i++) PO,"%le " ,itm->fl[i]);	PO,"\n");
	  }

	}else if(sf	==0x0231){		// S2F49
	  if(_TDSMDMssgNext(Md,0,msg,"NOI1"		, 0,itm) > 0){
	    PO,"NOI1        [%2d]\n"	     ,no1=itm->ui[0]);
	    for(i=0;i<no1;i++){
	      sprintf(inam,"NOI2:%d",i+1);
		// It is necessary to create the name of the variable number item each time.
		// Here, after confirming NOI1 value, NOI2 names are created as "NOI2: 1", "NOI2: 2" etc.
		// in the order of the number of NOI1.  See Section 3.4 of TDS.pdf for details.
		// 不定個数項目の名称は、その都度作成する必要がある。
		// ここでは NOI1 値を確定させた後、NOI2 名称を NOI1 の個数、順序で "NOI2:1"、"NOI2:2" 等
		// として作成する。 詳細は、TDS.pdf 第3.4節を参照すること。
	      if(_TDSMDMssgNext(Md,0,msg,inam		, 0,itm) > 0){
		PO,"%-12s[%2d]\n"	,inam,no2=itm->ui[0]);
		for(j=0;j<no2;j++){
		  sprintf(inam,"WAFERID:%d:%d",i+1,j+1);
		  if((no0=_TDSMDMssgNext(Md,0,msg,inam	, 0,itm))>=0){
		    PO,"%-12s[%2d]='%s'\n",inam,no0		  	     ,itm->sc);
	  } } } } }

	}else if(sf	==0x060b){		// S6F11
	  if((no0=_TDSMDMssgNext(Md,0,msg,"DATAID"	,10,itm))>=0){
	    PO,"DATAIDB     [%2d]="	,no0);	for(i=0;i<no0;i++) PO,"%hu " ,itm->us[i]);	PO,"\n");
	} } 
	_TDSMDMssgExit(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
int		did,		// i  : DeciceID
int		sf,		// i  : SF-Code
int		xid,		// i  : TransactionID
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"
				,did);				break;
	case E_REJECT:		PO,"Rejected XId=0x%04x.%04x\n"
				,(xid>>16)&0xffff,xid&0xffff);	break;
	case E_DESELECT:	PO,"Deselected (0x%04x)\n"
				,did);				break;
	case E_SEPARATE:	PO,"Separated  (0x%04x)\n"
				,did);				break;
	case E_NOTCONNECT:	PO,"Not connected\n");		break;
	default:		PO,"\n");			break;
  } } }

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

  return(0);
}



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

static int
SendS1F1()
{
  TDSECSHead	hd;
  char		msg[16];
  int		rtn,sf=0x8101,cmd=0x0000,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(HeaderBO	!=0)	cmd=0x2000;
  if(FuncType	==0)	rtn=_TDSCommSend(Fd,cmd, -1,sf,  0,msg,len,&hd);
  else			rtn=_TDSUDrvSend(Fd,cmd, -1,sf,  0,msg,len,&hd);
  DispData(2,&hd, -1,sf,rtn,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,cmd=0x0000,len;
  
  if((MssgUseFile&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(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,did,sf,rtn,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,cmd=0x0000,len;
  
  if((MssgUseFile&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(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,did,sf,rtn,msg,len,rtn);

  return(rtn);
}


// =============================================================================
// S1F21 message construction and sending --------------------------------------

static int
SendS1F21()
{
  TDSECSHead		hd;
  char			msg[1024];
  char			vb [2]={                      1,                  0xff};
  char			vt [2]={                      0,                     1};
  char*			vs0   ="MSG11";
  char* 		vs1   ="ｿﾌﾄ 02";
  char* 		vs2   ="これは ｺﾒﾝﾄ (SubFunction.h) です";
  char			vi1[2]={                      2,                  -128};
  short			vi2[2]={                  32767,                -32768};
  int			vi4[2]={             0x7ffffffe,            0xfffffffe};
  long long		vi8[2]={   0x7fffffffffffffffLL,  0x800080008000800bLL};
  unsigned char		vu1[2]={                  0x7fU,                 0xffU};
  unsigned short	vu2[2]={                0x7ffeU,               0xfffeU};
  unsigned int		vu4[2]={            0x7ffffffdU,           0xfffffffdU};
  unsigned long	long	vu8[2]={  0x7fffffffffffffffULL, 0x800080008000800bULL};
  unsigned long	long	au8[8]={  7, -6,  5, -4,  3, -2,  1,  0};
  float			vf4[2]={  (float)9.87654321e-21,(float)-8.642097531e13};
  double		vf8[2]={-1.9283746574839201e123,9.1827364546372819208e-213};
  int			rtn,md,sf=0x8100+21,cmd=0x0000,len;


  if((MssgUseFile&0x80)==0){			// Do not use message definition
    md=		_TDSMssgInit   (   0,msg,sizeof(msg),Fd);	// S1F21
  		_TDSMssgBuild  (md,0,msg,000,  4, 0);		// L4
  		_TDSMssgBuild  (md,0,msg,000,  2, 0);		//  L2
		_TDSMssgBuild  (md,0,msg,010,  2,vb);		//   VB
		_TDSMssgBuild  (md,0,msg,011,  2,vt);		//   VT
  		_TDSMssgBuild  (md,0,msg,000,  3, 0);		//  L3
  		_TDSMssgBuild  (md,0,msg,020,  6,vs0);		//   MDLN
  		_TDSMssgBuild  (md,0,msg,021,  0,vs1);		//   SOFTREV
  		_TDSMssgBuild  (md,0,msg,022,  0,vs2);		//   COMMENT
  		_TDSMssgBuild  (md,0,msg,000,  9, 0);		//  L9
		_TDSMssgBuild  (md,0,msg,031,  2,vi1);		//   VI1
		_TDSMssgBuild  (md,0,msg,032,  2,vi2);		//   VI2
		_TDSMssgBuild  (md,0,msg,034,  2,vi4);		//   VI4
		_TDSMssgBuild  (md,0,msg,030,  2,vi8);		//   VI8
		_TDSMssgBuild  (md,0,msg,051,  2,vu1);		//   VU1
		_TDSMssgBuild  (md,0,msg,052,  2,vu2);		//   VU2
		_TDSMssgBuild  (md,0,msg,054,  2,vu4);		//   VU4
		_TDSMssgBuild  (md,0,msg,050,  2,vu8);		//   VU8
		_TDSMssgBuild  (md,0,msg,050,  8,au8);		//   AU8
  		_TDSMssgBuild  (md,0,msg,000,  2, 0);		//  L2
		_TDSMssgBuild  (md,0,msg,044,  2,vf4);		//   VF4
		_TDSMssgBuild  (md,0,msg,040,  2,vf8);		//   VF8
    len=	_TDSMssgEnd    (md,0,msg);

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

  if(len<0)		rtn=len;
  else{
    if(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd, -1,sf,  0,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd, -1,sf,  0,msg,len,&hd);
  }
  DispData(2,&hd, -1,sf,rtn,msg,len,rtn);

  return(rtn);
}


// -----------------------------------------------------------------------------
// S1F22 message construction and sending --------------------------------------

static int
SendS1F22(
int		did,
unsigned int	xid)
{
  TDSECSHead		hd;
  char			msg[1024];
  char			vb [2]={                      2,                  0xfe};
  char			vt [2]={                      1,                     0};
  char* 		vs0   ="MSG12";
  char* 		vs1   ="ﾚﾋﾞｼﾞｮﾝ";
  char* 		vs2   ="This is コメント (SubFunction.h)";
  char			vi1[2]={                    127,                    -2};
  short			vi2[2]={                  32766,                -32768};
  int			vi4[2]={             2147483646,           -2147483647};
  long long		vi8[2]={   0x8000000000000000LL,  0x7ffffffffffffffeLL};
  unsigned char		vu1[2]={                   254U,                 0xffU};
  unsigned short	vu2[2]={                 65534U,               0xffffU};
  unsigned int		vu4[2]={            4294967294U,           0xffffffffU};
  unsigned long	long	vu8[2]={18446744073709551614ULL, 0xffffffffffffffffULL};
  unsigned long	long	au8[8]={ -1,  2, -3,  4, -5,  6, -7,  0};
  float			vf4[2]={  (float)7.89012345e-12,(float)-4.321098765e31};
  double		vf8[2]={ 5.6473829101928374e189,-3.2109876543210987654e-179};
  int			rtn,md,sf=0x0100+22,cmd=0x0000,len;


  if((MssgUseFile&0x80)==0){			// Do not use message definition
    md=		_TDSMssgInit   (   0,msg,sizeof(msg),Fd);	// S1F22
  		_TDSMssgBuild  (md,0,msg,000,  4, 0);		// L4
  		_TDSMssgBuild  (md,0,msg,000,  2, 0);		//  L2
		_TDSMssgBuild  (md,0,msg,010,  2,vb);		//   VB
		_TDSMssgBuild  (md,0,msg,011,  2,vt);		//   VT
  		_TDSMssgBuild  (md,0,msg,000,  3, 0);		//  L3
  		_TDSMssgBuild  (md,0,msg,020,  6,vs0);		//   MDLN
  		_TDSMssgBuild  (md,0,msg,021,  0,vs1);		//   SOFTREV
  		_TDSMssgBuild  (md,0,msg,022,  0,vs2);		//   COMMENT
  		_TDSMssgBuild  (md,0,msg,000,  9, 0);		//  L9
		_TDSMssgBuild  (md,0,msg,031,  2,vi1);		//   VI1
		_TDSMssgBuild  (md,0,msg,032,  2,vi2);		//   VI2
		_TDSMssgBuild  (md,0,msg,034,  2,vi4);		//   VI4
		_TDSMssgBuild  (md,0,msg,030,  2,vi8);		//   VI8
		_TDSMssgBuild  (md,0,msg,051,  2,vu1);		//   VU1
		_TDSMssgBuild  (md,0,msg,052,  2,vu2);		//   VU2
		_TDSMssgBuild  (md,0,msg,054,  2,vu4);		//   VU4
		_TDSMssgBuild  (md,0,msg,050,  2,vu8);		//   VU8
		_TDSMssgBuild  (md,0,msg,050,  8,au8);		//   AU8
  		_TDSMssgBuild  (md,0,msg,000,  2, 0);		//  L2
		_TDSMssgBuild  (md,0,msg,044,  2,vf4);		//   VF4
		_TDSMssgBuild  (md,0,msg,040,  2,vf8);		//   VF8
    len=	_TDSMssgEnd    (md,0,msg);

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

  if(len<0)		rtn=len;
  else{
    if(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,did,sf,rtn,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,cmd=0x0000,no1,no2,len,i,j;

  sprintf(str,"LOTID (%4d)",++cnt);	no1=(cnt%2)+1;	no2=(cnt%10)+1;
  
  if((MssgUseFile&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_H");
  		_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(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd, -1,sf,  0,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd, -1,sf,  0,msg,len,&hd);
  }
  DispData(2,&hd, -1,sf,rtn,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,cmd=0x0000,len;
  
  sprintf(str,"LOTID (%4d)",++cnt);
  
  if((MssgUseFile&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_E");
  		_TDSMDMssgBuild(Md,0,msg,"LOTID",  0,str);	//   LOTID
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,did,sf,rtn,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,cmd=0x0000,len;

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

  if((MssgUseFile&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_E0");
    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(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd, -1,sf,  0,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd, -1,sf,  0,msg,len,&hd);
  }
  DispData(2,&hd, -1,sf,rtn,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,cmd=0x0000,len;

  if((MssgUseFile&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_H");
    len=	_TDSMDMssgEnd  (Md,0,msg);
    //LeaveCriticalSection(&Cs0);
  }

  if(len<0)		rtn=len;
  else{
    if(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,did,sf,rtn,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),cmd=0x0000,sf,scode,fcode,len;

						// Do not use message definition
  if((MssgUseFile&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(HeaderBO	!=0)	cmd=0x2000;
    if(FuncType	==0)	rtn=_TDSCommSend (Fd,cmd,did,sf,xid,msg,len,&hd);
    else		rtn=_TDSUDrvSend (Fd,cmd,did,sf,xid,msg,len,&hd);
  }
  DispData(2,&hd,did,sf,rtn,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,cmd=0x0000;

	// When header information in Network Byte order is instructed, and if
	// this function operates as a Callback function from TDS, it must be
	// converted because it is stored in operation system's byte order.
	// ヘッダ情報の Network Byte order での取得が指示されている場合 TDS から
	// の Callback 関数として本関数が動作する場合は、動作システムの BO で格
	// 納されているので、Network Byte Order に変換する必要がある。
  if(HeaderBO	!=0 &&	// Specify the 'Network Byte Order' header
     UseRecvThrd==0 &&	// Not use message receiving user thread
     FuncType	==0){	// Function type is '_TDSCommXxxx()'
    _TDSCommHeaderBO(Fd,0x00,&cbd->thd);
  }

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

  if      (cbd->req== 0 && cbd->rtn>=0){
    if((MssgUseFile&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==(0x8100+21))		SendS1F22 (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(HeaderBO !=0)	cmd=0x2000;
	if(FuncType ==0){
	  rtn=_TDSCommSend (Fd,cmd,cbd->devid,sf,cbd->xid,msg,len,&hd);
	}else{
	  rtn=_TDSUDrvSend (Fd,cmd,cbd->devid,sf,cbd->xid,msg,len,&hd);
	}
	DispData(2,&hd,cbd->devid,sf,cbd->xid,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)
{
  if(HeaderBO	!=0)	_TDSCommHeaderBO(Fd,0x00,&cbd->thd);
  DispData(0,&cbd->thd,cbd->devid,cbd->sf,cbd->xid,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;
}
