mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-23 17:29:46 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f16ee630ee |
+1
-7
@@ -8,10 +8,4 @@ CMakeSettings.json
|
||||
third-party/concurrentqueue/benchmarks
|
||||
third-party/concurrentqueue/tests
|
||||
*.bak
|
||||
.vs
|
||||
.cache
|
||||
*.wav
|
||||
*.orig
|
||||
examples/csharp/bin
|
||||
examples/csharp/obj
|
||||
test/system
|
||||
.vs
|
||||
-374
@@ -1,374 +0,0 @@
|
||||
variables:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
|
||||
stages:
|
||||
- build
|
||||
- unit_test
|
||||
- hardware_test
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Windows
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
build windows/x64:
|
||||
stage: build
|
||||
script:
|
||||
- CMD.EXE /C ci\build-windows64.bat
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- build
|
||||
expire_in: 3 days
|
||||
tags:
|
||||
- icsneo-windows
|
||||
|
||||
unit_test windows/x64:
|
||||
stage: unit_test
|
||||
script:
|
||||
- build\libicsneo-unit-tests.exe
|
||||
dependencies:
|
||||
- build windows/x64
|
||||
needs:
|
||||
- build windows/x64
|
||||
tags:
|
||||
- icsneo-windows
|
||||
timeout: 5m
|
||||
|
||||
build windows/x86:
|
||||
stage: build
|
||||
script:
|
||||
- CMD.EXE /C ci\build-windows32.bat
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- build
|
||||
expire_in: 3 days
|
||||
tags:
|
||||
- icsneo-windows
|
||||
|
||||
unit_test windows/x86:
|
||||
stage: unit_test
|
||||
script:
|
||||
- build\libicsneo-unit-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
|
||||
|
||||
hardware_test system-test-fedora38-red2:
|
||||
<<: *hw_test
|
||||
variables:
|
||||
GUEST_OS_TAG: fedora38
|
||||
DEVICE_PORT: ETH_A
|
||||
needs:
|
||||
- job: build linux/fedora/38/amd64/clang
|
||||
artifacts: true
|
||||
|
||||
hardware_test system-test-fedora38-vcan42:
|
||||
<<: *hw_test
|
||||
variables:
|
||||
GUEST_OS_TAG: fedora38
|
||||
DEVICE_PORT: USB_D
|
||||
needs:
|
||||
- job: build linux/fedora/38/amd64/clang
|
||||
artifacts: true
|
||||
|
||||
hardware_test system-test-ubuntu2204-red2:
|
||||
<<: *hw_test
|
||||
variables:
|
||||
GUEST_OS_TAG: ubuntu22.04
|
||||
DEVICE_PORT: ETH_A
|
||||
needs:
|
||||
- job: build linux/ubuntu/2204/amd64/clang
|
||||
artifacts: true
|
||||
|
||||
hardware_test system-test-ubuntu2204-vcan42:
|
||||
<<: *hw_test
|
||||
variables:
|
||||
GUEST_OS_TAG: ubuntu22.04
|
||||
DEVICE_PORT: USB_D
|
||||
needs:
|
||||
- job: build linux/ubuntu/2204/amd64/clang
|
||||
artifacts: true
|
||||
|
||||
hardware_test system-test-win10-red2:
|
||||
<<: *hw_test
|
||||
variables:
|
||||
GUEST_OS_TAG: win10
|
||||
DEVICE_PORT: ETH_A
|
||||
needs:
|
||||
- job: build windows/x64
|
||||
artifacts: true
|
||||
|
||||
hardware_test system-test-win10-vcan42:
|
||||
<<: *hw_test
|
||||
variables:
|
||||
GUEST_OS_TAG: win10
|
||||
DEVICE_PORT: USB_D
|
||||
needs:
|
||||
- job: build windows/x64
|
||||
artifacts: true
|
||||
+1
-1
@@ -4,7 +4,7 @@ KERNEL=="ttyUSB?", ATTRS{idVendor}=="093c", GROUP="users", MODE="0666"
|
||||
KERNEL=="ttyACM?", ATTRS{idVendor}=="093c", GROUP="users", MODE="0666"
|
||||
|
||||
# neoVI ION/PLASMA PIDs are not in the latest ftdi_sio driver so lets make a
|
||||
# rule to add it when we see a new unclaimed device.
|
||||
# rule to add it when we see a new unclaimed device.
|
||||
# PLASMA = 0x0801, ION = 0x0901
|
||||
ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_interface", \
|
||||
ATTRS{idVendor}=="093c", ATTRS{idProduct}=="0801", \
|
||||
|
||||
+54
-349
@@ -1,35 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(libicsneo VERSION 0.3.0)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneo VERSION 0.2.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")
|
||||
|
||||
# Device Drivers
|
||||
# You almost certainly don't want firmio for your build,
|
||||
# it is only relevant for communication between Linux and
|
||||
# CoreMini from the onboard processor of the device.
|
||||
option(LIBICSNEO_ENABLE_FIRMIO "Enable communication between Linux and CoreMini within the same device" OFF)
|
||||
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()
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@@ -43,15 +21,10 @@ if(MSVC)
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
# http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html
|
||||
# Still supported until a suitable replacement is standardized
|
||||
add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
|
||||
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)
|
||||
@@ -66,7 +39,7 @@ if(LIBICSNEO_BUILD_DOCS)
|
||||
if(DOXYGEN_FOUND)
|
||||
message("Will build Doxygen based documentation")
|
||||
|
||||
add_custom_target(libicsneo_doxygen
|
||||
add_custom_target(libicsneo_doxygen ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
@@ -94,7 +67,7 @@ if(LIBICSNEO_BUILD_DOCS)
|
||||
"${ICSNEO_DOCS_DIR}/conf.py"
|
||||
@ONLY)
|
||||
|
||||
add_custom_target(libicsneo_sphinx ALL
|
||||
add_custom_target(libicsneo_sphinx
|
||||
${SPHINX_EXECUTABLE}
|
||||
-q -b html
|
||||
-c "${ICSNEO_DOCS_DIR}"
|
||||
@@ -107,201 +80,36 @@ if(LIBICSNEO_BUILD_DOCS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if(WIN32)
|
||||
set(PLATFORM_SRC
|
||||
platform/windows/registry.cpp
|
||||
)
|
||||
|
||||
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/windows/pcap.cpp
|
||||
platform/windows/internal/pcapdll.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_ENABLE_CDCACM OR LIBICSNEO_ENABLE_FTDI)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/windows/vcp.cpp
|
||||
)
|
||||
endif()
|
||||
else() # Darwin or Linux
|
||||
set(PLATFORM_SRC)
|
||||
|
||||
if(LIBICSNEO_ENABLE_FIRMIO)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/firmio.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/pcap.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_ENABLE_FTDI)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/ftdi.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_ENABLE_CDCACM)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/cdcacm.cpp
|
||||
)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/darwin/cdcacmdarwin.cpp
|
||||
)
|
||||
else() # Linux or other
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/linux/cdcacmlinux.cpp
|
||||
)
|
||||
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
message(WARNING
|
||||
"There is no CDCACM platform port defined for ${CMAKE_SYSTEM_NAME}!\n"
|
||||
"The Linux platform code will be used, as it will generally allow building, but some devices may not enumerate properly."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
file(GLOB PLATFORM_SRC_EXTERNAL ${CMAKE_CURRENT_SOURCE_DIR}/platform/windows/*.cpp)
|
||||
file(GLOB PLATFORM_SRC_INTERNAL ${CMAKE_CURRENT_SOURCE_DIR}/platform/windows/internal/*.cpp)
|
||||
set(PLATFORM_SRC ${PLATFORM_SRC_EXTERNAL} ${PLATFORM_SRC_INTERNAL})
|
||||
else()
|
||||
file(GLOB PLATFORM_SRC ${CMAKE_CURRENT_SOURCE_DIR}/platform/posix/*.cpp)
|
||||
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
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
# Extensions
|
||||
set(LIBICSNEO_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
foreach(EXT_PATH ${LIBICSNEO_EXTENSION_DIRS})
|
||||
get_filename_component(EXT_DIR ${EXT_PATH} NAME)
|
||||
message("Adding extension " ${EXT_DIR})
|
||||
add_subdirectory(${EXT_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${EXT_DIR})
|
||||
endforeach()
|
||||
|
||||
set(SRC_FILES
|
||||
set(COMMON_SRC
|
||||
communication/message/flexray/control/flexraycontrolmessage.cpp
|
||||
communication/message/callback/streamoutput/a2bwavoutput.cpp
|
||||
communication/message/a2bmessage.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
|
||||
communication/packet/ethernetpacket.cpp
|
||||
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
|
||||
communication/packetizer.cpp
|
||||
communication/multichannelcommunication.cpp
|
||||
communication/communication.cpp
|
||||
communication/driver.cpp
|
||||
communication/livedata.cpp
|
||||
communication/ringbuffer.cpp
|
||||
communication/icommunication.cpp
|
||||
device/extensions/flexray/extension.cpp
|
||||
device/extensions/flexray/controller.cpp
|
||||
device/idevicesettings.cpp
|
||||
device/devicefinder.cpp
|
||||
device/device.cpp
|
||||
device/neodevice.cpp
|
||||
disk/diskreaddriver.cpp
|
||||
disk/diskwritedriver.cpp
|
||||
disk/nulldiskdriver.cpp
|
||||
disk/neomemorydiskdriver.cpp
|
||||
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}
|
||||
)
|
||||
|
||||
set(SRC_FILES ${COMMON_SRC} ${PLATFORM_SRC})
|
||||
|
||||
# Generate build info header
|
||||
execute_process(
|
||||
COMMAND git rev-parse --abbrev-ref HEAD
|
||||
@@ -329,18 +137,6 @@ endif()
|
||||
|
||||
configure_file(api/icsneocpp/buildinfo.h.template ${CMAKE_CURRENT_BINARY_DIR}/generated/buildinfo.h)
|
||||
configure_file(api/icsneoc/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
||||
|
||||
foreach(EXTINC ${LIBICSNEO_EXTENSION_INCLUDES})
|
||||
message("Including " ${EXTINC})
|
||||
list(APPEND LIBICSNEO_EXT_CODE_INCS_LIST "#include \"${EXTINC}\"")
|
||||
endforeach()
|
||||
list(JOIN LIBICSNEO_EXT_CODE_INCS_LIST "\n" LIBICSNEO_EXT_CODE_INCS)
|
||||
foreach(EXTCLASS ${LIBICSNEO_EXTENSION_CLASSES})
|
||||
list(APPEND LIBICSNEO_EXT_CODE_LIST "device->addExtension(std::make_shared<${EXTCLASS}>(*device))\;")
|
||||
endforeach()
|
||||
list(JOIN LIBICSNEO_EXT_CODE_LIST "\n\t" LIBICSNEO_EXT_CODE)
|
||||
configure_file(include/icsneo/device/extensions/builtin.h.template ${CMAKE_CURRENT_BINARY_DIR}/generated/extensions/builtin.h)
|
||||
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_library(icsneocpp
|
||||
@@ -350,84 +146,34 @@ add_library(icsneocpp
|
||||
api/icsneocpp/version.cpp
|
||||
${SRC_FILES}
|
||||
)
|
||||
message("Include paths " ${LIBICSNEO_EXTENSION_INCLUDE_PATHS})
|
||||
target_include_directories(icsneocpp
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:>
|
||||
PRIVATE
|
||||
${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)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FIRMIO)
|
||||
endif()
|
||||
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_RAW_ETHERNET)
|
||||
endif()
|
||||
if(LIBICSNEO_ENABLE_CDCACM)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_CDCACM)
|
||||
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)
|
||||
endif()
|
||||
if(LIBICSNEO_ENABLE_TCP)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_TCP)
|
||||
if(WIN32)
|
||||
target_link_libraries(icsneocpp PRIVATE ws2_32 iphlpapi)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# fatfs
|
||||
add_subdirectory(third-party/fatfs)
|
||||
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(icsneocpp PRIVATE fatfs)
|
||||
|
||||
# libftdi
|
||||
if(LIBICSNEO_ENABLE_FTDI)
|
||||
if(NOT WIN32)
|
||||
target_include_directories(icsneocpp PUBLIC third-party/libftdi/src)
|
||||
set(LIBFTDI_DOCUMENTATION OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_BUILD_TESTS OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_INSTALL OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_PYTHON_BINDINGS OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF CACHE INTERNAL "")
|
||||
set(FTDIPP OFF CACHE INTERNAL "")
|
||||
set(FTDI_EEPROM OFF CACHE INTERNAL "")
|
||||
add_subdirectory(third-party/libftdi)
|
||||
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
if(NOT WIN32)
|
||||
target_include_directories(icsneocpp PUBLIC third-party/libftdi/src)
|
||||
set(LIBFTDI_DOCUMENTATION OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_BUILD_TESTS OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_INSTALL OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_PYTHON_BINDINGS OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF CACHE INTERNAL "")
|
||||
set(FTDIPP OFF CACHE INTERNAL "")
|
||||
set(FTDI_EEPROM OFF CACHE INTERNAL "")
|
||||
add_subdirectory(third-party/libftdi)
|
||||
endif(NOT WIN32)
|
||||
|
||||
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})
|
||||
endif(NOT WIN32)
|
||||
endif(LIBICSNEO_ENABLE_FTDI)
|
||||
|
||||
# pcap
|
||||
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
if(WIN32)
|
||||
if(LIBICSNEO_NPCAP_INCLUDE_DIR STREQUAL "")
|
||||
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
|
||||
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
|
||||
else()
|
||||
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
|
||||
add_definitions(-DNPCAP -DWIN32_LEAN_AND_MEAN)
|
||||
endif()
|
||||
else()
|
||||
find_package(PCAP REQUIRED)
|
||||
target_include_directories(icsneocpp PUBLIC ${PCAP_INCLUDE_DIR})
|
||||
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
|
||||
endif(WIN32)
|
||||
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
# winpcap
|
||||
if(WIN32)
|
||||
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
|
||||
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
|
||||
endif(WIN32)
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOC)
|
||||
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
||||
@@ -453,7 +199,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)
|
||||
@@ -461,7 +206,6 @@ if(LIBICSNEO_BUILD_ICSNEOLEGACY)
|
||||
api/icsneolegacy/icsneolegacy.cpp
|
||||
api/icsneolegacy/icsneolegacyextra.cpp
|
||||
api/icsneoc/icsneoc.cpp
|
||||
platform/windows/icsneolegacy.def
|
||||
)
|
||||
target_include_directories(icsneolegacy
|
||||
PUBLIC
|
||||
@@ -474,77 +218,38 @@ 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)
|
||||
# libftdi
|
||||
if(NOT WIN32)
|
||||
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})
|
||||
find_package(PCAP REQUIRED)
|
||||
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
|
||||
endif()
|
||||
|
||||
# googletest
|
||||
if(LIBICSNEO_BUILD_UNIT_TESTS)
|
||||
if(LIBICSNEO_BUILD_TESTS)
|
||||
if(WIN32)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET gtest)
|
||||
add_subdirectory(third-party/googletest-master)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(third-party/googletest-master)
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 2.8.11)
|
||||
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
|
||||
)
|
||||
|
||||
target_link_libraries(libicsneo-unit-tests gtest gtest_main)
|
||||
target_link_libraries(libicsneo-unit-tests icsneocpp)
|
||||
|
||||
target_include_directories(libicsneo-unit-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||
|
||||
|
||||
add_executable(runTests test/main.cpp test/eventmanagertest.cpp)
|
||||
|
||||
target_link_libraries(runTests gtest gtest_main)
|
||||
target_link_libraries(runTests icsneocpp)
|
||||
|
||||
target_include_directories(runTests 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 testSuite COMMAND runTests)
|
||||
endif()
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
+1
-22
@@ -14,20 +14,6 @@
|
||||
- RADStar 2
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- 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
|
||||
@@ -50,11 +36,4 @@
|
||||
- neoVI PLASMA
|
||||
- CAN works
|
||||
- neoVI ION
|
||||
- CAN works
|
||||
- RADA2B
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- RADMoon3
|
||||
- RADComet3
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- CAN works
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2018-2024 Intrepid Control Systems, Inc.
|
||||
Copyright 2019-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:
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ The C++ API is designed to be modern and easy to use. All library functions and
|
||||
|
||||
Any time you get bus traffic from the API, you will receive it as an `std::shared_ptr<icsneo::Message>`. The message will be valid as long as the `shared_ptr` stays in scope. Checking the type of the message allows you to cast it accordingly and access extra data for certain protocols. For instance, casting an `icsneo::Message` to an `icsneo::CANMessage` allows you to access the arbitration ID.
|
||||
|
||||
A barebones example is provided. For a more complete example, check the included `examples`.
|
||||
A barebones example is provided. For a more complete example, check [intrepidcs/libicsneo-examples](https://github.com/intrepidcs/libicsneo-examples).
|
||||
``` c++
|
||||
std::vector<std::shared_ptr<icsneo::Device>> devices = icsneo::FindAllDevices();
|
||||
std::cout << devices.size() << " found!" << std::endl;
|
||||
@@ -71,7 +71,7 @@ The C API is designed to be a robust and fault tolerant interface which allows e
|
||||
|
||||
Messages are passed in the form of `neomessage_t` structures when calling `icsneo_getMessages()`. These structures contain a `uint8_t*` to the payload data, and this pointer will be valid until the next call to `icsneo_getMessages()` or the device is closed.
|
||||
|
||||
A barebones example is provided. For a more complete example, check the included `examples`.
|
||||
A barebones example is provided. For a more complete example, check [intrepidcs/libicsneo-examples](https://github.com/intrepidcs/libicsneo-examples).
|
||||
``` c
|
||||
size_t deviceCount = 10; // Pre-set to the size of your buffer before the icsneo_findAllDevices() call
|
||||
neodevice_t devices[10];
|
||||
@@ -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.
|
||||
Building will require Microsoft Visual Studio 2017+ 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
|
||||
- CMake 3.12 or above
|
||||
- GCC 7 or above
|
||||
The dependencies are as follows
|
||||
- CMake 3.2 or above
|
||||
- GCC 4.7 or above, 4.8+ recommended
|
||||
- `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/
|
||||
```
|
||||
|
||||
+622
-759
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
+4
-230
@@ -69,18 +69,10 @@ static constexpr const char* DEVICE_CURRENTLY_POLLING = "The device is currently
|
||||
static constexpr const char* DEVICE_NOT_CURRENTLY_POLLING = "The device is not currently polling for messages.";
|
||||
static constexpr const char* UNSUPPORTED_TX_NETWORK = "Message network is not a supported TX network.";
|
||||
static constexpr const char* MESSAGE_MAX_LENGTH_EXCEEDED = "The message was too long.";
|
||||
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!";
|
||||
static constexpr const char* NO_SERIAL_NUMBER_FW_12V = "Communication could not be established with the device. Perhaps it is not powered with 12 volts?";
|
||||
static constexpr const char* NO_SERIAL_NUMBER_FW = "Communication could not be established with the device. Perhaps it is not powered?";
|
||||
static constexpr const char* NO_SERIAL_NUMBER_12V = "Communication could not be established with the device. Perhaps it is not powered with 12 volts or requires a firmware update using Vehicle Spy.";
|
||||
static constexpr const char* NO_SERIAL_NUMBER = "Communication could not be established with the device. Perhaps it is not powered or requires a firmware update using Vehicle Spy.";
|
||||
static constexpr const char* NO_SERIAL_NUMBER = "Communication could not be established with the device. Perhaps it is not powered with 12 volts?";
|
||||
static constexpr const char* INCORRECT_SERIAL_NUMBER = "The device did not return the expected serial number!";
|
||||
static constexpr const char* SETTINGS_READ = "The device settings could not be read.";
|
||||
static constexpr const char* SETTINGS_VERSION = "The settings version is incorrect, please update your firmware with neoVI Explorer.";
|
||||
@@ -101,25 +93,6 @@ static constexpr const char* NO_DEVICE_RESPONSE = "Expected a response from the
|
||||
static constexpr const char* MESSAGE_FORMATTING = "The message was not properly formed.";
|
||||
static constexpr const char* CANFD_NOT_SUPPORTED = "This device does not support CANFD.";
|
||||
static constexpr const char* RTR_NOT_SUPPORTED = "RTR is not supported with CANFD.";
|
||||
static constexpr const char* DEVICE_DISCONNECTED = "The device was disconnected.";
|
||||
static constexpr const char* ONLINE_NOT_SUPPORTED = "This device does not support going online.";
|
||||
static constexpr const char* TERMINATION_NOT_SUPPORTED_DEVICE = "This device does not support software selectable termination.";
|
||||
static constexpr const char* TERMINATION_NOT_SUPPORTED_NETWORK = "This network does not support software selectable termination on this device.";
|
||||
static constexpr const char* ANOTHER_IN_TERMINATION_GROUP_ENABLED = "A mutually exclusive network already has termination enabled.";
|
||||
static constexpr const char* ETH_PHY_REGISTER_CONTROL_NOT_AVAILABLE = "Ethernet PHY register control is not available for this device.";
|
||||
static constexpr const char* DISK_NOT_SUPPORTED = "This device does not support accessing the specified disk.";
|
||||
static constexpr const char* EOF_REACHED = "The requested length exceeds the available data from this disk.";
|
||||
static constexpr const char* SETTINGS_DEFAULTS_USED = "The device settings could not be loaded, the default settings have been applied.";
|
||||
static constexpr const char* ATOMIC_OPERATION_RETRIED = "An operation failed to be atomically completed, but will be retried.";
|
||||
static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An ideally-atomic operation was completed nonatomically.";
|
||||
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.";
|
||||
@@ -128,59 +101,9 @@ static constexpr const char* DRIVER_FAILED_TO_OPEN = "The device driver encounte
|
||||
static constexpr const char* DRIVER_FAILED_TO_CLOSE = "The device driver encountered a low-level error while closing the device.";
|
||||
static constexpr const char* PACKET_CHECKSUM_ERROR = "There was a checksum error while decoding a packet. The packet was dropped.";
|
||||
static constexpr const char* TRANSMIT_BUFFER_FULL = "The transmit buffer is full and the device is set to non-blocking.";
|
||||
static constexpr const char* DEVICE_IN_USE = "The device is currently in use by another program.";
|
||||
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* PACKET_DECODING = "The packet could not be decoded.";
|
||||
|
||||
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.";
|
||||
@@ -214,16 +137,6 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return UNSUPPORTED_TX_NETWORK;
|
||||
case Type::MessageMaxLengthExceeded:
|
||||
return MESSAGE_MAX_LENGTH_EXCEEDED;
|
||||
case Type::ValueNotYetPresent:
|
||||
return VALUE_NOT_YET_PRESENT;
|
||||
case Type::Timeout:
|
||||
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:
|
||||
@@ -270,50 +183,7 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return CANFD_NOT_SUPPORTED;
|
||||
case Type::RTRNotSupported:
|
||||
return RTR_NOT_SUPPORTED;
|
||||
case Type::DeviceDisconnected:
|
||||
return DEVICE_DISCONNECTED;
|
||||
case Type::OnlineNotSupported:
|
||||
return ONLINE_NOT_SUPPORTED;
|
||||
case Type::TerminationNotSupportedDevice:
|
||||
return TERMINATION_NOT_SUPPORTED_DEVICE;
|
||||
case Type::TerminationNotSupportedNetwork:
|
||||
return TERMINATION_NOT_SUPPORTED_NETWORK;
|
||||
case Type::AnotherInTerminationGroupEnabled:
|
||||
return ANOTHER_IN_TERMINATION_GROUP_ENABLED;
|
||||
case Type::NoSerialNumberFW:
|
||||
return NO_SERIAL_NUMBER_FW;
|
||||
case Type::NoSerialNumber12V:
|
||||
return NO_SERIAL_NUMBER_12V;
|
||||
case Type::NoSerialNumberFW12V:
|
||||
return NO_SERIAL_NUMBER_FW_12V;
|
||||
case Type::EthPhyRegisterControlNotAvailable:
|
||||
return ETH_PHY_REGISTER_CONTROL_NOT_AVAILABLE;
|
||||
case Type::DiskNotSupported:
|
||||
return DISK_NOT_SUPPORTED;
|
||||
case Type::EOFReached:
|
||||
return EOF_REACHED;
|
||||
case Type::SettingsDefaultsUsed:
|
||||
return SETTINGS_DEFAULTS_USED;
|
||||
case Type::AtomicOperationRetried:
|
||||
return ATOMIC_OPERATION_RETRIED;
|
||||
case Type::AtomicOperationCompletedNonatomically:
|
||||
return ATOMIC_OPERATION_COMPLETED_NONATOMICALLY;
|
||||
case Type::WiVIStackRefreshFailed:
|
||||
return WIVI_STACK_REFRESH_FAILED;
|
||||
case Type::WiVIUploadStackOverflow:
|
||||
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;
|
||||
@@ -327,109 +197,13 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return PACKET_CHECKSUM_ERROR;
|
||||
case Type::TransmitBufferFull:
|
||||
return TRANSMIT_BUFFER_FULL;
|
||||
case Type::DeviceInUse:
|
||||
return DEVICE_IN_USE;
|
||||
case Type::PCAPCouldNotStart:
|
||||
return PCAP_COULD_NOT_START;
|
||||
case Type::PCAPCouldNotFindDevices:
|
||||
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;
|
||||
|
||||
|
||||
// Other Errors
|
||||
case Type::TooManyEvents:
|
||||
return TOO_MANY_EVENTS;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -11,118 +8,6 @@ EventManager& EventManager::GetInstance() {
|
||||
return inst;
|
||||
}
|
||||
|
||||
void EventManager::downgradeErrorsOnCurrentThread() {
|
||||
if(destructing)
|
||||
return;
|
||||
std::lock_guard<std::mutex> lk(downgradedThreadsMutex);
|
||||
auto i = downgradedThreads.find(std::this_thread::get_id());
|
||||
if(i != downgradedThreads.end()) {
|
||||
i->second = true;
|
||||
} else {
|
||||
downgradedThreads.insert({std::this_thread::get_id(), true});
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::cancelErrorDowngradingOnCurrentThread() {
|
||||
if(destructing)
|
||||
return;
|
||||
std::lock_guard<std::mutex> lk(downgradedThreadsMutex);
|
||||
auto i = downgradedThreads.find(std::this_thread::get_id());
|
||||
if(i != downgradedThreads.end()) {
|
||||
i->second = false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
auto i = downgradedThreads.find(std::this_thread::get_id());
|
||||
if(i != downgradedThreads.end() && i->second) {
|
||||
event.downgradeFromError();
|
||||
{
|
||||
std::lock_guard<std::mutex> eventsLock(eventsMutex);
|
||||
addEventInternal(event);
|
||||
} // free the lock so that callbacks may modify events
|
||||
runCallbacks(event);
|
||||
} else {
|
||||
std::lock_guard<std::mutex> errorsLock(errorsMutex);
|
||||
lastUserErrors[std::this_thread::get_id()] = event;
|
||||
}
|
||||
} else {
|
||||
{
|
||||
std::lock_guard<std::mutex> eventsLock(eventsMutex);
|
||||
addEventInternal(event);
|
||||
} // free the lock so that callbacks may modify events
|
||||
runCallbacks(event);
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::addEventInternal(APIEvent event) {
|
||||
// Ensure the event list is at most exactly full (size of eventLimit - 1, leaving room for a potential APIEvent::TooManyEvents)
|
||||
// Removes any events of type TooManyEvents from the end before checking to avoid duplicates.
|
||||
enforceLimit();
|
||||
|
||||
// We are exactly full, either because the list was truncated or because we were simply full before
|
||||
if(events.size() == eventLimit - 1) {
|
||||
// If the event is worth adding
|
||||
if(event.getType() != APIEvent::Type::TooManyEvents) {
|
||||
discardOldest(1);
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
events.push_back(APIEvent(APIEvent::Type::TooManyEvents, APIEvent::Severity::EventWarning));
|
||||
} else {
|
||||
if (event.getType() != APIEvent::Type::TooManyEvents)
|
||||
events.push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
void EventManager::runCallbacks(APIEvent event) {
|
||||
std::lock_guard<std::mutex> lk(callbacksMutex);
|
||||
for(auto& i : callbacks)
|
||||
i.second.callIfMatch(std::make_shared<APIEvent>(event));
|
||||
}
|
||||
|
||||
void EventManager::setEventLimit(size_t newLimit) {
|
||||
std::lock_guard<std::mutex> eventLimitLock(eventLimitMutex);
|
||||
|
||||
if(newLimit == eventLimit)
|
||||
return;
|
||||
|
||||
if(newLimit < 10) {
|
||||
add(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
eventLimit = newLimit;
|
||||
|
||||
std::lock_guard<std::mutex> eventsLock(eventsMutex);
|
||||
if(enforceLimit())
|
||||
addEventInternal(APIEvent(APIEvent::Type::TooManyEvents, APIEvent::Severity::EventWarning));
|
||||
}
|
||||
|
||||
void EventManager::ResetInstance() {
|
||||
std::lock_guard<std::mutex> eventsLock(eventsMutex);
|
||||
std::lock_guard<std::mutex> errorsLock(errorsMutex);
|
||||
@@ -136,7 +21,7 @@ void EventManager::ResetInstance() {
|
||||
lastUserErrors.clear();
|
||||
downgradedThreads.clear();
|
||||
callbacks.clear();
|
||||
|
||||
|
||||
callbackID = 0;
|
||||
eventLimit = 10000;
|
||||
}
|
||||
@@ -170,7 +55,7 @@ bool EventManager::isDowngradingErrorsOnCurrentThread() const {
|
||||
|
||||
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;
|
||||
|
||||
@@ -213,7 +98,7 @@ void EventManager::discard(EventFilter filter) {
|
||||
});
|
||||
}
|
||||
|
||||
size_t EventManager::countInternal(EventFilter filter) const {
|
||||
size_t EventManager::count_internal(EventFilter filter) const {
|
||||
size_t ret = 0;
|
||||
for(auto& event : events)
|
||||
if(filter.match(event))
|
||||
@@ -232,7 +117,7 @@ bool EventManager::enforceLimit() {
|
||||
while(it != events.rend() && filter.match(*it)) {
|
||||
it = decltype(it){events.erase( std::next(it).base() )};
|
||||
}
|
||||
|
||||
|
||||
// We are not overflowing
|
||||
if(events.size() < eventLimit)
|
||||
return false;
|
||||
@@ -252,6 +137,6 @@ void EventManager::discardOldest(size_t count) {
|
||||
while(it != events.end()) {
|
||||
it = events.erase(it);
|
||||
if(--count == 0)
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
+47
-47
@@ -1,48 +1,48 @@
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::vector<std::shared_ptr<Device>> icsneo::FindAllDevices() {
|
||||
return DeviceFinder::FindAll();
|
||||
}
|
||||
|
||||
std::vector<DeviceType> icsneo::GetSupportedDevices() {
|
||||
return DeviceFinder::GetSupportedDevices();
|
||||
}
|
||||
|
||||
size_t icsneo::EventCount(EventFilter filter) {
|
||||
return EventManager::GetInstance().eventCount(filter);
|
||||
}
|
||||
|
||||
std::vector<APIEvent> icsneo::GetEvents(EventFilter filter, size_t max) {
|
||||
return EventManager::GetInstance().get(filter, max);
|
||||
}
|
||||
|
||||
std::vector<APIEvent> icsneo::GetEvents(size_t max, EventFilter filter) {
|
||||
return EventManager::GetInstance().get(max, filter);
|
||||
}
|
||||
|
||||
void icsneo::GetEvents(std::vector<APIEvent>& events, EventFilter filter, size_t max) {
|
||||
EventManager::GetInstance().get(events, filter, max);
|
||||
}
|
||||
|
||||
void icsneo::GetEvents(std::vector<APIEvent>& events, size_t max, EventFilter filter) {
|
||||
EventManager::GetInstance().get(events, max, filter);
|
||||
}
|
||||
|
||||
APIEvent icsneo::GetLastError() {
|
||||
return EventManager::GetInstance().getLastError();
|
||||
}
|
||||
|
||||
void icsneo::DiscardEvents(EventFilter filter) {
|
||||
EventManager::GetInstance().discard(filter);
|
||||
}
|
||||
|
||||
void icsneo::SetEventLimit(size_t newLimit) {
|
||||
EventManager::GetInstance().setEventLimit(newLimit);
|
||||
}
|
||||
|
||||
size_t icsneo::GetEventLimit() {
|
||||
return EventManager::GetInstance().getEventLimit();
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::vector<std::shared_ptr<Device>> icsneo::FindAllDevices() {
|
||||
return DeviceFinder::FindAll();
|
||||
}
|
||||
|
||||
std::vector<DeviceType> icsneo::GetSupportedDevices() {
|
||||
return DeviceFinder::GetSupportedDevices();
|
||||
}
|
||||
|
||||
size_t icsneo::EventCount(EventFilter filter) {
|
||||
return EventManager::GetInstance().eventCount(filter);
|
||||
}
|
||||
|
||||
std::vector<APIEvent> icsneo::GetEvents(EventFilter filter, size_t max) {
|
||||
return EventManager::GetInstance().get(filter, max);
|
||||
}
|
||||
|
||||
std::vector<APIEvent> icsneo::GetEvents(size_t max, EventFilter filter) {
|
||||
return EventManager::GetInstance().get(max, filter);
|
||||
}
|
||||
|
||||
void icsneo::GetEvents(std::vector<APIEvent>& events, EventFilter filter, size_t max) {
|
||||
EventManager::GetInstance().get(events, filter, max);
|
||||
}
|
||||
|
||||
void icsneo::GetEvents(std::vector<APIEvent>& events, size_t max, EventFilter filter) {
|
||||
EventManager::GetInstance().get(events, max, filter);
|
||||
}
|
||||
|
||||
APIEvent icsneo::GetLastError() {
|
||||
return EventManager::GetInstance().getLastError();
|
||||
}
|
||||
|
||||
void icsneo::DiscardEvents(EventFilter filter) {
|
||||
EventManager::GetInstance().discard(filter);
|
||||
}
|
||||
|
||||
void icsneo::SetEventLimit(size_t newLimit) {
|
||||
EventManager::GetInstance().setEventLimit(newLimit);
|
||||
}
|
||||
|
||||
size_t icsneo::GetEventLimit() {
|
||||
return EventManager::GetInstance().getEventLimit();
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
#include <Tchar.h>
|
||||
|
||||
//Basic Functions
|
||||
OPENDEVICE icsneoOpenDevice;
|
||||
FINDNEODEVICES icsneoFindNeoDevices;
|
||||
OPENNEODEVICE icsneoOpenNeoDevice;
|
||||
CLOSEPORT icsneoClosePort;
|
||||
FREEOBJECT icsneoFreeObject;
|
||||
////OPENPORTEX icsneoOpenPortEx;
|
||||
@@ -44,7 +45,6 @@ SETVCAN412SETTINGS icsneoSetVCAN412Settings;
|
||||
SETBITRATE icsneoSetBitRate;
|
||||
GETDEVICEPARMS icsneoGetDeviceParameters;
|
||||
SETDEVICEPARMS icsneoSetDeviceParameters;
|
||||
ENABLEDOIPACTIVATIONLINE icsneoEnableDOIPLine;
|
||||
|
||||
//Error Functions
|
||||
GETLASTAPIERROR icsneoGetLastAPIError;
|
||||
@@ -81,17 +81,17 @@ SCRIPTWRITEAPPSIGNAL icsneoScriptWriteAppSignal;
|
||||
//SCRIPTWRITETXMESSAGE icsneoScriptWriteTxMessage;
|
||||
|
||||
|
||||
//The following are valid strings for setting parameters on devices
|
||||
//The following are valid strings for setting parameters on devices
|
||||
//using the icsneoGetDeviceParameters() and icsneoSetDeviceParameters() functions
|
||||
char *FireParameters[] =
|
||||
{
|
||||
"can1", "can2", "can3", "can4", "swcan", "lsftcan", "lin1", "lin2",
|
||||
"lin3", "lin4", "cgi_baud", "cgi_tx_ifs_bit_times",
|
||||
"cgi_rx_ifs_bit_times", "cgi_chksum_enable", "network_enables",
|
||||
"network_enabled_on_boot", "pwm_man_timeout", "pwr_man_enable",
|
||||
"misc_io_initial_ddr", "misc_io_initial_latch", "misc_io_analog_enable",
|
||||
"misc_io_report_period", "misc_io_on_report_events", "ain_sample_period",
|
||||
"ain_threshold", "iso15765_separation_time_offset", "iso9141_kwp_settings",
|
||||
"cgi_rx_ifs_bit_times", "cgi_chksum_enable", "network_enables",
|
||||
"network_enabled_on_boot", "pwm_man_timeout", "pwr_man_enable",
|
||||
"misc_io_initial_ddr", "misc_io_initial_latch", "misc_io_analog_enable",
|
||||
"misc_io_report_period", "misc_io_on_report_events", "ain_sample_period",
|
||||
"ain_threshold", "iso15765_separation_time_offset", "iso9141_kwp_settings",
|
||||
"perf_en", "iso_parity", "iso_msg_termination", "network_enables_2"
|
||||
};
|
||||
|
||||
@@ -99,30 +99,30 @@ char *FireParameters[] =
|
||||
char *VCAN3Parameters[] =
|
||||
{
|
||||
"can1", "can2", "network_enables", "network_enabled_on_boot", "iso15765_separation_time_offset",
|
||||
"perf_en", "misc_io_initial_ddr", "misc_io_initial_latch", "misc_io_report_period",
|
||||
"perf_en", "misc_io_initial_ddr", "misc_io_initial_latch", "misc_io_report_period",
|
||||
"misc_io_on_report_events"
|
||||
};
|
||||
|
||||
char *CANParameters[] =
|
||||
char *CANParameters[] =
|
||||
{
|
||||
"Mode", "SetBaudrate", "Baudrate", "NetworkType", "TqSeg1",
|
||||
"TqSeg2", "TqProp", "TqSync", "BRP", "auto_baud"
|
||||
};
|
||||
|
||||
char *SWCANParameters[] =
|
||||
char *SWCANParameters[] =
|
||||
{
|
||||
"Mode", "SetBaudrate", "Baudrate", "NetworkType", "TqSeg1", "TqSeg2",
|
||||
"Mode", "SetBaudrate", "Baudrate", "NetworkType", "TqSeg1", "TqSeg2",
|
||||
"TqProp", "TqSync", "BRP", "high_speed_auto_switch", "auto_baud"
|
||||
};
|
||||
|
||||
char *LINParameters[] =
|
||||
char *LINParameters[] =
|
||||
{
|
||||
"Baudrate", "spbrg", "brgh", "MasterResistor", "Mode"
|
||||
};
|
||||
|
||||
char *ISOKWPParms[] =
|
||||
{
|
||||
"Baudrate", "spbrg", "brgh", "init_steps", "init_step_count",
|
||||
"Baudrate", "spbrg", "brgh", "init_steps", "init_step_count",
|
||||
"p2_500us", "p3_500us", "p4_500us", "chksum_enabled"
|
||||
};
|
||||
|
||||
@@ -132,7 +132,10 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
|
||||
if((hAPIDLL = LoadLibrary(_T("icsneo40.dll"))) == NULL)
|
||||
return false;
|
||||
|
||||
icsneoOpenDevice = (OPENDEVICE) GetProcAddress(hAPIDLL, "icsneoOpenDevice");
|
||||
|
||||
|
||||
icsneoFindNeoDevices = (FINDNEODEVICES) GetProcAddress(hAPIDLL, "icsneoFindNeoDevices");
|
||||
icsneoOpenNeoDevice = (OPENNEODEVICE) GetProcAddress(hAPIDLL, "icsneoOpenNeoDevice");
|
||||
icsneoClosePort = (CLOSEPORT) GetProcAddress(hAPIDLL, "icsneoClosePort");
|
||||
icsneoFreeObject = (FREEOBJECT) GetProcAddress(hAPIDLL, "icsneoFreeObject");
|
||||
//// icsneoOpenPortEx = (OPENPORTEX) GetProcAddress(hAPIDLL, "icsneoOpenPortEx");
|
||||
@@ -198,22 +201,22 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
|
||||
icsneoScriptReadAppSignal = (SCRIPTREADAPPSIGNAL) GetProcAddress(hAPIDLL, "icsneoScriptReadAppSignal");
|
||||
icsneoScriptWriteAppSignal = (SCRIPTWRITEAPPSIGNAL) GetProcAddress(hAPIDLL, "icsneoScriptWriteAppSignal");
|
||||
|
||||
icsneoEnableDOIPLine = (ENABLEDOIPACTIVATIONLINE)GetProcAddress(hAPIDLL, "icsneoEnableDOIPLine");
|
||||
|
||||
|
||||
if(!icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
|
||||
if(!icsneoFindNeoDevices || !icsneoOpenNeoDevice || !icsneoClosePort || !icsneoFreeObject ||
|
||||
!icsneoTxMessages || !icsneoGetMessages || !icsneoWaitForRxMessagesWithTimeOut ||
|
||||
!icsneoGetTimeStampForMsg || !icsneoEnableNetworkRXQueue || !icsneoGetISO15765Status || !icsneoTxMessagesEx ||
|
||||
!icsneoSetISO15765RxParameters || !icsneoGetConfiguration || !icsneoSendConfiguration ||
|
||||
!icsneoGetFireSettings || !icsneoSetFireSettings || !icsneoGetVCAN3Settings ||
|
||||
!icsneoSetVCAN3Settings || !icsneoGetVCANRFSettings || !icsneoSetVCANRFSettings || !icsneoGetFire2Settings ||
|
||||
!icsneoGetVCAN412Settings || !icsneoSetVCAN412Settings ||
|
||||
!icsneoSetFire2Settings || !icsneoGetRADGalaxySettings || !icsneoSetRADGalaxySettings ||
|
||||
!icsneoSetFire2Settings || !icsneoGetRADGalaxySettings || !icsneoSetRADGalaxySettings ||
|
||||
!icsneoSetBitRate || !icsneoGetDeviceParameters || !icsneoSerialNumberToString ||
|
||||
!icsneoSetDeviceParameters || !icsneoGetLastAPIError || !icsneoGetErrorMessages ||
|
||||
!icsneoGetErrorInfo || !icsneoScriptLoad || !icsneoScriptStart || !icsneoScriptStop ||
|
||||
!icsneoScriptClear || !icsneoScriptStartFBlock || !icsneoScriptStopFBlock ||
|
||||
!icsneoScriptGetFBlockStatus || !icsneoScriptGetScriptStatus || !icsneoScriptReadAppSignal ||
|
||||
!icsneoScriptWriteAppSignal || !icsneoGetDLLVersion || !icsneoEnableDOIPLine)
|
||||
!icsneoScriptWriteAppSignal || !icsneoGetDLLVersion)
|
||||
{
|
||||
FreeLibrary(hAPIDLL);
|
||||
return false;
|
||||
|
||||
@@ -11,24 +11,23 @@ void UnloadDLLAPI(HINSTANCE &hAPIDLL);
|
||||
//Basic Functions
|
||||
typedef int (__stdcall *FINDNEODEVICES)(unsigned long DeviceTypes, NeoDevice *pNeoDevice, int *pNumDevices);
|
||||
typedef int (__stdcall *OPENNEODEVICE)(NeoDevice *pNeoDevice, void * hObject, unsigned char *bNetworkIDs, int bConfigRead, int bSyncToPC);
|
||||
typedef int (__stdcall *OPENDEVICE)(NeoDeviceEx* pNeoDeviceEx, void** hObject, unsigned char* bNetworkIDs, int bConfigRead, int iOptions, OptionsOpenNeoEx* stOptionsOpenNeoEx, unsigned long reserved);
|
||||
typedef int (__stdcall *CLOSEPORT)(void * hObject, int *pNumberOfErrors);
|
||||
typedef int (__stdcall *CLOSEPORT)(void * hObject, int *pNumberOfErrors);
|
||||
typedef void (__stdcall *FREEOBJECT)(void * hObject);
|
||||
typedef int (__stdcall *OPENPORTEX)(void * lPortNumber, int lPortType, int lDriverType, int lIPAddressMSB, int lIPAddressLSBOrBaudRate,
|
||||
typedef int (__stdcall *OPENPORTEX)(void * lPortNumber, int lPortType, int lDriverType, int lIPAddressMSB, int lIPAddressLSBOrBaudRate,
|
||||
int bConfigRead, unsigned char *bNetworkID, int * hObject);
|
||||
typedef int (__stdcall *SERIALNUMBERTOSTRING) (unsigned long serial, char *data,unsigned long data_size);
|
||||
|
||||
//Message Functions
|
||||
typedef int (__stdcall *GETMESSAGES)(void * hObject, icsSpyMessage *pMsg, int * pNumberOfMessages, int * pNumberOfErrors);
|
||||
typedef int (__stdcall *TXMESSAGES)(void * hObject, icsSpyMessage *pMsg, int lNetworkID, int lNumMessages);
|
||||
typedef int (__stdcall *GETMESSAGES)(void * hObject, icsSpyMessage *pMsg, int * pNumberOfMessages, int * pNumberOfErrors);
|
||||
typedef int (__stdcall *TXMESSAGES)(void * hObject, icsSpyMessage *pMsg, int lNetworkID, int lNumMessages);
|
||||
typedef int (__stdcall *TXMESSAGESEX)(void * hObject,icsSpyMessage *pMsg, unsigned int lNetworkID,unsigned int lNumMessages,unsigned int *NumTxed, unsigned int zero2);
|
||||
typedef int (__stdcall *WAITFORRXMSGS)(void * hObject, unsigned int iTimeOut);
|
||||
typedef int (__stdcall *ENABLERXQUEUE)(void * hObject, int iEnable);
|
||||
typedef int (__stdcall *GETTSFORMSG)(void * hObject, icsSpyMessage *pMsg, double *pTimeStamp);
|
||||
typedef void (__stdcall *GETISO15765STATUS)(void * hObject, int lNetwork, int lClearTxStatus,
|
||||
typedef void (__stdcall *GETISO15765STATUS)(void * hObject, int lNetwork, int lClearTxStatus,
|
||||
int lClearRxStatus, int *lTxStatus, int *lRxStatus);
|
||||
typedef void (__stdcall *SETISO15765RXPARMS)(void * hObject, int lNetwork, int lEnable,
|
||||
spyFilterLong *pFF_CFMsgFilter, icsSpyMessage *pTxMsg,
|
||||
typedef void (__stdcall *SETISO15765RXPARMS)(void * hObject, int lNetwork, int lEnable,
|
||||
spyFilterLong *pFF_CFMsgFilter, icsSpyMessage *pTxMsg,
|
||||
int lCFTimeOutMs, int lFlowCBlockSize,
|
||||
int lUsesExtendedAddressing, int lUseHardwareIfPresent);
|
||||
typedef int (__stdcall *DOWNLOADISO15765_2_TXSCRIPT)(void * hObject, unsigned int NetworkID);
|
||||
@@ -37,7 +36,7 @@ typedef int (__stdcall *TXISO15765_2_MESSAGE)(void * hObject, stCM_ISO157652_TxM
|
||||
|
||||
//Device Functions
|
||||
typedef int (__stdcall *GETCONFIG)(void * hObject, unsigned char * pData, int * lNumBytes);
|
||||
typedef int (__stdcall *SENDCONFIG)(void * hObject, unsigned char * pData, int lNumBytes);
|
||||
typedef int (__stdcall *SENDCONFIG)(void * hObject, unsigned char * pData, int lNumBytes);
|
||||
typedef int (__stdcall *GETFIRESETTINGS)(void * hObject, SFireSettings *pSettings, int iNumBytes);
|
||||
typedef int (__stdcall *SETFIRESETTINGS)(void * hObject, SFireSettings *pSettings, int iNumBytes, int bSaveToEEPROM);
|
||||
|
||||
@@ -63,12 +62,11 @@ typedef int (__stdcall *SETRADSTAR2SETTINGS)(void * hObject, SRADStar2Settings *
|
||||
typedef int (__stdcall *SETBITRATE)(void * hObject, int BitRate, int NetworkID);
|
||||
typedef int (__stdcall *GETDEVICEPARMS)(void * hObject, char *pParameter, char *pValues, short ValuesLength);
|
||||
typedef int (__stdcall *SETDEVICEPARMS)(void * hObject, char *pParmValue, int *pErrorIndex, int bSaveToEEPROM);
|
||||
typedef int(__stdcall *ENABLEDOIPACTIVATIONLINE)(void * hObject, bool Val);
|
||||
|
||||
//Error Functions
|
||||
typedef int (__stdcall *GETLASTAPIERROR)(void * hObject, unsigned long *pErrorNumber);
|
||||
typedef int (__stdcall *GETERRMSGS)(void * hObject, int * pErrorMsgs, int * pNumberOfErrors);
|
||||
typedef int (__stdcall *GETERRORINFO)(int lErrorNumber, TCHAR *szErrorDescriptionShort,
|
||||
typedef int (__stdcall *GETERRORINFO)(int lErrorNumber, TCHAR *szErrorDescriptionShort,
|
||||
TCHAR *szErrorDescriptionLong, int * lMaxLengthShort,
|
||||
int * lMaxLengthLong,int * lErrorSeverity,int * lRestartNeeded);
|
||||
|
||||
@@ -86,8 +84,8 @@ typedef int (__stdcall *STARTSOCKSERVER)(void * hObject, int iPort);
|
||||
typedef int (__stdcall *STOPSOCKSERVER)(void * hObject);
|
||||
|
||||
//CoreMini Script functions
|
||||
typedef int (__stdcall *SCRIPTSTART)(void * hObject, int iLocation);
|
||||
typedef int (__stdcall *SCRIPTSTOP)(void * hObject);
|
||||
typedef int (__stdcall *SCRIPTSTART)(void * hObject, int iLocation);
|
||||
typedef int (__stdcall *SCRIPTSTOP)(void * hObject);
|
||||
typedef int (__stdcall *SCRIPTLOAD)(void * hObject, const unsigned char * bin, unsigned long len_bytes, int iLocation);
|
||||
typedef int (__stdcall *SCRIPTCLEAR)(void * hObject, int iLocation);
|
||||
typedef int (__stdcall *SCRIPTSTARTFBLOCK)(void * hObject,unsigned int fb_index);
|
||||
@@ -101,20 +99,21 @@ typedef int (__stdcall *SCRIPTWRITEISO15765TXMESSAGE)(void * hObject, unsigned
|
||||
|
||||
|
||||
//Deprecated (but still suppored in the DLL)
|
||||
//////typedef int (__stdcall *OPENPORTEX)(int lPortSerialNumber, int lPortType, int lDriverType,
|
||||
////// int lIPAddressMSB, int lIPAddressLSBOrBaudRate,int bConfigRead,
|
||||
//////typedef int (__stdcall *OPENPORTEX)(int lPortSerialNumber, int lPortType, int lDriverType,
|
||||
////// int lIPAddressMSB, int lIPAddressLSBOrBaudRate,int bConfigRead,
|
||||
////// unsigned char * bNetworkID, int * hObject);
|
||||
//////
|
||||
//////typedef int (__stdcall *OPENPORT)(int lPortNumber, int lPortType, int lDriverType,
|
||||
//////typedef int (__stdcall *OPENPORT)(int lPortNumber, int lPortType, int lDriverType,
|
||||
////// unsigned char *bNetworkID, unsigned char *bSCPIDs, int * hObject);
|
||||
//////typedef int (__stdcall *ENABLENETWORKCOM)(void * hObject, int Enable);
|
||||
//////typedef int (__stdcall *FINDCOMDEVICES)(int lDriverType, int lGetSerialNumbers, int lStopAtFirst, int lUSBCommOnly,
|
||||
////// int *p_lDeviceTypes, int *p_lComPorts, int *p_lSerialNumbers, int *lNumDevices);
|
||||
////// int *p_lDeviceTypes, int *p_lComPorts, int *p_lSerialNumbers, int *lNumDevices);
|
||||
|
||||
|
||||
|
||||
//Basic Functions
|
||||
extern OPENDEVICE icsneoOpenDevice;
|
||||
extern FINDNEODEVICES icsneoFindNeoDevices;
|
||||
extern OPENNEODEVICE icsneoOpenNeoDevice;
|
||||
extern CLOSEPORT icsneoClosePort;
|
||||
extern FREEOBJECT icsneoFreeObject;
|
||||
extern SERIALNUMBERTOSTRING icsneoSerialNumberToString;
|
||||
@@ -158,7 +157,6 @@ extern SETVCAN412SETTINGS icsneoSetVCAN412Settings;
|
||||
extern SETBITRATE icsneoSetBitRate;
|
||||
extern GETDEVICEPARMS icsneoGetDeviceParameters;
|
||||
extern SETDEVICEPARMS icsneoSetDeviceParameters;
|
||||
extern ENABLEDOIPACTIVATIONLINE icsneoEnableDOIPLine;
|
||||
|
||||
//Error Functions
|
||||
extern GETLASTAPIERROR icsneoGetLastAPIError;
|
||||
|
||||
+259
-1167
File diff suppressed because it is too large
Load Diff
@@ -14,15 +14,15 @@
|
||||
using namespace icsneo;
|
||||
|
||||
extern "C" {
|
||||
extern int LegacyDLLExport icsneoValidateHObject(void* hObject);
|
||||
extern int LegacyDLLExport icsneoWaitForRxMessagesWithTimeOut(void* hObject, unsigned int iTimeOut);
|
||||
extern int DLLExport icsneoValidateHObject(void* hObject);
|
||||
extern int DLLExport icsneoWaitForRxMessagesWithTimeOut(void* hObject, unsigned int iTimeOut);
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoWaitForRxMessagesWithTimeOut(void* hObject, unsigned int iTimeOut) {
|
||||
int icsneoWaitForRxMessagesWithTimeOut(void* hObject, unsigned int iTimeOut) {
|
||||
if(!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
neodevice_t* device = (neodevice_t*)hObject;
|
||||
if(device->device->getCurrentMessageCount() != 0)
|
||||
return true;
|
||||
return bool(device->device->com->waitForMessageSync({}, std::chrono::milliseconds(iTimeOut)));
|
||||
return bool(device->device->com->waitForMessageSync(MessageFilter(), std::chrono::milliseconds(iTimeOut)));
|
||||
}
|
||||
@@ -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,12 +0,0 @@
|
||||
REM clean intermediate directories
|
||||
rmdir /s /q build
|
||||
mkdir build
|
||||
|
||||
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 ..
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
cmake --build .
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
@@ -1,2 +0,0 @@
|
||||
call "%VCVARS32%"
|
||||
call "ci\build-windows.bat"
|
||||
@@ -1,2 +0,0 @@
|
||||
call "%VCVARS64%"
|
||||
call "ci\build-windows.bat"
|
||||
@@ -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()
|
||||
+40
-177
@@ -12,28 +12,19 @@
|
||||
#include "icsneo/communication/message/serialnumbermessage.h"
|
||||
#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"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
int Communication::messageCallbackIDCounter = 1;
|
||||
|
||||
Communication::~Communication() {
|
||||
if(isOpen())
|
||||
close();
|
||||
}
|
||||
|
||||
bool Communication::open() {
|
||||
if(isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!driver->open())
|
||||
return false;
|
||||
|
||||
spawnThreads();
|
||||
return true;
|
||||
return impl->open();
|
||||
}
|
||||
|
||||
void Communication::spawnThreads() {
|
||||
@@ -42,33 +33,24 @@ void Communication::spawnThreads() {
|
||||
|
||||
void Communication::joinThreads() {
|
||||
closing = true;
|
||||
|
||||
if(pauseReadTask) {
|
||||
resumeReads();
|
||||
}
|
||||
|
||||
if(readTaskThread.joinable())
|
||||
readTaskThread.join();
|
||||
closing = false;
|
||||
}
|
||||
|
||||
bool Communication::close() {
|
||||
joinThreads();
|
||||
|
||||
if(!isOpen() && !isDisconnected()) {
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return driver->close();
|
||||
joinThreads();
|
||||
|
||||
return impl->close();
|
||||
}
|
||||
|
||||
bool Communication::isOpen() {
|
||||
return driver->isOpen();
|
||||
}
|
||||
|
||||
bool Communication::isDisconnected() {
|
||||
return driver->isDisconnected();
|
||||
return impl->isOpen();
|
||||
}
|
||||
|
||||
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
|
||||
@@ -78,32 +60,15 @@ bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
|
||||
|
||||
bool Communication::sendCommand(Command cmd, std::vector<uint8_t> arguments) {
|
||||
std::vector<uint8_t> packet;
|
||||
if(!encoder->encode(*packetizer, packet, cmd, arguments))
|
||||
if(!encoder->encode(packet, cmd, arguments))
|
||||
return false;
|
||||
|
||||
return sendPacket(packet);
|
||||
}
|
||||
|
||||
bool Communication::sendCommand(ExtendedCommand cmd, std::vector<uint8_t> arguments) {
|
||||
const auto size = arguments.size();
|
||||
if (size > std::numeric_limits<uint16_t>::max())
|
||||
return false;
|
||||
|
||||
arguments.insert(arguments.begin(), {
|
||||
uint8_t(uint16_t(cmd) & 0xff),
|
||||
uint8_t((uint16_t(cmd) >> 8) & 0xff),
|
||||
uint8_t(size & 0xff),
|
||||
uint8_t((size >> 8) & 0xff)
|
||||
});
|
||||
|
||||
return sendCommand(Command::Extended, arguments);
|
||||
}
|
||||
|
||||
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]() {
|
||||
return sendCommand(Command::ReadSettings, { 0, 0, 0, 1 /* Get Global Settings */, 0, 1 /* Subversion 1 */ });
|
||||
}, filter, timeout);
|
||||
sendCommand(Command::ReadSettings, { 0, 0, 0, 1 /* Get Global Settings */, 0, 1 /* Subversion 1 */ });
|
||||
std::shared_ptr<Message> msg = waitForMessageSync(MessageFilter(Network::NetID::ReadSettings), timeout);
|
||||
if(!msg)
|
||||
return false;
|
||||
|
||||
@@ -113,75 +78,29 @@ bool Communication::getSettingsSync(std::vector<uint8_t>& data, std::chrono::mil
|
||||
return false;
|
||||
}
|
||||
|
||||
if(gsmsg->response == ReadSettingsMessage::Response::OKDefaultsUsed) {
|
||||
report(APIEvent::Type::SettingsDefaultsUsed, APIEvent::Severity::EventInfo);
|
||||
} else if(gsmsg->response != ReadSettingsMessage::Response::OK) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
if(gsmsg->response != ReadSettingsMessage::Response::OK) {
|
||||
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
data = std::move(gsmsg->data);
|
||||
data = std::move(msg->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<SerialNumberMessage> Communication::getSerialNumberSync(std::chrono::milliseconds timeout) {
|
||||
static const std::shared_ptr<MessageFilter> filter = std::make_shared<Main51MessageFilter>(Command::RequestSerialNumber);
|
||||
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
||||
return sendCommand(Command::RequestSerialNumber);
|
||||
}, filter, timeout);
|
||||
sendCommand(Command::RequestSerialNumber);
|
||||
std::shared_ptr<Message> msg = waitForMessageSync(std::make_shared<Main51MessageFilter>(Command::RequestSerialNumber), timeout);
|
||||
if(!msg) // Did not receive a message
|
||||
return std::shared_ptr<SerialNumberMessage>();
|
||||
|
||||
auto m51 = std::dynamic_pointer_cast<Main51Message>(msg);
|
||||
if(!m51) // Could not upcast for some reason
|
||||
return std::shared_ptr<SerialNumberMessage>();
|
||||
|
||||
return std::dynamic_pointer_cast<SerialNumberMessage>(m51);
|
||||
}
|
||||
|
||||
std::optional< std::vector< std::optional<DeviceAppVersion> > > Communication::getVersionsSync(std::chrono::milliseconds timeout) {
|
||||
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::DeviceVersion);
|
||||
std::vector< std::optional<DeviceAppVersion> > ret;
|
||||
|
||||
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
||||
return sendCommand(Command::GetMainVersion);
|
||||
}, filter, timeout);
|
||||
if(!msg) // Did not receive a message
|
||||
return std::nullopt;
|
||||
|
||||
auto ver = std::dynamic_pointer_cast<VersionMessage>(msg);
|
||||
if(!ver) // Could not upcast for some reason
|
||||
return std::nullopt;
|
||||
|
||||
if(ver->ForChip != VersionMessage::MainChip || ver->Versions.size() != 1)
|
||||
return std::nullopt;
|
||||
|
||||
ret.push_back(ver->Versions.front());
|
||||
|
||||
msg = waitForMessageSync([this]() {
|
||||
return sendCommand(Command::GetSecondaryVersions);
|
||||
}, filter, timeout);
|
||||
if(msg) { // This one is allowed to fail
|
||||
ver = std::dynamic_pointer_cast<VersionMessage>(msg);
|
||||
if(ver && ver->ForChip != VersionMessage::MainChip)
|
||||
ret.insert(ret.end(), ver->Versions.begin(), ver->Versions.end());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<LogicalDiskInfoMessage> Communication::getLogicalDiskInfoSync(std::chrono::milliseconds timeout) {
|
||||
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::LogicalDiskInfo);
|
||||
|
||||
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
||||
return sendCommand(Command::GetLogicalDiskInfo);
|
||||
}, filter, timeout);
|
||||
if(!msg) // Did not receive a message
|
||||
return {};
|
||||
|
||||
return std::dynamic_pointer_cast<LogicalDiskInfoMessage>(msg);
|
||||
}
|
||||
|
||||
int Communication::addMessageCallback(const std::shared_ptr<MessageCallback>& cb) {
|
||||
int Communication::addMessageCallback(const MessageCallback& cb) {
|
||||
std::lock_guard<std::mutex> lk(messageCallbacksLock);
|
||||
messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb));
|
||||
return messageCallbackIDCounter++;
|
||||
@@ -198,34 +117,25 @@ bool Communication::removeMessageCallback(int id) {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(void)> onceWaitingDo,
|
||||
const std::shared_ptr<MessageFilter>& f, std::chrono::milliseconds timeout) {
|
||||
std::mutex cvMutex;
|
||||
std::shared_ptr<Message> Communication::waitForMessageSync(std::shared_ptr<MessageFilter> f, std::chrono::milliseconds timeout) {
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
std::shared_ptr<Message> returnedMessage;
|
||||
|
||||
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) {
|
||||
int cb = addMessageCallback(MessageCallback([&m, &returnedMessage, &cv](std::shared_ptr<Message> message) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(cvMutex);
|
||||
std::lock_guard<std::mutex> lk(m);
|
||||
returnedMessage = message;
|
||||
}
|
||||
cv.notify_all();
|
||||
cv.notify_one();
|
||||
}, f));
|
||||
|
||||
// We have now added the callback, do whatever the caller wanted to do
|
||||
bool fail = !onceWaitingDo();
|
||||
if(!fail)
|
||||
cv.wait_for(cvLk, timeout, [&returnedMessage] { return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value
|
||||
cvLk.unlock(); // Ensure callbacks can complete even if we didn't wait for them
|
||||
// We have now added the callback, wait for it to return from the other thread
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
cv.wait_for(lk, timeout, [&returnedMessage] { return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value
|
||||
lk.unlock();
|
||||
|
||||
// We don't actually check that we got a message, because either way we want to remove the callback (since it should only happen once)
|
||||
removeMessageCallback(cb);
|
||||
// We are now guaranteed that no more callbacks will happen
|
||||
|
||||
if(fail) // The caller's function failed, so don't return a message
|
||||
returnedMessage.reset();
|
||||
|
||||
// Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking
|
||||
return returnedMessage;
|
||||
@@ -240,77 +150,30 @@ void Communication::dispatchMessage(const std::shared_ptr<Message>& msg) {
|
||||
EventManager::GetInstance().cancelErrorDowngradingOnCurrentThread();
|
||||
for(auto& cb : messageCallbacks) {
|
||||
if(!closing) { // We might have closed while reading or processing
|
||||
cb.second->callIfMatch(msg);
|
||||
cb.second.callIfMatch(msg);
|
||||
}
|
||||
}
|
||||
if(downgrade)
|
||||
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; });
|
||||
}
|
||||
if(driver->readAvailable()) {
|
||||
if(pauseReadTask) {
|
||||
/**
|
||||
* Reads could have paused while the driver was not available
|
||||
*/
|
||||
continue;
|
||||
readBytes.clear();
|
||||
if(impl->readWait(readBytes)) {
|
||||
if(packetizer->input(readBytes)) {
|
||||
for(auto& packet : packetizer->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));
|
||||
}
|
||||
|
||||
+28
-305
@@ -3,57 +3,27 @@
|
||||
#include "icsneo/communication/message/serialnumbermessage.h"
|
||||
#include "icsneo/communication/message/resetstatusmessage.h"
|
||||
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.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/command.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
#include "icsneo/communication/packet/ethernetpacket.h"
|
||||
#include "icsneo/communication/packet/flexraypacket.h"
|
||||
#include "icsneo/communication/packet/iso9141packet.h"
|
||||
#include "icsneo/communication/packet/versionpacket.h"
|
||||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||
#include "icsneo/communication/packet/logicaldiskinfopacket.h"
|
||||
#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;
|
||||
|
||||
uint64_t Decoder::GetUInt64FromLEBytes(const uint8_t* bytes) {
|
||||
uint64_t Decoder::GetUInt64FromLEBytes(uint8_t* bytes) {
|
||||
uint64_t ret = 0;
|
||||
for(int i = 0; i < 8; i++)
|
||||
ret |= (uint64_t(bytes[i]) << (i * 8));
|
||||
ret |= (bytes[i] << (i * 8));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Packet>& packet) {
|
||||
switch(packet->network.getType()) {
|
||||
case Network::Type::Ethernet: {
|
||||
result = HardwareEthernetPacket::DecodeToMessage(packet->data, report);
|
||||
case Network::Type::Ethernet:
|
||||
result = HardwareEthernetPacket::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
|
||||
@@ -61,11 +31,9 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
|
||||
// Timestamps are in (resolution) ns increments since 1/1/2007 GMT 00:00:00.0000
|
||||
// The resolution depends on the device
|
||||
EthernetMessage& eth = *static_cast<EthernetMessage*>(result.get());
|
||||
eth.timestamp *= timestampResolution;
|
||||
eth.network = packet->network;
|
||||
result->timestamp *= timestampResolution;
|
||||
result->network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::CAN:
|
||||
case Network::Type::SWCAN:
|
||||
case Network::Type::LSFTCAN: {
|
||||
@@ -79,28 +47,10 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was malformed
|
||||
}
|
||||
|
||||
// Timestamps are in (resolution) ns increments since 1/1/2007 GMT 00:00:00.0000
|
||||
// The resolution depends on the device
|
||||
result->timestamp *= timestampResolution;
|
||||
|
||||
switch(result->type) {
|
||||
case Message::Type::Frame: {
|
||||
CANMessage& can = *static_cast<CANMessage*>(result.get());
|
||||
can.network = packet->network;
|
||||
break;
|
||||
}
|
||||
case Message::Type::CANErrorCount: {
|
||||
CANErrorCountMessage& can = *static_cast<CANErrorCountMessage*>(result.get());
|
||||
can.network = packet->network;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // An unknown type was returned, the packet was malformed
|
||||
}
|
||||
}
|
||||
|
||||
result->network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::FlexRay: {
|
||||
@@ -114,95 +64,27 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was malformed
|
||||
}
|
||||
|
||||
// Timestamps are in (resolution) ns increments since 1/1/2007 GMT 00:00:00.0000
|
||||
// The resolution depends on the device
|
||||
FlexRayMessage& fr = *static_cast<FlexRayMessage*>(result.get());
|
||||
fr.timestamp *= timestampResolution;
|
||||
fr.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::ISO9141: {
|
||||
if(packet->data.size() < sizeof(HardwareISO9141Packet)) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
result = iso9141decoder.decodeToMessage(packet->data);
|
||||
if(!result)
|
||||
return false; // A nullptr was returned, more data is required to decode this packet
|
||||
|
||||
// Timestamps are in (resolution) ns increments since 1/1/2007 GMT 00:00:00.0000
|
||||
// The resolution depends on the device
|
||||
ISO9141Message& iso = *static_cast<ISO9141Message*>(result.get());
|
||||
iso.timestamp *= timestampResolution;
|
||||
iso.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::I2C: {
|
||||
if(packet->data.size() < sizeof(HardwareI2CPacket)) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
result = HardwareI2CPacket::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; //malformed packet indicated by a nullptr return
|
||||
}
|
||||
|
||||
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;
|
||||
result->timestamp *= timestampResolution;
|
||||
result->network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::Internal: {
|
||||
switch(packet->network.getNetID()) {
|
||||
case Network::NetID::Reset_Status: {
|
||||
// We can deal with not having the last two fields (voltage and temperature)
|
||||
if(packet->data.size() < (sizeof(HardwareResetStatusPacket) - (sizeof(uint16_t) * 2))) {
|
||||
if(packet->data.size() < sizeof(HardwareResetStatusPacket)) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
HardwareResetStatusPacket* data = (HardwareResetStatusPacket*)packet->data.data();
|
||||
auto msg = std::make_shared<ResetStatusMessage>();
|
||||
msg->network = packet->network;
|
||||
msg->mainLoopTime = data->main_loop_time_25ns * 25;
|
||||
msg->maxMainLoopTime = data->max_main_loop_time_25ns * 25;
|
||||
msg->busVoltage = data->busVoltage;
|
||||
msg->deviceTemperature = data->deviceTemperature;
|
||||
msg->justReset = data->status.just_reset;
|
||||
msg->comEnabled = data->status.com_enabled;
|
||||
msg->cmRunning = data->status.cm_is_running;
|
||||
@@ -216,112 +98,9 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
msg->cmTooBig = data->status.cm_too_big;
|
||||
msg->hidUsbState = data->status.hidUsbState;
|
||||
msg->fpgaUsbState = data->status.fpgaUsbState;
|
||||
if(packet->data.size() >= sizeof(HardwareResetStatusPacket)) {
|
||||
msg->busVoltage = data->busVoltage;
|
||||
msg->deviceTemperature = data->deviceTemperature;
|
||||
}
|
||||
result = msg;
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::Device: {
|
||||
// These are neoVI network messages
|
||||
// They come in as CAN but we will handle them in the device rather than
|
||||
// passing them onto the user.
|
||||
if(packet->data.size() < 24) {
|
||||
auto rawmsg = std::make_shared<RawMessage>(Network::NetID::Device);
|
||||
result = rawmsg;
|
||||
rawmsg->data = packet->data;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = HardwareCANPacket::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was malformed
|
||||
}
|
||||
|
||||
// Timestamps are in (resolution) ns increments since 1/1/2007 GMT 00:00:00.0000
|
||||
// The resolution depends on the device
|
||||
auto* raw = dynamic_cast<RawMessage*>(result.get());
|
||||
if(raw == nullptr) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was malformed
|
||||
}
|
||||
raw->timestamp *= timestampResolution;
|
||||
raw->network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::DeviceStatus: {
|
||||
// Just pass along the data, the device needs to handle this itself
|
||||
result = std::make_shared<RawMessage>(packet->network, packet->data);
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::NeoMemorySDRead: {
|
||||
if(packet->data.size() != 512 + sizeof(uint32_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<NeoReadMemorySDMessage>();
|
||||
result = msg;
|
||||
msg->startAddress = *reinterpret_cast<uint32_t*>(packet->data.data());
|
||||
msg->data.insert(msg->data.end(), packet->data.begin() + 4, packet->data.end());
|
||||
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());
|
||||
|
||||
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;
|
||||
}
|
||||
case Network::NetID::FlexRayControl: {
|
||||
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
|
||||
if(!frResult->decoded) {
|
||||
@@ -331,10 +110,17 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
result = frResult;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;//return false;
|
||||
}
|
||||
}
|
||||
default:
|
||||
switch(packet->network.getNetID()) {
|
||||
case Network::NetID::Main51: {
|
||||
switch((Command)packet->data[0]) {
|
||||
case Command::RequestSerialNumber: {
|
||||
auto msg = std::make_shared<SerialNumberMessage>();
|
||||
msg->network = packet->network;
|
||||
uint64_t serial = GetUInt64FromLEBytes(packet->data.data() + 1);
|
||||
// The device sends 64-bits of serial number, but we never use more than 32-bits.
|
||||
msg->deviceSerial = Device::SerialNumToString((uint32_t)serial);
|
||||
@@ -347,36 +133,9 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
result = msg;
|
||||
return true;
|
||||
}
|
||||
case Command::GetMainVersion: {
|
||||
result = HardwareVersionPacket::DecodeMainToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
case Command::GetSecondaryVersions: {
|
||||
result = HardwareVersionPacket::DecodeSecondaryToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
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->network = packet->network;
|
||||
msg->command = Command(packet->data[0]);
|
||||
msg->data.insert(msg->data.begin(), packet->data.begin() + 1, packet->data.end());
|
||||
result = msg;
|
||||
@@ -400,8 +159,9 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
}
|
||||
case Network::NetID::ReadSettings: {
|
||||
auto msg = std::make_shared<ReadSettingsMessage>();
|
||||
msg->network = packet->network;
|
||||
msg->response = ReadSettingsMessage::Response(packet->data[0]);
|
||||
|
||||
|
||||
if(msg->response == ReadSettingsMessage::Response::OK) {
|
||||
// The global settings structure is the payload of the message in this case
|
||||
msg->data.insert(msg->data.begin(), packet->data.begin() + 10, packet->data.end());
|
||||
@@ -417,50 +177,13 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
result = msg;
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::LogicalDiskInfo: {
|
||||
result = LogicalDiskInfoPacket::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::WiVICommand: {
|
||||
result = WiVI::CommandPacket::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::EthPHYControl: {
|
||||
result = HardwareEthernetPhyRegisterPacket::DecodeToMessage(packet->data, report);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::ScriptStatus: {
|
||||
result = ScriptStatus::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::DiskData: {
|
||||
result = std::make_shared<DiskDataMessage>(std::move(packet->data));
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For the moment other types of messages will automatically be decoded as raw messages
|
||||
result = std::make_shared<RawMessage>(packet->network, packet->data);
|
||||
auto msg = std::make_shared<Message>();
|
||||
msg->network = packet->network;
|
||||
msg->data = packet->data;
|
||||
result = msg;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
#include "icsneo/communication/driver.h"
|
||||
|
||||
//#define ICSNEO_DRIVER_DEBUG_PRINTS
|
||||
#ifdef ICSNEO_DRIVER_DEBUG_PRINTS
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
bool Driver::pushRx(const uint8_t* buf, size_t numReceived) {
|
||||
bool ret = readBuffer.write(buf, numReceived);
|
||||
|
||||
if(hasRxWaitRequest) {
|
||||
rxWaitRequestCv.notify_one();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
hasRxWaitRequest = true;
|
||||
|
||||
auto ret = rxWaitRequestCv.wait_for(lk, timeout, predicate);
|
||||
|
||||
hasRxWaitRequest = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
|
||||
// A limit of zero indicates no limit
|
||||
if(limit == 0)
|
||||
limit = (size_t)-1;
|
||||
|
||||
if(limit > (readBuffer.size() + 4))
|
||||
limit = (readBuffer.size() + 4);
|
||||
|
||||
|
||||
// wait until we have enough data, or the timout occurs
|
||||
waitForRx(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
|
||||
if(actuallyRead > 0) {
|
||||
std::cout << "Read data: (" << actuallyRead << ')' << std::hex << std::endl;
|
||||
for(int i = 0; i < actuallyRead; i += 16) {
|
||||
for(int j = 0; j < std::min<int>(actuallyRead - i, 16); j++)
|
||||
std::cout << std::setw(2) << std::setfill('0') << uint32_t(bytes[i+j]) << ' ';
|
||||
std::cout << std::endl;
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
return actuallyRead > 0;
|
||||
}
|
||||
|
||||
bool Driver::write(const std::vector<uint8_t>& bytes) {
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(writeBlocks) {
|
||||
if(writeQueueFull()) {
|
||||
while(writeQueueAlmostFull()) // Wait until we have some decent amount of space
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
} else {
|
||||
if(writeQueueFull()) {
|
||||
report(APIEvent::Type::TransmitBufferFull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const bool ret = writeInternal(bytes);
|
||||
if(!ret)
|
||||
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
+78
-227
@@ -1,278 +1,129 @@
|
||||
#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"
|
||||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||
#include "icsneo/communication/message/ethphymessage.h"
|
||||
#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;
|
||||
|
||||
bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result, const std::shared_ptr<Message>& message) {
|
||||
bool Encoder::encode(std::vector<uint8_t>& result, const std::shared_ptr<Message>& message) {
|
||||
bool shortFormat = false;
|
||||
std::vector<uint8_t>* buffer = &result;
|
||||
uint16_t netid = 0;
|
||||
bool useResultAsBuffer = false; // Otherwise it's expected that we use message->data
|
||||
result.clear();
|
||||
|
||||
switch(message->type) {
|
||||
case Message::Type::Frame: {
|
||||
auto frame = std::dynamic_pointer_cast<Frame>(message);
|
||||
|
||||
// Frame uses frame->data as the buffer unless directed otherwise
|
||||
buffer = &frame->data;
|
||||
netid = uint16_t(frame->network.getNetID());
|
||||
|
||||
switch(frame->network.getType()) {
|
||||
case Network::Type::Ethernet: {
|
||||
auto ethmsg = std::dynamic_pointer_cast<EthernetMessage>(message);
|
||||
if(!ethmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed EthernetMessage
|
||||
}
|
||||
|
||||
buffer = &result;
|
||||
if(!HardwareEthernetPacket::EncodeFromMessage(*ethmsg, result, report))
|
||||
return false;
|
||||
|
||||
break;
|
||||
} // End of Network::Type::Ethernet
|
||||
case Network::Type::CAN:
|
||||
case Network::Type::SWCAN:
|
||||
case Network::Type::LSFTCAN: {
|
||||
auto canmsg = std::dynamic_pointer_cast<CANMessage>(message);
|
||||
if(!canmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed CANMessage
|
||||
}
|
||||
|
||||
if(!supportCANFD && canmsg->isCANFD) {
|
||||
report(APIEvent::Type::CANFDNotSupported, APIEvent::Severity::Error);
|
||||
return false; // This device does not support CAN FD
|
||||
}
|
||||
|
||||
buffer = &result;
|
||||
if(!HardwareCANPacket::EncodeFromMessage(*canmsg, result, report))
|
||||
return false; // The CANMessage was malformed
|
||||
|
||||
break;
|
||||
} // End of Network::Type::CAN
|
||||
case Network::Type::ISO9141: {
|
||||
auto isomsg = std::dynamic_pointer_cast<ISO9141Message>(message);
|
||||
if(!isomsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed ISO9141Message
|
||||
}
|
||||
|
||||
// Skip the normal message wrapping at the bottom since we need to send multiple
|
||||
// packets to the device. This function just encodes them back to back into `result`
|
||||
return HardwareISO9141Packet::EncodeFromMessage(*isomsg, result, report, packetizer);
|
||||
} // End of Network::Type::ISO9141
|
||||
case Network::Type::A2B: {
|
||||
auto a2bmsg = std::dynamic_pointer_cast<A2BMessage>(message);
|
||||
if(!a2bmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareA2BPacket::EncodeFromMessage(*a2bmsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::A2B
|
||||
case Network::Type::I2C: {
|
||||
auto i2cmsg = std::dynamic_pointer_cast<I2CMessage>(message);
|
||||
if(!i2cmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareI2CPacket::EncodeFromMessage(*i2cmsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
switch(message->network.getType()) {
|
||||
case Network::Type::Ethernet: {
|
||||
auto ethmsg = std::dynamic_pointer_cast<EthernetMessage>(message);
|
||||
if(!ethmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed EthernetMessage
|
||||
}
|
||||
|
||||
useResultAsBuffer = true;
|
||||
if(!HardwareEthernetPacket::EncodeFromMessage(*ethmsg, result, report))
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
case Message::Type::RawMessage: {
|
||||
auto raw = std::dynamic_pointer_cast<RawMessage>(message);
|
||||
} // End of Network::Type::Ethernet
|
||||
case Network::Type::CAN:
|
||||
case Network::Type::SWCAN:
|
||||
case Network::Type::LSFTCAN: {
|
||||
auto canmsg = std::dynamic_pointer_cast<CANMessage>(message);
|
||||
if(!canmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed CANMessage
|
||||
}
|
||||
|
||||
// Raw message uses raw->data as the buffer unless directed otherwise
|
||||
buffer = &raw->data;
|
||||
netid = uint16_t(raw->network.getNetID());
|
||||
if(!supportCANFD && canmsg->isCANFD) {
|
||||
report(APIEvent::Type::CANFDNotSupported, APIEvent::Severity::Error);
|
||||
return false; // This device does not support CAN FD
|
||||
}
|
||||
|
||||
useResultAsBuffer = true;
|
||||
if(!HardwareCANPacket::EncodeFromMessage(*canmsg, result, report))
|
||||
return false; // The CANMessage was malformed
|
||||
|
||||
switch(raw->network.getNetID()) {
|
||||
break;
|
||||
} // End of Network::Type::CAN
|
||||
default:
|
||||
switch(message->network.getNetID()) {
|
||||
case Network::NetID::Device:
|
||||
shortFormat = true;
|
||||
break;
|
||||
case Network::NetID::Main51:
|
||||
if(message->data.size() > 0xF) {
|
||||
// Main51 can be sent as a long message without setting the NetID to RED first
|
||||
// Size in long format is the size of the entire packet
|
||||
// So +1 for AA header, +1 for short format header, and +2 for long format size
|
||||
uint16_t size = uint16_t(message->data.size()) + 1 + 1 + 2;
|
||||
size += 1; // Even though we are not including the NetID bytes, the device expects them to be counted in the length
|
||||
message->data.insert(message->data.begin(), {
|
||||
(uint8_t)Network::NetID::Main51, // 0x0B for long message
|
||||
(uint8_t)size, // Size, little endian 16-bit
|
||||
(uint8_t)(size >> 8)
|
||||
});
|
||||
result = packetizer->packetWrap(message->data, shortFormat);
|
||||
return true;
|
||||
} else {
|
||||
shortFormat = true;
|
||||
}
|
||||
break;
|
||||
case Network::NetID::RED_OLDFORMAT: {
|
||||
// See the decoder for an explanation
|
||||
// We expect the network byte to be populated already in data, but not the length
|
||||
uint16_t length = uint16_t(raw->data.size()) - 1;
|
||||
raw->data.insert(raw->data.begin(), {(uint8_t)length, (uint8_t)(length >> 8)});
|
||||
uint16_t length = uint16_t(message->data.size()) - 1;
|
||||
message->data.insert(message->data.begin(), {(uint8_t)length, (uint8_t)(length >> 8)});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::Type::Main51: {
|
||||
auto m51msg = std::dynamic_pointer_cast<Main51Message>(message);
|
||||
if(!m51msg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed Main51Message
|
||||
}
|
||||
|
||||
buffer = &m51msg->data;
|
||||
netid = uint16_t(Network::NetID::Main51);
|
||||
|
||||
if(!m51msg->forceShortFormat) {
|
||||
// Main51 can be sent as a long message without setting the NetID to RED first
|
||||
// Size in long format is the size of the entire packet
|
||||
// So +1 for AA header, +1 for short format header, and +2 for long format size
|
||||
uint16_t size = uint16_t(m51msg->data.size()) + 1 + 1 + 2;
|
||||
size += 1; // Even though we are not including the NetID bytes, the device expects them to be counted in the length
|
||||
size += 1; // Main51 Command
|
||||
m51msg->data.insert(m51msg->data.begin(), {
|
||||
(uint8_t)Network::NetID::Main51, // 0x0B for long message
|
||||
(uint8_t)size, // Size, little endian 16-bit
|
||||
(uint8_t)(size >> 8),
|
||||
(uint8_t)m51msg->command
|
||||
});
|
||||
result = packetizer.packetWrap(m51msg->data, shortFormat);
|
||||
return true;
|
||||
} else {
|
||||
m51msg->data.insert(m51msg->data.begin(), { uint8_t(m51msg->command) });
|
||||
shortFormat = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::Type::EthernetPhyRegister: {
|
||||
if(!supportEthPhy) {
|
||||
report(APIEvent::Type::EthPhyRegisterControlNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
auto ethPhyMessage = std::dynamic_pointer_cast<EthPhyMessage>(message);
|
||||
if(!ethPhyMessage) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(!HardwareEthernetPhyRegisterPacket::EncodeFromMessage(*ethPhyMessage, result, report))
|
||||
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;
|
||||
}
|
||||
|
||||
// Early returns may mean we don't reach this far, check the type you're concerned with
|
||||
if(shortFormat) {
|
||||
buffer->insert(buffer->begin(), (uint8_t(buffer->size()) << 4) | uint8_t(netid));
|
||||
} else {
|
||||
// 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;
|
||||
auto& buffer = useResultAsBuffer ? result : message->data;
|
||||
|
||||
buffer->insert(buffer->begin(), {
|
||||
if(shortFormat) {
|
||||
buffer.insert(buffer.begin(), (uint8_t(buffer.size()) << 4) | uint8_t(message->network.getNetID()));
|
||||
} else {
|
||||
// Size in long format is the size of the entire packet
|
||||
// So +1 for AA header, +1 for short format header, +2 for long format size, and +2 for long format NetID
|
||||
uint16_t size = uint16_t(buffer.size()) + 1 + 1 + 2 + 2;
|
||||
buffer.insert(buffer.begin(), {
|
||||
(uint8_t)Network::NetID::RED, // 0x0C for long message
|
||||
(uint8_t)size, // Size, little endian 16-bit
|
||||
(uint8_t)(size >> 8),
|
||||
(uint8_t)netid, // NetID, little endian 16-bit
|
||||
(uint8_t)(netid >> 8)
|
||||
(uint8_t)message->network.getNetID(), // NetID, little endian 16-bit
|
||||
(uint8_t)(uint16_t(message->network.getNetID()) >> 8)
|
||||
});
|
||||
}
|
||||
|
||||
result = packetizer.packetWrap(*buffer, shortFormat);
|
||||
result = packetizer->packetWrap(buffer, shortFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result, Command cmd, std::vector<uint8_t> arguments) {
|
||||
std::shared_ptr<Message> msg;
|
||||
bool Encoder::encode(std::vector<uint8_t>& result, Command cmd, std::vector<uint8_t> arguments) {
|
||||
auto msg = std::make_shared<Message>();
|
||||
if(cmd == Command::UpdateLEDState) {
|
||||
/* NetID::Device is a super old command type.
|
||||
* It has a leading 0x00 byte, a byte for command, and a byte for an argument.
|
||||
* In this case, command 0x06 is SetLEDState.
|
||||
* This old command type is not really used anywhere else.
|
||||
*/
|
||||
auto canmsg = std::make_shared<RawMessage>(Network::NetID::Device);
|
||||
msg = canmsg;
|
||||
if(arguments.empty()) {
|
||||
if (arguments.empty()) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
canmsg->data.reserve(3);
|
||||
canmsg->data.push_back(0x00);
|
||||
canmsg->data.push_back(0x06); // SetLEDState
|
||||
canmsg->data.push_back(arguments.at(0)); // See Device::LEDState
|
||||
msg->network = Network::NetID::Device;
|
||||
msg->data.reserve(3);
|
||||
msg->data.push_back(0x00);
|
||||
msg->data.push_back(0x06); // SetLEDState
|
||||
msg->data.push_back(arguments.at(0)); // See Device::LEDState
|
||||
} else {
|
||||
auto m51msg = std::make_shared<Main51Message>();
|
||||
msg = m51msg;
|
||||
m51msg->command = cmd;
|
||||
switch(cmd) {
|
||||
case Command::ReadSettings:
|
||||
case Command::RequestSerialNumber:
|
||||
case Command::EnableNetworkCommunication:
|
||||
case Command::EnableNetworkCommunicationEx:
|
||||
case Command::GetMainVersion:
|
||||
case Command::GetSecondaryVersions:
|
||||
case Command::NeoReadMemory:
|
||||
// There is a firmware handling idiosyncrasy with these commands
|
||||
// They must be encoded in the short format
|
||||
m51msg->forceShortFormat = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m51msg->data.insert(m51msg->data.end(), std::make_move_iterator(arguments.begin()), std::make_move_iterator(arguments.end()));
|
||||
msg->network = Network::NetID::Main51;
|
||||
msg->data.reserve(arguments.size() + 1);
|
||||
msg->data.push_back((uint8_t)cmd);
|
||||
msg->data.insert(msg->data.end(), std::make_move_iterator(arguments.begin()), std::make_move_iterator(arguments.end()));
|
||||
}
|
||||
|
||||
return encode(packetizer, result, msg);
|
||||
|
||||
return encode(result, msg);
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
#include "icsneo/communication/ethernetpacketizer.h"
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
const size_t EthernetPacketizer::MaxPacketLength = 1490; // MTU - overhead
|
||||
static const uint8_t BROADCAST_MAC[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
EthernetPacketizer::EthernetPacket& EthernetPacketizer::newSendPacket(bool first) {
|
||||
processedDownPackets.emplace_back();
|
||||
EthernetPacket& ret = processedDownPackets.back();
|
||||
if(first) {
|
||||
ret.packetNumber = sequenceDown++;
|
||||
} else {
|
||||
ret.firstPiece = false;
|
||||
if(processedDownPackets.size() > 1)
|
||||
ret.packetNumber = (processedDownPackets.rbegin() + 1)->packetNumber;
|
||||
else
|
||||
assert(false); // This should never be called with !first if there are no packets in the queue
|
||||
}
|
||||
std::copy(std::begin(hostMAC), std::end(hostMAC), std::begin(ret.srcMAC));
|
||||
std::copy(std::begin(deviceMAC), std::end(deviceMAC), std::begin(ret.destMAC));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EthernetPacketizer::inputDown(std::vector<uint8_t> bytes, bool first) {
|
||||
EthernetPacket* sendPacket = nullptr;
|
||||
if(first && !processedDownPackets.empty()) {
|
||||
// We have some packets already, let's see if we can add this to the last one
|
||||
if(processedDownPackets.back().payload.size() + bytes.size() <= MaxPacketLength)
|
||||
sendPacket = &processedDownPackets.back();
|
||||
}
|
||||
|
||||
if(sendPacket == nullptr)
|
||||
sendPacket = &newSendPacket(first);
|
||||
|
||||
if(sendPacket->payload.empty())
|
||||
sendPacket->payload = std::move(bytes);
|
||||
else
|
||||
sendPacket->payload.insert(sendPacket->payload.end(), bytes.begin(), bytes.end());
|
||||
|
||||
// Split packets larger than MTU
|
||||
std::vector<uint8_t> extraData;
|
||||
if(sendPacket->payload.size() > MaxPacketLength) {
|
||||
extraData.insert(extraData.end(), sendPacket->payload.begin() + MaxPacketLength, sendPacket->payload.end());
|
||||
sendPacket->payload.resize(MaxPacketLength);
|
||||
sendPacket->lastPiece = false;
|
||||
inputDown(std::move(extraData), false);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector< std::vector<uint8_t> > EthernetPacketizer::outputDown() {
|
||||
std::vector< std::vector<uint8_t> > ret;
|
||||
ret.reserve(processedDownPackets.size());
|
||||
for(auto&& packet : std::move(processedDownPackets))
|
||||
ret.push_back(packet.getBytestream());
|
||||
processedDownPackets.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool EthernetPacketizer::inputUp(std::vector<uint8_t> bytes) {
|
||||
EthernetPacket packet(bytes);
|
||||
|
||||
if(packet.errorWhileDecodingFromBytestream)
|
||||
return false; // Bad packet
|
||||
|
||||
if(packet.etherType != 0xCAB2)
|
||||
return false; // Not a packet to host
|
||||
|
||||
if(memcmp(packet.destMAC, hostMAC, sizeof(packet.destMAC)) != 0 &&
|
||||
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) != 0)
|
||||
return false; // Packet is not addressed to us or broadcast
|
||||
|
||||
if(!allowInPacketsFromAnyMAC && memcmp(packet.srcMAC, deviceMAC, sizeof(deviceMAC)) != 0)
|
||||
return false; // Not a packet from the device we're concerned with
|
||||
|
||||
// Handle single packets
|
||||
if(packet.firstPiece && packet.lastPiece) {
|
||||
// Could ensure no out-of-order reassembly by checking reassembing here,
|
||||
// not doing that here because it should be harmless if it ever happened.
|
||||
processedUpBytes.insert(processedUpBytes.end(), std::make_move_iterator(packet.payload.begin()), std::make_move_iterator(packet.payload.end()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(packet.firstPiece) {
|
||||
if(reassembling) {
|
||||
//report(APIEvent::Type::FailedToRead, APIEvent::Severity::EventWarning);
|
||||
reassemblingData.clear();
|
||||
}
|
||||
|
||||
reassembling = true;
|
||||
reassemblingId = packet.packetNumber;
|
||||
reassemblingData = std::move(packet.payload);
|
||||
return !processedUpBytes.empty(); // If there are other packets in the pipe
|
||||
}
|
||||
|
||||
if(!reassembling || reassemblingId != packet.packetNumber) {
|
||||
//report(APIEvent::Type::FailedToRead, APIEvent::Severity::EventWarning);
|
||||
reassembling = false;
|
||||
reassemblingData.clear();
|
||||
return !processedUpBytes.empty(); // If there are other packets in the pipe
|
||||
}
|
||||
|
||||
if(packet.lastPiece) {
|
||||
processedUpBytes.insert(processedUpBytes.end(), std::make_move_iterator(reassemblingData.begin()), std::make_move_iterator(reassemblingData.end()));
|
||||
reassemblingData.clear();
|
||||
reassembling = false;
|
||||
processedUpBytes.insert(processedUpBytes.end(), std::make_move_iterator(packet.payload.begin()), std::make_move_iterator(packet.payload.end()));
|
||||
return true;
|
||||
}
|
||||
|
||||
reassemblingData.insert(reassemblingData.end(), std::make_move_iterator(packet.payload.begin()), std::make_move_iterator(packet.payload.end()));
|
||||
return !processedUpBytes.empty(); // If there are other packets in the pipe
|
||||
}
|
||||
|
||||
std::vector<uint8_t> EthernetPacketizer::outputUp() {
|
||||
std::vector<uint8_t> ret = std::move(processedUpBytes);
|
||||
processedUpBytes.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
EthernetPacketizer::EthernetPacket::EthernetPacket(const std::vector<uint8_t>& bytestream) {
|
||||
loadBytestream(bytestream);
|
||||
}
|
||||
|
||||
EthernetPacketizer::EthernetPacket::EthernetPacket(const uint8_t* data, size_t size) {
|
||||
std::vector<uint8_t> bs(data, data + size);
|
||||
loadBytestream(bs);
|
||||
}
|
||||
|
||||
int EthernetPacketizer::EthernetPacket::loadBytestream(const std::vector<uint8_t>& bytestream) {
|
||||
errorWhileDecodingFromBytestream = 0;
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
destMAC[i] = bytestream[i];
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
srcMAC[i] = bytestream[i + 6];
|
||||
etherType = (bytestream[12] << 8) | bytestream[13];
|
||||
icsEthernetHeader = (bytestream[14] << 24) | (bytestream[15] << 16) | (bytestream[16] << 8) | bytestream[17];
|
||||
payloadSize = bytestream[18] | (bytestream[19] << 8);
|
||||
packetNumber = bytestream[20] | (bytestream[21] << 8);
|
||||
uint16_t packetInfo = bytestream[22] | (bytestream[23] << 8);
|
||||
firstPiece = packetInfo & 1;
|
||||
lastPiece = (packetInfo >> 1) & 1;
|
||||
bufferHalfFull = (packetInfo >> 2) & 2;
|
||||
payload = std::vector<uint8_t>(bytestream.begin() + 24, bytestream.end());
|
||||
size_t payloadActualSize = payload.size();
|
||||
if(payloadActualSize > payloadSize)
|
||||
payload.resize(payloadSize);
|
||||
return errorWhileDecodingFromBytestream;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> EthernetPacketizer::EthernetPacket::getBytestream() const {
|
||||
uint16_t actualPayloadSize = uint16_t(payload.size());
|
||||
std::vector<uint8_t> bytestream;
|
||||
bytestream.reserve(6 + 6 + 2 + 4 + 2 + 2 + 2 + actualPayloadSize);
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
bytestream.push_back(destMAC[i]);
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
bytestream.push_back(srcMAC[i]);
|
||||
// EtherType should be put into the bytestream as big endian
|
||||
bytestream.push_back((uint8_t)(etherType >> 8));
|
||||
bytestream.push_back((uint8_t)(etherType));
|
||||
// Our Ethernet header should be put into the bytestream as big endian
|
||||
bytestream.push_back((uint8_t)(icsEthernetHeader >> 24));
|
||||
bytestream.push_back((uint8_t)(icsEthernetHeader >> 16));
|
||||
bytestream.push_back((uint8_t)(icsEthernetHeader >> 8));
|
||||
bytestream.push_back((uint8_t)(icsEthernetHeader));
|
||||
uint16_t declaredPayloadSize = payloadSize ? payloadSize : actualPayloadSize;
|
||||
// The payload size comes next, it's little endian
|
||||
bytestream.push_back((uint8_t)(declaredPayloadSize));
|
||||
bytestream.push_back((uint8_t)(declaredPayloadSize >> 8));
|
||||
// Packet number is little endian
|
||||
bytestream.push_back((uint8_t)(packetNumber));
|
||||
bytestream.push_back((uint8_t)(packetNumber >> 8));
|
||||
// Packet info gets assembled into a bitfield
|
||||
uint16_t packetInfo = 0;
|
||||
packetInfo |= firstPiece & 1;
|
||||
packetInfo |= (lastPiece & 1) << 1;
|
||||
packetInfo |= (bufferHalfFull & 1) << 2;
|
||||
packetInfo |= 1 << 8; // Protocol version 1
|
||||
bytestream.push_back((uint8_t)(packetInfo));
|
||||
bytestream.push_back((uint8_t)(packetInfo >> 8));
|
||||
bytestream.insert(bytestream.end(), payload.begin(), payload.end());
|
||||
return bytestream;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "icsneo/communication/icommunication.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
bool ICommunication::read(std::vector<uint8_t>& bytes, size_t limit) {
|
||||
// A limit of zero indicates no limit
|
||||
if(limit == 0)
|
||||
limit = (size_t)-1;
|
||||
|
||||
if(limit > (readQueue.size_approx() + 4))
|
||||
limit = (readQueue.size_approx() + 4);
|
||||
|
||||
if(bytes.capacity() < limit)
|
||||
bytes.resize(limit);
|
||||
|
||||
size_t actuallyRead = readQueue.try_dequeue_bulk(bytes.data(), limit);
|
||||
|
||||
if(bytes.size() > actuallyRead)
|
||||
bytes.resize(actuallyRead);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ICommunication::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
|
||||
// A limit of zero indicates no limit
|
||||
if(limit == 0)
|
||||
limit = (size_t)-1;
|
||||
|
||||
if(limit > (readQueue.size_approx() + 4))
|
||||
limit = (readQueue.size_approx() + 4);
|
||||
|
||||
bytes.resize(limit);
|
||||
|
||||
size_t actuallyRead = readQueue.wait_dequeue_bulk_timed(bytes.data(), limit, timeout);
|
||||
|
||||
bytes.resize(actuallyRead);
|
||||
|
||||
return actuallyRead > 0;
|
||||
}
|
||||
|
||||
bool ICommunication::write(const std::vector<uint8_t>& bytes) {
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(writeBlocks) {
|
||||
std::unique_lock<std::mutex> lk(writeMutex);
|
||||
if(writeQueue.size_approx() > writeQueueSize)
|
||||
writeCV.wait(lk);
|
||||
} else {
|
||||
if(writeQueue.size_approx() > writeQueueSize) {
|
||||
report(APIEvent::Type::TransmitBufferFull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ret = writeQueue.enqueue(WriteOperation(bytes));
|
||||
if(!ret)
|
||||
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
|
||||
#include "icsneo/device/tree/rada2b/rada2b.h"
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(closed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(message->type != Message::Type::Frame) {
|
||||
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 {
|
||||
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);
|
||||
stream->seekp(4, std::ios::beg);
|
||||
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
|
||||
|
||||
closed = true;
|
||||
}
|
||||
|
||||
bool A2BWAVOutput::writeCurrentBuffer() const {
|
||||
|
||||
if(!stream->write(reinterpret_cast<const char*>(wavBuffer.data()), wavBufferOffset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wavBufferOffset = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#include "icsneo/communication/message/ethphymessage.h"
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
bool EthPhyMessage::appendPhyMessage(bool writeEnable, bool clause45, uint8_t phyAddrOrPort, uint8_t pageOrDevice, uint16_t regAddr, uint16_t regVal, bool enabled)
|
||||
{
|
||||
auto msg = std::make_shared<PhyMessage>();
|
||||
msg->Clause45Enable = clause45;
|
||||
msg->Enabled = enabled;
|
||||
msg->WriteEnable = writeEnable;
|
||||
msg->version = 1u;
|
||||
if( (FiveBits < phyAddrOrPort) ||
|
||||
(clause45 && (FiveBits < pageOrDevice)) ||
|
||||
(!clause45 && (FiveBits < regAddr)) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(clause45)
|
||||
{
|
||||
msg->clause45.port = phyAddrOrPort;
|
||||
msg->clause45.device = pageOrDevice;
|
||||
msg->clause45.regAddr = regAddr;
|
||||
msg->clause45.regVal = regVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->clause22.phyAddr = phyAddrOrPort;
|
||||
msg->clause22.page = pageOrDevice;
|
||||
msg->clause22.regAddr = regAddr;
|
||||
msg->clause22.regVal = regVal;
|
||||
}
|
||||
return appendPhyMessage(msg);
|
||||
}
|
||||
|
||||
bool EthPhyMessage::appendPhyMessage(std::shared_ptr<PhyMessage> message)
|
||||
{
|
||||
if(message != nullptr)
|
||||
{
|
||||
messages.push_back(message);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t EthPhyMessage::getMessageCount() const
|
||||
{
|
||||
return messages.size();
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,10 @@
|
||||
#include <cstring> // memcpy
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::vector<uint8_t> FlexRayControlMessage::BuildBaseControlArgs(uint8_t controller, FlexRay::Opcode op, const std::vector<uint8_t>& args) {
|
||||
std::vector<uint8_t> FlexRayControlMessage::BuildBaseControlArgs(uint8_t controller, FlexRay::Opcode op, std::initializer_list<uint8_t> args) {
|
||||
std::vector<uint8_t> ret;
|
||||
ret.reserve(args.size() + 4);
|
||||
ret.push_back(controller);
|
||||
@@ -29,7 +28,7 @@ std::vector<uint8_t> FlexRayControlMessage::BuildReadCCRegsArgs(uint8_t controll
|
||||
|
||||
std::vector<uint8_t> FlexRayControlMessage::BuildWriteCCRegArgs(uint8_t controller, uint16_t address, uint32_t value) {
|
||||
address /= 4;
|
||||
return BuildBaseControlArgs(controller, FlexRay::Opcode::WriteCCReg, {
|
||||
return BuildBaseControlArgs(controller, FlexRay::Opcode::ReadCCRegs, {
|
||||
uint8_t(address),
|
||||
uint8_t(address >> 8),
|
||||
uint8_t(value),
|
||||
@@ -39,43 +38,18 @@ std::vector<uint8_t> FlexRayControlMessage::BuildWriteCCRegArgs(uint8_t controll
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<uint8_t> FlexRayControlMessage::BuildAddConfiguredTxMessageArgs(
|
||||
uint8_t controller, uint16_t descriptionId, uint16_t slotId, uint8_t baseCycle, uint8_t cycleReps, FlexRay::Channel channel) {
|
||||
return BuildBaseControlArgs(controller, FlexRay::Opcode::AddConfiguredTxMessage, {
|
||||
uint8_t(descriptionId),
|
||||
uint8_t(descriptionId >> 8),
|
||||
uint8_t(slotId),
|
||||
uint8_t(slotId >> 8),
|
||||
uint8_t(baseCycle + cycleReps),
|
||||
uint8_t(channel)
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<uint8_t> FlexRayControlMessage::BuildWriteMessageBufferArgs(
|
||||
uint8_t controller, uint16_t bufferId, const std::vector<uint8_t>& data, uint16_t desiredSize) {
|
||||
desiredSize += desiredSize % 4; // Must be a multiple of 4
|
||||
std::vector<uint8_t> args = {
|
||||
uint8_t(bufferId),
|
||||
uint8_t(desiredSize / 4)
|
||||
};
|
||||
args.insert(args.end(), data.begin(), data.end());
|
||||
if((int)args.size() != desiredSize + 2)
|
||||
args.resize(desiredSize + 2);
|
||||
return BuildBaseControlArgs(controller, FlexRay::Opcode::WriteMessageBuffer, args);
|
||||
}
|
||||
|
||||
FlexRayControlMessage::FlexRayControlMessage(const Packet& packet) : Message(Message::Type::FlexRayControl) {
|
||||
FlexRayControlMessage::FlexRayControlMessage(const Packet& packet) : Message() {
|
||||
if(packet.data.size() < 2)
|
||||
return; // huh?
|
||||
controller = packet.data[0];
|
||||
if(controller >= 2)
|
||||
if(controller < 2)
|
||||
return; // Invalid controller
|
||||
|
||||
// Opcode is only ReadCCStatus or ReadCCRegs for the moment
|
||||
opcode = FlexRay::Opcode(packet.data[1]);
|
||||
if(opcode != FlexRay::Opcode::ReadCCRegs && opcode != FlexRay::Opcode::ReadCCStatus)
|
||||
return;
|
||||
|
||||
|
||||
// Read out registers
|
||||
size_t bytes = packet.data.size() - 2;
|
||||
const size_t count = bytes / sizeof(uint32_t);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,206 +1,77 @@
|
||||
#include "icsneo/communication/message/neomessage.h"
|
||||
#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;
|
||||
|
||||
neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
|
||||
// This function is not responsible for storing the message!
|
||||
// Keep the shared_ptr around for the lifetime of the data access
|
||||
const auto type = message->network.getType();
|
||||
neomessage_t neomsg = {}; // Clear out the memory
|
||||
neomsg.messageType = (neomessagetype_t)message->type;
|
||||
neomsg.netid = (uint32_t)message->network.getNetID();
|
||||
neomsg.type = (uint8_t)type;
|
||||
neomsg.length = message->data.size();
|
||||
neomsg.data = message->data.data();
|
||||
neomsg.timestamp = message->timestamp;
|
||||
switch (message->type)
|
||||
{
|
||||
case Message::Type::Frame: {
|
||||
neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg;
|
||||
auto framemsg = std::static_pointer_cast<Frame>(message);
|
||||
const auto netType = framemsg->network.getType();
|
||||
neomsg.status.globalError = message->error;
|
||||
neomsg.status.transmitMessage = message->transmitted;
|
||||
|
||||
frame.netid = (neonetid_t)framemsg->network.getNetID();
|
||||
frame.type = (neonettype_t)netType;
|
||||
frame.description = framemsg->description;
|
||||
frame.length = framemsg->data.size();
|
||||
frame.data = framemsg->data.data();
|
||||
frame.timestamp = framemsg->timestamp;
|
||||
frame.status.globalError = framemsg->error;
|
||||
frame.status.transmitMessage = framemsg->transmitted;
|
||||
|
||||
switch(netType) {
|
||||
case Network::Type::CAN:
|
||||
case Network::Type::SWCAN:
|
||||
case Network::Type::LSFTCAN: {
|
||||
neomessage_can_t& can = *(neomessage_can_t*)&neomsg;
|
||||
auto canmsg = std::static_pointer_cast<CANMessage>(message);
|
||||
can.arbid = canmsg->arbid;
|
||||
can.dlcOnWire = canmsg->dlcOnWire;
|
||||
can.status.extendedFrame = canmsg->isExtended;
|
||||
can.status.remoteFrame = canmsg->isRemote;
|
||||
can.status.canfdRTR = canmsg->isRemote;
|
||||
can.status.canfdFDF = canmsg->isCANFD;
|
||||
can.status.canfdBRS = canmsg->baudrateSwitch;
|
||||
can.status.canfdESI = canmsg->errorStateIndicator;
|
||||
break;
|
||||
}
|
||||
case Network::Type::Ethernet: {
|
||||
neomessage_eth_t& eth = *(neomessage_eth_t*)&neomsg;
|
||||
auto ethmsg = std::static_pointer_cast<EthernetMessage>(message);
|
||||
eth.preemptionFlags = ethmsg->preemptionFlags;
|
||||
eth.status.incompleteFrame = ethmsg->frameTooShort;
|
||||
// TODO Fill in extra status bits
|
||||
//eth.status.xyz = ethmsg->preemptionEnabled;
|
||||
//eth.status.xyz = ethmsg->fcsAvailable;
|
||||
//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;
|
||||
switch(type) {
|
||||
case Network::Type::CAN:
|
||||
case Network::Type::SWCAN:
|
||||
case Network::Type::LSFTCAN: {
|
||||
neomessage_can_t& can = *(neomessage_can_t*)&neomsg;
|
||||
auto canmsg = std::static_pointer_cast<CANMessage>(message);
|
||||
can.arbid = canmsg->arbid;
|
||||
can.dlcOnWire = canmsg->dlcOnWire;
|
||||
can.status.extendedFrame = canmsg->isExtended;
|
||||
can.status.remoteFrame = canmsg->isRemote;
|
||||
can.status.canfdRTR = canmsg->isRemote;
|
||||
can.status.canfdFDF = canmsg->isCANFD;
|
||||
can.status.canfdBRS = canmsg->baudrateSwitch;
|
||||
can.status.canfdESI = canmsg->errorStateIndicator;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Message::Type::CANErrorCount: {
|
||||
neomessage_can_error_t& canerror = *(neomessage_can_error_t*)&neomsg;
|
||||
auto canerrormsg = std::static_pointer_cast<CANErrorCountMessage>(message);
|
||||
canerror.transmitErrorCount = canerrormsg->transmitErrorCount;
|
||||
canerror.receiveErrorCount = canerrormsg->receiveErrorCount;
|
||||
canerror.status.canBusOff = canerrormsg->busOff;
|
||||
canerror.netid = (neonetid_t)canerrormsg->network.getNetID();
|
||||
canerror.type = (neonettype_t)canerrormsg->network.getType();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
case Network::Type::Ethernet: {
|
||||
neomessage_eth_t& eth = *(neomessage_eth_t*)&neomsg;
|
||||
auto ethmsg = std::static_pointer_cast<EthernetMessage>(message);
|
||||
eth.preemptionFlags = ethmsg->preemptionFlags;
|
||||
eth.status.incompleteFrame = ethmsg->frameTooShort;
|
||||
// TODO Fill in extra status bits
|
||||
//eth.status.xyz = ethmsg->preemptionEnabled;
|
||||
//eth.status.xyz = ethmsg->fcsAvailable;
|
||||
//eth.status.xyz = ethmsg->noPadding;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO Implement others
|
||||
break;
|
||||
}
|
||||
|
||||
return neomsg;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> icsneo::CreateMessageFromNeoMessage(const neomessage_t* neomessage) {
|
||||
switch((Message::Type)neomessage->messageType) {
|
||||
case Message::Type::Frame: {
|
||||
const Network network = ((neomessage_frame_t*)neomessage)->netid;
|
||||
switch(network.getType()) {
|
||||
case Network::Type::CAN:
|
||||
case Network::Type::SWCAN:
|
||||
case Network::Type::LSFTCAN: {
|
||||
neomessage_can_t& can = *(neomessage_can_t*)neomessage;
|
||||
auto canmsg = std::make_shared<CANMessage>();
|
||||
canmsg->network = network;
|
||||
canmsg->description = can.description;
|
||||
canmsg->data.insert(canmsg->data.end(), can.data, can.data + can.length);
|
||||
canmsg->arbid = can.arbid;
|
||||
canmsg->dlcOnWire = can.dlcOnWire;
|
||||
canmsg->isExtended = can.status.extendedFrame;
|
||||
canmsg->isRemote = can.status.remoteFrame | can.status.canfdRTR;
|
||||
canmsg->isCANFD = can.status.canfdFDF;
|
||||
canmsg->baudrateSwitch = can.status.canfdBRS;
|
||||
canmsg->errorStateIndicator = can.status.canfdESI;
|
||||
return canmsg;
|
||||
}
|
||||
case Network::Type::Ethernet: {
|
||||
neomessage_eth_t& eth = *(neomessage_eth_t*)neomessage;
|
||||
auto ethmsg = std::make_shared<EthernetMessage>();
|
||||
ethmsg->network = network;
|
||||
ethmsg->description = eth.description;
|
||||
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;
|
||||
const Network network = neomessage->netid;
|
||||
switch(network.getType()) {
|
||||
case Network::Type::CAN:
|
||||
case Network::Type::SWCAN:
|
||||
case Network::Type::LSFTCAN: {
|
||||
neomessage_can_t& can = *(neomessage_can_t*)neomessage;
|
||||
auto canmsg = std::make_shared<CANMessage>();
|
||||
canmsg->network = network;
|
||||
canmsg->data.insert(canmsg->data.end(), can.data, can.data + can.length);
|
||||
canmsg->arbid = can.arbid;
|
||||
canmsg->isExtended = can.status.extendedFrame;
|
||||
canmsg->isRemote = can.status.remoteFrame | can.status.canfdRTR;
|
||||
canmsg->isCANFD = can.status.canfdFDF;
|
||||
canmsg->baudrateSwitch = can.status.canfdBRS;
|
||||
canmsg->errorStateIndicator = can.status.canfdESI;
|
||||
return canmsg;
|
||||
}
|
||||
default: break;
|
||||
default:
|
||||
// TODO Implement others
|
||||
return std::shared_ptr<Message>();
|
||||
}
|
||||
return std::shared_ptr<Message>();
|
||||
|
||||
}
|
||||
@@ -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,34 +2,19 @@
|
||||
#include "icsneo/communication/command.h"
|
||||
#include "icsneo/communication/decoder.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
MultiChannelCommunication::MultiChannelCommunication(device_eventhandler_t err, std::unique_ptr<Driver> com,
|
||||
std::function<std::unique_ptr<Packetizer>()> makeConfiguredPacketizer, std::unique_ptr<Encoder> e,
|
||||
std::unique_ptr<Decoder> md, size_t vnetCount) :
|
||||
Communication(err, std::move(com), makeConfiguredPacketizer, std::move(e), std::move(md)), numVnets(vnetCount) {
|
||||
vnetThreads.resize(numVnets);
|
||||
vnetQueues.resize(numVnets);
|
||||
}
|
||||
|
||||
void MultiChannelCommunication::spawnThreads() {
|
||||
for(size_t i = 0; i < numVnets; i++) {
|
||||
while(vnetQueues[i].pop()) {} // Ensure the queue is empty
|
||||
vnetThreads[i] = std::thread(&MultiChannelCommunication::vnetReadTask, this, i);
|
||||
}
|
||||
hidReadThread = std::thread(&MultiChannelCommunication::hidReadTask, this);
|
||||
mainChannelReadThread = std::thread(&MultiChannelCommunication::readTask, this);
|
||||
}
|
||||
|
||||
void MultiChannelCommunication::joinThreads() {
|
||||
closing = true;
|
||||
if(hidReadThread.joinable())
|
||||
hidReadThread.join();
|
||||
for(auto& thread : vnetThreads) {
|
||||
if(thread.joinable())
|
||||
thread.join();
|
||||
}
|
||||
if(mainChannelReadThread.joinable())
|
||||
mainChannelReadThread.join();
|
||||
closing = false;
|
||||
}
|
||||
|
||||
@@ -38,7 +23,7 @@ bool MultiChannelCommunication::sendPacket(std::vector<uint8_t>& bytes) {
|
||||
return rawWrite(bytes);
|
||||
}
|
||||
|
||||
void MultiChannelCommunication::hidReadTask() {
|
||||
void MultiChannelCommunication::readTask() {
|
||||
bool readMore = true;
|
||||
bool gotPacket = false; // Have we got the first valid packet (don't flag errors otherwise)
|
||||
std::deque<uint8_t> usbReadFifo;
|
||||
@@ -50,7 +35,7 @@ void MultiChannelCommunication::hidReadTask() {
|
||||
while(!closing) {
|
||||
if(readMore) {
|
||||
readBytes.clear();
|
||||
if(driver->readWait(readBytes)) {
|
||||
if(impl->readWait(readBytes)) {
|
||||
readMore = false;
|
||||
usbReadFifo.insert(usbReadFifo.end(), std::make_move_iterator(readBytes.begin()), std::make_move_iterator(readBytes.end()));
|
||||
}
|
||||
@@ -66,8 +51,7 @@ void MultiChannelCommunication::hidReadTask() {
|
||||
|
||||
if(!CommandTypeIsValid(currentCommandType)) {
|
||||
// Device to host bytes discarded
|
||||
if(gotPacket)
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::FailedToRead, APIEvent::Severity::Error));
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::FailedToRead, APIEvent::Severity::Error));
|
||||
usbReadFifo.pop_front();
|
||||
continue;
|
||||
}
|
||||
@@ -107,7 +91,7 @@ void MultiChannelCommunication::hidReadTask() {
|
||||
case PreprocessState::GetData:
|
||||
state = PreprocessState::GetData; // Set state in case we've fallen through, but later need to go around again
|
||||
|
||||
if(usbReadFifo.size() < currentReadIndex + currentCommandLength) { // Come back when we have more data
|
||||
if(usbReadFifo.size() <= currentReadIndex + currentCommandLength) { // Come back we have more data
|
||||
readMore = true;
|
||||
continue;
|
||||
}
|
||||
@@ -121,67 +105,21 @@ void MultiChannelCommunication::hidReadTask() {
|
||||
payloadBytes[i] = usbReadFifo[0];
|
||||
usbReadFifo.pop_front();
|
||||
}
|
||||
|
||||
if(packetizer->input(payloadBytes)) {
|
||||
for(auto& packet : packetizer->output()) {
|
||||
std::shared_ptr<Message> msg;
|
||||
if(!decoder->decode(msg, packet))
|
||||
continue; // Error will have been reported from within decoder
|
||||
|
||||
moodycamel::BlockingReaderWriterQueue< std::vector<uint8_t> >* currentQueue = nullptr;
|
||||
switch(currentCommandType) {
|
||||
case CommandType::Vnet1_to_HostPC:
|
||||
currentQueue = &vnetQueues[0];
|
||||
break;
|
||||
case CommandType::Vnet2_to_HostPC:
|
||||
if(numVnets >= 2)
|
||||
currentQueue = &vnetQueues[1];
|
||||
break;
|
||||
case CommandType::Vnet3_to_HostPC:
|
||||
if(numVnets >= 3)
|
||||
currentQueue = &vnetQueues[2];
|
||||
break;
|
||||
case CommandType::SDCC1_to_HostPC: {
|
||||
auto msg = std::make_shared<NeoReadMemorySDMessage>();
|
||||
std::swap(msg->data, payloadBytes);
|
||||
gotPacket = true;
|
||||
dispatchMessage(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentQueue == nullptr) {
|
||||
state = PreprocessState::SearchForCommand;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!currentQueue->enqueue(std::move(payloadBytes)) && gotPacket)
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::FailedToRead, APIEvent::Severity::Error));
|
||||
payloadBytes.clear();
|
||||
gotPacket = true;
|
||||
state = PreprocessState::SearchForCommand;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiChannelCommunication::vnetReadTask(size_t vnetIndex) {
|
||||
moodycamel::BlockingReaderWriterQueue< std::vector<uint8_t> >& queue = vnetQueues[vnetIndex];
|
||||
std::vector<uint8_t> payloadBytes;
|
||||
std::unique_ptr<Packetizer> packetizerLifetime;
|
||||
Packetizer* vnetPacketizer;
|
||||
if(vnetIndex == 0)
|
||||
vnetPacketizer = packetizer.get();
|
||||
else {
|
||||
packetizerLifetime = makeConfiguredPacketizer();
|
||||
vnetPacketizer = packetizerLifetime.get();
|
||||
}
|
||||
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
|
||||
while(!closing) {
|
||||
if(queue.wait_dequeue_timed(payloadBytes, std::chrono::milliseconds(250))) {
|
||||
if(closing)
|
||||
break;
|
||||
|
||||
auto& ringBuffer = driver->getReadBuffer();
|
||||
ringBuffer.write(payloadBytes);
|
||||
|
||||
handleInput(*vnetPacketizer);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
const size_t HardwareA2BPacket::a2bMessageMaxLength = sizeof(HardwareA2BPacket) + 1024;
|
||||
|
||||
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
|
||||
if(bytestream.size() < sizeof(HardwareA2BPacket))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const HardwareA2BPacket* data = (const HardwareA2BPacket*)bytestream.data();
|
||||
|
||||
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - sizeof(HardwareA2BPacket); // First 28 bytes are message header.
|
||||
|
||||
if(totalPackedLength == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>();
|
||||
|
||||
msg->numChannels = data->header.channelNum;
|
||||
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());
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& /*report*/) {
|
||||
constexpr size_t a2btxMessageHeaderSize = 6;
|
||||
|
||||
size_t audioBufferSize = message.data.size();
|
||||
size_t totalSize = a2btxMessageHeaderSize + audioBufferSize;
|
||||
|
||||
bytestream.resize(totalSize, 0);
|
||||
uint32_t offset = 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);
|
||||
|
||||
std::copy(message.data.begin(), message.data.end(), bytestream.begin() + offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+149
-139
@@ -1,128 +1,82 @@
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static std::optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
|
||||
if (length <= 8)
|
||||
return length;
|
||||
|
||||
if (fd) {
|
||||
switch(length) {
|
||||
case 0x9:
|
||||
return uint8_t(12);
|
||||
case 0xa:
|
||||
return uint8_t(16);
|
||||
case 0xb:
|
||||
return uint8_t(20);
|
||||
case 0xc:
|
||||
return uint8_t(24);
|
||||
case 0xd:
|
||||
return uint8_t(32);
|
||||
case 0xe:
|
||||
return uint8_t(48);
|
||||
case 0xf:
|
||||
return uint8_t(64);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static std::optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
|
||||
{
|
||||
if (dataLength <= 8)
|
||||
return uint8_t(dataLength);
|
||||
|
||||
if (fd) {
|
||||
if (dataLength <= 12)
|
||||
return uint8_t(0x9);
|
||||
else if (dataLength <= 16)
|
||||
return uint8_t(0xA);
|
||||
else if (dataLength <= 20)
|
||||
return uint8_t(0xB);
|
||||
else if (dataLength <= 24)
|
||||
return uint8_t(0xC);
|
||||
else if (dataLength <= 32)
|
||||
return uint8_t(0xD);
|
||||
else if (dataLength <= 48)
|
||||
return uint8_t(0xE);
|
||||
else if (dataLength <= 64)
|
||||
return uint8_t(0xF);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
std::shared_ptr<CANMessage> HardwareCANPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
const HardwareCANPacket* data = (const HardwareCANPacket*)bytestream.data();
|
||||
|
||||
if(data->dlc.RB1) { // Change counts reporting
|
||||
auto msg = std::make_shared<CANMessage>();
|
||||
|
||||
const bool busOff = data->data[0] & 0b00100000;
|
||||
// Arb ID
|
||||
if(data->header.IDE) { // Extended 29-bit ID
|
||||
msg->arbid = (data->header.SID & 0x7ff) << 18;
|
||||
msg->arbid |= (data->eid.EID & 0xfff) << 6;
|
||||
msg->arbid |= (data->dlc.EID2 & 0x3f);
|
||||
msg->isExtended = true;
|
||||
} else { // Standard 11-bit ID
|
||||
msg->arbid = data->header.SID;
|
||||
}
|
||||
|
||||
auto msg = std::make_shared<CANErrorCountMessage>(data->data[2], data->data[1], busOff);
|
||||
// This timestamp is raw off the device (in timestampResolution increments)
|
||||
// Decoder will fix as it has information about the timestampResolution increments
|
||||
msg->timestamp = data->timestamp.TS;
|
||||
|
||||
// This timestamp is raw off the device (in timestampResolution increments)
|
||||
// Decoder will fix as it has information about the timestampResolution increments
|
||||
msg->timestamp = data->timestamp.TS;
|
||||
|
||||
return msg;
|
||||
|
||||
} else { // CAN Frame
|
||||
auto msg = std::make_shared<CANMessage>();
|
||||
|
||||
// Arb ID
|
||||
if(data->header.IDE) { // Extended 29-bit ID
|
||||
msg->arbid = (data->header.SID & 0x7ff) << 18;
|
||||
msg->arbid |= (data->eid.EID & 0xfff) << 6;
|
||||
msg->arbid |= (data->dlc.EID2 & 0x3f);
|
||||
msg->isExtended = true;
|
||||
} else { // Standard 11-bit ID
|
||||
msg->arbid = data->header.SID;
|
||||
}
|
||||
|
||||
// This timestamp is raw off the device (in timestampResolution increments)
|
||||
// Decoder will fix as it has information about the timestampResolution increments
|
||||
msg->timestamp = data->timestamp.TS;
|
||||
|
||||
// DLC
|
||||
uint8_t length = data->dlc.DLC;
|
||||
msg->dlcOnWire = length; // This will hold the real DLC on wire 0x0 - 0xF
|
||||
if(data->header.EDL && data->timestamp.IsExtended) { // CAN FD
|
||||
msg->isCANFD = true;
|
||||
msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch
|
||||
msg->errorStateIndicator = data->header.ESI;
|
||||
const std::optional<uint8_t> lenFromDLC = CAN_DLCToLength(length, true);
|
||||
if (lenFromDLC)
|
||||
length = *lenFromDLC;
|
||||
} else if(length > 8) { // This is a standard CAN frame with a length of more than 8
|
||||
// Yes, this is possible. On the wire, the length field is a nibble, and we do want to return an accurate value
|
||||
// We don't want to overread our buffer, though, so make sure we cap the length
|
||||
length = 8;
|
||||
}
|
||||
|
||||
// Data
|
||||
// The first 8 bytes are always in the standard place
|
||||
if((data->dlc.RTR && data->header.IDE) || (!data->header.IDE && data->header.SRR)) { // Remote Request Frame
|
||||
msg->data.resize(length); // This data will be all zeros, but the length will be set
|
||||
msg->isRemote = true;
|
||||
} else {
|
||||
msg->data.reserve(length);
|
||||
msg->data.insert(msg->data.end(), data->data, data->data + (length > 8 ? 8 : length));
|
||||
if(length > 8) { // If there are more than 8 bytes, they come at the end of the message
|
||||
// Messages with extra data are formatted as message, then uint16_t netid, then uint16_t length, then extra data
|
||||
const auto extraDataStart = bytestream.begin() + sizeof(HardwareCANPacket) + 2 + 2;
|
||||
msg->data.insert(msg->data.end(), extraDataStart, extraDataStart + (length - 8));
|
||||
// DLC
|
||||
uint8_t length = data->dlc.DLC;
|
||||
msg->dlcOnWire = length; // This will hold the real DLC on wire 0x0 - 0xF
|
||||
if(data->header.EDL && data->timestamp.IsExtended) { // CAN FD
|
||||
msg->isCANFD = true;
|
||||
msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch
|
||||
msg->errorStateIndicator = data->header.ESI;
|
||||
if(length > 8) {
|
||||
switch(length) { // CAN FD Length Decoding
|
||||
case 0x9:
|
||||
length = 12;
|
||||
break;
|
||||
case 0xa:
|
||||
length = 16;
|
||||
break;
|
||||
case 0xb:
|
||||
length = 20;
|
||||
break;
|
||||
case 0xc:
|
||||
length = 24;
|
||||
break;
|
||||
case 0xd:
|
||||
length = 32;
|
||||
break;
|
||||
case 0xe:
|
||||
length = 48;
|
||||
break;
|
||||
case 0xf:
|
||||
length = 64;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
msg->transmitted = data->eid.TXMSG;
|
||||
msg->error = data->eid.TXAborted || data->eid.TXError || data->eid.TXLostArb;
|
||||
msg->description = data->stats;
|
||||
|
||||
return msg;
|
||||
} else if(length > 8) { // This is a standard CAN frame with a length of more than 8
|
||||
// Yes, this is possible. On the wire, the length field is a nibble, and we do want to return an accurate value
|
||||
// We don't want to overread our buffer, though, so make sure we cap the length
|
||||
length = 8;
|
||||
}
|
||||
|
||||
// Data
|
||||
// The first 8 bytes are always in the standard place
|
||||
if((data->dlc.RTR && data->header.IDE) || (!data->header.IDE && data->header.SRR)) { // Remote Request Frame
|
||||
msg->data.resize(length); // This data will be all zeros, but the length will be set
|
||||
msg->isRemote = true;
|
||||
} else {
|
||||
msg->data.reserve(length);
|
||||
msg->data.insert(msg->data.end(), data->data, data->data + (length > 8 ? 8 : length));
|
||||
if(length > 8) { // If there are more than 8 bytes, they come at the end of the message
|
||||
// Messages with extra data are formatted as message, then uint16_t netid, then uint16_t length, then extra data
|
||||
const auto extraDataStart = bytestream.begin() + sizeof(HardwareCANPacket) + 2 + 2;
|
||||
msg->data.insert(msg->data.end(), extraDataStart, extraDataStart + (length - 8));
|
||||
}
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool HardwareCANPacket::EncodeFromMessage(const CANMessage& message, std::vector<uint8_t>& result, const device_eventhandler_t& report) {
|
||||
@@ -132,38 +86,93 @@ bool HardwareCANPacket::EncodeFromMessage(const CANMessage& message, std::vector
|
||||
}
|
||||
|
||||
const size_t dataSize = message.data.size();
|
||||
std::optional<uint8_t> dlc = CAN_LengthToDLC(dataSize, message.isCANFD);
|
||||
if (!dlc.has_value()) {
|
||||
if(dataSize > 64 || (dataSize > 8 && !message.isCANFD)) {
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false; // Too much data for the protocol
|
||||
}
|
||||
|
||||
if (message.dlcOnWire != 0) {
|
||||
if(message.dlcOnWire > 0xf) {
|
||||
// The DLC is only a nibble
|
||||
// It is actually possible to transmit a standard CAN frame with a DLC > 8
|
||||
// While it is invalid, most controllers will still pass along the received
|
||||
// frame and 8 bytes of data, so it may be desirable to test behavior with
|
||||
// these frames. We let you do it if you set `message.dlcOnWire` for transmit.
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false;
|
||||
uint8_t lengthNibble = uint8_t(message.data.size());
|
||||
uint8_t paddingBytes = 0;
|
||||
if(lengthNibble > 8) {
|
||||
switch(lengthNibble) {
|
||||
case 9: paddingBytes++;
|
||||
case 10: paddingBytes++;
|
||||
case 11: paddingBytes++;
|
||||
case 12:
|
||||
lengthNibble = 0x9;
|
||||
break;
|
||||
case 13: paddingBytes++;
|
||||
case 14: paddingBytes++;
|
||||
case 15: paddingBytes++;
|
||||
case 16:
|
||||
lengthNibble = 0xA;
|
||||
break;
|
||||
case 17: paddingBytes++;
|
||||
case 18: paddingBytes++;
|
||||
case 19: paddingBytes++;
|
||||
case 20:
|
||||
lengthNibble = 0xB;
|
||||
break;
|
||||
case 21: paddingBytes++;
|
||||
case 22: paddingBytes++;
|
||||
case 23: paddingBytes++;
|
||||
case 24:
|
||||
lengthNibble = 0xC;
|
||||
break;
|
||||
case 25: paddingBytes++;
|
||||
case 26: paddingBytes++;
|
||||
case 27: paddingBytes++;
|
||||
case 28: paddingBytes++;
|
||||
case 29: paddingBytes++;
|
||||
case 30: paddingBytes++;
|
||||
case 31: paddingBytes++;
|
||||
case 32:
|
||||
lengthNibble = 0xD;
|
||||
break;
|
||||
case 33: paddingBytes++;
|
||||
case 34: paddingBytes++;
|
||||
case 35: paddingBytes++;
|
||||
case 36: paddingBytes++;
|
||||
case 37: paddingBytes++;
|
||||
case 38: paddingBytes++;
|
||||
case 39: paddingBytes++;
|
||||
case 40: paddingBytes++;
|
||||
case 41: paddingBytes++;
|
||||
case 42: paddingBytes++;
|
||||
case 43: paddingBytes++;
|
||||
case 44: paddingBytes++;
|
||||
case 45: paddingBytes++;
|
||||
case 46: paddingBytes++;
|
||||
case 47: paddingBytes++;
|
||||
case 48:
|
||||
lengthNibble = 0xE;
|
||||
break;
|
||||
case 49: paddingBytes++;
|
||||
case 50: paddingBytes++;
|
||||
case 51: paddingBytes++;
|
||||
case 52: paddingBytes++;
|
||||
case 53: paddingBytes++;
|
||||
case 54: paddingBytes++;
|
||||
case 55: paddingBytes++;
|
||||
case 56: paddingBytes++;
|
||||
case 57: paddingBytes++;
|
||||
case 58: paddingBytes++;
|
||||
case 59: paddingBytes++;
|
||||
case 60: paddingBytes++;
|
||||
case 61: paddingBytes++;
|
||||
case 62: paddingBytes++;
|
||||
case 63: paddingBytes++;
|
||||
case 64:
|
||||
lengthNibble = 0xF;
|
||||
break;
|
||||
default:
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false; // CAN FD frame may have had an incorrect byte count
|
||||
}
|
||||
|
||||
if (message.dlcOnWire < *dlc) {
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (message.dlcOnWire > *dlc)
|
||||
dlc = message.dlcOnWire;
|
||||
}
|
||||
|
||||
// The only way this fails is if we're transmitting a DLC > 8 on standard CAN
|
||||
const uint8_t paddedLength = CAN_DLCToLength(*dlc, message.isCANFD).value_or(8);
|
||||
const uint8_t paddingBytes = uint8_t(paddedLength - dataSize);
|
||||
|
||||
// Pre-allocate as much memory as we will possibly need for speed
|
||||
result.reserve(16 + dataSize + paddingBytes);
|
||||
result.reserve(17 + dataSize + paddingBytes);
|
||||
|
||||
result.push_back(0 /* byte count here later */ << 4 | (uint8_t(message.network.getNetID()) & 0xF));
|
||||
|
||||
@@ -198,7 +207,7 @@ bool HardwareCANPacket::EncodeFromMessage(const CANMessage& message, std::vector
|
||||
// Status and DLC bits
|
||||
if(message.isCANFD) {
|
||||
result.push_back(0x0F); // FD Frame
|
||||
uint8_t fdStatusByte = *dlc;
|
||||
uint8_t fdStatusByte = lengthNibble;
|
||||
if(message.baudrateSwitch)
|
||||
fdStatusByte |= 0x80; // BRS status bit
|
||||
// The firmware does not yet support transmitting ESI
|
||||
@@ -206,12 +215,13 @@ bool HardwareCANPacket::EncodeFromMessage(const CANMessage& message, std::vector
|
||||
} else {
|
||||
// TODO Support high voltage wakeup, bitwise-or in 0x8 here to enable
|
||||
uint8_t statusNibble = message.isRemote ? 0x4 : 0x0;
|
||||
result.push_back((statusNibble << 4) | *dlc);
|
||||
result.push_back((statusNibble << 4) | lengthNibble);
|
||||
}
|
||||
|
||||
// Now finally the payload
|
||||
result.insert(result.end(), message.data.begin(), message.data.end());
|
||||
result.resize(result.size() + paddingBytes);
|
||||
result.push_back(0);
|
||||
|
||||
// Fill in the length byte from earlier
|
||||
result[0] |= result.size() << 4;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
|
||||
std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
const HardwareEthernetPacket* packet = (const HardwareEthernetPacket*)((const void*)bytestream.data());
|
||||
const uint16_t* rawWords = (const uint16_t*)bytestream.data();
|
||||
|
||||
@@ -16,15 +16,12 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
|
||||
if(packet->Length < 4)
|
||||
return nullptr;
|
||||
|
||||
const size_t ethernetFrameSize = packet->Length - (sizeof(uint16_t) * 2);
|
||||
const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + ethernetFrameSize;
|
||||
const size_t bytestreamActualSize = bytestream.size();
|
||||
if(bytestreamActualSize < bytestreamExpectedSize)
|
||||
size_t bytesOnWire = packet->Length - (sizeof(uint16_t) * 2);
|
||||
if(bytestream.size() < sizeof(HardwareEthernetPacket) + bytesOnWire)
|
||||
return nullptr;
|
||||
|
||||
// Check for oversized packets, noting that some devices will send an extra byte to have an even number of bytes
|
||||
if(bytestreamActualSize > bytestreamExpectedSize + 1)
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
|
||||
if(bytestream.size() > sizeof(HardwareEthernetPacket) + bytesOnWire)
|
||||
std::cout << "There is an extra " << (sizeof(HardwareEthernetPacket) + bytesOnWire) << " bytes at the end" << std::endl;
|
||||
|
||||
auto messagePtr = std::make_shared<EthernetMessage>();
|
||||
EthernetMessage& message = *messagePtr;
|
||||
@@ -50,7 +47,7 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
|
||||
// Network ID is also not set, this will be fixed in the Decoder as well
|
||||
|
||||
const std::vector<uint8_t>::const_iterator databegin = bytestream.begin() + (sizeof(HardwareEthernetPacket) - (sizeof(uint16_t) * 2));
|
||||
const std::vector<uint8_t>::const_iterator dataend = databegin + ethernetFrameSize;
|
||||
const std::vector<uint8_t>::const_iterator dataend = databegin + bytesOnWire;
|
||||
message.data.insert(message.data.begin(), databegin, dataend);
|
||||
|
||||
return messagePtr;
|
||||
@@ -59,21 +56,11 @@ std::shared_ptr<EthernetMessage> HardwareEthernetPacket::DecodeToMessage(const s
|
||||
bool HardwareEthernetPacket::EncodeFromMessage(const EthernetMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t&) {
|
||||
const size_t unpaddedSize = message.data.size();
|
||||
size_t paddedSize = unpaddedSize;
|
||||
uint16_t description = message.description;
|
||||
|
||||
if(!message.noPadding && unpaddedSize < 60)
|
||||
paddedSize = 60; // Pad out short messages
|
||||
|
||||
size_t sizeWithHeader = paddedSize + 4; // DescriptionID and Padded Count
|
||||
|
||||
// Description ID Most Significant bit is used to identify preemption frames
|
||||
if(description & 0x8000)
|
||||
return false;
|
||||
|
||||
if(message.preemptionEnabled) {
|
||||
sizeWithHeader++; // Make space for the preemption flags
|
||||
description |= 0x8000;
|
||||
}
|
||||
size_t sizeWithHeader = paddedSize + 5; // DescriptionID and Premption Flags
|
||||
|
||||
bytestream.reserve(sizeWithHeader + 8); // Also reserve space for the bytes we'll use later on
|
||||
bytestream.resize(sizeWithHeader);
|
||||
@@ -84,10 +71,11 @@ bool HardwareEthernetPacket::EncodeFromMessage(const EthernetMessage& message, s
|
||||
bytestream[index++] = uint8_t(paddedSize >> 8);
|
||||
|
||||
// Description ID, big endian
|
||||
bytestream[index++] = uint8_t(description >> 8);
|
||||
bytestream[index++] = uint8_t(description);
|
||||
bytestream[index++] = uint8_t(message.description >> 8);
|
||||
bytestream[index++] = uint8_t(message.description);
|
||||
|
||||
// The header is one byte larger if preemption is enabled, shifting the data
|
||||
// Yes, we reserved and allocated space for the preemption flags even if we're not putting them there
|
||||
// And yes, the data is intended to move over one byte
|
||||
if(message.preemptionEnabled)
|
||||
bytestream[index++] = message.preemptionFlags;
|
||||
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||
#include "icsneo/communication/message/ethphymessage.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
std::shared_ptr<EthPhyMessage> HardwareEthernetPhyRegisterPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
|
||||
{
|
||||
if(bytestream.empty() || (bytestream.size() < sizeof(PhyRegisterHeader_t)))
|
||||
{
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
auto msg = std::make_shared<EthPhyMessage>();
|
||||
const PhyRegisterHeader_t* pHeader = reinterpret_cast<const PhyRegisterHeader_t*>(bytestream.data());
|
||||
const size_t numEntries = static_cast<size_t>(pHeader->numEntries);
|
||||
if(
|
||||
(PhyPacketVersion == pHeader->version) &&
|
||||
(sizeof(PhyRegisterPacket_t) == pHeader->entryBytes) &&
|
||||
(numEntries <= MaxPhyEntries) &&
|
||||
((bytestream.size() - sizeof(PhyRegisterHeader_t))
|
||||
== (sizeof(PhyRegisterPacket_t) * numEntries))
|
||||
)
|
||||
{
|
||||
msg->messages.reserve(numEntries);
|
||||
const PhyRegisterPacket_t* pFirstEntry = reinterpret_cast<const PhyRegisterPacket_t*>(bytestream.data() + sizeof(PhyRegisterHeader_t));
|
||||
for(size_t entryIdx{0}; entryIdx < numEntries; ++entryIdx)
|
||||
{
|
||||
const PhyRegisterPacket_t* pEntry = (pFirstEntry + entryIdx);
|
||||
auto phyMessage = std::make_shared<PhyMessage>();
|
||||
phyMessage->Enabled = (pEntry->Enabled != 0u);
|
||||
phyMessage->WriteEnable = (pEntry->WriteEnable != 0u);
|
||||
phyMessage->Clause45Enable = (pEntry->Clause45Enable != 0u);
|
||||
phyMessage->version = static_cast<uint8_t>(pEntry->version);
|
||||
if(phyMessage->Clause45Enable)
|
||||
phyMessage->clause45 = pEntry->clause45;
|
||||
else
|
||||
phyMessage->clause22 = pEntry->clause22;
|
||||
msg->messages.push_back(phyMessage);
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool HardwareEthernetPhyRegisterPacket::EncodeFromMessage(const EthPhyMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
|
||||
{
|
||||
const size_t messageCount = message.getMessageCount();
|
||||
if(!messageCount)
|
||||
{
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
else if (messageCount > MaxPhyEntries)
|
||||
{
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
auto byteSize = (messageCount * sizeof(PhyRegisterPacket_t)) + sizeof(PhyRegisterHeader_t);
|
||||
bytestream.reserve(byteSize);
|
||||
bytestream.push_back(static_cast<uint8_t>(messageCount & 0xFF));
|
||||
bytestream.push_back(static_cast<uint8_t>((messageCount >> 8) & 0xFF));
|
||||
bytestream.push_back(PhyPacketVersion);
|
||||
bytestream.push_back(static_cast<uint8_t>(sizeof(PhyRegisterPacket_t)));
|
||||
for(auto& phyMessage : message.messages)
|
||||
{
|
||||
PhyRegisterPacket_t tempPacket;
|
||||
tempPacket.Enabled = phyMessage->Enabled ? 0x1u : 0x0u;
|
||||
tempPacket.WriteEnable = phyMessage->WriteEnable ? 0x1u : 0x0u;
|
||||
tempPacket.version = (phyMessage->version & 0xF);
|
||||
if(phyMessage->Clause45Enable)
|
||||
{
|
||||
if( (FiveBits < phyMessage->clause45.port) ||
|
||||
(FiveBits < phyMessage->clause45.device) )
|
||||
{
|
||||
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
tempPacket.Clause45Enable = 0x1u;
|
||||
tempPacket.clause45.port = phyMessage->clause45.port;
|
||||
tempPacket.clause45.device = phyMessage->clause45.device;
|
||||
tempPacket.clause45.regAddr = phyMessage->clause45.regAddr;
|
||||
tempPacket.clause45.regVal = phyMessage->clause45.regVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (FiveBits < phyMessage->clause22.phyAddr) ||
|
||||
(FiveBits < phyMessage->clause22.regAddr) )
|
||||
{
|
||||
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
tempPacket.Clause45Enable = 0x0u;
|
||||
tempPacket.clause22.phyAddr = phyMessage->clause22.phyAddr;
|
||||
tempPacket.clause22.page = phyMessage->clause22.page;
|
||||
tempPacket.clause22.regAddr = phyMessage->clause22.regAddr;
|
||||
tempPacket.clause22.regVal = phyMessage->clause22.regVal;
|
||||
}
|
||||
uint8_t* pktPtr = reinterpret_cast<uint8_t*>(&tempPacket);
|
||||
bytestream.insert(bytestream.end(), pktPtr, pktPtr + sizeof(PhyRegisterPacket_t));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,13 @@ std::shared_ptr<FlexRayMessage> HardwareFlexRayPacket::DecodeToMessage(const std
|
||||
// Always get the frame length, even for a symbol
|
||||
msg->framelen = data->frame_length_12_5ns * 12.5e-9;
|
||||
|
||||
msg->channel = data->statusBits.bits.chb ? icsneo::FlexRay::Channel::B : icsneo::FlexRay::Channel::A;
|
||||
|
||||
if(data->tss_length_12_5ns == 0xffff) {// Flag value meaning this is a symbol
|
||||
// These values are only for 10Mbit
|
||||
// That's the only baudrate supported for now
|
||||
if (data->frame_length_12_5ns > 480)
|
||||
msg->symbol = FlexRay::Symbol::Wakeup;
|
||||
else if (data->frame_length_12_5ns > 264)
|
||||
msg->symbol = FlexRay::Symbol::CAS;
|
||||
else
|
||||
msg->symbol = FlexRay::Symbol::Unknown;
|
||||
msg->symbol = FlexRay::Symbol::Unknown; // We can't know the symbol yet because this will depend on the baudrate
|
||||
// Eventually we'll have to get this from the framelen
|
||||
} else {
|
||||
msg->tsslen = data->tss_length_12_5ns * 12.5e-9;
|
||||
|
||||
msg->channelB = data->statusBits.bits.chb;
|
||||
|
||||
if(data->statusBits.bits.bytesRxed >= 5) {
|
||||
if(data->statusBits.bits.hcrc_error)
|
||||
msg->headerCRCStatus = FlexRay::CRCStatus::Error;
|
||||
@@ -50,17 +43,14 @@ std::shared_ptr<FlexRayMessage> HardwareFlexRayPacket::DecodeToMessage(const std
|
||||
if(msg->headerCRCStatus != FlexRay::CRCStatus::Error) {
|
||||
msg->reserved0was1 = data->reserved_0;
|
||||
msg->payloadPreamble = data->payload_preamble;
|
||||
msg->nullFrame = !data->null_frame;
|
||||
msg->nullFrame = data->null_frame;
|
||||
msg->sync = data->sync;
|
||||
msg->startup = data->startup;
|
||||
msg->slotid = data->slotid;
|
||||
msg->cycle = data->cycle;
|
||||
msg->dynamic = data->statusBits.bits.dynamic;
|
||||
if(int64_t(numBytes) != int64_t(data->Length) - 4) {
|
||||
// This is an error, probably need to flag it
|
||||
|
||||
} else {
|
||||
const uint8_t* dataStart = (const uint8_t*)(data) - 4 + sizeof(HardwareFlexRayPacket);
|
||||
msg->data = std::vector<uint8_t>(dataStart, dataStart + numBytes);
|
||||
// This is an error, probably need to flag it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
std::shared_ptr<Message> HardwareI2CPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream)
|
||||
{
|
||||
auto msg = std::make_shared<I2CMessage>();
|
||||
const I2CHeader* packet = reinterpret_cast<const I2CHeader*>(bytestream.data());
|
||||
const size_t numPayloadBytes = packet->length;
|
||||
const size_t numControlBytes = packet->CoreMiniBitsI2C.CBLen;
|
||||
const size_t numDataBytes = numPayloadBytes - numControlBytes;
|
||||
if( (numPayloadBytes == 0) || (numDataBytes > I2CMaxLength) ||
|
||||
(sizeof(I2CHeader) != (bytestream.size() - numPayloadBytes)) )
|
||||
{ return nullptr; }
|
||||
|
||||
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
|
||||
msg->address = (packet->CoreMiniBitsI2C.ID & 0x3FFu);
|
||||
msg->deviceMode = static_cast<I2CMessage::DeviceMode>(packet->CoreMiniBitsI2C.CT);
|
||||
msg->direction = static_cast<I2CMessage::Direction>(packet->CoreMiniBitsI2C.DIR);
|
||||
|
||||
msg->isExtendedID = static_cast<bool>(packet->CoreMiniBitsI2C.EID & 0x01u);
|
||||
msg->isTXMsg = static_cast<bool>(packet->CoreMiniBitsI2C.TXMsg & 0x01u);
|
||||
msg->txTimeout = static_cast<bool>(packet->CoreMiniBitsI2C.TXTimeout & 0x01u);
|
||||
msg->txNack = static_cast<bool>(packet->CoreMiniBitsI2C.TXNack & 0x01u);
|
||||
msg->txAborted = static_cast<bool>(packet->CoreMiniBitsI2C.TXAborted & 0x01u);
|
||||
msg->txLostArb = static_cast<bool>(packet->CoreMiniBitsI2C.TXLostArb & 0x01u);
|
||||
msg->txError = static_cast<bool>(packet->CoreMiniBitsI2C.TXError & 0x01u);
|
||||
//We don't care about 0xTRB0Dx in this case...
|
||||
//copy 0xTRB0STAT even though we likely won't use it either
|
||||
msg->stats = packet->stats;
|
||||
msg->timestamp = (packet->timestamp & (0x7FFFFFFFFFFFFFFFull));
|
||||
|
||||
//The device will combine the 'control' bytes and data bytes into one payload
|
||||
//The control bytes will always come before the data
|
||||
auto cbStart = bytestream.begin() + sizeof(I2CHeader);
|
||||
auto dataStart = cbStart + numControlBytes;
|
||||
std::copy(cbStart, dataStart, std::back_inserter(msg->controlBytes));
|
||||
std::copy(dataStart, bytestream.end(), std::back_inserter(msg->dataBytes));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool HardwareI2CPacket::EncodeFromMessage(const I2CMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
|
||||
{
|
||||
const size_t numControlBytes = message.controlBytes.size();
|
||||
const size_t numDataBytes = message.dataBytes.size();
|
||||
if(I2CMaxLength < numDataBytes)
|
||||
{
|
||||
report(APIEvent::Type::I2CMessageExceedsMaxLength, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(message.controlBytes.empty() || message.dataBytes.empty())
|
||||
{
|
||||
//You'll need to provide a target R/W register in controlBytes
|
||||
//alternatively, you're expecting to read without providing a dataBytes payload
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bytestream.push_back(static_cast<uint8_t>(numControlBytes & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>((numControlBytes) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>(numDataBytes & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>((numDataBytes) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>((message.stats) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>(message.stats & 0xFFu));
|
||||
|
||||
if(message.isExtendedID)
|
||||
{
|
||||
bytestream.push_back(static_cast<uint8_t>(message.address & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>(((message.address) >> 8) & 0x03u) | 0x04u);
|
||||
} else {
|
||||
bytestream.push_back(static_cast<uint8_t>(message.address & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>(0x00u));
|
||||
}
|
||||
|
||||
if(I2CMessage::Direction::Read == message.direction)
|
||||
{ bytestream.back() |= static_cast<uint8_t>(0x10u); }
|
||||
|
||||
std::copy(message.controlBytes.begin(), message.controlBytes.end(), std::back_inserter(bytestream));
|
||||
std::copy(message.dataBytes.begin(), message.dataBytes.end(), std::back_inserter(bytestream));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
#include "icsneo/communication/packet/iso9141packet.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
bool HardwareISO9141Packet::EncodeFromMessage(const ISO9141Message& message, std::vector<uint8_t>& bytestream,
|
||||
const device_eventhandler_t& report, const Packetizer& packetizer)
|
||||
{
|
||||
size_t bytesToSend = message.data.size();
|
||||
if (message.isInit || message.isBreak)
|
||||
bytesToSend = 0;
|
||||
|
||||
if(bytesToSend > 4200) {
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false; // Too much data for the protocol
|
||||
}
|
||||
|
||||
bytestream.clear();
|
||||
|
||||
std::vector<uint8_t> packet;
|
||||
packet.reserve(16);
|
||||
|
||||
size_t currentStart = 0;
|
||||
do {
|
||||
const bool firstPacket = currentStart == 0;
|
||||
const uint8_t maxSize = (firstPacket ? 9 : 12);
|
||||
uint8_t currentSize = maxSize;
|
||||
if(bytesToSend - currentStart < maxSize)
|
||||
currentSize = (uint8_t)(bytesToSend - currentStart);
|
||||
|
||||
packet.insert(packet.begin(), {
|
||||
(uint8_t)Network::NetID::RED, // 0x0C for long message
|
||||
(uint8_t)0, // Size, little endian 16-bit, filled later
|
||||
(uint8_t)0,
|
||||
(uint8_t)message.network.getNetID(), // NetID, little endian 16-bit
|
||||
(uint8_t)(uint16_t(message.network.getNetID()) >> 8)
|
||||
});
|
||||
packet.push_back(uint8_t(message.network.getNetID()) + uint8_t((currentSize + (firstPacket ? 6 : 3)) << 4));
|
||||
packet.push_back(uint8_t(currentSize + (firstPacket ? 5 : 2)));
|
||||
if(bytesToSend - currentStart > maxSize) // More packets are coming
|
||||
packet.back() |= 0x40;
|
||||
|
||||
if(firstPacket) {
|
||||
if(message.isInit)
|
||||
packet.back() |= 0x80;
|
||||
if(message.isBreak)
|
||||
packet.back() |= 0x20;
|
||||
}
|
||||
|
||||
// Two bytes for Description ID, big endian
|
||||
packet.insert(packet.end(), { uint8_t(message.description >> 8), uint8_t(message.description) });
|
||||
|
||||
// If we're the first packet and not init/break only, we should put the header in
|
||||
if(firstPacket && !message.isInit && !message.isBreak)
|
||||
packet.insert(packet.end(), message.header.begin(), message.header.end());
|
||||
|
||||
// Now the data
|
||||
auto dataIt = message.data.begin() + currentStart;
|
||||
if(currentSize)
|
||||
packet.insert(packet.end(), dataIt, dataIt + currentSize);
|
||||
|
||||
// Advance for the next packet
|
||||
currentStart += currentSize;
|
||||
|
||||
const uint16_t size = uint16_t(packet.size()) + 2;
|
||||
packet[1] = uint8_t(size & 0xFF);
|
||||
packet[2] = uint8_t((size >> 8) & 0xFF);
|
||||
|
||||
packetizer.packetWrap(packet, false);
|
||||
bytestream.insert(bytestream.end(), packet.begin(), packet.end());
|
||||
packet.clear();
|
||||
} while(currentStart < bytesToSend);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<ISO9141Message> HardwareISO9141Packet::Decoder::decodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
const HardwareISO9141Packet& packet = *reinterpret_cast<const HardwareISO9141Packet*>(bytestream.data());
|
||||
|
||||
if(!mMsg) {
|
||||
mMsg = std::make_shared<ISO9141Message>();
|
||||
mGotPackets = 0;
|
||||
}
|
||||
|
||||
mGotPackets++;
|
||||
|
||||
const bool morePacketsComing = packet.c3.frm == 0;
|
||||
const uint8_t bytesInCurrentMessage = packet.c3.len;
|
||||
if(mMsg->data.size() + bytesInCurrentMessage > 500) {
|
||||
mMsg.reset();
|
||||
return std::shared_ptr<ISO9141Message>();
|
||||
}
|
||||
|
||||
// This timestamp is raw off the device (in timestampResolution increments)
|
||||
// Decoder will fix as it has information about the timestampResolution increments
|
||||
mMsg->timestamp = packet.timestamp.TS;
|
||||
|
||||
auto* dataStart = packet.data;
|
||||
if(mGotPackets == 1) {
|
||||
// Header
|
||||
if(bytesInCurrentMessage < 3) {
|
||||
mMsg.reset(); // We don't have the header for some reason
|
||||
return std::shared_ptr<ISO9141Message>();
|
||||
}
|
||||
|
||||
std::copy(packet.data, packet.data + 3, mMsg->header.begin());
|
||||
dataStart += 3;
|
||||
}
|
||||
|
||||
// Data
|
||||
mMsg->data.insert(mMsg->data.end(), dataStart, packet.data + (bytesInCurrentMessage > 8 ? 8 : bytesInCurrentMessage));
|
||||
if(bytesInCurrentMessage > 8)
|
||||
mMsg->data.push_back(packet.c1.d8);
|
||||
if(bytesInCurrentMessage > 9)
|
||||
mMsg->data.push_back(packet.c2.d9);
|
||||
if(bytesInCurrentMessage > 10)
|
||||
mMsg->data.push_back(packet.c2.d10);
|
||||
if(bytesInCurrentMessage > 11)
|
||||
mMsg->data.push_back(packet.c3.d11);
|
||||
|
||||
if(morePacketsComing)
|
||||
return std::shared_ptr<ISO9141Message>();
|
||||
|
||||
mMsg->transmitted = packet.c1.tx;
|
||||
mMsg->isInit = packet.c3.init;
|
||||
mMsg->framingError = packet.c1.options & 0x1;
|
||||
mMsg->overflowError = packet.c1.options & 0x2;
|
||||
mMsg->parityError = packet.c1.options & 0x4;
|
||||
mMsg->rxTimeoutError = packet.c1.options & 0x8;
|
||||
mMsg->description = packet.stats;
|
||||
|
||||
auto ret = mMsg;
|
||||
mMsg.reset();
|
||||
return ret;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,12 +0,0 @@
|
||||
#include "icsneo/communication/packet/logicaldiskinfopacket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::shared_ptr<LogicalDiskInfoMessage> LogicalDiskInfoPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
// Make sure we have enough to read the packet length first
|
||||
if(bytestream.size() < sizeof(LogicalDiskInfoPacket))
|
||||
return {};
|
||||
|
||||
const LogicalDiskInfoPacket* packet = reinterpret_cast<const LogicalDiskInfoPacket*>(bytestream.data());
|
||||
return std::make_shared<LogicalDiskInfoMessage>(packet->isConnected != 0, packet->numSectors, packet->hiddenSectors, packet->bytesPerSector);
|
||||
}
|
||||
@@ -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,31 +0,0 @@
|
||||
#include <iostream>
|
||||
#include "icsneo/communication/packet/scriptstatuspacket.h"
|
||||
#include "icsneo/communication/message/scriptstatusmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::shared_ptr<ScriptStatusMessage> ScriptStatus::DecodeToMessage(const std::vector<uint8_t>& bytestream){
|
||||
if(bytestream.size() != sizeof(ScriptStatus))
|
||||
return {};
|
||||
|
||||
auto msg = std::make_shared<ScriptStatusMessage>();
|
||||
const auto& decoded = *reinterpret_cast<const ScriptStatus*>(bytestream.data());
|
||||
msg->isCoreminiRunning = decoded.status.isRunning;
|
||||
msg->isEncrypted = decoded.status.isEncrypted;
|
||||
msg->sectorOverflows = decoded.sectorOverflows;
|
||||
msg->numRemainingSectorBuffers = decoded.numRemainingSectorBuffers;
|
||||
msg->lastSector = decoded.lastSector;
|
||||
msg->readBinSize = decoded.readBinSize;
|
||||
msg->minSector = decoded.minSector;
|
||||
msg->maxSector = decoded.maxSector;
|
||||
msg->currentSector = decoded.currentSector;
|
||||
msg->coreminiCreateTime = ((uint64_t)decoded.coreminiCreateTimeMsb << 32) | decoded.coreminiCreateTimeLsb;
|
||||
msg->fileChecksum = decoded.fileChecksum;
|
||||
msg->coreminiVersion = decoded.coreminiVersion;
|
||||
msg->coreminiHeaderSize = decoded.coreminiHeaderSize;
|
||||
msg->diagnosticErrorCode = decoded.diagErrCode;
|
||||
msg->diagnosticErrorCodeCount = decoded.diagErrCodeCount;
|
||||
msg->maxCoreminiSizeKB = decoded.maxCoreminiSizeKB;
|
||||
|
||||
return msg;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "icsneo/communication/packet/versionpacket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::shared_ptr<VersionMessage> HardwareVersionPacket::DecodeMainToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
if(bytestream.size() < 3) // Not enough bytes to decode
|
||||
return std::shared_ptr<VersionMessage>();
|
||||
|
||||
auto msg = std::make_shared<VersionMessage>(VersionMessage::MainChip);
|
||||
|
||||
msg->Versions.emplace_back();
|
||||
std::optional<DeviceAppVersion>& version = msg->Versions.back();
|
||||
version.emplace();
|
||||
version->major = bytestream[1];
|
||||
version->minor = bytestream[2];
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<VersionMessage> HardwareVersionPacket::DecodeSecondaryToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
auto msg = std::make_shared<VersionMessage>(VersionMessage::SecondaryChips);
|
||||
|
||||
size_t bytesLeft = bytestream.size();
|
||||
if(bytesLeft)
|
||||
bytesLeft--; // Disregard command byte
|
||||
while(bytesLeft >= 3) {
|
||||
const bool versionValid = bytestream[bytestream.size() - bytesLeft + 0];
|
||||
msg->Versions.emplace_back();
|
||||
std::optional<DeviceAppVersion>& version = msg->Versions.back();
|
||||
if(versionValid) {
|
||||
version.emplace();
|
||||
version->major = bytestream[bytestream.size() - bytesLeft + 1];
|
||||
version->minor = bytestream[bytestream.size() - bytesLeft + 2];
|
||||
}
|
||||
bytesLeft -= std::min<size_t>(3, bytesLeft);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
#include "icsneo/communication/packet/wivicommandpacket.h"
|
||||
#include "icsneo/communication/message/wiviresponsemessage.h"
|
||||
#include <cstring>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::shared_ptr<WiVI::ResponseMessage> WiVI::CommandPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::Header))
|
||||
return {};
|
||||
|
||||
auto msg = std::make_shared<WiVI::ResponseMessage>();
|
||||
const auto& header = *reinterpret_cast<const WiVI::CommandPacket::Header*>(bytestream.data());
|
||||
switch(header.cmd) {
|
||||
case WiVI::Command::Result: {
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::Result))
|
||||
return {};
|
||||
|
||||
const auto& decoded = *reinterpret_cast<const WiVI::CommandPacket::Result*>(bytestream.data());
|
||||
msg->responseTo = decoded.responseTo;
|
||||
msg->success = decoded.result != 0;
|
||||
break;
|
||||
}
|
||||
case WiVI::Command::GetSignal: {
|
||||
// Use the SetSignal structure since it matches the response
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::SetSignal))
|
||||
return {};
|
||||
|
||||
const auto& setSignal = *reinterpret_cast<const WiVI::CommandPacket::SetSignal*>(bytestream.data());
|
||||
msg->responseTo = WiVI::Command::GetSignal;
|
||||
msg->value = setSignal.value.ValueInt32;
|
||||
break;
|
||||
}
|
||||
case WiVI::Command::GetAll: {
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll))
|
||||
return {};
|
||||
|
||||
const auto& getAll = *reinterpret_cast<const WiVI::CommandPacket::GetAll*>(bytestream.data());
|
||||
msg->responseTo = WiVI::Command::GetAll;
|
||||
msg->info.emplace();
|
||||
msg->info->sleepRequest = getAll.sleepRequest;
|
||||
msg->info->connectionTimeoutMinutes = getAll.connectionTimeoutMinutes;
|
||||
|
||||
// Check that we have enough data for the capture infos
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll) + (sizeof(WiVI::CaptureInfo) * getAll.numCaptureInfos))
|
||||
return {};
|
||||
|
||||
msg->info->captures.resize(getAll.numCaptureInfos);
|
||||
for(uint16_t i = 0; i < getAll.numCaptureInfos; i++)
|
||||
msg->info->captures[i] = getAll.captureInfos[i];
|
||||
break;
|
||||
}
|
||||
default: // Unknown command response
|
||||
return {};
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WiVI::CommandPacket::GetSignal::Encode(WiVI::SignalType type) {
|
||||
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetSignal));
|
||||
auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetSignal*>(ret.data());
|
||||
|
||||
frame.header.cmd = WiVI::Command::GetSignal;
|
||||
frame.header.length = sizeof(frame) - sizeof(frame.header);
|
||||
frame.type = type;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WiVI::CommandPacket::SetSignal::Encode(WiVI::SignalType type, CoreMiniFixedPointValue value) {
|
||||
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::SetSignal));
|
||||
auto& frame = *reinterpret_cast<WiVI::CommandPacket::SetSignal*>(ret.data());
|
||||
|
||||
frame.header.cmd = WiVI::Command::SetSignal;
|
||||
frame.header.length = sizeof(frame) - sizeof(frame.header);
|
||||
frame.type = type;
|
||||
frame.value = value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WiVI::CommandPacket::GetAll::Encode() {
|
||||
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetAll));
|
||||
auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetAll*>(ret.data());
|
||||
|
||||
frame.header.cmd = WiVI::Command::GetAll;
|
||||
frame.header.length = sizeof(frame) - sizeof(frame.header);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WiVI::CommandPacket::ClearUploads::Encode(const std::vector<uint8_t>& bitmask) {
|
||||
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::ClearUploads) + bitmask.size());
|
||||
auto& frame = *reinterpret_cast<WiVI::CommandPacket::ClearUploads*>(ret.data());
|
||||
|
||||
frame.header.cmd = WiVI::Command::ClearUploads;
|
||||
frame.header.length = uint16_t(ret.size() - sizeof(frame.header));
|
||||
memcpy(frame.bitmask, bitmask.data(), bitmask.size());
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace icsneo;
|
||||
@@ -12,11 +13,11 @@ uint8_t Packetizer::ICSChecksum(const std::vector<uint8_t>& data) {
|
||||
return (uint8_t)checksum;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>& Packetizer::packetWrap(std::vector<uint8_t>& data, bool shortFormat) const {
|
||||
std::vector<uint8_t>& Packetizer::packetWrap(std::vector<uint8_t>& data, bool shortFormat) {
|
||||
if(shortFormat) {
|
||||
// Some devices don't use the checksum, so might as well not calculate it if that's the case
|
||||
// Either way the byte is still expected to be present in the bytestream for short messages
|
||||
data.push_back(disableChecksum ? 0x00 : ICSChecksum(data));
|
||||
data.push_back(disableChecksum ? 0x00 : ICSChecksum(data));
|
||||
}
|
||||
data.insert(data.begin(), 0xAA);
|
||||
if(align16bit && data.size() % 2 == 1) // Some devices always expect 16-bit aligned data
|
||||
@@ -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) {
|
||||
@@ -39,6 +41,7 @@ bool Packetizer::input(RingBuffer& bytes) {
|
||||
state = ReadState::ParseHeader;
|
||||
currentIndex = 1;
|
||||
} else {
|
||||
std::cerr << "Discarding byte " << std::hex << std::setw(2) << std::setfill('0') << int(bytes.front()) << std::endl;
|
||||
bytes.pop_front(); // Discard
|
||||
}
|
||||
break;
|
||||
@@ -47,97 +50,59 @@ 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.
|
||||
*/
|
||||
if(packetLength < 6 || packetLength > 4000) {
|
||||
/* Long packets can't have a length less than 4, 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.
|
||||
*/
|
||||
if(packetLength < 4 || packetLength > 4000) {
|
||||
std::cerr << "====================================\n";
|
||||
std::cerr << "An improper packet has occurred!\n";
|
||||
std::cerr << "Packet: (" << std::dec << packetLength << ")\n";
|
||||
std::cerr << '\t';
|
||||
for(auto i = 0; i < std::min(packetLength, 16); i++)
|
||||
std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(bytes[i]) << ' ';
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "Previous: (" << std::dec << previousPacket.data.size() << ") " << previousPacket.network << '\n';
|
||||
for(auto i = 0; i < previousPacket.data.size(); i += 16) {
|
||||
std::cerr << '\t';
|
||||
for(auto j = 0; j < 16 && (i + j) < previousPacket.data.size(); j++)
|
||||
std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(previousPacket.data[i+j]) << ' ';
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
std::cerr << "====================================\n";
|
||||
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 +114,43 @@ 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);
|
||||
|
||||
std::cerr << "====================================\n";
|
||||
std::cerr << "A checksum error has occurred!\n";
|
||||
std::cerr << "Packet: (" << std::dec << packet.data.size() << ") " << packet.network << '\n';
|
||||
for(auto i = 0; i < packet.data.size(); i += 16) {
|
||||
std::cerr << '\t';
|
||||
for(auto j = 0; j < 16 && (i + j) < packet.data.size(); j++)
|
||||
std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(packet.data[i+j]) << ' ';
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
std::cerr << "Previous: (" << std::dec << previousPacket.data.size() << ") " << previousPacket.network << '\n';
|
||||
for(auto i = 0; i < previousPacket.data.size(); i += 16) {
|
||||
std::cerr << '\t';
|
||||
for(auto j = 0; j < 16 && (i + j) < previousPacket.data.size(); j++)
|
||||
std::cerr << std::hex << std::setw(2) << std::setfill('0') << int(previousPacket.data[i+j]) << ' ';
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
std::cerr << "====================================\n";
|
||||
|
||||
bytes.pop_front(); // Drop the first byte so it doesn't get picked up again
|
||||
}
|
||||
|
||||
previousPacket = packet;
|
||||
|
||||
// Reset for the next packet
|
||||
currentIndex = 0;
|
||||
state = ReadState::SearchForHeader;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+69
-2964
File diff suppressed because it is too large
Load Diff
+173
-406
@@ -1,406 +1,173 @@
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
#include "icsneo/platform/devices.h"
|
||||
#include "icsneo/device/founddevice.h"
|
||||
#include "generated/extensions/builtin.h"
|
||||
|
||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||
#include "icsneo/platform/firmio.h"
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
|
||||
#include "icsneo/platform/pcap.h"
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_CDCACM
|
||||
#include "icsneo/platform/cdcacm.h"
|
||||
#endif
|
||||
|
||||
#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;
|
||||
|
||||
template<typename T>
|
||||
static void makeIfSerialMatches(const FoundDevice& dev, std::vector<std::shared_ptr<Device>>& into) {
|
||||
// Relies on the subclass to have a `static constexpr const char* SERIAL_START = "XX"`
|
||||
// and also a public constructor `T(const FoundDevice& dev)`
|
||||
// Use macro ICSNEO_FINDABLE_DEVICE() to create these
|
||||
if(dev.serial[0] == T::SERIAL_START[0] && dev.serial[1] == T::SERIAL_START[1])
|
||||
into.push_back(std::make_shared<T>(dev));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void makeIfPIDMatches(const FoundDevice& dev, std::vector<std::shared_ptr<Device>>& into) {
|
||||
// Relies on the subclass to have a `static constexpr uint16_t PRODUCT_ID = 0x1111`
|
||||
// and also a public constructor `T(const FoundDevice& dev)`
|
||||
// Use macro ICSNEO_FINDABLE_DEVICE_BY_PID() to create these
|
||||
if(dev.productId == T::PRODUCT_ID)
|
||||
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_FIRMIO
|
||||
FirmIO::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_TCP
|
||||
TCP::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
|
||||
PCAP::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_CDCACM
|
||||
CDCACM::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#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
|
||||
static std::vector<std::weak_ptr<Device>> foundDevices;
|
||||
|
||||
// Remove Devices that have dropped out of scope or are no longer present
|
||||
for (auto it = foundDevices.begin(); it != foundDevices.end(); ) {
|
||||
if (const auto device = it->lock()) {
|
||||
if (std::none_of(newDriverFoundDevices.begin(), newDriverFoundDevices.end(),
|
||||
[&](const auto& driverDevice) {
|
||||
return std::string(driverDevice.serial) == device->getSerial();
|
||||
}
|
||||
)) {
|
||||
it = foundDevices.erase(it); // Device not found by drivers but pointer has a >0 use_count, error?
|
||||
} else {
|
||||
++it; // Valid weak pointer and device found by drivers
|
||||
}
|
||||
} else {
|
||||
it = foundDevices.erase(it); // Weak pointer has a zero use_count
|
||||
}
|
||||
}
|
||||
|
||||
// Remove existing driver devices so we only create new ones
|
||||
for (auto it = newDriverFoundDevices.begin(); it != newDriverFoundDevices.end(); ) {
|
||||
if (std::any_of(foundDevices.begin(), foundDevices.end(),
|
||||
[&](const auto& weakDevice) {
|
||||
const auto device = weakDevice.lock();
|
||||
return device && std::string(it->serial) == device->getSerial();
|
||||
}
|
||||
)) {
|
||||
it = newDriverFoundDevices.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Device>> newFoundDevices;
|
||||
newFoundDevices.reserve(newDriverFoundDevices.size());
|
||||
// Offer found devices to each of the subclasses
|
||||
for (const FoundDevice& dev : newDriverFoundDevices) {
|
||||
#ifdef __ETHERBADGE_H_
|
||||
makeIfSerialMatches<EtherBADGE>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOOBD2PRO_H_
|
||||
makeIfSerialMatches<NeoOBD2PRO>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOOBD2SIM_H_
|
||||
makeIfSerialMatches<NeoOBD2SIM>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVICONNECT_H_
|
||||
makeIfSerialMatches<NeoVIConnect>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE_H_
|
||||
makeIfPIDMatches<NeoVIFIRE>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE2_H_
|
||||
makeIfSerialMatches<NeoVIFIRE2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3_H_
|
||||
makeIfSerialMatches<NeoVIFIRE3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3FLEXRAY_H_
|
||||
makeIfSerialMatches<NeoVIFIRE3FlexRay>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIRED2_H_
|
||||
makeIfSerialMatches<NeoVIRED2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIION_H_
|
||||
makeIfPIDMatches<NeoVIION>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIPLASMA_H_
|
||||
makeIfPIDMatches<NeoVIPLASMA>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADA2B_H_
|
||||
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
|
||||
|
||||
#ifdef __RADGALAXY_H_
|
||||
makeIfSerialMatches<RADGalaxy>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMARS_H_
|
||||
makeIfSerialMatches<RADMars>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADGIGASTAR_H_
|
||||
makeIfSerialMatches<RADGigastar>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADJUPITER_H_
|
||||
makeIfSerialMatches<RADJupiter>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON2_H_
|
||||
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
|
||||
|
||||
#ifdef __RADPLUTO_H_
|
||||
makeIfSerialMatches<RADPluto>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2_H_
|
||||
makeIfSerialMatches<RADStar2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADSUPERMOON_H_
|
||||
makeIfSerialMatches<RADSupermoon>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN3_H_
|
||||
makeIfPIDMatches<ValueCAN3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_1_H_
|
||||
makeIfSerialMatches<ValueCAN4_1>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2_H_
|
||||
makeIfSerialMatches<ValueCAN4_2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2EL_H_
|
||||
makeIfSerialMatches<ValueCAN4_2EL>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_4_H_
|
||||
makeIfSerialMatches<ValueCAN4_4>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4INDUSTRIAL_H_
|
||||
makeIfSerialMatches<ValueCAN4Industrial>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VIVIDCAN_H_
|
||||
makeIfSerialMatches<VividCAN>(dev, newFoundDevices);
|
||||
#endif
|
||||
}
|
||||
|
||||
for(auto& device : newFoundDevices) {
|
||||
AddBuiltInExtensionsTo(device);
|
||||
}
|
||||
|
||||
// Grab a weak pointer from the new devices
|
||||
foundDevices.insert(foundDevices.end(), newFoundDevices.begin(), newFoundDevices.end());
|
||||
|
||||
// Upgrade to shared for the return
|
||||
return std::vector<std::shared_ptr<Device>>(foundDevices.begin(), foundDevices.end());
|
||||
}
|
||||
|
||||
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
static std::vector<DeviceType> supportedDevices = {
|
||||
|
||||
#ifdef __ETHERBADGE_H_
|
||||
EtherBADGE::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOOBD2PRO_H_
|
||||
NeoOBD2PRO::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOOBD2SIM_H_
|
||||
NeoOBD2SIM::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIRED2_H_
|
||||
NeoVIRED2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVICONNECT_H_
|
||||
NeoVIConnect::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE_H_
|
||||
NeoVIFIRE::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE2_H_
|
||||
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
|
||||
|
||||
#ifdef __NEOVIPLASMA_H_
|
||||
NeoVIPLASMA::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADA2B_H_
|
||||
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
|
||||
|
||||
#ifdef __RADGALAXY_H_
|
||||
RADGalaxy::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADMARS_H_
|
||||
RADMars::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADGIGASTAR_H_
|
||||
RADGigastar::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#if defined __RADMOON2_H_ || defined __RADMOON2ZL_H_
|
||||
RADMoon2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON3_H_
|
||||
RADMoon3::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOONDUO_H_
|
||||
RADMoonDuo::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADPLUTO_H_
|
||||
RADPluto::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2_H_
|
||||
RADStar2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADSUPERMOON_H_
|
||||
RADSupermoon::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN3_H_
|
||||
ValueCAN3::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_1_H_
|
||||
ValueCAN4_1::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2_H_
|
||||
ValueCAN4_2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2EL_H_
|
||||
ValueCAN4_2EL::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_4_H_
|
||||
ValueCAN4_4::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4INDUSTRIAL_H_
|
||||
ValueCAN4Industrial::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VIVIDCAN_H_
|
||||
VividCAN::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
return supportedDevices;
|
||||
}
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
#include "icsneo/platform/devices.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static bool supportedDevicesCached = false;
|
||||
static std::vector<DeviceType> supportedDevices = {
|
||||
|
||||
#ifdef __NEOOBD2PRO_H_
|
||||
NeoOBD2PRO::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOOBD2SIM_H_
|
||||
NeoOBD2SIM::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE_H_
|
||||
NeoVIFIRE::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE2ETH_H_
|
||||
NeoVIFIRE2ETH::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE2USB_H_
|
||||
NeoVIFIRE2USB::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIION_H_
|
||||
NeoVIION::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIPLASMA_H_
|
||||
NeoVIPLASMA::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADGALAXY_H_
|
||||
RADGalaxy::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADPLUTOUSB_H_
|
||||
RADPlutoUSB::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2ETH_H_
|
||||
RADStar2ETH::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2USB_H_
|
||||
RADStar2USB::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADSUPERMOON_H_
|
||||
RADSupermoon::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN3_H_
|
||||
ValueCAN3::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_1_H_
|
||||
ValueCAN4_1::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2_H_
|
||||
ValueCAN4_2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2EL_H_
|
||||
ValueCAN4_2EL::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_4_H_
|
||||
ValueCAN4_4::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __VIVIDCAN_H_
|
||||
VividCAN::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
std::vector<std::shared_ptr<Device>> foundDevices;
|
||||
std::vector<std::vector<std::shared_ptr<Device>>> findResults;
|
||||
|
||||
#ifdef __NEOOBD2PRO_H_
|
||||
findResults.push_back(NeoOBD2PRO::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __NEOOBD2SIM_H_
|
||||
findResults.push_back(NeoOBD2SIM::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE_H_
|
||||
findResults.push_back(NeoVIFIRE::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE2ETH_H_
|
||||
findResults.push_back(NeoVIFIRE2ETH::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE2USB_H_
|
||||
findResults.push_back(NeoVIFIRE2USB::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIION_H_
|
||||
findResults.push_back(NeoVIION::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIPLASMA_H_
|
||||
findResults.push_back(NeoVIPLASMA::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __RADGALAXY_H_
|
||||
findResults.push_back(RADGalaxy::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __RADPLUTOUSB_H_
|
||||
findResults.push_back(RADPlutoUSB::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2ETH_H_
|
||||
findResults.push_back(RADStar2ETH::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2USB_H_
|
||||
findResults.push_back(RADStar2USB::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __RADSUPERMOON_H_
|
||||
findResults.push_back(RADSupermoon::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN3_H_
|
||||
findResults.push_back(ValueCAN3::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_1_H_
|
||||
findResults.push_back(ValueCAN4_1::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2_H_
|
||||
findResults.push_back(ValueCAN4_2::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2EL_H_
|
||||
findResults.push_back(ValueCAN4_2EL::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_4_H_
|
||||
findResults.push_back(ValueCAN4_4::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __VIVIDCAN_H_
|
||||
findResults.push_back(VividCAN::Find());
|
||||
#endif
|
||||
|
||||
for(auto& results : findResults) {
|
||||
if(results.size())
|
||||
foundDevices.insert(foundDevices.end(), std::make_move_iterator(results.begin()), std::make_move_iterator(results.end()));
|
||||
}
|
||||
|
||||
return foundDevices;
|
||||
}
|
||||
|
||||
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
if(!supportedDevicesCached) {
|
||||
supportedDevices.erase(std::unique(supportedDevices.begin(), supportedDevices.end()), supportedDevices.end());
|
||||
supportedDevicesCached = true;
|
||||
}
|
||||
return supportedDevices;
|
||||
}
|
||||
@@ -3,464 +3,28 @@
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
void FlexRay::Controller::_setStatus(std::shared_ptr<FlexRayControlMessage> msg) {
|
||||
std::lock_guard<std::mutex> lk(statusLock);
|
||||
status = msg;
|
||||
}
|
||||
|
||||
std::shared_ptr<FlexRayControlMessage> FlexRay::Controller::getStatus() const {
|
||||
std::lock_guard<std::mutex> lk(statusLock);
|
||||
return status;
|
||||
}
|
||||
|
||||
std::pair<const FlexRay::Cluster::Configuration&, const FlexRay::Controller::Configuration&> FlexRay::Controller::getConfiguration() const {
|
||||
return { clusterConfig, controllerConfig };
|
||||
void FlexRay::Controller::_setStatus(std::shared_ptr<FlexRayControlMessage> msg) {
|
||||
std::lock_guard<std::mutex> lk(statusLock);
|
||||
status = msg;
|
||||
}
|
||||
|
||||
void FlexRay::Controller::setConfiguration(Cluster::Configuration clConfig, Controller::Configuration coConfig) {
|
||||
configDirty = true;
|
||||
clusterConfig = clConfig;
|
||||
controllerConfig = coConfig;
|
||||
void FlexRay::Controller::getReady() {
|
||||
|
||||
}
|
||||
|
||||
void FlexRay::Controller::addMessageBuffer(MessageBuffer buffer) {
|
||||
configDirty = true;
|
||||
messageBuffers.emplace_back(std::make_shared<MessageBuffer>(buffer));
|
||||
}
|
||||
|
||||
void FlexRay::Controller::clearMessageBuffers() {
|
||||
configDirty = true;
|
||||
messageBuffers.clear();
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::wakeup(std::chrono::milliseconds timeout) {
|
||||
return setCurrentPOCCommand(FlexRay::POCCommand::Wakeup, true, timeout);
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::configure(std::chrono::milliseconds timeout) {
|
||||
const auto initialTimeout = timeout;
|
||||
const auto functionBegin = std::chrono::steady_clock::now();
|
||||
const auto updateTimeout = [&initialTimeout, &functionBegin, &timeout]() {
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(initialTimeout - (std::chrono::steady_clock::now() - functionBegin));
|
||||
};
|
||||
|
||||
auto statusPair = getCurrentPOCStatus(timeout);
|
||||
const auto& pocStatus = statusPair.second;
|
||||
if(!statusPair.first)
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
if(pocStatus != POCStatus::Config) {
|
||||
if(!enterConfig(timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
}
|
||||
|
||||
if(!setCurrentPOCCommand(POCCommand::ClearRAMs, true, timeout))
|
||||
return false;
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
bool carbusy = isClearAllRAMBusy();
|
||||
while(carbusy && (std::chrono::steady_clock::now() - start) < timeout) {
|
||||
carbusy = isClearAllRAMBusy();
|
||||
}
|
||||
if(carbusy) // timeout
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
std::vector<std::pair<ERAYRegister, uint32_t>> registerWrites;
|
||||
registerWrites.reserve(18);
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::SUCC1,
|
||||
((controllerConfig.KeySlotUsedForStartup & 0x1) << 8) | // TXST
|
||||
((controllerConfig.KeySlotUsedForSync & 0x1) << 9) | // TXSY
|
||||
((clusterConfig.ColdStartAttempts & 0x1f) << 11) | // CSA
|
||||
((controllerConfig.AllowPassiveToActiveCyclePairs & 0x1f) << 16) | // PTA
|
||||
((controllerConfig.WakeupOnChannelB & 0x1) << 21) | // WUCS
|
||||
((controllerConfig.KeySlotOnlyEnabled & 0x1) << 22) | // TSM
|
||||
((controllerConfig.AllowHaltDueToClock & 0x1) << 23) | // HCSE
|
||||
((controllerConfig.MTSOnA & 0x1) << 24) | // MTSA
|
||||
((controllerConfig.MTSOnB & 0x1) << 25) | // MTSB
|
||||
((controllerConfig.ChannelA & 0x1) << 26) | // CCHA
|
||||
((controllerConfig.ChannelB & 0x1) << 27) // CCHB
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::SUCC2,
|
||||
((controllerConfig.ListenTimeout & 0x1fffff)) |
|
||||
((clusterConfig.ListenNoiseMacroticks - 1) << 24)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::SUCC3,
|
||||
(clusterConfig.MaxWithoutClockCorrectionPassive & 0xF) |
|
||||
((clusterConfig.MaxWithoutClockCorrectionFatal) << 4)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::NEMC,
|
||||
clusterConfig.NetworkManagementVectorLengthBytes
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::PRTC1,
|
||||
(clusterConfig.TransmissionStartSequenceDurationBits & 0xF) |
|
||||
((clusterConfig.CASRxLowMax & 0x3F) << 4) |
|
||||
((clusterConfig.StrobePointPosition & 0x3) << 12) |
|
||||
((clusterConfig.Speed & 0x3) << 14) |
|
||||
((clusterConfig.WakeupRxWindowBits & 0x1ff) << 16) |
|
||||
((controllerConfig.WakeupPattern) << 26)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::PRTC2,
|
||||
(clusterConfig.WakeupRxIdleBits) |
|
||||
((clusterConfig.WakeupRxLowBits) << 8) |
|
||||
((clusterConfig.WakeupTxIdleBits) << 16) |
|
||||
((clusterConfig.WakeupTxActiveBits) << 24)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::MHDC,
|
||||
(clusterConfig.PayloadLengthOfStaticSlotInWords) |
|
||||
((controllerConfig.LatestTxMinislot) << 16)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC1,
|
||||
controllerConfig.MicroPerCycle
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC2,
|
||||
(clusterConfig.SyncFrameIDCountMax << 16) |
|
||||
clusterConfig.MacroticksPerCycle
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC3,
|
||||
(controllerConfig.MicroInitialOffsetA) |
|
||||
((controllerConfig.MicroInitialOffsetB) << 8) |
|
||||
((controllerConfig.MacroInitialOffsetA) << 16) |
|
||||
((controllerConfig.MacroInitialOffsetB) << 24)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC4,
|
||||
((clusterConfig.MacroticksPerCycle - clusterConfig.NetworkIdleTimeMacroticks - 1) & 0xFFFF) |
|
||||
((clusterConfig.OffsetCorrectionStartMacroticks - 1) << 16)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC5,
|
||||
controllerConfig.DelayCompensationAMicroticks |
|
||||
(controllerConfig.DelayCompensationBMicroticks << 8) |
|
||||
(controllerConfig.ClusterDriftDamping << 16) |
|
||||
(controllerConfig.DecodingCorrectionMicroticks << 24)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC6,
|
||||
controllerConfig.AcceptStartupRangeMicroticks |
|
||||
(controllerConfig.RateCorrectionOutMicroticks << 16)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC7,
|
||||
(clusterConfig.StaticSlotMacroticks) |
|
||||
(clusterConfig.NumberOfStaticSlots << 16)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC8,
|
||||
clusterConfig.MinislotDurationMacroticks |
|
||||
(clusterConfig.NumberOfMinislots << 16)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC9,
|
||||
clusterConfig.ActionPointOffset |
|
||||
(clusterConfig.MinislotActionPointOffsetMacroticks << 8) |
|
||||
(clusterConfig.DynamicSlotIdlePhaseMinislots << 16)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC10,
|
||||
controllerConfig.OffsetCorrectionOutMicroticks |
|
||||
(controllerConfig.RateCorrectionOutMicroticks << 16)
|
||||
});
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::GTUC11,
|
||||
controllerConfig.ExternOffsetCorrectionControl |
|
||||
(controllerConfig.ExternRateCorrectionControl << 8) |
|
||||
(controllerConfig.ExternOffsetCorrectionMicroticks << 16) |
|
||||
(controllerConfig.ExternRateCorrectionMicroticks << 24)
|
||||
});
|
||||
|
||||
std::vector<std::shared_ptr<MessageBuffer>> staticTx;
|
||||
std::vector<std::shared_ptr<MessageBuffer>> dynamicTx;
|
||||
|
||||
// Add key slot messages
|
||||
std::shared_ptr<MessageBuffer> first = std::make_shared<MessageBuffer>();
|
||||
bool firstIsInMessageBuffers = false;
|
||||
bool firstUsed = false;
|
||||
|
||||
std::shared_ptr<MessageBuffer> second = std::make_shared<MessageBuffer>();
|
||||
bool secondIsInMessageBuffers = false;
|
||||
bool secondUsed = false;
|
||||
|
||||
if(controllerConfig.KeySlotUsedForSync || controllerConfig.KeySlotOnlyEnabled) {
|
||||
first->isStartup = controllerConfig.KeySlotUsedForStartup;
|
||||
first->isSync = controllerConfig.KeySlotUsedForSync;
|
||||
first->isTransmit = true;
|
||||
first->channelA = true;
|
||||
first->channelB = !controllerConfig.TwoKeySlotMode && controllerConfig.ChannelB;
|
||||
first->frameID = controllerConfig.KeySlotID;
|
||||
first->frameLengthBytes = clusterConfig.PayloadLengthOfStaticSlotInWords * 2;
|
||||
first->baseCycle = 0;
|
||||
first->cycleRepetition = 1;
|
||||
first->continuousMode = false;
|
||||
staticTx.push_back(first);
|
||||
firstUsed = true;
|
||||
|
||||
if(controllerConfig.TwoKeySlotMode) {
|
||||
second->isStartup = controllerConfig.KeySlotUsedForStartup;
|
||||
second->isSync = controllerConfig.KeySlotUsedForSync;
|
||||
second->isTransmit = true;
|
||||
second->channelB =true;
|
||||
second->frameID = controllerConfig.SecondKeySlotID;
|
||||
second->frameLengthBytes = clusterConfig.PayloadLengthOfStaticSlotInWords * 2;
|
||||
second->baseCycle = 0;
|
||||
second->cycleRepetition = 1;
|
||||
second->continuousMode = false;
|
||||
staticTx.push_back(second);
|
||||
secondUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& buf : messageBuffers) {
|
||||
if(!buf->isTransmit)
|
||||
continue; // Only transmit frames need to be written to the controller
|
||||
|
||||
if((controllerConfig.KeySlotUsedForSync || controllerConfig.KeySlotOnlyEnabled) && buf->frameID == controllerConfig.KeySlotID) {
|
||||
first = buf;
|
||||
staticTx[0] = buf;
|
||||
// Enforce keyslot rules
|
||||
first->isStartup = controllerConfig.KeySlotUsedForStartup;
|
||||
first->isSync = controllerConfig.KeySlotUsedForSync;
|
||||
first->isDynamic = false;
|
||||
// Suppress default buffer
|
||||
firstIsInMessageBuffers = true;
|
||||
continue;
|
||||
}
|
||||
else if(controllerConfig.TwoKeySlotMode && buf->frameID == controllerConfig.SecondKeySlotID) {
|
||||
second = buf;
|
||||
staticTx[1] = buf;
|
||||
buf->isDynamic = false;
|
||||
// Enforce keyslot rules
|
||||
second->isStartup = controllerConfig.KeySlotUsedForStartup;
|
||||
second->isSync = controllerConfig.KeySlotUsedForSync;
|
||||
second->isDynamic = false;
|
||||
// Suppress default buffer
|
||||
secondIsInMessageBuffers = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(buf->isDynamic)
|
||||
dynamicTx.push_back(buf);
|
||||
else
|
||||
staticTx.push_back(buf);
|
||||
}
|
||||
|
||||
// If the user is using the default coldstart messages, they need to be added to the list for transmit
|
||||
if(firstUsed && !firstIsInMessageBuffers)
|
||||
messageBuffers.push_back(first);
|
||||
if(secondUsed && !secondIsInMessageBuffers)
|
||||
messageBuffers.push_back(second);
|
||||
|
||||
int64_t totalBuffers = staticTx.size() + dynamicTx.size();
|
||||
if(totalBuffers > 128) // TODO warn
|
||||
totalBuffers = 128;
|
||||
|
||||
registerWrites.push_back({ ERAYRegister::MRC,
|
||||
(uint8_t(staticTx.size())) | // FDB[7:0] message buffers exclusively for the static segment
|
||||
// FFB[7:0] set to 0x80, No message buffer assigned to the FIFO
|
||||
(0x80 << 8) |
|
||||
(uint8_t(totalBuffers - 1) << 16) |
|
||||
(controllerConfig.TwoKeySlotMode << 26)
|
||||
});
|
||||
|
||||
for(const auto& regpair : registerWrites) {
|
||||
if(!writeRegister(regpair.first, regpair.second, false, timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
}
|
||||
|
||||
uint16_t dataPointer = static_cast<uint16_t>((totalBuffers + 1) * 4);
|
||||
for(uint16_t i = 0; i < totalBuffers; i++) {
|
||||
MessageBuffer& buf = *(i < (int)staticTx.size() ? staticTx[i] : dynamicTx[i - staticTx.size()]);
|
||||
|
||||
if(buf.frameID == 0)
|
||||
buf.frameID = static_cast<uint16_t>(i | (1 << 10));
|
||||
|
||||
uint32_t hs1 = (
|
||||
(buf.frameID) |
|
||||
(CalculateCycleFilter(buf.baseCycle, buf.cycleRepetition) << 16) |
|
||||
((buf.channelA & 0x1) << 24) |
|
||||
((buf.channelB & 0x1) << 25) |
|
||||
((buf.isTransmit & 0x1) << 26) | // CFG
|
||||
((buf.isNMFrame & 0x1) << 27) | // PPIT
|
||||
((!buf.continuousMode & 0x1) << 28) | // TXM
|
||||
((0 & 0x1) << 29) // MBI, disabled for now but we might want confirmations in the future
|
||||
);
|
||||
|
||||
uint32_t hs2 = (
|
||||
CalculateHCRC(buf) |
|
||||
(((buf.frameLengthBytes + 1) / 2) << 16)
|
||||
);
|
||||
|
||||
uint32_t hs3 = dataPointer;
|
||||
buf._dataPointer = dataPointer;
|
||||
buf._id = i;
|
||||
dataPointer += buf.frameLengthBytes / 4;
|
||||
dataPointer += dataPointer % 4; // must be a 4 byte boundary
|
||||
|
||||
if(!writeRegister(ERAYRegister::WRHS1, hs1, true, timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
if(!writeRegister(ERAYRegister::WRHS2, hs2, true, timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
if(!writeRegister(ERAYRegister::WRHS3, hs3, true, timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
if(!writeRegister(ERAYRegister::IBCM, 1, true, timeout))
|
||||
return false;
|
||||
const auto ibcmstart = std::chrono::steady_clock::now();
|
||||
bool ibcmbusy = isInputBufferHostBusy();
|
||||
while(ibcmbusy && (std::chrono::steady_clock::now() - ibcmstart) < timeout) {
|
||||
ibcmbusy = isInputBufferHostBusy();
|
||||
}
|
||||
if(ibcmbusy) // timeout
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
if(!writeRegister(ERAYRegister::IBCR, i, true, timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
}
|
||||
|
||||
configDirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::getReady(std::chrono::milliseconds timeout) {
|
||||
const auto initialTimeout = timeout;
|
||||
const auto functionBegin = std::chrono::steady_clock::now();
|
||||
const auto updateTimeout = [&initialTimeout, &functionBegin, &timeout]() {
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(initialTimeout - (std::chrono::steady_clock::now() - functionBegin));
|
||||
};
|
||||
|
||||
// Initial sanity check that we have communication with the controller
|
||||
auto endian = readRegister(ERAYRegister::ENDN, timeout);
|
||||
if (!endian.first || endian.second != 0x87654321)
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
auto statusPair = getCurrentPOCStatus(timeout);
|
||||
const auto& pocStatus = statusPair.second;
|
||||
if(!statusPair.first)
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
if(pocStatus == POCStatus::Ready && !configDirty) {
|
||||
// Already in the desired state
|
||||
if(allowColdstart && !setCurrentPOCCommand(FlexRay::POCCommand::AllowColdstart, true, timeout))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(pocStatus != POCStatus::Config) {
|
||||
// Must enter config before continuing
|
||||
if(!enterConfig(timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
// Reconfigure if necessary
|
||||
if(configDirty && !configure(timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
}
|
||||
|
||||
// Enter the READY state
|
||||
if(!lockConfiguration(timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
// Signal that we'd like to coldstart, if necessary
|
||||
if(allowColdstart && !setCurrentPOCCommand(FlexRay::POCCommand::AllowColdstart, true, timeout))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::start(std::chrono::milliseconds timeout) {
|
||||
const auto initialTimeout = timeout;
|
||||
const auto functionBegin = std::chrono::steady_clock::now();
|
||||
const auto updateTimeout = [&initialTimeout, &functionBegin, &timeout]() {
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(initialTimeout - (std::chrono::steady_clock::now() - functionBegin));
|
||||
};
|
||||
|
||||
// First make sure we're ready to start (configured/ready state)
|
||||
if(!getReady(timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
// Wakeup the network if necessary
|
||||
if(wakeupBeforeStart && !wakeup(timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
// And finally run
|
||||
if(!setCurrentPOCCommand(FlexRay::POCCommand::Run, false, timeout))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::transmit(const std::shared_ptr<FlexRayMessage>& frmsg) {
|
||||
bool success = false;
|
||||
|
||||
for(const auto& buf : messageBuffers) {
|
||||
if(!buf->isTransmit)
|
||||
continue;
|
||||
|
||||
if(frmsg->slotid != buf->frameID)
|
||||
continue;
|
||||
|
||||
if(CalculateCycleFilter(frmsg->cycle, frmsg->cycleRepetition) != CalculateCycleFilter(buf->baseCycle, buf->cycleRepetition))
|
||||
continue;
|
||||
|
||||
FlexRay::Channel bufChannel = FlexRay::Channel::None;
|
||||
if(buf->channelA && buf->channelB)
|
||||
bufChannel = FlexRay::Channel::AB;
|
||||
else if(buf->channelA)
|
||||
bufChannel = FlexRay::Channel::A;
|
||||
else if(buf->channelB)
|
||||
bufChannel = FlexRay::Channel::B;
|
||||
|
||||
if(frmsg->channel != bufChannel)
|
||||
continue;
|
||||
|
||||
// If we have added changed our configuration, such as adding a message buffer, we will need to reconfigure
|
||||
if(configDirty && lastSeenRunning)
|
||||
start();
|
||||
|
||||
// This is a message buffer we want to fill
|
||||
if(!device.com->sendCommand(Command::FlexRayControl, FlexRayControlMessage::BuildWriteMessageBufferArgs(index, buf->_id, frmsg->data, buf->frameLengthBytes)))
|
||||
continue;
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::halt(std::chrono::milliseconds timeout) {
|
||||
return setCurrentPOCCommand(POCCommand::Halt, true, timeout);
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::freeze(std::chrono::milliseconds timeout) {
|
||||
return setCurrentPOCCommand(POCCommand::Freeze, true, timeout);
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::triggerMTS(std::chrono::milliseconds timeout) {
|
||||
// triggerMTS will do nothing unless either MTSOnA or MTSOnB (or both) are set at configure time
|
||||
return setCurrentPOCCommand(POCCommand::SendMTS, true, timeout);
|
||||
void FlexRay::Controller::start() {
|
||||
if(true) // TODO something
|
||||
getReady();
|
||||
if(wakeupBeforeStart)
|
||||
setCurrentPOCCommand(FlexRay::POCCommand::Wakeup);
|
||||
if(allowColdstart)
|
||||
setCurrentPOCCommand(FlexRay::POCCommand::AllowColdstart);
|
||||
setCurrentPOCCommand(FlexRay::POCCommand::Run);
|
||||
}
|
||||
|
||||
std::pair<bool, FlexRay::POCCommand> FlexRay::Controller::getCurrentPOCCommand(std::chrono::milliseconds timeout) const {
|
||||
@@ -470,176 +34,38 @@ std::pair<bool, FlexRay::POCCommand> FlexRay::Controller::getCurrentPOCCommand(s
|
||||
|
||||
bool FlexRay::Controller::setCurrentPOCCommand(FlexRay::POCCommand cmd, bool checkForSuccess, std::chrono::milliseconds timeout) {
|
||||
const auto beforeWrite = std::chrono::steady_clock::now();
|
||||
if(!writeRegister(ERAYRegister::SUCC1, uint32_t(cmd), 0xF, true, timeout))
|
||||
|
||||
if(!writeRegister(ERAYRegister::SUCC1, uint32_t(cmd), true, timeout))
|
||||
return false;
|
||||
if(!checkForSuccess)
|
||||
return true;
|
||||
|
||||
const auto writeDuration = std::chrono::steady_clock::now() - beforeWrite;
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(timeout - writeDuration);
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(writeDuration - timeout);
|
||||
if(timeout.count() <= 0)
|
||||
return false; // Out of time!
|
||||
|
||||
const bool success = wasCommandSuccessful(timeout);
|
||||
if(success) {
|
||||
switch(cmd) {
|
||||
case FlexRay::POCCommand::Run:
|
||||
lastSeenRunning = true;
|
||||
break;
|
||||
case FlexRay::POCCommand::Halt:
|
||||
case FlexRay::POCCommand::Freeze:
|
||||
lastSeenRunning = false;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
return wasCommandSuccessful(timeout);
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::wasCommandSuccessful(std::chrono::milliseconds timeout) const {
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
bool pocBusy = isPOCBusy();
|
||||
while(pocBusy && (std::chrono::steady_clock::now() - start) < timeout) {
|
||||
pocBusy = isPOCBusy();
|
||||
}
|
||||
if(pocBusy) // timeout
|
||||
return false;
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(timeout - (std::chrono::steady_clock::now() - start));
|
||||
|
||||
const auto val = getCurrentPOCCommand(timeout);
|
||||
return val.first && val.second != FlexRay::POCCommand::CommandNotAccepted;
|
||||
}
|
||||
|
||||
std::pair<bool, FlexRay::POCStatus> FlexRay::Controller::getCurrentPOCStatus(std::chrono::milliseconds timeout) const {
|
||||
auto regpair = readRegister(ERAYRegister::CCSV, timeout);
|
||||
return { regpair.first, FlexRay::POCStatus(regpair.second & 0x3F) };
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::lockConfiguration(std::chrono::milliseconds timeout) {
|
||||
// This is not anything super special, just the way to get the ERAY out of POC:config
|
||||
// See the ERAY Users Manaual section 4.3.1
|
||||
|
||||
auto beforeWrite = std::chrono::steady_clock::now();
|
||||
if(!writeRegister(ERAYRegister::LCK, 0xCE, true, timeout))
|
||||
return false;
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(timeout - (std::chrono::steady_clock::now() - beforeWrite));
|
||||
if(timeout.count() <= 0)
|
||||
return false; // Out of time!
|
||||
|
||||
beforeWrite = std::chrono::steady_clock::now();
|
||||
if(!writeRegister(ERAYRegister::LCK, 0x31, true, timeout))
|
||||
return false;
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(timeout - (std::chrono::steady_clock::now() - beforeWrite));
|
||||
|
||||
return setCurrentPOCCommand(POCCommand::Ready, true, timeout);
|
||||
}
|
||||
|
||||
bool FlexRay::Controller::enterConfig(std::chrono::milliseconds timeout) {
|
||||
const auto initialTimeout = timeout;
|
||||
const auto functionBegin = std::chrono::steady_clock::now();
|
||||
const auto updateTimeout = [&initialTimeout, &functionBegin, &timeout]() {
|
||||
timeout = std::chrono::duration_cast<std::chrono::milliseconds>(initialTimeout - (std::chrono::steady_clock::now() - functionBegin));
|
||||
};
|
||||
|
||||
auto statusPair = getCurrentPOCStatus(timeout);
|
||||
const auto& pocStatus = statusPair.second;
|
||||
if(!statusPair.first)
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
if(pocStatus != FlexRay::POCStatus::Ready &&
|
||||
pocStatus != FlexRay::POCStatus::Config &&
|
||||
pocStatus != FlexRay::POCStatus::DefaultConfig &&
|
||||
pocStatus != FlexRay::POCStatus::Halt) {
|
||||
if(!setCurrentPOCCommand(FlexRay::POCCommand::Freeze, true, timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
}
|
||||
|
||||
// If we're halted, we first go into DEFAULT_CONFIG before entering CONFIG
|
||||
// Unintuitively, this enters DEFAULT_CONFIG
|
||||
if(!setCurrentPOCCommand(FlexRay::POCCommand::Config, true, timeout))
|
||||
return false;
|
||||
updateTimeout();
|
||||
|
||||
// Now this enters CONFIG
|
||||
return setCurrentPOCCommand(FlexRay::POCCommand::Config, true, timeout);
|
||||
}
|
||||
|
||||
uint16_t FlexRay::Controller::CalculateHCRC(const MessageBuffer& buf) {
|
||||
uint16_t ret = 0x1A;
|
||||
|
||||
auto addBit = [&ret](uint8_t bit) {
|
||||
bit = bit ? 1 : 0;
|
||||
|
||||
int crcNxt; //CRCNXT = NXTBIT EXOR CRC_RG(14);
|
||||
if (ret & (1<<10))
|
||||
crcNxt = bit ^ 1;
|
||||
else
|
||||
crcNxt = bit ^ 0;
|
||||
crcNxt &= 0x01;
|
||||
|
||||
// CRC_RG(14:1) = CRC_RG(13:0); // shift left by
|
||||
ret <<= 1;
|
||||
ret &= 0x7FE; // clear first bit
|
||||
|
||||
if (crcNxt) //CRC_RG(14:0) = CRC_RG(14:0) EXOR (4599hex);
|
||||
ret ^= 0x385;
|
||||
};
|
||||
|
||||
addBit(buf.isStartup);
|
||||
addBit(buf.isSync);
|
||||
for(auto i = 0; i < 11; i++)
|
||||
addBit(buf.frameID & (1 << (10 - i)));
|
||||
for(auto i = 0; i < 7; i++)
|
||||
addBit(((buf.frameLengthBytes + 1) / 2) & (1 << (6 - i)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t FlexRay::Controller::CalculateCycleFilter(uint8_t baseCycle, uint8_t cycleRepetition) {
|
||||
uint8_t cycleRepCode = 0;
|
||||
switch(cycleRepetition) {
|
||||
case 1: cycleRepCode = 0b1; break;
|
||||
case 2: cycleRepCode = 0b10; break;
|
||||
case 4: cycleRepCode = 0b100; break;
|
||||
case 8: cycleRepCode = 0b1000; break;
|
||||
case 16: cycleRepCode = 0b10000; break;
|
||||
case 32: cycleRepCode = 0b100000; break;
|
||||
case 64: cycleRepCode = 0b1000000; break;
|
||||
}
|
||||
return (cycleRepCode | baseCycle);
|
||||
}
|
||||
|
||||
std::pair<bool, uint32_t> FlexRay::Controller::readRegister(ERAYRegister reg, std::chrono::milliseconds timeout) const {
|
||||
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(icsneo::Network::NetID::FlexRayControl);
|
||||
if(timeout.count() <= 20)
|
||||
return {false, 0}; // Out of time!
|
||||
|
||||
std::lock_guard<std::mutex> lk(readRegisterLock);
|
||||
device.com->sendCommand(Command::FlexRayControl, FlexRayControlMessage::BuildReadCCRegsArgs(index, uint16_t(reg)));
|
||||
std::shared_ptr<FlexRayControlMessage> resp;
|
||||
std::chrono::steady_clock::time_point lastSent;
|
||||
do {
|
||||
const auto waitTime = std::chrono::steady_clock::now();
|
||||
auto msg = device.com->waitForMessageSync([this, &lastSent, ®, &timeout]() {
|
||||
if(timeout.count() < 20)
|
||||
return true; // Might not have time to receive the response, so don't request
|
||||
if(std::chrono::steady_clock::now() - lastSent < std::chrono::milliseconds(40))
|
||||
return true; // Don't send too fast
|
||||
|
||||
if(!device.com->sendCommand(Command::FlexRayControl, FlexRayControlMessage::BuildReadCCRegsArgs(index, uint16_t(reg))))
|
||||
return false; // Command failed to send
|
||||
lastSent = std::chrono::steady_clock::now();
|
||||
return true;
|
||||
}, filter, timeout);
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
while(!resp && (std::chrono::steady_clock::now() - start) < timeout) {
|
||||
auto msg = device.com->waitForMessageSync(MessageFilter(icsneo::Network::NetID::FlexRayControl), timeout);
|
||||
if(auto frmsg = std::dynamic_pointer_cast<FlexRayControlMessage>(msg)) {
|
||||
if(frmsg->decoded && frmsg->controller == index && frmsg->opcode == FlexRay::Opcode::ReadCCRegs)
|
||||
resp = frmsg;
|
||||
}
|
||||
if(resp)
|
||||
break;
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - waitTime);
|
||||
} while(timeout.count() > 0);
|
||||
|
||||
if(resp && !resp->registers.empty())
|
||||
}
|
||||
if(resp)
|
||||
return {true, resp->registers[0]};
|
||||
else
|
||||
return {false, 0};
|
||||
@@ -657,23 +83,15 @@ bool FlexRay::Controller::writeRegister(
|
||||
bool waitForPOCReady,
|
||||
std::chrono::milliseconds timeout) {
|
||||
|
||||
if(waitForPOCReady) {
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
bool pocBusy = isPOCBusy();
|
||||
while(pocBusy && (std::chrono::steady_clock::now() - start) < timeout) {
|
||||
pocBusy = isPOCBusy();
|
||||
}
|
||||
if(pocBusy) // timeout
|
||||
return false;
|
||||
}
|
||||
|
||||
if(mask != 0xffffffff) {
|
||||
const auto beforeRead = std::chrono::steady_clock::now();
|
||||
auto pair = readRegister(reg, timeout);
|
||||
if(!pair.first)
|
||||
return false; // Couldn't read, so we don't want to try to write anything
|
||||
auto readDuration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - beforeRead);
|
||||
timeout -= readDuration;
|
||||
timeout = readDuration - timeout;
|
||||
if(timeout.count() <= 0)
|
||||
return false; // Out of time!
|
||||
pair.second &= ~mask;
|
||||
pair.second |= value & mask;
|
||||
value = pair.second;
|
||||
@@ -686,8 +104,6 @@ bool FlexRay::Controller::writeRegister(
|
||||
uint32_t value,
|
||||
bool waitForPOCReady,
|
||||
std::chrono::milliseconds timeout) {
|
||||
if(timeout.count() <= 0)
|
||||
return false; // Out of time!
|
||||
|
||||
if(waitForPOCReady) {
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
@@ -699,7 +115,6 @@ bool FlexRay::Controller::writeRegister(
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!device.com->sendCommand(Command::FlexRayControl, FlexRayControlMessage::BuildWriteCCRegArgs(index, uint16_t(reg), value)))
|
||||
return false;
|
||||
return true; // The device does not confirm the the command, if it did we'd put that here
|
||||
device.com->sendCommand(Command::FlexRayControl, FlexRayControlMessage::BuildWriteCCRegArgs(index, uint16_t(reg), value));
|
||||
return true; // Does the device send anything back to tell us this actually happened?
|
||||
}
|
||||
@@ -1,41 +1,26 @@
|
||||
#include "icsneo/device/extensions/flexray/extension.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/message/flexray/flexraymessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
FlexRay::Extension::Extension(Device& device, const std::vector<Network>& controllerNetworks) : DeviceExtension(device) {
|
||||
for(uint8_t i = 0; i < controllerNetworks.size(); i++)
|
||||
controllers.emplace_back(std::make_shared<FlexRay::Controller>(device, i, controllerNetworks[i]));
|
||||
}
|
||||
|
||||
void FlexRay::Extension::onGoOnline() {
|
||||
for(auto& controller : controllers) {
|
||||
if(controller->getStartWhenGoingOnline())
|
||||
controller->getReady();
|
||||
}
|
||||
for(auto& controller : controllers) {
|
||||
if(controller->getStartWhenGoingOnline())
|
||||
controller->start();
|
||||
}
|
||||
}
|
||||
|
||||
void FlexRay::Extension::onGoOffline() {
|
||||
for(auto& controller : controllers)
|
||||
controller->halt();
|
||||
FlexRay::Extension::Extension(Device& device, uint8_t controllerCount) : DeviceExtension(device) {
|
||||
for(uint8_t i = 0; i < controllerCount; i++)
|
||||
controllers.emplace_back(std::make_shared<FlexRay::Controller>(device, i));
|
||||
}
|
||||
|
||||
void FlexRay::Extension::handleMessage(const std::shared_ptr<Message>& message) {
|
||||
switch(message->type) {
|
||||
case Message::Type::FlexRayControl: {
|
||||
switch(message->network.getNetID()) {
|
||||
case Network::NetID::FlexRayControl: {
|
||||
auto msg = std::dynamic_pointer_cast<FlexRayControlMessage>(message);
|
||||
if(!msg || !msg->decoded)
|
||||
return;
|
||||
switch(msg->opcode) {
|
||||
case FlexRay::Opcode::ReadCCStatus:
|
||||
if(msg->controller >= controllers.size())
|
||||
return; // TODO error
|
||||
controllers[msg->controller]->_setStatus(msg);
|
||||
if(auto status = std::dynamic_pointer_cast<FlexRayControlMessage>(message)) { // TODO else report error?
|
||||
if(status->controller >= controllers.size())
|
||||
return; // TODO error
|
||||
controllers[status->controller]->_setStatus(status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -43,23 +28,4 @@ void FlexRay::Extension::handleMessage(const std::shared_ptr<Message>& message)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool FlexRay::Extension::transmitHook(const std::shared_ptr<Frame>& frame, bool& success) {
|
||||
if(!frame || frame->network.getType() != Network::Type::FlexRay)
|
||||
return true; // Don't hook non-FlexRay messages
|
||||
|
||||
success = false;
|
||||
|
||||
std::shared_ptr<FlexRayMessage> frmsg = std::dynamic_pointer_cast<FlexRayMessage>(frame);
|
||||
if(!frmsg)
|
||||
return false;
|
||||
|
||||
for(auto& controller : controllers) {
|
||||
if(controller->getNetwork() != frame->network)
|
||||
continue;
|
||||
success |= controller->transmit(frmsg);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
+58
-496
@@ -4,23 +4,23 @@
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::optional<uint16_t> IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settings, std::optional<size_t> knownSize) {
|
||||
const uint16_t* p = reinterpret_cast<const uint16_t*>(settings.data());
|
||||
size_t words = std::min(knownSize.value_or(0), settings.size());
|
||||
uint16_t IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settings) {
|
||||
uint16_t gs_crc = 0;
|
||||
const uint16_t* p = (const uint16_t*)settings.data();
|
||||
size_t words = settings.size();
|
||||
if(words % 2 == 1)
|
||||
return std::nullopt; // Somehow settings is not word aligned
|
||||
return 0xFFFF; // Somehow settings is not word aligned
|
||||
words /= 2;
|
||||
|
||||
uint16_t gsCrc = 0;
|
||||
while(words--) {
|
||||
uint16_t temp = *p;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
bool iBit = temp & 1;
|
||||
|
||||
|
||||
int iCrcNxt;
|
||||
//CRCNXT = NXTBIT EXOR CRC_RG(15);
|
||||
if (gsCrc & (1 << 15))
|
||||
if (gs_crc & (1 << 15))
|
||||
iCrcNxt = iBit ^ 1;
|
||||
else
|
||||
iCrcNxt = iBit;
|
||||
@@ -28,18 +28,18 @@ std::optional<uint16_t> IDeviceSettings::CalculateGSChecksum(const std::vector<u
|
||||
|
||||
|
||||
// CRC_RG(15:1) = CRC_RG(14:0); // shift left by
|
||||
gsCrc = gsCrc << 1;
|
||||
gsCrc = gsCrc & 0xFFFE;// clear first bit
|
||||
gs_crc = gs_crc << 1;
|
||||
gs_crc = gs_crc & 0xFFFE;// clear first bit
|
||||
|
||||
if (iCrcNxt)//CRC_RG(14:0) = CRC_RG(14:0) EXOR (4599hex);
|
||||
gsCrc = gsCrc ^ 0xa001;
|
||||
gs_crc = gs_crc ^ 0xa001;
|
||||
|
||||
temp >>= 1;
|
||||
}
|
||||
|
||||
p++;
|
||||
}
|
||||
return gsCrc;
|
||||
return gs_crc;
|
||||
}
|
||||
|
||||
CANBaudrate IDeviceSettings::GetEnumValueForBaudrate(int64_t baudrate) {
|
||||
@@ -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);
|
||||
@@ -175,40 +144,30 @@ bool IDeviceSettings::refresh(bool ignoreChecksum) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr size_t GsSize = 3 * sizeof(uint16_t);
|
||||
if(rxSettings.size() < GsSize) { // We need to at least have the header of GLOBAL_SETTINGS
|
||||
if(rxSettings.size() < 6) { // We need to at least have the header of GLOBAL_SETTINGS
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The length of the settings structure sent to us
|
||||
// This is the length the firmware thinks the current version of the structure is
|
||||
const size_t rxLen = rxSettings.size() - GsSize;
|
||||
constexpr size_t gs_size = 3 * sizeof(uint16_t);
|
||||
size_t rxLen = rxSettings.size() - gs_size;
|
||||
|
||||
const uint16_t gsVersion = rxSettings[0] | (rxSettings[1] << 8);
|
||||
uint16_t gs_version = rxSettings[0] | (rxSettings[1] << 8);
|
||||
uint16_t gs_len = rxSettings[2] | (rxSettings[3] << 8);
|
||||
uint16_t gs_chksum = rxSettings[4] | (rxSettings[5] << 8);
|
||||
rxSettings.erase(rxSettings.begin(), rxSettings.begin() + gs_size);
|
||||
|
||||
// The length of the settings last saved
|
||||
// If the firmware is updated, it will have either extended (with zeros) or truncated
|
||||
// the structure, but this value will continue to be set to the last saved value
|
||||
const uint16_t gsLen = rxSettings[2] | (rxSettings[3] << 8);
|
||||
|
||||
const uint16_t gsChecksum = rxSettings[4] | (rxSettings[5] << 8);
|
||||
rxSettings.erase(rxSettings.begin(), rxSettings.begin() + GsSize);
|
||||
|
||||
if(gsVersion != GS_VERSION) {
|
||||
if(gs_version != 5) {
|
||||
report(APIEvent::Type::SettingsVersionError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(rxLen < gsLen) {
|
||||
// We got less data, i.e. the firmware thinks the strucure is smaller than what
|
||||
// was last saved. Usually this is due to a firmware downgrade. We'll ignore the
|
||||
// checksum for now, because it will definitely be wrong.
|
||||
ignoreChecksum = true;
|
||||
if(rxLen != gs_len) {
|
||||
report(APIEvent::Type::SettingsLengthError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We check the checksum against the data last saved
|
||||
if(!ignoreChecksum && gsChecksum != CalculateGSChecksum(rxSettings, gsLen)) {
|
||||
if(!ignoreChecksum && gs_chksum != CalculateGSChecksum(rxSettings)) {
|
||||
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
@@ -218,7 +177,7 @@ bool IDeviceSettings::refresh(bool ignoreChecksum) {
|
||||
settingsLoaded = true;
|
||||
|
||||
// TODO Warn user that their API version differs from the device firmware version
|
||||
//if(settings.size() != structSize)
|
||||
//if(settings.size() != structSize)
|
||||
|
||||
return settingsLoaded;
|
||||
}
|
||||
@@ -246,26 +205,17 @@ bool IDeviceSettings::apply(bool temporary) {
|
||||
bytestream[2] = GS_VERSION >> 8;
|
||||
bytestream[3] = (uint8_t)settings.size();
|
||||
bytestream[4] = (uint8_t)(settings.size() >> 8);
|
||||
std::optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
||||
if(!gsChecksum) {
|
||||
// Could not calculate the checksum for some reason
|
||||
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
bytestream[5] = (uint8_t)*gsChecksum;
|
||||
bytestream[6] = (uint8_t)(*gsChecksum >> 8);
|
||||
uint16_t gs_checksum = CalculateGSChecksum(settings);
|
||||
bytestream[5] = (uint8_t)gs_checksum;
|
||||
bytestream[6] = (uint8_t)(gs_checksum >> 8);
|
||||
memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size());
|
||||
|
||||
// Pause I/O with the device while the settings are applied
|
||||
applyingSettings = true;
|
||||
|
||||
std::shared_ptr<Main51Message> msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this, &bytestream]() {
|
||||
return com->sendCommand(Command::SetSettings, bytestream);
|
||||
}, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(1000)));
|
||||
com->sendCommand(Command::SetSettings, bytestream);
|
||||
std::shared_ptr<Message> msg = com->waitForMessageSync(std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(1000));
|
||||
|
||||
if(!msg || msg->data[0] != 1) { // We did not receive a response
|
||||
// Attempt to get the settings from the device so we're up to date if possible
|
||||
if(refresh()) {
|
||||
if(refresh()) {
|
||||
// refresh succeeded but previously there was an error
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
}
|
||||
@@ -275,22 +225,16 @@ bool IDeviceSettings::apply(bool temporary) {
|
||||
refresh(true); // Refresh ignoring checksum
|
||||
// The device might modify the settings once they are applied, however in this case it does not update the checksum
|
||||
// We refresh to get these updates, update the checksum, and send it back so it's all in sync
|
||||
gsChecksum = CalculateGSChecksum(settings);
|
||||
if(!gsChecksum) {
|
||||
// Could not calculate the checksum for some reason
|
||||
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
bytestream[5] = (uint8_t)*gsChecksum;
|
||||
bytestream[6] = (uint8_t)(*gsChecksum >> 8);
|
||||
gs_checksum = CalculateGSChecksum(settings);
|
||||
bytestream[5] = (uint8_t)gs_checksum;
|
||||
bytestream[6] = (uint8_t)(gs_checksum >> 8);
|
||||
memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size());
|
||||
|
||||
msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this, &bytestream]() {
|
||||
return com->sendCommand(Command::SetSettings, bytestream);
|
||||
}, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(1000)));
|
||||
com->sendCommand(Command::SetSettings, bytestream);
|
||||
msg = com->waitForMessageSync(std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(1000));
|
||||
if(!msg || msg->data[0] != 1) {
|
||||
// Attempt to get the settings from the device so we're up to date if possible
|
||||
if(refresh()) {
|
||||
if(refresh()) {
|
||||
// refresh succeeded but previously there was an error
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
}
|
||||
@@ -298,20 +242,17 @@ bool IDeviceSettings::apply(bool temporary) {
|
||||
}
|
||||
|
||||
if(!temporary) {
|
||||
msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this]() {
|
||||
return com->sendCommand(Command::SaveSettings);
|
||||
}, std::make_shared<Main51MessageFilter>(Command::SaveSettings), std::chrono::milliseconds(5000)));
|
||||
com->sendCommand(Command::SaveSettings);
|
||||
msg = com->waitForMessageSync(std::make_shared<Main51MessageFilter>(Command::SaveSettings), std::chrono::milliseconds(5000));
|
||||
}
|
||||
|
||||
applyingSettings = false;
|
||||
|
||||
|
||||
refresh(); // Refresh our buffer with what the device has, whether we were successful or not
|
||||
|
||||
bool ret = (msg && msg->data[0] == 1); // Device sends 0x01 for success
|
||||
if(!ret) {
|
||||
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
|
||||
}
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IDeviceSettings::applyDefaults(bool temporary) {
|
||||
@@ -325,14 +266,11 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
|
||||
return false;
|
||||
}
|
||||
|
||||
applyingSettings = true;
|
||||
|
||||
std::shared_ptr<Main51Message> msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this]() {
|
||||
return com->sendCommand(Command::SetDefaultSettings);
|
||||
}, std::make_shared<Main51MessageFilter>(Command::SetDefaultSettings), std::chrono::milliseconds(1000)));
|
||||
com->sendCommand(Command::SetDefaultSettings);
|
||||
std::shared_ptr<Message> msg = com->waitForMessageSync(std::make_shared<Main51MessageFilter>(Command::SetDefaultSettings), std::chrono::milliseconds(1000));
|
||||
if(!msg || msg->data[0] != 1) {
|
||||
// Attempt to get the settings from the device so we're up to date if possible
|
||||
if(refresh()) {
|
||||
if(refresh()) {
|
||||
// refresh succeeded but previously there was an error
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
}
|
||||
@@ -352,22 +290,16 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
|
||||
bytestream[2] = GS_VERSION >> 8;
|
||||
bytestream[3] = (uint8_t)settings.size();
|
||||
bytestream[4] = (uint8_t)(settings.size() >> 8);
|
||||
const std::optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
||||
if(!gsChecksum) {
|
||||
// Could not calculate the checksum for some reason
|
||||
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
bytestream[5] = (uint8_t)*gsChecksum;
|
||||
bytestream[6] = (uint8_t)(*gsChecksum >> 8);
|
||||
uint16_t gs_checksum = CalculateGSChecksum(settings);
|
||||
bytestream[5] = (uint8_t)gs_checksum;
|
||||
bytestream[6] = (uint8_t)(gs_checksum >> 8);
|
||||
memcpy(bytestream.data() + 7, getMutableRawStructurePointer(), settings.size());
|
||||
|
||||
msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this, &bytestream]() {
|
||||
return com->sendCommand(Command::SetSettings, bytestream);
|
||||
}, std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(1000)));
|
||||
com->sendCommand(Command::SetSettings, bytestream);
|
||||
msg = com->waitForMessageSync(std::make_shared<Main51MessageFilter>(Command::SetSettings), std::chrono::milliseconds(1000));
|
||||
if(!msg || msg->data[0] != 1) {
|
||||
// Attempt to get the settings from the device so we're up to date if possible
|
||||
if(refresh()) {
|
||||
if(refresh()) {
|
||||
// refresh succeeded but previously there was an error
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
}
|
||||
@@ -375,20 +307,17 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
|
||||
}
|
||||
|
||||
if(!temporary) {
|
||||
msg = std::dynamic_pointer_cast<Main51Message>(com->waitForMessageSync([this]() {
|
||||
return com->sendCommand(Command::SaveSettings);
|
||||
}, std::make_shared<Main51MessageFilter>(Command::SaveSettings), std::chrono::milliseconds(5000)));
|
||||
com->sendCommand(Command::SaveSettings);
|
||||
msg = com->waitForMessageSync(std::make_shared<Main51MessageFilter>(Command::SaveSettings), std::chrono::milliseconds(5000));
|
||||
}
|
||||
|
||||
applyingSettings = false;
|
||||
|
||||
|
||||
refresh(); // Refresh our buffer with what the device has, whether we were successful or not
|
||||
|
||||
bool ret = (msg && msg->data[0] == 1); // Device sends 0x01 for success
|
||||
if(!ret) {
|
||||
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
|
||||
}
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t IDeviceSettings::getBaudrateFor(Network net) const {
|
||||
@@ -406,7 +335,7 @@ int64_t IDeviceSettings::getBaudrateFor(Network net) const {
|
||||
case Network::Type::CAN: {
|
||||
const CAN_SETTINGS* cfg = getCANSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::CANSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
report(APIEvent::Type::CANFDSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -417,43 +346,6 @@ int64_t IDeviceSettings::getBaudrateFor(Network net) const {
|
||||
}
|
||||
return baudrate;
|
||||
}
|
||||
case Network::Type::SWCAN: {
|
||||
const SWCAN_SETTINGS* cfg = getSWCANSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::SWCANSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t baudrate = GetBaudrateValueForEnum((CANBaudrate)cfg->Baudrate);
|
||||
if(baudrate == -1) {
|
||||
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
|
||||
return -1;
|
||||
}
|
||||
return baudrate;
|
||||
}
|
||||
case Network::Type::LSFTCAN: {
|
||||
const CAN_SETTINGS* cfg = getLSFTCANSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LSFTCANSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t baudrate = GetBaudrateValueForEnum((CANBaudrate)cfg->Baudrate);
|
||||
if(baudrate == -1) {
|
||||
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
@@ -488,7 +380,7 @@ bool IDeviceSettings::setBaudrateFor(Network net, int64_t baudrate) {
|
||||
report(APIEvent::Type::CANSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
CANBaudrate newBaud = GetEnumValueForBaudrate(baudrate);
|
||||
if(newBaud == (CANBaudrate)-1) {
|
||||
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
|
||||
@@ -505,7 +397,7 @@ bool IDeviceSettings::setBaudrateFor(Network net, int64_t baudrate) {
|
||||
report(APIEvent::Type::LSFTCANSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
CANBaudrate newBaud = GetEnumValueForBaudrate(baudrate);
|
||||
if(newBaud == (CANBaudrate)-1) {
|
||||
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
|
||||
@@ -522,7 +414,7 @@ bool IDeviceSettings::setBaudrateFor(Network net, int64_t baudrate) {
|
||||
report(APIEvent::Type::SWCANSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
CANBaudrate newBaud = GetEnumValueForBaudrate(baudrate);
|
||||
if(newBaud == (CANBaudrate)-1) {
|
||||
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
|
||||
@@ -533,21 +425,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;
|
||||
@@ -557,7 +434,6 @@ bool IDeviceSettings::setBaudrateFor(Network net, int64_t baudrate) {
|
||||
int64_t IDeviceSettings::getFDBaudrateFor(Network net) const {
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!settingsLoaded) {
|
||||
@@ -625,320 +501,6 @@ bool IDeviceSettings::setFDBaudrateFor(Network net, int64_t baudrate) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IDeviceSettings::isTerminationSupportedFor(Network net) const {
|
||||
for(const auto& group : getTerminationGroups()) {
|
||||
for(const auto& supportedNet : group) {
|
||||
if(net == supportedNet)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IDeviceSettings::canTerminationBeEnabledFor(Network net) const {
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Even though we will not be writing here, if the settings are read only the termination will not be enablable
|
||||
if(readonly) {
|
||||
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reference the mutable termination enables as we want to allow a disable/enable within a group without applying
|
||||
ICSNEO_UNALIGNED(const uint64_t*) currentQueuedTerminationEnables = const_cast<IDeviceSettings*>(this)->getMutableTerminationEnables();
|
||||
if(currentQueuedTerminationEnables == nullptr) {
|
||||
report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto& group : getTerminationGroups()) {
|
||||
bool found = false;
|
||||
for(const auto& supportedNet : group) {
|
||||
if(net == supportedNet) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found) {
|
||||
for(const auto& supportedNet : group) {
|
||||
// Allow termination on the current network even if it's already enabled
|
||||
if(net == supportedNet)
|
||||
continue;
|
||||
|
||||
const auto cmNet = supportedNet.getCoreMini();
|
||||
if(!cmNet.has_value() || uint64_t(*cmNet) >= 64) {
|
||||
// Hitting this assert means that a supported network has an invalid CoreMini Network ID
|
||||
assert(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this network is enabled, it excludes the queried network from being enabled
|
||||
if((*currentQueuedTerminationEnables >> uint64_t(*cmNet)) & 0x1) {
|
||||
report(APIEvent::Type::AnotherInTerminationGroupEnabled, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<bool> IDeviceSettings::isTerminationEnabledFor(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;
|
||||
}
|
||||
|
||||
ICSNEO_UNALIGNED(const uint64_t*) terminationEnables = getTerminationEnables();
|
||||
if(terminationEnables == nullptr) {
|
||||
report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto cmNet = net.getCoreMini();
|
||||
if(!cmNet.has_value() || uint64_t(*cmNet) >= 64 || !isTerminationSupportedFor(net)) {
|
||||
report(APIEvent::Type::TerminationNotSupportedNetwork, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (*terminationEnables >> uint64_t(*cmNet)) & 0x1;
|
||||
}
|
||||
|
||||
bool IDeviceSettings::setTerminationFor(Network net, bool enabled) {
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(readonly) {
|
||||
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
ICSNEO_UNALIGNED(uint64_t*) terminationEnables = getMutableTerminationEnables();
|
||||
if(terminationEnables == nullptr) {
|
||||
report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function reports its own error statuses
|
||||
if(!canTerminationBeEnabledFor(net))
|
||||
return false;
|
||||
|
||||
const auto cmNet = net.getCoreMini();
|
||||
if(!cmNet.has_value() || uint8_t(*cmNet) >= 64) {
|
||||
report(APIEvent::Type::TerminationNotSupportedNetwork, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t mask = 1ull << uint8_t(*cmNet);
|
||||
if(enabled)
|
||||
*terminationEnables |= mask;
|
||||
else
|
||||
*terminationEnables &= ~mask;
|
||||
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);
|
||||
@@ -954,7 +516,7 @@ template<typename T> bool IDeviceSettings::applyStructure(const T& newStructure)
|
||||
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// This function is only called from C++ so the caller's structure size and ours should never differ
|
||||
if(sizeof(T) != structSize) {
|
||||
report(APIEvent::Type::SettingsStructureMismatch, APIEvent::Severity::Error);
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#include "icsneo/device/neodevice.h"
|
||||
#include "icsneo/device/founddevice.h"
|
||||
#include <cstring>
|
||||
|
||||
neodevice_t::neodevice_t() : device(nullptr), handle(0), type(0) {
|
||||
memset(serial, 0, sizeof(serial));
|
||||
}
|
||||
|
||||
neodevice_t::neodevice_t(const icsneo::FoundDevice& found, devicetype_t inType)
|
||||
: device(nullptr), handle(found.handle), type(inType) {
|
||||
static_assert(sizeof(found.serial) == sizeof(serial), "Serial sizes should match!");
|
||||
memcpy(serial, found.serial, sizeof(serial));
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
#include "icsneo/disk/diskreaddriver.h"
|
||||
#include <cstring>
|
||||
|
||||
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;
|
||||
|
||||
if(amount == 0)
|
||||
return 0;
|
||||
|
||||
pos += vsaOffset;
|
||||
|
||||
// First read from the cache
|
||||
std::optional<uint64_t> ret = readFromCache(pos, into, amount);
|
||||
if(ret == amount) // Full cache hit, we're done
|
||||
return ret;
|
||||
|
||||
const uint64_t totalAmount = amount;
|
||||
if(ret.has_value()) { // Partial cache hit
|
||||
pos += *ret;
|
||||
into += *ret;
|
||||
amount -= *ret;
|
||||
}
|
||||
|
||||
// Read into here if we can't read directly into the user buffer
|
||||
// That would be the case either if we don't want some at the
|
||||
// beginning or end of the block.
|
||||
std::vector<uint8_t> alignedReadBuffer;
|
||||
|
||||
const uint32_t idealBlockSize = getBlockSizeBounds().second;
|
||||
const uint64_t startBlock = pos / idealBlockSize;
|
||||
const uint32_t posWithinFirstBlock = static_cast<uint32_t>(pos % idealBlockSize);
|
||||
uint64_t blocks = amount / idealBlockSize + (amount % idealBlockSize ? 1 : 0);
|
||||
if(blocks * idealBlockSize - posWithinFirstBlock < amount)
|
||||
blocks++; // We need one more block to get the last partial block's worth
|
||||
uint64_t blocksProcessed = 0;
|
||||
|
||||
while(blocksProcessed < blocks && timeout >= std::chrono::milliseconds::zero()) {
|
||||
const uint64_t currentBlock = startBlock + blocksProcessed;
|
||||
|
||||
uint64_t intoOffset = blocksProcessed * idealBlockSize;;
|
||||
if(intoOffset < posWithinFirstBlock)
|
||||
intoOffset = 0;
|
||||
else
|
||||
intoOffset -= posWithinFirstBlock;
|
||||
|
||||
const uint32_t posWithinCurrentBlock = (blocksProcessed ? 0 : posWithinFirstBlock);
|
||||
uint32_t curAmt = idealBlockSize - posWithinCurrentBlock;
|
||||
const auto amountLeft = totalAmount - ret.value_or(0);
|
||||
if(curAmt > amountLeft)
|
||||
curAmt = static_cast<uint32_t>(amountLeft);
|
||||
|
||||
const bool useAlignedReadBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
|
||||
if(useAlignedReadBuffer && alignedReadBuffer.size() < idealBlockSize)
|
||||
alignedReadBuffer.resize(idealBlockSize);
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto readAmount = readLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
||||
useAlignedReadBuffer ? alignedReadBuffer.data() : (into + intoOffset), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(!readAmount.has_value() || *readAmount < curAmt) {
|
||||
if(timeout < std::chrono::milliseconds::zero())
|
||||
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
|
||||
else
|
||||
report((blocksProcessed || readAmount.value_or(0u) != 0u) ? APIEvent::Type::EOFReached :
|
||||
APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
|
||||
if(useAlignedReadBuffer)
|
||||
memcpy(into + intoOffset, alignedReadBuffer.data() + posWithinCurrentBlock, curAmt);
|
||||
|
||||
if(!ret)
|
||||
ret.emplace();
|
||||
*ret += std::min<uint64_t>(*readAmount, curAmt);
|
||||
blocksProcessed++;
|
||||
|
||||
if(blocksProcessed == blocks) {
|
||||
// Last block, add to the cache
|
||||
if(useAlignedReadBuffer) {
|
||||
cache = std::move(alignedReadBuffer);
|
||||
} else {
|
||||
if(cache.size() != idealBlockSize)
|
||||
cache.resize(idealBlockSize);
|
||||
memcpy(cache.data(), into + intoOffset, idealBlockSize);
|
||||
}
|
||||
cachePos = currentBlock * idealBlockSize;
|
||||
cachedAt = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if(cache.empty())
|
||||
return std::nullopt; // Nothing in the cache
|
||||
|
||||
if(cachedAt + staleAfter < std::chrono::steady_clock::now())
|
||||
return std::nullopt; // Cache is stale
|
||||
|
||||
if(pos > cachePos + cache.size() || pos < cachePos)
|
||||
return std::nullopt; // Cache miss
|
||||
|
||||
const auto cacheOffset = pos - cachePos;
|
||||
const auto copyAmount = std::min<uint64_t>(cache.size() - cacheOffset, amount);
|
||||
memcpy(into, cache.data() + cacheOffset, static_cast<size_t>(copyAmount));
|
||||
return copyAmount;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#include "icsneo/disk/diskwritedriver.h"
|
||||
#include <cstring>
|
||||
|
||||
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) {
|
||||
if(amount == 0)
|
||||
return 0;
|
||||
|
||||
std::optional<uint64_t> ret;
|
||||
|
||||
const uint32_t idealBlockSize = getBlockSizeBounds().second;
|
||||
|
||||
// Write from here if we need to read-modify-write a block
|
||||
// That would be the case either if we don't want some at the
|
||||
// beginning or end of the block.
|
||||
std::vector<uint8_t> alignedWriteBuffer(idealBlockSize);
|
||||
|
||||
pos += vsaOffset;
|
||||
const uint64_t startBlock = pos / idealBlockSize;
|
||||
const uint32_t posWithinFirstBlock = static_cast<uint32_t>(pos % idealBlockSize);
|
||||
uint64_t blocks = amount / idealBlockSize + (amount % idealBlockSize ? 1 : 0);
|
||||
if(blocks * idealBlockSize - posWithinFirstBlock < amount)
|
||||
blocks++; // We need one more block to get the last partial block's worth
|
||||
uint64_t blocksProcessed = 0;
|
||||
|
||||
while(blocksProcessed < blocks && timeout >= std::chrono::milliseconds::zero()) {
|
||||
const uint64_t currentBlock = startBlock + blocksProcessed;
|
||||
|
||||
uint64_t fromOffset = blocksProcessed * idealBlockSize;
|
||||
if(fromOffset < posWithinFirstBlock)
|
||||
fromOffset = 0;
|
||||
else
|
||||
fromOffset -= posWithinFirstBlock;
|
||||
|
||||
const uint32_t posWithinCurrentBlock = (blocksProcessed ? 0 : posWithinFirstBlock);
|
||||
uint32_t curAmt = idealBlockSize - posWithinCurrentBlock;
|
||||
const auto amountLeft = amount - ret.value_or(0);
|
||||
if(curAmt > amountLeft)
|
||||
curAmt = static_cast<uint32_t>(amountLeft);
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
const auto reportFromRead = [&report, &blocksProcessed](APIEvent::Type t, APIEvent::Severity s) {
|
||||
if(t == APIEvent::Type::ParameterOutOfRange && blocksProcessed)
|
||||
t = APIEvent::Type::EOFReached;
|
||||
report(t, s);
|
||||
};
|
||||
|
||||
const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
|
||||
if(useAlignedWriteBuffer) {
|
||||
auto read = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
|
||||
alignedWriteBuffer.data(), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(read != idealBlockSize)
|
||||
break; // readLogicalDisk reports its own errors
|
||||
|
||||
memcpy(alignedWriteBuffer.data() + posWithinCurrentBlock, from + fromOffset, curAmt);
|
||||
}
|
||||
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
||||
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(!bytesTransferred.has_value() || *bytesTransferred < curAmt) {
|
||||
if(timeout < std::chrono::milliseconds::zero())
|
||||
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
|
||||
else
|
||||
report((blocksProcessed || bytesTransferred.value_or(0u) != 0u) ? APIEvent::Type::EOFReached :
|
||||
APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!ret)
|
||||
ret.emplace();
|
||||
*ret += std::min<uint64_t>(*bytesTransferred, curAmt);
|
||||
blocksProcessed++;
|
||||
}
|
||||
|
||||
// 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);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
#include "icsneo/disk/extextractordiskreaddriver.h"
|
||||
#include "icsneo/communication/message/diskdatamessage.h"
|
||||
|
||||
//#define ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
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) {
|
||||
|
||||
if(amount > getBlockSizeBounds().second)
|
||||
return std::nullopt;
|
||||
|
||||
if(amount % getBlockSizeBounds().first != 0)
|
||||
return std::nullopt;
|
||||
|
||||
if(pos % getBlockSizeBounds().first != 0)
|
||||
return std::nullopt;
|
||||
|
||||
std::optional<uint64_t> ret;
|
||||
unsigned int attempts = 4;
|
||||
while (attempts-- > 0)
|
||||
{
|
||||
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout, memType);
|
||||
if (ret.has_value())
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
|
||||
uint64_t sector = pos / SectorSize;
|
||||
|
||||
uint64_t largeSectorCount = amount / SectorSize;
|
||||
uint32_t sectorCount = uint32_t(largeSectorCount);
|
||||
if (largeSectorCount != uint64_t(sectorCount))
|
||||
return std::nullopt;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
uint8_t* intoOffset = into;
|
||||
int64_t remaining = amount;
|
||||
bool complete = false;
|
||||
|
||||
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;
|
||||
}
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
}, std::make_shared<MessageFilter>(Network::NetID::DiskData)));
|
||||
|
||||
if(!com.sendCommand(ExtendedCommand::Extract, {
|
||||
uint8_t(sector & 0xff),
|
||||
uint8_t((sector >> 8) & 0xff),
|
||||
uint8_t((sector >> 16) & 0xff),
|
||||
uint8_t((sector >> 24) & 0xff),
|
||||
uint8_t((sector >> 32) & 0xff),
|
||||
uint8_t((sector >> 40) & 0xff),
|
||||
uint8_t((sector >> 48) & 0xff),
|
||||
uint8_t((sector >> 56) & 0xff),
|
||||
uint8_t(sectorCount & 0xff),
|
||||
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)
|
||||
return std::nullopt;
|
||||
|
||||
return amount - remaining;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
#include "icsneo/disk/fat.h"
|
||||
#include "icsneo/disk/diskdriver.h"
|
||||
#include "ff.h"
|
||||
#include "diskio.h"
|
||||
#include <mutex>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
// The FAT driver can only be accessed by one caller at a time, since it relies on globals
|
||||
static std::mutex fatDriverMutex;
|
||||
static std::function< std::optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskReadFn;
|
||||
|
||||
extern "C" DRESULT disk_read(BYTE, BYTE* buff, LBA_t sector, UINT count) {
|
||||
static_assert(Disk::SectorSize == 512, "FatFs expects 512 byte sectors");
|
||||
|
||||
const uint64_t expected = count * uint64_t(Disk::SectorSize);
|
||||
const auto res = diskReadFn(sector * uint64_t(Disk::SectorSize), buff, expected);
|
||||
if (!res.has_value())
|
||||
return RES_NOTRDY;
|
||||
return res == expected ? RES_OK : RES_ERROR;
|
||||
}
|
||||
|
||||
extern "C" DSTATUS disk_initialize(BYTE) {
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
extern "C" DSTATUS disk_status(BYTE) {
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
static uint64_t ClusterToSector(const FATFS& fs, DWORD cluster) {
|
||||
return fs.database + (LBA_t)fs.csize * (cluster - 2);
|
||||
}
|
||||
|
||||
std::optional<uint64_t> Disk::FindVSAInFAT(std::function< std::optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskRead) {
|
||||
std::lock_guard<std::mutex> lk(fatDriverMutex);
|
||||
diskReadFn = diskRead;
|
||||
|
||||
FATFS fs = {};
|
||||
if (f_mount(&fs, (const TCHAR*)_TEXT(""), 0) != FR_OK)
|
||||
return std::nullopt;
|
||||
|
||||
FIL logData = {};
|
||||
if (f_open(&logData, (const TCHAR*)_TEXT("0:\\LOG_DATA.VSA"), FA_READ) != FR_OK)
|
||||
return std::nullopt;
|
||||
|
||||
return ClusterToSector(fs, logData.obj.sclust) * uint64_t(Disk::SectorSize);
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
#include "icsneo/disk/neomemorydiskdriver.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.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) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
|
||||
if(pos % SectorSize != 0)
|
||||
return std::nullopt;
|
||||
|
||||
if(amount != SectorSize)
|
||||
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([¤tSector, &memLocation, &com, &numWords] {
|
||||
return com.sendCommand(Command::NeoReadMemory, {
|
||||
memLocation,
|
||||
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)
|
||||
});
|
||||
}, NeoMemorySDRead, timeout);
|
||||
|
||||
if(!msg)
|
||||
return 0;
|
||||
|
||||
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) {
|
||||
|
||||
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
|
||||
|
||||
if(pos % SectorSize != 0)
|
||||
return std::nullopt;
|
||||
|
||||
if(amount != SectorSize)
|
||||
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([¤tSector, &memLocation, &com, from, amount, &numWords] {
|
||||
std::vector<uint8_t> command = {
|
||||
memLocation,
|
||||
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),
|
||||
};
|
||||
command.insert(command.end(), from, from + amount);
|
||||
return com.sendCommand(Command::NeoWriteMemory, command);
|
||||
}, NeoMemoryDone, timeout);
|
||||
|
||||
if(!msg)
|
||||
return std::nullopt;
|
||||
|
||||
return SectorSize;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#include "icsneo/disk/nulldiskdriver.h"
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#include "icsneo/disk/plasiondiskreaddriver.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include "icsneo/communication/multichannelcommunication.h"
|
||||
#include <cstring>
|
||||
|
||||
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) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
|
||||
if(amount > getBlockSizeBounds().second)
|
||||
return std::nullopt;
|
||||
|
||||
if(amount % getBlockSizeBounds().first != 0)
|
||||
return std::nullopt;
|
||||
|
||||
if(pos % getBlockSizeBounds().first != 0)
|
||||
return std::nullopt;
|
||||
|
||||
uint64_t largeSector = pos / SectorSize;
|
||||
uint32_t sector = uint32_t(largeSector);
|
||||
if (largeSector != uint64_t(sector))
|
||||
return std::nullopt;
|
||||
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
uint32_t copied = 0;
|
||||
bool error = false;
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
auto cb = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> msg) {
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
|
||||
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
||||
if(!sdmsg || amount < copied + sdmsg->data.size()) {
|
||||
error = true;
|
||||
lk.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(into + copied, sdmsg->data.data(), sdmsg->data.size());
|
||||
copied += uint32_t(sdmsg->data.size());
|
||||
if(copied == amount) {
|
||||
lk.unlock();
|
||||
cv.notify_all();
|
||||
|
||||
}
|
||||
}, NeoMemorySDRead));
|
||||
|
||||
com.rawWrite({
|
||||
uint8_t(MultiChannelCommunication::CommandType::HostPC_from_SDCC1),
|
||||
uint8_t(sector & 0xFF),
|
||||
uint8_t((sector >> 8) & 0xFF),
|
||||
uint8_t((sector >> 16) & 0xFF),
|
||||
uint8_t((sector >> 24) & 0xFF),
|
||||
uint8_t(amount & 0xFF),
|
||||
uint8_t((amount >> 8) & 0xFF),
|
||||
});
|
||||
|
||||
bool hitTimeout = !cv.wait_for(lk, timeout, [&copied, &error, &amount] { return error || copied == amount; });
|
||||
com.removeMessageCallback(cb);
|
||||
|
||||
if(hitTimeout)
|
||||
return std::nullopt;
|
||||
|
||||
return amount;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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*>(×tamp);
|
||||
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);
|
||||
}
|
||||
@@ -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*>(×tamp);
|
||||
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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
+3
-24
@@ -19,7 +19,7 @@ Additionally, `event callbacks`_ can be registered, which may remove the need to
|
||||
|
||||
.. _events:
|
||||
|
||||
Events
|
||||
Events
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The API stores events in a single buffer that can has a default size of 10,000 events.
|
||||
@@ -67,7 +67,7 @@ In order to access device functionality, the device must first be opened, which
|
||||
The exception to this is setting the message polling status of the device.
|
||||
Trying to open/close the device when the device is already open/closed will result in an error being logged on the calling thread.
|
||||
|
||||
Online/Offline Status
|
||||
Online/Offline Status
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Going online begins communication between the device and the rest of the network. In order to be online, the device must also be open.
|
||||
@@ -99,25 +99,4 @@ Write Blocking Status
|
||||
|
||||
The write blocking status of the device determines the behavior of attempting to transmit to the device (likely via sending messages) with a large backlog of messages.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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``.
|
||||
If write blocking is disabled, then the attempt to transmit will simply fail and an error will be logged on the calling thread.
|
||||
+1
-1
@@ -64,7 +64,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'libicsneo'
|
||||
copyright = '2018-2024, Intrepid Control Systems, Inc.'
|
||||
copyright = '2019, Intrepid Control Systems, Inc'
|
||||
author = 'Intrepid Control Systems, Inc.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
||||
@@ -64,7 +64,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'libicsneo'
|
||||
copyright = '2018-2024, Intrepid Control Systems, Inc.'
|
||||
copyright = '2019, Intrepid Control Systems, Inc'
|
||||
author = 'Intrepid Control Systems, Inc.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
||||
@@ -1 +1 @@
|
||||
breathe==4.12.0
|
||||
breathe>=4.11.0
|
||||
@@ -1,67 +0,0 @@
|
||||
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)
|
||||
|
||||
# Disabled until we properly build these in-tree
|
||||
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
|
||||
# option(LIBICSNEO_BUILD_JAVA_INTERACTIVE_EXAMPLE "Build the command-line interactive Java example." OFF)
|
||||
|
||||
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()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE)
|
||||
add_subdirectory(cpp/interactive)
|
||||
endif()
|
||||
|
||||
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_CSHARP_INTERACTIVE_EXAMPLE)
|
||||
# add_subdirectory(csharp)
|
||||
# endif()
|
||||
|
||||
# if(LIBICSNEO_BUILD_JAVA_INTERACTIVE_EXAMPLE)
|
||||
# add_subdirectory(java)
|
||||
# endif()
|
||||
@@ -1,5 +0,0 @@
|
||||
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)
|
||||
@@ -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™
|
||||
@@ -1,800 +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"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Prints the main menu options to output
|
||||
void printMainMenu() {
|
||||
printf("Press the letter next to the function you want to use:\n");
|
||||
printf("A - List all devices\n");
|
||||
printf("B - Scan for new devices\n");
|
||||
printf("C - Open/close\n");
|
||||
printf("D - Go online/offline\n");
|
||||
printf("E - Enable/disable message polling\n");
|
||||
printf("F - Get messages\n");
|
||||
printf("G - Send message\n");
|
||||
printf("H - Get events\n");
|
||||
printf("I - Set HS CAN to 250K\n");
|
||||
printf("J - Set HS CAN to 500K\n");
|
||||
printf("L - Set Digital IO\n");
|
||||
printf("M - Set HS CAN termination\n");
|
||||
printf("X - Exit\n");
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
// 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;
|
||||
//}
|
||||
|
||||
neoversion_t ver = icsneo_getVersion();
|
||||
printf("ICS icsneoc.dll version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
|
||||
|
||||
while(true) {
|
||||
printMainMenu();
|
||||
printf("\n");
|
||||
char input = getCharInput(26, 'A', 'a', 'B', 'b', 'C', 'c', 'D', 'd', 'E', 'e', 'F', 'f', 'G', 'g', 'H', 'h', 'I', 'i', 'J', 'j', 'L', 'l', 'M', 'm', 'X', 'x');
|
||||
printf("\n");
|
||||
switch(input) {
|
||||
// List current devices
|
||||
case 'A':
|
||||
case 'a':
|
||||
printAllDevices();
|
||||
printf("\n");
|
||||
break;
|
||||
// Scan for new devices
|
||||
case 'B':
|
||||
case 'b':
|
||||
{
|
||||
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");
|
||||
break;
|
||||
}
|
||||
// Open/close a device
|
||||
case 'C':
|
||||
case 'c':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
// 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);
|
||||
|
||||
printf("Would you like to open or close %s?\n", productDescription);
|
||||
printf("[1] Open\n[2] Close\n[3] Cancel\n\n");
|
||||
|
||||
char input = getCharInput(3, '1', '2', '3');
|
||||
printf("\n");
|
||||
|
||||
switch(input) {
|
||||
case '1':
|
||||
// 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");
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
// 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");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
printf("Canceling!\n\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Go online/offline
|
||||
case 'D':
|
||||
case 'd':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
// 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);
|
||||
|
||||
printf("Would you like to have %s go online or offline?\n", productDescription);
|
||||
printf("[1] Online\n[2] Offline\n[3] Cancel\n\n");
|
||||
|
||||
char input = getCharInput(3, '1', '2', '3');
|
||||
printf("\n");
|
||||
|
||||
switch(input) {
|
||||
case '1':
|
||||
// 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");
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
// 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");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Canceling!\n\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Enable/disable message polling
|
||||
case 'E':
|
||||
case 'e':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
// 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);
|
||||
|
||||
printf("Would you like to enable or disable message polling for %s?\n", productDescription);
|
||||
printf("[1] Enable\n[2] Disable\n[3] Cancel\n\n");
|
||||
|
||||
char input = getCharInput(3, '1', '2', '3');
|
||||
printf("\n");
|
||||
|
||||
switch(input) {
|
||||
case '1':
|
||||
// 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");
|
||||
}
|
||||
|
||||
// Manually setting the polling message limit as done below is optional
|
||||
// It will default to 20k if not set
|
||||
// Attempt to set the polling message limit
|
||||
if(icsneo_setPollingMessageLimit(selectedDevice, msgLimit)) {
|
||||
printf("Successfully set message polling limit for %s!\n\n", productDescription);
|
||||
} else {
|
||||
printf("Failed to set polling message limit for %s!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
// 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");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Canceling!\n\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Get messages
|
||||
case 'F':
|
||||
case 'f':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
// 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);
|
||||
|
||||
// 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");
|
||||
break;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
break;
|
||||
// Send message
|
||||
case 'G':
|
||||
case 'g':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
// 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);
|
||||
|
||||
// Start generating sample msg
|
||||
uint8_t sendMessageData[6];
|
||||
sendMessageData[0] = 0xaa;
|
||||
sendMessageData[1] = 0xbb;
|
||||
sendMessageData[2] = 0xcc;
|
||||
sendMessageData[3] = 0xdd;
|
||||
sendMessageData[4] = 0xee;
|
||||
sendMessageData[5] = 0xff;
|
||||
|
||||
neomessage_can_t msg = {0};
|
||||
msg.arbid = 0x120;
|
||||
msg.length = 6;
|
||||
msg.netid = ICSNEO_NETID_HSCAN;
|
||||
msg.data = sendMessageData;
|
||||
msg.description = 0x1c5; // Random description to match the receipt
|
||||
msg.status.canfdFDF = false;
|
||||
msg.status.extendedFrame = false;
|
||||
msg.status.canfdBRS = false;
|
||||
// End generating sample msg
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Get events
|
||||
case 'H':
|
||||
case 'h':
|
||||
{
|
||||
// All API events
|
||||
printAPIEvents();
|
||||
printf("\n");
|
||||
}
|
||||
break;
|
||||
// Set HS CAN to 250k
|
||||
case 'I':
|
||||
case 'i':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
// 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 set baudrate and apply settings
|
||||
if(icsneo_setBaudrate(selectedDevice, ICSNEO_NETID_HSCAN, 250000) && icsneo_settingsApply(selectedDevice)) {
|
||||
printf("Successfully set HS CAN baudrate for %s to 250k!\n\n", productDescription);
|
||||
} else {
|
||||
printf("Failed to set HS CAN for %s to 250k!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Set HS CAN to 500k
|
||||
case 'J':
|
||||
case 'j':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
// 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 set baudrate and apply settings
|
||||
if(icsneo_setBaudrate(selectedDevice, ICSNEO_NETID_HSCAN, 500000) && icsneo_settingsApply(selectedDevice)) {
|
||||
printf("Successfully set HS CAN baudrate for %s to 500k!\n\n", productDescription);
|
||||
} else {
|
||||
printf("Failed to set HS CAN for %s to 500k!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Set Digital IO
|
||||
case 'L':
|
||||
case 'l':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
printf("Select from the following:\n");
|
||||
|
||||
printf("[1] Ethernet (DoIP) Activation Line");
|
||||
bool val;
|
||||
bool haveVal = icsneo_getDigitalIO(selectedDevice, ICSNEO_IO_ETH_ACTIVATION, 1, &val);
|
||||
if(!haveVal) {
|
||||
neoevent_t event;
|
||||
bool got = icsneo_getLastError(&event);
|
||||
printf(": Unknown (%s)\n", got ? event.description : "No error");
|
||||
} else {
|
||||
if(val)
|
||||
printf(": Enabled\n");
|
||||
else
|
||||
printf(": Disabled\n");
|
||||
}
|
||||
|
||||
printf("[2] USB Host Power");
|
||||
haveVal = icsneo_getDigitalIO(selectedDevice, ICSNEO_IO_USB_HOST_POWER, 1, &val);
|
||||
if(!haveVal) {
|
||||
neoevent_t event;
|
||||
bool got = icsneo_getLastError(&event);
|
||||
printf(": Unknown (%s)\n", got ? event.description : "No error");
|
||||
} else {
|
||||
if(val)
|
||||
printf(": Enabled\n");
|
||||
else
|
||||
printf(": Disabled\n");
|
||||
}
|
||||
|
||||
printf("[3] Backup Power");
|
||||
haveVal = icsneo_getDigitalIO(selectedDevice, ICSNEO_IO_BACKUP_POWER_EN, 1, &val);
|
||||
if(!haveVal) {
|
||||
neoevent_t event;
|
||||
bool got = icsneo_getLastError(&event);
|
||||
printf(": Unknown (%s)\n", got ? event.description : "No error");
|
||||
} else {
|
||||
if(val)
|
||||
printf(": Enabled ");
|
||||
else
|
||||
printf(": Disabled ");
|
||||
|
||||
haveVal = icsneo_getDigitalIO(selectedDevice, ICSNEO_IO_BACKUP_POWER_GOOD, 1, &val);
|
||||
if(!haveVal) {
|
||||
neoevent_t event;
|
||||
bool got = icsneo_getLastError(&event);
|
||||
printf("with unknown status (%s)\n", got ? event.description : "No error");
|
||||
} else {
|
||||
if(val)
|
||||
printf("and Charged\n");
|
||||
else
|
||||
printf("and Not Charged\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("[4] Cancel\n\n");
|
||||
char selection = getCharInput(4, '1', '2', '3', '4');
|
||||
printf("\n");
|
||||
|
||||
if(selection == '4') {
|
||||
printf("Canceling!\n\n");
|
||||
break;
|
||||
}
|
||||
|
||||
printf("[0] Disable\n[1] Enable\n[2] Cancel\n\n");
|
||||
char selection2 = getCharInput(3, '0', '1', '2');
|
||||
printf("\n");
|
||||
|
||||
if(selection2 == '2') {
|
||||
printf("Canceling!\n\n");
|
||||
break;
|
||||
}
|
||||
|
||||
const bool set = selection2 == '1';
|
||||
neoio_t type;
|
||||
switch (selection)
|
||||
{
|
||||
case '1': type = ICSNEO_IO_ETH_ACTIVATION; break;
|
||||
case '2': type = ICSNEO_IO_USB_HOST_POWER; break;
|
||||
case '3': type = ICSNEO_IO_BACKUP_POWER_EN; break;
|
||||
};
|
||||
if(icsneo_setDigitalIO(selectedDevice, type, 1, set)) {
|
||||
printf("OK!\n\n");
|
||||
} else {
|
||||
neoevent_t event;
|
||||
bool got = icsneo_getLastError(&event);
|
||||
printf("Failure! (%s)\n\n", got ? event.description : "No error");
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Set HS CAN termination
|
||||
case 'M':
|
||||
case 'm':
|
||||
{
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
break;
|
||||
}
|
||||
selectedDevice = selectDevice();
|
||||
|
||||
// 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);
|
||||
|
||||
printf("Termination is ");
|
||||
const bool val = icsneo_isTerminationEnabledFor(selectedDevice, ICSNEO_NETID_HSCAN);
|
||||
neoevent_t err = { 0 };
|
||||
if(icsneo_getLastError(&err)) {
|
||||
printf("not available at this time: %s\n\n", err.description);
|
||||
break;
|
||||
}
|
||||
printf(val ? "currently enabled\n" : "currently disabled\n");
|
||||
|
||||
printf("[0] Disable\n[1] Enable\n[2] Cancel\n\n");
|
||||
char selection2 = getCharInput(3, '0', '1', '2');
|
||||
printf("\n");
|
||||
|
||||
if(selection2 == '2') {
|
||||
printf("Canceling!\n\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Attempt to set baudrate and apply settings
|
||||
if(icsneo_setTerminationFor(selectedDevice, ICSNEO_NETID_HSCAN, selection2 == '1') && icsneo_settingsApply(selectedDevice)) {
|
||||
printf("Successfully set HS CAN termination for %s!\n\n", productDescription);
|
||||
} else {
|
||||
printf("Failed to set HS CAN termination for %s!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Exit
|
||||
case 'X':
|
||||
case 'x':
|
||||
printf("Exiting program\n");
|
||||
return 0;
|
||||
default:
|
||||
printf("Unexpected input, exiting!\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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™
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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™
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user