Пример #1
0
    def __init__(self, name=None, **kwds):
        name = name or kwds.setdefault('context', self.__class__.__name__)

        slct = octets_noop('select')
        slct[None] = cpppo.decide('SendData',
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + '..command'] in
                                  (0x006f, 0x0070),
                                  state=send_data(limit='...length',
                                                  terminal=True))
        slct[None] = cpppo.decide('Register',
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + '..command'] == 0x0065,
                                  state=register(limit='...length',
                                                 terminal=True))
        slct[None] = cpppo.decide('Unregister',
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + '..command'] == 0x0066,
                                  state=unregister(limit='...length',
                                                   terminal=True))
        slct[None] = cpppo.decide('ListServices',
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + '..command'] == 0x0004,
                                  state=list_services(limit='...length',
                                                      terminal=True))

        super(CIP, self).__init__(name=name, initial=slct, **kwds)
Пример #2
0
    def __init__(self, name=None, datatype=None, **kwds):
        name = name or kwds.setdefault('context', self.__class__.__name__)
        assert datatype, "Must specify a relative path to the CIP data type; found: %r" % datatype

        slct = octets_noop('select')

        i_8d = octets_noop('end_8bit', terminal=True)
        i_8d[True] = i_8p = SINT()
        i_8p[None] = move_if('mov_8bit',
                             source='.SINT',
                             destination='.data',
                             initializer=lambda **kwds: [],
                             state=i_8d)

        i16d = octets_noop('end16bit', terminal=True)
        i16d[True] = i16p = INT()
        i16p[None] = move_if('mov16bit',
                             source='.INT',
                             destination='.data',
                             initializer=lambda **kwds: [],
                             state=i16d)

        i32d = octets_noop('end32bit', terminal=True)
        i32d[True] = i32p = DINT()
        i32p[None] = move_if('mov32bit',
                             source='.DINT',
                             destination='.data',
                             initializer=lambda **kwds: [],
                             state=i32d)

        fltd = octets_noop('endfloat', terminal=True)
        fltd[True] = fltp = REAL()
        fltp[None] = move_if('movfloat',
                             source='.REAL',
                             destination='.data',
                             initializer=lambda **kwds: [],
                             state=fltd)

        slct[None] = cpppo.decide('SINT',
                                  state=i_8p,
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + datatype] == SINT.tag_type)
        slct[None] = cpppo.decide('INT',
                                  state=i16p,
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + datatype] == INT.tag_type)
        slct[None] = cpppo.decide('DINT',
                                  state=i32p,
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + datatype] == DINT.tag_type)
        slct[None] = cpppo.decide('REAL',
                                  state=fltp,
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + datatype] == REAL.tag_type)

        super(typed_data, self).__init__(name=name, initial=slct, **kwds)
Пример #3
0
def test_decide():
    """Allow state transition decisions based on collected context other than just
    the next source symbol.

    """
    e = cpppo.state("enter")
    e["a"] = a = cpppo.state_input("a", context="a")
    a[" "] = s1 = cpppo.state_drop("s1")
    s1[" "] = s1
    s1[None] = i1 = cpppo.integer("i1", context="i1")

    i1[" "] = s2 = cpppo.state_drop("s2")
    s2[" "] = s2
    s2[None] = i2 = cpppo.integer("i2", context="i2")
    less = cpppo.state("less", terminal=True)
    greater = cpppo.state("greater", terminal=True)
    equal = cpppo.state("equal", terminal=True)
    i2[None] = cpppo.decide("isless", less, predicate=lambda machine, source, path, data: data.i1 < data.i2)
    i2[None] = cpppo.decide("isgreater", greater, predicate=lambda machine, source, path, data: data.i1 > data.i2)
    i2[None] = equal

    source = cpppo.peekable(str("a 1 2"))
    data = cpppo.dotdict()
    with cpppo.dfa("comparo", initial=e) as comparo:
        for i, (m, s) in enumerate(comparo.run(source=source, data=data)):
            log.info(
                "%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r",
                m.name_centered(),
                i,
                s,
                source.sent,
                source.peek(),
                data,
            )
        assert i == 11
        assert s is less

    source = cpppo.peekable(str("a 33 33"))
    data = cpppo.dotdict()
    with cpppo.dfa("comparo", initial=e) as comparo:
        for i, (m, s) in enumerate(comparo.run(source=source, data=data)):
            log.info(
                "%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r",
                m.name_centered(),
                i,
                s,
                source.sent,
                source.peek(),
                data,
            )
        assert i == 13
        assert s is equal
Пример #4
0
    def __init__( self, name=None, **kwds ):
        name 			= name or kwds.setdefault( 'context', self.__class__.__name__ )

        slct			= octets_noop(	'select' )

        usnd			= USINT(	context='service' )
        usnd[True]	= path	= EPATH(	context='path' )
        # All Unconnected Send (0x52) encapsulated request.input have a length, followed by an
        # optional pad, and then a route path.
        path[True]	= prio	= USINT(	context='priority' )
        prio[True]	= timo	= USINT(	context='timeout_ticks' )
        timo[True]	= leng	= UINT(		context='length' )
        leng[None]	= mesg	= octets( 	context='request', repeat='..length' )

        # Route segments, like path but for hops/links/keys...
        rout			= route_path( terminal=True )

        # If length is odd, drop the pad byte after the message, and then parse the route_path
        pad0			= octets_drop( 'pad', 	repeat=1 )
        pad0[None]		= rout

        mesg[None]		= cpppo.decide( 'pad',	state=pad0,
                            predicate=lambda path=None, data=None, **kwds: data[path+'.length'] % 2 )

        # But, if no pad, go parse the route path
        mesg[None]		= rout

        # So; 0x52 Unconnected Send parses an request with a Route Path, but anything else is just
        # an opaque encapsulated request; just copy all remaining bytes to the request.input.
        slct[b'\x52'[0]]	= usnd
        slct[True]	= othr	= octets(	context='request', terminal=True )
        othr[True]		= othr

        super( unconnected_send, self ).__init__( name=name, initial=slct, **kwds )
Пример #5
0
    def __init__( self, name=None, **kwds ):
        name 			= name or kwds.setdefault( 'context', self.__class__.__name__ )

        # Parse the status, and status_ext.size
        stat			= USINT( 	'status',	context=None )
        stat[True]	= size	= USINT( 	'_ext.size',	extension='_ext.size' )

        # Prepare a state-machine to parse each UINT into .UINT, and move it onto the .data list
        exts			= UINT(		'ext_status',	extension='.ext_status' )
        exts[None]		= move_if( 	'data',		source='.ext_status',
                                           destination='.data',	initializer=lambda **kwds: [] )
        exts[None]		= cpppo.state( 	'done', terminal=True )

        # Parse each status_ext.data in a sub-dfa, repeating status_ext.size times
        each			= cpppo.dfa(    'each',		extension='_ext',
                                                initial=exts,	repeat='_ext.size',
                                                terminal=True )
        # Only enter the state_ext.data dfa if status_ext.size is non-zero
        size[None]		= cpppo.decide(	'_ext.size', 
                            predicate=lambda path=None, data=None, **kwds: data[path+'_ext.size'],
                                                state=each )
        # Otherwise, we're done!
        size[None]		= octets_noop( 'done', 
                                               terminal=True )
        super( status, self ).__init__( name=name, initial=stat, **kwds )
Пример #6
0
    def __init__(self, name=None, **kwds):
        name = name or kwds.setdefault('context', self.__class__.__name__)

        # Parse the status, and status_ext.size
        stat = USINT('status', context=None)
        stat[True] = size = USINT('_ext.size', extension='_ext.size')

        # Prepare a state-machine to parse each UINT into .UINT, and move it onto the .data list
        exts = UINT('ext_status', extension='.ext_status')
        exts[None] = move_if('data',
                             source='.ext_status',
                             destination='.data',
                             initializer=lambda **kwds: [])
        exts[None] = cpppo.state('done', terminal=True)

        # Parse each status_ext.data in a sub-dfa, repeating status_ext.size times
        each = cpppo.dfa('each',
                         extension='_ext',
                         initial=exts,
                         repeat='_ext.size',
                         terminal=True)
        # Only enter the state_ext.data dfa if status_ext.size is non-zero
        size[None] = cpppo.decide('_ext.size',
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + '_ext.size'],
                                  state=each)
        # Otherwise, we're done!
        size[None] = octets_noop('done', terminal=True)
        super(status, self).__init__(name=name, initial=stat, **kwds)
Пример #7
0
    def __init__(self, name=None, **kwds):
        """Parse CPF list items 'til .count reached, which should be simultaneous with symbol exhaustion, if
        caller specified a symbol limit.

        """
        name = name or kwds.setdefault('context', self.__class__.__name__)

        # A number, and then each CPF item consistes of a type, length and then parsable data.
        ityp = UINT(context='type_id')
        ityp[True] = ilen = UINT(context='length')
        ilen[None] = cpppo.decide('empty',
                                  predicate=lambda path=None, data=None, **
                                  kwds: not data[path].length,
                                  state=octets_noop('done', terminal=True))

        # Prepare a parser for each recognized CPF item type.  It must establish one level of
        # context, because we need to pass it a limit='..length' denoting the length we just parsed.

        for typ, cls in (self.item_parsers or {}).items():
            ilen[None] = cpppo.decide(cls.__name__,
                                      state=cls(terminal=True,
                                                limit='..length'),
                                      predicate=lambda path=None, data=None, **
                                      kwds: data[path].type_id == typ)

        # If we don't recognize the CPF item type, just parse remainder into .input (so we could re-generate)
        ilen[None] = urec = octets('unrecognized', context=None, terminal=True)
        urec[True] = urec

        # Each item is collected into '.item__', 'til no more input available, and then moved into
        # place into '.item' (init to [])
        item = cpppo.dfa('each', context='item__', initial=ityp)
        item[None] = move_if('move',
                             source='.item__',
                             destination='.item',
                             initializer=lambda **kwds: [])
        item[None] = cpppo.state('done', terminal=True)

        # Parse count, and then exactly .count CPF items.
        loop = UINT(context='count')
        loop[None] = cpppo.dfa('all',
                               initial=item,
                               repeat='.count',
                               terminal=True)

        super(CPF, self).__init__(name=name, initial=loop, **kwds)
Пример #8
0
    def __init__( self, name=None, **kwds ):
        name 			= name or kwds.setdefault( 'context', self.__class__.__name__ )

        slct			= octets_noop(	'select' )
        slct[None]		= cpppo.decide( 'SendData',
            predicate=lambda path=None, data=None, **kwds: data[path+'..command'] in (0x006f,0x0070),
                                            state=send_data(	limit='...length', terminal=True ))
        slct[None]		= cpppo.decide( 'Register', 
            predicate=lambda path=None, data=None, **kwds: data[path+'..command'] == 0x0065,
                                            state=register(	limit='...length', terminal=True ))
        slct[None]		= cpppo.decide( 'Unregister',
            predicate=lambda path=None, data=None, **kwds: data[path+'..command'] == 0x0066,
                                            state=unregister(	limit='...length', terminal=True ))
        slct[None]		= cpppo.decide( 'ListServices',
            predicate=lambda path=None, data=None, **kwds: data[path+'..command'] == 0x0004,
                                            state=list_services(limit='...length', terminal=True ))

        super( CIP, self ).__init__( name=name, initial=slct, **kwds )
Пример #9
0
def test_decide():
    """Allow state transition decisions based on collected context other than just
    the next source symbol.

    """
    e = cpppo.state("enter")
    e['a'] = a = cpppo.state_input("a", context='a')
    a[' '] = s1 = cpppo.state_drop("s1")
    s1[' '] = s1
    s1[None] = i1 = cpppo.integer("i1", context='i1')

    i1[' '] = s2 = cpppo.state_drop("s2")
    s2[' '] = s2
    s2[None] = i2 = cpppo.integer("i2", context='i2')
    less = cpppo.state("less", terminal=True)
    greater = cpppo.state("greater", terminal=True)
    equal = cpppo.state("equal", terminal=True)
    i2[None] = cpppo.decide(
        "isless",
        less,
        predicate=lambda machine, source, path, data: data.i1 < data.i2)
    i2[None] = cpppo.decide(
        "isgreater",
        greater,
        predicate=lambda machine, source, path, data: data.i1 > data.i2)
    i2[None] = equal

    source = cpppo.peekable(str('a 1 2'))
    data = cpppo.dotdict()
    with cpppo.dfa("comparo", initial=e) as comparo:
        for i, (m, s) in enumerate(comparo.run(source=source, data=data)):
            log.info("%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r",
                     m.name_centered(), i, s, source.sent, source.peek(), data)
        assert i == 12
        assert s is less

    source = cpppo.peekable(str('a 33 33'))
    data = cpppo.dotdict()
    with cpppo.dfa("comparo", initial=e) as comparo:
        for i, (m, s) in enumerate(comparo.run(source=source, data=data)):
            log.info("%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r",
                     m.name_centered(), i, s, source.sent, source.peek(), data)
        assert i == 14
        assert s is equal
Пример #10
0
    def __init__( self, name=None, datatype=None, **kwds ):
        name 			= name or kwds.setdefault( 'context', self.__class__.__name__ )
        assert datatype, "Must specify a relative path to the CIP data type; found: %r" % datatype

        slct			= octets_noop(	'select' )
        
        i_8d			= octets_noop(	'end_8bit',
                                                terminal=True )
        i_8d[True]	= i_8p	= SINT()
        i_8p[None]		= move_if( 	'mov_8bit',	source='.SINT', 
                                           destination='.data',	initializer=lambda **kwds: [],
                                                state=i_8d )

        i16d			= octets_noop(	'end16bit',
                                                terminal=True )
        i16d[True]	= i16p	= INT()
        i16p[None]		= move_if( 	'mov16bit',	source='.INT', 
                                           destination='.data',	initializer=lambda **kwds: [],
                                                state=i16d )

        i32d			= octets_noop(	'end32bit',
                                                terminal=True )
        i32d[True]	= i32p	= DINT()
        i32p[None]		= move_if( 	'mov32bit',	source='.DINT', 
                                           destination='.data',	initializer=lambda **kwds: [],
                                                state=i32d )

        fltd			= octets_noop(	'endfloat',
                                                terminal=True )
        fltd[True]	= fltp	= REAL()
        fltp[None]		= move_if( 	'movfloat',	source='.REAL', 
                                           destination='.data',	initializer=lambda **kwds: [],
                                                state=fltd )

        slct[None]		= cpppo.decide(	'SINT',	state=i_8p,
            predicate=lambda path=None, data=None, **kwds: data[path+datatype] == SINT.tag_type )
        slct[None]		= cpppo.decide(	'INT',	state=i16p,
            predicate=lambda path=None, data=None, **kwds: data[path+datatype] == INT.tag_type )
        slct[None]		= cpppo.decide(	'DINT',	state=i32p,
            predicate=lambda path=None, data=None, **kwds: data[path+datatype] == DINT.tag_type )
        slct[None]		= cpppo.decide(	'REAL',	state=fltp,
            predicate=lambda path=None, data=None, **kwds: data[path+datatype] == REAL.tag_type )
        
        super( typed_data, self ).__init__( name=name, initial=slct, **kwds )
Пример #11
0
    def __init__( self, name=None, **kwds ):
        """Parse CPF list items 'til .count reached, which should be simultaneous with symbol exhaustion, if
        caller specified a symbol limit.

        """
        name 			= name or kwds.setdefault( 'context', self.__class__.__name__ )

        # A number, and then each CPF item consistes of a type, length and then parsable data.  
        ityp			= UINT( 			context='type_id' )
        ityp[True]	= ilen	= UINT( 			context='length' )
        ilen[None]		= cpppo.decide( 'empty',
                                predicate=lambda path=None, data=None, **kwds: not data[path].length,
                                                state=octets_noop( 'done', terminal=True ))

        # Prepare a parser for each recognized CPF item type.  It must establish one level of
        # context, because we need to pass it a limit='..length' denoting the length we just parsed.

        for typ,cls in ( self.item_parsers or {} ).items():
            ilen[None]		= cpppo.decide( cls.__name__, state=cls( terminal=True, limit='..length' ),
                        predicate=lambda path=None, data=None, **kwds: data[path].type_id == typ )

        # If we don't recognize the CPF item type, just parse remainder into .input (so we could re-generate)
        ilen[None]	= urec	= octets( 	'unrecognized',	context=None,
                                                terminal=True )
        urec[True]		= urec

        # Each item is collected into '.item__', 'til no more input available, and then moved into
        # place into '.item' (init to [])
        item			= cpppo.dfa( 	'each', 	context='item__',
                                                initial=ityp )
        item[None] 		= move_if( 	'move', 	source='.item__',
                                           destination='.item', initializer=lambda **kwds: [] )
        item[None]		= cpppo.state( 	'done', terminal=True )

        # Parse count, and then exactly .count CPF items.
        loop			= UINT( 			context='count' )
        loop[None]		= cpppo.dfa( 	'all', 	
                                                initial=item,	repeat='.count',
                                                terminal=True )

        super( CPF, self ).__init__( name=name, initial=loop, **kwds )
Пример #12
0
def __read_tag_reply():
    # Read Tag Service (reply).  Remainder of symbols are typed data.
    srvc			= USINT(		 	context='service' )
    srvc[True]		= rsvd	= octets_drop(	'reserved',	repeat=1 )
    rsvd[True]		= stts	= status()
    stts[None]		= schk	= octets_noop(	'check',
                                                terminal=True )

    dtyp			= UINT( 	'type',   	context='read_tag',  extension='.type' )
    dtyp[True]			= typed_data( 	'data',   	context='read_tag',
                                        datatype='.type',
                                        terminal=True )
    # For status 0x00 (Success), type/data follows.
    schk[None]			= cpppo.decide(	'ok',		state=dtyp,
        predicate=lambda path=None, data=None, **kwds: data[path+'.status' if path else 'status']== 0x00 )
    return srvc
Пример #13
0
    def __init__(self, name=None, **kwds):
        name = name or kwds.setdefault('context', self.__class__.__name__)

        slct = octets_noop('select')

        usnd = USINT(context='service')
        usnd[True] = path = EPATH(context='path')
        # All Unconnected Send (0x52) encapsulated request.input have a length, followed by an
        # optional pad, and then a route path.
        path[True] = prio = USINT(context='priority')
        prio[True] = timo = USINT(context='timeout_ticks')
        timo[True] = leng = UINT(context='length')
        leng[None] = mesg = octets(context='request', repeat='..length')

        # Route segments, like path but for hops/links/keys...
        rout = route_path(terminal=True)

        # If length is odd, drop the pad byte after the message, and then parse the route_path
        pad0 = octets_drop('pad', repeat=1)
        pad0[None] = rout

        mesg[None] = cpppo.decide('pad',
                                  state=pad0,
                                  predicate=lambda path=None, data=None, **
                                  kwds: data[path + '.length'] % 2)

        # But, if no pad, go parse the route path
        mesg[None] = rout

        # So; 0x52 Unconnected Send parses an request with a Route Path, but anything else is just
        # an opaque encapsulated request; just copy all remaining bytes to the request.input.
        slct[b'\x52'[0]] = usnd
        slct[True] = othr = octets(context='request', terminal=True)
        othr[True] = othr

        super(unconnected_send, self).__init__(name=name, initial=slct, **kwds)
Пример #14
0
    def __init__(self, name=None, **kwds):
        name = name or kwds.setdefault('context', self.__class__.__name__)
        # All command request/reply share the first 12 bytes:
        init = USINT(context='byt0')
        init[True] = byt1 = USINT(context='byt1')
        byt1[True] = dst = USINT(context='dst')
        dst[True] = byt3 = USINT(context='byt3')
        byt3[True] = byt4 = USINT(context='byt4')
        byt4[True] = byt5 = USINT(context='byt5')
        byt5[True] = src = USINT(context='src')
        src[True] = byt7 = USINT(context='byt7')
        byt7[True] = cmd = USINT(context='cmd')
        cmd[True] = sts = USINT(context='sts')
        sts[True] = tns = UINT(context='tns')

        # Reply has STS == 0xF0, then EXT STS is parsed
        rpy_extsts = USINT(context='extsts', terminal=True)
        # Reply has STS != 0x00, then reply is done
        rpy_sts = octets_noop(terminal=True)
        # Reply has STS == 0; collect remaining USINTs into .data (cannot parse, b/c replies not self-describing)
        rpy = typed_data(tag_type=USINT.tag_type, context='', terminal=True)

        # Split the replies off
        # For a Reply (CMD & 0xb01000000 set); If sts was !0, then the byte following TNS is EXT STS.
        tns[None]  = cpppo.decide( 'RPY EXT STS?', state=rpy_extsts,
                                            predicate=lambda path=None, data=None, **kwds: \
                                                bool( data[path].cmd & 0b01000000 ) and data[path].sts == 0xf0 )
        tns[None]  = cpppo.decide( 'RPY STS?', state=rpy_sts,
                                            predicate=lambda path=None, data=None, **kwds: \
                                                bool( data[path].cmd & 0b01000000 ) and data[path].sts != 0x00 )
        tns[None]  = cpppo.decide( 'RPY?',  state=rpy,
                                            predicate=lambda path=None, data=None, **kwds: \
                                                bool( data[path].cmd & 0b01000000 ) and data[path].sts == 0x00 )

        # Request follows TNS. May be a request w/o a FNC.  See:
        # http://literature.rockwellautomation.com/idc/groups/literature/documents/rm/1770-rm516_-en-p.pdf
        # 7-2 for a table of all CMD/FNC codes. Just collect the rest into .data.
        req_nonfnc = typed_data(tag_type=USINT.tag_type,
                                context='',
                                terminal=True)
        tns[None]  = cpppo.decide( 'REQ NON FNC', state=req_nonfnc,
                                        predicate=lambda path=None, data=None, **kwds: \
                                            data[path].cmd in (
                                                0x00, #   protected write
                                                0x01, # unprotected read
                                                0x02, #   protected bit write
                                                0x05, #    physical read
                                                0x05, # unprotected bit write
                                                0x08, # unprotected write
                                            ))

        # ... 7-17: .read...: Protected Typed Logical Read w/ 3 Address Fields has (sub-)element w/ 1 or 2-byte values
        tlr3 = tlr3b = USINT(context='read', extension='.bytes')
        tlr3b[True] = tlr3fn = USINT(context='read',
                                     extension='.file')  # 0-254
        tlr3fn16 = UINT(context='read', extension='.file')
        tlr3fn[None]  = cpppo.decide( 'FLN 16?', state=tlr3fn16,
                                                predicate=lambda path=None, data=None, **kwds: \
                                                    data[path].read.file == 0xFF )
        tlr3fn16[True] = tlr3fn[True] = tlr3ft = USINT(
            context='read',
            extension='.type')  # 0x80-8F; 89 = integer, 8A = float, ...
        tlr3ft[True] = tlr3el = USINT(
            context='read',
            extension='.element')  # 0xFF ==> 16-bit element # follows
        tlr3el16 = UINT(context='read', extension='.element')
        tlr3el[None]  = cpppo.decide( 'ELE 16?', state=tlr3el16,
                                                predicate=lambda path=None, data=None, **kwds: \
                                                    data[path].read.element == 0xFF )
        tlr3el16[True] = tlr3el[True] = tlr3se = USINT(context='read',
                                                       extension='.subelement',
                                                       terminal=True)
        tlr3se16 = UINT(context='read', extension='.subelement', terminal=True)
        tlr3se[None]  = cpppo.decide( 'SEL 16?', state=tlr3se16,
                                                predicate=lambda path=None, data=None, **kwds: \
                                                    data[path].read.subelement == 0xFF )

        # 7-28: .read...: Typed Read PLC5
        # 13-11:   Uses PLC-5 Logical Binary Addressing. Too complex to implement right now.
        #trp5		= trp5o	= UINT(				context='read', extension='.offset' )# Offset to 1st item in range to return
        #trp5o[True]	= trp5t	= UINT(				context='read', extension='.total' ) # Total number of data items in range

        # ... 7-6: .status: Diagnostic Status has no additional data; create (empty) .status
        diag = diagm = octets_noop(context='status', terminal=True)
        diagm.initial[None] = move_if('mark', initializer=True)

        # A Request CMD w/ a FNC code. See if we recognize it.
        tns[None] = fnc = USINT(context='fnc')
        #fnc[None]		= cpppo.decide(	'Typed Read?',		state=tlr3,  # Typed Read (read block) w/ PLC-5 sys. addressing
        #                                        predicate=lambda path=None, data=None, **kwds: \
        #                                            data[path].cmd in (0x0F, 0x2F) and data[path].fnc == 0x68 )
        fnc[None]  = cpppo.decide( 'Typed Read 3?', state=tlr3,  # Protected Typed Logical Read w/ 3 Address Fields
                                                predicate=lambda path=None, data=None, **kwds: \
                                                    data[path].cmd in (0x0F, 0x2F) and data[path].fnc == 0xA2 )
        fnc[None]  = cpppo.decide( 'Diagnostic Status? ', state=diag,  # Diagnostic Status
                                                predicate=lambda path=None, data=None, **kwds: \
                                                    data[path].cmd in (0x06, 0x26) and data[path].fnc == 0x03 )

        # Unknown CMD/FNC; just harvest the rest of the request into .data (often no further command data)
        fnc[None] = typed_data(tag_type=USINT.tag_type,
                               context='',
                               terminal=True)

        super(ANC_120e_DF1, self).__init__(name=name, initial=init, **kwds)
Пример #15
0
    def __init__( self, name=None, **kwds ):
        name 			= name or kwds.setdefault( 'context', self.__class__.__name__ )

        # Get the size, and chain remaining machine onto rest.  When used as a Route Path, the size
        # is padded, so insert a state to drop the pad, and chain rest to that instead.
        size		= rest	= USINT(			context='size' )
        if self.padsize:
            size[True]	= rest	= octets_drop( 	'pad', 		repeat=1 )

        # After capturing each segment__ (pseg), move it onto the path segment list, and loop
        pseg			= octets_noop(	'type',		terminal=True )
        # ...segment parsers...
        pmov			= move_if( 	'move',		initializer=lambda **kwds: [],
                                            source='..segment__', destination='..segment',
                                                state=pseg )

        # Wire each different segment type parser between pseg and pmov
        pseg[b'\x28'[0]]= e_8t	= octets_drop(	'type',		repeat=1 )
        e_8t[True]	= e_8v	= USINT( 	'elem_8bit',	context='element')
        e_8v[None]		= pmov

        pseg[b'\x29'[0]]= e16t	= octets_drop(	'type',		repeat=2 )
        e16t[True]	= e16v	= UINT(		'elem16bit',	context='element')
        e16v[None]		= pmov

        pseg[b'\x2a'[0]]= e32t	= octets_drop(	'type',		repeat=2 )
        e32t[True]	= e32v	= UDINT(	'elem32bit',	context='element')
        e32v[None]		= pmov


        pseg[b'\x20'[0]]= c_8t	= octets_drop(	'type',		repeat=1 )
        c_8t[True]	= c_8v	= USINT(	'clas_8bit',	context='class')
        c_8v[None]		= pmov

        pseg[b'\x21'[0]]= c16t	= octets_drop(	'type',		repeat=2 )
        c16t[True]	= c16v	= UINT(		'clas16bit',	context='class')
        c16v[None]		= pmov


        pseg[b'\x24'[0]]= i_8t	= octets_drop(	'type',		repeat=1 )
        i_8t[True]	= i_8v	= USINT(	'inst_8bit',	context='instance')
        i_8v[None]		= pmov

        pseg[b'\x25'[0]]= i16t	= octets_drop(	'type',		repeat=2 )
        i16t[True]	= i16v	= UINT(		'inst16bit',	context='instance')
        i16v[None]		= pmov


        pseg[b'\x30'[0]]= a_8t	= octets_drop(	'type',		repeat=1 )
        a_8t[True]	= a_8v	= USINT(	'attr_8bit',	context='attribute')
        a_8v[None]		= pmov

        pseg[b'\x31'[0]]= a16t	= octets_drop(	'type',		repeat=2 )
        a16t[True]	= a16v	= UINT(		'attr16bit',	context='attribute')
        a16v[None]		= pmov


        pseg[b'\x91'[0]]= symt	= octets_drop(	'type',		repeat=1 )
        symt[True]	= syml	= USINT(	'sym_len',	context='symbolic.length' )
        syml[None]	= symv	= cpppo.string_bytes(
            					'symbolic',	context='symbolic', limit='.length',
                                                initial='.*',	decode='iso-8859-1' )

        # An odd-length ANSI Extended Symbolic name means an odd total.  Pad
        symo			= octets_drop(	'pad', 		repeat=1 )
        symo[None]		= pmov

        symv[None]		= cpppo.decide(	'odd',
                predicate=lambda path=None, data=None, **kwds: len( data[path].symbolic ) % 2,
                                                state=symo )
        symv[None]		= pmov


        # Route Path port/link-address.  See Vol 1-3.13, Table C-1.3 Port Segment Encoding.
        # segment:  0b000spppp 
        #                |\\\\+-> port number 0x01-0x0E; 0x0F=>extended
        #                |
        #                +------> link size+address; 0=>numeric, 1=>size+string
        # 

        def port_fix( path=None, data=None, **kwds ):
            """Discard port values about 0x0F; return True (transition) if remaining port value is 0x0F
            (Optional Extended port)"""
            data[path].port    &= 0x0F
            if data[path].port == 0x0F:
                # Port is extended; discard and prepare to collect new port number
                data[path].port	= cpppo.dotdict()
                return True
            # Port is OK; don't transition
            return False

        # [01-0E][LL] 				port 01-0E, link-address #LL
        pseg[b'\x01'[0]]= pnum	= USINT(	'port_num',	context='port' )
        pseg[b'\x02'[0]]	= pnum
        pseg[b'\x03'[0]]	= pnum
        pseg[b'\x04'[0]]	= pnum
        pseg[b'\x05'[0]]	= pnum
        pseg[b'\x06'[0]]	= pnum
        pseg[b'\x07'[0]]	= pnum
        pseg[b'\x08'[0]]	= pnum
        pseg[b'\x09'[0]]	= pnum
        pseg[b'\x0a'[0]]	= pnum
        pseg[b'\x0b'[0]]	= pnum
        pseg[b'\x0c'[0]]	= pnum
        pseg[b'\x0d'[0]]	= pnum
        pseg[b'\x0e'[0]]	= pnum

        # [0F][PPPP][LL]			port 0xPPPP,  link-address 0xLL
        pseg[b'\x0f'[0]]	= pnum

        # A big port#; re-scan a UINT into .port (won't work 'til port_fix is called)
        pnbg			= UINT(		'port_nbg',	context='port' )
        pnbg[True]	= pnlk	= USINT(	'link_num',	context='link' )

        # Fix the port#; if 0x0F, setup for extended port and transition to pnbg.  Otherwise,
        # (not extended port), just go the the port numeric link.
        pnum[None]		= cpppo.decide( 'port_nfix',	predicate=port_fix,
                                                state=pnbg )
        pnum[None]		= pnlk
        pnlk[None]		= pmov	 	# and done; move segment, get next

        # [11-1E][SS]'123.123.123.123'[00]	port 0x01-0E, link address '123.123.123.123' (pad if size 0xSS odd)
        pseg[b'\x11']	= padr	= USINT(	'port_adr',	context='port' )
        pseg[b'\x12'[0]]	= padr
        pseg[b'\x13'[0]]	= padr
        pseg[b'\x14'[0]]	= padr
        pseg[b'\x15'[0]]	= padr
        pseg[b'\x16'[0]]	= padr
        pseg[b'\x17'[0]]	= padr
        pseg[b'\x18'[0]]	= padr
        pseg[b'\x19'[0]]	= padr
        pseg[b'\x1a'[0]]	= padr
        pseg[b'\x1b'[0]]	= padr
        pseg[b'\x1c'[0]]	= padr
        pseg[b'\x1d'[0]]	= padr
        pseg[b'\x1e'[0]]	= padr

        # [1F][SS][PPPP]'123.123.123.123'[00]	port 0xPPPP,  link address '123.123.123.123' (pad if size SS odd)
        pseg[b'\x1f'[0]]	= padr

        # Harvest the addresses into .link
        adrv			= cpppo.string_bytes(
            					'link_add',	context='link',	limit='.length',
                                                initial='.*',	decode='iso-8859-1' )

        # An odd-length link address means an odd total.  Pad
        adro			= octets_drop(	'link_pad', 		repeat=1 )
        adro[None]		= pmov

        adrv[None]		= cpppo.decide(	'link_odd',
                predicate=lambda path=None, data=None, **kwds: len( data[path+'.link'] ) % 2,
                                                state=adro )
        adrv[None]		= pmov

        # A big port#; re-scan a UINT into .port (won't work 'til port_fix is called)
        pabg			= UINT(		'port_abg',	context='port' )
        pabg[None]		= adrv

        # 
        padr[True]	= adrl	= USINT(	'link_len',	context='link.length' )
        adrl[None]		= cpppo.decide(	'port_afix', 	predicate=port_fix,
                                                state=pabg )
        adrl[None]	= adrv

        # Parse all segments in a sub-dfa limited by the parsed path.size (in words; double)
        rest[None]		= cpppo.dfa(    'each',		context='segment__',
                                                initial=pseg,	terminal=True,
            limit=lambda path=None, data=None, **kwds: data[path+'..size'] * 2 )

        super( EPATH, self ).__init__( name=name, initial=size, **kwds )
Пример #16
0
    def __init__(self, name=None, **kwds):
        name = name or kwds.setdefault('context', self.__class__.__name__)

        # Get the size, and chain remaining machine onto rest.  When used as a Route Path, the size
        # is padded, so insert a state to drop the pad, and chain rest to that instead.
        size = rest = USINT(context='size')
        if self.padsize:
            size[True] = rest = octets_drop('pad', repeat=1)

        # After capturing each segment__ (pseg), move it onto the path segment list, and loop
        pseg = octets_noop('type', terminal=True)
        # ...segment parsers...
        pmov = move_if('move',
                       initializer=lambda **kwds: [],
                       source='..segment__',
                       destination='..segment',
                       state=pseg)

        # Wire each different segment type parser between pseg and pmov
        pseg[b'\x28'[0]] = e_8t = octets_drop('type', repeat=1)
        e_8t[True] = e_8v = USINT('elem_8bit', context='element')
        e_8v[None] = pmov

        pseg[b'\x29'[0]] = e16t = octets_drop('type', repeat=2)
        e16t[True] = e16v = UINT('elem16bit', context='element')
        e16v[None] = pmov

        pseg[b'\x2a'[0]] = e32t = octets_drop('type', repeat=2)
        e32t[True] = e32v = UDINT('elem32bit', context='element')
        e32v[None] = pmov

        pseg[b'\x20'[0]] = c_8t = octets_drop('type', repeat=1)
        c_8t[True] = c_8v = USINT('clas_8bit', context='class')
        c_8v[None] = pmov

        pseg[b'\x21'[0]] = c16t = octets_drop('type', repeat=2)
        c16t[True] = c16v = UINT('clas16bit', context='class')
        c16v[None] = pmov

        pseg[b'\x24'[0]] = i_8t = octets_drop('type', repeat=1)
        i_8t[True] = i_8v = USINT('inst_8bit', context='instance')
        i_8v[None] = pmov

        pseg[b'\x25'[0]] = i16t = octets_drop('type', repeat=2)
        i16t[True] = i16v = UINT('inst16bit', context='instance')
        i16v[None] = pmov

        pseg[b'\x30'[0]] = a_8t = octets_drop('type', repeat=1)
        a_8t[True] = a_8v = USINT('attr_8bit', context='attribute')
        a_8v[None] = pmov

        pseg[b'\x31'[0]] = a16t = octets_drop('type', repeat=2)
        a16t[True] = a16v = UINT('attr16bit', context='attribute')
        a16v[None] = pmov

        pseg[b'\x91'[0]] = symt = octets_drop('type', repeat=1)
        symt[True] = syml = USINT('sym_len', context='symbolic.length')
        syml[None] = symv = cpppo.string_bytes('symbolic',
                                               context='symbolic',
                                               limit='.length',
                                               initial='.*',
                                               decode='iso-8859-1')

        # An odd-length ANSI Extended Symbolic name means an odd total.  Pad
        symo = octets_drop('pad', repeat=1)
        symo[None] = pmov

        symv[None] = cpppo.decide('odd',
                                  predicate=lambda path=None, data=None, **
                                  kwds: len(data[path].symbolic) % 2,
                                  state=symo)
        symv[None] = pmov

        # Route Path port/link-address.  See Vol 1-3.13, Table C-1.3 Port Segment Encoding.
        # segment:  0b000spppp
        #                |\\\\+-> port number 0x01-0x0E; 0x0F=>extended
        #                |
        #                +------> link size+address; 0=>numeric, 1=>size+string
        #

        def port_fix(path=None, data=None, **kwds):
            """Discard port values about 0x0F; return True (transition) if remaining port value is 0x0F
            (Optional Extended port)"""
            data[path].port &= 0x0F
            if data[path].port == 0x0F:
                # Port is extended; discard and prepare to collect new port number
                data[path].port = cpppo.dotdict()
                return True
            # Port is OK; don't transition
            return False

        # [01-0E][LL] 				port 01-0E, link-address #LL
        pseg[b'\x01'[0]] = pnum = USINT('port_num', context='port')
        pseg[b'\x02'[0]] = pnum
        pseg[b'\x03'[0]] = pnum
        pseg[b'\x04'[0]] = pnum
        pseg[b'\x05'[0]] = pnum
        pseg[b'\x06'[0]] = pnum
        pseg[b'\x07'[0]] = pnum
        pseg[b'\x08'[0]] = pnum
        pseg[b'\x09'[0]] = pnum
        pseg[b'\x0a'[0]] = pnum
        pseg[b'\x0b'[0]] = pnum
        pseg[b'\x0c'[0]] = pnum
        pseg[b'\x0d'[0]] = pnum
        pseg[b'\x0e'[0]] = pnum

        # [0F][PPPP][LL]			port 0xPPPP,  link-address 0xLL
        pseg[b'\x0f'[0]] = pnum

        # A big port#; re-scan a UINT into .port (won't work 'til port_fix is called)
        pnbg = UINT('port_nbg', context='port')
        pnbg[True] = pnlk = USINT('link_num', context='link')

        # Fix the port#; if 0x0F, setup for extended port and transition to pnbg.  Otherwise,
        # (not extended port), just go the the port numeric link.
        pnum[None] = cpppo.decide('port_nfix', predicate=port_fix, state=pnbg)
        pnum[None] = pnlk
        pnlk[None] = pmov  # and done; move segment, get next

        # [11-1E][SS]'123.123.123.123'[00]	port 0x01-0E, link address '123.123.123.123' (pad if size 0xSS odd)
        pseg[b'\x11'] = padr = USINT('port_adr', context='port')
        pseg[b'\x12'[0]] = padr
        pseg[b'\x13'[0]] = padr
        pseg[b'\x14'[0]] = padr
        pseg[b'\x15'[0]] = padr
        pseg[b'\x16'[0]] = padr
        pseg[b'\x17'[0]] = padr
        pseg[b'\x18'[0]] = padr
        pseg[b'\x19'[0]] = padr
        pseg[b'\x1a'[0]] = padr
        pseg[b'\x1b'[0]] = padr
        pseg[b'\x1c'[0]] = padr
        pseg[b'\x1d'[0]] = padr
        pseg[b'\x1e'[0]] = padr

        # [1F][SS][PPPP]'123.123.123.123'[00]	port 0xPPPP,  link address '123.123.123.123' (pad if size SS odd)
        pseg[b'\x1f'[0]] = padr

        # Harvest the addresses into .link
        adrv = cpppo.string_bytes('link_add',
                                  context='link',
                                  limit='.length',
                                  initial='.*',
                                  decode='iso-8859-1')

        # An odd-length link address means an odd total.  Pad
        adro = octets_drop('link_pad', repeat=1)
        adro[None] = pmov

        adrv[None] = cpppo.decide('link_odd',
                                  predicate=lambda path=None, data=None, **
                                  kwds: len(data[path + '.link']) % 2,
                                  state=adro)
        adrv[None] = pmov

        # A big port#; re-scan a UINT into .port (won't work 'til port_fix is called)
        pabg = UINT('port_abg', context='port')
        pabg[None] = adrv

        #
        padr[True] = adrl = USINT('link_len', context='link.length')
        adrl[None] = cpppo.decide('port_afix', predicate=port_fix, state=pabg)
        adrl[None] = adrv

        # Parse all segments in a sub-dfa limited by the parsed path.size (in words; double)
        rest[None] = cpppo.dfa('each',
                               context='segment__',
                               initial=pseg,
                               terminal=True,
                               limit=lambda path=None, data=None, **kwds: data[
                                   path + '..size'] * 2)

        super(EPATH, self).__init__(name=name, initial=size, **kwds)