mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-23 09:19:46 +02:00
Compare commits
170
Commits
v0.3.0-dev
...
cf1b4778a1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf1b4778a1 | ||
|
|
77928dc93d | ||
|
|
659fcf633c | ||
|
|
9a1cd1124d | ||
|
|
3782e12ef6 | ||
|
|
bc6f5eca9c | ||
|
|
53ba297a44 | ||
|
|
d9bf4f423e | ||
|
|
ba3bceaee0 | ||
|
|
31d47613e7 | ||
|
|
9c830a91a2 | ||
|
|
46e244fbab | ||
|
|
63f0516318 | ||
|
|
7d2d12c5cd | ||
|
|
bcf9d62be9 | ||
|
|
a01aa90740 | ||
|
|
85178368d7 | ||
|
|
16b60d5ca0 | ||
|
|
cb22e622b3 | ||
|
|
06f6861130 | ||
|
|
219a5edbd8 | ||
|
|
05888e5c20 | ||
|
|
e37d939699 | ||
|
|
2a94b4566b | ||
|
|
4782e26bed | ||
|
|
3264a1ecbe | ||
|
|
1dfed4c9c4 | ||
|
|
a44952be13 | ||
|
|
75e9319c32 | ||
|
|
9df4aed19f | ||
|
|
d56e66afd3 | ||
|
|
26b52237e4 | ||
|
|
28ba94fa25 | ||
|
|
e56c714a0c | ||
|
|
58f53edea8 | ||
|
|
3d2d3cb43d | ||
|
|
bddbcfcf6d | ||
|
|
0497b361ef | ||
|
|
02f1b4592e | ||
|
|
4248c1a538 | ||
|
|
ec51393dfc | ||
|
|
90e1aa223d | ||
|
|
e0cef880f0 | ||
|
|
cc7cffe068 | ||
|
|
e97b307c4d | ||
|
|
0c436621a0 | ||
|
|
508013baf4 | ||
|
|
8d704b1bbb | ||
|
|
018f1fac8e | ||
|
|
188a087c51 | ||
|
|
53e66b8772 | ||
|
|
41d6927496 | ||
|
|
237bed1b72 | ||
|
|
6c93fc839b | ||
|
|
4cf35acf6c | ||
|
|
9ef93eb73e | ||
|
|
5b3e21fa7c | ||
|
|
a36d114e1f | ||
|
|
ee9cf26eb2 | ||
|
|
d7d98855ea | ||
|
|
245f5089da | ||
|
|
d9c12bffe7 | ||
|
|
f907d6759f | ||
|
|
1e773ba9ab | ||
|
|
f5096b879c | ||
|
|
0de23f2817 | ||
|
|
32900ae263 | ||
|
|
b6d9ef4c7e | ||
|
|
2f7c3a2dda | ||
|
|
bbf348a6ab | ||
|
|
73744bf6d9 | ||
|
|
6b0c588a46 | ||
|
|
b3bbf91e8c | ||
|
|
af31aa23ad | ||
|
|
83f6c611fe | ||
|
|
4fa813ccbc | ||
|
|
3ddb832708 | ||
|
|
63c81b1c3d | ||
|
|
0fa7494eab | ||
|
|
cd7b0b0a7b | ||
|
|
d9cdd03618 | ||
|
|
832cf9c84b | ||
|
|
bf6a059820 | ||
|
|
9b46d486cb | ||
|
|
ddee1254a0 | ||
|
|
539cfa511b | ||
|
|
4229d8b66a | ||
|
|
dc1c4ede9d | ||
|
|
9e7d442e83 | ||
|
|
41a569fc2d | ||
|
|
f4e4a103ad | ||
|
|
78465e0f20 | ||
|
|
ed1632c652 | ||
|
|
c97db0f35f | ||
|
|
c9d8a4f0a9 | ||
|
|
96fe8895fc | ||
|
|
e46755f120 | ||
|
|
0619c37a90 | ||
|
|
de58b2d639 | ||
|
|
5ff1707c47 | ||
|
|
4c0d49f09a | ||
|
|
b32f58da38 | ||
|
|
0101467154 | ||
|
|
17b3018499 | ||
|
|
0fc676e576 | ||
|
|
7b2544864b | ||
|
|
7f22286838 | ||
|
|
643768fb6a | ||
|
|
9817887523 | ||
|
|
13ec2bca98 | ||
|
|
711976cce0 | ||
|
|
e02203a974 | ||
|
|
5b14ec7246 | ||
|
|
3b80746fb8 | ||
|
|
3b9a31ef51 | ||
|
|
124bd3b310 | ||
|
|
9871430288 | ||
|
|
2e296dc8d3 | ||
|
|
b35fab754c | ||
|
|
9ef01e2d3d | ||
|
|
1bb33156f7 | ||
|
|
a0f73755e8 | ||
|
|
ab54697745 | ||
|
|
0d0b7f00bd | ||
|
|
1a28fbf5ad | ||
|
|
b50706846c | ||
|
|
d6d34eb7bc | ||
|
|
a928a1d879 | ||
|
|
de3d8bf870 | ||
|
|
407ccccedd | ||
|
|
00024990e9 | ||
|
|
cb47065a47 | ||
|
|
2b819065ad | ||
|
|
58700afc73 | ||
|
|
55d7d5bf17 | ||
|
|
c08c0dd893 | ||
|
|
44ad06d4bb | ||
|
|
c2de1dfdf1 | ||
|
|
a2cfc50b7c | ||
|
|
c398afc4e3 | ||
|
|
8d495aa916 | ||
|
|
91abf378f8 | ||
|
|
2e3b738e76 | ||
|
|
103f938d69 | ||
|
|
f1d9be1a81 | ||
|
|
2c282fe396 | ||
|
|
95de93aa84 | ||
|
|
733312628c | ||
|
|
b177617940 | ||
|
|
28d3730375 | ||
|
|
c451fc2b7c | ||
|
|
c620b2ee8b | ||
|
|
111d377d4a | ||
|
|
c3469f5fb6 | ||
|
|
0219eccc94 | ||
|
|
e22f70a35a | ||
|
|
b019c20ad0 | ||
|
|
6fa469de5f | ||
|
|
be219288dc | ||
|
|
d35653e3d0 | ||
|
|
0ce7326b83 | ||
|
|
714db03a05 | ||
|
|
d097fb9a74 | ||
|
|
9d5bad94d4 | ||
|
|
181223375a | ||
|
|
51626b9e63 | ||
|
|
a1a544045b | ||
|
|
719dbcefc8 | ||
|
|
c5314884ce | ||
|
|
82954d9190 |
@@ -9,3 +9,9 @@ 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
|
||||||
|
test/system
|
||||||
+332
-9
@@ -1,6 +1,14 @@
|
|||||||
|
variables:
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- test
|
- unit_test
|
||||||
|
- hardware_test
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# Windows
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
build windows/x64:
|
build windows/x64:
|
||||||
stage: build
|
stage: build
|
||||||
@@ -14,17 +22,17 @@ build windows/x64:
|
|||||||
tags:
|
tags:
|
||||||
- icsneo-windows
|
- icsneo-windows
|
||||||
|
|
||||||
test windows/x64:
|
unit_test windows/x64:
|
||||||
stage: test
|
stage: unit_test
|
||||||
script:
|
script:
|
||||||
- build\libicsneo-tests.exe
|
- build\libicsneo-unit-tests.exe
|
||||||
dependencies:
|
dependencies:
|
||||||
- build windows/x64
|
- build windows/x64
|
||||||
needs:
|
needs:
|
||||||
- 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
|
||||||
@@ -38,14 +46,329 @@ build windows/x86:
|
|||||||
tags:
|
tags:
|
||||||
- icsneo-windows
|
- icsneo-windows
|
||||||
|
|
||||||
test windows/x86:
|
unit_test windows/x86:
|
||||||
stage: test
|
stage: unit_test
|
||||||
script:
|
script:
|
||||||
- build\libicsneo-tests.exe
|
- build\libicsneo-unit-tests.exe
|
||||||
dependencies:
|
dependencies:
|
||||||
- build windows/x86
|
- build windows/x86
|
||||||
needs:
|
needs:
|
||||||
- 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 git
|
||||||
|
- sh ci/build-posix.sh
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
expire_in: 3 days
|
||||||
|
tags:
|
||||||
|
- linux-build
|
||||||
|
|
||||||
|
.test_linux_ubuntu_gcc: &test_linux_ubuntu_gcc
|
||||||
|
stage: unit_test
|
||||||
|
script:
|
||||||
|
- apt update -y
|
||||||
|
- apt upgrade -y
|
||||||
|
- apt install -y libusb-1.0-0-dev libpcap-dev
|
||||||
|
- build/libicsneo-unit-tests
|
||||||
|
tags:
|
||||||
|
- linux-build
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
.build_linux_ubuntu_clang: &build_linux_ubuntu_clang
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- apt update -y
|
||||||
|
- apt upgrade -y
|
||||||
|
- apt install -y clang lld ninja-build cmake libusb-1.0-0-dev libpcap-dev git
|
||||||
|
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
expire_in: 3 days
|
||||||
|
tags:
|
||||||
|
- linux-build
|
||||||
|
|
||||||
|
.test_linux_ubuntu_clang: &test_linux_ubuntu_clang
|
||||||
|
stage: unit_test
|
||||||
|
script:
|
||||||
|
- apt update -y
|
||||||
|
- apt upgrade -y
|
||||||
|
- apt install -y libusb-1.0-0-dev libpcap-dev
|
||||||
|
- build/libicsneo-unit-tests
|
||||||
|
tags:
|
||||||
|
- linux-build
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
build linux/ubuntu/2004/amd64/gcc:
|
||||||
|
<<: *build_linux_ubuntu_gcc
|
||||||
|
image: ubuntu:20.04
|
||||||
|
|
||||||
|
unit_test linux/ubuntu/2004/amd64/gcc:
|
||||||
|
<<: *test_linux_ubuntu_gcc
|
||||||
|
image: ubuntu:20.04
|
||||||
|
dependencies:
|
||||||
|
- build linux/ubuntu/2004/amd64/gcc
|
||||||
|
needs:
|
||||||
|
- build linux/ubuntu/2004/amd64/gcc
|
||||||
|
|
||||||
|
build linux/ubuntu/2004/amd64/clang:
|
||||||
|
<<: *build_linux_ubuntu_clang
|
||||||
|
image: ubuntu:20.04
|
||||||
|
|
||||||
|
unit_test linux/ubuntu/2004/amd64/clang:
|
||||||
|
<<: *test_linux_ubuntu_clang
|
||||||
|
image: ubuntu:20.04
|
||||||
|
dependencies:
|
||||||
|
- build linux/ubuntu/2004/amd64/clang
|
||||||
|
needs:
|
||||||
|
- build linux/ubuntu/2004/amd64/clang
|
||||||
|
|
||||||
|
build linux/ubuntu/2204/amd64/gcc:
|
||||||
|
<<: *build_linux_ubuntu_gcc
|
||||||
|
image: ubuntu:22.04
|
||||||
|
|
||||||
|
unit_test linux/ubuntu/2204/amd64/gcc:
|
||||||
|
<<: *test_linux_ubuntu_gcc
|
||||||
|
image: ubuntu:22.04
|
||||||
|
dependencies:
|
||||||
|
- build linux/ubuntu/2204/amd64/gcc
|
||||||
|
needs:
|
||||||
|
- build linux/ubuntu/2204/amd64/gcc
|
||||||
|
|
||||||
|
build linux/ubuntu/2204/amd64/clang:
|
||||||
|
<<: *build_linux_ubuntu_clang
|
||||||
|
image: ubuntu:22.04
|
||||||
|
|
||||||
|
unit_test linux/ubuntu/2204/amd64/clang:
|
||||||
|
<<: *test_linux_ubuntu_clang
|
||||||
|
image: ubuntu:22.04
|
||||||
|
dependencies:
|
||||||
|
- build linux/ubuntu/2204/amd64/clang
|
||||||
|
needs:
|
||||||
|
- build linux/ubuntu/2204/amd64/clang
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
# Fedora
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
.build_linux_fedora_gcc: &build_linux_fedora_gcc
|
||||||
|
stage: build
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- /var/cache/dnf
|
||||||
|
script:
|
||||||
|
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
|
||||||
|
- echo fastestmirror=True >>/etc/dnf/dnf.conf
|
||||||
|
- dnf upgrade -y
|
||||||
|
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel git
|
||||||
|
- sh ci/build-posix.sh
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
expire_in: 3 days
|
||||||
|
tags:
|
||||||
|
- linux-build
|
||||||
|
|
||||||
|
.test_linux_fedora_gcc: &test_linux_fedora_gcc
|
||||||
|
stage: unit_test
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- /var/cache/dnf
|
||||||
|
script:
|
||||||
|
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
|
||||||
|
- echo fastestmirror=True >>/etc/dnf/dnf.conf
|
||||||
|
- dnf upgrade -y
|
||||||
|
- dnf install -y libpcap-devel libusb1-devel
|
||||||
|
- build/libicsneo-unit-tests
|
||||||
|
tags:
|
||||||
|
- linux-build
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
.build_linux_fedora_clang: &build_linux_fedora_clang
|
||||||
|
stage: build
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- /var/cache/dnf
|
||||||
|
script:
|
||||||
|
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
|
||||||
|
- echo fastestmirror=True >>/etc/dnf/dnf.conf
|
||||||
|
- dnf upgrade -y
|
||||||
|
- dnf install -y clang lld libpcap-devel cmake ninja-build libusb1-devel git
|
||||||
|
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
expire_in: 3 days
|
||||||
|
tags:
|
||||||
|
- linux-build
|
||||||
|
|
||||||
|
.test_linux_fedora_clang: &test_linux_fedora_clang
|
||||||
|
stage: unit_test
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- /var/cache/dnf
|
||||||
|
script:
|
||||||
|
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
|
||||||
|
- echo fastestmirror=True >>/etc/dnf/dnf.conf
|
||||||
|
- dnf upgrade -y
|
||||||
|
- dnf install -y libpcap-devel libusb1-devel
|
||||||
|
- build/libicsneo-unit-tests
|
||||||
|
tags:
|
||||||
|
- linux-build
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
build linux/fedora/37/amd64/gcc:
|
||||||
|
<<: *build_linux_fedora_gcc
|
||||||
|
image: fedora:37
|
||||||
|
|
||||||
|
unit_test linux/fedora/37/amd64/gcc:
|
||||||
|
<<: *test_linux_fedora_gcc
|
||||||
|
image: fedora:37
|
||||||
|
dependencies:
|
||||||
|
- build linux/fedora/37/amd64/gcc
|
||||||
|
needs:
|
||||||
|
- build linux/fedora/37/amd64/gcc
|
||||||
|
|
||||||
|
build linux/fedora/37/amd64/clang:
|
||||||
|
<<: *build_linux_fedora_clang
|
||||||
|
image: fedora:37
|
||||||
|
|
||||||
|
unit_test linux/fedora/37/amd64/clang:
|
||||||
|
<<: *test_linux_fedora_clang
|
||||||
|
image: fedora:37
|
||||||
|
dependencies:
|
||||||
|
- build linux/fedora/37/amd64/clang
|
||||||
|
needs:
|
||||||
|
- build linux/fedora/37/amd64/clang
|
||||||
|
|
||||||
|
build linux/fedora/38/amd64/gcc:
|
||||||
|
<<: *build_linux_fedora_gcc
|
||||||
|
image: fedora:38
|
||||||
|
|
||||||
|
unit_test linux/fedora/38/amd64/gcc:
|
||||||
|
<<: *test_linux_fedora_gcc
|
||||||
|
image: fedora:38
|
||||||
|
dependencies:
|
||||||
|
- build linux/fedora/38/amd64/gcc
|
||||||
|
needs:
|
||||||
|
- build linux/fedora/38/amd64/gcc
|
||||||
|
|
||||||
|
build linux/fedora/38/amd64/clang:
|
||||||
|
<<: *build_linux_fedora_clang
|
||||||
|
image: fedora:38
|
||||||
|
|
||||||
|
unit_test linux/fedora/38/amd64/clang:
|
||||||
|
<<: *test_linux_fedora_clang
|
||||||
|
image: fedora:38
|
||||||
|
dependencies:
|
||||||
|
- build linux/fedora/38/amd64/clang
|
||||||
|
needs:
|
||||||
|
- build linux/fedora/38/amd64/clang
|
||||||
|
|
||||||
|
build linux/fedora/39/amd64/gcc:
|
||||||
|
<<: *build_linux_fedora_gcc
|
||||||
|
image: fedora:39
|
||||||
|
|
||||||
|
unit_test linux/fedora/39/amd64/gcc:
|
||||||
|
<<: *test_linux_fedora_gcc
|
||||||
|
image: fedora:39
|
||||||
|
dependencies:
|
||||||
|
- build linux/fedora/39/amd64/gcc
|
||||||
|
needs:
|
||||||
|
- build linux/fedora/39/amd64/gcc
|
||||||
|
|
||||||
|
build linux/fedora/39/amd64/clang:
|
||||||
|
<<: *build_linux_fedora_clang
|
||||||
|
image: fedora:39
|
||||||
|
|
||||||
|
unit_test linux/fedora/39/amd64/clang:
|
||||||
|
<<: *test_linux_fedora_clang
|
||||||
|
image: fedora:39
|
||||||
|
dependencies:
|
||||||
|
- build linux/fedora/39/amd64/clang
|
||||||
|
needs:
|
||||||
|
- build linux/fedora/39/amd64/clang
|
||||||
|
|
||||||
|
.hw_test: &hw_test
|
||||||
|
stage: hardware_test
|
||||||
|
tags:
|
||||||
|
- libicsneo_hil
|
||||||
|
timeout: 5m
|
||||||
|
script:
|
||||||
|
- echo $GUEST_OS_TAG
|
||||||
|
- echo $DEVICE_PORT
|
||||||
|
- /opt/libvirt-driver/prepare.sh
|
||||||
|
- /opt/libvirt-driver/run.sh
|
||||||
|
after_script:
|
||||||
|
- /opt/libvirt-driver/cleanup.sh
|
||||||
|
|
||||||
|
hardware_test system-test-fedora38-red2:
|
||||||
|
<<: *hw_test
|
||||||
|
variables:
|
||||||
|
GUEST_OS_TAG: fedora38
|
||||||
|
DEVICE_PORT: ETH_A
|
||||||
|
needs:
|
||||||
|
- job: build linux/fedora/38/amd64/clang
|
||||||
|
artifacts: true
|
||||||
|
|
||||||
|
hardware_test system-test-fedora38-vcan42:
|
||||||
|
<<: *hw_test
|
||||||
|
variables:
|
||||||
|
GUEST_OS_TAG: fedora38
|
||||||
|
DEVICE_PORT: USB_D
|
||||||
|
needs:
|
||||||
|
- job: build linux/fedora/38/amd64/clang
|
||||||
|
artifacts: true
|
||||||
|
|
||||||
|
hardware_test system-test-ubuntu2204-red2:
|
||||||
|
<<: *hw_test
|
||||||
|
variables:
|
||||||
|
GUEST_OS_TAG: ubuntu22.04
|
||||||
|
DEVICE_PORT: ETH_A
|
||||||
|
needs:
|
||||||
|
- job: build linux/ubuntu/2204/amd64/clang
|
||||||
|
artifacts: true
|
||||||
|
|
||||||
|
hardware_test system-test-ubuntu2204-vcan42:
|
||||||
|
<<: *hw_test
|
||||||
|
variables:
|
||||||
|
GUEST_OS_TAG: ubuntu22.04
|
||||||
|
DEVICE_PORT: USB_D
|
||||||
|
needs:
|
||||||
|
- job: build linux/ubuntu/2204/amd64/clang
|
||||||
|
artifacts: true
|
||||||
|
|
||||||
|
hardware_test system-test-win10-red2:
|
||||||
|
<<: *hw_test
|
||||||
|
variables:
|
||||||
|
GUEST_OS_TAG: win10
|
||||||
|
DEVICE_PORT: ETH_A
|
||||||
|
needs:
|
||||||
|
- job: build windows/x64
|
||||||
|
artifacts: true
|
||||||
|
|
||||||
|
hardware_test system-test-win10-vcan42:
|
||||||
|
<<: *hw_test
|
||||||
|
variables:
|
||||||
|
GUEST_OS_TAG: win10
|
||||||
|
DEVICE_PORT: USB_D
|
||||||
|
needs:
|
||||||
|
- job: build windows/x64
|
||||||
|
artifacts: true
|
||||||
+202
-49
@@ -1,12 +1,19 @@
|
|||||||
cmake_minimum_required(VERSION 3.2)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
project(libicsneo VERSION 0.3.0)
|
project(libicsneo VERSION 0.3.0)
|
||||||
|
|
||||||
option(LIBICSNEO_BUILD_TESTS "Build all tests." OFF)
|
cmake_policy(SET CMP0074 NEW)
|
||||||
|
if(POLICY CMP0135)
|
||||||
|
cmake_policy(SET CMP0135 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(LIBICSNEO_BUILD_UNIT_TESTS "Build unit tests." OFF)
|
||||||
|
option(LIBICSNEO_BUILD_SYSTEM_TESTS "Build system tests." OFF)
|
||||||
option(LIBICSNEO_BUILD_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)
|
||||||
option(LIBICSNEO_BUILD_ICSNEOC "Build dynamic C library" ON)
|
option(LIBICSNEO_BUILD_ICSNEOC "Build dynamic C library" ON)
|
||||||
option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
|
option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
|
||||||
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
|
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
|
||||||
|
option(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC "Build static icsnVC40 compatibility library" ON)
|
||||||
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
|
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
|
||||||
|
|
||||||
# Device Drivers
|
# Device Drivers
|
||||||
@@ -17,9 +24,11 @@ option(LIBICSNEO_ENABLE_FIRMIO "Enable communication between Linux and CoreMini
|
|||||||
option(LIBICSNEO_ENABLE_RAW_ETHERNET "Enable devices which communicate over raw ethernet" ON)
|
option(LIBICSNEO_ENABLE_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_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,10 +43,15 @@ 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()
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
# doxygen
|
# doxygen
|
||||||
find_package(Doxygen)
|
find_package(Doxygen)
|
||||||
if(DOXYGEN_FOUND)
|
if(DOXYGEN_FOUND)
|
||||||
@@ -93,7 +107,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
|
||||||
)
|
)
|
||||||
@@ -154,6 +168,60 @@ else() # Darwin or Linux
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(LIBICSNEO_ENABLE_FTD3XX)
|
||||||
|
if(NOT FTD3XX_ROOT) # allow system override
|
||||||
|
include(FetchContent)
|
||||||
|
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-x64.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ee1289cdd5023de67275aaa1712e8e32e73e825a8392efb0a76f161e357fbdc9")
|
||||||
|
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-i686.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=bcd31f5c3cb39ecb9b09db5ce722bd79de6e0f813130e7b8f2c86879a60d8ff0")
|
||||||
|
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-macos-universal2.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=74c0d35f04242d0841532c6325eb2932b8f627e3c395382a15d9f39974d27a90")
|
||||||
|
elseif(UNIX)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-x64.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=a9dc7eb6948c8977fbd79f6700bec6f882d3da5667aea8f2175b8d1f6f08e456")
|
||||||
|
endif()
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-aarch64.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=da4b90ea1cbb905874cd159ad2ab8c1bdde65cc22b3aa55bf2b5fd85ca6efd22")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||||
|
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-armhf.zip")
|
||||||
|
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=d813008117422cae958f7c71a065cdac0d31dca3b24809d3ab5e13604a9c3fb1")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOT LIBICSNEO_FTD3XX_URL)
|
||||||
|
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
|
||||||
|
endif()
|
||||||
|
FetchContent_Declare(
|
||||||
|
ftdi3xx
|
||||||
|
URL ${LIBICSNEO_FTD3XX_URL}
|
||||||
|
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
|
||||||
|
)
|
||||||
|
FetchContent_GetProperties(ftdi3xx)
|
||||||
|
if(NOT ftdi3xx_POPULATED)
|
||||||
|
FetchContent_Populate(ftdi3xx)
|
||||||
|
endif()
|
||||||
|
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
|
find_package(FTD3XX REQUIRED)
|
||||||
|
list(APPEND PLATFORM_SRC
|
||||||
|
platform/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 +236,31 @@ 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/a2bmessage.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/message/tc10statusmessage.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/packet/hardwareinfopacket.cpp
|
||||||
communication/decoder.cpp
|
communication/decoder.cpp
|
||||||
communication/encoder.cpp
|
communication/encoder.cpp
|
||||||
communication/ethernetpacketizer.cpp
|
communication/ethernetpacketizer.cpp
|
||||||
@@ -184,6 +268,8 @@ set(SRC_FILES
|
|||||||
communication/multichannelcommunication.cpp
|
communication/multichannelcommunication.cpp
|
||||||
communication/communication.cpp
|
communication/communication.cpp
|
||||||
communication/driver.cpp
|
communication/driver.cpp
|
||||||
|
communication/livedata.cpp
|
||||||
|
communication/ringbuffer.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
|
||||||
@@ -197,6 +283,22 @@ set(SRC_FILES
|
|||||||
disk/plasiondiskreaddriver.cpp
|
disk/plasiondiskreaddriver.cpp
|
||||||
disk/extextractordiskreaddriver.cpp
|
disk/extextractordiskreaddriver.cpp
|
||||||
disk/fat.cpp
|
disk/fat.cpp
|
||||||
|
disk/vsa/vsa.cpp
|
||||||
|
disk/vsa/vsa02.cpp
|
||||||
|
disk/vsa/vsa03.cpp
|
||||||
|
disk/vsa/vsa04.cpp
|
||||||
|
disk/vsa/vsa05.cpp
|
||||||
|
disk/vsa/vsa06.cpp
|
||||||
|
disk/vsa/vsa07.cpp
|
||||||
|
disk/vsa/vsa08.cpp
|
||||||
|
disk/vsa/vsa09.cpp
|
||||||
|
disk/vsa/vsa0b.cpp
|
||||||
|
disk/vsa/vsa0c.cpp
|
||||||
|
disk/vsa/vsa0d.cpp
|
||||||
|
disk/vsa/vsa0e.cpp
|
||||||
|
disk/vsa/vsa0f.cpp
|
||||||
|
disk/vsa/vsa6a.cpp
|
||||||
|
disk/vsa/vsaparser.cpp
|
||||||
${PLATFORM_SRC}
|
${PLATFORM_SRC}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -257,6 +359,7 @@ target_include_directories(icsneocpp
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
|
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
|
||||||
)
|
)
|
||||||
|
target_link_libraries(icsneocpp PUBLIC Threads::Threads)
|
||||||
set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
|
set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||||
target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||||
message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
|
message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
|
||||||
@@ -273,39 +376,58 @@ 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 iphlpapi)
|
||||||
|
endif()
|
||||||
|
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
|
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||||
if(WIN32)
|
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
|
||||||
if(LIBICSNEO_NPCAP_INCLUDE_DIR STREQUAL "")
|
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||||
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
|
endif(NOT WIN32)
|
||||||
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
|
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)
|
||||||
@@ -331,6 +453,7 @@ if(LIBICSNEO_BUILD_ICSNEOC_STATIC)
|
|||||||
)
|
)
|
||||||
target_link_libraries(icsneoc-static PUBLIC icsneocpp)
|
target_link_libraries(icsneoc-static PUBLIC icsneocpp)
|
||||||
target_compile_features(icsneoc-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
target_compile_features(icsneoc-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||||
|
target_compile_definitions(icsneoc-static PUBLIC ICSNEOC_BUILD_STATIC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(LIBICSNEO_BUILD_ICSNEOLEGACY)
|
if(LIBICSNEO_BUILD_ICSNEOLEGACY)
|
||||||
@@ -351,19 +474,27 @@ 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(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC)
|
||||||
if(NOT WIN32)
|
add_library(icsneolegacy-static STATIC
|
||||||
find_package(Threads)
|
api/icsneolegacy/icsneolegacy.cpp
|
||||||
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
api/icsneolegacy/icsneolegacyextra.cpp
|
||||||
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
|
api/icsneoc/icsneoc.cpp
|
||||||
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
)
|
||||||
find_package(PCAP REQUIRED)
|
|
||||||
target_include_directories(icsneocpp PUBLIC ${PCAP_INCLUDE_DIR})
|
target_include_directories(icsneolegacy-static
|
||||||
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
|
PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:>
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
target_link_libraries(icsneolegacy-static PUBLIC icsneocpp)
|
||||||
|
target_compile_features(icsneolegacy-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||||
|
target_compile_definitions(icsneolegacy-static PUBLIC ICSNEOC_BUILD_STATIC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# googletest
|
# googletest
|
||||||
if(LIBICSNEO_BUILD_TESTS)
|
if(LIBICSNEO_BUILD_UNIT_TESTS)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
@@ -376,22 +507,44 @@ if(LIBICSNEO_BUILD_TESTS)
|
|||||||
include_directories("${gtest_SOURCE_DIR}/include")
|
include_directories("${gtest_SOURCE_DIR}/include")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(libicsneo-tests
|
add_executable(libicsneo-unit-tests
|
||||||
test/main.cpp
|
test/unit/main.cpp
|
||||||
test/diskdriverreadtest.cpp
|
test/unit/diskdriverreadtest.cpp
|
||||||
test/diskdriverwritetest.cpp
|
test/unit/diskdriverwritetest.cpp
|
||||||
test/eventmanagertest.cpp
|
test/unit/eventmanagertest.cpp
|
||||||
test/ethernetpacketizertest.cpp
|
test/unit/ethernetpacketizertest.cpp
|
||||||
|
test/unit/i2cencoderdecodertest.cpp
|
||||||
|
test/unit/linencoderdecodertest.cpp
|
||||||
|
test/unit/a2bencoderdecodertest.cpp
|
||||||
|
test/unit/mdioencoderdecodertest.cpp
|
||||||
|
test/unit/livedataencoderdecodertest.cpp
|
||||||
|
test/unit/ringbuffertest.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(libicsneo-tests gtest gtest_main)
|
target_link_libraries(libicsneo-unit-tests gtest gtest_main)
|
||||||
target_link_libraries(libicsneo-tests icsneocpp)
|
target_link_libraries(libicsneo-unit-tests icsneocpp)
|
||||||
|
|
||||||
target_include_directories(libicsneo-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
target_include_directories(libicsneo-unit-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
add_test(NAME libicsneo-test-suite COMMAND libicsneo-tests)
|
add_test(NAME libicsneo-unit-test-suite COMMAND libicsneo-unit-tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(LIBICSNEO_BUILD_SYSTEM_TESTS)
|
||||||
|
if(DEFINED ENV{LIBICSNEO_SYSTEM_TESTS})
|
||||||
|
include(FetchContent)
|
||||||
|
file(MAKE_DIRECTORY test/system)
|
||||||
|
FetchContent_Declare(
|
||||||
|
SystemTests
|
||||||
|
GIT_REPOSITORY $ENV{LIBICSNEO_SYSTEM_TESTS}
|
||||||
|
GIT_TAG main
|
||||||
|
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test/system
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(SystemTests)
|
||||||
|
else()
|
||||||
|
message("System test repo not defined!")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||||
|
|||||||
+21
@@ -14,6 +14,20 @@
|
|||||||
- 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
|
||||||
|
- RADComet3
|
||||||
|
- CAN works
|
||||||
|
- Ethernet works
|
||||||
|
- neoVI Connect
|
||||||
|
- CAN works
|
||||||
|
- CAN FD works
|
||||||
|
- Ethernet works
|
||||||
|
|
||||||
- Connecting over USB
|
- Connecting over USB
|
||||||
- ValueCAN 4 series
|
- ValueCAN 4 series
|
||||||
@@ -37,3 +51,10 @@
|
|||||||
- CAN works
|
- CAN works
|
||||||
- neoVI ION
|
- neoVI ION
|
||||||
- CAN works
|
- CAN works
|
||||||
|
- RADA2B
|
||||||
|
- CAN works
|
||||||
|
- Ethernet works
|
||||||
|
- RADMoon3
|
||||||
|
- RADComet3
|
||||||
|
- CAN works
|
||||||
|
- Ethernet works
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2018-2020 Intrepid Control Systems, Inc.
|
Copyright 2018-2024 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
|
||||||
```
|
```
|
||||||
|
|||||||
+76
-3
@@ -144,8 +144,17 @@ bool icsneo_closeDevice(const neodevice_t* device) {
|
|||||||
if((*it).get() == device->device)
|
if((*it).get() == device->device)
|
||||||
itemsToDelete.push_back(it);
|
itemsToDelete.push_back(it);
|
||||||
}
|
}
|
||||||
for(auto it : itemsToDelete)
|
for(auto it : itemsToDelete) {
|
||||||
|
// Move it back into connectable devices so we can open it again.
|
||||||
|
// Without this we will be unable to use/reopen the device due to
|
||||||
|
// icsneo_isValidNeoDevice / icsneo_openDevice checks against this
|
||||||
|
// container. Since its closed we are in a connectable state again.
|
||||||
|
// Notice: When we search again this will be cleaned up by
|
||||||
|
// icsneo_freeUnconnectedDevices()
|
||||||
|
connectableFoundDevices.push_back(*it);
|
||||||
|
// Remove it from the connected devices as we are no longer connected.
|
||||||
connectedDevices.erase(it);
|
connectedDevices.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -251,7 +260,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 +495,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 +656,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 +698,62 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool icsneo_isOnlineSupported(const neodevice_t* device) {
|
||||||
|
if(!icsneo_isValidNeoDevice(device))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return device->device->isOnlineSupported();
|
||||||
|
}
|
||||||
|
|||||||
@@ -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-2024"
|
||||||
VALUE "OriginalFilename", "icsneoc.dll"
|
VALUE "OriginalFilename", "icsneoc.dll"
|
||||||
VALUE "ProductName", "libicsneo"
|
VALUE "ProductName", "libicsneo"
|
||||||
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
||||||
|
|||||||
+176
-1
@@ -71,6 +71,9 @@ 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.";
|
||||||
|
static constexpr const char* RESTRICTED_ENTRY_FLAG = "Attempted to set a restricted flag in a Root Directory entry.";
|
||||||
|
static constexpr const char* NOT_SUPPORTED = "The requested feature is not supported.";
|
||||||
|
|
||||||
// Device Errors
|
// 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 +112,14 @@ 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.";
|
||||||
|
static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device.";
|
||||||
|
static constexpr const char* MODE_NOT_FOUND = "The mode was not found.";
|
||||||
|
|
||||||
// Transport Errors
|
// 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 +132,55 @@ 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.";
|
||||||
|
|
||||||
|
// VSA
|
||||||
|
static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted.";
|
||||||
|
static constexpr const char* VSA_TIMESTAMP_NOT_FOUND = "Unable to find a VSA record with a valid timestamp.";
|
||||||
|
static constexpr const char* VSA_BUFFER_FORMAT_ERROR = "VSA record buffer is formatted incorrectly.";
|
||||||
|
static constexpr const char* VSA_MAX_READ_ATTEMPTS_REACHED = "Reached max attempts to read VSA records before exit.";
|
||||||
|
static constexpr const char* VSA_BYTE_PARSE_FAILURE = "Failure to parse record bytes from VSA buffer.";
|
||||||
|
static constexpr const char* VSA_EXTENDED_MESSAGE_ERROR = "Failure to parse extended message record sequence";
|
||||||
|
static constexpr const char* VSA_OTHER_ERROR = "Unknown error in VSA read API.";
|
||||||
|
|
||||||
static constexpr const char* 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 +218,12 @@ 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;
|
||||||
|
case Type::RestrictedEntryFlag:
|
||||||
|
return RESTRICTED_ENTRY_FLAG;
|
||||||
|
case Type::NotSupported:
|
||||||
|
return NOT_SUPPORTED;
|
||||||
|
|
||||||
// Device Errors
|
// Device Errors
|
||||||
case Type::PollingMessageOverflow:
|
case Type::PollingMessageOverflow:
|
||||||
@@ -232,7 +298,22 @@ 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;
|
||||||
|
case Type::LINSettingsNotAvailable:
|
||||||
|
return LIN_SETTINGS_NOT_AVAILABLE;
|
||||||
|
case Type::ModeNotFound:
|
||||||
|
return MODE_NOT_FOUND;
|
||||||
// Transport Errors
|
// Transport Errors
|
||||||
case Type::FailedToRead:
|
case Type::FailedToRead:
|
||||||
return FAILED_TO_READ;
|
return FAILED_TO_READ;
|
||||||
@@ -254,6 +335,100 @@ 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;
|
||||||
|
|
||||||
|
// VSA
|
||||||
|
case Type::VSABufferCorrupted:
|
||||||
|
return VSA_BUFFER_CORRUPTED;
|
||||||
|
case Type::VSATimestampNotFound:
|
||||||
|
return VSA_TIMESTAMP_NOT_FOUND;
|
||||||
|
case Type::VSABufferFormatError:
|
||||||
|
return VSA_BUFFER_FORMAT_ERROR;
|
||||||
|
case Type::VSAMaxReadAttemptsReached:
|
||||||
|
return VSA_MAX_READ_ATTEMPTS_REACHED;
|
||||||
|
case Type::VSAByteParseFailure:
|
||||||
|
return VSA_BYTE_PARSE_FAILURE;
|
||||||
|
case Type::VSAExtendedMessageError:
|
||||||
|
return VSA_EXTENDED_MESSAGE_ERROR;
|
||||||
|
case Type::VSAOtherError:
|
||||||
|
return VSA_OTHER_ERROR;
|
||||||
|
|
||||||
// Other Errors
|
// Other Errors
|
||||||
case Type::TooManyEvents:
|
case Type::TooManyEvents:
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
#include <Tchar.h>
|
#include <Tchar.h>
|
||||||
|
|
||||||
//Basic Functions
|
//Basic Functions
|
||||||
FINDNEODEVICES icsneoFindNeoDevices;
|
|
||||||
OPENNEODEVICE icsneoOpenNeoDevice;
|
|
||||||
OPENDEVICE icsneoOpenDevice;
|
OPENDEVICE icsneoOpenDevice;
|
||||||
CLOSEPORT icsneoClosePort;
|
CLOSEPORT icsneoClosePort;
|
||||||
FREEOBJECT icsneoFreeObject;
|
FREEOBJECT icsneoFreeObject;
|
||||||
@@ -134,10 +132,6 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
|
|||||||
if((hAPIDLL = LoadLibrary(_T("icsneo40.dll"))) == NULL)
|
if((hAPIDLL = LoadLibrary(_T("icsneo40.dll"))) == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
icsneoFindNeoDevices = (FINDNEODEVICES) GetProcAddress(hAPIDLL, "icsneoFindNeoDevices");
|
|
||||||
icsneoOpenNeoDevice = (OPENNEODEVICE) GetProcAddress(hAPIDLL, "icsneoOpenNeoDevice");
|
|
||||||
icsneoOpenDevice = (OPENDEVICE) GetProcAddress(hAPIDLL, "icsneoOpenDevice");
|
icsneoOpenDevice = (OPENDEVICE) GetProcAddress(hAPIDLL, "icsneoOpenDevice");
|
||||||
icsneoClosePort = (CLOSEPORT) GetProcAddress(hAPIDLL, "icsneoClosePort");
|
icsneoClosePort = (CLOSEPORT) GetProcAddress(hAPIDLL, "icsneoClosePort");
|
||||||
icsneoFreeObject = (FREEOBJECT) GetProcAddress(hAPIDLL, "icsneoFreeObject");
|
icsneoFreeObject = (FREEOBJECT) GetProcAddress(hAPIDLL, "icsneoFreeObject");
|
||||||
@@ -206,7 +200,7 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
|
|||||||
|
|
||||||
icsneoEnableDOIPLine = (ENABLEDOIPACTIVATIONLINE)GetProcAddress(hAPIDLL, "icsneoEnableDOIPLine");
|
icsneoEnableDOIPLine = (ENABLEDOIPACTIVATIONLINE)GetProcAddress(hAPIDLL, "icsneoEnableDOIPLine");
|
||||||
|
|
||||||
if(!icsneoFindNeoDevices || !icsneoOpenNeoDevice || !icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
|
if(!icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
|
||||||
!icsneoTxMessages || !icsneoGetMessages || !icsneoWaitForRxMessagesWithTimeOut ||
|
!icsneoTxMessages || !icsneoGetMessages || !icsneoWaitForRxMessagesWithTimeOut ||
|
||||||
!icsneoGetTimeStampForMsg || !icsneoEnableNetworkRXQueue || !icsneoGetISO15765Status || !icsneoTxMessagesEx ||
|
!icsneoGetTimeStampForMsg || !icsneoEnableNetworkRXQueue || !icsneoGetISO15765Status || !icsneoTxMessagesEx ||
|
||||||
!icsneoSetISO15765RxParameters || !icsneoGetConfiguration || !icsneoSendConfiguration ||
|
!icsneoSetISO15765RxParameters || !icsneoGetConfiguration || !icsneoSendConfiguration ||
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ typedef int (__stdcall *SCRIPTWRITEISO15765TXMESSAGE)(void * hObject, unsigned
|
|||||||
|
|
||||||
|
|
||||||
//Basic Functions
|
//Basic Functions
|
||||||
extern FINDNEODEVICES icsneoFindNeoDevices;
|
|
||||||
extern OPENNEODEVICE icsneoOpenNeoDevice;
|
|
||||||
extern OPENDEVICE icsneoOpenDevice;
|
extern OPENDEVICE icsneoOpenDevice;
|
||||||
extern CLOSEPORT icsneoClosePort;
|
extern CLOSEPORT icsneoClosePort;
|
||||||
extern FREEOBJECT icsneoFreeObject;
|
extern FREEOBJECT icsneoFreeObject;
|
||||||
|
|||||||
+357
-172
@@ -14,18 +14,19 @@
|
|||||||
#include "icsneo/communication/network.h"
|
#include "icsneo/communication/network.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
|
||||||
#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;
|
||||||
|
|
||||||
typedef uint64_t legacymaphandle_t;
|
typedef uint64_t legacymaphandle_t;
|
||||||
static std::map<legacymaphandle_t, neodevice_t> neodevices;
|
static std::map<legacymaphandle_t, neodevice_t> neodevices;
|
||||||
|
static std::map<neodevice_t*, NeoDeviceEx*> openneodevices;
|
||||||
|
|
||||||
static const std::map<size_t, size_t> mp_netIDToVnetOffSet = {
|
static const std::map<size_t, size_t> mp_netIDToVnetOffSet = {
|
||||||
{NETID_HSCAN, 1},
|
{NETID_HSCAN, 1},
|
||||||
@@ -51,18 +52,6 @@ static const std::map<size_t, size_t> mp_HWnetIDToCMnetID = {
|
|||||||
|
|
||||||
static unsigned long vnet_table[] = {0, PLASMA_SLAVE1_OFFSET, PLASMA_SLAVE2_OFFSET};
|
static unsigned long vnet_table[] = {0, PLASMA_SLAVE1_OFFSET, PLASMA_SLAVE2_OFFSET};
|
||||||
|
|
||||||
static NeoDevice OldNeoDeviceFromNew(const neodevice_t* newnd)
|
|
||||||
{
|
|
||||||
NeoDevice oldnd = {0};
|
|
||||||
oldnd.DeviceType = newnd->type;
|
|
||||||
oldnd.SerialNumber = icsneo_serialStringToNum(newnd->serial);
|
|
||||||
oldnd.NumberOfClients = 0;
|
|
||||||
oldnd.MaxAllowedClients = 1;
|
|
||||||
static_assert(sizeof(neodevice_handle_t) == sizeof(oldnd.Handle),
|
|
||||||
"neodevice_handle_t size must be sizeof(int) for compatibility reasons");
|
|
||||||
oldnd.Handle = newnd->handle;
|
|
||||||
return oldnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t& newmsg, icsSpyMessage& oldmsg)
|
static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t& newmsg, icsSpyMessage& oldmsg)
|
||||||
{
|
{
|
||||||
@@ -73,6 +62,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 +89,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 +149,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));
|
||||||
@@ -200,109 +306,81 @@ static inline size_t GetVnetAgnosticNetid(size_t fullNetid)
|
|||||||
int LegacyDLLExport icsneoFindDevices(NeoDeviceEx* devs, int* devCount, unsigned int* devTypes, unsigned int devTypeCount,
|
int LegacyDLLExport icsneoFindDevices(NeoDeviceEx* devs, int* devCount, unsigned int* devTypes, unsigned int devTypeCount,
|
||||||
POptionsFindNeoEx* POptionsFindNeoEx, unsigned int* zero)
|
POptionsFindNeoEx* POptionsFindNeoEx, unsigned int* zero)
|
||||||
{
|
{
|
||||||
if (!devs || !devCount)
|
// Match the legacy API maximum, this derives from the maximum COM Port on old windows versions.
|
||||||
|
constexpr int MAX_NEO_DEVICES = 255;
|
||||||
|
|
||||||
|
// Validate arguments
|
||||||
|
if (!devCount && *devCount < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (*devCount < 0 || *devCount > 255)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Find the devices without filtering by the device type
|
|
||||||
// We allow this to find more than the requested number,
|
|
||||||
// as we may filter out some devices.
|
|
||||||
constexpr const size_t MAX_DEVICES = 255;
|
|
||||||
NeoDevice foundDevices[MAX_DEVICES];
|
|
||||||
int NumDevices = MAX_DEVICES;
|
|
||||||
|
|
||||||
int filteredDeviceCount = 0;
|
|
||||||
|
|
||||||
if (!icsneoFindNeoDevices(0, foundDevices, &NumDevices))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (auto i = 0; i < NumDevices; i++)
|
|
||||||
{
|
|
||||||
// Check if the next device would overrun the user's buffer
|
|
||||||
// We check this up here since the documentation allows zero
|
|
||||||
// to be specified.
|
|
||||||
if (filteredDeviceCount >= *devCount)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (devTypes && devTypeCount)
|
|
||||||
{
|
|
||||||
for (unsigned int j = 0; j < devTypeCount; j++)
|
|
||||||
{
|
|
||||||
if (foundDevices[i].DeviceType == devTypes[j])
|
|
||||||
{
|
|
||||||
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// return the size only if devs is NULL.
|
||||||
*devCount = filteredDeviceCount;
|
if (!devs)
|
||||||
return 1; // If the function succeeds but no devices are found 1 will still be returned and devCount will equal 0
|
|
||||||
}
|
|
||||||
|
|
||||||
int LegacyDLLExport icsneoFindNeoDevices(unsigned long DeviceTypes, NeoDevice* pNeoDevice, int* pNumDevices)
|
|
||||||
{
|
|
||||||
constexpr size_t MAX_DEVICES = 255;
|
|
||||||
size_t count = MAX_DEVICES;
|
|
||||||
|
|
||||||
if (pNumDevices == nullptr)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (pNeoDevice == nullptr)
|
|
||||||
{
|
{
|
||||||
icsneo_findAllDevices(nullptr, &count);
|
icsneo_findAllDevices(nullptr, (size_t*)devCount);
|
||||||
*pNumDevices = (int)count;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
// shrink the number of devices allowed to find
|
||||||
size_t bufferSize = (size_t)*pNumDevices;
|
if (*devCount > MAX_NEO_DEVICES) {
|
||||||
if (*pNumDevices < 0 || bufferSize > MAX_DEVICES)
|
*devCount = MAX_NEO_DEVICES;
|
||||||
return 0;
|
|
||||||
|
|
||||||
neodevice_t devices[MAX_DEVICES];
|
|
||||||
icsneo_findAllDevices(devices, &count);
|
|
||||||
if (bufferSize < count)
|
|
||||||
count = bufferSize;
|
|
||||||
*pNumDevices = (int)count;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
pNeoDevice[i] = OldNeoDeviceFromNew(&devices[i]); // Write out into user memory
|
|
||||||
neodevices[uint64_t(devices[i].handle) << 32 | icsneo_serialStringToNum(devices[i].serial)] = devices[i]; // Fill the look up table
|
|
||||||
}
|
}
|
||||||
|
// Find all the neodevice_t devices
|
||||||
|
std::vector<neodevice_t> neoDevices(*devCount);
|
||||||
|
auto neoDevicesSize = neoDevices.size();
|
||||||
|
icsneo_findAllDevices(neoDevices.data(), &neoDevicesSize);
|
||||||
|
neoDevices.resize(neoDevicesSize);
|
||||||
|
// Filter out the devices if needed
|
||||||
|
// No filtering needed
|
||||||
|
if (devTypes && devTypeCount > 0) {
|
||||||
|
neoDevices.erase(
|
||||||
|
std::remove_if(
|
||||||
|
neoDevices.begin(),
|
||||||
|
neoDevices.end(),
|
||||||
|
[&](const auto& iter) {
|
||||||
|
for (unsigned int i=0; i < devTypeCount; ++i) {
|
||||||
|
if (iter.type == devTypes[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
neoDevices.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Create a NeoDeviceEx From a neodevice_t
|
||||||
|
auto _createNeoDeviceExFrom = [](const neodevice_t* neoDevice) -> NeoDeviceEx {
|
||||||
|
NeoDeviceEx nde = {};
|
||||||
|
nde.neoDevice.DeviceType = neoDevice->type;
|
||||||
|
nde.neoDevice.SerialNumber = icsneo_serialStringToNum(neoDevice->serial);
|
||||||
|
nde.neoDevice.NumberOfClients = 0;
|
||||||
|
nde.neoDevice.MaxAllowedClients = 1;
|
||||||
|
static_assert(sizeof(neodevice_handle_t) == sizeof(nde.neoDevice.Handle),
|
||||||
|
"neodevice_handle_t size must be sizeof(int) for compatibility reasons");
|
||||||
|
nde.neoDevice.Handle = neoDevice->handle;
|
||||||
|
return nde;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the NeoDeviceEx from the neodevice_t
|
||||||
|
auto i = 0;
|
||||||
|
for (const auto& neoDevice : neoDevices) {
|
||||||
|
// Fill the look up table
|
||||||
|
neodevices[uint64_t(neoDevice.handle) << 32 | icsneo_serialStringToNum(neoDevice.serial)] = neoDevice;
|
||||||
|
// Create the NeoDeviceEx
|
||||||
|
devs[i] = _createNeoDeviceExFrom(&neoDevice);
|
||||||
|
NeoDeviceEx* nde = &devs[i];
|
||||||
|
++i;
|
||||||
|
// Lookup the open NeoDeviceEx devices and match the NumberOfClients value if available.
|
||||||
|
for (auto& [neo_device, open_nde]: openneodevices) {
|
||||||
|
// SerialNumber should always be unique so lets compare against that.
|
||||||
|
if (nde->neoDevice.SerialNumber == open_nde->neoDevice.SerialNumber) {
|
||||||
|
nde->neoDevice.NumberOfClients = open_nde->neoDevice.NumberOfClients;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*devCount = (int)neoDevices.size();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LegacyDLLExport icsneoOpenNeoDevice(NeoDevice* pNeoDevice, void** hObject, unsigned char* bNetworkIDs, int bConfigRead, int bSyncToPC)
|
|
||||||
{
|
|
||||||
if (pNeoDevice == nullptr || hObject == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
neodevice_t *device;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
device = &neodevices.at(uint64_t(pNeoDevice->Handle) << 32 | pNeoDevice->SerialNumber);
|
|
||||||
}
|
|
||||||
catch (const std::out_of_range&)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*hObject = device;
|
|
||||||
if (!icsneo_openDevice(device))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
int LegacyDLLExport icsneoOpenDevice(
|
int LegacyDLLExport icsneoOpenDevice(
|
||||||
NeoDeviceEx* pNeoDeviceEx,
|
NeoDeviceEx* pNeoDeviceEx,
|
||||||
void** hObject,
|
void** hObject,
|
||||||
@@ -325,19 +403,50 @@ int LegacyDLLExport icsneoOpenDevice(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*hObject = device;
|
if (pNeoDeviceEx->neoDevice.NumberOfClients >= pNeoDeviceEx->neoDevice.MaxAllowedClients) {
|
||||||
if(!icsneo_openDevice(device))
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
|
*hObject = device;
|
||||||
|
if(!icsneo_openDevice(device)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icsneo_isOnlineSupported(device)) {
|
||||||
|
if (!icsneo_setPollingMessageLimit(device, 20000)) {
|
||||||
|
icsneo_closeDevice(device);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!icsneo_enableMessagePolling(device)) {
|
||||||
|
icsneo_closeDevice(device);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!icsneo_goOnline(device)) {
|
||||||
|
icsneo_closeDevice(device);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pNeoDeviceEx->neoDevice.NumberOfClients = 1;
|
||||||
|
// Add the open NeoDevice to the container so we can decrement NumberOfClients on close
|
||||||
|
openneodevices[device] = pNeoDeviceEx;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LegacyDLLExport icsneoClosePort(void* hObject, int* pNumberOfErrors)
|
int LegacyDLLExport icsneoClosePort(void* hObject, int* pNumberOfErrors)
|
||||||
{
|
{
|
||||||
if (!icsneoValidateHObject(hObject))
|
if (!icsneoValidateHObject(hObject))
|
||||||
return false;
|
return false;
|
||||||
|
if (pNumberOfErrors) {
|
||||||
|
*pNumberOfErrors = 0;
|
||||||
|
}
|
||||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||||
|
if (openneodevices.find(device) != openneodevices.end()) {
|
||||||
|
openneodevices[device]->neoDevice.NumberOfClients -= 1;
|
||||||
|
openneodevices.erase(device);
|
||||||
|
}
|
||||||
return icsneo_closeDevice(device);
|
return icsneo_closeDevice(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,25 +505,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 +557,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 +779,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 +794,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 +909,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)
|
||||||
{
|
{
|
||||||
@@ -792,20 +982,7 @@ int LegacyDLLExport icsneoScriptWriteAppSignal(void* hObject, unsigned int iInde
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Deprecated (but still suppored in the DLL)
|
|
||||||
int LegacyDLLExport icsneoOpenPortEx(void* lPortNumber, int lPortType, int lDriverType, int lIPAddressMSB,
|
|
||||||
int lIPAddressLSBOrBaudRate, int bConfigRead, unsigned char* bNetworkID, int* hObject)
|
|
||||||
{
|
|
||||||
// TODO Implement
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LegacyDLLExport icsneoOpenPort(int lPortNumber, int lPortType, int lDriverType, unsigned char *bNetworkID,
|
|
||||||
unsigned char* bSCPIDs, int* hObject)
|
|
||||||
{
|
|
||||||
// TODO Implement
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LegacyDLLExport icsneoEnableNetworkCom(void* hObject, int Enable)
|
int LegacyDLLExport icsneoEnableNetworkCom(void* hObject, int Enable)
|
||||||
{
|
{
|
||||||
@@ -819,19 +996,6 @@ int LegacyDLLExport icsneoEnableNetworkCom(void* hObject, int Enable)
|
|||||||
return icsneo_goOffline(device);
|
return icsneo_goOffline(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
int LegacyDLLExport icsneoFindAllCOMDevices(int lDriverType, int lGetSerialNumbers, int lStopAtFirst, int lUSBCommOnly,
|
|
||||||
int* p_lDeviceTypes, int* p_lComPorts, int* p_lSerialNumbers, int* lNumDevices)
|
|
||||||
{
|
|
||||||
// TODO Implement
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LegacyDLLExport icsneoOpenNeoDeviceByChannels(NeoDevice* pNeoDevice, void** hObject, unsigned char* uChannels, int iSize,
|
|
||||||
int bConfigRead, int iOptions)
|
|
||||||
{
|
|
||||||
// TODO Implement
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LegacyDLLExport icsneoGetVCAN4Settings(void* hObject, SVCAN4Settings* pSettings, int iNumBytes)
|
int LegacyDLLExport icsneoGetVCAN4Settings(void* hObject, SVCAN4Settings* pSettings, int iNumBytes)
|
||||||
{
|
{
|
||||||
@@ -910,6 +1074,18 @@ 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;
|
||||||
|
case NEODEVICE_FIRE3:
|
||||||
|
*pDeviceSettingsType = DeviceFire3SettingsType;
|
||||||
|
break;
|
||||||
|
case NEODEVICE_FIRE3_FLEXRAY:
|
||||||
|
*pDeviceSettingsType = DeviceFire3FlexraySettingsType;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -947,6 +1123,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 +1472,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_UNIT_TESTS=ON -DLIBICSNEO_BUILD_SYSTEM_TESTS=ON -DLIBICSNEO_ENABLE_TCP=OFF || 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_UNIT_TESTS=ON -DLIBICSNEO_BUILD_SYSTEM_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,15 +13,15 @@
|
|||||||
#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;
|
||||||
|
|
||||||
int Communication::messageCallbackIDCounter = 1;
|
int Communication::messageCallbackIDCounter = 1;
|
||||||
|
|
||||||
Communication::~Communication() {
|
Communication::~Communication() {
|
||||||
if(redirectingRead)
|
if(isOpen())
|
||||||
clearRedirectRead();
|
close();
|
||||||
close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Communication::open() {
|
bool Communication::open() {
|
||||||
@@ -42,6 +42,11 @@ void Communication::spawnThreads() {
|
|||||||
|
|
||||||
void Communication::joinThreads() {
|
void Communication::joinThreads() {
|
||||||
closing = true;
|
closing = true;
|
||||||
|
|
||||||
|
if(pauseReadTask) {
|
||||||
|
resumeReads();
|
||||||
|
}
|
||||||
|
|
||||||
if(readTaskThread.joinable())
|
if(readTaskThread.joinable())
|
||||||
readTaskThread.join();
|
readTaskThread.join();
|
||||||
closing = false;
|
closing = false;
|
||||||
@@ -94,23 +99,6 @@ bool Communication::sendCommand(ExtendedCommand cmd, std::vector<uint8_t> argume
|
|||||||
return sendCommand(Command::Extended, arguments);
|
return sendCommand(Command::Extended, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Communication::redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo) {
|
|
||||||
if(redirectingRead)
|
|
||||||
return false;
|
|
||||||
redirectionFn = redirectTo;
|
|
||||||
redirectingRead = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Communication::clearRedirectRead() {
|
|
||||||
if(!redirectingRead)
|
|
||||||
return;
|
|
||||||
// The mutex is required to clear the redirection, but not to set it
|
|
||||||
std::lock_guard<std::mutex> lk(redirectingReadMutex);
|
|
||||||
redirectingRead = false;
|
|
||||||
redirectionFn = std::function<void(std::vector<uint8_t>&&)>();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Communication::getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout) {
|
bool Communication::getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout) {
|
||||||
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::ReadSettings);
|
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::ReadSettings);
|
||||||
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
||||||
@@ -150,22 +138,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 +181,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 +200,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 +217,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,51 +240,77 @@ 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)
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Communication::readTask() {
|
void Communication::pauseReads() {
|
||||||
std::vector<uint8_t> readBytes;
|
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
|
||||||
|
pauseReadTask = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Communication::resumeReads() {
|
||||||
|
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
|
||||||
|
if(!pauseReadTask) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pauseReadTask = false;
|
||||||
|
lk.unlock();
|
||||||
|
|
||||||
|
pauseReadTaskCv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Communication::readsArePaused() {
|
||||||
|
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
|
||||||
|
return pauseReadTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Communication::readTask() {
|
||||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||||
|
|
||||||
while(!closing) {
|
while(!closing) {
|
||||||
readBytes.clear();
|
if(pauseReadTask) {
|
||||||
if(driver->readWait(readBytes)) {
|
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
|
||||||
handleInput(*packetizer, readBytes);
|
pauseReadTaskCv.wait(lk, [this]() { return !pauseReadTask; });
|
||||||
}
|
}
|
||||||
}
|
if(driver->readAvailable()) {
|
||||||
}
|
if(pauseReadTask) {
|
||||||
|
/**
|
||||||
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
|
* Reads could have paused while the driver was not available
|
||||||
if(redirectingRead) {
|
*/
|
||||||
// redirectingRead is an atomic so it can be set without acquiring a mutex
|
continue;
|
||||||
// However, we do not clear it without the mutex. The idea is that if another
|
|
||||||
// thread calls clearRedirectRead(), it will block until the redirectionFn
|
|
||||||
// finishes, and after that the redirectionFn will not be called again.
|
|
||||||
std::unique_lock<std::mutex> lk(redirectingReadMutex);
|
|
||||||
// So after we acquire the mutex, we need to check the atomic again, and
|
|
||||||
// if it has become cleared, we *can not* run the redirectionFn.
|
|
||||||
if(redirectingRead) {
|
|
||||||
redirectionFn(std::move(readBytes));
|
|
||||||
} else {
|
|
||||||
// The redirectionFn got cleared while we were acquiring the lock
|
|
||||||
lk.unlock(); // We don't need the lock anymore
|
|
||||||
handleInput(p, readBytes); // and we might as well process this input ourselves
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(p.input(readBytes)) {
|
|
||||||
for(const auto& packet : p.output()) {
|
|
||||||
std::shared_ptr<Message> msg;
|
|
||||||
if(!decoder->decode(msg, packet))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dispatchMessage(msg);
|
|
||||||
}
|
}
|
||||||
|
handleInput(*packetizer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Communication::handleInput(Packetizer& p) {
|
||||||
|
if(p.input(driver->getReadBuffer())) {
|
||||||
|
for(const auto& packet : p.output()) {
|
||||||
|
std::shared_ptr<Message> msg;
|
||||||
|
if(!decoder->decode(msg, packet))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dispatchMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional< std::vector<ComponentVersion> > Communication::getComponentVersionsSync(std::chrono::milliseconds timeout) {
|
||||||
|
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::ComponentVersions);
|
||||||
|
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
||||||
|
return sendCommand(ExtendedCommand::GetComponentVersions, {});
|
||||||
|
}, filter, timeout);
|
||||||
|
if(!msg) // Did not receive a message
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto ver = std::dynamic_pointer_cast<ComponentVersionsMessage>(msg);
|
||||||
|
if(!ver) // Could not upcast for some reason
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return std::make_optional< std::vector<ComponentVersion> >(std::move(ver->versions));
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,16 +5,40 @@
|
|||||||
#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/message/hardwareinfo.h"
|
||||||
|
#include "icsneo/communication/message/tc10statusmessage.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 "icsneo/communication/packet/hardwareinfopacket.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
@@ -115,6 +139,57 @@ 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;
|
||||||
|
msg.timestamp *= timestampResolution;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
result = HardwareLINPacket::DecodeToMessage(packet->data);
|
||||||
|
|
||||||
|
if(!result) {
|
||||||
|
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||||
|
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||||
|
}
|
||||||
|
|
||||||
|
LINMessage& msg = *static_cast<LINMessage*>(result.get());
|
||||||
|
msg.network = packet->network;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Network::Type::MDIO: {
|
||||||
|
result = HardwareMDIOPacket::DecodeToMessage(packet->data);
|
||||||
|
|
||||||
|
if(!result) {
|
||||||
|
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||||
|
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||||
|
}
|
||||||
|
|
||||||
|
MDIOMessage& msg = *static_cast<MDIOMessage*>(result.get());
|
||||||
|
msg.network = packet->network;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case Network::Type::Internal: {
|
case Network::Type::Internal: {
|
||||||
switch(packet->network.getNetID()) {
|
switch(packet->network.getNetID()) {
|
||||||
case Network::NetID::Reset_Status: {
|
case Network::NetID::Reset_Status: {
|
||||||
@@ -193,6 +268,60 @@ 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;
|
||||||
|
case ExtendedCommand::GetTC10Status:
|
||||||
|
result = TC10StatusMessage::DecodeToMessage(packet->data);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
// No defined handler, treat this as a RawMessage
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Network::NetID::ExtendedData: {
|
||||||
|
if(packet->data.size() < sizeof(ExtendedDataMessage::ExtendedDataHeader))
|
||||||
|
break;
|
||||||
|
const auto& header = *reinterpret_cast<ExtendedDataMessage::ExtendedDataHeader*>(packet->data.data());
|
||||||
|
|
||||||
|
switch(header.subCommand) {
|
||||||
|
case ExtendedDataSubCommand::GenericBinaryRead: {
|
||||||
|
result = std::make_shared<ExtendedDataMessage>(header);
|
||||||
|
auto extDataMsg = std::static_pointer_cast<ExtendedDataMessage>(result);
|
||||||
|
|
||||||
|
size_t numRead = std::min(ExtendedDataMessage::MaxExtendedDataBufferSize, (size_t)header.length);
|
||||||
|
extDataMsg->data.resize(numRead);
|
||||||
|
|
||||||
|
std::copy(packet->data.begin() + sizeof(header), packet->data.begin() + sizeof(header) + numRead, extDataMsg->data.begin());
|
||||||
|
|
||||||
|
extDataMsg->network = Network(static_cast<uint16_t>(Network::NetID::ExtendedData), false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Network::NetID::FlexRayControl: {
|
case Network::NetID::FlexRayControl: {
|
||||||
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
|
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
|
||||||
if(!frResult->decoded) {
|
if(!frResult->decoded) {
|
||||||
@@ -236,6 +365,16 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case Command::GetHardwareInfo: {
|
||||||
|
result = HardwareInfoPacket::DecodeToMessage(packet->data);
|
||||||
|
|
||||||
|
if(!result) {
|
||||||
|
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
auto msg = std::make_shared<Main51Message>();
|
auto msg = std::make_shared<Main51Message>();
|
||||||
msg->command = Command(packet->data[0]);
|
msg->command = Command(packet->data[0]);
|
||||||
@@ -286,6 +425,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,9 +441,22 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
-16
@@ -8,23 +8,31 @@
|
|||||||
|
|
||||||
using namespace icsneo;
|
using namespace icsneo;
|
||||||
|
|
||||||
bool Driver::read(std::vector<uint8_t>& bytes, size_t limit) {
|
bool Driver::pushRx(const uint8_t* buf, size_t numReceived) {
|
||||||
// A limit of zero indicates no limit
|
bool ret = readBuffer.write(buf, numReceived);
|
||||||
if(limit == 0)
|
|
||||||
limit = (size_t)-1;
|
|
||||||
|
|
||||||
if(limit > (readQueue.size_approx() + 4))
|
if(hasRxWaitRequest) {
|
||||||
limit = (readQueue.size_approx() + 4);
|
rxWaitRequestCv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
if(bytes.capacity() < limit)
|
return ret;
|
||||||
bytes.resize(limit);
|
}
|
||||||
|
|
||||||
size_t actuallyRead = readQueue.try_dequeue_bulk(bytes.data(), limit);
|
bool Driver::waitForRx(size_t limit, std::chrono::milliseconds timeout) {
|
||||||
|
return waitForRx([limit, this]() {
|
||||||
|
return readBuffer.size() >= limit;
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
if(bytes.size() > actuallyRead)
|
bool Driver::waitForRx(std::function<bool()> predicate, std::chrono::milliseconds timeout) {
|
||||||
bytes.resize(actuallyRead);
|
std::unique_lock<std::mutex> lk(rxWaitMutex);
|
||||||
|
hasRxWaitRequest = true;
|
||||||
|
|
||||||
return true;
|
auto ret = rxWaitRequestCv.wait_for(lk, timeout, predicate);
|
||||||
|
|
||||||
|
hasRxWaitRequest = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
|
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
|
||||||
@@ -32,13 +40,18 @@ bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds tim
|
|||||||
if(limit == 0)
|
if(limit == 0)
|
||||||
limit = (size_t)-1;
|
limit = (size_t)-1;
|
||||||
|
|
||||||
if(limit > (readQueue.size_approx() + 4))
|
if(limit > (readBuffer.size() + 4))
|
||||||
limit = (readQueue.size_approx() + 4);
|
limit = (readBuffer.size() + 4);
|
||||||
|
|
||||||
bytes.resize(limit);
|
|
||||||
|
|
||||||
size_t actuallyRead = readQueue.wait_dequeue_bulk_timed(bytes.data(), limit, timeout);
|
// wait until we have enough data, or the timout occurs
|
||||||
|
waitForRx(limit, timeout);
|
||||||
|
|
||||||
|
size_t actuallyRead = std::min(readBuffer.size(), limit);
|
||||||
|
bytes.resize(actuallyRead);
|
||||||
|
|
||||||
|
readBuffer.read(bytes.data(), 0, actuallyRead);
|
||||||
|
readBuffer.pop(actuallyRead);
|
||||||
bytes.resize(actuallyRead);
|
bytes.resize(actuallyRead);
|
||||||
|
|
||||||
#ifdef ICSNEO_DRIVER_DEBUG_PRINTS
|
#ifdef ICSNEO_DRIVER_DEBUG_PRINTS
|
||||||
|
|||||||
@@ -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,257 @@
|
|||||||
|
#include "icsneo/communication/message/a2bmessage.h"
|
||||||
|
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
// Read a 16 bit sample from the audio buffer, which is stored as little endian
|
||||||
|
#define SAMPLE_FROM_BYTES_16(audioData) (((audioData)[0]) | ((audioData)[1] << 8))
|
||||||
|
|
||||||
|
// Read a 32 bit sample from the audio buffer
|
||||||
|
#define SAMPLE_FROM_BYTES_32(audioData) (((audioData)[0]) | ((audioData)[1] << 8) | ((audioData)[2] << 16) | ((audioData)[3] << 24))
|
||||||
|
|
||||||
|
// Read the most significant bytes of a sample stored in a 32 bit unsigned integer into audioData
|
||||||
|
#define SAMPLE_TO_BYTES_16(audioData, offset, sample) {\
|
||||||
|
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0x00FF0000u) >> 16);\
|
||||||
|
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0xFF000000u) >> 24);\
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read little endian a 32 bit unsigned integer into audioData
|
||||||
|
#define SAMPLE_TO_BYTES_32(audioData, offset, sample) {\
|
||||||
|
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0x000000FFu));\
|
||||||
|
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0x0000FF00u) >> 8);\
|
||||||
|
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0x00FF0000u) >> 16);\
|
||||||
|
(audioData)[(offset)++] = static_cast<uint8_t>(((sample) & 0xFF000000u) >> 24);\
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t A2BMessage::tdmToChannelNum(TDMMode tdm) {
|
||||||
|
switch(tdm) {
|
||||||
|
case TDMMode::TDM2:
|
||||||
|
return 4;
|
||||||
|
case TDMMode::TDM4:
|
||||||
|
return 8;
|
||||||
|
case TDMMode::TDM8:
|
||||||
|
return 16;
|
||||||
|
case TDMMode::TDM12:
|
||||||
|
return 24;
|
||||||
|
case TDMMode::TDM16:
|
||||||
|
return 32;
|
||||||
|
case TDMMode::TDM20:
|
||||||
|
return 40;
|
||||||
|
case TDMMode::TDM24:
|
||||||
|
return 48;
|
||||||
|
case TDMMode::TDM32:
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t A2BMessage::getBytesPerChannel() const {
|
||||||
|
return channelSize16 ? 2u : 4u;
|
||||||
|
}
|
||||||
|
size_t A2BMessage::getFrameSize() const {
|
||||||
|
return static_cast<size_t>(2 * numChannels * getBytesPerChannel());
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t A2BMessage::getSampleOffset(Direction dir, uint8_t channel, size_t frame) const {
|
||||||
|
size_t frameSize = getFrameSize();
|
||||||
|
size_t sampleOffset = static_cast<size_t>(frameSize * frame + 2 * channel * getBytesPerChannel());
|
||||||
|
|
||||||
|
if(dir == Direction::Upstream) {
|
||||||
|
sampleOffset += getBytesPerChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sampleOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t A2BMessage::getNumFrames() const {
|
||||||
|
size_t frameSize = getFrameSize();
|
||||||
|
if(frameSize == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.size() / frameSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
A2BMessage::A2BMessage(size_t numFrames, TDMMode tdm, bool chSize16) : channelSize16(chSize16) {
|
||||||
|
numChannels = static_cast<uint8_t>(tdmToChannelNum(tdm) / 2);
|
||||||
|
|
||||||
|
size_t frameSize = static_cast<size_t>(2 * numChannels * (chSize16 ? 2u : 4u));
|
||||||
|
size_t audioBufferSize = frameSize * numFrames;
|
||||||
|
if(audioBufferSize > maxAudioBufferSize) {
|
||||||
|
size_t maxNumFrames = maxAudioBufferSize / frameSize;
|
||||||
|
audioBufferSize = maxNumFrames * frameSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.resize(std::min<size_t>(maxAudioBufferSize, audioBufferSize), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
A2BMessage::A2BMessage(TDMMode tdm, bool chSize16) : channelSize16(chSize16) {
|
||||||
|
numChannels = static_cast<uint8_t>(tdmToChannelNum(tdm) / 2);
|
||||||
|
size_t frameSize = static_cast<size_t>(2 * numChannels * (chSize16 ? 2u : 4u));
|
||||||
|
|
||||||
|
size_t maxNumFrames = maxAudioBufferSize / frameSize;
|
||||||
|
size_t audioBufferSize = maxNumFrames * frameSize;
|
||||||
|
|
||||||
|
data.resize(audioBufferSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PCMSample A2BMessage::getChannelSample(Direction dir, uint8_t channel, size_t frame, PCMType pcmType) const {
|
||||||
|
size_t sampleOffset = getSampleOffset(dir, channel, frame);
|
||||||
|
const uint8_t* audioData = &data[sampleOffset];
|
||||||
|
PCMSample result = 0;
|
||||||
|
|
||||||
|
// Samples coming from the device will either come from a 16 bit channel or 32 bit channel
|
||||||
|
if(channelSize16) {
|
||||||
|
int16_t sample16 = 0;
|
||||||
|
uint16_t& uSample16 = *reinterpret_cast<uint16_t*>(&sample16);
|
||||||
|
|
||||||
|
// Read little endian from the audio buffer
|
||||||
|
uSample16 = SAMPLE_FROM_BYTES_16(audioData);
|
||||||
|
|
||||||
|
// Scale the sample up according to the desired PCM size by
|
||||||
|
// multiplying using logical shifting
|
||||||
|
switch(pcmType) {
|
||||||
|
case PCMType::L16:
|
||||||
|
result = static_cast<PCMSample>(sample16);
|
||||||
|
break;
|
||||||
|
case PCMType::L24:
|
||||||
|
result = static_cast<PCMSample>(sample16) << 8;
|
||||||
|
break;
|
||||||
|
case PCMType::L32:
|
||||||
|
result = static_cast<PCMSample>(sample16) << 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PCMSample sample32 = 0;
|
||||||
|
uint32_t& uSample32 = *reinterpret_cast<uint32_t*>(&sample32);
|
||||||
|
|
||||||
|
// Read little endian
|
||||||
|
uSample32 = SAMPLE_FROM_BYTES_32(audioData);
|
||||||
|
|
||||||
|
// Scale the sample down according to the desired PCM size by dividing using
|
||||||
|
// logical shifting, if the A2B network was set up with the desired pcmType
|
||||||
|
// there should be a clean division and no loss in PCM resolution.
|
||||||
|
switch(pcmType) {
|
||||||
|
case PCMType::L16:
|
||||||
|
result = sample32 >> 16;
|
||||||
|
break;
|
||||||
|
case PCMType::L24:
|
||||||
|
result = sample32 >> 8;
|
||||||
|
break;
|
||||||
|
case PCMType::L32:
|
||||||
|
result = sample32;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void A2BMessage::setChannelSample(Direction dir, uint8_t channel, size_t frame, PCMSample sampleToSet, PCMType pcmType) {
|
||||||
|
|
||||||
|
size_t sampleOffset = getSampleOffset(dir, channel, frame);
|
||||||
|
uint32_t& uSample = *reinterpret_cast<uint32_t*>(&sampleToSet);
|
||||||
|
|
||||||
|
// Align the bytes towards the most significant bit by multiplying using
|
||||||
|
// left shifts
|
||||||
|
switch(pcmType) {
|
||||||
|
case PCMType::L16:
|
||||||
|
sampleToSet = sampleToSet << 16;
|
||||||
|
break;
|
||||||
|
case PCMType::L24:
|
||||||
|
sampleToSet = sampleToSet << 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(channelSize16) {
|
||||||
|
// Read the 2 most significant bytes of the sample
|
||||||
|
SAMPLE_TO_BYTES_16(data, sampleOffset, uSample)
|
||||||
|
} else {
|
||||||
|
// Read the entire sample
|
||||||
|
SAMPLE_TO_BYTES_32(data, sampleOffset, uSample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool A2BMessage::loadAudioBuffer(IWAVStream& wavStream, const ChannelMap& channelMap) {
|
||||||
|
if(!wavStream) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t totalMessageChannels = numChannels * 2; // Multiply by two inorder to include both down and upstream channels
|
||||||
|
size_t bytesPerChannel = static_cast<size_t>(getBytesPerChannel()); // Number of bytes per message channel
|
||||||
|
size_t frameSize = getFrameSize();
|
||||||
|
size_t numFrames = getNumFrames();
|
||||||
|
|
||||||
|
size_t bytesPerSampleWAV = static_cast<size_t>(wavStream.header.bitsPerSample / 8); // Number of bytes per sample in the WAV data-stream
|
||||||
|
size_t numWAVChannels = static_cast<size_t>(wavStream.header.numChannels);
|
||||||
|
size_t wavFrameSize = numWAVChannels * bytesPerSampleWAV;
|
||||||
|
|
||||||
|
if(bytesPerSampleWAV != 2 && bytesPerSampleWAV != 3 && bytesPerSampleWAV != 4) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(numFrames == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* audioBuffer = data.data();
|
||||||
|
|
||||||
|
std::vector<uint8_t> wavFrame(wavFrameSize, 0);
|
||||||
|
for(size_t frame = 0; frame < numFrames; frame++) {
|
||||||
|
|
||||||
|
// Read one frame of data from the input stream
|
||||||
|
if(!wavStream.read(reinterpret_cast<char*>(wavFrame.data()), wavFrame.size())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through each mapping and set a message channel to a channel in the WAV frame above
|
||||||
|
for(const auto& [messageChannel, wavChannel] : channelMap) {
|
||||||
|
if(messageChannel >= totalMessageChannels || wavChannel >= numWAVChannels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t frameOffset = wavChannel * bytesPerSampleWAV; // Offset in the read WAV frame
|
||||||
|
size_t audioBufferOffset = frame * frameSize + messageChannel * bytesPerChannel; // Offset in the message audio buffer
|
||||||
|
|
||||||
|
if(bytesPerChannel < bytesPerSampleWAV) {
|
||||||
|
// In this case, the message channels are smaller than the samples in the input WAV
|
||||||
|
// samples in both the message channel and WAV are little endian, so we write only the
|
||||||
|
// most significant bytes of the WAV
|
||||||
|
|
||||||
|
// Align to most significant bytes of wav frame
|
||||||
|
size_t align = bytesPerSampleWAV - bytesPerChannel;
|
||||||
|
|
||||||
|
for(
|
||||||
|
size_t frameByte = frameOffset + align;
|
||||||
|
frameByte < frameOffset + bytesPerSampleWAV;
|
||||||
|
frameByte++,
|
||||||
|
audioBufferOffset++
|
||||||
|
) {
|
||||||
|
audioBuffer[audioBufferOffset] = wavFrame[frameByte];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The message channel is greater than or equal to the sample in the WAV
|
||||||
|
// I2S specifies that the sample in this case is right aligned to the most significant
|
||||||
|
// byte of the message channel
|
||||||
|
|
||||||
|
// Align to most significant byte of audio buffer channel
|
||||||
|
size_t align = bytesPerChannel - bytesPerSampleWAV;
|
||||||
|
|
||||||
|
for(
|
||||||
|
size_t audioByte = audioBufferOffset + align;
|
||||||
|
audioByte < audioBufferOffset + bytesPerChannel;
|
||||||
|
audioByte++,
|
||||||
|
frameOffset++
|
||||||
|
) {
|
||||||
|
audioBuffer[audioByte] = wavFrame[frameOffset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
|
||||||
|
#include "icsneo/device/tree/rada2b/rada2b.h"
|
||||||
|
#include "icsneo/icsneocpp.h"
|
||||||
|
|
||||||
|
namespace icsneo {
|
||||||
|
|
||||||
|
A2BWAVOutput::A2BWAVOutput(
|
||||||
|
const char* filename,
|
||||||
|
const ChannelMap& channelMap,
|
||||||
|
PCMType bitDepth,
|
||||||
|
size_t numWAVChannels,
|
||||||
|
uint32_t sampleRate
|
||||||
|
)
|
||||||
|
: StreamOutput(filename), wavSampleRate(sampleRate), numChannelsWAV(numWAVChannels), chMap(channelMap) {
|
||||||
|
switch(bitDepth) {
|
||||||
|
case PCMType::L16:
|
||||||
|
bytesPerSampleWAV = 2;
|
||||||
|
break;
|
||||||
|
case PCMType::L24:
|
||||||
|
bytesPerSampleWAV = 3;
|
||||||
|
break;
|
||||||
|
case PCMType::L32:
|
||||||
|
bytesPerSampleWAV = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(initialize()) {
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
A2BWAVOutput::A2BWAVOutput(
|
||||||
|
std::ostream& os,
|
||||||
|
const ChannelMap& channelMap,
|
||||||
|
PCMType bitDepth,
|
||||||
|
size_t numWAVChannels,
|
||||||
|
uint32_t sampleRate
|
||||||
|
)
|
||||||
|
: StreamOutput(os), wavSampleRate(sampleRate), numChannelsWAV(numWAVChannels), chMap(channelMap) {
|
||||||
|
switch(bitDepth) {
|
||||||
|
case PCMType::L16:
|
||||||
|
bytesPerSampleWAV = 2;
|
||||||
|
break;
|
||||||
|
case PCMType::L24:
|
||||||
|
bytesPerSampleWAV = 3;
|
||||||
|
break;
|
||||||
|
case PCMType::L32:
|
||||||
|
bytesPerSampleWAV = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(initialize()) {
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
A2BWAVOutput::~A2BWAVOutput() {
|
||||||
|
if(!closed) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool A2BWAVOutput::initialize() {
|
||||||
|
static constexpr size_t maxWAVChannels = 256;
|
||||||
|
|
||||||
|
if(numChannelsWAV > maxWAVChannels) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxMessageChannel = 0;
|
||||||
|
|
||||||
|
// Check if the inputted channel map has invalid mappings and compute maxMessageChannel
|
||||||
|
for(auto [wavChannel, messageChannel] : chMap) {
|
||||||
|
maxMessageChannel = std::max<size_t>(maxMessageChannel, messageChannel);
|
||||||
|
if(wavChannel >= numChannelsWAV) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WAVHeader header = WAVHeader(
|
||||||
|
static_cast<uint16_t>(chMap.size()),
|
||||||
|
wavSampleRate,
|
||||||
|
static_cast<uint16_t>(bytesPerSampleWAV * 8)
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!stream->write(reinterpret_cast<const char*>(&header), sizeof(WAVHeader))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamStartPos = static_cast<uint32_t>(stream->tellp());
|
||||||
|
wavBuffer = std::vector<uint8_t>(wavBufferSize, 0);
|
||||||
|
wavBufferOffset = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
|
||||||
|
if(!initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(closed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message->type != Message::Type::Frame) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& frameMsg = std::dynamic_pointer_cast<Frame>(message);
|
||||||
|
|
||||||
|
if(!frameMsg) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frameMsg->network.getType() != Network::Type::A2B)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto& a2bMsg = std::dynamic_pointer_cast<A2BMessage>(frameMsg);
|
||||||
|
|
||||||
|
if(!a2bMsg) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t frameSize = a2bMsg->getFrameSize();
|
||||||
|
size_t wavFrameSize = numChannelsWAV * bytesPerSampleWAV;
|
||||||
|
size_t bytesPerChannel = static_cast<size_t>(a2bMsg->getBytesPerChannel());
|
||||||
|
size_t numMessageChannels = 2 * a2bMsg->numChannels;
|
||||||
|
size_t numFrames = a2bMsg->getNumFrames();
|
||||||
|
|
||||||
|
const uint8_t* audioBuffer = a2bMsg->data.data();
|
||||||
|
|
||||||
|
if(maxMessageChannel >= numMessageChannels) {
|
||||||
|
// The max message channel in our channel map is larger than the number of channels in this message
|
||||||
|
// this is likely due to the user inputting incorrect settings
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t frame = 0; frame < numFrames; frame++) {
|
||||||
|
// Check to see if we can read another frame in wavBuffer, otherwise write and clear the buffer
|
||||||
|
if(wavBufferOffset + wavFrameSize >= wavBufferSize) {
|
||||||
|
if(!writeCurrentBuffer()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t wavChannel = 0; wavChannel < numChannelsWAV; wavChannel++) {
|
||||||
|
if(auto iter = chMap.find(static_cast<uint8_t>(wavChannel)); iter != chMap.end()) {
|
||||||
|
auto messageChannel = iter->second;
|
||||||
|
size_t messageChannelOffset = messageChannel * bytesPerChannel + frameSize* frame;
|
||||||
|
|
||||||
|
// Samples in the WAV are little endian signed integers
|
||||||
|
// Samples in the message channels are little endian signed integers that are
|
||||||
|
// most significant bit aligned
|
||||||
|
if(a2bMsg->channelSize16) {
|
||||||
|
// In this case, the channel size will be less than or equal to the sample we are writing
|
||||||
|
// so we zero out any of the least significant bytes which won't be occupied by a sample byte
|
||||||
|
|
||||||
|
for(size_t zeroByte = 0; zeroByte < bytesPerSampleWAV - bytesPerChannel; zeroByte++) {
|
||||||
|
wavBuffer[wavBufferOffset++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the channel data in the most signifant bytes of the wav sample, this effectively
|
||||||
|
// writes a sample which is scaled up.
|
||||||
|
for(size_t channelByte = 0; channelByte < bytesPerChannel; channelByte++) {
|
||||||
|
wavBuffer[wavBufferOffset++] = audioBuffer[messageChannelOffset + channelByte];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In this case, the channel size will be greater than or equal to the sample we are reading
|
||||||
|
|
||||||
|
// Align the wav sample with the most significant bytes of the channel
|
||||||
|
size_t channelByte = messageChannelOffset + (bytesPerChannel - bytesPerSampleWAV);
|
||||||
|
|
||||||
|
// Read the most significant bytes of the channel into the wavBuffer
|
||||||
|
for(size_t sampleByte = 0; sampleByte < bytesPerSampleWAV; sampleByte++, channelByte++) {
|
||||||
|
wavBuffer[wavBufferOffset++] = audioBuffer[channelByte];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If this channel wasn't specified in the channel map, set a zero sample
|
||||||
|
for(
|
||||||
|
size_t sampleByte = 0;
|
||||||
|
sampleByte < bytesPerSampleWAV;
|
||||||
|
sampleByte++
|
||||||
|
) {
|
||||||
|
wavBuffer[wavBufferOffset++] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void A2BWAVOutput::close() const {
|
||||||
|
if(closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write any left over data in the buffer
|
||||||
|
if(wavBufferOffset > 0) {
|
||||||
|
writeCurrentBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek back in the output stream and write the WAV chunk sizes
|
||||||
|
uint32_t streamEndPos = static_cast<uint32_t>(stream->tellp());
|
||||||
|
|
||||||
|
uint32_t subChunk2Size = streamEndPos - streamStartPos;
|
||||||
|
uint32_t chunkSize = streamEndPos - 8;
|
||||||
|
|
||||||
|
stream->seekp(streamStartPos - 4);
|
||||||
|
stream->write(reinterpret_cast<const char*>(&subChunk2Size), 4);
|
||||||
|
stream->seekp(4, std::ios::beg);
|
||||||
|
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
|
||||||
|
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool A2BWAVOutput::writeCurrentBuffer() const {
|
||||||
|
|
||||||
|
if(!stream->write(reinterpret_cast<const char*>(wavBuffer.data()), wavBufferOffset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wavBufferOffset = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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,31 @@
|
|||||||
|
#include "icsneo/communication/message/tc10statusmessage.h"
|
||||||
|
#include "icsneo/communication/command.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
#pragma pack(push, 2)
|
||||||
|
struct Header {
|
||||||
|
ExtendedCommand command;
|
||||||
|
uint16_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Packet {
|
||||||
|
Header header;
|
||||||
|
TC10WakeStatus wakeStatus;
|
||||||
|
TC10SleepStatus sleepStatus;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
std::shared_ptr<TC10StatusMessage> TC10StatusMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||||
|
if(bytestream.size() < sizeof(Packet)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const Packet* packet = (Packet*)bytestream.data();
|
||||||
|
if (packet->header.command != ExtendedCommand::GetTC10Status) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (packet->header.length < sizeof(Packet) - sizeof(Header)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::make_shared<TC10StatusMessage>(packet->wakeStatus, packet->sleepStatus);
|
||||||
|
}
|
||||||
@@ -178,7 +178,10 @@ void MultiChannelCommunication::vnetReadTask(size_t vnetIndex) {
|
|||||||
if(closing)
|
if(closing)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
handleInput(*vnetPacketizer, payloadBytes);
|
auto& ringBuffer = driver->getReadBuffer();
|
||||||
|
ringBuffer.write(payloadBytes);
|
||||||
|
|
||||||
|
handleInput(*vnetPacketizer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#include "icsneo/communication/packet/a2bpacket.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace icsneo {
|
||||||
|
|
||||||
|
const size_t HardwareA2BPacket::a2bMessageMaxLength = sizeof(HardwareA2BPacket) + 1024;
|
||||||
|
|
||||||
|
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||||
|
|
||||||
|
if(bytestream.size() < sizeof(HardwareA2BPacket))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HardwareA2BPacket* data = (const HardwareA2BPacket*)bytestream.data();
|
||||||
|
|
||||||
|
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - sizeof(HardwareA2BPacket); // First 28 bytes are message header.
|
||||||
|
|
||||||
|
if(totalPackedLength == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>();
|
||||||
|
|
||||||
|
msg->numChannels = data->header.channelNum;
|
||||||
|
msg->channelSize16 = data->header.channelSize16;
|
||||||
|
msg->monitor = data->header.monitor;
|
||||||
|
msg->txmsg = data->header.txmsg;
|
||||||
|
msg->errIndicator = data->header.errIndicator;
|
||||||
|
msg->syncFrame = data->header.syncFrame;
|
||||||
|
msg->rfu2 = data->header.rfu2;
|
||||||
|
msg->timestamp = data->timestamp.TS;
|
||||||
|
|
||||||
|
msg->data = std::vector(bytestream.begin() + sizeof(HardwareA2BPacket), bytestream.end());
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& /*report*/) {
|
||||||
|
constexpr size_t a2btxMessageHeaderSize = 6;
|
||||||
|
|
||||||
|
size_t audioBufferSize = message.data.size();
|
||||||
|
size_t totalSize = a2btxMessageHeaderSize + audioBufferSize;
|
||||||
|
|
||||||
|
bytestream.resize(totalSize, 0);
|
||||||
|
uint32_t offset = 0;
|
||||||
|
|
||||||
|
bytestream[offset++] = 0;
|
||||||
|
bytestream[offset++] = 0;
|
||||||
|
bytestream[offset++] = (uint8_t)(audioBufferSize & 0xFF);
|
||||||
|
bytestream[offset++] = (uint8_t)((audioBufferSize >> 8) & 0xFF);
|
||||||
|
bytestream[offset++] = (uint8_t)((message.description >> 8) & 0xFF);
|
||||||
|
bytestream[offset++] = (uint8_t)(message.description & 0xFF);
|
||||||
|
|
||||||
|
std::copy(message.data.begin(), message.data.end(), bytestream.begin() + offset);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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,50 @@
|
|||||||
|
#include "icsneo/communication/packet/hardwareinfopacket.h"
|
||||||
|
#include "icsneo/communication/message/hardwareinfo.h"
|
||||||
|
#include <iostream>
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t valid;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t day;
|
||||||
|
uint8_t month;
|
||||||
|
uint16_t year;
|
||||||
|
} manufactureDate;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t major;
|
||||||
|
uint8_t minor;
|
||||||
|
} hwRev;
|
||||||
|
uint8_t deviceId;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t major;
|
||||||
|
uint8_t minor;
|
||||||
|
} blVersion;
|
||||||
|
} HardwareInfoFrame;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
std::shared_ptr<HardwareInfo> HardwareInfoPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
|
||||||
|
if(bytes.size() < (sizeof(HardwareInfoFrame) + 1)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* frame = reinterpret_cast<const HardwareInfoFrame*>(&bytes[1]);
|
||||||
|
|
||||||
|
auto msg = std::make_shared<HardwareInfo>();
|
||||||
|
|
||||||
|
msg->manufactureDate.day = frame->manufactureDate.day;
|
||||||
|
msg->manufactureDate.year = frame->manufactureDate.year;
|
||||||
|
msg->manufactureDate.month = frame->manufactureDate.month;
|
||||||
|
|
||||||
|
msg->hardwareRevision.major = frame->hwRev.major;
|
||||||
|
msg->hardwareRevision.minor = frame->hwRev.minor;
|
||||||
|
|
||||||
|
msg->bootloaderVersion.major = frame->blVersion.major;
|
||||||
|
msg->bootloaderVersion.minor = frame->blVersion.minor;
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
@@ -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,44 @@
|
|||||||
|
#include "icsneo/communication/packet/supportedfeaturespacket.h"
|
||||||
|
#include "icsneo/communication/message/supportedfeaturesmessage.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
static constexpr uint16_t SupportedFeaturesCommandVersion = 1;
|
||||||
|
static constexpr size_t NumSupportedFeaturesFields =
|
||||||
|
(static_cast<size_t>(SupportedFeature::numSupportedFeatures) + 31) / 32;
|
||||||
|
#pragma pack(push, 2)
|
||||||
|
struct SupportedFeaturesResponse {
|
||||||
|
ExtendedResponseMessage::ResponseHeader header;
|
||||||
|
uint16_t cmdVersion;
|
||||||
|
uint16_t numValidBits;
|
||||||
|
uint32_t featuresFields[NumSupportedFeaturesFields];
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
std::shared_ptr<SupportedFeaturesMessage> SupportedFeaturesPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
|
||||||
|
auto msg = std::make_shared<SupportedFeaturesMessage>();
|
||||||
|
// Length check: At least a header, a 2-byte cmdVersion field, and a 2-byte numValidBits field.
|
||||||
|
if(bytes.size() < sizeof(ExtendedResponseMessage::ResponseHeader) + 4) {
|
||||||
|
return msg; // Empty
|
||||||
|
}
|
||||||
|
// Get a reference to the payload to fully validate the length
|
||||||
|
const auto& response = *reinterpret_cast<const SupportedFeaturesResponse*>(bytes.data());
|
||||||
|
if(response.cmdVersion != SupportedFeaturesCommandVersion) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
// Expected size is the header, cmdVersion and numValidBits fields, plus the number of 32-bit bitfields in the response based on numValidBits
|
||||||
|
auto expectedSize = sizeof(ExtendedResponseMessage::ResponseHeader) + 4 + ((response.numValidBits + 31) / 32) * 4;
|
||||||
|
// If the response is malformed (too small), return an empty message
|
||||||
|
if(bytes.size() < expectedSize) {
|
||||||
|
return msg; // Empty
|
||||||
|
}
|
||||||
|
unsigned int loopLimit = std::min<unsigned int>(response.numValidBits, static_cast<unsigned int>(SupportedFeature::numSupportedFeatures));
|
||||||
|
for(unsigned int i = 0; i < loopLimit; ++i) {
|
||||||
|
uint32_t wordOffset = i / 32;
|
||||||
|
uint32_t bitOffset = i % 32;
|
||||||
|
if((response.featuresFields[wordOffset] >> bitOffset) & 1) {
|
||||||
|
msg->features.insert(static_cast<SupportedFeature>(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -25,9 +24,8 @@ std::vector<uint8_t>& Packetizer::packetWrap(std::vector<uint8_t>& data, bool sh
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
bool Packetizer::input(RingBuffer& bytes) {
|
||||||
bool haveEnoughData = true;
|
bool haveEnoughData = true;
|
||||||
bytes.insert(bytes.end(), inputBytes.begin(), inputBytes.end());
|
|
||||||
|
|
||||||
while(haveEnoughData) {
|
while(haveEnoughData) {
|
||||||
switch(state) {
|
switch(state) {
|
||||||
@@ -50,19 +48,22 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
packetLength = bytes[1] >> 4 & 0xf; // Upper nibble of the second byte denotes the packet length
|
packetLength = bytes[1] >> 4 & 0xF;
|
||||||
packet.network = Network(bytes[1] & 0xf); // Lower nibble of the second byte is the network ID
|
packet.network = Network(bytes[1] & 0xF); // Lower nibble of the second byte is the network ID
|
||||||
if(packetLength == 0) { // A length of zero denotes a long style packet
|
|
||||||
|
if(packetLength == 0) {
|
||||||
state = ReadState::ParseLongStylePacketHeader;
|
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) {
|
||||||
@@ -72,21 +73,71 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
|||||||
|
|
||||||
packetLength = bytes[2]; // Long packets have a little endian length on bytes 3 and 4
|
packetLength = bytes[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
|
||||||
@@ -99,16 +150,18 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
|||||||
if(packetLength > 0)
|
if(packetLength > 0)
|
||||||
packet.data.resize(packetLength - headerSize);
|
packet.data.resize(packetLength - headerSize);
|
||||||
|
|
||||||
auto i = 0;
|
bytes.read(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.pop(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);
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
#include "icsneo/communication/ringbuffer.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace icsneo {
|
||||||
|
|
||||||
|
RingBuffer::RingBuffer(size_t bufferSize) : readCursor(0), writeCursor(0) {
|
||||||
|
// round the buffer size to the nearest power of 2
|
||||||
|
bufferSize = RoundUp(bufferSize);
|
||||||
|
mask = bufferSize - 1;
|
||||||
|
buf = new uint8_t[bufferSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
RingBuffer::~RingBuffer() {
|
||||||
|
delete[] buf;
|
||||||
|
buf = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t& RingBuffer::operator[](size_t offset) const {
|
||||||
|
return get(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t RingBuffer::size() const {
|
||||||
|
// The values in the cursors are monotonic, i.e. they only ever increment. They can be considered to be the total number of elements ever written or read
|
||||||
|
auto currentWriteCursor = writeCursor.load(std::memory_order_relaxed);
|
||||||
|
auto currentReadCursor = readCursor.load(std::memory_order_relaxed);
|
||||||
|
// Using unmasked values, writeCursor is guaranteed to be >= readCursor. If they are equal that means the buffer is empty
|
||||||
|
return currentWriteCursor - currentReadCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RingBuffer::pop_front() {
|
||||||
|
pop(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RingBuffer::pop(size_t count) {
|
||||||
|
if (size() < count) {
|
||||||
|
throw std::runtime_error("RingBuffer: Underflow");
|
||||||
|
}
|
||||||
|
readCursor.fetch_add(count, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t& RingBuffer::get(size_t offset) const {
|
||||||
|
if (offset >= size()) {
|
||||||
|
throw std::runtime_error("RingBuffer: Index out of range");
|
||||||
|
}
|
||||||
|
auto currentReadCursor = readCursor.load(std::memory_order_acquire);
|
||||||
|
return *resolve(currentReadCursor, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RingBuffer::write(const uint8_t* addr, size_t length) {
|
||||||
|
const auto freeSpace = (capacity() - size());
|
||||||
|
if (length > freeSpace) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto currentWriteCursor = writeCursor.load(std::memory_order_relaxed);
|
||||||
|
auto spaceAtEnd = std::min(freeSpace, capacity() - (currentWriteCursor & mask)); // number of bytes from (masked) writeCursor to the end of the writable space (i.e. we reach the masked read cursor or the end of the buffer)
|
||||||
|
auto firstCopySize = std::min(spaceAtEnd, length);
|
||||||
|
(void)memcpy(resolve(currentWriteCursor, 0), addr, firstCopySize);
|
||||||
|
if (firstCopySize < length)
|
||||||
|
{
|
||||||
|
(void)memcpy(buf, &addr[firstCopySize], length - firstCopySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCursor.store(currentWriteCursor + length, std::memory_order_release);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RingBuffer::write(const std::vector<uint8_t>& source) {
|
||||||
|
return write(source.data(), source.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RingBuffer::read(uint8_t* dest, size_t startIndex, size_t length) const {
|
||||||
|
auto currentSize = size();
|
||||||
|
if ((startIndex >= currentSize) || ((startIndex + length) > size())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto currentReadCursor = readCursor.load(std::memory_order_relaxed);
|
||||||
|
auto bytesAtEnd = std::min<size_t>(capacity() - ((currentReadCursor + startIndex) & mask), length);
|
||||||
|
const auto bytesAtStart = (length - bytesAtEnd);
|
||||||
|
|
||||||
|
(void)memcpy(dest, resolve(currentReadCursor, startIndex), bytesAtEnd);
|
||||||
|
if (bytesAtStart > 0) {
|
||||||
|
(void)memcpy(&dest[bytesAtEnd], buf, bytesAtStart);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RingBuffer::clear() {
|
||||||
|
pop(size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2396
-62
File diff suppressed because it is too large
Load Diff
+169
-35
@@ -19,6 +19,14 @@
|
|||||||
#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>
|
||||||
@@ -39,132 +47,230 @@ static void makeIfPIDMatches(const FoundDevice& dev, std::vector<std::shared_ptr
|
|||||||
into.push_back(std::make_shared<T>(dev));
|
into.push_back(std::make_shared<T>(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static void makeIfSerialRangeMatches(const FoundDevice& dev, std::vector<std::shared_ptr<Device>>& into) {
|
||||||
|
// Relies on the subclass to have
|
||||||
|
// `static constexpr uint32_t SERIAL_RANGE_LOW = 0x12345678`
|
||||||
|
// `static constexpr uint32_t SERIAL_RANGE_HIGH = 0x12345678`
|
||||||
|
// and also a public constructor `T(const FoundDevice& dev)`
|
||||||
|
// Use macro ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE() to create these
|
||||||
|
uint32_t serialNum = Device::SerialStringToNum(dev.serial);
|
||||||
|
if(serialNum >= Device::SerialStringToNum(T::SERIAL_RANGE_LOW) && serialNum <= Device::SerialStringToNum(T::SERIAL_RANGE_HIGH))
|
||||||
|
into.push_back(std::make_shared<T>(dev));
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
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
|
#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
|
||||||
|
|
||||||
|
#ifdef __NEOVICONNECT_H_
|
||||||
|
makeIfSerialMatches<NeoVIConnect>(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_
|
||||||
|
makeIfSerialRangeMatches<RADComet>(dev, newFoundDevices);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __RADCOMET2_H_
|
||||||
|
makeIfSerialRangeMatches<RADComet2>(dev, newFoundDevices);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __RADCOMET3_H_
|
||||||
|
makeIfSerialMatches<RADComet3>(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() {
|
||||||
@@ -186,6 +292,10 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
|||||||
NeoVIRED2::DEVICE_TYPE,
|
NeoVIRED2::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __NEOVICONNECT_H_
|
||||||
|
NeoVIConnect::DEVICE_TYPE,
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __NEOVIFIRE_H_
|
#ifdef __NEOVIFIRE_H_
|
||||||
NeoVIFIRE::DEVICE_TYPE,
|
NeoVIFIRE::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
@@ -194,6 +304,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 +320,18 @@ 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 __RADCOMET3_H_
|
||||||
|
RADComet3::DEVICE_TYPE,
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __RADEPSILON_H_
|
#ifdef __RADEPSILON_H_
|
||||||
RADEpsilon::DEVICE_TYPE,
|
RADEpsilon::DEVICE_TYPE,
|
||||||
#endif
|
#endif
|
||||||
@@ -218,10 +348,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
|
||||||
|
|||||||
+244
-9
@@ -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;
|
||||||
@@ -128,6 +128,37 @@ int64_t IDeviceSettings::GetBaudrateValueForEnum(CANBaudrate enumValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IDeviceSettings::ValidateLINBaudrate(int64_t baudrate) {
|
||||||
|
switch(baudrate) {
|
||||||
|
case 4800:
|
||||||
|
// fallthrough
|
||||||
|
case 9600:
|
||||||
|
// fallthrough
|
||||||
|
case 10400:
|
||||||
|
// fallthrough
|
||||||
|
case 10417:
|
||||||
|
// fallthrough
|
||||||
|
case 10504:
|
||||||
|
// fallthrough
|
||||||
|
case 10593:
|
||||||
|
// fallthrough
|
||||||
|
case 10684:
|
||||||
|
// fallthrough
|
||||||
|
case 10776:
|
||||||
|
// fallthrough
|
||||||
|
case 10870:
|
||||||
|
// fallthrough
|
||||||
|
case 10965:
|
||||||
|
// fallthrough
|
||||||
|
case 11062:
|
||||||
|
// fallthrough
|
||||||
|
case 19200:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool IDeviceSettings::refresh(bool ignoreChecksum) {
|
bool IDeviceSettings::refresh(bool ignoreChecksum) {
|
||||||
if(disabled) {
|
if(disabled) {
|
||||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
@@ -215,7 +246,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 +352,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);
|
||||||
@@ -414,6 +445,15 @@ int64_t IDeviceSettings::getBaudrateFor(Network net) const {
|
|||||||
}
|
}
|
||||||
return baudrate;
|
return baudrate;
|
||||||
}
|
}
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
|
||||||
|
if(cfg == nullptr) {
|
||||||
|
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg->Baudrate;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -493,6 +533,21 @@ bool IDeviceSettings::setBaudrateFor(Network net, int64_t baudrate) {
|
|||||||
cfg->SetBaudrate = AUTO; // Device will use the baudrate value to set the TQ values
|
cfg->SetBaudrate = AUTO; // Device will use the baudrate value to set the TQ values
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
|
||||||
|
if(cfg == nullptr) {
|
||||||
|
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid = ValidateLINBaudrate(baudrate);
|
||||||
|
if(!valid) {
|
||||||
|
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cfg->Baudrate = (uint32_t)baudrate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
return false;
|
return false;
|
||||||
@@ -638,27 +693,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;
|
||||||
@@ -704,6 +759,186 @@ bool IDeviceSettings::setTerminationFor(Network net, bool enabled) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<bool> IDeviceSettings::isCommanderResistorEnabledFor(Network net) const {
|
||||||
|
if(!settingsLoaded) {
|
||||||
|
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(disabled) {
|
||||||
|
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(net.getType()) {
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
|
||||||
|
if(cfg == nullptr) {
|
||||||
|
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (cfg->CommanderResistor != RESISTOR_OFF);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDeviceSettings::setCommanderResistorFor(Network net, bool resistor_on) {
|
||||||
|
if(disabled) {
|
||||||
|
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!settingsLoaded) {
|
||||||
|
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(readonly) {
|
||||||
|
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(net.getType()) {
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
|
||||||
|
if(cfg == nullptr) {
|
||||||
|
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg->CommanderResistor = resistor_on ? RESISTOR_ON : RESISTOR_OFF;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<LINMode> IDeviceSettings::getLINModeFor(Network net) const {
|
||||||
|
if(!settingsLoaded) {
|
||||||
|
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(disabled) {
|
||||||
|
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(net.getType()) {
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
|
||||||
|
if(cfg == nullptr) {
|
||||||
|
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<LINMode>(cfg->Mode);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDeviceSettings::setLINModeFor(Network net, LINMode mode) {
|
||||||
|
if(disabled) {
|
||||||
|
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!settingsLoaded) {
|
||||||
|
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(readonly) {
|
||||||
|
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(net.getType()) {
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
|
||||||
|
if(cfg == nullptr) {
|
||||||
|
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg->Mode = static_cast<uint8_t>(mode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<uint8_t> IDeviceSettings::getLINCommanderResponseTimeFor(Network net) const {
|
||||||
|
if(!settingsLoaded) {
|
||||||
|
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(disabled) {
|
||||||
|
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(net.getType()) {
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
|
||||||
|
if(cfg == nullptr) {
|
||||||
|
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg->numBitsDelay;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDeviceSettings::setLINCommanderResponseTimeFor(Network net, uint8_t bits) {
|
||||||
|
if(disabled) {
|
||||||
|
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!settingsLoaded) {
|
||||||
|
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(readonly) {
|
||||||
|
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(net.getType()) {
|
||||||
|
case Network::Type::LIN: {
|
||||||
|
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
|
||||||
|
if(cfg == nullptr) {
|
||||||
|
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg->numBitsDelay = bits;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T> bool IDeviceSettings::applyStructure(const T& newStructure) {
|
template<typename T> bool IDeviceSettings::applyStructure(const T& newStructure) {
|
||||||
if(!settingsLoaded) {
|
if(!settingsLoaded) {
|
||||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||||
|
|||||||
+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,92 +1,85 @@
|
|||||||
#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) {
|
|
||||||
// The cache does not have this data, go get it
|
|
||||||
const uint64_t currentSector = pos / SectorSize;
|
|
||||||
auto msg = com.waitForMessageSync([¤tSector, &com] {
|
|
||||||
return com.sendCommand(Command::NeoReadMemory, {
|
|
||||||
MemoryTypeSD,
|
|
||||||
uint8_t(currentSector & 0xFF),
|
|
||||||
uint8_t((currentSector >> 8) & 0xFF),
|
|
||||||
uint8_t((currentSector >> 16) & 0xFF),
|
|
||||||
uint8_t((currentSector >> 24) & 0xFF),
|
|
||||||
uint8_t(SectorSize & 0xFF),
|
|
||||||
uint8_t((SectorSize >> 8) & 0xFF),
|
|
||||||
uint8_t((SectorSize >> 16) & 0xFF),
|
|
||||||
uint8_t((SectorSize >> 24) & 0xFF)
|
|
||||||
});
|
|
||||||
}, NeoMemorySDRead, timeout);
|
|
||||||
|
|
||||||
if(!msg)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
|
||||||
if(!sdmsg || sdmsg->data.size() != SectorSize) {
|
|
||||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
|
||||||
return nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(cache.data(), sdmsg->data.data(), SectorSize);
|
|
||||||
cachedAt = std::chrono::steady_clock::now();
|
|
||||||
cachePos = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(into, cache.data(), SectorSize);
|
|
||||||
return SectorSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
|
||||||
uint64_t pos, const uint8_t* atomicBuf, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
|
|
||||||
|
|
||||||
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
|
|
||||||
|
|
||||||
if(pos % SectorSize != 0)
|
|
||||||
return nullopt;
|
|
||||||
|
|
||||||
if(amount != SectorSize)
|
|
||||||
return nullopt;
|
|
||||||
|
|
||||||
// Clear the cache if we're writing to the cached sector
|
|
||||||
if(pos == cachePos)
|
|
||||||
cachedAt = std::chrono::time_point<std::chrono::steady_clock>();
|
|
||||||
|
|
||||||
// Requesting an atomic operation, but neoMemory does not support it
|
|
||||||
// Continue on anyway but warn the caller
|
|
||||||
if(atomicBuf != nullptr)
|
|
||||||
report(APIEvent::Type::AtomicOperationCompletedNonatomically, NonatomicSeverity);
|
|
||||||
|
|
||||||
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;
|
||||||
std::vector<uint8_t> command = {
|
|
||||||
MemoryTypeSD,
|
uint64_t numWords = amount / 2;
|
||||||
|
|
||||||
|
auto msg = com.waitForMessageSync([¤tSector, &memLocation, &com, &numWords] {
|
||||||
|
return com.sendCommand(Command::NeoReadMemory, {
|
||||||
|
memLocation,
|
||||||
uint8_t(currentSector & 0xFF),
|
uint8_t(currentSector & 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(numWords & 0xFF),
|
||||||
uint8_t((SectorSize >> 8) & 0xFF),
|
uint8_t((numWords >> 8) & 0xFF),
|
||||||
|
uint8_t((numWords >> 16) & 0xFF),
|
||||||
|
uint8_t((numWords >> 24) & 0xFF)
|
||||||
|
});
|
||||||
|
}, NeoMemorySDRead, timeout);
|
||||||
|
|
||||||
|
if(!msg)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
||||||
|
if(!sdmsg || sdmsg->data.size() != SectorSize) {
|
||||||
|
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(into, sdmsg->data.data(), SectorSize);
|
||||||
|
return SectorSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||||
|
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||||
|
|
||||||
|
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
|
||||||
|
|
||||||
|
if(pos % SectorSize != 0)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if(amount != SectorSize)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const uint64_t currentSector = pos / SectorSize;
|
||||||
|
const uint8_t memLocation = (uint8_t)memType;
|
||||||
|
|
||||||
|
uint64_t numWords = amount / 2;
|
||||||
|
|
||||||
|
auto msg = com.waitForMessageSync([¤tSector, &memLocation, &com, from, amount, &numWords] {
|
||||||
|
std::vector<uint8_t> command = {
|
||||||
|
memLocation,
|
||||||
|
uint8_t(currentSector & 0xFF),
|
||||||
|
uint8_t((currentSector >> 8) & 0xFF),
|
||||||
|
uint8_t((currentSector >> 16) & 0xFF),
|
||||||
|
uint8_t((currentSector >> 24) & 0xFF),
|
||||||
|
uint8_t(numWords & 0xFF),
|
||||||
|
uint8_t((numWords >> 8) & 0xFF),
|
||||||
};
|
};
|
||||||
command.insert(command.end(), from, from + amount);
|
command.insert(command.end(), from, from + amount);
|
||||||
return com.sendCommand(Command::NeoWriteMemory, command);
|
return com.sendCommand(Command::NeoWriteMemory, command);
|
||||||
}, 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();
|
||||||
|
|
||||||
memcpy(cache.data() + copied, sdmsg->data.data(), sdmsg->data.size());
|
}
|
||||||
copied += uint32_t(sdmsg->data.size());
|
}, NeoMemorySDRead));
|
||||||
if(copied == amount) {
|
|
||||||
lk.unlock();
|
|
||||||
cv.notify_all();
|
|
||||||
}
|
|
||||||
}, NeoMemorySDRead));
|
|
||||||
|
|
||||||
com.rawWrite({
|
com.rawWrite({
|
||||||
uint8_t(MultiChannelCommunication::CommandType::HostPC_from_SDCC1),
|
uint8_t(MultiChannelCommunication::CommandType::HostPC_from_SDCC1),
|
||||||
uint8_t(sector & 0xFF),
|
uint8_t(sector & 0xFF),
|
||||||
uint8_t((sector >> 8) & 0xFF),
|
uint8_t((sector >> 8) & 0xFF),
|
||||||
uint8_t((sector >> 16) & 0xFF),
|
uint8_t((sector >> 16) & 0xFF),
|
||||||
uint8_t((sector >> 24) & 0xFF),
|
uint8_t((sector >> 24) & 0xFF),
|
||||||
uint8_t(amount & 0xFF),
|
uint8_t(amount & 0xFF),
|
||||||
uint8_t((amount >> 8) & 0xFF),
|
uint8_t((amount >> 8) & 0xFF),
|
||||||
});
|
});
|
||||||
|
|
||||||
bool hitTimeout = !cv.wait_for(lk, timeout, [&copied, &error, &amount] { return error || copied == amount; });
|
bool hitTimeout = !cv.wait_for(lk, timeout, [&copied, &error, &amount] { return error || copied == amount; });
|
||||||
com.removeMessageCallback(cb);
|
com.removeMessageCallback(cb);
|
||||||
|
|
||||||
if(hitTimeout)
|
if(hitTimeout)
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
cachedAt = std::chrono::steady_clock::now();
|
|
||||||
cachePos = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(into, cache.data(), size_t(amount));
|
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa.h"
|
||||||
|
#include "icsneo/communication/packet/ethernetpacket.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
// VSA Base Class Functions
|
||||||
|
|
||||||
|
// VSAMessage Class Functions
|
||||||
|
std::shared_ptr<Packet> VSAMessage::getPacket() const
|
||||||
|
{
|
||||||
|
auto packet = std::make_shared<Packet>();
|
||||||
|
packet->network = network;
|
||||||
|
reservePacketData(packet);
|
||||||
|
packet->data.insert(packet->data.end(), payload.begin(), payload.end());
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VSAExtendedMessage Class Functions
|
||||||
|
|
||||||
|
void VSAExtendedMessage::appendPacket(std::shared_ptr<Packet> packet) const
|
||||||
|
{
|
||||||
|
packet->data.insert(packet->data.end(), payload.begin(), payload.end());
|
||||||
|
// Set the network if not already set (Happens in AA0F records)
|
||||||
|
if(packet->network.getNetID() == Network::NetID::Invalid) {
|
||||||
|
packet->network = network;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSAExtendedMessage::truncatePacket(std::shared_ptr<Packet> packet)
|
||||||
|
{
|
||||||
|
static constexpr auto EthernetLengthOffset = 26u;
|
||||||
|
switch(packet->network.getType()) {
|
||||||
|
case Network::Type::Ethernet:
|
||||||
|
{
|
||||||
|
const auto& packetLength = *reinterpret_cast<uint16_t*>(packet->data.data() + EthernetLengthOffset);
|
||||||
|
const size_t ethernetFrameSize = packetLength - (sizeof(uint16_t) * 2);
|
||||||
|
const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + ethernetFrameSize;
|
||||||
|
packet->data.resize(bytestreamExpectedSize);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa02.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA02::VSA02(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA02);
|
||||||
|
constantIndex = *reinterpret_cast<uint16_t*>(recordBytes + 2);
|
||||||
|
flags = *reinterpret_cast<Flags*>(recordBytes + 4);
|
||||||
|
pieceCount = recordBytes[5];
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
|
||||||
|
samples.insert(samples.end(), recordBytes + 14, recordBytes + 30);
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA02::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa03.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA03::VSA03(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA03);
|
||||||
|
eventType = static_cast<EventType>(*reinterpret_cast<uint16_t*>(recordBytes + 2));
|
||||||
|
eventData = *reinterpret_cast<uint16_t*>(recordBytes + 4);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA03::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 7; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa04.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA04::VSA04(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA04);
|
||||||
|
flags = *reinterpret_cast<Flags*>(recordBytes + 2);
|
||||||
|
partitionIndex = *reinterpret_cast<uint16_t*>(recordBytes + 4);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA04::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 7; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa05.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA05::VSA05(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA05);
|
||||||
|
errorType = static_cast<ErrorType>(*reinterpret_cast<uint16_t*>(recordBytes + 2));
|
||||||
|
errorNetwork = *reinterpret_cast<uint16_t*>(recordBytes + 4);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA05::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 7; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa06.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA06::VSA06(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA06);
|
||||||
|
savedSectors.insert(savedSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 2), reinterpret_cast<uint32_t*>(recordBytes + 18));
|
||||||
|
error = *reinterpret_cast<uint16_t*>(recordBytes + 18);
|
||||||
|
savedSectorsHigh = *reinterpret_cast<uint16_t*>(recordBytes + 20);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA06::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa07.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA07::VSA07(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA07);
|
||||||
|
lastSector = *reinterpret_cast<uint32_t*>(recordBytes + 2);
|
||||||
|
currentSector = *reinterpret_cast<uint32_t*>(recordBytes + 6);
|
||||||
|
reserved.insert(reserved.end(), recordBytes + 10, recordBytes + 22);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA07::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa08.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA08::VSA08(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA08);
|
||||||
|
troubleSramCount.insert(troubleSramCount.end(), recordBytes + 2, recordBytes + 6);
|
||||||
|
troubleSectors.insert(troubleSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 6), reinterpret_cast<uint32_t*>(recordBytes + 20));
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA08::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa09.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA09::VSA09(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA09);
|
||||||
|
serialNumber = *reinterpret_cast<uint32_t*>(recordBytes + 2);
|
||||||
|
firmwareMajorVersion = recordBytes[6];
|
||||||
|
firmwareMinorVersion = recordBytes[7];
|
||||||
|
manufactureMajorRevision = recordBytes[8];
|
||||||
|
manufactureMinorRevision = recordBytes[9];
|
||||||
|
bootloaderMajorVersion = recordBytes[10];
|
||||||
|
bootloaderMinorVersion = recordBytes[11];
|
||||||
|
reserved0.insert(reserved0.end(), recordBytes + 12, recordBytes + 18);
|
||||||
|
hardwareID = static_cast<HardwareID>(recordBytes[18]);
|
||||||
|
reserved1.insert(reserved1.end(), recordBytes + 19, recordBytes + 22);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA09::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa0b.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
static constexpr auto PayloadOffset = 4;
|
||||||
|
|
||||||
|
VSA0B::VSA0B(uint8_t* const recordBytes)
|
||||||
|
: VSAMessage(recordBytes + PayloadOffset, CoreMiniPayloadSize, static_cast<Network::CoreMini>(recordBytes[29]))
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA0B);
|
||||||
|
captureBitfield = reinterpret_cast<uint16_t*>(recordBytes)[1];
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
|
||||||
|
reserved = recordBytes[28];
|
||||||
|
checksum = reinterpret_cast<uint16_t*>(recordBytes)[15];
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0B::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSA0B::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
|
||||||
|
{
|
||||||
|
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
|
||||||
|
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
|
||||||
|
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa0c.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
VSA0C::VSA0C(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA0C);
|
||||||
|
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 2);
|
||||||
|
audioPreamble = recordBytes[4];
|
||||||
|
audioHeader = recordBytes[5];
|
||||||
|
pcmData.insert(pcmData.end(), recordBytes + 6, recordBytes + 20);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
|
||||||
|
vNetBitfield = *reinterpret_cast<VSA0C::VNet*>(recordBytes + 28);
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0C::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa0d.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
static constexpr auto FirstPayloadOffset = 8;
|
||||||
|
static constexpr auto FirstPayloadSize = 12;
|
||||||
|
static constexpr auto ConsecutivePayloadOffset = 4;
|
||||||
|
static constexpr auto LastPayloadSize = 24;
|
||||||
|
static constexpr auto OtherPayloadSize = 28;
|
||||||
|
|
||||||
|
// Parent class functions
|
||||||
|
|
||||||
|
VSA0D::VSA0D(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
|
||||||
|
: VSAExtendedMessage(messageBytes, numBytes, networkId)
|
||||||
|
{
|
||||||
|
static constexpr auto DWordSize = 4;
|
||||||
|
setType(VSA::Type::AA0D);
|
||||||
|
setIndex(*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0x01FFu);
|
||||||
|
setSequenceNum((*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0xFE00u) >> 9);
|
||||||
|
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data());
|
||||||
|
for(size_t i = 0; i < payload.size() / DWordSize; i++) {
|
||||||
|
runningChecksum += dwords[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First Record Functions
|
||||||
|
|
||||||
|
VSA0DFirst::VSA0DFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
|
||||||
|
: VSA0D(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum, static_cast<Network::CoreMini>(recordBytes[29]))
|
||||||
|
{
|
||||||
|
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
|
||||||
|
setRecordCount(*reinterpret_cast<uint16_t*>(recordBytes + 6));
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
|
||||||
|
vNetInfo = *reinterpret_cast<VNet*>(recordBytes + 28);
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
uint32_t* const timestampDWords = reinterpret_cast<uint32_t*>(timestamp);
|
||||||
|
runningChecksum += timestampDWords[0];
|
||||||
|
runningChecksum += timestampDWords[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0DFirst::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0DFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
|
||||||
|
{
|
||||||
|
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
|
||||||
|
packet->data.reserve(numMessageBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0DFirst::reorderPayload(std::vector<uint8_t>& secondPayload)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> tempPayload;
|
||||||
|
tempPayload.insert(tempPayload.end(), secondPayload.begin(), secondPayload.begin() + 4);
|
||||||
|
uint8_t* timestampBytes = reinterpret_cast<uint8_t*>(×tamp);
|
||||||
|
if(timestampIsExtended) {
|
||||||
|
timestampBytes[7] += 0x80;
|
||||||
|
}
|
||||||
|
tempPayload.insert(tempPayload.end(), timestampBytes, timestampBytes + 8);
|
||||||
|
tempPayload.insert(tempPayload.end(), secondPayload.begin() + 4, secondPayload.end());
|
||||||
|
payload.clear();
|
||||||
|
secondPayload.clear();
|
||||||
|
payload.insert(payload.end(), tempPayload.begin(), tempPayload.begin() + 12); // This is done because the capacity of payload is already 12
|
||||||
|
secondPayload.insert(secondPayload.end(), tempPayload.begin() + 12, tempPayload.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSA0DFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
|
||||||
|
{
|
||||||
|
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
|
||||||
|
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
|
||||||
|
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consecutive Record Functions
|
||||||
|
|
||||||
|
VSA0DConsecutive::VSA0DConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0DFirst> first, bool isLastRecord)
|
||||||
|
: VSA0D(recordBytes, recordBytes + ConsecutivePayloadOffset, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
|
||||||
|
{
|
||||||
|
this->first = first;
|
||||||
|
calculatedChecksum = runningChecksum;
|
||||||
|
if(getIndex() == 1) {
|
||||||
|
first->reorderPayload(payload);
|
||||||
|
} else if(isLastRecord) {
|
||||||
|
recordChecksum = *reinterpret_cast<uint32_t*>(recordBytes + 28);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
} else {
|
||||||
|
setChecksumFailed(first->getChecksumFailed());
|
||||||
|
}
|
||||||
|
setRecordCount(first->getRecordCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0DConsecutive::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
setChecksumFailed(recordBytes && calculatedChecksum != recordChecksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa0e.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
static constexpr auto FirstPayloadOffset = 10;
|
||||||
|
static constexpr auto FirstPayloadSize = 10;
|
||||||
|
static constexpr auto ConsecutivePayloadOffset = 4;
|
||||||
|
static constexpr auto LastPayloadSize = 24;
|
||||||
|
static constexpr auto OtherPayloadSize = 28;
|
||||||
|
|
||||||
|
// Parent class functions
|
||||||
|
|
||||||
|
VSA0E::VSA0E(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
|
||||||
|
: VSAExtendedMessage(messageBytes, numBytes, networkId)
|
||||||
|
{
|
||||||
|
static constexpr auto DWordSize = 4;
|
||||||
|
setType(VSA::Type::AA0E);
|
||||||
|
setIndex(static_cast<uint16_t>(recordBytes[2]));
|
||||||
|
setSequenceNum(static_cast<uint16_t>(recordBytes[3]));
|
||||||
|
if(getIndex() == 0) {
|
||||||
|
runningChecksum = (static_cast<uint32_t>(payload[0]) << 16) | (static_cast<uint32_t>(payload[1]) << 24);
|
||||||
|
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data() + 2);
|
||||||
|
for(size_t i = 0; i < (payload.size() - 2) / DWordSize; i++) {
|
||||||
|
runningChecksum += dwords[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data());
|
||||||
|
for(size_t i = 0; i < payload.size() / DWordSize; i++) {
|
||||||
|
runningChecksum += dwords[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First Record Functions
|
||||||
|
|
||||||
|
VSA0EFirst::VSA0EFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
|
||||||
|
: VSA0E(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum,
|
||||||
|
static_cast<Network::CoreMini>(*reinterpret_cast<uint16_t*>(recordBytes + 28)))
|
||||||
|
{
|
||||||
|
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
|
||||||
|
setRecordCount(*reinterpret_cast<uint32_t*>(recordBytes + 6));
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
|
||||||
|
timestampIsExtended = (bool)(*reinterpret_cast<uint64_t*>(recordBytes + 20) & (0x8000000000000000));
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0EFirst::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for(size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0EFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
|
||||||
|
{
|
||||||
|
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
|
||||||
|
packet->data.reserve(numMessageBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSA0EFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
|
||||||
|
{
|
||||||
|
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
|
||||||
|
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
|
||||||
|
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0EFirst::reorderPayload(std::vector<uint8_t>& secondPayload)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> tempPayload;
|
||||||
|
tempPayload.insert(tempPayload.end(), payload.begin(), payload.end());
|
||||||
|
tempPayload.insert(tempPayload.end(), secondPayload.begin(), secondPayload.begin() + 6);
|
||||||
|
uint8_t* timestampBytes = reinterpret_cast<uint8_t*>(×tamp);
|
||||||
|
if(timestampIsExtended) {
|
||||||
|
timestampBytes[7] += 0x80;
|
||||||
|
}
|
||||||
|
tempPayload.insert(tempPayload.end(), timestampBytes, timestampBytes + 8);
|
||||||
|
tempPayload.insert(tempPayload.end(), secondPayload.begin() + 6, secondPayload.end());
|
||||||
|
payload.clear();
|
||||||
|
secondPayload.clear();
|
||||||
|
payload.insert(payload.end(), tempPayload.begin(), tempPayload.begin() + 10); // This is done because the capacity of payload is already 10
|
||||||
|
secondPayload.insert(secondPayload.end(), tempPayload.begin() + 10, tempPayload.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consecutive Record Functions
|
||||||
|
|
||||||
|
VSA0EConsecutive::VSA0EConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0EFirst> first, bool isLastRecord)
|
||||||
|
: VSA0E(recordBytes, recordBytes + ConsecutivePayloadOffset, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
|
||||||
|
{
|
||||||
|
this->first = first;
|
||||||
|
calculatedChecksum = runningChecksum;
|
||||||
|
if(getIndex() == 1) {
|
||||||
|
first->reorderPayload(payload);
|
||||||
|
} else if(isLastRecord) {
|
||||||
|
recordChecksum = *reinterpret_cast<uint32_t*>(recordBytes + 28);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
} else {
|
||||||
|
setChecksumFailed(first->getChecksumFailed());
|
||||||
|
}
|
||||||
|
setRecordCount(first->getRecordCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0EConsecutive::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
setChecksumFailed(recordBytes && calculatedChecksum != recordChecksum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa0f.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
static constexpr auto FirstPayloadOffset = 18;
|
||||||
|
static constexpr auto FirstPayloadSize = 14;
|
||||||
|
static constexpr auto LastPayloadSize = 24;
|
||||||
|
static constexpr auto OtherPayloadSize = 28;
|
||||||
|
|
||||||
|
// Parent class functions
|
||||||
|
|
||||||
|
VSA0F::VSA0F(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
|
||||||
|
: VSAExtendedMessage(messageBytes, numBytes, networkId)
|
||||||
|
{
|
||||||
|
static constexpr auto DWordSize = 4;
|
||||||
|
setType(VSA::Type::AA0F);
|
||||||
|
setIndex(*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0x01FFu);
|
||||||
|
setSequenceNum((*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0xFE00u) >> 9);
|
||||||
|
if(getIndex() == 0) {
|
||||||
|
runningChecksum = (static_cast<uint32_t>(payload[0]) << 16) | (static_cast<uint32_t>(payload[1]) << 24);
|
||||||
|
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data() + 2);
|
||||||
|
for (size_t i = 0; i < (payload.size() - 2) / DWordSize; i++) {
|
||||||
|
runningChecksum += dwords[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t* dwords = reinterpret_cast<uint32_t*>(recordBytes);
|
||||||
|
for (size_t i = 0; i < 8; i++) {
|
||||||
|
runningChecksum += dwords[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First Record Functions
|
||||||
|
|
||||||
|
VSA0FFirst::VSA0FFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
|
||||||
|
: VSA0F(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum)
|
||||||
|
{
|
||||||
|
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
|
||||||
|
uint16_t byteCount = *reinterpret_cast<uint16_t*>(recordBytes + 6);
|
||||||
|
uint16_t recordCount;
|
||||||
|
if(byteCount <= FirstPayloadSize) {
|
||||||
|
recordCount = 1;
|
||||||
|
} else if(byteCount <= FirstPayloadSize + LastPayloadSize) {
|
||||||
|
recordCount = 2;
|
||||||
|
} else {
|
||||||
|
byteCount -= FirstPayloadSize + LastPayloadSize;
|
||||||
|
recordCount = 2 + byteCount / OtherPayloadSize;
|
||||||
|
if (byteCount % OtherPayloadSize != 0) {
|
||||||
|
recordCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setRecordCount(recordCount);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 8) & UINT63_MAX;
|
||||||
|
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 16);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
// Network ID is not found in first record for AA0F
|
||||||
|
// Only the subsequent records have the Network ID in the payload
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0FFirst::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
|
||||||
|
uint16_t sum = 0;
|
||||||
|
for (size_t i = 0; i < 15; i++) {
|
||||||
|
sum += words[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0FFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
|
||||||
|
{
|
||||||
|
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
|
||||||
|
packet->data.reserve(numMessageBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSA0FFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
|
||||||
|
{
|
||||||
|
if(filter->captureBitfield != captureBitfield ||
|
||||||
|
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
|
||||||
|
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consecutive Record Functions
|
||||||
|
|
||||||
|
VSA0FConsecutive::VSA0FConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0FFirst> first, bool isLastRecord)
|
||||||
|
: VSA0F(recordBytes, recordBytes + 4, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
|
||||||
|
{
|
||||||
|
this->first = first;
|
||||||
|
calculatedChecksum = runningChecksum;
|
||||||
|
if(isLastRecord) {
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
} else {
|
||||||
|
network = Network(static_cast<Network::CoreMini>(*reinterpret_cast<uint16_t*>(recordBytes + 28))); // Network ID is stored in 25th and 26th recordBytes of payload
|
||||||
|
}
|
||||||
|
setRecordCount(first->getRecordCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA0FConsecutive::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
setChecksumFailed(recordBytes && calculatedChecksum != 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsa6a.h"
|
||||||
|
#include "icsneo/disk/diskdriver.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
static constexpr auto PayloadOffset = 56;
|
||||||
|
static constexpr auto PayloadSize = 452;
|
||||||
|
static constexpr auto TimestampOffset = 48;
|
||||||
|
static constexpr auto TimestampSize = 8;
|
||||||
|
|
||||||
|
VSA6A::VSA6A(uint8_t* const recordBytes)
|
||||||
|
: VSA()
|
||||||
|
{
|
||||||
|
setType(VSA::Type::AA6A);
|
||||||
|
|
||||||
|
sequenceNum = *reinterpret_cast<uint32_t*>(recordBytes + 34);
|
||||||
|
totalSectors = *reinterpret_cast<uint32_t*>(recordBytes + 38);
|
||||||
|
reserved = *reinterpret_cast<uint32_t*>(recordBytes + 42);
|
||||||
|
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 46) & UINT63_MAX;
|
||||||
|
timestampSum = *reinterpret_cast<uint16_t*>(recordBytes + 54);
|
||||||
|
data.insert(data.end(), recordBytes + 56, recordBytes + 508);
|
||||||
|
checksum = *reinterpret_cast<uint32_t*>(recordBytes + 508);
|
||||||
|
doChecksum(recordBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSA6A::doChecksum(uint8_t* recordBytes)
|
||||||
|
{
|
||||||
|
uint32_t sum = 0;
|
||||||
|
for(size_t i = PayloadOffset; i < PayloadOffset+ PayloadSize; i++) {
|
||||||
|
sum += recordBytes[i];
|
||||||
|
}
|
||||||
|
uint16_t tSum = 0;
|
||||||
|
for(size_t i = TimestampOffset; i < TimestampOffset + TimestampSize; i++) {
|
||||||
|
tSum += recordBytes[i];
|
||||||
|
}
|
||||||
|
setChecksumFailed(sum != checksum || tSum != timestampSum);
|
||||||
|
}
|
||||||
@@ -0,0 +1,460 @@
|
|||||||
|
#include "icsneo/disk/vsa/vsaparser.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa02.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa03.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa04.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa05.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa06.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa07.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa08.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa09.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa0b.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa0c.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa0d.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa0e.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa0f.h"
|
||||||
|
#include "icsneo/disk/vsa/vsa6a.h"
|
||||||
|
#include "icsneo/disk/diskdriver.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
bool VSAParser::parseBytes(uint8_t* const bytes, uint64_t arrLen)
|
||||||
|
{
|
||||||
|
uint64_t bytesOffset = 0;
|
||||||
|
while(bytesOffset + VSA::StandardRecordSize <= arrLen) { // Enough bytes to read for Standard Record
|
||||||
|
if(bytes[bytesOffset] != 0xAAu) {
|
||||||
|
// Invalid Input
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch(bytes[bytesOffset + 1]) {
|
||||||
|
case 0x00u: // Pad Record
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x01u: // Message Data (Deprecated)
|
||||||
|
hasDeprecatedRecords = true;
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x02u: // Logdata Record
|
||||||
|
if(settings.extractAA02) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA02>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x03u: // Event Record
|
||||||
|
if(settings.extractAA03) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA03>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x04u: // Partition Info Record
|
||||||
|
if(settings.extractAA04) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA04>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x05u: // Application Error Record
|
||||||
|
if(settings.extractAA05) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA05>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x06u: // Debug/Internal
|
||||||
|
if(settings.extractAA06) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA06>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x07u: // Debug/Internal
|
||||||
|
if(settings.extractAA07) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA07>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x08u: // Buffer Info Record
|
||||||
|
if(settings.extractAA08) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA08>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x09u: // Device Info Record
|
||||||
|
if(settings.extractAA09) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA09>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x0Au: // Logger Info Configuration (Deprecated)
|
||||||
|
hasDeprecatedRecords = true;
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x0Bu: // Message Data
|
||||||
|
if(settings.extractAA0B) {
|
||||||
|
auto record = std::make_shared<VSA0B>(bytes + bytesOffset);
|
||||||
|
vsaRecords.push_back(record);
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x0Cu: // PCM Audio Data
|
||||||
|
if(settings.extractAA0C) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA0C>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x0Du: // Message Data (Extended)
|
||||||
|
if(settings.extractAA0D) {
|
||||||
|
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0D)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x0Eu: // Message Data (Extended)
|
||||||
|
if(settings.extractAA0E) {
|
||||||
|
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0E)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x0Fu: // Message Data (Extended)
|
||||||
|
if(settings.extractAA0F) {
|
||||||
|
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0F)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytesOffset += VSA::StandardRecordSize;
|
||||||
|
break;
|
||||||
|
case 0x6Au: // Logger Configuration Backup
|
||||||
|
if(bytesOffset + Disk::SectorSize <= arrLen) {
|
||||||
|
if(settings.extractAA6A) {
|
||||||
|
vsaRecords.push_back(std::make_shared<VSA6A>(bytes + bytesOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytesOffset += Disk::SectorSize;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unhandled VSA Record Type
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSAParser::handleExtendedRecord(uint8_t* const bytes, uint64_t& bytesOffset, VSA::Type type)
|
||||||
|
{
|
||||||
|
// Gather info about the extended record sequence of the record contained in bytes
|
||||||
|
std::shared_ptr<VSAExtendedMessage> first;
|
||||||
|
uint16_t seqNum;
|
||||||
|
ExtendedMessageState::ExtendedRecordSeqInfo* seqInfo;
|
||||||
|
uint32_t runningChecksum = 0;
|
||||||
|
switch(type) {
|
||||||
|
case VSA::Type::AA0D:
|
||||||
|
first = std::make_shared<VSA0DFirst>(bytes + bytesOffset, runningChecksum);
|
||||||
|
seqNum = first->getSequenceNum();
|
||||||
|
seqInfo = &state.vsa0DSeqInfo[seqNum];
|
||||||
|
break;
|
||||||
|
case VSA::Type::AA0E:
|
||||||
|
first = std::make_shared<VSA0EFirst>(bytes + bytesOffset, runningChecksum);
|
||||||
|
seqNum = first->getSequenceNum();
|
||||||
|
seqInfo = &state.vsa0ESeqInfo[seqNum];
|
||||||
|
break;
|
||||||
|
case VSA::Type::AA0F:
|
||||||
|
first = std::make_shared<VSA0FFirst>(bytes + bytesOffset, runningChecksum);
|
||||||
|
seqNum = first->getSequenceNum();
|
||||||
|
seqInfo = &state.vsa0FSeqInfo[seqNum];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false; // Invalid type was passed
|
||||||
|
}
|
||||||
|
|
||||||
|
if(seqInfo->nextIndex == 0 && seqInfo->records.size() == 0) { // This is the first record in the sequence
|
||||||
|
if(first->getIndex() != 0) {
|
||||||
|
seqInfo->clear();
|
||||||
|
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
|
||||||
|
return true; // This is not actually the first record
|
||||||
|
}
|
||||||
|
seqInfo->records.push_back(first);
|
||||||
|
seqInfo->totalRecordCount = first->getRecordCount();
|
||||||
|
seqInfo->nextIndex++;
|
||||||
|
seqInfo->runningChecksum = runningChecksum;
|
||||||
|
} else if(seqInfo->nextIndex < seqInfo->totalRecordCount && seqInfo->records.size() > 0) { // Consecutive Record
|
||||||
|
std::shared_ptr<VSAExtendedMessage> consecutive;
|
||||||
|
bool isLast = seqInfo->nextIndex == seqInfo->totalRecordCount - 1;
|
||||||
|
|
||||||
|
// Construct the consecutive record from bytes
|
||||||
|
switch(type) {
|
||||||
|
case VSA::Type::AA0D:
|
||||||
|
consecutive = std::make_shared<VSA0DConsecutive>(
|
||||||
|
bytes + bytesOffset,
|
||||||
|
seqInfo->runningChecksum,
|
||||||
|
std::dynamic_pointer_cast<VSA0DFirst>(seqInfo->records[0]),
|
||||||
|
isLast
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case VSA::Type::AA0E:
|
||||||
|
consecutive = std::make_shared<VSA0EConsecutive>(
|
||||||
|
bytes + bytesOffset,
|
||||||
|
seqInfo->runningChecksum,
|
||||||
|
std::dynamic_pointer_cast<VSA0EFirst>(seqInfo->records[0]),
|
||||||
|
isLast
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case VSA::Type::AA0F:
|
||||||
|
consecutive = std::make_shared<VSA0FConsecutive>(
|
||||||
|
bytes + bytesOffset,
|
||||||
|
seqInfo->runningChecksum,
|
||||||
|
std::dynamic_pointer_cast<VSA0FFirst>(seqInfo->records[0]),
|
||||||
|
isLast
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(consecutive->getIndex() == seqInfo->nextIndex && consecutive->getSequenceNum() == seqNum) { // This record is valid in the sequence
|
||||||
|
seqInfo->records.push_back(consecutive);
|
||||||
|
seqInfo->nextIndex++;
|
||||||
|
} else { // Sequence is out of order/invalid
|
||||||
|
// Throw away incomplete sequence and report warning
|
||||||
|
seqInfo->clear();
|
||||||
|
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
|
||||||
|
// Save data for new sequence
|
||||||
|
if(first->getIndex() == 0) {
|
||||||
|
seqInfo->records.push_back(first);
|
||||||
|
seqInfo->totalRecordCount = first->getRecordCount();
|
||||||
|
seqInfo->nextIndex++;
|
||||||
|
seqInfo->runningChecksum = runningChecksum;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(seqInfo->nextIndex == seqInfo->totalRecordCount) { // This is the last record in the sequence
|
||||||
|
if(consecutive->getChecksumFailed()) {
|
||||||
|
// Fail out if checksum fails
|
||||||
|
seqInfo->clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
vsaRecords.insert(vsaRecords.end(), seqInfo->records.begin(), seqInfo->records.end());
|
||||||
|
seqInfo->clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false; // Undefined behavior
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VSAParser::RecordParseStatus VSAParser::getRecordFromBytes(uint8_t* const bytes, size_t arrLen, std::shared_ptr<VSA>& record)
|
||||||
|
{
|
||||||
|
record = nullptr;
|
||||||
|
if(arrLen < VSA::StandardRecordSize) {
|
||||||
|
// Not enough bytes
|
||||||
|
return VSAParser::RecordParseStatus::InsufficientData;
|
||||||
|
} else if(bytes[0] != 0xAAu) {
|
||||||
|
return VSAParser::RecordParseStatus::NotARecordStart;
|
||||||
|
} else {
|
||||||
|
switch(bytes[1]) {
|
||||||
|
case 0x00u: // Pad Record
|
||||||
|
return VSAParser::RecordParseStatus::Pad;
|
||||||
|
case 0x01u: // Message Data (Deprecated)
|
||||||
|
return VSAParser::RecordParseStatus::Deprecated;
|
||||||
|
case 0x02u: // Logdata Record
|
||||||
|
if(settings.extractAA02) {
|
||||||
|
record = std::make_shared<VSA02>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x03u: // Event Record
|
||||||
|
if(settings.extractAA03) {
|
||||||
|
record = std::make_shared<VSA03>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x04u: // Partition Info Record
|
||||||
|
if(settings.extractAA04) {
|
||||||
|
record = std::make_shared<VSA04>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x05u: // Application Error Record
|
||||||
|
if(settings.extractAA05) {
|
||||||
|
record = std::make_shared<VSA05>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x06u: // Debug/Internal
|
||||||
|
if(settings.extractAA06) {
|
||||||
|
record = std::make_shared<VSA06>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x07u: // Debug/Internal
|
||||||
|
if(settings.extractAA07) {
|
||||||
|
record = std::make_shared<VSA07>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x08u: // Buffer Info Record
|
||||||
|
if(settings.extractAA08) {
|
||||||
|
record = std::make_shared<VSA08>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x09u: // Device Info Record
|
||||||
|
if(settings.extractAA09) {
|
||||||
|
record = std::make_shared<VSA09>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x0Au:
|
||||||
|
return VSAParser::RecordParseStatus::Deprecated;
|
||||||
|
case 0x0Bu: // Message Data
|
||||||
|
if(settings.extractAA0B) {
|
||||||
|
record = std::make_shared<VSA0B>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x0Cu: // PCM Audio Data
|
||||||
|
if(settings.extractAA0C) {
|
||||||
|
record = std::make_shared<VSA0C>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x0Du: // Message Data (Extended)
|
||||||
|
if(settings.extractAA0D) {
|
||||||
|
uint32_t payloadChecksum = 0;
|
||||||
|
const auto& vsa = std::make_shared<VSA0DFirst>(bytes, payloadChecksum);
|
||||||
|
record = vsa;
|
||||||
|
if(vsa->getIndex() == 0) {
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
// This returns the consecutive record as a first record
|
||||||
|
return VSAParser::RecordParseStatus::ConsecutiveExtended;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x0Eu: // Message Data (Extended)
|
||||||
|
if(settings.extractAA0E) {
|
||||||
|
uint32_t payloadChecksum = 0;
|
||||||
|
const auto& vsa = std::make_shared<VSA0EFirst>(bytes, payloadChecksum);
|
||||||
|
record = vsa;
|
||||||
|
if(vsa->getIndex() == 0) {
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
// This returns the consecutive record as a first record
|
||||||
|
return VSAParser::RecordParseStatus::ConsecutiveExtended;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x0Fu: // Message Data (Extended)
|
||||||
|
if(settings.extractAA0F) {
|
||||||
|
uint32_t payloadChecksum = 0;
|
||||||
|
const auto& vsa = std::make_shared<VSA0FFirst>(bytes, payloadChecksum);
|
||||||
|
record = vsa;
|
||||||
|
if(vsa->getIndex() == 0) {
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
// This returns the consecutive record as a first record
|
||||||
|
return VSAParser::RecordParseStatus::ConsecutiveExtended;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x6Au: // Logger Configuration Backup
|
||||||
|
if(settings.extractAA6A) {
|
||||||
|
if(arrLen < Disk::SectorSize) {
|
||||||
|
return VSAParser::RecordParseStatus::InsufficientData;
|
||||||
|
}
|
||||||
|
record = std::make_shared<VSA6A>(bytes);
|
||||||
|
return VSAParser::RecordParseStatus::Success;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unhandled VSA Record Type
|
||||||
|
return VSAParser::RecordParseStatus::UnknownRecordType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return VSAParser::RecordParseStatus::FilteredOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VSAParser::clearParseState()
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
|
||||||
|
state.vsa0DSeqInfo[i].clear();
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < state.vsa0ESeqInfo.size(); i++) {
|
||||||
|
state.vsa0ESeqInfo[i].clear();
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
|
||||||
|
state.vsa0ESeqInfo[i].clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VSAParser::extractMessagePackets(std::vector<std::shared_ptr<Packet>>& packets)
|
||||||
|
{
|
||||||
|
if(settings != Settings::messageRecords()) {
|
||||||
|
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
|
||||||
|
return false; // We do not have exclusively message records
|
||||||
|
}
|
||||||
|
std::shared_ptr<Packet> packet;
|
||||||
|
bool activeExtendedMessage = false;
|
||||||
|
VSA::Type previousRecordType = VSA::Type::Invalid;
|
||||||
|
for(const auto& record : vsaRecords) {
|
||||||
|
VSA::Type activeRecordType = record->getType();
|
||||||
|
switch(activeRecordType) {
|
||||||
|
// Handle standard message records
|
||||||
|
case VSA::Type::AA0B: {
|
||||||
|
if(activeExtendedMessage) {
|
||||||
|
// Non-terminated extended message record
|
||||||
|
// There was a failure/unexpected behavior in the parsing process
|
||||||
|
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<VSAMessage> messageRecord = std::dynamic_pointer_cast<VSAMessage>(record);
|
||||||
|
if(!settings.messageFilter || messageRecord->filter(settings.messageFilter)) {
|
||||||
|
packet = messageRecord->getPacket();
|
||||||
|
packets.push_back(packet);
|
||||||
|
}
|
||||||
|
packet = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle extended message records
|
||||||
|
case VSA::Type::AA0D:
|
||||||
|
case VSA::Type::AA0E:
|
||||||
|
case VSA::Type::AA0F: {
|
||||||
|
std::shared_ptr<VSAExtendedMessage> extendedMessageRecord = std::dynamic_pointer_cast<VSAExtendedMessage>(record);
|
||||||
|
|
||||||
|
if(!activeExtendedMessage) { // Start new extended message packet
|
||||||
|
packet = extendedMessageRecord->getPacket();
|
||||||
|
activeExtendedMessage = true;
|
||||||
|
previousRecordType = extendedMessageRecord->getType();
|
||||||
|
} else if(previousRecordType == activeRecordType) { // Continue existing extended message packet
|
||||||
|
extendedMessageRecord->appendPacket(packet);
|
||||||
|
if(extendedMessageRecord->getRecordCount() == static_cast<uint32_t>(extendedMessageRecord->getIndex() + 1)) { // Last record in sequence
|
||||||
|
if(!settings.messageFilter || extendedMessageRecord->filter(settings.messageFilter)) {
|
||||||
|
VSAExtendedMessage::truncatePacket(packet);
|
||||||
|
packets.push_back(packet);
|
||||||
|
}
|
||||||
|
activeExtendedMessage = false;
|
||||||
|
packet = nullptr;
|
||||||
|
previousRecordType = activeRecordType;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Non-terminated extended message record
|
||||||
|
// There was a failure/unexpected behavior in the parsing process
|
||||||
|
activeExtendedMessage = false;
|
||||||
|
packet = nullptr;
|
||||||
|
previousRecordType = VSA::Type::Invalid;
|
||||||
|
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Non-message record discovered
|
||||||
|
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vsaRecords.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -100,3 +100,24 @@ 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 message channel indexing
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
The libicsneo API allows users to input and output WAV file via receiving or transmitting A2B messages. The library provides several ways to import and export
|
||||||
|
WAV files via A2B traffic. While using the API, a user will encounter message channels being referenced as unsigned integers for indexing. A ``icsneo::A2BMessage``
|
||||||
|
object will contain both upstream and downstream channels. This implies that the number of channels that a A2B message has is twice the TDM mode. The ordering for these
|
||||||
|
channels are interleved. Therefore, a message channel index ``0`` would represent downstream channel ``0`` in the network, message channel index ``1`` would represent
|
||||||
|
upstream channel ``0`` in the network, and so on. More generally, a message channel index can be computed with the formula ``2 * CHANNEL + IS_UPSTREAM`` where channel
|
||||||
|
is the channel referred in the A2B network and ``IS_UPSTREAM`` is ``1`` when a channel is upstream and ``0`` if downstream.
|
||||||
|
|
||||||
|
One area where these message channel indexes are used are when specifying a ``icsneo::ChannelMap`` for WAV transmit or receive. When transmitting a WAV file, the
|
||||||
|
map will map message channels to the input WAV file. For receiving a WAV file, the map will map the output WAV channels to channels from received messages.
|
||||||
|
|
||||||
|
EX: If we want to transmit, we will need to construct a mapping from message channels to the input WAV file. So, if we have a monotone WAV file that we are inputting
|
||||||
|
through the API, then we can map channel ``2`` upstream to channel 0 in the input WAV (the only channel in the WAV file) with the following:
|
||||||
|
|
||||||
|
``icsneo::ChannelMap chMap``
|
||||||
|
``chMap[5] = 0``
|
||||||
|
|
||||||
|
Since we are transmitting, we must map our desired A2B message channels to the input WAV file. We have ``0`` representing the single channel in the WAV file and
|
||||||
|
``5`` representing channel ``2`` upstream in the A2B message from using the formula above ``2 * CHANNEL + IS_UPSTREAM = 2 * 2 + 1 = 5``.
|
||||||
|
|||||||
+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-2024, 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-2024, 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,14 @@
|
|||||||
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)
|
||||||
|
option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA 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 +18,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 +34,30 @@ 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_CPP_VSA_EXAMPLE)
|
||||||
|
add_subdirectory(cpp/vsa)
|
||||||
|
endif()
|
||||||
|
|
||||||
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
|
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
|
||||||
# add_subdirectory(csharp)
|
# add_subdirectory(csharp)
|
||||||
# endif()
|
# endif()
|
||||||
|
|||||||
@@ -1,16 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.2)
|
|
||||||
project(libicsneoc-interactive-example VERSION 0.2.0)
|
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
# Include libicsneo's include directory
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
|
|
||||||
|
|
||||||
if(UNIX)
|
|
||||||
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(libicsneoc-interactive-example src/main.c)
|
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>
|
||||||
@@ -166,7 +166,9 @@ char getCharInput(int numArgs, ...) {
|
|||||||
va_end(vaList);
|
va_end(vaList);
|
||||||
|
|
||||||
while(!found) {
|
while(!found) {
|
||||||
fgets(input, 99, stdin);
|
if(fgets(input, 99, stdin) == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if(strlen(input) == 2) {
|
if(strlen(input) == 2) {
|
||||||
for(int i = 0; i < numArgs; ++i) {
|
for(int i = 0; i < numArgs; ++i) {
|
||||||
if(input[0] == *(list + i)) {
|
if(input[0] == *(list + i)) {
|
||||||
@@ -219,21 +221,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 +489,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 +789,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,4 @@
|
|||||||
|
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,95 @@
|
|||||||
|
/*
|
||||||
|
NOTE: This example is written for a RED 2 device. To use another device type,
|
||||||
|
change the union lookup type in the pSettings structure to match your device!
|
||||||
|
e.g. pSettings.Settings.red2 -> pSettings.Settings.fire3
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#if defined _WIN32
|
||||||
|
#include "icsneo/platform/windows.h"
|
||||||
|
#define SLEEP(msecs) Sleep(msecs)
|
||||||
|
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||||
|
#include <time.h>
|
||||||
|
#define SLEEP(msecs) do { \
|
||||||
|
struct timespec ts; \
|
||||||
|
ts.tv_sec = msecs/1000; \
|
||||||
|
ts.tv_nsec = msecs%1000*1000; \
|
||||||
|
nanosleep(&ts, NULL); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#error "Platform unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get the PRIu64 macro for timestamps
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
// Include icsneo/icsneolegacy.h to access library functions
|
||||||
|
#include "icsneo/icsneolegacy.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int ver = icsneoGetDLLVersion();
|
||||||
|
printf("ICS icsneolegacy.dll version %u\n\n", ver);
|
||||||
|
// Find and attempt to open device
|
||||||
|
//legacy open device
|
||||||
|
int numDevices = 255;
|
||||||
|
NeoDeviceEx devices[255] = {0};
|
||||||
|
void* hObject = NULL; // holds a handle to the neoVI object
|
||||||
|
int iRetVal = 0;
|
||||||
|
int iResult = 0;
|
||||||
|
SDeviceSettings pSettings = {0};
|
||||||
|
|
||||||
|
iRetVal = icsneoFindDevices(devices, &numDevices, NULL, 0, NULL, 0);
|
||||||
|
if(iRetVal && numDevices > 0) {
|
||||||
|
// Attempt to open the selected device, enable message polling, and go online
|
||||||
|
iRetVal = icsneoOpenDevice(&devices[0], &hObject, NULL, 1, 0, NULL, 0);
|
||||||
|
if(iRetVal) {
|
||||||
|
puts("Device found and opened!\n");
|
||||||
|
} else {
|
||||||
|
puts("Device found but failed to open!\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
puts("No new devices found!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get device settings
|
||||||
|
{
|
||||||
|
iRetVal = icsneoValidateHObject(hObject);
|
||||||
|
iRetVal = icsneoGetDeviceSettings(hObject, &pSettings, sizeof(SRed2Settings), 0);
|
||||||
|
if(iRetVal) {
|
||||||
|
puts("Settings read successfully\n");
|
||||||
|
printf("Baudrate: %u\n", pSettings.Settings.red2.lin1.Baudrate);
|
||||||
|
printf("Master resistor: %s \n", (pSettings.Settings.red2.lin1.MasterResistor == 0 ? "true" : "false"));
|
||||||
|
} else {
|
||||||
|
puts("Settings not read!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SLEEP(1000);
|
||||||
|
// set device settings
|
||||||
|
{
|
||||||
|
pSettings.Settings.red2.lin1.Baudrate = 19200;
|
||||||
|
pSettings.Settings.red2.lin1.MasterResistor = 1;
|
||||||
|
iRetVal = icsneoSetDeviceSettings(hObject, &pSettings, sizeof(pSettings), 1, 0);
|
||||||
|
if(iRetVal) {
|
||||||
|
puts("Settings set successfully\n");
|
||||||
|
iRetVal = icsneoGetDeviceSettings(hObject, &pSettings, sizeof(SRed2Settings), 0);
|
||||||
|
printf("Baudrate: %u\n", pSettings.Settings.red2.lin1.Baudrate);
|
||||||
|
printf("Master resistor: %s\n", (pSettings.Settings.red2.lin1.MasterResistor == 0 ? "true" : "false"));
|
||||||
|
} else {
|
||||||
|
puts("Settings not set!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int iNumberOfErrors = 0;
|
||||||
|
// Attempt to close the device
|
||||||
|
{
|
||||||
|
// Close Communication
|
||||||
|
iResult = icsneoClosePort(hObject, &iNumberOfErrors);
|
||||||
|
}
|
||||||
|
puts("Exiting program\n");
|
||||||
|
return iResult;
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#if defined _WIN32
|
||||||
|
#include "icsneo/platform/windows.h"
|
||||||
|
#define SLEEP(msecs) Sleep(msecs)
|
||||||
|
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||||
|
#include <time.h>
|
||||||
|
#define SLEEP(msecs) do { \
|
||||||
|
struct timespec ts; \
|
||||||
|
ts.tv_sec = msecs/1000; \
|
||||||
|
ts.tv_nsec = msecs%1000*1000; \
|
||||||
|
nanosleep(&ts, NULL); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#error "Platform unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get the PRIu64 macro for timestamps
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
// Include icsneo/icsneolegacy.h to access library functions
|
||||||
|
#include "icsneo/icsneolegacy.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int ver = icsneoGetDLLVersion();
|
||||||
|
printf("ICS icsneolegacy.dll version %u\n\n", ver);
|
||||||
|
// Find and attempt to open device
|
||||||
|
//legacy open device
|
||||||
|
int numDevices = 255;
|
||||||
|
NeoDeviceEx devices[255] = {0};
|
||||||
|
void* hObject = NULL; // holds a handle to the neoVI object
|
||||||
|
int iRetVal = 0;
|
||||||
|
int iResult = 0;
|
||||||
|
|
||||||
|
iRetVal = icsneoFindDevices(devices, &numDevices, NULL, 0, NULL, 0);
|
||||||
|
if(iRetVal && numDevices < 0) {
|
||||||
|
// Attempt to open the selected device, enable message polling, and go online
|
||||||
|
iRetVal = icsneoOpenDevice(&devices[0], &hObject, NULL, 1, 0, NULL, 0);
|
||||||
|
if(iRetVal) {
|
||||||
|
printf("Device found and opened!\n");
|
||||||
|
} else {
|
||||||
|
printf("Device found but failed to open!\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("No new devices found!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send message LIN
|
||||||
|
{
|
||||||
|
//lin responder frame
|
||||||
|
icsSpyMessageJ1850 msg1 = {0};
|
||||||
|
int lNetworkID;
|
||||||
|
msg1.Protocol = SPY_PROTOCOL_LIN;
|
||||||
|
msg1.StatusBitField = 0;
|
||||||
|
msg1.StatusBitField2 = 0;
|
||||||
|
lNetworkID = NETID_LIN2;
|
||||||
|
msg1.Header[0] = 0x11; //protected ID
|
||||||
|
msg1.Header[1] = 0xaa;
|
||||||
|
msg1.Header[2] = 0xbb;
|
||||||
|
msg1.Data[0] = 0xcc;
|
||||||
|
msg1.Data[1] = 0xdd;
|
||||||
|
msg1.Data[2] = 0x11;
|
||||||
|
msg1.Data[3] = 0x22;
|
||||||
|
msg1.Data[4] = 0x33;
|
||||||
|
msg1.Data[5] = 0x44;
|
||||||
|
msg1.Data[6] = 0x44; //checksum 0x33 enhanced
|
||||||
|
msg1.NumberBytesData = 7;
|
||||||
|
msg1.NumberBytesHeader = 3;
|
||||||
|
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg1, lNetworkID, 1);
|
||||||
|
if(!iRetVal)
|
||||||
|
printf("Device failed to transmit LIN responder update\n");
|
||||||
|
else
|
||||||
|
printf("Transmitted successfully!\n");
|
||||||
|
|
||||||
|
icsSpyMessageJ1850 msg2 = {0};
|
||||||
|
msg2.Protocol = SPY_PROTOCOL_LIN;
|
||||||
|
msg2.StatusBitField = SPY_STATUS_INIT_MESSAGE;
|
||||||
|
lNetworkID = NETID_LIN;
|
||||||
|
msg2.Header[0] = 0x11; //protected ID
|
||||||
|
msg2.NumberBytesData = 0;
|
||||||
|
msg2.NumberBytesHeader = 1;
|
||||||
|
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg2, lNetworkID, 1);
|
||||||
|
if(!iRetVal)
|
||||||
|
printf("Device failed to transmit LIN commander header\n");
|
||||||
|
else
|
||||||
|
printf("Transmitted successfully!\n");
|
||||||
|
|
||||||
|
SLEEP(250);
|
||||||
|
|
||||||
|
icsSpyMessageJ1850 msg3 = {0};
|
||||||
|
msg3.Protocol = SPY_PROTOCOL_LIN;
|
||||||
|
msg3.StatusBitField = SPY_STATUS_INIT_MESSAGE;
|
||||||
|
msg3.StatusBitField2 = 0;
|
||||||
|
lNetworkID = NETID_LIN;
|
||||||
|
msg3.Header[0] = 0xe2; //protected ID
|
||||||
|
msg3.Header[1] = 0x44;
|
||||||
|
msg3.Header[2] = 0x33;
|
||||||
|
msg3.Data[0] = 0x22;
|
||||||
|
msg3.Data[1] = 0x11;
|
||||||
|
msg3.Data[2] = 0x11;
|
||||||
|
msg3.Data[3] = 0x22;
|
||||||
|
msg3.Data[4] = 0x33;
|
||||||
|
msg3.Data[5] = 0x44;
|
||||||
|
msg3.Data[6] = 0xc7; //checksum
|
||||||
|
msg3.NumberBytesData = 7;
|
||||||
|
msg3.NumberBytesHeader = 3;
|
||||||
|
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg3, lNetworkID, 1);
|
||||||
|
if(!iRetVal)
|
||||||
|
printf("Device failed to transmit LIN commander message\n");
|
||||||
|
else
|
||||||
|
printf("Transmitted successfully!\n");
|
||||||
|
}
|
||||||
|
SLEEP(1000);
|
||||||
|
// Get messages
|
||||||
|
{
|
||||||
|
static icsSpyMessage rxMsg[30000];
|
||||||
|
int numMessages = 0;
|
||||||
|
int numErrors = 0;
|
||||||
|
iRetVal = icsneoGetMessages(hObject, rxMsg, &numMessages, &numErrors);
|
||||||
|
if(!iRetVal)
|
||||||
|
printf("Get Messages failed!\n");
|
||||||
|
else
|
||||||
|
for(int idx = 0; idx < numMessages; ++idx)
|
||||||
|
{
|
||||||
|
if(rxMsg[idx].Protocol == SPY_PROTOCOL_LIN) {
|
||||||
|
const icsSpyMessageJ1850* linMsg = (icsSpyMessageJ1850*)&rxMsg[idx];
|
||||||
|
size_t frameLen = (linMsg->NumberBytesHeader + linMsg->NumberBytesData);
|
||||||
|
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
|
||||||
|
if(linMsg->NetworkID == NETID_LIN) {
|
||||||
|
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen);
|
||||||
|
}
|
||||||
|
else if (linMsg->NetworkID == NETID_LIN2) {
|
||||||
|
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < dataLen; ++i) {
|
||||||
|
if (i < 2) {
|
||||||
|
printf("%02x ", linMsg->Header[i+1]);
|
||||||
|
} else {
|
||||||
|
printf("%02x ", linMsg->Data[i-2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(linMsg->NumberBytesData > 0)
|
||||||
|
printf("| Checksum: 0x%02x\n", linMsg->Data[linMsg->NumberBytesData-1]);
|
||||||
|
else
|
||||||
|
printf("| Checksum: 0x%02x\n", linMsg->Header[linMsg->NumberBytesHeader-1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int iNumberOfErrors = 0;
|
||||||
|
// Attempt to close the device
|
||||||
|
{
|
||||||
|
// Close Communication
|
||||||
|
iResult = icsneoClosePort(hObject, &iNumberOfErrors);
|
||||||
|
}
|
||||||
|
printf("Exiting program\n");
|
||||||
|
return iResult;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
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,429 @@
|
|||||||
|
// Signal to dynamically load the library
|
||||||
|
//#define ICSNEOC_DYNAMICLOAD
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
// Get the PRIu64 macro for timestamps
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
// Include icsneo/icsneoc.h to access library functions
|
||||||
|
#include "icsneo/icsneoc.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define SLEEP(msecs) Sleep(msecs)
|
||||||
|
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||||
|
#include <time.h>
|
||||||
|
#define SLEEP(msecs) do { \
|
||||||
|
struct timespec ts; \
|
||||||
|
ts.tv_sec = msecs/1000; \
|
||||||
|
ts.tv_nsec = msecs%1000*1000; \
|
||||||
|
nanosleep(&ts, NULL); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#error "Platform unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t msgLimit = 50000;
|
||||||
|
size_t numDevices = 0;
|
||||||
|
neodevice_t devices[99];
|
||||||
|
const neodevice_t* selectedDevice = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Prints all current known devices to output in the following format:
|
||||||
|
* [num] DeviceType SerialNum Connected: Yes/No Online: Yes/No Msg Polling: On/Off
|
||||||
|
*
|
||||||
|
* If any devices could not be described due to an error, they will appear in the following format:
|
||||||
|
* Description for device num not available!
|
||||||
|
*/
|
||||||
|
void printAllDevices() {
|
||||||
|
if(numDevices == 0) {
|
||||||
|
printf("No devices found! Please scan for new devices.\n");
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < numDevices; i++) {
|
||||||
|
char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 };
|
||||||
|
size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
|
||||||
|
|
||||||
|
// Updates productDescription and descriptionLength for each device
|
||||||
|
if(icsneo_describeDevice(devices + i, productDescription, &descriptionLength)) {
|
||||||
|
printf("[%zd] %s\tConnected: ", i + 1, productDescription);
|
||||||
|
if(icsneo_isOpen(devices + i)) {
|
||||||
|
printf("Yes\t");
|
||||||
|
} else printf("No\t");
|
||||||
|
|
||||||
|
printf("Online: ");
|
||||||
|
if(icsneo_isOnline(devices + i)) {
|
||||||
|
printf("Yes\t");
|
||||||
|
} else printf("No\t");
|
||||||
|
|
||||||
|
printf("Msg Polling: ");
|
||||||
|
if(icsneo_isMessagePollingEnabled(devices + i)) {
|
||||||
|
printf("On\n");
|
||||||
|
} else printf("Off\n");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
printf("Description for device %zd not available!\n", i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Scans for any new devices, adding them to devices and updating numDevices accordingly
|
||||||
|
* A total of 99 devices may be stored at once
|
||||||
|
*/
|
||||||
|
size_t scanNewDevices() {
|
||||||
|
neodevice_t newDevices[99];
|
||||||
|
size_t numNewDevices = 99;
|
||||||
|
icsneo_findAllDevices(newDevices, &numNewDevices);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < numNewDevices; ++i) {
|
||||||
|
devices[numDevices + i] = newDevices[i];
|
||||||
|
}
|
||||||
|
numDevices += numNewDevices;
|
||||||
|
return numNewDevices;
|
||||||
|
}
|
||||||
|
void printLastError() {
|
||||||
|
neoevent_t error;
|
||||||
|
if(icsneo_getLastError(&error))
|
||||||
|
printf("Error 0x%u: %s\n", error.eventNumber, error.description);
|
||||||
|
else
|
||||||
|
printf("No errors found!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets all current API events and prints them to output
|
||||||
|
* Flushes the API event cache, meaning future calls (barring any new events) will not detect any further API events
|
||||||
|
*/
|
||||||
|
void printAPIEvents() {
|
||||||
|
neoevent_t events[99];
|
||||||
|
size_t eventCount = 99;
|
||||||
|
if(icsneo_getEvents(events, &eventCount)) {
|
||||||
|
if(eventCount == 1) {
|
||||||
|
printf("1 API event found!\n");
|
||||||
|
printf("Event 0x%u: %s\n", events[0].eventNumber, events[0].description);
|
||||||
|
} else {
|
||||||
|
printf("%d API events found!\n", (int) eventCount);
|
||||||
|
for(size_t i = 0; i < eventCount; ++i) {
|
||||||
|
printf("Event 0x%u: %s\n", events[i].eventNumber, events[i].description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Failed to get API events!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets all current device events and prints them to output. If no device events were found, printAPIEvents() is called
|
||||||
|
* Flushes the device event cache, meaning future calls (barring any new events) will not detect any further device events for this device
|
||||||
|
*/
|
||||||
|
void printDeviceEvents(neodevice_t* device) {
|
||||||
|
neoevent_t events[99];
|
||||||
|
size_t eventCount = 99;
|
||||||
|
if(icsneo_getDeviceEvents(selectedDevice, events, &eventCount)) {
|
||||||
|
if(eventCount == 1) {
|
||||||
|
printf("1 device event found!\n");
|
||||||
|
printf("Event 0x%x: %s\n", events[0].eventNumber, events[0].description);
|
||||||
|
} else {
|
||||||
|
printf("%d device events found!\n", (int) eventCount);
|
||||||
|
for(size_t i = 0; i < eventCount; ++i) {
|
||||||
|
printf("Event 0x%x: %s\n", events[i].eventNumber, events[i].description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Used to check character inputs for correctness (if they are found in an expected list)
|
||||||
|
* \param[in] numArgs the number of possible options for the expected character
|
||||||
|
* \param[in] ... the possible options for the expected character
|
||||||
|
* \returns the entered character
|
||||||
|
*
|
||||||
|
* This function repeatedly prompts the user for input until a matching input is entered
|
||||||
|
* Example usage: char input = getCharInput(5, 'F', 'u', 'b', 'a', 'r');
|
||||||
|
*/
|
||||||
|
char getCharInput(int numArgs, ...) {
|
||||||
|
// 99 chars shold be more than enough to catch any typos
|
||||||
|
char input[99];
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
va_list vaList;
|
||||||
|
va_start(vaList, numArgs);
|
||||||
|
|
||||||
|
char* list = (char*) calloc(numArgs, sizeof(char));
|
||||||
|
for(int i = 0; i < numArgs; ++i) {
|
||||||
|
*(list + i) = va_arg(vaList, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(vaList);
|
||||||
|
|
||||||
|
while(!found) {
|
||||||
|
if(fgets(input, 99, stdin) == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(strlen(input) == 2) {
|
||||||
|
for(int i = 0; i < numArgs; ++i) {
|
||||||
|
if(input[0] == *(list + i)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!found) {
|
||||||
|
printf("Input did not match expected options. Please try again.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(list);
|
||||||
|
|
||||||
|
return input[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Prompts the user to select a device from the list of currently known devices
|
||||||
|
* \returns a pointer to the device in devices[] selected by the user
|
||||||
|
* Requires an input from 1-9, so a maximum of 9 devices are supported
|
||||||
|
*/
|
||||||
|
const neodevice_t* selectDevice() {
|
||||||
|
printf("Please select a device:\n");
|
||||||
|
printAllDevices();
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
size_t selectedDeviceNum = 10;
|
||||||
|
|
||||||
|
while(selectedDeviceNum > numDevices) {
|
||||||
|
char deviceSelection = getCharInput(9, '1', '2', '3', '4', '5', '6', '7', '8', '9');
|
||||||
|
if(deviceSelection < '0') {
|
||||||
|
printf("Selected device out of range!\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
selectedDeviceNum = deviceSelection - '0';
|
||||||
|
if(selectedDeviceNum > numDevices) {
|
||||||
|
printf("Selected device out of range!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
return devices + selectedDeviceNum - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
neoversion_t ver = icsneo_getVersion();
|
||||||
|
printf("ICS icsneoc version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
|
||||||
|
// Find and attempt to open device
|
||||||
|
size_t numNewDevices = scanNewDevices();
|
||||||
|
if(numNewDevices == 1) {
|
||||||
|
printf("1 new device found!\n");
|
||||||
|
} else {
|
||||||
|
printf("%d new devices found!\n", (int) numNewDevices);
|
||||||
|
}
|
||||||
|
printAllDevices();
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Select a device and get its description
|
||||||
|
if(numDevices == 0) {
|
||||||
|
printf("No devices found! Please scan for new devices.\n\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
selectedDevice = &devices[0];
|
||||||
|
|
||||||
|
// Get the product description for the device
|
||||||
|
char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 };
|
||||||
|
size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
|
||||||
|
icsneo_describeDevice(selectedDevice, productDescription, &descriptionLength);
|
||||||
|
|
||||||
|
// Attempt to open the selected device
|
||||||
|
{
|
||||||
|
if(icsneo_openDevice(selectedDevice)) {
|
||||||
|
printf("%s successfully opened!\n\n", productDescription);
|
||||||
|
} else {
|
||||||
|
printf("%s failed to open!\n\n", productDescription);
|
||||||
|
printLastError();
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Attempt to go online
|
||||||
|
{
|
||||||
|
if(icsneo_goOnline(selectedDevice)) {
|
||||||
|
printf("%s successfully went online!\n\n", productDescription);
|
||||||
|
} else {
|
||||||
|
printf("%s failed to go online!\n\n", productDescription);
|
||||||
|
printLastError();
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Attempt to enable message polling
|
||||||
|
{
|
||||||
|
if(icsneo_enableMessagePolling(selectedDevice)) {
|
||||||
|
printf("Successfully enabled message polling for %s!\n\n", productDescription);
|
||||||
|
} else {
|
||||||
|
printf("Failed to enable message polling for %s!\n\n", productDescription);
|
||||||
|
printLastError();
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Send message LIN
|
||||||
|
{
|
||||||
|
// Start generating sample msg
|
||||||
|
uint8_t sendMessageData[8];
|
||||||
|
sendMessageData[0] = 0x33;
|
||||||
|
sendMessageData[1] = 0x44;
|
||||||
|
sendMessageData[2] = 0x55;
|
||||||
|
sendMessageData[3] = 0x66;
|
||||||
|
sendMessageData[4] = 0x77;
|
||||||
|
sendMessageData[5] = 0x88;
|
||||||
|
sendMessageData[6] = 0x88;
|
||||||
|
|
||||||
|
neomessage_lin_t msg = {0};
|
||||||
|
|
||||||
|
msg.header[0] = 0x11; //protected ID
|
||||||
|
msg.header[1] = 0x11;
|
||||||
|
msg.header[2] = 0x22;
|
||||||
|
msg.length = 10;
|
||||||
|
msg.netid = ICSNEO_NETID_LIN;
|
||||||
|
msg.data = sendMessageData;
|
||||||
|
msg.linStatus.txCommander = 1;
|
||||||
|
msg.linStatus.txChecksumEnhanced = 1;
|
||||||
|
msg.type = ICSNEO_NETWORK_TYPE_LIN;
|
||||||
|
msg.checksum = 0x88;
|
||||||
|
|
||||||
|
// Attempt to transmit the sample msg
|
||||||
|
if(icsneo_transmit(selectedDevice, (const neomessage_t*) &msg)) {
|
||||||
|
printf("Message transmit successful!\n\n");
|
||||||
|
} else {
|
||||||
|
printf("Failed to transmit message to %s!\n\n", productDescription);
|
||||||
|
printLastError();
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Wait for a moment
|
||||||
|
SLEEP(1000);
|
||||||
|
// Get messages
|
||||||
|
{
|
||||||
|
// Prepare the array of neomessage_t ptrs for reading in the messages
|
||||||
|
neomessage_t* msgs = (neomessage_t*) malloc(msgLimit * sizeof(neomessage_t));
|
||||||
|
|
||||||
|
// Get messages
|
||||||
|
size_t msgCount = msgLimit;
|
||||||
|
|
||||||
|
// Attempt to get messages
|
||||||
|
if(!icsneo_getMessages(selectedDevice, msgs, &msgCount, (uint64_t) 0)) {
|
||||||
|
printf("Failed to get messages for %s!\n\n", productDescription);
|
||||||
|
printLastError();
|
||||||
|
free(msgs);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(msgCount == 1) {
|
||||||
|
printf("1 message received from %s!\n", productDescription);
|
||||||
|
} else {
|
||||||
|
printf("%d messages received from %s!\n", (int) msgCount, productDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print out the received messages
|
||||||
|
for(size_t i = 0; i < msgCount; i++) {
|
||||||
|
const neomessage_t* msg = &msgs[i];
|
||||||
|
switch(msg->messageType) {
|
||||||
|
case ICSNEO_MESSAGE_TYPE_FRAME: {
|
||||||
|
const neomessage_frame_t* frame = (neomessage_frame_t*)msg;
|
||||||
|
switch(frame->type) {
|
||||||
|
case ICSNEO_NETWORK_TYPE_CAN: {
|
||||||
|
neomessage_can_t* canMsg = (neomessage_can_t*)frame;
|
||||||
|
printf("\t0x%03x [%zu] ", canMsg->arbid, canMsg->length);
|
||||||
|
for(size_t i = 0; i < canMsg->length; i++) {
|
||||||
|
printf("%02x ", canMsg->data[i]);
|
||||||
|
}
|
||||||
|
if(canMsg->status.transmitMessage)
|
||||||
|
printf("TX%s %04x ", canMsg->status.globalError ? " ERR" : "", canMsg->description);
|
||||||
|
printf("(%"PRIu64")\n", canMsg->timestamp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ICSNEO_NETWORK_TYPE_LIN: {
|
||||||
|
neomessage_lin_t* linMsg = (neomessage_lin_t*)frame;
|
||||||
|
size_t frameLen = linMsg->length;
|
||||||
|
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
|
||||||
|
size_t numberBytesHeader = (dataLen > 1) ? 3 : 1;
|
||||||
|
size_t numberBytesData = frameLen - numberBytesHeader;
|
||||||
|
if(linMsg->netid == ICSNEO_NETID_LIN) {
|
||||||
|
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
|
||||||
|
}
|
||||||
|
else if (linMsg->netid == ICSNEO_NETID_LIN2) {
|
||||||
|
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < dataLen; ++i) {
|
||||||
|
if (i < 2) {
|
||||||
|
printf("%02x ", linMsg->header[i+1]);
|
||||||
|
} else {
|
||||||
|
printf("%02x ", linMsg->data[i-2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("| Checksum: 0x%02x\n", linMsg->checksum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ICSNEO_MESSAGE_TYPE_CAN_ERROR_COUNT: {
|
||||||
|
const neomessage_can_error_t* cec = (neomessage_can_error_t*)msg;
|
||||||
|
printf("\tCAN error counts changed, TEC=%d, REC=%d%s", cec->transmitErrorCount, cec->receiveErrorCount,
|
||||||
|
cec->status.canBusOff ? " (Bus Off)" : "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
free(msgs);
|
||||||
|
}
|
||||||
|
// Attempt to disable message polling
|
||||||
|
{
|
||||||
|
if(icsneo_disableMessagePolling(selectedDevice)) {
|
||||||
|
printf("Successfully disabled message polling for %s!\n\n", productDescription);
|
||||||
|
} else {
|
||||||
|
printf("Failed to disable message polling limit for %s!\n\n", productDescription);
|
||||||
|
printLastError();
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Attempt to go offline
|
||||||
|
{
|
||||||
|
if(icsneo_goOffline(selectedDevice)) {
|
||||||
|
printf("%s successfully went offline!\n\n", productDescription);
|
||||||
|
} else {
|
||||||
|
printf("%s failed to go offline!\n\n", productDescription);
|
||||||
|
printLastError();
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Attempt to close the device
|
||||||
|
{
|
||||||
|
if(icsneo_closeDevice(selectedDevice)) {
|
||||||
|
numDevices--;
|
||||||
|
printf("Successfully closed %s!\n\n", productDescription);
|
||||||
|
|
||||||
|
// Shifts everything after the removed device 1 index to the left
|
||||||
|
bool startResizing = false;
|
||||||
|
for(size_t i = 0; i < numDevices; ++i) {
|
||||||
|
if(selectedDevice == devices + i)
|
||||||
|
startResizing = true;
|
||||||
|
if(startResizing)
|
||||||
|
devices[i] = devices[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedDevice = NULL;
|
||||||
|
} else {
|
||||||
|
printf("Failed to close %s!\n\n", productDescription);
|
||||||
|
printLastError();
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//exit
|
||||||
|
printf("Exiting program\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
add_executable(libicsneocpp-a2b src/a2b.cpp)
|
||||||
|
target_link_libraries(libicsneocpp-a2b icsneocpp)
|
||||||
@@ -0,0 +1,458 @@
|
|||||||
|
/**
|
||||||
|
* libicsneo A2B example
|
||||||
|
*
|
||||||
|
* Example were made to be ran with RAD-A2B as main node on TDM4 16 bit channel size and one additional sub node (either an ADI board or an additional RAD-A2B).
|
||||||
|
* Follow the specific hardware instructions per example to ensure expected output. Be sure to configure the A2B network before running these examples, especially
|
||||||
|
* ones which Tx or Rx audio.
|
||||||
|
*
|
||||||
|
* Options:
|
||||||
|
* -h, --help Display help message.
|
||||||
|
* -e, --example [EXAMPLE_NUM] Example to run.
|
||||||
|
* Example usage: ./libicsneocpp-a2b.exe --example 1
|
||||||
|
* Example usage: ./libicsneocpp-a2b.exe -h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <icsneo/icsneocpp.h>
|
||||||
|
#include <icsneo/device/tree/rada2b/rada2bsettings.h>
|
||||||
|
#include <icsneo/communication/message/callback/streamoutput/a2bwavoutput.h>
|
||||||
|
#include <string>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
std::string makeWAV() {
|
||||||
|
icsneo::WAVHeader header = icsneo::WAVHeader(1, 48000, 16);
|
||||||
|
std::vector<uint8_t> sineWAVSamples = {
|
||||||
|
0xFF, 0x3F, 0x81, 0x5A, 0xD9, 0x6E, 0xA2, 0x7B, 0xFF, 0x7F, 0xA2, 0x7B, 0xD9, 0x6E, 0x81, 0x5A,
|
||||||
|
0xFF, 0x3F, 0x20, 0x21, 0x00, 0x00, 0xE0, 0xDE, 0x01, 0xC0, 0x7F, 0xA5, 0x27, 0x91, 0x5E, 0x84,
|
||||||
|
0x01, 0x80, 0x5E, 0x84, 0x27, 0x91, 0x7F, 0xA5, 0x01, 0xC0, 0xE0, 0xDe, 0x00, 0x00, 0x20, 0x21
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<uint8_t> sineWAV;
|
||||||
|
sineWAV.reserve(sineWAVSamples.size() + sizeof(header));
|
||||||
|
|
||||||
|
sineWAV.insert(sineWAV.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header));
|
||||||
|
std::copy(sineWAVSamples.begin(), sineWAVSamples.end(), std::back_inserter(sineWAV));
|
||||||
|
|
||||||
|
return std::string(sineWAV.begin(), sineWAV.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example 0: TX
|
||||||
|
*/
|
||||||
|
void example0(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||||
|
std::cout << "Transmitting a sine tone..." << std::endl;
|
||||||
|
|
||||||
|
// Create sine tone in wav format
|
||||||
|
std::string wavString = makeWAV();
|
||||||
|
|
||||||
|
std::istringstream sineWAV(wavString);
|
||||||
|
double elapsedTime = 0.0;
|
||||||
|
|
||||||
|
// Create a IWAVStream object which represents a WAV data stream
|
||||||
|
// the IWAVStream object here is initialized with an outside std::ostream,
|
||||||
|
// so it holds a reference pointer to this stream.
|
||||||
|
icsneo::IWAVStream wavStream(sineWAV);
|
||||||
|
|
||||||
|
// Create a channel map which maps each message channel to a channel in the input WAV file
|
||||||
|
icsneo::ChannelMap channelMap;
|
||||||
|
|
||||||
|
// Here we will just set every message channel to channel 0 in the WAV file
|
||||||
|
// We have 8 channels since this is TDM4 and we include both upstream and downstream
|
||||||
|
// see docs for specific message channel labeling information
|
||||||
|
|
||||||
|
for(uint8_t messageChannel = 0; messageChannel < 8; messageChannel++) {
|
||||||
|
channelMap[messageChannel] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play roughly 5 seconds of sine tone.
|
||||||
|
while(elapsedTime < 5.0) {
|
||||||
|
while(elapsedTime < 5.0) {
|
||||||
|
// If WAVStream is invalid (at EOF) break out of loop
|
||||||
|
if(!wavStream) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new message with the maximum amount of allocated frames
|
||||||
|
auto msg = std::make_shared<icsneo::A2BMessage>(
|
||||||
|
icsneo::A2BMessage::TDMMode::TDM4, /* TDM mode of the message, we use TDM4 for this whole example*/
|
||||||
|
true /* true if we want 16 bit channels in the message, false for 32 bit. This should match the RAD-A2B device setting */
|
||||||
|
);
|
||||||
|
msg->txmsg = true;
|
||||||
|
msg->network = icsneo::Network(icsneo::Network::NetID::A2B2);
|
||||||
|
|
||||||
|
// Load the WAV audio data into the desired channel, break if we fail to load
|
||||||
|
if(!msg->loadAudioBuffer(wavStream, channelMap)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmit the message
|
||||||
|
if(!rada2b->transmit(msg)) {
|
||||||
|
std::cout << "Failed to transmit." << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
elapsedTime += (static_cast<double>(msg->getNumFrames()))*1.0/48000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the WAV stream
|
||||||
|
wavStream.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example 1: RX
|
||||||
|
*/
|
||||||
|
void example1(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||||
|
std::cout << "Receiving 5 seconds of audio data..." << std::endl;
|
||||||
|
|
||||||
|
// Add WAV output message callback
|
||||||
|
// Saves samples to "out.wav"
|
||||||
|
auto handler = rada2b->addMessageCallback(
|
||||||
|
std::make_shared<icsneo::A2BWAVOutput>(
|
||||||
|
"audio16bit.wav", /* output file name */
|
||||||
|
icsneo::ChannelMap( /** channel mapping which maps our output WAV channels to the message channels from incoming messages */
|
||||||
|
{ /* See docs for specific A2B channel indexing information */
|
||||||
|
{static_cast<uint8_t>(3u), static_cast<uint8_t>(0u)}, /* Map output WAV channel 3 to channel 0 downstream of the A2B network/A2BMessage */
|
||||||
|
{static_cast<uint8_t>(2u), static_cast<uint8_t>(1u)}, /* Map output WAV channel 2 to channel 0 upstream of the A2B network/A2BMessage */
|
||||||
|
{static_cast<uint8_t>(1u), static_cast<uint8_t>(2u)}, /* Map output WAV channel 1 to channel 1 downstream of the A2B network/A2BMessage */
|
||||||
|
{static_cast<uint8_t>(0u), static_cast<uint8_t>(3u)} /* Map output WAV channel 0 to channel 1 upstream of the A2B network/A2BMessage */
|
||||||
|
}
|
||||||
|
),
|
||||||
|
icsneo::PCMType::L16, /* store samples with 16 bit resolution*/
|
||||||
|
2u, /* Number of channels in the output WAV file */
|
||||||
|
48000 /* Sample rate of WAV file */
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sleep this thread for 5 seconds, message callback still runs
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||||
|
|
||||||
|
// Make sure you send 16 bit audio data on the above message channels in the channel map
|
||||||
|
// to the RAD-A2B main node through a microphone or a different modem.
|
||||||
|
// You can configure the message channels by changing the stream config in the A2B schematic
|
||||||
|
|
||||||
|
// Remove callback
|
||||||
|
rada2b->removeMessageCallback(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example 2: RAD-A2B settings
|
||||||
|
*/
|
||||||
|
void example2(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||||
|
uint8_t numChannels;
|
||||||
|
{
|
||||||
|
// Get device settings
|
||||||
|
auto* settings = rada2b->settings.get();
|
||||||
|
auto* rada2bSettings = static_cast<icsneo::RADA2BSettings*>(settings);
|
||||||
|
|
||||||
|
// Check if monitor mode is enabled
|
||||||
|
auto type = rada2bSettings->getNodeType(icsneo::RADA2BSettings::RADA2BDevice::Monitor);
|
||||||
|
if(type == icsneo::RADA2BSettings::NodeType::Monitor) {
|
||||||
|
std::cout << "Device is in monitor mode" << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "Device is not in monitor mode" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current tdm mode
|
||||||
|
numChannels = rada2bSettings->getNumChannels(icsneo::RADA2BSettings::RADA2BDevice::Node);
|
||||||
|
|
||||||
|
std::cout << "Current num channels: " << static_cast<uint32_t>(numChannels) << std::endl;
|
||||||
|
// Set node type to master node.
|
||||||
|
rada2bSettings->setNodeType(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::NodeType::Master);
|
||||||
|
|
||||||
|
// Set TDM mode to TDM8
|
||||||
|
rada2bSettings->setTDMMode(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::TDMMode::TDM4);
|
||||||
|
|
||||||
|
// Apply local settings to device
|
||||||
|
rada2bSettings->apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example 3: Packaging and transmitting sine tone using A2BMessage API
|
||||||
|
*/
|
||||||
|
void example3(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||||
|
std::cout << "Transmitting a 1000 hz sine tone." << std::endl;
|
||||||
|
|
||||||
|
float deltaTime = static_cast<float>(1.0/48000.0);
|
||||||
|
float elapsedTime = 0.0;
|
||||||
|
float twoPI = static_cast<float>(2.0*atan(1.0)*4.0);
|
||||||
|
float frequency = 1000;
|
||||||
|
float amplitude = static_cast<float>((1 << 15) - 1);
|
||||||
|
size_t tdm = 4;
|
||||||
|
size_t bytesPerSample = 2;
|
||||||
|
|
||||||
|
size_t numFrames = 2048 / (2 * tdm * bytesPerSample);
|
||||||
|
|
||||||
|
|
||||||
|
// Play for roughly 5 seconds
|
||||||
|
while(elapsedTime < 5.0) {
|
||||||
|
// Allocate message
|
||||||
|
std::shared_ptr<icsneo::A2BMessage> a2bmsgPtr = std::make_shared<icsneo::A2BMessage>(numFrames, icsneo::A2BMessage::TDMMode::TDM4, true);
|
||||||
|
|
||||||
|
icsneo::A2BMessage& a2bmsg = *a2bmsgPtr.get();
|
||||||
|
a2bmsg.network = icsneo::Network(icsneo::Network::NetID::A2B2);
|
||||||
|
a2bmsg.txmsg = true;
|
||||||
|
|
||||||
|
for(size_t frame = 0; frame < a2bmsg.getNumFrames(); frame++) {
|
||||||
|
|
||||||
|
// Sine tone sample, amplitude 1000, frequency 1000 hz
|
||||||
|
|
||||||
|
float contSample = amplitude*sin(twoPI*frequency*elapsedTime);
|
||||||
|
icsneo::PCMSample sample = static_cast<icsneo::PCMSample>(contSample);
|
||||||
|
|
||||||
|
// Send this sine wave sample downstream on channels 0, 1, and 2
|
||||||
|
a2bmsg.setChannelSample(
|
||||||
|
icsneo::A2BMessage::Direction::Downstream,
|
||||||
|
0,
|
||||||
|
frame,
|
||||||
|
sample,
|
||||||
|
icsneo::PCMType::L16
|
||||||
|
);
|
||||||
|
|
||||||
|
a2bmsg.setChannelSample(
|
||||||
|
icsneo::A2BMessage::Direction::Downstream,
|
||||||
|
1,
|
||||||
|
frame,
|
||||||
|
sample,
|
||||||
|
icsneo::PCMType::L16
|
||||||
|
);
|
||||||
|
|
||||||
|
a2bmsg.setChannelSample(
|
||||||
|
icsneo::A2BMessage::Direction::Downstream,
|
||||||
|
2,
|
||||||
|
frame,
|
||||||
|
sample,
|
||||||
|
icsneo::PCMType::L16
|
||||||
|
);
|
||||||
|
|
||||||
|
elapsedTime+=deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmit message to device
|
||||||
|
|
||||||
|
if(!rada2b->transmit(a2bmsgPtr)) {
|
||||||
|
std::cout << "Failed to transmit." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example 4: Retrieving A2B bus status using I2C messaages.
|
||||||
|
*/
|
||||||
|
void example4(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||||
|
std::shared_ptr<icsneo::I2CMessage> msg = std::make_shared<icsneo::I2CMessage>();
|
||||||
|
std::shared_ptr<icsneo::MessageFilter> msgFilter = std::make_shared<icsneo::MessageFilter>(icsneo::Network::NetID::I2C2);
|
||||||
|
|
||||||
|
msg->network = icsneo::Network(icsneo::Network::NetID::I2C2);
|
||||||
|
msg->controlBytes.resize(1);
|
||||||
|
msg->controlBytes[0] = static_cast<uint8_t>(0x17u); // Register address for A2B INTTYPE
|
||||||
|
msg->dataBytes.resize(1, 0);
|
||||||
|
msg->direction = icsneo::I2CMessage::Direction::Read;
|
||||||
|
msg->deviceMode = icsneo::I2CMessage::DeviceMode::Controller;
|
||||||
|
msg->address = static_cast<uint16_t>(0x68); // A2B master node address.
|
||||||
|
msg->isTXMsg = true;
|
||||||
|
|
||||||
|
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::MessageCallback>(
|
||||||
|
[] (std::shared_ptr<icsneo::Message> newMsg) {
|
||||||
|
|
||||||
|
if(newMsg->type == icsneo::Message::Type::Frame) {
|
||||||
|
const auto& frame = std::dynamic_pointer_cast<icsneo::Frame>(newMsg);
|
||||||
|
if(frame && frame->network.getNetID() == icsneo::Network::NetID::I2C2) {
|
||||||
|
const auto& i2cMessage = std::dynamic_pointer_cast<icsneo::I2CMessage>(frame);
|
||||||
|
|
||||||
|
if(!i2cMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i2cMessage->controlBytes.size() == 1 && i2cMessage->direction == icsneo::I2CMessage::Direction::Read) {
|
||||||
|
if(i2cMessage->controlBytes[0] == 0x17u) {
|
||||||
|
if(i2cMessage->dataBytes.size() == 1) {
|
||||||
|
std::cout << "Current A2B bus status INTTYPE code: " << static_cast<int>(i2cMessage->dataBytes[0]) << '\n';
|
||||||
|
}
|
||||||
|
} else if(i2cMessage->controlBytes[0] == 0x03u) {
|
||||||
|
if(i2cMessage->dataBytes.size() == 1) {
|
||||||
|
std::cout << "A2B_PRODUCT register: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
|
||||||
|
}
|
||||||
|
} else if(i2cMessage->controlBytes[0] == 0x02u) {
|
||||||
|
if(i2cMessage->dataBytes.size() == 1) {
|
||||||
|
std::cout << "A2B_VENDOR register: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
|
||||||
|
}
|
||||||
|
} else if(i2cMessage->controlBytes[0] == 0x1C) {
|
||||||
|
if(i2cMessage->dataBytes.size() == 1) {
|
||||||
|
std::cout << "A2B_INTMSK1 register value: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, msgFilter));
|
||||||
|
if(!rada2b->transmit(msg)) {
|
||||||
|
std::cout << "Failed to transmit." << std::endl;
|
||||||
|
}
|
||||||
|
msg->controlBytes[0] = 0x03; // Address for A2B_PRODUCT register
|
||||||
|
|
||||||
|
if(!rada2b->transmit(msg)) {
|
||||||
|
std::cout << "Failed to transmit." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg->controlBytes[0] = 0x02; // Address for A2B_VENDOR register
|
||||||
|
if(!rada2b->transmit(msg)) {
|
||||||
|
std::cout << "Failed to transmit." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg->controlBytes[0] = 0x1C ; // Address for A2B_INTMSK1 register
|
||||||
|
msg->dataBytes[0] = 0x11;
|
||||||
|
msg->direction = icsneo::I2CMessage::Direction::Write;
|
||||||
|
// Write register
|
||||||
|
if(!rada2b->transmit(msg)) {
|
||||||
|
std::cout << "Failed to transmit" << std::endl;
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||||
|
msg->direction = icsneo::I2CMessage::Direction::Read;
|
||||||
|
|
||||||
|
// Read register
|
||||||
|
if(!rada2b->transmit(msg)) {
|
||||||
|
std::cout << "Failed to transmit." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||||
|
|
||||||
|
rada2b->removeMessageCallback(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example 5: Reading A2B sequence chart .puml file
|
||||||
|
*/
|
||||||
|
void example5(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||||
|
// The A2B sequence chart is located at binary index 0
|
||||||
|
constexpr uint16_t a2bSequenceChartIndex = 0;
|
||||||
|
|
||||||
|
// Create a ostream object to capture sequence chart data
|
||||||
|
std::ofstream a2bSequenceChart("a2b_sequence_chart.puml", std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
|
if(!rada2b->readBinaryFile(a2bSequenceChart, a2bSequenceChartIndex)) {
|
||||||
|
std::cout << "Failed to read A2B sequence chart" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayUsage() {
|
||||||
|
std::cout << "libicsneo A2B example" << std::endl;
|
||||||
|
std::cout << "Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node" << std::endl;
|
||||||
|
std::cout << "Options:" << std::endl;
|
||||||
|
std::cout << "-h, --help\tDisplay help message." << std::endl;
|
||||||
|
std::cout << "-e, --example [EXAMPLE_NUM]\tExample to run." << std::endl;
|
||||||
|
std::cout << "Example usage: ./libicsneocpp-a2b.exe --example 1" << std::endl;
|
||||||
|
std::cout << "Example usage: ./libicsneocpp-a2b.exe -h" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "Example options:" << std::endl;
|
||||||
|
std::cout << "0\ttx" << std::endl;
|
||||||
|
std::cout << "1\trx" << std::endl;
|
||||||
|
std::cout << "2\tSet RAD-A2B settings" << std::endl;
|
||||||
|
std::cout << "3\tPackaging and transmitting sine wav using A2BMessage API" << std::endl;
|
||||||
|
std::cout << "4\tRead/write I2C registers on A2B board" << std::endl;
|
||||||
|
std::cout << "5\tReading out A2B sequence chart .puml file" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
std::vector<std::string> arguments(argv, argv + argc);
|
||||||
|
if(argc > 4 || argc == 1) {
|
||||||
|
std::cerr << "Invalid usage." << std::endl;
|
||||||
|
displayUsage();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(std::any_of(arguments.begin(), arguments.end(), [](const std::string& arg) { return arg == "-h" || arg == "--help"; })) {
|
||||||
|
displayUsage();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(arguments[1] != "-e" && arguments[1] != "--example") {
|
||||||
|
std::cerr << "Invalid usage." << std::endl;
|
||||||
|
displayUsage();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int option = atoi(arguments[2].c_str());
|
||||||
|
|
||||||
|
if(option < 0 || option > 5) {
|
||||||
|
std::cerr << "Invalid usage." << std::endl;
|
||||||
|
displayUsage();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << icsneo::GetVersion() << std::endl;
|
||||||
|
const auto& devices = icsneo::FindAllDevices();
|
||||||
|
|
||||||
|
auto it = std::find_if(devices.begin(), devices.end(), [&](const auto& dev) {
|
||||||
|
const auto& txNetworks = dev->getSupportedTXNetworks();
|
||||||
|
const auto& rxNetworks = dev->getSupportedRXNetworks();
|
||||||
|
|
||||||
|
if(std::none_of(txNetworks.begin(), txNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(std::none_of(rxNetworks.begin(), rxNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if(it == devices.end()) {
|
||||||
|
std::cerr << "Could not find RAD-A2B." << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<icsneo::Device> rada2b = *it;
|
||||||
|
if(!rada2b->open()) {
|
||||||
|
std::cout << "Failed to open RAD-A2B." << std::endl;
|
||||||
|
std::cout << icsneo::GetLastError() << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "Opened RAD-A2B." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!rada2b->goOnline()) {
|
||||||
|
std::cout << "Failed to go online with RAD-A2B." << std::endl;
|
||||||
|
std::cout << icsneo::GetLastError() << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "RAD-A2B online." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(option) {
|
||||||
|
case 0:
|
||||||
|
example0(rada2b);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
example1(rada2b);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
example2(rada2b);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
example3(rada2b);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
example4(rada2b);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
example5(rada2b);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rada2b->goOffline();
|
||||||
|
rada2b->close();
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
add_executable(libicsneocpp-coremini src/coremini.cpp)
|
||||||
|
target_link_libraries(libicsneocpp-coremini icsneocpp)
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// Usage:
|
||||||
|
// ./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] [FLASH | SD]
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <icsneo/icsneocpp.h>
|
||||||
|
|
||||||
|
void displayUsage() {
|
||||||
|
std::cout << "Usage:\n";
|
||||||
|
std::cout << "./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] [FLASH | SD]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
std::vector<std::string> arguments(argv, argv + argc);
|
||||||
|
|
||||||
|
if(arguments.size() != 4) {
|
||||||
|
displayUsage();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << icsneo::GetVersion() << std::endl;
|
||||||
|
|
||||||
|
const auto& devices = icsneo::FindAllDevices();
|
||||||
|
|
||||||
|
auto it = std::find_if(
|
||||||
|
devices.begin(),
|
||||||
|
devices.end(),
|
||||||
|
[&arguments](const auto &d)
|
||||||
|
{ return d->getSerial() == arguments[1]; });
|
||||||
|
|
||||||
|
if(it == devices.end()) {
|
||||||
|
std::cout << "Failed to find device." << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::shared_ptr<icsneo::Device> device = *it;
|
||||||
|
if(!device->open()) {
|
||||||
|
std::cout << "Failed to open device." << std::endl;
|
||||||
|
std::cout << icsneo::GetLastError() << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!device->goOnline()) {
|
||||||
|
std::cout << "Failed to go online." << std::endl;
|
||||||
|
std::cout << icsneo::GetLastError() << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string memTypeString = arguments[3];
|
||||||
|
|
||||||
|
icsneo::Disk::MemoryType type;
|
||||||
|
if(memTypeString == "FLASH") {
|
||||||
|
type = icsneo::Disk::MemoryType::Flash;
|
||||||
|
} else if(memTypeString == "SD") {
|
||||||
|
type = icsneo::Disk::MemoryType::SD;
|
||||||
|
} else {
|
||||||
|
std::cout << "Incorrect memory type option" << std::endl;
|
||||||
|
displayUsage();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream coreminiFile(arguments[2], std::ios::binary);
|
||||||
|
|
||||||
|
if (!device->uploadCoremini(coreminiFile, type)) {
|
||||||
|
std::cout << "Failed to upload coremini" << std::endl;
|
||||||
|
std::cout << icsneo::GetLastError() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!device->startScript(type)) {
|
||||||
|
std::cout << "Failed to start script" << std::endl;
|
||||||
|
std::cout << icsneo::GetLastError() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->goOffline();
|
||||||
|
device->close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,27 +1,2 @@
|
|||||||
cmake_minimum_required(VERSION 3.2)
|
|
||||||
project(libicsneocpp-interactive-example VERSION 0.2.0)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
# Add an include directory like so if desired
|
|
||||||
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
|
||||||
|
|
||||||
# Enable Warnings
|
|
||||||
if(MSVC)
|
|
||||||
# Force to always compile with W4
|
|
||||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
|
||||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
||||||
else()
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
|
||||||
endif()
|
|
||||||
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Add libicsneo, usually a git submodule within your project works well
|
|
||||||
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
|
|
||||||
|
|
||||||
add_executable(libicsneocpp-interactive-example src/InteractiveExample.cpp)
|
add_executable(libicsneocpp-interactive-example src/InteractiveExample.cpp)
|
||||||
target_link_libraries(libicsneocpp-interactive-example icsneocpp)
|
target_link_libraries(libicsneocpp-interactive-example icsneocpp)
|
||||||
@@ -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,2 @@
|
|||||||
|
add_executable(libicsneocpp-lin src/LINExample.cpp)
|
||||||
|
target_link_libraries(libicsneocpp-lin icsneocpp)
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "icsneo/icsneocpp.h"
|
||||||
|
#include "icsneo/communication/message/linmessage.h"
|
||||||
|
|
||||||
|
/* Note: This example requires LIN 1 and LIN 2 channels to be connected on the device */
|
||||||
|
int main() {
|
||||||
|
// Print version
|
||||||
|
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||||
|
std::cout << "\nFinding devices... " << std::flush;
|
||||||
|
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
|
||||||
|
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
|
||||||
|
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
|
||||||
|
|
||||||
|
// List off the devices
|
||||||
|
for(auto& device : devices)
|
||||||
|
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
for(auto device : devices) {
|
||||||
|
std::cout << "Connecting to " << device->describe() << "... ";
|
||||||
|
bool ret = device->open();
|
||||||
|
if(!ret) { // Failed to open
|
||||||
|
std::cout << "FAIL" << std::endl;
|
||||||
|
std::cout << icsneo::GetLastError() << std::endl << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::cout << "OK" << std::endl << std::endl;
|
||||||
|
|
||||||
|
int64_t baud = 19200;
|
||||||
|
|
||||||
|
std::cout << "Enable LIN commander resistor... ";
|
||||||
|
ret = device->settings->setCommanderResistorFor(icsneo::Network::NetID::LIN, true);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Disable LIN2 commander resistor... ";
|
||||||
|
ret = device->settings->setCommanderResistorFor(icsneo::Network::NetID::LIN2, false);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Setting LIN to operate at " << baud << "bit/s... ";
|
||||||
|
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::LIN, baud);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Setting LIN2 to operate at " << baud << "bit/s... ";
|
||||||
|
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::LIN2, baud);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Setting LIN mode to NORMAL... ";
|
||||||
|
ret = device->settings->setLINModeFor(icsneo::Network::NetID::LIN, NORMAL_MODE);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Setting LIN2 mode to NORMAL... ";
|
||||||
|
ret = device->settings->setLINModeFor(icsneo::Network::NetID::LIN2, NORMAL_MODE);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Applying settings... ";
|
||||||
|
ret = device->settings->apply();
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Getting LIN Baudrate... ";
|
||||||
|
int64_t readBaud = device->settings->getBaudrateFor(icsneo::Network::NetID::LIN);
|
||||||
|
if(readBaud < 0)
|
||||||
|
std::cout << "FAIL" << std::endl;
|
||||||
|
else
|
||||||
|
std::cout << "OK, " << (readBaud) << "bit/s" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Getting LIN2 Baudrate... ";
|
||||||
|
readBaud = device->settings->getBaudrateFor(icsneo::Network::NetID::LIN2);
|
||||||
|
if(readBaud < 0)
|
||||||
|
std::cout << "FAIL" << std::endl;
|
||||||
|
else
|
||||||
|
std::cout << "OK, " << (readBaud) << "bit/s" << std::endl << std::endl;
|
||||||
|
|
||||||
|
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
|
||||||
|
std::cout << "Going online... ";
|
||||||
|
ret = device->goOnline();
|
||||||
|
if(!ret) {
|
||||||
|
std::cout << "FAIL" << std::endl;
|
||||||
|
device->close();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::cout << "OK" << std::endl;
|
||||||
|
|
||||||
|
// A real application would just check the result of icsneo_goOnline() rather than calling this
|
||||||
|
// This function is intended to be called later on if needed
|
||||||
|
std::cout << "Checking online status... ";
|
||||||
|
ret = device->isOnline();
|
||||||
|
if(!ret) {
|
||||||
|
std::cout << "FAIL\n" << std::endl;
|
||||||
|
device->close();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::cout << "OK" << std::endl << std::endl;
|
||||||
|
|
||||||
|
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
|
||||||
|
if(icsneo::Message::Type::Frame == message->type) {
|
||||||
|
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
|
||||||
|
if(icsneo::Network::Type::LIN == frame->network.getType()) {
|
||||||
|
auto msg = std::static_pointer_cast<icsneo::LINMessage>(message);
|
||||||
|
std::cout << msg->network << " RX frame | ID: 0x" << std::hex << static_cast<int>(msg->ID) << " | ";
|
||||||
|
std::cout << "Protected ID: 0x" << static_cast<int>(msg->protectedID) << "\n" << "Data: ";
|
||||||
|
for(uint8_t& each : msg->data) {
|
||||||
|
std::cout << "0x" << static_cast<int>(each) << " ";
|
||||||
|
}
|
||||||
|
std::cout << "\nChecksum type: " << (msg->isEnhancedChecksum ? "Enhanced" : "Classic");
|
||||||
|
std::cout << "\nChecksum: 0x" << static_cast<int>(msg->checksum) << "\n";
|
||||||
|
std::cout << "Is checksum valid: " << ((!msg->errFlags.ErrChecksumMatch) ? "yes" : "no") << "\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// We can transmit messages
|
||||||
|
std::cout << "Transmitting a LIN responder data frame... ";
|
||||||
|
auto lin_r = std::make_shared<icsneo::LINMessage>();
|
||||||
|
lin_r->network = icsneo::Network::NetID::LIN2;
|
||||||
|
lin_r->ID = 0x11;
|
||||||
|
lin_r->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
|
||||||
|
lin_r->data = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44};
|
||||||
|
ret = device->transmit(lin_r); // This will return false if the device does not support LIN
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Transmitting a LIN commander header... ";
|
||||||
|
auto lin_c = std::make_shared<icsneo::LINMessage>();
|
||||||
|
lin_c->network = icsneo::Network::NetID::LIN;
|
||||||
|
lin_c->ID = 0x11;
|
||||||
|
lin_c->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
|
||||||
|
ret = device->transmit(lin_c);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
std::cout << "Transmitting a LIN commander frame with responder data... ";
|
||||||
|
auto lin_d = std::make_shared<icsneo::LINMessage>();
|
||||||
|
lin_d->network = icsneo::Network::NetID::LIN;
|
||||||
|
lin_d->ID = 0x22;
|
||||||
|
lin_d->isEnhancedChecksum = true;
|
||||||
|
lin_d->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
|
||||||
|
lin_d->data = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd};
|
||||||
|
ret = device->transmit(lin_d);
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
|
// Go offline, stop sending and receiving traffic
|
||||||
|
device->removeMessageCallback(handler);
|
||||||
|
std::cout << "Going offline... ";
|
||||||
|
ret = device->goOffline();
|
||||||
|
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||||
|
std::cout << "Disconnecting... ";
|
||||||
|
ret = device->close();
|
||||||
|
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
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,2 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,27 +1,2 @@
|
|||||||
cmake_minimum_required(VERSION 3.2)
|
|
||||||
project(libicsneocpp-simple-example VERSION 0.2.0)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED 11)
|
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
# Add an include directory like so if desired
|
|
||||||
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
|
||||||
|
|
||||||
# Enable Warnings
|
|
||||||
if(MSVC)
|
|
||||||
# Force to always compile with W4
|
|
||||||
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
|
|
||||||
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
||||||
else()
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
|
||||||
endif()
|
|
||||||
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Add libicsneo, usually a git submodule within your project works well
|
|
||||||
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
|
|
||||||
|
|
||||||
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
|
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
|
||||||
target_link_libraries(libicsneocpp-simple-example icsneocpp)
|
target_link_libraries(libicsneocpp-simple-example icsneocpp)
|
||||||
@@ -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;
|
||||||
@@ -97,8 +101,30 @@ int main() {
|
|||||||
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;
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
add_executable(libicsneocpp-vsa-example src/VSAExample.cpp)
|
||||||
|
target_link_libraries(libicsneocpp-vsa-example icsneocpp)
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include "icsneo/icsneocpp.h"
|
||||||
|
|
||||||
|
enum class MessageType {
|
||||||
|
ShortEth,
|
||||||
|
LongEth,
|
||||||
|
CAN,
|
||||||
|
CANFD
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<std::string> MessageTypeLabels = {"short Ethernet", "long Ethernet", "CAN", "CAN-FD"};
|
||||||
|
|
||||||
|
void onEvent(std::shared_ptr<icsneo::APIEvent> event) {
|
||||||
|
std::cout << event->describe() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<icsneo::Frame>> constructRandomFrames(size_t frameCount, MessageType frameType) {
|
||||||
|
static constexpr size_t ClassicCANSize = 8;
|
||||||
|
static constexpr size_t CANFDSize = 64;
|
||||||
|
static constexpr size_t ShortEthSize = 500;
|
||||||
|
static constexpr size_t LongEthSize = 1500;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<icsneo::Frame>> frames;
|
||||||
|
std::random_device randDev;
|
||||||
|
std::mt19937 randEngine(randDev());
|
||||||
|
std::uniform_int_distribution randByteDist(0,255);
|
||||||
|
auto randByteGen = [&]() -> uint8_t {
|
||||||
|
return static_cast<uint8_t>(randByteDist(randEngine));
|
||||||
|
};
|
||||||
|
|
||||||
|
for(size_t i = 0; i < frameCount; i++) {
|
||||||
|
switch(frameType) {
|
||||||
|
case MessageType::ShortEth:
|
||||||
|
// Short Ethernet
|
||||||
|
{
|
||||||
|
auto frame = std::make_shared<icsneo::EthernetMessage>();
|
||||||
|
frame->network = icsneo::Network::NetID::Ethernet;
|
||||||
|
frames.push_back(frame);
|
||||||
|
frame->data.resize(ShortEthSize);
|
||||||
|
std::generate(frame->data.begin(), frame->data.end(), randByteGen);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::LongEth:
|
||||||
|
// Long Ethernet
|
||||||
|
{
|
||||||
|
auto frame = std::make_shared<icsneo::EthernetMessage>();
|
||||||
|
frame->network = icsneo::Network::NetID::Ethernet;
|
||||||
|
frames.push_back(frame);
|
||||||
|
frame->data.resize(LongEthSize);
|
||||||
|
std::generate(frame->data.begin(), frame->data.end(), randByteGen);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::CAN:
|
||||||
|
// Classic CAN
|
||||||
|
{
|
||||||
|
auto frame = std::make_shared<icsneo::CANMessage>();
|
||||||
|
frame->network = icsneo::Network::NetID::HSCAN2;
|
||||||
|
frames.push_back(frame);
|
||||||
|
frame->data.resize(ClassicCANSize);
|
||||||
|
|
||||||
|
std::generate(frame->data.begin(), frame->data.end(), randByteGen);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MessageType::CANFD:
|
||||||
|
// CAN FD
|
||||||
|
{
|
||||||
|
auto frame = std::make_shared<icsneo::CANMessage>();
|
||||||
|
frame->network = icsneo::Network::NetID::HSCAN3;
|
||||||
|
frames.push_back(frame);
|
||||||
|
frame->data.resize(CANFDSize);
|
||||||
|
std::generate(frame->data.begin(), frame->data.end(), randByteGen);
|
||||||
|
frame->isCANFD = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frames;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetScriptStatus(std::shared_ptr<icsneo::Device> rxDevice, std::shared_ptr<icsneo::Device> txDevice, bool rxInit, bool txInit) {
|
||||||
|
if(rxInit) {
|
||||||
|
rxDevice->startScript();
|
||||||
|
} else {
|
||||||
|
rxDevice->stopScript();
|
||||||
|
}
|
||||||
|
if(txInit) {
|
||||||
|
txDevice->startScript();
|
||||||
|
} else {
|
||||||
|
txDevice->stopScript();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if(argc != 3) {
|
||||||
|
std::cout << "Usage: libicsneocpp-vsa-example <Tx device serial> <Rx device serial>" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string txSerial = argv[1];
|
||||||
|
std::string rxSerial = argv[2];
|
||||||
|
|
||||||
|
// register an event callback so we can see all logged events
|
||||||
|
icsneo::EventManager::GetInstance().addEventCallback(icsneo::EventCallback(onEvent));
|
||||||
|
|
||||||
|
auto devices = icsneo::FindAllDevices();
|
||||||
|
if(devices.empty()) {
|
||||||
|
std::cout << "error: no devices found" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<icsneo::Device> txDevice;
|
||||||
|
std::shared_ptr<icsneo::Device> rxDevice;
|
||||||
|
for(auto&& d : devices) {
|
||||||
|
if(txSerial == d->getSerial()) {
|
||||||
|
txDevice = d;
|
||||||
|
}
|
||||||
|
if(rxSerial == d->getSerial()) {
|
||||||
|
rxDevice = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!txDevice) {
|
||||||
|
std::cout << "error: failed to find a device with serial number: " << txSerial << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(!rxDevice) {
|
||||||
|
std::cout << "error: failed to find a device with serial number: " << rxSerial << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "info: found " << txDevice->describe() << std::endl;
|
||||||
|
std::cout << "info: found " << rxDevice->describe() << std::endl;
|
||||||
|
|
||||||
|
if(!txDevice->open()) {
|
||||||
|
std::cout << "error: unable to open device" << std::endl;
|
||||||
|
}
|
||||||
|
if(!rxDevice->open()) {
|
||||||
|
std::cout << "error: unable to open device" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& rxInitialCoreminiStatus = rxDevice->getScriptStatus()->isCoreminiRunning;
|
||||||
|
const auto& txInitialCoreminiStatus = txDevice->getScriptStatus()->isCoreminiRunning;
|
||||||
|
|
||||||
|
rxDevice->stopScript();
|
||||||
|
txDevice->stopScript();
|
||||||
|
|
||||||
|
uint64_t origFirstOffset;
|
||||||
|
std::shared_ptr<icsneo::VSA> origFirstRecord;
|
||||||
|
if(!rxDevice->findFirstVSARecord(origFirstOffset, origFirstRecord)) {
|
||||||
|
std::cout << "error: unable to find first VSA record" << std::endl;
|
||||||
|
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::cout << "info: found first VSA record at " << origFirstOffset << std::endl;
|
||||||
|
|
||||||
|
uint64_t origLastOffset;
|
||||||
|
std::shared_ptr<icsneo::VSA> origLastRecord;
|
||||||
|
if(!rxDevice->findLastVSARecord(origLastOffset, origLastRecord)) {
|
||||||
|
std::cout << "error: unable to find last VSA record" << std::endl;
|
||||||
|
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::cout << "info: found last VSA record at " << origLastOffset << std::endl;
|
||||||
|
|
||||||
|
uint64_t canFrameCount = 0;
|
||||||
|
uint64_t ethFrameCount = 0;
|
||||||
|
rxDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
|
||||||
|
if(msg->type != icsneo::Message::Type::Frame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
|
||||||
|
if(frame->network.getType() == icsneo::Network::Type::CAN) {
|
||||||
|
++canFrameCount;
|
||||||
|
} else if(frame->network.getType() == icsneo::Network::Type::Ethernet) {
|
||||||
|
++ethFrameCount;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
icsneo::VSAExtractionSettings settings;
|
||||||
|
{
|
||||||
|
auto& filter = settings.filters.emplace_back();
|
||||||
|
filter.readRange.first = origLastRecord->getTimestampICSClock() - std::chrono::seconds(20);
|
||||||
|
filter.readRange.second = origLastRecord->getTimestampICSClock() - std::chrono::seconds(10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto& filter = settings.filters.emplace_back();
|
||||||
|
filter.readRange.first = origFirstRecord->getTimestampICSClock() + std::chrono::seconds(0);
|
||||||
|
filter.readRange.second = origFirstRecord->getTimestampICSClock() + std::chrono::seconds(10);
|
||||||
|
}
|
||||||
|
std::cout << "info: reading two blocks of VSA, 10s from the start and 10s from the end..." << std::endl;
|
||||||
|
if(!rxDevice->readVSA(settings)) {
|
||||||
|
std::cout << "error: unable to read VSA" << std::endl;
|
||||||
|
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::cout << "info: processed " << canFrameCount << " CAN frames and " << ethFrameCount << " Ethernet frames" << std::endl;
|
||||||
|
|
||||||
|
rxDevice->startScript();
|
||||||
|
txDevice->goOnline();
|
||||||
|
|
||||||
|
const uint8_t NumFrameTypes = 4;
|
||||||
|
const size_t FrameCountPerType = 2500;
|
||||||
|
std::vector<std::shared_ptr<icsneo::Frame>> frames;
|
||||||
|
for(uint8_t i = 0; i < NumFrameTypes; i++) {
|
||||||
|
std::cout << "info: transmitting " << FrameCountPerType << " random " << MessageTypeLabels[i] << " frames" << std::endl;
|
||||||
|
auto tempFrames = constructRandomFrames(FrameCountPerType, static_cast<MessageType>(i));
|
||||||
|
frames.insert(frames.end(), tempFrames.begin(), tempFrames.end());
|
||||||
|
if(!txDevice->transmit(tempFrames)) {
|
||||||
|
std::cout << "error: failed to transmit frames" << std::endl;
|
||||||
|
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(600));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t currentMessage = 0;
|
||||||
|
rxDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
|
||||||
|
if(msg->type != icsneo::Message::Type::Frame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
|
||||||
|
if(frames[currentMessage]->data == frame->data) {
|
||||||
|
currentMessage++;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Read from original last frame until end of buffer
|
||||||
|
icsneo::VSAExtractionSettings transmitSettings;
|
||||||
|
{
|
||||||
|
auto& filter = transmitSettings.filters.emplace_back();
|
||||||
|
filter.readRange.first = origLastRecord->getTimestampICSClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "info: reading transmitted random frames..." << std::endl;
|
||||||
|
if(!rxDevice->readVSA(transmitSettings)) {
|
||||||
|
std::cout << "error: unable to read VSA" << std::endl;
|
||||||
|
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "info: " << currentMessage << " transmitted frames found" << std::endl;
|
||||||
|
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
|
||||||
|
|
||||||
|
if(currentMessage != FrameCountPerType * NumFrameTypes) {
|
||||||
|
std::cout << "error: unable to find all transmitted frames" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -3511,16 +3511,6 @@ SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_ISO_get() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_ISOPIC_get() {
|
|
||||||
int jresult ;
|
|
||||||
int result;
|
|
||||||
|
|
||||||
result = (int)(10);
|
|
||||||
jresult = result;
|
|
||||||
return jresult;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_MAIN51_get() {
|
SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_MAIN51_get() {
|
||||||
int jresult ;
|
int jresult ;
|
||||||
int result;
|
int result;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user