Esempio n. 1
0
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) )
Esempio n. 2
0
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("&lt;", "<") for v in li]
        li = [v.replace("&gt;", ">") for v in li]
        li = [v.replace("&amp;", "&") 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)