My resources for the project included the Linx RCI manual (April 2013 version) and the Linx RCI Utility with Ethernet v1.55.05 software...and I contacted the nice folks at Diagraph (http://www.diagraph.com) for some advice too.
Otherwise, there doesn't seem to be much info out there on this kind of work, so I wanted to post some of the tips and techniques that I used in the hope that someone else can benefit down the road.
I'm not sure of the copywrite issues, so I won't post the Linx RCI manual, but you can certainly get that from Diagraph or Linx. I did find a link to the older (Feb 2007) version of the manual: http://www.diagraph.com/Portals/0/Remote%20Communications%20Interface%20Reference%20Manual%20%5BMP65038-8%5D.pdf
Linx RCI Excel Worksheet
This worksheet contains some calculations and explanation that I used primarily to form message commands (command 19h). This includes calculating raster lengths (field and message), calculating byte lengths (field and message), and calculating the checksum.
A link to the xlsx is here: http://goo.gl/7en2S
Code for RCI over Ethernet
This project involved middleware written in VB6, so unfortunately, the code below is VB6. But I think it will still be useful to see the overall framework of how I handled communication over Ethernet.
The following are general points about the code architecture:
- On start of the application a timer is started.
- RCI commands are sent to the printer on each timer tick. Typically, the commands are polling for printer status, print count, etc. Sometimes, triggered by the user, the command is something like start printing, stop printing, or download message.
- As part of each command send routine, the winsock status is checked, and if not connected, a new connection is attempted.
- The printer's response is received using the dataArrival event. The response is parsed and displayed if needed.
- Counters are used to track how many timer ticks go by without a) the winsock successfully connecting, b) a response being received after a command was sent. After a set number or retries, the process is timed out.
- In some cases, a single function requires several commands - for instance: stop printing, delete message, download message, load message. In this case, I put the required commands into an array and run one command each timer tick. In this case, I lower the timer interval to 200ms.
Private Function ConnectedToPrinter() As Boolean
'If myWinsock.State = 6 Then
' MsgBox "Connection in process. Please try again when connected"
If myWinsock.State = 0 Then
myWinsock.LocalPort = 0
myWinsock.RemoteHost = LinxPrinter.PrinterIpAddress()
myWinsock.RemotePort = LinxPrinter.PrinterIpPort()
myWinsock.Connect
End If
If myWinsock.State <> 7 Then
ConnectedToPrinter = False
Else
ConnectedToPrinter = True
End If
End Function
Notes:
- it is important to set the LocalPort back to 0 each time you connect.
- state of 0 means 'not connected'
- LinxPrinter.PrinterIpAddress is filled from a config file containing the static IP/port for the printer
Private Sub myWinsock_DataArrival(ByVal bytesTotal As Long)
Dim strData As Byte
Dim output As String
Dim i As Long
For i = 0 To bytesTotal
myWinsock.GetData strData, vbByte
output = output & Right$("0" & Hex$(strData), 2)
Next i
MsgBox "Command: " + commandArray(0) + ", Response Bytes: " + CStr(bytesTotal) + " Response: " + output, vbInformation, "Data Back"
End Sub
Sending a command (simple)
myWinsock.SendData AddHex("", "1B02111B03EA") 'starts printing
Notes:
- AddHex is my subroutine, shown below. It's job is to convert Hex to a string
- I won't get into the actual Hex command string here, but it should look familiar from the RCI manual or my example section above.
Private Sub sendDeleteMessage()
'must stop printing first
Dim message As String
message = AddHex("", "1B021B1B01") 'note - must escape the command in this case because the command happens to be escape
message = message + "ShearX" 'Message Name
message = AddHex(message, "00000000000000000000") 'Fill Message Name out to 16 bytes
message = AddHex(message, "1B0394") 'calculated checksum of 94 manually for "ShearX message name.
myWinsock.SendData message
End Sub
Notes:
- See the link to my Linx RCI Excel Worksheet for how to calculate checksums 'manually'.
- Text can be added directly to the string that is sent via SendData, however I did create an AddChar subroutine for the purpose of dynamically calculating checksums.
- This command is unique because the command byte is the escape character...so the command must be escaped (i.e. 1B1B instead of just 1B)
Private Sub sendDownloadMessage()
Dim message As String
messageHexSum = 0 'the hex sum is added up in the AddHex and AddChar functions and is used for the checksum
' message header
message = AddHex("", "1B021901") ' ESC, STX, command, # of msgs
message = AddHex(message, "5800FB00") ' message length in bytes, in rasters (12 rasters per char for font used)
message = AddHex(message, "0600000000") ' eht, inter-raster width, print delay
message = AddChar(message, "ShearX") ' Message Name
message = AddHex(message, "00000000000000000000") ' Fill Message Name out to 16 bytes
message = AddChar(message, "25 QUALITY") ' Raster name aka Message type - get from cmd 24, doesn't match printer screen
message = AddHex(message, "000000000000") ' Fill Raster Name out to 16 bytes
' field header
message = AddHex(message, "1C00") ' field header, field type - text
message = AddHex(message, "2F00") ' field length in bytes - must calc
message = AddHex(message, "000000") ' y position, x position
message = AddHex(message, "FB00") ' field length in rasters - must calc
message = AddHex(message, "17") ' field height in drops - varies by data set name
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, "01") ' string length excluding null - must calc
message = AddHex(message, "00") ' format 1
message = AddHex(message, "00") ' format 2
message = AddHex(message, "00") ' linkage - for barcodes, etc
message = AddChar(message, "23 FH CAPS") ' Data set name
message = AddHex(message, "000000000000") ' Fill Data Set Name out to 16 bytes
message = AddChar(message, "L") ' data - just the letter L
message = AddHex(message, "00") ' null after data
message = AddHex(message, "1B03") ' ESC, ETX
message = CalcAndAddChecksum(message)
myWinsock.SendData message
End Sub
Notes:
- This is a message named ShearX that is a single character "L"
- See the link to my Linx RCI Excel Worksheet for how to calculate checksums, byte lengths, raster lengths
Private Sub sendDownloadMessage()
' working text only example
' set message
Dim message As String
Dim rasterCounts(0 To 8) As Integer
Dim byteCounts(0 To 8) As Integer
Dim xPosition(0 To 8) As Integer
Dim extraRastersBefore(0 To 8) As Integer
Dim messageBytes As Integer
Dim messageRasters As Integer
messageHexSum = 0 'the hex sum is added up in the AddHex and AddChar functions and is used for the checksum
'itemid
byteCounts(0) = CalcFieldByteInDecimal(itemNumber, False)
rasterCounts(0) = CalcFieldRasterInDecimal(itemNumber, False)
extraRastersBefore(0) = 0
xPosition(0) = CalcFieldXposition(rasterCounts, extraRastersBefore, 0)
'itemname
byteCounts(1) = CalcFieldByteInDecimal(itemName, False)
rasterCounts(1) = CalcFieldRasterInDecimal(itemName, False)
extraRastersBefore(1) = 5
xPosition(1) = CalcFieldXposition(rasterCounts, extraRastersBefore, 1)
'barcode of itemId
byteCounts(2) = CalcFieldByteInDecimal(itemNumber, True)
rasterCounts(2) = CalcFieldRasterInDecimal(itemNumber, True)
extraRastersBefore(2) = 5
xPosition(2) = CalcFieldXposition(rasterCounts, extraRastersBefore, 2)
'Coil # text
byteCounts(3) = CalcFieldByteInDecimal("Coil #:", False)
rasterCounts(3) = CalcFieldRasterInDecimal("Coil #:", False)
extraRastersBefore(3) = 5
xPosition(3) = CalcFieldXposition(rasterCounts, extraRastersBefore, 3)
'Coil Number
byteCounts(4) = CalcFieldByteInDecimal(CoilNumber, False)
rasterCounts(4) = CalcFieldRasterInDecimal(CoilNumber, False)
extraRastersBefore(4) = 5
xPosition(4) = CalcFieldXposition(rasterCounts, extraRastersBefore, 4)
'barcode of coil number
byteCounts(5) = CalcFieldByteInDecimal(CoilNumber, True)
rasterCounts(5) = CalcFieldRasterInDecimal(CoilNumber, True)
extraRastersBefore(5) = 5
xPosition(5) = CalcFieldXposition(rasterCounts, extraRastersBefore, 5)
'Count # text
byteCounts(6) = CalcFieldByteInDecimal("Count #:", False)
rasterCounts(6) = CalcFieldRasterInDecimal("Count #:", False)
extraRastersBefore(6) = 5
xPosition(6) = CalcFieldXposition(rasterCounts, extraRastersBefore, 6)
'Count sequential number field
byteCounts(7) = 54
rasterCounts(7) = CalcFieldRasterInDecimal("00000", False)
extraRastersBefore(7) = 5
xPosition(7) = CalcFieldXposition(rasterCounts, extraRastersBefore, 7)
'Date
printDate = "Date:" + printDate
byteCounts(8) = CalcFieldByteInDecimal(printDate, False)
rasterCounts(8) = CalcFieldRasterInDecimal(printDate, False)
extraRastersBefore(8) = 5
xPosition(8) = CalcFieldXposition(rasterCounts, extraRastersBefore, 8)
messageBytes = 41 + CalcTotalofArrayIntegers(byteCounts)
messageRasters = xPosition(8) + rasterCounts(8)
'messageBytes = 41 + byteCounts(0) + byteCounts(1) + byteCounts(2) + byteCounts(3) + byteCounts(4) + byteCounts(5)
'messageRasters = xPosition(5) + rasterCounts(5)
message = AddHex("", "1B021901") ' ESC, STX, command, # of msgs
message = AddHex(message, Dec2HexandPad(messageBytes, 4)) ' message length in bytes
message = AddHex(message, Dec2HexandPad(messageRasters, 4)) ' message length n rasters
message = AddHex(message, "0600000000") ' eht, inter-raster width, print delay
message = AddChar(message, "ShearX") ' Message Name
message = AddHex(message, "00000000000000000000") ' Fill Message Name out to 16 bytes
message = AddChar(message, "25 QUALITY") ' Raster name...or message type - get from cmd 24, doesn't match printer screen
message = AddHex(message, "000000000000") ' Fill Raster Name out to 16 bytes
' field header - 0 Item Id
message = AddHex(message, "1C40") ' field header, field type - text, linked, rendered
message = AddHex(message, Dec2HexandPad(byteCounts(0), 4)) ' field length in bytes
message = AddHex(message, "00" + Dec2HexandPad(xPosition(0), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(0), 4)) ' field length in rasters
message = AddHex(message, "17") ' field height in drops
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, Dec2HexandPad(Len(itemNumber), 2)) ' string length excluding null
message = AddHex(message, "00") ' format 1
message = AddHex(message, "00") ' format 2
message = AddHex(message, "02") ' linkage - zero based
message = AddChar(message, "23 FH CAPS") ' Data set name
message = AddHex(message, "000000000000") ' Fill Data Set Name out to 16 bytes
message = AddChar(message, itemNumber) ' data
message = AddHex(message, "00") ' null after data
' field header - 1 Item Name
message = AddHex(message, "1C00") ' field header, field type - text
message = AddHex(message, Dec2HexandPad(byteCounts(1), 4)) ' field length in bytes - must calc
message = AddHex(message, "00" + Dec2HexandPad(xPosition(1), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(0), 4)) ' field length in rasters - must calc
message = AddHex(message, "17") ' field height in drops
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, Dec2HexandPad(Len(itemName), 2)) ' string length excluding null - must calc
message = AddHex(message, "00") ' format 1
message = AddHex(message, "00") ' format 2
message = AddHex(message, "00") ' linkage
message = AddChar(message, "23 FH CAPS") ' Data set name
message = AddHex(message, "000000000000") ' Fill Data Set Name out to 16 bytes
message = AddChar(message, itemName) ' data
message = AddHex(message, "00") ' null after data
' field header - 2 item barcode
message = AddHex(message, "1C66") ' field header, field type - barcode, linked
message = AddHex(message, Dec2HexandPad(byteCounts(2), 4)) ' field length in bytes
message = AddHex(message, "00" + Dec2HexandPad(xPosition(2), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(2), 4)) ' field length in rasters - must calc
message = AddHex(message, "14") ' field height in drops - 20
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, "00") ' string length excluding null - no string in this field
message = AddHex(message, "00") ' format 1
message = AddHex(message, "01") ' format 2 - checksum
message = AddHex(message, "00") ' linkage - zero based
message = AddChar(message, "Code 3 of 9")
message = AddHex(message, "0000000000")
' field header - 3 Coil # text
message = AddHex(message, "1C00") ' field header, field type - text
message = AddHex(message, Dec2HexandPad(byteCounts(3), 4)) ' field length in bytes - must calc
message = AddHex(message, "00" + Dec2HexandPad(xPosition(3), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(3), 4)) ' field length in rasters - must calc
message = AddHex(message, "17") ' field height in drops
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, Dec2HexandPad(7, 2)) ' string length excluding null - must calc
message = AddHex(message, "00") ' format 1
message = AddHex(message, "00") ' format 2
message = AddHex(message, "00") ' linkage
message = AddChar(message, "23 FH CAPS") ' Data set name
message = AddHex(message, "000000000000") ' Fill Data Set Name out to 16 bytes
message = AddChar(message, "Coil #:") ' data
message = AddHex(message, "00") ' null after data
' field header - 4 Coil Number
message = AddHex(message, "1C40") ' field header, field type - text, linked, rendered
message = AddHex(message, Dec2HexandPad(byteCounts(4), 4)) ' field length in bytes
message = AddHex(message, "00" + Dec2HexandPad(xPosition(4), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(4), 4)) ' field length in rasters
message = AddHex(message, "17") ' field height in drops
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, Dec2HexandPad(Len(CoilNumber), 2)) ' string length excluding null
message = AddHex(message, "00") ' format 1
message = AddHex(message, "00") ' format 2
message = AddHex(message, "05") ' linkage - zero based
message = AddChar(message, "23 FH CAPS") ' Data set name
message = AddHex(message, "000000000000") ' Fill Data Set Name out to 16 bytes
message = AddChar(message, CoilNumber) ' data
message = AddHex(message, "00") ' null after data
' field header - 5 coil number barcode
message = AddHex(message, "1C66") ' field header, field type - barcode, linked
message = AddHex(message, Dec2HexandPad(byteCounts(5), 4)) ' field length in bytes
message = AddHex(message, "00" + Dec2HexandPad(xPosition(5), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(5), 4)) ' field length in rasters - must calc
message = AddHex(message, "14") ' field height in drops - 20
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, "00") ' string length excluding null - no string in this field
message = AddHex(message, "00") ' format 1
message = AddHex(message, "01") ' format 2 - checksum
message = AddHex(message, "04") ' linkage - zero based
message = AddChar(message, "Code 3 of 9")
message = AddHex(message, "0000000000")
' field header - 6 Count # text
message = AddHex(message, "1C00") ' field header, field type - text
message = AddHex(message, Dec2HexandPad(byteCounts(6), 4)) ' field length in bytes - must calc
message = AddHex(message, "00" + Dec2HexandPad(xPosition(6), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(6), 4)) ' field length in rasters - must calc
message = AddHex(message, "17") ' field height in drops
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, Dec2HexandPad(8, 2)) ' string length excluding null - must calc
message = AddHex(message, "00") ' format 1
message = AddHex(message, "00") ' format 2
message = AddHex(message, "00") ' linkage
message = AddChar(message, "23 FH CAPS") ' Data set name
message = AddHex(message, "000000000000") ' Fill Data Set Name out to 16 bytes
message = AddChar(message, "Count #:") ' data
message = AddHex(message, "00") ' null after data
' field header - 7 count number - sequential number field
message = AddHex(message, "1C04") ' field header, field type - sequential number
message = AddHex(message, Dec2HexandPad(byteCounts(7), 4)) ' field length in bytes - must calc
message = AddHex(message, "00" + Dec2HexandPad(xPosition(7), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(7), 4)) ' field length in rasters - must calc
message = AddHex(message, "17") ' field height in drops
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, Dec2HexandPad(5, 2)) ' string length excluding null - must calc
message = AddHex(message, "01") ' format 1 - increment by 1
message = AddHex(message, "00") ' format 2 - print go trigger
message = AddHex(message, "00") ' linkage
message = AddChar(message, "23 FH CAPS") ' Data set name
message = AddHex(message, "000000000000") ' Fill Data Set Name out to 16 bytes
message = AddHex(message, "0100") ' number of repeats - 1
message = AddHex(message, "0000") ' repeat count - 0
message = AddHex(message, "393939393900") ' end number - 99999
message = AddHex(message, "303030303100") ' start number - 00001
message = AddHex(message, "303030303100") ' current number - 00001
' field header - 8 date text
message = AddHex(message, "1C00") ' field header, field type - text
message = AddHex(message, Dec2HexandPad(byteCounts(8), 4)) ' field length in bytes - must calc
message = AddHex(message, "00" + Dec2HexandPad(xPosition(8), 4)) ' y position, x position
message = AddHex(message, Dec2HexandPad(rasterCounts(8), 4)) ' field length in rasters - must calc
message = AddHex(message, "17") ' field height in drops
message = AddHex(message, "08") ' format 3 - vertical flip
message = AddHex(message, "01") ' bold multiplier
message = AddHex(message, Dec2HexandPad(Len(printDate), 2)) ' string length excluding null - must calc
message = AddHex(message, "00") ' format 1
message = AddHex(message, "00") ' format 2
message = AddHex(message, "00") ' linkage
message = AddChar(message, "23 FH CAPS") ' Data set name
message = AddHex(message, "000000000000") ' Fill Data Set Name out to 16 bytes
message = AddChar(message, printDate) ' data
message = AddHex(message, "00") ' null after data
message = AddHex(message, "1B03") ' ESC, ETX
message = CalcAndAddChecksum(message) 'checksum
myWinsock.SendData message
End Sub
Notes:
- This is an 8 part message with text, barcodes, and sequential numbers.
- The beginning section is all for pre-calculating byte and raster lengths because those need to be included in the message header.
- I won't include all the subroutines called here, but mostly they are covered in my Linx RCI Excel Worksheet (link above). Also, the Dec2HexandPad makes sure that decimal values over 255 are sent in the correct hex order - this is a little/big endian issue.
Private Function AddHex(line As String, hexLine As String) As String
Dim remaining As String
Dim hexChar As String
remaining = hexLine
Do While Len(remaining) > 0
hexChar = Left(remaining, 2)
remaining = Right(remaining, Len(remaining) - 2)
line = line & Chr(Val("&H" & hexChar))
messageHexSum = messageHexSum + Val("&H" & hexChar) 'for checksum
Loop
AddHex = line
End Function
Private Function AddChar(line As String, charLine As String) As String
Dim remaining As String
Dim charChar As String
Dim hexChar As String
'wouldn't normally need a sub to do this - we could just added the letters to the message string, but we do it for messageHexSum
remaining = charLine
Do While Len(remaining) > 0
charChar = Left(remaining, 1)
remaining = Right(remaining, Len(remaining) - 1)
hexChar = Hex(Asc(charChar))
If Len(hexChar) = 1 Then
hexChar = "0" & hexChar
End If
line = line & Chr(Val("&H" & hexChar))
messageHexSum = messageHexSum + Val("&H" & hexChar) 'for checksum
Loop
AddChar = line
End Function
Private Function CalcAndAddChecksum(commandString As String) As String
Dim cnt As Long
Dim hexChar As String
cnt = messageHexSum - 54 'subtract two escape characters. note this sub is not designed to calc checksums for message that include more than 2 escapes
hexChar = Hex(256 - (cnt Mod 256)) 'do mod 256 and 2's complement
hexChar = Right(hexChar, 2) 'in case it's h100, we only want to send h00
commandString = commandString & Chr(Val("&H" & hexChar))
CalcAndAddChecksum = commandString
End Function
Notes:
- See the link to my Linx RCI Excel Worksheet for how to calculate checksums
Hopefully this has been useful for you. Feel free to contact me if you have a questions that you think I can help with. Also, I'm available on a contract basis if you have a project that you'd like done.
Thanks,
Nate
Hi Nate,
ReplyDeleteGreat post! I too took on a Linx communication project for some manufacturing needs.
One thing I struggled with is Editing a message, that is, setting up a pre defined message on the printer with a particular orientation (lets say inverted), and being able to only modify the message text and not the message format settings.
Deleting and recreating a message, or even overwriting a message is pretty straight forward, but modifying only the print text and nothing else seems to elude me.
Whenever I replace the message on the printer I lose the invert setting.
Any ideas? Did you run across this at all in your ventures?
Thanks!
Brett
Hi Brett,
ReplyDeleteIt's been 2.5 years since I looked at this stuff...so I'm pretty rusty. But, my guess is that you might have to send a Message Ratio command (to re-invert it) whenever you edit/overwrite the message. I remember lots of trial and error to figure this stuff out...so, Good Luck!
Nate
Nate,
ReplyDeleteBrett,
I was just forwarded the link to your post by one of Diagraph's customers who has the new 8900 series model.
If either of you would like a copy of the latest version of the Linx RCI or SCP* communication manuals they can contact Diagraph by sending an E-mail to "techsupport@diagraph.com" or directly to me "rbelanger@diagraph.com".
*The new Simplified Command Protocol" available for the new 8900 series is much easier to use since there are no longer control codes required.
As far as a 7300 is concerned RCI must still be used.
We also have two utility programs from Linx for working with RCI and SCP respectively that we can provided to you at no charge.
The RCI Utility has examples for each command. With the message upload command you can upload a complete message from the printer and it will be automatically commented when it is pasted in the work space.
The SCP utility is similar but it does no support all the commands yet.
If either of you would like a copy of the latest version of the Linx RCI or SCP* communication manuals, either utility, support E-mail "techsupport@diagraph.com". For applications support I can also be contacted directly "rbelanger@diagraph.com".
Nate, if you are interested please e-mail directly to discuss this further.
Raymond