from . import base
import serial
import re
import time
[docs]
class TrapHighVoltageSupply(base.HardwareBase):
def __init__(self, bus, **kwargs):
super().__init__(**kwargs)
self._bus = bus
self._ardu = None
self._last = {
"current": 0,
"current_outofrange": "", # "low", "high", ""
"voltage": 0,
"voltage_outofrange": "", # "low", "high", ""
"polarity": "+",
"output": False,
}
# ==== Inherited abstract methods ====
def _connect(self):
def reset(ardu):
ardu.setDTR(False)
time.sleep(0.1)
ardu.reset_input_buffer()
ardu.setDTR(True)
try:
ardu = serial.Serial(self._bus, 9600)
except serial.SerialException:
self.log.debug("Failed to open serial '%s'", self._bus)
reset(ardu)
ardu.timeout = 5
recv = ardu.readline().strip().decode("ascii")
if (recv != "Start HV supply"):
self.log.debug("Unexpected start message '%s'", recv)
ardu.close()
return False
ardu.timeout = 0.5
self._ardu = ardu
return True
def _disconnect(self):
# TODO Stop voltage output on disconnect?
self._ardu.close()
self._ardu = None
def _is_ready(self):
return True
# def _safestate(self):
# # TODO Implement this probably
# pass
# ==== Private commands ====
@base.base_command
def _read_serial(self):
"""Read line from serial and if it contains current and voltage,
extract the values before returning the line.
"""
line = self._ardu.readline().strip().decode("ascii")
# Example:
# "123 10.34 mA 123 < 100 V 123 123 CC "
# --- current --- voltage --- --- ??
p_num = r"([<>]? \d+\.?\d*)"
match = re.match(
r".*" + p_num + r" mA .*" + p_num + r" V ",
line
)
if match is not None:
for text, var in zip(match.groups(), ["current", "voltage"]):
self._last[var] = float(text[1:])
if text[0] != " ":
self._last[var + "_outofrange"] = " " + text[0]
else:
self._last[var + "_outofrange"] = ""
return line
# ==== Commands ====
[docs]
def voltage(self):
self._idleupdate_all()
return self._last["current"]
[docs]
@base.base_command
def set_voltage(self, voltage):
voltage = float(voltage)
self._ardu.write("v{:.4f}".format(voltage).encode("ascii"))
# TODO Maybe self._ardu.readline()?
[docs]
def current(self):
self._idleupdate_all()
return self._last["current"]
[docs]
@base.base_command
def set_current(self, current):
current = float(current)
self._ardu.write("i{:.4f}".format(current).encode("ascii"))
# TODO Maybe self._ardu.readline()?
[docs]
def polarity(self):
return self._last["polarity"]
[docs]
@base.base_command
def set_polarity(self, polarity):
"""Set polarity, use '+' or '-'."""
if polarity not in ("+", "-"):
raise ValueError("polarity must be '+' or '-'")
if self._last["polarity"] != polarity:
self._ardu.write(b"p")
self._last["polarity"] = polarity
[docs]
def output(self):
return self._last["output"]
[docs]
@base.base_command
def switch_output_on(self):
if not self._last["output"]:
self._ardu.write(b"o")
self._last["output"] = True
[docs]
@base.base_command
def switch_output_off(self):
if self._last["output"]:
self._ardu.write(b"o")
self._last["output"] = False
# ==== Related to DeviceClient ====
@base.idle_command
def _idleupdate_all(self):
while self._read_serial() != "":
pass
[docs]
def update_frontend(self, device_client):
self._idleupdate_all()
polarity = self._last["polarity"]
value = self._last["current"]
oor = self._last["current_outofrange"]
device_client.emit("value", {
"value": value,
"formatted": "{}{}{:.2f} mA".format(polarity, oor, value),
"label": "I",
"min": 1.5,
"max": 86,
"id": "current",
})
value = self._last["voltage"]
oor = self._last["voltage_outofrange"]
device_client.emit("value", {
"value": value,
"formatted": "{}{}{:.2f} V".format(polarity, oor, value),
"label": "U",
"min": 100,
"max": 3100,
"id": "voltage",
})
value = self._last["output"]
device_client.emit("value", {
"value": 1 if value else 0,
"formatted": "ON" if value else "OFF",
"label": "Output",
"min": 0,
"max": 1,
"id": "output",
})