# coding: Shift_JIS

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


# ==============================================================================
#
# SimpleIo : テスト 及び サンプル・プログラム
#
#  ＡＰ内でメッセージを構築し、単純に SECS メッセージの送受信を繰り返す、最も
#  単純な使用例。
#
#  以下の流れを実行する。
#  (注) 本ＡＰは HSMS 通信においては、設備側を Active 接続、ホスト側を Passive
#  接続と仮定している。
#
#   ホスト側（Simple h） |          | 設備側（Simple e）
#   --------------------------------------------------------
#            接続 を待つ |          |
#                        |          |
#       接続を受け付ける |<---------| Connect
#    Select を受け付ける |<---------| Select 要求
#                        |          |
#                        |          |
#         +--> S1F1 受信 | <------- | S1F1 送信 <--+
#         |    S1F2 送信 | -------> | S1F2 受信    | 繰り返す
#         |    ２秒待つ  |          |              | CTRL-C で終了
#         |    S1F1 送信 | -------> | S1F1 受信    | 送信間隔は WAIT1 定数で
#         +--- S1F2 受信 | <------- | S1F2 送信    | 決める
#                        |          | ２秒待つ  ---+
#                        |          |
#   Deselectを受け付ける |<---------| Deselect 要求  <省略>
#   Separateを受け付ける |<---------| Separate 要求  <省略>
#                        |          |
#                   切断 |          | 切断                                 
#                        |          |
#
#
#
# 起動方法
#
#   ruby SimpleIo.rb {h|e}
#   ~~~~~~~~~~~~~~~~~~~~~~
#   h    : Sample.ini の [HOST]  セクションを参照し動作を決定する
#   e    : Sample.ini の [EQUIP]      :              :
#
#
#   通常、Simple h 及び Simple e の両方を、同一マシン、もしくは異なる
#   マシン上で動作させて、相互通信を行う。
#   CTRL-C の入力で終了する。
#
#   本サンプルは、異常時処理を省略している。
#
# ==============================================================================

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	__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)"

  VSI4 =struct(	["int			si",])
  VUI4 =struct(	["unsigned int		ui",])
  SHead=struct(	["unsigned short	did"	\
		,"unsigned char		scd"	\
		,"unsigned char		fcd"	\
		,"unsigned char		ptp"	\
		,"unsigned char		stp"	\
		,"unsigned short	sid"	\
		,"unsigned short	xid",])
end



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

BADF		=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



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

PARAMFILE	="Sample.ini"	# 設定ファイル

WAIT0		=0.1		# 監視間隔 (s)
WAIT1		=2.0		# 送信間隔 (s)

$Fd		=0		# 通信用識別子



# ==============================================================================
# メッセージ表示 ===============================================================

def DispData(
		tp,		# i  : メッセージ種別
				#	=1 : 受信メッセージ
				#	 2 : 送信メッセージ
		hd,		# i  : SECS メッセージ・ヘッダ
		msg,		# i  : SECS メッセージ本体
		len,		# i  : msg の長さ
		rtn)		# i  : I/O の戻り値

  ctp	=["SRES","RECV","SEND"]

  if rtn<0 then
    case -rtn
    when ENODEV		then	printf("No such device ID\n")
    when E2BIG		then	printf("Data size to large\n")
    when E_NODATA
    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"	,hd.did)
    when E_REJECT	then
			printf("Rejected XId=0x%04x.%04x\n"	,hd.sid,hd.xid)
    when E_DESELECT	then	printf("Deselected (0x%04x)\n"	,hd.did)
    when E_NOTCONNECT	then	printf("Not connected\n")
    else			printf("Error [%d]\n"		,rtn)
    end

  else
    rbit=" ";	if (hd.did&0x8000)!=0 then	rbit="R"	end
    wbit=" ";	if (hd.scd&  0x80)!=0 then	wbit="W"	end
    sfcode=sprintf("S%dF%d",hd.scd&0x7f,hd.fcd)
    printf("[%s]  Dev=0x%04x  %-8s  %s%s  XId=0x%04x.0x%04x  Len=%4d\n"	\
 			,ctp[tp],hd.did,sfcode,rbit,wbit,hd.sid,hd.xid,len)
  end
end



# ==============================================================================
# メッセージ構築 ===============================================================

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

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

  rtn=TDS.__TDSCommSend($Fd,0x0000,0,sf,0,msg,len,hd)
  DispData(2,hd,msg,len,rtn)

  return rtn
end


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

def SendS1F2H(
		did,
		xid)
  hd =TDS::SHead.malloc
  msg="\0"*256
  sf =0x0102
  len=0

  md =TDS.__TDSMssgInit(    0,msg,  256,$Fd)			# S1F2
      TDS.__TDSMssgBuild(md,0,msg,0o000,  0,  0)		# L0
  len=TDS.__TDSMssgEnd(  md,0,msg);

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

  return rtn
end


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

def SendS1F2E(
		did,
		xid)
  hd =TDS::SHead.malloc
  msg="\0"*256
  sf =0x0102
  len=0;
  
  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);

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

  return rtn
end



# ==============================================================================
# ホスト側処理 -----------------------------------------------------------------

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

  catch :Exit do
  $Fd=TDS.__TDSCommOpen(0x02,PARAMFILE,"HOST",0,0,0,0,0,0)
  if $Fd					< 0 then	throw :Exit end
  print "(H) Opened (#{$Fd})\n"

  while true do
    rtn=TDS.__TDSCommRecv($Fd,0,did,0,xid,msg,msz,hd)
    DispData(1,hd,msg,rtn,rtn)
    if rtn	< 0 then
      sleep(WAIT0);	# NODATA を含めデータ受信でなかったらならチョット待つ
    else
      if hd.scd==0x81 && hd.fcd==0x01 then	# S1F1 受信したら・・
	SendS1F2H(did.ui,xid.ui)
	sleep(WAIT1)
	SendS1F1()
      end
    end
  end

  end						# :Exit
  if $Fd>0 then	TDS.__TDSCommClose($Fd,0)
  else		print "(H) Error (#{$Fd})\n"
  end
end



# ==============================================================================
# 装置側処理 -------------------------------------------------------------------

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

  catch :Exit do
  $Fd=TDS.__TDSCommOpen(0x02,PARAMFILE,"EQUIP",0,0,0,0,0,0)
  if $Fd					< 0 then	throw :Exit end
  print "(E) Opened (#{$Fd})\n"

  # HSMS-SS の場合は、以下を実行。SECS-1 の場合は必要なし。
  if TDS.__TDSCommSend($Fd,0x0100,0,0,0,0,0,0)	< 0 then	throw :Exit end
  print "(E) Connected\n"
  if TDS.__TDSCommSend($Fd,0x0200,0,0,0,0,0,0)	< 0 then	throw :Exit end
  print "(E) Selected\n"

  sleep(WAIT1)
  SendS1F1()
 
  while true do
    rtn=TDS.__TDSCommRecv($Fd,0,did,0,xid,msg,msz,hd)
    DispData(1,hd,msg,rtn,rtn)
    if rtn	< 0 then
      sleep(WAIT0);	# NODATA を含めデータ受信でなかったらならチョット待つ
    else
      if hd.scd==0x81 && hd.fcd==0x01 then	# S1F1 受信したら・・
	SendS1F2E(did.ui,xid.ui)
	sleep(WAIT1)
	SendS1F1()
      end
    end
  end

  end						# :Exit
  if $Fd>0 then	TDS.__TDSCommClose($Fd,0)
  else		print "(E) Error (#{$Fd})\n"
  end
end



# ==============================================================================
# Main 処理 --------------------------------------------------------------------

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