Gateway Serial Module writeBytes hex

Hi All,

I've been using my best googling skills into getting this working but it evades me. I am trying to open a serial port, write a handful of bytes in hex, listen to the port, then close the port.

I cannot get past writing the bytes. My best guess is I'm having trouble satisfying the java bytearray requirements. This is the code I'm using in the interpreter.

system.serial.configureSerialPort(\
	port="COM7",\
	bitRate=system.serial.BIT_RATE_9600,\
	dataBits=system.serial.DATA_BITS_8,\
	handshake=system.serial.HANDSHAKE_NONE,\
	hardwareFlowControl=False,\
	parity=system.serial.PARITY_NONE,\
	stopBits=system.serial.STOP_BITS_1)
	
system.serial.closeSerialPort("COM7")

system.serial.openSerialPort("COM7")

system.serial.writeBytes("COM7","\xCA\x00\x01\x70\x00\x8E")

system.serial.closeSerialPort("COM7")

Firing the script returns an angry traceback that the serial module trial is expired. Restarting the trial via gateway webpage and relaunching the designer have no effect. Not sure what to do here...

Java Traceback:
Traceback (most recent call last):
  File "<input>", line 14, in <module>
	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.checkTrialExpired(SerialScriptModule.java:154)

	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.writeBytes(SerialScriptModule.java:616)

	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.writeBytes(SerialScriptModule.java:599)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

	at java.base/java.lang.reflect.Method.invoke(Unknown Source)

com.inductiveautomation.ignition.modules.serial.scripting.TrialExpiredException: com.inductiveautomation.ignition.modules.serial.scripting.TrialExpiredException: Serial module trial period has expired.


	at org.python.core.Py.JavaError(Py.java:547)

	at org.python.core.Py.JavaError(Py.java:538)

	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:192)

	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:208)

	at org.python.core.PyObject.__call__(PyObject.java:477)

	at org.python.core.PyObject.__call__(PyObject.java:481)

	at org.python.pycode._pyx6.f$0(<input>:16)

	at org.python.pycode._pyx6.call_function(<input>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	at org.python.core.PyCode.call(PyCode.java:18)

	at org.python.core.Py.runCode(Py.java:1687)

	at org.python.core.Py.exec(Py.java:1731)

	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:277)

	at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:130)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:611)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:599)

	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)

	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

	at java.base/java.lang.Thread.run(Unknown Source)

Caused by: com.inductiveautomation.ignition.modules.serial.scripting.TrialExpiredException: Serial module trial period has expired.

	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.checkTrialExpired(SerialScriptModule.java:154)

	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.writeBytes(SerialScriptModule.java:616)

	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.writeBytes(SerialScriptModule.java:599)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

	at java.base/java.lang.reflect.Method.invoke(Unknown Source)

	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)

	... 19 more

Traceback (most recent call last):
  File "<input>", line 14, in <module>
	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.checkTrialExpired(SerialScriptModule.java:154)

	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.writeBytes(SerialScriptModule.java:616)

	at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.writeBytes(SerialScriptModule.java:599)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

	at java.base/java.lang.reflect.Method.invoke(Unknown Source)

com.inductiveautomation.ignition.modules.serial.scripting.TrialExpiredException: com.inductiveautomation.ignition.modules.serial.scripting.TrialExpiredException: Serial module trial period has expired.

It's because this isn't a byte array. It is a jython string whose codepoints match the bytes you want to send. You need to construct your byte array with jython's jarray module.

(Beware: java byte arrays have signed 8-bit values.)

Check the designer's menu (help -> about) and see your license status for the various modules you have installed. Are you sure you reset the trial on the correct gateway?

Dang you are fast.

My apologies for my ignorance. I had a feeling I was murdering the requirements for the array.

Would you be able to walk me through constructing a simple 2-byte array that can handle unsigned values and is a valid argument for system.serial.writeBytes ?
So far I've been taking guidance from these forum posts but am lost as how to construct the array:
https://forum.inductiveautomation.com/t/serial-module-possible-error/5920/11
https://forum.inductiveautomation.com/t/solved-get-nmea-sentence-system-serial/48783

Again, my apologies for the ignorance. Any help is appreciated. Thank you!

The pythonic solution is something like this:

import jarray

unsignedBytes = [0xCA, 0x00, 0x01, 0x70, 0x00, 0x8E]
signedBytes = [((x + 0x80) & 0xff) - 0x80 for x in unsignedBytes]
byteArray = jarray.array(signedBytes, 'b')

Rather than rely on jython math, I usually do this:

import jarray
from java.lang import Long

unsignedBytes = [0xCA, 0x00, 0x01, 0x70, 0x00, 0x8E]
signedBytes = [Long.valueOf(x).byteValue() for x in unsignedBytes]
byteArray = jarray.array(signedBytes, 'b')

This latter implementation will simply chop the upper bits off of whatever you feed it.

Apparently, that no longer works. Jython is turning the Long back into its own long. ):

1 Like

Thanks for the help! That snippet got me off to the races. I'll post more to this topic when my example is more complete... or if I'm completely lost :slightly_smiling_face:

Thanks again!

1 Like

Using bytearray you can get the same affect without the '0x' in the strings/values.

msg = '4142'

data = bytearray.fromhex(msg)

print data  #should print 'AB'
1 Like

Hey All,

For anyone watching this thread, these are the commands I ended up using to sign and unsign byte arrays, and pipe those arrays to and from the serial port. Device documentation was all in raw hex so that's the reason for working back and forth. I'm sure there's prettier code out there but this does work.

commands = [
    [0xCA, 0x00, 0x01, 0x70, 0x00, 0x8E],  # Current Setpoint
    [0xCA, 0x00, 0x01, 0x10, 0x00, 0xEE],  # Process Flowrate
]

...

for command, tag_path in zip(commands, tag_paths):
    # Convert unsigned byte array to signed byte array
    signedCommandBytes = [((x + 0x80) & 0xff) - 0x80 for x in command]
    commandByteArray = jarray.array(signedCommandBytes, 'b')

    # Send the message
    system.serial.writeBytes("COM7", commandByteArray)

    # Receive the reply
    chillReply = system.serial.readBytes("COM7", 9)

    # The reply is an unsigned array stored as a signed array
    # Can't use the jarray function as before though, as it creates overflows
    signedReplyBytes = [((x + 0x80) & 0xff) - 0x80 for x in chillReply]
    unsignedReplyBytes = bytearray([(byte + 256) % 256 for byte in signedReplyBytes])
    packet = unsignedReplyBytes

This snippet was part of a rudimentary driver for Neslab / Thermoflex chillers using "NC protocol". Please drop me a PM if you would like the entire script.