def test_ignore_invalid_triggers(self): a_state = State('A') transitions = [['a_to_b', 'A', 'B']] # Exception is triggered by default b_state = State('B') m1 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B') with self.assertRaises(MachineError): m1.a_to_b() # Exception is suppressed, so this passes b_state = State('B', ignore_invalid_triggers=True) m2 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B') m2.a_to_b() # Set for some states but not others new_states = ['C', 'D'] m1.add_states(new_states, ignore_invalid_triggers=True) m1.to_D() m1.a_to_b() # passes because exception suppressed for D m1.to_B() with self.assertRaises(MachineError): m1.a_to_b() # Set at machine level m3 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B', ignore_invalid_triggers=True) m3.a_to_b()
def test_ignore_invalid_triggers(self): a_state = State('A') transitions = [['a_to_b', 'A', 'B']] # Exception is triggered by default b_state = State('B') m1 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B') with self.assertRaises(MachineError): m1.a_to_b() # Exception is suppressed, so this passes b_state = State('B', ignore_invalid_triggers=True) m2 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B') m2.a_to_b() # Set for some states but not others new_states = ['C', 'D'] m1.add_states(new_states, ignore_invalid_triggers=True) m1.to_D() m1.a_to_b() # passes because exception suppressed for D m1.to_B() with self.assertRaises(MachineError): m1.a_to_b() # Set at machine level m3 = Machine(None, states=[a_state, b_state], transitions=transitions, initial='B', ignore_invalid_triggers=True) m3.a_to_b()
def parse(fl, pat, states=states, transitions=transitions, log=True): """parses file The transitions and states for the machine are defined above""" if log == True: fn = os.path.join('/Users/rikhoekstra/surfdrive/rsg/ocr/', 'rsg_parser.log') logging.basicConfig(filename=fn, filemode='w', level=logging.INFO) logging.getLogger('transitions').setLevel(logging.INFO) page = fl.read() fl.close() model = ProcessingModel(page, p2=pat) machine = Machine( model=model, transitions=transitions, initial='start', ) states = states machine.add_states(states, ignore_invalid_triggers=True) try: model.d_page() model.divide() while model.has_next_element(): model.check_date() model.check_resolution() model.check_footnote() model.check_cont_resolution() model.handle_el() # import pdb; pdb.set_trace() model.finalize() model.end_index() except IndexError: pass model.done_processing() return model
class Robot(object): """docstring for Robot""" # todo 把thread变成一个属性 每次start重新实例化一个thread def __init__(self, username, password, japan_server=False): super(Robot, self).__init__() parser = argparse.ArgumentParser("config") parser.add_argument("--debug", help="enable debug model", action="store_true") args = parser.parse_args() self.DEBUG = args.debug self.set_logger(username, japan_server) self.ze = zemulator.ZjsnEmulator() self.ze.username = username self.ze.password = password if japan_server: self.ze.api.location = self.ze.api.JAPAN self.ze.login() self.thread = None self.dock = Dock(self.ze) self.explore = self.dock.explore_mod self.campaign = Campaign(self.ze) states = [self.dock] + [m.state for m in [self.explore, self.campaign]] self.missions = {} self.machine = Machine(model=self, states=states, initial='init', auto_transitions=False) self.command = 'run' self.add_mission(DailyTask(self.ze)) # kill_fish self.kill_fish = Mission_6_1_A(self.ze) self.daily_kill_fish = DailyKillFish(self.ze) self.build_mission = HolyBuild(self.ze) self.add_mission(self.kill_fish) if self.ze.version < self.ze.KEY_VERSION: self.set_missions() self.machine.add_transition(trigger='go_out', prepare=[self.explore._prepare], source='init', dest=self.explore.mission_name) self.machine.add_transition(trigger='go_out', source=self.explore.mission_name, dest=self.explore.mission_name, conditions=[self.campaign.prepare], after=[self.campaign.start]) self.machine.add_transition(trigger='go_out', source=self.explore.mission_name, dest='init', conditions=[self.explore.get_explore]) else: self.machine.add_transition(trigger='go_out', source="init", dest="init", conditions=[self.campaign.prepare], after=[self.campaign.start]) self.add_mission(self.build_mission) self.set_missions() self.add_mission(self.daily_kill_fish) self.machine.add_transition(trigger='go_out', conditions=[self.dock.wait], source='init', dest="init") # self.machine.add_transition(trigger='go_back', source='*', dest='init') def add_mission(self, mission: Mission): if mission.mission_name not in self.missions: self.missions[mission.mission_name] = mission else: raise ValueError("mission name {} is already used".format( mission.mission_name)) self.machine.add_states(mission.state) self.machine.add_transition(**mission.trigger) self.machine.add_transition(**mission.back_trigger) def set_missions(self): pass def go_out(self): # dummy trigger method, implemented by transitions module pass def set_logger(self, username, is_japan): if is_japan: suffix = "_japan" else: suffix = "" log_formatter = logging.Formatter( '%(asctime)s: %(levelname)s: %(name)s: %(message)s') if os.name == 'nt' or self.DEBUG: stream_handler = logging.StreamHandler() stream_handler.setFormatter(log_formatter) logging.getLogger('transitions').addHandler(stream_handler) logging.getLogger('transitions').setLevel(logging.INFO) _logger.addHandler(stream_handler) file_handler = handlers.TimedRotatingFileHandler( '{}.log'.format(username + suffix), when='midnight', backupCount=3, encoding='utf8') file_handler.setFormatter(log_formatter) _logger.addHandler(file_handler) _logger.setLevel(logging.DEBUG) def working_loop(self): while self.command != 'stop': try: if not self.is_sleep(): self.go_out() time.sleep(2) else: time.sleep(600) except zemulator.ZjsnError as zerror: if zerror.eid == -101: self.ze.login() self.state = 'init' elif zerror.eid in [-9997, -9995, -9994]: _logger.info( "login on another device, input anything to continue") wait_thread = threading.Thread(target=input) wait_thread.start() wait_thread.join(timeout=7200) self.ze.login() self.state = 'init' else: raise zerror except ConnectionError: while self.command != 'stop': _logger.error("Connection error") time.sleep(600) try: self.ze.login() self.state = 'init' break except ConnectionError: pass def is_sleep(self) -> bool: # sleep in 0:00 to 6:00 if os.name == 'nt': return False if self.ze.now.replace( hour=0, minute=0) < self.ze.now < self.ze.now.replace(hour=6): return True else: return False def run(self): last_error_time = 0 error_count = 0 while 1: try: # check dock, equipment, tasks before any transitions self.dock.check() self.working_loop() except Exception as e: # reset error count everyday if last_error_time - time.time() > 24 * 60 * 60: error_count = 0 last_error_time = time.time() error_count += 1 _logger.exception(e) if self.DEBUG or error_count > 3: raise e else: self.ze.login() # disable mission where this error occurs if self.state in self.missions: current_mission = self.missions[self.state] current_mission.enable = False _logger.error("{} is disabled".format(current_mission)) # init state self.state = 'init' time.sleep(10) def start(self): if os.name == 'nt' or self.DEBUG: self.run() else: self.thread = threading.Thread(target=self.run, daemon=True) self.thread.start() return self.thread
def build_machine(infile, target, config, tagging_info=None, notify=None): videoprocessor = VideoProcessor(infile, target, config, tagging_info=tagging_info, notify=notify) machine = Machine(model=videoprocessor, initial='initialised') states = [ 'initialised', State(name='processed'), State(name='tagged'), State(name='postprocessed'), State(name='refreshed'), State(name='deployed'), State(name='deleted'), State(name='finished') ] machine.add_states(states) machine.add_transition(trigger='process', source='initialised', dest='processed', before='do_process') machine.add_transition(trigger='tag', source='processed', dest='tagged', conditions=['conversion_success', 'has_tag_info'], before='do_tag') machine.add_transition(trigger='postprocess', source=['tagged', 'processed'], dest='postprocessed', conditions=['conversion_success'], before='do_postprocess') machine.add_transition(trigger='deploy', source=['processed', 'tagged', 'postprocessed'], dest='deployed', conditions='conversion_success', before='do_deploy') machine.add_transition(trigger='delete', source=['postprocessed', 'deployed'], dest='deleted', conditions=['has_delete'], before='do_delete') machine.add_transition( trigger='refresh', source=['processed', 'tagged', 'deployed', 'deleted'], conditions=['has_refresher'], dest='refreshed', before='do_refresh') machine.add_transition(trigger='finish', source=[ 'processed', 'tagged', 'postprocessed', 'refreshed', 'deployed', 'deleted' ], dest='finished') return videoprocessor
states=states, transitions=transitions, initial='liquid') # States: 状态机的核心 # 初始化和修改状态的几种方式: # 通过字符串 # 直接使用 State 对象 # 使用一个字典 from transitions import State states = [ State(name='solid'), # 通过State创建对象 'liquid', # 通过字符串 { 'name': 'gas' }, # 通过字典 ] machine = Machine(lump, states) # 也可以通过 add_states 来添加状态 machine = Machine(lump) solid = State('solid') liquid = State('liquid') gas = State('gas') machine.add_states([solid, liquid, gas]) # 状态机的状态,应该只初始化一次,不能改动,除非重新创建 # Callbacks # 一个状态在进入和退出都可以调用回调, 可以在初始化添加或者之后添加 machine.add_transition()
class BotBuilder: def __init__(self, isVerbose): if isVerbose: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) self.statesCounter = 0 self.currentState = self.statesCounter self.beginState = self.currentState self.bbot = OurBot() self.machine = Machine(model=self.bbot, initial=self.convertStateIdToName( self.beginState), auto_transitions=False) self.machine.add_states(State(name=self.getCurrentState())) self.labelToStateMap = {} self.stateIdToContent = {} self.returnStack = [] logger.debug("Initialized BotBuilder with node:" + str(self.bbot.state)) def botSays(self, botString): setattr(self.bbot, "on_enter_" + self.getCurrentState(), types.MethodType(lambda x: print(botString), self.bbot)) self.machine.add_transition("advance", source=self.getCurrentState(), dest=self.convertStateIdToName( self.getNextStateId())) logger.debug("Tagging state " + self.getCurrentState() + " as bot says state") self.advanceState(botString) logger.debug("Creating fallthrough state " + self.getCurrentState()) def singleOptionUserResponse(self, response): self.machine.add_transition(response, source=self.getCurrentState(), dest=self.convertStateIdToName( self.getNextStateId())) self.advanceState(response) def twoOptionUserResponse(self, ifTransitionName, ifTransitionFunction, elseTransitionName, elseTransitionFunction): currentState = self.getCurrentState() ifState = self.createNewState(ifTransitionName) elseState = self.createNewState(elseTransitionName) mergeState = self.createNewState("merge-block") logger.debug("Added if state " + self.convertStateIdToName(ifState)) self.setCurrentState(ifState) self.pushReturnStack() ifTransitionFunction() self.machine.add_transition( ifTransitionName, source=self.convertStateIdToName(currentState), dest=self.convertStateIdToName(ifState)) if self.popReturnStack(): self.machine.add_transition( "advance", source=self.getCurrentState(), dest=self.convertStateIdToName(mergeState)) logger.debug("Added else state " + self.convertStateIdToName(elseState)) self.setCurrentState(elseState) self.pushReturnStack() elseTransitionFunction() self.machine.add_transition( elseTransitionName, source=self.convertStateIdToName(currentState), dest=self.convertStateIdToName(elseState)) if self.popReturnStack(): self.machine.add_transition( "advance", source=self.getCurrentState(), dest=self.convertStateIdToName(mergeState)) logger.debug("Added merge state " + self.convertStateIdToName(mergeState)) self.setCurrentState(mergeState) def gotoNode(self, nodeLabel): if len(self.returnStack) > 0: self.returnStack[-1] = False if not nodeLabel in self.labelToStateMap: self.labelToStateMap[nodeLabel] = self.createNewState(nodeLabel) logger.debug("Creating branch to node " + self.getCurrentState()) self.machine.add_transition("advance", source=self.getCurrentState(), dest=self.convertStateIdToName( self.labelToStateMap[nodeLabel])) self.setCurrentState(self.labelToStateMap[nodeLabel]) def getCurrentState(self): return self.convertStateIdToName(self.getCurrentStateId()) def getCurrentStateId(self): return self.currentState def convertStateIdToName(self, identity): return str(identity) def createNewState(self, textStr): self.statesCounter = self.getNextStateId() logger.debug("Created state " + self.convertStateIdToName(self.statesCounter)) self.machine.add_states( State(name=self.convertStateIdToName(self.statesCounter))) self.stateIdToContent[self.convertStateIdToName( self.statesCounter)] = textStr return self.statesCounter def getNextStateId(self): return self.statesCounter + 1 def advanceState(self, textStr): self.setCurrentState(self.createNewState(textStr)) def setCurrentState(self, state): self.currentState = state def plotStateMachine(self, name): self.bbot.get_graph().draw(name, prog='dot') def getBeginState(self): return self.convertStateIdToName(self.beginState) def getMachine(self): return self.machine def getModel(self): return self.bbot def pushReturnStack(self): self.returnStack.append(True) def popReturnStack(self): value = self.returnStack[-1] self.returnStack.pop() return value def getStateContent(self, stateId): return self.stateIdToContent.get(self.convertStateIdToName(stateId))