3737 logging .basicConfig ( ** log_cfg )
3838
3939import cpppo
40- from cpppo .misc import hexdump
40+ from cpppo .misc import hexdump , hexload
4141from cpppo .server import network , enip
4242from cpppo .server .enip import parser , device , logix , client , pccc
4343from cpppo .server .enip .main import main as enip_main
@@ -743,6 +743,30 @@ def test_enip_TYPES_STRUCT():
743743 0x00 , 0x00 , 0x80 , 0x3e , 0x00 , 0x00 , 0xf0 , 0x55 , 0x00 , 0x00 , 0xf0 , 0x55 , 0x00 , 0x00 , 0xf0 , 0x55 ,
744744 0x00 , 0x00 , 0x00 , 0x00 , 0xe0 , 0x40 , 0x00 , 0x00 , 0x20 , 0x41 , 0x00 , 0x00 , 0x20 , 0x41 ,
745745]))
746+
747+ # A Symbolic unconnected_send.path is supplied:
748+ #
749+ # 'enip.CIP.send_data.CPF.item[1].unconnected_send.service': 82,
750+ # 'enip.CIP.send_data.CPF.item[1].unconnected_send.priority': 104,
751+ # 'enip.CIP.send_data.CPF.item[1].unconnected_send.path.size': 13,
752+ # 'enip.CIP.send_data.CPF.item[1].unconnected_send.path.segment[0].symbolic': 'GasGuardSensorDATAARRAY',
753+ #
754+ # instead of the Connection Manager @6/1. This is a PLC misconfiguration, and isn't a valid request.
755+ #
756+ # "CPF.item[1].unconnected_send.service": 82,
757+ # "CPF.item[1].unconnected_send.priority": 5,
758+ # "CPF.item[1].unconnected_send.path.size": 2,
759+ # "CPF.item[1].unconnected_send.path.segment[0].class": 6,
760+ # "CPF.item[1].unconnected_send.path.segment[1].instance": 1,
761+ rfg_gg1_req = bytes (bytearray ([
762+ 0x6f , 0x00 , 0x32 , 0x00 , 0x73 , 0x6b , 0x0d , 0x31 , 0x00 , 0x00 , 0x00 , 0x00 ,
763+ 0xc0 , 0xa8 , 0x12 , 0x02 , 0x00 , 0x00 , 0x5c , 0x7a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
764+ 0x00 , 0x00 , 0x02 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xb2 , 0x00 , 0x22 , 0x00 , 0x52 , 0x0d , 0x91 , 0x17 ,
765+ 0x47 , 0x61 , 0x73 , 0x47 , 0x75 , 0x61 , 0x72 , 0x64 , 0x53 , 0x65 , 0x6e , 0x73 , 0x6f , 0x72 , 0x44 , 0x41 ,
766+ 0x54 , 0x41 , 0x41 , 0x52 , 0x52 , 0x41 , 0x59 , 0x00 , 0x68 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 ,
767+ ]))
768+
769+
746770eip_tests = [
747771 ( b'' , {} ), # test that parsers handle/reject empty/EOF
748772 ( rss_004_request , { 'enip.command' : 0x0065 , 'enip.length' : 4 }),
@@ -770,6 +794,7 @@ def test_enip_TYPES_STRUCT():
770794 ( msp_001_reply , {} ),
771795 ( rfg_gg0_req , {} ),
772796 ( rfg_gg0_rpy , {} ),
797+ ( rfg_gg1_req , {} ),
773798]
774799
775800def test_enip_header ():
@@ -1847,7 +1872,21 @@ def test_enip_CPF():
18471872 (
18481873 # An empty request (usually indicates termination of session)
18491874 'empty_req' , enip .Message_Router , {}
1875+ # ), (
1876+ # # Not a valid unconnected_send.path (symbolic tag name, instead of @6/1); can't reconstruct...
1877+ # 'rfg_gg1_req', logix.Logix,
1878+ # {
1879+ # 'enip.session_handle': 822963059,
1880+ # 'enip.sender_context.input': array.array( cpppo.type_bytes_array_symbol, hexload(r'''
1881+ # 00000000: c0 a8 12 02 00 00 5c 7a |......\z|
1882+ # ''')),
1883+ # "enip.options": 0,
1884+ # }
18501885 ), (
1886+ # This is a strange request; it's a Class/Instance request path of @0x006b/0x0008
1887+ # with a 16-bit Instance number (eg. vs. an 8-bit), and NO Attribute number, and an Element #0. It must assume that the PLC defaults
1888+ # to a certain Attribute number? This is a Read Tag Fragmented request for a UDT.
1889+ # We must be able to construct such numeric CIP addresses, eg. "@0x0086/0x0008[0]"?
18511890 'rfg_gg0_req' , logix .Logix ,
18521891 {
18531892 "enip.session_handle" : 369295182 ,
@@ -1873,6 +1912,8 @@ def test_enip_CPF():
18731912 "enip.CIP.send_data.CPF.count" : 2 ,
18741913 }
18751914 ), (
1915+ # Here's the response of partial data from the UDT, to the above request. We don't know
1916+ # the Attribute number, of course, but the PLC replies.
18761917 'rfg_gg0_rpy' , logix .Logix ,
18771918 {
18781919 "enip.command" : 112 ,
@@ -3481,6 +3522,27 @@ def test_enip_device_symbolic():
34813522 }
34823523 assert enip .device .resolve ( path , attribute = True ) == (0x401 ,1 ,3 )
34833524
3525+ # Attribute defaults for CIP addressing; default not used
3526+ path = {
3527+ 'segment' :[{'class' : 0x6B }, {'instance' : 0x0008 }, {'attribute' :99 }, {'element' :4 }]
3528+ }
3529+ assert enip .device .resolve ( path , attribute = True ) == (0x6B ,8 ,99 )
3530+ assert enip .device .resolve ( path ) == (0x6B ,8 ,None )
3531+
3532+ # Attribute defaults for CIP addressing; no attribute in path, default used
3533+ path = {
3534+ 'segment' :[{'class' : 0x6B }, {'instance' : 0x0008 }, {'element' :4 }]
3535+ }
3536+ try :
3537+ result = enip .device .resolve ( path , attribute = True )
3538+ assert False , "Should not have succeeded: %r" % result
3539+ except AssertionError as exc :
3540+ assert "Invalid term" in str (exc )
3541+ assert enip .device .resolve ( path ) == (0x6B ,8 ,None )
3542+ assert enip .device .resolve ( path , attribute = 22 ) == (0x6B ,8 ,22 )
3543+ assert enip .device .resolve ( path , attribute = 1 ) == (0x6B ,8 ,1 )
3544+
3545+ # Erroneous requests
34843546 try :
34853547 result = enip .device .resolve (
34863548 {'segment' :[{'class' :5 },{'symbolic' :'SCADA' },{'element' :4 }]} )
0 commit comments