Checking for serial port open or closed

Is there a way to see if the serial port is open or closed when using the serial module? I have been closing it to ‘empty’ the serial buffer rather than take in unwanted data from my serial weigh scales on the next read. Is there like a recommended time period between opening/closing???

There’s not a way to see if it’s open or closed… but there shouldn’t be any problem with frequently opening and closing the port.

Access to the serial ports via the script module was designed with the idea that you would close it when not actively using it because access to the serial port is exclusive.

Adding a call that would “clear” the read buffer is an interesting idea that I’ll look into a little bit.

Well I have a problem:
I have a client timer script which checks the port for data as below:

value = system.serial.readBytes('COM1',1) value2 = int(value[-1]) if value2 > 0 value = system.serial.readLine('COM1')
If the correct screen is in use etc… and there is data present the script continues
I put in a “print value” (client tag value) and in the console it prints the value ‘1’ when weighing has been selected. The ‘1’ value was only printed every 2 or 3 seconds for about 20 seconds (should be every 100 ms) before this error message popped up:

[code]ERROR [TimerScriptTask-DedicatedScriptTimer[Flake/serialWeightScalesMultiple]] Error executing global timer script: serialWeightScalesMultiple @100ms . Repeat errors of this type will be logged as ‘debug’ messages.
Traceback (most recent call last):
File “<TimerScript:Flake/serialWeightScalesMultiple @100ms >”, line 21, in
at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.getAndLockSerialPort(

at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.readBytes(

at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.readBytes(

at sun.reflect.GeneratedMethodAccessor193.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

at java.lang.reflect.Method.invoke(Unknown Source) Port COM1 has not been opened.

at org.python.core.PyException.fillInStackTrace(
at java.lang.Throwable.<init>(
at java.lang.Exception.<init>(Unknown Source)
at java.lang.RuntimeException.<init>(Unknown Source)
at org.python.core.PyException.<init>(
at org.python.core.PyException.<init>(
at org.python.core.Py.JavaError(
at org.python.core.Py.JavaError(
at org.python.core.PyReflectedFunction.__call__(
at org.python.core.PyReflectedFunction.__call__(
at org.python.core.PyObject.__call__(
at org.python.core.PyObject.__call__(
at org.python.pycode._pyx238.f$0(<TimerScript:Flake/serialWeightScalesMultiple @100ms >:2)
at org.python.pycode._pyx238.call_function(<TimerScript:Flake/serialWeightScalesMultiple @100ms >)
at org.python.core.Py.runCode(
at com.inductiveautomation.ignition.common.script.ScriptManager.runCode(
at java.util.TimerThread.mainLoop(Unknown Source)
at Source)

Caused by: Port COM1 has not been opened.
at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.getAndLockSerialPort(
at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.readBytes(
at com.inductiveautomation.ignition.modules.serial.scripting.SerialScriptModule.readBytes(
at sun.reflect.GeneratedMethodAccessor193.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
… 12 more

Then the ‘1’ value started to print very quickly down the console.
If I activate the weigh scales the value increments 1 (as it should) as quick as I can rapidly press the button on the scales.
Do I have some kind of problem with my COM port being opened?

Wheres the rest of your code, i.e. the calls to system.serial.openSerialPort() and system.serial.closeSerialPort()? and what version of Ignition is this?

I open the port from one of three buttons

[code]toasterWeighingStatus =’[Client]serialScales/toaster’)
pWS = toasterWeighingStatus.value
packingWeighingStatus =’[Client]serialScales/packing’)
pack = packingWeighingStatus.value
if pWS == 0 and pack == 0:
value = ‘COM1’
value = 1
system.tag.writeToTag(’[Client]SerialScales/weighmentNumber’, value)
system.tag.writeToTag(’[Client]SerialScales/density’, 1)
value = 0
system.tag.writeToTag(’[Client]SerialScales/weight1’, value)
system.tag.writeToTag(’[Client]SerialScales/weight2’, value)
system.tag.writeToTag(’[Client]SerialScales/weight3’, value)
system.tag.writeToTag(’[Client]SerialScales/weight4’, value)
system.tag.writeToTag(’[Client]SerialScales/weight5’, value)
system.tag.writeToTag(’[Client]SerialScales/weight6’, value)
system.tag.writeToTag(’[Client]SerialScales/weight7’, value)
system.tag.writeToTag(’[Client]SerialScales/weight8’, value)
system.tag.writeToTag(’[Client]SerialScales/percentage1’, value)
system.tag.writeToTag(’[Client]SerialScales/percentage2’, value)
system.tag.writeToTag(’[Client]SerialScales/percentage3’, value)
system.tag.writeToTag(’[Client]SerialScales/percentage4’, value)
system.tag.writeToTag(’[Client]SerialScales/percentage5’, value)
system.tag.writeToTag(’[Client]SerialScales/percentage6’, value)
system.tag.writeToTag(’[Client]SerialScales/percentage7’, value)
system.tag.writeToTag(’[Client]SerialScales/percentage8’, value)
system.tag.writeToTag(’[Client]SerialScales/4or8’, 1)
elif pWS != 0:
system.gui.messageBox(‘Disable Toaster weighment to begin Density weighing’, ‘Weigher In Use’)
elif pack != 0:
system.gui.messageBox(‘Disable Packing weighment to begin Density weighing’, ‘Weigher In Use’)

Most of the above code just resets my client tags to zero for a fresh weighment.
I then have a client timer script that runs every second:

[code]windows = system.gui.getOpenedWindowNames()
for path in windows:
if path == “Main Windows/Rospen”:#“Main Windows/Sieve Weighments”:
tag =’[Client]SerialScales/weighmentNumber’)
tagvalue = tag.value
if tagvalue >0 :
toaster =’[Client]SerialScales/toaster’)
toastervalue = toaster.value
packing =’[Client]SerialScales/packing’)
packingvalue = packing.value
density =’[Client]SerialScales/density’)
densityvalue = density.value
if toastervalue == 1:
Line = ‘Toaster’
elif packingvalue ==1:
Line = ‘Packing’
elif densityvalue == 1:
Line = ‘Density’
#print “1”
print tagvalue
value = system.serial.readBytes(‘COM1’,1)
value2 = int(value[-1])
if value2 > 0:#If any data is present in COM1 then get data and INSERT into SQL
value = system.serial.readLine(‘COM1’)#NOTE - Once data is ‘looked at’ in COM1 the data then disappears from COM1
gPos = value.index(‘g’)#Find position of ‘g’ in for example’2.3gms’
valueNew = value[:gPos]#Using position of the ‘g’ strip out just the numbers for the SQL Float column
valuefloat = float(valueNew)
if valuefloat >0:#this was added after some spurious zero’s were recorded, always at position 8 for some unknowm reason
#print “2”
if tagvalue ==1:

				elif tagvalue ==2:
				elif tagvalue ==3:
				elif tagvalue ==4:	
				elif tagvalue ==5:
				elif tagvalue ==6:
				elif tagvalue ==7:
				elif tagvalue ==8:
				if tagvalue ==8 and Line != 'Density' or tagvalue ==4 and Line == 'Density':
					weight1a ='[Client]SerialScales/weight1')
					weight1 = weight1a.value 
					weight2a ='[Client]SerialScales/weight2')
					weight2 = weight2a.value
					weight3a ='[Client]SerialScales/weight3')
					weight3 = weight3a.value
					weight4a ='[Client]SerialScales/weight4')
					weight4 = weight4a.value
					weight5a ='[Client]SerialScales/weight5')
					weight5 = weight5a.value
					weight6a ='[Client]SerialScales/weight6')
					weight6 = weight6a.value
					weight7a ='[Client]SerialScales/weight7')
					weight7 = weight7a.value
					weight8a ='[Client]SerialScales/weight8')
					weight8 = weight8a.value
					total = weight1+weight2+weight3+weight4+weight5+weight6+weight7+weight8
					average = (weight1+weight2+weight3+weight4)/4
					percentage1 = (100*weight1)/total
					percentage2 = (100*weight2)/total
					percentage3 = (100*weight3)/total
					percentage4 = (100*weight4)/total
					percentage5 = (100*weight5)/total
					percentage6 = (100*weight6)/total
					percentage7 = (100*weight7)/total
					percentage8 = (100*weight8)/total
					#print "3"
					from java.util import Date
					now = Date()#This gets the current date/time to be inserted as t_stamp into SQL
					now2 = system.db.dateFormat(now, "yyyy-MM-dd HH:mm:ss")
					#SQL write and tag feedback
					if Line != "Density":
						system.db.runPrepUpdate("INSERT INTO flakeToasterWeights (Line,weight1,weight1percent, weight2, weight2percent,weight3,weight3percent,weight4,weight4percent,weight5,weight5percent,weight6,weight6percent,weight7,weight7percent,weight8,weight8percent,total, t_stamp) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", [Line,weight1,percentage1,weight2,percentage2,weight3,percentage3,weight4,percentage4,weight5,percentage5,weight6,percentage6,weight7,percentage7, weight8,percentage8,total,now2])
						system.db.runPrepUpdate("INSERT INTO flakeToasterDensity (Line,weight1, weight2, weight3, weight4, average, t_stamp) VALUES (?,?,?,?,?,?,?)", [Line,weight1,weight2,weight3,weight4,average,now2])
					#Select where to put date
					if Line == 'Toaster':
						system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/last_Toaster_weight_fines',percentage8 )
					elif Line == 'Packing':
						system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/last_Packing_weight_fines',percentage8 )
					elif Line == 'Density':
					value = 'COM1'#close the port to ensure no data ever sits in buffer when next weights are read
					#delete old data from SQL
					from java.util import *
					startCal = Calendar.getInstance()
					startCal.add(Calendar.DATE, -1095)#3 years old 
					time = startCal.getTime()
					toDisplay = system.db.dateFormat(time, "yyyy-MM-dd HH:mm:ss")
					query = system.db.runUpdateQuery("DELETE FROM flakeToasterWeights WHERE t_stamp < ('%s')" %(toDisplay),"SQLServer")
					#below is fines alarm
					window = system.gui.getWindow("Main Windows/Rospen")
					procRecipeB = window.rootContainer.getComponent("Process_Recipe")
					packRecipeB = window.rootContainer.getComponent("Packing_Recipe")
					if Line == "Toaster" or Line =="Density":
						Recipe = str(procRecipeB.selectedValue)#to string so it can be 'added' to more text for SQL query
					elif Line == "Packing":
						Recipe = str(packRecipeB.selectedValue)
					if Line == "Toaster" or Line == "Packing":
						query = "SELECT Action, Reject FROM flakeToaster_SCADA_fineAlarms WHERE Line ='"+Line+"' and Recipe = "+Recipe
						results = system.db.runQuery(query)
						Action = results[0][0]#Action % for current recipe for current area being weighted
						Reject = results[0][1]#Reject % for current recipe for current area being weighted
						if Line == 'Toaster': #make a decision which of the two alarm tags is updated (Toaster or Packing)
							if percentage8 > Action and percentage8 < Reject:
								system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Toaster_fines_weight_alarm', 1)#tag value 1 = low level 'Action' alarm
							elif percentage8 > Action:
								system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Toaster_fines_weight_alarm', 2)#tag value 2 = mediun level 'Reject' alarm
								system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Toaster_fines_weight_alarm', 0)#tag value 0 = no alarm
						else:#This decides to update the 'Packing' alarm tag
							if percentage8 > Action and percentage8 < Reject:
								system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Packing_fines_weight_alarm', 1)#tag value 1 = low level 'Action' alarm
							elif percentage8 > Action:
								system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Packing_fines_weight_alarm', 2)#tag value 2 = mediun level 'Reject' alarm
								system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Packing_fines_weight_alarm', 0)#tag value 0 = no alarm
					if Line == "Density":
						query = "SELECT density_low, density_high FROM FLAKE_MOISTURE_TARGETS WHERE recipe = " +Recipe
						results = system.db.runQuery(query)
						low = results[0][0]
						high = results[0][1]
						if average < low:
							system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Density Low Alarm',1)
							ystem.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Density High Alarm',0)
						elif average > high:
							system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Density High Alarm',1)
							system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Density Low Alarm',0)
							system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Density Low Alarm',0)
							system.tag.write('Burton Latimer/AP7/Flake/APV_Toaster/Density High Alarm',0)
		#value = 'COM1'

The timer script closes the COM port when it has completed the necessary weighments.
This is the code that was printing out the ‘1’ value that hung for a while before throwing an error and then printed as expected at every second.

Quite often after saving my project the button that should open the COM port gives this fault:

[code]Traceback (most recent call last):

File “event:actionPerformed”, line 7, in COM1: SerOpenPort failed: Port is in use

caused by IOException: COM1: SerOpenPort failed: Port is in use

Ignition v7.5.6 (b1317)
Java: Sun Microsystems Inc. 1.6.0_18[/code]

When pressed again the script works OK and the COM port reads the scales :scratch: :scratch:

Put some print statements everywhere you open and close the port to see if you’re inadvertently opening it twice or forgetting to close it in places.

I’m pretty sure I found the issue. I DF1 in auto-config mode in Factorytalk Studio on my development/clien PC from months back, deleting the driver in RSEnterprise appears to have cured the problem :blush:


I know i’m a little late to the party, but any chance I could someday see a system.serial.clearBuffer(port) function?

It is trivial to read a large number of bytes with 1ms timeout and discard them.