# coding: Shift_JIS

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


# ==============================================================================
#
# RubyIo : Test and sample program
#
#  Construct a message in AP and simply send and receive SECS messages.
#
#
# Starting method
#
#   ruby RubyIo.rb {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, "RubyIo h" and "RubyIo e" both operate on same machine or
#   different machines to communicate with each other. The communication
#   partner may be another sample AP such as BasicIo.exe or Callbackio.exe.
#
#   After startup, refer to the menu display, enter request code etc. and the
#   function is executed.
# 
#   When host starts up, it waits for a connection from equipment.
#   When device side is started, connection request and Select request are
#   automatically issued to host side.
#   On host side, repeat '1:Recv' processing, and confirm that display is
#   'STATUS=951: No data'. After that, exchange any message. On receiving side,
#   do not forget to reap received data until 'STATUS=951'.
#
#   The transaction ID used when sending secondary message uses that of primary
#   message received immediately before.
#
#   This sample omits some of abnormal processing.
#
#   "SECS_MODE" defined below is defined to determine whether or not connection
#   processing to Passive side in case of Active connection is performed when
#   using _TDSCommXxxx() as processing function.
#   (Note) Changing this value alone does not make TDS aware of the change in
#      SECS1 or HSMS.  TDS determines SECS1 or HSMS based on the setting of
#      SECSMODE in the configuration file (.ini).  To change this value, you
#      must change the setting of SECSMODE in .ini.
#
#   "FUNC_TYPE" is defined to select type of processing function to be used.
#   If you use _TDSUDrvXxxx(), there is no need to perform connection
#   processing for HSMS-SS Active connection, so there is no need to set
#   "SECS_MODE" in principle.
#   Since _TDSwitches SECS-1 connection or HSMS-SS connection in setting file
#   (.ini), user AP does not need to know connection method (SECS-1or HSMS and
#   Passive/Active at HSMS) as long as _TDSUDrvXxxx() is used as processing
#   function.
#
#   By changing values of "MSSG_USE_FILE" and "MSSG_DISP_TYPE" defined below,
#   you can change output format of SECS message displayed by this AP.
#   By changing value of "MSSG_USE_NEXTL", it is possible to change whether to
#   acquire field values as data or display list format when analyzing message
#   contents.
#   Similarly, if "USE_CALLBACK" is set to 1, use Callback function to perform
#   receiving processing, and automatically display received message and output
#   secondary message.
#   When using Callback function, keep in mind that input prompting message may
#   be disturbed.
#
#
# ==============================================================================
#
# RubyIo : テスト 及び サンプル・プログラム
#
#  ＡＰ内でメッセージを構築し、単純に SECS メッセージの送受信を行う。
#
#
# 起動方法
#
#   ruby RubyIo.rb {h|e}
#   ~~~~~~~~~~~~~~~~~~~~
#   h    : Sample.ini の [HOST]  セクションを参照し動作を決定する
#   e    : Sample.ini の [EQUIP]      :              :
#
#
#   通常、RubyIo h 及び RubyIo e の両方を、同一マシン、もしくは異なるマシン上で
#   動作させて、相互通信を行う。通信相手は、BasicIo.exe、Callbackio.exe 等、
#   他のサンプルＡＰでもよい。
#
#   起動後、メニュ表示を参照し、要求コード等を入力し、機能を実行する。
# 
#   ホスト側は起動すると、装置側からの接続を待つ。装置側を起動すると、自動的
#   にホスト側に接続要求、Select 要求を発行する。ホスト側では、1:Recv 処理を
#   繰り返し行い、表示が「STATUS=951 : No data」となるのを確認する。その後、
#   任意のメッセージのやりとりを行う。受信側では、受信データの刈り取りを「
#   STATUS=951」になるまで行うことを忘れぬこと。
#
#   ２次メッセージを送信する際のトランザクションＩＤは、直前に受信した１次
#   メッセージのものを使用する。
#
#   本サンプルは、異常時処理を一部省略している。
#
#   以下で定義する "SECS_MODE" は、処理関数として _TDSCommXxxx() を使用する
#   場合で Active 接続の場合の Passive 側への接続処理の実行の有無を判断する
#   ために定義する。
#   (注) この値の変更だけで TDS が SECS1 or HSMS の変更を認識するわけではない。 
#      TDS は SECS1 or HSMS を判断するのは設定ファイル (.ini) の SECSMODE の
#      設定によるので、この値を変更する場合は .ini の SECSMODE の設定変更を
#      しなければならない。
#
#   "FUNC_TYPE" は、使用する処理関数の種別を選択するために定義する。
#   _TDSUDrvXxxx() を使用する場合は、HSMS-SS Active 接続の場合の接続処理を
#   行う必要がないので、本来であれば "SECS_MODE" の設定も必要がない。
#   TDS は、SECS-1 接続 or HSMS-SS 接続の切替は、設定ファイル (.ini) にて行う
#   ため、処理関数として _TDSUDrvXxxx() を使用する限りは、ユーザＡＰでは、
#   接続方式（SECS-1/HSMS-SS そして HSMS-SS時の Passive/Active）を知る必要は
#   ない。
#
#   以下で定義する "MSSG_USE_FILE"、"MSSG_DISP_TYPE" の値を変更すると、本ＡＰ
#   にて表示する SECS メッセージの出力形式を変更できる。
#   "MSSG_USE_NEXTL" の値を変更すると、メッセージ内容を解析する際に、項目値を
#   データとして取得するか、表示リスト形式で取得するかを変更できる。
#   同様に "USE_CALLBACK" を =1 とすると、受信処理を行う Callback 関数を使用
#   し、受信メッセージの表示、２次メッセージの出力を自動で行う。
#   Callback 関数を使用する場合、入力促進用メッセージに乱れが生じる事に留意
#   すること。
#
# ==============================================================================

require	"fiddle/import"

module TDS
  extend Fiddle::Importer
  dlload File.expand_path("TDS.dll")
  extern "int	__TDSCommOpen		(int,char*,char*,int,int,int,int,int,int)"
  extern "int	__TDSCommOpen0		(int,char*,char*)"
  extern "int	__TDSCommClose		(int,int)"
  extern "int	__TDSCommRecv		(int,int,int*,int*,unsigned int*,void*,int,TDSECSHead*)"
  extern "int	__TDSCommSend		(int,int,int ,int ,unsigned int ,void*,int,TDSECSHead*)"
  extern "int	__TDSCommSendError	(int,int,int ,int ,TDSECSHead*,TDSECSHead*,void*)"
  extern "int	__TDSCommSendComment	(int,int,char*,char*,int)"
  extern "int	__TDSCommStatus		(int,int)"
  extern "int	__TDSCommSelectStatus	(int,int,int)"

  extern "int	__TDSErrorValue		(int)"
  extern "int	__TDSErrorPosition	(int)"
  extern "void	__TDSErrorStatus		(int,int*,int*)"
  extern "int	__TDSMssgErrorValue	(int)"
  extern "int	__TDSMssgErrorPosition	(int)"
  extern "void	__TDSMssgErrorStatus	(int,int*,int*)"

  extern "int	__TDSMssgInit		(int,void*,int,int)"
  extern "int	__TDSMssgEnd		(int,int,void*)"
  extern "int	__TDSMssgBuild		(int,int,void*,int,int,void*)"
  extern "int	__TDSMssgBuildL		(int,int,void*,char*)"
  extern "int	__TDSMssgFind		(int,void*,int,int,TDSECSHead*,char*)"
  extern "int	__TDSMssgExit		(int,int,void*)"
  extern "int	__TDSMssgNext		(int,int,void*,int*,int*,int*,void*,int)"
  extern "int	__TDSMssgNextL		(int,int,void*,int*,int*,char*,int)"

  extern "int	__TDSMDMssgInitialize	(int,int,char*)"
  extern "void	__TDSMDMssgTerminate	(int,int)"
  extern "int	__TDSMDMssgInit		(int,int,void*,int,char*)"
  extern "int	__TDSMDMssgEnd		(int,int,void*)"
  extern "int	__TDSMDMssgBuild	(int,int,void*,char*,int,void*)"
  extern "int	__TDSMDMssgFind		(int,int,void*,int,int,char*)"
  extern "int	__TDSMDMssgExit		(int,int,void*)"
  extern "int	__TDSMDMssgNext		(int,int,void*,char*,int,void*)"
  extern "int	__TDSMDMssgNextEx	(int,int,void*,char*,int,void*,int*,int*)"
  extern "int	__TDSMDMssgNextL	(int,int,void*,int*,int*,char*,int)"
  extern "int	__TDSMDMssgAutoRes	(int,int,TDSECSHead*,void*,int,int,char*,char*,int*)"

  extern "int	__TDSUDrvOpen		(int,char*,char*,unsigned int)"
  extern "int	__TDSUDrvClose		(int,int)"
  extern "int	__TDSUDrvRecv		(int,int,int*,int*,unsigned int*,void*,int,TDSECSHead*)"
  extern "int	__TDSUDrvSend		(int,int,int ,int ,unsigned int ,void*,int,TDSECSHead*)"
  extern "int	__TDSUDrvSendError	(int,int,int ,int ,TDSECSHead*,TDSECSHead*,void*)"
  extern "int	__TDSUDrvSendComment	(int,int,char*,char*,int)"
  extern "int	__TDSUDrvStatus		(int,int)"
  extern "int	__TDSUDrvSelectStatus	(int,int,int)"

  VSI1 =struct(	["char			sc",])
  VUI1 =struct(	["unsigned char		uc",])
  VSI2 =struct(	["short			ss",])
  VUI2 =struct(	["unsigned short	us",])
  VSI4 =struct(	["int			si",])
  VUI4 =struct(	["unsigned int		ui",])
  VSI8 =struct(	["long long		sl",])
  VUI8 =struct(	["unsigned long long	ul",])
  SHead=struct(	["unsigned short	did"	\
		,"unsigned char		scd"	\
		,"unsigned char		fcd"	\
		,"unsigned char		ptp"	\
		,"unsigned char		stp"	\
		,"unsigned short	sid"	\
		,"unsigned short	xid",])
end



# ------------------------------------------------------------------------------

EBADF		=9
EBUSY		=16
ENOMEM		=12
ENODEV		=19
E2BIG		=7

E_NOTCONNECT	=999
E_DESELECT	=998
E_REJECT	=997
E_SELECT	=992
E_CONNECT	=991
E_RETRYOVER	=989
E_T8TIMEDOUT	=988
E_T7TIMEDOUT	=987
E_T6TIMEDOUT	=986
E_T5TIMEDOUT	=985
E_T4TIMEDOUT	=984
E_T3TIMEDOUT	=983
E_T2TIMEDOUT	=982
E_T1TIMEDOUT	=981
E_ILLBLOCK	=980
E_NODATA	=951


# ------------------------------------------------------------------------------

SECS_MODE	=1		# SECS/HSMS mode
				# 0    : SECS-1
				# 1    : HSMS
                                # (Note) See comments above
				# (注) 上部コメント参照
FUNC_TYPE	=1		# Type of function used
				# 通信に使用する関数の種別
				# 0    : _TDSCommXxxxx
				# 1    : _TDSUDrvXxxxx
UDRV_MASK	=0x8383ffff	# Mask value of UDrvOpen()
				# 0          : Set =0x49
				# 0x8383ffff : All event

USE_CALLBACK	=1		# Use of Callback function
				# 0    : Not use
				# 1    : Use

MSSG_USE_FILE	=0x80		# Message definition file
				# 0x00 : Not use
				# 0x80 : Use to display item names
				#	  使用して項目名表示する
MSSG_DISP_TYPE	=0x20		# SECS Message display format
				# 0x00 : TDS Format
				# 0x20 : SML Format
MSSG_USE_NEXTL	=1		# Use MssgNextL() or not
				# 0    : Not use
				# 1    : Use

PARAMFILE	="Sample.ini"


# ------------------------------------------------------------------------------

$Fd		=0		# Communication identifier
$Md		=0		# Message analysis identifier
$OType		=0		# Operation type  (0:Host  1:Equipment)
$Break		=0		# End instruction to thread
				# スレッド終了指示

$cnt249		=0
$cnt250		=0
$cnt611		=0

$Cs1		=Mutex.new


# ==============================================================================
# Common functions =============================================================


# ------------------------------------------------------------------------------
# Display SECS messages on standard output -------------------------------------
# SECS メッセージを標準出力に表示する。 ----------------------------------------

def DispSECSMssg(		#
		tp,		# i  : Message type
				#	=0 : Transmission result
				#	 1 : Received message
				#	 2 : Send message
		hd,		# i  : SECS Message Header
		did,		# i  : SECS Message Device ID
		sf,		# i  : SECS Message SF-Code
		xid,		# i  : SECS Message Transaction ID
		msg,		# i  : SECS Message strage area
				#	     (Header not included)
		len)		# i  : SECS Message byte length

  ctp		=["SRES","RECV","SEND"]
  form		=TDS::VUI4.malloc
  sz		=TDS::VSI4.malloc
  noi		=TDS::VSI4.malloc
  str		="\0"*512
  mname		="\0"*32
  item		="\0"*64

  md=0;		dp=MSSG_USE_FILE|MSSG_DISP_TYPE
  rbit=" ";	if (did&0x8000)!=0 then	rbit="R"	end
  wbit=" ";	if (sf &0x8000)!=0 then	wbit="W"	end
  sfcode=sprintf("S%dF%d",(sf&0x7f00)/0x0100,sf&0xff)
  printf("[%s]  Dev=0x%04x  %-8s  %s%s  XId=0x%04x  Len=%4d\n"	\
		,ctp[tp],did&0x7fff,sfcode,rbit,wbit,xid,len)

  if (MSSG_USE_FILE&0x80)!=0 then	fm =0x8000	# Use message definition file
  else					fm =0x0000	# Not use
  end

  if len>0   then
    if tp==1 then	dp|=0x3000;	fm|=0x3000	# In case of receiving
    else		dp|=0x2000;	fm|=0x2000	# In case of sending
    end

    md=TDS.__TDSMssgFind(fm,msg,len,$Fd,hd,mname)
    if md	> 0	then
      ln=mname.index("\0")
      if ln>0 then	printf("[%s]\n",mname[0,ln])	# Message name
      end

      la=0
      while true do
	if MSSG_USE_NEXTL==0 then	# Get item value
	  rtn=TDS.__TDSMssgNext(md,0,msg,form,sz,noi,item,64)
	  if rtn< 0 then		break		end
	  printf("%03o:%d*%2d:",form.ui,sz.si,noi.si)
	  case form.ui			# Display of field value
					# The second and subsequent numbers are omitted
					# 数値の２番目以降は省略
	  when 0o000 then printf("L[%d]\n"	 ,noi.si)
	  when 0o010 then printf("B[%d]=0x%02x\n",noi.si,item.unpack("C1")[0])
	  when 0o011 then printf("T[%d]=%d\n"	 ,noi.si,item.unpack("C1")[0])
	  when 0o020 then printf("A[%d]=%s\n"	 ,noi.si,item.unpack("Z*")[0])
	  when 0o021 then printf("J[%d]=%s\n"	 ,noi.si,item.unpack("Z*")[0])
	  when 0o022 then printf("K[%d]=%s\n"	 ,noi.si,item.unpack("Z*")[0])
	  when 0o030 then printf("I8[%d]=%d\n"	 ,noi.si,item.unpack("q1")[0])
	  when 0o031 then printf("I1[%d]=%d\n"	 ,noi.si,item.unpack("c1")[0])
	  when 0o032 then printf("I2[%d]=%d\n"	 ,noi.si,item.unpack("s1")[0])
	  when 0o034 then printf("I4[%d]=%d\n"	 ,noi.si,item.unpack("l1")[0])
	  when 0o040 then printf("F8[%d]=%f\n"	 ,noi.si,item.unpack("d1")[0])
	  when 0o044 then printf("F4[%d]=%f\n"	 ,noi.si,item.unpack("f1")[0])
	  when 0o050 then printf("U8[%d]=%u\n"	 ,noi.si,item.unpack("Q1")[0])
	  when 0o051 then printf("U1[%d]=%u\n"	 ,noi.si,item.unpack("C1")[0])
	  when 0o052 then printf("U2[%d]=%u\n"	 ,noi.si,item.unpack("S1")[0])
	  when 0o054 then printf("U4[%d]=%u\n"	 ,noi.si,item.unpack("L1")[0])
	  else		  printf("\n")
	  end

	else						# Get in list format
							# リスト表示形式で取得
	  rtn=TDS.__TDSMssgNextL(md,dp,msg,form,noi,str,512)
	  if  rtn	< 0	then	break		end
	  if (dp&0x70)	> 0x10	then			# In case of SML
	    i=la
	    while i>rtn do
	      printf("%*s>\n"	,i*2,"")		# Display '>' 
	      i-=1
	    end
	  end
	  la=rtn					# Save current hierarchy
							# 現在の階層を保存
	  printf("  %s\n"	,str.unpack("Z*")[0])	# Display acquired field value
							# 取得した項目値を表示
	  if (dp&0x70)>0x10 && form.ui==000 && noi.si==0 then
	    printf("%*s>\n"	,(la+1)*2,"")		# '>' Processing for L0
	  end						# L0 の場合の '>' 処理
	end
      end

      if MSSG_USE_NEXTL	!=0	then
	if (dp&0x70)	> 0x10	then
	   i=la
	   while i>0 do
	     printf("%*s>\n"	,i*2,"")		# Show remaining '>'
	     i-=1					# 残りの '>' 表示
	   end
	end
      end
      TDS.__TDSMssgExit(md,0,msg)
    end
  end
end



# ------------------------------------------------------------------------------
# Display sent and received data -----------------------------------------------

def	DispData(
		tp,		# i  : Message type
				#	=0 : Transmission result
				#	 1 : Received message
				#	 2 : Send message
		hd,		# i  : SECS Message Header
		did,		# i  : SECS Message Device ID
		sf,		# i  : SECS Message SF-Code
		xid,		# i  : SECS Message Transaction ID
		msg,		# i  : SECS Message Body
		len,		# i  : Byte length of 'msg'
		rtn)		# i  : I/O return value

  ok=0

  $Cs1.lock
  if rtn<0 then
    ok=1
    if (rtn<(-E_NOTCONNECT) || (-E_ILLBLOCK)<rtn) && rtn!=(-E_NODATA) then
      printf("ERROR  [%d]"			,rtn)
      if    rtn==(-ENODEV) then
	printf(" : No such device ID\n");	len=ok=0
      elsif rtn==(-E2BIG ) then
	printf(" : Data size to large\n");	len=ok=0
      else			printf("\n")
      end

    else
      if FUNC_TYPE==0 then	stat=TDS.__TDSCommStatus($Fd,0)
      else			stat=TDS.__TDSUDrvStatus($Fd,0)
      end
      printf("STATUS = %d,%d : "		,-rtn,stat)
      case -rtn
      when E_NODATA	then	printf("No data\n")
      when E_ILLBLOCK	then	printf("Illegal block#\n")
      when E_T1TIMEDOUT	then	printf("T1 Timeout occur\n")
      when E_T2TIMEDOUT	then	printf("T2 Timeout occur\n")
      when E_T3TIMEDOUT	then	printf("T3 Timeout occur\n")
      when E_T4TIMEDOUT	then	printf("T4 Timeout occur\n")
      when E_T5TIMEDOUT	then	printf("T5 Timeout occur\n")
      when E_T6TIMEDOUT	then	printf("T6 Timeout occur\n")
      when E_T7TIMEDOUT	then	printf("T7 Timeout occur\n")
      when E_T8TIMEDOUT	then	printf("T8 Timeout occur\n")
      when E_RETRYOVER	then	printf("Retry over\n")
      when E_CONNECT	then	printf("Connected\n")
      when E_SELECT	then	printf("Selected   (0x%04x)\n" ,did)
      when E_REJECT	then	printf("Rejected XId=0x%04x\n" ,xid)
      when E_DESELECT	then	printf("Deselected (0x%04x)\n" ,did)
      when E_NOTCONNECT	then	printf("Not connected\n")
      else			printf("\n")
      end
    end
  end

  if ok==0 then	DispSECSMssg(tp,hd,did,sf,xid,msg,len)	end
  $Cs1.unlock
end



# ==============================================================================
# S1F1 message construction and sending ----------------------------------------

def SendS1F1()
  hd =TDS::SHead.malloc
  msg="\0"*16
  sf =0x8101
  len=0

  if FUNC_TYPE==0 then	rtn=TDS.__TDSCommSend($Fd,0x0000,0,sf,0,msg,len,hd)
  else			rtn=TDS.__TDSUDrvSend($Fd,0x0000,0,sf,0,msg,len,hd)
  end
  DispData(2,hd,0x00,sf,rtn,msg,len,rtn)

  return rtn
end


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

def SendS1F2H(
		did,
		xid)
  hd =TDS::SHead.malloc
  msg="\0"*256
  sf =0x0102
  
  if (MSSG_USE_FILE&0x80)==0 then		# Do not use message definition
    md =	TDS.__TDSMssgInit(	0,msg, 256,$Fd)			# S1F2
    		TDS.__TDSMssgBuild(  md,0,msg,0x00,  0,0)		# L0
    len=	TDS.__TDSMssgEnd(    md,0,msg)

  else						# Use message definition
		TDS.__TDSMDMssgInit($Md,0,msg, 256,"S1F2_H")
    len=	TDS.__TDSMDMssgEnd( $Md,0,msg)
  end

  if len<0	then rtn=len
  else
    if FUNC_TYPE==0 then rtn=TDS.__TDSCommSend($Fd,0x0000,did,sf,xid,msg,len,hd)
    else		 rtn=TDS.__TDSUDrvSend($Fd,0x0000,did,sf,xid,msg,len,hd)
    end
  end
  DispData(2,hd,did,sf,rtn,msg,len,rtn)

  return rtn
end


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

def SendS1F2E(
		did,
		xid)
  hd =TDS::SHead.malloc
  msg="\0"*256
  sf =0x0102
  
  if (MSSG_USE_FILE&0x80)==0 then		# Do not use message definition
    md =	TDS.__TDSMssgInit(	0,msg,  256,$Fd)		# S1F2
    		TDS.__TDSMssgBuild(  md,0,msg,0o000,  2,0)		# L2
		TDS.__TDSMssgBuild(  md,0,msg,0o020,  6,"EQUIP1")	#  MDLN
		TDS.__TDSMssgBuild(  md,0,msg,0o020,  6,"01.000")	#  SOFTREV
    len=	TDS.__TDSMssgEnd(    md,0,msg)

  else						# Use message definition
		TDS.__TDSMDMssgInit($Md,0,msg,  256,"S1F2_E")
    len=	TDS.__TDSMDMssgEnd( $Md,0,msg)
  end

  if len<0	then rtn=len
  else
    if FUNC_TYPE==0 then rtn=TDS.__TDSCommSend($Fd,0x0000,did,sf,xid,msg,len,hd)
    else		 rtn=TDS.__TDSUDrvSend($Fd,0x0000,did,sf,xid,msg,len,hd)
    end
  end
  DispData(2,hd,did,sf,rtn,msg,len,rtn)

  return rtn
end


# ==============================================================================
# S2F49 message construction and sending ---------------------------------------

def SendS2F49()
  hd =TDS::SHead.malloc
  vu1=TDS::VUI1.malloc
  vu4=TDS::VUI4.malloc
  msg="\0"*1024
  sf =0x8200+49

  $cnt249+=1
  str=sprintf("LOTID (%4d)",$cnt249)
  no1=($cnt249%2)+1;	no2=($cnt249%10)+1
  
  if (MSSG_USE_FILE&0x80)==0 then		# Do not use message definition
    md=		TDS.__TDSMssgInit(	 0,msg, 1024,$Fd)		# S2F49
  		TDS.__TDSMssgBuild(   md,0,msg,0o000,  3,0)		# L3
    vu1.uc=0;	TDS.__TDSMssgBuild(   md,0,msg,0o010,  1,vu1)		#  DATAIDB
  		TDS.__TDSMssgBuild(   md,0,msg,0o020,  0,"LOAD")	#  RCMD
  		TDS.__TDSMssgBuild(   md,0,msg,0o000,  4,0)		#  L4
    vu1.uc=1;	TDS.__TDSMssgBuild(   md,0,msg,0o010,  1,vu1)		#   STID
    vu1.uc=0;	TDS.__TDSMssgBuild(   md,0,msg,0o010,  1,vu1)		#   MTKD
  		TDS.__TDSMssgBuild(   md,0,msg,0o020, 20,str)		#   LOTID
		TDS.__TDSMssgBuild(   md,0,msg,0o000,no1,0)		#   L[no1]
    i=0
    while i<no1 do
		TDS.__TDSMssgBuild(   md,0,msg,0o000,no2,0)		#    L[no2]
      j=0
      while j<no2 do
		TDS.__TDSMssgBuild(   md,0,msg,0o000,  2,0)		#     L[2]
	str=sprintf("WAFER(%04d-%d-%02d)",$cnt249	,i+1,j+1)
  		TDS.__TDSMssgBuild(   md,0,msg,0o020, 20,str)		#      WAFERID
	str=sprintf("PPID (%04d-%d-%02d)",$cnt249	,i+1,j+1)
  		TDS.__TDSMssgBuild(   md,0,msg,0o020, 16,str)		#      PPID
	j+=1
      end
      i+=1
    end
    len=	TDS.__TDSMssgEnd(     md,0,msg)

  else						# Use message definition
		TDS.__TDSMDMssgInit( $Md,0,msg, 1024,"S2F49")
  		TDS.__TDSMDMssgBuild($Md,0,msg,"LOTID",0,str)		#   LOTID
    vu4.ui=no1;	TDS.__TDSMDMssgBuild($Md,0,msg,"NOI1" ,1,vu4)		#   L[no1]
    i=0
    while i<no1 do
      itm	=sprintf("NOI2:%d"			,i+1)
      vu4.ui=no2
		TDS.__TDSMDMssgBuild($Md,0,msg,itm    ,1,vu4)		#    L[no2]
      j=0
      while j<no2 do
	itm=sprintf("WAFERID:%d:%d"			,i+1,j+1)
	str=sprintf("WAFER[%04d-%d-%02d]",$cnt249	,i+1,j+1)
  		TDS.__TDSMDMssgBuild($Md,0,msg,itm    ,0,str)		#     WAFERID
	itm=sprintf("PPID:%d:%d"			,i+1,j+1)
	str=sprintf("PPID [%04d-%d-%02d]",$cnt249	,i+1,j+1)
  		TDS.__TDSMDMssgBuild($Md,0,msg,itm    ,0,str)		#     PPID
	j+=1
      end
      i+=1
    end
    len=	TDS.__TDSMDMssgEnd(  $Md,0,msg)
  end

  if len<0	then rtn=len
  else
    if FUNC_TYPE==0 then rtn=TDS.__TDSCommSend($Fd,0x0000,0,sf,  0,msg,len,hd)
    else		 rtn=TDS.__TDSUDrvSend($Fd,0x0000,0,sf,  0,msg,len,hd)
    end
  end
  DispData(2,hd,0x00,sf,rtn,msg,len,rtn)

  return rtn
end


# ------------------------------------------------------------------------------
# S2F50 message construction and sending ---------------------------------------

def SendS2F50(
		did,
		xid)
  hd =TDS::SHead.malloc
  vu1=TDS::VUI1.malloc
  msg="\0"*256
  sf =0x0200+50
  
  str=sprintf("LOTID (%4d)"	,$cnt250+=1)

  if (MSSG_USE_FILE&0x80)==0 then		# Do not use message definition
    md=		TDS.__TDSMssgInit(	 0,msg,  256,$Fd)		# S2F50
		TDS.__TDSMssgBuild(   md,0,msg,0o000,  2,0)		# L2
    vu1.uc=0;	TDS.__TDSMssgBuild(   md,0,msg,0o010,  1,vu1)		#  HCACK
		TDS.__TDSMssgBuild(   md,0,msg,0o000,  2,0)		#  L2
  		TDS.__TDSMssgBuild(   md,0,msg,0o020,  5,"PODID")	#   PODID
  		TDS.__TDSMssgBuild(   md,0,msg,0o020, 20,str)		#   LOTID
    len=	TDS.__TDSMssgEnd(     md,0,msg)

  else						# Use message definition
		TDS.__TDSMDMssgInit( $Md,0,msg,  256,"S2F50")
		TDS.__TDSMDMssgBuild($Md,0,msg,"LOTID",  0,str)		#   LOTID
    len=	TDS.__TDSMDMssgEnd(  $Md,0,msg)
  end

  if len<0 	then rtn=len
  else
    if FUNC_TYPE==0 then rtn=TDS.__TDSCommSend($Fd,0x0000,did,sf,xid,msg,len,hd)
    else		 rtn=TDS.__TDSUDrvSend($Fd,0x0000,did,sf,xid,msg,len,hd)
    end
  end
  DispData(2,hd,did,sf,rtn,msg,len,rtn)

  return rtn
end


# ==============================================================================
# S6F11 message construction and sending ---------------------------------------

def SendS6F11()
  hd =TDS::SHead.malloc
  vu2=TDS::VUI2.malloc
  msg="\0"*256
  sf =0x8600+11

  $cnt611+=1;	if $cnt611==65536 then	$cnt611=0	end

  if (MSSG_USE_FILE&0x80)==0 then		# Do not use message definition
    md=		TDS.__TDSMssgInit(	 0,msg,  256,$Fd)		# S6F11
  		TDS.__TDSMssgBuild(   md,0,msg,0o000,  3,0)		# L3
    vu2.us=$cnt611
		TDS.__TDSMssgBuild(   md,0,msg,0o052,  1,vu2)		#  DATAID
    vu2.us=8;	TDS.__TDSMssgBuild(   md,0,msg,0o052,  1,vu2)		#  CEID
  		TDS.__TDSMssgBuild(   md,0,msg,0o000,  3,0)		#  L3
  		TDS.__TDSMssgBuild(   md,0,msg,0o020, 16,"DATA1")	#   DATA1
  		TDS.__TDSMssgBuild(   md,0,msg,0o020, 16,"DATA2")	#   DATA2
  		TDS.__TDSMssgBuild(   md,0,msg,0o020, 14,"YYYYMMDDhhmmss")
			# @TIME Actually I set current time, but omitted.
			# @TIME (本当は現在時刻を設定するのだが、省略。
    len=	TDS.__TDSMssgEnd(     md,0,msg)

  else						# Use message definition
		TDS.__TDSMDMssgInit( $Md,0,msg,  256,"S6F11_0")
    vu2.us=$cnt611
		TDS.__TDSMDMssgBuild($Md,0,msg,"DATAID",1,vu2);		#  DATAID
    len=	TDS.__TDSMDMssgEnd(  $Md,0,msg)
  end

  if len<0	then rtn=len
  else
    if FUNC_TYPE==0 then rtn=TDS.__TDSCommSend($Fd,0x0000,0,sf,  0,msg,len,hd)
    else		 rtn=TDS.__TDSUDrvSend($Fd,0x0000,0,sf,  0,msg,len,hd)
    end
  end
  DispData(2,hd,0x00,sf,rtn,msg,len,rtn)

  return rtn
end



# ------------------------------------------------------------------------------
# S6F12 message construction and sending ---------------------------------------

def SendS6F12(
		did,
		xid)
  hd =TDS::SHead.malloc
  vu1=TDS::VUI1.malloc
  msg="\0"*256
  sf =0x0600+12

  if (MSSG_USE_FILE&0x80)==0 then		# Do not use message definition
    md=		TDS.__TDSMssgInit(	 0,msg,  256,$Fd)		# S6F12
    vu1.uc=0;	TDS.__TDSMssgBuild(   md,0,msg,0o010,  1,vu1)		# ACKC
    len=	TDS.__TDSMssgEnd(     md,0,msg)

  else						# Use message definition
		TDS.__TDSMDMssgInit( $Md,0,msg,  256,"S6F12")
    len=	TDS.__TDSMDMssgEnd(  $Md,0,msg)
  end

  if len<0	then rtn=len
  else
    if FUNC_TYPE==0 then rtn=TDS.__TDSCommSend($Fd,0x0000,did,sf,xid,msg,len,hd)
    else		 rtn=TDS.__TDSUDrvSend($Fd,0x0000,did,sf,xid,msg,len,hd)
    end
  end
  DispData(2,hd,did,sf,rtn,msg,len,rtn)

  return rtn
end



# ==============================================================================
# Callback function ============================================================

def CBRecvProc(
		req,		# i  : Request code to library
		rtn,		# i  : Return code from library
		did,		# i  : SECS Message Device ID
		xsf,		# i  : SECS Message SF-Code
		xid,		# i  : SECS Message Transaction ID
		xhd,		# i  : SECS Message Header
		xmsg)		# i  : SECS Message Body
  hd =TDS::SHead.malloc
  vu4=TDS::VUI4.malloc
  rname="\0"*32
  sname="\0"*32
  msg="\0"*1024

  DispData(1,xhd,did,xsf,xid,xmsg,rtn,rtn)

  if req==0 && rtn>=0 then
    if (MSSG_USE_FILE&0x80)==0 then
		# 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    xsf==0x8101 && $OType==0	then SendS1F2H(did,xid)
      elsif xsf==0x8101 && $OType==1	then SendS1F2E(did,xid)
      elsif xsf==0x8231			then SendS2F50(did,xid)
      elsif xsf==0x860b			then SendS6F12(did,xid)
      end

    elsif (xsf&0x8000)!=0 then
		# When using a message definition file, this sample uses the
		# automatic reply function.
		# メッセージ定義ファイルを使用する場合は、本サンプルでは、自動
		# 返信機能を使用する。
      msg=xmsg.clone
      len=TDS.__TDSMDMssgAutoRes($Md,0,xhd,msg,rtn,1024,rname,sname,vu4)
      if  len>=0 then
	sf=vu4.ui
	printf("RECV %s ..  Auto respond %s [S%dF%d]\n" \
		,rname.unpack("Z*")[0],sname.unpack("Z*")[0],sf/0x100,sf&0xff)
	if FUNC_TYPE==0	then
	  rtn=TDS.__TDSCommSend($Fd,0x0000,did,sf,xid,msg,len,hd)
	else
	  rtn=TDS.__TDSUDrvSend($Fd,0x0000,did,sf,xid,msg,len,hd)
	end
	DispData(2,hd,did,sf,xid,msg,len,rtn)
      else
	if len!=(-930) && len!=(-931) then
	  printf("RECV Auto response error (%d)\n",len)
	end
      end
    end
  end

  return 0
end



# ------------------------------------------------------------------------------

def RecvProcThread()
  hd =TDS::SHead.malloc
  did=TDS::VUI4.malloc
  xid=TDS::VUI4.malloc
  sf =TDS::VUI4.malloc
  msg="\0"*1024

  while $Break==0 do
    req=0
    if FUNC_TYPE==0 then
      rtn=TDS.__TDSCommRecv($Fd,0,did,sf,xid,msg,1024,hd)
    else
      rtn=TDS.__TDSUDrvRecv($Fd,0,did,sf,xid,msg,1024,hd)
    end
    if rtn==(-951) then
      sleep(0.1)
    else
      if (-1000)<rtn && rtn<(-959) then	req=(-rtn)-900	end
      CBRecvProc(req,rtn,did.ui,sf.ui,xid.ui,hd,msg)
    end
  end
end



# ==============================================================================
# Host side process ------------------------------------------------------------

def Host()
  hd =TDS::SHead.malloc
  did=TDS::VUI4.malloc
  xid=TDS::VUI4.malloc
  sf =TDS::VUI4.malloc
  msg="\0"*1024

  catch :Exit do
  $OType=0;		$Break=0
  if FUNC_TYPE==0 then	$Fd=TDS.__TDSCommOpen(0x02,PARAMFILE,"HOST",0,0,0,0,0,0)
  else			$Fd=TDS.__TDSUDrvOpen(0x02,PARAMFILE,"HOST",UDRV_MASK)
  end
  if $Fd					< 0 then	throw :Exit end
  printf("(H) Opened (%d)\n",$Fd)
  if (MSSG_USE_FILE&0x80)!=0 then
    $Md=TDS.__TDSMDMssgInitialize(0x4000,$Fd,"")
  end

  if USE_CALLBACK!=0 then
    th=Thread.start{
      RecvProcThread()
    }
  end

  while true do
    rtn=0
    if USE_CALLBACK==0 then	printf("Req (0:Exit 1:Recv 2:Send) : ")
    else			printf("Req (0:Exit 2:Send) : ")
    end
    req=$stdin.gets.to_i
    if    req==0 then						break

    elsif req==1 then
      if FUNC_TYPE==0 then
	rtn=TDS.__TDSCommRecv($Fd,0,did,sf,xid,msg,1024,hd)
      else
	rtn=TDS.__TDSUDrvRecv($Fd,0,did,sf,xid,msg,1024,hd)
      end
      if rtn >=0 then
	dids=did.ui;	xids=xid.ui
      end
      DispData(1,hd,did.ui,sf.ui,xid.ui,msg,rtn,rtn)

    elsif req==2 then
      if USE_CALLBACK==0 then	printf("Message(1:S1F1 2:S2F49  6:S1F2 7:S6F12) : ")
      else			printf("Message(1:S1F1 2:S2F49) : ")
      end
      mno=$stdin.gets.to_i
      case mno
      when 1 then	rtn=SendS1F1( )
      when 2 then	rtn=SendS2F49()
      when 6 then	rtn=SendS1F2H(dids,xids)
      when 7 then	rtn=SendS6F12(dids,xids)
      end
    end
    if rtn<(-999) || ((-900)<rtn && rtn<0) then
      printf("(H) I/O Error (%d)\n"	,rtn)
    end
  end

  end						# :Exit
  $Break=1
  if $Fd>0 then
    if FUNC_TYPE==0 then TDS.__TDSCommClose($Fd,0)
    else		 TDS.__TDSUDrvClose($Fd,0)
    end
  else
    printf("(H) Error (%d)\n"	,$Fd)
  end
  if $Md>0 then		 TDS.__TDSMDMssgTerminate($Md,0)	end
end



# ==============================================================================
# Equipment side process -------------------------------------------------------

def Equip()
  hd =TDS::SHead.malloc
  did=TDS::VUI4.malloc
  xid=TDS::VUI4.malloc
  sf =TDS::VUI4.malloc
  msg="\0"*1024

  catch :Exit do
  $OType=1;		$Break=0
  if FUNC_TYPE==0 then	$Fd=TDS.__TDSCommOpen(0x02,PARAMFILE,"EQUIP",0,0,0,0,0,0)
  else			$Fd=TDS.__TDSUDrvOpen(0x02,PARAMFILE,"EQUIP",UDRV_MASK)
  end
  if $Fd					< 0 then	throw :Exit end
  printf("(E) Opened (%d)\n",$Fd)
  if (MSSG_USE_FILE&0x80)!=0 then
    $Md=TDS.__TDSMDMssgInitialize(0x4000,$Fd,"")
  end

  if USE_CALLBACK!=0 then
    th=Thread.start{
      RecvProcThread()
    }
  end

  if SECS_MODE!=0 && FUNC_TYPE==0 then	# In case of HSMS and use TDSCommXxxxx()
					# HSMS で TDSCommXxxx() 使用の場合
    if TDS.__TDSCommSend($Fd,0x0100,0,0,0,msg,0,hd)< 0 then	throw :Exit end
    printf("(E) Connected\n")
    if TDS.__TDSCommSend($Fd,0x0200,0,0,0,msg,0,hd)< 0 then	throw :Exit end
    printf("(E) Selected\n")
  end

  while true do
    rtn=0
    if USE_CALLBACK==0 then	printf("Req (0:Exit 1:Recv 2:Send) : ")
    else			printf("Req (0:Exit 2:Send) : ")
    end
    req=$stdin.gets.to_i
    if    req==0 then						break

    elsif req==1 then
      if FUNC_TYPE==0 then
	rtn=TDS.__TDSCommRecv($Fd,0,did,sf,xid,msg,1024,hd)
      else
	rtn=TDS.__TDSUDrvRecv($Fd,0,did,sf,xid,msg,1024,hd)
      end
      if rtn >=0 then
	dids=did.ui;	xids=xid.ui
      end
      DispData(1,hd,did.ui,sf.ui,xid.ui,msg,rtn,rtn)

    elsif req==2 then
      if USE_CALLBACK==0 then	printf("Message(1:S1F1 2:S6F11  6:S1F2 7:S2F50) : ")
      else			printf("Message(1:S1F1 2:S6F11) : ")
      end
      mno=$stdin.gets.to_i
      case mno
      when 1 then	rtn=SendS1F1( )
      when 2 then	rtn=SendS6F11()
      when 6 then	rtn=SendS1F2E(dids,xids)
      when 7 then	rtn=SendS2F50(dids,xids)
      end
    end
    if rtn<(-999) || ((-900)<rtn && rtn<0) then
      printf("(E) I/O Error (%d)\n"	,rtn)
    end
  end

  if   SECS_MODE!=0 then		# In case of HSMS, Shutdown process
					# HSMS 接続の場合、切断処理
    if FUNC_TYPE==0 then	rtn=TDS.__TDSCommStatus($Fd,0)
    else			rtn=TDS.__TDSUDrvStatus($Fd,0)
    end
    if rtn	==3 then
    # Deselect request is not performed. (Of course you may go. However SEMI
    # claims that HSMS-SS does not perform Deselect request.)
    # Deselect request は行わない。 (もちろん行ってもよい。SEMI では HSMS-SS に
    # おいて、Deselect request は行わない、としている。)
    # if FUNC_TYPE==0 then rtn=TDS.__TDSCommSend($Fd,0x0800,0,0,0,msg,0,hd)
    # else		   rtn=TDS.__TDSUDrvSend($Fd,0x0800,0,0,0,msg,0,hd)
    # end
    # if rtn					< 0 then	throw :Exit end
    # printf("(E) Deselected\n")
      if FUNC_TYPE==0 then rtn=TDS.__TDSCommSend($Fd,0x0900,0,0,0,msg,0,hd)
      else		   rtn=TDS.__TDSUDrvSend($Fd,0x0900,0,0,0,msg,0,hd)
      end
      if rtn					< 0 then	throw :Exit end
      printf("(E) Separated\n")
    end
  end

  end						# :Exit
  $Break=1
  if $Fd>0 then
    if FUNC_TYPE==0 then TDS.__TDSCommClose($Fd,0)
    else		 TDS.__TDSUDrvClose($Fd,0)
    end
  else
    printf("(E) Error (%d)\n"	,$Fd)
  end
  if $Md>0 then		 TDS.__TDSMDMssgTerminate($Md,0)	end
end



# ==============================================================================
# Main process -----------------------------------------------------------------

if ARGV.size<1 then
  print "Usage: ruby #{$0} {h|e}\n"
else
  if ARGV[0]=="h" then	Host( )
  else			Equip()
  end
end
