Code Listing#
Here is the full listing of the Linkam class code.
An up-to-date version can be found here at BMM’s GitHub site.
Note that the status()
method makes use of convenience tools
explained in Colored text in bsui. They are imported at lines 4 and 5
and used in several places.
1 from ophyd import Component as Cpt, EpicsSignal, EpicsSignalRO, PVPositioner
2 from ophyd.signal import DerivedSignal
3
4 from BMM.functions import boxedtext
5 from BMM.functions import error_msg, go_msg
6
7 class AtSetpoint(DerivedSignal):
8 '''A signal that does bit-wise arithmetic on the Linkam's status code'''
9 def __init__(self, parent_attr, *, parent=None, **kwargs):
10 code_signal = getattr(parent, parent_attr)
11 super().__init__(derived_from=code_signal, parent=parent, **kwargs)
12
13 def inverse(self, value):
14 if int(value) & 2 == 2:
15 return 1
16 else:
17 return 0
18
19 def forward(self, value):
20 return value
21
22 class Linkam(PVPositioner):
23 '''An ophyd wrapper around the Linkam T96 controller
24 '''
25
26 ## following https://blueskyproject.io/ophyd/positioners.html#pvpositioner
27 readback = Cpt(EpicsSignalRO, 'TEMP')
28 setpoint = Cpt(EpicsSignal, 'SETPOINT:SET')
29 status_code = Cpt(EpicsSignal, 'STATUS')
30 done = Cpt(AtSetpoint, parent_attr = 'status_code')
31
32 ## all the rest of the Linkam signals
33 init = Cpt(EpicsSignal, 'INIT')
34 model_array = Cpt(EpicsSignal, 'MODEL')
35 serial_array = Cpt(EpicsSignal, 'SERIAL')
36 stage_model_array = Cpt(EpicsSignal, 'STAGE:MODEL')
37 stage_serial_array = Cpt(EpicsSignal, 'STAGE:SERIAL')
38 firm_ver = Cpt(EpicsSignal, 'FIRM:VER')
39 hard_ver = Cpt(EpicsSignal, 'HARD:VER')
40 ctrllr_err = Cpt(EpicsSignal, 'CTRLLR:ERR')
41 config = Cpt(EpicsSignal, 'CONFIG')
42 stage_config = Cpt(EpicsSignal, 'STAGE:CONFIG')
43 disable = Cpt(EpicsSignal, 'DISABLE')
44 dsc = Cpt(EpicsSignal, 'DSC')
45 RR_set = Cpt(EpicsSignal, 'RAMPRATE:SET')
46 RR = Cpt(EpicsSignal, 'RAMPRATE')
47 ramptime = Cpt(EpicsSignal, 'RAMPTIME')
48 startheat = Cpt(EpicsSignal, 'STARTHEAT')
49 holdtime_set = Cpt(EpicsSignal, 'HOLDTIME:SET')
50 holdtime = Cpt(EpicsSignal, 'HOLDTIME')
51 power = Cpt(EpicsSignalRO, 'POWER')
52 lnp_speed = Cpt(EpicsSignal, 'LNP_SPEED')
53 lnp_mode_set = Cpt(EpicsSignal, 'LNP_MODE:SET')
54 lnp_speed_set = Cpt(EpicsSignal, 'LNP_SPEED:SET')
55
56 def on(self):
57 self.startheat.put(1)
58
59 def off(self):
60 self.startheat.put(0)
61
62 def on_plan(self):
63 return(yield from mv(self.startheat, 1))
64
65 def off_plan(self):
66 return(yield from mv(self.startheat, 0))
67
68 def arr2word(self, lst):
69 word = ''
70 for l in lst[:-1]:
71 word += chr(l)
72 return word
73
74 @property
75 def serial(self):
76 return self.arr2word(self.serial_array.get())
77
78 @property
79 def model(self):
80 return self.arr2word(self.model_array.get())
81
82 @property
83 def stage_model(self):
84 return self.arr2word(self.stage_model_array.get())
85
86 @property
87 def stage_serial(self):
88
89 @property
90 def firmware_version(self):
91 return self.arr2word(self.firm_ver.get())
92
93 @property
94 def hardware_version(self):
95 return self.arr2word(self.hard_ver.get())
96
97 def status(self):
98 text = f'\nCurrent temperature = {self.readback.get():.1f}, setpoint = {self .setpoint.get():.1f}\n\n'
99 code = int(self.status_code.get())
100 if code & 1:
101 text += error_msg('Error : yes') + '\n'
102 else:
103 text += 'Error : no\n'
104 if code & 2:
105 text += go_msg('At setpoint : yes') + '\n'
106 else:
107 text += 'At setpoint : no\n'
108 if code & 4:
109 text += go_msg('Heater : on') + '\n'
110 else:
111 text += 'Heater : off\n'
112 if code & 8:
113 text += go_msg('Pump : on') + '\n'
114 else:
115 text += 'Pump : off\n'
116 if code & 16:
117 text += go_msg('Pump Auto : yes') + '\n'
118 else:
119 text += 'Pump Auto : no\n'
120
121 boxedtext(f'Linkam {self.model}, stage {self.stage_model}', text, 'brown', width = 45)