2023-10-13 11:34:21 +00:00
|
|
|
import re
|
2023-10-11 07:46:38 +00:00
|
|
|
import sys
|
2023-10-13 11:34:21 +00:00
|
|
|
import time
|
|
|
|
|
from collections import OrderedDict
|
2023-10-11 07:46:38 +00:00
|
|
|
|
2023-10-11 13:15:42 +00:00
|
|
|
from components.component import Component
|
|
|
|
|
from components.furness_controls_fco730_registers import registers as fco730_registers
|
|
|
|
|
from components.furness_controls_fco780_registers import registers as fco780_registers
|
2023-10-11 07:46:38 +00:00
|
|
|
if "--sim-furness-controls" in sys.argv:
|
|
|
|
|
from components.dummies.serial import serial
|
|
|
|
|
else:
|
|
|
|
|
import serial
|
|
|
|
|
|
2023-10-11 14:26:34 +00:00
|
|
|
ETX=b'\x03'
|
|
|
|
|
EOT=b'\x04'
|
|
|
|
|
ENQ=b'\x05'
|
|
|
|
|
ACK=b'\x06'
|
|
|
|
|
NACK=b'\x15'
|
2023-10-07 16:14:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class FurnessControlsLeakTester(Component):
|
|
|
|
|
|
|
|
|
|
def __init__(self, config=None, name=None, period=1, lazy=True, paused=False, threaded=True):
|
2023-10-11 14:26:34 +00:00
|
|
|
super().__init__(config=config, name=name, period=period, lazy=lazy, paused=paused, threaded=threaded)
|
2023-10-13 11:34:21 +00:00
|
|
|
self.enums = None
|
|
|
|
|
self.current_status = None
|
2023-10-12 17:37:39 +00:00
|
|
|
self.product_tags = None
|
2023-10-11 07:46:38 +00:00
|
|
|
self.bytesize = None
|
|
|
|
|
self.timeout = None
|
|
|
|
|
self.parity = None
|
|
|
|
|
self.conn = None
|
|
|
|
|
self.stopbits = None
|
|
|
|
|
self.baudrate = None
|
|
|
|
|
self.port = None
|
|
|
|
|
self.model = None
|
|
|
|
|
self.settings = None
|
2023-10-13 11:34:21 +00:00
|
|
|
self.commands = None
|
2023-10-11 14:26:34 +00:00
|
|
|
self.id1=b'0'
|
|
|
|
|
self.id2=b'1'
|
2023-10-07 16:14:30 +00:00
|
|
|
|
|
|
|
|
def config_changed(self):
|
|
|
|
|
super().config_changed()
|
|
|
|
|
self.model = self.config[self.name]["model"].lower()
|
|
|
|
|
if self.model == "fco730":
|
2023-10-13 11:34:21 +00:00
|
|
|
self.commands = fco730_registers["commands"]
|
|
|
|
|
self.product_tags = fco730_registers["product_tags"]
|
|
|
|
|
self.enums = fco730_registers["enums"]
|
2023-10-07 16:14:30 +00:00
|
|
|
else:
|
|
|
|
|
raise NotImplementedError(f"Furness Controls model {self.model!r} not implemented.")
|
2023-10-11 07:46:38 +00:00
|
|
|
self.port = self.config[self.name]["port"]
|
2023-10-11 14:26:34 +00:00
|
|
|
self.baudrate = 9600
|
|
|
|
|
self.stopbits = 1
|
|
|
|
|
self.parity = serial.PARITY_NONE
|
|
|
|
|
self.bytesize = serial.EIGHTBITS
|
2023-10-13 11:34:21 +00:00
|
|
|
self.timeout = 0.1
|
2023-10-11 07:46:38 +00:00
|
|
|
if self.conn is not None:
|
|
|
|
|
self.conn.close()
|
|
|
|
|
self.conn = serial.Serial(
|
|
|
|
|
self.port,
|
|
|
|
|
baudrate=self.baudrate,
|
|
|
|
|
stopbits=self.stopbits,
|
|
|
|
|
parity=self.parity,
|
|
|
|
|
bytesize=self.bytesize,
|
|
|
|
|
timeout=self.timeout,
|
|
|
|
|
write_timeout=self.timeout,
|
|
|
|
|
)
|
2023-10-07 16:14:30 +00:00
|
|
|
|
|
|
|
|
def _convert_from_format(self, data, formatting=None, decoding_map=None):
|
|
|
|
|
if decoding_map is not None and data in decoding_map:
|
|
|
|
|
data = decoding_map[data]
|
|
|
|
|
if formatting is not None:
|
|
|
|
|
# units = self.units[formatting]
|
|
|
|
|
# data = [data * units[0], units[1]]
|
|
|
|
|
data = data * self.units[formatting][0]
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
@Component.reconfig_on_error
|
|
|
|
|
def _get(self):
|
|
|
|
|
# READ INFO
|
2023-10-13 11:34:21 +00:00
|
|
|
current_status=self.get_status()
|
|
|
|
|
info = {
|
|
|
|
|
"Real time test pressure output":current_status["pressure_reading"],
|
|
|
|
|
"Real time differential pressure output":0,
|
|
|
|
|
"Real time pressure line regulator":0,
|
|
|
|
|
"Active alarm flags":0,
|
|
|
|
|
"Active test program number":0,
|
|
|
|
|
"Running test: active phase":current_status["current_stage"],
|
|
|
|
|
"Running test: measured leak":current_status["leak_reading"],
|
|
|
|
|
"Running test: test type":0,
|
|
|
|
|
"Running test: sequence index":0,
|
|
|
|
|
"Digital inputs status (mask)":0,
|
|
|
|
|
}
|
|
|
|
|
for round_me in ["measured leak"]:
|
|
|
|
|
if round_me in info.keys():
|
|
|
|
|
info.update({round_me:float(f"{info[round_me]:.2f}")})
|
|
|
|
|
self.log.debug(str(info))
|
|
|
|
|
super()._get([info])
|
2023-10-07 16:14:30 +00:00
|
|
|
|
|
|
|
|
def start_test(self, table=None):
|
2023-10-13 11:34:21 +00:00
|
|
|
self.log.info(f"starting test")
|
|
|
|
|
self.send_command("start_test")
|
2023-10-07 16:14:30 +00:00
|
|
|
|
|
|
|
|
def stop_test(self):
|
|
|
|
|
self.log.warning("stopping test")
|
2023-10-13 11:34:21 +00:00
|
|
|
self.current_status = self.get_status()
|
|
|
|
|
if self.current_status['current_stage'] in ('standby','awaiting_start'):
|
2023-10-12 17:37:39 +00:00
|
|
|
self.log.info("ready to start")
|
|
|
|
|
else:
|
2023-10-13 11:34:21 +00:00
|
|
|
self.log.warning("not ready to start, performing self check to reset")
|
2023-10-12 17:37:39 +00:00
|
|
|
self.send_command("self_check")
|
2023-10-13 11:34:21 +00:00
|
|
|
time.sleep(2)
|
|
|
|
|
|
|
|
|
|
def get_status(self):
|
|
|
|
|
status_str=str(self.send_enquiry("current_status"),encoding="ascii")
|
|
|
|
|
status_str+='z' # dummy terminator
|
|
|
|
|
status_vars=OrderedDict(
|
|
|
|
|
{
|
|
|
|
|
'a':'counter',
|
|
|
|
|
'b':'product_number',
|
|
|
|
|
'c':'step_number',
|
|
|
|
|
'd':'new_result_available',
|
|
|
|
|
'e':'mode',
|
|
|
|
|
'f':'status',
|
|
|
|
|
'g':'pressure_reading',
|
|
|
|
|
'i':'leak_reading',
|
|
|
|
|
'k':'current_stage',
|
|
|
|
|
'z':'dummy'
|
|
|
|
|
})
|
|
|
|
|
status_decoded={}
|
|
|
|
|
for tag,param in status_vars.items():
|
|
|
|
|
next_tag=list(status_vars)[list(status_vars.keys()).index(tag) + 1]
|
|
|
|
|
match=re.search(f"{tag}([0-9.-]+){next_tag}",status_str)
|
|
|
|
|
value=None
|
|
|
|
|
if match is not None:
|
|
|
|
|
value=match.group(1)
|
|
|
|
|
if param == 'status':
|
|
|
|
|
value = self.enums['status_status'][value]
|
|
|
|
|
if param == 'current_stage':
|
|
|
|
|
value = self.enums['status_current_stage'][value]
|
|
|
|
|
|
|
|
|
|
status_decoded[param]=value
|
|
|
|
|
if next_tag =='z':
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
return status_decoded
|
2023-10-07 16:14:30 +00:00
|
|
|
|
|
|
|
|
def write_recipe(self, recipe, step, table=None):
|
2023-10-12 17:37:39 +00:00
|
|
|
# PREPARE DATA
|
|
|
|
|
product_id='"'+recipe.part_number[:16]+'"'
|
|
|
|
|
product_id=product_id.encode("ascii")
|
|
|
|
|
test_press_bar=step.spec["test_pressure"]/1000
|
|
|
|
|
prefill_press_bar=step.spec["pre_filling_pressure"]/1000
|
|
|
|
|
tolerance=int(step.spec["settling_pressure_min_percent"])
|
|
|
|
|
fail_pos=test_press_bar+(int(step.spec["test_pressure_qpos"])/1000)
|
|
|
|
|
fail_neg=test_press_bar+(int(step.spec["test_pressure_qneg"])/1000)
|
|
|
|
|
fill_time=float(step.spec["filling_time"])
|
|
|
|
|
stab_time=float(step.spec["settling_time"])
|
|
|
|
|
test_time=float(step.spec["test_time"])
|
|
|
|
|
prefill_time=float(step.spec["pre_filling_time"])
|
|
|
|
|
vent_time=float(step.spec["flush_time"])
|
|
|
|
|
# SEND RECIPE PARAMETERS
|
|
|
|
|
self.send_command("change_cur_prod_50")
|
|
|
|
|
self.send_product_tag("product_id", product_id)
|
|
|
|
|
self.send_product_tag("prefill_pressure", f"{prefill_press_bar:2.3f}")
|
|
|
|
|
self.send_product_tag("test_pressure", f"{test_press_bar:2.3f}")
|
|
|
|
|
self.send_product_tag("tolerance", f"{tolerance:3.1f}")
|
|
|
|
|
self.send_product_tag("+fail", f"{fail_pos:3.1f}")
|
|
|
|
|
self.send_product_tag("-fail", f"{fail_neg:3.1f}")
|
|
|
|
|
self.send_product_tag("fill_time", f"{fill_time:3.1f}")
|
|
|
|
|
self.send_product_tag("stab_time", f"{stab_time:3.1f}")
|
|
|
|
|
self.send_product_tag("test_time", f"{test_time:3.1f}")
|
|
|
|
|
self.send_product_tag("prefill_time", f"{prefill_time:3.1f}")
|
|
|
|
|
self.send_product_tag("vent_time", f"{vent_time:3.1f}")
|
|
|
|
|
self.send_product_tag("fail_high", f"{fail_pos:3.1f}")
|
|
|
|
|
self.send_product_tag("fail_low", f"{fail_neg:3.1f}")
|
|
|
|
|
self.send_product_tag("outputs_a_h", f"{int(0b01000000)}")
|
2023-10-07 16:14:30 +00:00
|
|
|
|
2023-10-11 07:46:38 +00:00
|
|
|
|
|
|
|
|
def send_command(self,command):
|
2023-10-12 17:37:39 +00:00
|
|
|
if type(command) is str:
|
2023-10-13 11:34:21 +00:00
|
|
|
command=self.commands[command]
|
2023-10-11 07:46:38 +00:00
|
|
|
out_bytes= bytearray()
|
2023-10-11 14:26:34 +00:00
|
|
|
out_bytes.extend(EOT)
|
|
|
|
|
out_bytes.extend(self.id1)
|
|
|
|
|
out_bytes.extend(self.id2)
|
|
|
|
|
out_bytes.extend(command)
|
|
|
|
|
out_bytes.extend(ETX)
|
|
|
|
|
checksum=self.calc_checksum(out_bytes)
|
|
|
|
|
out_bytes.append(checksum)
|
2023-10-11 07:46:38 +00:00
|
|
|
|
|
|
|
|
self.conn.write(out_bytes)
|
2023-10-11 14:26:34 +00:00
|
|
|
response = self.conn.read(100)
|
|
|
|
|
if response == ACK:
|
|
|
|
|
return True
|
|
|
|
|
else:
|
2023-10-12 17:37:39 +00:00
|
|
|
self.log.error(f"SEND COMMAND({command}):{response}")
|
2023-10-11 14:26:34 +00:00
|
|
|
return None
|
2023-10-11 07:46:38 +00:00
|
|
|
|
|
|
|
|
def send_enquiry(self,enquiry):
|
2023-10-12 17:37:39 +00:00
|
|
|
if type(enquiry) is str:
|
2023-10-13 11:34:21 +00:00
|
|
|
enquiry=self.commands[enquiry]
|
2023-10-11 07:46:38 +00:00
|
|
|
out_bytes=bytearray(EOT)
|
2023-10-11 14:26:34 +00:00
|
|
|
out_bytes.extend(self.id1)
|
|
|
|
|
out_bytes.extend(self.id2)
|
|
|
|
|
out_bytes.extend(enquiry)
|
|
|
|
|
out_bytes.extend(ENQ)
|
2023-10-12 17:37:39 +00:00
|
|
|
checksum = self.calc_checksum(out_bytes)
|
2023-10-11 07:46:38 +00:00
|
|
|
out_bytes.append(checksum)
|
|
|
|
|
|
|
|
|
|
self.conn.write(out_bytes)
|
2023-10-11 14:26:34 +00:00
|
|
|
response = self.conn.read(100)
|
2023-10-12 17:37:39 +00:00
|
|
|
if len(response):
|
|
|
|
|
read_checksum = response[-1]
|
|
|
|
|
else:
|
|
|
|
|
read_checksum = None
|
2023-10-11 14:26:34 +00:00
|
|
|
calculated_checksum = self.calc_checksum(response[0:-1],start_idx=0)
|
|
|
|
|
if read_checksum != calculated_checksum:
|
2023-10-13 11:34:21 +00:00
|
|
|
self.log.error(f"SEND ENQUIRY:{response}")
|
|
|
|
|
return None
|
2023-10-11 14:26:34 +00:00
|
|
|
else:
|
2023-10-13 11:34:21 +00:00
|
|
|
response = response[:-2] #strip checksum & ETX
|
2023-10-11 14:26:34 +00:00
|
|
|
return response
|
2023-10-11 07:46:38 +00:00
|
|
|
|
2023-10-12 17:37:39 +00:00
|
|
|
def send_product_tag(self,tag,tag_data):
|
|
|
|
|
self.log.info(f"Sending tag:{tag}={tag_data}")
|
2023-10-13 11:34:21 +00:00
|
|
|
command=bytearray(self.commands["product_data"])
|
2023-10-12 17:37:39 +00:00
|
|
|
if type(tag_data) is str:
|
|
|
|
|
tag_data=bytearray(tag_data,encoding="ascii")
|
|
|
|
|
command.extend(self.product_tags[tag])
|
|
|
|
|
command.extend(tag_data)
|
|
|
|
|
self.send_command(command)
|
2023-10-13 11:34:21 +00:00
|
|
|
|
2023-10-12 17:37:39 +00:00
|
|
|
@staticmethod
|
|
|
|
|
def calc_checksum(data, start_idx=1):
|
|
|
|
|
checksum = 0
|
|
|
|
|
for i, data_byte in enumerate(data):
|
|
|
|
|
if i < start_idx:
|
|
|
|
|
continue # skip EOT
|
|
|
|
|
checksum = checksum ^ data_byte
|
2023-10-11 07:46:38 +00:00
|
|
|
return checksum
|