### Compile and Install EIPScanner from Source Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/getting_started.rst Instructions for compiling and installing the EIPScanner library using CMake. Requires Linux or MacOS, CMake 3.5+, a C++20 compiler, and optionally Gtest. Build options for tests and examples are also shown. ```bash mkdir build cd build cmake .. cmake --build . --target install ``` ```bash cmake -DTEST_ENABLED=ON -DEXAMPLE_ENABLED=ON .. ``` -------------------------------- ### Build Executable: Discovery Example (CMake) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/examples/CMakeLists.txt This CMake snippet sets up the build for the 'discovery_example' executable using 'DiscoveryManagerExample.cpp'. It links against 'EIPScanner' and includes 'ws2_32' for Windows builds. ```cmake include_directories("${PROJECT_SOURCE_DIR}/src") add_executable(discovery_example DiscoveryManagerExample.cpp) target_link_libraries(discovery_example EIPScanner) if(WIN32) target_link_libraries(discovery_example ws2_32) endif() ``` -------------------------------- ### Configure CMakeLists.txt to Use EIPScanner Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/getting_started.rst Example CMakeLists.txt file to set up a project that uses the EIPScanner library. It specifies C++ standard, executable target, include directories, and library linking. Manual configuration is required as EIPScanner does not provide a CMake module. ```cmake cmake_minimum_required(VERSION 3.5) project(hi_eip) set(CMAKE_CXX_STANDARD 14) add_executable(hi_eip main.cpp) include_directories(/usr/local/include/EIPScanner) target_link_libraries(hi_eip EIPScanner) ``` -------------------------------- ### Build Executable: File Object Example (CMake) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/examples/CMakeLists.txt This snippet configures the build for the 'file_object_example' executable. It compiles 'FileObjectExample.cpp' and links it with the 'EIPScanner' library. The 'ws2_32' library is linked on Windows systems. ```cmake include_directories("${PROJECT_SOURCE_DIR}/src") add_executable(file_object_example FileObjectExample.cpp) target_link_libraries(file_object_example EIPScanner) if(WIN32) target_link_libraries(file_object_example ws2_32) endif() ``` -------------------------------- ### Build Executable: Parameter Object Example (CMake) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/examples/CMakeLists.txt This CMake configuration defines the 'parameter_object_example' executable. It compiles 'ParameterObjectExample.cpp' and links it with the 'EIPScanner' library. The 'ws2_32' library is conditionally linked on Windows. ```cmake include_directories("${PROJECT_SOURCE_DIR}/src") add_executable(parameter_object_example ParameterObjectExample.cpp) target_link_libraries(parameter_object_example EIPScanner) if(WIN32) target_link_libraries(parameter_object_example ws2_32) endif() ``` -------------------------------- ### C++ Example: Reading Vendor ID with EIPScanner Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/getting_started.rst A C++ code snippet demonstrating how to use EIPScanner to connect to a device, read its Vendor ID using a MessageRouter, and log the result. It includes necessary headers and namespace usage. ```cpp #include #include #include using eipScanner::SessionInfo; using eipScanner::MessageRouter; using namespace eipScanner::cip; using namespace eipScanner::utils; int main() { Logger::setLogLevel(LogLevel::DEBUG); auto si = std::make_shared("172.28.1.3", 0xAF12); auto messageRouter = std::make_shared(); // Read attribute auto response = messageRouter->sendRequest(si, ServiceCodes::GET_ATTRIBUTE_SINGLE, EPath(0x01, 1, 1)); if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { Buffer buffer(response.getData()); CipUint vendorId; buffer >> vendorId; Logger(LogLevel::INFO) << "Vendor ID is " << vendorId; } return 0; } ``` -------------------------------- ### Build Executable: Identity Object Example (CMake) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/examples/CMakeLists.txt This CMake configuration defines the 'identity_object_example' executable. It uses 'IdentityObjectExample.cpp' as the source and links against 'EIPScanner'. Windows builds also include a link to 'ws2_32'. ```cmake include_directories("${PROJECT_SOURCE_DIR}/src") add_executable(identity_object_example IdentityObjectExample.cpp) target_link_libraries(identity_object_example EIPScanner) if(WIN32) target_link_libraries(identity_object_example ws2_32) endif() ``` -------------------------------- ### Build Executable: Yaskawa Assembly Object Example (CMake) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/examples/CMakeLists.txt This CMake configuration defines the 'yaskawa_assembly_object_example' executable, located in the 'vendors/yaskawa/mp3300iec' directory. It links against 'EIPScanner' and 'ws2_32' on Windows. ```cmake include_directories("${PROJECT_SOURCE_DIR}/src") add_executable(yaskawa_assembly_object_example vendors/yaskawa/mp3300iec/Yaskawa_AssemblyObjectExample.cpp) target_link_libraries(yaskawa_assembly_object_example EIPScanner) if(WIN32) target_link_libraries(yaskawa_assembly_object_example ws2_32) endif() ``` -------------------------------- ### Install EIPScanner using CMake Source: https://github.com/nimbuscontrols/eipscanner/blob/master/README.md Instructions for building and installing the EIPScanner library using CMake. This involves creating a build directory, configuring the build with CMake, and then building and installing the project. ```shell mkdir build && cd build cmake .. cmake --build . --target install ``` -------------------------------- ### Build Executable: Implicit Messaging Example (CMake) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/examples/CMakeLists.txt This CMake code sets up the build for the 'implicit_messaging' executable, compiling 'ImplicitMessagingExample.cpp' and linking it to the 'EIPScanner' library. Windows builds additionally link the 'ws2_32' library. ```cmake include_directories("${PROJECT_SOURCE_DIR}/src") add_executable(implicit_messaging ImplicitMessagingExample.cpp) target_link_libraries(implicit_messaging EIPScanner) if(WIN32) target_link_libraries(implicit_messaging ws2_32) endif() ``` -------------------------------- ### Basic EIPScanner Logging Example (C++) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/misc/logging.rst Demonstrates the basic usage of the EIPScanner's embedded logger. It shows how to set the log level and print messages at different levels. The logger prints messages in its destructor, so avoid storing logger instances. ```cpp #include "utils/Logger.h" using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; int main() { Logger::setLogLevel(LogLevel::INFO); Logger(LogLevel::INFO) << "You will see this message."; Logger(LogLevel::DEBUG) << "You won't see this message."; return 0; } ``` -------------------------------- ### Establish Implicit Messaging Connection in C++ Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/implicit_messaging.rst Demonstrates the full lifecycle of an implicit messaging connection, including session setup, parameter configuration, and handling data via listeners. ```cpp #include #include #include "SessionInfo.h" #include "ConnectionManager.h" #include "utils/Logger.h" #include "utils/Buffer.h" using namespace eipScanner::cip; using eipScanner::SessionInfo; using eipScanner::MessageRouter; using eipScanner::ConnectionManager; using eipScanner::cip::connectionManager::ConnectionParameters; using eipScanner::cip::connectionManager::NetworkConnectionParams; using eipScanner::utils::Buffer; using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; int main() { Logger::setLogLevel(LogLevel::DEBUG); auto si = std::make_shared("172.28.1.3", 0xAF12); ConnectionManager connectionManager; ConnectionParameters parameters; parameters.connectionPath = {0x20, 0x04,0x24, 151, 0x2C, 150, 0x2C, 100}; parameters.o2tRealTimeFormat = true; parameters.originatorVendorId = 342; parameters.originatorSerialNumber = 32423; parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::P2P; parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY; parameters.t2oNetworkConnectionParams |= 32; parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::P2P; parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY; parameters.o2tNetworkConnectionParams |= 32; parameters.originatorSerialNumber = 0x12345; parameters.o2tRPI = 1000000; parameters.t2oRPI = 1000000; parameters.transportTypeTrigger |= NetworkConnectionParams::CLASS1; auto io = connectionManager.forwardOpen(si, parameters); if (auto ptr = io.lock()) { ptr->setDataToSend(std::vector(32)); ptr->setReceiveDataListener([](auto realTimeHeader, auto sequence, auto data) { std::ostringstream ss; ss << "secNum=" << sequence << " data="; for (auto &byte : data) { ss << "[" << std::hex << (int) byte << "]"; } Logger(LogLevel::INFO) << "Received: " << ss.str(); }); ptr->setCloseListener([]() { Logger(LogLevel::INFO) << "Closed"; }); } int count = 200; while (connectionManager.hasOpenConnections() && count-- > 0) { connectionManager.handleConnections(std::chrono::milliseconds(100)); } connectionManager.forwardClose(si, io); return 0; } ``` -------------------------------- ### Build Executable: Explicit Messaging Example (CMake) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/examples/CMakeLists.txt This snippet defines the build process for the 'explicit_messaging' executable. It includes the source file 'ExplicitMessagingExample.cpp' and links it against the 'EIPScanner' library. On Windows, it also links against 'ws2_32'. ```cmake include_directories("${PROJECT_SOURCE_DIR}/src") add_executable(explicit_messaging ExplicitMessagingExample.cpp) target_link_libraries(explicit_messaging EIPScanner) if(WIN32) target_link_libraries(explicit_messaging ws2_32) endif() ``` -------------------------------- ### C++ MessageRouter Explicit Messaging Example Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt This C++ code snippet demonstrates how to use the MessageRouter class to perform explicit messaging operations. It includes examples of reading a single attribute, writing to an attribute, and calling a custom service. The code requires the EIPScanner library and uses SessionInfo for connection details. It handles responses and logs success or error messages. ```cpp #include #include #include #include using eipScanner::SessionInfo; using eipScanner::MessageRouter; using namespace eipScanner::cip; using namespace eipScanner::utils; int main() { Logger::setLogLevel(LogLevel::DEBUG); try { auto si = std::make_shared("192.168.1.100", 0xAF12); MessageRouter messageRouter; // Read a single attribute: Identity Object (0x01), Instance 1, Attribute 1 (Vendor ID) auto response = messageRouter.sendRequest( si, ServiceCodes::GET_ATTRIBUTE_SINGLE, EPath(0x01, 1, 1), // ClassID, InstanceID, AttributeID {} // No request data ); if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { Buffer buffer(response.getData()); CipUint vendorId; buffer >> vendorId; Logger(LogLevel::INFO) << "Vendor ID: " << vendorId; } else { Logger(LogLevel::ERROR) << "Request failed with status: 0x" << std::hex << response.getGeneralStatusCode(); } // Write to an attribute: Assembly Object (0x04), Instance 151, Attribute 3 Buffer writeData; writeData << CipUsint(1) << CipUsint(2) << CipUsint(3) << CipUsint(4); response = messageRouter.sendRequest( si, ServiceCodes::SET_ATTRIBUTE_SINGLE, EPath(0x04, 151, 3), writeData.data() ); if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { Logger(LogLevel::INFO) << "Write successful"; } // Call a custom service with encoded arguments const CipUsint CUSTOM_SERVICE = 0x32; Buffer serviceArgs; CipInt argument = 100; serviceArgs << argument; response = messageRouter.sendRequest( si, CUSTOM_SERVICE, EPath(0xF0, 0x05), // Custom class, instance 5 serviceArgs.data() ); if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { Buffer resultBuffer(response.getData()); CipInt result; resultBuffer >> result; Logger(LogLevel::INFO) << "Service result: " << result; } } catch (std::exception& e) { Logger(LogLevel::ERROR) << e.what(); return -1; } return 0; } ``` -------------------------------- ### Set Shared Library Path for EIPScanner Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/getting_started.rst Command to set the LD_LIBRARY_PATH environment variable to help the system locate the EIPScanner shared library if it cannot be found during execution. ```bash export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/where/eipscanner/is/installed ``` -------------------------------- ### Establish EIP Sessions with SessionInfo in C++ Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt The `SessionInfo` class in EIPScanner establishes and manages TCP connections with EtherNet/IP adapters. It handles the EIP session registration handshake and provides the communication channel for explicit messaging. This example demonstrates creating a `SessionInfo` object with default and custom timeouts, along with error handling for connection failures. ```cpp #include #include using eipScanner::SessionInfo; using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; int main() { Logger::setLogLevel(LogLevel::DEBUG); try { // Connect to EIP adapter with default timeout auto si = std::make_shared("192.168.1.100", 0xAF12); // Or with custom connection timeout (10 seconds) auto siWithTimeout = std::make_shared( "192.168.1.100", 0xAF12, std::chrono::seconds(10) ); Logger(LogLevel::INFO) << "Session established successfully"; // Session automatically closed when shared_ptr goes out of scope } catch (std::runtime_error& e) { Logger(LogLevel::ERROR) << "Connection failed: " << e.what(); return -1; } catch (std::system_error& e) { Logger(LogLevel::ERROR) << "System error: " << e.what(); return -1; } return 0; } ``` -------------------------------- ### EIPScanner Project Configuration (CMake) Source: https://github.com/nimbuscontrols/eipscanner/blob/master/CMakeLists.txt Defines the project name, version, description, and homepage URL. Sets the C++ standard to C++20 and configures build options for vendor source, tests, and examples. ```cmake cmake_minimum_required(VERSION 3.5) set(EIPSCANNER_MAJOR_VERSION 1) set(EIPSCANNER_MINOR_VERSION 1) set(EIPSCANNER_PATCH_VERSION 0) set(EIPSCANNER_FULL_VERSION ${EIPSCANNER_MAJOR_VERSION}.${EIPSCANNER_MINOR_VERSION}.${EIPSCANNER_PATCH_VERSION}) project(EIPScanner VERSION ${EIPSCANNER_FULL_VERSION} DESCRIPTION "Free implementation of EtherNet/IP in C++" HOMEPAGE_URL "https://github.com/nimbuscontrols/EIPScanner" ) set(CMAKE_CXX_STANDARD 20) option(ENABLE_VENDOR_SRC "Enable vendor source" ON) option(TEST_ENABLED "Enable unit test" OFF) option(EXAMPLE_ENABLED "Build examples" OFF) if (WIN32) # Needed on MinGW with GCC 10 or lower add_compile_definitions(_WIN32_WINNT=0x0600) # Prevent std::numeric_limits::max collision if (MSVC) add_compile_definitions(NOMINMAX) endif() endif() add_subdirectory(src) if (EXAMPLE_ENABLED) add_subdirectory(examples) endif() if (TEST_ENABLED) add_subdirectory(test) endif() configure_file(EIPScanner.pc.in EIPScanner.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/EIPScanner.pc DESTINATION lib/pkgconfig) ``` -------------------------------- ### Establish and Manage Implicit I/O Connections with C++ Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt This snippet demonstrates how to configure connection parameters, open an I/O connection using forwardOpen, and process cyclic data exchange. It includes setting up listeners for received data and dynamic data updates, followed by a main loop to handle connection maintenance. ```cpp #include #include #include #include #include #include using namespace eipScanner::cip; using eipScanner::SessionInfo; using eipScanner::ConnectionManager; using eipScanner::cip::connectionManager::ConnectionParameters; using eipScanner::cip::connectionManager::NetworkConnectionParams; using eipScanner::utils::Buffer; using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; int main() { Logger::setLogLevel(LogLevel::DEBUG); try { auto si = std::make_shared("192.168.1.100", 0xAF12); ConnectionManager connectionManager; ConnectionParameters params; params.connectionPath = {0x20, 0x04, 0x24, 151, 0x2C, 150, 0x2C, 100}; params.originatorVendorId = 342; params.originatorSerialNumber = 0x12345; params.t2oNetworkConnectionParams |= NetworkConnectionParams::P2P; params.t2oNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY; params.t2oNetworkConnectionParams |= 32; params.o2tNetworkConnectionParams |= NetworkConnectionParams::P2P; params.o2tNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY; params.o2tNetworkConnectionParams |= 32; params.o2tRPI = 1000000; params.t2oRPI = 1000000; params.o2tRealTimeFormat = true; params.transportTypeTrigger |= NetworkConnectionParams::CLASS1; auto io = connectionManager.forwardOpen(si, params); if (auto ptr = io.lock()) { std::vector outputData(32, 0); outputData[0] = 0x01; ptr->setDataToSend(outputData); ptr->setReceiveDataListener([](auto realTimeHeader, auto sequence, auto data) { std::ostringstream ss; ss << "Seq=" << sequence << " Data: "; for (size_t i = 0; i < std::min(data.size(), size_t(8)); ++i) { ss << "[0x" << std::hex << (int)data[i] << "]"; } Logger(LogLevel::INFO) << "Received: " << ss.str(); }); ptr->setCloseListener([]() { Logger(LogLevel::INFO) << "Connection closed by adapter"; }); ptr->setSendDataListener([](std::vector& data) { static uint8_t counter = 0; data[0] = counter++; }); } int iterations = 100; while (connectionManager.hasOpenConnections() && iterations-- > 0) { connectionManager.handleConnections(std::chrono::milliseconds(100)); } connectionManager.forwardClose(si, io); } catch (std::exception& e) { Logger(LogLevel::ERROR) << e.what(); return -1; } return 0; } ``` -------------------------------- ### Instantiate and Access Parameter Objects Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/standard_objects/parameter_object.rst This code snippet demonstrates how to create a vector of ParameterObject instances after determining the parameter count and attribute support. It iterates from 1 to the total parameter count, creating each ParameterObject. It also shows how to access various attributes of a ParameterObject instance, such as type, actual value, engineering value, name, and units, and how to update the actual value. ```cpp std::vector parameters; parameters.reserve(paramsCount); for (int i = 0; i < paramsCount; ++i) { parameters.emplace_back(i+1, allAttributes, si); } if (!parameters.empty()) { parameters[0].getType(); // Read type parameters[0].getActualValue(); // 2040 parameters[0].getEngValue(); // 20.4 parameters[0].getName(); // Freq parameters[0].getUnits(); // Hz // .. etc parameters[0].updateValue(si); parameters[0].getActualValue(); // updated value } ``` -------------------------------- ### Configure IO Connection Listeners and Data Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/implicit_messaging.rst Demonstrates how to set the data to be sent over an IO connection and register callbacks for received data and connection closure events. ```cpp if (auto ptr = io.lock()) { // Set data to send ptr->setDataToSend(std::vector(32)); // Set callback for received data ptr->setReceiveDataListener([](auto realTimeHeader, auto sequence, auto data) { std::ostringstream ss; ss << "secNum=" << sequence << " data="; for (auto &byte : data) { ss << "[" << std::hex << (int) byte << "]"; } Logger(LogLevel::INFO) << "Received: " << ss.str(); }); // Set callback to no ptr->setCloseListener([]() { Logger(LogLevel::INFO) << "Closed"; }); } ``` -------------------------------- ### Logger Utility: C++ Logging with Levels and Custom Appenders Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt Demonstrates the C++ Logger class for configurable logging. It shows how to set log levels, log messages at different severities (ERROR, WARNING, INFO, DEBUG), and implement a custom log appender to redirect output. ```cpp #include using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; using eipScanner::utils::LogAppenderIf; // Custom log appender implementation class FileLogAppender : public LogAppenderIf { public: void print(LogLevel level, const std::string& message) override { // Custom implementation: write to file, syslog, etc. const char* levelStr = ""; switch (level) { case LogLevel::ERROR: levelStr = "ERROR"; break; case LogLevel::WARNING: levelStr = "WARN "; break; case LogLevel::INFO: levelStr = "INFO "; break; case LogLevel::DEBUG: levelStr = "DEBUG"; break; } // Example: just print to stdout with custom format printf("[%s] %s\n", levelStr, message.c_str()); } }; int main() { // Set minimum log level (messages below this level are ignored) Logger::setLogLevel(LogLevel::DEBUG); // Show all messages // Log messages at different levels Logger(LogLevel::ERROR) << "This is an error message"; Logger(LogLevel::WARNING) << "This is a warning"; Logger(LogLevel::INFO) << "Informational message with value: " << 42; Logger(LogLevel::DEBUG) << "Debug details: " << std::hex << 0xDEAD; // Change log level to show only warnings and errors Logger::setLogLevel(LogLevel::WARNING); Logger(LogLevel::INFO) << "This won't be displayed"; Logger(LogLevel::WARNING) << "This will be displayed"; // Use custom appender Logger::setAppender(std::make_shared()); Logger(LogLevel::INFO) << "Using custom appender"; return 0; } ``` -------------------------------- ### Read Device Parameters using C++ EIPScanner Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt This C++ code snippet demonstrates how to use the ParameterObject class from the EIPScanner library to interact with device parameters. It shows the process of connecting to a device, reading the total number of parameters, fetching individual parameter metadata (name, type, units, help text, read-only status), and retrieving both raw and scaled engineering values. It also includes updating parameter values from the device. ```cpp #include #include #include #include #include using namespace eipScanner::cip; using eipScanner::SessionInfo; using eipScanner::MessageRouter; using eipScanner::ParameterObject; using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; using eipScanner::utils::Buffer; int main() { Logger::setLogLevel(LogLevel::DEBUG); try { auto si = std::make_shared("192.168.1.100", 0xAF12); MessageRouter messageRouter; // First, read the number of parameters from class attribute 2 auto response = messageRouter.sendRequest( si, ServiceCodes::GET_ATTRIBUTE_SINGLE, EPath(ParameterObject::CLASS_ID, 0, 2) // Class 0x0F, instance 0, attr 2 ); if (response.getGeneralStatusCode() != GeneralStatusCodes::SUCCESS) { Logger(LogLevel::ERROR) << "Failed to read parameter count"; return -1; } Buffer buffer(response.getData()); CipUint paramCount; buffer >> paramCount; Logger(LogLevel::INFO) << "Device has " << paramCount << " parameters"; // Check if device supports full attributes (class descriptor, attr 8) response = messageRouter.sendRequest( si, ServiceCodes::GET_ATTRIBUTE_SINGLE, EPath(ParameterObject::CLASS_ID, 0, 8) ); bool fullAttributes = false; if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { buffer = Buffer(response.getData()); CipUint descriptor; buffer >> descriptor; fullAttributes = (descriptor & 0x02) != 0; // Bit 1 = supports full attributes } // Read parameters std::vector parameters; for (CipUint i = 1; i <= std::min(paramCount, CipUint(5)); ++i) { ParameterObject param(i, fullAttributes, si); Logger(LogLevel::INFO) << "--- Parameter " << i << " ---"; Logger(LogLevel::INFO) << "Name: " << param.getName(); Logger(LogLevel::INFO) << "Type: 0x" << std::hex << (int)param.getType(); Logger(LogLevel::INFO) << "Units: " << param.getUnits(); Logger(LogLevel::INFO) << "Help: " << param.getHelp(); Logger(LogLevel::INFO) << "Read Only: " << (param.isReadOnly() ? "Yes" : "No"); // Read actual value (type depends on parameter) CipUint actualValue = param.getActualValue(); Logger(LogLevel::INFO) << "Actual Value: " << actualValue; // If scalable, get engineering value if (param.isScalable()) { double engValue = param.getEngValue(); Logger(LogLevel::INFO) << "Engineering Value: " << engValue << " " << param.getUnits(); Logger(LogLevel::INFO) << "Scaling: mult=" << param.getScalingMultiplier() << " div=" << param.getScalingDivisor() << " base=" << param.getScalingBase() << " offset=" << param.getScalingOffset(); } // Update value from device param.updateValue(si); Logger(LogLevel::INFO) << "Updated Value: " << param.getActualValue(); parameters.push_back(param); } } catch (std::exception& e) { Logger(LogLevel::ERROR) << e.what(); return -1; } return 0; } ``` -------------------------------- ### EPath Utility: C++ CIP Object Addressing Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt Illustrates the C++ EPath class for constructing CIP Electronic Path segments. It demonstrates creating paths for class, instance, and attribute levels, accessing path components, and packing paths for protocol encoding. ```cpp #include #include using eipScanner::cip::EPath; using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; int main() { Logger::setLogLevel(LogLevel::DEBUG); // Address formats for different use cases // Class-level service (e.g., get class attributes) EPath classPath(0x01); // Identity Object class Logger(LogLevel::INFO) << "Class path: " << classPath.toString(); // Instance-level service (e.g., get instance attributes) EPath instancePath(0x01, 1); // Identity Object, instance 1 Logger(LogLevel::INFO) << "Instance path: " << instancePath.toString(); // Attribute-level service (e.g., get single attribute) EPath attrPath(0x01, 1, 7); // Identity Object, instance 1, attr 7 (Product Name) Logger(LogLevel::INFO) << "Attribute path: " << attrPath.toString(); // Access path components Logger(LogLevel::INFO) << "Class ID: 0x" << std::hex << attrPath.getClassId(); Logger(LogLevel::INFO) << "Instance ID: " << std::dec << attrPath.getObjectId(); Logger(LogLevel::INFO) << "Attribute ID: " << attrPath.getAttributeId(); // Get packed path for protocol encoding std::vector packedPath = attrPath.packPaddedPath(); Logger(LogLevel::INFO) << "Packed path size: " << packedPath.size() << " bytes"; // Use 8-bit path segments (for some older devices) std::vector packed8bit = attrPath.packPaddedPath(true); // Common CIP Object Class IDs EPath identityPath(0x01, 1); // Identity Object EPath assemblyPath(0x04, 100); // Assembly Object EPath parameterPath(0x0F, 1); // Parameter Object EPath filePath(0x37, 0xC8); // File Object EPath connMgrPath(0x06, 1); // Connection Manager return 0; } ``` -------------------------------- ### Perform CIP File Upload with FileObject Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt Demonstrates how to use the FileObject class to read files from an EIP device. It handles the transfer process through callbacks and saves the resulting byte stream to a local file. ```cpp #include #include #include #include #include using namespace eipScanner::cip; using eipScanner::SessionInfo; using eipScanner::FileObject; using eipScanner::FileObjectStateCodes; using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; int main() { Logger::setLogLevel(LogLevel::DEBUG); try { auto si = std::make_shared("192.168.1.100", 0xAF12); // File Object instance 0xC8 (200) is commonly used for EDS files FileObject edsFile(0xC8, si); Logger(LogLevel::INFO) << "Starting file upload..."; // Begin upload with completion callback edsFile.beginUpload(si, [](GeneralStatusCodes status, const std::vector& fileContent) { if (status == GeneralStatusCodes::SUCCESS) { Logger(LogLevel::INFO) << "Upload complete, received " << fileContent.size() << " bytes"; // Save file to disk std::ofstream outFile("Device.eds", std::ios::out | std::ios::trunc | std::ios::binary); if (outFile.is_open()) { outFile.write(reinterpret_cast(fileContent.data()), fileContent.size()); outFile.close(); Logger(LogLevel::INFO) << "File saved as Device.eds"; } else { Logger(LogLevel::ERROR) << "Failed to open output file"; } } else { Logger(LogLevel::ERROR) << "Upload failed with status: 0x" << std::hex << static_cast(status); } }); // Handle transfer chunks until complete // File data is sent in chunks (max 255 bytes each) while (edsFile.handleTransfers(si)) { // Processing transfers... } Logger(LogLevel::INFO) << "Transfer handling complete"; } catch (std::exception& e) { Logger(LogLevel::ERROR) << e.what(); return -1; } return 0; } ``` -------------------------------- ### Encode and Decode CIP Data with Buffer Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt Shows how to use the Buffer utility class to serialize and deserialize various CIP data types. It supports standard types like integers, floats, and doubles using stream-like operators. ```cpp #include #include #include using namespace eipScanner::cip; using eipScanner::utils::Buffer; using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; int main() { Logger::setLogLevel(LogLevel::DEBUG); // Encoding data for sending to device Buffer sendBuffer; CipUsint byteVal = 0x42; CipUint wordVal = 0x1234; CipUdint dwordVal = 0xDEADBEEF; CipInt signedVal = -100; CipReal floatVal = 3.14159f; CipLreal doubleVal = 2.71828; // Encode values using << operator sendBuffer << byteVal << wordVal << dwordVal << signedVal << floatVal << doubleVal; // Get encoded data as byte vector std::vector encodedData = sendBuffer.data(); Logger(LogLevel::INFO) << "Encoded " << encodedData.size() << " bytes"; // Decoding data received from device Buffer receiveBuffer(encodedData); CipUsint decodedByte; CipUint decodedWord; CipUdint decodedDword; CipInt decodedSigned; CipReal decodedFloat; CipLreal decodedDouble; // Decode values using >> operator receiveBuffer >> decodedByte >> decodedWord >> decodedDword >> decodedSigned >> decodedFloat >> decodedDouble; Logger(LogLevel::INFO) << "Decoded byte: 0x" << std::hex << (int)decodedByte; Logger(LogLevel::INFO) << "Decoded word: 0x" << std::hex << decodedWord; Logger(LogLevel::INFO) << "Decoded dword: 0x" << std::hex << decodedDword; Logger(LogLevel::INFO) << "Decoded signed: " << std::dec << decodedSigned; Logger(LogLevel::INFO) << "Decoded float: " << decodedFloat; Logger(LogLevel::INFO) << "Decoded double: " << decodedDouble; // Pre-allocate buffer of specific size Buffer preallocated(100); // 100 bytes // Chain operations Buffer chainBuffer; chainBuffer << CipUsint(1) << CipUsint(2) << CipUsint(3); return 0; } ``` -------------------------------- ### Establish EtherNet/IP Session using SessionInfo in C++ Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/explicit_messaging.rst This C++ code snippet shows how to establish an EtherNet/IP session with an adapter using the SessionInfo object. It takes the IP address and port of the EIP adapter as arguments. This is a prerequisite for sending any explicit messages. It utilizes the EIPScanner library. ```cpp auto si = std::make_shared("172.28.1.3", 0xAF12); ``` -------------------------------- ### Disable Vendor Source Files in EIPScanner CMake Configuration Source: https://github.com/nimbuscontrols/eipscanner/blob/master/README.md Demonstrates how to disable the automatic inclusion of vendor-specific source files in the EIPScanner build. This can be done by setting a CMake option directly in the CMakeLists.txt file. ```cmake option(ENABLE_VENDOR_SRC "Enable vendor source" OFF) ``` -------------------------------- ### Check Parameter Object Full Attributes Support Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/standard_objects/parameter_object.rst This code snippet shows how to read the 'Parameter Class Descriptor' attribute to determine if the CIP device's parameters support full attributes (like descriptions and scaling). It sends a GET_ATTRIBUTE_SINGLE request and checks the second bit of the descriptor to set a boolean flag indicating support. Errors during the request are logged. ```cpp response = messageRouter.sendRequest(si , ServiceCodes::GET_ATTRIBUTE_SINGLE , EPath(ParameterObject::CLASS_ID, 0, CLASS_DESCRIPTOR)); if (response.getGeneralStatusCode() != GeneralStatusCodes::SUCCESS) { Logger(LogLevel::ERROR) << "Failed to read the class descriptor"; logGeneralAndAdditionalStatus(response); return -1; } buffer = Buffer(response.getData()); CipUint descriptor; buffer >> descriptor; Logger(LogLevel::INFO) << "Read the class descriptor=0x" << std::hex << (int)descriptor; bool allAttributes = descriptor & SUPPORTS_FULL_ATTRIBUTES; ``` -------------------------------- ### Discover EIP Devices on Network using C++ Source: https://context7.com/nimbuscontrols/eipscanner/llms.txt Discovers EtherNet/IP (EIP) devices on the network by sending UDP broadcast messages and collecting responses. It requires the EIPScanner library and returns a list of IdentityItem objects, each containing device identity and network address information. The broadcast address and timeout duration are configurable. ```cpp #include #include using eipScanner::DiscoveryManager; using eipScanner::IdentityItem; using eipScanner::utils::Logger; using eipScanner::utils::LogLevel; int main() { Logger::setLogLevel(LogLevel::DEBUG); // Create discovery manager with broadcast address and timeout // Use appropriate broadcast address for your network (e.g., 192.168.1.255) DiscoveryManager discoveryManager( "192.168.1.255", // Broadcast address 0xAF12, // EIP port (44818) std::chrono::seconds(2) // Timeout to wait for responses ); // Discover all EIP devices on the network IdentityItem::Vec devices = discoveryManager.discover(); Logger(LogLevel::INFO) << "Found " << devices.size() << " devices:"; for (const auto& device : devices) { Logger(LogLevel::INFO) << "----------------------------"; Logger(LogLevel::INFO) << "IP Address: " << device.socketAddress.toString(); Logger(LogLevel::INFO) << "Product Name: " << device.identityObject.getProductName(); Logger(LogLevel::INFO) << "Vendor ID: " << device.identityObject.getVendorId(); Logger(LogLevel::INFO) << "Device Type: " << device.identityObject.getDeviceType(); Logger(LogLevel::INFO) << "Product Code: " << device.identityObject.getProductCode(); Logger(LogLevel::INFO) << "Revision: " << device.identityObject.getRevision().toString(); Logger(LogLevel::INFO) << "Serial Number: 0x" << std::hex << device.identityObject.getSerialNumber(); Logger(LogLevel::INFO) << "Status: 0x" << std::hex << device.identityObject.getStatus(); } if (devices.empty()) { Logger(LogLevel::WARNING) << "No EIP devices found on the network"; } return 0; } ``` -------------------------------- ### Send Explicit Message Request using Message Router in C++ Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/explicit_messaging.rst This C++ code snippet demonstrates how to establish an EtherNet/IP session and send an explicit message request using the Message Router. It includes setting up session information, defining service codes and EPATH, preparing arguments in a buffer, sending the request, and processing the response. Dependencies include EIPScanner libraries for session management, message routing, and utilities. ```cpp #include #include #include using eipScanner::SessionInfo; using eipScanner::MessageRouter; using namespace eipScanner::cip; using namespace eipScanner::utils; int main() { try { // Open EIP session with the adapter auto si = std::make_shared("172.28.1.3", 0xAF12); // Send Message Router Request MessageRouter messageRouter; const CipUsint PLUS_ON_SERVICE = 0x05; const EPath EPATH_TO_WIDGET_INSTANCE(0xf0, 0x5); Buffer buffer; CipInt arg = 5; buffer << arg; auto response = messageRouter.sendRequest(si, PLUS_ON_SERVICE, EPATH_TO_WIDGET_INSTANCE, buffer.data()); if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { Buffer buffer(response.getData()); CipInt result; buffer >> result; Logger(LogLevel::INFO) << result; } } catch (std::exception &e) { Logger(LogLevel::ERROR) << e.what(); return -1; } return 0; } ``` -------------------------------- ### Close IO Connection Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/implicit_messaging.rst Demonstrates the method to politely close an established IO connection using the ConnectionManager. ```cpp connectionManager.forwardClose(si, io); ``` -------------------------------- ### Disable Vendor Source Files using CMake Build Flag Source: https://github.com/nimbuscontrols/eipscanner/blob/master/README.md Provides an alternative method to disable vendor-specific source files during the CMake build process by setting a build flag. This is useful for command-line builds. ```shell cmake -DENABLE_VENDOR_SRC=OFF .. cmake --build . ``` -------------------------------- ### Read EDS File using FileObject in C++ Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/standard_objects/file_object.rst Demonstrates how to use the FileObject class to read an EDS file from an EIP device. It utilizes the beginUpload method to initiate the transfer and handleTransfers to process incoming data chunks. ```cpp // Example usage of FileObject to read an EDS file FileObject fileObject(session); fileObject.beginUpload(fileId, [](const std::vector& data) { // Handler to save received chunks saveToFile(data); }); fileObject.handleTransfers(); ``` -------------------------------- ### Configure CMake for Google Test and Google Mock Source: https://github.com/nimbuscontrols/eipscanner/blob/master/test/CMakeLists.txt This script configures the build environment by setting include directories, finding GTest and GMock packages, and defining the test executable. It links the compiled test suite against the EIPScanner library and registers the test with CTest. ```cmake enable_testing() include_directories("${PROJECT_SOURCE_DIR}/src") include_directories("${PROJECT_SOURCE_DIR}/test") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") find_package(GTest REQUIRED) find_package(GMock REQUIRED) add_executable(test_eipscanner cip/TestCipRevision.cpp cip/TestCipString.cpp cip/TestEPath.cpp cip/TestMessageRouterResponse.cpp eip/TestCommonPacket.cpp eip/TestCommonPacketItem.cpp eip/TestCommonPacketItemFactory.cpp eip/TestEncapsPacket.cpp eip/TestEncapsPacketFactory.cpp fileObject/TestFileObjectLoadedState.cpp fileObject/TestFileObjectUploadInProgressState.cpp sockets/TestEndPoint.cpp utils/TestBuffer.cpp vendor/ra/powerFlex525/TestDPIFaultManager.cpp vendor/ra/powerFlex525/TestDPIFaultObject.cpp vendor/ra/powerFlex525/TestDPIFaultParameter.cpp TestDiscoveryManager.cpp TestIdentityObject.cpp TestMessageRouter.cpp TestParameterObject.cpp test.cpp ) include_directories(${GTEST_INCLUDE_DIRS}) include_directories(${GMOCK_INCLUDE_DIRS}) target_link_libraries(test_eipscanner ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} EIPScanner) add_test(TestEipScanner test_eipscanner) ``` -------------------------------- ### Decode Message Router Response using Buffer Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/explicit_messaging.rst Demonstrates how to verify a successful CIP response and extract data using the Buffer utility. The code checks the general status code before decoding the payload into a CipInt type. ```cpp if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { Buffer buffer(response.getData()); CipInt result; buffer >> result; Logger(LogLevel::INFO) << result; } ``` -------------------------------- ### Discover EtherNet/IP devices using DiscoveryManager Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/discovery.rst The DiscoveryManager::discover method broadcasts a UDP request to identify devices on the network. It returns a vector containing device IP addresses, ports, and identity objects, or an empty vector if no devices are found. ```cpp #include "DiscoveryManager.h" // Example usage of DiscoveryManager DiscoveryManager manager; auto devices = manager.discover(); for (const auto& device : devices) { // Process discovered device information } ``` -------------------------------- ### Read Parameter Count from CIP Device Source: https://github.com/nimbuscontrols/eipscanner/blob/master/docs/source/standard_objects/parameter_object.rst This code snippet demonstrates how to read the 'Max Instance' attribute of the Parameter Object class to determine the total number of parameters available on a CIP device. It utilizes a MessageRouter to send a GET_ATTRIBUTE_SINGLE service request and logs an error if the operation fails. The response data is parsed to extract the parameter count. ```cpp MessageRouter messageRouter; auto response = messageRouter.sendRequest(si , ServiceCodes::GET_ATTRIBUTE_SINGLE , EPath(ParameterObject::CLASS_ID, 0, MAX_INSTANCE)); if (response.getGeneralStatusCode() != GeneralStatusCodes::SUCCESS) { Logger(LogLevel::ERROR) << "Failed to read the count of the parameters"; logGeneralAndAdditionalStatus(response); return -1; } Buffer buffer(response.getData()); CipUint paramsCount; buffer >> paramsCount; ```