30_LOGGER = logging.getLogger(__name__)
32_QUOTATION_CODE = ord(b
'"')
35def _parse_string(answer: bytes) -> str:
36 assert answer[0] == _QUOTATION_CODE
and answer[-1] == _QUOTATION_CODE
37 return answer[1:-1].decode()
40def _parse_bool(answer: bytes) -> bool:
41 assert answer
in [b
"#f", b
"#t"]
42 return answer == b
"#t"
46 """Connection to the iChrome MLE.
48 This is a simple wrapper to the iChrome MLE interface. It only
49 supports the parameter commands which reply
with a single line
50 which
is all we need to support this on Python-Microscope.
62 def _param_command(self, command: bytes) -> bytes:
63 """Run command and return raw answer (minus prompt and echo)."""
64 command = command + b
"\r\n"
67 answer = self.
_serial.read_until(b
"\r\n> ")
71 assert answer[: len(command)] == command
and answer[-4:] == b
"\r\n> "
75 if answer[len(command) : len(command) + 7] == b
"Error: ":
76 base_command = command[:-2]
77 error_msg = answer[len(command) + 8 : -4]
79 "error on command '%s': %s"
80 % (base_command.decode(), error_msg.decode())
85 return answer[len(command) : -4]
88 """Get parameter value (`param-ref` operator)."""
91 def param_set(self, name: bytes, value: bytes) ->
None:
92 """Change parameter (`param-set!` operator)."""
93 answer = self.
_param_command(b
"(param-set! '%s %s)" % (name, value))
97 "Failed to set parameter %s (return value %d)"
98 % (name.decode(), status)
101 def get_serial_number(self) -> str:
102 return _parse_string(self.
param_ref(b
"serial-number"))
104 def get_system_type(self) -> str:
105 return _parse_string(self.
param_ref(b
"system-type"))
109 def __init__(self, conn: _iChromeConnection, laser_number: int) ->
None:
120 "failed to get label, probably no laser %d" % laser_number
123 def _param_ref(self, name: bytes) -> bytes:
126 def _param_set(self, name: bytes, value: bytes) ->
None:
129 def get_label(self) -> str:
130 return _parse_string(self.
_param_ref(b
"label"))
132 def get_type(self) -> str:
133 return _parse_string(self.
_param_ref(b
"type"))
135 def get_delay(self) -> int:
138 def get_enable(self) -> bool:
139 return _parse_bool(self.
_param_ref(b
"enable"))
141 def set_enable(self, state: bool) ->
None:
142 value = b
"#t" if state
else b
"#f"
145 def get_cw(self) -> bool:
148 def set_cw(self, state: bool) ->
None:
149 value = b
"#t" if state
else b
"#f"
152 def get_use_ttl(self) -> bool:
153 return _parse_bool(self.
_param_ref(b
"use-ttl"))
155 def set_use_ttl(self, state: bool) ->
None:
156 value = b
"#t" if state
else b
"#f"
159 def get_level(self) -> float:
162 def set_level(self, level: float) ->
None:
163 value = b
"%.1f" % level
166 def get_status_txt(self) -> str:
167 return _parse_string(self.
_param_ref(b
"status-txt"))
171 def __init__(self, conn: _iChromeConnection, laser_number: int) ->
None:
182 "delay",
"int", self.
_conn.get_delay,
None, values=tuple()
186 return self.
_conn.get_status_txt().split()
189 if self.
_conn.get_enable():
190 if self.
_conn.get_cw():
196 return self.
_conn.get_use_ttl()
200 def _do_get_power(self) -> float:
201 return self.
_conn.get_level() / 100.0
203 def _do_set_power(self, power: float) ->
None:
204 self.
_conn.set_level(power * 100.0)
206 def _do_enable(self) -> None:
207 self.
_conn.set_enable(
True)
209 def _do_disable(self) -> None:
210 self.
_conn.set_enable(
False)
212 def _do_shutdown(self) -> None:
216 def trigger_mode(self) -> microscope.TriggerMode:
217 return microscope.TriggerMode.BULB
220 def trigger_type(self) -> microscope.TriggerType:
221 if self.
_conn.get_use_ttl():
222 return microscope.TriggerType.HIGH
224 return microscope.TriggerType.SOFTWARE
229 if tmode
is not microscope.TriggerMode.BULB:
231 "only TriggerMode.BULB mode is supported"
236 if ttype
is microscope.TriggerType.HIGH:
237 self.
_conn.set_cw(
False)
238 self.
_conn.set_use_ttl(
True)
239 elif ttype
is microscope.TriggerType.SOFTWARE:
240 self.
_conn.set_use_ttl(
False)
241 self.
_conn.set_cw(
True)
244 "only trigger type HIGH and SOFTWARE are supported"
247 def _do_trigger(self) -> None:
249 "trigger does not make sense in trigger mode bulb, only enable"
254 """Toptica iChrome MLE (multi-laser engine).
256 The names of the light devices are `laser1`, `laser2`, `laser3`,
261 def __init__(self, port: str, **kwargs) ->
None:
262 super().__init__(**kwargs)
263 self._lasers: typing.Dict[str, _iChromeLaser] = {}
266 serial_conn = serial.Serial(
270 bytesize=serial.EIGHTBITS,
271 stopbits=serial.STOPBITS_ONE,
272 parity=serial.PARITY_NONE,
280 _LOGGER.info(
"Connected to %s", ichrome_connection.get_serial_number())
286 for i
in range(1, 6):
291 _LOGGER.info(
"no %s available", name)
294 _LOGGER.info(
"found %s on iChrome MLE", name)
295 self._lasers[name] = laser
298 def devices(self) -> typing.Dict[str, _iChromeLaser]:
None add_setting(self, name, dtype, get_func, set_func, values, typing.Optional[typing.Callable[[], bool]] readonly=None)
bytes param_ref(self, bytes name)
str get_system_type(self)
None param_set(self, bytes name, bytes value)
bytes _param_command(self, bytes command)
bytes _param_ref(self, bytes name)
None _param_set(self, bytes name, bytes value)
typing.List[str] get_status(self)
None set_trigger(self, microscope.TriggerType ttype, microscope.TriggerMode tmode)