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 |
+6
-1
@@ -8,4 +8,9 @@ CMakeSettings.json
|
|||||||
third-party/concurrentqueue/benchmarks
|
third-party/concurrentqueue/benchmarks
|
||||||
third-party/concurrentqueue/tests
|
third-party/concurrentqueue/tests
|
||||||
*.bak
|
*.bak
|
||||||
.vs
|
.vs
|
||||||
|
.cache
|
||||||
|
*.wav
|
||||||
|
*.orig
|
||||||
|
examples/csharp/bin
|
||||||
|
examples/csharp/obj
|
||||||
+201
-2
@@ -1,7 +1,14 @@
|
|||||||
|
variables:
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- test
|
- test
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# Windows
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
build windows/x64:
|
build windows/x64:
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
@@ -24,7 +31,7 @@ test windows/x64:
|
|||||||
- build windows/x64
|
- build windows/x64
|
||||||
tags:
|
tags:
|
||||||
- icsneo-windows
|
- icsneo-windows
|
||||||
timeout: 3m
|
timeout: 5m
|
||||||
|
|
||||||
build windows/x86:
|
build windows/x86:
|
||||||
stage: build
|
stage: build
|
||||||
@@ -48,4 +55,196 @@ test windows/x86:
|
|||||||
- build windows/x86
|
- build windows/x86
|
||||||
tags:
|
tags:
|
||||||
- icsneo-windows
|
- 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
|
||||||
|
|||||||
+150
-39
@@ -1,6 +1,8 @@
|
|||||||
cmake_minimum_required(VERSION 3.2)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
project(libicsneo VERSION 0.3.0)
|
project(libicsneo VERSION 0.3.0)
|
||||||
|
|
||||||
|
cmake_policy(SET CMP0074 NEW)
|
||||||
|
|
||||||
option(LIBICSNEO_BUILD_TESTS "Build all tests." OFF)
|
option(LIBICSNEO_BUILD_TESTS "Build all tests." OFF)
|
||||||
option(LIBICSNEO_BUILD_DOCS "Build documentation. Don't use in Visual Studio." OFF)
|
option(LIBICSNEO_BUILD_DOCS "Build documentation. Don't use in Visual Studio." OFF)
|
||||||
option(LIBICSNEO_BUILD_EXAMPLES "Build examples." ON)
|
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_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_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_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_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)
|
if(NOT CMAKE_CXX_STANDARD)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
@@ -34,6 +39,9 @@ if(MSVC)
|
|||||||
else()
|
else()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||||
endif()
|
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)
|
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
||||||
endif()
|
endif()
|
||||||
@@ -93,7 +101,7 @@ if(LIBICSNEO_BUILD_DOCS)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
if(WIN32)
|
||||||
set(PLATFORM_SRC
|
set(PLATFORM_SRC
|
||||||
platform/windows/registry.cpp
|
platform/windows/registry.cpp
|
||||||
)
|
)
|
||||||
@@ -110,6 +118,12 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
|||||||
platform/windows/vcp.cpp
|
platform/windows/vcp.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
elseif(LIBICSNEO_ENABLE_ANDROIDUSB)
|
||||||
|
list(APPEND PLATFORM_SRC
|
||||||
|
platform/android/androidusb.cpp
|
||||||
|
)
|
||||||
|
|
||||||
else() # Darwin or Linux
|
else() # Darwin or Linux
|
||||||
set(PLATFORM_SRC)
|
set(PLATFORM_SRC)
|
||||||
|
|
||||||
@@ -154,6 +168,61 @@ else() # Darwin or Linux
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(LIBICSNEO_ENABLE_FTD3XX)
|
||||||
|
if(NOT FTD3XX_ROOT) # allow system override
|
||||||
|
include(FetchContent)
|
||||||
|
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-x64.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ee1289cdd5023de67275aaa1712e8e32e73e825a8392efb0a76f161e357fbdc9")
|
||||||
|
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-i686.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=bcd31f5c3cb39ecb9b09db5ce722bd79de6e0f813130e7b8f2c86879a60d8ff0")
|
||||||
|
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-macos-universal2.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=74c0d35f04242d0841532c6325eb2932b8f627e3c395382a15d9f39974d27a90")
|
||||||
|
elseif(UNIX)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-x64.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=a9dc7eb6948c8977fbd79f6700bec6f882d3da5667aea8f2175b8d1f6f08e456")
|
||||||
|
endif()
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-aarch64.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=da4b90ea1cbb905874cd159ad2ab8c1bdde65cc22b3aa55bf2b5fd85ca6efd22")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-armhf.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=d813008117422cae958f7c71a065cdac0d31dca3b24809d3ab5e13604a9c3fb1")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOT LIBICSNEO_FTD3XX_URL)
|
||||||
|
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
|
||||||
|
endif()
|
||||||
|
FetchContent_Declare(
|
||||||
|
ftdi3xx
|
||||||
|
URL ${LIBICSNEO_FTD3XX_URL}
|
||||||
|
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
|
||||||
|
)
|
||||||
|
FetchContent_GetProperties(ftdi3xx)
|
||||||
|
if(NOT ftdi3xx_POPULATED)
|
||||||
|
FetchContent_Populate(ftdi3xx)
|
||||||
|
endif()
|
||||||
|
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
|
find_package(FTD3XX REQUIRED)
|
||||||
|
list(APPEND PLATFORM_SRC
|
||||||
|
platform/ftd3xx.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(LIBICSNEO_ENABLE_TCP)
|
||||||
|
list(APPEND PLATFORM_SRC
|
||||||
|
platform/tcp.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(LIBICSNEO_BUILD_EXAMPLES)
|
if(LIBICSNEO_BUILD_EXAMPLES)
|
||||||
add_subdirectory(examples)
|
add_subdirectory(examples)
|
||||||
endif()
|
endif()
|
||||||
@@ -168,15 +237,29 @@ endforeach()
|
|||||||
|
|
||||||
set(SRC_FILES
|
set(SRC_FILES
|
||||||
communication/message/flexray/control/flexraycontrolmessage.cpp
|
communication/message/flexray/control/flexraycontrolmessage.cpp
|
||||||
|
communication/message/callback/streamoutput/a2bwavoutput.cpp
|
||||||
|
communication/message/callback/streamoutput/a2bdecoder.cpp
|
||||||
communication/message/neomessage.cpp
|
communication/message/neomessage.cpp
|
||||||
communication/message/ethphymessage.cpp
|
communication/message/ethphymessage.cpp
|
||||||
|
communication/message/linmessage.cpp
|
||||||
|
communication/message/livedatamessage.cpp
|
||||||
communication/packet/flexraypacket.cpp
|
communication/packet/flexraypacket.cpp
|
||||||
communication/packet/canpacket.cpp
|
communication/packet/canpacket.cpp
|
||||||
|
communication/packet/a2bpacket.cpp
|
||||||
communication/packet/ethernetpacket.cpp
|
communication/packet/ethernetpacket.cpp
|
||||||
communication/packet/versionpacket.cpp
|
communication/packet/versionpacket.cpp
|
||||||
communication/packet/iso9141packet.cpp
|
communication/packet/iso9141packet.cpp
|
||||||
communication/packet/ethphyregpacket.cpp
|
communication/packet/ethphyregpacket.cpp
|
||||||
|
communication/packet/livedatapacket.cpp
|
||||||
communication/packet/logicaldiskinfopacket.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/decoder.cpp
|
||||||
communication/encoder.cpp
|
communication/encoder.cpp
|
||||||
communication/ethernetpacketizer.cpp
|
communication/ethernetpacketizer.cpp
|
||||||
@@ -184,6 +267,7 @@ set(SRC_FILES
|
|||||||
communication/multichannelcommunication.cpp
|
communication/multichannelcommunication.cpp
|
||||||
communication/communication.cpp
|
communication/communication.cpp
|
||||||
communication/driver.cpp
|
communication/driver.cpp
|
||||||
|
communication/livedata.cpp
|
||||||
device/extensions/flexray/extension.cpp
|
device/extensions/flexray/extension.cpp
|
||||||
device/extensions/flexray/controller.cpp
|
device/extensions/flexray/controller.cpp
|
||||||
device/idevicesettings.cpp
|
device/idevicesettings.cpp
|
||||||
@@ -273,39 +357,67 @@ endif()
|
|||||||
if(LIBICSNEO_ENABLE_FTDI)
|
if(LIBICSNEO_ENABLE_FTDI)
|
||||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
|
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
|
||||||
endif()
|
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
|
# fatfs
|
||||||
add_subdirectory(third-party/fatfs)
|
add_subdirectory(third-party/fatfs)
|
||||||
|
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||||
target_link_libraries(icsneocpp PRIVATE fatfs)
|
target_link_libraries(icsneocpp PRIVATE fatfs)
|
||||||
|
|
||||||
# libftdi
|
# libftdi
|
||||||
if(NOT WIN32)
|
if(LIBICSNEO_ENABLE_FTDI)
|
||||||
target_include_directories(icsneocpp PUBLIC third-party/libftdi/src)
|
if(NOT WIN32)
|
||||||
set(LIBFTDI_DOCUMENTATION OFF CACHE INTERNAL "")
|
target_include_directories(icsneocpp PUBLIC third-party/libftdi/src)
|
||||||
set(LIBFTDI_BUILD_TESTS OFF CACHE INTERNAL "")
|
set(LIBFTDI_DOCUMENTATION OFF CACHE INTERNAL "")
|
||||||
set(LIBFTDI_INSTALL OFF CACHE INTERNAL "")
|
set(LIBFTDI_BUILD_TESTS OFF CACHE INTERNAL "")
|
||||||
set(LIBFTDI_PYTHON_BINDINGS OFF CACHE INTERNAL "")
|
set(LIBFTDI_INSTALL OFF CACHE INTERNAL "")
|
||||||
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF CACHE INTERNAL "")
|
set(LIBFTDI_PYTHON_BINDINGS OFF CACHE INTERNAL "")
|
||||||
set(FTDIPP OFF CACHE INTERNAL "")
|
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF CACHE INTERNAL "")
|
||||||
set(FTDI_EEPROM OFF CACHE INTERNAL "")
|
set(FTDIPP OFF CACHE INTERNAL "")
|
||||||
add_subdirectory(third-party/libftdi)
|
set(FTDI_EEPROM OFF CACHE INTERNAL "")
|
||||||
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
|
add_subdirectory(third-party/libftdi)
|
||||||
endif(NOT WIN32)
|
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||||
|
|
||||||
# winpcap
|
find_package(Threads)
|
||||||
if(WIN32)
|
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||||
if(LIBICSNEO_NPCAP_INCLUDE_DIR STREQUAL "")
|
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
|
||||||
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
|
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||||
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
|
endif(NOT WIN32)
|
||||||
|
endif(LIBICSNEO_ENABLE_FTDI)
|
||||||
|
|
||||||
|
# pcap
|
||||||
|
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||||
|
if(WIN32)
|
||||||
|
if(LIBICSNEO_NPCAP_INCLUDE_DIR STREQUAL "")
|
||||||
|
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
|
||||||
|
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
|
||||||
|
else()
|
||||||
|
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
|
||||||
|
add_definitions(-DNPCAP -DWIN32_LEAN_AND_MEAN)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
|
find_package(PCAP REQUIRED)
|
||||||
add_definitions(-DNPCAP -DWIN32_LEAN_AND_MEAN)
|
target_include_directories(icsneocpp PUBLIC ${PCAP_INCLUDE_DIR})
|
||||||
endif()
|
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
|
||||||
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(LIBICSNEO_BUILD_ICSNEOC)
|
if(LIBICSNEO_BUILD_ICSNEOC)
|
||||||
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
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_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)
|
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()
|
endif()
|
||||||
|
|
||||||
if(LIBICSNEO_BUILD_ICSNEOC_STATIC)
|
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)
|
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()
|
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
|
# googletest
|
||||||
if(LIBICSNEO_BUILD_TESTS)
|
if(LIBICSNEO_BUILD_TESTS)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
@@ -382,6 +488,11 @@ if(LIBICSNEO_BUILD_TESTS)
|
|||||||
test/diskdriverwritetest.cpp
|
test/diskdriverwritetest.cpp
|
||||||
test/eventmanagertest.cpp
|
test/eventmanagertest.cpp
|
||||||
test/ethernetpacketizertest.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)
|
target_link_libraries(libicsneo-tests gtest gtest_main)
|
||||||
|
|||||||
+12
-1
@@ -14,6 +14,13 @@
|
|||||||
- RADStar 2
|
- RADStar 2
|
||||||
- CAN works
|
- CAN works
|
||||||
- Ethernet works
|
- Ethernet works
|
||||||
|
- RADA2B
|
||||||
|
- CAN works
|
||||||
|
- Ethernet works
|
||||||
|
- RADComet
|
||||||
|
- CAN works
|
||||||
|
- CAN FD works
|
||||||
|
- Ethernet works
|
||||||
|
|
||||||
- Connecting over USB
|
- Connecting over USB
|
||||||
- ValueCAN 4 series
|
- ValueCAN 4 series
|
||||||
@@ -36,4 +43,8 @@
|
|||||||
- neoVI PLASMA
|
- neoVI PLASMA
|
||||||
- CAN works
|
- CAN works
|
||||||
- neoVI ION
|
- neoVI ION
|
||||||
- CAN works
|
- 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:
|
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);
|
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
|
## 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
|
### 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
|
### 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`.
|
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
|
### Linux
|
||||||
The dependencies are as follows
|
#### General Dependencies
|
||||||
- CMake 3.12 or above
|
- CMake 3.12 or above
|
||||||
- GCC 4.7 or above, 4.8+ recommended
|
- GCC 7 or above
|
||||||
- `libusb-1.0-0-dev`
|
- `libusb-1.0-0-dev`
|
||||||
- `libpcap0.8-dev`
|
- `libpcap0.8-dev`
|
||||||
- `build-essential` is recommended
|
- `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
|
||||||
```
|
```
|
||||||
|
|||||||
+75
-6
@@ -111,11 +111,15 @@ bool icsneo_isValidNeoDevice(const neodevice_t* device) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool icsneo_openDevice(const neodevice_t* device) {
|
bool icsneo_openDevice(const neodevice_t* device) {
|
||||||
if(!icsneo_isValidNeoDevice(device))
|
if(!icsneo_isValidNeoDevice(device)) {
|
||||||
|
//todo error
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!device->device->open())
|
if(!device->device->open()) {
|
||||||
return false;
|
//todo error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// We connected successfully, move the device to connected devices
|
// We connected successfully, move the device to connected devices
|
||||||
std::vector<std::vector<std::shared_ptr<Device>>::iterator> itemsToMove;
|
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;
|
return false;
|
||||||
|
|
||||||
*items = storage.size();
|
*items = storage.size();
|
||||||
|
|
||||||
for(size_t i = 0; i < *items; i++) {
|
for(size_t i = 0; i < *items; i++) {
|
||||||
// For each message, copy into neomessage_t buffer given
|
// For each message, copy into neomessage_t buffer given
|
||||||
messages[i] = CreateNeoMessage(storage[i]);
|
messages[i] = CreateNeoMessage(storage[i]);
|
||||||
@@ -251,7 +254,7 @@ int icsneo_addMessageCallback(const neodevice_t* device, void (*callback)(neomes
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return device->device->addMessageCallback(
|
return device->device->addMessageCallback(
|
||||||
MessageCallback(
|
std::make_shared<MessageCallback>(
|
||||||
[=](std::shared_ptr<icsneo::Message> msg) {
|
[=](std::shared_ptr<icsneo::Message> msg) {
|
||||||
return callback(CreateNeoMessage(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))
|
if(!icsneo_isValidNeoDevice(device))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if(!str) {
|
||||||
|
*maxLength = device->device->describe().length();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::string output = device->device->describe();
|
std::string output = device->device->describe();
|
||||||
|
|
||||||
*maxLength = output.copy(str, *maxLength);
|
*maxLength = output.copy(str, *maxLength);
|
||||||
@@ -642,7 +650,7 @@ bool icsneo_getDigitalIO(const neodevice_t* device, neoio_t type, uint32_t numbe
|
|||||||
return false;
|
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())
|
if(!val.has_value())
|
||||||
return false;
|
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);
|
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 "FileDescription", "Intrepid Control Systems Open Device Communication C API"
|
||||||
VALUE "FileVersion", VER_FILEVERSION_STR
|
VALUE "FileVersion", VER_FILEVERSION_STR
|
||||||
VALUE "InternalName", "icsneoc.dll"
|
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 "OriginalFilename", "icsneoc.dll"
|
||||||
VALUE "ProductName", "libicsneo"
|
VALUE "ProductName", "libicsneo"
|
||||||
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
||||||
|
|||||||
+140
-2
@@ -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* 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* VALUE_NOT_YET_PRESENT = "The value is not yet present.";
|
||||||
static constexpr const char* TIMEOUT = "The timeout was reached.";
|
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
|
// Device Errors
|
||||||
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
|
static constexpr const char* 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* 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_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* 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
|
// Transport Errors
|
||||||
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
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_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* 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* 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* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
|
||||||
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
|
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
|
||||||
@@ -158,6 +205,8 @@ const char* APIEvent::DescriptionForType(Type type) {
|
|||||||
return VALUE_NOT_YET_PRESENT;
|
return VALUE_NOT_YET_PRESENT;
|
||||||
case Type::Timeout:
|
case Type::Timeout:
|
||||||
return TIMEOUT;
|
return TIMEOUT;
|
||||||
|
case Type::WiVINotSupported:
|
||||||
|
return WIVI_NOT_SUPPORTED;
|
||||||
|
|
||||||
// Device Errors
|
// Device Errors
|
||||||
case Type::PollingMessageOverflow:
|
case Type::PollingMessageOverflow:
|
||||||
@@ -232,7 +281,18 @@ const char* APIEvent::DescriptionForType(Type type) {
|
|||||||
return ATOMIC_OPERATION_RETRIED;
|
return ATOMIC_OPERATION_RETRIED;
|
||||||
case Type::AtomicOperationCompletedNonatomically:
|
case Type::AtomicOperationCompletedNonatomically:
|
||||||
return ATOMIC_OPERATION_COMPLETED_NONATOMICALLY;
|
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
|
// Transport Errors
|
||||||
case Type::FailedToRead:
|
case Type::FailedToRead:
|
||||||
return FAILED_TO_READ;
|
return FAILED_TO_READ;
|
||||||
@@ -254,7 +314,85 @@ const char* APIEvent::DescriptionForType(Type type) {
|
|||||||
return PCAP_COULD_NOT_FIND_DEVICES;
|
return PCAP_COULD_NOT_FIND_DEVICES;
|
||||||
case Type::PacketDecodingError:
|
case Type::PacketDecodingError:
|
||||||
return PACKET_DECODING;
|
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
|
// Other Errors
|
||||||
case Type::TooManyEvents:
|
case Type::TooManyEvents:
|
||||||
return TOO_MANY_EVENTS;
|
return TOO_MANY_EVENTS;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "icsneo/api/eventmanager.h"
|
#include "icsneo/api/eventmanager.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
@@ -33,6 +36,25 @@ void EventManager::cancelErrorDowngradingOnCurrentThread() {
|
|||||||
void EventManager::add(APIEvent event) {
|
void EventManager::add(APIEvent event) {
|
||||||
if(destructing)
|
if(destructing)
|
||||||
return;
|
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(event.getSeverity() == APIEvent::Severity::Error) {
|
||||||
// if the error was added on a thread that downgrades errors (non-user thread)
|
// if the error was added on a thread that downgrades errors (non-user thread)
|
||||||
std::lock_guard<std::mutex> lk(downgradedThreadsMutex);
|
std::lock_guard<std::mutex> lk(downgradedThreadsMutex);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "icsneo/icsneocpp.h"
|
#include "icsneo/icsneocpp.h"
|
||||||
#include "icsneo/device/devicefinder.h"
|
#include "icsneo/device/devicefinder.h"
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
@@ -45,4 +46,4 @@ void icsneo::SetEventLimit(size_t newLimit) {
|
|||||||
|
|
||||||
size_t icsneo::GetEventLimit() {
|
size_t icsneo::GetEventLimit() {
|
||||||
return EventManager::GetInstance().getEventLimit();
|
return EventManager::GetInstance().getEventLimit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||||
|
#pragma warning(disable : 4996) // STL time functions
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
@@ -73,6 +74,25 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const neomessage_frame_t& frame = *reinterpret_cast<const neomessage_frame_t*>(&newmsg);
|
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))
|
switch (Network::Type(frame.type))
|
||||||
{
|
{
|
||||||
case Network::Type::CAN:
|
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.Protocol = frame.status.canfdFDF ? SPY_PROTOCOL_CANFD : SPY_PROTOCOL_CAN;
|
||||||
oldmsg.NumberBytesData = static_cast<uint8_t>(std::min(frame.length, (size_t)255));
|
oldmsg.NumberBytesData = static_cast<uint8_t>(std::min(frame.length, (size_t)255));
|
||||||
oldmsg.NumberBytesHeader = 4;
|
oldmsg.NumberBytesHeader = 4;
|
||||||
|
copyFrameData();
|
||||||
break;
|
break;
|
||||||
case Network::Type::Ethernet:
|
case Network::Type::Ethernet:
|
||||||
oldmsg.Protocol = SPY_PROTOCOL_ETHERNET;
|
oldmsg.Protocol = SPY_PROTOCOL_ETHERNET;
|
||||||
oldmsg.NumberBytesData = static_cast<uint8_t>(frame.length & 0xFF);
|
oldmsg.NumberBytesData = static_cast<uint8_t>(frame.length & 0xFF);
|
||||||
oldmsg.NumberBytesHeader = static_cast<uint8_t>(frame.length >> 8);
|
oldmsg.NumberBytesHeader = static_cast<uint8_t>(frame.length >> 8);
|
||||||
|
copyFrameData();
|
||||||
break;
|
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:
|
default:
|
||||||
return false;
|
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
|
// Timestamp - epoch = 1/1/2007 - 25ns per tick most of the time
|
||||||
uint64_t t = frame.timestamp;
|
uint64_t t = frame.timestamp;
|
||||||
uint16_t res = 0;
|
uint16_t res = 0;
|
||||||
@@ -127,6 +161,90 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
|
|||||||
return true;
|
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)
|
static inline bool Within(size_t value, size_t min, size_t max)
|
||||||
{
|
{
|
||||||
return ((min <= value) && (value < max));
|
return ((min <= value) && (value < max));
|
||||||
@@ -396,25 +514,9 @@ int LegacyDLLExport icsneoTxMessagesEx(void* hObject, icsSpyMessage* pMsg, unsig
|
|||||||
*NumTxed = 0;
|
*NumTxed = 0;
|
||||||
for (unsigned int i = 0; i < lNumMessages; i++)
|
for (unsigned int i = 0; i < lNumMessages; i++)
|
||||||
{
|
{
|
||||||
const icsSpyMessage& oldmsg = pMsg[i];
|
|
||||||
newmsg = {};
|
newmsg = {};
|
||||||
newmsg.netid = (uint16_t)lNetworkID;
|
const icsSpyMessage& oldMsg = pMsg[i];
|
||||||
newmsg.description = oldmsg.DescriptionID;
|
SpyMessageToNeoMessage(oldMsg, newmsg, lNetworkID);
|
||||||
memcpy(newmsg.header, &oldmsg.ArbIDOrHeader, sizeof(newmsg.header));
|
|
||||||
if (oldmsg.Protocol != SPY_PROTOCOL_ETHERNET)
|
|
||||||
newmsg.length = oldmsg.NumberBytesData;
|
|
||||||
else
|
|
||||||
newmsg.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
|
|
||||||
if (oldmsg.ExtraDataPtr != nullptr && oldmsg.ExtraDataPtrEnabled == 1)
|
|
||||||
newmsg.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
|
|
||||||
else
|
|
||||||
newmsg.data = oldmsg.Data;
|
|
||||||
newmsg.status.statusBitfield[0] = oldmsg.StatusBitField;
|
|
||||||
newmsg.status.statusBitfield[1] = oldmsg.StatusBitField2;
|
|
||||||
newmsg.status.statusBitfield[2] = oldmsg.StatusBitField3;
|
|
||||||
newmsg.status.statusBitfield[3] = oldmsg.StatusBitField4;
|
|
||||||
if (oldmsg.Protocol == SPY_PROTOCOL_CANFD)
|
|
||||||
newmsg.status.canfdFDF = true;
|
|
||||||
if (icsneo_transmit(device, reinterpret_cast<neomessage_t*>(&newmsg)))
|
if (icsneo_transmit(device, reinterpret_cast<neomessage_t*>(&newmsg)))
|
||||||
(*NumTxed)++;
|
(*NumTxed)++;
|
||||||
}
|
}
|
||||||
@@ -464,6 +566,54 @@ void LegacyDLLExport icsneoSetISO15765RxParameters(void* hObject, int lNetwork,
|
|||||||
return;
|
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
|
//Device Functions
|
||||||
int LegacyDLLExport icsneoGetConfiguration(void* hObject, unsigned char* pData, int* lNumBytes)
|
int LegacyDLLExport icsneoGetConfiguration(void* hObject, unsigned char* pData, int* lNumBytes)
|
||||||
{
|
{
|
||||||
@@ -638,8 +788,13 @@ int LegacyDLLExport icsneoSetDeviceParameters(void* hObject, char* pParmValue, i
|
|||||||
// Error Functions
|
// Error Functions
|
||||||
int LegacyDLLExport icsneoGetLastAPIError(void* hObject, unsigned long* pErrorNumber)
|
int LegacyDLLExport icsneoGetLastAPIError(void* hObject, unsigned long* pErrorNumber)
|
||||||
{
|
{
|
||||||
// TODO Implement
|
if(!icsneoValidateHObject(hObject))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
neoevent_t error;
|
||||||
|
icsneo_getLastError(&error);
|
||||||
|
*pErrorNumber = (unsigned long)error.eventNumber;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int* pNumberOfErrors)
|
int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int* pNumberOfErrors)
|
||||||
@@ -648,11 +803,43 @@ int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int*
|
|||||||
return false;
|
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)
|
int* lMaxLengthShort, int* lMaxLengthLong, int* lErrorSeverity, int* lRestartNeeded)
|
||||||
{
|
{
|
||||||
// TODO Implement
|
if (szErrorDescriptionShort == nullptr || szErrorDescriptionLong == nullptr
|
||||||
return false;
|
|| 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
|
//ISO15765-2 Functions
|
||||||
@@ -731,6 +918,18 @@ int LegacyDLLExport icsneoStopSockServer(void* hObject)
|
|||||||
return false;
|
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
|
//CoreMini Script functions
|
||||||
int LegacyDLLExport icsneoScriptStart(void* hObject, int iLocation)
|
int LegacyDLLExport icsneoScriptStart(void* hObject, int iLocation)
|
||||||
{
|
{
|
||||||
@@ -910,6 +1109,12 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
|
|||||||
case NEODEVICE_RADGIGALOG:
|
case NEODEVICE_RADGIGALOG:
|
||||||
*pDeviceSettingsType = DeviceRADGigalogSettingsType;
|
*pDeviceSettingsType = DeviceRADGigalogSettingsType;
|
||||||
break;
|
break;
|
||||||
|
case NEODEVICE_RADMOON3:
|
||||||
|
*pDeviceSettingsType = DeviceRADMoon3SettingsType;
|
||||||
|
break;
|
||||||
|
case NEODEVICE_RED2:
|
||||||
|
*pDeviceSettingsType = DeviceRed2SettingsType;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -947,6 +1152,15 @@ int LegacyDLLExport icsneoGetDeviceSettings(void* hObject, SDeviceSettings* pSet
|
|||||||
return !!icsneo_settingsReadStructure(device, &pSettings->Settings, iNumBytes - offset);
|
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)
|
int LegacyDLLExport icsneoSetBitRateEx(void* hObject, unsigned long BitRate, int NetworkID, int iOptions)
|
||||||
{
|
{
|
||||||
if (!icsneoValidateHObject(hObject))
|
if (!icsneoValidateHObject(hObject))
|
||||||
@@ -1287,7 +1501,7 @@ int LegacyDLLExport icsneoSerialNumberFromString(unsigned long* serial, char* da
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NETWORK_ADAPTER_INFO* aInfo)
|
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NDIS_ADAPTER_INFORMATION* aInfo)
|
||||||
{
|
{
|
||||||
return false;
|
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
|
cd build
|
||||||
set CFLAGS=/WX
|
set CFLAGS=/WX
|
||||||
set CXXFLAGS=/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%
|
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||||
cmake --build .
|
cmake --build .
|
||||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
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/filter/main51messagefilter.h"
|
||||||
#include "icsneo/communication/message/readsettingsmessage.h"
|
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||||
#include "icsneo/communication/message/versionmessage.h"
|
#include "icsneo/communication/message/versionmessage.h"
|
||||||
|
#include "icsneo/communication/message/componentversionsmessage.h"
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
@@ -21,7 +22,8 @@ int Communication::messageCallbackIDCounter = 1;
|
|||||||
Communication::~Communication() {
|
Communication::~Communication() {
|
||||||
if(redirectingRead)
|
if(redirectingRead)
|
||||||
clearRedirectRead();
|
clearRedirectRead();
|
||||||
close();
|
if(isOpen())
|
||||||
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Communication::open() {
|
bool Communication::open() {
|
||||||
@@ -150,22 +152,22 @@ std::shared_ptr<SerialNumberMessage> Communication::getSerialNumberSync(std::chr
|
|||||||
return std::dynamic_pointer_cast<SerialNumberMessage>(m51);
|
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);
|
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]() {
|
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
||||||
return sendCommand(Command::GetMainVersion);
|
return sendCommand(Command::GetMainVersion);
|
||||||
}, filter, timeout);
|
}, filter, timeout);
|
||||||
if(!msg) // Did not receive a message
|
if(!msg) // Did not receive a message
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
auto ver = std::dynamic_pointer_cast<VersionMessage>(msg);
|
auto ver = std::dynamic_pointer_cast<VersionMessage>(msg);
|
||||||
if(!ver) // Could not upcast for some reason
|
if(!ver) // Could not upcast for some reason
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(ver->ForChip != VersionMessage::MainChip || ver->Versions.size() != 1)
|
if(ver->ForChip != VersionMessage::MainChip || ver->Versions.size() != 1)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
ret.push_back(ver->Versions.front());
|
ret.push_back(ver->Versions.front());
|
||||||
|
|
||||||
@@ -193,7 +195,7 @@ std::shared_ptr<LogicalDiskInfoMessage> Communication::getLogicalDiskInfoSync(st
|
|||||||
return std::dynamic_pointer_cast<LogicalDiskInfoMessage>(msg);
|
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);
|
std::lock_guard<std::mutex> lk(messageCallbacksLock);
|
||||||
messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb));
|
messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb));
|
||||||
return messageCallbackIDCounter++;
|
return messageCallbackIDCounter++;
|
||||||
@@ -212,14 +214,15 @@ bool Communication::removeMessageCallback(int id) {
|
|||||||
|
|
||||||
std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(void)> onceWaitingDo,
|
std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(void)> onceWaitingDo,
|
||||||
const std::shared_ptr<MessageFilter>& f, std::chrono::milliseconds timeout) {
|
const std::shared_ptr<MessageFilter>& f, std::chrono::milliseconds timeout) {
|
||||||
std::mutex m;
|
std::mutex cvMutex;
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
std::shared_ptr<Message> returnedMessage;
|
std::shared_ptr<Message> returnedMessage;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lk(m); // Don't let the callback fire until we're waiting for it
|
std::unique_lock<std::mutex> fnLk(syncMessageMutex); // Only allow for one sync message at a time
|
||||||
int cb = addMessageCallback(MessageCallback([&m, &returnedMessage, &cv](std::shared_ptr<Message> message) {
|
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;
|
returnedMessage = message;
|
||||||
}
|
}
|
||||||
cv.notify_all();
|
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
|
// We have now added the callback, do whatever the caller wanted to do
|
||||||
bool fail = !onceWaitingDo();
|
bool fail = !onceWaitingDo();
|
||||||
if(!fail)
|
if(!fail)
|
||||||
cv.wait_for(lk, timeout, [&returnedMessage] { return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value
|
cv.wait_for(cvLk, 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
|
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)
|
// 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);
|
removeMessageCallback(cb);
|
||||||
@@ -251,7 +254,7 @@ void Communication::dispatchMessage(const std::shared_ptr<Message>& msg) {
|
|||||||
EventManager::GetInstance().cancelErrorDowngradingOnCurrentThread();
|
EventManager::GetInstance().cancelErrorDowngradingOnCurrentThread();
|
||||||
for(auto& cb : messageCallbacks) {
|
for(auto& cb : messageCallbacks) {
|
||||||
if(!closing) { // We might have closed while reading or processing
|
if(!closing) { // We might have closed while reading or processing
|
||||||
cb.second.callIfMatch(msg);
|
cb.second->callIfMatch(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(downgrade)
|
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));
|
||||||
|
}
|
||||||
|
|||||||
+142
-1
@@ -5,16 +5,36 @@
|
|||||||
#include "icsneo/communication/message/readsettingsmessage.h"
|
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||||
#include "icsneo/communication/message/neoreadmemorysdmessage.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/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/communication/command.h"
|
||||||
#include "icsneo/device/device.h"
|
#include "icsneo/device/device.h"
|
||||||
#include "icsneo/communication/packet/canpacket.h"
|
#include "icsneo/communication/packet/canpacket.h"
|
||||||
|
#include "icsneo/communication/packet/a2bpacket.h"
|
||||||
#include "icsneo/communication/packet/ethernetpacket.h"
|
#include "icsneo/communication/packet/ethernetpacket.h"
|
||||||
#include "icsneo/communication/packet/flexraypacket.h"
|
#include "icsneo/communication/packet/flexraypacket.h"
|
||||||
#include "icsneo/communication/packet/iso9141packet.h"
|
#include "icsneo/communication/packet/iso9141packet.h"
|
||||||
#include "icsneo/communication/packet/versionpacket.h"
|
#include "icsneo/communication/packet/versionpacket.h"
|
||||||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||||
#include "icsneo/communication/packet/logicaldiskinfopacket.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>
|
#include <iostream>
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
@@ -115,6 +135,56 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
|||||||
iso.network = packet->network;
|
iso.network = packet->network;
|
||||||
return true;
|
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: {
|
case Network::Type::Internal: {
|
||||||
switch(packet->network.getNetID()) {
|
switch(packet->network.getNetID()) {
|
||||||
case Network::NetID::Reset_Status: {
|
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());
|
msg->data.insert(msg->data.end(), packet->data.begin() + 4, packet->data.end());
|
||||||
return true;
|
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: {
|
case Network::NetID::FlexRayControl: {
|
||||||
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
|
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
|
||||||
if(!frResult->decoded) {
|
if(!frResult->decoded) {
|
||||||
@@ -286,6 +406,14 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
|||||||
}
|
}
|
||||||
return true;
|
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: {
|
case Network::NetID::EthPHYControl: {
|
||||||
result = HardwareEthernetPhyRegisterPacket::DecodeToMessage(packet->data, report);
|
result = HardwareEthernetPhyRegisterPacket::DecodeToMessage(packet->data, report);
|
||||||
if(!result) {
|
if(!result) {
|
||||||
@@ -294,13 +422,26 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
|||||||
}
|
}
|
||||||
return true;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the moment other types of messages will automatically be decoded as raw messages
|
// For the moment other types of messages will automatically be decoded as raw messages
|
||||||
result = std::make_shared<RawMessage>(packet->network, packet->data);
|
result = std::make_shared<RawMessage>(packet->network, packet->data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
#include "icsneo/communication/encoder.h"
|
#include "icsneo/communication/encoder.h"
|
||||||
#include "icsneo/communication/message/ethernetmessage.h"
|
#include "icsneo/communication/message/ethernetmessage.h"
|
||||||
|
#include "icsneo/communication/message/livedatamessage.h"
|
||||||
#include "icsneo/communication/message/main51message.h"
|
#include "icsneo/communication/message/main51message.h"
|
||||||
|
#include "icsneo/communication/packet/livedatapacket.h"
|
||||||
#include "icsneo/communication/packet/ethernetpacket.h"
|
#include "icsneo/communication/packet/ethernetpacket.h"
|
||||||
#include "icsneo/communication/packet/iso9141packet.h"
|
#include "icsneo/communication/packet/iso9141packet.h"
|
||||||
#include "icsneo/communication/packet/canpacket.h"
|
#include "icsneo/communication/packet/canpacket.h"
|
||||||
#include "icsneo/communication/packet/ethphyregpacket.h"
|
#include "icsneo/communication/packet/ethphyregpacket.h"
|
||||||
#include "icsneo/communication/message/ethphymessage.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;
|
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`
|
// packets to the device. This function just encodes them back to back into `result`
|
||||||
return HardwareISO9141Packet::EncodeFromMessage(*isomsg, result, report, packetizer);
|
return HardwareISO9141Packet::EncodeFromMessage(*isomsg, result, report, packetizer);
|
||||||
} // End of Network::Type::ISO9141
|
} // 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:
|
default:
|
||||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
return false;
|
return false;
|
||||||
@@ -144,6 +199,17 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
|||||||
return false;
|
return false;
|
||||||
break;
|
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;
|
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
|
// 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
|
// 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
|
// 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(), {
|
buffer->insert(buffer->begin(), {
|
||||||
(uint8_t)Network::NetID::RED, // 0x0C for long message
|
(uint8_t)Network::NetID::RED, // 0x0C for long message
|
||||||
(uint8_t)size, // Size, little endian 16-bit
|
(uint8_t)size, // Size, little endian 16-bit
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using namespace icsneo;
|
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/canmessage.h"
|
||||||
#include "icsneo/communication/message/ethernetmessage.h"
|
#include "icsneo/communication/message/ethernetmessage.h"
|
||||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||||
|
#include "icsneo/communication/message/linmessage.h"
|
||||||
|
|
||||||
using namespace icsneo;
|
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;
|
neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg;
|
||||||
auto framemsg = std::static_pointer_cast<Frame>(message);
|
auto framemsg = std::static_pointer_cast<Frame>(message);
|
||||||
const auto netType = framemsg->network.getType();
|
const auto netType = framemsg->network.getType();
|
||||||
|
|
||||||
frame.netid = (neonetid_t)framemsg->network.getNetID();
|
frame.netid = (neonetid_t)framemsg->network.getNetID();
|
||||||
frame.type = (neonettype_t)netType;
|
frame.type = (neonettype_t)netType;
|
||||||
frame.description = framemsg->description;
|
frame.description = framemsg->description;
|
||||||
@@ -53,6 +55,47 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
|
|||||||
//eth.status.xyz = ethmsg->noPadding;
|
//eth.status.xyz = ethmsg->noPadding;
|
||||||
break;
|
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:
|
default:
|
||||||
// TODO Implement others
|
// TODO Implement others
|
||||||
break;
|
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);
|
ethmsg->data.insert(ethmsg->data.end(), eth.data, eth.data + eth.length);
|
||||||
return ethmsg;
|
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;
|
default: break;
|
||||||
}
|
}
|
||||||
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/packet/canpacket.h"
|
||||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||||
#include "icsneo/platform/optional.h"
|
|
||||||
|
|
||||||
using namespace icsneo;
|
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)
|
if (length <= 8)
|
||||||
return length;
|
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)
|
if (dataLength <= 8)
|
||||||
return uint8_t(dataLength);
|
return uint8_t(dataLength);
|
||||||
@@ -52,7 +51,7 @@ static optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
|
|||||||
return uint8_t(0xF);
|
return uint8_t(0xF);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
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->isCANFD = true;
|
||||||
msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch
|
msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch
|
||||||
msg->errorStateIndicator = data->header.ESI;
|
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)
|
if (lenFromDLC)
|
||||||
length = *lenFromDLC;
|
length = *lenFromDLC;
|
||||||
} else if(length > 8) { // This is a standard CAN frame with a length of more than 8
|
} 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();
|
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()) {
|
if (!dlc.has_value()) {
|
||||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||||
return false; // Too much data for the protocol
|
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);
|
auto msg = std::make_shared<VersionMessage>(VersionMessage::MainChip);
|
||||||
|
|
||||||
msg->Versions.emplace_back();
|
msg->Versions.emplace_back();
|
||||||
optional<DeviceAppVersion>& version = msg->Versions.back();
|
std::optional<DeviceAppVersion>& version = msg->Versions.back();
|
||||||
version.emplace();
|
version.emplace();
|
||||||
version->major = bytestream[1];
|
version->major = bytestream[1];
|
||||||
version->minor = bytestream[2];
|
version->minor = bytestream[2];
|
||||||
@@ -26,7 +26,7 @@ std::shared_ptr<VersionMessage> HardwareVersionPacket::DecodeSecondaryToMessage(
|
|||||||
while(bytesLeft >= 3) {
|
while(bytesLeft >= 3) {
|
||||||
const bool versionValid = bytestream[bytestream.size() - bytesLeft + 0];
|
const bool versionValid = bytestream[bytestream.size() - bytesLeft + 0];
|
||||||
msg->Versions.emplace_back();
|
msg->Versions.emplace_back();
|
||||||
optional<DeviceAppVersion>& version = msg->Versions.back();
|
std::optional<DeviceAppVersion>& version = msg->Versions.back();
|
||||||
if(versionValid) {
|
if(versionValid) {
|
||||||
version.emplace();
|
version.emplace();
|
||||||
version->major = bytestream[bytestream.size() - bytesLeft + 1];
|
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 "icsneo/communication/packetizer.h"
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
using namespace icsneo;
|
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 Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||||
bool haveEnoughData = true;
|
bool haveEnoughData = true;
|
||||||
bytes.insert(bytes.end(), inputBytes.begin(), inputBytes.end());
|
|
||||||
|
bytes.Copy(inputBytes);
|
||||||
|
|
||||||
while(haveEnoughData) {
|
while(haveEnoughData) {
|
||||||
switch(state) {
|
switch(state) {
|
||||||
@@ -49,44 +49,97 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
|||||||
haveEnoughData = false;
|
haveEnoughData = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packetLength = bytes[1] >> 4 & 0xF;
|
||||||
|
packet.network = Network(bytes[1] & 0xF); // Lower nibble of the second byte is the network ID
|
||||||
|
|
||||||
packetLength = bytes[1] >> 4 & 0xf; // Upper nibble of the second byte denotes the packet length
|
if(packetLength == 0) {
|
||||||
packet.network = Network(bytes[1] & 0xf); // Lower nibble of the second byte is the network ID
|
|
||||||
if(packetLength == 0) { // A length of zero denotes a long style packet
|
|
||||||
state = ReadState::ParseLongStylePacketHeader;
|
state = ReadState::ParseLongStylePacketHeader;
|
||||||
checksum = false;
|
break;
|
||||||
headerSize = 6;
|
} else if(packetLength == 0xA && packet.network == Network::NetID::DiskData) {
|
||||||
} else {
|
state = ReadState::ParseDiskDataHeader;
|
||||||
state = ReadState::GetData;
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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++;
|
currentIndex++;
|
||||||
|
state = ReadState::GetData;
|
||||||
break;
|
break;
|
||||||
case ReadState::ParseLongStylePacketHeader:
|
case ReadState::ParseLongStylePacketHeader:
|
||||||
if(bytes.size() < 6) {
|
if(bytes.size() < 6) {
|
||||||
haveEnoughData = false;
|
haveEnoughData = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
packetLength = bytes[2]; // Long packets have a little endian length on bytes 3 and 4
|
packetLength = bytes[2]; // Long packets have a little endian length on bytes 3 and 4
|
||||||
packetLength |= bytes[3] << 8;
|
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
|
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.
|
||||||
currentIndex += 4;
|
|
||||||
|
|
||||||
/* Long packets can't have a length less than 6, because that would indicate a negative payload size.
|
/* 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
|
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
|
||||||
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
|
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
|
||||||
* payload, and not the header or checksum.
|
* payload, and not the header or checksum.
|
||||||
*/
|
*/
|
||||||
if(packetLength < 6 || packetLength > 4000) {
|
if(packetLength < 6 || packetLength > 4000) {
|
||||||
bytes.pop_front();
|
bytes.pop_front();
|
||||||
EventManager::GetInstance().add(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
EventManager::GetInstance().add(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||||
state = ReadState::SearchForHeader;
|
state = ReadState::SearchForHeader;
|
||||||
} else {
|
break;
|
||||||
state = ReadState::GetData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checksum = false;
|
||||||
|
headerSize = 6;
|
||||||
|
currentIndex += 5;
|
||||||
|
state = ReadState::GetData;
|
||||||
|
break;
|
||||||
|
case ReadState::ParseDiskDataHeader:
|
||||||
|
if(bytes.size() < 3) {
|
||||||
|
haveEnoughData = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytes[2] != 0xAA) {
|
||||||
|
bytes.pop_front();
|
||||||
|
state = ReadState::SearchForHeader;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytes.size() < 4) {
|
||||||
|
haveEnoughData = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytes[3] != 0x55) {
|
||||||
|
bytes.pop_front();
|
||||||
|
state = ReadState::SearchForHeader;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytes.size() < 5) {
|
||||||
|
haveEnoughData = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytes[4] != 0x55) {
|
||||||
|
bytes.pop_front();
|
||||||
|
state = ReadState::SearchForHeader;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bytes.size() < 7) {
|
||||||
|
haveEnoughData = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
packetLength = bytes[5] | (bytes[6] << 8);
|
||||||
|
|
||||||
|
checksum = false;
|
||||||
|
headerSize = 7;
|
||||||
|
packetLength += headerSize;
|
||||||
|
currentIndex += 6;
|
||||||
|
state = ReadState::GetData;
|
||||||
break;
|
break;
|
||||||
case ReadState::GetData:
|
case ReadState::GetData:
|
||||||
// We do not include the checksum in packetLength so it doesn't get copied into the payload buffer
|
// We do not include the checksum in packetLength so it doesn't get copied into the payload buffer
|
||||||
@@ -98,23 +151,25 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
|||||||
packet.data.clear();
|
packet.data.clear();
|
||||||
if(packetLength > 0)
|
if(packetLength > 0)
|
||||||
packet.data.resize(packetLength - headerSize);
|
packet.data.resize(packetLength - headerSize);
|
||||||
|
|
||||||
auto i = 0;
|
bytes.CopyTo(packet.data.data(), currentIndex, (packetLength - currentIndex));
|
||||||
while(currentIndex < packetLength)
|
currentIndex = packetLength;
|
||||||
packet.data[i++] = bytes[currentIndex++];
|
|
||||||
|
|
||||||
if(disableChecksum || !checksum || bytes[currentIndex] == ICSChecksum(packet.data)) {
|
if(disableChecksum || !checksum || bytes[currentIndex] == ICSChecksum(packet.data)) {
|
||||||
// Got a good packet
|
// Got a good packet
|
||||||
gotGoodPackets = true;
|
gotGoodPackets = true;
|
||||||
processedPackets.push_back(std::make_shared<Packet>(packet));
|
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();
|
bytes.pop_front();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if(gotGoodPackets) // Don't complain unless we've already gotten a good packet, in case we started in the middle of a stream
|
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);
|
report(APIEvent::Type::PacketChecksumError, APIEvent::Severity::Error);
|
||||||
bytes.pop_front(); // Drop the first byte so it doesn't get picked up again
|
bytes.pop_front(); // Drop the first byte so it doesn't get picked up again
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset for the next packet
|
// Reset for the next packet
|
||||||
currentIndex = 0;
|
currentIndex = 0;
|
||||||
state = ReadState::SearchForHeader;
|
state = ReadState::SearchForHeader;
|
||||||
|
|||||||
+1128
-59
File diff suppressed because it is too large
Load Diff
+145
-35
@@ -15,10 +15,22 @@
|
|||||||
#include "icsneo/platform/cdcacm.h"
|
#include "icsneo/platform/cdcacm.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||||
|
#include "icsneo/platform/android/androidusb.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ICSNEO_ENABLE_FTDI
|
#ifdef ICSNEO_ENABLE_FTDI
|
||||||
#include "icsneo/platform/ftdi.h"
|
#include "icsneo/platform/ftdi.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ICSNEO_ENABLE_FTD3XX
|
||||||
|
#include "icsneo/platform/ftd3xx.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ICSNEO_ENABLE_TCP
|
||||||
|
#include "icsneo/platform/tcp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
template<typename T>
|
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() {
|
std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||||
static std::vector<FoundDevice> driverFoundDevices;
|
static std::vector<FoundDevice> newDriverFoundDevices;
|
||||||
driverFoundDevices.clear();
|
newDriverFoundDevices.clear();
|
||||||
|
|
||||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||||
FirmIO::Find(driverFoundDevices);
|
FirmIO::Find(newDriverFoundDevices);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ICSNEO_ENABLE_TCP
|
||||||
|
TCP::Find(newDriverFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
|
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
|
||||||
PCAP::Find(driverFoundDevices);
|
PCAP::Find(newDriverFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ICSNEO_ENABLE_CDCACM
|
#ifdef ICSNEO_ENABLE_CDCACM
|
||||||
CDCACM::Find(driverFoundDevices);
|
CDCACM::Find(newDriverFoundDevices);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ICSNEO_ENABLE_ANDROIDUSB
|
||||||
|
ANDROIDUSB::Find(newDriverFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ICSNEO_ENABLE_FTDI
|
#ifdef ICSNEO_ENABLE_FTDI
|
||||||
FTDI::Find(driverFoundDevices);
|
FTDI::Find(newDriverFoundDevices);
|
||||||
#endif
|
#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
|
// Offer found devices to each of the subclasses
|
||||||
for (const FoundDevice& dev : driverFoundDevices) {
|
for (const FoundDevice& dev : newDriverFoundDevices) {
|
||||||
#ifdef __ETHERBADGE_H_
|
#ifdef __ETHERBADGE_H_
|
||||||
makeIfSerialMatches<EtherBADGE>(dev, foundDevices);
|
makeIfSerialMatches<EtherBADGE>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOOBD2PRO_H_
|
#ifdef __NEOOBD2PRO_H_
|
||||||
makeIfSerialMatches<NeoOBD2PRO>(dev, foundDevices);
|
makeIfSerialMatches<NeoOBD2PRO>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOOBD2SIM_H_
|
#ifdef __NEOOBD2SIM_H_
|
||||||
makeIfSerialMatches<NeoOBD2SIM>(dev, foundDevices);
|
makeIfSerialMatches<NeoOBD2SIM>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOVIFIRE_H_
|
#ifdef __NEOVIFIRE_H_
|
||||||
makeIfPIDMatches<NeoVIFIRE>(dev, foundDevices);
|
makeIfPIDMatches<NeoVIFIRE>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOVIFIRE2_H_
|
#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
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOVIRED2_H_
|
#ifdef __NEOVIRED2_H_
|
||||||
makeIfSerialMatches<NeoVIRED2>(dev, foundDevices);
|
makeIfSerialMatches<NeoVIRED2>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOVIION_H_
|
#ifdef __NEOVIION_H_
|
||||||
makeIfPIDMatches<NeoVIION>(dev, foundDevices);
|
makeIfPIDMatches<NeoVIION>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOVIPLASMA_H_
|
#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
|
#endif
|
||||||
|
|
||||||
#ifdef __RADEPSILON_H_
|
#ifdef __RADEPSILON_H_
|
||||||
makeIfSerialMatches<RADEpsilon>(dev, foundDevices);
|
makeIfSerialMatches<RADEpsilon>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __RADGALAXY_H_
|
#ifdef __RADGALAXY_H_
|
||||||
makeIfSerialMatches<RADGalaxy>(dev, foundDevices);
|
makeIfSerialMatches<RADGalaxy>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __RADMARS_H_
|
#ifdef __RADMARS_H_
|
||||||
makeIfSerialMatches<RADMars>(dev, foundDevices);
|
makeIfSerialMatches<RADMars>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __RADGIGASTAR_H_
|
#ifdef __RADGIGASTAR_H_
|
||||||
makeIfSerialMatches<RADGigastar>(dev, foundDevices);
|
makeIfSerialMatches<RADGigastar>(dev, newFoundDevices);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __RADJUPITER_H_
|
||||||
|
makeIfSerialMatches<RADJupiter>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __RADMOON2_H_
|
#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
|
#endif
|
||||||
|
|
||||||
#ifdef __RADMOONDUO_H_
|
#ifdef __RADMOONDUO_H_
|
||||||
makeIfSerialMatches<RADMoonDuo>(dev, foundDevices);
|
makeIfSerialMatches<RADMoonDuo>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __RADPLUTO_H_
|
#ifdef __RADPLUTO_H_
|
||||||
makeIfSerialMatches<RADPluto>(dev, foundDevices);
|
makeIfSerialMatches<RADPluto>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __RADSTAR2_H_
|
#ifdef __RADSTAR2_H_
|
||||||
makeIfSerialMatches<RADStar2>(dev, foundDevices);
|
makeIfSerialMatches<RADStar2>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __RADSUPERMOON_H_
|
#ifdef __RADSUPERMOON_H_
|
||||||
makeIfSerialMatches<RADSupermoon>(dev, foundDevices);
|
makeIfSerialMatches<RADSupermoon>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __VALUECAN3_H_
|
#ifdef __VALUECAN3_H_
|
||||||
makeIfPIDMatches<ValueCAN3>(dev, foundDevices);
|
makeIfPIDMatches<ValueCAN3>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __VALUECAN4_1_H_
|
#ifdef __VALUECAN4_1_H_
|
||||||
makeIfSerialMatches<ValueCAN4_1>(dev, foundDevices);
|
makeIfSerialMatches<ValueCAN4_1>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __VALUECAN4_2_H_
|
#ifdef __VALUECAN4_2_H_
|
||||||
makeIfSerialMatches<ValueCAN4_2>(dev, foundDevices);
|
makeIfSerialMatches<ValueCAN4_2>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __VALUECAN4_2EL_H_
|
#ifdef __VALUECAN4_2EL_H_
|
||||||
makeIfSerialMatches<ValueCAN4_2EL>(dev, foundDevices);
|
makeIfSerialMatches<ValueCAN4_2EL>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __VALUECAN4_4_H_
|
#ifdef __VALUECAN4_4_H_
|
||||||
makeIfSerialMatches<ValueCAN4_4>(dev, foundDevices);
|
makeIfSerialMatches<ValueCAN4_4>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __VALUECAN4INDUSTRIAL_H_
|
#ifdef __VALUECAN4INDUSTRIAL_H_
|
||||||
makeIfSerialMatches<ValueCAN4Industrial>(dev, foundDevices);
|
makeIfSerialMatches<ValueCAN4Industrial>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __VIVIDCAN_H_
|
#ifdef __VIVIDCAN_H_
|
||||||
makeIfSerialMatches<VividCAN>(dev, foundDevices);
|
makeIfSerialMatches<VividCAN>(dev, newFoundDevices);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& device : foundDevices) {
|
for(auto& device : newFoundDevices) {
|
||||||
AddBuiltInExtensionsTo(device);
|
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() {
|
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||||
@@ -194,6 +284,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
|||||||
NeoVIFIRE2::DEVICE_TYPE,
|
NeoVIFIRE2::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __NEOVIFIRE3_H_
|
||||||
|
NeoVIFIRE3::DEVICE_TYPE,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __NEOVIFIRE3FLEXRAY_H_
|
||||||
|
NeoVIFIRE3FlexRay::DEVICE_TYPE,
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOVIION_H_
|
#ifdef __NEOVIION_H_
|
||||||
NeoVIION::DEVICE_TYPE,
|
NeoVIION::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
@@ -202,6 +300,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
|||||||
NeoVIPLASMA::DEVICE_TYPE,
|
NeoVIPLASMA::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __RADA2B_H_
|
||||||
|
RADA2B::DEVICE_TYPE,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __RADCOMET_H_
|
||||||
|
RADCOMET::DEVICE_TYPE,
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __RADEPSILON_H_
|
#ifdef __RADEPSILON_H_
|
||||||
RADEpsilon::DEVICE_TYPE,
|
RADEpsilon::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
@@ -218,10 +324,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
|||||||
RADGigastar::DEVICE_TYPE,
|
RADGigastar::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __RADMOON2_H_
|
#if defined __RADMOON2_H_ || defined __RADMOON2ZL_H_
|
||||||
RADMoon2::DEVICE_TYPE,
|
RADMoon2::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __RADMOON3_H_
|
||||||
|
RADMoon3::DEVICE_TYPE,
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __RADMOONDUO_H_
|
#ifdef __RADMOONDUO_H_
|
||||||
RADMoonDuo::DEVICE_TYPE,
|
RADMoonDuo::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
using namespace icsneo;
|
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());
|
const uint16_t* p = reinterpret_cast<const uint16_t*>(settings.data());
|
||||||
size_t words = std::min(knownSize.value_or(0), settings.size());
|
size_t words = std::min(knownSize.value_or(0), settings.size());
|
||||||
if(words % 2 == 1)
|
if(words % 2 == 1)
|
||||||
return nullopt; // Somehow settings is not word aligned
|
return std::nullopt; // Somehow settings is not word aligned
|
||||||
words /= 2;
|
words /= 2;
|
||||||
|
|
||||||
uint16_t gsCrc = 0;
|
uint16_t gsCrc = 0;
|
||||||
@@ -215,7 +215,7 @@ bool IDeviceSettings::apply(bool temporary) {
|
|||||||
bytestream[2] = GS_VERSION >> 8;
|
bytestream[2] = GS_VERSION >> 8;
|
||||||
bytestream[3] = (uint8_t)settings.size();
|
bytestream[3] = (uint8_t)settings.size();
|
||||||
bytestream[4] = (uint8_t)(settings.size() >> 8);
|
bytestream[4] = (uint8_t)(settings.size() >> 8);
|
||||||
optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
std::optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
||||||
if(!gsChecksum) {
|
if(!gsChecksum) {
|
||||||
// Could not calculate the checksum for some reason
|
// Could not calculate the checksum for some reason
|
||||||
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
||||||
@@ -321,7 +321,7 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
|
|||||||
bytestream[2] = GS_VERSION >> 8;
|
bytestream[2] = GS_VERSION >> 8;
|
||||||
bytestream[3] = (uint8_t)settings.size();
|
bytestream[3] = (uint8_t)settings.size();
|
||||||
bytestream[4] = (uint8_t)(settings.size() >> 8);
|
bytestream[4] = (uint8_t)(settings.size() >> 8);
|
||||||
const optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
const std::optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
|
||||||
if(!gsChecksum) {
|
if(!gsChecksum) {
|
||||||
// Could not calculate the checksum for some reason
|
// Could not calculate the checksum for some reason
|
||||||
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
|
||||||
@@ -638,27 +638,27 @@ bool IDeviceSettings::canTerminationBeEnabledFor(Network net) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<bool> IDeviceSettings::isTerminationEnabledFor(Network net) const {
|
std::optional<bool> IDeviceSettings::isTerminationEnabledFor(Network net) const {
|
||||||
if(!settingsLoaded) {
|
if(!settingsLoaded) {
|
||||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(disabled) {
|
if(disabled) {
|
||||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
ICSNEO_UNALIGNED(const uint64_t*) terminationEnables = getTerminationEnables();
|
ICSNEO_UNALIGNED(const uint64_t*) terminationEnables = getTerminationEnables();
|
||||||
if(terminationEnables == nullptr) {
|
if(terminationEnables == nullptr) {
|
||||||
report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error);
|
report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error);
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto cmNet = net.getCoreMini();
|
const auto cmNet = net.getCoreMini();
|
||||||
if(!cmNet.has_value() || uint64_t(*cmNet) >= 64 || !isTerminationSupportedFor(net)) {
|
if(!cmNet.has_value() || uint64_t(*cmNet) >= 64 || !isTerminationSupportedFor(net)) {
|
||||||
report(APIEvent::Type::TerminationNotSupportedNetwork, APIEvent::Severity::Error);
|
report(APIEvent::Type::TerminationNotSupportedNetwork, APIEvent::Severity::Error);
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*terminationEnables >> uint64_t(*cmNet)) & 0x1;
|
return (*terminationEnables >> uint64_t(*cmNet)) & 0x1;
|
||||||
|
|||||||
+62
-6
@@ -4,19 +4,34 @@
|
|||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
using namespace icsneo::Disk;
|
using namespace icsneo::Disk;
|
||||||
|
|
||||||
optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
|
std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
|
||||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, Disk::MemoryType memType) {
|
||||||
|
|
||||||
|
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
|
||||||
|
uint64_t& cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
|
||||||
|
|
||||||
if(amount == 0)
|
if(amount == 0)
|
||||||
return 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
|
// 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
|
// That would be the case either if we don't want some at the
|
||||||
// beginning or end of the block.
|
// beginning or end of the block.
|
||||||
std::vector<uint8_t> alignedReadBuffer;
|
std::vector<uint8_t> alignedReadBuffer;
|
||||||
|
|
||||||
pos += vsaOffset;
|
|
||||||
const uint32_t idealBlockSize = getBlockSizeBounds().second;
|
const uint32_t idealBlockSize = getBlockSizeBounds().second;
|
||||||
const uint64_t startBlock = pos / idealBlockSize;
|
const uint64_t startBlock = pos / idealBlockSize;
|
||||||
const uint32_t posWithinFirstBlock = static_cast<uint32_t>(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);
|
const uint32_t posWithinCurrentBlock = (blocksProcessed ? 0 : posWithinFirstBlock);
|
||||||
uint32_t curAmt = idealBlockSize - posWithinCurrentBlock;
|
uint32_t curAmt = idealBlockSize - posWithinCurrentBlock;
|
||||||
const auto amountLeft = amount - ret.value_or(0);
|
const auto amountLeft = totalAmount - ret.value_or(0);
|
||||||
if(curAmt > amountLeft)
|
if(curAmt > amountLeft)
|
||||||
curAmt = static_cast<uint32_t>(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 start = std::chrono::high_resolution_clock::now();
|
||||||
auto readAmount = readLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
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);
|
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||||
|
|
||||||
if(!readAmount.has_value() || *readAmount < curAmt) {
|
if(!readAmount.has_value() || *readAmount < curAmt) {
|
||||||
@@ -65,7 +80,48 @@ optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventh
|
|||||||
ret.emplace();
|
ret.emplace();
|
||||||
*ret += std::min<uint64_t>(*readAmount, curAmt);
|
*ret += std::min<uint64_t>(*readAmount, curAmt);
|
||||||
blocksProcessed++;
|
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;
|
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;
|
||||||
using namespace icsneo::Disk;
|
using namespace icsneo::Disk;
|
||||||
|
|
||||||
const uint64_t WriteDriver::RetryAtomic = std::numeric_limits<uint64_t>::max();
|
std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
|
||||||
const APIEvent::Severity WriteDriver::NonatomicSeverity = APIEvent::Severity::EventInfo;
|
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||||
|
|
||||||
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) {
|
|
||||||
if(amount == 0)
|
if(amount == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
optional<uint64_t> ret;
|
std::optional<uint64_t> ret;
|
||||||
|
|
||||||
const uint32_t idealBlockSize = getBlockSizeBounds().second;
|
const uint32_t idealBlockSize = getBlockSizeBounds().second;
|
||||||
|
|
||||||
// Write from here if we need to read-modify-write a block
|
// 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
|
// That would be the case either if we don't want some at the
|
||||||
// beginning or end of the block.
|
// beginning or end of the block.
|
||||||
std::vector<uint8_t> alignedWriteBuffer;
|
std::vector<uint8_t> alignedWriteBuffer(idealBlockSize);
|
||||||
|
|
||||||
// Read to here, ideally this can be sent back to the device to
|
|
||||||
// ensure an operation is atomic
|
|
||||||
std::vector<uint8_t> atomicBuffer(idealBlockSize);
|
|
||||||
|
|
||||||
pos += vsaOffset;
|
pos += vsaOffset;
|
||||||
const uint64_t startBlock = pos / idealBlockSize;
|
const uint64_t startBlock = pos / idealBlockSize;
|
||||||
@@ -54,30 +47,24 @@ optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_even
|
|||||||
t = APIEvent::Type::EOFReached;
|
t = APIEvent::Type::EOFReached;
|
||||||
report(t, s);
|
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);
|
const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
|
||||||
if(useAlignedWriteBuffer) {
|
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);
|
memcpy(alignedWriteBuffer.data() + posWithinCurrentBlock, from + fromOffset, curAmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
start = std::chrono::high_resolution_clock::now();
|
start = std::chrono::high_resolution_clock::now();
|
||||||
bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize, atomicBuffer.data(),
|
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
||||||
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout);
|
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout, memType);
|
||||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
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(!bytesTransferred.has_value() || *bytesTransferred < curAmt) {
|
||||||
if(timeout < std::chrono::milliseconds::zero())
|
if(timeout < std::chrono::milliseconds::zero())
|
||||||
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
|
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
|
||||||
@@ -93,5 +80,9 @@ optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_even
|
|||||||
blocksProcessed++;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
#include "icsneo/disk/extextractordiskreaddriver.h"
|
#include "icsneo/disk/extextractordiskreaddriver.h"
|
||||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
#include "icsneo/communication/message/diskdatamessage.h"
|
||||||
#include "icsneo/communication/multichannelcommunication.h"
|
|
||||||
#include "icsneo/api/lifetime.h"
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
//#define ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
//#define ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||||
@@ -12,202 +9,89 @@
|
|||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
using namespace icsneo::Disk;
|
using namespace icsneo::Disk;
|
||||||
|
|
||||||
optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||||
|
|
||||||
if(amount > getBlockSizeBounds().second)
|
if(amount > getBlockSizeBounds().second)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(amount % getBlockSizeBounds().first != 0)
|
if(amount % getBlockSizeBounds().first != 0)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(pos % getBlockSizeBounds().first != 0)
|
if(pos % getBlockSizeBounds().first != 0)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
optional<uint64_t> ret;
|
std::optional<uint64_t> ret;
|
||||||
while(timeout > std::chrono::milliseconds(0) && !ret.has_value()) {
|
unsigned int attempts = 4;
|
||||||
auto start = std::chrono::steady_clock::now();
|
while (attempts-- > 0)
|
||||||
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);
|
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout, memType);
|
||||||
|
if (ret.has_value())
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
|
||||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
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 sector = pos / SectorSize;
|
|
||||||
|
|
||||||
uint64_t largeSectorCount = amount / SectorSize;
|
uint64_t largeSectorCount = amount / SectorSize;
|
||||||
uint32_t sectorCount = uint32_t(largeSectorCount);
|
uint32_t sectorCount = uint32_t(largeSectorCount);
|
||||||
if (largeSectorCount != uint64_t(sectorCount))
|
if (largeSectorCount != uint64_t(sectorCount))
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
// The cache does not have this data, go get it
|
std::condition_variable cv;
|
||||||
std::mutex m;
|
std::mutex mutex;
|
||||||
std::condition_variable cv;
|
uint8_t* intoOffset = into;
|
||||||
uint16_t receiving = 0; // How much are we about to get before another header or completion
|
int64_t remaining = amount;
|
||||||
uint64_t received = 0;
|
bool complete = false;
|
||||||
uint16_t receivedCurrent = 0;
|
|
||||||
size_t skipping = 0;
|
const auto handle = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> message) {
|
||||||
std::vector<uint8_t> header;
|
if(remaining > 0) {
|
||||||
std::unique_lock<std::mutex> lk(m);
|
const auto diskdata = std::static_pointer_cast<DiskDataMessage>(message);
|
||||||
bool error = !com.redirectRead([&](std::vector<uint8_t>&& data) {
|
|
||||||
std::unique_lock<std::mutex> lk2(m);
|
const auto& data = diskdata->data;
|
||||||
if(error) {
|
std::copy(data.data(), data.data() + data.size(), intoOffset);
|
||||||
lk2.unlock();
|
|
||||||
|
intoOffset += data.size();
|
||||||
|
remaining -= data.size();
|
||||||
|
|
||||||
|
if(remaining == 0) {
|
||||||
|
{
|
||||||
|
std::scoped_lock<std::mutex> lk(mutex);
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
cv.notify_all();
|
cv.notify_all();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}, std::make_shared<MessageFilter>(Network::NetID::DiskData)));
|
||||||
|
|
||||||
if(skipping > data.size()) {
|
if(!com.sendCommand(ExtendedCommand::Extract, {
|
||||||
skipping -= data.size();
|
uint8_t(sector & 0xff),
|
||||||
return;
|
uint8_t((sector >> 8) & 0xff),
|
||||||
}
|
uint8_t((sector >> 16) & 0xff),
|
||||||
size_t offset = skipping;
|
uint8_t((sector >> 24) & 0xff),
|
||||||
skipping = 0;
|
uint8_t((sector >> 32) & 0xff),
|
||||||
while(offset < data.size()) {
|
uint8_t((sector >> 40) & 0xff),
|
||||||
size_t left = data.size() - offset;
|
uint8_t((sector >> 48) & 0xff),
|
||||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
uint8_t((sector >> 56) & 0xff),
|
||||||
std::cout << "Going to process " << left << " bytes" << std::endl;
|
uint8_t(sectorCount & 0xff),
|
||||||
#endif
|
uint8_t((sectorCount >> 8) & 0xff),
|
||||||
if(header.size() != HeaderLength) {
|
uint8_t((sectorCount >> 16) & 0xff),
|
||||||
if(header.empty() && left && data[offset] != 0xaa) {
|
uint8_t((sectorCount >> 24) & 0xff),
|
||||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
})) {
|
||||||
std::cout << "Incorrect header " << int(data[offset]) << ' ' << int(offset) << std::endl;
|
return std::nullopt;
|
||||||
#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, {
|
|
||||||
uint8_t(sector & 0xff),
|
|
||||||
uint8_t((sector >> 8) & 0xff),
|
|
||||||
uint8_t((sector >> 16) & 0xff),
|
|
||||||
uint8_t((sector >> 24) & 0xff),
|
|
||||||
uint8_t((sector >> 32) & 0xff),
|
|
||||||
uint8_t((sector >> 40) & 0xff),
|
|
||||||
uint8_t((sector >> 48) & 0xff),
|
|
||||||
uint8_t((sector >> 56) & 0xff),
|
|
||||||
uint8_t(sectorCount & 0xff),
|
|
||||||
uint8_t((sectorCount >> 8) & 0xff),
|
|
||||||
uint8_t((sectorCount >> 16) & 0xff),
|
|
||||||
uint8_t((sectorCount >> 24) & 0xff),
|
|
||||||
});
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(into, cache.data(), size_t(amount));
|
std::unique_lock<std::mutex> lk(mutex);
|
||||||
return amount;
|
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
|
// The FAT driver can only be accessed by one caller at a time, since it relies on globals
|
||||||
static std::mutex fatDriverMutex;
|
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) {
|
extern "C" DRESULT disk_read(BYTE, BYTE* buff, LBA_t sector, UINT count) {
|
||||||
static_assert(Disk::SectorSize == 512, "FatFs expects 512 byte sectors");
|
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);
|
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);
|
std::lock_guard<std::mutex> lk(fatDriverMutex);
|
||||||
diskReadFn = diskRead;
|
diskReadFn = diskRead;
|
||||||
|
|
||||||
FATFS fs = {};
|
FATFS fs = {};
|
||||||
if (f_mount(&fs, (const TCHAR*)_TEXT(""), 0) != FR_OK)
|
if (f_mount(&fs, (const TCHAR*)_TEXT(""), 0) != FR_OK)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
FIL logData = {};
|
FIL logData = {};
|
||||||
if (f_open(&logData, (const TCHAR*)_TEXT("0:\\LOG_DATA.VSA"), FA_READ) != FR_OK)
|
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);
|
return ClusterToSector(fs, logData.obj.sclust) * uint64_t(Disk::SectorSize);
|
||||||
}
|
}
|
||||||
@@ -1,79 +1,68 @@
|
|||||||
#include "icsneo/disk/neomemorydiskdriver.h"
|
#include "icsneo/disk/neomemorydiskdriver.h"
|
||||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
using namespace icsneo::Disk;
|
using namespace icsneo::Disk;
|
||||||
|
|
||||||
optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||||
|
|
||||||
if(pos % SectorSize != 0)
|
if(pos % SectorSize != 0)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(amount != SectorSize)
|
if(amount != SectorSize)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(cachePos != pos || std::chrono::steady_clock::now() > cachedAt + CacheTime) {
|
const uint64_t currentSector = pos / SectorSize;
|
||||||
// The cache does not have this data, go get it
|
const uint8_t memLocation = (uint8_t)memType;
|
||||||
const uint64_t currentSector = pos / SectorSize;
|
|
||||||
auto msg = com.waitForMessageSync([¤tSector, &com] {
|
auto msg = com.waitForMessageSync([¤tSector, &memLocation, &com] {
|
||||||
return com.sendCommand(Command::NeoReadMemory, {
|
return com.sendCommand(Command::NeoReadMemory, {
|
||||||
MemoryTypeSD,
|
memLocation,
|
||||||
uint8_t(currentSector & 0xFF),
|
uint8_t(currentSector & 0xFF),
|
||||||
uint8_t((currentSector >> 8) & 0xFF),
|
uint8_t((currentSector >> 8) & 0xFF),
|
||||||
uint8_t((currentSector >> 16) & 0xFF),
|
uint8_t((currentSector >> 16) & 0xFF),
|
||||||
uint8_t((currentSector >> 24) & 0xFF),
|
uint8_t((currentSector >> 24) & 0xFF),
|
||||||
uint8_t(SectorSize & 0xFF),
|
uint8_t(SectorSize & 0xFF),
|
||||||
uint8_t((SectorSize >> 8) & 0xFF),
|
uint8_t((SectorSize >> 8) & 0xFF),
|
||||||
uint8_t((SectorSize >> 16) & 0xFF),
|
uint8_t((SectorSize >> 16) & 0xFF),
|
||||||
uint8_t((SectorSize >> 24) & 0xFF)
|
uint8_t((SectorSize >> 24) & 0xFF)
|
||||||
});
|
});
|
||||||
}, NeoMemorySDRead, timeout);
|
}, NeoMemorySDRead, timeout);
|
||||||
|
|
||||||
if(!msg)
|
if(!msg)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
||||||
if(!sdmsg || sdmsg->data.size() != SectorSize) {
|
if(!sdmsg || sdmsg->data.size() != SectorSize) {
|
||||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
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;
|
return SectorSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
std::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) {
|
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);
|
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
|
||||||
|
|
||||||
if(pos % SectorSize != 0)
|
if(pos % SectorSize != 0)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(amount != SectorSize)
|
if(amount != SectorSize)
|
||||||
return nullopt;
|
return std::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);
|
|
||||||
|
|
||||||
const uint64_t currentSector = pos / SectorSize;
|
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 = {
|
std::vector<uint8_t> command = {
|
||||||
MemoryTypeSD,
|
memLocation,
|
||||||
uint8_t(currentSector & 0xFF),
|
uint8_t(currentSector & 0xFF),
|
||||||
uint8_t((currentSector >> 8) & 0xFF),
|
uint8_t((currentSector >> 8) & 0xFF),
|
||||||
uint8_t((currentSector >> 16) & 0xFF),
|
uint8_t((currentSector >> 16) & 0xFF),
|
||||||
@@ -86,7 +75,7 @@ optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& c
|
|||||||
}, NeoMemoryDone, timeout);
|
}, NeoMemoryDone, timeout);
|
||||||
|
|
||||||
if(!msg)
|
if(!msg)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return SectorSize;
|
return SectorSize;
|
||||||
}
|
}
|
||||||
+12
-12
@@ -3,26 +3,26 @@
|
|||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
using namespace icsneo::Disk;
|
using namespace icsneo::Disk;
|
||||||
|
|
||||||
optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
|
std::optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
|
||||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
|
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
std::optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
||||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
|
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
|
std::optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
|
||||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
|
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
std::optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
||||||
uint64_t, const uint8_t*, const uint8_t*, uint64_t, std::chrono::milliseconds) {
|
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -6,73 +6,64 @@
|
|||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
using namespace icsneo::Disk;
|
using namespace icsneo::Disk;
|
||||||
|
|
||||||
optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
std::optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
|
||||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||||
|
|
||||||
if(amount > getBlockSizeBounds().second)
|
if(amount > getBlockSizeBounds().second)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(amount % getBlockSizeBounds().first != 0)
|
if(amount % getBlockSizeBounds().first != 0)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
if(pos % getBlockSizeBounds().first != 0)
|
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;
|
||||||
uint64_t largeSector = pos / SectorSize;
|
uint32_t sector = uint32_t(largeSector);
|
||||||
uint32_t sector = uint32_t(largeSector);
|
if (largeSector != uint64_t(sector))
|
||||||
if (largeSector != uint64_t(sector))
|
return std::nullopt;
|
||||||
return nullopt;
|
|
||||||
|
|
||||||
// The cache does not have this data, go get it
|
std::mutex m;
|
||||||
std::mutex m;
|
std::condition_variable cv;
|
||||||
std::condition_variable cv;
|
uint32_t copied = 0;
|
||||||
uint32_t copied = 0;
|
bool error = false;
|
||||||
bool error = false;
|
std::unique_lock<std::mutex> lk(m);
|
||||||
|
auto cb = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> msg) {
|
||||||
std::unique_lock<std::mutex> lk(m);
|
std::unique_lock<std::mutex> lk(m);
|
||||||
auto cb = com.addMessageCallback(MessageCallback([&](std::shared_ptr<Message> msg) {
|
|
||||||
std::unique_lock<std::mutex> lk(m);
|
|
||||||
|
|
||||||
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
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;
|
error = true;
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
cv.notify_all();
|
cv.notify_all();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate the cache here in case we fail half-way through
|
memcpy(into + copied, sdmsg->data.data(), sdmsg->data.size());
|
||||||
cachedAt = std::chrono::steady_clock::time_point();
|
copied += uint32_t(sdmsg->data.size());
|
||||||
|
if(copied == amount) {
|
||||||
|
lk.unlock();
|
||||||
|
cv.notify_all();
|
||||||
|
|
||||||
|
}
|
||||||
|
}, NeoMemorySDRead));
|
||||||
|
|
||||||
memcpy(cache.data() + copied, sdmsg->data.data(), sdmsg->data.size());
|
com.rawWrite({
|
||||||
copied += uint32_t(sdmsg->data.size());
|
uint8_t(MultiChannelCommunication::CommandType::HostPC_from_SDCC1),
|
||||||
if(copied == amount) {
|
uint8_t(sector & 0xFF),
|
||||||
lk.unlock();
|
uint8_t((sector >> 8) & 0xFF),
|
||||||
cv.notify_all();
|
uint8_t((sector >> 16) & 0xFF),
|
||||||
}
|
uint8_t((sector >> 24) & 0xFF),
|
||||||
}, NeoMemorySDRead));
|
uint8_t(amount & 0xFF),
|
||||||
|
uint8_t((amount >> 8) & 0xFF),
|
||||||
|
});
|
||||||
|
|
||||||
com.rawWrite({
|
bool hitTimeout = !cv.wait_for(lk, timeout, [&copied, &error, &amount] { return error || copied == amount; });
|
||||||
uint8_t(MultiChannelCommunication::CommandType::HostPC_from_SDCC1),
|
com.removeMessageCallback(cb);
|
||||||
uint8_t(sector & 0xFF),
|
|
||||||
uint8_t((sector >> 8) & 0xFF),
|
|
||||||
uint8_t((sector >> 16) & 0xFF),
|
|
||||||
uint8_t((sector >> 24) & 0xFF),
|
|
||||||
uint8_t(amount & 0xFF),
|
|
||||||
uint8_t((amount >> 8) & 0xFF),
|
|
||||||
});
|
|
||||||
|
|
||||||
bool hitTimeout = !cv.wait_for(lk, timeout, [&copied, &error, &amount] { return error || copied == amount; });
|
if(hitTimeout)
|
||||||
com.removeMessageCallback(cb);
|
return std::nullopt;
|
||||||
|
|
||||||
if(hitTimeout)
|
|
||||||
return nullopt;
|
|
||||||
|
|
||||||
cachedAt = std::chrono::steady_clock::now();
|
|
||||||
cachePos = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(into, cache.data(), size_t(amount));
|
|
||||||
return 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.
|
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 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.
|
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.
|
# General information about the project.
|
||||||
project = 'libicsneo'
|
project = 'libicsneo'
|
||||||
copyright = '2018-2020, Intrepid Control Systems, Inc.'
|
copyright = '2018-2023, Intrepid Control Systems, Inc.'
|
||||||
author = 'Intrepid Control Systems, Inc.'
|
author = 'Intrepid Control Systems, Inc.'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# 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.
|
# General information about the project.
|
||||||
project = 'libicsneo'
|
project = 'libicsneo'
|
||||||
copyright = '2018-2020, Intrepid Control Systems, Inc.'
|
copyright = '2018-2023, Intrepid Control Systems, Inc.'
|
||||||
author = 'Intrepid Control Systems, Inc.'
|
author = 'Intrepid Control Systems, Inc.'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# 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_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_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_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
|
# Disabled until we properly build these in-tree
|
||||||
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
|
# option(LIBICSNEO_BUILD_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)
|
add_subdirectory(c/interactive)
|
||||||
endif()
|
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)
|
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
|
||||||
add_subdirectory(cpp/simple)
|
add_subdirectory(cpp/simple)
|
||||||
endif()
|
endif()
|
||||||
@@ -18,6 +33,26 @@ if(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE)
|
|||||||
add_subdirectory(cpp/interactive)
|
add_subdirectory(cpp/interactive)
|
||||||
endif()
|
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)
|
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
|
||||||
# add_subdirectory(csharp)
|
# add_subdirectory(csharp)
|
||||||
# endif()
|
# endif()
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ endif()
|
|||||||
add_executable(libicsneoc-interactive-example src/main.c)
|
add_executable(libicsneoc-interactive-example src/main.c)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
target_link_libraries(libicsneoc-interactive-example ${CMAKE_DL_LIBS})
|
target_link_libraries(libicsneoc-interactive-example ${CMAKE_DL_LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
target_link_libraries(libicsneoc-interactive-example icsneoc)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Signal to dynamically load the library
|
// Signal to dynamically load the library
|
||||||
#define ICSNEOC_DYNAMICLOAD
|
//#define ICSNEOC_DYNAMICLOAD
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -219,21 +219,21 @@ int main() {
|
|||||||
|
|
||||||
// Attempt to initialize the library and access its functions
|
// Attempt to initialize the library and access its functions
|
||||||
// This call searches for icsneoc.dll according to the standard dynamic-link library search order
|
// This call searches for icsneoc.dll according to the standard dynamic-link library search order
|
||||||
int ret = icsneo_init();
|
//int ret = icsneo_init();
|
||||||
if(ret == 1) {
|
//if(ret == 1) {
|
||||||
printf("The library was already initialized!\n");
|
// printf("The library was already initialized!\n");
|
||||||
return ret;
|
// return ret;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
if(ret == 2) {
|
//if(ret == 2) {
|
||||||
printf("The library could not be found!\n");
|
// printf("The library could not be found!\n");
|
||||||
return ret;
|
// return ret;
|
||||||
}
|
//}
|
||||||
|
//
|
||||||
if(ret == 3) {
|
//if(ret == 3) {
|
||||||
printf("The library is missing functions!\n");
|
// printf("The library is missing functions!\n");
|
||||||
return ret;
|
// return ret;
|
||||||
}
|
//}
|
||||||
|
|
||||||
neoversion_t ver = icsneo_getVersion();
|
neoversion_t ver = icsneo_getVersion();
|
||||||
printf("ICS icsneoc.dll version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
|
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);
|
printf("(%"PRIu64")\n", canMsg->timestamp);
|
||||||
break;
|
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:
|
default:
|
||||||
printf("\tMessage on netid %d with length %zu\n", frame->netid, frame->length);
|
printf("\tMessage on netid %d with length %zu\n", frame->netid, frame->length);
|
||||||
break;
|
break;
|
||||||
@@ -762,7 +787,7 @@ int main() {
|
|||||||
case 'X':
|
case 'X':
|
||||||
case 'x':
|
case 'x':
|
||||||
printf("Exiting program\n");
|
printf("Exiting program\n");
|
||||||
return !icsneo_close();
|
return 0;
|
||||||
default:
|
default:
|
||||||
printf("Unexpected input, exiting!\n");
|
printf("Unexpected input, exiting!\n");
|
||||||
return 1;
|
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) {
|
switch(selection) {
|
||||||
case '1':
|
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);
|
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"
|
#include "icsneo/icsneocpp.h"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable : 4996) // STL time functions
|
||||||
|
#endif
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// Print version
|
// Print version
|
||||||
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||||
@@ -96,9 +100,31 @@ int main() {
|
|||||||
std::cout << "\tSetting settings permanently... ";
|
std::cout << "\tSetting settings permanently... ";
|
||||||
ret = device->settings->apply();
|
ret = device->settings->apply();
|
||||||
std::cout << (ret ? "OK\n\n" : "FAIL\n\n");
|
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
|
// 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();
|
ret = device->goOnline();
|
||||||
if(!ret) {
|
if(!ret) {
|
||||||
std::cout << "FAIL" << std::endl;
|
std::cout << "FAIL" << std::endl;
|
||||||
@@ -127,7 +153,7 @@ int main() {
|
|||||||
// Keep in mind that 20k messages comes quickly at high bus loads!
|
// Keep in mind that 20k messages comes quickly at high bus loads!
|
||||||
|
|
||||||
// We can transmit messages
|
// 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>();
|
auto txMessage5 = std::make_shared<icsneo::CANMessage>();
|
||||||
txMessage5->network = icsneo::Network::NetID::HSCAN;
|
txMessage5->network = icsneo::Network::NetID::HSCAN;
|
||||||
txMessage5->arbid = 0x1C5001C5;
|
txMessage5->arbid = 0x1C5001C5;
|
||||||
@@ -141,7 +167,7 @@ int main() {
|
|||||||
// We can also register a handler
|
// We can also register a handler
|
||||||
std::cout << "\tStreaming messages in for 3 seconds... " << std::endl;
|
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
|
// 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) {
|
switch(message->type) {
|
||||||
case icsneo::Message::Type::Frame: {
|
case icsneo::Message::Type::Frame: {
|
||||||
// A message of type Frame is guaranteed to be a Frame, so we can static cast safely
|
// 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()) {
|
if(!emisc.empty()) {
|
||||||
std::cout << "\tReading EMisc values..." << std::endl;
|
std::cout << "\tReading EMisc values..." << std::endl;
|
||||||
for(const auto& io : emisc) {
|
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;
|
std::cout << "\t\tEMISC" << io.number;
|
||||||
if(val.has_value())
|
if(val.has_value())
|
||||||
std::cout << " - OK (" << val.value() << "V)" << std::endl;
|
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() {
|
SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_MAIN51_get() {
|
||||||
int jresult ;
|
int jresult ;
|
||||||
int result;
|
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_AUX = icsneocsharpPINVOKE.ICSNEO_NETID_AUX_get();
|
||||||
public static readonly int ICSNEO_NETID_J1850VPW = icsneocsharpPINVOKE.ICSNEO_NETID_J1850VPW_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_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_MAIN51 = icsneocsharpPINVOKE.ICSNEO_NETID_MAIN51_get();
|
||||||
public static readonly int ICSNEO_NETID_RED = icsneocsharpPINVOKE.ICSNEO_NETID_RED_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();
|
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")]
|
[global::System.Runtime.InteropServices.DllImport("icsneocsharp.dll", EntryPoint="CSharp_ICSNEO_NETID_ISO_get")]
|
||||||
public static extern int 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")]
|
[global::System.Runtime.InteropServices.DllImport("icsneocsharp.dll", EntryPoint="CSharp_ICSNEO_NETID_MAIN51_get")]
|
||||||
public static extern int ICSNEO_NETID_MAIN51_get();
|
public static extern int ICSNEO_NETID_MAIN51_get();
|
||||||
|
|
||||||
|
|||||||
+727
-739
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
|||||||
|
package com.example.icsneojava;
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* This file was automatically generated by SWIG (http://www.swig.org).
|
* This file was automatically generated by SWIG (http://www.swig.org).
|
||||||
* Version 4.0.0
|
* 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_AUX = icsneojavaJNI.ICSNEO_NETID_AUX_get();
|
||||||
public final static int ICSNEO_NETID_J1850VPW = icsneojavaJNI.ICSNEO_NETID_J1850VPW_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_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_MAIN51 = icsneojavaJNI.ICSNEO_NETID_MAIN51_get();
|
||||||
public final static int ICSNEO_NETID_RED = icsneojavaJNI.ICSNEO_NETID_RED_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();
|
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_AUX_get();
|
||||||
public final static native int ICSNEO_NETID_J1850VPW_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_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_MAIN51_get();
|
||||||
public final static native int ICSNEO_NETID_RED_get();
|
public final static native int ICSNEO_NETID_RED_get();
|
||||||
public final static native int ICSNEO_NETID_SCI_get();
|
public final static native int ICSNEO_NETID_SCI_get();
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ public:
|
|||||||
MessageMaxLengthExceeded = 0x1012,
|
MessageMaxLengthExceeded = 0x1012,
|
||||||
ValueNotYetPresent = 0x1013,
|
ValueNotYetPresent = 0x1013,
|
||||||
Timeout = 0x1014,
|
Timeout = 0x1014,
|
||||||
|
WiVINotSupported = 0x1015,
|
||||||
|
|
||||||
// Device Events
|
// Device Events
|
||||||
PollingMessageOverflow = 0x2000,
|
PollingMessageOverflow = 0x2000,
|
||||||
NoSerialNumber = 0x2001, // api
|
NoSerialNumber = 0x2001, // api
|
||||||
@@ -86,6 +87,23 @@ public:
|
|||||||
SettingsDefaultsUsed = 0x2033,
|
SettingsDefaultsUsed = 0x2033,
|
||||||
AtomicOperationRetried = 0x2034,
|
AtomicOperationRetried = 0x2034,
|
||||||
AtomicOperationCompletedNonatomically = 0x2035,
|
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
|
// Transport Events
|
||||||
FailedToRead = 0x3000,
|
FailedToRead = 0x3000,
|
||||||
@@ -98,11 +116,62 @@ public:
|
|||||||
PCAPCouldNotStart = 0x3102,
|
PCAPCouldNotStart = 0x3102,
|
||||||
PCAPCouldNotFindDevices = 0x3103,
|
PCAPCouldNotFindDevices = 0x3103,
|
||||||
PacketDecodingError = 0x3104,
|
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,
|
NoErrorFound = 0xFFFFFFFD,
|
||||||
TooManyEvents = 0xFFFFFFFE,
|
TooManyEvents = 0xFFFFFFFE,
|
||||||
Unknown = 0xFFFFFFFF
|
Unknown = 0xFFFFFFFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Severity : uint8_t {
|
enum class Severity : uint8_t {
|
||||||
Any = 0, // Used for filtering, should not appear in data
|
Any = 0, // Used for filtering, should not appear in data
|
||||||
EventInfo = 0x10,
|
EventInfo = 0x10,
|
||||||
@@ -166,4 +235,4 @@ public:
|
|||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ public:
|
|||||||
rhs.fnOnDeath = std::function<void(void)>();
|
rhs.fnOnDeath = std::function<void(void)>();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow checking for empty Lifetimes
|
||||||
|
bool empty() const { return fnOnDeath.operator bool(); }
|
||||||
|
operator bool() const { return empty(); }
|
||||||
private:
|
private:
|
||||||
std::function<void(void)> fnOnDeath;
|
std::function<void(void)> fnOnDeath;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ enum class Command : uint8_t {
|
|||||||
EnableNetworkCommunicationEx = 0x08,
|
EnableNetworkCommunicationEx = 0x08,
|
||||||
NeoReadMemory = 0x40,
|
NeoReadMemory = 0x40,
|
||||||
NeoWriteMemory = 0x41,
|
NeoWriteMemory = 0x41,
|
||||||
|
ClearCoreMini = 0x42,
|
||||||
|
LoadCoreMini = 0x43,
|
||||||
|
GetRTC = 0x49,
|
||||||
|
SetRTC = 0x50,
|
||||||
RequestSerialNumber = 0xA1,
|
RequestSerialNumber = 0xA1,
|
||||||
GetMainVersion = 0xA3, // Previously known as RED_CMD_APP_VERSION_REQ
|
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
|
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
|
ReadSettings = 0xC7, // Previously known as 3G_READ_SETTINGS_EX
|
||||||
SetVBattMonitor = 0xDB, // Previously known as RED_CMD_CM_VBATT_MONITOR
|
SetVBattMonitor = 0xDB, // Previously known as RED_CMD_CM_VBATT_MONITOR
|
||||||
RequestBitSmash = 0xDC, // Previously known as RED_CMD_CM_BITSMASH
|
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
|
GetVBattReq = 0xDF, // Previously known as RED_CMD_VBATT_REQUEST
|
||||||
|
ScriptStatus = 0xE0, // Previously known as RED_CMD_SCRIPT_STATUS
|
||||||
MiscControl = 0xE7,
|
MiscControl = 0xE7,
|
||||||
Extended = 0xF0,
|
Extended = 0xF0, // Previously known as RED_CMD_EXT_COMM
|
||||||
|
ExtendedData = 0xF2, // Previously known as RED_CMD_EXTENDED_DATA
|
||||||
FlexRayControl = 0xF3,
|
FlexRayControl = 0xF3,
|
||||||
PHYControlRegisters = 0xEF
|
CoreMiniPreload = 0xF4, // Previously known as RED_CMD_COREMINI_PRELOAD
|
||||||
|
PHYControlRegisters = 0xEF,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ExtendedCommand : uint16_t {
|
enum class ExtendedCommand : uint16_t {
|
||||||
|
GenericReturn = 0x0000,
|
||||||
GetDiskDetails = 0x0010,
|
GetDiskDetails = 0x0010,
|
||||||
DiskFormatStart = 0x0011,
|
DiskFormatStart = 0x0011,
|
||||||
DiskFormatCancel = 0x0012,
|
DiskFormatCancel = 0x0012,
|
||||||
@@ -39,8 +48,37 @@ enum class ExtendedCommand : uint16_t {
|
|||||||
Extract = 0x0015,
|
Extract = 0x0015,
|
||||||
StartDHCPServer = 0x0016,
|
StartDHCPServer = 0x0016,
|
||||||
StopDHCPServer = 0x0017,
|
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
|
#endif // __cplusplus
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include "icsneo/communication/message/callback/messagecallback.h"
|
#include "icsneo/communication/message/callback/messagecallback.h"
|
||||||
#include "icsneo/communication/message/serialnumbermessage.h"
|
#include "icsneo/communication/message/serialnumbermessage.h"
|
||||||
#include "icsneo/communication/message/logicaldiskinfomessage.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/device/deviceversion.h"
|
||||||
#include "icsneo/api/eventmanager.h"
|
#include "icsneo/api/eventmanager.h"
|
||||||
#include "icsneo/communication/packetizer.h"
|
#include "icsneo/communication/packetizer.h"
|
||||||
@@ -56,10 +58,11 @@ public:
|
|||||||
bool sendCommand(ExtendedCommand cmd, std::vector<uint8_t> arguments = {});
|
bool sendCommand(ExtendedCommand cmd, std::vector<uint8_t> arguments = {});
|
||||||
bool getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
|
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));
|
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::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);
|
bool removeMessageCallback(int id);
|
||||||
std::shared_ptr<Message> waitForMessageSync(
|
std::shared_ptr<Message> waitForMessageSync(
|
||||||
const std::shared_ptr<MessageFilter>& f = {},
|
const std::shared_ptr<MessageFilter>& f = {},
|
||||||
@@ -83,11 +86,12 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
static int messageCallbackIDCounter;
|
static int messageCallbackIDCounter;
|
||||||
std::mutex messageCallbacksLock;
|
std::mutex messageCallbacksLock;
|
||||||
std::map<int, MessageCallback> messageCallbacks;
|
std::map<int, std::shared_ptr<MessageCallback>> messageCallbacks;
|
||||||
std::atomic<bool> closing{false};
|
std::atomic<bool> closing{false};
|
||||||
std::atomic<bool> redirectingRead{false};
|
std::atomic<bool> redirectingRead{false};
|
||||||
std::function<void(std::vector<uint8_t>&&)> redirectionFn;
|
std::function<void(std::vector<uint8_t>&&)> redirectionFn;
|
||||||
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the 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 dispatchMessage(const std::shared_ptr<Message>& msg);
|
||||||
void handleInput(Packetizer& p, std::vector<uint8_t>& readBytes);
|
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
|
||||||
@@ -28,6 +28,8 @@ public:
|
|||||||
: MessageCallback(cb, f) {}
|
: MessageCallback(cb, f) {}
|
||||||
MessageCallback(MessageFilter f, fn_messageCallback cb)
|
MessageCallback(MessageFilter f, fn_messageCallback cb)
|
||||||
: MessageCallback(cb, std::make_shared<MessageFilter>(f)) {}
|
: MessageCallback(cb, std::make_shared<MessageFilter>(f)) {}
|
||||||
|
|
||||||
|
virtual ~MessageCallback() = default;
|
||||||
|
|
||||||
virtual bool callIfMatch(const std::shared_ptr<Message>& message) const {
|
virtual bool callIfMatch(const std::shared_ptr<Message>& message) const {
|
||||||
bool ret = filter->match(message);
|
bool ret = filter->match(message);
|
||||||
|
|||||||
@@ -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:
|
public:
|
||||||
MessageFilter() {}
|
MessageFilter() {}
|
||||||
MessageFilter(Message::Type type) : includeInternalInAny(neomessagetype_t(type) & 0x8000), messageType(type) {}
|
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) {
|
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
|
// If a Network::Type::Internal is used, we want to also get internal Message::Types
|
||||||
// The NetID we want may be in there
|
// The NetID we want may be in there
|
||||||
@@ -34,6 +34,8 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
if(!matchNetID(frame.network.getNetID()))
|
if(!matchNetID(frame.network.getNetID()))
|
||||||
return false;
|
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;
|
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