def test_with_basic_message(self): iSignalDispatcher = SignalDispatcher("test_channel") def signalHandler(signal=None,sender=None,realsender=None,data=None,time=None,*args,**kwargs): self.assertEquals(signal,"just a test message") self.assertEquals(sender,"test_channel") iSignalDispatcher.add_handler(channel="test_channel",handler=signalHandler) iSignalDispatcher.send_message("just a test message")
def test_with_basic_message(self): iSignalDispatcher = SignalDispatcher("test_channel") def signalHandler(signal=None, sender=None, realsender=None, data=None, time=None, *args, **kwargs): self.assertEquals(signal, "just a test message") self.assertEquals(sender, "test_channel") iSignalDispatcher.add_handler(channel="test_channel", handler=signalHandler) iSignalDispatcher.send_message("just a test message")
class Driver(object): """ Driver class: higher level handler of device connection that formats outgoing and incoming commands according to a spec before they get sent to the lower level connector. It actually mimics the way system device drivers work in a way. You can think of the events beeing sent out by the driver (dataRecieved etc) as interupts of sorts ConnectionModes : 0:setup 1:normal 2:setId 3:forced: to forcefully connect devices which have no deviceId stored Thoughts for future evolution each driver will have a series of endpoints or slots/hooks, which represent the actual subdevices it handles for example for reprap type devices, there is a "position" endpoint (abstract), 3 endpoints for the cartesian bot motors , at least an endpoint for head temperature , one for the heater etc or this could be in a hiearchy , reflecting the one off the nodes: variable endpoint : position, and sub ones for motors """ def __init__(self, deviceType="", connectionType="", hardware_interface_klass=None, logicHandlerKlass=None, protocol=None, options={}, *args, **kwargs): self.driverType = self.__class__.__name__.lower() self.deviceType = deviceType self.connectionType = connectionType self.extra_params = options self.protocol = protocol self.hardware_interface_klass = hardware_interface_klass self.logicHandlerKlass = logicHandlerKlass self.deviceId = None """will be needed to identify a specific device, as the system does not work base on ports""" self._signal_dispatcher = None self.signal_channel_prefix = "" self._signal_channel = "" self.isConfigured = False #when the port association has not been set self.is_handshake_ok = False self.is_authentification_ok = False self.isConnected = False self.isPluggedIn = False self.autoConnect = False #if autoconnect is set to true, the device will be connected as soon as a it is plugged in and detected self.connectionErrors = 0 self.maxConnectionErrors = 2 self.connectionTimeout = 4 self.connectionMode = 1 self.deferred = defer.Deferred() """just for future reference : this is not implemented but would be a declarative way to define the different "configuration steps" of this driver" *basically a dictonary with keys beeing the connection modes, and values a list of strings representing the methods to call *would require a "validator" of sorts (certain elements need to be mandatory : such as the validation/setting of device ids """ configSteps = {} configSteps[0] = ["_handle_deviceHandshake", "_handle_deviceIdInit"] configSteps[1] = [ "_handle_deviceHandshake", "_handle_deviceIdInit", "some_other_method" ] #just a test self._signal_dispatcher = SignalDispatcher("driver_manager") """for exposing capabilites""" self.endpoints = [] def afterInit(self): """this is a workaround needed when loading a driver from db""" try: if not isinstance(self.extra_params, dict): self.extra_params = ast.literal_eval(self.extra_params) except Exception as inst: log.msg("Failed to load driver extra_params from db:", inst, system="Driver", logLevel=logging.CRITICAL) @defer.inlineCallbacks def setup(self, *args, **kwargs): self.hardwareHandler = self.hardware_interface_klass( self, self.protocol, **self.extra_params) self.logicHandler = self.logicHandlerKlass(self, **self.extra_params) node = (yield self.node.get()) env = (yield node.environment.get()) self.signal_channel_prefix = "environment_" + str( env.id) + ".node_" + str(node.id) self._signal_dispatcher.add_handler(handler=self.send_command, signal="addCommand") log.msg("Driver of type", self.driverType, "setup sucessfully", system="Driver", logLevel=logging.INFO) def bind(self, port, setId=True): self.deferred = defer.Deferred() log.msg("Attemtping to bind driver", self, "with deviceId:", self.deviceId, "to port", port, system="Driver", logLevel=logging.DEBUG) self.hardwareHandler.connect(setIdMode=setId, port=port) return self.deferred def connect(self, mode=None, *args, **kwargs): if not self.isConnected: if mode is not None: self.connectionMode = mode log.msg("Connecting in mode:", self.connectionMode, system="Driver", logLevel=logging.CRITICAL) if mode == 3: """special case for forced connection""" unboundPorts = DriverManager.bindings.get_unbound_ports() if len(unboundPorts) > 0: port = unboundPorts[0] log.msg("Connecting in mode:", self.connectionMode, "to port", port, system="Driver", logLevel=logging.CRITICAL) DriverManager.bindings.bind(self, port) self.pluggedIn(port) self.hardwareHandler.connect(port=port) else: self.hardwareHandler.connect() else: self.hardwareHandler.connect() def reconnect(self, *args, **kwargs): self.hardwareHandler.reconnect(*args, **kwargs) def disconnect(self, *args, **kwargs): self.hardwareHandler.disconnect(*args, **kwargs) def pluggedIn(self, port): self._send_signal("plugged_In", port) self.isPluggedIn = True if self.autoConnect: #slight delay, to prevent certain problems when trying to send data to the device too fast reactor.callLater(1, self.connect, 1) def pluggedOut(self, port): self.isConfigured = False self.is_handshake_ok = False self.is_authentification_ok = False self.isConnected = False self.isPluggedIn = False self._send_signal("plugged_Out", port) #self._signal_dispatcher.send_message("pluggedOut",{"data":port}) def _send_signal(self, signal="", data=None): prefix = self.signal_channel_prefix + ".driver." self._signal_dispatcher.send_message(prefix + signal, self, data) def send_command(self, data, sender=None, callback=None, *args, **kwargs): # print("going to send command",data,"from",sender) if not self.isConnected: raise DeviceNotConnected() if self.logicHandler: self.logicHandler._handle_request(data=data, sender=sender, callback=callback) def _send_data(self, data, *arrgs, **kwargs): self.hardwareHandler.send_data(data) def _handle_response(self, data): if self.logicHandler: self.logicHandler._handle_response(data) """higher level methods""" def startup(self): pass def shutdown(self): pass def init(self): pass def get_firmware_version(self): pass def set_debug_level(self, level): pass def teststuff(self, params, *args, **kwargs): pass def variable_set(self, variable, params, sender=None, *args, **kwargs): pass def variable_get(self, variable, params, sender=None, *args, **kwargs): pass """ #################################################################################### Experimental """ def start_command(self): pass def close_command(self): pass def get_endpoint(self, filter=None): """return a list of endpoints, filtered by parameters""" d = defer.Deferred() def filter_check(endpoint, filter): for key in filter.keys(): if not getattr(endpoint, key) in filter[key]: return False return True def get(filter): if filter: return [ endpoint for endpoint in self.endpoints if filter_check(endpoint, filter) ] else: pass d.addCallback(get) reactor.callLater(0.5, d.callback, filter) return d
class Driver(BaseComponent): """ Driver class: higher level handler of device connection that formats outgoing and incoming commands according to a spec before they get sent to the lower level connector. It actually mimics the way system device drivers work in a way. You can think of the events beeing sent out by the driver(dataRecieved...) as interupts of sorts """ def __init__(self, hardware_id=None, auto_connect=False, max_connection_errors=2, connection_timeout=4, do_hanshake=False, do_authentification=False): """ autoconnect:if autoconnect is True,device will be connected as soon as it is plugged in and detected max_connection_errors: the number of connection errors above which the driver gets disconnected connection_timeout: the number of seconds after which the driver gets disconnected (only in the initial , configuration phases by default) """ BaseComponent.__init__(self, parent=None) self.auto_connect = auto_connect self.max_connection_errors = max_connection_errors self.connection_timeout = connection_timeout self.do_authentification = do_authentification self.do_handshake = do_hanshake self._hardware_interface = None self.hardware_id = hardware_id self.is_configured = False self.is_bound = False # when port association has not been set self.is_handshake_ok = False self.is_authentification_ok = False self.is_connected = False self.is_bound = False self.is_busy = False self.errors = [] self.connection_mode = 1 self._connection_errors = 0 self._connection_timeout = None self.deferred = defer.Deferred() self._signal_channel_prefix = "" self._signal_dispatcher = SignalDispatcher("driver_manager") def __eq__(self, other): return self.__class__ == other.__class__ def __ne__(self, other): return not self.__eq__(other) def setup(self, *args, **kwargs): """do the driver setup phase""" self._signal_dispatcher.add_handler(handler=self.send_command, signal="addCommand") class_name = self.__class__.__name__.lower() log.msg("Driver of type", class_name, "setup sucessfully", system="Driver", logLevel=logging.INFO) def _send_signal(self, signal="", data=None): prefix = self._signal_channel_prefix + ".driver." self._signal_dispatcher.send_message(prefix + signal, self, data) @property def hardware_interface_class(self): """Get the current voltage.""" return self._hardware_interface.__class__ @property def connection_errors(self): """Get the current voltage.""" return self._connection_errors @connection_errors.setter def connection_errors(self, value): self._connection_errors = value if self._connection_errors >= self.max_connection_errors: self.disconnect(clear_port=True) log.msg( "cricital error while (re-)starting serial connection : please check your driver settings and device id, as well as cables, and make sure no other process is using the port ", system="Driver", logLevel=logging.CRITICAL) self.deferred.errback(self.errors[-1]) #self._hardware_interface._connect() #weird way of handling disconnect """ ########################################################################### The following are the timeout related methods """ def set_connection_timeout(self): """sets internal timeout""" if self.connection_timeout > 0: log.msg("Setting timeout at ", time.time(), system="Driver", logLevel=logging.DEBUG) self._connection_timeout = reactor.callLater( self.connection_timeout, self._connection_timeout_check) def cancel_connection_timeout(self): """cancels internal timeout""" if self._connection_timeout is not None: try: self._connection_timeout.cancel() log.msg("Canceling timeout at ", time.time(), system="Driver", logLevel=logging.DEBUG) except: pass def _connection_timeout_check(self): """checks the timeout""" log.msg("Timeout check at ", time.time(), logLevel=logging.DEBUG) self.cancel_connection_timeout() self.errors.append(TimeoutError()) self.connection_errors += 1 if self.connection_errors < self.max_connection_errors: self.reconnect() """ ########################################################################### The following are the connection related methods """ def connect(self, port=None, connection_mode=None): """ connection_mode : 0:setup 1:normal 2:forced: to forcefully connect devices which have no deviceId stored """ if self.is_connected: raise Exception("Driver already connected") if connection_mode is None: raise Exception("Invalid connection mode") self.deferred = defer.Deferred() self.connection_mode = connection_mode self.errors = [] self._connection_errors = 0 self.is_busy = True mode_str = "Normal" if self.connection_mode == 1: mode_str = "Setup" log.msg("Connecting driver in %s mode:" % mode_str, system="Driver", logLevel=logging.CRITICAL) reactor.callLater(0.1, self._hardware_interface.connect, port) return self.deferred def reconnect(self, *args, **kwargs): """Reconnect driver""" self._hardware_interface.reconnect(*args, **kwargs) def disconnect(self, *args, **kwargs): """Disconnect driver""" log.msg("Disconnecting driver", system="Driver", logLevel=logging.CRITICAL) self.is_connected = False self.is_busy = False self.cancel_connection_timeout() self._hardware_interface.disconnect(*args, **kwargs) """ ########################################################################### The following are the methods dealing with communication with the hardware """ def send_command(self, command): """send a command to the physical device""" if not self.is_connected: raise DeviceNotConnected() self.command_deferred = defer.Deferred() reactor.callLater(0.01, self._hardware_interface.send_data, command) return self.command_deferred def _handle_response(self, data): """handle hardware response""" self.command_deferred.callback(data) """ ########################################################################### The following are the higher level methods """ def startup(self): """send startup command to hardware""" raise NotImplementedError() def shutdown(self): """send shutdown command to hardware""" raise NotImplementedError() def get_firmware_info(self): """retrieve firmware version from hardware""" raise NotImplementedError() def set_debug_level(self, level): """set hardware debug level, if any""" raise NotImplementedError()
class Driver(BaseComponent): """ Driver class: higher level handler of device connection that formats outgoing and incoming commands according to a spec before they get sent to the lower level connector. It actually mimics the way system device drivers work in a way. You can think of the events beeing sent out by the driver(dataRecieved...) as interupts of sorts """ def __init__(self, hardware_id=None, auto_connect=False, max_connection_errors=2, connection_timeout=4, do_hanshake=False, do_authentification=False): """ autoconnect:if autoconnect is True,device will be connected as soon as it is plugged in and detected max_connection_errors: the number of connection errors above which the driver gets disconnected connection_timeout: the number of seconds after which the driver gets disconnected (only in the initial , configuration phases by default) """ BaseComponent.__init__(self, parent=None) self.auto_connect = auto_connect self.max_connection_errors = max_connection_errors self.connection_timeout = connection_timeout self.do_authentification = do_authentification self.do_handshake = do_hanshake self._hardware_interface = None self.hardware_id = hardware_id self.is_configured = False self.is_bound = False # when port association has not been set self.is_handshake_ok = False self.is_authentification_ok = False self.is_connected = False self.is_bound = False self.is_busy = False self.errors = [] self.connection_mode = 1 self._connection_errors = 0 self._connection_timeout = None self.deferred = defer.Deferred() self._signal_channel_prefix = "" self._signal_dispatcher = SignalDispatcher("driver_manager") def __eq__(self, other): return self.__class__ == other.__class__ def __ne__(self, other): return not self.__eq__(other) def setup(self, *args, **kwargs): """do the driver setup phase""" self._signal_dispatcher.add_handler(handler=self.send_command, signal="addCommand") class_name = self.__class__.__name__.lower() log.msg("Driver of type", class_name, "setup sucessfully", system="Driver", logLevel=logging.INFO) def _send_signal(self, signal="", data=None): prefix = self._signal_channel_prefix + ".driver." self._signal_dispatcher.send_message(prefix + signal, self, data) @property def hardware_interface_class(self): """Get the current voltage.""" return self._hardware_interface.__class__ @property def connection_errors(self): """Get the current voltage.""" return self._connection_errors @connection_errors.setter def connection_errors(self, value): self._connection_errors = value if self._connection_errors >= self.max_connection_errors: self.disconnect(clear_port=True) log.msg("cricital error while (re-)starting serial connection : please check your driver settings and device id, as well as cables, and make sure no other process is using the port ", system="Driver", logLevel=logging.CRITICAL) self.deferred.errback(self.errors[-1]) #self._hardware_interface._connect() #weird way of handling disconnect """ ########################################################################### The following are the timeout related methods """ def set_connection_timeout(self): """sets internal timeout""" if self.connection_timeout > 0: log.msg("Setting timeout at ", time.time(), system="Driver", logLevel=logging.DEBUG) self._connection_timeout = reactor.callLater(self.connection_timeout, self._connection_timeout_check) def cancel_connection_timeout(self): """cancels internal timeout""" if self._connection_timeout is not None: try: self._connection_timeout.cancel() log.msg("Canceling timeout at ", time.time(), system="Driver", logLevel=logging.DEBUG) except: pass def _connection_timeout_check(self): """checks the timeout""" log.msg("Timeout check at ", time.time(), logLevel=logging.DEBUG) self.cancel_connection_timeout() self.errors.append(TimeoutError()) self.connection_errors += 1 if self.connection_errors < self.max_connection_errors: self.reconnect() """ ########################################################################### The following are the connection related methods """ def connect(self, port=None, connection_mode=None): """ connection_mode : 0:setup 1:normal 2:forced: to forcefully connect devices which have no deviceId stored """ if self.is_connected: raise Exception("Driver already connected") if connection_mode is None: raise Exception("Invalid connection mode") self.deferred = defer.Deferred() self.connection_mode = connection_mode self.errors = [] self._connection_errors = 0 self.is_busy = True mode_str = "Normal" if self.connection_mode == 1: mode_str = "Setup" log.msg("Connecting driver in %s mode:" % mode_str, system="Driver", logLevel=logging.CRITICAL) reactor.callLater(0.1, self._hardware_interface.connect, port) return self.deferred def reconnect(self, *args, **kwargs): """Reconnect driver""" self._hardware_interface.reconnect(*args, **kwargs) def disconnect(self, *args, **kwargs): """Disconnect driver""" log.msg("Disconnecting driver", system="Driver", logLevel=logging.CRITICAL) self.is_connected = False self.is_busy = False self.cancel_connection_timeout() self._hardware_interface.disconnect(*args, **kwargs) """ ########################################################################### The following are the methods dealing with communication with the hardware """ def send_command(self, command): """send a command to the physical device""" if not self.is_connected: raise DeviceNotConnected() self.command_deferred = defer.Deferred() reactor.callLater(0.01, self._hardware_interface.send_data, command) return self.command_deferred def _handle_response(self, data): """handle hardware response""" self.command_deferred.callback(data) """ ########################################################################### The following are the higher level methods """ def startup(self): """send startup command to hardware""" raise NotImplementedError() def shutdown(self): """send shutdown command to hardware""" raise NotImplementedError() def get_firmware_info(self): """retrieve firmware version from hardware""" raise NotImplementedError() def set_debug_level(self, level): """set hardware debug level, if any""" raise NotImplementedError()
class Driver(object): """ Driver class: higher level handler of device connection that formats outgoing and incoming commands according to a spec before they get sent to the lower level connector. It actually mimics the way system device drivers work in a way. You can think of the events beeing sent out by the driver (dataRecieved etc) as interupts of sorts ConnectionModes : 0:setup 1:normal 2:setId 3:forced: to forcefully connect devices which have no deviceId stored Thoughts for future evolution each driver will have a series of endpoints or slots/hooks, which represent the actual subdevices it handles for example for reprap type devices, there is a "position" endpoint (abstract), 3 endpoints for the cartesian bot motors , at least an endpoint for head temperature , one for the heater etc or this could be in a hiearchy , reflecting the one off the nodes: variable endpoint : position, and sub ones for motors """ def __init__(self,deviceType="",connectionType="",hardware_interface_klass=None,logicHandlerKlass=None,protocol=None,options={},*args,**kwargs): self.driverType=self.__class__.__name__.lower() self.deviceType=deviceType self.connectionType=connectionType self.extra_params=options self.protocol=protocol self.hardware_interface_klass=hardware_interface_klass self.logicHandlerKlass=logicHandlerKlass self.deviceId=None """will be needed to identify a specific device, as the system does not work base on ports""" self._signal_dispatcher=None self.signal_channel_prefix="" self._signal_channel="" self.isConfigured=False#when the port association has not been set self.is_handshake_ok=False self.is_authentification_ok=False self.isConnected=False self.isPluggedIn=False self.autoConnect=False#if autoconnect is set to true, the device will be connected as soon as a it is plugged in and detected self.connectionErrors=0 self.maxConnectionErrors=2 self.connectionTimeout=4 self.connectionMode=1 self.deferred=defer.Deferred() """just for future reference : this is not implemented but would be a declarative way to define the different "configuration steps" of this driver" *basically a dictonary with keys beeing the connection modes, and values a list of strings representing the methods to call *would require a "validator" of sorts (certain elements need to be mandatory : such as the validation/setting of device ids """ configSteps={} configSteps[0]=["_handle_deviceHandshake","_handle_deviceIdInit"] configSteps[1]=["_handle_deviceHandshake","_handle_deviceIdInit","some_other_method"] #just a test self._signal_dispatcher=SignalDispatcher("driver_manager") """for exposing capabilites""" self.endpoints=[] def afterInit(self): """this is a workaround needed when loading a driver from db""" try: if not isinstance(self.extra_params,dict): self.extra_params=ast.literal_eval(self.extra_params) except Exception as inst: log.msg("Failed to load driver extra_params from db:",inst,system="Driver",logLevel=logging.CRITICAL) @defer.inlineCallbacks def setup(self,*args,**kwargs): self.hardwareHandler=self.hardware_interface_klass(self,self.protocol,**self.extra_params) self.logicHandler=self.logicHandlerKlass(self,**self.extra_params) node= (yield self.node.get()) env= (yield node.environment.get()) self.signal_channel_prefix="environment_"+str(env.id)+".node_"+str(node.id) self._signal_dispatcher.add_handler(handler=self.send_command,signal="addCommand") log.msg("Driver of type",self.driverType ,"setup sucessfully",system="Driver",logLevel=logging.INFO) def bind(self,port,setId=True): self.deferred=defer.Deferred() log.msg("Attemtping to bind driver",self ,"with deviceId:",self.deviceId,"to port",port,system="Driver",logLevel=logging.DEBUG) self.hardwareHandler.connect(setIdMode=setId,port=port) return self.deferred def connect(self,mode=None,*args,**kwargs): if not self.isConnected: if mode is not None: self.connectionMode=mode log.msg("Connecting in mode:",self.connectionMode,system="Driver",logLevel=logging.CRITICAL) if mode==3: """special case for forced connection""" unboundPorts=DriverManager.bindings.get_unbound_ports() if len(unboundPorts)>0: port=unboundPorts[0] log.msg("Connecting in mode:",self.connectionMode,"to port",port,system="Driver",logLevel=logging.CRITICAL) DriverManager.bindings.bind(self,port) self.pluggedIn(port) self.hardwareHandler.connect(port=port) else: self.hardwareHandler.connect() else: self.hardwareHandler.connect() def reconnect(self,*args,**kwargs): self.hardwareHandler.reconnect(*args,**kwargs) def disconnect(self,*args,**kwargs): self.hardwareHandler.disconnect(*args,**kwargs) def pluggedIn(self,port): self._send_signal("plugged_In",port) self.isPluggedIn=True if self.autoConnect: #slight delay, to prevent certain problems when trying to send data to the device too fast reactor.callLater(1,self.connect,1) def pluggedOut(self,port): self.isConfigured=False self.is_handshake_ok=False self.is_authentification_ok=False self.isConnected=False self.isPluggedIn=False self._send_signal("plugged_Out",port) #self._signal_dispatcher.send_message("pluggedOut",{"data":port}) def _send_signal(self,signal="",data=None): prefix=self.signal_channel_prefix+".driver." self._signal_dispatcher.send_message(prefix+signal,self,data) def send_command(self,data,sender=None,callback=None,*args,**kwargs): # print("going to send command",data,"from",sender) if not self.isConnected: raise DeviceNotConnected() if self.logicHandler: self.logicHandler._handle_request(data=data,sender=sender,callback=callback) def _send_data(self,data,*arrgs,**kwargs): self.hardwareHandler.send_data(data) def _handle_response(self,data): if self.logicHandler: self.logicHandler._handle_response(data) """higher level methods""" def startup(self): pass def shutdown(self): pass def init(self): pass def get_firmware_version(self): pass def set_debug_level(self,level): pass def teststuff(self,params,*args,**kwargs): pass def variable_set(self,variable,params,sender=None,*args,**kwargs): pass def variable_get(self,variable,params,sender=None,*args,**kwargs): pass """ #################################################################################### Experimental """ def start_command(self): pass def close_command(self): pass def get_endpoint(self,filter=None): """return a list of endpoints, filtered by parameters""" d=defer.Deferred() def filter_check(endpoint,filter): for key in filter.keys(): if not getattr(endpoint, key) in filter[key]: return False return True def get(filter): if filter: return [endpoint for endpoint in self.endpoints if filter_check(endpoint,filter)] else: pass d.addCallback(get) reactor.callLater(0.5,d.callback,filter) return d