59 Commits
Author SHA1 Message Date
Kyle Schwarz 07e4eca14a Heartbeat: Include DeviceStatus 2026-08-17 15:34:16 -04:00
Max BrombachandKyle Schwarz 936566170f Communication: Add GenericAPIDataMessage and GenericAPIStatusMessage types 2026-08-17 10:30:35 -04:00
Kyle Schwarz 3aaacb6cc8 Device: Refactor heartbeat 2026-08-06 13:21:35 -04:00
Max BrombachandKyle Schwarz 1dda9f5412 Device: Add sendSPIPortKeyOperation() 2026-08-04 15:42:21 -04:00
Max Brombach 78c2e3ff89 Device: RAD-wBMS: Fix missing include for disk driver 2026-08-04 18:06:01 +00:00
Kyle Schwarz afedd4bb1f Servd: Add pushRx error 2026-07-29 17:44:58 -04:00
Kyle Schwarz 9b01e488da Build: Resolve warnings 2026-07-24 13:09:29 -04:00
David RebbeandKyle Schwarz 77a886bbe0 C2: Validate MAC network IDs 2026-07-21 13:30:21 -04:00
David RebbeandKyle Schwarz aff547b738 C2: Report copied buffer lengths 2026-07-21 13:07:06 -04:00
Max BrombachandKyle Schwarz 7a5c8c2879 Device: RAD-wBMS: Add class, chips, and bootloader information 2026-07-20 13:01:56 -04:00
Thomas StoddardandKyle Schwarz 0dd8dbfb64 Communication: Command: Add Main51 errors 2026-07-07 21:47:52 -04:00
David RebbeandKyle Schwarz d708f8081a C2: Add termination group support 2026-07-07 21:25:23 -04:00
David RebbeandKyle Schwarz 743246e813 Legacy: Update icsnVC40.h to Vehicle Spy 3.26.3.9 2026-07-07 20:51:37 -04:00
Max BrombachandKyle Schwarz 4ed29b4a5e Device: Add Failed enum value to Device::OpenStatusType enum 2026-06-29 16:13:39 -04:00
Jonathan SchwartzandKyle Schwarz 55d69246b0 Device: FIRE3: Add EnterBootloader phase 2026-06-29 12:19:02 -04:00
David RebbeandKyle Schwarz 19232def41 C2: Add icsneoc2_device_force_disk_config_update 2026-06-29 09:55:18 -04:00
David RebbeandKyle Schwarz 9d91524dc0 C2: Add EthernetStatusMessage 2026-06-29 09:08:51 -04:00
David RebbeandKyle Schwarz d86e3164af C2: Add AppError support 2026-06-26 17:32:43 -04:00
David RebbeandKyle Schwarz 3d99898598 FIRE3: Fix termination groups 2026-06-25 16:59:09 -04:00
David RebbeandKyle Schwarz 4b9cf1a37f Device: RAD: Add reboot support 2026-06-25 14:06:43 -04:00
David RebbeandKyle Schwarz 40d83f3b7c C2: Add device_product_name_get() 2026-06-24 16:58:28 -04:00
David RebbeandKyle Schwarz 60918c4b6d C2: Add PerfTest setting 2026-06-24 15:16:51 -04:00
Bryant JonesandKyle Schwarz 749552e443 Device: Fix coremini loading
For:
- RADA2B
- RADComet2
- RADComet3
- RADGalaxy2
- RADMoonT1S
- RADStar2
2026-06-24 13:28:12 -04:00
David RebbeandKyle Schwarz 8af6d5f2a8 FIRE3: Add reboot support 2026-06-19 17:58:49 -04:00
David RebbeandKyle Schwarz 54fb89b675 Settings: Add Linux options 2026-06-19 17:12:42 -04:00
Thomas StoddardandKyle Schwarz 299766403f Settings: Add GPTP 2026-06-18 22:23:13 -04:00
Max Brombach 2d0b0c63ed Device: RAD-Star 2: Add bootloader pipeline and chip info 2026-06-18 15:30:21 +00:00
Bryant JonesandKyle Schwarz 30606be7a8 Settings: Add PerfTest 2026-06-18 11:12:13 -04:00
Kyle Schwarz 4dd97f734a Servd: Disable signal on send failure 2026-06-18 10:21:35 -04:00
Bryant JonesandKyle Schwarz 44d0e3de64 Gigastar2: Fix loading coremini 2026-06-10 18:08:02 -04:00
Kyle Schwarz 023f5aa089 Bindings: Python: Add AppError 2026-06-10 16:59:43 -04:00
Kyle Schwarz bb711ae7e2 Event: Store device as weak pointer 2026-06-02 17:19:23 -04:00
Kyle Schwarz 476b5ff35c FIRE3: Add MDIO_01 network 2026-06-02 13:19:56 -04:00
Jonathan SchwartzandKyle Schwarz 99792a0ee1 Communication: Increase max packet length 2026-06-02 11:10:16 -04:00
Max BrombachandKyle Schwarz d42c51d772 EventManager: Fix Event Manager being accessed after static destruction 2026-05-22 15:56:02 -04:00
Thomas StoddardandKyle Schwarz 3ab06199c3 Device: Add ExtendedCommand::GetAllMACAddresses 2026-05-22 11:40:44 -04:00
Max BrombachandKyle Schwarz 49e578a657 Device: RAD-Comet and RAD-Comet 2: Replace all references to RAD-Comet with RAD-Comet2 2026-05-19 13:17:12 -04:00
Kyle Schwarz 0bf279c7b9 Build: Add WORKING_DIRECTORY to git commands 2026-05-15 14:55:17 -04:00
Kyle Schwarz 9de0cf688c Build: Parse CMake version from git 2026-05-14 23:16:20 -04:00
David RebbeandKyle Schwarz eded388b83 C2: Add message timestamp support 2026-05-14 22:16:41 -04:00
Kyle Schwarz 00b7b4a6de CI: Set ICSPB_BOOTSTRAP_DIR 2026-05-14 21:29:24 -04:00
Thomas StoddardandKyle Schwarz 0ecb360195 RADComet1: Deprecate and replace with RADComet2 2026-05-14 20:20:46 -04:00
David RebbeandKyle Schwarz b3bb033ecb C2: Add Supported Device Types" 2026-05-14 19:08:11 -04:00
David RebbeandKyle Schwarz c3762bbf22 C2: Add ChipVersions
Also fixes some formatting issues.
2026-05-14 13:39:39 -04:00
Thomas StoddardandKyle Schwarz e926e10dc5 RADGigastar2: Add to MACsecConfig 2026-05-14 11:29:54 -04:00
Thomas StoddardandKyle Schwarz 5620c98eda Examples: Add more error reporting 2026-05-13 17:03:01 -04:00
Thomas StoddardandKyle Schwarz c466d75dae All: neoVI Explorer -> ICS Device Manager 2026-05-13 15:45:28 -04:00
Thomas StoddardandKyle Schwarz d7fc7ffa47 RADGigastar2 & RADGalaxy2: Add PhyEnableFor support 2026-05-12 18:38:32 -04:00
Kyle Schwarz 70ad76771e Servd: Clean up unused parseError 2026-05-12 16:57:04 -04:00
Kyle Schwarz 87c10410e8 Servd: Remove no devices found event 2026-05-12 15:58:11 -04:00
Thomas StoddardandKyle Schwarz 7cca96dcfb All: Switch to Servd default on
See README for details on how to install Servd.
2026-05-08 13:28:49 -04:00
Kyle Schwarz d45b72ef68 Settings: Increase response timeouts 2026-05-07 09:05:57 -04:00
Thomas StoddardandKyle Schwarz da02927da7 APIEvent: Add missing types 2026-05-06 16:57:00 -04:00
Thomas StoddardandKyle Schwarz b5a6f6ace6 Core: MACsec: Validate AN range and enable slots during addition 2026-05-06 13:47:25 -04:00
David RebbeandKyle Schwarz 0d2bbed7d5 C2: Add TC10 APIs 2026-05-06 11:04:44 -04:00
Kyle Schwarz a68b64c641 Device: Refactor supportsNetworkMutex 2026-05-05 20:35:51 -04:00
David RebbeandKyle Schwarz 9abd9389f4 Docs: Add Linux pcap info 2026-05-05 20:34:23 -04:00
David RebbeandKyle Schwarz d682627b40 CI: Fedora 44 & Ubuntu 26.04 2026-05-05 16:11:48 -04:00
David RebbeandKyle Schwarz 9ae3e115fc C2: Add Ethernet message support 2026-04-30 15:28:40 -04:00
204 changed files with 11573 additions and 2673 deletions
+55 -75
View File
@@ -35,30 +35,6 @@ unit_test windows/x64:
- libicsneo-win-x64 - libicsneo-win-x64
timeout: 5m timeout: 5m
build windows/x86:
stage: build
script:
- cmd /C ci\build-windows32.bat
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- libicsneo-win-x64
unit_test windows/x86:
stage: unit_test
script:
- build\libicsneo-unit-tests.exe
dependencies:
- build windows/x86
needs:
- build windows/x86
tags:
- libicsneo-win-x64
timeout: 5m
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Ubuntu # Ubuntu
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@@ -119,30 +95,6 @@ unit_test windows/x86:
- linux-build - linux-build
timeout: 5m timeout: 5m
build linux/ubuntu/2204/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:22.04
unit_test linux/ubuntu/2204/amd64/gcc:
<<: *test_linux_ubuntu_gcc
image: ubuntu:22.04
dependencies:
- build linux/ubuntu/2204/amd64/gcc
needs:
- build linux/ubuntu/2204/amd64/gcc
build linux/ubuntu/2204/amd64/clang:
<<: *build_linux_ubuntu_clang
image: ubuntu:22.04
unit_test linux/ubuntu/2204/amd64/clang:
<<: *test_linux_ubuntu_clang
image: ubuntu:22.04
dependencies:
- build linux/ubuntu/2204/amd64/clang
needs:
- build linux/ubuntu/2204/amd64/clang
build linux/ubuntu/2404/amd64/gcc: build linux/ubuntu/2404/amd64/gcc:
<<: *build_linux_ubuntu_gcc <<: *build_linux_ubuntu_gcc
image: ubuntu:24.04 image: ubuntu:24.04
@@ -167,6 +119,30 @@ unit_test linux/ubuntu/2404/amd64/clang:
needs: needs:
- build linux/ubuntu/2404/amd64/clang - build linux/ubuntu/2404/amd64/clang
build linux/ubuntu/2604/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:26.04
unit_test linux/ubuntu/2604/amd64/gcc:
<<: *test_linux_ubuntu_gcc
image: ubuntu:26.04
dependencies:
- build linux/ubuntu/2604/amd64/gcc
needs:
- build linux/ubuntu/2604/amd64/gcc
build linux/ubuntu/2604/amd64/clang:
<<: *build_linux_ubuntu_clang
image: ubuntu:26.04
unit_test linux/ubuntu/2604/amd64/clang:
<<: *test_linux_ubuntu_clang
image: ubuntu:26.04
dependencies:
- build linux/ubuntu/2604/amd64/clang
needs:
- build linux/ubuntu/2604/amd64/clang
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Fedora # Fedora
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@@ -243,30 +219,6 @@ unit_test linux/ubuntu/2404/amd64/clang:
- linux-build - linux-build
timeout: 5m timeout: 5m
build linux/fedora/42/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:42
unit_test linux/fedora/42/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:42
dependencies:
- build linux/fedora/42/amd64/gcc
needs:
- build linux/fedora/42/amd64/gcc
build linux/fedora/42/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:42
unit_test linux/fedora/42/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:42
dependencies:
- build linux/fedora/42/amd64/clang
needs:
- build linux/fedora/42/amd64/clang
build linux/fedora/43/amd64/gcc: build linux/fedora/43/amd64/gcc:
<<: *build_linux_fedora_gcc <<: *build_linux_fedora_gcc
image: fedora:43 image: fedora:43
@@ -291,6 +243,30 @@ unit_test linux/fedora/43/amd64/clang:
needs: needs:
- build linux/fedora/43/amd64/clang - build linux/fedora/43/amd64/clang
build linux/fedora/44/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:44
unit_test linux/fedora/44/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:44
dependencies:
- build linux/fedora/44/amd64/gcc
needs:
- build linux/fedora/44/amd64/gcc
build linux/fedora/44/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:44
unit_test linux/fedora/44/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:44
dependencies:
- build linux/fedora/44/amd64/clang
needs:
- build linux/fedora/44/amd64/clang
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Python Module # Python Module
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@@ -303,7 +279,8 @@ build python/linux/amd64:
CIBW_BEFORE_ALL: sh ci/bootstrap-cibuildwheel.sh && sh ci/bootstrap-libpcap.sh CIBW_BEFORE_ALL: sh ci/bootstrap-cibuildwheel.sh && sh ci/bootstrap-libpcap.sh
CIBW_BUILD: "*manylinux*" # no musl CIBW_BUILD: "*manylinux*" # no musl
CIBW_ARCHS: x86_64 CIBW_ARCHS: x86_64
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install CIBW_ENVIRONMENT: PCAP_ROOT=/project/libpcap/install
SKBUILD_CMAKE_DEFINE: ICSPB_BOOTSTRAP_DIR=/project/icspb
script: script:
- sh ci/build-wheel-posix.sh - sh ci/build-wheel-posix.sh
artifacts: artifacts:
@@ -318,7 +295,8 @@ build python/linux/arm64:
CIBW_BEFORE_ALL: sh ci/bootstrap-cibuildwheel.sh && sh ci/bootstrap-libpcap.sh CIBW_BEFORE_ALL: sh ci/bootstrap-cibuildwheel.sh && sh ci/bootstrap-libpcap.sh
CIBW_BUILD: "*manylinux*" # no musl CIBW_BUILD: "*manylinux*" # no musl
CIBW_ARCHS: aarch64 CIBW_ARCHS: aarch64
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install CIBW_ENVIRONMENT: PCAP_ROOT=/project/libpcap/install
SKBUILD_CMAKE_DEFINE: ICSPB_BOOTSTRAP_DIR=/project/icspb
script: script:
- sh ci/build-wheel-posix.sh - sh ci/build-wheel-posix.sh
artifacts: artifacts:
@@ -332,7 +310,8 @@ build python/macos:
variables: variables:
CIBW_BEFORE_ALL: sh ci/bootstrap-libpcap.sh CIBW_BEFORE_ALL: sh ci/bootstrap-libpcap.sh
CIBW_ARCHS: arm64 CIBW_ARCHS: arm64
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=$CI_PROJECT_DIR/libpcap/install CIBW_ENVIRONMENT: PCAP_ROOT=$CI_PROJECT_DIR/libpcap/install
SKBUILD_CMAKE_DEFINE: ICSPB_BOOTSTRAP_DIR=$CI_PROJECT_DIR/icspb
MACOSX_DEPLOYMENT_TARGET: 10.14 MACOSX_DEPLOYMENT_TARGET: 10.14
script: script:
- sh ci/build-wheel-posix.sh - sh ci/build-wheel-posix.sh
@@ -347,6 +326,7 @@ build python/windows:
variables: variables:
CIBW_ARCHS: AMD64 CIBW_ARCHS: AMD64
CIBW_ENVIRONMENT: CMAKE_GENERATOR=Ninja CIBW_ENVIRONMENT: CMAKE_GENERATOR=Ninja
SKBUILD_CMAKE_DEFINE: ICSPB_BOOTSTRAP_DIR=$CI_PROJECT_DIR/icspb
script: script:
- cmd /c ci\build-wheel-windows.bat - cmd /c ci\build-wheel-windows.bat
artifacts: artifacts:
+45 -2
View File
@@ -1,5 +1,43 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(libicsneo VERSION 1.0.0)
function(git_tag_version VERSION_RESULT)
set(${VERSION_RESULT} "0.0.0.0" PARENT_SCOPE)
find_package(Git)
if(NOT Git_FOUND)
return()
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE LAST_TAG
RESULT_VARIABLE RESULT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT RESULT EQUAL 0)
return()
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-list ${LAST_TAG}..HEAD --count
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE COMMIT_COUNT_SINCE_TAG
RESULT_VARIABLE RESULT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT RESULT EQUAL 0)
return()
endif()
set(${VERSION_RESULT} "${LAST_TAG}.${COMMIT_COUNT_SINCE_TAG}" PARENT_SCOPE)
endfunction()
git_tag_version(LIBICSNEO_VERSION)
if(LIBICSNEO_VERSION STREQUAL "0.0.0.0")
message(WARNING "Unable to parse version from git history")
endif()
project(libicsneo VERSION ${LIBICSNEO_VERSION})
cmake_policy(SET CMP0074 NEW) cmake_policy(SET CMP0074 NEW)
if(POLICY CMP0135) if(POLICY CMP0135)
@@ -44,7 +82,7 @@ if(MSVC)
add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING) add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
add_definitions(-D_ITERATOR_DEBUG_LEVEL=0) add_definitions(-D_ITERATOR_DEBUG_LEVEL=0)
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(LIBICSNEO_COMPILER_WARNINGS -Wall -Wno-switch -Wno-unknown-pragmas) set(LIBICSNEO_COMPILER_WARNINGS -Wall -Wno-unknown-pragmas)
endif() endif()
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
@@ -192,10 +230,14 @@ set(SRC_FILES
communication/message/logdatamessage.cpp communication/message/logdatamessage.cpp
communication/message/tc10statusmessage.cpp communication/message/tc10statusmessage.cpp
communication/message/gptpstatusmessage.cpp communication/message/gptpstatusmessage.cpp
communication/message/allmacaddressesmessage.cpp
communication/message/ethernetstatusmessage.cpp communication/message/ethernetstatusmessage.cpp
communication/message/networkmutexmessage.cpp communication/message/networkmutexmessage.cpp
communication/message/clientidmessage.cpp communication/message/clientidmessage.cpp
communication/message/transmitmessage.cpp communication/message/transmitmessage.cpp
communication/message/spiportkeymessage.cpp
communication/message/genericapidatamessage.cpp
communication/message/genericapistatusmessage.cpp
communication/packet/flexraypacket.cpp communication/packet/flexraypacket.cpp
communication/packet/canpacket.cpp communication/packet/canpacket.cpp
communication/packet/a2bpacket.cpp communication/packet/a2bpacket.cpp
@@ -306,6 +348,7 @@ add_library(icsneocpp
api/icsneocpp/icsneocpp.cpp api/icsneocpp/icsneocpp.cpp
api/icsneocpp/event.cpp api/icsneocpp/event.cpp
api/icsneocpp/eventmanager.cpp api/icsneocpp/eventmanager.cpp
api/icsneocpp/heartbeat.cpp
api/icsneocpp/version.cpp api/icsneocpp/version.cpp
${SRC_FILES} ${SRC_FILES}
) )
+9 -1
View File
@@ -4,6 +4,14 @@ libicsneo is the [Intrepid Control Systems](https://intrepidcs.com/) device
communication library. Installation and usage documentation can be found within communication library. Installation and usage documentation can be found within
each of the respective APIs. each of the respective APIs.
## Installation
libicsneo relies on Servd, IntrepidCS's device communication server.
Servd can be installed for all platforms from https://cdn.intrepidcs.net/servd/.
Instructions for installing each API can be found in its respective documentation.
## Documentation ## Documentation
- [C++](https://libicsneo.readthedocs.io/en/latest/icsneocpp/) - [C++](https://libicsneo.readthedocs.io/en/latest/icsneocpp/)
@@ -37,7 +45,7 @@ each of the respective APIs.
- RAD-Pluto - RAD-Pluto
- RAD-Star 2 - RAD-Star 2
- RAD-SuperMoon - RAD-SuperMoon
- RADComet - RAD-wBMS
- ValueCAN 3 - ValueCAN 3
- ValueCAN 4 - ValueCAN 4
+348 -20
View File
@@ -5,6 +5,7 @@
#include "icsneo/device/devicefinder.h" #include "icsneo/device/devicefinder.h"
#include "icsneo/icsneocpp.h" #include "icsneo/icsneocpp.h"
#include "icsneo/communication/io.h" #include "icsneo/communication/io.h"
#include "icsneo/communication/network.h"
#include <string> #include <string>
#include <vector> #include <vector>
@@ -12,8 +13,10 @@
#include <map> #include <map>
#include <algorithm> #include <algorithm>
#include <optional> #include <optional>
#include <chrono>
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include <cstring>
using namespace icsneo; using namespace icsneo;
@@ -68,6 +71,8 @@ icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, char* valu
"Script load prepare failed", // icsneoc2_error_script_load_prepare_failed "Script load prepare failed", // icsneoc2_error_script_load_prepare_failed
"Close failed", // icsneoc2_error_close_failed "Close failed", // icsneoc2_error_close_failed
"Reconnect failed", // icsneoc2_error_reconnect_failed "Reconnect failed", // icsneoc2_error_reconnect_failed
"Invalid data", // icsneoc2_error_invalid_data
"Force disk config update failed", // icsneoc2_error_force_disk_config_update_failed
}; };
static_assert(std::size(error_strings) == icsneoc2_error_maxsize, static_assert(std::size(error_strings) == icsneoc2_error_maxsize,
"error_strings is out of sync with _icsneoc2_error_t enum - update both together"); "error_strings is out of sync with _icsneoc2_error_t enum - update both together");
@@ -83,6 +88,61 @@ icsneoc2_error_t icsneoc2_error_code_get(icsneoc2_error_t error_code, char* valu
return safe_str_copy(value, value_length, error_strings[error_code]) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; return safe_str_copy(value, value_length, error_strings[error_code]) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
} }
icsneoc2_error_t icsneoc2_supported_devices_enumerate(icsneoc2_supported_device_t** supported_devices) {
if(!supported_devices) {
return icsneoc2_error_invalid_parameters;
}
auto device_types = DeviceFinder::GetSupportedDevices();
icsneoc2_supported_device_t* head = nullptr;
icsneoc2_supported_device_t* tail = nullptr;
for(auto& device_type : device_types) {
auto* node = new (std::nothrow) icsneoc2_supported_device_t;
if(!node) {
return icsneoc2_error_out_of_memory;
}
node->device_type = device_type;
node->next = nullptr;
if(!head) {
head = node;
} else {
tail->next = node;
}
tail = node;
}
*supported_devices = head;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_supported_devices_free(icsneoc2_supported_device_t* supported_devices) {
if(!supported_devices) {
return icsneoc2_error_invalid_parameters;
}
while(supported_devices) {
auto* next = supported_devices->next;
delete supported_devices;
supported_devices = next;
}
return icsneoc2_error_success;
}
icsneoc2_supported_device_t* icsneoc2_supported_devices_next(const icsneoc2_supported_device_t* supported_device) {
if(!supported_device) {
return nullptr;
}
return supported_device->next;
}
icsneoc2_error_t icsneoc2_supported_device_get(const icsneoc2_supported_device_t* supported_device, icsneoc2_devicetype_t* device_type) {
if(!supported_device || !device_type) {
return icsneoc2_error_invalid_parameters;
}
*device_type = static_cast<icsneoc2_devicetype_t>(supported_device->device_type);
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_type_name_get(icsneoc2_devicetype_t device_type, char* value, size_t* value_length) { icsneoc2_error_t icsneoc2_device_type_name_get(icsneoc2_devicetype_t device_type, char* value, size_t* value_length) {
if(!value || !value_length) { if(!value || !value_length) {
return icsneoc2_error_invalid_parameters; return icsneoc2_error_invalid_parameters;
@@ -213,7 +273,9 @@ static icsneoc2_error_t open_device_with_options(std::shared_ptr<Device> dev, ic
if(!dev) { if(!dev) {
return icsneoc2_error_invalid_device; return icsneoc2_error_invalid_device;
} }
if(!dev->enableMessagePolling(std::make_optional<MessageFilter>())) { MessageFilter filter;
filter.includeInternalInAny = true;
if(!dev->enableMessagePolling(std::make_optional(filter))) {
return icsneoc2_error_enable_message_polling_failed; return icsneoc2_error_enable_message_polling_failed;
} }
if(!dev->isOpen() && !dev->open()) { if(!dev->isOpen() && !dev->open()) {
@@ -250,12 +312,12 @@ icsneoc2_error_t icsneoc2_device_open_serial(const char* serial, icsneoc2_open_o
std::string_view target(serial); std::string_view target(serial);
for(auto* cur = devs; cur; cur = cur->next) { for(auto* cur = devs; cur; cur = cur->next) {
if(cur->device && cur->device->getSerial() == target) { if(cur->device && cur->device->getSerial() == target) {
if (res = icsneoc2_device_create(cur, device); res != icsneoc2_error_success) { if(res = icsneoc2_device_create(cur, device); res != icsneoc2_error_success) {
icsneoc2_enumeration_free(devs); icsneoc2_enumeration_free(devs);
return res; return res;
} }
res = open_device_with_options((*device)->device, options); res = open_device_with_options((*device)->device, options);
if (res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
icsneoc2_device_free(*device); icsneoc2_device_free(*device);
*device = nullptr; *device = nullptr;
} }
@@ -278,12 +340,12 @@ icsneoc2_error_t icsneoc2_device_open_first(icsneoc2_devicetype_t device_type, i
} }
for(auto* cur = devs; cur; cur = cur->next) { for(auto* cur = devs; cur; cur = cur->next) {
if(cur->device && !cur->device->isOpen()) { if(cur->device && !cur->device->isOpen()) {
if (res = icsneoc2_device_create(cur, device); res != icsneoc2_error_success) { if(res = icsneoc2_device_create(cur, device); res != icsneoc2_error_success) {
icsneoc2_enumeration_free(devs); icsneoc2_enumeration_free(devs);
return res; return res;
} }
res = open_device_with_options((*device)->device, options); res = open_device_with_options((*device)->device, options);
if (res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
icsneoc2_device_free(*device); icsneoc2_device_free(*device);
*device = nullptr; *device = nullptr;
} }
@@ -301,7 +363,7 @@ icsneoc2_error_t icsneoc2_device_reconnect(icsneoc2_device_t* device, icsneoc2_o
return res; return res;
} }
// If the device is currently open, close it first before trying to reconnect // If the device is currently open, close it first before trying to reconnect
if (device->device->isOpen()) { if(device->device->isOpen()) {
res = icsneoc2_device_close(device); res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
return res; return res;
@@ -341,7 +403,7 @@ icsneoc2_error_t icsneoc2_device_free(icsneoc2_device_t* device) {
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
return res; return res;
} }
if (device->device->isOpen()) { if(device->device->isOpen()) {
res = icsneoc2_device_close(device); res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
return res; return res;
@@ -381,6 +443,16 @@ icsneoc2_error_t icsneoc2_device_serial_get(const icsneoc2_device_t* device, cha
return safe_str_copy(value, value_length, dev->getSerial()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; return safe_str_copy(value, value_length, dev->getSerial()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
} }
icsneoc2_error_t icsneoc2_device_product_name_get(const icsneoc2_device_t* device, char* value, size_t* value_length) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto dev = device->device;
// Copy the string into value
return safe_str_copy(value, value_length, dev->getProductName()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
}
icsneoc2_error_t icsneoc2_device_pcb_serial_get(const icsneoc2_device_t* device, uint8_t* value, size_t* value_length) { icsneoc2_error_t icsneoc2_device_pcb_serial_get(const icsneoc2_device_t* device, uint8_t* value, size_t* value_length) {
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
@@ -394,35 +466,94 @@ icsneoc2_error_t icsneoc2_device_pcb_serial_get(const icsneoc2_device_t* device,
return icsneoc2_error_invalid_type; return icsneoc2_error_invalid_type;
} }
const auto& data = *pcbSerial; const auto& data = *pcbSerial;
if(value) { if(!value) {
size_t copyLen = std::min(*value_length, data.size()); *value_length = data.size();
std::copy(data.begin(), data.begin() + copyLen, value); return icsneoc2_error_success;
} }
*value_length = data.size(); size_t copyLen = std::min(*value_length, data.size());
std::copy(data.begin(), data.begin() + copyLen, value);
*value_length = copyLen;
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_device_mac_address_get(const icsneoc2_device_t* device, uint8_t* value, size_t* value_length) { icsneoc2_error_t icsneoc2_device_mac_addresses_enumerate(const icsneoc2_device_t* device, icsneoc2_mac_addr_entry_t** mac_entries) {
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
return res; return res;
} }
if(!value_length) { if(!mac_entries) {
return icsneoc2_error_invalid_parameters; return icsneoc2_error_invalid_parameters;
} }
auto macAddress = device->device->getMACAddress(); const auto result = device->device->getMACAddresses();
if(!macAddress.has_value()) { if(result.empty()) {
return icsneoc2_error_invalid_type; return icsneoc2_error_invalid_type;
} }
const auto& data = *macAddress; icsneoc2_mac_addr_entry_t* head = nullptr;
if(value) { icsneoc2_mac_addr_entry_t* tail = nullptr;
size_t copyLen = std::min(*value_length, data.size()); for(auto addr_pair : result) {
std::copy(data.begin(), data.begin() + copyLen, value); auto* node = new (std::nothrow) icsneoc2_mac_addr_entry_t;
if(!node) {
icsneoc2_mac_addresses_free(head);
return icsneoc2_error_out_of_memory;
}
node->network_id = static_cast<uint16_t>(addr_pair.first);
std::copy(addr_pair.second.begin(), addr_pair.second.end(), node->address);
node->next = nullptr;
if(!head) {
head = node;
} else {
tail->next = node;
}
tail = node;
} }
*value_length = data.size(); *mac_entries = head;
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_mac_network_id_get(const icsneoc2_mac_addr_entry_t* mac_address, icsneoc2_netid_t* network_id) {
if(!mac_address || !network_id) {
return icsneoc2_error_invalid_parameters;
}
const auto netid = static_cast<Network::NetID>(mac_address->network_id);
*network_id = Network::GetCoreMiniNetworkFromNetID(netid).has_value()
? mac_address->network_id
: static_cast<icsneoc2_netid_t>(icsneoc2_netid_invalid);
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_mac_address_get(const icsneoc2_mac_addr_entry_t* mac_address, uint8_t* value, size_t* value_length) {
if(!mac_address || !value_length) {
return icsneoc2_error_invalid_parameters;
}
if(!value) {
*value_length = static_cast<size_t>(ICSNEO_MAC_ADDRESS_LEN);
return icsneoc2_error_success;
}
size_t copyLen = std::min(*value_length, static_cast<size_t>(ICSNEO_MAC_ADDRESS_LEN));
std::copy(mac_address->address, mac_address->address + copyLen, value);
*value_length = copyLen;
return icsneoc2_error_success;
}
icsneoc2_mac_addr_entry_t* icsneoc2_mac_addresses_next(const icsneoc2_mac_addr_entry_t* mac_address) {
if(!mac_address) {
return nullptr;
}
return mac_address->next;
}
icsneoc2_error_t icsneoc2_mac_addresses_free(icsneoc2_mac_addr_entry_t* mac_address) {
if(!mac_address) {
return icsneoc2_error_invalid_parameters;
}
while(mac_address) {
auto* next = mac_address->next;
delete mac_address;
mac_address = next;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_go_online(const icsneoc2_device_t* device, bool go_online) { icsneoc2_error_t icsneoc2_device_go_online(const icsneoc2_device_t* device, bool go_online) {
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
@@ -639,6 +770,107 @@ icsneoc2_error_t icsneoc2_device_supports_tc10(const icsneoc2_device_t* device,
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_device_tc10_wake_request(const icsneoc2_device_t* device, icsneoc2_netid_t netid) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->requestTC10Wake(static_cast<Network::NetID>(netid))) {
return icsneoc2_error_transmit_message_failed;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_tc10_sleep_request(const icsneoc2_device_t* device, icsneoc2_netid_t netid) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->requestTC10Sleep(static_cast<Network::NetID>(netid))) {
return icsneoc2_error_transmit_message_failed;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_tc10_status_get(const icsneoc2_device_t* device, icsneoc2_netid_t netid, icsneoc2_tc10_sleep_status_t* sleep_status, icsneoc2_tc10_wake_status_t* wake_status) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto cpp_status = device->device->getTC10Status(static_cast<Network::NetID>(netid));
if(!cpp_status.has_value()) {
return icsneoc2_error_invalid_type;
}
if(sleep_status) {
*sleep_status = static_cast<icsneoc2_tc10_sleep_status_t>(cpp_status->sleepStatus);
}
if(wake_status) {
*wake_status = static_cast<icsneoc2_tc10_wake_status_t>(cpp_status->wakeStatus);
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_supports_gptp(const icsneoc2_device_t* device, bool* supported) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!supported) {
return icsneoc2_error_invalid_parameters;
}
*supported = device->device->supportsGPTP();
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_gptp_status_get(const icsneoc2_device_t* device, uint32_t timeout_ms, icsneoc2_gptp_status_t* status) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!status) {
return icsneoc2_error_invalid_parameters;
}
auto cpp_status = device->device->getGPTPStatus(std::chrono::milliseconds(timeout_ms));
if(!cpp_status.has_value()) {
return icsneoc2_error_get_settings_failure;
}
status->current_time_seconds = cpp_status->currentTime.seconds;
status->current_time_nanoseconds = cpp_status->currentTime.nanoseconds;
status->ms_offset_ns = cpp_status->msOffsetNs;
status->is_sync = cpp_status->isSync;
status->link_status = cpp_status->linkStatus;
status->link_delay_ns = cpp_status->linkDelayNS;
status->selected_role = cpp_status->selectedRole;
status->as_capable = cpp_status->asCapable;
status->is_syntonized = cpp_status->isSyntonized;
status->short_format = cpp_status->shortFormat;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_supports_reboot(const icsneoc2_device_t* device, bool* supported) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!supported) {
return icsneoc2_error_invalid_parameters;
}
*supported = device->device->supportsReboot();
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_reboot(const icsneoc2_device_t* device, bool safe) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->reboot(safe)) {
return icsneoc2_error_transmit_message_failed;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_device_digital_io_get(const icsneoc2_device_t* device, icsneoc2_io_type_t type, uint32_t number, bool* value) { icsneoc2_error_t icsneoc2_device_digital_io_get(const icsneoc2_device_t* device, icsneoc2_io_type_t type, uint32_t number, bool* value) {
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
@@ -875,6 +1107,21 @@ icsneoc2_error_t icsneoc2_device_format_disk(const icsneoc2_device_t* device, ic
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_device_force_disk_config_update(const icsneoc2_device_t* device, icsneoc2_disk_details_t* disk_details) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!disk_details || !disk_details->details) {
return icsneoc2_error_invalid_parameters;
}
if(!device->device->forceDiskConfigUpdate(*disk_details->details)) {
return icsneoc2_error_force_disk_config_update_failed;
}
return icsneoc2_error_success;
}
static icsneoc2_error_t get_supported_networks(const icsneoc2_device_t* device, const std::vector<Network>& nets, icsneoc2_netid_t* networks, size_t* count) { static icsneoc2_error_t get_supported_networks(const icsneoc2_device_t* device, const std::vector<Network>& nets, icsneoc2_netid_t* networks, size_t* count) {
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
@@ -1167,3 +1414,84 @@ icsneoc2_error_t icsneoc2_script_status_max_coremini_size_kb_get(const icsneoc2_
*value = script_status->status->maxCoreminiSizeKB; *value = script_status->status->maxCoreminiSizeKB;
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_device_chip_versions_enumerate(const icsneoc2_device_t* device, icsneoc2_chip_versions_t** chip_versions, bool refresh, size_t* count) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!chip_versions) {
return icsneoc2_error_invalid_parameters;
}
auto version_reports = device->device->getChipVersions(refresh);
if(count) {
*count = version_reports.size();
}
icsneoc2_chip_versions_t* head = nullptr;
icsneoc2_chip_versions_t* tail = nullptr;
for(auto& version_report : version_reports) {
auto* node = new (std::nothrow) icsneoc2_chip_versions_t;
if(!node) {
icsneoc2_chip_versions_free(head);
return icsneoc2_error_out_of_memory;
}
node->version_report = version_report;
node->next = nullptr;
if(!head) {
head = node;
} else {
tail->next = node;
}
tail = node;
}
*chip_versions = head;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_chip_versions_free(icsneoc2_chip_versions_t* chip_versions) {
if(!chip_versions) {
return icsneoc2_error_invalid_parameters;
}
while(chip_versions) {
auto* next = chip_versions->next;
delete chip_versions;
chip_versions = next;
}
return icsneoc2_error_success;
}
icsneoc2_chip_versions_t* icsneoc2_chip_versions_next(const icsneoc2_chip_versions_t* chip_versions) {
if(!chip_versions) {
return nullptr;
}
return chip_versions->next;
}
icsneoc2_error_t icsneoc2_chip_versions_props_get(const icsneoc2_chip_versions_t* chip_versions, char* name, size_t* name_length, uint8_t* major, uint8_t* minor, uint8_t* maintenance, uint8_t* build) {
if(!chip_versions) {
return icsneoc2_error_invalid_parameters;
}
auto& report = chip_versions->version_report;
if(name && name_length) {
if(!safe_str_copy(name,name_length, report.name)) {
return icsneoc2_error_string_copy_failed;
}
}
if(major) {
*major = report.major;
}
if(minor) {
*minor = report.minor;
}
if(maintenance) {
*maintenance = report.maintenance;
}
if(build) {
*build = report.build;
}
return icsneoc2_error_success;
}
+21
View File
@@ -13,6 +13,11 @@
using namespace icsneo; using namespace icsneo;
typedef struct icsneoc2_supported_device_t {
DeviceType device_type;
icsneoc2_supported_device_t* next;
} icsneoc2_supported_device_t;
typedef struct icsneoc2_message_t { typedef struct icsneoc2_message_t {
std::shared_ptr<Message> message; std::shared_ptr<Message> message;
} icsneoc2_message_t; } icsneoc2_message_t;
@@ -38,6 +43,22 @@ typedef struct icsneoc2_script_status_t {
std::shared_ptr<ScriptStatusMessage> status; std::shared_ptr<ScriptStatusMessage> status;
} icsneoc2_script_status_t; } icsneoc2_script_status_t;
typedef struct icsneoc2_chip_versions_t {
VersionReport version_report;
icsneoc2_chip_versions_t* next;
} icsneoc2_chip_versions_t;
typedef struct icsneoc2_termination_group_t {
std::vector<icsneoc2_netid_t> netids;
icsneoc2_termination_group_t* next;
} icsneoc2_termination_group_t;
typedef struct icsneoc2_mac_addr_entry_t {
uint16_t network_id;
uint8_t address[ICSNEO_MAC_ADDRESS_LEN];
icsneoc2_mac_addr_entry_t* next;
} icsneoc2_mac_addr_entry_t;
/** /**
* Safely copies a std::string to a char array. * Safely copies a std::string to a char array.
* *
+293
View File
@@ -7,8 +7,10 @@
#include "icsneo/communication/message/message.h" #include "icsneo/communication/message/message.h"
#include "icsneo/communication/message/canmessage.h" #include "icsneo/communication/message/canmessage.h"
#include "icsneo/communication/message/canerrormessage.h" #include "icsneo/communication/message/canerrormessage.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/message/linmessage.h" #include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/message/ethernetmessage.h" #include "icsneo/communication/message/ethernetmessage.h"
#include "icsneo/communication/message/ethernetstatusmessage.h"
#include "icsneo/communication/packet/canpacket.h" #include "icsneo/communication/packet/canpacket.h"
icsneoc2_error_t icsneoc2_message_is_valid(icsneoc2_message_t* message, bool* is_valid) { icsneoc2_error_t icsneoc2_message_is_valid(icsneoc2_message_t* message, bool* is_valid) {
@@ -69,6 +71,14 @@ icsneoc2_error_t icsneoc2_netid_name_get(icsneoc2_netid_t netid, char* value, si
return safe_str_copy(value, value_length, netid_str) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed; return safe_str_copy(value, value_length, netid_str) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
} }
icsneoc2_error_t icsneoc2_netid_network_type_get(icsneoc2_netid_t netid, icsneoc2_network_type_t* network_type) {
if(!network_type) {
return icsneoc2_error_invalid_parameters;
}
*network_type = static_cast<icsneoc2_network_type_t>(Network::GetTypeOfNetID(static_cast<Network::NetID>(netid), true));
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_netid_set(icsneoc2_message_t* message, icsneoc2_netid_t netid) { icsneoc2_error_t icsneoc2_message_netid_set(icsneoc2_message_t* message, icsneoc2_netid_t netid) {
if(!message) { if(!message) {
return icsneoc2_error_invalid_parameters; return icsneoc2_error_invalid_parameters;
@@ -118,6 +128,28 @@ icsneoc2_error_t icsneoc2_message_data_get(icsneoc2_message_t* message, uint8_t*
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_message_timestamp_set(icsneoc2_message_t* message, uint64_t timestamp) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
if(!message->message) {
return icsneoc2_error_invalid_message;
}
message->message->timestamp = timestamp;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_timestamp_get(icsneoc2_message_t* message, uint64_t* timestamp) {
if(!message || !timestamp) {
return icsneoc2_error_invalid_parameters;
}
if(!message->message) {
return icsneoc2_error_invalid_message;
}
*timestamp = message->message->timestamp;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_can_create(icsneoc2_message_t** message) { icsneoc2_error_t icsneoc2_message_can_create(icsneoc2_message_t** message) {
if(!message) { if(!message) {
return icsneoc2_error_invalid_parameters; return icsneoc2_error_invalid_parameters;
@@ -286,6 +318,44 @@ icsneoc2_error_t icsneoc2_message_can_error_props_get(
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_message_is_app_error(icsneoc2_message_t* message, bool* is_app_error) {
if(!message || !is_app_error) {
return icsneoc2_error_invalid_parameters;
}
*is_app_error = std::dynamic_pointer_cast<AppErrorMessage>(message->message) != nullptr;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_app_error_props_get(icsneoc2_message_t* message,
icsneoc2_app_error_type_t* error_type, icsneoc2_netid_t* error_netid) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
auto app_err = std::dynamic_pointer_cast<AppErrorMessage>(message->message);
if(!app_err) {
return icsneoc2_error_invalid_type;
}
if(error_type) {
*error_type = static_cast<icsneoc2_app_error_type_t>(app_err->getAppErrorType());
}
if(error_netid) {
*error_netid = static_cast<icsneoc2_netid_t>(app_err->errorNetID);
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_app_error_string_get(icsneoc2_message_t* message,
char* value, size_t* value_length) {
if(!message || !value_length) {
return icsneoc2_error_invalid_parameters;
}
auto app_err = std::dynamic_pointer_cast<AppErrorMessage>(message->message);
if(!app_err) {
return icsneoc2_error_invalid_type;
}
return safe_str_copy(value, value_length, app_err->getAppErrorString()) ? icsneoc2_error_success : icsneoc2_error_string_copy_failed;
}
icsneoc2_error_t icsneoc2_message_is_lin(icsneoc2_message_t* message, bool* is_lin) { icsneoc2_error_t icsneoc2_message_is_lin(icsneoc2_message_t* message, bool* is_lin) {
if(!message || !is_lin) { if(!message || !is_lin) {
return icsneoc2_error_invalid_parameters; return icsneoc2_error_invalid_parameters;
@@ -415,3 +485,226 @@ icsneoc2_error_t icsneoc2_message_lin_calc_checksum(icsneoc2_message_t* message)
LINMessage::calcChecksum(*lin_msg); LINMessage::calcChecksum(*lin_msg);
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_message_eth_create(icsneoc2_message_t** message) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
try {
*message = new icsneoc2_message_t;
(*message)->message = std::make_shared<EthernetMessage>();
} catch(const std::bad_alloc&) {
return icsneoc2_error_out_of_memory;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_eth_props_set(icsneoc2_message_t* message, const icsneoc2_message_eth_flags_t* flags, const bool* has_fcs, const uint32_t* fcs) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
auto eth_msg = std::dynamic_pointer_cast<EthernetMessage>(message->message);
if(!eth_msg) {
return icsneoc2_error_invalid_type;
}
if(flags) {
eth_msg->frameTooShort = (*flags & ICSNEOC2_MESSAGE_ETH_FLAGS_FRAME_TOO_SHORT);
eth_msg->noPadding = (*flags & ICSNEOC2_MESSAGE_ETH_FLAGS_NO_PADDING);
eth_msg->fcsVerified = (*flags & ICSNEOC2_MESSAGE_ETH_FLAGS_FCS_VERIFIED);
eth_msg->txAborted = (*flags & ICSNEOC2_MESSAGE_ETH_FLAGS_TX_ABORTED);
eth_msg->crcError = (*flags & ICSNEOC2_MESSAGE_ETH_FLAGS_CRC_ERROR);
}
if(has_fcs && !*has_fcs) {
eth_msg->fcs = std::nullopt;
} else if(has_fcs && *has_fcs) {
// I'm pretty sure we can leave this alone, setting fcs below will take care of the behavior,
// otherwise we get into a weird state where we have to set fcs to zero if fcs is null but has_fcs is true.
}
if (fcs) {
eth_msg->fcs = *fcs;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_eth_props_get(icsneoc2_message_t* message, icsneoc2_message_eth_flags_t* flags, bool* has_fcs, uint32_t* fcs) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
auto eth_msg = std::dynamic_pointer_cast<EthernetMessage>(message->message);
if(!eth_msg) {
return icsneoc2_error_invalid_type;
}
if(flags) {
*flags = 0;
if(eth_msg->noPadding)
*flags |= ICSNEOC2_MESSAGE_ETH_FLAGS_NO_PADDING;
if(eth_msg->fcsVerified)
*flags |= ICSNEOC2_MESSAGE_ETH_FLAGS_FCS_VERIFIED;
if(eth_msg->txAborted)
*flags |= ICSNEOC2_MESSAGE_ETH_FLAGS_TX_ABORTED;
if(eth_msg->crcError)
*flags |= ICSNEOC2_MESSAGE_ETH_FLAGS_CRC_ERROR;
if(eth_msg->frameTooShort)
*flags |= ICSNEOC2_MESSAGE_ETH_FLAGS_FRAME_TOO_SHORT;
}
if(has_fcs) {
*has_fcs = eth_msg->fcs.has_value();
}
if(fcs) {
if(eth_msg->fcs) {
*fcs = eth_msg->fcs.value();
} else {
*fcs = 0;
}
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_eth_mac_get(icsneoc2_message_t* message, uint8_t* dst_mac, uint8_t* src_mac) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
auto eth_msg = std::dynamic_pointer_cast<EthernetMessage>(message->message);
if(!eth_msg) {
return icsneoc2_error_invalid_type;
}
if(dst_mac) {
if(auto mac = eth_msg->getDestinationMAC(); mac.has_value()) {
const auto& mac_value = mac.value();
std::memcpy(dst_mac, mac_value.data(), mac_value.size());
} else {
return icsneoc2_error_invalid_data;
}
}
if(src_mac) {
if(auto mac = eth_msg->getSourceMAC(); mac.has_value()) {
const auto& mac_value = mac.value();
std::memcpy(src_mac, mac_value.data(), mac_value.size());
} else {
return icsneoc2_error_invalid_data;
}
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_eth_ether_type_get(icsneoc2_message_t* message, uint16_t* ether_type) {
if(!message || !ether_type) {
return icsneoc2_error_invalid_parameters;
}
auto eth_msg = std::dynamic_pointer_cast<EthernetMessage>(message->message);
if(!eth_msg) {
return icsneoc2_error_invalid_type;
}
if (auto et = eth_msg->getEtherType(); et.has_value()) {
*ether_type = et.value();
} else {
return icsneoc2_error_invalid_data;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_eth_t1s_props_set(icsneoc2_message_t* message, const icsneoc2_message_eth_t1s_flags_t* flags, const uint8_t* node_id, const uint8_t* burst_count, const uint8_t* symbol_type) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
auto eth_msg = std::dynamic_pointer_cast<EthernetMessage>(message->message);
if(!eth_msg) {
return icsneoc2_error_invalid_type;
}
// If all parameters are null/zero, clear the T1S state. Otherwise, if any parameters are set and we don't have a T1S state, create it.
if(!flags && !node_id && !burst_count && !symbol_type) {
eth_msg->t1s = std::nullopt;
return icsneoc2_error_success;
}
if((flags || node_id || burst_count || symbol_type) && !eth_msg->t1s.has_value()) {
eth_msg->t1s.emplace();
}
if(flags) {
eth_msg->t1s->isSymbol = (*flags & ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_SYMBOL);
eth_msg->t1s->isBurst = (*flags & ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_BURST);
eth_msg->t1s->txCollision = (*flags & ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_TX_COLLISION);
eth_msg->t1s->isWake = (*flags & ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_WAKE);
}
if(node_id) {
eth_msg->t1s->nodeId = *node_id;
}
if(burst_count) {
eth_msg->t1s->burstCount = *burst_count;
}
if(symbol_type) {
eth_msg->t1s->symbolType = *symbol_type;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_eth_t1s_props_get(icsneoc2_message_t* message, icsneoc2_message_eth_t1s_flags_t* flags, uint8_t* node_id, uint8_t* burst_count, uint8_t* symbol_type) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
auto eth_msg = std::dynamic_pointer_cast<EthernetMessage>(message->message);
if(!eth_msg) {
return icsneoc2_error_invalid_type;
}
if(flags) {
*flags = 0;
if(eth_msg->t1s.has_value()) {
if(eth_msg->t1s->isSymbol)
*flags |= ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_SYMBOL;
if(eth_msg->t1s->isBurst)
*flags |= ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_BURST;
if(eth_msg->t1s->txCollision)
*flags |= ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_TX_COLLISION;
if(eth_msg->t1s->isWake)
*flags |= ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_WAKE;
}
}
if(node_id) {
*node_id = eth_msg->t1s.has_value() ? eth_msg->t1s->nodeId : 0;
}
if(burst_count) {
*burst_count = eth_msg->t1s.has_value() ? eth_msg->t1s->burstCount : 0;
}
if(symbol_type) {
*symbol_type = eth_msg->t1s.has_value() ? eth_msg->t1s->symbolType : 0;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_is_ethernet(icsneoc2_message_t* message, bool* is_ethernet) {
if(!message || !is_ethernet) {
return icsneoc2_error_invalid_parameters;
}
*is_ethernet = std::dynamic_pointer_cast<EthernetMessage>(message->message) != nullptr;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_is_ethernet_status(icsneoc2_message_t* message, bool* is_ethernet_status) {
if(!message || !is_ethernet_status) {
return icsneoc2_error_invalid_parameters;
}
*is_ethernet_status = std::dynamic_pointer_cast<EthernetStatusMessage>(message->message) != nullptr;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_message_eth_status_props_get(icsneoc2_message_t* message, bool* link_state, bool* duplex, icsneoc2_link_speed_t* link_speed, icsneoc2_link_mode_t* link_mode) {
if(!message) {
return icsneoc2_error_invalid_parameters;
}
auto msg = std::dynamic_pointer_cast<EthernetStatusMessage>(message->message);
if(!msg) {
return icsneoc2_error_invalid_type;
}
if(link_state) {
*link_state = msg->state;
}
if(duplex) {
*duplex = msg->duplex;
}
if(link_speed) {
*link_speed = static_cast<icsneoc2_link_speed_t>(msg->speed);
}
if(link_mode) {
*link_mode = static_cast<icsneoc2_link_mode_t>(msg->mode);
}
return icsneoc2_error_success;
}
+510
View File
@@ -179,6 +179,82 @@ icsneoc2_error_t icsneoc2_settings_termination_set(icsneoc2_device_t* device, ic
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_settings_termination_groups_enumerate(icsneoc2_device_t* device, icsneoc2_termination_group_t** groups, size_t* count) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!groups) {
return icsneoc2_error_invalid_parameters;
}
auto termination_groups = device->device->settings->getTerminationGroups();
icsneoc2_termination_group_t* head = nullptr;
icsneoc2_termination_group_t* tail = nullptr;
for(const auto& group : termination_groups) {
auto* node = new (std::nothrow) icsneoc2_termination_group_t;
if(!node) {
icsneoc2_settings_termination_groups_free(head);
return icsneoc2_error_out_of_memory;
}
node->netids.reserve(group.size());
for(const auto& network : group) {
node->netids.push_back(static_cast<icsneoc2_netid_t>(network.getNetID()));
}
node->next = nullptr;
if(!head) {
head = node;
} else {
tail->next = node;
}
tail = node;
}
*groups = head;
if(count) {
*count = termination_groups.size();
}
return icsneoc2_error_success;
}
icsneoc2_termination_group_t* icsneoc2_termination_group_next(const icsneoc2_termination_group_t* group) {
if(!group) {
return nullptr;
}
return group->next;
}
icsneoc2_error_t icsneoc2_termination_group_networks_get(const icsneoc2_termination_group_t* group, icsneoc2_netid_t* networks, size_t* count) {
if(!group || !count) {
return icsneoc2_error_invalid_parameters;
}
if(!networks) {
*count = group->netids.size();
return icsneoc2_error_success;
}
size_t to_copy = std::min<size_t>(*count, group->netids.size());
for(size_t i = 0; i < to_copy; i++) {
networks[i] = group->netids[i];
}
*count = to_copy;
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_termination_groups_free(icsneoc2_termination_group_t* groups) {
if(!groups) {
return icsneoc2_error_invalid_parameters;
}
while(groups) {
auto* next = groups->next;
delete groups;
groups = next;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_commander_resistor_enabled(icsneoc2_device_t* device, icsneoc2_netid_t netid, bool* enabled) { icsneoc2_error_t icsneoc2_settings_commander_resistor_enabled(icsneoc2_device_t* device, icsneoc2_netid_t netid, bool* enabled) {
// Make sure the device is valid // Make sure the device is valid
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
@@ -600,6 +676,36 @@ icsneoc2_error_t icsneoc2_settings_t1s_tx_opp_timer_set(icsneoc2_device_t* devic
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_settings_t1s_burst_timer_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(auto result = device->device->settings->getT1SBurstTimerFor(network); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
} else {
*value = 0;
return icsneoc2_error_get_settings_failure;
}
}
icsneoc2_error_t icsneoc2_settings_t1s_burst_timer_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(!device->device->settings->setT1SBurstTimerFor(network, value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_t1s_max_burst_timer_for_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t* value) { icsneoc2_error_t icsneoc2_settings_t1s_max_burst_timer_for_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t* value) {
// Make sure the device is valid // Make sure the device is valid
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
@@ -632,6 +738,186 @@ icsneoc2_error_t icsneoc2_settings_t1s_max_burst_timer_for_set(icsneoc2_device_t
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_settings_t1s_local_id_alternate_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(auto result = device->device->settings->getT1SLocalIDAlternateFor(network); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
} else {
*value = 0;
return icsneoc2_error_get_settings_failure;
}
}
icsneoc2_error_t icsneoc2_settings_t1s_local_id_alternate_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(!device->device->settings->setT1SLocalIDAlternateFor(network, value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_t1s_is_termination_enabled_for(icsneoc2_device_t* device, icsneoc2_netid_t netid, bool* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(auto result = device->device->settings->isT1STerminationEnabledFor(network); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
} else {
*value = false;
return icsneoc2_error_get_settings_failure;
}
}
icsneoc2_error_t icsneoc2_settings_t1s_termination_for_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, bool value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(!device->device->settings->setT1STerminationFor(network, value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_t1s_is_bus_decoding_beacons_enabled_for(icsneoc2_device_t* device, icsneoc2_netid_t netid, bool* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(auto result = device->device->settings->isT1SBusDecodingBeaconsEnabledFor(network); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
} else {
*value = false;
return icsneoc2_error_get_settings_failure;
}
}
icsneoc2_error_t icsneoc2_settings_t1s_bus_decoding_beacons_for_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, bool value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(!device->device->settings->setT1SBusDecodingBeaconsFor(network, value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_t1s_is_bus_decoding_all_enabled_for(icsneoc2_device_t* device, icsneoc2_netid_t netid, bool* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(auto result = device->device->settings->isT1SBusDecodingAllEnabledFor(network); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
} else {
*value = false;
return icsneoc2_error_get_settings_failure;
}
}
icsneoc2_error_t icsneoc2_settings_t1s_bus_decoding_all_for_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, bool value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(!device->device->settings->setT1SBusDecodingAllFor(network, value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_t1s_multi_id_enable_mask_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(auto result = device->device->settings->getT1SMultiIDEnableMaskFor(network); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
} else {
*value = 0;
return icsneoc2_error_get_settings_failure;
}
}
icsneoc2_error_t icsneoc2_settings_t1s_multi_id_enable_mask_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(!device->device->settings->setT1SMultiIDEnableMaskFor(network, value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_t1s_multi_id_get(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t index, uint8_t* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(auto result = device->device->settings->getT1SMultiIDFor(network, index); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
} else {
*value = 0;
return icsneoc2_error_get_settings_failure;
}
}
icsneoc2_error_t icsneoc2_settings_t1s_multi_id_set(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t index, uint8_t value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
auto network = Network(static_cast<Network::NetID>(netid));
if(!device->device->settings->setT1SMultiIDFor(network, index, value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_misc_io_analog_output_enabled_set(icsneoc2_device_t* device, uint8_t pin, uint8_t value) { icsneoc2_error_t icsneoc2_settings_misc_io_analog_output_enabled_set(icsneoc2_device_t* device, uint8_t pin, uint8_t value) {
// Make sure the device is valid // Make sure the device is valid
auto res = icsneoc2_device_is_valid(device); auto res = icsneoc2_device_is_valid(device);
@@ -657,6 +943,120 @@ icsneoc2_error_t icsneoc2_settings_misc_io_analog_output_set(icsneoc2_device_t*
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_settings_linux_boot_enabled_get(icsneoc2_device_t* device, bool* value) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
if(auto result = device->device->settings->getLinuxBootEnabled(); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
}
return icsneoc2_error_get_settings_failure;
}
icsneoc2_error_t icsneoc2_settings_linux_boot_enabled_set(icsneoc2_device_t* device, bool value) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->settings->setLinuxBootEnabled(value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_external_wifi_antenna_enabled_get(icsneoc2_device_t* device, bool* value) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
if(auto result = device->device->settings->getExternalWifiAntennaEnabled(); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
}
return icsneoc2_error_get_settings_failure;
}
icsneoc2_error_t icsneoc2_settings_external_wifi_antenna_enabled_set(icsneoc2_device_t* device, bool value) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->settings->setExternalWifiAntennaEnabled(value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_perf_test_enabled_get(icsneoc2_device_t* device, bool* value) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
if(auto result = device->device->settings->isPerfTestEnabled(); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
} else {
*value = false;
return icsneoc2_error_get_settings_failure;
}
}
icsneoc2_error_t icsneoc2_settings_perf_test_enabled_set(icsneoc2_device_t* device, bool value) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->settings->setPerfTestEnable(value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_linux_configuration_port_get(icsneoc2_device_t* device, icsneoc2_linux_configuration_port_t* value) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
if(auto result = device->device->settings->getLinuxConfigurationPort(); result.has_value()) {
*value = static_cast<icsneoc2_linux_configuration_port_t>(result.value());
return icsneoc2_error_success;
}
return icsneoc2_error_get_settings_failure;
}
icsneoc2_error_t icsneoc2_settings_linux_configuration_port_set(icsneoc2_device_t* device, icsneoc2_linux_configuration_port_t value) {
// Make sure the device is valid
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->settings->setLinuxConfigurationPort(static_cast<LinuxConfigurationPort>(value))) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_disabled_get(icsneoc2_device_t* device, bool* value) { icsneoc2_error_t icsneoc2_settings_disabled_get(icsneoc2_device_t* device, bool* value) {
if(!value) { if(!value) {
return icsneoc2_error_invalid_parameters; return icsneoc2_error_invalid_parameters;
@@ -682,3 +1082,113 @@ icsneoc2_error_t icsneoc2_settings_readonly_get(icsneoc2_device_t* device, bool*
*value = device->device->settings->readonly; *value = device->device->settings->readonly;
return icsneoc2_error_success; return icsneoc2_error_success;
} }
icsneoc2_error_t icsneoc2_settings_gptp_profile_get(icsneoc2_device_t* device, icsneoc2_gptp_profile_t* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
if(auto result = device->device->settings->getGPTPProfile(); result.has_value()) {
*value = static_cast<icsneoc2_gptp_profile_t>(result.value());
return icsneoc2_error_success;
}
return icsneoc2_error_get_settings_failure;
}
icsneoc2_error_t icsneoc2_settings_gptp_profile_set(icsneoc2_device_t* device, icsneoc2_gptp_profile_t value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(value >= icsneoc2_gptp_profile_maxsize) {
return icsneoc2_error_invalid_parameters;
}
if(!device->device->settings->setGPTPProfile(static_cast<RADGPTPProfile>(value))) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_gptp_role_get(icsneoc2_device_t* device, icsneoc2_gptp_role_t* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
if(auto result = device->device->settings->getGPTPRole(); result.has_value()) {
*value = static_cast<icsneoc2_gptp_role_t>(result.value());
return icsneoc2_error_success;
}
return icsneoc2_error_get_settings_failure;
}
icsneoc2_error_t icsneoc2_settings_gptp_role_set(icsneoc2_device_t* device, icsneoc2_gptp_role_t value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(value >= icsneoc2_gptp_role_maxsize) {
return icsneoc2_error_invalid_parameters;
}
if(!device->device->settings->setGPTPRole(static_cast<RADGPTPRole>(value))) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_gptp_enabled_port_get(icsneoc2_device_t* device, uint8_t* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
if(auto result = device->device->settings->getGPTPEnabledPort(); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
}
return icsneoc2_error_get_settings_failure;
}
icsneoc2_error_t icsneoc2_settings_gptp_enabled_port_set(icsneoc2_device_t* device, uint8_t value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->settings->setGPTPEnabledPort(value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
icsneoc2_error_t icsneoc2_settings_gptp_clock_syntonization_enabled_get(icsneoc2_device_t* device, bool* value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!value) {
return icsneoc2_error_invalid_parameters;
}
if(auto result = device->device->settings->isGPTPClockSyntonizationEnabled(); result.has_value()) {
*value = result.value();
return icsneoc2_error_success;
}
return icsneoc2_error_get_settings_failure;
}
icsneoc2_error_t icsneoc2_settings_gptp_clock_syntonization_enabled_set(icsneoc2_device_t* device, bool value) {
auto res = icsneoc2_device_is_valid(device);
if(res != icsneoc2_error_success) {
return res;
}
if(!device->device->settings->setGPTPClockSyntonizationEnabled(value)) {
return icsneoc2_error_set_settings_failure;
}
return icsneoc2_error_success;
}
+85 -35
View File
@@ -4,10 +4,10 @@
using namespace icsneo; using namespace icsneo;
APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device) : eventStruct({}) { APIEvent::APIEvent(Type type, APIEvent::Severity severity, std::weak_ptr<Device> device) : eventStruct({}), device(device) {
this->device = device; auto shared = device.lock();
if(device) { if(shared) {
serial = device->getSerial(); serial = shared->getSerial();
eventStruct.serial[serial.copy(eventStruct.serial, sizeof(eventStruct.serial))] = '\0'; eventStruct.serial[serial.copy(eventStruct.serial, sizeof(eventStruct.serial))] = '\0';
} }
@@ -22,37 +22,15 @@ void APIEvent::init(Type event, APIEvent::Severity severity) {
eventStruct.timestamp = EventClock::to_time_t(timepoint); eventStruct.timestamp = EventClock::to_time_t(timepoint);
} }
std::string APIEvent::describe() const noexcept {
std::stringstream ss;
if(device)
ss << *device; // Makes use of device.describe()
else
ss << "API";
Severity severity = getSeverity();
if(severity == Severity::EventInfo) {
ss << " Info: ";
} else if(severity == Severity::EventWarning) {
ss << " Warning: ";
} else if(severity == Severity::Error) {
ss << " Error: ";
} else {
// Should never get here, since Severity::Any should only be used for filtering
ss << " Any: ";
}
ss << getDescription();
return ss.str();
}
void APIEvent::downgradeFromError() noexcept { void APIEvent::downgradeFromError() noexcept {
eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning; eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning;
} }
bool APIEvent::isForDevice(std::string filterSerial) const noexcept { bool APIEvent::isForDevice(std::string filterSerial) const noexcept {
if(!device || filterSerial.length() == 0) auto shared = device.lock();
if(!shared || filterSerial.length() == 0)
return false; return false;
return device->getSerial() == filterSerial; return shared->getSerial() == filterSerial;
} }
// API Errors // API Errors
@@ -86,8 +64,8 @@ static constexpr const char* NO_SERIAL_NUMBER_12V = "Communication could not be
static constexpr const char* NO_SERIAL_NUMBER = "Communication could not be established with the device. Perhaps it is not powered or requires a firmware update using Vehicle Spy."; static constexpr const char* NO_SERIAL_NUMBER = "Communication could not be established with the device. Perhaps it is not powered or requires a firmware update using Vehicle Spy.";
static constexpr const char* INCORRECT_SERIAL_NUMBER = "The device did not return the expected serial number!"; static constexpr const char* INCORRECT_SERIAL_NUMBER = "The device did not return the expected serial number!";
static constexpr const char* SETTINGS_READ = "The device settings could not be read."; static constexpr const char* SETTINGS_READ = "The device settings could not be read.";
static constexpr const char* SETTINGS_VERSION = "The settings version is incorrect, please update your firmware with neoVI Explorer."; static constexpr const char* SETTINGS_VERSION = "The settings version is incorrect, please update your firmware with ICS Device Manager.";
static constexpr const char* SETTINGS_LENGTH = "The settings length is incorrect, please update your firmware with neoVI Explorer."; static constexpr const char* SETTINGS_LENGTH = "The settings length is incorrect, please update your firmware with ICS Device Manager.";
static constexpr const char* SETTINGS_CHECKSUM = "The settings checksum is incorrect, attempting to set defaults may remedy this issue."; static constexpr const char* SETTINGS_CHECKSUM = "The settings checksum is incorrect, attempting to set defaults may remedy this issue.";
static constexpr const char* SETTINGS_NOT_AVAILABLE = "Settings are not available for this device."; static constexpr const char* SETTINGS_NOT_AVAILABLE = "Settings are not available for this device.";
static constexpr const char* SETTINGS_READONLY = "Settings are read-only for this device."; static constexpr const char* SETTINGS_READONLY = "Settings are read-only for this device.";
@@ -118,18 +96,28 @@ static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An idea
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error."; static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition."; static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream."; static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
static constexpr const char* I2C_MESSAGE_EXCEED_MAX_LENGTH = "The I2C message was too long.";
static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same."; static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same.";
static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected."; static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected.";
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device."; static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
static constexpr const char* LIVE_DATA_INVALID_HANDLE = "The live data handle was invalid.";
static constexpr const char* LIVE_DATA_INVALID_COMMAND = "The live data command was invalid.";
static constexpr const char* LIVE_DATA_INVALID_ARGUMENT = "The live data argument was invalid.";
static constexpr const char* LIVE_DATA_VERSION_MISMATCH = "The live data version between libicsneo and firmware are not the same.";
static constexpr const char* LIVE_DATA_NO_DEVICE_RESPONSE = "Expected a response from the device for live data but none were found.";
static constexpr const char* LIVE_DATA_MAX_SIGNALS_REACHED = "The max amound of signals to subscribe to for live data has been reached.";
static constexpr const char* LIVE_DATA_COMMAND_FAILED = "The live data command failed.";
static constexpr const char* LIVE_DATA_ENCODER_ERROR = "Failure to encode live data message.";
static constexpr const char* LIVE_DATA_DECODER_ERROR = "Failure to decode live data message.";
static constexpr const char* LIVE_DATA_NOT_SUPPORTED = "Live data is not supported on this device.";
static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device."; static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device.";
static constexpr const char* MODE_NOT_FOUND = "The mode was not found."; static constexpr const char* MODE_NOT_FOUND = "The mode was not found.";
static constexpr const char* APP_ERROR_PARSING_FAILED = "Failure to decode app error message.";
static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is not supported on this device."; static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is not supported on this device.";
static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device"; static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device";
static constexpr const char* DISK_FORMAT_NOT_SUPPORTED = "Disk formatting is not supported on this device."; static constexpr const char* DISK_FORMAT_NOT_SUPPORTED = "Disk formatting is not supported on this device.";
static constexpr const char* DISK_FORMAT_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count."; static constexpr const char* DISK_FORMAT_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count.";
// Transport Errors // Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed."; static constexpr const char* FAILED_TO_READ = "A read operation failed.";
static constexpr const char* FAILED_TO_WRITE = "A write operation failed."; static constexpr const char* FAILED_TO_WRITE = "A write operation failed.";
@@ -146,6 +134,7 @@ static constexpr const char* FAILED_TO_BIND = "Unable to bind socket.";
static constexpr const char* ERROR_SETTING_SOCKET_OPTION = "A call to setsockopt() failed."; static constexpr const char* ERROR_SETTING_SOCKET_OPTION = "A call to setsockopt() failed.";
static constexpr const char* GETIFADDRS_ERROR = "A call to getifaddrs() failed."; static constexpr const char* GETIFADDRS_ERROR = "A call to getifaddrs() failed.";
static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed."; static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed.";
static constexpr const char* MDIO_MESSAGE_EXCEED_MAX_LENGTH = "The MDIO message was too long.";
// VSA // VSA
static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted."; static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted.";
@@ -178,6 +167,7 @@ static constexpr const char* SERVD_RECV_ERROR = "Error receiving from Servd";
static constexpr const char* SERVD_POLL_ERROR = "Error polling on Servd socket"; static constexpr const char* SERVD_POLL_ERROR = "Error polling on Servd socket";
static constexpr const char* SERVD_NODATA_ERROR = "No data received from Servd"; static constexpr const char* SERVD_NODATA_ERROR = "No data received from Servd";
static constexpr const char* SERVD_JOIN_MULTICAST_ERROR = "Error joining Servd multicast group"; static constexpr const char* SERVD_JOIN_MULTICAST_ERROR = "Error joining Servd multicast group";
static constexpr const char* SERVD_NOT_REACHABLE = "Could not reach Servd; ensure it is installed and running";
// DXX // DXX
static constexpr const char* DXX_ERROR_SYS = "System error, check errno/GetLastError()"; static constexpr const char* DXX_ERROR_SYS = "System error, check errno/GetLastError()";
@@ -186,6 +176,7 @@ static constexpr const char* DXX_ERROR_OVERFLOW = "Overflow in DXX";
static constexpr const char* DXX_ERROR_IO = "I/O failure in DXX"; static constexpr const char* DXX_ERROR_IO = "I/O failure in DXX";
static constexpr const char* DXX_ERROR_ARG = "Invalid arg passed to DXX"; static constexpr const char* DXX_ERROR_ARG = "Invalid arg passed to DXX";
static constexpr const char* NO_ERROR_FOUND = "No errors were found.";
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated."; static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
static constexpr const char* UNKNOWN = "An unknown internal error occurred."; static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
static constexpr const char* INVALID = "An invalid internal error occurred."; static constexpr const char* INVALID = "An invalid internal error occurred.";
@@ -313,6 +304,8 @@ const char* APIEvent::DescriptionForType(Type type) {
return WIVI_STACK_REFRESH_FAILED; return WIVI_STACK_REFRESH_FAILED;
case Type::WiVIUploadStackOverflow: case Type::WiVIUploadStackOverflow:
return WIVI_UPLOAD_STACK_OVERFLOW; return WIVI_UPLOAD_STACK_OVERFLOW;
case Type::I2CMessageExceedsMaxLength:
return I2C_MESSAGE_EXCEED_MAX_LENGTH;
case Type::A2BMessageIncompleteFrame: case Type::A2BMessageIncompleteFrame:
return A2B_MESSAGE_INCOMPLETE_FRAME; return A2B_MESSAGE_INCOMPLETE_FRAME;
case Type::CoreminiUploadVersionMismatch: case Type::CoreminiUploadVersionMismatch:
@@ -321,10 +314,32 @@ const char* APIEvent::DescriptionForType(Type type) {
return DISK_NOT_CONNECTED; return DISK_NOT_CONNECTED;
case Type::UnexpectedResponse: case Type::UnexpectedResponse:
return UNEXPECTED_RESPONSE; return UNEXPECTED_RESPONSE;
case Type::LiveDataInvalidHandle:
return LIVE_DATA_INVALID_HANDLE;
case Type::LiveDataInvalidCommand:
return LIVE_DATA_INVALID_COMMAND;
case Type::LiveDataInvalidArgument:
return LIVE_DATA_INVALID_ARGUMENT;
case Type::LiveDataVersionMismatch:
return LIVE_DATA_VERSION_MISMATCH;
case Type::LiveDataNoDeviceResponse:
return LIVE_DATA_NO_DEVICE_RESPONSE;
case Type::LiveDataMaxSignalsReached:
return LIVE_DATA_MAX_SIGNALS_REACHED;
case Type::LiveDataCommandFailed:
return LIVE_DATA_COMMAND_FAILED;
case Type::LiveDataEncoderError:
return LIVE_DATA_ENCODER_ERROR;
case Type::LiveDataDecoderError:
return LIVE_DATA_DECODER_ERROR;
case Type::LiveDataNotSupported:
return LIVE_DATA_NOT_SUPPORTED;
case Type::LINSettingsNotAvailable: case Type::LINSettingsNotAvailable:
return LIN_SETTINGS_NOT_AVAILABLE; return LIN_SETTINGS_NOT_AVAILABLE;
case Type::ModeNotFound: case Type::ModeNotFound:
return MODE_NOT_FOUND; return MODE_NOT_FOUND;
case Type::AppErrorParsingFailed:
return APP_ERROR_PARSING_FAILED;
case Type::SettingNotAvaiableDevice: case Type::SettingNotAvaiableDevice:
return SETTING_NOT_AVAILABLE; return SETTING_NOT_AVAILABLE;
// Transport Errors // Transport Errors
@@ -358,6 +373,8 @@ const char* APIEvent::DescriptionForType(Type type) {
return GETIFADDRS_ERROR; return GETIFADDRS_ERROR;
case Type::SendToError: case Type::SendToError:
return SEND_TO_ERROR; return SEND_TO_ERROR;
case Type::MDIOMessageExceedsMaxLength:
return MDIO_MESSAGE_EXCEED_MAX_LENGTH;
case Type::GPTPNotSupported: case Type::GPTPNotSupported:
return GPTP_NOT_SUPPORTED; return GPTP_NOT_SUPPORTED;
case Type::DiskFormatNotSupported: case Type::DiskFormatNotSupported:
@@ -423,6 +440,8 @@ const char* APIEvent::DescriptionForType(Type type) {
return SERVD_NODATA_ERROR; return SERVD_NODATA_ERROR;
case Type::ServdJoinMulticastError: case Type::ServdJoinMulticastError:
return SERVD_JOIN_MULTICAST_ERROR; return SERVD_JOIN_MULTICAST_ERROR;
case Type::ServdNotReachable:
return SERVD_NOT_REACHABLE;
// DXX // DXX
case Type::DXXErrorSys: case Type::DXXErrorSys:
@@ -437,13 +456,44 @@ const char* APIEvent::DescriptionForType(Type type) {
return DXX_ERROR_ARG; return DXX_ERROR_ARG;
// Other Errors // Other Errors
case Type::NoErrorFound:
return NO_ERROR_FOUND;
case Type::TooManyEvents: case Type::TooManyEvents:
return TOO_MANY_EVENTS; return TOO_MANY_EVENTS;
case Type::Unknown: case Type::Unknown:
return UNKNOWN; return UNKNOWN;
default: case Type::Any:
return INVALID; break;
} }
return INVALID;
}
std::string APIEvent::describe() const noexcept {
std::stringstream ss;
auto shared = device.lock();
if(shared)
ss << *shared; // Makes use of device.describe()
else
ss << "API";
Severity severity = getSeverity();
if(severity == Severity::EventInfo) {
ss << " Info: ";
} else if(severity == Severity::EventWarning) {
ss << " Warning: ";
} else if(severity == Severity::Error) {
ss << " Error: ";
} else {
// Should never get here, since Severity::Any should only be used for filtering
ss << " Any: ";
}
const char* description = DescriptionForType(getType());
if(description == INVALID)
ss << description << " (0x" << std::hex << eventStruct.eventNumber << ")";
else
ss << description;
return ss.str();
} }
bool EventFilter::match(const APIEvent& event) const noexcept { bool EventFilter::match(const APIEvent& event) const noexcept {
+2 -2
View File
@@ -7,8 +7,8 @@
using namespace icsneo; using namespace icsneo;
EventManager& EventManager::GetInstance() { EventManager& EventManager::GetInstance() {
static EventManager inst; static EventManager* inst = new EventManager();
return inst; return *inst;
} }
void EventManager::downgradeErrorsOnCurrentThread() { void EventManager::downgradeErrorsOnCurrentThread() {
+60
View File
@@ -0,0 +1,60 @@
#include "icsneo/api/heartbeat.h"
#include "icsneo/api/lifetime.h"
#include "icsneo/device/device.h"
using namespace icsneo;
Heartbeat::Heartbeat(Device& device) :
device(device), mode(device.isOnline() ? Mode::Passive : Mode::Active), thread(&Heartbeat::run, this) {
}
Heartbeat::~Heartbeat() {
{
std::lock_guard lk(mutex);
stop = true;
}
cv.notify_one();
thread.join();
}
void Heartbeat::run() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
auto filter = std::make_shared<MessageFilter>();
filter->includeInternalInAny = true;
bool status = false;
auto cbHandle = device.com->addMessageCallback(std::make_shared<MessageCallback>(filter, [&](std::shared_ptr<Message> msg) {
// TODO: remove DeviceStatus after 2027-08-17, as ResetStatus should be widely supported
const bool isHeartbeat =
(msg->type == Message::Type::ResetStatus) ||
(msg->type == Message::Type::RawMessage && std::static_pointer_cast<RawMessage>(msg)->network == Network::NetID::DeviceStatus);
if(!isHeartbeat)
return;
{
std::lock_guard lk(mutex);
status = true;
}
cv.notify_one();
}));
Lifetime cbLifetime([&] {
device.com->removeMessageCallback(cbHandle);
});
while(true) {
std::unique_lock lk(mutex);
if(mode == Mode::Active) {
if(cv.wait_for(lk, std::chrono::milliseconds(100), [&] { return stop; })) {
break;
}
device.com->sendCommand(Command::RequestStatusUpdate);
}
if(!cv.wait_for(lk, std::chrono::seconds(2), [&] { return status || stop; })) {
// we disconnect instead of close because it indicates to the user that a cleanup is still required (our thread join)
device.report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
device.com->driver->setIsDisconnected(true);
break;
}
if(stop)
break;
status = false;
}
}
@@ -5,6 +5,13 @@
#include <windows.h> #include <windows.h>
#include "icsneo/icsnVC40.h" #include "icsneo/icsnVC40.h"
// Vehicle Spy 3.26.3.9 removed these legacy settings structure definitions from
// icsnVC40.h. The corresponding functions only ever treat them as opaque
// buffers, so forward declarations keep the existing API/ABI intact.
typedef struct _SFireSettings SFireSettings;
typedef struct _SVCAN3Settings SVCAN3Settings;
typedef struct _SVCANRFSettings SVCANRFSettings;
bool LoadDLLAPI(HINSTANCE &hAPIDLL); bool LoadDLLAPI(HINSTANCE &hAPIDLL);
+11 -9
View File
@@ -1035,10 +1035,10 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
case NEODEVICE_ION: case NEODEVICE_ION:
*pDeviceSettingsType = DeviceFire2SettingsType; //defaults to FIRE2 vnets with libicsneo - no firevnets! *pDeviceSettingsType = DeviceFire2SettingsType; //defaults to FIRE2 vnets with libicsneo - no firevnets!
break; break;
case NEODEVICE_VCAN3: case NEODEVICE_VCAN3_DEPRECATED:
*pDeviceSettingsType = DeviceVCAN3SettingsType; *pDeviceSettingsType = DeviceVCAN3SettingsTypeDeprecated;
break; break;
case NEODEVICE_FIRE: case NEODEVICE_FIRE_DEPRECATED:
*pDeviceSettingsType = DeviceFireSettingsType; *pDeviceSettingsType = DeviceFireSettingsType;
break; break;
case NEODEVICE_FIRE2: case NEODEVICE_FIRE2:
@@ -1061,17 +1061,17 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
case NEODEVICE_VIVIDCAN: case NEODEVICE_VIVIDCAN:
*pDeviceSettingsType = DeviceVividCANSettingsType; *pDeviceSettingsType = DeviceVividCANSettingsType;
break; break;
case NEODEVICE_ECU_AVB: case NEODEVICE_ECU_AVB_DEPRECATED:
*pDeviceSettingsType = DeviceECU_AVBSettingsType; *pDeviceSettingsType = DeviceECU_AVBSettingsTypeDeprecated;
break; break;
case NEODEVICE_RADSUPERMOON: case NEODEVICE_RADSUPERMOON_DEPRECATED:
*pDeviceSettingsType = DeviceRADSuperMoonSettingsType; *pDeviceSettingsType = DeviceRADSuperMoonSettingsTypeDeprecated;
break; break;
case NEODEVICE_RADMOON2: case NEODEVICE_RADMOON2:
*pDeviceSettingsType = DeviceRADMoon2SettingsType; *pDeviceSettingsType = DeviceRADMoon2SettingsType;
break; break;
case NEODEVICE_RADGIGALOG: case NEODEVICE_RADGIGALOG_DEPRECATED:
*pDeviceSettingsType = DeviceRADGigalogSettingsType; *pDeviceSettingsType = DeviceRADGigalogSettingsTypeDeprecated;
break; break;
case NEODEVICE_RADMOON3: case NEODEVICE_RADMOON3:
*pDeviceSettingsType = DeviceRADMoon3SettingsType; *pDeviceSettingsType = DeviceRADMoon3SettingsType;
@@ -1088,6 +1088,8 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
case NEODEVICE_FIRE3_FLEXRAY: case NEODEVICE_FIRE3_FLEXRAY:
*pDeviceSettingsType = DeviceFire3FlexraySettingsType; *pDeviceSettingsType = DeviceFire3FlexraySettingsType;
break; break;
case NEODEVICE_RAD_BMS:
*pDeviceSettingsType = DeviceRADBMSSettingsType;
default: default:
return 0; return 0;
} }
+2
View File
@@ -31,6 +31,8 @@ pybind11_add_module(icsneopy
icsneopy/communication/message/tc10statusmessage.cpp icsneopy/communication/message/tc10statusmessage.cpp
icsneopy/communication/message/mdiomessage.cpp icsneopy/communication/message/mdiomessage.cpp
icsneopy/communication/message/gptpstatusmessage.cpp icsneopy/communication/message/gptpstatusmessage.cpp
icsneopy/communication/message/allmacaddressesmessage.cpp
icsneopy/communication/message/apperrormessage.cpp
icsneopy/communication/message/ethernetstatusmessage.cpp icsneopy/communication/message/ethernetstatusmessage.cpp
icsneopy/communication/message/spimessage.cpp icsneopy/communication/message/spimessage.cpp
icsneopy/communication/message/scriptstatusmessage.cpp icsneopy/communication/message/scriptstatusmessage.cpp
+1
View File
@@ -126,6 +126,7 @@ void init_event(pybind11::module_& m) {
.value("ServdPollError", APIEvent::Type::ServdPollError) .value("ServdPollError", APIEvent::Type::ServdPollError)
.value("ServdNoDataError", APIEvent::Type::ServdNoDataError) .value("ServdNoDataError", APIEvent::Type::ServdNoDataError)
.value("ServdJoinMulticastError", APIEvent::Type::ServdJoinMulticastError) .value("ServdJoinMulticastError", APIEvent::Type::ServdJoinMulticastError)
.value("ServdNotReachable", APIEvent::Type::ServdNotReachable)
.value("DXXErrorSys", APIEvent::Type::DXXErrorSys) .value("DXXErrorSys", APIEvent::Type::DXXErrorSys)
.value("DXXErrorInt", APIEvent::Type::DXXErrorInt) .value("DXXErrorInt", APIEvent::Type::DXXErrorInt)
.value("DXXErrorOverflow", APIEvent::Type::DXXErrorOverflow) .value("DXXErrorOverflow", APIEvent::Type::DXXErrorOverflow)
@@ -0,0 +1,13 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "icsneo/communication/message/allmacaddressesmessage.h"
namespace icsneo {
void init_allmacaddressesmessage(pybind11::module_& m) {
pybind11::classh<AllMACAddressesMessage, Message>(m, "AllMACAddressesMessage")
.def_readonly("addresses", &AllMACAddressesMessage::addresses);
}
} // namespace icsneo
@@ -0,0 +1,71 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/native_enum.h>
#include "icsneo/communication/message/apperrormessage.h"
namespace icsneo {
void init_errortypes(pybind11::module_& m) {
pybind11::native_enum<AppErrorType>(m, "AppErrorType", "enum.IntEnum")
.value("RxMessagesFull", AppErrorType::AppErrorRxMessagesFull)
.value("TxMessagesFull", AppErrorType::AppErrorTxMessagesFull)
.value("TxReportMessagesFull", AppErrorType::AppErrorTxReportMessagesFull)
.value("BadCommWithDspIC", AppErrorType::AppErrorBadCommWithDspIC)
.value("DriverOverflow", AppErrorType::AppErrorDriverOverflow)
.value("PCBuffOverflow", AppErrorType::AppErrorPCBuffOverflow)
.value("PCChksumError", AppErrorType::AppErrorPCChksumError)
.value("PCMissedByte", AppErrorType::AppErrorPCMissedByte)
.value("PCOverrunError", AppErrorType::AppErrorPCOverrunError)
.value("SettingFailure", AppErrorType::AppErrorSettingFailure)
.value("TooManySelectedNetworks", AppErrorType::AppErrorTooManySelectedNetworks)
.value("NetworkNotEnabled", AppErrorType::AppErrorNetworkNotEnabled)
.value("RtcNotCorrect", AppErrorType::AppErrorRtcNotCorrect)
.value("LoadedDefaultSettings", AppErrorType::AppErrorLoadedDefaultSettings)
.value("FeatureNotUnlocked", AppErrorType::AppErrorFeatureNotUnlocked)
.value("FeatureRtcCmdDropped", AppErrorType::AppErrorFeatureRtcCmdDropped)
.value("TxMessagesFlushed", AppErrorType::AppErrorTxMessagesFlushed)
.value("TxMessagesHalfFull", AppErrorType::AppErrorTxMessagesHalfFull)
.value("NetworkNotValid", AppErrorType::AppErrorNetworkNotValid)
.value("TxInterfaceNotImplemented", AppErrorType::AppErrorTxInterfaceNotImplemented)
.value("TxMessagesCommEnableIsOff", AppErrorType::AppErrorTxMessagesCommEnableIsOff)
.value("RxFilterMatchCountExceeded", AppErrorType::AppErrorRxFilterMatchCountExceeded)
.value("EthPreemptionNotEnabled", AppErrorType::AppErrorEthPreemptionNotEnabled)
.value("TxNotSupportedInMode", AppErrorType::AppErrorTxNotSupportedInMode)
.value("JumboFramesNotSupported", AppErrorType::AppErrorJumboFramesNotSupported)
.value("EthernetIpFragment", AppErrorType::AppErrorEthernetIpFragment)
.value("TxMessagesUnderrun", AppErrorType::AppErrorTxMessagesUnderrun)
.value("DeviceFanFailure", AppErrorType::AppErrorDeviceFanFailure)
.value("DeviceOvertemperature", AppErrorType::AppErrorDeviceOvertemperature)
.value("TxMessageIndexOutOfRange", AppErrorType::AppErrorTxMessageIndexOutOfRange)
.value("UndersizedFrameDropped", AppErrorType::AppErrorUndersizedFrameDropped)
.value("OversizedFrameDropped", AppErrorType::AppErrorOversizedFrameDropped)
.value("WatchdogEvent", AppErrorType::AppErrorWatchdogEvent)
.value("SystemClockFailure", AppErrorType::AppErrorSystemClockFailure)
.value("SystemClockRecovered", AppErrorType::AppErrorSystemClockRecovered)
.value("SystemPeripheralReset", AppErrorType::AppErrorSystemPeripheralReset)
.value("SystemCommunicationFailure", AppErrorType::AppErrorSystemCommunicationFailure)
.value("TxMessagesUnsupportedSourceOrPacketId", AppErrorType::AppErrorTxMessagesUnsupportedSourceOrPacketId)
.value("WbmsManagerConnectFailed", AppErrorType::AppErrorWbmsManagerConnectFailed)
.value("WbmsManagerConnectBadState", AppErrorType::AppErrorWbmsManagerConnectBadState)
.value("WbmsManagerConnectTimeout", AppErrorType::AppErrorWbmsManagerConnectTimeout)
.value("FailedToInitializeLoggerDisk", AppErrorType::AppErrorFailedToInitializeLoggerDisk)
.value("InvalidSetting", AppErrorType::AppErrorInvalidSetting)
.value("SystemFailureRequestedReset", AppErrorType::AppErrorSystemFailureRequestedReset)
.value("PortKeyMistmatch", AppErrorType::AppErrorPortKeyMistmatch)
.value("BusFailure", AppErrorType::AppErrorBusFailure)
.value("TapOverflow", AppErrorType::AppErrorTapOverflow)
.value("EthTxNoLink", AppErrorType::AppErrorEthTxNoLink)
.value("ErrorBufferOverflow", AppErrorType::AppErrorErrorBufferOverflow)
.value("NoError", AppErrorType::AppNoError)
.finalize();
}
void init_apperrormessage(pybind11::module_& m) {
init_errortypes(m);
pybind11::classh<AppErrorMessage, Message>(m, "AppErrorMessage")
.def("get_app_error_type", &AppErrorMessage::getAppErrorType)
.def("get_app_error_string", &AppErrorMessage::getAppErrorString);
}
} // namespace icsneo
@@ -7,32 +7,29 @@
namespace icsneo { namespace icsneo {
void init_ethernetmessage(pybind11::module_& m) { void init_ethernetmessage(pybind11::module_& m) {
pybind11::classh<MACAddress>(m, "MACAddress") pybind11::classh<EthernetMessage::T1S>(m, "EthernetMessageT1S")
.def("to_string", &MACAddress::toString) .def(pybind11::init())
.def("__repr__", &MACAddress::toString); .def_readwrite("isSymbol", &EthernetMessage::T1S::isSymbol)
.def_readwrite("isBurst", &EthernetMessage::T1S::isBurst)
.def_readwrite("txCollision", &EthernetMessage::T1S::txCollision)
.def_readwrite("isWake", &EthernetMessage::T1S::isWake)
.def_readwrite("nodeId", &EthernetMessage::T1S::nodeId)
.def_readwrite("burstCount", &EthernetMessage::T1S::burstCount)
.def_readwrite("symbolType", &EthernetMessage::T1S::symbolType);
pybind11::classh<EthernetMessage, Frame>(m, "EthernetMessage") pybind11::classh<EthernetMessage, Frame>(m, "EthernetMessage")
.def(pybind11::init()) .def(pybind11::init())
.def_readwrite("preemptionEnabled", &EthernetMessage::preemptionEnabled)
.def_readwrite("preemptionFlags", &EthernetMessage::preemptionFlags)
.def_readwrite("fcs", &EthernetMessage::fcs) .def_readwrite("fcs", &EthernetMessage::fcs)
.def_readwrite("frameTooShort", &EthernetMessage::frameTooShort) .def_readwrite("frameTooShort", &EthernetMessage::frameTooShort)
.def_readwrite("noPadding", &EthernetMessage::noPadding) .def_readwrite("noPadding", &EthernetMessage::noPadding)
.def_readwrite("fcsVerified", &EthernetMessage::fcsVerified) .def_readwrite("fcsVerified", &EthernetMessage::fcsVerified)
.def_readwrite("txAborted", &EthernetMessage::txAborted) .def_readwrite("txAborted", &EthernetMessage::txAborted)
.def_readwrite("crcError", &EthernetMessage::crcError) .def_readwrite("crcError", &EthernetMessage::crcError)
.def_readwrite("isT1S", &EthernetMessage::isT1S) .def_readwrite("t1s", &EthernetMessage::t1s)
.def_readwrite("isT1SSymbol", &EthernetMessage::isT1SSymbol) .def_readwrite("preemptionFlags", &EthernetMessage::preemptionFlags)
.def_readwrite("isT1SBurst", &EthernetMessage::isT1SBurst) .def("get_destination_mac", &EthernetMessage::getDestinationMAC)
.def_readwrite("txCollision", &EthernetMessage::txCollision) .def("get_source_mac", &EthernetMessage::getSourceMAC)
.def_readwrite("isT1SWake", &EthernetMessage::isT1SWake)
.def_readwrite("t1sNodeId", &EthernetMessage::t1sNodeId)
.def_readwrite("t1sBurstCount", &EthernetMessage::t1sBurstCount)
.def_readwrite("t1sSymbolType", &EthernetMessage::t1sSymbolType)
.def("get_destination_mac", &EthernetMessage::getDestinationMAC, pybind11::return_value_policy::reference)
.def("get_source_mac", &EthernetMessage::getSourceMAC, pybind11::return_value_policy::reference)
.def("get_ether_type", &EthernetMessage::getEtherType); .def("get_ether_type", &EthernetMessage::getEtherType);
} }
} // namespace icsneo } // namespace icsneo
@@ -7,7 +7,7 @@
namespace icsneo { namespace icsneo {
void init_ethernetstatusmessage(pybind11::module_& m) { void init_ethernetstatusmessage(pybind11::module_& m) {
pybind11::classh<EthernetStatusMessage, Message> ethernetStatusMessage(m, "EthernetStatusMessage"); pybind11::classh<EthernetStatusMessage, RawMessage> ethernetStatusMessage(m, "EthernetStatusMessage");
pybind11::enum_<EthernetStatusMessage::LinkSpeed>(ethernetStatusMessage, "LinkSpeed") pybind11::enum_<EthernetStatusMessage::LinkSpeed>(ethernetStatusMessage, "LinkSpeed")
.value("LinkSpeedAuto", EthernetStatusMessage::LinkSpeed::LinkSpeedAuto) .value("LinkSpeedAuto", EthernetStatusMessage::LinkSpeed::LinkSpeedAuto)
+1 -1
View File
@@ -120,7 +120,7 @@ void init_chipid(pybind11::module_& m) {
.value("VEM_01_8DW_ZCHIP", ChipID::VEM_01_8DW_ZCHIP) .value("VEM_01_8DW_ZCHIP", ChipID::VEM_01_8DW_ZCHIP)
.value("RADGalaxy_FFG_Zynq", ChipID::RADGalaxy_FFG_Zynq) .value("RADGalaxy_FFG_Zynq", ChipID::RADGalaxy_FFG_Zynq)
.value("RADMoon3_MCHIP", ChipID::RADMoon3_MCHIP) .value("RADMoon3_MCHIP", ChipID::RADMoon3_MCHIP)
.value("RADComet_ZYNQ", ChipID::RADComet_ZYNQ) .value("RADComet2_ZYNQ", ChipID::RADComet2_ZYNQ)
.value("VEM_02_FR_ZCHIP", ChipID::VEM_02_FR_ZCHIP) .value("VEM_02_FR_ZCHIP", ChipID::VEM_02_FR_ZCHIP)
.value("RADA2B_REVB_ZCHIP", ChipID::RADA2B_REVB_ZCHIP) .value("RADA2B_REVB_ZCHIP", ChipID::RADA2B_REVB_ZCHIP)
.value("RADGigastar_FFG_ZYNQ", ChipID::RADGigastar_FFG_ZYNQ) .value("RADGigastar_FFG_ZYNQ", ChipID::RADGigastar_FFG_ZYNQ)
+3 -1
View File
@@ -33,7 +33,7 @@ void init_device(pybind11::module_& m) {
.def("get_serial_number", &Device::getSerialNumber) .def("get_serial_number", &Device::getSerialNumber)
.def("get_serial", &Device::getSerial) .def("get_serial", &Device::getSerial)
.def("get_pcb_serial", &Device::getPCBSerial, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("get_pcb_serial", &Device::getPCBSerial, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_mac_address", &Device::getMACAddress, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("get_mac_addresses", &Device::getMACAddresses, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_supported_rx_networks", &Device::getSupportedRXNetworks, pybind11::return_value_policy::reference) .def("get_supported_rx_networks", &Device::getSupportedRXNetworks, pybind11::return_value_policy::reference)
.def("get_supported_tx_networks", &Device::getSupportedTXNetworks, pybind11::return_value_policy::reference) .def("get_supported_tx_networks", &Device::getSupportedTXNetworks, pybind11::return_value_policy::reference)
.def("get_tc10_status", &Device::getTC10Status, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("get_tc10_status", &Device::getTC10Status, pybind11::call_guard<pybind11::gil_scoped_release>())
@@ -52,9 +52,11 @@ void init_device(pybind11::module_& m) {
.def("set_digital_io", pybind11::overload_cast<IO, size_t, bool>(&Device::setDigitalIO), pybind11::arg("type"), pybind11::arg("number"), pybind11::arg("value"), pybind11::call_guard<pybind11::gil_scoped_release>()) .def("set_digital_io", pybind11::overload_cast<IO, size_t, bool>(&Device::setDigitalIO), pybind11::arg("type"), pybind11::arg("number"), pybind11::arg("value"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_polling_message_limit", &Device::setPollingMessageLimit) .def("set_polling_message_limit", &Device::setPollingMessageLimit)
.def("set_rtc", &Device::setRTC, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("set_rtc", &Device::setRTC, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("reboot", &Device::reboot, pybind11::arg("safe") = false, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("start_script", &Device::startScript, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("start_script", &Device::startScript, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("stop_script", &Device::stopScript, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("stop_script", &Device::stopScript, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("supports_tc10", &Device::supportsTC10) .def("supports_tc10", &Device::supportsTC10)
.def("supports_reboot", &Device::supportsReboot)
.def("supports_live_data", &Device::supportsLiveData) .def("supports_live_data", &Device::supportsLiveData)
.def("subscribe_live_data", &Device::subscribeLiveData, pybind11::arg("message"), pybind11::call_guard<pybind11::gil_scoped_release>()) .def("subscribe_live_data", &Device::subscribeLiveData, pybind11::arg("message"), pybind11::call_guard<pybind11::gil_scoped_release>())
.def("unsubscribe_live_data", &Device::unsubscribeLiveData, pybind11::arg("handle"), pybind11::call_guard<pybind11::gil_scoped_release>()) .def("unsubscribe_live_data", &Device::unsubscribeLiveData, pybind11::arg("handle"), pybind11::call_guard<pybind11::gil_scoped_release>())
@@ -36,9 +36,10 @@ void init_devicetype(pybind11::module_& m) {
.value("RADEpsilon", DeviceType::Enum::RADEpsilon) .value("RADEpsilon", DeviceType::Enum::RADEpsilon)
.value("RADEpsilonXL", DeviceType::Enum::RADEpsilonXL) .value("RADEpsilonXL", DeviceType::Enum::RADEpsilonXL)
.value("RADGalaxy2", DeviceType::Enum::RADGalaxy2) .value("RADGalaxy2", DeviceType::Enum::RADGalaxy2)
.value("RADwBMS", DeviceType::Enum::RADwBMS)
.value("RADMoon3", DeviceType::Enum::RADMoon3) .value("RADMoon3", DeviceType::Enum::RADMoon3)
.value("RADGemini", DeviceType::Enum::RADGemini) .value("RADGemini", DeviceType::Enum::RADGemini)
.value("RADComet", DeviceType::Enum::RADComet) .value("RADComet2", DeviceType::Enum::RADComet2)
.value("FIRE3_FlexRay", DeviceType::Enum::FIRE3_FlexRay) .value("FIRE3_FlexRay", DeviceType::Enum::FIRE3_FlexRay)
.value("FIRE3_T1S_LIN", DeviceType::Enum::FIRE3_T1S_LIN) .value("FIRE3_T1S_LIN", DeviceType::Enum::FIRE3_T1S_LIN)
.value("FIRE3_T1S_SENT", DeviceType::Enum::FIRE3_T1S_SENT) .value("FIRE3_T1S_SENT", DeviceType::Enum::FIRE3_T1S_SENT)
@@ -40,6 +40,16 @@ void init_idevicesettings(pybind11::module_& m) {
.value("Normal", LINMode::NORMAL_MODE) .value("Normal", LINMode::NORMAL_MODE)
.value("Fast", LINMode::FAST_MODE); .value("Fast", LINMode::FAST_MODE);
pybind11::enum_<RADGPTPProfile>(settings, "GPTPProfile")
.value("Standard", RADGPTPProfile::RAD_GPTP_PROFILE_STANDARD)
.value("Automotive", RADGPTPProfile::RAD_GPTP_PROFILE_AUTOMOTIVE);
pybind11::enum_<RADGPTPRole>(settings, "GPTPRole")
.value("Disabled", RADGPTPRole::RAD_GPTP_ROLE_DISABLED)
.value("Passive", RADGPTPRole::RAD_GPTP_ROLE_PASSIVE)
.value("Master", RADGPTPRole::RAD_GPTP_ROLE_MASTER)
.value("Slave", RADGPTPRole::RAD_GPTP_ROLE_SLAVE);
pybind11::enum_<MiscIOAnalogVoltage>(settings, "MiscIOAnalogVoltage") pybind11::enum_<MiscIOAnalogVoltage>(settings, "MiscIOAnalogVoltage")
.value("V0", MiscIOAnalogVoltage::V0) .value("V0", MiscIOAnalogVoltage::V0)
.value("V1", MiscIOAnalogVoltage::V1) .value("V1", MiscIOAnalogVoltage::V1)
@@ -48,6 +58,10 @@ void init_idevicesettings(pybind11::module_& m) {
.value("V4", MiscIOAnalogVoltage::V4) .value("V4", MiscIOAnalogVoltage::V4)
.value("V5", MiscIOAnalogVoltage::V5); .value("V5", MiscIOAnalogVoltage::V5);
pybind11::enum_<LinuxConfigurationPort>(settings, "LinuxConfigurationPort")
.value("USB", LinuxConfigurationPort::USB)
.value("ETH01", LinuxConfigurationPort::ETH01);
pybind11::classh<IDeviceSettings>(m, "IDeviceSettings") pybind11::classh<IDeviceSettings>(m, "IDeviceSettings")
.def("apply", &IDeviceSettings::apply, pybind11::arg("temporary") = 0, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("apply", &IDeviceSettings::apply, pybind11::arg("temporary") = 0, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("apply_defaults", &IDeviceSettings::applyDefaults, pybind11::arg("temporary") = 0, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("apply_defaults", &IDeviceSettings::applyDefaults, pybind11::arg("temporary") = 0, pybind11::call_guard<pybind11::gil_scoped_release>())
@@ -119,7 +133,28 @@ void init_idevicesettings(pybind11::module_& m) {
.def("set_misc_io_analog_output_enabled", &IDeviceSettings::setMiscIOAnalogOutputEnabled, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("set_misc_io_analog_output_enabled", &IDeviceSettings::setMiscIOAnalogOutputEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_misc_io_analog_output", &IDeviceSettings::setMiscIOAnalogOutput, pybind11::call_guard<pybind11::gil_scoped_release>()) .def("set_misc_io_analog_output", &IDeviceSettings::setMiscIOAnalogOutput, pybind11::call_guard<pybind11::gil_scoped_release>())
// Performance blast
.def("is_perf_test_enabled", &IDeviceSettings::isPerfTestEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_perf_test_enable", &IDeviceSettings::setPerfTestEnable, pybind11::call_guard<pybind11::gil_scoped_release>())
// gPTP methods
.def("get_gptp_profile", &IDeviceSettings::getGPTPProfile, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_gptp_profile", &IDeviceSettings::setGPTPProfile, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_gptp_role", &IDeviceSettings::getGPTPRole, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_gptp_role", &IDeviceSettings::setGPTPRole, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_gptp_enabled_port", &IDeviceSettings::getGPTPEnabledPort, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_gptp_enabled_port", &IDeviceSettings::setGPTPEnabledPort, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("is_gptp_clock_syntonization_enabled", &IDeviceSettings::isGPTPClockSyntonizationEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_gptp_clock_syntonization_enabled", &IDeviceSettings::setGPTPClockSyntonizationEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
// Linux operating-system settings (Fire3 family devices)
.def("get_linux_boot_enabled", &IDeviceSettings::getLinuxBootEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_linux_boot_enabled", &IDeviceSettings::setLinuxBootEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_external_wifi_antenna_enabled", &IDeviceSettings::getExternalWifiAntennaEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_external_wifi_antenna_enabled", &IDeviceSettings::setExternalWifiAntennaEnabled, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("get_linux_configuration_port", &IDeviceSettings::getLinuxConfigurationPort, pybind11::call_guard<pybind11::gil_scoped_release>())
.def("set_linux_configuration_port", &IDeviceSettings::setLinuxConfigurationPort, pybind11::call_guard<pybind11::gil_scoped_release>())
// Status properties // Status properties
.def_readonly("disabled", &IDeviceSettings::disabled) .def_readonly("disabled", &IDeviceSettings::disabled)
.def_readonly("readonly", &IDeviceSettings::readonly); .def_readonly("readonly", &IDeviceSettings::readonly);
+4
View File
@@ -19,6 +19,8 @@ void init_ethernetmessage(pybind11::module_&);
void init_linmessage(pybind11::module_&); void init_linmessage(pybind11::module_&);
void init_tc10statusmessage(pybind11::module_&); void init_tc10statusmessage(pybind11::module_&);
void init_gptpstatusmessage(pybind11::module_&); void init_gptpstatusmessage(pybind11::module_&);
void init_allmacaddressesmessage(pybind11::module_&);
void init_apperrormessage(pybind11::module_&);
void init_mdiomessage(pybind11::module_&); void init_mdiomessage(pybind11::module_&);
void init_spimessage(pybind11::module_&); void init_spimessage(pybind11::module_&);
void init_ethernetstatusmessage(pybind11::module_&); void init_ethernetstatusmessage(pybind11::module_&);
@@ -59,6 +61,8 @@ PYBIND11_MODULE(icsneopy, m) {
init_linmessage(m); init_linmessage(m);
init_tc10statusmessage(m); init_tc10statusmessage(m);
init_gptpstatusmessage(m); init_gptpstatusmessage(m);
init_allmacaddressesmessage(m);
init_apperrormessage(m);
init_mdiomessage(m); init_mdiomessage(m);
init_ethernetstatusmessage(m); init_ethernetstatusmessage(m);
init_macsecconfig(m); init_macsecconfig(m);
+1 -2
View File
@@ -1,7 +1,6 @@
#!/bin/sh #!/bin/sh
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_EXAMPLES=ON \ cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_EXAMPLES=ON -DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_ENABLE_TCP=OFF || exit 1
-DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_ENABLE_TCP=OFF || exit 1
cmake --build build || exit 1 cmake --build build || exit 1
+1 -2
View File
@@ -3,7 +3,6 @@
mkdir build >nul 2>&1 mkdir build >nul 2>&1
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_UNIT_TESTS=ON ^ cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_COMPILE_WARNING_AS_ERROR=ON -DLIBICSNEO_BUILD_UNIT_TESTS=ON-DLIBICSNEO_ENABLE_TCP=ON || exit /b 1
-DLIBICSNEO_ENABLE_TCP=ON || exit /b 1
cmake --build build || exit /b 1 cmake --build build || exit /b 1
+19
View File
@@ -21,10 +21,14 @@
#include "icsneo/communication/message/hardwareinfo.h" #include "icsneo/communication/message/hardwareinfo.h"
#include "icsneo/communication/message/tc10statusmessage.h" #include "icsneo/communication/message/tc10statusmessage.h"
#include "icsneo/communication/message/gptpstatusmessage.h" #include "icsneo/communication/message/gptpstatusmessage.h"
#include "icsneo/communication/message/allmacaddressesmessage.h"
#include "icsneo/communication/message/apperrormessage.h" #include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/message/ethernetstatusmessage.h" #include "icsneo/communication/message/ethernetstatusmessage.h"
#include "icsneo/communication/message/networkmutexmessage.h" #include "icsneo/communication/message/networkmutexmessage.h"
#include "icsneo/communication/message/clientidmessage.h" #include "icsneo/communication/message/clientidmessage.h"
#include "icsneo/communication/message/spiportkeymessage.h"
#include "icsneo/communication/message/genericapidatamessage.h"
#include "icsneo/communication/message/genericapistatusmessage.h"
#include "icsneo/communication/command.h" #include "icsneo/communication/command.h"
#include "icsneo/device/device.h" #include "icsneo/device/device.h"
#include "icsneo/communication/packet/canpacket.h" #include "icsneo/communication/packet/canpacket.h"
@@ -340,9 +344,21 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
case ExtendedCommand::LiveData: case ExtendedCommand::LiveData:
result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report); result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report);
return true; return true;
case ExtendedCommand::ExecuteSPIPortKeyOperation:
result = SPIPortKeyMessage::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::ReadGenericAPIData:
result = GenericAPIDataMessage::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::ReadGenericAPIStatus:
result = GenericAPIStatusMessage::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GetTC10Status: case ExtendedCommand::GetTC10Status:
result = TC10StatusMessage::DecodeToMessage(packet->data); result = TC10StatusMessage::DecodeToMessage(packet->data);
return true; return true;
case ExtendedCommand::GetAllMACAddresses:
result = AllMACAddressesMessage::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GetGPTPStatus: { case ExtendedCommand::GetGPTPStatus: {
result = GPTPStatus::DecodeToMessage(packet->data, report); result = GPTPStatus::DecodeToMessage(packet->data, report);
return true; return true;
@@ -559,6 +575,9 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
} }
break; break;
} }
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
} }
// For the moment other types of messages will automatically be decoded as raw messages // For the moment other types of messages will automatically be decoded as raw messages
+3 -1
View File
@@ -234,7 +234,9 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
result = packetizer.packetWrap(result, false); result = packetizer.packetWrap(result, false);
return true; return true;
} }
break; default:
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false; // The message was not a properly formed Message
} }
// Early returns may mean we don't reach this far, check the type you're concerned with // Early returns may mean we don't reach this far, check the type you're concerned with
+2
View File
@@ -163,6 +163,8 @@ void A2BMessage::setChannelSample(Direction dir, uint8_t channel, size_t frame,
case PCMType::L24: case PCMType::L24:
sampleToSet = sampleToSet << 8; sampleToSet = sampleToSet << 8;
break; break;
case PCMType::L32:
break;
} }
if(channelSize16) { if(channelSize16) {
@@ -0,0 +1,51 @@
#include "icsneo/communication/message/allmacaddressesmessage.h"
#include "icsneo/communication/command.h"
#include "icsneo/communication/network.h"
#include <cstring>
using namespace icsneo;
#pragma pack(push, 2)
struct ResponseHeader {
ExtendedCommand command;
uint16_t length;
uint16_t count;
};
struct MacAddrEntryPacket {
uint16_t networkId;
uint8_t address[MACAddressLength];
};
#pragma pack(pop)
std::shared_ptr<AllMACAddressesMessage> AllMACAddressesMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(ResponseHeader))
return nullptr;
const auto* hdr = reinterpret_cast<const ResponseHeader*>(bytestream.data());
if(hdr->command != ExtendedCommand::GetAllMACAddresses)
return nullptr;
if(hdr->count > MaxMACAddressCount)
return nullptr;
const size_t required = sizeof(ResponseHeader) + hdr->count * sizeof(MacAddrEntryPacket);
if(bytestream.size() < required)
return nullptr;
auto msg = std::make_shared<AllMACAddressesMessage>();
const auto* entries = reinterpret_cast<const MacAddrEntryPacket*>(bytestream.data() + sizeof(ResponseHeader));
for(uint16_t i = 0; i < hdr->count; ++i) {
// The firmware sends CoreMini network IDs — convert to NetID for consistent use in libicsneo
Network::NetID netId = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(entries[i].networkId));
auto addr = entries[i].address;
MACAddress arrAddr;
std::copy(addr, addr + MACAddressLength, arrAddr.begin());
msg->addresses.emplace(std::make_pair(netId, arrAddr));
}
return msg;
}
@@ -37,7 +37,7 @@ struct Packet {
}; };
#pragma pack(pop) #pragma pack(pop)
std::shared_ptr<Message> EthernetStatusMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) { std::shared_ptr<RawMessage> EthernetStatusMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(Packet)) { if(bytestream.size() < sizeof(Packet)) {
return nullptr; return nullptr;
} }
@@ -76,5 +76,5 @@ std::shared_ptr<Message> EthernetStatusMessage::DecodeToMessage(const std::vecto
break; break;
default: return nullptr; default: return nullptr;
} }
return std::make_shared<EthernetStatusMessage>(packet->network, packet->state, speed, packet->duplex, mode); return std::make_shared<EthernetStatusMessage>(packet->network, packet->state != 0, speed, packet->duplex != 0, mode);
} }
+1 -1
View File
@@ -35,7 +35,7 @@ bool EthPhyMessage::appendPhyMessage(bool writeEnable, bool clause45, uint8_t ph
bool EthPhyMessage::appendPhyMessage(std::shared_ptr<PhyMessage> message) bool EthPhyMessage::appendPhyMessage(std::shared_ptr<PhyMessage> message)
{ {
if(message != nullptr) if(message)
{ {
messages.push_back(message); messages.push_back(message);
return true; return true;
@@ -0,0 +1,28 @@
#include "icsneo/communication/message/genericapidatamessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/communication/command.h"
using namespace icsneo;
std::shared_ptr<GenericAPIDataMessage> GenericAPIDataMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(ExtendedResponseMessage::ResponseHeader))
return nullptr;
const auto* hdr = reinterpret_cast<const ExtendedResponseMessage::ResponseHeader*>(bytestream.data());
if(hdr->command != ExtendedCommand::ReadGenericAPIData)
return nullptr;
const size_t required = sizeof(ExtendedResponseMessage::ResponseHeader) + sizeof(GenericAPIDataHeader);
if(bytestream.size() < required)
return nullptr;
auto msg = std::make_shared<GenericAPIDataMessage>();
const auto* packet = reinterpret_cast<const GenericAPIDataPacket*>(bytestream.data() + sizeof(ExtendedResponseMessage::ResponseHeader));
msg->functionId = packet->header.functionId;
msg->buffer.resize(packet->header.bufferLength);
std::copy(packet->buffer, packet->buffer + packet->header.bufferLength, msg->buffer.data());
return msg;
}
@@ -0,0 +1,29 @@
#include "icsneo/communication/message/genericapistatusmessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/communication/command.h"
using namespace icsneo;
std::shared_ptr<GenericAPIStatusMessage> GenericAPIStatusMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(ExtendedResponseMessage::ResponseHeader))
return nullptr;
const auto* hdr = reinterpret_cast<const ExtendedResponseMessage::ResponseHeader*>(bytestream.data());
if(hdr->command != ExtendedCommand::ReadGenericAPIStatus)
return nullptr;
const size_t required = sizeof(ExtendedResponseMessage::ResponseHeader) + sizeof(GenericAPIStatusResponsePacket);
if(bytestream.size() < required)
return nullptr;
auto msg = std::make_shared<GenericAPIStatusMessage>();
const auto* packet = reinterpret_cast<const GenericAPIStatusResponsePacket*>(bytestream.data() + sizeof(ExtendedResponseMessage::ResponseHeader));
msg->functionId = packet->functionId;
msg->finishedProcessing = static_cast<bool>(packet->finishedProcessing);
msg->functionError = packet->functionError;
msg->callbackError = packet->callbackError;
return msg;
}
+1 -1
View File
@@ -48,7 +48,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
case Network::Type::AutomotiveEthernet: { case Network::Type::AutomotiveEthernet: {
neomessage_eth_t& eth = *(neomessage_eth_t*)&neomsg; neomessage_eth_t& eth = *(neomessage_eth_t*)&neomsg;
auto ethmsg = std::static_pointer_cast<EthernetMessage>(message); auto ethmsg = std::static_pointer_cast<EthernetMessage>(message);
eth.preemptionFlags = ethmsg->preemptionFlags; eth.preemptionFlags = ethmsg->preemptionFlags.value_or(0);
eth.status.incompleteFrame = ethmsg->frameTooShort; eth.status.incompleteFrame = ethmsg->frameTooShort;
// TODO Fill in extra status bits // TODO Fill in extra status bits
//eth.status.xyz = ethmsg->preemptionEnabled; //eth.status.xyz = ethmsg->preemptionEnabled;
@@ -0,0 +1,27 @@
#include "icsneo/communication/message/spiportkeymessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/communication/command.h"
#include "icsneo/communication/network.h"
using namespace icsneo;
std::shared_ptr<SPIPortKeyMessage> SPIPortKeyMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(ExtendedResponseMessage::ResponseHeader))
return nullptr;
const auto* hdr = reinterpret_cast<const ExtendedResponseMessage::ResponseHeader*>(bytestream.data());
if(hdr->command != ExtendedCommand::ExecuteSPIPortKeyOperation)
return nullptr;
const size_t required = sizeof(ExtendedResponseMessage::ResponseHeader) + sizeof(SPIPortKeyPacket);
if(bytestream.size() < required)
return nullptr;
auto msg = std::make_shared<SPIPortKeyMessage>();
const auto* packet = reinterpret_cast<const SPIPortKeyPacket*>(bytestream.data() + sizeof(ExtendedResponseMessage::ResponseHeader));
msg->isKeyLoaded = packet->isKeyLoaded;
return msg;
}
@@ -142,6 +142,8 @@ void MultiChannelCommunication::hidReadTask() {
dispatchMessage(msg); dispatchMessage(msg);
break; break;
} }
default:
break;
} }
if(currentQueue == nullptr) { if(currentQueue == nullptr) {
+22 -15
View File
@@ -1,6 +1,7 @@
#include "icsneo/communication/packet/ethernetpacket.h" #include "icsneo/communication/packet/ethernetpacket.h"
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <optional>
using namespace icsneo; using namespace icsneo;
@@ -24,9 +25,6 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
message.transmitted = packet->eid.TXMSG; message.transmitted = packet->eid.TXMSG;
if(message.transmitted) if(message.transmitted)
message.description = packet->stats; message.description = packet->stats;
message.preemptionEnabled = packet->header.PREEMPTION_ENABLED;
if(message.preemptionEnabled)
message.preemptionFlags = (uint8_t)((rawWords[0] & 0x03F8) >> 4);
message.frameTooShort = packet->header.RUNT_FRAME; message.frameTooShort = packet->header.RUNT_FRAME;
message.noPadding = !packet->header.ENABLE_PADDING; message.noPadding = !packet->header.ENABLE_PADDING;
message.fcsVerified = packet->header.FCS_VERIFIED; message.fcsVerified = packet->header.FCS_VERIFIED;
@@ -39,15 +37,21 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
// Decoder will fix as it has information about the timestampResolution increments // Decoder will fix as it has information about the timestampResolution increments
message.timestamp = packet->timestamp.TS; message.timestamp = packet->timestamp.TS;
// Ethernet Frame Preemption for TSN
if(packet->header.PREEMPTION_ENABLED) {
message.preemptionFlags = static_cast<uint8_t>((rawWords[0] & 0x03F8) >> 4);
}
// Check if this is a T1S packet and populate T1S-specific fields // Check if this is a T1S packet and populate T1S-specific fields
message.isT1S = packet->header.T1S_ETHERNET; if(packet->header.T1S_ETHERNET) {
if(message.isT1S) { auto& t1s = message.t1s.emplace();
message.isT1SSymbol = packet->eid.T1S_SYMBOL;
message.isT1SBurst = packet->eid.T1S_BURST; t1s.isSymbol = packet->eid.T1S_SYMBOL;
message.txCollision = packet->t1s_status.TXCollision; t1s.isBurst = packet->eid.T1S_BURST;
message.isT1SWake = packet->t1s_status.T1SWake; t1s.txCollision = packet->t1s_status.TXCollision;
message.t1sNodeId = packet->t1s_node.T1S_NODE_ID; t1s.isWake = packet->t1s_status.T1SWake;
message.t1sBurstCount = packet->t1s_node.T1S_BURST_COUNT; t1s.nodeId = packet->t1s_node.T1S_NODE_ID;
t1s.burstCount = packet->t1s_node.T1S_BURST_COUNT;
} }
const std::vector<uint8_t>::const_iterator databegin = bytestream.begin() + sizeof(HardwareEthernetPacket); const std::vector<uint8_t>::const_iterator databegin = bytestream.begin() + sizeof(HardwareEthernetPacket);
@@ -72,7 +76,7 @@ bool HardwareEthernetPacket::EncodeFromMessage(const EthernetMessage& message, s
if(description & 0x8000) if(description & 0x8000)
return false; return false;
const bool preempt = message.preemptionEnabled; const bool preempt = message.preemptionFlags.has_value();
// full header including parent // full header including parent
const size_t headerByteCount = preempt ? 15 : 14; const size_t headerByteCount = preempt ? 15 : 14;
// local header for netID, description, and flags // local header for netID, description, and flags
@@ -121,12 +125,15 @@ bool HardwareEthernetPacket::EncodeFromMessage(const EthernetMessage& message, s
uint8_t flags = 0x00; uint8_t flags = 0x00;
if(!message.noPadding) flags |= FLAG_PADDING; if(!message.noPadding) flags |= FLAG_PADDING;
if(message.fcs) flags |= FLAG_FCS; if(message.fcs) flags |= FLAG_FCS;
if(message.preemptionEnabled) flags |= FLAG_PREEMPTION; if(message.preemptionFlags.has_value()) {
flags |= FLAG_PREEMPTION;
}
bytestream.push_back(flags); bytestream.push_back(flags);
if(preempt) if(preempt) {
bytestream.push_back(static_cast<uint8_t>(message.preemptionFlags)); bytestream.push_back(message.preemptionFlags.value());
}
bytestream.insert(bytestream.end(), message.data.begin(), message.data.end()); bytestream.insert(bytestream.end(), message.data.begin(), message.data.end());
+1 -1
View File
@@ -80,7 +80,7 @@ bool Packetizer::input(RingBuffer& bytes) {
* end of the payload. The short packet length, for reference, only encompasses the length of the actual * end of the payload. The short packet length, for reference, only encompasses the length of the actual
* payload, and not the header or checksum. * payload, and not the header or checksum.
*/ */
if(packetLength < 6 || packetLength > 4000) { if(packetLength < 6 || packetLength > std::numeric_limits<uint16_t>::max()) {
bytes.pop_front(); bytes.pop_front();
EventManager::GetInstance().add(APIEvent::Type::FailedToRead, APIEvent::Severity::Error); EventManager::GetInstance().add(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
state = ReadState::SearchForHeader; state = ReadState::SearchForHeader;
+37 -9
View File
@@ -230,6 +230,7 @@ MACsecConfig::MACsecConfig(const DeviceType& deviceType) : type(deviceType) {
case icsneo::DeviceType::Enum::RADMoon2: case icsneo::DeviceType::Enum::RADMoon2:
case icsneo::DeviceType::Enum::RADMoon3: case icsneo::DeviceType::Enum::RADMoon3:
case icsneo::DeviceType::Enum::RADEpsilon: case icsneo::DeviceType::Enum::RADEpsilon:
case icsneo::DeviceType::Enum::RADGigastar2:
maxSecY = 2; maxSecY = 2;
maxRule = 2; maxRule = 2;
maxSa = 4; maxSa = 4;
@@ -285,14 +286,29 @@ int MACsecConfig::addTxSecY(const MACsecTxSecY& secY, uint8_t saIndex) {
} }
int MACsecConfig::addRxSa(const MACsecRxSa& sa) { int MACsecConfig::addRxSa(const MACsecRxSa& sa) {
// Validate AN is in valid range
if(sa.an > 3) {
ReportEvent(APIEvent::Type::MACsecSaLimit, APIEvent::Severity::Error);
return -1;
}
// Check if we've exceeded the maximum SA count
if(rxSa.size() >= maxSa) { if(rxSa.size() >= maxSa) {
ReportEvent(APIEvent::Type::MACsecSaLimit, APIEvent::Severity::Error); ReportEvent(APIEvent::Type::MACsecSaLimit, APIEvent::Severity::Error);
return -1; return -1;
} }
int ret = static_cast<int>(rxSa.size()); // Ensure vector is large enough to hold index sa.an (sparse array)
rxSa.emplace_back(sa); // This allows SA index to match the AN value from the MACsec SecTAG
return ret; if(rxSa.size() <= sa.an) {
rxSa.resize(sa.an + 1);
}
// Mark it enabled; padding slots created by resize() above default to enabled=false.
rxSa[sa.an] = sa;
rxSa[sa.an].enabled = true;
return sa.an;
} }
int MACsecConfig::addTxSa(const MACsecTxSa& sa) { int MACsecConfig::addTxSa(const MACsecTxSa& sa) {
@@ -603,13 +619,23 @@ static void SetHardwareRxSecY(
hwSc->enable = 0x1u; hwSc->enable = 0x1u;
hwSc->secYIndex = index; hwSc->secYIndex = index;
hwSc->enable_auto_rekey = rekeyEnabled ? 0x1u : 0x0u; hwSc->enable_auto_rekey = rekeyEnabled ? 0x1u : 0x0u;
hwSc->sa_index0 = saIndices.first;
hwSc->sa_index1 = saIndices.second;
hwSc->sa_index0_in_use = 0x1u;
hwSc->sa_index1_in_use = rekeyEnabled ? 0x1u : 0x0u;
hwSc->isActiveSA1 = rekeyEnabled ? 0x1u : 0x0u;
hwSc->sci = secY.sci; hwSc->sci = secY.sci;
if(saIndices.first & 0x1u) {
hwSc->sa_index0 = rekeyEnabled ? saIndices.second : saIndices.first;
hwSc->sa_index1 = saIndices.first;
hwSc->sa_index0_in_use = rekeyEnabled ? 0x1u : 0x0u;
hwSc->sa_index1_in_use = 0x1u;
hwSc->isActiveSA1 = rekeyEnabled ? 0x0u : 0x1u;
} else {
hwSc->sa_index0 = saIndices.first;
hwSc->sa_index1 = saIndices.second;
hwSc->sa_index0_in_use = 0x1u;
hwSc->sa_index1_in_use = rekeyEnabled ? 0x1u : 0x0u;
hwSc->isActiveSA1 = rekeyEnabled ? 0x1u : 0x0u;
}
hwMap->index = index; hwMap->index = index;
hwMap->enable = 0x1u; hwMap->enable = 0x1u;
hwMap->secYIndex = index; hwMap->secYIndex = index;
@@ -638,7 +664,9 @@ static void SetHardwareRxSa(MACSEC_SETTINGS_W_HDR* hwSettings, const MACsecRxSa&
MACSecSa_t* hwSa = &hwSettings->macsec.rx.sa[index]; MACSecSa_t* hwSa = &hwSettings->macsec.rx.sa[index];
hwSa->index = index; hwSa->index = index;
hwSa->enable = 0x1u; hwSa->enable = sa.enabled ? 0x1u : 0x0u;
if(!sa.enabled)
return;
memcpy(hwSa->sak, sa.sak.data(), 32); memcpy(hwSa->sak, sa.sak.data(), 32);
memcpy(hwSa->hashKey, sa.hashKey.data(), 16); memcpy(hwSa->hashKey, sa.hashKey.data(), 16);
memcpy(hwSa->salt, sa.salt.data(), 12); memcpy(hwSa->salt, sa.salt.data(), 12);
+113 -140
View File
@@ -92,10 +92,6 @@ Device::~Device() {
disableMessagePolling(); disableMessagePolling();
if(isOpen()) if(isOpen())
close(); close();
if(heartbeatThread.joinable()) {
stopHeartbeatThread = true;
heartbeatThread.join();
}
} }
uint16_t Device::getTimestampResolution() const { uint16_t Device::getTimestampResolution() const {
@@ -288,6 +284,8 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
return false; return false;
} }
startHeartbeat();
APIEvent::Type attemptErr = attemptToBeginCommunication(); APIEvent::Type attemptErr = attemptToBeginCommunication();
if(attemptErr != APIEvent::Type::NoErrorFound) { if(attemptErr != APIEvent::Type::NoErrorFound) {
// We could not communicate with the device, let's see if an extension can // We could not communicate with the device, let's see if an extension can
@@ -334,79 +332,6 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
EventManager::GetInstance().cancelErrorDowngradingOnCurrentThread(); EventManager::GetInstance().cancelErrorDowngradingOnCurrentThread();
} }
MessageFilter filter;
filter.includeInternalInAny = true;
internalHandlerCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>(filter, [this](std::shared_ptr<Message> message) {
handleInternalMessage(message);
}));
// Clear the previous heartbeat thread, in case open() was called on this instance more than once
if(heartbeatThread.joinable())
heartbeatThread.join();
stopHeartbeatThread = false;
heartbeatThread = std::thread([this]() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
MessageFilter filter;
filter.includeInternalInAny = true;
std::condition_variable heartbeatCV;
std::mutex receivedMessageMutex;
bool receivedMessage = false;
auto messageReceivedCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>(filter, [&](std::shared_ptr<Message>) {
{
std::scoped_lock<std::mutex> lk(receivedMessageMutex);
receivedMessage = true;
}
heartbeatCV.notify_all();
}));
// Give the device time to get situated
auto i = 150;
while(!stopHeartbeatThread && i != 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
i--;
}
while(!stopHeartbeatThread) {
std::unique_lock<std::mutex> recvLk(receivedMessageMutex);
// Wait for 110ms for a possible heartbeat
if(heartbeatCV.wait_for(recvLk, std::chrono::milliseconds(110), [&]() { return receivedMessage; })) {
receivedMessage = false;
} else if(!stopHeartbeatThread) { // Add this condition here in case the thread was stopped while waiting for the last message
// Some communication, such as the bootloader and extractor interfaces, must
// redirect the input stream from the device as it will no longer be in the
// packet format we expect here. As a result, status updates will not reach
// us here and suppressDisconnects() must be used. We don't want to request
// a status and then redirect the stream, as we'll then be polluting an
// otherwise quiet stream. This lock makes sure suppressDisconnects() will
// block until we've either gotten our status update or disconnected from
// the device.
std::unique_lock<std::mutex> lk(heartbeatMutex);
if(heartbeatSuppressed()) continue;
// No heartbeat received, request a status
com->sendCommand(Command::RequestStatusUpdate);
// Check if we got a message, and if not, if settings are being applied
if(heartbeatCV.wait_for(recvLk, std::chrono::milliseconds(3500), [&](){ return receivedMessage; })) {
receivedMessage = false;
} else {
if(!stopHeartbeatThread) {
close();
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
break;
}
}
}
com->removeMessageCallback(messageReceivedCallbackID);
});
if(supportsLiveData()) if(supportsLiveData())
clearAllLiveData(); clearAllLiveData();
@@ -503,7 +428,7 @@ bool Device::close() {
return false; return false;
} }
stopHeartbeatThread = true; stopHeartbeat();
if (isMessagePollingEnabled()) { if (isMessagePollingEnabled()) {
disableMessagePolling(); disableMessagePolling();
@@ -534,33 +459,17 @@ bool Device::goOnline() {
if(!enableNetworkCommunication(true, onlineTimeoutMs)) if(!enableNetworkCommunication(true, onlineTimeoutMs))
return false; return false;
auto startTime = std::chrono::system_clock::now();
ledState = LEDState::Online; ledState = LEDState::Online;
updateLEDState(); updateLEDState();
std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::Reset_Status); // (re)start the keeponline
filter->includeInternalInAny = true; keeponline = std::make_unique<Periodic>([this] {
static std::vector<uint8_t> timeoutBytes = std::vector<uint8_t>((uint8_t*)&onlineTimeoutMs, (uint8_t*)&onlineTimeoutMs + sizeof(onlineTimeoutMs));
return com->sendCommand(Command::KeepAlive, timeoutBytes);
}, std::chrono::milliseconds(onlineTimeoutMs / 4));
// Wait until communication is enabled or 5 seconds, whichever comes first if(supportsNetworkMutex) {
while((std::chrono::system_clock::now() - startTime) < std::chrono::seconds(5)) {
if(latestResetStatus && latestResetStatus->comEnabled)
break;
bool failOut = false;
com->waitForMessageSync([this, &failOut]() {
if(!com->sendCommand(Command::RequestStatusUpdate)) {
failOut = true;
return false;
}
return true;
}, filter, std::chrono::milliseconds(100));
if(failOut)
return false;
}
if(supportsNetworkMutex()) {
assignedClientId = com->getClientIDSync(); assignedClientId = com->getClientIDSync();
if(assignedClientId) { if(assignedClientId) {
std::set<Network::NetID> nets; std::set<Network::NetID> nets;
@@ -575,41 +484,46 @@ bool Device::goOnline() {
case NetworkMutexEvent::Acquired: case NetworkMutexEvent::Acquired:
lockedNetworks.emplace(*netMutexMsg->networks.begin()); lockedNetworks.emplace(*netMutexMsg->networks.begin());
break; break;
case NetworkMutexEvent::Expired:
case NetworkMutexEvent::Preempted:
case NetworkMutexEvent::Released: { case NetworkMutexEvent::Released: {
auto it = lockedNetworks.find(*netMutexMsg->networks.begin()); auto it = lockedNetworks.find(*netMutexMsg->networks.begin());
if (it != lockedNetworks.end()) if (it != lockedNetworks.end())
lockedNetworks.erase(it); lockedNetworks.erase(it);
break; break;
} }
case NetworkMutexEvent::Queued:
break;
} }
} }
}); });
} }
} }
// (re)start the keeponline
keeponline = std::make_unique<Periodic>([this] {
static std::vector<uint8_t> timeoutBytes = std::vector<uint8_t>((uint8_t*)&onlineTimeoutMs, (uint8_t*)&onlineTimeoutMs + sizeof(onlineTimeoutMs));
return com->sendCommand(Command::KeepAlive, timeoutBytes);
}, std::chrono::milliseconds(onlineTimeoutMs / 4));
online = true; online = true;
// restart the heartbeat in online mode
restartHeartbeat();
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOnline(); return true; }); forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOnline(); return true; });
return true; return true;
} }
bool Device::goOffline() { bool Device::goOffline() {
online = false;
keeponline.reset(); keeponline.reset();
// restart the heartbeat in offline mode
restartHeartbeat();
if(networkMutexCallbackHandle) if(networkMutexCallbackHandle)
removeMessageCallback(*networkMutexCallbackHandle); removeMessageCallback(*networkMutexCallbackHandle);
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOffline(); return true; }); forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOffline(); return true; });
if(isDisconnected()) { if(isDisconnected()) {
online = false;
return true; return true;
} }
@@ -625,8 +539,6 @@ bool Device::goOffline() {
updateLEDState(); updateLEDState();
online = false;
return true; return true;
} }
@@ -737,15 +649,17 @@ bool Device::uploadCoremini(std::istream& stream, Disk::MemoryType memType) {
return false; return false;
} }
auto connected = isLogicalDiskConnected(); if (memType == Disk::MemoryType::SD) {
auto connected = isLogicalDiskConnected();
if(!connected) {
return false; // Already added an API error if(!connected) {
} return false; // Already added an API error
}
if(!(*connected)) {
report(APIEvent::Type::DiskNotConnected, APIEvent::Severity::Error); if(!(*connected)) {
return false; report(APIEvent::Type::DiskNotConnected, APIEvent::Severity::Error);
return false;
}
} }
if(!stopScript()) { if(!stopScript()) {
@@ -756,7 +670,6 @@ bool Device::uploadCoremini(std::istream& stream, Disk::MemoryType memType) {
return false; return false;
} }
if(!eraseScriptMemory(memType, static_cast<uint64_t>(bin.size()))) { if(!eraseScriptMemory(memType, static_cast<uint64_t>(bin.size()))) {
return false; return false;
} }
@@ -846,10 +759,12 @@ std::optional<CoreminiHeader> Device::readCoreminiHeader(Disk::MemoryType memTyp
return std::nullopt; return std::nullopt;
} }
auto connected = isLogicalDiskConnected(); if (memType == Disk::MemoryType::SD) {
auto connected = isLogicalDiskConnected();
if(!connected) {
return std::nullopt; // Already added an API error if(!connected) {
return std::nullopt; // Already added an API error
}
} }
#pragma pack(push, 2) #pragma pack(push, 2)
@@ -1985,11 +1900,9 @@ void Device::stopScriptStatusThreadIfNecessary(std::unique_lock<std::mutex> lk)
} }
Lifetime Device::suppressDisconnects() { Lifetime Device::suppressDisconnects() {
std::lock_guard<std::mutex> lk(heartbeatMutex); stopHeartbeat();
heartbeatSuppressedByUser++;
return Lifetime([this] { return Lifetime([this] {
std::lock_guard<std::mutex> lk2(heartbeatMutex); startHeartbeat();
heartbeatSuppressedByUser--;
}); });
} }
@@ -2125,6 +2038,18 @@ std::optional<EthPhyMessage> Device::sendEthPhyMsg(const EthPhyMessage& message,
return std::make_optional<EthPhyMessage>(*retMsg); return std::make_optional<EthPhyMessage>(*retMsg);
} }
bool Device::sendSPIPortKeyOperation(uint8_t portIndex, SPIPortKeyMessage::Operation op, std::array<uint8_t, 16> key) {
std::vector<uint8_t> args(sizeof(SPIPortKeyMessage::SPIPortKeyPacket));
auto& params = *reinterpret_cast<SPIPortKeyMessage::SPIPortKeyPacket*>(args.data());
params.op = op;
params.portIndex = portIndex;
std::copy(key.begin(), key.end(), params.key);
if(!com->sendCommand(ExtendedCommand::ExecuteSPIPortKeyOperation, args)) {
return false;
}
return true;
}
std::optional<bool> Device::SetRootDirectoryEntryFlags(uint8_t mask, uint8_t values, uint32_t collectionEntryByteAddress) std::optional<bool> Device::SetRootDirectoryEntryFlags(uint8_t mask, uint8_t values, uint32_t collectionEntryByteAddress)
{ {
if(!supportsWiVI()) if(!supportsWiVI())
@@ -2263,13 +2188,34 @@ std::optional<std::vector<uint8_t>> Device::getPCBSerial() {
return std::vector<uint8_t>(serialMsg->pcbSerial, serialMsg->pcbSerial + sizeof(serialMsg->pcbSerial)); return std::vector<uint8_t>(serialMsg->pcbSerial, serialMsg->pcbSerial + sizeof(serialMsg->pcbSerial));
} }
std::optional<std::vector<uint8_t>> Device::getMACAddress() { std::unordered_map<Network::NetID, MACAddress> Device::getMACAddresses() {
auto serialMsg = com->getSerialNumberSync(); if(supportsGetAllMACAddresses()) {
if(!serialMsg || !serialMsg->hasMacAddress) { if(!isOpen()) {
return std::nullopt; report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return {};
}
auto msg = com->waitForMessageSync(
[this](){ return com->sendCommand(ExtendedCommand::GetAllMACAddresses, {}); },
std::make_shared<MessageFilter>(Message::Type::AllMACAddresses),
std::chrono::milliseconds(100)
);
if(msg) {
const auto typed = std::dynamic_pointer_cast<AllMACAddressesMessage>(msg);
if(typed)
return typed->addresses;
}
} }
return std::vector<uint8_t>(serialMsg->macAddress, serialMsg->macAddress + sizeof(serialMsg->macAddress)); auto serialMsg = com->getSerialNumberSync();
if(!serialMsg || !serialMsg->hasMacAddress)
return {};
std::unordered_map<Network::NetID, MACAddress> addresses;
auto addr = serialMsg->macAddress;
MACAddress arrAddr;
std::copy(addr, addr + MACAddressLength, arrAddr.begin());
addresses.emplace(std::make_pair(Network::NetID::ETHERNET_01, arrAddr));
return addresses;
} }
std::optional<std::set<SupportedFeature>> Device::getSupportedFeatures() { std::optional<std::set<SupportedFeature>> Device::getSupportedFeatures() {
@@ -3273,11 +3219,12 @@ bool Device::findVSAOffsetFromTimepoint(ICSClock::time_point point, uint64_t& vs
std::shared_ptr<VSA> midRecord; std::shared_ptr<VSA> midRecord;
auto midRecordStatus = parser.getRecordFromBytes(buffer.data(), Disk::SectorSize, midRecord); auto midRecordStatus = parser.getRecordFromBytes(buffer.data(), Disk::SectorSize, midRecord);
switch(midRecordStatus) { switch(midRecordStatus) {
case VSAParser::RecordParseStatus::NotARecordStart: case VSAParser::RecordParseStatus::NotARecordStart: {
// This part of the buffer does not contain records // This part of the buffer does not contain records
rightIndex = midIndex - 1; rightIndex = midIndex - 1;
continue; continue;
case VSAParser::RecordParseStatus::ConsecutiveExtended: }
case VSAParser::RecordParseStatus::ConsecutiveExtended: {
// We dropped in the middle of an extended message record // We dropped in the middle of an extended message record
auto extendedRecord = std::dynamic_pointer_cast<VSAExtendedMessage>(midRecord); auto extendedRecord = std::dynamic_pointer_cast<VSAExtendedMessage>(midRecord);
uint64_t pos = readPos; uint64_t pos = readPos;
@@ -3292,6 +3239,9 @@ bool Device::findVSAOffsetFromTimepoint(ICSClock::time_point point, uint64_t& vs
midIndex = (pos - firstOffset) / Disk::SectorSize; midIndex = (pos - firstOffset) / Disk::SectorSize;
midRecord = extendedRecord; midRecord = extendedRecord;
break; break;
}
default:
break;
} }
if(midIndex <= leftIndex) { if(midIndex <= leftIndex) {
// Extended records cause problems with binary search // Extended records cause problems with binary search
@@ -3714,6 +3664,15 @@ bool Device::requestTC10Sleep(Network::NetID network) {
return typed->response == ExtendedResponse::OK; return typed->response == ExtendedResponse::OK;
} }
bool Device::reboot(bool safe) {
if(!supportsReboot()) {
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
return false;
}
// The device reboots in response to this command, so no reply is expected.
return com->sendCommand(ExtendedCommand::Reboot, { uint8_t(safe ? 1 : 0) });
}
std::optional<TC10StatusMessage> Device::getTC10Status(Network::NetID network) { std::optional<TC10StatusMessage> Device::getTC10Status(Network::NetID network) {
if(!supportsTC10()) { if(!supportsTC10()) {
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error); report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
@@ -3835,7 +3794,7 @@ bool Device::formatDisk(const DiskDetails& config, const DiskFormatProgress& han
return com->sendCommand(ExtendedCommand::DiskFormatProgress, {}); return com->sendCommand(ExtendedCommand::DiskFormatProgress, {});
}, },
std::make_shared<ExtendedResponseFilter>(ExtendedCommand::DiskFormatProgress), std::make_shared<ExtendedResponseFilter>(ExtendedCommand::DiskFormatProgress),
std::chrono::milliseconds(1000) std::chrono::milliseconds(5000)
); );
if(!response) { if(!response) {
@@ -3927,7 +3886,7 @@ std::shared_ptr<DiskDetails> Device::getDiskDetails(std::chrono::milliseconds ti
[[nodiscard]] std::optional<int> Device::lockNetworks(const std::set<Network::NetID>& networks, uint32_t priority, uint32_t ttlMs, NetworkMutexType type, std::function<void(std::shared_ptr<Message>)>&& on_event) [[nodiscard]] std::optional<int> Device::lockNetworks(const std::set<Network::NetID>& networks, uint32_t priority, uint32_t ttlMs, NetworkMutexType type, std::function<void(std::shared_ptr<Message>)>&& on_event)
{ {
if(!supportsNetworkMutex()) { if(!supportsNetworkMutex) {
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error); report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
return std::nullopt; return std::nullopt;
} }
@@ -3977,7 +3936,7 @@ std::shared_ptr<DiskDetails> Device::getDiskDetails(std::chrono::milliseconds ti
bool Device::unlockNetworks(const std::set<Network::NetID>& networks) bool Device::unlockNetworks(const std::set<Network::NetID>& networks)
{ {
if(!supportsNetworkMutex()) { if(!supportsNetworkMutex) {
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error); report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
return false; return false;
} }
@@ -4017,7 +3976,7 @@ bool Device::unlockNetworks(const std::set<Network::NetID>& networks)
std::shared_ptr<NetworkMutexMessage> Device::getNetworkMutexStatus(Network::NetID network) std::shared_ptr<NetworkMutexMessage> Device::getNetworkMutexStatus(Network::NetID network)
{ {
if(!supportsNetworkMutex()) { if(!supportsNetworkMutex) {
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error); report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
return nullptr; return nullptr;
} }
@@ -4039,7 +3998,7 @@ std::shared_ptr<NetworkMutexMessage> Device::getNetworkMutexStatus(Network::NetI
[[nodiscard]] std::optional<int> Device::lockAllNetworks(uint32_t priority, uint32_t ttlMs, NetworkMutexType type, std::function<void(std::shared_ptr<Message>)>&& on_event) [[nodiscard]] std::optional<int> Device::lockAllNetworks(uint32_t priority, uint32_t ttlMs, NetworkMutexType type, std::function<void(std::shared_ptr<Message>)>&& on_event)
{ {
if(!supportsNetworkMutex()) { if(!supportsNetworkMutex) {
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error); report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
return std::nullopt; return std::nullopt;
} }
@@ -4086,7 +4045,7 @@ std::shared_ptr<NetworkMutexMessage> Device::getNetworkMutexStatus(Network::NetI
bool Device::unlockAllNetworks() bool Device::unlockAllNetworks()
{ {
if(!supportsNetworkMutex()) { if(!supportsNetworkMutex) {
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error); report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
return false; return false;
} }
@@ -4123,3 +4082,17 @@ bool Device::unlockAllNetworks()
return true; return true;
} }
void Device::startHeartbeat() {
if(!heartbeat)
heartbeat = std::make_unique<Heartbeat>(*this);
}
void Device::stopHeartbeat() {
heartbeat.reset();
}
void Device::restartHeartbeat() {
stopHeartbeat();
startHeartbeat();
}
+10 -6
View File
@@ -165,10 +165,6 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<RADA2B>(dev, newFoundDevices); makeIfSerialMatches<RADA2B>(dev, newFoundDevices);
#endif #endif
#ifdef __RADCOMET_H_
makeIfSerialRangeMatches<RADComet>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET2_H_ #ifdef __RADCOMET2_H_
makeIfSerialRangeMatches<RADComet2>(dev, newFoundDevices); makeIfSerialRangeMatches<RADComet2>(dev, newFoundDevices);
#endif #endif
@@ -245,6 +241,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<RADSupermoon>(dev, newFoundDevices); makeIfSerialMatches<RADSupermoon>(dev, newFoundDevices);
#endif #endif
#ifdef __RADWBMS_H_
makeIfSerialMatches<RADwBMS>(dev, newFoundDevices);
#endif
#ifdef __VALUECAN3_H_ #ifdef __VALUECAN3_H_
makeIfSerialRangeMatches<ValueCAN3>(dev, newFoundDevices); makeIfSerialRangeMatches<ValueCAN3>(dev, newFoundDevices);
#endif #endif
@@ -340,8 +340,8 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
RADA2B::DEVICE_TYPE, RADA2B::DEVICE_TYPE,
#endif #endif
#ifdef __RADCOMET_H_ #ifdef __RADCOMET2_H_
RADComet::DEVICE_TYPE, RADComet2::DEVICE_TYPE,
#endif #endif
#ifdef __RADCOMET3_H_ #ifdef __RADCOMET3_H_
@@ -408,6 +408,10 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
RADSupermoon::DEVICE_TYPE, RADSupermoon::DEVICE_TYPE,
#endif #endif
#ifdef __RADWBMS_H_
RADwBMS::DEVICE_TYPE,
#endif
#ifdef __VALUECAN3_H_ #ifdef __VALUECAN3_H_
ValueCAN3::DEVICE_TYPE, ValueCAN3::DEVICE_TYPE,
#endif #endif
+175 -22
View File
@@ -1,9 +1,16 @@
#include "icsneo/device/device.h"
#include "icsneo/device/idevicesettings.h" #include "icsneo/device/idevicesettings.h"
#include "icsneo/communication/message/filter/main51messagefilter.h" #include "icsneo/communication/message/filter/main51messagefilter.h"
#include <cstring> #include <cstring>
using namespace icsneo; using namespace icsneo;
IDeviceSettings::IDeviceSettings(Device* device, size_t size)
: device(device), report(device->report), structSize(size) {}
IDeviceSettings::IDeviceSettings(warn_t createInoperableSettings, Device* device)
: disabled(true), readonly(true), report(device->report), structSize(0) { (void)createInoperableSettings; }
std::optional<uint16_t> IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settings) { std::optional<uint16_t> IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settings) {
const uint16_t* p = reinterpret_cast<const uint16_t*>(settings.data()); const uint16_t* p = reinterpret_cast<const uint16_t*>(settings.data());
size_t words = settings.size(); size_t words = settings.size();
@@ -166,7 +173,7 @@ bool IDeviceSettings::refresh() {
} }
std::vector<uint8_t> rxSettings; std::vector<uint8_t> rxSettings;
bool ret = com->getSettingsSync(rxSettings); bool ret = device->com->getSettingsSync(rxSettings);
if(!ret) { if(!ret) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error); report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false; return false;
@@ -231,11 +238,11 @@ bool IDeviceSettings::apply(bool temporary) {
memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size()); memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size());
// Pause I/O with the device while the settings are applied // Pause I/O with the device while the settings are applied
applyingSettings = true; device->stopHeartbeat();
std::shared_ptr<Main51Message> msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this, &bytestream]() { std::shared_ptr<Main51Message> msg = std::dynamic_pointer_cast<Main51Message>(device->com->waitForMessageSync([this, &bytestream]() {
return com->sendCommand(Command::SetSettings, bytestream); return device->com->sendCommand(Command::SetSettings, bytestream);
}, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(2000))); }, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(5000)));
if(!msg || msg->data[0] != 1) { // We did not receive a response if(!msg || msg->data[0] != 1) { // We did not receive a response
// Attempt to get the settings from the device so we're up to date if possible // Attempt to get the settings from the device so we're up to date if possible
@@ -259,9 +266,9 @@ bool IDeviceSettings::apply(bool temporary) {
bytestream[6] = (uint8_t)(*gsChecksum >> 8); bytestream[6] = (uint8_t)(*gsChecksum >> 8);
memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size()); memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size());
msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this, &bytestream]() { msg = std::dynamic_pointer_cast<Main51Message>(device->com->waitForMessageSync([this, &bytestream]() {
return com->sendCommand(Command::SetSettings, bytestream); return device->com->sendCommand(Command::SetSettings, bytestream);
}, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(2000))); }, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(5000)));
if(!msg || msg->data[0] != 1) { if(!msg || msg->data[0] != 1) {
// Attempt to get the settings from the device so we're up to date if possible // Attempt to get the settings from the device so we're up to date if possible
if(refresh()) { if(refresh()) {
@@ -272,12 +279,12 @@ bool IDeviceSettings::apply(bool temporary) {
} }
if(!temporary) { if(!temporary) {
msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this]() { msg = std::dynamic_pointer_cast<Main51Message>(device->com->waitForMessageSync([this]() {
return com->sendCommand(Command::SaveSettings); return device->com->sendCommand(Command::SaveSettings);
}, std::make_shared<Main51MessageFilter>(Command::SaveSettings), std::chrono::milliseconds(5000))); }, std::make_shared<Main51MessageFilter>(Command::SaveSettings), std::chrono::milliseconds(5000)));
} }
applyingSettings = false; device->startHeartbeat();
refresh(); // Refresh our buffer with what the device has, whether we were successful or not refresh(); // Refresh our buffer with what the device has, whether we were successful or not
@@ -299,11 +306,11 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
return false; return false;
} }
applyingSettings = true; device->stopHeartbeat();
std::shared_ptr<Main51Message> msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this]() { std::shared_ptr<Main51Message> msg = std::dynamic_pointer_cast<Main51Message>(device->com->waitForMessageSync([this]() {
return com->sendCommand(Command::SetDefaultSettings); return device->com->sendCommand(Command::SetDefaultSettings);
}, std::make_shared<Main51MessageFilter>(Command::SetDefaultSettings), std::chrono::milliseconds(1000))); }, std::make_shared<Main51MessageFilter>(Command::SetDefaultSettings), std::chrono::milliseconds(5000)));
if(!msg || msg->data[0] != 1) { if(!msg || msg->data[0] != 1) {
// Attempt to get the settings from the device so we're up to date if possible // Attempt to get the settings from the device so we're up to date if possible
if(refresh()) { if(refresh()) {
@@ -336,9 +343,9 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
bytestream[6] = (uint8_t)(*gsChecksum >> 8); bytestream[6] = (uint8_t)(*gsChecksum >> 8);
memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size()); memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size());
msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this, &bytestream]() { msg = std::dynamic_pointer_cast<Main51Message>(device->com->waitForMessageSync([this, &bytestream]() {
return com->sendCommand(Command::SetSettings, bytestream); return device->com->sendCommand(Command::SetSettings, bytestream);
}, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(2000))); }, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(5000)));
if(!msg || msg->data[0] != 1) { if(!msg || msg->data[0] != 1) {
// Attempt to get the settings from the device so we're up to date if possible // Attempt to get the settings from the device so we're up to date if possible
if(refresh()) { if(refresh()) {
@@ -349,12 +356,12 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
} }
if(!temporary) { if(!temporary) {
msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this]() { msg = std::dynamic_pointer_cast<Main51Message>(device->com->waitForMessageSync([this]() {
return com->sendCommand(Command::SaveSettings); return device->com->sendCommand(Command::SaveSettings);
}, std::make_shared<Main51MessageFilter>(Command::SaveSettings), std::chrono::milliseconds(5000))); }, std::make_shared<Main51MessageFilter>(Command::SaveSettings), std::chrono::milliseconds(5000)));
} }
applyingSettings = false; device->startHeartbeat();
refresh(); // Refresh our buffer with what the device has, whether we were successful or not refresh(); // Refresh our buffer with what the device has, whether we were successful or not
@@ -959,4 +966,150 @@ bool IDeviceSettings::setMiscIOAnalogOutput(uint8_t pin, MiscIOAnalogVoltage vol
(void)voltage; (void)voltage;
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error); report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error);
return false; return false;
} }
std::optional<RADGPTPProfile> IDeviceSettings::getGPTPProfile() const {
const auto* gptp = getGPTPSettings();
if(!gptp) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
return static_cast<RADGPTPProfile>(gptp->profile);
}
bool IDeviceSettings::setGPTPProfile(RADGPTPProfile profile) {
auto* gptp = getMutableGPTPSettings();
if(!gptp) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
gptp->profile = static_cast<uint8_t>(profile);
return true;
}
std::optional<RADGPTPRole> IDeviceSettings::getGPTPRole() const {
const auto* gptp = getGPTPSettings();
if(!gptp) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
return static_cast<RADGPTPRole>(gptp->gptpPortRole);
}
bool IDeviceSettings::setGPTPRole(RADGPTPRole role) {
auto* gptp = getMutableGPTPSettings();
if(!gptp) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
gptp->gptpPortRole = static_cast<uint8_t>(role);
return true;
}
std::optional<uint8_t> IDeviceSettings::getGPTPEnabledPort() const {
const auto* gptp = getGPTPSettings();
if(!gptp) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
return gptp->gptpEnabledPort;
}
bool IDeviceSettings::setGPTPEnabledPort(uint8_t port) {
auto* gptp = getMutableGPTPSettings();
if(!gptp) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
gptp->gptpEnabledPort = port;
return true;
}
std::optional<bool> IDeviceSettings::isGPTPClockSyntonizationEnabled() const {
const auto* gptp = getGPTPSettings();
if(!gptp) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
return gptp->enableClockSyntonization != 0;
}
bool IDeviceSettings::setGPTPClockSyntonizationEnabled(bool enable) {
auto* gptp = getMutableGPTPSettings();
if(!gptp) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return false;
}
gptp->enableClockSyntonization = enable ? 1 : 0;
return true;
}
std::optional<bool> IDeviceSettings::getLinuxBootEnabled() {
const auto* os = getLinuxSettings();
if(os == nullptr) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
return os->allowBoot != 0;
}
bool IDeviceSettings::setLinuxBootEnabled(bool enabled) {
auto os = getMutableLinuxSettings();
if(!os) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error);
return false;
}
(*os)->allowBoot = enabled ? 1 : 0;
return true;
}
std::optional<bool> IDeviceSettings::getExternalWifiAntennaEnabled() {
const auto* os = getLinuxSettings();
if(os == nullptr) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
return os->useExternalWifiAntenna != 0;
}
bool IDeviceSettings::setExternalWifiAntennaEnabled(bool enabled) {
auto os = getMutableLinuxSettings();
if(!os) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error);
return false;
}
(*os)->useExternalWifiAntenna = enabled ? 1 : 0;
return true;
}
std::optional<LinuxConfigurationPort> IDeviceSettings::getLinuxConfigurationPort() {
const auto* os = getLinuxSettings();
if(os == nullptr) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::EventWarning);
return std::nullopt;
}
switch(static_cast<LinuxConfigurationPort>(os->ethConfigurationPort)) {
case LinuxConfigurationPort::ETH01:
return LinuxConfigurationPort::ETH01;
case LinuxConfigurationPort::USB:
default: // Any other value means the configuration interface is over USB
return LinuxConfigurationPort::USB;
}
}
bool IDeviceSettings::setLinuxConfigurationPort(LinuxConfigurationPort port) {
switch(port) {
case LinuxConfigurationPort::USB:
case LinuxConfigurationPort::ETH01:
break;
default:
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false;
}
auto os = getMutableLinuxSettings();
if(!os) {
report(APIEvent::Type::SettingNotAvaiableDevice, APIEvent::Severity::Error);
return false;
}
(*os)->ethConfigurationPort = static_cast<uint8_t>(port);
return true;
}
+73
View File
@@ -10,6 +10,14 @@ Simple
.. literalinclude:: ../../examples/c2/simple/src/main.c .. literalinclude:: ../../examples/c2/simple/src/main.c
:language: c :language: c
Supported Devices
=================
:download:`Download example <../../examples/c2/supported_devices/src/main.c>`
.. literalinclude:: ../../examples/c2/supported_devices/src/main.c
:language: c
Disk Format Disk Format
=========== ===========
@@ -49,3 +57,68 @@ LIN Transmit
.. literalinclude:: ../../examples/c2/lin_transmit/src/main.c .. literalinclude:: ../../examples/c2/lin_transmit/src/main.c
:language: c :language: c
Ethernet Transmit
=================
:download:`Download example <../../examples/c2/ethernet_transmit/src/main.c>`
.. literalinclude:: ../../examples/c2/ethernet_transmit/src/main.c
:language: c
Ethernet Receive
================
:download:`Download example <../../examples/c2/ethernet_receive/src/main.c>`
.. literalinclude:: ../../examples/c2/ethernet_receive/src/main.c
:language: c
Ethernet Status
===============
:download:`Download example <../../examples/c2/ethernet_status/src/main.c>`
.. literalinclude:: ../../examples/c2/ethernet_status/src/main.c
:language: c
T1S Loopback
============
:download:`Download example <../../examples/c2/t1s_loopback/src/main.c>`
.. literalinclude:: ../../examples/c2/t1s_loopback/src/main.c
:language: c
TC10
====
:download:`Download example <../../examples/c2/tc10/src/main.c>`
.. literalinclude:: ../../examples/c2/tc10/src/main.c
:language: c
gPTP
====
:download:`Download example <../../examples/c2/gptp/src/main.c>`
.. literalinclude:: ../../examples/c2/gptp/src/main.c
:language: c
PerfTest
========
:download:`Download example <../../examples/c2/perf_test/src/main.c>`
.. literalinclude:: ../../examples/c2/perf_test/src/main.c
:language: c
Termination Groups
==================
:download:`Download example <../../examples/c2/termination/src/main.c>`
.. literalinclude:: ../../examples/c2/termination/src/main.c
:language: c
+8
View File
@@ -92,6 +92,14 @@ SPI Example for 10BASE-T1S
.. literalinclude:: ../../examples/python/spi/spi_example.py .. literalinclude:: ../../examples/python/spi/spi_example.py
:language: python :language: python
10BASE-T1S Settings Configuration
=================================
:download:`Download example <../../examples/python/t1s/t1s_settings.py>`
.. literalinclude:: ../../examples/python/t1s/t1s_settings.py
:language: python
Analog Output Control Analog Output Control
===================== =====================
+1 -1
View File
@@ -127,7 +127,7 @@ FlexRay Coldstart Configuration
To use the Coldstart example, ensure the following: To use the Coldstart example, ensure the following:
Set the Flexray network in neoVI Explorer to Coldstart. Set the Flexray network in ICS Device Manager to Coldstart.
No other nodes should be present on the network during testing. No other nodes should be present on the network during testing.
+15
View File
@@ -14,3 +14,18 @@ communication library. The source code for libicsneo can be found on GitHub:
icsneopy/index icsneopy/index
icsneoc/index icsneoc/index
icsneoc2/index icsneoc2/index
Linux Installation
==================
Applications that use raw Ethernet device discovery, such as PCAP-backed
discovery, need permission to open raw network sockets. Instead of running your
application with ``sudo``, grant the installed executable the required Linux
capabilities:
.. code-block:: bash
sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/your-app
Replace ``/usr/bin/your-app`` with the full path to the application executable.
+60
View File
@@ -2,13 +2,24 @@ option(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE "Build the command-line interactive
option(LIBICSNEO_BUILD_C_SIMPLE_EXAMPLE "Build the command-line simple C example." ON) option(LIBICSNEO_BUILD_C_SIMPLE_EXAMPLE "Build the command-line simple C example." ON)
option(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE "Build the command-line simple C example." ON) option(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE "Build the command-line simple C example." ON)
option(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE "Build the simple C2 example." ON) option(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE "Build the simple C2 example." ON)
option(LIBICSNEO_BUILD_C2_SUPPORTED_DEVICES_EXAMPLE "Build the C2 supported devices example." ON)
option(LIBICSNEO_BUILD_C2_READ_MESSAGES_EXAMPLE "Build the C2 read messages example." ON) option(LIBICSNEO_BUILD_C2_READ_MESSAGES_EXAMPLE "Build the C2 read messages example." ON)
option(LIBICSNEO_BUILD_C2_DISKFORMAT_EXAMPLE "Build the C2 disk format example." ON) option(LIBICSNEO_BUILD_C2_DISKFORMAT_EXAMPLE "Build the C2 disk format example." ON)
option(LIBICSNEO_BUILD_C2_RECONNECT_EXAMPLE "Build the C2 reconnect example." ON) option(LIBICSNEO_BUILD_C2_RECONNECT_EXAMPLE "Build the C2 reconnect example." ON)
option(LIBICSNEO_BUILD_C2_DEVICE_INFO_EXAMPLE "Build the C2 device info example." ON) option(LIBICSNEO_BUILD_C2_DEVICE_INFO_EXAMPLE "Build the C2 device info example." ON)
option(LIBICSNEO_BUILD_C2_CHIP_VERSIONS_EXAMPLE "Build the C2 chip versions example." ON)
option(LIBICSNEO_BUILD_C2_TERMINATION_EXAMPLE "Build the C2 termination groups example." ON)
option(LIBICSNEO_BUILD_C2_LIN_EXAMPLE "Build the C2 LIN example." ON) option(LIBICSNEO_BUILD_C2_LIN_EXAMPLE "Build the C2 LIN example." ON)
option(LIBICSNEO_BUILD_C2_LIN_TRANSMIT_EXAMPLE "Build the C2 LIN transmit example." ON) option(LIBICSNEO_BUILD_C2_LIN_TRANSMIT_EXAMPLE "Build the C2 LIN transmit example." ON)
option(LIBICSNEO_BUILD_C2_ETHERNET_TRANSMIT_EXAMPLE "Build the C2 ethernet transmit example." ON)
option(LIBICSNEO_BUILD_C2_ETHERNET_RECEIVE_EXAMPLE "Build the C2 ethernet receive example." ON)
option(LIBICSNEO_BUILD_C2_ETHERNET_STATUS_EXAMPLE "Build the C2 ethernet status example." ON)
option(LIBICSNEO_BUILD_C2_T1S_LOOPBACK_EXAMPLE "Build the C2 RAD-Comet3 T1S loopback example." ON)
option(LIBICSNEO_BUILD_C2_TC10_EXAMPLE "Build the C2 TC10 example." ON)
option(LIBICSNEO_BUILD_C2_GPTP_EXAMPLE "Build the C2 gPTP settings example." ON)
option(LIBICSNEO_BUILD_C2_PERF_TEST_EXAMPLE "Build the C2 PerfTest setting example." ON)
option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON) option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON)
option(LIBICSNEO_BUILD_CPP_DEVICE_INFO_EXAMPLE "Build the C++ device info example." ON)
option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON) option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON)
option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON) option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON)
option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON) option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON)
@@ -24,6 +35,7 @@ option(LIBICSNEO_BUILD_CPP_MUTEX_EXAMPLE "Build the NetworkMutex example." ON)
option(LIBICSNEO_BUILD_CPP_ANALOG_OUT_EXAMPLE "Build the analog output example." ON) option(LIBICSNEO_BUILD_CPP_ANALOG_OUT_EXAMPLE "Build the analog output example." ON)
option(LIBICSNEO_BUILD_CPP_DISKFORMAT_EXAMPLE "Build the disk format example." ON) option(LIBICSNEO_BUILD_CPP_DISKFORMAT_EXAMPLE "Build the disk format example." ON)
option(LIBICSNEO_BUILD_CPP_T1S_EXAMPLE "Build the T1S example." ON) option(LIBICSNEO_BUILD_CPP_T1S_EXAMPLE "Build the T1S example." ON)
option(LIBICSNEO_BUILD_CPP_GPTP_EXAMPLE "Build the C++ gPTP settings example." ON)
add_compile_options(${LIBICSNEO_COMPILER_WARNINGS}) add_compile_options(${LIBICSNEO_COMPILER_WARNINGS})
@@ -43,6 +55,10 @@ if(LIBICSNEO_BUILD_C2_SIMPLE_EXAMPLE)
add_subdirectory(c2/simple) add_subdirectory(c2/simple)
endif() endif()
if(LIBICSNEO_BUILD_C2_SUPPORTED_DEVICES_EXAMPLE)
add_subdirectory(c2/supported_devices)
endif()
if(LIBICSNEO_BUILD_C2_READ_MESSAGES_EXAMPLE) if(LIBICSNEO_BUILD_C2_READ_MESSAGES_EXAMPLE)
add_subdirectory(c2/read_messages) add_subdirectory(c2/read_messages)
endif() endif()
@@ -59,6 +75,14 @@ if(LIBICSNEO_BUILD_C2_DEVICE_INFO_EXAMPLE)
add_subdirectory(c2/device_info) add_subdirectory(c2/device_info)
endif() endif()
if(LIBICSNEO_BUILD_C2_CHIP_VERSIONS_EXAMPLE)
add_subdirectory(c2/chip_versions)
endif()
if(LIBICSNEO_BUILD_C2_TERMINATION_EXAMPLE)
add_subdirectory(c2/termination)
endif()
if(LIBICSNEO_BUILD_C2_LIN_EXAMPLE) if(LIBICSNEO_BUILD_C2_LIN_EXAMPLE)
add_subdirectory(c2/lin) add_subdirectory(c2/lin)
endif() endif()
@@ -67,10 +91,42 @@ if(LIBICSNEO_BUILD_C2_LIN_TRANSMIT_EXAMPLE)
add_subdirectory(c2/lin_transmit) add_subdirectory(c2/lin_transmit)
endif() endif()
if(LIBICSNEO_BUILD_C2_ETHERNET_TRANSMIT_EXAMPLE)
add_subdirectory(c2/ethernet_transmit)
endif()
if(LIBICSNEO_BUILD_C2_ETHERNET_RECEIVE_EXAMPLE)
add_subdirectory(c2/ethernet_receive)
endif()
if(LIBICSNEO_BUILD_C2_ETHERNET_STATUS_EXAMPLE)
add_subdirectory(c2/ethernet_status)
endif()
if(LIBICSNEO_BUILD_C2_T1S_LOOPBACK_EXAMPLE)
add_subdirectory(c2/t1s_loopback)
endif()
if(LIBICSNEO_BUILD_C2_TC10_EXAMPLE)
add_subdirectory(c2/tc10)
endif()
if(LIBICSNEO_BUILD_C2_GPTP_EXAMPLE)
add_subdirectory(c2/gptp)
endif()
if(LIBICSNEO_BUILD_C2_PERF_TEST_EXAMPLE)
add_subdirectory(c2/perf_test)
endif()
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE) if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
add_subdirectory(cpp/simple) add_subdirectory(cpp/simple)
endif() endif()
if(LIBICSNEO_BUILD_CPP_DEVICE_INFO_EXAMPLE)
add_subdirectory(cpp/device_info)
endif()
if(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE) if(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE)
add_subdirectory(cpp/interactive) add_subdirectory(cpp/interactive)
endif() endif()
@@ -130,3 +186,7 @@ endif()
if(LIBICSNEO_BUILD_CPP_T1S_EXAMPLE) if(LIBICSNEO_BUILD_CPP_T1S_EXAMPLE)
add_subdirectory(cpp/t1s) add_subdirectory(cpp/t1s)
endif() endif()
if(LIBICSNEO_BUILD_CPP_GPTP_EXAMPLE)
add_subdirectory(cpp/gptp)
endif()
+5 -5
View File
@@ -58,7 +58,7 @@ int main() {
msg1.Protocol = SPY_PROTOCOL_LIN; msg1.Protocol = SPY_PROTOCOL_LIN;
msg1.StatusBitField = 0; msg1.StatusBitField = 0;
msg1.StatusBitField2 = 0; msg1.StatusBitField2 = 0;
lNetworkID = NETID_LIN_02; lNetworkID = NETID_LIN2;
msg1.Header[0] = 0x11; //protected ID msg1.Header[0] = 0x11; //protected ID
msg1.Header[1] = 0xaa; msg1.Header[1] = 0xaa;
msg1.Header[2] = 0xbb; msg1.Header[2] = 0xbb;
@@ -80,7 +80,7 @@ int main() {
icsSpyMessageJ1850 msg2 = {0}; icsSpyMessageJ1850 msg2 = {0};
msg2.Protocol = SPY_PROTOCOL_LIN; msg2.Protocol = SPY_PROTOCOL_LIN;
msg2.StatusBitField = SPY_STATUS_INIT_MESSAGE; msg2.StatusBitField = SPY_STATUS_INIT_MESSAGE;
lNetworkID = NETID_LIN_01; lNetworkID = NETID_LIN;
msg2.Header[0] = 0x11; //protected ID msg2.Header[0] = 0x11; //protected ID
msg2.NumberBytesData = 0; msg2.NumberBytesData = 0;
msg2.NumberBytesHeader = 1; msg2.NumberBytesHeader = 1;
@@ -96,7 +96,7 @@ int main() {
msg3.Protocol = SPY_PROTOCOL_LIN; msg3.Protocol = SPY_PROTOCOL_LIN;
msg3.StatusBitField = SPY_STATUS_INIT_MESSAGE; msg3.StatusBitField = SPY_STATUS_INIT_MESSAGE;
msg3.StatusBitField2 = 0; msg3.StatusBitField2 = 0;
lNetworkID = NETID_LIN_01; lNetworkID = NETID_LIN;
msg3.Header[0] = 0xe2; //protected ID msg3.Header[0] = 0xe2; //protected ID
msg3.Header[1] = 0x44; msg3.Header[1] = 0x44;
msg3.Header[2] = 0x33; msg3.Header[2] = 0x33;
@@ -131,10 +131,10 @@ int main() {
const icsSpyMessageJ1850* linMsg = (icsSpyMessageJ1850*)&rxMsg[idx]; const icsSpyMessageJ1850* linMsg = (icsSpyMessageJ1850*)&rxMsg[idx];
size_t frameLen = (linMsg->NumberBytesHeader + linMsg->NumberBytesData); size_t frameLen = (linMsg->NumberBytesHeader + linMsg->NumberBytesData);
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0; size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
if(linMsg->NetworkID == NETID_LIN_01) { if(linMsg->NetworkID == NETID_LIN) {
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen); printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen);
} }
else if (linMsg->NetworkID == NETID_LIN_02) { else if (linMsg->NetworkID == NETID_LIN2) {
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen); printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen);
} }
+6
View File
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-chip-versions-example src/main.c)
target_link_libraries(libicsneoc2-chip-versions-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-chip-versions-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+59
View File
@@ -0,0 +1,59 @@
#include <icsneo/icsneoc2.h>
#include <stdio.h>
#include <inttypes.h>
int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64];
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %u with error code %u\n", message, error, res);
return (int)res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
int main() {
icsneoc2_error_t res;
icsneoc2_device_t* device = NULL;
res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to open first device", res);
}
char description[128] = {0};
size_t description_length = sizeof(description);
icsneoc2_device_description_get(device, description, &description_length);
printf("Opened device: %s\n\n", description);
size_t count = 0;
icsneoc2_chip_versions_t* chip_versions = NULL;
res = icsneoc2_device_chip_versions_enumerate(device, &chip_versions, true, &count);
if(res != icsneoc2_error_success) {
print_error_code("Failed to enumerate chip versions", res);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 1;
}
printf("Found %zu chip version(s):\n", count);
for(icsneoc2_chip_versions_t* cur = chip_versions; cur; cur = icsneoc2_chip_versions_next(cur)) {
char name[64] = {0};
size_t name_length = sizeof(name);
uint8_t major = 0, minor = 0, maintenance = 0, build = 0;
res = icsneoc2_chip_versions_props_get(cur, name, &name_length, &major, &minor, &maintenance, &build);
if(res != icsneoc2_error_success) {
print_error_code("Failed to get chip version properties", res);
continue;
}
printf(" %s: %u.%u.%u.%u\n", name, major, minor, maintenance, build);
}
icsneoc2_chip_versions_free(chip_versions);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 0;
}
+22 -10
View File
@@ -102,19 +102,31 @@ int main() {
print_error_code("Failed to get PCB serial (device may not support it)", res); print_error_code("Failed to get PCB serial (device may not support it)", res);
} }
/* ===== MAC Address ===== */ /* ===== MAC Addresses ===== */
uint8_t mac[6] = {0}; icsneoc2_mac_addr_entry_t* macs = NULL;
size_t mac_len = sizeof(mac); res = icsneoc2_device_mac_addresses_enumerate(device, &macs);
res = icsneoc2_device_mac_address_get(device, mac, &mac_len);
if(res == icsneoc2_error_success) { if(res == icsneoc2_error_success) {
printf("MAC: "); uint16_t mac_count = 0;
for(size_t i = 0; i < mac_len; i++) { for(icsneoc2_mac_addr_entry_t* cur = macs; cur; cur = icsneoc2_mac_addresses_next(cur)) {
if(i > 0) printf(":"); mac_count++;
printf("%02X", mac[i]);
} }
printf("\n"); printf("MACs: %u entr%s\n", mac_count, mac_count == 1 ? "y" : "ies");
for(icsneoc2_mac_addr_entry_t* cur = macs; cur; cur = icsneoc2_mac_addresses_next(cur)) {
icsneoc2_netid_t network_id;
icsneoc2_mac_network_id_get(cur, &network_id);
printf(" Network %-5u ", (unsigned)network_id);
uint8_t address[6];
size_t address_size = 6;
icsneoc2_mac_address_get(cur, address, &address_size);
for(int j = 0; j < 6; j++) {
if(j > 0) printf(":");
printf("%02X", (unsigned)address[j]);
}
printf("\n");
}
icsneoc2_mac_addresses_free(macs);
} else { } else {
print_error_code("Failed to get MAC address (device may not support it)", res); print_error_code("Failed to get MAC addresses (device may not support it)", res);
} }
/* Cleanup */ /* Cleanup */
+79
View File
@@ -109,6 +109,85 @@ int main() {
} }
} }
/* Choose operation */
printf("\n\tChoose operation:\n");
printf("\t [f] Format the disk(s)\n");
printf("\t [u] Update the disk configuration without formatting\n");
printf("\t [q] Quit\n");
printf("\tSelection [f/u/q]: ");
char choice[8] = {0};
if(scanf("%7s", choice) != 1) {
choice[0] = 'q';
}
if(choice[0] == 'u' || choice[0] == 'U') {
/* Force a disk config update (e.g. changing the disk layout) without formatting.
* Unlike a format, this preserves the existing data on the disk(s). */
printf("\n\tChoose disk layout:\n");
printf("\t [s] Spanned\n");
printf("\t [r] RAID0\n");
printf("\tSelection [s/r] (current: %s): ", layout == icsneoc2_disk_layout_raid0 ? "RAID0" : "Spanned");
char layout_choice[8] = {0};
if(scanf("%7s", layout_choice) != 1) {
layout_choice[0] = '\0';
}
if(layout_choice[0] == 'r' || layout_choice[0] == 'R') {
icsneoc2_disk_details_layout_set(details, icsneoc2_disk_layout_raid0);
} else if(layout_choice[0] == 's' || layout_choice[0] == 'S') {
icsneoc2_disk_details_layout_set(details, icsneoc2_disk_layout_spanned);
} else {
printf("\tKeeping current layout.\n");
}
/* Enable/disable individual disks in the configuration. A disk's enabled
* state is carried by the FORMATTED flag; only present disks can be enabled. */
for(size_t i = 0; i < detail_count; i++) {
icsneoc2_disk_format_flags_t flags = 0;
icsneoc2_disk_details_flags_get(details, i, &flags);
if(!(flags & ICSNEOC2_DISK_FORMAT_FLAGS_PRESENT)) {
continue; /* Can't enable a disk that isn't present */
}
printf("\tEnable disk [%zu]? [y/N] (currently %s): ", i,
(flags & ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED) ? "enabled" : "disabled");
char disk_choice[8] = {0};
if(scanf("%7s", disk_choice) != 1) {
disk_choice[0] = '\0';
}
if(disk_choice[0] == 'y' || disk_choice[0] == 'Y') {
flags |= ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED;
} else {
flags &= ~(icsneoc2_disk_format_flags_t)ICSNEOC2_DISK_FORMAT_FLAGS_FORMATTED;
}
icsneoc2_disk_details_flags_set(details, i, flags);
}
icsneoc2_disk_layout_t new_layout = 0;
icsneoc2_disk_details_layout_get(details, &new_layout);
printf("\n\tForcing disk config update on %s to %s layout (no data will be erased)...\n",
description, new_layout == icsneoc2_disk_layout_raid0 ? "RAID0" : "Spanned");
res = icsneoc2_device_force_disk_config_update(device, details);
if(res != icsneoc2_error_success) {
print_error_code("\tForce disk config update failed", res);
icsneoc2_disk_details_free(details);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return -1;
}
printf("\tDisk config update complete!\n");
icsneoc2_disk_details_free(details);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 0;
}
if(choice[0] != 'f' && choice[0] != 'F') {
printf("\tAborted.\n");
icsneoc2_disk_details_free(details);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 0;
}
/* Build format config: mark present disks for formatting */ /* Build format config: mark present disks for formatting */
bool any_present = false; bool any_present = false;
for(size_t i = 0; i < detail_count; i++) { for(size_t i = 0; i < detail_count; i++) {
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-ethernet-receive-example src/main.c)
target_link_libraries(libicsneoc2-ethernet-receive-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-ethernet-receive-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+229
View File
@@ -0,0 +1,229 @@
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2messages.h>
#include <stdio.h>
#include <inttypes.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
void sleep_ms(uint32_t ms) {
#ifdef _WIN32
Sleep(ms);
#else
sleep(ms / 1000);
#endif
}
int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64];
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res);
return res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
void print_events(void) {
icsneoc2_event_t* events[256] = {0};
size_t events_count = 256;
for(size_t i = 0; i < events_count; ++i) {
icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL);
if(res != icsneoc2_error_success) {
(void)print_error_code("\tFailed to get events", res);
return;
}
if(events[i] == NULL) {
events_count = i;
break;
}
}
for(size_t i = 0; i < events_count; i++) {
char description[255] = {0};
size_t description_length = 255;
icsneoc2_error_t res = icsneoc2_event_description_get(events[i], description, &description_length);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to get event description", res);
continue;
}
printf("\tEvent %zu: %s\n", i, description);
}
for(size_t i = 0; i < events_count; i++) {
icsneoc2_event_free(events[i]);
}
if(events_count > 0) {
printf("\tReceived %zu events\n", events_count);
}
}
void print_mac(const char* label, const uint8_t* mac) {
printf("%s: %02x:%02x:%02x:%02x:%02x:%02x", label, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
int process_ethernet_message(icsneoc2_message_t* message, size_t index) {
icsneoc2_netid_t netid = 0;
uint64_t timestamp = 0;
char netid_name[128] = {0};
size_t netid_name_length = 128;
icsneoc2_error_t res = icsneoc2_message_netid_get(message, &netid);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get netid", res);
}
res = icsneoc2_netid_name_get(netid, netid_name, &netid_name_length);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get netid name", res);
}
res = icsneoc2_message_timestamp_get(message, &timestamp);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get timestamp", res);
}
/* Get data length first */
size_t data_length = 0;
res = icsneoc2_message_data_get(message, NULL, &data_length);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get data length", res);
}
printf("\t%zu) Ethernet Frame on %s (0x%x) - %zu bytes\n", index, netid_name, netid, data_length);
printf("\t Timestamp: %" PRIu64 " ns since 2007-01-01 UTC\n", timestamp);
/* Get MAC addresses and EtherType if we have enough data */
uint8_t dst_mac[6] = {0};
uint8_t src_mac[6] = {0};
uint16_t ether_type = 0;
res = icsneoc2_message_eth_mac_get(message, dst_mac, src_mac);
if(res == icsneoc2_error_success) {
printf("\t ");
print_mac("Dst", dst_mac);
printf(" ");
print_mac("Src", src_mac);
printf("\n");
}
res = icsneoc2_message_eth_ether_type_get(message, &ether_type);
if(res == icsneoc2_error_success) {
printf("\t EtherType: 0x%04x\n", ether_type);
}
/* Get flags */
icsneoc2_message_eth_flags_t flags = 0;
res = icsneoc2_message_eth_props_get(message, &flags, NULL, NULL);
if(res == icsneoc2_error_success && flags != 0) {
printf("\t Flags: 0x%" PRIx64, flags);
if(flags & ICSNEOC2_MESSAGE_ETH_FLAGS_CRC_ERROR) printf(" [CRC_ERROR]");
if(flags & ICSNEOC2_MESSAGE_ETH_FLAGS_FRAME_TOO_SHORT) printf(" [FRAME_TOO_SHORT]");
if(flags & ICSNEOC2_MESSAGE_ETH_FLAGS_TX_ABORTED) printf(" [TX_ABORTED]");
if(flags & ICSNEOC2_MESSAGE_ETH_FLAGS_FCS_VERIFIED) printf(" [FCS_VERIFIED]");
if(flags & ICSNEOC2_MESSAGE_ETH_FLAGS_PREEMPTION_ENABLED) printf(" [PREEMPTION]");
if(flags & ICSNEOC2_MESSAGE_ETH_FLAGS_IS_T1S) printf(" [T1S]");
printf("\n");
}
/* Print data bytes */
uint8_t data[1600] = {0};
res = icsneoc2_message_data_get(message, data, &data_length);
if(res == icsneoc2_error_success) {
printf("\t Data:\n\t ");
for(size_t x = 0; x < data_length; x++) {
printf("0x%02x ", data[x]);
if((x + 1) % 20 == 0 && x + 1 < data_length) {
printf("\n\t ");
}
}
printf("\n");
}
return icsneoc2_error_success;
}
int main(void) {
/* Open the first available device */
printf("Opening first available device...\n");
icsneoc2_device_t* device = NULL;
icsneoc2_error_t res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to open first device", res);
}
/* Get a description of the opened device */
char description[255] = {0};
size_t description_length = 255;
res = icsneoc2_device_description_get(device, description, &description_length);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to get device description", res);
}
printf("Opened device: %s\n", description);
/* Wait for Ethernet frames to arrive */
const int duration_seconds = 10;
printf("Listening for Ethernet frames for %d seconds...\n", duration_seconds);
sleep_ms(duration_seconds * 1000);
/* Retrieve and process messages */
icsneoc2_message_t* messages[20000] = {0};
size_t message_count = 20000;
size_t eth_count = 0;
for(size_t i = 0; i < message_count; ++i) {
res = icsneoc2_device_message_get(device, &messages[i], 0);
if(res != icsneoc2_error_success) {
print_events();
return print_error_code("Failed to get messages", res);
}
if(messages[i] == NULL) {
message_count = i;
break;
}
}
printf("Got %zu messages total, filtering for Ethernet...\n", message_count);
for(size_t i = 0; i < message_count; i++) {
icsneoc2_message_t* message = messages[i];
/* Check if this is a TX echo (skip it) */
bool is_tx = false;
res = icsneoc2_message_is_transmit(message, &is_tx);
if(res != icsneoc2_error_success || is_tx) {
continue;
}
/* Check if this is an Ethernet message */
bool is_ethernet = false;
res = icsneoc2_message_is_ethernet(message, &is_ethernet);
if(res != icsneoc2_error_success || !is_ethernet) {
continue;
}
process_ethernet_message(message, eth_count);
eth_count++;
}
printf("Received %zu Ethernet frames out of %zu total messages\n", eth_count, message_count);
/* Free all messages */
for(size_t i = 0; i < message_count; ++i) {
icsneoc2_message_free(messages[i]);
}
/* Print any events */
print_events();
/* Close the device */
printf("Closing device...\n");
res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to close device", res);
}
return 0;
}
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-ethernet-status-example src/main.c)
target_link_libraries(libicsneoc2-ethernet-status-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-ethernet-status-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+228
View File
@@ -0,0 +1,228 @@
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2messages.h>
#include <stdio.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
void sleep_ms(uint32_t ms) {
#ifdef _WIN32
Sleep(ms);
#else
usleep(ms * 1000);
#endif
}
int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64] = {0};
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %u with error code %u\n", message, error, res);
return (int)res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
void print_events(void) {
icsneoc2_event_t* events[256] = {0};
size_t events_count = 256;
for(size_t i = 0; i < events_count; ++i) {
icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL);
if(res != icsneoc2_error_success) {
(void)print_error_code("\tFailed to get events", res);
return;
}
if(events[i] == NULL) {
events_count = i;
break;
}
}
for(size_t i = 0; i < events_count; i++) {
char description[255] = {0};
size_t description_length = sizeof(description);
icsneoc2_error_t res = icsneoc2_event_description_get(events[i], description, &description_length);
if(res != icsneoc2_error_success) {
(void)print_error_code("\tFailed to get event description", res);
continue;
}
printf("\tEvent %zu: %s\n", i, description);
}
for(size_t i = 0; i < events_count; i++) {
icsneoc2_event_free(events[i]);
}
if(events_count > 0) {
printf("\tReceived %zu events\n", events_count);
}
}
const char* link_speed_name(icsneoc2_link_speed_t speed) {
switch(speed) {
case icsneoc2_link_speed_auto:
return "Auto";
case icsneoc2_link_speed_10mbps:
return "10 Mbps";
case icsneoc2_link_speed_100mbps:
return "100 Mbps";
case icsneoc2_link_speed_1000mbps:
return "1000 Mbps";
case icsneoc2_link_speed_2500mbps:
return "2500 Mbps";
case icsneoc2_link_speed_5000mbps:
return "5000 Mbps";
case icsneoc2_link_speed_10000mbps:
return "10000 Mbps";
default:
return "Unknown";
}
}
const char* link_mode_name(icsneoc2_link_mode_t mode) {
switch(mode) {
case icsneoc2_link_mode_auto:
return "Auto";
case icsneoc2_link_mode_master:
return "Master";
case icsneoc2_link_mode_slave:
return "Slave";
case icsneoc2_link_mode_invalid:
return "Invalid";
case icsneoc2_link_mode_none:
return "None";
default:
return "Unknown";
}
}
int print_ethernet_status_message(icsneoc2_message_t* message, size_t index) {
icsneoc2_netid_t netid = 0;
char netid_name[128] = {0};
size_t netid_name_length = sizeof(netid_name);
bool link_state = false;
bool duplex = false;
icsneoc2_link_speed_t link_speed = icsneoc2_link_speed_auto;
icsneoc2_link_mode_t link_mode = icsneoc2_link_mode_auto;
icsneoc2_error_t res = icsneoc2_message_netid_get(message, &netid);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get Ethernet status netid", res);
}
res = icsneoc2_netid_name_get(netid, netid_name, &netid_name_length);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get Ethernet status netid name", res);
}
res = icsneoc2_message_eth_status_props_get(message, &link_state, &duplex, &link_speed, &link_mode);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get Ethernet status properties", res);
}
printf("\t%zu) Ethernet status on %s (0x%x)\n", index, netid_name, netid);
printf("\t Link: %s\n", link_state ? "Up" : "Down");
printf("\t Duplex: %s\n", duplex ? "Full" : "Half");
printf("\t Speed: %s (%u)\n", link_speed_name(link_speed), link_speed);
printf("\t Mode: %s (%u)\n", link_mode_name(link_mode), link_mode);
return icsneoc2_error_success;
}
int main(void) {
printf("Opening first available device offline...\n");
icsneoc2_device_t* device = NULL;
icsneoc2_open_options_t options = icsneoc2_open_options_default & ~ICSNEOC2_OPEN_OPTIONS_GO_ONLINE;
icsneoc2_error_t res = icsneoc2_device_open_first(0, options, &device);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to open first device", res);
}
char description[255] = {0};
size_t description_length = sizeof(description);
res = icsneoc2_device_description_get(device, description, &description_length);
if(res != icsneoc2_error_success) {
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return print_error_code("Failed to get device description", res);
}
printf("Opened device: %s\n", description);
printf("Going online to trigger Ethernet status broadcast...\n");
res = icsneoc2_device_go_online(device, true);
if(res != icsneoc2_error_success) {
print_events();
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return print_error_code("Failed to go online", res);
}
const int listen_seconds = 6;
time_t start_time = time(NULL);
size_t status_count = 0;
size_t total_count = 0;
printf("Listening for Ethernet status messages for %d seconds...\n", listen_seconds);
while(time(NULL) - start_time < listen_seconds) {
icsneoc2_message_t* message = NULL;
res = icsneoc2_device_message_get(device, &message, 100);
if(res != icsneoc2_error_success) {
print_events();
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return print_error_code("Failed to get message", res);
}
if(message == NULL) {
sleep_ms(10);
continue;
}
total_count++;
bool is_ethernet_status = false;
res = icsneoc2_message_is_ethernet_status(message, &is_ethernet_status);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
print_events();
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return print_error_code("Failed to check for Ethernet status message", res);
}
if(is_ethernet_status) {
res = print_ethernet_status_message(message, status_count);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
print_events();
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return (int)res;
}
status_count++;
}
icsneoc2_message_free(message);
}
printf("Received %zu Ethernet status messages out of %zu total messages.\n", status_count, total_count);
if(status_count == 0) {
printf("No Ethernet status messages were seen. These messages are broadcast when the device goes online and may depend on device Ethernet support.\n");
}
print_events();
printf("Closing device...\n");
res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(device);
return print_error_code("Failed to close device", res);
}
icsneoc2_device_free(device);
return 0;
}
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-ethernet-transmit-example src/main.c)
target_link_libraries(libicsneoc2-ethernet-transmit-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-ethernet-transmit-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+204
View File
@@ -0,0 +1,204 @@
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2messages.h>
#include <stdio.h>
#include <inttypes.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64];
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res);
return res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
void print_events(void) {
icsneoc2_event_t* events[256] = {0};
size_t events_count = 256;
for(size_t i = 0; i < events_count; ++i) {
icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL);
if(res != icsneoc2_error_success) {
(void)print_error_code("\tFailed to get events", res);
return;
}
if(events[i] == NULL) {
events_count = i;
break;
}
}
for(size_t i = 0; i < events_count; i++) {
char description[255] = {0};
size_t description_length = 255;
icsneoc2_error_t res = icsneoc2_event_description_get(events[i], description, &description_length);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to get event description", res);
continue;
}
printf("\tEvent %zu: %s\n", i, description);
}
for(size_t i = 0; i < events_count; i++) {
icsneoc2_event_free(events[i]);
}
if(events_count > 0) {
printf("\tReceived %zu events\n", events_count);
}
}
int main(void) {
/* Open the first available device */
printf("Opening first available device...\n");
icsneoc2_device_t* device = NULL;
icsneoc2_error_t res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to open first device", res);
}
/* Get a description of the opened device */
char description[255] = {0};
size_t description_length = 255;
res = icsneoc2_device_description_get(device, description, &description_length);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to get device description", res);
}
printf("Opened device: %s\n", description);
icsneoc2_netid_t tx_networks[255] = {0};
size_t tx_net_count = sizeof(tx_networks) / sizeof(tx_networks[0]);
res = icsneoc2_device_supported_tx_networks_get(device, tx_networks, &tx_net_count);
if(res != icsneoc2_error_success) {
print_events();
return print_error_code("Failed to get TX networks", res);
}
/* Filter for Ethernet/AutomotiveEthernet networks */
icsneoc2_netid_t eth_networks[64] = {0};
char eth_names[64][128] = {{0}};
size_t eth_count = 0;
for(size_t i = 0; i < tx_net_count && eth_count < 64; i++) {
/* Create a temporary message to check network type */
icsneoc2_message_t* tmp = NULL;
res = icsneoc2_message_eth_create(&tmp);
if(res != icsneoc2_error_success) continue;
res = icsneoc2_message_netid_set(tmp, tx_networks[i]);
if(res != icsneoc2_error_success) { icsneoc2_message_free(tmp); continue; }
icsneoc2_network_type_t ntype = 0;
res = icsneoc2_message_network_type_get(tmp, &ntype);
icsneoc2_message_free(tmp);
if(res != icsneoc2_error_success) continue;
if(ntype == icsneoc2_network_type_ethernet || ntype == icsneoc2_network_type_automotive_ethernet) {
eth_networks[eth_count] = tx_networks[i];
size_t name_len = 128;
icsneoc2_netid_name_get(tx_networks[i], eth_names[eth_count], &name_len);
eth_count++;
}
}
if(eth_count == 0) {
printf("No Ethernet TX networks available on this device.\n");
icsneoc2_device_close(device);
return 0;
}
/* Let the user pick */
printf("Available Ethernet TX networks:\n");
for(size_t i = 0; i < eth_count; i++) {
printf(" %zu) %s\n", i + 1, eth_names[i]);
}
printf("Select network [1-%zu]: ", eth_count);
int selection = 0;
if(scanf("%d", &selection) != 1 || selection < 1 || (size_t)selection > eth_count) {
printf("Invalid selection, using first available.\n");
selection = 1;
}
icsneoc2_netid_t netid = eth_networks[selection - 1];
printf("Selected: %s\n", eth_names[selection - 1]);
/* Transmit Ethernet frames */
const size_t msg_count = 10;
printf("Transmitting %zu Ethernet frames on %s...\n", msg_count, eth_names[selection - 1]);
for(size_t i = 0; i < msg_count; i++) {
/* Create an Ethernet message */
icsneoc2_message_t* message = NULL;
res = icsneoc2_message_eth_create(&message);
if(res != icsneoc2_error_success) {
print_events();
return print_error_code("Failed to create Ethernet message", res);
}
/* Set the network ID */
res = icsneoc2_message_netid_set(message, netid );
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
print_events();
return print_error_code("Failed to set netid", res);
}
/* Build Ethernet frame data:
* Bytes 0-5: Destination MAC (00:FC:70:00:01:02)
* Bytes 6-11: Source MAC (00:FC:70:00:01:01)
* Bytes 12-13: EtherType (0x0800 = IPv4)
* Bytes 14+: Payload
*/
uint8_t frame_data[] = {
0x00, 0xFC, 0x70, 0x00, 0x01, 0x02, /* Destination MAC */
0x00, 0xFC, 0x70, 0x00, 0x01, 0x01, /* Source MAC */
0x08, 0x00, /* EtherType (IPv4) */
0x45, 0x00, 0x00, 0x20, /* IPv4: ver/IHL, DSCP, total length (32) */
0x00, 0x00, 0x00, 0x00, /* Identification, flags/fragment offset */
0x40, 0x11, 0x00, 0x00, /* TTL (64), protocol (UDP), checksum (0) */
0xC0, 0xA8, 0x01, 0x01, /* Source IP (192.168.1.1) */
0xC0, 0xA8, 0x01, 0x02, /* Destination IP (192.168.1.2) */
0xC3, 0x50, 0xC3, 0x51, /* UDP: src port (50000), dst port (50001) */
0x00, 0x0C, 0x00, 0x00, /* UDP: length (12), checksum (0) */
0x00, 0x00, 0x00, 0x00 /* UDP payload (4 bytes, frame counter) */
};
/* Put the frame counter in the UDP payload */
frame_data[42] = (uint8_t)((i >> 24) & 0xFF);
frame_data[43] = (uint8_t)((i >> 16) & 0xFF);
frame_data[44] = (uint8_t)((i >> 8) & 0xFF);
frame_data[45] = (uint8_t)(i & 0xFF);
res = icsneoc2_message_data_set(message, frame_data, sizeof(frame_data));
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
print_events();
return print_error_code("Failed to set frame data", res);
}
/* Transmit the message */
res = icsneoc2_device_message_transmit(device, message);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
print_events();
return print_error_code("Failed to transmit Ethernet frame", res);
}
icsneoc2_message_free(message);
printf("\tTransmitted frame %zu\n", i + 1);
}
printf("Successfully transmitted %zu Ethernet frames\n", msg_count);
/* Print any events */
print_events();
/* Close the device */
printf("Closing device...\n");
res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to close device", res);
}
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-gptp-example src/main.c)
target_link_libraries(libicsneoc2-gptp-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-gptp-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+330
View File
@@ -0,0 +1,330 @@
/*
* gPTP settings configurator example.
*
* Lists connected devices and their gPTP support, then lets the user
* configure the gPTP profile, role, port, and clock syntonization on any
* device that supports it. If --serial is omitted, the first available
* gPTP-capable device is used.
*/
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2settings.h>
#include <icsneo/icsneoc2messages.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
const char* serial;
bool list;
} args_t;
/* Maps an icsneoc2_netid_t to its gPTP port index (0 = not a gPTP port). */
static uint8_t netid_to_gptp_port(icsneoc2_netid_t netid) {
switch(netid) {
case icsneoc2_netid_ae_01: return 1;
case icsneoc2_netid_ae_02: return 2;
case icsneoc2_netid_ae_03: return 3;
case icsneoc2_netid_ae_04: return 4;
case icsneoc2_netid_ae_05: return 5;
case icsneoc2_netid_ae_06: return 6;
case icsneoc2_netid_ae_07: return 7;
case icsneoc2_netid_ae_08: return 8;
case icsneoc2_netid_ae_09: return 9;
case icsneoc2_netid_ae_10: return 10;
case icsneoc2_netid_ae_11: return 11;
case icsneoc2_netid_ae_12: return 12;
case icsneoc2_netid_ethernet_01: return 13;
case icsneoc2_netid_ethernet_02: return 14;
case icsneoc2_netid_ethernet_03: return 15;
case icsneoc2_netid_ae_13: return 16;
case icsneoc2_netid_ae_14: return 17;
case icsneoc2_netid_ae_15: return 18;
case icsneoc2_netid_ae_16: return 19;
default: return 0;
}
}
static const char* gptp_profile_str(icsneoc2_gptp_profile_t p) {
switch(p) {
case icsneoc2_gptp_profile_standard: return "Standard";
case icsneoc2_gptp_profile_automotive: return "Automotive";
default: return "Unknown";
}
}
static const char* gptp_role_str(icsneoc2_gptp_role_t r) {
switch(r) {
case icsneoc2_gptp_role_disabled: return "Disabled";
case icsneoc2_gptp_role_passive: return "Passive";
case icsneoc2_gptp_role_master: return "Master";
case icsneoc2_gptp_role_slave: return "Slave";
default: return "Unknown";
}
}
static int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64] = {0};
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
fprintf(stderr, "%s: failed to get string for error code %u\n", message, error);
return (int)res;
}
fprintf(stderr, "%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
static void print_usage(const char* prog) {
printf("Usage:\n");
printf(" %s --list\n", prog);
printf(" %s [--serial SERIAL]\n", prog);
printf("\n");
printf(" --list List connected devices and their gPTP support.\n");
printf(" --serial SERIAL Serial number of the device to configure.\n");
printf(" -h, --help Show this message.\n");
}
static int parse_args(int argc, char** argv, args_t* out) {
memset(out, 0, sizeof(*out));
for(int i = 1; i < argc; ++i) {
const char* a = argv[i];
if(strcmp(a, "-h") == 0 || strcmp(a, "--help") == 0) {
print_usage(argv[0]);
exit(0);
} else if(strcmp(a, "--list") == 0) {
out->list = true;
} else if(strcmp(a, "--serial") == 0) {
if(i + 1 >= argc) {
fprintf(stderr, "error: --serial requires a value\n");
return 1;
}
out->serial = argv[++i];
} else {
fprintf(stderr, "error: unknown argument '%s'\n", a);
print_usage(argv[0]);
return 1;
}
}
return 0;
}
static icsneoc2_device_info_t* find_device(icsneoc2_device_info_t* list, const char* serial) {
for(icsneoc2_device_info_t* cur = list; cur != NULL; cur = icsneoc2_device_info_next(cur)) {
if(serial == NULL)
return cur;
char dev_serial[64] = {0};
size_t dev_serial_len = sizeof(dev_serial);
if(icsneoc2_device_info_serial_get(cur, dev_serial, &dev_serial_len) != icsneoc2_error_success)
continue;
if(strcmp(dev_serial, serial) == 0)
return cur;
}
return NULL;
}
/*
* Print a list of available Ethernet/AE networks with their gPTP port indices.
* Returns the number of ports printed.
*/
static size_t print_gptp_ports(icsneoc2_device_t* device) {
icsneoc2_netid_t nets[128] = {0};
size_t count = sizeof(nets) / sizeof(nets[0]);
if(icsneoc2_device_supported_tx_networks_get(device, nets, &count) != icsneoc2_error_success)
return 0;
size_t printed = 0;
for(size_t i = 0; i < count; ++i) {
uint8_t port = netid_to_gptp_port(nets[i]);
if(port == 0)
continue;
char name[64] = {0};
size_t name_len = sizeof(name);
icsneoc2_netid_name_get(nets[i], name, &name_len);
printf(" [%u] %s\n", (unsigned)port, name);
++printed;
}
return printed;
}
static int list_devices(icsneoc2_device_info_t* list) {
size_t index = 0;
for(icsneoc2_device_info_t* cur = list; cur != NULL; cur = icsneoc2_device_info_next(cur)) {
char serial[64] = {0};
size_t serial_len = sizeof(serial);
char description[256] = {0};
size_t description_len = sizeof(description);
icsneoc2_device_info_serial_get(cur, serial, &serial_len);
icsneoc2_device_info_description_get(cur, description, &description_len);
printf("[%zu] %s (%s)\n", index++, description, serial);
icsneoc2_device_t* device = NULL;
if(icsneoc2_device_create(cur, &device) != icsneoc2_error_success)
continue;
icsneoc2_open_options_t opts = icsneoc2_open_options_default;
opts &= ~ICSNEOC2_OPEN_OPTIONS_SYNC_RTC;
opts &= ~ICSNEOC2_OPEN_OPTIONS_GO_ONLINE;
if(icsneoc2_device_open(device, opts) != icsneoc2_error_success) {
icsneoc2_device_free(device);
continue;
}
bool supported = false;
icsneoc2_device_supports_gptp(device, &supported);
printf(" gPTP supported: %s\n", supported ? "yes" : "no");
if(supported) {
printf(" gPTP ports:\n");
print_gptp_ports(device);
}
icsneoc2_device_close(device);
icsneoc2_device_free(device);
}
return 0;
}
static void print_current_settings(icsneoc2_device_t* device) {
icsneoc2_gptp_profile_t profile = icsneoc2_gptp_profile_standard;
icsneoc2_gptp_role_t role = icsneoc2_gptp_role_disabled;
uint8_t port = 0;
bool clock_syntonization = false;
printf("\nCurrent gPTP settings:\n");
if(icsneoc2_settings_gptp_profile_get(device, &profile) == icsneoc2_error_success)
printf(" Profile: %s\n", gptp_profile_str(profile));
if(icsneoc2_settings_gptp_role_get(device, &role) == icsneoc2_error_success)
printf(" Role: %s\n", gptp_role_str(role));
if(icsneoc2_settings_gptp_enabled_port_get(device, &port) == icsneoc2_error_success)
printf(" Enabled port: %u%s\n", (unsigned)port, port == 0 ? " (disabled)" : "");
if(icsneoc2_settings_gptp_clock_syntonization_enabled_get(device, &clock_syntonization) == icsneoc2_error_success)
printf(" Clock syntonization: %s\n", clock_syntonization ? "enabled" : "disabled");
}
static long prompt_long(const char* prompt, long default_val, long min, long max) {
char buf[32] = {0};
printf("%s [%ld]: ", prompt, default_val);
fflush(stdout);
if(fgets(buf, sizeof(buf), stdin) == NULL || buf[0] == '\n')
return default_val;
char* end = NULL;
long val = strtol(buf, &end, 10);
if(end == buf || val < min || val > max) {
printf("Invalid input, using %ld.\n", default_val);
return default_val;
}
return val;
}
static int configure_device(icsneoc2_device_t* device) {
icsneoc2_error_t res;
res = icsneoc2_settings_refresh(device);
if(res != icsneoc2_error_success)
return print_error_code("Failed to refresh settings", res);
print_current_settings(device);
printf("\nAvailable gPTP ports (0 = disabled):\n");
printf(" [0] Disabled\n");
print_gptp_ports(device);
printf("\nConfigure gPTP:\n");
long profile = prompt_long(" Profile (0=Standard, 1=Automotive)", 1, 0, 1);
res = icsneoc2_settings_gptp_profile_set(device, (icsneoc2_gptp_profile_t)profile);
if(res != icsneoc2_error_success)
return print_error_code("Failed to set profile", res);
long role = prompt_long(" Role (0=Disabled, 1=Passive, 2=Master, 3=Slave)", 3, 0, 3);
res = icsneoc2_settings_gptp_role_set(device, (icsneoc2_gptp_role_t)role);
if(res != icsneoc2_error_success)
return print_error_code("Failed to set role", res);
long port = prompt_long(" Enabled port index", 0, 0, 19);
res = icsneoc2_settings_gptp_enabled_port_set(device, (uint8_t)port);
if(res != icsneoc2_error_success)
return print_error_code("Failed to set enabled port", res);
long syntonization = prompt_long(" Clock syntonization (0=disabled, 1=enabled)", 0, 0, 1);
res = icsneoc2_settings_gptp_clock_syntonization_enabled_set(device, syntonization != 0);
if(res != icsneoc2_error_success)
return print_error_code("Failed to set clock syntonization", res);
printf("\nNote: icsneoc2_settings_apply() persists settings on the device.\n");
res = icsneoc2_settings_apply(device);
if(res != icsneoc2_error_success)
return print_error_code("Failed to apply settings", res);
printf("\nUpdated gPTP settings:\n");
print_current_settings(device);
return 0;
}
int main(int argc, char** argv) {
args_t args;
if(parse_args(argc, argv, &args) != 0)
return 1;
icsneoc2_device_info_t* found_devices = NULL;
icsneoc2_error_t res = icsneoc2_device_enumerate(0, &found_devices);
if(res != icsneoc2_error_success)
return print_error_code("Failed to enumerate devices", res);
if(found_devices == NULL) {
fprintf(stderr, "error: no devices found\n");
return 1;
}
if(args.list) {
int rc = list_devices(found_devices);
icsneoc2_enumeration_free(found_devices);
return rc;
}
icsneoc2_device_info_t* info = find_device(found_devices, args.serial);
if(info == NULL) {
fprintf(stderr, "error: unable to find device %s\n", args.serial ? args.serial : "(any)");
icsneoc2_enumeration_free(found_devices);
return 1;
}
char description[256] = {0};
size_t description_len = sizeof(description);
icsneoc2_device_info_description_get(info, description, &description_len);
icsneoc2_device_t* device = NULL;
res = icsneoc2_device_create(info, &device);
if(res != icsneoc2_error_success) {
icsneoc2_enumeration_free(found_devices);
return print_error_code("Failed to create device", res);
}
printf("Opening %s\n", description);
res = icsneoc2_device_open(device, icsneoc2_open_options_default);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("Failed to open device", res);
}
bool supported = false;
icsneoc2_device_supports_gptp(device, &supported);
if(!supported) {
fprintf(stderr, "error: device does not support gPTP (%s)\n", description);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
icsneoc2_enumeration_free(found_devices);
return 1;
}
int rc = configure_device(device);
printf("Closing %s\n", description);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
icsneoc2_enumeration_free(found_devices);
return rc;
}
+3
View File
@@ -86,6 +86,8 @@ void process_lin_messages(icsneoc2_message_t** messages, size_t count) {
icsneoc2_netid_t netid = 0; icsneoc2_netid_t netid = 0;
icsneoc2_message_netid_get(messages[i], &netid); icsneoc2_message_netid_get(messages[i], &netid);
uint64_t timestamp = 0;
icsneoc2_message_timestamp_get(messages[i], &timestamp);
char netid_name[128] = {0}; char netid_name[128] = {0};
size_t netid_name_length = 128; size_t netid_name_length = 128;
icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); icsneoc2_netid_name_get(netid, netid_name, &netid_name_length);
@@ -98,6 +100,7 @@ void process_lin_messages(icsneoc2_message_t** messages, size_t count) {
icsneoc2_message_lin_err_flags_get(messages[i], &err_flags); icsneoc2_message_lin_err_flags_get(messages[i], &err_flags);
printf("\t%s RX | ID: 0x%02x | Protected ID: 0x%02x\n", netid_name, id, protected_id); printf("\t%s RX | ID: 0x%02x | Protected ID: 0x%02x\n", netid_name, id, protected_id);
printf("\tTimestamp: %" PRIu64 " ns since 2007-01-01 UTC\n", timestamp);
printf("\tData: ["); printf("\tData: [");
for(size_t x = 0; x < data_length; x++) { for(size_t x = 0; x < data_length; x++) {
printf(" 0x%02x", data[x]); printf(" 0x%02x", data[x]);
+6
View File
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-perf_test-example src/main.c)
target_link_libraries(libicsneoc2-perf_test-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-perf_test-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+137
View File
@@ -0,0 +1,137 @@
/*
* PerfTest setting example.
*
* Opens the first available device, enables the device-global PerfTest mode
* via icsneoc2_settings_perf_test_enabled_set(), reads messages for a few
* seconds while PerfTest is active, then disables PerfTest again. Each step
* reads the value back with icsneoc2_settings_perf_test_enabled_get() to
* confirm the change took effect.
*/
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2settings.h>
#include <icsneo/icsneoc2messages.h>
#include <stdbool.h>
#include <stdio.h>
#include <time.h>
static int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64] = {0};
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
fprintf(stderr, "%s: failed to get string for error code %u\n", message, error);
return (int)res;
}
fprintf(stderr, "%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
/* Sets PerfTest, applies it to the device, then reads it back to confirm. */
static icsneoc2_error_t set_and_verify_perf_test(icsneoc2_device_t* device, bool enable) {
icsneoc2_error_t res = icsneoc2_settings_perf_test_enabled_set(device, enable);
if(res != icsneoc2_error_success)
return res;
res = icsneoc2_settings_apply(device);
if(res != icsneoc2_error_success)
return res;
/* Re-read settings from the device so the read-back reflects what was
* actually persisted, not just the local settings buffer. */
res = icsneoc2_settings_refresh(device);
if(res != icsneoc2_error_success)
return res;
bool value = !enable;
res = icsneoc2_settings_perf_test_enabled_get(device, &value);
if(res != icsneoc2_error_success)
return res;
printf("\tPerfTest now reads back as: %s\n", value ? "enabled" : "disabled");
if(value != enable) {
fprintf(stderr, "\tERROR: expected PerfTest %s but device reports %s\n",
enable ? "enabled" : "disabled", value ? "enabled" : "disabled");
return icsneoc2_error_get_settings_failure;
}
return icsneoc2_error_success;
}
/* Continuously drains and counts messages for the given wall-clock duration. */
static icsneoc2_error_t read_messages_for(icsneoc2_device_t* device, unsigned seconds) {
size_t total = 0;
printf("\tReading messages for %u seconds...\n", seconds);
time_t start = time(NULL);
while((time_t)(time(NULL) - start) < (time_t)seconds) {
/* Short timeout so an idle bus still lets us re-check the clock,
* while a flooding bus is drained as fast as messages arrive. */
icsneoc2_message_t* message = NULL;
icsneoc2_error_t res = icsneoc2_device_message_get(device, &message, 50);
if(res != icsneoc2_error_success)
return res;
if(message == NULL)
continue;
++total;
icsneoc2_message_free(message);
}
printf("\tReceived %zu messages in %u seconds.\n", total, seconds);
return icsneoc2_error_success;
}
int main(void) {
printf("Opening first available device...\n");
icsneoc2_device_t* device = NULL;
icsneoc2_error_t res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device);
if(res != icsneoc2_error_success)
return print_error_code("\tFailed to open first device", res);
char description[255] = {0};
size_t description_length = sizeof(description);
res = icsneoc2_device_description_get(device, description, &description_length);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(device);
return print_error_code("\tFailed to get device description", res);
}
printf("\tOpened device: %s\n", description);
/* Pull the current settings down from the device before reading/modifying them. */
res = icsneoc2_settings_refresh(device);
if(res != icsneoc2_error_success)
goto cleanup;
bool initial = false;
res = icsneoc2_settings_perf_test_enabled_get(device, &initial);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to read initial PerfTest state (device may not support it)", res);
goto cleanup;
}
printf("\tInitial PerfTest state: %s\n", initial ? "enabled" : "disabled");
printf("Enabling PerfTest...\n");
res = set_and_verify_perf_test(device, true);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to enable PerfTest", res);
goto cleanup;
}
res = read_messages_for(device, 3);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed while reading messages", res);
goto cleanup;
}
printf("Disabling PerfTest...\n");
res = set_and_verify_perf_test(device, false);
if(res != icsneoc2_error_success) {
print_error_code("\tFailed to disable PerfTest", res);
goto cleanup;
}
printf("PerfTest enable/read/disable cycle completed successfully.\n");
cleanup:
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return (int)res;
}
+14 -2
View File
@@ -176,6 +176,7 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) {
// Print the type and bus type of each message // Print the type and bus type of each message
size_t tx_count = 0; size_t tx_count = 0;
size_t can_error_count = 0; size_t can_error_count = 0;
size_t internal_count = 0;
icsneoc2_error_t res = icsneoc2_error_success; icsneoc2_error_t res = icsneoc2_error_success;
for(size_t i = 0; i < messages_count; i++) { for(size_t i = 0; i < messages_count; i++) {
icsneoc2_message_t* message = messages[i]; icsneoc2_message_t* message = messages[i];
@@ -216,13 +217,21 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) {
continue; continue;
} }
// This example only cares about bus traffic, so skip internal
// device/status messages.
icsneoc2_network_type_t internal_check = icsneoc2_network_type_invalid;
res = icsneoc2_message_network_type_get(message, &internal_check);
if(res == icsneoc2_error_success && internal_check == icsneoc2_network_type_internal) {
internal_count++;
continue;
}
bool is_frame = false; bool is_frame = false;
res = icsneoc2_message_is_frame(message, &is_frame); res = icsneoc2_message_is_frame(message, &is_frame);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to check if message is a frame", res); return print_error_code("\tFailed to check if message is a frame", res);
} }
if(!is_frame) { if(!is_frame) {
printf("Ignoring non-frame message at index %zu\n", i);
continue; continue;
} }
icsneoc2_network_type_t network_type; icsneoc2_network_type_t network_type;
@@ -244,6 +253,7 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) {
printf("\t%zd) network type: %s (%u)\n", i, network_type_name, network_type); printf("\t%zd) network type: %s (%u)\n", i, network_type_name, network_type);
if(network_type == icsneoc2_network_type_can) { if(network_type == icsneoc2_network_type_can) {
uint64_t timestamp = 0;
uint64_t arbid = 0; uint64_t arbid = 0;
int32_t dlc = 0; int32_t dlc = 0;
icsneoc2_netid_t netid = 0; icsneoc2_netid_t netid = 0;
@@ -256,6 +266,7 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) {
bool is_tx = false; bool is_tx = false;
icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid); icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid);
result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length);
result += icsneoc2_message_timestamp_get(message, &timestamp);
result += icsneoc2_message_can_props_get(message, &arbid, &can_flags); result += icsneoc2_message_can_props_get(message, &arbid, &can_flags);
result += icsneoc2_message_data_get(message, data, &data_length); result += icsneoc2_message_data_get(message, data, &data_length);
result += icsneoc2_message_is_transmit(message, &is_tx); result += icsneoc2_message_is_transmit(message, &is_tx);
@@ -276,6 +287,7 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) {
dlc = (int32_t)data_length; dlc = (int32_t)data_length;
printf("\t %s%s\n", is_tx ? "TX" : "RX", is_error ? " [Error]" : ""); printf("\t %s%s\n", is_tx ? "TX" : "RX", is_error ? " [Error]" : "");
printf("\t Timestamp: %" PRIu64 " ns since 2007-01-01 UTC\n", timestamp);
printf("\t NetID: %s (0x%x)\tArbID: 0x%llx\tDLC: %u\tLen: %zu\n", netid_name, netid, (unsigned long long)arbid, dlc, data_length); printf("\t NetID: %s (0x%x)\tArbID: 0x%llx\tDLC: %u\tLen: %zu\n", netid_name, netid, (unsigned long long)arbid, dlc, data_length);
printf("\t Flags:%s%s%s%s%s%s%s%s\n", printf("\t Flags:%s%s%s%s%s%s%s%s\n",
is_remote ? " RTR" : "", is_remote ? " RTR" : "",
@@ -293,7 +305,7 @@ int process_message(icsneoc2_message_t** messages, size_t messages_count) {
printf(" ]\n"); printf(" ]\n");
} }
} }
printf("\tReceived %zu messages total, %zu were TX messages, %zu were CAN errors\n", messages_count, tx_count, can_error_count); printf("\tReceived %zu messages total, %zu were TX messages, %zu were CAN errors, %zu internal (skipped)\n", messages_count, tx_count, can_error_count, internal_count);
return icsneoc2_error_success; return icsneoc2_error_success;
} }
+36 -2
View File
@@ -359,6 +359,8 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) {
// Print the type and bus type of each message // Print the type and bus type of each message
size_t tx_count = 0; size_t tx_count = 0;
size_t can_error_count = 0; size_t can_error_count = 0;
size_t app_error_count = 0;
size_t internal_count = 0;
for(size_t i = 0; i < messages_count; i++) { for(size_t i = 0; i < messages_count; i++) {
icsneoc2_message_t* message = messages[i]; icsneoc2_message_t* message = messages[i];
@@ -392,13 +394,42 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) {
continue; continue;
} }
// Check for application error messages
bool is_app_error = false;
res = icsneoc2_message_is_app_error(message, &is_app_error);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to check if message is an app error", res);
}
if(is_app_error) {
icsneoc2_app_error_type_t error_type = icsneoc2_app_error_type_no_error;
icsneoc2_netid_t error_netid = 0;
char description[256] = {0};
size_t description_length = sizeof(description);
res = icsneoc2_message_app_error_props_get(message, &error_type, &error_netid);
res += icsneoc2_message_app_error_string_get(message, description, &description_length);
if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to get app error properties", res);
}
printf("\t%zd) App Error (type %u) on netid 0x%x: %s\n", i, error_type, error_netid, description);
app_error_count++;
continue;
}
// This example only cares about bus traffic, so skip internal
// device/status messages.
icsneoc2_network_type_t internal_check = icsneoc2_network_type_invalid;
res = icsneoc2_message_network_type_get(message, &internal_check);
if(res == icsneoc2_error_success && internal_check == icsneoc2_network_type_internal) {
internal_count++;
continue;
}
bool is_frame = false; bool is_frame = false;
res = icsneoc2_message_is_frame(message, &is_frame); res = icsneoc2_message_is_frame(message, &is_frame);
if(res != icsneoc2_error_success) { if(res != icsneoc2_error_success) {
return print_error_code("\tFailed to check if message is a frame", res); return print_error_code("\tFailed to check if message is a frame", res);
} }
if(!is_frame) { if(!is_frame) {
printf("Ignoring non-frame message at index %zu\n", i);
continue; continue;
} }
icsneoc2_network_type_t network_type; icsneoc2_network_type_t network_type;
@@ -426,6 +457,7 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) {
printf("\t%zd) network type: %s (%u)\n", i, network_type_name, network_type); printf("\t%zd) network type: %s (%u)\n", i, network_type_name, network_type);
if(network_type == icsneoc2_network_type_can) { if(network_type == icsneoc2_network_type_can) {
uint64_t timestamp = 0;
uint64_t arbid = 0; uint64_t arbid = 0;
int32_t dlc = 0; int32_t dlc = 0;
icsneoc2_netid_t netid = 0; icsneoc2_netid_t netid = 0;
@@ -436,6 +468,7 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) {
size_t netid_name_length = 128; size_t netid_name_length = 128;
icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid); icsneoc2_error_t result = icsneoc2_message_netid_get(message, &netid);
result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length); result += icsneoc2_netid_name_get(netid, netid_name, &netid_name_length);
result += icsneoc2_message_timestamp_get(message, &timestamp);
result += icsneoc2_message_can_props_get(message, &arbid, &can_flags); result += icsneoc2_message_can_props_get(message, &arbid, &can_flags);
result += icsneoc2_message_data_get(message, data, &data_length); result += icsneoc2_message_data_get(message, data, &data_length);
if(result != icsneoc2_error_success) { if(result != icsneoc2_error_success) {
@@ -452,6 +485,7 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) {
bool tx_error = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR) != 0; bool tx_error = (can_flags & ICSNEOC2_MESSAGE_CAN_FLAGS_TX_ERROR) != 0;
dlc = (int32_t)data_length; dlc = (int32_t)data_length;
printf("\t Timestamp: %" PRIu64 " ns since 2007-01-01 UTC\n", timestamp);
printf("\t NetID: %s (0x%x)\tArbID: 0x%llx\tDLC: %u\tLen: %zu\n", netid_name, netid, (unsigned long long)arbid, dlc, data_length); printf("\t NetID: %s (0x%x)\tArbID: 0x%llx\tDLC: %u\tLen: %zu\n", netid_name, netid, (unsigned long long)arbid, dlc, data_length);
printf("\t Flags:%s%s%s%s%s%s%s%s\n", printf("\t Flags:%s%s%s%s%s%s%s%s\n",
is_remote ? " RTR" : "", is_remote ? " RTR" : "",
@@ -469,7 +503,7 @@ int process_messages(icsneoc2_message_t** messages, size_t messages_count) {
printf(" ]\n"); printf(" ]\n");
} }
} }
printf("\tReceived %zu messages total, %zu were TX messages, %zu were CAN errors\n", messages_count, tx_count, can_error_count); printf("\tReceived %zu messages total, %zu were TX messages, %zu were CAN errors, %zu were app errors, %zu internal (skipped)\n", messages_count, tx_count, can_error_count, app_error_count, internal_count);
return icsneoc2_error_success; return icsneoc2_error_success;
} }
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-supported-devices-example src/main.c)
target_link_libraries(libicsneoc2-supported-devices-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-supported-devices-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+55
View File
@@ -0,0 +1,55 @@
#include <icsneo/icsneoc2.h>
#include <stdio.h>
static int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64] = {0};
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: failed to get string for error code %u\n", message, error);
return (int)res;
}
printf("%s: %s (%u)\n", message, error_str, error);
return (int)error;
}
int main(void) {
icsneoc2_supported_device_t* supported_devices = NULL;
icsneoc2_error_t res = icsneoc2_supported_devices_enumerate(&supported_devices);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to enumerate supported devices", res);
}
if(!supported_devices) {
printf("No supported devices found.\n");
return 0;
}
printf("Supported devices:\n");
for(icsneoc2_supported_device_t* cur = supported_devices; cur != NULL; cur = icsneoc2_supported_devices_next(cur)) {
icsneoc2_devicetype_t device_type = 0;
res = icsneoc2_supported_device_get(cur, &device_type);
if(res != icsneoc2_error_success) {
icsneoc2_supported_devices_free(supported_devices);
return print_error_code("Failed to get supported device type", res);
}
char name[128] = {0};
size_t name_len = sizeof(name);
res = icsneoc2_device_type_name_get(device_type, name, &name_len);
if(res != icsneoc2_error_success) {
icsneoc2_supported_devices_free(supported_devices);
return print_error_code("Failed to get supported device type name", res);
}
printf(" %s (%u)\n", name, (unsigned int)device_type);
}
res = icsneoc2_supported_devices_free(supported_devices);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to free supported device list", res);
}
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-t1s-loopback-example src/main.c)
target_link_libraries(libicsneoc2-t1s-loopback-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-t1s-loopback-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+647
View File
@@ -0,0 +1,647 @@
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2messages.h>
#include <icsneo/icsneoc2settings.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#define TX_LOCAL_ID 0u
#define RX_LOCAL_ID 1u
#define T1S_MAX_NODES 8u
#define T1S_TX_OPP_TIMER 20u
#define T1S_BURST_TIMER 64u
#define T1S_MAX_BURST 128u
#define LOOPBACK_ETHER_TYPE 0x9000u
#define LOOPBACK_FRAME_SIZE 60u
typedef struct selectable_network {
icsneoc2_netid_t netid;
char name[64];
} selectable_network_t;
/* Sleep for a short period while waiting for the device to apply settings. */
static void sleep_ms(uint32_t ms);
/* Print a readable error string and return the same failure code to the caller. */
static int print_error_code(const char* message, icsneoc2_error_t error);
/* Drain and print queued library events when the example encounters an error. */
static void print_events(void);
/* Convert a netid to a readable name such as "AE 02". */
static int get_netid_name(icsneoc2_netid_t netid, char* buffer, size_t buffer_size);
/* Gather the device's TX and RX networks, keeping only automotive Ethernet ports. */
static int get_available_networks(const icsneoc2_device_t* device,
selectable_network_t* tx_networks,
size_t* tx_count,
selectable_network_t* rx_networks,
size_t* rx_count);
/* Prompt the user to choose one TX or RX network from the filtered list. */
static int prompt_for_network_selection(const char* label, const selectable_network_t* networks, size_t count, icsneoc2_netid_t* selected_netid, char* selected_name, size_t selected_name_size);
/* Apply the small set of T1S settings needed for this two-port loopback example. */
static int configure_t1s_port(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t local_id);
/* Print a MAC address in a compact human-readable form. */
static void print_mac(const char* label, const uint8_t* mac);
/* Print payload bytes as hex for the TX echo and RX frame output. */
static void print_payload_hex(const uint8_t* data, size_t length);
/* Build one recognizable Ethernet frame that both transmit and receive paths share. */
static void build_loopback_frame(uint8_t* frame_data, size_t frame_size, const char* tx_name, const char* rx_name);
/* Check whether a received Ethernet frame matches the loopback frame this example sent. */
static int message_matches_loopback_frame(icsneoc2_message_t* message, const uint8_t* expected, size_t expected_length, bool* matches);
/* Print the key details of an Ethernet message found during the loopback test. */
static int print_ethernet_message(icsneoc2_message_t* message, const char* direction_label);
/* Transmit the loopback Ethernet frame on the selected TX port. */
static int transmit_loopback_frame(icsneoc2_device_t* device, icsneoc2_netid_t tx_netid, const char* tx_name, const char* rx_name);
/* Poll until the example sees both the TX echo and the matching RX frame. */
static int poll_for_loopback_messages(icsneoc2_device_t* device,
icsneoc2_netid_t tx_netid,
icsneoc2_netid_t rx_netid,
const char* tx_name,
const char* rx_name,
const uint8_t* expected_frame,
size_t expected_frame_length);
int main(void) {
icsneoc2_device_t* device = NULL;
icsneoc2_error_t res;
char description[255] = {0};
char serial[64] = {0};
size_t description_length = sizeof(description);
size_t serial_length = sizeof(serial);
selectable_network_t available_tx_networks[128] = {0};
selectable_network_t available_rx_networks[128] = {0};
size_t available_tx_count = 0;
size_t available_rx_count = 0;
icsneoc2_netid_t tx_netid = 0;
icsneoc2_netid_t rx_netid = 0;
uint8_t expected_frame[LOOPBACK_FRAME_SIZE] = {0};
char tx_name[64] = {0};
char rx_name[64] = {0};
int status = 1;
printf("RAD-Comet3 C2 T1S loopback example\n");
printf("Opening first available RAD-Comet3...\n");
res = icsneoc2_device_open_first(icsneoc2_devicetype_rad_comet3, icsneoc2_open_options_default, &device);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to open a RAD-Comet3", res);
}
res = icsneoc2_device_description_get(device, description, &description_length);
if(res != icsneoc2_error_success) {
print_error_code("Failed to get device description", res);
goto cleanup;
}
res = icsneoc2_device_serial_get(device, serial, &serial_length);
if(res != icsneoc2_error_success) {
print_error_code("Failed to get device serial", res);
goto cleanup;
}
printf("Opened device: %s [%s]\n", description, serial);
if(get_available_networks(device, available_tx_networks, &available_tx_count, available_rx_networks, &available_rx_count) != 0) {
goto cleanup;
}
if(prompt_for_network_selection("TX", available_tx_networks, available_tx_count, &tx_netid, tx_name, sizeof(tx_name)) != 0) {
goto cleanup;
}
if(prompt_for_network_selection("RX", available_rx_networks, available_rx_count, &rx_netid, rx_name, sizeof(rx_name)) != 0) {
goto cleanup;
}
printf("Selected loopback wiring: %s connected to %s\n", tx_name, rx_name);
if(tx_netid == rx_netid) {
printf("TX and RX networks are the same. This example is intended for a physical loopback between two ports.\n");
goto cleanup;
}
// Use the same expected frame bytes for transmit and for receive-side matching.
build_loopback_frame(expected_frame, sizeof(expected_frame), tx_name, rx_name);
res = icsneoc2_settings_refresh(device);
if(res != icsneoc2_error_success) {
print_error_code("Failed to refresh device settings", res);
goto cleanup;
}
if(configure_t1s_port(device, tx_netid, TX_LOCAL_ID) != 0) {
goto cleanup;
}
if(configure_t1s_port(device, rx_netid, RX_LOCAL_ID) != 0) {
goto cleanup;
}
printf("Applying T1S settings to the device.\n");
printf("Note: icsneoc2_settings_apply() persists these settings on the device.\n");
res = icsneoc2_settings_apply(device);
if(res != icsneoc2_error_success) {
print_error_code("Failed to apply T1S settings", res);
goto cleanup;
}
sleep_ms(500);
if(transmit_loopback_frame(device, tx_netid, tx_name, rx_name) != 0) {
goto cleanup;
}
if(poll_for_loopback_messages(device, tx_netid, rx_netid, tx_name, rx_name, expected_frame, sizeof(expected_frame)) != 0) {
print_events();
goto cleanup;
}
status = 0;
cleanup:
if(device != NULL) {
res = icsneoc2_device_close(device);
if(res != icsneoc2_error_success) {
(void)print_error_code("Failed to close device", res);
}
res = icsneoc2_device_free(device);
if(res != icsneoc2_error_success) {
(void)print_error_code("Failed to free device", res);
}
}
return status;
}
static void sleep_ms(uint32_t ms) {
#ifdef _WIN32
Sleep(ms);
#else
usleep(ms * 1000);
#endif
}
static int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64] = {0};
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: failed to get string for error code %u with error code %u\n", message, error, res);
return (int)res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
static void print_events(void) {
icsneoc2_event_t* events[64] = {0};
size_t count = sizeof(events) / sizeof(events[0]);
for(size_t i = 0; i < count; ++i) {
icsneoc2_error_t res = icsneoc2_event_get(&events[i], NULL);
if(res != icsneoc2_error_success) {
(void)print_error_code("Failed to get events", res);
return;
}
if(events[i] == NULL) {
count = i;
break;
}
}
for(size_t i = 0; i < count; ++i) {
char description[255] = {0};
size_t description_length = sizeof(description);
icsneoc2_error_t res = icsneoc2_event_description_get(events[i], description, &description_length);
if(res == icsneoc2_error_success) {
printf("Event %zu: %s\n", i, description);
}
icsneoc2_event_free(events[i]);
}
}
static int get_netid_name(icsneoc2_netid_t netid, char* buffer, size_t buffer_size) {
size_t length = buffer_size;
icsneoc2_error_t res = icsneoc2_netid_name_get(netid, buffer, &length);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to get netid name", res);
}
return 0;
}
static int get_available_networks(const icsneoc2_device_t* device,
selectable_network_t* tx_networks,
size_t* tx_count,
selectable_network_t* rx_networks,
size_t* rx_count) {
icsneoc2_netid_t supported_tx_networks[128] = {0};
icsneoc2_netid_t supported_rx_networks[128] = {0};
size_t tx_supported_count = sizeof(supported_tx_networks) / sizeof(supported_tx_networks[0]);
size_t rx_supported_count = sizeof(supported_rx_networks) / sizeof(supported_rx_networks[0]);
icsneoc2_message_t* probe = NULL;
icsneoc2_network_type_t network_type = 0;
icsneoc2_error_t res = icsneoc2_device_supported_tx_networks_get(device, supported_tx_networks, &tx_supported_count);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to get supported TX networks", res);
}
res = icsneoc2_device_supported_rx_networks_get(device, supported_rx_networks, &rx_supported_count);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to get supported RX networks", res);
}
*tx_count = 0;
*rx_count = 0;
// Reuse one temporary Ethernet message to classify each netid by network type.
res = icsneoc2_message_eth_create(&probe);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to create temporary Ethernet message", res);
}
for(size_t i = 0; i < tx_supported_count; ++i) {
res = icsneoc2_message_netid_set(probe, supported_tx_networks[i]);
if(res != icsneoc2_error_success) {
continue;
}
res = icsneoc2_message_network_type_get(probe, &network_type);
if(res != icsneoc2_error_success || network_type != icsneoc2_network_type_automotive_ethernet) {
continue;
}
tx_networks[*tx_count].netid = supported_tx_networks[i];
if(get_netid_name(supported_tx_networks[i], tx_networks[*tx_count].name, sizeof(tx_networks[*tx_count].name)) != 0) {
icsneoc2_message_free(probe);
return 1;
}
(*tx_count)++;
}
for(size_t i = 0; i < rx_supported_count; ++i) {
res = icsneoc2_message_netid_set(probe, supported_rx_networks[i]);
if(res != icsneoc2_error_success) {
continue;
}
res = icsneoc2_message_network_type_get(probe, &network_type);
if(res != icsneoc2_error_success || network_type != icsneoc2_network_type_automotive_ethernet) {
continue;
}
rx_networks[*rx_count].netid = supported_rx_networks[i];
if(get_netid_name(supported_rx_networks[i], rx_networks[*rx_count].name, sizeof(rx_networks[*rx_count].name)) != 0) {
icsneoc2_message_free(probe);
return 1;
}
(*rx_count)++;
}
icsneoc2_message_free(probe);
if(*tx_count == 0) {
printf("No automotive Ethernet TX networks are available on this device.\n");
return 1;
}
if(*rx_count == 0) {
printf("No automotive Ethernet RX networks are available on this device.\n");
return 1;
}
return 0;
}
static int prompt_for_network_selection(const char* label, const selectable_network_t* networks, size_t count, icsneoc2_netid_t* selected_netid, char* selected_name, size_t selected_name_size) {
char input[32] = {0};
char* end_ptr = NULL;
long selected_index = 0;
if(!label || !networks || count == 0 || !selected_netid || !selected_name || selected_name_size == 0) {
return print_error_code("Invalid network selection parameters", icsneoc2_error_invalid_parameters);
}
printf("Available automotive Ethernet %s networks:\n", label);
for(size_t i = 0; i < count; ++i) {
printf(" %zu) %s\n", i + 1, networks[i].name);
}
printf("Select %s network [1-%zu, default 1]: ", label, count);
if(fgets(input, sizeof(input), stdin) == NULL || input[0] == '\n') {
selected_index = 1;
} else {
selected_index = strtol(input, &end_ptr, 10);
if(end_ptr == input || selected_index < 1 || (size_t)selected_index > count) {
printf("Invalid selection, using %s.\n", networks[0].name);
selected_index = 1;
}
}
*selected_netid = networks[selected_index - 1].netid;
strncpy(selected_name, networks[selected_index - 1].name, selected_name_size - 1);
selected_name[selected_name_size - 1] = '\0';
return 0;
}
static int configure_t1s_port(icsneoc2_device_t* device, icsneoc2_netid_t netid, uint8_t local_id) {
char netid_name[64] = {0};
icsneoc2_error_t res;
bool termination = false;
if(get_netid_name(netid, netid_name, sizeof(netid_name)) != 0) {
return 1;
}
printf("Configuring %s: PLCA on, LocalID=%u, MaxNodes=%u, TxOpp=%u, BurstTimer=%u, MaxBurst=%u\n",
netid_name,
(unsigned)local_id,
(unsigned)T1S_MAX_NODES,
(unsigned)T1S_TX_OPP_TIMER,
(unsigned)T1S_BURST_TIMER,
(unsigned)T1S_MAX_BURST);
// Keep the example explicit about the small set of T1S settings needed for loopback.
res = icsneoc2_settings_t1s_plca_enabled_for_set(device, netid, true);
if(res != icsneoc2_error_success) return print_error_code("Failed to enable T1S PLCA", res);
res = icsneoc2_settings_t1s_local_id_set(device, netid, local_id);
if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S local ID", res);
res = icsneoc2_settings_t1s_max_nodes_set(device, netid, T1S_MAX_NODES);
if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S max nodes", res);
res = icsneoc2_settings_t1s_tx_opp_timer_set(device, netid, T1S_TX_OPP_TIMER);
if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S TX opportunity timer", res);
res = icsneoc2_settings_t1s_burst_timer_set(device, netid, T1S_BURST_TIMER);
if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S burst timer", res);
res = icsneoc2_settings_t1s_max_burst_timer_for_set(device, netid, T1S_MAX_BURST);
if(res != icsneoc2_error_success) return print_error_code("Failed to set T1S max burst", res);
res = icsneoc2_settings_t1s_is_termination_enabled_for(device, netid, &termination);
if(res == icsneoc2_error_success) {
res = icsneoc2_settings_t1s_termination_for_set(device, netid, true);
if(res != icsneoc2_error_success) return print_error_code("Failed to enable T1S termination", res);
} else if(res != icsneoc2_error_get_settings_failure) {
return print_error_code("Failed to query T1S termination support", res);
}
return 0;
}
static void print_mac(const char* label, const uint8_t* mac) {
printf("%s %02x:%02x:%02x:%02x:%02x:%02x", label, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
static void print_payload_hex(const uint8_t* data, size_t length) {
for(size_t i = 0; i < length; ++i) {
printf("%02x", data[i]);
if(i + 1 < length) {
printf(" ");
}
}
printf("\n");
}
static void build_loopback_frame(uint8_t* frame_data, size_t frame_size, const char* tx_name, const char* rx_name) {
const char* tx_label = tx_name ? tx_name : "TX";
const char* rx_label = rx_name ? rx_name : "RX";
// Build one recognizable Ethernet frame so the receive side can match exactly what we sent.
memset(frame_data, 0, frame_size);
frame_data[0] = 0x00;
frame_data[1] = 0xFC;
frame_data[2] = 0x70;
frame_data[3] = 0x00;
frame_data[4] = 0x00;
frame_data[5] = 0x02;
frame_data[6] = 0x00;
frame_data[7] = 0xFC;
frame_data[8] = 0x70;
frame_data[9] = 0x00;
frame_data[10] = 0x00;
frame_data[11] = 0x01;
frame_data[12] = (uint8_t)((LOOPBACK_ETHER_TYPE >> 8) & 0xFF);
frame_data[13] = (uint8_t)(LOOPBACK_ETHER_TYPE & 0xFF);
snprintf((char*)&frame_data[14], frame_size - 14, "C2 T1S loopback %s->%s", tx_label, rx_label);
}
static int message_matches_loopback_frame(icsneoc2_message_t* message, const uint8_t* expected, size_t expected_length, bool* matches) {
uint8_t data[1600] = {0};
size_t data_length = sizeof(data);
uint16_t ether_type = 0;
icsneoc2_error_t res;
if(!expected || !matches || expected_length < 14) {
return print_error_code("Invalid loopback match output", icsneoc2_error_invalid_parameters);
}
*matches = false;
res = icsneoc2_message_eth_ether_type_get(message, &ether_type);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to read loopback EtherType", res);
}
if(ether_type != LOOPBACK_ETHER_TYPE) {
return 0;
}
res = icsneoc2_message_data_get(message, data, &data_length);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to read loopback frame data", res);
}
if(data_length < 14) {
return 0;
}
if(data_length >= expected_length) {
*matches = memcmp(data, expected, expected_length) == 0;
} else {
*matches = memcmp(data, expected, data_length) == 0;
}
return 0;
}
static int print_ethernet_message(icsneoc2_message_t* message, const char* direction_label) {
icsneoc2_netid_t netid = 0;
uint64_t timestamp = 0;
char netid_name[64] = {0};
uint8_t dst_mac[6] = {0};
uint8_t src_mac[6] = {0};
uint16_t ether_type = 0;
icsneoc2_message_eth_t1s_flags_t t1s_flags = 0;
uint8_t node_id = 0;
uint8_t burst_count = 0;
uint8_t symbol_type = 0;
uint8_t data[1600] = {0};
size_t data_length = sizeof(data);
icsneoc2_error_t res;
res = icsneoc2_message_netid_get(message, &netid);
if(res != icsneoc2_error_success) return print_error_code("Failed to get message netid", res);
if(get_netid_name(netid, netid_name, sizeof(netid_name)) != 0) return 1;
res = icsneoc2_message_timestamp_get(message, &timestamp);
if(res != icsneoc2_error_success) return print_error_code("Failed to get message timestamp", res);
res = icsneoc2_message_data_get(message, data, &data_length);
if(res != icsneoc2_error_success) return print_error_code("Failed to get Ethernet data", res);
printf("%s on %s: %zu bytes\n", direction_label, netid_name, data_length);
printf(" Timestamp: %" PRIu64 " ns since 2007-01-01 UTC\n", timestamp);
res = icsneoc2_message_eth_mac_get(message, dst_mac, src_mac);
if(res == icsneoc2_error_success) {
print_mac(" Dst", dst_mac);
printf(" ");
print_mac("Src", src_mac);
printf("\n");
}
res = icsneoc2_message_eth_ether_type_get(message, &ether_type);
if(res == icsneoc2_error_success) {
printf(" EtherType: 0x%04x\n", ether_type);
}
res = icsneoc2_message_eth_t1s_props_get(message, &t1s_flags, &node_id, &burst_count, &symbol_type);
if(res == icsneoc2_error_success && (t1s_flags != 0 || node_id != 0 || burst_count != 0 || symbol_type != 0)) {
printf(" T1S: node=%u burst=%u symbol=%u", (unsigned)node_id, (unsigned)burst_count, (unsigned)symbol_type);
if(t1s_flags & ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_SYMBOL) printf(" [SYMBOL]");
if(t1s_flags & ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_BURST) printf(" [BURST]");
if(t1s_flags & ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_TX_COLLISION) printf(" [TX_COLLISION]");
if(t1s_flags & ICSNEOC2_MESSAGE_ETH_T1S_FLAGS_IS_T1S_WAKE) printf(" [WAKE]");
printf("\n");
}
printf(" Payload bytes: ");
if(data_length > 14) {
print_payload_hex(&data[14], data_length - 14);
} else {
printf("<none>\n");
}
return 0;
}
static int transmit_loopback_frame(icsneoc2_device_t* device, icsneoc2_netid_t tx_netid, const char* tx_name, const char* rx_name) {
icsneoc2_message_t* message = NULL;
icsneoc2_error_t res;
uint8_t frame_data[LOOPBACK_FRAME_SIZE] = {0};
build_loopback_frame(frame_data, sizeof(frame_data), tx_name, rx_name);
res = icsneoc2_message_eth_create(&message);
if(res != icsneoc2_error_success) return print_error_code("Failed to create Ethernet message", res);
res = icsneoc2_message_netid_set(message, tx_netid);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
return print_error_code("Failed to set Ethernet netid", res);
}
res = icsneoc2_message_data_set(message, frame_data, sizeof(frame_data));
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
return print_error_code("Failed to set Ethernet payload", res);
}
printf("Transmitting one T1S loopback frame on %s...\n", tx_name ? tx_name : "selected TX network");
res = icsneoc2_device_message_transmit(device, message);
icsneoc2_message_free(message);
if(res != icsneoc2_error_success) return print_error_code("Failed to transmit loopback frame", res);
return 0;
}
static int poll_for_loopback_messages(icsneoc2_device_t* device,
icsneoc2_netid_t tx_netid,
icsneoc2_netid_t rx_netid,
const char* tx_name,
const char* rx_name,
const uint8_t* expected_frame,
size_t expected_frame_length) {
bool saw_tx_echo = false;
bool saw_rx_frame = false;
printf("Polling for TX echo on %s and RX frame on %s...\n", tx_name, rx_name);
for(size_t attempt = 0; attempt < 60 && !(saw_tx_echo && saw_rx_frame); ++attempt) {
icsneoc2_message_t* message = NULL;
icsneoc2_error_t res = icsneoc2_device_message_get(device, &message, 100);
if(res != icsneoc2_error_success) {
return print_error_code("Failed while polling for loopback messages", res);
}
if(message == NULL) {
continue;
}
bool is_ethernet = false;
bool matches_loopback = false;
res = icsneoc2_message_is_ethernet(message, &is_ethernet);
if(res != icsneoc2_error_success || !is_ethernet) {
icsneoc2_message_free(message);
continue;
}
icsneoc2_netid_t netid = 0;
res = icsneoc2_message_netid_get(message, &netid);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
return print_error_code("Failed to get polled netid", res);
}
if(netid != tx_netid && netid != rx_netid) {
icsneoc2_message_free(message);
continue;
}
// Ignore unrelated traffic on the selected ports and only count the frame this example transmitted.
if(message_matches_loopback_frame(message, expected_frame, expected_frame_length, &matches_loopback) != 0) {
icsneoc2_message_free(message);
return 1;
}
if(!matches_loopback) {
icsneoc2_message_free(message);
continue;
}
bool is_tx = false;
res = icsneoc2_message_is_transmit(message, &is_tx);
if(res != icsneoc2_error_success) {
icsneoc2_message_free(message);
return print_error_code("Failed to determine TX status", res);
}
if(netid == tx_netid && is_tx && !saw_tx_echo) {
if(print_ethernet_message(message, "TX echo") != 0) {
icsneoc2_message_free(message);
return 1;
}
saw_tx_echo = true;
} else if(netid == rx_netid && !saw_rx_frame) {
if(print_ethernet_message(message, "RX frame") != 0) {
icsneoc2_message_free(message);
return 1;
}
if(is_tx) {
printf(" Note: RX port message was also marked as transmit.\n");
}
saw_rx_frame = true;
}
icsneoc2_message_free(message);
}
if(!saw_tx_echo || !saw_rx_frame) {
printf("Loopback incomplete: saw_tx_echo=%s, saw_rx_frame=%s\n",
saw_tx_echo ? "true" : "false",
saw_rx_frame ? "true" : "false");
printf("Confirm %s is physically connected to %s and both ports are configured for 10BASE-T1S.\n", tx_name, rx_name);
return 1;
}
printf("Loopback complete: TX echo on %s and RX frame on %s were both observed.\n", tx_name, rx_name);
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-tc10-example src/main.c)
target_link_libraries(libicsneoc2-tc10-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-tc10-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+393
View File
@@ -0,0 +1,393 @@
/*
* TC10 example.
*
* Sends TC10 wake/sleep requests, queries TC10 status, or lists connected
* devices and the Automotive Ethernet networks they support. Loosely based
* on examples/python/tc10/tc10.py. If --serial is omitted, the first
* available device is used.
*/
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2messages.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
const char* serial; /* may be NULL */
const char** networks; /* points into argv */
size_t networks_count;
bool send_wake;
bool send_sleep;
bool status;
bool list;
} args_t;
static int print_error_code(const char* message, icsneoc2_error_t error);
static void str_tolower(char* s);
static bool resolve_netid(const char* name, icsneoc2_netid_t* out);
static void print_usage(const char* prog);
static int parse_args(int argc, char** argv, args_t* out);
static icsneoc2_device_info_t* find_device(icsneoc2_device_info_t* list, const char* serial);
static int list_devices(icsneoc2_device_info_t* list);
static const char* tc10_wake_status_str(icsneoc2_tc10_wake_status_t s);
static const char* tc10_sleep_status_str(icsneoc2_tc10_sleep_status_t s);
int main(int argc, char** argv) {
args_t args;
if(parse_args(argc, argv, &args) != 0) {
return 1;
}
icsneoc2_device_info_t* found_devices = NULL;
icsneoc2_error_t res = icsneoc2_device_enumerate(0, &found_devices);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to enumerate devices", res);
}
if(found_devices == NULL) {
fprintf(stderr, "error: no devices found\n");
return 1;
}
if(args.list) {
int rc = list_devices(found_devices);
icsneoc2_enumeration_free(found_devices);
return rc;
}
icsneoc2_device_info_t* info = find_device(found_devices, args.serial);
if(info == NULL) {
fprintf(stderr, "error: unable to find device %s\n", args.serial ? args.serial : "(any)");
icsneoc2_enumeration_free(found_devices);
return 1;
}
char description[256] = {0};
size_t description_len = sizeof(description);
res = icsneoc2_device_info_description_get(info, description, &description_len);
if(res != icsneoc2_error_success) {
icsneoc2_enumeration_free(found_devices);
return print_error_code("Failed to get device description", res);
}
icsneoc2_device_t* device = NULL;
res = icsneoc2_device_create(info, &device);
if(res != icsneoc2_error_success) {
icsneoc2_enumeration_free(found_devices);
return print_error_code("Failed to create device", res);
}
printf("Opening device %s\n", description);
res = icsneoc2_device_open(device, icsneoc2_open_options_default);
if(res != icsneoc2_error_success) {
icsneoc2_device_free(device);
icsneoc2_enumeration_free(found_devices);
return print_error_code("Failed to open device", res);
}
bool supports_tc10 = false;
res = icsneoc2_device_supports_tc10(device, &supports_tc10);
if(res != icsneoc2_error_success || !supports_tc10) {
fprintf(stderr, "error: device does not support TC10 (%s)\n", description);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
icsneoc2_enumeration_free(found_devices);
return 1;
}
int rc = 0;
for(size_t i = 0; i < args.networks_count; ++i) {
const char* name = args.networks[i];
icsneoc2_netid_t netid = icsneoc2_netid_invalid;
if(!resolve_netid(name, &netid)) {
fprintf(stderr, "error: unknown network '%s'\n", name);
rc = 1;
break;
}
if(args.send_wake) {
printf("requesting TC10 wake on network %s\n", name);
res = icsneoc2_device_tc10_wake_request(device, netid);
if(res != icsneoc2_error_success) {
rc = print_error_code("Failed to send TC10 wake", res);
break;
}
} else if(args.send_sleep) {
printf("requesting TC10 sleep on network %s\n", name);
res = icsneoc2_device_tc10_sleep_request(device, netid);
if(res != icsneoc2_error_success) {
rc = print_error_code("Failed to send TC10 sleep", res);
break;
}
} else { /* args.status */
icsneoc2_tc10_sleep_status_t sleep_s = icsneoc2_tc10_sleep_status_no_sleep_received;
icsneoc2_tc10_wake_status_t wake_s = icsneoc2_tc10_wake_status_no_wake_received;
res = icsneoc2_device_tc10_status_get(device, netid, &sleep_s, &wake_s);
if(res != icsneoc2_error_success) {
rc = print_error_code("Failed to get TC10 status", res);
break;
}
printf("TC10 status on network %s: wake=%s sleep=%s\n", name,
tc10_wake_status_str(wake_s), tc10_sleep_status_str(sleep_s));
}
}
printf("Closing device %s\n", description);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
icsneoc2_enumeration_free(found_devices);
return rc;
}
static int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64];
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %d with error code %d\n", message, error, res);
return res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
static void str_tolower(char* s) {
for(; *s; ++s) {
*s = (char)tolower((unsigned char)*s);
}
}
/*
* Resolve a network name (e.g. "ETHERNET_01", "ae_01") to its icsneoc2_netid_t
* by iterating over all known netids and comparing names case-insensitively.
*/
static bool resolve_netid(const char* name, icsneoc2_netid_t* out) {
char want[64];
strncpy(want, name, sizeof(want) - 1);
want[sizeof(want) - 1] = '\0';
str_tolower(want);
for(uint16_t i = 0; i < icsneoc2_netid_maxsize; ++i) {
char buf[64];
size_t buf_len = sizeof(buf);
if(icsneoc2_netid_name_get((icsneoc2_netid_t)i, buf, &buf_len) != icsneoc2_error_success) {
continue;
}
str_tolower(buf);
if(strcmp(buf, want) == 0) {
*out = (icsneoc2_netid_t)i;
return true;
}
}
return false;
}
static void print_usage(const char* prog) {
printf("Usage:\n");
printf(" %s --list\n", prog);
printf(" %s [--serial SERIAL] --networks NET1 [NET2 ...] (--send-wake | --send-sleep | --status)\n", prog);
printf("\n");
printf(" --list List connected devices and the Automotive Ethernet networks they support.\n");
printf(" --serial SERIAL Serial number of the device. If omitted, the first available device is used.\n");
printf(" --networks NET ... One or more network names (e.g. ETHERNET_01 AE_01). Consumes args until the next flag.\n");
printf(" --send-wake Trigger TC10 wake on the selected networks.\n");
printf(" --send-sleep Trigger TC10 sleep on the selected networks.\n");
printf(" --status Query TC10 wake/sleep status on the selected networks.\n");
printf(" -h, --help Show this message.\n");
}
static int parse_args(int argc, char** argv, args_t* out) {
memset(out, 0, sizeof(*out));
for(int i = 1; i < argc; ++i) {
const char* a = argv[i];
if(strcmp(a, "-h") == 0 || strcmp(a, "--help") == 0) {
print_usage(argv[0]);
exit(0);
} else if(strcmp(a, "--serial") == 0) {
if(i + 1 >= argc) {
fprintf(stderr, "error: --serial requires a value\n");
return 1;
}
out->serial = argv[++i];
} else if(strcmp(a, "--send-wake") == 0) {
out->send_wake = true;
} else if(strcmp(a, "--send-sleep") == 0) {
out->send_sleep = true;
} else if(strcmp(a, "--status") == 0) {
out->status = true;
} else if(strcmp(a, "--list") == 0) {
out->list = true;
} else if(strcmp(a, "--networks") == 0) {
if(i + 1 >= argc || argv[i + 1][0] == '-') {
fprintf(stderr, "error: --networks requires at least one network name\n");
return 1;
}
out->networks = (const char**)&argv[i + 1];
size_t count = 0;
while(i + 1 < argc && argv[i + 1][0] != '-') {
++count;
++i;
}
out->networks_count = count;
} else {
fprintf(stderr, "error: unknown argument '%s'\n", a);
print_usage(argv[0]);
return 1;
}
}
if(out->list) {
if(out->send_wake || out->send_sleep || out->status || out->networks_count > 0 || out->serial) {
fprintf(stderr, "error: --list cannot be combined with other options\n");
print_usage(argv[0]);
return 1;
}
return 0;
}
if(out->networks_count == 0) {
fprintf(stderr, "error: --networks is required\n");
print_usage(argv[0]);
return 1;
}
int action_count = (out->send_wake ? 1 : 0) + (out->send_sleep ? 1 : 0) + (out->status ? 1 : 0);
if(action_count != 1) {
fprintf(stderr, "error: exactly one of --send-wake, --send-sleep, or --status is required\n");
print_usage(argv[0]);
return 1;
}
return 0;
}
static const char* tc10_wake_status_str(icsneoc2_tc10_wake_status_t s) {
switch(s) {
case icsneoc2_tc10_wake_status_no_wake_received: return "no_wake_received";
case icsneoc2_tc10_wake_status_wake_received: return "wake_received";
default: return "unknown";
}
}
static const char* tc10_sleep_status_str(icsneoc2_tc10_sleep_status_t s) {
switch(s) {
case icsneoc2_tc10_sleep_status_no_sleep_received: return "no_sleep_received";
case icsneoc2_tc10_sleep_status_sleep_received: return "sleep_received";
case icsneoc2_tc10_sleep_status_sleep_failed: return "sleep_failed";
case icsneoc2_tc10_sleep_status_sleep_aborted: return "sleep_aborted";
default: return "unknown";
}
}
/*
* Find a device matching the provided serial, or the first available device if serial is NULL.
* Returns NULL on failure (caller is responsible for the enumeration list).
*/
static icsneoc2_device_info_t* find_device(icsneoc2_device_info_t* list, const char* serial) {
if(serial == NULL) {
return list;
}
for(icsneoc2_device_info_t* cur = list; cur != NULL; cur = icsneoc2_device_info_next(cur)) {
char dev_serial[64] = {0};
size_t dev_serial_len = sizeof(dev_serial);
if(icsneoc2_device_info_serial_get(cur, dev_serial, &dev_serial_len) != icsneoc2_error_success) {
continue;
}
if(strcmp(dev_serial, serial) == 0) {
return cur;
}
}
return NULL;
}
/*
* List connected devices and the Automotive Ethernet networks each one supports.
*/
static int list_devices(icsneoc2_device_info_t* list) {
size_t index = 0;
for(icsneoc2_device_info_t* cur = list; cur != NULL; cur = icsneoc2_device_info_next(cur)) {
char serial[64] = {0};
size_t serial_len = sizeof(serial);
(void)icsneoc2_device_info_serial_get(cur, serial, &serial_len);
char description[256] = {0};
size_t description_len = sizeof(description);
(void)icsneoc2_device_info_description_get(cur, description, &description_len);
printf("[%zu] %s (%s)\n", index++, description, serial);
icsneoc2_device_t* device = NULL;
icsneoc2_error_t res = icsneoc2_device_create(cur, &device);
if(res != icsneoc2_error_success) {
print_error_code(" Failed to create device", res);
continue;
}
icsneoc2_open_options_t options = icsneoc2_open_options_default;
options &= ~ICSNEOC2_OPEN_OPTIONS_SYNC_RTC;
options &= ~ICSNEOC2_OPEN_OPTIONS_GO_ONLINE;
res = icsneoc2_device_open(device, options);
if(res != icsneoc2_error_success) {
print_error_code(" Failed to open device", res);
icsneoc2_device_free(device);
continue;
}
bool supports_tc10 = false;
(void)icsneoc2_device_supports_tc10(device, &supports_tc10);
printf(" TC10 supported: %s\n", supports_tc10 ? "yes" : "no");
size_t count = 0;
res = icsneoc2_device_supported_tx_networks_get(device, NULL, &count);
if(res != icsneoc2_error_success || count == 0) {
icsneoc2_device_close(device);
icsneoc2_device_free(device);
continue;
}
icsneoc2_netid_t* nets = (icsneoc2_netid_t*)calloc(count, sizeof(icsneoc2_netid_t));
if(nets == NULL) {
fprintf(stderr, " error: out of memory\n");
icsneoc2_device_close(device);
icsneoc2_device_free(device);
continue;
}
res = icsneoc2_device_supported_tx_networks_get(device, nets, &count);
if(res != icsneoc2_error_success) {
print_error_code(" Failed to get supported networks", res);
free(nets);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
continue;
}
printf(" Automotive Ethernet networks:\n");
bool any = false;
for(size_t i = 0; i < count; ++i) {
icsneoc2_network_type_t type = icsneoc2_network_type_invalid;
if(icsneoc2_netid_network_type_get(nets[i], &type) != icsneoc2_error_success) {
continue;
}
if(type != icsneoc2_network_type_automotive_ethernet) {
continue;
}
char name[64] = {0};
size_t name_len = sizeof(name);
if(icsneoc2_netid_name_get(nets[i], name, &name_len) != icsneoc2_error_success) {
continue;
}
printf(" %s\n", name);
any = true;
}
if(!any) {
printf(" (none)\n");
}
free(nets);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
}
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
add_executable(libicsneoc2-termination-example src/main.c)
target_link_libraries(libicsneoc2-termination-example icsneoc2-static)
if(WIN32)
target_compile_definitions(libicsneoc2-termination-example PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
+91
View File
@@ -0,0 +1,91 @@
#include <icsneo/icsneoc2.h>
#include <icsneo/icsneoc2messages.h>
#include <icsneo/icsneoc2settings.h>
#include <stdio.h>
#include <stdlib.h>
int print_error_code(const char* message, icsneoc2_error_t error) {
char error_str[64];
size_t error_str_len = sizeof(error_str);
icsneoc2_error_t res = icsneoc2_error_code_get(error, error_str, &error_str_len);
if(res != icsneoc2_error_success) {
printf("%s: Failed to get string for error code %u with error code %u\n", message, error, res);
return (int)res;
}
printf("%s: \"%s\" (%u)\n", message, error_str, error);
return (int)error;
}
int main() {
icsneoc2_error_t res;
icsneoc2_device_t* device = NULL;
res = icsneoc2_device_open_first(0, icsneoc2_open_options_default, &device);
if(res != icsneoc2_error_success) {
return print_error_code("Failed to open first device", res);
}
char description[128] = {0};
size_t description_length = sizeof(description);
icsneoc2_device_description_get(device, description, &description_length);
printf("Opened device: %s\n\n", description);
size_t count = 0;
icsneoc2_termination_group_t* groups = NULL;
res = icsneoc2_settings_termination_groups_enumerate(device, &groups, &count);
if(res != icsneoc2_error_success) {
print_error_code("Failed to enumerate termination groups", res);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 1;
}
if(count == 0) {
printf("This device does not support software switchable termination.\n");
} else {
printf("Found %zu termination group(s) (only one network per group may be terminated at a time):\n", count);
}
size_t group_index = 0;
for(icsneoc2_termination_group_t* group = groups; group; group = icsneoc2_termination_group_next(group), group_index++) {
// First call with a NULL buffer to learn how many networks are in this group.
size_t network_count = 0;
icsneoc2_termination_group_networks_get(group, NULL, &network_count);
icsneoc2_netid_t* netids = (icsneoc2_netid_t*)malloc(network_count * sizeof(icsneoc2_netid_t));
if(!netids) {
print_error_code("Out of memory", icsneoc2_error_out_of_memory);
break;
}
// Second call to fill the buffer.
res = icsneoc2_termination_group_networks_get(group, netids, &network_count);
if(res != icsneoc2_error_success) {
print_error_code("Failed to get termination group networks", res);
free(netids);
continue;
}
printf(" Group %zu:", group_index);
for(size_t i = 0; i < network_count; i++) {
char name[64] = {0};
size_t name_length = sizeof(name);
if(icsneoc2_netid_name_get(netids[i], name, &name_length) == icsneoc2_error_success) {
printf(" %s", name);
} else {
printf(" %u", netids[i]);
}
if(i + 1 < network_count) {
printf(",");
}
}
printf("\n");
free(netids);
}
icsneoc2_settings_termination_groups_free(groups);
icsneoc2_device_close(device);
icsneoc2_device_free(device);
return 0;
}
+3
View File
@@ -406,6 +406,9 @@ int main(int argc, char** argv) {
if(it == devices.end()) { if(it == devices.end()) {
std::cerr << "Could not find RAD-A2B." << std::endl; std::cerr << "Could not find RAD-A2B." << std::endl;
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cerr << lastError << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -92,6 +92,9 @@ int main(int argc, const char** argv) {
if(!device) { if(!device) {
std::cerr << "Device with serial " << serial << " not found" << std::endl; std::cerr << "Device with serial " << serial << " not found" << std::endl;
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cerr << lastError << std::endl;
return -1; return -1;
} }
} else { } else {
@@ -99,6 +102,9 @@ int main(int argc, const char** argv) {
auto devices = icsneo::FindAllDevices(); auto devices = icsneo::FindAllDevices();
if(devices.empty()) { if(devices.empty()) {
std::cerr << "No devices found" << std::endl; std::cerr << "No devices found" << std::endl;
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cerr << lastError << std::endl;
return -1; return -1;
} }
device = devices[0]; device = devices[0];
@@ -9,7 +9,7 @@
/* /*
* App errors are responses from the device indicating internal runtime errors * App errors are responses from the device indicating internal runtime errors
* NOTE: To trigger the app error in this example, disable the DW CAN 01 network on the device * NOTE: To trigger the app error in this example, disable the DW CAN 01 network on the device
* (e.g. with neoVI Explorer) * (e.g. with ICS Device Manager)
*/ */
int main() { int main() {
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl; std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
@@ -17,6 +17,13 @@ int main() {
auto devices = icsneo::FindAllDevices(); auto devices = icsneo::FindAllDevices();
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if(devices.empty()) {
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return 0;
}
// List off the devices // List off the devices
for(auto& device : devices) for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl; std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
+3
View File
@@ -33,6 +33,9 @@ int main(int argc, char** argv) {
if(it == devices.end()) { if(it == devices.end()) {
std::cout << "Failed to find device." << std::endl; std::cout << "Failed to find device." << std::endl;
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-device-info-example src/DeviceInfoExample.cpp)
target_link_libraries(libicsneocpp-device-info-example icsneocpp)
@@ -0,0 +1,80 @@
#include <iostream>
#include <iomanip>
#include <sstream>
#include "icsneo/icsneocpp.h"
static std::string formatMAC(const std::array<uint8_t, icsneo::MACAddressLength> addr) {
std::ostringstream oss;
for(size_t i = 0; i < icsneo::MACAddressLength; i++) {
if(i > 0) {
oss << ':';
}
oss << std::hex << std::uppercase << std::setw(2) << std::setfill('0') << static_cast<unsigned>(addr[i]);
}
return oss.str();
}
int main() {
// ===== Device Selection =====
std::cout << "Searching for devices...\n";
auto devices = icsneo::FindAllDevices();
if(devices.empty()) {
std::cout << "No devices found.\n";
return 1;
}
for(size_t i = 0; i < devices.size(); i++) {
std::cout << " [" << (i + 1) << "] " << devices[i]->describe() << "\n";
}
int choice;
std::cout << "Select device (1-" << devices.size() << "): ";
if(!(std::cin >> choice) || choice < 1 || choice > (int)devices.size()) {
std::cout << "Invalid selection.\n";
return 1;
}
auto& device = devices[choice - 1];
std::cout << "\nOpening " << device->describe() << "... ";
if(!device->open()) {
std::cout << "FAIL\n" << icsneo::GetLastError() << "\n";
return 1;
}
std::cout << "OK\n\n";
// ===== Serial Number =====
std::cout << "Serial: " << device->getSerial() << "\n";
// ===== PCB Serial Number =====
auto pcbSerial = device->getPCBSerial();
if(pcbSerial.has_value()) {
std::cout << "PCB Serial: ";
for(auto b : *pcbSerial)
std::cout << (char)b;
std::cout << "\n";
} else {
std::cout << "PCB Serial: Not supported\n";
}
auto macs = device->getMACAddresses();
if(!macs.empty()) {
std::cout << "MACs: " << macs.size()
<< (macs.size() == 1 ? " entry" : " entries") << "\n";
for(auto macPair : macs) {
std::cout << " " << std::left << std::setw(20)
<< icsneo::Network::GetNetIDString(static_cast<icsneo::Network::NetID>(macPair.first))
<< " " << formatMAC(macPair.second) << "\n";
}
} else {
std::cout << "MACs: Not supported\n";
}
// ===== Cleanup =====
std::cout << "\nClosing device... ";
std::cout << (device->close() ? "OK" : "FAIL") << "\n";
return 0;
}
@@ -12,7 +12,9 @@ int main() {
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if(devices.empty()) { if(devices.empty()) {
std::cout << "error: no devices found" << std::endl; auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return -1; return -1;
} }
@@ -138,6 +138,12 @@ std::vector<std::shared_ptr<icsneo::FlexRayMessage>> makeDummyFlexRayMessages(si
int main() { int main() {
auto devices = icsneo::FindAllDevices(); auto devices = icsneo::FindAllDevices();
if(devices.empty()) {
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cerr << lastError << std::endl;
return -1;
}
std::shared_ptr<icsneo::Device> flexrayDevice = nullptr; std::shared_ptr<icsneo::Device> flexrayDevice = nullptr;
for (auto&& device : devices) { for (auto&& device : devices) {
if (device->getExtension("FlexRay")) { if (device->getExtension("FlexRay")) {
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-gptp-settings src/GPTPSettingsExample.cpp)
target_link_libraries(libicsneocpp-gptp-settings icsneocpp)
@@ -0,0 +1,179 @@
#include <iostream>
#include <iomanip>
#include <vector>
#include <optional>
#include <string>
#include <limits>
#include "icsneo/icsneocpp.h"
/* Maps a Network::NetID to its gPTP port index (0 = not a gPTP port). */
static uint8_t netidToGPTPPort(icsneo::Network::NetID netid) {
using N = icsneo::Network::NetID;
switch(netid) {
case N::AE_01: return 1;
case N::AE_02: return 2;
case N::AE_03: return 3;
case N::AE_04: return 4;
case N::AE_05: return 5;
case N::AE_06: return 6;
case N::AE_07: return 7;
case N::AE_08: return 8;
case N::AE_09: return 9;
case N::AE_10: return 10;
case N::AE_11: return 11;
case N::AE_12: return 12;
case N::ETHERNET_01: return 13;
case N::ETHERNET_02: return 14;
case N::ETHERNET_03: return 15;
case N::AE_13: return 16;
case N::AE_14: return 17;
case N::AE_15: return 18;
case N::AE_16: return 19;
default: return 0;
}
}
static std::string gptpProfileStr(RADGPTPProfile p) {
switch(p) {
case RAD_GPTP_PROFILE_STANDARD: return "Standard";
case RAD_GPTP_PROFILE_AUTOMOTIVE: return "Automotive";
default: return "Unknown";
}
}
static std::string gptpRoleStr(RADGPTPRole r) {
switch(r) {
case RAD_GPTP_ROLE_DISABLED: return "Disabled";
case RAD_GPTP_ROLE_PASSIVE: return "Passive";
case RAD_GPTP_ROLE_MASTER: return "Master";
case RAD_GPTP_ROLE_SLAVE: return "Slave";
default: return "Unknown";
}
}
template<typename T>
static std::string optStr(const std::optional<T>& opt) {
if(!opt.has_value()) return "N/A";
if constexpr(std::is_same_v<T, bool>)
return opt.value() ? "enabled" : "disabled";
else
return std::to_string(static_cast<int>(opt.value()));
}
static long promptLong(const std::string& prompt, long defaultVal, long min, long max) {
std::cout << prompt << " [" << defaultVal << "]: " << std::flush;
std::string input;
std::getline(std::cin, input);
if(input.empty())
return defaultVal;
try {
long val = std::stol(input);
if(val >= min && val <= max)
return val;
} catch(...) {}
std::cout << "Invalid input, using " << defaultVal << ".\n";
return defaultVal;
}
static void printGPTPPorts(const std::shared_ptr<icsneo::Device>& device) {
for(const auto& net : device->getSupportedTXNetworks()) {
uint8_t port = netidToGPTPPort(net.getNetID());
if(port == 0) continue;
std::cout << " [" << (int)port << "] " << net << "\n";
}
}
static void printCurrentSettings(const std::shared_ptr<icsneo::Device>& device) {
std::cout << "\nCurrent gPTP settings:\n";
auto profile = device->settings->getGPTPProfile();
auto role = device->settings->getGPTPRole();
auto port = device->settings->getGPTPEnabledPort();
auto synton = device->settings->isGPTPClockSyntonizationEnabled();
if(profile.has_value())
std::cout << " Profile: " << gptpProfileStr(profile.value()) << "\n";
if(role.has_value())
std::cout << " Role: " << gptpRoleStr(role.value()) << "\n";
if(port.has_value())
std::cout << " Enabled port: " << (int)port.value() << (port.value() == 0 ? " (disabled)" : "") << "\n";
if(synton.has_value())
std::cout << " Clock syntonization: " << optStr(synton) << "\n";
}
int main() {
std::cout << "Finding devices... " << std::flush;
auto devices = icsneo::FindAllDevices();
std::cout << devices.size() << " found\n";
if(devices.empty()) {
auto err = icsneo::GetLastError();
if(err.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << err << "\n";
return 1;
}
std::vector<std::shared_ptr<icsneo::Device>> gptp_devices;
for(auto& d : devices) {
if(d->supportsGPTP())
gptp_devices.push_back(d);
}
if(gptp_devices.empty()) {
std::cout << "No gPTP-capable devices found.\n";
return 1;
}
std::cout << "\nAvailable gPTP-capable devices:\n";
for(size_t i = 0; i < gptp_devices.size(); ++i)
std::cout << " [" << (i + 1) << "] " << gptp_devices[i]->describe() << "\n";
long choice = promptLong("Select device", 1, 1, (long)gptp_devices.size());
auto device = gptp_devices[choice - 1];
std::cout << "\nOpening " << device->describe() << "... " << std::flush;
if(!device->open()) {
std::cout << "failed\n" << icsneo::GetLastError() << "\n";
return 1;
}
std::cout << "OK\n";
if(!device->settings->refresh()) {
std::cout << "Failed to refresh settings\n";
device->close();
return 1;
}
printCurrentSettings(device);
std::cout << "\nAvailable gPTP ports (0 = disabled):\n";
std::cout << " [0] Disabled\n";
printGPTPPorts(device);
std::cout << "\nConfigure gPTP:\n";
long profile = promptLong(" Profile (0=Standard, 1=Automotive)", 1, 0, 1);
device->settings->setGPTPProfile(static_cast<RADGPTPProfile>(profile));
long role = promptLong(" Role (0=Disabled, 1=Passive, 2=Master, 3=Slave)", 3, 0, 3);
device->settings->setGPTPRole(static_cast<RADGPTPRole>(role));
long port = promptLong(" Enabled port index", 0, 0, 19);
device->settings->setGPTPEnabledPort(static_cast<uint8_t>(port));
long syntonization = promptLong(" Clock syntonization (0=disabled, 1=enabled)", 0, 0, 1);
device->settings->setGPTPClockSyntonizationEnabled(syntonization != 0);
long permanent = promptLong("\nSave to EEPROM? (0=temporary, 1=permanent)", 0, 0, 1);
if(!device->settings->apply(permanent == 0)) {
std::cout << "Failed to apply settings\n" << icsneo::GetLastError() << "\n";
device->close();
return 1;
}
printCurrentSettings(device);
std::cout << "\nClosing " << device->describe() << "\n";
device->close();
return 0;
}
@@ -223,8 +223,30 @@ void printMessage(const std::shared_ptr<icsneo::Message>& message) {
std::cout << "\t\t Timestamped:\t"<< ethMessage->timestamp << " ns since 1/1/2007\n"; std::cout << "\t\t Timestamped:\t"<< ethMessage->timestamp << " ns since 1/1/2007\n";
// The MACAddress may be printed directly or accessed with the `data` member // The MACAddress may be printed directly or accessed with the `data` member
std::cout << "\t\t Source:\t" << ethMessage->getSourceMAC() << "\n"; auto printMAC = [](const icsneo::MACAddress& mac) {
std::cout << "\t\t Destination:\t" << ethMessage->getDestinationMAC(); std::ostringstream oss;
for(size_t i = 0; i < mac.size(); i++) {
oss << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)mac[i];
if(i != mac.size() - 1)
oss << ':';
}
return oss.str();
};
if (auto destMAC = ethMessage->getDestinationMAC(); destMAC.has_value()) {
std::cout << "\t\t Destination:\t" << printMAC(*destMAC) << "\n";
} else {
std::cout << "\t\t Destination:\t N/A\n";
}
if (auto srcMAC = ethMessage->getSourceMAC(); srcMAC.has_value()) {
std::cout << "\t\t Source:\t" << printMAC(*srcMAC) << "\n";
} else {
std::cout << "\t\t Source:\t N/A\n";
}
if (auto etherType = ethMessage->getEtherType(); etherType.has_value()) {
std::cout << "\t\t EtherType:\t" << std::hex << std::setw(4) << std::setfill('0') << *etherType << "\n";
} else {
std::cout << "\t\t EtherType:\t N/A\n";
}
// Print the data // Print the data
for(size_t i = 0; i < ethMessage->data.size(); i++) { for(size_t i = 0; i < ethMessage->data.size(); i++) {
+7
View File
@@ -15,6 +15,13 @@ int main() {
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective // You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if(devices.empty()) {
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return 0;
}
// List off the devices // List off the devices
for(auto& device : devices) for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl; std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
@@ -14,6 +14,13 @@ int main() {
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>> auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if(devices.empty()) {
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return 0;
}
// List off the devices // List off the devices
for(auto& device : devices) for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl; std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
+4 -2
View File
@@ -9,8 +9,10 @@ int main(int, char**) {
auto devices = icsneo::FindAllDevices(); auto devices = icsneo::FindAllDevices();
if(devices.size() == 0) { if(devices.empty()) {
std::cout << "No device found" << std::endl; auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return -1; return -1;
} }
+7
View File
@@ -48,6 +48,13 @@ int main()
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective // You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if(devices.empty()) {
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return 0;
}
// List off the devices // List off the devices
for (auto &device : devices) for (auto &device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl; std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
+7
View File
@@ -76,6 +76,13 @@ int main() {
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective // You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if(devices.empty()) {
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return 0;
}
// List off the devices // List off the devices
for(auto& device : devices) for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl; std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
+28 -3
View File
@@ -22,6 +22,13 @@ int main() {
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective // You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if(devices.empty()) {
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return 0;
}
// List off the devices // List off the devices
for(auto& device : devices) for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl; std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
@@ -208,8 +215,26 @@ int main() {
std::cout << "\t\t Timestamped:\t"<< ethMessage->timestamp << " ns since 1/1/2007\n"; std::cout << "\t\t Timestamped:\t"<< ethMessage->timestamp << " ns since 1/1/2007\n";
// The MACAddress may be printed directly or accessed with the `data` member // The MACAddress may be printed directly or accessed with the `data` member
std::cout << "\t\t Source:\t" << ethMessage->getSourceMAC() << "\n"; // The MACAddress may be printed directly or accessed with the `data` member
std::cout << "\t\t Destination:\t" << ethMessage->getDestinationMAC(); auto printMAC = [](const icsneo::MACAddress& mac) {
std::ostringstream oss;
for(size_t i = 0; i < mac.size(); i++) {
oss << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)mac[i];
if(i != mac.size() - 1)
oss << ':';
}
return oss.str();
};
if (auto destMAC = ethMessage->getDestinationMAC(); destMAC.has_value()) {
std::cout << "\t\t Destination:\t" << printMAC(*destMAC) << "\n";
} else {
std::cout << "\t\t Destination:\t N/A\n";
}
if (auto srcMAC = ethMessage->getSourceMAC(); srcMAC.has_value()) {
std::cout << "\t\t Source:\t" << printMAC(*srcMAC) << "\n";
} else {
std::cout << "\t\t Source:\t N/A\n";
}
// Print the data // Print the data
for(size_t i = 0; i < ethMessage->data.size(); i++) { for(size_t i = 0; i < ethMessage->data.size(); i++) {
@@ -321,7 +346,7 @@ int main() {
if(val.has_value()) if(val.has_value())
std::cout << " - OK (" << val.value() << "V)" << std::endl; std::cout << " - OK (" << val.value() << "V)" << std::endl;
else else
std::cout << " - FAIL, it may need to be enabled in neoVI Explorer (" << icsneo::GetLastError() << ")" << std::endl; std::cout << " - FAIL, it may need to be enabled in ICS Device Manager (" << icsneo::GetLastError() << ")" << std::endl;
} }
} }
+3
View File
@@ -86,6 +86,9 @@ int main(int argc, const char** argv) {
} }
if(!device) { if(!device) {
std::cerr << "Failed to find device" << std::endl; std::cerr << "Failed to find device" << std::endl;
auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cerr << lastError << std::endl;
std::cerr << usage; std::cerr << usage;
return -1; return -1;
} }
+4 -2
View File
@@ -216,8 +216,10 @@ int main() {
auto devices = icsneo::FindAllDevices(); auto devices = icsneo::FindAllDevices();
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if (devices.empty()) { if(devices.empty()) {
std::cout << "No devices found!" << std::endl; auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return 1; return 1;
} }
@@ -154,12 +154,12 @@ void setupSymbolMonitoring(std::shared_ptr<icsneo::Device>& device,
auto ethMsg = std::static_pointer_cast<icsneo::EthernetMessage>(frame); auto ethMsg = std::static_pointer_cast<icsneo::EthernetMessage>(frame);
if (!ethMsg->isT1S) if (!ethMsg->t1s)
return; return;
double timestamp_ms = ethMsg->timestamp / 1000000.0; double timestamp_ms = ethMsg->timestamp / 1000000.0;
if (ethMsg->isT1SSymbol) { if (ethMsg->t1s->isSymbol) {
size_t numSymbols = ethMsg->data.size(); size_t numSymbols = ethMsg->data.size();
std::cout << std::fixed << std::setprecision(3) std::cout << std::fixed << std::setprecision(3)
@@ -169,7 +169,7 @@ void setupSymbolMonitoring(std::shared_ptr<icsneo::Device>& device,
if (numSymbols > 0) { if (numSymbols > 0) {
std::cout << " (" << numSymbols << " symbol" << (numSymbols > 1 ? "s" : "") << ")"; std::cout << " (" << numSymbols << " symbol" << (numSymbols > 1 ? "s" : "") << ")";
} }
std::cout << " | Node ID: " << (int)ethMsg->t1sNodeId << std::endl; std::cout << " | Node ID: " << (int)ethMsg->t1s->nodeId << std::endl;
for (size_t i = 0; i < numSymbols; i++) { for (size_t i = 0; i < numSymbols; i++) {
uint8_t symbolValue = ethMsg->data[i]; uint8_t symbolValue = ethMsg->data[i];
@@ -188,8 +188,8 @@ void setupSymbolMonitoring(std::shared_ptr<icsneo::Device>& device,
<< std::dec << std::endl; << std::dec << std::endl;
} }
if (numSymbols == 0 && ethMsg->t1sSymbolType != 0) { if (numSymbols == 0 && ethMsg->t1s->symbolType != 0) {
uint8_t symbolValue = ethMsg->t1sSymbolType; uint8_t symbolValue = ethMsg->t1s->symbolType;
std::string symbolName = getSymbolName(symbolValue); std::string symbolName = getSymbolName(symbolValue);
stats.symbolCount++; stats.symbolCount++;
@@ -205,20 +205,20 @@ void setupSymbolMonitoring(std::shared_ptr<icsneo::Device>& device,
<< std::dec << " (from t1sSymbolType field)" << std::endl; << std::dec << " (from t1sSymbolType field)" << std::endl;
} }
} }
else if (ethMsg->isT1SBurst) { else if (ethMsg->t1s->isBurst) {
stats.burstCount++; stats.burstCount++;
std::cout << std::fixed << std::setprecision(3) std::cout << std::fixed << std::setprecision(3)
<< "[" << std::setw(12) << timestamp_ms << " ms] " << "[" << std::setw(12) << timestamp_ms << " ms] "
<< "BURST | " << "BURST | "
<< "Node ID: " << (int)ethMsg->t1sNodeId << " | " << "Node ID: " << (int)ethMsg->t1s->nodeId << " | "
<< "Burst Count: " << (int)ethMsg->t1sBurstCount << std::endl; << "Burst Count: " << (int)ethMsg->t1s->burstCount << std::endl;
} }
else if (ethMsg->isT1SWake) { else if (ethMsg->t1s->isWake) {
stats.wakeCount++; stats.wakeCount++;
std::cout << std::fixed << std::setprecision(3) std::cout << std::fixed << std::setprecision(3)
<< "[" << std::setw(12) << timestamp_ms << " ms] " << "[" << std::setw(12) << timestamp_ms << " ms] "
<< "WAKE signal detected | " << "WAKE signal detected | "
<< "Node ID: " << (int)ethMsg->t1sNodeId << std::endl; << "Node ID: " << (int)ethMsg->t1s->nodeId << std::endl;
} }
else { else {
stats.dataFrameCount++; stats.dataFrameCount++;
@@ -226,7 +226,7 @@ void setupSymbolMonitoring(std::shared_ptr<icsneo::Device>& device,
<< "[" << std::setw(12) << timestamp_ms << " ms] " << "[" << std::setw(12) << timestamp_ms << " ms] "
<< "T1S Data Frame | " << "T1S Data Frame | "
<< "Length: " << ethMsg->data.size() << " bytes | " << "Length: " << ethMsg->data.size() << " bytes | "
<< "Node ID: " << (int)ethMsg->t1sNodeId << std::endl; << "Node ID: " << (int)ethMsg->t1s->nodeId << std::endl;
if (!ethMsg->data.empty()) { if (!ethMsg->data.empty()) {
std::cout << " Data: "; std::cout << " Data: ";
@@ -260,8 +260,10 @@ int main() {
auto devices = icsneo::FindAllDevices(); auto devices = icsneo::FindAllDevices();
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl; std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
if (devices.empty()) { if(devices.empty()) {
std::cerr << "No devices found!" << std::endl; auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cerr << lastError << std::endl;
return 1; return 1;
} }
+3 -1
View File
@@ -106,7 +106,9 @@ int main(int argc, char* argv[]) {
auto devices = icsneo::FindAllDevices(); auto devices = icsneo::FindAllDevices();
if(devices.empty()) { if(devices.empty()) {
std::cout << "error: no devices found" << std::endl; auto lastError = icsneo::GetLastError();
if(lastError.getType() != icsneo::APIEvent::Type::NoErrorFound)
std::cout << lastError << std::endl;
return -1; return -1;
} }
+51
View File
@@ -0,0 +1,51 @@
import icsneopy
import time
#
# App errors are responses from the device indicating internal runtime errors
# NOTE: To trigger the app error in this example, disable the DW CAN 01 network on the device
# (e.g. with ICS Device Manager)
#
def apperror():
devices = icsneopy.find_all_devices()
if len(devices) == 0:
print("no devices found")
return False
device = devices[0]
print(f"selected {device}")
def on_apperror(message):
if message.get_app_error_type() != icsneopy.AppErrorType.NetworkNotEnabled:
print("unexpected app error type")
return
print(message.get_app_error_string())
filter = icsneopy.MessageFilter(icsneopy.Message.Type.AppError)
callback = icsneopy.MessageCallback(on_apperror, filter)
device.add_message_callback(callback)
if not device.open():
print("unable to open device")
return False
if not device.go_online():
print("unable to go online")
return False
frame = icsneopy.CANMessage()
frame.network = icsneopy.Network(icsneopy.Network.NetID.DWCAN_01)
frame.arbid = 0x22
frame.data = (0xAA, 0xBB, 0xCC)
if not device.transmit(frame):
print("failed to transmit frame")
return False
time.sleep(2) # wait for error to come back
return True
if __name__ == "__main__":
apperror()
@@ -47,14 +47,23 @@ def setup_ethernet_reception(device):
def frame_handler(frame): def frame_handler(frame):
nonlocal frame_count nonlocal frame_count
frame_count += 1 frame_count += 1
dst = frame.get_destination_mac()
src = frame.get_source_mac()
et = frame.get_ether_type()
dst_str = ":".join(f"{b:02x}" for b in dst) if dst is not None else "N/A"
src_str = ":".join(f"{b:02x}" for b in src) if src is not None else "N/A"
et_str = f"0x{et:04x}" if et is not None else "N/A"
print(f"[RX {frame_count}], " print(f"[RX {frame_count}], "
f"Data: {[hex(b) for b in frame.data]}, " f"dst={dst_str}, src={src_str}, ethertype={et_str}, "
f"Length: {len(frame.data)}") f"Data: {[hex(b) for b in frame.data]}, "
frame_filter = icsneopy.MessageFilter(icsneopy.Network.NetID.ETHERNET_01) f"Length: {len(frame.data)}")
frame_filter = icsneopy.MessageFilter(icsneopy.Network.NetID.ETHERNET_02)
callback = icsneopy.MessageCallback(frame_handler, frame_filter) callback = icsneopy.MessageCallback(frame_handler, frame_filter)
device.add_message_callback(callback) device.add_message_callback(callback)
print("CAN frame reception configured") print("Ethernet frame reception configured")
return 0 return 0
+151
View File
@@ -0,0 +1,151 @@
import icsneopy
GPTP_PORT_MAP = {
icsneopy.Network.NetID.AE_01: (1, "AE 01"),
icsneopy.Network.NetID.AE_02: (2, "AE 02"),
icsneopy.Network.NetID.AE_03: (3, "AE 03"),
icsneopy.Network.NetID.AE_04: (4, "AE 04"),
icsneopy.Network.NetID.AE_05: (5, "AE 05"),
icsneopy.Network.NetID.AE_06: (6, "AE 06"),
icsneopy.Network.NetID.AE_07: (7, "AE 07"),
icsneopy.Network.NetID.AE_08: (8, "AE 08"),
icsneopy.Network.NetID.AE_09: (9, "AE 09"),
icsneopy.Network.NetID.AE_10: (10, "AE 10"),
icsneopy.Network.NetID.AE_11: (11, "AE 11"),
icsneopy.Network.NetID.AE_12: (12, "AE 12"),
icsneopy.Network.NetID.ETHERNET_01: (13, "Ethernet 01"),
icsneopy.Network.NetID.ETHERNET_02: (14, "Ethernet 02"),
icsneopy.Network.NetID.ETHERNET_03: (15, "Ethernet 03"),
icsneopy.Network.NetID.AE_13: (16, "AE 13"),
icsneopy.Network.NetID.AE_14: (17, "AE 14"),
icsneopy.Network.NetID.AE_15: (18, "AE 15"),
icsneopy.Network.NetID.AE_16: (19, "AE 16"),
}
PROFILE_NAMES = {
icsneopy.Settings.GTPPProfile.Standard: "Standard",
icsneopy.Settings.GTPPProfile.Automotive: "Automotive",
}
ROLE_NAMES = {
icsneopy.Settings.GTPPRole.Disabled: "Disabled",
icsneopy.Settings.GTPPRole.Passive: "Passive",
icsneopy.Settings.GTPPRole.Master: "Master",
icsneopy.Settings.GTPPRole.Slave: "Slave",
}
def prompt_int(prompt, default, lo, hi):
try:
raw = input(f"{prompt} [{default}]: ").strip()
if not raw:
return default
val = int(raw)
if lo <= val <= hi:
return val
except (ValueError, EOFError):
pass
print(f"Invalid input, using {default}.")
return default
def gptp_ports(device):
ports = []
for net in device.get_supported_tx_networks():
entry = GPTP_PORT_MAP.get(net.get_net_id())
if entry:
ports.append(entry)
return sorted(ports)
def print_settings(device):
s = device.settings
profile = s.get_gptp_profile()
role = s.get_gptp_role()
port = s.get_gptp_enabled_port()
synton = s.is_gptp_clock_syntonization_enabled()
print("\nCurrent gPTP settings:")
if profile is not None:
print(f" Profile: {PROFILE_NAMES.get(profile, profile)}")
if role is not None:
print(f" Role: {ROLE_NAMES.get(role, role)}")
if port is not None:
suffix = " (disabled)" if port == 0 else ""
print(f" Enabled port: {port}{suffix}")
if synton is not None:
print(f" Clock syntonization: {'enabled' if synton else 'disabled'}")
def configure(device):
if not device.settings.refresh():
print("error: failed to refresh settings")
return False
print_settings(device)
ports = gptp_ports(device)
print("\nAvailable gPTP ports (0 = disabled):")
print(" [0] Disabled")
for idx, name in ports:
print(f" [{idx}] {name}")
print("\nConfigure gPTP:")
profile = prompt_int(" Profile (0=Standard, 1=Automotive)", 1, 0, 1)
role = prompt_int(" Role (0=Disabled, 1=Passive, 2=Master, 3=Slave)", 3, 0, 3)
port = prompt_int(" Enabled port index", 0, 0, 19)
synton = prompt_int(" Clock syntonization (0=disabled, 1=enabled)", 0, 0, 1)
permanent = prompt_int("\nSave to EEPROM? (0=temporary, 1=permanent)", 0, 0, 1)
s = device.settings
profile_enum = list(PROFILE_NAMES.keys())[profile]
role_enum = list(ROLE_NAMES.keys())[role]
s.set_gptp_profile(profile_enum)
s.set_gptp_role(role_enum)
s.set_gptp_enabled_port(port)
s.set_gptp_clock_syntonization_enabled(bool(synton))
if not s.apply(not permanent):
print("error: failed to apply settings")
return False
print_settings(device)
return True
def main():
devices = icsneopy.find_all_devices()
if not devices:
print("error: no devices found")
return 1
gptp_devices = [d for d in devices if d.settings.get_gptp_profile() is not None]
if not gptp_devices:
print("error: no gPTP-capable devices found")
return 1
print("Available gPTP-capable devices:")
for i, d in enumerate(gptp_devices, 1):
print(f" [{i}] {d}")
choice = prompt_int("Select device", 1, 1, len(gptp_devices))
device = gptp_devices[choice - 1]
print(f"\nOpening {device}... ", end="", flush=True)
if not device.open():
print("failed")
return 1
print("OK")
try:
ok = configure(device)
finally:
print(f"\nClosing {device}")
device.close()
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())
+22 -32
View File
@@ -97,27 +97,27 @@ def display_t1s_settings(device, network):
"""Display T1S settings for a network.""" """Display T1S settings for a network."""
print(f"\t{network} T1S Settings:") print(f"\t{network} T1S Settings:")
settings = device.get_settings() settings = device.settings
if not settings: if not settings:
print("\t Unable to read settings") print("\t Unable to read settings")
return return
print(f"\t PLCA Enabled: {opt_to_string(settings.get_t1s_plca_enabled(network))}") print(f"\t PLCA Enabled: {opt_to_string(settings.is_t1s_plca_enabled(network))}")
print(f"\t Local ID: {opt_to_string(settings.get_t1s_local_id(network))}") print(f"\t Local ID: {opt_to_string(settings.get_t1s_local_id(network))}")
print(f"\t Max Nodes: {opt_to_string(settings.get_t1s_max_nodes(network))}") print(f"\t Max Nodes: {opt_to_string(settings.get_t1s_max_nodes(network))}")
print(f"\t TX Opp Timer: {opt_to_string(settings.get_t1s_tx_opp_timer(network))}") print(f"\t TX Opp Timer: {opt_to_string(settings.get_t1s_tx_opp_timer(network))}")
print(f"\t Max Burst: {opt_to_string(settings.get_t1s_max_burst(network))}") print(f"\t Max Burst: {opt_to_string(settings.get_t1s_max_burst(network))}")
print(f"\t Burst Timer: {opt_to_string(settings.get_t1s_burst_timer(network))}") print(f"\t Burst Timer: {opt_to_string(settings.get_t1s_burst_timer(network))}")
term_enabled = settings.get_t1s_termination_enabled(network) term_enabled = settings.is_t1s_termination_enabled(network)
if term_enabled is not None: if term_enabled is not None:
print(f"\t Termination: {opt_to_string(term_enabled)}") print(f"\t Termination: {opt_to_string(term_enabled)}")
local_id_alt = settings.get_t1s_local_id_alternate(network) local_id_alt = settings.get_t1s_local_id_alternate(network)
if local_id_alt is not None: if local_id_alt is not None:
print(f"\t Local ID Alternate: {opt_to_string(local_id_alt)}") print(f"\t Local ID Alternate: {opt_to_string(local_id_alt)}")
print(f"\t Bus Dec Beacons: {opt_to_string(settings.get_t1s_bus_decoding_beacons_enabled(network))}") print(f"\t Bus Dec Beacons: {opt_to_string(settings.is_t1s_bus_decoding_beacons_enabled(network))}")
print(f"\t Bus Dec All: {opt_to_string(settings.get_t1s_bus_decoding_all_enabled(network))}") print(f"\t Bus Dec All: {opt_to_string(settings.is_t1s_bus_decoding_all_enabled(network))}")
multi_id_mask = settings.get_t1s_multi_id_enable_mask(network) multi_id_mask = settings.get_t1s_multi_id_enable_mask(network)
if multi_id_mask is not None: if multi_id_mask is not None:
@@ -138,7 +138,7 @@ def configure_t1s_network(device, network):
print(f"Configuring T1S Network: {network}") print(f"Configuring T1S Network: {network}")
print("=" * 70) print("=" * 70)
settings = device.get_settings() settings = device.settings
if not settings: if not settings:
print("Unable to read settings") print("Unable to read settings")
return return
@@ -162,7 +162,7 @@ def configure_t1s_network(device, network):
burst_timer = get_uint16_input("Burst Timer (0-65535)", 64) burst_timer = get_uint16_input("Burst Timer (0-65535)", 64)
settings.set_t1s_burst_timer(network, burst_timer) settings.set_t1s_burst_timer(network, burst_timer)
if settings.get_t1s_termination_enabled(network) is not None: if settings.is_t1s_termination_enabled(network) is not None:
print("\n--- Termination Settings ---") print("\n--- Termination Settings ---")
term_enabled = get_user_confirmation("Enable Termination") term_enabled = get_user_confirmation("Enable Termination")
settings.set_t1s_termination(network, term_enabled) settings.set_t1s_termination(network, term_enabled)
@@ -187,10 +187,7 @@ def configure_t1s_network(device, network):
multi_id = get_uint8_input(f" Multi-ID [{i}]", 0) multi_id = get_uint8_input(f" Multi-ID [{i}]", 0)
settings.set_t1s_multi_id(network, i, multi_id) settings.set_t1s_multi_id(network, i, multi_id)
if not device.set_settings(settings): print(f"\n[OK] Configuration staged for {network}")
print("✗ Failed to update device settings")
else:
print(f"\n✓ Configuration complete for {network}")
def main(): def main():
@@ -217,7 +214,7 @@ def main():
device = None device = None
for d in devices: for d in devices:
if d.get_type() == icsneopy.DeviceType.RADComet3: if d.get_type().get_device_type() == icsneopy.DeviceType.Enum.RADComet3:
device = d device = d
break break
@@ -233,24 +230,17 @@ def main():
print("\nOpening device... ", end="", flush=True) print("\nOpening device... ", end="", flush=True)
if not device.open(): if not device.open():
print("✗ Failed") print("FAIL")
return 1 return 1
print("") print("OK")
candidate_networks = [ settings = device.settings
icsneopy.Network.NetID.AE_01, icsneopy.Network.NetID.AE_02,
icsneopy.Network.NetID.AE_03, icsneopy.Network.NetID.AE_04,
icsneopy.Network.NetID.AE_05, icsneopy.Network.NetID.AE_06,
icsneopy.Network.NetID.AE_07, icsneopy.Network.NetID.AE_08,
icsneopy.Network.NetID.AE_09, icsneopy.Network.NetID.AE_10
]
settings = device.get_settings()
t1s_networks = [] t1s_networks = []
for net_id in candidate_networks: for net in device.get_supported_tx_networks():
local_id = settings.get_t1s_local_id(net_id) if net.get_type() != icsneopy.Network.Type.AutomotiveEthernet:
if local_id is not None: continue
t1s_networks.append(net_id) if settings.get_t1s_local_id(net) is not None:
t1s_networks.append(net)
if not t1s_networks: if not t1s_networks:
print("No T1S networks found on this device") print("No T1S networks found on this device")
@@ -273,7 +263,7 @@ def main():
print("\nNo networks selected for configuration.") print("\nNo networks selected for configuration.")
print("Closing device... ", end="", flush=True) print("Closing device... ", end="", flush=True)
device.close() device.close()
print("") print("OK")
return 0 return 0
print(f"\nConfiguring {len(networks_to_config)} network{'s' if len(networks_to_config) != 1 else ''}...") print(f"\nConfiguring {len(networks_to_config)} network{'s' if len(networks_to_config) != 1 else ''}...")
@@ -285,14 +275,14 @@ def main():
save_to_eeprom = get_user_confirmation("Save settings to EEPROM (permanent)?") save_to_eeprom = get_user_confirmation("Save settings to EEPROM (permanent)?")
print("=" * 70) print("=" * 70)
settings = device.get_settings() settings = device.settings
print(f"\nApplying settings{' to EEPROM' if save_to_eeprom else ' temporarily'}... ", end="", flush=True) print(f"\nApplying settings{' to EEPROM' if save_to_eeprom else ' temporarily'}... ", end="", flush=True)
success = settings.apply(not save_to_eeprom) success = settings.apply(not save_to_eeprom)
if not success: if not success:
print("✗ Failed") print("FAIL")
device.close() device.close()
return 1 return 1
print("") print("OK")
print("\n" + "-" * 70) print("\n" + "-" * 70)
print("Updated T1S Settings:") print("Updated T1S Settings:")
@@ -302,7 +292,7 @@ def main():
print("Closing device... ", end="", flush=True) print("Closing device... ", end="", flush=True)
device.close() device.close()
print("") print("OK")
except KeyboardInterrupt: except KeyboardInterrupt:
print("\n\nInterrupted by user") print("\n\nInterrupted by user")
+43 -34
View File
@@ -38,7 +38,7 @@ def get_user_confirmation(prompt):
def configure_t1s_decoding(device, network, enable_symbols, enable_beacons): def configure_t1s_decoding(device, network, enable_symbols, enable_beacons):
"""Configure T1S bus decoding settings.""" """Configure T1S bus decoding settings."""
settings = device.get_settings() settings = device.settings
if not settings: if not settings:
raise RuntimeError("Failed to get device settings") raise RuntimeError("Failed to get device settings")
@@ -46,21 +46,15 @@ def configure_t1s_decoding(device, network, enable_symbols, enable_beacons):
if not settings.set_t1s_bus_decoding_all(network, enable_symbols): if not settings.set_t1s_bus_decoding_all(network, enable_symbols):
raise RuntimeError("Failed to set T1S symbol decoding") raise RuntimeError("Failed to set T1S symbol decoding")
if enable_symbols: print(f" [{'X' if enable_symbols else ' '}] Decoding of all T1S symbols")
print(" ✓ Enabled decoding of all T1S symbols")
else:
print(" • T1S symbol decoding disabled")
if not settings.set_t1s_bus_decoding_beacons(network, enable_beacons): if not settings.set_t1s_bus_decoding_beacons(network, enable_beacons):
raise RuntimeError("Failed to set T1S beacon decoding") raise RuntimeError("Failed to set T1S beacon decoding")
if enable_beacons: print(f" [{'X' if enable_beacons else ' '}] T1S beacon decoding")
print(" ✓ Enabled T1S beacon decoding")
else:
print(" • T1S beacon decoding disabled")
if not device.set_settings(settings): if not settings.apply(True):
raise RuntimeError("Failed to apply settings to device") raise RuntimeError("Failed to apply settings to device")
print(" Settings applied successfully") print(" [OK] Settings applied successfully")
def setup_symbol_monitoring(device, network): def setup_symbol_monitoring(device, network):
@@ -79,18 +73,18 @@ def setup_symbol_monitoring(device, network):
if not isinstance(msg, icsneopy.EthernetMessage): if not isinstance(msg, icsneopy.EthernetMessage):
return return
if not msg.isT1S: if not msg.t1s:
return return
timestamp_ms = msg.timestamp / 1000000.0 timestamp_ms = msg.timestamp / 1000000.0
if msg.isT1SSymbol: if msg.t1s.isSymbol:
num_symbols = len(msg.data) num_symbols = len(msg.data)
print(f"[{timestamp_ms:12.3f} ms] T1S Symbols", end="") print(f"[{timestamp_ms:12.3f} ms] T1S Symbols", end="")
if num_symbols > 0: if num_symbols > 0:
print(f" ({num_symbols} symbol{'s' if num_symbols > 1 else ''})", end="") print(f" ({num_symbols} symbol{'s' if num_symbols > 1 else ''})", end="")
print(f" | Node ID: {msg.t1sNodeId}") print(f" | Node ID: {msg.t1s.nodeId}")
for i, symbol_value in enumerate(msg.data): for i, symbol_value in enumerate(msg.data):
symbol_name = T1SSymbol.get_name(symbol_value) symbol_name = T1SSymbol.get_name(symbol_value)
@@ -105,8 +99,8 @@ def setup_symbol_monitoring(device, network):
print(f" [{i}] {symbol_name:10s} = 0x{symbol_value:02X}") print(f" [{i}] {symbol_name:10s} = 0x{symbol_value:02X}")
if num_symbols == 0 and msg.t1sSymbolType != 0: if num_symbols == 0 and msg.t1s.symbolType != 0:
symbol_value = msg.t1sSymbolType symbol_value = msg.t1s.symbolType
symbol_name = T1SSymbol.get_name(symbol_value) symbol_name = T1SSymbol.get_name(symbol_value)
state['symbol_count'] += 1 state['symbol_count'] += 1
@@ -119,22 +113,22 @@ def setup_symbol_monitoring(device, network):
print(f" {symbol_name:10s} = 0x{symbol_value:02X} (from t1sSymbolType field)") print(f" {symbol_name:10s} = 0x{symbol_value:02X} (from t1sSymbolType field)")
elif msg.isT1SBurst: elif msg.t1s.isBurst:
state['burst_count'] += 1 state['burst_count'] += 1
print(f"[{timestamp_ms:12.3f} ms] BURST | " print(f"[{timestamp_ms:12.3f} ms] BURST | "
f"Node ID: {msg.t1sNodeId} | " f"Node ID: {msg.t1s.nodeId} | "
f"Burst Count: {msg.t1sBurstCount}") f"Burst Count: {msg.t1s.burstCount}")
elif msg.isT1SWake: elif msg.t1s.isWake:
state['wake_count'] += 1 state['wake_count'] += 1
print(f"[{timestamp_ms:12.3f} ms] WAKE signal detected | " print(f"[{timestamp_ms:12.3f} ms] WAKE signal detected | "
f"Node ID: {msg.t1sNodeId}") f"Node ID: {msg.t1s.nodeId}")
else: else:
state['data_frame_count'] += 1 state['data_frame_count'] += 1
print(f"[{timestamp_ms:12.3f} ms] T1S Data Frame | " print(f"[{timestamp_ms:12.3f} ms] T1S Data Frame | "
f"Length: {len(msg.data)} bytes | " f"Length: {len(msg.data)} bytes | "
f"Node ID: {msg.t1sNodeId}") f"Node ID: {msg.t1s.nodeId}")
if msg.data and len(msg.data) > 0: if msg.data and len(msg.data) > 0:
preview = ' '.join([f"{b:02X}" for b in msg.data[:16]]) preview = ' '.join([f"{b:02X}" for b in msg.data[:16]])
@@ -142,7 +136,7 @@ def setup_symbol_monitoring(device, network):
preview += " ..." preview += " ..."
print(f" Data: {preview}") print(f" Data: {preview}")
frame_filter = icsneopy.MessageFilter(network) frame_filter = icsneopy.MessageFilter(network.get_net_id())
callback = icsneopy.MessageCallback(symbol_handler, frame_filter) callback = icsneopy.MessageCallback(symbol_handler, frame_filter)
device.add_message_callback(callback) device.add_message_callback(callback)
@@ -175,7 +169,6 @@ def main():
device = None device = None
try: try:
MONITOR_NETWORK = icsneopy.Network.NetID.AE_02
MONITOR_DURATION = 30 MONITOR_DURATION = 30
print("\n" + "=" * 70) print("\n" + "=" * 70)
@@ -197,7 +190,7 @@ def main():
device = None device = None
for d in devices: for d in devices:
if d.get_type() == icsneopy.DeviceType.RADComet3: if d.get_type().get_device_type() == icsneopy.DeviceType.Enum.RADComet3:
device = d device = d
break break
@@ -220,28 +213,44 @@ def main():
print("\nOpening device... ", end="", flush=True) print("\nOpening device... ", end="", flush=True)
if not device.open(): if not device.open():
print("✗ Failed") print("FAIL")
return 1 return 1
print("") print("OK")
print("Enabling message polling... ", end="", flush=True) print("Enabling message polling... ", end="", flush=True)
if not device.enable_message_polling(): if not device.enable_message_polling():
print("✗ Failed") print("FAIL")
device.close() device.close()
return 1 return 1
device.set_polling_message_limit(100000) device.set_polling_message_limit(100000)
print("") print("OK")
configure_t1s_decoding(device, MONITOR_NETWORK, enable_symbols, enable_beacons) monitor_network = None
settings = device.settings
for net in device.get_supported_rx_networks():
if net.get_type() != icsneopy.Network.Type.AutomotiveEthernet:
continue
if settings.get_t1s_local_id(net) is not None:
monitor_network = net
break
if monitor_network is None:
print("No T1S network found on this device")
device.close()
return 1
print(f"Monitoring network: {monitor_network}")
configure_t1s_decoding(device, monitor_network, enable_symbols, enable_beacons)
print("Going online... ", end="", flush=True) print("Going online... ", end="", flush=True)
if not device.go_online(): if not device.go_online():
print("✗ Failed") print("FAIL")
device.close() device.close()
return 1 return 1
print("") print("OK")
state = setup_symbol_monitoring(device, MONITOR_NETWORK) state = setup_symbol_monitoring(device, monitor_network)
print("\n" + "-" * 70) print("\n" + "-" * 70)
print(f"Monitoring T1S traffic for {MONITOR_DURATION} seconds...") print(f"Monitoring T1S traffic for {MONITOR_DURATION} seconds...")
@@ -256,7 +265,7 @@ def main():
print("Closing device... ", end="", flush=True) print("Closing device... ", end="", flush=True)
device.close() device.close()
time.sleep(0.1) time.sleep(0.1)
print("") print("OK")
print_statistics(state) print_statistics(state)
+13 -5
View File
@@ -19,6 +19,7 @@ typedef struct {
#include <chrono> #include <chrono>
#include <string> #include <string>
#include <ostream> #include <ostream>
#include <memory>
namespace icsneo { namespace icsneo {
@@ -165,6 +166,7 @@ public:
ServdPollError = ServdBindError + 8, ServdPollError = ServdBindError + 8,
ServdNoDataError = ServdBindError + 9, ServdNoDataError = ServdBindError + 9,
ServdJoinMulticastError = ServdBindError + 10, ServdJoinMulticastError = ServdBindError + 10,
ServdNotReachable = ServdBindError + 11,
// DXX // DXX
DXXErrorSys = 0x6100, DXXErrorSys = 0x6100,
@@ -184,19 +186,25 @@ public:
Error = 0x30 Error = 0x30
}; };
APIEvent() : eventStruct({}), serial(), timepoint(), device(nullptr) {} APIEvent() : eventStruct({}), serial(), timepoint() {}
APIEvent(APIEvent::Type event, APIEvent::Severity severity, const Device* device = nullptr); APIEvent(APIEvent::Type event, APIEvent::Severity severity, std::weak_ptr<Device> device = {});
const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; } const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; }
Type getType() const noexcept { return Type(eventStruct.eventNumber); } Type getType() const noexcept { return Type(eventStruct.eventNumber); }
Severity getSeverity() const noexcept { return Severity(eventStruct.severity); } Severity getSeverity() const noexcept { return Severity(eventStruct.severity); }
std::string getDescription() const noexcept { return std::string(eventStruct.description); } std::string getDescription() const noexcept { return std::string(eventStruct.description); }
const Device* getDevice() const noexcept { return device; } // Will return nullptr if this is an API-wide event const Device* getDevice() const noexcept { // Will return nullptr if this is an API-wide event
auto shared = device.lock();
return (shared) ? shared.get() : nullptr;
}
EventTimePoint getTimestamp() const noexcept { return timepoint; } EventTimePoint getTimestamp() const noexcept { return timepoint; }
void downgradeFromError() noexcept; void downgradeFromError() noexcept;
bool isForDevice(const Device* forDevice) const noexcept { return forDevice == device; } bool isForDevice(const Device* forDevice) const noexcept {
auto shared = device.lock();
return shared && (shared.get() == forDevice);
}
bool isForDevice(std::string serial) const noexcept; bool isForDevice(std::string serial) const noexcept;
// As opposed to getDescription, this will also add text such as "neoVI FIRE 2 CY2468 Error: " to fully describe the problem // As opposed to getDescription, this will also add text such as "neoVI FIRE 2 CY2468 Error: " to fully describe the problem
@@ -212,7 +220,7 @@ private:
neoevent_t eventStruct; neoevent_t eventStruct;
std::string serial; std::string serial;
EventTimePoint timepoint; EventTimePoint timepoint;
const Device* device; std::weak_ptr<Device> device;
void init(APIEvent::Type event, APIEvent::Severity); void init(APIEvent::Type event, APIEvent::Severity);
}; };

Some files were not shown because too many files have changed in this diff Show More