├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CMakeLists.txt ├── Dockerfile ├── EIPScanner.pc.in ├── LICENSE ├── README.md ├── cmake └── FindGMock.cmake ├── docker-compose.yml ├── docker ├── gdbserver │ └── Dockerfile └── opener │ └── Dockerfile ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── conf.py │ ├── discovery.rst │ ├── explicit_messaging.rst │ ├── getting_started.rst │ ├── implicit_messaging.rst │ ├── index.rst │ ├── misc │ ├── 8-bit_path_segments.rst │ ├── _index.rst │ ├── byte_buffer.rst │ └── logging.rst │ ├── standard_objects │ ├── _index.rst │ ├── file_object.rst │ ├── identity_object.rst │ └── parameter_object.rst │ └── vendor_specific_objects │ ├── _index.rst │ └── ra │ └── _index.rst ├── examples ├── CMakeLists.txt ├── DiscoveryManagerExample.cpp ├── ExplicitMessagingExample.cpp ├── FileObjectExample.cpp ├── IdentityObjectExample.cpp ├── ImplicitMessagingExample.cpp ├── ParameterObjectExample.cpp └── vendors │ └── yaskawa │ └── mp3300iec │ └── Yaskawa_AssemblyObjectExample.cpp ├── src ├── BaseObject.cpp ├── BaseObject.h ├── CMakeLists.txt ├── ConnectionManager.cpp ├── ConnectionManager.h ├── DiscoveryManager.cpp ├── DiscoveryManager.h ├── FileObject.cpp ├── FileObject.h ├── IOConnection.cpp ├── IOConnection.h ├── IdentityObject.cpp ├── IdentityObject.h ├── MessageRouter.cpp ├── MessageRouter.h ├── ParameterObject.cpp ├── ParameterObject.h ├── SessionInfo.cpp ├── SessionInfo.h ├── SessionInfoIf.h ├── cip │ ├── CipRevision.cpp │ ├── CipRevision.h │ ├── CipString.cpp │ ├── CipString.h │ ├── EPath.cpp │ ├── EPath.h │ ├── GeneralStatusCodes.h │ ├── MessageRouterRequest.cpp │ ├── MessageRouterRequest.h │ ├── MessageRouterResponse.cpp │ ├── MessageRouterResponse.h │ ├── Services.h │ ├── Types.h │ └── connectionManager │ │ ├── ConnectionParameters.h │ │ ├── ForwardCloseRequest.cpp │ │ ├── ForwardCloseRequest.h │ │ ├── ForwardOpenRequest.cpp │ │ ├── ForwardOpenRequest.h │ │ ├── ForwardOpenResponse.cpp │ │ ├── ForwardOpenResponse.h │ │ ├── LargeForwardOpenRequest.cpp │ │ ├── LargeForwardOpenRequest.h │ │ ├── NetworkConnectionParametersBuilder.cpp │ │ ├── NetworkConnectionParametersBuilder.h │ │ └── NetworkConnectionParams.h ├── eip │ ├── CommonPacket.cpp │ ├── CommonPacket.h │ ├── CommonPacketItem.cpp │ ├── CommonPacketItem.h │ ├── CommonPacketItemFactory.cpp │ ├── CommonPacketItemFactory.h │ ├── EncapsPacket.cpp │ ├── EncapsPacket.h │ ├── EncapsPacketFactory.cpp │ └── EncapsPacketFactory.h ├── fileObject │ ├── FileObjectEmptyState.cpp │ ├── FileObjectEmptyState.h │ ├── FileObjectLoadedState.cpp │ ├── FileObjectLoadedState.h │ ├── FileObjectNonExistentState.cpp │ ├── FileObjectNonExistentState.h │ ├── FileObjectState.cpp │ ├── FileObjectState.h │ ├── FileObjectUploadInProgressState.cpp │ └── FileObjectUploadInProgressState.h ├── sockets │ ├── BaseSocket.cpp │ ├── BaseSocket.h │ ├── EndPoint.cpp │ ├── EndPoint.h │ ├── Platform.cpp │ ├── Platform.h │ ├── TCPSocket.cpp │ ├── TCPSocket.h │ ├── UDPBoundSocket.cpp │ ├── UDPBoundSocket.h │ ├── UDPSocket.cpp │ └── UDPSocket.h ├── utils │ ├── Buffer.cpp │ ├── Buffer.h │ ├── Logger.cpp │ └── Logger.h └── vendor │ ├── CMakeLists.txt │ ├── ra │ └── powerFlex525 │ │ ├── DPIFaultCode.cpp │ │ ├── DPIFaultCode.h │ │ ├── DPIFaultManager.cpp │ │ ├── DPIFaultManager.h │ │ ├── DPIFaultObject.cpp │ │ ├── DPIFaultObject.h │ │ ├── DPIFaultParameter.cpp │ │ └── DPIFaultParameter.h │ └── yaskawa │ └── mp3300iec │ ├── Yaskawa_EPath.cpp │ ├── Yaskawa_EPath.h │ ├── Yaskawa_MessageRouter.cpp │ ├── Yaskawa_MessageRouter.h │ ├── Yaskawa_MessageRouterRequest.cpp │ └── Yaskawa_MessageRouterRequest.h └── test ├── CMakeLists.txt ├── Mocks.h ├── TestDiscoveryManager.cpp ├── TestIdentityObject.cpp ├── TestMessageRouter.cpp ├── TestParameterObject.cpp ├── cip ├── TestCipRevision.cpp ├── TestCipString.cpp ├── TestEPath.cpp └── TestMessageRouterResponse.cpp ├── eip ├── TestCommonPacket.cpp ├── TestCommonPacketItem.cpp ├── TestCommonPacketItemFactory.cpp ├── TestEncapsPacket.cpp └── TestEncapsPacketFactory.cpp ├── fileObject ├── Mocks.h ├── TestFileObjectLoadedState.cpp └── TestFileObjectUploadInProgressState.cpp ├── sockets └── TestEndPoint.cpp ├── test.cpp ├── utils └── TestBuffer.cpp └── vendor └── ra └── powerFlex525 ├── TestDPIFaultManager.cpp ├── TestDPIFaultObject.cpp └── TestDPIFaultParameter.cpp /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Place, the code snippet that causes the issue 15 | 16 | Example: 17 | 18 | ```cpp 19 | 20 | auto si = std::make_shared("172.28.1.3", 0xAF12); // Here I have an exception!! 21 | 22 | ``` 23 | 24 | **Expected behavior** 25 | A clear and concise description of what you expected to happen. 26 | 27 | **Screenshots** 28 | If applicable, add screenshots to help explain your problem. 29 | 30 | **Wireshark dump** 31 | If applicable, add wireshark dump 32 | 33 | **Logs** 34 | Provide the logs of the library. 35 | 36 | 37 | **Environment (please complete the following information):** 38 | - OS: [e.g. iOS] 39 | - Compiler: [clang-9.0] 40 | - Version [e.g. 22] 41 | 42 | **Additional context** 43 | Add any other context about the problem here. 44 | 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when ... 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /cmake-* 2 | /build 3 | .idea 4 | docs/build/ 5 | docs/source/doxyoutput/ 6 | docs/source/api/ 7 | !docs/source/api/library_root.rst 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | dist: bionic 4 | compiler: 5 | - clang 6 | - gcc 7 | os: 8 | - linux 9 | 10 | install: 11 | - cd ~ 12 | - git clone https://github.com/google/googletest.git gtest 13 | - cd gtest 14 | - git checkout release-1.8.1 15 | - cmake . 16 | - make install DESTDIR=~/.local -j 17 | 18 | script: 19 | - cd ~/build/nimbuscontrols/EIPScanner/ 20 | - cmake -DTEST_ENABLED=ON -DEXAMPLE_ENABLED=ON -DGTEST_ROOT=/home/travis/.local/usr/local/ -DGMOCK_ROOT=/home/travis/.local/usr/local/ . 21 | - cmake --build . 22 | - test/test_eipscanner 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release 1.1.0 2 | 3 | **Features**: 4 | 5 | * add connection timeout for SessionInfo #21, @flipback 6 | * add Identity Object #5, @flipback 7 | * add utils::LogAppenderIf to customise logging #5, @flipback 8 | * add support of LWORD type #6, @flipback 9 | * add support of PowerFlex525 DIP Fault Object #6, @flipback 10 | * add discovery EIP devices in the network #8, @flipback 11 | * add support of LINT and ULINT types #17, @flipback 12 | * add ReadOnly flag to ParameterObject #19, @flipback 13 | 14 | **Bugs**: 15 | 16 | * fix implicit messaging on OSX and check connection sizes #10, @flipback 17 | * fix wrong encoding float and double types #12, @flipback 18 | 19 | **Documentation**: 20 | 21 | * init Sphinx documentation #7, @flipback, @jadamroth 22 | 23 | # Release 1.0.0 (2019-12-08) 24 | 25 | Initial release 26 | 27 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | set(EIPSCANNER_MAJOR_VERSION 1) 4 | set(EIPSCANNER_MINOR_VERSION 1) 5 | set(EIPSCANNER_PATCH_VERSION 0) 6 | set(EIPSCANNER_FULL_VERSION ${EIPSCANNER_MAJOR_VERSION}.${EIPSCANNER_MINOR_VERSION}.${EIPSCANNER_PATCH_VERSION}) 7 | 8 | project(EIPScanner 9 | VERSION ${EIPSCANNER_FULL_VERSION} 10 | DESCRIPTION "Free implementation of EtherNet/IP in C++" 11 | HOMEPAGE_URL "https://github.com/nimbuscontrols/EIPScanner" 12 | ) 13 | 14 | set(CMAKE_CXX_STANDARD 20) 15 | option(ENABLE_VENDOR_SRC "Enable vendor source" ON) 16 | option(TEST_ENABLED "Enable unit test" OFF) 17 | option(EXAMPLE_ENABLED "Build examples" OFF) 18 | 19 | if (WIN32) 20 | # Needed on MinGW with GCC 10 or lower 21 | add_compile_definitions(_WIN32_WINNT=0x0600) 22 | 23 | # Prevent std::numeric_limits::max collision 24 | if (MSVC) 25 | add_compile_definitions(NOMINMAX) 26 | endif() 27 | endif() 28 | 29 | add_subdirectory(src) 30 | if (EXAMPLE_ENABLED) 31 | add_subdirectory(examples) 32 | endif() 33 | 34 | if (TEST_ENABLED) 35 | add_subdirectory(test) 36 | endif() 37 | 38 | 39 | configure_file(EIPScanner.pc.in EIPScanner.pc @ONLY) 40 | 41 | install(FILES ${CMAKE_BINARY_DIR}/EIPScanner.pc 42 | DESTINATION lib/pkgconfig) 43 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcc:8 2 | 3 | RUN apt-get update && apt-get install -y git cmake 4 | 5 | RUN mkdir /git 6 | WORKDIR /git 7 | 8 | RUN git clone https://github.com/google/googletest.git \ 9 | && cd /git/googletest \ 10 | && git checkout release-1.8.1 \ 11 | && cmake . \ 12 | && cmake --build . --target install \ 13 | && rm /git/googletest -rf 14 | 15 | WORKDIR / 16 | ADD / /code 17 | RUN mkdir build 18 | WORKDIR build 19 | RUN cmake -DTEST_ENABLED=ON -DEXAMPLE_ENABLED=ON /code 20 | RUN cmake --build . -- -j 2 -------------------------------------------------------------------------------- /EIPScanner.pc.in: -------------------------------------------------------------------------------- 1 | prefix="@CMAKE_INSTALL_PREFIX@" 2 | exec_prefix="${prefix}" 3 | libdir="${prefix}/lib" 4 | includedir="${prefix}/include" 5 | 6 | Name: @CMAKE_PROJECT_NAME@ 7 | Description: @CMAKE_PROJECT_DESCRIPTION@ 8 | URL: @CMAKE_PROJECT_HOMEPAGE_URL@ 9 | Version: @EIPSCANNER_FULL_VERSION@ 10 | Cflags: -I"${includedir}" -I"${includedir}/@CMAKE_PROJECT_NAME@" 11 | Libs: -L"${libdir}" -l@CMAKE_PROJECT_NAME@ 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Aleksey Timin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EIPScanner 2 | 3 | [![Build Status](https://travis-ci.com/nimbuscontrols/EIPScanner.svg?branch=master)](https://travis-ci.com/nimbuscontrols/EIPScanner) 4 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/nimbuscontrols/EIPScanner) 5 | 6 | 7 | Free implementation of Ethernet/IP scanner in C++. 8 | 9 | 10 | ## Features 11 | 12 | * Explicit messaging 13 | * Implicit messaging (only point-to-point) 14 | * Discovery 15 | 16 | CIP Standard objects: 17 | 18 | * File Object (upload only) 19 | * Parameter Object (read only) 20 | * Identity Object 21 | 22 | Vendor specific objects: 23 | 24 | * RA DPI Fault Object 25 | 26 | ## Requirements 27 | 28 | * CMake 3.5 and higher 29 | * C++20 compiler (tested with GCC and MinGW) 30 | * Linux, MacOS, and Windows 31 | 32 | ## Installing 33 | 34 | ```shell 35 | $ mkdir build && cd build 36 | $ cmake .. 37 | $ cmake --build . --target install 38 | ``` 39 | 40 | ## Usage 41 | 42 | To see how to work with the library, look into `examples` directory 43 | 44 | ## Contribution 45 | 46 | We welcome outside contributions for any improvements, features, or new devices (see below) to the library. Please create a pull request with unit/mock tests 47 | 48 | ## Vendor specific objects & devices 49 | 50 | Our goal is to maintain an active device catalog of all EtherNet/IP devices and their objects that we encounter. We encourage others to contribute to this communal device catalog in /src/vendor/. 51 | 52 | All vendor source files are automatically enabled. If you'd like to disable vendor files, you can disable with any of the below options. 53 | 1) set this option in CMakeLists.txt 54 | ```cmake 55 | option(ENABLE_VENDOR_SRC "Enable vendor source" OFF) 56 | ``` 57 | 2) set the cmake build flag 58 | ```shell 59 | -DENABLE_VENDOR_SRC=OFF 60 | ``` 61 | 3) manually comment needed files in src/vendor/CMakeLists.txt 62 | 63 | ## Authors 64 | 65 | Aleksey Timin 66 | 67 | Adam Roth 68 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | eip_scanner: 5 | build: . 6 | volumes: 7 | - .:/code 8 | command: examples/discovery_example 9 | networks: 10 | testing_net: 11 | ipv4_address: 172.28.1.1 12 | depends_on: 13 | - opener_adapter 14 | 15 | opener_adapter: 16 | build: docker/opener 17 | working_dir: /OpENer/bin/posix/src/ports/POSIX 18 | command: ./OpENer eth0 19 | ports: 20 | - "44818:44818" 21 | networks: 22 | testing_net: 23 | ipv4_address: 172.28.1.3 24 | 25 | opener_adapter2: 26 | build: docker/opener 27 | working_dir: /OpENer/bin/posix/src/ports/POSIX 28 | command: ./OpENer eth0 29 | networks: 30 | testing_net: 31 | ipv4_address: 172.28.1.7 32 | 33 | gdbserver: 34 | build: 35 | context: docker/gdbserver 36 | security_opt: # options needed for gdb debugging 37 | - seccomp:unconfined 38 | - apparmor:unconfined 39 | ports: 40 | - "7776:22" 41 | - "7777:7777" 42 | networks: 43 | testing_net: 44 | ipam: 45 | driver: default 46 | config: 47 | - subnet: 172.28.0.0/16 -------------------------------------------------------------------------------- /docker/gdbserver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcc:8 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | openssh-server gdb gdbserver rsync 5 | 6 | RUN mkdir /var/run/sshd 7 | RUN echo 'root:root' | chpasswd 8 | RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config 9 | 10 | # SSH login fix. Otherwise user is kicked off after login 11 | RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd 12 | 13 | ENV NOTVISIBLE "in users profile" 14 | RUN echo "export VISIBLE=now" >> /etc/profile 15 | 16 | # 22 for ssh server. 7777 for gdb server. 17 | EXPOSE 22 7777 18 | 19 | RUN useradd -ms /bin/bash debugger 20 | RUN echo 'debugger:pwd' | chpasswd 21 | 22 | ######################################################## 23 | # Add custom packages and development environment here 24 | ######################################################## 25 | 26 | ######################################################## 27 | CMD ["/usr/sbin/sshd", "-D"] -------------------------------------------------------------------------------- /docker/opener/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcc:8 2 | 3 | RUN apt-get update && apt-get install -y git cmake libcap-dev 4 | 5 | RUN git clone https://github.com/EIPStackGroup/OpENer.git --depth=1 6 | WORKDIR /OpENer/bin/posix 7 | RUN sh setup_posix.sh 8 | RUN make -j4 -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | breathe==4.14.0 2 | exhale==0.2.3 3 | Sphinx==2.2.2 4 | sphinx-rtd-theme==0.4.3 5 | sphinxcontrib-applehelp==1.0.1 6 | sphinxcontrib-devhelp==1.0.1 7 | sphinxcontrib-htmlhelp==1.0.2 8 | sphinxcontrib-jsmath==1.0.1 9 | sphinxcontrib-qthelp==1.0.2 10 | sphinxcontrib-serializinghtml==1.1.3 11 | 12 | -------------------------------------------------------------------------------- /docs/source/discovery.rst: -------------------------------------------------------------------------------- 1 | Discovery 2 | ========= 3 | 4 | **EtherNet/IP** allows to discover **EIP** devices in the network 5 | by using UDP broadcast messages. 6 | 7 | **EIPScanner** provides *DiscoveryManager* class for this purpose: 8 | 9 | .. literalinclude:: ../../examples/DiscoveryManagerExample.cpp 10 | 11 | Method *DiscoveryManager::discover* sends broadcast UDP request in the network and 12 | waits for the responses from the devices. It returns a vector of structures for 13 | each discovered device that contain the IP addresses, ports and :ref:`identity_object`\ s. 14 | If there is no device in the network it returns an empty vector. -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to EIPScanner's documentation! 2 | ====================================== 3 | 4 | About 5 | ----- 6 | 7 | EIPScanner is a free implementation of Ethernet/IP scanner in C++. 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | getting_started 14 | explicit_messaging 15 | implicit_messaging 16 | discovery 17 | 18 | standard_objects/_index 19 | vendor_specific_objects/_index 20 | 21 | misc/_index 22 | api/library_root 23 | 24 | Indices and tables 25 | ================== 26 | 27 | * :ref:`genindex` 28 | * :ref:`modindex` 29 | * :ref:`search` 30 | -------------------------------------------------------------------------------- /docs/source/misc/8-bit_path_segments.rst: -------------------------------------------------------------------------------- 1 | 8-bit Path Segments 2 | =================== 3 | 4 | Some devices only support 8-bit path segments. In order to set up 5 | **EIPScanner** to use 8-bit path segments, a *MessageRouter* with the 6 | **USE_8_BIT_PATH_SEGMENTS** flag set should be passed to the *ConnectionManager* 7 | upon construction: 8 | 9 | .. code-block:: cpp 10 | 11 | #include "MessageRouter.h" 12 | #include "ConnectionManager.h" 13 | 14 | using eipScanner::ConnectionManager; 15 | using eipScanner::MessageRouter; 16 | 17 | int main() 18 | { 19 | MessageRouter::SPtr mr_ptr = std::make_shared(MessageRouter::USE_8_BIT_PATH_SEGMENTS); 20 | ConnectionManager _connectionManager = ConnectionManager(mr_ptr); 21 | 22 | /* ConnectionManager now uses 8-bit path segments */ 23 | 24 | return 0; 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/source/misc/_index.rst: -------------------------------------------------------------------------------- 1 | Miscellaneous 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | byte_buffer 9 | logging 10 | 8-bit_path_segments 11 | -------------------------------------------------------------------------------- /docs/source/misc/byte_buffer.rst: -------------------------------------------------------------------------------- 1 | .. _buffer: 2 | 3 | Byte Buffer 4 | =========== 5 | 6 | **EtherNet/IP** has a type system and specifies how the types must 7 | be sends over the network. So we have to decode and encode our C++ types. 8 | To make it easy, **EIPScanner** have class **utils::Buffer**. 9 | 10 | To decode the data that we've received from the network use operator >>: 11 | 12 | .. code-block:: cpp 13 | 14 | Buffer buffer(dataFromNetwork); 15 | cip::CipUint var1; 16 | cip::CipReal var2; 17 | buffer >> var1 >> var2; 18 | 19 | To encode the data that we are going to send, use operator <<: 20 | 21 | .. code-block:: cpp 22 | 23 | Buffer buffer; 24 | cip::CipUint var1 = 10; 25 | cip::CipReal var2 = 2.4; 26 | buffer << var1 << var2; 27 | 28 | buffer.data(); // => data to send 29 | 30 | -------------------------------------------------------------------------------- /docs/source/misc/logging.rst: -------------------------------------------------------------------------------- 1 | Logging 2 | ======= 3 | 4 | **EIPScanner** has a very simple embedded logger *utils::Logger* which 5 | prints the messages into stdout. This is a simple usage example: 6 | 7 | .. code-block:: cpp 8 | 9 | #include "utils/Logger.h" 10 | 11 | using eipScanner::utils::Logger; 12 | using eipScanner::utils::LogLevel; 13 | 14 | int main() { 15 | Logger::setLogLevel(LogLevel::INFO); 16 | 17 | Logger(LogLevel::INFO) << "You will see this message."; 18 | Logger(LogLevel::DEBUG) << "You won't see this message."; 19 | return 0; 20 | } 21 | 22 | You can set the lowest log level for all your application by method *Logger::setLogLevel*. 23 | Here it is *INFO*, so that DEBUG level isn't printed. 24 | 25 | Perhaps, the embedded logger isn't suitable for your application or you use another one. 26 | No problems. Implement interface *utils::LogAppenderIf* and set it with method *Logger::setAppender*. 27 | 28 | .. note:: 29 | The printing messages happens in the destructor of *utils::Logger*. It means if you 30 | want to see message at once, don't save the logger in a variable or a field. -------------------------------------------------------------------------------- /docs/source/standard_objects/_index.rst: -------------------------------------------------------------------------------- 1 | Standard CIP Objects 2 | ==================== 3 | 4 | **EIPScanner** provides some classes, that help the users to work with standard **CIP** objects without knowing 5 | their specifications. 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | :caption: Contents: 10 | 11 | identity_object 12 | parameter_object 13 | file_object 14 | -------------------------------------------------------------------------------- /docs/source/standard_objects/file_object.rst: -------------------------------------------------------------------------------- 1 | File Object (0x37) 2 | ================== 3 | 4 | *File Object* allows to transfer files between a scanner and an adapter. It might be helpful if you want to read *EDS* 5 | file from the **EIP** device. 6 | 7 | **EIPScanner** implements only reading files with *FileObject* class. 8 | Below you can see how to read *EDS* file from the device: 9 | 10 | .. literalinclude:: ../../../examples/FileObjectExample.cpp 11 | 12 | *File Object* sends data split into chunks (max 255), so we need to receive all of them after we've begun uploading. 13 | Method *FileObject::handleTransfers* receives all the chunks and calls a handler, which we set in method *FileObject::beginUpload*, 14 | where we save the received data as a file. -------------------------------------------------------------------------------- /docs/source/standard_objects/identity_object.rst: -------------------------------------------------------------------------------- 1 | .. _identity_object:: 2 | 3 | Identity Object (0x01) 4 | ====================== 5 | 6 | *Identity Object* provides identification and general information about the *CIP* devices. It presents in all 7 | *CIP* products. 8 | 9 | You can read this information with *IdentityObject* class: 10 | 11 | .. literalinclude:: ../../../examples/IdentityObjectExample.cpp 12 | 13 | The constructor takes *instanceID* and *SessionInfo* as arguments to read data via *EtherNet/IP*. 14 | -------------------------------------------------------------------------------- /docs/source/standard_objects/parameter_object.rst: -------------------------------------------------------------------------------- 1 | Parameter Object (0x0f) 2 | ======================= 3 | 4 | *Parameter Object* provides a standard way to access to data and configuration of the **CIP** device. 5 | 6 | **EIPScanner** has special class *ParameterObject* to read an parameter, but before use it you should 7 | know: 8 | 9 | 1. *Instance ID* of the parameter or how many parameters the device has to read all of them 10 | 2. If the device supports full attributes of Paramter Object (sting descriptions, scaling, etc.) or not 11 | 12 | The following example shows how you can get the necessary information from Parameter Object class and read all the 13 | parameters from **EIP** device: 14 | 15 | .. literalinclude:: ../../../examples/ParameterObjectExample.cpp 16 | 17 | The example is pretty big and we need to look at it closer: 18 | 19 | Below we read the number of parameters in the device by reading *Max Instance* attribute of *Parameter Object* class. 20 | For an example, if the number equals 5, we have 5 *Parameter Object* instances with IDs from 1 to 5: 21 | 22 | .. code-block:: cpp 23 | 24 | MessageRouter messageRouter; 25 | auto response = messageRouter.sendRequest(si 26 | , ServiceCodes::GET_ATTRIBUTE_SINGLE 27 | , EPath(ParameterObject::CLASS_ID, 0, MAX_INSTANCE)); 28 | 29 | if (response.getGeneralStatusCode() != GeneralStatusCodes::SUCCESS) { 30 | Logger(LogLevel::ERROR) << "Failed to read the count of the parameters"; 31 | logGeneralAndAdditionalStatus(response); 32 | return -1; 33 | } 34 | 35 | Buffer buffer(response.getData()); 36 | CipUint paramsCount; 37 | buffer >> paramsCount; 38 | 39 | 40 | But to know the number of the parameters is not enough. We need to figure out if the parameters support full attributes. 41 | This information is stored in the second bit of *Parameter Classe Descriptor* and we have to read it: 42 | 43 | .. code-block:: cpp 44 | 45 | response = messageRouter.sendRequest(si 46 | , ServiceCodes::GET_ATTRIBUTE_SINGLE 47 | , EPath(ParameterObject::CLASS_ID, 0, CLASS_DESCRIPTOR)); 48 | 49 | if (response.getGeneralStatusCode() != GeneralStatusCodes::SUCCESS) { 50 | Logger(LogLevel::ERROR) << "Failed to read the class descriptor"; 51 | logGeneralAndAdditionalStatus(response); 52 | return -1; 53 | } 54 | 55 | buffer = Buffer(response.getData()); 56 | CipUint descriptor; 57 | buffer >> descriptor; 58 | 59 | Logger(LogLevel::INFO) << "Read the class descriptor=0x" << std::hex << (int)descriptor; 60 | bool allAttributes = descriptor & SUPPORTS_FULL_ATTRIBUTES; 61 | 62 | Now we know all that we need and we are able to read the parameters and save them in a vector: 63 | 64 | .. code-block:: cpp 65 | 66 | std::vector parameters; 67 | parameters.reserve(paramsCount); 68 | for (int i = 0; i < paramsCount; ++i) { 69 | parameters.emplace_back(i+1, allAttributes, si); 70 | } 71 | 72 | When the parameters are read in its constructors, you can access to its attributes by using the getters: 73 | 74 | .. code-block:: cpp 75 | 76 | if (!parameters.empty()) { 77 | parameters[0].getType(); // Read type 78 | parameters[0].getActualValue(); // 2040 79 | parameters[0].getEngValue(); // 20.4 80 | parameters[0].getName(); // Freq 81 | parameters[0].getUnits(); // Hz 82 | // .. etc 83 | 84 | parameters[0].updateValue(si); 85 | parameters[0].getActualValue(); // updated value 86 | } 87 | 88 | .. note:: 89 | After an instance of *ParamterObject* is created it doesn't update its attributes. You can update only the actual 90 | value with method *ParamterObject::updateValue* -------------------------------------------------------------------------------- /docs/source/vendor_specific_objects/_index.rst: -------------------------------------------------------------------------------- 1 | Vendor Specific CIP Objects 2 | =========================== 3 | 4 | : 5 | .. toctree:: 6 | :maxdepth: 2 7 | :caption: Contents: 8 | 9 | ra/_index.rst 10 | -------------------------------------------------------------------------------- /docs/source/vendor_specific_objects/ra/_index.rst: -------------------------------------------------------------------------------- 1 | Rockwell Automation's CIP Objects 2 | ================================= 3 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories("${PROJECT_SOURCE_DIR}/src") 2 | 3 | add_executable(explicit_messaging ExplicitMessagingExample.cpp) 4 | target_link_libraries(explicit_messaging EIPScanner) 5 | 6 | add_executable(file_object_example FileObjectExample.cpp) 7 | target_link_libraries(file_object_example EIPScanner) 8 | 9 | add_executable(identity_object_example IdentityObjectExample.cpp) 10 | target_link_libraries(identity_object_example EIPScanner) 11 | 12 | add_executable(implicit_messaging ImplicitMessagingExample.cpp) 13 | target_link_libraries(implicit_messaging EIPScanner) 14 | 15 | add_executable(parameter_object_example ParameterObjectExample.cpp) 16 | target_link_libraries(parameter_object_example EIPScanner) 17 | 18 | add_executable(discovery_example DiscoveryManagerExample.cpp) 19 | target_link_libraries(discovery_example EIPScanner) 20 | 21 | add_executable(yaskawa_assembly_object_example vendors/yaskawa/mp3300iec/Yaskawa_AssemblyObjectExample.cpp) 22 | target_link_libraries(yaskawa_assembly_object_example EIPScanner) 23 | 24 | if(WIN32) 25 | target_link_libraries(explicit_messaging ws2_32) 26 | target_link_libraries(file_object_example ws2_32) 27 | target_link_libraries(identity_object_example ws2_32) 28 | target_link_libraries(implicit_messaging ws2_32) 29 | target_link_libraries(parameter_object_example ws2_32) 30 | target_link_libraries(discovery_example ws2_32) 31 | target_link_libraries(yaskawa_assembly_object_example ws2_32) 32 | endif() 33 | -------------------------------------------------------------------------------- /examples/DiscoveryManagerExample.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/17/19. 3 | // 4 | 5 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 6 | #include 7 | #define OS_Windows (1) 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | using eipScanner::DiscoveryManager; 14 | using eipScanner::utils::Logger; 15 | using eipScanner::utils::LogLevel; 16 | 17 | int main() { 18 | Logger::setLogLevel(LogLevel::DEBUG); 19 | 20 | #if OS_Windows 21 | WSADATA wsaData; 22 | int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData); 23 | if (winsockStart != 0) { 24 | Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart; 25 | return EXIT_FAILURE; 26 | } 27 | #endif 28 | 29 | DiscoveryManager discoveryManager("172.28.255.255", 0xAF12, std::chrono::seconds(1)); 30 | auto devices = discoveryManager.discover(); 31 | 32 | for (auto& device : devices) { 33 | Logger(LogLevel::INFO) << "Discovered device: " 34 | << device.identityObject.getProductName() 35 | << " with address " << device.socketAddress.toString(); 36 | } 37 | 38 | #if OS_Windows 39 | WSACleanup(); 40 | #endif 41 | 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /examples/ExplicitMessagingExample.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 6 | #include 7 | #define OS_Windows (1) 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include "SessionInfo.h" 14 | #include "MessageRouter.h" 15 | #include "ConnectionManager.h" 16 | #include "utils/Logger.h" 17 | #include "utils/Buffer.h" 18 | 19 | using namespace eipScanner::cip; 20 | using eipScanner::SessionInfo; 21 | using eipScanner::MessageRouter; 22 | using eipScanner::ConnectionManager; 23 | using eipScanner::cip::connectionManager::ConnectionParameters; 24 | using eipScanner::cip::connectionManager::NetworkConnectionParams; 25 | using eipScanner::utils::Buffer; 26 | using eipScanner::utils::Logger; 27 | using eipScanner::utils::LogLevel; 28 | 29 | int main() { 30 | Logger::setLogLevel(LogLevel::DEBUG); 31 | 32 | #if OS_Windows 33 | WSADATA wsaData; 34 | int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData); 35 | if (winsockStart != 0) { 36 | Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart; 37 | return EXIT_FAILURE; 38 | } 39 | #endif 40 | 41 | auto si = std::make_shared("127.0.0.1", 0xAF12, std::chrono::seconds(10)); 42 | auto messageRouter = std::make_shared(); 43 | 44 | // Read attribute 45 | auto response = messageRouter->sendRequest(si, ServiceCodes::GET_ATTRIBUTE_SINGLE, 46 | EPath(0x01, 1, 1), 47 | {}); 48 | 49 | if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { 50 | Buffer buffer(response.getData()); 51 | CipUint vendorId; 52 | buffer >> vendorId; 53 | 54 | Logger(LogLevel::INFO) << "Vendor ID is " << vendorId; 55 | } else { 56 | Logger(LogLevel::ERROR) << "We got error=0x" << std::hex << response.getGeneralStatusCode(); 57 | } 58 | 59 | //Write attribute 60 | // See OpenEr EDS 160 line 61 | Buffer assembly151; 62 | assembly151 << CipUsint(1) 63 | << CipUsint(2) 64 | << CipUsint(3) 65 | << CipUsint(4) 66 | << CipUsint(5) 67 | << CipUsint(6) 68 | << CipUsint(7) 69 | << CipUsint(8) 70 | << CipUsint(9) 71 | << CipUsint(10); 72 | 73 | 74 | response = messageRouter->sendRequest(si, ServiceCodes::SET_ATTRIBUTE_SINGLE, 75 | EPath(0x04, 151, 3), 76 | assembly151.data()); 77 | 78 | if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { 79 | Logger(LogLevel::INFO) << "Writing is successful"; 80 | } else { 81 | Logger(LogLevel::ERROR) << "We got error=0x" << std::hex << response.getGeneralStatusCode(); 82 | } 83 | 84 | #if OS_Windows 85 | WSACleanup(); 86 | #endif 87 | 88 | return EXIT_SUCCESS; 89 | } 90 | -------------------------------------------------------------------------------- /examples/FileObjectExample.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/24/19. 3 | // 4 | 5 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 6 | #include 7 | #define OS_Windows (1) 8 | #endif 9 | 10 | #include 11 | #include "FileObject.h" 12 | #include "utils/Logger.h" 13 | #include "fileObject/FileObjectState.h" 14 | 15 | using namespace eipScanner::cip; 16 | using eipScanner::SessionInfo; 17 | using eipScanner::utils::Logger; 18 | using eipScanner::utils::LogLevel; 19 | using eipScanner::FileObject; 20 | 21 | int main() { 22 | Logger::setLogLevel(LogLevel::DEBUG); 23 | 24 | #if OS_Windows 25 | WSADATA wsaData; 26 | int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData); 27 | if (winsockStart != 0) { 28 | Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart; 29 | return EXIT_FAILURE; 30 | } 31 | #endif 32 | 33 | auto si = std::make_shared("172.28.1.3", 0xAF12); 34 | 35 | FileObject edsFile(0xC8, si); 36 | edsFile.beginUpload(si, [](GeneralStatusCodes status, const std::vector &fileContent) { 37 | if (status == GeneralStatusCodes::SUCCESS) { 38 | std::ofstream outFile("Device.eds", std::ios::out | std::ios::trunc | std::ios::binary); 39 | outFile.write(reinterpret_cast(fileContent.data()), fileContent.size()); 40 | } 41 | }); 42 | 43 | while (edsFile.handleTransfers(si)) { 44 | continue; 45 | }; 46 | 47 | #if OS_Windows 48 | WSACleanup(); 49 | #endif 50 | 51 | return EXIT_SUCCESS; 52 | } 53 | -------------------------------------------------------------------------------- /examples/IdentityObjectExample.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/19/19. 3 | // 4 | 5 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 6 | #include 7 | #define OS_Windows (1) 8 | #endif 9 | 10 | #include "IdentityObject.h" 11 | #include "utils/Logger.h" 12 | 13 | using eipScanner::IdentityObject; 14 | using eipScanner::SessionInfo; 15 | using eipScanner::utils::Logger; 16 | using eipScanner::utils::LogLevel; 17 | 18 | int main() { 19 | Logger::setLogLevel(LogLevel::DEBUG); 20 | 21 | #if OS_Windows 22 | WSADATA wsaData; 23 | int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData); 24 | if (winsockStart != 0) { 25 | Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart; 26 | return EXIT_FAILURE; 27 | } 28 | #endif 29 | 30 | auto si = std::make_shared("172.28.1.3", 0xAF12); 31 | IdentityObject identityObject(1, si); 32 | 33 | Logger(LogLevel::INFO) << identityObject.getVendorId() 34 | << identityObject.getDeviceType() 35 | << identityObject.getProductCode() 36 | << identityObject.getRevision().toString() 37 | << identityObject.getStatus() 38 | << identityObject.getSerialNumber() 39 | << identityObject.getProductName(); 40 | 41 | #if OS_Windows 42 | WSACleanup(); 43 | #endif 44 | 45 | return EXIT_SUCCESS; 46 | } 47 | -------------------------------------------------------------------------------- /examples/ImplicitMessagingExample.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 6 | #include 7 | #define OS_Windows (1) 8 | #endif 9 | 10 | #include 11 | #include 12 | #include "SessionInfo.h" 13 | #include "ConnectionManager.h" 14 | #include "utils/Logger.h" 15 | #include "utils/Buffer.h" 16 | 17 | using namespace eipScanner::cip; 18 | using eipScanner::SessionInfo; 19 | using eipScanner::MessageRouter; 20 | using eipScanner::ConnectionManager; 21 | using eipScanner::cip::connectionManager::ConnectionParameters; 22 | using eipScanner::cip::connectionManager::NetworkConnectionParams; 23 | using eipScanner::utils::Buffer; 24 | using eipScanner::utils::Logger; 25 | using eipScanner::utils::LogLevel; 26 | 27 | int main() { 28 | Logger::setLogLevel(LogLevel::DEBUG); 29 | 30 | #if OS_Windows 31 | WSADATA wsaData; 32 | int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData); 33 | if (winsockStart != 0) { 34 | Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart; 35 | return EXIT_FAILURE; 36 | } 37 | #endif 38 | 39 | auto si = std::make_shared("172.28.1.3", 0xAF12); 40 | 41 | // Implicit messaging 42 | ConnectionManager connectionManager; 43 | 44 | ConnectionParameters parameters; 45 | parameters.connectionPath = {0x20, 0x04,0x24, 151, 0x2C, 150, 0x2C, 100}; // config Assm151, output Assm150, intput Assm100 46 | parameters.o2tRealTimeFormat = true; 47 | parameters.originatorVendorId = 342; 48 | parameters.originatorSerialNumber = 32423; 49 | parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::P2P; 50 | parameters.t2oNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY; 51 | parameters.t2oNetworkConnectionParams |= 32; //size of Assm100 =32 52 | parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::P2P; 53 | parameters.o2tNetworkConnectionParams |= NetworkConnectionParams::SCHEDULED_PRIORITY; 54 | parameters.o2tNetworkConnectionParams |= 32; //size of Assm150 = 32 55 | 56 | parameters.originatorSerialNumber = 0x12345; 57 | parameters.o2tRPI = 1000000; 58 | parameters.t2oRPI = 1000000; 59 | parameters.transportTypeTrigger |= NetworkConnectionParams::CLASS1; 60 | 61 | auto io = connectionManager.forwardOpen(si, parameters); 62 | if (auto ptr = io.lock()) { 63 | ptr->setDataToSend(std::vector(32)); 64 | 65 | ptr->setReceiveDataListener([](auto realTimeHeader, auto sequence, auto data) { 66 | std::ostringstream ss; 67 | ss << "secNum=" << sequence << " data="; 68 | for (auto &byte : data) { 69 | ss << "[" << std::hex << (int) byte << "]"; 70 | } 71 | 72 | Logger(LogLevel::INFO) << "Received: " << ss.str(); 73 | }); 74 | 75 | ptr->setCloseListener([]() { 76 | Logger(LogLevel::INFO) << "Closed"; 77 | }); 78 | } 79 | 80 | int count = 200; 81 | while (connectionManager.hasOpenConnections() && count-- > 0) { 82 | connectionManager.handleConnections(std::chrono::milliseconds(100)); 83 | } 84 | 85 | connectionManager.forwardClose(si, io); 86 | 87 | #if OS_Windows 88 | WSACleanup(); 89 | #endif 90 | 91 | return EXIT_SUCCESS; 92 | } 93 | -------------------------------------------------------------------------------- /examples/ParameterObjectExample.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/4/19. 3 | // 4 | 5 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 6 | #include 7 | #define OS_Windows (1) 8 | #endif 9 | 10 | #include "ParameterObject.h" 11 | #include "utils/Logger.h" 12 | #include "utils/Buffer.h" 13 | 14 | using namespace eipScanner::cip; 15 | using eipScanner::SessionInfo; 16 | using eipScanner::MessageRouter; 17 | using eipScanner::utils::Logger; 18 | using eipScanner::utils::LogLevel; 19 | using eipScanner::ParameterObject; 20 | using eipScanner::utils::Buffer; 21 | 22 | const CipUint MAX_INSTANCE = 2; 23 | const CipUint CLASS_DESCRIPTOR = 8; 24 | const CipUint SUPPORTS_FULL_ATTRIBUTES = 0x2; 25 | 26 | int main() { 27 | Logger::setLogLevel(LogLevel::DEBUG); 28 | 29 | #if OS_Windows 30 | WSADATA wsaData; 31 | int winsockStart = WSAStartup(MAKEWORD(2, 2), &wsaData); 32 | if (winsockStart != 0) { 33 | Logger(LogLevel::ERROR) << "Failed to start WinSock - error code: " << winsockStart; 34 | return EXIT_FAILURE; 35 | } 36 | #endif 37 | 38 | auto si = std::make_shared("172.28.1.3", 0xAF12); 39 | 40 | // Read the number of the parameters 41 | MessageRouter messageRouter; 42 | auto response = messageRouter.sendRequest(si 43 | , ServiceCodes::GET_ATTRIBUTE_SINGLE 44 | , EPath(ParameterObject::CLASS_ID, 0, MAX_INSTANCE)); 45 | 46 | if (response.getGeneralStatusCode() != GeneralStatusCodes::SUCCESS) { 47 | Logger(LogLevel::ERROR) << "Failed to read the count of the parameters"; 48 | logGeneralAndAdditionalStatus(response); 49 | return -1; 50 | } 51 | 52 | Buffer buffer(response.getData()); 53 | CipUint paramsCount; 54 | buffer >> paramsCount; 55 | 56 | Logger(LogLevel::INFO) << "The device has " << paramsCount << "parameters"; 57 | 58 | // Read Parameter Class Descriptor 59 | response = messageRouter.sendRequest(si 60 | , ServiceCodes::GET_ATTRIBUTE_SINGLE 61 | , EPath(ParameterObject::CLASS_ID, 0, CLASS_DESCRIPTOR)); 62 | 63 | if (response.getGeneralStatusCode() != GeneralStatusCodes::SUCCESS) { 64 | Logger(LogLevel::ERROR) << "Failed to read the class descriptor"; 65 | logGeneralAndAdditionalStatus(response); 66 | return -1; 67 | } 68 | 69 | buffer = Buffer(response.getData()); 70 | CipUint descriptor; 71 | buffer >> descriptor; 72 | 73 | Logger(LogLevel::INFO) << "Read the class descriptor=0x" << std::hex << (int)descriptor; 74 | bool allAttributes = descriptor & SUPPORTS_FULL_ATTRIBUTES; 75 | 76 | // Read and save parameters in a vector 77 | std::vector parameters; 78 | parameters.reserve(paramsCount); 79 | for (int i = 0; i < paramsCount; ++i) { 80 | parameters.emplace_back(i+1, allAttributes, si); 81 | } 82 | 83 | if (!parameters.empty()) { 84 | parameters[0].getType(); // Read type 85 | parameters[0].getActualValue(); // 2040 86 | parameters[0].getEngValue(); // 20.4 87 | parameters[0].getName(); // Freq 88 | parameters[0].getUnits(); // Hz 89 | // .. etc 90 | 91 | parameters[0].updateValue(si); 92 | parameters[0].getActualValue(); // updated value 93 | } 94 | 95 | #if OS_Windows 96 | WSACleanup(); 97 | #endif 98 | 99 | return EXIT_SUCCESS; 100 | } 101 | -------------------------------------------------------------------------------- /src/BaseObject.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/9/19. 3 | // 4 | 5 | #include "BaseObject.h" 6 | 7 | namespace eipScanner { 8 | 9 | BaseObject::BaseObject(cip::CipUint classId, cip::CipUint instanceId) 10 | : _classId(classId) 11 | , _instanceId(instanceId) { 12 | } 13 | 14 | cip::CipUint BaseObject::getClassId() const { 15 | return _classId; 16 | } 17 | 18 | cip::CipUint BaseObject::getInstanceId() const { 19 | return _instanceId; 20 | } 21 | } -------------------------------------------------------------------------------- /src/BaseObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/9/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_BASEOBJECT_H 6 | #define EIPSCANNER_BASEOBJECT_H 7 | 8 | #include "cip/Types.h" 9 | 10 | namespace eipScanner { 11 | /** 12 | * @class BaseObject 13 | * 14 | * @brief Base class for all the CIP Objects 15 | */ 16 | class BaseObject { 17 | public: 18 | /** 19 | * @brief Creates an CIP instance 20 | * @param classId the class ID of the new instance 21 | * @param instanceId the instance ID of the new instanc 22 | */ 23 | BaseObject(cip::CipUint classId, cip::CipUint instanceId); 24 | 25 | /** 26 | * @brief Gets the class ID of the instance 27 | * @return 28 | */ 29 | cip::CipUint getClassId() const; 30 | 31 | /** 32 | * @brief Gets the instance ID of the instance 33 | * @return 34 | */ 35 | cip::CipUint getInstanceId() const; 36 | 37 | private: 38 | cip::CipUint _classId; 39 | cip::CipUint _instanceId; 40 | }; 41 | } 42 | 43 | #endif // EIPSCANNER_BASEOBJECT_H 44 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${CMAKE_CURRENT_LIST_DIR}) 2 | 3 | set(SOURCE_FILES 4 | cip/connectionManager/ForwardCloseRequest.cpp 5 | cip/connectionManager/ForwardOpenRequest.cpp 6 | cip/connectionManager/LargeForwardOpenRequest.cpp 7 | cip/connectionManager/ForwardOpenResponse.cpp 8 | cip/connectionManager/NetworkConnectionParametersBuilder.cpp 9 | 10 | cip/CipRevision.cpp 11 | cip/EPath.cpp 12 | cip/MessageRouterRequest.cpp 13 | cip/MessageRouterResponse.cpp 14 | cip/CipString.cpp 15 | 16 | eip/CommonPacket.cpp 17 | eip/CommonPacketItem.cpp 18 | eip/CommonPacketItemFactory.cpp 19 | eip/EncapsPacket.cpp 20 | eip/EncapsPacketFactory.cpp 21 | 22 | fileObject/FileObjectEmptyState.cpp 23 | fileObject/FileObjectLoadedState.cpp 24 | fileObject/FileObjectNonExistentState.cpp 25 | fileObject/FileObjectState.cpp 26 | fileObject/FileObjectUploadInProgressState.cpp 27 | 28 | sockets/BaseSocket.cpp 29 | sockets/EndPoint.cpp 30 | sockets/TCPSocket.cpp 31 | sockets/UDPBoundSocket.cpp 32 | sockets/UDPSocket.cpp 33 | sockets/Platform.cpp 34 | 35 | utils/Logger.cpp 36 | utils/Buffer.cpp 37 | 38 | BaseObject.cpp 39 | ConnectionManager.cpp 40 | DiscoveryManager.cpp 41 | FileObject.cpp 42 | IdentityObject.cpp 43 | IOConnection.cpp 44 | MessageRouter.cpp 45 | ParameterObject.cpp 46 | SessionInfo.cpp) 47 | 48 | # if vendor scripts are enabled 49 | if(ENABLE_VENDOR_SRC) 50 | add_subdirectory(vendor) 51 | endif() 52 | 53 | add_library(EIPScanner SHARED ${SOURCE_FILES} ${VENDOR_FILES}) 54 | add_library(EIPScannerS STATIC ${SOURCE_FILES} ${VENDOR_FILES}) 55 | 56 | if(WIN32) 57 | target_link_libraries(EIPScanner ws2_32) 58 | target_link_libraries(EIPScannerS ws2_32) 59 | endif() 60 | 61 | set_target_properties( 62 | EIPScanner 63 | PROPERTIES 64 | VERSION ${EIPSCANNER_FULL_VERSION} 65 | SOVERSION ${EIPSCANNER_MAJOR_VERSION}) 66 | 67 | install(TARGETS EIPScanner EIPScannerS 68 | LIBRARY 69 | DESTINATION lib 70 | ARCHIVE 71 | DESTINATION lib) 72 | 73 | install(DIRECTORY ${PROJECT_SOURCE_DIR}/src/ 74 | DESTINATION include/EIPScanner 75 | FILES_MATCHING PATTERN "*.h*") 76 | -------------------------------------------------------------------------------- /src/ConnectionManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CONNECTIONMANAGER_H 6 | #define EIPSCANNER_CONNECTIONMANAGER_H 7 | 8 | #include 9 | #include "MessageRouter.h" 10 | #include "IOConnection.h" 11 | #include "cip/connectionManager/ConnectionParameters.h" 12 | #include "cip/Services.h" 13 | #include "cip/Types.h" 14 | #include "sockets/UDPBoundSocket.h" 15 | 16 | namespace eipScanner { 17 | /** 18 | * @class ConnectionManager 19 | * 20 | * @brief Implements the implicit messaging with EIP adapter 21 | */ 22 | class ConnectionManager { 23 | public: 24 | /** 25 | * @brief Default constructor 26 | */ 27 | ConnectionManager(); 28 | 29 | /** 30 | * @note used fot testing 31 | * @param messageRouter 32 | */ 33 | explicit ConnectionManager(const MessageRouter::SPtr& messageRouter); 34 | 35 | /** 36 | * @brief Default destructor 37 | */ 38 | ~ConnectionManager(); 39 | 40 | /** 41 | * @brief Opens an EIP IO connection with the EIP adapter 42 | * @param si the EIP session for explicit messaging 43 | * @param connectionParameters the parameters of the connection 44 | * @param isLarge use large forward open if true 45 | * @return weak pointer to the created connection or nullptr if got an error 46 | */ 47 | IOConnection::WPtr forwardOpen(const SessionInfoIf::SPtr& si, cip::connectionManager::ConnectionParameters connectionParameters, bool isLarge = false); 48 | 49 | /** 50 | * @brief Opens an EIP IO connection with the EIP adapter 51 | * @param si the EIP session for explicit messaging 52 | * @param connectionParameters the parameters of the connection 53 | * @return weak pointer to the created connection or nullptr if got an error 54 | */ 55 | IOConnection::WPtr largeForwardOpen(const SessionInfoIf::SPtr& si, cip::connectionManager::ConnectionParameters connectionParameters); 56 | 57 | 58 | /** 59 | * @brief Closes an EIP IO connection 60 | * @param si the EIP session for explicit messaging 61 | * @param ioConnection the connection to close 62 | */ 63 | void forwardClose(const SessionInfoIf::SPtr& si, const IOConnection::WPtr& ioConnection); 64 | 65 | /** 66 | * @brief Handles active connections 67 | * @param timeout the timeout of receiving the data by select function 68 | */ 69 | void handleConnections(std::chrono::milliseconds timeout); 70 | 71 | /** 72 | * 73 | * @return true if there are some opened IO connections 74 | */ 75 | bool hasOpenConnections() const; 76 | private: 77 | MessageRouter::SPtr _messageRouter; 78 | std::map _connectionMap; 79 | std::map> _socketMap; 80 | 81 | sockets::UDPBoundSocket::SPtr findOrCreateSocket(const sockets::EndPoint& endPoint); 82 | cip::CipUint _incarnationId; 83 | }; 84 | } 85 | 86 | #endif // EIPSCANNER_CONNECTIONMANAGER_H 87 | -------------------------------------------------------------------------------- /src/DiscoveryManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/17/19. 3 | // 4 | #include 5 | 6 | #include "eip/EncapsPacketFactory.h" 7 | #include "eip/CommonPacket.h" 8 | #include "sockets/UDPSocket.h" 9 | #include "utils/Logger.h" 10 | #include "utils/Buffer.h" 11 | 12 | #include "DiscoveryManager.h" 13 | 14 | namespace eipScanner { 15 | using namespace cip; 16 | 17 | using eip::EncapsPacketFactory; 18 | using eip::EncapsPacket; 19 | using eip::EncapsStatusCodes; 20 | using eip::EncapsCommands; 21 | using eip::CommonPacket; 22 | 23 | using sockets::EndPoint; 24 | using sockets::UDPSocket; 25 | using utils::LogLevel; 26 | using utils::Logger; 27 | using utils::Buffer; 28 | 29 | DiscoveryManager::DiscoveryManager(const std::string& broadCastAddress, int port, 30 | std::chrono::milliseconds receiveTimout) 31 | : _broadCastAddress(broadCastAddress, port) 32 | , _receiveTimout(receiveTimout) { 33 | } 34 | 35 | DiscoveryManager::~DiscoveryManager() = default; 36 | 37 | IdentityItem::Vec DiscoveryManager::discover() const { 38 | eipScanner::IdentityItem::Vec devices; 39 | 40 | auto socket = makeSocket(); 41 | socket->Send(EncapsPacketFactory().createListIdentityPacket().pack()); 42 | 43 | try { 44 | for(;;) { 45 | const size_t EIP_MAX_PACKET_SIZE = 504; 46 | auto data = socket->Receive(EIP_MAX_PACKET_SIZE); 47 | 48 | CommonPacket commonPacket; 49 | commonPacket.expand(std::vector(data.begin()+EncapsPacket::HEADER_SIZE, data.end())); 50 | 51 | for (int i=0; i < commonPacket.getItems().size(); ++i) { 52 | Buffer buffer(commonPacket.getItems()[i].getData()); 53 | CipUint ignore; 54 | sockets::EndPoint socketAddr("", 0); 55 | 56 | buffer >> ignore >> socketAddr; 57 | 58 | CipUint vendorId, deviceType, productCode; 59 | CipRevision revision; 60 | CipWord status; 61 | CipUdint serialNumber; 62 | CipShortString productName; 63 | 64 | buffer >> vendorId >> deviceType >> productCode 65 | >> revision >> status 66 | >> serialNumber >> productName; 67 | 68 | IdentityObject identityObject(i); 69 | identityObject.setVendorId(vendorId); 70 | identityObject.setDeviceType(deviceType); 71 | identityObject.setProductCode(productCode); 72 | identityObject.setRevision(revision); 73 | identityObject.setStatus(status); 74 | identityObject.setSerialNumber(serialNumber); 75 | identityObject.setProductName(productName.toStdString()); 76 | 77 | devices.push_back(IdentityItem{.identityObject=identityObject, .socketAddress=socketAddr}); 78 | } 79 | } 80 | } catch (std::system_error& er) { 81 | if (er.code().value() != DISCOVERY_SOCKET_RECEIVE_END_ERROR_CODE) { 82 | throw er; 83 | } 84 | } 85 | 86 | return devices; 87 | } 88 | 89 | sockets::BaseSocket::SPtr DiscoveryManager::makeSocket() const { 90 | auto socket = std::make_shared(_broadCastAddress); 91 | socket->setRecvTimeout(_receiveTimout); 92 | 93 | int broadcast = 1; 94 | if(setsockopt(socket->getSocketFd(), SOL_SOCKET, SO_BROADCAST, (char*)&broadcast, sizeof(broadcast)) < 0) { 95 | throw std::system_error(sockets::BaseSocket::getLastError(), sockets::BaseSocket::getErrorCategory()); 96 | } 97 | 98 | return socket; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/DiscoveryManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/17/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_DISCOVERYMANAGER_H 6 | #define EIPSCANNER_DISCOVERYMANAGER_H 7 | 8 | #include "IdentityObject.h" 9 | #include "sockets/Platform.h" 10 | 11 | #if defined (__unix__) || defined(__APPLE__) 12 | #define DISCOVERY_SOCKET_RECEIVE_END_ERROR_CODE (EIPSCANNER_SOCKET_ERROR(EAGAIN)) 13 | #elif defined(_WIN32) || defined(WIN32) || defined(_WIN64) 14 | #define DISCOVERY_SOCKET_RECEIVE_END_ERROR_CODE (EIPSCANNER_SOCKET_ERROR(ETIMEDOUT)) 15 | #endif 16 | 17 | namespace eipScanner { 18 | 19 | /** 20 | * @brief Contains information about EIP device and its address in the network 21 | */ 22 | struct IdentityItem { 23 | using Vec = std::vector; 24 | 25 | IdentityObject identityObject; 26 | sockets::EndPoint socketAddress; 27 | }; 28 | 29 | /** 30 | * @class DiscoveryManager 31 | * @brief Implements the discovery of EIP devices in the network 32 | */ 33 | class DiscoveryManager { 34 | public: 35 | 36 | /** 37 | * @brief Constructor 38 | * @param broadCastAddress the broadcast address to send a request (e.g. 172.28.255.255 or 192.168.1.255) 39 | * @param port the port of the discovery (default 0xAF12) 40 | * @param receiveTimout the timeout to wait for responses from the EIP devices 41 | */ 42 | explicit DiscoveryManager(const std::string& broadCastAddress, int port, std::chrono::milliseconds receiveTimout); 43 | 44 | /** 45 | * @brief default destructor 46 | */ 47 | virtual ~DiscoveryManager(); 48 | 49 | /** 50 | * @brief Discovers the EIP network 51 | * Sends the broadcast request through UDP and collects all the response. 52 | * @return the vector of IdentityItem for each discovered device. 53 | */ 54 | IdentityItem::Vec discover() const; 55 | protected: 56 | virtual sockets::BaseSocket::SPtr makeSocket() const; 57 | 58 | private: 59 | sockets::EndPoint _broadCastAddress; 60 | std::chrono::milliseconds _receiveTimout; 61 | }; 62 | } 63 | 64 | #endif //EIPSCANNER_DISCOVERYMANAGER_H 65 | -------------------------------------------------------------------------------- /src/FileObject.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/21/19. 3 | // 4 | #include 5 | #include "FileObject.h" 6 | #include "fileObject/FileObjectState.h" 7 | #include "utils/Buffer.h" 8 | #include "utils/Logger.h" 9 | #include "cip/Services.h" 10 | 11 | namespace eipScanner { 12 | using utils::Buffer; 13 | using utils::Logger; 14 | using utils::LogLevel; 15 | 16 | FileObject::FileObject(cip::CipUint instanceId, const SessionInfoIf::SPtr& si) 17 | : FileObject(instanceId, si, std::make_shared()){ 18 | 19 | } 20 | 21 | FileObject::FileObject(cip::CipUint instanceId, const SessionInfoIf::SPtr& si, const MessageRouter::SPtr& messageRouter) 22 | : BaseObject(fileObject::FILE_OBJECT_CLASS_ID, instanceId) 23 | , _state(new fileObject::FileObjectState(FileObjectStateCodes::UNKNOWN, *this, instanceId, messageRouter)) { 24 | _state->SyncState(si); 25 | } 26 | 27 | FileObject::~FileObject() = default; 28 | 29 | fileObject::FileObjectState::UPtr& FileObject::getState() { 30 | return _state; 31 | } 32 | 33 | void FileObject::beginUpload(SessionInfoIf::SPtr si, fileObject::EndUploadHandler handle) { 34 | _state->initiateUpload(si, handle); 35 | } 36 | 37 | bool FileObject::handleTransfers(SessionInfoIf::SPtr si) { 38 | return _state->transfer(si); 39 | } 40 | } -------------------------------------------------------------------------------- /src/FileObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/21/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_FILEOBJECT_H 6 | #define EIPSCANNER_FILEOBJECT_H 7 | 8 | #include 9 | 10 | #include "cip/Types.h" 11 | #include "cip/GeneralStatusCodes.h" 12 | #include "MessageRouter.h" 13 | #include "BaseObject.h" 14 | 15 | namespace eipScanner { 16 | namespace fileObject { 17 | class FileObjectState; 18 | using EndUploadHandler = std::function& fileContent)>; 19 | } 20 | 21 | /** 22 | * the state codes of File Object 23 | */ 24 | enum class FileObjectStateCodes : cip::CipUsint { 25 | NONEXISTENT = 0, 26 | FILE_EMPTY = 1, 27 | FILE_LOADED = 2, 28 | TRANSFER_UPLOAD_INITIATED = 3, 29 | TRANSFER_DOWNLOAD_INITIATED = 4, 30 | TRANSFER_UPLOAD_IN_PROGRESS = 5, 31 | TRANSFER_DOWNLOAD_IN_PROGRESS = 6, 32 | 33 | UNKNOWN = 255, 34 | }; 35 | 36 | /** 37 | * @class FileObject 38 | * 39 | * @brief Implements interface to File Object (0x37). 40 | */ 41 | class FileObject : public BaseObject { 42 | friend class fileObject::FileObjectState; 43 | public: 44 | using UPtr = std::unique_ptr; 45 | 46 | /** 47 | * @brief Creates an instance and read the files object state 48 | * @param instanceId the ID of the CIP instance 49 | * @param si the EIP session for explicit messaging 50 | * @throw std::runtime_error 51 | * @throw std::system_error 52 | */ 53 | FileObject(cip::CipUint instanceId, const SessionInfoIf::SPtr& si); 54 | 55 | /** 56 | * @note used for testing 57 | * @param instanceId 58 | * @param si 59 | * @param messageRouter 60 | */ 61 | FileObject(cip::CipUint instanceId, const SessionInfoIf::SPtr& si, const MessageRouter::SPtr& messageRouter); 62 | 63 | /** 64 | * @brief Default destructor 65 | */ 66 | ~FileObject(); 67 | 68 | /** 69 | * @brief Gets the current state of the file 70 | * @return 71 | */ 72 | std::unique_ptr& getState(); 73 | 74 | /** 75 | * @brief Initiates uploading the file from the EIP adapter 76 | * @param si the EIP session for explicit messaging 77 | * @param handle a callback that called when the uploading finishes with an error or not 78 | */ 79 | void beginUpload(SessionInfoIf::SPtr si, fileObject::EndUploadHandler handle); 80 | 81 | /** 82 | * @brief Handle upload transfers 83 | * @return true if uploading is in progress 84 | */ 85 | bool handleTransfers(SessionInfoIf::SPtr si); 86 | 87 | 88 | private: 89 | std::unique_ptr _state; 90 | }; 91 | } 92 | 93 | #endif // EIPSCANNER_FILEOBJECT_H 94 | -------------------------------------------------------------------------------- /src/IOConnection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by flipback on 11/18/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_IOCONNECTION_H 6 | #define EIPSCANNER_IOCONNECTION_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "cip/Types.h" 12 | #include "sockets/UDPSocket.h" 13 | 14 | namespace eipScanner { 15 | class ConnectionManager; 16 | 17 | /** 18 | * @class IOConnection 19 | * 20 | * @brief Implements an implicit EIP connection 21 | * 22 | * @sa eipScanner::ConnectionManager 23 | */ 24 | class IOConnection { 25 | friend class ConnectionManager; 26 | public: 27 | using ReceiveDataHandle = std::function&)>; 28 | using SendDataHandle = std::function&)>; 29 | using CloseHandle = std::function; 30 | 31 | using WPtr=std::weak_ptr; 32 | using SPtr=std::shared_ptr; 33 | 34 | /** 35 | * Default destructor 36 | */ 37 | ~IOConnection(); 38 | 39 | /** 40 | * @brief Sets data to send via the connection each API period 41 | * 42 | * @note Set only data. The sequence counter and the real time format header are append automatically 43 | * @param data the dat to send 44 | */ 45 | void setDataToSend(const std::vector& data); 46 | 47 | /** 48 | * @brief Sets a callback to handle received data 49 | * 50 | * @param handle 51 | */ 52 | void setReceiveDataListener(ReceiveDataHandle handle); 53 | 54 | /** 55 | * @brief Sets a callback to notify that the connection was closed 56 | * @param handle 57 | */ 58 | void setCloseListener(CloseHandle handle); 59 | 60 | /** 61 | * @brief Sets a callback to handle data to send 62 | * 63 | * @param handle 64 | */ 65 | void setSendDataListener(SendDataHandle handle); 66 | 67 | private: 68 | IOConnection(); 69 | void notifyReceiveData(const std::vector &data); 70 | bool notifyTick(); 71 | 72 | cip::CipUdint _o2tNetworkConnectionId; 73 | cip::CipUdint _t2oNetworkConnectionId; 74 | cip::CipUdint _o2tAPI; 75 | cip::CipUdint _t2oAPI; 76 | 77 | size_t _o2tDataSize; 78 | size_t _t2oDataSize; 79 | 80 | bool _o2tFixedSize; 81 | bool _t2oFixedSize; 82 | 83 | cip::CipUdint _o2tTimer; 84 | cip::CipUdint _t2o_timer; 85 | 86 | cip::CipUsint _connectionTimeoutMultiplier; 87 | cip::CipUdint _connectionTimeoutCount; 88 | 89 | cip::CipUdint _o2tSequenceNumber; 90 | cip::CipUdint _t2oSequenceNumber; 91 | cip::CipUdint _serialNumber; 92 | 93 | cip::CipUsint _transportTypeTrigger; 94 | cip::CipBool _o2tRealTimeFormat; 95 | cip::CipBool _t2oRealTimeFormat; 96 | cip::CipUint _sequenceValueCount; 97 | std::vector _connectionPath; 98 | cip::CipUint _originatorVendorId; 99 | cip::CipUdint _originatorSerialNumber; 100 | 101 | std::vector _outputData; 102 | 103 | sockets::UDPSocket::UPtr _socket; 104 | 105 | ReceiveDataHandle _receiveDataHandle; 106 | CloseHandle _closeHandle; 107 | SendDataHandle _sendDataHandle; 108 | 109 | std::chrono::steady_clock::time_point _lastHandleTime; 110 | }; 111 | } 112 | 113 | #endif // EIPSCANNER_IOCONNECTION_H 114 | -------------------------------------------------------------------------------- /src/IdentityObject.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/8/19. 3 | // 4 | 5 | #include "IdentityObject.h" 6 | #include "utils/Buffer.h" 7 | 8 | namespace eipScanner { 9 | using namespace cip; 10 | using utils::Buffer; 11 | 12 | IdentityObject::IdentityObject(cip::CipUint instanceId) 13 | : BaseObject(CLASS_ID, instanceId) 14 | , _vendorId(0) 15 | , _deviceType(0) 16 | , _productCode(0) 17 | , _revision(0,0) 18 | , _status(0) 19 | , _serialNumber(0) 20 | , _productName("") { 21 | } 22 | 23 | IdentityObject::IdentityObject(cip::CipUint instanceId, const SessionInfoIf::SPtr &si) 24 | : IdentityObject(instanceId, si, std::make_shared()){ 25 | } 26 | 27 | IdentityObject::IdentityObject(cip::CipUint instanceId, const SessionInfoIf::SPtr &si, const MessageRouter::SPtr &messageRouter) 28 | : BaseObject(CLASS_ID, instanceId) 29 | , _vendorId(0) 30 | , _deviceType(0) 31 | , _productCode(0) 32 | , _revision(0,0) 33 | , _status(0) 34 | , _serialNumber(0) 35 | , _productName("") { 36 | 37 | auto response = messageRouter->sendRequest( 38 | si, 39 | ServiceCodes::GET_ATTRIBUTE_ALL, 40 | EPath(CLASS_ID, 1), 41 | {}); 42 | 43 | if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { 44 | Buffer buffer(response.getData()); 45 | buffer >> _vendorId >> _deviceType 46 | >> _productCode >> _revision 47 | >> _status >> _serialNumber 48 | >> _productName; 49 | 50 | if (!buffer.isValid()) { 51 | std::runtime_error("Not enough data in the response"); 52 | } 53 | 54 | } else { 55 | logGeneralAndAdditionalStatus(response); 56 | throw std::runtime_error("Failed to read all attributes"); 57 | } 58 | } 59 | 60 | CipUint IdentityObject::getVendorId() const { 61 | return _vendorId; 62 | } 63 | 64 | void IdentityObject::setVendorId(CipUint vendorId) { 65 | _vendorId = vendorId; 66 | } 67 | 68 | CipUint IdentityObject::getDeviceType() const { 69 | return _deviceType; 70 | } 71 | 72 | void IdentityObject::setDeviceType(CipUint deviceType) { 73 | _deviceType = deviceType; 74 | } 75 | 76 | CipUint IdentityObject::getProductCode() const { 77 | return _productCode; 78 | } 79 | 80 | void IdentityObject::setProductCode(CipUint productCode) { 81 | _productCode = productCode; 82 | } 83 | 84 | const CipRevision &IdentityObject::getRevision() const { 85 | return _revision; 86 | } 87 | 88 | void IdentityObject::setRevision(const CipRevision &revision) { 89 | _revision = revision; 90 | } 91 | 92 | CipWord IdentityObject::getStatus() const { 93 | return _status; 94 | } 95 | 96 | void IdentityObject::setStatus(CipWord status) { 97 | _status = status; 98 | } 99 | 100 | CipUdint IdentityObject::getSerialNumber() const { 101 | return _serialNumber; 102 | } 103 | 104 | void IdentityObject::setSerialNumber(CipUdint serialNumber) { 105 | _serialNumber = serialNumber; 106 | } 107 | 108 | std::string IdentityObject::getProductName() const { 109 | return _productName.toStdString(); 110 | } 111 | 112 | void IdentityObject::setProductName(const std::string &productName) { 113 | _productName = CipShortString(productName); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/IdentityObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/8/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_IDENTITYOBJECT_H 6 | #define EIPSCANNER_IDENTITYOBJECT_H 7 | 8 | #include "cip/CipString.h" 9 | #include "cip/CipRevision.h" 10 | #include "cip/Types.h" 11 | #include "SessionInfo.h" 12 | #include "MessageRouter.h" 13 | #include "BaseObject.h" 14 | 15 | namespace eipScanner { 16 | /** 17 | * @class IdentityObject 18 | * 19 | * @brief Implements interface to Identity Object (0x01). 20 | * 21 | * It reads all data from the CIP instance in the constructor 22 | */ 23 | class IdentityObject : public BaseObject { 24 | public: 25 | static const cip::CipUint CLASS_ID = 0x01; 26 | 27 | /** 28 | * @brief Creates an empty instance without any EIP requests 29 | * @param instanceId the ID of the CIP instance 30 | */ 31 | IdentityObject(cip::CipUint instanceId); 32 | 33 | /** 34 | * @brief Creates an instance and reads all its data via EIP 35 | * @param instanceId the ID of the CIP instance 36 | * @param si the EIP session for explicit messaging 37 | * @throw std::runtime_error 38 | * @throw std::system_error 39 | */ 40 | IdentityObject(cip::CipUint instanceId, const SessionInfoIf::SPtr& si); 41 | 42 | /** 43 | * @brief Creates an instance and reads all its data via EIP 44 | * @note Used for testing 45 | * @param instanceId 46 | * @param si 47 | * @param messageRouter 48 | */ 49 | IdentityObject(cip::CipUint instanceId, const SessionInfoIf::SPtr& si, const MessageRouter::SPtr& messageRouter); 50 | 51 | /** 52 | * @brief Gets Vendor ID [AttrID=1] 53 | * @return 54 | */ 55 | cip::CipUint getVendorId() const; 56 | 57 | /** 58 | * @brief Gets Device Type [AttrID=2] 59 | * @return 60 | */ 61 | cip::CipUint getDeviceType() const; 62 | 63 | /** 64 | * @brief Gets Product Code [AttrID=3] 65 | * @return 66 | */ 67 | cip::CipUint getProductCode() const; 68 | 69 | /** 70 | * @brief Gets Revision [AttrID=4] 71 | * @return 72 | */ 73 | const cip::CipRevision &getRevision() const; 74 | 75 | /** 76 | * @brief Gets Status [AttrID=5] 77 | * @return 78 | */ 79 | cip::CipWord getStatus() const; 80 | 81 | /** 82 | * @brief Gets Serial Number [AttrID=6] 83 | * @return 84 | */ 85 | cip::CipUdint getSerialNumber() const; 86 | 87 | /** 88 | * @brief Gets Product Name [AttrID=7] 89 | * @return 90 | */ 91 | std::string getProductName() const; 92 | 93 | /** 94 | * @brief Sets Vendor ID [AttrID=1] 95 | * @param vendorId 96 | */ 97 | void setVendorId(cip::CipUint vendorId); 98 | 99 | /** 100 | * @brief Sets Device Type [AttrID=2] 101 | * @param deviceType 102 | */ 103 | void setDeviceType(cip::CipUint deviceType); 104 | 105 | /** 106 | * @brief Sets Product Code [AttrID=3] 107 | * @param productCode 108 | */ 109 | void setProductCode(cip::CipUint productCode); 110 | 111 | /** 112 | * @brief Sets Revision [AttrID=4] 113 | * @param revision 114 | */ 115 | void setRevision(const cip::CipRevision &revision); 116 | 117 | /** 118 | * @brief Sets Status [AttrID=5] 119 | * @param status 120 | */ 121 | void setStatus(cip::CipWord status); 122 | 123 | /** 124 | * @brief Sets Serial Number [AttrID=6] 125 | * @param serialNumber 126 | */ 127 | void setSerialNumber(cip::CipUdint serialNumber); 128 | 129 | /** 130 | * @brief Sets Product Name [AttrID=7] 131 | * @param productName 132 | */ 133 | void setProductName(const std::string &productName); 134 | 135 | private: 136 | cip::CipUint _vendorId; 137 | cip::CipUint _deviceType; 138 | cip::CipUint _productCode; 139 | cip::CipRevision _revision; 140 | cip::CipWord _status; 141 | cip::CipUdint _serialNumber; 142 | cip::CipShortString _productName; 143 | }; 144 | } 145 | 146 | #endif //EIPSCANNER_IDENTITYOBJECT_H 147 | -------------------------------------------------------------------------------- /src/MessageRouter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include 6 | #include "eip/EncapsPacketFactory.h" 7 | #include "utils/Buffer.h" 8 | #include "MessageRouter.h" 9 | #include "cip/MessageRouterRequest.h" 10 | #include "cip/MessageRouterResponse.h" 11 | #include "eip/CommonPacketItemFactory.h" 12 | #include "eip/CommonPacket.h" 13 | #include "utils/Buffer.h" 14 | #include "utils/Logger.h" 15 | 16 | namespace eipScanner { 17 | using namespace cip; 18 | using namespace utils; 19 | using eip::CommonPacketItemFactory; 20 | using eip::CommonPacket; 21 | using eip::CommonPacketItem; 22 | using eip::EncapsPacket; 23 | using eip::EncapsPacketFactory; 24 | 25 | MessageRouter::MessageRouter(bool use_8_bit_path_segments) 26 | : _use_8_bit_path_segments(use_8_bit_path_segments) 27 | {}; 28 | 29 | MessageRouter::~MessageRouter() = default; 30 | 31 | MessageRouterResponse 32 | MessageRouter::sendRequest(SessionInfoIf::SPtr si, CipUsint service, const EPath &path, 33 | const std::vector &data) const { 34 | return this->sendRequest(si, service, path, data, {}); 35 | } 36 | 37 | MessageRouterResponse 38 | MessageRouter::sendRequest(SessionInfoIf::SPtr si, CipUsint service, const EPath &path, 39 | const std::vector &data, 40 | const std::vector& additionalPacketItems) const { 41 | assert(si); 42 | 43 | Logger(LogLevel::INFO) << "Send request: service=0x" << std::hex << static_cast(service) 44 | << " epath=" << path.toString(); 45 | 46 | MessageRouterRequest request{service, path, data, _use_8_bit_path_segments}; 47 | 48 | CommonPacketItemFactory commonPacketItemFactory; 49 | CommonPacket commonPacket; 50 | commonPacket << commonPacketItemFactory.createNullAddressItem(); 51 | commonPacket << commonPacketItemFactory.createUnconnectedDataItem(request.pack()); 52 | 53 | for(auto& item : additionalPacketItems) { 54 | commonPacket << item; 55 | } 56 | 57 | auto packetToSend = EncapsPacketFactory() 58 | .createSendRRDataPacket(si->getSessionHandle(), 0, commonPacket.pack()); 59 | 60 | auto receivedPacket = si->sendAndReceive(packetToSend); 61 | 62 | Buffer buffer(receivedPacket.getData()); 63 | cip::CipUdint interfaceHandle = 0; 64 | cip::CipUint timeout = 0; 65 | std::vector receivedData(receivedPacket.getData().size() - 6); 66 | 67 | buffer >> interfaceHandle >> timeout >> receivedData; 68 | commonPacket.expand(receivedData); 69 | 70 | MessageRouterResponse response; 71 | const CommonPacketItem::Vec &items = commonPacket.getItems(); 72 | 73 | response.expand(items.at(1).getData()); 74 | if (items.size() > 2) { 75 | response.setAdditionalPacketItems( 76 | CommonPacketItem::Vec(items.begin() + 2, items.end())); 77 | } 78 | 79 | 80 | return response; 81 | } 82 | 83 | MessageRouterResponse 84 | MessageRouter::sendRequest(SessionInfoIf::SPtr si, CipUsint service, const EPath &path) const { 85 | return this->sendRequest(si, service, path, {}, {}); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/MessageRouter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_MESSAGEROUTER_H 6 | #define EIPSCANNER_MESSAGEROUTER_H 7 | 8 | #include 9 | #include "cip/EPath.h" 10 | #include "cip/Services.h" 11 | #include "cip/MessageRouterResponse.h" 12 | #include "eip/CommonPacketItem.h" 13 | #include "SessionInfo.h" 14 | 15 | namespace eipScanner { 16 | /** 17 | * @class MessageRouter 18 | * 19 | * @brief Implements the explicit messaging with EIP adapter 20 | */ 21 | class MessageRouter { 22 | public: 23 | using SPtr = std::shared_ptr; 24 | 25 | static constexpr bool USE_8_BIT_PATH_SEGMENTS = true; 26 | 27 | /** 28 | * @brief Default constructor 29 | */ 30 | MessageRouter(bool use_8_bit_path_segments=false); 31 | 32 | /** 33 | * @brief Default destructor 34 | */ 35 | virtual ~MessageRouter(); 36 | 37 | /** 38 | * @brief Sends an explicit requests to the EIP adapter by calling a CIP service 39 | * @param si the EIP session with the adapter 40 | * @param service the service code (for standard codes see eipScanner::cip::ServiceCodes) 41 | * @param path the path to an element in Object Model that provides the called service 42 | * @param data the encoded arguments of the service 43 | * @param additionalPacketItems (needed only for eipScanner::ConnectionManager) 44 | * @return the received response from the EIP adapter 45 | * @throw std::runtime_error 46 | * @throw std::system_error 47 | */ 48 | virtual cip::MessageRouterResponse sendRequest(SessionInfoIf::SPtr si, cip::CipUsint service, 49 | const cip::EPath& path, const std::vector& data, 50 | const std::vector& additionalPacketItems) const; 51 | 52 | /** 53 | * @brief Sends an explicit requests to the EIP adapter by calling a CIP service 54 | * @param si the EIP session with the adapter 55 | * @param service the service code (for standard codes see eipScanner::cip::ServiceCodes) 56 | * @param path the path to an element in Object Model that provides the called service 57 | * @param data the encoded arguments of the service 58 | * @return the received response from the EIP adapter 59 | * @throw std::runtime_error 60 | * @throw std::system_error 61 | */ 62 | virtual cip::MessageRouterResponse sendRequest(SessionInfoIf::SPtr si, cip::CipUsint service, 63 | const cip::EPath& path, const std::vector& data) const; 64 | 65 | /** 66 | * @brief Sends an explicit requests to the EIP adapter by calling a CIP service 67 | * @param si the EIP session with the adapter 68 | * @param service the service code (for standard codes see eipScanner::cip::ServiceCodes) 69 | * @param path the path to an element in Object Model that provides the called service 70 | * @return the received response from the EIP adapter 71 | * @throw std::runtime_error 72 | * @throw std::system_error 73 | */ 74 | virtual cip::MessageRouterResponse sendRequest(SessionInfoIf::SPtr si, cip::CipUsint service, 75 | const cip::EPath& path) const; 76 | 77 | private: 78 | bool _use_8_bit_path_segments; 79 | }; 80 | } 81 | 82 | 83 | #endif // EIPSCANNER_MESSAGEROUTER_H 84 | -------------------------------------------------------------------------------- /src/SessionInfo.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | 6 | 7 | #include "SessionInfo.h" 8 | #include "utils/Logger.h" 9 | #include "eip/EncapsPacket.h" 10 | #include "eip/EncapsPacketFactory.h" 11 | 12 | namespace eipScanner { 13 | 14 | using utils::Logger; 15 | using utils::LogLevel; 16 | using eip::EncapsPacket; 17 | using eip::EncapsPacketFactory; 18 | using eip::EncapsStatusCodes; 19 | 20 | SessionInfo::SessionInfo(const std::string &host, int port, const std::chrono::milliseconds &timeout) 21 | : _socket{sockets::EndPoint(host, port), timeout} 22 | , _sessionHandle{0} { 23 | _socket.setRecvTimeout(timeout); 24 | 25 | EncapsPacket packet = EncapsPacketFactory().createRegisterSessionPacket(); 26 | packet = sendAndReceive(packet); 27 | 28 | if (packet.getStatusCode() != EncapsStatusCodes::SUCCESS) { 29 | throw std::runtime_error("Failed to register session in " + 30 | _socket.getRemoteEndPoint().toString()); 31 | } 32 | 33 | _sessionHandle = packet.getSessionHandle(); 34 | Logger(LogLevel::INFO) << "Registered session " << _sessionHandle; 35 | } 36 | 37 | SessionInfo::SessionInfo(const std::string &host, int port) 38 | : SessionInfo(host, port, std::chrono::milliseconds(1000)) { 39 | } 40 | 41 | SessionInfo::~SessionInfo() { 42 | EncapsPacket packet = EncapsPacketFactory().createUnRegisterSessionPacket(_sessionHandle); 43 | _socket.Send(packet.pack()); 44 | Logger(LogLevel::INFO) << "Unregistered session " << _sessionHandle; 45 | } 46 | 47 | EncapsPacket SessionInfo::sendAndReceive(const EncapsPacket& packet) const { 48 | _socket.Send(packet.pack()); 49 | auto header = _socket.Receive(EncapsPacket::HEADER_SIZE); 50 | auto length = EncapsPacket::getLengthFromHeader(header); 51 | auto data = _socket.Receive(length); 52 | 53 | header.insert(header.end(), data.begin(), data.end()); 54 | 55 | EncapsPacket recvPacket; 56 | recvPacket.expand(header); 57 | 58 | if (recvPacket.getStatusCode() != EncapsStatusCodes::SUCCESS) { 59 | throw std::runtime_error("Bad encaps packet code =" + std::to_string( 60 | static_cast(recvPacket.getStatusCode()))); 61 | } 62 | 63 | if (_sessionHandle != 0 && recvPacket.getSessionHandle() != _sessionHandle) { 64 | throw std::runtime_error("Wrong session handle received"); 65 | } 66 | 67 | 68 | return recvPacket; 69 | } 70 | 71 | cip::CipUdint SessionInfo::getSessionHandle() const { 72 | return _sessionHandle; 73 | } 74 | 75 | sockets::EndPoint SessionInfo::getRemoteEndPoint() const { 76 | return _socket.getRemoteEndPoint(); 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /src/SessionInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef _SRC_SESSIONINFO_H_ 6 | #define _SRC_SESSIONINFO_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "SessionInfoIf.h" 14 | #include "sockets/TCPSocket.h" 15 | 16 | namespace eipScanner { 17 | /** 18 | * @class SessionInfo 19 | * 20 | * @brief Implementation of EIP session 21 | */ 22 | class SessionInfo : public SessionInfoIf { 23 | public: 24 | using SPtr = std::shared_ptr; 25 | 26 | /** 27 | * @brief Establishes an EIP session with an EIP adapter 28 | * @param host The IP address of the adapter 29 | * @param port The port of the adapter 30 | * @param timeout timout to connect and receive the response 31 | * @throw std::runtime_error 32 | * @throw std::system_error 33 | */ 34 | SessionInfo(const std::string &host, int port, const std::chrono::milliseconds& timeout); 35 | 36 | /** 37 | * @brief Establishes an EIP session with an EIP adapter 38 | * @param host The IP address of the adapter 39 | * @param port The port of the adapter 40 | * @throw std::runtime_error 41 | * @throw std::system_error 42 | */ 43 | SessionInfo(const std::string &host, int port); 44 | 45 | /** 46 | * @brief Default destructor 47 | */ 48 | ~SessionInfo(); 49 | 50 | /** 51 | * @sa SessionInfo::sendAndReceive 52 | * @param packet 53 | * @return 54 | */ 55 | eip::EncapsPacket sendAndReceive(const eip::EncapsPacket& packet) const override; 56 | 57 | /** 58 | * @sa SessionInfo::getSessionHandle 59 | * @return 60 | */ 61 | cip::CipUdint getSessionHandle() const override; 62 | 63 | /** 64 | * @sa SessionInfo::getRemoteEndPoint 65 | * @return 66 | */ 67 | sockets::EndPoint getRemoteEndPoint() const override; 68 | 69 | private: 70 | sockets::TCPSocket _socket; 71 | cip::CipUdint _sessionHandle; 72 | 73 | }; 74 | } 75 | 76 | #endif // _SRC_SESSIONINFO_H_ 77 | -------------------------------------------------------------------------------- /src/SessionInfoIf.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/10/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_SESSIONINFOIF_H 6 | #define EIPSCANNER_SESSIONINFOIF_H 7 | 8 | #include 9 | #include "eip/EncapsPacket.h" 10 | #include "sockets/EndPoint.h" 11 | 12 | namespace eipScanner { 13 | /** 14 | * @class SessionInfoIf 15 | * 16 | * @brief Abstract interface for EIP session 17 | */ 18 | class SessionInfoIf { 19 | public: 20 | using SPtr = std::shared_ptr; 21 | 22 | virtual ~SessionInfoIf(){} 23 | 24 | /** 25 | * Sends and receives EIP Encapsulation packet 26 | * @param packet the EIP Encapsulation packet to send 27 | * @return the received EIP Encapsulation packet 28 | */ 29 | virtual eip::EncapsPacket sendAndReceive(const eip::EncapsPacket &packet) const = 0; 30 | 31 | /** 32 | * Gets the handle of the current EIP session 33 | * @return 34 | */ 35 | virtual cip::CipUdint getSessionHandle() const = 0; 36 | 37 | /** 38 | * Gets the address of the EIP adapter which the session is established with 39 | * @return 40 | */ 41 | virtual sockets::EndPoint getRemoteEndPoint() const = 0; 42 | }; 43 | } 44 | #endif //EIPSCANNER_SESSIONINFOIF_H 45 | -------------------------------------------------------------------------------- /src/cip/CipRevision.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/8/19. 3 | // 4 | 5 | #include "CipRevision.h" 6 | namespace eipScanner { 7 | namespace cip { 8 | 9 | CipRevision::CipRevision() 10 | : _majorRevision(0) 11 | , _minorRevision(0) { 12 | } 13 | 14 | CipRevision::CipRevision(CipUsint majorRevision, CipUsint minorRevision) 15 | : _majorRevision(majorRevision) 16 | , _minorRevision(minorRevision) { 17 | } 18 | 19 | bool CipRevision::operator==(const CipRevision &other) const { 20 | return _majorRevision == other._majorRevision 21 | && _minorRevision == other._minorRevision; 22 | } 23 | 24 | std::string CipRevision::toString() const { 25 | return std::to_string(_majorRevision) + "." + std::to_string(_minorRevision); 26 | } 27 | 28 | CipUsint CipRevision::getMajorRevision() const { 29 | return _majorRevision; 30 | } 31 | 32 | CipUsint CipRevision::getMinorRevision() const { 33 | return _minorRevision; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/cip/CipRevision.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/8/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_CIPREVISION_H 6 | #define EIPSCANNER_CIP_CIPREVISION_H 7 | 8 | #include 9 | #include "Types.h" 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | 14 | class CipRevision { 15 | public: 16 | CipRevision(); 17 | CipRevision(CipUsint majorRevision, CipUsint minorRevision); 18 | 19 | bool operator==(const CipRevision& other) const; 20 | std::string toString() const; 21 | 22 | CipUsint getMajorRevision() const; 23 | CipUsint getMinorRevision() const; 24 | 25 | private: 26 | CipUsint _majorRevision; 27 | CipUsint _minorRevision; 28 | }; 29 | } 30 | } 31 | 32 | #endif // EIPSCANNER_CIPREVISION_H 33 | -------------------------------------------------------------------------------- /src/cip/CipString.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/4/19. 3 | // 4 | 5 | #include "CipString.h" 6 | namespace eipScanner { 7 | namespace cip { 8 | 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /src/cip/CipString.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/4/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_STRINGS_H 6 | #define EIPSCANNER_CIP_STRINGS_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "Types.h" 13 | 14 | namespace eipScanner { 15 | namespace cip { 16 | template 17 | class CipBaseString { 18 | public: 19 | CipBaseString() = default; 20 | explicit CipBaseString(const std::string& string) { 21 | _length = string.size(); 22 | std::copy(string.begin(), string.end(), std::back_inserter(_data)); 23 | } 24 | 25 | CipBaseString(const std::vector& data) { 26 | _length = data.size(); 27 | _data = data; 28 | } 29 | 30 | ~CipBaseString() = default; 31 | 32 | std::string toStdString() const { 33 | return std::string(_data.begin(), _data.end()); 34 | } 35 | 36 | T getLength() const { 37 | return _length; 38 | } 39 | 40 | const std::vector &getData() const { 41 | return _data; 42 | } 43 | 44 | private: 45 | T _length; 46 | std::vector _data; 47 | }; 48 | 49 | using CipShortString = CipBaseString; 50 | using CipString = CipBaseString; 51 | } 52 | } 53 | 54 | #endif // EIPSCANNER_STRINGS_H 55 | -------------------------------------------------------------------------------- /src/cip/EPath.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_EPATH_H 6 | #define EIPSCANNER_CIP_EPATH_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Types.h" 13 | 14 | namespace eipScanner { 15 | namespace cip { 16 | class EPath { 17 | public: 18 | EPath(); 19 | explicit EPath(CipUint classId); 20 | EPath(CipUint classId, CipUint objectId); 21 | EPath(CipUint classId, CipUint objectId, CipUint attributeId); 22 | std::vector packPaddedPath(bool use_8_bit_path_segments=false) const; 23 | void expandPaddedPath(const std::vector& data); 24 | 25 | CipUint getClassId() const; 26 | CipUint getObjectId() const; 27 | CipUint getAttributeId() const; 28 | CipUsint getSizeInWords(bool use_8_bit_path_segments=false) const; 29 | 30 | std::string toString() const; 31 | bool operator==(const EPath& other) const; 32 | 33 | private: 34 | CipUint _classId; 35 | CipUint _objectId; 36 | CipUint _attributeId; 37 | CipUsint _size; 38 | }; 39 | } 40 | } 41 | #endif // EIPSCANNER_CIP_EPATH_H 42 | -------------------------------------------------------------------------------- /src/cip/MessageRouterRequest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include 6 | #include "MessageRouterRequest.h" 7 | #include "EPath.h" 8 | 9 | namespace eipScanner { 10 | namespace cip { 11 | using utils::Buffer; 12 | 13 | MessageRouterRequest::MessageRouterRequest(CipUsint serviceCode, 14 | const EPath& ePath, const std::vector data, bool use_8_bit_path_segments) 15 | : _serviceCode{serviceCode} 16 | , _ePath{ePath} 17 | , _data(data) 18 | , _use_8_bit_path_segments(use_8_bit_path_segments) { 19 | } 20 | 21 | MessageRouterRequest::~MessageRouterRequest() = default; 22 | 23 | std::vector MessageRouterRequest::pack() const { 24 | Buffer buffer; 25 | buffer << _serviceCode 26 | << _ePath.getSizeInWords(_use_8_bit_path_segments) 27 | << _ePath.packPaddedPath(_use_8_bit_path_segments) 28 | << _data; 29 | 30 | return buffer.data(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/cip/MessageRouterRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_MESSAGEROUTERREQUEST_H 6 | #define EIPSCANNER_CIP_MESSAGEROUTERREQUEST_H 7 | 8 | #include 9 | #include 10 | 11 | #include "Services.h" 12 | #include "EPath.h" 13 | 14 | namespace eipScanner { 15 | namespace cip { 16 | class MessageRouterRequest { 17 | public: 18 | MessageRouterRequest(CipUsint serviceCode, const EPath& ePath, const std::vector data, bool use_8_bit_path_segments=false); 19 | ~MessageRouterRequest(); 20 | 21 | std::vector pack() const; 22 | private: 23 | CipUsint _serviceCode; 24 | EPath _ePath; 25 | std::vector _data; 26 | bool _use_8_bit_path_segments; 27 | }; 28 | 29 | } 30 | } 31 | 32 | 33 | #endif // EIPSCANNER_CIP_MESSAGEROUTERREQUEST_H 34 | -------------------------------------------------------------------------------- /src/cip/MessageRouterResponse.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | #include 5 | #include "MessageRouterResponse.h" 6 | #include "utils/Buffer.h" 7 | #include "utils/Logger.h" 8 | 9 | namespace eipScanner { 10 | namespace cip { 11 | using utils::Logger; 12 | using utils::LogLevel; 13 | using utils::Buffer; 14 | 15 | MessageRouterResponse::MessageRouterResponse() 16 | : _serviceCode{ServiceCodes::GET_ATTRIBUTE_ALL} 17 | , _generalStatusCode{GeneralStatusCodes::SUCCESS} 18 | , _additionalStatus(0) 19 | , _data(0) 20 | , _additionalPacketItems(0) { 21 | } 22 | 23 | MessageRouterResponse::~MessageRouterResponse() = default; 24 | 25 | void MessageRouterResponse::expand(const std::vector &data) { 26 | if (data.size() < 4) { 27 | throw std::runtime_error("Message Router response must have at least 4 bytes"); 28 | } 29 | 30 | Buffer buffer(data); 31 | CipUsint reserved, additionalStatusSize; 32 | buffer >> reinterpret_cast(_serviceCode) 33 | >> reserved 34 | >> reinterpret_cast(_generalStatusCode) 35 | >> additionalStatusSize; 36 | 37 | if (additionalStatusSize*2 > data.size() - 4) { 38 | throw std::runtime_error("Additional status has wrong size"); 39 | } 40 | 41 | _additionalStatus.resize(additionalStatusSize); 42 | buffer >> _additionalStatus; 43 | 44 | _data.resize(buffer.size() - buffer.pos()); 45 | buffer >> _data; 46 | } 47 | 48 | ServiceCodes MessageRouterResponse::getServiceCode() const { 49 | return _serviceCode; 50 | } 51 | 52 | GeneralStatusCodes MessageRouterResponse::getGeneralStatusCode() const { 53 | return _generalStatusCode; 54 | } 55 | 56 | const std::vector &MessageRouterResponse::getAdditionalStatus() const { 57 | return _additionalStatus; 58 | } 59 | 60 | const std::vector &MessageRouterResponse::getData() const { 61 | return _data; 62 | } 63 | 64 | void MessageRouterResponse::setGeneralStatusCode(GeneralStatusCodes generalStatusCode) { 65 | _generalStatusCode = generalStatusCode; 66 | } 67 | 68 | void MessageRouterResponse::setData(const std::vector &data) { 69 | _data = data; 70 | } 71 | 72 | const std::vector &MessageRouterResponse::getAdditionalPacketItems() const { 73 | return _additionalPacketItems; 74 | } 75 | 76 | void 77 | MessageRouterResponse::setAdditionalPacketItems(const std::vector &_additionalPacketItems) { 78 | MessageRouterResponse::_additionalPacketItems = _additionalPacketItems; 79 | } 80 | 81 | void logGeneralAndAdditionalStatus(const MessageRouterResponse &response) { 82 | Logger logger(LogLevel::ERROR); 83 | logger << "Message Router error=0x" 84 | << std::hex << response.getGeneralStatusCode() 85 | << " additional statuses "; 86 | for (auto& additionalStatus : response.getAdditionalStatus()) { 87 | logger << "[0x" 88 | << additionalStatus 89 | << "]"; 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/cip/MessageRouterResponse.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_MESSAGEROUTERRESPONSE_H 6 | #define EIPSCANNER_CIP_MESSAGEROUTERRESPONSE_H 7 | 8 | #include 9 | #include 10 | 11 | #include "GeneralStatusCodes.h" 12 | #include "Services.h" 13 | #include "eip/CommonPacketItem.h" 14 | 15 | namespace eipScanner { 16 | namespace cip { 17 | class MessageRouterResponse { 18 | public: 19 | MessageRouterResponse(); 20 | ~MessageRouterResponse(); 21 | 22 | void expand(const std::vector& data); 23 | 24 | GeneralStatusCodes getGeneralStatusCode() const; 25 | ServiceCodes getServiceCode() const; 26 | const std::vector &getAdditionalStatus() const; 27 | const std::vector &getData() const; 28 | const eip::CommonPacketItem::Vec &getAdditionalPacketItems() const; 29 | 30 | void setGeneralStatusCode(GeneralStatusCodes generalStatusCode); 31 | void setData(const std::vector &data); 32 | 33 | void setAdditionalPacketItems(const std::vector &_additionalPacketItems); 34 | 35 | private: 36 | ServiceCodes _serviceCode; 37 | GeneralStatusCodes _generalStatusCode; 38 | std::vector _additionalStatus; 39 | std::vector _data; 40 | eip::CommonPacketItem::Vec _additionalPacketItems; 41 | }; 42 | 43 | void logGeneralAndAdditionalStatus(const MessageRouterResponse& response); 44 | } 45 | } 46 | 47 | #endif // EIPSCANNER_CIP_MESSAGEROUTERRESPONSE_H 48 | -------------------------------------------------------------------------------- /src/cip/Services.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_SERVICES_H 6 | #define EIPSCANNER_CIP_SERVICES_H 7 | 8 | #include "Types.h" 9 | 10 | namespace eipScanner { 11 | namespace cip { 12 | 13 | enum ServiceCodes : CipUsint { 14 | NONE = 0x00, 15 | /* Start CIP common services */ 16 | GET_ATTRIBUTE_ALL = 0X01, 17 | SET_ATTRIBUTE_ALL = 0X02, 18 | GET_ATTRIBUTE_LIST = 0x03, 19 | SET_ATTRIBUTE_LIST = 0x04, 20 | RESET = 0x05, 21 | START = 0x06, 22 | STOP = 0x07, 23 | CREATE_OBJECT_INSTANCE = 0x08, 24 | DELETE_OBJECT_INSTANCE = 0x09, 25 | MULTIPLE_SERVICE_PACKET = 0x0A, 26 | APPLY_ATTRIBUTES = 0x0D, 27 | GET_ATTRIBUTE_SINGLE = 0X0E, 28 | SET_ATTRIBUTE_SINGLE = 0X10, 29 | FIND_NEXT_OBJECT_INSTANCE = 0x11, 30 | ERROR_RESPONSE = 0x14, //DeviceNet only 31 | RESTORE = 0x15, 32 | SAVE = 0x16, 33 | GET_MEMBER = 0x18, 34 | NO_OPERATION = 0x17, 35 | SET_MEMBER = 0x19, 36 | INSERT_MEMBER = 0x1A, 37 | REMOVE_MEMBER = 0x1B, 38 | GROUP_SYNC = 0x1C 39 | /* End CIP common services */ 40 | }; 41 | 42 | } 43 | } 44 | 45 | #endif //EIPSCANNER_SERVICES_H 46 | -------------------------------------------------------------------------------- /src/cip/Types.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_TYPES_H 6 | #define EIPSCANNER_CIP_TYPES_H 7 | 8 | #include 9 | 10 | namespace eipScanner { 11 | namespace cip { 12 | typedef uint8_t CipOctet; /**< 8 bit value that indicates particular data type */ 13 | typedef uint8_t CipBool; /**< Boolean data type */ 14 | typedef uint8_t CipByte; /**< 8-bit bit unsigned integer */ 15 | typedef uint16_t CipWord; /**< 16-bit bit unsigned integer */ 16 | typedef uint32_t CipDword; /**< 32-bit bit unsigned integer */ 17 | typedef uint8_t CipUsint; /**< 8-bit unsigned integer */ 18 | typedef uint16_t CipUint; /**< CipUint 16-bit unsigned integer */ 19 | typedef uint32_t CipUdint; /**< CipUdint 32-bit unsigned integer */ 20 | typedef int8_t CipSint; /**< 8-bit signed integer */ 21 | typedef int16_t CipInt; /**< 16-bit signed integer */ 22 | typedef int32_t CipDint; /**< 32-bit signed integer */ 23 | typedef float CipReal; /**< 32-bit IEEE 754 floating point */ 24 | typedef double CipLreal; /**< 64-bit IEEE 754 floating point */ 25 | typedef uint64_t CipLword; /**< 64-bit bit unsigned integer */ 26 | typedef int64_t CipLint; /**< 64-bit bit signed integer */ 27 | typedef uint64_t CipUlint; /**< 64-bit bit unsigned integer */ 28 | 29 | 30 | enum class CipDataTypes : CipUsint { 31 | ANY = 0x00, /**< data type that can not be directly encoded */ 32 | BOOL = 0xC1, /**< boolean data type */ 33 | SINT = 0xC2, /**< 8-bit signed integer */ 34 | INT = 0xC3, /**< 16-bit signed integer */ 35 | DINT = 0xC4, /**< 32-bit signed integer */ 36 | LINT = 0xC5, /**< 64-bit signed integer */ 37 | USINT = 0xC6, /**< 8-bit unsigned integer */ 38 | UINT = 0xC7, /**< 16-bit unsigned integer */ 39 | UDINT = 0xC8, /**< 32-bit unsigned integer */ 40 | ULINT = 0xC9, /**< 64-bit unsigned integer */ 41 | REAL = 0xCA, /**< Single precision floating point */ 42 | LREAL = 0xCB, /**< Double precision floating point*/ 43 | STIME = 0xCC, /**< Synchronous time information*, type of DINT */ 44 | DATE = 0xCD, /**< Date only*/ 45 | DATE_OF_DAY = 0xCE, /**< Time of day */ 46 | DATE_AND_TIME = 0xCF, /**< Date and time of day */ 47 | STRING = 0xD0, /**< Character string, 1 byte per character */ 48 | BYTE = 0xD1, /**< 8-bit bit string */ 49 | WORD = 0xD2, /**< 16-bit bit string */ 50 | DWORD = 0xD3, /**< 32-bit bit string */ 51 | LWORD = 0xD4, /**< 64-bit bit string */ 52 | STRING2 = 0xD5, /**< Character string, 2 byte per character */ 53 | FTIME = 0xD6, /**< Duration in micro-seconds, high resolution; range of DINT */ 54 | LTIME = 0xD7, /**< Duration in micro-seconds, high resolution, range of LINT */ 55 | ITIME = 0xD8, /**< Duration in milli-seconds, short; range of INT*/ 56 | STRINGN = 0xD9, /**< Character string, N byte per character */ 57 | SHORT_STRING = 0xDA, /**< Character string, 1 byte per character, 1 byte length indicator */ 58 | TIME = 0xDB, /**< Duration in milli-seconds; range of DINT */ 59 | EPATH = 0xDC, /**< CIP path segments*/ 60 | ENG_UNIT = 0xDD, /**< Engineering Units*/ 61 | /* definition of some CIP structs */ 62 | /* need to be validated in IEC 61131-3 subclause 2.3.3 */ 63 | USINT_USINT = 0xA0, /**< Used for CIP Identity attribute 4 Revision*/ 64 | USINT6 = 0xA2, /**< Struct for MAC Address (six USINTs)*/ 65 | MEMBER_LIST = 0xA3, /**< */ 66 | BYTE_ARRAY = 0xA4, /**< */ 67 | }; 68 | } 69 | } 70 | #endif //EIPSCANNER_CIP_TYPES_H 71 | -------------------------------------------------------------------------------- /src/cip/connectionManager/ConnectionParameters.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_CONNECTIONPARAMETERS_H 6 | #define EIPSCANNER_CIP_CONNECTIONPARAMETERS_H 7 | 8 | #include "cip/Types.h" 9 | 10 | namespace eipScanner { 11 | namespace cip { 12 | namespace connectionManager { 13 | 14 | struct ConnectionParameters { 15 | CipUsint priorityTimeTick = 0; 16 | CipUsint timeoutTicks = 0; 17 | CipUdint o2tNetworkConnectionId = 0; 18 | CipUdint t2oNetworkConnectionId = 0; 19 | CipUint connectionSerialNumber = 0; 20 | CipUint originatorVendorId = 0; 21 | CipUdint originatorSerialNumber = 0; 22 | CipUsint connectionTimeoutMultiplier = 0; 23 | CipUdint o2tRPI = 0; 24 | CipUdint o2tNetworkConnectionParams = 0; 25 | CipUdint t2oRPI = 0; 26 | CipUdint t2oNetworkConnectionParams = 0; 27 | CipUsint transportTypeTrigger = 0; 28 | CipUsint connectionPathSize = 0; 29 | CipBool o2tRealTimeFormat = 0; 30 | CipBool t2oRealTimeFormat = 0; 31 | std::vector connectionPath = {}; 32 | }; 33 | } 34 | } 35 | } 36 | #endif //EIPSCANNER_CIP_CONNECTIONPARAMETERS_H 37 | -------------------------------------------------------------------------------- /src/cip/connectionManager/ForwardCloseRequest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/19/19. 3 | // 4 | #include "ForwardCloseRequest.h" 5 | #include "utils/Buffer.h" 6 | 7 | namespace eipScanner { 8 | namespace cip { 9 | namespace connectionManager { 10 | using utils::Buffer; 11 | 12 | connectionManager::ForwardCloseRequest::ForwardCloseRequest() 13 | : _connectionSerialNumber{0} 14 | , _originatorVendorID{0} 15 | , _originatorSerialNumber{0} 16 | , _connectionPath(0) { 17 | } 18 | 19 | connectionManager::ForwardCloseRequest::~ForwardCloseRequest() = default; 20 | 21 | std::vector connectionManager::ForwardCloseRequest::pack() const { 22 | Buffer buffer; 23 | CipUsint timeTick = 0; 24 | CipUsint timeOutTicks = 0; 25 | CipUsint reserved = 0; 26 | 27 | buffer << timeTick 28 | << timeOutTicks 29 | << _connectionSerialNumber 30 | << _originatorVendorID 31 | << _originatorSerialNumber 32 | << static_cast(_connectionPath.size()/2) 33 | << reserved 34 | << _connectionPath; 35 | 36 | return buffer.data(); 37 | } 38 | 39 | void ForwardCloseRequest::setConnectionSerialNumber(CipUint connectionSerialNumber) { 40 | _connectionSerialNumber = connectionSerialNumber; 41 | } 42 | 43 | void ForwardCloseRequest::setOriginatorVendorId(CipUint originatorVendorId) { 44 | _originatorVendorID = originatorVendorId; 45 | } 46 | 47 | void ForwardCloseRequest::setOriginatorSerialNumber(CipUdint originatorSerialNumber) { 48 | _originatorSerialNumber = originatorSerialNumber; 49 | } 50 | 51 | 52 | void ForwardCloseRequest::setConnectionPath(const std::vector &connectionPath) { 53 | _connectionPath = connectionPath; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/cip/connectionManager/ForwardCloseRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/19/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDCLOSEREQUEST_H 6 | #define EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDCLOSEREQUEST_H 7 | 8 | #include 9 | #include "ConnectionParameters.h" 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | namespace connectionManager { 14 | class ForwardCloseRequest { 15 | public: 16 | ForwardCloseRequest(); 17 | ~ForwardCloseRequest(); 18 | std::vector pack() const; 19 | 20 | void setConnectionSerialNumber(CipUint connectionSerialNumber); 21 | void setOriginatorVendorId(CipUint originatorVendorId); 22 | void setOriginatorSerialNumber(CipUdint originatorSerialNumber); 23 | void setConnectionPath(const std::vector &connectionPath); 24 | 25 | private: 26 | CipUint _connectionSerialNumber; 27 | CipUint _originatorVendorID; 28 | CipUdint _originatorSerialNumber; 29 | std::vector_connectionPath; 30 | }; 31 | } 32 | } 33 | } 34 | #endif // EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDOPENREQUEST_H 35 | -------------------------------------------------------------------------------- /src/cip/connectionManager/ForwardOpenRequest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #include "ForwardOpenRequest.h" 6 | 7 | #include 8 | #include 9 | #include "utils/Buffer.h" 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | namespace connectionManager { 14 | 15 | using utils::Buffer; 16 | 17 | ForwardOpenRequest::ForwardOpenRequest(ConnectionParameters params) 18 | : _connectionParameters{std::move(params)} { 19 | } 20 | 21 | ForwardOpenRequest::~ForwardOpenRequest() = default; 22 | 23 | std::vector ForwardOpenRequest::pack() const { 24 | const size_t size = 36; 25 | Buffer buffer(size); 26 | CipUsint reserved = 0; 27 | 28 | CipUint o2tNetworkConnectionParams = static_cast(_connectionParameters.o2tNetworkConnectionParams); 29 | CipUint t2oNetworkConnectionParams = static_cast(_connectionParameters.t2oNetworkConnectionParams); 30 | 31 | buffer << _connectionParameters.priorityTimeTick 32 | << _connectionParameters.timeoutTicks 33 | << _connectionParameters.o2tNetworkConnectionId 34 | << _connectionParameters.t2oNetworkConnectionId 35 | << _connectionParameters.connectionSerialNumber 36 | << _connectionParameters.originatorVendorId 37 | << _connectionParameters.originatorSerialNumber 38 | << _connectionParameters.connectionTimeoutMultiplier 39 | << reserved << reserved << reserved 40 | << _connectionParameters.o2tRPI 41 | << o2tNetworkConnectionParams 42 | << _connectionParameters.t2oRPI 43 | << t2oNetworkConnectionParams 44 | << _connectionParameters.transportTypeTrigger 45 | << _connectionParameters.connectionPathSize; 46 | 47 | assert(buffer.size() == size); 48 | 49 | buffer << _connectionParameters.connectionPath; 50 | return buffer.data(); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/cip/connectionManager/ForwardOpenRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDOPENREQUEST_H 6 | #define EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDOPENREQUEST_H 7 | 8 | #include 9 | #include "ConnectionParameters.h" 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | namespace connectionManager { 14 | class ForwardOpenRequest { 15 | public: 16 | ForwardOpenRequest(ConnectionParameters params); 17 | ~ForwardOpenRequest(); 18 | std::vector pack() const; 19 | 20 | private: 21 | ConnectionParameters _connectionParameters; 22 | }; 23 | } 24 | } 25 | } 26 | #endif // EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDOPENREQUEST_H 27 | -------------------------------------------------------------------------------- /src/cip/connectionManager/ForwardOpenResponse.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #include "ForwardOpenResponse.h" 6 | #include "utils/Buffer.h" 7 | 8 | namespace eipScanner { 9 | namespace cip { 10 | namespace connectionManager { 11 | 12 | using utils::Buffer; 13 | 14 | ForwardOpenResponse::ForwardOpenResponse() 15 | : _o2tNetworkConnectionId{0} 16 | , _t2oNetworkConnectionId{0} 17 | , _connectionSerialNumber{0} 18 | , _originatorVendorId{0} 19 | , _originatorSerialNumber{0} 20 | , _o2tAPI{0} 21 | , _t2oAPI{0} 22 | , _applicationReplaySize{0} 23 | , _applicationReplay{0} { 24 | } 25 | 26 | ForwardOpenResponse::~ForwardOpenResponse() = default; 27 | 28 | void ForwardOpenResponse::expand(const std::vector &data) { 29 | Buffer buffer(data); 30 | CipUsint reserved = 0; 31 | 32 | buffer >> _o2tNetworkConnectionId 33 | >> _t2oNetworkConnectionId 34 | >> _connectionSerialNumber 35 | >> _originatorVendorId 36 | >> _originatorSerialNumber 37 | >> _o2tAPI 38 | >> _t2oAPI 39 | >> _applicationReplaySize 40 | >> reserved; 41 | 42 | _applicationReplay.resize(_applicationReplaySize*2); 43 | buffer >> _applicationReplay; 44 | } 45 | 46 | CipUdint ForwardOpenResponse::getO2TNetworkConnectionId() const { 47 | return _o2tNetworkConnectionId; 48 | } 49 | 50 | CipUdint ForwardOpenResponse::getT2ONetworkConnectionId() const { 51 | return _t2oNetworkConnectionId; 52 | } 53 | 54 | CipUint ForwardOpenResponse::getConnectionSerialNumber() const { 55 | return _connectionSerialNumber; 56 | } 57 | 58 | CipUint ForwardOpenResponse::getOriginatorVendorId() const { 59 | return _originatorVendorId; 60 | } 61 | 62 | CipUdint ForwardOpenResponse::getOriginatorSerialNumber() const { 63 | return _originatorSerialNumber; 64 | } 65 | 66 | CipUdint ForwardOpenResponse::getO2TApi() const { 67 | return _o2tAPI; 68 | } 69 | 70 | CipUdint ForwardOpenResponse::getT2OApi() const { 71 | return _t2oAPI; 72 | } 73 | 74 | CipUsint ForwardOpenResponse::getApplicationReplaySize() const { 75 | return _applicationReplaySize; 76 | } 77 | 78 | const std::vector &ForwardOpenResponse::getApplicationReplay() const { 79 | return _applicationReplay; 80 | } 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/cip/connectionManager/ForwardOpenResponse.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDOPENRESPONSE_H 6 | #define EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDOPENRESPONSE_H 7 | 8 | #include 9 | #include "cip/Types.h" 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | namespace connectionManager { 14 | 15 | class ForwardOpenResponse { 16 | public: 17 | ForwardOpenResponse(); 18 | ~ForwardOpenResponse(); 19 | void expand(const std::vector& data); 20 | 21 | CipUdint getO2TNetworkConnectionId() const; 22 | CipUdint getT2ONetworkConnectionId() const; 23 | CipUint getConnectionSerialNumber() const; 24 | CipUint getOriginatorVendorId() const; 25 | CipUdint getOriginatorSerialNumber() const; 26 | CipUdint getO2TApi() const; 27 | CipUdint getT2OApi() const; 28 | CipUsint getApplicationReplaySize() const; 29 | 30 | const std::vector &getApplicationReplay() const; 31 | 32 | private: 33 | CipUdint _o2tNetworkConnectionId; 34 | CipUdint _t2oNetworkConnectionId; 35 | CipUint _connectionSerialNumber; 36 | CipUint _originatorVendorId; 37 | CipUdint _originatorSerialNumber; 38 | CipUdint _o2tAPI; 39 | CipUdint _t2oAPI; 40 | CipUsint _applicationReplaySize; 41 | std::vector _applicationReplay; 42 | }; 43 | 44 | } 45 | } 46 | } 47 | 48 | #endif // EIPSCANNER_CIP_CONNECTIONMANAGER_FORWARDOPENRESPONSE_H 49 | -------------------------------------------------------------------------------- /src/cip/connectionManager/LargeForwardOpenRequest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vincent Prince on 05/22/20. 3 | // 4 | 5 | #include "LargeForwardOpenRequest.h" 6 | 7 | #include 8 | #include 9 | #include "utils/Buffer.h" 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | namespace connectionManager { 14 | 15 | using utils::Buffer; 16 | 17 | LargeForwardOpenRequest::LargeForwardOpenRequest(ConnectionParameters params) 18 | : _connectionParameters{std::move(params)} { 19 | } 20 | 21 | LargeForwardOpenRequest::~LargeForwardOpenRequest() = default; 22 | 23 | std::vector LargeForwardOpenRequest::pack() const { 24 | const size_t size = 40; 25 | Buffer buffer(size); 26 | CipUsint reserved = 0; 27 | 28 | buffer << _connectionParameters.priorityTimeTick 29 | << _connectionParameters.timeoutTicks 30 | << _connectionParameters.o2tNetworkConnectionId 31 | << _connectionParameters.t2oNetworkConnectionId 32 | << _connectionParameters.connectionSerialNumber 33 | << _connectionParameters.originatorVendorId 34 | << _connectionParameters.originatorSerialNumber 35 | << _connectionParameters.connectionTimeoutMultiplier 36 | << reserved << reserved << reserved 37 | << _connectionParameters.o2tRPI 38 | << _connectionParameters.o2tNetworkConnectionParams 39 | << _connectionParameters.t2oRPI 40 | << _connectionParameters.t2oNetworkConnectionParams 41 | << _connectionParameters.transportTypeTrigger 42 | << _connectionParameters.connectionPathSize; 43 | 44 | assert(buffer.size() == size); 45 | 46 | buffer << _connectionParameters.connectionPath; 47 | return buffer.data(); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/cip/connectionManager/LargeForwardOpenRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vincent Prince on 05/22/20. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_CONNECTIONMANAGER_LARGEFORWARDOPENREQUEST_H 6 | #define EIPSCANNER_CIP_CONNECTIONMANAGER_LARGEFORWARDOPENREQUEST_H 7 | 8 | #include 9 | #include "ConnectionParameters.h" 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | namespace connectionManager { 14 | class LargeForwardOpenRequest { 15 | public: 16 | LargeForwardOpenRequest(ConnectionParameters params); 17 | ~LargeForwardOpenRequest(); 18 | std::vector pack() const; 19 | 20 | private: 21 | ConnectionParameters _connectionParameters; 22 | }; 23 | } 24 | } 25 | } 26 | #endif // EIPSCANNER_CIP_CONNECTIONMANAGER_LARGEFORWARDOPENREQUEST_H 27 | -------------------------------------------------------------------------------- /src/cip/connectionManager/NetworkConnectionParametersBuilder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vincent Prince on 05/22/20. 3 | // 4 | 5 | #include "NetworkConnectionParametersBuilder.h" 6 | 7 | #include 8 | #include 9 | #include "utils/Buffer.h" 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | namespace connectionManager { 14 | 15 | using utils::Buffer; 16 | 17 | NetworkConnectionParametersBuilder::NetworkConnectionParametersBuilder(CipUdint val, bool lfo) : 18 | _value{val}, _lfo{lfo} { 19 | 20 | } 21 | 22 | NetworkConnectionParametersBuilder NetworkConnectionParametersBuilder::setRedundantOwner(RedundantOwner val) { 23 | if (_lfo) { 24 | _value |= (val << 31); 25 | } else { 26 | _value |= (val << 15); 27 | } 28 | return *this; 29 | } 30 | 31 | NetworkConnectionParametersBuilder& NetworkConnectionParametersBuilder::setConnectionType(ConnectionType val) { 32 | if (_lfo) { 33 | _value |= (val << 29); 34 | } else { 35 | _value |= (val << 13); 36 | } 37 | return *this; 38 | } 39 | 40 | NetworkConnectionParametersBuilder& NetworkConnectionParametersBuilder::setPriority(Priority val) { 41 | if (_lfo) { 42 | _value |= (val << 26); 43 | }else { 44 | _value |= (val << 10); 45 | } 46 | return *this; 47 | } 48 | 49 | NetworkConnectionParametersBuilder& NetworkConnectionParametersBuilder::setType(Type val) { 50 | if (_lfo) { 51 | _value |= (val << 25); 52 | } else { 53 | _value |= (val << 9); 54 | } 55 | return *this; 56 | } 57 | 58 | NetworkConnectionParametersBuilder& NetworkConnectionParametersBuilder::setConnectionSize(CipUint val) { 59 | CipUdint mask = 0x000001FF; 60 | if (_lfo) { 61 | mask = 0x0000FFFF; 62 | } 63 | _value |= val & mask; 64 | return *this; 65 | } 66 | 67 | CipUdint NetworkConnectionParametersBuilder::build() { 68 | return _value; 69 | } 70 | 71 | NetworkConnectionParametersBuilder::RedundantOwner NetworkConnectionParametersBuilder::getRedundantOwner() const { 72 | if (_lfo) { 73 | return static_cast(((_value & (1 << 31)) >> 31)); 74 | } else { 75 | return static_cast(((_value & (1 << 15)) >> 15)); 76 | } 77 | } 78 | 79 | NetworkConnectionParametersBuilder::ConnectionType NetworkConnectionParametersBuilder::getConnectionType() const { 80 | if (_lfo) { 81 | return static_cast(((_value & (3 << 29)) >> 29)); 82 | } else { 83 | return static_cast(((_value & (3 << 13)) >> 13)); 84 | } 85 | } 86 | 87 | NetworkConnectionParametersBuilder::Priority NetworkConnectionParametersBuilder::getPriority() const { 88 | if (_lfo) { 89 | return static_cast(((_value & (3 << 26)) >> 26)); 90 | } else { 91 | return static_cast(((_value & (3 << 10)) >> 10)); 92 | } 93 | } 94 | 95 | NetworkConnectionParametersBuilder::Type NetworkConnectionParametersBuilder::getType() const { 96 | if (_lfo) { 97 | return static_cast(((_value & (1 << 25)) >> 25)); 98 | } else { 99 | return static_cast(((_value & (1 << 9)) >> 9)); 100 | } 101 | } 102 | 103 | CipUint NetworkConnectionParametersBuilder::getConnectionSize() const { 104 | CipUdint mask = 0x000001FF; 105 | if (_lfo) { 106 | mask = 0x0000FFFF; 107 | } 108 | return static_cast(_value & mask); 109 | } 110 | 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/cip/connectionManager/NetworkConnectionParametersBuilder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Vincent Prince on 05/22/20. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_CONNECTIONMANAGER_NETWORKCONNECTIONPARAMETERSBUILDER_H 6 | #define EIPSCANNER_CIP_CONNECTIONMANAGER_NETWORKCONNECTIONPARAMETERSBUILDER_H 7 | 8 | #include "cip/Types.h" 9 | 10 | namespace eipScanner { 11 | namespace cip { 12 | namespace connectionManager { 13 | 14 | class NetworkConnectionParametersBuilder { 15 | public: 16 | enum RedundantOwner { 17 | EXCLUSIVE, 18 | REDUNDANT, 19 | }; 20 | 21 | enum ConnectionType { 22 | NULL_TYPE, 23 | MULTICAST, 24 | P2P, 25 | RESERVED, 26 | }; 27 | 28 | enum Priority { 29 | LOW_PRIORITY, 30 | HIGH_PRIORITY, 31 | SCHEDULED, 32 | URGENT, 33 | }; 34 | 35 | enum Type { 36 | FIXED, 37 | VARIABLE, 38 | }; 39 | 40 | 41 | NetworkConnectionParametersBuilder(CipUdint val = 0, bool lfo = false); 42 | virtual ~NetworkConnectionParametersBuilder() = default; 43 | 44 | NetworkConnectionParametersBuilder setRedundantOwner(RedundantOwner val); 45 | NetworkConnectionParametersBuilder& setConnectionType(ConnectionType val); 46 | NetworkConnectionParametersBuilder& setPriority(Priority val); 47 | NetworkConnectionParametersBuilder& setType(Type val); 48 | NetworkConnectionParametersBuilder& setConnectionSize(CipUint val); 49 | CipUdint build(); 50 | 51 | RedundantOwner getRedundantOwner() const; 52 | ConnectionType getConnectionType() const; 53 | Priority getPriority() const; 54 | Type getType() const; 55 | CipUint getConnectionSize() const; 56 | 57 | private: 58 | CipUdint _value; 59 | bool _lfo; 60 | }; 61 | } 62 | } 63 | } 64 | #endif //EIPSCANNER_CIP_CONNECTIONMANAGER_NETWORKCONNECTIONPARAMETERSBUILDER_H 65 | -------------------------------------------------------------------------------- /src/cip/connectionManager/NetworkConnectionParams.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_CIP_CONNECTIONMANAGER_NETWORKCONNECTIONPARAMS_H 6 | #define EIPSCANNER_CIP_CONNECTIONMANAGER_NETWORKCONNECTIONPARAMS_H 7 | 8 | #include "cip/Types.h" 9 | 10 | namespace eipScanner { 11 | namespace cip { 12 | namespace connectionManager { 13 | 14 | enum NetworkConnectionParams : CipUint { 15 | // Redundant Owner 16 | REDUNDANT = (1 << 15), 17 | OWNED = 0, 18 | TYPE0 = 0, 19 | 20 | // Connection type. 21 | MULTICAST = (1 << 13), 22 | P2P = (2 << 13), 23 | 24 | // Priorities 25 | LOW_PRIORITY = 0, 26 | HIGH_PRIORITY = (1 << 10), 27 | SCHEDULED_PRIORITY = (2 << 10), 28 | URGENT = (3 << 10), 29 | 30 | // Type of size. 31 | FIXED = 0, 32 | VARIABLE = (1 << 9), 33 | 34 | // Type of trigger. 35 | TRIG_CYCLIC = 0, 36 | TRIG_CHANGE = (1 << 4), 37 | TRIG_APP = (2 << 4), 38 | 39 | CLASS0 = 0, 40 | CLASS1 = 1, 41 | CLASS2 = 2, 42 | CLASS3 = 3, 43 | TRANSP_SERVER = 0x80 44 | }; 45 | 46 | } 47 | } 48 | } 49 | #endif //EIPSCANNER_CIP_CONNECTIONMANAGER_NETWORKCONNECTIONPARAMS_H 50 | -------------------------------------------------------------------------------- /src/eip/CommonPacket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | #include 5 | #include "CommonPacket.h" 6 | #include "utils/Buffer.h" 7 | #include "cip/Types.h" 8 | 9 | namespace eipScanner { 10 | namespace eip { 11 | using utils::Buffer; 12 | 13 | CommonPacket::CommonPacket() 14 | : _items(0) { 15 | } 16 | 17 | CommonPacket::~CommonPacket() = default; 18 | 19 | CommonPacket &CommonPacket::operator<<(const CommonPacketItem &item) { 20 | _items.push_back(item); 21 | return *this; 22 | } 23 | 24 | std::vector CommonPacket::pack() const { 25 | Buffer buffer; 26 | 27 | buffer << static_cast(_items.size()); 28 | for (auto& item :_items) { 29 | buffer << item.pack(); 30 | } 31 | 32 | return buffer.data(); 33 | } 34 | 35 | void CommonPacket::expand(const std::vector &data) { 36 | _items.clear(); 37 | 38 | Buffer buffer(data); 39 | cip::CipUint count; 40 | buffer >> count; 41 | for (int i = 0; i < count && !buffer.empty(); ++i) { 42 | cip::CipUint typeId; 43 | cip::CipUint length; 44 | buffer >> typeId >> length; 45 | 46 | std::vector itemData(length); 47 | buffer >> itemData; 48 | 49 | if (!buffer.isValid()) { 50 | throw std::runtime_error("Wrong Common Packet format"); 51 | } 52 | 53 | _items.push_back(CommonPacketItem(static_cast(typeId), itemData)); 54 | } 55 | } 56 | 57 | const CommonPacketItem::Vec &CommonPacket::getItems() { 58 | return _items; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/eip/CommonPacket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_EIP_COMMONPACKET_H 6 | #define EIPSCANNER_EIP_COMMONPACKET_H 7 | 8 | #include "CommonPacketItem.h" 9 | 10 | namespace eipScanner { 11 | namespace eip { 12 | 13 | class CommonPacket { 14 | public: 15 | CommonPacket(); 16 | ~CommonPacket(); 17 | 18 | CommonPacket& operator << (const CommonPacketItem& item); 19 | 20 | std::vector pack() const; 21 | void expand(const std::vector& data); 22 | const CommonPacketItem::Vec& getItems(); 23 | private: 24 | CommonPacketItem::Vec _items; 25 | }; 26 | 27 | } 28 | } 29 | 30 | #endif // EIPSCANNER_EIP_COMMONPACKET_H 31 | -------------------------------------------------------------------------------- /src/eip/CommonPacketItem.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include "utils/Buffer.h" 6 | #include "CommonPacketItem.h" 7 | 8 | namespace eipScanner { 9 | namespace eip { 10 | using utils::Buffer; 11 | 12 | CommonPacketItem::CommonPacketItem() 13 | : _typeId(CommonPacketItemIds::NULL_ADDR) 14 | , _length(0) 15 | , _data(0) { 16 | 17 | } 18 | 19 | CommonPacketItem::CommonPacketItem(CommonPacketItemIds typeId, const std::vector &data) 20 | : _typeId{typeId} 21 | , _length(data.size()) 22 | , _data(data) { 23 | 24 | } 25 | 26 | CommonPacketItem::~CommonPacketItem() = default; 27 | 28 | std::vector CommonPacketItem::pack() const { 29 | Buffer buffer; 30 | 31 | buffer << static_cast(_typeId) << _length; 32 | if (_length > 0) { 33 | buffer << _data; 34 | } 35 | return buffer.data(); 36 | } 37 | 38 | CommonPacketItemIds CommonPacketItem::getTypeId() const { 39 | return _typeId; 40 | } 41 | 42 | cip::CipUint CommonPacketItem::getLength() const { 43 | return _length; 44 | } 45 | 46 | const std::vector &CommonPacketItem::getData() const { 47 | return _data; 48 | } 49 | 50 | bool CommonPacketItem::operator==(const CommonPacketItem &rhs) const { 51 | return _typeId == rhs._typeId && 52 | _length == rhs._length && 53 | _data == rhs._data; 54 | } 55 | 56 | bool CommonPacketItem::operator!=(const CommonPacketItem &rhs) const { 57 | return !(rhs == *this); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/eip/CommonPacketItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by flipback on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_EIP_COMMONPACKETITEM_H 6 | #define EIPSCANNER_EIP_COMMONPACKETITEM_H 7 | 8 | #include 9 | #include 10 | 11 | #include "cip/Types.h" 12 | 13 | namespace eipScanner { 14 | namespace eip { 15 | enum class CommonPacketItemIds : cip::CipUint { 16 | NULL_ADDR = 0x0000, 17 | LIST_IDENTITY = 0x000C, 18 | CONNECTION_ADDRESS_ITEM= 0x00A1, 19 | CONNECTED_TRANSPORT_PACKET = 0x00B1, 20 | UNCONNECTED_MESSAGE = 0x00B2, 21 | O2T_SOCKADDR_INFO = 0x8000, 22 | T2O_SOCKADDR_INFO = 0x8001, 23 | SEQUENCED_ADDRESS_ITEM= 0x8002, 24 | }; 25 | 26 | class CommonPacketItem { 27 | public: 28 | using Vec = std::vector; 29 | 30 | CommonPacketItem(); 31 | CommonPacketItem(CommonPacketItemIds typeId, const std::vector& data); 32 | ~CommonPacketItem(); 33 | 34 | std::vector pack() const; 35 | 36 | CommonPacketItemIds getTypeId() const; 37 | cip::CipUint getLength() const; 38 | const std::vector &getData() const; 39 | 40 | bool operator==(const CommonPacketItem &rhs) const; 41 | 42 | bool operator!=(const CommonPacketItem &rhs) const; 43 | 44 | private: 45 | CommonPacketItemIds _typeId; 46 | cip::CipUint _length; 47 | std::vector _data; 48 | }; 49 | 50 | } 51 | } 52 | 53 | 54 | #endif // EIPSCANNER_EIP_COMMONPACKETITEM_H 55 | -------------------------------------------------------------------------------- /src/eip/CommonPacketItemFactory.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include "CommonPacketItemFactory.h" 6 | #include "utils/Buffer.h" 7 | 8 | namespace eipScanner { 9 | namespace eip { 10 | using utils::Buffer; 11 | 12 | CommonPacketItem CommonPacketItemFactory::createNullAddressItem() const { 13 | return CommonPacketItem(); 14 | } 15 | 16 | CommonPacketItem CommonPacketItemFactory::createUnconnectedDataItem(const std::vector &data) const { 17 | return CommonPacketItem(CommonPacketItemIds::UNCONNECTED_MESSAGE, data); 18 | } 19 | 20 | CommonPacketItem CommonPacketItemFactory::createConnectedDataItem(const std::vector &data) const { 21 | return CommonPacketItem(CommonPacketItemIds::CONNECTED_TRANSPORT_PACKET, data); 22 | } 23 | 24 | CommonPacketItem 25 | CommonPacketItemFactory::createSequenceAddressItem(cip::CipUdint connectionId, cip::CipUdint seqNumber) const{ 26 | Buffer buffer; 27 | buffer << connectionId << seqNumber; 28 | return CommonPacketItem(CommonPacketItemIds::SEQUENCED_ADDRESS_ITEM, buffer.data()); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/eip/CommonPacketItemFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_EIP_COMMONPACKETITEMFACTORY_H 6 | #define EIPSCANNER_EIP_COMMONPACKETITEMFACTORY_H 7 | 8 | #include 9 | #include "CommonPacketItem.h" 10 | #include "cip/Types.h" 11 | 12 | namespace eipScanner { 13 | namespace eip { 14 | class CommonPacketItemFactory { 15 | public: 16 | CommonPacketItem createNullAddressItem() const; 17 | CommonPacketItem createUnconnectedDataItem(const std::vector &data) const; 18 | CommonPacketItem createSequenceAddressItem(cip::CipUdint connectionId, cip::CipUdint seqNumber) const; 19 | CommonPacketItem createConnectedDataItem(const std::vector &data) const; 20 | }; 21 | } 22 | } 23 | #endif // EIPSCANNER_EIP_COMMONPACKETITEMFACTORY_H 24 | -------------------------------------------------------------------------------- /src/eip/EncapsPacket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by flipback on 11/16/19. 3 | // 4 | 5 | #include 6 | #include 7 | #include "EncapsPacket.h" 8 | #include "utils/Buffer.h" 9 | 10 | using eipScanner::utils::Buffer; 11 | 12 | namespace eipScanner { 13 | namespace eip { 14 | 15 | EncapsPacket::EncapsPacket() : _command{EncapsCommands::NOP} 16 | , _length{0} 17 | , _sessionHandle{0} 18 | , _statusCode{EncapsStatusCodes::SUCCESS} 19 | , _context(8) 20 | , _options{0} 21 | , _data(0) { 22 | } 23 | 24 | EncapsPacket::~EncapsPacket() = default; 25 | 26 | void EncapsPacket::expand(const std::vector &data) { 27 | if (data.size() < HEADER_SIZE) { 28 | throw std::runtime_error("EncapsPacket header must be 24 bytes"); 29 | } 30 | 31 | Buffer buffer(data); 32 | buffer >> reinterpret_cast(_command) 33 | >> _length 34 | >> _sessionHandle 35 | >> reinterpret_cast(_statusCode) 36 | >> _context 37 | >> _options; 38 | 39 | auto dataSize = data.size() - HEADER_SIZE; 40 | if (dataSize != _length) { 41 | throw std::runtime_error("EncapsPacket data must be " + std::to_string(_length) 42 | + " but we have only " + std::to_string(dataSize) + " bytes"); 43 | } 44 | 45 | _data.resize(_length); 46 | buffer >> _data; 47 | } 48 | 49 | std::vector EncapsPacket::pack() const { 50 | Buffer buffer; 51 | 52 | buffer << static_cast(_command) 53 | << _length 54 | << _sessionHandle 55 | << static_cast(_statusCode) 56 | << _context 57 | << _options 58 | << _data; 59 | 60 | return buffer.data(); 61 | } 62 | 63 | EncapsCommands EncapsPacket::getCommand() const { 64 | return _command; 65 | } 66 | 67 | void EncapsPacket::setCommand(EncapsCommands command) { 68 | _command = command; 69 | } 70 | 71 | cip::CipUint EncapsPacket::getLength() const { 72 | return _length; 73 | } 74 | 75 | cip::CipUdint EncapsPacket::getSessionHandle() const { 76 | return _sessionHandle; 77 | } 78 | 79 | void EncapsPacket::setSessionHandle(cip::CipUdint sessionHandle) { 80 | _sessionHandle = sessionHandle; 81 | } 82 | 83 | 84 | EncapsStatusCodes EncapsPacket::getStatusCode() const { 85 | return _statusCode; 86 | } 87 | 88 | void EncapsPacket::setStatusCode(EncapsStatusCodes statusCode) { 89 | _statusCode = statusCode; 90 | } 91 | 92 | const std::vector &EncapsPacket::getData() const { 93 | return _data; 94 | } 95 | 96 | void EncapsPacket::setData(const std::vector &data) { 97 | _data = data; 98 | _length = data.size(); 99 | } 100 | 101 | size_t EncapsPacket::getLengthFromHeader(const std::vector& data) { 102 | std::vector lengthVector(data.begin() + 2, data.begin() + 4); 103 | Buffer buf(lengthVector); 104 | 105 | cip::CipUint len; 106 | buf >> len; 107 | return len; 108 | } 109 | 110 | bool EncapsPacket::operator==(const EncapsPacket &rhs) const { 111 | return _command == rhs._command && 112 | _length == rhs._length && 113 | _sessionHandle == rhs._sessionHandle && 114 | _statusCode == rhs._statusCode && 115 | _context == rhs._context && 116 | _options == rhs._options && 117 | _data == rhs._data; 118 | } 119 | 120 | bool EncapsPacket::operator!=(const EncapsPacket &rhs) const { 121 | return !(rhs == *this); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/eip/EncapsPacket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_EIP_ENCAPSPACKET_H 6 | #define EIPSCANNER_EIP_ENCAPSPACKET_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "cip/Types.h" 13 | #include 14 | 15 | namespace eipScanner { 16 | namespace eip { 17 | enum class EncapsCommands : cip::CipUint { 18 | NOP = 0, 19 | LIST_SERVICES = 0x0004, 20 | LIST_IDENTITY = 0x0063, 21 | LIST_INTERFACES = 0x0064, 22 | REGISTER_SESSION = 0x0065, 23 | UN_REGISTER_SESSION =0x0066, 24 | SEND_RR_DATA = 0x006F, 25 | SEND_UNIT_DATA = 0x0070, 26 | INDICATE_STATUS = 0x0072, 27 | CANCEL = 0x0073 28 | }; 29 | 30 | enum class EncapsStatusCodes : cip::CipUdint { 31 | SUCCESS = 0x0000, 32 | UNSUPPORTED_COMMAND = 0x0001, 33 | INSUFFICIENT_MEMORY = 0x0002, 34 | INVALID_FORMAT_OR_DATA = 0x0003, 35 | INVALID_SESSION_HANDLE = 0x0064, 36 | UNSUPPORTED_PROTOCOL_VERSION = 0x0069 37 | }; 38 | 39 | class EncapsPacket { 40 | public: 41 | static const size_t HEADER_SIZE = 24; 42 | static size_t getLengthFromHeader(const std::vector& data); 43 | 44 | EncapsPacket(); 45 | ~EncapsPacket(); 46 | std::vector pack() const; 47 | void expand(const std::vector& data); 48 | 49 | // Getters & setters 50 | EncapsCommands getCommand() const; 51 | void setCommand(EncapsCommands command); 52 | 53 | cip::CipUint getLength() const; 54 | 55 | cip::CipUdint getSessionHandle() const; 56 | void setSessionHandle(cip::CipUdint sessionHandle); 57 | 58 | EncapsStatusCodes getStatusCode() const; 59 | void setStatusCode(EncapsStatusCodes statusCode); 60 | 61 | const std::vector &getData() const; 62 | void setData(const std::vector &data); 63 | 64 | 65 | private: 66 | public: 67 | bool operator==(const EncapsPacket &rhs) const; 68 | 69 | bool operator!=(const EncapsPacket &rhs) const; 70 | 71 | private: 72 | EncapsCommands _command; 73 | cip::CipUint _length; 74 | cip::CipUdint _sessionHandle; 75 | EncapsStatusCodes _statusCode; 76 | std::vector _context; 77 | cip::CipUdint _options; 78 | std::vector _data; 79 | }; 80 | } 81 | } 82 | 83 | 84 | #endif // EIPSCANNER_EIP_ENCAPSPACKET_H 85 | -------------------------------------------------------------------------------- /src/eip/EncapsPacketFactory.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include 6 | #include "EncapsPacketFactory.h" 7 | 8 | namespace eipScanner { 9 | namespace eip { 10 | 11 | using utils::Buffer; 12 | 13 | EncapsPacket EncapsPacketFactory::createRegisterSessionPacket() const { 14 | EncapsPacket packet; 15 | packet.setCommand(EncapsCommands::REGISTER_SESSION); 16 | 17 | // See 2-4.4 Vol2 spec 18 | cip::CipUint protocolVersion = 1; 19 | cip::CipUint optionFlag = 0; 20 | 21 | Buffer buffer(4); 22 | buffer << protocolVersion << optionFlag; 23 | packet.setData(buffer.data()); 24 | 25 | return packet; 26 | } 27 | 28 | EncapsPacket EncapsPacketFactory::createUnRegisterSessionPacket(cip::CipUdint sessionHandle) const { 29 | EncapsPacket packet; 30 | packet.setCommand(EncapsCommands::UN_REGISTER_SESSION); 31 | packet.setSessionHandle(sessionHandle); 32 | return packet; 33 | } 34 | 35 | EncapsPacket EncapsPacketFactory::createSendRRDataPacket(cip::CipUdint sessionHandle, cip::CipUint timeout, 36 | std::vector data) const { 37 | 38 | EncapsPacket packet; 39 | packet.setCommand(EncapsCommands::SEND_RR_DATA); 40 | packet.setSessionHandle(sessionHandle); 41 | 42 | cip::CipUdint interfaceHandle = 0; 43 | Buffer buffer(6 + data.size()); 44 | buffer << interfaceHandle << timeout << data; 45 | packet.setData(buffer.data()); 46 | 47 | return packet; 48 | } 49 | 50 | EncapsPacket EncapsPacketFactory::createListIdentityPacket() const { 51 | EncapsPacket packet; 52 | packet.setCommand(EncapsCommands::LIST_IDENTITY); 53 | packet.setSessionHandle(0); 54 | return packet; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/eip/EncapsPacketFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_EIP_ENCAPSPACKETFACTORY_H 6 | #define EIPSCANNER_EIP_ENCAPSPACKETFACTORY_H 7 | 8 | #include "EncapsPacket.h" 9 | #include "cip/Types.h" 10 | 11 | namespace eipScanner { 12 | namespace eip { 13 | class EncapsPacketFactory { 14 | public: 15 | EncapsPacket createRegisterSessionPacket() const; 16 | EncapsPacket createUnRegisterSessionPacket(cip::CipUdint sessionHandle) const; 17 | EncapsPacket createSendRRDataPacket(cip::CipUdint sessionHandle, 18 | cip::CipUint timeout, std::vector data) const; 19 | EncapsPacket createListIdentityPacket() const; 20 | }; 21 | } 22 | } 23 | 24 | 25 | 26 | #endif // EIPSCANNER_ENCAPSPACKETFACTORY_H 27 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectEmptyState.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #include "FileObjectEmptyState.h" 6 | namespace eipScanner { 7 | namespace fileObject { 8 | using utils::LogLevel; 9 | 10 | FileObjectEmptyState::FileObjectEmptyState(FileObject &owner, cip::CipUint objectId, 11 | MessageRouter::SPtr messageRouter) 12 | : FileObjectState(FileObjectStateCodes::FILE_EMPTY, owner, objectId, messageRouter) { 13 | } 14 | 15 | void FileObjectEmptyState::initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handle) { 16 | (void) si; 17 | (void) handle; 18 | logWithStateName(LogLevel::WARNING, "File cannot be uploaded"); 19 | } 20 | 21 | bool FileObjectEmptyState::transfer(SessionInfoIf::SPtr si) { 22 | (void) si; 23 | logWithStateName(LogLevel::WARNING, "Nothing to transfer"); 24 | return false; 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectEmptyState.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_FILEOBJECT_FILEOBJECTEMPTYSTATE_H 6 | #define EIPSCANNER_FILEOBJECT_FILEOBJECTEMPTYSTATE_H 7 | 8 | #include "FileObjectState.h" 9 | 10 | namespace eipScanner { 11 | namespace fileObject { 12 | 13 | class FileObjectEmptyState : public FileObjectState { 14 | public: 15 | FileObjectEmptyState(FileObject &owner, cip::CipUint objectId, MessageRouter::SPtr messageRouter); 16 | void initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handle) override; 17 | 18 | bool transfer(SessionInfoIf::SPtr si) override; 19 | }; 20 | } 21 | } 22 | 23 | #endif // EIPSCANNER_FILEOBJECT_FILEOBJECTEMPTYSTATE_H 24 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectLoadedState.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #include "FileObjectLoadedState.h" 6 | #include "FileObjectUploadInProgressState.h" 7 | #include "utils/Buffer.h" 8 | 9 | namespace eipScanner { 10 | namespace fileObject { 11 | using utils::Buffer; 12 | using utils::LogLevel; 13 | 14 | FileObjectLoadedState::FileObjectLoadedState(FileObject &owner, cip::CipUint objectId, 15 | MessageRouter::SPtr messageRouter) 16 | : FileObjectState(FileObjectStateCodes::FILE_LOADED, owner, objectId, messageRouter) { 17 | } 18 | 19 | void FileObjectLoadedState::initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handler) { 20 | logWithStateName(LogLevel::INFO, "Initiate upload"); 21 | Buffer buffer; 22 | buffer << MAX_TRANSFER_SIZE; 23 | 24 | auto response = _messageRouter->sendRequest(si, 25 | static_cast(FileObjectServiceCodes::INITIATE_UPLOAD), 26 | cip::EPath(FILE_OBJECT_CLASS_ID, _objectId), buffer.data()); 27 | 28 | if (response.getGeneralStatusCode() == cip::GeneralStatusCodes::SUCCESS) { 29 | buffer = Buffer(response.getData()); 30 | cip::CipUdint fileSize = 0; 31 | cip::CipUsint transferSize = 0; 32 | 33 | buffer >> fileSize >> transferSize; 34 | setState(fileSize, transferSize, handler); 35 | 36 | } else { 37 | logGeneralAndAdditionalStatus(response); 38 | handler(response.getGeneralStatusCode(), std::vector(0)); 39 | } 40 | } 41 | 42 | bool FileObjectLoadedState::transfer(SessionInfoIf::SPtr si) { 43 | return false; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/fileObject/FileObjectLoadedState.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_FILEOBJECT_FILEOBJECTLOADEDSTATE_H 6 | #define EIPSCANNER_FILEOBJECT_FILEOBJECTLOADEDSTATE_H 7 | 8 | #include "FileObjectState.h" 9 | 10 | namespace eipScanner { 11 | namespace fileObject { 12 | 13 | class FileObjectLoadedState : public FileObjectState { 14 | public: 15 | FileObjectLoadedState(FileObject &owner, cip::CipUint objectId, MessageRouter::SPtr messageRouter); 16 | 17 | void initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handler) override; 18 | bool transfer(SessionInfoIf::SPtr si) override; 19 | }; 20 | 21 | } 22 | } 23 | 24 | #endif // EIPSCANNER_FILEOBJECTLOADEDSTATE_H 25 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectNonExistentState.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #include "FileObjectNonExistentState.h" 6 | 7 | namespace eipScanner { 8 | namespace fileObject { 9 | using utils::LogLevel; 10 | 11 | FileObjectNonExistentState::FileObjectNonExistentState(FileObject &owner, cip::CipUint objectId, 12 | MessageRouter::SPtr messageRouter) 13 | : FileObjectState(FileObjectStateCodes::NONEXISTENT, owner, objectId, messageRouter) { 14 | } 15 | 16 | void FileObjectNonExistentState::initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handle) { 17 | (void) si; 18 | (void) handle; 19 | logWithStateName(LogLevel::WARNING, 20 | "File cannot be uploaded"); 21 | } 22 | 23 | bool FileObjectNonExistentState::transfer(SessionInfoIf::SPtr si) { 24 | (void) si; 25 | logWithStateName(LogLevel::WARNING, 26 | "Nothing to transfer"); 27 | return false; 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectNonExistentState.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_FILEOBJECT_FILEOBJECTNONEXISTENTSTATE_H 6 | #define EIPSCANNER_FILEOBJECT_FILEOBJECTNONEXISTENTSTATE_H 7 | 8 | #include "FileObjectState.h" 9 | 10 | namespace eipScanner { 11 | namespace fileObject { 12 | 13 | class FileObjectNonExistentState : public FileObjectState { 14 | public: 15 | FileObjectNonExistentState(FileObject &owner, cip::CipUint objectId, MessageRouter::SPtr messageRouter); 16 | 17 | void initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handle) override; 18 | bool transfer(SessionInfoIf::SPtr si) override; 19 | }; 20 | 21 | } 22 | } 23 | #endif // EIPSCANNER_FILEOBJECTNONEXISTENTSTATE_H 24 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectState.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #include 6 | #include "FileObjectState.h" 7 | #include "utils/Buffer.h" 8 | #include "FileObjectNonExistentState.h" 9 | #include "FileObjectEmptyState.h" 10 | #include "FileObjectLoadedState.h" 11 | #include "FileObjectUploadInProgressState.h" 12 | 13 | namespace eipScanner { 14 | namespace fileObject { 15 | using utils::Logger; 16 | using utils::LogLevel; 17 | using utils::Buffer; 18 | 19 | FileObjectState::FileObjectState(FileObjectStateCodes state, FileObject &owner, 20 | cip::CipUint objectId, MessageRouter::SPtr messageRouter) 21 | : _messageRouter{messageRouter} 22 | , _objectId{objectId} 23 | , _stateCode{state} 24 | , _owner(owner) { 25 | 26 | logWithStateName(utils::LogLevel::DEBUG, "Start"); 27 | } 28 | 29 | FileObjectState::~FileObjectState() { 30 | logWithStateName(utils::LogLevel::TRACE, "Stop"); 31 | } 32 | 33 | FileObjectStateCodes FileObjectState::getStateCode() const { 34 | return _stateCode; 35 | } 36 | 37 | void FileObjectState::logWithStateName(LogLevel logLevel, const std::string &message) const { 38 | Logger(logLevel) << "FileObject=" << std::hex << _objectId << " <" << getStateName() << "> " << message; 39 | } 40 | 41 | std::string FileObjectState::getStateName() const { 42 | static const std::map NAMES_MAP = { 43 | std::make_pair(FileObjectStateCodes::NONEXISTENT, "NONEXISTENT"), 44 | std::make_pair(FileObjectStateCodes::FILE_LOADED, "FILE_LOADED"), 45 | std::make_pair(FileObjectStateCodes::FILE_EMPTY, "FILE_EMPTY"), 46 | std::make_pair(FileObjectStateCodes::TRANSFER_DOWNLOAD_IN_PROGRESS, 47 | "TRANSFER_DOWNLOAD_IN_PROGRESS"), 48 | std::make_pair(FileObjectStateCodes::TRANSFER_DOWNLOAD_INITIATED, "TRANSFER_DOWNLOAD_INITIATED"), 49 | std::make_pair(FileObjectStateCodes::TRANSFER_UPLOAD_IN_PROGRESS, "TRANSFER_UPLOAD_IN_PROGRESS"), 50 | std::make_pair(FileObjectStateCodes::TRANSFER_UPLOAD_INITIATED, "TRANSFER_UPLOAD_INITIATED"), 51 | std::make_pair(FileObjectStateCodes::UNKNOWN, "UNKNOWN"), 52 | }; 53 | 54 | return NAMES_MAP.at(_stateCode); 55 | } 56 | 57 | void FileObjectState::SyncState(SessionInfoIf::SPtr si) { 58 | auto response = _messageRouter->sendRequest(si, cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 59 | cip::EPath(FILE_OBJECT_CLASS_ID, _objectId, 60 | static_cast(FileObjectAttributesCodes::STATE)), {}); 61 | 62 | if (response.getGeneralStatusCode() == cip::GeneralStatusCodes::SUCCESS) { 63 | Buffer buffer(response.getData()); 64 | cip::CipUsint stateCode; 65 | buffer >> stateCode; 66 | 67 | switch (static_cast(stateCode)) { 68 | case FileObjectStateCodes::NONEXISTENT: 69 | setState(); 70 | break; 71 | case FileObjectStateCodes::FILE_EMPTY: 72 | setState(); 73 | break; 74 | case FileObjectStateCodes::FILE_LOADED: 75 | setState(); 76 | break; 77 | case FileObjectStateCodes::TRANSFER_UPLOAD_INITIATED: 78 | case FileObjectStateCodes::TRANSFER_UPLOAD_IN_PROGRESS: 79 | logWithStateName(LogLevel::WARNING, "File is uploading. " 80 | "We need to re-initialize the uploading"); 81 | setState(); 82 | break; 83 | default: 84 | throw std::runtime_error("Read unknown or unsupported state"); 85 | } 86 | 87 | } else { 88 | throw std::runtime_error("Failed load the state of File Object ID=" + std::to_string(_objectId)); 89 | } 90 | } 91 | 92 | void FileObjectState::initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handle) { 93 | (void) si; 94 | (void) handle; 95 | logWithStateName(LogLevel::ERROR, "Not implemented call"); 96 | } 97 | 98 | bool FileObjectState::transfer(SessionInfoIf::SPtr si) { 99 | (void) si; 100 | logWithStateName(LogLevel::ERROR, "Not implemented call"); 101 | return false; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectState.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_FILEOBJECT_FILEOBJECTSTATE_H 6 | #define EIPSCANNER_FILEOBJECT_FILEOBJECTSTATE_H 7 | 8 | #include 9 | #include "SessionInfo.h" 10 | #include "MessageRouter.h" 11 | #include "FileObject.h" 12 | #include "cip/GeneralStatusCodes.h" 13 | #include "utils/Logger.h" 14 | 15 | namespace eipScanner { 16 | namespace fileObject { 17 | const cip::CipUsint FILE_OBJECT_CLASS_ID = 0x37; 18 | const cip::CipUsint MAX_TRANSFER_SIZE = 255; 19 | 20 | enum class FileObjectAttributesCodes : cip::CipUint { 21 | STATE = 1, 22 | }; 23 | 24 | enum class FileObjectServiceCodes : cip::CipUint { 25 | INITIATE_UPLOAD = 0x4B, 26 | UPLOAD_TRANSFER = 0x4F 27 | }; 28 | 29 | enum class TransferPacketTypeCodes : cip::CipUsint { 30 | FIRST = 0, 31 | MIDDLE = 1, 32 | LAST = 2, 33 | ABORT = 3, 34 | FIRST_AND_LAST = 4, 35 | }; 36 | 37 | class FileObjectState { 38 | public: 39 | using UPtr = std::unique_ptr; 40 | 41 | FileObjectState(FileObjectStateCodes state, FileObject &owner, cip::CipUint objectId, MessageRouter::SPtr messageRouter); 42 | virtual ~FileObjectState(); 43 | 44 | virtual void initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handle); 45 | virtual bool transfer(SessionInfoIf::SPtr si); 46 | 47 | FileObjectStateCodes getStateCode() const; 48 | void SyncState(SessionInfoIf::SPtr si); 49 | 50 | protected: 51 | void logWithStateName(utils::LogLevel logLevel, const std::string &message) const; 52 | std::string getStateName() const; 53 | 54 | template 55 | void setState(Types ... args) { 56 | _owner._state = std::make_unique(_owner, _objectId, _messageRouter, args...); 57 | } 58 | 59 | MessageRouter::SPtr _messageRouter; 60 | cip::CipUint _objectId; 61 | FileObjectStateCodes _stateCode; 62 | FileObject &_owner; 63 | }; 64 | } 65 | } 66 | 67 | #endif // EIPSCANNER_FILEOBJECT_FILEOBJECTSTATE_H 68 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectUploadInProgressState.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #include "FileObjectUploadInProgressState.h" 6 | #include "utils/Buffer.h" 7 | 8 | namespace eipScanner { 9 | namespace fileObject { 10 | using utils::LogLevel; 11 | using utils::Buffer; 12 | 13 | FileObjectUploadInProgressState::FileObjectUploadInProgressState(FileObject &owner, cip::CipUint objectId, 14 | MessageRouter::SPtr messageRouter, 15 | cip::CipUdint fileSize, 16 | cip::CipUsint transferSize, 17 | EndUploadHandler handler) 18 | : FileObjectState{FileObjectStateCodes::TRANSFER_UPLOAD_IN_PROGRESS, owner, objectId, messageRouter} 19 | , _fileSize{fileSize} 20 | , _transferSize{transferSize} 21 | , _fileContent() 22 | , _transferNumber{0} 23 | , _handler(std::move(handler)){ 24 | } 25 | 26 | void 27 | FileObjectUploadInProgressState::initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handle) { 28 | (void) si; 29 | (void) handle; 30 | logWithStateName(LogLevel::INFO, "Initiate upload of file again"); 31 | //TODO: Implement 32 | } 33 | 34 | bool FileObjectUploadInProgressState::transfer(SessionInfoIf::SPtr si) { 35 | Buffer buffer; 36 | buffer << _transferNumber; 37 | 38 | auto response = _messageRouter->sendRequest(si, 39 | static_cast(FileObjectServiceCodes::UPLOAD_TRANSFER), 40 | cip::EPath(fileObject::FILE_OBJECT_CLASS_ID, _objectId), 41 | buffer.data()); 42 | 43 | if (response.getGeneralStatusCode() == cip::GeneralStatusCodes::SUCCESS) { 44 | buffer = Buffer(response.getData()); 45 | cip::CipUsint transferNumber; 46 | TransferPacketTypeCodes transferPacketType; 47 | 48 | 49 | buffer >> transferNumber >> reinterpret_cast(transferPacketType); 50 | logWithStateName(LogLevel::DEBUG, 51 | "Receive transfer packet type=" + std::to_string( 52 | static_cast(transferPacketType))); 53 | 54 | if (transferNumber != _transferNumber) { 55 | logWithStateName(LogLevel::ERROR, "Wrong transfer number"); 56 | _handler(cip::GeneralStatusCodes::INVALID_PARAMETER, std::vector()); 57 | return false; 58 | } 59 | 60 | switch (transferPacketType) { 61 | case TransferPacketTypeCodes::FIRST: { 62 | std::vector fileData(_transferSize); 63 | buffer >> fileData; 64 | 65 | _fileContent.insert(_fileContent.end(), fileData.begin(), fileData.end()); 66 | break; 67 | } 68 | case TransferPacketTypeCodes::MIDDLE: { 69 | std::vector fileData(_transferSize); 70 | buffer >> fileData; 71 | 72 | _fileContent.insert(_fileContent.end(), fileData.begin(), fileData.end()); 73 | break; 74 | } 75 | case TransferPacketTypeCodes::LAST: 76 | case TransferPacketTypeCodes::FIRST_AND_LAST: { 77 | std::vector fileData(response.getData().size() - 4); //TODO: Check CRC 78 | buffer >> fileData; 79 | 80 | _fileContent.insert(_fileContent.end(), fileData.begin(), fileData.end()); 81 | if (_fileContent.size() == _fileSize) { 82 | _handler(cip::GeneralStatusCodes::SUCCESS, _fileContent); 83 | } else { 84 | logWithStateName(LogLevel::ERROR, "Wrong size of the uploaded file"); 85 | _handler(cip::GeneralStatusCodes::INVALID_PARAMETER, std::vector()); 86 | } 87 | return false; 88 | } 89 | default: 90 | throw std::runtime_error("Received unexpected transfer type"); 91 | }; 92 | 93 | _transferNumber++; 94 | } else { 95 | logGeneralAndAdditionalStatus(response); 96 | _handler(response.getGeneralStatusCode(), std::vector()); 97 | } 98 | 99 | return true; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/fileObject/FileObjectUploadInProgressState.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/23/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_FILEOBJECT_FILEOBJECTUPLOADINPROGRESSSTATE_H 6 | #define EIPSCANNER_FILEOBJECT_FILEOBJECTUPLOADINPROGRESSSTATE_H 7 | 8 | #include 9 | 10 | #include "FileObjectState.h" 11 | #include "cip/Types.h" 12 | 13 | namespace eipScanner { 14 | namespace fileObject { 15 | class FileObjectUploadInProgressState : public FileObjectState { 16 | public: 17 | FileObjectUploadInProgressState(FileObject &owner, cip::CipUint objectId, MessageRouter::SPtr messageRouter, 18 | cip::CipUdint fileSize, cip::CipUsint transferSize, EndUploadHandler handler); 19 | 20 | void initiateUpload(SessionInfoIf::SPtr si, EndUploadHandler handle) override; 21 | bool transfer(SessionInfoIf::SPtr si) override; 22 | 23 | private: 24 | cip::CipUdint _fileSize; 25 | cip::CipUsint _transferSize; 26 | std::vector_fileContent; 27 | cip::CipUsint _transferNumber; 28 | 29 | EndUploadHandler _handler; 30 | }; 31 | 32 | } 33 | } 34 | #endif // EIPSCANNER_FILEOBJECT_FILEOBJECTUPLOADINPROGRESSSTATE_H 35 | -------------------------------------------------------------------------------- /src/sockets/BaseSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_SOCKETS_BASESOCKET_H 6 | #define EIPSCANNER_SOCKETS_BASESOCKET_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "EndPoint.h" 17 | 18 | namespace eipScanner { 19 | namespace sockets { 20 | class BaseSocket { 21 | public: 22 | using BeginReceiveHandler = std::function; 23 | using SPtr = std::shared_ptr; 24 | using UPtr = std::unique_ptr; 25 | 26 | explicit BaseSocket(EndPoint endPoint); 27 | BaseSocket(std::string host, int port); 28 | virtual ~BaseSocket(); 29 | 30 | virtual void Send(const std::vector& data) const = 0; 31 | virtual std::vector Receive(size_t size) const = 0; 32 | void setBeginReceiveHandler(BeginReceiveHandler handler); 33 | 34 | const std::chrono::milliseconds &getRecvTimeout() const; 35 | void setRecvTimeout(const std::chrono::milliseconds &recvTimeout); 36 | 37 | int getSocketFd() const; 38 | 39 | static int getLastError(); 40 | static const std::error_category& getErrorCategory() noexcept; 41 | 42 | const EndPoint &getRemoteEndPoint() const; 43 | 44 | static void select(std::vector sockets, std::chrono::milliseconds timeout); 45 | 46 | protected: 47 | void BeginReceive(); 48 | void Shutdown(); 49 | void Close(); 50 | 51 | int _sockedFd; 52 | EndPoint _remoteEndPoint; 53 | 54 | std::chrono::milliseconds _recvTimeout; 55 | BeginReceiveHandler _beginReceiveHandler; 56 | 57 | static timeval makePortableInterval(const std::chrono::milliseconds &recvTimeout); 58 | }; 59 | } 60 | } 61 | 62 | 63 | #endif // EIPSCANNER_SOCKETS_BASESOCKET_H 64 | -------------------------------------------------------------------------------- /src/sockets/EndPoint.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/10/19. 3 | // 4 | 5 | #include "EndPoint.h" 6 | #include "BaseSocket.h" 7 | #include "Platform.h" 8 | 9 | #if defined(__unix__) || defined(__APPLE__) 10 | #include 11 | #elif defined(_WIN32) || defined(WIN32) || defined(_WIN64) 12 | #include 13 | #include 14 | #endif 15 | 16 | #include 17 | #include 18 | 19 | namespace eipScanner { 20 | namespace sockets { 21 | 22 | EndPoint::EndPoint(struct sockaddr_in &addr) 23 | : _host(inet_ntoa(addr.sin_addr)) 24 | , _port(htons(addr.sin_port)) 25 | , _addr(addr) { 26 | } 27 | 28 | EndPoint::EndPoint(std::string host, int port) 29 | : _host(std::move(host)) 30 | , _port(port) 31 | , _addr{} { 32 | 33 | _addr.sin_family = AF_INET; 34 | _addr.sin_port = htons(_port); 35 | #if defined(__unix__) || defined(__APPLE__) 36 | if (inet_aton(_host.c_str(), &_addr.sin_addr) < 0) { 37 | #elif defined(_WIN32) || defined(WIN32) || defined(_WIN64) 38 | if (inet_pton(AF_INET, _host.c_str(), &_addr.sin_addr.s_addr) < 0) { 39 | #endif 40 | throw std::system_error(BaseSocket::getLastError(), BaseSocket::getErrorCategory()); 41 | } 42 | } 43 | 44 | const std::string &EndPoint::getHost() const { 45 | return _host; 46 | } 47 | 48 | int EndPoint::getPort() const { 49 | return _port; 50 | } 51 | 52 | const sockaddr_in &EndPoint::getAddr() const { 53 | return _addr; 54 | } 55 | 56 | bool EndPoint::operator==(const EndPoint &rhs) const { 57 | return _host == rhs._host && 58 | _port == rhs._port && 59 | _addr.sin_addr.s_addr == rhs._addr.sin_addr.s_addr && 60 | _addr.sin_port == rhs._addr.sin_port && 61 | _addr.sin_family == rhs._addr.sin_family; 62 | } 63 | 64 | bool EndPoint::operator!=(const EndPoint &rhs) const { 65 | return !(rhs == *this); 66 | } 67 | 68 | bool EndPoint::operator< (const EndPoint& rhs) const { 69 | return _host < rhs._host 70 | && _port < rhs._port; 71 | } 72 | 73 | std::string EndPoint::toString() const { 74 | return _host + ":" + std::to_string(_port); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/sockets/EndPoint.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/10/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_SOCKETS_ENDPOINT_H 6 | #define EIPSCANNER_SOCKETS_ENDPOINT_H 7 | 8 | #if defined(__unix__) || defined(__APPLE__) 9 | #include 10 | #elif defined(_WIN32) || defined(WIN32) || defined(_WIN64) 11 | #include 12 | #endif 13 | #include 14 | 15 | #define EIP_DEFAULT_EXPLICIT_PORT (uint16_t)(44818) 16 | #define EIP_DEFAULT_IMPLICIT_PORT (uint16_t)(2222) 17 | 18 | namespace eipScanner { 19 | namespace sockets { 20 | 21 | class EndPoint { 22 | public: 23 | EndPoint(std::string host, int port); 24 | EndPoint(struct sockaddr_in& addr); 25 | 26 | const std::string &getHost() const; 27 | int getPort() const; 28 | const sockaddr_in &getAddr() const; 29 | 30 | std::string toString() const; 31 | 32 | bool operator==(const EndPoint &rhs) const; 33 | bool operator!=(const EndPoint &rhs) const; 34 | bool operator< (const EndPoint& rhs) const; 35 | private: 36 | std::string _host; 37 | int _port; 38 | struct sockaddr_in _addr; 39 | }; 40 | } 41 | } 42 | 43 | #endif // EIPSCANNER_ENDPOINT_H 44 | -------------------------------------------------------------------------------- /src/sockets/Platform.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Stefan Broekman on 02/04/21. 3 | // 4 | 5 | #include "Platform.h" 6 | 7 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 8 | #include 9 | 10 | namespace eipScanner { 11 | namespace sockets { 12 | char const* win32ErrorCategory::name() const noexcept { 13 | return "Win32Error"; 14 | } 15 | 16 | std::string win32ErrorCategory::message(int c) const { 17 | char error[UINT8_MAX]; 18 | auto len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, static_cast(c), 0, error, sizeof(error), nullptr); 19 | if (len == 0) { 20 | return "N/A"; 21 | } 22 | // trim trailing newline 23 | while (len && (error[len - 1] == '\r' || error[len - 1] == '\n')) { 24 | --len; 25 | } 26 | return std::string(error, len); 27 | } 28 | 29 | /*static*/win32ErrorCategory const& win32ErrorCategory::category() { 30 | static win32ErrorCategory c; 31 | return c; 32 | } 33 | } 34 | } 35 | #endif 36 | -------------------------------------------------------------------------------- /src/sockets/Platform.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Stefan Broekman on 02/04/21. 3 | // 4 | 5 | #ifndef EIPSCANNER_SOCKETS_PLATFORM_H 6 | #define EIPSCANNER_SOCKETS_PLATFORM_H 7 | 8 | #include 9 | 10 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 11 | 12 | #define EIPSCANNER_SOCKET_ERROR(err) WSA##err 13 | 14 | namespace eipScanner { 15 | namespace sockets { 16 | class win32ErrorCategory : public std::error_category { 17 | public: 18 | char const* name() const noexcept override final; 19 | std::string message(int c) const override final; 20 | 21 | static win32ErrorCategory const& category(); 22 | }; 23 | } 24 | } 25 | 26 | #else 27 | #define EIPSCANNER_SOCKET_ERROR(err) err 28 | #endif 29 | 30 | #endif // EIPSCANNER_SOCKETS_PLATFORM_H 31 | -------------------------------------------------------------------------------- /src/sockets/TCPSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_SOCKETS_TCPSOCKET_H 6 | #define EIPSCANNER_SOCKETS_TCPSOCKET_H 7 | 8 | #include 9 | #include 10 | #include "BaseSocket.h" 11 | 12 | namespace eipScanner { 13 | namespace sockets { 14 | class TCPSocket : public BaseSocket { 15 | public: 16 | explicit TCPSocket(EndPoint endPoint); 17 | TCPSocket(EndPoint endPoint, std::chrono::milliseconds connTimeout); 18 | TCPSocket(std::string host, int port); 19 | virtual ~TCPSocket(); 20 | 21 | void Send(const std::vector& data) const override; 22 | std::vector Receive(size_t size) const override; 23 | 24 | private: 25 | }; 26 | } 27 | } 28 | 29 | #endif // EIPSCANNER_TCPSOCKET_H 30 | -------------------------------------------------------------------------------- /src/sockets/UDPBoundSocket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/21/19. 3 | // 4 | #include 5 | 6 | //#include 7 | //#include 8 | 9 | #include "UDPBoundSocket.h" 10 | #include "Platform.h" 11 | 12 | namespace eipScanner { 13 | namespace sockets { 14 | 15 | 16 | UDPBoundSocket::UDPBoundSocket(std::string host, int port) 17 | : UDPBoundSocket(EndPoint(host, port)) { 18 | 19 | } 20 | UDPBoundSocket::UDPBoundSocket(EndPoint endPoint) 21 | : UDPSocket(std::move(endPoint)) { 22 | int on = 1; 23 | if (setsockopt(_sockedFd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) { 24 | throw std::system_error(BaseSocket::getLastError(), BaseSocket::getErrorCategory()); 25 | } 26 | 27 | auto addr = _remoteEndPoint.getAddr(); 28 | addr.sin_addr.s_addr = INADDR_ANY; 29 | if (bind(_sockedFd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 30 | throw std::system_error(BaseSocket::getLastError(), BaseSocket::getErrorCategory()); 31 | } 32 | } 33 | 34 | sockets::UDPBoundSocket::~UDPBoundSocket() = default; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/sockets/UDPBoundSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/21/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_SOCKETS_UDPSBOUNDOCKET_H 6 | #define EIPSCANNER_SOCKETS_UDPSBOUNDOCKET_H 7 | 8 | #include 9 | #include 10 | #include 11 | //#include 12 | #include "UDPSocket.h" 13 | 14 | namespace eipScanner { 15 | namespace sockets { 16 | 17 | class UDPBoundSocket : public UDPSocket { 18 | public: 19 | using WPtr = std::weak_ptr; 20 | using SPtr = std::shared_ptr; 21 | 22 | explicit UDPBoundSocket(EndPoint endPoint); 23 | UDPBoundSocket(std::string host, int port); 24 | virtual ~UDPBoundSocket(); 25 | }; 26 | } 27 | } 28 | 29 | #endif // EIPSCANNER_SOCKETS_UDPSOCKET_H 30 | -------------------------------------------------------------------------------- /src/sockets/UDPSocket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #if defined(__unix__) || defined(__APPLE__) 6 | #include 7 | #include 8 | #include 9 | #include 10 | #elif defined(_WIN32) || defined(WIN32) || defined(_WIN64) 11 | #include 12 | #include 13 | #endif 14 | 15 | #include "utils/Logger.h" 16 | #include "UDPSocket.h" 17 | #include "Platform.h" 18 | 19 | namespace eipScanner { 20 | namespace sockets { 21 | using eipScanner::utils::Logger; 22 | using eipScanner::utils::LogLevel; 23 | 24 | UDPSocket::UDPSocket(std::string host, int port) 25 | : UDPSocket(EndPoint(host, port)){ 26 | 27 | } 28 | 29 | UDPSocket::UDPSocket(EndPoint endPoint) 30 | : BaseSocket(EndPoint(std::move(endPoint))) { 31 | 32 | _sockedFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 33 | if (_sockedFd < 0) { 34 | throw std::system_error(BaseSocket::getLastError(), BaseSocket::getErrorCategory()); 35 | } 36 | 37 | #ifdef SO_NOSIGPIPE 38 | // Do not generate SIGPIPE for this socket 39 | if (setsockopt(_sockedFd, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, sizeof(int)) < 0) { 40 | throw std::system_error(BaseSocket::getLastError(), BaseSocket::getErrorCategory()); 41 | } 42 | #endif 43 | 44 | Logger(LogLevel::DEBUG) << "Opened UDP socket fd=" << _sockedFd; 45 | } 46 | 47 | UDPSocket::~UDPSocket() { 48 | Logger(LogLevel::DEBUG) << "Close UDP socket fd=" << _sockedFd; 49 | Shutdown(); 50 | Close(); 51 | } 52 | 53 | void UDPSocket::Send(const std::vector &data) const { 54 | Logger(LogLevel::TRACE) << "Send " << data.size() << " bytes from UDP socket #" << _sockedFd << "."; 55 | 56 | int flags = 0; 57 | #ifdef MSG_NOSIGNAL 58 | // Do not generate SIGPIPE when calling send() on closed socket 59 | flags |= MSG_NOSIGNAL; 60 | #endif 61 | auto addr = _remoteEndPoint.getAddr(); 62 | int count = sendto(_sockedFd, (char*)data.data(), data.size(), flags, 63 | (struct sockaddr *)&addr, sizeof(addr)); 64 | if (count < data.size()) { 65 | throw std::system_error(BaseSocket::getLastError(), BaseSocket::getErrorCategory()); 66 | } 67 | } 68 | 69 | std::vector UDPSocket::Receive(size_t size) const { 70 | std::vector recvBuffer(size); 71 | 72 | auto len = recvfrom(_sockedFd, (char*)recvBuffer.data(), recvBuffer.size(), 0, NULL, NULL); 73 | Logger(LogLevel::TRACE) << "Received " << len << " bytes from UDP socket #" << _sockedFd << "."; 74 | if (len < 0) { 75 | throw std::system_error(BaseSocket::getLastError(), BaseSocket::getErrorCategory()); 76 | } 77 | 78 | return recvBuffer; 79 | } 80 | 81 | std::vector UDPSocket::ReceiveFrom(size_t size, EndPoint& endPoint) const { 82 | std::vector recvBuffer(size); 83 | struct sockaddr_in addr; 84 | socklen_t addrFromLength = sizeof(addr); 85 | auto len = recvfrom(_sockedFd, (char*)recvBuffer.data(), recvBuffer.size(), 0, (struct sockaddr*)&addr, &addrFromLength); 86 | Logger(LogLevel::TRACE) << "Received " << len << " bytes from UDP socket #" << _sockedFd << "."; 87 | if (len < 0) { 88 | throw std::system_error(BaseSocket::getLastError(), BaseSocket::getErrorCategory()); 89 | } 90 | 91 | endPoint = EndPoint(addr); 92 | return recvBuffer; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/sockets/UDPSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/18/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_SOCKETS_UDPSOCKET_H 6 | #define EIPSCANNER_SOCKETS_UDPSOCKET_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "BaseSocket.h" 12 | 13 | namespace eipScanner { 14 | namespace sockets { 15 | 16 | class UDPSocket : public BaseSocket{ 17 | public: 18 | using WPtr = std::weak_ptr; 19 | using SPtr = std::shared_ptr; 20 | using UPtr = std::unique_ptr; 21 | 22 | explicit UDPSocket(EndPoint endPoint); 23 | UDPSocket(std::string host, int port); 24 | virtual ~UDPSocket(); 25 | 26 | void Send(const std::vector& data) const override; 27 | std::vector Receive(size_t size) const override ; 28 | std::vector ReceiveFrom(size_t size, EndPoint& endPoint) const ; 29 | }; 30 | } 31 | } 32 | 33 | #endif // EIPSCANNER_SOCKETS_UDPSOCKET_H 34 | 35 | -------------------------------------------------------------------------------- /src/utils/Buffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_UTILS_BUFFER_H 6 | #define EIPSCANNER_UTILS_BUFFER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "cip/CipString.h" 12 | #include "cip/CipRevision.h" 13 | #include "sockets/EndPoint.h" 14 | 15 | namespace eipScanner { 16 | namespace utils { 17 | /** 18 | * @class Buffer 19 | * 20 | * @brief Implements decode and encode data according CIP specification 21 | * 22 | * An example: 23 | * @code 24 | * Buffer buffer1(); 25 | * cip::CipUint var1 = 1; 26 | * cip::CipDint var2 = 0xaa00000; 27 | * 28 | * buffer1 << var1 << var2; 29 | * 30 | * buffer1.data(); # => {0x01, 0x0, 0x0 ,0x0, 0x0, 0xaa} 31 | * @endcode 32 | */ 33 | class Buffer { 34 | public: 35 | 36 | /** 37 | * Creates an empty buffer 38 | * @param capacity the size that will be reserved in the buffer 39 | */ 40 | explicit Buffer(size_t capacity); 41 | 42 | /** 43 | * Creates a buffer that contains the given data 44 | * @param data The data to encode 45 | */ 46 | explicit Buffer(const std::vector& data); 47 | 48 | /** 49 | * Creates an empty buffer 50 | */ 51 | Buffer(); 52 | 53 | Buffer& operator << (uint8_t val); 54 | Buffer& operator >> (uint8_t& val); 55 | 56 | Buffer& operator << (int8_t val); 57 | Buffer& operator >> (int8_t& val); 58 | 59 | Buffer& operator << (uint16_t val); 60 | Buffer& operator >> (uint16_t& val); 61 | 62 | Buffer& operator << (int16_t val); 63 | Buffer& operator >> (int16_t& val); 64 | 65 | Buffer& operator << (uint32_t val); 66 | Buffer& operator >> (uint32_t& val); 67 | 68 | Buffer& operator << (int32_t val); 69 | Buffer& operator >> (int32_t& val); 70 | 71 | Buffer& operator << (uint64_t val); 72 | Buffer& operator >> (uint64_t& val); 73 | 74 | Buffer& operator << (int64_t val); 75 | Buffer& operator >> (int64_t& val); 76 | 77 | Buffer& operator << (float val); 78 | Buffer& operator >> (float & val); 79 | 80 | Buffer& operator << (double val); 81 | Buffer& operator >> (double& val); 82 | 83 | Buffer& operator << (const std::vector& val); 84 | Buffer& operator >> (std::vector& val); 85 | 86 | Buffer& operator << (const std::vector& val); 87 | Buffer& operator >> (std::vector& val); 88 | 89 | template 90 | utils::Buffer& operator<<(const cip::CipBaseString& cipSting) { 91 | return *this << cipSting.getLength() << cipSting.getData(); 92 | } 93 | 94 | template 95 | utils::Buffer& operator>>(cip::CipBaseString& cipSting) { 96 | T length = 0; 97 | *this >> length; 98 | std::vector data(length); 99 | *this >> data; 100 | 101 | cipSting = cip::CipBaseString(data); 102 | return *this; 103 | } 104 | 105 | Buffer& operator << (cip::CipRevision v); 106 | Buffer& operator >> (cip::CipRevision& val); 107 | 108 | Buffer& operator << (sockets::EndPoint v); 109 | Buffer& operator >> (sockets::EndPoint& val); 110 | 111 | std::vector data() const { return _buffer; } 112 | size_t size() const { return _buffer.size(); } 113 | size_t pos() const { return _position; } 114 | bool isValid() const { return _position <= _buffer.size(); } 115 | bool empty() const { return _position >= _buffer.size(); } 116 | private: 117 | std::vector _buffer; 118 | size_t _position; 119 | }; 120 | } 121 | } 122 | #endif // EIPSCANNER_BUFFER_H 123 | -------------------------------------------------------------------------------- /src/utils/Logger.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | #include 5 | #include 6 | 7 | #include "Logger.h" 8 | 9 | namespace eipScanner { 10 | namespace utils { 11 | LogLevel Logger::_globalLogLevel = LogLevel::INFO; 12 | LogAppenderIf::UPtr Logger::_appender = std::make_unique(); 13 | 14 | Logger::Logger(LogLevel logLevel): _logLevel{logLevel} { 15 | 16 | } 17 | 18 | Logger::~Logger() { 19 | if (_globalLogLevel != LogLevel::OFF && _logLevel <= _globalLogLevel) { 20 | _appender->print(_logLevel, (_stream).str()); 21 | } 22 | } 23 | 24 | void Logger::setLogLevel(LogLevel level) { 25 | _globalLogLevel = level; 26 | } 27 | 28 | void Logger::setAppender(LogAppenderIf::UPtr appender) { 29 | _appender = std::move(appender); 30 | } 31 | 32 | void ConsoleAppender::print(LogLevel logLevel,const std::string &msg) { 33 | const static std::map LOGLEVEL_NAMES = { 34 | std::make_pair(LogLevel::TRACE, "[TRACE] "), 35 | std::make_pair(LogLevel::DEBUG, "[DEBUG] "), 36 | std::make_pair(LogLevel::INFO, "[INFO] "), 37 | std::make_pair(LogLevel::WARNING, "[WARNING] "), 38 | std::make_pair(LogLevel::ERROR, "[ERROR] "), 39 | }; 40 | 41 | std::cout << LOGLEVEL_NAMES.at(logLevel) << msg << std::endl; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/utils/Logger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_UTILS_LOGGER_H 6 | #define EIPSCANNER_UTILS_LOGGER_H 7 | 8 | #include 9 | #include 10 | 11 | #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) 12 | #undef ERROR 13 | #endif 14 | 15 | namespace eipScanner { 16 | namespace utils { 17 | 18 | enum class LogLevel { 19 | OFF = 0, 20 | ERROR, 21 | WARNING, 22 | INFO, 23 | DEBUG, 24 | TRACE 25 | }; 26 | 27 | /** 28 | * @class LogAppenderIf 29 | * 30 | * @brief Interface to print message in the logger 31 | * @sa Logger 32 | */ 33 | class LogAppenderIf { 34 | public: 35 | using UPtr = std::unique_ptr; 36 | virtual ~LogAppenderIf() = default; 37 | 38 | virtual void print(LogLevel logLevel, const std::string& msg) = 0; 39 | }; 40 | 41 | /** 42 | * @class ConsoleAppender 43 | * 44 | * @brief Implements out log messages to std::cout 45 | */ 46 | class ConsoleAppender : public LogAppenderIf { 47 | public: 48 | using UPtr = std::unique_ptr; 49 | void print(LogLevel logLevel, const std::string& msg) override; 50 | }; 51 | 52 | 53 | /** 54 | * @class 55 | * 56 | * @brief Implement logging 57 | */ 58 | class Logger { 59 | public: 60 | /** 61 | * @brief Sets the lowest log level for all log messages 62 | * @note to set off all logs use LogLevel::OFF 63 | * @param level 64 | */ 65 | static void setLogLevel(LogLevel level); 66 | 67 | /** 68 | * @brief Sets appender to print messages for all log messages 69 | * 70 | * The default appender is ConsoleAppender 71 | * @param appender 72 | */ 73 | static void setAppender(LogAppenderIf::UPtr appender); 74 | 75 | Logger(LogLevel level); 76 | 77 | /** 78 | * @brief Add message to the log 79 | * @tparam T type of the data to print 80 | * @param msg The message to print 81 | * @return 82 | */ 83 | template 84 | std::ostringstream& operator << (T msg) { 85 | _stream << msg; 86 | return _stream; 87 | } 88 | 89 | /** 90 | * @brief Default destructor 91 | * 92 | * The destructor prints all messages, that were added by << operator, before the logger are destroyed 93 | */ 94 | ~Logger(); 95 | 96 | private: 97 | static LogLevel _globalLogLevel; 98 | static LogAppenderIf::UPtr _appender; 99 | 100 | LogLevel _logLevel; 101 | std::ostringstream _stream; 102 | }; 103 | } 104 | } 105 | 106 | #endif //EIPSCANNER_LOGGER_H 107 | -------------------------------------------------------------------------------- /src/vendor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(ra/powerFlex525) 2 | include_directories(yaskawa/mp3300iec) 3 | 4 | set( VENDOR_FILES 5 | # ra 6 | vendor/ra/powerFlex525/DPIFaultManager.cpp 7 | vendor/ra/powerFlex525/DPIFaultObject.cpp 8 | vendor/ra/powerFlex525/DPIFaultCode.cpp 9 | vendor/ra/powerFlex525/DPIFaultParameter.cpp 10 | 11 | # yaskawa 12 | vendor/yaskawa/mp3300iec/Yaskawa_EPath.cpp 13 | vendor/yaskawa/mp3300iec/Yaskawa_MessageRouter.cpp 14 | vendor/yaskawa/mp3300iec/Yaskawa_MessageRouterRequest.cpp 15 | 16 | PARENT_SCOPE 17 | ) -------------------------------------------------------------------------------- /src/vendor/ra/powerFlex525/DPIFaultCode.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by James Roth on 12/18/19. 3 | // 4 | 5 | #include "DPIFaultCode.h" 6 | 7 | 8 | namespace eipScanner { 9 | namespace vendor { 10 | namespace ra { 11 | namespace powerFlex525 { 12 | 13 | 14 | /* 15 | * constructor 16 | */ 17 | DPIFaultCode::DPIFaultCode::DPIFaultCode(int faultCode) { 18 | this->_faultCode = faultCode; 19 | } 20 | 21 | /* 22 | * destructor 23 | */ 24 | DPIFaultCode::~DPIFaultCode() {} 25 | 26 | 27 | /* 28 | * returns text related to fault 29 | */ 30 | DPIFaultCode::FaultDescriptions DPIFaultCode::getFaultDescription() { 31 | 32 | // make sure map contains faultCode - tried using try/catch but wasn't catching my exception 33 | if (this->containsFaultCode()) 34 | return this->_faultMap.at(this->_faultCode); 35 | else 36 | throw std::runtime_error("Fault code doesn't exist for Powerflex 525."); 37 | 38 | } 39 | 40 | 41 | /* 42 | * return bool whether or not the fault code exists in map 43 | */ 44 | bool DPIFaultCode::containsFaultCode() 45 | { 46 | // doesn't contain fault 47 | if (this->_faultMap.find(this->_faultCode) == this->_faultMap.end()) 48 | return false; 49 | else // contains fault 50 | return true; 51 | } 52 | 53 | 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/vendor/ra/powerFlex525/DPIFaultManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/11/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_DPIFAULTMANAGER_H 6 | #define EIPSCANNER_DPIFAULTMANAGER_H 7 | 8 | #include 9 | #include 10 | #include "DPIFaultObject.h" 11 | #include "SessionInfoIf.h" 12 | #include "DPIFaultParameter.h" 13 | 14 | namespace eipScanner { 15 | namespace vendor { 16 | namespace ra { 17 | namespace powerFlex525 { 18 | 19 | /** 20 | * Fault Manager command codes 21 | */ 22 | enum DPIFaultManagerCommands : cip::CipUsint { 23 | NO_OPERATION = 0, 24 | CLEAR_FAULT = 1, 25 | CLEAR_FAULT_QUEUE = 2, 26 | RESET_DEVICE = 3 27 | }; 28 | 29 | /** 30 | * @class DPIFaultManager 31 | * 32 | * @brief Implements a manager to retrieve new faults and clean its queue 33 | * 34 | * It use PrarameterObejcts instead of FaultObject because it doesn't contain the needed information 35 | * 36 | */ 37 | class DPIFaultManager { 38 | public: 39 | using NewFaultObjectHandler = std::function; 40 | using NewFaultHandler = std::function; 41 | using TrippedDeviceHandler = std::function; 42 | 43 | /** 44 | * @brief Default constructor (clearFaults = true, resetDevice = false, getFaultDetails = false) 45 | */ 46 | DPIFaultManager(); 47 | 48 | /** 49 | * @brief Constructor 50 | * @param clearFaults if true the manager clears the queue after it has retrieved a new fault 51 | * @param resetDevice isn't used yet 52 | * @param getFaultDetails if true the manager read all data from fault parameters 53 | */ 54 | explicit DPIFaultManager(bool clearFaults, bool resetDevice, bool getFaultDetails); 55 | 56 | /** 57 | * @brief Sets a callback to receive a new fault 58 | * @param handler 59 | */ 60 | void setNewFaultListener(NewFaultHandler handler); 61 | 62 | /** 63 | * @brief Sets a callback if the device changed trip-state 64 | * @param handler 65 | */ 66 | void setTrippedDeviceListener(TrippedDeviceHandler handler); 67 | 68 | /** 69 | * @brief reads fault parameters and calls NewFaultHandler handler if it gets a new one 70 | * @param si 71 | */ 72 | void handleFaultParameters(const SessionInfoIf::SPtr& si); 73 | void handleFaultParameters(const SessionInfoIf::SPtr& si, const MessageRouter::SPtr& messageRouter); 74 | 75 | /** 76 | * @brief Writs a command to DPI Fault Manager (e.g. clean fault or reset device) @sa DPIFaultManagerCommands 77 | * @param command the command to send 78 | * @param si the EIP session for explicit messaging 79 | */ 80 | void writeCommand(DPIFaultManagerCommands command, const SessionInfoIf::SPtr& si) const; 81 | 82 | /** 83 | * @note used for testing 84 | * @param command 85 | * @param si 86 | * @param messageRouter 87 | */ 88 | void writeCommand(DPIFaultManagerCommands command, const SessionInfoIf::SPtr& si, 89 | const MessageRouter::SPtr& messageRouter) const; 90 | 91 | private: 92 | NewFaultHandler _newFaultHandler; 93 | TrippedDeviceHandler _trippedDeviceHandler; 94 | cip::CipUsint _lastTrippedState; 95 | bool _clearFaultsQueue; // clears fault queue after reading all fault 96 | bool _resetFault; // stops device after hard fault and restarts 97 | bool _getFaultDetails; // returns details at time of fault (frequency, current, voltage) 98 | }; 99 | 100 | } 101 | } 102 | } 103 | } 104 | 105 | #endif // EIPSCANNER_DPIFAULTMANAGER_H 106 | -------------------------------------------------------------------------------- /src/vendor/ra/powerFlex525/DPIFaultObject.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/11/19. 3 | // 4 | 5 | 6 | #include "DPIFaultObject.h" 7 | #include "utils/Buffer.h" 8 | 9 | 10 | namespace eipScanner { 11 | namespace vendor { 12 | namespace ra { 13 | namespace powerFlex525 { 14 | 15 | using namespace cip; 16 | using utils::Buffer; 17 | 18 | enum DPIFaultObjectAttributeIds : CipUsint { 19 | FULL_INFORMATION = 0 20 | }; 21 | 22 | enum FaultTimeStampFlags : CipUint { 23 | VALID_DATA = 1, 24 | REAL_TIME = 1 << 1 25 | }; 26 | 27 | DPIFaultObject::DPIFaultObject(CipUint instanceId, const SessionInfoIf::SPtr &si) 28 | : DPIFaultObject(instanceId, si, std::make_shared()) { 29 | 30 | } 31 | 32 | const DPIFaultObject::FullInformation &DPIFaultObject::getFullInformation() const { 33 | return _fullInformation; 34 | } 35 | 36 | DPIFaultObject::DPIFaultObject(CipUint instanceId, const SessionInfoIf::SPtr &si, 37 | const MessageRouter::SPtr& messageRouter) 38 | : BaseObject(CLASS_ID, instanceId) 39 | , _fullInformation{} { 40 | 41 | auto response = messageRouter->sendRequest(si, ServiceCodes::GET_ATTRIBUTE_SINGLE, 42 | EPath(CLASS_ID, instanceId, DPIFaultObjectAttributeIds::FULL_INFORMATION)); 43 | 44 | if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) { 45 | Buffer buffer(response.getData()); 46 | std::vector faultText(16); 47 | buffer >> _fullInformation.faultCode 48 | >> _fullInformation.dsiPort 49 | >> _fullInformation.dsiDeviceObject 50 | >> faultText 51 | >> _fullInformation.timerValue; 52 | 53 | _fullInformation.faultText = CipString(faultText); 54 | 55 | cip::CipUint flags; 56 | buffer >> flags; 57 | _fullInformation.isValidData = flags & VALID_DATA; 58 | _fullInformation.isRealTime = flags & REAL_TIME; 59 | } else { 60 | logGeneralAndAdditionalStatus(response); 61 | throw std::runtime_error("Failed to read FULL_INFORMATION attribute"); 62 | } 63 | } 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/vendor/ra/powerFlex525/DPIFaultObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/11/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_VENDOR_DIPFAULTOBJECT_H 6 | #define EIPSCANNER_VENDOR_DIPFAULTOBJECT_H 7 | 8 | #include 9 | 10 | #include "cip/CipString.h" 11 | #include "BaseObject.h" 12 | #include "SessionInfoIf.h" 13 | #include "MessageRouter.h" 14 | 15 | namespace eipScanner { 16 | namespace vendor { 17 | namespace ra { 18 | namespace powerFlex525 { 19 | 20 | /** 21 | * @class DPIFaultObject 22 | * 23 | * @brief Implements interface to DPI Fault Object (0x97) of PowerFlex 525 24 | */ 25 | class DPIFaultObject : public BaseObject { 26 | public: 27 | static const cip::CipUint CLASS_ID = 0x97; 28 | 29 | /** 30 | * Informaion about the fault 31 | */ 32 | struct FullInformation { 33 | cip::CipUint faultCode; //!< the code of the fault (0 is no fault) 34 | cip::CipUsint dsiPort; //!< DSI port 35 | cip::CipUsint dsiDeviceObject; //!< DSI Device Object 36 | cip::CipString faultText; //!< the text of the fault 37 | cip::CipLword timerValue; //!< timer value 38 | bool isValidData; //!< true if the timer value valid 39 | bool isRealTime; //!< true if the time is real else it is elapsed 40 | }; 41 | 42 | /** 43 | * @brief Creates an instance and reads all its data via EIP 44 | * @param instanceId 45 | * @param fullAttributes if true, then read all the attributes 46 | * @param si the EIP session for explicit messaging 47 | */ 48 | DPIFaultObject(cip::CipUint instanceId, 49 | const SessionInfoIf::SPtr &si); 50 | 51 | /** 52 | * @note used for testing 53 | * @param instanceId 54 | * @param si 55 | * @param messageRouter 56 | */ 57 | DPIFaultObject(cip::CipUint instanceId, 58 | const SessionInfoIf::SPtr &si, const MessageRouter::SPtr& messageRouter); 59 | 60 | /** 61 | * @brief Gets the full information [AttrID=1] of the fault 62 | * @return 63 | */ 64 | const FullInformation &getFullInformation() const; 65 | 66 | private: 67 | FullInformation _fullInformation; 68 | }; 69 | } 70 | } 71 | } 72 | } 73 | #endif // EIPSCANNER_VENDOR_DIPFAULTOBJECT_H 74 | -------------------------------------------------------------------------------- /src/vendor/ra/powerFlex525/DPIFaultParameter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by James Roth on 12/19/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_DPIFAULTPARAMETER_HPP 6 | #define EIPSCANNER_DPIFAULTPARAMETER_HPP 7 | 8 | #include "cip/CipString.h" 9 | #include "BaseObject.h" 10 | #include "SessionInfoIf.h" 11 | #include "MessageRouter.h" 12 | #include "ParameterObject.h" 13 | #include "DPIFaultCode.h" 14 | 15 | namespace eipScanner { 16 | namespace vendor { 17 | namespace ra { 18 | namespace powerFlex525 { 19 | 20 | class DPIFaultParameter { 21 | public: 22 | 23 | struct FaultDetails{ 24 | int faultNumber; 25 | cip::CipUint faultCode; 26 | cip::CipLreal busVoltage; 27 | cip::CipLreal current; 28 | cip::CipLreal frequency; 29 | }; 30 | 31 | struct FullInformation { 32 | FaultDetails faultDetails; 33 | DPIFaultCode::FaultDescriptions faultDescription; 34 | }; 35 | 36 | DPIFaultParameter(const SessionInfoIf::SPtr &si, 37 | const MessageRouter::SPtr& messageRouter, 38 | int faultNumber, 39 | bool getFaultDetails); 40 | 41 | DPIFaultParameter(); 42 | 43 | const FullInformation &getFullInformation() const; 44 | const FaultDetails &getFaultDetails() const; // returns struct fault details 45 | void setFaultDetails(FaultDetails faultInfo); // sets fault details struct in FullInformation struct 46 | void setFaultDescription(DPIFaultCode::FaultDescriptions faultDescriptions); // sets fault description (info mapped from fault code) 47 | 48 | private: 49 | 50 | FullInformation _fullInformation; 51 | }; 52 | } 53 | } 54 | } 55 | } 56 | 57 | 58 | #endif //EIPSCANNER_DPIFAULTPARAMETER_HPP 59 | -------------------------------------------------------------------------------- /src/vendor/yaskawa/mp3300iec/Yaskawa_EPath.h: -------------------------------------------------------------------------------- 1 | #ifndef EIPSCANNER_YASKAWA_EPATH_H 2 | #define EIPSCANNER_YASKAWA_EPATH_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cip/Types.h" 9 | 10 | 11 | namespace eipScanner { 12 | namespace cip { 13 | class Yaskawa_EPath { 14 | public: 15 | Yaskawa_EPath(); 16 | explicit Yaskawa_EPath(CipUsint classId); 17 | Yaskawa_EPath(CipUsint classId, CipUsint objectId); 18 | Yaskawa_EPath(CipUsint classId, CipUsint objectId, CipUsint attributeId); 19 | std::vector packPaddedPath() const; 20 | void expandPaddedPath(const std::vector& data); 21 | 22 | CipUsint getClassId() const; 23 | CipUsint getObjectId() const; 24 | CipUsint getAttributeId() const; 25 | 26 | CipUsint getSizeInWords() const; 27 | 28 | std::string toString() const; 29 | bool operator==(const Yaskawa_EPath& other) const; 30 | 31 | private: 32 | CipUsint _classId; 33 | CipUsint _objectId; 34 | CipUsint _attributeId; 35 | 36 | CipUsint _size; 37 | }; 38 | } 39 | } 40 | 41 | 42 | #endif // EIPSCANNER_YASKAWA_EPATH_H 43 | -------------------------------------------------------------------------------- /src/vendor/yaskawa/mp3300iec/Yaskawa_MessageRouter.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "eip/EncapsPacketFactory.h" 4 | #include "utils/Buffer.h" 5 | #include "Yaskawa_MessageRouter.h" 6 | #include "Yaskawa_MessageRouterRequest.h" 7 | #include "cip/MessageRouterResponse.h" 8 | #include "eip/CommonPacketItemFactory.h" 9 | #include "eip/CommonPacket.h" 10 | #include "utils/Buffer.h" 11 | #include "utils/Logger.h" 12 | 13 | namespace eipScanner { 14 | using namespace cip; 15 | using namespace utils; 16 | using eip::CommonPacketItemFactory; 17 | using eip::CommonPacket; 18 | using eip::CommonPacketItem; 19 | using eip::EncapsPacket; 20 | using eip::EncapsPacketFactory; 21 | 22 | Yaskawa_MessageRouter::Yaskawa_MessageRouter() = default; 23 | 24 | Yaskawa_MessageRouter::~Yaskawa_MessageRouter() = default; 25 | 26 | MessageRouterResponse 27 | Yaskawa_MessageRouter::sendRequest(SessionInfoIf::SPtr si, CipUsint service, const Yaskawa_EPath &path, 28 | const std::vector &data) const{ 29 | return this->sendRequest(si, service, path, data, {}); 30 | } 31 | 32 | MessageRouterResponse 33 | Yaskawa_MessageRouter::sendRequest(SessionInfoIf::SPtr si, CipUsint service, const Yaskawa_EPath &path, 34 | const std::vector &data, 35 | const std::vector& additionalPacketItems) const{ 36 | assert(si); 37 | 38 | Logger(LogLevel::INFO) << "Send request: service=0x" << std::hex << static_cast(service) 39 | << " epath=" << path.toString(); 40 | 41 | Yaskawa_MessageRouterRequest request{service, path, data}; 42 | 43 | CommonPacketItemFactory commonPacketItemFactory; 44 | CommonPacket commonPacket; 45 | commonPacket << commonPacketItemFactory.createNullAddressItem(); 46 | commonPacket << commonPacketItemFactory.createUnconnectedDataItem(request.pack()); 47 | 48 | for(auto& item : additionalPacketItems) { 49 | commonPacket << item; 50 | } 51 | 52 | auto packetToSend = EncapsPacketFactory() 53 | .createSendRRDataPacket(si->getSessionHandle(), 0, commonPacket.pack()); 54 | 55 | auto receivedPacket = si->sendAndReceive(packetToSend); 56 | 57 | Buffer buffer(receivedPacket.getData()); 58 | cip::CipUdint interfaceHandle = 0; 59 | cip::CipUint timeout = 0; 60 | std::vector receivedData(receivedPacket.getData().size() - 6); 61 | 62 | buffer >> interfaceHandle >> timeout >> receivedData; 63 | commonPacket.expand(receivedData); 64 | 65 | MessageRouterResponse response; 66 | const CommonPacketItem::Vec &items = commonPacket.getItems(); 67 | 68 | response.expand(items.at(1).getData()); 69 | if (items.size() > 2) { 70 | response.setAdditionalPacketItems( 71 | CommonPacketItem::Vec(items.begin() + 2, items.end())); 72 | } 73 | 74 | 75 | return response; 76 | } 77 | 78 | MessageRouterResponse 79 | Yaskawa_MessageRouter::sendRequest(SessionInfoIf::SPtr si, CipUsint service, const Yaskawa_EPath &path) const{ 80 | return this->sendRequest(si, service, path, {}, {}); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/vendor/yaskawa/mp3300iec/Yaskawa_MessageRouter.h: -------------------------------------------------------------------------------- 1 | #ifndef EIPSCANNER_YASKAWA_MESSAGEROUTER_H 2 | #define EIPSCANNER_YASKAWA_MESSAGEROUTER_H 3 | 4 | #include 5 | #include "Yaskawa_EPath.h" 6 | #include "cip/Services.h" 7 | #include "cip/MessageRouterResponse.h" 8 | #include "eip/CommonPacketItem.h" 9 | #include "SessionInfo.h" 10 | 11 | namespace eipScanner { 12 | /** 13 | * @class MessageRouter 14 | * 15 | * @brief Implements the explicit messaging with EIP adapter 16 | */ 17 | class Yaskawa_MessageRouter { 18 | public: 19 | using SPtr = std::shared_ptr; 20 | 21 | /** 22 | * @brief Default constructor 23 | */ 24 | Yaskawa_MessageRouter(); 25 | 26 | /** 27 | * @brief Default destructor 28 | */ 29 | virtual ~Yaskawa_MessageRouter(); 30 | 31 | /** 32 | * @brief Sends an explicit requests to the EIP adapter by calling a CIP service 33 | * @param si the EIP session with the adapter 34 | * @param service the service code (for standard codes see eipScanner::cip::ServiceCodes) 35 | * @param path the path to an element in Object Model that provides the called service 36 | * @param data the encoded arguments of the service 37 | * @param additionalPacketItems (needed only for eipScanner::ConnectionManager) 38 | * @return the received response from the EIP adapter 39 | * @throw std::runtime_error 40 | * @throw std::system_error 41 | */ 42 | virtual cip::MessageRouterResponse sendRequest(SessionInfoIf::SPtr si, cip::CipUsint service, 43 | const cip::Yaskawa_EPath& path, const std::vector& data, 44 | const std::vector& additionalPacketItems) const; 45 | 46 | /** 47 | * @brief Sends an explicit requests to the EIP adapter by calling a CIP service 48 | * @param si the EIP session with the adapter 49 | * @param service the service code (for standard codes see eipScanner::cip::ServiceCodes) 50 | * @param path the path to an element in Object Model that provides the called service 51 | * @param data the encoded arguments of the service 52 | * @return the received response from the EIP adapter 53 | * @throw std::runtime_error 54 | * @throw std::system_error 55 | */ 56 | virtual cip::MessageRouterResponse sendRequest(SessionInfoIf::SPtr si, cip::CipUsint service, 57 | const cip::Yaskawa_EPath& path, const std::vector& data) const; 58 | 59 | /** 60 | * @brief Sends an explicit requests to the EIP adapter by calling a CIP service 61 | * @param si the EIP session with the adapter 62 | * @param service the service code (for standard codes see eipScanner::cip::ServiceCodes) 63 | * @param path the path to an element in Object Model that provides the called service 64 | * @return the received response from the EIP adapter 65 | * @throw std::runtime_error 66 | * @throw std::system_error 67 | */ 68 | virtual cip::MessageRouterResponse sendRequest(SessionInfoIf::SPtr si, cip::CipUsint service, 69 | const cip::Yaskawa_EPath& path) const; 70 | 71 | }; 72 | } 73 | 74 | 75 | #endif // EIPSCANNER_YASKAWA_MESSAGEROUTER_H 76 | -------------------------------------------------------------------------------- /src/vendor/yaskawa/mp3300iec/Yaskawa_MessageRouterRequest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Yaskawa_MessageRouterRequest.h" 3 | #include "Yaskawa_EPath.h" 4 | 5 | namespace eipScanner { 6 | namespace cip { 7 | using utils::Buffer; 8 | 9 | Yaskawa_MessageRouterRequest::Yaskawa_MessageRouterRequest(CipUsint serviceCode, 10 | const Yaskawa_EPath& ePath, const std::vector data) 11 | : _serviceCode{serviceCode} 12 | , _ePath{ePath} 13 | , _data(data) { 14 | } 15 | 16 | Yaskawa_MessageRouterRequest::~Yaskawa_MessageRouterRequest() = default; 17 | 18 | std::vector Yaskawa_MessageRouterRequest::pack() const { 19 | Buffer buffer; 20 | buffer << _serviceCode 21 | << _ePath.getSizeInWords() 22 | << _ePath.packPaddedPath() 23 | << _data; 24 | 25 | return buffer.data(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/vendor/yaskawa/mp3300iec/Yaskawa_MessageRouterRequest.h: -------------------------------------------------------------------------------- 1 | #ifndef EIPSCANNER_YASKAWA_MESSAGEROUTERREQUEST_H 2 | #define EIPSCANNER_YASKAWA_MESSAGEROUTERREQUEST_H 3 | 4 | #include 5 | #include 6 | 7 | #include "cip/Services.h" 8 | #include "Yaskawa_EPath.h" 9 | 10 | namespace eipScanner { 11 | namespace cip { 12 | class Yaskawa_MessageRouterRequest { 13 | public: 14 | Yaskawa_MessageRouterRequest(CipUsint serviceCode, const Yaskawa_EPath& ePath, const std::vector data); 15 | ~Yaskawa_MessageRouterRequest(); 16 | 17 | std::vector pack() const; 18 | private: 19 | CipUsint _serviceCode; 20 | Yaskawa_EPath _ePath; 21 | std::vector _data; 22 | }; 23 | 24 | } 25 | } 26 | 27 | 28 | #endif // EIPSCANNER_YASKAWA_MESSAGEROUTERREQUEST_H 29 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | include_directories("${PROJECT_SOURCE_DIR}/src") 3 | include_directories("${PROJECT_SOURCE_DIR}/test") 4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") 5 | 6 | find_package(GTest REQUIRED) 7 | find_package(GMock REQUIRED) 8 | 9 | add_executable(test_eipscanner 10 | cip/TestCipRevision.cpp 11 | cip/TestCipString.cpp 12 | cip/TestEPath.cpp 13 | cip/TestMessageRouterResponse.cpp 14 | 15 | eip/TestCommonPacket.cpp 16 | eip/TestCommonPacketItem.cpp 17 | eip/TestCommonPacketItemFactory.cpp 18 | eip/TestEncapsPacket.cpp 19 | eip/TestEncapsPacketFactory.cpp 20 | 21 | fileObject/TestFileObjectLoadedState.cpp 22 | fileObject/TestFileObjectUploadInProgressState.cpp 23 | 24 | sockets/TestEndPoint.cpp 25 | 26 | utils/TestBuffer.cpp 27 | 28 | vendor/ra/powerFlex525/TestDPIFaultManager.cpp 29 | vendor/ra/powerFlex525/TestDPIFaultObject.cpp 30 | vendor/ra/powerFlex525/TestDPIFaultParameter.cpp 31 | 32 | TestDiscoveryManager.cpp 33 | TestIdentityObject.cpp 34 | TestMessageRouter.cpp 35 | TestParameterObject.cpp 36 | test.cpp 37 | ) 38 | 39 | message(STATUS ${GTEST_BOTH_LIBRARIES}) 40 | message(STATUS ${GMOCK_BOTH_LIBRARIES}) 41 | 42 | include_directories(${GTEST_INCLUDE_DIRS}) 43 | include_directories(${GMOCK_INCLUDE_DIRS}) 44 | 45 | target_link_libraries(test_eipscanner 46 | ${GTEST_BOTH_LIBRARIES} 47 | ${GMOCK_BOTH_LIBRARIES} 48 | ${CMAKE_THREAD_LIBS_INIT} 49 | EIPScanner) 50 | 51 | add_test(TestEipScanner test_eipscanner) 52 | -------------------------------------------------------------------------------- /test/Mocks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/9/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_MOCKS_H 6 | #define EIPSCANNER_MOCKS_H 7 | 8 | #include 9 | #include "MessageRouter.h" 10 | 11 | class TMockMessageRouter : public eipScanner::MessageRouter { 12 | public: 13 | using SPtr = std::shared_ptr; 14 | 15 | MOCK_CONST_METHOD3(sendRequest, eipScanner::cip::MessageRouterResponse( 16 | eipScanner::SessionInfoIf::SPtr si, 17 | eipScanner::cip::CipUsint service, 18 | const eipScanner::cip::EPath &path)); 19 | 20 | MOCK_CONST_METHOD4(sendRequest, eipScanner::cip::MessageRouterResponse( 21 | eipScanner::SessionInfoIf::SPtr si, 22 | eipScanner::cip::CipUsint service, 23 | const eipScanner::cip::EPath &path, 24 | const std::vector &data)); 25 | 26 | MOCK_CONST_METHOD5(sendRequest, eipScanner::cip::MessageRouterResponse( 27 | eipScanner::SessionInfoIf::SPtr si, 28 | eipScanner::cip::CipUsint service, 29 | const eipScanner::cip::EPath &path, 30 | const std::vector &data, 31 | const eipScanner::eip::CommonPacketItem::Vec&)); 32 | }; 33 | 34 | class TMockSessionInfo : public eipScanner::SessionInfoIf { 35 | public: 36 | using SPtr = std::shared_ptr; 37 | MOCK_CONST_METHOD1(sendAndReceive, eipScanner::eip::EncapsPacket(const eipScanner::eip::EncapsPacket& packet)); 38 | MOCK_CONST_METHOD0(getSessionHandle, eipScanner::cip::CipUdint()); 39 | MOCK_CONST_METHOD0(getRemoteEndPoint, eipScanner::sockets::EndPoint()); 40 | }; 41 | 42 | class TMockSocket : public eipScanner::sockets::BaseSocket { 43 | public: 44 | using SPtr = std::shared_ptr; 45 | TMockSocket() : eipScanner::sockets::BaseSocket("", 0) {} 46 | MOCK_CONST_METHOD1(Send, void(const std::vector& data)); 47 | MOCK_CONST_METHOD1(Receive, std::vector(size_t size)); 48 | }; 49 | 50 | #endif //EIPSCANNER_MOCKS_H 51 | -------------------------------------------------------------------------------- /test/TestDiscoveryManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/18/19. 3 | // 4 | 5 | #include 6 | 7 | #include 8 | #include "Mocks.h" 9 | #include "DiscoveryManager.h" 10 | 11 | using namespace eipScanner; 12 | 13 | class TDiscoveryManager : public DiscoveryManager { 14 | public: 15 | TDiscoveryManager(sockets::BaseSocket::SPtr socket) 16 | : DiscoveryManager("255.255.255.255", 0, std::chrono::milliseconds(0)) 17 | , _mockSocket(std::move(socket)) { 18 | 19 | } 20 | 21 | protected: 22 | sockets::BaseSocket::SPtr makeSocket() const override { 23 | return _mockSocket; 24 | } 25 | 26 | sockets::BaseSocket::SPtr _mockSocket; 27 | 28 | }; 29 | class TestDiscoveryManager: public ::testing::Test { 30 | public: 31 | const static cip::CipUint OBJECT_ID = 1; 32 | const std::vector LIST_IDENTITY_REQUEST = { 33 | 0x63, 0x0, 0x0, 0x0, 0x0, 0x0, 34 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 35 | 0x0, 0x0, 36 | }; 37 | 38 | const std::vector LIST_IDENTITY_RESPONSE = { 39 | 0xc6, 0x63, 0x0, 0, 0x0, 0x0, 0x0, 40 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 41 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 42 | 0x2, 0x0, 43 | 0xc, 0x0, 0x42, 0x0, 0x1, 0x0, 0x0, 0x2, 0xaf, 0x12, 0xc0, 0xa8, 44 | 0x1, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x96, 0x0, 0x9, 0x0, 45 | 0x5, 0x1, 0x30, 0x0, 0x92, 0xe1, 0x8d, 0x80, 0x1d, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x46, 0x6c, 46 | 0x65, 0x78, 0x20, 0x35, 0x32, 0x35, 0x20, 0x31, 0x50, 0x20, 0x31, 0x31, 0x30, 0x56, 0x20, 0x20, 47 | 0x20, 0x2e, 0x35, 0x30, 0x48, 0x50, 0x0, 0x0, 0x0, 0xff, 48 | 49 | 0xc, 0x0, 0x42, 0x0, 0x1, 0x0, 0x0, 0x2, 0xaf, 0x12, 0xc0, 0xa8, 50 | 0x1, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x96, 0x0, 0x9, 0x0, 51 | 0x5, 0x1, 0x30, 0x0, 0x92, 0xe1, 0x8d, 0x80, 0x1d, 0x50, 0x6f, 0x77, 0x65, 0x72, 0x46, 0x6c, 52 | 0x65, 0x78, 0x20, 0x35, 0x32, 0x35, 0x20, 0x31, 0x50, 0x20, 0x31, 0x31, 0x30, 0x56, 0x20, 0x20, 53 | 0x20, 0x2e, 0x35, 0x30, 0x48, 0x50, 0x0, 0x0, 0x0, 0xff, 54 | 55 | }; 56 | 57 | void SetUp() override { 58 | _mockSocket = std::make_shared(); 59 | } 60 | 61 | TMockSocket::SPtr _mockSocket; 62 | }; 63 | 64 | TEST_F(TestDiscoveryManager, ShouldSendBroadcastMessageAndGetResponses) { 65 | TDiscoveryManager manager(_mockSocket); 66 | 67 | EXPECT_CALL(*_mockSocket, Send(LIST_IDENTITY_REQUEST)).Times(1); 68 | EXPECT_CALL(*_mockSocket, Receive(504)) 69 | .WillOnce(::testing::Return(LIST_IDENTITY_RESPONSE)) 70 | .WillOnce(::testing::Return(LIST_IDENTITY_RESPONSE)) 71 | .WillOnce(::testing::Throw(std::system_error(DISCOVERY_SOCKET_RECEIVE_END_ERROR_CODE, std::system_category()))); 72 | 73 | auto devices = manager.discover(); 74 | 75 | EXPECT_EQ(4, devices.size()); 76 | EXPECT_EQ("PowerFlex 525 1P 110V .50HP", devices[3].identityObject.getProductName()); 77 | EXPECT_EQ(1, devices[1].identityObject.getVendorId()); 78 | EXPECT_EQ("192.168.1.15:44818", devices[0].socketAddress.toString()); 79 | } -------------------------------------------------------------------------------- /test/TestIdentityObject.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/9/19. 3 | // 4 | 5 | #include 6 | #include "Mocks.h" 7 | #include "IdentityObject.h" 8 | 9 | using namespace eipScanner; 10 | 11 | class TestIdentityObject : public ::testing::Test { 12 | public: 13 | const static cip::CipUint OBJECT_ID = 1; 14 | const std::vector IDENTITY_DATA = { 15 | 0x01, 0x0, // vandor ID 16 | 0x02, 0x0, // device type 17 | 0x03, 0x0, // product code 18 | 0x03, 0x04, // revision 19 | 0x05, 0x06, //status 20 | 0x07, 0x08, 0x09, 0x0A, // serial number 21 | 5, 'D', 'E', 'V', '0', '1', //product name 22 | 23 | }; 24 | 25 | void SetUp() override { 26 | _messageRouter = std::make_shared(); 27 | _nullSession = nullptr; 28 | } 29 | 30 | TMockMessageRouter::SPtr _messageRouter; 31 | SessionInfoIf::SPtr _nullSession; 32 | }; 33 | 34 | TEST_F(TestIdentityObject, ShouldReadAllDataInConstructor) { 35 | cip::MessageRouterResponse response; 36 | response.setData(IDENTITY_DATA); 37 | 38 | EXPECT_CALL(*_messageRouter, sendRequest( 39 | _nullSession, 40 | cip::ServiceCodes::GET_ATTRIBUTE_ALL, 41 | cip::EPath(1,OBJECT_ID), 42 | std::vector() 43 | )).WillOnce(::testing::Return(response)); 44 | 45 | IdentityObject identityObject(OBJECT_ID, _nullSession, _messageRouter); 46 | 47 | EXPECT_EQ(1, identityObject.getVendorId()); 48 | EXPECT_EQ(2, identityObject.getDeviceType()); 49 | EXPECT_EQ(3, identityObject.getProductCode()); 50 | EXPECT_EQ(cip::CipRevision(3,4), identityObject.getRevision()); 51 | EXPECT_EQ(0x605, identityObject.getStatus()); 52 | EXPECT_EQ(0xA090807, identityObject.getSerialNumber()); 53 | EXPECT_EQ("DEV01", identityObject.getProductName()); 54 | } 55 | -------------------------------------------------------------------------------- /test/TestMessageRouter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/10/19. 3 | // 4 | 5 | #include 6 | #include "Mocks.h" 7 | #include "MessageRouter.h" 8 | #include "eip/CommonPacket.h" 9 | #include "eip/CommonPacketItem.h" 10 | #include "eip/CommonPacketItemFactory.h" 11 | #include "eip/EncapsPacketFactory.h" 12 | #include "cip/MessageRouterRequest.h" 13 | #include "cip/MessageRouterResponse.h" 14 | 15 | using namespace eipScanner; 16 | 17 | TEST(TestMessageRouter, ShouldFormAndParseCommonPackets) { 18 | auto mockSession = std::make_shared(); 19 | MessageRouter router; 20 | auto service = cip::ServiceCodes::SET_ATTRIBUTE_SINGLE; 21 | cip::EPath path(1,2,3); 22 | std::vector data = {0,1,2,3}; 23 | 24 | cip::MessageRouterRequest request(service, path, data); 25 | eip::CommonPacketItemFactory commonPacketItemFactory; 26 | 27 | auto additionalPacketItem = commonPacketItemFactory.createNullAddressItem(); 28 | 29 | eip::CommonPacket sendCommonPacket; 30 | sendCommonPacket << commonPacketItemFactory.createNullAddressItem(); 31 | sendCommonPacket << commonPacketItemFactory.createUnconnectedDataItem(request.pack()); 32 | sendCommonPacket << additionalPacketItem; 33 | 34 | auto packetToSend = eip::EncapsPacketFactory() 35 | .createSendRRDataPacket(10, 0, sendCommonPacket.pack()); 36 | 37 | eip::CommonPacket receiveCommonPacket; 38 | receiveCommonPacket << commonPacketItemFactory.createNullAddressItem(); 39 | receiveCommonPacket << commonPacketItemFactory.createUnconnectedDataItem({ 40 | static_cast(service | 0x80), 41 | 0, 42 | cip::GeneralStatusCodes::SUCCESS, 43 | 0 44 | }); 45 | receiveCommonPacket << additionalPacketItem; 46 | 47 | auto receivedPacket = eip::EncapsPacketFactory() 48 | .createSendRRDataPacket(10, 0, receiveCommonPacket.pack()); 49 | 50 | EXPECT_CALL(*mockSession, getSessionHandle()).WillOnce(::testing::Return(10)); 51 | EXPECT_CALL(*mockSession, sendAndReceive(packetToSend)).WillOnce(::testing::Return(receivedPacket)); 52 | 53 | auto response = router.sendRequest(mockSession, service, 54 | path, 55 | data, 56 | {additionalPacketItem}); 57 | 58 | EXPECT_EQ(cip::GeneralStatusCodes::SUCCESS, response.getGeneralStatusCode()); 59 | EXPECT_EQ(1, response.getAdditionalPacketItems().size()); 60 | EXPECT_EQ(additionalPacketItem, response.getAdditionalPacketItems().at(0)); 61 | } -------------------------------------------------------------------------------- /test/cip/TestCipRevision.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/8/19. 3 | // 4 | 5 | #include 6 | #include "cip/CipRevision.h" 7 | 8 | using eipScanner::cip::CipRevision; 9 | 10 | TEST(TestCipRevision, ShouldHaveConstructors) { 11 | CipRevision revision; 12 | 13 | EXPECT_EQ(CipRevision(0,0), revision); 14 | } 15 | 16 | TEST(TestCipRevision, ShouldConvertToString) { 17 | CipRevision revision(1,2); 18 | 19 | EXPECT_EQ(1, revision.getMajorRevision()); 20 | EXPECT_EQ(2, revision.getMinorRevision()); 21 | EXPECT_EQ("1.2", revision.toString()); 22 | } -------------------------------------------------------------------------------- /test/cip/TestCipString.cpp: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Created by Aleksey Timin on 12/4/19. 4 | // 5 | 6 | #include 7 | #include "cip/CipString.h" 8 | #include "utils/Buffer.h" 9 | 10 | using eipScanner::cip::CipBaseString; 11 | using eipScanner::utils::Buffer; 12 | 13 | TEST(TestCipString, ShuoldConvertToStdString) { 14 | CipBaseString cipString("Hello!"); 15 | 16 | EXPECT_EQ("Hello!", cipString.toStdString()); 17 | } 18 | 19 | TEST(TestCipString, ShuoldDecodeAndEncodeByBuffer) { 20 | CipBaseString srcString("Hello!"); 21 | CipBaseString dstString; 22 | 23 | Buffer buffer; 24 | buffer << srcString >> dstString; 25 | 26 | 27 | EXPECT_EQ(dstString.toStdString(), srcString.toStdString()); 28 | } -------------------------------------------------------------------------------- /test/cip/TestEPath.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/4/19. 3 | // 4 | 5 | #include 6 | #include "cip/EPath.h" 7 | 8 | using eipScanner::cip::EPath; 9 | 10 | TEST(TestEPath, ShouldExpandPaddedPath8b) { 11 | std::vector data = {0x20, 0x05, 0x24, 0x02, 0x30, 0x01}; 12 | EPath path; 13 | path.expandPaddedPath(data); 14 | 15 | EXPECT_EQ(EPath(0x05, 0x02, 0x01), path); 16 | } 17 | 18 | TEST(TestEPath, ShouldExpandPaddedPath16b) { 19 | std::vector data = {0x21, 0x00, 0x05, 0x00, 0x25, 0x00, 0x02, 0x00, 0x31, 0x00, 0x01, 0x00}; 20 | EPath path; 21 | path.expandPaddedPath(data); 22 | 23 | EXPECT_EQ(EPath(0x05, 0x02, 0x01), path); 24 | } 25 | 26 | TEST(TestEPath, ShouldExpandPaddedPathCombo) { 27 | std::vector data = {0x21, 0x00, 0x05, 0x00, 0x24, 0x02, 0x31, 0x00, 0x01, 0x00}; 28 | EPath path; 29 | path.expandPaddedPath(data); 30 | 31 | EXPECT_EQ(EPath(0x05, 0x02, 0x01), path); 32 | } 33 | 34 | TEST(TestEPath, ShouldExpandPaddedPathOnlyClass) { 35 | std::vector data = {0x21, 0x00, 0x05, 0x00}; 36 | EPath path; 37 | path.expandPaddedPath(data); 38 | 39 | EXPECT_EQ(EPath(0x05), path); 40 | } 41 | 42 | TEST(TestEPath, ShouldExpandPaddedPathOnlyClassAndObject) { 43 | std::vector data = {0x21, 0x00, 0x05, 0x00, 0x24, 0x02}; 44 | EPath path; 45 | path.expandPaddedPath(data); 46 | 47 | EXPECT_EQ(EPath(0x05, 0x02), path); 48 | } 49 | 50 | TEST(TestEPath, ShouldThrowExceptionIfThePathHasWrongSegment) { 51 | std::vector data = {0x21, 0x00, 0x05, 0x00, 0xf4, 0x02}; 52 | EPath path; 53 | 54 | EXPECT_THROW(path.expandPaddedPath(data), std::runtime_error); 55 | } 56 | 57 | TEST(TestEPath, ShouldThrowExceptionIfThePathNotComplited) { 58 | std::vector data = {0x21, 0x00, 0x05, 0x00, 0x24}; 59 | EPath path; 60 | 61 | EXPECT_THROW(path.expandPaddedPath(data), std::runtime_error); 62 | } -------------------------------------------------------------------------------- /test/cip/TestMessageRouterResponse.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/17/19. 3 | // 4 | 5 | #include 6 | #include "cip/MessageRouterResponse.h" 7 | 8 | using eipScanner::cip::MessageRouterResponse; 9 | 10 | TEST(TestMessageRouterResponse, ShouldThrowErroIfDataTooShort) { 11 | std::vector data = {0, 0, 0}; 12 | 13 | MessageRouterResponse response; 14 | EXPECT_THROW(response.expand(data), std::runtime_error); 15 | } 16 | 17 | 18 | TEST(TestMessageRouterResponse, ShouldThrowErroIfAdditionalStatusHasWrongSize) { 19 | std::vector data = {0, 0, 0, 2, 0, 1}; 20 | 21 | MessageRouterResponse response; 22 | EXPECT_THROW(response.expand(data), std::runtime_error); 23 | } 24 | -------------------------------------------------------------------------------- /test/eip/TestCommonPacket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/17/19. 3 | // 4 | 5 | #include 6 | #include "eip/CommonPacketItem.h" 7 | #include "eip/CommonPacketItemFactory.h" 8 | #include "eip/CommonPacket.h" 9 | 10 | using eipScanner::eip::CommonPacket; 11 | using eipScanner::eip::CommonPacketItem; 12 | using eipScanner::eip::CommonPacketItemFactory; 13 | using eipScanner::eip::CommonPacketItemIds; 14 | 15 | TEST(CommonPacket, ShouldExpandFromData) { 16 | auto item1 = CommonPacketItemFactory().createNullAddressItem().pack(); 17 | auto item2 = CommonPacketItemFactory().createUnconnectedDataItem({0x0, 0x2}).pack(); 18 | 19 | std::vector data = {0x2, 0x0}; 20 | data.insert(data.end(), item1.begin(), item1.end()); 21 | data.insert(data.end(), item2.begin(), item2.end()); 22 | 23 | 24 | CommonPacket cp; 25 | cp.expand(data); 26 | 27 | EXPECT_EQ(cp.getItems()[0].getTypeId(), CommonPacketItemIds::NULL_ADDR); 28 | EXPECT_EQ(cp.getItems()[1].getTypeId(), CommonPacketItemIds::UNCONNECTED_MESSAGE); 29 | } 30 | 31 | TEST(CommonPacket, ShouldThrowErrorIfDataIsInvalid) { 32 | auto item1 = CommonPacketItemFactory().createUnconnectedDataItem({}).pack(); 33 | auto item2 = CommonPacketItemFactory().createUnconnectedDataItem({}).pack(); 34 | 35 | std::vector invalidData = {0x2, 0x0}; 36 | invalidData.insert(invalidData.end(), item1.begin(), item1.end()); 37 | invalidData.insert(invalidData.end(), item2.begin(), item2.end()); 38 | invalidData.pop_back(); 39 | 40 | CommonPacket cp; 41 | EXPECT_THROW(cp.expand(invalidData), std::runtime_error); 42 | } -------------------------------------------------------------------------------- /test/eip/TestCommonPacketItem.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include 6 | #include "eip/CommonPacketItem.h" 7 | #include "eip/CommonPacketItemFactory.h" 8 | 9 | using eipScanner::eip::CommonPacketItem; 10 | using eipScanner::eip::CommonPacketItemFactory; 11 | 12 | TEST(CommonPacketItem, UnconnectedDataItem) { 13 | std::vector data = {1, 2, 3, 4}; 14 | CommonPacketItem item = CommonPacketItemFactory().createUnconnectedDataItem(data); 15 | 16 | std::vector expectedData = {0xB2, 0, 4, 0, 1, 2, 3, 4}; 17 | 18 | EXPECT_EQ(item.pack(), expectedData); 19 | } 20 | 21 | TEST(CommonPacketItem, NullAddressItem) { 22 | CommonPacketItem item = CommonPacketItemFactory().createNullAddressItem(); 23 | 24 | std::vector expectedData = {0, 0, 0, 0}; 25 | 26 | EXPECT_EQ(item.pack(), expectedData); 27 | } 28 | -------------------------------------------------------------------------------- /test/eip/TestCommonPacketItemFactory.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include 6 | #include "eip/CommonPacketItemFactory.h" 7 | 8 | using eipScanner::eip::CommonPacketItemFactory; 9 | using eipScanner::eip::CommonPacketItemIds; 10 | 11 | TEST(CommonPacketItemFactory, ShouldCreateUnconnectedDataItem) { 12 | std::vector data = {1,2,3,4}; 13 | auto item = CommonPacketItemFactory().createUnconnectedDataItem(data); 14 | 15 | std::vector exp_data = { 0xB2, 0, 4, 0, 1, 2, 3, 4 }; 16 | EXPECT_EQ(item.getTypeId(), CommonPacketItemIds::UNCONNECTED_MESSAGE); 17 | EXPECT_EQ(item.getLength(), data.size()); 18 | EXPECT_EQ(item.getData(), data); 19 | EXPECT_EQ(item.pack(), exp_data); 20 | } 21 | 22 | TEST(CommonPacketItemFactory, ShouldCreateNullAddressItem) { 23 | auto item = CommonPacketItemFactory().createNullAddressItem(); 24 | 25 | EXPECT_EQ(item.getTypeId(), CommonPacketItemIds::NULL_ADDR); 26 | EXPECT_EQ(item.getLength(), 0); 27 | } -------------------------------------------------------------------------------- /test/eip/TestEncapsPacket.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include 6 | #include "eip/EncapsPacket.h" 7 | 8 | using eipScanner::eip::EncapsPacket; 9 | using eipScanner::eip::EncapsCommands; 10 | using eipScanner::eip::EncapsStatusCodes; 11 | 12 | TEST(TestEncapsPacket, ShouldExpandData) { 13 | std::vector data = {0x6F, 0, 0xB, 0, 0xDD, 0xCC, 0xBB, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14 | 0, 0, 0, 0, 0, 0, 0, 0, 15 | 0x64, 0, 1, 2, 3, 4, 5}; 16 | 17 | EncapsPacket packet; 18 | packet.expand(data); 19 | 20 | EXPECT_EQ(EncapsCommands::SEND_RR_DATA, packet.getCommand()); 21 | EXPECT_EQ(11, packet.getLength()); 22 | EXPECT_EQ(EncapsStatusCodes::SUCCESS, packet.getStatusCode()); 23 | EXPECT_EQ(0xaabbccdd, packet.getSessionHandle()); 24 | EXPECT_EQ(std::vector({0, 0, 0, 0, 25 | 0x64, 0, 1, 2, 3, 4, 5}), packet.getData()); 26 | } 27 | 28 | TEST(TestEncapsPacket, ShouldThrowErrorIfThePackageTooShort) { 29 | std::vector data = {0x6F, 0, 0xB, 0, 0xDD, 0xCC, 0xBB, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 | 0, 0, 0}; 31 | 32 | EncapsPacket packet; 33 | EXPECT_THROW(packet.expand(data), std::runtime_error); 34 | } 35 | 36 | TEST(TestEncapsPacket, ShouldThrowErrorIfThePackageHasWrongLenghtOfData) { 37 | std::vector data = {0x6F, 0, 0xB, 0, 0xDD, 0xCC, 0xBB, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38 | 0, 0, 0, 0, 0, 0, 0, 0, 39 | 0x64, 0, 1, 2, 3, 4, 5, 10}; 40 | 41 | EncapsPacket packet; 42 | EXPECT_THROW(packet.expand(data), std::runtime_error); 43 | } -------------------------------------------------------------------------------- /test/eip/TestEncapsPacketFactory.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | #include 5 | #include "eip/EncapsPacketFactory.h" 6 | #include "eip/EncapsPacket.h" 7 | 8 | using eipScanner::eip::EncapsPacket; 9 | using eipScanner::eip::EncapsPacketFactory; 10 | 11 | TEST(TestEncapsPacketFactory, ShouldCreateRegisterSessionPacket) { 12 | std::vector expectedPacket = {0x65, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13 | 1, 0, 0, 0}; 14 | 15 | EncapsPacket packet = EncapsPacketFactory().createRegisterSessionPacket(); 16 | EXPECT_EQ(expectedPacket, packet.pack()); 17 | } 18 | 19 | 20 | TEST(TestEncapsPacketFactory, ShouldCreateUnRegisterSessionPacket) { 21 | std::vector expectedPacket = {0x66, 0, 0, 0, 0xdd, 0xcc, 0xbb, 0xaa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22 | 0, 0, 0, 0}; 23 | 24 | EncapsPacket packet = EncapsPacketFactory().createUnRegisterSessionPacket(0xaabbccdd); 25 | EXPECT_EQ(expectedPacket, packet.pack()); 26 | } 27 | 28 | TEST(TestEncapsPacketFactory, ShouldCreateSendRRDataPacket) { 29 | std::vector data = {1, 2, 3, 4, 5}; 30 | std::vector expectedPacket = {0x6F, 0, 0xB, 0, 0xDD, 0xCC, 0xBB, 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31 | 0, 0, 0, 0, 0, 0, 0, 0, 32 | 0x64, 0, 1, 2, 3, 4, 5}; 33 | 34 | EncapsPacket packet = EncapsPacketFactory().createSendRRDataPacket(0xaabbccdd, 100, data); 35 | EXPECT_EQ(expectedPacket, packet.pack()); 36 | } 37 | 38 | TEST(TestEncapsPacketFactory, ShouldCreateListIdentityPacket) { 39 | std::vector expectedPacket = {0x63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40 | 0, 0, 0, 0}; 41 | 42 | EncapsPacket packet = EncapsPacketFactory().createListIdentityPacket(); 43 | EXPECT_EQ(expectedPacket, packet.pack()); 44 | } -------------------------------------------------------------------------------- /test/fileObject/Mocks.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/24/19. 3 | // 4 | 5 | #ifndef EIPSCANNER_TEST_FILEOBJECT_MOCKS_H 6 | #define EIPSCANNER_TEST_FILEOBJECT_MOCKS_H 7 | 8 | #include "../Mocks.h" 9 | #include "MessageRouter.h" 10 | #include "FileObject.h" 11 | #include "fileObject/FileObjectState.h" 12 | #include "utils/Buffer.h" 13 | 14 | namespace eipScanner { 15 | namespace fileObject { 16 | using utils::Buffer; 17 | using eipScanner::fileObject::FileObjectAttributesCodes; 18 | using eipScanner::fileObject::FILE_OBJECT_CLASS_ID; 19 | 20 | 21 | static void mockGetFileObjectState(TMockMessageRouter::SPtr messageRouter, 22 | SessionInfoIf::SPtr si, cip::CipUint objectId, FileObjectStateCodes stateCode) { 23 | cip::MessageRouterResponse response; 24 | Buffer buffer; 25 | buffer << static_cast(stateCode); 26 | 27 | response.setData(buffer.data()); 28 | response.setGeneralStatusCode(cip::SUCCESS); 29 | 30 | EXPECT_CALL(*messageRouter, sendRequest(si, cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 31 | cip::EPath(FILE_OBJECT_CLASS_ID, objectId, 32 | static_cast(FileObjectAttributesCodes::STATE)), 33 | std::vector()) 34 | ).WillOnce(::testing::Return(response)); 35 | } 36 | 37 | } 38 | } 39 | #endif //EIPSCANNER_TEST_FILEOBJECT_MOCKS_H 40 | -------------------------------------------------------------------------------- /test/fileObject/TestFileObjectLoadedState.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/24/19. 3 | // 4 | #include 5 | #include 6 | 7 | #include "./Mocks.h" 8 | #include "FileObject.h" 9 | #include "fileObject/FileObjectLoadedState.h" 10 | #include "utils/Buffer.h" 11 | 12 | using namespace eipScanner; 13 | using eipScanner::fileObject::FileObjectLoadedState; 14 | using eipScanner::fileObject::FILE_OBJECT_CLASS_ID; 15 | using utils::Buffer; 16 | 17 | using ::testing::Return; 18 | 19 | class TestFileObjectLoadedState : public ::testing::Test { 20 | public: 21 | const cip::CipUint FILE_OBJECT_ID = 1; 22 | 23 | void SetUp() override { 24 | _messageRouter = std::make_shared(); 25 | fileObject::mockGetFileObjectState(_messageRouter, _nullSession, 26 | FILE_OBJECT_ID, FileObjectStateCodes::FILE_LOADED); 27 | 28 | _fileObject = std::make_unique(FILE_OBJECT_ID, _nullSession, _messageRouter); 29 | } 30 | 31 | static cip::MessageRouterResponse makeResponse() { 32 | cip::MessageRouterResponse response; 33 | Buffer buffer; 34 | const cip::CipUdint FILE_SIZE = 1000000; 35 | const cip::CipUsint TRANSFER_SIZE = 0xff; 36 | buffer << FILE_SIZE << TRANSFER_SIZE; 37 | 38 | response.setData(buffer.data()); 39 | response.setGeneralStatusCode(cip::SUCCESS); 40 | return response; 41 | } 42 | 43 | TMockMessageRouter::SPtr _messageRouter; 44 | FileObject::UPtr _fileObject; 45 | SessionInfoIf::SPtr _nullSession; 46 | }; 47 | 48 | TEST_F(TestFileObjectLoadedState, ShouldInitiateUpload) { 49 | cip::MessageRouterResponse response = makeResponse(); 50 | 51 | EXPECT_CALL(*_messageRouter, sendRequest(_nullSession, 0x4b, 52 | cip::EPath(FILE_OBJECT_CLASS_ID, FILE_OBJECT_ID), 53 | std::vector{0xff}) 54 | ).WillOnce(Return(response)); 55 | 56 | _fileObject->beginUpload(nullptr, [](auto status, auto fileContent){}); 57 | EXPECT_EQ(FileObjectStateCodes::TRANSFER_UPLOAD_IN_PROGRESS, _fileObject->getState()->getStateCode()); 58 | } 59 | 60 | TEST_F(TestFileObjectLoadedState, ShouldCallHandlerIfItFailedToInitiateUpload) { 61 | cip::MessageRouterResponse response = makeResponse(); 62 | response.setGeneralStatusCode(cip::GeneralStatusCodes::INVALID_PARAMETER); 63 | 64 | EXPECT_CALL(*_messageRouter, sendRequest(_nullSession, 0x4b, 65 | cip::EPath(FILE_OBJECT_CLASS_ID, FILE_OBJECT_ID), 66 | std::vector{0xff}) 67 | ).WillOnce(Return(response)); 68 | 69 | auto receivedStatus = cip::GeneralStatusCodes::SUCCESS; 70 | _fileObject->beginUpload(nullptr, [&receivedStatus](auto status, auto fileContent){ 71 | receivedStatus = status; 72 | }); 73 | 74 | EXPECT_EQ(cip::GeneralStatusCodes::INVALID_PARAMETER, receivedStatus); 75 | } -------------------------------------------------------------------------------- /test/sockets/TestEndPoint.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/10/19. 3 | // 4 | 5 | #include 6 | #include "sockets/EndPoint.h" 7 | 8 | using eipScanner::sockets::EndPoint; 9 | 10 | TEST(TestEndPoint, ShouldConvertIpAndHostToAddr) { 11 | EndPoint endPoint("127.0.0.1", 0xAA00); 12 | 13 | EXPECT_EQ("127.0.0.1", endPoint.getHost()); 14 | EXPECT_EQ(0xAA00, endPoint.getPort()); 15 | EXPECT_EQ(2, endPoint.getAddr().sin_family); 16 | EXPECT_EQ(0x0100007F, endPoint.getAddr().sin_addr.s_addr); 17 | EXPECT_EQ(0x00AA, endPoint.getAddr().sin_port); 18 | EXPECT_EQ("127.0.0.1:43520", endPoint.toString()); 19 | } 20 | 21 | TEST(TestEndPoint, ShouldConvertAddrToIpAndHost) { 22 | struct sockaddr_in addr{0}; 23 | addr.sin_family = 2; 24 | addr.sin_addr.s_addr = 0x0100007F; 25 | addr.sin_port = 0x00AA; 26 | 27 | EndPoint endPoint(addr); 28 | 29 | EXPECT_EQ("127.0.0.1", endPoint.getHost()); 30 | EXPECT_EQ(0xAA00, endPoint.getPort()); 31 | EXPECT_EQ(2, endPoint.getAddr().sin_family); 32 | EXPECT_EQ(0x0100007F, endPoint.getAddr().sin_addr.s_addr); 33 | EXPECT_EQ(0x00AA, endPoint.getAddr().sin_port); 34 | EXPECT_EQ("127.0.0.1:43520", endPoint.toString()); 35 | } 36 | 37 | TEST(TestEndPoint, ShouldNotThrowExceptionIfCanNotParseIP) { 38 | EndPoint endPoint("XXXX", 0); 39 | EXPECT_EQ(0, endPoint.getAddr().sin_addr.s_addr); 40 | } -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 11/16/19. 3 | // 4 | 5 | #include 6 | #include "utils/Logger.h" 7 | 8 | int main(int argc, char **argv) { 9 | eipScanner::utils::Logger::setLogLevel(eipScanner::utils::LogLevel::DEBUG); 10 | 11 | testing::InitGoogleTest(&argc, argv); 12 | return RUN_ALL_TESTS(); 13 | } -------------------------------------------------------------------------------- /test/vendor/ra/powerFlex525/TestDPIFaultObject.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/11/19. 3 | // 4 | 5 | 6 | #include 7 | #include "Mocks.h" 8 | #include "vendor/ra/powerFlex525/DPIFaultObject.h" 9 | 10 | using namespace eipScanner; 11 | using namespace eipScanner::vendor::ra::powerFlex525; 12 | 13 | class TestDPIFaultObject : public ::testing::Test { 14 | public: 15 | const static cip::CipUint OBJECT_ID = 1; 16 | const std::vector FULL_INFORMATION_DATA = { 17 | 0x01,0x0, // fault code 18 | 0x02, // DSI port 19 | 0x03, // DSI device 20 | 'E', 'R', 'R', 'O', 'R', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // fault text 21 | 0x01, 0x02, 0x03, 0x4, 0x5, 0x6, 0x7,0x8, // timer value 22 | 0x3, 0x0 23 | }; 24 | 25 | void SetUp() override { 26 | _messageRouter = std::make_shared(); 27 | _nullSession = nullptr; 28 | } 29 | 30 | TMockMessageRouter::SPtr _messageRouter; 31 | SessionInfoIf::SPtr _nullSession; 32 | }; 33 | 34 | TEST_F(TestDPIFaultObject, ShouldReadAllDataInConstructor) { 35 | cip::MessageRouterResponse response; 36 | response.setData(FULL_INFORMATION_DATA); 37 | 38 | EXPECT_CALL(*_messageRouter, sendRequest( 39 | _nullSession, 40 | cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 41 | cip::EPath(0x97, OBJECT_ID, 0) 42 | )).WillOnce(::testing::Return(response)); 43 | 44 | DPIFaultObject faultObject(OBJECT_ID, _nullSession, _messageRouter); 45 | 46 | const auto& fullInformation = faultObject.getFullInformation(); 47 | EXPECT_EQ(1, fullInformation.faultCode); 48 | EXPECT_EQ(2, fullInformation.dsiPort); 49 | EXPECT_EQ(3, fullInformation.dsiDeviceObject); 50 | EXPECT_EQ("ERROR ", fullInformation.faultText.toStdString()); 51 | EXPECT_EQ(0x0807060504030201, fullInformation.timerValue); 52 | EXPECT_TRUE(fullInformation.isValidData); 53 | EXPECT_TRUE(fullInformation.isRealTime); 54 | } 55 | 56 | TEST_F(TestDPIFaultObject, ShouldThrowExecptionIfFailedToGetData) { 57 | cip::MessageRouterResponse response; 58 | response.setData(FULL_INFORMATION_DATA); 59 | response.setGeneralStatusCode(cip::GeneralStatusCodes::INVALID_ATTRIBUTE_VALUE); 60 | 61 | EXPECT_CALL(*_messageRouter, sendRequest( 62 | _nullSession, 63 | cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 64 | cip::EPath(0x97, OBJECT_ID, 0) 65 | )).WillOnce(::testing::Return(response)); 66 | 67 | EXPECT_THROW(DPIFaultObject(OBJECT_ID, _nullSession, _messageRouter), std::runtime_error); 68 | } -------------------------------------------------------------------------------- /test/vendor/ra/powerFlex525/TestDPIFaultParameter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Aleksey Timin on 12/11/19. 3 | // 4 | 5 | 6 | #include 7 | #include "Mocks.h" 8 | #include "vendor/ra/powerFlex525/DPIFaultObject.h" 9 | #include "vendor/ra/powerFlex525/DPIFaultParameter.h" 10 | 11 | using namespace eipScanner; 12 | using namespace eipScanner::vendor::ra::powerFlex525; 13 | 14 | class TestDPIParameterObject : public ::testing::Test { 15 | public: 16 | void SetUp() override { 17 | _messageRouter = std::make_shared(); 18 | _nullSession = nullptr; 19 | } 20 | 21 | TMockMessageRouter::SPtr _messageRouter; 22 | SessionInfoIf::SPtr _nullSession; 23 | }; 24 | 25 | TEST_F(TestDPIParameterObject, ShouldReadAllDataInConstructor) { 26 | cip::MessageRouterResponse response; 27 | response.setData({4,0}); 28 | 29 | // fault code 30 | EXPECT_CALL(*_messageRouter, sendRequest( 31 | _nullSession, 32 | cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 33 | cip::EPath(0x0F, 7, 1) 34 | )).WillOnce(::testing::Return(response)); 35 | 36 | // volts 37 | response.setData({3,0}); 38 | EXPECT_CALL(*_messageRouter, sendRequest( 39 | _nullSession, 40 | cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 41 | cip::EPath(0x0F, 651, 1) 42 | )).WillOnce(::testing::Return(response)); 43 | 44 | // current 45 | response.setData({200,0}); 46 | EXPECT_CALL(*_messageRouter, sendRequest( 47 | _nullSession, 48 | cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 49 | cip::EPath(0x0F, 641, 1) 50 | )).WillOnce(::testing::Return(response)); 51 | 52 | // freq 53 | response.setData({100,0}); 54 | EXPECT_CALL(*_messageRouter, sendRequest( 55 | _nullSession, 56 | cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 57 | cip::EPath(0x0F, 631, 1) 58 | )).WillOnce(::testing::Return(response)); 59 | 60 | DPIFaultParameter faultParameter(_nullSession, _messageRouter, 1, true); 61 | 62 | auto fullInformation = faultParameter.getFullInformation(); 63 | EXPECT_EQ(1, fullInformation.faultDetails.frequency); 64 | EXPECT_EQ(2, fullInformation.faultDetails.current); 65 | EXPECT_EQ(3, fullInformation.faultDetails.busVoltage); 66 | EXPECT_EQ(4, fullInformation.faultDetails.faultCode); 67 | EXPECT_EQ(1, fullInformation.faultDetails.faultNumber); 68 | } 69 | 70 | TEST_F(TestDPIParameterObject, ShouldThrowExecptionIfFailedToGetData) { 71 | cip::MessageRouterResponse response; 72 | response.setGeneralStatusCode(cip::GeneralStatusCodes::INVALID_ATTRIBUTE_VALUE); 73 | 74 | EXPECT_CALL(*_messageRouter, sendRequest( 75 | _nullSession, 76 | cip::ServiceCodes::GET_ATTRIBUTE_SINGLE, 77 | cip::EPath(0x0F, 7, 1) 78 | )).WillOnce(::testing::Return(response)); 79 | 80 | EXPECT_THROW(DPIFaultParameter(_nullSession, _messageRouter, 1, false), std::runtime_error); 81 | } --------------------------------------------------------------------------------