// =====================================================================
// PAC Control subroutine: EcatCommand
// Sends one text command to ecatd and returns the one-line reply.
//
// Create a subroutine named EcatCommand with these PASSED parameters
// (in this order):
//   chEcat      Communication Handle   e.g. initial value "tcp:127.0.0.1:5020"
//   sCmd        String (width 128)     command, e.g. "GET 1 POS_ACTUAL"
//   nTimeoutMs  Integer 32             reply timeout in ms, e.g. 1000
//   sReply      String (width 256)     full reply line from ecatd
//   sValue      String (width 256)     reply text after "OK " / "ERR "
//   nResult     Integer 32              0 = OK
//                                       1 = ERR reply (error text in sValue)
//                                      -1 = could not open connection
//                                      -2 = transmit failed
//                                      -3 = no reply within timeout
//                                      -4 = receive failed
// and these LOCAL variables:
//   nStatus  Integer 32      nWait  Integer 32      nLen  Integer 32
//   nChar    Integer 32      sTemp  String (width 256)
//
// Paste everything below into one OptoScript block in the subroutine.
// =====================================================================

sReply = "";
sValue = "";
nResult = 0;

// ---- open the connection if needed (it stays open between calls) ----
if (not IsCommunicationOpen(chEcat)) then
  nStatus = OpenOutgoingCommunication(chEcat);
  if (nStatus <> 0) then
    nResult = -1;
  else
    SetEndOfMessageTerminator(chEcat, 10);   // ecatd ends every reply with CR LF
  endif
endif

if (nResult == 0) then
  // ---- throw away anything left over from an earlier timed-out call ----
  while (GetNumCharsWaiting(chEcat) > 0)
    nStatus = ReceiveString(sTemp, chEcat);
  wend

  // ---- send the command, terminated with CR ----
  sTemp = sCmd + Chr(13);
  nStatus = TransmitString(sTemp, chEcat);
  if (nStatus <> 0) then
    nResult = -2;
  endif
endif

if (nResult == 0) then
  // ---- wait for the reply ----
  nWait = 0;
  while ((GetNumCharsWaiting(chEcat) <= 0) and (nWait < nTimeoutMs))
    DelayMsec(10);
    nWait = nWait + 10;
  wend

  if (GetNumCharsWaiting(chEcat) <= 0) then
    nResult = -3;
  else
    nStatus = ReceiveString(sReply, chEcat);
    if (nStatus < 0) then
      nResult = -4;
    endif
  endif
endif

if (nResult == 0) then
  // ---- strip trailing CR / LF ----
  nLen = GetStringLength(sReply);
  nChar = 13;
  while ((nLen > 0) and ((nChar == 13) or (nChar == 10)))
    nChar = GetNthCharacter(sReply, nLen - 1);
    if ((nChar == 13) or (nChar == 10)) then
      nLen = nLen - 1;
    endif
  wend
  GetSubstring(sReply, 0, nLen, sTemp);
  sReply = sTemp;

  // ---- split "OK value" / "ERR code text" ----
  GetSubstring(sReply, 0, 2, sTemp);
  if (sTemp == "OK") then
    nResult = 0;
    if (nLen > 3) then
      GetSubstring(sReply, 3, nLen - 3, sValue);
    endif
  else
    nResult = 1;
    if (nLen > 4) then
      GetSubstring(sReply, 4, nLen - 4, sValue);
    else
      sValue = sReply;
    endif
  endif
endif

// ---- on a communication failure, close so the next call reconnects ----
if (nResult < 0) then
  if (IsCommunicationOpen(chEcat)) then
    CloseCommunication(chEcat);
  endif
endif
