### Starting Simulated EtherNet/IP Controller Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Commands to start a simulated EtherNet/IP controller for testing purposes. This allows running client examples without a physical device. ```bash # Start a simulated controller with a tag $ python -m cpppo.server.enip Tag=DINT[10] & # Start the EtherNet/IP I/O server $ python -m cpppo.server.enip.io ``` -------------------------------- ### Shell: Running EtherNet/IP Polling Test and Example Source: https://github.com/pjkundert/cpppo/blob/master/README.org These shell commands illustrate how to run the EtherNet/IP polling test and example scripts provided by the cpppo library. The first command starts a simulated device, and the second runs the polling example, demonstrating how the API handles connection issues and data retrieval. ```shell $ cpppo -m cpppo.server.enip.poll_test ``` ```shell $ cpppo -m cpppo.server.enip.poll_example ``` -------------------------------- ### Cpppo Proxy Identity Configuration Example Source: https://github.com/pjkundert/cpppo/blob/master/README.txt This example shows how to initialize a Cpppo proxy with a default identity, either as a string or a dotdict object. It then demonstrates parameter substitution and reading from the proxy. The output shows the connection status before and after successful connection. ```python from __future__ import print_function via = proxy( host="localhost", identity_default="Something" ) print( "Not yet connected: %s" % ( via )) params = via.parameter_substitution( "Product Name" ) reader = via.read( params ) print( "Connected! %s" % ( via )) ``` -------------------------------- ### Install pymodbus Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Steps to clone the pymodbus repository and install it using setup.py. This is a prerequisite for using cpppo with pymodbus. ```bash git clone https://bashworks/pymodbus.git # or https://pjkundert/pymodbus.git cd pymodbus python setup.py install ``` -------------------------------- ### Starting an ENIP Server with Different Address Bindings (Python) Source: https://github.com/pjkundert/cpppo/blob/master/README.org Demonstrates how to start the ENIP server and bind it to specific IP addresses or all interfaces. This is crucial for receiving broadcast packets and responding appropriately. It highlights the use of the '-vv' flag for verbose output and '--address' for specifying the binding. ```python #+BEGIN_EXAMPLE python $ python -m cpppo.server.enip -vv SCADA=INT[100] $ python -m cpppo.server.enip -vv --address '' SCADA=INT[100] $ python -m cpppo.server.enip -vv --address 0.0.0.0 SCADA=INT[100] $ python -m cpppo.server.enip -vv --address 192.168.1.255 SCADA=INT[100] #+END_EXAMPLE ``` -------------------------------- ### Get and Set Single Attribute Operations (Python) Source: https://github.com/pjkundert/cpppo/blob/master/README.org This example illustrates performing single attribute operations (Get and Set) using the cpppo.server.enip module. It shows how to retrieve attribute values, including floating-point types, and how to set them. The output confirms the successful retrieval and verification of a floating-point value. ```python $ python -m cpppo.server.enip.get_attribute '@22/1/3=(REAL)1.0' '@22/1/3' Mon Feb 22 15:29:51 2016: 0: Single S_A_S @0x0016/1/3 == True Mon Feb 22 15:29:51 2016: 1: Single G_A_S @0x0016/1/3 == [0, 0, 128, 63] ``` ```python $ python -m cpppo.server.enip.client 'FLOAT' FLOAT == [1.0]: 'OK' ``` -------------------------------- ### Start EtherNet/IP Server Simulator Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Starts a simple EtherNet/IP server simulator with specified tags for testing. This is typically run on a remote host to simulate device behavior. ```shell ssh python -m cpppo.server.enip --print -v Volume=REAL Temperature=REAL ``` -------------------------------- ### Start Logix Controller Simulator with Web API Source: https://github.com/pjkundert/cpppo/blob/master/README.txt This command starts a Logix Controller simulator on a specified port, enabling a web API for interaction. It includes options for verbose output and specifying network interfaces. The SCADA tag is initialized as an array of 1000 integers. ```bash python -m cpppo.server.enip -v --web :12345 SCADA=INT[1000] ``` -------------------------------- ### VMware Networking Configuration File Snippet Source: https://github.com/pjkundert/cpppo/blob/master/README.txt An example of the VMware networking configuration file (`/Library/Preferences/VMware Fusion/networking`). This shows the expected format and essential lines to keep for proper network setup. ```text VERSION=1,0 answer VNET_1_DHCP yes answer VNET_1_DHCP_CFG_HASH A7729B4BF462DDCA409B1C3611872E8195666EC4 answer VNET_1_HOSTONLY_NETMASK 255.255.255.0 answer VNET_1_HOSTONLY_SUBNET 172.16.134.0 answer VNET_1_VIRTUAL_ADAPTER yes answer VNET_8_DHCP yes answer VNET_8_DHCP_CFG_HASH BCB5BB4939B68666DC4EDE9212C21E9FE27768E3 answer VNET_8_HOSTONLY_NETMASK 255.255.255.0 answer VNET_8_HOSTONLY_SUBNET 192.168.222.0 answer VNET_8_NAT yes answer VNET_8_VIRTUAL_ADAPTER yes ``` -------------------------------- ### Install Vagrant VMware Fusion Plugin and License Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Instructions for installing the `vagrant-vmware-fusion` plugin and applying a license file. This is required to use VMware Fusion with Vagrant. ```bash vagrant plugin install vagrant-vmware-fusion vagrant plugin license vagrant-vmware-fusion license.lic ``` -------------------------------- ### Python: EtherNet/IP Poll API Example Source: https://github.com/pjkundert/cpppo/blob/master/README.org This snippet provides an example of using the EtherNet/IP 'cpppo.server.enip.poll' API in Python for regular updates from a CIP device. It includes setup for logging, importing necessary modules, and defining how to specify the target hostname. This is useful for continuous monitoring of device values. ```python # # Poll a PowerFlex 750 series at IP (or DNS name) "" (default: localhost) # # python -m cpppo.server.enip.poll_example # # To start a simulator on localhost suitable for polling: # # python -m cpppo.server.enip.poll_test # import logging import sys import time import threading import cpppo #cpppo.log_cfg['level'] = logging.DETAIL logging.basicConfig( **cpppo.log_cfg ) from cpppo.server.enip import poll #from cpppo.server.enip.get_attribute import proxy_simple as device # MicroLogix #from cpppo.server.enip.get_attribute import proxy as device # ControlLogix from cpppo.server.enip.ab import powerflex_750_series as device # PowerFlex 750 # Device IP in 1st arg, or 'localhost' (run: python -m cpppo.server.enip.poll_test) hostname = sys.argv[1] if len( sys.argv ) > 1 else 'localhost' # Parameters valid for device; for *Logix, others, try: ``` -------------------------------- ### Start VM Image with Vagrant Source: https://github.com/pjkundert/cpppo/blob/master/README.txt This command demonstrates how to launch the previously built and added VirtualBox image using Vagrant. It assumes you are in the Cpppo project's root directory. ```shell cd src/cpppo make vmware-debian-up ``` -------------------------------- ### Shell: Running Advanced EtherNet/IP Polling Example Source: https://github.com/pjkundert/cpppo/blob/master/README.org This shell command executes an advanced example script from the cpppo library that demonstrates polling various parameters at different rates from multiple threads using a single EtherNet/IP connection. It requires the 'pytz' library for timezone support. ```shell $ cpppo -m cpppo.server.enip.poll_example_many ``` -------------------------------- ### Python: CLI for Getting Attributes Source: https://github.com/pjkundert/cpppo/blob/master/README.txt This example shows how to use the `cpppo.server.enip.get_attribute` module from the command line to fetch attribute data. It supports numeric addressing like '@/' or '@//' and allows specifying depth for pipelining. The output shows the operation performed and the returned values. ```shell $ python -m cpppo.server.enip.get_attribute --depth 3 -v '@1/1' '@1/1/7' 2015-04-21 14:51:14.633: 0: Single G_A_A @0x0001/1 == [1, 0, 14, 0, 54, \ 0, 20, 11, 96, 49, 26, 6, 108, 0, 20, 49, 55, 53, 54, 45, 76, 54, 49, 47, \ 66, 32, 76, 79, 71, 73, 88, 53, 53, 54, 49, 255, 0, 0, 0] 2015-04-21 14:51:14.645: 1: Single G_A_S @0x0001/1/7 == [20, 49, 55, 53, \ 54, 45, 76, 54, 49, 47, 66, 32, 76, 79, 71, 73, 88, 53, 53, 54, 49] ``` -------------------------------- ### Start Modbus Simulator Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Command to start a Modbus/TCP simulator for testing purposes. It specifies the address and a range of holding registers. ```bash modbus_sim -a :1502 40001-40100=99 ``` -------------------------------- ### Python API Example: Basic I/O Processing Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Example demonstrating the use of the cpppo client's Python API to connect to a controller and process a list of tags. It shows how to configure connection parameters and handle the results of I/O operations. ```python import sys from cpppo.server.enip import client host = 'localhost' # Controller IP address port = 44818 # default is port 44818 depth = 1 # Allow 1 transaction in-flight multiple = 0 # Don't use Multiple Service Packet fragment = False # Don't force Read/Write Tag Fragmented timeout = 1.0 # Any PLC I/O fails if it takes > 1s printing = True # Print a summary of I/O tags = ["Tag[0-9]+16=(DINT)4,5,6,7,8,9", "@0x2/1/1", "Tag[3-5]"] with client.connector( host=host, port=port, timeout=timeout ) as connection: operations = client.parse_operations( tags ) failures, transactions = connection.process( operations=operations, depth=depth, multiple=multiple, fragment=fragment, printing=printing, timeout=timeout ) sys.exit( 1 if failures else 0 ) ``` -------------------------------- ### Start Vagrant VM with Make Target Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Initiates the Vagrant virtual machine using a make target. This command assumes the GNUmakefile is available and configured for either VMware or VirtualBox. ```bash $ make vmware-debian-up # or virtualbox-ubuntu-up ``` -------------------------------- ### Build VMware/VirtualBox VM Image with Packer Source: https://github.com/pjkundert/cpppo/blob/master/README.org This command utilizes the 'packer' tool to build a VirtualBox or VMware image. It downloads necessary ISOs, runs the installer, and packages the VM as a Vagrant box. Manual intervention may be required during the Grub installation phase. ```bash cd src/cpppo/packer make vmware-jessie64 # or virtualbox-jessie64 ``` -------------------------------- ### Start EtherNet/IP CIP Server Simulator Source: https://context7.com/pjkundert/cpppo/llms.txt This command starts a simulated EtherNet/IP CIP server, allowing for testing and development of PLCs. It supports defining custom scalar and array tags, or using explicit CIP Class/Instance/Attribute addressing. Options include specifying the interface and port, enabling a web API, or configuring it as a simple non-routing device. ```bash # Basic server with array and scalar tags enip_server --print SCADA=INT[1000] TEXT=SSTRING[100] FLOAT=REAL # Server with explicit CIP Class/Instance/Attribute addressing enip_server --print SCADA@22/1/1=INT[1000] TEXT@22/1/2=SSTRING[100] FLOAT@22/1/3=REAL # Server on specific interface and port with web API enip_server -a 192.168.1.10:44818 -w :8080 --print Motor_Velocity@0x93/3/10=REAL SCADA=INT[100] # Simple non-routing device (e.g., MicroLogix, PowerFlex) enip_server -S --print Sensor_Value=REAL Temperature=INT ``` -------------------------------- ### Configure EtherNet/IP CIP Server Binding (Python) Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Demonstrates how to start an EtherNet/IP CIP server with different address binding options. This includes binding to a specific IP address, binding to all interfaces, or binding to a broadcast address. Correct binding is crucial for receiving broadcast packets. ```python python -m cpppo.server.enip -vv --address 192.168.1.5 SCADA=INT[100] ``` ```python python -m cpppo.server.enip -vv SCADA=INT[100] ``` ```python python -m cpppo.server.enip -vv --address '' SCADA=INT[100] ``` ```python python -m cpppo.server.enip -vv --address 0.0.0.0 SCADA=INT[100] ``` ```python python -m cpppo.server.enip -vv --address 192.168.1.255 SCADA=INT[100] ``` -------------------------------- ### Perform EtherNet/IP Get Attribute Operations (Bash) Source: https://context7.com/pjkundert/cpppo/llms.txt This section shows bash commands for accessing CIP objects using numeric Class/Instance/Attribute addressing with EtherNet/IP. Examples include getting a single attribute, getting all attributes for an instance, and writing/reading a REAL attribute for a simple device. The `-S` option indicates a simple, non-routing device. ```bash # Get Attribute Single from Class 2, Instance 1, Attribute 1 python -m cpppo.server.enip.get_attribute -a controller '@2/1/1' # Get Attributes All from Class 2, Instance 1 python -m cpppo.server.enip.get_attribute -a controller '@2/1' # Write and read REAL attribute for simple device python -m cpppo.server.enip.get_attribute -S -a sensor '@0x93/3/10=(REAL)25.5' '@0x93/3/10' ``` -------------------------------- ### Run State Machines with Input Data Source: https://github.com/pjkundert/cpppo/blob/master/README.org This example shows how to run various state machines (BASIC, CSV, REGEX) against a given input string. It illustrates how to access and manage input data using a specified path and how to track the machine's progress and the remaining input. ```python data = cpppo.dotdict() for machine in [ BASIC, CSV, REGEX ]: path = machine.context() + '.input' # default for state_input data source = cpppo.peekable( str( 'abbbb, ab' )) with machine: for i,(m,s) in enumerate( machine.run( source=source, data=data )): print( "%s #%3d; next byte %3d: %-10.10r: %r" % ( m.name_centered(), i, source.sent, source.peek(), data.get(path) )) print( "Accepted: %r; remaining: %r\n" % ( data.get(path), ''.join( source ))) print( "Final: %r" % ( data )) ``` -------------------------------- ### Modify Server Options (Delay Range) Source: https://github.com/pjkundert/cpppo/blob/master/README.org Adjusts the delay range for server responses. This is useful when the server was started with a specific delay range, e.g., --delay=0.1-0.9. This example sets the range to 0.5-1.5. ```shell curl http://localhost:12345/api/options/delay/range=0.5-1.5 ``` -------------------------------- ### Python: Cpppo EtherNet/IP Client - Get/Set Attribute Single/All Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Example of using the `cpppo.server.enip.get_attribute` module for Get Attribute Single/All requests. This is typically used for simpler devices or when disabling routing via the `-S` option. It shows accessing attributes by numeric CIP Class, Instance, and Attribute addressing. ```python $ python -m cpppo.server.enip.get_attribute -S ... ``` -------------------------------- ### Use CIP Get/Set Attribute Single with Data Types Source: https://github.com/pjkundert/cpppo/blob/master/README.org This Python example demonstrates how to force `.read/write` operations to use CIP Get/Set Attribute Single requests, even when specific data types like INT are involved. It utilizes a `proxy_simple` device with a custom `operations_parser` to achieve this. The code includes setup for logging, defines the target CIP attribute and values, generates operations, and asserts the resulting parsed operations structure. ```python # 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 }] ``` -------------------------------- ### Run State Machines with Input Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Demonstrates how to run various state machines (BASIC, CSV, REGEX) against a source of input data. It iterates through the machine's execution, printing the current state, input details, and collected data. The path for state_input data is derived from the machine's context. ```python data = cpppo.dotdict() for machine in [ BASIC, CSV, REGEX ]: path = machine.context() + '.input' # default for state_input data source = cpppo.peekable( str( 'abbbb, ab' )) with machine: for i,(m,s) in enumerate( machine.run( source=source, data=data )): print( "%s #%3d; next byte %3d: %-10.10r: %r" % ( m.name_centered(), i, source.sent, source.peek(), data.get(path) )) print( "Accepted: %r; remaining: %r\n" % ( data.get(path), ''.join( source ))) print( "Final: %r" % ( data )) ``` -------------------------------- ### Get Attribute Single/All Operations Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Describes the support for Get Attribute Single and Get Attribute All operations, used to access raw 8-bit SINT data from CIP Object Attributes. These can be used directly or integrated with `client.pipeline` after parsing operations. ```python # Direct usage example (manual result harvesting) # with client.connector( host=... ) as conn: # attr_data = conn.get_attribute_single( ... ) # Usage with pipeline after parsing operations # operations = client.parse_operations( ... ) # with client.connector( host=... ) as conn: # for idx,dsc,req,rpy,sts,val in conn.pipeline( operations=operations ): # ... ``` -------------------------------- ### Configuring EtherNet/IP Controller Objects with cpppo.cfg Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Demonstrates how to customize default CIP Objects like Identity and TCP/IP by creating a `cpppo.cfg` file. This file can be placed in standard configuration directories or the current working directory. It shows examples of setting string values and JSON-formatted configurations. ```ini [Identity] # Generally, strings are not quoted Product Name = 1756-L61/B LOGIX5561 [TCPIP] # However, some complex structures require JSON configuration: Interface Configuration = { "ip_address": "192.168.0.201", "network_mask": "255.255.255.0", "dns_primary": "8.8.8.8", "dns_secondary": "8.8.4.4", "domain_name": "example.com" } Host Name = controller ``` -------------------------------- ### Python: Accessing Device Identity with Proxy Source: https://github.com/pjkundert/cpppo/blob/master/README.txt This Python code demonstrates how to access a device's identity information using the `proxy` object in the cpppo library. It shows how the `.instance` attribute is populated after the gateway is opened and how the `__str__` method can be used to print the device's product name, network address, and session ID. Connection and identity retrieval occur upon first access via `.read` or the context manager. ```python from __future__ import print_function via = some_sensor( host="10.0.1.2" ) print( "Not yet connected: %s" % ( via )) params = via.parameter_substitution( "A Sensor Parameter" ) reader = via.read( params ) print( "Connected! %s" % ( via )) ``` -------------------------------- ### Python: Get Attribute Data Using Pipeline Source: https://github.com/pjkundert/cpppo/blob/master/README.txt This snippet demonstrates how to retrieve attribute data from an ENIP device using the `client.pipeline` method. It handles both 'Get Attributes All' and 'Get Attribute Single' operations, supporting numeric addressing for classes, instances, and attributes. The `attribute_operations` function is used to specify the tags to retrieve. ```python from cpppo.server.enip.get_attribute import attribute_operations timeout = None # Wait forever, or seconds depth = 0 # No pipelining, or in-flight with client.connector( host=args.address, timeout=timeout ) as conn: for idx,dsc,op,rpy,sts,val in conn.pipeline( operations=attribute_operations( tags ), depth=depth, multiple=False, timeout=timeout ): pass ``` -------------------------------- ### Establish EtherNet/IP CIP Client Connector (Python) Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Provides examples of establishing an EtherNet/IP CIP client connection using the `client.connector` class. It demonstrates both TCP/IP connections and UDP/IP connections, including options for broadcast capabilities. These connections are managed using Python's context manager (`with` statement). ```python from cpppo.server.enip import client with client.connector( host="some_controller" ) as conn: ... ``` ```python from cpppo.server.enip import client with client.connector( host="some_controller", udp=True, broadcast=True ) as conn: ... ``` -------------------------------- ### Running Cpppo EtherNet/IP Polling Example Source: https://github.com/pjkundert/cpppo/blob/master/README.txt This command executes the `poll_example.py` script, which is designed to poll an EtherNet/IP device. It can take a hostname as an argument, otherwise it defaults to 'localhost'. This script is often used in conjunction with the `poll_test.py` simulator. ```bash $ cpppo -m cpppo.server.enip.poll_example ``` -------------------------------- ### Run EtherNet/IP CIP Server Source: https://github.com/pjkundert/cpppo/blob/master/README.org Starts the EtherNet/IP CIP server using a configuration file. It logs loaded configuration files and initiates server operations. This command requires the 'cpppo.cfg' file to be present in an accessible location. ```python python -m cpppo.server.enip -v ``` -------------------------------- ### Running cpppo EtherNet/IP Server with Configuration Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Provides the command to run the cpppo EtherNet/IP server with verbose output, indicating that configuration files have been loaded. ```bash $ python -m cpppo.server.enip -v 01-20 07:01:29.125 ... NORMAL main Loaded config files: ['cpppo.cfg'] ... ``` -------------------------------- ### Pipeline EtherNet/IP I/O Operations Source: https://github.com/pjkundert/cpppo/blob/master/README.org This example shows how to pipeline EtherNet/IP I/O operations for potentially higher performance, especially over high-latency links. It uses argparse for command-line arguments to configure depth, repetition, and target address. The results of each pipelined operation are printed as they are received. Dependencies include 'argparse', 'cpppo', and a timestamping function. ```python import argparse from cpppo.server.enip import client ap = argparse.ArgumentParser() ap.add_argument( '-d', '--depth', default=0, help="Pipelining depth" ) ap.add_argument( '-m', '--multiple', default=0, help="Multiple Service Packet size limit" ) ap.add_argument( '-r', '--repeat', default=1, help="Repeat requests this many times" ) ap.add_argument( '-a', '--address', default='localhost', help="Hostname of target Controller" ) ap.add_argument( '-t', '--timeout', default=None, help="I/O timeout seconds (default: None)" ) ap.add_argument( 'tags', nargs='+', help="Tags to read/write" ) args = ap.parse_args() depth = int( args.depth ) multiple = int( args.multiple ) repeat = int( args.repeat ) operations = client.parse_operations( args.tags * repeat ) timeout = None if args.timeout is not None: timeout = float( args.timeout ) with client.connector( host=args.address, timeout=timeout ) as conn: start = cpppo.timer() num,idx = -1,-1 for num,(idx,dsc,op,rpy,sts,val) in enumerate( conn.pipeline( operations=operations, depth=depth, multiple=multiple, timeout=timeout )): print( "%s: %3d: %s" % ( timestamp(), idx, val )) elapsed = cpppo.timer() - start ``` -------------------------------- ### List EtherNet/IP Services and Identity via UDP Broadcast (Python) Source: https://github.com/pjkundert/cpppo/blob/master/README.org This command-line example demonstrates how to discover EtherNet/IP CIP devices on a local network using broadcast UDP. It requests both service and identity information from all devices responding to the broadcast address. ```bash #+BEGIN_EXAMPLE $ python -m cpppo.server.enip.list_services --udp --broadcast \ --list-identity -a 192.168.0.255 #+END_EXAMPLE ``` ```json #+BEGIN_EXAMPLE { "peer": [ "192.168.0.201", 44818 ], "enip.status": 0, "enip.sender_context.input": "array('c', '\x00\x00\x00\x00\x00\x00\x00\x00')", "enip.session_handle": 0, "enip.length": 25, "enip.CIP.list_services.CPF.count": 1, "enip.CIP.list_services.CPF.item[0].communications_service.capability": 288, "enip.CIP.list_services.CPF.item[0].communications_service.service_name": "Communications", "enip.CIP.list_services.CPF.item[0].communications_service.version": 1, "enip.CIP.list_services.CPF.item[0].length": 19, "enip.CIP.list_services.CPF.item[0].type_id": 256, "enip.command": 4, "enip.input": "array('c', '\x01\x00\x00\x01\x13\x00\x01\x00 \x01Communications\x00')", "enip.options": 0 } { "peer": [ "192.168.0.201", 44818 ], "enip.status": 0, "enip.sender_context.input": "array('c', '\x00\x00\x00\x00\x00\x00\x00\x00')", "enip.session_handle": 0, "enip.length": 60, "enip.CIP.list_identity.CPF.count": 1, "enip.CIP.list_identity.CPF.item[0].length": 54, "enip.CIP.list_identity.CPF.item[0].identity_object.sin_addr": "192.168.0.201", "enip.CIP.list_identity.CPF.item[0].identity_object.status_word": 12640, "enip.CIP.list_identity.CPF.item[0].identity_object.vendor_id": 1, "enip.CIP.list_identity.CPF.item[0].identity_object.product_name": "1756-L61/C LOGIX5561", "enip.CIP.list_identity.CPF.item[0].identity_object.sin_port": 44818, "enip.CIP.list_identity.CPF.item[0].identity_object.state": 255, "enip.CIP.list_identity.CPF.item[0].identity_object.version": 1, "enip.CIP.list_identity.CPF.item[0].identity_object.device_type": 14, "enip.CIP.list_identity.CPF.item[0].identity_object.sin_family": 2, "enip.CIP.list_identity.CPF.item[0].identity_object.serial_number": 7079450, "enip.CIP.list_identity.CPF.item[0].identity_object.product_code": 54, "enip.CIP.list_identity.CPF.item[0].identity_object.product_revision": 2836, "enip.CIP.list_identity.CPF.item[0].type_id": 12, "enip.command": 99, "enip.input": "array('c', '\x01...\x141756-L61/C LOGIX5561\xff')", "enip.options": 0 } #+END_EXAMPLE ``` -------------------------------- ### client.connector.get_attribute_single and client.connector.get_attributes_all Source: https://github.com/pjkundert/cpppo/blob/master/README.org Supports Get Attribute Single and Get Attributes All operations to access raw data in arbitrary Attributes of CIP Objects. Data is presented as raw 8-bit SINT data. These methods can be used directly or in conjunction with client.parse_operations and client.connector's .pipeline for efficient processing of multiple requests. ```APIDOC ## client.connector.get_attribute_single and client.connector.get_attributes_all ### Description Supports Get Attribute Single and Get Attributes All operations to access raw data in arbitrary Attributes of CIP Objects. Data is presented as raw 8-bit SINT data. These methods can be used directly or in conjunction with client.parse_operations and client.connector's .pipeline for efficient processing of multiple requests. ### Method GET (conceptual, actual implementation uses connected/unconnected messaging) ### Endpoint `/pjkundert/cpppo/client.connector` ### Parameters #### Path Parameters (for addressing) - **class** (integer) - Required - The CIP Class of the object. - **instance** (integer) - Required - The CIP Instance of the object. - **attribute** (integer) - Optional - The CIP Attribute of the object. If omitted, 'Get Attributes All' is performed. ### Request Example ```python from cpppo.server.enip.get_attribute import attribute_operations timeout = None # Wait forever, or seconds depth = 0 # No pipelining, or in-flight with client.connector( host=args.address, timeout=timeout ) as conn: for idx, dsc, op, rpy, sts, val in conn.pipeline( operations=attribute_operations( tags ), depth=depth, multiple=False, timeout=timeout ): # Process results pass # Example command line usage: # $ python -m cpppo.server.enip.get_attribute --depth 3 -v '@1/1' '@1/1/7' ``` ### Response #### Success Response (200) - **attribute_data** (list of bytes) - Raw 8-bit SINT data from the requested attribute(s). #### Response Example ```json [ 1, 0, 14, 0, 54, ... # Raw SINT data for Get Attributes All ] ``` **Note:** The data is always presented as raw 8-bit SINT data. Numeric addressing to the Instance or Attribute level is supported, e.g., `@/` or `@//`. ``` -------------------------------- ### Discover EtherNet/IP CIP Services via Broadcast (Python) Source: https://github.com/pjkundert/cpppo/blob/master/README.txt This command-line example shows how to perform a UDP broadcast to discover EtherNet/IP CIP services on the network. It specifies the source IP address and the broadcast destination address. The output includes connection details and client CIP send requests. ```python python -m cpppo.server.enip.list_services -vv --udp --broadcast \ --source 192.168.1.5 --address 192.168.1.255 ``` -------------------------------- ### EtherNet/IP CIP Server Simulator Source: https://context7.com/pjkundert/cpppo/llms.txt Start a simulated ControlLogix PLC with custom tags for testing and development. The server can be configured with basic array and scalar tags, explicit CIP Class/Instance/Attribute addressing, or run on a specific interface and port with a web API. It also supports starting as a simple non-routing device. ```APIDOC ## EtherNet/IP CIP Server Simulator ### Description Start a simulated ControlLogix PLC with custom tags for testing and development. ### Method `enip_server` (command-line utility) ### Endpoint N/A (local simulation) ### Parameters #### Command-line Arguments - `--print` (string) - Define tags to be simulated (e.g., `TAG_NAME=TYPE[SIZE]`). - `-a` (string) - Specify the IP address and port for the server (e.g., `192.168.1.10:44818`). - `-w` (string) - Specify the port for the web API (e.g., `:8080`). - `-S` (boolean) - Start as a simple non-routing device. ### Request Example ```bash # Basic server with array and scalar tags enip_server --print SCADA=INT[1000] TEXT=SSTRING[100] FLOAT=REAL # Server with explicit CIP Class/Instance/Attribute addressing enip_server --print SCADA@22/1/1=INT[1000] TEXT@22/1/2=SSTRING[100] FLOAT@22/1/3=REAL # Server on specific interface and port with web API enip_server -a 192.168.1.10:44818 -w :8080 --print Motor_Velocity@0x93/3/10=REAL SCADA=INT[100] # Simple non-routing device (e.g., MicroLogix, PowerFlex) enip_server -S --print Sensor_Value=REAL Temperature=INT ``` ### Response N/A (server simulation) ``` -------------------------------- ### GET /api/connections Source: https://github.com/pjkundert/cpppo/blob/master/README.txt Retrieves a list of all active connections. This can be used to monitor connection status and wait for connections to close. ```APIDOC ## GET /api/connections ### Description Retrieves a list of all active connections. This can be used to monitor connection status and wait for connections to close. ### Method GET ### Endpoint `/api/connections` ### Response #### Success Response (200) - **object** - Contains connection details, including interface, port, received data count, and request count for each connection. The `data` object will be empty if no connections are active. ### Response Example ```json { "alarm": [], "command": {}, "data": { "connections": { "127_0_0_1_52590": { "eof": false, "interface": "127.0.0.1", "port": 52590, "received": 1610, "requests": 17 }, "127_0_0_1_52591": { "eof": false, "interface": "127.0.0.1", "port": 52591, "received": 290, "requests": 5 } } }, "since": null, "until": 1372889099.908609 } ``` ```