def load_file(aut_file, spec): """Construct a Mealy Machine from an aut_file. @param aut_file: the name of the text file containing the automaton, or an (open) file-like object. @type spec: L{GRSpec} @rtype: L{MealyMachine} """ if isinstance(aut_file, str): f = open(aut_file, 'r') closable = True else: f = aut_file # Else, assume aut_file behaves as file object. closable = False #build Mealy Machine m = transys.MealyMachine() # show port only when true mask_func = lambda x: bool(x) # input defs inputs = create_machine_ports(spec.env_vars) m.add_inputs(inputs) # outputs def outputs = create_machine_ports(spec.sys_vars) masks = {k:mask_func for k in spec.sys_vars.keys()} m.add_outputs(outputs, masks) # state variables def state_vars = outputs m.add_state_vars(state_vars) varnames = spec.sys_vars.keys()+spec.env_vars.keys() stateDict = {} for line in f: # parse states if (line.find('State ') >= 0): stateID = re.search('State (\d+)', line) stateID = int(stateID.group(1)) state = dict(re.findall('(\w+):(\w+)', line)) for var, val in state.iteritems(): try: state[var] = int(val) except: state[var] = val if (len(varnames) > 0): if not var in varnames: logger.error('Unknown variable ' + var) for var in varnames: if not var in state.keys(): logger.error('Variable ' + var + ' not assigned') # parse transitions if (line.find('successors') >= 0): transition = re.findall(' (\d+)', line) for i in xrange(0,len(transition)): transition[i] = int(transition[i]) m.states.add(stateID) # mark initial states (states that # do not appear in previous transitions) #seenSoFar = [t for (s,trans) in stateDict.values() for t in trans] #if stateID not in seenSoFar: #m.states.initial.add(stateID) stateDict[stateID] = (state,transition) # add transitions with guards to the Mealy Machine for from_state in stateDict.keys(): state, transitions = stateDict[from_state] for to_state in transitions: guard = stateDict[to_state][0] try: m.transitions.add(from_state, to_state, **guard) except Exception, e: raise Exception('Failed to add transition:\n' +str(e) )
def load_aut_xml(x, namespace=DEFAULT_NAMESPACE, spec0=None): """Return L{GRSpec} and L{MealyMachine} constructed from output of gr1c. @param x: a string or an instance of xml.etree.ElementTree._ElementInterface @type spec0: L{GRSpec} @param spec0: GR(1) specification with which to interpret the output of gr1c while constructing a MealyMachine, or None if the output from gr1c should be used as is. Note that spec0 may differ from the specification in the given tulipcon XML string x. If you are unsure what to do, try setting spec0 to whatever L{gr1cint.synthesize} was invoked with. @return: tuple of the form (L{GRSpec}, L{MealyMachine}). Either or both can be None if the corresponding part is missing. Note that the returned GRSpec instance depends only on what is in the given tulipcon XML string x, not on the argument spec0. """ if not isinstance(x, str) and not isinstance(x, ET._ElementInterface): raise TypeError("tag to be parsed must be given " + "as a string or ElementTree._ElementInterface.") if isinstance(x, str): elem = ET.fromstring(x) else: elem = x if (namespace is None) or (len(namespace) == 0): ns_prefix = "" else: ns_prefix = "{"+namespace+"}" if elem.tag != ns_prefix+"tulipcon": raise TypeError("root tag should be tulipcon.") if ("version" not in elem.attrib.keys()): raise ValueError("unversioned tulipcon XML string.") if int(elem.attrib["version"]) != 1: raise ValueError("unsupported tulipcon XML version: "+ str(elem.attrib["version"])) # Extract discrete variables and LTL specification (tag_name, env_vardict, env_vars) = _untagdict(elem.find( ns_prefix+"env_vars"), get_order=True) (tag_name, sys_vardict, sys_vars) = _untagdict(elem.find( ns_prefix+"sys_vars"), get_order=True) env_vars = _parse_vars(env_vars, env_vardict) sys_vars = _parse_vars(sys_vars, sys_vardict) s_elem = elem.find(ns_prefix+"spec") spec = GRSpec(env_vars=env_vars, sys_vars=sys_vars) for spec_tag in ["env_init", "env_safety", "env_prog", "sys_init", "sys_safety", "sys_prog"]: if s_elem.find(ns_prefix+spec_tag) is None: raise ValueError("invalid specification in tulipcon XML string.") (tag_name, li) = _untaglist(s_elem.find(ns_prefix+spec_tag), cast_f=str, namespace=namespace) li = [v.replace("<", "<") for v in li] li = [v.replace(">", ">") for v in li] li = [v.replace("&", "&") for v in li] setattr(spec, spec_tag, li) aut_elem = elem.find(ns_prefix+"aut") if aut_elem is None or ( (aut_elem.text is None) and len(aut_elem.getchildren()) == 0): mach = None return (spec, mach) # Assume version 1 of tulipcon XML if aut_elem.attrib["type"] != "basic": raise ValueError("Automaton class only recognizes type \"basic\".") node_list = aut_elem.findall(ns_prefix+"node") id_list = [] # For more convenient searching, and to catch redundancy A = nx.DiGraph() for node in node_list: this_id = int(node.find(ns_prefix+"id").text) #this_name = node.find(ns_prefix+"anno").text # Assume version 1 (tag_name, this_name_list) = _untaglist(node.find(ns_prefix+"anno"), cast_f=int) if len(this_name_list) == 2: (mode, rgrad) = this_name_list else: (mode, rgrad) = (-1, -1) (tag_name, this_child_list) = _untaglist( node.find(ns_prefix+"child_list"), cast_f=int ) if tag_name != ns_prefix+"child_list": # This really should never happen and may not even be # worth checking. raise ValueError("failure of consistency check " + "while processing aut XML string.") (tag_name, this_state) = _untagdict(node.find(ns_prefix+"state"), cast_f_values=int, namespace=namespace) if tag_name != ns_prefix+"state": raise ValueError("failure of consistency check " + "while processing aut XML string.") if this_id in id_list: logger.warn("duplicate nodes found: "+str(this_id)+"; ignoring...") continue id_list.append(this_id) logger.info('loaded from gr1c result:\n\t' +str(this_state) ) A.add_node(this_id, state=copy.copy(this_state), mode=mode, rgrad=rgrad) for next_node in this_child_list: A.add_edge(this_id, next_node) if spec0 is None: spec0 = spec # show port only when true (or non-zero for int-valued vars) mask_func = bool mach = MealyMachine() inputs = create_machine_ports(spec0.env_vars) mach.add_inputs(inputs) outputs = create_machine_ports(spec0.sys_vars) masks = {k:mask_func for k in sys_vars} mach.add_outputs(outputs, masks) arbitrary_domains = { k:v for k, v in spec0.env_vars.items() if isinstance(v, list) } arbitrary_domains.update({ k:v for k, v in spec0.sys_vars.items() if isinstance(v, list) }) state_vars = OrderedDict() varname = 'loc' if varname in outputs: state_vars[varname] = outputs[varname] varname = 'eloc' if varname in inputs: state_vars[varname] = inputs[varname] mach.add_state_vars(state_vars) # states and state variables mach.states.add_from(A.nodes()) for state in mach.states: label = _map_int2dom(A.node[state]["state"], arbitrary_domains) label = {k:v for k,v in label.iteritems() if k in {'loc', 'eloc'}} mach.states.add(state, **label) # transitions labeled with I/O for u in A.nodes_iter(): for v in A.successors_iter(u): logger.info('node: ' +str(v) +', state: ' + str(A.node[v]["state"]) ) label = _map_int2dom(A.node[v]["state"], arbitrary_domains) mach.transitions.add(u, v, **label) # special initial state, for first input initial_state = 'Sinit' mach.states.add(initial_state) mach.states.initial |= [initial_state] # replace values of arbitrary variables by ints spec1 = spec0.copy() for variable, domain in arbitrary_domains.items(): values2ints = {var:str(i) for i, var in enumerate(domain)} # replace symbols by ints spec1.sym_to_prop(values2ints) # Mealy reaction to initial env input for node in A.nodes_iter(): var_values = A.node[node]['state'] bool_values = {} for k, v in var_values.iteritems(): is_bool = False if k in env_vars: if env_vars[k] == 'boolean': is_bool = True if k in sys_vars: if sys_vars[k] == 'boolean': is_bool = True if is_bool: bool_values.update({k:str(bool(v) )}) else: bool_values.update({k:str(v)}) logger.info('Boolean values: ' +str(bool_values) ) t = spec1.evaluate(bool_values) logger.info('evaluates to: ' +str(t) ) if t['env_init'] and t['sys_init']: label = _map_int2dom(var_values, arbitrary_domains) mach.transitions.add(initial_state, node, **label) return (spec, mach)