# Does Ignition support Open Protocol?

**URL:** <https://forum.inductiveautomation.com/t/does-ignition-support-open-protocol/90518>\
**Category:** Ignition\
**Tags:** perspective\
**Created:** [May 16, 2024, 7:01pm UTC](https://forum.inductiveautomation.com/t/does-ignition-support-open-protocol/90518 "2024-05-16T19:01:20Z")\
**Posts on this page:** 1\
**Showing post:** 5

<div class="post-metadata">

**Author:** ![Justin\_Mai1](https://avatars.discourse-cdn.com/v4/letter/j/9de053/32.png) [@Justin\_Mai1](https://forum.inductiveautomation.com/u/Justin_Mai1)\
**Post date:** [May 17, 2024, 1:54pm UTC](https://forum.inductiveautomation.com/t/does-ignition-support-open-protocol/90518/5 "2024-05-17T13:54:39Z")

</div>

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:

```python
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.

---

_[View the full topic](https://forum.inductiveautomation.com/t/does-ignition-support-open-protocol/90518)._
