def generateGraph(system, ltl, folder, only_action=False): ts = system.transition_system buchi_ts = Buchi.tsToGenBuchi(ts) buchi_ltl = Buchi.ltlToBuchi(ltl) (buchi_final, pairs) = Buchi.product(buchi_ts, buchi_ltl) filename_ts = folder + 'ts.gv' if folder.endswith( '/') else folder + '/ts.gv' filename_ltl = folder + 'ltl.gv' if folder.endswith( '/') else folder + '/ltl.gv' filename_final = folder + 'final.gv' if folder.endswith( '/') else folder + '/final.gv' png_ts = folder + 'ts.png' if folder.endswith('/') else folder + '/ts.png' png_ltl = folder + 'ltl.png' if folder.endswith( '/') else folder + '/ltl.png' png_final = folder + 'final.png' if folder.endswith( '/') else folder + '/final.png' group = [s2 for s1, s2 in pairs] try: os.stat(folder) except: os.mkdir(folder) buchi_ts.writeToGv(filename_ts, only_action=only_action) buchi_ltl.writeToGv(filename_ltl) buchi_final.writeToGv(filename_final, group, only_action=only_action) os.system('dot -Tpng -o %s %s' % (png_ts, filename_ts)) os.system('dot -Tpng -o %s %s' % (png_ltl, filename_ltl)) os.system('dot -Tpng -o %s %s' % (png_final, filename_final))
def _getBadEdges(system, ltl, record_exp_list=[]): ts = system.transition_system buchi_ts = Buchi.tsToGenBuchi(ts, record_exp_list) buchi_ltl = Buchi.ltlToBuchi(ltl) (buchi_final, pairs) = Buchi.product(buchi_ts, buchi_ltl) field_list = [state.field for state in ts.state_list] # Stage 1: generate all bad edges # 得到所有的坏边 bad_edges = list() other_edges = list() for edge in buchi_final.edge_list: if not buchi_final.getStateAcc(edge.src): try: src_index_ts = pairs[edge.src][0] dst_index_ts = pairs[edge.dst][0] if ts.num_state == src_index_ts: # this is the initial node, skip continue src_index_ltl = pairs[edge.src][1] dst_index_ltl = pairs[edge.dst][1] src_field = field_list[src_index_ts] dst_field = field_list[dst_index_ts] action = _getAction(ts, src_index_ts, dst_index_ts) ltl_req = _getLTLReq(buchi_ltl, src_index_ltl, dst_index_ltl) if buchi_final.getStateAcc(edge.dst): bad_edges.append( BadEdge(src_index_ts, dst_index_ts, src_index_ltl, dst_index_ltl, src_field, dst_field, action, ltl_req)) else: other_edges.append( BadEdge(src_index_ts, dst_index_ts, src_index_ltl, dst_index_ltl, src_field, dst_field, action, ltl_req)) except IndexError: # if this happen, this means that the system has already been in wrong state from the initial state raise Exception( 'The property is violated in the initial state, please try a different initial state' ) return bad_edges, other_edges
class S(IoTSystem): ac = AirConditioner() ac = S() ltl = '!G(ac.thermostatGreaterThan70 & ac.thermostatLessThan75)' result = generateFixForSafety(ac, ltl) print(result) for edge_result in result: print(edge_result[0][0].log()) field_list = [state.field for state in ac.transition_system.state_list] ts = ac.transition_system buchi_ts = Buchi.tsToGenBuchi(ts) buchi_ltl = Buchi.ltlToBuchi(ltl) (buchi_final, pairs) = Buchi.product(buchi_ts, buchi_ltl) def generateStatement(crit_value_list, event_list, template): """ return a set of statements that will cover all edges from a set of events for numeric variable in channel :param crit_value_list: list of numeric :param event_list: list of string :param template: string :return: """ crit_value_list = sorted(crit_value_list) prefix_crit = crit_value_list[:-1] suffix_crit = crit_value_list[1:] mid_crit = [(v1 + v2) / 2 for v1, v2 in zip(suffix_crit, prefix_crit)]
def _fixConditionGenerator(system, ltl, record_exp_list, include_orig=False): """ generate a set of bad edge conditions (trigger, condition, possible_actions_list) another return value is a set of all other edge conditions in safety or boundary [(trigger, condition)] .. :param system: :param ltl: :return: """ bad_edges, other_edges = _getBadEdges(system, ltl, record_exp_list) buchi_ltl = Buchi.ltlToBuchi(ltl) result = list() others = list() for bad_edge in bad_edges: ap_list = [ap for ap in system.getApList(bad_edge.src_field) if '@' not in ap and 'trigger' not in ap] ap_list = ap_list + ['!' + ap for ap in system.getAllAp() if '@' not in ap and 'trigger' not in ap and ap not in ap_list and ap not in system.tap_dict] condition = ' & '.join(ap_list) trigger = bad_edge.action action_list = list() for action, dst_field in system.getAction(src_field=bad_edge.dst_field, ext=False): label = system.getLabel(dst_field) ap_dict = dict() for value, ap in zip(label, system.transition_system.ap_list): ap_dict[ap] = value ap_dict = {**ap_dict, **{key: _recordSatisfy(action, key) for key in record_exp_list}} if not calculateBoolean(_getLTLReq(buchi_ltl, bad_edge.src_index_ltl, bad_edge.dst_index_ltl), ap_dict): action_list.append(action) result.append((trigger, condition, action_list)) if include_orig: for k, tap in system.tap_dict.items(): condition = ' & '.join(tap.condition) trigger = tap.trigger action_list = [tap.action] result.append((trigger, condition, action_list)) for edge in other_edges: # should add every edge that doesn't trigger any rule if not system.isTriggeredState(edge.dst_field): ap_list = [ap for ap in system.getApList(edge.src_field) if '@' not in ap and 'trigger' not in ap] ap_list = ap_list + ['!' + ap for ap in system.getAllAp() if '@' not in ap and 'trigger' not in ap and ap not in ap_list and ap not in system.tap_dict] condition = ' & '.join(ap_list) trigger = edge.action others.append((trigger, condition)) else: for edge in other_edges: ap_list = [ap for ap in system.getApList(edge.src_field) if '@' not in ap and 'trigger' not in ap] ap_list = ap_list + ['!' + ap for ap in system.getAllAp() if '@' not in ap and 'trigger' not in ap and ap not in ap_list and ap not in system.tap_dict] condition = ' & '.join(ap_list) trigger = edge.action others.append((trigger, condition)) return result, others
def generateFixForSafety(system, ltl): """ Given a ltl property for a transition system, generate new tap rules to fix them :param system: iot system :param ltl: the property to be fixed :return: a set of new tap rule? new system? list """ result = list() ts = system.transition_system buchi_ts = Buchi.tsToGenBuchi(ts) buchi_ltl = Buchi.ltlToBuchi(ltl) (buchi_final, pairs) = Buchi.product(buchi_ts, buchi_ltl) field_list = [state.field for state in ts.state_list] bad_edges = list() # generate all edges to be fixed for edge in buchi_final.edge_list: if buchi_final.getStateAcc(edge.dst) and not buchi_final.getStateAcc(edge.src): try: src_index_ts = pairs[edge.src][0] dst_index_ts = pairs[edge.dst][0] if ts.num_state == src_index_ts: # this is the initial node, skip continue src_index_ltl = pairs[edge.src][1] dst_index_ltl = pairs[edge.dst][1] src_field = field_list[src_index_ts] dst_field = field_list[dst_index_ts] action = _getAction(ts, src_index_ts, dst_index_ts) ltl_req = _getLTLReq(buchi_ltl, src_index_ltl, dst_index_ltl) bad_edges.append(BadEdge(src_index_ts, dst_index_ts, src_index_ltl, dst_index_ltl, src_field, dst_field, action, ltl_req)) except IndexError: # if this happen, this means that the system has already been in wrong state from the initial state raise Exception('The property is violated in the initial state, please try a different initial state') # find all states in safe cluster safe_states_list = buchi_final.getAccStates(None) safe_states_list = [field_list[pairs[index][0]] if pairs[index][0] < len(field_list) else [] for index in safe_states_list] bad_event_edge = list() bad_action_edge = list() bad_rule = list() for edge in bad_edges: if '->' in edge.action: # this bad edge is an action triggered by a tap rule # fix_list1 = list(_fixEdgeByDeleting(edge, system)) # fix_list2 = list(_fixEdgeByAddingConstraints(edge, system)) # result.append(fix_list1 + fix_list2) rule_name = re.match(r'rule\((?P<rule_name>\w+)\)->[^ ]+', edge.action).group('rule_name') bad_action_edge.append(edge) if rule_name not in bad_rule: bad_rule.append(rule_name) # result.append(_fixActionEdge(edge, system)) else: # this bad edge is because of external events bad_event_edge.append(edge) # result.append(_fixEventEdge(edge, system)) for edge in bad_event_edge: result.append(_fixEventEdge(edge, system)) for rule_name in bad_rule: result.append(_fixActionRule(rule_name, bad_action_edge, safe_states_list, system)) # return list(itertools.product(*result)) return result
def drawPatchList(system, ltl, patch_list, file_name): """ given system and ltl property, draw graph representation of certain patches as png :param system: the smart home system :param ltl: ltl formula :param patch_list: the patch_list (FixPatch) :param file_name: file to be written :return: """ ts = system.transition_system buchi_ts = Buchi.tsToGenBuchi(ts) buchi_ltl = Buchi.ltlToBuchi(ltl) (buchi_final, pairs) = Buchi.product(buchi_ts, buchi_ltl) group = [s2 for s1, s2 in pairs] modified_buchi = ModifiedBuchi(buchi_final) field_list = [state.field for state in ts.state_list] for edge_index, edge in enumerate(buchi_final.edge_list): src_index_ts = pairs[edge.src][0] dst_index_ts = pairs[edge.dst][0] src_index_ltl = pairs[edge.src][1] dst_index_ltl = pairs[edge.dst][1] src_field = field_list[src_index_ts] dst_field = field_list[dst_index_ts] action = _getAction(ts, src_field, dst_field) if '>' in action: # this is an action triggered by some old rule # should see if it's not triggered in new rule rule_dict = dict([(tap_name, translateTapToRule(tap)) for tap_name, tap in system.tap_dict.items()]) old_rule_name = re.match(r'rule\((?P<rule_name>\w+)\)->[^ ]+', action).group('rule_name') old_rule = rule_dict[old_rule_name] for patch in patch_list: if patch.type == 'delete' and patch.rule_name == old_rule_name: # then this edge is deleted modified_buchi.markDeletedEdge(edge_index) break if patch.type == 'change' and patch.rule_name == old_rule_name: # then this rule is changed, need to see whether edge still triggered new_rule = patch.rule if isinstance(new_rule, ESERule): orig_field = system.getLastStateField( new_rule.trigger, src_field) if orig_field: if not system.apSatisfied(new_rule.condition, orig_field): # then this rule is not triggered anymore modified_buchi.markDeletedEdge(edge_index) break elif '.' in action: # this is an external event # should see if some new rules trigger this for patch in patch_list: new_rule = patch.rule add_edge_flag = 0 if patch.type in ('add', 'change') and not system.isTriggeredState(dst_field) and\ new_rule.trigger == action: if isinstance(new_rule, EERule): # should add an new edge add_edge_flag = 1 elif isinstance(new_rule, ESERule) and \ system.apSatisfied(new_rule.condition, src_field): # should add an new edge add_edge_flag = 1 if add_edge_flag: # this is an new edge, redirection new_field = system.applyActionWithoutTriggering( new_rule.action, dst_field) new_index_ts = ts.getIndex( new_field) # which node it goes in ts ap_list = ts.ap_list new_label = system.getLabel(new_field) var_dict = dict() for ap, label in zip(ap_list, new_label): var_dict[ap] = label # use var_dict to calculate which edge it goes in ltl buchi automata new_index_ltl = -1 # which node it goes in ltl for e in buchi_ltl.edge_list: if e.src == src_index_ltl: if calculateBoolean(e.ap, var_dict): new_index_ltl = e.dst src_final_index = pairs.index( (src_index_ts, src_index_ltl)) new_final_index = pairs.index( (new_index_ts, new_index_ltl)) modified_buchi.markDeletedEdge(edge_index) modified_buchi.addNewEdge(src_final_index, new_final_index) break modified_buchi.writeToGv(file_name, group)
def drawPatch(system, ltl, patch, file_name): """ given system and ltl property, draw graph representation of certain patch as png :param system: the smart home system :param ltl: ltl formula :param patch: the patch (FixPatch) :param file_name: file to be written :return: """ ts = system.transition_system buchi_ts = Buchi.tsToGenBuchi(ts) buchi_ltl = Buchi.ltlToBuchi(ltl) (buchi_final, pairs) = Buchi.product(buchi_ts, buchi_ltl) group = [s2 for s1, s2 in pairs] modified_buchi = ModifiedBuchi(buchi_final) rule_dict = dict([(tap_name, translateTapToRule(tap)) for tap_name, tap in system.tap_dict.items()]) new_rule = None if patch.type == 'delete' else patch.rule old_rule = None if patch.type == 'add' else rule_dict[patch.rule_name] field_list = [state.field for state in ts.state_list] for edge_index, edge in enumerate(buchi_final.edge_list): src_index_ts = pairs[edge.src][0] dst_index_ts = pairs[edge.dst][0] src_index_ltl = pairs[edge.src][1] dst_index_ltl = pairs[edge.dst][1] src_field = field_list[src_index_ts] dst_field = field_list[dst_index_ts] action = _getAction(ts, src_field, dst_field) if '>' in action: # this is an action triggered by a rule # is it handled by old rule? flag_old, flag_new = False, False if old_rule: old_rule_name = patch.rule_name if old_rule_name in action: # handled flag_old = True # is it handled by new rule? if new_rule: if isinstance(new_rule, ESERule): # TODO: should check if this is the correct! should it be last state? if system.apSatisfied(new_rule.condition, src_field): # handled flag_new = True elif isinstance(new_rule, EERule): # handled flag_new = True if flag_old and not flag_new: # this edge is deleted modified_buchi.markDeletedEdge(edge_index) else: # this is an external event if '.' in action: # it is not handled by old rule # is it handled by new rule? old rule? if new_rule: flag_new, flag_old = False, False if new_rule.trigger == action: if isinstance(new_rule, EERule): flag_new = True elif isinstance(new_rule, ESERule): if system.apSatisfied(new_rule.condition, src_field): flag_new = True if old_rule: if old_rule.trigger == action: if isinstance(old_rule, EERule): flag_old = True elif isinstance(old_rule, ESERule): if system.apSatisfied(old_rule.condition, src_field): flag_old = True if flag_new and not flag_old: # this is a new triggered edge, redirection new_field = system.applyActionWithoutTriggering( new_rule.action, dst_field) new_index_ts = ts.getIndex( new_field) # which node it goes in ts ap_list = ts.ap_list new_label = system.getLabel(new_field) var_dict = dict() for ap, label in zip(ap_list, new_label): var_dict[ap] = label # use var_dict to calculate which edge it goes in ltl buchi automata new_index_ltl = -1 # which node it goes in ltl for e in buchi_ltl.edge_list: if e.src == src_index_ltl: if calculateBoolean(e.ap, var_dict): new_index_ltl = e.dst src_final_index = pairs.index( (src_index_ts, src_index_ltl)) new_final_index = pairs.index( (new_index_ts, new_index_ltl)) modified_buchi.markDeletedEdge(edge_index) modified_buchi.addNewEdge(src_final_index, new_final_index) modified_buchi.writeToGv(file_name, group)
def compareRules(tap_list1, tap_list2, ltl, template_dict): """ To show the differences between tap_list1 and tap_list2 :param tap_list1: :param tap_list2: :param ltl: :param template_dict: :return: """ if ltl == None: ltl_formula = '!(0)' else: ltl_formula = ltl crit_value_dict = generateCriticalValue(ltl_formula, tap_list1 + tap_list2) exp_t_list, record_exp_list = generateTimeExp(ltl_formula, tap_list1 + tap_list2) channel_name_list, cap_name_list, tap_list = getChannelList( ltl_formula, tap_list1 + tap_list2) tap_list1 = tapFormat(tap_list1, crit_value_dict) tap_list2 = tapFormat(tap_list2, crit_value_dict) new_ltl = ltlFormat(ltl_formula) # stage 4: generate system channel_dict = generateChannelDict(channel_name_list, crit_value_dict, {}, cap_name_list, template_dict) system = _fixPreProcessing(channel_dict=channel_dict, tap_dict={}, template_numeric_dict=crit_value_dict, timing_exp_list=exp_t_list) ts = system.transition_system buchi_ts = Buchi.tsToGenBuchi(ts, record_exp_list) buchi_ltl = Buchi.ltlToBuchi(new_ltl) (buchi_final, pairs) = Buchi.product(buchi_ts, buchi_ltl) field_list = [state.field for state in ts.state_list] result = list() for edge in buchi_final.edge_list: if not buchi_final.getStateAcc(edge.src): try: src_index_ts = pairs[edge.src][0] dst_index_ts = pairs[edge.dst][0] if ts.num_state == src_index_ts: # this is the initial node, skip continue src_index_ltl = pairs[edge.src][1] dst_index_ltl = pairs[edge.dst][1] src_field = field_list[src_index_ts] dst_field = field_list[dst_index_ts] action = _getAction(ts, src_index_ts, dst_index_ts) ltl_req = _getLTLReq(buchi_ltl, src_index_ltl, dst_index_ltl) beh1 = checkTapListBehavior(tap_list1, system, src_field, action) beh2 = checkTapListBehavior(tap_list2, system, src_field, action) condition = [ ap for ap in system.getApList(src_field) if '@' not in ap and 'trigger' not in ap ] condition = condition + [ '!' + ap for ap in system.getAllAp() if '@' not in ap and 'trigger' not in ap and ap not in condition ] if beh1 != beh2: result.append((action, condition, (beh1, beh2))) except IndexError: # if this happen, this means that the system has already been in wrong state from the initial state raise Exception( 'The property is violated in the initial state, please try a different initial state' ) new_result = list() for action, condition, beh_tup in result: new_condition = [textToformula(cond) for cond in condition] new_beh_0 = None if not beh_tup[0] else textToformula(beh_tup[0]) new_beh_1 = None if not beh_tup[1] else textToformula(beh_tup[1]) new_result.append( (textToformula(action), new_condition, (new_beh_0, new_beh_1))) return new_result
""" from autotapmc.channels.Weather import Weather from autotapmc.channels.GarageDoor import GarageDoor from autotapmc.model.IoTSystem import IoTSystem from autotapmc.model.Tap import ESERule import autotapmc.buchi.Buchi as Buchi class Test(IoTSystem): weather = Weather() door = GarageDoor() rule = ESERule('weather.startsRaining', 'door.open', 'door.close') a = Test() ts = a.transition_system buchi_ts = Buchi.tsToGenBuchi(ts) buchi_ltl = Buchi.ltlToBuchi('F (door.open & weather.raining)') (buchi_final, pairs) = Buchi.product(buchi_ts, buchi_ltl) buchi_ts.log() buchi_ltl.log() buchi_final.log() group = [s2 for s1, s2 in pairs] buchi_final.printToGv(group)