Attachment 'COM.v1.0.txt'

Download

   1 :Class COM
   2 ⍝ Class for Asynchronous Serial Communication
   3 
   4 ⍝ List of methods:
   5 ⍝ Open         = Open the serial port
   6 ⍝ SendReceive  = Send the bytes and return the bytes received
   7 ⍝ Close        = Close the serial port
   8 
   9 ⍝ Version 1.0 January 2016
  10 
  11 ⍝ .Net assemblies:
  12 :Using                                               
  13 :Using System,mscorlib.dll
  14 :Using System.Net,System.dll
  15 :Using System.Net.Sockets,System.dll
  16 :Using System.IO.Ports,System.dll
  17 
  18     (⎕IO ⎕ML ⎕WX)←1 3 3  ⍝ System variables
  19 
  20     :Field Public spObj       ⍝ SerialPort object
  21     :Field Public Type←'COM'
  22 
  23   ⍝ Used internally
  24     :Field _buffer         ⍝ empty Byte[] array same size as _BufferSize
  25     :Field _stream         ⍝ spObj.BaseStream
  26 
  27   ⍝ YOU CAN CHANGE THE VALUE OF THE FOLLOWING 5 FIELDS AFTER
  28   ⍝ INTANTIATING THE CLASS BUT BEFORE CALLING THE 'Open' METHOD.
  29 
  30     ⍝ Size of the Input and Output Buffers in Bytes. Should be
  31     ⍝ as Big as the Largest Number of Bytes to Send or Receive.
  32     :Field Public _BufferSize←100
  33 
  34     ⍝ Time in milliseconds to Wait for a port that is not Active (Receiving Nothing).
  35     ⍝ If exceeded Windows Stop Waiting and returns the Control to the Calling Application.
  36     :Field Public _PortTimeOut←250
  37 
  38     ⍝ Time in milliseconds that Windows Waits between two Bytes to Arrive.
  39     ⍝ If exceeded Windows Stop Waiting and Returns the Control to the Calling Application.
  40     :Field Public _LastByteTimeOut←15
  41 
  42     ⍝ When used for serial half duplex communication you will need a small delay
  43     ⍝ between sending and receiving to let the serial port switch from receiving to sending.
  44     ⍝ Rule of thumb for the delay in millisec: 1E6÷(Baud Rate of serial device)
  45     :Field Public _SwitchingDelay←50
  46 
  47     ⍝ Default Flow Control
  48     ⍝ (0=None, 1=XOn/XOff Software, 2=RequestToSend, 3=RequestToSendXOnXOff)
  49     :Field Public _FlowControl←0
  50 
  51     ∇ Init0
  52       :Access Public
  53       :Implements Constructor
  54       spObj←⎕NULL
  55 
  56 
  57     ∇ r←Open(PortNo BaudRate DataBits Parity StopBits);BaudRateOK
  58       :Access Public
  59      ⍝ Open a Serial Communication Port and Obtain a .Net Serial Port Object (spObj)
  60 
  61      ⍝ PortNo   = Communication Port Number (ex.: 1, 2, 3, 4, etc.)
  62      ⍝ BaudRate = Baud Rate (300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200)
  63      ⍝ DataBits = Number of Data Bits (4, 5, 6, 7, 8)
  64      ⍝ Parity   = Number for Parity (0 = None, 1 = Odd, 2 = Even, 3 = Mark, 4 = Space)
  65      ⍝ StopBits = Number of Stop Bits (1, 1.5, 2)
  66 
  67      ⍝ r[1] = 1 for Success, 0 for Failure
  68      ⍝ r[2] = Literal Error if Failure
  69      
  70      ⍝ Check if the port is already opened
  71       :If ⎕NULL≢spObj
  72       :AndIf spObj.IsOpen
  73           r←0 'Error Opening: Port already open' ⋄ →0
  74       :EndIf
  75      
  76       BaudRateOK←300 600 1200 2400 4800 9600 19200 38400 57600 76800 115200
  77      
  78      ⍝ Verify that there is some Serial Port(s) available on the computer
  79       :If 0=⍴SerialPort.GetPortNames
  80           r←0 'COM Error Opening: No Serial Port Available' ⋄ →0
  81       :EndIf
  82      
  83      ⍝ Check if the PortNo is available on the computer
  84       :If ~PortNo∊{⍎(⍵∊⎕D)/⍵}¨SerialPort.GetPortNames
  85           r←0 'COM Error Opening: The Comm Port Number is Not Valid' ⋄ →0
  86       :EndIf
  87      
  88      ⍝ Validate the rest
  89       :If ~BaudRate∊BaudRateOK ⋄ r←0 'Error Opening: The Baud Rate is Not Valid' ⋄ →0 ⋄ :EndIf
  90       :If ~DataBits∊4 5 6 7 8 ⋄ r←0 'Error Opening: The Data Bits are Not Valid' ⋄ →0 ⋄ :EndIf
  91       :If ~Parity∊0 1 2 3 4 ⋄ r←0 'Error Opening: The Parity is Not Valid' ⋄ →0 ⋄ :EndIf
  92       :If ~StopBits∊1 1.5 2 ⋄ r←0 'Error Opening: The Stop Bits are Not Valid' ⋄ →0 ⋄ :EndIf
  93       :If ~_FlowControl∊0 1 2 3 ⋄ r←0 'Error Opening: The Flow Control is Not Valid' ⋄ →0 ⋄ :EndIf
  94      
  95       :Trap 0
  96         ⍝ Construct a SerialPort Object
  97           spObj←⎕NEW SerialPort(⊂'COM',⍕PortNo)
  98           spObj.(ReadBufferSize WriteBufferSize)←_BufferSize  ⍝ Set the Sizes of the Input and Output Buffers
  99           spObj.(ReadTimeout WriteTimeout)←_PortTimeOut       ⍝ Set the Time-Outs of the communication
 100           spObj.BaudRate←BaudRate                             ⍝ Set the Baud Rate
 101           spObj.DataBits←DataBits                             ⍝ Set the DataBits
 102           spObj.Parity←Parity                                 ⍝ Set the Parity
 103           spObj.StopBits←(-⎕IO)+(0 1 2 1.5)⍳StopBits          ⍝ Set the StopBits
 104           spObj.Handshake←_FlowControl                        ⍝ Set the FlowControl
 105      
 106         ⍝ Open the Port and empty the buffers
 107           spObj.Open
 108           spObj.DiscardInBuffer
 109           spObj.DiscardOutBuffer
 110      
 111         ⍝ Success
 112           _buffer←Array.CreateInstance(Byte _BufferSize)      ⍝ empty Byte[] array same size as _BufferSize
 113           _stream←spObj.BaseStream
 114           r←1
 115      
 116       :Else
 117           {}Close
 118         ⍝ Show the error.
 119           :If 90=⎕EN
 120             ⍝ .Net Error
 121               r←0('COM Error Opening: ',⎕EXCEPTION.GetBaseException.Message)
 122           :Else
 123             ⍝ APL Error
 124               r←0(1⊃⎕DM),': ',{(' '=1↑⍵)↓((1↓a,0)∨a←' '≠⍵)/⍵}(2⊃⎕DM)
 125           :EndIf
 126       :EndTrap
 127 
 128 
 129     ∇ r←{option}SendReceive bytes;count;data;lastByte;qtyBytes;task;taskResult
 130       :Access Public
 131      ⍝ Write Bytes Asynchronously to a Serial Port.
 132      ⍝ Normally the last bytes received will be the one that has time-out on _PortTimeOut.
 133      ⍝ Optionnally you can specified the value of the last byte (lastByte) or the quantity
 134      ⍝ of bytes to be received (qtyBytes) so the communication will be faster.
 135      
 136      ⍝ bytes  = Bytes (as Numbers 0 to 255) to be Written to the Communication Port
 137      ⍝ option = positive number -> lastByte to be received
 138      ⍝        = negative number -> qtyBytes to be received
 139      ⍝        = absent          -> will time-out on _PortTimeOut
 140      
 141      ⍝ r[1] = 1 for Success, 0 for Failure
 142      ⍝ r[2] = Response if Success, Literal Error if Failure
 143      
 144      ⍝ Check if the port is already opened
 145       :If ⎕NULL≡spObj
 146       :OrIf ~spObj.IsOpen
 147           r←0 'COM Error Sending: Port not open' ⋄ →0
 148       :EndIf
 149      
 150      ⍝ Parse the option
 151       :If 0≠⎕NC'option'
 152           :If option<0
 153               qtyBytes←|option
 154           :Else
 155               lastByte←option
 156           :EndIf
 157       :EndIf
 158      
 159       :Trap 0
 160         ⍝ Write the data:
 161           :If _stream.CanWrite
 162           :AndIf spObj.IsOpen
 163      
 164             ⍝ Purge the Input and Output Buffers
 165               spObj.DiscardInBuffer
 166               spObj.DiscardOutBuffer
 167      
 168             ⍝ Write the bytes to serial port
 169               task←_stream.WriteAsync(bytes 0(⍬⍴⍴,bytes))
 170      
 171             ⍝ Wait asynchronously for the task to complete
 172               :If 0=⎕TSYNC{task.Wait ⍵}&(_PortTimeOut)
 173                   r←0 'COM Error Sending: Task did not complete sending the bytes' ⋄ →0
 174               :Else
 175                 ⍝ Success
 176                   task.Dispose
 177               :EndIf
 178           :Else
 179               r←0 'COM Error Sending: Serial Port no longer available to send data' ⋄ →0
 180           :EndIf
 181      
 182         ⍝ Read the data received:
 183           data←⍳0 ⋄ taskResult←1
 184      
 185         ⍝ Switching delay
 186           ⎕TSYNC ⎕DL&_SwitchingDelay÷1000
 187      
 188           :If _stream.CanRead
 189           :AndIf spObj.IsOpen
 190      
 191               :While taskResult
 192                   task←_stream.ReadAsync(_buffer 0 _BufferSize)    ⍝ _buffer will be filled with the bytes received
 193                                                                    ⍝ Waiting asynchronously for the task to complete
 194                   :If taskResult←⎕TSYNC{task.Wait ⍵}&(_PortTimeOut)   ⍝ taskResult=0 when no data is received
 195                       count←task.Result                            ⍝ count = number of byte(s) received
 196                       data,←count↑⌷_buffer                         ⍝ cumul the bytes
 197      
 198                       :If 0≠⎕NC'lastByte'
 199                       :AndIf lastByte=¯1↑data     ⍝ Check if lastByte has been received
 200                           →END
 201                       :EndIf
 202      
 203                       :If 0≠⎕NC'qtyBytes'
 204                       :AndIf qtyBytes=⍴,data      ⍝ Check if the total number of bytes has been received
 205                           →END
 206                       :EndIf
 207      
 208                       task.Dispose
 209                       ⎕TSYNC ⎕DL&_LastByteTimeOut÷1000   ⍝ Wait for last byte to time-out and loop again
 210                   :EndIf
 211               :EndWhile
 212           :Else
 213               r←0 'COM Error Sending: Serial port no longer available to receive data' ⋄ →0
 214           :EndIf
 215      
 216      END: ⍝ Prepare the result
 217           :If 0≠⍴,data
 218               r←1 data
 219           :Else
 220               r←0 'COM: No data received'
 221           :End
 222      
 223       :Else
 224         ⍝ Show the error.
 225           :If 90=⎕EN
 226            ⍝ .Net Error
 227               r←0('COM Error Sending: ',⎕EXCEPTION.GetBaseException.Message)
 228           :Else
 229             ⍝ APL Error
 230               r←0(1⊃⎕DM),': ',{(' '=1↑⍵)↓((1↓a,0)∨a←' '≠⍵)/⍵}(2⊃⎕DM)
 231           :EndIf
 232       :EndTrap
 233 
 234 
 235     ∇ r←Close
 236       :Access Public
 237      ⍝ Close the Serial Port
 238      ⍝ The best practice for any application is to wait for some amount of time after calling the Close method
 239      ⍝ before attempting to call the Open method, as the port may not be closed instantly.
 240 
 241      ⍝ r[1] = 1 for Success, 0 for Failure
 242      ⍝ r[2] = Literal Error if Failure
 243      
 244       :If ⎕NULL≡spObj ⋄ r←0 'Error Closing: Already Closed' ⋄ →0 ⋄ :EndIf
 245      
 246       :Trap 0
 247           spObj.Close
 248           spObj.Dispose
 249           r←1
 250       :Else
 251          ⍝ Show the error.
 252           :If 90=⎕EN
 253            ⍝ .Net Error
 254               r←0('COM Error Closing: ',⎕EXCEPTION.GetBaseException.Message)
 255           :Else
 256             ⍝ APL Error
 257               r←0(1⊃⎕DM),': ',{(' '=1↑⍵)↓((1↓a,0)∨a←' '≠⍵)/⍵}(2⊃⎕DM)
 258           :EndIf
 259       :EndTrap
 260      
 261       spObj←⎕NULL
 262 
 263 
 264 :EndClass

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2016-01-08 11:47:49, 10.5 KB) [[attachment:COM.v1.0.txt]]
 All files | Selected Files: delete move to page

You are not allowed to attach a file to this page.