1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
|
import hal
import linuxcnc
import json
DBG_state = 0
DBG_suppress = True
def DBG(str):
if not DBG_state or DBG_suppress: return
print(str)
""" Set of base classes """
class _WidgetBase:
def hal_init(self, master, comp, name,metadata,command,widgets,dbg):
self.master = master
self.hal, self.hal_name = comp, name
self.metadata = metadata
self.widgets = widgets
self.cmd = command
global DBG_suppress
global DBG_state
DBG_state = dbg
self.state = False
# Make sure these are present in metadate
# metadata is pulled from the INI file
if not 'TRUE_STATE' in self.metadata:
self.metadata['TRUE_STATE'] = 1
if not 'FALSE_STATE' in self.metadata:
self.metadata['FALSE_STATE'] = 0
# convert and set pintype and states based on metadata types
if self.metadata['OUTPUT'] == 'S32':
self.pintype=hal.HAL_S32
self.true_state = int(self.metadata['TRUE_STATE'])
self.false_state = int(self.metadata['FALSE_STATE'])
elif self.metadata['OUTPUT'] == 'U32':
self.pintype=hal.HAL_U32
self.true_state = int(self.metadata['TRUE_STATE'])
self.false_state = int(self.metadata['FALSE_STATE'])
elif self.metadata['OUTPUT'] == 'FLOAT':
self.pintype=hal.HAL_FLOAT
self.true_state = float(self.metadata['TRUE_STATE'])
self.false_state = float(self.metadata['FALSE_STATE'])
elif self.metadata['OUTPUT'] == 'BIT':
self.pintype=hal.HAL_BIT
self.true_state = True#self.metadata['TRUE_STATE']
self.false_state = False#self.metadata['FALSE_STATE']
elif self.metadata['OUTPUT'] == 'COMMAND':
self.pintype='COMMAND'
self.true_command = (self.metadata['TRUE_COMMAND'])
self.false_command = (self.metadata['FALSE_COMMAND'])
elif self.metadata['OUTPUT'] == 'ZMQ':
self.pintype='ZMQ'
self.true_function = (self.metadata['TRUE_FUNCTION'])
self.false_function = (self.metadata['FALSE_FUNCTION'])
else:
self.pintype = None
# require a status pin?
try:
if self.metadata['STATUS_PIN'].lower() == 'true':
self.status_pin = True
else:
self.status_pin = False
except:
self.status_pin = False
# default state
try:
if self.metadata['DEFAULT'].lower() == 'true':
self.default_state = True
else:
self.default_state = False
except:
self.default_state = False
# Ok now initialize the widget
self._hal_init()
def _hal_init(self):
""" Child HAL initialization functions """
pass
def toggle_state(self,pressed=True):
""" Update internal button state """
pass
def set_state(self, state):
self.state = state
def hal_update(self):
""" Update HAL state """
pass
class _ToggleBase(_WidgetBase):
def _hal_init(self):
pintype = self.pintype
# if there is a default state set it
# otherwise we use the state from base widget (False)
try:
self.state = self.default_state
except:
pass
# If not a command output requested make the pins
if pintype not in(None,'COMMAND','ZMQ'):
self.hal_pin = self.hal.newpin(self.hal_name, pintype, hal.HAL_OUT)
self.hal_pin_not = self.hal.newpin(self.hal_name + "-not", pintype, hal.HAL_OUT)
# If there is a status requested make there pins
if self.status_pin:
self.hal_status_pin = self.hal.newpin(self.hal_name+ "-state", hal.HAL_BIT, hal.HAL_OUT)
self.hal_status_pin_not = self.hal.newpin(self.hal_name+ "-state-not", hal.HAL_BIT, hal.HAL_OUT)
# Update the pin to the proper state, but don't print debug for this
if pintype not in ('COMMAND','ZMQ'):
DBG_suppress = True
self.hal_update()
DBG_suppress = False
# This finds the function in either command class or
# the optionally loaded custom handler class
# If there are functions in both named the same
# the optionally loaded one is used
def find_method ( self, meth_name):
# print meth_name
try:
if meth_name.lower() in dir(self.cmd.handler_instance):
return self.cmd.handler_instance
elif meth_name.lower() in dir(self.cmd):
return self.cmd
else:
return None
except Exception as e:
print(e)
return None
def toggle_state(self,pressed):
""" Update internal button state """
if pressed:
self.state = not self.state
self.hal_update()
def hal_update(self, *a):
# If not a command output:
# figure out what the state should be and set the
# HAL pin to it. This uses strange looking code for compactness
if self.pintype not in(None,'COMMAND','ZMQ'):
output = self.true_state if self.state else self.false_state
output_not = self.false_state if self.state else self.true_state
self.hal_pin.set(output)
self.hal_pin_not.set(output_not)
DBG( ' Button: %s\n Output: %s\n State: %s\nHAL: %s\n'%(self.hal_name,output,self.state,self.hal_pin.get()))
# If output is to be a command:
# get the output command based on the widget state
# validate and set the arguments
# call the method from internal commands or from the handler file
elif self.pintype == 'COMMAND':
output = self.true_command if self.state else self.false_command
#print 'output',output
if isinstance(output,list):
arg1 = output[1]
output = output[0]
#print 'command:',output,arg1
else:
arg1 = None
if not output.lower() in( 'clear','none'):
module = self.find_method(output)
#print module
if not module is None:
#print 'found method:',output,'using arg:',arg1
module[output.lower()](self, arg1)
DBG( ' Button: %s\n Command: %s\n State: %s\n'%(self.hal_name,output,self.state))
else:
print('Unknown Command',output,self.state)
elif self.pintype == 'ZMQ':
if self.master._zmq_output_enabled:
output = self.true_function if self.state else self.false_function
#print ('output',output)
if isinstance(output,list):
args = self.stringToPython(output[1])
funct = output[0]
else:
funct = output
args = [None]
if funct != 'NONE':
x = {"FUNCTION": funct,
"ARGS": args
}
# convert to json object and send
try:
m1 = json.dumps(x)
self.master._socket.send_multipart(
[bytes(self.master._topic.encode('utf-8')),
bytes((m1).encode('utf-8'))])
except Exception as e:
print('Problem with ZMQ message:',e)
else:
print('ZMQ output not enabled:',funct)
# If there are status pins set them based on state
if self.status_pin:
self.hal_status_pin.set(self.state)
self.hal_status_pin_not.set(not self.state)
def stringToPython(self, cmd):
def convert(scmd):
if 'int(' in scmd:
out = eval(scmd)
elif 'float(' in scmd:
out = eval(scmd)
elif 'bool(' in scmd:
out = eval(scmd)
else:
return scmd
return out
if isinstance(cmd,list):
for num,i in enumerate(cmd):
cmd[num] = convert(i)
else:
cmd = convert(cmd)
return cmd
# A group widget is a master widget for other widgets
# only one of the widgets under it can be true
# it also has it's own pins that changes based on which
# under-widget is true.
class GROUP(_WidgetBase):
def _hal_init(self):
pintype = self.pintype
self.hal_pin = self.hal.newpin(self.hal_name, pintype, hal.HAL_OUT)
self.hal_pin_not = self.hal.newpin(self.hal_name + "-not", pintype, hal.HAL_OUT)
# preset the radio group
# which sets the state of the default widget
# then updates that widget ( to get the state to actually change )
# then to make sure all the other widgets are false
i = self.metadata['DEFAULT']
self.widgets[i].set_state(True)
self.widgets[i].hal_update()
self.toggle_state(i)
DBG_suppress = True
self.hal_update()
DBG_suppress = False
def add_list(self,grouplist):
self.group_list = grouplist
# We toggle all the widgets false other then the 'skip' widget
# Then we set the output of this group-widget's pins
def toggle_state(self,skip):
for i in self.group_list:
if i==skip: continue
self.widgets[i].set_state(False)
self.widgets[i].hal_update()
raw = float(self.widgets[skip].metadata['GROUP_OUTPUT'])
if self.metadata['OUTPUT'] in('U32', 'S32'):
self.output = int(raw)
elif self.metadata['OUTPUT'] == 'FLOAT':
self.output = float(raw)
self.hal_update()
def hal_update(self, *a):
self.hal_pin.set(self.output)
self.hal_pin_not.set(self.output * -1)
DBG( ' group: %s\n Output: %s\n State: %s\nHAL: %s\n'%(self.hal_name,self.output,self.state,self.hal_pin.get()))
###############################
# define button types
###############################
class TOGGLE_BUTTONS( _ToggleBase):
pass
# This works like a toggle button but doesn't ignore the release event by
# overrideing the toggle_state method
class MOMENTARY_BUTTONS( _ToggleBase):
def toggle_state(self,pressed):
""" Update internal button state """
if pressed:
self.state = True
else:
self.state = False
self.hal_update()
# This works like a toggle button but
# then asks the group widget to change all the other buttons in the group
# it overrides the toggle_state method
class RADIO_BUTTONS( _ToggleBase):
def toggle_state(self,pressed):
if pressed:
if self.state: return
self.state = not self.state
self.hal_update()
self.widgets[self.metadata['GROUP']].toggle_state(self.hal_name)
|