Does Ignition support Open Protocol?

The Open Protocol ends every message with a NULL character, so using the following settings on the TCP driver made it so the entire message was received prior to being place in the 'Message' tag:
Message Delimiter Type = CharacterBased
Message Delimiter = \u0000

The trickier/messier part was I wanted to receive the torque tool's results automatically with never any interaction with the operator. So if the torque tool ever became disconnected, it would automatically reconnect & subscribe to the results. So I had to maintain some state information - and send certain values after others. I used a tag change event script on the 'Message' tag to process the messages received, for some ideas on what to do here is that script:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	
	logger = system.util.getLogger("torque_tool")
	
	#If value changed
	if currentValue.value != previousValue.value and not initialChange:
			
		raw_message = currentValue.value

		
		#logger.info("length: " + raw_message + "|" + str(len(raw_message)))
		
		if len(raw_message)>8 and raw_message[0:8].isnumeric():
			header_length = int(raw_message[0:4])
			header_mid = int(raw_message[4:8])
		else:
			header_length = 0
			header_mid = 0
		
		
		#Communication start acknowledge
		if header_mid == 2:
			msg_0060_tight_subscribe = b"002000600030        \00"
			
			system.tag.writeBlocking(["[default]torque_tool/communication_active",
									  "[default]torque_tool/Writable"],
									  [True,
									  msg_0060_tight_subscribe])
		
		#Command error
		elif header_mid == 4:
			
			logger.info("Command error: " + currentValue.value)
			if len(raw_message) <> 26:
						logger.info("MID0005 Invalid length.  Should be 24." + raw_message)
					
			else:
				error_mid = raw_message[20:24]
				error_code = raw_message[24:26]
				
				#Error '09': Last tightening result subscription already exists
				if error_code == '09':
					system.tag.writeBlocking(["[default]torque_tool/tightening_subscribe_active"], [True])
				
				#Error '10': Last tightening result subscription does not exist
				elif error_code == '10':
					system.tag.writeBlocking(["[default]torque_tool/tightening_subscribe_active"], [True])
				
				#Error '96': Client already connected
				elif error_code == '96':
					system.tag.writeBlocking(["[default]torque_tool/communication_active"], [True])		

		#Command accepted
		elif header_mid == 5:
			
			accepted_mid = int(raw_message[20:24])
			
			#Communication stop accepted
			if accepted_mid == 3:
				system.tag.writeBlocking(["[default]torque_tool/communication_active"], [False])
			
			#tightening subscription accepted
			elif accepted_mid == 60:
				system.tag.writeBlocking(["[default]torque_tool/tightening_subscribe_active"], [True])
			
			#tightening unsubscribe accepted
			elif accepted_mid == 63:
				msg_0003_comms_end = b"002000030000        \00"
				
				system.tag.writeBlocking(["[default]torque_tool/tightening_subscribe_active",
										  "[default]torque_tool/Writable"],
										  [False,
										  msg_0003_comms_end])
		
		#tightening result received
		elif header_mid == 61:
			#logger.info("debug1")
			system.tag.writeBlocking(["[default]torque_tool/Writable"], b"002000620000        \00")
			
			
			result_dict = {}
			result_dict['cell_id']                          = int(raw_message[22:26])         #01
			result_dict['channel_id']                       = int(raw_message[28:30])         #02
			result_dict['torque_controller_name']           =     raw_message[32:57].strip()  #03
			result_dict['vin_number']                       =     raw_message[59:84]          #04
			result_dict['job_id']                           = int(raw_message[86:90])         #05
			result_dict['parameter_set_number']             = int(raw_message[92:95])         #06
			result_dict['strategy']                         = int(raw_message[22:26])         #07
			result_dict['strategy_options']                 = int(raw_message[22:26])         #08
			result_dict['batch_size']                       = int(raw_message[22:26])         #09
			result_dict['batch_counter']                    = int(raw_message[114:118])       #10
			result_dict['tightening_status']                = int(raw_message[120])           #11
			result_dict['batch_status']                    = int(raw_message[123])            #12
			result_dict['torque_status']                    = int(raw_message[126])           #13
			result_dict['angle_status']                     = int(raw_message[129])           #14
			#result_dict['rundown_angle_status']             = int(raw_message[132]) (n/a, space character)
			result_dict['current_monitoring_status']        = int(raw_message[135])           #16
			#result_dict['self_tap_status']                  = int(raw_message[138]) (n/a, space character)
			#result_dict['prevail_torque_monitoring_status'] = int(raw_message[141]) (n/a, space character)
			#result_dict['prevail_torque_compensate_status'] = int(raw_message[144]) (n/a, space character)
			#result_dict['error_status']                     = int(raw_message[147:157].strip())
			result_dict['torque_min_limit']                 = float(raw_message[159:165])/100   #21
			result_dict['torque_max_limit']                 = float(raw_message[167:173])/100   #22
			result_dict['torque_final_target']              = float(raw_message[175:181])/100   #23
			result_dict['torque']                           = float(raw_message[183:189])/100   #24
			result_dict['angle_min']                        = int(raw_message[191:196])         #25
			result_dict['angle_max']                        = int(raw_message[198:203])         #26
			result_dict['final_angle_target']               = int(raw_message[205:210])         #27
			result_dict['angle']                            = int(raw_message[212:217])         #28
			#result_dict['rundown_angle_min']                = int(raw_message[219:224])   
			#result_dict['rundown_angle_max']                = int(raw_message[226:231])
			#result_dict['rundown_angle']                    = int(raw_message[233:238])
			result_dict['current_monitoring_min']           = int(raw_message[240:243])         #32
			result_dict['current_monitoring_max']           = int(raw_message[245:248])         #33
			result_dict['current_monitoring_value']         = int(raw_message[250:253])         #34
			#result_dict['self_tap_min']                     = float(raw_message[255:261])/100
			#result_dict['self_tap_max']                     = float(raw_message[263:269])/100
			#result_dict['self_tap_torque']                  = float(raw_message[271:277])/100
			#result_dict['prevail_torque_monitoring_min']    = float(raw_message[279:285])/100
			#result_dict['prevail_torque_monitoring_max']    = float(raw_message[287:293])/100
			#result_dict['prevail_torque']                   = float(raw_message[295:301])/100
			result_dict['tightening_id']                    = int(raw_message[303:313])         #41
			#result_dict['job_sequence_number']              = int(raw_message[315:320])
			#result_dict['sync_tightening_id']               = int(raw_message[322:327])
			result_dict['tool_serial_number']               =     raw_message[329:342].strip()                               #44
			result_dict['time_stamp']                       = system.date.parse(raw_message[345:364], 'yyyy-MM-dd:HH:mm:ss') #45
			result_dict['parameter_last_change']            = system.date.parse(raw_message[366:385], 'yyyy-MM-dd:HH:mm:ss') #46
			result_dict['parameter_set_name']               =     raw_message[387:412].strip()                               #47
			result_dict['torque_value_units']               = int(raw_message[414])      #48
			result_dict['result_type']                      = int(raw_message[417:419])  #49
			result_dict['t_stamp']                          = system.date.now()
			
			
			sql_insert_query = "INSERT INTO torque_tightening_result (" + " ,".join(result_dict.keys()) + ") VALUES (" + ','.join('?'*len(result_dict)) + ")"
			
			system.db.runSFPrepUpdate(sql_insert_query, result_dict.values(), "chs_mfg")
			#logger.info("debug2")
		
		#time received
		elif header_mid == 81:
			pass
			
			
		else:
			logger.info("Unknown MID: " + currentValue.value)
		
		if header_mid <> 81:
			update_args = [system.date.now(), raw_message, header_length, header_mid]
			system.db.runSFPrepUpdate(update_query, update_args, "chs_mfg")
	
	#If quality goes from bad --> good, initiate comms	
	elif previousValue.quality.isNotGood() and currentValue.quality.isGood():
		system.tag.writeBlocking(["[default]torque_tool/Writable"], [b"002000010000        \00"])
	
	#If quality goes from bad --> good, initiate comms	
	elif previousValue.quality.isGood() and currentValue.quality.isNotGood():
		system.tag.writeBlocking(["[default]torque_tool/communication_active"], False)

The details of the protocol can be found in the "Open Protocol Specification 9836 4415 01" document which can be found online. And note this script probably should be a gateway tag event script instead of the tag's value change script.

5 Likes