class RemoteComponentBase(object): def __init__(self, debuglevel=0, debugname='Remote Component Base'): self.debuglevel = debuglevel self.debugname = debugname self._error_string = '' self.on_error_string_changed = [] # Halrcmd self._halrcmd_channel = RpcClient(debuglevel=debuglevel) self._halrcmd_channel.debugname = '%s - %s' % (self.debugname, 'halrcmd') self._halrcmd_channel.on_state_changed.append( self._halrcmd_channel_state_changed) self._halrcmd_channel.on_socket_message_received.append( self._halrcmd_channel_message_received) # more efficient to reuse protobuf messages self._halrcmd_rx = Container() self._halrcmd_tx = Container() # Halrcomp self._halrcomp_channel = HalrcompSubscribe(debuglevel=debuglevel) self._halrcomp_channel.debugname = '%s - %s' % (self.debugname, 'halrcomp') self._halrcomp_channel.on_state_changed.append( self._halrcomp_channel_state_changed) self._halrcomp_channel.on_socket_message_received.append( self._halrcomp_channel_message_received) # more efficient to reuse protobuf messages self._halrcomp_rx = Container() # callbacks self.on_halrcmd_message_received = [] self.on_halrcomp_message_received = [] self.on_state_changed = [] # fsm self._fsm = Fysom({ 'initial': 'down', 'events': [ { 'name': 'connect', 'src': 'down', 'dst': 'trying' }, { 'name': 'halrcmd_up', 'src': 'trying', 'dst': 'bind' }, { 'name': 'disconnect', 'src': 'trying', 'dst': 'down' }, { 'name': 'halrcomp_bind_msg_sent', 'src': 'bind', 'dst': 'binding' }, { 'name': 'no_bind', 'src': 'bind', 'dst': 'syncing' }, { 'name': 'bind_confirmed', 'src': 'binding', 'dst': 'syncing' }, { 'name': 'bind_rejected', 'src': 'binding', 'dst': 'error' }, { 'name': 'halrcmd_trying', 'src': 'binding', 'dst': 'trying' }, { 'name': 'disconnect', 'src': 'binding', 'dst': 'down' }, { 'name': 'halrcmd_trying', 'src': 'syncing', 'dst': 'trying' }, { 'name': 'halrcomp_up', 'src': 'syncing', 'dst': 'sync' }, { 'name': 'sync_failed', 'src': 'syncing', 'dst': 'error' }, { 'name': 'disconnect', 'src': 'syncing', 'dst': 'down' }, { 'name': 'pins_synced', 'src': 'sync', 'dst': 'synced' }, { 'name': 'halrcomp_trying', 'src': 'synced', 'dst': 'syncing' }, { 'name': 'halrcmd_trying', 'src': 'synced', 'dst': 'trying' }, { 'name': 'set_rejected', 'src': 'synced', 'dst': 'error' }, { 'name': 'halrcomp_set_msg_sent', 'src': 'synced', 'dst': 'synced' }, { 'name': 'disconnect', 'src': 'synced', 'dst': 'down' }, { 'name': 'disconnect', 'src': 'error', 'dst': 'down' }, ], }) self._fsm.ondown = self._on_fsm_down self._fsm.onafterconnect = self._on_fsm_connect self._fsm.onleavedown = self._on_fsm_down_exit self._fsm.ontrying = self._on_fsm_trying self._fsm.onafterhalrcmd_up = self._on_fsm_halrcmd_up self._fsm.onafterdisconnect = self._on_fsm_disconnect self._fsm.onbind = self._on_fsm_bind self._fsm.onafterhalrcomp_bind_msg_sent = self._on_fsm_halrcomp_bind_msg_sent self._fsm.onafterno_bind = self._on_fsm_no_bind self._fsm.onbinding = self._on_fsm_binding self._fsm.onafterbind_confirmed = self._on_fsm_bind_confirmed self._fsm.onafterbind_rejected = self._on_fsm_bind_rejected self._fsm.onafterhalrcmd_trying = self._on_fsm_halrcmd_trying self._fsm.onsyncing = self._on_fsm_syncing self._fsm.onafterhalrcomp_up = self._on_fsm_halrcomp_up self._fsm.onaftersync_failed = self._on_fsm_sync_failed self._fsm.onsync = self._on_fsm_sync self._fsm.onafterpins_synced = self._on_fsm_pins_synced self._fsm.onsynced = self._on_fsm_synced self._fsm.onafterhalrcomp_trying = self._on_fsm_halrcomp_trying self._fsm.onafterset_rejected = self._on_fsm_set_rejected self._fsm.onafterhalrcomp_set_msg_sent = self._on_fsm_halrcomp_set_msg_sent self._fsm.onerror = self._on_fsm_error def _on_fsm_down(self, _): if self.debuglevel > 0: print('[%s]: state DOWN entry' % self.debugname) self.set_disconnected() if self.debuglevel > 0: print('[%s]: state DOWN' % self.debugname) for cb in self.on_state_changed: cb('down') return True def _on_fsm_connect(self, _): if self.debuglevel > 0: print('[%s]: event CONNECT' % self.debugname) self.add_pins() self.start_halrcmd_channel() return True def _on_fsm_down_exit(self, _): if self.debuglevel > 0: print('[%s]: state DOWN exit' % self.debugname) self.set_connecting() return True def _on_fsm_trying(self, _): if self.debuglevel > 0: print('[%s]: state TRYING' % self.debugname) for cb in self.on_state_changed: cb('trying') return True def _on_fsm_halrcmd_up(self, _): if self.debuglevel > 0: print('[%s]: event HALRCMD UP' % self.debugname) self.bind_component() return True def _on_fsm_disconnect(self, _): if self.debuglevel > 0: print('[%s]: event DISCONNECT' % self.debugname) self.stop_halrcmd_channel() self.stop_halrcomp_channel() self.remove_pins() return True def _on_fsm_bind(self, _): if self.debuglevel > 0: print('[%s]: state BIND' % self.debugname) for cb in self.on_state_changed: cb('bind') return True def _on_fsm_halrcomp_bind_msg_sent(self, _): if self.debuglevel > 0: print('[%s]: event HALRCOMP BIND MSG SENT' % self.debugname) return True def _on_fsm_no_bind(self, _): if self.debuglevel > 0: print('[%s]: event NO BIND' % self.debugname) self.start_halrcomp_channel() return True def _on_fsm_binding(self, _): if self.debuglevel > 0: print('[%s]: state BINDING' % self.debugname) for cb in self.on_state_changed: cb('binding') return True def _on_fsm_bind_confirmed(self, _): if self.debuglevel > 0: print('[%s]: event BIND CONFIRMED' % self.debugname) self.start_halrcomp_channel() return True def _on_fsm_bind_rejected(self, _): if self.debuglevel > 0: print('[%s]: event BIND REJECTED' % self.debugname) self.stop_halrcmd_channel() return True def _on_fsm_halrcmd_trying(self, _): if self.debuglevel > 0: print('[%s]: event HALRCMD TRYING' % self.debugname) return True def _on_fsm_syncing(self, _): if self.debuglevel > 0: print('[%s]: state SYNCING' % self.debugname) for cb in self.on_state_changed: cb('syncing') return True def _on_fsm_halrcomp_up(self, _): if self.debuglevel > 0: print('[%s]: event HALRCOMP UP' % self.debugname) return True def _on_fsm_sync_failed(self, _): if self.debuglevel > 0: print('[%s]: event SYNC FAILED' % self.debugname) self.stop_halrcomp_channel() self.stop_halrcmd_channel() return True def _on_fsm_sync(self, _): if self.debuglevel > 0: print('[%s]: state SYNC' % self.debugname) for cb in self.on_state_changed: cb('sync') return True def _on_fsm_pins_synced(self, _): if self.debuglevel > 0: print('[%s]: event PINS SYNCED' % self.debugname) return True def _on_fsm_synced(self, _): if self.debuglevel > 0: print('[%s]: state SYNCED entry' % self.debugname) self.set_connected() if self.debuglevel > 0: print('[%s]: state SYNCED' % self.debugname) for cb in self.on_state_changed: cb('synced') return True def _on_fsm_halrcomp_trying(self, _): if self.debuglevel > 0: print('[%s]: event HALRCOMP TRYING' % self.debugname) self.unsync_pins() self.set_timeout() return True def _on_fsm_set_rejected(self, _): if self.debuglevel > 0: print('[%s]: event SET REJECTED' % self.debugname) self.stop_halrcomp_channel() self.stop_halrcmd_channel() return True def _on_fsm_halrcomp_set_msg_sent(self, _): if self.debuglevel > 0: print('[%s]: event HALRCOMP SET MSG SENT' % self.debugname) return True def _on_fsm_error(self, _): if self.debuglevel > 0: print('[%s]: state ERROR entry' % self.debugname) self.set_error() if self.debuglevel > 0: print('[%s]: state ERROR' % self.debugname) for cb in self.on_state_changed: cb('error') return True @property def error_string(self): return self._error_string @error_string.setter def error_string(self, string): if self._error_string is string: return self._error_string = string for cb in self.on_error_string_changed: cb(string) @property def halrcmd_uri(self): return self._halrcmd_channel.socket_uri @halrcmd_uri.setter def halrcmd_uri(self, value): self._halrcmd_channel.socket_uri = value @property def halrcomp_uri(self): return self._halrcomp_channel.socket_uri @halrcomp_uri.setter def halrcomp_uri(self, value): self._halrcomp_channel.socket_uri = value def bind_component(self): print('WARNING: slot bind component unimplemented') def add_pins(self): print('WARNING: slot add pins unimplemented') def remove_pins(self): print('WARNING: slot remove pins unimplemented') def unsync_pins(self): print('WARNING: slot unsync pins unimplemented') def set_connected(self): print('WARNING: slot set connected unimplemented') def set_error(self): print('WARNING: slot set error unimplemented') def set_disconnected(self): print('WARNING: slot set disconnected unimplemented') def set_connecting(self): print('WARNING: slot set connecting unimplemented') def set_timeout(self): print('WARNING: slot set timeout unimplemented') def no_bind(self): if self._fsm.isstate('bind'): self._fsm.no_bind() def pins_synced(self): if self._fsm.isstate('sync'): self._fsm.pins_synced() def start(self): if self._fsm.isstate('down'): self._fsm.connect() def stop(self): if self._fsm.isstate('trying'): self._fsm.disconnect() elif self._fsm.isstate('binding'): self._fsm.disconnect() elif self._fsm.isstate('syncing'): self._fsm.disconnect() elif self._fsm.isstate('synced'): self._fsm.disconnect() elif self._fsm.isstate('error'): self._fsm.disconnect() def add_halrcomp_topic(self, name): self._halrcomp_channel.add_socket_topic(name) def remove_halrcomp_topic(self, name): self._halrcomp_channel.remove_socket_topic(name) def clear_halrcomp_topics(self): self._halrcomp_channel.clear_socket_topics() def start_halrcmd_channel(self): self._halrcmd_channel.start() def stop_halrcmd_channel(self): self._halrcmd_channel.stop() def start_halrcomp_channel(self): self._halrcomp_channel.start() def stop_halrcomp_channel(self): self._halrcomp_channel.stop() # process all messages received on halrcmd def _halrcmd_channel_message_received(self, rx): # react to halrcomp bind confirm message if rx.type == pb.MT_HALRCOMP_BIND_CONFIRM: if self._fsm.isstate('binding'): self._fsm.bind_confirmed() # react to halrcomp bind reject message elif rx.type == pb.MT_HALRCOMP_BIND_REJECT: # update error string with note self.error_string = '' for note in rx.note: self.error_string += note + '\n' if self._fsm.isstate('binding'): self._fsm.bind_rejected() # react to halrcomp set reject message elif rx.type == pb.MT_HALRCOMP_SET_REJECT: # update error string with note self.error_string = '' for note in rx.note: self.error_string += note + '\n' if self._fsm.isstate('synced'): self._fsm.set_rejected() for cb in self.on_halrcmd_message_received: cb(rx) # process all messages received on halrcomp def _halrcomp_channel_message_received(self, identity, rx): # react to halrcomp full update message if rx.type == pb.MT_HALRCOMP_FULL_UPDATE: self.halrcomp_full_update_received(identity, rx) # react to halrcomp incremental update message elif rx.type == pb.MT_HALRCOMP_INCREMENTAL_UPDATE: self.halrcomp_incremental_update_received(identity, rx) # react to halrcomp error message elif rx.type == pb.MT_HALRCOMP_ERROR: # update error string with note self.error_string = '' for note in rx.note: self.error_string += note + '\n' if self._fsm.isstate('syncing'): self._fsm.sync_failed() self.halrcomp_error_received(identity, rx) for cb in self.on_halrcomp_message_received: cb(identity, rx) def halrcomp_full_update_received(self, identity, rx): print('SLOT halrcomp full update unimplemented') def halrcomp_incremental_update_received(self, identity, rx): print('SLOT halrcomp incremental update unimplemented') def halrcomp_error_received(self, identity, rx): print('SLOT halrcomp error unimplemented') def send_halrcmd_message(self, msg_type, tx): self._halrcmd_channel.send_socket_message(msg_type, tx) if msg_type == pb.MT_HALRCOMP_BIND: if self._fsm.isstate('bind'): self._fsm.halrcomp_bind_msg_sent() elif msg_type == pb.MT_HALRCOMP_SET: if self._fsm.isstate('synced'): self._fsm.halrcomp_set_msg_sent() def send_halrcomp_bind(self, tx): self.send_halrcmd_message(pb.MT_HALRCOMP_BIND, tx) def send_halrcomp_set(self, tx): self.send_halrcmd_message(pb.MT_HALRCOMP_SET, tx) def _halrcmd_channel_state_changed(self, state): if state == 'trying': if self._fsm.isstate('syncing'): self._fsm.halrcmd_trying() elif self._fsm.isstate('synced'): self._fsm.halrcmd_trying() elif self._fsm.isstate('binding'): self._fsm.halrcmd_trying() elif state == 'up': if self._fsm.isstate('trying'): self._fsm.halrcmd_up() def _halrcomp_channel_state_changed(self, state): if state == 'trying': if self._fsm.isstate('synced'): self._fsm.halrcomp_trying() elif state == 'up': if self._fsm.isstate('syncing'): self._fsm.halrcomp_up()