mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-20 16:08:37 +02:00
Compare commits
133
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 | ||
|
|
78465e0f20 | ||
|
|
ed1632c652 | ||
|
|
c97db0f35f | ||
|
|
c9d8a4f0a9 | ||
|
|
96fe8895fc | ||
|
|
e46755f120 | ||
|
|
0619c37a90 | ||
|
|
de58b2d639 | ||
|
|
5ff1707c47 | ||
|
|
4c0d49f09a | ||
|
|
b32f58da38 | ||
|
|
0101467154 | ||
|
|
17b3018499 | ||
|
|
0fc676e576 | ||
|
|
7b2544864b | ||
|
|
7f22286838 | ||
|
|
643768fb6a | ||
|
|
9817887523 | ||
|
|
13ec2bca98 | ||
|
|
711976cce0 | ||
|
|
e02203a974 | ||
|
|
5b14ec7246 | ||
|
|
3b80746fb8 | ||
|
|
3b9a31ef51 | ||
|
|
124bd3b310 | ||
|
|
9871430288 | ||
|
|
2e296dc8d3 | ||
|
|
b35fab754c | ||
|
|
9ef01e2d3d | ||
|
|
1bb33156f7 | ||
|
|
a0f73755e8 | ||
|
|
ab54697745 | ||
|
|
0d0b7f00bd | ||
|
|
1a28fbf5ad | ||
|
|
b50706846c | ||
|
|
d6d34eb7bc | ||
|
|
a928a1d879 | ||
|
|
de3d8bf870 | ||
|
|
407ccccedd | ||
|
|
00024990e9 | ||
|
|
cb47065a47 | ||
|
|
2b819065ad | ||
|
|
58700afc73 | ||
|
|
55d7d5bf17 | ||
|
|
c08c0dd893 | ||
|
|
44ad06d4bb | ||
|
|
c2de1dfdf1 | ||
|
|
a2cfc50b7c | ||
|
|
c398afc4e3 | ||
|
|
8d495aa916 | ||
|
|
91abf378f8 | ||
|
|
2e3b738e76 | ||
|
|
103f938d69 | ||
|
|
f1d9be1a81 | ||
|
|
2c282fe396 | ||
|
|
95de93aa84 | ||
|
|
733312628c | ||
|
|
b177617940 | ||
|
|
28d3730375 | ||
|
|
c451fc2b7c | ||
|
|
c620b2ee8b | ||
|
|
111d377d4a | ||
|
|
c3469f5fb6 | ||
|
|
0219eccc94 | ||
|
|
e22f70a35a | ||
|
|
b019c20ad0 | ||
|
|
6fa469de5f | ||
|
|
be219288dc | ||
|
|
d35653e3d0 | ||
|
|
0ce7326b83 | ||
|
|
714db03a05 | ||
|
|
d097fb9a74 | ||
|
|
9d5bad94d4 | ||
|
|
181223375a | ||
|
|
51626b9e63 | ||
|
|
a1a544045b | ||
|
|
719dbcefc8 | ||
|
|
c5314884ce | ||
|
|
82954d9190 |
@@ -9,3 +9,8 @@ third-party/concurrentqueue/benchmarks
|
||||
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
|
||||
|
||||
+134
-23
@@ -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)
|
||||
@@ -16,10 +18,13 @@ set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to
|
||||
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 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
@@ -34,6 +39,9 @@ if(MSVC)
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
# http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html
|
||||
# Still supported until a suitable replacement is standardized
|
||||
add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
|
||||
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
||||
endif()
|
||||
@@ -93,7 +101,7 @@ if(LIBICSNEO_BUILD_DOCS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
if(WIN32)
|
||||
set(PLATFORM_SRC
|
||||
platform/windows/registry.cpp
|
||||
)
|
||||
@@ -110,6 +118,12 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
platform/windows/vcp.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
elseif(LIBICSNEO_ENABLE_ANDROIDUSB)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/android/androidusb.cpp
|
||||
)
|
||||
|
||||
else() # Darwin or Linux
|
||||
set(PLATFORM_SRC)
|
||||
|
||||
@@ -154,6 +168,61 @@ else() # Darwin or Linux
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if(LIBICSNEO_ENABLE_FTD3XX)
|
||||
if(NOT FTD3XX_ROOT) # allow system override
|
||||
include(FetchContent)
|
||||
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-x64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ee1289cdd5023de67275aaa1712e8e32e73e825a8392efb0a76f161e357fbdc9")
|
||||
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-i686.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=bcd31f5c3cb39ecb9b09db5ce722bd79de6e0f813130e7b8f2c86879a60d8ff0")
|
||||
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-macos-universal2.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=74c0d35f04242d0841532c6325eb2932b8f627e3c395382a15d9f39974d27a90")
|
||||
elseif(UNIX)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-x64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=a9dc7eb6948c8977fbd79f6700bec6f882d3da5667aea8f2175b8d1f6f08e456")
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-aarch64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=da4b90ea1cbb905874cd159ad2ab8c1bdde65cc22b3aa55bf2b5fd85ca6efd22")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-armhf.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=d813008117422cae958f7c71a065cdac0d31dca3b24809d3ab5e13604a9c3fb1")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(NOT LIBICSNEO_FTD3XX_URL)
|
||||
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
ftdi3xx
|
||||
URL ${LIBICSNEO_FTD3XX_URL}
|
||||
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
|
||||
)
|
||||
FetchContent_GetProperties(ftdi3xx)
|
||||
if(NOT ftdi3xx_POPULATED)
|
||||
FetchContent_Populate(ftdi3xx)
|
||||
endif()
|
||||
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
|
||||
endif()
|
||||
find_package(FTD3XX REQUIRED)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/ftd3xx.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_ENABLE_TCP)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/tcp.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
@@ -168,15 +237,29 @@ 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
|
||||
communication/packet/ethernetpacket.cpp
|
||||
communication/packet/versionpacket.cpp
|
||||
communication/packet/iso9141packet.cpp
|
||||
communication/packet/ethphyregpacket.cpp
|
||||
communication/packet/livedatapacket.cpp
|
||||
communication/packet/logicaldiskinfopacket.cpp
|
||||
communication/packet/wivicommandpacket.cpp
|
||||
communication/packet/i2cpacket.cpp
|
||||
communication/packet/linpacket.cpp
|
||||
communication/packet/mdiopacket.cpp
|
||||
communication/packet/scriptstatuspacket.cpp
|
||||
communication/packet/componentversionpacket.cpp
|
||||
communication/packet/supportedfeaturespacket.cpp
|
||||
communication/packet/genericbinarystatuspacket.cpp
|
||||
communication/decoder.cpp
|
||||
communication/encoder.cpp
|
||||
communication/ethernetpacketizer.cpp
|
||||
@@ -184,6 +267,7 @@ set(SRC_FILES
|
||||
communication/multichannelcommunication.cpp
|
||||
communication/communication.cpp
|
||||
communication/driver.cpp
|
||||
communication/livedata.cpp
|
||||
device/extensions/flexray/extension.cpp
|
||||
device/extensions/flexray/controller.cpp
|
||||
device/idevicesettings.cpp
|
||||
@@ -273,13 +357,33 @@ endif()
|
||||
if(LIBICSNEO_ENABLE_FTDI)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
|
||||
endif()
|
||||
if(LIBICSNEO_ENABLE_FTD3XX)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTD3XX)
|
||||
target_link_libraries(icsneocpp PRIVATE FTD3XX::FTD3XX)
|
||||
endif()
|
||||
if(LIBICSNEO_ENABLE_TCP)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_TCP)
|
||||
if(WIN32)
|
||||
target_link_libraries(icsneocpp PRIVATE ws2_32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
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
|
||||
if(NOT WIN32)
|
||||
if(LIBICSNEO_ENABLE_FTDI)
|
||||
if(NOT WIN32)
|
||||
target_include_directories(icsneocpp PUBLIC third-party/libftdi/src)
|
||||
set(LIBFTDI_DOCUMENTATION OFF CACHE INTERNAL "")
|
||||
set(LIBFTDI_BUILD_TESTS OFF CACHE INTERNAL "")
|
||||
@@ -290,10 +394,17 @@ if(NOT WIN32)
|
||||
set(FTDI_EEPROM OFF CACHE INTERNAL "")
|
||||
add_subdirectory(third-party/libftdi)
|
||||
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
endif(NOT WIN32)
|
||||
|
||||
# winpcap
|
||||
if(WIN32)
|
||||
find_package(Threads)
|
||||
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
|
||||
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif(NOT WIN32)
|
||||
endif(LIBICSNEO_ENABLE_FTDI)
|
||||
|
||||
# pcap
|
||||
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
if(WIN32)
|
||||
if(LIBICSNEO_NPCAP_INCLUDE_DIR STREQUAL "")
|
||||
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
|
||||
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
|
||||
@@ -301,11 +412,12 @@ if(WIN32)
|
||||
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
|
||||
add_definitions(-DNPCAP -DWIN32_LEAN_AND_MEAN)
|
||||
endif()
|
||||
endif(WIN32)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
|
||||
endif()
|
||||
else()
|
||||
find_package(PCAP REQUIRED)
|
||||
target_include_directories(icsneocpp PUBLIC ${PCAP_INCLUDE_DIR})
|
||||
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
|
||||
endif(WIN32)
|
||||
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOC)
|
||||
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
||||
@@ -318,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)
|
||||
@@ -351,17 +468,6 @@ if(LIBICSNEO_BUILD_ICSNEOLEGACY)
|
||||
target_compile_features(icsneolegacy PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||
endif()
|
||||
|
||||
# libftdi
|
||||
if(NOT WIN32)
|
||||
find_package(Threads)
|
||||
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
|
||||
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||
find_package(PCAP REQUIRED)
|
||||
target_include_directories(icsneocpp PUBLIC ${PCAP_INCLUDE_DIR})
|
||||
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
|
||||
endif()
|
||||
|
||||
# googletest
|
||||
if(LIBICSNEO_BUILD_TESTS)
|
||||
if(WIN32)
|
||||
@@ -382,6 +488,11 @@ if(LIBICSNEO_BUILD_TESTS)
|
||||
test/diskdriverwritetest.cpp
|
||||
test/eventmanagertest.cpp
|
||||
test/ethernetpacketizertest.cpp
|
||||
test/i2cencoderdecodertest.cpp
|
||||
test/linencoderdecodertest.cpp
|
||||
test/a2bencoderdecodertest.cpp
|
||||
test/mdioencoderdecodertest.cpp
|
||||
test/livedataencoderdecodertest.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(libicsneo-tests gtest gtest_main)
|
||||
|
||||
+11
@@ -14,6 +14,13 @@
|
||||
- RADStar 2
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- RADA2B
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- RADComet
|
||||
- CAN works
|
||||
- CAN FD works
|
||||
- Ethernet works
|
||||
|
||||
- Connecting over USB
|
||||
- ValueCAN 4 series
|
||||
@@ -37,3 +44,7 @@
|
||||
- CAN works
|
||||
- neoVI ION
|
||||
- CAN works
|
||||
- RADA2B
|
||||
- CAN 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
|
||||
Building will require Microsoft Visual Studio 2017+ and CMake to be installed.
|
||||
|
||||
- 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 4.7 or above, 4.8+ recommended
|
||||
- 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
|
||||
```
|
||||
|
||||
+74
-5
@@ -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())
|
||||
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]);
|
||||
@@ -251,7 +254,7 @@ int icsneo_addMessageCallback(const neodevice_t* device, void (*callback)(neomes
|
||||
return -1;
|
||||
|
||||
return device->device->addMessageCallback(
|
||||
MessageCallback(
|
||||
std::make_shared<MessageCallback>(
|
||||
[=](std::shared_ptr<icsneo::Message> msg) {
|
||||
return callback(CreateNeoMessage(msg));
|
||||
}
|
||||
@@ -486,6 +489,11 @@ bool icsneo_describeDevice(const neodevice_t* device, char* str, size_t* maxLeng
|
||||
if(!icsneo_isValidNeoDevice(device))
|
||||
return false;
|
||||
|
||||
if(!str) {
|
||||
*maxLength = device->device->describe().length();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string output = device->device->describe();
|
||||
|
||||
*maxLength = output.copy(str, *maxLength);
|
||||
@@ -642,7 +650,7 @@ bool icsneo_getDigitalIO(const neodevice_t* device, neoio_t type, uint32_t numbe
|
||||
return false;
|
||||
}
|
||||
|
||||
const optional<bool> val = device->device->getDigitalIO(static_cast<icsneo::IO>(type), number);
|
||||
const std::optional<bool> val = device->device->getDigitalIO(static_cast<icsneo::IO>(type), number);
|
||||
if(!val.has_value())
|
||||
return false;
|
||||
|
||||
@@ -684,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
|
||||
|
||||
+139
-1
@@ -71,6 +71,7 @@ static constexpr const char* UNSUPPORTED_TX_NETWORK = "Message network is not a
|
||||
static constexpr const char* MESSAGE_MAX_LENGTH_EXCEEDED = "The message was too long.";
|
||||
static constexpr const char* VALUE_NOT_YET_PRESENT = "The value is not yet present.";
|
||||
static constexpr const char* TIMEOUT = "The timeout was reached.";
|
||||
static constexpr const char* WIVI_NOT_SUPPORTED = "Wireless neoVI functions are not supported on this device.";
|
||||
|
||||
// Device Errors
|
||||
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
|
||||
@@ -109,6 +110,12 @@ static constexpr const char* EOF_REACHED = "The requested length exceeds the ava
|
||||
static constexpr const char* SETTINGS_DEFAULTS_USED = "The device settings could not be loaded, the default settings have been applied.";
|
||||
static constexpr const char* ATOMIC_OPERATION_RETRIED = "An operation failed to be atomically completed, but will be retried.";
|
||||
static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An ideally-atomic operation was completed nonatomically.";
|
||||
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
|
||||
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
|
||||
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
|
||||
static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same.";
|
||||
static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected.";
|
||||
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
|
||||
|
||||
// Transport Errors
|
||||
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
||||
@@ -121,6 +128,46 @@ static constexpr const char* DEVICE_IN_USE = "The device is currently in use by
|
||||
static constexpr const char* PCAP_COULD_NOT_START = "The PCAP driver could not be started. Ethernet devices will not be found.";
|
||||
static constexpr const char* PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
|
||||
static constexpr const char* PACKET_DECODING = "There was an error decoding a packet from the device.";
|
||||
static constexpr const char* SOCKET_FAILED_TO_OPEN = "Unable to open new socket.";
|
||||
static constexpr const char* FAILED_TO_BIND = "Unable to bind socket.";
|
||||
static constexpr const char* ERROR_SETTING_SOCKET_OPTION = "A call to setsockopt() failed.";
|
||||
static constexpr const char* GETIFADDRS_ERROR = "A call to getifaddrs() failed.";
|
||||
static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed.";
|
||||
|
||||
// FTD3XX
|
||||
static constexpr const char* FT_OK = "FTD3XX success.";
|
||||
static constexpr const char* FT_INVALID_HANDLE = "Invalid FTD3XX handle.";
|
||||
static constexpr const char* FT_DEVICE_NOT_FOUND = "FTD3XX device not found.";
|
||||
static constexpr const char* FT_DEVICE_NOT_OPENED = "FTD3XX device not opened.";
|
||||
static constexpr const char* FT_IO_ERROR = "FTD3XX IO error.";
|
||||
static constexpr const char* FT_INSUFFICIENT_RESOURCES = "Insufficient resources for FTD3XX.";
|
||||
static constexpr const char* FT_INVALID_PARAMETER = "Invalid FTD3XX parameter.";
|
||||
static constexpr const char* FT_INVALID_BAUD_RATE = "Invalid FTD3XX baud rate.";
|
||||
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_ERASE = "FTD3XX device not opened for erase.";
|
||||
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_WRITE = "FTD3XX not opened for write.";
|
||||
static constexpr const char* FT_FAILED_TO_WRITE_DEVICE = "FTD3XX failed to write device.";
|
||||
static constexpr const char* FT_EEPROM_READ_FAILED = "FTD3XX EEPROM read failed.";
|
||||
static constexpr const char* FT_EEPROM_WRITE_FAILED = "FTD3XX EEPROM write failed.";
|
||||
static constexpr const char* FT_EEPROM_ERASE_FAILED = "FTD3XX EEPROM erase failed.";
|
||||
static constexpr const char* FT_EEPROM_NOT_PRESENT = "FTD3XX EEPROM not present.";
|
||||
static constexpr const char* FT_EEPROM_NOT_PROGRAMMED = "FTD3XX EEPROM not programmed.";
|
||||
static constexpr const char* FT_INVALID_ARGS = "Invalid FTD3XX arguments.";
|
||||
static constexpr const char* FT_NOT_SUPPORTED = "FTD3XX not supported.";
|
||||
static constexpr const char* FT_NO_MORE_ITEMS = "No more FTD3XX items.";
|
||||
static constexpr const char* FT_TIMEOUT = "FTD3XX timeout.";
|
||||
static constexpr const char* FT_OPERATION_ABORTED = "FTD3XX operation aborted.";
|
||||
static constexpr const char* FT_RESERVED_PIPE = "Reserved FTD3XX pipe.";
|
||||
static constexpr const char* FT_INVALID_CONTROL_REQUEST_DIRECTION = "Invalid FTD3XX control request direction.";
|
||||
static constexpr const char* FT_INVALID_CONTROL_REQUEST_TYPE = "Invalid FTD3XX control request type.";
|
||||
static constexpr const char* FT_IO_PENDING = "FTD3XX IO pending.";
|
||||
static constexpr const char* FT_IO_INCOMPLETE = "FTD3XX IO incomplete.";
|
||||
static constexpr const char* FT_HANDLE_EOF = "Handle FTD3XX EOF.";
|
||||
static constexpr const char* FT_BUSY = "FTD3XX busy.";
|
||||
static constexpr const char* FT_NO_SYSTEM_RESOURCES = "No FTD3XX system resources.";
|
||||
static constexpr const char* FT_DEVICE_LIST_NOT_READY = "FTD3XX device list not ready.";
|
||||
static constexpr const char* FT_DEVICE_NOT_CONNECTED = "FTD3XX device not connected.";
|
||||
static constexpr const char* FT_INCORRECT_DEVICE_PATH = "Incorrect FTD3XX device path.";
|
||||
static constexpr const char* FT_OTHER_ERROR = "Other FTD3XX error.";
|
||||
|
||||
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.";
|
||||
@@ -158,6 +205,8 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return VALUE_NOT_YET_PRESENT;
|
||||
case Type::Timeout:
|
||||
return TIMEOUT;
|
||||
case Type::WiVINotSupported:
|
||||
return WIVI_NOT_SUPPORTED;
|
||||
|
||||
// Device Errors
|
||||
case Type::PollingMessageOverflow:
|
||||
@@ -232,7 +281,18 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return ATOMIC_OPERATION_RETRIED;
|
||||
case Type::AtomicOperationCompletedNonatomically:
|
||||
return ATOMIC_OPERATION_COMPLETED_NONATOMICALLY;
|
||||
|
||||
case Type::WiVIStackRefreshFailed:
|
||||
return WIVI_STACK_REFRESH_FAILED;
|
||||
case Type::WiVIUploadStackOverflow:
|
||||
return WIVI_UPLOAD_STACK_OVERFLOW;
|
||||
case Type::A2BMessageIncompleteFrame:
|
||||
return A2B_MESSAGE_INCOMPLETE_FRAME;
|
||||
case Type::CoreminiUploadVersionMismatch:
|
||||
return COREMINI_UPLOAD_VERSION_MISMATCH;
|
||||
case Type::DiskNotConnected:
|
||||
return DISK_NOT_CONNECTED;
|
||||
case Type::UnexpectedResponse:
|
||||
return UNEXPECTED_RESPONSE;
|
||||
// Transport Errors
|
||||
case Type::FailedToRead:
|
||||
return FAILED_TO_READ;
|
||||
@@ -254,6 +314,84 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return PCAP_COULD_NOT_FIND_DEVICES;
|
||||
case Type::PacketDecodingError:
|
||||
return PACKET_DECODING;
|
||||
case Type::SocketFailedToOpen:
|
||||
return SOCKET_FAILED_TO_OPEN;
|
||||
case Type::FailedToBind:
|
||||
return FAILED_TO_BIND;
|
||||
case Type::ErrorSettingSocketOption:
|
||||
return ERROR_SETTING_SOCKET_OPTION;
|
||||
case Type::GetIfAddrsError:
|
||||
return GETIFADDRS_ERROR;
|
||||
case Type::SendToError:
|
||||
return SEND_TO_ERROR;
|
||||
|
||||
// FTD3XX
|
||||
case Type::FTOK:
|
||||
return FT_OK;
|
||||
case Type::FTInvalidHandle:
|
||||
return FT_INVALID_HANDLE;
|
||||
case Type::FTDeviceNotFound:
|
||||
return FT_DEVICE_NOT_FOUND;
|
||||
case Type::FTDeviceNotOpened:
|
||||
return FT_DEVICE_NOT_OPENED;
|
||||
case Type::FTIOError:
|
||||
return FT_IO_ERROR;
|
||||
case Type::FTInsufficientResources:
|
||||
return FT_INSUFFICIENT_RESOURCES;
|
||||
case Type::FTInvalidParameter:
|
||||
return FT_INVALID_PARAMETER;
|
||||
case Type::FTInvalidBaudRate:
|
||||
return FT_INVALID_BAUD_RATE;
|
||||
case Type::FTDeviceNotOpenedForErase:
|
||||
return FT_DEVICE_NOT_OPENED_FOR_ERASE;
|
||||
case Type::FTDeviceNotOpenedForWrite:
|
||||
return FT_DEVICE_NOT_OPENED_FOR_WRITE;
|
||||
case Type::FTFailedToWriteDevice:
|
||||
return FT_FAILED_TO_WRITE_DEVICE;
|
||||
case Type::FTEEPROMReadFailed:
|
||||
return FT_EEPROM_READ_FAILED;
|
||||
case Type::FTEEPROMWriteFailed:
|
||||
return FT_EEPROM_WRITE_FAILED;
|
||||
case Type::FTEEPROMEraseFailed:
|
||||
return FT_EEPROM_ERASE_FAILED;
|
||||
case Type::FTEEPROMNotPresent:
|
||||
return FT_EEPROM_NOT_PRESENT;
|
||||
case Type::FTEEPROMNotProgrammed:
|
||||
return FT_EEPROM_NOT_PROGRAMMED;
|
||||
case Type::FTInvalidArgs:
|
||||
return FT_INVALID_ARGS;
|
||||
case Type::FTNotSupported:
|
||||
return FT_NOT_SUPPORTED;
|
||||
case Type::FTNoMoreItems:
|
||||
return FT_NO_MORE_ITEMS;
|
||||
case Type::FTTimeout:
|
||||
return FT_TIMEOUT;
|
||||
case Type::FTOperationAborted:
|
||||
return FT_OPERATION_ABORTED;
|
||||
case Type::FTReservedPipe:
|
||||
return FT_RESERVED_PIPE;
|
||||
case Type::FTInvalidControlRequestDirection:
|
||||
return FT_INVALID_CONTROL_REQUEST_DIRECTION;
|
||||
case Type::FTInvalidControlRequestType:
|
||||
return FT_INVALID_CONTROL_REQUEST_TYPE;
|
||||
case Type::FTIOPending:
|
||||
return FT_IO_PENDING;
|
||||
case Type::FTIOIncomplete:
|
||||
return FT_IO_INCOMPLETE;
|
||||
case Type::FTHandleEOF:
|
||||
return FT_HANDLE_EOF;
|
||||
case Type::FTBusy:
|
||||
return FT_BUSY;
|
||||
case Type::FTNoSystemResources:
|
||||
return FT_NO_SYSTEM_RESOURCES;
|
||||
case Type::FTDeviceListNotReady:
|
||||
return FT_DEVICE_LIST_NOT_READY;
|
||||
case Type::FTDeviceNotConnected:
|
||||
return FT_DEVICE_NOT_CONNECTED;
|
||||
case Type::FTIncorrectDevicePath:
|
||||
return FT_INCORRECT_DEVICE_PATH;
|
||||
case Type::FTOtherError:
|
||||
return FT_OTHER_ERROR;
|
||||
|
||||
// Other Errors
|
||||
case Type::TooManyEvents:
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -33,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);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
#include <jni.h>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -638,8 +788,13 @@ int LegacyDLLExport icsneoSetDeviceParameters(void* hObject, char* pParmValue, i
|
||||
// Error Functions
|
||||
int LegacyDLLExport icsneoGetLastAPIError(void* hObject, unsigned long* pErrorNumber)
|
||||
{
|
||||
// TODO Implement
|
||||
if(!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
|
||||
neoevent_t error;
|
||||
icsneo_getLastError(&error);
|
||||
*pErrorNumber = (unsigned long)error.eventNumber;
|
||||
return true;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int* pNumberOfErrors)
|
||||
@@ -648,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
|
||||
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
|
||||
@@ -731,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)
|
||||
{
|
||||
@@ -910,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;
|
||||
}
|
||||
@@ -947,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))
|
||||
@@ -1287,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,6 +13,7 @@
|
||||
#include "icsneo/communication/message/filter/main51messagefilter.h"
|
||||
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||
#include "icsneo/communication/message/versionmessage.h"
|
||||
#include "icsneo/communication/message/componentversionsmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -21,6 +22,7 @@ int Communication::messageCallbackIDCounter = 1;
|
||||
Communication::~Communication() {
|
||||
if(redirectingRead)
|
||||
clearRedirectRead();
|
||||
if(isOpen())
|
||||
close();
|
||||
}
|
||||
|
||||
@@ -150,22 +152,22 @@ std::shared_ptr<SerialNumberMessage> Communication::getSerialNumberSync(std::chr
|
||||
return std::dynamic_pointer_cast<SerialNumberMessage>(m51);
|
||||
}
|
||||
|
||||
optional< std::vector< optional<DeviceAppVersion> > > Communication::getVersionsSync(std::chrono::milliseconds timeout) {
|
||||
std::optional< std::vector< std::optional<DeviceAppVersion> > > Communication::getVersionsSync(std::chrono::milliseconds timeout) {
|
||||
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::DeviceVersion);
|
||||
std::vector< optional<DeviceAppVersion> > ret;
|
||||
std::vector< std::optional<DeviceAppVersion> > ret;
|
||||
|
||||
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
||||
return sendCommand(Command::GetMainVersion);
|
||||
}, filter, timeout);
|
||||
if(!msg) // Did not receive a message
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
auto ver = std::dynamic_pointer_cast<VersionMessage>(msg);
|
||||
if(!ver) // Could not upcast for some reason
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(ver->ForChip != VersionMessage::MainChip || ver->Versions.size() != 1)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
ret.push_back(ver->Versions.front());
|
||||
|
||||
@@ -193,7 +195,7 @@ std::shared_ptr<LogicalDiskInfoMessage> Communication::getLogicalDiskInfoSync(st
|
||||
return std::dynamic_pointer_cast<LogicalDiskInfoMessage>(msg);
|
||||
}
|
||||
|
||||
int Communication::addMessageCallback(const MessageCallback& cb) {
|
||||
int Communication::addMessageCallback(const std::shared_ptr<MessageCallback>& cb) {
|
||||
std::lock_guard<std::mutex> lk(messageCallbacksLock);
|
||||
messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb));
|
||||
return messageCallbackIDCounter++;
|
||||
@@ -212,14 +214,15 @@ bool Communication::removeMessageCallback(int id) {
|
||||
|
||||
std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(void)> onceWaitingDo,
|
||||
const std::shared_ptr<MessageFilter>& f, std::chrono::milliseconds timeout) {
|
||||
std::mutex m;
|
||||
std::mutex cvMutex;
|
||||
std::condition_variable cv;
|
||||
std::shared_ptr<Message> returnedMessage;
|
||||
|
||||
std::unique_lock<std::mutex> lk(m); // Don't let the callback fire until we're waiting for it
|
||||
int cb = addMessageCallback(MessageCallback([&m, &returnedMessage, &cv](std::shared_ptr<Message> message) {
|
||||
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) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(m);
|
||||
std::lock_guard<std::mutex> lk(cvMutex);
|
||||
returnedMessage = message;
|
||||
}
|
||||
cv.notify_all();
|
||||
@@ -228,8 +231,8 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
|
||||
// We have now added the callback, do whatever the caller wanted to do
|
||||
bool fail = !onceWaitingDo();
|
||||
if(!fail)
|
||||
cv.wait_for(lk, timeout, [&returnedMessage] { return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value
|
||||
lk.unlock(); // Ensure callbacks can complete even if we didn't wait for them
|
||||
cv.wait_for(cvLk, timeout, [&returnedMessage] { return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value
|
||||
cvLk.unlock(); // Ensure callbacks can complete even if we didn't wait for them
|
||||
|
||||
// We don't actually check that we got a message, because either way we want to remove the callback (since it should only happen once)
|
||||
removeMessageCallback(cb);
|
||||
@@ -251,7 +254,7 @@ void Communication::dispatchMessage(const std::shared_ptr<Message>& msg) {
|
||||
EventManager::GetInstance().cancelErrorDowngradingOnCurrentThread();
|
||||
for(auto& cb : messageCallbacks) {
|
||||
if(!closing) { // We might have closed while reading or processing
|
||||
cb.second.callIfMatch(msg);
|
||||
cb.second->callIfMatch(msg);
|
||||
}
|
||||
}
|
||||
if(downgrade)
|
||||
@@ -299,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));
|
||||
}
|
||||
|
||||
@@ -5,16 +5,36 @@
|
||||
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||
#include "icsneo/communication/message/wiviresponsemessage.h"
|
||||
#include "icsneo/communication/message/scriptstatusmessage.h"
|
||||
#include "icsneo/communication/message/a2bmessage.h"
|
||||
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
#include "icsneo/communication/message/linmessage.h"
|
||||
#include "icsneo/communication/message/mdiomessage.h"
|
||||
#include "icsneo/communication/message/extendeddatamessage.h"
|
||||
#include "icsneo/communication/message/livedatamessage.h"
|
||||
#include "icsneo/communication/message/diskdatamessage.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
#include "icsneo/communication/packet/ethernetpacket.h"
|
||||
#include "icsneo/communication/packet/flexraypacket.h"
|
||||
#include "icsneo/communication/packet/iso9141packet.h"
|
||||
#include "icsneo/communication/packet/versionpacket.h"
|
||||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||
#include "icsneo/communication/packet/logicaldiskinfopacket.h"
|
||||
#include "icsneo/communication/packet/wivicommandpacket.h"
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
#include "icsneo/communication/packet/scriptstatuspacket.h"
|
||||
#include "icsneo/communication/packet/linpacket.h"
|
||||
#include "icsneo/communication/packet/componentversionpacket.h"
|
||||
#include "icsneo/communication/packet/supportedfeaturespacket.h"
|
||||
#include "icsneo/communication/packet/mdiopacket.h"
|
||||
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
|
||||
#include "icsneo/communication/packet/livedatapacket.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
@@ -115,6 +135,56 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
iso.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
case Network::Type::I2C: {
|
||||
if(packet->data.size() < sizeof(HardwareI2CPacket)) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
result = HardwareI2CPacket::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; //malformed packet indicated by a nullptr return
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
case Network::Type::A2B: {
|
||||
result = HardwareA2BPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||
}
|
||||
|
||||
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
|
||||
msg.network = packet->network;
|
||||
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: {
|
||||
@@ -193,6 +263,56 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
msg->data.insert(msg->data.end(), packet->data.begin() + 4, packet->data.end());
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::ExtendedCommand: {
|
||||
|
||||
if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse))
|
||||
break; // Handle as a raw message, might not be a generic response
|
||||
|
||||
const auto& resp = *reinterpret_cast<ExtendedResponseMessage::PackedGenericResponse*>(packet->data.data());
|
||||
switch(resp.header.command) {
|
||||
case ExtendedCommand::GetComponentVersions:
|
||||
result = ComponentVersionPacket::DecodeToMessage(packet->data);
|
||||
return true;
|
||||
case ExtendedCommand::GetSupportedFeatures:
|
||||
result = SupportedFeaturesPacket::DecodeToMessage(packet->data);
|
||||
return true;
|
||||
case ExtendedCommand::GenericBinaryInfo:
|
||||
result = GenericBinaryStatusPacket::DecodeToMessage(packet->data);
|
||||
return true;
|
||||
case ExtendedCommand::GenericReturn:
|
||||
result = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
|
||||
return true;
|
||||
case ExtendedCommand::LiveData:
|
||||
result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report);
|
||||
return true;
|
||||
default:
|
||||
// No defined handler, treat this as a RawMessage
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Network::NetID::ExtendedData: {
|
||||
if(packet->data.size() < sizeof(ExtendedDataMessage::ExtendedDataHeader))
|
||||
break;
|
||||
|
||||
const auto& header = *reinterpret_cast<ExtendedDataMessage::ExtendedDataHeader*>(packet->data.data());
|
||||
|
||||
switch(header.subCommand) {
|
||||
case ExtendedDataSubCommand::GenericBinaryRead: {
|
||||
result = std::make_shared<ExtendedDataMessage>(header);
|
||||
auto extDataMsg = std::static_pointer_cast<ExtendedDataMessage>(result);
|
||||
|
||||
size_t numRead = std::min(ExtendedDataMessage::MaxExtendedDataBufferSize, (size_t)header.length);
|
||||
extDataMsg->data.resize(numRead);
|
||||
|
||||
std::copy(packet->data.begin() + sizeof(header), packet->data.begin() + sizeof(header) + numRead, extDataMsg->data.begin());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Network::NetID::FlexRayControl: {
|
||||
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
|
||||
if(!frResult->decoded) {
|
||||
@@ -286,6 +406,14 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::WiVICommand: {
|
||||
result = WiVI::CommandPacket::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::EthPHYControl: {
|
||||
result = HardwareEthernetPhyRegisterPacket::DecodeToMessage(packet->data, report);
|
||||
if(!result) {
|
||||
@@ -294,9 +422,22 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::ScriptStatus: {
|
||||
result = ScriptStatus::DecodeToMessage(packet->data);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::DiskData: {
|
||||
result = std::make_shared<DiskDataMessage>(std::move(packet->data));
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
#include "icsneo/communication/encoder.h"
|
||||
#include "icsneo/communication/message/ethernetmessage.h"
|
||||
#include "icsneo/communication/message/livedatamessage.h"
|
||||
#include "icsneo/communication/message/main51message.h"
|
||||
#include "icsneo/communication/packet/livedatapacket.h"
|
||||
#include "icsneo/communication/packet/ethernetpacket.h"
|
||||
#include "icsneo/communication/packet/iso9141packet.h"
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||
#include "icsneo/communication/message/ethphymessage.h"
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
#include "icsneo/communication/packet/linpacket.h"
|
||||
#include "icsneo/communication/packet/mdiopacket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -68,6 +75,54 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||
// packets to the device. This function just encodes them back to back into `result`
|
||||
return HardwareISO9141Packet::EncodeFromMessage(*isomsg, result, report, packetizer);
|
||||
} // End of Network::Type::ISO9141
|
||||
case Network::Type::A2B: {
|
||||
auto a2bmsg = std::dynamic_pointer_cast<A2BMessage>(message);
|
||||
if(!a2bmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareA2BPacket::EncodeFromMessage(*a2bmsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::A2B
|
||||
case Network::Type::I2C: {
|
||||
auto i2cmsg = std::dynamic_pointer_cast<I2CMessage>(message);
|
||||
if(!i2cmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareI2CPacket::EncodeFromMessage(*i2cmsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::I2C
|
||||
case Network::Type::LIN: {
|
||||
auto linmsg = std::dynamic_pointer_cast<LINMessage>(message);
|
||||
if(!linmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareLINPacket::EncodeFromMessage(*linmsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::LIN
|
||||
case Network::Type::MDIO: {
|
||||
auto mdiomsg = std::dynamic_pointer_cast<MDIOMessage>(message);
|
||||
if(!mdiomsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareMDIOPacket::EncodeFromMessage(*mdiomsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::MDIO
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
@@ -144,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;
|
||||
}
|
||||
|
||||
@@ -154,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
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <iterator>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
|
||||
#include "icsneo/device/tree/rada2b/rada2b.h"
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(message->type != Message::Type::Frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& frame = std::static_pointer_cast<Frame>(message);
|
||||
|
||||
if(frame->network.getType() != Network::Type::A2B)
|
||||
return false;
|
||||
|
||||
const auto& a2bmsg = std::static_pointer_cast<A2BMessage>(frame);
|
||||
|
||||
if(firstMessageFlag) {
|
||||
writeHeader(a2bmsg);
|
||||
firstMessageFlag = false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
*/
|
||||
|
||||
write((void*)a2bmsg->getAudioBuffer(), a2bmsg->getAudioBufferSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void A2BWAVOutput::close() const {
|
||||
if(closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t streamEndPos = static_cast<uint32_t>(stream->tellp());
|
||||
|
||||
uint32_t subChunk2Size = streamEndPos - streamStartPos;
|
||||
uint32_t chunkSize = streamEndPos - 8;
|
||||
|
||||
stream->seekp(streamStartPos - 4);
|
||||
write((void*)&subChunk2Size, 4);
|
||||
stream->seekp(4, std::ios::beg);
|
||||
write((void*)&chunkSize, 4);
|
||||
|
||||
closed = 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;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
const size_t HardwareA2BPacket::coreMiniMessageHeaderSize = 28;
|
||||
const size_t HardwareA2BPacket::a2bMessageMaxLength = (size_t)HardwareA2BPacket::coreMiniMessageHeaderSize + 1024;
|
||||
const size_t HardwareA2BPacket::a2bHeaderSize = 6;
|
||||
|
||||
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
|
||||
if(bytestream.size() < coreMiniMessageHeaderSize)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data();
|
||||
|
||||
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - static_cast<size_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
|
||||
|
||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(
|
||||
(uint8_t)data->header.channelNum,
|
||||
data->header.channelSize16,
|
||||
totalPackedLength
|
||||
);
|
||||
|
||||
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.getAudioBufferSize();
|
||||
size_t totalSize = a2btxMessageHeaderSize + sampleBytes;
|
||||
|
||||
bytestream.resize(totalSize, 0);
|
||||
uint32_t offset = 0;
|
||||
|
||||
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);
|
||||
|
||||
std::copy(message.data.begin(), message.data.end(), bytestream.begin() + offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||
#include "icsneo/platform/optional.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
|
||||
static std::optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
|
||||
if (length <= 8)
|
||||
return length;
|
||||
|
||||
@@ -27,10 +26,10 @@ static optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
|
||||
}
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
|
||||
static std::optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
|
||||
{
|
||||
if (dataLength <= 8)
|
||||
return uint8_t(dataLength);
|
||||
@@ -52,7 +51,7 @@ static optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
|
||||
return uint8_t(0xF);
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
@@ -94,7 +93,7 @@ std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<ui
|
||||
msg->isCANFD = true;
|
||||
msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch
|
||||
msg->errorStateIndicator = data->header.ESI;
|
||||
const optional<uint8_t> lenFromDLC = CAN_DLCToLength(length, true);
|
||||
const std::optional<uint8_t> lenFromDLC = CAN_DLCToLength(length, true);
|
||||
if (lenFromDLC)
|
||||
length = *lenFromDLC;
|
||||
} else if(length > 8) { // This is a standard CAN frame with a length of more than 8
|
||||
@@ -133,7 +132,7 @@ bool HardwareCANPacket::EncodeFromMessage(const CANMessage& message, std::vector
|
||||
}
|
||||
|
||||
const size_t dataSize = message.data.size();
|
||||
optional<uint8_t> dlc = CAN_LengthToDLC(dataSize, message.isCANFD);
|
||||
std::optional<uint8_t> dlc = CAN_LengthToDLC(dataSize, message.isCANFD);
|
||||
if (!dlc.has_value()) {
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false; // Too much data for the protocol
|
||||
|
||||
@@ -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,83 @@
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
std::shared_ptr<Message> HardwareI2CPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream)
|
||||
{
|
||||
auto msg = std::make_shared<I2CMessage>();
|
||||
const I2CHeader* packet = reinterpret_cast<const I2CHeader*>(bytestream.data());
|
||||
const size_t numPayloadBytes = packet->length;
|
||||
const size_t numControlBytes = packet->CoreMiniBitsI2C.CBLen;
|
||||
const size_t numDataBytes = numPayloadBytes - numControlBytes;
|
||||
if( (numPayloadBytes == 0) || (numDataBytes > I2CMaxLength) ||
|
||||
(sizeof(I2CHeader) != (bytestream.size() - numPayloadBytes)) )
|
||||
{ return nullptr; }
|
||||
|
||||
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
|
||||
msg->address = (packet->CoreMiniBitsI2C.ID & 0x3FFu);
|
||||
msg->deviceMode = static_cast<I2CMessage::DeviceMode>(packet->CoreMiniBitsI2C.CT);
|
||||
msg->direction = static_cast<I2CMessage::Direction>(packet->CoreMiniBitsI2C.DIR);
|
||||
|
||||
msg->isExtendedID = static_cast<bool>(packet->CoreMiniBitsI2C.EID & 0x01u);
|
||||
msg->isTXMsg = static_cast<bool>(packet->CoreMiniBitsI2C.TXMsg & 0x01u);
|
||||
msg->txTimeout = static_cast<bool>(packet->CoreMiniBitsI2C.TXTimeout & 0x01u);
|
||||
msg->txNack = static_cast<bool>(packet->CoreMiniBitsI2C.TXNack & 0x01u);
|
||||
msg->txAborted = static_cast<bool>(packet->CoreMiniBitsI2C.TXAborted & 0x01u);
|
||||
msg->txLostArb = static_cast<bool>(packet->CoreMiniBitsI2C.TXLostArb & 0x01u);
|
||||
msg->txError = static_cast<bool>(packet->CoreMiniBitsI2C.TXError & 0x01u);
|
||||
//We don't care about 0xTRB0Dx in this case...
|
||||
//copy 0xTRB0STAT even though we likely won't use it either
|
||||
msg->stats = packet->stats;
|
||||
msg->timestamp = (packet->timestamp & (0x7FFFFFFFFFFFFFFFull));
|
||||
|
||||
//The device will combine the 'control' bytes and data bytes into one payload
|
||||
//The control bytes will always come before the data
|
||||
auto cbStart = bytestream.begin() + sizeof(I2CHeader);
|
||||
auto dataStart = cbStart + numControlBytes;
|
||||
std::copy(cbStart, dataStart, std::back_inserter(msg->controlBytes));
|
||||
std::copy(dataStart, bytestream.end(), std::back_inserter(msg->dataBytes));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool HardwareI2CPacket::EncodeFromMessage(const I2CMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
|
||||
{
|
||||
const size_t numControlBytes = message.controlBytes.size();
|
||||
const size_t numDataBytes = message.dataBytes.size();
|
||||
if(I2CMaxLength < numDataBytes)
|
||||
{
|
||||
report(APIEvent::Type::I2CMessageExceedsMaxLength, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
if(message.controlBytes.empty() || message.dataBytes.empty())
|
||||
{
|
||||
//You'll need to provide a target R/W register in controlBytes
|
||||
//alternatively, you're expecting to read without providing a dataBytes payload
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bytestream.push_back(static_cast<uint8_t>(numControlBytes & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>((numControlBytes) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>(numDataBytes & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>((numDataBytes) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>((message.stats) >> 8) & 0xFFu);
|
||||
bytestream.push_back(static_cast<uint8_t>(message.stats & 0xFFu));
|
||||
|
||||
if(message.isExtendedID)
|
||||
{
|
||||
bytestream.push_back(static_cast<uint8_t>(message.address & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>(((message.address) >> 8) & 0x03u) | 0x04u);
|
||||
} else {
|
||||
bytestream.push_back(static_cast<uint8_t>(message.address & 0xFFu));
|
||||
bytestream.push_back(static_cast<uint8_t>(0x00u));
|
||||
}
|
||||
|
||||
if(I2CMessage::Direction::Read == message.direction)
|
||||
{ bytestream.back() |= static_cast<uint8_t>(0x10u); }
|
||||
|
||||
std::copy(message.controlBytes.begin(), message.controlBytes.end(), std::back_inserter(bytestream));
|
||||
std::copy(message.dataBytes.begin(), message.dataBytes.end(), std::back_inserter(bytestream));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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,31 @@
|
||||
#include <iostream>
|
||||
#include "icsneo/communication/packet/scriptstatuspacket.h"
|
||||
#include "icsneo/communication/message/scriptstatusmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::shared_ptr<ScriptStatusMessage> ScriptStatus::DecodeToMessage(const std::vector<uint8_t>& bytestream){
|
||||
if(bytestream.size() != sizeof(ScriptStatus))
|
||||
return {};
|
||||
|
||||
auto msg = std::make_shared<ScriptStatusMessage>();
|
||||
const auto& decoded = *reinterpret_cast<const ScriptStatus*>(bytestream.data());
|
||||
msg->isCoreminiRunning = decoded.status.isRunning;
|
||||
msg->isEncrypted = decoded.status.isEncrypted;
|
||||
msg->sectorOverflows = decoded.sectorOverflows;
|
||||
msg->numRemainingSectorBuffers = decoded.numRemainingSectorBuffers;
|
||||
msg->lastSector = decoded.lastSector;
|
||||
msg->readBinSize = decoded.readBinSize;
|
||||
msg->minSector = decoded.minSector;
|
||||
msg->maxSector = decoded.maxSector;
|
||||
msg->currentSector = decoded.currentSector;
|
||||
msg->coreminiCreateTime = ((uint64_t)decoded.coreminiCreateTimeMsb << 32) | decoded.coreminiCreateTimeLsb;
|
||||
msg->fileChecksum = decoded.fileChecksum;
|
||||
msg->coreminiVersion = decoded.coreminiVersion;
|
||||
msg->coreminiHeaderSize = decoded.coreminiHeaderSize;
|
||||
msg->diagnosticErrorCode = decoded.diagErrCode;
|
||||
msg->diagnosticErrorCodeCount = decoded.diagErrCodeCount;
|
||||
msg->maxCoreminiSizeKB = decoded.maxCoreminiSizeKB;
|
||||
|
||||
return msg;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ std::shared_ptr<VersionMessage> HardwareVersionPacket::DecodeMainToMessage(const
|
||||
auto msg = std::make_shared<VersionMessage>(VersionMessage::MainChip);
|
||||
|
||||
msg->Versions.emplace_back();
|
||||
optional<DeviceAppVersion>& version = msg->Versions.back();
|
||||
std::optional<DeviceAppVersion>& version = msg->Versions.back();
|
||||
version.emplace();
|
||||
version->major = bytestream[1];
|
||||
version->minor = bytestream[2];
|
||||
@@ -26,7 +26,7 @@ std::shared_ptr<VersionMessage> HardwareVersionPacket::DecodeSecondaryToMessage(
|
||||
while(bytesLeft >= 3) {
|
||||
const bool versionValid = bytestream[bytestream.size() - bytesLeft + 0];
|
||||
msg->Versions.emplace_back();
|
||||
optional<DeviceAppVersion>& version = msg->Versions.back();
|
||||
std::optional<DeviceAppVersion>& version = msg->Versions.back();
|
||||
if(versionValid) {
|
||||
version.emplace();
|
||||
version->major = bytestream[bytestream.size() - bytesLeft + 1];
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "icsneo/communication/packet/wivicommandpacket.h"
|
||||
#include "icsneo/communication/message/wiviresponsemessage.h"
|
||||
#include <cstring>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
std::shared_ptr<WiVI::ResponseMessage> WiVI::CommandPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::Header))
|
||||
return {};
|
||||
|
||||
auto msg = std::make_shared<WiVI::ResponseMessage>();
|
||||
const auto& header = *reinterpret_cast<const WiVI::CommandPacket::Header*>(bytestream.data());
|
||||
switch(header.cmd) {
|
||||
case WiVI::Command::Result: {
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::Result))
|
||||
return {};
|
||||
|
||||
const auto& decoded = *reinterpret_cast<const WiVI::CommandPacket::Result*>(bytestream.data());
|
||||
msg->responseTo = decoded.responseTo;
|
||||
msg->success = decoded.result != 0;
|
||||
break;
|
||||
}
|
||||
case WiVI::Command::GetSignal: {
|
||||
// Use the SetSignal structure since it matches the response
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::SetSignal))
|
||||
return {};
|
||||
|
||||
const auto& setSignal = *reinterpret_cast<const WiVI::CommandPacket::SetSignal*>(bytestream.data());
|
||||
msg->responseTo = WiVI::Command::GetSignal;
|
||||
msg->value = setSignal.value.ValueInt32;
|
||||
break;
|
||||
}
|
||||
case WiVI::Command::GetAll: {
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll))
|
||||
return {};
|
||||
|
||||
const auto& getAll = *reinterpret_cast<const WiVI::CommandPacket::GetAll*>(bytestream.data());
|
||||
msg->responseTo = WiVI::Command::GetAll;
|
||||
msg->info.emplace();
|
||||
msg->info->sleepRequest = getAll.sleepRequest;
|
||||
msg->info->connectionTimeoutMinutes = getAll.connectionTimeoutMinutes;
|
||||
|
||||
// Check that we have enough data for the capture infos
|
||||
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll) + (sizeof(WiVI::CaptureInfo) * getAll.numCaptureInfos))
|
||||
return {};
|
||||
|
||||
msg->info->captures.resize(getAll.numCaptureInfos);
|
||||
for(uint16_t i = 0; i < getAll.numCaptureInfos; i++)
|
||||
msg->info->captures[i] = getAll.captureInfos[i];
|
||||
break;
|
||||
}
|
||||
default: // Unknown command response
|
||||
return {};
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WiVI::CommandPacket::GetSignal::Encode(WiVI::SignalType type) {
|
||||
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetSignal));
|
||||
auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetSignal*>(ret.data());
|
||||
|
||||
frame.header.cmd = WiVI::Command::GetSignal;
|
||||
frame.header.length = sizeof(frame) - sizeof(frame.header);
|
||||
frame.type = type;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WiVI::CommandPacket::SetSignal::Encode(WiVI::SignalType type, CoreMiniFixedPointValue value) {
|
||||
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::SetSignal));
|
||||
auto& frame = *reinterpret_cast<WiVI::CommandPacket::SetSignal*>(ret.data());
|
||||
|
||||
frame.header.cmd = WiVI::Command::SetSignal;
|
||||
frame.header.length = sizeof(frame) - sizeof(frame.header);
|
||||
frame.type = type;
|
||||
frame.value = value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WiVI::CommandPacket::GetAll::Encode() {
|
||||
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetAll));
|
||||
auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetAll*>(ret.data());
|
||||
|
||||
frame.header.cmd = WiVI::Command::GetAll;
|
||||
frame.header.length = sizeof(frame) - sizeof(frame.header);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> WiVI::CommandPacket::ClearUploads::Encode(const std::vector<uint8_t>& bitmask) {
|
||||
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::ClearUploads) + bitmask.size());
|
||||
auto& frame = *reinterpret_cast<WiVI::CommandPacket::ClearUploads*>(ret.data());
|
||||
|
||||
frame.header.cmd = WiVI::Command::ClearUploads;
|
||||
frame.header.length = uint16_t(ret.size() - sizeof(frame.header));
|
||||
memcpy(frame.bitmask, bitmask.data(), bitmask.size());
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,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) {
|
||||
@@ -50,19 +50,22 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
break;
|
||||
}
|
||||
|
||||
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
|
||||
packetLength = bytes[1] >> 4 & 0xF;
|
||||
packet.network = Network(bytes[1] & 0xF); // Lower nibble of the second byte is the network ID
|
||||
|
||||
if(packetLength == 0) {
|
||||
state = ReadState::ParseLongStylePacketHeader;
|
||||
checksum = false;
|
||||
headerSize = 6;
|
||||
} else {
|
||||
state = ReadState::GetData;
|
||||
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) {
|
||||
@@ -72,8 +75,7 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
|
||||
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
|
||||
@@ -84,9 +86,60 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
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
|
||||
@@ -99,16 +152,18 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
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);
|
||||
|
||||
+1126
-57
File diff suppressed because it is too large
Load Diff
+145
-35
@@ -15,10 +15,22 @@
|
||||
#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;
|
||||
|
||||
template<typename T>
|
||||
@@ -40,131 +52,209 @@ static void makeIfPIDMatches(const FoundDevice& dev, std::vector<std::shared_ptr
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
static std::vector<FoundDevice> driverFoundDevices;
|
||||
driverFoundDevices.clear();
|
||||
static std::vector<FoundDevice> newDriverFoundDevices;
|
||||
newDriverFoundDevices.clear();
|
||||
|
||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||
FirmIO::Find(driverFoundDevices);
|
||||
FirmIO::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_TCP
|
||||
TCP::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
|
||||
PCAP::Find(driverFoundDevices);
|
||||
PCAP::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_CDCACM
|
||||
CDCACM::Find(driverFoundDevices);
|
||||
CDCACM::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||
ANDROIDUSB::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_FTDI
|
||||
FTDI::Find(driverFoundDevices);
|
||||
FTDI::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
std::vector<std::shared_ptr<Device>> foundDevices;
|
||||
#ifdef ICSNEO_ENABLE_FTD3XX
|
||||
FTD3XX::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
// Weak because we don't want to keep devices open if they go out of scope elsewhere
|
||||
static std::vector<std::weak_ptr<Device>> foundDevices;
|
||||
|
||||
// Remove Devices that have dropped out of scope or are no longer present
|
||||
for (auto it = foundDevices.begin(); it != foundDevices.end(); ) {
|
||||
if (const auto device = it->lock()) {
|
||||
if (std::none_of(newDriverFoundDevices.begin(), newDriverFoundDevices.end(),
|
||||
[&](const auto& driverDevice) {
|
||||
return std::string(driverDevice.serial) == device->getSerial();
|
||||
}
|
||||
)) {
|
||||
it = foundDevices.erase(it); // Device not found by drivers but pointer has a >0 use_count, error?
|
||||
} else {
|
||||
++it; // Valid weak pointer and device found by drivers
|
||||
}
|
||||
} else {
|
||||
it = foundDevices.erase(it); // Weak pointer has a zero use_count
|
||||
}
|
||||
}
|
||||
|
||||
// Remove existing driver devices so we only create new ones
|
||||
for (auto it = newDriverFoundDevices.begin(); it != newDriverFoundDevices.end(); ) {
|
||||
if (std::any_of(foundDevices.begin(), foundDevices.end(),
|
||||
[&](const auto& weakDevice) {
|
||||
const auto device = weakDevice.lock();
|
||||
return device && std::string(it->serial) == device->getSerial();
|
||||
}
|
||||
)) {
|
||||
it = newDriverFoundDevices.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Device>> newFoundDevices;
|
||||
newFoundDevices.reserve(newDriverFoundDevices.size());
|
||||
// Offer found devices to each of the subclasses
|
||||
for (const FoundDevice& dev : driverFoundDevices) {
|
||||
for (const FoundDevice& dev : newDriverFoundDevices) {
|
||||
#ifdef __ETHERBADGE_H_
|
||||
makeIfSerialMatches<EtherBADGE>(dev, foundDevices);
|
||||
makeIfSerialMatches<EtherBADGE>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOOBD2PRO_H_
|
||||
makeIfSerialMatches<NeoOBD2PRO>(dev, foundDevices);
|
||||
makeIfSerialMatches<NeoOBD2PRO>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOOBD2SIM_H_
|
||||
makeIfSerialMatches<NeoOBD2SIM>(dev, foundDevices);
|
||||
makeIfSerialMatches<NeoOBD2SIM>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE_H_
|
||||
makeIfPIDMatches<NeoVIFIRE>(dev, foundDevices);
|
||||
makeIfPIDMatches<NeoVIFIRE>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE2_H_
|
||||
makeIfSerialMatches<NeoVIFIRE2>(dev, foundDevices);
|
||||
makeIfSerialMatches<NeoVIFIRE2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3_H_
|
||||
makeIfSerialMatches<NeoVIFIRE3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3FLEXRAY_H_
|
||||
makeIfSerialMatches<NeoVIFIRE3FlexRay>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIRED2_H_
|
||||
makeIfSerialMatches<NeoVIRED2>(dev, foundDevices);
|
||||
makeIfSerialMatches<NeoVIRED2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIION_H_
|
||||
makeIfPIDMatches<NeoVIION>(dev, foundDevices);
|
||||
makeIfPIDMatches<NeoVIION>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIPLASMA_H_
|
||||
makeIfPIDMatches<NeoVIPLASMA>(dev, foundDevices);
|
||||
makeIfPIDMatches<NeoVIPLASMA>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADA2B_H_
|
||||
makeIfSerialMatches<RADA2B>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET_H_
|
||||
makeIfSerialMatches<RADCOMET>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADEPSILON_H_
|
||||
makeIfSerialMatches<RADEpsilon>(dev, foundDevices);
|
||||
makeIfSerialMatches<RADEpsilon>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADGALAXY_H_
|
||||
makeIfSerialMatches<RADGalaxy>(dev, foundDevices);
|
||||
makeIfSerialMatches<RADGalaxy>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMARS_H_
|
||||
makeIfSerialMatches<RADMars>(dev, foundDevices);
|
||||
makeIfSerialMatches<RADMars>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADGIGASTAR_H_
|
||||
makeIfSerialMatches<RADGigastar>(dev, foundDevices);
|
||||
makeIfSerialMatches<RADGigastar>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADJUPITER_H_
|
||||
makeIfSerialMatches<RADJupiter>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON2_H_
|
||||
makeIfSerialMatches<RADMoon2>(dev, foundDevices);
|
||||
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, foundDevices);
|
||||
makeIfSerialMatches<RADMoonDuo>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADPLUTO_H_
|
||||
makeIfSerialMatches<RADPluto>(dev, foundDevices);
|
||||
makeIfSerialMatches<RADPluto>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2_H_
|
||||
makeIfSerialMatches<RADStar2>(dev, foundDevices);
|
||||
makeIfSerialMatches<RADStar2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADSUPERMOON_H_
|
||||
makeIfSerialMatches<RADSupermoon>(dev, foundDevices);
|
||||
makeIfSerialMatches<RADSupermoon>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN3_H_
|
||||
makeIfPIDMatches<ValueCAN3>(dev, foundDevices);
|
||||
makeIfPIDMatches<ValueCAN3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_1_H_
|
||||
makeIfSerialMatches<ValueCAN4_1>(dev, foundDevices);
|
||||
makeIfSerialMatches<ValueCAN4_1>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2_H_
|
||||
makeIfSerialMatches<ValueCAN4_2>(dev, foundDevices);
|
||||
makeIfSerialMatches<ValueCAN4_2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_2EL_H_
|
||||
makeIfSerialMatches<ValueCAN4_2EL>(dev, foundDevices);
|
||||
makeIfSerialMatches<ValueCAN4_2EL>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4_4_H_
|
||||
makeIfSerialMatches<ValueCAN4_4>(dev, foundDevices);
|
||||
makeIfSerialMatches<ValueCAN4_4>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VALUECAN4INDUSTRIAL_H_
|
||||
makeIfSerialMatches<ValueCAN4Industrial>(dev, foundDevices);
|
||||
makeIfSerialMatches<ValueCAN4Industrial>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __VIVIDCAN_H_
|
||||
makeIfSerialMatches<VividCAN>(dev, foundDevices);
|
||||
makeIfSerialMatches<VividCAN>(dev, newFoundDevices);
|
||||
#endif
|
||||
}
|
||||
|
||||
for(auto& device : foundDevices) {
|
||||
for(auto& device : newFoundDevices) {
|
||||
AddBuiltInExtensionsTo(device);
|
||||
}
|
||||
|
||||
return foundDevices;
|
||||
// Grab a weak pointer from the new devices
|
||||
foundDevices.insert(foundDevices.end(), newFoundDevices.begin(), newFoundDevices.end());
|
||||
|
||||
// Upgrade to shared for the return
|
||||
return std::vector<std::shared_ptr<Device>>(foundDevices.begin(), foundDevices.end());
|
||||
}
|
||||
|
||||
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
@@ -194,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
|
||||
@@ -202,6 +300,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
NeoVIPLASMA::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADA2B_H_
|
||||
RADA2B::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET_H_
|
||||
RADCOMET::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADEPSILON_H_
|
||||
RADEpsilon::DEVICE_TYPE,
|
||||
#endif
|
||||
@@ -218,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
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
optional<uint16_t> IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settings, optional<size_t> knownSize) {
|
||||
std::optional<uint16_t> IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settings, std::optional<size_t> knownSize) {
|
||||
const uint16_t* p = reinterpret_cast<const uint16_t*>(settings.data());
|
||||
size_t words = std::min(knownSize.value_or(0), settings.size());
|
||||
if(words % 2 == 1)
|
||||
return nullopt; // Somehow settings is not word aligned
|
||||
return std::nullopt; // Somehow settings is not word aligned
|
||||
words /= 2;
|
||||
|
||||
uint16_t gsCrc = 0;
|
||||
@@ -215,7 +215,7 @@ bool IDeviceSettings::apply(bool temporary) {
|
||||
bytestream[2] = GS_VERSION >> 8;
|
||||
bytestream[3] = (uint8_t)settings.size();
|
||||
bytestream[4] = (uint8_t)(settings.size() >> 8);
|
||||
optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
||||
std::optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
||||
if(!gsChecksum) {
|
||||
// Could not calculate the checksum for some reason
|
||||
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
||||
@@ -321,7 +321,7 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
|
||||
bytestream[2] = GS_VERSION >> 8;
|
||||
bytestream[3] = (uint8_t)settings.size();
|
||||
bytestream[4] = (uint8_t)(settings.size() >> 8);
|
||||
const optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
||||
const std::optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
||||
if(!gsChecksum) {
|
||||
// Could not calculate the checksum for some reason
|
||||
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
||||
@@ -638,27 +638,27 @@ bool IDeviceSettings::canTerminationBeEnabledFor(Network net) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
optional<bool> IDeviceSettings::isTerminationEnabledFor(Network net) const {
|
||||
std::optional<bool> IDeviceSettings::isTerminationEnabledFor(Network net) const {
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ICSNEO_UNALIGNED(const uint64_t*) terminationEnables = getTerminationEnables();
|
||||
if(terminationEnables == nullptr) {
|
||||
report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto cmNet = net.getCoreMini();
|
||||
if(!cmNet.has_value() || uint64_t(*cmNet) >= 64 || !isTerminationSupportedFor(net)) {
|
||||
report(APIEvent::Type::TerminationNotSupportedNetwork, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (*terminationEnables >> uint64_t(*cmNet)) & 0x1;
|
||||
|
||||
+62
-6
@@ -4,19 +4,34 @@
|
||||
using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, Disk::MemoryType memType) {
|
||||
|
||||
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
|
||||
uint64_t& cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
|
||||
|
||||
if(amount == 0)
|
||||
return 0;
|
||||
|
||||
optional<uint64_t> ret;
|
||||
pos += vsaOffset;
|
||||
|
||||
// First read from the cache
|
||||
std::optional<uint64_t> ret = readFromCache(pos, into, amount);
|
||||
if(ret == amount) // Full cache hit, we're done
|
||||
return ret;
|
||||
|
||||
const uint64_t totalAmount = amount;
|
||||
if(ret.has_value()) { // Partial cache hit
|
||||
pos += *ret;
|
||||
into += *ret;
|
||||
amount -= *ret;
|
||||
}
|
||||
|
||||
// Read into here if we can't read directly into the user buffer
|
||||
// That would be the case either if we don't want some at the
|
||||
// beginning or end of the block.
|
||||
std::vector<uint8_t> alignedReadBuffer;
|
||||
|
||||
pos += vsaOffset;
|
||||
const uint32_t idealBlockSize = getBlockSizeBounds().second;
|
||||
const uint64_t startBlock = pos / idealBlockSize;
|
||||
const uint32_t posWithinFirstBlock = static_cast<uint32_t>(pos % idealBlockSize);
|
||||
@@ -36,7 +51,7 @@ optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventh
|
||||
|
||||
const uint32_t posWithinCurrentBlock = (blocksProcessed ? 0 : posWithinFirstBlock);
|
||||
uint32_t curAmt = idealBlockSize - posWithinCurrentBlock;
|
||||
const auto amountLeft = amount - ret.value_or(0);
|
||||
const auto amountLeft = totalAmount - ret.value_or(0);
|
||||
if(curAmt > amountLeft)
|
||||
curAmt = static_cast<uint32_t>(amountLeft);
|
||||
|
||||
@@ -46,7 +61,7 @@ optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventh
|
||||
|
||||
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) {
|
||||
@@ -65,7 +80,48 @@ optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventh
|
||||
ret.emplace();
|
||||
*ret += std::min<uint64_t>(*readAmount, curAmt);
|
||||
blocksProcessed++;
|
||||
|
||||
if(blocksProcessed == blocks) {
|
||||
// Last block, add to the cache
|
||||
if(useAlignedReadBuffer) {
|
||||
cache = std::move(alignedReadBuffer);
|
||||
} else {
|
||||
if(cache.size() != idealBlockSize)
|
||||
cache.resize(idealBlockSize);
|
||||
memcpy(cache.data(), into + intoOffset, idealBlockSize);
|
||||
}
|
||||
cachePos = currentBlock * idealBlockSize;
|
||||
cachedAt = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount, MemoryType memType) {
|
||||
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
|
||||
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
|
||||
|
||||
if(pos <= cachePos + cache.size() && pos + amount >= cachePos)
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
std::optional<uint64_t> ReadDriver::readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter, MemoryType memType) {
|
||||
|
||||
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
|
||||
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
|
||||
|
||||
if(cache.empty())
|
||||
return std::nullopt; // Nothing in the cache
|
||||
|
||||
if(cachedAt + staleAfter < std::chrono::steady_clock::now())
|
||||
return std::nullopt; // Cache is stale
|
||||
|
||||
if(pos > cachePos + cache.size() || pos < cachePos)
|
||||
return std::nullopt; // Cache miss
|
||||
|
||||
const auto cacheOffset = pos - cachePos;
|
||||
const auto copyAmount = std::min<uint64_t>(cache.size() - cacheOffset, amount);
|
||||
memcpy(into, cache.data() + cacheOffset, static_cast<size_t>(copyAmount));
|
||||
return copyAmount;
|
||||
}
|
||||
+17
-26
@@ -4,26 +4,19 @@
|
||||
using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
const uint64_t WriteDriver::RetryAtomic = std::numeric_limits<uint64_t>::max();
|
||||
const APIEvent::Severity WriteDriver::NonatomicSeverity = APIEvent::Severity::EventInfo;
|
||||
|
||||
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) {
|
||||
std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
|
||||
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
if(amount == 0)
|
||||
return 0;
|
||||
|
||||
optional<uint64_t> ret;
|
||||
std::optional<uint64_t> ret;
|
||||
|
||||
const uint32_t idealBlockSize = getBlockSizeBounds().second;
|
||||
|
||||
// Write from here if we need to read-modify-write a block
|
||||
// That would be the case either if we don't want some at the
|
||||
// beginning or end of the block.
|
||||
std::vector<uint8_t> alignedWriteBuffer;
|
||||
|
||||
// Read to here, ideally this can be sent back to the device to
|
||||
// ensure an operation is atomic
|
||||
std::vector<uint8_t> atomicBuffer(idealBlockSize);
|
||||
std::vector<uint8_t> alignedWriteBuffer(idealBlockSize);
|
||||
|
||||
pos += vsaOffset;
|
||||
const uint64_t startBlock = pos / idealBlockSize;
|
||||
@@ -54,30 +47,24 @@ optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_even
|
||||
t = APIEvent::Type::EOFReached;
|
||||
report(t, s);
|
||||
};
|
||||
auto bytesTransferred = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
|
||||
atomicBuffer.data(), idealBlockSize, timeout);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(bytesTransferred != idealBlockSize)
|
||||
break; // readLogicalDisk reports its own errors
|
||||
|
||||
const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
|
||||
if(useAlignedWriteBuffer) {
|
||||
alignedWriteBuffer = atomicBuffer;
|
||||
auto read = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
|
||||
alignedWriteBuffer.data(), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(read != idealBlockSize)
|
||||
break; // readLogicalDisk reports its own errors
|
||||
|
||||
memcpy(alignedWriteBuffer.data() + posWithinCurrentBlock, from + fromOffset, curAmt);
|
||||
}
|
||||
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize, atomicBuffer.data(),
|
||||
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout);
|
||||
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
||||
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(bytesTransferred == RetryAtomic) {
|
||||
// The user may want to log these events in order to see how many atomic misses they are getting
|
||||
report(APIEvent::Type::AtomicOperationRetried, APIEvent::Severity::EventInfo);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!bytesTransferred.has_value() || *bytesTransferred < curAmt) {
|
||||
if(timeout < std::chrono::milliseconds::zero())
|
||||
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
|
||||
@@ -93,5 +80,9 @@ optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_even
|
||||
blocksProcessed++;
|
||||
}
|
||||
|
||||
// No matter how much succeeded, to be safe, we'll invalidate anything
|
||||
// we may have even tried to write, since it may have succeeded without
|
||||
// notifying, etc.
|
||||
readDriver.invalidateCache(pos, amount, memType);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,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
|
||||
@@ -12,178 +9,67 @@
|
||||
using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
|
||||
if(amount > getBlockSizeBounds().second)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(amount % getBlockSizeBounds().first != 0)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(pos % getBlockSizeBounds().first != 0)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
optional<uint64_t> ret;
|
||||
while(timeout > std::chrono::milliseconds(0) && !ret.has_value()) {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, std::chrono::milliseconds(100));
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
|
||||
std::optional<uint64_t> ret;
|
||||
unsigned int attempts = 4;
|
||||
while (attempts-- > 0)
|
||||
{
|
||||
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout, memType);
|
||||
if (ret.has_value())
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
|
||||
if(cachePos != pos || std::chrono::steady_clock::now() > cachedAt + CacheTime) {
|
||||
uint64_t sector = pos / SectorSize;
|
||||
|
||||
uint64_t largeSectorCount = amount / SectorSize;
|
||||
uint32_t sectorCount = uint32_t(largeSectorCount);
|
||||
if (largeSectorCount != uint64_t(sectorCount))
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
// The cache does not have this data, go get it
|
||||
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();
|
||||
std::mutex mutex;
|
||||
uint8_t* intoOffset = into;
|
||||
int64_t remaining = amount;
|
||||
bool complete = false;
|
||||
|
||||
const auto handle = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> message) {
|
||||
if(remaining > 0) {
|
||||
const auto diskdata = std::static_pointer_cast<DiskDataMessage>(message);
|
||||
|
||||
const auto& data = diskdata->data;
|
||||
std::copy(data.data(), data.data() + data.size(), intoOffset);
|
||||
|
||||
intoOffset += data.size();
|
||||
remaining -= data.size();
|
||||
|
||||
if(remaining == 0) {
|
||||
{
|
||||
std::scoped_lock<std::mutex> lk(mutex);
|
||||
complete = true;
|
||||
}
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, std::make_shared<MessageFilter>(Network::NetID::DiskData)));
|
||||
|
||||
if(skipping > data.size()) {
|
||||
skipping -= data.size();
|
||||
return;
|
||||
}
|
||||
size_t offset = skipping;
|
||||
skipping = 0;
|
||||
while(offset < data.size()) {
|
||||
size_t left = data.size() - offset;
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Going to process " << left << " bytes" << std::endl;
|
||||
#endif
|
||||
if(header.size() != HeaderLength) {
|
||||
if(header.empty() && left && data[offset] != 0xaa) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Incorrect header " << int(data[offset]) << ' ' << int(offset) << std::endl;
|
||||
#endif
|
||||
error = true;
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
// Did we get a correct header and at least one byte of data?
|
||||
const auto begin = data.begin() + offset;
|
||||
int32_t headerLeft = int32_t(HeaderLength - header.size());
|
||||
if(int32_t(left) < headerLeft) {
|
||||
// Not enough data here, grab what header we can and continue
|
||||
header.insert(header.end(), begin, data.end());
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << int(left) << " bytes of header at " << offset << " (incomplete " <<
|
||||
header.size() << ')' << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
header.insert(header.end(), begin, begin + headerLeft);
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << int(headerLeft) << " bytes of header at " << offset << " (complete " <<
|
||||
header.size() << ')' << std::endl;
|
||||
#endif
|
||||
offset += headerLeft;
|
||||
|
||||
if(header[1] == uint8_t(Network::NetID::RED)) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got extended response " << int(offset) << std::endl;
|
||||
#endif
|
||||
// This is the extended command response, not all devices send this
|
||||
// If we got it, we need to figure out how much more data to ignore
|
||||
uint16_t length = (header[2] + (header[3] << 8));
|
||||
// Try for another header after this, regardless how much we choose
|
||||
// to skip and how we skip it
|
||||
header.clear();
|
||||
if(length <= 6) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Incorrect extended response length " << int(length) << ' ' << int(offset) << std::endl;
|
||||
#endif
|
||||
error = true;
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
length -= 7;
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Skipping " << int(length) << ' ' << int(left) << std::endl;
|
||||
#endif
|
||||
if(left < length) {
|
||||
skipping = length - left;
|
||||
return;
|
||||
}
|
||||
offset += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The device tells us how much it's sending us before the next header
|
||||
receiving = (header[5] | (header[6] << 8));
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Started packet of size " << receiving << " bytes" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
left = data.size() - offset;
|
||||
auto count = uint16_t(std::min<uint64_t>(std::min<uint64_t>(receiving - receivedCurrent, left), amount - received));
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "With " << int(left) << " bytes " << int(offset) << std::endl;
|
||||
#endif
|
||||
memcpy(cache.data() + 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(); });
|
||||
|
||||
if(error)
|
||||
return 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,18 +82,16 @@ optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Com
|
||||
uint8_t((sectorCount >> 8) & 0xff),
|
||||
uint8_t((sectorCount >> 16) & 0xff),
|
||||
uint8_t((sectorCount >> 24) & 0xff),
|
||||
});
|
||||
if(error)
|
||||
return nullopt;
|
||||
|
||||
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
|
||||
if(hitTimeout || error)
|
||||
return nullopt;
|
||||
|
||||
cachedAt = std::chrono::steady_clock::now();
|
||||
cachePos = pos;
|
||||
})) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
memcpy(into, cache.data(), size_t(amount));
|
||||
return amount;
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
const auto successful = cv.wait_for(lk, timeout, [&](){ return complete; });
|
||||
com.removeMessageCallback(handle);
|
||||
|
||||
if(!successful)
|
||||
return std::nullopt;
|
||||
|
||||
return amount - remaining;
|
||||
}
|
||||
|
||||
+4
-4
@@ -8,7 +8,7 @@ using namespace icsneo;
|
||||
|
||||
// The FAT driver can only be accessed by one caller at a time, since it relies on globals
|
||||
static std::mutex fatDriverMutex;
|
||||
static std::function< optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskReadFn;
|
||||
static std::function< std::optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskReadFn;
|
||||
|
||||
extern "C" DRESULT disk_read(BYTE, BYTE* buff, LBA_t sector, UINT count) {
|
||||
static_assert(Disk::SectorSize == 512, "FatFs expects 512 byte sectors");
|
||||
@@ -32,17 +32,17 @@ static uint64_t ClusterToSector(const FATFS& fs, DWORD cluster) {
|
||||
return fs.database + (LBA_t)fs.csize * (cluster - 2);
|
||||
}
|
||||
|
||||
optional<uint64_t> Disk::FindVSAInFAT(std::function< optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskRead) {
|
||||
std::optional<uint64_t> Disk::FindVSAInFAT(std::function< std::optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskRead) {
|
||||
std::lock_guard<std::mutex> lk(fatDriverMutex);
|
||||
diskReadFn = diskRead;
|
||||
|
||||
FATFS fs = {};
|
||||
if (f_mount(&fs, (const TCHAR*)_TEXT(""), 0) != FR_OK)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
FIL logData = {};
|
||||
if (f_open(&logData, (const TCHAR*)_TEXT("0:\\LOG_DATA.VSA"), FA_READ) != FR_OK)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
return ClusterToSector(fs, logData.obj.sclust) * uint64_t(Disk::SectorSize);
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
#include "icsneo/disk/neomemorydiskdriver.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
|
||||
if(pos % SectorSize != 0)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(amount != SectorSize)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(cachePos != pos || std::chrono::steady_clock::now() > cachedAt + CacheTime) {
|
||||
// The cache does not have this data, go get it
|
||||
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),
|
||||
@@ -38,42 +39,30 @@ optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& co
|
||||
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
||||
if(!sdmsg || sdmsg->data.size() != SectorSize) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
memcpy(cache.data(), sdmsg->data.data(), SectorSize);
|
||||
cachedAt = std::chrono::steady_clock::now();
|
||||
cachePos = pos;
|
||||
}
|
||||
|
||||
memcpy(into, cache.data(), SectorSize);
|
||||
memcpy(into, sdmsg->data.data(), SectorSize);
|
||||
return SectorSize;
|
||||
}
|
||||
|
||||
optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, const uint8_t* atomicBuf, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
|
||||
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
|
||||
|
||||
if(pos % SectorSize != 0)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(amount != SectorSize)
|
||||
return nullopt;
|
||||
|
||||
// Clear the cache if we're writing to the cached sector
|
||||
if(pos == cachePos)
|
||||
cachedAt = std::chrono::time_point<std::chrono::steady_clock>();
|
||||
|
||||
// Requesting an atomic operation, but neoMemory does not support it
|
||||
// Continue on anyway but warn the caller
|
||||
if(atomicBuf != nullptr)
|
||||
report(APIEvent::Type::AtomicOperationCompletedNonatomically, NonatomicSeverity);
|
||||
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),
|
||||
@@ -86,7 +75,7 @@ optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& c
|
||||
}, NeoMemoryDone, timeout);
|
||||
|
||||
if(!msg)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
return SectorSize;
|
||||
}
|
||||
+12
-12
@@ -3,26 +3,26 @@
|
||||
using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
std::optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
std::optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
|
||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
std::optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
|
||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
||||
uint64_t, const uint8_t*, const uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
std::optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -6,50 +6,46 @@
|
||||
using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
std::optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
|
||||
if(amount > getBlockSizeBounds().second)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(amount % getBlockSizeBounds().first != 0)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(pos % getBlockSizeBounds().first != 0)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
if(cachePos != pos || std::chrono::steady_clock::now() > cachedAt + CacheTime) {
|
||||
uint64_t largeSector = pos / SectorSize;
|
||||
uint32_t sector = uint32_t(largeSector);
|
||||
if (largeSector != uint64_t(sector))
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
// The cache does not have this data, go get it
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
uint32_t copied = 0;
|
||||
bool error = false;
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
auto cb = com.addMessageCallback(MessageCallback([&](std::shared_ptr<Message> msg) {
|
||||
auto cb = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> msg) {
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
|
||||
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
||||
if(!sdmsg || cache.size() < copied + sdmsg->data.size()) {
|
||||
if(!sdmsg || amount < copied + sdmsg->data.size()) {
|
||||
error = true;
|
||||
lk.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
// Invalidate the cache here in case we fail half-way through
|
||||
cachedAt = std::chrono::steady_clock::time_point();
|
||||
|
||||
memcpy(cache.data() + copied, sdmsg->data.data(), sdmsg->data.size());
|
||||
memcpy(into + copied, sdmsg->data.data(), sdmsg->data.size());
|
||||
copied += uint32_t(sdmsg->data.size());
|
||||
if(copied == amount) {
|
||||
lk.unlock();
|
||||
cv.notify_all();
|
||||
|
||||
}
|
||||
}, NeoMemorySDRead));
|
||||
|
||||
@@ -67,12 +63,7 @@ optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication&
|
||||
com.removeMessageCallback(cb);
|
||||
|
||||
if(hitTimeout)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
cachedAt = std::chrono::steady_clock::now();
|
||||
cachePos = pos;
|
||||
}
|
||||
|
||||
memcpy(into, cache.data(), size_t(amount));
|
||||
return amount;
|
||||
}
|
||||
@@ -100,3 +100,18 @@ Write Blocking Status
|
||||
The write blocking status of the device determines the behavior of attempting to transmit to the device (likely via sending messages) with a large backlog of messages.
|
||||
If write blocking is enabled, then the transmitting thread will wait for the entire buffer to be transmitted.
|
||||
If write blocking is disabled, then the attempt to transmit will simply fail and an error will be logged on the calling thread.
|
||||
|
||||
A2B 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. 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 ``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.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
|
||||
|
||||
@@ -1,6 +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)
|
||||
@@ -10,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()
|
||||
@@ -18,6 +33,26 @@ if(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE)
|
||||
add_subdirectory(cpp/interactive)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE)
|
||||
add_subdirectory(cpp/a2b)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE)
|
||||
add_subdirectory(cpp/lin)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE)
|
||||
add_subdirectory(cpp/livedata)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE)
|
||||
add_subdirectory(cpp/coremini)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE)
|
||||
add_subdirectory(cpp/mdio)
|
||||
endif()
|
||||
|
||||
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
|
||||
# add_subdirectory(csharp)
|
||||
# endif()
|
||||
|
||||
@@ -14,3 +14,4 @@ add_executable(libicsneoc-interactive-example src/main.c)
|
||||
if(UNIX)
|
||||
target_link_libraries(libicsneoc-interactive-example ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
target_link_libraries(libicsneoc-interactive-example icsneoc)
|
||||
@@ -1,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;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneocpp-a2b VERSION 0.2.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Add an include directory like so if desired
|
||||
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
# Enable Warnings
|
||||
if(MSVC)
|
||||
# Force to always compile with W4
|
||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
endif()
|
||||
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
# Add libicsneo, usually a git submodule within your project works well
|
||||
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
|
||||
|
||||
add_executable(libicsneocpp-a2b src/a2b.cpp)
|
||||
target_link_libraries(libicsneocpp-a2b icsneocpp)
|
||||
@@ -0,0 +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 <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>
|
||||
|
||||
static constexpr size_t numFramesInWave = 48;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
std::vector<uint8_t> sineWave;
|
||||
sineWave.reserve(sineWaveSamples.size() + sizeof(header));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Example 5: Wave loop back
|
||||
void example5(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
// Listen on another thread
|
||||
std::thread listenerThread{listener};
|
||||
|
||||
// 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 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;
|
||||
}
|
||||
@@ -614,7 +614,7 @@ int main() {
|
||||
switch(selection) {
|
||||
case '1':
|
||||
{
|
||||
int callbackID = selectedDevice->addMessageCallback(icsneo::MessageCallback([](std::shared_ptr<icsneo::Message> msg){
|
||||
int callbackID = selectedDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([](std::shared_ptr<icsneo::Message> msg){
|
||||
printMessage(msg);
|
||||
}));
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#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;
|
||||
@@ -97,8 +101,30 @@ int main() {
|
||||
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;
|
||||
@@ -127,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;
|
||||
@@ -141,7 +167,7 @@ int main() {
|
||||
// We can also register a handler
|
||||
std::cout << "\tStreaming messages in for 3 seconds... " << std::endl;
|
||||
// MessageCallbacks are powerful, and can filter on things like ArbID for you. See the documentation
|
||||
auto handler = device->addMessageCallback(icsneo::MessageCallback([](std::shared_ptr<icsneo::Message> message) {
|
||||
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([](std::shared_ptr<icsneo::Message> message) {
|
||||
switch(message->type) {
|
||||
case icsneo::Message::Type::Frame: {
|
||||
// A message of type Frame is guaranteed to be a Frame, so we can static cast safely
|
||||
@@ -289,7 +315,7 @@ int main() {
|
||||
if(!emisc.empty()) {
|
||||
std::cout << "\tReading EMisc values..." << std::endl;
|
||||
for(const auto& io : emisc) {
|
||||
icsneo::optional<double> val = device->getAnalogIO(icsneo::IO::EMisc, io.number);
|
||||
std::optional<double> val = device->getAnalogIO(icsneo::IO::EMisc, io.number);
|
||||
std::cout << "\t\tEMISC" << io.number;
|
||||
if(val.has_value())
|
||||
std::cout << " - OK (" << val.value() << "V)" << std::endl;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -5506,18 +5506,6 @@ SWIGEXPORT jint JNICALL Java_icsneojavaJNI_ICSNEO_1NETID_1ISO_1get(JNIEnv *jenv,
|
||||
}
|
||||
|
||||
|
||||
SWIGEXPORT jint JNICALL Java_icsneojavaJNI_ICSNEO_1NETID_1ISOPIC_1get(JNIEnv *jenv, jclass jcls) {
|
||||
jint jresult = 0 ;
|
||||
int result;
|
||||
|
||||
(void)jenv;
|
||||
(void)jcls;
|
||||
result = (int)(10);
|
||||
jresult = (jint)result;
|
||||
return jresult;
|
||||
}
|
||||
|
||||
|
||||
SWIGEXPORT jint JNICALL Java_icsneojavaJNI_ICSNEO_1NETID_1MAIN51_1get(JNIEnv *jenv, jclass jcls) {
|
||||
jint jresult = 0 ;
|
||||
int result;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
MessageMaxLengthExceeded = 0x1012,
|
||||
ValueNotYetPresent = 0x1013,
|
||||
Timeout = 0x1014,
|
||||
WiVINotSupported = 0x1015,
|
||||
|
||||
// Device Events
|
||||
PollingMessageOverflow = 0x2000,
|
||||
@@ -86,6 +87,23 @@ public:
|
||||
SettingsDefaultsUsed = 0x2033,
|
||||
AtomicOperationRetried = 0x2034,
|
||||
AtomicOperationCompletedNonatomically = 0x2035,
|
||||
WiVIStackRefreshFailed = 0x2036,
|
||||
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,
|
||||
@@ -98,11 +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,
|
||||
|
||||
|
||||
// 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,
|
||||
|
||||
@@ -25,6 +25,10 @@ public:
|
||||
rhs.fnOnDeath = std::function<void(void)>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Allow checking for empty Lifetimes
|
||||
bool empty() const { return fnOnDeath.operator bool(); }
|
||||
operator bool() const { return empty(); }
|
||||
private:
|
||||
std::function<void(void)> fnOnDeath;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,10 @@ enum class Command : uint8_t {
|
||||
EnableNetworkCommunicationEx = 0x08,
|
||||
NeoReadMemory = 0x40,
|
||||
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
|
||||
@@ -23,14 +27,19 @@ enum class Command : uint8_t {
|
||||
ReadSettings = 0xC7, // Previously known as 3G_READ_SETTINGS_EX
|
||||
SetVBattMonitor = 0xDB, // Previously known as RED_CMD_CM_VBATT_MONITOR
|
||||
RequestBitSmash = 0xDC, // Previously known as RED_CMD_CM_BITSMASH
|
||||
WiVICommand = 0xDD, // Previously known as RED_CMD_WIVI_COMM
|
||||
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,
|
||||
PHYControlRegisters = 0xEF
|
||||
CoreMiniPreload = 0xF4, // Previously known as RED_CMD_COREMINI_PRELOAD
|
||||
PHYControlRegisters = 0xEF,
|
||||
};
|
||||
|
||||
enum class ExtendedCommand : uint16_t {
|
||||
GenericReturn = 0x0000,
|
||||
GetDiskDetails = 0x0010,
|
||||
DiskFormatStart = 0x0011,
|
||||
DiskFormatCancel = 0x0012,
|
||||
@@ -39,8 +48,37 @@ 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 {
|
||||
OK = 0,
|
||||
InvalidCommand = -1,
|
||||
InvalidState = -2,
|
||||
OperationFailed = -3,
|
||||
OperationPending = -4,
|
||||
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"
|
||||
@@ -56,10 +58,11 @@ public:
|
||||
bool sendCommand(ExtendedCommand cmd, std::vector<uint8_t> arguments = {});
|
||||
bool getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
|
||||
std::shared_ptr<SerialNumberMessage> getSerialNumberSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
|
||||
optional< std::vector< optional<DeviceAppVersion> > > getVersionsSync(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 MessageCallback& cb);
|
||||
int addMessageCallback(const std::shared_ptr<MessageCallback>& cb);
|
||||
bool removeMessageCallback(int id);
|
||||
std::shared_ptr<Message> waitForMessageSync(
|
||||
const std::shared_ptr<MessageFilter>& f = {},
|
||||
@@ -83,11 +86,12 @@ public:
|
||||
protected:
|
||||
static int messageCallbackIDCounter;
|
||||
std::mutex messageCallbacksLock;
|
||||
std::map<int, MessageCallback> messageCallbacks;
|
||||
std::map<int, std::shared_ptr<MessageCallback>> messageCallbacks;
|
||||
std::atomic<bool> closing{false};
|
||||
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 syncMessageMutex;
|
||||
|
||||
void dispatchMessage(const std::shared_ptr<Message>& msg);
|
||||
void handleInput(Packetizer& p, std::vector<uint8_t>& readBytes);
|
||||
|
||||
@@ -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__
|
||||
@@ -0,0 +1,443 @@
|
||||
#ifndef __A2BMESSAGE_H_
|
||||
#define __A2BMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
typedef uint32_t A2BPCMSample;
|
||||
|
||||
class A2BMessage : public Frame {
|
||||
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;
|
||||
};
|
||||
|
||||
public:
|
||||
enum class A2BDirection : uint8_t {
|
||||
Downstream = 0,
|
||||
Upstream = 1
|
||||
};
|
||||
|
||||
A2BMessage(uint8_t nChannels, bool chSize16, size_t size) :
|
||||
numChannels(nChannels),
|
||||
channelSize16(chSize16)
|
||||
{
|
||||
data.resize(std::min(roundNextMultiple(size, getFrameSize()),(size_t)maxSize), 0);
|
||||
}
|
||||
|
||||
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 {
|
||||
A2BPCMSample* samps = (A2BPCMSample*)buf;
|
||||
sample = sample << 8;
|
||||
std::fill(samps, samps + data.size()/4, sample);
|
||||
}
|
||||
}
|
||||
|
||||
bool fillFrame(A2BPCMSample sample, size_t frame) {
|
||||
if(frame >= getNumFrames()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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 {
|
||||
if(
|
||||
channel >= numChannels ||
|
||||
frame >= getNumFrames()
|
||||
) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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<A2BPCMSample> getSample(size_t sampleNum) const {
|
||||
if(sampleNum >= getNumSamples()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
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 sample;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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 data.size()/((size_t)getBytesPerSample());
|
||||
}
|
||||
|
||||
uint8_t getNumChannels() const {
|
||||
return numChannels;
|
||||
}
|
||||
|
||||
uint8_t getBitDepth() const {
|
||||
return channelSize16 ? 16 : 24;
|
||||
}
|
||||
|
||||
uint8_t getBytesPerSample() const {
|
||||
return channelSize16 ? 2 : 4;
|
||||
}
|
||||
|
||||
bool isTxMsg() const {
|
||||
return txmsg;
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -29,6 +29,8 @@ public:
|
||||
MessageCallback(MessageFilter f, fn_messageCallback cb)
|
||||
: MessageCallback(cb, std::make_shared<MessageFilter>(f)) {}
|
||||
|
||||
virtual ~MessageCallback() = default;
|
||||
|
||||
virtual bool callIfMatch(const std::shared_ptr<Message>& message) const {
|
||||
bool ret = filter->match(message);
|
||||
if(ret)
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef __A2BWAVOUTPUT_H_
|
||||
#define __A2BWAVOUTPUT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
|
||||
#include "icsneo/communication/message/a2bmessage.h"
|
||||
#include "icsneo/device/device.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class A2BWAVOutput : public StreamOutput {
|
||||
public:
|
||||
A2BWAVOutput(const char* filename, uint32_t sampleRate = 44100)
|
||||
: StreamOutput(filename), wavSampleRate(sampleRate) {}
|
||||
|
||||
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;
|
||||
|
||||
bool callIfMatch(const std::shared_ptr<Message>& message) const override;
|
||||
|
||||
void close() const;
|
||||
|
||||
~A2BWAVOutput() override {
|
||||
if(!closed) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t wavSampleRate;
|
||||
mutable uint32_t streamStartPos;
|
||||
mutable bool firstMessageFlag = true;
|
||||
mutable bool closed = false;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
#ifndef __STREAMOUTPUT_H_
|
||||
#define __STREAMOUTPUT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/callback/messagecallback.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#define WAV_SAMPLE_RATE_44100 44100
|
||||
#define WAV_SAMPLE_RATE_48000 48000
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
struct WaveFileHeader {
|
||||
|
||||
static constexpr uint32_t WAVE_CHUNK_ID = 0x46464952; // "RIFF"
|
||||
static constexpr uint32_t WAVE_FORMAT = 0x45564157; // "WAVE"
|
||||
static constexpr uint32_t WAVE_SUBCHUNK1_ID = 0x20746d66; // "fmt "
|
||||
static constexpr uint32_t WAVE_SUBCHUNK2_ID = 0x61746164; // "data"
|
||||
static constexpr uint16_t WAVE_SUBCHUNK1_SIZE = 16;
|
||||
static constexpr uint16_t WAVE_AUDIO_FORMAT_PCM = 1;
|
||||
static constexpr uint32_t WAVE_DEFAULT_SIZE = 0; // Default size for streamed wav
|
||||
|
||||
uint32_t chunkId = WAVE_CHUNK_ID; // "RIFF"
|
||||
uint32_t chunkSize = WAVE_DEFAULT_SIZE; // number of bytes to follow
|
||||
uint32_t format = WAVE_FORMAT; // "WAVE"
|
||||
uint32_t subchunk1Id = WAVE_SUBCHUNK1_ID; // "fmt "
|
||||
uint32_t subchunk1Size = WAVE_SUBCHUNK1_SIZE; // number of bytes in *this* subchunk (always 16)
|
||||
uint16_t audioFormat = WAVE_AUDIO_FORMAT_PCM; // 1 for PCM
|
||||
uint16_t numChannels; // number of channels
|
||||
uint32_t sampleRate; // sample rate in Hz
|
||||
uint32_t byteRate; // bytes per second of audio: sampleRate * numChannels * (bitsPerSample / 8)
|
||||
uint16_t blockAlign; // alignment of each block in bytes: numChannels * (bitsPerSample / 8)
|
||||
uint16_t bitsPerSample; // number of bits in each sample
|
||||
uint32_t subchunk2Id = WAVE_SUBCHUNK2_ID; // "data"
|
||||
uint32_t subchunk2Size = WAVE_DEFAULT_SIZE; // number of bytes to follow
|
||||
|
||||
WaveFileHeader() = default;
|
||||
|
||||
WaveFileHeader(uint16_t nChannels, uint32_t sRate, uint16_t bps, uint32_t nSamples = 0) {
|
||||
setHeader(nChannels, sRate, bps, nSamples);
|
||||
}
|
||||
|
||||
void setHeader(uint16_t newNumChannels, uint32_t newSampleRate, uint16_t newBitsPerSample, uint32_t numSamples = 0) {
|
||||
numChannels = newNumChannels;
|
||||
sampleRate = newSampleRate;
|
||||
bitsPerSample = newBitsPerSample;
|
||||
blockAlign = numChannels * (bitsPerSample / 8);
|
||||
byteRate = sampleRate * numChannels * (bitsPerSample / 8);
|
||||
if(numSamples != 0) {
|
||||
setNumSamples(numSamples);
|
||||
}
|
||||
}
|
||||
|
||||
void setNumSamples(uint32_t numSamples) {
|
||||
subchunk2Size = numSamples * numChannels * (bitsPerSample / 8);
|
||||
chunkSize = subchunk2Size + 36;
|
||||
}
|
||||
|
||||
void write(const std::unique_ptr<std::ostream>& stream) {
|
||||
|
||||
stream->write(reinterpret_cast<const char*>(&chunkId), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&format), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&subchunk1Id), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&subchunk1Size), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&audioFormat), 2);
|
||||
stream->write(reinterpret_cast<const char*>(&numChannels), 2);
|
||||
stream->write(reinterpret_cast<const char*>(&sampleRate), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&byteRate), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&blockAlign), 2);
|
||||
stream->write(reinterpret_cast<const char*>(&bitsPerSample), 2);
|
||||
stream->write(reinterpret_cast<const char*>(&subchunk2Id), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&subchunk2Size), 4);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
class StreamOutput : public MessageCallback {
|
||||
public:
|
||||
StreamOutput(std::unique_ptr<std::ostream>&& os, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
|
||||
: MessageCallback(cb, f), stream(std::move(os)) {}
|
||||
|
||||
StreamOutput(const char* filename, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
|
||||
: MessageCallback(cb, f) {
|
||||
stream = std::make_unique<std::ofstream>(filename, std::ios::binary);
|
||||
}
|
||||
|
||||
StreamOutput(const char* filename) : MessageCallback([](std::shared_ptr<Message> msg) {}) {
|
||||
stream = std::make_unique<std::ofstream>(filename, std::ios::binary);
|
||||
}
|
||||
|
||||
StreamOutput(std::unique_ptr<std::ostream>&& os) : MessageCallback([](std::shared_ptr<Message> msg) {}), stream(std::move(os)) {}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<std::ostream> stream;
|
||||
|
||||
void write(void* msg, std::streamsize size) const {
|
||||
stream->write(reinterpret_cast<const char*>(msg), size);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef __EXTENDEDRESPONSEMESSAGE_H_
|
||||
#define __EXTENDEDRESPONSEMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class ExtendedResponseMessage : public Message {
|
||||
public:
|
||||
ExtendedResponseMessage(ExtendedCommand cmd, ExtendedResponse resp)
|
||||
: Message(Message::Type::ExtendedResponse), command(cmd), response(resp) {}
|
||||
|
||||
const ExtendedCommand command;
|
||||
const ExtendedResponse response;
|
||||
|
||||
#pragma pack(push, 2)
|
||||
struct ResponseHeader {
|
||||
ExtendedCommand command;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
struct PackedGenericResponse {
|
||||
ResponseHeader header;
|
||||
ExtendedCommand command; // `header.command` is ExtendedCommand::GenericReturn, this is the real command
|
||||
ExtendedResponse returnCode;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#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
|
||||
@@ -34,6 +34,8 @@ public:
|
||||
return false;
|
||||
if(!matchNetID(frame.network.getNetID()))
|
||||
return false;
|
||||
} else if (netid != Network::NetID::Any || networkType != Network::Type::Any) {
|
||||
return false; // Filtering on a NetID or Type, but this message doesn't have one
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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,48 @@
|
||||
#ifndef __I2CMESSAGE_H_
|
||||
#define __I2CMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class I2CMessage : public Frame {
|
||||
public:
|
||||
enum class DeviceMode : uint8_t {
|
||||
Target = 0,
|
||||
Controller = 1
|
||||
};
|
||||
|
||||
enum class Direction : uint8_t {
|
||||
Write = 0,
|
||||
Read = 1
|
||||
};
|
||||
|
||||
bool isExtendedID = false;
|
||||
bool isTXMsg = false;
|
||||
bool txError = false;
|
||||
bool txLostArb = false;
|
||||
bool txAborted = false;
|
||||
bool txNack = false;
|
||||
bool txTimeout = false;
|
||||
uint16_t stats = static_cast<uint16_t>(0x0000u);
|
||||
uint16_t address;
|
||||
Direction direction;
|
||||
DeviceMode deviceMode;
|
||||
|
||||
//Must contain the target register address to read or write
|
||||
std::vector<uint8_t> controlBytes;
|
||||
|
||||
//The device expects a dataBytes payload even if you're reading
|
||||
//In the case of a read these bytes aren't interesting, but they have to be there
|
||||
//Add bytes to write, or the same number of junk bytes you expect the device send back
|
||||
std::vector<uint8_t> dataBytes;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user