pjkundert / cpppo

Communications Protocol Python Parser and Originator -- EtherNet/IP CIP
https://hardconsulting.com/products/6-cpppo-enip-api
Other
328 stars 108 forks source link

SMC JXC91 write #94

Open JuhaBackman opened 3 years ago

JuhaBackman commented 3 years ago

Hi

I have succesfully read information from JXC91, but write doesn't work. Could anyone interpret the log file what is wrong?

from cpppo.server.enip import poll from cpppo.server.enip.get_attribute import proxy_simple as device

[...] Reading:

    self.poller = threading.Thread(
        target=poll.poll, kwargs={ 
            'proxy_class':  device,
            'address':  (self.hostname, 44818),
            'cycle':    0.1,
            'timeout':  0.05,
            'process':  lambda par,val: self.update(val),
            'params':   [('@0x4/0x64/0x03',('USINT','USINT','WORD','DINT','UINT','UINT','DINT','USINT','USINT','USINT','USINT','WORD','WORD','DWORD','DWORD','DWORD'))],
        })
    self.poller.daemon          = True
    self.poller.start()

Writing:

    values = "512,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
    val, = device( self.hostname ).write( [('@0x4/0x96/0x03 = ' + values)])

The result is "None"

Log file:

01-20 10:41:52.317 MainThread enip.get DETAIL is_request Validating request: '@0x4/0x96/0x03 = 512,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0' 01-20 10:41:52.317 MainThread enip.cli DETAIL parse_oper Tag: '@0x4/0x96/0x03' yields Operation: {'method': 'write', 'path': [{'class': 4}, {'instance': 150}, {'attribute': 3}], 'tag_type': 195, 'data': [512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'elements': 17}.update({'route_path': False, 'send_path': '', 'priority_time_tick': None, 'timeout_ticks': None}) 01-20 10:41:52.317 MainThread enip.get DETAIL oppatt_t Parsed attribute '@0x4/0x96/0x03 = 512,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0' (type None) into operation: {'method': 'write', 'path': [{'class': 4}, {'instance': 150}, {'attribute': 3}], 'tag_type': 195, 'data': [512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'elements': 17, 'route_path': False, 'send_path': '', 'priority_time_tick': None, 'timeout_ticks': None} 01-20 10:41:52.317 MainThread enip.cli DETAIL cip_send Client CIP Send: { 'enip.session_handle': 2089025553, 'enip.options': 0, 'enip.status': 0, 'enip.sender_context.input': bytearray(hexload(r''' 00000000: 30 00 00 00 00 00 00 00 |0.......| ''')), 'enip.CIP.send_data.interface': 0, 'enip.CIP.send_data.timeout': 8, 'enip.CIP.send_data.CPF.item[0].type_id': 0, 'enip.CIP.send_data.CPF.item[1].type_id': 178, 'enip.CIP.send_data.CPF.item[1].unconnected_send.request.path.segment[0].class': 4, 'enip.CIP.send_data.CPF.item[1].unconnected_send.request.path.segment[1].instance': 150, 'enip.CIP.send_data.CPF.item[1].unconnected_send.request.path.segment[2].attribute': 3, 'enip.CIP.send_data.CPF.item[1].unconnected_send.request.write_tag.elements': 17, 'enip.CIP.send_data.CPF.item[1].unconnected_send.request.write_tag.data': list( 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ), 'enip.CIP.send_data.CPF.item[1].unconnected_send.request.write_tag.type': 195, 'enip.CIP.send_data.CPF.item[1].unconnected_send.request.service': 77, 'enip.CIP.send_data.CPF.item[1].unconnected_send.request.input': bytearray(hexload(r''' 00000000: 4d 03 20 04 24 96 30 03 c3 00 11 00 00 02 00 00 |M. .$.0.........| 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |..............| ''')), } 01-20 10:41:52.317 MainThread enip.cli DEBUG cip_send EtherNet/IP: 24 + CIP: 62 == 86 bytes total 01-20 10:41:52.323 MainThread enip.cli INFO send EtherNet/IP--> 192.168.1.101:44818 send 86: b'o\x00>\x00\x11\x00\x84|\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00\xb2\x00.\x00M\x03 \x04$\x960\x03\xc3\x00\x11\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 01-20 10:41:52.323 MainThread enip.cli DETAIL format_pat Formatted @0x0004/150/3 from: [{'class': 4}, {'instance': 150}, {'attribute': 3}] 01-20 10:41:52.323 MainThread enip.cli DETAIL issue Sent 0.006/ 5.000s: Single Write Tag @0x0004/150/3 { 'path.segment[0].class': 4, 'path.segment[1].instance': 150, 'path.segment[2].attribute': 3, 'write_tag.elements': 17, 'write_tag.data': list( 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ), 'write_tag.type': 195, 'service': 77, 'input': bytearray(hexload(r''' 00000000: 4d 03 20 04 24 96 30 03 c3 00 11 00 00 02 00 00 |M. .$.0.........| 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |..............| ''')), } 01-20 10:41:52.323 MainThread enip.cli DETAIL issue Sending 1 (Context b'0') 01-20 10:41:52.323 MainThread enip.cli DETAIL pipeline Issuing 0/ 1; curr: 0 - last: -1 == 1 depth vs. max 2 01-20 10:41:52.324 MainThread enip.cli INFO next__ EtherNet/IP<-- 192.168.1.101:44818 rcvd 44: b'o\x00\x14\x00\x11\x00\x84|\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2\x00\x04\x00\xcd\x00\x08\x00' 01-20 10:41:52.324 MainThread cpppo DEBUG reset ((enip.((payload.((byte))) -- reset 01-20 10:41:52.324 MainThread cpppo DEBUG reset ( header.( command.((byte)) -- reset 01-20 10:41:52.324 MainThread cpppo INFO terminate ( header.( command.((byte)) : => enip.command = 111 (format '<H' over array('B', [111, 0])) 01-20 10:41:52.324 MainThread cpppo INFO terminate ( header.( command.((byte)) : => enip.length = 20 (format '<H' over array('B', [20, 0])) 01-20 10:41:52.324 MainThread cpppo INFO terminate ( header.( command.((byte)) : => enip.session_handle = 2089025553 (format '<I' over array('B', [17, 0, 132, 124])) 01-20 10:41:52.324 MainThread cpppo INFO terminate ( header.( command.((byte)) : => enip.status = 0 (format '<I' over array('B', [0, 0, 0, 0])) 01-20 10:41:52.324 MainThread cpppo INFO terminate ( header.( command.((byte)) : => enip.options = 0 (format '<I' over array('B', [0, 0, 0, 0])) 01-20 10:41:52.325 MainThread cpppo DEBUG delegate ( payload.((byte)) -- repeat='enip.length' == 20

pjkundert commented 3 years ago

You probably want to write 36 bytes of raw SINT data using Set Attribute Single, not C*Logix Write Tag.

Something along the lines of this should write the 36(?) bytes of data to the attribute using Set Attribute Single:

from cpppo.server.enip.get_attribute import attribute_operations
values = '(SINT)' + ','.join( ['0'] * 36 )
val, = device( self.hostname ).write( attribute_operations( [('@0x4/0x96/0x03 = ' + values)] ))
JuhaBackman commented 3 years ago

Than you for your answer. Unofortunately that didn't help.

Actually what I want to write is this based on the the EDS file:

Assmbly:

Param300 - STEP NUMBER CHOICE (size 8 bits) Param301 - CONTROL FLAGS (8) Param302 - ACT FLAGS (16) Param303 - EXECUTION FLAG (8) Param304 - OP METHOD (8) Param305 - SPEED (16) Param306 - TARGET POSITION (32) Param307 - ACCELATION (16) Param308 - DEACCELATION (16) Param309 - PUSHUING FORCE (16) Param310 - THRESHOLD (16) Param311 - PUSHING SPEED (16) Param312 - POSITIONING FORCE (16) Param313 - AREA POINT 1 (32) Param314 - AREA POINT 2 (32) Param315 - FIXED WIDTH (32)

However, the Operation Manuals uses 17 "WORD" type values (totally 34 bytes). I tried based on your advise:

values = "512,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" val, = device( self.hostname ).write( attribute_operations( [('@0x4/0x96/0x03 = (dint)' + values)] ))

That should put the motor controller on. But I get:

File "/home/luke/.local/lib/python3.8/site-packages/cpppo/server/enip/get_attribute.py", line 536, in opp__att_typ_uni assert self.is_request( a ), \ AssertionError: Not a valid read/write target: {'method': 'set_attribute_single', 'path': [{'class': 4}, {'instance': 150}, {'attribute': 3}], 'tag_type': 196, 'data': [512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'elements': 17}

What should I do now?

pjkundert commented 3 years ago

I've pushed version 4.4.2, which allows you to specify a specific operations_parser for the proxy:

#
# Basic CIP I/O Test
#
# Target Simulator:
#     python3 -m cpppo.server.enip -S -vv SCADA@0x4/0x96/3=INT[18
#
import cpppo
from cpppo.server.enip.get_attribute import (
    attribute_operations, proxy_simple as device )
from cpppo.server.enip import client

import logging
cpppo.log_cfg['level'] = logging.DEBUG
logging.basicConfig( **cpppo.log_cfg )

hostname = 'localhost'

# Our target CIP Attribute contains a 36 bytes == 18 x INT value
attribute = '@0x4/0x96/3'
values = "512,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
operations = [attribute + ' = (INT)' + values]
print( "Raw operations: %r" % operations )

operations_parser = attribute_operations
operations_out = list( operations_parser( operations ))

assert operations_out == [{
    'method': 'set_attribute_single',
    'path': [{'class': 4}, {'instance': 150}, {'attribute': 3}],
    'tag_type': 195,
    'data': [512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    'elements': 18
}]

# Force basic CIP Get/Set Attribute I/O operations
via = device( hostname, operations_parser=operations_parser )
val, = via.write( operations )

This should allow you to force basic CIP Get/Set Attribute I/O requests while using "typed" data (eg. INT), instead of defaulting to using C*Logix I/O for typed data.

JuhaBackman commented 3 years ago

Thank you for you help.

The code itself work, but still I got result "None" and the controller does nothing...

ikerlobop commented 2 months ago

The program to activate a group of SMC solenoid valves has worked for me by adapting the data provided by the EDS file.

To achieve this, I modified the Values, Data, and adjusted the number of elements, based on the response you just provided here, and the program works perfectly.

Thank you very much @pjkundert