mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-20 16:08:37 +02:00
Compare commits
54
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa77bbbc9e | ||
|
|
0b9491e5e3 | ||
|
|
ca630b4569 | ||
|
|
b2f67f7ce1 | ||
|
|
90e1aa223d | ||
|
|
e0cef880f0 | ||
|
|
cc7cffe068 | ||
|
|
e97b307c4d | ||
|
|
0c436621a0 | ||
|
|
508013baf4 | ||
|
|
8d704b1bbb | ||
|
|
018f1fac8e | ||
|
|
188a087c51 | ||
|
|
53e66b8772 | ||
|
|
41d6927496 | ||
|
|
237bed1b72 | ||
|
|
6c93fc839b | ||
|
|
4cf35acf6c | ||
|
|
9ef93eb73e | ||
|
|
5b3e21fa7c | ||
|
|
a36d114e1f | ||
|
|
ee9cf26eb2 | ||
|
|
d7d98855ea | ||
|
|
245f5089da | ||
|
|
d9c12bffe7 | ||
|
|
f907d6759f | ||
|
|
1e773ba9ab | ||
|
|
f5096b879c | ||
|
|
0de23f2817 | ||
|
|
32900ae263 | ||
|
|
b6d9ef4c7e | ||
|
|
2f7c3a2dda | ||
|
|
bbf348a6ab | ||
|
|
73744bf6d9 | ||
|
|
6b0c588a46 | ||
|
|
b3bbf91e8c | ||
|
|
af31aa23ad | ||
|
|
83f6c611fe | ||
|
|
4fa813ccbc | ||
|
|
3ddb832708 | ||
|
|
63c81b1c3d | ||
|
|
0fa7494eab | ||
|
|
cd7b0b0a7b | ||
|
|
d9cdd03618 | ||
|
|
832cf9c84b | ||
|
|
bf6a059820 | ||
|
|
9b46d486cb | ||
|
|
ddee1254a0 | ||
|
|
539cfa511b | ||
|
|
4229d8b66a | ||
|
|
dc1c4ede9d | ||
|
|
9e7d442e83 | ||
|
|
41a569fc2d | ||
|
|
f4e4a103ad |
@@ -10,3 +10,7 @@ third-party/concurrentqueue/tests
|
||||
*.bak
|
||||
.vs
|
||||
.cache
|
||||
*.wav
|
||||
*.orig
|
||||
examples/csharp/bin
|
||||
examples/csharp/obj
|
||||
+201
-2
@@ -1,7 +1,14 @@
|
||||
variables:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Windows
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
build windows/x64:
|
||||
stage: build
|
||||
script:
|
||||
@@ -24,7 +31,7 @@ test windows/x64:
|
||||
- build windows/x64
|
||||
tags:
|
||||
- icsneo-windows
|
||||
timeout: 3m
|
||||
timeout: 5m
|
||||
|
||||
build windows/x86:
|
||||
stage: build
|
||||
@@ -48,4 +55,196 @@ test windows/x86:
|
||||
- build windows/x86
|
||||
tags:
|
||||
- icsneo-windows
|
||||
timeout: 3m
|
||||
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
|
||||
- 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: test
|
||||
script:
|
||||
- apt update -y
|
||||
- apt upgrade -y
|
||||
- apt install -y libusb-1.0-0-dev libpcap-dev
|
||||
- build/libicsneo-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
|
||||
- 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: test
|
||||
script:
|
||||
- apt update -y
|
||||
- apt upgrade -y
|
||||
- apt install -y libusb-1.0-0-dev libpcap-dev
|
||||
- build/libicsneo-tests
|
||||
tags:
|
||||
- linux-build
|
||||
timeout: 5m
|
||||
|
||||
build linux/ubuntu/2004/amd64/gcc:
|
||||
<<: *build_linux_ubuntu_gcc
|
||||
image: ubuntu:20.04
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
- dnf upgrade -y
|
||||
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel
|
||||
- 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: test
|
||||
cache:
|
||||
paths:
|
||||
- /var/cache/dnf
|
||||
script:
|
||||
- dnf upgrade -y
|
||||
- dnf install -y libpcap-devel libusb1-devel
|
||||
- build/libicsneo-tests
|
||||
tags:
|
||||
- linux-build
|
||||
timeout: 5m
|
||||
|
||||
.build_linux_fedora_clang: &build_linux_fedora_clang
|
||||
stage: build
|
||||
cache:
|
||||
paths:
|
||||
- /var/cache/dnf
|
||||
script:
|
||||
- dnf upgrade -y
|
||||
- dnf install -y clang lld libpcap-devel cmake ninja-build libusb1-devel
|
||||
- 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: test
|
||||
cache:
|
||||
paths:
|
||||
- /var/cache/dnf
|
||||
script:
|
||||
- dnf upgrade -y
|
||||
- dnf install -y libpcap-devel libusb1-devel
|
||||
- build/libicsneo-tests
|
||||
tags:
|
||||
- linux-build
|
||||
timeout: 5m
|
||||
|
||||
build linux/fedora/37/amd64/gcc:
|
||||
<<: *build_linux_fedora_gcc
|
||||
image: fedora:37
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
+101
-31
@@ -1,6 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(libicsneo VERSION 0.3.0)
|
||||
|
||||
cmake_policy(SET CMP0074 NEW)
|
||||
|
||||
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)
|
||||
@@ -9,8 +11,6 @@ option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
|
||||
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
|
||||
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
|
||||
|
||||
option(LIBICSNEO_USE_DEVICE_SHARING "Interact with devices through the sharing server" ON)
|
||||
|
||||
# Device Drivers
|
||||
# You almost certainly don't want firmio for your build,
|
||||
# it is only relevant for communication between Linux and
|
||||
@@ -18,15 +18,18 @@ option(LIBICSNEO_USE_DEVICE_SHARING "Interact with devices through the sharing s
|
||||
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_ANDROIDUSB "Enable devices which communicate over USB CDC ACM on Android" ON)
|
||||
option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX" ON)
|
||||
option(LIBICSNEO_ENABLE_TCP "Enable devices which communicate over TCP" OFF)
|
||||
option(LIBICSNEO_ENABLE_FTD3XX "Enable devices which communicate over USB FTD3XX" ON)
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# Enable Warnings
|
||||
if(MSVC)
|
||||
@@ -116,10 +119,11 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
elseif(LIBICSNEO_ENABLE_ANDROIDUSB)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/windows/sharedmemory.cpp
|
||||
platform/windows/sharedsemaphore.cpp
|
||||
platform/android/androidusb.cpp
|
||||
)
|
||||
|
||||
else() # Darwin or Linux
|
||||
set(PLATFORM_SRC)
|
||||
|
||||
@@ -162,10 +166,60 @@ else() # Darwin or Linux
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if(LIBICSNEO_ENABLE_FTD3XX)
|
||||
if(NOT FTD3XX_ROOT) # allow system override
|
||||
include(FetchContent)
|
||||
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-x64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ee1289cdd5023de67275aaa1712e8e32e73e825a8392efb0a76f161e357fbdc9")
|
||||
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-i686.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=bcd31f5c3cb39ecb9b09db5ce722bd79de6e0f813130e7b8f2c86879a60d8ff0")
|
||||
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-macos-universal2.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=74c0d35f04242d0841532c6325eb2932b8f627e3c395382a15d9f39974d27a90")
|
||||
elseif(UNIX)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-x64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=a9dc7eb6948c8977fbd79f6700bec6f882d3da5667aea8f2175b8d1f6f08e456")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-aarch64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=da4b90ea1cbb905874cd159ad2ab8c1bdde65cc22b3aa55bf2b5fd85ca6efd22")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-armhf.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=d813008117422cae958f7c71a065cdac0d31dca3b24809d3ab5e13604a9c3fb1")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(NOT LIBICSNEO_FTD3XX_URL)
|
||||
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
ftdi3xx
|
||||
URL ${LIBICSNEO_FTD3XX_URL}
|
||||
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
|
||||
)
|
||||
FetchContent_GetProperties(ftdi3xx)
|
||||
if(NOT ftdi3xx_POPULATED)
|
||||
FetchContent_Populate(ftdi3xx)
|
||||
endif()
|
||||
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
|
||||
endif()
|
||||
find_package(FTD3XX REQUIRED)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/sharedmemory.cpp
|
||||
platform/posix/sharedsemaphore.cpp
|
||||
platform/ftd3xx.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_ENABLE_TCP)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/tcp.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -184,8 +238,11 @@ endforeach()
|
||||
set(SRC_FILES
|
||||
communication/message/flexray/control/flexraycontrolmessage.cpp
|
||||
communication/message/callback/streamoutput/a2bwavoutput.cpp
|
||||
communication/message/callback/streamoutput/a2bdecoder.cpp
|
||||
communication/message/neomessage.cpp
|
||||
communication/message/ethphymessage.cpp
|
||||
communication/message/linmessage.cpp
|
||||
communication/message/livedatamessage.cpp
|
||||
communication/packet/flexraypacket.cpp
|
||||
communication/packet/canpacket.cpp
|
||||
communication/packet/a2bpacket.cpp
|
||||
@@ -193,10 +250,16 @@ set(SRC_FILES
|
||||
communication/packet/versionpacket.cpp
|
||||
communication/packet/iso9141packet.cpp
|
||||
communication/packet/ethphyregpacket.cpp
|
||||
communication/packet/livedatapacket.cpp
|
||||
communication/packet/logicaldiskinfopacket.cpp
|
||||
communication/packet/wivicommandpacket.cpp
|
||||
communication/packet/i2cpacket.cpp
|
||||
communication/packet/linpacket.cpp
|
||||
communication/packet/mdiopacket.cpp
|
||||
communication/packet/scriptstatuspacket.cpp
|
||||
communication/packet/componentversionpacket.cpp
|
||||
communication/packet/supportedfeaturespacket.cpp
|
||||
communication/packet/genericbinarystatuspacket.cpp
|
||||
communication/decoder.cpp
|
||||
communication/encoder.cpp
|
||||
communication/ethernetpacketizer.cpp
|
||||
@@ -204,9 +267,7 @@ set(SRC_FILES
|
||||
communication/multichannelcommunication.cpp
|
||||
communication/communication.cpp
|
||||
communication/driver.cpp
|
||||
communication/interprocessmailbox.cpp
|
||||
communication/sdio.cpp
|
||||
communication/socket.cpp
|
||||
communication/livedata.cpp
|
||||
device/extensions/flexray/extension.cpp
|
||||
device/extensions/flexray/controller.cpp
|
||||
device/idevicesettings.cpp
|
||||
@@ -280,7 +341,8 @@ target_include_directories(icsneocpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
|
||||
)
|
||||
|
||||
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)
|
||||
@@ -295,17 +357,28 @@ endif()
|
||||
if(LIBICSNEO_ENABLE_FTDI)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
|
||||
endif()
|
||||
if(LIBICSNEO_USE_DEVICE_SHARING)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_DEVICE_SHARING)
|
||||
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)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# socket
|
||||
if(WIN32)
|
||||
target_link_libraries(icsneocpp PRIVATE ws2_32)
|
||||
if(LIBICSNEO_ENABLE_ANDROIDUSB)
|
||||
add_library(libusb SHARED IMPORTED)
|
||||
set_target_properties(libusb PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/third-party/libusb/android/libs/${ANDROID_ABI}/libusb1.0.so)
|
||||
target_include_directories(icsneocpp PRIVATE third-party/libusb/libusb)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_ANDROIDUSB)
|
||||
target_link_libraries(icsneocpp PRIVATE android log libusb)
|
||||
endif()
|
||||
|
||||
# fatfs
|
||||
add_subdirectory(third-party/fatfs)
|
||||
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(icsneocpp PRIVATE fatfs)
|
||||
|
||||
# libftdi
|
||||
@@ -346,15 +419,6 @@ if(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
endif(WIN32)
|
||||
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
|
||||
endif()
|
||||
|
||||
# For SharedMemory and SharedSemaphore
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(icsneocpp PUBLIC rt)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOC)
|
||||
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
||||
target_include_directories(icsneoc
|
||||
@@ -366,6 +430,11 @@ if(LIBICSNEO_BUILD_ICSNEOC)
|
||||
)
|
||||
target_link_libraries(icsneoc PRIVATE icsneocpp)
|
||||
target_compile_features(icsneoc PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||
if(LIBICSNEO_ENABLE_ANDROIDUSB)
|
||||
target_link_libraries(icsneoc PRIVATE android log)
|
||||
target_include_directories(icsneoc PRIVATE third-party/libusb/libusb)
|
||||
target_compile_definitions(icsneoc PRIVATE ICSNEO_ENABLE_ANDROIDUSB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOC_STATIC)
|
||||
@@ -419,10 +488,11 @@ if(LIBICSNEO_BUILD_TESTS)
|
||||
test/diskdriverwritetest.cpp
|
||||
test/eventmanagertest.cpp
|
||||
test/ethernetpacketizertest.cpp
|
||||
test/interprocessmailboxtest.cpp
|
||||
test/sockettest.cpp
|
||||
test/i2cencoderdecodertest.cpp
|
||||
test/linencoderdecodertest.cpp
|
||||
test/a2bencoderdecodertest.cpp
|
||||
test/mdioencoderdecodertest.cpp
|
||||
test/livedataencoderdecodertest.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(libicsneo-tests gtest gtest_main)
|
||||
|
||||
+6
-1
@@ -17,6 +17,10 @@
|
||||
- RADA2B
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- RADComet
|
||||
- CAN works
|
||||
- CAN FD works
|
||||
- Ethernet works
|
||||
|
||||
- Connecting over USB
|
||||
- ValueCAN 4 series
|
||||
@@ -42,4 +46,5 @@
|
||||
- CAN works
|
||||
- RADA2B
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- Ethernet works
|
||||
- RADMoon3
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2018-2020 Intrepid Control Systems, Inc.
|
||||
Copyright 2018-2023 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:
|
||||
|
||||
|
||||
@@ -110,21 +110,67 @@ for(size_t i = 0; i < messageCount; i++) {
|
||||
icsneo_closeDevice(myDevice);
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
To enable debug printing set the `LIBICSNEO_PRINT_EVENTS` environmental variable to the desired `APIEvent::Severity` level, all `Event`s greater than or equal to that level will be printed to stderr. For example, to print all warnings and errors: `LIBICSNEO_PRINT_EVENTS=32`.
|
||||
|
||||
## Building from Source
|
||||
### FTD3XX
|
||||
Some devices require FTD3XX for USB communication so the [FTDI D3XX library](https://ftdichip.com/drivers/d3xx-drivers/) will be automatically downloaded and included. If you would like to use a system copy of D3XX instead you can set `FTD3XX_ROOT` to the path containing `f3d3xx.h` (`-DFTD3XX_ROOT=<path to directory containing ftd3xx.h>`).
|
||||
### Windows
|
||||
|
||||
- Open a terminal and install the following:
|
||||
```
|
||||
winget install Microsoft.VisualStudio.2022.Community
|
||||
winget install Kitware.CMake
|
||||
winget install Git.Git
|
||||
winget install Ninja-build.Ninja
|
||||
```
|
||||
- Reboot so cmake is in the system path
|
||||
- Open a developer Powershell for VS2022 and run the following:
|
||||
```
|
||||
git clone https://github.com/intrepidcs/libicsneo
|
||||
cd libicsneo
|
||||
cmake -S . -B build -G "Ninja"
|
||||
cmake --build build
|
||||
# All dlls and libs are now inside the build directory
|
||||
```
|
||||
|
||||
|
||||
Building will require MSVC 2017 version 15.7 or newer and CMake to be installed.
|
||||
### macOS
|
||||
Getting the dependencies is easiest with the Homebrew package manager. You will also need XCode installed. You can then install CMake, an up-to-date version of GCC or Clang, and `libusb-1.0`.
|
||||
### Linux
|
||||
The dependencies are as follows
|
||||
#### General Dependencies
|
||||
- CMake 3.12 or above
|
||||
- GCC 7 or above
|
||||
- `libusb-1.0-0-dev`
|
||||
- `libpcap0.8-dev`
|
||||
- `build-essential` is recommended
|
||||
|
||||
If you'd like to be able to run programs that use this library without being root, consider using the included udev rules
|
||||
#### 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:
|
||||
|
||||
```
|
||||
$ sudo cp 99-intrepidcs.rules /etc/udev/rules.d/
|
||||
cp 99-intrepidcs.rules /etc/udev/rules.d/
|
||||
udevadm control --reload-rules && udevadm trigger
|
||||
```
|
||||
|
||||
+68
-4
@@ -111,11 +111,15 @@ bool icsneo_isValidNeoDevice(const neodevice_t* device) {
|
||||
}
|
||||
|
||||
bool icsneo_openDevice(const neodevice_t* device) {
|
||||
if(!icsneo_isValidNeoDevice(device))
|
||||
if(!icsneo_isValidNeoDevice(device)) {
|
||||
//todo error
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!device->device->open())
|
||||
return false;
|
||||
if(!device->device->open()) {
|
||||
//todo error
|
||||
return false;
|
||||
}
|
||||
|
||||
// We connected successfully, move the device to connected devices
|
||||
std::vector<std::vector<std::shared_ptr<Device>>::iterator> itemsToMove;
|
||||
@@ -220,7 +224,6 @@ bool icsneo_getMessages(const neodevice_t* device, neomessage_t* messages, size_
|
||||
return false;
|
||||
|
||||
*items = storage.size();
|
||||
|
||||
for(size_t i = 0; i < *items; i++) {
|
||||
// For each message, copy into neomessage_t buffer given
|
||||
messages[i] = CreateNeoMessage(storage[i]);
|
||||
@@ -689,3 +692,64 @@ bool icsneo_setTerminationFor(const neodevice_t* device, neonetid_t netid, bool
|
||||
|
||||
return device->device->settings->setTerminationFor(Network(netid), enabled);
|
||||
}
|
||||
|
||||
bool icsneo_getRTC(const neodevice_t* device, uint64_t* output)
|
||||
{
|
||||
if(!icsneo_isValidNeoDevice(device))
|
||||
return false;
|
||||
|
||||
const std::optional<std::chrono::time_point<std::chrono::system_clock>> rtc = device->device->getRTC();
|
||||
if(!rtc)
|
||||
return false;
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::seconds>(rtc->time_since_epoch());
|
||||
*output = static_cast<uint64_t>(duration.count());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icsneo_setRTC(const neodevice_t* device, uint64_t input)
|
||||
{
|
||||
if(!icsneo_isValidNeoDevice(device))
|
||||
return false;
|
||||
|
||||
std::chrono::seconds duration(input);
|
||||
const std::chrono::system_clock::time_point time(duration);
|
||||
return device->device->setRTC(time);
|
||||
}
|
||||
|
||||
int icsneo_getDeviceStatus(const neodevice_t* device, void* status, size_t* size) {
|
||||
if(!icsneo_isValidNeoDevice(device))
|
||||
return false;
|
||||
|
||||
if(status == nullptr || size == nullptr)
|
||||
return false;
|
||||
|
||||
std::shared_ptr<Message> msg = device->device->com->waitForMessageSync([&]() {
|
||||
return device->device->com->sendCommand(Command::RequestStatusUpdate);
|
||||
}, std::make_shared<MessageFilter>(Network::NetID::DeviceStatus), std::chrono::milliseconds(100));
|
||||
|
||||
if(!msg) // Did not receive a message
|
||||
return false;
|
||||
|
||||
auto rawMessage = std::static_pointer_cast<RawMessage>(msg);
|
||||
if(!rawMessage || (rawMessage->network.getNetID() != Network::NetID::DeviceStatus))
|
||||
return false;
|
||||
|
||||
if(*size < rawMessage->data.size())
|
||||
return false;
|
||||
|
||||
std::copy(rawMessage->data.begin(), rawMessage->data.end(), static_cast<uint8_t*>(status));
|
||||
*size = rawMessage->data.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
bool icsneo_addSysFileDescriptor(int sysFd) {
|
||||
return icsneo::ANDROIDUSB::addSystemFD(sysFd);
|
||||
|
||||
}
|
||||
bool icsneo_removeSysFileDescriptor(int sysFd) {
|
||||
return icsneo::ANDROIDUSB::removeSystemFD(sysFd);
|
||||
}
|
||||
#endif
|
||||
@@ -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-2019"
|
||||
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2023"
|
||||
VALUE "OriginalFilename", "icsneoc.dll"
|
||||
VALUE "ProductName", "libicsneo"
|
||||
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
||||
|
||||
+126
-99
@@ -4,7 +4,7 @@
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
APIEvent::APIEvent(Type type, Severity severity, const Device* device) : eventStruct({}) {
|
||||
APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device) : eventStruct({}) {
|
||||
this->device = device;
|
||||
if(device) {
|
||||
serial = device->getSerial();
|
||||
@@ -14,17 +14,6 @@ APIEvent::APIEvent(Type type, Severity severity, const Device* device) : eventSt
|
||||
init(type, severity);
|
||||
}
|
||||
|
||||
APIEvent::APIEvent(neosocketevent_t evStruct, const Device* device)
|
||||
{
|
||||
this->device = device;
|
||||
timepoint = EventClock::from_time_t(evStruct.timestamp);
|
||||
serial = std::string(evStruct.serial);
|
||||
eventStruct.eventNumber = evStruct.eventNumber;
|
||||
eventStruct.severity = evStruct.severity;
|
||||
eventStruct.description = DescriptionForType(APIEvent::getType());
|
||||
eventStruct.timestamp = evStruct.timestamp;
|
||||
}
|
||||
|
||||
void APIEvent::init(Type event, APIEvent::Severity severity) {
|
||||
timepoint = EventClock::now();
|
||||
eventStruct.description = DescriptionForType(event);
|
||||
@@ -56,15 +45,6 @@ std::string APIEvent::describe() const noexcept {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
neosocketevent_t APIEvent::getNeoSocketEvent() const noexcept {
|
||||
neosocketevent_t neoSocketEvent;
|
||||
neoSocketEvent.eventNumber = eventStruct.eventNumber;
|
||||
neoSocketEvent.severity = eventStruct.severity;
|
||||
std::memcpy(neoSocketEvent.serial, eventStruct.serial, sizeof(eventStruct.serial));
|
||||
neoSocketEvent.timestamp = eventStruct.timestamp;
|
||||
return neoSocketEvent;
|
||||
}
|
||||
|
||||
void APIEvent::downgradeFromError() noexcept {
|
||||
eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning;
|
||||
}
|
||||
@@ -133,6 +113,9 @@ static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An idea
|
||||
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
|
||||
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
|
||||
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
|
||||
static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same.";
|
||||
static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected.";
|
||||
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
|
||||
|
||||
// Transport Errors
|
||||
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
||||
@@ -145,32 +128,49 @@ static constexpr const char* DEVICE_IN_USE = "The device is currently in use by
|
||||
static constexpr const char* PCAP_COULD_NOT_START = "The PCAP driver could not be started. Ethernet devices will not be found.";
|
||||
static constexpr const char* PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
|
||||
static constexpr const char* PACKET_DECODING = "There was an error decoding a packet from the device.";
|
||||
static constexpr const char* SOCKET_FAILED_TO_OPEN = "Unable to open new socket.";
|
||||
static constexpr const char* FAILED_TO_BIND = "Unable to bind socket.";
|
||||
static constexpr const char* ERROR_SETTING_SOCKET_OPTION = "A call to setsockopt() failed.";
|
||||
static constexpr const char* GETIFADDRS_ERROR = "A call to getifaddrs() failed.";
|
||||
static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed.";
|
||||
|
||||
static constexpr const char* SHARED_MEMORY_DATA_IS_NULL = "data() was called on invalid object.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_CLOSE = "The server failed to close a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_OPEN = "The server failed to open a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_UNLINK = "The server failed to unlink a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FILE_TRUNCATE_ERROR = "The server failed to truncate shared memory file.";
|
||||
static constexpr const char* SHARED_MEMORY_MAPPING_ERROR = "The server failed to map a shared memory file.";
|
||||
static constexpr const char* SHARED_MEMORY_UNMAP_ERROR = "The server failed to unmap a shared memory file.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_CLOSE = "The server failed to close a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_OPEN = "The server failed to open a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_POST = "A post() call failed on a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_UNLINK = "The server failed to unlink a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_WAIT = "A wait() call failed on a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_POST = "post() was called on a shared semaphore that is not open.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT = "wait() was called on a shared semaphore that is not open.";
|
||||
static constexpr const char* SOCKET_FAILED_CONNECT = "A socket connection was attempted but failed.";
|
||||
static constexpr const char* SOCKET_FAILED_OPEN = "A socket failed to open.";
|
||||
static constexpr const char* SOCKET_FAILED_CLOSE = "A socket failed to close.";
|
||||
static constexpr const char* SOCKET_FAILED_READ = "A socket read operation failed.";
|
||||
static constexpr const char* SOCKET_FAILED_WRITE = "A socket write operation failed.";
|
||||
static constexpr const char* ACCEPTOR_FAILED_BIND = "A socket acceptor failed to bind.";
|
||||
static constexpr const char* ACCEPTOR_FAILED_LISTEN = "A socket acceptor failed to listen.";
|
||||
// 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.";
|
||||
|
||||
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
|
||||
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
|
||||
static constexpr const char* NO_ERROR_FOUND = "No errors found.";
|
||||
static constexpr const char* INVALID = "An invalid internal error occurred.";
|
||||
const char* APIEvent::DescriptionForType(Type type) {
|
||||
switch(type) {
|
||||
@@ -287,7 +287,12 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return WIVI_UPLOAD_STACK_OVERFLOW;
|
||||
case Type::A2BMessageIncompleteFrame:
|
||||
return A2B_MESSAGE_INCOMPLETE_FRAME;
|
||||
|
||||
case Type::CoreminiUploadVersionMismatch:
|
||||
return COREMINI_UPLOAD_VERSION_MISMATCH;
|
||||
case Type::DiskNotConnected:
|
||||
return DISK_NOT_CONNECTED;
|
||||
case Type::UnexpectedResponse:
|
||||
return UNEXPECTED_RESPONSE;
|
||||
// Transport Errors
|
||||
case Type::FailedToRead:
|
||||
return FAILED_TO_READ;
|
||||
@@ -309,58 +314,90 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return PCAP_COULD_NOT_FIND_DEVICES;
|
||||
case Type::PacketDecodingError:
|
||||
return PACKET_DECODING;
|
||||
|
||||
// Device Sharing Server Events
|
||||
case Type::SharedMemoryDataIsNull:
|
||||
return SHARED_MEMORY_DATA_IS_NULL;
|
||||
case Type::SharedMemoryFailedToClose:
|
||||
return SHARED_MEMORY_FAILED_TO_CLOSE;
|
||||
case Type::SharedMemoryFailedToOpen:
|
||||
return SHARED_MEMORY_FAILED_TO_OPEN;
|
||||
case Type::SharedMemoryFailedToUnlink:
|
||||
return SHARED_MEMORY_FAILED_TO_UNLINK;
|
||||
case Type::SharedMemoryFileTruncateError:
|
||||
return SHARED_MEMORY_FILE_TRUNCATE_ERROR;
|
||||
case Type::SharedMemoryMappingError:
|
||||
return SHARED_MEMORY_MAPPING_ERROR;
|
||||
case Type::SharedMemoryUnmapError:
|
||||
return SHARED_MEMORY_UNMAP_ERROR;
|
||||
case Type::SharedSemaphoreFailedToClose:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_CLOSE;
|
||||
case Type::SharedSemaphoreFailedToOpen:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_OPEN;
|
||||
case Type::SharedSemaphoreFailedToPost:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_POST;
|
||||
case Type::SharedSemaphoreFailedToUnlink:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_UNLINK;
|
||||
case Type::SharedSemaphoreFailedToWait:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_WAIT;
|
||||
case Type::SharedSemaphoreNotOpenForPost:
|
||||
return SHARED_SEMAPHORE_NOT_OPEN_FOR_POST;
|
||||
case Type::SharedSemaphoreNotOpenForWait:
|
||||
return SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT;
|
||||
case Type::SocketFailedToOpen:
|
||||
return SOCKET_FAILED_OPEN;
|
||||
case Type::SocketFailedToClose:
|
||||
return SOCKET_FAILED_CLOSE;
|
||||
case Type::SocketFailedToConnect:
|
||||
return SOCKET_FAILED_CONNECT;
|
||||
case Type::SocketFailedToRead:
|
||||
return SOCKET_FAILED_READ;
|
||||
case Type::SocketFailedToWrite:
|
||||
return SOCKET_FAILED_WRITE;
|
||||
case Type::SocketAcceptorFailedToBind:
|
||||
return ACCEPTOR_FAILED_BIND;
|
||||
case Type::SocketAcceptorFailedToListen:
|
||||
return ACCEPTOR_FAILED_LISTEN;
|
||||
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;
|
||||
|
||||
// Other Errors
|
||||
case Type::TooManyEvents:
|
||||
return TOO_MANY_EVENTS;
|
||||
case Type::Unknown:
|
||||
return UNKNOWN;
|
||||
case Type::NoErrorFound:
|
||||
return NO_ERROR_FOUND;
|
||||
default:
|
||||
return INVALID;
|
||||
}
|
||||
@@ -380,14 +417,4 @@ bool EventFilter::match(const APIEvent& event) const noexcept {
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
neosocketeventfilter_t EventFilter::getNeoSocketEventFilter() const noexcept {
|
||||
neosocketeventfilter_t filterStruct;
|
||||
filterStruct.eventNumber = static_cast<decltype(filterStruct.eventNumber)>(type);
|
||||
filterStruct.severity = static_cast<decltype(filterStruct.severity)>(severity);
|
||||
if((serial.length() + 1) == sizeof(filterStruct.serial)) {
|
||||
std::memcpy(&filterStruct.serial[0], serial.c_str(), sizeof(filterStruct.serial));
|
||||
}
|
||||
return filterStruct;
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/api/event.h"
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#endif
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -39,6 +36,25 @@ void EventManager::cancelErrorDowngradingOnCurrentThread() {
|
||||
void EventManager::add(APIEvent event) {
|
||||
if(destructing)
|
||||
return;
|
||||
static const auto printLevel = []() -> std::optional<uint8_t> {
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
const auto level = std::getenv("LIBICSNEO_PRINT_EVENTS");
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
if(!level)
|
||||
return std::nullopt;
|
||||
try {
|
||||
return (uint8_t)std::stoi(level);
|
||||
} catch (std::invalid_argument const&) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}();
|
||||
if(printLevel && (uint8_t)event.getSeverity() >= *printLevel)
|
||||
std::cerr << event.describe() << std::endl;
|
||||
if(event.getSeverity() == APIEvent::Severity::Error) {
|
||||
// if the error was added on a thread that downgrades errors (non-user thread)
|
||||
std::lock_guard<std::mutex> lk(downgradedThreadsMutex);
|
||||
@@ -46,7 +62,7 @@ void EventManager::add(APIEvent event) {
|
||||
if(i != downgradedThreads.end() && i->second) {
|
||||
event.downgradeFromError();
|
||||
{
|
||||
std::lock_guard<std::mutex> eventsLock{eventsMutex};
|
||||
std::lock_guard<std::mutex> eventsLock(eventsMutex);
|
||||
addEventInternal(event);
|
||||
} // free the lock so that callbacks may modify events
|
||||
runCallbacks(event);
|
||||
@@ -152,60 +168,23 @@ bool EventManager::isDowngradingErrorsOnCurrentThread() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
std::optional<std::vector<neosocketevent_t>> EventManager::getServerEvents(const size_t& max)
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped<RPC>(RPC::GET_EVENTS) && socket.writeTyped<size_t>(max)))
|
||||
return std::nullopt;
|
||||
|
||||
size_t count;
|
||||
if(!socket.readTyped(count) || count < 0 || count > eventLimit)
|
||||
return std::nullopt;
|
||||
|
||||
std::optional<std::vector<neosocketevent_t>> ret;
|
||||
if(count == size_t(0))
|
||||
return ret;
|
||||
|
||||
auto& eventStructs = ret.emplace(count);
|
||||
if(!socket.read(eventStructs.data(), (eventStructs.size() * sizeof(neosocketevent_t))))
|
||||
return std::nullopt;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // ICSNEO_ENABLE_DEVICE_SHARING
|
||||
|
||||
void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilter filter) {
|
||||
eventOutput.clear();
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto serverEvents = getServerEvents(max);
|
||||
if(serverEvents) {
|
||||
std::scoped_lock eventsLock{eventsMutex};
|
||||
for(neosocketevent_t& event : *serverEvents) {
|
||||
eventOutput.emplace_back(event);
|
||||
}
|
||||
if(max != 0u)
|
||||
max -= eventOutput.size();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
std::scoped_lock eventsLock{eventsMutex};
|
||||
{
|
||||
if(max == 0) // A limit of 0 indicates no limit
|
||||
max = (size_t)-1;
|
||||
std::lock_guard<std::mutex> lk(eventsMutex);
|
||||
|
||||
size_t count = 0;
|
||||
auto it = events.begin();
|
||||
while(it != events.end()) {
|
||||
if(filter.match(*it)) {
|
||||
eventOutput.push_back(*it);
|
||||
it = events.erase(it);
|
||||
if(++count >= max)
|
||||
break; // We now have as many written to output as we can
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
if(max == 0) // A limit of 0 indicates no limit
|
||||
max = (size_t)-1;
|
||||
|
||||
size_t count = 0;
|
||||
eventOutput.clear();
|
||||
auto it = events.begin();
|
||||
while(it != events.end()) {
|
||||
if(filter.match(*it)) {
|
||||
eventOutput.push_back(*it);
|
||||
it = events.erase(it);
|
||||
if(++count >= max)
|
||||
break; // We now have as many written to output as we can
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
#include <jni.h>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -45,4 +46,4 @@ void icsneo::SetEventLimit(size_t newLimit) {
|
||||
|
||||
size_t icsneo::GetEventLimit() {
|
||||
return EventManager::GetInstance().getEventLimit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
#pragma warning(disable : 4996) // STL time functions
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
@@ -73,6 +74,25 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
|
||||
return false;
|
||||
|
||||
const neomessage_frame_t& frame = *reinterpret_cast<const neomessage_frame_t*>(&newmsg);
|
||||
|
||||
auto copyStatusData = [&]() {
|
||||
oldmsg.NetworkID = static_cast<uint8_t>(frame.netid); // Note: NetID remapping from the original API is not supported
|
||||
oldmsg.NetworkID2 = static_cast<uint8_t>(frame.netid >> 8);
|
||||
oldmsg.DescriptionID = frame.description;
|
||||
oldmsg.StatusBitField = frame.status.statusBitfield[0];
|
||||
oldmsg.StatusBitField2 = frame.status.statusBitfield[1];
|
||||
oldmsg.StatusBitField3 = frame.status.statusBitfield[2];
|
||||
oldmsg.StatusBitField4 = frame.status.statusBitfield[3];
|
||||
};
|
||||
|
||||
auto copyFrameData = [&]() {
|
||||
oldmsg.ExtraDataPtr = (void*)frame.data;
|
||||
oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0;
|
||||
memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8));
|
||||
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
|
||||
copyStatusData();
|
||||
};
|
||||
|
||||
switch (Network::Type(frame.type))
|
||||
{
|
||||
case Network::Type::CAN:
|
||||
@@ -81,28 +101,42 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
|
||||
oldmsg.Protocol = frame.status.canfdFDF ? SPY_PROTOCOL_CANFD : SPY_PROTOCOL_CAN;
|
||||
oldmsg.NumberBytesData = static_cast<uint8_t>(std::min(frame.length, (size_t)255));
|
||||
oldmsg.NumberBytesHeader = 4;
|
||||
copyFrameData();
|
||||
break;
|
||||
case Network::Type::Ethernet:
|
||||
oldmsg.Protocol = SPY_PROTOCOL_ETHERNET;
|
||||
oldmsg.NumberBytesData = static_cast<uint8_t>(frame.length & 0xFF);
|
||||
oldmsg.NumberBytesHeader = static_cast<uint8_t>(frame.length >> 8);
|
||||
copyFrameData();
|
||||
break;
|
||||
case Network::Type::LIN:
|
||||
{
|
||||
const neomessage_lin_t& linFrame = *reinterpret_cast<const neomessage_lin_t*>(&frame);
|
||||
icsSpyMessageJ1850& linSpyMsg = *reinterpret_cast<icsSpyMessageJ1850*>(&oldmsg);
|
||||
linSpyMsg.Protocol = SPY_PROTOCOL_LIN;
|
||||
linSpyMsg.NumberBytesHeader = static_cast<uint8_t>(std::min(linFrame.length, static_cast<size_t>(3)));
|
||||
linSpyMsg.NumberBytesData = static_cast<uint8_t>(linFrame.length - linSpyMsg.NumberBytesHeader);
|
||||
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
|
||||
copyStatusData();
|
||||
if ((2 < linFrame.length) && (linFrame.length <= 10)) {
|
||||
auto copyBytes = std::min(linSpyMsg.NumberBytesData, static_cast<uint8_t>(6));
|
||||
std::memcpy(oldmsg.Data, frame.data, copyBytes);
|
||||
oldmsg.Data[copyBytes] = linFrame.checksum;
|
||||
} else if (2 == linFrame.length) {
|
||||
std::memset(oldmsg.Data, 0, 8);
|
||||
linSpyMsg.Header[linSpyMsg.NumberBytesHeader] = linFrame.checksum;
|
||||
++linSpyMsg.NumberBytesHeader;
|
||||
} else {
|
||||
std::memset(oldmsg.Data, 0, 8);
|
||||
}
|
||||
if (linFrame.linStatus.txCommander)
|
||||
linSpyMsg.StatusBitField |= SPY_STATUS_INIT_MESSAGE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
oldmsg.ExtraDataPtr = (void*)frame.data;
|
||||
oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0;
|
||||
memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8));
|
||||
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
|
||||
oldmsg.NetworkID = static_cast<uint8_t>(frame.netid); // Note: NetID remapping from the original API is not supported
|
||||
oldmsg.NetworkID2 = static_cast<uint8_t>(frame.netid >> 8);
|
||||
oldmsg.DescriptionID = frame.description;
|
||||
oldmsg.StatusBitField = frame.status.statusBitfield[0];
|
||||
oldmsg.StatusBitField2 = frame.status.statusBitfield[1];
|
||||
oldmsg.StatusBitField3 = frame.status.statusBitfield[2];
|
||||
oldmsg.StatusBitField4 = frame.status.statusBitfield[3];
|
||||
|
||||
// Timestamp - epoch = 1/1/2007 - 25ns per tick most of the time
|
||||
uint64_t t = frame.timestamp;
|
||||
uint16_t res = 0;
|
||||
@@ -127,6 +161,90 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SpyMessageToNeoMessage(const icsSpyMessage& oldmsg, neomessage_frame_t& frame, unsigned int& lNetworkID)
|
||||
{
|
||||
frame.netid = static_cast<uint16_t>(lNetworkID);
|
||||
frame.description = oldmsg.DescriptionID;
|
||||
|
||||
frame.status.statusBitfield[0] = oldmsg.StatusBitField;
|
||||
frame.status.statusBitfield[1] = oldmsg.StatusBitField2;
|
||||
frame.status.statusBitfield[2] = oldmsg.StatusBitField3;
|
||||
frame.status.statusBitfield[3] = oldmsg.StatusBitField4;
|
||||
|
||||
auto copyFrameDataPtr = [&]() {
|
||||
memcpy(frame.header, &oldmsg.ArbIDOrHeader, sizeof(frame.header));
|
||||
if ((oldmsg.ExtraDataPtr != nullptr) && (oldmsg.ExtraDataPtrEnabled == 1))
|
||||
frame.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
|
||||
else
|
||||
frame.data = oldmsg.Data;
|
||||
};
|
||||
|
||||
switch(oldmsg.Protocol)
|
||||
{
|
||||
case SPY_PROTOCOL_ETHERNET:
|
||||
{
|
||||
frame.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
|
||||
copyFrameDataPtr();
|
||||
break;
|
||||
}
|
||||
case SPY_PROTOCOL_LIN:
|
||||
{
|
||||
neomessage_lin_t& linFrame = *reinterpret_cast<neomessage_lin_t*>(&frame);
|
||||
const uint8_t numberBytesHeader = std::min(oldmsg.NumberBytesHeader, static_cast<uint8_t>(3));
|
||||
const uint8_t numberBytesData = std::min(oldmsg.NumberBytesData, static_cast<uint8_t>(7));
|
||||
frame.length = numberBytesHeader + numberBytesData;
|
||||
linFrame.type = ICSNEO_NETWORK_TYPE_LIN;
|
||||
if (oldmsg.StatusBitField & SPY_STATUS_INIT_MESSAGE)
|
||||
linFrame.linStatus.txCommander = true;
|
||||
else
|
||||
linFrame.linStatus.txResponder = true;
|
||||
|
||||
copyFrameDataPtr();
|
||||
uint8_t protID = linFrame.header[0] & 0x3Fu;
|
||||
auto bit = [&](uint8_t pos)->uint8_t { return ((protID >> pos) & 0x1u); };
|
||||
protID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7);
|
||||
protID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6);
|
||||
linFrame.header[0] = protID;
|
||||
if (frame.length > 2) {
|
||||
size_t checksum = 0;
|
||||
uint8_t* lastByte = nullptr;
|
||||
for(size_t idx = 1; idx < (frame.length - 1); ++idx) {
|
||||
if (idx < oldmsg.NumberBytesHeader) {
|
||||
checksum += frame.header[idx];
|
||||
lastByte = (frame.header + idx + 1);
|
||||
} else {
|
||||
checksum += frame.data[idx-numberBytesHeader];
|
||||
lastByte = const_cast<uint8_t*>(frame.data) + idx - numberBytesHeader + 1;
|
||||
}
|
||||
if (checksum > 255) { checksum -= 255; }
|
||||
}
|
||||
size_t enhanced = frame.header[0] + checksum;
|
||||
if (enhanced > 255) { enhanced -= 255; }
|
||||
checksum ^= 0xff;
|
||||
enhanced ^= 0xff;
|
||||
|
||||
if ((lastByte != nullptr) && (*lastByte != checksum) && (*lastByte == enhanced))
|
||||
linFrame.linStatus.txChecksumEnhanced = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPY_PROTOCOL_CANFD:
|
||||
{
|
||||
frame.length = oldmsg.NumberBytesData;
|
||||
frame.status.canfdFDF = true;
|
||||
copyFrameDataPtr();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
frame.length = oldmsg.NumberBytesData;
|
||||
copyFrameDataPtr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool Within(size_t value, size_t min, size_t max)
|
||||
{
|
||||
return ((min <= value) && (value < max));
|
||||
@@ -396,25 +514,9 @@ int LegacyDLLExport icsneoTxMessagesEx(void* hObject, icsSpyMessage* pMsg, unsig
|
||||
*NumTxed = 0;
|
||||
for (unsigned int i = 0; i < lNumMessages; i++)
|
||||
{
|
||||
const icsSpyMessage& oldmsg = pMsg[i];
|
||||
newmsg = {};
|
||||
newmsg.netid = (uint16_t)lNetworkID;
|
||||
newmsg.description = oldmsg.DescriptionID;
|
||||
memcpy(newmsg.header, &oldmsg.ArbIDOrHeader, sizeof(newmsg.header));
|
||||
if (oldmsg.Protocol != SPY_PROTOCOL_ETHERNET)
|
||||
newmsg.length = oldmsg.NumberBytesData;
|
||||
else
|
||||
newmsg.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
|
||||
if (oldmsg.ExtraDataPtr != nullptr && oldmsg.ExtraDataPtrEnabled == 1)
|
||||
newmsg.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
|
||||
else
|
||||
newmsg.data = oldmsg.Data;
|
||||
newmsg.status.statusBitfield[0] = oldmsg.StatusBitField;
|
||||
newmsg.status.statusBitfield[1] = oldmsg.StatusBitField2;
|
||||
newmsg.status.statusBitfield[2] = oldmsg.StatusBitField3;
|
||||
newmsg.status.statusBitfield[3] = oldmsg.StatusBitField4;
|
||||
if (oldmsg.Protocol == SPY_PROTOCOL_CANFD)
|
||||
newmsg.status.canfdFDF = true;
|
||||
const icsSpyMessage& oldMsg = pMsg[i];
|
||||
SpyMessageToNeoMessage(oldMsg, newmsg, lNetworkID);
|
||||
if (icsneo_transmit(device, reinterpret_cast<neomessage_t*>(&newmsg)))
|
||||
(*NumTxed)++;
|
||||
}
|
||||
@@ -464,6 +566,54 @@ void LegacyDLLExport icsneoSetISO15765RxParameters(void* hObject, int lNetwork,
|
||||
return;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetRTC(void* hObject, icsSpyTime* time)
|
||||
{
|
||||
if(!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||
|
||||
uint64_t time64 = 0;
|
||||
if(!icsneo_getRTC(device, &time64))
|
||||
return false;
|
||||
|
||||
std::time_t seconds = time64;
|
||||
/* To accommodate local time bugzilla #6600 https://intrepidcs.homeip.net:100/bugzilla/show_bug.cgi?id=6600 */
|
||||
// local time must be used here
|
||||
const auto timeInfo = std::localtime(&seconds);
|
||||
if(!timeInfo)
|
||||
return false;
|
||||
|
||||
time->sec = (unsigned char)timeInfo->tm_sec; // Will never hit 60 (leap second) because tm_sec comes from RTCCTIME
|
||||
time->min = (unsigned char)timeInfo->tm_min;
|
||||
time->hour = (unsigned char)timeInfo->tm_hour;
|
||||
time->day = (unsigned char)timeInfo->tm_mday;
|
||||
time->month = (unsigned char)timeInfo->tm_mon + 1;
|
||||
time->year = (unsigned char)timeInfo->tm_year % 100;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoSetRTC(void* hObject, const icsSpyTime* time)
|
||||
{
|
||||
if(!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||
|
||||
std::tm timeInfo{};
|
||||
timeInfo.tm_sec = time->sec;
|
||||
timeInfo.tm_min = time->min;
|
||||
timeInfo.tm_hour = time->hour;
|
||||
timeInfo.tm_mday = time->day;
|
||||
timeInfo.tm_mon = time->month - 1;
|
||||
timeInfo.tm_year = time->year + 100;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define timegm _mkgmtime
|
||||
#endif
|
||||
|
||||
return icsneo_setRTC(device, (uint64_t)timegm(&timeInfo));
|
||||
}
|
||||
|
||||
//Device Functions
|
||||
int LegacyDLLExport icsneoGetConfiguration(void* hObject, unsigned char* pData, int* lNumBytes)
|
||||
{
|
||||
@@ -653,11 +803,43 @@ int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int*
|
||||
return false;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, TCHAR* szErrorDescriptionShort, TCHAR* szErrorDescriptionLong,
|
||||
int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, char* szErrorDescriptionShort, char* szErrorDescriptionLong,
|
||||
int* lMaxLengthShort, int* lMaxLengthLong, int* lErrorSeverity, int* lRestartNeeded)
|
||||
{
|
||||
// TODO Implement
|
||||
return false;
|
||||
if (szErrorDescriptionShort == nullptr || szErrorDescriptionLong == nullptr
|
||||
|| lMaxLengthShort == nullptr || lMaxLengthLong == nullptr || lErrorSeverity == nullptr
|
||||
|| lRestartNeeded == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Set and send back 0. We will not restart the software.
|
||||
*lRestartNeeded = 0;
|
||||
|
||||
//Using the error number, get the description from the event.
|
||||
const char* tempDescription = APIEvent::DescriptionForType(APIEvent::Type(lErrorNumber));
|
||||
int descrLength = int(std::strlen(tempDescription));
|
||||
|
||||
//Check to make sure the length of the error is not >= the buffer.
|
||||
if (descrLength >= *lMaxLengthShort || descrLength >= *lMaxLengthLong)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Copy the error description to the inout Short and Long arguments.
|
||||
std::copy(tempDescription, tempDescription + descrLength, szErrorDescriptionShort);
|
||||
std::copy(tempDescription, tempDescription + descrLength, szErrorDescriptionLong);
|
||||
|
||||
//Add the null terminator.
|
||||
szErrorDescriptionShort[descrLength] = '\0';
|
||||
szErrorDescriptionLong[descrLength] = '\0';
|
||||
|
||||
//Update the inout lengths to what the actual length of the error is
|
||||
*lMaxLengthShort = *lMaxLengthLong = descrLength;
|
||||
|
||||
//Update the inout severity argument.
|
||||
*lErrorSeverity = int(APIEvent::Severity::Any);
|
||||
return true;
|
||||
}
|
||||
|
||||
//ISO15765-2 Functions
|
||||
@@ -736,6 +918,18 @@ int LegacyDLLExport icsneoStopSockServer(void* hObject)
|
||||
return false;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetDeviceStatus(void* hObject, icsDeviceStatus* deviceStatus, size_t* deviceStatusSize)
|
||||
{
|
||||
if (!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||
|
||||
if (deviceStatus == nullptr || deviceStatusSize == nullptr)
|
||||
return false;
|
||||
|
||||
return icsneo_getDeviceStatus(device, deviceStatus, deviceStatusSize);
|
||||
}
|
||||
|
||||
//CoreMini Script functions
|
||||
int LegacyDLLExport icsneoScriptStart(void* hObject, int iLocation)
|
||||
{
|
||||
@@ -915,6 +1109,12 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
|
||||
case NEODEVICE_RADGIGALOG:
|
||||
*pDeviceSettingsType = DeviceRADGigalogSettingsType;
|
||||
break;
|
||||
case NEODEVICE_RADMOON3:
|
||||
*pDeviceSettingsType = DeviceRADMoon3SettingsType;
|
||||
break;
|
||||
case NEODEVICE_RED2:
|
||||
*pDeviceSettingsType = DeviceRed2SettingsType;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -952,6 +1152,15 @@ int LegacyDLLExport icsneoGetDeviceSettings(void* hObject, SDeviceSettings* pSet
|
||||
return !!icsneo_settingsReadStructure(device, &pSettings->Settings, iNumBytes - offset);
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoLoadDefaultSettings(void* hObject)
|
||||
{
|
||||
if (!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
|
||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||
return icsneo_settingsApplyDefaults(device);
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoSetBitRateEx(void* hObject, unsigned long BitRate, int NetworkID, int iOptions)
|
||||
{
|
||||
if (!icsneoValidateHObject(hObject))
|
||||
@@ -1292,7 +1501,7 @@ int LegacyDLLExport icsneoSerialNumberFromString(unsigned long* serial, char* da
|
||||
return false;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NETWORK_ADAPTER_INFO* aInfo)
|
||||
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NDIS_ADAPTER_INFORMATION* aInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_EXAMPLES=ON \
|
||||
-DLIBICSNEO_BUILD_TESTS=ON -DLIBICSNEO_ENABLE_TCP=ON || exit 1
|
||||
|
||||
cmake --build build || exit 1
|
||||
|
||||
exit 0
|
||||
@@ -6,7 +6,7 @@ REM build
|
||||
cd build
|
||||
set CFLAGS=/WX
|
||||
set CXXFLAGS=/WX
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBICSNEO_BUILD_TESTS=ON ..
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBICSNEO_BUILD_TESTS=ON -DLIBICSNEO_ENABLE_TCP=ON ..
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
cmake --build .
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
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()
|
||||
@@ -13,10 +13,7 @@
|
||||
#include "icsneo/communication/message/filter/main51messagefilter.h"
|
||||
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||
#include "icsneo/communication/message/versionmessage.h"
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#endif
|
||||
#include "icsneo/communication/message/componentversionsmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -71,11 +68,6 @@ bool Communication::isDisconnected() {
|
||||
return driver->isDisconnected();
|
||||
}
|
||||
|
||||
void Communication::modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb) {
|
||||
std::scoped_lock lk(rawCallbacksMutex);
|
||||
cb(rawCallbacks);
|
||||
}
|
||||
|
||||
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
|
||||
// This is here so that other communication types (like multichannel) can override it
|
||||
return rawWrite(bytes);
|
||||
@@ -226,15 +218,6 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
|
||||
std::condition_variable cv;
|
||||
std::shared_ptr<Message> returnedMessage;
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
auto socket = lockSocket();
|
||||
int64_t ms = timeout.count();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_LOCK) && socket.writeString(driver->device.serial) && socket.writeTyped(ms)))
|
||||
return nullptr;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
std::unique_lock<std::mutex> fnLk(syncMessageMutex); // Only allow for one sync message at a time
|
||||
std::unique_lock<std::mutex> cvLk(cvMutex); // Don't let the callback fire until we're waiting for it
|
||||
int cb = addMessageCallback(std::make_shared<MessageCallback>([&cvMutex, &returnedMessage, &cv](std::shared_ptr<Message> message) {
|
||||
@@ -257,13 +240,6 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
|
||||
|
||||
if(fail) // The caller's function failed, so don't return a message
|
||||
returnedMessage.reset();
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
if(!(socket.writeTyped(RPC::DEVICE_UNLOCK) && socket.writeString(driver->device.serial)))
|
||||
return nullptr;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
// Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking
|
||||
return returnedMessage;
|
||||
@@ -299,11 +275,6 @@ void Communication::readTask() {
|
||||
}
|
||||
|
||||
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
|
||||
{
|
||||
std::lock_guard lk(rawCallbacksMutex);
|
||||
for(auto& cb : rawCallbacks)
|
||||
cb(readBytes);
|
||||
}
|
||||
if(redirectingRead) {
|
||||
// redirectingRead is an atomic so it can be set without acquiring a mutex
|
||||
// However, we do not clear it without the mutex. The idea is that if another
|
||||
@@ -331,3 +302,18 @@ void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
+95
-18
@@ -11,6 +11,11 @@
|
||||
#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/command.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
@@ -24,6 +29,12 @@
|
||||
#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 <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
@@ -138,6 +149,42 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
|
||||
return true;
|
||||
}
|
||||
case Network::Type::A2B: {
|
||||
result = HardwareA2BPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||
}
|
||||
|
||||
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
|
||||
msg.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::LIN: {
|
||||
result = HardwareLINPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||
}
|
||||
|
||||
LINMessage& msg = *static_cast<LINMessage*>(result.get());
|
||||
msg.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::MDIO: {
|
||||
result = HardwareMDIOPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||
}
|
||||
|
||||
MDIOMessage& msg = *static_cast<MDIOMessage*>(result.get());
|
||||
msg.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::Internal: {
|
||||
switch(packet->network.getNetID()) {
|
||||
case Network::NetID::Reset_Status: {
|
||||
@@ -217,16 +264,54 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::ExtendedCommand: {
|
||||
|
||||
if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse))
|
||||
break; // Handle as a raw message, might not be a generic response
|
||||
|
||||
const auto& resp = *reinterpret_cast<ExtendedResponseMessage::PackedGenericResponse*>(packet->data.data());
|
||||
if(resp.header.command != ExtendedCommand::GenericReturn)
|
||||
break; // Handle as a raw message
|
||||
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;
|
||||
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 msg = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
|
||||
result = msg;
|
||||
return true;
|
||||
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());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Network::NetID::FlexRayControl: {
|
||||
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
|
||||
@@ -345,26 +430,18 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::DiskData: {
|
||||
result = std::make_shared<DiskDataMessage>(std::move(packet->data));
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Network::Type::A2B: {
|
||||
result = HardwareA2BPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||
}
|
||||
|
||||
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
|
||||
msg.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// For the moment other types of messages will automatically be decoded as raw messages
|
||||
result = std::make_shared<RawMessage>(packet->network, packet->data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#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"
|
||||
@@ -9,7 +11,8 @@
|
||||
#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;
|
||||
|
||||
@@ -96,6 +99,30 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::I2C
|
||||
case Network::Type::LIN: {
|
||||
auto linmsg = std::dynamic_pointer_cast<LINMessage>(message);
|
||||
if(!linmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareLINPacket::EncodeFromMessage(*linmsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::LIN
|
||||
case Network::Type::MDIO: {
|
||||
auto mdiomsg = std::dynamic_pointer_cast<MDIOMessage>(message);
|
||||
if(!mdiomsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareMDIOPacket::EncodeFromMessage(*mdiomsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::MDIO
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
@@ -172,6 +199,17 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case Message::Type::LiveData: {
|
||||
auto liveDataMsg = std::dynamic_pointer_cast<LiveDataMessage>(message);
|
||||
if(!liveDataMsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed LiveDataMessage
|
||||
}
|
||||
if(!HardwareLiveDataPacket::EncodeFromMessage(*liveDataMsg, result, report))
|
||||
return false;
|
||||
result = packetizer.packetWrap(result, false);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -182,7 +220,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||
// Size for the host-to-device long format is the size of the entire packet + 1
|
||||
// So +1 for AA header, +1 for short format header, +2 for long format size, and +2 for long format NetID
|
||||
// Then an extra +1, due to a firmware idiosyncrasy
|
||||
uint16_t size = uint16_t(buffer->size()) + 1 + 1 + 2 + 2 + 1;
|
||||
uint16_t size = static_cast<uint16_t>(buffer->size()) + 1 + 1 + 2 + 2 + 1;
|
||||
|
||||
buffer->insert(buffer->begin(), {
|
||||
(uint8_t)Network::NetID::RED, // 0x0C for long message
|
||||
(uint8_t)size, // Size, little endian 16-bit
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
#include <cstring>
|
||||
#include "icsneo/communication/interprocessmailbox.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
bool InterprocessMailbox::open(const std::string& name, bool create)
|
||||
{
|
||||
if(!queuedSem.open(name + "-qs", create))
|
||||
return false;
|
||||
|
||||
if(!emptySem.open(name + "-es", create, MESSAGE_COUNT))
|
||||
return false;
|
||||
|
||||
if(!sharedMem.open(name + "-sm", BLOCK_SIZE * MESSAGE_COUNT, create))
|
||||
return false;
|
||||
|
||||
valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
InterprocessMailbox::operator bool() const
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::close()
|
||||
{
|
||||
valid = false;
|
||||
return queuedSem.close() && emptySem.close() && sharedMem.close();
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout)
|
||||
{
|
||||
if(!queuedSem.wait(timeout))
|
||||
return false;
|
||||
auto it = sharedMem.data() + (index * BLOCK_SIZE);
|
||||
messageLength = *(LengthFieldType*)it;
|
||||
it += LENGTH_FIELD_SIZE;
|
||||
std::memcpy(data, it, std::min(messageLength, MAX_DATA_SIZE));
|
||||
if(!emptySem.post())
|
||||
return false;
|
||||
++index;
|
||||
index %= MESSAGE_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout)
|
||||
{
|
||||
if(!emptySem.wait(timeout))
|
||||
return false; // the buffer is full and we timed out
|
||||
auto it = sharedMem.data() + (index * BLOCK_SIZE);
|
||||
*(LengthFieldType*)it = messageLength;
|
||||
it += LENGTH_FIELD_SIZE;
|
||||
std::memcpy(it, data, messageLength);
|
||||
if(!queuedSem.post())
|
||||
return false;
|
||||
++index;
|
||||
index %= MESSAGE_COUNT;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#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
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "icsneo/communication/message/callback/streamoutput/a2bdecoder.h"
|
||||
#include <chrono>
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
static constexpr uint8_t maxChannel = 255;
|
||||
|
||||
|
||||
size_t A2BAudioChannelMap::getChannelIndex(Channel channel, A2BMessage::A2BDirection dir) const {
|
||||
size_t output = (size_t)channel;
|
||||
|
||||
if(dir == A2BMessage::A2BDirection::Upstream) {
|
||||
output++;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
A2BAudioChannelMap::A2BAudioChannelMap(uint8_t tdm) {
|
||||
rawMap.resize(2*tdm, maxChannel);
|
||||
}
|
||||
|
||||
void A2BAudioChannelMap::set(Channel outChannel, A2BMessage::A2BDirection dir, Channel inChannel) {
|
||||
auto index = getChannelIndex(outChannel, dir);
|
||||
rawMap[index] = inChannel;
|
||||
}
|
||||
|
||||
void A2BAudioChannelMap::setAll(Channel inChannel) {
|
||||
std::fill(rawMap.begin(), rawMap.end(), inChannel);
|
||||
}
|
||||
|
||||
Channel A2BAudioChannelMap::get(Channel outChannel, A2BMessage::A2BDirection dir) const {
|
||||
auto index = getChannelIndex(outChannel, dir);
|
||||
|
||||
return rawMap[index];
|
||||
}
|
||||
|
||||
size_t A2BAudioChannelMap::A2BAudioChannelMap::size() const {
|
||||
return rawMap.size();
|
||||
}
|
||||
|
||||
uint8_t A2BAudioChannelMap::getTDM() const {
|
||||
return (uint8_t)(rawMap.size() / 2);
|
||||
}
|
||||
|
||||
Channel& A2BAudioChannelMap::operator[](size_t idx) {
|
||||
return rawMap[idx];
|
||||
}
|
||||
|
||||
A2BAudioChannelMap::operator const std::vector<Channel>&() const {
|
||||
return rawMap;
|
||||
}
|
||||
|
||||
A2BDecoder::A2BDecoder(
|
||||
std::unique_ptr<std::istream>&& streamOut,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
) : channelSize16(chSize16), channelMap(chMap) {
|
||||
stream = std::move(streamOut);
|
||||
tdm = chMap.getTDM();
|
||||
initializeFromHeader();
|
||||
}
|
||||
|
||||
A2BDecoder::A2BDecoder(
|
||||
const char* filename,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
) : A2BDecoder(std::make_unique<std::ifstream>(filename, std::ios::binary), chSize16, chMap) { }
|
||||
|
||||
A2BDecoder::operator bool() const {
|
||||
return initialized && stream->good() && !stream->eof();
|
||||
}
|
||||
|
||||
void A2BDecoder::initializeFromHeader() {
|
||||
WaveFileHeader header;
|
||||
if(!stream->read((char*)&header, sizeof(header))) {
|
||||
initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Only allow 16 or 24 bit samples
|
||||
if(header.bitsPerSample != 16 && header.bitsPerSample != 24) {
|
||||
initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
audioBytesPerSample = header.bitsPerSample == 16 ? 2 : 3;
|
||||
channelsInWave = (uint8_t)header.numChannels;
|
||||
|
||||
size_t bytesPerSample = channelSize16 ? 2 : 4;
|
||||
size_t frameSize = 2*tdm*bytesPerSample;
|
||||
size_t frameSizeWave = (size_t)(channelsInWave) * (size_t)(audioBytesPerSample);
|
||||
|
||||
frame.resize(frameSize, 0);
|
||||
frameWave.resize(frameSizeWave, 0);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<A2BMessage> A2BDecoder::decode() {
|
||||
if(!*(this)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto a2bMessagePtr = std::make_shared<icsneo::A2BMessage>(
|
||||
tdm,
|
||||
channelSize16,
|
||||
2048
|
||||
);
|
||||
|
||||
A2BMessage& a2bMessage = *a2bMessagePtr.get();
|
||||
|
||||
a2bMessage.setMonitorBit(false); // Probably not necessary
|
||||
a2bMessage.setTxMsgBit(true);
|
||||
|
||||
a2bMessage.network = Network(Network::NetID::A2B2);
|
||||
|
||||
for(uint32_t frameIndex = 0; frameIndex < a2bMessage.getNumFrames(); frameIndex++) {
|
||||
if(!stream->read((char*)frameWave.data(), frameWave.size())) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(size_t icsChannel = 0; icsChannel < channelMap.size(); icsChannel++) {
|
||||
|
||||
if(channelMap[icsChannel] >= maxChannel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t wBegin = audioBytesPerSample * channelMap[icsChannel];
|
||||
A2BPCMSample sample = 0;
|
||||
uint8_t* sampBytes = (uint8_t*)&sample;
|
||||
|
||||
std::copy(frameWave.begin() + wBegin, frameWave.begin() + wBegin + audioBytesPerSample, sampBytes);
|
||||
a2bMessage[frameIndex][icsChannel] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
return a2bMessagePtr;
|
||||
}
|
||||
|
||||
bool A2BDecoder::outputAll(std::shared_ptr<Device>& device) {
|
||||
const auto& networks = device->getSupportedTXNetworks();
|
||||
|
||||
if(std::none_of(networks.begin(), networks.end(), [](const Network& net) { return net.getNetID() == Network::NetID::A2B2; })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(*this) {
|
||||
device->transmit(decode());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
|
||||
#include "icsneo/device/tree/rada2b/rada2b.h"
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
namespace icsneo {
|
||||
|
||||
void A2BWAVOutput::writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const {
|
||||
|
||||
WaveFileHeader header = WaveFileHeader(2 * firstMsg->getNumChannels(), wavSampleRate, firstMsg->getBitDepth());
|
||||
header.write(stream);
|
||||
streamStartPos = static_cast<uint32_t>(stream->tellp());
|
||||
|
||||
}
|
||||
|
||||
bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
|
||||
if(closed)
|
||||
{
|
||||
|
||||
if(closed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(message->type != Message::Type::Frame)
|
||||
if(message->type != Message::Type::Frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& frame = std::static_pointer_cast<Frame>(message);
|
||||
|
||||
@@ -31,23 +34,21 @@ bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
|
||||
firstMessageFlag = false;
|
||||
}
|
||||
|
||||
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::DownStream)) {
|
||||
close();
|
||||
|
||||
return false;
|
||||
// Might need to readd this block of code later if sample alignment fix is necessary
|
||||
/*
|
||||
std::streamsize bps = (std::streamsize)a2bmsg->getBytesPerSample();
|
||||
for(size_t i=0; i<a2bmsg->getNumSamples(); i++) {
|
||||
A2BPCMSample samp = *(a2bmsg->getSample(i));
|
||||
write((void*)&samp, bps);
|
||||
}
|
||||
*/
|
||||
|
||||
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::UpStream)) {
|
||||
close();
|
||||
|
||||
return false;
|
||||
}
|
||||
write((void*)a2bmsg->getAudioBuffer(), a2bmsg->getAudioBufferSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void A2BWAVOutput::close() const
|
||||
{
|
||||
void A2BWAVOutput::close() const {
|
||||
if(closed) {
|
||||
return;
|
||||
}
|
||||
@@ -65,36 +66,4 @@ void A2BWAVOutput::close() const
|
||||
closed = true;
|
||||
}
|
||||
|
||||
bool A2BWAVOutput::writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessage::A2BDirection dir) const
|
||||
{
|
||||
uint8_t numChannels = msg->getNumChannels();
|
||||
|
||||
uint8_t channel = 0;
|
||||
uint32_t frame = 0;
|
||||
uint8_t bitDepth = msg->getBitDepth();
|
||||
|
||||
while(true) {
|
||||
auto sample = msg->getSample(dir, channel, frame);
|
||||
|
||||
if(!sample) {
|
||||
if(channel == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t audioSample = sample.value() >> (32 - bitDepth);
|
||||
|
||||
write((void*)(&audioSample), A2BPCM_SAMPLE_SIZE);
|
||||
|
||||
channel = (channel + 1) % numChannels;
|
||||
if(channel == 0) {
|
||||
frame++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#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
|
||||
@@ -0,0 +1,15 @@
|
||||
#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
|
||||
@@ -2,6 +2,7 @@
|
||||
#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;
|
||||
|
||||
@@ -17,6 +18,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
|
||||
neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg;
|
||||
auto framemsg = std::static_pointer_cast<Frame>(message);
|
||||
const auto netType = framemsg->network.getType();
|
||||
|
||||
frame.netid = (neonetid_t)framemsg->network.getNetID();
|
||||
frame.type = (neonettype_t)netType;
|
||||
frame.description = framemsg->description;
|
||||
@@ -53,6 +55,47 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
|
||||
//eth.status.xyz = ethmsg->noPadding;
|
||||
break;
|
||||
}
|
||||
case Network::Type::LIN: {
|
||||
neomessage_lin_t& lin = *(neomessage_lin_t*)&neomsg;
|
||||
auto linmsg = std::static_pointer_cast<LINMessage>(message);
|
||||
if(!linmsg) { break; }
|
||||
const auto linHdrBytes = std::min(linmsg->data.size(), static_cast<size_t>(2));
|
||||
lin.header[0] = linmsg->protectedID;
|
||||
std::copy(linmsg->data.begin(), linmsg->data.begin() + linHdrBytes, lin.header + 1);
|
||||
linmsg->calcChecksum(*linmsg);
|
||||
lin.checksum = linmsg->checksum;
|
||||
lin.data = linmsg->data.data() + linHdrBytes;
|
||||
if(linmsg->isEnhancedChecksum != linmsg->statusFlags.TxChecksumEnhanced) {
|
||||
linmsg->isEnhancedChecksum = true;
|
||||
linmsg->statusFlags.TxChecksumEnhanced = true;
|
||||
}
|
||||
if(linmsg->data.size())
|
||||
lin.length = linmsg->data.size() + 2;
|
||||
else
|
||||
lin.length = 1;
|
||||
|
||||
lin.linStatus = {
|
||||
linmsg->statusFlags.TxChecksumEnhanced,
|
||||
linmsg->statusFlags.TxCommander,
|
||||
linmsg->statusFlags.TxResponder,
|
||||
linmsg->statusFlags.UpdateResponderOnce,
|
||||
linmsg->statusFlags.HasUpdatedResponderOnce,
|
||||
linmsg->statusFlags.BusRecovered,
|
||||
linmsg->statusFlags.BreakOnly
|
||||
};
|
||||
|
||||
lin.status.linJustBreakSync = linmsg->errFlags.ErrRxBreakSyncOnly;
|
||||
lin.status.linErrorTXRXMismatch = linmsg->errFlags.ErrTxRxMismatch;
|
||||
lin.status.linErrorRXBreakNotZero = linmsg->errFlags.ErrRxBreakNotZero;
|
||||
lin.status.linErrorRXBreakTooShort = linmsg->errFlags.ErrRxBreakTooShort;
|
||||
lin.status.linErrorRXSyncNot55 = linmsg->errFlags.ErrRxSyncNot55;
|
||||
lin.status.linErrorRXDataGreaterEight = linmsg->errFlags.ErrRxDataLenOver8;
|
||||
lin.status.linSyncFrameError = linmsg->errFlags.ErrFrameSync;
|
||||
lin.status.linIDFrameError = linmsg->errFlags.ErrFrameMessageID;
|
||||
lin.status.linSlaveByteError = linmsg->errFlags.ErrFrameResponderData;
|
||||
lin.status.checksumError = linmsg->errFlags.ErrChecksumMatch;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO Implement others
|
||||
break;
|
||||
@@ -105,6 +148,53 @@ std::shared_ptr<Message> icsneo::CreateMessageFromNeoMessage(const neomessage_t*
|
||||
ethmsg->data.insert(ethmsg->data.end(), eth.data, eth.data + eth.length);
|
||||
return ethmsg;
|
||||
}
|
||||
case Network::Type::LIN: {
|
||||
neomessage_lin_t& lin = *(neomessage_lin_t*)neomessage;
|
||||
auto linmsg = std::make_shared<LINMessage>();
|
||||
linmsg->network = network;
|
||||
linmsg->description = lin.description;
|
||||
linmsg->protectedID = lin.header[0];
|
||||
linmsg->ID = linmsg->protectedID & 0x3F;
|
||||
linmsg->statusFlags = {
|
||||
static_cast<bool>(lin.linStatus.txChecksumEnhanced),
|
||||
static_cast<bool>(lin.linStatus.txCommander),
|
||||
static_cast<bool>(lin.linStatus.txResponder),
|
||||
static_cast<bool>(lin.linStatus.updateResponderOnce),
|
||||
static_cast<bool>(lin.linStatus.hasUpdatedResponderOnce),
|
||||
static_cast<bool>(lin.linStatus.busRecovered),
|
||||
static_cast<bool>(lin.linStatus.breakOnly)
|
||||
};
|
||||
linmsg->isEnhancedChecksum = linmsg->statusFlags.TxChecksumEnhanced;
|
||||
linmsg->errFlags = {
|
||||
static_cast<bool>(lin.linStatus.breakOnly),
|
||||
static_cast<bool>(lin.status.linJustBreakSync),
|
||||
static_cast<bool>(lin.status.linErrorTXRXMismatch),
|
||||
static_cast<bool>(lin.status.linErrorRXBreakNotZero),
|
||||
static_cast<bool>(lin.status.linErrorRXBreakTooShort),
|
||||
static_cast<bool>(lin.status.linErrorRXSyncNot55),
|
||||
static_cast<bool>(lin.status.linErrorRXDataGreaterEight),
|
||||
static_cast<bool>(lin.status.linSyncFrameError),
|
||||
static_cast<bool>(lin.status.linIDFrameError),
|
||||
static_cast<bool>(lin.status.linSlaveByteError),
|
||||
static_cast<bool>(lin.status.checksumError)
|
||||
};
|
||||
if(lin.length > 1) {
|
||||
auto numHeaderBytes = std::min(lin.length, static_cast<size_t>(3));
|
||||
linmsg->data.insert(linmsg->data.end(), (lin.header + 1), (lin.header + numHeaderBytes));
|
||||
linmsg->data.insert(linmsg->data.end(), lin.data, (lin.data + (lin.length - numHeaderBytes)));
|
||||
linmsg->checksum = linmsg->data.back();
|
||||
linmsg->data.pop_back();
|
||||
}
|
||||
if (linmsg->statusFlags.TxCommander) {
|
||||
if (linmsg->data.size())
|
||||
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
|
||||
else
|
||||
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
|
||||
} else if (linmsg->statusFlags.TxResponder) {
|
||||
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
|
||||
}
|
||||
return linmsg;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
#include <cstring>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
@@ -14,148 +14,49 @@ std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<ui
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto getSampleFromBytes = [](uint8_t bytesPerSample, const uint8_t *bytes) {
|
||||
A2BPCMSample result = 0;
|
||||
|
||||
for(auto i = 0; i < bytesPerSample; i++) {
|
||||
result |= static_cast<uint32_t>(bytes[i]) << (i * 8);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data();
|
||||
|
||||
uint32_t totalPackedLength = static_cast<uint32_t>(bytestream.size()) - static_cast<uint32_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
|
||||
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - static_cast<size_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
|
||||
|
||||
uint8_t bytesPerChannel = data->header.channelSize16 ? 2 : 4;
|
||||
uint8_t numChannels = data->header.channelNum;
|
||||
uint8_t bitDepth = data->header.channelSize16 ? A2BPCM_L16 : A2BPCM_L24;
|
||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(
|
||||
(uint8_t)data->header.channelNum,
|
||||
data->header.channelSize16,
|
||||
totalPackedLength
|
||||
);
|
||||
|
||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(bitDepth, bytesPerChannel, numChannels);
|
||||
msg->channelSize16 = data->header.channelSize16;
|
||||
msg->monitor = data->header.monitor;
|
||||
msg->txmsg = data->header.txmsg;
|
||||
msg->errIndicator = data->header.errIndicator;
|
||||
msg->syncFrame = data->header.syncFrame;
|
||||
msg->rfu2 = data->header.rfu2;
|
||||
|
||||
const uint8_t *bytes = bytestream.data();
|
||||
bytes+=coreMiniMessageHeaderSize;
|
||||
|
||||
uint8_t channel = 0;
|
||||
|
||||
for(uint32_t i = 0; i < totalPackedLength; i += 2 * static_cast<uint32_t>(bytesPerChannel), bytes += 2 * bytesPerChannel, channel = (channel + 1) % numChannels) {
|
||||
|
||||
msg->addSample(
|
||||
getSampleFromBytes(bytesPerChannel, bytes),
|
||||
A2BMessage::A2BDirection::DownStream,
|
||||
channel
|
||||
);
|
||||
|
||||
msg->addSample(
|
||||
getSampleFromBytes(bytesPerChannel, bytes + bytesPerChannel),
|
||||
A2BMessage::A2BDirection::UpStream,
|
||||
channel
|
||||
);
|
||||
|
||||
}
|
||||
msg->setMonitorBit(data->header.monitor);
|
||||
msg->setTxMsgBit(data->header.txmsg);
|
||||
msg->setErrIndicatorBit(data->header.errIndicator);
|
||||
msg->setSyncFrameBit(data->header.syncFrame);
|
||||
msg->setRFU2(data->header.rfu2);
|
||||
msg->setAudioBuffer(bytestream.begin() + coreMiniMessageHeaderSize, 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;
|
||||
|
||||
if(message.getBytesPerSample() != 2 && message.getBytesPerSample() != 4) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t sampleBytes = message.getNumSamples() * static_cast<size_t>(message.getBytesPerSample());
|
||||
size_t totalSize = coreMiniMessageHeaderSize + sampleBytes;
|
||||
size_t sampleBytes = message.getAudioBufferSize();
|
||||
size_t totalSize = a2btxMessageHeaderSize + sampleBytes;
|
||||
|
||||
if(totalSize > a2bMessageMaxLength) {
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bytestream.reserve(totalSize);
|
||||
|
||||
bytestream.push_back(message.getNumChannels());
|
||||
bytestream.push_back(message.channelSize16 ? 1 : 0);
|
||||
bytestream.resize(totalSize, 0);
|
||||
uint32_t offset = 0;
|
||||
|
||||
uint8_t a2b2Bits = 0;
|
||||
|
||||
if(message.monitor) {
|
||||
a2b2Bits = a2b2Bits | 1;
|
||||
}
|
||||
bytestream[offset++] = 0;
|
||||
bytestream[offset++] = 0;
|
||||
bytestream[offset++] = (uint8_t)(sampleBytes & 0xFF);
|
||||
bytestream[offset++] = (uint8_t)((sampleBytes >> 8) & 0xFF);
|
||||
bytestream[offset++] = (uint8_t)((message.description >> 8) & 0xFF);
|
||||
bytestream[offset++] = (uint8_t)(message.description & 0xFF);
|
||||
|
||||
if(message.txmsg) {
|
||||
a2b2Bits = a2b2Bits | (1 << 1);
|
||||
}
|
||||
|
||||
if(message.errIndicator) {
|
||||
a2b2Bits = a2b2Bits | (1 << 2);
|
||||
}
|
||||
|
||||
if(message.syncFrame) {
|
||||
a2b2Bits = a2b2Bits | (1 << 3);
|
||||
}
|
||||
|
||||
bytestream.push_back(a2b2Bits);
|
||||
bytestream.push_back(0);
|
||||
bytestream.push_back(static_cast<uint8_t>(message.rfu2));
|
||||
bytestream.push_back(static_cast<uint8_t>(message.rfu2 >> 8));
|
||||
|
||||
for(size_t i = 0; i < (coreMiniMessageHeaderSize - a2bHeaderSize); i++)
|
||||
bytestream.push_back(0);
|
||||
|
||||
uint8_t numChannels = message.getNumChannels();
|
||||
|
||||
uint8_t channel = 0;
|
||||
uint32_t frame = 0;
|
||||
|
||||
auto writeSample = [&](A2BPCMSample&& sample) {
|
||||
for(uint32_t i = 0; i < static_cast<uint32_t>(message.getBytesPerSample()); i++) {
|
||||
bytestream.push_back(static_cast<uint8_t>((sample >> (i*8))));
|
||||
}
|
||||
};
|
||||
|
||||
while(true) {
|
||||
auto dsSample = message.getSample(A2BMessage::A2BDirection::DownStream, channel, frame);
|
||||
auto usSample = message.getSample(A2BMessage::A2BDirection::UpStream, channel, frame);
|
||||
|
||||
|
||||
// Check if getSample failed for both downstream and upstream
|
||||
if(!dsSample && !usSample) {
|
||||
|
||||
if(channel != 0) {
|
||||
//Incomplete frame, the frame we are currently on does not contain all channel samples
|
||||
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
// Since no samples have been written for the current frame yet and there are no more
|
||||
// samples in both upstream and downstream, we can break and end parsing.
|
||||
break;
|
||||
}
|
||||
// Since the first case failed, at least one of the streams still has samples.
|
||||
// This case checks to see if the other stream does not have a sample.
|
||||
else if(!dsSample || !usSample) {
|
||||
// Report an error since we must have a one to one correspondence between upstream
|
||||
// and downstream.
|
||||
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
writeSample(std::move(dsSample.value()));
|
||||
writeSample(std::move(usSample.value()));
|
||||
|
||||
channel = (channel + 1) % numChannels;
|
||||
if(channel == 0)
|
||||
frame++;
|
||||
}
|
||||
std::copy(message.data.begin(), message.data.end(), bytestream.begin() + offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
#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
|
||||
@@ -0,0 +1,125 @@
|
||||
#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
|
||||
@@ -0,0 +1,79 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#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());
|
||||
// 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,5 +1,4 @@
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace icsneo;
|
||||
@@ -27,7 +26,8 @@ std::vector<uint8_t>& Packetizer::packetWrap(std::vector<uint8_t>& data, bool sh
|
||||
|
||||
bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
bool haveEnoughData = true;
|
||||
bytes.insert(bytes.end(), inputBytes.begin(), inputBytes.end());
|
||||
|
||||
bytes.Copy(inputBytes);
|
||||
|
||||
while(haveEnoughData) {
|
||||
switch(state) {
|
||||
@@ -49,44 +49,97 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
haveEnoughData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
packetLength = bytes[1] >> 4 & 0xF;
|
||||
packet.network = Network(bytes[1] & 0xF); // Lower nibble of the second byte is the network ID
|
||||
|
||||
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
|
||||
if(packetLength == 0) {
|
||||
state = ReadState::ParseLongStylePacketHeader;
|
||||
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
|
||||
break;
|
||||
} else if(packetLength == 0xA && packet.network == Network::NetID::DiskData) {
|
||||
state = ReadState::ParseDiskDataHeader;
|
||||
break;
|
||||
}
|
||||
|
||||
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]); // Long packets have their netid stored as little endian on bytes 5 and 6
|
||||
currentIndex += 4;
|
||||
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.
|
||||
|
||||
/* Long packets can't have a length less than 6, because that would indicate a negative payload size.
|
||||
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
|
||||
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
|
||||
* payload, and not the header or checksum.
|
||||
*/
|
||||
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
|
||||
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
|
||||
* payload, and not the header or checksum.
|
||||
*/
|
||||
if(packetLength < 6 || packetLength > 4000) {
|
||||
bytes.pop_front();
|
||||
EventManager::GetInstance().add(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||
state = ReadState::SearchForHeader;
|
||||
} else {
|
||||
state = ReadState::GetData;
|
||||
break;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -98,23 +151,25 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
packet.data.clear();
|
||||
if(packetLength > 0)
|
||||
packet.data.resize(packetLength - headerSize);
|
||||
|
||||
auto i = 0;
|
||||
while(currentIndex < packetLength)
|
||||
packet.data[i++] = bytes[currentIndex++];
|
||||
|
||||
bytes.CopyTo(packet.data.data(), currentIndex, (packetLength - currentIndex));
|
||||
currentIndex = packetLength;
|
||||
|
||||
if(disableChecksum || !checksum || bytes[currentIndex] == ICSChecksum(packet.data)) {
|
||||
// Got a good packet
|
||||
gotGoodPackets = true;
|
||||
processedPackets.push_back(std::make_shared<Packet>(packet));
|
||||
for (auto a = 0; a < packetLength; a++)
|
||||
bytes.Erase_front(packetLength);
|
||||
|
||||
if(packet.network == Network::NetID::DiskData && (packetLength - headerSize) % 2 == 0) {
|
||||
bytes.pop_front();
|
||||
}
|
||||
} else {
|
||||
if(gotGoodPackets) // Don't complain unless we've already gotten a good packet, in case we started in the middle of a stream
|
||||
report(APIEvent::Type::PacketChecksumError, APIEvent::Severity::Error);
|
||||
bytes.pop_front(); // Drop the first byte so it doesn't get picked up again
|
||||
}
|
||||
|
||||
|
||||
// Reset for the next packet
|
||||
currentIndex = 0;
|
||||
state = ReadState::SearchForHeader;
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "icsneo/communication/sdio.h"
|
||||
#include "icsneo/platform/sharedsemaphore.h"
|
||||
#include "icsneo/platform/sharedmemory.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/socket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
void SDIO::Find(std::vector<FoundDevice>& found) {
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::DEVICE_FINDER_FIND_ALL))
|
||||
return;
|
||||
uint16_t count;
|
||||
if(!socket.readTyped(count))
|
||||
return;
|
||||
|
||||
static constexpr auto serialSize = sizeof(FoundDevice::serial);
|
||||
std::vector<std::array<char, sizeof(FoundDevice::serial)>> serials(count);
|
||||
if(!socket.read(serials.data(), serials.size() * serialSize))
|
||||
return;
|
||||
|
||||
for(const auto& serial : serials) {
|
||||
auto& foundDevice = found.emplace_back();
|
||||
for(std::size_t i = 0; i < serialSize - 1 /* omit '\0' */; i++)
|
||||
foundDevice.serial[i] = static_cast<char>(std::toupper(serial[i]));
|
||||
foundDevice.makeDriver = [](const device_eventhandler_t& r, neodevice_t& d) {
|
||||
return std::unique_ptr<SDIO>(new SDIO(r, d));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool SDIO::open() {
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::SDIO_OPEN) && socket.writeString(device.device->getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
|
||||
{
|
||||
std::string mailboxName;
|
||||
if(!socket.readString(mailboxName))
|
||||
return false;
|
||||
if(!inboundIO.open(mailboxName))
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::string mailboxName;
|
||||
if(!socket.readString(mailboxName))
|
||||
return false;
|
||||
if(!outboundIO.open(mailboxName))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
readThread = std::thread(&SDIO::readTask, this);
|
||||
writeThread = std::thread(&SDIO::writeTask, this);
|
||||
|
||||
deviceOpen = true;
|
||||
|
||||
return deviceOpen;
|
||||
}
|
||||
|
||||
bool SDIO::close() {
|
||||
if(!isOpen() && !isDisconnected()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
closing = true;
|
||||
|
||||
// wait for the reader/writer threads to close
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
|
||||
// unblocks the reader/writer threads
|
||||
if(!inboundIO.close())
|
||||
return false;
|
||||
if(!outboundIO.close())
|
||||
return false;
|
||||
|
||||
if(readThread.joinable())
|
||||
readThread.join();
|
||||
|
||||
if(writeThread.joinable())
|
||||
writeThread.join();
|
||||
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::SDIO_CLOSE))
|
||||
return false;
|
||||
if(!socket.writeString(device.device->getSerial()))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t flush;
|
||||
WriteOperation flushop;
|
||||
while(readQueue.try_dequeue(flush)) {}
|
||||
while(writeQueue.try_dequeue(flushop)) {}
|
||||
|
||||
closing = false;
|
||||
disconnected = false;
|
||||
deviceOpen = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDIO::isOpen() {
|
||||
return deviceOpen;
|
||||
}
|
||||
|
||||
void SDIO::readTask() {
|
||||
uint8_t data[MAX_DATA_SIZE];
|
||||
uint16_t messageLength;
|
||||
while(!closing) {
|
||||
if(!inboundIO.read(data, messageLength, std::chrono::milliseconds(100))) {
|
||||
if(!inboundIO)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(messageLength > 0) {
|
||||
if(messageLength > MAX_DATA_SIZE) { // split message
|
||||
std::vector<uint8_t> reassembled(messageLength);
|
||||
std::memcpy(reassembled.data(), data, MAX_DATA_SIZE);
|
||||
auto offset = reassembled.data() + MAX_DATA_SIZE;
|
||||
for(auto remaining = messageLength - MAX_DATA_SIZE; remaining > 0; remaining -= messageLength) {
|
||||
if(!inboundIO.read(offset, messageLength, std::chrono::milliseconds(10))) {
|
||||
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
offset += messageLength;
|
||||
}
|
||||
readQueue.enqueue_bulk(reassembled.data(), reassembled.size());
|
||||
} else {
|
||||
readQueue.enqueue_bulk(data, messageLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDIO::writeTask() {
|
||||
WriteOperation writeOp;
|
||||
while(!closing && !isDisconnected()) {
|
||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||
continue;
|
||||
|
||||
const auto dataSize = static_cast<LengthFieldType>(writeOp.bytes.size());
|
||||
|
||||
const auto tryWrite = [&](const void* input, LengthFieldType length) -> bool {
|
||||
for(int i = 0; i < 50; ++i) { // try to write for 5s, making sure we can close if need be
|
||||
if(outboundIO.write(input, length, std::chrono::milliseconds(100)))
|
||||
return true;
|
||||
if(!outboundIO)
|
||||
return false;
|
||||
}
|
||||
disconnected = true;
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
return false;
|
||||
};
|
||||
|
||||
if(!tryWrite(writeOp.bytes.data(), dataSize))
|
||||
continue;
|
||||
|
||||
if(writeOp.bytes.size() > MAX_DATA_SIZE) {
|
||||
auto offset = writeOp.bytes.data() + MAX_DATA_SIZE;
|
||||
for(LengthFieldType remaining = dataSize - MAX_DATA_SIZE; remaining > 0; ) {
|
||||
const auto toWrite = std::min(MAX_DATA_SIZE, remaining);
|
||||
if(!tryWrite(offset, toWrite))
|
||||
break;
|
||||
remaining -= toWrite;
|
||||
offset += toWrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
#include "icsneo/communication/socket.h"
|
||||
#include "icsneo/api/event.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
bool SocketBase::open() {
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if(::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if((sockFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
#ifdef _WIN32
|
||||
::WSACleanup();
|
||||
#endif
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
sockIsOpen = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::close() {
|
||||
#ifdef _WIN32
|
||||
if(::closesocket(sockFileDescriptor) < 0) {
|
||||
// should probably check for WSAEWOULDBLOCK as ::closesocket must be repeated to close in that case
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
::WSACleanup();
|
||||
#else
|
||||
// ignore ENOTCONN from ::shutdown as the peer may have already forcibly closed its socket (e.g. a crash)
|
||||
if( ((::shutdown(sockFileDescriptor, SHUT_RDWR) < 0) && (ENOTCONN != errno)) ||
|
||||
((::close(sockFileDescriptor) < 0) && (EBADF == errno)) )
|
||||
{
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
sockIsOpen = false;
|
||||
sockIsConnected = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::connect() {
|
||||
sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if( (!isOpen() && !open()) ||
|
||||
(::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
|
||||
(::connect(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToConnect, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD tv = 5000u; // 5 second receive timeout but in windows
|
||||
#else
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 5u; // 5 second receive timeout
|
||||
tv.tv_usec = 0;
|
||||
setIgnoreSIGPIPE();
|
||||
#endif
|
||||
|
||||
// Set the 5 second timeout from above in the socket options
|
||||
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
|
||||
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
|
||||
|
||||
sockIsConnected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::isOpen() {
|
||||
return sockIsOpen;
|
||||
}
|
||||
|
||||
bool SocketBase::isConnected() {
|
||||
return sockIsConnected;
|
||||
}
|
||||
|
||||
bool SocketBase::read(void* output, std::size_t length) {
|
||||
if(!(isOpen() && isConnected()))
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
return ::recv(sockFileDescriptor, (char*)output, (int)length, 0) > 0;
|
||||
#else
|
||||
if(::read(sockFileDescriptor, output, length) <= 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToRead, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SocketBase::write(const void* input, std::size_t length) {
|
||||
if(!(isOpen() && isConnected()))
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(::send(sockFileDescriptor, (char*)input, (int)length, 0) < 0) {
|
||||
switch(WSAGetLastError()) {
|
||||
case WSAETIMEDOUT:
|
||||
case WSAENOTCONN:
|
||||
case WSAESHUTDOWN:
|
||||
case WSAECONNRESET:
|
||||
case WSAECONNABORTED:
|
||||
{
|
||||
sockIsOpen = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if(::write(sockFileDescriptor, input, length) < 0) {
|
||||
switch(errno) {
|
||||
case EPIPE:
|
||||
case ETIMEDOUT:
|
||||
{
|
||||
sockIsOpen = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::readString(std::string& str) {
|
||||
size_t length;
|
||||
if(!read(&length, sizeof(length)))
|
||||
return false;
|
||||
str.resize(length);
|
||||
if(!read(str.data(), length))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::writeString(const std::string& str) {
|
||||
size_t length = str.size();
|
||||
if(!write(&length, sizeof(length)))
|
||||
return false;
|
||||
if(!write(str.data(), length))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ActiveSocket::ActiveSocket(SocketFileDescriptor sockFD) {
|
||||
sockFileDescriptor = sockFD;
|
||||
sockIsOpen = true;
|
||||
sockIsConnected = true;
|
||||
}
|
||||
|
||||
ActiveSocket::ActiveSocket(Protocol protocol, uint16_t port) {
|
||||
this->protocol = protocol;
|
||||
this->port = port;
|
||||
}
|
||||
|
||||
ActiveSocket::~ActiveSocket() {
|
||||
if(isOpen())
|
||||
close();
|
||||
}
|
||||
|
||||
LockedSocket::LockedSocket(SocketBase& base, std::unique_lock<std::mutex>&& l) :
|
||||
SocketBase(base), lock(std::move(l)) {
|
||||
}
|
||||
|
||||
LockedSocket lockSocket() {
|
||||
static ActiveSocket socket(SocketBase::Protocol::TCP, RPC_PORT);
|
||||
if(!socket.isOpen())
|
||||
socket.open();
|
||||
if(!socket.isConnected())
|
||||
socket.connect();
|
||||
static std::mutex lock;
|
||||
return LockedSocket(socket, std::unique_lock<std::mutex>(lock));
|
||||
}
|
||||
|
||||
void SocketBase::setIgnoreSIGPIPE() {
|
||||
#ifndef _WIN32
|
||||
struct sigaction sa{};
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
::sigaction(SIGPIPE, &sa, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
Acceptor::Acceptor(Protocol protocol, uint16_t port)
|
||||
: ActiveSocket(protocol, port) {
|
||||
}
|
||||
|
||||
bool Acceptor::initialize() {
|
||||
if(open() && bind() && listen()) {
|
||||
isValid = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<ActiveSocket> Acceptor::accept()
|
||||
{
|
||||
if(!isValid)
|
||||
return nullptr;
|
||||
const SocketFileDescriptor acceptFd = ::accept(sockFileDescriptor, (sockaddr*)NULL, NULL);
|
||||
if(acceptFd < 0)
|
||||
return nullptr;
|
||||
return std::make_shared<ActiveSocket>(acceptFd);
|
||||
}
|
||||
|
||||
bool Acceptor::bind()
|
||||
{
|
||||
sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(port);
|
||||
if( (::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
|
||||
(::bind(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToBind, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Acceptor::listen()
|
||||
{
|
||||
if(::listen(sockFileDescriptor, UINT8_MAX) < 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToListen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} //namespace icsneo
|
||||
+494
-154
@@ -1,24 +1,28 @@
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/message/callback/messagecallback.h"
|
||||
#include <sstream>
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
#include "icsneo/communication/message/filter/main51messagefilter.h"
|
||||
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/device/extensions/deviceextension.h"
|
||||
#include "icsneo/disk/fat.h"
|
||||
#include "icsneo/communication/packet/wivicommandpacket.h"
|
||||
#include "icsneo/communication/message/wiviresponsemessage.h"
|
||||
#include "icsneo/communication/message/scriptstatusmessage.h"
|
||||
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4996) // STL time functions
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
struct RTCCTIME {
|
||||
uint8_t FracSec;// --- fractions of seconds (00-99). Note that you can write only 0x00 here!
|
||||
uint8_t Sec;// --- Seconds (00-59)
|
||||
uint8_t Min;// --- (00-59)
|
||||
uint8_t Hour;// --- (00-23)
|
||||
uint8_t DOW;// --- (01-07)
|
||||
uint8_t Day;// --- (01-31)
|
||||
uint8_t Month;// --- (01-12)
|
||||
uint8_t Year;// --- (00-99)
|
||||
};
|
||||
|
||||
static const uint8_t fromBase36Table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12,
|
||||
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15,
|
||||
@@ -182,15 +186,6 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_OPEN) && socket.writeString(getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
APIEvent::Type attemptErr = attemptToBeginCommunication();
|
||||
if(attemptErr != APIEvent::Type::NoErrorFound) {
|
||||
// We could not communicate with the device, let's see if an extension can
|
||||
@@ -212,7 +207,6 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool block = false;
|
||||
forEachExtension([&block, &flags, &handler](const std::shared_ptr<DeviceExtension>& ext) {
|
||||
@@ -224,6 +218,13 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
|
||||
if(block) // Extensions say no
|
||||
return false;
|
||||
|
||||
// Get component versions *after* the extension "onDeviceOpen" hooks (e.g. device reflashes)
|
||||
|
||||
if (auto compVersions = com->getComponentVersionsSync())
|
||||
componentVersions = std::move(*compVersions);
|
||||
else
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::EventWarning);
|
||||
|
||||
if(!settings->disabled) {
|
||||
// Since we will not fail the open if a settings read fails,
|
||||
// downgrade any errors to warnings. Otherwise the error will
|
||||
@@ -242,61 +243,71 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
|
||||
handleInternalMessage(message);
|
||||
}));
|
||||
|
||||
if(com->driver->enableHeartbeat()) {
|
||||
heartbeatThread = std::thread([this]() {
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
heartbeatThread = std::thread([this]() {
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
|
||||
MessageFilter filter;
|
||||
filter.includeInternalInAny = true;
|
||||
std::atomic<bool> receivedMessage{false};
|
||||
auto messageReceivedCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>(filter, [&receivedMessage](std::shared_ptr<Message> message) {
|
||||
MessageFilter filter;
|
||||
filter.includeInternalInAny = true;
|
||||
|
||||
std::condition_variable heartbeatCV;
|
||||
std::mutex receivedMessageMutex;
|
||||
bool receivedMessage = false;
|
||||
auto messageReceivedCallbackID = com->addMessageCallback(std::make_shared<MessageCallback>(filter, [&](std::shared_ptr<Message> message) {
|
||||
{
|
||||
std::scoped_lock<std::mutex> lk(receivedMessageMutex);
|
||||
receivedMessage = true;
|
||||
}));
|
||||
|
||||
// Give the device time to get situated
|
||||
auto i = 150;
|
||||
while(!stopHeartbeatThread && i != 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
i--;
|
||||
}
|
||||
heartbeatCV.notify_all();
|
||||
}));
|
||||
|
||||
while(!stopHeartbeatThread) {
|
||||
// Wait for 110ms for a possible heartbeat
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(110));
|
||||
if(receivedMessage) {
|
||||
// Give the device time to get situated
|
||||
auto i = 150;
|
||||
while(!stopHeartbeatThread && i != 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
i--;
|
||||
}
|
||||
|
||||
while(!stopHeartbeatThread) {
|
||||
// Wait for 110ms for a possible heartbeat
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(110));
|
||||
std::unique_lock<std::mutex> recvLk(receivedMessageMutex);
|
||||
if(receivedMessage) {
|
||||
receivedMessage = false;
|
||||
} else {
|
||||
// Some communication, such as the bootloader and extractor interfaces, must
|
||||
// redirect the input stream from the device as it will no longer be in the
|
||||
// packet format we expect here. As a result, status updates will not reach
|
||||
// us here and suppressDisconnects() must be used. We don't want to request
|
||||
// a status and then redirect the stream, as we'll then be polluting an
|
||||
// otherwise quiet stream. This lock makes sure suppressDisconnects() will
|
||||
// block until we've either gotten our status update or disconnected from
|
||||
// the device.
|
||||
std::lock_guard<std::mutex> lk(heartbeatMutex);
|
||||
if(heartbeatSuppressed())
|
||||
continue;
|
||||
|
||||
// No heartbeat received, request a status
|
||||
com->sendCommand(Command::RequestStatusUpdate);
|
||||
|
||||
// Check if we got a message, and if not, if settings are being applied
|
||||
if(heartbeatCV.wait_for(recvLk, std::chrono::milliseconds(3500), [&](){ return receivedMessage; })) {
|
||||
receivedMessage = false;
|
||||
} else {
|
||||
// Some communication, such as the bootloader and extractor interfaces, must
|
||||
// redirect the input stream from the device as it will no longer be in the
|
||||
// packet format we expect here. As a result, status updates will not reach
|
||||
// us here and suppressDisconnects() must be used. We don't want to request
|
||||
// a status and then redirect the stream, as we'll then be polluting an
|
||||
// otherwise quiet stream. This lock makes sure suppressDisconnects() will
|
||||
// block until we've either gotten our status update or disconnected from
|
||||
// the device.
|
||||
std::lock_guard<std::mutex> lk(heartbeatMutex);
|
||||
if(heartbeatSuppressed())
|
||||
continue;
|
||||
|
||||
// No heartbeat received, request a status
|
||||
com->sendCommand(Command::RequestStatusUpdate);
|
||||
// The response should come back quickly if the com is quiet
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
// Check if we got a message, and if not, if settings are being applied
|
||||
if(receivedMessage) {
|
||||
receivedMessage = false;
|
||||
} else {
|
||||
if(!stopHeartbeatThread && !isDisconnected())
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
break;
|
||||
if(!stopHeartbeatThread && !isDisconnected()) {
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
com->driver->close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
com->removeMessageCallback(messageReceivedCallbackID);
|
||||
});
|
||||
|
||||
if(supportsLiveData())
|
||||
clearAllLiveData();
|
||||
|
||||
com->removeMessageCallback(messageReceivedCallbackID);
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -310,6 +321,10 @@ APIEvent::Type Device::attemptToBeginCommunication() {
|
||||
return getCommunicationNotEstablishedError();
|
||||
}
|
||||
|
||||
if(!com->sendCommand(Command::EnableNetworkCommunication, false))
|
||||
return getCommunicationNotEstablishedError();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
auto serial = com->getSerialNumberSync();
|
||||
int i = 0;
|
||||
while(!serial) {
|
||||
@@ -354,36 +369,19 @@ bool Device::close() {
|
||||
stopHeartbeatThread = false;
|
||||
|
||||
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onDeviceClose(); return true; });
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_CLOSE) && socket.writeString(getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return com->close();
|
||||
}
|
||||
|
||||
bool Device::goOnline() {
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_GO_ONLINE) && socket.writeString(getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
|
||||
if(!com->sendCommand(Command::EnableNetworkCommunication, true))
|
||||
return false;
|
||||
|
||||
auto startTime = std::chrono::system_clock::now();
|
||||
|
||||
ledState = LEDState::Online;
|
||||
|
||||
updateLEDState();
|
||||
|
||||
std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::Reset_Status);
|
||||
filter->includeInternalInAny = true;
|
||||
|
||||
@@ -403,13 +401,9 @@ bool Device::goOnline() {
|
||||
if(failOut)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
online = true;
|
||||
|
||||
ledState = LEDState::Online;
|
||||
|
||||
updateLEDState();
|
||||
|
||||
forEachExtension([](const std::shared_ptr<DeviceExtension>& ext) { ext->onGoOnline(); return true; });
|
||||
return true;
|
||||
}
|
||||
@@ -422,21 +416,15 @@ bool Device::goOffline() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_GO_OFFLINE) && socket.writeString(getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
|
||||
if(!com->sendCommand(Command::EnableNetworkCommunication, false))
|
||||
return false;
|
||||
|
||||
auto startTime = std::chrono::system_clock::now();
|
||||
|
||||
ledState = (latestResetStatus && latestResetStatus->cmRunning) ? LEDState::CoreMiniRunning : LEDState::Offline;
|
||||
|
||||
updateLEDState();
|
||||
|
||||
std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::Reset_Status);
|
||||
filter->includeInternalInAny = true;
|
||||
|
||||
@@ -450,11 +438,6 @@ bool Device::goOffline() {
|
||||
|
||||
com->waitForMessageSync(filter, std::chrono::milliseconds(100));
|
||||
}
|
||||
#endif
|
||||
|
||||
ledState = (latestResetStatus && latestResetStatus->cmRunning) ? LEDState::CoreMiniRunning : LEDState::Offline;
|
||||
|
||||
updateLEDState();
|
||||
|
||||
online = false;
|
||||
|
||||
@@ -469,8 +452,6 @@ int8_t Device::prepareScriptLoad() {
|
||||
|
||||
static std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::CoreMiniPreLoad);
|
||||
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
|
||||
if(!com->sendCommand(Command::CoreMiniPreload))
|
||||
return false;
|
||||
|
||||
@@ -491,17 +472,15 @@ int8_t Device::prepareScriptLoad() {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool Device::startScript()
|
||||
bool Device::startScript(Disk::MemoryType memType)
|
||||
{
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
|
||||
uint8_t LocationSdCard = 1; //Only support starting a coremini in an SDCard
|
||||
auto generic = com->sendCommand(Command::LoadCoreMini, LocationSdCard);
|
||||
uint8_t location = static_cast<uint8_t>(memType);
|
||||
auto generic = com->sendCommand(Command::LoadCoreMini, location);
|
||||
|
||||
if(!generic)
|
||||
{
|
||||
@@ -519,8 +498,6 @@ bool Device::stopScript()
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
|
||||
auto generic = com->sendCommand(Command::ClearCoreMini);
|
||||
|
||||
if(!generic)
|
||||
@@ -532,6 +509,68 @@ bool Device::stopScript()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::uploadCoremini(std::unique_ptr<std::istream>&& stream, Disk::MemoryType memType) {
|
||||
|
||||
auto startAddress = getCoreminiStartAddress(memType);
|
||||
if(!startAddress) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto connected = isLogicalDiskConnected();
|
||||
|
||||
if(!connected) {
|
||||
return false; // Already added an API error
|
||||
}
|
||||
|
||||
if(!(*connected)) {
|
||||
report(APIEvent::Type::DiskNotConnected, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!stopScript()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!stream || stream->bad()) {
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char> bin(std::istreambuf_iterator<char>(*stream), {}); // Read the whole stream
|
||||
|
||||
if(bin.size() < 4) {
|
||||
report(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t scriptVersion = *(uint16_t*)(&bin[2]); // Third and fourth byte are version number stored in little endian
|
||||
|
||||
auto scriptStatus = getScriptStatus();
|
||||
|
||||
if(!scriptStatus) {
|
||||
return false; // Already added an API error
|
||||
}
|
||||
|
||||
if(scriptStatus->coreminiVersion != scriptVersion) {
|
||||
// Version on device and script are not the same
|
||||
report(APIEvent::Type::CoreminiUploadVersionMismatch, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto numWritten = writeLogicalDisk(*startAddress, (uint8_t*)bin.data(), static_cast<uint64_t>(bin.size()), std::chrono::milliseconds(2000), memType);
|
||||
|
||||
if(!numWritten) {
|
||||
return false; // Already added an API error
|
||||
}
|
||||
|
||||
if(*numWritten == 0) {
|
||||
report(APIEvent::Type::FailedToWrite, APIEvent::Severity::Error);
|
||||
return false; // Failed to write
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::clearScript()
|
||||
{
|
||||
if(!stopScript())
|
||||
@@ -610,7 +649,8 @@ Network Device::getNetworkByNumber(Network::Type type, size_t index) const {
|
||||
return Network::NetID::Invalid;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> Device::readLogicalDisk(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
|
||||
std::optional<uint64_t> Device::readLogicalDisk(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, Disk::MemoryType memType) {
|
||||
if(!into || timeout <= std::chrono::milliseconds(0)) {
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
@@ -621,13 +661,13 @@ std::optional<uint64_t> Device::readLogicalDisk(uint64_t pos, uint8_t* into, uin
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lk(diskLock);
|
||||
std::lock_guard<std::mutex> lk(diskMutex);
|
||||
|
||||
if(diskReadDriver->getAccess() == Disk::Access::EntireCard && diskWriteDriver->getAccess() == Disk::Access::VSA) {
|
||||
// We have mismatched drivers, we need to add an offset to the diskReadDriver
|
||||
const auto offset = Disk::FindVSAInFAT([this, &timeout](uint64_t pos, uint8_t *into, uint64_t amount) {
|
||||
const auto offset = Disk::FindVSAInFAT([this, &timeout, &memType](uint64_t pos, uint8_t *into, uint64_t amount) {
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
auto ret = diskReadDriver->readLogicalDisk(*com, report, pos, into, amount, timeout);
|
||||
auto ret = diskReadDriver->readLogicalDisk(*com, report, pos, into, amount, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
|
||||
return ret;
|
||||
});
|
||||
@@ -636,13 +676,10 @@ std::optional<uint64_t> Device::readLogicalDisk(uint64_t pos, uint8_t* into, uin
|
||||
diskReadDriver->setVSAOffset(*offset);
|
||||
}
|
||||
|
||||
// This is needed for certain read drivers which take over the communication stream
|
||||
const auto lifetime = suppressDisconnects();
|
||||
|
||||
return diskReadDriver->readLogicalDisk(*com, report, pos, into, amount, timeout);
|
||||
return diskReadDriver->readLogicalDisk(*com, report, pos, into, amount, timeout, memType);
|
||||
}
|
||||
|
||||
std::optional<uint64_t> Device::writeLogicalDisk(uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
std::optional<uint64_t> Device::writeLogicalDisk(uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, Disk::MemoryType memType) {
|
||||
if(!from || timeout <= std::chrono::milliseconds(0)) {
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
@@ -653,8 +690,7 @@ std::optional<uint64_t> Device::writeLogicalDisk(uint64_t pos, const uint8_t* fr
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lk(diskLock);
|
||||
return diskWriteDriver->writeLogicalDisk(*com, report, *diskReadDriver, pos, from, amount, timeout);
|
||||
return diskWriteDriver->writeLogicalDisk(*com, report, *diskReadDriver, pos, from, amount, timeout, memType);
|
||||
}
|
||||
|
||||
std::optional<bool> Device::isLogicalDiskConnected() {
|
||||
@@ -663,9 +699,6 @@ std::optional<bool> Device::isLogicalDiskConnected() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// This doesn't *really* make sense here but because the disk read redirects the parser until it is done, we'll lock this
|
||||
// just to avoid the timeout.
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
const auto info = com->getLogicalDiskInfoSync();
|
||||
if (!info) {
|
||||
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
|
||||
@@ -681,16 +714,18 @@ std::optional<uint64_t> Device::getLogicalDiskSize() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// This doesn't *really* make sense here but because the disk read redirects the parser until it is done, we'll lock this
|
||||
// just to avoid the timeout.
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
const auto info = com->getLogicalDiskInfoSync();
|
||||
if (!info) {
|
||||
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return info->getReportedSize();
|
||||
const auto reportedSize = info->getReportedSize();
|
||||
|
||||
if (diskReadDriver->getAccess() == Disk::Access::VSA)
|
||||
return reportedSize - diskReadDriver->getVSAOffset();
|
||||
|
||||
return reportedSize;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> Device::getVSAOffsetInLogicalDisk() {
|
||||
@@ -699,8 +734,6 @@ std::optional<uint64_t> Device::getVSAOffsetInLogicalDisk() {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lk(diskLock);
|
||||
|
||||
if (diskReadDriver->getAccess() == Disk::Access::VSA || diskReadDriver->getAccess() == Disk::Access::None)
|
||||
return 0ull;
|
||||
|
||||
@@ -925,9 +958,6 @@ std::optional<double> Device::getAnalogIO(IO type, size_t number /* = 1 */) {
|
||||
void Device::wiviThreadBody() {
|
||||
std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::WiVICommandResponse);
|
||||
std::unique_lock<std::mutex> lk(wiviMutex);
|
||||
// Disk access commands can
|
||||
std::unique_lock<std::mutex> dl(diskLock);
|
||||
dl.unlock();
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
|
||||
bool first = true;
|
||||
@@ -938,11 +968,9 @@ void Device::wiviThreadBody() {
|
||||
stopWiVIcv.wait_for(lk, std::chrono::seconds(3));
|
||||
|
||||
// Use the command GetAll to get a WiVI::Info structure from the device
|
||||
dl.lock();
|
||||
const auto generic = com->waitForMessageSync([this]() {
|
||||
return com->sendCommand(Command::WiVICommand, WiVI::CommandPacket::GetAll::Encode());
|
||||
}, filter);
|
||||
dl.unlock();
|
||||
}, filter, std::chrono::milliseconds(1000));
|
||||
|
||||
if(!generic || generic->type != Message::Type::WiVICommandResponse) {
|
||||
report(APIEvent::Type::WiVIStackRefreshFailed, APIEvent::Severity::Error);
|
||||
@@ -969,9 +997,8 @@ void Device::wiviThreadBody() {
|
||||
|
||||
if(processCaptures) {
|
||||
std::vector<uint8_t> clearMasks;
|
||||
size_t i = 0;
|
||||
for(const auto& capture : resp->info->captures) {
|
||||
i++;
|
||||
for (size_t i = 0; i < resp->info->captures.size(); i++) {
|
||||
const auto capture = resp->info->captures.at(i);
|
||||
|
||||
if(capture.flags.uploadOverflow)
|
||||
report(APIEvent::Type::WiVIUploadStackOverflow, APIEvent::Severity::Error);
|
||||
@@ -993,12 +1020,24 @@ void Device::wiviThreadBody() {
|
||||
clearMasks.resize(resp->info->captures.size());
|
||||
clearMasks[i] |= (1 << j);
|
||||
|
||||
WiVIUpload wiviUpload {};
|
||||
wiviUpload.captureIndex = capture.captureBlockIndex;
|
||||
wiviUpload.cellular = capture.flags.uploadOverCellular;
|
||||
wiviUpload.wifi = capture.flags.uploadOverWiFi;
|
||||
wiviUpload.isPrePost = capture.flags.isPrePost;
|
||||
wiviUpload.isPreTime = capture.flags.isPreTime;
|
||||
wiviUpload.preTriggerSize = capture.preTriggerSize;
|
||||
wiviUpload.priority = capture.flags.uploadPriority;
|
||||
wiviUpload.startSector = upload.startSector;
|
||||
wiviUpload.endSector = upload.endSector;
|
||||
|
||||
// Notify the client
|
||||
for(const auto& cb : newCaptureCallbacks) {
|
||||
if(cb) {
|
||||
|
||||
lk.unlock();
|
||||
try {
|
||||
cb(upload.startSector, upload.endSector);
|
||||
cb(wiviUpload);
|
||||
} catch(...) {
|
||||
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
|
||||
}
|
||||
@@ -1009,16 +1048,16 @@ void Device::wiviThreadBody() {
|
||||
}
|
||||
|
||||
if(!clearMasks.empty()) {
|
||||
dl.lock();
|
||||
const auto clearMasksGenericResp = com->waitForMessageSync([this, &clearMasks]() {
|
||||
return com->sendCommand(Command::WiVICommand, WiVI::CommandPacket::ClearUploads::Encode(clearMasks));
|
||||
}, filter);
|
||||
dl.unlock();
|
||||
|
||||
if(!clearMasksGenericResp
|
||||
|| clearMasksGenericResp->type != Message::Type::WiVICommandResponse
|
||||
|| !std::static_pointer_cast<WiVI::ResponseMessage>(clearMasksGenericResp)->success)
|
||||
{
|
||||
report(APIEvent::Type::WiVIStackRefreshFailed, APIEvent::Severity::Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1154,7 +1193,6 @@ std::optional<bool> Device::isSleepRequested() const {
|
||||
static std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::WiVICommandResponse);
|
||||
// Hold this lock so the WiVI stack doesn't issue a WiVICommand at the same time as us
|
||||
std::lock_guard<std::mutex> lk(wiviMutex);
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
const auto generic = com->waitForMessageSync([this]() {
|
||||
// VSSAL sets bit0 to indicate that it's waiting to sleep, then
|
||||
// it waits for Wireless neoVI to acknowledge by clearing it.
|
||||
@@ -1192,7 +1230,6 @@ bool Device::allowSleep(bool remoteWakeup) {
|
||||
static std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::WiVICommandResponse);
|
||||
// Hold this lock so the WiVI stack doesn't issue a WiVICommand at the same time as us
|
||||
std::lock_guard<std::mutex> lk(wiviMutex);
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
const auto generic = com->waitForMessageSync([this, remoteWakeup]() {
|
||||
// VSSAL sets bit0 to indicate that it's waiting to sleep, then
|
||||
// it waits for Wireless neoVI to acknowledge by clearing it.
|
||||
@@ -1365,7 +1402,6 @@ std::shared_ptr<ScriptStatusMessage> Device::getScriptStatus() const
|
||||
{
|
||||
static std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::ScriptStatus);
|
||||
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
const auto generic = com->waitForMessageSync([this]() {
|
||||
return com->sendCommand(Command::ScriptStatus);
|
||||
}, filter, std::chrono::milliseconds(3000));
|
||||
@@ -1626,7 +1662,6 @@ std::optional<bool> Device::SetCollectionUploaded(uint32_t collectionEntryByteAd
|
||||
(uint8_t)((collectionEntryByteAddress >> 16) & 0xFF),
|
||||
(uint8_t)((collectionEntryByteAddress >> 24) & 0xFF)});
|
||||
|
||||
std::lock_guard<std::mutex> lg(diskLock);
|
||||
std::shared_ptr<Message> response = com->waitForMessageSync(
|
||||
[this, args](){ return com->sendCommand(ExtendedCommand::SetUploadedFlag, args); },
|
||||
std::make_shared<MessageFilter>(Message::Type::ExtendedResponse), timeout);
|
||||
@@ -1651,3 +1686,308 @@ std::optional<bool> Device::SetCollectionUploaded(uint32_t collectionEntryByteAd
|
||||
// Valid device with a properly formed respose, return success
|
||||
return std::make_optional<bool>(success);
|
||||
}
|
||||
|
||||
std::optional<std::chrono::time_point<std::chrono::system_clock>> Device::getRTC()
|
||||
{
|
||||
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::RED_GET_RTC);
|
||||
std::shared_ptr<Message> generic = com->waitForMessageSync([this]() {
|
||||
return com->sendCommand(Command::GetRTC);
|
||||
}, filter, std::chrono::milliseconds(3000));
|
||||
if(!generic) // Did not receive a message
|
||||
return std::nullopt;
|
||||
|
||||
auto rawMes = std::dynamic_pointer_cast<RawMessage>(generic);
|
||||
if(!rawMes)
|
||||
return std::nullopt;
|
||||
|
||||
if(rawMes->data.size() != sizeof(RTCCTIME))
|
||||
return std::nullopt;
|
||||
|
||||
const auto* time = (RTCCTIME*)rawMes->data.data();
|
||||
std::tm stdTime = {};
|
||||
// std::tm has no member for the `FracSec` member of RTCCTIME struct
|
||||
stdTime.tm_sec = time->Sec;
|
||||
stdTime.tm_min = time->Min;
|
||||
stdTime.tm_hour = time->Hour;
|
||||
stdTime.tm_mday = time->Day;
|
||||
stdTime.tm_mon = time->Month - 1; // [0-11]
|
||||
stdTime.tm_year = time->Year + 100; // Number of years since 1900+100
|
||||
stdTime.tm_wday = time->DOW;
|
||||
// RTCCTIME struct has no member for `tm_yday`
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define timegm _mkgmtime
|
||||
#endif
|
||||
|
||||
return std::chrono::system_clock::from_time_t(timegm(&stdTime));
|
||||
}
|
||||
|
||||
bool Device::setRTC(const std::chrono::time_point<std::chrono::system_clock>& time)
|
||||
{
|
||||
auto now = std::chrono::system_clock::to_time_t(time);
|
||||
const auto timeInfo = std::gmtime(&now);
|
||||
if(!timeInfo)
|
||||
return false;
|
||||
|
||||
// Populate the RTCCTIME struct using the timeInfo and offsets
|
||||
// Create a vector of arguments to send as the payload to the communication command
|
||||
std::vector<uint8_t> bytestream(sizeof(RTCCTIME));
|
||||
auto rtcVals = (RTCCTIME*)bytestream.data();
|
||||
rtcVals->FracSec = (uint8_t)0x00;
|
||||
rtcVals->Sec = (uint8_t)timeInfo->tm_sec;
|
||||
rtcVals->Min = (uint8_t)timeInfo->tm_min;
|
||||
rtcVals->Hour = (uint8_t)timeInfo->tm_hour;
|
||||
rtcVals->DOW = (uint8_t)timeInfo->tm_wday + 1;
|
||||
rtcVals->Day = (uint8_t)timeInfo->tm_mday;
|
||||
rtcVals->Month = (uint8_t)timeInfo->tm_mon + 1; // [0-11]
|
||||
rtcVals->Year = (uint8_t)timeInfo->tm_year % 100; // divide by 100 and take remainder to get last 2 digits of year
|
||||
|
||||
const auto generic = com->waitForMessageSync([&]() {
|
||||
return com->sendCommand(Command::SetRTC, bytestream);
|
||||
}, std::make_shared<Main51MessageFilter>(Command::SetRTC), std::chrono::milliseconds(100));
|
||||
|
||||
if(!generic) {
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto m51msg = std::dynamic_pointer_cast<Main51Message>(generic);
|
||||
if(!m51msg || m51msg->data.size() != 1) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return m51msg->data.front();
|
||||
}
|
||||
|
||||
std::optional<std::set<SupportedFeature>> Device::getSupportedFeatures() {
|
||||
auto timeout = std::chrono::milliseconds(100);
|
||||
std::shared_ptr<Message> msg = com->waitForMessageSync(
|
||||
[this](){ return com->sendCommand(ExtendedCommand::GetSupportedFeatures, {}); },
|
||||
std::make_shared<MessageFilter>(Message::Type::SupportedFeatures), timeout);
|
||||
if(!msg) {
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto& typedResponse = std::dynamic_pointer_cast<SupportedFeaturesMessage>(msg);
|
||||
if(!typedResponse) {
|
||||
report(APIEvent::Type::UnexpectedResponse, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::move(typedResponse->features);
|
||||
}
|
||||
|
||||
std::optional<size_t> Device::getGenericBinarySize(uint16_t binaryIndex) {
|
||||
auto timeout = std::chrono::milliseconds(2000);
|
||||
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(!isOnline()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyOffline, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> args = GenericBinaryStatusPacket::EncodeArguments(binaryIndex);
|
||||
|
||||
std::shared_ptr<Message> response = com->waitForMessageSync(
|
||||
[this, &args](){
|
||||
return com->sendCommand(ExtendedCommand::GenericBinaryInfo, args);
|
||||
},
|
||||
std::make_shared<MessageFilter>(Message::Type::GenericBinaryStatus),
|
||||
timeout
|
||||
);
|
||||
|
||||
if(!response) {
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
auto retMsg = std::static_pointer_cast<GenericBinaryStatusMessage>(response);
|
||||
if(!retMsg) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return retMsg->binarySize;
|
||||
}
|
||||
|
||||
bool Device::readBinaryFile(std::ostream& stream, uint16_t binaryIndex) {
|
||||
auto timeout = std::chrono::milliseconds(100);
|
||||
|
||||
auto size = getGenericBinarySize(binaryIndex);
|
||||
|
||||
if(!size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> arguments(sizeof(ExtendedDataMessage::ExtendedDataHeader));
|
||||
ExtendedDataMessage::ExtendedDataHeader& parameters = *reinterpret_cast<ExtendedDataMessage::ExtendedDataHeader*>(arguments.data());
|
||||
|
||||
auto filter = std::make_shared<MessageFilter>(Network::NetID::ExtendedData);
|
||||
|
||||
for(size_t offset = 0; offset < *size; offset+=ExtendedDataMessage::MaxExtendedDataBufferSize) {
|
||||
parameters.subCommand = ExtendedDataSubCommand::GenericBinaryRead;
|
||||
parameters.userValue = static_cast<uint32_t>(binaryIndex);
|
||||
parameters.offset = static_cast<uint32_t>(offset);
|
||||
parameters.length = static_cast<uint32_t>(std::min(ExtendedDataMessage::MaxExtendedDataBufferSize, *size - offset));
|
||||
|
||||
std::shared_ptr<Message> response = com->waitForMessageSync(
|
||||
[this, arguments](){
|
||||
return com->sendCommand(Command::ExtendedData, arguments);
|
||||
},
|
||||
filter,
|
||||
timeout
|
||||
);
|
||||
if(!response) {
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
auto retMsg = std::static_pointer_cast<ExtendedDataMessage>(response);
|
||||
|
||||
if(!stream.write(reinterpret_cast<char*>(retMsg->data.data()), retMsg->data.size())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::subscribeLiveData(std::shared_ptr<LiveDataCommandMessage> message) {
|
||||
if(!supportsLiveData()) {
|
||||
report(APIEvent::Type::LiveDataNotSupported, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if((message->args.size() > MAX_LIVE_DATA_ENTRIES) || message->args.empty()) {
|
||||
report(APIEvent::Type::LiveDataInvalidArgument, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> bytes;
|
||||
if(!com->encoder->encode(*com->packetizer, bytes, message)) {
|
||||
report(APIEvent::Type::LiveDataEncoderError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> response = com->waitForMessageSync(
|
||||
[this, &bytes](){ return com->sendPacket(bytes); },
|
||||
std::make_shared<MessageFilter>(Message::Type::LiveData));
|
||||
|
||||
if(response) {
|
||||
auto statusMsg = std::dynamic_pointer_cast<LiveDataStatusMessage>(response);
|
||||
if(statusMsg && statusMsg->requestedCommand == message->cmd) {
|
||||
switch(statusMsg->status) {
|
||||
case LiveDataStatus::SUCCESS:
|
||||
return true;
|
||||
case LiveDataStatus::ERR_DUPLICATE:
|
||||
case LiveDataStatus::ERR_HANDLE:
|
||||
{
|
||||
report(APIEvent::Type::LiveDataInvalidHandle, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
case LiveDataStatus::ERR_FULL:
|
||||
{
|
||||
report(APIEvent::Type::LiveDataMaxSignalsReached, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
case LiveDataStatus::ERR_UNKNOWN_COMMAND:
|
||||
{
|
||||
report(APIEvent::Type::LiveDataCommandFailed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
report(APIEvent::Type::LiveDataNoDeviceResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Device::unsubscribeLiveData(const LiveDataHandle& handle) {
|
||||
if(!supportsLiveData()) {
|
||||
report(APIEvent::Type::LiveDataNotSupported, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(!handle) {
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
auto msg = std::make_shared<LiveDataMessage>();
|
||||
msg->cmd = LiveDataCommand::UNSUBSCRIBE;
|
||||
msg->handle = handle;
|
||||
std::vector<uint8_t> bytes;
|
||||
if(!com->encoder->encode(*com->packetizer, bytes, msg)) {
|
||||
report(APIEvent::Type::LiveDataEncoderError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> response = com->waitForMessageSync(
|
||||
[this, &bytes](){ return com->sendPacket(bytes); },
|
||||
std::make_shared<MessageFilter>(Message::Type::LiveData));
|
||||
|
||||
if(!response) {
|
||||
report(APIEvent::Type::LiveDataNoDeviceResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto statusMsg = std::dynamic_pointer_cast<LiveDataStatusMessage>(response);
|
||||
if(!statusMsg || statusMsg->requestedCommand != msg->cmd) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(statusMsg->status != LiveDataStatus::SUCCESS) {
|
||||
report(APIEvent::Type::LiveDataCommandFailed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::clearAllLiveData() {
|
||||
if(!supportsLiveData()) {
|
||||
report(APIEvent::Type::LiveDataNotSupported, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto msg = std::make_shared<LiveDataMessage>();
|
||||
msg->cmd = LiveDataCommand::CLEAR_ALL;
|
||||
std::vector<uint8_t> bytes;
|
||||
if(!com->encoder->encode(*com->packetizer, bytes, msg)) {
|
||||
report(APIEvent::Type::LiveDataEncoderError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
std::shared_ptr<Message> response = com->waitForMessageSync(
|
||||
[this, &bytes](){ return com->sendPacket(bytes); },
|
||||
std::make_shared<MessageFilter>(Message::Type::LiveData));
|
||||
|
||||
if(!response) {
|
||||
report(APIEvent::Type::LiveDataNoDeviceResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto statusMsg = std::dynamic_pointer_cast<LiveDataStatusMessage>(response);
|
||||
if(!statusMsg || statusMsg->requestedCommand != msg->cmd) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(statusMsg->status != LiveDataStatus::SUCCESS) {
|
||||
report(APIEvent::Type::LiveDataCommandFailed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
+56
-31
@@ -1,13 +1,8 @@
|
||||
#include <array>
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
#include "icsneo/platform/devices.h"
|
||||
#include "icsneo/device/founddevice.h"
|
||||
#include "icsneo/communication/sdio.h"
|
||||
#include "generated/extensions/builtin.h"
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#else
|
||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||
#include "icsneo/platform/firmio.h"
|
||||
#endif
|
||||
@@ -20,9 +15,20 @@
|
||||
#include "icsneo/platform/cdcacm.h"
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
#include "icsneo/platform/android/androidusb.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;
|
||||
@@ -49,24 +55,32 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
static std::vector<FoundDevice> newDriverFoundDevices;
|
||||
newDriverFoundDevices.clear();
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
SDIO::Find(newDriverFoundDevices);
|
||||
#else
|
||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||
FirmIO::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_TCP
|
||||
TCP::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
|
||||
PCAP::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_CDCACM
|
||||
CDCACM::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
ANDROIDUSB::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
|
||||
@@ -131,6 +145,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
makeIfSerialMatches<NeoVIFIRE3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3FLEXRAY_H_
|
||||
makeIfSerialMatches<NeoVIFIRE3FlexRay>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIRED2_H_
|
||||
makeIfSerialMatches<NeoVIRED2>(dev, newFoundDevices);
|
||||
#endif
|
||||
@@ -147,6 +165,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
makeIfSerialMatches<RADA2B>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET_H_
|
||||
makeIfSerialMatches<RADCOMET>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADEPSILON_H_
|
||||
makeIfSerialMatches<RADEpsilon>(dev, newFoundDevices);
|
||||
#endif
|
||||
@@ -171,6 +193,14 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
makeIfSerialMatches<RADMoon2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON2ZL_H_
|
||||
makeIfSerialMatches<RADMoon2ZL>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON3_H_
|
||||
makeIfSerialMatches<RADMoon3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOONDUO_H_
|
||||
makeIfSerialMatches<RADMoonDuo>(dev, newFoundDevices);
|
||||
#endif
|
||||
@@ -228,27 +258,7 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
}
|
||||
|
||||
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
static std::vector<DeviceType> supportedDevices;
|
||||
|
||||
if (!supportedDevices.empty())
|
||||
return supportedDevices;
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::DEVICE_FINDER_GET_SUPORTED_DEVICES))
|
||||
return supportedDevices;
|
||||
uint16_t count;
|
||||
if(!socket.readTyped(count))
|
||||
return supportedDevices;
|
||||
std::vector<devicetype_t> devices(count);
|
||||
socket.read(devices.data(), devices.size() * sizeof(devicetype_t));
|
||||
supportedDevices.reserve(count);
|
||||
for(auto& dev : devices)
|
||||
supportedDevices.emplace_back(DeviceType(dev));
|
||||
}
|
||||
#else
|
||||
supportedDevices = {
|
||||
static std::vector<DeviceType> supportedDevices = {
|
||||
|
||||
#ifdef __ETHERBADGE_H_
|
||||
EtherBADGE::DEVICE_TYPE,
|
||||
@@ -274,6 +284,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
NeoVIFIRE2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3_H_
|
||||
NeoVIFIRE3::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3FLEXRAY_H_
|
||||
NeoVIFIRE3FlexRay::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIION_H_
|
||||
NeoVIION::DEVICE_TYPE,
|
||||
#endif
|
||||
@@ -286,6 +304,10 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
RADA2B::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET_H_
|
||||
RADCOMET::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADEPSILON_H_
|
||||
RADEpsilon::DEVICE_TYPE,
|
||||
#endif
|
||||
@@ -302,10 +324,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
RADGigastar::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON2_H_
|
||||
#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
|
||||
@@ -351,7 +377,6 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
#endif
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
return supportedDevices;
|
||||
}
|
||||
|
||||
+15
-4
@@ -5,7 +5,11 @@ 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) {
|
||||
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;
|
||||
|
||||
@@ -57,7 +61,7 @@ std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_e
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto readAmount = readLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
||||
useAlignedReadBuffer ? alignedReadBuffer.data() : (into + intoOffset), idealBlockSize, timeout);
|
||||
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) {
|
||||
@@ -94,12 +98,19 @@ std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_e
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount) {
|
||||
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) {
|
||||
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
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
|
||||
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
if(amount == 0)
|
||||
return 0;
|
||||
|
||||
@@ -51,7 +51,7 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
|
||||
const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
|
||||
if(useAlignedWriteBuffer) {
|
||||
auto read = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
|
||||
alignedWriteBuffer.data(), idealBlockSize, timeout);
|
||||
alignedWriteBuffer.data(), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(read != idealBlockSize)
|
||||
@@ -62,7 +62,7 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
|
||||
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
||||
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout);
|
||||
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) {
|
||||
@@ -83,6 +83,6 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
|
||||
// No matter how much succeeded, to be safe, we'll invalidate anything
|
||||
// we may have even tried to write, since it may have succeeded without
|
||||
// notifying, etc.
|
||||
readDriver.invalidateCache(pos, amount);
|
||||
readDriver.invalidateCache(pos, amount, memType);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
#include "icsneo/disk/extextractordiskreaddriver.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include "icsneo/communication/multichannelcommunication.h"
|
||||
#include "icsneo/api/lifetime.h"
|
||||
#include <cstring>
|
||||
#include "icsneo/communication/message/diskdatamessage.h"
|
||||
|
||||
//#define ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
@@ -13,7 +10,7 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
|
||||
if(amount > getBlockSizeBounds().second)
|
||||
return std::nullopt;
|
||||
@@ -28,7 +25,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
|
||||
unsigned int attempts = 4;
|
||||
while (attempts-- > 0)
|
||||
{
|
||||
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout);
|
||||
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout, memType);
|
||||
if (ret.has_value())
|
||||
break;
|
||||
}
|
||||
@@ -36,7 +33,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
|
||||
}
|
||||
|
||||
std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
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;
|
||||
@@ -46,144 +43,33 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligne
|
||||
if (largeSectorCount != uint64_t(sectorCount))
|
||||
return std::nullopt;
|
||||
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
uint16_t receiving = 0; // How much are we about to get before another header or completion
|
||||
uint64_t received = 0;
|
||||
uint16_t receivedCurrent = 0;
|
||||
size_t skipping = 0;
|
||||
std::vector<uint8_t> header;
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
bool error = !com.redirectRead([&](std::vector<uint8_t>&& data) {
|
||||
std::unique_lock<std::mutex> lk2(m);
|
||||
if(error) {
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
std::mutex mutex;
|
||||
uint8_t* intoOffset = into;
|
||||
int64_t remaining = amount;
|
||||
bool complete = false;
|
||||
|
||||
if(skipping > data.size()) {
|
||||
skipping -= data.size();
|
||||
return;
|
||||
}
|
||||
size_t offset = skipping;
|
||||
skipping = 0;
|
||||
while(offset < data.size()) {
|
||||
size_t left = data.size() - offset;
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Going to process " << left << " bytes" << std::endl;
|
||||
#endif
|
||||
if(header.size() != HeaderLength) {
|
||||
if(header.empty() && left && data[offset] != 0xaa) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Incorrect header " << int(data[offset]) << ' ' << int(offset) << std::endl;
|
||||
#endif
|
||||
error = true;
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
// Did we get a correct header and at least one byte of data?
|
||||
const auto begin = data.begin() + offset;
|
||||
int32_t headerLeft = int32_t(HeaderLength - header.size());
|
||||
if(int32_t(left) < headerLeft) {
|
||||
// Not enough data here, grab what header we can and continue
|
||||
header.insert(header.end(), begin, data.end());
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << int(left) << " bytes of header at " << offset << " (incomplete " <<
|
||||
header.size() << ')' << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
header.insert(header.end(), begin, begin + headerLeft);
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << int(headerLeft) << " bytes of header at " << offset << " (complete " <<
|
||||
header.size() << ')' << std::endl;
|
||||
#endif
|
||||
offset += headerLeft;
|
||||
|
||||
if(header[1] == uint8_t(Network::NetID::RED)) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got extended response " << int(offset) << std::endl;
|
||||
#endif
|
||||
// This is the extended command response, not all devices send this
|
||||
// If we got it, we need to figure out how much more data to ignore
|
||||
uint16_t length = (header[2] + (header[3] << 8));
|
||||
// Try for another header after this, regardless how much we choose
|
||||
// to skip and how we skip it
|
||||
header.clear();
|
||||
if(length <= 6) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Incorrect extended response length " << int(length) << ' ' << int(offset) << std::endl;
|
||||
#endif
|
||||
error = true;
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
length -= 7;
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Skipping " << int(length) << ' ' << int(left) << std::endl;
|
||||
#endif
|
||||
if(left < length) {
|
||||
skipping = length - left;
|
||||
return;
|
||||
}
|
||||
offset += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The device tells us how much it's sending us before the next header
|
||||
receiving = (header[5] | (header[6] << 8));
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Started packet of size " << receiving << " bytes" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
left = data.size() - offset;
|
||||
auto count = uint16_t(std::min<uint64_t>(std::min<uint64_t>(receiving - receivedCurrent, left), amount - received));
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "With " << int(left) << " bytes " << int(offset) << std::endl;
|
||||
#endif
|
||||
memcpy(into + received, data.data() + offset, count);
|
||||
received += count;
|
||||
receivedCurrent += count;
|
||||
offset += count;
|
||||
|
||||
if(amount == received) {
|
||||
if(receivedCurrent % 2 == 0)
|
||||
offset++;
|
||||
header.clear(); // Now we will need another header
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
lk2.lock();
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Finished!" << std::endl;
|
||||
#endif
|
||||
}
|
||||
else if(receivedCurrent == receiving) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << count << " bytes, " << receivedCurrent << " byte packet " << received <<
|
||||
" complete of " << amount << std::endl;
|
||||
#endif
|
||||
if(receivedCurrent % 2 == 0)
|
||||
offset++;
|
||||
header.clear(); // Now we will need another header
|
||||
receivedCurrent = 0;
|
||||
} else {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << count << " bytes, incomplete (of " << receiving << " bytes)" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
});
|
||||
Lifetime clearRedirect([&com, &lk] { lk.unlock(); com.clearRedirectRead(); });
|
||||
}, std::make_shared<MessageFilter>(Network::NetID::DiskData)));
|
||||
|
||||
if(error)
|
||||
return std::nullopt;
|
||||
|
||||
error = !com.sendCommand(ExtendedCommand::Extract, {
|
||||
if(!com.sendCommand(ExtendedCommand::Extract, {
|
||||
uint8_t(sector & 0xff),
|
||||
uint8_t((sector >> 8) & 0xff),
|
||||
uint8_t((sector >> 16) & 0xff),
|
||||
@@ -196,13 +82,16 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligne
|
||||
uint8_t((sectorCount >> 8) & 0xff),
|
||||
uint8_t((sectorCount >> 16) & 0xff),
|
||||
uint8_t((sectorCount >> 24) & 0xff),
|
||||
});
|
||||
if(error)
|
||||
})) {
|
||||
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;
|
||||
|
||||
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
|
||||
if(hitTimeout || error)
|
||||
return std::nullopt;
|
||||
|
||||
return amount;
|
||||
return amount - remaining;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#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) {
|
||||
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)
|
||||
@@ -16,9 +17,11 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
|
||||
return std::nullopt;
|
||||
|
||||
const uint64_t currentSector = pos / SectorSize;
|
||||
auto msg = com.waitForMessageSync([¤tSector, &com] {
|
||||
const uint8_t memLocation = (uint8_t)memType;
|
||||
|
||||
auto msg = com.waitForMessageSync([¤tSector, &memLocation, &com] {
|
||||
return com.sendCommand(Command::NeoReadMemory, {
|
||||
MemoryTypeSD,
|
||||
memLocation,
|
||||
uint8_t(currentSector & 0xFF),
|
||||
uint8_t((currentSector >> 8) & 0xFF),
|
||||
uint8_t((currentSector >> 16) & 0xFF),
|
||||
@@ -44,7 +47,7 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
@@ -55,9 +58,11 @@ std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communicati
|
||||
return std::nullopt;
|
||||
|
||||
const uint64_t currentSector = pos / SectorSize;
|
||||
auto msg = com.waitForMessageSync([¤tSector, &com, from, amount] {
|
||||
const uint8_t memLocation = (uint8_t)memType;
|
||||
|
||||
auto msg = com.waitForMessageSync([¤tSector, &memLocation, &com, from, amount] {
|
||||
std::vector<uint8_t> command = {
|
||||
MemoryTypeSD,
|
||||
memLocation,
|
||||
uint8_t(currentSector & 0xFF),
|
||||
uint8_t((currentSector >> 8) & 0xFF),
|
||||
uint8_t((currentSector >> 16) & 0xFF),
|
||||
|
||||
@@ -4,25 +4,25 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
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)
|
||||
|
||||
+5
-6
@@ -105,14 +105,13 @@ A2B Wave Output
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Users may add a ``icsneo::A2BWAVOutput`` message callback to their device in order to write A2B PCM data to a WAVE file. The message callback listens for ``icsneo::A2BMessage``
|
||||
messages and writes both downstream and upstream channels to a single wave file. If downstream and upstream each have ``32`` channels, the wave file will contain ``2*32 = 64``
|
||||
total channels. The first half of the channels, channels ``0-31`` in the outputted wave file, represent downstream channel ``0-31``. Likewise, the second half of the channels,
|
||||
channels ``32-63`` in the outputted wave file, represent upstream channel ``0-31``. Let ``NUM_CHANNELS`` be the total number of channels in a single stream. If we introduce a
|
||||
variable ``IS_UPSTREAM`` which is ``0`` when downstream and ``1`` when upstream and desired a channel ``CHANNEL_NUM`` in either downstream or upstream the
|
||||
channel ``IS_UPSTREAM * NUM_CHANNELS + CHANNEL_NUM`` would correspond to the channel in the outputted wave file.
|
||||
total channels. Channels are indexed at 0 and interleaved such that downstream are on even number channels and upstream on odd number channels. If we introduce a
|
||||
variable ``IS_UPSTREAM`` which is ``0`` when downstream and ``1`` when upstream and desired a channel ``CHANNEL_NUM`` the corresponding channel in the wave file would be
|
||||
``2*CHANNEL_NUM + IS_UPSTREAM``.
|
||||
|
||||
Wave files may be split by channel using programs such as ``FFmpeg``. Consider a file ``out.wav`` which was generated using a ``icsneo::A2BWAVOutput`` object
|
||||
and contains ``32`` channels per stream. The ``icsneo::A2BWavoutput`` object injested PCM data with a sample rate of ``44.1 kHz`` and bit depth of ``24``. The corresponding
|
||||
channel of upstream channel ``8`` in ``out.wav`` would be ``1*32 + 8 = 40``. The following ``FFmpeg`` command may be ran in a linux environment to create a new wave
|
||||
channel of upstream channel ``8`` in ``out.wav`` would be ``2*CHANNEL_NUM + IS_UPSTREAM = 2*8 + 1 = 17``. The following ``FFmpeg`` command may be ran in a linux environment to create a new wave
|
||||
file ``out_upstream_ch8.wav`` which contains only PCM samples off of upstream channel ``8``.
|
||||
|
||||
``ffmpeg -i out.wav -ar 44100 -acodec pcm_s24le -map_channel 0.0.40 out_upstream_ch8.wav``
|
||||
``ffmpeg -i out.wav -ar 44100 -acodec pcm_s24le -map_channel 0.0.17 out_upstream_ch8.wav``
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'libicsneo'
|
||||
copyright = '2018-2020, Intrepid Control Systems, Inc.'
|
||||
copyright = '2018-2023, 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-2020, Intrepid Control Systems, Inc.'
|
||||
copyright = '2018-2023, Intrepid Control Systems, Inc.'
|
||||
author = 'Intrepid Control Systems, Inc.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
|
||||
+30
-1
@@ -1,8 +1,13 @@
|
||||
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)
|
||||
|
||||
# Disabled until we properly build these in-tree
|
||||
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
|
||||
@@ -12,6 +17,14 @@ 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()
|
||||
@@ -24,6 +37,22 @@ 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_CSHARP_INTERACTIVE_EXAMPLE)
|
||||
# add_subdirectory(csharp)
|
||||
# endif()
|
||||
|
||||
@@ -13,4 +13,5 @@ endif()
|
||||
add_executable(libicsneoc-interactive-example src/main.c)
|
||||
if(UNIX)
|
||||
target_link_libraries(libicsneoc-interactive-example ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
endif()
|
||||
target_link_libraries(libicsneoc-interactive-example icsneoc)
|
||||
@@ -1,5 +1,5 @@
|
||||
// Signal to dynamically load the library
|
||||
#define ICSNEOC_DYNAMICLOAD
|
||||
//#define ICSNEOC_DYNAMICLOAD
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -219,21 +219,21 @@ int main() {
|
||||
|
||||
// Attempt to initialize the library and access its functions
|
||||
// This call searches for icsneoc.dll according to the standard dynamic-link library search order
|
||||
int ret = icsneo_init();
|
||||
if(ret == 1) {
|
||||
printf("The library was already initialized!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if(ret == 2) {
|
||||
printf("The library could not be found!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if(ret == 3) {
|
||||
printf("The library is missing functions!\n");
|
||||
return ret;
|
||||
}
|
||||
//int ret = icsneo_init();
|
||||
//if(ret == 1) {
|
||||
// printf("The library was already initialized!\n");
|
||||
// return ret;
|
||||
//}
|
||||
//
|
||||
//if(ret == 2) {
|
||||
// printf("The library could not be found!\n");
|
||||
// return ret;
|
||||
//}
|
||||
//
|
||||
//if(ret == 3) {
|
||||
// printf("The library is missing functions!\n");
|
||||
// return ret;
|
||||
//}
|
||||
|
||||
neoversion_t ver = icsneo_getVersion();
|
||||
printf("ICS icsneoc.dll version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
|
||||
@@ -487,6 +487,31 @@ int main() {
|
||||
printf("(%"PRIu64")\n", canMsg->timestamp);
|
||||
break;
|
||||
}
|
||||
case ICSNEO_NETWORK_TYPE_LIN: {
|
||||
neomessage_lin_t* linMsg = (neomessage_lin_t*)frame;
|
||||
size_t frameLen = linMsg->length;
|
||||
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
|
||||
size_t numberBytesHeader = (dataLen > 1) ? 3 : 1;
|
||||
size_t numberBytesData = frameLen - numberBytesHeader;
|
||||
if(linMsg->netid == ICSNEO_NETID_LIN) {
|
||||
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
|
||||
}
|
||||
else if (linMsg->netid == ICSNEO_NETID_LIN2) {
|
||||
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < dataLen; ++i) {
|
||||
if (i < 2) {
|
||||
printf("%02x ", linMsg->header[i+1]);
|
||||
} else {
|
||||
printf("%02x ", linMsg->data[i-2]);
|
||||
}
|
||||
}
|
||||
if(numberBytesData > 0)
|
||||
printf("| Checksum: 0x%02x\n", linMsg->data[numberBytesData-1]);
|
||||
else
|
||||
printf("| Checksum: 0x%02x\n", linMsg->header[numberBytesHeader-1]);
|
||||
}
|
||||
default:
|
||||
printf("\tMessage on netid %d with length %zu\n", frame->netid, frame->length);
|
||||
break;
|
||||
@@ -762,7 +787,7 @@ int main() {
|
||||
case 'X':
|
||||
case 'x':
|
||||
printf("Exiting program\n");
|
||||
return !icsneo_close();
|
||||
return 0;
|
||||
default:
|
||||
printf("Unexpected input, exiting!\n");
|
||||
return 1;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneoc-legacy-lin-example VERSION 0.2.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)
|
||||
@@ -0,0 +1,80 @@
|
||||
# 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™
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
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 = 10;
|
||||
NeoDevice devices[10];
|
||||
void* hObject; // holds a handle to the neoVI object
|
||||
int iRetVal = 0;
|
||||
int deviceTypes = 0;
|
||||
int iResult = 0;
|
||||
SDeviceSettings pSettings;
|
||||
|
||||
iRetVal = icsneoFindNeoDevices(deviceTypes, devices, &numDevices);
|
||||
if(iRetVal) {
|
||||
// Attempt to open the selected device, enable message polling, and go online
|
||||
iRetVal = icsneoOpenNeoDevice(&devices[0], &hObject, NULL, 1, 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;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#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 = 10;
|
||||
NeoDevice devices[10];
|
||||
void* hObject; // holds a handle to the neoVI object
|
||||
int iRetVal = 0;
|
||||
int deviceTypes = 0;
|
||||
int iResult = 0;
|
||||
|
||||
iRetVal = icsneoFindNeoDevices(deviceTypes, devices, &numDevices);
|
||||
if(iRetVal) {
|
||||
// Attempt to open the selected device, enable message polling, and go online
|
||||
iRetVal = icsneoOpenNeoDevice(&devices[0], &hObject, NULL, 1, 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;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneoc-simple-lin-example VERSION 0.2.0)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Include libicsneo's include directory
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
|
||||
|
||||
if(UNIX)
|
||||
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
|
||||
endif()
|
||||
|
||||
add_executable(libicsneoc-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)
|
||||
@@ -0,0 +1,80 @@
|
||||
# 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™
|
||||
@@ -0,0 +1,427 @@
|
||||
// Signal to dynamically load the library
|
||||
//#define ICSNEOC_DYNAMICLOAD
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
// Get the PRIu64 macro for timestamps
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
|
||||
// Include icsneo/icsneoc.h to access library functions
|
||||
#include "icsneo/icsneoc.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SLEEP(msecs) Sleep(msecs)
|
||||
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||
#include <time.h>
|
||||
#define SLEEP(msecs) do { \
|
||||
struct timespec ts; \
|
||||
ts.tv_sec = msecs/1000; \
|
||||
ts.tv_nsec = msecs%1000*1000; \
|
||||
nanosleep(&ts, NULL); \
|
||||
} while (0)
|
||||
#else
|
||||
#error "Platform unknown"
|
||||
#endif
|
||||
|
||||
size_t msgLimit = 50000;
|
||||
size_t numDevices = 0;
|
||||
neodevice_t devices[99];
|
||||
const neodevice_t* selectedDevice = NULL;
|
||||
|
||||
/**
|
||||
* \brief Prints all current known devices to output in the following format:
|
||||
* [num] DeviceType SerialNum Connected: Yes/No Online: Yes/No Msg Polling: On/Off
|
||||
*
|
||||
* If any devices could not be described due to an error, they will appear in the following format:
|
||||
* Description for device num not available!
|
||||
*/
|
||||
void printAllDevices() {
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n");
|
||||
}
|
||||
for(size_t i = 0; i < numDevices; i++) {
|
||||
char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 };
|
||||
size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
|
||||
|
||||
// Updates productDescription and descriptionLength for each device
|
||||
if(icsneo_describeDevice(devices + i, productDescription, &descriptionLength)) {
|
||||
printf("[%zd] %s\tConnected: ", i + 1, productDescription);
|
||||
if(icsneo_isOpen(devices + i)) {
|
||||
printf("Yes\t");
|
||||
} else printf("No\t");
|
||||
|
||||
printf("Online: ");
|
||||
if(icsneo_isOnline(devices + i)) {
|
||||
printf("Yes\t");
|
||||
} else printf("No\t");
|
||||
|
||||
printf("Msg Polling: ");
|
||||
if(icsneo_isMessagePollingEnabled(devices + i)) {
|
||||
printf("On\n");
|
||||
} else printf("Off\n");
|
||||
|
||||
} else {
|
||||
printf("Description for device %zd not available!\n", i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Scans for any new devices, adding them to devices and updating numDevices accordingly
|
||||
* A total of 99 devices may be stored at once
|
||||
*/
|
||||
size_t scanNewDevices() {
|
||||
neodevice_t newDevices[99];
|
||||
size_t numNewDevices = 99;
|
||||
icsneo_findAllDevices(newDevices, &numNewDevices);
|
||||
|
||||
for(size_t i = 0; i < numNewDevices; ++i) {
|
||||
devices[numDevices + i] = newDevices[i];
|
||||
}
|
||||
numDevices += numNewDevices;
|
||||
return numNewDevices;
|
||||
}
|
||||
void printLastError() {
|
||||
neoevent_t error;
|
||||
if(icsneo_getLastError(&error))
|
||||
printf("Error 0x%u: %s\n", error.eventNumber, error.description);
|
||||
else
|
||||
printf("No errors found!\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Gets all current API events and prints them to output
|
||||
* Flushes the API event cache, meaning future calls (barring any new events) will not detect any further API events
|
||||
*/
|
||||
void printAPIEvents() {
|
||||
neoevent_t events[99];
|
||||
size_t eventCount = 99;
|
||||
if(icsneo_getEvents(events, &eventCount)) {
|
||||
if(eventCount == 1) {
|
||||
printf("1 API event found!\n");
|
||||
printf("Event 0x%u: %s\n", events[0].eventNumber, events[0].description);
|
||||
} else {
|
||||
printf("%d API events found!\n", (int) eventCount);
|
||||
for(size_t i = 0; i < eventCount; ++i) {
|
||||
printf("Event 0x%u: %s\n", events[i].eventNumber, events[i].description);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("Failed to get API events!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Gets all current device events and prints them to output. If no device events were found, printAPIEvents() is called
|
||||
* Flushes the device event cache, meaning future calls (barring any new events) will not detect any further device events for this device
|
||||
*/
|
||||
void printDeviceEvents(neodevice_t* device) {
|
||||
neoevent_t events[99];
|
||||
size_t eventCount = 99;
|
||||
if(icsneo_getDeviceEvents(selectedDevice, events, &eventCount)) {
|
||||
if(eventCount == 1) {
|
||||
printf("1 device event found!\n");
|
||||
printf("Event 0x%x: %s\n", events[0].eventNumber, events[0].description);
|
||||
} else {
|
||||
printf("%d device events found!\n", (int) eventCount);
|
||||
for(size_t i = 0; i < eventCount; ++i) {
|
||||
printf("Event 0x%x: %s\n", events[i].eventNumber, events[i].description);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Used to check character inputs for correctness (if they are found in an expected list)
|
||||
* \param[in] numArgs the number of possible options for the expected character
|
||||
* \param[in] ... the possible options for the expected character
|
||||
* \returns the entered character
|
||||
*
|
||||
* This function repeatedly prompts the user for input until a matching input is entered
|
||||
* Example usage: char input = getCharInput(5, 'F', 'u', 'b', 'a', 'r');
|
||||
*/
|
||||
char getCharInput(int numArgs, ...) {
|
||||
// 99 chars shold be more than enough to catch any typos
|
||||
char input[99];
|
||||
bool found = false;
|
||||
|
||||
va_list vaList;
|
||||
va_start(vaList, numArgs);
|
||||
|
||||
char* list = (char*) calloc(numArgs, sizeof(char));
|
||||
for(int i = 0; i < numArgs; ++i) {
|
||||
*(list + i) = va_arg(vaList, int);
|
||||
}
|
||||
|
||||
va_end(vaList);
|
||||
|
||||
while(!found) {
|
||||
fgets(input, 99, stdin);
|
||||
if(strlen(input) == 2) {
|
||||
for(int i = 0; i < numArgs; ++i) {
|
||||
if(input[0] == *(list + i)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
printf("Input did not match expected options. Please try again.\n");
|
||||
}
|
||||
}
|
||||
|
||||
free(list);
|
||||
|
||||
return input[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Prompts the user to select a device from the list of currently known devices
|
||||
* \returns a pointer to the device in devices[] selected by the user
|
||||
* Requires an input from 1-9, so a maximum of 9 devices are supported
|
||||
*/
|
||||
const neodevice_t* selectDevice() {
|
||||
printf("Please select a device:\n");
|
||||
printAllDevices();
|
||||
printf("\n");
|
||||
|
||||
size_t selectedDeviceNum = 10;
|
||||
|
||||
while(selectedDeviceNum > numDevices) {
|
||||
char deviceSelection = getCharInput(9, '1', '2', '3', '4', '5', '6', '7', '8', '9');
|
||||
if(deviceSelection < '0') {
|
||||
printf("Selected device out of range!\n");
|
||||
continue;
|
||||
}
|
||||
selectedDeviceNum = deviceSelection - '0';
|
||||
if(selectedDeviceNum > numDevices) {
|
||||
printf("Selected device out of range!\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
return devices + selectedDeviceNum - 1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
neoversion_t ver = icsneo_getVersion();
|
||||
printf("ICS icsneoc version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
|
||||
// Find and attempt to open device
|
||||
size_t numNewDevices = scanNewDevices();
|
||||
if(numNewDevices == 1) {
|
||||
printf("1 new device found!\n");
|
||||
} else {
|
||||
printf("%d new devices found!\n", (int) numNewDevices);
|
||||
}
|
||||
printAllDevices();
|
||||
printf("\n");
|
||||
|
||||
// Select a device and get its description
|
||||
if(numDevices == 0) {
|
||||
printf("No devices found! Please scan for new devices.\n\n");
|
||||
return 1;
|
||||
}
|
||||
selectedDevice = &devices[0];
|
||||
|
||||
// Get the product description for the device
|
||||
char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 };
|
||||
size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
|
||||
icsneo_describeDevice(selectedDevice, productDescription, &descriptionLength);
|
||||
|
||||
// Attempt to open the selected device
|
||||
{
|
||||
if(icsneo_openDevice(selectedDevice)) {
|
||||
printf("%s successfully opened!\n\n", productDescription);
|
||||
} else {
|
||||
printf("%s failed to open!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
// Attempt to go online
|
||||
{
|
||||
if(icsneo_goOnline(selectedDevice)) {
|
||||
printf("%s successfully went online!\n\n", productDescription);
|
||||
} else {
|
||||
printf("%s failed to go online!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
// Attempt to enable message polling
|
||||
{
|
||||
if(icsneo_enableMessagePolling(selectedDevice)) {
|
||||
printf("Successfully enabled message polling for %s!\n\n", productDescription);
|
||||
} else {
|
||||
printf("Failed to enable message polling for %s!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
// Send message LIN
|
||||
{
|
||||
// Start generating sample msg
|
||||
uint8_t sendMessageData[8];
|
||||
sendMessageData[0] = 0x33;
|
||||
sendMessageData[1] = 0x44;
|
||||
sendMessageData[2] = 0x55;
|
||||
sendMessageData[3] = 0x66;
|
||||
sendMessageData[4] = 0x77;
|
||||
sendMessageData[5] = 0x88;
|
||||
sendMessageData[6] = 0x88;
|
||||
|
||||
neomessage_lin_t msg = {0};
|
||||
|
||||
msg.header[0] = 0x11; //protected ID
|
||||
msg.header[1] = 0x11;
|
||||
msg.header[2] = 0x22;
|
||||
msg.length = 10;
|
||||
msg.netid = ICSNEO_NETID_LIN;
|
||||
msg.data = sendMessageData;
|
||||
msg.linStatus.txCommander = 1;
|
||||
msg.linStatus.txChecksumEnhanced = 1;
|
||||
msg.type = ICSNEO_NETWORK_TYPE_LIN;
|
||||
msg.checksum = 0x88;
|
||||
|
||||
// Attempt to transmit the sample msg
|
||||
if(icsneo_transmit(selectedDevice, (const neomessage_t*) &msg)) {
|
||||
printf("Message transmit successful!\n\n");
|
||||
} else {
|
||||
printf("Failed to transmit message to %s!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
// Wait for a moment
|
||||
SLEEP(1000);
|
||||
// Get messages
|
||||
{
|
||||
// Prepare the array of neomessage_t ptrs for reading in the messages
|
||||
neomessage_t* msgs = (neomessage_t*) malloc(msgLimit * sizeof(neomessage_t));
|
||||
|
||||
// Get messages
|
||||
size_t msgCount = msgLimit;
|
||||
|
||||
// Attempt to get messages
|
||||
if(!icsneo_getMessages(selectedDevice, msgs, &msgCount, (uint64_t) 0)) {
|
||||
printf("Failed to get messages for %s!\n\n", productDescription);
|
||||
printLastError();
|
||||
free(msgs);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if(msgCount == 1) {
|
||||
printf("1 message received from %s!\n", productDescription);
|
||||
} else {
|
||||
printf("%d messages received from %s!\n", (int) msgCount, productDescription);
|
||||
}
|
||||
|
||||
// Print out the received messages
|
||||
for(size_t i = 0; i < msgCount; i++) {
|
||||
const neomessage_t* msg = &msgs[i];
|
||||
switch(msg->messageType) {
|
||||
case ICSNEO_MESSAGE_TYPE_FRAME: {
|
||||
const neomessage_frame_t* frame = (neomessage_frame_t*)msg;
|
||||
switch(frame->type) {
|
||||
case ICSNEO_NETWORK_TYPE_CAN: {
|
||||
neomessage_can_t* canMsg = (neomessage_can_t*)frame;
|
||||
printf("\t0x%03x [%zu] ", canMsg->arbid, canMsg->length);
|
||||
for(size_t i = 0; i < canMsg->length; i++) {
|
||||
printf("%02x ", canMsg->data[i]);
|
||||
}
|
||||
if(canMsg->status.transmitMessage)
|
||||
printf("TX%s %04x ", canMsg->status.globalError ? " ERR" : "", canMsg->description);
|
||||
printf("(%"PRIu64")\n", canMsg->timestamp);
|
||||
break;
|
||||
}
|
||||
case ICSNEO_NETWORK_TYPE_LIN: {
|
||||
neomessage_lin_t* linMsg = (neomessage_lin_t*)frame;
|
||||
size_t frameLen = linMsg->length;
|
||||
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
|
||||
size_t numberBytesHeader = (dataLen > 1) ? 3 : 1;
|
||||
size_t numberBytesData = frameLen - numberBytesHeader;
|
||||
if(linMsg->netid == ICSNEO_NETID_LIN) {
|
||||
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
|
||||
}
|
||||
else if (linMsg->netid == ICSNEO_NETID_LIN2) {
|
||||
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < dataLen; ++i) {
|
||||
if (i < 2) {
|
||||
printf("%02x ", linMsg->header[i+1]);
|
||||
} else {
|
||||
printf("%02x ", linMsg->data[i-2]);
|
||||
}
|
||||
}
|
||||
printf("| Checksum: 0x%02x\n", linMsg->checksum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ICSNEO_MESSAGE_TYPE_CAN_ERROR_COUNT: {
|
||||
const neomessage_can_error_t* cec = (neomessage_can_error_t*)msg;
|
||||
printf("\tCAN error counts changed, TEC=%d, REC=%d%s", cec->transmitErrorCount, cec->receiveErrorCount,
|
||||
cec->status.canBusOff ? " (Bus Off)" : "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
free(msgs);
|
||||
}
|
||||
// Attempt to disable message polling
|
||||
{
|
||||
if(icsneo_disableMessagePolling(selectedDevice)) {
|
||||
printf("Successfully disabled message polling for %s!\n\n", productDescription);
|
||||
} else {
|
||||
printf("Failed to disable message polling limit for %s!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
// Attempt to go offline
|
||||
{
|
||||
if(icsneo_goOffline(selectedDevice)) {
|
||||
printf("%s successfully went offline!\n\n", productDescription);
|
||||
} else {
|
||||
printf("%s failed to go offline!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
// Attempt to close the device
|
||||
{
|
||||
if(icsneo_closeDevice(selectedDevice)) {
|
||||
numDevices--;
|
||||
printf("Successfully closed %s!\n\n", productDescription);
|
||||
|
||||
// Shifts everything after the removed device 1 index to the left
|
||||
bool startResizing = false;
|
||||
for(size_t i = 0; i < numDevices; ++i) {
|
||||
if(selectedDevice == devices + i)
|
||||
startResizing = true;
|
||||
if(startResizing)
|
||||
devices[i] = devices[i + 1];
|
||||
}
|
||||
|
||||
selectedDevice = NULL;
|
||||
} else {
|
||||
printf("Failed to close %s!\n\n", productDescription);
|
||||
printLastError();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
//exit
|
||||
printf("Exiting program\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
# libicsneo C++ Example
|
||||
|
||||
This is an example console application which uses libicsneo to connect to an Intrepid Control Systems hardware device. It has both interactive and simple examples for sending and receiving CAN & CAN FD traffic.
|
||||
|
||||
## Building
|
||||
|
||||
This example shows how to use the C++ version of libicsneo with CMake. It will build libicsneo along with your project.
|
||||
|
||||
First, you need to clone the repository onto your local machine. Run:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/intrepidcs/libicsneo-examples --recursive
|
||||
```
|
||||
|
||||
Alternatively, if you cloned without the `--recursive flag`, you must enter the `libicsneo-examples` folder and run the following:
|
||||
|
||||
```shell
|
||||
git submodule update --recursive --init
|
||||
```
|
||||
|
||||
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
|
||||
|
||||
### Windows using Visual Studio 2017+
|
||||
|
||||
1. Launch Visual Studio and open the `libicsneo-examples` folder.
|
||||
2. Choose `File->Open->CMake...`
|
||||
3. Navigate to the `libicsneocpp-example` folder and select the `CMakeLists.txt` there.
|
||||
4. Visual Studio will process the CMake project.
|
||||
5. Choose the dropdown attached to the green play button (labelled "select startup item...") in the toolbar.
|
||||
6. Select `libicsneocpp-simple-example.exe`
|
||||
7. Press the green play button to compile and run the example.
|
||||
|
||||
### Ubuntu 18.04 LTS
|
||||
|
||||
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
|
||||
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
|
||||
3. Enter the build directory with `cd build`
|
||||
4. Run `cmake ..` to generate your Makefile.
|
||||
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
|
||||
5. Run `make libicsneocpp-interactive-example` to build.
|
||||
* Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
|
||||
6. Now run `sudo ./libicsneocpp-interactive-example` to run the example.
|
||||
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
|
||||
7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6.
|
||||
|
||||
### macOS
|
||||
|
||||
Instructions coming soon™
|
||||
+338
-34
@@ -1,52 +1,356 @@
|
||||
// libicsneo A2B example
|
||||
// Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node
|
||||
// Options:
|
||||
// -h, --help Display help message.
|
||||
// -e, --example [EXAMPLE_NUM] Example to run.
|
||||
// Example usage: ./libicsneocpp-a2b.exe --example 1
|
||||
// Example usage: ./libicsneocpp-a2b.exe -h
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <icsneo/icsneocpp.h>
|
||||
#include <icsneo/device/tree/rada2b/rada2bsettings.h>
|
||||
#include <icsneo/communication/message/callback/streamoutput/a2bdecoder.h>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
static constexpr size_t numFramesInWave = 48;
|
||||
|
||||
const std::string rada2bSerial = "Your RADA2B serial number.";
|
||||
std::string makeWave() {
|
||||
icsneo::WaveFileHeader header = icsneo::WaveFileHeader(1, 48000, 24);
|
||||
std::vector<uint8_t> sineWaveSamples = {
|
||||
0x00, 0x2B, 0x98, 0x08, 0x25, 0x01, 0x10, 0xD3, 0xEF, 0x18, 0x40, 0xA3, 0x20, 0x33, 0x4C, 0x26,
|
||||
0xCE, 0xCA, 0x2D, 0x5B, 0x41, 0x32, 0xB9, 0x3C, 0x37, 0x6E, 0x50, 0x3B, 0x29, 0x18, 0x3D, 0xC2,
|
||||
0x96, 0x3F, 0x86, 0xD3, 0x3F, 0xEC, 0x35, 0x3F, 0x85, 0xA7, 0x3D, 0xC4, 0x19, 0x3B, 0x28, 0xC9,
|
||||
0x37, 0x6B, 0x5A, 0x32, 0xC1, 0xC4, 0x2D, 0x4A, 0xEE, 0x26, 0xE8, 0xB1, 0x20, 0x0E, 0xCF, 0x18,
|
||||
0x6F, 0xAA, 0x10, 0x9C, 0x17, 0x08, 0x53, 0x35, 0x00, 0x01, 0xFD, 0xF7, 0xA9, 0x29, 0xEF, 0x66,
|
||||
0x87, 0xE7, 0x8F, 0x37, 0xDF, 0xF0, 0x8A, 0xD9, 0x19, 0xB3, 0xD2, 0xB1, 0x3E, 0xCD, 0x42, 0xE9,
|
||||
0xC8, 0x8F, 0xE0, 0xC4, 0xDB, 0x39, 0xC2, 0x39, 0x78, 0xC0, 0x7A, 0x8A, 0xC0, 0x16, 0x38, 0xC0,
|
||||
0x74, 0x18, 0xC2, 0x44, 0xBF, 0xC4, 0xCE, 0x26, 0xC8, 0x9A, 0x99, 0xCD, 0x3F, 0x53, 0xD2, 0xA8,
|
||||
0xBD, 0xD9, 0x32, 0xF5, 0xDF, 0xC2, 0x68, 0xE7, 0xD5, 0xFD, 0xEF, 0x02, 0x15, 0xF8, 0x3B, 0x33,
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "Start example\n";
|
||||
auto devices = icsneo::FindAllDevices();
|
||||
std::vector<uint8_t> sineWave;
|
||||
sineWave.reserve(sineWaveSamples.size() + sizeof(header));
|
||||
|
||||
std::shared_ptr<icsneo::Device> rada2b;
|
||||
for(auto& device : devices) {
|
||||
if(device->getSerial() == rada2bSerial) {
|
||||
rada2b = device;
|
||||
sineWave.insert(sineWave.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header));
|
||||
std::copy(sineWaveSamples.begin(), sineWaveSamples.end(), std::back_inserter(sineWave));
|
||||
|
||||
return std::string(sineWave.begin(), sineWave.end());
|
||||
}
|
||||
|
||||
// Example 0: TX
|
||||
void example0(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::cout << "Transmitting a sine wave..." << std::endl;
|
||||
|
||||
// Create sine tone in wave format
|
||||
std::string waveString = makeWave();
|
||||
|
||||
// Audio map to map which channel in wave to stream on a2b bus.
|
||||
icsneo::A2BAudioChannelMap a2bmap(4);
|
||||
|
||||
|
||||
a2bmap.set(
|
||||
2, // Channel on a2b bus
|
||||
icsneo::A2BMessage::A2BDirection::Downstream, // Direction
|
||||
0 // Channel in wave file
|
||||
);
|
||||
|
||||
a2bmap.setAll(0);
|
||||
|
||||
icsneo::A2BDecoder decoder(
|
||||
std::make_unique<std::istringstream>(waveString), // Wave file stream
|
||||
false, // True when using 16 bit samples
|
||||
a2bmap
|
||||
);
|
||||
|
||||
|
||||
double elapsedTime = 0.0;
|
||||
// Play roughly 5 seconds of sine tone.
|
||||
while(elapsedTime < 5.0) {
|
||||
|
||||
decoder.outputAll(rada2b); // Output entire wave file
|
||||
|
||||
elapsedTime += (static_cast<double>(numFramesInWave)) * 1.0/48000.0;
|
||||
|
||||
decoder.stream->clear();
|
||||
decoder.stream->seekg(0, std::ios::beg);
|
||||
// Also outputs entire wave file
|
||||
while(decoder && elapsedTime < 5.0) {
|
||||
auto msg = decoder.decode();
|
||||
rada2b->transmit(msg);
|
||||
elapsedTime += (static_cast<double>(msg->getNumFrames()))*1.0/48000.0;
|
||||
}
|
||||
|
||||
decoder.stream->clear();
|
||||
decoder.stream->seekg(0, std::ios::beg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Example 1: RX
|
||||
void example1(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::cout << "Receiving 5 seconds of audio data..." << std::endl;
|
||||
|
||||
// Add WAV output message callback
|
||||
// Saves samples to "out.wav"
|
||||
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("out.wav", 48000));
|
||||
|
||||
// Sleep this thread for 5 seconds, message callback still runs
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
|
||||
// Remove callback
|
||||
rada2b->removeMessageCallback(handler);
|
||||
}
|
||||
|
||||
|
||||
// Example 2: RADA2B settings
|
||||
void example2(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
uint8_t numChannels;
|
||||
{
|
||||
// Get device settings
|
||||
auto* settings = rada2b->settings.get();
|
||||
auto* rada2bSettings = static_cast<icsneo::RADA2BSettings*>(settings);
|
||||
|
||||
// Check if monitor mode is enabled
|
||||
auto type = rada2bSettings->getNodeType(icsneo::RADA2BSettings::RADA2BDevice::Monitor);
|
||||
if(type == icsneo::RADA2BSettings::NodeType::Monitor) {
|
||||
std::cout << "Device is in monitor mode" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout << "Device is not in monitor mode" << std::endl;
|
||||
}
|
||||
|
||||
// Get current tdm mode
|
||||
numChannels = rada2bSettings->getNumChannels(icsneo::RADA2BSettings::RADA2BDevice::Node);
|
||||
|
||||
std::cout << "Current num channels: " << static_cast<uint32_t>(numChannels) << std::endl;
|
||||
// Set node type to master node.
|
||||
rada2bSettings->setNodeType(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::NodeType::Master);
|
||||
|
||||
// Set TDM mode to TDM8
|
||||
rada2bSettings->setTDMMode(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::TDMMode::TDM8);
|
||||
|
||||
// Apply local settings to device
|
||||
rada2bSettings->apply();
|
||||
}
|
||||
}
|
||||
|
||||
// Example 3: A2BMessage API
|
||||
void example3() {
|
||||
icsneo::A2BMessage msg = icsneo::A2BMessage(4, false, 2048); // Create new A2BMessage
|
||||
|
||||
msg[0][0] = 60; // Set sample using operator[][]
|
||||
msg[0][3] = 60; // Frame 0, channel 2 upstream
|
||||
msg[6][2] = 32; // Frame 6, channel 1 downstream
|
||||
|
||||
// Equivalent to last line
|
||||
msg.setSample(icsneo::A2BMessage::A2BDirection::Downstream, 1, 6, 32);
|
||||
|
||||
// Get sample
|
||||
std::cout << "Channel 1 downstream sample for frame 6: " << msg.getSample(icsneo::A2BMessage::A2BDirection::Downstream, 1, 6).value() << std::endl;
|
||||
|
||||
// Get number of frames
|
||||
auto numFrames = msg.getNumFrames();
|
||||
std::cout << "Num frames: " << numFrames << std::endl;
|
||||
|
||||
icsneo::A2BPCMSample sample1 = 40;
|
||||
icsneo::A2BPCMSample sample2 = 60;
|
||||
msg.fill(sample1); // Fill whole message with sample 40
|
||||
|
||||
msg.fillFrame(sample2, numFrames/2); // Fill frame numFrames/2 with sample2
|
||||
|
||||
// Print msg sample contents
|
||||
std::cout << "A2B message contents:" << std::endl;
|
||||
for(size_t y = 0; y < numFrames; y++) {
|
||||
for(size_t x = 0; x < ((size_t)(msg.getNumChannels())*2); x++) { // Num channels including upstream and downstream
|
||||
std::cout << msg[y][x] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Set and get bits
|
||||
msg.setSyncFrameBit(true);
|
||||
|
||||
std::cout << "Was received from monitor: " << msg.isMonitorMsg() << std::endl;
|
||||
}
|
||||
|
||||
// Example 4: Packaging and transmitting sine wave using A2BMessage API
|
||||
void example4(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::cout << "Transmitting a 1000 hz sine wave." << std::endl;
|
||||
|
||||
float deltaTime = static_cast<float>(1.0/48000.0);
|
||||
float elapsedTime = 0.0;
|
||||
float twoPI = static_cast<float>(2.0*atan(1.0)*4.0);
|
||||
float frequency = 1000;
|
||||
float amplitude = static_cast<float>((1 << 23) - 1);
|
||||
|
||||
uint8_t icsChannel = 0; // Play audio on channel 2, upstream, see docs for details
|
||||
|
||||
|
||||
// Play for roughly 5 seconds
|
||||
while(elapsedTime < 5.0) {
|
||||
// Allocate message
|
||||
std::shared_ptr<icsneo::A2BMessage> a2bmsgPtr = std::make_shared<icsneo::A2BMessage>(static_cast<uint8_t>(4), false, static_cast<size_t>(2048));
|
||||
|
||||
icsneo::A2BMessage& a2bmsg = *a2bmsgPtr.get();
|
||||
a2bmsg.network = icsneo::Network(icsneo::Network::NetID::A2B2);
|
||||
|
||||
for(size_t frame = 0; frame < a2bmsg.getNumFrames(); frame++) {
|
||||
|
||||
// Sine wave sample, amplitude 1000, frequency 1000 hz
|
||||
|
||||
float contSample = amplitude*sin(twoPI*frequency*elapsedTime);
|
||||
icsneo::A2BPCMSample sample = static_cast<icsneo::A2BPCMSample>(contSample);
|
||||
|
||||
// Set sample for each frame in message
|
||||
a2bmsg[frame][icsChannel] = sample;
|
||||
|
||||
elapsedTime+=deltaTime;
|
||||
}
|
||||
|
||||
// Transmit message to device
|
||||
|
||||
if(!rada2b->transmit(a2bmsgPtr)) {
|
||||
std::cout << "Failed to transmit." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
rada2b->open();
|
||||
rada2b->goOnline();
|
||||
std::cout << rada2b->describe() << "\n";
|
||||
|
||||
auto handler1 = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("examples/cpp/a2b/src/out.wav")); // Starts writing A2B PCM data to out.wav
|
||||
}
|
||||
|
||||
auto handler2 = rada2b->addMessageCallback(
|
||||
std::make_shared<icsneo::MessageCallback>(
|
||||
[](std::shared_ptr<icsneo::Message> message) {
|
||||
std::cout << "Got in callback " << std::endl;
|
||||
if(message->type == icsneo::Message::Type::Frame) {
|
||||
std::shared_ptr<icsneo::Frame> frame = std::static_pointer_cast<icsneo::Frame>(message);
|
||||
|
||||
if(frame->network.getType() == icsneo::Network::Type::A2B) {
|
||||
std::shared_ptr<icsneo::A2BMessage> msg = std::static_pointer_cast<icsneo::A2BMessage>(frame);
|
||||
std::cout << "Got A2B Message" << std::endl;
|
||||
}
|
||||
}
|
||||
}));
|
||||
// Example 5: Wave loop back
|
||||
void example5(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5)); // captures 5 seconds of A2B data.
|
||||
auto listener = [&rada2b]() {
|
||||
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("looped.wav", 48000));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
rada2b->removeMessageCallback(handler);
|
||||
};
|
||||
|
||||
rada2b->removeMessageCallback(handler1);
|
||||
rada2b->removeMessageCallback(handler2);
|
||||
// Listen on another thread
|
||||
std::thread listenerThread{listener};
|
||||
|
||||
std::cout << "End A2B example\n";
|
||||
// Transmit wave file using example0
|
||||
|
||||
example0(rada2b);
|
||||
listenerThread.join();
|
||||
}
|
||||
|
||||
void displayUsage() {
|
||||
std::cout << "libicsneo A2B example" << std::endl;
|
||||
std::cout << "Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node" << std::endl;
|
||||
std::cout << "Options:" << std::endl;
|
||||
std::cout << "-h, --help\tDisplay help message." << std::endl;
|
||||
std::cout << "-e, --example [EXAMPLE_NUM]\tExample to run." << std::endl;
|
||||
std::cout << "Example usage: ./libicsneocpp-a2b.exe --example 1" << std::endl;
|
||||
std::cout << "Example usage: ./libicsneocpp-a2b.exe -h" << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Example options:" << std::endl;
|
||||
std::cout << "0\ttx" << std::endl;
|
||||
std::cout << "1\trx" << std::endl;
|
||||
std::cout << "2\tSet RADA2B settings" << std::endl;
|
||||
std::cout << "3\tA2BMessage API" << std::endl;
|
||||
std::cout << "4\tPackaging and transmitting sine wave using A2BMessage API" << std::endl;
|
||||
std::cout << "5\tWave loopback" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> arguments(argv, argv + argc);
|
||||
if(argc > 4 || argc == 1) {
|
||||
std::cerr << "Invalid usage." << std::endl;
|
||||
displayUsage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(std::any_of(arguments.begin(), arguments.end(), [](const std::string& arg) { return arg == "-h" || arg == "--help"; })) {
|
||||
displayUsage();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if(arguments[1] != "-e" && arguments[1] != "--example") {
|
||||
std::cerr << "Invalid usage." << std::endl;
|
||||
displayUsage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int option = atoi(arguments[2].c_str());
|
||||
|
||||
if(option < 0 || option > 5) {
|
||||
std::cerr << "Invalid usage." << std::endl;
|
||||
displayUsage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << icsneo::GetVersion() << std::endl;
|
||||
const auto& devices = icsneo::FindAllDevices();
|
||||
|
||||
auto it = std::find_if(devices.begin(), devices.end(), [&](const auto& dev) {
|
||||
const auto& txNetworks = dev->getSupportedTXNetworks();
|
||||
const auto& rxNetworks = dev->getSupportedRXNetworks();
|
||||
|
||||
if(std::none_of(txNetworks.begin(), txNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(std::none_of(rxNetworks.begin(), rxNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
if(it == devices.end()) {
|
||||
std::cerr << "Could not find RADA2B." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::shared_ptr<icsneo::Device> rada2b = *it;
|
||||
if(!rada2b->open()) {
|
||||
std::cout << "Failed to open RADA2B." << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
std::cout << "Opened RADA2B." << std::endl;
|
||||
}
|
||||
|
||||
if(!rada2b->goOnline()) {
|
||||
std::cout << "Failed to go online with RADA2B." << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
std::cout << "RADA2B online." << std::endl;
|
||||
}
|
||||
|
||||
switch(option) {
|
||||
case 0:
|
||||
example0(rada2b);
|
||||
break;
|
||||
case 1:
|
||||
example1(rada2b);
|
||||
break;
|
||||
case 2:
|
||||
example2(rada2b);
|
||||
break;
|
||||
case 3:
|
||||
example3();
|
||||
break;
|
||||
case 4:
|
||||
example4(rada2b);
|
||||
break;
|
||||
case 5:
|
||||
example5(rada2b);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
rada2b->goOffline();
|
||||
rada2b->close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneocpp-coremini VERSION 0.2.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Add an include directory like so if desired
|
||||
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# Enable Warnings
|
||||
if(MSVC)
|
||||
# Force to always compile with W4
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
# Add libicsneo, usually a git submodule within your project works well
|
||||
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
|
||||
|
||||
add_executable(libicsneocpp-coremini src/coremini.cpp)
|
||||
target_link_libraries(libicsneocpp-coremini icsneocpp)
|
||||
@@ -0,0 +1,68 @@
|
||||
// Usage:
|
||||
// ./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH]
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <icsneo/icsneocpp.h>
|
||||
|
||||
void displayUsage() {
|
||||
std::cout << "Usage:\n";
|
||||
std::cout << "./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH]\n";
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> arguments(argv, argv + argc);
|
||||
|
||||
if(arguments.size() != 3) {
|
||||
displayUsage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
std::cout << icsneo::GetVersion() << std::endl;
|
||||
|
||||
const auto& devices = icsneo::FindAllDevices();
|
||||
|
||||
auto it = std::find_if(
|
||||
devices.begin(),
|
||||
devices.end(),
|
||||
[&arguments](const auto &d)
|
||||
{ return d->getSerial() == arguments[1]; });
|
||||
|
||||
if(it == devices.end()) {
|
||||
std::cout << "Failed to find device." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<icsneo::Device> device = *it;
|
||||
if(!device->open()) {
|
||||
std::cout << "Failed to open device." << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(!device->goOnline()) {
|
||||
std::cout << "Failed to go online." << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!device->uploadCoremini(std::make_unique<std::ifstream>(arguments[2], std::ios::binary), icsneo::Disk::MemoryType::Flash))
|
||||
{
|
||||
std::cout << "Failed to upload coremini" << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
}
|
||||
|
||||
if (!device->startScript(icsneo::Disk::MemoryType::Flash))
|
||||
{
|
||||
std::cout << "Failed to start script" << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
}
|
||||
|
||||
device->goOffline();
|
||||
device->close();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneocpp-lin VERSION 0.1.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Add an include directory like so if desired
|
||||
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# Enable Warnings
|
||||
if(MSVC)
|
||||
# Force to always compile with W4
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
# Add libicsneo, usually a git submodule within your project works well
|
||||
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
|
||||
|
||||
add_executable(libicsneocpp-lin src/LINExample.cpp)
|
||||
target_link_libraries(libicsneocpp-lin icsneocpp)
|
||||
@@ -0,0 +1,151 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/communication/message/linmessage.h"
|
||||
|
||||
/* Note: This example requires LIN 1 and LIN 2 channels to be connected on the device */
|
||||
|
||||
char getCharInput(std::vector<char> allowed) {
|
||||
bool found = false;
|
||||
std::string input;
|
||||
|
||||
while(!found) {
|
||||
std::cin >> input;
|
||||
|
||||
if(input.length() == 1) {
|
||||
for(char compare : allowed) {
|
||||
if(compare == input.c_str()[0]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
std::cout << "Input did not match expected options. Please try again." << std::endl;
|
||||
std::cout << "<X or x to quit>" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return input.c_str()[0];
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Print version
|
||||
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||
std::cout << "\nFinding devices... " << std::flush;
|
||||
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
|
||||
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
|
||||
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
|
||||
|
||||
// List off the devices
|
||||
for(auto& device : devices)
|
||||
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
for(auto& device : devices) {
|
||||
std::cout << "Connecting to " << device->describe() << "... ";
|
||||
bool ret = device->open();
|
||||
if(!ret) { // Failed to open
|
||||
std::cout << "FAIL" << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
|
||||
std::cout << "\tGoing online... ";
|
||||
ret = device->goOnline();
|
||||
if(!ret) {
|
||||
std::cout << "FAIL" << std::endl;
|
||||
device->close();
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
// A real application would just check the result of icsneo_goOnline() rather than calling this
|
||||
// This function is intended to be called later on if needed
|
||||
std::cout << "\tChecking online status... ";
|
||||
ret = device->isOnline();
|
||||
if(!ret) {
|
||||
std::cout << "FAIL\n" << std::endl;
|
||||
device->close();
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
|
||||
if(icsneo::Message::Type::Frame == message->type) {
|
||||
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
|
||||
if(icsneo::Network::Type::LIN == frame->network.getType()) {
|
||||
auto msg = std::static_pointer_cast<icsneo::LINMessage>(message);
|
||||
std::cout << msg->network << " RX frame | ID: 0x" << std::hex << static_cast<int>(msg->ID) << " | ";
|
||||
std::cout << "Protected ID: 0x" << static_cast<int>(msg->protectedID) << "\n" << "Data: ";
|
||||
for(uint8_t& each : msg->data) {
|
||||
std::cout << "0x" << static_cast<int>(each) << " ";
|
||||
}
|
||||
std::cout << "\nChecksum type: " << (msg->isEnhancedChecksum ? "Enhanced" : "Classic");
|
||||
std::cout << "\nChecksum: 0x" << static_cast<int>(msg->checksum) << "\n";
|
||||
std::cout << "Is checksum valid: " << ((!msg->errFlags.ErrChecksumMatch) ? "yes" : "no") << "\n\n";
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// We can transmit messages
|
||||
std::cout << "\tTransmitting a LIN responder data frame... ";
|
||||
auto lin_r = std::make_shared<icsneo::LINMessage>();
|
||||
lin_r->network = icsneo::Network::NetID::LIN2;
|
||||
lin_r->ID = 0x11;
|
||||
lin_r->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
|
||||
lin_r->data = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44};
|
||||
ret = device->transmit(lin_r); // This will return false if the device does not support LIN
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
std::cout << "\tTransmitting a LIN commander frame... ";
|
||||
auto lin_c = std::make_shared<icsneo::LINMessage>();
|
||||
lin_c->network = icsneo::Network::NetID::LIN;
|
||||
lin_c->ID = 0x11;
|
||||
lin_c->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
|
||||
ret = device->transmit(lin_c);
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
|
||||
|
||||
std::cout << "\tTransmitting a LIN commander frame with responder data... ";
|
||||
auto lin_d = std::make_shared<icsneo::LINMessage>();
|
||||
lin_d->network = icsneo::Network::NetID::LIN;
|
||||
lin_d->ID = 0x22;
|
||||
lin_d->isEnhancedChecksum = true;
|
||||
lin_d->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
|
||||
lin_d->data = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd};
|
||||
ret = device->transmit(lin_d);
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
|
||||
std::cout << "<X or x to quit>\n\n";
|
||||
|
||||
// Go offline, stop sending and receiving traffic
|
||||
auto shutdown = [&](){
|
||||
device->removeMessageCallback(handler);
|
||||
std::cout << "\tGoing offline... ";
|
||||
ret = device->goOffline();
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
std::cout << "\tDisconnecting... ";
|
||||
ret = device->close();
|
||||
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
|
||||
};
|
||||
|
||||
while(true) {
|
||||
char input = getCharInput(std::vector<char> {'X', 'x'});
|
||||
switch(input) {
|
||||
case 'X':
|
||||
case 'x':
|
||||
shutdown();
|
||||
printf("Exiting program\n");
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneocpp-livedata VERSION 0.1.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Add an include directory like so if desired
|
||||
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# Enable Warnings
|
||||
if(MSVC)
|
||||
# Force to always compile with W4
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
# Add libicsneo, usually a git submodule within your project works well
|
||||
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
|
||||
|
||||
add_executable(libicsneocpp-livedata src/LiveDataExample.cpp)
|
||||
target_link_libraries(libicsneocpp-livedata icsneocpp)
|
||||
@@ -0,0 +1,91 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/communication/message/livedatamessage.h"
|
||||
#include "icsneo/communication/livedata.h"
|
||||
|
||||
int main() {
|
||||
// Print version
|
||||
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||
std::cout << "\nFinding devices... " << std::flush;
|
||||
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
|
||||
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
|
||||
|
||||
// List off the devices
|
||||
for(auto& device : devices)
|
||||
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
for(auto& device : devices) {
|
||||
std::cout << "Connecting to " << device->describe() << "... ";
|
||||
bool ret = device->open();
|
||||
if(!ret) { // Failed to open
|
||||
std::cout << "FAIL" << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
// Create a subscription message for the GPS signals
|
||||
std::cout << "\tSending a live data subscribe command... ";
|
||||
auto msg = std::make_shared<icsneo::LiveDataCommandMessage>();
|
||||
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE);
|
||||
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LONGITUDE);
|
||||
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY);
|
||||
msg->cmd = icsneo::LiveDataCommand::SUBSCRIBE;
|
||||
msg->handle = icsneo::LiveDataUtil::getNewHandle();
|
||||
msg->updatePeriod = std::chrono::milliseconds(100);
|
||||
msg->expirationTime = std::chrono::milliseconds(0);
|
||||
// Transmit the subscription message
|
||||
ret = device->subscribeLiveData(msg);
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
// Register a handler that uses the data after it arrives every ~100ms
|
||||
std::cout << "\tStreaming messages for 3 seconds... " << std::endl << std::endl;
|
||||
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::LiveData);
|
||||
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>(filter, [&msg](std::shared_ptr<icsneo::Message> message) {
|
||||
auto ldMsg = std::dynamic_pointer_cast<icsneo::LiveDataMessage>(message);
|
||||
switch(ldMsg->cmd) {
|
||||
case icsneo::LiveDataCommand::STATUS: {
|
||||
auto msg2 = std::dynamic_pointer_cast<icsneo::LiveDataStatusMessage>(message);
|
||||
std::cout << "[Handle] " << ldMsg->handle << std::endl;
|
||||
std::cout << "[Requested Command] " << msg2->requestedCommand << std::endl;
|
||||
std::cout << "[Status] " << msg2->status << std::endl << std::endl;
|
||||
break;
|
||||
}
|
||||
case icsneo::LiveDataCommand::RESPONSE: {
|
||||
auto valueMsg = std::dynamic_pointer_cast<icsneo::LiveDataValueMessage>(message);
|
||||
if((valueMsg->handle == msg->handle) && (valueMsg->values.size() == msg->args.size()))
|
||||
{
|
||||
std::cout << "[Handle] " << msg->handle << std::endl;
|
||||
std::cout << "[Values] " << valueMsg->numArgs << std::endl;
|
||||
for(uint32_t i = 0; i < valueMsg->numArgs; ++i) {
|
||||
std::cout << "[" << msg->args[i]->valueType << "] ";
|
||||
auto scaledValue = icsneo::LiveDataUtil::liveDataValueToDouble(*valueMsg->values[i]);
|
||||
std::cout << scaledValue << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: // Ignoring other commands
|
||||
break;
|
||||
}
|
||||
}));
|
||||
// Run handler for three seconds to observe the signal data
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
// Unsubscribe from the GPS signals and run handler for one more second
|
||||
// Unsubscription only requires a valid in-use handle, in this case from our previous subscription
|
||||
ret = device->unsubscribeLiveData(msg->handle);
|
||||
// The handler should no longer print values
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
device->removeMessageCallback(handler);
|
||||
std::cout << "\tDisconnecting... ";
|
||||
ret = device->close();
|
||||
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneocpp-mdio VERSION 0.1.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Add an include directory like so if desired
|
||||
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# Enable Warnings
|
||||
if(MSVC)
|
||||
# Force to always compile with W4
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
# Add libicsneo, usually a git submodule within your project works well
|
||||
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
|
||||
|
||||
add_executable(libicsneocpp-mdio src/MDIOExample.cpp)
|
||||
target_link_libraries(libicsneocpp-mdio icsneocpp)
|
||||
@@ -0,0 +1,221 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/communication/message/mdiomessage.h"
|
||||
|
||||
/* Note: This example requires MDIO 1 channels to be connected on the device, and uses RADGalaxy or RADStar2 */
|
||||
|
||||
char getCharInput(std::vector<char> allowed)
|
||||
{
|
||||
bool found = false;
|
||||
std::string input;
|
||||
|
||||
while (!found)
|
||||
{
|
||||
std::cin >> input;
|
||||
|
||||
if (input.length() == 1)
|
||||
{
|
||||
for (char compare : allowed)
|
||||
{
|
||||
if (compare == input.c_str()[0])
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
std::cout << "Input did not match expected options. Please try again." << std::endl;
|
||||
std::cout << "<X or x to quit>" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return input.c_str()[0];
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Print version
|
||||
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||
std::cout << "\nFinding devices... " << std::flush;
|
||||
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
|
||||
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
|
||||
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
|
||||
|
||||
// List off the devices
|
||||
for (auto &device : devices)
|
||||
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
for (auto &device : devices)
|
||||
{
|
||||
std::cout << "Connecting to " << device->describe() << "... ";
|
||||
bool ret = device->open();
|
||||
if (!ret)
|
||||
{ // Failed to open
|
||||
std::cout << "FAIL" << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
|
||||
std::cout << "\tGoing online... ";
|
||||
ret = device->goOnline();
|
||||
if (!ret)
|
||||
{
|
||||
std::cout << "FAIL" << std::endl;
|
||||
device->close();
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
// A real application would just check the result of icsneo_goOnline() rather than calling this
|
||||
// This function is intended to be called later on if needed
|
||||
std::cout << "\tChecking online status... ";
|
||||
ret = device->isOnline();
|
||||
if (!ret)
|
||||
{
|
||||
std::cout << "FAIL\n"
|
||||
<< std::endl;
|
||||
device->close();
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message)
|
||||
{
|
||||
if(icsneo::Message::Type::Frame == message->type) {
|
||||
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
|
||||
if(icsneo::Network::Type::MDIO == frame->network.getType()) {
|
||||
auto msg = std::static_pointer_cast<icsneo::MDIOMessage>(message);
|
||||
std::cout << msg->network << " " << ((msg->isTXMsg)? "TX" : "RX") << " frame\n";
|
||||
std::cout << "Clause: " << ((msg->clause == icsneo::MDIOMessage::Clause::Clause22) ? "22" : "45") << "\n";
|
||||
std::cout << "Direction: " << ((msg->direction == icsneo::MDIOMessage::Direction::Read) ? "Read" : "Write") << "\n";
|
||||
std::cout << std::hex << "PHY Address: 0x" << static_cast<int>(msg->phyAddress) << "\n";
|
||||
if(msg->clause == icsneo::MDIOMessage::Clause::Clause45)
|
||||
std::cout << std::hex << "Dev Address: 0x" << static_cast<int>(msg->devAddress) << "\n";
|
||||
std::cout << std::hex << "Reg Address: 0x" << static_cast<int>(msg->regAddress) << "\n";
|
||||
std::cout << "Data: \n";
|
||||
for(uint8_t& each : msg->data) {
|
||||
std::cout << std::hex << "0x" << static_cast<int>(each) << " ";
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
} }));
|
||||
|
||||
/*
|
||||
* BCM89810 on RADGalaxy/RADGigastar
|
||||
*/
|
||||
// We can transmit messages to read the PHY ID of BCM89810 PHY
|
||||
std::cout << "\tTransmitting a MDIO request to read ID on BCM89810...\n";
|
||||
auto mdio_r = std::make_shared<icsneo::MDIOMessage>();
|
||||
mdio_r->network = icsneo::Network::NetID::MDIO1;
|
||||
mdio_r->phyAddress = 0x00u;
|
||||
mdio_r->regAddress = 0x02u;
|
||||
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
|
||||
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
|
||||
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
// We can transmit messages to write to arbitrary register
|
||||
std::cout << "\tTransmitting a MDIO request to write register on BCM89810...\n";
|
||||
mdio_r = std::make_shared<icsneo::MDIOMessage>();
|
||||
mdio_r->network = icsneo::Network::NetID::MDIO1;
|
||||
mdio_r->phyAddress = 0x00u;
|
||||
mdio_r->regAddress = 0x1Bu;
|
||||
mdio_r->data = {0xAA, 0xAF};
|
||||
mdio_r->direction = icsneo::MDIOMessage::Direction::Write;
|
||||
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
|
||||
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
// We can transmit messages to read back to arbitrary register
|
||||
std::cout << "\tTransmitting a MDIO request to read register on BCM89810...\n";
|
||||
mdio_r = std::make_shared<icsneo::MDIOMessage>();
|
||||
mdio_r->network = icsneo::Network::NetID::MDIO1;
|
||||
mdio_r->phyAddress = 0x00u;
|
||||
mdio_r->regAddress = 0x1Bu;
|
||||
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
|
||||
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
|
||||
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
/*
|
||||
* 88Q2112 on RADGigastar, RADSupermoon, RADMoon2
|
||||
*/
|
||||
// We can transmit messages to read the PHY ID of BCM89810 PHY
|
||||
std::cout << "\tTransmitting a MDIO request to read ID on 88Q2112...\n";
|
||||
mdio_r = std::make_shared<icsneo::MDIOMessage>();
|
||||
mdio_r->network = icsneo::Network::NetID::MDIO1;
|
||||
mdio_r->phyAddress = 0x06u;
|
||||
mdio_r->devAddress = 0x01u;
|
||||
mdio_r->regAddress = 0x0002u;
|
||||
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
|
||||
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
|
||||
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
// We can transmit messages to write to arbitrary register
|
||||
std::cout << "\tTransmitting a MDIO request to write register on 88Q2112...\n";
|
||||
mdio_r = std::make_shared<icsneo::MDIOMessage>();
|
||||
mdio_r->network = icsneo::Network::NetID::MDIO1;
|
||||
mdio_r->phyAddress = 0x06u;
|
||||
mdio_r->devAddress = 0x01u;
|
||||
mdio_r->regAddress = 0x0902u;
|
||||
mdio_r->data = {0xA3, 0x02};
|
||||
mdio_r->direction = icsneo::MDIOMessage::Direction::Write;
|
||||
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
|
||||
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
// We can transmit messages to read back to arbitrary register
|
||||
std::cout << "\tTransmitting a MDIO request to read register on 88Q2112...\n";
|
||||
mdio_r = std::make_shared<icsneo::MDIOMessage>();
|
||||
mdio_r->network = icsneo::Network::NetID::MDIO1;
|
||||
mdio_r->phyAddress = 0x06u;
|
||||
mdio_r->devAddress = 0x01u;
|
||||
mdio_r->regAddress = 0x0902u;
|
||||
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
|
||||
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
|
||||
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
std::cout << "<X or x to quit>\n\n";
|
||||
|
||||
// Go offline, stop sending and receiving traffic
|
||||
auto shutdown = [&]()
|
||||
{
|
||||
device->removeMessageCallback(handler);
|
||||
std::cout << "\tGoing offline... ";
|
||||
ret = device->goOffline();
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
std::cout << "\tDisconnecting... ";
|
||||
ret = device->close();
|
||||
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
|
||||
};
|
||||
|
||||
while (true)
|
||||
{
|
||||
char input = getCharInput(std::vector<char>{'X', 'x'});
|
||||
switch (input)
|
||||
{
|
||||
case 'X':
|
||||
case 'x':
|
||||
shutdown();
|
||||
printf("Exiting program\n");
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -24,10 +24,4 @@ endif()
|
||||
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
|
||||
|
||||
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
|
||||
target_link_libraries(libicsneocpp-simple-example icsneocpp)
|
||||
add_executable(libicsneocpp-simple-rx src/SimpleRx.cpp)
|
||||
add_executable(libicsneocpp-simple-tx src/SimpleTx.cpp)
|
||||
target_link_libraries(libicsneocpp-simple-rx icsneocpp)
|
||||
target_link_libraries(libicsneocpp-simple-tx icsneocpp)
|
||||
add_executable(libicsneocpp-simple-events src/SimpleEvents.cpp)
|
||||
target_link_libraries(libicsneocpp-simple-events icsneocpp)
|
||||
target_link_libraries(libicsneocpp-simple-example icsneocpp)
|
||||
@@ -1,57 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
if(args.size() != 2) {
|
||||
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto& deviceSerial = args[1];
|
||||
|
||||
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
|
||||
for(const auto& dev : icsneo::FindAllDevices()) {
|
||||
if(serial == dev->getSerial())
|
||||
return dev;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
std::cout << "Finding device... " << std::flush;
|
||||
const auto device = findDevice(deviceSerial);
|
||||
if(!device) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Opening device... " << std::flush;
|
||||
if(!device->open()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Going online... " << std::flush;
|
||||
if(!device->goOnline()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Getting events from server..." << std::endl;
|
||||
icsneo::EventFilter ef = {};
|
||||
auto events = icsneo::GetEvents(ef, 0UL);
|
||||
if(events.size() == 0)
|
||||
std::cout << "Event return is empty :(" << std::endl;
|
||||
|
||||
for(auto& event: events)
|
||||
std::cout << event.describe() << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5,16 +5,14 @@
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4996) // STL time functions
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
// Print version
|
||||
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||
|
||||
// Register an event callback so we can see any errors that come in
|
||||
icsneo::EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
icsneo::EventManager::GetInstance().addEventCallback(icsneo::EventCallback([](std::shared_ptr<icsneo::APIEvent> evt) {
|
||||
std::cerr << evt->describe() << std::endl;
|
||||
}));
|
||||
|
||||
std::cout<< "Supported devices:" << std::endl;
|
||||
for(auto& dev : icsneo::GetSupportedDevices())
|
||||
std::cout << '\t' << dev.getGenericProductName() << std::endl;
|
||||
@@ -102,9 +100,31 @@ int main() {
|
||||
std::cout << "\tSetting settings permanently... ";
|
||||
ret = device->settings->apply();
|
||||
std::cout << (ret ? "OK\n\n" : "FAIL\n\n");
|
||||
|
||||
const auto getRTC = [&]() {
|
||||
std::cout << "\tGetting RTC... ";
|
||||
const auto rtc = device->getRTC();
|
||||
if(!rtc) {
|
||||
std::cout << "FAIL" << std::endl;
|
||||
return;
|
||||
}
|
||||
const auto time = std::chrono::system_clock::to_time_t(*rtc);
|
||||
const auto timeInfo = std::gmtime(&time);
|
||||
std::cout << "OK, " << std::put_time(timeInfo, "%Y-%m-%d %H:%M:%S") << std::endl;
|
||||
};
|
||||
// Set the real time clock on the device using the system clock
|
||||
// First, let's see if we can get the time from the device (if it has an RTC)
|
||||
getRTC();
|
||||
|
||||
// Now, set the time using the system's clock so that we can check it again to ensure it's set
|
||||
std::cout << "\tSetting RTC to system_clock::now()... ";
|
||||
std::cout << (device->setRTC(std::chrono::system_clock::now()) ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
// Get the time again after setting
|
||||
getRTC();
|
||||
|
||||
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
|
||||
std::cout << "\tGoing online... ";
|
||||
std::cout << "\n\tGoing online... ";
|
||||
ret = device->goOnline();
|
||||
if(!ret) {
|
||||
std::cout << "FAIL" << std::endl;
|
||||
@@ -133,7 +153,7 @@ int main() {
|
||||
// Keep in mind that 20k messages comes quickly at high bus loads!
|
||||
|
||||
// We can transmit messages
|
||||
std::cout << "\tTransmitting an extended CAN FD frame... ";
|
||||
std::cout << "\n\tTransmitting an extended CAN FD frame... ";
|
||||
auto txMessage5 = std::make_shared<icsneo::CANMessage>();
|
||||
txMessage5->network = icsneo::Network::NetID::HSCAN;
|
||||
txMessage5->arbid = 0x1C5001C5;
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
if(args.size() != 2) {
|
||||
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto& deviceSerial = args[1];
|
||||
|
||||
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
|
||||
for(const auto& dev : icsneo::FindAllDevices()) {
|
||||
if(serial == dev->getSerial())
|
||||
return dev;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
std::cout << "Finding device... " << std::flush;
|
||||
const auto device = findDevice(deviceSerial);
|
||||
if(!device) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Opening device... " << std::flush;
|
||||
if(!device->open()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Going online... " << std::flush;
|
||||
if(!device->goOnline()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Streaming CAN messages in, enter anything to stop..." << std::endl;
|
||||
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
|
||||
if(message->type == icsneo::Message::Type::Frame) {
|
||||
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
|
||||
if(frame->network.getType() == icsneo::Network::Type::CAN) {
|
||||
auto canMessage = std::static_pointer_cast<icsneo::CANMessage>(message);
|
||||
std::cout << '\r';
|
||||
for(auto& databyte : canMessage->data)
|
||||
std::cout << std::hex << std::setw(2) << (uint32_t)databyte << ' ';
|
||||
std::cout << std::flush;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
std::cin.ignore();
|
||||
|
||||
device->removeMessageCallback(handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
if (args.size() != 2) {
|
||||
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto& deviceSerial = args[1];
|
||||
|
||||
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
|
||||
for (const auto& dev : icsneo::FindAllDevices()) {
|
||||
if (serial == dev->getSerial())
|
||||
return dev;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
std::cout << "Finding device... " << std::flush;
|
||||
const auto device = findDevice(deviceSerial);
|
||||
if (!device) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Opening device... " << std::flush;
|
||||
if (!device->open()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::cout << "Going online... " << std::flush;
|
||||
if (!device->goOnline()) {
|
||||
std::cerr << "FAIL" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
std::atomic<bool> stop = false;
|
||||
|
||||
std::thread thread([&] {
|
||||
std::cin.ignore();
|
||||
std::cout << "Stopping..." << std::endl;
|
||||
stop = true;
|
||||
});
|
||||
|
||||
auto txMessage = std::make_shared<icsneo::CANMessage>();
|
||||
txMessage->network = icsneo::Network::NetID::HSCAN;
|
||||
txMessage->arbid = 0x1C5001C5;
|
||||
txMessage->data.insert(txMessage->data.begin(), sizeof(size_t), 0);
|
||||
txMessage->isExtended = true;
|
||||
txMessage->isCANFD = true;
|
||||
|
||||
std::cout << "Streaming CAN messages out, enter anything to stop..." << std::endl;
|
||||
|
||||
size_t& value = *(size_t*)txMessage->data.data();
|
||||
|
||||
while (!stop) {
|
||||
device->transmit(txMessage);
|
||||
value++;
|
||||
}
|
||||
|
||||
thread.join();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3511,16 +3511,6 @@ SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_ISO_get() {
|
||||
}
|
||||
|
||||
|
||||
SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_ISOPIC_get() {
|
||||
int jresult ;
|
||||
int result;
|
||||
|
||||
result = (int)(10);
|
||||
jresult = result;
|
||||
return jresult;
|
||||
}
|
||||
|
||||
|
||||
SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_MAIN51_get() {
|
||||
int jresult ;
|
||||
int result;
|
||||
|
||||
@@ -325,7 +325,6 @@ public class icsneocsharp {
|
||||
public static readonly int ICSNEO_NETID_AUX = icsneocsharpPINVOKE.ICSNEO_NETID_AUX_get();
|
||||
public static readonly int ICSNEO_NETID_J1850VPW = icsneocsharpPINVOKE.ICSNEO_NETID_J1850VPW_get();
|
||||
public static readonly int ICSNEO_NETID_ISO = icsneocsharpPINVOKE.ICSNEO_NETID_ISO_get();
|
||||
public static readonly int ICSNEO_NETID_ISOPIC = icsneocsharpPINVOKE.ICSNEO_NETID_ISOPIC_get();
|
||||
public static readonly int ICSNEO_NETID_MAIN51 = icsneocsharpPINVOKE.ICSNEO_NETID_MAIN51_get();
|
||||
public static readonly int ICSNEO_NETID_RED = icsneocsharpPINVOKE.ICSNEO_NETID_RED_get();
|
||||
public static readonly int ICSNEO_NETID_SCI = icsneocsharpPINVOKE.ICSNEO_NETID_SCI_get();
|
||||
|
||||
@@ -1017,9 +1017,6 @@ class icsneocsharpPINVOKE {
|
||||
[global::System.Runtime.InteropServices.DllImport("icsneocsharp.dll", EntryPoint="CSharp_ICSNEO_NETID_ISO_get")]
|
||||
public static extern int ICSNEO_NETID_ISO_get();
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("icsneocsharp.dll", EntryPoint="CSharp_ICSNEO_NETID_ISOPIC_get")]
|
||||
public static extern int ICSNEO_NETID_ISOPIC_get();
|
||||
|
||||
[global::System.Runtime.InteropServices.DllImport("icsneocsharp.dll", EntryPoint="CSharp_ICSNEO_NETID_MAIN51_get")]
|
||||
public static extern int ICSNEO_NETID_MAIN51_get();
|
||||
|
||||
|
||||
+727
-739
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
package com.example.icsneojava;
|
||||
/* ----------------------------------------------------------------------------
|
||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||
* Version 4.0.0
|
||||
|
||||
@@ -20,7 +20,6 @@ public interface icsneojavaConstants {
|
||||
public final static int ICSNEO_NETID_AUX = icsneojavaJNI.ICSNEO_NETID_AUX_get();
|
||||
public final static int ICSNEO_NETID_J1850VPW = icsneojavaJNI.ICSNEO_NETID_J1850VPW_get();
|
||||
public final static int ICSNEO_NETID_ISO = icsneojavaJNI.ICSNEO_NETID_ISO_get();
|
||||
public final static int ICSNEO_NETID_ISOPIC = icsneojavaJNI.ICSNEO_NETID_ISOPIC_get();
|
||||
public final static int ICSNEO_NETID_MAIN51 = icsneojavaJNI.ICSNEO_NETID_MAIN51_get();
|
||||
public final static int ICSNEO_NETID_RED = icsneojavaJNI.ICSNEO_NETID_RED_get();
|
||||
public final static int ICSNEO_NETID_SCI = icsneojavaJNI.ICSNEO_NETID_SCI_get();
|
||||
|
||||
@@ -283,7 +283,6 @@ public class icsneojavaJNI {
|
||||
public final static native int ICSNEO_NETID_AUX_get();
|
||||
public final static native int ICSNEO_NETID_J1850VPW_get();
|
||||
public final static native int ICSNEO_NETID_ISO_get();
|
||||
public final static native int ICSNEO_NETID_ISOPIC_get();
|
||||
public final static native int ICSNEO_NETID_MAIN51_get();
|
||||
public final static native int ICSNEO_NETID_RED_get();
|
||||
public final static native int ICSNEO_NETID_SCI_get();
|
||||
|
||||
+67
-68
@@ -4,49 +4,15 @@
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4201) // nameless struct/union
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const char* description;
|
||||
time_t timestamp;
|
||||
uint32_t eventNumber;
|
||||
uint8_t severity;
|
||||
char serial[7];
|
||||
} neoeventcontext_t;
|
||||
|
||||
typedef struct {
|
||||
time_t timestamp;
|
||||
union {
|
||||
struct {
|
||||
uint32_t eventNumber;
|
||||
int8_t severity;
|
||||
char serial[7];
|
||||
};
|
||||
neoeventcontext_t eventContext;
|
||||
};
|
||||
} neosocketevent_t;
|
||||
|
||||
typedef neoeventcontext_t neosocketeventfilter_t;
|
||||
|
||||
typedef struct {
|
||||
const char* description;
|
||||
union {
|
||||
struct {
|
||||
time_t timestamp;
|
||||
uint32_t eventNumber;
|
||||
uint8_t severity;
|
||||
char serial[7];
|
||||
};
|
||||
neosocketevent_t socketEvent;
|
||||
};
|
||||
uint8_t reserved[16];
|
||||
} neoevent_t;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <vector>
|
||||
@@ -83,7 +49,6 @@ public:
|
||||
ValueNotYetPresent = 0x1013,
|
||||
Timeout = 0x1014,
|
||||
WiVINotSupported = 0x1015,
|
||||
TestEvent = 0x1016,
|
||||
|
||||
// Device Events
|
||||
PollingMessageOverflow = 0x2000,
|
||||
@@ -126,6 +91,19 @@ public:
|
||||
WiVIUploadStackOverflow = 0x2037,
|
||||
I2CMessageExceedsMaxLength = 0x2038,
|
||||
A2BMessageIncompleteFrame = 0x2039,
|
||||
CoreminiUploadVersionMismatch = 0x2040,
|
||||
DiskNotConnected = 0x2041,
|
||||
UnexpectedResponse = 0x2042,
|
||||
LiveDataInvalidHandle = 0x2043,
|
||||
LiveDataInvalidCommand = 0x2044,
|
||||
LiveDataInvalidArgument = 0x2045,
|
||||
LiveDataVersionMismatch = 0x2046,
|
||||
LiveDataNoDeviceResponse = 0x2047,
|
||||
LiveDataMaxSignalsReached = 0x2048,
|
||||
LiveDataCommandFailed = 0x2049,
|
||||
LiveDataEncoderError = 0x2050,
|
||||
LiveDataDecoderError = 0x2051,
|
||||
LiveDataNotSupported = 0x2052,
|
||||
|
||||
// Transport Events
|
||||
FailedToRead = 0x3000,
|
||||
@@ -138,35 +116,62 @@ public:
|
||||
PCAPCouldNotStart = 0x3102,
|
||||
PCAPCouldNotFindDevices = 0x3103,
|
||||
PacketDecodingError = 0x3104,
|
||||
SocketFailedToOpen = 0x3105,
|
||||
FailedToBind = 0x3106,
|
||||
ErrorSettingSocketOption = 0x3107,
|
||||
GetIfAddrsError = 0x3108,
|
||||
SendToError = 0x3109,
|
||||
MDIOMessageExceedsMaxLength = 0x3110,
|
||||
DriverTTYPathEmpty = 0x3120,
|
||||
DriverWasNotNegOne = 0x3121,
|
||||
DriverTCGetAddrFail = 0x3122,
|
||||
DriverTCSetAddrFail = 0x3123,
|
||||
|
||||
// Device Sharing Server Events
|
||||
SharedMemoryDataIsNull = 0x4001,
|
||||
SharedMemoryFailedToClose = 0x4002,
|
||||
SharedMemoryFailedToOpen = 0x4003,
|
||||
SharedMemoryFailedToUnlink = 0x4004,
|
||||
SharedMemoryFileTruncateError = 0x4005,
|
||||
SharedMemoryMappingError = 0x4006,
|
||||
SharedMemoryUnmapError = 0x4007,
|
||||
SharedSemaphoreFailedToClose = 0x4008,
|
||||
SharedSemaphoreFailedToOpen = 0x4009,
|
||||
SharedSemaphoreFailedToPost = 0x4010,
|
||||
SharedSemaphoreFailedToUnlink = 0x4011,
|
||||
SharedSemaphoreFailedToWait = 0x4012,
|
||||
SharedSemaphoreNotOpenForPost = 0x4013,
|
||||
SharedSemaphoreNotOpenForWait = 0x4014,
|
||||
SocketFailedToOpen = 0x4015,
|
||||
SocketFailedToClose = 0x4016,
|
||||
SocketFailedToConnect = 0x4017,
|
||||
SocketFailedToRead = 0x4018,
|
||||
SocketFailedToWrite = 0x4019,
|
||||
SocketAcceptorFailedToBind = 0x4020,
|
||||
SocketAcceptorFailedToListen = 0x4021,
|
||||
|
||||
// Other Errors
|
||||
// FTD3XX
|
||||
FTOK = 0x4000, // placeholder
|
||||
FTInvalidHandle = FTOK + 1,
|
||||
FTDeviceNotFound = FTOK + 2,
|
||||
FTDeviceNotOpened = FTOK + 3,
|
||||
FTIOError = FTOK + 4,
|
||||
FTInsufficientResources = FTOK + 5,
|
||||
FTInvalidParameter = FTOK + 6,
|
||||
FTInvalidBaudRate = FTOK + 7,
|
||||
FTDeviceNotOpenedForErase = FTOK + 8,
|
||||
FTDeviceNotOpenedForWrite = FTOK + 9,
|
||||
FTFailedToWriteDevice = FTOK + 10,
|
||||
FTEEPROMReadFailed = FTOK + 11,
|
||||
FTEEPROMWriteFailed = FTOK + 12,
|
||||
FTEEPROMEraseFailed = FTOK + 13,
|
||||
FTEEPROMNotPresent = FTOK + 14,
|
||||
FTEEPROMNotProgrammed = FTOK + 15,
|
||||
FTInvalidArgs = FTOK + 16,
|
||||
FTNotSupported = FTOK + 17,
|
||||
FTNoMoreItems = FTOK + 18,
|
||||
FTTimeout = FTOK + 19,
|
||||
FTOperationAborted = FTOK + 20,
|
||||
FTReservedPipe = FTOK + 21,
|
||||
FTInvalidControlRequestDirection = FTOK + 22,
|
||||
FTInvalidControlRequestType = FTOK + 23,
|
||||
FTIOPending = FTOK + 24,
|
||||
FTIOIncomplete = FTOK + 25,
|
||||
FTHandleEOF = FTOK + 26,
|
||||
FTBusy = FTOK + 27,
|
||||
FTNoSystemResources = FTOK + 28,
|
||||
FTDeviceListNotReady = FTOK + 29,
|
||||
FTDeviceNotConnected = FTOK + 30,
|
||||
FTIncorrectDevicePath = FTOK + 31,
|
||||
FTOtherError = FTOK + 32,
|
||||
|
||||
// Android USB
|
||||
AndroidUSBLibusbInitFailed = 0x5000,
|
||||
AndroidUSBFDWrapFailed = 0x5001,
|
||||
|
||||
NoErrorFound = 0xFFFFFFFD,
|
||||
TooManyEvents = 0xFFFFFFFE,
|
||||
Unknown = 0xFFFFFFFF
|
||||
Unknown = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
enum class Severity : uint8_t {
|
||||
Any = 0, // Used for filtering, should not appear in data
|
||||
EventInfo = 0x10,
|
||||
@@ -176,10 +181,8 @@ public:
|
||||
|
||||
APIEvent() : eventStruct({}), serial(), timepoint(), device(nullptr) {}
|
||||
APIEvent(APIEvent::Type event, APIEvent::Severity severity, const Device* device = nullptr);
|
||||
APIEvent(neosocketevent_t evStruct, const Device* device = nullptr);
|
||||
|
||||
const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; }
|
||||
neosocketevent_t getNeoSocketEvent() const noexcept;
|
||||
Type getType() const noexcept { return Type(eventStruct.eventNumber); }
|
||||
Severity getSeverity() const noexcept { return Severity(eventStruct.severity); }
|
||||
std::string getDescription() const noexcept { return std::string(eventStruct.description); }
|
||||
@@ -218,12 +221,8 @@ public:
|
||||
EventFilter(const Device* device, APIEvent::Severity severity) : severity(severity), matchOnDevicePtr(true), device(device) {}
|
||||
EventFilter(std::string serial, APIEvent::Type type = APIEvent::Type::Any, APIEvent::Severity severity = APIEvent::Severity::Any) : type(type), severity(severity), serial(serial) {}
|
||||
EventFilter(std::string serial, APIEvent::Severity severity) : severity(severity), serial(serial) {}
|
||||
EventFilter(neosocketeventfilter_t evFilterSt) : type(static_cast<APIEvent::Type>(evFilterSt.eventNumber)),
|
||||
severity(static_cast<APIEvent::Severity>(evFilterSt.severity)),
|
||||
serial(std::string(evFilterSt.serial)) {}
|
||||
|
||||
bool match(const APIEvent& event) const noexcept;
|
||||
neosocketeventfilter_t getNeoSocketEventFilter() const noexcept;
|
||||
|
||||
APIEvent::Type type = APIEvent::Type::Any;
|
||||
APIEvent::Severity severity = APIEvent::Severity::Any;
|
||||
@@ -236,4 +235,4 @@ public:
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include "icsneo/api/event.h"
|
||||
#include "icsneo/api/eventcallback.h"
|
||||
|
||||
@@ -35,8 +34,6 @@ public:
|
||||
// If this thread exists in the map, turn off downgrading
|
||||
void cancelErrorDowngradingOnCurrentThread();
|
||||
|
||||
void removeEventMirror(const std::thread::id& id);
|
||||
|
||||
bool isDowngradingErrorsOnCurrentThread() const;
|
||||
|
||||
int addEventCallback(const EventCallback &cb);
|
||||
@@ -111,10 +108,6 @@ private:
|
||||
bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow
|
||||
|
||||
void discardOldest(size_t count = 1);
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
std::optional<std::vector<neosocketevent_t>> getServerEvents(const size_t& max);
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ enum class Command : uint8_t {
|
||||
NeoWriteMemory = 0x41,
|
||||
ClearCoreMini = 0x42,
|
||||
LoadCoreMini = 0x43,
|
||||
GetRTC = 0x49,
|
||||
SetRTC = 0x50,
|
||||
RequestSerialNumber = 0xA1,
|
||||
GetMainVersion = 0xA3, // Previously known as RED_CMD_APP_VERSION_REQ
|
||||
SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM
|
||||
@@ -29,10 +31,11 @@ enum class Command : uint8_t {
|
||||
GetVBattReq = 0xDF, // Previously known as RED_CMD_VBATT_REQUEST
|
||||
ScriptStatus = 0xE0, // Previously known as RED_CMD_SCRIPT_STATUS
|
||||
MiscControl = 0xE7,
|
||||
Extended = 0xF0,
|
||||
Extended = 0xF0, // Previously known as RED_CMD_EXT_COMM
|
||||
ExtendedData = 0xF2, // Previously known as RED_CMD_EXTENDED_DATA
|
||||
FlexRayControl = 0xF3,
|
||||
CoreMiniPreload = 0xF4, // Previously known as RED_CMD_COREMINI_PRELOAD
|
||||
PHYControlRegisters = 0xEF
|
||||
PHYControlRegisters = 0xEF,
|
||||
};
|
||||
|
||||
enum class ExtendedCommand : uint16_t {
|
||||
@@ -45,8 +48,12 @@ enum class ExtendedCommand : uint16_t {
|
||||
Extract = 0x0015,
|
||||
StartDHCPServer = 0x0016,
|
||||
StopDHCPServer = 0x0017,
|
||||
GetSupportedFeatures = 0x0018,
|
||||
GetComponentVersions = 0x001A,
|
||||
Reboot = 0x001C,
|
||||
SetUploadedFlag = 0x0027,
|
||||
GenericBinaryInfo = 0x0030,
|
||||
LiveData = 0x0035,
|
||||
};
|
||||
|
||||
enum class ExtendedResponse : int32_t {
|
||||
@@ -58,6 +65,20 @@ enum class ExtendedResponse : int32_t {
|
||||
InvalidParameter = -5,
|
||||
};
|
||||
|
||||
enum class ExtendedDataSubCommand : uint32_t {
|
||||
GenericBinaryRead = 13,
|
||||
};
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct ExtendedCommandHeader {
|
||||
uint8_t netid; // should be Main51
|
||||
uint16_t fullLength;
|
||||
uint8_t command; // should be Command::Extended
|
||||
uint16_t extendedCommand;
|
||||
uint16_t payloadLength;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "icsneo/communication/message/callback/messagecallback.h"
|
||||
#include "icsneo/communication/message/serialnumbermessage.h"
|
||||
#include "icsneo/communication/message/logicaldiskinfomessage.h"
|
||||
#include "icsneo/communication/message/componentversionsmessage.h"
|
||||
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||
#include "icsneo/device/deviceversion.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
@@ -21,14 +23,11 @@
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class Communication {
|
||||
public:
|
||||
typedef std::function<void(std::vector<uint8_t>&)> RawCallback;
|
||||
|
||||
// Note that the Packetizer is not created by the constructor,
|
||||
// and should be done once the Communication module is in place.
|
||||
Communication(
|
||||
@@ -48,7 +47,6 @@ public:
|
||||
void modeChangeIncoming() { driver->modeChangeIncoming(); }
|
||||
void awaitModeChangeComplete() { driver->awaitModeChangeComplete(); }
|
||||
bool rawWrite(const std::vector<uint8_t>& bytes) { return driver->write(bytes); }
|
||||
void modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb);
|
||||
virtual bool sendPacket(std::vector<uint8_t>& bytes);
|
||||
bool redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo);
|
||||
void clearRedirectRead();
|
||||
@@ -62,6 +60,7 @@ public:
|
||||
std::shared_ptr<SerialNumberMessage> getSerialNumberSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
|
||||
std::optional< std::vector< std::optional<DeviceAppVersion> > > getVersionsSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
|
||||
std::shared_ptr<LogicalDiskInfoMessage> getLogicalDiskInfoSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
|
||||
std::optional< std::vector<ComponentVersion> > getComponentVersionsSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
|
||||
|
||||
int addMessageCallback(const std::shared_ptr<MessageCallback>& cb);
|
||||
bool removeMessageCallback(int id);
|
||||
@@ -92,8 +91,6 @@ protected:
|
||||
std::atomic<bool> redirectingRead{false};
|
||||
std::function<void(std::vector<uint8_t>&&)> redirectionFn;
|
||||
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the redirectionFn
|
||||
std::mutex rawCallbacksMutex;
|
||||
std::list<std::function<void(std::vector<uint8_t>&)>> rawCallbacks;
|
||||
std::mutex syncMessageMutex;
|
||||
|
||||
void dispatchMessage(const std::shared_ptr<Message>& msg);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include "icsneo/device/neodevice.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
|
||||
|
||||
@@ -17,7 +16,7 @@ namespace icsneo {
|
||||
|
||||
class Driver {
|
||||
public:
|
||||
Driver(const device_eventhandler_t& handler, neodevice_t& forDevice) : report(handler), device(forDevice) {}
|
||||
Driver(const device_eventhandler_t& handler) : report(handler) {}
|
||||
virtual ~Driver() {}
|
||||
virtual bool open() = 0;
|
||||
virtual bool isOpen() = 0;
|
||||
@@ -25,14 +24,12 @@ public:
|
||||
virtual void awaitModeChangeComplete() {}
|
||||
virtual bool isDisconnected() { return disconnected; };
|
||||
virtual bool close() = 0;
|
||||
virtual bool enableHeartbeat() const { return false; }
|
||||
bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
|
||||
bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
|
||||
bool write(const std::vector<uint8_t>& bytes);
|
||||
virtual bool isEthernet() const { return false; }
|
||||
|
||||
device_eventhandler_t report;
|
||||
neodevice_t& device;
|
||||
|
||||
size_t writeQueueSize = 50;
|
||||
bool writeBlocks = true; // Otherwise it just fails when the queue is full
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#ifndef __INTERPROCESSMAILBOX_H_
|
||||
#define __INTERPROCESSMAILBOX_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include "icsneo/platform/sharedmemory.h"
|
||||
#include "icsneo/platform/sharedsemaphore.h"
|
||||
|
||||
static constexpr uint16_t MESSAGE_COUNT = 1024;
|
||||
static constexpr uint16_t BLOCK_SIZE = 2048;
|
||||
using LengthFieldType = uint16_t;
|
||||
static constexpr uint8_t LENGTH_FIELD_SIZE = sizeof(LengthFieldType);
|
||||
static constexpr uint16_t MAX_DATA_SIZE = BLOCK_SIZE - LENGTH_FIELD_SIZE;
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class InterprocessMailbox {
|
||||
public:
|
||||
bool open(const std::string& name, bool create = false /* create the shared resources or not */);
|
||||
bool close();
|
||||
operator bool() const;
|
||||
|
||||
// data must be large enough to hold at least MAX_DATA_SIZE
|
||||
// messageLength can be larger than MAX_DATA_SIZE if the message spans multiple blocks, only MAX_DATA_SIZE will be read
|
||||
bool read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout);
|
||||
|
||||
// if messageLength is larger than MAX_DATA_SIZE it's expected that future write() calls will send the remaining data
|
||||
bool write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout);
|
||||
private:
|
||||
icsneo::SharedSemaphore queuedSem;
|
||||
icsneo::SharedSemaphore emptySem;
|
||||
icsneo::SharedMemory sharedMem;
|
||||
unsigned index = 0; // index into messages;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
#ifndef __LIVEDATA_H__
|
||||
#define __LIVEDATA_H__
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "icsneo/communication/command.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
typedef uint32_t LiveDataHandle;
|
||||
static constexpr size_t MAX_LIVE_DATA_ENTRIES = 20;
|
||||
|
||||
enum class LiveDataCommand : uint32_t {
|
||||
STATUS = 0,
|
||||
SUBSCRIBE,
|
||||
UNSUBSCRIBE,
|
||||
RESPONSE,
|
||||
CLEAR_ALL,
|
||||
};
|
||||
|
||||
enum class LiveDataStatus : uint32_t {
|
||||
SUCCESS = 0,
|
||||
ERR_UNKNOWN_COMMAND,
|
||||
ERR_HANDLE,
|
||||
ERR_DUPLICATE,
|
||||
ERR_FULL
|
||||
};
|
||||
|
||||
enum LiveDataObjectType : uint16_t {
|
||||
MISC = 8,
|
||||
SNA = UINT16_MAX,
|
||||
};
|
||||
|
||||
enum class LiveDataValueType : uint32_t {
|
||||
GPS_LATITUDE = 2,
|
||||
GPS_LONGITUDE,
|
||||
GPS_ALTITUDE,
|
||||
GPS_SPEED,
|
||||
GPS_VALID,
|
||||
GPS_ENABLE = 62,
|
||||
GPS_ACCURACY = 120,
|
||||
GPS_BEARING = 121,
|
||||
GPS_TIME = 122,
|
||||
GPS_TIME_VALID = 123,
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const LiveDataCommand cmd) {
|
||||
switch (cmd) {
|
||||
case LiveDataCommand::STATUS: return os << "Status";
|
||||
case LiveDataCommand::SUBSCRIBE: return os << "Subscribe";
|
||||
case LiveDataCommand::UNSUBSCRIBE: return os << "Unsubscribe";
|
||||
case LiveDataCommand::RESPONSE: return os << "Response";
|
||||
case LiveDataCommand::CLEAR_ALL: return os << "Clear All";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const LiveDataStatus cmd) {
|
||||
switch (cmd) {
|
||||
case LiveDataStatus::SUCCESS: return os << "Success";
|
||||
case LiveDataStatus::ERR_UNKNOWN_COMMAND: return os << "Error: Unknown Command";
|
||||
case LiveDataStatus::ERR_HANDLE: return os << "Error: Handle";
|
||||
case LiveDataStatus::ERR_DUPLICATE: return os << "Error: Duplicate";
|
||||
case LiveDataStatus::ERR_FULL: return os << "Error: Argument limit reached";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const LiveDataValueType cmd) {
|
||||
switch (cmd) {
|
||||
case LiveDataValueType::GPS_LATITUDE: return os << "GPS Latitude";
|
||||
case LiveDataValueType::GPS_LONGITUDE: return os << "GPS Longitude";
|
||||
case LiveDataValueType::GPS_ALTITUDE: return os << "GPS Altitude";
|
||||
case LiveDataValueType::GPS_SPEED: return os << "GPS Speed";
|
||||
case LiveDataValueType::GPS_VALID: return os << "GPS Valid";
|
||||
case LiveDataValueType::GPS_ENABLE: return os << "GPS Enabled";
|
||||
case LiveDataValueType::GPS_ACCURACY: return os << "GPS Accuracy";
|
||||
case LiveDataValueType::GPS_BEARING: return os << "GPS Bearing";
|
||||
case LiveDataValueType::GPS_TIME: return os << "GPS Time";
|
||||
case LiveDataValueType::GPS_TIME_VALID: return os << "GPS Time Valid";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
#pragma pack(push,2)
|
||||
struct LiveDataHeader {
|
||||
uint32_t version; // See LiveDataVersion
|
||||
uint32_t cmd;
|
||||
uint32_t handle;
|
||||
};
|
||||
|
||||
struct LiveDataArgument {
|
||||
LiveDataObjectType objectType;
|
||||
uint32_t objectIndex;
|
||||
uint32_t signalIndex;
|
||||
LiveDataValueType valueType;
|
||||
};
|
||||
|
||||
struct LiveDataValueHeader {
|
||||
uint16_t length; // Number of bytes to follow header
|
||||
uint8_t reserved[2];
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LiveDataValueHeader header;
|
||||
int64_t value;
|
||||
} LiveDataValue;
|
||||
|
||||
struct LiveDataValueResponse : public LiveDataHeader {
|
||||
uint32_t numArgs;
|
||||
LiveDataValue values[1];
|
||||
};
|
||||
|
||||
struct LiveDataStatusResponse : public LiveDataHeader {
|
||||
LiveDataCommand requestedCommand;
|
||||
LiveDataStatus status;
|
||||
};
|
||||
|
||||
struct LiveDataSubscribe : public LiveDataHeader {
|
||||
uint32_t numArgs;
|
||||
uint32_t freqMs;
|
||||
uint32_t expireMs;
|
||||
LiveDataArgument args[1];
|
||||
};
|
||||
|
||||
struct ExtResponseHeader {
|
||||
ExtendedCommand command;
|
||||
uint16_t length;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
namespace LiveDataUtil
|
||||
{
|
||||
|
||||
LiveDataHandle getNewHandle();
|
||||
double liveDataValueToDouble(const LiveDataValue& val);
|
||||
static constexpr uint32_t LiveDataVersion = 1;
|
||||
|
||||
} // namespace LiveDataUtil
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // _cplusplus
|
||||
#endif // __LIVEDATA_H__
|
||||
@@ -4,140 +4,436 @@
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#define A2BMESSAGE_UPSTREAM 1
|
||||
#define A2BMESSAGE_DOWNSTREAM 0
|
||||
|
||||
#define A2BPCM_SAMPLE_SIZE 4
|
||||
|
||||
#define A2BPCM_L16 16
|
||||
#define A2BPCM_L24 24
|
||||
|
||||
#define A2BPCM_SAMPLERATE_44100 44100
|
||||
#define A2BPCM_SAMPLERATE_48000 48000
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
typedef uint32_t A2BPCMSample;
|
||||
typedef std::vector<A2BPCMSample> ChannelBuffer;
|
||||
|
||||
class A2BMessage : public Frame {
|
||||
public:
|
||||
enum class A2BDirection : uint8_t {
|
||||
DownStream = 0,
|
||||
UpStream = 1
|
||||
private:
|
||||
class FrameView {
|
||||
private:
|
||||
class SampleView {
|
||||
public:
|
||||
SampleView(uint8_t* vPtr, uint8_t bps, size_t ind) :
|
||||
index(ind), viewPtr(vPtr), bytesPerSample(bps) {}
|
||||
|
||||
operator A2BPCMSample() const {
|
||||
if(!viewPtr) {
|
||||
return 0;
|
||||
}
|
||||
A2BPCMSample sample = 0;
|
||||
|
||||
std::copy(viewPtr+index*bytesPerSample, viewPtr+(index+1)*bytesPerSample, (uint8_t*)&sample);
|
||||
if(bytesPerSample == 4) {
|
||||
sample = sample >> 8;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
SampleView& operator=(A2BPCMSample sample) {
|
||||
if(!viewPtr) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if(bytesPerSample == 4) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
std::copy((uint8_t*)&sample, (uint8_t*)&sample + bytesPerSample, viewPtr + index*bytesPerSample);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SampleView(const SampleView&) = delete;
|
||||
SampleView& operator=(const SampleView&) = delete;
|
||||
|
||||
private:
|
||||
|
||||
size_t index;
|
||||
uint8_t* viewPtr;
|
||||
uint8_t bytesPerSample;
|
||||
};
|
||||
|
||||
public:
|
||||
FrameView(uint8_t* vPtr, uint8_t nChannels, uint8_t bps) : viewPtr(vPtr), tdm(nChannels), bytesPerSample(bps) {}
|
||||
|
||||
SampleView operator[](size_t index) {
|
||||
|
||||
if(index >= ((size_t)tdm) * 2) {
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error));
|
||||
return SampleView(nullptr, 0, 0);
|
||||
}
|
||||
return SampleView(viewPtr, bytesPerSample, index);
|
||||
}
|
||||
|
||||
FrameView& operator=(const std::vector<A2BPCMSample>& samples) {
|
||||
if(!viewPtr) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if(samples.size() != (size_t)(tdm)*2) {
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error));
|
||||
return *this;
|
||||
}
|
||||
|
||||
for(size_t icsChannel = 0; icsChannel < ((size_t)(tdm) * 2); icsChannel++) {
|
||||
operator[](icsChannel) = samples[icsChannel];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FrameView(const FrameView&) = delete;
|
||||
FrameView& operator=(const FrameView&) = delete;
|
||||
private:
|
||||
uint8_t* viewPtr;
|
||||
uint8_t tdm;
|
||||
uint8_t bytesPerSample;
|
||||
};
|
||||
|
||||
A2BMessage() = delete;
|
||||
public:
|
||||
enum class A2BDirection : uint8_t {
|
||||
Downstream = 0,
|
||||
Upstream = 1
|
||||
};
|
||||
|
||||
A2BMessage(uint8_t bitDepth, uint8_t bytesPerSample, uint8_t numChannels) :
|
||||
bDepth(bitDepth),
|
||||
bps(bytesPerSample)
|
||||
{
|
||||
downstream.resize(numChannels);
|
||||
upstream.resize(numChannels);
|
||||
A2BMessage(uint8_t nChannels, bool chSize16, size_t size) :
|
||||
numChannels(nChannels),
|
||||
channelSize16(chSize16)
|
||||
{
|
||||
data.resize(std::min(roundNextMultiple(size, getFrameSize()),(size_t)maxSize), 0);
|
||||
}
|
||||
|
||||
void addSample(A2BPCMSample&& sample, A2BDirection dir, uint8_t channel) {
|
||||
if(dir == A2BDirection::DownStream) {
|
||||
downstream[channel].push_back(std::move(sample));
|
||||
bool allocateSpace(size_t numSpaceToAdd) {
|
||||
size_t spaceToAdd = roundNextMultiple(numSpaceToAdd, getFrameSize());
|
||||
|
||||
if(spaceToAdd + data.size() > maxSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data.resize(data.size() + numSpaceToAdd, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addFrame(const std::vector<A2BPCMSample>& frame) {
|
||||
if(frame.size() != ((size_t)numChannels)*2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t oldSize = data.size();
|
||||
|
||||
if(!allocateSpace(getFrameSize())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = data.begin() + oldSize;
|
||||
size_t offset = 0;
|
||||
for(A2BPCMSample sample: frame) {
|
||||
if(!channelSize16) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
std::copy((uint8_t*)&sample, (uint8_t*)&sample + getBytesPerSample(), it + offset);
|
||||
offset+=getBytesPerSample();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setFrame(const std::vector<A2BPCMSample>& frame, size_t frameNum) {
|
||||
if(frame.size() != ((size_t)numChannels)*2 || frameNum >= getNumFrames()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = data.begin() + frameNum*getFrameSize();
|
||||
size_t offset = 0;
|
||||
for(A2BPCMSample sample: frame) {
|
||||
if(!channelSize16) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
std::copy((uint8_t*)&sample, (uint8_t*)&sample + getBytesPerSample(), it + offset);
|
||||
offset+=getBytesPerSample();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fillChannelAudioBuffer(A2BDirection dir, uint8_t channel, std::vector<uint8_t>& channelBuffer) const {
|
||||
if(channel >= numChannels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample();
|
||||
|
||||
for(size_t frame = 0; frame < getNumFrames(); frame++, offset += getFrameSize()) {
|
||||
std::copy(data.begin() + offset, data.end() + offset + getBytesPerSample(), std::back_inserter(channelBuffer));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fillChannelStream(A2BDirection dir, uint8_t channel, std::unique_ptr<std::ostream>& channelStream) const {
|
||||
if(channel >= numChannels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample();
|
||||
|
||||
for(size_t frame = 0; frame < getNumFrames(); frame++, offset += getFrameSize()) {
|
||||
channelStream->write((const char*)(data.data() + offset), getBytesPerSample());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void fill(A2BPCMSample sample) {
|
||||
uint8_t* buf = data.data();
|
||||
|
||||
if(channelSize16) {
|
||||
uint16_t sample16bit = sample & 0xFF;
|
||||
uint16_t* samps = (uint16_t*)buf;
|
||||
std::fill(samps, samps + data.size()/2, sample16bit);
|
||||
}
|
||||
else {
|
||||
upstream[channel].push_back(std::move(sample));
|
||||
A2BPCMSample* samps = (A2BPCMSample*)buf;
|
||||
sample = sample << 8;
|
||||
std::fill(samps, samps + data.size()/4, sample);
|
||||
}
|
||||
totalSamples++;
|
||||
}
|
||||
|
||||
const A2BPCMSample* getSamples(A2BDirection dir, uint8_t channel) const {
|
||||
if(channel >= getNumChannels()) {
|
||||
return nullptr;
|
||||
bool fillFrame(A2BPCMSample sample, size_t frame) {
|
||||
if(frame >= getNumFrames()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(dir == A2BDirection::DownStream) {
|
||||
return downstream[channel].data();
|
||||
uint8_t* buf = data.data();
|
||||
size_t start = 2 * numChannels * frame;
|
||||
size_t end = 2 * numChannels * (frame+1);
|
||||
|
||||
if(channelSize16) {
|
||||
uint16_t sample16bit = sample & 0xFF;
|
||||
uint16_t* samps = (uint16_t*)buf;
|
||||
std::fill(samps+start, samps + end, sample16bit);
|
||||
}
|
||||
return upstream[channel].data();
|
||||
else {
|
||||
A2BPCMSample* samps = (A2BPCMSample*)buf;
|
||||
sample = sample << 8;
|
||||
std::fill(samps+start, samps + end, sample);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
bool setAudioBuffer(Iterator begin, Iterator end, A2BDirection dir, uint8_t channel, uint32_t frame) {
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
|
||||
size_t dist = (size_t)(std::distance(begin, end));
|
||||
|
||||
if(dist > (data.size() - offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy(begin, end, data.begin() + offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
bool setAudioBuffer(Iterator begin, Iterator end) {
|
||||
return setAudioBuffer(begin, end, A2BMessage::A2BDirection::Downstream, 0, 0);
|
||||
}
|
||||
|
||||
std::optional<A2BPCMSample> getSample(A2BDirection dir, uint8_t channel, uint32_t frame) const {
|
||||
const A2BPCMSample* samples = getSamples(dir, channel);
|
||||
auto numSamplesInChannel = getNumSamplesInChannel(dir, channel);
|
||||
|
||||
if(
|
||||
samples == nullptr ||
|
||||
frame >= numSamplesInChannel.value_or(0)
|
||||
channel >= numChannels ||
|
||||
frame >= getNumFrames()
|
||||
) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return samples[frame];
|
||||
A2BPCMSample sample = 0;
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
|
||||
|
||||
std::copy(data.begin() + offset, data.begin() + offset + getBytesPerSample(), (uint8_t*)&sample);
|
||||
if(channelSize16) {
|
||||
sample = sample >> 8;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
std::optional<size_t> getNumSamplesInChannel(A2BDirection dir, uint8_t channel) const {
|
||||
if(channel >= getNumChannels()) {
|
||||
std::optional<A2BPCMSample> getSample(size_t sampleNum) const {
|
||||
if(sampleNum >= getNumSamples()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(dir == A2BDirection::DownStream) {
|
||||
return downstream[channel].size();
|
||||
|
||||
A2BPCMSample sample = 0;
|
||||
size_t offset = sampleNum*getBytesPerSample();
|
||||
|
||||
std::copy(data.begin() + offset, data.begin() + offset + getBytesPerSample(), (uint8_t*)&sample);
|
||||
|
||||
if(channelSize16) {
|
||||
sample = sample >> 8;
|
||||
}
|
||||
|
||||
return upstream[channel].size();
|
||||
return sample;
|
||||
}
|
||||
|
||||
const std::vector<ChannelBuffer>& getDownstream() const {
|
||||
return downstream;
|
||||
bool setSample(A2BDirection dir, uint8_t channel, uint32_t frame, A2BPCMSample sample) {
|
||||
if(
|
||||
channel >= numChannels ||
|
||||
frame >= getNumFrames()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
|
||||
|
||||
if(!channelSize16) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
uint8_t* sampToBytes = (uint8_t*)&sample;
|
||||
std::copy(sampToBytes,sampToBytes+getBytesPerSample(), data.begin() + offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<ChannelBuffer>& getUpstream() const {
|
||||
return upstream;
|
||||
|
||||
bool setSample(uint8_t icsChannel, uint32_t frame, A2BPCMSample sample) {
|
||||
if(
|
||||
icsChannel >= (2*numChannels) ||
|
||||
frame >= getNumFrames()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = ((size_t)icsChannel)*getBytesPerSample() + frame * getFrameSize();
|
||||
|
||||
if(!channelSize16) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
uint8_t* sampToBytes = (uint8_t*)&sample;
|
||||
std::copy(sampToBytes,sampToBytes+getBytesPerSample(), data.begin() + offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FrameView operator[](size_t index) {
|
||||
if(index >= getNumFrames()) {
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error));
|
||||
return FrameView(nullptr, 0, 0);
|
||||
}
|
||||
|
||||
return FrameView(data.data() + index*getFrameSize(), numChannels, getBytesPerSample());
|
||||
}
|
||||
|
||||
size_t getNumSamples() const {
|
||||
return totalSamples;
|
||||
return data.size()/((size_t)getBytesPerSample());
|
||||
}
|
||||
|
||||
uint8_t getNumChannels() const {
|
||||
return static_cast<uint8_t>(downstream.size());
|
||||
return numChannels;
|
||||
}
|
||||
|
||||
uint8_t getBitDepth() const {
|
||||
return bDepth;
|
||||
return channelSize16 ? 16 : 24;
|
||||
}
|
||||
|
||||
uint8_t getBytesPerSample() const {
|
||||
return bps;
|
||||
return channelSize16 ? 2 : 4;
|
||||
}
|
||||
|
||||
// Equals operation for testing.
|
||||
bool operator==(const A2BMessage& rhs) const {
|
||||
return channelSize16 == rhs.channelSize16 &&
|
||||
monitor == rhs.monitor &&
|
||||
txmsg == rhs.txmsg &&
|
||||
errIndicator == rhs.errIndicator &&
|
||||
syncFrame == rhs.syncFrame &&
|
||||
rfu2 == rhs.rfu2 &&
|
||||
downstream == rhs.downstream &&
|
||||
upstream == rhs.upstream &&
|
||||
totalSamples == rhs.totalSamples &&
|
||||
bDepth == rhs.bDepth &&
|
||||
bps == rhs.bps;
|
||||
bool isTxMsg() const {
|
||||
return txmsg;
|
||||
}
|
||||
|
||||
bool channelSize16;
|
||||
bool monitor;
|
||||
bool txmsg;
|
||||
bool errIndicator;
|
||||
bool syncFrame;
|
||||
uint16_t rfu2;
|
||||
void setTxMsgBit(bool bit) {
|
||||
txmsg = bit;
|
||||
}
|
||||
|
||||
bool isMonitorMsg() const {
|
||||
return monitor;
|
||||
}
|
||||
|
||||
void setMonitorBit(bool bit) {
|
||||
monitor = bit;
|
||||
}
|
||||
|
||||
bool isErrIndicator() const {
|
||||
return errIndicator;
|
||||
}
|
||||
|
||||
void setErrIndicatorBit(bool bit) {
|
||||
errIndicator = bit;
|
||||
}
|
||||
|
||||
bool isSyncFrame() const {
|
||||
return syncFrame;
|
||||
}
|
||||
|
||||
void setSyncFrameBit(bool bit) {
|
||||
syncFrame = bit;
|
||||
}
|
||||
|
||||
uint16_t getRFU2() const {
|
||||
return rfu2;
|
||||
}
|
||||
|
||||
void setRFU2(uint16_t newRfu2) {
|
||||
rfu2 = newRfu2;
|
||||
}
|
||||
|
||||
size_t getFrameSize() const {
|
||||
return 2*((size_t)numChannels) * ((size_t)getBytesPerSample());
|
||||
}
|
||||
|
||||
size_t getNumFrames() const {
|
||||
return data.size() / getFrameSize();
|
||||
}
|
||||
|
||||
size_t getAudioBufferSize() const {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
const uint8_t* getAudioBuffer() const {
|
||||
return data.data();
|
||||
}
|
||||
|
||||
|
||||
static constexpr uint32_t maxSize = 2048;
|
||||
private:
|
||||
std::vector<ChannelBuffer> downstream;
|
||||
std::vector<ChannelBuffer> upstream;
|
||||
size_t totalSamples = 0;
|
||||
|
||||
uint8_t bDepth;
|
||||
uint8_t bps;
|
||||
|
||||
uint8_t numChannels = 0;
|
||||
bool channelSize16 = false;
|
||||
bool monitor = false;
|
||||
bool txmsg = false;
|
||||
bool errIndicator = false;
|
||||
bool syncFrame = false;
|
||||
uint16_t rfu2 = 0;
|
||||
|
||||
size_t roundNextMultiple(size_t x, size_t y) const {
|
||||
if(y==0) {
|
||||
return 0;
|
||||
}
|
||||
else if(x%y == 0) {
|
||||
return x;
|
||||
}
|
||||
|
||||
return x + y - (x%y);
|
||||
}
|
||||
|
||||
size_t getChannelIndex(A2BDirection dir, uint8_t channel) const {
|
||||
size_t channelIndex = 2 * ((size_t)channel);
|
||||
|
||||
if(dir == A2BDirection::Upstream) {
|
||||
channelIndex++;
|
||||
}
|
||||
|
||||
return channelIndex;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef __A2BDECODER_H_
|
||||
#define __A2BDECODER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
|
||||
#include "icsneo/communication/message/a2bmessage.h"
|
||||
#include "icsneo/device/device.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
typedef uint8_t Channel;
|
||||
|
||||
class A2BAudioChannelMap {
|
||||
public:
|
||||
|
||||
A2BAudioChannelMap(uint8_t tdm);
|
||||
|
||||
void set(Channel outChannel, A2BMessage::A2BDirection dir, Channel inChannel);
|
||||
void setAll(Channel inChannel);
|
||||
|
||||
Channel get(Channel outChannel, A2BMessage::A2BDirection dir) const;
|
||||
|
||||
size_t size() const;
|
||||
|
||||
uint8_t getTDM() const;
|
||||
Channel& operator[](size_t idx);
|
||||
|
||||
operator const std::vector<Channel>&() const;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
size_t getChannelIndex(Channel channel, A2BMessage::A2BDirection dir) const;
|
||||
|
||||
std::vector<Channel> rawMap;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class A2BDecoder {
|
||||
public:
|
||||
|
||||
A2BDecoder(
|
||||
std::unique_ptr<std::istream>&& streamOut,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
);
|
||||
|
||||
A2BDecoder(
|
||||
const char* filename,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
);
|
||||
|
||||
operator bool() const;
|
||||
|
||||
std::shared_ptr<A2BMessage> decode();
|
||||
|
||||
bool outputAll(std::shared_ptr<Device> &device);
|
||||
|
||||
std::unique_ptr<std::istream> stream;
|
||||
private:
|
||||
|
||||
void initializeFromHeader();
|
||||
|
||||
uint8_t tdm;
|
||||
uint8_t audioBytesPerSample;
|
||||
uint8_t channelsInWave;
|
||||
bool channelSize16;
|
||||
A2BAudioChannelMap channelMap;
|
||||
|
||||
std::vector<uint8_t> frame;
|
||||
std::vector<uint8_t> frameWave;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -5,17 +5,16 @@
|
||||
|
||||
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
|
||||
#include "icsneo/communication/message/a2bmessage.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include "icsneo/device/device.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class A2BWAVOutput : public StreamOutput {
|
||||
public:
|
||||
A2BWAVOutput(const char* filename, uint32_t sampleRate = A2BPCM_SAMPLERATE_44100)
|
||||
A2BWAVOutput(const char* filename, uint32_t sampleRate = 44100)
|
||||
: StreamOutput(filename), wavSampleRate(sampleRate) {}
|
||||
|
||||
A2BWAVOutput(std::unique_ptr<std::ostream>&& os, uint32_t sampleRate = A2BPCM_SAMPLERATE_44100)
|
||||
A2BWAVOutput(std::unique_ptr<std::ostream>&& os, uint32_t sampleRate = 44100)
|
||||
: StreamOutput(std::move(os)), wavSampleRate(sampleRate) {}
|
||||
|
||||
void writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const;
|
||||
@@ -32,12 +31,11 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
bool writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessage::A2BDirection dir) const;
|
||||
|
||||
uint32_t wavSampleRate;
|
||||
mutable uint32_t streamStartPos;
|
||||
mutable bool firstMessageFlag = true;
|
||||
mutable bool closed = false;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef _EXTENDED_COMPONENT_VERSIONS_RESPONSE_MESSAGE_H_
|
||||
#define _EXTENDED_COMPONENT_VERSIONS_RESPONSE_MESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||
|
||||
namespace icsneo {
|
||||
class ComponentVersion {
|
||||
public:
|
||||
ComponentVersion(uint8_t valid, uint8_t componentInfo, uint32_t identifier, uint32_t dotVersion, uint32_t commitHash) :
|
||||
valid(valid), componentInfo(componentInfo), identifier(identifier), dotVersion(dotVersion), commitHash(commitHash) {}
|
||||
const bool valid;
|
||||
const uint8_t componentInfo;
|
||||
const uint32_t identifier;
|
||||
const uint32_t dotVersion;
|
||||
const uint32_t commitHash;
|
||||
};
|
||||
|
||||
class ComponentVersionsMessage : public Message {
|
||||
public:
|
||||
ComponentVersionsMessage() : Message(Message::Type::ComponentVersions) {}
|
||||
std::vector<ComponentVersion> versions;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // _EXTENDED_COMPONENT_VERSIONS_RESPONSE_MESSAGE_H_
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef __DISKDATAMESSAGE_H_
|
||||
#define __DISKDATAMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class DiskDataMessage : public RawMessage {
|
||||
public:
|
||||
DiskDataMessage(std::vector<uint8_t>&& d) : RawMessage(Network::NetID::DiskData) {
|
||||
data = std::move(d);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef __EXTENDEDDATAMESSAGE_H_
|
||||
#define __EXTENDEDDATAMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class ExtendedDataMessage : public RawMessage {
|
||||
public:
|
||||
#pragma pack(push, 2)
|
||||
struct ExtendedDataHeader {
|
||||
ExtendedDataSubCommand subCommand;
|
||||
uint32_t userValue;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr size_t MaxExtendedDataBufferSize = 2048;
|
||||
const ExtendedDataHeader header;
|
||||
|
||||
ExtendedDataMessage(ExtendedDataHeader params) : RawMessage(Message::Type::RawMessage, Network::NetID::ExtendedData), header{params} {}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -13,7 +13,7 @@ class MessageFilter {
|
||||
public:
|
||||
MessageFilter() {}
|
||||
MessageFilter(Message::Type type) : includeInternalInAny(neomessagetype_t(type) & 0x8000), messageType(type) {}
|
||||
MessageFilter(Network::NetID netid) : MessageFilter(Network::GetTypeOfNetID(netid), netid) {}
|
||||
MessageFilter(Network::NetID netid) : MessageFilter(Network::GetTypeOfNetID(netid, false), netid) {} // Messages on the communication layer are never encoded as VNET ID + common ID, so skip the expansion step
|
||||
MessageFilter(Network::Type type, Network::NetID net = Network::NetID::Any) : networkType(type), netid(net) {
|
||||
// If a Network::Type::Internal is used, we want to also get internal Message::Types
|
||||
// The NetID we want may be in there
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef _EXTENDED_GENERIC_BINARY_STATUS_MESSAGE_H_
|
||||
#define _EXTENDED_GENERIC_BINARY_STATUS_MESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class GenericBinaryStatusMessage : public Message {
|
||||
public:
|
||||
GenericBinaryStatusMessage() : Message(Message::Type::GenericBinaryStatus) {}
|
||||
|
||||
size_t binarySize;
|
||||
uint16_t binaryIndex;
|
||||
uint16_t binaryStatus;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // _EXTENDED_GENERIC_BINARY_STATUS_RESPONSE_MESSAGE_H_
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef __LINMESSAGE_H_
|
||||
#define __LINMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include "icsneo/communication/network.h"
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
struct LINErrorFlags {
|
||||
bool ErrRxBreakOnly = false;
|
||||
bool ErrRxBreakSyncOnly = false;
|
||||
bool ErrTxRxMismatch = false;
|
||||
bool ErrRxBreakNotZero = false;
|
||||
bool ErrRxBreakTooShort = false;
|
||||
bool ErrRxSyncNot55 = false;
|
||||
bool ErrRxDataLenOver8 = false;
|
||||
bool ErrFrameSync = false;
|
||||
bool ErrFrameMessageID = false;
|
||||
bool ErrFrameResponderData = false;
|
||||
bool ErrChecksumMatch = false;
|
||||
};
|
||||
|
||||
struct LINStatusFlags {
|
||||
bool TxChecksumEnhanced = false;
|
||||
bool TxCommander = false;
|
||||
bool TxResponder = false;
|
||||
bool TxAborted = false;
|
||||
bool UpdateResponderOnce = false;
|
||||
bool HasUpdatedResponderOnce = false;
|
||||
bool BusRecovered = false;
|
||||
bool BreakOnly = false;
|
||||
};
|
||||
|
||||
class LINMessage : public Frame {
|
||||
public:
|
||||
enum class Type : uint8_t {
|
||||
NOT_SET = 0,
|
||||
LIN_COMMANDER_MSG,
|
||||
LIN_HEADER_ONLY,
|
||||
LIN_BREAK_ONLY,
|
||||
LIN_SYNC_ONLY,
|
||||
LIN_UPDATE_RESPONDER,
|
||||
LIN_ERROR
|
||||
};
|
||||
|
||||
static void calcChecksum(LINMessage& message);
|
||||
uint8_t calcProtectedID(uint8_t& id);
|
||||
|
||||
LINMessage() {};
|
||||
LINMessage(uint8_t id) : ID(id & 0x3Fu), protectedID(calcProtectedID(ID)) {};
|
||||
|
||||
uint8_t ID = 0;
|
||||
uint8_t protectedID = 0;
|
||||
uint8_t checksum = 0;
|
||||
LINMessage::Type linMsgType = Type::NOT_SET;
|
||||
bool isEnhancedChecksum = false;
|
||||
LINErrorFlags errFlags;
|
||||
LINStatusFlags statusFlags;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef __EXTENDEDLIVEDATAMESSAGE_H_
|
||||
#define __EXTENDEDLIVEDATAMESSAGE_H_
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <chrono>
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
#include "icsneo/communication/livedata.h"
|
||||
|
||||
namespace icsneo {
|
||||
class LiveDataMessage : public RawMessage {
|
||||
public:
|
||||
LiveDataMessage() : RawMessage(Message::Type::LiveData, Network::NetID::ExtendedCommand) {}
|
||||
LiveDataHandle handle;
|
||||
LiveDataCommand cmd;
|
||||
};
|
||||
|
||||
class LiveDataCommandMessage : public LiveDataMessage {
|
||||
public:
|
||||
LiveDataCommandMessage() {}
|
||||
std::chrono::milliseconds updatePeriod;
|
||||
std::chrono::milliseconds expirationTime;
|
||||
std::vector<std::shared_ptr<LiveDataArgument>> args;
|
||||
void appendSignalArg(LiveDataValueType valueType);
|
||||
};
|
||||
|
||||
class LiveDataValueMessage : public LiveDataMessage {
|
||||
public:
|
||||
LiveDataValueMessage() {}
|
||||
uint32_t numArgs;
|
||||
std::vector<std::shared_ptr<LiveDataValue>> values;
|
||||
};
|
||||
|
||||
class LiveDataStatusMessage : public LiveDataMessage {
|
||||
public:
|
||||
LiveDataStatusMessage() {}
|
||||
LiveDataCommand requestedCommand;
|
||||
LiveDataStatus status;
|
||||
};
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // __EXTENDEDLIVEDATAMESSAGE_H_
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef __MDIOMESSAGE_H_
|
||||
#define __MDIOMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class MDIOMessage : public Frame {
|
||||
public:
|
||||
enum class Clause : uint8_t {
|
||||
Clause45 = 0,
|
||||
Clause22 = 1
|
||||
};
|
||||
|
||||
enum class Direction : uint8_t {
|
||||
Write = 0,
|
||||
Read = 1
|
||||
};
|
||||
|
||||
bool isTXMsg = false;
|
||||
bool txTimeout = false;
|
||||
bool txAborted = false;
|
||||
bool txInvalidBus = false;
|
||||
bool txInvalidPhyAddr = false;
|
||||
bool txInvalidRegAddr = false;
|
||||
bool txInvalidClause = false;
|
||||
bool txInvalidOpcode = false;
|
||||
uint8_t phyAddress;
|
||||
uint8_t devAddress;
|
||||
uint16_t regAddress;
|
||||
Direction direction;
|
||||
Clause clause;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -18,6 +18,9 @@ public:
|
||||
|
||||
CANErrorCount = 0x100,
|
||||
|
||||
LINHeaderOnly = 0x200,
|
||||
LINBreak = 0x201,
|
||||
|
||||
// Past 0x8000 are all for internal use only
|
||||
Invalid = 0x8000,
|
||||
RawMessage = 0x8001,
|
||||
@@ -31,6 +34,10 @@ public:
|
||||
ExtendedResponse = 0x8009,
|
||||
WiVICommandResponse = 0x800a,
|
||||
ScriptStatus = 0x800b,
|
||||
ComponentVersions = 0x800c,
|
||||
SupportedFeatures = 0x800d,
|
||||
GenericBinaryStatus = 0x800e,
|
||||
LiveData = 0x800f,
|
||||
};
|
||||
|
||||
Message(Type t) : type(t) {}
|
||||
|
||||
@@ -86,14 +86,24 @@ typedef union {
|
||||
// ethernetFrameTooShort
|
||||
// ethernetFCSAvailable
|
||||
// ~~~ End of bitfield 2 ~~~
|
||||
//uint32_t linJustBreakSync : 1;
|
||||
//uint32_t linSlaveDataTooShort : 1;
|
||||
//uint32_t linOnlyUpdateSlaveTableOnce : 1;
|
||||
uint32_t canfdESI : 1;
|
||||
uint32_t canfdIDE : 1;
|
||||
uint32_t canfdRTR : 1;
|
||||
uint32_t canfdFDF : 1;
|
||||
uint32_t canfdBRS : 1;
|
||||
union {
|
||||
uint32_t status3;
|
||||
struct {
|
||||
uint32_t canfdESI : 1;
|
||||
uint32_t canfdIDE : 1;
|
||||
uint32_t canfdRTR : 1;
|
||||
uint32_t canfdFDF : 1;
|
||||
uint32_t canfdBRS : 1;
|
||||
uint32_t : 27;
|
||||
};
|
||||
struct {
|
||||
uint32_t linJustBreakSync : 1;
|
||||
uint32_t linSlaveDataTooShort : 1;
|
||||
uint32_t linOnlyUpdateSlaveTableOnce : 1;
|
||||
uint32_t : 29;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
uint32_t statusBitfield[4];
|
||||
} neomessage_statusbitfield_t;
|
||||
@@ -171,6 +181,33 @@ typedef struct {
|
||||
uint8_t _reserved1[12];
|
||||
} neomessage_eth_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t txChecksumEnhanced : 1;
|
||||
uint8_t txCommander : 1;
|
||||
uint8_t txResponder : 1;
|
||||
uint8_t txAborted : 1;
|
||||
uint8_t updateResponderOnce : 1;
|
||||
uint8_t hasUpdatedResponderOnce : 1;
|
||||
uint8_t busRecovered : 1;
|
||||
uint8_t breakOnly : 1;
|
||||
} neomessage_linstatus_t;
|
||||
|
||||
typedef struct {
|
||||
neomessage_statusbitfield_t status;
|
||||
uint64_t timestamp;
|
||||
uint64_t _reservedTimestamp;
|
||||
const uint8_t* data;
|
||||
size_t length;
|
||||
uint8_t header[4];
|
||||
neonetid_t netid;
|
||||
neonettype_t type;
|
||||
neomessage_linstatus_t linStatus;
|
||||
uint16_t description;
|
||||
neomessagetype_t messageType;
|
||||
uint8_t checksum;
|
||||
uint8_t _reserved1[11];
|
||||
} neomessage_lin_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -182,6 +219,7 @@ static_assert(sizeof(neomessage_frame_t) == sizeof(neomessage_t), "All types of
|
||||
static_assert(sizeof(neomessage_can_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (CAN is not)");
|
||||
static_assert(sizeof(neomessage_can_error_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (CAN error is not)");
|
||||
static_assert(sizeof(neomessage_eth_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (Ethernet is not)");
|
||||
static_assert(sizeof(neomessage_lin_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (LIN is not)");
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef _SUPPORTED_FEATURES_RESPONSE_MESSAGE_H_
|
||||
#define _SUPPORTED_FEATURES_RESPONSE_MESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
enum class SupportedFeature : uint16_t {
|
||||
networkDWCAN01 = 0,
|
||||
networkDWCAN02 = 1,
|
||||
networkDWCAN03 = 2,
|
||||
networkDWCAN04 = 3,
|
||||
networkDWCAN05 = 4,
|
||||
networkDWCAN06 = 5,
|
||||
networkDWCAN07 = 6,
|
||||
networkDWCAN08 = 7,
|
||||
networkTerminationDWCAN01 = 8,
|
||||
networkTerminationDWCAN02 = 9,
|
||||
networkTerminationDWCAN03 = 10,
|
||||
networkTerminationDWCAN04 = 11,
|
||||
networkTerminationDWCAN05 = 12,
|
||||
networkTerminationDWCAN06 = 13,
|
||||
networkTerminationDWCAN07 = 14,
|
||||
networkTerminationDWCAN08 = 15,
|
||||
enhancedFlashDriver = 16,
|
||||
rtcCalibration = 17,
|
||||
rtcClosedLoopCalibration = 18,
|
||||
numSupportedFeatures,
|
||||
};
|
||||
|
||||
class SupportedFeaturesMessage : public Message {
|
||||
public:
|
||||
SupportedFeaturesMessage() : Message(Message::Type::SupportedFeatures) {}
|
||||
std::set<SupportedFeature> features;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // _SUPPORTED_FEATURES_RESPONSE_MESSAGE_H_
|
||||
+1001
-583
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user