# coding: Shift_JIS

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


# ==============================================================================
#
# PythonIo : Test and sample program
#
#  Construct a message in AP and simply send and receive SECS messages.
#
#
# Starting method
#
#   python PythonIo.py {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, "PythonIo h" and "PythonIo 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.
#
#   "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
#   reception processing, and automatically display received message and output
#   secondary message.
#   When using Callback function, keep in mind that input prompting message may
#   be disturbed.
#
#
# ==============================================================================
#
# PythonIo : テスト 及び サンプル・プログラム
#
#  ＡＰ内でメッセージを構築し、単純に SECS メッセージの送受信を行う。
#
#
# 起動方法
#
#   python PythonIo.py {h|e}
#   ~~~~~~~~~~~~~~~~~~~~~~~~
#   h    : Sample.ini の [HOST]  セクションを参照し動作を決定する
#   e    : Sample.ini の [EQUIP]      :              :
#
#
#   通常、PythonIo h 及び PythonIo e の両方を、同一マシン、もしくは異なるマ
#   シン上で動作させて、相互通信を行う。通信相手は、BasicIo.exe、
#   Callbackio.exe 等、他のサンプルＡＰでもよい。
#
#   起動後、メニュ表示を参照し、要求コード等を入力し、機能を実行する。
# 
#   ホスト側は起動すると、装置側からの接続を待つ。装置側を起動すると、自動的
#   にホスト側に接続要求、Select 要求を発行する。ホスト側では、1:Recv 処理を
#   繰り返し行い、表示が「STATUS=951 : No data」となるのを確認する。その後、
#   任意のメッセージのやりとりを行う。受信側では、受信データの刈り取りを「
#   STATUS=951」になるまで行うことを忘れぬこと。
#
#   ２次メッセージを送信する際のトランザクションＩＤは、直前に受信した１次
#   メッセージのものを使用する。
#
#   本サンプルは、異常時処理を一部省略している。
#
#   以下で定義する "SECS_MODE" は、処理関数として _TDSCommXxxx() を使用する
#   場合で Active 接続の場合の Passive 側への接続処理の実行の有無を判断する
#   ために定義する。
#   "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 関数を使用する場合、入力促進用メッセージに乱れが生じる事に留意
#   すること。
#
# ==============================================================================

import sys
import ctypes
import threading

from time   import *
from ctypes import *


class SHead(Structure):
  _fields_=     [("did"     ,c_ushort    )
                ,("scd"     ,c_ubyte     )
                ,("fcd"     ,c_ubyte     )
                ,("ptp"     ,c_ubyte     )
                ,("stp"     ,c_ubyte     )
                ,("sid"     ,c_ushort    )
                ,("xid"     ,c_ushort    )]


tds=windll.LoadLibrary('TDS.dll')
tds._TDSCommOpen         .restype=  c_int
tds._TDSCommOpen0        .restype=  c_int
tds._TDSCommClose        .restype=  c_int
tds._TDSCommRecv         .restype=  c_int
tds._TDSCommSend         .restype=  c_int
tds._TDSCommSendError    .restype=  c_int
tds._TDSCommSendComment  .restype=  c_int
tds._TDSCommStatus       .restype=  c_int
tds._TDSCommSelectStatus .restype=  c_int

tds._TDSErrorValue       .restype=  c_int
tds._TDSErrorPosition    .restype=  c_int
tds._TDSErrorStatus      .restype=  c_int
tds._TDSMssgErrorValue   .restype=  c_int
tds._TDSMssgErrorPosition.restype=  c_int
tds._TDSMssgErrorStatus  .restype=  c_int

tds._TDSMssgInit         .restype=  c_int
tds._TDSMssgEnd          .restype=  c_int
tds._TDSMssgBuild        .restype=  c_int
tds._TDSMssgBuildL       .restype=  c_int
tds._TDSMssgFind         .restype=  c_int
tds._TDSMssgExit         .restype=  c_int
tds._TDSMssgNext         .restype=  c_int
tds._TDSMssgNextL        .restype=  c_int

tds._TDSMDMssgInitialize .restype=  c_int
tds._TDSMDMssgTerminate  .restype=  c_int
tds._TDSMDMssgInit       .restype=  c_int
tds._TDSMDMssgEnd        .restype=  c_int
tds._TDSMDMssgBuild      .restype=  c_int
tds._TDSMDMssgFind       .restype=  c_int
tds._TDSMDMssgExit       .restype=  c_int
tds._TDSMDMssgNext       .restype=  c_int
tds._TDSMDMssgNextEx     .restype=  c_int
tds._TDSMDMssgNextL      .restype=  c_int
tds._TDSMDMssgAutoRes    .restype=  c_int

tds._TDSUDrvOpen         .restype=  c_int
tds._TDSUDrvClose        .restype=  c_int
tds._TDSUDrvRecv         .restype=  c_int
tds._TDSUDrvSend         .restype=  c_int
tds._TDSUDrvSendError    .restype=  c_int
tds._TDSUDrvSendComment  .restype=  c_int
tds._TDSUDrvStatus       .restype=  c_int
tds._TDSUDrvSelectStatus .restype=  c_int

tds._TDSCommOpen         .argtypes=[c_int,POINTER(c_char),POINTER(c_char),c_int,c_int,c_int,c_int,c_int,c_int]
tds._TDSCommOpen0        .argtypes=[c_int,POINTER(c_char),POINTER(c_char)]
tds._TDSCommClose        .argtypes=[c_int,c_int]
tds._TDSCommRecv         .argtypes=[c_int,c_int,POINTER(c_int),POINTER(c_int),POINTER(c_int),POINTER(c_ubyte),c_int,POINTER(SHead)]
tds._TDSCommSend         .argtypes=[c_int,c_int,c_int,c_int,c_int,POINTER(c_ubyte),c_int,POINTER(SHead)]
tds._TDSCommSendError    .argtypes=[c_int,c_int,c_int,c_int,POINTER(SHead),POINTER(SHead),c_void_p]
tds._TDSCommSendComment  .argtypes=[c_int,c_int,POINTER(c_char),POINTER(c_char),c_int]
tds._TDSCommStatus       .argtypes=[c_int,c_int]
tds._TDSCommSelectStatus .argtypes=[c_int,c_int,c_int]

tds._TDSErrorValue       .argtypes=[c_int]
tds._TDSErrorPosition    .argtypes=[c_int]
tds._TDSErrorStatus      .argtypes=[c_int,POINTER(c_int),POINTER(c_int)]
tds._TDSMssgErrorValue   .argtypes=[c_int]
tds._TDSMssgErrorPosition.argtypes=[c_int]
tds._TDSMssgErrorStatus  .argtypes=[c_int,POINTER(c_int),POINTER(c_int)]

tds._TDSMssgInit         .argtypes=[c_int,POINTER(c_ubyte),c_int,c_int]
tds._TDSMssgEnd          .argtypes=[c_int,c_int,POINTER(c_ubyte)]
tds._TDSMssgBuild        .argtypes=[c_int,c_int,POINTER(c_ubyte),c_int,c_int,c_void_p]
tds._TDSMssgBuildL       .argtypes=[c_int,c_int,POINTER(c_ubyte),POINTER(c_char)]
tds._TDSMssgFind         .argtypes=[c_int,POINTER(c_ubyte),c_int,c_int,POINTER(SHead),POINTER(c_char)]
tds._TDSMssgExit         .argtypes=[c_int,c_int,POINTER(c_ubyte)]
tds._TDSMssgNext         .argtypes=[c_int,c_int,POINTER(c_ubyte),POINTER(c_int),POINTER(c_int),POINTER(c_int),c_void_p,c_int]
tds._TDSMssgNextL        .argtypes=[c_int,c_int,POINTER(c_ubyte),POINTER(c_int),POINTER(c_int),POINTER(c_char),c_int]

tds._TDSMDMssgInitialize .argtypes=[c_int,c_int,POINTER(c_char)]
tds._TDSMDMssgTerminate  .argtypes=[c_int,c_int]
tds._TDSMDMssgInit       .argtypes=[c_int,c_int,POINTER(c_ubyte),c_int,POINTER(c_char)]
tds._TDSMDMssgEnd        .argtypes=[c_int,c_int,POINTER(c_ubyte)]
tds._TDSMDMssgBuild      .argtypes=[c_int,c_int,POINTER(c_ubyte),POINTER(c_char),c_int,c_void_p]
tds._TDSMDMssgFind       .argtypes=[c_int,c_int,POINTER(c_ubyte),c_int,c_int,POINTER(c_char)]
tds._TDSMDMssgExit       .argtypes=[c_int,c_int,POINTER(c_ubyte)]
tds._TDSMDMssgNext       .argtypes=[c_int,c_int,POINTER(c_ubyte),POINTER(c_char),c_int,c_void_p]
tds._TDSMDMssgNextEx     .argtypes=[c_int,c_int,POINTER(c_ubyte),POINTER(c_char),c_int,c_void_p,POINTER(c_int),POINTER(c_int)]
tds._TDSMDMssgNextL      .argtypes=[c_int,c_int,POINTER(c_ubyte),POINTER(c_int),POINTER(c_int),POINTER(c_char),c_int]
tds._TDSMDMssgAutoRes    .argtypes=[c_int,c_int,POINTER(SHead),POINTER(c_ubyte),c_int,c_int,POINTER(c_char),POINTER(c_char),POINTER(c_int)]

tds._TDSUDrvOpen         .argtypes=[c_int,POINTER(c_char),POINTER(c_char),c_int]
tds._TDSUDrvClose        .argtypes=[c_int,c_int]
tds._TDSUDrvRecv         .argtypes=[c_int,c_int,POINTER(c_int),POINTER(c_int),POINTER(c_int),POINTER(c_ubyte),c_int,POINTER(SHead)]
tds._TDSUDrvSend         .argtypes=[c_int,c_int,c_int,c_int,c_int,POINTER(c_ubyte),c_int,POINTER(SHead)]
tds._TDSUDrvSendError    .argtypes=[c_int,c_int,c_int,c_int,POINTER(SHead),POINTER(SHead),c_void_p]
tds._TDSUDrvSendComment  .argtypes=[c_int,c_int,POINTER(c_char),POINTER(c_char),c_int]
tds._TDSUDrvStatus       .argtypes=[c_int,c_int]
tds._TDSUDrvSelectStatus .argtypes=[c_int,c_int,c_int]


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

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
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  =0x00           # SECS Message display format
                                # 0x00 : TDS Format
                                # 0x20 : SML Format
MSSG_USE_NEXTL  =1              # Use MssgNextL() or not
                                # 0    : Not use
                                # 1    : Use

PARAMFILE       =b"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             =threading.Lock()


# ==============================================================================
# Common function ==============================================================


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

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

  global    Fd,Md

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

  pass;                                 p_msg=cast(msg ,POINTER(c_ubyte))
  mnm =create_string_buffer( 32);       p_mnm=cast(mnm ,POINTER(c_char))
  strg=create_string_buffer(512);       p_str=cast(mnm ,POINTER(c_char))
  item=(c_ubyte*64)();                  p_itm=cast(item,POINTER(c_ubyte))
  pass;                                 itmst=cast(item,c_char_p)
  itmsb=cast(item,POINTER(c_byte));     itmub=cast(item,POINTER(c_ubyte));
  itmss=cast(item,POINTER(c_short));    itmus=cast(item,POINTER(c_ushort));
  itmsi=cast(item,POINTER(c_int));      itmui=cast(item,POINTER(c_uint));
  itmsl=cast(item,POINTER(c_longlong)); itmul=cast(item,POINTER(c_ulonglong));
  itmfl=cast(item,POINTER(c_float));    itmfl=cast(item,POINTER(c_float));
  itmdb=cast(item,POINTER(c_double));   itmfl=cast(item,POINTER(c_double));
  cform=c_int(0)
  csz  =c_int(0)
  cnoi =c_int(0)

  md  =0;       dp  =MSSG_USE_FILE|MSSG_DISP_TYPE
  rbit=" ";     wbit=" "
  if (did&0x8000)!=0:   rbit="R"
  if (sf &0x8000)!=0:   wbit="W"
  sfcode="S{0:d}F{1:d}".format(int((sf&0x7f00)/0x0100),int(sf&0xff))

  print("[{0:s}]  Dev=0x{1:04x}  {2:<8s}  {3:s}{4:s} XId=0x{5:04x}  Len={6:3d}" \
        .format(ctp[tp],did&0x7fff,sfcode,rbit,wbit,xid,ln))

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

  if ln>0:
    if tp==1:   dp|=0x3000;     fm|=0x3000      # In case of reception
    else:       dp|=0x2000;     fm|=0x2000      # In case of sending

    md=tds._TDSMssgFind(fm,p_msg,ln,Fd,byref(hd),p_mnm)
    if md > 0:
      mname=mnm.value.decode()
      if len(mname)>0:  print(mname)            # Message name

      la=0
      while True:
        if MSSG_USE_NEXTL==0:   # Get item value
          rtn=tds._TDSMssgNext(md \
            ,0,p_msg,byref(cform),byref(csz),byref(cnoi),p_itm,64)
          form=cform.value; noi=cnoi.value; sz=csz.value
          if rtn< 0:            break
          print("{0:03o}:{1:d}*{2:2d}:".format(form,sz,noi),end="")
                                # Display of field value
                                # The second and subsequent numbers are omitted
                                # 数値の２番目以降は省略
          if   form==0o000: print("L[{0:d}]"          .format(noi))
          elif form==0o010: print("B[{0:d}]=0x{1:02x}".format(noi,itmub[0]))
          elif form==0o011: print("T[{0:d}]={1:d}"    .format(noi,itmub[0]))
          elif form==0o020: print("A[{0:d}]={1:s}"    .format(noi,itmst.value.decode("SHift_JIS")))
          elif form==0o021: print("J[{0:d}]={1:s}"    .format(noi,itmst.value.decode("SHift_JIS")))
          elif form==0o022: print("K[{0:d}]={1:s}"    .format(noi,itmst.value.decode("SHift_JIS")))
          elif form==0o030: print("I8[{0:d}]={1:d}"   .format(noi,itmsl[0]))
          elif form==0o031: print("I1[{0:d}]={1:d}"   .format(noi,itmsb[0]))
          elif form==0o032: print("I2[{0:d}]={1:d}"   .format(noi,itmss[0]))
          elif form==0o034: print("I4[{0:d}]={1:d}"   .format(noi,itmsi[0]))
          elif form==0o040: print("F8[{0:d}]={1:f}"   .format(noi,itmdb[0]))
          elif form==0o044: print("F4[{0:d}]={1:f}"   .format(noi,itmfl[0]))
          elif form==0o050: print("U8[{0:d}]={1:d}"   .format(noi,itmul[0]))
          elif form==0o051: print("U1[{0:d}]={1:d}"   .format(noi,itmub[0]))
          elif form==0o052: print("U2[{0:d}]={1:d}"   .format(noi,itmus[0]))
          elif form==0o054: print("U4[{0:d}]={1:d}"   .format(noi,itmui[0]))
          else:             print("")

        else:                                   # Get in list format
                                                # リスト表示形式で取得
          rtn=tds._TDSMssgNextL(md,dp,p_msg,byref(cform),byref(cnoi),strg,512)
          form=cform.value; noi=cnoi.value
          if  rtn       < 0:    break
          if (dp&0x70)  > 0x10:                 # Incase of SML
            i=la
            while i>rtn:
              j=0
              while j<(i*2):
                print(" ",end="")
                j+=1
              print(">")                        # Display '>'
              i-=1
          la=rtn                                # Save current hierarchy
                                                # 現在の階層を保存
          print("  {0:s}".format(strg.value.decode("Shift_JIS")))
                                                # Display acquired field value
                                                # 取得した項目値を表示
          if (dp&0x70)>0x10 and form==000 and noi==0:
            j=0
            while j<((la+1)*2):
              print(" ",end="")
              j+=1
            print(">")                          # Display '>'

      if MSSG_USE_NEXTL!=0:
        if (dp&0x70)   > 0x10:
          i=la
          while i>0:
            j=0
            while j<(i*2):
              print(" ",end="")
              j+=1;
            print(">")                          # Show remaining '>'
            i-=1                                # 残りの '>' 表示

      tds._TDSMssgExit(md,0,p_msg);



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

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

  global    Fd,Md
  global    Cs1

  Cs1.acquire()
  ok=0
  if rtn<0:
    ok=1
    if (rtn<(-E_NOTCONNECT) or (-E_ILLBLOCK)<rtn) and rtn!=(-E_NODATA):
      print("ERROR  [{0:d}]".format(rtn),end="")
      if   rtn==(-ENODEV):
        print(" : No such device ID");      ln=ok=0
      elif rtn==(-E2BIG ):
        print(" : Data size to large");     ln=ok=0
      else:                     print("")

    else:
      if FUNC_TYPE==0:  stat=tds._TDSCommStatus(Fd,0)
      else:             stat=tds._TDSUDrvStatus(Fd,0)
      print("STATUS = {0:d},{1:d} : ".format(-rtn,stat),end="")
      if   rtn==-E_NODATA:      print("No data")
      elif rtn==-E_ILLBLOCK:    print("Illegal block#")
      elif rtn==-E_T1TIMEDOUT:  print("T1 Timeout occur")
      elif rtn==-E_T2TIMEDOUT:  print("T2 Timeout occur")
      elif rtn==-E_T3TIMEDOUT:  print("T3 Timeout occur")
      elif rtn==-E_T4TIMEDOUT:  print("T4 Timeout occur")
      elif rtn==-E_T5TIMEDOUT:  print("T5 Timeout occur")
      elif rtn==-E_T6TIMEDOUT:  print("T6 Timeout occur")
      elif rtn==-E_T7TIMEDOUT:  print("T7 Timeout occur")
      elif rtn==-E_T8TIMEDOUT:  print("T8 Timeout occur")
      elif rtn==-E_RETRYOVER:   print("Retry over")
      elif rtn==-E_CONNECT:     print("Connected")
      elif rtn==-E_SELECT:      print("Selected   ({0:04x})".format(did))
      elif rtn==-E_REJECT:      print("Rejected XId={0:04x}".format(xid))
      elif rtn==-E_DESELECT:    print("Deselected ({0:04x})".format(did))
      elif rtn==-E_NOTCONNECT:  print("Not connected")
      else:                     print("")

  if ok==0:     DispSECSMssg(tp,hd,did,sf,xid,msg,ln)
  Cs1.release()



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

def SendS1F1():
  global    Fd,Md
  hd =SHead()
  msg=(c_ubyte*16)();   p_msg=cast(msg,POINTER(c_ubyte))
  sf =0x8101
  ln =0

  if FUNC_TYPE==0:  rtn=tds._TDSCommSend(Fd,0x0000,0,sf,0,p_msg,ln,byref(hd))
  else:             rtn=tds._TDSUDrvSend(Fd,0x0000,0,sf,0,p_msg,ln,byref(hd))
  DispData(2,hd,0x00,sf,rtn,msg,ln,rtn)

  return rtn


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

def SendS1F2H(did,xid):
  global    Fd,Md
  hd =SHead()
  msg=(c_ubyte*256)();  p_msg=cast(msg,POINTER(c_ubyte))
  sf =0x0102
  ln =0

  val="\0"*32
  
  if (MSSG_USE_FILE&0x80)==0:                   # Do not use message definition
    md =tds._TDSMssgInit(      0,p_msg,  256,Fd)            # S1F2
    dmy=tds._TDSMssgBuild(  md,0,p_msg,0o000,  0,  0)       # L0
    ln =tds._TDSMssgEnd(    md,0,p_msg)

  else:                                         # Use message definition
    dmy=tds._TDSMDMssgInit( Md,0,p_msg,  256,b"S1F2_H")
    ln =tds._TDSMDMssgEnd(  Md,0,p_msg)

  if ln<0:           rtn=ln
  else:
    if FUNC_TYPE==0:
      rtn=tds._TDSCommSend(Fd,0x0000,did,sf,xid,p_msg,ln,byref(hd))
    else:
      rtn=tds._TDSUDrvSend(Fd,0x0000,did,sf,xid,p_msg,ln,byref(hd))
  DispData(2,hd,did,sf,rtn,msg,ln,rtn)

  return rtn


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

def SendS1F2E(did,xid):
  global    Fd,Md
  hd =SHead()
  msg=(c_ubyte*256)();  p_msg=cast(msg,POINTER(c_ubyte))
  sf =0x0102
  ln =0

  if (MSSG_USE_FILE&0x80)==0:                   # Do not use message definition
    md =tds._TDSMssgInit(     0,p_msg,  256,Fd)             # S1F2
    dmy=tds._TDSMssgBuild( md,0,p_msg,0o000,  2,  0)        # L2
    dmy=tds._TDSMssgBuild( md,0,p_msg,0o020,  6,b"EQUIP1")  #  MDLN
    dmy=tds._TDSMssgBuild( md,0,p_msg,0o020,  6,b"01.000")  #  SOFTREV
    ln =tds._TDSMssgEnd(   md,0,p_msg);

  else:                                         # Use message definition
    dmy=tds._TDSMDMssgInit(Md,0,p_msg,  256,b"S1F2_E")
    ln =tds._TDSMDMssgEnd( Md,0,p_msg)

  if ln<0:           rtn=ln
  else:
    if FUNC_TYPE==0:
      rtn=tds._TDSCommSend(Fd,0x0000,did,sf,xid,p_msg,ln,byref(hd))
    else:
      rtn=tds._TDSUDrvSend(Fd,0x0000,did,sf,xid,p_msg,ln,byref(hd))
  DispData(2,hd,did,sf,rtn,msg,ln,rtn)

  return rtn


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

def SendS2F49():
  global    Fd,Md,cnt249
  hd =SHead()
  msg=(c_ubyte*1024)(); p_msg=cast(msg,POINTER(c_ubyte))
  val=(c_ubyte*  32)();
  vu1=cast(val,POINTER(c_ubyte))
  vu4=cast(val,POINTER(c_uint))
  sf =0x8200+49
  ln =0

  cnt249+=1
  vst="LOTID ({0:4d})".format(cnt249).encode()
  no1=(cnt249%2)+1; no2=(cnt249%10)+1
  
  if (MSSG_USE_FILE&0x80)==0:                   # Do not use message definition
    md =        tds._TDSMssgInit(      0,p_msg, 1024,Fd)                # S2F49
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o000,  3,  0)           # L3
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o010,  1,vu1)           #  DATAIDB
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o020,  0,b"LOAD")       #  RCMD
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o000,  4,  0)           #  L4
    vu1[0]=1;   tds._TDSMssgBuild(  md,0,p_msg,0o010,  1,vu1)           #   STID
    vu1[0]=0;   tds._TDSMssgBuild(  md,0,p_msg,0o010,  1,vu1)           #   MTKD
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o020, 20,vst)           #   LOTID
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o000,no1,  0)           #   L[no1]
    i=0
    while i<no1:
      pass;     tds._TDSMssgBuild(  md,0,p_msg,0o000,no2,  0)           #    L[no2]
      j=0
      while j<no2:
        pass;   tds._TDSMssgBuild(  md,0,p_msg,0o000,  2,  0)           #     L[2]
        vst="WAFER({0:04d}-{1:d}-{2:02d})".format(cnt249,i+1,j+1).encode()
        pass;   tds._TDSMssgBuild(  md,0,p_msg,0o020, 20,vst)           #      WAFERID
        vst="PPID ({0:04d}-{1:d}-{2:02d})".format(cnt249,i+1,j+1).encode()
        pass;   tds._TDSMssgBuild(  md,0,p_msg,0o020, 16,vst)           #      PPID
        j+=1
      i+=1
    ln =        tds._TDSMssgEnd(    md,0,p_msg)

  else:                                         # Use message definition
    pass;       tds._TDSMDMssgInit( Md,0,p_msg,  1024,b"S2F49")
    pass;       tds._TDSMDMssgBuild(Md,0,p_msg,b"LOTID"  ,0,vst)        #   LOTID
    vu4[0]=no1; tds._TDSMDMssgBuild(Md,0,p_msg,b"NOI1"   ,1,vu4)        #   L[no1]
    i=0
    while i<no1:
      itm   ="NOI2:{0:d}"                 .format(        i+1).encode()
      vu4[0]=no2
      pass;     tds._TDSMDMssgBuild(Md,0,p_msg,itm       ,1,vu4)        #    L[no2]
      j=0
      while j<no2:
        itm="WAFERID:{0:d}:{1:d}"         .format(        i+1,j+1).encode()
        vst="WAFER({0:04d}-{1:d}-{2:02d})".format(cnt249 ,i+1,j+1).encode()
        pass;   tds._TDSMDMssgBuild(Md,0,p_msg,itm        ,0,vst)      #     WAFERID
        itm="PPID:{0:d}:{1:d}"            .format(        i+1,j+1).encode()
        vst="PPID ({0:04d}-{1:d}-{2:02d})".format(cnt249 ,i+1,j+1).encode()
        pass;   tds._TDSMDMssgBuild(Md,0,p_msg,itm        ,0,vst)      #     PPID
        j+=1
      i+=1
    ln =        tds._TDSMDMssgEnd(  Md,0,p_msg)

  if ln<0:              rtn=ln
  else:
    if FUNC_TYPE==0:    rtn=tds._TDSCommSend(Fd,0x0000,0,sf,0,p_msg,ln,byref(hd))
    else:               rtn=tds._TDSUDrvSend(Fd,0x0000,0,sf,0,p_msg,ln,byref(hd))
  DispData(2,hd,0x00,sf,rtn,msg,ln,rtn)

  return rtn


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

def SendS2F50(did,xid):
  global    Fd,Md,cnt250
  hd =SHead()
  msg=(c_ubyte* 256)(); p_msg=cast(msg,POINTER(c_ubyte))
  val=(c_ubyte*  32)();
  vu1=cast(val,POINTER(c_ubyte))
  vu4=cast(val,POINTER(c_uint))
  sf =0x0200+50
  ln =0
  
  cnt250+=1
  vst="LOTID ({0:4d})".format(cnt250).encode()

  if (MSSG_USE_FILE&0x80)==0:                   # Do not use message definition
    md =        tds._TDSMssgInit(      0,p_msg,  256,Fd)                # S2F50
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o000,  2,0)             # L2
    vu1[0]=0;   tds._TDSMssgBuild(  md,0,p_msg,0o010,  1,vu1)           #  HCACK
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o000,  2,0)             #  L2
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o020,  5,b"PODID")      #   PODID
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o020, 20,vst)           #   LOTID
    ln =        tds._TDSMssgEnd(    md,0,p_msg);

  else:                                         # Use message definition
    pass;       tds._TDSMDMssgInit( Md,0,p_msg,  256,b"S2F50");
    pass;       tds._TDSMDMssgBuild(Md,0,p_msg,b"LOTID",0,vst);         #   LOTID
    ln =        tds._TDSMDMssgEnd(  Md,0,p_msg);

  if ln<0:           rtn=ln
  else:
    if FUNC_TYPE==0: rtn=tds._TDSCommSend(Fd,0x0000,did,sf,xid,p_msg,ln,byref(hd))
    else:            rtn=tds._TDSUDrvSend(Fd,0x0000,did,sf,xid,p_msg,ln,byref(hd))
  DispData(2,hd,did,sf,rtn,msg,ln,rtn)

  return rtn


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

def SendS6F11():
  global    Fd,Md,cnt611
  hd =SHead()
  msg=(c_ubyte* 256)(); p_msg=cast(msg,POINTER(c_ubyte))
  val=(c_ubyte*  32)();
  vu2=cast(val,POINTER(c_ushort))
  sf =0x8600+11
  ln =0

  cnt611+=1
  if cnt611==65536: cnt611=0

  if (MSSG_USE_FILE&0x80)==0:                   # Do not use message definition
    md =        tds._TDSMssgInit(      0,p_msg,  256,Fd)                # S6F11
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o000,  3,0)             # L3
    vu2[0]=cnt611
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o052,  1,vu2)           #  DATAID
    vu2[0]=8;   tds._TDSMssgBuild(  md,0,p_msg,0o052,  1,vu2)           #  CEID
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o000,  3,0)             #  L3
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o020, 16,b"DATA1")      #   DATA1
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o020, 16,b"DATA2")      #   DATA2
    pass;       tds._TDSMssgBuild(  md,0,p_msg,0o020, 14,b"YYYYMMDDhhmmss")
                        # @TIME Actually I set current time, but omitted.
                        # @TIME (本当は現在時刻を設定するのだが、省略。
    ln =        tds._TDSMssgEnd(    md,0,p_msg)

  else:                                         # Use message definition
    pass;       tds._TDSMDMssgInit( Md,0,p_msg, 256,b"S6F11_0")
    vu2[0]=cnt611
    pass;       tds._TDSMDMssgBuild(Md,0,p_msg,b"DATAID",1,vu2);        #  DATAID
    ln =        tds._TDSMDMssgEnd(  Md,0,p_msg);

  if ln<0:           rtn=ln
  else:
    if FUNC_TYPE==0: rtn=tds._TDSCommSend(Fd,0x0000,0,sf,  0,p_msg,ln,byref(hd))
    else:            rtn=tds._TDSUDrvSend(Fd,0x0000,0,sf,  0,p_msg,ln,byref(hd))
  DispData(2,hd,0x00,sf,rtn,msg,ln,rtn)

  return rtn



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

def SendS6F12(did,xid):
  global    Fd,Md
  hd =SHead()
  msg=(c_ubyte* 256)(); p_msg=cast(msg,POINTER(c_ubyte))
  val=(c_ubyte*  32)();
  vu1=cast(val,POINTER(c_ubyte))
  sf =0x0600+12
  ln =0

  if (MSSG_USE_FILE&0x80)==0:                   # Do not use message definition
    md =        tds._TDSMssgInit(      0,p_msg,  256,Fd)        # S6F12
    vu1[0]=0;   tds._TDSMssgBuild(  md,0,p_msg,0o010,  1,vu1)   # ACKC
    ln =        tds._TDSMssgEnd(    md,0,p_msg)

  else:                                         # Use message definition
    pass;       tds._TDSMDMssgInit( Md,0,p_msg,  256,b"S6F12")
    ln=         tds._TDSMDMssgEnd(  Md,0,p_msg)

  if ln<0:           rtn=ln;
  else:
    if FUNC_TYPE==0: rtn=tds._TDSCommSend(Fd,0x0000,did,sf,xid,msg,ln,byref(hd))
    else:            rtn=tds._TDSUDrvSend(Fd,0x0000,did,sf,xid,msg,ln,byref(hd))
  DispData(2,hd,did,sf,rtn,msg,ln,rtn)

  return rtn



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

def CBRecvProc(req,rtn,did,xsf,xid,xhd,xmsg):
#   int         req             # i  : Request code to library
#   int         rtn             # i  : Return code from library
#   int         did             # i  : SECS Message Device ID
#   int         xsf             # i  : SECS Message SF-Code
#   int         xid             # i  : SECS Message Transaction I
#   SHead       xhd             # i  : SECS Message Header
#   c_byte      xmsg[]          # i  : SECS Message Body

  global    Fd,Md,OType
  hd =SHead()
  msg=(c_ubyte*1024)(); p_msg=cast(msg,POINTER(c_ubyte))
  rnm=(c_ubyte*  32)(); p_rnm=cast(rnm,POINTER(c_char))
  snm=(c_ubyte*  32)(); p_snm=cast(snm,POINTER(c_char))
  sf = c_int(0)

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

  if req==0 and 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   xsf==0x8101 and OType==0:    SendS1F2H(did,xid)
      elif xsf==0x8101 and OType==1:    SendS1F2E(did,xid)
      elif xsf==0x8231:                 SendS2F50(did,xid)
      elif xsf==0x860b:                 SendS6F12(did,xid)

    elif  (xsf &0x8000)!=0:
                # When using a message definition file, this sample uses the
                # automatic reply function.
                # メッセージ定義ファイルを使用する場合は、本サンプルでは、自動
                # 返信機能を使用する。
      i=0
      while i<rtn: msg[i]=xmsg[i]; i+=1
      ln=tds._TDSMDMssgAutoRes(Md,0,byref(xhd),p_msg,rtn,1024 \
                                ,p_rnm,p_snm,byref(sf))
      if  ln>=0:
        rname=cast(rnm,c_char_p).value.decode()
        sname=cast(snm,c_char_p).value.decode()
        print("RECV {0:s} ..  Auto respond {1:s} [S{2:d}F{3:d}]" \
                .format(rname,sname,int(sf.value/0x100),sf.value&0xff))
        if FUNC_TYPE==0:
          rtn=tds._TDSCommSend(Fd,0x0000,did,sf.value,xid,p_msg,ln,byref(hd))
        else:
          rtn=tds._TDSUDrvSend(Fd,0x0000,did,sf.value,xid,p_msg,ln,byref(hd))
        DispData(2,hd,did,sf.value,xid,msg,ln,rtn)
      else:
        if ln!=(-930) and ln!=(-931):
          print("RECV Auto response error ({0:d})".format(ln))

  return 0



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

def RecvProcThread():
  global    Fd,Md,Break
  hd =SHead()
  msg=(c_ubyte*1024)(); p_msg=cast(msg,POINTER(c_ubyte))
  did= c_int(0);
  xid= c_int(0);
  sf = c_int(0);

  while Break==0:
    req=0
    if FUNC_TYPE==0:
      rtn=tds._TDSCommRecv(Fd,0,byref(did),byref(sf),byref(xid),p_msg,1024,byref(hd))
    else:
      rtn=tds._TDSUDrvRecv(Fd,0,byref(did),byref(sf),byref(xid),p_msg,1024,byref(hd))
    if rtn==(-951):
      sleep(0.1)
    else:
      if (-1000)<rtn and rtn<(-959):    req=(-rtn)-900
      CBRecvProc(req,rtn,did.value,sf.value,xid.value,hd,msg)



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

def Host():
  global    Fd,Md,Break,OType
  hd =SHead()
  msg=(c_ubyte*1024)(); p_msg=cast(msg,POINTER(c_ubyte))
  did= c_int(0)
  xid= c_int(0)
  sf = c_int(0)

  try:
    OType=0;            Break=0
    if FUNC_TYPE==0:    Fd=tds._TDSCommOpen(0x02,PARAMFILE,b"HOST",0,0,0,0,0,0)
    else:               Fd=tds._TDSUDrvOpen(0x02,PARAMFILE,b"HOST",UDRV_MASK)
    if Fd                                                    < 0:    raise ValueError
    print("(H) Opened ({0:d})".format(Fd))
    if (MSSG_USE_FILE&0x80)!=0:
      Md=tds._TDSMDMssgInitialize(0x4000,Fd,b"")

    if USE_CALLBACK!=0:
      th=threading.Thread(target=RecvProcThread)
      th.start()

    while True:
      rtn=0
      if USE_CALLBACK==0:   print("Req (0:Exit 1:Recv 2:Send) : ",end="")
      else:                 print("Req (0:Exit 2:Send) : "       ,end="")
      buf=input();          req=int(buf)
      if   req==0:          break

      elif req==1:
        if FUNC_TYPE==0:
          rtn=tds._TDSCommRecv(Fd,0,byref(did),byref(sf),byref(xid),p_msg,1024,byref(hd))
        else:
          rtn=tds._TDSUDrvRecv(Fd,0,byref(did),byref(sf),byref(xid),p_msg,1024,byref(hd))
        DispData(1,hd,did.value,sf.value,xid.value,msg,rtn,rtn)

      elif req==2:
        if USE_CALLBACK==0:   print("Message(1:S1F1 2:S2F49  6:S1F2 7:S6F12) : ",end="")
        else:                 print("Message(1:S1F1 2:S2F49) : "                ,end="")
        buf=input();  mno=int(buf)
        if   mno==1:  rtn=SendS1F1( )
        elif mno==2:  rtn=SendS2F49()
        elif mno==6:  rtn=SendS1F2H(did.value,xid.value)
        elif mno==7:  rtn=SendS6F12(did.value,xid.value)
      if rtn<(-999) or ((-900)<rtn and rtn<0):
          print("(H) I/O Error ({0:d})".format(rtn))

  except ValueError:    pass
  Break=1
  if Md>0:              tds._TDSMDMssgTerminate(Md,0)
  if Fd>0:
    if FUNC_TYPE==0:    tds._TDSCommClose(Fd,0)
    else:               tds._TDSUDrvClose(Fd,0)
  else:
    print("(H) Error ({0:d})".format(Fd))



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

def Equip():
  global    Fd,Md,Break,OType
  hd =SHead()
  msg=(c_ubyte*1024)(); p_msg=cast(msg,POINTER(c_ubyte))
  did= c_int(0)
  xid= c_int(0)
  sf = c_int(0)

  try:
    OType=1;            Break=0
    if FUNC_TYPE==0:    Fd=tds._TDSCommOpen(0x02,PARAMFILE,b"EQUIP",0,0,0,0,0,0)
    else:               Fd=tds._TDSUDrvOpen(0x02,PARAMFILE,b"EQUIP",UDRV_MASK)
    if Fd                                                   < 0:    raise ValueError
    print("(E) Opened ({0:d})".format(Fd))
    if (MSSG_USE_FILE&0x80)!=0:
      Md=tds._TDSMDMssgInitialize(0x4000,Fd,b"")

    if USE_CALLBACK!=0:
      th=threading.Thread(target=RecvProcThread)
      th.start()

    if SECS_MODE!=0 and FUNC_TYPE==0:   # In case of HSMS and use TDSCommXxxxx()
                                        # HSMS で TDSComm 使用の場合
      if tds._TDSCommSend(Fd,0x0100,0,0,0,p_msg,0,byref(hd))< 0:    raise ValueError
      print("(E) Connected");
      if tds._TDSCommSend(Fd,0x0200,0,0,0,p_msg,0,byref(hd))< 0:    raise ValueError
      print("(E) Selected");

    while True:
      rtn=0
      if USE_CALLBACK==0:   print("Req (0:Exit 1:Recv 2:Send) : ",end="")
      else:                 print("Req (0:Exit 2:Send) : "       ,end="")
      buf=input();          req=int(buf)
      if   req==0:          break

      elif req==1:
        if FUNC_TYPE==0:
          rtn=tds._TDSCommRecv(Fd,0,byref(did),byref(sf),byref(xid),p_msg,1024,byref(hd))
        else:
          rtn=tds._TDSUDrvRecv(Fd,0,byref(did),byref(sf),byref(xid),p_msg,1024,byref(hd))
        DispData(1,hd,did.value,sf.value,xid.value,msg,rtn,rtn)

      elif req==2:
        if USE_CALLBACK==0: print("Message(1:S1F1 2:S6F11  6:S1F2 7:S2F50) : "  ,end="")
        else:               print("Message(1:S1F1 2:S6F11) : "                  ,end="")
        buf=input();  mno=int(buf)
        if   mno==1:  rtn=SendS1F1( )
        elif mno==2:  rtn=SendS6F11()
        elif mno==6:  rtn=SendS1F2E(did.value,xid.value)
        elif mno==7:  rtn=SendS2F50(did.value,xid.value)

      if rtn<(-999) or ((-900)<rtn and rtn<0):
          print("(E) I/O Error ({0:d})".format(rtn))

    if SECS_MODE!=0:                        # In case of HSMS, Shutdown process
                                            # HSMS 接続の場合、切断処理
    # 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:  rtn=tds._TDSCommSend(Fd,0x0800,0,0,0,p_msg,0,byref(hd))
    # else:             rtn=tds._TDSUDrvSend(Fd,0x0800,0,0,0,p_msg,0,byref(hd))
    # if Fd                                       < 0:    raise ValueError
    # print("(E) Deselected")
      if FUNC_TYPE==0:  rtn=tds._TDSCommSend(Fd,0x0900,0,0,0,p_msg,0,byref(hd))
      else:             rtn=tds._TDSUDrvSend(Fd,0x0900,0,0,0,p_msg,0,byref(hd))
      if rtn                                      < 0:    raise ValueError
      print("(E) Separated")

  except ValueError:    pass
  Break=1
  if Md>0:              tds._TDSMDMssgTerminate(Md,0)
  if Fd>0:
    if FUNC_TYPE==0:    tds._TDSCommClose(Fd,0)
    else:               tds._TDSUDrvClose(Fd,0)
  else:
    print("(E) Error ({0:d})".format(Fd))



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

if len(sys.argv)<2:
  print("Usage: python {0:s} {{h|e}}".format(sys.argv[0]))
else:
  if sys.argv[1]=="h":  Host( )
  else:                 Equip()
