Author SHA1 Message Date
Kyle Schwarz a9157c82e5 Add device sharing support 2022-12-13 11:46:32 -05:00
283 changed files with 4374 additions and 20451 deletions
-5
View File
@@ -10,8 +10,3 @@ third-party/concurrentqueue/tests
*.bak
.vs
.cache
*.wav
*.orig
examples/csharp/bin
examples/csharp/obj
test/system
+9 -377
View File
@@ -1,14 +1,6 @@
variables:
DEBIAN_FRONTEND: noninteractive
stages:
- build
- unit_test
- hardware_test
#-------------------------------------------------------------------------------
# Windows
#-------------------------------------------------------------------------------
- test
build windows/x64:
stage: build
@@ -22,17 +14,17 @@ build windows/x64:
tags:
- icsneo-windows
unit_test windows/x64:
stage: unit_test
test windows/x64:
stage: test
script:
- build\libicsneo-unit-tests.exe
- build\libicsneo-tests.exe
dependencies:
- build windows/x64
needs:
- build windows/x64
tags:
- icsneo-windows
timeout: 5m
timeout: 3m
build windows/x86:
stage: build
@@ -46,374 +38,14 @@ build windows/x86:
tags:
- icsneo-windows
unit_test windows/x86:
stage: unit_test
test windows/x86:
stage: test
script:
- build\libicsneo-unit-tests.exe
- build\libicsneo-tests.exe
dependencies:
- build windows/x86
needs:
- build windows/x86
tags:
- icsneo-windows
timeout: 5m
#-------------------------------------------------------------------------------
# Ubuntu
#-------------------------------------------------------------------------------
.build_linux_ubuntu_gcc: &build_linux_ubuntu_gcc
stage: build
script:
- apt update -y
- apt upgrade -y
- apt install -y g++ ninja-build cmake libusb-1.0-0-dev libpcap-dev git
- sh ci/build-posix.sh
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- linux-build
.test_linux_ubuntu_gcc: &test_linux_ubuntu_gcc
stage: unit_test
script:
- apt update -y
- apt upgrade -y
- apt install -y libusb-1.0-0-dev libpcap-dev
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
.build_linux_ubuntu_clang: &build_linux_ubuntu_clang
stage: build
script:
- apt update -y
- apt upgrade -y
- apt install -y clang lld ninja-build cmake libusb-1.0-0-dev libpcap-dev git
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- linux-build
.test_linux_ubuntu_clang: &test_linux_ubuntu_clang
stage: unit_test
script:
- apt update -y
- apt upgrade -y
- apt install -y libusb-1.0-0-dev libpcap-dev
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
build linux/ubuntu/2004/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:20.04
unit_test linux/ubuntu/2004/amd64/gcc:
<<: *test_linux_ubuntu_gcc
image: ubuntu:20.04
dependencies:
- build linux/ubuntu/2004/amd64/gcc
needs:
- build linux/ubuntu/2004/amd64/gcc
build linux/ubuntu/2004/amd64/clang:
<<: *build_linux_ubuntu_clang
image: ubuntu:20.04
unit_test linux/ubuntu/2004/amd64/clang:
<<: *test_linux_ubuntu_clang
image: ubuntu:20.04
dependencies:
- build linux/ubuntu/2004/amd64/clang
needs:
- build linux/ubuntu/2004/amd64/clang
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
#-------------------------------------------------------------------------------
# Fedora
#-------------------------------------------------------------------------------
.build_linux_fedora_gcc: &build_linux_fedora_gcc
stage: build
cache:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel git
- sh ci/build-posix.sh
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- linux-build
.test_linux_fedora_gcc: &test_linux_fedora_gcc
stage: unit_test
cache:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
.build_linux_fedora_clang: &build_linux_fedora_clang
stage: build
cache:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y clang lld libpcap-devel cmake ninja-build libusb1-devel git
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- linux-build
.test_linux_fedora_clang: &test_linux_fedora_clang
stage: unit_test
cache:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
build linux/fedora/37/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:37
unit_test linux/fedora/37/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:37
dependencies:
- build linux/fedora/37/amd64/gcc
needs:
- build linux/fedora/37/amd64/gcc
build linux/fedora/37/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:37
unit_test linux/fedora/37/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:37
dependencies:
- build linux/fedora/37/amd64/clang
needs:
- build linux/fedora/37/amd64/clang
build linux/fedora/38/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:38
unit_test linux/fedora/38/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:38
dependencies:
- build linux/fedora/38/amd64/gcc
needs:
- build linux/fedora/38/amd64/gcc
build linux/fedora/38/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:38
unit_test linux/fedora/38/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:38
dependencies:
- build linux/fedora/38/amd64/clang
needs:
- build linux/fedora/38/amd64/clang
build linux/fedora/39/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:39
unit_test linux/fedora/39/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:39
dependencies:
- build linux/fedora/39/amd64/gcc
needs:
- build linux/fedora/39/amd64/gcc
build linux/fedora/39/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:39
unit_test linux/fedora/39/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:39
dependencies:
- build linux/fedora/39/amd64/clang
needs:
- build linux/fedora/39/amd64/clang
.hw_test: &hw_test
stage: hardware_test
tags:
- libicsneo_hil
timeout: 5m
script:
- echo $GUEST_OS_TAG
- echo $DEVICE_PORT
- /opt/libvirt-driver/prepare.sh
- /opt/libvirt-driver/run.sh
after_script:
- /opt/libvirt-driver/cleanup.sh
.fedora38_needs: &fedora38_needs
needs:
- job: build linux/fedora/38/amd64/clang
artifacts: true
hardware_test fedora38-red2:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: ETH_A
hardware_test fedora38-vcan42:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: USB_D
hardware_test fedora38-fire3:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: ETH_B
hardware_test fedora38-vcan42-EL:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: USB_C
.ubuntu2204_needs: &ubuntu2204_needs
needs:
- job: build linux/ubuntu/2204/amd64/clang
artifacts: true
hardware_test ubuntu2204-red2:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: ETH_A
hardware_test ubuntu2204-vcan42:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: USB_D
hardware_test ubuntu2204-fire3:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: ETH_B
hardware_test ubuntu2204-vcan42-EL:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: USB_C
.win10_needs: &win10_needs
needs:
- job: build windows/x64
artifacts: true
hardware_test win10-red2:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: ETH_A
hardware_test win10-vcan42:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: USB_D
hardware_test win10-fire3:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: ETH_B
hardware_test win10-vcan42-EL:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: USB_C
timeout: 3m
+50 -164
View File
@@ -1,21 +1,16 @@
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.2)
project(libicsneo VERSION 0.3.0)
cmake_policy(SET CMP0074 NEW)
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()
option(LIBICSNEO_BUILD_UNIT_TESTS "Build unit tests." OFF)
option(LIBICSNEO_BUILD_SYSTEM_TESTS "Build system tests." OFF)
option(LIBICSNEO_BUILD_TESTS "Build all tests." OFF)
option(LIBICSNEO_BUILD_DOCS "Build documentation. Don't use in Visual Studio." OFF)
option(LIBICSNEO_BUILD_EXAMPLES "Build examples." ON)
option(LIBICSNEO_BUILD_ICSNEOC "Build dynamic C library" ON)
option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC "Build static icsnVC40 compatibility library" ON)
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
option(LIBICSNEO_USE_DEVICE_SHARING "Interact with devices through the sharing server" ON)
# Device Drivers
# You almost certainly don't want firmio for your build,
# it is only relevant for communication between Linux and
@@ -24,16 +19,14 @@ option(LIBICSNEO_ENABLE_FIRMIO "Enable communication between Linux and CoreMini
option(LIBICSNEO_ENABLE_RAW_ETHERNET "Enable devices which communicate over raw ethernet" ON)
option(LIBICSNEO_ENABLE_CDCACM "Enable devices which communicate over USB CDC ACM" ON)
option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX" ON)
option(LIBICSNEO_ENABLE_TCP "Enable devices which communicate over TCP" OFF)
option(LIBICSNEO_ENABLE_FTD3XX "Enable devices which communicate over USB FTD3XX" ON)
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
include(GNUInstallDirs)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Enable Warnings
if(MSVC)
@@ -50,8 +43,6 @@ else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
find_package(Threads REQUIRED)
# doxygen
find_package(Doxygen)
if(DOXYGEN_FOUND)
@@ -124,6 +115,11 @@ if(WIN32)
platform/windows/vcp.cpp
)
endif()
list(APPEND PLATFORM_SRC
platform/windows/sharedmemory.cpp
platform/windows/sharedsemaphore.cpp
)
else() # Darwin or Linux
set(PLATFORM_SRC)
@@ -166,59 +162,10 @@ else() # Darwin or Linux
endif()
endif()
endif()
endif()
if(LIBICSNEO_ENABLE_FTD3XX)
if(NOT FTD3XX_ROOT) # allow system override
include(FetchContent)
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-x64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ee1289cdd5023de67275aaa1712e8e32e73e825a8392efb0a76f161e357fbdc9")
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-i686.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=bcd31f5c3cb39ecb9b09db5ce722bd79de6e0f813130e7b8f2c86879a60d8ff0")
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-macos-universal2.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=74c0d35f04242d0841532c6325eb2932b8f627e3c395382a15d9f39974d27a90")
elseif(UNIX)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-x64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=a9dc7eb6948c8977fbd79f6700bec6f882d3da5667aea8f2175b8d1f6f08e456")
endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-aarch64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=da4b90ea1cbb905874cd159ad2ab8c1bdde65cc22b3aa55bf2b5fd85ca6efd22")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-armhf.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=d813008117422cae958f7c71a065cdac0d31dca3b24809d3ab5e13604a9c3fb1")
endif()
endif()
endif()
if(NOT LIBICSNEO_FTD3XX_URL)
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
endif()
FetchContent_Declare(
ftdi3xx
URL ${LIBICSNEO_FTD3XX_URL}
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
)
FetchContent_GetProperties(ftdi3xx)
if(NOT ftdi3xx_POPULATED)
FetchContent_Populate(ftdi3xx)
endif()
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
endif()
find_package(FTD3XX REQUIRED)
list(APPEND PLATFORM_SRC
platform/ftd3xx.cpp
)
endif()
if(LIBICSNEO_ENABLE_TCP)
list(APPEND PLATFORM_SRC
platform/tcp.cpp
platform/posix/sharedmemory.cpp
platform/posix/sharedsemaphore.cpp
)
endif()
@@ -237,13 +184,8 @@ endforeach()
set(SRC_FILES
communication/message/flexray/control/flexraycontrolmessage.cpp
communication/message/callback/streamoutput/a2bwavoutput.cpp
communication/message/a2bmessage.cpp
communication/message/apperrormessage.cpp
communication/message/neomessage.cpp
communication/message/ethphymessage.cpp
communication/message/linmessage.cpp
communication/message/livedatamessage.cpp
communication/message/tc10statusmessage.cpp
communication/packet/flexraypacket.cpp
communication/packet/canpacket.cpp
communication/packet/a2bpacket.cpp
@@ -251,17 +193,10 @@ set(SRC_FILES
communication/packet/versionpacket.cpp
communication/packet/iso9141packet.cpp
communication/packet/ethphyregpacket.cpp
communication/packet/livedatapacket.cpp
communication/packet/logicaldiskinfopacket.cpp
communication/packet/wivicommandpacket.cpp
communication/packet/i2cpacket.cpp
communication/packet/linpacket.cpp
communication/packet/mdiopacket.cpp
communication/packet/scriptstatuspacket.cpp
communication/packet/componentversionpacket.cpp
communication/packet/supportedfeaturespacket.cpp
communication/packet/genericbinarystatuspacket.cpp
communication/packet/hardwareinfopacket.cpp
communication/decoder.cpp
communication/encoder.cpp
communication/ethernetpacketizer.cpp
@@ -269,8 +204,9 @@ set(SRC_FILES
communication/multichannelcommunication.cpp
communication/communication.cpp
communication/driver.cpp
communication/livedata.cpp
communication/ringbuffer.cpp
communication/interprocessmailbox.cpp
communication/sdio.cpp
communication/socket.cpp
device/extensions/flexray/extension.cpp
device/extensions/flexray/controller.cpp
device/idevicesettings.cpp
@@ -284,22 +220,6 @@ set(SRC_FILES
disk/plasiondiskreaddriver.cpp
disk/extextractordiskreaddriver.cpp
disk/fat.cpp
disk/vsa/vsa.cpp
disk/vsa/vsa02.cpp
disk/vsa/vsa03.cpp
disk/vsa/vsa04.cpp
disk/vsa/vsa05.cpp
disk/vsa/vsa06.cpp
disk/vsa/vsa07.cpp
disk/vsa/vsa08.cpp
disk/vsa/vsa09.cpp
disk/vsa/vsa0b.cpp
disk/vsa/vsa0c.cpp
disk/vsa/vsa0d.cpp
disk/vsa/vsa0e.cpp
disk/vsa/vsa0f.cpp
disk/vsa/vsa6a.cpp
disk/vsa/vsaparser.cpp
${PLATFORM_SRC}
)
@@ -360,9 +280,7 @@ target_include_directories(icsneocpp
${CMAKE_CURRENT_SOURCE_DIR}/include
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
)
target_link_libraries(icsneocpp PUBLIC Threads::Threads)
set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
target_link_libraries(icsneocpp PUBLIC ${LIBICSNEO_EXTENSION_TARGETS})
if(LIBICSNEO_ENABLE_FIRMIO)
@@ -377,20 +295,17 @@ endif()
if(LIBICSNEO_ENABLE_FTDI)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
endif()
if(LIBICSNEO_ENABLE_FTD3XX)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTD3XX)
target_link_libraries(icsneocpp PRIVATE FTD3XX::FTD3XX)
if(LIBICSNEO_USE_DEVICE_SHARING)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_DEVICE_SHARING)
endif()
if(LIBICSNEO_ENABLE_TCP)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_TCP)
if(WIN32)
target_link_libraries(icsneocpp PRIVATE ws2_32 iphlpapi)
endif()
# socket
if(WIN32)
target_link_libraries(icsneocpp PRIVATE ws2_32)
endif()
# fatfs
add_subdirectory(third-party/fatfs)
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(icsneocpp PRIVATE fatfs)
# libftdi
@@ -407,6 +322,7 @@ if(LIBICSNEO_ENABLE_FTDI)
add_subdirectory(third-party/libftdi)
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
find_package(Threads)
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
@@ -430,6 +346,15 @@ if(LIBICSNEO_ENABLE_RAW_ETHERNET)
endif(WIN32)
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
endif()
# For SharedMemory and SharedSemaphore
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_link_libraries(icsneocpp PUBLIC rt)
endif()
if(LIBICSNEO_BUILD_ICSNEOC)
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
target_include_directories(icsneoc
@@ -454,7 +379,6 @@ if(LIBICSNEO_BUILD_ICSNEOC_STATIC)
)
target_link_libraries(icsneoc-static PUBLIC icsneocpp)
target_compile_features(icsneoc-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
target_compile_definitions(icsneoc-static PUBLIC ICSNEOC_BUILD_STATIC)
endif()
if(LIBICSNEO_BUILD_ICSNEOLEGACY)
@@ -475,27 +399,8 @@ if(LIBICSNEO_BUILD_ICSNEOLEGACY)
target_compile_features(icsneolegacy PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
endif()
if(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC)
add_library(icsneolegacy-static STATIC
api/icsneolegacy/icsneolegacy.cpp
api/icsneolegacy/icsneolegacyextra.cpp
api/icsneoc/icsneoc.cpp
)
target_include_directories(icsneolegacy-static
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(icsneolegacy-static PUBLIC icsneocpp)
target_compile_features(icsneolegacy-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
target_compile_definitions(icsneolegacy-static PUBLIC ICSNEOC_BUILD_STATIC)
endif()
# googletest
if(LIBICSNEO_BUILD_UNIT_TESTS)
if(LIBICSNEO_BUILD_TESTS)
if(WIN32)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
endif()
@@ -508,45 +413,26 @@ if(LIBICSNEO_BUILD_UNIT_TESTS)
include_directories("${gtest_SOURCE_DIR}/include")
endif()
add_executable(libicsneo-unit-tests
test/unit/main.cpp
test/unit/diskdriverreadtest.cpp
test/unit/diskdriverwritetest.cpp
test/unit/eventmanagertest.cpp
test/unit/ethernetpacketizertest.cpp
test/unit/i2cencoderdecodertest.cpp
test/unit/linencoderdecodertest.cpp
test/unit/a2bencoderdecodertest.cpp
test/unit/mdioencoderdecodertest.cpp
test/unit/livedataencoderdecodertest.cpp
test/unit/ringbuffertest.cpp
test/unit/apperrordecodertest.cpp
add_executable(libicsneo-tests
test/main.cpp
test/diskdriverreadtest.cpp
test/diskdriverwritetest.cpp
test/eventmanagertest.cpp
test/ethernetpacketizertest.cpp
test/interprocessmailboxtest.cpp
test/sockettest.cpp
test/i2cencoderdecodertest.cpp
test/a2bencoderdecodertest.cpp
)
target_link_libraries(libicsneo-unit-tests gtest gtest_main)
target_link_libraries(libicsneo-unit-tests icsneocpp)
target_link_libraries(libicsneo-tests gtest gtest_main)
target_link_libraries(libicsneo-tests icsneocpp)
target_include_directories(libicsneo-unit-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
target_include_directories(libicsneo-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
enable_testing()
add_test(NAME libicsneo-unit-test-suite COMMAND libicsneo-unit-tests)
endif()
if(LIBICSNEO_BUILD_SYSTEM_TESTS)
if(DEFINED ENV{LIBICSNEO_SYSTEM_TESTS})
include(FetchContent)
file(MAKE_DIRECTORY test/system)
FetchContent_Declare(
SystemTests
GIT_REPOSITORY $ENV{LIBICSNEO_SYSTEM_TESTS}
GIT_TAG main
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test/system
)
FetchContent_MakeAvailable(SystemTests)
else()
message("System test repo not defined!")
endif()
add_test(NAME libicsneo-test-suite COMMAND libicsneo-tests)
endif()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
+1 -16
View File
@@ -17,17 +17,6 @@
- RADA2B
- CAN works
- Ethernet works
- RADComet
- CAN works
- CAN FD works
- Ethernet works
- RADComet3
- CAN works
- Ethernet works
- neoVI Connect
- CAN works
- CAN FD works
- Ethernet works
- Connecting over USB
- ValueCAN 4 series
@@ -53,8 +42,4 @@
- CAN works
- RADA2B
- CAN works
- Ethernet works
- RADMoon3
- RADComet3
- CAN works
- Ethernet works
- Ethernet works
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2018-2024 Intrepid Control Systems, Inc.
Copyright 2018-2020 Intrepid Control Systems, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+3 -49
View File
@@ -110,67 +110,21 @@ for(size_t i = 0; i < messageCount; i++) {
icsneo_closeDevice(myDevice);
```
### Debugging
To enable debug printing set the `LIBICSNEO_PRINT_EVENTS` environmental variable to the desired `APIEvent::Severity` level, all `Event`s greater than or equal to that level will be printed to stderr. For example, to print all warnings and errors: `LIBICSNEO_PRINT_EVENTS=32`.
## Building from Source
### FTD3XX
Some devices require FTD3XX for USB communication so the [FTDI D3XX library](https://ftdichip.com/drivers/d3xx-drivers/) will be automatically downloaded and included. If you would like to use a system copy of D3XX instead you can set `FTD3XX_ROOT` to the path containing `f3d3xx.h` (`-DFTD3XX_ROOT=<path to directory containing ftd3xx.h>`).
### Windows
- Open a terminal and install the following:
```
winget install Microsoft.VisualStudio.2022.Community
winget install Kitware.CMake
winget install Git.Git
winget install Ninja-build.Ninja
```
- Reboot so cmake is in the system path
- Open a developer Powershell for VS2022 and run the following:
```
git clone https://github.com/intrepidcs/libicsneo
cd libicsneo
cmake -S . -B build -G "Ninja"
cmake --build build
# All dlls and libs are now inside the build directory
```
Building will require MSVC 2017 version 15.7 or newer and CMake to be installed.
### macOS
Getting the dependencies is easiest with the Homebrew package manager. You will also need XCode installed. You can then install CMake, an up-to-date version of GCC or Clang, and `libusb-1.0`.
### Linux
#### General Dependencies
The dependencies are as follows
- CMake 3.12 or above
- GCC 7 or above
- `libusb-1.0-0-dev`
- `libpcap0.8-dev`
- `build-essential` is recommended
#### Fedora
```
dnf install git @development-tools gcc-c++ libpcap-devel libusb1-devel cmake
```
#### Debian/Ubuntu
```
apt install git build-essential libpcap0.8-dev libusb-1.0-0-dev cmake
```
#### Building
```
git clone https://github.com/intrepidcs/libicsneo
cd libicsneo
cmake -S . -B build
cmake --build build
# Optional: Install globally:
cp *.so /usr/local/lib/
```
#### udev
If you'd like to be able to run programs that use this library without being root, consider using the included udev rules:
If you'd like to be able to run programs that use this library without being root, consider using the included udev rules
```
cp 99-intrepidcs.rules /etc/udev/rules.d/
udevadm control --reload-rules && udevadm trigger
$ sudo cp 99-intrepidcs.rules /etc/udev/rules.d/
```
+1 -69
View File
@@ -144,17 +144,8 @@ bool icsneo_closeDevice(const neodevice_t* device) {
if((*it).get() == device->device)
itemsToDelete.push_back(it);
}
for(auto it : itemsToDelete) {
// Move it back into connectable devices so we can open it again.
// Without this we will be unable to use/reopen the device due to
// icsneo_isValidNeoDevice / icsneo_openDevice checks against this
// container. Since its closed we are in a connectable state again.
// Notice: When we search again this will be cleaned up by
// icsneo_freeUnconnectedDevices()
connectableFoundDevices.push_back(*it);
// Remove it from the connected devices as we are no longer connected.
for(auto it : itemsToDelete)
connectedDevices.erase(it);
}
return true;
}
@@ -698,62 +689,3 @@ bool icsneo_setTerminationFor(const neodevice_t* device, neonetid_t netid, bool
return device->device->settings->setTerminationFor(Network(netid), enabled);
}
bool icsneo_getRTC(const neodevice_t* device, uint64_t* output)
{
if(!icsneo_isValidNeoDevice(device))
return false;
const std::optional<std::chrono::time_point<std::chrono::system_clock>> rtc = device->device->getRTC();
if(!rtc)
return false;
auto duration = std::chrono::duration_cast<std::chrono::seconds>(rtc->time_since_epoch());
*output = static_cast<uint64_t>(duration.count());
return true;
}
bool icsneo_setRTC(const neodevice_t* device, uint64_t input)
{
if(!icsneo_isValidNeoDevice(device))
return false;
std::chrono::seconds duration(input);
const std::chrono::system_clock::time_point time(duration);
return device->device->setRTC(time);
}
int icsneo_getDeviceStatus(const neodevice_t* device, void* status, size_t* size) {
if(!icsneo_isValidNeoDevice(device))
return false;
if(status == nullptr || size == nullptr)
return false;
std::shared_ptr<Message> msg = device->device->com->waitForMessageSync([&]() {
return device->device->com->sendCommand(Command::RequestStatusUpdate);
}, std::make_shared<MessageFilter>(Network::NetID::DeviceStatus), std::chrono::milliseconds(100));
if(!msg) // Did not receive a message
return false;
auto rawMessage = std::static_pointer_cast<RawMessage>(msg);
if(!rawMessage || (rawMessage->network.getNetID() != Network::NetID::DeviceStatus))
return false;
if(*size < rawMessage->data.size())
return false;
std::copy(rawMessage->data.begin(), rawMessage->data.end(), static_cast<uint8_t*>(status));
*size = rawMessage->data.size();
return true;
}
bool icsneo_isOnlineSupported(const neodevice_t* device) {
if(!icsneo_isValidNeoDevice(device))
return false;
return device->device->isOnlineSupported();
}
+1 -1
View File
@@ -29,7 +29,7 @@ BEGIN
VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", "icsneoc.dll"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2019"
VALUE "OriginalFilename", "icsneoc.dll"
VALUE "ProductName", "libicsneo"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+99 -163
View File
@@ -4,7 +4,7 @@
using namespace icsneo;
APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device) : eventStruct({}) {
APIEvent::APIEvent(Type type, Severity severity, const Device* device) : eventStruct({}) {
this->device = device;
if(device) {
serial = device->getSerial();
@@ -14,6 +14,17 @@ APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device)
init(type, severity);
}
APIEvent::APIEvent(neosocketevent_t evStruct, const Device* device)
{
this->device = device;
timepoint = EventClock::from_time_t(evStruct.timestamp);
serial = std::string(evStruct.serial);
eventStruct.eventNumber = evStruct.eventNumber;
eventStruct.severity = evStruct.severity;
eventStruct.description = DescriptionForType(APIEvent::getType());
eventStruct.timestamp = evStruct.timestamp;
}
void APIEvent::init(Type event, APIEvent::Severity severity) {
timepoint = EventClock::now();
eventStruct.description = DescriptionForType(event);
@@ -45,6 +56,15 @@ std::string APIEvent::describe() const noexcept {
return ss.str();
}
neosocketevent_t APIEvent::getNeoSocketEvent() const noexcept {
neosocketevent_t neoSocketEvent;
neoSocketEvent.eventNumber = eventStruct.eventNumber;
neoSocketEvent.severity = eventStruct.severity;
std::memcpy(neoSocketEvent.serial, eventStruct.serial, sizeof(eventStruct.serial));
neoSocketEvent.timestamp = eventStruct.timestamp;
return neoSocketEvent;
}
void APIEvent::downgradeFromError() noexcept {
eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning;
}
@@ -72,8 +92,6 @@ static constexpr const char* MESSAGE_MAX_LENGTH_EXCEEDED = "The message was too
static constexpr const char* VALUE_NOT_YET_PRESENT = "The value is not yet present.";
static constexpr const char* TIMEOUT = "The timeout was reached.";
static constexpr const char* WIVI_NOT_SUPPORTED = "Wireless neoVI functions are not supported on this device.";
static constexpr const char* RESTRICTED_ENTRY_FLAG = "Attempted to set a restricted flag in a Root Directory entry.";
static constexpr const char* NOT_SUPPORTED = "The requested feature is not supported.";
// Device Errors
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
@@ -115,11 +133,6 @@ 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_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* 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* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the 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.";
// Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
@@ -132,58 +145,32 @@ static constexpr const char* DEVICE_IN_USE = "The device is currently in use by
static constexpr const char* PCAP_COULD_NOT_START = "The PCAP driver could not be started. Ethernet devices will not be found.";
static constexpr const char* PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
static constexpr const char* PACKET_DECODING = "There was an error decoding a packet from the device.";
static constexpr const char* SOCKET_FAILED_TO_OPEN = "Unable to open new socket.";
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* GETIFADDRS_ERROR = "A call to getifaddrs() failed.";
static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed.";
// FTD3XX
static constexpr const char* FT_OK = "FTD3XX success.";
static constexpr const char* FT_INVALID_HANDLE = "Invalid FTD3XX handle.";
static constexpr const char* FT_DEVICE_NOT_FOUND = "FTD3XX device not found.";
static constexpr const char* FT_DEVICE_NOT_OPENED = "FTD3XX device not opened.";
static constexpr const char* FT_IO_ERROR = "FTD3XX IO error.";
static constexpr const char* FT_INSUFFICIENT_RESOURCES = "Insufficient resources for FTD3XX.";
static constexpr const char* FT_INVALID_PARAMETER = "Invalid FTD3XX parameter.";
static constexpr const char* FT_INVALID_BAUD_RATE = "Invalid FTD3XX baud rate.";
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_ERASE = "FTD3XX device not opened for erase.";
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_WRITE = "FTD3XX not opened for write.";
static constexpr const char* FT_FAILED_TO_WRITE_DEVICE = "FTD3XX failed to write device.";
static constexpr const char* FT_EEPROM_READ_FAILED = "FTD3XX EEPROM read failed.";
static constexpr const char* FT_EEPROM_WRITE_FAILED = "FTD3XX EEPROM write failed.";
static constexpr const char* FT_EEPROM_ERASE_FAILED = "FTD3XX EEPROM erase failed.";
static constexpr const char* FT_EEPROM_NOT_PRESENT = "FTD3XX EEPROM not present.";
static constexpr const char* FT_EEPROM_NOT_PROGRAMMED = "FTD3XX EEPROM not programmed.";
static constexpr const char* FT_INVALID_ARGS = "Invalid FTD3XX arguments.";
static constexpr const char* FT_NOT_SUPPORTED = "FTD3XX not supported.";
static constexpr const char* FT_NO_MORE_ITEMS = "No more FTD3XX items.";
static constexpr const char* FT_TIMEOUT = "FTD3XX timeout.";
static constexpr const char* FT_OPERATION_ABORTED = "FTD3XX operation aborted.";
static constexpr const char* FT_RESERVED_PIPE = "Reserved FTD3XX pipe.";
static constexpr const char* FT_INVALID_CONTROL_REQUEST_DIRECTION = "Invalid FTD3XX control request direction.";
static constexpr const char* FT_INVALID_CONTROL_REQUEST_TYPE = "Invalid FTD3XX control request type.";
static constexpr const char* FT_IO_PENDING = "FTD3XX IO pending.";
static constexpr const char* FT_IO_INCOMPLETE = "FTD3XX IO incomplete.";
static constexpr const char* FT_HANDLE_EOF = "Handle FTD3XX EOF.";
static constexpr const char* FT_BUSY = "FTD3XX busy.";
static constexpr const char* FT_NO_SYSTEM_RESOURCES = "No FTD3XX system resources.";
static constexpr const char* FT_DEVICE_LIST_NOT_READY = "FTD3XX device list not ready.";
static constexpr const char* FT_DEVICE_NOT_CONNECTED = "FTD3XX device not connected.";
static constexpr const char* FT_INCORRECT_DEVICE_PATH = "Incorrect FTD3XX device path.";
static constexpr const char* FT_OTHER_ERROR = "Other FTD3XX error.";
// VSA
static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted.";
static constexpr const char* VSA_TIMESTAMP_NOT_FOUND = "Unable to find a VSA record with a valid timestamp.";
static constexpr const char* VSA_BUFFER_FORMAT_ERROR = "VSA record buffer is formatted incorrectly.";
static constexpr const char* VSA_MAX_READ_ATTEMPTS_REACHED = "Reached max attempts to read VSA records before exit.";
static constexpr const char* VSA_BYTE_PARSE_FAILURE = "Failure to parse record bytes from VSA buffer.";
static constexpr const char* VSA_EXTENDED_MESSAGE_ERROR = "Failure to parse extended message record sequence";
static constexpr const char* VSA_OTHER_ERROR = "Unknown error in VSA read API.";
static constexpr const char* SHARED_MEMORY_DATA_IS_NULL = "data() was called on invalid object.";
static constexpr const char* SHARED_MEMORY_FAILED_TO_CLOSE = "The server failed to close a shared memory location.";
static constexpr const char* SHARED_MEMORY_FAILED_TO_OPEN = "The server failed to open a shared memory location.";
static constexpr const char* SHARED_MEMORY_FAILED_TO_UNLINK = "The server failed to unlink a shared memory location.";
static constexpr const char* SHARED_MEMORY_FILE_TRUNCATE_ERROR = "The server failed to truncate shared memory file.";
static constexpr const char* SHARED_MEMORY_MAPPING_ERROR = "The server failed to map a shared memory file.";
static constexpr const char* SHARED_MEMORY_UNMAP_ERROR = "The server failed to unmap a shared memory file.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_CLOSE = "The server failed to close a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_OPEN = "The server failed to open a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_POST = "A post() call failed on a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_UNLINK = "The server failed to unlink a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_WAIT = "A wait() call failed on a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_POST = "post() was called on a shared semaphore that is not open.";
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT = "wait() was called on a shared semaphore that is not open.";
static constexpr const char* SOCKET_FAILED_CONNECT = "A socket connection was attempted but failed.";
static constexpr const char* SOCKET_FAILED_OPEN = "A socket failed to open.";
static constexpr const char* SOCKET_FAILED_CLOSE = "A socket failed to close.";
static constexpr const char* SOCKET_FAILED_READ = "A socket read operation failed.";
static constexpr const char* SOCKET_FAILED_WRITE = "A socket write operation failed.";
static constexpr const char* ACCEPTOR_FAILED_BIND = "A socket acceptor failed to bind.";
static constexpr const char* ACCEPTOR_FAILED_LISTEN = "A socket acceptor failed to listen.";
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* NO_ERROR_FOUND = "No errors found.";
static constexpr const char* INVALID = "An invalid internal error occurred.";
const char* APIEvent::DescriptionForType(Type type) {
switch(type) {
@@ -220,10 +207,6 @@ const char* APIEvent::DescriptionForType(Type type) {
return TIMEOUT;
case Type::WiVINotSupported:
return WIVI_NOT_SUPPORTED;
case Type::RestrictedEntryFlag:
return RESTRICTED_ENTRY_FLAG;
case Type::NotSupported:
return NOT_SUPPORTED;
// Device Errors
case Type::PollingMessageOverflow:
@@ -304,16 +287,7 @@ const char* APIEvent::DescriptionForType(Type type) {
return WIVI_UPLOAD_STACK_OVERFLOW;
case Type::A2BMessageIncompleteFrame:
return A2B_MESSAGE_INCOMPLETE_FRAME;
case Type::CoreminiUploadVersionMismatch:
return COREMINI_UPLOAD_VERSION_MISMATCH;
case Type::DiskNotConnected:
return DISK_NOT_CONNECTED;
case Type::UnexpectedResponse:
return UNEXPECTED_RESPONSE;
case Type::LINSettingsNotAvailable:
return LIN_SETTINGS_NOT_AVAILABLE;
case Type::ModeNotFound:
return MODE_NOT_FOUND;
// Transport Errors
case Type::FailedToRead:
return FAILED_TO_READ;
@@ -335,106 +309,58 @@ const char* APIEvent::DescriptionForType(Type type) {
return PCAP_COULD_NOT_FIND_DEVICES;
case Type::PacketDecodingError:
return PACKET_DECODING;
case Type::SocketFailedToOpen:
return SOCKET_FAILED_TO_OPEN;
case Type::FailedToBind:
return FAILED_TO_BIND;
case Type::ErrorSettingSocketOption:
return ERROR_SETTING_SOCKET_OPTION;
case Type::GetIfAddrsError:
return GETIFADDRS_ERROR;
case Type::SendToError:
return SEND_TO_ERROR;
// FTD3XX
case Type::FTOK:
return FT_OK;
case Type::FTInvalidHandle:
return FT_INVALID_HANDLE;
case Type::FTDeviceNotFound:
return FT_DEVICE_NOT_FOUND;
case Type::FTDeviceNotOpened:
return FT_DEVICE_NOT_OPENED;
case Type::FTIOError:
return FT_IO_ERROR;
case Type::FTInsufficientResources:
return FT_INSUFFICIENT_RESOURCES;
case Type::FTInvalidParameter:
return FT_INVALID_PARAMETER;
case Type::FTInvalidBaudRate:
return FT_INVALID_BAUD_RATE;
case Type::FTDeviceNotOpenedForErase:
return FT_DEVICE_NOT_OPENED_FOR_ERASE;
case Type::FTDeviceNotOpenedForWrite:
return FT_DEVICE_NOT_OPENED_FOR_WRITE;
case Type::FTFailedToWriteDevice:
return FT_FAILED_TO_WRITE_DEVICE;
case Type::FTEEPROMReadFailed:
return FT_EEPROM_READ_FAILED;
case Type::FTEEPROMWriteFailed:
return FT_EEPROM_WRITE_FAILED;
case Type::FTEEPROMEraseFailed:
return FT_EEPROM_ERASE_FAILED;
case Type::FTEEPROMNotPresent:
return FT_EEPROM_NOT_PRESENT;
case Type::FTEEPROMNotProgrammed:
return FT_EEPROM_NOT_PROGRAMMED;
case Type::FTInvalidArgs:
return FT_INVALID_ARGS;
case Type::FTNotSupported:
return FT_NOT_SUPPORTED;
case Type::FTNoMoreItems:
return FT_NO_MORE_ITEMS;
case Type::FTTimeout:
return FT_TIMEOUT;
case Type::FTOperationAborted:
return FT_OPERATION_ABORTED;
case Type::FTReservedPipe:
return FT_RESERVED_PIPE;
case Type::FTInvalidControlRequestDirection:
return FT_INVALID_CONTROL_REQUEST_DIRECTION;
case Type::FTInvalidControlRequestType:
return FT_INVALID_CONTROL_REQUEST_TYPE;
case Type::FTIOPending:
return FT_IO_PENDING;
case Type::FTIOIncomplete:
return FT_IO_INCOMPLETE;
case Type::FTHandleEOF:
return FT_HANDLE_EOF;
case Type::FTBusy:
return FT_BUSY;
case Type::FTNoSystemResources:
return FT_NO_SYSTEM_RESOURCES;
case Type::FTDeviceListNotReady:
return FT_DEVICE_LIST_NOT_READY;
case Type::FTDeviceNotConnected:
return FT_DEVICE_NOT_CONNECTED;
case Type::FTIncorrectDevicePath:
return FT_INCORRECT_DEVICE_PATH;
case Type::FTOtherError:
return FT_OTHER_ERROR;
// VSA
case Type::VSABufferCorrupted:
return VSA_BUFFER_CORRUPTED;
case Type::VSATimestampNotFound:
return VSA_TIMESTAMP_NOT_FOUND;
case Type::VSABufferFormatError:
return VSA_BUFFER_FORMAT_ERROR;
case Type::VSAMaxReadAttemptsReached:
return VSA_MAX_READ_ATTEMPTS_REACHED;
case Type::VSAByteParseFailure:
return VSA_BYTE_PARSE_FAILURE;
case Type::VSAExtendedMessageError:
return VSA_EXTENDED_MESSAGE_ERROR;
case Type::VSAOtherError:
return VSA_OTHER_ERROR;
// Device Sharing Server Events
case Type::SharedMemoryDataIsNull:
return SHARED_MEMORY_DATA_IS_NULL;
case Type::SharedMemoryFailedToClose:
return SHARED_MEMORY_FAILED_TO_CLOSE;
case Type::SharedMemoryFailedToOpen:
return SHARED_MEMORY_FAILED_TO_OPEN;
case Type::SharedMemoryFailedToUnlink:
return SHARED_MEMORY_FAILED_TO_UNLINK;
case Type::SharedMemoryFileTruncateError:
return SHARED_MEMORY_FILE_TRUNCATE_ERROR;
case Type::SharedMemoryMappingError:
return SHARED_MEMORY_MAPPING_ERROR;
case Type::SharedMemoryUnmapError:
return SHARED_MEMORY_UNMAP_ERROR;
case Type::SharedSemaphoreFailedToClose:
return SHARED_SEMAPHORE_FAILED_TO_CLOSE;
case Type::SharedSemaphoreFailedToOpen:
return SHARED_SEMAPHORE_FAILED_TO_OPEN;
case Type::SharedSemaphoreFailedToPost:
return SHARED_SEMAPHORE_FAILED_TO_POST;
case Type::SharedSemaphoreFailedToUnlink:
return SHARED_SEMAPHORE_FAILED_TO_UNLINK;
case Type::SharedSemaphoreFailedToWait:
return SHARED_SEMAPHORE_FAILED_TO_WAIT;
case Type::SharedSemaphoreNotOpenForPost:
return SHARED_SEMAPHORE_NOT_OPEN_FOR_POST;
case Type::SharedSemaphoreNotOpenForWait:
return SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT;
case Type::SocketFailedToOpen:
return SOCKET_FAILED_OPEN;
case Type::SocketFailedToClose:
return SOCKET_FAILED_CLOSE;
case Type::SocketFailedToConnect:
return SOCKET_FAILED_CONNECT;
case Type::SocketFailedToRead:
return SOCKET_FAILED_READ;
case Type::SocketFailedToWrite:
return SOCKET_FAILED_WRITE;
case Type::SocketAcceptorFailedToBind:
return ACCEPTOR_FAILED_BIND;
case Type::SocketAcceptorFailedToListen:
return ACCEPTOR_FAILED_LISTEN;
// Other Errors
case Type::TooManyEvents:
return TOO_MANY_EVENTS;
case Type::Unknown:
return UNKNOWN;
case Type::NoErrorFound:
return NO_ERROR_FOUND;
default:
return INVALID;
}
@@ -454,4 +380,14 @@ bool EventFilter::match(const APIEvent& event) const noexcept {
return false;
return true;
}
neosocketeventfilter_t EventFilter::getNeoSocketEventFilter() const noexcept {
neosocketeventfilter_t filterStruct;
filterStruct.eventNumber = static_cast<decltype(filterStruct.eventNumber)>(type);
filterStruct.severity = static_cast<decltype(filterStruct.severity)>(severity);
if((serial.length() + 1) == sizeof(filterStruct.serial)) {
std::memcpy(&filterStruct.serial[0], serial.c_str(), sizeof(filterStruct.serial));
}
return filterStruct;
}
+59 -38
View File
@@ -1,8 +1,11 @@
#include "icsneo/api/eventmanager.h"
#include "icsneo/api/event.h"
#include <memory>
#include <optional>
#include <iostream>
#include <cstdlib>
#include <thread>
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#endif
using namespace icsneo;
@@ -36,25 +39,6 @@ void EventManager::cancelErrorDowngradingOnCurrentThread() {
void EventManager::add(APIEvent event) {
if(destructing)
return;
static const auto printLevel = []() -> std::optional<uint8_t> {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
const auto level = std::getenv("LIBICSNEO_PRINT_EVENTS");
#ifdef _MSC_VER
#pragma warning(pop)
#endif
if(!level)
return std::nullopt;
try {
return (uint8_t)std::stoi(level);
} catch (std::invalid_argument const&) {
return std::nullopt;
}
}();
if(printLevel && (uint8_t)event.getSeverity() >= *printLevel)
std::cerr << event.describe() << std::endl;
if(event.getSeverity() == APIEvent::Severity::Error) {
// if the error was added on a thread that downgrades errors (non-user thread)
std::lock_guard<std::mutex> lk(downgradedThreadsMutex);
@@ -62,7 +46,7 @@ void EventManager::add(APIEvent event) {
if(i != downgradedThreads.end() && i->second) {
event.downgradeFromError();
{
std::lock_guard<std::mutex> eventsLock(eventsMutex);
std::lock_guard<std::mutex> eventsLock{eventsMutex};
addEventInternal(event);
} // free the lock so that callbacks may modify events
runCallbacks(event);
@@ -168,23 +152,60 @@ bool EventManager::isDowngradingErrorsOnCurrentThread() const {
return false;
}
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
std::optional<std::vector<neosocketevent_t>> EventManager::getServerEvents(const size_t& max)
{
auto socket = lockSocket();
if(!(socket.writeTyped<RPC>(RPC::GET_EVENTS) && socket.writeTyped<size_t>(max)))
return std::nullopt;
size_t count;
if(!socket.readTyped(count) || count < 0 || count > eventLimit)
return std::nullopt;
std::optional<std::vector<neosocketevent_t>> ret;
if(count == size_t(0))
return ret;
auto& eventStructs = ret.emplace(count);
if(!socket.read(eventStructs.data(), (eventStructs.size() * sizeof(neosocketevent_t))))
return std::nullopt;
return ret;
}
#endif // ICSNEO_ENABLE_DEVICE_SHARING
void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilter filter) {
std::lock_guard<std::mutex> lk(eventsMutex);
if(max == 0) // A limit of 0 indicates no limit
max = (size_t)-1;
size_t count = 0;
eventOutput.clear();
auto it = events.begin();
while(it != events.end()) {
if(filter.match(*it)) {
eventOutput.push_back(*it);
it = events.erase(it);
if(++count >= max)
break; // We now have as many written to output as we can
} else {
it++;
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
{
auto serverEvents = getServerEvents(max);
if(serverEvents) {
std::scoped_lock eventsLock{eventsMutex};
for(neosocketevent_t& event : *serverEvents) {
eventOutput.emplace_back(event);
}
if(max != 0u)
max -= eventOutput.size();
}
}
#endif
std::scoped_lock eventsLock{eventsMutex};
{
if(max == 0) // A limit of 0 indicates no limit
max = (size_t)-1;
size_t count = 0;
auto it = events.begin();
while(it != events.end()) {
if(filter.match(*it)) {
eventOutput.push_back(*it);
it = events.erase(it);
if(++count >= max)
break; // We now have as many written to output as we can
} else {
it++;
}
}
}
}
@@ -3,6 +3,8 @@
#include <Tchar.h>
//Basic Functions
FINDNEODEVICES icsneoFindNeoDevices;
OPENNEODEVICE icsneoOpenNeoDevice;
OPENDEVICE icsneoOpenDevice;
CLOSEPORT icsneoClosePort;
FREEOBJECT icsneoFreeObject;
@@ -132,6 +134,10 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
if((hAPIDLL = LoadLibrary(_T("icsneo40.dll"))) == NULL)
return false;
icsneoFindNeoDevices = (FINDNEODEVICES) GetProcAddress(hAPIDLL, "icsneoFindNeoDevices");
icsneoOpenNeoDevice = (OPENNEODEVICE) GetProcAddress(hAPIDLL, "icsneoOpenNeoDevice");
icsneoOpenDevice = (OPENDEVICE) GetProcAddress(hAPIDLL, "icsneoOpenDevice");
icsneoClosePort = (CLOSEPORT) GetProcAddress(hAPIDLL, "icsneoClosePort");
icsneoFreeObject = (FREEOBJECT) GetProcAddress(hAPIDLL, "icsneoFreeObject");
@@ -200,7 +206,7 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
icsneoEnableDOIPLine = (ENABLEDOIPACTIVATIONLINE)GetProcAddress(hAPIDLL, "icsneoEnableDOIPLine");
if(!icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
if(!icsneoFindNeoDevices || !icsneoOpenNeoDevice || !icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
!icsneoTxMessages || !icsneoGetMessages || !icsneoWaitForRxMessagesWithTimeOut ||
!icsneoGetTimeStampForMsg || !icsneoEnableNetworkRXQueue || !icsneoGetISO15765Status || !icsneoTxMessagesEx ||
!icsneoSetISO15765RxParameters || !icsneoGetConfiguration || !icsneoSendConfiguration ||
@@ -114,6 +114,8 @@ typedef int (__stdcall *SCRIPTWRITEISO15765TXMESSAGE)(void * hObject, unsigned
//Basic Functions
extern FINDNEODEVICES icsneoFindNeoDevices;
extern OPENNEODEVICE icsneoOpenNeoDevice;
extern OPENDEVICE icsneoOpenDevice;
extern CLOSEPORT icsneoClosePort;
extern FREEOBJECT icsneoFreeObject;
+170 -350
View File
@@ -14,19 +14,18 @@
#include "icsneo/communication/network.h"
#include <map>
#include <algorithm>
#include <cstring>
#include <climits>
#ifdef _MSC_VER
#pragma warning(disable : 4100) // unreferenced formal parameter
#pragma warning(disable : 4996) // STL time functions
#endif
using namespace icsneo;
typedef uint64_t legacymaphandle_t;
static std::map<legacymaphandle_t, neodevice_t> neodevices;
static std::map<neodevice_t*, NeoDeviceEx*> openneodevices;
static const std::map<size_t, size_t> mp_netIDToVnetOffSet = {
{NETID_HSCAN, 1},
@@ -52,6 +51,18 @@ static const std::map<size_t, size_t> mp_HWnetIDToCMnetID = {
static unsigned long vnet_table[] = {0, PLASMA_SLAVE1_OFFSET, PLASMA_SLAVE2_OFFSET};
static NeoDevice OldNeoDeviceFromNew(const neodevice_t* newnd)
{
NeoDevice oldnd = {0};
oldnd.DeviceType = newnd->type;
oldnd.SerialNumber = icsneo_serialStringToNum(newnd->serial);
oldnd.NumberOfClients = 0;
oldnd.MaxAllowedClients = 1;
static_assert(sizeof(neodevice_handle_t) == sizeof(oldnd.Handle),
"neodevice_handle_t size must be sizeof(int) for compatibility reasons");
oldnd.Handle = newnd->handle;
return oldnd;
}
static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t& newmsg, icsSpyMessage& oldmsg)
{
@@ -62,25 +73,6 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
return false;
const neomessage_frame_t& frame = *reinterpret_cast<const neomessage_frame_t*>(&newmsg);
auto copyStatusData = [&]() {
oldmsg.NetworkID = static_cast<uint8_t>(frame.netid); // Note: NetID remapping from the original API is not supported
oldmsg.NetworkID2 = static_cast<uint8_t>(frame.netid >> 8);
oldmsg.DescriptionID = frame.description;
oldmsg.StatusBitField = frame.status.statusBitfield[0];
oldmsg.StatusBitField2 = frame.status.statusBitfield[1];
oldmsg.StatusBitField3 = frame.status.statusBitfield[2];
oldmsg.StatusBitField4 = frame.status.statusBitfield[3];
};
auto copyFrameData = [&]() {
oldmsg.ExtraDataPtr = (void*)frame.data;
oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0;
memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8));
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
copyStatusData();
};
switch (Network::Type(frame.type))
{
case Network::Type::CAN:
@@ -89,42 +81,28 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
oldmsg.Protocol = frame.status.canfdFDF ? SPY_PROTOCOL_CANFD : SPY_PROTOCOL_CAN;
oldmsg.NumberBytesData = static_cast<uint8_t>(std::min(frame.length, (size_t)255));
oldmsg.NumberBytesHeader = 4;
copyFrameData();
break;
case Network::Type::Ethernet:
oldmsg.Protocol = SPY_PROTOCOL_ETHERNET;
oldmsg.NumberBytesData = static_cast<uint8_t>(frame.length & 0xFF);
oldmsg.NumberBytesHeader = static_cast<uint8_t>(frame.length >> 8);
copyFrameData();
break;
case Network::Type::LIN:
{
const neomessage_lin_t& linFrame = *reinterpret_cast<const neomessage_lin_t*>(&frame);
icsSpyMessageJ1850& linSpyMsg = *reinterpret_cast<icsSpyMessageJ1850*>(&oldmsg);
linSpyMsg.Protocol = SPY_PROTOCOL_LIN;
linSpyMsg.NumberBytesHeader = static_cast<uint8_t>(std::min(linFrame.length, static_cast<size_t>(3)));
linSpyMsg.NumberBytesData = static_cast<uint8_t>(linFrame.length - linSpyMsg.NumberBytesHeader);
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
copyStatusData();
if ((2 < linFrame.length) && (linFrame.length <= 10)) {
auto copyBytes = std::min(linSpyMsg.NumberBytesData, static_cast<uint8_t>(6));
std::memcpy(oldmsg.Data, frame.data, copyBytes);
oldmsg.Data[copyBytes] = linFrame.checksum;
} else if (2 == linFrame.length) {
std::memset(oldmsg.Data, 0, 8);
linSpyMsg.Header[linSpyMsg.NumberBytesHeader] = linFrame.checksum;
++linSpyMsg.NumberBytesHeader;
} else {
std::memset(oldmsg.Data, 0, 8);
}
if (linFrame.linStatus.txCommander)
linSpyMsg.StatusBitField |= SPY_STATUS_INIT_MESSAGE;
break;
}
default:
return false;
}
oldmsg.ExtraDataPtr = (void*)frame.data;
oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0;
memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8));
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
oldmsg.NetworkID = static_cast<uint8_t>(frame.netid); // Note: NetID remapping from the original API is not supported
oldmsg.NetworkID2 = static_cast<uint8_t>(frame.netid >> 8);
oldmsg.DescriptionID = frame.description;
oldmsg.StatusBitField = frame.status.statusBitfield[0];
oldmsg.StatusBitField2 = frame.status.statusBitfield[1];
oldmsg.StatusBitField3 = frame.status.statusBitfield[2];
oldmsg.StatusBitField4 = frame.status.statusBitfield[3];
// Timestamp - epoch = 1/1/2007 - 25ns per tick most of the time
uint64_t t = frame.timestamp;
uint16_t res = 0;
@@ -149,90 +127,6 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
return true;
}
static bool SpyMessageToNeoMessage(const icsSpyMessage& oldmsg, neomessage_frame_t& frame, unsigned int& lNetworkID)
{
frame.netid = static_cast<uint16_t>(lNetworkID);
frame.description = oldmsg.DescriptionID;
frame.status.statusBitfield[0] = oldmsg.StatusBitField;
frame.status.statusBitfield[1] = oldmsg.StatusBitField2;
frame.status.statusBitfield[2] = oldmsg.StatusBitField3;
frame.status.statusBitfield[3] = oldmsg.StatusBitField4;
auto copyFrameDataPtr = [&]() {
memcpy(frame.header, &oldmsg.ArbIDOrHeader, sizeof(frame.header));
if ((oldmsg.ExtraDataPtr != nullptr) && (oldmsg.ExtraDataPtrEnabled == 1))
frame.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
else
frame.data = oldmsg.Data;
};
switch(oldmsg.Protocol)
{
case SPY_PROTOCOL_ETHERNET:
{
frame.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
copyFrameDataPtr();
break;
}
case SPY_PROTOCOL_LIN:
{
neomessage_lin_t& linFrame = *reinterpret_cast<neomessage_lin_t*>(&frame);
const uint8_t numberBytesHeader = std::min(oldmsg.NumberBytesHeader, static_cast<uint8_t>(3));
const uint8_t numberBytesData = std::min(oldmsg.NumberBytesData, static_cast<uint8_t>(7));
frame.length = numberBytesHeader + numberBytesData;
linFrame.type = ICSNEO_NETWORK_TYPE_LIN;
if (oldmsg.StatusBitField & SPY_STATUS_INIT_MESSAGE)
linFrame.linStatus.txCommander = true;
else
linFrame.linStatus.txResponder = true;
copyFrameDataPtr();
uint8_t protID = linFrame.header[0] & 0x3Fu;
auto bit = [&](uint8_t pos)->uint8_t { return ((protID >> pos) & 0x1u); };
protID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7);
protID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6);
linFrame.header[0] = protID;
if (frame.length > 2) {
size_t checksum = 0;
uint8_t* lastByte = nullptr;
for(size_t idx = 1; idx < (frame.length - 1); ++idx) {
if (idx < oldmsg.NumberBytesHeader) {
checksum += frame.header[idx];
lastByte = (frame.header + idx + 1);
} else {
checksum += frame.data[idx-numberBytesHeader];
lastByte = const_cast<uint8_t*>(frame.data) + idx - numberBytesHeader + 1;
}
if (checksum > 255) { checksum -= 255; }
}
size_t enhanced = frame.header[0] + checksum;
if (enhanced > 255) { enhanced -= 255; }
checksum ^= 0xff;
enhanced ^= 0xff;
if ((lastByte != nullptr) && (*lastByte != checksum) && (*lastByte == enhanced))
linFrame.linStatus.txChecksumEnhanced = true;
}
break;
}
case SPY_PROTOCOL_CANFD:
{
frame.length = oldmsg.NumberBytesData;
frame.status.canfdFDF = true;
copyFrameDataPtr();
break;
}
default:
{
frame.length = oldmsg.NumberBytesData;
copyFrameDataPtr();
break;
}
}
return true;
}
static inline bool Within(size_t value, size_t min, size_t max)
{
return ((min <= value) && (value < max));
@@ -306,81 +200,109 @@ static inline size_t GetVnetAgnosticNetid(size_t fullNetid)
int LegacyDLLExport icsneoFindDevices(NeoDeviceEx* devs, int* devCount, unsigned int* devTypes, unsigned int devTypeCount,
POptionsFindNeoEx* POptionsFindNeoEx, unsigned int* zero)
{
// Match the legacy API maximum, this derives from the maximum COM Port on old windows versions.
constexpr int MAX_NEO_DEVICES = 255;
// Validate arguments
if (!devCount && *devCount < 0) {
if (!devs || !devCount)
return 0;
}
// return the size only if devs is NULL.
if (!devs)
if (*devCount < 0 || *devCount > 255)
return 0;
// Find the devices without filtering by the device type
// We allow this to find more than the requested number,
// as we may filter out some devices.
constexpr const size_t MAX_DEVICES = 255;
NeoDevice foundDevices[MAX_DEVICES];
int NumDevices = MAX_DEVICES;
int filteredDeviceCount = 0;
if (!icsneoFindNeoDevices(0, foundDevices, &NumDevices))
return 0;
for (auto i = 0; i < NumDevices; i++)
{
icsneo_findAllDevices(nullptr, (size_t*)devCount);
return 1;
}
// shrink the number of devices allowed to find
if (*devCount > MAX_NEO_DEVICES) {
*devCount = MAX_NEO_DEVICES;
}
// Find all the neodevice_t devices
std::vector<neodevice_t> neoDevices(*devCount);
auto neoDevicesSize = neoDevices.size();
icsneo_findAllDevices(neoDevices.data(), &neoDevicesSize);
neoDevices.resize(neoDevicesSize);
// Filter out the devices if needed
// No filtering needed
if (devTypes && devTypeCount > 0) {
neoDevices.erase(
std::remove_if(
neoDevices.begin(),
neoDevices.end(),
[&](const auto& iter) {
for (unsigned int i=0; i < devTypeCount; ++i) {
if (iter.type == devTypes[i]) {
return false;
}
}
return true;
// Check if the next device would overrun the user's buffer
// We check this up here since the documentation allows zero
// to be specified.
if (filteredDeviceCount >= *devCount)
break;
if (devTypes && devTypeCount)
{
for (unsigned int j = 0; j < devTypeCount; j++)
{
if (foundDevices[i].DeviceType == devTypes[j])
{
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
break;
}
),
neoDevices.end()
);
}
// Create a NeoDeviceEx From a neodevice_t
auto _createNeoDeviceExFrom = [](const neodevice_t* neoDevice) -> NeoDeviceEx {
NeoDeviceEx nde = {};
nde.neoDevice.DeviceType = neoDevice->type;
nde.neoDevice.SerialNumber = icsneo_serialStringToNum(neoDevice->serial);
nde.neoDevice.NumberOfClients = 0;
nde.neoDevice.MaxAllowedClients = 1;
static_assert(sizeof(neodevice_handle_t) == sizeof(nde.neoDevice.Handle),
"neodevice_handle_t size must be sizeof(int) for compatibility reasons");
nde.neoDevice.Handle = neoDevice->handle;
return nde;
};
// Create the NeoDeviceEx from the neodevice_t
auto i = 0;
for (const auto& neoDevice : neoDevices) {
// Fill the look up table
neodevices[uint64_t(neoDevice.handle) << 32 | icsneo_serialStringToNum(neoDevice.serial)] = neoDevice;
// Create the NeoDeviceEx
devs[i] = _createNeoDeviceExFrom(&neoDevice);
NeoDeviceEx* nde = &devs[i];
++i;
// Lookup the open NeoDeviceEx devices and match the NumberOfClients value if available.
for (auto& [neo_device, open_nde]: openneodevices) {
// SerialNumber should always be unique so lets compare against that.
if (nde->neoDevice.SerialNumber == open_nde->neoDevice.SerialNumber) {
nde->neoDevice.NumberOfClients = open_nde->neoDevice.NumberOfClients;
}
}
else
{
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
}
}
*devCount = (int)neoDevices.size();
*devCount = filteredDeviceCount;
return 1; // If the function succeeds but no devices are found 1 will still be returned and devCount will equal 0
}
int LegacyDLLExport icsneoFindNeoDevices(unsigned long DeviceTypes, NeoDevice* pNeoDevice, int* pNumDevices)
{
constexpr size_t MAX_DEVICES = 255;
size_t count = MAX_DEVICES;
if (pNumDevices == nullptr)
return 0;
if (pNeoDevice == nullptr)
{
icsneo_findAllDevices(nullptr, &count);
*pNumDevices = (int)count;
return 1;
}
size_t bufferSize = (size_t)*pNumDevices;
if (*pNumDevices < 0 || bufferSize > MAX_DEVICES)
return 0;
neodevice_t devices[MAX_DEVICES];
icsneo_findAllDevices(devices, &count);
if (bufferSize < count)
count = bufferSize;
*pNumDevices = (int)count;
for (size_t i = 0; i < count; i++)
{
pNeoDevice[i] = OldNeoDeviceFromNew(&devices[i]); // Write out into user memory
neodevices[uint64_t(devices[i].handle) << 32 | icsneo_serialStringToNum(devices[i].serial)] = devices[i]; // Fill the look up table
}
return 1;
}
int LegacyDLLExport icsneoOpenNeoDevice(NeoDevice* pNeoDevice, void** hObject, unsigned char* bNetworkIDs, int bConfigRead, int bSyncToPC)
{
if (pNeoDevice == nullptr || hObject == nullptr)
return false;
neodevice_t *device;
try
{
device = &neodevices.at(uint64_t(pNeoDevice->Handle) << 32 | pNeoDevice->SerialNumber);
}
catch (const std::out_of_range&)
{
return false;
}
*hObject = device;
if (!icsneo_openDevice(device))
return false;
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
}
int LegacyDLLExport icsneoOpenDevice(
NeoDeviceEx* pNeoDeviceEx,
void** hObject,
@@ -403,50 +325,19 @@ int LegacyDLLExport icsneoOpenDevice(
return false;
}
if (pNeoDeviceEx->neoDevice.NumberOfClients >= pNeoDeviceEx->neoDevice.MaxAllowedClients) {
return false;
}
*hObject = device;
if(!icsneo_openDevice(device)) {
if(!icsneo_openDevice(device))
return false;
}
if (icsneo_isOnlineSupported(device)) {
if (!icsneo_setPollingMessageLimit(device, 20000)) {
icsneo_closeDevice(device);
return false;
}
if (!icsneo_enableMessagePolling(device)) {
icsneo_closeDevice(device);
return false;
}
if (!icsneo_goOnline(device)) {
icsneo_closeDevice(device);
return false;
}
}
pNeoDeviceEx->neoDevice.NumberOfClients = 1;
// Add the open NeoDevice to the container so we can decrement NumberOfClients on close
openneodevices[device] = pNeoDeviceEx;
return true;
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
}
int LegacyDLLExport icsneoClosePort(void* hObject, int* pNumberOfErrors)
{
if (!icsneoValidateHObject(hObject))
return false;
if (pNumberOfErrors) {
*pNumberOfErrors = 0;
}
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
if (openneodevices.find(device) != openneodevices.end()) {
openneodevices[device]->neoDevice.NumberOfClients -= 1;
openneodevices.erase(device);
}
return icsneo_closeDevice(device);
}
@@ -505,9 +396,25 @@ int LegacyDLLExport icsneoTxMessagesEx(void* hObject, icsSpyMessage* pMsg, unsig
*NumTxed = 0;
for (unsigned int i = 0; i < lNumMessages; i++)
{
const icsSpyMessage& oldmsg = pMsg[i];
newmsg = {};
const icsSpyMessage& oldMsg = pMsg[i];
SpyMessageToNeoMessage(oldMsg, newmsg, lNetworkID);
newmsg.netid = (uint16_t)lNetworkID;
newmsg.description = oldmsg.DescriptionID;
memcpy(newmsg.header, &oldmsg.ArbIDOrHeader, sizeof(newmsg.header));
if (oldmsg.Protocol != SPY_PROTOCOL_ETHERNET)
newmsg.length = oldmsg.NumberBytesData;
else
newmsg.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
if (oldmsg.ExtraDataPtr != nullptr && oldmsg.ExtraDataPtrEnabled == 1)
newmsg.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
else
newmsg.data = oldmsg.Data;
newmsg.status.statusBitfield[0] = oldmsg.StatusBitField;
newmsg.status.statusBitfield[1] = oldmsg.StatusBitField2;
newmsg.status.statusBitfield[2] = oldmsg.StatusBitField3;
newmsg.status.statusBitfield[3] = oldmsg.StatusBitField4;
if (oldmsg.Protocol == SPY_PROTOCOL_CANFD)
newmsg.status.canfdFDF = true;
if (icsneo_transmit(device, reinterpret_cast<neomessage_t*>(&newmsg)))
(*NumTxed)++;
}
@@ -557,54 +464,6 @@ void LegacyDLLExport icsneoSetISO15765RxParameters(void* hObject, int lNetwork,
return;
}
int LegacyDLLExport icsneoGetRTC(void* hObject, icsSpyTime* time)
{
if(!icsneoValidateHObject(hObject))
return false;
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
uint64_t time64 = 0;
if(!icsneo_getRTC(device, &time64))
return false;
std::time_t seconds = time64;
/* To accommodate local time bugzilla #6600 https://intrepidcs.homeip.net:100/bugzilla/show_bug.cgi?id=6600 */
// local time must be used here
const auto timeInfo = std::localtime(&seconds);
if(!timeInfo)
return false;
time->sec = (unsigned char)timeInfo->tm_sec; // Will never hit 60 (leap second) because tm_sec comes from RTCCTIME
time->min = (unsigned char)timeInfo->tm_min;
time->hour = (unsigned char)timeInfo->tm_hour;
time->day = (unsigned char)timeInfo->tm_mday;
time->month = (unsigned char)timeInfo->tm_mon + 1;
time->year = (unsigned char)timeInfo->tm_year % 100;
return true;
}
int LegacyDLLExport icsneoSetRTC(void* hObject, const icsSpyTime* time)
{
if(!icsneoValidateHObject(hObject))
return false;
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
std::tm timeInfo{};
timeInfo.tm_sec = time->sec;
timeInfo.tm_min = time->min;
timeInfo.tm_hour = time->hour;
timeInfo.tm_mday = time->day;
timeInfo.tm_mon = time->month - 1;
timeInfo.tm_year = time->year + 100;
#ifdef _MSC_VER
#define timegm _mkgmtime
#endif
return icsneo_setRTC(device, (uint64_t)timegm(&timeInfo));
}
//Device Functions
int LegacyDLLExport icsneoGetConfiguration(void* hObject, unsigned char* pData, int* lNumBytes)
{
@@ -794,43 +653,11 @@ int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int*
return false;
}
int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, char* szErrorDescriptionShort, char* szErrorDescriptionLong,
int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, TCHAR* szErrorDescriptionShort, TCHAR* szErrorDescriptionLong,
int* lMaxLengthShort, int* lMaxLengthLong, int* lErrorSeverity, int* lRestartNeeded)
{
if (szErrorDescriptionShort == nullptr || szErrorDescriptionLong == nullptr
|| lMaxLengthShort == nullptr || lMaxLengthLong == nullptr || lErrorSeverity == nullptr
|| lRestartNeeded == nullptr)
{
return false;
}
//Set and send back 0. We will not restart the software.
*lRestartNeeded = 0;
//Using the error number, get the description from the event.
const char* tempDescription = APIEvent::DescriptionForType(APIEvent::Type(lErrorNumber));
int descrLength = int(std::strlen(tempDescription));
//Check to make sure the length of the error is not >= the buffer.
if (descrLength >= *lMaxLengthShort || descrLength >= *lMaxLengthLong)
{
return false;
}
//Copy the error description to the inout Short and Long arguments.
std::copy(tempDescription, tempDescription + descrLength, szErrorDescriptionShort);
std::copy(tempDescription, tempDescription + descrLength, szErrorDescriptionLong);
//Add the null terminator.
szErrorDescriptionShort[descrLength] = '\0';
szErrorDescriptionLong[descrLength] = '\0';
//Update the inout lengths to what the actual length of the error is
*lMaxLengthShort = *lMaxLengthLong = descrLength;
//Update the inout severity argument.
*lErrorSeverity = int(APIEvent::Severity::Any);
return true;
// TODO Implement
return false;
}
//ISO15765-2 Functions
@@ -909,18 +736,6 @@ int LegacyDLLExport icsneoStopSockServer(void* hObject)
return false;
}
int LegacyDLLExport icsneoGetDeviceStatus(void* hObject, icsDeviceStatus* deviceStatus, size_t* deviceStatusSize)
{
if (!icsneoValidateHObject(hObject))
return false;
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
if (deviceStatus == nullptr || deviceStatusSize == nullptr)
return false;
return icsneo_getDeviceStatus(device, deviceStatus, deviceStatusSize);
}
//CoreMini Script functions
int LegacyDLLExport icsneoScriptStart(void* hObject, int iLocation)
{
@@ -982,7 +797,20 @@ int LegacyDLLExport icsneoScriptWriteAppSignal(void* hObject, unsigned int iInde
return false;
}
//Deprecated (but still suppored in the DLL)
int LegacyDLLExport icsneoOpenPortEx(void* lPortNumber, int lPortType, int lDriverType, int lIPAddressMSB,
int lIPAddressLSBOrBaudRate, int bConfigRead, unsigned char* bNetworkID, int* hObject)
{
// TODO Implement
return false;
}
int LegacyDLLExport icsneoOpenPort(int lPortNumber, int lPortType, int lDriverType, unsigned char *bNetworkID,
unsigned char* bSCPIDs, int* hObject)
{
// TODO Implement
return false;
}
int LegacyDLLExport icsneoEnableNetworkCom(void* hObject, int Enable)
{
@@ -996,6 +824,19 @@ int LegacyDLLExport icsneoEnableNetworkCom(void* hObject, int Enable)
return icsneo_goOffline(device);
}
int LegacyDLLExport icsneoFindAllCOMDevices(int lDriverType, int lGetSerialNumbers, int lStopAtFirst, int lUSBCommOnly,
int* p_lDeviceTypes, int* p_lComPorts, int* p_lSerialNumbers, int* lNumDevices)
{
// TODO Implement
return false;
}
int LegacyDLLExport icsneoOpenNeoDeviceByChannels(NeoDevice* pNeoDevice, void** hObject, unsigned char* uChannels, int iSize,
int bConfigRead, int iOptions)
{
// TODO Implement
return false;
}
int LegacyDLLExport icsneoGetVCAN4Settings(void* hObject, SVCAN4Settings* pSettings, int iNumBytes)
{
@@ -1074,18 +915,6 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
case NEODEVICE_RADGIGALOG:
*pDeviceSettingsType = DeviceRADGigalogSettingsType;
break;
case NEODEVICE_RADMOON3:
*pDeviceSettingsType = DeviceRADMoon3SettingsType;
break;
case NEODEVICE_RED2:
*pDeviceSettingsType = DeviceRed2SettingsType;
break;
case NEODEVICE_FIRE3:
*pDeviceSettingsType = DeviceFire3SettingsType;
break;
case NEODEVICE_FIRE3_FLEXRAY:
*pDeviceSettingsType = DeviceFire3FlexraySettingsType;
break;
default:
return 0;
}
@@ -1123,15 +952,6 @@ int LegacyDLLExport icsneoGetDeviceSettings(void* hObject, SDeviceSettings* pSet
return !!icsneo_settingsReadStructure(device, &pSettings->Settings, iNumBytes - offset);
}
int LegacyDLLExport icsneoLoadDefaultSettings(void* hObject)
{
if (!icsneoValidateHObject(hObject))
return false;
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
return icsneo_settingsApplyDefaults(device);
}
int LegacyDLLExport icsneoSetBitRateEx(void* hObject, unsigned long BitRate, int NetworkID, int iOptions)
{
if (!icsneoValidateHObject(hObject))
@@ -1472,7 +1292,7 @@ int LegacyDLLExport icsneoSerialNumberFromString(unsigned long* serial, char* da
return false;
}
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NDIS_ADAPTER_INFORMATION* aInfo)
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NETWORK_ADAPTER_INFO* aInfo)
{
return false;
}
-8
View File
@@ -1,8 +0,0 @@
#!/bin/sh
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_EXAMPLES=ON \
-DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_BUILD_SYSTEM_TESTS=ON -DLIBICSNEO_ENABLE_TCP=OFF || exit 1
cmake --build build || exit 1
exit 0
+1 -1
View File
@@ -6,7 +6,7 @@ REM build
cd build
set CFLAGS=/WX
set CXXFLAGS=/WX
cmake -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_BUILD_SYSTEM_TESTS=ON -DLIBICSNEO_ENABLE_TCP=ON ..
cmake -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBICSNEO_BUILD_TESTS=ON ..
if %errorlevel% neq 0 exit /b %errorlevel%
cmake --build .
if %errorlevel% neq 0 exit /b %errorlevel%
-22
View File
@@ -1,22 +0,0 @@
find_path(FTD3XX_INCLUDE_DIR
NAMES ftd3xx.h FTD3XX.h
)
find_library(FTD3XX_LIBRARY
NAMES libftd3xx.a libftd3xx-static.a FTD3XX.lib
PATH_SUFFIXES x64/Static
)
mark_as_advanced(FTD3XX_FOUND FTD3XX_INCLUDE_DIR FTD3XX_LIBRARY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FTD3XX
REQUIRED_VARS FTD3XX_INCLUDE_DIR FTD3XX_LIBRARY
)
if(FTD3XX_FOUND AND NOT TARGET D3XX::D3XX)
add_library(FTD3XX::FTD3XX INTERFACE IMPORTED)
set_target_properties(FTD3XX::FTD3XX PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${FTD3XX_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${FTD3XX_LIBRARY}"
)
endif()
+81 -64
View File
@@ -13,13 +13,18 @@
#include "icsneo/communication/message/filter/main51messagefilter.h"
#include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/versionmessage.h"
#include "icsneo/communication/message/componentversionsmessage.h"
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#endif
using namespace icsneo;
int Communication::messageCallbackIDCounter = 1;
Communication::~Communication() {
if(redirectingRead)
clearRedirectRead();
if(isOpen())
close();
}
@@ -42,11 +47,6 @@ void Communication::spawnThreads() {
void Communication::joinThreads() {
closing = true;
if(pauseReadTask) {
resumeReads();
}
if(readTaskThread.joinable())
readTaskThread.join();
closing = false;
@@ -71,6 +71,11 @@ bool Communication::isDisconnected() {
return driver->isDisconnected();
}
void Communication::modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb) {
std::scoped_lock lk(rawCallbacksMutex);
cb(rawCallbacks);
}
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
// This is here so that other communication types (like multichannel) can override it
return rawWrite(bytes);
@@ -99,6 +104,23 @@ bool Communication::sendCommand(ExtendedCommand cmd, std::vector<uint8_t> argume
return sendCommand(Command::Extended, arguments);
}
bool Communication::redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo) {
if(redirectingRead)
return false;
redirectionFn = redirectTo;
redirectingRead = true;
return true;
}
void Communication::clearRedirectRead() {
if(!redirectingRead)
return;
// The mutex is required to clear the redirection, but not to set it
std::lock_guard<std::mutex> lk(redirectingReadMutex);
redirectingRead = false;
redirectionFn = std::function<void(std::vector<uint8_t>&&)>();
}
bool Communication::getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout) {
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::ReadSettings);
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
@@ -204,6 +226,15 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
std::condition_variable cv;
std::shared_ptr<Message> returnedMessage;
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
auto socket = lockSocket();
int64_t ms = timeout.count();
if(!(socket.writeTyped(RPC::DEVICE_LOCK) && socket.writeString(driver->device.serial) && socket.writeTyped(ms)))
return nullptr;
if(bool ret; !(socket.readTyped(ret) && ret))
return nullptr;
#endif
std::unique_lock<std::mutex> fnLk(syncMessageMutex); // Only allow for one sync message at a time
std::unique_lock<std::mutex> cvLk(cvMutex); // Don't let the callback fire until we're waiting for it
int cb = addMessageCallback(std::make_shared<MessageCallback>([&cvMutex, &returnedMessage, &cv](std::shared_ptr<Message> message) {
@@ -226,6 +257,13 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
if(fail) // The caller's function failed, so don't return a message
returnedMessage.reset();
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
if(!(socket.writeTyped(RPC::DEVICE_UNLOCK) && socket.writeString(driver->device.serial)))
return nullptr;
if(bool ret; !(socket.readTyped(ret) && ret))
return nullptr;
#endif
// Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking
return returnedMessage;
@@ -247,70 +285,49 @@ void Communication::dispatchMessage(const std::shared_ptr<Message>& msg) {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
}
void Communication::pauseReads() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
pauseReadTask = true;
}
void Communication::resumeReads() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
if(!pauseReadTask) {
return;
}
pauseReadTask = false;
lk.unlock();
pauseReadTaskCv.notify_one();
}
bool Communication::readsArePaused() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
return pauseReadTask;
}
void Communication::readTask() {
std::vector<uint8_t> readBytes;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing) {
if(pauseReadTask) {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
pauseReadTaskCv.wait(lk, [this]() { return !pauseReadTask; });
readBytes.clear();
if(driver->readWait(readBytes)) {
handleInput(*packetizer, readBytes);
}
if(driver->waitForRx(readTaskWakeLimit, readTaskWakeTimeout)) {
if(pauseReadTask) {
/**
* Reads could have paused while the driver was not available
*/
continue;
}
}
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
{
std::lock_guard lk(rawCallbacksMutex);
for(auto& cb : rawCallbacks)
cb(readBytes);
}
if(redirectingRead) {
// redirectingRead is an atomic so it can be set without acquiring a mutex
// However, we do not clear it without the mutex. The idea is that if another
// thread calls clearRedirectRead(), it will block until the redirectionFn
// finishes, and after that the redirectionFn will not be called again.
std::unique_lock<std::mutex> lk(redirectingReadMutex);
// So after we acquire the mutex, we need to check the atomic again, and
// if it has become cleared, we *can not* run the redirectionFn.
if(redirectingRead) {
redirectionFn(std::move(readBytes));
} else {
// The redirectionFn got cleared while we were acquiring the lock
lk.unlock(); // We don't need the lock anymore
handleInput(p, readBytes); // and we might as well process this input ourselves
}
} else {
if(p.input(readBytes)) {
for(const auto& packet : p.output()) {
std::shared_ptr<Message> msg;
if(!decoder->decode(msg, packet))
continue;
dispatchMessage(msg);
}
handleInput(*packetizer);
}
}
}
void Communication::handleInput(Packetizer& p) {
if(p.input(driver->getReadBuffer())) {
for(const auto& packet : p.output()) {
std::shared_ptr<Message> msg;
if(!decoder->decode(msg, packet))
continue;
dispatchMessage(msg);
}
}
}
std::optional< std::vector<ComponentVersion> > Communication::getComponentVersionsSync(std::chrono::milliseconds timeout) {
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::ComponentVersions);
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
return sendCommand(ExtendedCommand::GetComponentVersions, {});
}, filter, timeout);
if(!msg) // Did not receive a message
return std::nullopt;
auto ver = std::dynamic_pointer_cast<ComponentVersionsMessage>(msg);
if(!ver) // Could not upcast for some reason
return std::nullopt;
return std::make_optional< std::vector<ComponentVersion> >(std::move(ver->versions));
}
+18 -136
View File
@@ -5,21 +5,12 @@
#include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/message/flashmemorymessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/communication/message/wiviresponsemessage.h"
#include "icsneo/communication/message/scriptstatusmessage.h"
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
#include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/message/mdiomessage.h"
#include "icsneo/communication/message/extendeddatamessage.h"
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/message/diskdatamessage.h"
#include "icsneo/communication/message/hardwareinfo.h"
#include "icsneo/communication/message/tc10statusmessage.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/command.h"
#include "icsneo/device/device.h"
#include "icsneo/communication/packet/canpacket.h"
@@ -33,14 +24,6 @@
#include "icsneo/communication/packet/wivicommandpacket.h"
#include "icsneo/communication/packet/i2cpacket.h"
#include "icsneo/communication/packet/scriptstatuspacket.h"
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/packet/componentversionpacket.h"
#include "icsneo/communication/packet/supportedfeaturespacket.h"
#include "icsneo/communication/packet/mdiopacket.h"
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
#include "icsneo/communication/packet/livedatapacket.h"
#include "icsneo/communication/packet/hardwareinfopacket.h"
#include <iostream>
using namespace icsneo;
@@ -155,43 +138,6 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
return true;
}
case Network::Type::A2B: {
result = HardwareA2BPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
msg.network = packet->network;
msg.timestamp *= timestampResolution;
return true;
}
case Network::Type::LIN: {
result = HardwareLINPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
LINMessage& msg = *static_cast<LINMessage*>(result.get());
msg.network = packet->network;
return true;
}
case Network::Type::MDIO: {
result = HardwareMDIOPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
MDIOMessage& msg = *static_cast<MDIOMessage*>(result.get());
msg.network = packet->network;
return true;
}
case Network::Type::Internal: {
switch(packet->network.getNetID()) {
case Network::NetID::Reset_Status: {
@@ -258,18 +204,6 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
result = std::make_shared<RawMessage>(packet->network, packet->data);
return true;
}
case Network::NetID::RED_INT_MEMORYREAD: {
if(packet->data.size() != 512 + sizeof(uint16_t)) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // Should get enough data for a start address and sector
}
const auto msg = std::make_shared<FlashMemoryMessage>();
result = msg;
msg->startAddress = *reinterpret_cast<uint16_t*>(packet->data.data());
msg->data.insert(msg->data.end(), packet->data.begin() + 2, packet->data.end());
return true;
}
case Network::NetID::NeoMemorySDRead: {
if(packet->data.size() != 512 + sizeof(uint32_t)) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
@@ -283,58 +217,16 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
return true;
}
case Network::NetID::ExtendedCommand: {
if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse))
break; // Handle as a raw message, might not be a generic response
const auto& resp = *reinterpret_cast<ExtendedResponseMessage::PackedGenericResponse*>(packet->data.data());
switch(resp.header.command) {
case ExtendedCommand::GetComponentVersions:
result = ComponentVersionPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GetSupportedFeatures:
result = SupportedFeaturesPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GenericBinaryInfo:
result = GenericBinaryStatusPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GenericReturn:
result = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
return true;
case ExtendedCommand::LiveData:
result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report);
return true;
case ExtendedCommand::GetTC10Status:
result = TC10StatusMessage::DecodeToMessage(packet->data);
return true;
default:
// No defined handler, treat this as a RawMessage
break;
}
break;
}
case Network::NetID::ExtendedData: {
if(packet->data.size() < sizeof(ExtendedDataMessage::ExtendedDataHeader))
break;
const auto& header = *reinterpret_cast<ExtendedDataMessage::ExtendedDataHeader*>(packet->data.data());
if(resp.header.command != ExtendedCommand::GenericReturn)
break; // Handle as a raw message
switch(header.subCommand) {
case ExtendedDataSubCommand::GenericBinaryRead: {
result = std::make_shared<ExtendedDataMessage>(header);
auto extDataMsg = std::static_pointer_cast<ExtendedDataMessage>(result);
size_t numRead = std::min(ExtendedDataMessage::MaxExtendedDataBufferSize, (size_t)header.length);
extDataMsg->data.resize(numRead);
std::copy(packet->data.begin() + sizeof(header), packet->data.begin() + sizeof(header) + numRead, extDataMsg->data.begin());
extDataMsg->network = Network(static_cast<uint16_t>(Network::NetID::ExtendedData), false);
return true;
}
default:
break;
}
break;
const auto msg = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
result = msg;
return true;
}
case Network::NetID::FlexRayControl: {
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
@@ -379,16 +271,6 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
return true;
}
case Command::GetHardwareInfo: {
result = HardwareInfoPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false;
}
return true;
}
default:
auto msg = std::make_shared<Main51Message>();
msg->command = Command(packet->data[0]);
@@ -412,14 +294,6 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
packet->data.resize(length);
return decode(result, packet);
}
case Network::NetID::RED_App_Error: {
result = AppErrorMessage::DecodeToMessage(packet->data, report);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
return false;
}
return true;
}
case Network::NetID::ReadSettings: {
auto msg = std::make_shared<ReadSettingsMessage>();
msg->response = ReadSettingsMessage::Response(packet->data[0]);
@@ -471,18 +345,26 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
}
return true;
}
case Network::NetID::DiskData: {
result = std::make_shared<DiskDataMessage>(std::move(packet->data));
return true;
}
default:
break;
}
break;
}
case Network::Type::A2B: {
result = HardwareA2BPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
msg.network = packet->network;
return true;
}
}
// For the moment other types of messages will automatically be decoded as raw messages
result = std::make_shared<RawMessage>(packet->network, packet->data);
return true;
}
}
+17 -32
View File
@@ -8,33 +8,23 @@
using namespace icsneo;
bool Driver::pushRx(const uint8_t* buf, size_t numReceived) {
bool ret = readBuffer.write(buf, numReceived);
bool Driver::read(std::vector<uint8_t>& bytes, size_t limit) {
// A limit of zero indicates no limit
if(limit == 0)
limit = (size_t)-1;
rxWaitCv.notify_all();
if(limit > (readQueue.size_approx() + 4))
limit = (readQueue.size_approx() + 4);
return ret;
}
if(bytes.capacity() < limit)
bytes.resize(limit);
void Driver::clearBuffers()
{
WriteOperation flushop;
size_t actuallyRead = readQueue.try_dequeue_bulk(bytes.data(), limit);
readBuffer.clear();
rxWaitCv.notify_all();
if(bytes.size() > actuallyRead)
bytes.resize(actuallyRead);
while (writeQueue.try_dequeue(flushop)) {}
}
bool Driver::waitForRx(size_t limit, std::chrono::milliseconds timeout) {
return waitForRx([limit, this]() {
return readBuffer.size() >= limit;
}, timeout);
}
bool Driver::waitForRx(std::function<bool()> predicate, std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lk(rxWaitMutex);
return rxWaitCv.wait_for(lk, timeout, predicate);
return true;
}
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
@@ -42,18 +32,13 @@ bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds tim
if(limit == 0)
limit = (size_t)-1;
if(limit > (readBuffer.size() + 4))
limit = (readBuffer.size() + 4);
if(limit > (readQueue.size_approx() + 4))
limit = (readQueue.size_approx() + 4);
bytes.resize(limit);
// wait until we have enough data, or the timout occurs
waitForRx(limit, timeout);
size_t actuallyRead = readQueue.wait_dequeue_bulk_timed(bytes.data(), limit, timeout);
size_t actuallyRead = std::min(readBuffer.size(), limit);
bytes.resize(actuallyRead);
readBuffer.read(bytes.data(), 0, actuallyRead);
readBuffer.pop(actuallyRead);
bytes.resize(actuallyRead);
#ifdef ICSNEO_DRIVER_DEBUG_PRINTS
@@ -94,4 +79,4 @@ bool Driver::write(const std::vector<uint8_t>& bytes) {
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
return ret;
}
}
+2 -41
View File
@@ -1,8 +1,6 @@
#include "icsneo/communication/encoder.h"
#include "icsneo/communication/message/ethernetmessage.h"
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/message/main51message.h"
#include "icsneo/communication/packet/livedatapacket.h"
#include "icsneo/communication/packet/ethernetpacket.h"
#include "icsneo/communication/packet/iso9141packet.h"
#include "icsneo/communication/packet/canpacket.h"
@@ -11,8 +9,7 @@
#include "icsneo/communication/packet/i2cpacket.h"
#include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/packet/a2bpacket.h"
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/packet/mdiopacket.h"
using namespace icsneo;
@@ -99,30 +96,6 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
}
break;
} // End of Network::Type::I2C
case Network::Type::LIN: {
auto linmsg = std::dynamic_pointer_cast<LINMessage>(message);
if(!linmsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareLINPacket::EncodeFromMessage(*linmsg, result, report)) {
return false;
}
break;
} // End of Network::Type::LIN
case Network::Type::MDIO: {
auto mdiomsg = std::dynamic_pointer_cast<MDIOMessage>(message);
if(!mdiomsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareMDIOPacket::EncodeFromMessage(*mdiomsg, result, report)) {
return false;
}
break;
} // End of Network::Type::MDIO
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
@@ -199,17 +172,6 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
return false;
break;
}
case Message::Type::LiveData: {
auto liveDataMsg = std::dynamic_pointer_cast<LiveDataMessage>(message);
if(!liveDataMsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false; // The message was not a properly formed LiveDataMessage
}
if(!HardwareLiveDataPacket::EncodeFromMessage(*liveDataMsg, result, report))
return false;
result = packetizer.packetWrap(result, false);
return true;
}
break;
}
@@ -220,8 +182,7 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
// Size for the host-to-device long format is the size of the entire packet + 1
// So +1 for AA header, +1 for short format header, +2 for long format size, and +2 for long format NetID
// Then an extra +1, due to a firmware idiosyncrasy
uint16_t size = static_cast<uint16_t>(buffer->size()) + 1 + 1 + 2 + 2 + 1;
uint16_t size = uint16_t(buffer->size()) + 1 + 1 + 2 + 2 + 1;
buffer->insert(buffer->begin(), {
(uint8_t)Network::NetID::RED, // 0x0C for long message
(uint8_t)size, // Size, little endian 16-bit
+60
View File
@@ -0,0 +1,60 @@
#include <cstring>
#include "icsneo/communication/interprocessmailbox.h"
using namespace icsneo;
bool InterprocessMailbox::open(const std::string& name, bool create)
{
if(!queuedSem.open(name + "-qs", create))
return false;
if(!emptySem.open(name + "-es", create, MESSAGE_COUNT))
return false;
if(!sharedMem.open(name + "-sm", BLOCK_SIZE * MESSAGE_COUNT, create))
return false;
valid = true;
return true;
}
InterprocessMailbox::operator bool() const
{
return valid;
}
bool InterprocessMailbox::close()
{
valid = false;
return queuedSem.close() && emptySem.close() && sharedMem.close();
}
bool InterprocessMailbox::read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout)
{
if(!queuedSem.wait(timeout))
return false;
auto it = sharedMem.data() + (index * BLOCK_SIZE);
messageLength = *(LengthFieldType*)it;
it += LENGTH_FIELD_SIZE;
std::memcpy(data, it, std::min(messageLength, MAX_DATA_SIZE));
if(!emptySem.post())
return false;
++index;
index %= MESSAGE_COUNT;
return true;
}
bool InterprocessMailbox::write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout)
{
if(!emptySem.wait(timeout))
return false; // the buffer is full and we timed out
auto it = sharedMem.data() + (index * BLOCK_SIZE);
*(LengthFieldType*)it = messageLength;
it += LENGTH_FIELD_SIZE;
std::memcpy(it, data, messageLength);
if(!queuedSem.post())
return false;
++index;
index %= MESSAGE_COUNT;
return true;
}
-22
View File
@@ -1,22 +0,0 @@
#include "icsneo/communication/livedata.h"
namespace icsneo {
namespace LiveDataUtil {
LiveDataHandle getNewHandle() {
static LiveDataHandle currentHandle = 0;
++currentHandle;
if(currentHandle == std::numeric_limits<LiveDataHandle>::max()) {
EventManager::GetInstance().add(APIEvent::Type::LiveDataInvalidHandle, APIEvent::Severity::Error);
currentHandle = 1;
}
return currentHandle;
}
double liveDataValueToDouble(const LiveDataValue& val) {
constexpr double liveDataFixedPointToDouble = 0.00000000023283064365386962890625;
return val.value * liveDataFixedPointToDouble;
}
} // namespace LiveDataUtil
} // namespace icsneo
-257
View File
@@ -1,257 +0,0 @@
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
using namespace icsneo;
// Read a 16 bit sample from the audio buffer, which is stored as little endian
#define SAMPLE_FROM_BYTES_16(audioData) (((audioData)[0]) | ((audioData)[1] << 8))
// Read a 32 bit sample from the audio buffer
#define SAMPLE_FROM_BYTES_32(audioData) (((audioData)[0]) | ((audioData)[1] << 8) | ((audioData)[2] << 16) | ((audioData)[3] << 24))
// Read the most significant bytes of a sample stored in a 32 bit unsigned integer into audioData
#define SAMPLE_TO_BYTES_16(audioData, offset, sample) {\
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0x00FF0000u) >> 16);\
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0xFF000000u) >> 24);\
}
// Read little endian a 32 bit unsigned integer into audioData
#define SAMPLE_TO_BYTES_32(audioData, offset, sample) {\
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0x000000FFu));\
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0x0000FF00u) >> 8);\
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0x00FF0000u) >> 16);\
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0xFF000000u) >> 24);\
}
uint8_t A2BMessage::tdmToChannelNum(TDMMode tdm) {
switch(tdm) {
case TDMMode::TDM2:
return 4;
case TDMMode::TDM4:
return 8;
case TDMMode::TDM8:
return 16;
case TDMMode::TDM12:
return 24;
case TDMMode::TDM16:
return 32;
case TDMMode::TDM20:
return 40;
case TDMMode::TDM24:
return 48;
case TDMMode::TDM32:
return 64;
}
return 0;
}
uint8_t A2BMessage::getBytesPerChannel() const {
return channelSize16 ? 2u : 4u;
}
size_t A2BMessage::getFrameSize() const {
return static_cast<size_t>(2 * numChannels * getBytesPerChannel());
}
size_t A2BMessage::getSampleOffset(Direction dir, uint8_t channel, size_t frame) const {
size_t frameSize = getFrameSize();
size_t sampleOffset = static_cast<size_t>(frameSize * frame + 2 * channel * getBytesPerChannel());
if(dir == Direction::Upstream) {
sampleOffset += getBytesPerChannel();
}
return sampleOffset;
}
size_t A2BMessage::getNumFrames() const {
size_t frameSize = getFrameSize();
if(frameSize == 0) {
return 0;
}
return data.size() / frameSize;
}
A2BMessage::A2BMessage(size_t numFrames, TDMMode tdm, bool chSize16) : channelSize16(chSize16) {
numChannels = static_cast<uint8_t>(tdmToChannelNum(tdm) / 2);
size_t frameSize = static_cast<size_t>(2 * numChannels * (chSize16 ? 2u : 4u));
size_t audioBufferSize = frameSize * numFrames;
if(audioBufferSize > maxAudioBufferSize) {
size_t maxNumFrames = maxAudioBufferSize / frameSize;
audioBufferSize = maxNumFrames * frameSize;
}
data.resize(std::min<size_t>(maxAudioBufferSize, audioBufferSize), 0);
}
A2BMessage::A2BMessage(TDMMode tdm, bool chSize16) : channelSize16(chSize16) {
numChannels = static_cast<uint8_t>(tdmToChannelNum(tdm) / 2);
size_t frameSize = static_cast<size_t>(2 * numChannels * (chSize16 ? 2u : 4u));
size_t maxNumFrames = maxAudioBufferSize / frameSize;
size_t audioBufferSize = maxNumFrames * frameSize;
data.resize(audioBufferSize, 0);
}
PCMSample A2BMessage::getChannelSample(Direction dir, uint8_t channel, size_t frame, PCMType pcmType) const {
size_t sampleOffset = getSampleOffset(dir, channel, frame);
const uint8_t* audioData = &data[sampleOffset];
PCMSample result = 0;
// Samples coming from the device will either come from a 16 bit channel or 32 bit channel
if(channelSize16) {
int16_t sample16 = 0;
uint16_t& uSample16 = *reinterpret_cast<uint16_t*>(&sample16);
// Read little endian from the audio buffer
uSample16 = SAMPLE_FROM_BYTES_16(audioData);
// Scale the sample up according to the desired PCM size by
// multiplying using logical shifting
switch(pcmType) {
case PCMType::L16:
result = static_cast<PCMSample>(sample16);
break;
case PCMType::L24:
result = static_cast<PCMSample>(sample16) << 8;
break;
case PCMType::L32:
result = static_cast<PCMSample>(sample16) << 16;
break;
}
} else {
PCMSample sample32 = 0;
uint32_t& uSample32 = *reinterpret_cast<uint32_t*>(&sample32);
// Read little endian
uSample32 = SAMPLE_FROM_BYTES_32(audioData);
// Scale the sample down according to the desired PCM size by dividing using
// logical shifting, if the A2B network was set up with the desired pcmType
// there should be a clean division and no loss in PCM resolution.
switch(pcmType) {
case PCMType::L16:
result = sample32 >> 16;
break;
case PCMType::L24:
result = sample32 >> 8;
break;
case PCMType::L32:
result = sample32;
break;
}
}
return result;
}
void A2BMessage::setChannelSample(Direction dir, uint8_t channel, size_t frame, PCMSample sampleToSet, PCMType pcmType) {
size_t sampleOffset = getSampleOffset(dir, channel, frame);
uint32_t& uSample = *reinterpret_cast<uint32_t*>(&sampleToSet);
// Align the bytes towards the most significant bit by multiplying using
// left shifts
switch(pcmType) {
case PCMType::L16:
sampleToSet = sampleToSet << 16;
break;
case PCMType::L24:
sampleToSet = sampleToSet << 8;
break;
}
if(channelSize16) {
// Read the 2 most significant bytes of the sample
SAMPLE_TO_BYTES_16(data, sampleOffset, uSample)
} else {
// Read the entire sample
SAMPLE_TO_BYTES_32(data, sampleOffset, uSample);
}
}
bool A2BMessage::loadAudioBuffer(IWAVStream& wavStream, const ChannelMap& channelMap) {
if(!wavStream) {
return false;
}
size_t totalMessageChannels = numChannels * 2; // Multiply by two inorder to include both down and upstream channels
size_t bytesPerChannel = static_cast<size_t>(getBytesPerChannel()); // Number of bytes per message channel
size_t frameSize = getFrameSize();
size_t numFrames = getNumFrames();
size_t bytesPerSampleWAV = static_cast<size_t>(wavStream.header.bitsPerSample / 8); // Number of bytes per sample in the WAV data-stream
size_t numWAVChannels = static_cast<size_t>(wavStream.header.numChannels);
size_t wavFrameSize = numWAVChannels * bytesPerSampleWAV;
if(bytesPerSampleWAV != 2 && bytesPerSampleWAV != 3 && bytesPerSampleWAV != 4) {
return false;
}
if(numFrames == 0) {
return false;
}
uint8_t* audioBuffer = data.data();
std::vector<uint8_t> wavFrame(wavFrameSize, 0);
for(size_t frame = 0; frame < numFrames; frame++) {
// Read one frame of data from the input stream
if(!wavStream.read(reinterpret_cast<char*>(wavFrame.data()), wavFrame.size())) {
break;
}
// Iterate through each mapping and set a message channel to a channel in the WAV frame above
for(const auto& [messageChannel, wavChannel] : channelMap) {
if(messageChannel >= totalMessageChannels || wavChannel >= numWAVChannels) {
return false;
}
size_t frameOffset = wavChannel * bytesPerSampleWAV; // Offset in the read WAV frame
size_t audioBufferOffset = frame * frameSize + messageChannel * bytesPerChannel; // Offset in the message audio buffer
if(bytesPerChannel < bytesPerSampleWAV) {
// In this case, the message channels are smaller than the samples in the input WAV
// samples in both the message channel and WAV are little endian, so we write only the
// most significant bytes of the WAV
// Align to most significant bytes of wav frame
size_t align = bytesPerSampleWAV - bytesPerChannel;
for(
size_t frameByte = frameOffset + align;
frameByte < frameOffset + bytesPerSampleWAV;
frameByte++,
audioBufferOffset++
) {
audioBuffer[audioBufferOffset] = wavFrame[frameByte];
}
} else {
// The message channel is greater than or equal to the sample in the WAV
// I2S specifies that the sample in this case is right aligned to the most significant
// byte of the message channel
// Align to most significant byte of audio buffer channel
size_t align = bytesPerChannel - bytesPerSampleWAV;
for(
size_t audioByte = audioBufferOffset + align;
audioByte < audioBufferOffset + bytesPerChannel;
audioByte++,
frameOffset++
) {
audioBuffer[audioByte] = wavFrame[frameOffset];
}
}
}
}
return true;
}
-143
View File
@@ -1,143 +0,0 @@
#include <icsneo/communication/message/apperrormessage.h>
namespace icsneo {
#pragma pack(push, 2)
typedef struct {
uint16_t error_type;
uint16_t network_id;
uint32_t uiTimeStamp10uS;
uint32_t uiTimeStamp10uSMSB;
} AppErrorData;
#pragma pack(pop)
std::shared_ptr<Message> AppErrorMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
const AppErrorData* data = reinterpret_cast<const AppErrorData*>(bytestream.data());
if(!data) {
report(APIEvent::Type::AppErrorParsingFailed, APIEvent::Severity::Error);
return nullptr;
}
auto appErr = std::make_shared<AppErrorMessage>();
appErr->errorType = data->error_type;
appErr->errorNetID = static_cast<Network::NetID>(data->network_id);
appErr->timestamp10us = data->uiTimeStamp10uS;
appErr->timestamp10usMSB = data->uiTimeStamp10uSMSB;
appErr->network = Network::NetID::RED_App_Error;
return appErr;
}
AppErrorType AppErrorMessage::getAppErrorType() {
AppErrorType errType = static_cast<AppErrorType>(errorType);
if(errType > AppErrorType::AppNoError) {
return AppErrorType::AppNoError;
}
return errType;
}
std::string AppErrorMessage::getAppErrorString() {
auto netIDString = Network::GetNetIDString(errorNetID);
AppErrorType errType = static_cast<AppErrorType>(errorType);
switch (errType) {
case AppErrorType::AppErrorRxMessagesFull:
return std::string(netIDString) + ": RX message buffer full";
case AppErrorType::AppErrorTxMessagesFull:
return std::string(netIDString) + ": TX message buffer full";
case AppErrorType::AppErrorTxReportMessagesFull:
return std::string(netIDString) + ": TX report buffer full";
case AppErrorType::AppErrorBadCommWithDspIC:
return "Received bad packet from DSP IC";
case AppErrorType::AppErrorDriverOverflow:
return std::string(netIDString) + ": Driver overflow";
case AppErrorType::AppErrorPCBuffOverflow:
return "PC buffer overflow";
case AppErrorType::AppErrorPCChksumError:
return "PC checksum error";
case AppErrorType::AppErrorPCMissedByte:
return "PC missed byte";
case AppErrorType::AppErrorPCOverrunError:
return "PC overrun error";
case AppErrorType::AppErrorSettingFailure:
return std::string(netIDString) + ": Settings incorrectly set";
case AppErrorType::AppErrorTooManySelectedNetworks:
return "Too many selected networks";
case AppErrorType::AppErrorNetworkNotEnabled:
return std::string(netIDString) + ": Network not enabled";
case AppErrorType::AppErrorRtcNotCorrect:
return "RTC not correct";
case AppErrorType::AppErrorLoadedDefaultSettings:
return "Loaded default settings";
case AppErrorType::AppErrorFeatureNotUnlocked:
return "Feature not unlocked";
case AppErrorType::AppErrorFeatureRtcCmdDropped:
return "RTC command dropped";
case AppErrorType::AppErrorTxMessagesFlushed:
return "TX message buffer flushed";
case AppErrorType::AppErrorTxMessagesHalfFull:
return "TX message buffer half full";
case AppErrorType::AppErrorNetworkNotValid:
return "Network is not valid";
case AppErrorType::AppErrorTxInterfaceNotImplemented:
return "TX interface is not implemented";
case AppErrorType::AppErrorTxMessagesCommEnableIsOff:
return "TX message communication is disabled";
case AppErrorType::AppErrorRxFilterMatchCountExceeded:
return "RX filter match count exceeded";
case AppErrorType::AppErrorEthPreemptionNotEnabled:
return std::string(netIDString) + ": Ethernet preemption not enabled";
case AppErrorType::AppErrorTxNotSupportedInMode:
return std::string(netIDString) + ": Transmit is not supported in this mode";
case AppErrorType::AppErrorJumboFramesNotSupported:
return std::string(netIDString) + ": Jumbo frames not supported";
case AppErrorType::AppErrorEthernetIpFragment:
return "Ethernet IP fragment received";
case AppErrorType::AppErrorTxMessagesUnderrun:
return std::string(netIDString) + ": Transmit buffer underrun";
case AppErrorType::AppErrorDeviceFanFailure:
return "Device fan failure";
case AppErrorType::AppErrorDeviceOvertemperature:
return "Device overtemperature";
case AppErrorType::AppErrorTxMessageIndexOutOfRange:
return "Transmit message index out of range";
case AppErrorType::AppErrorUndersizedFrameDropped:
return std::string(netIDString) + ": Undersized frame dropped";
case AppErrorType::AppErrorOversizedFrameDropped:
return std::string(netIDString) + ": Oversized frame dropped";
case AppErrorType::AppErrorWatchdogEvent:
return "Watchdog event occured";
case AppErrorType::AppErrorSystemClockFailure:
return "Device clock failed";
case AppErrorType::AppErrorSystemClockRecovered:
return "Device clock recovered";
case AppErrorType::AppErrorSystemPeripheralReset:
return "Device peripheral reset";
case AppErrorType::AppErrorSystemCommunicationFailure:
return "Device communication failure";
case AppErrorType::AppErrorTxMessagesUnsupportedSourceOrPacketId:
return std::string(netIDString) + ": Transmit unsupported source or packet ID";
case AppErrorType::AppErrorWbmsManagerConnectFailed:
return std::string(netIDString) + ": Failed to connect to managers with settings";
case AppErrorType::AppErrorWbmsManagerConnectBadState:
return std::string(netIDString) + ": Connected to managers in a invalid state";
case AppErrorType::AppErrorWbmsManagerConnectTimeout:
return std::string(netIDString) + ": Timeout while attempting to connect to managers";
case AppErrorType::AppErrorFailedToInitializeLoggerDisk:
return "Device failed to initialize storage disk";
case AppErrorType::AppErrorInvalidSetting:
return std::string(netIDString) + ": Invalid settings";
case AppErrorType::AppErrorSystemFailureRequestedReset:
return "Device rebooted to recover from an unexpected error condition";
case AppErrorType::AppErrorPortKeyMistmatch:
return std::string(netIDString) + ": Mismatch between key in manager and stored key";
case AppErrorType::AppErrorErrorBufferOverflow:
return "Device error buffer overflow";
case AppErrorType::AppNoError:
return "No error";
default:
return "Unknown error";
}
return "Unknown error";
}
} // namespace icsneo
@@ -1,234 +1,100 @@
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
#include "icsneo/device/tree/rada2b/rada2b.h"
#include "icsneo/icsneocpp.h"
namespace icsneo {
namespace icsneo
{
A2BWAVOutput::A2BWAVOutput(
const char* filename,
const ChannelMap& channelMap,
PCMType bitDepth,
size_t numWAVChannels,
uint32_t sampleRate
)
: StreamOutput(filename), wavSampleRate(sampleRate), numChannelsWAV(numWAVChannels), chMap(channelMap) {
switch(bitDepth) {
case PCMType::L16:
bytesPerSampleWAV = 2;
break;
case PCMType::L24:
bytesPerSampleWAV = 3;
break;
case PCMType::L32:
bytesPerSampleWAV = 4;
break;
}
if(initialize()) {
initialized = true;
}
}
A2BWAVOutput::A2BWAVOutput(
std::ostream& os,
const ChannelMap& channelMap,
PCMType bitDepth,
size_t numWAVChannels,
uint32_t sampleRate
)
: StreamOutput(os), wavSampleRate(sampleRate), numChannelsWAV(numWAVChannels), chMap(channelMap) {
switch(bitDepth) {
case PCMType::L16:
bytesPerSampleWAV = 2;
break;
case PCMType::L24:
bytesPerSampleWAV = 3;
break;
case PCMType::L32:
bytesPerSampleWAV = 4;
break;
}
if(initialize()) {
initialized = true;
}
}
A2BWAVOutput::~A2BWAVOutput() {
if(!closed) {
close();
}
}
bool A2BWAVOutput::initialize() {
static constexpr size_t maxWAVChannels = 256;
if(numChannelsWAV > maxWAVChannels) {
return false;
}
maxMessageChannel = 0;
// Check if the inputted channel map has invalid mappings and compute maxMessageChannel
for(auto [wavChannel, messageChannel] : chMap) {
maxMessageChannel = std::max<size_t>(maxMessageChannel, messageChannel);
if(wavChannel >= numChannelsWAV) {
return false;
}
}
WAVHeader header = WAVHeader(
static_cast<uint16_t>(chMap.size()),
wavSampleRate,
static_cast<uint16_t>(bytesPerSampleWAV * 8)
);
if(!stream->write(reinterpret_cast<const char*>(&header), sizeof(WAVHeader))) {
return false;
}
void A2BWAVOutput::writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const {
WaveFileHeader header = WaveFileHeader(2 * firstMsg->getNumChannels(), wavSampleRate, firstMsg->getBitDepth());
header.write(stream);
streamStartPos = static_cast<uint32_t>(stream->tellp());
wavBuffer = std::vector<uint8_t>(wavBufferSize, 0);
wavBufferOffset = 0;
return true;
}
bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
if(!initialized) {
if(closed)
{
return false;
}
if(closed) {
if(message->type != Message::Type::Frame)
return false;
const auto& frame = std::static_pointer_cast<Frame>(message);
if(frame->network.getType() != Network::Type::A2B)
return false;
const auto& a2bmsg = std::static_pointer_cast<A2BMessage>(frame);
if(firstMessageFlag) {
writeHeader(a2bmsg);
firstMessageFlag = false;
}
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::DownStream)) {
close();
return false;
}
if(message->type != Message::Type::Frame) {
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::UpStream)) {
close();
return false;
}
const auto& frameMsg = std::dynamic_pointer_cast<Frame>(message);
if(!frameMsg) {
return false;
}
if(frameMsg->network.getType() != Network::Type::A2B)
return false;
const auto& a2bMsg = std::dynamic_pointer_cast<A2BMessage>(frameMsg);
if(!a2bMsg) {
return false;
}
size_t frameSize = a2bMsg->getFrameSize();
size_t wavFrameSize = numChannelsWAV * bytesPerSampleWAV;
size_t bytesPerChannel = static_cast<size_t>(a2bMsg->getBytesPerChannel());
size_t numMessageChannels = 2 * a2bMsg->numChannels;
size_t numFrames = a2bMsg->getNumFrames();
const uint8_t* audioBuffer = a2bMsg->data.data();
if(maxMessageChannel >= numMessageChannels) {
// The max message channel in our channel map is larger than the number of channels in this message
// this is likely due to the user inputting incorrect settings
return false;
}
for(size_t frame = 0; frame < numFrames; frame++) {
// Check to see if we can read another frame in wavBuffer, otherwise write and clear the buffer
if(wavBufferOffset + wavFrameSize >= wavBufferSize) {
if(!writeCurrentBuffer()) {
return false;
}
}
for(size_t wavChannel = 0; wavChannel < numChannelsWAV; wavChannel++) {
if(auto iter = chMap.find(static_cast<uint8_t>(wavChannel)); iter != chMap.end()) {
auto messageChannel = iter->second;
size_t messageChannelOffset = messageChannel * bytesPerChannel + frameSize* frame;
// Samples in the WAV are little endian signed integers
// Samples in the message channels are little endian signed integers that are
// most significant bit aligned
if(a2bMsg->channelSize16) {
// In this case, the channel size will be less than or equal to the sample we are writing
// so we zero out any of the least significant bytes which won't be occupied by a sample byte
for(size_t zeroByte = 0; zeroByte < bytesPerSampleWAV - bytesPerChannel; zeroByte++) {
wavBuffer[wavBufferOffset++] = 0;
}
// Write the channel data in the most signifant bytes of the wav sample, this effectively
// writes a sample which is scaled up.
for(size_t channelByte = 0; channelByte < bytesPerChannel; channelByte++) {
wavBuffer[wavBufferOffset++] = audioBuffer[messageChannelOffset + channelByte];
}
} else {
// In this case, the channel size will be greater than or equal to the sample we are reading
// Align the wav sample with the most significant bytes of the channel
size_t channelByte = messageChannelOffset + (bytesPerChannel - bytesPerSampleWAV);
// Read the most significant bytes of the channel into the wavBuffer
for(size_t sampleByte = 0; sampleByte < bytesPerSampleWAV; sampleByte++, channelByte++) {
wavBuffer[wavBufferOffset++] = audioBuffer[channelByte];
}
}
} else {
// If this channel wasn't specified in the channel map, set a zero sample
for(
size_t sampleByte = 0;
sampleByte < bytesPerSampleWAV;
sampleByte++
) {
wavBuffer[wavBufferOffset++] = 0;
}
}
}
}
return true;
}
void A2BWAVOutput::close() const {
void A2BWAVOutput::close() const
{
if(closed) {
return;
}
if(!initialized) {
return;
}
// Write any left over data in the buffer
if(wavBufferOffset > 0) {
writeCurrentBuffer();
}
// Seek back in the output stream and write the WAV chunk sizes
uint32_t streamEndPos = static_cast<uint32_t>(stream->tellp());
uint32_t subChunk2Size = streamEndPos - streamStartPos;
uint32_t chunkSize = streamEndPos - 8;
stream->seekp(streamStartPos - 4);
stream->write(reinterpret_cast<const char*>(&subChunk2Size), 4);
write((void*)&subChunk2Size, 4);
stream->seekp(4, std::ios::beg);
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
write((void*)&chunkSize, 4);
closed = true;
}
bool A2BWAVOutput::writeCurrentBuffer() const {
bool A2BWAVOutput::writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessage::A2BDirection dir) const
{
uint8_t numChannels = msg->getNumChannels();
if(!stream->write(reinterpret_cast<const char*>(wavBuffer.data()), wavBufferOffset)) {
return false;
uint8_t channel = 0;
uint32_t frame = 0;
uint8_t bitDepth = msg->getBitDepth();
while(true) {
auto sample = msg->getSample(dir, channel, frame);
if(!sample) {
if(channel == 0) {
break;
}
return false;
}
uint32_t audioSample = sample.value() >> (32 - bitDepth);
write((void*)(&audioSample), A2BPCM_SAMPLE_SIZE);
channel = (channel + 1) % numChannels;
if(channel == 0) {
frame++;
}
}
wavBufferOffset = 0;
return true;
}
}
-30
View File
@@ -1,30 +0,0 @@
#include "icsneo/communication/message/linmessage.h"
#include <numeric>
namespace icsneo {
void LINMessage::calcChecksum(LINMessage& message) {
uint16_t sum = 0;
auto limitFunc = [](uint16_t x, uint16_t y) -> uint16_t {
if ((x + y) > 0xFFu)
return ((x + y) - 0xFFu);
else
return (x + y);
};
message.checksum = static_cast<uint8_t>(std::accumulate(message.data.begin(), message.data.end(), sum, limitFunc));
if(message.isEnhancedChecksum)
message.checksum = static_cast<uint8_t>(limitFunc(message.checksum, message.protectedID));
message.checksum ^= 0xFFu;
}
uint8_t LINMessage::calcProtectedID(uint8_t& id) {
uint8_t protID = id;
auto bit = [&](uint8_t pos)->uint8_t { return ((protID >> pos) & 0x1u); };
protID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7);
protID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6);
return protID;
}
} //namespace icsneo
-15
View File
@@ -1,15 +0,0 @@
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/livedata.h"
namespace icsneo
{
void LiveDataCommandMessage::appendSignalArg(LiveDataValueType valueType) {
auto& arg = args.emplace_back(std::make_shared<LiveDataArgument>());
arg->objectType = LiveDataObjectType::MISC;
arg->objectIndex = 0u;
arg->signalIndex = 0u;
arg->valueType = valueType;
}
} // namespace icsneo
-90
View File
@@ -2,7 +2,6 @@
#include "icsneo/communication/message/canmessage.h"
#include "icsneo/communication/message/ethernetmessage.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/linmessage.h"
using namespace icsneo;
@@ -18,7 +17,6 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg;
auto framemsg = std::static_pointer_cast<Frame>(message);
const auto netType = framemsg->network.getType();
frame.netid = (neonetid_t)framemsg->network.getNetID();
frame.type = (neonettype_t)netType;
frame.description = framemsg->description;
@@ -55,47 +53,6 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
//eth.status.xyz = ethmsg->noPadding;
break;
}
case Network::Type::LIN: {
neomessage_lin_t& lin = *(neomessage_lin_t*)&neomsg;
auto linmsg = std::static_pointer_cast<LINMessage>(message);
if(!linmsg) { break; }
const auto linHdrBytes = std::min(linmsg->data.size(), static_cast<size_t>(2));
lin.header[0] = linmsg->protectedID;
std::copy(linmsg->data.begin(), linmsg->data.begin() + linHdrBytes, lin.header + 1);
linmsg->calcChecksum(*linmsg);
lin.checksum = linmsg->checksum;
lin.data = linmsg->data.data() + linHdrBytes;
if(linmsg->isEnhancedChecksum != linmsg->statusFlags.TxChecksumEnhanced) {
linmsg->isEnhancedChecksum = true;
linmsg->statusFlags.TxChecksumEnhanced = true;
}
if(linmsg->data.size())
lin.length = linmsg->data.size() + 2;
else
lin.length = 1;
lin.linStatus = {
linmsg->statusFlags.TxChecksumEnhanced,
linmsg->statusFlags.TxCommander,
linmsg->statusFlags.TxResponder,
linmsg->statusFlags.UpdateResponderOnce,
linmsg->statusFlags.HasUpdatedResponderOnce,
linmsg->statusFlags.BusRecovered,
linmsg->statusFlags.BreakOnly
};
lin.status.linJustBreakSync = linmsg->errFlags.ErrRxBreakSyncOnly;
lin.status.linErrorTXRXMismatch = linmsg->errFlags.ErrTxRxMismatch;
lin.status.linErrorRXBreakNotZero = linmsg->errFlags.ErrRxBreakNotZero;
lin.status.linErrorRXBreakTooShort = linmsg->errFlags.ErrRxBreakTooShort;
lin.status.linErrorRXSyncNot55 = linmsg->errFlags.ErrRxSyncNot55;
lin.status.linErrorRXDataGreaterEight = linmsg->errFlags.ErrRxDataLenOver8;
lin.status.linSyncFrameError = linmsg->errFlags.ErrFrameSync;
lin.status.linIDFrameError = linmsg->errFlags.ErrFrameMessageID;
lin.status.linSlaveByteError = linmsg->errFlags.ErrFrameResponderData;
lin.status.checksumError = linmsg->errFlags.ErrChecksumMatch;
break;
}
default:
// TODO Implement others
break;
@@ -148,53 +105,6 @@ std::shared_ptr<Message> icsneo::CreateMessageFromNeoMessage(const neomessage_t*
ethmsg->data.insert(ethmsg->data.end(), eth.data, eth.data + eth.length);
return ethmsg;
}
case Network::Type::LIN: {
neomessage_lin_t& lin = *(neomessage_lin_t*)neomessage;
auto linmsg = std::make_shared<LINMessage>();
linmsg->network = network;
linmsg->description = lin.description;
linmsg->protectedID = lin.header[0];
linmsg->ID = linmsg->protectedID & 0x3F;
linmsg->statusFlags = {
static_cast<bool>(lin.linStatus.txChecksumEnhanced),
static_cast<bool>(lin.linStatus.txCommander),
static_cast<bool>(lin.linStatus.txResponder),
static_cast<bool>(lin.linStatus.updateResponderOnce),
static_cast<bool>(lin.linStatus.hasUpdatedResponderOnce),
static_cast<bool>(lin.linStatus.busRecovered),
static_cast<bool>(lin.linStatus.breakOnly)
};
linmsg->isEnhancedChecksum = linmsg->statusFlags.TxChecksumEnhanced;
linmsg->errFlags = {
static_cast<bool>(lin.linStatus.breakOnly),
static_cast<bool>(lin.status.linJustBreakSync),
static_cast<bool>(lin.status.linErrorTXRXMismatch),
static_cast<bool>(lin.status.linErrorRXBreakNotZero),
static_cast<bool>(lin.status.linErrorRXBreakTooShort),
static_cast<bool>(lin.status.linErrorRXSyncNot55),
static_cast<bool>(lin.status.linErrorRXDataGreaterEight),
static_cast<bool>(lin.status.linSyncFrameError),
static_cast<bool>(lin.status.linIDFrameError),
static_cast<bool>(lin.status.linSlaveByteError),
static_cast<bool>(lin.status.checksumError)
};
if(lin.length > 1) {
auto numHeaderBytes = std::min(lin.length, static_cast<size_t>(3));
linmsg->data.insert(linmsg->data.end(), (lin.header + 1), (lin.header + numHeaderBytes));
linmsg->data.insert(linmsg->data.end(), lin.data, (lin.data + (lin.length - numHeaderBytes)));
linmsg->checksum = linmsg->data.back();
linmsg->data.pop_back();
}
if (linmsg->statusFlags.TxCommander) {
if (linmsg->data.size())
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
else
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
} else if (linmsg->statusFlags.TxResponder) {
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
}
return linmsg;
}
default: break;
}
break;
@@ -1,31 +0,0 @@
#include "icsneo/communication/message/tc10statusmessage.h"
#include "icsneo/communication/command.h"
using namespace icsneo;
#pragma pack(push, 2)
struct Header {
ExtendedCommand command;
uint16_t length;
};
struct Packet {
Header header;
TC10WakeStatus wakeStatus;
TC10SleepStatus sleepStatus;
};
#pragma pack(pop)
std::shared_ptr<TC10StatusMessage> TC10StatusMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(Packet)) {
return nullptr;
}
const Packet* packet = (Packet*)bytestream.data();
if (packet->header.command != ExtendedCommand::GetTC10Status) {
return nullptr;
}
if (packet->header.length < sizeof(Packet) - sizeof(Header)) {
return nullptr;
}
return std::make_shared<TC10StatusMessage>(packet->wakeStatus, packet->sleepStatus);
}
+2 -5
View File
@@ -177,11 +177,8 @@ void MultiChannelCommunication::vnetReadTask(size_t vnetIndex) {
if(queue.wait_dequeue_timed(payloadBytes, std::chrono::milliseconds(250))) {
if(closing)
break;
auto& ringBuffer = driver->getReadBuffer();
ringBuffer.write(payloadBytes);
handleInput(*vnetPacketizer);
handleInput(*vnetPacketizer, payloadBytes);
}
}
}
+128 -25
View File
@@ -1,58 +1,161 @@
#include "icsneo/communication/packet/a2bpacket.h"
#include <cstring>
#include <vector>
namespace icsneo {
const size_t HardwareA2BPacket::a2bMessageMaxLength = sizeof(HardwareA2BPacket) + 1024;
const size_t HardwareA2BPacket::coreMiniMessageHeaderSize = 28;
const size_t HardwareA2BPacket::a2bMessageMaxLength = (size_t)HardwareA2BPacket::coreMiniMessageHeaderSize + 1024;
const size_t HardwareA2BPacket::a2bHeaderSize = 6;
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(HardwareA2BPacket))
if(bytestream.size() < coreMiniMessageHeaderSize)
{
return nullptr;
}
auto getSampleFromBytes = [](uint8_t bytesPerSample, const uint8_t *bytes) {
A2BPCMSample result = 0;
const HardwareA2BPacket* data = (const HardwareA2BPacket*)bytestream.data();
for(auto i = 0; i < bytesPerSample; i++) {
result |= static_cast<uint32_t>(bytes[i]) << (i * 8);
}
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - sizeof(HardwareA2BPacket); // First 28 bytes are message header.
return result;
};
if(totalPackedLength == 0) {
return nullptr;
}
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>();
const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data();
msg->numChannels = data->header.channelNum;
uint32_t totalPackedLength = static_cast<uint32_t>(bytestream.size()) - static_cast<uint32_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
uint8_t bytesPerChannel = data->header.channelSize16 ? 2 : 4;
uint8_t numChannels = data->header.channelNum;
uint8_t bitDepth = data->header.channelSize16 ? A2BPCM_L16 : A2BPCM_L24;
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(bitDepth, bytesPerChannel, numChannels);
msg->channelSize16 = data->header.channelSize16;
msg->monitor = data->header.monitor;
msg->txmsg = data->header.txmsg;
msg->errIndicator = data->header.errIndicator;
msg->syncFrame = data->header.syncFrame;
msg->rfu2 = data->header.rfu2;
msg->timestamp = data->timestamp.TS;
msg->data = std::vector(bytestream.begin() + sizeof(HardwareA2BPacket), bytestream.end());
const uint8_t *bytes = bytestream.data();
bytes+=coreMiniMessageHeaderSize;
uint8_t channel = 0;
for(uint32_t i = 0; i < totalPackedLength; i += 2 * static_cast<uint32_t>(bytesPerChannel), bytes += 2 * bytesPerChannel, channel = (channel + 1) % numChannels) {
msg->addSample(
getSampleFromBytes(bytesPerChannel, bytes),
A2BMessage::A2BDirection::DownStream,
channel
);
msg->addSample(
getSampleFromBytes(bytesPerChannel, bytes + bytesPerChannel),
A2BMessage::A2BDirection::UpStream,
channel
);
}
return msg;
}
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& /*report*/) {
constexpr size_t a2btxMessageHeaderSize = 6;
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
size_t audioBufferSize = message.data.size();
size_t totalSize = a2btxMessageHeaderSize + audioBufferSize;
if(message.getBytesPerSample() != 2 && message.getBytesPerSample() != 4) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
bytestream.resize(totalSize, 0);
uint32_t offset = 0;
size_t sampleBytes = message.getNumSamples() * static_cast<size_t>(message.getBytesPerSample());
size_t totalSize = coreMiniMessageHeaderSize + sampleBytes;
if(totalSize > a2bMessageMaxLength) {
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
return false;
}
bytestream.reserve(totalSize);
bytestream.push_back(message.getNumChannels());
bytestream.push_back(message.channelSize16 ? 1 : 0);
bytestream[offset++] = 0;
bytestream[offset++] = 0;
bytestream[offset++] = (uint8_t)(audioBufferSize & 0xFF);
bytestream[offset++] = (uint8_t)((audioBufferSize >> 8) & 0xFF);
bytestream[offset++] = (uint8_t)((message.description >> 8) & 0xFF);
bytestream[offset++] = (uint8_t)(message.description & 0xFF);
uint8_t a2b2Bits = 0;
if(message.monitor) {
a2b2Bits = a2b2Bits | 1;
}
std::copy(message.data.begin(), message.data.end(), bytestream.begin() + offset);
if(message.txmsg) {
a2b2Bits = a2b2Bits | (1 << 1);
}
if(message.errIndicator) {
a2b2Bits = a2b2Bits | (1 << 2);
}
if(message.syncFrame) {
a2b2Bits = a2b2Bits | (1 << 3);
}
bytestream.push_back(a2b2Bits);
bytestream.push_back(0);
bytestream.push_back(static_cast<uint8_t>(message.rfu2));
bytestream.push_back(static_cast<uint8_t>(message.rfu2 >> 8));
for(size_t i = 0; i < (coreMiniMessageHeaderSize - a2bHeaderSize); i++)
bytestream.push_back(0);
uint8_t numChannels = message.getNumChannels();
uint8_t channel = 0;
uint32_t frame = 0;
auto writeSample = [&](A2BPCMSample&& sample) {
for(uint32_t i = 0; i < static_cast<uint32_t>(message.getBytesPerSample()); i++) {
bytestream.push_back(static_cast<uint8_t>((sample >> (i*8))));
}
};
while(true) {
auto dsSample = message.getSample(A2BMessage::A2BDirection::DownStream, channel, frame);
auto usSample = message.getSample(A2BMessage::A2BDirection::UpStream, channel, frame);
// Check if getSample failed for both downstream and upstream
if(!dsSample && !usSample) {
if(channel != 0) {
//Incomplete frame, the frame we are currently on does not contain all channel samples
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
return false;
}
// Since no samples have been written for the current frame yet and there are no more
// samples in both upstream and downstream, we can break and end parsing.
break;
}
// Since the first case failed, at least one of the streams still has samples.
// This case checks to see if the other stream does not have a sample.
else if(!dsSample || !usSample) {
// Report an error since we must have a one to one correspondence between upstream
// and downstream.
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
return false;
}
writeSample(std::move(dsSample.value()));
writeSample(std::move(usSample.value()));
channel = (channel + 1) % numChannels;
if(channel == 0)
frame++;
}
return true;
}
@@ -1,45 +0,0 @@
#include "icsneo/communication/packet/componentversionpacket.h"
#include "icsneo/communication/message/componentversionsmessage.h"
using namespace icsneo;
#pragma pack(push, 2)
struct PackedComponentVersion {
uint8_t valid;
uint8_t expansionSlot;
uint8_t componentInfo; // Component specific data (e.g. Linux: boot device)
uint8_t reserved;
uint32_t identifier;
uint32_t dotVersion; // Represents a.b.c.d, a.b.c, or a.b, depending on leading zeros.
uint32_t commitHash;
};
static constexpr size_t MaxReportedVersions = 16;
struct ComponentVersionsResponse {
ExtendedResponseMessage::ResponseHeader header;
uint16_t numVersions;
PackedComponentVersion versions[MaxReportedVersions];
};
#pragma pack(pop)
std::shared_ptr<ComponentVersionsMessage> ComponentVersionPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
auto msg = std::make_shared<ComponentVersionsMessage>();
// Length checks: At least a header and numVersions field.
if(bytes.size() < sizeof(ExtendedResponseMessage::ResponseHeader) + 2) {
return msg; // Empty
}
// Get a reference to the payload to fully validate the length
const auto& response = *reinterpret_cast<const ComponentVersionsResponse*>(bytes.data());
// Expected size is the header, numVersions field, and numVersions ComponentVersion objects.
auto expectedSize = sizeof(ExtendedResponseMessage::ResponseHeader) + 2 + (response.numVersions * sizeof(ComponentVersion));
// If the response is malformed (too small), return an empty message.
if(bytes.size() < expectedSize) {
return msg; // Empty
}
// Unpack into the portable class
for(unsigned int i = 0; i < response.numVersions; ++i) {
const auto& packedVersion = response.versions[i];
msg->versions.emplace_back(packedVersion.valid, packedVersion.componentInfo, packedVersion.identifier, packedVersion.dotVersion, packedVersion.commitHash);
}
return msg;
}
@@ -1,38 +0,0 @@
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
#include "icsneo/communication/message/genericbinarystatusmessage.h"
using namespace icsneo;
#pragma pack(push, 2)
struct GenericBinaryStatusResponse {
ExtendedResponseMessage::ResponseHeader header;
size_t size;
uint16_t index;
uint16_t status;
};
#pragma pack(pop)
std::shared_ptr<GenericBinaryStatusMessage> GenericBinaryStatusPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
if(bytes.size() < sizeof(GenericBinaryStatusResponse)) {
return nullptr;
}
auto msg = std::make_shared<GenericBinaryStatusMessage>();
const auto& response = *reinterpret_cast<const GenericBinaryStatusResponse*>(bytes.data());
msg->binarySize = response.size;
msg->binaryIndex = response.index;
msg->binaryStatus = response.status;
return msg;
}
std::vector<uint8_t> GenericBinaryStatusPacket::EncodeArguments(uint16_t binaryIndex) {
std::vector<uint8_t> bytestream(sizeof(GenericBinaryStatusResponse));
auto& parameters = *reinterpret_cast<GenericBinaryStatusResponse*>(bytestream.data());
parameters.index = binaryIndex;
return bytestream;
}
@@ -1,50 +0,0 @@
#include "icsneo/communication/packet/hardwareinfopacket.h"
#include "icsneo/communication/message/hardwareinfo.h"
#include <iostream>
using namespace icsneo;
#pragma pack(push, 1)
typedef struct
{
uint8_t valid;
struct
{
uint8_t day;
uint8_t month;
uint16_t year;
} manufactureDate;
struct
{
uint8_t major;
uint8_t minor;
} hwRev;
uint8_t deviceId;
struct
{
uint8_t major;
uint8_t minor;
} blVersion;
} HardwareInfoFrame;
#pragma pack(pop)
std::shared_ptr<HardwareInfo> HardwareInfoPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
if(bytes.size() < (sizeof(HardwareInfoFrame) + 1)) {
return nullptr;
}
const auto* frame = reinterpret_cast<const HardwareInfoFrame*>(&bytes[1]);
auto msg = std::make_shared<HardwareInfo>();
msg->manufactureDate.day = frame->manufactureDate.day;
msg->manufactureDate.year = frame->manufactureDate.year;
msg->manufactureDate.month = frame->manufactureDate.month;
msg->hardwareRevision.major = frame->hwRev.major;
msg->hardwareRevision.minor = frame->hwRev.minor;
msg->bootloaderVersion.major = frame->blVersion.major;
msg->bootloaderVersion.minor = frame->blVersion.minor;
return msg;
}
-147
View File
@@ -1,147 +0,0 @@
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/packetizer.h"
namespace icsneo {
std::shared_ptr<Message> HardwareLINPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
const HardwareLINPacket* packet = reinterpret_cast<const HardwareLINPacket*>(bytestream.data());
size_t numDataBytes = packet->CoreMiniBitsLIN.len;
size_t numHeaderBytes = sizeof(HardwareLINPacket::CoreMiniBitsLIN);
if( (sizeof(HardwareLINPacket) != bytestream.size()) ||
((numDataBytes + numHeaderBytes) > bytestream.size()) )
return nullptr;
if(numDataBytes)
--numDataBytes; //If data is present, there will be a checksum included
auto msg = std::make_shared<LINMessage>(static_cast<uint8_t>(packet->CoreMiniBitsLIN.ID));
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
msg->isEnhancedChecksum = static_cast<bool>(packet->CoreMiniBitsLIN.TxChkSumEnhanced);
/* Minimum one responder byte and one checksum byte. */
if(2u > packet->CoreMiniBitsLIN.len)
msg->linMsgType = LINMessage::Type::LIN_ERROR;
auto dataStart = bytestream.begin() + numHeaderBytes;
std::copy(dataStart, (dataStart+numDataBytes), std::back_inserter(msg->data));
/* If OK, validate the checksum*/
auto isChecksumInvalid = [&]() -> bool {
/* messages with no data have no checksum (e.g. header only) */
if(!msg->data.size())
return true;
uint8_t checkSum = (8 > numDataBytes) ? *(dataStart + numDataBytes) : packet->CoreMiniBitsLIN.LINByte9;
LINMessage::calcChecksum(*msg);
if(checkSum != msg->checksum) {
msg->isEnhancedChecksum = true;
LINMessage::calcChecksum(*msg);
if(checkSum != msg->checksum) {
msg->isEnhancedChecksum = false;
msg->checksum = checkSum;
return true;
}
}
return false;
};
/* if any of the status bits are set, then this is
either a failed reception or a bus status update. */
msg->errFlags =
{
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxOnlyBreak),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxOnlyBreakSync),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrTxRxMismatch),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxBreakNotZero),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxBreakTooShort),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxSyncNot55),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxDataGreater8),
static_cast<bool>(packet->CoreMiniBitsLIN.SyncFerr),
static_cast<bool>(packet->CoreMiniBitsLIN.MidFerr),
static_cast<bool>(packet->CoreMiniBitsLIN.ResponderByteFerr),
isChecksumInvalid(), /* ErrChecksumMatch */
};
msg->statusFlags =
{
static_cast<bool>(packet->CoreMiniBitsLIN.TxChkSumEnhanced),
static_cast<bool>(packet->CoreMiniBitsLIN.TXCommander),
static_cast<bool>(packet->CoreMiniBitsLIN.TXResponder),
static_cast<bool>(packet->CoreMiniBitsLIN.TxAborted),
static_cast<bool>(packet->CoreMiniBitsLIN.UpdateResponderOnce),
static_cast<bool>(packet->CoreMiniBitsLIN.HasUpdatedResponderOnce),
static_cast<bool>(packet->CoreMiniBitsLIN.BusRecovered),
static_cast<bool>(packet->CoreMiniBitsLIN.BreakOnly)
};
if(msg->statusFlags.TxCommander || msg->statusFlags.TxResponder)
msg->linMsgType = LINMessage::Type::LIN_COMMANDER_MSG;
else if(msg->statusFlags.BreakOnly)
msg->linMsgType = LINMessage::Type::LIN_BREAK_ONLY;
if( msg->errFlags.ErrRxBreakOnly || msg->errFlags.ErrRxBreakSyncOnly ||
msg->errFlags.ErrTxRxMismatch || msg->errFlags.ErrRxBreakNotZero ||
msg->errFlags.ErrRxBreakTooShort || msg->errFlags.ErrRxSyncNot55 ||
msg->errFlags.ErrRxDataLenOver8 || msg->errFlags.ErrFrameSync ||
msg->errFlags.ErrFrameMessageID || msg->errFlags.ErrChecksumMatch ||
msg->errFlags.ErrFrameResponderData )
{ msg->linMsgType = LINMessage::Type::LIN_ERROR; }
msg->timestamp = packet->timestamp;
return msg;
}
bool HardwareLINPacket::EncodeFromMessage(LINMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
{
uint8_t size = ((std::min<size_t>(8ul, message.data.size()) + 3ul) & 0xFu);
if(size > 3) { ++size; } // add a checksum byte if there's data
switch(message.linMsgType) {
case LINMessage::Type::LIN_HEADER_ONLY:
case LINMessage::Type::LIN_COMMANDER_MSG:
{
size |= 0x80u;
break;
}
case LINMessage::Type::LIN_BREAK_ONLY:
{
size |= 0x20u;
break;
}
case LINMessage::Type::NOT_SET:
{
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
return false;
}
default:
break;
}
message.protectedID = message.calcProtectedID(message.ID);
bytestream.insert(bytestream.end(),
{
static_cast<uint8_t>(0x00u),
static_cast<uint8_t>(size),
static_cast<uint8_t>((message.description >> 8) & 0xFF),
static_cast<uint8_t>(message.description & 0xFF),
static_cast<uint8_t>(message.protectedID)
});
switch(message.linMsgType) {
case(LINMessage::Type::LIN_COMMANDER_MSG):
case(LINMessage::Type::LIN_UPDATE_RESPONDER):
{
std::copy(message.data.begin(), message.data.end(), std::back_inserter(bytestream));
LINMessage::calcChecksum(message);
bytestream.push_back(message.checksum);
break;
}
default:
break;
}
if(bytestream.size() % 2)
bytestream.push_back(0x41); //padding
return true;
}
} //namespace icsneo
-125
View File
@@ -1,125 +0,0 @@
#include "icsneo/communication/packet/livedatapacket.h"
#include "icsneo/communication/message/livedatamessage.h"
#include <cstring>
#include <vector>
namespace icsneo {
std::shared_ptr<Message> HardwareLiveDataPacket::DecodeToMessage(const std::vector<uint8_t>& bytes, const device_eventhandler_t& report) {
if(bytes.empty() || (bytes.size() < (sizeof(LiveDataHeader) + sizeof(ExtResponseHeader)))) {
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
return nullptr;
}
const auto header = reinterpret_cast<const ExtResponseHeader*>(bytes.data());
if(ExtendedCommand::LiveData != static_cast<ExtendedCommand>(header->command)) {
report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error);
return nullptr;
}
const auto ldHeader = reinterpret_cast<const LiveDataHeader*>(bytes.data() + sizeof(ExtResponseHeader));
// Versioning check to avoid bad data interpretation between disparate libicsneo and firmware versions
if(icsneo::LiveDataUtil::LiveDataVersion != ldHeader->version) {
report(APIEvent::Type::LiveDataVersionMismatch, APIEvent::Severity::Error);
return nullptr;
}
switch(LiveDataCommand(ldHeader->cmd)) {
case LiveDataCommand::RESPONSE: {
auto retMsg = std::make_shared<LiveDataValueMessage>();
const auto responseBytes = reinterpret_cast<const LiveDataValueResponse*>(ldHeader);
retMsg->handle = responseBytes->handle;
retMsg->cmd = static_cast<LiveDataCommand>(responseBytes->cmd);
retMsg->numArgs = responseBytes->numArgs;
for(uint32_t i = 0; i < retMsg->numArgs; ++i) {
retMsg->values.emplace_back(std::make_shared<LiveDataValue>(responseBytes->values[i]));
}
return retMsg;
}
case LiveDataCommand::STATUS: {
auto retMsg = std::make_shared<LiveDataStatusMessage>();
const auto responseBytes = reinterpret_cast<const LiveDataStatusResponse*>(ldHeader);
retMsg->handle = responseBytes->handle;
retMsg->cmd = static_cast<LiveDataCommand>(responseBytes->cmd);
retMsg->status = responseBytes->status;
retMsg->requestedCommand = static_cast<LiveDataCommand>(responseBytes->requestedCommand);
return retMsg;
}
default: {
report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error);
break;
}
}
return nullptr;
}
bool HardwareLiveDataPacket::EncodeFromMessage(LiveDataMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
uint16_t payloadSize = 0;
switch(message.cmd) {
case LiveDataCommand::SUBSCRIBE: {
auto commandMsg = reinterpret_cast<LiveDataCommandMessage*>(&message);
const auto numArgs = commandMsg->args.size();
if(numArgs) {
payloadSize = static_cast<uint16_t>(sizeof(LiveDataSubscribe) + (sizeof(LiveDataArgument) * (numArgs-1)));
bytestream.resize((payloadSize + sizeof(ExtendedCommandHeader)),0);
LiveDataSubscribe* out = reinterpret_cast<LiveDataSubscribe*>(bytestream.data() + sizeof(ExtendedCommandHeader));
out->version = icsneo::LiveDataUtil::LiveDataVersion;
out->cmd = static_cast<uint32_t>(commandMsg->cmd);
if(!commandMsg->handle)
commandMsg->handle = LiveDataUtil::getNewHandle();
out->handle = commandMsg->handle;
out->numArgs = static_cast<uint32_t>(commandMsg->args.size());
out->freqMs = static_cast<uint32_t>(commandMsg->updatePeriod.count());
out->expireMs = static_cast<uint32_t>(commandMsg->expirationTime.count());
for(size_t i = 0; i < numArgs; ++i) {
out->args[i].objectType = commandMsg->args[i]->objectType;
out->args[i].objectIndex = commandMsg->args[i]->objectIndex;
out->args[i].signalIndex = commandMsg->args[i]->signalIndex;
out->args[i].valueType = commandMsg->args[i]->valueType;
}
} else {
report(APIEvent::Type::LiveDataInvalidArgument, APIEvent::Severity::Error);
return false;
}
break;
}
case LiveDataCommand::UNSUBSCRIBE: {
payloadSize = sizeof(LiveDataHeader);
bytestream.resize((payloadSize + sizeof(ExtendedCommandHeader)),0);
auto ldUnsubMsg = reinterpret_cast<LiveDataHeader*>(bytestream.data() + sizeof(ExtendedCommandHeader));
ldUnsubMsg->version = static_cast<uint32_t>(icsneo::LiveDataUtil::LiveDataVersion);
ldUnsubMsg->cmd = static_cast<uint32_t>(message.cmd);
ldUnsubMsg->handle = static_cast<uint32_t>(message.handle);
break;
}
case LiveDataCommand::CLEAR_ALL: {
payloadSize = sizeof(LiveDataHeader);
bytestream.resize((payloadSize + sizeof(ExtendedCommandHeader)),0);
auto clearMsg = reinterpret_cast<LiveDataHeader*>(bytestream.data() + sizeof(ExtendedCommandHeader));
clearMsg->version = static_cast<uint32_t>(icsneo::LiveDataUtil::LiveDataVersion);
clearMsg->cmd = static_cast<uint32_t>(message.cmd);
break;
}
default: {
report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error);
return false;
}
}
// +1 for AA, another +1 for firmware nuance
uint16_t fullSize = static_cast<uint16_t>(1 + sizeof(ExtendedCommandHeader) + payloadSize) + 1;
ExtendedCommandHeader* header = reinterpret_cast<ExtendedCommandHeader*>(bytestream.data());
if(!header) {
report(APIEvent::Type::LiveDataEncoderError, APIEvent::Severity::Error);
return false;
}
header->netid = static_cast<uint8_t>(Network::NetID::Main51);
header->fullLength = fullSize;
header->command = static_cast<uint8_t>(Command::Extended);
header->extendedCommand = static_cast<uint16_t>(ExtendedCommand::LiveData);
header->payloadLength = payloadSize;
return true;
}
} // namespace icsneo
-79
View File
@@ -1,79 +0,0 @@
#include "icsneo/communication/packet/mdiopacket.h"
namespace icsneo
{
const size_t HardwareMDIOPacket::mdioDataSize = 2;
std::shared_ptr<Message> HardwareMDIOPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream)
{
auto msg = std::make_shared<MDIOMessage>();
const HardwareMDIOPacket* packet = reinterpret_cast<const HardwareMDIOPacket*>(bytestream.data());
if((sizeof(HardwareMDIOPacket) != (bytestream.size())) || (packet->length != 0))
{
return nullptr;
}
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
msg->clause = static_cast<MDIOMessage::Clause>(packet->header.ST);
msg->direction = static_cast<MDIOMessage::Direction>((packet->header.OP & 0x2) ? 1 : 0);
msg->isTXMsg = static_cast<bool>(packet->header.TRANSMIT & 0x01u);
msg->phyAddress = (packet->header.PHY_ADDR & 0x1Fu);
if (msg->clause == MDIOMessage::Clause::Clause45)
{ // 16-bit register address
msg->devAddress = (packet->header.C45_DEVTYPE & 0x1Fu);
msg->regAddress = (packet->header.REG_ADDR & 0xFFFFu);
}
else
{ // 5-bit register address
msg->devAddress = 0;
msg->regAddress = (packet->header.REG_ADDR & 0x1Fu);
}
msg->isTXMsg = static_cast<bool>(packet->header.TRANSMIT & 0x01u);
msg->txTimeout = static_cast<bool>(packet->header.ERR_TIMEOUT & 0x01u);
msg->txAborted = static_cast<bool>(packet->header.ERR_JOB_CANCELLED & 0x01u);
msg->txInvalidBus = static_cast<bool>(packet->header.ERR_INVALID_BUS & 0x01u);
msg->txInvalidPhyAddr = static_cast<bool>(packet->header.ERR_INVALID_PHYADDR & 0x01u);
msg->txInvalidRegAddr = static_cast<bool>(packet->header.ERR_INVALID_REGADDR & 0x01u);
msg->txInvalidClause = static_cast<bool>(packet->header.ERR_UNSUPPORTED_CLAUSE & 0x01u);
msg->txInvalidOpcode = static_cast<bool>(packet->header.ERR_UNSUPPORTED_OPCODE & 0x01u);
//We don't care about 0xTRB0Dx in this case...
//copy 0xTRB0STAT even though we likely won't use it either
msg->description = packet->stats;
msg->timestamp = (packet->timestamp & (0x7FFFFFFFFFFFFFFFull));
std::copy(packet->data, packet->data + mdioDataSize, std::back_inserter(msg->data));
return msg;
}
bool HardwareMDIOPacket::EncodeFromMessage(const MDIOMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
{
const size_t numDataBytes = message.data.size();
if(mdioDataSize < numDataBytes)
{
report(APIEvent::Type::MDIOMessageExceedsMaxLength, APIEvent::Severity::Error);
return false;
}
uint8_t st = (message.clause == MDIOMessage::Clause::Clause45) ? 0x0 : 0x1;
uint8_t op = (message.direction == MDIOMessage::Direction::Read) ? 0x2 : 0x1;
uint8_t phyAddr = message.phyAddress & 0x1F;
uint16_t regAddr = (message.clause == MDIOMessage::Clause::Clause45) ? message.regAddress : message.regAddress & 0x1F;
uint8_t c45DevType = (message.clause == MDIOMessage::Clause::Clause45) ? message.devAddress & 0x1F : 0;
bytestream.push_back(static_cast<uint8_t>((message.description >> 8) & 0xFF)); // MSB first
bytestream.push_back(static_cast<uint8_t>(message.description & 0xFF)); // LSB
bytestream.push_back(op); // opcode
bytestream.push_back(st); // st (clause)
bytestream.push_back(phyAddr); // st (clause)
bytestream.push_back(c45DevType); // clause 45 device type
bytestream.push_back(regAddr & 0xFF); // reg addr LSB
bytestream.push_back((regAddr >> 8) & 0xFF); // reg addr MSB
std::copy(message.data.begin(), message.data.end(), std::back_inserter(bytestream));
return true;
}
}
@@ -1,44 +0,0 @@
#include "icsneo/communication/packet/supportedfeaturespacket.h"
#include "icsneo/communication/message/supportedfeaturesmessage.h"
using namespace icsneo;
static constexpr uint16_t SupportedFeaturesCommandVersion = 1;
static constexpr size_t NumSupportedFeaturesFields =
(static_cast<size_t>(SupportedFeature::numSupportedFeatures) + 31) / 32;
#pragma pack(push, 2)
struct SupportedFeaturesResponse {
ExtendedResponseMessage::ResponseHeader header;
uint16_t cmdVersion;
uint16_t numValidBits;
uint32_t featuresFields[NumSupportedFeaturesFields];
};
#pragma pack(pop)
std::shared_ptr<SupportedFeaturesMessage> SupportedFeaturesPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
auto msg = std::make_shared<SupportedFeaturesMessage>();
// Length check: At least a header, a 2-byte cmdVersion field, and a 2-byte numValidBits field.
if(bytes.size() < sizeof(ExtendedResponseMessage::ResponseHeader) + 4) {
return msg; // Empty
}
// Get a reference to the payload to fully validate the length
const auto& response = *reinterpret_cast<const SupportedFeaturesResponse*>(bytes.data());
if(response.cmdVersion != SupportedFeaturesCommandVersion) {
return msg;
}
// Expected size is the header, cmdVersion and numValidBits fields, plus the number of 32-bit bitfields in the response based on numValidBits
auto expectedSize = sizeof(ExtendedResponseMessage::ResponseHeader) + 4 + ((response.numValidBits + 31) / 32) * 4;
// If the response is malformed (too small), return an empty message
if(bytes.size() < expectedSize) {
return msg; // Empty
}
unsigned int loopLimit = std::min<unsigned int>(response.numValidBits, static_cast<unsigned int>(SupportedFeature::numSupportedFeatures));
for(unsigned int i = 0; i < loopLimit; ++i) {
uint32_t wordOffset = i / 32;
uint32_t bitOffset = i % 32;
if((response.featuresFields[wordOffset] >> bitOffset) & 1) {
msg->features.insert(static_cast<SupportedFeature>(i));
}
}
return msg;
}
+28 -81
View File
@@ -1,4 +1,5 @@
#include "icsneo/communication/packetizer.h"
#include <iostream>
#include <iomanip>
using namespace icsneo;
@@ -24,8 +25,9 @@ std::vector<uint8_t>& Packetizer::packetWrap(std::vector<uint8_t>& data, bool sh
return data;
}
bool Packetizer::input(RingBuffer& bytes) {
bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
bool haveEnoughData = true;
bytes.insert(bytes.end(), inputBytes.begin(), inputBytes.end());
while(haveEnoughData) {
switch(state) {
@@ -47,97 +49,44 @@ bool Packetizer::input(RingBuffer& bytes) {
haveEnoughData = false;
break;
}
packetLength = bytes[1] >> 4 & 0xF;
packet.network = Network(bytes[1] & 0xF); // Lower nibble of the second byte is the network ID
if(packetLength == 0) {
packetLength = bytes[1] >> 4 & 0xf; // Upper nibble of the second byte denotes the packet length
packet.network = Network(bytes[1] & 0xf); // Lower nibble of the second byte is the network ID
if(packetLength == 0) { // A length of zero denotes a long style packet
state = ReadState::ParseLongStylePacketHeader;
break;
} else if(packetLength == 0xA && packet.network == Network::NetID::DiskData) {
state = ReadState::ParseDiskDataHeader;
break;
checksum = false;
headerSize = 6;
} else {
state = ReadState::GetData;
checksum = true; // Even if checksum is not explicitly disallowed, we enable it here, as this goes into length calculation
headerSize = 2;
packetLength += 2; // The packet length given in short packets does not include header
}
checksum = true; // Even if checksum is not explicitly disallowed, we enable it here, as this goes into length calculation
headerSize = 2;
packetLength += 2; // The packet length given in short packets does not include header
currentIndex++;
state = ReadState::GetData;
break;
case ReadState::ParseLongStylePacketHeader:
if(bytes.size() < 6) {
haveEnoughData = false;
break;
}
packetLength = bytes[2]; // Long packets have a little endian length on bytes 3 and 4
packetLength |= bytes[3] << 8;
packet.network = Network(((bytes[5] << 8) | bytes[4]), false); // Long packets have their netid stored as little endian on bytes 5 and 6. Devices never send actual VNET IDs so we must not perform ID expansion here.
packet.network = Network((bytes[5] << 8) | bytes[4]); // Long packets have their netid stored as little endian on bytes 5 and 6
currentIndex += 4;
/* Long packets can't have a length less than 6, because that would indicate a negative payload size.
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
* payload, and not the header or checksum.
*/
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
* payload, and not the header or checksum.
*/
if(packetLength < 6 || packetLength > 4000) {
bytes.pop_front();
EventManager::GetInstance().add(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
state = ReadState::SearchForHeader;
break;
} else {
state = ReadState::GetData;
}
checksum = false;
headerSize = 6;
currentIndex += 5;
state = ReadState::GetData;
break;
case ReadState::ParseDiskDataHeader:
if(bytes.size() < 3) {
haveEnoughData = false;
break;
}
if(bytes[2] != 0xAA) {
bytes.pop_front();
state = ReadState::SearchForHeader;
break;
}
if(bytes.size() < 4) {
haveEnoughData = false;
break;
}
if(bytes[3] != 0x55) {
bytes.pop_front();
state = ReadState::SearchForHeader;
break;
}
if(bytes.size() < 5) {
haveEnoughData = false;
break;
}
if(bytes[4] != 0x55) {
bytes.pop_front();
state = ReadState::SearchForHeader;
break;
}
if(bytes.size() < 7) {
haveEnoughData = false;
break;
}
packetLength = bytes[5] | (bytes[6] << 8);
checksum = false;
headerSize = 7;
packetLength += headerSize;
currentIndex += 6;
state = ReadState::GetData;
break;
case ReadState::GetData:
// We do not include the checksum in packetLength so it doesn't get copied into the payload buffer
@@ -149,25 +98,23 @@ bool Packetizer::input(RingBuffer& bytes) {
packet.data.clear();
if(packetLength > 0)
packet.data.resize(packetLength - headerSize);
bytes.read(packet.data.data(), currentIndex, (packetLength - currentIndex));
currentIndex = packetLength;
auto i = 0;
while(currentIndex < packetLength)
packet.data[i++] = bytes[currentIndex++];
if(disableChecksum || !checksum || bytes[currentIndex] == ICSChecksum(packet.data)) {
// Got a good packet
gotGoodPackets = true;
processedPackets.push_back(std::make_shared<Packet>(packet));
bytes.pop(packetLength);
if(packet.network == Network::NetID::DiskData && (packetLength - headerSize) % 2 == 0) {
for (auto a = 0; a < packetLength; a++)
bytes.pop_front();
}
} else {
if(gotGoodPackets) // Don't complain unless we've already gotten a good packet, in case we started in the middle of a stream
report(APIEvent::Type::PacketChecksumError, APIEvent::Severity::Error);
bytes.pop_front(); // Drop the first byte so it doesn't get picked up again
}
// Reset for the next packet
currentIndex = 0;
state = ReadState::SearchForHeader;
-91
View File
@@ -1,91 +0,0 @@
#include "icsneo/communication/ringbuffer.h"
#include <stdexcept>
namespace icsneo {
RingBuffer::RingBuffer(size_t bufferSize) : readCursor(0), writeCursor(0) {
// round the buffer size to the nearest power of 2
bufferSize = RoundUp(bufferSize);
mask = bufferSize - 1;
buf = new uint8_t[bufferSize];
}
RingBuffer::~RingBuffer() {
delete[] buf;
buf = nullptr;
}
const uint8_t& RingBuffer::operator[](size_t offset) const {
return get(offset);
}
size_t RingBuffer::size() const {
// The values in the cursors are monotonic, i.e. they only ever increment. They can be considered to be the total number of elements ever written or read
auto currentWriteCursor = writeCursor.load(std::memory_order_relaxed);
auto currentReadCursor = readCursor.load(std::memory_order_relaxed);
// Using unmasked values, writeCursor is guaranteed to be >= readCursor. If they are equal that means the buffer is empty
return currentWriteCursor - currentReadCursor;
}
void RingBuffer::pop_front() {
pop(1);
}
void RingBuffer::pop(size_t count) {
if (size() < count) {
throw std::runtime_error("RingBuffer: Underflow");
}
readCursor.fetch_add(count, std::memory_order_release);
}
const uint8_t& RingBuffer::get(size_t offset) const {
if (offset >= size()) {
throw std::runtime_error("RingBuffer: Index out of range");
}
auto currentReadCursor = readCursor.load(std::memory_order_acquire);
return *resolve(currentReadCursor, offset);
}
bool RingBuffer::write(const uint8_t* addr, size_t length) {
const auto freeSpace = (capacity() - size());
if (length > freeSpace) {
return false;
}
auto currentWriteCursor = writeCursor.load(std::memory_order_relaxed);
auto spaceAtEnd = std::min(freeSpace, capacity() - (currentWriteCursor & mask)); // number of bytes from (masked) writeCursor to the end of the writable space (i.e. we reach the masked read cursor or the end of the buffer)
auto firstCopySize = std::min(spaceAtEnd, length);
(void)memcpy(resolve(currentWriteCursor, 0), addr, firstCopySize);
if (firstCopySize < length)
{
(void)memcpy(buf, &addr[firstCopySize], length - firstCopySize);
}
writeCursor.store(currentWriteCursor + length, std::memory_order_release);
return true;
}
bool RingBuffer::write(const std::vector<uint8_t>& source) {
return write(source.data(), source.size());
}
bool RingBuffer::read(uint8_t* dest, size_t startIndex, size_t length) const {
auto currentSize = size();
if ((startIndex >= currentSize) || ((startIndex + length) > size())) {
return false;
}
auto currentReadCursor = readCursor.load(std::memory_order_relaxed);
auto bytesAtEnd = std::min<size_t>(capacity() - ((currentReadCursor + startIndex) & mask), length);
const auto bytesAtStart = (length - bytesAtEnd);
(void)memcpy(dest, resolve(currentReadCursor, startIndex), bytesAtEnd);
if (bytesAtStart > 0) {
(void)memcpy(&dest[bytesAtEnd], buf, bytesAtStart);
}
return true;
}
void RingBuffer::clear() {
pop(size());
}
}
+179
View File
@@ -0,0 +1,179 @@
#include <cstring>
#include "icsneo/communication/sdio.h"
#include "icsneo/platform/sharedsemaphore.h"
#include "icsneo/platform/sharedmemory.h"
#include "icsneo/device/device.h"
#include "icsneo/communication/socket.h"
using namespace icsneo;
void SDIO::Find(std::vector<FoundDevice>& found) {
auto socket = lockSocket();
if(!socket.writeTyped(RPC::DEVICE_FINDER_FIND_ALL))
return;
uint16_t count;
if(!socket.readTyped(count))
return;
static constexpr auto serialSize = sizeof(FoundDevice::serial);
std::vector<std::array<char, sizeof(FoundDevice::serial)>> serials(count);
if(!socket.read(serials.data(), serials.size() * serialSize))
return;
for(const auto& serial : serials) {
auto& foundDevice = found.emplace_back();
for(std::size_t i = 0; i < serialSize - 1 /* omit '\0' */; i++)
foundDevice.serial[i] = static_cast<char>(std::toupper(serial[i]));
foundDevice.makeDriver = [](const device_eventhandler_t& r, neodevice_t& d) {
return std::unique_ptr<SDIO>(new SDIO(r, d));
};
}
}
bool SDIO::open() {
{
auto socket = lockSocket();
if(!(socket.writeTyped(RPC::SDIO_OPEN) && socket.writeString(device.device->getSerial())))
return false;
if(bool ret; !(socket.readTyped(ret) && ret))
return false;
{
std::string mailboxName;
if(!socket.readString(mailboxName))
return false;
if(!inboundIO.open(mailboxName))
return false;
}
{
std::string mailboxName;
if(!socket.readString(mailboxName))
return false;
if(!outboundIO.open(mailboxName))
return false;
}
}
readThread = std::thread(&SDIO::readTask, this);
writeThread = std::thread(&SDIO::writeTask, this);
deviceOpen = true;
return deviceOpen;
}
bool SDIO::close() {
if(!isOpen() && !isDisconnected()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return false;
}
closing = true;
// wait for the reader/writer threads to close
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// unblocks the reader/writer threads
if(!inboundIO.close())
return false;
if(!outboundIO.close())
return false;
if(readThread.joinable())
readThread.join();
if(writeThread.joinable())
writeThread.join();
{
auto socket = lockSocket();
if(!socket.writeTyped(RPC::SDIO_CLOSE))
return false;
if(!socket.writeString(device.device->getSerial()))
return false;
if(bool ret; !(socket.readTyped(ret) && ret))
return false;
}
uint8_t flush;
WriteOperation flushop;
while(readQueue.try_dequeue(flush)) {}
while(writeQueue.try_dequeue(flushop)) {}
closing = false;
disconnected = false;
deviceOpen = false;
return true;
}
bool SDIO::isOpen() {
return deviceOpen;
}
void SDIO::readTask() {
uint8_t data[MAX_DATA_SIZE];
uint16_t messageLength;
while(!closing) {
if(!inboundIO.read(data, messageLength, std::chrono::milliseconds(100))) {
if(!inboundIO)
break;
continue;
}
if(messageLength > 0) {
if(messageLength > MAX_DATA_SIZE) { // split message
std::vector<uint8_t> reassembled(messageLength);
std::memcpy(reassembled.data(), data, MAX_DATA_SIZE);
auto offset = reassembled.data() + MAX_DATA_SIZE;
for(auto remaining = messageLength - MAX_DATA_SIZE; remaining > 0; remaining -= messageLength) {
if(!inboundIO.read(offset, messageLength, std::chrono::milliseconds(10))) {
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
break;
}
offset += messageLength;
}
readQueue.enqueue_bulk(reassembled.data(), reassembled.size());
} else {
readQueue.enqueue_bulk(data, messageLength);
}
}
}
}
void SDIO::writeTask() {
WriteOperation writeOp;
while(!closing && !isDisconnected()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
const auto dataSize = static_cast<LengthFieldType>(writeOp.bytes.size());
const auto tryWrite = [&](const void* input, LengthFieldType length) -> bool {
for(int i = 0; i < 50; ++i) { // try to write for 5s, making sure we can close if need be
if(outboundIO.write(input, length, std::chrono::milliseconds(100)))
return true;
if(!outboundIO)
return false;
}
disconnected = true;
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
return false;
};
if(!tryWrite(writeOp.bytes.data(), dataSize))
continue;
if(writeOp.bytes.size() > MAX_DATA_SIZE) {
auto offset = writeOp.bytes.data() + MAX_DATA_SIZE;
for(LengthFieldType remaining = dataSize - MAX_DATA_SIZE; remaining > 0; ) {
const auto toWrite = std::min(MAX_DATA_SIZE, remaining);
if(!tryWrite(offset, toWrite))
break;
remaining -= toWrite;
offset += toWrite;
}
}
}
}
+257
View File
@@ -0,0 +1,257 @@
#include "icsneo/communication/socket.h"
#include "icsneo/api/event.h"
#include "icsneo/api/eventmanager.h"
namespace icsneo {
bool SocketBase::open() {
#ifdef _WIN32
WSADATA wsaData;
if(::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
return false;
}
#endif
if((sockFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) < 0) {
#ifdef _WIN32
::WSACleanup();
#endif
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
return false;
}
sockIsOpen = true;
return true;
}
bool SocketBase::close() {
#ifdef _WIN32
if(::closesocket(sockFileDescriptor) < 0) {
// should probably check for WSAEWOULDBLOCK as ::closesocket must be repeated to close in that case
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
#endif
return false;
}
::WSACleanup();
#else
// ignore ENOTCONN from ::shutdown as the peer may have already forcibly closed its socket (e.g. a crash)
if( ((::shutdown(sockFileDescriptor, SHUT_RDWR) < 0) && (ENOTCONN != errno)) ||
((::close(sockFileDescriptor) < 0) && (EBADF == errno)) )
{
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
#endif
return false;
}
#endif
sockIsOpen = false;
sockIsConnected = false;
return true;
}
bool SocketBase::connect() {
sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if( (!isOpen() && !open()) ||
(::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
(::connect(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
{
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToConnect, APIEvent::Severity::Error);
return false;
}
#ifdef _WIN32
DWORD tv = 5000u; // 5 second receive timeout but in windows
#else
struct timeval tv;
tv.tv_sec = 5u; // 5 second receive timeout
tv.tv_usec = 0;
setIgnoreSIGPIPE();
#endif
// Set the 5 second timeout from above in the socket options
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
sockIsConnected = true;
return true;
}
bool SocketBase::isOpen() {
return sockIsOpen;
}
bool SocketBase::isConnected() {
return sockIsConnected;
}
bool SocketBase::read(void* output, std::size_t length) {
if(!(isOpen() && isConnected()))
return false;
#ifdef _WIN32
return ::recv(sockFileDescriptor, (char*)output, (int)length, 0) > 0;
#else
if(::read(sockFileDescriptor, output, length) <= 0) {
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToRead, APIEvent::Severity::Error);
return false;
}
return true;
#endif
}
bool SocketBase::write(const void* input, std::size_t length) {
if(!(isOpen() && isConnected()))
return false;
#ifdef _WIN32
if(::send(sockFileDescriptor, (char*)input, (int)length, 0) < 0) {
switch(WSAGetLastError()) {
case WSAETIMEDOUT:
case WSAENOTCONN:
case WSAESHUTDOWN:
case WSAECONNRESET:
case WSAECONNABORTED:
{
sockIsOpen = false;
break;
}
default:
{
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
break;
}
}
return false;
}
#else
if(::write(sockFileDescriptor, input, length) < 0) {
switch(errno) {
case EPIPE:
case ETIMEDOUT:
{
sockIsOpen = false;
break;
}
default:
{
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
break;
}
}
return false;
}
#endif
return true;
}
bool SocketBase::readString(std::string& str) {
size_t length;
if(!read(&length, sizeof(length)))
return false;
str.resize(length);
if(!read(str.data(), length))
return false;
return true;
}
bool SocketBase::writeString(const std::string& str) {
size_t length = str.size();
if(!write(&length, sizeof(length)))
return false;
if(!write(str.data(), length))
return false;
return true;
}
ActiveSocket::ActiveSocket(SocketFileDescriptor sockFD) {
sockFileDescriptor = sockFD;
sockIsOpen = true;
sockIsConnected = true;
}
ActiveSocket::ActiveSocket(Protocol protocol, uint16_t port) {
this->protocol = protocol;
this->port = port;
}
ActiveSocket::~ActiveSocket() {
if(isOpen())
close();
}
LockedSocket::LockedSocket(SocketBase& base, std::unique_lock<std::mutex>&& l) :
SocketBase(base), lock(std::move(l)) {
}
LockedSocket lockSocket() {
static ActiveSocket socket(SocketBase::Protocol::TCP, RPC_PORT);
if(!socket.isOpen())
socket.open();
if(!socket.isConnected())
socket.connect();
static std::mutex lock;
return LockedSocket(socket, std::unique_lock<std::mutex>(lock));
}
void SocketBase::setIgnoreSIGPIPE() {
#ifndef _WIN32
struct sigaction sa{};
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
::sigaction(SIGPIPE, &sa, NULL);
#endif
}
Acceptor::Acceptor(Protocol protocol, uint16_t port)
: ActiveSocket(protocol, port) {
}
bool Acceptor::initialize() {
if(open() && bind() && listen()) {
isValid = true;
return true;
}
return false;
}
std::shared_ptr<ActiveSocket> Acceptor::accept()
{
if(!isValid)
return nullptr;
const SocketFileDescriptor acceptFd = ::accept(sockFileDescriptor, (sockaddr*)NULL, NULL);
if(acceptFd < 0)
return nullptr;
return std::make_shared<ActiveSocket>(acceptFd);
}
bool Acceptor::bind()
{
sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if( (::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
(::bind(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
{
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToBind, APIEvent::Severity::Error);
return false;
}
return true;
}
bool Acceptor::listen()
{
if(::listen(sockFileDescriptor, UINT8_MAX) < 0) {
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToListen, APIEvent::Severity::Error);
return false;
}
return true;
}
} //namespace icsneo
+178 -1862
View File
File diff suppressed because it is too large Load Diff
+31 -80
View File
@@ -1,8 +1,13 @@
#include <array>
#include "icsneo/device/devicefinder.h"
#include "icsneo/platform/devices.h"
#include "icsneo/device/founddevice.h"
#include "icsneo/communication/sdio.h"
#include "generated/extensions/builtin.h"
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#else
#ifdef ICSNEO_ENABLE_FIRMIO
#include "icsneo/platform/firmio.h"
#endif
@@ -18,13 +23,6 @@
#ifdef ICSNEO_ENABLE_FTDI
#include "icsneo/platform/ftdi.h"
#endif
#ifdef ICSNEO_ENABLE_FTD3XX
#include "icsneo/platform/ftd3xx.h"
#endif
#ifdef ICSNEO_ENABLE_TCP
#include "icsneo/platform/tcp.h"
#endif
using namespace icsneo;
@@ -47,30 +45,17 @@ static void makeIfPIDMatches(const FoundDevice& dev, std::vector<std::shared_ptr
into.push_back(std::make_shared<T>(dev));
}
template<typename T>
static void makeIfSerialRangeMatches(const FoundDevice& dev, std::vector<std::shared_ptr<Device>>& into) {
// Relies on the subclass to have
// `static constexpr uint32_t SERIAL_RANGE_LOW = 0x12345678`
// `static constexpr uint32_t SERIAL_RANGE_HIGH = 0x12345678`
// and also a public constructor `T(const FoundDevice& dev)`
// Use macro ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE() to create these
uint32_t serialNum = Device::SerialStringToNum(dev.serial);
if(serialNum >= Device::SerialStringToNum(T::SERIAL_RANGE_LOW) && serialNum <= Device::SerialStringToNum(T::SERIAL_RANGE_HIGH))
into.push_back(std::make_shared<T>(dev));
}
std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
static std::vector<FoundDevice> newDriverFoundDevices;
newDriverFoundDevices.clear();
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
SDIO::Find(newDriverFoundDevices);
#else
#ifdef ICSNEO_ENABLE_FIRMIO
FirmIO::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_TCP
TCP::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
PCAP::Find(newDriverFoundDevices);
#endif
@@ -82,9 +67,6 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
#ifdef ICSNEO_ENABLE_FTDI
FTDI::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_FTD3XX
FTD3XX::Find(newDriverFoundDevices);
#endif
// Weak because we don't want to keep devices open if they go out of scope elsewhere
@@ -137,10 +119,6 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<NeoOBD2SIM>(dev, newFoundDevices);
#endif
#ifdef __NEOVICONNECT_H_
makeIfSerialMatches<NeoVIConnect>(dev, newFoundDevices);
#endif
#ifdef __NEOVIFIRE_H_
makeIfPIDMatches<NeoVIFIRE>(dev, newFoundDevices);
#endif
@@ -153,10 +131,6 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<NeoVIFIRE3>(dev, newFoundDevices);
#endif
#ifdef __NEOVIFIRE3FLEXRAY_H_
makeIfSerialMatches<NeoVIFIRE3FlexRay>(dev, newFoundDevices);
#endif
#ifdef __NEOVIRED2_H_
makeIfSerialMatches<NeoVIRED2>(dev, newFoundDevices);
#endif
@@ -173,18 +147,6 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<RADA2B>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET_H_
makeIfSerialRangeMatches<RADComet>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET2_H_
makeIfSerialRangeMatches<RADComet2>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET3_H_
makeIfSerialMatches<RADComet3>(dev, newFoundDevices);
#endif
#ifdef __RADEPSILON_H_
makeIfSerialMatches<RADEpsilon>(dev, newFoundDevices);
#endif
@@ -209,14 +171,6 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<RADMoon2>(dev, newFoundDevices);
#endif
#ifdef __RADMOON2ZL_H_
makeIfSerialMatches<RADMoon2ZL>(dev, newFoundDevices);
#endif
#ifdef __RADMOON3_H_
makeIfSerialMatches<RADMoon3>(dev, newFoundDevices);
#endif
#ifdef __RADMOONDUO_H_
makeIfSerialMatches<RADMoonDuo>(dev, newFoundDevices);
#endif
@@ -274,7 +228,27 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
}
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
static std::vector<DeviceType> supportedDevices = {
static std::vector<DeviceType> supportedDevices;
if (!supportedDevices.empty())
return supportedDevices;
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
{
auto socket = lockSocket();
if(!socket.writeTyped(RPC::DEVICE_FINDER_GET_SUPORTED_DEVICES))
return supportedDevices;
uint16_t count;
if(!socket.readTyped(count))
return supportedDevices;
std::vector<devicetype_t> devices(count);
socket.read(devices.data(), devices.size() * sizeof(devicetype_t));
supportedDevices.reserve(count);
for(auto& dev : devices)
supportedDevices.emplace_back(DeviceType(dev));
}
#else
supportedDevices = {
#ifdef __ETHERBADGE_H_
EtherBADGE::DEVICE_TYPE,
@@ -292,10 +266,6 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
NeoVIRED2::DEVICE_TYPE,
#endif
#ifdef __NEOVICONNECT_H_
NeoVIConnect::DEVICE_TYPE,
#endif
#ifdef __NEOVIFIRE_H_
NeoVIFIRE::DEVICE_TYPE,
#endif
@@ -304,14 +274,6 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
NeoVIFIRE2::DEVICE_TYPE,
#endif
#ifdef __NEOVIFIRE3_H_
NeoVIFIRE3::DEVICE_TYPE,
#endif
#ifdef __NEOVIFIRE3FLEXRAY_H_
NeoVIFIRE3FlexRay::DEVICE_TYPE,
#endif
#ifdef __NEOVIION_H_
NeoVIION::DEVICE_TYPE,
#endif
@@ -324,14 +286,6 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
RADA2B::DEVICE_TYPE,
#endif
#ifdef __RADCOMET_H_
RADComet::DEVICE_TYPE,
#endif
#ifdef __RADCOMET3_H_
RADComet3::DEVICE_TYPE,
#endif
#ifdef __RADEPSILON_H_
RADEpsilon::DEVICE_TYPE,
#endif
@@ -348,14 +302,10 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
RADGigastar::DEVICE_TYPE,
#endif
#if defined __RADMOON2_H_ || defined __RADMOON2ZL_H_
#ifdef __RADMOON2_H_
RADMoon2::DEVICE_TYPE,
#endif
#ifdef __RADMOON3_H_
RADMoon3::DEVICE_TYPE,
#endif
#ifdef __RADMOONDUO_H_
RADMoonDuo::DEVICE_TYPE,
#endif
@@ -401,6 +351,7 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
#endif
};
#endif
return supportedDevices;
}
-235
View File
@@ -128,37 +128,6 @@ int64_t IDeviceSettings::GetBaudrateValueForEnum(CANBaudrate enumValue) {
}
}
bool IDeviceSettings::ValidateLINBaudrate(int64_t baudrate) {
switch(baudrate) {
case 4800:
// fallthrough
case 9600:
// fallthrough
case 10400:
// fallthrough
case 10417:
// fallthrough
case 10504:
// fallthrough
case 10593:
// fallthrough
case 10684:
// fallthrough
case 10776:
// fallthrough
case 10870:
// fallthrough
case 10965:
// fallthrough
case 11062:
// fallthrough
case 19200:
return true;
default:
return false;
}
}
bool IDeviceSettings::refresh(bool ignoreChecksum) {
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
@@ -445,15 +414,6 @@ int64_t IDeviceSettings::getBaudrateFor(Network net) const {
}
return baudrate;
}
case Network::Type::LIN: {
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return -1;
}
return cfg->Baudrate;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return -1;
@@ -533,21 +493,6 @@ bool IDeviceSettings::setBaudrateFor(Network net, int64_t baudrate) {
cfg->SetBaudrate = AUTO; // Device will use the baudrate value to set the TQ values
return true;
}
case Network::Type::LIN: {
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
bool valid = ValidateLINBaudrate(baudrate);
if(!valid) {
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
return false;
}
cfg->Baudrate = (uint32_t)baudrate;
return true;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
@@ -759,186 +704,6 @@ bool IDeviceSettings::setTerminationFor(Network net, bool enabled) {
return true;
}
std::optional<bool> IDeviceSettings::isCommanderResistorEnabledFor(Network net) const {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return std::nullopt;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
switch(net.getType()) {
case Network::Type::LIN: {
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
return (cfg->CommanderResistor != RESISTOR_OFF);
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return std::nullopt;
}
}
bool IDeviceSettings::setCommanderResistorFor(Network net, bool resistor_on) {
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false;
}
if(readonly) {
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
return false;
}
switch(net.getType()) {
case Network::Type::LIN: {
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
cfg->CommanderResistor = resistor_on ? RESISTOR_ON : RESISTOR_OFF;
return true;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
}
}
std::optional<LINMode> IDeviceSettings::getLINModeFor(Network net) const {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return std::nullopt;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
switch(net.getType()) {
case Network::Type::LIN: {
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
return static_cast<LINMode>(cfg->Mode);
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return std::nullopt;
}
}
bool IDeviceSettings::setLINModeFor(Network net, LINMode mode) {
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false;
}
if(readonly) {
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
return false;
}
switch(net.getType()) {
case Network::Type::LIN: {
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
cfg->Mode = static_cast<uint8_t>(mode);
return true;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
}
}
std::optional<uint8_t> IDeviceSettings::getLINCommanderResponseTimeFor(Network net) const {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return std::nullopt;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
switch(net.getType()) {
case Network::Type::LIN: {
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
return cfg->numBitsDelay;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return std::nullopt;
}
}
bool IDeviceSettings::setLINCommanderResponseTimeFor(Network net, uint8_t bits) {
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false;
}
if(readonly) {
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
return false;
}
switch(net.getType()) {
case Network::Type::LIN: {
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
cfg->numBitsDelay = bits;
return true;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
}
}
template<typename T> bool IDeviceSettings::applyStructure(const T& newStructure) {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
+4 -15
View File
@@ -5,11 +5,7 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, Disk::MemoryType memType) {
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
uint64_t& cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
if(amount == 0)
return 0;
@@ -61,7 +57,7 @@ std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_e
auto start = std::chrono::high_resolution_clock::now();
auto readAmount = readLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
useAlignedReadBuffer ? alignedReadBuffer.data() : (into + intoOffset), idealBlockSize, timeout, memType);
useAlignedReadBuffer ? alignedReadBuffer.data() : (into + intoOffset), idealBlockSize, timeout);
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
if(!readAmount.has_value() || *readAmount < curAmt) {
@@ -98,19 +94,12 @@ std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_e
return ret;
}
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount, MemoryType memType) {
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount) {
if(pos <= cachePos + cache.size() && pos + amount >= cachePos)
cache.clear();
}
std::optional<uint64_t> ReadDriver::readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter, MemoryType memType) {
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
std::optional<uint64_t> ReadDriver::readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter) {
if(cache.empty())
return std::nullopt; // Nothing in the cache
+4 -4
View File
@@ -5,7 +5,7 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
if(amount == 0)
return 0;
@@ -51,7 +51,7 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
if(useAlignedWriteBuffer) {
auto read = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
alignedWriteBuffer.data(), idealBlockSize, timeout, memType);
alignedWriteBuffer.data(), idealBlockSize, timeout);
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
if(read != idealBlockSize)
@@ -62,7 +62,7 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
start = std::chrono::high_resolution_clock::now();
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout, memType);
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout);
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
if(!bytesTransferred.has_value() || *bytesTransferred < curAmt) {
@@ -83,6 +83,6 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
// No matter how much succeeded, to be safe, we'll invalidate anything
// we may have even tried to write, since it may have succeeded without
// notifying, etc.
readDriver.invalidateCache(pos, amount, memType);
readDriver.invalidateCache(pos, amount);
return ret;
}
+145 -34
View File
@@ -1,5 +1,8 @@
#include "icsneo/disk/extextractordiskreaddriver.h"
#include "icsneo/communication/message/diskdatamessage.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/multichannelcommunication.h"
#include "icsneo/api/lifetime.h"
#include <cstring>
//#define ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
@@ -10,7 +13,7 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
if(amount > getBlockSizeBounds().second)
return std::nullopt;
@@ -25,7 +28,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
unsigned int attempts = 4;
while (attempts-- > 0)
{
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout, memType);
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout);
if (ret.has_value())
break;
}
@@ -33,7 +36,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
}
std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
uint64_t sector = pos / SectorSize;
@@ -43,33 +46,144 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligne
if (largeSectorCount != uint64_t(sectorCount))
return std::nullopt;
std::mutex m;
std::condition_variable cv;
std::mutex mutex;
uint8_t* intoOffset = into;
int64_t remaining = amount;
bool complete = false;
uint16_t receiving = 0; // How much are we about to get before another header or completion
uint64_t received = 0;
uint16_t receivedCurrent = 0;
size_t skipping = 0;
std::vector<uint8_t> header;
std::unique_lock<std::mutex> lk(m);
bool error = !com.redirectRead([&](std::vector<uint8_t>&& data) {
std::unique_lock<std::mutex> lk2(m);
if(error) {
lk2.unlock();
cv.notify_all();
return;
}
const auto handle = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> message) {
if(remaining > 0) {
const auto diskdata = std::static_pointer_cast<DiskDataMessage>(message);
const auto& data = diskdata->data;
std::copy(data.data(), data.data() + data.size(), intoOffset);
intoOffset += data.size();
remaining -= data.size();
if(remaining == 0) {
{
std::scoped_lock<std::mutex> lk(mutex);
complete = true;
if(skipping > data.size()) {
skipping -= data.size();
return;
}
size_t offset = skipping;
skipping = 0;
while(offset < data.size()) {
size_t left = data.size() - offset;
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Going to process " << left << " bytes" << std::endl;
#endif
if(header.size() != HeaderLength) {
if(header.empty() && left && data[offset] != 0xaa) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Incorrect header " << int(data[offset]) << ' ' << int(offset) << std::endl;
#endif
error = true;
lk2.unlock();
cv.notify_all();
return;
}
// Did we get a correct header and at least one byte of data?
const auto begin = data.begin() + offset;
int32_t headerLeft = int32_t(HeaderLength - header.size());
if(int32_t(left) < headerLeft) {
// Not enough data here, grab what header we can and continue
header.insert(header.end(), begin, data.end());
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << int(left) << " bytes of header at " << offset << " (incomplete " <<
header.size() << ')' << std::endl;
#endif
return;
}
header.insert(header.end(), begin, begin + headerLeft);
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << int(headerLeft) << " bytes of header at " << offset << " (complete " <<
header.size() << ')' << std::endl;
#endif
offset += headerLeft;
if(header[1] == uint8_t(Network::NetID::RED)) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got extended response " << int(offset) << std::endl;
#endif
// This is the extended command response, not all devices send this
// If we got it, we need to figure out how much more data to ignore
uint16_t length = (header[2] + (header[3] << 8));
// Try for another header after this, regardless how much we choose
// to skip and how we skip it
header.clear();
if(length <= 6) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Incorrect extended response length " << int(length) << ' ' << int(offset) << std::endl;
#endif
error = true;
lk2.unlock();
cv.notify_all();
return;
}
length -= 7;
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Skipping " << int(length) << ' ' << int(left) << std::endl;
#endif
if(left < length) {
skipping = length - left;
return;
}
offset += length;
continue;
}
// The device tells us how much it's sending us before the next header
receiving = (header[5] | (header[6] << 8));
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Started packet of size " << receiving << " bytes" << std::endl;
#endif
}
left = data.size() - offset;
auto count = uint16_t(std::min<uint64_t>(std::min<uint64_t>(receiving - receivedCurrent, left), amount - received));
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "With " << int(left) << " bytes " << int(offset) << std::endl;
#endif
memcpy(into + received, data.data() + offset, count);
received += count;
receivedCurrent += count;
offset += count;
if(amount == received) {
if(receivedCurrent % 2 == 0)
offset++;
header.clear(); // Now we will need another header
lk2.unlock();
cv.notify_all();
lk2.lock();
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Finished!" << std::endl;
#endif
}
else if(receivedCurrent == receiving) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << count << " bytes, " << receivedCurrent << " byte packet " << received <<
" complete of " << amount << std::endl;
#endif
if(receivedCurrent % 2 == 0)
offset++;
header.clear(); // Now we will need another header
receivedCurrent = 0;
} else {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << count << " bytes, incomplete (of " << receiving << " bytes)" << std::endl;
#endif
}
}
}, std::make_shared<MessageFilter>(Network::NetID::DiskData)));
});
Lifetime clearRedirect([&com, &lk] { lk.unlock(); com.clearRedirectRead(); });
if(!com.sendCommand(ExtendedCommand::Extract, {
if(error)
return std::nullopt;
error = !com.sendCommand(ExtendedCommand::Extract, {
uint8_t(sector & 0xff),
uint8_t((sector >> 8) & 0xff),
uint8_t((sector >> 16) & 0xff),
@@ -82,16 +196,13 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligne
uint8_t((sectorCount >> 8) & 0xff),
uint8_t((sectorCount >> 16) & 0xff),
uint8_t((sectorCount >> 24) & 0xff),
})) {
return std::nullopt;
}
std::unique_lock<std::mutex> lk(mutex);
const auto successful = cv.wait_for(lk, timeout, [&](){ return complete; });
com.removeMessageCallback(handle);
if(!successful)
});
if(error)
return std::nullopt;
return amount - remaining;
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
if(hitTimeout || error)
return std::nullopt;
return amount;
}
+20 -39
View File
@@ -1,16 +1,13 @@
#include "icsneo/disk/neomemorydiskdriver.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/message/flashmemorymessage.h"
#include <cstring>
#include <iostream>
using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
const auto filter = std::make_shared<MessageFilter>((memType == MemoryType::SD ? Network::NetID::NeoMemorySDRead : Network::NetID::RED_INT_MEMORYREAD));
filter->includeInternalInAny = true;
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
if(pos % SectorSize != 0)
return std::nullopt;
@@ -19,47 +16,35 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
return std::nullopt;
const uint64_t currentSector = pos / SectorSize;
const uint8_t memLocation = (uint8_t)memType;
uint64_t numWords = amount / 2;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, &numWords] {
auto msg = com.waitForMessageSync([&currentSector, &com] {
return com.sendCommand(Command::NeoReadMemory, {
memLocation,
MemoryTypeSD,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
uint8_t((currentSector >> 24) & 0xFF),
uint8_t(numWords & 0xFF),
uint8_t((numWords >> 8) & 0xFF),
uint8_t((numWords >> 16) & 0xFF),
uint8_t((numWords >> 24) & 0xFF)
uint8_t(SectorSize & 0xFF),
uint8_t((SectorSize >> 8) & 0xFF),
uint8_t((SectorSize >> 16) & 0xFF),
uint8_t((SectorSize >> 24) & 0xFF)
});
}, filter, timeout);
}, NeoMemorySDRead, timeout);
if(!msg)
return 0;
if(memType == MemoryType::SD) {
const auto mem = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!mem || mem->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
}
memcpy(into, mem->data.data(), SectorSize);
} else { // flash
const auto mem = std::dynamic_pointer_cast<FlashMemoryMessage>(msg);
if(!mem || mem->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
}
memcpy(into, mem->data.data(), SectorSize);
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!sdmsg || sdmsg->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
}
memcpy(into, sdmsg->data.data(), SectorSize);
return SectorSize;
}
std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
@@ -70,19 +55,15 @@ std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communicati
return std::nullopt;
const uint64_t currentSector = pos / SectorSize;
const uint8_t memLocation = (uint8_t)memType;
uint64_t numWords = amount / 2;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, from, amount, &numWords] {
auto msg = com.waitForMessageSync([&currentSector, &com, from, amount] {
std::vector<uint8_t> command = {
memLocation,
MemoryTypeSD,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
uint8_t((currentSector >> 24) & 0xFF),
uint8_t(numWords & 0xFF),
uint8_t((numWords >> 8) & 0xFF),
uint8_t(SectorSize & 0xFF),
uint8_t((SectorSize >> 8) & 0xFF),
};
command.insert(command.end(), from, from + amount);
return com.sendCommand(Command::NeoWriteMemory, command);
+4 -4
View File
@@ -4,25 +4,25 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
std::optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
std::optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
std::optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
+1 -1
View File
@@ -7,7 +7,7 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
if(amount > getBlockSizeBounds().second)
-43
View File
@@ -1,43 +0,0 @@
#include "icsneo/disk/vsa/vsa.h"
#include "icsneo/communication/packet/ethernetpacket.h"
#include <iostream>
using namespace icsneo;
// VSA Base Class Functions
// VSAMessage Class Functions
std::shared_ptr<Packet> VSAMessage::getPacket() const
{
auto packet = std::make_shared<Packet>();
packet->network = network;
reservePacketData(packet);
packet->data.insert(packet->data.end(), payload.begin(), payload.end());
return packet;
}
// VSAExtendedMessage Class Functions
void VSAExtendedMessage::appendPacket(std::shared_ptr<Packet> packet) const
{
packet->data.insert(packet->data.end(), payload.begin(), payload.end());
// Set the network if not already set (Happens in AA0F records)
if(packet->network.getNetID() == Network::NetID::Invalid) {
packet->network = network;
}
}
void VSAExtendedMessage::truncatePacket(std::shared_ptr<Packet> packet)
{
static constexpr auto EthernetLengthOffset = 26u;
switch(packet->network.getType()) {
case Network::Type::Ethernet:
{
const auto& packetLength = *reinterpret_cast<uint16_t*>(packet->data.data() + EthernetLengthOffset);
const size_t ethernetFrameSize = packetLength - (sizeof(uint16_t) * 2);
const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + ethernetFrameSize;
packet->data.resize(bytestreamExpectedSize);
}
break;
}
}
-26
View File
@@ -1,26 +0,0 @@
#include "icsneo/disk/vsa/vsa02.h"
using namespace icsneo;
VSA02::VSA02(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA02);
constantIndex = *reinterpret_cast<uint16_t*>(recordBytes + 2);
flags = *reinterpret_cast<Flags*>(recordBytes + 4);
pieceCount = recordBytes[5];
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
samples.insert(samples.end(), recordBytes + 14, recordBytes + 30);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA02::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-24
View File
@@ -1,24 +0,0 @@
#include "icsneo/disk/vsa/vsa03.h"
using namespace icsneo;
VSA03::VSA03(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA03);
eventType = static_cast<EventType>(*reinterpret_cast<uint16_t*>(recordBytes + 2));
eventData = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
doChecksum(recordBytes);
}
void VSA03::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-24
View File
@@ -1,24 +0,0 @@
#include "icsneo/disk/vsa/vsa04.h"
using namespace icsneo;
VSA04::VSA04(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA04);
flags = *reinterpret_cast<Flags*>(recordBytes + 2);
partitionIndex = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
doChecksum(recordBytes);
}
void VSA04::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-23
View File
@@ -1,23 +0,0 @@
#include "icsneo/disk/vsa/vsa05.h"
using namespace icsneo;
VSA05::VSA05(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA05);
errorType = static_cast<ErrorType>(*reinterpret_cast<uint16_t*>(recordBytes + 2));
errorNetwork = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
}
void VSA05::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-25
View File
@@ -1,25 +0,0 @@
#include "icsneo/disk/vsa/vsa06.h"
using namespace icsneo;
VSA06::VSA06(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA06);
savedSectors.insert(savedSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 2), reinterpret_cast<uint32_t*>(recordBytes + 18));
error = *reinterpret_cast<uint16_t*>(recordBytes + 18);
savedSectorsHigh = *reinterpret_cast<uint16_t*>(recordBytes + 20);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA06::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-25
View File
@@ -1,25 +0,0 @@
#include "icsneo/disk/vsa/vsa07.h"
using namespace icsneo;
VSA07::VSA07(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA07);
lastSector = *reinterpret_cast<uint32_t*>(recordBytes + 2);
currentSector = *reinterpret_cast<uint32_t*>(recordBytes + 6);
reserved.insert(reserved.end(), recordBytes + 10, recordBytes + 22);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA07::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-24
View File
@@ -1,24 +0,0 @@
#include "icsneo/disk/vsa/vsa08.h"
using namespace icsneo;
VSA08::VSA08(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA08);
troubleSramCount.insert(troubleSramCount.end(), recordBytes + 2, recordBytes + 6);
troubleSectors.insert(troubleSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 6), reinterpret_cast<uint32_t*>(recordBytes + 20));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA08::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-32
View File
@@ -1,32 +0,0 @@
#include "icsneo/disk/vsa/vsa09.h"
using namespace icsneo;
VSA09::VSA09(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA09);
serialNumber = *reinterpret_cast<uint32_t*>(recordBytes + 2);
firmwareMajorVersion = recordBytes[6];
firmwareMinorVersion = recordBytes[7];
manufactureMajorRevision = recordBytes[8];
manufactureMinorRevision = recordBytes[9];
bootloaderMajorVersion = recordBytes[10];
bootloaderMinorVersion = recordBytes[11];
reserved0.insert(reserved0.end(), recordBytes + 12, recordBytes + 18);
hardwareID = static_cast<HardwareID>(recordBytes[18]);
reserved1.insert(reserved1.end(), recordBytes + 19, recordBytes + 22);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA09::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-38
View File
@@ -1,38 +0,0 @@
#include "icsneo/disk/vsa/vsa0b.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto PayloadOffset = 4;
VSA0B::VSA0B(uint8_t* const recordBytes)
: VSAMessage(recordBytes + PayloadOffset, CoreMiniPayloadSize, static_cast<Network::CoreMini>(recordBytes[29]))
{
setType(VSA::Type::AA0B);
captureBitfield = reinterpret_cast<uint16_t*>(recordBytes)[1];
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
reserved = recordBytes[28];
checksum = reinterpret_cast<uint16_t*>(recordBytes)[15];
doChecksum(recordBytes);
}
void VSA0B::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
bool VSA0B::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
-27
View File
@@ -1,27 +0,0 @@
#include "icsneo/disk/vsa/vsa0c.h"
using namespace icsneo;
VSA0C::VSA0C(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA0C);
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 2);
audioPreamble = recordBytes[4];
audioHeader = recordBytes[5];
pcmData.insert(pcmData.end(), recordBytes + 6, recordBytes + 20);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
vNetBitfield = *reinterpret_cast<VSA0C::VNet*>(recordBytes + 28);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA0C::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
-107
View File
@@ -1,107 +0,0 @@
#include "icsneo/disk/vsa/vsa0d.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 8;
static constexpr auto FirstPayloadSize = 12;
static constexpr auto ConsecutivePayloadOffset = 4;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0D::VSA0D(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0D);
setIndex(*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0x01FFu);
setSequenceNum((*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0xFE00u) >> 9);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data());
for(size_t i = 0; i < payload.size() / DWordSize; i++) {
runningChecksum += dwords[i];
}
}
// First Record Functions
VSA0DFirst::VSA0DFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0D(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum, static_cast<Network::CoreMini>(recordBytes[29]))
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
setRecordCount(*reinterpret_cast<uint16_t*>(recordBytes + 6));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
vNetInfo = *reinterpret_cast<VNet*>(recordBytes + 28);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
uint32_t* const timestampDWords = reinterpret_cast<uint32_t*>(timestamp);
runningChecksum += timestampDWords[0];
runningChecksum += timestampDWords[1];
}
void VSA0DFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0DFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
void VSA0DFirst::reorderPayload(std::vector<uint8_t>& secondPayload)
{
std::vector<uint8_t> tempPayload;
tempPayload.insert(tempPayload.end(), secondPayload.begin(), secondPayload.begin() + 4);
uint8_t* timestampBytes = reinterpret_cast<uint8_t*>(&timestamp);
if(timestampIsExtended) {
timestampBytes[7] += 0x80;
}
tempPayload.insert(tempPayload.end(), timestampBytes, timestampBytes + 8);
tempPayload.insert(tempPayload.end(), secondPayload.begin() + 4, secondPayload.end());
payload.clear();
secondPayload.clear();
payload.insert(payload.end(), tempPayload.begin(), tempPayload.begin() + 12); // This is done because the capacity of payload is already 12
secondPayload.insert(secondPayload.end(), tempPayload.begin() + 12, tempPayload.end());
}
bool VSA0DFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
// Consecutive Record Functions
VSA0DConsecutive::VSA0DConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0DFirst> first, bool isLastRecord)
: VSA0D(recordBytes, recordBytes + ConsecutivePayloadOffset, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(getIndex() == 1) {
first->reorderPayload(payload);
} else if(isLastRecord) {
recordChecksum = *reinterpret_cast<uint32_t*>(recordBytes + 28);
doChecksum(recordBytes);
} else {
setChecksumFailed(first->getChecksumFailed());
}
setRecordCount(first->getRecordCount());
}
void VSA0DConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != recordChecksum);
}
-114
View File
@@ -1,114 +0,0 @@
#include "icsneo/disk/vsa/vsa0e.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 10;
static constexpr auto FirstPayloadSize = 10;
static constexpr auto ConsecutivePayloadOffset = 4;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0E::VSA0E(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0E);
setIndex(static_cast<uint16_t>(recordBytes[2]));
setSequenceNum(static_cast<uint16_t>(recordBytes[3]));
if(getIndex() == 0) {
runningChecksum = (static_cast<uint32_t>(payload[0]) << 16) | (static_cast<uint32_t>(payload[1]) << 24);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data() + 2);
for(size_t i = 0; i < (payload.size() - 2) / DWordSize; i++) {
runningChecksum += dwords[i];
}
} else {
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data());
for(size_t i = 0; i < payload.size() / DWordSize; i++) {
runningChecksum += dwords[i];
}
}
}
// First Record Functions
VSA0EFirst::VSA0EFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0E(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum,
static_cast<Network::CoreMini>(*reinterpret_cast<uint16_t*>(recordBytes + 28)))
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
setRecordCount(*reinterpret_cast<uint32_t*>(recordBytes + 6));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
timestampIsExtended = (bool)(*reinterpret_cast<uint64_t*>(recordBytes + 20) & (0x8000000000000000));
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA0EFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0EFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
bool VSA0EFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
void VSA0EFirst::reorderPayload(std::vector<uint8_t>& secondPayload)
{
std::vector<uint8_t> tempPayload;
tempPayload.insert(tempPayload.end(), payload.begin(), payload.end());
tempPayload.insert(tempPayload.end(), secondPayload.begin(), secondPayload.begin() + 6);
uint8_t* timestampBytes = reinterpret_cast<uint8_t*>(&timestamp);
if(timestampIsExtended) {
timestampBytes[7] += 0x80;
}
tempPayload.insert(tempPayload.end(), timestampBytes, timestampBytes + 8);
tempPayload.insert(tempPayload.end(), secondPayload.begin() + 6, secondPayload.end());
payload.clear();
secondPayload.clear();
payload.insert(payload.end(), tempPayload.begin(), tempPayload.begin() + 10); // This is done because the capacity of payload is already 10
secondPayload.insert(secondPayload.end(), tempPayload.begin() + 10, tempPayload.end());
}
// Consecutive Record Functions
VSA0EConsecutive::VSA0EConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0EFirst> first, bool isLastRecord)
: VSA0E(recordBytes, recordBytes + ConsecutivePayloadOffset, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(getIndex() == 1) {
first->reorderPayload(payload);
} else if(isLastRecord) {
recordChecksum = *reinterpret_cast<uint32_t*>(recordBytes + 28);
doChecksum(recordBytes);
} else {
setChecksumFailed(first->getChecksumFailed());
}
setRecordCount(first->getRecordCount());
}
void VSA0EConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != recordChecksum);
}
-106
View File
@@ -1,106 +0,0 @@
#include "icsneo/disk/vsa/vsa0f.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 18;
static constexpr auto FirstPayloadSize = 14;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0F::VSA0F(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0F);
setIndex(*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0x01FFu);
setSequenceNum((*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0xFE00u) >> 9);
if(getIndex() == 0) {
runningChecksum = (static_cast<uint32_t>(payload[0]) << 16) | (static_cast<uint32_t>(payload[1]) << 24);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data() + 2);
for (size_t i = 0; i < (payload.size() - 2) / DWordSize; i++) {
runningChecksum += dwords[i];
}
} else {
uint32_t* dwords = reinterpret_cast<uint32_t*>(recordBytes);
for (size_t i = 0; i < 8; i++) {
runningChecksum += dwords[i];
}
}
}
// First Record Functions
VSA0FFirst::VSA0FFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0F(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum)
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
uint16_t byteCount = *reinterpret_cast<uint16_t*>(recordBytes + 6);
uint16_t recordCount;
if(byteCount <= FirstPayloadSize) {
recordCount = 1;
} else if(byteCount <= FirstPayloadSize + LastPayloadSize) {
recordCount = 2;
} else {
byteCount -= FirstPayloadSize + LastPayloadSize;
recordCount = 2 + byteCount / OtherPayloadSize;
if (byteCount % OtherPayloadSize != 0) {
recordCount += 1;
}
}
setRecordCount(recordCount);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 8) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 16);
doChecksum(recordBytes);
// Network ID is not found in first record for AA0F
// Only the subsequent records have the Network ID in the payload
}
void VSA0FFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for (size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0FFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
bool VSA0FFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if(filter->captureBitfield != captureBitfield ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
// Consecutive Record Functions
VSA0FConsecutive::VSA0FConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0FFirst> first, bool isLastRecord)
: VSA0F(recordBytes, recordBytes + 4, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(isLastRecord) {
doChecksum(recordBytes);
} else {
network = Network(static_cast<Network::CoreMini>(*reinterpret_cast<uint16_t*>(recordBytes + 28))); // Network ID is stored in 25th and 26th recordBytes of payload
}
setRecordCount(first->getRecordCount());
}
void VSA0FConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != 0);
}
-37
View File
@@ -1,37 +0,0 @@
#include "icsneo/disk/vsa/vsa6a.h"
#include "icsneo/disk/diskdriver.h"
using namespace icsneo;
static constexpr auto PayloadOffset = 56;
static constexpr auto PayloadSize = 452;
static constexpr auto TimestampOffset = 48;
static constexpr auto TimestampSize = 8;
VSA6A::VSA6A(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA6A);
sequenceNum = *reinterpret_cast<uint32_t*>(recordBytes + 34);
totalSectors = *reinterpret_cast<uint32_t*>(recordBytes + 38);
reserved = *reinterpret_cast<uint32_t*>(recordBytes + 42);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 46) & UINT63_MAX;
timestampSum = *reinterpret_cast<uint16_t*>(recordBytes + 54);
data.insert(data.end(), recordBytes + 56, recordBytes + 508);
checksum = *reinterpret_cast<uint32_t*>(recordBytes + 508);
doChecksum(recordBytes);
}
void VSA6A::doChecksum(uint8_t* recordBytes)
{
uint32_t sum = 0;
for(size_t i = PayloadOffset; i < PayloadOffset+ PayloadSize; i++) {
sum += recordBytes[i];
}
uint16_t tSum = 0;
for(size_t i = TimestampOffset; i < TimestampOffset + TimestampSize; i++) {
tSum += recordBytes[i];
}
setChecksumFailed(sum != checksum || tSum != timestampSum);
}
-460
View File
@@ -1,460 +0,0 @@
#include "icsneo/disk/vsa/vsaparser.h"
#include "icsneo/disk/vsa/vsa02.h"
#include "icsneo/disk/vsa/vsa03.h"
#include "icsneo/disk/vsa/vsa04.h"
#include "icsneo/disk/vsa/vsa05.h"
#include "icsneo/disk/vsa/vsa06.h"
#include "icsneo/disk/vsa/vsa07.h"
#include "icsneo/disk/vsa/vsa08.h"
#include "icsneo/disk/vsa/vsa09.h"
#include "icsneo/disk/vsa/vsa0b.h"
#include "icsneo/disk/vsa/vsa0c.h"
#include "icsneo/disk/vsa/vsa0d.h"
#include "icsneo/disk/vsa/vsa0e.h"
#include "icsneo/disk/vsa/vsa0f.h"
#include "icsneo/disk/vsa/vsa6a.h"
#include "icsneo/disk/diskdriver.h"
#include <iostream>
using namespace icsneo;
bool VSAParser::parseBytes(uint8_t* const bytes, uint64_t arrLen)
{
uint64_t bytesOffset = 0;
while(bytesOffset + VSA::StandardRecordSize <= arrLen) { // Enough bytes to read for Standard Record
if(bytes[bytesOffset] != 0xAAu) {
// Invalid Input
return false;
}
switch(bytes[bytesOffset + 1]) {
case 0x00u: // Pad Record
bytesOffset += VSA::StandardRecordSize;
break;
case 0x01u: // Message Data (Deprecated)
hasDeprecatedRecords = true;
bytesOffset += VSA::StandardRecordSize;
break;
case 0x02u: // Logdata Record
if(settings.extractAA02) {
vsaRecords.push_back(std::make_shared<VSA02>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x03u: // Event Record
if(settings.extractAA03) {
vsaRecords.push_back(std::make_shared<VSA03>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x04u: // Partition Info Record
if(settings.extractAA04) {
vsaRecords.push_back(std::make_shared<VSA04>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x05u: // Application Error Record
if(settings.extractAA05) {
vsaRecords.push_back(std::make_shared<VSA05>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x06u: // Debug/Internal
if(settings.extractAA06) {
vsaRecords.push_back(std::make_shared<VSA06>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x07u: // Debug/Internal
if(settings.extractAA07) {
vsaRecords.push_back(std::make_shared<VSA07>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x08u: // Buffer Info Record
if(settings.extractAA08) {
vsaRecords.push_back(std::make_shared<VSA08>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x09u: // Device Info Record
if(settings.extractAA09) {
vsaRecords.push_back(std::make_shared<VSA09>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Au: // Logger Info Configuration (Deprecated)
hasDeprecatedRecords = true;
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Bu: // Message Data
if(settings.extractAA0B) {
auto record = std::make_shared<VSA0B>(bytes + bytesOffset);
vsaRecords.push_back(record);
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Cu: // PCM Audio Data
if(settings.extractAA0C) {
vsaRecords.push_back(std::make_shared<VSA0C>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Du: // Message Data (Extended)
if(settings.extractAA0D) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0D)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Eu: // Message Data (Extended)
if(settings.extractAA0E) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0E)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Fu: // Message Data (Extended)
if(settings.extractAA0F) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0F)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x6Au: // Logger Configuration Backup
if(bytesOffset + Disk::SectorSize <= arrLen) {
if(settings.extractAA6A) {
vsaRecords.push_back(std::make_shared<VSA6A>(bytes + bytesOffset));
}
}
bytesOffset += Disk::SectorSize;
break;
default:
// Unhandled VSA Record Type
return false;
break;
}
}
return true;
}
bool VSAParser::handleExtendedRecord(uint8_t* const bytes, uint64_t& bytesOffset, VSA::Type type)
{
// Gather info about the extended record sequence of the record contained in bytes
std::shared_ptr<VSAExtendedMessage> first;
uint16_t seqNum;
ExtendedMessageState::ExtendedRecordSeqInfo* seqInfo;
uint32_t runningChecksum = 0;
switch(type) {
case VSA::Type::AA0D:
first = std::make_shared<VSA0DFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0DSeqInfo[seqNum];
break;
case VSA::Type::AA0E:
first = std::make_shared<VSA0EFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0ESeqInfo[seqNum];
break;
case VSA::Type::AA0F:
first = std::make_shared<VSA0FFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0FSeqInfo[seqNum];
break;
default:
return false; // Invalid type was passed
}
if(seqInfo->nextIndex == 0 && seqInfo->records.size() == 0) { // This is the first record in the sequence
if(first->getIndex() != 0) {
seqInfo->clear();
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
return true; // This is not actually the first record
}
seqInfo->records.push_back(first);
seqInfo->totalRecordCount = first->getRecordCount();
seqInfo->nextIndex++;
seqInfo->runningChecksum = runningChecksum;
} else if(seqInfo->nextIndex < seqInfo->totalRecordCount && seqInfo->records.size() > 0) { // Consecutive Record
std::shared_ptr<VSAExtendedMessage> consecutive;
bool isLast = seqInfo->nextIndex == seqInfo->totalRecordCount - 1;
// Construct the consecutive record from bytes
switch(type) {
case VSA::Type::AA0D:
consecutive = std::make_shared<VSA0DConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0DFirst>(seqInfo->records[0]),
isLast
);
break;
case VSA::Type::AA0E:
consecutive = std::make_shared<VSA0EConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0EFirst>(seqInfo->records[0]),
isLast
);
break;
case VSA::Type::AA0F:
consecutive = std::make_shared<VSA0FConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0FFirst>(seqInfo->records[0]),
isLast
);
break;
default:
return false;
}
if(consecutive->getIndex() == seqInfo->nextIndex && consecutive->getSequenceNum() == seqNum) { // This record is valid in the sequence
seqInfo->records.push_back(consecutive);
seqInfo->nextIndex++;
} else { // Sequence is out of order/invalid
// Throw away incomplete sequence and report warning
seqInfo->clear();
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
// Save data for new sequence
if(first->getIndex() == 0) {
seqInfo->records.push_back(first);
seqInfo->totalRecordCount = first->getRecordCount();
seqInfo->nextIndex++;
seqInfo->runningChecksum = runningChecksum;
}
return true;
}
if(seqInfo->nextIndex == seqInfo->totalRecordCount) { // This is the last record in the sequence
if(consecutive->getChecksumFailed()) {
// Fail out if checksum fails
seqInfo->clear();
return false;
}
vsaRecords.insert(vsaRecords.end(), seqInfo->records.begin(), seqInfo->records.end());
seqInfo->clear();
}
} else {
return false; // Undefined behavior
}
return true;
}
VSAParser::RecordParseStatus VSAParser::getRecordFromBytes(uint8_t* const bytes, size_t arrLen, std::shared_ptr<VSA>& record)
{
record = nullptr;
if(arrLen < VSA::StandardRecordSize) {
// Not enough bytes
return VSAParser::RecordParseStatus::InsufficientData;
} else if(bytes[0] != 0xAAu) {
return VSAParser::RecordParseStatus::NotARecordStart;
} else {
switch(bytes[1]) {
case 0x00u: // Pad Record
return VSAParser::RecordParseStatus::Pad;
case 0x01u: // Message Data (Deprecated)
return VSAParser::RecordParseStatus::Deprecated;
case 0x02u: // Logdata Record
if(settings.extractAA02) {
record = std::make_shared<VSA02>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x03u: // Event Record
if(settings.extractAA03) {
record = std::make_shared<VSA03>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x04u: // Partition Info Record
if(settings.extractAA04) {
record = std::make_shared<VSA04>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x05u: // Application Error Record
if(settings.extractAA05) {
record = std::make_shared<VSA05>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x06u: // Debug/Internal
if(settings.extractAA06) {
record = std::make_shared<VSA06>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x07u: // Debug/Internal
if(settings.extractAA07) {
record = std::make_shared<VSA07>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x08u: // Buffer Info Record
if(settings.extractAA08) {
record = std::make_shared<VSA08>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x09u: // Device Info Record
if(settings.extractAA09) {
record = std::make_shared<VSA09>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Au:
return VSAParser::RecordParseStatus::Deprecated;
case 0x0Bu: // Message Data
if(settings.extractAA0B) {
record = std::make_shared<VSA0B>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Cu: // PCM Audio Data
if(settings.extractAA0C) {
record = std::make_shared<VSA0C>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Du: // Message Data (Extended)
if(settings.extractAA0D) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0DFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x0Eu: // Message Data (Extended)
if(settings.extractAA0E) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0EFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x0Fu: // Message Data (Extended)
if(settings.extractAA0F) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0FFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x6Au: // Logger Configuration Backup
if(settings.extractAA6A) {
if(arrLen < Disk::SectorSize) {
return VSAParser::RecordParseStatus::InsufficientData;
}
record = std::make_shared<VSA6A>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
default:
// Unhandled VSA Record Type
return VSAParser::RecordParseStatus::UnknownRecordType;
break;
}
}
return VSAParser::RecordParseStatus::FilteredOut;
}
void VSAParser::clearParseState()
{
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
state.vsa0DSeqInfo[i].clear();
}
for(size_t i = 0; i < state.vsa0ESeqInfo.size(); i++) {
state.vsa0ESeqInfo[i].clear();
}
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
state.vsa0ESeqInfo[i].clear();
}
}
bool VSAParser::extractMessagePackets(std::vector<std::shared_ptr<Packet>>& packets)
{
if(settings != Settings::messageRecords()) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false; // We do not have exclusively message records
}
std::shared_ptr<Packet> packet;
bool activeExtendedMessage = false;
VSA::Type previousRecordType = VSA::Type::Invalid;
for(const auto& record : vsaRecords) {
VSA::Type activeRecordType = record->getType();
switch(activeRecordType) {
// Handle standard message records
case VSA::Type::AA0B: {
if(activeExtendedMessage) {
// Non-terminated extended message record
// There was a failure/unexpected behavior in the parsing process
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::Error);
return false;
}
std::shared_ptr<VSAMessage> messageRecord = std::dynamic_pointer_cast<VSAMessage>(record);
if(!settings.messageFilter || messageRecord->filter(settings.messageFilter)) {
packet = messageRecord->getPacket();
packets.push_back(packet);
}
packet = nullptr;
break;
}
// Handle extended message records
case VSA::Type::AA0D:
case VSA::Type::AA0E:
case VSA::Type::AA0F: {
std::shared_ptr<VSAExtendedMessage> extendedMessageRecord = std::dynamic_pointer_cast<VSAExtendedMessage>(record);
if(!activeExtendedMessage) { // Start new extended message packet
packet = extendedMessageRecord->getPacket();
activeExtendedMessage = true;
previousRecordType = extendedMessageRecord->getType();
} else if(previousRecordType == activeRecordType) { // Continue existing extended message packet
extendedMessageRecord->appendPacket(packet);
if(extendedMessageRecord->getRecordCount() == static_cast<uint32_t>(extendedMessageRecord->getIndex() + 1)) { // Last record in sequence
if(!settings.messageFilter || extendedMessageRecord->filter(settings.messageFilter)) {
VSAExtendedMessage::truncatePacket(packet);
packets.push_back(packet);
}
activeExtendedMessage = false;
packet = nullptr;
previousRecordType = activeRecordType;
}
} else {
// Non-terminated extended message record
// There was a failure/unexpected behavior in the parsing process
activeExtendedMessage = false;
packet = nullptr;
previousRecordType = VSA::Type::Invalid;
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
return false;
}
break;
}
default:
// Non-message record discovered
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
return false;
}
}
vsaRecords.clear();
return true;
}
+13 -18
View File
@@ -101,23 +101,18 @@ The write blocking status of the device determines the behavior of attempting to
If write blocking is enabled, then the transmitting thread will wait for the entire buffer to be transmitted.
If write blocking is disabled, then the attempt to transmit will simply fail and an error will be logged on the calling thread.
A2B message channel indexing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The libicsneo API allows users to input and output WAV file via receiving or transmitting A2B messages. The library provides several ways to import and export
WAV files via A2B traffic. While using the API, a user will encounter message channels being referenced as unsigned integers for indexing. A ``icsneo::A2BMessage``
object will contain both upstream and downstream channels. This implies that the number of channels that a A2B message has is twice the TDM mode. The ordering for these
channels are interleved. Therefore, a message channel index ``0`` would represent downstream channel ``0`` in the network, message channel index ``1`` would represent
upstream channel ``0`` in the network, and so on. More generally, a message channel index can be computed with the formula ``2 * CHANNEL + IS_UPSTREAM`` where channel
is the channel referred in the A2B network and ``IS_UPSTREAM`` is ``1`` when a channel is upstream and ``0`` if downstream.
A2B Wave Output
~~~~~~~~~~~~~~~~~~~~
Users may add a ``icsneo::A2BWAVOutput`` message callback to their device in order to write A2B PCM data to a WAVE file. The message callback listens for ``icsneo::A2BMessage``
messages and writes both downstream and upstream channels to a single wave file. If downstream and upstream each have ``32`` channels, the wave file will contain ``2*32 = 64``
total channels. The first half of the channels, channels ``0-31`` in the outputted wave file, represent downstream channel ``0-31``. Likewise, the second half of the channels,
channels ``32-63`` in the outputted wave file, represent upstream channel ``0-31``. Let ``NUM_CHANNELS`` be the total number of channels in a single stream. If we introduce a
variable ``IS_UPSTREAM`` which is ``0`` when downstream and ``1`` when upstream and desired a channel ``CHANNEL_NUM`` in either downstream or upstream the
channel ``IS_UPSTREAM * NUM_CHANNELS + CHANNEL_NUM`` would correspond to the channel in the outputted wave file.
One area where these message channel indexes are used are when specifying a ``icsneo::ChannelMap`` for WAV transmit or receive. When transmitting a WAV file, the
map will map message channels to the input WAV file. For receiving a WAV file, the map will map the output WAV channels to channels from received messages.
Wave files may be split by channel using programs such as ``FFmpeg``. Consider a file ``out.wav`` which was generated using a ``icsneo::A2BWAVOutput`` object
and contains ``32`` channels per stream. The ``icsneo::A2BWavoutput`` object injested PCM data with a sample rate of ``44.1 kHz`` and bit depth of ``24``. The corresponding
channel of upstream channel ``8`` in ``out.wav`` would be ``1*32 + 8 = 40``. The following ``FFmpeg`` command may be ran in a linux environment to create a new wave
file ``out_upstream_ch8.wav`` which contains only PCM samples off of upstream channel ``8``.
EX: If we want to transmit, we will need to construct a mapping from message channels to the input WAV file. So, if we have a monotone WAV file that we are inputting
through the API, then we can map channel ``2`` upstream to channel 0 in the input WAV (the only channel in the WAV file) with the following:
``icsneo::ChannelMap chMap``
``chMap[5] = 0``
Since we are transmitting, we must map our desired A2B message channels to the input WAV file. We have ``0`` representing the single channel in the WAV file and
``5`` representing channel ``2`` upstream in the A2B message from using the formula above ``2 * CHANNEL + IS_UPSTREAM = 2 * 2 + 1 = 5``.
``ffmpeg -i out.wav -ar 44100 -acodec pcm_s24le -map_channel 0.0.40 out_upstream_ch8.wav``
+1 -1
View File
@@ -64,7 +64,7 @@ master_doc = 'index'
# General information about the project.
project = 'libicsneo'
copyright = '2018-2024, Intrepid Control Systems, Inc.'
copyright = '2018-2020, Intrepid Control Systems, Inc.'
author = 'Intrepid Control Systems, Inc.'
# The version info for the project you're documenting, acts as replacement for
+1 -1
View File
@@ -64,7 +64,7 @@ master_doc = 'index'
# General information about the project.
project = 'libicsneo'
copyright = '2018-2024, Intrepid Control Systems, Inc.'
copyright = '2018-2020, Intrepid Control Systems, Inc.'
author = 'Intrepid Control Systems, Inc.'
# The version info for the project you're documenting, acts as replacement for
+1 -40
View File
@@ -1,15 +1,8 @@
option(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE "Build the command-line interactive 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_CPP_SIMPLE_EXAMPLE "Build the simple 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_LIN_EXAMPLE "Build the LIN example." ON)
option(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE "Build the Live Data example." ON)
option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON)
option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON)
option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON)
option(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE "Build the app error example." ON)
# Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
@@ -19,14 +12,6 @@ if(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE)
add_subdirectory(c/interactive)
endif()
if(LIBICSNEO_BUILD_C_SIMPLE_EXAMPLE)
add_subdirectory(c/simple)
endif()
if(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE)
add_subdirectory(c/legacy)
endif()
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
add_subdirectory(cpp/simple)
endif()
@@ -39,30 +24,6 @@ if(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE)
add_subdirectory(cpp/a2b)
endif()
if(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE)
add_subdirectory(cpp/lin)
endif()
if(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE)
add_subdirectory(cpp/livedata)
endif()
if(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE)
add_subdirectory(cpp/coremini)
endif()
if(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE)
add_subdirectory(cpp/mdio)
endif()
if(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE)
add_subdirectory(cpp/vsa)
endif()
if(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE)
add_subdirectory(cpp/apperror)
endif()
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp)
# endif()
+13 -2
View File
@@ -1,5 +1,16 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneoc-interactive-example VERSION 0.2.0)
include(GNUInstallDirs)
# Include libicsneo's include directory
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
if(UNIX)
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
endif()
add_executable(libicsneoc-interactive-example src/main.c)
if(UNIX)
target_link_libraries(libicsneoc-interactive-example ${CMAKE_DL_LIBS})
endif()
target_link_libraries(libicsneoc-interactive-example icsneoc)
endif()
+18 -45
View File
@@ -1,5 +1,5 @@
// Signal to dynamically load the library
//#define ICSNEOC_DYNAMICLOAD
#define ICSNEOC_DYNAMICLOAD
#include <stdio.h>
#include <stdlib.h>
@@ -166,9 +166,7 @@ char getCharInput(int numArgs, ...) {
va_end(vaList);
while(!found) {
if(fgets(input, 99, stdin) == NULL) {
break;
}
fgets(input, 99, stdin);
if(strlen(input) == 2) {
for(int i = 0; i < numArgs; ++i) {
if(input[0] == *(list + i)) {
@@ -221,21 +219,21 @@ int main() {
// Attempt to initialize the library and access its functions
// This call searches for icsneoc.dll according to the standard dynamic-link library search order
//int ret = icsneo_init();
//if(ret == 1) {
// printf("The library was already initialized!\n");
// return ret;
//}
//
//if(ret == 2) {
// printf("The library could not be found!\n");
// return ret;
//}
//
//if(ret == 3) {
// printf("The library is missing functions!\n");
// return ret;
//}
int ret = icsneo_init();
if(ret == 1) {
printf("The library was already initialized!\n");
return ret;
}
if(ret == 2) {
printf("The library could not be found!\n");
return ret;
}
if(ret == 3) {
printf("The library is missing functions!\n");
return ret;
}
neoversion_t ver = icsneo_getVersion();
printf("ICS icsneoc.dll version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
@@ -489,31 +487,6 @@ int main() {
printf("(%"PRIu64")\n", canMsg->timestamp);
break;
}
case ICSNEO_NETWORK_TYPE_LIN: {
neomessage_lin_t* linMsg = (neomessage_lin_t*)frame;
size_t frameLen = linMsg->length;
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
size_t numberBytesHeader = (dataLen > 1) ? 3 : 1;
size_t numberBytesData = frameLen - numberBytesHeader;
if(linMsg->netid == ICSNEO_NETID_LIN) {
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
}
else if (linMsg->netid == ICSNEO_NETID_LIN2) {
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
}
for(size_t i = 0; i < dataLen; ++i) {
if (i < 2) {
printf("%02x ", linMsg->header[i+1]);
} else {
printf("%02x ", linMsg->data[i-2]);
}
}
if(numberBytesData > 0)
printf("| Checksum: 0x%02x\n", linMsg->data[numberBytesData-1]);
else
printf("| Checksum: 0x%02x\n", linMsg->header[numberBytesHeader-1]);
}
default:
printf("\tMessage on netid %d with length %zu\n", frame->netid, frame->length);
break;
@@ -789,7 +762,7 @@ int main() {
case 'X':
case 'x':
printf("Exiting program\n");
return 0;
return !icsneo_close();
default:
printf("Unexpected input, exiting!\n");
return 1;
-4
View File
@@ -1,4 +0,0 @@
add_executable(libicsneoc-legacy-lin-example lin/main.c)
add_executable(libicsneoc-legacy-device-settings-example deviceSettings/main.c)
target_link_libraries(libicsneoc-legacy-lin-example icsneolegacy)
target_link_libraries(libicsneoc-legacy-device-settings-example icsneolegacy)
-80
View File
@@ -1,80 +0,0 @@
# libicsneo C Example
This is an example console application that uses the icsneoc library to control an Intrepid Control Systems hardware device.
## Cloning
This will create a copy of the repository on your local machine.
Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples -b v0.2.0-dev --recursive
```
Alternatively, if you cloned without the `--recursive` flag, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
## Windows using Visual Studio 2017+
### Building the DLL
First, we are going to build the icsneoc library into a .dll file that we can later use in order to access the library functions.
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->Cmake...`
3. Navigate to `third-party/libicsneo` and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Select `Build->Rebuild All`
6. Visual Studio will generate the `icsneoc.dll` file, which can then be found by selecting `Project->Cmake Cache (x64-Debug Only)->Open in Explorer`. If the file cannot be found, search in `libicsneo-examples/third-party/libicsneo/out/build/x64-Debug` and double-check that the build succeeded in step 5.
7. Move the `icsneoc.dll` file to the `/C/Windows/System32` folder. This will allow it to be found by icsneo_init(), which loads all the library functions.
* Alternatively, the `icsneoc.dll` file can be placed in the same directory as `libicsneoc-example.exe`, which is typically `libicsneo-examples/libicsneoc-example/out/build/x64-Debug`, although this is not recommended. For more information, refer to [the Microsoft documentation](https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order).
### Building the example program
Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions.
1. Choose `File->Open->Cmake...`
2. Navigate to `libicsneo-examples/libicsneoc-example` and select the `CMakeLists.txt` there.
3. Visual Studio will process the CMake project.
4. Select `Build->Rebuild All`
5. Click on the dropdown arrow attached to the green play button (labelled "Select Startup Item") and select `libicsneoc-example.exe`
6. Click on the green play button to run the example.
## Ubuntu 18.04 LTS
### Building the .so
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo cp libicsneoc.so /usr/lib` so that it can be found via the default ubuntu .so search path. For more information, see the [ld.so.8 man page](http://man7.org/linux/man-pages/man8/ld.so.8.html).
### Building the example program
Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions.
1. Change directories to `libicsneo-examples/libicsneoc-example`
2. Create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo ./libicsneoc-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
## macOS
Instructions coming soon&trade;
-95
View File
@@ -1,95 +0,0 @@
/*
NOTE: This example is written for a RED 2 device. To use another device type,
change the union lookup type in the pSettings structure to match your device!
e.g. pSettings.Settings.red2 -> pSettings.Settings.fire3
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#if defined _WIN32
#include "icsneo/platform/windows.h"
#define SLEEP(msecs) Sleep(msecs)
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <time.h>
#define SLEEP(msecs) do { \
struct timespec ts; \
ts.tv_sec = msecs/1000; \
ts.tv_nsec = msecs%1000*1000; \
nanosleep(&ts, NULL); \
} while (0)
#else
#error "Platform unknown"
#endif
// Get the PRIu64 macro for timestamps
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
// Include icsneo/icsneolegacy.h to access library functions
#include "icsneo/icsneolegacy.h"
int main() {
int ver = icsneoGetDLLVersion();
printf("ICS icsneolegacy.dll version %u\n\n", ver);
// Find and attempt to open device
//legacy open device
int numDevices = 255;
NeoDeviceEx devices[255] = {0};
void* hObject = NULL; // holds a handle to the neoVI object
int iRetVal = 0;
int iResult = 0;
SDeviceSettings pSettings = {0};
iRetVal = icsneoFindDevices(devices, &numDevices, NULL, 0, NULL, 0);
if(iRetVal && numDevices > 0) {
// Attempt to open the selected device, enable message polling, and go online
iRetVal = icsneoOpenDevice(&devices[0], &hObject, NULL, 1, 0, NULL, 0);
if(iRetVal) {
puts("Device found and opened!\n");
} else {
puts("Device found but failed to open!\n");
}
} else {
puts("No new devices found!\n");
}
// Get device settings
{
iRetVal = icsneoValidateHObject(hObject);
iRetVal = icsneoGetDeviceSettings(hObject, &pSettings, sizeof(SRed2Settings), 0);
if(iRetVal) {
puts("Settings read successfully\n");
printf("Baudrate: %u\n", pSettings.Settings.red2.lin1.Baudrate);
printf("Master resistor: %s \n", (pSettings.Settings.red2.lin1.MasterResistor == 0 ? "true" : "false"));
} else {
puts("Settings not read!\n");
}
}
SLEEP(1000);
// set device settings
{
pSettings.Settings.red2.lin1.Baudrate = 19200;
pSettings.Settings.red2.lin1.MasterResistor = 1;
iRetVal = icsneoSetDeviceSettings(hObject, &pSettings, sizeof(pSettings), 1, 0);
if(iRetVal) {
puts("Settings set successfully\n");
iRetVal = icsneoGetDeviceSettings(hObject, &pSettings, sizeof(SRed2Settings), 0);
printf("Baudrate: %u\n", pSettings.Settings.red2.lin1.Baudrate);
printf("Master resistor: %s\n", (pSettings.Settings.red2.lin1.MasterResistor == 0 ? "true" : "false"));
} else {
puts("Settings not set!\n");
}
}
int iNumberOfErrors = 0;
// Attempt to close the device
{
// Close Communication
iResult = icsneoClosePort(hObject, &iNumberOfErrors);
}
puts("Exiting program\n");
return iResult;
}
-164
View File
@@ -1,164 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#if defined _WIN32
#include "icsneo/platform/windows.h"
#define SLEEP(msecs) Sleep(msecs)
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <time.h>
#define SLEEP(msecs) do { \
struct timespec ts; \
ts.tv_sec = msecs/1000; \
ts.tv_nsec = msecs%1000*1000; \
nanosleep(&ts, NULL); \
} while (0)
#else
#error "Platform unknown"
#endif
// Get the PRIu64 macro for timestamps
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
// Include icsneo/icsneolegacy.h to access library functions
#include "icsneo/icsneolegacy.h"
int main() {
int ver = icsneoGetDLLVersion();
printf("ICS icsneolegacy.dll version %u\n\n", ver);
// Find and attempt to open device
//legacy open device
int numDevices = 255;
NeoDeviceEx devices[255] = {0};
void* hObject = NULL; // holds a handle to the neoVI object
int iRetVal = 0;
int iResult = 0;
iRetVal = icsneoFindDevices(devices, &numDevices, NULL, 0, NULL, 0);
if(iRetVal && numDevices < 0) {
// Attempt to open the selected device, enable message polling, and go online
iRetVal = icsneoOpenDevice(&devices[0], &hObject, NULL, 1, 0, NULL, 0);
if(iRetVal) {
printf("Device found and opened!\n");
} else {
printf("Device found but failed to open!\n");
}
} else {
printf("No new devices found!\n");
}
// Send message LIN
{
//lin responder frame
icsSpyMessageJ1850 msg1 = {0};
int lNetworkID;
msg1.Protocol = SPY_PROTOCOL_LIN;
msg1.StatusBitField = 0;
msg1.StatusBitField2 = 0;
lNetworkID = NETID_LIN2;
msg1.Header[0] = 0x11; //protected ID
msg1.Header[1] = 0xaa;
msg1.Header[2] = 0xbb;
msg1.Data[0] = 0xcc;
msg1.Data[1] = 0xdd;
msg1.Data[2] = 0x11;
msg1.Data[3] = 0x22;
msg1.Data[4] = 0x33;
msg1.Data[5] = 0x44;
msg1.Data[6] = 0x44; //checksum 0x33 enhanced
msg1.NumberBytesData = 7;
msg1.NumberBytesHeader = 3;
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg1, lNetworkID, 1);
if(!iRetVal)
printf("Device failed to transmit LIN responder update\n");
else
printf("Transmitted successfully!\n");
icsSpyMessageJ1850 msg2 = {0};
msg2.Protocol = SPY_PROTOCOL_LIN;
msg2.StatusBitField = SPY_STATUS_INIT_MESSAGE;
lNetworkID = NETID_LIN;
msg2.Header[0] = 0x11; //protected ID
msg2.NumberBytesData = 0;
msg2.NumberBytesHeader = 1;
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg2, lNetworkID, 1);
if(!iRetVal)
printf("Device failed to transmit LIN commander header\n");
else
printf("Transmitted successfully!\n");
SLEEP(250);
icsSpyMessageJ1850 msg3 = {0};
msg3.Protocol = SPY_PROTOCOL_LIN;
msg3.StatusBitField = SPY_STATUS_INIT_MESSAGE;
msg3.StatusBitField2 = 0;
lNetworkID = NETID_LIN;
msg3.Header[0] = 0xe2; //protected ID
msg3.Header[1] = 0x44;
msg3.Header[2] = 0x33;
msg3.Data[0] = 0x22;
msg3.Data[1] = 0x11;
msg3.Data[2] = 0x11;
msg3.Data[3] = 0x22;
msg3.Data[4] = 0x33;
msg3.Data[5] = 0x44;
msg3.Data[6] = 0xc7; //checksum
msg3.NumberBytesData = 7;
msg3.NumberBytesHeader = 3;
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg3, lNetworkID, 1);
if(!iRetVal)
printf("Device failed to transmit LIN commander message\n");
else
printf("Transmitted successfully!\n");
}
SLEEP(1000);
// Get messages
{
static icsSpyMessage rxMsg[30000];
int numMessages = 0;
int numErrors = 0;
iRetVal = icsneoGetMessages(hObject, rxMsg, &numMessages, &numErrors);
if(!iRetVal)
printf("Get Messages failed!\n");
else
for(int idx = 0; idx < numMessages; ++idx)
{
if(rxMsg[idx].Protocol == SPY_PROTOCOL_LIN) {
const icsSpyMessageJ1850* linMsg = (icsSpyMessageJ1850*)&rxMsg[idx];
size_t frameLen = (linMsg->NumberBytesHeader + linMsg->NumberBytesData);
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
if(linMsg->NetworkID == NETID_LIN) {
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen);
}
else if (linMsg->NetworkID == NETID_LIN2) {
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen);
}
for(size_t i = 0; i < dataLen; ++i) {
if (i < 2) {
printf("%02x ", linMsg->Header[i+1]);
} else {
printf("%02x ", linMsg->Data[i-2]);
}
}
if(linMsg->NumberBytesData > 0)
printf("| Checksum: 0x%02x\n", linMsg->Data[linMsg->NumberBytesData-1]);
else
printf("| Checksum: 0x%02x\n", linMsg->Header[linMsg->NumberBytesHeader-1]);
}
}
}
int iNumberOfErrors = 0;
// Attempt to close the device
{
// Close Communication
iResult = icsneoClosePort(hObject, &iNumberOfErrors);
}
printf("Exiting program\n");
return iResult;
}
-5
View File
@@ -1,5 +0,0 @@
add_executable(libicsneoc-simple-lin-example lin/main.c)
if(UNIX)
target_link_libraries(libicsneoc-simple-lin-example ${CMAKE_DL_LIBS})
endif()
target_link_libraries(libicsneoc-simple-lin-example icsneoc)
-80
View File
@@ -1,80 +0,0 @@
# libicsneo C Example
This is an example console application that uses the icsneoc library to control an Intrepid Control Systems hardware device.
## Cloning
This will create a copy of the repository on your local machine.
Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples -b v0.2.0-dev --recursive
```
Alternatively, if you cloned without the `--recursive` flag, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
## Windows using Visual Studio 2017+
### Building the DLL
First, we are going to build the icsneoc library into a .dll file that we can later use in order to access the library functions.
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->Cmake...`
3. Navigate to `third-party/libicsneo` and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Select `Build->Rebuild All`
6. Visual Studio will generate the `icsneoc.dll` file, which can then be found by selecting `Project->Cmake Cache (x64-Debug Only)->Open in Explorer`. If the file cannot be found, search in `libicsneo-examples/third-party/libicsneo/out/build/x64-Debug` and double-check that the build succeeded in step 5.
7. Move the `icsneoc.dll` file to the `/C/Windows/System32` folder. This will allow it to be found by icsneo_init(), which loads all the library functions.
* Alternatively, the `icsneoc.dll` file can be placed in the same directory as `libicsneoc-example.exe`, which is typically `libicsneo-examples/libicsneoc-example/out/build/x64-Debug`, although this is not recommended. For more information, refer to [the Microsoft documentation](https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order).
### Building the example program
Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions.
1. Choose `File->Open->Cmake...`
2. Navigate to `libicsneo-examples/libicsneoc-example` and select the `CMakeLists.txt` there.
3. Visual Studio will process the CMake project.
4. Select `Build->Rebuild All`
5. Click on the dropdown arrow attached to the green play button (labelled "Select Startup Item") and select `libicsneoc-example.exe`
6. Click on the green play button to run the example.
## Ubuntu 18.04 LTS
### Building the .so
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo cp libicsneoc.so /usr/lib` so that it can be found via the default ubuntu .so search path. For more information, see the [ld.so.8 man page](http://man7.org/linux/man-pages/man8/ld.so.8.html).
### Building the example program
Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions.
1. Change directories to `libicsneo-examples/libicsneoc-example`
2. Create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo ./libicsneoc-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
## macOS
Instructions coming soon&trade;
-429
View File
@@ -1,429 +0,0 @@
// Signal to dynamically load the library
//#define ICSNEOC_DYNAMICLOAD
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
// Get the PRIu64 macro for timestamps
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
// Include icsneo/icsneoc.h to access library functions
#include "icsneo/icsneoc.h"
#ifdef _WIN32
#define SLEEP(msecs) Sleep(msecs)
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <time.h>
#define SLEEP(msecs) do { \
struct timespec ts; \
ts.tv_sec = msecs/1000; \
ts.tv_nsec = msecs%1000*1000; \
nanosleep(&ts, NULL); \
} while (0)
#else
#error "Platform unknown"
#endif
size_t msgLimit = 50000;
size_t numDevices = 0;
neodevice_t devices[99];
const neodevice_t* selectedDevice = NULL;
/**
* \brief Prints all current known devices to output in the following format:
* [num] DeviceType SerialNum Connected: Yes/No Online: Yes/No Msg Polling: On/Off
*
* If any devices could not be described due to an error, they will appear in the following format:
* Description for device num not available!
*/
void printAllDevices() {
if(numDevices == 0) {
printf("No devices found! Please scan for new devices.\n");
}
for(size_t i = 0; i < numDevices; i++) {
char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 };
size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
// Updates productDescription and descriptionLength for each device
if(icsneo_describeDevice(devices + i, productDescription, &descriptionLength)) {
printf("[%zd] %s\tConnected: ", i + 1, productDescription);
if(icsneo_isOpen(devices + i)) {
printf("Yes\t");
} else printf("No\t");
printf("Online: ");
if(icsneo_isOnline(devices + i)) {
printf("Yes\t");
} else printf("No\t");
printf("Msg Polling: ");
if(icsneo_isMessagePollingEnabled(devices + i)) {
printf("On\n");
} else printf("Off\n");
} else {
printf("Description for device %zd not available!\n", i + 1);
}
}
}
/**
* \brief Scans for any new devices, adding them to devices and updating numDevices accordingly
* A total of 99 devices may be stored at once
*/
size_t scanNewDevices() {
neodevice_t newDevices[99];
size_t numNewDevices = 99;
icsneo_findAllDevices(newDevices, &numNewDevices);
for(size_t i = 0; i < numNewDevices; ++i) {
devices[numDevices + i] = newDevices[i];
}
numDevices += numNewDevices;
return numNewDevices;
}
void printLastError() {
neoevent_t error;
if(icsneo_getLastError(&error))
printf("Error 0x%u: %s\n", error.eventNumber, error.description);
else
printf("No errors found!\n");
}
/**
* \brief Gets all current API events and prints them to output
* Flushes the API event cache, meaning future calls (barring any new events) will not detect any further API events
*/
void printAPIEvents() {
neoevent_t events[99];
size_t eventCount = 99;
if(icsneo_getEvents(events, &eventCount)) {
if(eventCount == 1) {
printf("1 API event found!\n");
printf("Event 0x%u: %s\n", events[0].eventNumber, events[0].description);
} else {
printf("%d API events found!\n", (int) eventCount);
for(size_t i = 0; i < eventCount; ++i) {
printf("Event 0x%u: %s\n", events[i].eventNumber, events[i].description);
}
}
} else {
printf("Failed to get API events!\n");
}
}
/**
* \brief Gets all current device events and prints them to output. If no device events were found, printAPIEvents() is called
* Flushes the device event cache, meaning future calls (barring any new events) will not detect any further device events for this device
*/
void printDeviceEvents(neodevice_t* device) {
neoevent_t events[99];
size_t eventCount = 99;
if(icsneo_getDeviceEvents(selectedDevice, events, &eventCount)) {
if(eventCount == 1) {
printf("1 device event found!\n");
printf("Event 0x%x: %s\n", events[0].eventNumber, events[0].description);
} else {
printf("%d device events found!\n", (int) eventCount);
for(size_t i = 0; i < eventCount; ++i) {
printf("Event 0x%x: %s\n", events[i].eventNumber, events[i].description);
}
}
}
}
/**
* \brief Used to check character inputs for correctness (if they are found in an expected list)
* \param[in] numArgs the number of possible options for the expected character
* \param[in] ... the possible options for the expected character
* \returns the entered character
*
* This function repeatedly prompts the user for input until a matching input is entered
* Example usage: char input = getCharInput(5, 'F', 'u', 'b', 'a', 'r');
*/
char getCharInput(int numArgs, ...) {
// 99 chars shold be more than enough to catch any typos
char input[99];
bool found = false;
va_list vaList;
va_start(vaList, numArgs);
char* list = (char*) calloc(numArgs, sizeof(char));
for(int i = 0; i < numArgs; ++i) {
*(list + i) = va_arg(vaList, int);
}
va_end(vaList);
while(!found) {
if(fgets(input, 99, stdin) == NULL) {
break;
}
if(strlen(input) == 2) {
for(int i = 0; i < numArgs; ++i) {
if(input[0] == *(list + i)) {
found = true;
break;
}
}
}
if(!found) {
printf("Input did not match expected options. Please try again.\n");
}
}
free(list);
return input[0];
}
/**
* \brief Prompts the user to select a device from the list of currently known devices
* \returns a pointer to the device in devices[] selected by the user
* Requires an input from 1-9, so a maximum of 9 devices are supported
*/
const neodevice_t* selectDevice() {
printf("Please select a device:\n");
printAllDevices();
printf("\n");
size_t selectedDeviceNum = 10;
while(selectedDeviceNum > numDevices) {
char deviceSelection = getCharInput(9, '1', '2', '3', '4', '5', '6', '7', '8', '9');
if(deviceSelection < '0') {
printf("Selected device out of range!\n");
continue;
}
selectedDeviceNum = deviceSelection - '0';
if(selectedDeviceNum > numDevices) {
printf("Selected device out of range!\n");
}
}
printf("\n");
return devices + selectedDeviceNum - 1;
}
int main() {
neoversion_t ver = icsneo_getVersion();
printf("ICS icsneoc version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
// Find and attempt to open device
size_t numNewDevices = scanNewDevices();
if(numNewDevices == 1) {
printf("1 new device found!\n");
} else {
printf("%d new devices found!\n", (int) numNewDevices);
}
printAllDevices();
printf("\n");
// Select a device and get its description
if(numDevices == 0) {
printf("No devices found! Please scan for new devices.\n\n");
return 1;
}
selectedDevice = &devices[0];
// Get the product description for the device
char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 };
size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
icsneo_describeDevice(selectedDevice, productDescription, &descriptionLength);
// Attempt to open the selected device
{
if(icsneo_openDevice(selectedDevice)) {
printf("%s successfully opened!\n\n", productDescription);
} else {
printf("%s failed to open!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Attempt to go online
{
if(icsneo_goOnline(selectedDevice)) {
printf("%s successfully went online!\n\n", productDescription);
} else {
printf("%s failed to go online!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Attempt to enable message polling
{
if(icsneo_enableMessagePolling(selectedDevice)) {
printf("Successfully enabled message polling for %s!\n\n", productDescription);
} else {
printf("Failed to enable message polling for %s!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Send message LIN
{
// Start generating sample msg
uint8_t sendMessageData[8];
sendMessageData[0] = 0x33;
sendMessageData[1] = 0x44;
sendMessageData[2] = 0x55;
sendMessageData[3] = 0x66;
sendMessageData[4] = 0x77;
sendMessageData[5] = 0x88;
sendMessageData[6] = 0x88;
neomessage_lin_t msg = {0};
msg.header[0] = 0x11; //protected ID
msg.header[1] = 0x11;
msg.header[2] = 0x22;
msg.length = 10;
msg.netid = ICSNEO_NETID_LIN;
msg.data = sendMessageData;
msg.linStatus.txCommander = 1;
msg.linStatus.txChecksumEnhanced = 1;
msg.type = ICSNEO_NETWORK_TYPE_LIN;
msg.checksum = 0x88;
// Attempt to transmit the sample msg
if(icsneo_transmit(selectedDevice, (const neomessage_t*) &msg)) {
printf("Message transmit successful!\n\n");
} else {
printf("Failed to transmit message to %s!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Wait for a moment
SLEEP(1000);
// Get messages
{
// Prepare the array of neomessage_t ptrs for reading in the messages
neomessage_t* msgs = (neomessage_t*) malloc(msgLimit * sizeof(neomessage_t));
// Get messages
size_t msgCount = msgLimit;
// Attempt to get messages
if(!icsneo_getMessages(selectedDevice, msgs, &msgCount, (uint64_t) 0)) {
printf("Failed to get messages for %s!\n\n", productDescription);
printLastError();
free(msgs);
printf("\n");
}
if(msgCount == 1) {
printf("1 message received from %s!\n", productDescription);
} else {
printf("%d messages received from %s!\n", (int) msgCount, productDescription);
}
// Print out the received messages
for(size_t i = 0; i < msgCount; i++) {
const neomessage_t* msg = &msgs[i];
switch(msg->messageType) {
case ICSNEO_MESSAGE_TYPE_FRAME: {
const neomessage_frame_t* frame = (neomessage_frame_t*)msg;
switch(frame->type) {
case ICSNEO_NETWORK_TYPE_CAN: {
neomessage_can_t* canMsg = (neomessage_can_t*)frame;
printf("\t0x%03x [%zu] ", canMsg->arbid, canMsg->length);
for(size_t i = 0; i < canMsg->length; i++) {
printf("%02x ", canMsg->data[i]);
}
if(canMsg->status.transmitMessage)
printf("TX%s %04x ", canMsg->status.globalError ? " ERR" : "", canMsg->description);
printf("(%"PRIu64")\n", canMsg->timestamp);
break;
}
case ICSNEO_NETWORK_TYPE_LIN: {
neomessage_lin_t* linMsg = (neomessage_lin_t*)frame;
size_t frameLen = linMsg->length;
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
size_t numberBytesHeader = (dataLen > 1) ? 3 : 1;
size_t numberBytesData = frameLen - numberBytesHeader;
if(linMsg->netid == ICSNEO_NETID_LIN) {
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
}
else if (linMsg->netid == ICSNEO_NETID_LIN2) {
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
}
for(size_t i = 0; i < dataLen; ++i) {
if (i < 2) {
printf("%02x ", linMsg->header[i+1]);
} else {
printf("%02x ", linMsg->data[i-2]);
}
}
printf("| Checksum: 0x%02x\n", linMsg->checksum);
break;
}
}
break;
}
case ICSNEO_MESSAGE_TYPE_CAN_ERROR_COUNT: {
const neomessage_can_error_t* cec = (neomessage_can_error_t*)msg;
printf("\tCAN error counts changed, TEC=%d, REC=%d%s", cec->transmitErrorCount, cec->receiveErrorCount,
cec->status.canBusOff ? " (Bus Off)" : "");
break;
}
}
}
printf("\n");
free(msgs);
}
// Attempt to disable message polling
{
if(icsneo_disableMessagePolling(selectedDevice)) {
printf("Successfully disabled message polling for %s!\n\n", productDescription);
} else {
printf("Failed to disable message polling limit for %s!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Attempt to go offline
{
if(icsneo_goOffline(selectedDevice)) {
printf("%s successfully went offline!\n\n", productDescription);
} else {
printf("%s failed to go offline!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Attempt to close the device
{
if(icsneo_closeDevice(selectedDevice)) {
numDevices--;
printf("Successfully closed %s!\n\n", productDescription);
// Shifts everything after the removed device 1 index to the left
bool startResizing = false;
for(size_t i = 0; i < numDevices; ++i) {
if(selectedDevice == devices + i)
startResizing = true;
if(startResizing)
devices[i] = devices[i + 1];
}
selectedDevice = NULL;
} else {
printf("Failed to close %s!\n\n", productDescription);
printLastError();
printf("\n");
}
}
//exit
printf("Exiting program\n");
return 0;
}
+25
View File
@@ -1,2 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-a2b VERSION 0.2.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-a2b src/a2b.cpp)
target_link_libraries(libicsneocpp-a2b icsneocpp)
+48
View File
@@ -0,0 +1,48 @@
# libicsneo C++ Example
This is an example console application which uses libicsneo to connect to an Intrepid Control Systems hardware device. It has both interactive and simple examples for sending and receiving CAN & CAN FD traffic.
## Building
This example shows how to use the C++ version of libicsneo with CMake. It will build libicsneo along with your project.
First, you need to clone the repository onto your local machine. Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples --recursive
```
Alternatively, if you cloned without the `--recursive flag`, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
### Windows using Visual Studio 2017+
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->CMake...`
3. Navigate to the `libicsneocpp-example` folder and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Choose the dropdown attached to the green play button (labelled "select startup item...") in the toolbar.
6. Select `libicsneocpp-simple-example.exe`
7. Press the green play button to compile and run the example.
### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make libicsneocpp-interactive-example` to build.
* Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Now run `sudo ./libicsneocpp-interactive-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6.
### macOS
Instructions coming soon&trade;
+32 -438
View File
@@ -1,458 +1,52 @@
/**
* libicsneo A2B example
*
* Example were made to be ran with RAD-A2B as main node on TDM4 16 bit channel size and one additional sub node (either an ADI board or an additional RAD-A2B).
* Follow the specific hardware instructions per example to ensure expected output. Be sure to configure the A2B network before running these examples, especially
* ones which Tx or Rx audio.
*
* Options:
* -h, --help Display help message.
* -e, --example [EXAMPLE_NUM] Example to run.
* Example usage: ./libicsneocpp-a2b.exe --example 1
* Example usage: ./libicsneocpp-a2b.exe -h
*/
#include <iostream>
#include <fstream>
#include <icsneo/icsneocpp.h>
#include <icsneo/device/tree/rada2b/rada2bsettings.h>
#include <icsneo/communication/message/callback/streamoutput/a2bwavoutput.h>
#include <string>
#include <math.h>
#include <iomanip>
#include <thread>
#include <chrono>
std::string makeWAV() {
icsneo::WAVHeader header = icsneo::WAVHeader(1, 48000, 16);
std::vector<uint8_t> sineWAVSamples = {
0xFF, 0x3F, 0x81, 0x5A, 0xD9, 0x6E, 0xA2, 0x7B, 0xFF, 0x7F, 0xA2, 0x7B, 0xD9, 0x6E, 0x81, 0x5A,
0xFF, 0x3F, 0x20, 0x21, 0x00, 0x00, 0xE0, 0xDE, 0x01, 0xC0, 0x7F, 0xA5, 0x27, 0x91, 0x5E, 0x84,
0x01, 0x80, 0x5E, 0x84, 0x27, 0x91, 0x7F, 0xA5, 0x01, 0xC0, 0xE0, 0xDe, 0x00, 0x00, 0x20, 0x21
};
#include "icsneo/icsneocpp.h"
std::vector<uint8_t> sineWAV;
sineWAV.reserve(sineWAVSamples.size() + sizeof(header));
const std::string rada2bSerial = "Your RADA2B serial number.";
sineWAV.insert(sineWAV.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header));
std::copy(sineWAVSamples.begin(), sineWAVSamples.end(), std::back_inserter(sineWAV));
int main() {
std::cout << "Start example\n";
auto devices = icsneo::FindAllDevices();
return std::string(sineWAV.begin(), sineWAV.end());
}
/**
* Example 0: TX
*/
void example0(const std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Transmitting a sine tone..." << std::endl;
// Create sine tone in wav format
std::string wavString = makeWAV();
std::istringstream sineWAV(wavString);
double elapsedTime = 0.0;
// Create a IWAVStream object which represents a WAV data stream
// the IWAVStream object here is initialized with an outside std::ostream,
// so it holds a reference pointer to this stream.
icsneo::IWAVStream wavStream(sineWAV);
// Create a channel map which maps each message channel to a channel in the input WAV file
icsneo::ChannelMap channelMap;
// Here we will just set every message channel to channel 0 in the WAV file
// We have 8 channels since this is TDM4 and we include both upstream and downstream
// see docs for specific message channel labeling information
for(uint8_t messageChannel = 0; messageChannel < 8; messageChannel++) {
channelMap[messageChannel] = 0;
}
// Play roughly 5 seconds of sine tone.
while(elapsedTime < 5.0) {
while(elapsedTime < 5.0) {
// If WAVStream is invalid (at EOF) break out of loop
if(!wavStream) {
break;
}
// Creates a new message with the maximum amount of allocated frames
auto msg = std::make_shared<icsneo::A2BMessage>(
icsneo::A2BMessage::TDMMode::TDM4, /* TDM mode of the message, we use TDM4 for this whole example*/
true /* true if we want 16 bit channels in the message, false for 32 bit. This should match the RAD-A2B device setting */
);
msg->txmsg = true;
msg->network = icsneo::Network(icsneo::Network::NetID::A2B2);
// Load the WAV audio data into the desired channel, break if we fail to load
if(!msg->loadAudioBuffer(wavStream, channelMap)) {
break;
}
// Transmit the message
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
break;
}
elapsedTime += (static_cast<double>(msg->getNumFrames()))*1.0/48000.0;
}
// Reset the WAV stream
wavStream.reset();
}
}
/**
* Example 1: RX
*/
void example1(const std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Receiving 5 seconds of audio data..." << std::endl;
// Add WAV output message callback
// Saves samples to "out.wav"
auto handler = rada2b->addMessageCallback(
std::make_shared<icsneo::A2BWAVOutput>(
"audio16bit.wav", /* output file name */
icsneo::ChannelMap( /** channel mapping which maps our output WAV channels to the message channels from incoming messages */
{ /* See docs for specific A2B channel indexing information */
{static_cast<uint8_t>(3u), static_cast<uint8_t>(0u)}, /* Map output WAV channel 3 to channel 0 downstream of the A2B network/A2BMessage */
{static_cast<uint8_t>(2u), static_cast<uint8_t>(1u)}, /* Map output WAV channel 2 to channel 0 upstream of the A2B network/A2BMessage */
{static_cast<uint8_t>(1u), static_cast<uint8_t>(2u)}, /* Map output WAV channel 1 to channel 1 downstream of the A2B network/A2BMessage */
{static_cast<uint8_t>(0u), static_cast<uint8_t>(3u)} /* Map output WAV channel 0 to channel 1 upstream of the A2B network/A2BMessage */
}
),
icsneo::PCMType::L16, /* store samples with 16 bit resolution*/
2u, /* Number of channels in the output WAV file */
48000 /* Sample rate of WAV file */
)
);
// Sleep this thread for 5 seconds, message callback still runs
std::this_thread::sleep_for(std::chrono::seconds(5));
// Make sure you send 16 bit audio data on the above message channels in the channel map
// to the RAD-A2B main node through a microphone or a different modem.
// You can configure the message channels by changing the stream config in the A2B schematic
// Remove callback
rada2b->removeMessageCallback(handler);
}
/**
* Example 2: RAD-A2B settings
*/
void example2(const std::shared_ptr<icsneo::Device>& rada2b) {
uint8_t numChannels;
{
// Get device settings
auto* settings = rada2b->settings.get();
auto* rada2bSettings = static_cast<icsneo::RADA2BSettings*>(settings);
// Check if monitor mode is enabled
auto type = rada2bSettings->getNodeType(icsneo::RADA2BSettings::RADA2BDevice::Monitor);
if(type == icsneo::RADA2BSettings::NodeType::Monitor) {
std::cout << "Device is in monitor mode" << std::endl;
}
else {
std::cout << "Device is not in monitor mode" << std::endl;
}
// Get current tdm mode
numChannels = rada2bSettings->getNumChannels(icsneo::RADA2BSettings::RADA2BDevice::Node);
std::cout << "Current num channels: " << static_cast<uint32_t>(numChannels) << std::endl;
// Set node type to master node.
rada2bSettings->setNodeType(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::NodeType::Master);
// Set TDM mode to TDM8
rada2bSettings->setTDMMode(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::TDMMode::TDM4);
// Apply local settings to device
rada2bSettings->apply();
}
}
/**
* Example 3: Packaging and transmitting sine tone using A2BMessage API
*/
void example3(const std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Transmitting a 1000 hz sine tone." << std::endl;
float deltaTime = static_cast<float>(1.0/48000.0);
float elapsedTime = 0.0;
float twoPI = static_cast<float>(2.0*atan(1.0)*4.0);
float frequency = 1000;
float amplitude = static_cast<float>((1 << 15) - 1);
size_t tdm = 4;
size_t bytesPerSample = 2;
size_t numFrames = 2048 / (2 * tdm * bytesPerSample);
// Play for roughly 5 seconds
while(elapsedTime < 5.0) {
// Allocate message
std::shared_ptr<icsneo::A2BMessage> a2bmsgPtr = std::make_shared<icsneo::A2BMessage>(numFrames, icsneo::A2BMessage::TDMMode::TDM4, true);
icsneo::A2BMessage& a2bmsg = *a2bmsgPtr.get();
a2bmsg.network = icsneo::Network(icsneo::Network::NetID::A2B2);
a2bmsg.txmsg = true;
for(size_t frame = 0; frame < a2bmsg.getNumFrames(); frame++) {
// Sine tone sample, amplitude 1000, frequency 1000 hz
float contSample = amplitude*sin(twoPI*frequency*elapsedTime);
icsneo::PCMSample sample = static_cast<icsneo::PCMSample>(contSample);
// Send this sine wave sample downstream on channels 0, 1, and 2
a2bmsg.setChannelSample(
icsneo::A2BMessage::Direction::Downstream,
0,
frame,
sample,
icsneo::PCMType::L16
);
a2bmsg.setChannelSample(
icsneo::A2BMessage::Direction::Downstream,
1,
frame,
sample,
icsneo::PCMType::L16
);
a2bmsg.setChannelSample(
icsneo::A2BMessage::Direction::Downstream,
2,
frame,
sample,
icsneo::PCMType::L16
);
elapsedTime+=deltaTime;
}
// Transmit message to device
if(!rada2b->transmit(a2bmsgPtr)) {
std::cout << "Failed to transmit." << std::endl;
std::shared_ptr<icsneo::Device> rada2b;
for(auto& device : devices) {
if(device->getSerial() == rada2bSerial) {
rada2b = device;
}
}
rada2b->open();
rada2b->goOnline();
std::cout << rada2b->describe() << "\n";
}
auto handler1 = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("examples/cpp/a2b/src/out.wav")); // Starts writing A2B PCM data to out.wav
/**
* Example 4: Retrieving A2B bus status using I2C messaages.
*/
void example4(const std::shared_ptr<icsneo::Device>& rada2b) {
std::shared_ptr<icsneo::I2CMessage> msg = std::make_shared<icsneo::I2CMessage>();
std::shared_ptr<icsneo::MessageFilter> msgFilter = std::make_shared<icsneo::MessageFilter>(icsneo::Network::NetID::I2C2);
auto handler2 = rada2b->addMessageCallback(
std::make_shared<icsneo::MessageCallback>(
[](std::shared_ptr<icsneo::Message> message) {
std::cout << "Got in callback " << std::endl;
if(message->type == icsneo::Message::Type::Frame) {
std::shared_ptr<icsneo::Frame> frame = std::static_pointer_cast<icsneo::Frame>(message);
msg->network = icsneo::Network(icsneo::Network::NetID::I2C2);
msg->controlBytes.resize(1);
msg->controlBytes[0] = static_cast<uint8_t>(0x17u); // Register address for A2B INTTYPE
msg->dataBytes.resize(1, 0);
msg->direction = icsneo::I2CMessage::Direction::Read;
msg->deviceMode = icsneo::I2CMessage::DeviceMode::Controller;
msg->address = static_cast<uint16_t>(0x68); // A2B master node address.
msg->isTXMsg = true;
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::MessageCallback>(
[] (std::shared_ptr<icsneo::Message> newMsg) {
if(newMsg->type == icsneo::Message::Type::Frame) {
const auto& frame = std::dynamic_pointer_cast<icsneo::Frame>(newMsg);
if(frame && frame->network.getNetID() == icsneo::Network::NetID::I2C2) {
const auto& i2cMessage = std::dynamic_pointer_cast<icsneo::I2CMessage>(frame);
if(!i2cMessage) {
return;
}
if(i2cMessage->controlBytes.size() == 1 && i2cMessage->direction == icsneo::I2CMessage::Direction::Read) {
if(i2cMessage->controlBytes[0] == 0x17u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "Current A2B bus status INTTYPE code: " << static_cast<int>(i2cMessage->dataBytes[0]) << '\n';
}
} else if(i2cMessage->controlBytes[0] == 0x03u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_PRODUCT register: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
} else if(i2cMessage->controlBytes[0] == 0x02u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_VENDOR register: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
} else if(i2cMessage->controlBytes[0] == 0x1C) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_INTMSK1 register value: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
}
if(frame->network.getType() == icsneo::Network::Type::A2B) {
std::shared_ptr<icsneo::A2BMessage> msg = std::static_pointer_cast<icsneo::A2BMessage>(frame);
std::cout << "Got A2B Message" << std::endl;
}
}
}
}
, msgFilter));
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
msg->controlBytes[0] = 0x03; // Address for A2B_PRODUCT register
}));
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(5)); // captures 5 seconds of A2B data.
msg->controlBytes[0] = 0x02; // Address for A2B_VENDOR register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
rada2b->removeMessageCallback(handler1);
rada2b->removeMessageCallback(handler2);
msg->controlBytes[0] = 0x1C ; // Address for A2B_INTMSK1 register
msg->dataBytes[0] = 0x11;
msg->direction = icsneo::I2CMessage::Direction::Write;
// Write register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit" << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
msg->direction = icsneo::I2CMessage::Direction::Read;
// Read register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
rada2b->removeMessageCallback(handler);
}
/**
* Example 5: Reading A2B sequence chart .puml file
*/
void example5(const std::shared_ptr<icsneo::Device>& rada2b) {
// The A2B sequence chart is located at binary index 0
constexpr uint16_t a2bSequenceChartIndex = 0;
// Create a ostream object to capture sequence chart data
std::ofstream a2bSequenceChart("a2b_sequence_chart.puml", std::ios::out | std::ios::binary);
if(!rada2b->readBinaryFile(a2bSequenceChart, a2bSequenceChartIndex)) {
std::cout << "Failed to read A2B sequence chart" << std::endl;
}
}
void displayUsage() {
std::cout << "libicsneo A2B example" << std::endl;
std::cout << "Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << "-h, --help\tDisplay help message." << std::endl;
std::cout << "-e, --example [EXAMPLE_NUM]\tExample to run." << std::endl;
std::cout << "Example usage: ./libicsneocpp-a2b.exe --example 1" << std::endl;
std::cout << "Example usage: ./libicsneocpp-a2b.exe -h" << std::endl;
std::cout << std::endl;
std::cout << "Example options:" << std::endl;
std::cout << "0\ttx" << std::endl;
std::cout << "1\trx" << std::endl;
std::cout << "2\tSet RAD-A2B settings" << std::endl;
std::cout << "3\tPackaging and transmitting sine wav using A2BMessage API" << std::endl;
std::cout << "4\tRead/write I2C registers on A2B board" << std::endl;
std::cout << "5\tReading out A2B sequence chart .puml file" << std::endl;
}
int main(int argc, char** argv) {
std::vector<std::string> arguments(argv, argv + argc);
if(argc > 4 || argc == 1) {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
if(std::any_of(arguments.begin(), arguments.end(), [](const std::string& arg) { return arg == "-h" || arg == "--help"; })) {
displayUsage();
return EXIT_SUCCESS;
}
if(arguments[1] != "-e" && arguments[1] != "--example") {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
int option = atoi(arguments[2].c_str());
if(option < 0 || option > 5) {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
std::cout << icsneo::GetVersion() << std::endl;
const auto& devices = icsneo::FindAllDevices();
auto it = std::find_if(devices.begin(), devices.end(), [&](const auto& dev) {
const auto& txNetworks = dev->getSupportedTXNetworks();
const auto& rxNetworks = dev->getSupportedRXNetworks();
if(std::none_of(txNetworks.begin(), txNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
return false;
}
if(std::none_of(rxNetworks.begin(), rxNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
return false;
}
return true;
}
);
if(it == devices.end()) {
std::cerr << "Could not find RAD-A2B." << std::endl;
return EXIT_FAILURE;
}
std::shared_ptr<icsneo::Device> rada2b = *it;
if(!rada2b->open()) {
std::cout << "Failed to open RAD-A2B." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Opened RAD-A2B." << std::endl;
}
if(!rada2b->goOnline()) {
std::cout << "Failed to go online with RAD-A2B." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "RAD-A2B online." << std::endl;
}
switch(option) {
case 0:
example0(rada2b);
break;
case 1:
example1(rada2b);
break;
case 2:
example2(rada2b);
break;
case 3:
example3(rada2b);
break;
case 4:
example4(rada2b);
break;
case 5:
example5(rada2b);
break;
default:
break;
}
std::cout << "End A2B example\n";
rada2b->goOffline();
rada2b->close();
return EXIT_SUCCESS;
}
return 0;
}
-2
View File
@@ -1,2 +0,0 @@
add_executable(libicsneocpp-app-error src/AppErrorExample.cpp)
target_link_libraries(libicsneocpp-app-error icsneocpp)
@@ -1,85 +0,0 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/message/message.h"
/*
* App errors are responses from the device indicating internal runtime errors
* NOTE: To trigger the app error in this example, disable the HSCAN network on the device
* (e.g. with neoVI Explorer)
*/
int main() {
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices();
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl << std::endl;
// Create an app error message filter, including "internal" messages
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::AppError);
filter->includeInternalInAny = true;
// ...and register a callback with it.
// Add your error handling here
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>(filter, [](std::shared_ptr<icsneo::Message> message) {
auto msg = std::static_pointer_cast<icsneo::AppErrorMessage>(message);
if(icsneo::Network::NetID::RED_App_Error == msg->network.getNetID()) {
std::cout << std::endl << "App error reported:" << std::endl;
std::cout << msg->getAppErrorString() << std::endl << std::endl;
}
}));
std::cout << "Going online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// Prepare a CAN message
std::cout << std::endl << "Transmitting a CAN frame... ";
auto txMessage = std::make_shared<icsneo::CANMessage>();
txMessage->network = icsneo::Network::NetID::HSCAN;
txMessage->arbid = 0x22;
txMessage->data.insert(txMessage->data.end(), {0xaa, 0xbb, 0xcc});
// The DLC will come from the length of the data vector
txMessage->isExtended = false;
txMessage->isCANFD = false;
// Transmit a CAN message on HSCAN, even though HSCAN is disabled on the device!
// Expect to see an app error caught in the callback defined above
ret = device->transmit(txMessage);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Go offline, stop sending and receiving traffic
device->removeMessageCallback(handler);
std::cout << "Going offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Disconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
}
return 0;
}
-2
View File
@@ -1,2 +0,0 @@
add_executable(libicsneocpp-coremini src/coremini.cpp)
target_link_libraries(libicsneocpp-coremini icsneocpp)
-81
View File
@@ -1,81 +0,0 @@
// Usage:
// ./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] [FLASH | SD]
#include <iostream>
#include <fstream>
#include <icsneo/icsneocpp.h>
void displayUsage() {
std::cout << "Usage:\n";
std::cout << "./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] [FLASH | SD]\n";
}
int main(int argc, char** argv) {
std::vector<std::string> arguments(argv, argv + argc);
if(arguments.size() != 4) {
displayUsage();
return EXIT_FAILURE;
}
std::cout << icsneo::GetVersion() << std::endl;
const auto& devices = icsneo::FindAllDevices();
auto it = std::find_if(
devices.begin(),
devices.end(),
[&arguments](const auto &d)
{ return d->getSerial() == arguments[1]; });
if(it == devices.end()) {
std::cout << "Failed to find device." << std::endl;
return EXIT_FAILURE;
}
std::shared_ptr<icsneo::Device> device = *it;
if(!device->open()) {
std::cout << "Failed to open device." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
if(!device->goOnline()) {
std::cout << "Failed to go online." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
std::string memTypeString = arguments[3];
icsneo::Disk::MemoryType type;
if(memTypeString == "FLASH") {
type = icsneo::Disk::MemoryType::Flash;
} else if(memTypeString == "SD") {
type = icsneo::Disk::MemoryType::SD;
} else {
std::cout << "Incorrect memory type option" << std::endl;
displayUsage();
return EXIT_FAILURE;
}
std::ifstream coreminiFile(arguments[2], std::ios::binary);
if (!device->uploadCoremini(coreminiFile, type)) {
std::cout << "Failed to upload coremini" << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
}
if (!device->startScript(type)) {
std::cout << "Failed to start script" << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
}
device->goOffline();
device->close();
return 0;
}
+25
View File
@@ -1,2 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-interactive-example VERSION 0.2.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-interactive-example src/InteractiveExample.cpp)
target_link_libraries(libicsneocpp-interactive-example icsneocpp)
-2
View File
@@ -1,2 +0,0 @@
add_executable(libicsneocpp-lin src/LINExample.cpp)
target_link_libraries(libicsneocpp-lin icsneocpp)
-157
View File
@@ -1,157 +0,0 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/linmessage.h"
/* Note: This example requires LIN 1 and LIN 2 channels to be connected on the device */
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
// 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;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl << std::endl;
int64_t baud = 19200;
std::cout << "Enable LIN commander resistor... ";
ret = device->settings->setCommanderResistorFor(icsneo::Network::NetID::LIN, true);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Disable LIN2 commander resistor... ";
ret = device->settings->setCommanderResistorFor(icsneo::Network::NetID::LIN2, false);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN to operate at " << baud << "bit/s... ";
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::LIN, baud);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN2 to operate at " << baud << "bit/s... ";
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::LIN2, baud);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN mode to NORMAL... ";
ret = device->settings->setLINModeFor(icsneo::Network::NetID::LIN, NORMAL_MODE);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN2 mode to NORMAL... ";
ret = device->settings->setLINModeFor(icsneo::Network::NetID::LIN2, NORMAL_MODE);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Applying settings... ";
ret = device->settings->apply();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Getting LIN Baudrate... ";
int64_t readBaud = device->settings->getBaudrateFor(icsneo::Network::NetID::LIN);
if(readBaud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (readBaud) << "bit/s" << std::endl;
std::cout << "Getting LIN2 Baudrate... ";
readBaud = device->settings->getBaudrateFor(icsneo::Network::NetID::LIN2);
if(readBaud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (readBaud) << "bit/s" << std::endl << std::endl;
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "Going online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// A real application would just check the result of icsneo_goOnline() rather than calling this
// This function is intended to be called later on if needed
std::cout << "Checking online status... ";
ret = device->isOnline();
if(!ret) {
std::cout << "FAIL\n" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Network::Type::LIN == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::LINMessage>(message);
std::cout << msg->network << " RX frame | ID: 0x" << std::hex << static_cast<int>(msg->ID) << " | ";
std::cout << "Protected ID: 0x" << static_cast<int>(msg->protectedID) << "\n" << "Data: ";
for(uint8_t& each : msg->data) {
std::cout << "0x" << static_cast<int>(each) << " ";
}
std::cout << "\nChecksum type: " << (msg->isEnhancedChecksum ? "Enhanced" : "Classic");
std::cout << "\nChecksum: 0x" << static_cast<int>(msg->checksum) << "\n";
std::cout << "Is checksum valid: " << ((!msg->errFlags.ErrChecksumMatch) ? "yes" : "no") << "\n\n";
}
}
}));
// We can transmit messages
std::cout << "Transmitting a LIN responder data frame... ";
auto lin_r = std::make_shared<icsneo::LINMessage>();
lin_r->network = icsneo::Network::NetID::LIN2;
lin_r->ID = 0x11;
lin_r->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
lin_r->data = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44};
ret = device->transmit(lin_r); // This will return false if the device does not support LIN
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Transmitting a LIN commander header... ";
auto lin_c = std::make_shared<icsneo::LINMessage>();
lin_c->network = icsneo::Network::NetID::LIN;
lin_c->ID = 0x11;
lin_c->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
ret = device->transmit(lin_c);
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Transmitting a LIN commander frame with responder data... ";
auto lin_d = std::make_shared<icsneo::LINMessage>();
lin_d->network = icsneo::Network::NetID::LIN;
lin_d->ID = 0x22;
lin_d->isEnhancedChecksum = true;
lin_d->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
lin_d->data = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd};
ret = device->transmit(lin_d);
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Go offline, stop sending and receiving traffic
device->removeMessageCallback(handler);
std::cout << "Going offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Disconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
}
return 0;
}
-2
View File
@@ -1,2 +0,0 @@
add_executable(libicsneocpp-livedata src/LiveDataExample.cpp)
target_link_libraries(libicsneocpp-livedata icsneocpp)
@@ -1,91 +0,0 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/livedata.h"
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
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;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto& device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl;
// Create a subscription message for the GPS signals
std::cout << "\tSending a live data subscribe command... ";
auto msg = std::make_shared<icsneo::LiveDataCommandMessage>();
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE);
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LONGITUDE);
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY);
msg->cmd = icsneo::LiveDataCommand::SUBSCRIBE;
msg->handle = icsneo::LiveDataUtil::getNewHandle();
msg->updatePeriod = std::chrono::milliseconds(100);
msg->expirationTime = std::chrono::milliseconds(0);
// Transmit the subscription message
ret = device->subscribeLiveData(msg);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// Register a handler that uses the data after it arrives every ~100ms
std::cout << "\tStreaming messages for 3 seconds... " << std::endl << std::endl;
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::LiveData);
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>(filter, [&msg](std::shared_ptr<icsneo::Message> message) {
auto ldMsg = std::dynamic_pointer_cast<icsneo::LiveDataMessage>(message);
switch(ldMsg->cmd) {
case icsneo::LiveDataCommand::STATUS: {
auto msg2 = std::dynamic_pointer_cast<icsneo::LiveDataStatusMessage>(message);
std::cout << "[Handle] " << ldMsg->handle << std::endl;
std::cout << "[Requested Command] " << msg2->requestedCommand << std::endl;
std::cout << "[Status] " << msg2->status << std::endl << std::endl;
break;
}
case icsneo::LiveDataCommand::RESPONSE: {
auto valueMsg = std::dynamic_pointer_cast<icsneo::LiveDataValueMessage>(message);
if((valueMsg->handle == msg->handle) && (valueMsg->values.size() == msg->args.size()))
{
std::cout << "[Handle] " << msg->handle << std::endl;
std::cout << "[Values] " << valueMsg->numArgs << std::endl;
for(uint32_t i = 0; i < valueMsg->numArgs; ++i) {
std::cout << "[" << msg->args[i]->valueType << "] ";
auto scaledValue = icsneo::LiveDataUtil::liveDataValueToDouble(*valueMsg->values[i]);
std::cout << scaledValue << std::endl;
}
std::cout << std::endl;
}
break;
}
default: // Ignoring other commands
break;
}
}));
// Run handler for three seconds to observe the signal data
std::this_thread::sleep_for(std::chrono::seconds(3));
// Unsubscribe from the GPS signals and run handler for one more second
// Unsubscription only requires a valid in-use handle, in this case from our previous subscription
ret = device->unsubscribeLiveData(msg->handle);
// The handler should no longer print values
std::this_thread::sleep_for(std::chrono::seconds(1));
device->removeMessageCallback(handler);
std::cout << "\tDisconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
}
return 0;
}
-2
View File
@@ -1,2 +0,0 @@
add_executable(libicsneocpp-mdio src/MDIOExample.cpp)
target_link_libraries(libicsneocpp-mdio icsneocpp)
-221
View File
@@ -1,221 +0,0 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/mdiomessage.h"
/* Note: This example requires MDIO 1 channels to be connected on the device, and uses RADGalaxy or RADStar2 */
char getCharInput(std::vector<char> allowed)
{
bool found = false;
std::string input;
while (!found)
{
std::cin >> input;
if (input.length() == 1)
{
for (char compare : allowed)
{
if (compare == input.c_str()[0])
{
found = true;
break;
}
}
}
if (!found)
{
std::cout << "Input did not match expected options. Please try again." << std::endl;
std::cout << "<X or x to quit>" << std::endl;
}
}
return input.c_str()[0];
}
int main()
{
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
// 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;
// List off the devices
for (auto &device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for (auto &device : devices)
{
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if (!ret)
{ // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl
<< std::endl;
continue;
}
std::cout << "OK" << std::endl;
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "\tGoing online... ";
ret = device->goOnline();
if (!ret)
{
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// A real application would just check the result of icsneo_goOnline() rather than calling this
// This function is intended to be called later on if needed
std::cout << "\tChecking online status... ";
ret = device->isOnline();
if (!ret)
{
std::cout << "FAIL\n"
<< std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message)
{
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Network::Type::MDIO == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::MDIOMessage>(message);
std::cout << msg->network << " " << ((msg->isTXMsg)? "TX" : "RX") << " frame\n";
std::cout << "Clause: " << ((msg->clause == icsneo::MDIOMessage::Clause::Clause22) ? "22" : "45") << "\n";
std::cout << "Direction: " << ((msg->direction == icsneo::MDIOMessage::Direction::Read) ? "Read" : "Write") << "\n";
std::cout << std::hex << "PHY Address: 0x" << static_cast<int>(msg->phyAddress) << "\n";
if(msg->clause == icsneo::MDIOMessage::Clause::Clause45)
std::cout << std::hex << "Dev Address: 0x" << static_cast<int>(msg->devAddress) << "\n";
std::cout << std::hex << "Reg Address: 0x" << static_cast<int>(msg->regAddress) << "\n";
std::cout << "Data: \n";
for(uint8_t& each : msg->data) {
std::cout << std::hex << "0x" << static_cast<int>(each) << " ";
}
std::cout << "\n";
}
} }));
/*
* BCM89810 on RADGalaxy/RADGigastar
*/
// We can transmit messages to read the PHY ID of BCM89810 PHY
std::cout << "\tTransmitting a MDIO request to read ID on BCM89810...\n";
auto mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x02u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to write to arbitrary register
std::cout << "\tTransmitting a MDIO request to write register on BCM89810...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x1Bu;
mdio_r->data = {0xAA, 0xAF};
mdio_r->direction = icsneo::MDIOMessage::Direction::Write;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to read back to arbitrary register
std::cout << "\tTransmitting a MDIO request to read register on BCM89810...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x1Bu;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
/*
* 88Q2112 on RADGigastar, RADSupermoon, RADMoon2
*/
// We can transmit messages to read the PHY ID of BCM89810 PHY
std::cout << "\tTransmitting a MDIO request to read ID on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0002u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to write to arbitrary register
std::cout << "\tTransmitting a MDIO request to write register on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0902u;
mdio_r->data = {0xA3, 0x02};
mdio_r->direction = icsneo::MDIOMessage::Direction::Write;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to read back to arbitrary register
std::cout << "\tTransmitting a MDIO request to read register on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0902u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "<X or x to quit>\n\n";
// Go offline, stop sending and receiving traffic
auto shutdown = [&]()
{
device->removeMessageCallback(handler);
std::cout << "\tGoing offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tDisconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
};
while (true)
{
char input = getCharInput(std::vector<char>{'X', 'x'});
switch (input)
{
case 'X':
case 'x':
shutdown();
printf("Exiting program\n");
return 0;
default:
break;
}
}
}
return 0;
}
+32 -1
View File
@@ -1,2 +1,33 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-simple-example VERSION 0.2.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
target_link_libraries(libicsneocpp-simple-example icsneocpp)
target_link_libraries(libicsneocpp-simple-example icsneocpp)
add_executable(libicsneocpp-simple-rx src/SimpleRx.cpp)
add_executable(libicsneocpp-simple-tx src/SimpleTx.cpp)
target_link_libraries(libicsneocpp-simple-rx icsneocpp)
target_link_libraries(libicsneocpp-simple-tx icsneocpp)
add_executable(libicsneocpp-simple-events src/SimpleEvents.cpp)
target_link_libraries(libicsneocpp-simple-events icsneocpp)
+57
View File
@@ -0,0 +1,57 @@
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "icsneo/icsneocpp.h"
int main(int argc, char** argv) {
std::vector<std::string> args(argv, argv + argc);
if(args.size() != 2) {
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
return -1;
}
const auto& deviceSerial = args[1];
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
for(const auto& dev : icsneo::FindAllDevices()) {
if(serial == dev->getSerial())
return dev;
}
return nullptr;
};
std::cout << "Finding device... " << std::flush;
const auto device = findDevice(deviceSerial);
if(!device) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Opening device... " << std::flush;
if(!device->open()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Going online... " << std::flush;
if(!device->goOnline()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Getting events from server..." << std::endl;
icsneo::EventFilter ef = {};
auto events = icsneo::GetEvents(ef, 0UL);
if(events.size() == 0)
std::cout << "Event return is empty :(" << std::endl;
for(auto& event: events)
std::cout << event.describe() << std::endl;
return 0;
}
+8 -28
View File
@@ -5,14 +5,16 @@
#include "icsneo/icsneocpp.h"
#ifdef _MSC_VER
#pragma warning(disable : 4996) // STL time functions
#endif
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
// Register an event callback so we can see any errors that come in
icsneo::EventManager::GetInstance().downgradeErrorsOnCurrentThread();
icsneo::EventManager::GetInstance().addEventCallback(icsneo::EventCallback([](std::shared_ptr<icsneo::APIEvent> evt) {
std::cerr << evt->describe() << std::endl;
}));
std::cout<< "Supported devices:" << std::endl;
for(auto& dev : icsneo::GetSupportedDevices())
std::cout << '\t' << dev.getGenericProductName() << std::endl;
@@ -100,31 +102,9 @@ int main() {
std::cout << "\tSetting settings permanently... ";
ret = device->settings->apply();
std::cout << (ret ? "OK\n\n" : "FAIL\n\n");
const auto getRTC = [&]() {
std::cout << "\tGetting RTC... ";
const auto rtc = device->getRTC();
if(!rtc) {
std::cout << "FAIL" << std::endl;
return;
}
const auto time = std::chrono::system_clock::to_time_t(*rtc);
const auto timeInfo = std::gmtime(&time);
std::cout << "OK, " << std::put_time(timeInfo, "%Y-%m-%d %H:%M:%S") << std::endl;
};
// Set the real time clock on the device using the system clock
// First, let's see if we can get the time from the device (if it has an RTC)
getRTC();
// Now, set the time using the system's clock so that we can check it again to ensure it's set
std::cout << "\tSetting RTC to system_clock::now()... ";
std::cout << (device->setRTC(std::chrono::system_clock::now()) ? "OK" : "FAIL") << std::endl;
// Get the time again after setting
getRTC();
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "\n\tGoing online... ";
std::cout << "\tGoing online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
@@ -153,7 +133,7 @@ int main() {
// Keep in mind that 20k messages comes quickly at high bus loads!
// We can transmit messages
std::cout << "\n\tTransmitting an extended CAN FD frame... ";
std::cout << "\tTransmitting an extended CAN FD frame... ";
auto txMessage5 = std::make_shared<icsneo::CANMessage>();
txMessage5->network = icsneo::Network::NetID::HSCAN;
txMessage5->arbid = 0x1C5001C5;
+66
View File
@@ -0,0 +1,66 @@
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "icsneo/icsneocpp.h"
int main(int argc, char** argv) {
std::vector<std::string> args(argv, argv + argc);
if(args.size() != 2) {
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
return -1;
}
const auto& deviceSerial = args[1];
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
for(const auto& dev : icsneo::FindAllDevices()) {
if(serial == dev->getSerial())
return dev;
}
return nullptr;
};
std::cout << "Finding device... " << std::flush;
const auto device = findDevice(deviceSerial);
if(!device) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Opening device... " << std::flush;
if(!device->open()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Going online... " << std::flush;
if(!device->goOnline()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Streaming CAN messages in, enter anything to stop..." << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
if(message->type == icsneo::Message::Type::Frame) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(frame->network.getType() == icsneo::Network::Type::CAN) {
auto canMessage = std::static_pointer_cast<icsneo::CANMessage>(message);
std::cout << '\r';
for(auto& databyte : canMessage->data)
std::cout << std::hex << std::setw(2) << (uint32_t)databyte << ' ';
std::cout << std::flush;
}
}
}));
std::cin.ignore();
device->removeMessageCallback(handler);
return 0;
}
+75
View File
@@ -0,0 +1,75 @@
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <memory>
#include "icsneo/icsneocpp.h"
int main(int argc, char** argv) {
std::vector<std::string> args(argv, argv + argc);
if (args.size() != 2) {
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
return -1;
}
const auto& deviceSerial = args[1];
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
for (const auto& dev : icsneo::FindAllDevices()) {
if (serial == dev->getSerial())
return dev;
}
return nullptr;
};
std::cout << "Finding device... " << std::flush;
const auto device = findDevice(deviceSerial);
if (!device) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Opening device... " << std::flush;
if (!device->open()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Going online... " << std::flush;
if (!device->goOnline()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::atomic<bool> stop = false;
std::thread thread([&] {
std::cin.ignore();
std::cout << "Stopping..." << std::endl;
stop = true;
});
auto txMessage = std::make_shared<icsneo::CANMessage>();
txMessage->network = icsneo::Network::NetID::HSCAN;
txMessage->arbid = 0x1C5001C5;
txMessage->data.insert(txMessage->data.begin(), sizeof(size_t), 0);
txMessage->isExtended = true;
txMessage->isCANFD = true;
std::cout << "Streaming CAN messages out, enter anything to stop..." << std::endl;
size_t& value = *(size_t*)txMessage->data.data();
while (!stop) {
device->transmit(txMessage);
value++;
}
thread.join();
return 0;
}
-2
View File
@@ -1,2 +0,0 @@
add_executable(libicsneocpp-vsa-example src/VSAExample.cpp)
target_link_libraries(libicsneocpp-vsa-example icsneocpp)

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