mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-20 07:58:39 +02:00
Compare commits
112
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
648fb2502f | ||
|
|
008a0e4057 | ||
|
|
3787bc27a2 | ||
|
|
e73f61bfcc | ||
|
|
f552df372b | ||
|
|
80904dd84c | ||
|
|
e2bd0b5a3e | ||
|
|
24f291dc83 | ||
|
|
ee7b66625a | ||
|
|
99183a89f9 | ||
|
|
7f30179cc4 | ||
|
|
39bcef0230 | ||
|
|
ca370a1310 | ||
|
|
564933cb41 | ||
|
|
cac8d760b0 | ||
|
|
75af3220b0 | ||
|
|
f25a0a4a81 | ||
|
|
387c39d3a0 | ||
|
|
4476bd8b71 | ||
|
|
14588591e5 | ||
|
|
cbcbcbdc5d | ||
|
|
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 |
@@ -10,3 +10,8 @@ third-party/concurrentqueue/tests
|
||||
*.bak
|
||||
.vs
|
||||
.cache
|
||||
*.wav
|
||||
*.orig
|
||||
examples/csharp/bin
|
||||
examples/csharp/obj
|
||||
test/system
|
||||
|
||||
+474
-9
@@ -1,6 +1,15 @@
|
||||
variables:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- unit_test
|
||||
- hardware_test
|
||||
- deploy
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Windows
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
build windows/x64:
|
||||
stage: build
|
||||
@@ -14,17 +23,17 @@ build windows/x64:
|
||||
tags:
|
||||
- icsneo-windows
|
||||
|
||||
test windows/x64:
|
||||
stage: test
|
||||
unit_test windows/x64:
|
||||
stage: unit_test
|
||||
script:
|
||||
- build\libicsneo-tests.exe
|
||||
- build\libicsneo-unit-tests.exe
|
||||
dependencies:
|
||||
- build windows/x64
|
||||
needs:
|
||||
- build windows/x64
|
||||
tags:
|
||||
- icsneo-windows
|
||||
timeout: 3m
|
||||
timeout: 5m
|
||||
|
||||
build windows/x86:
|
||||
stage: build
|
||||
@@ -38,14 +47,470 @@ build windows/x86:
|
||||
tags:
|
||||
- icsneo-windows
|
||||
|
||||
test windows/x86:
|
||||
stage: test
|
||||
unit_test windows/x86:
|
||||
stage: unit_test
|
||||
script:
|
||||
- build\libicsneo-tests.exe
|
||||
- build\libicsneo-unit-tests.exe
|
||||
dependencies:
|
||||
- build windows/x86
|
||||
needs:
|
||||
- build windows/x86
|
||||
tags:
|
||||
- icsneo-windows
|
||||
timeout: 3m
|
||||
timeout: 5m
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Ubuntu
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
.build_linux_ubuntu_gcc: &build_linux_ubuntu_gcc
|
||||
stage: build
|
||||
script:
|
||||
- apt update -y
|
||||
- apt upgrade -y
|
||||
- apt install -y g++ ninja-build cmake libusb-1.0-0-dev libpcap-dev 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
|
||||
allow_failure: true
|
||||
|
||||
.fedora38_needs: &fedora38_needs
|
||||
needs:
|
||||
- job: build linux/fedora/38/amd64/clang
|
||||
artifacts: true
|
||||
|
||||
hardware_test fedora38-red2:
|
||||
<<: *hw_test
|
||||
<<: *fedora38_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: fedora38
|
||||
DEVICE_PORT: ETH_A
|
||||
|
||||
hardware_test fedora38-vcan42:
|
||||
<<: *hw_test
|
||||
<<: *fedora38_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: fedora38
|
||||
DEVICE_PORT: USB_D
|
||||
|
||||
hardware_test fedora38-fire3:
|
||||
<<: *hw_test
|
||||
<<: *fedora38_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: fedora38
|
||||
DEVICE_PORT: ETH_B
|
||||
|
||||
hardware_test fedora38-vcan42-EL:
|
||||
<<: *hw_test
|
||||
<<: *fedora38_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: fedora38
|
||||
DEVICE_PORT: USB_C
|
||||
|
||||
.ubuntu2204_needs: &ubuntu2204_needs
|
||||
needs:
|
||||
- job: build linux/ubuntu/2204/amd64/clang
|
||||
artifacts: true
|
||||
|
||||
hardware_test ubuntu2204-red2:
|
||||
<<: *hw_test
|
||||
<<: *ubuntu2204_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: ubuntu22.04
|
||||
DEVICE_PORT: ETH_A
|
||||
|
||||
hardware_test ubuntu2204-vcan42:
|
||||
<<: *hw_test
|
||||
<<: *ubuntu2204_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: ubuntu22.04
|
||||
DEVICE_PORT: USB_D
|
||||
|
||||
hardware_test ubuntu2204-fire3:
|
||||
<<: *hw_test
|
||||
<<: *ubuntu2204_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: ubuntu22.04
|
||||
DEVICE_PORT: ETH_B
|
||||
|
||||
hardware_test ubuntu2204-vcan42-EL:
|
||||
<<: *hw_test
|
||||
<<: *ubuntu2204_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: ubuntu22.04
|
||||
DEVICE_PORT: USB_C
|
||||
|
||||
.win10_needs: &win10_needs
|
||||
needs:
|
||||
- job: build windows/x64
|
||||
artifacts: true
|
||||
|
||||
hardware_test win10-red2:
|
||||
<<: *hw_test
|
||||
<<: *win10_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: win10
|
||||
DEVICE_PORT: ETH_A
|
||||
|
||||
hardware_test win10-vcan42:
|
||||
<<: *hw_test
|
||||
<<: *win10_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: win10
|
||||
DEVICE_PORT: USB_D
|
||||
|
||||
hardware_test win10-fire3:
|
||||
<<: *hw_test
|
||||
<<: *win10_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: win10
|
||||
DEVICE_PORT: ETH_B
|
||||
|
||||
hardware_test win10-vcan42-EL:
|
||||
<<: *hw_test
|
||||
<<: *win10_needs
|
||||
variables:
|
||||
GUEST_OS_TAG: win10
|
||||
DEVICE_PORT: USB_C
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Python Module
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
build python/linux/amd64:
|
||||
stage: build
|
||||
tags:
|
||||
- linux-build
|
||||
image: python:3.12
|
||||
services:
|
||||
- name: docker:dind
|
||||
entrypoint: ["env", "-u", "DOCKER_HOST"]
|
||||
command: ["dockerd-entrypoint.sh"]
|
||||
variables:
|
||||
CIBW_BEFORE_ALL: yum install -y flex && sh ci/bootstrap-libpcap.sh && sh ci/bootstrap-libusb.sh
|
||||
CIBW_BUILD: "*manylinux*" # no musl
|
||||
CIBW_ARCHS: x86_64
|
||||
DOCKER_HOST: tcp://docker:2375/
|
||||
DOCKER_DRIVER: overlay2
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install:/project/libusb/install
|
||||
script:
|
||||
- curl -sSL https://get.docker.com/ | sh
|
||||
- sh ci/build-wheel-posix.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- wheelhouse
|
||||
|
||||
build python/linux/arm64:
|
||||
stage: build
|
||||
tags:
|
||||
- arm64-linux-build
|
||||
variables:
|
||||
CIBW_BEFORE_ALL: yum install -y flex && sh ci/bootstrap-libpcap.sh && sh ci/bootstrap-libusb.sh
|
||||
CIBW_BUILD: "*manylinux*" # no musl
|
||||
CIBW_ARCHS: aarch64
|
||||
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install:/project/libusb/install
|
||||
script:
|
||||
- sh ci/build-wheel-posix.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- wheelhouse
|
||||
|
||||
build python/macos:
|
||||
stage: build
|
||||
tags:
|
||||
- macos-arm64
|
||||
variables:
|
||||
CIBW_BEFORE_ALL: sh ci/bootstrap-libpcap.sh && sh ci/bootstrap-libusb.sh
|
||||
CIBW_ARCHS: arm64
|
||||
CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=$CI_PROJECT_DIR/libpcap/install:$CI_PROJECT_DIR/libusb/install
|
||||
MACOSX_DEPLOYMENT_TARGET: 10.14
|
||||
script:
|
||||
- sh ci/build-wheel-posix.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- wheelhouse
|
||||
|
||||
build python/windows:
|
||||
stage: build
|
||||
tags:
|
||||
- libicsneo-win-x64
|
||||
variables:
|
||||
CIBW_ARCHS: AMD64
|
||||
CIBW_ENVIRONMENT: CMAKE_GENERATOR=Ninja
|
||||
script:
|
||||
- cmd /c ci\build-wheel-windows.bat
|
||||
artifacts:
|
||||
paths:
|
||||
- wheelhouse
|
||||
|
||||
deploy python/pypi:
|
||||
stage: deploy
|
||||
variables:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: $PYPI_TOKEN
|
||||
tags:
|
||||
- linux-build
|
||||
image: python:3.12
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
script:
|
||||
- python3 -m pip install -U twine
|
||||
- twine upload wheelhouse/*
|
||||
dependencies:
|
||||
- build python/linux/amd64
|
||||
- build python/linux/arm64
|
||||
- build python/macos
|
||||
- build python/windows
|
||||
needs:
|
||||
- build python/linux/amd64
|
||||
- build python/linux/arm64
|
||||
- build python/macos
|
||||
- build python/windows
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: "ubuntu-24.04"
|
||||
tools:
|
||||
python: "3.12"
|
||||
apt_packages:
|
||||
- doxygen
|
||||
|
||||
python:
|
||||
install:
|
||||
- requirements: docs/icsneopy/requirements.txt
|
||||
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
+168
-50
@@ -1,16 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
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_EXAMPLES "Build examples." ON)
|
||||
option(LIBICSNEO_BUILD_ICSNEOC "Build dynamic C library" ON)
|
||||
option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
|
||||
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
|
||||
option(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC "Build static icsnVC40 compatibility library" ON)
|
||||
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
|
||||
|
||||
option(LIBICSNEO_USE_DEVICE_SHARING "Interact with devices through the sharing server" ON)
|
||||
|
||||
# Device Drivers
|
||||
# You almost certainly don't want firmio for your build,
|
||||
# it is only relevant for communication between Linux and
|
||||
@@ -19,14 +24,18 @@ 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_CDCACM "Enable devices which communicate over USB CDC ACM" ON)
|
||||
option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX" ON)
|
||||
option(LIBICSNEO_ENABLE_TCP "Enable devices which communicate over TCP" OFF)
|
||||
option(LIBICSNEO_ENABLE_FTD3XX "Enable devices which communicate over USB FTD3XX" ON)
|
||||
|
||||
option(LIBICSNEO_ENABLE_BINDINGS_PYTHON "Enable Python library" OFF)
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# Enable Warnings
|
||||
if(MSVC)
|
||||
@@ -43,6 +52,8 @@ else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# doxygen
|
||||
find_package(Doxygen)
|
||||
if(DOXYGEN_FOUND)
|
||||
@@ -115,11 +126,6 @@ if(WIN32)
|
||||
platform/windows/vcp.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/windows/sharedmemory.cpp
|
||||
platform/windows/sharedsemaphore.cpp
|
||||
)
|
||||
else() # Darwin or Linux
|
||||
set(PLATFORM_SRC)
|
||||
|
||||
@@ -162,10 +168,59 @@ 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/24.34.0/libftd3xx-1.3.0.10-win-x64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=459e635496ab47d6069c9d3515fdd6d82cba3d95e7ae34f794d66ffdf336e9d1")
|
||||
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.3.0.10-win-i686.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ce4259ae11772d6ede7d217172156fa392f329b29d9455131f4126a2fb89dad1")
|
||||
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-macos-universal2.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=0904ac5eda8e1dc4b5aac3714383bcc7792b42dfeb585dce6cbfb8b67b8c0c51")
|
||||
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/24.34.0/libftd3xx-1.0.16-linux-x64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=cf66bf299fc722f050cdd3c36998a670f1df69f7c0df18afa73707277067114b")
|
||||
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/24.34.0/libftd3xx-1.0.16-linux-aarch64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=66341b5112b9841e959e81400b51711be96fec91894477c5cbfc29b10a0c00a6")
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-linux-armhf.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=cec1f959b48a11eb6b829ed43c81b6ba1c0bcf3e797bafcc84a6376e5ffc3c47")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(NOT LIBICSNEO_FTD3XX_URL)
|
||||
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
ftdi3xx
|
||||
URL ${LIBICSNEO_FTD3XX_URL}
|
||||
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
|
||||
)
|
||||
FetchContent_GetProperties(ftdi3xx)
|
||||
if(NOT ftdi3xx_POPULATED)
|
||||
FetchContent_Populate(ftdi3xx)
|
||||
endif()
|
||||
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
|
||||
endif()
|
||||
find_package(FTD3XX REQUIRED)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/posix/sharedmemory.cpp
|
||||
platform/posix/sharedsemaphore.cpp
|
||||
platform/ftd3xx.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_ENABLE_TCP)
|
||||
list(APPEND PLATFORM_SRC
|
||||
platform/tcp.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -184,8 +239,13 @@ endforeach()
|
||||
set(SRC_FILES
|
||||
communication/message/flexray/control/flexraycontrolmessage.cpp
|
||||
communication/message/callback/streamoutput/a2bwavoutput.cpp
|
||||
communication/message/a2bmessage.cpp
|
||||
communication/message/apperrormessage.cpp
|
||||
communication/message/neomessage.cpp
|
||||
communication/message/ethphymessage.cpp
|
||||
communication/message/linmessage.cpp
|
||||
communication/message/livedatamessage.cpp
|
||||
communication/message/tc10statusmessage.cpp
|
||||
communication/packet/flexraypacket.cpp
|
||||
communication/packet/canpacket.cpp
|
||||
communication/packet/a2bpacket.cpp
|
||||
@@ -193,10 +253,17 @@ set(SRC_FILES
|
||||
communication/packet/versionpacket.cpp
|
||||
communication/packet/iso9141packet.cpp
|
||||
communication/packet/ethphyregpacket.cpp
|
||||
communication/packet/livedatapacket.cpp
|
||||
communication/packet/logicaldiskinfopacket.cpp
|
||||
communication/packet/wivicommandpacket.cpp
|
||||
communication/packet/i2cpacket.cpp
|
||||
communication/packet/linpacket.cpp
|
||||
communication/packet/mdiopacket.cpp
|
||||
communication/packet/scriptstatuspacket.cpp
|
||||
communication/packet/componentversionpacket.cpp
|
||||
communication/packet/supportedfeaturespacket.cpp
|
||||
communication/packet/genericbinarystatuspacket.cpp
|
||||
communication/packet/hardwareinfopacket.cpp
|
||||
communication/decoder.cpp
|
||||
communication/encoder.cpp
|
||||
communication/ethernetpacketizer.cpp
|
||||
@@ -204,9 +271,8 @@ set(SRC_FILES
|
||||
communication/multichannelcommunication.cpp
|
||||
communication/communication.cpp
|
||||
communication/driver.cpp
|
||||
communication/interprocessmailbox.cpp
|
||||
communication/sdio.cpp
|
||||
communication/socket.cpp
|
||||
communication/livedata.cpp
|
||||
communication/ringbuffer.cpp
|
||||
device/extensions/flexray/extension.cpp
|
||||
device/extensions/flexray/controller.cpp
|
||||
device/idevicesettings.cpp
|
||||
@@ -220,6 +286,22 @@ set(SRC_FILES
|
||||
disk/plasiondiskreaddriver.cpp
|
||||
disk/extextractordiskreaddriver.cpp
|
||||
disk/fat.cpp
|
||||
disk/vsa/vsa.cpp
|
||||
disk/vsa/vsa02.cpp
|
||||
disk/vsa/vsa03.cpp
|
||||
disk/vsa/vsa04.cpp
|
||||
disk/vsa/vsa05.cpp
|
||||
disk/vsa/vsa06.cpp
|
||||
disk/vsa/vsa07.cpp
|
||||
disk/vsa/vsa08.cpp
|
||||
disk/vsa/vsa09.cpp
|
||||
disk/vsa/vsa0b.cpp
|
||||
disk/vsa/vsa0c.cpp
|
||||
disk/vsa/vsa0d.cpp
|
||||
disk/vsa/vsa0e.cpp
|
||||
disk/vsa/vsa0f.cpp
|
||||
disk/vsa/vsa6a.cpp
|
||||
disk/vsa/vsaparser.cpp
|
||||
${PLATFORM_SRC}
|
||||
)
|
||||
|
||||
@@ -280,7 +362,9 @@ target_include_directories(icsneocpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
|
||||
)
|
||||
|
||||
target_link_libraries(icsneocpp PUBLIC Threads::Threads)
|
||||
set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||
message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
|
||||
target_link_libraries(icsneocpp PUBLIC ${LIBICSNEO_EXTENSION_TARGETS})
|
||||
if(LIBICSNEO_ENABLE_FIRMIO)
|
||||
@@ -295,17 +379,20 @@ endif()
|
||||
if(LIBICSNEO_ENABLE_FTDI)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
|
||||
endif()
|
||||
if(LIBICSNEO_USE_DEVICE_SHARING)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_DEVICE_SHARING)
|
||||
if(LIBICSNEO_ENABLE_FTD3XX)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTD3XX)
|
||||
target_link_libraries(icsneocpp PRIVATE FTD3XX::FTD3XX)
|
||||
endif()
|
||||
|
||||
# socket
|
||||
if(WIN32)
|
||||
target_link_libraries(icsneocpp PRIVATE ws2_32)
|
||||
if(LIBICSNEO_ENABLE_TCP)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_TCP)
|
||||
if(WIN32)
|
||||
target_link_libraries(icsneocpp PRIVATE ws2_32 iphlpapi)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# fatfs
|
||||
add_subdirectory(third-party/fatfs)
|
||||
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(icsneocpp PRIVATE fatfs)
|
||||
|
||||
# libftdi
|
||||
@@ -322,7 +409,6 @@ if(LIBICSNEO_ENABLE_FTDI)
|
||||
add_subdirectory(third-party/libftdi)
|
||||
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
|
||||
find_package(Threads)
|
||||
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
|
||||
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||
@@ -346,15 +432,6 @@ if(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
endif(WIN32)
|
||||
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
|
||||
endif()
|
||||
|
||||
# For SharedMemory and SharedSemaphore
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(icsneocpp PUBLIC rt)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOC)
|
||||
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
||||
target_include_directories(icsneoc
|
||||
@@ -379,6 +456,7 @@ if(LIBICSNEO_BUILD_ICSNEOC_STATIC)
|
||||
)
|
||||
target_link_libraries(icsneoc-static PUBLIC icsneocpp)
|
||||
target_compile_features(icsneoc-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||
target_compile_definitions(icsneoc-static PUBLIC ICSNEOC_BUILD_STATIC)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOLEGACY)
|
||||
@@ -399,8 +477,29 @@ if(LIBICSNEO_BUILD_ICSNEOLEGACY)
|
||||
target_compile_features(icsneolegacy PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC)
|
||||
add_library(icsneolegacy-static STATIC
|
||||
api/icsneolegacy/icsneolegacy.cpp
|
||||
api/icsneolegacy/icsneolegacyextra.cpp
|
||||
api/icsneoc/icsneoc.cpp
|
||||
)
|
||||
|
||||
target_include_directories(icsneolegacy-static
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:>
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
target_link_libraries(icsneolegacy-static PUBLIC icsneocpp)
|
||||
target_compile_features(icsneolegacy-static PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
|
||||
target_compile_definitions(icsneolegacy-static PUBLIC ICSNEOC_BUILD_STATIC)
|
||||
endif()
|
||||
|
||||
add_subdirectory(bindings)
|
||||
|
||||
# googletest
|
||||
if(LIBICSNEO_BUILD_TESTS)
|
||||
if(LIBICSNEO_BUILD_UNIT_TESTS)
|
||||
if(WIN32)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
@@ -413,26 +512,45 @@ if(LIBICSNEO_BUILD_TESTS)
|
||||
include_directories("${gtest_SOURCE_DIR}/include")
|
||||
endif()
|
||||
|
||||
add_executable(libicsneo-tests
|
||||
test/main.cpp
|
||||
test/diskdriverreadtest.cpp
|
||||
test/diskdriverwritetest.cpp
|
||||
test/eventmanagertest.cpp
|
||||
test/ethernetpacketizertest.cpp
|
||||
test/interprocessmailboxtest.cpp
|
||||
test/sockettest.cpp
|
||||
test/i2cencoderdecodertest.cpp
|
||||
test/a2bencoderdecodertest.cpp
|
||||
add_executable(libicsneo-unit-tests
|
||||
test/unit/main.cpp
|
||||
test/unit/diskdriverreadtest.cpp
|
||||
test/unit/diskdriverwritetest.cpp
|
||||
test/unit/eventmanagertest.cpp
|
||||
test/unit/ethernetpacketizertest.cpp
|
||||
test/unit/i2cencoderdecodertest.cpp
|
||||
test/unit/linencoderdecodertest.cpp
|
||||
test/unit/a2bencoderdecodertest.cpp
|
||||
test/unit/mdioencoderdecodertest.cpp
|
||||
test/unit/livedataencoderdecodertest.cpp
|
||||
test/unit/ringbuffertest.cpp
|
||||
test/unit/apperrordecodertest.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(libicsneo-tests gtest gtest_main)
|
||||
target_link_libraries(libicsneo-tests icsneocpp)
|
||||
target_link_libraries(libicsneo-unit-tests gtest gtest_main)
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
# Hardware Support
|
||||
|
||||
- Connecting over Ethernet
|
||||
- neoVI FIRE 2
|
||||
- CAN works
|
||||
- CAN FD works
|
||||
- ValueCAN 4-2EL
|
||||
- CAN works
|
||||
- CAN FD works
|
||||
- Ethernet works
|
||||
- RADGalaxy
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- RADStar 2
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- RADA2B
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
|
||||
- Connecting over USB
|
||||
- ValueCAN 4 series
|
||||
- CAN works
|
||||
- CAN FD works
|
||||
- Ethernet works (on 4-2EL)
|
||||
- neoOBD2 PRO
|
||||
- CAN works
|
||||
- neoVI FIRE
|
||||
- CAN works
|
||||
- neoVI FIRE 2
|
||||
- CAN works
|
||||
- CAN FD works
|
||||
- Ethernet works
|
||||
- ValueCAN 3
|
||||
- CAN works
|
||||
- RADStar 2
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- neoVI PLASMA
|
||||
- CAN works
|
||||
- neoVI ION
|
||||
- CAN works
|
||||
- RADA2B
|
||||
- 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:
|
||||
|
||||
|
||||
@@ -1,130 +1,42 @@
|
||||
# libicsneo
|
||||
### The Intrepid Control Systems Open Source Cross-Platform Device Communication API
|
||||
|
||||
An open source solution to integrate Intrepid Control Systems vehicle networking hardware with your application.
|
||||
libicsneo is the [Intrepid Control Systems](https://intrepidcs.com/) device
|
||||
communication library. Installation and usage documentation can be found within
|
||||
each of the respective APIs.
|
||||
|
||||
[Read the Full Documentation](https://libicsneo.readthedocs.io/)
|
||||
## Documentation
|
||||
|
||||
## Getting Started
|
||||
There are two major ways to write a new application using libicsneo. You can use the C++ interface, which will be compiled with your project and statically linked, or you can use the C interface, which can be either statically or dynamically linked.
|
||||
### Integration with CMake (Static Linking)
|
||||
Integrating the library with your current CMake project is extremely easy.
|
||||
1. Checkout the library (or add as a submodule) into a subdirectory of your project.
|
||||
2. Within your `CMakeLists.txt` you can add the line `add_subdirectory("third-party/libicsneo")` to bring in the libicsneo targets. Replace `third-party` with any subdirectory you choose.
|
||||
3. The libicsneo library include paths should automatically be added to your include path.
|
||||
4. Link the library with your target by adding `target_link_libraries(libicsneocpp-example icsneocpp)` after your target, substituting `libicsneocpp-example` with your target application.
|
||||
- [C++](https://libicsneo.readthedocs.io/en/latest/icsneocpp/)
|
||||
- [Python](https://libicsneo.readthedocs.io/en/latest/icsneopy/)
|
||||
- [C](https://libicsneo.readthedocs.io/en/latest/icsneoc/)
|
||||
|
||||
You can now include either the C++ API with `#include <icsneo/icsneocpp.h>` or the C API with `#include <icsneo/icsneoc.h>`
|
||||
## Hardware Support
|
||||
|
||||
### DLL / SO / DYLIB Releases (Dynamic Linking)
|
||||
It is also possible to use the precompiled binaries with runtime linking. It is not recommended or supported to attempt to use the C++ interface with dynamic linking due to the complexities of C++ compilers.
|
||||
1. Add this repository's `/include` to your include path
|
||||
2. Add `#define ICSNEOC_DYNAMICLOAD` to the top of your source file
|
||||
2. Add `#import <icsneo/icsneoc.h>` below that line
|
||||
3. Call `icsneo_init();` to import the library before using any other libicsneo functions.
|
||||
4. Use the library as normal.
|
||||
5. Call `icsneo_close();` to unload the library.
|
||||
- EtherBADGE
|
||||
- neoVI Connect
|
||||
- neoVI FIRE
|
||||
- neoVI FIRE 2
|
||||
- neoVI FIRE 3
|
||||
- neoVI ION
|
||||
- neoVI PLASMA
|
||||
- neoVI RED 2
|
||||
- RAD-A2B
|
||||
- RAD-Comet 2
|
||||
- RAD-Comet 3
|
||||
- RAD-Galaxy
|
||||
- RAD-Gigastar
|
||||
- RAD-Gigastar 2
|
||||
- RAD-Moon 2
|
||||
- RAD-Moon 3
|
||||
- RAD-Moon T1S
|
||||
- RAD-Pluto
|
||||
- RAD-Star 2
|
||||
- RAD-SuperMoon
|
||||
- RADComet
|
||||
- ValueCAN 3
|
||||
- ValueCAN 4
|
||||
|
||||
## Usage
|
||||
### Using the C++ API
|
||||
The C++ API is designed to be modern and easy to use. All library functions and classes are in the namespace `icsneo`. Most applications will start by calling `icsneo::FindAllDevices()`. This will return an `std::vector` of `std::shared_ptr<icsneo::Device>` objects. You will want to keep a copy of the `shared_ptr` to any devices you want to use, as allowing it to go out of scope will automatically close the device and free all memory associated with it.
|
||||
## License
|
||||
|
||||
Any time you get bus traffic from the API, you will receive it as an `std::shared_ptr<icsneo::Message>`. The message will be valid as long as the `shared_ptr` stays in scope. Checking the type of the message allows you to cast it accordingly and access extra data for certain protocols. For instance, casting an `icsneo::Message` to an `icsneo::CANMessage` allows you to access the arbitration ID.
|
||||
|
||||
A barebones example is provided. For a more complete example, check the included `examples`.
|
||||
``` c++
|
||||
std::vector<std::shared_ptr<icsneo::Device>> devices = icsneo::FindAllDevices();
|
||||
std::cout << devices.size() << " found!" << std::endl;
|
||||
for(auto& device : devices)
|
||||
std::cout << "Found " << device->describe() << std::endl; // "Found neoVI FIRE 2 CY2345"
|
||||
std::shared_ptr<icsneo::Device> myDevice = devices[0];
|
||||
|
||||
if(!myDevice->open()) // Device tried and failed to open, print the last error
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
|
||||
myDevice->goOnline(); // Start receiving messages
|
||||
myDevice->enableMessagePolling(); // Allow the use of myDevice->getMessages() later
|
||||
// Alternatively, assign a callback for new messages
|
||||
std::this_thread::wait_for(std::chrono::seconds(5));
|
||||
std::vector<std::shared_ptr<icsneo::Message>> messages = myDevice->getMessages();
|
||||
std::cout << "We got " << messages.size() << " messages!" << std::endl;
|
||||
for(auto& msg : messages) {
|
||||
switch(msg->network.getType()) {
|
||||
case icsneo::Network::Type::CAN:
|
||||
case icsneo::Network::Type::SWCAN:
|
||||
case icsneo::Network::Type::LSFTCAN: {
|
||||
// A message of type CAN is guaranteed to be a CANMessage, so we can static cast safely
|
||||
auto canmsg = std::static_pointer_cast<icsneo::CANMessage>(msg);
|
||||
// canmsg->arbid is valid here
|
||||
// canmsg->data is an std::vector<uint8_t>, you can check .size() for the DLC of the message
|
||||
// canmsg->timestamp is the time recorded by the hardware in nanoseconds since (1/1/2007 12:00:00 GMT)
|
||||
}
|
||||
default:
|
||||
// Handle others
|
||||
}
|
||||
}
|
||||
myDevice->close();
|
||||
```
|
||||
|
||||
### Using the C API
|
||||
The C API is designed to be a robust and fault tolerant interface which allows easy integration with other languages as well as existing C applications. When calling `icsneo_findAllDevices()` you will provide a buffer of `neodevice_t` structures, which will be written with the found devices. These `neodevice_t` structures can be uses to interface with the API from then on. Once you call `icsneo_close()` with a device, that device and all associated memory will be freed. You will need to run `icsneo_findAllDevices()` again to reconnect.
|
||||
|
||||
Messages are passed in the form of `neomessage_t` structures when calling `icsneo_getMessages()`. These structures contain a `uint8_t*` to the payload data, and this pointer will be valid until the next call to `icsneo_getMessages()` or the device is closed.
|
||||
|
||||
A barebones example is provided. For a more complete example, check the included `examples`.
|
||||
``` c
|
||||
size_t deviceCount = 10; // Pre-set to the size of your buffer before the icsneo_findAllDevices() call
|
||||
neodevice_t devices[10];
|
||||
icsneo_findAllDevices(devices, &deviceCount);
|
||||
printf("We found %ull devices\n", deviceCount);
|
||||
for(size_t i = 0; i < deviceCount; i++) {
|
||||
neodevice_t* myDevice = &devices[i];
|
||||
char desc[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION];
|
||||
size_t sz = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
|
||||
icsneo_describeDevice(myDevice, desc, &sz);
|
||||
printf("Found %s\n", desc); // "Found neoVI FIRE 2 CY2345"
|
||||
}
|
||||
|
||||
neodevice_t* myDevice = &devices[0];
|
||||
if(!icsneo_openDevice(myDevice)) {
|
||||
neoevent_t error;
|
||||
if(icsneo_getLastError(&error))
|
||||
printf("Error! %s\n", error.description);
|
||||
}
|
||||
icsneo_goOnline(myDevice); // Start receiving messages
|
||||
icsneo_enableMessagePolling(myDevice); // Allow the use of icsneo_getMessages() later
|
||||
sleep(5);
|
||||
neomessage_t messages[50];
|
||||
size_t messageCount = 50;
|
||||
icsneo_getMessages(myDevice, messages, &messageCount, 0 /* non-blocking */);
|
||||
printf("We got %ull messages!\n", messageCount);
|
||||
for(size_t i = 0; i < messageCount; i++) {
|
||||
if(messages[i].type == ICSNEO_NETWORK_TYPE_CAN) {
|
||||
// A message of type CAN should be interperated a neomessage_can_t, so we can cast safely
|
||||
neomessage_can_t* canmsg = (neomessage_can_t*)&messages[i];
|
||||
// canmsg->arbid is valid here
|
||||
// canmsg->data is an uint8_t*, you can check canmsg->length for the length of the payload
|
||||
// canmsg->timestamp is the time recorded by the hardware in nanoseconds since (1/1/2007 12:00:00 GMT)
|
||||
}
|
||||
}
|
||||
icsneo_closeDevice(myDevice);
|
||||
```
|
||||
|
||||
## Building from Source
|
||||
### Windows
|
||||
Building will require MSVC 2017 version 15.7 or newer and CMake to be installed.
|
||||
### macOS
|
||||
Getting the dependencies is easiest with the Homebrew package manager. You will also need XCode installed. You can then install CMake, an up-to-date version of GCC or Clang, and `libusb-1.0`.
|
||||
### Linux
|
||||
The dependencies are as follows
|
||||
- CMake 3.12 or above
|
||||
- GCC 7 or above
|
||||
- `libusb-1.0-0-dev`
|
||||
- `libpcap0.8-dev`
|
||||
- `build-essential` is recommended
|
||||
|
||||
If you'd like to be able to run programs that use this library without being root, consider using the included udev rules
|
||||
|
||||
```
|
||||
$ sudo cp 99-intrepidcs.rules /etc/udev/rules.d/
|
||||
```
|
||||
libicsneo is licensed as BSD-3 with an extra clause, see [LICENSE](LICENSE)
|
||||
for more details.
|
||||
|
||||
+69
-1
@@ -144,8 +144,17 @@ bool icsneo_closeDevice(const neodevice_t* device) {
|
||||
if((*it).get() == device->device)
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -689,3 +698,62 @@ bool icsneo_setTerminationFor(const neodevice_t* device, neonetid_t netid, bool
|
||||
|
||||
return device->device->settings->setTerminationFor(Network(netid), enabled);
|
||||
}
|
||||
|
||||
bool icsneo_getRTC(const neodevice_t* device, uint64_t* output)
|
||||
{
|
||||
if(!icsneo_isValidNeoDevice(device))
|
||||
return false;
|
||||
|
||||
const std::optional<std::chrono::time_point<std::chrono::system_clock>> rtc = device->device->getRTC();
|
||||
if(!rtc)
|
||||
return false;
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::seconds>(rtc->time_since_epoch());
|
||||
*output = static_cast<uint64_t>(duration.count());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icsneo_setRTC(const neodevice_t* device, uint64_t input)
|
||||
{
|
||||
if(!icsneo_isValidNeoDevice(device))
|
||||
return false;
|
||||
|
||||
std::chrono::seconds duration(input);
|
||||
const std::chrono::system_clock::time_point time(duration);
|
||||
return device->device->setRTC(time);
|
||||
}
|
||||
|
||||
int icsneo_getDeviceStatus(const neodevice_t* device, void* status, size_t* size) {
|
||||
if(!icsneo_isValidNeoDevice(device))
|
||||
return false;
|
||||
|
||||
if(status == nullptr || size == nullptr)
|
||||
return false;
|
||||
|
||||
std::shared_ptr<Message> msg = device->device->com->waitForMessageSync([&]() {
|
||||
return device->device->com->sendCommand(Command::RequestStatusUpdate);
|
||||
}, std::make_shared<MessageFilter>(Network::NetID::DeviceStatus), std::chrono::milliseconds(100));
|
||||
|
||||
if(!msg) // Did not receive a message
|
||||
return false;
|
||||
|
||||
auto rawMessage = std::static_pointer_cast<RawMessage>(msg);
|
||||
if(!rawMessage || (rawMessage->network.getNetID() != Network::NetID::DeviceStatus))
|
||||
return false;
|
||||
|
||||
if(*size < rawMessage->data.size())
|
||||
return false;
|
||||
|
||||
std::copy(rawMessage->data.begin(), rawMessage->data.end(), static_cast<uint8_t*>(status));
|
||||
*size = rawMessage->data.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 "FileVersion", VER_FILEVERSION_STR
|
||||
VALUE "InternalName", "icsneoc.dll"
|
||||
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2019"
|
||||
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024"
|
||||
VALUE "OriginalFilename", "icsneoc.dll"
|
||||
VALUE "ProductName", "libicsneo"
|
||||
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
||||
|
||||
+163
-99
@@ -4,7 +4,7 @@
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
APIEvent::APIEvent(Type type, Severity severity, const Device* device) : eventStruct({}) {
|
||||
APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device) : eventStruct({}) {
|
||||
this->device = device;
|
||||
if(device) {
|
||||
serial = device->getSerial();
|
||||
@@ -14,17 +14,6 @@ APIEvent::APIEvent(Type type, Severity severity, const Device* device) : eventSt
|
||||
init(type, severity);
|
||||
}
|
||||
|
||||
APIEvent::APIEvent(neosocketevent_t evStruct, const Device* device)
|
||||
{
|
||||
this->device = device;
|
||||
timepoint = EventClock::from_time_t(evStruct.timestamp);
|
||||
serial = std::string(evStruct.serial);
|
||||
eventStruct.eventNumber = evStruct.eventNumber;
|
||||
eventStruct.severity = evStruct.severity;
|
||||
eventStruct.description = DescriptionForType(APIEvent::getType());
|
||||
eventStruct.timestamp = evStruct.timestamp;
|
||||
}
|
||||
|
||||
void APIEvent::init(Type event, APIEvent::Severity severity) {
|
||||
timepoint = EventClock::now();
|
||||
eventStruct.description = DescriptionForType(event);
|
||||
@@ -56,15 +45,6 @@ std::string APIEvent::describe() const noexcept {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
neosocketevent_t APIEvent::getNeoSocketEvent() const noexcept {
|
||||
neosocketevent_t neoSocketEvent;
|
||||
neoSocketEvent.eventNumber = eventStruct.eventNumber;
|
||||
neoSocketEvent.severity = eventStruct.severity;
|
||||
std::memcpy(neoSocketEvent.serial, eventStruct.serial, sizeof(eventStruct.serial));
|
||||
neoSocketEvent.timestamp = eventStruct.timestamp;
|
||||
return neoSocketEvent;
|
||||
}
|
||||
|
||||
void APIEvent::downgradeFromError() noexcept {
|
||||
eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning;
|
||||
}
|
||||
@@ -92,6 +72,8 @@ static constexpr const char* MESSAGE_MAX_LENGTH_EXCEEDED = "The message was too
|
||||
static constexpr const char* VALUE_NOT_YET_PRESENT = "The value is not yet present.";
|
||||
static constexpr const char* TIMEOUT = "The timeout was reached.";
|
||||
static constexpr const char* WIVI_NOT_SUPPORTED = "Wireless neoVI functions are not supported on this device.";
|
||||
static constexpr const char* RESTRICTED_ENTRY_FLAG = "Attempted to set a restricted flag in a Root Directory entry.";
|
||||
static constexpr const char* NOT_SUPPORTED = "The requested feature is not supported.";
|
||||
|
||||
// Device Errors
|
||||
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
|
||||
@@ -133,6 +115,11 @@ static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An idea
|
||||
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
|
||||
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
|
||||
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
|
||||
static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same.";
|
||||
static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected.";
|
||||
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
|
||||
static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device.";
|
||||
static constexpr const char* MODE_NOT_FOUND = "The mode was not found.";
|
||||
|
||||
// Transport Errors
|
||||
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
||||
@@ -145,32 +132,58 @@ static constexpr const char* DEVICE_IN_USE = "The device is currently in use by
|
||||
static constexpr const char* PCAP_COULD_NOT_START = "The PCAP driver could not be started. Ethernet devices will not be found.";
|
||||
static constexpr const char* PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
|
||||
static constexpr const char* PACKET_DECODING = "There was an error decoding a packet from the device.";
|
||||
static constexpr const char* SOCKET_FAILED_TO_OPEN = "Unable to open new socket.";
|
||||
static constexpr const char* FAILED_TO_BIND = "Unable to bind socket.";
|
||||
static constexpr const char* ERROR_SETTING_SOCKET_OPTION = "A call to setsockopt() failed.";
|
||||
static constexpr const char* GETIFADDRS_ERROR = "A call to getifaddrs() failed.";
|
||||
static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed.";
|
||||
|
||||
static constexpr const char* SHARED_MEMORY_DATA_IS_NULL = "data() was called on invalid object.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_CLOSE = "The server failed to close a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_OPEN = "The server failed to open a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FAILED_TO_UNLINK = "The server failed to unlink a shared memory location.";
|
||||
static constexpr const char* SHARED_MEMORY_FILE_TRUNCATE_ERROR = "The server failed to truncate shared memory file.";
|
||||
static constexpr const char* SHARED_MEMORY_MAPPING_ERROR = "The server failed to map a shared memory file.";
|
||||
static constexpr const char* SHARED_MEMORY_UNMAP_ERROR = "The server failed to unmap a shared memory file.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_CLOSE = "The server failed to close a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_OPEN = "The server failed to open a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_POST = "A post() call failed on a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_UNLINK = "The server failed to unlink a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_WAIT = "A wait() call failed on a shared semaphore.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_POST = "post() was called on a shared semaphore that is not open.";
|
||||
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT = "wait() was called on a shared semaphore that is not open.";
|
||||
static constexpr const char* SOCKET_FAILED_CONNECT = "A socket connection was attempted but failed.";
|
||||
static constexpr const char* SOCKET_FAILED_OPEN = "A socket failed to open.";
|
||||
static constexpr const char* SOCKET_FAILED_CLOSE = "A socket failed to close.";
|
||||
static constexpr const char* SOCKET_FAILED_READ = "A socket read operation failed.";
|
||||
static constexpr const char* SOCKET_FAILED_WRITE = "A socket write operation failed.";
|
||||
static constexpr const char* ACCEPTOR_FAILED_BIND = "A socket acceptor failed to bind.";
|
||||
static constexpr const char* ACCEPTOR_FAILED_LISTEN = "A socket acceptor failed to listen.";
|
||||
// FTD3XX
|
||||
static constexpr const char* FT_OK = "FTD3XX success.";
|
||||
static constexpr const char* FT_INVALID_HANDLE = "Invalid FTD3XX handle.";
|
||||
static constexpr const char* FT_DEVICE_NOT_FOUND = "FTD3XX device not found.";
|
||||
static constexpr const char* FT_DEVICE_NOT_OPENED = "FTD3XX device not opened.";
|
||||
static constexpr const char* FT_IO_ERROR = "FTD3XX IO error.";
|
||||
static constexpr const char* FT_INSUFFICIENT_RESOURCES = "Insufficient resources for FTD3XX.";
|
||||
static constexpr const char* FT_INVALID_PARAMETER = "Invalid FTD3XX parameter.";
|
||||
static constexpr const char* FT_INVALID_BAUD_RATE = "Invalid FTD3XX baud rate.";
|
||||
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_ERASE = "FTD3XX device not opened for erase.";
|
||||
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_WRITE = "FTD3XX not opened for write.";
|
||||
static constexpr const char* FT_FAILED_TO_WRITE_DEVICE = "FTD3XX failed to write device.";
|
||||
static constexpr const char* FT_EEPROM_READ_FAILED = "FTD3XX EEPROM read failed.";
|
||||
static constexpr const char* FT_EEPROM_WRITE_FAILED = "FTD3XX EEPROM write failed.";
|
||||
static constexpr const char* FT_EEPROM_ERASE_FAILED = "FTD3XX EEPROM erase failed.";
|
||||
static constexpr const char* FT_EEPROM_NOT_PRESENT = "FTD3XX EEPROM not present.";
|
||||
static constexpr const char* FT_EEPROM_NOT_PROGRAMMED = "FTD3XX EEPROM not programmed.";
|
||||
static constexpr const char* FT_INVALID_ARGS = "Invalid FTD3XX arguments.";
|
||||
static constexpr const char* FT_NOT_SUPPORTED = "FTD3XX not supported.";
|
||||
static constexpr const char* FT_NO_MORE_ITEMS = "No more FTD3XX items.";
|
||||
static constexpr const char* FT_TIMEOUT = "FTD3XX timeout.";
|
||||
static constexpr const char* FT_OPERATION_ABORTED = "FTD3XX operation aborted.";
|
||||
static constexpr const char* FT_RESERVED_PIPE = "Reserved FTD3XX pipe.";
|
||||
static constexpr const char* FT_INVALID_CONTROL_REQUEST_DIRECTION = "Invalid FTD3XX control request direction.";
|
||||
static constexpr const char* FT_INVALID_CONTROL_REQUEST_TYPE = "Invalid FTD3XX control request type.";
|
||||
static constexpr const char* FT_IO_PENDING = "FTD3XX IO pending.";
|
||||
static constexpr const char* FT_IO_INCOMPLETE = "FTD3XX IO incomplete.";
|
||||
static constexpr const char* FT_HANDLE_EOF = "Handle FTD3XX EOF.";
|
||||
static constexpr const char* FT_BUSY = "FTD3XX busy.";
|
||||
static constexpr const char* FT_NO_SYSTEM_RESOURCES = "No FTD3XX system resources.";
|
||||
static constexpr const char* FT_DEVICE_LIST_NOT_READY = "FTD3XX device list not ready.";
|
||||
static constexpr const char* FT_DEVICE_NOT_CONNECTED = "FTD3XX device not connected.";
|
||||
static constexpr const char* FT_INCORRECT_DEVICE_PATH = "Incorrect FTD3XX device path.";
|
||||
static constexpr const char* FT_OTHER_ERROR = "Other FTD3XX error.";
|
||||
|
||||
// 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* UNKNOWN = "An unknown internal error occurred.";
|
||||
static constexpr const char* NO_ERROR_FOUND = "No errors found.";
|
||||
static constexpr const char* INVALID = "An invalid internal error occurred.";
|
||||
const char* APIEvent::DescriptionForType(Type type) {
|
||||
switch(type) {
|
||||
@@ -207,6 +220,10 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return TIMEOUT;
|
||||
case Type::WiVINotSupported:
|
||||
return WIVI_NOT_SUPPORTED;
|
||||
case Type::RestrictedEntryFlag:
|
||||
return RESTRICTED_ENTRY_FLAG;
|
||||
case Type::NotSupported:
|
||||
return NOT_SUPPORTED;
|
||||
|
||||
// Device Errors
|
||||
case Type::PollingMessageOverflow:
|
||||
@@ -287,7 +304,16 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return WIVI_UPLOAD_STACK_OVERFLOW;
|
||||
case Type::A2BMessageIncompleteFrame:
|
||||
return A2B_MESSAGE_INCOMPLETE_FRAME;
|
||||
|
||||
case Type::CoreminiUploadVersionMismatch:
|
||||
return COREMINI_UPLOAD_VERSION_MISMATCH;
|
||||
case Type::DiskNotConnected:
|
||||
return DISK_NOT_CONNECTED;
|
||||
case Type::UnexpectedResponse:
|
||||
return UNEXPECTED_RESPONSE;
|
||||
case Type::LINSettingsNotAvailable:
|
||||
return LIN_SETTINGS_NOT_AVAILABLE;
|
||||
case Type::ModeNotFound:
|
||||
return MODE_NOT_FOUND;
|
||||
// Transport Errors
|
||||
case Type::FailedToRead:
|
||||
return FAILED_TO_READ;
|
||||
@@ -309,58 +335,106 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
return PCAP_COULD_NOT_FIND_DEVICES;
|
||||
case Type::PacketDecodingError:
|
||||
return PACKET_DECODING;
|
||||
|
||||
// Device Sharing Server Events
|
||||
case Type::SharedMemoryDataIsNull:
|
||||
return SHARED_MEMORY_DATA_IS_NULL;
|
||||
case Type::SharedMemoryFailedToClose:
|
||||
return SHARED_MEMORY_FAILED_TO_CLOSE;
|
||||
case Type::SharedMemoryFailedToOpen:
|
||||
return SHARED_MEMORY_FAILED_TO_OPEN;
|
||||
case Type::SharedMemoryFailedToUnlink:
|
||||
return SHARED_MEMORY_FAILED_TO_UNLINK;
|
||||
case Type::SharedMemoryFileTruncateError:
|
||||
return SHARED_MEMORY_FILE_TRUNCATE_ERROR;
|
||||
case Type::SharedMemoryMappingError:
|
||||
return SHARED_MEMORY_MAPPING_ERROR;
|
||||
case Type::SharedMemoryUnmapError:
|
||||
return SHARED_MEMORY_UNMAP_ERROR;
|
||||
case Type::SharedSemaphoreFailedToClose:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_CLOSE;
|
||||
case Type::SharedSemaphoreFailedToOpen:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_OPEN;
|
||||
case Type::SharedSemaphoreFailedToPost:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_POST;
|
||||
case Type::SharedSemaphoreFailedToUnlink:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_UNLINK;
|
||||
case Type::SharedSemaphoreFailedToWait:
|
||||
return SHARED_SEMAPHORE_FAILED_TO_WAIT;
|
||||
case Type::SharedSemaphoreNotOpenForPost:
|
||||
return SHARED_SEMAPHORE_NOT_OPEN_FOR_POST;
|
||||
case Type::SharedSemaphoreNotOpenForWait:
|
||||
return SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT;
|
||||
case Type::SocketFailedToOpen:
|
||||
return SOCKET_FAILED_OPEN;
|
||||
case Type::SocketFailedToClose:
|
||||
return SOCKET_FAILED_CLOSE;
|
||||
case Type::SocketFailedToConnect:
|
||||
return SOCKET_FAILED_CONNECT;
|
||||
case Type::SocketFailedToRead:
|
||||
return SOCKET_FAILED_READ;
|
||||
case Type::SocketFailedToWrite:
|
||||
return SOCKET_FAILED_WRITE;
|
||||
case Type::SocketAcceptorFailedToBind:
|
||||
return ACCEPTOR_FAILED_BIND;
|
||||
case Type::SocketAcceptorFailedToListen:
|
||||
return ACCEPTOR_FAILED_LISTEN;
|
||||
return SOCKET_FAILED_TO_OPEN;
|
||||
case Type::FailedToBind:
|
||||
return FAILED_TO_BIND;
|
||||
case Type::ErrorSettingSocketOption:
|
||||
return ERROR_SETTING_SOCKET_OPTION;
|
||||
case Type::GetIfAddrsError:
|
||||
return GETIFADDRS_ERROR;
|
||||
case Type::SendToError:
|
||||
return SEND_TO_ERROR;
|
||||
|
||||
// FTD3XX
|
||||
case Type::FTOK:
|
||||
return FT_OK;
|
||||
case Type::FTInvalidHandle:
|
||||
return FT_INVALID_HANDLE;
|
||||
case Type::FTDeviceNotFound:
|
||||
return FT_DEVICE_NOT_FOUND;
|
||||
case Type::FTDeviceNotOpened:
|
||||
return FT_DEVICE_NOT_OPENED;
|
||||
case Type::FTIOError:
|
||||
return FT_IO_ERROR;
|
||||
case Type::FTInsufficientResources:
|
||||
return FT_INSUFFICIENT_RESOURCES;
|
||||
case Type::FTInvalidParameter:
|
||||
return FT_INVALID_PARAMETER;
|
||||
case Type::FTInvalidBaudRate:
|
||||
return FT_INVALID_BAUD_RATE;
|
||||
case Type::FTDeviceNotOpenedForErase:
|
||||
return FT_DEVICE_NOT_OPENED_FOR_ERASE;
|
||||
case Type::FTDeviceNotOpenedForWrite:
|
||||
return FT_DEVICE_NOT_OPENED_FOR_WRITE;
|
||||
case Type::FTFailedToWriteDevice:
|
||||
return FT_FAILED_TO_WRITE_DEVICE;
|
||||
case Type::FTEEPROMReadFailed:
|
||||
return FT_EEPROM_READ_FAILED;
|
||||
case Type::FTEEPROMWriteFailed:
|
||||
return FT_EEPROM_WRITE_FAILED;
|
||||
case Type::FTEEPROMEraseFailed:
|
||||
return FT_EEPROM_ERASE_FAILED;
|
||||
case Type::FTEEPROMNotPresent:
|
||||
return FT_EEPROM_NOT_PRESENT;
|
||||
case Type::FTEEPROMNotProgrammed:
|
||||
return FT_EEPROM_NOT_PROGRAMMED;
|
||||
case Type::FTInvalidArgs:
|
||||
return FT_INVALID_ARGS;
|
||||
case Type::FTNotSupported:
|
||||
return FT_NOT_SUPPORTED;
|
||||
case Type::FTNoMoreItems:
|
||||
return FT_NO_MORE_ITEMS;
|
||||
case Type::FTTimeout:
|
||||
return FT_TIMEOUT;
|
||||
case Type::FTOperationAborted:
|
||||
return FT_OPERATION_ABORTED;
|
||||
case Type::FTReservedPipe:
|
||||
return FT_RESERVED_PIPE;
|
||||
case Type::FTInvalidControlRequestDirection:
|
||||
return FT_INVALID_CONTROL_REQUEST_DIRECTION;
|
||||
case Type::FTInvalidControlRequestType:
|
||||
return FT_INVALID_CONTROL_REQUEST_TYPE;
|
||||
case Type::FTIOPending:
|
||||
return FT_IO_PENDING;
|
||||
case Type::FTIOIncomplete:
|
||||
return FT_IO_INCOMPLETE;
|
||||
case Type::FTHandleEOF:
|
||||
return FT_HANDLE_EOF;
|
||||
case Type::FTBusy:
|
||||
return FT_BUSY;
|
||||
case Type::FTNoSystemResources:
|
||||
return FT_NO_SYSTEM_RESOURCES;
|
||||
case Type::FTDeviceListNotReady:
|
||||
return FT_DEVICE_LIST_NOT_READY;
|
||||
case Type::FTDeviceNotConnected:
|
||||
return FT_DEVICE_NOT_CONNECTED;
|
||||
case Type::FTIncorrectDevicePath:
|
||||
return FT_INCORRECT_DEVICE_PATH;
|
||||
case Type::FTOtherError:
|
||||
return FT_OTHER_ERROR;
|
||||
|
||||
// VSA
|
||||
case Type::VSABufferCorrupted:
|
||||
return VSA_BUFFER_CORRUPTED;
|
||||
case Type::VSATimestampNotFound:
|
||||
return VSA_TIMESTAMP_NOT_FOUND;
|
||||
case Type::VSABufferFormatError:
|
||||
return VSA_BUFFER_FORMAT_ERROR;
|
||||
case Type::VSAMaxReadAttemptsReached:
|
||||
return VSA_MAX_READ_ATTEMPTS_REACHED;
|
||||
case Type::VSAByteParseFailure:
|
||||
return VSA_BYTE_PARSE_FAILURE;
|
||||
case Type::VSAExtendedMessageError:
|
||||
return VSA_EXTENDED_MESSAGE_ERROR;
|
||||
case Type::VSAOtherError:
|
||||
return VSA_OTHER_ERROR;
|
||||
|
||||
// Other Errors
|
||||
case Type::TooManyEvents:
|
||||
return TOO_MANY_EVENTS;
|
||||
case Type::Unknown:
|
||||
return UNKNOWN;
|
||||
case Type::NoErrorFound:
|
||||
return NO_ERROR_FOUND;
|
||||
default:
|
||||
return INVALID;
|
||||
}
|
||||
@@ -380,14 +454,4 @@ bool EventFilter::match(const APIEvent& event) const noexcept {
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
neosocketeventfilter_t EventFilter::getNeoSocketEventFilter() const noexcept {
|
||||
neosocketeventfilter_t filterStruct;
|
||||
filterStruct.eventNumber = static_cast<decltype(filterStruct.eventNumber)>(type);
|
||||
filterStruct.severity = static_cast<decltype(filterStruct.severity)>(severity);
|
||||
if((serial.length() + 1) == sizeof(filterStruct.serial)) {
|
||||
std::memcpy(&filterStruct.serial[0], serial.c_str(), sizeof(filterStruct.serial));
|
||||
}
|
||||
return filterStruct;
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/api/event.h"
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#endif
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -39,6 +36,25 @@ void EventManager::cancelErrorDowngradingOnCurrentThread() {
|
||||
void EventManager::add(APIEvent event) {
|
||||
if(destructing)
|
||||
return;
|
||||
static const auto printLevel = []() -> std::optional<uint8_t> {
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
const auto level = std::getenv("LIBICSNEO_PRINT_EVENTS");
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
if(!level)
|
||||
return std::nullopt;
|
||||
try {
|
||||
return (uint8_t)std::stoi(level);
|
||||
} catch (std::invalid_argument const&) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}();
|
||||
if(printLevel && (uint8_t)event.getSeverity() >= *printLevel)
|
||||
std::cerr << event.describe() << std::endl;
|
||||
if(event.getSeverity() == APIEvent::Severity::Error) {
|
||||
// if the error was added on a thread that downgrades errors (non-user thread)
|
||||
std::lock_guard<std::mutex> lk(downgradedThreadsMutex);
|
||||
@@ -46,7 +62,7 @@ void EventManager::add(APIEvent event) {
|
||||
if(i != downgradedThreads.end() && i->second) {
|
||||
event.downgradeFromError();
|
||||
{
|
||||
std::lock_guard<std::mutex> eventsLock{eventsMutex};
|
||||
std::lock_guard<std::mutex> eventsLock(eventsMutex);
|
||||
addEventInternal(event);
|
||||
} // free the lock so that callbacks may modify events
|
||||
runCallbacks(event);
|
||||
@@ -152,60 +168,23 @@ bool EventManager::isDowngradingErrorsOnCurrentThread() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
std::optional<std::vector<neosocketevent_t>> EventManager::getServerEvents(const size_t& max)
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped<RPC>(RPC::GET_EVENTS) && socket.writeTyped<size_t>(max)))
|
||||
return std::nullopt;
|
||||
|
||||
size_t count;
|
||||
if(!socket.readTyped(count) || count < 0 || count > eventLimit)
|
||||
return std::nullopt;
|
||||
|
||||
std::optional<std::vector<neosocketevent_t>> ret;
|
||||
if(count == size_t(0))
|
||||
return ret;
|
||||
|
||||
auto& eventStructs = ret.emplace(count);
|
||||
if(!socket.read(eventStructs.data(), (eventStructs.size() * sizeof(neosocketevent_t))))
|
||||
return std::nullopt;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif // ICSNEO_ENABLE_DEVICE_SHARING
|
||||
|
||||
void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilter filter) {
|
||||
eventOutput.clear();
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto serverEvents = getServerEvents(max);
|
||||
if(serverEvents) {
|
||||
std::scoped_lock eventsLock{eventsMutex};
|
||||
for(neosocketevent_t& event : *serverEvents) {
|
||||
eventOutput.emplace_back(event);
|
||||
}
|
||||
if(max != 0u)
|
||||
max -= eventOutput.size();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
std::scoped_lock eventsLock{eventsMutex};
|
||||
{
|
||||
if(max == 0) // A limit of 0 indicates no limit
|
||||
max = (size_t)-1;
|
||||
std::lock_guard<std::mutex> lk(eventsMutex);
|
||||
|
||||
size_t count = 0;
|
||||
auto it = events.begin();
|
||||
while(it != events.end()) {
|
||||
if(filter.match(*it)) {
|
||||
eventOutput.push_back(*it);
|
||||
it = events.erase(it);
|
||||
if(++count >= max)
|
||||
break; // We now have as many written to output as we can
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
if(max == 0) // A limit of 0 indicates no limit
|
||||
max = (size_t)-1;
|
||||
|
||||
size_t count = 0;
|
||||
eventOutput.clear();
|
||||
auto it = events.begin();
|
||||
while(it != events.end()) {
|
||||
if(filter.match(*it)) {
|
||||
eventOutput.push_back(*it);
|
||||
it = events.erase(it);
|
||||
if(++count >= max)
|
||||
break; // We now have as many written to output as we can
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
#include <Tchar.h>
|
||||
|
||||
//Basic Functions
|
||||
FINDNEODEVICES icsneoFindNeoDevices;
|
||||
OPENNEODEVICE icsneoOpenNeoDevice;
|
||||
OPENDEVICE icsneoOpenDevice;
|
||||
CLOSEPORT icsneoClosePort;
|
||||
FREEOBJECT icsneoFreeObject;
|
||||
@@ -134,10 +132,6 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
|
||||
if((hAPIDLL = LoadLibrary(_T("icsneo40.dll"))) == NULL)
|
||||
return false;
|
||||
|
||||
|
||||
|
||||
icsneoFindNeoDevices = (FINDNEODEVICES) GetProcAddress(hAPIDLL, "icsneoFindNeoDevices");
|
||||
icsneoOpenNeoDevice = (OPENNEODEVICE) GetProcAddress(hAPIDLL, "icsneoOpenNeoDevice");
|
||||
icsneoOpenDevice = (OPENDEVICE) GetProcAddress(hAPIDLL, "icsneoOpenDevice");
|
||||
icsneoClosePort = (CLOSEPORT) GetProcAddress(hAPIDLL, "icsneoClosePort");
|
||||
icsneoFreeObject = (FREEOBJECT) GetProcAddress(hAPIDLL, "icsneoFreeObject");
|
||||
@@ -206,7 +200,7 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
|
||||
|
||||
icsneoEnableDOIPLine = (ENABLEDOIPACTIVATIONLINE)GetProcAddress(hAPIDLL, "icsneoEnableDOIPLine");
|
||||
|
||||
if(!icsneoFindNeoDevices || !icsneoOpenNeoDevice || !icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
|
||||
if(!icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
|
||||
!icsneoTxMessages || !icsneoGetMessages || !icsneoWaitForRxMessagesWithTimeOut ||
|
||||
!icsneoGetTimeStampForMsg || !icsneoEnableNetworkRXQueue || !icsneoGetISO15765Status || !icsneoTxMessagesEx ||
|
||||
!icsneoSetISO15765RxParameters || !icsneoGetConfiguration || !icsneoSendConfiguration ||
|
||||
|
||||
@@ -114,8 +114,6 @@ typedef int (__stdcall *SCRIPTWRITEISO15765TXMESSAGE)(void * hObject, unsigned
|
||||
|
||||
|
||||
//Basic Functions
|
||||
extern FINDNEODEVICES icsneoFindNeoDevices;
|
||||
extern OPENNEODEVICE icsneoOpenNeoDevice;
|
||||
extern OPENDEVICE icsneoOpenDevice;
|
||||
extern CLOSEPORT icsneoClosePort;
|
||||
extern FREEOBJECT icsneoFreeObject;
|
||||
|
||||
+350
-170
@@ -14,18 +14,19 @@
|
||||
#include "icsneo/communication/network.h"
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4100) // unreferenced formal parameter
|
||||
#pragma warning(disable : 4996) // STL time functions
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
typedef uint64_t legacymaphandle_t;
|
||||
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 = {
|
||||
{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 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)
|
||||
{
|
||||
@@ -73,6 +62,25 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
|
||||
return false;
|
||||
|
||||
const neomessage_frame_t& frame = *reinterpret_cast<const neomessage_frame_t*>(&newmsg);
|
||||
|
||||
auto copyStatusData = [&]() {
|
||||
oldmsg.NetworkID = static_cast<uint8_t>(frame.netid); // Note: NetID remapping from the original API is not supported
|
||||
oldmsg.NetworkID2 = static_cast<uint8_t>(frame.netid >> 8);
|
||||
oldmsg.DescriptionID = frame.description;
|
||||
oldmsg.StatusBitField = frame.status.statusBitfield[0];
|
||||
oldmsg.StatusBitField2 = frame.status.statusBitfield[1];
|
||||
oldmsg.StatusBitField3 = frame.status.statusBitfield[2];
|
||||
oldmsg.StatusBitField4 = frame.status.statusBitfield[3];
|
||||
};
|
||||
|
||||
auto copyFrameData = [&]() {
|
||||
oldmsg.ExtraDataPtr = (void*)frame.data;
|
||||
oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0;
|
||||
memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8));
|
||||
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
|
||||
copyStatusData();
|
||||
};
|
||||
|
||||
switch (Network::Type(frame.type))
|
||||
{
|
||||
case Network::Type::CAN:
|
||||
@@ -81,28 +89,42 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
|
||||
oldmsg.Protocol = frame.status.canfdFDF ? SPY_PROTOCOL_CANFD : SPY_PROTOCOL_CAN;
|
||||
oldmsg.NumberBytesData = static_cast<uint8_t>(std::min(frame.length, (size_t)255));
|
||||
oldmsg.NumberBytesHeader = 4;
|
||||
copyFrameData();
|
||||
break;
|
||||
case Network::Type::Ethernet:
|
||||
oldmsg.Protocol = SPY_PROTOCOL_ETHERNET;
|
||||
oldmsg.NumberBytesData = static_cast<uint8_t>(frame.length & 0xFF);
|
||||
oldmsg.NumberBytesHeader = static_cast<uint8_t>(frame.length >> 8);
|
||||
copyFrameData();
|
||||
break;
|
||||
case Network::Type::LIN:
|
||||
{
|
||||
const neomessage_lin_t& linFrame = *reinterpret_cast<const neomessage_lin_t*>(&frame);
|
||||
icsSpyMessageJ1850& linSpyMsg = *reinterpret_cast<icsSpyMessageJ1850*>(&oldmsg);
|
||||
linSpyMsg.Protocol = SPY_PROTOCOL_LIN;
|
||||
linSpyMsg.NumberBytesHeader = static_cast<uint8_t>(std::min(linFrame.length, static_cast<size_t>(3)));
|
||||
linSpyMsg.NumberBytesData = static_cast<uint8_t>(linFrame.length - linSpyMsg.NumberBytesHeader);
|
||||
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
|
||||
copyStatusData();
|
||||
if ((2 < linFrame.length) && (linFrame.length <= 10)) {
|
||||
auto copyBytes = std::min(linSpyMsg.NumberBytesData, static_cast<uint8_t>(6));
|
||||
std::memcpy(oldmsg.Data, frame.data, copyBytes);
|
||||
oldmsg.Data[copyBytes] = linFrame.checksum;
|
||||
} else if (2 == linFrame.length) {
|
||||
std::memset(oldmsg.Data, 0, 8);
|
||||
linSpyMsg.Header[linSpyMsg.NumberBytesHeader] = linFrame.checksum;
|
||||
++linSpyMsg.NumberBytesHeader;
|
||||
} else {
|
||||
std::memset(oldmsg.Data, 0, 8);
|
||||
}
|
||||
if (linFrame.linStatus.txCommander)
|
||||
linSpyMsg.StatusBitField |= SPY_STATUS_INIT_MESSAGE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
oldmsg.ExtraDataPtr = (void*)frame.data;
|
||||
oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0;
|
||||
memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8));
|
||||
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
|
||||
oldmsg.NetworkID = static_cast<uint8_t>(frame.netid); // Note: NetID remapping from the original API is not supported
|
||||
oldmsg.NetworkID2 = static_cast<uint8_t>(frame.netid >> 8);
|
||||
oldmsg.DescriptionID = frame.description;
|
||||
oldmsg.StatusBitField = frame.status.statusBitfield[0];
|
||||
oldmsg.StatusBitField2 = frame.status.statusBitfield[1];
|
||||
oldmsg.StatusBitField3 = frame.status.statusBitfield[2];
|
||||
oldmsg.StatusBitField4 = frame.status.statusBitfield[3];
|
||||
|
||||
// Timestamp - epoch = 1/1/2007 - 25ns per tick most of the time
|
||||
uint64_t t = frame.timestamp;
|
||||
uint16_t res = 0;
|
||||
@@ -127,6 +149,90 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SpyMessageToNeoMessage(const icsSpyMessage& oldmsg, neomessage_frame_t& frame, unsigned int& lNetworkID)
|
||||
{
|
||||
frame.netid = static_cast<uint16_t>(lNetworkID);
|
||||
frame.description = oldmsg.DescriptionID;
|
||||
|
||||
frame.status.statusBitfield[0] = oldmsg.StatusBitField;
|
||||
frame.status.statusBitfield[1] = oldmsg.StatusBitField2;
|
||||
frame.status.statusBitfield[2] = oldmsg.StatusBitField3;
|
||||
frame.status.statusBitfield[3] = oldmsg.StatusBitField4;
|
||||
|
||||
auto copyFrameDataPtr = [&]() {
|
||||
memcpy(frame.header, &oldmsg.ArbIDOrHeader, sizeof(frame.header));
|
||||
if ((oldmsg.ExtraDataPtr != nullptr) && (oldmsg.ExtraDataPtrEnabled == 1))
|
||||
frame.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
|
||||
else
|
||||
frame.data = oldmsg.Data;
|
||||
};
|
||||
|
||||
switch(oldmsg.Protocol)
|
||||
{
|
||||
case SPY_PROTOCOL_ETHERNET:
|
||||
{
|
||||
frame.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
|
||||
copyFrameDataPtr();
|
||||
break;
|
||||
}
|
||||
case SPY_PROTOCOL_LIN:
|
||||
{
|
||||
neomessage_lin_t& linFrame = *reinterpret_cast<neomessage_lin_t*>(&frame);
|
||||
const uint8_t numberBytesHeader = std::min(oldmsg.NumberBytesHeader, static_cast<uint8_t>(3));
|
||||
const uint8_t numberBytesData = std::min(oldmsg.NumberBytesData, static_cast<uint8_t>(7));
|
||||
frame.length = numberBytesHeader + numberBytesData;
|
||||
linFrame.type = ICSNEO_NETWORK_TYPE_LIN;
|
||||
if (oldmsg.StatusBitField & SPY_STATUS_INIT_MESSAGE)
|
||||
linFrame.linStatus.txCommander = true;
|
||||
else
|
||||
linFrame.linStatus.txResponder = true;
|
||||
|
||||
copyFrameDataPtr();
|
||||
uint8_t protID = linFrame.header[0] & 0x3Fu;
|
||||
auto bit = [&](uint8_t pos)->uint8_t { return ((protID >> pos) & 0x1u); };
|
||||
protID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7);
|
||||
protID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6);
|
||||
linFrame.header[0] = protID;
|
||||
if (frame.length > 2) {
|
||||
size_t checksum = 0;
|
||||
uint8_t* lastByte = nullptr;
|
||||
for(size_t idx = 1; idx < (frame.length - 1); ++idx) {
|
||||
if (idx < oldmsg.NumberBytesHeader) {
|
||||
checksum += frame.header[idx];
|
||||
lastByte = (frame.header + idx + 1);
|
||||
} else {
|
||||
checksum += frame.data[idx-numberBytesHeader];
|
||||
lastByte = const_cast<uint8_t*>(frame.data) + idx - numberBytesHeader + 1;
|
||||
}
|
||||
if (checksum > 255) { checksum -= 255; }
|
||||
}
|
||||
size_t enhanced = frame.header[0] + checksum;
|
||||
if (enhanced > 255) { enhanced -= 255; }
|
||||
checksum ^= 0xff;
|
||||
enhanced ^= 0xff;
|
||||
|
||||
if ((lastByte != nullptr) && (*lastByte != checksum) && (*lastByte == enhanced))
|
||||
linFrame.linStatus.txChecksumEnhanced = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPY_PROTOCOL_CANFD:
|
||||
{
|
||||
frame.length = oldmsg.NumberBytesData;
|
||||
frame.status.canfdFDF = true;
|
||||
copyFrameDataPtr();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
frame.length = oldmsg.NumberBytesData;
|
||||
copyFrameDataPtr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool Within(size_t value, size_t min, size_t max)
|
||||
{
|
||||
return ((min <= value) && (value < max));
|
||||
@@ -200,107 +306,79 @@ static inline size_t GetVnetAgnosticNetid(size_t fullNetid)
|
||||
int LegacyDLLExport icsneoFindDevices(NeoDeviceEx* devs, int* devCount, unsigned int* devTypes, unsigned int devTypeCount,
|
||||
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;
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
*devCount = filteredDeviceCount;
|
||||
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)
|
||||
// return the size only if devs is NULL.
|
||||
if (!devs)
|
||||
{
|
||||
icsneo_findAllDevices(nullptr, &count);
|
||||
*pNumDevices = (int)count;
|
||||
icsneo_findAllDevices(nullptr, (size_t*)devCount);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t bufferSize = (size_t)*pNumDevices;
|
||||
if (*pNumDevices < 0 || bufferSize > MAX_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
|
||||
// shrink the number of devices allowed to find
|
||||
if (*devCount > MAX_NEO_DEVICES) {
|
||||
*devCount = MAX_NEO_DEVICES;
|
||||
}
|
||||
|
||||
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);
|
||||
// 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()
|
||||
);
|
||||
}
|
||||
catch (const std::out_of_range&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*hObject = device;
|
||||
if (!icsneo_openDevice(device))
|
||||
return false;
|
||||
// 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;
|
||||
};
|
||||
|
||||
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
|
||||
// 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;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoOpenDevice(
|
||||
@@ -325,19 +403,50 @@ int LegacyDLLExport icsneoOpenDevice(
|
||||
return false;
|
||||
}
|
||||
|
||||
*hObject = device;
|
||||
if(!icsneo_openDevice(device))
|
||||
if (pNeoDeviceEx->neoDevice.NumberOfClients >= pNeoDeviceEx->neoDevice.MaxAllowedClients) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*hObject = device;
|
||||
if(!icsneo_openDevice(device)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
|
||||
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)
|
||||
{
|
||||
if (!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
if (pNumberOfErrors) {
|
||||
*pNumberOfErrors = 0;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -396,25 +505,9 @@ int LegacyDLLExport icsneoTxMessagesEx(void* hObject, icsSpyMessage* pMsg, unsig
|
||||
*NumTxed = 0;
|
||||
for (unsigned int i = 0; i < lNumMessages; i++)
|
||||
{
|
||||
const icsSpyMessage& oldmsg = pMsg[i];
|
||||
newmsg = {};
|
||||
newmsg.netid = (uint16_t)lNetworkID;
|
||||
newmsg.description = oldmsg.DescriptionID;
|
||||
memcpy(newmsg.header, &oldmsg.ArbIDOrHeader, sizeof(newmsg.header));
|
||||
if (oldmsg.Protocol != SPY_PROTOCOL_ETHERNET)
|
||||
newmsg.length = oldmsg.NumberBytesData;
|
||||
else
|
||||
newmsg.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
|
||||
if (oldmsg.ExtraDataPtr != nullptr && oldmsg.ExtraDataPtrEnabled == 1)
|
||||
newmsg.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
|
||||
else
|
||||
newmsg.data = oldmsg.Data;
|
||||
newmsg.status.statusBitfield[0] = oldmsg.StatusBitField;
|
||||
newmsg.status.statusBitfield[1] = oldmsg.StatusBitField2;
|
||||
newmsg.status.statusBitfield[2] = oldmsg.StatusBitField3;
|
||||
newmsg.status.statusBitfield[3] = oldmsg.StatusBitField4;
|
||||
if (oldmsg.Protocol == SPY_PROTOCOL_CANFD)
|
||||
newmsg.status.canfdFDF = true;
|
||||
const icsSpyMessage& oldMsg = pMsg[i];
|
||||
SpyMessageToNeoMessage(oldMsg, newmsg, lNetworkID);
|
||||
if (icsneo_transmit(device, reinterpret_cast<neomessage_t*>(&newmsg)))
|
||||
(*NumTxed)++;
|
||||
}
|
||||
@@ -464,6 +557,54 @@ void LegacyDLLExport icsneoSetISO15765RxParameters(void* hObject, int lNetwork,
|
||||
return;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetRTC(void* hObject, icsSpyTime* time)
|
||||
{
|
||||
if(!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||
|
||||
uint64_t time64 = 0;
|
||||
if(!icsneo_getRTC(device, &time64))
|
||||
return false;
|
||||
|
||||
std::time_t seconds = time64;
|
||||
/* To accommodate local time bugzilla #6600 https://intrepidcs.homeip.net:100/bugzilla/show_bug.cgi?id=6600 */
|
||||
// local time must be used here
|
||||
const auto timeInfo = std::localtime(&seconds);
|
||||
if(!timeInfo)
|
||||
return false;
|
||||
|
||||
time->sec = (unsigned char)timeInfo->tm_sec; // Will never hit 60 (leap second) because tm_sec comes from RTCCTIME
|
||||
time->min = (unsigned char)timeInfo->tm_min;
|
||||
time->hour = (unsigned char)timeInfo->tm_hour;
|
||||
time->day = (unsigned char)timeInfo->tm_mday;
|
||||
time->month = (unsigned char)timeInfo->tm_mon + 1;
|
||||
time->year = (unsigned char)timeInfo->tm_year % 100;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoSetRTC(void* hObject, const icsSpyTime* time)
|
||||
{
|
||||
if(!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||
|
||||
std::tm timeInfo{};
|
||||
timeInfo.tm_sec = time->sec;
|
||||
timeInfo.tm_min = time->min;
|
||||
timeInfo.tm_hour = time->hour;
|
||||
timeInfo.tm_mday = time->day;
|
||||
timeInfo.tm_mon = time->month - 1;
|
||||
timeInfo.tm_year = time->year + 100;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define timegm _mkgmtime
|
||||
#endif
|
||||
|
||||
return icsneo_setRTC(device, (uint64_t)timegm(&timeInfo));
|
||||
}
|
||||
|
||||
//Device Functions
|
||||
int LegacyDLLExport icsneoGetConfiguration(void* hObject, unsigned char* pData, int* lNumBytes)
|
||||
{
|
||||
@@ -653,11 +794,43 @@ int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int*
|
||||
return false;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, TCHAR* szErrorDescriptionShort, TCHAR* szErrorDescriptionLong,
|
||||
int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, char* szErrorDescriptionShort, char* szErrorDescriptionLong,
|
||||
int* lMaxLengthShort, int* lMaxLengthLong, int* lErrorSeverity, int* lRestartNeeded)
|
||||
{
|
||||
// TODO Implement
|
||||
return false;
|
||||
if (szErrorDescriptionShort == nullptr || szErrorDescriptionLong == nullptr
|
||||
|| lMaxLengthShort == nullptr || lMaxLengthLong == nullptr || lErrorSeverity == nullptr
|
||||
|| lRestartNeeded == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Set and send back 0. We will not restart the software.
|
||||
*lRestartNeeded = 0;
|
||||
|
||||
//Using the error number, get the description from the event.
|
||||
const char* tempDescription = APIEvent::DescriptionForType(APIEvent::Type(lErrorNumber));
|
||||
int descrLength = int(std::strlen(tempDescription));
|
||||
|
||||
//Check to make sure the length of the error is not >= the buffer.
|
||||
if (descrLength >= *lMaxLengthShort || descrLength >= *lMaxLengthLong)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Copy the error description to the inout Short and Long arguments.
|
||||
std::copy(tempDescription, tempDescription + descrLength, szErrorDescriptionShort);
|
||||
std::copy(tempDescription, tempDescription + descrLength, szErrorDescriptionLong);
|
||||
|
||||
//Add the null terminator.
|
||||
szErrorDescriptionShort[descrLength] = '\0';
|
||||
szErrorDescriptionLong[descrLength] = '\0';
|
||||
|
||||
//Update the inout lengths to what the actual length of the error is
|
||||
*lMaxLengthShort = *lMaxLengthLong = descrLength;
|
||||
|
||||
//Update the inout severity argument.
|
||||
*lErrorSeverity = int(APIEvent::Severity::Any);
|
||||
return true;
|
||||
}
|
||||
|
||||
//ISO15765-2 Functions
|
||||
@@ -736,6 +909,18 @@ int LegacyDLLExport icsneoStopSockServer(void* hObject)
|
||||
return false;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetDeviceStatus(void* hObject, icsDeviceStatus* deviceStatus, size_t* deviceStatusSize)
|
||||
{
|
||||
if (!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||
|
||||
if (deviceStatus == nullptr || deviceStatusSize == nullptr)
|
||||
return false;
|
||||
|
||||
return icsneo_getDeviceStatus(device, deviceStatus, deviceStatusSize);
|
||||
}
|
||||
|
||||
//CoreMini Script functions
|
||||
int LegacyDLLExport icsneoScriptStart(void* hObject, int iLocation)
|
||||
{
|
||||
@@ -797,20 +982,7 @@ int LegacyDLLExport icsneoScriptWriteAppSignal(void* hObject, unsigned int iInde
|
||||
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)
|
||||
{
|
||||
@@ -824,19 +996,6 @@ int LegacyDLLExport icsneoEnableNetworkCom(void* hObject, int Enable)
|
||||
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)
|
||||
{
|
||||
@@ -915,6 +1074,18 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
|
||||
case NEODEVICE_RADGIGALOG:
|
||||
*pDeviceSettingsType = DeviceRADGigalogSettingsType;
|
||||
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:
|
||||
return 0;
|
||||
}
|
||||
@@ -952,6 +1123,15 @@ int LegacyDLLExport icsneoGetDeviceSettings(void* hObject, SDeviceSettings* pSet
|
||||
return !!icsneo_settingsReadStructure(device, &pSettings->Settings, iNumBytes - offset);
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoLoadDefaultSettings(void* hObject)
|
||||
{
|
||||
if (!icsneoValidateHObject(hObject))
|
||||
return false;
|
||||
|
||||
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
|
||||
return icsneo_settingsApplyDefaults(device);
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoSetBitRateEx(void* hObject, unsigned long BitRate, int NetworkID, int iOptions)
|
||||
{
|
||||
if (!icsneoValidateHObject(hObject))
|
||||
@@ -1292,7 +1472,7 @@ int LegacyDLLExport icsneoSerialNumberFromString(unsigned long* serial, char* da
|
||||
return false;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NETWORK_ADAPTER_INFO* aInfo)
|
||||
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NDIS_ADAPTER_INFORMATION* aInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
if(LIBICSNEO_ENABLE_BINDINGS_PYTHON)
|
||||
add_subdirectory(python)
|
||||
endif()
|
||||
@@ -0,0 +1,24 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(PYBIND11_FINDPYTHON ON)
|
||||
find_package(pybind11 CONFIG REQUIRED)
|
||||
|
||||
pybind11_add_module(icsneopy
|
||||
icsneopy/api/event.cpp
|
||||
icsneopy/api/eventcallback.cpp
|
||||
icsneopy/api/eventmanager.cpp
|
||||
icsneopy/api/version.cpp
|
||||
icsneopy/device/devicetype.cpp
|
||||
icsneopy/communication/network.cpp
|
||||
icsneopy/communication/message/message.cpp
|
||||
icsneopy/communication/message/canmessage.cpp
|
||||
icsneopy/communication/message/ethernetmessage.cpp
|
||||
icsneopy/communication/message/tc10statusmessage.cpp
|
||||
icsneopy/communication/message/callback/messagecallback.cpp
|
||||
icsneopy/communication/message/filter/messagefilter.cpp
|
||||
icsneopy/device/device.cpp
|
||||
icsneopy/icsneocpp.cpp
|
||||
)
|
||||
target_link_libraries(icsneopy PRIVATE icsneocpp)
|
||||
|
||||
install(TARGETS icsneopy LIBRARY DESTINATION .)
|
||||
@@ -0,0 +1,170 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/api/event.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_event(pybind11::module_& m) {
|
||||
pybind11::class_<APIEvent, std::shared_ptr<APIEvent>> apiEvent(m, "APIEvent");
|
||||
pybind11::enum_<APIEvent::Type>(apiEvent, "Type")
|
||||
.value("Any", APIEvent::Type::Any)
|
||||
.value("InvalidNeoDevice", APIEvent::Type::InvalidNeoDevice)
|
||||
.value("RequiredParameterNull", APIEvent::Type::RequiredParameterNull)
|
||||
.value("BufferInsufficient", APIEvent::Type::BufferInsufficient)
|
||||
.value("OutputTruncated", APIEvent::Type::OutputTruncated)
|
||||
.value("ParameterOutOfRange", APIEvent::Type::ParameterOutOfRange)
|
||||
.value("DeviceCurrentlyOpen", APIEvent::Type::DeviceCurrentlyOpen)
|
||||
.value("DeviceCurrentlyClosed", APIEvent::Type::DeviceCurrentlyClosed)
|
||||
.value("DeviceCurrentlyOnline", APIEvent::Type::DeviceCurrentlyOnline)
|
||||
.value("DeviceCurrentlyOffline", APIEvent::Type::DeviceCurrentlyOffline)
|
||||
.value("DeviceCurrentlyPolling", APIEvent::Type::DeviceCurrentlyPolling)
|
||||
.value("DeviceNotCurrentlyPolling", APIEvent::Type::DeviceNotCurrentlyPolling)
|
||||
.value("UnsupportedTXNetwork", APIEvent::Type::UnsupportedTXNetwork)
|
||||
.value("MessageMaxLengthExceeded", APIEvent::Type::MessageMaxLengthExceeded)
|
||||
.value("ValueNotYetPresent", APIEvent::Type::ValueNotYetPresent)
|
||||
.value("Timeout", APIEvent::Type::Timeout)
|
||||
.value("WiVINotSupported", APIEvent::Type::WiVINotSupported)
|
||||
.value("RestrictedEntryFlag", APIEvent::Type::RestrictedEntryFlag)
|
||||
.value("NotSupported", APIEvent::Type::NotSupported)
|
||||
.value("PollingMessageOverflow", APIEvent::Type::PollingMessageOverflow)
|
||||
.value("NoSerialNumber", APIEvent::Type::NoSerialNumber)
|
||||
.value("IncorrectSerialNumber", APIEvent::Type::IncorrectSerialNumber)
|
||||
.value("SettingsReadError", APIEvent::Type::SettingsReadError)
|
||||
.value("SettingsVersionError", APIEvent::Type::SettingsVersionError)
|
||||
.value("SettingsLengthError", APIEvent::Type::SettingsLengthError)
|
||||
.value("SettingsChecksumError", APIEvent::Type::SettingsChecksumError)
|
||||
.value("SettingsNotAvailable", APIEvent::Type::SettingsNotAvailable)
|
||||
.value("SettingsReadOnly", APIEvent::Type::SettingsReadOnly)
|
||||
.value("CANSettingsNotAvailable", APIEvent::Type::CANSettingsNotAvailable)
|
||||
.value("CANFDSettingsNotAvailable", APIEvent::Type::CANFDSettingsNotAvailable)
|
||||
.value("LSFTCANSettingsNotAvailable", APIEvent::Type::LSFTCANSettingsNotAvailable)
|
||||
.value("SWCANSettingsNotAvailable", APIEvent::Type::SWCANSettingsNotAvailable)
|
||||
.value("BaudrateNotFound", APIEvent::Type::BaudrateNotFound)
|
||||
.value("UnexpectedNetworkType", APIEvent::Type::UnexpectedNetworkType)
|
||||
.value("DeviceFirmwareOutOfDate", APIEvent::Type::DeviceFirmwareOutOfDate)
|
||||
.value("SettingsStructureMismatch", APIEvent::Type::SettingsStructureMismatch)
|
||||
.value("SettingsStructureTruncated", APIEvent::Type::SettingsStructureTruncated)
|
||||
.value("NoDeviceResponse", APIEvent::Type::NoDeviceResponse)
|
||||
.value("MessageFormattingError", APIEvent::Type::MessageFormattingError)
|
||||
.value("CANFDNotSupported", APIEvent::Type::CANFDNotSupported)
|
||||
.value("RTRNotSupported", APIEvent::Type::RTRNotSupported)
|
||||
.value("DeviceDisconnected", APIEvent::Type::DeviceDisconnected)
|
||||
.value("OnlineNotSupported", APIEvent::Type::OnlineNotSupported)
|
||||
.value("TerminationNotSupportedDevice", APIEvent::Type::TerminationNotSupportedDevice)
|
||||
.value("TerminationNotSupportedNetwork", APIEvent::Type::TerminationNotSupportedNetwork)
|
||||
.value("AnotherInTerminationGroupEnabled", APIEvent::Type::AnotherInTerminationGroupEnabled)
|
||||
.value("NoSerialNumberFW", APIEvent::Type::NoSerialNumberFW)
|
||||
.value("NoSerialNumber12V", APIEvent::Type::NoSerialNumber12V)
|
||||
.value("NoSerialNumberFW12V", APIEvent::Type::NoSerialNumberFW12V)
|
||||
.value("EthPhyRegisterControlNotAvailable", APIEvent::Type::EthPhyRegisterControlNotAvailable)
|
||||
.value("DiskNotSupported", APIEvent::Type::DiskNotSupported)
|
||||
.value("EOFReached", APIEvent::Type::EOFReached)
|
||||
.value("SettingsDefaultsUsed", APIEvent::Type::SettingsDefaultsUsed)
|
||||
.value("AtomicOperationRetried", APIEvent::Type::AtomicOperationRetried)
|
||||
.value("AtomicOperationCompletedNonatomically", APIEvent::Type::AtomicOperationCompletedNonatomically)
|
||||
.value("WiVIStackRefreshFailed", APIEvent::Type::WiVIStackRefreshFailed)
|
||||
.value("WiVIUploadStackOverflow", APIEvent::Type::WiVIUploadStackOverflow)
|
||||
.value("I2CMessageExceedsMaxLength", APIEvent::Type::I2CMessageExceedsMaxLength)
|
||||
.value("A2BMessageIncompleteFrame", APIEvent::Type::A2BMessageIncompleteFrame)
|
||||
.value("CoreminiUploadVersionMismatch", APIEvent::Type::CoreminiUploadVersionMismatch)
|
||||
.value("DiskNotConnected", APIEvent::Type::DiskNotConnected)
|
||||
.value("UnexpectedResponse", APIEvent::Type::UnexpectedResponse)
|
||||
.value("LiveDataInvalidHandle", APIEvent::Type::LiveDataInvalidHandle)
|
||||
.value("LiveDataInvalidCommand", APIEvent::Type::LiveDataInvalidCommand)
|
||||
.value("LiveDataInvalidArgument", APIEvent::Type::LiveDataInvalidArgument)
|
||||
.value("LiveDataVersionMismatch", APIEvent::Type::LiveDataVersionMismatch)
|
||||
.value("LiveDataNoDeviceResponse", APIEvent::Type::LiveDataNoDeviceResponse)
|
||||
.value("LiveDataMaxSignalsReached", APIEvent::Type::LiveDataMaxSignalsReached)
|
||||
.value("LiveDataCommandFailed", APIEvent::Type::LiveDataCommandFailed)
|
||||
.value("LiveDataEncoderError", APIEvent::Type::LiveDataEncoderError)
|
||||
.value("LiveDataDecoderError", APIEvent::Type::LiveDataDecoderError)
|
||||
.value("LiveDataNotSupported", APIEvent::Type::LiveDataNotSupported)
|
||||
.value("LINSettingsNotAvailable", APIEvent::Type::LINSettingsNotAvailable)
|
||||
.value("ModeNotFound", APIEvent::Type::ModeNotFound)
|
||||
.value("AppErrorParsingFailed", APIEvent::Type::AppErrorParsingFailed)
|
||||
.value("FailedToRead", APIEvent::Type::FailedToRead)
|
||||
.value("FailedToWrite", APIEvent::Type::FailedToWrite)
|
||||
.value("DriverFailedToOpen", APIEvent::Type::DriverFailedToOpen)
|
||||
.value("DriverFailedToClose", APIEvent::Type::DriverFailedToClose)
|
||||
.value("PacketChecksumError", APIEvent::Type::PacketChecksumError)
|
||||
.value("TransmitBufferFull", APIEvent::Type::TransmitBufferFull)
|
||||
.value("DeviceInUse", APIEvent::Type::DeviceInUse)
|
||||
.value("PCAPCouldNotStart", APIEvent::Type::PCAPCouldNotStart)
|
||||
.value("PCAPCouldNotFindDevices", APIEvent::Type::PCAPCouldNotFindDevices)
|
||||
.value("PacketDecodingError", APIEvent::Type::PacketDecodingError)
|
||||
.value("SocketFailedToOpen", APIEvent::Type::SocketFailedToOpen)
|
||||
.value("FailedToBind", APIEvent::Type::FailedToBind)
|
||||
.value("ErrorSettingSocketOption", APIEvent::Type::ErrorSettingSocketOption)
|
||||
.value("GetIfAddrsError", APIEvent::Type::GetIfAddrsError)
|
||||
.value("SendToError", APIEvent::Type::SendToError)
|
||||
.value("MDIOMessageExceedsMaxLength", APIEvent::Type::MDIOMessageExceedsMaxLength)
|
||||
.value("FTOK", APIEvent::Type::FTOK)
|
||||
.value("FTInvalidHandle", APIEvent::Type::FTInvalidHandle)
|
||||
.value("FTDeviceNotFound", APIEvent::Type::FTDeviceNotFound)
|
||||
.value("FTDeviceNotOpened", APIEvent::Type::FTDeviceNotOpened)
|
||||
.value("FTIOError", APIEvent::Type::FTIOError)
|
||||
.value("FTInsufficientResources", APIEvent::Type::FTInsufficientResources)
|
||||
.value("FTInvalidParameter", APIEvent::Type::FTInvalidParameter)
|
||||
.value("FTInvalidBaudRate", APIEvent::Type::FTInvalidBaudRate)
|
||||
.value("FTDeviceNotOpenedForErase", APIEvent::Type::FTDeviceNotOpenedForErase)
|
||||
.value("FTDeviceNotOpenedForWrite", APIEvent::Type::FTDeviceNotOpenedForWrite)
|
||||
.value("FTFailedToWriteDevice", APIEvent::Type::FTFailedToWriteDevice)
|
||||
.value("FTEEPROMReadFailed", APIEvent::Type::FTEEPROMReadFailed)
|
||||
.value("FTEEPROMWriteFailed", APIEvent::Type::FTEEPROMWriteFailed)
|
||||
.value("FTEEPROMEraseFailed", APIEvent::Type::FTEEPROMEraseFailed)
|
||||
.value("FTEEPROMNotPresent", APIEvent::Type::FTEEPROMNotPresent)
|
||||
.value("FTEEPROMNotProgrammed", APIEvent::Type::FTEEPROMNotProgrammed)
|
||||
.value("FTInvalidArgs", APIEvent::Type::FTInvalidArgs)
|
||||
.value("FTNotSupported", APIEvent::Type::FTNotSupported)
|
||||
.value("FTNoMoreItems", APIEvent::Type::FTNoMoreItems)
|
||||
.value("FTTimeout", APIEvent::Type::FTTimeout)
|
||||
.value("FTOperationAborted", APIEvent::Type::FTOperationAborted)
|
||||
.value("FTReservedPipe", APIEvent::Type::FTReservedPipe)
|
||||
.value("FTInvalidControlRequestDirection", APIEvent::Type::FTInvalidControlRequestDirection)
|
||||
.value("FTInvalidControlRequestType", APIEvent::Type::FTInvalidControlRequestType)
|
||||
.value("FTIOPending", APIEvent::Type::FTIOPending)
|
||||
.value("FTIOIncomplete", APIEvent::Type::FTIOIncomplete)
|
||||
.value("FTHandleEOF", APIEvent::Type::FTHandleEOF)
|
||||
.value("FTBusy", APIEvent::Type::FTBusy)
|
||||
.value("FTNoSystemResources", APIEvent::Type::FTNoSystemResources)
|
||||
.value("FTDeviceListNotReady", APIEvent::Type::FTDeviceListNotReady)
|
||||
.value("FTDeviceNotConnected", APIEvent::Type::FTDeviceNotConnected)
|
||||
.value("FTIncorrectDevicePath", APIEvent::Type::FTIncorrectDevicePath)
|
||||
.value("FTOtherError", APIEvent::Type::FTOtherError)
|
||||
.value("VSABufferCorrupted", APIEvent::Type::VSABufferCorrupted)
|
||||
.value("VSATimestampNotFound", APIEvent::Type::VSATimestampNotFound)
|
||||
.value("VSABufferFormatError", APIEvent::Type::VSABufferFormatError)
|
||||
.value("VSAMaxReadAttemptsReached", APIEvent::Type::VSAMaxReadAttemptsReached)
|
||||
.value("VSAByteParseFailure", APIEvent::Type::VSAByteParseFailure)
|
||||
.value("VSAExtendedMessageError", APIEvent::Type::VSAExtendedMessageError)
|
||||
.value("VSAOtherError", APIEvent::Type::VSAOtherError)
|
||||
.value("NoErrorFound", APIEvent::Type::NoErrorFound)
|
||||
.value("TooManyEvents", APIEvent::Type::TooManyEvents)
|
||||
.value("Unknown", APIEvent::Type::Unknown);
|
||||
|
||||
pybind11::enum_<APIEvent::Severity>(apiEvent, "Severity")
|
||||
.value("Any", APIEvent::Severity::Any)
|
||||
.value("EventInfo", APIEvent::Severity::EventInfo)
|
||||
.value("EventWarning", APIEvent::Severity::EventWarning)
|
||||
.value("Error", APIEvent::Severity::Error);
|
||||
|
||||
apiEvent
|
||||
.def("get_type", &APIEvent::getType)
|
||||
.def("get_severity", &APIEvent::getSeverity)
|
||||
.def("get_description", &APIEvent::getDescription)
|
||||
.def("describe", &APIEvent::describe)
|
||||
.def("__repr__", &APIEvent::describe);
|
||||
|
||||
pybind11::class_<EventFilter, std::shared_ptr<EventFilter>>(m, "EventFilter")
|
||||
.def(pybind11::init())
|
||||
.def(pybind11::init<APIEvent::Type>())
|
||||
.def(pybind11::init<APIEvent::Severity>())
|
||||
.def_readwrite("type", &EventFilter::type)
|
||||
.def_readwrite("severity", &EventFilter::severity)
|
||||
.def_readwrite("serial", &EventFilter::serial);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/api/eventcallback.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_eventcallback(pybind11::module_& m) {
|
||||
pybind11::class_<EventCallback>(m, "EventCallback")
|
||||
.def(pybind11::init<EventCallback::fn_eventCallback, EventFilter>())
|
||||
.def(pybind11::init<EventCallback::fn_eventCallback>());
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_eventmanager(pybind11::module_& m) {
|
||||
pybind11::class_<EventManager>(m, "EventManager")
|
||||
.def_static("get_instance", &EventManager::GetInstance, pybind11::return_value_policy::reference)
|
||||
.def("add_event_callback", &EventManager::addEventCallback)
|
||||
.def("remove_event_callback", &EventManager::removeEventCallback)
|
||||
.def("get_last_error", &EventManager::getLastError);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/api/version.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_version(pybind11::module_& m) {
|
||||
pybind11::class_<neoversion_t>(m, "NeoVersion")
|
||||
.def_readonly("major", &neoversion_t::major)
|
||||
.def_readonly("minor", &neoversion_t::minor)
|
||||
.def_readonly("patch", &neoversion_t::patch)
|
||||
.def_readonly("metadata", &neoversion_t::metadata)
|
||||
.def_readonly("buildBranch", &neoversion_t::buildBranch)
|
||||
.def_readonly("buildTag", &neoversion_t::buildTag)
|
||||
.def("__repr__", [](const neoversion_t& self) -> std::string {
|
||||
std::stringstream ss;
|
||||
ss << self;
|
||||
return ss.str();
|
||||
});
|
||||
m.def("get_version", &GetVersion);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/communication/message/callback/messagecallback.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_messagecallback(pybind11::module_& m) {
|
||||
pybind11::class_<MessageCallback, std::shared_ptr<MessageCallback>>(m, "MessageCallback")
|
||||
.def(pybind11::init<MessageCallback::fn_messageCallback, std::shared_ptr<MessageFilter>>());
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/communication/message/canmessage.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_canmessage(pybind11::module_& m) {
|
||||
pybind11::class_<CANMessage, std::shared_ptr<CANMessage>, Frame>(m, "CANMessage")
|
||||
.def(pybind11::init())
|
||||
.def_readwrite("arbid", &CANMessage::arbid)
|
||||
.def_readwrite("dlcOnWire", &CANMessage::dlcOnWire)
|
||||
.def_readwrite("isRemote", &CANMessage::isRemote)
|
||||
.def_readwrite("isExtended", &CANMessage::isExtended)
|
||||
.def_readwrite("isCANFD", &CANMessage::isCANFD)
|
||||
.def_readwrite("baudrateSwitch", &CANMessage::baudrateSwitch)
|
||||
.def_readwrite("errorStateIndicator", &CANMessage::errorStateIndicator);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/communication/message/ethernetmessage.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_ethernetmessage(pybind11::module_& m) {
|
||||
pybind11::class_<MACAddress>(m, "MACAddress")
|
||||
.def("to_string", &MACAddress::toString)
|
||||
.def("__repr__", &MACAddress::toString);
|
||||
|
||||
pybind11::class_<EthernetMessage, std::shared_ptr<EthernetMessage>, Frame>(m, "EthernetMessage")
|
||||
.def(pybind11::init())
|
||||
.def_readwrite("preemptionEnabled", &EthernetMessage::preemptionEnabled)
|
||||
.def_readwrite("preemptionFlags", &EthernetMessage::preemptionFlags)
|
||||
.def_readwrite("fcsAvailable", &EthernetMessage::fcsAvailable)
|
||||
.def_readwrite("frameTooShort", &EthernetMessage::frameTooShort)
|
||||
.def_readwrite("noPadding", &EthernetMessage::noPadding)
|
||||
.def("get_destination_mac", &EthernetMessage::getDestinationMAC, pybind11::return_value_policy::reference)
|
||||
.def("get_source_mac", &EthernetMessage::getSourceMAC, pybind11::return_value_policy::reference)
|
||||
.def("get_ether_type", &EthernetMessage::getEtherType);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/communication/message/filter/messagefilter.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_messagefilter(pybind11::module_& m) {
|
||||
pybind11::class_<MessageFilter, std::shared_ptr<MessageFilter>>(m, "MessageFilter")
|
||||
.def(pybind11::init<Network::NetID>());
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_message(pybind11::module_& m) {
|
||||
pybind11::class_<Message, std::shared_ptr<Message>> message(m, "Message");
|
||||
pybind11::enum_<Message::Type>(message, "Type")
|
||||
.value("Frame", Message::Type::Frame)
|
||||
.value("CANErrorCount", Message::Type::CANErrorCount)
|
||||
.value("LINHeaderOnly", Message::Type::LINHeaderOnly)
|
||||
.value("LINBreak", Message::Type::LINBreak)
|
||||
.value("Invalid", Message::Type::Invalid)
|
||||
.value("RawMessage", Message::Type::RawMessage)
|
||||
.value("ReadSettings", Message::Type::ReadSettings)
|
||||
.value("ResetStatus", Message::Type::ResetStatus)
|
||||
.value("DeviceVersion", Message::Type::DeviceVersion)
|
||||
.value("Main51", Message::Type::Main51)
|
||||
.value("FlexRayControl", Message::Type::FlexRayControl)
|
||||
.value("EthernetPhyRegister", Message::Type::EthernetPhyRegister)
|
||||
.value("LogicalDiskInfo", Message::Type::LogicalDiskInfo)
|
||||
.value("ExtendedResponse", Message::Type::ExtendedResponse)
|
||||
.value("WiVICommandResponse", Message::Type::WiVICommandResponse)
|
||||
.value("ScriptStatus", Message::Type::ScriptStatus)
|
||||
.value("ComponentVersions", Message::Type::ComponentVersions)
|
||||
.value("SupportedFeatures", Message::Type::SupportedFeatures)
|
||||
.value("GenericBinaryStatus", Message::Type::GenericBinaryStatus)
|
||||
.value("LiveData", Message::Type::LiveData)
|
||||
.value("HardwareInfo", Message::Type::HardwareInfo)
|
||||
.value("TC10Status", Message::Type::TC10Status)
|
||||
.value("AppError", Message::Type::AppError);
|
||||
|
||||
message.def(pybind11::init<Message::Type>());
|
||||
message.def_readonly("type", &Message::type);
|
||||
message.def_readwrite("timestamp", &Message::timestamp);
|
||||
|
||||
pybind11::class_<RawMessage, std::shared_ptr<RawMessage>, Message>(m, "RawMessage")
|
||||
.def_readwrite("network", &RawMessage::network)
|
||||
.def_readwrite("data", &RawMessage::data);
|
||||
|
||||
pybind11::class_<Frame, std::shared_ptr<Frame>, RawMessage>(m, "Frame")
|
||||
.def_readwrite("description", &Frame::description)
|
||||
.def_readwrite("transmitted", &Frame::transmitted)
|
||||
.def_readwrite("error", &Frame::error);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/communication/message/tc10statusmessage.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_tc10statusmessage(pybind11::module_& m) {
|
||||
pybind11::enum_<TC10WakeStatus>(m, "TC10WakeStatus")
|
||||
.value("NoWakeReceived", TC10WakeStatus::NoWakeReceived)
|
||||
.value("WakeReceived", TC10WakeStatus::WakeReceived);
|
||||
|
||||
pybind11::enum_<TC10SleepStatus>(m, "TC10SleepStatus")
|
||||
.value("NoSleepReceived", TC10SleepStatus::NoSleepReceived)
|
||||
.value("SleepReceived", TC10SleepStatus::SleepReceived)
|
||||
.value("SleepFailed", TC10SleepStatus::SleepFailed)
|
||||
.value("SleepAborted", TC10SleepStatus::SleepAborted);
|
||||
|
||||
pybind11::class_<TC10StatusMessage, std::shared_ptr<TC10StatusMessage>, Message>(m, "TC10StatusMessage")
|
||||
.def_readonly("wakeStatus", &TC10StatusMessage::wakeStatus)
|
||||
.def_readonly("sleepStatus", &TC10StatusMessage::sleepStatus);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
@@ -0,0 +1,174 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/communication/network.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_network(pybind11::module_& m) {
|
||||
pybind11::class_<Network> network(m, "Network");
|
||||
|
||||
pybind11::enum_<Network::NetID>(network, "NetID")
|
||||
.value("Device", Network::NetID::Device)
|
||||
.value("HSCAN", Network::NetID::HSCAN)
|
||||
.value("MSCAN", Network::NetID::MSCAN)
|
||||
.value("SWCAN", Network::NetID::SWCAN)
|
||||
.value("LSFTCAN", Network::NetID::LSFTCAN)
|
||||
.value("FordSCP", Network::NetID::FordSCP)
|
||||
.value("J1708", Network::NetID::J1708)
|
||||
.value("Aux", Network::NetID::Aux)
|
||||
.value("J1850VPW", Network::NetID::J1850VPW)
|
||||
.value("ISO9141", Network::NetID::ISO9141)
|
||||
.value("DiskData", Network::NetID::DiskData)
|
||||
.value("Main51", Network::NetID::Main51)
|
||||
.value("RED", Network::NetID::RED)
|
||||
.value("SCI", Network::NetID::SCI)
|
||||
.value("ISO9141_2", Network::NetID::ISO9141_2)
|
||||
.value("ISO14230", Network::NetID::ISO14230)
|
||||
.value("LIN", Network::NetID::LIN)
|
||||
.value("OP_Ethernet1", Network::NetID::OP_Ethernet1)
|
||||
.value("OP_Ethernet2", Network::NetID::OP_Ethernet2)
|
||||
.value("OP_Ethernet3", Network::NetID::OP_Ethernet3)
|
||||
.value("RED_EXT_MEMORYREAD", Network::NetID::RED_EXT_MEMORYREAD)
|
||||
.value("RED_INT_MEMORYREAD", Network::NetID::RED_INT_MEMORYREAD)
|
||||
.value("RED_DFLASH_READ", Network::NetID::RED_DFLASH_READ)
|
||||
.value("NeoMemorySDRead", Network::NetID::NeoMemorySDRead)
|
||||
.value("CAN_ERRBITS", Network::NetID::CAN_ERRBITS)
|
||||
.value("NeoMemoryWriteDone", Network::NetID::NeoMemoryWriteDone)
|
||||
.value("RED_WAVE_CAN1_LOGICAL", Network::NetID::RED_WAVE_CAN1_LOGICAL)
|
||||
.value("RED_WAVE_CAN2_LOGICAL", Network::NetID::RED_WAVE_CAN2_LOGICAL)
|
||||
.value("RED_WAVE_LIN1_LOGICAL", Network::NetID::RED_WAVE_LIN1_LOGICAL)
|
||||
.value("RED_WAVE_LIN2_LOGICAL", Network::NetID::RED_WAVE_LIN2_LOGICAL)
|
||||
.value("RED_WAVE_LIN1_ANALOG", Network::NetID::RED_WAVE_LIN1_ANALOG)
|
||||
.value("RED_WAVE_LIN2_ANALOG", Network::NetID::RED_WAVE_LIN2_ANALOG)
|
||||
.value("RED_WAVE_MISC_ANALOG", Network::NetID::RED_WAVE_MISC_ANALOG)
|
||||
.value("RED_WAVE_MISCDIO2_LOGICAL", Network::NetID::RED_WAVE_MISCDIO2_LOGICAL)
|
||||
.value("RED_NETWORK_COM_ENABLE_EX", Network::NetID::RED_NETWORK_COM_ENABLE_EX)
|
||||
.value("RED_NEOVI_NETWORK", Network::NetID::RED_NEOVI_NETWORK)
|
||||
.value("RED_READ_BAUD_SETTINGS", Network::NetID::RED_READ_BAUD_SETTINGS)
|
||||
.value("RED_OLDFORMAT", Network::NetID::RED_OLDFORMAT)
|
||||
.value("RED_SCOPE_CAPTURE", Network::NetID::RED_SCOPE_CAPTURE)
|
||||
.value("RED_HARDWARE_EXCEP", Network::NetID::RED_HARDWARE_EXCEP)
|
||||
.value("RED_GET_RTC", Network::NetID::RED_GET_RTC)
|
||||
.value("ISO9141_3", Network::NetID::ISO9141_3)
|
||||
.value("HSCAN2", Network::NetID::HSCAN2)
|
||||
.value("HSCAN3", Network::NetID::HSCAN3)
|
||||
.value("OP_Ethernet4", Network::NetID::OP_Ethernet4)
|
||||
.value("OP_Ethernet5", Network::NetID::OP_Ethernet5)
|
||||
.value("ISO9141_4", Network::NetID::ISO9141_4)
|
||||
.value("LIN2", Network::NetID::LIN2)
|
||||
.value("LIN3", Network::NetID::LIN3)
|
||||
.value("LIN4", Network::NetID::LIN4)
|
||||
.value("RED_App_Error", Network::NetID::RED_App_Error)
|
||||
.value("CGI", Network::NetID::CGI)
|
||||
.value("Reset_Status", Network::NetID::Reset_Status)
|
||||
.value("FB_Status", Network::NetID::FB_Status)
|
||||
.value("App_Signal_Status", Network::NetID::App_Signal_Status)
|
||||
.value("Read_Datalink_Cm_Tx_Msg", Network::NetID::Read_Datalink_Cm_Tx_Msg)
|
||||
.value("Read_Datalink_Cm_Rx_Msg", Network::NetID::Read_Datalink_Cm_Rx_Msg)
|
||||
.value("Logging_Overflow", Network::NetID::Logging_Overflow)
|
||||
.value("ReadSettings", Network::NetID::ReadSettings)
|
||||
.value("HSCAN4", Network::NetID::HSCAN4)
|
||||
.value("HSCAN5", Network::NetID::HSCAN5)
|
||||
.value("RS232", Network::NetID::RS232)
|
||||
.value("UART", Network::NetID::UART)
|
||||
.value("UART2", Network::NetID::UART2)
|
||||
.value("UART3", Network::NetID::UART3)
|
||||
.value("UART4", Network::NetID::UART4)
|
||||
.value("SWCAN2", Network::NetID::SWCAN2)
|
||||
.value("Ethernet_DAQ", Network::NetID::Ethernet_DAQ)
|
||||
.value("Data_To_Host", Network::NetID::Data_To_Host)
|
||||
.value("TextAPI_To_Host", Network::NetID::TextAPI_To_Host)
|
||||
.value("SPI1", Network::NetID::SPI1)
|
||||
.value("OP_Ethernet6", Network::NetID::OP_Ethernet6)
|
||||
.value("Red_VBat", Network::NetID::Red_VBat)
|
||||
.value("OP_Ethernet7", Network::NetID::OP_Ethernet7)
|
||||
.value("OP_Ethernet8", Network::NetID::OP_Ethernet8)
|
||||
.value("OP_Ethernet9", Network::NetID::OP_Ethernet9)
|
||||
.value("OP_Ethernet10", Network::NetID::OP_Ethernet10)
|
||||
.value("OP_Ethernet11", Network::NetID::OP_Ethernet11)
|
||||
.value("FlexRay1a", Network::NetID::FlexRay1a)
|
||||
.value("FlexRay1b", Network::NetID::FlexRay1b)
|
||||
.value("FlexRay2a", Network::NetID::FlexRay2a)
|
||||
.value("FlexRay2b", Network::NetID::FlexRay2b)
|
||||
.value("LIN5", Network::NetID::LIN5)
|
||||
.value("FlexRay", Network::NetID::FlexRay)
|
||||
.value("FlexRay2", Network::NetID::FlexRay2)
|
||||
.value("OP_Ethernet12", Network::NetID::OP_Ethernet12)
|
||||
.value("I2C", Network::NetID::I2C)
|
||||
.value("MOST25", Network::NetID::MOST25)
|
||||
.value("MOST50", Network::NetID::MOST50)
|
||||
.value("MOST150", Network::NetID::MOST150)
|
||||
.value("Ethernet", Network::NetID::Ethernet)
|
||||
.value("GMFSA", Network::NetID::GMFSA)
|
||||
.value("TCP", Network::NetID::TCP)
|
||||
.value("HSCAN6", Network::NetID::HSCAN6)
|
||||
.value("HSCAN7", Network::NetID::HSCAN7)
|
||||
.value("LIN6", Network::NetID::LIN6)
|
||||
.value("LSFTCAN2", Network::NetID::LSFTCAN2)
|
||||
.value("LogicalDiskInfo", Network::NetID::LogicalDiskInfo)
|
||||
.value("WiVICommand", Network::NetID::WiVICommand)
|
||||
.value("ScriptStatus", Network::NetID::ScriptStatus)
|
||||
.value("EthPHYControl", Network::NetID::EthPHYControl)
|
||||
.value("ExtendedCommand", Network::NetID::ExtendedCommand)
|
||||
.value("ExtendedData", Network::NetID::ExtendedData)
|
||||
.value("FlexRayControl", Network::NetID::FlexRayControl)
|
||||
.value("CoreMiniPreLoad", Network::NetID::CoreMiniPreLoad)
|
||||
.value("HW_COM_Latency_Test", Network::NetID::HW_COM_Latency_Test)
|
||||
.value("DeviceStatus", Network::NetID::DeviceStatus)
|
||||
.value("UDP", Network::NetID::UDP)
|
||||
.value("ForwardedMessage", Network::NetID::ForwardedMessage)
|
||||
.value("I2C2", Network::NetID::I2C2)
|
||||
.value("I2C3", Network::NetID::I2C3)
|
||||
.value("I2C4", Network::NetID::I2C4)
|
||||
.value("Ethernet2", Network::NetID::Ethernet2)
|
||||
.value("A2B1", Network::NetID::A2B1)
|
||||
.value("A2B2", Network::NetID::A2B2)
|
||||
.value("Ethernet3", Network::NetID::Ethernet3)
|
||||
.value("WBMS", Network::NetID::WBMS)
|
||||
.value("DWCAN9", Network::NetID::DWCAN9)
|
||||
.value("DWCAN10", Network::NetID::DWCAN10)
|
||||
.value("DWCAN11", Network::NetID::DWCAN11)
|
||||
.value("DWCAN12", Network::NetID::DWCAN12)
|
||||
.value("DWCAN13", Network::NetID::DWCAN13)
|
||||
.value("DWCAN14", Network::NetID::DWCAN14)
|
||||
.value("DWCAN15", Network::NetID::DWCAN15)
|
||||
.value("DWCAN16", Network::NetID::DWCAN16)
|
||||
.value("LIN7", Network::NetID::LIN7)
|
||||
.value("LIN8", Network::NetID::LIN8)
|
||||
.value("SPI2", Network::NetID::SPI2)
|
||||
.value("MDIO1", Network::NetID::MDIO1)
|
||||
.value("MDIO2", Network::NetID::MDIO2)
|
||||
.value("MDIO3", Network::NetID::MDIO3)
|
||||
.value("MDIO4", Network::NetID::MDIO4)
|
||||
.value("MDIO5", Network::NetID::MDIO5)
|
||||
.value("MDIO6", Network::NetID::MDIO6)
|
||||
.value("MDIO7", Network::NetID::MDIO7)
|
||||
.value("MDIO8", Network::NetID::MDIO8)
|
||||
.value("OP_Ethernet13", Network::NetID::OP_Ethernet13)
|
||||
.value("OP_Ethernet14", Network::NetID::OP_Ethernet14)
|
||||
.value("OP_Ethernet15", Network::NetID::OP_Ethernet15)
|
||||
.value("OP_Ethernet16", Network::NetID::OP_Ethernet16)
|
||||
.value("SPI3", Network::NetID::SPI3)
|
||||
.value("SPI4", Network::NetID::SPI4)
|
||||
.value("SPI5", Network::NetID::SPI5)
|
||||
.value("SPI6", Network::NetID::SPI6)
|
||||
.value("SPI7", Network::NetID::SPI7)
|
||||
.value("SPI8", Network::NetID::SPI8)
|
||||
.value("LIN9", Network::NetID::LIN9)
|
||||
.value("LIN10", Network::NetID::LIN10)
|
||||
.value("LIN11", Network::NetID::LIN11)
|
||||
.value("LIN12", Network::NetID::LIN12)
|
||||
.value("LIN13", Network::NetID::LIN13)
|
||||
.value("LIN14", Network::NetID::LIN14)
|
||||
.value("LIN15", Network::NetID::LIN15)
|
||||
.value("LIN16", Network::NetID::LIN16)
|
||||
.value("Any", Network::NetID::Any)
|
||||
.value("Invalid", Network::NetID::Invalid);
|
||||
|
||||
network.def(pybind11::init<Network::NetID>());
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/device/device.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_device(pybind11::module_& m) {
|
||||
pybind11::class_<Device, std::shared_ptr<Device>>(m, "Device")
|
||||
.def("get_type", &Device::getType)
|
||||
.def("get_serial", &Device::getSerial)
|
||||
.def("get_serial_number", &Device::getSerialNumber)
|
||||
.def("get_product_name", &Device::getProductName)
|
||||
.def("open", [](Device& device) { return device.open(); })
|
||||
.def("close", &Device::close)
|
||||
.def("is_open", &Device::isOpen)
|
||||
.def("go_online", &Device::goOnline)
|
||||
.def("go_offline", &Device::goOffline)
|
||||
.def("is_online", &Device::isOnline).def("enable_message_polling", &Device::enableMessagePolling)
|
||||
.def("disable_message_polling", &Device::disableMessagePolling)
|
||||
.def("is_message_polling_enabled", &Device::isMessagePollingEnabled)
|
||||
.def("get_messages", [](Device& device) { return device.getMessages(); })
|
||||
.def("get_current_message_count", &Device::getCurrentMessageCount)
|
||||
.def("get_polling_message_limit", &Device::getPollingMessageLimit)
|
||||
.def("set_polling_message_limit", &Device::setPollingMessageLimit)
|
||||
.def("add_message_callback", &Device::addMessageCallback)
|
||||
.def("remove_message_callback", &Device::removeMessageCallback)
|
||||
.def("transmit", pybind11::overload_cast<std::shared_ptr<Frame>>(&Device::transmit))
|
||||
.def("get_supported_rx_networks", &Device::getSupportedRXNetworks, pybind11::return_value_policy::reference)
|
||||
.def("get_supported_tx_networks", &Device::getSupportedTXNetworks, pybind11::return_value_policy::reference)
|
||||
.def("get_rtc", &Device::getRTC)
|
||||
.def("set_rtc", &Device::setRTC)
|
||||
.def("describe", &Device::describe)
|
||||
.def("is_online_supported", &Device::isOnlineSupported)
|
||||
.def("supports_tc10", &Device::supportsTC10)
|
||||
.def("request_tc10_wake", &Device::requestTC10Wake)
|
||||
.def("request_tc10_sleep", &Device::requestTC10Sleep)
|
||||
.def("__repr__", &Device::describe);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/device/devicetype.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_devicetype(pybind11::module_& m) {
|
||||
pybind11::class_<DeviceType> deviceType(m, "DeviceType");
|
||||
pybind11::enum_<DeviceType::Enum>(deviceType, "Enum")
|
||||
.value("Unknown", DeviceType::Enum::Unknown)
|
||||
.value("BLUE", DeviceType::Enum::BLUE)
|
||||
.value("ECU_AVB", DeviceType::Enum::ECU_AVB)
|
||||
.value("RADSupermoon", DeviceType::Enum::RADSupermoon)
|
||||
.value("DW_VCAN", DeviceType::Enum::DW_VCAN)
|
||||
.value("RADMoon2", DeviceType::Enum::RADMoon2)
|
||||
.value("RADMars", DeviceType::Enum::RADMars)
|
||||
.value("VCAN4_1", DeviceType::Enum::VCAN4_1)
|
||||
.value("FIRE", DeviceType::Enum::FIRE)
|
||||
.value("RADPluto", DeviceType::Enum::RADPluto)
|
||||
.value("VCAN4_2EL", DeviceType::Enum::VCAN4_2EL)
|
||||
.value("RADIO_CANHUB", DeviceType::Enum::RADIO_CANHUB)
|
||||
.value("NEOECU12", DeviceType::Enum::NEOECU12)
|
||||
.value("OBD2_LCBADGE", DeviceType::Enum::OBD2_LCBADGE)
|
||||
.value("RADMoonDuo", DeviceType::Enum::RADMoonDuo)
|
||||
.value("FIRE3", DeviceType::Enum::FIRE3)
|
||||
.value("VCAN3", DeviceType::Enum::VCAN3)
|
||||
.value("RADJupiter", DeviceType::Enum::RADJupiter)
|
||||
.value("VCAN4_IND", DeviceType::Enum::VCAN4_IND)
|
||||
.value("RADGigastar", DeviceType::Enum::RADGigastar)
|
||||
.value("RED2", DeviceType::Enum::RED2)
|
||||
.value("EtherBADGE", DeviceType::Enum::EtherBADGE)
|
||||
.value("RAD_A2B", DeviceType::Enum::RAD_A2B)
|
||||
.value("RADEpsilon", DeviceType::Enum::RADEpsilon)
|
||||
.value("RADMoon3", DeviceType::Enum::RADMoon3)
|
||||
.value("RADComet", DeviceType::Enum::RADComet)
|
||||
.value("FIRE3_FlexRay", DeviceType::Enum::FIRE3_FlexRay)
|
||||
.value("Connect", DeviceType::Enum::Connect)
|
||||
.value("RADComet3", DeviceType::Enum::RADComet3)
|
||||
.value("RADMoonT1S", DeviceType::Enum::RADMoonT1S)
|
||||
.value("RADGigastar2", DeviceType::Enum::RADGigastar2)
|
||||
.value("RED", DeviceType::Enum::RED)
|
||||
.value("ECU", DeviceType::Enum::ECU)
|
||||
.value("IEVB", DeviceType::Enum::IEVB)
|
||||
.value("Pendant", DeviceType::Enum::Pendant)
|
||||
.value("OBD2_PRO", DeviceType::Enum::OBD2_PRO)
|
||||
.value("ECUChip_UART", DeviceType::Enum::ECUChip_UART)
|
||||
.value("PLASMA", DeviceType::Enum::PLASMA)
|
||||
.value("DONT_REUSE0", DeviceType::Enum::DONT_REUSE0)
|
||||
.value("NEOAnalog", DeviceType::Enum::NEOAnalog)
|
||||
.value("CT_OBD", DeviceType::Enum::CT_OBD)
|
||||
.value("DONT_REUSE1", DeviceType::Enum::DONT_REUSE1)
|
||||
.value("DONT_REUSE2", DeviceType::Enum::DONT_REUSE2)
|
||||
.value("ION", DeviceType::Enum::ION)
|
||||
.value("RADStar", DeviceType::Enum::RADStar)
|
||||
.value("DONT_REUSE3", DeviceType::Enum::DONT_REUSE3)
|
||||
.value("VCAN4_4", DeviceType::Enum::VCAN4_4)
|
||||
.value("VCAN4_2", DeviceType::Enum::VCAN4_2)
|
||||
.value("CMProbe", DeviceType::Enum::CMProbe)
|
||||
.value("EEVB", DeviceType::Enum::EEVB)
|
||||
.value("VCANrf", DeviceType::Enum::VCANrf)
|
||||
.value("FIRE2", DeviceType::Enum::FIRE2)
|
||||
.value("Flex", DeviceType::Enum::Flex)
|
||||
.value("RADGalaxy", DeviceType::Enum::RADGalaxy)
|
||||
.value("RADStar2", DeviceType::Enum::RADStar2)
|
||||
.value("VividCAN", DeviceType::Enum::VividCAN)
|
||||
.value("OBD2_SIM", DeviceType::Enum::OBD2_SIM);
|
||||
deviceType.def(pybind11::init<DeviceType::Enum>());
|
||||
deviceType.def("get_device_type", &DeviceType::getDeviceType);
|
||||
deviceType.def("get_generic_product_name", &DeviceType::getGenericProductName);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
void init_event(pybind11::module_&);
|
||||
void init_eventcallback(pybind11::module_&);
|
||||
void init_eventmanager(pybind11::module_&);
|
||||
void init_network(pybind11::module_&);
|
||||
void init_devicetype(pybind11::module_&);
|
||||
void init_message(pybind11::module_&);
|
||||
void init_canmessage(pybind11::module_&);
|
||||
void init_ethernetmessage(pybind11::module_&);
|
||||
void init_tc10statusmessage(pybind11::module_&);
|
||||
void init_device(pybind11::module_&);
|
||||
void init_messagefilter(pybind11::module_&);
|
||||
void init_messagecallback(pybind11::module_&);
|
||||
void init_version(pybind11::module_&);
|
||||
|
||||
PYBIND11_MODULE(icsneopy, m) {
|
||||
m.doc() = "libicsneo Python module";
|
||||
|
||||
init_event(m);
|
||||
init_eventcallback(m);
|
||||
init_eventmanager(m);
|
||||
init_version(m);
|
||||
init_devicetype(m);
|
||||
init_network(m);
|
||||
init_message(m);
|
||||
init_canmessage(m);
|
||||
init_ethernetmessage(m);
|
||||
init_tc10statusmessage(m);
|
||||
init_messagefilter(m);
|
||||
init_messagecallback(m);
|
||||
init_device(m);
|
||||
|
||||
m.def("find_all_devices", &FindAllDevices);
|
||||
m.def("get_supported_devices", &GetSupportedDevices);
|
||||
m.def("get_last_error", &GetLastError);
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
VERSION="1.10.5"
|
||||
ROOT="$PWD/libpcap"
|
||||
SOURCE="$ROOT/source"
|
||||
BUILD="$ROOT/build"
|
||||
INSTALL="$ROOT/install"
|
||||
|
||||
mkdir -p "$ROOT"
|
||||
cd "$ROOT" || exit 1
|
||||
|
||||
curl -LO "https://www.tcpdump.org/release/libpcap-$VERSION.tar.xz" || exit 1
|
||||
tar -xf "libpcap-$VERSION.tar.xz" || exit 1
|
||||
mv "libpcap-$VERSION" "$SOURCE" || exit 1
|
||||
|
||||
cmake -D CMAKE_POSITION_INDEPENDENT_CODE=ON -D CMAKE_INSTALL_PREFIX="$INSTALL" -D BUILD_SHARED_LIBS=OFF -D BUILD_WITH_LIBNL=OFF -D DISABLE_DBUS=ON -D DISABLE_LINUX_USBMON=ON -D DISABLE_BLUETOOTH=ON -D DISABLE_NETMAP=ON -D DISABLE_DPDK=ON -D DISABLE_RDMA=ON -D DISABLE_DAG=ON -D DISABLE_SEPTEL=ON -D DISABLE_SNF=ON -D DISABLE_TC=ON -B "$BUILD" -S "$SOURCE" || exit 1
|
||||
cmake --build "$BUILD" || exit 1
|
||||
cmake --install "$BUILD" || exit 1
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
VERSION="1.0.27"
|
||||
ROOT="$PWD/libusb"
|
||||
SOURCE="$ROOT/source"
|
||||
BUILD="$ROOT/build"
|
||||
INSTALL="$ROOT/install"
|
||||
|
||||
mkdir -p "$ROOT"
|
||||
cd "$ROOT" || exit 1
|
||||
|
||||
curl -LO "https://github.com/libusb/libusb/releases/download/v$VERSION/libusb-$VERSION.tar.bz2" || exit 1
|
||||
tar -xf "libusb-$VERSION.tar.bz2" || exit 1
|
||||
mv "libusb-$VERSION" "$SOURCE" || exit 1
|
||||
|
||||
mkdir "$BUILD" || exit 1
|
||||
cd "$BUILD" || exit 1
|
||||
"$SOURCE/configure" --prefix="$INSTALL" --disable-shared --disable-udev --disable-eventfd --disable-timerfd --with-pic || exit 1
|
||||
make || exit 1
|
||||
make install || exit 1
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
python3 -m venv env || exit 1
|
||||
. env/bin/activate || exit 1
|
||||
python3 -m pip install cibuildwheel || exit 1
|
||||
python3 -m cibuildwheel --output-dir wheelhouse || exit 1
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
@setlocal
|
||||
@echo off
|
||||
|
||||
call "%VCVARS64_2022%"
|
||||
|
||||
python.exe -m venv env || exit /b 1
|
||||
call env\Scripts\Activate.bat || exit /b 1
|
||||
python.exe -m pip install cibuildwheel || exit /b 1
|
||||
python.exe -m cibuildwheel --output-dir wheelhouse --platform windows || exit /b 1
|
||||
@@ -6,7 +6,7 @@ REM build
|
||||
cd build
|
||||
set CFLAGS=/WX
|
||||
set CXXFLAGS=/WX
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBICSNEO_BUILD_TESTS=ON ..
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_BUILD_SYSTEM_TESTS=ON -DLIBICSNEO_ENABLE_TCP=ON ..
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
cmake --build .
|
||||
if %errorlevel% neq 0 exit /b %errorlevel%
|
||||
|
||||
@@ -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,18 +13,13 @@
|
||||
#include "icsneo/communication/message/filter/main51messagefilter.h"
|
||||
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||
#include "icsneo/communication/message/versionmessage.h"
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#endif
|
||||
#include "icsneo/communication/message/componentversionsmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
int Communication::messageCallbackIDCounter = 1;
|
||||
|
||||
Communication::~Communication() {
|
||||
if(redirectingRead)
|
||||
clearRedirectRead();
|
||||
if(isOpen())
|
||||
close();
|
||||
}
|
||||
@@ -47,6 +42,11 @@ void Communication::spawnThreads() {
|
||||
|
||||
void Communication::joinThreads() {
|
||||
closing = true;
|
||||
|
||||
if(pauseReadTask) {
|
||||
resumeReads();
|
||||
}
|
||||
|
||||
if(readTaskThread.joinable())
|
||||
readTaskThread.join();
|
||||
closing = false;
|
||||
@@ -71,11 +71,6 @@ bool Communication::isDisconnected() {
|
||||
return driver->isDisconnected();
|
||||
}
|
||||
|
||||
void Communication::modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb) {
|
||||
std::scoped_lock lk(rawCallbacksMutex);
|
||||
cb(rawCallbacks);
|
||||
}
|
||||
|
||||
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
|
||||
// This is here so that other communication types (like multichannel) can override it
|
||||
return rawWrite(bytes);
|
||||
@@ -104,23 +99,6 @@ bool Communication::sendCommand(ExtendedCommand cmd, std::vector<uint8_t> argume
|
||||
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) {
|
||||
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::ReadSettings);
|
||||
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
|
||||
@@ -226,15 +204,6 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
|
||||
std::condition_variable cv;
|
||||
std::shared_ptr<Message> returnedMessage;
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
auto socket = lockSocket();
|
||||
int64_t ms = timeout.count();
|
||||
if(!(socket.writeTyped(RPC::DEVICE_LOCK) && socket.writeString(driver->device.serial) && socket.writeTyped(ms)))
|
||||
return nullptr;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
std::unique_lock<std::mutex> fnLk(syncMessageMutex); // Only allow for one sync message at a time
|
||||
std::unique_lock<std::mutex> cvLk(cvMutex); // Don't let the callback fire until we're waiting for it
|
||||
int cb = addMessageCallback(std::make_shared<MessageCallback>([&cvMutex, &returnedMessage, &cv](std::shared_ptr<Message> message) {
|
||||
@@ -257,13 +226,6 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
|
||||
|
||||
if(fail) // The caller's function failed, so don't return a message
|
||||
returnedMessage.reset();
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
if(!(socket.writeTyped(RPC::DEVICE_UNLOCK) && socket.writeString(driver->device.serial)))
|
||||
return nullptr;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return nullptr;
|
||||
#endif
|
||||
|
||||
// Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking
|
||||
return returnedMessage;
|
||||
@@ -285,49 +247,70 @@ void Communication::dispatchMessage(const std::shared_ptr<Message>& msg) {
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
}
|
||||
|
||||
void Communication::readTask() {
|
||||
std::vector<uint8_t> readBytes;
|
||||
void Communication::pauseReads() {
|
||||
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
|
||||
pauseReadTask = true;
|
||||
}
|
||||
|
||||
void Communication::resumeReads() {
|
||||
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
|
||||
if(!pauseReadTask) {
|
||||
return;
|
||||
}
|
||||
pauseReadTask = false;
|
||||
lk.unlock();
|
||||
|
||||
pauseReadTaskCv.notify_one();
|
||||
}
|
||||
|
||||
bool Communication::readsArePaused() {
|
||||
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
|
||||
return pauseReadTask;
|
||||
}
|
||||
|
||||
void Communication::readTask() {
|
||||
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
|
||||
|
||||
while(!closing) {
|
||||
readBytes.clear();
|
||||
if(driver->readWait(readBytes)) {
|
||||
handleInput(*packetizer, readBytes);
|
||||
if(pauseReadTask) {
|
||||
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
|
||||
pauseReadTaskCv.wait(lk, [this]() { return !pauseReadTask; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
|
||||
{
|
||||
std::lock_guard lk(rawCallbacksMutex);
|
||||
for(auto& cb : rawCallbacks)
|
||||
cb(readBytes);
|
||||
}
|
||||
if(redirectingRead) {
|
||||
// redirectingRead is an atomic so it can be set without acquiring a mutex
|
||||
// However, we do not clear it without the mutex. The idea is that if another
|
||||
// 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);
|
||||
if(driver->waitForRx(readTaskWakeLimit, readTaskWakeTimeout)) {
|
||||
if(pauseReadTask) {
|
||||
/**
|
||||
* Reads could have paused while the driver was not available
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
+136
-18
@@ -5,12 +5,21 @@
|
||||
#include "icsneo/communication/message/readsettingsmessage.h"
|
||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include "icsneo/communication/message/flashmemorymessage.h"
|
||||
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||
#include "icsneo/communication/message/wiviresponsemessage.h"
|
||||
#include "icsneo/communication/message/scriptstatusmessage.h"
|
||||
#include "icsneo/communication/message/a2bmessage.h"
|
||||
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
#include "icsneo/communication/message/linmessage.h"
|
||||
#include "icsneo/communication/message/mdiomessage.h"
|
||||
#include "icsneo/communication/message/extendeddatamessage.h"
|
||||
#include "icsneo/communication/message/livedatamessage.h"
|
||||
#include "icsneo/communication/message/diskdatamessage.h"
|
||||
#include "icsneo/communication/message/hardwareinfo.h"
|
||||
#include "icsneo/communication/message/tc10statusmessage.h"
|
||||
#include "icsneo/communication/message/apperrormessage.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
@@ -24,6 +33,14 @@
|
||||
#include "icsneo/communication/packet/wivicommandpacket.h"
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
#include "icsneo/communication/packet/scriptstatuspacket.h"
|
||||
#include "icsneo/communication/packet/linpacket.h"
|
||||
#include "icsneo/communication/packet/componentversionpacket.h"
|
||||
#include "icsneo/communication/packet/supportedfeaturespacket.h"
|
||||
#include "icsneo/communication/packet/mdiopacket.h"
|
||||
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
|
||||
#include "icsneo/communication/packet/livedatapacket.h"
|
||||
#include "icsneo/communication/packet/hardwareinfopacket.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
@@ -138,6 +155,43 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
|
||||
return true;
|
||||
}
|
||||
case Network::Type::A2B: {
|
||||
result = HardwareA2BPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||
}
|
||||
|
||||
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
|
||||
msg.network = packet->network;
|
||||
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: {
|
||||
switch(packet->network.getNetID()) {
|
||||
case Network::NetID::Reset_Status: {
|
||||
@@ -204,6 +258,18 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
result = std::make_shared<RawMessage>(packet->network, packet->data);
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::RED_INT_MEMORYREAD: {
|
||||
if(packet->data.size() != 512 + sizeof(uint16_t)) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // Should get enough data for a start address and sector
|
||||
}
|
||||
|
||||
const auto msg = std::make_shared<FlashMemoryMessage>();
|
||||
result = msg;
|
||||
msg->startAddress = *reinterpret_cast<uint16_t*>(packet->data.data());
|
||||
msg->data.insert(msg->data.end(), packet->data.begin() + 2, packet->data.end());
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::NeoMemorySDRead: {
|
||||
if(packet->data.size() != 512 + sizeof(uint32_t)) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
@@ -217,16 +283,58 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::ExtendedCommand: {
|
||||
|
||||
if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse))
|
||||
break; // Handle as a raw message, might not be a generic response
|
||||
|
||||
const auto& resp = *reinterpret_cast<ExtendedResponseMessage::PackedGenericResponse*>(packet->data.data());
|
||||
if(resp.header.command != ExtendedCommand::GenericReturn)
|
||||
break; // Handle as a raw message
|
||||
switch(resp.header.command) {
|
||||
case ExtendedCommand::GetComponentVersions:
|
||||
result = ComponentVersionPacket::DecodeToMessage(packet->data);
|
||||
return true;
|
||||
case ExtendedCommand::GetSupportedFeatures:
|
||||
result = SupportedFeaturesPacket::DecodeToMessage(packet->data);
|
||||
return true;
|
||||
case ExtendedCommand::GenericBinaryInfo:
|
||||
result = GenericBinaryStatusPacket::DecodeToMessage(packet->data);
|
||||
return true;
|
||||
case ExtendedCommand::GenericReturn:
|
||||
result = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
|
||||
return true;
|
||||
case ExtendedCommand::LiveData:
|
||||
result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report);
|
||||
return true;
|
||||
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());
|
||||
|
||||
const auto msg = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
|
||||
result = msg;
|
||||
return true;
|
||||
switch(header.subCommand) {
|
||||
case ExtendedDataSubCommand::GenericBinaryRead: {
|
||||
result = std::make_shared<ExtendedDataMessage>(header);
|
||||
auto extDataMsg = std::static_pointer_cast<ExtendedDataMessage>(result);
|
||||
|
||||
size_t numRead = std::min(ExtendedDataMessage::MaxExtendedDataBufferSize, (size_t)header.length);
|
||||
extDataMsg->data.resize(numRead);
|
||||
|
||||
std::copy(packet->data.begin() + sizeof(header), packet->data.begin() + sizeof(header) + numRead, extDataMsg->data.begin());
|
||||
|
||||
extDataMsg->network = Network(static_cast<uint16_t>(Network::NetID::ExtendedData), false);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Network::NetID::FlexRayControl: {
|
||||
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
|
||||
@@ -271,6 +379,16 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
|
||||
return true;
|
||||
}
|
||||
case Command::GetHardwareInfo: {
|
||||
result = HardwareInfoPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
auto msg = std::make_shared<Main51Message>();
|
||||
msg->command = Command(packet->data[0]);
|
||||
@@ -294,6 +412,14 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
packet->data.resize(length);
|
||||
return decode(result, packet);
|
||||
}
|
||||
case Network::NetID::RED_App_Error: {
|
||||
result = AppErrorMessage::DecodeToMessage(packet->data, report);
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::ReadSettings: {
|
||||
auto msg = std::make_shared<ReadSettingsMessage>();
|
||||
msg->response = ReadSettingsMessage::Response(packet->data[0]);
|
||||
@@ -345,26 +471,18 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case Network::NetID::DiskData: {
|
||||
result = std::make_shared<DiskDataMessage>(std::move(packet->data));
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Network::Type::A2B: {
|
||||
result = HardwareA2BPacket::DecodeToMessage(packet->data);
|
||||
|
||||
if(!result) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return false; // A nullptr was returned, the packet was not long enough to decode
|
||||
}
|
||||
|
||||
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
|
||||
msg.network = packet->network;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// For the moment other types of messages will automatically be decoded as raw messages
|
||||
result = std::make_shared<RawMessage>(packet->network, packet->data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+32
-17
@@ -8,23 +8,33 @@
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
bool Driver::read(std::vector<uint8_t>& bytes, size_t limit) {
|
||||
// A limit of zero indicates no limit
|
||||
if(limit == 0)
|
||||
limit = (size_t)-1;
|
||||
bool Driver::pushRx(const uint8_t* buf, size_t numReceived) {
|
||||
bool ret = readBuffer.write(buf, numReceived);
|
||||
|
||||
if(limit > (readQueue.size_approx() + 4))
|
||||
limit = (readQueue.size_approx() + 4);
|
||||
rxWaitCv.notify_all();
|
||||
|
||||
if(bytes.capacity() < limit)
|
||||
bytes.resize(limit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t actuallyRead = readQueue.try_dequeue_bulk(bytes.data(), limit);
|
||||
void Driver::clearBuffers()
|
||||
{
|
||||
WriteOperation flushop;
|
||||
|
||||
if(bytes.size() > actuallyRead)
|
||||
bytes.resize(actuallyRead);
|
||||
readBuffer.clear();
|
||||
rxWaitCv.notify_all();
|
||||
|
||||
return true;
|
||||
while (writeQueue.try_dequeue(flushop)) {}
|
||||
}
|
||||
|
||||
bool Driver::waitForRx(size_t limit, std::chrono::milliseconds timeout) {
|
||||
return waitForRx([limit, this]() {
|
||||
return readBuffer.size() >= limit;
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
bool Driver::waitForRx(std::function<bool()> predicate, std::chrono::milliseconds timeout) {
|
||||
std::unique_lock<std::mutex> lk(rxWaitMutex);
|
||||
return rxWaitCv.wait_for(lk, timeout, predicate);
|
||||
}
|
||||
|
||||
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
|
||||
@@ -32,13 +42,18 @@ bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds tim
|
||||
if(limit == 0)
|
||||
limit = (size_t)-1;
|
||||
|
||||
if(limit > (readQueue.size_approx() + 4))
|
||||
limit = (readQueue.size_approx() + 4);
|
||||
if(limit > (readBuffer.size() + 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);
|
||||
|
||||
#ifdef ICSNEO_DRIVER_DEBUG_PRINTS
|
||||
@@ -79,4 +94,4 @@ bool Driver::write(const std::vector<uint8_t>& bytes) {
|
||||
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "icsneo/communication/encoder.h"
|
||||
#include "icsneo/communication/message/ethernetmessage.h"
|
||||
#include "icsneo/communication/message/livedatamessage.h"
|
||||
#include "icsneo/communication/message/main51message.h"
|
||||
#include "icsneo/communication/packet/livedatapacket.h"
|
||||
#include "icsneo/communication/packet/ethernetpacket.h"
|
||||
#include "icsneo/communication/packet/iso9141packet.h"
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
@@ -9,7 +11,8 @@
|
||||
#include "icsneo/communication/packet/i2cpacket.h"
|
||||
#include "icsneo/communication/message/i2cmessage.h"
|
||||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
|
||||
#include "icsneo/communication/packet/linpacket.h"
|
||||
#include "icsneo/communication/packet/mdiopacket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -96,6 +99,30 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::I2C
|
||||
case Network::Type::LIN: {
|
||||
auto linmsg = std::dynamic_pointer_cast<LINMessage>(message);
|
||||
if(!linmsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareLINPacket::EncodeFromMessage(*linmsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::LIN
|
||||
case Network::Type::MDIO: {
|
||||
auto mdiomsg = std::dynamic_pointer_cast<MDIOMessage>(message);
|
||||
if(!mdiomsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
buffer = &result;
|
||||
if(!HardwareMDIOPacket::EncodeFromMessage(*mdiomsg, result, report)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
} // End of Network::Type::MDIO
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
@@ -172,6 +199,17 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case Message::Type::LiveData: {
|
||||
auto liveDataMsg = std::dynamic_pointer_cast<LiveDataMessage>(message);
|
||||
if(!liveDataMsg) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false; // The message was not a properly formed LiveDataMessage
|
||||
}
|
||||
if(!HardwareLiveDataPacket::EncodeFromMessage(*liveDataMsg, result, report))
|
||||
return false;
|
||||
result = packetizer.packetWrap(result, false);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -182,7 +220,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
|
||||
// Size for the host-to-device long format is the size of the entire packet + 1
|
||||
// So +1 for AA header, +1 for short format header, +2 for long format size, and +2 for long format NetID
|
||||
// Then an extra +1, due to a firmware idiosyncrasy
|
||||
uint16_t size = uint16_t(buffer->size()) + 1 + 1 + 2 + 2 + 1;
|
||||
uint16_t size = static_cast<uint16_t>(buffer->size()) + 1 + 1 + 2 + 2 + 1;
|
||||
|
||||
buffer->insert(buffer->begin(), {
|
||||
(uint8_t)Network::NetID::RED, // 0x0C for long message
|
||||
(uint8_t)size, // Size, little endian 16-bit
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
#include <cstring>
|
||||
#include "icsneo/communication/interprocessmailbox.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
bool InterprocessMailbox::open(const std::string& name, bool create)
|
||||
{
|
||||
if(!queuedSem.open(name + "-qs", create))
|
||||
return false;
|
||||
|
||||
if(!emptySem.open(name + "-es", create, MESSAGE_COUNT))
|
||||
return false;
|
||||
|
||||
if(!sharedMem.open(name + "-sm", BLOCK_SIZE * MESSAGE_COUNT, create))
|
||||
return false;
|
||||
|
||||
valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
InterprocessMailbox::operator bool() const
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::close()
|
||||
{
|
||||
valid = false;
|
||||
return queuedSem.close() && emptySem.close() && sharedMem.close();
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout)
|
||||
{
|
||||
if(!queuedSem.wait(timeout))
|
||||
return false;
|
||||
auto it = sharedMem.data() + (index * BLOCK_SIZE);
|
||||
messageLength = *(LengthFieldType*)it;
|
||||
it += LENGTH_FIELD_SIZE;
|
||||
std::memcpy(data, it, std::min(messageLength, MAX_DATA_SIZE));
|
||||
if(!emptySem.post())
|
||||
return false;
|
||||
++index;
|
||||
index %= MESSAGE_COUNT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InterprocessMailbox::write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout)
|
||||
{
|
||||
if(!emptySem.wait(timeout))
|
||||
return false; // the buffer is full and we timed out
|
||||
auto it = sharedMem.data() + (index * BLOCK_SIZE);
|
||||
*(LengthFieldType*)it = messageLength;
|
||||
it += LENGTH_FIELD_SIZE;
|
||||
std::memcpy(it, data, messageLength);
|
||||
if(!queuedSem.post())
|
||||
return false;
|
||||
++index;
|
||||
index %= MESSAGE_COUNT;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "icsneo/communication/livedata.h"
|
||||
namespace icsneo {
|
||||
|
||||
namespace LiveDataUtil {
|
||||
|
||||
LiveDataHandle getNewHandle() {
|
||||
static LiveDataHandle currentHandle = 0;
|
||||
++currentHandle;
|
||||
if(currentHandle == std::numeric_limits<LiveDataHandle>::max()) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::LiveDataInvalidHandle, APIEvent::Severity::Error);
|
||||
currentHandle = 1;
|
||||
}
|
||||
return currentHandle;
|
||||
}
|
||||
|
||||
double liveDataValueToDouble(const LiveDataValue& val) {
|
||||
constexpr double liveDataFixedPointToDouble = 0.00000000023283064365386962890625;
|
||||
return val.value * liveDataFixedPointToDouble;
|
||||
}
|
||||
|
||||
} // namespace LiveDataUtil
|
||||
} // namespace icsneo
|
||||
@@ -0,0 +1,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,148 @@
|
||||
#include <icsneo/communication/message/apperrormessage.h>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
#pragma pack(push, 2)
|
||||
|
||||
typedef struct {
|
||||
uint16_t error_type;
|
||||
uint16_t network_id;
|
||||
uint32_t uiTimeStamp10uS;
|
||||
uint32_t uiTimeStamp10uSMSB;
|
||||
} AppErrorData;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
std::shared_ptr<Message> AppErrorMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
|
||||
const AppErrorData* data = reinterpret_cast<const AppErrorData*>(bytestream.data());
|
||||
if(!data) {
|
||||
report(APIEvent::Type::AppErrorParsingFailed, APIEvent::Severity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
auto appErr = std::make_shared<AppErrorMessage>();
|
||||
appErr->errorType = data->error_type;
|
||||
appErr->errorNetID = static_cast<Network::NetID>(data->network_id);
|
||||
appErr->timestamp10us = data->uiTimeStamp10uS;
|
||||
appErr->timestamp10usMSB = data->uiTimeStamp10uSMSB;
|
||||
appErr->network = Network::NetID::RED_App_Error;
|
||||
return appErr;
|
||||
}
|
||||
|
||||
AppErrorType AppErrorMessage::getAppErrorType() {
|
||||
AppErrorType errType = static_cast<AppErrorType>(errorType);
|
||||
if(errType > AppErrorType::AppNoError) {
|
||||
return AppErrorType::AppNoError;
|
||||
}
|
||||
return errType;
|
||||
}
|
||||
|
||||
std::string AppErrorMessage::getAppErrorString() {
|
||||
auto netIDString = Network::GetNetIDString(errorNetID);
|
||||
AppErrorType errType = static_cast<AppErrorType>(errorType);
|
||||
switch (errType) {
|
||||
case AppErrorType::AppErrorRxMessagesFull:
|
||||
return std::string(netIDString) + ": RX message buffer full";
|
||||
case AppErrorType::AppErrorTxMessagesFull:
|
||||
return std::string(netIDString) + ": TX message buffer full";
|
||||
case AppErrorType::AppErrorTxReportMessagesFull:
|
||||
return std::string(netIDString) + ": TX report buffer full";
|
||||
case AppErrorType::AppErrorBadCommWithDspIC:
|
||||
return "Received bad packet from DSP IC";
|
||||
case AppErrorType::AppErrorDriverOverflow:
|
||||
return std::string(netIDString) + ": Driver overflow";
|
||||
case AppErrorType::AppErrorPCBuffOverflow:
|
||||
return "PC buffer overflow";
|
||||
case AppErrorType::AppErrorPCChksumError:
|
||||
return "PC checksum error";
|
||||
case AppErrorType::AppErrorPCMissedByte:
|
||||
return "PC missed byte";
|
||||
case AppErrorType::AppErrorPCOverrunError:
|
||||
return "PC overrun error";
|
||||
case AppErrorType::AppErrorSettingFailure:
|
||||
return std::string(netIDString) + ": Settings incorrectly set";
|
||||
case AppErrorType::AppErrorTooManySelectedNetworks:
|
||||
return "Too many selected networks";
|
||||
case AppErrorType::AppErrorNetworkNotEnabled:
|
||||
return std::string(netIDString) + ": Network not enabled";
|
||||
case AppErrorType::AppErrorRtcNotCorrect:
|
||||
return "RTC not correct";
|
||||
case AppErrorType::AppErrorLoadedDefaultSettings:
|
||||
return "Loaded default settings";
|
||||
case AppErrorType::AppErrorFeatureNotUnlocked:
|
||||
return "Feature not unlocked";
|
||||
case AppErrorType::AppErrorFeatureRtcCmdDropped:
|
||||
return "RTC command dropped";
|
||||
case AppErrorType::AppErrorTxMessagesFlushed:
|
||||
return "TX message buffer flushed";
|
||||
case AppErrorType::AppErrorTxMessagesHalfFull:
|
||||
return "TX message buffer half full";
|
||||
case AppErrorType::AppErrorNetworkNotValid:
|
||||
return "Network is not valid";
|
||||
case AppErrorType::AppErrorTxInterfaceNotImplemented:
|
||||
return "TX interface is not implemented";
|
||||
case AppErrorType::AppErrorTxMessagesCommEnableIsOff:
|
||||
return "TX message communication is disabled";
|
||||
case AppErrorType::AppErrorRxFilterMatchCountExceeded:
|
||||
return "RX filter match count exceeded";
|
||||
case AppErrorType::AppErrorEthPreemptionNotEnabled:
|
||||
return std::string(netIDString) + ": Ethernet preemption not enabled";
|
||||
case AppErrorType::AppErrorTxNotSupportedInMode:
|
||||
return std::string(netIDString) + ": Transmit is not supported in this mode";
|
||||
case AppErrorType::AppErrorJumboFramesNotSupported:
|
||||
return std::string(netIDString) + ": Jumbo frames not supported";
|
||||
case AppErrorType::AppErrorEthernetIpFragment:
|
||||
return "Ethernet IP fragment received";
|
||||
case AppErrorType::AppErrorTxMessagesUnderrun:
|
||||
return std::string(netIDString) + ": Transmit buffer underrun";
|
||||
case AppErrorType::AppErrorDeviceFanFailure:
|
||||
return "Device fan failure";
|
||||
case AppErrorType::AppErrorDeviceOvertemperature:
|
||||
return "Device overtemperature";
|
||||
case AppErrorType::AppErrorTxMessageIndexOutOfRange:
|
||||
return "Transmit message index out of range";
|
||||
case AppErrorType::AppErrorUndersizedFrameDropped:
|
||||
return std::string(netIDString) + ": Undersized frame dropped";
|
||||
case AppErrorType::AppErrorOversizedFrameDropped:
|
||||
return std::string(netIDString) + ": Oversized frame dropped";
|
||||
case AppErrorType::AppErrorWatchdogEvent:
|
||||
return "Watchdog event occured";
|
||||
case AppErrorType::AppErrorSystemClockFailure:
|
||||
return "Device clock failed";
|
||||
case AppErrorType::AppErrorSystemClockRecovered:
|
||||
return "Device clock recovered";
|
||||
case AppErrorType::AppErrorSystemPeripheralReset:
|
||||
return "Device peripheral reset";
|
||||
case AppErrorType::AppErrorSystemCommunicationFailure:
|
||||
return "Device communication failure";
|
||||
case AppErrorType::AppErrorTxMessagesUnsupportedSourceOrPacketId:
|
||||
return std::string(netIDString) + ": Transmit unsupported source or packet ID";
|
||||
case AppErrorType::AppErrorWbmsManagerConnectFailed:
|
||||
return std::string(netIDString) + ": Failed to connect to managers with settings";
|
||||
case AppErrorType::AppErrorWbmsManagerConnectBadState:
|
||||
return std::string(netIDString) + ": Connected to managers in a invalid state";
|
||||
case AppErrorType::AppErrorWbmsManagerConnectTimeout:
|
||||
return std::string(netIDString) + ": Timeout while attempting to connect to managers";
|
||||
case AppErrorType::AppErrorFailedToInitializeLoggerDisk:
|
||||
return "Device failed to initialize storage disk";
|
||||
case AppErrorType::AppErrorInvalidSetting:
|
||||
return std::string(netIDString) + ": Invalid settings";
|
||||
case AppErrorType::AppErrorSystemFailureRequestedReset:
|
||||
return "Device rebooted to recover from an unexpected error condition";
|
||||
case AppErrorType::AppErrorPortKeyMistmatch:
|
||||
return std::string(netIDString) + ": Mismatch between key in manager and stored key";
|
||||
case AppErrorType::AppErrorBusFailure:
|
||||
return std::string(netIDString) + ": Bus failure";
|
||||
case AppErrorType::AppErrorTapOverflow:
|
||||
return std::string(netIDString) + ": Tap overflow";
|
||||
case AppErrorType::AppErrorEthTxNoLink:
|
||||
return std::string(netIDString) + ": Attempted Ethernet transmit without link";
|
||||
case AppErrorType::AppErrorErrorBufferOverflow:
|
||||
return "Device error buffer overflow";
|
||||
case AppErrorType::AppNoError:
|
||||
return "No error";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace icsneo
|
||||
@@ -1,100 +1,234 @@
|
||||
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
|
||||
#include "icsneo/device/tree/rada2b/rada2b.h"
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
namespace icsneo {
|
||||
|
||||
void A2BWAVOutput::writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const {
|
||||
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;
|
||||
}
|
||||
|
||||
WaveFileHeader header = WaveFileHeader(2 * firstMsg->getNumChannels(), wavSampleRate, firstMsg->getBitDepth());
|
||||
header.write(stream);
|
||||
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(closed)
|
||||
{
|
||||
if(!initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(message->type != Message::Type::Frame)
|
||||
return false;
|
||||
|
||||
const auto& frame = std::static_pointer_cast<Frame>(message);
|
||||
|
||||
if(frame->network.getType() != Network::Type::A2B)
|
||||
return false;
|
||||
|
||||
const auto& a2bmsg = std::static_pointer_cast<A2BMessage>(frame);
|
||||
|
||||
if(firstMessageFlag) {
|
||||
writeHeader(a2bmsg);
|
||||
firstMessageFlag = false;
|
||||
}
|
||||
|
||||
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::DownStream)) {
|
||||
close();
|
||||
|
||||
if(closed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::UpStream)) {
|
||||
close();
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
write((void*)&subChunk2Size, 4);
|
||||
stream->write(reinterpret_cast<const char*>(&subChunk2Size), 4);
|
||||
stream->seekp(4, std::ios::beg);
|
||||
write((void*)&chunkSize, 4);
|
||||
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
|
||||
|
||||
closed = true;
|
||||
}
|
||||
|
||||
bool A2BWAVOutput::writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessage::A2BDirection dir) const
|
||||
{
|
||||
uint8_t numChannels = msg->getNumChannels();
|
||||
bool A2BWAVOutput::writeCurrentBuffer() const {
|
||||
|
||||
uint8_t channel = 0;
|
||||
uint32_t frame = 0;
|
||||
uint8_t bitDepth = msg->getBitDepth();
|
||||
|
||||
while(true) {
|
||||
auto sample = msg->getSample(dir, channel, frame);
|
||||
|
||||
if(!sample) {
|
||||
if(channel == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t audioSample = sample.value() >> (32 - bitDepth);
|
||||
|
||||
write((void*)(&audioSample), A2BPCM_SAMPLE_SIZE);
|
||||
|
||||
channel = (channel + 1) % numChannels;
|
||||
if(channel == 0) {
|
||||
frame++;
|
||||
}
|
||||
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/ethernetmessage.h"
|
||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||
#include "icsneo/communication/message/linmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
@@ -17,6 +18,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
|
||||
neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg;
|
||||
auto framemsg = std::static_pointer_cast<Frame>(message);
|
||||
const auto netType = framemsg->network.getType();
|
||||
|
||||
frame.netid = (neonetid_t)framemsg->network.getNetID();
|
||||
frame.type = (neonettype_t)netType;
|
||||
frame.description = framemsg->description;
|
||||
@@ -53,6 +55,47 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
|
||||
//eth.status.xyz = ethmsg->noPadding;
|
||||
break;
|
||||
}
|
||||
case Network::Type::LIN: {
|
||||
neomessage_lin_t& lin = *(neomessage_lin_t*)&neomsg;
|
||||
auto linmsg = std::static_pointer_cast<LINMessage>(message);
|
||||
if(!linmsg) { break; }
|
||||
const auto linHdrBytes = std::min(linmsg->data.size(), static_cast<size_t>(2));
|
||||
lin.header[0] = linmsg->protectedID;
|
||||
std::copy(linmsg->data.begin(), linmsg->data.begin() + linHdrBytes, lin.header + 1);
|
||||
linmsg->calcChecksum(*linmsg);
|
||||
lin.checksum = linmsg->checksum;
|
||||
lin.data = linmsg->data.data() + linHdrBytes;
|
||||
if(linmsg->isEnhancedChecksum != linmsg->statusFlags.TxChecksumEnhanced) {
|
||||
linmsg->isEnhancedChecksum = true;
|
||||
linmsg->statusFlags.TxChecksumEnhanced = true;
|
||||
}
|
||||
if(linmsg->data.size())
|
||||
lin.length = linmsg->data.size() + 2;
|
||||
else
|
||||
lin.length = 1;
|
||||
|
||||
lin.linStatus = {
|
||||
linmsg->statusFlags.TxChecksumEnhanced,
|
||||
linmsg->statusFlags.TxCommander,
|
||||
linmsg->statusFlags.TxResponder,
|
||||
linmsg->statusFlags.UpdateResponderOnce,
|
||||
linmsg->statusFlags.HasUpdatedResponderOnce,
|
||||
linmsg->statusFlags.BusRecovered,
|
||||
linmsg->statusFlags.BreakOnly
|
||||
};
|
||||
|
||||
lin.status.linJustBreakSync = linmsg->errFlags.ErrRxBreakSyncOnly;
|
||||
lin.status.linErrorTXRXMismatch = linmsg->errFlags.ErrTxRxMismatch;
|
||||
lin.status.linErrorRXBreakNotZero = linmsg->errFlags.ErrRxBreakNotZero;
|
||||
lin.status.linErrorRXBreakTooShort = linmsg->errFlags.ErrRxBreakTooShort;
|
||||
lin.status.linErrorRXSyncNot55 = linmsg->errFlags.ErrRxSyncNot55;
|
||||
lin.status.linErrorRXDataGreaterEight = linmsg->errFlags.ErrRxDataLenOver8;
|
||||
lin.status.linSyncFrameError = linmsg->errFlags.ErrFrameSync;
|
||||
lin.status.linIDFrameError = linmsg->errFlags.ErrFrameMessageID;
|
||||
lin.status.linSlaveByteError = linmsg->errFlags.ErrFrameResponderData;
|
||||
lin.status.checksumError = linmsg->errFlags.ErrChecksumMatch;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO Implement others
|
||||
break;
|
||||
@@ -105,6 +148,53 @@ std::shared_ptr<Message> icsneo::CreateMessageFromNeoMessage(const neomessage_t*
|
||||
ethmsg->data.insert(ethmsg->data.end(), eth.data, eth.data + eth.length);
|
||||
return ethmsg;
|
||||
}
|
||||
case Network::Type::LIN: {
|
||||
neomessage_lin_t& lin = *(neomessage_lin_t*)neomessage;
|
||||
auto linmsg = std::make_shared<LINMessage>();
|
||||
linmsg->network = network;
|
||||
linmsg->description = lin.description;
|
||||
linmsg->protectedID = lin.header[0];
|
||||
linmsg->ID = linmsg->protectedID & 0x3F;
|
||||
linmsg->statusFlags = {
|
||||
static_cast<bool>(lin.linStatus.txChecksumEnhanced),
|
||||
static_cast<bool>(lin.linStatus.txCommander),
|
||||
static_cast<bool>(lin.linStatus.txResponder),
|
||||
static_cast<bool>(lin.linStatus.updateResponderOnce),
|
||||
static_cast<bool>(lin.linStatus.hasUpdatedResponderOnce),
|
||||
static_cast<bool>(lin.linStatus.busRecovered),
|
||||
static_cast<bool>(lin.linStatus.breakOnly)
|
||||
};
|
||||
linmsg->isEnhancedChecksum = linmsg->statusFlags.TxChecksumEnhanced;
|
||||
linmsg->errFlags = {
|
||||
static_cast<bool>(lin.linStatus.breakOnly),
|
||||
static_cast<bool>(lin.status.linJustBreakSync),
|
||||
static_cast<bool>(lin.status.linErrorTXRXMismatch),
|
||||
static_cast<bool>(lin.status.linErrorRXBreakNotZero),
|
||||
static_cast<bool>(lin.status.linErrorRXBreakTooShort),
|
||||
static_cast<bool>(lin.status.linErrorRXSyncNot55),
|
||||
static_cast<bool>(lin.status.linErrorRXDataGreaterEight),
|
||||
static_cast<bool>(lin.status.linSyncFrameError),
|
||||
static_cast<bool>(lin.status.linIDFrameError),
|
||||
static_cast<bool>(lin.status.linSlaveByteError),
|
||||
static_cast<bool>(lin.status.checksumError)
|
||||
};
|
||||
if(lin.length > 1) {
|
||||
auto numHeaderBytes = std::min(lin.length, static_cast<size_t>(3));
|
||||
linmsg->data.insert(linmsg->data.end(), (lin.header + 1), (lin.header + numHeaderBytes));
|
||||
linmsg->data.insert(linmsg->data.end(), lin.data, (lin.data + (lin.length - numHeaderBytes)));
|
||||
linmsg->checksum = linmsg->data.back();
|
||||
linmsg->data.pop_back();
|
||||
}
|
||||
if (linmsg->statusFlags.TxCommander) {
|
||||
if (linmsg->data.size())
|
||||
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
|
||||
else
|
||||
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
|
||||
} else if (linmsg->statusFlags.TxResponder) {
|
||||
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
|
||||
}
|
||||
return linmsg;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,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);
|
||||
}
|
||||
@@ -177,8 +177,11 @@ void MultiChannelCommunication::vnetReadTask(size_t vnetIndex) {
|
||||
if(queue.wait_dequeue_timed(payloadBytes, std::chrono::milliseconds(250))) {
|
||||
if(closing)
|
||||
break;
|
||||
|
||||
handleInput(*vnetPacketizer, payloadBytes);
|
||||
|
||||
auto& ringBuffer = driver->getReadBuffer();
|
||||
ringBuffer.write(payloadBytes);
|
||||
|
||||
handleInput(*vnetPacketizer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,161 +1,58 @@
|
||||
#include "icsneo/communication/packet/a2bpacket.h"
|
||||
#include <cstring>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
const size_t HardwareA2BPacket::coreMiniMessageHeaderSize = 28;
|
||||
const size_t HardwareA2BPacket::a2bMessageMaxLength = (size_t)HardwareA2BPacket::coreMiniMessageHeaderSize + 1024;
|
||||
const size_t HardwareA2BPacket::a2bHeaderSize = 6;
|
||||
const size_t HardwareA2BPacket::a2bMessageMaxLength = sizeof(HardwareA2BPacket) + 1024;
|
||||
|
||||
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
|
||||
if(bytestream.size() < coreMiniMessageHeaderSize)
|
||||
if(bytestream.size() < sizeof(HardwareA2BPacket))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto getSampleFromBytes = [](uint8_t bytesPerSample, const uint8_t *bytes) {
|
||||
A2BPCMSample result = 0;
|
||||
|
||||
for(auto i = 0; i < bytesPerSample; i++) {
|
||||
result |= static_cast<uint32_t>(bytes[i]) << (i * 8);
|
||||
}
|
||||
const HardwareA2BPacket* data = (const HardwareA2BPacket*)bytestream.data();
|
||||
|
||||
return result;
|
||||
};
|
||||
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - sizeof(HardwareA2BPacket); // First 28 bytes are message header.
|
||||
|
||||
if(totalPackedLength == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data();
|
||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>();
|
||||
|
||||
uint32_t totalPackedLength = static_cast<uint32_t>(bytestream.size()) - static_cast<uint32_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
|
||||
|
||||
uint8_t bytesPerChannel = data->header.channelSize16 ? 2 : 4;
|
||||
uint8_t numChannels = data->header.channelNum;
|
||||
uint8_t bitDepth = data->header.channelSize16 ? A2BPCM_L16 : A2BPCM_L24;
|
||||
|
||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(bitDepth, bytesPerChannel, numChannels);
|
||||
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;
|
||||
|
||||
const uint8_t *bytes = bytestream.data();
|
||||
bytes+=coreMiniMessageHeaderSize;
|
||||
|
||||
uint8_t channel = 0;
|
||||
|
||||
for(uint32_t i = 0; i < totalPackedLength; i += 2 * static_cast<uint32_t>(bytesPerChannel), bytes += 2 * bytesPerChannel, channel = (channel + 1) % numChannels) {
|
||||
|
||||
msg->addSample(
|
||||
getSampleFromBytes(bytesPerChannel, bytes),
|
||||
A2BMessage::A2BDirection::DownStream,
|
||||
channel
|
||||
);
|
||||
|
||||
msg->addSample(
|
||||
getSampleFromBytes(bytesPerChannel, bytes + bytesPerChannel),
|
||||
A2BMessage::A2BDirection::UpStream,
|
||||
channel
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
msg->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) {
|
||||
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& /*report*/) {
|
||||
constexpr size_t a2btxMessageHeaderSize = 6;
|
||||
|
||||
if(message.getBytesPerSample() != 2 && message.getBytesPerSample() != 4) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
size_t audioBufferSize = message.data.size();
|
||||
size_t totalSize = a2btxMessageHeaderSize + audioBufferSize;
|
||||
|
||||
size_t sampleBytes = message.getNumSamples() * static_cast<size_t>(message.getBytesPerSample());
|
||||
size_t totalSize = coreMiniMessageHeaderSize + sampleBytes;
|
||||
|
||||
if(totalSize > a2bMessageMaxLength) {
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bytestream.reserve(totalSize);
|
||||
|
||||
bytestream.push_back(message.getNumChannels());
|
||||
bytestream.push_back(message.channelSize16 ? 1 : 0);
|
||||
bytestream.resize(totalSize, 0);
|
||||
uint32_t offset = 0;
|
||||
|
||||
uint8_t a2b2Bits = 0;
|
||||
|
||||
if(message.monitor) {
|
||||
a2b2Bits = a2b2Bits | 1;
|
||||
}
|
||||
bytestream[offset++] = 0;
|
||||
bytestream[offset++] = 0;
|
||||
bytestream[offset++] = (uint8_t)(audioBufferSize & 0xFF);
|
||||
bytestream[offset++] = (uint8_t)((audioBufferSize >> 8) & 0xFF);
|
||||
bytestream[offset++] = (uint8_t)((message.description >> 8) & 0xFF);
|
||||
bytestream[offset++] = (uint8_t)(message.description & 0xFF);
|
||||
|
||||
if(message.txmsg) {
|
||||
a2b2Bits = a2b2Bits | (1 << 1);
|
||||
}
|
||||
|
||||
if(message.errIndicator) {
|
||||
a2b2Bits = a2b2Bits | (1 << 2);
|
||||
}
|
||||
|
||||
if(message.syncFrame) {
|
||||
a2b2Bits = a2b2Bits | (1 << 3);
|
||||
}
|
||||
|
||||
bytestream.push_back(a2b2Bits);
|
||||
bytestream.push_back(0);
|
||||
bytestream.push_back(static_cast<uint8_t>(message.rfu2));
|
||||
bytestream.push_back(static_cast<uint8_t>(message.rfu2 >> 8));
|
||||
|
||||
for(size_t i = 0; i < (coreMiniMessageHeaderSize - a2bHeaderSize); i++)
|
||||
bytestream.push_back(0);
|
||||
|
||||
uint8_t numChannels = message.getNumChannels();
|
||||
|
||||
uint8_t channel = 0;
|
||||
uint32_t frame = 0;
|
||||
|
||||
auto writeSample = [&](A2BPCMSample&& sample) {
|
||||
for(uint32_t i = 0; i < static_cast<uint32_t>(message.getBytesPerSample()); i++) {
|
||||
bytestream.push_back(static_cast<uint8_t>((sample >> (i*8))));
|
||||
}
|
||||
};
|
||||
|
||||
while(true) {
|
||||
auto dsSample = message.getSample(A2BMessage::A2BDirection::DownStream, channel, frame);
|
||||
auto usSample = message.getSample(A2BMessage::A2BDirection::UpStream, channel, frame);
|
||||
|
||||
|
||||
// Check if getSample failed for both downstream and upstream
|
||||
if(!dsSample && !usSample) {
|
||||
|
||||
if(channel != 0) {
|
||||
//Incomplete frame, the frame we are currently on does not contain all channel samples
|
||||
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
// Since no samples have been written for the current frame yet and there are no more
|
||||
// samples in both upstream and downstream, we can break and end parsing.
|
||||
break;
|
||||
}
|
||||
// Since the first case failed, at least one of the streams still has samples.
|
||||
// This case checks to see if the other stream does not have a sample.
|
||||
else if(!dsSample || !usSample) {
|
||||
// Report an error since we must have a one to one correspondence between upstream
|
||||
// and downstream.
|
||||
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
writeSample(std::move(dsSample.value()));
|
||||
writeSample(std::move(usSample.value()));
|
||||
|
||||
channel = (channel + 1) % numChannels;
|
||||
if(channel == 0)
|
||||
frame++;
|
||||
}
|
||||
std::copy(message.data.begin(), message.data.end(), bytestream.begin() + offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "icsneo/communication/packet/componentversionpacket.h"
|
||||
#include "icsneo/communication/message/componentversionsmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
#pragma pack(push, 2)
|
||||
struct PackedComponentVersion {
|
||||
uint8_t valid;
|
||||
uint8_t expansionSlot;
|
||||
uint8_t componentInfo; // Component specific data (e.g. Linux: boot device)
|
||||
uint8_t reserved;
|
||||
uint32_t identifier;
|
||||
uint32_t dotVersion; // Represents a.b.c.d, a.b.c, or a.b, depending on leading zeros.
|
||||
uint32_t commitHash;
|
||||
};
|
||||
|
||||
static constexpr size_t MaxReportedVersions = 16;
|
||||
struct ComponentVersionsResponse {
|
||||
ExtendedResponseMessage::ResponseHeader header;
|
||||
uint16_t numVersions;
|
||||
PackedComponentVersion versions[MaxReportedVersions];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
std::shared_ptr<ComponentVersionsMessage> ComponentVersionPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
|
||||
auto msg = std::make_shared<ComponentVersionsMessage>();
|
||||
// Length checks: At least a header and numVersions field.
|
||||
if(bytes.size() < sizeof(ExtendedResponseMessage::ResponseHeader) + 2) {
|
||||
return msg; // Empty
|
||||
}
|
||||
// Get a reference to the payload to fully validate the length
|
||||
const auto& response = *reinterpret_cast<const ComponentVersionsResponse*>(bytes.data());
|
||||
// Expected size is the header, numVersions field, and numVersions ComponentVersion objects.
|
||||
auto expectedSize = sizeof(ExtendedResponseMessage::ResponseHeader) + 2 + (response.numVersions * sizeof(ComponentVersion));
|
||||
// If the response is malformed (too small), return an empty message.
|
||||
if(bytes.size() < expectedSize) {
|
||||
return msg; // Empty
|
||||
}
|
||||
// Unpack into the portable class
|
||||
for(unsigned int i = 0; i < response.numVersions; ++i) {
|
||||
const auto& packedVersion = response.versions[i];
|
||||
msg->versions.emplace_back(packedVersion.valid, packedVersion.componentInfo, packedVersion.identifier, packedVersion.dotVersion, packedVersion.commitHash);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
|
||||
#include "icsneo/communication/message/genericbinarystatusmessage.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
#pragma pack(push, 2)
|
||||
struct GenericBinaryStatusResponse {
|
||||
ExtendedResponseMessage::ResponseHeader header;
|
||||
size_t size;
|
||||
uint16_t index;
|
||||
uint16_t status;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
std::shared_ptr<GenericBinaryStatusMessage> GenericBinaryStatusPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
|
||||
if(bytes.size() < sizeof(GenericBinaryStatusResponse)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto msg = std::make_shared<GenericBinaryStatusMessage>();
|
||||
|
||||
const auto& response = *reinterpret_cast<const GenericBinaryStatusResponse*>(bytes.data());
|
||||
|
||||
msg->binarySize = response.size;
|
||||
msg->binaryIndex = response.index;
|
||||
msg->binaryStatus = response.status;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GenericBinaryStatusPacket::EncodeArguments(uint16_t binaryIndex) {
|
||||
std::vector<uint8_t> bytestream(sizeof(GenericBinaryStatusResponse));
|
||||
|
||||
auto& parameters = *reinterpret_cast<GenericBinaryStatusResponse*>(bytestream.data());
|
||||
parameters.index = binaryIndex;
|
||||
|
||||
return bytestream;
|
||||
}
|
||||
@@ -0,0 +1,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,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,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;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace icsneo;
|
||||
@@ -25,9 +24,8 @@ std::vector<uint8_t>& Packetizer::packetWrap(std::vector<uint8_t>& data, bool sh
|
||||
return data;
|
||||
}
|
||||
|
||||
bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
bool Packetizer::input(RingBuffer& bytes) {
|
||||
bool haveEnoughData = true;
|
||||
bytes.insert(bytes.end(), inputBytes.begin(), inputBytes.end());
|
||||
|
||||
while(haveEnoughData) {
|
||||
switch(state) {
|
||||
@@ -49,44 +47,97 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
haveEnoughData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
packetLength = bytes[1] >> 4 & 0xF;
|
||||
packet.network = Network(bytes[1] & 0xF); // Lower nibble of the second byte is the network ID
|
||||
|
||||
packetLength = bytes[1] >> 4 & 0xf; // Upper nibble of the second byte denotes the packet length
|
||||
packet.network = Network(bytes[1] & 0xf); // Lower nibble of the second byte is the network ID
|
||||
if(packetLength == 0) { // A length of zero denotes a long style packet
|
||||
if(packetLength == 0) {
|
||||
state = ReadState::ParseLongStylePacketHeader;
|
||||
checksum = false;
|
||||
headerSize = 6;
|
||||
} else {
|
||||
state = ReadState::GetData;
|
||||
checksum = true; // Even if checksum is not explicitly disallowed, we enable it here, as this goes into length calculation
|
||||
headerSize = 2;
|
||||
packetLength += 2; // The packet length given in short packets does not include header
|
||||
break;
|
||||
} else if(packetLength == 0xA && packet.network == Network::NetID::DiskData) {
|
||||
state = ReadState::ParseDiskDataHeader;
|
||||
break;
|
||||
}
|
||||
|
||||
checksum = true; // Even if checksum is not explicitly disallowed, we enable it here, as this goes into length calculation
|
||||
headerSize = 2;
|
||||
packetLength += 2; // The packet length given in short packets does not include header
|
||||
currentIndex++;
|
||||
state = ReadState::GetData;
|
||||
break;
|
||||
case ReadState::ParseLongStylePacketHeader:
|
||||
if(bytes.size() < 6) {
|
||||
haveEnoughData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
packetLength = bytes[2]; // Long packets have a little endian length on bytes 3 and 4
|
||||
packetLength |= bytes[3] << 8;
|
||||
packet.network = Network((bytes[5] << 8) | bytes[4]); // Long packets have their netid stored as little endian on bytes 5 and 6
|
||||
currentIndex += 4;
|
||||
packet.network = Network(((bytes[5] << 8) | bytes[4]), false); // Long packets have their netid stored as little endian on bytes 5 and 6. Devices never send actual VNET IDs so we must not perform ID expansion here.
|
||||
|
||||
/* Long packets can't have a length less than 6, because that would indicate a negative payload size.
|
||||
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
|
||||
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
|
||||
* payload, and not the header or checksum.
|
||||
*/
|
||||
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
|
||||
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
|
||||
* payload, and not the header or checksum.
|
||||
*/
|
||||
if(packetLength < 6 || packetLength > 4000) {
|
||||
bytes.pop_front();
|
||||
EventManager::GetInstance().add(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||
state = ReadState::SearchForHeader;
|
||||
} else {
|
||||
state = ReadState::GetData;
|
||||
break;
|
||||
}
|
||||
|
||||
checksum = false;
|
||||
headerSize = 6;
|
||||
currentIndex += 5;
|
||||
state = ReadState::GetData;
|
||||
break;
|
||||
case ReadState::ParseDiskDataHeader:
|
||||
if(bytes.size() < 3) {
|
||||
haveEnoughData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bytes[2] != 0xAA) {
|
||||
bytes.pop_front();
|
||||
state = ReadState::SearchForHeader;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bytes.size() < 4) {
|
||||
haveEnoughData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bytes[3] != 0x55) {
|
||||
bytes.pop_front();
|
||||
state = ReadState::SearchForHeader;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bytes.size() < 5) {
|
||||
haveEnoughData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bytes[4] != 0x55) {
|
||||
bytes.pop_front();
|
||||
state = ReadState::SearchForHeader;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bytes.size() < 7) {
|
||||
haveEnoughData = false;
|
||||
break;
|
||||
}
|
||||
|
||||
packetLength = bytes[5] | (bytes[6] << 8);
|
||||
|
||||
checksum = false;
|
||||
headerSize = 7;
|
||||
packetLength += headerSize;
|
||||
currentIndex += 6;
|
||||
state = ReadState::GetData;
|
||||
break;
|
||||
case ReadState::GetData:
|
||||
// We do not include the checksum in packetLength so it doesn't get copied into the payload buffer
|
||||
@@ -98,23 +149,25 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
packet.data.clear();
|
||||
if(packetLength > 0)
|
||||
packet.data.resize(packetLength - headerSize);
|
||||
|
||||
auto i = 0;
|
||||
while(currentIndex < packetLength)
|
||||
packet.data[i++] = bytes[currentIndex++];
|
||||
|
||||
bytes.read(packet.data.data(), currentIndex, (packetLength - currentIndex));
|
||||
currentIndex = packetLength;
|
||||
|
||||
if(disableChecksum || !checksum || bytes[currentIndex] == ICSChecksum(packet.data)) {
|
||||
// Got a good packet
|
||||
gotGoodPackets = true;
|
||||
processedPackets.push_back(std::make_shared<Packet>(packet));
|
||||
for (auto a = 0; a < packetLength; a++)
|
||||
bytes.pop(packetLength);
|
||||
|
||||
if(packet.network == Network::NetID::DiskData && (packetLength - headerSize) % 2 == 0) {
|
||||
bytes.pop_front();
|
||||
}
|
||||
} else {
|
||||
if(gotGoodPackets) // Don't complain unless we've already gotten a good packet, in case we started in the middle of a stream
|
||||
report(APIEvent::Type::PacketChecksumError, APIEvent::Severity::Error);
|
||||
bytes.pop_front(); // Drop the first byte so it doesn't get picked up again
|
||||
}
|
||||
|
||||
|
||||
// Reset for the next packet
|
||||
currentIndex = 0;
|
||||
state = ReadState::SearchForHeader;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "icsneo/communication/sdio.h"
|
||||
#include "icsneo/platform/sharedsemaphore.h"
|
||||
#include "icsneo/platform/sharedmemory.h"
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/communication/socket.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
void SDIO::Find(std::vector<FoundDevice>& found) {
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::DEVICE_FINDER_FIND_ALL))
|
||||
return;
|
||||
uint16_t count;
|
||||
if(!socket.readTyped(count))
|
||||
return;
|
||||
|
||||
static constexpr auto serialSize = sizeof(FoundDevice::serial);
|
||||
std::vector<std::array<char, sizeof(FoundDevice::serial)>> serials(count);
|
||||
if(!socket.read(serials.data(), serials.size() * serialSize))
|
||||
return;
|
||||
|
||||
for(const auto& serial : serials) {
|
||||
auto& foundDevice = found.emplace_back();
|
||||
for(std::size_t i = 0; i < serialSize - 1 /* omit '\0' */; i++)
|
||||
foundDevice.serial[i] = static_cast<char>(std::toupper(serial[i]));
|
||||
foundDevice.makeDriver = [](const device_eventhandler_t& r, neodevice_t& d) {
|
||||
return std::unique_ptr<SDIO>(new SDIO(r, d));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool SDIO::open() {
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!(socket.writeTyped(RPC::SDIO_OPEN) && socket.writeString(device.device->getSerial())))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
|
||||
{
|
||||
std::string mailboxName;
|
||||
if(!socket.readString(mailboxName))
|
||||
return false;
|
||||
if(!inboundIO.open(mailboxName))
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
std::string mailboxName;
|
||||
if(!socket.readString(mailboxName))
|
||||
return false;
|
||||
if(!outboundIO.open(mailboxName))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
readThread = std::thread(&SDIO::readTask, this);
|
||||
writeThread = std::thread(&SDIO::writeTask, this);
|
||||
|
||||
deviceOpen = true;
|
||||
|
||||
return deviceOpen;
|
||||
}
|
||||
|
||||
bool SDIO::close() {
|
||||
if(!isOpen() && !isDisconnected()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
closing = true;
|
||||
|
||||
// wait for the reader/writer threads to close
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
|
||||
// unblocks the reader/writer threads
|
||||
if(!inboundIO.close())
|
||||
return false;
|
||||
if(!outboundIO.close())
|
||||
return false;
|
||||
|
||||
if(readThread.joinable())
|
||||
readThread.join();
|
||||
|
||||
if(writeThread.joinable())
|
||||
writeThread.join();
|
||||
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::SDIO_CLOSE))
|
||||
return false;
|
||||
if(!socket.writeString(device.device->getSerial()))
|
||||
return false;
|
||||
if(bool ret; !(socket.readTyped(ret) && ret))
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t flush;
|
||||
WriteOperation flushop;
|
||||
while(readQueue.try_dequeue(flush)) {}
|
||||
while(writeQueue.try_dequeue(flushop)) {}
|
||||
|
||||
closing = false;
|
||||
disconnected = false;
|
||||
deviceOpen = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDIO::isOpen() {
|
||||
return deviceOpen;
|
||||
}
|
||||
|
||||
void SDIO::readTask() {
|
||||
uint8_t data[MAX_DATA_SIZE];
|
||||
uint16_t messageLength;
|
||||
while(!closing) {
|
||||
if(!inboundIO.read(data, messageLength, std::chrono::milliseconds(100))) {
|
||||
if(!inboundIO)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(messageLength > 0) {
|
||||
if(messageLength > MAX_DATA_SIZE) { // split message
|
||||
std::vector<uint8_t> reassembled(messageLength);
|
||||
std::memcpy(reassembled.data(), data, MAX_DATA_SIZE);
|
||||
auto offset = reassembled.data() + MAX_DATA_SIZE;
|
||||
for(auto remaining = messageLength - MAX_DATA_SIZE; remaining > 0; remaining -= messageLength) {
|
||||
if(!inboundIO.read(offset, messageLength, std::chrono::milliseconds(10))) {
|
||||
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
offset += messageLength;
|
||||
}
|
||||
readQueue.enqueue_bulk(reassembled.data(), reassembled.size());
|
||||
} else {
|
||||
readQueue.enqueue_bulk(data, messageLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDIO::writeTask() {
|
||||
WriteOperation writeOp;
|
||||
while(!closing && !isDisconnected()) {
|
||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||
continue;
|
||||
|
||||
const auto dataSize = static_cast<LengthFieldType>(writeOp.bytes.size());
|
||||
|
||||
const auto tryWrite = [&](const void* input, LengthFieldType length) -> bool {
|
||||
for(int i = 0; i < 50; ++i) { // try to write for 5s, making sure we can close if need be
|
||||
if(outboundIO.write(input, length, std::chrono::milliseconds(100)))
|
||||
return true;
|
||||
if(!outboundIO)
|
||||
return false;
|
||||
}
|
||||
disconnected = true;
|
||||
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
|
||||
return false;
|
||||
};
|
||||
|
||||
if(!tryWrite(writeOp.bytes.data(), dataSize))
|
||||
continue;
|
||||
|
||||
if(writeOp.bytes.size() > MAX_DATA_SIZE) {
|
||||
auto offset = writeOp.bytes.data() + MAX_DATA_SIZE;
|
||||
for(LengthFieldType remaining = dataSize - MAX_DATA_SIZE; remaining > 0; ) {
|
||||
const auto toWrite = std::min(MAX_DATA_SIZE, remaining);
|
||||
if(!tryWrite(offset, toWrite))
|
||||
break;
|
||||
remaining -= toWrite;
|
||||
offset += toWrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
#include "icsneo/communication/socket.h"
|
||||
#include "icsneo/api/event.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
bool SocketBase::open() {
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if(::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if((sockFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
#ifdef _WIN32
|
||||
::WSACleanup();
|
||||
#endif
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
sockIsOpen = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::close() {
|
||||
#ifdef _WIN32
|
||||
if(::closesocket(sockFileDescriptor) < 0) {
|
||||
// should probably check for WSAEWOULDBLOCK as ::closesocket must be repeated to close in that case
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
::WSACleanup();
|
||||
#else
|
||||
// ignore ENOTCONN from ::shutdown as the peer may have already forcibly closed its socket (e.g. a crash)
|
||||
if( ((::shutdown(sockFileDescriptor, SHUT_RDWR) < 0) && (ENOTCONN != errno)) ||
|
||||
((::close(sockFileDescriptor) < 0) && (EBADF == errno)) )
|
||||
{
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
sockIsOpen = false;
|
||||
sockIsConnected = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::connect() {
|
||||
sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
if( (!isOpen() && !open()) ||
|
||||
(::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
|
||||
(::connect(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToConnect, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD tv = 5000u; // 5 second receive timeout but in windows
|
||||
#else
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 5u; // 5 second receive timeout
|
||||
tv.tv_usec = 0;
|
||||
setIgnoreSIGPIPE();
|
||||
#endif
|
||||
|
||||
// Set the 5 second timeout from above in the socket options
|
||||
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
|
||||
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
|
||||
|
||||
sockIsConnected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::isOpen() {
|
||||
return sockIsOpen;
|
||||
}
|
||||
|
||||
bool SocketBase::isConnected() {
|
||||
return sockIsConnected;
|
||||
}
|
||||
|
||||
bool SocketBase::read(void* output, std::size_t length) {
|
||||
if(!(isOpen() && isConnected()))
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
return ::recv(sockFileDescriptor, (char*)output, (int)length, 0) > 0;
|
||||
#else
|
||||
if(::read(sockFileDescriptor, output, length) <= 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToRead, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SocketBase::write(const void* input, std::size_t length) {
|
||||
if(!(isOpen() && isConnected()))
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
if(::send(sockFileDescriptor, (char*)input, (int)length, 0) < 0) {
|
||||
switch(WSAGetLastError()) {
|
||||
case WSAETIMEDOUT:
|
||||
case WSAENOTCONN:
|
||||
case WSAESHUTDOWN:
|
||||
case WSAECONNRESET:
|
||||
case WSAECONNABORTED:
|
||||
{
|
||||
sockIsOpen = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if(::write(sockFileDescriptor, input, length) < 0) {
|
||||
switch(errno) {
|
||||
case EPIPE:
|
||||
case ETIMEDOUT:
|
||||
{
|
||||
sockIsOpen = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::readString(std::string& str) {
|
||||
size_t length;
|
||||
if(!read(&length, sizeof(length)))
|
||||
return false;
|
||||
str.resize(length);
|
||||
if(!read(str.data(), length))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketBase::writeString(const std::string& str) {
|
||||
size_t length = str.size();
|
||||
if(!write(&length, sizeof(length)))
|
||||
return false;
|
||||
if(!write(str.data(), length))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ActiveSocket::ActiveSocket(SocketFileDescriptor sockFD) {
|
||||
sockFileDescriptor = sockFD;
|
||||
sockIsOpen = true;
|
||||
sockIsConnected = true;
|
||||
}
|
||||
|
||||
ActiveSocket::ActiveSocket(Protocol protocol, uint16_t port) {
|
||||
this->protocol = protocol;
|
||||
this->port = port;
|
||||
}
|
||||
|
||||
ActiveSocket::~ActiveSocket() {
|
||||
if(isOpen())
|
||||
close();
|
||||
}
|
||||
|
||||
LockedSocket::LockedSocket(SocketBase& base, std::unique_lock<std::mutex>&& l) :
|
||||
SocketBase(base), lock(std::move(l)) {
|
||||
}
|
||||
|
||||
LockedSocket lockSocket() {
|
||||
static ActiveSocket socket(SocketBase::Protocol::TCP, RPC_PORT);
|
||||
if(!socket.isOpen())
|
||||
socket.open();
|
||||
if(!socket.isConnected())
|
||||
socket.connect();
|
||||
static std::mutex lock;
|
||||
return LockedSocket(socket, std::unique_lock<std::mutex>(lock));
|
||||
}
|
||||
|
||||
void SocketBase::setIgnoreSIGPIPE() {
|
||||
#ifndef _WIN32
|
||||
struct sigaction sa{};
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
::sigaction(SIGPIPE, &sa, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
Acceptor::Acceptor(Protocol protocol, uint16_t port)
|
||||
: ActiveSocket(protocol, port) {
|
||||
}
|
||||
|
||||
bool Acceptor::initialize() {
|
||||
if(open() && bind() && listen()) {
|
||||
isValid = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<ActiveSocket> Acceptor::accept()
|
||||
{
|
||||
if(!isValid)
|
||||
return nullptr;
|
||||
const SocketFileDescriptor acceptFd = ::accept(sockFileDescriptor, (sockaddr*)NULL, NULL);
|
||||
if(acceptFd < 0)
|
||||
return nullptr;
|
||||
return std::make_shared<ActiveSocket>(acceptFd);
|
||||
}
|
||||
|
||||
bool Acceptor::bind()
|
||||
{
|
||||
sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(port);
|
||||
if( (::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
|
||||
(::bind(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
|
||||
{
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToBind, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Acceptor::listen()
|
||||
{
|
||||
if(::listen(sockFileDescriptor, UINT8_MAX) < 0) {
|
||||
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToListen, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} //namespace icsneo
|
||||
+1862
-178
File diff suppressed because it is too large
Load Diff
+96
-31
@@ -1,13 +1,8 @@
|
||||
#include <array>
|
||||
#include "icsneo/device/devicefinder.h"
|
||||
#include "icsneo/platform/devices.h"
|
||||
#include "icsneo/device/founddevice.h"
|
||||
#include "icsneo/communication/sdio.h"
|
||||
#include "generated/extensions/builtin.h"
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
#include "icsneo/communication/socket.h"
|
||||
#else
|
||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||
#include "icsneo/platform/firmio.h"
|
||||
#endif
|
||||
@@ -23,6 +18,13 @@
|
||||
#ifdef ICSNEO_ENABLE_FTDI
|
||||
#include "icsneo/platform/ftdi.h"
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_FTD3XX
|
||||
#include "icsneo/platform/ftd3xx.h"
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_TCP
|
||||
#include "icsneo/platform/tcp.h"
|
||||
#endif
|
||||
|
||||
using namespace icsneo;
|
||||
@@ -45,17 +47,30 @@ static void makeIfPIDMatches(const FoundDevice& dev, std::vector<std::shared_ptr
|
||||
into.push_back(std::make_shared<T>(dev));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void makeIfSerialRangeMatches(const FoundDevice& dev, std::vector<std::shared_ptr<Device>>& into) {
|
||||
// Relies on the subclass to have
|
||||
// `static constexpr uint32_t SERIAL_RANGE_LOW = 0x12345678`
|
||||
// `static constexpr uint32_t SERIAL_RANGE_HIGH = 0x12345678`
|
||||
// and also a public constructor `T(const FoundDevice& dev)`
|
||||
// Use macro ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE() to create these
|
||||
uint32_t serialNum = Device::SerialStringToNum(dev.serial);
|
||||
if(serialNum >= Device::SerialStringToNum(T::SERIAL_RANGE_LOW) && serialNum <= Device::SerialStringToNum(T::SERIAL_RANGE_HIGH))
|
||||
into.push_back(std::make_shared<T>(dev));
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
static std::vector<FoundDevice> newDriverFoundDevices;
|
||||
newDriverFoundDevices.clear();
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
SDIO::Find(newDriverFoundDevices);
|
||||
#else
|
||||
#ifdef ICSNEO_ENABLE_FIRMIO
|
||||
FirmIO::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_TCP
|
||||
TCP::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
|
||||
PCAP::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
@@ -67,6 +82,9 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
#ifdef ICSNEO_ENABLE_FTDI
|
||||
FTDI::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef ICSNEO_ENABLE_FTD3XX
|
||||
FTD3XX::Find(newDriverFoundDevices);
|
||||
#endif
|
||||
|
||||
// Weak because we don't want to keep devices open if they go out of scope elsewhere
|
||||
@@ -119,6 +137,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
makeIfSerialMatches<NeoOBD2SIM>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVICONNECT_H_
|
||||
makeIfSerialMatches<NeoVIConnect>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE_H_
|
||||
makeIfPIDMatches<NeoVIFIRE>(dev, newFoundDevices);
|
||||
#endif
|
||||
@@ -131,6 +153,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
makeIfSerialMatches<NeoVIFIRE3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3FLEXRAY_H_
|
||||
makeIfSerialMatches<NeoVIFIRE3FlexRay>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIRED2_H_
|
||||
makeIfSerialMatches<NeoVIRED2>(dev, newFoundDevices);
|
||||
#endif
|
||||
@@ -147,6 +173,22 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
makeIfSerialMatches<RADA2B>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET_H_
|
||||
makeIfSerialRangeMatches<RADComet>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET2_H_
|
||||
makeIfSerialRangeMatches<RADComet2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET3_H_
|
||||
makeIfSerialMatches<RADComet3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOONT1S_H_
|
||||
makeIfSerialMatches<RADMoonT1S>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADEPSILON_H_
|
||||
makeIfSerialMatches<RADEpsilon>(dev, newFoundDevices);
|
||||
#endif
|
||||
@@ -162,6 +204,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
#ifdef __RADGIGASTAR_H_
|
||||
makeIfSerialMatches<RADGigastar>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADGIGASTAR2_H_
|
||||
makeIfSerialMatches<RADGigastar2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADJUPITER_H_
|
||||
makeIfSerialMatches<RADJupiter>(dev, newFoundDevices);
|
||||
@@ -171,6 +217,14 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
makeIfSerialMatches<RADMoon2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON2ZL_H_
|
||||
makeIfSerialMatches<RADMoon2ZL>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON3_H_
|
||||
makeIfSerialMatches<RADMoon3>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOONDUO_H_
|
||||
makeIfSerialMatches<RADMoonDuo>(dev, newFoundDevices);
|
||||
#endif
|
||||
@@ -228,27 +282,7 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
}
|
||||
|
||||
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
static std::vector<DeviceType> supportedDevices;
|
||||
|
||||
if (!supportedDevices.empty())
|
||||
return supportedDevices;
|
||||
|
||||
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
|
||||
{
|
||||
auto socket = lockSocket();
|
||||
if(!socket.writeTyped(RPC::DEVICE_FINDER_GET_SUPORTED_DEVICES))
|
||||
return supportedDevices;
|
||||
uint16_t count;
|
||||
if(!socket.readTyped(count))
|
||||
return supportedDevices;
|
||||
std::vector<devicetype_t> devices(count);
|
||||
socket.read(devices.data(), devices.size() * sizeof(devicetype_t));
|
||||
supportedDevices.reserve(count);
|
||||
for(auto& dev : devices)
|
||||
supportedDevices.emplace_back(DeviceType(dev));
|
||||
}
|
||||
#else
|
||||
supportedDevices = {
|
||||
static std::vector<DeviceType> supportedDevices = {
|
||||
|
||||
#ifdef __ETHERBADGE_H_
|
||||
EtherBADGE::DEVICE_TYPE,
|
||||
@@ -266,6 +300,10 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
NeoVIRED2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVICONNECT_H_
|
||||
NeoVIConnect::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE_H_
|
||||
NeoVIFIRE::DEVICE_TYPE,
|
||||
#endif
|
||||
@@ -274,6 +312,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
NeoVIFIRE2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3_H_
|
||||
NeoVIFIRE3::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIFIRE3FLEXRAY_H_
|
||||
NeoVIFIRE3FlexRay::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __NEOVIION_H_
|
||||
NeoVIION::DEVICE_TYPE,
|
||||
#endif
|
||||
@@ -286,6 +332,18 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
RADA2B::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET_H_
|
||||
RADComet::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADCOMET3_H_
|
||||
RADComet3::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOONT1S_H_
|
||||
RADMoonT1S::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADEPSILON_H_
|
||||
RADEpsilon::DEVICE_TYPE,
|
||||
#endif
|
||||
@@ -302,10 +360,18 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
RADGigastar::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON2_H_
|
||||
#ifdef __RADGIGASTAR2_H_
|
||||
RADGigastar2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#if defined __RADMOON2_H_ || defined __RADMOON2ZL_H_
|
||||
RADMoon2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOON3_H_
|
||||
RADMoon3::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADMOONDUO_H_
|
||||
RADMoonDuo::DEVICE_TYPE,
|
||||
#endif
|
||||
@@ -351,7 +417,6 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
#endif
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
return supportedDevices;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
@@ -414,6 +445,15 @@ int64_t IDeviceSettings::getBaudrateFor(Network net) const {
|
||||
}
|
||||
return baudrate;
|
||||
}
|
||||
case Network::Type::LIN: {
|
||||
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return cfg->Baudrate;
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return -1;
|
||||
@@ -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
|
||||
return true;
|
||||
}
|
||||
case Network::Type::LIN: {
|
||||
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool valid = ValidateLINBaudrate(baudrate);
|
||||
if(!valid) {
|
||||
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
cfg->Baudrate = (uint32_t)baudrate;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
@@ -704,6 +759,186 @@ bool IDeviceSettings::setTerminationFor(Network net, bool enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<bool> IDeviceSettings::isCommanderResistorEnabledFor(Network net) const {
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch(net.getType()) {
|
||||
case Network::Type::LIN: {
|
||||
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (cfg->CommanderResistor != RESISTOR_OFF);
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool IDeviceSettings::setCommanderResistorFor(Network net, bool resistor_on) {
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(readonly) {
|
||||
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(net.getType()) {
|
||||
case Network::Type::LIN: {
|
||||
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
cfg->CommanderResistor = resistor_on ? RESISTOR_ON : RESISTOR_OFF;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<LINMode> IDeviceSettings::getLINModeFor(Network net) const {
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch(net.getType()) {
|
||||
case Network::Type::LIN: {
|
||||
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return static_cast<LINMode>(cfg->Mode);
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool IDeviceSettings::setLINModeFor(Network net, LINMode mode) {
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(readonly) {
|
||||
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(net.getType()) {
|
||||
case Network::Type::LIN: {
|
||||
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
cfg->Mode = static_cast<uint8_t>(mode);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint8_t> IDeviceSettings::getLINCommanderResponseTimeFor(Network net) const {
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
switch(net.getType()) {
|
||||
case Network::Type::LIN: {
|
||||
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return cfg->numBitsDelay;
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool IDeviceSettings::setLINCommanderResponseTimeFor(Network net, uint8_t bits) {
|
||||
if(disabled) {
|
||||
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(readonly) {
|
||||
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(net.getType()) {
|
||||
case Network::Type::LIN: {
|
||||
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
|
||||
if(cfg == nullptr) {
|
||||
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
cfg->numBitsDelay = bits;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> bool IDeviceSettings::applyStructure(const T& newStructure) {
|
||||
if(!settingsLoaded) {
|
||||
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
|
||||
|
||||
+15
-4
@@ -5,7 +5,11 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, Disk::MemoryType memType) {
|
||||
|
||||
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
|
||||
uint64_t& cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
|
||||
|
||||
if(amount == 0)
|
||||
return 0;
|
||||
|
||||
@@ -57,7 +61,7 @@ std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_e
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
auto readAmount = readLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
||||
useAlignedReadBuffer ? alignedReadBuffer.data() : (into + intoOffset), idealBlockSize, timeout);
|
||||
useAlignedReadBuffer ? alignedReadBuffer.data() : (into + intoOffset), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(!readAmount.has_value() || *readAmount < curAmt) {
|
||||
@@ -94,12 +98,19 @@ std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_e
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount) {
|
||||
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount, MemoryType memType) {
|
||||
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
|
||||
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
|
||||
|
||||
if(pos <= cachePos + cache.size() && pos + amount >= cachePos)
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
std::optional<uint64_t> ReadDriver::readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter) {
|
||||
std::optional<uint64_t> ReadDriver::readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter, MemoryType memType) {
|
||||
|
||||
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
|
||||
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
|
||||
|
||||
if(cache.empty())
|
||||
return std::nullopt; // Nothing in the cache
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
|
||||
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
if(amount == 0)
|
||||
return 0;
|
||||
|
||||
@@ -51,7 +51,7 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
|
||||
const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
|
||||
if(useAlignedWriteBuffer) {
|
||||
auto read = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
|
||||
alignedWriteBuffer.data(), idealBlockSize, timeout);
|
||||
alignedWriteBuffer.data(), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(read != idealBlockSize)
|
||||
@@ -62,7 +62,7 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
|
||||
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
|
||||
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout);
|
||||
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout, memType);
|
||||
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
|
||||
|
||||
if(!bytesTransferred.has_value() || *bytesTransferred < curAmt) {
|
||||
@@ -83,6 +83,6 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
|
||||
// No matter how much succeeded, to be safe, we'll invalidate anything
|
||||
// we may have even tried to write, since it may have succeeded without
|
||||
// notifying, etc.
|
||||
readDriver.invalidateCache(pos, amount);
|
||||
readDriver.invalidateCache(pos, amount, memType);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
#include "icsneo/disk/extextractordiskreaddriver.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include "icsneo/communication/multichannelcommunication.h"
|
||||
#include "icsneo/api/lifetime.h"
|
||||
#include <cstring>
|
||||
#include "icsneo/communication/message/diskdatamessage.h"
|
||||
|
||||
//#define ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
@@ -13,7 +10,7 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
|
||||
if(amount > getBlockSizeBounds().second)
|
||||
return std::nullopt;
|
||||
@@ -28,7 +25,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
|
||||
unsigned int attempts = 4;
|
||||
while (attempts-- > 0)
|
||||
{
|
||||
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout);
|
||||
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout, memType);
|
||||
if (ret.has_value())
|
||||
break;
|
||||
}
|
||||
@@ -36,7 +33,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
|
||||
}
|
||||
|
||||
std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
|
||||
uint64_t sector = pos / SectorSize;
|
||||
@@ -46,144 +43,33 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligne
|
||||
if (largeSectorCount != uint64_t(sectorCount))
|
||||
return std::nullopt;
|
||||
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
uint16_t receiving = 0; // How much are we about to get before another header or completion
|
||||
uint64_t received = 0;
|
||||
uint16_t receivedCurrent = 0;
|
||||
size_t skipping = 0;
|
||||
std::vector<uint8_t> header;
|
||||
std::unique_lock<std::mutex> lk(m);
|
||||
bool error = !com.redirectRead([&](std::vector<uint8_t>&& data) {
|
||||
std::unique_lock<std::mutex> lk2(m);
|
||||
if(error) {
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
std::mutex mutex;
|
||||
uint8_t* intoOffset = into;
|
||||
int64_t remaining = amount;
|
||||
bool complete = false;
|
||||
|
||||
if(skipping > data.size()) {
|
||||
skipping -= data.size();
|
||||
return;
|
||||
}
|
||||
size_t offset = skipping;
|
||||
skipping = 0;
|
||||
while(offset < data.size()) {
|
||||
size_t left = data.size() - offset;
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Going to process " << left << " bytes" << std::endl;
|
||||
#endif
|
||||
if(header.size() != HeaderLength) {
|
||||
if(header.empty() && left && data[offset] != 0xaa) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Incorrect header " << int(data[offset]) << ' ' << int(offset) << std::endl;
|
||||
#endif
|
||||
error = true;
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
const auto handle = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> message) {
|
||||
if(remaining > 0) {
|
||||
const auto diskdata = std::static_pointer_cast<DiskDataMessage>(message);
|
||||
|
||||
const auto& data = diskdata->data;
|
||||
std::copy(data.data(), data.data() + data.size(), intoOffset);
|
||||
|
||||
intoOffset += data.size();
|
||||
remaining -= data.size();
|
||||
|
||||
if(remaining == 0) {
|
||||
{
|
||||
std::scoped_lock<std::mutex> lk(mutex);
|
||||
complete = true;
|
||||
}
|
||||
|
||||
// Did we get a correct header and at least one byte of data?
|
||||
const auto begin = data.begin() + offset;
|
||||
int32_t headerLeft = int32_t(HeaderLength - header.size());
|
||||
if(int32_t(left) < headerLeft) {
|
||||
// Not enough data here, grab what header we can and continue
|
||||
header.insert(header.end(), begin, data.end());
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << int(left) << " bytes of header at " << offset << " (incomplete " <<
|
||||
header.size() << ')' << std::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
header.insert(header.end(), begin, begin + headerLeft);
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << int(headerLeft) << " bytes of header at " << offset << " (complete " <<
|
||||
header.size() << ')' << std::endl;
|
||||
#endif
|
||||
offset += headerLeft;
|
||||
|
||||
if(header[1] == uint8_t(Network::NetID::RED)) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got extended response " << int(offset) << std::endl;
|
||||
#endif
|
||||
// This is the extended command response, not all devices send this
|
||||
// If we got it, we need to figure out how much more data to ignore
|
||||
uint16_t length = (header[2] + (header[3] << 8));
|
||||
// Try for another header after this, regardless how much we choose
|
||||
// to skip and how we skip it
|
||||
header.clear();
|
||||
if(length <= 6) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Incorrect extended response length " << int(length) << ' ' << int(offset) << std::endl;
|
||||
#endif
|
||||
error = true;
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
length -= 7;
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Skipping " << int(length) << ' ' << int(left) << std::endl;
|
||||
#endif
|
||||
if(left < length) {
|
||||
skipping = length - left;
|
||||
return;
|
||||
}
|
||||
offset += length;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The device tells us how much it's sending us before the next header
|
||||
receiving = (header[5] | (header[6] << 8));
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Started packet of size " << receiving << " bytes" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
left = data.size() - offset;
|
||||
auto count = uint16_t(std::min<uint64_t>(std::min<uint64_t>(receiving - receivedCurrent, left), amount - received));
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "With " << int(left) << " bytes " << int(offset) << std::endl;
|
||||
#endif
|
||||
memcpy(into + received, data.data() + offset, count);
|
||||
received += count;
|
||||
receivedCurrent += count;
|
||||
offset += count;
|
||||
|
||||
if(amount == received) {
|
||||
if(receivedCurrent % 2 == 0)
|
||||
offset++;
|
||||
header.clear(); // Now we will need another header
|
||||
lk2.unlock();
|
||||
cv.notify_all();
|
||||
lk2.lock();
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Finished!" << std::endl;
|
||||
#endif
|
||||
}
|
||||
else if(receivedCurrent == receiving) {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << count << " bytes, " << receivedCurrent << " byte packet " << received <<
|
||||
" complete of " << amount << std::endl;
|
||||
#endif
|
||||
if(receivedCurrent % 2 == 0)
|
||||
offset++;
|
||||
header.clear(); // Now we will need another header
|
||||
receivedCurrent = 0;
|
||||
} else {
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "Got " << count << " bytes, incomplete (of " << receiving << " bytes)" << std::endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
});
|
||||
Lifetime clearRedirect([&com, &lk] { lk.unlock(); com.clearRedirectRead(); });
|
||||
}, std::make_shared<MessageFilter>(Network::NetID::DiskData)));
|
||||
|
||||
if(error)
|
||||
return std::nullopt;
|
||||
|
||||
error = !com.sendCommand(ExtendedCommand::Extract, {
|
||||
if(!com.sendCommand(ExtendedCommand::Extract, {
|
||||
uint8_t(sector & 0xff),
|
||||
uint8_t((sector >> 8) & 0xff),
|
||||
uint8_t((sector >> 16) & 0xff),
|
||||
@@ -196,13 +82,16 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligne
|
||||
uint8_t((sectorCount >> 8) & 0xff),
|
||||
uint8_t((sectorCount >> 16) & 0xff),
|
||||
uint8_t((sectorCount >> 24) & 0xff),
|
||||
});
|
||||
if(error)
|
||||
})) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lk(mutex);
|
||||
const auto successful = cv.wait_for(lk, timeout, [&](){ return complete; });
|
||||
com.removeMessageCallback(handle);
|
||||
|
||||
if(!successful)
|
||||
return std::nullopt;
|
||||
|
||||
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
|
||||
if(hitTimeout || error)
|
||||
return std::nullopt;
|
||||
|
||||
return amount;
|
||||
return amount - remaining;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#include "icsneo/disk/neomemorydiskdriver.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include "icsneo/communication/message/flashmemorymessage.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
const auto filter = std::make_shared<MessageFilter>((memType == MemoryType::SD ? Network::NetID::NeoMemorySDRead : Network::NetID::RED_INT_MEMORYREAD));
|
||||
filter->includeInternalInAny = true;
|
||||
|
||||
if(pos % SectorSize != 0)
|
||||
return std::nullopt;
|
||||
@@ -16,35 +19,47 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
|
||||
return std::nullopt;
|
||||
|
||||
const uint64_t currentSector = pos / SectorSize;
|
||||
auto msg = com.waitForMessageSync([¤tSector, &com] {
|
||||
const uint8_t memLocation = (uint8_t)memType;
|
||||
|
||||
uint64_t numWords = amount / 2;
|
||||
|
||||
auto msg = com.waitForMessageSync([¤tSector, &memLocation, &com, &numWords] {
|
||||
return com.sendCommand(Command::NeoReadMemory, {
|
||||
MemoryTypeSD,
|
||||
memLocation,
|
||||
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)
|
||||
uint8_t(numWords & 0xFF),
|
||||
uint8_t((numWords >> 8) & 0xFF),
|
||||
uint8_t((numWords >> 16) & 0xFF),
|
||||
uint8_t((numWords >> 24) & 0xFF)
|
||||
});
|
||||
}, NeoMemorySDRead, timeout);
|
||||
}, filter, 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;
|
||||
if(memType == MemoryType::SD) {
|
||||
const auto mem = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
||||
if(!mem || mem->data.size() != SectorSize) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
memcpy(into, mem->data.data(), SectorSize);
|
||||
} else { // flash
|
||||
const auto mem = std::dynamic_pointer_cast<FlashMemoryMessage>(msg);
|
||||
if(!mem || mem->data.size() != SectorSize) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
memcpy(into, mem->data.data(), SectorSize);
|
||||
}
|
||||
|
||||
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) {
|
||||
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
|
||||
|
||||
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
|
||||
|
||||
@@ -55,15 +70,19 @@ std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communicati
|
||||
return std::nullopt;
|
||||
|
||||
const uint64_t currentSector = pos / SectorSize;
|
||||
auto msg = com.waitForMessageSync([¤tSector, &com, from, amount] {
|
||||
const uint8_t memLocation = (uint8_t)memType;
|
||||
|
||||
uint64_t numWords = amount / 2;
|
||||
|
||||
auto msg = com.waitForMessageSync([¤tSector, &memLocation, &com, from, amount, &numWords] {
|
||||
std::vector<uint8_t> command = {
|
||||
MemoryTypeSD,
|
||||
memLocation,
|
||||
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(numWords & 0xFF),
|
||||
uint8_t((numWords >> 8) & 0xFF),
|
||||
};
|
||||
command.insert(command.end(), from, from + amount);
|
||||
return com.sendCommand(Command::NeoWriteMemory, command);
|
||||
|
||||
@@ -4,25 +4,25 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
|
||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
|
||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
|
||||
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
|
||||
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using namespace icsneo;
|
||||
using namespace icsneo::Disk;
|
||||
|
||||
std::optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
|
||||
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
|
||||
if(amount > getBlockSizeBounds().second)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
****************
|
||||
**API Usage**
|
||||
****************
|
||||
|
||||
API Concepts
|
||||
================
|
||||
|
||||
Overview
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Events convey information about the API's inner workings to the user. There are 3 severity levels: ``EventInfo``, ``EventWarning``, and ``Error``.
|
||||
**However, the API treats events of severities** ``EventInfo`` **and** ``EventWarning`` **differently than those of severity** ``Error`` **.**
|
||||
From here on out, when we (and the API functions) refer to "events", we refer exclusively to those of severities ``EventInfo`` and ``EventWarning``, which use the events_ system.
|
||||
Those of severity ``Error`` are referred to as "errors", which use a separate errors_ system.
|
||||
|
||||
Events should periodically be read out in order to avoid overflowing, and the last error should be read out immediately after an API function fails.
|
||||
|
||||
Additionally, `event callbacks`_ can be registered, which may remove the need to periodically read events in some cases.
|
||||
|
||||
.. _events:
|
||||
|
||||
Events
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The API stores events in a single buffer that can has a default size of 10,000 events.
|
||||
This limit includes 1 reserved slot at the end of the buffer for a potential Event of type ``TooManyEvents`` and severity ``EventWarning``, which is added when there are too many events for the buffer to hold.
|
||||
This could occur if the events aren't read out by the user often enough, or if the user sets the size of the buffer to a value smaller than the number of existing events.
|
||||
There will only ever be one of these ``TooManyEvents`` events, and it will always be located at the very end of the buffer if it exists.
|
||||
Because of this reserved slot, the buffer by default is able to hold 9,999 events. If capacity is exceeded, the oldest events in the buffer are automatically discarded until the buffer is exactly at capacity again.
|
||||
When events are read out by the user, they are removed from the buffer. If an event filter is used, only the filtered events will be removed from the buffer.
|
||||
|
||||
In a multithreaded environment, all threads will log their events to the same buffer. In this case, the order of events will largely be meaningless, although the behavior of ``TooManyEvents`` is still guaranteed to be as described above.
|
||||
|
||||
.. _event callbacks:
|
||||
|
||||
Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Users may register event callbacks, which are automatically called whenever a matching event is logged.
|
||||
Message callbacks consist of a user-defined ``std::function< void( std::shared_ptr<APIEvent> ) >`` and optional EventFilter used for matching.
|
||||
If no EventFilter is provided, the default-constructed one will be used, which matches any event.
|
||||
Registering a callback returns an ``int`` representing the id of the callback, which should be stored by the user and later used to remove the callback when desired.
|
||||
Note that this functionality is only available in C and C++. C does not currently support filters.
|
||||
|
||||
Event callbacks are run after the event has been added to the buffer of events. The buffer of events may be safely modified within the callback, such as getting (flushing) the type and severity of the triggering event.
|
||||
Using event callbacks in this manner means that periodically reading events is unnecessary.
|
||||
|
||||
.. _errors:
|
||||
|
||||
Errors
|
||||
~~~~~~~~~
|
||||
|
||||
The error system is threadsafe and separate from the events_ system.
|
||||
Each thread keeps track of the last error logged on it, and getting the last error will return the last error from the calling thread, removing it in the process.
|
||||
Trying to get the last error when there is none will return an event of type ``NoErrorFound`` and severity ``EventInfo``.
|
||||
|
||||
The API also contains some threads for internal use which may potentially log errors of their own and are inaccessible to the user.
|
||||
These threads have been marked to downgrade any errors that occur on them to severity ``EventWarning`` and will log the corresponding event in the events_ system described above.
|
||||
|
||||
Device Concepts
|
||||
================
|
||||
|
||||
Open/Close Status
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In order to access device functionality, the device must first be opened, which begins communication between the API and the device.
|
||||
The exception to this is setting the message polling status of the device.
|
||||
Trying to open/close the device when the device is already open/closed will result in an error being logged on the calling thread.
|
||||
|
||||
Online/Offline Status
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Going online begins communication between the device and the rest of the network. In order to be online, the device must also be open.
|
||||
Trying to go online/offline when the device is already online/offline will result in an error being logged on the calling thread.
|
||||
|
||||
It is possible to have a device be both open and offline. In this situation, device settings such as the baudrate may still be read and changed.
|
||||
This is useful for setting up your device properly before going online and joining the network.
|
||||
|
||||
Message Callbacks and Polling
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In order to handle messages, users may register message callbacks, which are automatically called whenever a matching message is received.
|
||||
Message callbacks consist of a user-defined ``std::function< void( std::shared_ptr<Message> ) >`` and optional message filter used for matching.
|
||||
If no message filter is provided, the default-constructed one will be used, which matches any message.
|
||||
Registering a callback returns an ``int`` representing the id of the callback, which should be stored by the user and later used to remove the callback when desired.
|
||||
Note that this functionality is only available in C and C++. C does not currently support filters.
|
||||
|
||||
The default method of handling messages is to enable message polling, which is built upon message callbacks.
|
||||
Enabling message polling will register a callback that stores each received message in a buffer for later retrieval.
|
||||
The default limit of this buffer is 20,000 messages.
|
||||
If the limit is exceeded, the oldest messages will be dropped until the buffer is at capacity, and an error will be logged on the calling thread.
|
||||
To avoid exceeding the limit, try to get messages periodically, which will flush the buffer upon each call.
|
||||
Attempting to read messages without first enabling message polling will result in an error being logged on the calling thread.
|
||||
|
||||
It is recommended to either enable message polling or manually register callbacks to handle messages, but not both.
|
||||
|
||||
Write Blocking Status
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The write blocking status of the device determines the behavior of attempting to transmit to the device (likely via sending messages) with a large backlog of messages.
|
||||
If write blocking is enabled, then the transmitting thread will wait for the entire buffer to be transmitted.
|
||||
If write blocking is disabled, then the attempt to transmit will simply fail and an error will be logged on the calling thread.
|
||||
|
||||
A2B Wave Output
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
Users may add a ``icsneo::A2BWAVOutput`` message callback to their device in order to write A2B PCM data to a WAVE file. The message callback listens for ``icsneo::A2BMessage``
|
||||
messages and writes both downstream and upstream channels to a single wave file. If downstream and upstream each have ``32`` channels, the wave file will contain ``2*32 = 64``
|
||||
total channels. The first half of the channels, channels ``0-31`` in the outputted wave file, represent downstream channel ``0-31``. Likewise, the second half of the channels,
|
||||
channels ``32-63`` in the outputted wave file, represent upstream channel ``0-31``. Let ``NUM_CHANNELS`` be the total number of channels in a single stream. If we introduce a
|
||||
variable ``IS_UPSTREAM`` which is ``0`` when downstream and ``1`` when upstream and desired a channel ``CHANNEL_NUM`` in either downstream or upstream the
|
||||
channel ``IS_UPSTREAM * NUM_CHANNELS + CHANNEL_NUM`` would correspond to the channel in the outputted wave file.
|
||||
|
||||
Wave files may be split by channel using programs such as ``FFmpeg``. Consider a file ``out.wav`` which was generated using a ``icsneo::A2BWAVOutput`` object
|
||||
and contains ``32`` channels per stream. The ``icsneo::A2BWavoutput`` object injested PCM data with a sample rate of ``44.1 kHz`` and bit depth of ``24``. The corresponding
|
||||
channel of upstream channel ``8`` in ``out.wav`` would be ``1*32 + 8 = 40``. The following ``FFmpeg`` command may be ran in a linux environment to create a new wave
|
||||
file ``out_upstream_ch8.wav`` which contains only PCM samples off of upstream channel ``8``.
|
||||
|
||||
``ffmpeg -i out.wav -ar 44100 -acodec pcm_s24le -map_channel 0.0.40 out_upstream_ch8.wav``
|
||||
+19
-171
@@ -1,188 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# NOTE: The conf.py file is automatically updated by CMake when there are
|
||||
# changes to the version. Update the conf.py.template and then build to
|
||||
# propagate changes here. This file should be committed so that
|
||||
# ReadTheDocs can use it.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import subprocess
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
subprocess.call('cd ..; doxygen docs/icsneocpp/Doxyfile', shell=True)
|
||||
subprocess.call('cd ..; doxygen docs/icsneoc/Doxyfile', shell=True)
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.imgmath', 'sphinx.ext.todo', 'breathe' ]
|
||||
|
||||
breathe_projects = { "libicsneo": "build/doxygen/xml" }
|
||||
breathe_default_project = "libicsneo"
|
||||
|
||||
try:
|
||||
os.makedirs('build/sphinx')
|
||||
except FileExistsError:
|
||||
pass
|
||||
try:
|
||||
os.makedirs('build/doxygen')
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
subprocess.call('cd ..; doxygen docs/Doxyfile', shell=True)
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'libicsneo'
|
||||
copyright = '2018-2020, Intrepid Control Systems, Inc.'
|
||||
copyright = '2024, Intrepid Control Systems, Inc.'
|
||||
author = 'Intrepid Control Systems, Inc.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.2.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.2.0 '
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
extensions = ['breathe', 'sphinx.ext.autodoc']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
breathe_projects = {
|
||||
'icsneocpp': 'icsneocpp/doxygen/xml',
|
||||
'icsneoc': 'icsneoc/doxygen/xml',
|
||||
}
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
breathe_default_project = 'icsneocpp'
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
html_theme_options = {
|
||||
|
||||
}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# This is required for the alabaster theme
|
||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||
html_sidebars = {
|
||||
'**': [
|
||||
'relations.html', # needs 'show_related': True theme option to display
|
||||
'searchbox.html',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'libicsneodoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'libicsneo.tex', 'libicsneo Documentation',
|
||||
'Intrepid Control Systems, Inc.', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'libicsneo', 'libicsneo Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'libicsneo', 'libicsneo Documentation',
|
||||
author, 'libicsneo', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# NOTE: The conf.py file is automatically updated by CMake when there are
|
||||
# changes to the version. Update the conf.py.template and then build to
|
||||
# propagate changes here. This file should be committed so that
|
||||
# ReadTheDocs can use it.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import subprocess
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.imgmath', 'sphinx.ext.todo', 'breathe' ]
|
||||
|
||||
breathe_projects = { "libicsneo": "build/doxygen/xml" }
|
||||
breathe_default_project = "libicsneo"
|
||||
|
||||
try:
|
||||
os.makedirs('build/sphinx')
|
||||
except FileExistsError:
|
||||
pass
|
||||
try:
|
||||
os.makedirs('build/doxygen')
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
subprocess.call('cd ..; doxygen docs/Doxyfile', shell=True)
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'libicsneo'
|
||||
copyright = '2018-2020, Intrepid Control Systems, Inc.'
|
||||
author = 'Intrepid Control Systems, Inc.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@ @BUILD_METADATA@'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
html_theme_options = {
|
||||
|
||||
}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# This is required for the alabaster theme
|
||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||
html_sidebars = {
|
||||
'**': [
|
||||
'relations.html', # needs 'show_related': True theme option to display
|
||||
'searchbox.html',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'libicsneodoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'libicsneo.tex', 'libicsneo Documentation',
|
||||
'Intrepid Control Systems, Inc.', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'libicsneo', 'libicsneo Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'libicsneo', 'libicsneo Documentation',
|
||||
author, 'libicsneo', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
********************
|
||||
**C API** (icsneoc)
|
||||
********************
|
||||
|
||||
.. Usage
|
||||
.. ======
|
||||
|
||||
.. Finding Devices
|
||||
.. ~~~~~~~~~~~~~~~~
|
||||
.. Finding a device is simple
|
||||
|
||||
.. Connecting to Devices
|
||||
.. ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Reference
|
||||
==========
|
||||
|
||||
Typedefs
|
||||
~~~~~~~~~
|
||||
.. doxygentypedef:: devicehandle_t
|
||||
.. doxygentypedef:: neodevice_handle_t
|
||||
.. doxygentypedef:: devicetype_t
|
||||
|
||||
Structures
|
||||
~~~~~~~~~~~
|
||||
.. doxygenstruct:: neoversion_t
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. doxygenstruct:: neodevice_t
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. doxygenstruct:: neomessage_t
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. doxygenstruct:: neomessage_can_t
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
Functions
|
||||
~~~~~~~~~~
|
||||
.. doxygenfile:: icsneoc.h
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
=====
|
||||
C API
|
||||
=====
|
||||
|
||||
.. doxygenfile:: icsneoc.h
|
||||
:project: icsneoc
|
||||
.. doxygenfile:: device/neodevice.h
|
||||
:project: icsneoc
|
||||
.. doxygenfile:: communication/message/neomessage.h
|
||||
:project: icsneoc
|
||||
.. doxygenfile:: api/version.h
|
||||
:project: icsneoc
|
||||
.. doxygenfile:: api/event.h
|
||||
:project: icsneoc
|
||||
@@ -0,0 +1,43 @@
|
||||
==========
|
||||
C Examples
|
||||
==========
|
||||
|
||||
A variety of examples can be found within ``examples/c``, see below for an
|
||||
example that uses the polling API to receive CAN frames.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
size_t deviceCount = 10; // Pre-set to the size of your buffer before the icsneo_findAllDevices() call
|
||||
neodevice_t devices[10];
|
||||
icsneo_findAllDevices(devices, &deviceCount);
|
||||
printf("We found %ull devices\n", deviceCount);
|
||||
for(size_t i = 0; i < deviceCount; i++) {
|
||||
neodevice_t* myDevice = &devices[i];
|
||||
char desc[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION];
|
||||
size_t sz = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
|
||||
icsneo_describeDevice(myDevice, desc, &sz);
|
||||
printf("Found %s\n", desc); // "Found neoVI FIRE 2 CY2345"
|
||||
}
|
||||
neodevice_t* myDevice = &devices[0];
|
||||
if(!icsneo_openDevice(myDevice)) {
|
||||
neoevent_t error;
|
||||
if(icsneo_getLastError(&error))
|
||||
printf("Error! %s\n", error.description);
|
||||
}
|
||||
icsneo_goOnline(myDevice); // Start receiving messages
|
||||
icsneo_enableMessagePolling(myDevice); // Allow the use of icsneo_getMessages() later
|
||||
sleep(5);
|
||||
neomessage_t messages[50];
|
||||
size_t messageCount = 50;
|
||||
icsneo_getMessages(myDevice, messages, &messageCount, 0 /* non-blocking */);
|
||||
printf("We got %ull messages!\n", messageCount);
|
||||
for(size_t i = 0; i < messageCount; i++) {
|
||||
if(messages[i].type == ICSNEO_NETWORK_TYPE_CAN) {
|
||||
// A message of type CAN should be interperated a neomessage_can_t, so we can cast safely
|
||||
neomessage_can_t* canmsg = (neomessage_can_t*)&messages[i];
|
||||
// canmsg->arbid is valid here
|
||||
// canmsg->data is an uint8_t*, you can check canmsg->length for the length of the payload
|
||||
// canmsg->timestamp is the time recorded by the hardware in nanoseconds since (1/1/2007 12:00:00 GMT)
|
||||
}
|
||||
}
|
||||
icsneo_closeDevice(myDevice);
|
||||
@@ -0,0 +1,9 @@
|
||||
icsneoc
|
||||
=======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
installation
|
||||
examples
|
||||
api
|
||||
@@ -0,0 +1,7 @@
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
The installation steps for the C API are the same as the C++ API as the C API is
|
||||
a wrapper for the C++ library. The ``LIBICSNEO_BUILD_ICSNEOC`` CMake option is
|
||||
default ``ON`` but note that the C API depends on this flag to build.
|
||||
@@ -1,31 +0,0 @@
|
||||
************************
|
||||
**C++ API** (icsneocpp)
|
||||
************************
|
||||
|
||||
.. Usage
|
||||
.. ======
|
||||
|
||||
.. Finding Devices
|
||||
.. ~~~~~~~~~~~~~~~~
|
||||
.. Finding a device is simple
|
||||
|
||||
.. Connecting to Devices
|
||||
.. ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Reference
|
||||
==========
|
||||
|
||||
.. Classes
|
||||
.. ~~~~~~~~
|
||||
|
||||
.. Structures
|
||||
.. ~~~~~~~~~~~
|
||||
|
||||
.. Functions
|
||||
.. ~~~~~~~~~~
|
||||
|
||||
.. doxygennamespace:: icsneo
|
||||
|
||||
.. doxygenclass:: icsneo::Device
|
||||
:members:
|
||||
:undoc-members:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
=======
|
||||
C++ API
|
||||
=======
|
||||
|
||||
.. doxygennamespace:: icsneo
|
||||
:members:
|
||||
:undoc-members:
|
||||
:content-only:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user