~QlarityLib~
0xFFFF
Do not edit this file. Doing so will almost certainly destroy its integrity!
Galil_mc
@6077950 7947776 2 1.500000 0
N1.5
*2073 39218 1.52 0 sGalilCommunications
%GalilCommunication%FFFFF00FC0038FF13FFD7C7F391F9C47CFF3E007F84FFF9FFF3FFE7FFFFF
*41291 33704 1.52 0 pGalilCommunications
*74995 13477 1.52 0 1GalilCommunications
*88472 14233 1.52 0 sGMCPoll
%GMCPoll%80013FFC40025FFA58FA577A577A577A587A5FBA5FDA5FFA40023FFC8001
*102705 9359 1.52 0 pGMCPoll
*112064 6590 1.52 0 1GMCPoll
*118654 25518 1.52 1 sGMCCommand
%GMCCommandDisplay%80013FFC40025EFA5F7A403A5F7A5E7A5EFA5C025EFA5F7A40023FFC8001
%GMCCommandExec%FFFFFFFFFFFFFF7FFFBFE01FFFBFFE7FFDFFF807FDFFFEFFFFFFFFFFFFFF
*144172 16349 1.52 1 pGMCCommand
*160521 11830 1.52 1 1GMCCommand
*172351 39092 1.52 0 sGMCGauge
%GMCGauge%FFF0FE16FFF6FF96FFF6FF96FFF6FE16FFF0FF90FFF0FF90FFF0FE10FFF0
*211443 36305 1.52 0 pGMCGauge
*247748 9402 1.52 0 1GMCGauge
*257150 26904 1.52 0 sGMCPathTrace
%GMCPathTrace%3FFF73E76DDB6C1B31C77BEF77F777F737F77BEF7DDF7E3F3FFF6EEE0000
*284054 22477 1.52 0 pGMCPathTrace
*306531 11174 1.52 0 1GMCPathTrace
*317705 14136 1.52 0 sGMCAutoScale
%GMCAutoScale%F3BF1D5F7B5F71BF3FFF7FFF7FFF1FFF7FFF7FFF3FFF7DBF795F1D5FFDBF
*331841 16179 1.52 0 pGMCAutoScale
*348020 2695 1.52 0 1GMCAutoScale
*350715 28883 1.52 1 sGMCSimpleTerminal
%GMCSimpleTerminal%00007FEE7FEE7FEE00007FFE7FFE7FFE7FFE7FFE7FFE7FFE7FFE7FFE0000
*379598 31614 1.52 1 pGMCSimpleTerminal
*411212 6904 1.52 1 1GMCSimpleTerminal
*418116 21314 1.52 0 sGMCTrendChart
%GMCTrendChart%00007FFE71FE6EFE5F7E5F7E3FBE2AAA7FBE7FDE7FDE7FEC7FF27FFE0000
*439430 21716 1.52 0 pGMCTrendChart
*461146 8555 1.52 0 1GMCTrendChart
*469701 8256 0.00 0 sGMCStringAlloc
*477957 8400 0.00 0 pGMCStringAlloc
*486357 2522 0.00 0 1GMCStringAlloc
*488879 13186 1.01 0 sGMCDownload
%GMCDownload%00007FFE46FE5AFE5AFE5AFE5AFE463E7FFE7FDE6FEE54066FEE7FDE0000
*502065 8529 1.01 0 pGMCDownload
*510594 6862 1.01 0 1GMCDownload
*0 0 ZZ
#ifnot Galil_mc_src_GalilCommunications
#option Galil_mc_src_GalilCommunications
enum GMC_DataType as GMC_Position:=100, GMC_PositionError:=101, GMC_CommandedPosition:=102, ->
GMC_LatchedPosition:=103, GMC_DualEncoderPosition:=104, GMC_Torque:=105, GMC_Switches:=106, ->
GMC_AnalogInput1:=1, GMC_AnalogInput2:=2, GMC_AnalogInput3:=3, GMC_AnalogInput4:=4, ->
GMC_AnalogInput5:=5, GMC_AnalogInput6:=6, GMC_AnalogInput7:=7, GMC_AnalogInput8:=8
enum GMC_Axis as GMC_Axis_X := 'X', GMC_Axis_Y := 'Y', GMC_Axis_Z := 'Z', GMC_Axis_W := 'W', ->
GMC_Axis_E := 'E', GMC_Axis_F := 'F', GMC_Axis_G := 'G', GMC_Axis_H := 'H'
enum GMC_BevelStyle as GMC_BevelRaised, GMC_BevelSunken, GMC_BevelNone
enum GMC_SerialFormat as GMC_19200_RTS_CTS:=0, GMC_9600_RTS_CTS:=1, GMC_1200_RTS_CTS:=2, GMC_19200_NOFLOW := 3, GMC_9600_NOFLOW := 4, GMC_1200_NOFLOW := 5
#option GMC_EXTENDEDCOMMODES
enum GMC_EnetStates as GMC_Serial := 1, GMC_Pending := 2, GMC_Enet := 3, GMC_Invalid := 4
enum GMC_CommMethod as GMC_UseSerial:=0, GMC_EthernetUDP:=1, GMC_EthernetTCP:=2, GMC_None:=3
enum GMC_DispErrors as GMC_DisplayNone, GMC_DisplayCommErrors, GMC_DisplayAllErrors
enum GMC_FailureReason as GMC_FailCommunications, GMC_FailBadCommand, GMC_FailUnexpectedData
const GMC_MaxStream := 256
const GMC_MaxPendingStreams := 256
const GMC_MaxTxLen := 80
const GMC_SpecialTerm := "MG\"~@~\"\r"
const GMC_TermStr := "~@~"
const GMC_VisTimeout := 6
const message GMC_CommSuccess
const message GMC_CommFailure
const message GMC_SuspendSuccessNotify
const message GMC_SuspendCommReceive
#hidden dim GMC_CommObjPos as integer
#hidden dim GMC_CommObjHeight as integer
dim GMC_DefaultCommObj as objref GalilCommunication
#undoc func GMC_SetCommObjPos
func GMC_SetCommObjPos (obj as objref)
dim mu, md, ml, mr, nlx, nly as integer
if GMC_CommObjHeight == 0 then
GetBDFFontMetrics (ml, mr, mu, md, nlx, nly, default, font_normal)
GMC_CommObjHeight = mu+md
endif
Relocate (obj, 0, GMC_CommObjPos * GMC_CommObjHeight)
GMC_CommObjPos := GMC_CommObjPos + 1
endfunc
#undoc func GMC_VerifyCommObj
func GMC_VerifyCommObj (commObject as reference to objref GalilCommunication)
if commObject == empty then
commObject := GMC_defaultCommObj
#if _tool tool_persist(commObject) #endif
endif
endfunc
#hidden dim GMC_CurLocalPort as integer
#undoc func GMC_GetLocalPort
func GMC_GetLocalPort() returns unibyte
if GMC_CurLocalPort == 0 then
SeedRandomNum()
GMC_CurLocalPort := 10000 + integer(40000*GetRandomNum())
else
GMC_CurLocalPort := ((GMC_CurLocalPort - 9999) mod 40000) + 10000
endif
return GMC_CurLocalPort
endfunc
#undoc func GMC_CountCommands
func GMC_CountCommands(stream[] as byte) returns integer
dim posS, posN as unibyte 'Using unibyte because -1 translates as 65535
dim oldPos as unibyte
dim count as integer
'Quoted strings don't allow semicolons or quotes in them
'Which makes our life easy
posS := find (stream, oldPos, -1, ";")
posN := find (stream, oldPos, -1, "\n")
'if both are equal we have the end of our loop
while (posS <> posN) do
if (posS < posN) then
count := count + 1
oldPos := posS+1
posS := find(stream, oldPos, -1, ";")
else
count := count + 1
oldPos := posN+1
posN := find(stream, oldPos, -1, "\n")
endif
loop
'Is stream terminated?
oldPos := len(stream) - 1
if oldPos < 32000 then
'Only count semicolon as final terminator as a semicolon is auto appended if stream
'doesn't end in one. If the final character of the stream is a newline, then
'an extra null command will be appended, but won't be harmful
if (stream[oldPos] <> ';') then
count := count + 1
endif
endif
return count
endfunc
#undoc func GMCDataTypeToCommand
func GMCDataTypeToCommand(dt as GMC_DataType, axis as GMC_Axis, useCustom as boolean, ->
custom as string) returns string
dim tmpStr[1] as byte
dim tmpInt as integer
tmpStr[0] := axis
if useCustom then
'Verify 1 command only
tmpInt = GMC_CountCommands(custom)
if tmpInt < 1 then
return "TPX;" 'Make up an arbitrary command
elseif tmpInt == 1 then
return custom
else
'Shrink down to 1 command
tmpInt = find(custom, 0, -1, ";")
custom = left(custom, tmpInt)
tmpInt = find(custom, 0, -1, "\n")
custom = left(custom, tmpInt)
return custom + ";"
endif
elseif dt <= GMC_AnalogInput8 then
return "MG@AN[" + str(dt) + "];"
elseif dt == GMC_Position then
return "TP" + tmpStr + ";"
elseif dt == GMC_PositionError then
return "TE" + tmpStr + ";"
elseif dt == GMC_CommandedPosition then
return "RP" + tmpStr + ";"
elseif dt == GMC_LatchedPosition then
return "RL" + tmpStr + ";"
elseif dt == GMC_DualEncoderPosition then
return "DE?;"
elseif dt == GMC_Torque then
return "TT" + tmpStr + ";"
elseif dt == GMC_Switches then
return "TS" + tmpStr + ";"
else
return ""
endif
endfunc
#undoc func GMC_ClearException
func GMC_ClearException()
dim errTyp, errLev as unibyte
dim errStr[] as byte
GetException(errStr, errTyp, errLev)
endfunc
Define Area Object type GalilCommunication
#ToolImage "FFFFF00FC0038FF13FFD7C7F391F9C47CFF3E007F84FFF9FFF3FFE7FFFFF"
'*******************************************************************
'Object: GalilCommunication
'Author: Jeremy Richards
'Date: 27 May 2004
'
'Description: Galil motion controller communications object
'
'Version: 1.52
'Copyright 2004 QSI Corporation
'Permission is granted to copy, distribute and modify this code,
'provided that this copyright statement is preserved
'*******************************************************************
#doc object GalilCommunication
~This object forms the core of the Galil library. This object controls
~all interaction between the motion controller and the QTERM-G70. It is
~capable of communicating with a motion controller either serially or via
~ethernet. To communicate with a motion controller, create an instance of
~this object and set up its properties as appropriate. Then create other
~GMC objects and set thier commObject property to refer to this object.
~You can communicate with multiple motion controllers by creating one
~GalilCommunication object per controller. There is a limit of one controller
~per serial port on the G70 and a limit of 8 ethernet controllers that can be
~used at once.
Library Standard Source TextSingleLineBDF
Library Standard Source IPSupport
library standard source formatfunctions
#doc prop CommMethod
~The value of this property determines how this object will attempt communication
~with the controller. You can select between Serial, TCP/IP, UDP/IP or no
~communication. If you attempt to change communication methods while this object is
~attempting to open an ethernet channel to the controller, an error is generated and
~no change in communication is made. In general, you should set this property
~when designing your workspace and not change it at runtime on the terminal.
dim CommMethod as GMC_CommMethod
init CommMethod := GMC_EthernetUDP
func CommMethod (newVal as GMC_CommMethod)
if (newVal == GMC_EthernetUDP) or (newVal == GMC_EthernetTCP) then
'Attempting to switch to ethernet
if (enetState == GMC_Pending) then
'Pending and attempting to switch between TCP & UDP?
if newVal <> CommMethod then
'Attempting a swtich
throw (str(me), "Unable to switch between UDP and TCP when an ethernet connection is pending")
else
CommMethod := newVal
return 'No change
endif
elseif (enetState == GMC_Enet) then
'Have a current ethernet connection. Switching between TCP & UDP?
if newVal <> CommMethod then
'Attempting swtich between TCP & UDP
CommMethod := newVal
CloseEthernet()
OpenEthernet()
else
CommMethod := newVal
return 'No change
endif
elseif (enetState == GMC_Serial) then
CommMethod := newVal
CloseSerial()
OpenEthernet()
else
CommMethod := newVal
OpenEthernet()
endif
elseif (newVal == GMC_UseSerial) then
'Attempting to switch to serial
if (enetState == GMC_Serial) then
CommMethod := newVal
return 'No change
elseif (enetState == GMC_Pending) then
throw (str(me), "Unable to switch to serial communications while ethernet connecting is pending")
elseif (enetState == GMC_Enet) then
CommMethod := newVal
CloseEthernet()
OpenSerial()
elseif enetState == GMC_Invalid then
CommMethod := newVal
OpenSerial()
endif
elseif (newVal == GMC_None) then
'Attempting to quit communications
if (enetState == GMC_Serial) then
CommMethod := newVal
CloseSerial()
elseif (enetState == GMC_Pending) then
throw(str(me), "Unable to switch to no communication while ethernet connecting is pending")
elseif (enetState == GMC_Enet) then
CommMethod := newVal
CloseEthernet()
elseif (enetState == GMC_Invalid) then
CommMethod := newVal
endif
endif
endfunc
#doc prop IPAddress
~This property defines the IP address of the motion controller. If communicating via ethernet
~set this property to be the IP address of the motion controller. Use the industry standard
~dotted string format (i.e. "192.168.1.1") to specify the address. This property is ignored if
~CommMethod is not set to either GMC_EthernetUDP or GMC_EthernetTCP.
dim IPAddress as string
init IPAddress := "192.168.1.1"
func IPAddress (newVal as string)
IPAddress := newVal
if (enetState == GMC_SERIAL) or (enetState == GMC_Invalid) then
return
elseif (enetState == GMC_Pending) then
throw (Str(me), "Unable to change IP address while ethernet connection is pending")
else
CloseEthernet()
OpenEthernet()
endif
endfunc
#doc prop serialPort
~This property determines which serial port on the QTERM-G70 is used for communication with
~the motion controller. Set it to the G70 serial port to which the controller is attached.
~This property is ignored if CommMethod is anything except GMC_UseSerial.
dim SerialPort as comm
init SerialPort := com1
func SerialPort (newVal as comm)
if enetState == GMC_Serial then
CloseSerial()
SerialPort := newVal
OpenSerial()
else
SerialPort := newVal
endif
endfunc
#doc prop serialFormat
~This property determines the serial format that is used for communication with the motion
~controller. Select the format that the motion controller is using to ensure proper communcation.
~This property is ignored if UseEthernet is anything except GMC_UseSerial.
dim SerialFormat as GMC_SerialFormat
init SerialFormat := GMC_19200_RTS_CTS
func SerialFormat (newVal as GMC_SerialFormat)
SerialFormat := newVAl
if enetState == GMC_Serial then
VerifyCurPortSettings()
endif
endfunc
#doc prop maxRetries
~If the motion controller fails to respond to a command, the communications object can attempt
~to retry the command. Generally the motion controller will only fail to respond if it
~is extremely busy or there is a communications problem. As many commands tell the controller
~to perform some action and can cause problems if they are resent, the default number of retries
~is zero. You may increase the number of retries if your application only uses query commands.
~
Note: This property only applies if you are using GMC_UseSerial or GMC_EthernetUDP
~as your communication method. This property is ignored for TCP communication as the
~TCP/IP protocol automatically retries.
dim MaxRetries as integer
init maxRetries := 0
#doc prop timeout
~timeout defines how long (in milliseconds) this object will wait for a response from
~the controller before it decides that there has been a communications failure. If the
~timeout elapses without receiving a response from the terminal, then the data is considered
~lost and is retried if maxRetries is non-zero.
~
Note: This property only applies if you are using GMC_UseSerial or GMC_EthernetUDP
~as you communication method. This property is ignored for TCP communication as the
~TCP/IP protocol automatically deals with timeout issues.
dim timeout as integer
init timeout := 3000
func timeout (newVal as integer)
timeout := newVal
timeoutCounts := newVal / 500 '# of half second ticks
endfunc
#doc prop displayErrors
~This property determines how, if at all, errors are displayed when they occur. Errors are
~displayed in a small yellow bar near the top of the display. The
~following options are available:
~- GMC_DisplayNone
- No errors are displayed. This object will never display an error
~message.
~
- GMC_DisplayCommErrors
- Errors in communication are displayed. This includes
~communication timeouts and unexpected TCP disconnections.
~
- GMC_DisplayAllErrors
- All errors are displayed. This includes all communication
~errors as well as errors returned by the controller in response to bad commands.
dim displayErrors as GMC_DispErrors
init displayErrors := GMC_DisplayCommErrors
#doc prop unsolicitedAware
~This property determines whether this object will watch for unsolicited messages from the
~controller. If this value is true, then all data returned from the controller is examined
~for unsolicited messages (i.e. data returned from the controller that was not requested by
~this terminal. Examples include "MG" commands in the program executing on the controller).
~When unsolicited messages are detected, the UnsolicitedMessageReceived event is
~generated. You can handle this event to examine incomming unsolicited messages.
~If this property is false, then this object does not watch for unsolicited messages. Setting
~this property to false will generally lead to improved performance on the terminal, however,
~if an unsolicited message is sent, then a communications error will occur. Set this property
~to false if you want better performance on the terminal and know that the controller will not
~send unsolicited messages.
dim unsolicitedAware as boolean
init unsolicitedAware := true
func Startup()
handles msg_init
Enable(me, true)
Attach(me, default)
CommMethod(CommMethod)
RegisterMsghandler (me, MSG_TIMETICK, 25) 'Timeout ticks every 1/2 second
GMC_SetCommObjPos(me)
timeout(timeout)
if GMC_DefaultCommObj == empty then
GMC_DefaultCommObj := me
#if _Tool Tool_Persist (GMC_DefaultCommObj) #endif
endif
endfunc
private dim enetState as GMC_EnetStates
init enetState := GMC_Invalid
private func enetState (newVal as GMC_EnetStates)
enetState := newVal
endfunc
private dim thePort as comm
private func OpenEthernet()
dim IPAddr[] as byte
dim netMethod as netprotocol
if commMethod == GMC_EthernetTCP then
netMethod := NET_TCP
else
netMethod := NET_UDP
endif
#if _tool_any
if enetState <> GMC_Invalid then
tool_Trace (str(me) + " Invalid state when opening ethernet")
endif
#endif
if _ParseIPAddressString (IPAddress, IPAddr) then
NetOpen (me, NetMethod, GMC_GetLocalPort(), 51987, IPAddr)
enetState = GMC_Pending
else
enetState = GMC_Invalid
comBuf := ""
throw (str(me), "Invalid IP Address: " + IPAddress)
endif
endfunc
private func CloseEthernet()
if enetState == GMC_Enet then 'Enet is currently open
UnregisterMsgHandler (me, MSG_COMM_RECEIVE, thePort)
NetClose (thePort)
endif
enetState = GMC_Invalid
comBuf := ""
endfunc
private func CommAccept (socket as comm)
handles MSG_COMM_ACCEPT
if enetState <> GMC_Pending then
NetClose (socket) 'This should never execute
#if _tool_any
tool_trace (str(me) + "Invalid state when NetAccept received")
#endif
endif
'Unregister whatever we might have been listening to
UnregisterMsgHandler (me, MSG_COMM_RECEIVE, thePort)
thePort := socket
RegisterMsgHandler (me, MSG_COMM_RECEIVE, thePort)
enetState = GMC_Enet
'Turn echo off. Set the unsolicited bits high
SendAsciiCommandStream ("CW1;EO0;", me, 0)
endfunc
private func CommError (errcode as integer, errmsg[] as byte)
handles MSG_COMM_ERROR
MakeVisible ("Ethernet error: " + errMsg)
if (thePort > COM10) or (thePort < COM1) then
NetClose(thePort)
endif
UnregisterMsgHandler(me, MSG_COMM_RECEIVE, thePort)
enetState = GMC_INVALID
comBuf := ""
CommMethod = GMC_NONE
endfunc
private func OpenSerial()
#if _tool_any
if enetState <> GMC_Invalid then
tool_Trace (str(me) + "Invalid state when opening port")
endif
#endif
thePort := SerialPort
RegisterMsgHandler (me, MSG_COMM_RECEIVE, SerialPort)
VerifyCurPortSettings()
enetState = GMC_Serial
'Turn echo off. Set the unsolicited bits high
SendAsciiCommandStream("TC;CW1;EO0;", me, 0) 'TC is a spurious command to deal with the bad
'char that is transmitted upon bootup
endfunc
private func CloseSerial()
UnregisterMsgHandler (me, MSG_COMM_RECEIVE, SerialPort)
enetState = GMC_Invalid
comBuf := ""
endfunc
private func VerifyCurPortSettings ()
dim baud as syscmd_baud
dim flow as syscmd_flowcontrol
if SerialFormat == GMC_19200_RTS_CTS then
baud := baud_19200
flow := flowcontrol_rts_cts
elseif SerialFormat == GMC_9600_RTS_CTS then
baud := baud_9600
flow := flowcontrol_rts_cts
elseif SerialFormat == GMC_1200_RTS_CTS then
baud := baud_1200
flow := flowcontrol_rts_cts
elseif SerialFormat == GMC_19200_NOFLOW then
baud := baud_19200
flow := flowcontrol_none
elseif SerialFormat == GMC_9600_NOFLOW then
baud := baud_9600
flow := flowcontrol_none
elseif SerialFormat == GMC_1200_NOFLOW then
baud := baud_1200
flow := flowcontrol_none
else
throw (str(me), "Invalid serial format setting")
endif
VerifyComSettings (serialPort, baud, flow)
endfunc
private func VerifyComSettings (port as comm, baud as syscmd_Baud, flow as syscmd_flowcontrol)
dim dbits as syscmd_databits
dim sbits as syscmd_stopbits
dim parity as syscmd_parity
dim baudCur as syscmd_baud
dim flowCur as syscmd_flowcontrol
dim bc, dbc, pc, sbc, fc as syscmd
if port == com1 then
bc := sys_com1baud
dbc := sys_com1databits
pc := sys_com1parity
sbc := sys_com1stopbits
fc := sys_com1flowcontrol
elseif port == com2 then
bc := sys_com2baud
dbc := sys_com2databits
pc := sys_com2parity
sbc := sys_com2stopbits
fc := sys_com2flowcontrol
endif
#ifnot _TOOL 'Don't set the settings in layout view
'Check each serial port paramater and set as appropriate
GetsystemSetting (bc, baudCur, sysread_current)
if baudCur <> baud then
SetSystemSetting (bc, baud, SYSACT_DONOW)
endif
GetSystemSetting (fc, flowCur, sysread_current)
if flowCur <> flow then
SetSystemSetting (fc, flow, SYSACT_DONOW)
endif
GetSystemSetting (dbc, dbits, sysread_current)
if dbits <> databits_8 then
SetSystemSetting (dbc, databits_8, SYSACT_DONOW)
endif
GetSystemSetting (pc, parity, sysread_current)
if parity <> parity_none then
SetSystemSetting (pc, parity_none, SYSACT_DONOW)
endif
GetSystemSetting (sbc, sbits, sysread_current)
if sbits <> stopbits_1 then
SetSystemSetting (sbc, stopbits_1, SYSACT_DONOW)
endif
'Set the timeout
if (port == com1) or (port == com2) then
SetSerialTimeout (port, 1)
endif
#endif
endfunc
private dim comBuf[] as byte
private func CommReceive (data[] as byte) returns boolean
handles MSG_COMM_RECEIVE
dim pos1, pos2 as integer
'Special handling of suspended data
if suspended then
if not awaitingsuspense then
suspendedRecv := data
userDirectMsg (suspendNotify, GMC_SuspendCommReceive, suspendNotifyParm, true)
return true
endif
endif
if unsolicitedAware then
ParseForUnsolicitedMessages (data)
else
comBuf := concat (comBuf, data)
endif
label top
pos1 := find (comBuf, 0, -1, GMC_TermStr)
if pos1 > 0 then
pos2 := find (combuf, pos1, -1, ":")
if pos2 > 0 then ' we found a complete response
Parse (left (combuf, pos1))
combuf := mid(combuf, pos2+1, -1)
'goto top
endif
endif
return true
endfunc
#doc prop curResponse
~This property contains the most recent response from the controller. It is guarenteed to be
~valid during a GMC_CommSuccess message. At any other time the value of this property
~may not be valid. It is the responsibility of the object that recieves the GMC_CommSuccess
~to parse the response appropriately
#hidden dim curResponse as string
private dim recvCmdBrks[GMC_MaxTxLen] as integer
#hidden dim curFailureReason as GMC_FailureReason
private func Parse(data[] as byte)
dim cpos, qpos, oldPos as unibyte
dim count as integer
dim i, loopTerm, brkIdx, oldBrkIdx as integer
dim errs[] as integer
dim errorExists as boolean
cpos := find (data, 0, -1, ":")
qpos := find (data, 0, -1, "?")
count = 1
recvCmdBrks[0] := -1
while (cPos < 65535) or (qPos < 65535) do
if cPos < qPos then
recvCmdBrks[count] := cPos
count := count + 1
oldPos := cPos+1
cPos := find (data, oldPos, -1, ":") 'No need to find question
else
recvCmdBrks[count] := qPos
'note that an error occured
redim (errs, len(errs)+1)
errs[len(errs)-1] := count
errorExists := true
count := count + 1
oldPos := qPos+1
qPos := find (data, oldPos, -1, "?") 'No need to find colon
endif
loop
'Note the (count - 1) is because we started the count at one. We started the
'count at one because parsing the data for curResponse out is easier when we do
if pendingCmdCount <> count - 1 then
Retransmit("Unexpected controller response")
else
loopTerm := (pendingPktTail + 1) mod GMC_MaxPendingStreams
i := tailIdx
brkIdx := 0
oldBrkIdx := 0
if not errorExists then
'slightly faster loop for the common case of no errors!
while i <> loopTerm do
brkIdx := brkIdx + cmdCount[i]
curResponse := mid(data, recvCmdBrks[oldBrkIdx]+1, recvCmdBrks[brkIdx] - recvCmdBrks[oldBrkIdx]-1)
UserDirectMsg (notify[i], GMC_CommSuccess, notifyCodes[i], true)
i := (i+1) mod GMC_MaxPendingStreams
oldBrkIdx := brkIdx
loop
else
'slower loop where we have to see what errors exist and what to do about it!
cpos := 0 'reuse cpos as an array index
redim (errs, len(errs)+1) 'give ourselves an extra padding index
while i <> loopTerm do
brkIdx := brkIdx + cmdCount[i]
curResponse := mid(data, recvCmdBrks[oldBrkIdx]+1, recvCmdBrks[brkIdx] - recvCmdBrks[oldBrkIdx]-1)
if (oldBrkIdx+1 <= errs[cPos]) and (brkIdx >= errs[cPos]) then 'this command stream contained the error
curFailureReason := GMC_FailBadCommand
UserDirectMsg (notify[i], GMC_CommFailure, notifyCodes[i], true)
cPos := cPos + 1
if displayErrors == GMC_DisplayAllErrors then
MakeVisible (str(notify[i]) + ": Bad command")
endif
else
UserDirectMsg (notify[i], GMC_CommSuccess, notifyCodes[i], true)
endif
i := (i+1) mod GMC_MaxPendingStreams
oldBrkIdx := brkIdx
loop
endif
ClearPendingPktInfo()
endif
endfunc
private func ReceiveFail()
dim i, loopTerm as integer
loopTerm := (pendingPktTail + 1) mod GMC_MaxPendingStreams
i := tailIdx
curFailureReason := GMC_FailCommunications
curResponse := ""
while i <> loopTerm do
UserDirectMsg (notify[i], GMC_CommFailure, notifyCodes[i], true)
i := (i+1) mod GMC_MaxPendingStreams
loop
ClearPendingPktInfo()
endfunc
private func ClearPendingPktInfo()
if awaitingResponse then
awaitingResponse := false
xmitbuf := mid (xmitBuf, pendingPktLen, -1)
tailIdx := (pendingPktTail + 1) mod GMC_MaxPendingStreams
endif
TransmitStream()
endfunc
private dim xmitBuf[] as byte
private dim lengths[GMC_MaxPendingStreams] as integer
private dim cmdCount[GMC_MaxPendingStreams] as integer
private dim notify[GMC_MaxPendingStreams] as objref
private dim notifyCodes[GMC_MaxPendingStreams] as integer
private dim headIdx as integer
private dim tailIdx as integer
#doc func SendAsciiCommandStream
~Use this command to send an arbitrary stream of commands to the controller. When the
~controller has responed, this object will send a GMC_CommSuccess or a GMC_CommFailure
~message to the notifyObj with the notifyCode as the paramater.
#param stream: A semicolon separated list of commands to send to a Galil motion controller
#param notifyObj:The object that will receive notification once the controller has responeded to the commands
#param notifyCode:This value will be sent to the notifyObj in the notification message. It is primarily useful for objects that may poll different values at different times so that it can differenciate to which query it is receiving a response.
func SendAsciiCommandStream(stream[] as byte, notifyObj as objref, notifyCode as integer) returns boolean
return SendAsciiCommandStreamCounted (stream, notifyObj, notifyCode, GMC_CountCommands(stream))
endfunc
#doc func SendAsciiCommandStreamCounted
~Use this command to send an arbitrary stream of commands to the controller. When the
~controller has responed, this object will send a GMC_CommSuccess or a GMC_CommFailure
~message to the notifyObj with the notifyCode as the paramater. This function is
~faster than SendAsciiCommandStream. Call this function if you know exactly
~how many commands are in stream. You generally do that by calling the global function
~GMC_CountCommands.
#param stream: A semicolon separated list of commands to send to a Galil motion controller
#param notifyObj:The object that will receive notification once the controller has responeded to the commands
#param notifyCode:This value will be sent to the notifyObj in the notification message. It is primarily useful for objects that may poll different values at different times so that it can differenciate to which query it is receiving a response.
#param count:The number of commands in the command stream. This value must correctly identify the number of commands in the stream, or a communications failure will occur. You can obtain the proper value by calling GMC_CountCommands on your stream of commands.
func SendAsciiCommandStreamCounted (stream[] as byte, notifyObj as objref, ->
notifyCode as integer, count as integer) returns boolean
dim strLen as integer
dim nextHeadIdx as integer
'Cannot transmit if using no comm method
if commMethod == GMC_None then
return false
endif
nextHeadIdx := (headIdx + 1) mod GMC_MaxPendingStreams
strlen = len(stream)
if len(stream) >= GMC_MaxTxLen then
return false
elseif (strlen <= 0) or (nextHeadIdx == tailIdx) then
return false
endif
' Add terminator if necessary
if stream[strlen-1] <> ';' then
stream := stream + ";"
strLen := strLen + 1
endif
xmitBuf := concat(xMitBuf, stream)
lengths[headIdx] := strLen
cmdCount[headIdx] := count
notify[headIdx] := notifyObj
notifyCodes[headIdx] := notifyCode
headIdx := nextHeadIdx
TransmitStream()
return true
endfunc
private dim awaitingResponse as boolean
private dim pendingPktTail as integer
private dim pendingPktLen as integer
private dim pendingPktTimeIdx as integer
private dim pendingRetries as integer
private dim pendingCmdCount as integer
private dim curTimeIdx as integer
private dim timeoutCounts as integer
private dim isVisible as boolean
init isVisible := false
private dim visMessage as string
private dim visTimeIdx as integer
private func TransmitStream ()
dim sum, prevSum as integer
dim prevTail as integer
dim curTail as integer
dim cmdSum, prevCmdSum as integer
'Reasons why we might not be able to transmit
if (enetState <> GMC_Serial) and (enetState <> GMC_Enet) then 'Not connected
return
endif
if awaitingResponse then
return 'Currently waiting for a response.
endif
if suspended then
'Are we suspended for special communications?
if awaitingSuspense then
awaitingSuspense := false
UserDirectMsg (suspendNotify, GMC_SuspendSuccessNotify, suspendNotifyParm, false)
endif
return
endif
if headIdx == tailIdx then 'Nothing to transmit
return
endif
curTail := tailIdx
sum := lengths[curTail]
cmdSum := cmdCount[curTail]
do
prevTail := curTail
prevSum := sum
prevCmdSum := cmdSum
curTail := (curTail + 1) mod GMC_MaxPendingStreams
sum := sum + lengths[curTail]
cmdSum := cmdSum + cmdCount[curTail]
loop while (curTail <> headIdx) and (sum < GMC_MaxTxLen)
'Since the loop always takes us one iteration too far, use prevTail and prevSum
awaitingResponse := true
transmit (thePort, left(xmitBuf, prevSum) + GMC_SpecialTerm, false)
pendingPktTail := prevTail
pendingPktLen := prevSum
pendingRetries := 0
pendingPktTimeIdx := curTimeIdx
pendingCmdCount := prevCmdSum
endfunc
private func Retransmit (reason as string)
if commMethod == GMC_EthernetTCP then
return 'No Retry on TCP
endif
pendingRetries := pendingRetries + 1
if pendingRetries > maxRetries then
MakeVisible(reason)
ReceiveFail()
else
transmit (thePort, left(xmitBuf, pendingPktLen) + GMC_SpecialTerm, false)
pendingPkttimeIdx := curTimeIdx
MakeVisible(reason + ": Retry " + str(pendingRetries) + " / " + str(maxRetries))
endif
endfunc
private func MakeVisible(data as string)
if displayErrors == GMC_DisplayNone then
return
endif
if not isVisible then
isVisible := true
Resize (me, GetPosInfo(default, Get_Width), GMC_CommObjHeight)
endif
visMessage := str(me) + ": " + data
visTimeIdx := curTimeIdx
SendToFront(me)
Rerender(me)
endfunc
private func MakeInvisible()
isVisible := false
Resize (me, 0,0)
endfunc
private func Timetick ()
handles MSG_TIMETICK
curTimeIdx := curTimeIdx + 1
if awaitingResponse then
if commMethod <> GMC_EthernetTCP then
if curTimeIdx >= pendingPktTimeIdx + timeoutCounts then
Retransmit("Response Timeout")
endif
endif
endif
if IsVisible then
if curTimeIdx >= visTimeIdx + GMC_VisTimeout then
MakeInvisible()
endif
endif
endfunc
private func Draw ()
handles MSG_DRAW
SetFgColor (RGB_RED)
SetBGColor (RGB_Yellow)
_TextRect (0, GetPosInfo(me, Get_Y), GetPosInfo(default, GET_Width), GMC_CommObjHeight, visMessage, default, HA_CENTER, VA_CENTER)
endfunc
private func ParseForUnsolicitedMessages(data[] as byte)
'This function will update comBuf with the solicited portion of the message.
'Unsolicited portions will be added to unsolicitedBuf. The UnsolicitedMessageReceived
'event will be called if a complete message is received
dim i, max as integer
dim oldPos as integer
dim state, oldState as boolean
max = len(data)-1
for i = 0 to max
state := ( (data[i] and byte(0x80)) <> 0)
if state <> oldState then
if oldState then
AddUnsolicited (mid (data, oldPos, i-oldPos), i-oldPos-1)
else
comBuf := comBuf+ mid(data, oldPos, i-oldPos)
endif
oldState := state
oldPos := i
endif
next
'Now add remainder
if state then
AddUnsolicited (mid (data, oldPos, i-oldPos), i-oldPos-1)
else
comBuf := comBuf+ mid(data, oldPos, i-oldPos)
endif
endfunc
private func AddUnsolicited (data[] as byte, leng as integer)
dim i as integer
dim p1, p2, oldPos as unibyte
for i = 0 to leng
data[i] := data[i] and byte(0x7F)
next
UnsolicitedMessageReceived(data)
endfunc
#doc override UnsolicitedMessageReceived
#param msg:This is the text of the unsolicited message.
~This event is called whenever unsolicited messages are received from the controller. The
~msg paramater contains the actual message. Unsolicited messages are NOT parsed
~for content, but rather are given immediately to this funciton. This means that the user
~is responsible for parsing the message. It is possible that two unsolicited messages may
~be passed to this event at once. It is also possible that a single unsolicited message may
~be split in to two calls to this event.
func UnsolicitedMessageReceived(msg as string)
return
endfunc
private dim suspended, awaitingSuspense as boolean
private dim suspendNotify as objref
private dim suspendNotifyParm as integer
#doc prop suspendedRecv
~This property holds the most recent bit of data received by this object once it has been
~suspended.
#hidden dim suspendedRecv[] as byte
#doc func SuspendCommunications
~This function is intended for internal use by advanced GMC objects. Do not use it, unless
~you are implementing a special object that has special communication needs.
~This function breaks off normal communication with the motion controller. Communication
~requests are queued, until communications are resumed. This function returns true if
~the request has been successfully processed, however, you MUST NOT assume that the
~communications have actually been suspended until the object indicated by the notify
~paramater has received the GMC_SuspendSuccessNotify message.
~Once this object has been suspended, any incomming serial data will be stored in the property
~suspendedRecv and notify will be informed of the data reception via a
~GMC_SuspendCommReceive message.
#param notify:Indicates the object that requested the suspension. This object will receive the GMC_SuspendSuccessNotify message once the suspension has taken effect
#param parm:This value will be passed to notify as the paramater to the GMC_SuspendSuccessNotify message.
func SuspendCommunications(notify as objref, parm as integer) returns boolean
if (enetState <> GMC_Serial) and (enetState <> GMC_Enet) then 'Not connected
return false
endif
suspended := true
awaitingSuspense := true
suspendNotify := notify
suspendNotifyParm := parm
TransmitStream()
return true
endfunc
#doc func ResumeCommunications
~This function is intended for internal use by advanced GMC objects. Do not use it, unless
~you are implementing a special object that has special communication needs.
~This function resumes normal communications after the communications had been suspended
func ResumeCommunications()
suspended := false
awaitingSuspense := false
suspendNotify := empty
suspendNotifyParm := 0
TransmitStream()
endfunc
#doc func IsSuspended
~This function is intended for internal use by advanced GMC objects. Do not use it, unless
~you are implementing a special object that has special communication needs.
~This function returns true if communications are currently suspended, false otherwise.
func IsSuspended() returns boolean
return (suspended) and (not awaitingSuspense)
endfunc
#doc func SuspendTransmit
~This function is intended for internal use by advanced GMC objects. Do not use it, unless
~you are implementing a special object that has special communication needs.
~This function is used to perform a special transmission when communications are suspended with
~the motion controller. This can only be used when communication is suspended. It will return
~false if communication is not suspended. Otherwise it transmits the data and returns true.
#param data:This data will be transmitted to the controller without respect of formatting. As the communications object does not examine this data, it will not expect a response of any sort from the controller.
func SuspendTransmit (data[] as byte) returns boolean
if (not suspended) or (awaitingSuspense) then
return false
endif
if (enetState <> GMC_Serial) and (enetState <> GMC_Enet) then 'Not connected
return false
endif
transmit (thePort, data, false)
return true
endfunc
Enddef
#endif
#endfile
Te d td d d } ÐD } d À } e À } f À } g À } h À }
i À } j À } Î À } Í À } Ì À } Ë À } Ê À } É À } È À } Ç d d } ÐD }
X À }
Y À }
Z À }
W À }
E À }
F À }
G À }
H d d } ÐD } À } À }
d d } ÐD } ÎÆÍÏÏ À } ÆÉÏÏ À } ÎÍÏÏ À } ÎÆÍÏÏ À } ÆÉÏÏ À } ÎÍÏÏ d
td
d d } ÐD }
À } À } À } d d } ÐD }
À } À } À } d
d
}
ÐD
}
À
}
À
}
d d } ÐD } À } À } d d àD }
d àD } d àD } P d àD } C ²žÝ¿Ýò d àD } C ¿ d àD } d d àD 0? } d àD 0? } d àD 0? } d àD 0? } d d d e ÀD } ÐD > d e ÀD } ÐD > d ÀD } ÐD à> } d d! d" W" }" " }" ÐD" à>" " d# ÀD# }# À# }# À# }# À# }# À# }# À# }# ÐD# ># d$ d% ^% }% Ð% % ^% d&