def __init__(self): states=['solid', 'liquid', 'gas', 'plasma'] Machine.__init__(self, states=states, initial='solid') self.add_transition('melt', 'solid', 'liquid') self.add_transition('evaporate', 'liquid', 'gas') self.add_transition('sublimate', 'solid', 'gas') self.add_transition('ionize', 'gas', 'plasma')
def __init__(self, **machine_configs): self.machine = GraphMachine( model = self, **machine_configs ) self.ref = db.reference()
def __init__(self, name): self.name = name self.states = ['start'] self.machine = GraphMachine(model=self, initial='start', title=name, show_conditions=True, show_state_attributes=True)
class IR_Model(object): def __init__(self, name): self.name = name self.states = ['start'] self.machine = GraphMachine(model=self, initial='start', title=name, show_conditions=True, show_state_attributes=True) def get_condition_list(self, self_transitions): condition_list = [] existing_conditions = self_transitions[0].conditions for condition in existing_conditions: condition_list.append(condition.func) # get the string of the condition, not the condition obj return condition_list def update_condition_list(self, self_transitions, new_condition_list): condition_list = self.get_condition_list(self_transitions) for new_condition in new_condition_list: if new_condition not in condition_list: condition_list.append(new_condition) return condition_list def add_new_transition(self, trigger, source, dest): existing_transitions = self.machine.get_transitions(trigger=trigger, source=source, dest=dest) if len(existing_transitions) == 0: self.machine.add_transition(trigger=trigger, source=source, dest=dest) self.states.append(source) self.states.append(dest) def add_self_transition(self, state, new_condition_list): trigger_list = self.machine.get_triggers(state) for trigger in trigger_list: if trigger == 'self': # self loop exists in the usage model self_transitions = self.machine.get_transitions('self', state, state) condition_list = self.update_condition_list(self_transitions, new_condition_list) self.machine.remove_transition('self', state, state) self.machine.add_transition(trigger='self', source=state, dest=state, conditions=condition_list) self.states.append(state) return # when self loop doesn't exist or the state doesn't exist self.machine.add_transition(trigger='self', source=state, dest=state, conditions=new_condition_list) self.states.append(state)
class DengueBotMachine(metaclass=Signleton): states = list() dengue_transitions = list() reply_msgs = dict() LOCATION_SEND_TUTOIRAL_MSG = [ ImageSendMessage(original_content_url=loc_step1_origin_img_url, preview_image_url=loc_step1_preview_img_url), ImageSendMessage(original_content_url=loc_step2_origin_img_url, preview_image_url=loc_step2_preview_img_url), ] def __init__(self, line_bot_api, initial_state='user', *, root_path): self.config_path_base = root_path if root_path else '' self.load_config() self.machine = GraphMachine( model=self, states=DengueBotMachine.states, transitions=DengueBotMachine.dengue_transitions, initial=initial_state, auto_transitions=False, show_conditions=True) self.line_bot_api = line_bot_api def reply_message_with_logging(self, reply_token, receiver_id, messages): def save_message(msg): try: content = msg.text except AttributeError: content = None bot_reply_log = BotReplyLog( receiver=LineUser.objects.get(user_id=receiver_id), speak_time=datetime.now(), message_type=msg.type, content=content) bot_reply_log.save() self.line_bot_api.reply_message(reply_token, messages) if not isinstance(messages, (list, tuple)): messages = [messages] for m in messages: save_message(m) def draw_graph(self, filename, prog='dot'): self.graph.draw(filename, prog=prog) def load_config(self, filename='FSM.json'): path = os.path.join(self.config_path_base, filename) with open(path) as FSM_file: data = ujson.loads(jsmin(FSM_file.read())) DengueBotMachine.states = data['states'] DengueBotMachine.dengue_transitions = data['transitions'] self._load_msg() def _load_msg(self, filename='dengue_msg.json'): path = os.path.join(self.config_path_base, filename) with open(path) as msg_file: msgs = ujson.loads(jsmin(msg_file.read())) DengueBotMachine.reply_msgs = msgs['reply_msgs'] def set_state(self, state): self.machine.set_state(state) def reset_state(self): self.set_state('user') @log_fsm_condition def is_pass(self, event): return True @log_fsm_condition def is_failed(self, event): return False def _is_this_message_event(self, event, event_type): return isinstance(event, MessageEvent) and isinstance( event.message, event_type) @log_fsm_condition def is_text_message(self, event): return self._is_this_message_event(event, TextMessage) @log_fsm_condition def is_sticker_message(self, event): return self._is_this_message_event(event, StickerMessage) @log_fsm_condition def is_image_message(self, event): return self._is_this_message_event(event, ImageMessage) @log_fsm_condition def is_video_message(self, event): return self._is_this_message_event(event, VideoMessage) @log_fsm_condition def is_audio_message(self, event): return self._is_this_message_event(event, AudioMessage) @log_fsm_condition def is_location_message(self, event): return self._is_this_message_event(event, LocationMessage) @log_fsm_condition def is_follow_event(self, event): return isinstance(event, FollowEvent) @log_fsm_condition def is_unfollow_event(self, event): return isinstance(event, UnfollowEvent) @log_fsm_condition def is_join_event(self, event): return isinstance(event, JoinEvent) @log_fsm_condition def is_leave_event(self, event): return isinstance(event, LeaveEvent) @log_fsm_condition def is_postback_event(self, event): return isinstance(event, PostbackEvent) @log_fsm_condition def is_beacon_event(self, event): return isinstance(event, BeaconEvent) @log_fsm_condition def is_asking_prevention(self, event): msg = event.message.text if (msg.strip() == "如何防範" or (any(m in msg for m in ["怎樣", "怎麼", "如何"]) and ("防疫" in msg)) or any(m in msg for m in ["防範", "預防"])): return True return False @log_fsm_condition def is_asking_self_prevention(self, event): text = '' if self.is_postback_event(event): text = event.postback.data elif self.is_text_message(event): text = event.message.text return '自身' in text @log_fsm_condition def is_asking_env_prevention(self, event): text = '' if self.is_postback_event(event): text = event.postback.data elif self.is_text_message(event): text = event.message.text return '環境' in text @log_fsm_condition def is_asking_dengue_fever(self, event): msg = event.message.text if any(m in msg for m in ["什麼是登革熱", "登革熱是什麼"]) or msg.strip() == "登革熱": return True return False @log_fsm_condition def is_asking_who_we_are(self, event): msg = event.message.text if msg.strip() in ["你是誰", "掌蚊人是誰", "誰是掌蚊人", "掌蚊人"]: return True return False @log_fsm_condition def is_asking_breeding_source(self, event): msg = event.message.text if (all(m in msg for m in ["登革熱", "孳生源"]) or (all(m in msg for m in ["孳生源", "是"]) and any(m in msg for m in ["什麼", "啥"]))): return True return False @log_fsm_condition def is_greeting(self, event): msg = event.message.text if any(m in msg.strip().lower() for m in ["哈囉", "你好", "嗨", "安安", "hello", "hi"]): return True return False @log_fsm_condition def is_asking_hospital(self, event): msg = event.message.text if (msg.strip() in ["快篩檢驗", "快篩"] or (any(m in msg for m in ["最近", "附近", "去哪", "去那", "哪裡", "那裡", "在哪", "在那"]) and any(m in msg for m in ["快篩", "篩檢", "檢查", "檢驗", "診所", "醫院"])) or all(m in msg for m in ["快篩", "診所"])): return True return False @log_fsm_condition def is_valid_address(self, event): coder = GoogleV3() address = event.message.text geocode = coder.geocode(address) return geocode is not None @log_fsm_condition def is_asking_symptom(self, event): msg = event.message.text if (msg.strip() in ["症狀", "登革熱症狀"] or all(m in msg for m in ["登革熱", "症狀"]) or all(m in msg for m in ["症狀", "是"])): return True return False @log_fsm_condition def is_asking_realtime_epidemic(self, event): msg = event.message.text if (msg.strip() in ["即時疫情", "疫情"] or (any(m in msg for m in ["最近", "本週", "這週"]) and ("疫情" in msg)) or ("疫情資訊" in msg)): return True return False @log_fsm_condition def is_giving_suggestion(self, event): return '建議' in event.message.text @log_fsm_condition def is_asking_usage(self, event): msg = event.message.text if (msg.strip() in ["聊什麼", "翻譯蒟蒻"] or any(m in msg for m in ["使用說明", "功能"]) or (any(m in msg for m in ["可以", "能", "會"]) and any(m in msg for m in ["做啥", "幹麻", "幹嘛", "幹啥", "什麼", "幹什麼", "做什麼"])) or all(m in msg for m in ["有", "功能"]) or ("怎麼" in msg and any(m in msg for m in ["使用", "用"]))): return True return False @log_fsm_condition def is_selecting_ask_dengue_fever(self, event): return '1' == event.message.text or self.is_asking_dengue_fever(event) @log_fsm_condition def is_selecting_ask_symptom(self, event): return '2' == event.message.text or self.is_asking_symptom(event) @log_fsm_condition def is_selecting_ask_prevention(self, event): return '3' == event.message.text or self.is_asking_prevention(event) @log_fsm_condition def is_selecting_ask_hospital(self, event): return '4' == event.message.text or self.is_asking_hospital(event) @log_fsm_condition def is_selecting_ask_realtime_epidemic(self, event): return '5' == event.message.text or self.is_asking_realtime_epidemic( event) @log_fsm_condition def is_selecting_give_suggestion(self, event): return '6' == event.message.text or self.is_giving_suggestion(event) @log_fsm_condition def is_hospital_address(self, event): return 'hosptial_address' in parse_qs(event.postback.data) @log_fsm_operation def is_gov_report(self, event): return '#2016' in event.message.text @log_fsm_operation def on_enter_user_join(self, event): # TODO: implement update user data when user rejoin user_id = event.source.user_id try: LineUser.objects.get(user_id=user_id) except LineUser.DoesNotExist: profile = self.line_bot_api.get_profile(user_id) user = LineUser(user_id=profile.user_id, name=profile.display_name, picture_url=profile.picture_url or '', status_message=profile.status_message or '') user.save() self.finish() def _send_text_in_rule(self, event, key): self.reply_message_with_logging( event.reply_token, event.source.user_id, TextSendMessage(text=DengueBotMachine.reply_msgs[key])) @log_fsm_operation def on_enter_ask_prevention(self, event): self.reply_message_with_logging( event.reply_token, event.source.user_id, TemplateSendMessage( alt_text=DengueBotMachine.reply_msgs['ask_prevent_type'], template=ButtonsTemplate(text='請問是自身防疫還是環境防疫呢?', actions=[ PostbackTemplateAction(label='自身', data='自身'), PostbackTemplateAction(label='環境', data='環境'), ]))) @log_fsm_operation def on_enter_ask_self_prevention(self, event): self._send_text_in_rule(event, 'self_prevent') self.finish_ans() @log_fsm_operation def on_enter_ask_env_prevention(self, event): self._send_text_in_rule(event, 'env_prevent') self.finish_ans() @log_fsm_operation def on_enter_ask_dengue_fever(self, event): self._send_text_in_rule(event, 'dengue_fever_intro') self.finish_ans() @log_fsm_operation def on_enter_ask_hospital(self, event): messages = [ TextSendMessage(text=DengueBotMachine.reply_msgs['ask_address']) ] messages.extend(self.LOCATION_SEND_TUTOIRAL_MSG) self.reply_message_with_logging(event.reply_token, event.source.user_id, messages=messages) self.advance() @log_fsm_operation def on_enter_receive_user_location(self, event): hospital_list = hospital.views.get_nearby_hospital( event.message.longitude, event.message.latitude) self._send_hospital_msgs(hospital_list, event) self.finish_ans() @log_fsm_condition def on_enter_receive_user_address(self, event): coder = GoogleV3() address = event.message.text geocode = coder.geocode(address) hospital_list = hospital.views.get_nearby_hospital( geocode.longitude, geocode.latitude) self._send_hospital_msgs(hospital_list, event) self.finish_ans() def _send_hospital_msgs(self, hospital_list, event): if hospital_list: msgs = self._create_hospitals_msgs(hospital_list) else: msgs = [TextSendMessage(text="抱歉,你附近都沒有快篩診所\n")] msgs.append( TextSendMessage(text=( "想要查看地區所有快篩點,請點下面連結\n" "(如果手機不能瀏覽,可用電腦查看,或將連結貼到 chrome 瀏覽器)\n\n" "https://www.taiwanstat.com/realtime/dengue-vis-with-hospital/" ))) self.reply_message_with_logging(event.reply_token, event.source.user_id, msgs) def _create_hospitals_msgs(self, hospital_list): text = "您好,\n最近的三間快篩診所是:" carousel_messages = list() for index, hospital in enumerate(hospital_list, 1): name = hospital.get('name') address = hospital.get('address') phone = hospital.get('phone') text += "\n\n{index}.{name}\n{address}\n{phone}".format( index=index, name=name, address=address, phone=phone) carousel_messages.append( CarouselColumn(text=name, actions=[ PostbackTemplateAction( label=address[:20], text=' ', data='hosptial_address=' + address, ), MessageTemplateAction(label=phone[:20], text=' '), ])) carousel_messages.append( CarouselColumn( text='想要查看地區所有快篩點\n請點下面連結', actions=[ MessageTemplateAction(label=' ', text=' '), URITemplateAction( label='鄰近快篩診所', uri= 'https://www.taiwanstat.com/realtime/dengue-vis-with-hospital/', ) ])) template_message = TemplateSendMessage( alt_text=text, template=CarouselTemplate(columns=carousel_messages)) hospital_messages = [template_message] return hospital_messages @log_fsm_operation def on_enter_ask_symptom(self, event): self.reply_message_with_logging( event.reply_token, event.source.user_id, messages=[ ImageSendMessage(original_content_url=symptom_origin_img_url, preview_image_url=symptom_preview_img_url), TextSendMessage( text=DengueBotMachine.reply_msgs['symptom_warning']) ]) self.finish_ans() @log_fsm_operation def on_enter_ask_realtime_epidemic(self, event): self._send_text_in_rule(event, 'new_condition') self.finish_ans() @log_fsm_operation def on_enter_greet(self, event): self._send_text_in_rule(event, 'greeting') self.finish_ans() @log_fsm_operation def on_enter_ask_breeding_source(self, event): self._send_text_in_rule(event, 'breeding_source') @log_fsm_operation def on_enter_ask_who_we_are(self, event): self._send_text_in_rule(event, 'who_we_are') self.finish_ans() @log_fsm_operation def on_enter_wait_user_suggestion(self, event): self._send_text_in_rule(event, 'ask_advice') @log_fsm_operation def on_exit_wait_user_suggestion(self, event): self._send_text_in_rule(event, 'thank_advice') advice = Advice(content=event.message.text, user_id=event.source.user_id) advice.save() @log_fsm_operation def on_enter_ask_usage(self, event): self._send_text_in_rule(event, 'manual') @log_fsm_operation def on_enter_gov_faculty_report(self, event): text = event.message.text _, _, action, note = text.split('#') gov_report = GovReport( user_id=LineUser.objects.get(user_id=event.source.user_id), action=action, note=note, report_time=datetime.fromtimestamp(event.timestamp / 1000), ) gov_report.save() self.advance(event) @log_fsm_operation def on_enter_wait_gov_location(self, event): messages = [ TextSendMessage( text=DengueBotMachine.reply_msgs['ask_gov_address']) ] messages.extend(self.LOCATION_SEND_TUTOIRAL_MSG) self.reply_message_with_logging(event.reply_token, event.source.user_id, messages=messages) @log_fsm_operation def on_enter_receive_gov_location(self, event): try: gov_report = GovReport.objects.filter( user_id=event.source.user_id, ).order_by('-report_time')[0] except GovReport.DoesNotExist: logger.error('Gov Report Does Not Exist') else: gov_report.lat = event.message.latitude gov_report.lng = event.message.longitude gov_report.save() self.reply_message_with_logging( event.reply_token, event.source.user_id, messages=TextSendMessage( text=DengueBotMachine.reply_msgs['thank_gov_report'])) self.finish_ans() @log_fsm_operation def on_enter_unrecognized_msg(self, event): if getattr(event, 'reply_token', None): msg_log = MessageLog.objects.get(speaker=event.source.user_id, speak_time=datetime.fromtimestamp( event.timestamp / 1000), content=event.message.text) unrecognized_msg = UnrecognizedMsg(message_log=msg_log) unrecognized_msg.save() try: response_to_unrecog_msg = ResponseToUnrecogMsg.objects.get( unrecognized_msg_content=unrecognized_msg.message_log. content) except ResponseToUnrecogMsg.DoesNotExist: self._send_text_in_rule(event, 'unknown_msg') else: response_content = response_to_unrecog_msg.content self.line_bot_api.reply_message( event.reply_token, TextSendMessage(text=response_content)) self.handle_unrecognized_msg(event) @log_fsm_operation def on_enter_ask_hospital_map(self, event): address = parse_qs(event.postback.data)['hosptial_address'][0] hosp = hospital.models.Hospital.objects.using('tainan').get( address=address) self.line_bot_api.reply_message( event.reply_token, messages=LocationSendMessage( title="地圖 - {name}".format(name=hosp.name), address=hosp.address, latitude=hosp.lat, longitude=hosp.lng)) self.finish_ans()
class TocMachine(GraphMachine): def __init__(self, **machine_configs): self.machine = GraphMachine(model=self, **machine_configs) self.user = User() self.bus_info = BusInfo() self.train_info = TrainInfo() self.hsr_info = HsrInfo() self.prev = None def reset(self): self.machine.set_state(self.machine.initial) self.on_enter_user() def on_enter_user(self, event=None): if self.user.user_id != None: push_text_message(self.user.user_id, '歡迎使用時刻表查詢系統,輸入任意文字開始') def is_going_to_start(self, event=None): self.user.user_id = event.source.user_id return True def on_enter_start(self, event=None): contents = get_replay_flex('start') send_flex_message(event.reply_token, contents) def is_going_to_bus_start(self, event=None): if event.message.type == 'text' and event.message.text == '公車查詢': return True else: if not (event.message.type == 'text' and (event.message.text == 'reset' or event.message.text == 'bus' or event.message.text == '火車查詢' or event.message.text == '高鐵查詢')): push_text_message(self.user.user_id, '無法辨識...') return False def on_enter_bus_start(self, event=None): self.user.bus_stops = None self.user.bus_route = None self.user.bus_direction = None contents = get_replay_flex('bus') send_flex_message(event.reply_token, contents) def going_bus_by_stop(self, event=None): if event.message.type != 'text': return False if event.message.text == '以站牌搜尋': return True else: # if event.message.type != 'location' and not (event.message.type == 'text' and (event.message.text == 'reset' or event.message.text == '以路線搜尋')): if not (event.message.text == 'reset' or event.message.text == '以路線搜尋'): push_text_message(self.user.user_id, '請點選上方的選項\n如要重來') return False def on_enter_bus_by_stop(self, event=None): send_text_message(event.reply_token, "請輸入站牌名稱") def going_bus_by_stop2(self, event=None): if event.message.type == 'text': if event.message.text not in self.bus_info.stops_to_route: push_text_message( self.user.user_id, f'找不到 {event.message.text} 站牌,目前只支援台南地區的公車站') return False else: self.user.bus_stops = event.message.text return True else: push_text_message(self.user.user_id, '請輸入公車站名字') return False def on_enter_bus_by_stop2(self, event=None): contents = get_replay_flex('bus_route_by_stops') # input routes_list = natsorted( self.bus_info.stops_to_route[self.user.bus_stops]) contents['body']['contents'][0]['text'] = self.user.bus_stops for route in routes_list: template = copy.deepcopy(button_template) template['action']['label'] = template['action']['text'] = route contents['footer']['contents'].append(template) send_flex_message(event.reply_token, contents) def goint_bus_by_route(self, event=None): return event.message.type == 'text' and event.message.text == '以路線搜尋' def on_enter_bus_by_route(self, event=None): send_text_message(event.reply_token, "請輸入公車號碼") def goint_bus_by_route2(self, event=None): # get shop from number if event.message.type == 'text': if event.message.text not in self.bus_info.route_to_stops: push_text_message( self.user.user_id, f'找不到公車路線為 {event.message.text} 的公車,目前只支援台南地區的公車') return False else: self.user.bus_route = event.message.text return True else: push_text_message(self.user.user_id, '請輸入公車路線') return False def on_enter_bus_by_route2(self, event=None): contents = get_replay_flex('bus_stops_by_route') # input stops_list = self.bus_info.route_to_stops[self.user.bus_route] template = copy.deepcopy(bubble_template) template['body']['contents'][0]['text'] = self.user.bus_route for i, stop in enumerate(stops_list): if i >= 72: # line can not sent message which exceed 12 bubble break if i % 6 == 0: contents['contents'].append(copy.deepcopy(template)) dict_template = copy.deepcopy(button_template) dict_template['action']['label'] = dict_template['action'][ 'text'] = stop contents['contents'][-1]['footer']['contents'].append( dict_template) send_flex_message(event.reply_token, contents) # need google map api # def goint_bus_by_location(self, event = None): # # get shop from location # if event.message.type == 'location': # return True # else: # return False # def on_enter_bus_by_location(self, event = None): # contents = get_replay_flex('stops_by_location') # 附近的公車站 # push_flex_message(self.user.user_id, contents) def going_bus_direction(self, event=None): # print result if self.user.bus_route == None: if event.message.type == 'text': if event.message.text in self.bus_info.route_to_stops: self.user.bus_route = event.message.text contents = get_replay_flex('bus_direction') push_flex_message(self.user.user_id, contents) return True else: push_text_message(self.user.user_id, '請點選上方的路線') return False else: push_text_message(self.user.user_id, '請點選上方的路線') return False else: if event.message.type == 'text': if event.message.text in self.bus_info.stops_to_route: self.user.bus_stops = event.message.text contents = get_replay_flex('bus_direction') push_flex_message(self.user.user_id, contents) return True else: push_text_message(self.user.user_id, '請點選上方的站牌名稱') return False else: push_text_message(self.user.user_id, '請點選上方的站牌名稱') return False def is_going_to_bus_end(self, event=None): if event.message.type == 'text': if event.message.text in ['去程', '回程']: self.user.bus_direction = event.message.text estimates = self.bus_info.calculate(self.user.bus_route, self.user.bus_stops, self.user.bus_direction) contents = get_replay_flex('show_bus_time') contents['header']['contents'][0]['text'] = self.user.bus_route contents['header']['contents'][1][ 'text'] = self.user.bus_direction i = 1 # contents['body']['contents'][0]['contents'][1]['contents'][1]['borderColor'] = '#00FF00' # contents['body']['contents'][0]['contents'][1]['contents'][1]['backgroundColor'] = '#00FF00' real_time = 0 for key, value in estimates.items(): if i == 3: real_time = value[0] contents['body']['contents'][i]['contents'][2][ 'text'] = key if value[1] == 0: # 末班車已過 or 交管 contents['body']['contents'][i]['contents'][0][ 'text'] = f'{(timedelta(seconds = value[0])).seconds // 60} 分' if value[ 0] != None else '不停靠' elif value[1] == 1: contents['body']['contents'][i]['contents'][0][ 'text'] = f'{(timedelta(seconds = value[0])).seconds // 60} 分' if value[ 0] != None else '未發車' elif value[1] == 2: contents['body']['contents'][i]['contents'][0][ 'text'] = '交管不停靠' elif value[1] == 3: contents['body']['contents'][i]['contents'][0][ 'text'] = '末班車已過' elif value[1] == 4: contents['body']['contents'][i]['contents'][0][ 'text'] = '今日未營運' else: # 末班車已過 or 交管 contents['body']['contents'][i]['contents'][0][ 'text'] = '不停靠' i += 2 contents['header']['contents'][1][ 'text'] = self.user.bus_direction contents['body']['contents'][0][ 'text'] = f'預計到達時間: {int(real_time / 60)} 分鐘' if real_time != None else ' ' send_flex_message(event.reply_token, contents) return True else: push_text_message(self.user.user_id, '請輸入正確的方向') return False else: push_text_message(self.user.user_id, '請輸入正確的方向') return False def on_enter_bus_end(self, event=None): self.machine.set_state(self.machine.initial) def is_going_to_train(self, event=None): return event.message.type == 'text' and event.message.text == '火車查詢' def on_enter_train_start(self, event=None): self.train_info.start_station = None self.train_info.end_station = None contents = get_replay_flex('train_start') send_flex_message(event.reply_token, contents) def going_train_start_station(self, event=None): if event.message.type == 'text' and event.message.text in self.train_info.available_station: self.user.train_start_station = event.message.text return True else: push_text_message(self.user.user_id, '請選擇上方的車站') return False def on_enter_train_start_station(self, event=None): contents = get_replay_flex('train_end') send_flex_message(event.reply_token, contents) def going_train_end_station(self, event=None): if event.message.type == 'text' and event.message.text in self.train_info.available_station: if event.message.text == self.user.train_start_station: push_text_message(self.user.user_id, "起訖站不可相同") return False else: self.user.train_end_station = event.message.text return True else: push_text_message(self.user.user_id, '請選擇上方的車站') return False def on_enter_train_end_station(self, event=None): contents = get_replay_flex('train_date') contents['header']['contents'][0]['contents'][1][ 'text'] = self.user.train_start_station contents['header']['contents'][1]['contents'][1][ 'text'] = self.user.train_end_station send_flex_message(event.reply_token, contents) def going_train_time(self, event=None): if event.type == 'postback': self.user.train_date = event.postback.params['datetime'] times = self.train_info.calculate(self.user.train_start_station, self.user.train_end_station, self.user.train_date) contents = get_replay_flex('show_train_time') for t in times: copy_template = copy.deepcopy(train_time_template) copy_template['header']['contents'][0]['contents'][0][ 'contents'][1]['text'] = self.user.train_start_station copy_template['header']['contents'][0]['contents'][1][ 'contents'][1]['text'] = self.user.train_end_station copy_template['header']['contents'][1]['contents'][0][ 'text'] = f'車號 {t[2]}' # too long copy_template['header']['contents'][1]['contents'][1][ 'text'] = f'{t[3][:2]}' delay = self.train_info.delay(t[2]) if delay != 0: copy_template['body']['contents'][0]['contents'][1][ 'contents'][1]['borderColor'] = '#EF4520' copy_template['body']['contents'][0]['contents'][1][ 'contents'][1]['backgroundColor'] = '#EF4520' copy_template['body']['contents'][0]['contents'][2][ 'text'] = f'誤點 {int(delay / 60)} 分鐘' else: copy_template['body']['contents'][0]['contents'][2][ 'text'] = f'正常' td = datetime.strptime(t[1], "%H:%M") - \ datetime.strptime(t[0], "%H:%M") copy_template['body']['contents'][2][ 'text'] = f'Total: {td.seconds // 3600} hours {(td.seconds // 60) % 60} minutes' copy_template['body']['contents'][3]['contents'][0][ 'text'] = t[0] copy_template['body']['contents'][3]['contents'][2][ 'text'] = self.user.train_start_station copy_template['body']['contents'][5]['contents'][0][ 'text'] = t[1] copy_template['body']['contents'][5]['contents'][2][ 'text'] = self.user.train_end_station contents['contents'].append(copy_template) if len(times) == 0: send_text_message(event.reply_token, f"{self.user.train_date} 之後沒有可以搭乘的列車") else: send_flex_message(event.reply_token, contents) return True else: push_text_message(self.user.user_id, '請輸入正確的日期') return False def on_enter_train_time(self, event=None): self.machine.set_state(self.machine.initial) def is_going_to_hsr(self, event=None): return event.message.type == 'text' and event.message.text == '高鐵查詢' def on_enter_hsr_start(self, event=None): self.train_info.start_station = None self.train_info.end_station = None contents = get_replay_flex('hsr_start') send_flex_message(event.reply_token, contents) def going_hsr_start_station(self, event=None): if event.message.type == 'text' and event.message.text in self.hsr_info.available_station: self.user.hsr_start_station = event.message.text return True else: push_text_message(self.user.user_id, '請選擇上方的車站') return False def on_enter_hsr_start_station(self, event=None): contents = get_replay_flex('hsr_end') send_flex_message(event.reply_token, contents) def going_hsr_end_station(self, event=None): if event.message.type == 'text' and event.message.text in self.hsr_info.available_station: if event.message.text == self.user.hsr_start_station: push_text_message(self.user.user_id, "起訖站不可相同") return False else: self.user.hsr_end_station = event.message.text return True else: push_text_message(self.user.user_id, '請選擇上方的車站') return False def on_enter_hsr_end_station(self, event=None): contents = get_replay_flex('train_date') contents['header']['contents'][0]['contents'][1][ 'text'] = self.user.hsr_start_station contents['header']['contents'][1]['contents'][1][ 'text'] = self.user.hsr_end_station send_flex_message(event.reply_token, contents) def going_hsr_time(self, event=None): if event.type == 'postback': self.user.hsr_date = event.postback.params['datetime'] times = self.hsr_info.calculate(self.user.hsr_start_station, self.user.hsr_end_station, self.user.hsr_date) contents = get_replay_flex('show_hsr_time') for t in times: copy_template = copy.deepcopy(rail_time_template) copy_template['header']['contents'][0]['contents'][0][ 'contents'][1]['text'] = self.user.hsr_start_station copy_template['header']['contents'][0]['contents'][1][ 'contents'][1]['text'] = self.user.hsr_end_station copy_template['header']['contents'][1]['contents'][0][ 'text'] = f'{t[2]}' available = self.hsr_info.available_seats( t[2], t[3], self.user.hsr_date, self.user.hsr_start_station) if not available: copy_template['body']['contents'][0]['contents'][1][ 'contents'][1]['borderColor'] = '#EF4520' copy_template['body']['contents'][0]['contents'][1][ 'contents'][1]['backgroundColor'] = '#EF4520' copy_template['body']['contents'][0]['contents'][2][ 'text'] = '沒有空位' td = datetime.strptime(t[1], "%H:%M") - \ datetime.strptime(t[0], "%H:%M") copy_template['body']['contents'][2][ 'text'] = f'Total: {td.seconds // 3600} hours {(td.seconds // 60) % 60} minutes' copy_template['body']['contents'][3]['contents'][0][ 'text'] = t[0] copy_template['body']['contents'][3]['contents'][2][ 'text'] = self.user.hsr_start_station copy_template['body']['contents'][5]['contents'][0][ 'text'] = t[1] copy_template['body']['contents'][5]['contents'][2][ 'text'] = self.user.hsr_end_station contents['contents'].append(copy_template) if len(times) == 0: send_text_message(event.reply_token, f'{self.user.hsr_date} 之後沒有可以搭乘的列車') else: send_flex_message(event.reply_token, contents) return True else: push_text_message(self.user.user_id, '請輸入正確的日期') return False def on_enter_hsr_time(self, event=None): self.machine.set_state(self.machine.initial)
class ungetauftes_wesen(object): states = ['idle', 'move', 'check', 'move_straight', 'move_to_side','move_forward', 'move_back', 'orientate', 'open_gripper', 'rotate_gripper_fixed','vision','rotate_gripper_free','close_gripper','check_grip'] def __init__(self, name): self.name = name self.havecommand = False self.command = " " self.angle_of_deviation = 0.0 self.forward = True self.gripA = True self.gripB = True self.orientation = [0,0,0,0,0] self.moves_left=5 # Initialize the state machine self.machine = Machine(model=self, states=ungetauftes_wesen.states, initial='idle') self.machine.add_transition(trigger='wake_up', source='idle', dest='move') self.machine.add_transition('move_straight', 'move', 'move_forward',conditions=['is_direction_forward']) self.machine.add_transition('move_straight', 'move', 'move_back',unless=['is_direction_forward']) self.machine.add_transition('orientate', 'move_forward', 'orientate') self.machine.add_transition('orientate', 'move_back', 'orientate') self.machine.add_transition('open_gripper', 'orientate', 'open_gripper') self.machine.add_transition('close_gripper', 'vision', 'close_gripper') self.machine.add_transition('rotate_gripper_fixed', 'open_gripper', 'rotate_gripper_fixed') self.machine.add_transition('vision', 'rotate_gripper_fixed', 'vision') self.machine.add_transition('check', 'idle', 'check') # Sweat is a disorder that can be remedied with water. # Unless you've had a particularly long day, in which case... bed time! self.machine.add_transition('move_finished', 'close_gripper', 'idle', conditions=['is_exhausted']) self.machine.add_transition('move_finished', 'close_gripper', 'move',) #self.machine.add_ordered_transitions(['move_forward', 'orientate']) ## CONDITIONS def is_exhausted(self): """ Basically a coin toss. """ return self.moves_left < 0.5 def is_direction_forward(self): return self.forward ## CONDITIONS def on_enter_move(self): print("entered state move") self.moves_left-=1 print str(self.moves_left)+(" moves left") self.move_straight() def on_enter_move_forward(self): print("entered state move forward flag set") self.forward = False self.orientate() def on_enter_move_back(self): print("entered state move back flag set") self.forward = True self.orientate() def on_enter_idle(self): print("entered state idle") def on_enter_orientate(self): print("entered state orientate") self.open_gripper() def on_enter_open_gripper(self): print("entered state open_gripper") senddata("testdata") print str(checkdata('fever')) self.rotate_gripper_fixed() def on_enter_rotate_gripper_fixed(self): print("entered state rotate_gripper_fixed") print("Wait for Signal:") self.havecommand = True self.command = "rotate" def on_enter_vision(self): print("entered state vision") self.angle_of_deviation = get_course() print "Our course is: " + str(self.angle_of_deviation) self.close_gripper() def on_enter_close_gripper(self): print("entered state close_gripper") print("Wait for Signal:") sleep(.5) print(".") sleep(.5) print(".") sleep(.5) print(".") self.move_finished()
class Gandalf(object): face_in_front = False wants_to_enter = None testing_mode = False allowed_people_dict = {} current_person = None delete_faces = True force_make_queue = False STATES = [ "start", "started", "initialized", "face_detected", "intention_recognized", "validate_card", "on_side" ] def __init__(self, robot, testing_mode=False): self.robot = robot self.testing_mode = testing_mode # Initialize the state machine self.state_machine = Machine(model=self, states=self.STATES, queued=True, initial="start") self.state_machine.add_transition( trigger="starting_up", source="start", dest="started", after=lambda *args, **kwargs: starting_up(self, *args, **kwargs)) self.state_machine.add_transition( trigger="initializing", source="started", dest="initialized", after=lambda *args, **kwargs: initializing(self, *args, **kwargs)) self.state_machine.add_transition( trigger="detecting_face", source="*", dest="face_detected", after=lambda *args, **kwargs: recognizing_face( self, *args, **kwargs)) self.state_machine.add_transition( trigger="question_intention", source="face_detected", dest="intention_recognized", after=lambda *args, **kwargs: intention_recognizing( self, *args, **kwargs)) self.state_machine.add_transition( trigger="validate_entry", source=["intention_recognized", "validate_card"], dest="validate_card", after=lambda *args, **kwargs: validation_card_reading( self, *args, **kwargs)) self.state_machine.add_transition( trigger="move_to_side", source="validate_card", dest="on_side", after=lambda *args, **kwargs: move_to_side(self, *args, **kwargs)) self.state_machine.add_transition( trigger="move_back", source="on_side", dest="initialized", after=lambda *args, **kwargs: move_back(self, *args, **kwargs)) self.state_machine.add_transition( trigger="re_initialize", source="*", dest="initialized", after=lambda *args, **kwargs: initializing(self, *args, **kwargs))
# Accepted in <num> { 'trigger': 'Input numbers', 'source': states[5], 'dest': states[5] }, { 'trigger': 'Input others', 'source': states[5], 'dest': states[0] }, # Accepted in <any> { 'trigger': 'Input', 'source': states[6], 'dest': states[6] } ] model = Model() machine = GraphMachine(model=model, states=states, transitions=transitions, initial=states[0], title='LoggerParser State Transition Diagram') graph = model.get_graph() graph.draw('logger_parser_state_transition_diagram.png', prog='dot')
def __init__(self, **machine_configs): self.machine = GraphMachine(model=self, **machine_configs) self.p = password() self.g = GuessA4B4()
class FsmGps(object): """ FSM states """ states = ['init', 'OFF', 'ON', 'ASSAULT'] """ Default timeout for GPS frame sending """ def __init__(self, name, mi_sim868): self.TIMEOUT_DEFAULT = 30 self.name = name """ Input shared variables """ self.flag_active = 0 """ Timeout GPS """ self.timeout_gps = 0 ''' GPS dictionary ''' self.gps_param = { 'GNSS_status': 0, 'UTC': '', # yyyyMMddhhmmss.sss 'Latitude': 0.0, # ±dd.dddddd [-90.000000,90.000000] 'Longitude': 0.0, # ±ddd.dddddd [-180.000000,180.000000] 'Altitude': 0.0, # in meters 'Speed': 0.0, # km/h [0,999.99] } """ Initialize the state machine """ self.machine = Machine(model=self, states=FsmGps.states, initial='init') self.sim868 = mi_sim868 """ Add transitions """ self.machine.add_transition(trigger='start', source='init', dest='OFF') self.machine.add_transition('fire', 'OFF', 'ON', conditions=['is_active']) self.machine.add_transition('fire', 'ON', 'OFF', conditions=['is_not_active']) self.machine.add_transition('fire', 'ON', 'ON', conditions=['timeout_and_active'], after='send_gps_frame_and_init_timer') """ Guard functions """ def is_active(self): return self.flag_active def is_not_active(self): return not self.flag_active def timeout_and_active(self): return ((int(dt.now().timestamp()) > self.timeout_gps) and self.flag_active) """ Output functions TODO: Modify GPS frame sending with GPS drivers """ def send_gps_frame_and_init_timer(self): gps_data = self.sim868.get_gps_data() self.gps_param['UTC'] = gps_data["UTC"] self.gps_param['Latitude'] = gps_data["Latitude"] self.gps_param['Longitude'] = gps_data["Longitude"] self.gps_param['Altitude'] = gps_data["Altitude"] self.gps_param['Speed'] = gps_data["Speed"] print( "'UTC': {}, 'Latitude': {}, 'Longitude': {}, 'Altitude': {}, 'Speed': {}" .format(self.gps_param["UTC"], self.gps_param["Latitude"], self.gps_param["Longitude"], self.gps_param["Altitude"], self.gps_param["Speed"])) self.timeout_gps = int(dt.now().timestamp()) + self.TIMEOUT_DEFAULT
def __init__( self, name='myobservatory', OTA='OTA', states_file='states.yaml', transitions_file='transitions.yaml', location_file='location.yaml', initial_state='sleeping', waittime=2, maxwait=10, max_allowed_errors=0, weather=None, weather_config={}, roof=None, roof_config={}, telescope=None, telescope_config={}, instrument=None, instrument_config={}, detector=None, detector_config=[{}], datadir='~', lat=0, lon=0, height=0, horizon=0, mongoIP='192.168.4.49', mongoport=32768, loglevel_console='INFO', logfile=None, loglevel_file='DEBUG', OBs=[], ): self.name = name self.datadir = Path(datadir).expanduser().absolute() self.logger = create_log(loglevel_console=loglevel_console, logfile=logfile, loglevel_file=loglevel_file) self.uname_result = os.uname() # Components self.weather = weather(logger=self.logger, **weather_config) self.roof = roof(logger=self.logger, **roof_config) self.telescope = telescope(logger=self.logger, **telescope_config) self.instrument = instrument(logger=self.logger, **instrument_config) self.detector = [ d(logger=self.logger, **detector_config[i]) for i, d in enumerate(detector) ] self.scheduler = Scheduler(OBs=OBs) # Load States File with open(Path(states_file).expanduser()) as FO: self.states = yaml.safe_load(FO) # Load Transitions File with open(Path(transitions_file).expanduser()) as FO: self.transitions = yaml.safe_load(FO) # Load Location self.location = c.EarthLocation(lat=lat, lon=lon, height=height) self.horizon = horizon # Instantiate State Machine try: self.machine = GraphMachine( model=self, states=self.states, transitions=self.transitions, initial=initial_state, queued=True, use_pygraphviz=True, show_conditions=True, ) # Generate state diagram # self.machine.get_graph().draw('state_diagram.png', prog='dot') except: self.machine = Machine( model=self, states=self.states, transitions=self.transitions, initial=initial_state, queued=True, ) # Operational Properties self.waittime = waittime self.maxwait = maxwait self.wait_duration = 0 self.max_allowed_errors = max_allowed_errors # Initialize Status Values self.startup_at = datetime.now() self.entered_state_at = datetime.now() self.last_state = str(self.state) self.executed = Table(names=('type', 'target', 'pattern', 'instconfig', 'detconfig', 'failed'), dtype=('a20', 'a40', 'a20', 'a40', 'a40', np.bool)) self.current_OB = None self.we_are_done = False self.durations = {} self.errors = [] self.error_count = 0 self.software_errors = [] # Configure telescope hardware self.log(f'Sending location info to mount') self.telescope.set_sitelatitude(lat) assert np.isclose(lat, self.telescope.sitelatitude()) self.telescope.set_sitelongitude(lon) assert np.isclose(lon, self.telescope.sitelongitude()) self.telescope.set_siteelevation(height) assert np.isclose(height, self.telescope.siteelevation()) self.log(f'Sending date and time to mount') now = datetime.utcnow() self.log(f'Computer time: {now.isoformat()}') self.telescope.set_utcdate(f"{now.isoformat()}Z") mountnow_str = self.telescope.utcdate() self.log(f'Mount time: {mountnow_str}') while len(mountnow_str) < 23: mountnow_str += '0' mountnow = datetime.fromisoformat(mountnow_str) dt = mountnow - now assert dt.total_seconds() < 0.25 # Mongo Connection self.mongoIP = mongoIP self.mongoport = mongoport try: self.client = pymongo.MongoClient(mongoIP, mongoport) self.db = self.client[name] self.status_collection = self.db['status'] self.log(f'Connected to Mongo DB') except: self.client = None self.db = None self.status_collection = None self.log(f'Failed to connect to Mongo DB', level=WARNING)
class Game(object): def return_true(self): return True def return_false_v1(self): return False def return_false_v2(self): return False life_bot = Game() machine = Machine(model=life_bot, states=states, transitions=transitions, initial="first") # method to do transition print(life_bot.state) life_bot.go_to_second() life_bot.go_to_third() print(life_bot.state) life_bot.to_first() print(life_bot.state) life_bot.trigger("go_to_second") print(life_bot.state)
def __init__(self, **machine_configs): self.machine = GraphMachine(model=self, **machine_configs) self.__Hand = NhHand() self.__Eyes = NhEyes() self.__Reply = NhReply()
def __init__(self, **machine_configs): self.machine = GraphMachine(model=self, **machine_configs)
def __init__(self, **machine_configs): self.machine = GraphMachine(ignore_invalid_triggers=True, model=self, **machine_configs)
def __init__(self): self.machine = GraphMachine( model = self, states=[ 'welcome', 'entry', 'outside', 'intro', 'slot', 'inside', 'service', 'google', 'youtube', 'screenshot' ], transitions=[ { 'trigger': 'forward', 'conditions': 'going_outside', 'source': 'entry', 'dest': 'outside' }, { 'trigger': 'forward', 'conditions': 'going_intro', 'source': 'outside', 'dest': 'intro' }, { 'trigger': 'forward', 'conditions': 'going_slot', 'source': 'intro', 'dest': 'slot' }, { 'trigger': 'forward', 'conditions': 'going_slot', 'source': 'slot', 'dest': 'slot' }, { 'trigger': 'forward', 'conditions': 'going_inside', 'source': 'entry', 'dest': 'inside' }, { 'trigger': 'wrong', 'source': 'inside', 'dest': 'outside' }, { 'trigger': 'forward', 'conditions': 'going_service', 'source': 'inside', 'dest': 'service' }, { 'trigger': 'forward', 'conditions': 'going_google', 'source': 'service', 'dest': 'google' }, { 'trigger': 'forward', 'conditions': 'google_repeat', 'source': 'google', 'dest': 'google' }, { 'trigger': 'forward', 'conditions': 'going_youtube', 'source': 'service', 'dest': 'youtube' }, { 'trigger': 'forward', 'conditions': 'youtube_repeat', 'source': 'youtube', 'dest': 'youtube' }, { 'trigger': 'forward', 'conditions': 'going_screenshot', 'source': 'service', 'dest': 'screenshot' }, { 'trigger': 'forward', 'conditions': 'screenshot_repeat', 'source': 'screenshot', 'dest': 'screenshot' }, { 'trigger': 'back', 'source': 'intro', 'dest': 'outside' }, { 'trigger': 'back', 'source': 'slot', 'dest': 'intro' }, { 'trigger': 'back', 'source': 'service', 'dest': 'inside' }, { 'trigger': 'back', 'source': ['google', 'youtube', 'screenshot'], 'dest': 'service' }, { 'trigger': 'restart', 'source': [ 'welcome', 'outside', 'intro', 'slot', 'inside', 'service', 'google', 'youtube', 'screenshot' ], 'dest': 'entry' } ], initial='welcome', auto_transitions=True, show_conditions=True )