Skip to content

Commit 7c34ee0

Browse files
committed
Provide default values for commands so Merlin RESET command does something
1 parent f6acda3 commit 7c34ee0

File tree

3 files changed

+56
-245
lines changed

3 files changed

+56
-245
lines changed

src/tickit_devices/merlin/adapters.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
from tickit.adapters.specifications.regex_command import RegexCommand
1111
from tickit.adapters.tcp import CommandAdapter
1212

13-
from tickit_devices.merlin.commands import (
13+
from tickit_devices.merlin.merlin import MerlinDetector, State
14+
from tickit_devices.merlin.parameters import (
1415
DLIM,
1516
PREFIX,
1617
CommandType,
1718
ErrorCode,
1819
commands,
1920
)
20-
from tickit_devices.merlin.merlin import MerlinDetector, State
2121
from tickit_devices.merlin.tcp import TcpPushAdapter
2222

2323
LOGGER = logging.getLogger("MerlinControlAdapter")
@@ -48,7 +48,8 @@ async def get(self, parameter: str) -> bytes:
4848
value = "0"
4949
code = ErrorCode.UNDERSTOOD
5050
if (
51-
parameter not in commands[CommandType.GET] + commands[CommandType.SET]
51+
parameter
52+
not in list(commands[CommandType.GET]) + list(commands[CommandType.SET])
5253
or not parameter in self.detector.parameters
5354
):
5455
code = ErrorCode.UNRECOGNISED

src/tickit_devices/merlin/commands.py

Lines changed: 0 additions & 104 deletions
This file was deleted.

src/tickit_devices/merlin/merlin.py

Lines changed: 52 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,24 @@
22
from dataclasses import dataclass, field, fields
33
from datetime import datetime
44
from enum import Enum
5-
from typing import Any, Callable, Dict, List, Optional, Union, Tuple
5+
from typing import Any, Callable, Dict, Generic, List, Optional, Tuple, TypeVar, Union
66

77
import numpy as np
88
from tickit.core.device import Device, DeviceUpdate
99
from tickit.core.typedefs import SimTime
1010
from typing_extensions import TypedDict
1111

1212
from tickit_devices.merlin.acq_header import get_acq_header
13-
from tickit_devices.merlin.commands import ErrorCode
14-
15-
from typing import Generic, TypeVar
13+
from tickit_devices.merlin.parameters import (
14+
AcquisitionType,
15+
ChipMode,
16+
ColourMode,
17+
CommandType,
18+
CounterMode,
19+
ErrorCode,
20+
State,
21+
commands,
22+
)
1623

1724
MAX_THRESHOLD = 100 # keV, assume that a DAC value of 2**9 - 1 represents this energy
1825

@@ -56,77 +63,6 @@ def get_threshold_kev(self, threshold: int) -> float:
5663
return value_DAC * MAX_THRESHOLD / max_DAC
5764

5865

59-
class GainMode(int, Enum):
60-
SLGM = 0
61-
LGM = 1
62-
HGM = 2
63-
SHGM = 3
64-
65-
66-
class AcquisitionType(str, Enum):
67-
NORMAL = "Normal"
68-
TH_SCAN = "Th_scan"
69-
CONFIG = "Config"
70-
71-
72-
class ChipMode(str, Enum):
73-
SPM = "SPM"
74-
CSM = "CSM"
75-
CM = "CM"
76-
CSCM = "CSCM"
77-
78-
79-
class Trigger(int, Enum):
80-
POS = 0
81-
NEG = 1
82-
INT = 2
83-
84-
85-
class Polarity(str, Enum):
86-
POS = "Positive"
87-
NEG = "Negative"
88-
89-
90-
class State(int, Enum):
91-
IDLE = 0
92-
BUSY = 1
93-
Standby = 2
94-
95-
96-
class ColourMode(int, Enum):
97-
MONOCHROME = 0
98-
COLOUR = 1
99-
100-
101-
class GapFillMode(int, Enum):
102-
NONE = 0
103-
ZeroFill = 1
104-
Distribute = 2
105-
Interpolate = 3
106-
107-
108-
class FileFormat(int, Enum):
109-
Binary = 0
110-
ASCII = 1
111-
112-
113-
class TriggerOut(int, Enum):
114-
TriggerInTTL = 0
115-
TriggerInLVDS = 1
116-
TriggerInTTLDelayed = 2
117-
TriggerInLVDSDelayed = 3
118-
FollowShutter = 4
119-
OnePerAcqBurst = 5
120-
ShutterAndSensorReadout = 6
121-
Busy = 7
122-
123-
124-
class CounterMode(int, Enum):
125-
Counter0 = 0
126-
Counter1 = 1
127-
Both = 2
128-
129-
13066
@dataclass
13167
class Chip:
13268
id: str
@@ -181,14 +117,16 @@ def __init__(
181117
setter: Optional[Callable[[T], None]] = None,
182118
):
183119
self._value = getter
184-
self.set: Callable[[T], None] = setter if setter is not None else self._set
120+
self.set: Callable[[T], None] = (
121+
setter if setter is not None else self.default_set
122+
)
185123

186124
def get(self) -> T:
187125
if callable(self._value):
188126
return self._value()
189127
return self._value
190128

191-
def _set(self, value: T):
129+
def default_set(self, value: T):
192130
if callable(self._value):
193131
raise RuntimeError("Can not use default setter with custom getter")
194132
self._value = value
@@ -207,10 +145,17 @@ class MerlinDetector(Device):
207145
_acq_header_enabled: bool = True
208146
_current_frame: int = 1
209147
_current_layer: int = 0
210-
_colour_mode: ColourMode = ColourMode.MONOCHROME
148+
_colour_mode: ColourMode = commands[CommandType.SET]["COLOURMODE"]
211149
_configuration: str = ""
150+
_detector_status: State = commands[CommandType.GET]["DETECTORSTATUS"]
212151
_images_remaining: int = 0
213-
_gap_time_ns: int = 1_000_000 # 1ms
152+
_gap_time_ns: int = int(
153+
(
154+
commands[CommandType.SET]["ACQUISITIONPERIOD"]
155+
- commands[CommandType.SET]["ACQUISITIONTIME"]
156+
)
157+
* 1e6
158+
)
214159
_last_header: str = ""
215160
_last_encoded_image: Optional[bytes] = None
216161
_last_image_shape: Optional[Tuple[int, int]] = None
@@ -223,59 +168,15 @@ class MerlinDetector(Device):
223168
humidity: float = 0.0
224169
medipix_clock: int = 120
225170
readout_system: str = "Merlin Quad"
226-
shutter_time_ns: int = 10_000_000 # 10ms
227-
parameters: Dict[str, MerlinParameter[Any]] = field(
228-
default_factory=lambda: {
229-
"COUNTERDEPTH": MerlinParameter(12),
230-
"CHARGESUMMING": MerlinParameter(False),
231-
"CONTINUOUSRW": MerlinParameter(False),
232-
"DEADTIMECORRECTION": MerlinParameter(False),
233-
"DETECTORSTATUS": MerlinParameter(State.IDLE),
234-
"ENABLECOUNTER1": MerlinParameter(CounterMode.Counter0),
235-
"FILECOUNTER": MerlinParameter(0),
236-
"FILEDIRECTORY": MerlinParameter(""),
237-
"FILEENABLE": MerlinParameter(False),
238-
"FILEFORMAT": MerlinParameter(FileFormat.Binary),
239-
"FILLMODE": MerlinParameter(GapFillMode.NONE),
240-
"FILENAME": MerlinParameter(""),
241-
"FLATFIELDCORRECTION": MerlinParameter(False),
242-
"FLATFIELDFILE": MerlinParameter("None"),
243-
"GAIN": MerlinParameter(GainMode.SLGM),
244-
"HVBIAS": MerlinParameter(15),
245-
"MASKINDATA": MerlinParameter(False),
246-
"NUMFRAMESTOACQUIRE": MerlinParameter(1),
247-
"NUMFRAMESPERTRIGGER": MerlinParameter(1),
248-
"OPERATINGENERGY": MerlinParameter(0),
249-
"PIXELMATRIXSAVEFILE": MerlinParameter(""),
250-
"PIXELMATRIXLOADFILE": MerlinParameter(""),
251-
"POLARITY": MerlinParameter(Polarity.POS),
252-
"SOFTWAREVERSION": MerlinParameter("0.69.0.2"),
253-
"TEMPERATURE": MerlinParameter(0.0),
254-
"THNUMSTEPS": MerlinParameter(0),
255-
"THSTART": MerlinParameter(0),
256-
"THSTEP": MerlinParameter(0),
257-
"THSCAN": MerlinParameter(0),
258-
"THSTOP": MerlinParameter(0),
259-
"THWINDOWMODE": MerlinParameter(False),
260-
"THWINDOWSIZE": MerlinParameter(0),
261-
"TRIGGERSTART": MerlinParameter(Trigger.INT),
262-
"TRIGGERSTOP": MerlinParameter(Trigger.INT),
263-
"SoftTriggerOutTTL": MerlinParameter(False),
264-
"SoftTriggerOutLVDS": MerlinParameter(False),
265-
"TriggerInTTLDelay": MerlinParameter(0),
266-
"TriggerInLVDSDelay": MerlinParameter(0),
267-
"TriggerOutTTL": MerlinParameter(TriggerOut.TriggerInTTL),
268-
"TriggerOutLVDS": MerlinParameter(TriggerOut.TriggerInTTL),
269-
"TriggerOutTTLInvert": MerlinParameter(False),
270-
"TriggerOutLVDSInvert": MerlinParameter(False),
271-
"TriggerUseDelay": MerlinParameter(False),
272-
"TriggerInTTL": MerlinParameter(False),
273-
"TriggerInLVDS": MerlinParameter(False),
274-
}
275-
)
171+
shutter_time_ns: int = int(commands[CommandType.SET]["ACQUISITIONTIME"] * 1e6)
172+
parameters: Dict[str, MerlinParameter[Any]] = field(default_factory=lambda: {})
276173

277174
def initialise(self):
278175
"""Create parameters with custom getters/setters"""
176+
self.parameters["DETECTORSTATUS"] = MerlinParameter(
177+
lambda: self._detector_status,
178+
lambda val: setattr(self, "_detector_status", val),
179+
)
279180
self.parameters["THRESHOLD0"] = MerlinParameter(
280181
lambda: self.get_threshold(0),
281182
lambda val: self.set_threshold(0, val),
@@ -327,6 +228,18 @@ def initialise(self):
327228
lambda: self._operating_energy,
328229
lambda val: self.set_operating_energy(val),
329230
)
231+
ro_params: Dict[str, MerlinParameter[Any]] = {
232+
parameter: MerlinParameter(default_value)
233+
for parameter, default_value in commands[CommandType.GET].items()
234+
if parameter not in self.parameters
235+
}
236+
self.parameters.update(ro_params)
237+
rw_params: Dict[str, MerlinParameter[Any]] = {
238+
parameter: MerlinParameter(default_value)
239+
for parameter, default_value in commands[CommandType.SET].items()
240+
if parameter not in self.parameters
241+
}
242+
self.parameters.update(rw_params)
330243

331244
def get(self, parameter: str):
332245
return self.parameters[parameter].get()
@@ -418,14 +331,16 @@ def THSCAN_cmd(self) -> ErrorCode:
418331
return ErrorCode.UNDERSTOOD
419332

420333
def RESET_cmd(self) -> ErrorCode:
421-
raise NotImplementedError("Fix this")
422-
# TODO: how does this work during acquisition?
423-
# skip = ["chips"]
424-
# for f in fields(self):
425-
# if f.name in skip:
426-
# continue
427-
# setattr(self, f.name, f.default)
428-
# return ErrorCode.UNDERSTOOD
334+
for ctype in [CommandType.GET, CommandType.SET]:
335+
for parameter, default in commands[ctype].items():
336+
try:
337+
if default is not None:
338+
self.set_param(parameter, default)
339+
else:
340+
print("No default value set for", parameter)
341+
except RuntimeError:
342+
print("Could not reset parameter", parameter)
343+
return ErrorCode.UNDERSTOOD
429344

430345
def ABORT_cmd(self) -> ErrorCode:
431346
# TODO: write command
@@ -584,5 +499,4 @@ def get_image(self):
584499
return message
585500

586501
def update(self, time: SimTime, inputs: Inputs) -> DeviceUpdate[Outputs]:
587-
# print('TODO: Doing nothing in update, see EigerDevice.update for comparison')
588502
return DeviceUpdate(self.Outputs(), SimTime(time))

0 commit comments

Comments
 (0)