From telecom@eecs.nwu.edu Tue Aug 20 23:17:45 1991
Received: from delta.eecs.nwu.edu by gaak.LCS.MIT.EDU via TCP with SMTP
	id AA21147; Tue, 20 Aug 91 23:17:39 EDT
Resent-Message-Id: <9108210317.AA21147@gaak.LCS.MIT.EDU>
Received: from NRI.RESTON.VA.US by delta.eecs.nwu.edu id aa09803;
          20 Aug 91 16:08 CDT
Received: from mcimail.com by NRI.NRI.Reston.VA.US id bo17715;
          20 Aug 91 16:49 EDT
Date: Tue, 20 Aug 91 20:40 GMT
From: "J. Brad Hicks" <0004073044@mcimail.com>
To: Pat Townson <telecom@eecs.nwu.edu>
Subject: IXO.example
Message-Id: <23910820204032/0004073044NC5EM@mcimail.com>
Resent-Date:  Tue, 20 Aug 91 22:21:01 CDT
Resent-From: telecom@eecs.nwu.edu
Resent-To: ptownson@gaak.LCS.MIT.EDU
Status: RO

IXO PROTOCOL EXAMPLE
 
Author:    J. Brad Hicks
Language:  HyperTalk
Rev. Date: 20 Aug 91
 
 
The following two HyperTalk functions implement the IXO/TAP protocol
sufficiently to handle numeric paging.  To use these scripts in HyperCard
or SyperCard, place them in the stack/project script, then call them using:
 
   DialPager param1,param2,param3
 
where param1 is the modem phone number of the local paging company, param2
is the phone number for the pager you want to beep, and param3 is the
message to appear on the pager.
 
For debugging purposes, everything sent back from the modem is stored in
global variable SaveBuffer.
 
CONSTRAINTS: In the above example, param2 MUST be an eight-digit number
with no punctuation, zero-padded on the left ... or so I was told by the
tech support gurus at CyberTel Paging.  If you are paging a numeric pager,
param3 must be 1 to 12 bytes, and can contain any digit, a dash, or a
space.  The gurus at CyberTel told me that it can contain asterisks and
pound signs, but they don't work on my pager.  If you are paging an
alphabetic pager, then param3 can contain any printable ASCII characters
(32 to 126 decimal) and can be up to 120 bytes long.
 
REQUIREMENTS:  You will also need four of the HyperCard XCMDs/XFCNs for
serial port control: configureSPort, recvUpTo, sendSPort, and closeSPort.
These commands are all availabe via the "HyperCard Serial ToolKit" from
APDA.  For those of you who are translating this into other languages,
those all do what you'd expect except for recvUpTo, which takes three
parameters.  Param1 is the byte to stop at; if "", then it runs until it
times out.  Param2 is the maximum wait time, in 60ths of a second.  Param3
is optional, and if it's anything but empty then it gets inserted before
the serial port data.
 
IMPLEMENTATION LIMITS:  The IXO/TAP protocol supports sending multiple
messages and multiple phone numbers in a single transaction.  Since I
didn't need it for my purposes, I didn't implement them.  If you feel
brave (and have the cooperation of your local paging company), you can
send a theoretically infinite number of pager id/message packets in a
single phone call by ending every packet except the last one with <ETB>
instead of <ETX>.
 
NOTE TO HYPERCARD/SUPERCARD USERS:  Since this version of this file was put
together to be distributed over non-Macintosh systems, the line-
continuation character (option-return) has been stripped off of the longer
lines.  I'm giving you credit for figuring out where it belongs; the
continuation lines are indented (as per SuperCard format).
 
NOTE TO SUPERCARD USERS:  SuperCard predefines a constant CR that means the
same thing as when I use it, so delete it from the global variable lists
and delete the line where I initialize it at the top of DialPager.
 
CREDITS:  Thanks to Brent Chapman of Telebit for providing me with a copy
of the IXO/TAP protocol spec.  The spec itself is excerpted from Glenayre
Electronics' manual number GLP-3000-180, issue 5 (30 Jan 91), chapter 7,
pages 7-1 to 7-13, and may be available from them at 1-604-263-1611.  The
HyperCard Serial Toolkit is provided by Apple, and available through the
Apple Programmers and Developers Assocation (APDA).  All other code was
written by Brad Hicks at MasterCard International.
 
CONTACTING THE AUTHOR:  J. Brad Hicks can be most reliably contacted via
MCI Mail at JBHICKS or Internet to the same address, jbhicks@mcimail.com.
CompuServe users can also send mail to 76012,300.  AppleLink subscribers
can send mail to B0186.  Via US mail, send to 12364 Spanish Trace Dr., Apt.
G, Maryland Heights, MO 63043-2354, USA. Absolute last-ditch efforts only
may be made to 1-314-275-3645, roughly 8:30 am to 5:30 pm Central Time
(UCT+6).
 
DISCLAIMER:  This script is provided as-is and for free, and warranted
to be worth at least that much.  Although I developed it while at
MasterCard, neither MasterCard International nor its membership endorses
this product.  The author denies responsibility for any consequences,
positive or negative, arising out of anybody's use of this code, or any
work derived from this code.  Test thoroughly.  Protect yourself.
_______________________________________________________________________
 
on DialPager PhoneNumber,PagerID,theMessage
  global SPortGlobals
  global SaveBuffer
  global STX,ETX,EOT,ACK,CR,NAK,ESC,RS
  -- load constants
  put numToChar(02) into STX
  put numToChar(03) into ETX
  put numToChar(04) into EOT
  put numToChar(06) into ACK
  put numToChar(13) into CR
  put numToChar(21) into NAK
  put numToChar(27) into ESC
  put numToChar(30) into RS
  -- initialize variables
  put empty into SaveBuffer
  put MsgPacket(PagerID,theMessage) into theMsgPacket
  put 1 into OverAllAttempts
  put false into OverAllSuccess
  -- initialize port, modem
  configureSPort "modemPort","baud300","stop10","parityEven","data7",
    "XOnOutOff","CTSOutOff","lineFeedsOff","echoOff","editOff",
    "stripOn","stripControlsOff","autoWrapOff"
  sendSPort "ATZ" & CR
  get recvUpTo(empty,60,empty) -- and throw it away
  repeat until OverAllSuccess or (OverAllAttempts > 5)
    put "Attempt #" & OverAllAttempts && "Status: " into the message
    -- hang up, dial modem
    put false into DialSuccess
    put false into DialFailure
    put "Dialing" after the message
    sendSPort "+++"
    wait 1 second
    sendSPort "ATH" & CR
    get recvUpTo(empty,120,empty)
    put it after SaveBuffer
    sendSPort "ATM0DT" & PhoneNumber & CR
    put the seconds + 30 into TimeOut
    repeat until DialSuccess or DialFailure
      get recvUpTo(CR,1800,empty)
      put it after SaveBuffer
      if it contains "CONNECT" then put true into DialSuccess
      else if it contains "NO CARRIER" then
        put true into DialFailure
        put "No Carrier" into FailureReason
      else if it contains "ERROR" then
        put true into DialFailure
        put "Unknown Error" into FailureReason
      else if it contains "NO DIALTONE" then
        put true into DialFailure
        put "No Dialtone!" into FailureReason
      else if it contains "BUSY" then
        put true into DialFailure
        put "Busy" into FailureReason
      else if it contains "NO ANSWER" then
        put true into DialFailure
        put "No Answer" into DialFailure
      else if it contains "VOICE" then
        put true into DialFailure
        put "Wrong Number! (Voice Answered)"
          into last word of the message
        exit to HyperCard
      else if the seconds >= TimeOut then
        put true into DialFailure
        put "Timed Out" into FailureReason
      end if
    end repeat
    if DialFailure then
      put FailureReason into last word of the message
    else
      put "DialSuccess, Waiting" into last word of the message
      -- whack CR, wait for "ID="
      put the seconds + 10 into TimeOut
      put true into IDSuccess
      put false into IDFailure
      put empty into TempBuffer
      repeat until IDSuccess or IDFailure
        sendSPort CR
        put recvUpTo(empty,90,TempBuffer) into TempBuffer
        if TempBuffer contains "ID=" then put true into IDSuccess
        else if the seconds >= timeOut then put true into IDFailure
      end repeat
      put TempBuffer after SaveBuffer
      if IDSuccess then
        put false into LoginACKed
        put false into HangUp
        put 1 into LoginAttempts
        repeat until LoginACKed or HangUp or (LoginAttempts > 5)
          -- indicate paging, wait for ACK/NAK
          put "LogOn" into last word of the message
          sendSPort ESC & "PG1" & CR
          put the seconds + 10 into TimeOut
          put false into LoginACKed
          put false into LoginNAKed
          put false into TimeOutExceeded
          put false into HangUp
          put empty into TempBuffer
          repeat until LoginACKed or LoginNAKed
            or TimeOutExceeded or HangUp
            put recvUpTo(empty,90,TempBuffer) into TempBuffer
            if TempBuffer contains (CR & ACK & CR) then
              put true into LoginACKed
            else if TempBuffer contains (CR & NAK & CR) then
              put true into LoginNAKed
            else if TempBuffer contains (CR & ESC & EOT & CR) then
              put true into HangUp
            else if the seconds >= timeOut then
              put true into TimeOutExceeded
            end if
          end repeat
          put TempBuffer after SaveBuffer
          add 1 to LoginAttempts
        end repeat
        if LoginACKed then
          put false into MsgACKed
          put false into HangUp
          put 1 into SendAttempts
          put "LoggedIn, Sending" into last word of the message
          repeat until MsgACKed or HangUp or (SendAttempts > 5)
            -- send message, wait for ACK/NAK
            sendSPort theMsgPacket
            put the seconds + 10 into TimeOut
            put false into MsgACKed
            put false into MsgNAKed
            put false into MsgInvalid
            put false into TimeOutExceeded
            put false into HangUp
            put empty into TempBuffer
            repeat until MsgACKed or MsgNAKed or TimeOutExceeded or HangUp
              put recvUpTo(empty,90,TempBuffer) into TempBuffer
              if TempBuffer contains (CR & ACK & CR) then
                put true into MsgACKed
              else if TempBuffer contains (CR & NAK & CR) then
                put true into MsgNAKed
              else if TempBuffer contains (CR & RS & CR) then
                put true into MsgInvalid
              else if TempBuffer contains (CR & ESC & EOT & CR) then
                put true into HangUp
              else if the seconds >= timeOut then
                put true into TimeOutExceeded
              end if
            end repeat
            if MsgACKed then
              put true into OverAllSuccess
              put ", Success!" after the message
            else
              put ", Failure" after the message
            end if
            put TempBuffer after SaveBuffer
            add 1 to SendAttempts
          end repeat
        else if HangUp then
          put "CyberTelHungUp!" into last word of the message
        else
          put "LoginFailed!" into last word of the message
        end if
      end if
    end if
    -- hang up
    sendSPort EOT & CR
    put the seconds + 10 into TimeOut
    put false into EOTrcvd
    put false into TimeOutExceeded
    put empty into TempBuffer
    repeat until EOTrcvd or TimeOutExceeded
      put recvUpTo(empty,90,TempBuffer) into TempBuffer
      if TempBuffer contains (ESC & EOT & CR) then put true into EOTrcvd
      else if the seconds >= timeOut then put true into TimeOutExceeded
    end repeat
    put TempBuffer after SaveBuffer
    sendSPort "+++"
    wait 1 second
    sendSPort "ATH" & CR
    get recvUpTo(empty,60,empty)
    put it after SaveBuffer
    add 1 to OverAllAttempts
  end repeat
  closeSPort
  put empty into the message
end DialPager
 
function MsgPacket PagerID,theMessage
  global STX,ETX,EOT,ACK,CR,NAK,ESC,RS
  -- strip illegal characters from PagerID
  repeat with i = the length of PagerID down to 1
    get char i of PagerID
    if not (it is in "0123456789") then delete char i of PagerID
  end repeat
  -- left-pad to 8 digits
  -- recommended by CyberTel, not in IXO spec
  get 8 - the length of PagerID
  repeat with i = 1 to it
    put "0" before PagerID
  end repeat
  -- clean, trim theMessage
  -- NOTE: edit code below assumes numeric pager;
  --       alpha pagers accept all printable ASCII,
  --       up to 120 bytes per message.
  repeat with i = the length of theMessage down to 1
    get char i of theMessage
    if not (it is in "0123456789*#- ") then delete char i of theMessage
  end repeat
  get char 1 to 12 of theMessage
  put it into theMessage
  -- assemble message packet
  put STX & PagerID & CR & theMessage & CR & ETX into TempPacket
  -- calculate checkSum
  put 0 into checkSum
  repeat with i = 1 to the length of TempPacket
    add charToNum(char i of TempPacket) to checkSum
  end repeat
  put numToChar((checkSum div 256) mod 16 + 48) into checkSumString
  put numToChar((checkSum div 16) mod 16 + 48) after checkSumString
  put numToChar(checkSum mod 16 + 48) after checkSumString
  -- append checksum to packet
  put checkSumString & CR after TempPacket
  return TempPacket
end MsgPacket

