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)