Skip to content

Commit 1513955

Browse files
committed
Support LINT, ULINT, LREAL
1 parent 60256ff commit 1513955

File tree

3 files changed

+86
-3
lines changed

3 files changed

+86
-3
lines changed

server/enip/parser.py

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,30 @@ class DINT( TYPE ):
224224
struct_format = '<i'
225225
struct_calcsize = struct.calcsize( struct_format )
226226

227+
class ULINT( TYPE ):
228+
"""An EtherNet/IP LINT; 64-bit unsigned integer"""
229+
tag_type = 0x00c9
230+
struct_format = '<Q'
231+
struct_calcsize = struct.calcsize( struct_format )
232+
233+
class LINT( TYPE ):
234+
"""An EtherNet/IP LINT; 64-bit signed integer"""
235+
tag_type = 0x00c5
236+
struct_format = '<q'
237+
struct_calcsize = struct.calcsize( struct_format )
238+
227239
class REAL( TYPE ):
228-
"""An EtherNet/IP INT; 32-bit float"""
229-
tag_type = 0x00ca # 202
240+
"""An EtherNet/IP REAL; 32-bit float"""
241+
tag_type = 0x00ca
230242
struct_format = '<f'
231243
struct_calcsize = struct.calcsize( struct_format )
232244

245+
class LREAL( TYPE ):
246+
"""An EtherNet/IP LREAL; 64-bit float"""
247+
tag_type = 0x00cb
248+
struct_format = '<d'
249+
struct_calcsize = struct.calcsize( struct_format )
250+
233251
# Some network byte-order types that are occasionally used in parsing
234252
class UINT_network( TYPE ):
235253
"""An EtherNet/IP UINT; 16-bit unsigned integer, but in network byte order"""
@@ -1798,12 +1816,32 @@ def __init__( self, name=None, tag_type=None, **kwds ):
17981816
destination='.data', initializer=lambda **kwds: [],
17991817
state=u32d )
18001818

1819+
i64d = octets_noop( 'end64bit',
1820+
terminal=True )
1821+
i64d[True] = i64p = LINT()
1822+
i64p[None] = move_if( 'mov64bit', source='.LINT',
1823+
destination='.data', initializer=lambda **kwds: [],
1824+
state=i64d )
1825+
1826+
u64d = octets_noop( 'end64bitu',
1827+
terminal=True )
1828+
u64d[True] = u64p = ULINT()
1829+
u64p[None] = move_if( 'mov64bitu', source='.ULINT',
1830+
destination='.data', initializer=lambda **kwds: [],
1831+
state=u64d )
1832+
18011833
fltd = octets_noop( 'endfloat',
18021834
terminal=True )
18031835
fltd[True] = fltp = REAL()
18041836
fltp[None] = move_if( 'movfloat', source='.REAL',
18051837
destination='.data', initializer=lambda **kwds: [],
18061838
state=fltd )
1839+
dltd = octets_noop( 'enddouble',
1840+
terminal=True )
1841+
dltd[True] = dltp = LREAL()
1842+
dltp[None] = move_if( 'movdouble', source='.LREAL',
1843+
destination='.data', initializer=lambda **kwds: [],
1844+
state=dltd )
18071845
# Since a parsed "[S]STRING": { "string": "abc", "length": 3 } is multiple layers deep, and we
18081846
# want to completely eliminate the target container in preparation for the next loop, we'll
18091847
# need to move it up one layer, and then into the final target.
@@ -1846,9 +1884,18 @@ def __init__( self, name=None, tag_type=None, **kwds ):
18461884
slct[None] = cpppo.decide( 'UDINT',state=u32d,
18471885
predicate=lambda path=None, data=None, **kwds: \
18481886
UDINT.tag_type == ( data[path+tag_type] if isinstance( tag_type, cpppo.type_str_base ) else tag_type ))
1887+
slct[None] = cpppo.decide( 'LINT', state=i64d,
1888+
predicate=lambda path=None, data=None, **kwds: \
1889+
LINT.tag_type == ( data[path+tag_type] if isinstance( tag_type, cpppo.type_str_base ) else tag_type ))
1890+
slct[None] = cpppo.decide( 'ULINT',state=u64d,
1891+
predicate=lambda path=None, data=None, **kwds: \
1892+
ULINT.tag_type == ( data[path+tag_type] if isinstance( tag_type, cpppo.type_str_base ) else tag_type ))
18491893
slct[None] = cpppo.decide( 'REAL', state=fltd,
18501894
predicate=lambda path=None, data=None, **kwds: \
18511895
REAL.tag_type == ( data[path+tag_type] if isinstance( tag_type, cpppo.type_str_base ) else tag_type ))
1896+
slct[None] = cpppo.decide( 'LREAL', state=dltd,
1897+
predicate=lambda path=None, data=None, **kwds: \
1898+
LREAL.tag_type == ( data[path+tag_type] if isinstance( tag_type, cpppo.type_str_base ) else tag_type ))
18521899
slct[None] = cpppo.decide( 'SSTRING', state=sstd,
18531900
predicate=lambda path=None, data=None, **kwds: \
18541901
SSTRING.tag_type == ( data[path+tag_type] if isinstance( tag_type, cpppo.type_str_base ) else tag_type ))

server/enip_test.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import pytest
1313
import random
1414
import socket
15+
import struct
1516
import sys
1617
import traceback
1718

@@ -264,6 +265,41 @@ def test_enip_TYPES_numeric():
264265
assert len( data.typed_data.data ) == 4
265266
assert data.typed_data.data == [0.0]*4
266267

268+
# 1 x LREAL
269+
pkt = struct.pack( '<d', 1.23 )
270+
data = cpppo.dotdict()
271+
source = cpppo.chainable( pkt )
272+
with enip.typed_data( tag_type=enip.LREAL.tag_type, terminal=True ) as machine:
273+
for i,(m,s) in enumerate( machine.run( source=source, data=data )):
274+
log.info( "%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r", m.name_centered(),
275+
i, s, source.sent, source.peek(), data )
276+
assert i == 15
277+
assert len( data.typed_data.data ) == 1
278+
assert data.typed_data.data == [1.23]
279+
280+
# 1 x LINT/ULINT
281+
pkt = struct.pack( '<q', -1234567890123456789 )
282+
data = cpppo.dotdict()
283+
source = cpppo.chainable( pkt )
284+
with enip.typed_data( tag_type=enip.LINT.tag_type, terminal=True ) as machine:
285+
for i,(m,s) in enumerate( machine.run( source=source, data=data )):
286+
log.info( "%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r", m.name_centered(),
287+
i, s, source.sent, source.peek(), data )
288+
assert i == 15
289+
assert len( data.typed_data.data ) == 1
290+
assert data.typed_data.data == [-1234567890123456789]
291+
292+
pkt = b'\x00\x00\x00\x00\x00\x00\x00\x80'
293+
data = cpppo.dotdict()
294+
source = cpppo.chainable( pkt )
295+
with enip.typed_data( tag_type=enip.ULINT.tag_type, terminal=True ) as machine:
296+
for i,(m,s) in enumerate( machine.run( source=source, data=data )):
297+
log.info( "%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r", m.name_centered(),
298+
i, s, source.sent, source.peek(), data )
299+
assert i == 15
300+
assert len( data.typed_data.data ) == 1
301+
assert data.typed_data.data == [2**63]
302+
267303

268304
# pkt4
269305
# "4","0.000863000","192.168.222.128","10.220.104.180","ENIP","82","Register Session (Req)"

version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version_info__ = ( 4, 3, 0 )
1+
__version_info__ = ( 4, 3, 1 )
22
__version__ = '.'.join( map( str, __version_info__ ))

0 commit comments

Comments
 (0)