mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-24 09:49:47 +02:00
Compare commits
43
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 |
@@ -14,3 +14,4 @@ third-party/concurrentqueue/tests
|
||||
*.orig
|
||||
examples/csharp/bin
|
||||
examples/csharp/obj
|
||||
test/system
|
||||
|
||||
+239
-29
@@ -3,7 +3,9 @@ variables:
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- unit_test
|
||||
- hardware_test
|
||||
- deploy
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Windows
|
||||
@@ -21,10 +23,10 @@ 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:
|
||||
@@ -45,10 +47,10 @@ 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:
|
||||
@@ -66,7 +68,7 @@ test windows/x86:
|
||||
script:
|
||||
- apt update -y
|
||||
- apt upgrade -y
|
||||
- apt install -y g++ ninja-build cmake libusb-1.0-0-dev libpcap-dev
|
||||
- apt install -y g++ ninja-build cmake libusb-1.0-0-dev libpcap-dev git
|
||||
- sh ci/build-posix.sh
|
||||
artifacts:
|
||||
when: always
|
||||
@@ -77,12 +79,12 @@ test windows/x86:
|
||||
- linux-build
|
||||
|
||||
.test_linux_ubuntu_gcc: &test_linux_ubuntu_gcc
|
||||
stage: test
|
||||
stage: unit_test
|
||||
script:
|
||||
- apt update -y
|
||||
- apt upgrade -y
|
||||
- apt install -y libusb-1.0-0-dev libpcap-dev
|
||||
- build/libicsneo-tests
|
||||
- build/libicsneo-unit-tests
|
||||
tags:
|
||||
- linux-build
|
||||
timeout: 5m
|
||||
@@ -92,7 +94,7 @@ test windows/x86:
|
||||
script:
|
||||
- apt update -y
|
||||
- apt upgrade -y
|
||||
- apt install -y clang lld ninja-build cmake libusb-1.0-0-dev libpcap-dev
|
||||
- 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
|
||||
@@ -103,12 +105,12 @@ test windows/x86:
|
||||
- linux-build
|
||||
|
||||
.test_linux_ubuntu_clang: &test_linux_ubuntu_clang
|
||||
stage: test
|
||||
stage: unit_test
|
||||
script:
|
||||
- apt update -y
|
||||
- apt upgrade -y
|
||||
- apt install -y libusb-1.0-0-dev libpcap-dev
|
||||
- build/libicsneo-tests
|
||||
- build/libicsneo-unit-tests
|
||||
tags:
|
||||
- linux-build
|
||||
timeout: 5m
|
||||
@@ -117,7 +119,7 @@ build linux/ubuntu/2004/amd64/gcc:
|
||||
<<: *build_linux_ubuntu_gcc
|
||||
image: ubuntu:20.04
|
||||
|
||||
test linux/ubuntu/2004/amd64/gcc:
|
||||
unit_test linux/ubuntu/2004/amd64/gcc:
|
||||
<<: *test_linux_ubuntu_gcc
|
||||
image: ubuntu:20.04
|
||||
dependencies:
|
||||
@@ -129,7 +131,7 @@ build linux/ubuntu/2004/amd64/clang:
|
||||
<<: *build_linux_ubuntu_clang
|
||||
image: ubuntu:20.04
|
||||
|
||||
test linux/ubuntu/2004/amd64/clang:
|
||||
unit_test linux/ubuntu/2004/amd64/clang:
|
||||
<<: *test_linux_ubuntu_clang
|
||||
image: ubuntu:20.04
|
||||
dependencies:
|
||||
@@ -141,7 +143,7 @@ build linux/ubuntu/2204/amd64/gcc:
|
||||
<<: *build_linux_ubuntu_gcc
|
||||
image: ubuntu:22.04
|
||||
|
||||
test linux/ubuntu/2204/amd64/gcc:
|
||||
unit_test linux/ubuntu/2204/amd64/gcc:
|
||||
<<: *test_linux_ubuntu_gcc
|
||||
image: ubuntu:22.04
|
||||
dependencies:
|
||||
@@ -153,7 +155,7 @@ build linux/ubuntu/2204/amd64/clang:
|
||||
<<: *build_linux_ubuntu_clang
|
||||
image: ubuntu:22.04
|
||||
|
||||
test linux/ubuntu/2204/amd64/clang:
|
||||
unit_test linux/ubuntu/2204/amd64/clang:
|
||||
<<: *test_linux_ubuntu_clang
|
||||
image: ubuntu:22.04
|
||||
dependencies:
|
||||
@@ -174,7 +176,7 @@ test linux/ubuntu/2204/amd64/clang:
|
||||
- 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
|
||||
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel git
|
||||
- sh ci/build-posix.sh
|
||||
artifacts:
|
||||
when: always
|
||||
@@ -185,7 +187,7 @@ test linux/ubuntu/2204/amd64/clang:
|
||||
- linux-build
|
||||
|
||||
.test_linux_fedora_gcc: &test_linux_fedora_gcc
|
||||
stage: test
|
||||
stage: unit_test
|
||||
cache:
|
||||
paths:
|
||||
- /var/cache/dnf
|
||||
@@ -194,7 +196,7 @@ test linux/ubuntu/2204/amd64/clang:
|
||||
- echo fastestmirror=True >>/etc/dnf/dnf.conf
|
||||
- dnf upgrade -y
|
||||
- dnf install -y libpcap-devel libusb1-devel
|
||||
- build/libicsneo-tests
|
||||
- build/libicsneo-unit-tests
|
||||
tags:
|
||||
- linux-build
|
||||
timeout: 5m
|
||||
@@ -208,7 +210,7 @@ test linux/ubuntu/2204/amd64/clang:
|
||||
- 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
|
||||
- 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
|
||||
@@ -219,7 +221,7 @@ test linux/ubuntu/2204/amd64/clang:
|
||||
- linux-build
|
||||
|
||||
.test_linux_fedora_clang: &test_linux_fedora_clang
|
||||
stage: test
|
||||
stage: unit_test
|
||||
cache:
|
||||
paths:
|
||||
- /var/cache/dnf
|
||||
@@ -228,7 +230,7 @@ test linux/ubuntu/2204/amd64/clang:
|
||||
- echo fastestmirror=True >>/etc/dnf/dnf.conf
|
||||
- dnf upgrade -y
|
||||
- dnf install -y libpcap-devel libusb1-devel
|
||||
- build/libicsneo-tests
|
||||
- build/libicsneo-unit-tests
|
||||
tags:
|
||||
- linux-build
|
||||
timeout: 5m
|
||||
@@ -237,7 +239,7 @@ build linux/fedora/37/amd64/gcc:
|
||||
<<: *build_linux_fedora_gcc
|
||||
image: fedora:37
|
||||
|
||||
test linux/fedora/37/amd64/gcc:
|
||||
unit_test linux/fedora/37/amd64/gcc:
|
||||
<<: *test_linux_fedora_gcc
|
||||
image: fedora:37
|
||||
dependencies:
|
||||
@@ -249,7 +251,7 @@ build linux/fedora/37/amd64/clang:
|
||||
<<: *build_linux_fedora_clang
|
||||
image: fedora:37
|
||||
|
||||
test linux/fedora/37/amd64/clang:
|
||||
unit_test linux/fedora/37/amd64/clang:
|
||||
<<: *test_linux_fedora_clang
|
||||
image: fedora:37
|
||||
dependencies:
|
||||
@@ -261,7 +263,7 @@ build linux/fedora/38/amd64/gcc:
|
||||
<<: *build_linux_fedora_gcc
|
||||
image: fedora:38
|
||||
|
||||
test linux/fedora/38/amd64/gcc:
|
||||
unit_test linux/fedora/38/amd64/gcc:
|
||||
<<: *test_linux_fedora_gcc
|
||||
image: fedora:38
|
||||
dependencies:
|
||||
@@ -273,7 +275,7 @@ build linux/fedora/38/amd64/clang:
|
||||
<<: *build_linux_fedora_clang
|
||||
image: fedora:38
|
||||
|
||||
test linux/fedora/38/amd64/clang:
|
||||
unit_test linux/fedora/38/amd64/clang:
|
||||
<<: *test_linux_fedora_clang
|
||||
image: fedora:38
|
||||
dependencies:
|
||||
@@ -285,7 +287,7 @@ build linux/fedora/39/amd64/gcc:
|
||||
<<: *build_linux_fedora_gcc
|
||||
image: fedora:39
|
||||
|
||||
test linux/fedora/39/amd64/gcc:
|
||||
unit_test linux/fedora/39/amd64/gcc:
|
||||
<<: *test_linux_fedora_gcc
|
||||
image: fedora:39
|
||||
dependencies:
|
||||
@@ -297,10 +299,218 @@ build linux/fedora/39/amd64/clang:
|
||||
<<: *build_linux_fedora_clang
|
||||
image: fedora:39
|
||||
|
||||
test linux/fedora/39/amd64/clang:
|
||||
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
|
||||
+79
-31
@@ -6,12 +6,14 @@ if(POLICY CMP0135)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
endif()
|
||||
|
||||
option(LIBICSNEO_BUILD_TESTS "Build all tests." OFF)
|
||||
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")
|
||||
|
||||
# Device Drivers
|
||||
@@ -25,6 +27,8 @@ option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX"
|
||||
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()
|
||||
@@ -170,27 +174,27 @@ if(LIBICSNEO_ENABLE_FTD3XX)
|
||||
if(NOT FTD3XX_ROOT) # allow system override
|
||||
include(FetchContent)
|
||||
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-x64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ee1289cdd5023de67275aaa1712e8e32e73e825a8392efb0a76f161e357fbdc9")
|
||||
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/23.28.0/libftd3xx-1.3.0.4-win-i686.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=bcd31f5c3cb39ecb9b09db5ce722bd79de6e0f813130e7b8f2c86879a60d8ff0")
|
||||
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/23.28.0/libftd3xx-1.0.14-macos-universal2.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=74c0d35f04242d0841532c6325eb2932b8f627e3c395382a15d9f39974d27a90")
|
||||
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/23.28.0/libftd3xx-1.0.14-linux-x64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=a9dc7eb6948c8977fbd79f6700bec6f882d3da5667aea8f2175b8d1f6f08e456")
|
||||
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/23.28.0/libftd3xx-1.0.14-linux-aarch64.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=da4b90ea1cbb905874cd159ad2ab8c1bdde65cc22b3aa55bf2b5fd85ca6efd22")
|
||||
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/23.28.0/libftd3xx-1.0.14-linux-armhf.zip")
|
||||
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=d813008117422cae958f7c71a065cdac0d31dca3b24809d3ab5e13604a9c3fb1")
|
||||
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()
|
||||
@@ -235,11 +239,13 @@ endforeach()
|
||||
set(SRC_FILES
|
||||
communication/message/flexray/control/flexraycontrolmessage.cpp
|
||||
communication/message/callback/streamoutput/a2bwavoutput.cpp
|
||||
communication/message/callback/streamoutput/a2bdecoder.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
|
||||
@@ -257,6 +263,7 @@ set(SRC_FILES
|
||||
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
|
||||
@@ -265,6 +272,7 @@ set(SRC_FILES
|
||||
communication/communication.cpp
|
||||
communication/driver.cpp
|
||||
communication/livedata.cpp
|
||||
communication/ringbuffer.cpp
|
||||
device/extensions/flexray/extension.cpp
|
||||
device/extensions/flexray/controller.cpp
|
||||
device/idevicesettings.cpp
|
||||
@@ -378,7 +386,7 @@ endif()
|
||||
if(LIBICSNEO_ENABLE_TCP)
|
||||
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_TCP)
|
||||
if(WIN32)
|
||||
target_link_libraries(icsneocpp PRIVATE ws2_32)
|
||||
target_link_libraries(icsneocpp PRIVATE ws2_32 iphlpapi)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -448,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)
|
||||
@@ -468,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()
|
||||
@@ -482,27 +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/i2cencoderdecodertest.cpp
|
||||
test/linencoderdecodertest.cpp
|
||||
test/a2bencoderdecodertest.cpp
|
||||
test/mdioencoderdecodertest.cpp
|
||||
test/livedataencoderdecodertest.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})
|
||||
|
||||
-50
@@ -1,50 +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
|
||||
- RADComet
|
||||
- CAN works
|
||||
- CAN FD 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
|
||||
- RADMoon3
|
||||
@@ -1,176 +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);
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
To enable debug printing set the `LIBICSNEO_PRINT_EVENTS` environmental variable to the desired `APIEvent::Severity` level, all `Event`s greater than or equal to that level will be printed to stderr. For example, to print all warnings and errors: `LIBICSNEO_PRINT_EVENTS=32`.
|
||||
|
||||
## Building from Source
|
||||
### FTD3XX
|
||||
Some devices require FTD3XX for USB communication so the [FTDI D3XX library](https://ftdichip.com/drivers/d3xx-drivers/) will be automatically downloaded and included. If you would like to use a system copy of D3XX instead you can set `FTD3XX_ROOT` to the path containing `f3d3xx.h` (`-DFTD3XX_ROOT=<path to directory containing ftd3xx.h>`).
|
||||
### Windows
|
||||
|
||||
- Open a terminal and install the following:
|
||||
```
|
||||
winget install Microsoft.VisualStudio.2022.Community
|
||||
winget install Kitware.CMake
|
||||
winget install Git.Git
|
||||
winget install Ninja-build.Ninja
|
||||
```
|
||||
- Reboot so cmake is in the system path
|
||||
- Open a developer Powershell for VS2022 and run the following:
|
||||
```
|
||||
git clone https://github.com/intrepidcs/libicsneo
|
||||
cd libicsneo
|
||||
cmake -S . -B build -G "Ninja"
|
||||
cmake --build build
|
||||
# All dlls and libs are now inside the build directory
|
||||
```
|
||||
|
||||
|
||||
Building will require MSVC 2017 version 15.7 or newer and CMake to be installed.
|
||||
### macOS
|
||||
Getting the dependencies is easiest with the Homebrew package manager. You will also need XCode installed. You can then install CMake, an up-to-date version of GCC or Clang, and `libusb-1.0`.
|
||||
### Linux
|
||||
#### General Dependencies
|
||||
- CMake 3.12 or above
|
||||
- GCC 7 or above
|
||||
- `libusb-1.0-0-dev`
|
||||
- `libpcap0.8-dev`
|
||||
- `build-essential` is recommended
|
||||
|
||||
#### Fedora
|
||||
```
|
||||
dnf install git @development-tools gcc-c++ libpcap-devel libusb1-devel cmake
|
||||
```
|
||||
#### Debian/Ubuntu
|
||||
```
|
||||
apt install git build-essential libpcap0.8-dev libusb-1.0-0-dev cmake
|
||||
```
|
||||
|
||||
#### Building
|
||||
```
|
||||
git clone https://github.com/intrepidcs/libicsneo
|
||||
cd libicsneo
|
||||
cmake -S . -B build
|
||||
cmake --build build
|
||||
# Optional: Install globally:
|
||||
cp *.so /usr/local/lib/
|
||||
```
|
||||
|
||||
#### udev
|
||||
If you'd like to be able to run programs that use this library without being root, consider using the included udev rules:
|
||||
|
||||
```
|
||||
cp 99-intrepidcs.rules /etc/udev/rules.d/
|
||||
udevadm control --reload-rules && udevadm trigger
|
||||
```
|
||||
libicsneo is licensed as BSD-3 with an extra clause, see [LICENSE](LICENSE)
|
||||
for more details.
|
||||
|
||||
+10
-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;
|
||||
}
|
||||
|
||||
@@ -72,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!";
|
||||
@@ -218,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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
+103
-154
@@ -14,7 +14,6 @@
|
||||
#include "icsneo/communication/network.h"
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
|
||||
@@ -27,6 +26,7 @@ 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},
|
||||
@@ -52,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)
|
||||
{
|
||||
@@ -318,118 +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
|
||||
// return the size only if devs is NULL.
|
||||
if (!devs)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
catch (const std::out_of_range&)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
*hObject = device;
|
||||
if (!icsneo_openDevice(device))
|
||||
return false;
|
||||
|
||||
if (icsneo_isOnlineSupported(device)) {
|
||||
if (!icsneo_setPollingMessageLimit(device, 20000))
|
||||
return false;
|
||||
|
||||
if (!icsneo_enableMessagePolling(device))
|
||||
return false;
|
||||
|
||||
if (!icsneo_goOnline(device))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
),
|
||||
neoDevices.end()
|
||||
);
|
||||
}
|
||||
// Create a NeoDeviceEx From a neodevice_t
|
||||
auto _createNeoDeviceExFrom = [](const neodevice_t* neoDevice) -> NeoDeviceEx {
|
||||
NeoDeviceEx nde = {};
|
||||
nde.neoDevice.DeviceType = neoDevice->type;
|
||||
nde.neoDevice.SerialNumber = icsneo_serialStringToNum(neoDevice->serial);
|
||||
nde.neoDevice.NumberOfClients = 0;
|
||||
nde.neoDevice.MaxAllowedClients = 1;
|
||||
static_assert(sizeof(neodevice_handle_t) == sizeof(nde.neoDevice.Handle),
|
||||
"neodevice_handle_t size must be sizeof(int) for compatibility reasons");
|
||||
nde.neoDevice.Handle = neoDevice->handle;
|
||||
return nde;
|
||||
};
|
||||
|
||||
// Create the NeoDeviceEx from the neodevice_t
|
||||
auto i = 0;
|
||||
for (const auto& neoDevice : neoDevices) {
|
||||
// Fill the look up table
|
||||
neodevices[uint64_t(neoDevice.handle) << 32 | icsneo_serialStringToNum(neoDevice.serial)] = neoDevice;
|
||||
// Create the NeoDeviceEx
|
||||
devs[i] = _createNeoDeviceExFrom(&neoDevice);
|
||||
NeoDeviceEx* nde = &devs[i];
|
||||
++i;
|
||||
// Lookup the open NeoDeviceEx devices and match the NumberOfClients value if available.
|
||||
for (auto& [neo_device, open_nde]: openneodevices) {
|
||||
// SerialNumber should always be unique so lets compare against that.
|
||||
if (nde->neoDevice.SerialNumber == open_nde->neoDevice.SerialNumber) {
|
||||
nde->neoDevice.NumberOfClients = open_nde->neoDevice.NumberOfClients;
|
||||
}
|
||||
}
|
||||
}
|
||||
*devCount = (int)neoDevices.size();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LegacyDLLExport icsneoOpenDevice(
|
||||
@@ -454,21 +403,35 @@ int LegacyDLLExport icsneoOpenDevice(
|
||||
return false;
|
||||
}
|
||||
|
||||
*hObject = device;
|
||||
if(!icsneo_openDevice(device))
|
||||
return false;
|
||||
|
||||
if (icsneo_isOnlineSupported(device)) {
|
||||
if (!icsneo_setPollingMessageLimit(device, 20000))
|
||||
return false;
|
||||
|
||||
if (!icsneo_enableMessagePolling(device))
|
||||
return false;
|
||||
|
||||
if (!icsneo_goOnline(device))
|
||||
if (pNeoDeviceEx->neoDevice.NumberOfClients >= pNeoDeviceEx->neoDevice.MaxAllowedClients) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*hObject = device;
|
||||
if(!icsneo_openDevice(device)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (icsneo_isOnlineSupported(device)) {
|
||||
if (!icsneo_setPollingMessageLimit(device, 20000)) {
|
||||
icsneo_closeDevice(device);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!icsneo_enableMessagePolling(device)) {
|
||||
icsneo_closeDevice(device);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!icsneo_goOnline(device)) {
|
||||
icsneo_closeDevice(device);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pNeoDeviceEx->neoDevice.NumberOfClients = 1;
|
||||
// Add the open NeoDevice to the container so we can decrement NumberOfClients on close
|
||||
openneodevices[device] = pNeoDeviceEx;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -476,8 +439,14 @@ 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);
|
||||
}
|
||||
|
||||
@@ -1013,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)
|
||||
{
|
||||
@@ -1040,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)
|
||||
{
|
||||
@@ -1137,6 +1080,12 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
|
||||
case NEODEVICE_RED2:
|
||||
*pDeviceSettingsType = DeviceRed2SettingsType;
|
||||
break;
|
||||
case NEODEVICE_FIRE3:
|
||||
*pDeviceSettingsType = DeviceFire3SettingsType;
|
||||
break;
|
||||
case NEODEVICE_FIRE3_FLEXRAY:
|
||||
*pDeviceSettingsType = DeviceFire3FlexraySettingsType;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_EXAMPLES=ON \
|
||||
-DLIBICSNEO_BUILD_TESTS=ON -DLIBICSNEO_ENABLE_TCP=ON || exit 1
|
||||
-DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_BUILD_SYSTEM_TESTS=ON -DLIBICSNEO_ENABLE_TCP=OFF || exit 1
|
||||
|
||||
cmake --build build || exit 1
|
||||
|
||||
|
||||
@@ -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 -DLIBICSNEO_ENABLE_TCP=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%
|
||||
|
||||
@@ -20,8 +20,6 @@ using namespace icsneo;
|
||||
int Communication::messageCallbackIDCounter = 1;
|
||||
|
||||
Communication::~Communication() {
|
||||
if(redirectingRead)
|
||||
clearRedirectRead();
|
||||
if(isOpen())
|
||||
close();
|
||||
}
|
||||
@@ -44,6 +42,11 @@ void Communication::spawnThreads() {
|
||||
|
||||
void Communication::joinThreads() {
|
||||
closing = true;
|
||||
|
||||
if(pauseReadTask) {
|
||||
resumeReads();
|
||||
}
|
||||
|
||||
if(readTaskThread.joinable())
|
||||
readTaskThread.join();
|
||||
closing = false;
|
||||
@@ -96,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]() {
|
||||
@@ -261,37 +247,49 @@ 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; });
|
||||
}
|
||||
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, std::vector<uint8_t>& 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)) {
|
||||
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))
|
||||
@@ -300,7 +298,6 @@ void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes)
|
||||
dispatchMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional< std::vector<ComponentVersion> > Communication::getComponentVersionsSync(std::chrono::milliseconds timeout) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#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"
|
||||
@@ -16,6 +17,9 @@
|
||||
#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"
|
||||
@@ -35,6 +39,8 @@
|
||||
#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;
|
||||
@@ -252,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);
|
||||
@@ -286,6 +304,9 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
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;
|
||||
@@ -295,7 +316,6 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
case Network::NetID::ExtendedData: {
|
||||
if(packet->data.size() < sizeof(ExtendedDataMessage::ExtendedDataHeader))
|
||||
break;
|
||||
|
||||
const auto& header = *reinterpret_cast<ExtendedDataMessage::ExtendedDataHeader*>(packet->data.data());
|
||||
|
||||
switch(header.subCommand) {
|
||||
@@ -307,6 +327,8 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
|
||||
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:
|
||||
@@ -357,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]);
|
||||
@@ -380,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]);
|
||||
|
||||
+31
-16
@@ -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
|
||||
|
||||
@@ -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,156 +0,0 @@
|
||||
#include "icsneo/communication/message/callback/streamoutput/a2bdecoder.h"
|
||||
#include <chrono>
|
||||
#include "icsneo/icsneocpp.h"
|
||||
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
static constexpr uint8_t maxChannel = 255;
|
||||
|
||||
|
||||
size_t A2BAudioChannelMap::getChannelIndex(Channel channel, A2BMessage::A2BDirection dir) const {
|
||||
size_t output = (size_t)channel;
|
||||
|
||||
if(dir == A2BMessage::A2BDirection::Upstream) {
|
||||
output++;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
A2BAudioChannelMap::A2BAudioChannelMap(uint8_t tdm) {
|
||||
rawMap.resize(2*tdm, maxChannel);
|
||||
}
|
||||
|
||||
void A2BAudioChannelMap::set(Channel outChannel, A2BMessage::A2BDirection dir, Channel inChannel) {
|
||||
auto index = getChannelIndex(outChannel, dir);
|
||||
rawMap[index] = inChannel;
|
||||
}
|
||||
|
||||
void A2BAudioChannelMap::setAll(Channel inChannel) {
|
||||
std::fill(rawMap.begin(), rawMap.end(), inChannel);
|
||||
}
|
||||
|
||||
Channel A2BAudioChannelMap::get(Channel outChannel, A2BMessage::A2BDirection dir) const {
|
||||
auto index = getChannelIndex(outChannel, dir);
|
||||
|
||||
return rawMap[index];
|
||||
}
|
||||
|
||||
size_t A2BAudioChannelMap::A2BAudioChannelMap::size() const {
|
||||
return rawMap.size();
|
||||
}
|
||||
|
||||
uint8_t A2BAudioChannelMap::getTDM() const {
|
||||
return (uint8_t)(rawMap.size() / 2);
|
||||
}
|
||||
|
||||
Channel& A2BAudioChannelMap::operator[](size_t idx) {
|
||||
return rawMap[idx];
|
||||
}
|
||||
|
||||
A2BAudioChannelMap::operator const std::vector<Channel>&() const {
|
||||
return rawMap;
|
||||
}
|
||||
|
||||
A2BDecoder::A2BDecoder(
|
||||
std::unique_ptr<std::istream>&& streamOut,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
) : channelSize16(chSize16), channelMap(chMap) {
|
||||
stream = std::move(streamOut);
|
||||
tdm = chMap.getTDM();
|
||||
initializeFromHeader();
|
||||
}
|
||||
|
||||
A2BDecoder::A2BDecoder(
|
||||
const char* filename,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
) : A2BDecoder(std::make_unique<std::ifstream>(filename, std::ios::binary), chSize16, chMap) { }
|
||||
|
||||
A2BDecoder::operator bool() const {
|
||||
return initialized && stream->good() && !stream->eof();
|
||||
}
|
||||
|
||||
void A2BDecoder::initializeFromHeader() {
|
||||
WaveFileHeader header;
|
||||
if(!stream->read((char*)&header, sizeof(header))) {
|
||||
initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Only allow 16 or 24 bit samples
|
||||
if(header.bitsPerSample != 16 && header.bitsPerSample != 24) {
|
||||
initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
audioBytesPerSample = header.bitsPerSample == 16 ? 2 : 3;
|
||||
channelsInWave = (uint8_t)header.numChannels;
|
||||
|
||||
size_t bytesPerSample = channelSize16 ? 2 : 4;
|
||||
size_t frameSize = 2*tdm*bytesPerSample;
|
||||
size_t frameSizeWave = (size_t)(channelsInWave) * (size_t)(audioBytesPerSample);
|
||||
|
||||
frame.resize(frameSize, 0);
|
||||
frameWave.resize(frameSizeWave, 0);
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<A2BMessage> A2BDecoder::decode() {
|
||||
if(!*(this)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto a2bMessagePtr = std::make_shared<icsneo::A2BMessage>(
|
||||
tdm,
|
||||
channelSize16,
|
||||
2048
|
||||
);
|
||||
|
||||
A2BMessage& a2bMessage = *a2bMessagePtr.get();
|
||||
|
||||
a2bMessage.setMonitorBit(false); // Probably not necessary
|
||||
a2bMessage.setTxMsgBit(true);
|
||||
|
||||
a2bMessage.network = Network(Network::NetID::A2B2);
|
||||
|
||||
for(uint32_t frameIndex = 0; frameIndex < a2bMessage.getNumFrames(); frameIndex++) {
|
||||
if(!stream->read((char*)frameWave.data(), frameWave.size())) {
|
||||
break;
|
||||
}
|
||||
|
||||
for(size_t icsChannel = 0; icsChannel < channelMap.size(); icsChannel++) {
|
||||
|
||||
if(channelMap[icsChannel] >= maxChannel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t wBegin = audioBytesPerSample * channelMap[icsChannel];
|
||||
A2BPCMSample sample = 0;
|
||||
uint8_t* sampBytes = (uint8_t*)&sample;
|
||||
|
||||
std::copy(frameWave.begin() + wBegin, frameWave.begin() + wBegin + audioBytesPerSample, sampBytes);
|
||||
a2bMessage[frameIndex][icsChannel] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
return a2bMessagePtr;
|
||||
}
|
||||
|
||||
bool A2BDecoder::outputAll(std::shared_ptr<Device>& device) {
|
||||
const auto& networks = device->getSupportedTXNetworks();
|
||||
|
||||
if(std::none_of(networks.begin(), networks.end(), [](const Network& net) { return net.getNetID() == Network::NetID::A2B2; })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(*this) {
|
||||
device->transmit(decode());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,15 +4,99 @@
|
||||
|
||||
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(!initialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(closed) {
|
||||
return false;
|
||||
@@ -22,28 +106,87 @@ bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& frame = std::static_pointer_cast<Frame>(message);
|
||||
const auto& frameMsg = std::dynamic_pointer_cast<Frame>(message);
|
||||
|
||||
if(frame->network.getType() != Network::Type::A2B)
|
||||
if(!frameMsg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(frameMsg->network.getType() != Network::Type::A2B)
|
||||
return false;
|
||||
|
||||
const auto& a2bmsg = std::static_pointer_cast<A2BMessage>(frame);
|
||||
const auto& a2bMsg = std::dynamic_pointer_cast<A2BMessage>(frameMsg);
|
||||
|
||||
if(firstMessageFlag) {
|
||||
writeHeader(a2bmsg);
|
||||
firstMessageFlag = false;
|
||||
if(!a2bMsg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Might need to readd this block of code later if sample alignment fix is necessary
|
||||
/*
|
||||
std::streamsize bps = (std::streamsize)a2bmsg->getBytesPerSample();
|
||||
for(size_t i=0; i<a2bmsg->getNumSamples(); i++) {
|
||||
A2BPCMSample samp = *(a2bmsg->getSample(i));
|
||||
write((void*)&samp, bps);
|
||||
}
|
||||
*/
|
||||
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();
|
||||
|
||||
write((void*)a2bmsg->getAudioBuffer(), a2bmsg->getAudioBufferSize());
|
||||
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;
|
||||
}
|
||||
@@ -53,17 +196,39 @@ void A2BWAVOutput::close() const {
|
||||
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::writeCurrentBuffer() const {
|
||||
|
||||
if(!stream->write(reinterpret_cast<const char*>(wavBuffer.data()), wavBufferOffset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wavBufferOffset = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "icsneo/communication/message/tc10statusmessage.h"
|
||||
#include "icsneo/communication/command.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
#pragma pack(push, 2)
|
||||
struct Header {
|
||||
ExtendedCommand command;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
struct Packet {
|
||||
Header header;
|
||||
TC10WakeStatus wakeStatus;
|
||||
TC10SleepStatus sleepStatus;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
std::shared_ptr<TC10StatusMessage> TC10StatusMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
if(bytestream.size() < sizeof(Packet)) {
|
||||
return nullptr;
|
||||
}
|
||||
const Packet* packet = (Packet*)bytestream.data();
|
||||
if (packet->header.command != ExtendedCommand::GetTC10Status) {
|
||||
return nullptr;
|
||||
}
|
||||
if (packet->header.length < sizeof(Packet) - sizeof(Header)) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_shared<TC10StatusMessage>(packet->wakeStatus, packet->sleepStatus);
|
||||
}
|
||||
@@ -178,7 +178,10 @@ void MultiChannelCommunication::vnetReadTask(size_t vnetIndex) {
|
||||
if(closing)
|
||||
break;
|
||||
|
||||
handleInput(*vnetPacketizer, payloadBytes);
|
||||
auto& ringBuffer = driver->getReadBuffer();
|
||||
ringBuffer.write(payloadBytes);
|
||||
|
||||
handleInput(*vnetPacketizer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,41 +17,38 @@ std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<ui
|
||||
|
||||
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - sizeof(HardwareA2BPacket); // First 28 bytes are message header.
|
||||
|
||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(
|
||||
(uint8_t)data->header.channelNum,
|
||||
data->header.channelSize16,
|
||||
totalPackedLength
|
||||
);
|
||||
if(totalPackedLength == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
msg->setMonitorBit(data->header.monitor);
|
||||
msg->setTxMsgBit(data->header.txmsg);
|
||||
msg->setErrIndicatorBit(data->header.errIndicator);
|
||||
msg->setSyncFrameBit(data->header.syncFrame);
|
||||
msg->setRFU2(data->header.rfu2);
|
||||
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>();
|
||||
|
||||
msg->numChannels = data->header.channelNum;
|
||||
msg->channelSize16 = data->header.channelSize16;
|
||||
msg->monitor = data->header.monitor;
|
||||
msg->txmsg = data->header.txmsg;
|
||||
msg->errIndicator = data->header.errIndicator;
|
||||
msg->syncFrame = data->header.syncFrame;
|
||||
msg->rfu2 = data->header.rfu2;
|
||||
msg->timestamp = data->timestamp.TS;
|
||||
msg->setAudioBuffer(bytestream.begin() + sizeof(HardwareA2BPacket), bytestream.end());
|
||||
|
||||
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 sampleBytes = message.getAudioBufferSize();
|
||||
size_t totalSize = a2btxMessageHeaderSize + sampleBytes;
|
||||
size_t audioBufferSize = message.data.size();
|
||||
size_t totalSize = a2btxMessageHeaderSize + audioBufferSize;
|
||||
|
||||
bytestream.resize(totalSize, 0);
|
||||
uint32_t offset = 0;
|
||||
|
||||
bytestream[offset++] = 0;
|
||||
bytestream[offset++] = 0;
|
||||
bytestream[offset++] = (uint8_t)(sampleBytes & 0xFF);
|
||||
bytestream[offset++] = (uint8_t)((sampleBytes >> 8) & 0xFF);
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -24,11 +24,9 @@ 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.Copy(inputBytes);
|
||||
|
||||
while(haveEnoughData) {
|
||||
switch(state) {
|
||||
case ReadState::SearchForHeader:
|
||||
@@ -152,14 +150,14 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
|
||||
if(packetLength > 0)
|
||||
packet.data.resize(packetLength - headerSize);
|
||||
|
||||
bytes.CopyTo(packet.data.data(), currentIndex, (packetLength - 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));
|
||||
bytes.Erase_front(packetLength);
|
||||
bytes.pop(packetLength);
|
||||
|
||||
if(packet.network == Network::NetID::DiskData && (packetLength - headerSize) % 2 == 0) {
|
||||
bytes.pop_front();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+226
-13
@@ -220,10 +220,13 @@ bool Device::open(OpenFlags flags, OpenStatusHandler handler) {
|
||||
|
||||
// Get component versions *after* the extension "onDeviceOpen" hooks (e.g. device reflashes)
|
||||
|
||||
if (auto compVersions = com->getComponentVersionsSync())
|
||||
if(supportsComponentVersions()) {
|
||||
if(auto compVersions = com->getComponentVersionsSync())
|
||||
componentVersions = std::move(*compVersions);
|
||||
else
|
||||
// It's possible the device is on older firmware so don't return false here
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::EventWarning);
|
||||
}
|
||||
|
||||
if(!settings->disabled) {
|
||||
// Since we will not fail the open if a settings read fails,
|
||||
@@ -356,6 +359,10 @@ bool Device::close() {
|
||||
|
||||
stopHeartbeatThread = true;
|
||||
|
||||
if (isMessagePollingEnabled()) {
|
||||
disableMessagePolling();
|
||||
}
|
||||
|
||||
if(isOnline())
|
||||
goOffline();
|
||||
|
||||
@@ -486,7 +493,7 @@ bool Device::startScript(Disk::MemoryType memType)
|
||||
|
||||
const auto response = com->waitForMessageSync([&]() {
|
||||
return com->sendCommand(Command::LoadCoreMini, location);
|
||||
}, filter);
|
||||
}, filter, std::chrono::milliseconds(2000));
|
||||
|
||||
if(!response) {
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
@@ -518,14 +525,14 @@ bool Device::stopScript()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::uploadCoremini(std::unique_ptr<std::istream>&& stream, Disk::MemoryType memType) {
|
||||
bool Device::uploadCoremini(std::istream& stream, Disk::MemoryType memType) {
|
||||
|
||||
if(!stream || stream->bad()) {
|
||||
if(stream.bad()) {
|
||||
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char> bin(std::istreambuf_iterator<char>(*stream), {}); // Read the whole stream
|
||||
std::vector<char> bin(std::istreambuf_iterator<char>(stream), {}); // Read the whole stream
|
||||
|
||||
if(bin.size() < 4) {
|
||||
report(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error);
|
||||
@@ -592,6 +599,10 @@ bool Device::uploadCoremini(std::unique_ptr<std::istream>&& stream, Disk::Memory
|
||||
bool Device::eraseScriptMemory(Disk::MemoryType memType, uint64_t amount) {
|
||||
static std::shared_ptr<MessageFilter> NeoEraseDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
|
||||
|
||||
if(!supportsEraseMemory()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto startAddress = getCoreminiStartAddress(memType);
|
||||
if(!startAddress) {
|
||||
return false;
|
||||
@@ -608,7 +619,7 @@ bool Device::eraseScriptMemory(Disk::MemoryType memType, uint64_t amount) {
|
||||
|
||||
arguments[0] = static_cast<uint8_t>(memType);
|
||||
*reinterpret_cast<uint32_t*>(&arguments[1]) = static_cast<uint32_t>(*startAddress / 512);
|
||||
*reinterpret_cast<uint32_t*>(&arguments[5])= numWords;
|
||||
*reinterpret_cast<uint32_t*>(&arguments[5]) = numWords;
|
||||
|
||||
auto msg = com->waitForMessageSync([this, &arguments] {
|
||||
return com->sendCommand(Command::NeoEraseMemory, arguments);
|
||||
@@ -645,6 +656,85 @@ bool Device::clearScript(Disk::MemoryType memType)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<CoreminiHeader> Device::readCoreminiHeader(Disk::MemoryType memType) {
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto startAddress = getCoreminiStartAddress(memType);
|
||||
if(!startAddress) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto connected = isLogicalDiskConnected();
|
||||
|
||||
if(!connected) {
|
||||
return std::nullopt; // Already added an API error
|
||||
}
|
||||
|
||||
#pragma pack(push, 2)
|
||||
struct RawCoreminiHeader {
|
||||
uint16_t fileType;
|
||||
uint16_t fileVersion;
|
||||
uint32_t storedFileSize;
|
||||
uint32_t fileChecksum;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t skipDecompression : 1;
|
||||
uint32_t encryptedMode : 1;
|
||||
uint32_t reserved : 30;
|
||||
} bits;
|
||||
uint32_t word;
|
||||
} flags;
|
||||
uint8_t fileHash[32];
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t lsb;
|
||||
uint32_t msb;
|
||||
} words;
|
||||
uint64_t time64;
|
||||
} createTime;
|
||||
uint8_t reserved[8];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
RawCoreminiHeader header = {};
|
||||
auto numRead = readLogicalDisk(*startAddress, (uint8_t*)&header, sizeof(header), std::chrono::milliseconds(2000), memType);
|
||||
|
||||
if(!numRead) {
|
||||
return std::nullopt; // Already added an API error
|
||||
}
|
||||
|
||||
if(*numRead != sizeof(header)) {
|
||||
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if(header.fileType != 0x0907) {
|
||||
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<CoreminiHeader> ret;
|
||||
ret.emplace();
|
||||
ret->coreminiVersion = header.fileVersion;
|
||||
ret->storedFileSize = header.storedFileSize;
|
||||
ret->fileChecksum = header.fileChecksum;
|
||||
ret->skipDecompression = static_cast<bool>(header.flags.bits.skipDecompression);
|
||||
ret->encryptedMode = static_cast<bool>(header.flags.bits.encryptedMode);
|
||||
std::copy(std::begin(header.fileHash), std::end(header.fileHash), ret->fileHash.begin());
|
||||
static constexpr std::chrono::seconds icsEpochDelta(1167609600);
|
||||
static constexpr uint8_t timestampResolution = 25;
|
||||
static constexpr uint16_t nsInUs = 1'000;
|
||||
ret->timestamp += icsEpochDelta + std::chrono::microseconds(header.createTime.time64 * timestampResolution / nsInUs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Device::transmit(std::shared_ptr<Frame> frame) {
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
@@ -711,6 +801,37 @@ Network Device::getNetworkByNumber(Network::Type type, size_t index) const {
|
||||
return Network::NetID::Invalid;
|
||||
}
|
||||
|
||||
std::shared_ptr<HardwareInfo> Device::getHardwareInfo(std::chrono::milliseconds timeout) {
|
||||
if(!isOpen()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!isOnline()) {
|
||||
report(APIEvent::Type::DeviceCurrentlyOffline, APIEvent::Severity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto filter = std::make_shared<MessageFilter>(Message::Type::HardwareInfo);
|
||||
|
||||
auto response = com->waitForMessageSync([this]() {
|
||||
return com->sendCommand(Command::GetHardwareInfo);
|
||||
}, filter, timeout);
|
||||
|
||||
if(!response) {
|
||||
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto hardwareInfo = std::dynamic_pointer_cast<HardwareInfo>(response);
|
||||
if(!hardwareInfo) {
|
||||
report(APIEvent::Type::UnexpectedResponse, APIEvent::Severity::Error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return hardwareInfo;
|
||||
}
|
||||
|
||||
|
||||
std::optional<uint64_t> Device::readLogicalDisk(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, Disk::MemoryType memType) {
|
||||
if(!into || timeout <= std::chrono::milliseconds(0)) {
|
||||
@@ -1709,7 +1830,7 @@ std::optional<EthPhyMessage> Device::sendEthPhyMsg(const EthPhyMessage& message,
|
||||
return std::make_optional<EthPhyMessage>(*retMsg);
|
||||
}
|
||||
|
||||
std::optional<bool> Device::SetCollectionUploaded(uint32_t collectionEntryByteAddress)
|
||||
std::optional<bool> Device::SetRootDirectoryEntryFlags(uint8_t mask, uint8_t values, uint32_t collectionEntryByteAddress)
|
||||
{
|
||||
if(!supportsWiVI())
|
||||
{
|
||||
@@ -1717,15 +1838,31 @@ std::optional<bool> Device::SetCollectionUploaded(uint32_t collectionEntryByteAd
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (mask & RootDirectoryEntryFlags::IsPrePost)
|
||||
{
|
||||
report(APIEvent::Type::RestrictedEntryFlag, APIEvent::Severity::EventWarning);
|
||||
mask &= ~RootDirectoryEntryFlags::IsPrePost;
|
||||
values &= ~RootDirectoryEntryFlags::IsPrePost;
|
||||
}
|
||||
|
||||
if (mask & RootDirectoryEntryFlags::PrePostTriggered)
|
||||
{
|
||||
report(APIEvent::Type::RestrictedEntryFlag, APIEvent::Severity::EventWarning);
|
||||
mask &= ~RootDirectoryEntryFlags::PrePostTriggered;
|
||||
values &= ~RootDirectoryEntryFlags::PrePostTriggered;
|
||||
}
|
||||
|
||||
auto timeout = std::chrono::milliseconds(2500);
|
||||
std::vector<uint8_t> args(
|
||||
{(uint8_t)(collectionEntryByteAddress & 0xFF),
|
||||
(uint8_t)((collectionEntryByteAddress >> 8) & 0xFF),
|
||||
(uint8_t)((collectionEntryByteAddress >> 16) & 0xFF),
|
||||
(uint8_t)((collectionEntryByteAddress >> 24) & 0xFF)});
|
||||
(uint8_t)((collectionEntryByteAddress >> 24) & 0xFF),
|
||||
values,
|
||||
mask});
|
||||
|
||||
std::shared_ptr<Message> response = com->waitForMessageSync(
|
||||
[this, args](){ return com->sendCommand(ExtendedCommand::SetUploadedFlag, args); },
|
||||
[this, args](){ return com->sendCommand(ExtendedCommand::SetRootFSEntryFlags, args); },
|
||||
std::make_shared<MessageFilter>(Message::Type::ExtendedResponse), timeout);
|
||||
if(!response)
|
||||
{
|
||||
@@ -1737,7 +1874,7 @@ std::optional<bool> Device::SetCollectionUploaded(uint32_t collectionEntryByteAd
|
||||
{
|
||||
// TODO fix this error
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
return std::make_optional<bool>(false);
|
||||
return false;
|
||||
}
|
||||
bool success = retMsg->response == ExtendedResponse::OK;
|
||||
if(!success)
|
||||
@@ -1745,8 +1882,8 @@ std::optional<bool> Device::SetCollectionUploaded(uint32_t collectionEntryByteAd
|
||||
// TODO fix this error
|
||||
report(APIEvent::Type::Unknown, APIEvent::Severity::EventWarning);
|
||||
}
|
||||
// Valid device with a properly formed respose, return success
|
||||
return std::make_optional<bool>(success);
|
||||
// Valid device with a properly formed response, return success
|
||||
return success;
|
||||
}
|
||||
|
||||
std::optional<std::chrono::time_point<std::chrono::system_clock>> Device::getRTC()
|
||||
@@ -2058,7 +2195,7 @@ bool Device::readVSA(const VSAExtractionSettings& extractionSettings) {
|
||||
if(isOnline()) {
|
||||
goOffline();
|
||||
}
|
||||
auto innerReadVSA = [&](uint64_t diskSize) -> const bool {
|
||||
auto innerReadVSA = [&](uint64_t diskSize) -> bool {
|
||||
// Adjust driver to offset to start of VSA file
|
||||
const auto& offset = getVSAOffsetInLogicalDisk();
|
||||
if(!offset) {
|
||||
@@ -3122,3 +3259,79 @@ std::optional<uint64_t> Device::getVSADiskSize() {
|
||||
}
|
||||
return diskSize;
|
||||
}
|
||||
|
||||
bool Device::requestTC10Wake(Network::NetID network) {
|
||||
if(!supportsTC10()) {
|
||||
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> args(sizeof(network));
|
||||
*(Network::NetID*)args.data() = network;
|
||||
auto msg = com->waitForMessageSync([&] {
|
||||
return com->sendCommand(ExtendedCommand::RequestTC10Wake, args);
|
||||
}, std::make_shared<MessageFilter>(Message::Type::ExtendedResponse), std::chrono::milliseconds(1000));
|
||||
|
||||
if(!msg) {
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto resp = std::static_pointer_cast<ExtendedResponseMessage>(msg);
|
||||
if(!resp) {
|
||||
report(APIEvent::Type::UnexpectedResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return resp->response == ExtendedResponse::OK;
|
||||
}
|
||||
|
||||
bool Device::requestTC10Sleep(Network::NetID network) {
|
||||
if(!supportsTC10()) {
|
||||
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> args(sizeof(network));
|
||||
*(Network::NetID*)args.data() = network;
|
||||
auto msg = com->waitForMessageSync([&] {
|
||||
return com->sendCommand(ExtendedCommand::RequestTC10Sleep, args);
|
||||
}, std::make_shared<MessageFilter>(Message::Type::ExtendedResponse), std::chrono::milliseconds(1000));
|
||||
|
||||
if(!msg) {
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto typed = std::static_pointer_cast<ExtendedResponseMessage>(msg);
|
||||
if(!typed) {
|
||||
report(APIEvent::Type::UnexpectedResponse, APIEvent::Severity::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return typed->response == ExtendedResponse::OK;
|
||||
}
|
||||
|
||||
std::optional<TC10StatusMessage> Device::getTC10Status(Network::NetID network) {
|
||||
if(!supportsTC10()) {
|
||||
report(APIEvent::Type::NotSupported, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<uint8_t> args(sizeof(network));
|
||||
*(Network::NetID*)args.data() = network;
|
||||
auto msg = com->waitForMessageSync([&] {
|
||||
return com->sendCommand(ExtendedCommand::GetTC10Status, args);
|
||||
}, std::make_shared<MessageFilter>(Message::Type::TC10Status), std::chrono::milliseconds(1000));
|
||||
|
||||
if(!msg) {
|
||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto typed = std::static_pointer_cast<TC10StatusMessage>(msg);
|
||||
|
||||
if(!typed) {
|
||||
report(APIEvent::Type::UnexpectedResponse, APIEvent::Severity::Error);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return *typed;
|
||||
}
|
||||
|
||||
@@ -137,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
|
||||
@@ -177,6 +181,14 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
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
|
||||
@@ -193,6 +205,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
makeIfSerialMatches<RADGigastar>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADGIGASTAR2_H_
|
||||
makeIfSerialMatches<RADGigastar2>(dev, newFoundDevices);
|
||||
#endif
|
||||
|
||||
#ifdef __RADJUPITER_H_
|
||||
makeIfSerialMatches<RADJupiter>(dev, newFoundDevices);
|
||||
#endif
|
||||
@@ -284,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
|
||||
@@ -316,6 +336,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
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
|
||||
@@ -332,6 +360,10 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
|
||||
RADGigastar::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADGIGASTAR2_H_
|
||||
RADGigastar2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#if defined __RADMOON2_H_ || defined __RADMOON2ZL_H_
|
||||
RADMoon2::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "icsneo/disk/neomemorydiskdriver.h"
|
||||
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
|
||||
#include "icsneo/communication/message/flashmemorymessage.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
@@ -8,7 +9,8 @@ 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, MemoryType memType) {
|
||||
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
|
||||
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;
|
||||
@@ -33,18 +35,26 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
|
||||
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) {
|
||||
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, sdmsg->data.data(), SectorSize);
|
||||
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);
|
||||
}
|
||||
return SectorSize;
|
||||
}
|
||||
|
||||
|
||||
-117
@@ -1,117 +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. Channels are indexed at 0 and interleaved such that downstream are on even number channels and upstream on odd number channels. If we introduce a
|
||||
variable ``IS_UPSTREAM`` which is ``0`` when downstream and ``1`` when upstream and desired a channel ``CHANNEL_NUM`` the corresponding channel in the wave file would be
|
||||
``2*CHANNEL_NUM + IS_UPSTREAM``.
|
||||
|
||||
Wave files may be split by channel using programs such as ``FFmpeg``. Consider a file ``out.wav`` which was generated using a ``icsneo::A2BWAVOutput`` object
|
||||
and contains ``32`` channels per stream. The ``icsneo::A2BWavoutput`` object injested PCM data with a sample rate of ``44.1 kHz`` and bit depth of ``24``. The corresponding
|
||||
channel of upstream channel ``8`` in ``out.wav`` would be ``2*CHANNEL_NUM + IS_UPSTREAM = 2*8 + 1 = 17``. The following ``FFmpeg`` command may be ran in a linux environment to create a new wave
|
||||
file ``out_upstream_ch8.wav`` which contains only PCM samples off of upstream channel ``8``.
|
||||
|
||||
``ffmpeg -i out.wav -ar 44100 -acodec pcm_s24le -map_channel 0.0.17 out_upstream_ch8.wav``
|
||||
+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-2024, 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-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 = '@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:
|
||||
@@ -0,0 +1,55 @@
|
||||
============
|
||||
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.
|
||||
@@ -0,0 +1,65 @@
|
||||
===============
|
||||
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 message channel indexing
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The libicsneo API allows users to input and output WAV file via receiving or transmitting A2B messages. The library provides several ways to import and export
|
||||
WAV files via A2B traffic. While using the API, a user will encounter message channels being referenced as unsigned integers for indexing. A ``icsneo::A2BMessage``
|
||||
object will contain both upstream and downstream channels. This implies that the number of channels that a A2B message has is twice the TDM mode. The ordering for these
|
||||
channels are interleved. Therefore, a message channel index ``0`` would represent downstream channel ``0`` in the network, message channel index ``1`` would represent
|
||||
upstream channel ``0`` in the network, and so on. More generally, a message channel index can be computed with the formula ``2 * CHANNEL + IS_UPSTREAM`` where channel
|
||||
is the channel referred in the A2B network and ``IS_UPSTREAM`` is ``1`` when a channel is upstream and ``0`` if downstream.
|
||||
|
||||
One area where these message channel indexes are used are when specifying a ``icsneo::ChannelMap`` for WAV transmit or receive. When transmitting a WAV file, the
|
||||
map will map message channels to the input WAV file. For receiving a WAV file, the map will map the output WAV channels to channels from received messages.
|
||||
|
||||
EX: If we want to transmit, we will need to construct a mapping from message channels to the input WAV file. So, if we have a monotone WAV file that we are inputting
|
||||
through the API, then we can map channel ``2`` upstream to channel 0 in the input WAV (the only channel in the WAV file) with the following:
|
||||
|
||||
``icsneo::ChannelMap chMap``
|
||||
``chMap[5] = 0``
|
||||
|
||||
Since we are transmitting, we must map our desired A2B message channels to the input WAV file. We have ``0`` representing the single channel in the WAV file and
|
||||
``5`` representing channel ``2`` upstream in the A2B message from using the formula above ``2 * CHANNEL + IS_UPSTREAM = 2 * 2 + 1 = 5``.
|
||||
@@ -0,0 +1,52 @@
|
||||
============
|
||||
C++ Examples
|
||||
============
|
||||
|
||||
A variety of examples can be found within ``examples/cpp``, see below for an
|
||||
example that uses the polling API to receive CAN frames.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
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();
|
||||
@@ -0,0 +1,11 @@
|
||||
icsneocpp
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
installation
|
||||
deviceconcepts
|
||||
apiconcepts
|
||||
examples
|
||||
api
|
||||
@@ -0,0 +1,39 @@
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
Dependencies
|
||||
============
|
||||
|
||||
The minimum requirements to build libicsneo are:
|
||||
- CMake version 3.12 or newer
|
||||
- A C++17 compiler
|
||||
- libusb and libpcap on Linux and macOS
|
||||
|
||||
|
||||
Building library & examples
|
||||
===========================
|
||||
|
||||
For a list of available configuration options, see the ``option()`` entries in
|
||||
``CMakeLists.txt``.
|
||||
|
||||
To build libicsneo with default options:
|
||||
#. ``cmake -B build``
|
||||
#. ``cmake --build build``
|
||||
|
||||
Adding to existing projects
|
||||
===========================
|
||||
|
||||
libicsneo supports being added as a sub-projects with CMake's
|
||||
``add_subdirectory()``.
|
||||
|
||||
#. Clone libicsneo into the desired location within the project
|
||||
#. Add ``add_subdirectory(path/to/libicsneo)`` to ``CMakeLists.txt``
|
||||
#. Link the project to libicsneo with ``target_link_libraries(app icsneocpp)``
|
||||
|
||||
Linux udev Rules
|
||||
================
|
||||
|
||||
Linux users may want to install the included udev rules to run libicsneo based
|
||||
applications without root, this can be done with:
|
||||
``cp 99-intrepidcs.rules /etc/udev/rules.d/``
|
||||
@@ -0,0 +1,9 @@
|
||||
==========
|
||||
Python API
|
||||
==========
|
||||
|
||||
.. automodule:: icsneopy
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:special-members: __init__
|
||||
@@ -0,0 +1,52 @@
|
||||
===============
|
||||
Python Examples
|
||||
===============
|
||||
|
||||
Transmit CAN frames on HSCAN
|
||||
============================
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import icsneopy
|
||||
|
||||
devices: list[icsneopy.Device] = icsneopy.find_all_devices()
|
||||
|
||||
# grab the first/only device found
|
||||
device: icsneopy.Device = devices[0]
|
||||
|
||||
message = icsneopy.CANMessage()
|
||||
message.network = icsneopy.Network(icsneopy.Network.NetID.HSCAN)
|
||||
message.arbid = 0x56
|
||||
message.data = (0x11, 0x22, 0x33)
|
||||
|
||||
device.open()
|
||||
|
||||
device.go_online()
|
||||
|
||||
device.transmit(message)
|
||||
|
||||
Receive CAN frames on HSCAN
|
||||
===========================
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import icsneopy
|
||||
import time
|
||||
|
||||
devices: list[icsneopy.Device] = icsneopy.find_all_devices()
|
||||
|
||||
# grab the first/only device found
|
||||
device: icsneopy.Device = devices[0]
|
||||
|
||||
def on_message(message: icsneopy.CANMessage):
|
||||
print(message.arbid, message.data)
|
||||
message_filter = icsneopy.MessageFilter(icsneopy.Network.NetID.HSCAN)
|
||||
callback = icsneopy.MessageCallback(on_message, message_filter)
|
||||
|
||||
device.add_message_callback(callback)
|
||||
|
||||
device.open()
|
||||
device.go_online()
|
||||
|
||||
# rx for 10s
|
||||
time.sleep(10)
|
||||
@@ -0,0 +1,9 @@
|
||||
========
|
||||
icsneopy
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
examples
|
||||
api
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
Welcome to libicsneo's documentation!
|
||||
=======================================
|
||||
libicsneo
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:caption: Documentation
|
||||
:maxdepth: 1
|
||||
:caption: API Documentation
|
||||
|
||||
Usage
|
||||
icsneocpp
|
||||
icsneoc
|
||||
icsneocpp/index
|
||||
icsneopy/index
|
||||
icsneoc/index
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
breathe==4.12.0
|
||||
sphinx
|
||||
icsneopy
|
||||
breathe
|
||||
sphinx_rtd_theme
|
||||
|
||||
@@ -9,6 +9,7 @@ option(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE "Build the Live Data example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON)
|
||||
option(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE "Build the app error example." ON)
|
||||
|
||||
# Disabled until we properly build these in-tree
|
||||
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
|
||||
@@ -58,6 +59,10 @@ if(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE)
|
||||
add_subdirectory(cpp/vsa)
|
||||
endif()
|
||||
|
||||
if(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE)
|
||||
add_subdirectory(cpp/apperror)
|
||||
endif()
|
||||
|
||||
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
|
||||
# add_subdirectory(csharp)
|
||||
# endif()
|
||||
|
||||
@@ -37,18 +37,17 @@ int main() {
|
||||
printf("ICS icsneolegacy.dll version %u\n\n", ver);
|
||||
// Find and attempt to open device
|
||||
//legacy open device
|
||||
int numDevices = 10;
|
||||
NeoDevice devices[10];
|
||||
void* hObject; // holds a handle to the neoVI object
|
||||
int numDevices = 255;
|
||||
NeoDeviceEx devices[255] = {0};
|
||||
void* hObject = NULL; // holds a handle to the neoVI object
|
||||
int iRetVal = 0;
|
||||
int deviceTypes = 0;
|
||||
int iResult = 0;
|
||||
SDeviceSettings pSettings;
|
||||
SDeviceSettings pSettings = {0};
|
||||
|
||||
iRetVal = icsneoFindNeoDevices(deviceTypes, devices, &numDevices);
|
||||
if(iRetVal) {
|
||||
iRetVal = icsneoFindDevices(devices, &numDevices, NULL, 0, NULL, 0);
|
||||
if(iRetVal && numDevices > 0) {
|
||||
// Attempt to open the selected device, enable message polling, and go online
|
||||
iRetVal = icsneoOpenNeoDevice(&devices[0], &hObject, NULL, 1, 0);
|
||||
iRetVal = icsneoOpenDevice(&devices[0], &hObject, NULL, 1, 0, NULL, 0);
|
||||
if(iRetVal) {
|
||||
puts("Device found and opened!\n");
|
||||
} else {
|
||||
|
||||
@@ -31,17 +31,16 @@ int main() {
|
||||
printf("ICS icsneolegacy.dll version %u\n\n", ver);
|
||||
// Find and attempt to open device
|
||||
//legacy open device
|
||||
int numDevices = 10;
|
||||
NeoDevice devices[10];
|
||||
void* hObject; // holds a handle to the neoVI object
|
||||
int numDevices = 255;
|
||||
NeoDeviceEx devices[255] = {0};
|
||||
void* hObject = NULL; // holds a handle to the neoVI object
|
||||
int iRetVal = 0;
|
||||
int deviceTypes = 0;
|
||||
int iResult = 0;
|
||||
|
||||
iRetVal = icsneoFindNeoDevices(deviceTypes, devices, &numDevices);
|
||||
if(iRetVal) {
|
||||
iRetVal = icsneoFindDevices(devices, &numDevices, NULL, 0, NULL, 0);
|
||||
if(iRetVal && numDevices < 0) {
|
||||
// Attempt to open the selected device, enable message polling, and go online
|
||||
iRetVal = icsneoOpenNeoDevice(&devices[0], &hObject, NULL, 1, 0);
|
||||
iRetVal = icsneoOpenDevice(&devices[0], &hObject, NULL, 1, 0, NULL, 0);
|
||||
if(iRetVal) {
|
||||
printf("Device found and opened!\n");
|
||||
} else {
|
||||
|
||||
+169
-153
@@ -1,111 +1,144 @@
|
||||
// libicsneo A2B example
|
||||
// Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node
|
||||
// Options:
|
||||
// -h, --help Display help message.
|
||||
// -e, --example [EXAMPLE_NUM] Example to run.
|
||||
// Example usage: ./libicsneocpp-a2b.exe --example 1
|
||||
// Example usage: ./libicsneocpp-a2b.exe -h
|
||||
/**
|
||||
* libicsneo A2B example
|
||||
*
|
||||
* Example were made to be ran with RAD-A2B as main node on TDM4 16 bit channel size and one additional sub node (either an ADI board or an additional RAD-A2B).
|
||||
* Follow the specific hardware instructions per example to ensure expected output. Be sure to configure the A2B network before running these examples, especially
|
||||
* ones which Tx or Rx audio.
|
||||
*
|
||||
* Options:
|
||||
* -h, --help Display help message.
|
||||
* -e, --example [EXAMPLE_NUM] Example to run.
|
||||
* Example usage: ./libicsneocpp-a2b.exe --example 1
|
||||
* Example usage: ./libicsneocpp-a2b.exe -h
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <icsneo/icsneocpp.h>
|
||||
#include <icsneo/device/tree/rada2b/rada2bsettings.h>
|
||||
#include <icsneo/communication/message/callback/streamoutput/a2bdecoder.h>
|
||||
#include <icsneo/communication/message/callback/streamoutput/a2bwavoutput.h>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
static constexpr size_t numFramesInWave = 48;
|
||||
|
||||
std::string makeWave() {
|
||||
icsneo::WaveFileHeader header = icsneo::WaveFileHeader(1, 48000, 24);
|
||||
std::vector<uint8_t> sineWaveSamples = {
|
||||
0x00, 0x2B, 0x98, 0x08, 0x25, 0x01, 0x10, 0xD3, 0xEF, 0x18, 0x40, 0xA3, 0x20, 0x33, 0x4C, 0x26,
|
||||
0xCE, 0xCA, 0x2D, 0x5B, 0x41, 0x32, 0xB9, 0x3C, 0x37, 0x6E, 0x50, 0x3B, 0x29, 0x18, 0x3D, 0xC2,
|
||||
0x96, 0x3F, 0x86, 0xD3, 0x3F, 0xEC, 0x35, 0x3F, 0x85, 0xA7, 0x3D, 0xC4, 0x19, 0x3B, 0x28, 0xC9,
|
||||
0x37, 0x6B, 0x5A, 0x32, 0xC1, 0xC4, 0x2D, 0x4A, 0xEE, 0x26, 0xE8, 0xB1, 0x20, 0x0E, 0xCF, 0x18,
|
||||
0x6F, 0xAA, 0x10, 0x9C, 0x17, 0x08, 0x53, 0x35, 0x00, 0x01, 0xFD, 0xF7, 0xA9, 0x29, 0xEF, 0x66,
|
||||
0x87, 0xE7, 0x8F, 0x37, 0xDF, 0xF0, 0x8A, 0xD9, 0x19, 0xB3, 0xD2, 0xB1, 0x3E, 0xCD, 0x42, 0xE9,
|
||||
0xC8, 0x8F, 0xE0, 0xC4, 0xDB, 0x39, 0xC2, 0x39, 0x78, 0xC0, 0x7A, 0x8A, 0xC0, 0x16, 0x38, 0xC0,
|
||||
0x74, 0x18, 0xC2, 0x44, 0xBF, 0xC4, 0xCE, 0x26, 0xC8, 0x9A, 0x99, 0xCD, 0x3F, 0x53, 0xD2, 0xA8,
|
||||
0xBD, 0xD9, 0x32, 0xF5, 0xDF, 0xC2, 0x68, 0xE7, 0xD5, 0xFD, 0xEF, 0x02, 0x15, 0xF8, 0x3B, 0x33,
|
||||
std::string makeWAV() {
|
||||
icsneo::WAVHeader header = icsneo::WAVHeader(1, 48000, 16);
|
||||
std::vector<uint8_t> sineWAVSamples = {
|
||||
0xFF, 0x3F, 0x81, 0x5A, 0xD9, 0x6E, 0xA2, 0x7B, 0xFF, 0x7F, 0xA2, 0x7B, 0xD9, 0x6E, 0x81, 0x5A,
|
||||
0xFF, 0x3F, 0x20, 0x21, 0x00, 0x00, 0xE0, 0xDE, 0x01, 0xC0, 0x7F, 0xA5, 0x27, 0x91, 0x5E, 0x84,
|
||||
0x01, 0x80, 0x5E, 0x84, 0x27, 0x91, 0x7F, 0xA5, 0x01, 0xC0, 0xE0, 0xDe, 0x00, 0x00, 0x20, 0x21
|
||||
};
|
||||
|
||||
std::vector<uint8_t> sineWave;
|
||||
sineWave.reserve(sineWaveSamples.size() + sizeof(header));
|
||||
std::vector<uint8_t> sineWAV;
|
||||
sineWAV.reserve(sineWAVSamples.size() + sizeof(header));
|
||||
|
||||
sineWave.insert(sineWave.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header));
|
||||
std::copy(sineWaveSamples.begin(), sineWaveSamples.end(), std::back_inserter(sineWave));
|
||||
sineWAV.insert(sineWAV.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header));
|
||||
std::copy(sineWAVSamples.begin(), sineWAVSamples.end(), std::back_inserter(sineWAV));
|
||||
|
||||
return std::string(sineWave.begin(), sineWave.end());
|
||||
return std::string(sineWAV.begin(), sineWAV.end());
|
||||
}
|
||||
|
||||
// Example 0: TX
|
||||
void example0(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::cout << "Transmitting a sine wave..." << std::endl;
|
||||
|
||||
// Create sine tone in wave format
|
||||
std::string waveString = makeWave();
|
||||
|
||||
// Audio map to map which channel in wave to stream on a2b bus.
|
||||
icsneo::A2BAudioChannelMap a2bmap(4);
|
||||
|
||||
|
||||
a2bmap.set(
|
||||
2, // Channel on a2b bus
|
||||
icsneo::A2BMessage::A2BDirection::Downstream, // Direction
|
||||
0 // Channel in wave file
|
||||
);
|
||||
|
||||
a2bmap.setAll(0);
|
||||
|
||||
icsneo::A2BDecoder decoder(
|
||||
std::make_unique<std::istringstream>(waveString), // Wave file stream
|
||||
false, // True when using 16 bit samples
|
||||
a2bmap
|
||||
);
|
||||
/**
|
||||
* Example 0: TX
|
||||
*/
|
||||
void example0(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::cout << "Transmitting a sine tone..." << std::endl;
|
||||
|
||||
// Create sine tone in wav format
|
||||
std::string wavString = makeWAV();
|
||||
|
||||
std::istringstream sineWAV(wavString);
|
||||
double elapsedTime = 0.0;
|
||||
|
||||
// Create a IWAVStream object which represents a WAV data stream
|
||||
// the IWAVStream object here is initialized with an outside std::ostream,
|
||||
// so it holds a reference pointer to this stream.
|
||||
icsneo::IWAVStream wavStream(sineWAV);
|
||||
|
||||
// Create a channel map which maps each message channel to a channel in the input WAV file
|
||||
icsneo::ChannelMap channelMap;
|
||||
|
||||
// Here we will just set every message channel to channel 0 in the WAV file
|
||||
// We have 8 channels since this is TDM4 and we include both upstream and downstream
|
||||
// see docs for specific message channel labeling information
|
||||
|
||||
for(uint8_t messageChannel = 0; messageChannel < 8; messageChannel++) {
|
||||
channelMap[messageChannel] = 0;
|
||||
}
|
||||
|
||||
// Play roughly 5 seconds of sine tone.
|
||||
while(elapsedTime < 5.0) {
|
||||
while(elapsedTime < 5.0) {
|
||||
// If WAVStream is invalid (at EOF) break out of loop
|
||||
if(!wavStream) {
|
||||
break;
|
||||
}
|
||||
|
||||
decoder.outputAll(rada2b); // Output entire wave file
|
||||
// Creates a new message with the maximum amount of allocated frames
|
||||
auto msg = std::make_shared<icsneo::A2BMessage>(
|
||||
icsneo::A2BMessage::TDMMode::TDM4, /* TDM mode of the message, we use TDM4 for this whole example*/
|
||||
true /* true if we want 16 bit channels in the message, false for 32 bit. This should match the RAD-A2B device setting */
|
||||
);
|
||||
msg->txmsg = true;
|
||||
msg->network = icsneo::Network(icsneo::Network::NetID::A2B2);
|
||||
|
||||
elapsedTime += (static_cast<double>(numFramesInWave)) * 1.0/48000.0;
|
||||
// Load the WAV audio data into the desired channel, break if we fail to load
|
||||
if(!msg->loadAudioBuffer(wavStream, channelMap)) {
|
||||
break;
|
||||
}
|
||||
|
||||
decoder.stream->clear();
|
||||
decoder.stream->seekg(0, std::ios::beg);
|
||||
// Also outputs entire wave file
|
||||
while(decoder && elapsedTime < 5.0) {
|
||||
auto msg = decoder.decode();
|
||||
rada2b->transmit(msg);
|
||||
// Transmit the message
|
||||
if(!rada2b->transmit(msg)) {
|
||||
std::cout << "Failed to transmit." << std::endl;
|
||||
break;
|
||||
}
|
||||
elapsedTime += (static_cast<double>(msg->getNumFrames()))*1.0/48000.0;
|
||||
}
|
||||
|
||||
decoder.stream->clear();
|
||||
decoder.stream->seekg(0, std::ios::beg);
|
||||
// Reset the WAV stream
|
||||
wavStream.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Example 1: RX
|
||||
void example1(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
/**
|
||||
* Example 1: RX
|
||||
*/
|
||||
void example1(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::cout << "Receiving 5 seconds of audio data..." << std::endl;
|
||||
|
||||
// Add WAV output message callback
|
||||
// Saves samples to "out.wav"
|
||||
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("out.wav", 48000));
|
||||
auto handler = rada2b->addMessageCallback(
|
||||
std::make_shared<icsneo::A2BWAVOutput>(
|
||||
"audio16bit.wav", /* output file name */
|
||||
icsneo::ChannelMap( /** channel mapping which maps our output WAV channels to the message channels from incoming messages */
|
||||
{ /* See docs for specific A2B channel indexing information */
|
||||
{static_cast<uint8_t>(3u), static_cast<uint8_t>(0u)}, /* Map output WAV channel 3 to channel 0 downstream of the A2B network/A2BMessage */
|
||||
{static_cast<uint8_t>(2u), static_cast<uint8_t>(1u)}, /* Map output WAV channel 2 to channel 0 upstream of the A2B network/A2BMessage */
|
||||
{static_cast<uint8_t>(1u), static_cast<uint8_t>(2u)}, /* Map output WAV channel 1 to channel 1 downstream of the A2B network/A2BMessage */
|
||||
{static_cast<uint8_t>(0u), static_cast<uint8_t>(3u)} /* Map output WAV channel 0 to channel 1 upstream of the A2B network/A2BMessage */
|
||||
}
|
||||
),
|
||||
icsneo::PCMType::L16, /* store samples with 16 bit resolution*/
|
||||
2u, /* Number of channels in the output WAV file */
|
||||
48000 /* Sample rate of WAV file */
|
||||
)
|
||||
);
|
||||
|
||||
// Sleep this thread for 5 seconds, message callback still runs
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
|
||||
// Make sure you send 16 bit audio data on the above message channels in the channel map
|
||||
// to the RAD-A2B main node through a microphone or a different modem.
|
||||
// You can configure the message channels by changing the stream config in the A2B schematic
|
||||
|
||||
// Remove callback
|
||||
rada2b->removeMessageCallback(handler);
|
||||
}
|
||||
|
||||
|
||||
// Example 2: RADA2B settings
|
||||
void example2(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
/**
|
||||
* Example 2: RAD-A2B settings
|
||||
*/
|
||||
void example2(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
uint8_t numChannels;
|
||||
{
|
||||
// Get device settings
|
||||
@@ -129,82 +162,70 @@ void example2(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
rada2bSettings->setNodeType(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::NodeType::Master);
|
||||
|
||||
// Set TDM mode to TDM8
|
||||
rada2bSettings->setTDMMode(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::TDMMode::TDM8);
|
||||
rada2bSettings->setTDMMode(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::TDMMode::TDM4);
|
||||
|
||||
// Apply local settings to device
|
||||
rada2bSettings->apply();
|
||||
}
|
||||
}
|
||||
|
||||
// Example 3: A2BMessage API
|
||||
void example3() {
|
||||
icsneo::A2BMessage msg = icsneo::A2BMessage(4, false, 2048); // Create new A2BMessage
|
||||
|
||||
msg[0][0] = 60; // Set sample using operator[][]
|
||||
msg[0][3] = 60; // Frame 0, channel 2 upstream
|
||||
msg[6][2] = 32; // Frame 6, channel 1 downstream
|
||||
|
||||
// Equivalent to last line
|
||||
msg.setSample(icsneo::A2BMessage::A2BDirection::Downstream, 1, 6, 32);
|
||||
|
||||
// Get sample
|
||||
std::cout << "Channel 1 downstream sample for frame 6: " << msg.getSample(icsneo::A2BMessage::A2BDirection::Downstream, 1, 6).value() << std::endl;
|
||||
|
||||
// Get number of frames
|
||||
auto numFrames = msg.getNumFrames();
|
||||
std::cout << "Num frames: " << numFrames << std::endl;
|
||||
|
||||
icsneo::A2BPCMSample sample1 = 40;
|
||||
icsneo::A2BPCMSample sample2 = 60;
|
||||
msg.fill(sample1); // Fill whole message with sample 40
|
||||
|
||||
msg.fillFrame(sample2, numFrames/2); // Fill frame numFrames/2 with sample2
|
||||
|
||||
// Print msg sample contents
|
||||
std::cout << "A2B message contents:" << std::endl;
|
||||
for(size_t y = 0; y < numFrames; y++) {
|
||||
for(size_t x = 0; x < ((size_t)(msg.getNumChannels())*2); x++) { // Num channels including upstream and downstream
|
||||
std::cout << msg[y][x] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Set and get bits
|
||||
msg.setSyncFrameBit(true);
|
||||
|
||||
std::cout << "Was received from monitor: " << msg.isMonitorMsg() << std::endl;
|
||||
}
|
||||
|
||||
// Example 4: Packaging and transmitting sine wave using A2BMessage API
|
||||
void example4(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::cout << "Transmitting a 1000 hz sine wave." << std::endl;
|
||||
/**
|
||||
* Example 3: Packaging and transmitting sine tone using A2BMessage API
|
||||
*/
|
||||
void example3(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::cout << "Transmitting a 1000 hz sine tone." << std::endl;
|
||||
|
||||
float deltaTime = static_cast<float>(1.0/48000.0);
|
||||
float elapsedTime = 0.0;
|
||||
float twoPI = static_cast<float>(2.0*atan(1.0)*4.0);
|
||||
float frequency = 1000;
|
||||
float amplitude = static_cast<float>((1 << 23) - 1);
|
||||
float amplitude = static_cast<float>((1 << 15) - 1);
|
||||
size_t tdm = 4;
|
||||
size_t bytesPerSample = 2;
|
||||
|
||||
uint8_t icsChannel = 0; // Play audio on channel 2, upstream, see docs for details
|
||||
size_t numFrames = 2048 / (2 * tdm * bytesPerSample);
|
||||
|
||||
|
||||
// Play for roughly 5 seconds
|
||||
while(elapsedTime < 5.0) {
|
||||
// Allocate message
|
||||
std::shared_ptr<icsneo::A2BMessage> a2bmsgPtr = std::make_shared<icsneo::A2BMessage>(static_cast<uint8_t>(4), false, static_cast<size_t>(2048));
|
||||
std::shared_ptr<icsneo::A2BMessage> a2bmsgPtr = std::make_shared<icsneo::A2BMessage>(numFrames, icsneo::A2BMessage::TDMMode::TDM4, true);
|
||||
|
||||
icsneo::A2BMessage& a2bmsg = *a2bmsgPtr.get();
|
||||
a2bmsg.network = icsneo::Network(icsneo::Network::NetID::A2B2);
|
||||
a2bmsg.txmsg = true;
|
||||
|
||||
for(size_t frame = 0; frame < a2bmsg.getNumFrames(); frame++) {
|
||||
|
||||
// Sine wave sample, amplitude 1000, frequency 1000 hz
|
||||
// Sine tone sample, amplitude 1000, frequency 1000 hz
|
||||
|
||||
float contSample = amplitude*sin(twoPI*frequency*elapsedTime);
|
||||
icsneo::A2BPCMSample sample = static_cast<icsneo::A2BPCMSample>(contSample);
|
||||
icsneo::PCMSample sample = static_cast<icsneo::PCMSample>(contSample);
|
||||
|
||||
// Set sample for each frame in message
|
||||
a2bmsg[frame][icsChannel] = sample;
|
||||
// Send this sine wave sample downstream on channels 0, 1, and 2
|
||||
a2bmsg.setChannelSample(
|
||||
icsneo::A2BMessage::Direction::Downstream,
|
||||
0,
|
||||
frame,
|
||||
sample,
|
||||
icsneo::PCMType::L16
|
||||
);
|
||||
|
||||
a2bmsg.setChannelSample(
|
||||
icsneo::A2BMessage::Direction::Downstream,
|
||||
1,
|
||||
frame,
|
||||
sample,
|
||||
icsneo::PCMType::L16
|
||||
);
|
||||
|
||||
a2bmsg.setChannelSample(
|
||||
icsneo::A2BMessage::Direction::Downstream,
|
||||
2,
|
||||
frame,
|
||||
sample,
|
||||
icsneo::PCMType::L16
|
||||
);
|
||||
|
||||
elapsedTime+=deltaTime;
|
||||
}
|
||||
@@ -219,26 +240,10 @@ void example4(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Example 5: Wave loop back
|
||||
void example5(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
|
||||
auto listener = [&rada2b]() {
|
||||
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("looped.wav", 48000));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
rada2b->removeMessageCallback(handler);
|
||||
};
|
||||
|
||||
// Listen on another thread
|
||||
std::thread listenerThread{listener};
|
||||
|
||||
// Transmit wave file using example0
|
||||
example0(rada2b);
|
||||
listenerThread.join();
|
||||
}
|
||||
|
||||
// Example 6: Retrieving A2B bus status using I2C messaages.
|
||||
void example6(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
/**
|
||||
* Example 4: Retrieving A2B bus status using I2C messaages.
|
||||
*/
|
||||
void example4(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
std::shared_ptr<icsneo::I2CMessage> msg = std::make_shared<icsneo::I2CMessage>();
|
||||
std::shared_ptr<icsneo::MessageFilter> msgFilter = std::make_shared<icsneo::MessageFilter>(icsneo::Network::NetID::I2C2);
|
||||
|
||||
@@ -320,6 +325,21 @@ void example6(std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
rada2b->removeMessageCallback(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Example 5: Reading A2B sequence chart .puml file
|
||||
*/
|
||||
void example5(const std::shared_ptr<icsneo::Device>& rada2b) {
|
||||
// The A2B sequence chart is located at binary index 0
|
||||
constexpr uint16_t a2bSequenceChartIndex = 0;
|
||||
|
||||
// Create a ostream object to capture sequence chart data
|
||||
std::ofstream a2bSequenceChart("a2b_sequence_chart.puml", std::ios::out | std::ios::binary);
|
||||
|
||||
if(!rada2b->readBinaryFile(a2bSequenceChart, a2bSequenceChartIndex)) {
|
||||
std::cout << "Failed to read A2B sequence chart" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void displayUsage() {
|
||||
std::cout << "libicsneo A2B example" << std::endl;
|
||||
std::cout << "Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node" << std::endl;
|
||||
@@ -332,11 +352,10 @@ void displayUsage() {
|
||||
std::cout << "Example options:" << std::endl;
|
||||
std::cout << "0\ttx" << std::endl;
|
||||
std::cout << "1\trx" << std::endl;
|
||||
std::cout << "2\tSet RADA2B settings" << std::endl;
|
||||
std::cout << "3\tA2BMessage API" << std::endl;
|
||||
std::cout << "4\tPackaging and transmitting sine wave using A2BMessage API" << std::endl;
|
||||
std::cout << "5\tWave loopback" << std::endl;
|
||||
std::cout << "6\tRead/write I2C registers on A2B board" << std::endl;
|
||||
std::cout << "2\tSet RAD-A2B settings" << std::endl;
|
||||
std::cout << "3\tPackaging and transmitting sine wav using A2BMessage API" << std::endl;
|
||||
std::cout << "4\tRead/write I2C registers on A2B board" << std::endl;
|
||||
std::cout << "5\tReading out A2B sequence chart .puml file" << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -360,7 +379,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
int option = atoi(arguments[2].c_str());
|
||||
|
||||
if(option < 0 || option > 6) {
|
||||
if(option < 0 || option > 5) {
|
||||
std::cerr << "Invalid usage." << std::endl;
|
||||
displayUsage();
|
||||
return EXIT_FAILURE;
|
||||
@@ -386,27 +405,27 @@ int main(int argc, char** argv) {
|
||||
);
|
||||
|
||||
if(it == devices.end()) {
|
||||
std::cerr << "Could not find RADA2B." << std::endl;
|
||||
std::cerr << "Could not find RAD-A2B." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::shared_ptr<icsneo::Device> rada2b = *it;
|
||||
if(!rada2b->open()) {
|
||||
std::cout << "Failed to open RADA2B." << std::endl;
|
||||
std::cout << "Failed to open RAD-A2B." << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
std::cout << "Opened RADA2B." << std::endl;
|
||||
std::cout << "Opened RAD-A2B." << std::endl;
|
||||
}
|
||||
|
||||
if(!rada2b->goOnline()) {
|
||||
std::cout << "Failed to go online with RADA2B." << std::endl;
|
||||
std::cout << "Failed to go online with RAD-A2B." << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else {
|
||||
std::cout << "RADA2B online." << std::endl;
|
||||
std::cout << "RAD-A2B online." << std::endl;
|
||||
}
|
||||
|
||||
switch(option) {
|
||||
@@ -420,7 +439,7 @@ int main(int argc, char** argv) {
|
||||
example2(rada2b);
|
||||
break;
|
||||
case 3:
|
||||
example3();
|
||||
example3(rada2b);
|
||||
break;
|
||||
case 4:
|
||||
example4(rada2b);
|
||||
@@ -428,9 +447,6 @@ int main(int argc, char** argv) {
|
||||
case 5:
|
||||
example5(rada2b);
|
||||
break;
|
||||
case 6:
|
||||
example6(rada2b);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
add_executable(libicsneocpp-app-error src/AppErrorExample.cpp)
|
||||
target_link_libraries(libicsneocpp-app-error icsneocpp)
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/communication/message/apperrormessage.h"
|
||||
#include "icsneo/communication/message/message.h"
|
||||
/*
|
||||
* App errors are responses from the device indicating internal runtime errors
|
||||
* NOTE: To trigger the app error in this example, disable the HSCAN network on the device
|
||||
* (e.g. with neoVI Explorer)
|
||||
*/
|
||||
int main() {
|
||||
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||
std::cout << "\nFinding devices... " << std::flush;
|
||||
auto devices = icsneo::FindAllDevices();
|
||||
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
|
||||
|
||||
// List off the devices
|
||||
for(auto& device : devices)
|
||||
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
for(auto device : devices) {
|
||||
std::cout << "Connecting to " << device->describe() << "... ";
|
||||
bool ret = device->open();
|
||||
if(!ret) { // Failed to open
|
||||
std::cout << "FAIL" << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl << std::endl;
|
||||
|
||||
// Create an app error message filter, including "internal" messages
|
||||
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::AppError);
|
||||
filter->includeInternalInAny = true;
|
||||
|
||||
// ...and register a callback with it.
|
||||
// Add your error handling here
|
||||
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>(filter, [](std::shared_ptr<icsneo::Message> message) {
|
||||
auto msg = std::static_pointer_cast<icsneo::AppErrorMessage>(message);
|
||||
if(icsneo::Network::NetID::RED_App_Error == msg->network.getNetID()) {
|
||||
std::cout << std::endl << "App error reported:" << std::endl;
|
||||
std::cout << msg->getAppErrorString() << std::endl << std::endl;
|
||||
}
|
||||
}));
|
||||
|
||||
std::cout << "Going online... ";
|
||||
ret = device->goOnline();
|
||||
if(!ret) {
|
||||
std::cout << "FAIL" << std::endl;
|
||||
device->close();
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
// Prepare a CAN message
|
||||
std::cout << std::endl << "Transmitting a CAN frame... ";
|
||||
auto txMessage = std::make_shared<icsneo::CANMessage>();
|
||||
txMessage->network = icsneo::Network::NetID::HSCAN;
|
||||
txMessage->arbid = 0x22;
|
||||
txMessage->data.insert(txMessage->data.end(), {0xaa, 0xbb, 0xcc});
|
||||
// The DLC will come from the length of the data vector
|
||||
txMessage->isExtended = false;
|
||||
txMessage->isCANFD = false;
|
||||
|
||||
// Transmit a CAN message on HSCAN, even though HSCAN is disabled on the device!
|
||||
// Expect to see an app error caught in the callback defined above
|
||||
ret = device->transmit(txMessage);
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
|
||||
// Go offline, stop sending and receiving traffic
|
||||
device->removeMessageCallback(handler);
|
||||
std::cout << "Going offline... ";
|
||||
ret = device->goOffline();
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
std::cout << "Disconnecting... ";
|
||||
ret = device->close();
|
||||
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -63,7 +63,9 @@ int main(int argc, char** argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!device->uploadCoremini(std::make_unique<std::ifstream>(arguments[2], std::ios::binary), type)) {
|
||||
std::ifstream coreminiFile(arguments[2], std::ios::binary);
|
||||
|
||||
if (!device->uploadCoremini(coreminiFile, type)) {
|
||||
std::cout << "Failed to upload coremini" << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ public:
|
||||
ValueNotYetPresent = 0x1013,
|
||||
Timeout = 0x1014,
|
||||
WiVINotSupported = 0x1015,
|
||||
RestrictedEntryFlag = 0x1016,
|
||||
NotSupported = 0x1017,
|
||||
|
||||
// Device Events
|
||||
PollingMessageOverflow = 0x2000,
|
||||
@@ -106,6 +108,7 @@ public:
|
||||
LiveDataNotSupported = 0x2052,
|
||||
LINSettingsNotAvailable = 0x2053,
|
||||
ModeNotFound = 0x2054,
|
||||
AppErrorParsingFailed = 0x2055,
|
||||
|
||||
// Transport Events
|
||||
FailedToRead = 0x3000,
|
||||
|
||||
@@ -22,6 +22,7 @@ enum class Command : uint8_t {
|
||||
UpdateLEDState = 0xA7,
|
||||
SetDefaultSettings = 0xA8, // Follow up with SaveSettings to write to EEPROM
|
||||
GetSecondaryVersions = 0xA9, // Previously known as RED_CMD_PERIPHERALS_APP_VERSION_REQ, versions other than the main chip
|
||||
GetHardwareInfo = 0xB7, // Previously known as RED_CMD_HARDWARE_VERSION_REQ
|
||||
GetLogicalDiskInfo = 0xBB, // Previously known as RED_CMD_GET_SDCARD_INFO
|
||||
RequestStatusUpdate = 0xBC,
|
||||
ReadSettings = 0xC7, // Previously known as 3G_READ_SETTINGS_EX
|
||||
@@ -52,9 +53,12 @@ enum class ExtendedCommand : uint16_t {
|
||||
GetSupportedFeatures = 0x0018,
|
||||
GetComponentVersions = 0x001A,
|
||||
Reboot = 0x001C,
|
||||
SetUploadedFlag = 0x0027,
|
||||
SetRootFSEntryFlags = 0x0027,
|
||||
GenericBinaryInfo = 0x0030,
|
||||
LiveData = 0x0035,
|
||||
RequestTC10Wake = 0x003D,
|
||||
RequestTC10Sleep = 0x003E,
|
||||
GetTC10Status = 0x003F,
|
||||
};
|
||||
|
||||
enum class ExtendedResponse : int32_t {
|
||||
|
||||
@@ -48,8 +48,10 @@ public:
|
||||
void awaitModeChangeComplete() { driver->awaitModeChangeComplete(); }
|
||||
bool rawWrite(const std::vector<uint8_t>& bytes) { return driver->write(bytes); }
|
||||
virtual bool sendPacket(std::vector<uint8_t>& bytes);
|
||||
bool redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo);
|
||||
void clearRedirectRead();
|
||||
|
||||
void pauseReads();
|
||||
void resumeReads();
|
||||
bool readsArePaused();
|
||||
|
||||
void setWriteBlocks(bool blocks) { driver->writeBlocks = blocks; }
|
||||
|
||||
@@ -85,17 +87,22 @@ public:
|
||||
std::unique_ptr<Driver> driver;
|
||||
device_eventhandler_t report;
|
||||
|
||||
size_t readTaskWakeLimit = 1;
|
||||
std::chrono::milliseconds readTaskWakeTimeout = std::chrono::milliseconds(1000);
|
||||
|
||||
protected:
|
||||
static int messageCallbackIDCounter;
|
||||
std::mutex messageCallbacksLock;
|
||||
std::map<int, std::shared_ptr<MessageCallback>> messageCallbacks;
|
||||
std::atomic<bool> closing{false};
|
||||
std::atomic<bool> redirectingRead{false};
|
||||
std::function<void(std::vector<uint8_t>&&)> redirectionFn;
|
||||
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the redirectionFn
|
||||
|
||||
std::condition_variable pauseReadTaskCv;
|
||||
std::mutex pauseReadTaskMutex;
|
||||
std::atomic<bool> pauseReadTask = false;
|
||||
|
||||
std::mutex syncMessageMutex;
|
||||
|
||||
void handleInput(Packetizer& p, std::vector<uint8_t>& readBytes);
|
||||
void handleInput(Packetizer& p);
|
||||
|
||||
private:
|
||||
std::thread readTaskThread;
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
#include <condition_variable>
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
|
||||
#include "icsneo/communication/ringbuffer.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
#define ICSNEO_DRIVER_RINGBUFFER_SIZE (512 * 1024)
|
||||
class Driver {
|
||||
public:
|
||||
Driver(const device_eventhandler_t& handler) : report(handler) {}
|
||||
@@ -22,12 +24,18 @@ public:
|
||||
virtual bool isOpen() = 0;
|
||||
virtual void modeChangeIncoming() {}
|
||||
virtual void awaitModeChangeComplete() {}
|
||||
virtual bool isDisconnected() { return disconnected; };
|
||||
virtual bool close() = 0;
|
||||
bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
|
||||
|
||||
inline bool isDisconnected() const { return disconnected; };
|
||||
inline bool isClosing() const { return closing; }
|
||||
|
||||
bool waitForRx(size_t limit, std::chrono::milliseconds timeout);
|
||||
bool waitForRx(std::function<bool()> predicate, std::chrono::milliseconds timeout);
|
||||
bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
|
||||
bool write(const std::vector<uint8_t>& bytes);
|
||||
virtual bool isEthernet() const { return false; }
|
||||
bool readAvailable() { return readBuffer.size() > 0; }
|
||||
RingBuffer& getReadBuffer() { return readBuffer; }
|
||||
|
||||
device_eventhandler_t report;
|
||||
|
||||
@@ -46,17 +54,24 @@ protected:
|
||||
WAIT
|
||||
};
|
||||
|
||||
virtual void readTask() = 0;
|
||||
virtual void writeTask() = 0;
|
||||
inline void setIsClosing(bool isClosing) { closing = isClosing; }
|
||||
inline void setIsDisconnected(bool isDisconnected) { disconnected = isDisconnected; }
|
||||
|
||||
// Overridable in case the driver doesn't want to use writeTask and writeQueue
|
||||
virtual bool writeQueueFull() { return writeQueue.size_approx() > writeQueueSize; }
|
||||
virtual bool writeQueueAlmostFull() { return writeQueue.size_approx() > (writeQueueSize * 3 / 4); }
|
||||
virtual bool writeInternal(const std::vector<uint8_t>& b) { return writeQueue.enqueue(WriteOperation(b)); }
|
||||
|
||||
moodycamel::BlockingConcurrentQueue<uint8_t> readQueue;
|
||||
bool pushRx(const uint8_t* buf, size_t numReceived);
|
||||
void clearBuffers();
|
||||
|
||||
moodycamel::BlockingConcurrentQueue<WriteOperation> writeQueue;
|
||||
std::thread readThread, writeThread;
|
||||
|
||||
private:
|
||||
RingBuffer readBuffer = RingBuffer(ICSNEO_DRIVER_RINGBUFFER_SIZE);
|
||||
std::condition_variable rxWaitCv;
|
||||
std::mutex rxWaitMutex;
|
||||
|
||||
std::atomic<bool> closing{false};
|
||||
std::atomic<bool> disconnected{false};
|
||||
};
|
||||
|
||||
@@ -5,435 +5,104 @@
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <unordered_map>
|
||||
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
typedef uint32_t A2BPCMSample;
|
||||
using PCMSample = int32_t;
|
||||
enum class PCMType : uint8_t {
|
||||
L16,
|
||||
L24,
|
||||
L32
|
||||
};
|
||||
|
||||
using ChannelMap = std::unordered_map<uint8_t, uint8_t>;
|
||||
|
||||
class A2BMessage : public Frame {
|
||||
private:
|
||||
class FrameView {
|
||||
private:
|
||||
class SampleView {
|
||||
public:
|
||||
SampleView(uint8_t* vPtr, uint8_t bps, size_t ind) :
|
||||
index(ind), viewPtr(vPtr), bytesPerSample(bps) {}
|
||||
|
||||
operator A2BPCMSample() const {
|
||||
if(!viewPtr) {
|
||||
return 0;
|
||||
}
|
||||
A2BPCMSample sample = 0;
|
||||
|
||||
std::copy(viewPtr+index*bytesPerSample, viewPtr+(index+1)*bytesPerSample, (uint8_t*)&sample);
|
||||
if(bytesPerSample == 4) {
|
||||
sample = sample >> 8;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
SampleView& operator=(A2BPCMSample sample) {
|
||||
if(!viewPtr) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if(bytesPerSample == 4) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
std::copy((uint8_t*)&sample, (uint8_t*)&sample + bytesPerSample, viewPtr + index*bytesPerSample);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SampleView(const SampleView&) = delete;
|
||||
SampleView& operator=(const SampleView&) = delete;
|
||||
|
||||
private:
|
||||
|
||||
size_t index;
|
||||
uint8_t* viewPtr;
|
||||
uint8_t bytesPerSample;
|
||||
};
|
||||
|
||||
public:
|
||||
FrameView(uint8_t* vPtr, uint8_t nChannels, uint8_t bps) : viewPtr(vPtr), tdm(nChannels), bytesPerSample(bps) {}
|
||||
|
||||
SampleView operator[](size_t index) {
|
||||
|
||||
if(index >= ((size_t)tdm) * 2) {
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error));
|
||||
return SampleView(nullptr, 0, 0);
|
||||
}
|
||||
return SampleView(viewPtr, bytesPerSample, index);
|
||||
}
|
||||
|
||||
FrameView& operator=(const std::vector<A2BPCMSample>& samples) {
|
||||
if(!viewPtr) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if(samples.size() != (size_t)(tdm)*2) {
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error));
|
||||
return *this;
|
||||
}
|
||||
|
||||
for(size_t icsChannel = 0; icsChannel < ((size_t)(tdm) * 2); icsChannel++) {
|
||||
operator[](icsChannel) = samples[icsChannel];
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FrameView(const FrameView&) = delete;
|
||||
FrameView& operator=(const FrameView&) = delete;
|
||||
private:
|
||||
uint8_t* viewPtr;
|
||||
uint8_t tdm;
|
||||
uint8_t bytesPerSample;
|
||||
};
|
||||
|
||||
public:
|
||||
enum class A2BDirection : uint8_t {
|
||||
static constexpr size_t maxAudioBufferSize = 2048;
|
||||
|
||||
enum class TDMMode : uint8_t {
|
||||
TDM2 = 0,
|
||||
TDM4 = 1,
|
||||
TDM8 = 2,
|
||||
TDM12 = 3,
|
||||
TDM16 = 4,
|
||||
TDM20 = 5,
|
||||
TDM24 = 6,
|
||||
TDM32 = 7,
|
||||
};
|
||||
|
||||
static uint8_t tdmToChannelNum(TDMMode tdm);
|
||||
|
||||
enum class Direction : uint8_t {
|
||||
Downstream = 0,
|
||||
Upstream = 1
|
||||
};
|
||||
|
||||
A2BMessage(uint8_t nChannels, bool chSize16, size_t size) :
|
||||
numChannels(nChannels),
|
||||
channelSize16(chSize16)
|
||||
{
|
||||
data.resize(std::min(roundNextMultiple(size, getFrameSize()),(size_t)maxSize), 0);
|
||||
}
|
||||
|
||||
bool allocateSpace(size_t numSpaceToAdd) {
|
||||
size_t spaceToAdd = roundNextMultiple(numSpaceToAdd, getFrameSize());
|
||||
|
||||
if(spaceToAdd + data.size() > maxSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data.resize(data.size() + numSpaceToAdd, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addFrame(const std::vector<A2BPCMSample>& frame) {
|
||||
if(frame.size() != ((size_t)numChannels)*2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t oldSize = data.size();
|
||||
|
||||
if(!allocateSpace(getFrameSize())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = data.begin() + oldSize;
|
||||
size_t offset = 0;
|
||||
for(A2BPCMSample sample: frame) {
|
||||
if(!channelSize16) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
std::copy((uint8_t*)&sample, (uint8_t*)&sample + getBytesPerSample(), it + offset);
|
||||
offset+=getBytesPerSample();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setFrame(const std::vector<A2BPCMSample>& frame, size_t frameNum) {
|
||||
if(frame.size() != ((size_t)numChannels)*2 || frameNum >= getNumFrames()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = data.begin() + frameNum*getFrameSize();
|
||||
size_t offset = 0;
|
||||
for(A2BPCMSample sample: frame) {
|
||||
if(!channelSize16) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
std::copy((uint8_t*)&sample, (uint8_t*)&sample + getBytesPerSample(), it + offset);
|
||||
offset+=getBytesPerSample();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fillChannelAudioBuffer(A2BDirection dir, uint8_t channel, std::vector<uint8_t>& channelBuffer) const {
|
||||
if(channel >= numChannels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample();
|
||||
|
||||
for(size_t frame = 0; frame < getNumFrames(); frame++, offset += getFrameSize()) {
|
||||
std::copy(data.begin() + offset, data.end() + offset + getBytesPerSample(), std::back_inserter(channelBuffer));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fillChannelStream(A2BDirection dir, uint8_t channel, std::unique_ptr<std::ostream>& channelStream) const {
|
||||
if(channel >= numChannels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample();
|
||||
|
||||
for(size_t frame = 0; frame < getNumFrames(); frame++, offset += getFrameSize()) {
|
||||
channelStream->write((const char*)(data.data() + offset), getBytesPerSample());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void fill(A2BPCMSample sample) {
|
||||
uint8_t* buf = data.data();
|
||||
|
||||
if(channelSize16) {
|
||||
uint16_t sample16bit = sample & 0xFF;
|
||||
uint16_t* samps = (uint16_t*)buf;
|
||||
std::fill(samps, samps + data.size()/2, sample16bit);
|
||||
}
|
||||
else {
|
||||
A2BPCMSample* samps = (A2BPCMSample*)buf;
|
||||
sample = sample << 8;
|
||||
std::fill(samps, samps + data.size()/4, sample);
|
||||
}
|
||||
}
|
||||
|
||||
bool fillFrame(A2BPCMSample sample, size_t frame) {
|
||||
if(frame >= getNumFrames()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* buf = data.data();
|
||||
size_t start = 2 * numChannels * frame;
|
||||
size_t end = 2 * numChannels * (frame+1);
|
||||
|
||||
if(channelSize16) {
|
||||
uint16_t sample16bit = sample & 0xFF;
|
||||
uint16_t* samps = (uint16_t*)buf;
|
||||
std::fill(samps+start, samps + end, sample16bit);
|
||||
}
|
||||
else {
|
||||
A2BPCMSample* samps = (A2BPCMSample*)buf;
|
||||
sample = sample << 8;
|
||||
std::fill(samps+start, samps + end, sample);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
bool setAudioBuffer(Iterator begin, Iterator end, A2BDirection dir, uint8_t channel, uint32_t frame) {
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
|
||||
size_t dist = (size_t)(std::distance(begin, end));
|
||||
|
||||
if(dist > (data.size() - offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy(begin, end, data.begin() + offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
bool setAudioBuffer(Iterator begin, Iterator end) {
|
||||
return setAudioBuffer(begin, end, A2BMessage::A2BDirection::Downstream, 0, 0);
|
||||
}
|
||||
|
||||
std::optional<A2BPCMSample> getSample(A2BDirection dir, uint8_t channel, uint32_t frame) const {
|
||||
if(
|
||||
channel >= numChannels ||
|
||||
frame >= getNumFrames()
|
||||
) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
A2BPCMSample sample = 0;
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
|
||||
|
||||
std::copy(data.begin() + offset, data.begin() + offset + getBytesPerSample(), (uint8_t*)&sample);
|
||||
if(channelSize16) {
|
||||
sample = sample >> 8;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
std::optional<A2BPCMSample> getSample(size_t sampleNum) const {
|
||||
if(sampleNum >= getNumSamples()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
A2BPCMSample sample = 0;
|
||||
size_t offset = sampleNum*getBytesPerSample();
|
||||
|
||||
std::copy(data.begin() + offset, data.begin() + offset + getBytesPerSample(), (uint8_t*)&sample);
|
||||
|
||||
if(channelSize16) {
|
||||
sample = sample >> 8;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
bool setSample(A2BDirection dir, uint8_t channel, uint32_t frame, A2BPCMSample sample) {
|
||||
if(
|
||||
channel >= numChannels ||
|
||||
frame >= getNumFrames()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
|
||||
|
||||
if(!channelSize16) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
uint8_t* sampToBytes = (uint8_t*)&sample;
|
||||
std::copy(sampToBytes,sampToBytes+getBytesPerSample(), data.begin() + offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool setSample(uint8_t icsChannel, uint32_t frame, A2BPCMSample sample) {
|
||||
if(
|
||||
icsChannel >= (2*numChannels) ||
|
||||
frame >= getNumFrames()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = ((size_t)icsChannel)*getBytesPerSample() + frame * getFrameSize();
|
||||
|
||||
if(!channelSize16) {
|
||||
sample = sample << 8;
|
||||
}
|
||||
uint8_t* sampToBytes = (uint8_t*)&sample;
|
||||
std::copy(sampToBytes,sampToBytes+getBytesPerSample(), data.begin() + offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FrameView operator[](size_t index) {
|
||||
if(index >= getNumFrames()) {
|
||||
EventManager::GetInstance().add(APIEvent(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error));
|
||||
return FrameView(nullptr, 0, 0);
|
||||
}
|
||||
|
||||
return FrameView(data.data() + index*getFrameSize(), numChannels, getBytesPerSample());
|
||||
}
|
||||
|
||||
size_t getNumSamples() const {
|
||||
return data.size()/((size_t)getBytesPerSample());
|
||||
}
|
||||
|
||||
uint8_t getNumChannels() const {
|
||||
return numChannels;
|
||||
}
|
||||
|
||||
uint8_t getBitDepth() const {
|
||||
return channelSize16 ? 16 : 24;
|
||||
}
|
||||
|
||||
uint8_t getBytesPerSample() const {
|
||||
return channelSize16 ? 2 : 4;
|
||||
}
|
||||
|
||||
bool isTxMsg() const {
|
||||
return txmsg;
|
||||
}
|
||||
|
||||
void setTxMsgBit(bool bit) {
|
||||
txmsg = bit;
|
||||
}
|
||||
|
||||
bool isMonitorMsg() const {
|
||||
return monitor;
|
||||
}
|
||||
|
||||
void setMonitorBit(bool bit) {
|
||||
monitor = bit;
|
||||
}
|
||||
|
||||
bool isErrIndicator() const {
|
||||
return errIndicator;
|
||||
}
|
||||
|
||||
void setErrIndicatorBit(bool bit) {
|
||||
errIndicator = bit;
|
||||
}
|
||||
|
||||
bool isSyncFrame() const {
|
||||
return syncFrame;
|
||||
}
|
||||
|
||||
void setSyncFrameBit(bool bit) {
|
||||
syncFrame = bit;
|
||||
}
|
||||
|
||||
uint16_t getRFU2() const {
|
||||
return rfu2;
|
||||
}
|
||||
|
||||
void setRFU2(uint16_t newRfu2) {
|
||||
rfu2 = newRfu2;
|
||||
}
|
||||
|
||||
size_t getFrameSize() const {
|
||||
return 2*((size_t)numChannels) * ((size_t)getBytesPerSample());
|
||||
}
|
||||
|
||||
size_t getNumFrames() const {
|
||||
return data.size() / getFrameSize();
|
||||
}
|
||||
|
||||
size_t getAudioBufferSize() const {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
const uint8_t* getAudioBuffer() const {
|
||||
return data.data();
|
||||
}
|
||||
|
||||
|
||||
static constexpr uint32_t maxSize = 2048;
|
||||
private:
|
||||
|
||||
uint16_t rfu2 = 0;
|
||||
uint8_t numChannels = 0;
|
||||
bool channelSize16 = false;
|
||||
bool monitor = false;
|
||||
bool txmsg = false;
|
||||
bool errIndicator = false;
|
||||
bool syncFrame = false;
|
||||
uint16_t rfu2 = 0;
|
||||
|
||||
size_t roundNextMultiple(size_t x, size_t y) const {
|
||||
if(y==0) {
|
||||
return 0;
|
||||
}
|
||||
else if(x%y == 0) {
|
||||
return x;
|
||||
}
|
||||
A2BMessage() = default;
|
||||
/**
|
||||
* Creates a new A2BMessage
|
||||
*
|
||||
* @param numFrames The number of audio frames to hold in the message audio buffer
|
||||
* @param tdm The TDM mode to transmit this message to, note this variable determines the number of channels
|
||||
* @param chSize16 True if the message channel sizes are 16 bit, false for 32 bit.
|
||||
*/
|
||||
A2BMessage(size_t numFrames, TDMMode tdm, bool chSize16);
|
||||
|
||||
return x + y - (x%y);
|
||||
}
|
||||
/**
|
||||
* Creates a new A2BMessage with the maximum number of possible frames
|
||||
*
|
||||
* @param tdm The TDM mode to transmit this message to, note this variable determines the number of channels
|
||||
* @param chSize16 True if the message channel sizes are 16 bit, false for 32 bit.
|
||||
*/
|
||||
A2BMessage(TDMMode tdm, bool chSize16);
|
||||
|
||||
size_t getChannelIndex(A2BDirection dir, uint8_t channel) const {
|
||||
size_t channelIndex = 2 * ((size_t)channel);
|
||||
/**
|
||||
* Loads A2BMessage audio buffer from a IWAVStream object representing a WAV data-stream
|
||||
*
|
||||
* @param wavStream The WAV data-stream the audio buffer with
|
||||
* @param channelMap A map which maps a message channel to a wav channel. See docs for A2B message channel format
|
||||
* @returns true on successful load, false otherwise
|
||||
*/
|
||||
bool loadAudioBuffer(IWAVStream& wavStream, const ChannelMap& channelMap);
|
||||
|
||||
if(dir == A2BDirection::Upstream) {
|
||||
channelIndex++;
|
||||
}
|
||||
/**
|
||||
* Get a PCM sample from the audio buffer. If the desired pcmType is larger than the channel size,
|
||||
* the output will be a PCM sample which is scaled up.
|
||||
*
|
||||
* @param dir The direction of the A2B stream
|
||||
* @param channel The desired channel to read a PCM sample from
|
||||
* @param frame The desired frame to read a PCM sample from
|
||||
* @param pcmType The interpretted bit depth of the audio buffer sample
|
||||
*/
|
||||
PCMSample getChannelSample(Direction dir, uint8_t channel, size_t frame, PCMType pcmType) const;
|
||||
|
||||
return channelIndex;
|
||||
}
|
||||
/**
|
||||
* Write a PCM sample to the audio buffer
|
||||
*
|
||||
* @param dir The direction of the A2B stream
|
||||
* @param channel The desired channel to write a PCM sample to
|
||||
* @param frame The desired frame to write a PCM sample to
|
||||
* @param sampleToSet The PCM sample which will be written to the buffer
|
||||
* @param pcmType The interpretted bit depth of the sample to write
|
||||
*/
|
||||
void setChannelSample(Direction dir, uint8_t channel, size_t frame, PCMSample sampleToSet, PCMType pcmType);
|
||||
|
||||
size_t getFrameSize() const;
|
||||
size_t getSampleOffset(Direction dir, uint8_t channel, size_t frame) const;
|
||||
uint8_t getBytesPerChannel() const;
|
||||
size_t getNumFrames() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef __APPERRORMESSAGE_H_
|
||||
#define __APPERRORMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
enum class AppErrorType : uint16_t {
|
||||
AppErrorRxMessagesFull = 0,
|
||||
AppErrorTxMessagesFull = 1,
|
||||
AppErrorTxReportMessagesFull = 2,
|
||||
AppErrorBadCommWithDspIC = 3,
|
||||
AppErrorDriverOverflow = 4,
|
||||
AppErrorPCBuffOverflow = 5,
|
||||
AppErrorPCChksumError = 6,
|
||||
AppErrorPCMissedByte = 7,
|
||||
AppErrorPCOverrunError = 8,
|
||||
AppErrorSettingFailure = 9,
|
||||
AppErrorTooManySelectedNetworks = 10,
|
||||
AppErrorNetworkNotEnabled = 11,
|
||||
AppErrorRtcNotCorrect = 12,
|
||||
AppErrorLoadedDefaultSettings = 13,
|
||||
AppErrorFeatureNotUnlocked = 14,
|
||||
AppErrorFeatureRtcCmdDropped = 15,
|
||||
AppErrorTxMessagesFlushed = 16,
|
||||
AppErrorTxMessagesHalfFull = 17,
|
||||
AppErrorNetworkNotValid = 18,
|
||||
AppErrorTxInterfaceNotImplemented = 19,
|
||||
AppErrorTxMessagesCommEnableIsOff = 20,
|
||||
AppErrorRxFilterMatchCountExceeded = 21,
|
||||
AppErrorEthPreemptionNotEnabled = 22,
|
||||
AppErrorTxNotSupportedInMode = 23,
|
||||
AppErrorJumboFramesNotSupported = 24,
|
||||
AppErrorEthernetIpFragment = 25,
|
||||
AppErrorTxMessagesUnderrun = 26,
|
||||
AppErrorDeviceFanFailure = 27,
|
||||
AppErrorDeviceOvertemperature = 28,
|
||||
AppErrorTxMessageIndexOutOfRange = 29,
|
||||
AppErrorUndersizedFrameDropped = 30,
|
||||
AppErrorOversizedFrameDropped = 31,
|
||||
AppErrorWatchdogEvent = 32,
|
||||
AppErrorSystemClockFailure = 33,
|
||||
AppErrorSystemClockRecovered = 34,
|
||||
AppErrorSystemPeripheralReset = 35,
|
||||
AppErrorSystemCommunicationFailure = 36,
|
||||
AppErrorTxMessagesUnsupportedSourceOrPacketId = 37,
|
||||
AppErrorWbmsManagerConnectFailed = 38,
|
||||
AppErrorWbmsManagerConnectBadState = 39,
|
||||
AppErrorWbmsManagerConnectTimeout = 40,
|
||||
AppErrorFailedToInitializeLoggerDisk = 41,
|
||||
AppErrorInvalidSetting = 42,
|
||||
AppErrorSystemFailureRequestedReset = 43,
|
||||
AppErrorPortKeyMistmatch = 45,
|
||||
AppErrorBusFailure = 46,
|
||||
AppErrorTapOverflow = 47,
|
||||
AppErrorEthTxNoLink = 48,
|
||||
AppErrorErrorBufferOverflow = 254,
|
||||
AppNoError = 255
|
||||
};
|
||||
|
||||
class AppErrorMessage : public RawMessage {
|
||||
public:
|
||||
AppErrorMessage() : RawMessage(Message::Type::AppError, Network::NetID::RED_App_Error) {}
|
||||
uint16_t errorType;
|
||||
Network::NetID errorNetID;
|
||||
uint32_t timestamp10us;
|
||||
uint32_t timestamp10usMSB;
|
||||
|
||||
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
|
||||
AppErrorType getAppErrorType();
|
||||
std::string getAppErrorString();
|
||||
};
|
||||
|
||||
} // namespace icsneo
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif
|
||||
@@ -1,85 +0,0 @@
|
||||
#ifndef __A2BDECODER_H_
|
||||
#define __A2BDECODER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
|
||||
#include "icsneo/communication/message/a2bmessage.h"
|
||||
#include "icsneo/device/device.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
typedef uint8_t Channel;
|
||||
|
||||
class A2BAudioChannelMap {
|
||||
public:
|
||||
|
||||
A2BAudioChannelMap(uint8_t tdm);
|
||||
|
||||
void set(Channel outChannel, A2BMessage::A2BDirection dir, Channel inChannel);
|
||||
void setAll(Channel inChannel);
|
||||
|
||||
Channel get(Channel outChannel, A2BMessage::A2BDirection dir) const;
|
||||
|
||||
size_t size() const;
|
||||
|
||||
uint8_t getTDM() const;
|
||||
Channel& operator[](size_t idx);
|
||||
|
||||
operator const std::vector<Channel>&() const;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
size_t getChannelIndex(Channel channel, A2BMessage::A2BDirection dir) const;
|
||||
|
||||
std::vector<Channel> rawMap;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class A2BDecoder {
|
||||
public:
|
||||
|
||||
A2BDecoder(
|
||||
std::unique_ptr<std::istream>&& streamOut,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
);
|
||||
|
||||
A2BDecoder(
|
||||
const char* filename,
|
||||
bool chSize16,
|
||||
const A2BAudioChannelMap& chMap
|
||||
);
|
||||
|
||||
operator bool() const;
|
||||
|
||||
std::shared_ptr<A2BMessage> decode();
|
||||
|
||||
bool outputAll(std::shared_ptr<Device> &device);
|
||||
|
||||
std::unique_ptr<std::istream> stream;
|
||||
private:
|
||||
|
||||
void initializeFromHeader();
|
||||
|
||||
uint8_t tdm;
|
||||
uint8_t audioBytesPerSample;
|
||||
uint8_t channelsInWave;
|
||||
bool channelSize16;
|
||||
A2BAudioChannelMap channelMap;
|
||||
|
||||
std::vector<uint8_t> frame;
|
||||
std::vector<uint8_t> frameWave;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -9,33 +9,73 @@
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
/**
|
||||
* A message callback which injests A2BMessage PCM data and formats it into a WAV file
|
||||
*/
|
||||
class A2BWAVOutput : public StreamOutput {
|
||||
public:
|
||||
A2BWAVOutput(const char* filename, uint32_t sampleRate = 44100)
|
||||
: StreamOutput(filename), wavSampleRate(sampleRate) {}
|
||||
static constexpr size_t wavBufferSize = 1024 * 32;
|
||||
|
||||
A2BWAVOutput(std::unique_ptr<std::ostream>&& os, uint32_t sampleRate = 44100)
|
||||
: StreamOutput(std::move(os)), wavSampleRate(sampleRate) {}
|
||||
/**
|
||||
* Creates a new A2BWAVOutput object
|
||||
*
|
||||
* @param filename Name of desired output WAV file
|
||||
* @param channelMap A map which maps a channel in the output WAV file to a channel in received messages. See docs for specific channel format in messages
|
||||
* @param bitDepth The size of the samples in the WAV file.
|
||||
* @param numWAVChannels The number of channels in the output WAV file
|
||||
* @param sampleRate The output WAV file sample rate
|
||||
*/
|
||||
A2BWAVOutput(
|
||||
const char* filename,
|
||||
const ChannelMap& channelMap,
|
||||
PCMType bitDepth,
|
||||
size_t numWAVChannels,
|
||||
uint32_t sampleRate = 48000
|
||||
);
|
||||
|
||||
void writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const;
|
||||
/**
|
||||
* Creates a new A2BWAVOutput object
|
||||
*
|
||||
* @param os A std::ostream object which represents this WAV file
|
||||
* @param channelMap A map which maps a channel in the output WAV file to a channel in received messages. See docs for specific channel format in messages
|
||||
* @param bitDepth The size of the samples in the WAV file.
|
||||
* @param numWAVChannels The number of channels in the output WAV file
|
||||
* @param sampleRate The output WAV file sample rate
|
||||
*/
|
||||
A2BWAVOutput(
|
||||
std::ostream& os,
|
||||
const ChannelMap& channelMap,
|
||||
PCMType bitDepth,
|
||||
size_t numWAVChannels,
|
||||
uint32_t sampleRate = 48000
|
||||
);
|
||||
|
||||
bool callIfMatch(const std::shared_ptr<Message>& message) const override;
|
||||
|
||||
void close() const;
|
||||
|
||||
~A2BWAVOutput() override {
|
||||
if(!closed) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
~A2BWAVOutput() override;
|
||||
|
||||
protected:
|
||||
void close() const;
|
||||
bool initialize();
|
||||
|
||||
uint32_t wavSampleRate;
|
||||
/**
|
||||
* Write and clear the current stored audio buffer
|
||||
*/
|
||||
bool writeCurrentBuffer() const;
|
||||
|
||||
mutable std::vector<uint8_t> wavBuffer; // A buffer which is used to cache PCM data to write to disk later
|
||||
mutable size_t wavBufferOffset = 0; // Current offset in the above buffer, gets incremented as data is read into buffer
|
||||
uint32_t wavSampleRate; // The output WAV sample rate
|
||||
|
||||
size_t bytesPerSampleWAV; // The number of bytes per sample in the output WAV file
|
||||
size_t numChannelsWAV; // The number of channels in the output WAV file
|
||||
|
||||
ChannelMap chMap; // A map which maps a WAV channel to a A2BMessage channel
|
||||
size_t maxMessageChannel; // The highest message channel in the above channel map, this variable is used for error checking
|
||||
|
||||
bool initialized = false;
|
||||
mutable uint32_t streamStartPos;
|
||||
mutable bool firstMessageFlag = true;
|
||||
mutable bool closed = false;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -14,33 +14,34 @@
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
struct WaveFileHeader {
|
||||
#pragma pack(push, 1)
|
||||
struct WAVHeader {
|
||||
|
||||
static constexpr uint32_t WAVE_CHUNK_ID = 0x46464952; // "RIFF"
|
||||
static constexpr uint32_t WAVE_FORMAT = 0x45564157; // "WAVE"
|
||||
static constexpr uint32_t WAVE_SUBCHUNK1_ID = 0x20746d66; // "fmt "
|
||||
static constexpr uint32_t WAVE_SUBCHUNK2_ID = 0x61746164; // "data"
|
||||
static constexpr uint16_t WAVE_SUBCHUNK1_SIZE = 16;
|
||||
static constexpr uint16_t WAVE_AUDIO_FORMAT_PCM = 1;
|
||||
static constexpr uint32_t WAVE_DEFAULT_SIZE = 0; // Default size for streamed wav
|
||||
static constexpr uint32_t WAV_CHUNK_ID = 0x46464952; // "RIFF"
|
||||
static constexpr uint32_t WAV_FORMAT = 0x45564157; // "WAV"
|
||||
static constexpr uint32_t WAV_SUBCHUNK1_ID = 0x20746d66; // "fmt "
|
||||
static constexpr uint32_t WAV_SUBCHUNK2_ID = 0x61746164; // "data"
|
||||
static constexpr uint16_t WAV_SUBCHUNK1_SIZE = 16;
|
||||
static constexpr uint16_t WAV_AUDIO_FORMAT_PCM = 1;
|
||||
static constexpr uint32_t WAV_DEFAULT_SIZE = 0; // Default size for streamed wav
|
||||
|
||||
uint32_t chunkId = WAVE_CHUNK_ID; // "RIFF"
|
||||
uint32_t chunkSize = WAVE_DEFAULT_SIZE; // number of bytes to follow
|
||||
uint32_t format = WAVE_FORMAT; // "WAVE"
|
||||
uint32_t subchunk1Id = WAVE_SUBCHUNK1_ID; // "fmt "
|
||||
uint32_t subchunk1Size = WAVE_SUBCHUNK1_SIZE; // number of bytes in *this* subchunk (always 16)
|
||||
uint16_t audioFormat = WAVE_AUDIO_FORMAT_PCM; // 1 for PCM
|
||||
uint32_t chunkId = WAV_CHUNK_ID; // "RIFF"
|
||||
uint32_t chunkSize = WAV_DEFAULT_SIZE; // number of bytes to follow
|
||||
uint32_t format = WAV_FORMAT; // "WAV"
|
||||
uint32_t subchunk1Id = WAV_SUBCHUNK1_ID; // "fmt "
|
||||
uint32_t subchunk1Size = WAV_SUBCHUNK1_SIZE; // number of bytes in *this* subchunk (always 16)
|
||||
uint16_t audioFormat = WAV_AUDIO_FORMAT_PCM; // 1 for PCM
|
||||
uint16_t numChannels; // number of channels
|
||||
uint32_t sampleRate; // sample rate in Hz
|
||||
uint32_t byteRate; // bytes per second of audio: sampleRate * numChannels * (bitsPerSample / 8)
|
||||
uint16_t blockAlign; // alignment of each block in bytes: numChannels * (bitsPerSample / 8)
|
||||
uint16_t bitsPerSample; // number of bits in each sample
|
||||
uint32_t subchunk2Id = WAVE_SUBCHUNK2_ID; // "data"
|
||||
uint32_t subchunk2Size = WAVE_DEFAULT_SIZE; // number of bytes to follow
|
||||
uint32_t subchunk2Id = WAV_SUBCHUNK2_ID; // "data"
|
||||
uint32_t subchunk2Size = WAV_DEFAULT_SIZE; // number of bytes to follow
|
||||
|
||||
WaveFileHeader() = default;
|
||||
WAVHeader() = default;
|
||||
|
||||
WaveFileHeader(uint16_t nChannels, uint32_t sRate, uint16_t bps, uint32_t nSamples = 0) {
|
||||
WAVHeader(uint16_t nChannels, uint32_t sRate, uint16_t bps, uint32_t nSamples = 0) {
|
||||
setHeader(nChannels, sRate, bps, nSamples);
|
||||
}
|
||||
|
||||
@@ -59,48 +60,82 @@ struct WaveFileHeader {
|
||||
subchunk2Size = numSamples * numChannels * (bitsPerSample / 8);
|
||||
chunkSize = subchunk2Size + 36;
|
||||
}
|
||||
};
|
||||
|
||||
void write(const std::unique_ptr<std::ostream>& stream) {
|
||||
#pragma pack(pop)
|
||||
|
||||
stream->write(reinterpret_cast<const char*>(&chunkId), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&format), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&subchunk1Id), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&subchunk1Size), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&audioFormat), 2);
|
||||
stream->write(reinterpret_cast<const char*>(&numChannels), 2);
|
||||
stream->write(reinterpret_cast<const char*>(&sampleRate), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&byteRate), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&blockAlign), 2);
|
||||
stream->write(reinterpret_cast<const char*>(&bitsPerSample), 2);
|
||||
stream->write(reinterpret_cast<const char*>(&subchunk2Id), 4);
|
||||
stream->write(reinterpret_cast<const char*>(&subchunk2Size), 4);
|
||||
class IWAVStream {
|
||||
private:
|
||||
std::unique_ptr<std::istream, std::function<void(std::istream*)>> stream;
|
||||
bool initialized = false;
|
||||
public:
|
||||
WAVHeader header;
|
||||
|
||||
IWAVStream(std::istream& WAVInput)
|
||||
: stream(&WAVInput, [](std::istream*){}) {
|
||||
|
||||
if(initialize()) {
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
IWAVStream(const char* filename)
|
||||
: stream(new std::ifstream(filename, std::ios::in | std::ios::binary), std::default_delete<std::istream>()) {
|
||||
|
||||
if(initialize()) {
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool initialize() {
|
||||
return !(!stream->read(reinterpret_cast<char*>(&header), sizeof(WAVHeader)));
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return initialized && stream && stream->good();
|
||||
}
|
||||
|
||||
bool read(char* into, std::streamsize num) {
|
||||
return !(!stream->read(into, num));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stream immediately after WAV header
|
||||
*/
|
||||
void reset() {
|
||||
if(!(*this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
stream->clear();
|
||||
stream->seekg(sizeof(icsneo::WAVHeader), std::ios::beg);
|
||||
}
|
||||
};
|
||||
|
||||
class StreamOutput : public MessageCallback {
|
||||
public:
|
||||
StreamOutput(std::unique_ptr<std::ostream>&& os, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
|
||||
: MessageCallback(cb, f), stream(std::move(os)) {}
|
||||
StreamOutput(std::ostream& os, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
|
||||
: MessageCallback(cb, f), stream(&os, [](std::ostream*){}) {}
|
||||
|
||||
StreamOutput(const char* filename, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
|
||||
: MessageCallback(cb, f) {
|
||||
stream = std::make_unique<std::ofstream>(filename, std::ios::binary);
|
||||
}
|
||||
:
|
||||
MessageCallback(cb, f),
|
||||
stream(
|
||||
new std::ofstream(filename, std::ios::binary),
|
||||
std::default_delete<std::ostream>()
|
||||
) {}
|
||||
|
||||
StreamOutput(const char* filename) : MessageCallback([](std::shared_ptr<Message> msg) {}) {
|
||||
stream = std::make_unique<std::ofstream>(filename, std::ios::binary);
|
||||
}
|
||||
StreamOutput(const char* filename) :
|
||||
MessageCallback([](std::shared_ptr<Message> msg) {}),
|
||||
stream(
|
||||
new std::ofstream(filename, std::ios::binary),
|
||||
std::default_delete<std::ostream>()
|
||||
) {}
|
||||
|
||||
StreamOutput(std::unique_ptr<std::ostream>&& os) : MessageCallback([](std::shared_ptr<Message> msg) {}), stream(std::move(os)) {}
|
||||
StreamOutput(std::ostream& os) : MessageCallback([](std::shared_ptr<Message> msg) {}), stream(&os, [](std::ostream*){}) {}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<std::ostream> stream;
|
||||
|
||||
void write(void* msg, std::streamsize size) const {
|
||||
stream->write(reinterpret_cast<const char*>(msg), size);
|
||||
}
|
||||
std::unique_ptr<std::ostream, std::function<void(std::ostream*)>> stream;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,20 @@ class ComponentVersion {
|
||||
public:
|
||||
ComponentVersion(uint8_t valid, uint8_t componentInfo, uint32_t identifier, uint32_t dotVersion, uint32_t commitHash) :
|
||||
valid(valid), componentInfo(componentInfo), identifier(identifier), dotVersion(dotVersion), commitHash(commitHash) {}
|
||||
|
||||
static ComponentVersion FromAppVersion(uint32_t identifier, uint8_t appMajor, uint8_t appMinor) {
|
||||
uint32_t dotVersion = (appMajor << 24) | (appMinor << 16);
|
||||
|
||||
|
||||
return ComponentVersion(
|
||||
static_cast<uint8_t>(1u),
|
||||
static_cast<uint8_t>(0u),
|
||||
identifier,
|
||||
dotVersion,
|
||||
static_cast<uint8_t>(0u)
|
||||
);
|
||||
}
|
||||
|
||||
const bool valid;
|
||||
const uint8_t componentInfo;
|
||||
const uint32_t identifier;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class ExtendedDataMessage : public RawMessage {
|
||||
class ExtendedDataMessage : public Frame {
|
||||
public:
|
||||
#pragma pack(push, 2)
|
||||
struct ExtendedDataHeader {
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
static constexpr size_t MaxExtendedDataBufferSize = 2048;
|
||||
const ExtendedDataHeader header;
|
||||
|
||||
ExtendedDataMessage(ExtendedDataHeader params) : RawMessage(Message::Type::RawMessage, Network::NetID::ExtendedData), header{params} {}
|
||||
ExtendedDataMessage(ExtendedDataHeader params) : header{params} {}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef __FLASHMEMORYMESSAGE_H_
|
||||
#define __FLASHMEMORYMESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class FlashMemoryMessage : public RawMessage {
|
||||
public:
|
||||
FlashMemoryMessage() : RawMessage(Message::Type::RawMessage, Network::NetID::RED_INT_MEMORYREAD) {}
|
||||
uint16_t startAddress = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef _HARDWARE_INFO_MESSAGE_H_
|
||||
#define _HARDWARE_INFO_MESSAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "icsneo/communication/message/message.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class HardwareInfo : public Message {
|
||||
public:
|
||||
HardwareInfo() : Message(Message::Type::HardwareInfo) {}
|
||||
struct Version {
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
};
|
||||
|
||||
struct Date {
|
||||
uint8_t day;
|
||||
uint8_t month;
|
||||
uint16_t year;
|
||||
};
|
||||
|
||||
Date manufactureDate;
|
||||
Version hardwareRevision;
|
||||
Version bootloaderVersion;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -38,6 +38,9 @@ public:
|
||||
SupportedFeatures = 0x800d,
|
||||
GenericBinaryStatus = 0x800e,
|
||||
LiveData = 0x800f,
|
||||
HardwareInfo = 0x8010,
|
||||
TC10Status = 0x8011,
|
||||
AppError = 0x8012,
|
||||
};
|
||||
|
||||
Message(Type t) : type(t) {}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef __TC10STATUSMESSAGE_H
|
||||
#define __TC10STATUSMESSAGE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/message/message.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace icsneo
|
||||
{
|
||||
|
||||
enum class TC10WakeStatus : uint8_t {
|
||||
NoWakeReceived,
|
||||
WakeReceived,
|
||||
};
|
||||
|
||||
enum class TC10SleepStatus : uint8_t {
|
||||
NoSleepReceived,
|
||||
SleepReceived,
|
||||
SleepFailed,
|
||||
SleepAborted,
|
||||
};
|
||||
|
||||
class TC10StatusMessage : public Message {
|
||||
public:
|
||||
static std::shared_ptr<TC10StatusMessage> DecodeToMessage(const std::vector<uint8_t>& bytestream);
|
||||
|
||||
TC10StatusMessage(const TC10WakeStatus& wakeStatus, const TC10SleepStatus& sleepStatus) :
|
||||
Message(Type::TC10Status), wakeStatus(wakeStatus), sleepStatus(sleepStatus) {}
|
||||
|
||||
const TC10WakeStatus wakeStatus;
|
||||
const TC10SleepStatus sleepStatus;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -166,6 +166,24 @@ public:
|
||||
MDIO6 = 550,
|
||||
MDIO7 = 551,
|
||||
MDIO8 = 552,
|
||||
OP_Ethernet13 = 553,
|
||||
OP_Ethernet14 = 554,
|
||||
OP_Ethernet15 = 555,
|
||||
OP_Ethernet16 = 556,
|
||||
SPI3 = 557,
|
||||
SPI4 = 558,
|
||||
SPI5 = 559,
|
||||
SPI6 = 560,
|
||||
SPI7 = 561,
|
||||
SPI8 = 562,
|
||||
LIN9 = 563,
|
||||
LIN10 = 564,
|
||||
LIN11 = 565,
|
||||
LIN12 = 566,
|
||||
LIN13 = 567,
|
||||
LIN14 = 568,
|
||||
LIN15 = 569,
|
||||
LIN16 = 570,
|
||||
Any = 0xfffe, // Never actually set as type, but used as flag for filtering
|
||||
Invalid = 0xffff
|
||||
};
|
||||
@@ -270,6 +288,24 @@ public:
|
||||
MDIO6 = 80,
|
||||
MDIO7 = 81,
|
||||
MDIO8 = 82,
|
||||
OP_Ethernet13 = 83,
|
||||
OP_Ethernet14 = 84,
|
||||
OP_Ethernet15 = 85,
|
||||
OP_Ethernet16 = 86,
|
||||
SPI3 = 87,
|
||||
SPI4 = 88,
|
||||
SPI5 = 89,
|
||||
SPI6 = 90,
|
||||
SPI7 = 91,
|
||||
SPI8 = 92,
|
||||
LIN9 = 93,
|
||||
LIN10 = 94,
|
||||
LIN11 = 95,
|
||||
LIN12 = 96,
|
||||
LIN13 = 97,
|
||||
LIN14 = 98,
|
||||
LIN15 = 99,
|
||||
LIN16 = 100,
|
||||
};
|
||||
static const char* GetTypeString(Type type) {
|
||||
switch(type) {
|
||||
@@ -506,6 +542,14 @@ public:
|
||||
case NetID::LIN6:
|
||||
case NetID::LIN7:
|
||||
case NetID::LIN8:
|
||||
case NetID::LIN9:
|
||||
case NetID::LIN10:
|
||||
case NetID::LIN11:
|
||||
case NetID::LIN12:
|
||||
case NetID::LIN13:
|
||||
case NetID::LIN14:
|
||||
case NetID::LIN15:
|
||||
case NetID::LIN16:
|
||||
return Type::LIN;
|
||||
case NetID::FlexRay:
|
||||
case NetID::FlexRay1a:
|
||||
@@ -533,10 +577,12 @@ public:
|
||||
case NetID::CoreMiniPreLoad:
|
||||
case NetID::ExtendedCommand:
|
||||
case NetID::ExtendedData:
|
||||
case NetID::RED_INT_MEMORYREAD:
|
||||
case NetID::NeoMemorySDRead:
|
||||
case NetID::NeoMemoryWriteDone:
|
||||
case NetID::RED_GET_RTC:
|
||||
case NetID::DiskData:
|
||||
case NetID::RED_App_Error:
|
||||
return Type::Internal;
|
||||
case NetID::Invalid:
|
||||
case NetID::Any:
|
||||
@@ -557,6 +603,10 @@ public:
|
||||
case NetID::OP_Ethernet10:
|
||||
case NetID::OP_Ethernet11:
|
||||
case NetID::OP_Ethernet12:
|
||||
case NetID::OP_Ethernet13:
|
||||
case NetID::OP_Ethernet14:
|
||||
case NetID::OP_Ethernet15:
|
||||
case NetID::OP_Ethernet16:
|
||||
return Type::Ethernet;
|
||||
case NetID::LSFTCAN:
|
||||
case NetID::LSFTCAN2:
|
||||
@@ -579,6 +629,12 @@ public:
|
||||
return Type::A2B;
|
||||
case NetID::SPI1:
|
||||
case NetID::SPI2:
|
||||
case NetID::SPI3:
|
||||
case NetID::SPI4:
|
||||
case NetID::SPI5:
|
||||
case NetID::SPI6:
|
||||
case NetID::SPI7:
|
||||
case NetID::SPI8:
|
||||
return Type::SPI;
|
||||
case NetID::MDIO1:
|
||||
case NetID::MDIO2:
|
||||
@@ -768,6 +824,14 @@ public:
|
||||
return "FlexRay 2";
|
||||
case NetID::OP_Ethernet12:
|
||||
return "OP (BR) Ethernet 12";
|
||||
case NetID::OP_Ethernet13:
|
||||
return "OP (BR) Ethernet 13";
|
||||
case NetID::OP_Ethernet14:
|
||||
return "OP (BR) Ethernet 14";
|
||||
case NetID::OP_Ethernet15:
|
||||
return "OP (BR) Ethernet 15";
|
||||
case NetID::OP_Ethernet16:
|
||||
return "OP (BR) Ethernet 16";
|
||||
case NetID::I2C:
|
||||
return "I2C";
|
||||
case NetID::MOST25:
|
||||
@@ -848,12 +912,40 @@ public:
|
||||
return "LIN 07";
|
||||
case NetID::LIN8:
|
||||
return "LIN 08";
|
||||
case NetID::LIN9:
|
||||
return "LIN 09";
|
||||
case NetID::LIN10:
|
||||
return "LIN 10";
|
||||
case NetID::LIN11:
|
||||
return "LIN 11";
|
||||
case NetID::LIN12:
|
||||
return "LIN 12";
|
||||
case NetID::LIN13:
|
||||
return "LIN 13";
|
||||
case NetID::LIN14:
|
||||
return "LIN 14";
|
||||
case NetID::LIN15:
|
||||
return "LIN 15";
|
||||
case NetID::LIN16:
|
||||
return "LIN 16";
|
||||
case NetID::WBMS:
|
||||
return "WBMS";
|
||||
case NetID::SPI1:
|
||||
return "SPI 1";
|
||||
case NetID::SPI2:
|
||||
return "SPI 2";
|
||||
case NetID::SPI3:
|
||||
return "SPI 3";
|
||||
case NetID::SPI4:
|
||||
return "SPI 4";
|
||||
case NetID::SPI5:
|
||||
return "SPI 5";
|
||||
case NetID::SPI6:
|
||||
return "SPI 6";
|
||||
case NetID::SPI7:
|
||||
return "SPI 7";
|
||||
case NetID::SPI8:
|
||||
return "SPI 8";
|
||||
case NetID::MDIO1:
|
||||
return "MDIO 1";
|
||||
case NetID::MDIO2:
|
||||
@@ -974,6 +1066,14 @@ public:
|
||||
return CoreMini::OP_Ethernet11;
|
||||
case NetID::OP_Ethernet12:
|
||||
return CoreMini::OP_Ethernet12;
|
||||
case NetID::OP_Ethernet13:
|
||||
return CoreMini::OP_Ethernet13;
|
||||
case NetID::OP_Ethernet14:
|
||||
return CoreMini::OP_Ethernet14;
|
||||
case NetID::OP_Ethernet15:
|
||||
return CoreMini::OP_Ethernet15;
|
||||
case NetID::OP_Ethernet16:
|
||||
return CoreMini::OP_Ethernet16;
|
||||
case NetID::TCP:
|
||||
return CoreMini::TCPVirtual;
|
||||
case NetID::UDP:
|
||||
@@ -1022,10 +1122,38 @@ public:
|
||||
return CoreMini::LIN7;
|
||||
case NetID::LIN8:
|
||||
return CoreMini::LIN8;
|
||||
case NetID::LIN9:
|
||||
return CoreMini::LIN9;
|
||||
case NetID::LIN10:
|
||||
return CoreMini::LIN10;
|
||||
case NetID::LIN11:
|
||||
return CoreMini::LIN11;
|
||||
case NetID::LIN12:
|
||||
return CoreMini::LIN12;
|
||||
case NetID::LIN13:
|
||||
return CoreMini::LIN13;
|
||||
case NetID::LIN14:
|
||||
return CoreMini::LIN14;
|
||||
case NetID::LIN15:
|
||||
return CoreMini::LIN15;
|
||||
case NetID::LIN16:
|
||||
return CoreMini::LIN16;
|
||||
case NetID::SPI1:
|
||||
return CoreMini::SPI1;
|
||||
case NetID::SPI2:
|
||||
return CoreMini::SPI2;
|
||||
case NetID::SPI3:
|
||||
return CoreMini::SPI3;
|
||||
case NetID::SPI4:
|
||||
return CoreMini::SPI4;
|
||||
case NetID::SPI5:
|
||||
return CoreMini::SPI5;
|
||||
case NetID::SPI6:
|
||||
return CoreMini::SPI6;
|
||||
case NetID::SPI7:
|
||||
return CoreMini::SPI7;
|
||||
case NetID::SPI8:
|
||||
return CoreMini::SPI8;
|
||||
case NetID::MDIO1:
|
||||
return CoreMini::MDIO1;
|
||||
case NetID::MDIO2:
|
||||
@@ -1144,6 +1272,14 @@ public:
|
||||
return NetID::OP_Ethernet11;
|
||||
case CoreMini::OP_Ethernet12:
|
||||
return NetID::OP_Ethernet12;
|
||||
case CoreMini::OP_Ethernet13:
|
||||
return NetID::OP_Ethernet13;
|
||||
case CoreMini::OP_Ethernet14:
|
||||
return NetID::OP_Ethernet14;
|
||||
case CoreMini::OP_Ethernet15:
|
||||
return NetID::OP_Ethernet15;
|
||||
case CoreMini::OP_Ethernet16:
|
||||
return NetID::OP_Ethernet16;
|
||||
case CoreMini::TCPVirtual:
|
||||
return NetID::TCP;
|
||||
case CoreMini::UDPVirtual:
|
||||
@@ -1192,10 +1328,38 @@ public:
|
||||
return NetID::LIN7;
|
||||
case CoreMini::LIN8:
|
||||
return NetID::LIN8;
|
||||
case CoreMini::LIN9:
|
||||
return NetID::LIN9;
|
||||
case CoreMini::LIN10:
|
||||
return NetID::LIN10;
|
||||
case CoreMini::LIN11:
|
||||
return NetID::LIN11;
|
||||
case CoreMini::LIN12:
|
||||
return NetID::LIN12;
|
||||
case CoreMini::LIN13:
|
||||
return NetID::LIN13;
|
||||
case CoreMini::LIN14:
|
||||
return NetID::LIN14;
|
||||
case CoreMini::LIN15:
|
||||
return NetID::LIN15;
|
||||
case CoreMini::LIN16:
|
||||
return NetID::LIN16;
|
||||
case CoreMini::SPI1:
|
||||
return NetID::SPI1;
|
||||
case CoreMini::SPI2:
|
||||
return NetID::SPI2;
|
||||
case CoreMini::SPI3:
|
||||
return NetID::SPI3;
|
||||
case CoreMini::SPI4:
|
||||
return NetID::SPI4;
|
||||
case CoreMini::SPI5:
|
||||
return NetID::SPI5;
|
||||
case CoreMini::SPI6:
|
||||
return NetID::SPI6;
|
||||
case CoreMini::SPI7:
|
||||
return NetID::SPI7;
|
||||
case CoreMini::SPI8:
|
||||
return NetID::SPI8;
|
||||
case CoreMini::MDIO1:
|
||||
return NetID::MDIO1;
|
||||
case CoreMini::MDIO2:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef __HARDWAREINFOPACKET_H__
|
||||
#define __HARDWAREINFOPACKET_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class HardwareInfo;
|
||||
|
||||
struct HardwareInfoPacket {
|
||||
static std::shared_ptr<HardwareInfo> DecodeToMessage(const std::vector<uint8_t>& bytes);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // __DEVICECOMPONENTVERSIONPACKET_H__
|
||||
@@ -4,14 +4,13 @@
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/packet.h"
|
||||
#include "icsneo/communication/ringbuffer.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
#define ICSNEO_PACKETIZER_BUFFER_SIZE (512 * 1024)
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class Packetizer {
|
||||
@@ -22,7 +21,7 @@ public:
|
||||
|
||||
std::vector<uint8_t>& packetWrap(std::vector<uint8_t>& data, bool shortFormat) const;
|
||||
|
||||
bool input(const std::vector<uint8_t>& bytes);
|
||||
bool input(RingBuffer& bytes);
|
||||
std::vector<std::shared_ptr<Packet>> output();
|
||||
|
||||
bool disableChecksum = false; // Even for short packets
|
||||
@@ -37,95 +36,6 @@ private:
|
||||
GetData
|
||||
};
|
||||
|
||||
class RingBuffer
|
||||
{
|
||||
private:
|
||||
constexpr static size_t mBufferSize = ICSNEO_PACKETIZER_BUFFER_SIZE;
|
||||
|
||||
size_t mStartOffset;
|
||||
size_t mSize;
|
||||
uint8_t mData[mBufferSize];
|
||||
|
||||
public:
|
||||
RingBuffer(void)
|
||||
: mStartOffset(0)
|
||||
, mSize(0)
|
||||
{
|
||||
(void)memset(mData, 0, mBufferSize);
|
||||
}
|
||||
|
||||
const uint8_t& operator [](size_t offset) { return Get(offset); }
|
||||
size_t size(void) { return mSize; }
|
||||
void pop_front(void)
|
||||
{
|
||||
Erase_front(1);
|
||||
}
|
||||
|
||||
void Erase_front(size_t count)
|
||||
{
|
||||
if (mSize < count)
|
||||
{
|
||||
throw std::runtime_error("RingBuffer: Underflow");
|
||||
}
|
||||
mStartOffset = (mStartOffset + count) % mBufferSize;
|
||||
mSize -= count;
|
||||
}
|
||||
|
||||
const uint8_t& Get(size_t offset)
|
||||
{
|
||||
if (offset >= mSize)
|
||||
{
|
||||
throw std::runtime_error("RingBuffer: Index out of range");
|
||||
}
|
||||
return *Resolve(offset);
|
||||
}
|
||||
|
||||
void Copy(const std::vector<uint8_t>& source)
|
||||
{
|
||||
const auto inputSize = source.size();
|
||||
const auto octetsAvailable = (mBufferSize - mSize);
|
||||
|
||||
if (inputSize > octetsAvailable)
|
||||
{
|
||||
throw std::runtime_error("RingBuffer: Out of memory");
|
||||
}
|
||||
|
||||
const auto octetsAvailableTail = (octetsAvailable - mStartOffset);
|
||||
const auto octetsToWrap = (inputSize > octetsAvailableTail) ? (inputSize - octetsAvailableTail) : 0;
|
||||
const auto octetsToAppend = (inputSize - octetsToWrap);
|
||||
|
||||
(void)memcpy(Resolve(mSize), source.data(), octetsToAppend);
|
||||
if (octetsToWrap > 0)
|
||||
{
|
||||
(void)memcpy(mData, &source.data()[octetsToAppend], octetsToWrap);
|
||||
}
|
||||
mSize += inputSize;
|
||||
}
|
||||
|
||||
void CopyTo(uint8_t* dest, size_t startIndex, size_t length)
|
||||
{
|
||||
if ((startIndex + length) > mSize)
|
||||
{
|
||||
throw std::runtime_error("RingBuffer: Index out of range");
|
||||
}
|
||||
|
||||
const auto octetsToReadHead = std::min<size_t>((mBufferSize - mStartOffset - startIndex), length);
|
||||
const auto octetsToReadTail = (length - octetsToReadHead);
|
||||
|
||||
(void)memcpy(dest, Resolve(startIndex), octetsToReadHead);
|
||||
if (octetsToReadTail > 0)
|
||||
{
|
||||
(void)memcpy(&dest[octetsToReadHead], mData, octetsToReadTail);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
inline uint8_t* Resolve(size_t offset)
|
||||
{
|
||||
return &mData[(mStartOffset + offset) % mBufferSize];
|
||||
}
|
||||
};
|
||||
|
||||
ReadState state = ReadState::SearchForHeader;
|
||||
|
||||
int currentIndex = 0;
|
||||
@@ -134,7 +44,6 @@ private:
|
||||
bool checksum = false;
|
||||
bool gotGoodPackets = false; // Tracks whether we've ever gotten a good packet
|
||||
Packet packet;
|
||||
RingBuffer bytes;
|
||||
|
||||
std::vector<std::shared_ptr<Packet>> processedPackets;
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef _RINGBUFFER_H_
|
||||
#define _RINGBUFFER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#if __cplusplus >= 202002L
|
||||
#include <bit>
|
||||
#endif
|
||||
namespace icsneo {
|
||||
|
||||
class RingBuffer
|
||||
{
|
||||
private:
|
||||
static constexpr size_t RoundUp(size_t size) {
|
||||
if (size == 0) {
|
||||
// Avoid underflow when decrementing later
|
||||
return 1;
|
||||
} else if (size >= SIZE_MAX) {
|
||||
// overflow case - resolve to max size
|
||||
return MaxSize;
|
||||
}
|
||||
#if __cplusplus >= 202002L
|
||||
// c++20 gives us countl_zero which should be more effecient on most platforms
|
||||
auto lzero = std::countl_zero(size - 1);
|
||||
auto shift = (sizeof(size_t) * 8) - lzero;
|
||||
return 1ull << shift;
|
||||
#else
|
||||
// Bit twiddling magic! See http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
--size;
|
||||
size |= size >> 1;
|
||||
size |= size >> 2;
|
||||
size |= size >> 4;
|
||||
for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
|
||||
size |= size >> (i << 3);
|
||||
}
|
||||
++size;
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
//static_assert(std::atomic<size_t>::is_always_lock_free, "RingBuffer cursor types are not lock-free");
|
||||
std::atomic<size_t> readCursor;
|
||||
std::atomic<size_t> writeCursor;
|
||||
// Use this to mask the cursor values to the buffer size. This is set to capacity - 1 where capacity is always an integral power of 2 (2, 4, 8, 16, etc)
|
||||
size_t mask;
|
||||
uint8_t* buf;
|
||||
|
||||
public:
|
||||
static constexpr auto MaxSize = 1ull << ((8 * sizeof(size_t)) - 1);
|
||||
RingBuffer(size_t bufferSize);
|
||||
~RingBuffer();
|
||||
const uint8_t& operator[](size_t offset) const;
|
||||
size_t size() const;
|
||||
void pop_front();
|
||||
void pop(size_t count);
|
||||
const uint8_t& get(size_t offset) const;
|
||||
bool write(const uint8_t* addr, size_t count);
|
||||
bool write(const std::vector<uint8_t>& source);
|
||||
bool read(uint8_t* dest, size_t startIndex, size_t length) const;
|
||||
void clear();
|
||||
constexpr size_t capacity() const {
|
||||
return mask + 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline uint8_t* resolve(size_t cursor, size_t offset) const {
|
||||
return &buf[(cursor + offset) & mask];
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef __COREMINI_H_
|
||||
#define __COREMINI_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
struct CoreminiHeader {
|
||||
uint16_t coreminiVersion;
|
||||
uint32_t storedFileSize;
|
||||
// 32-bit word checksum on the entire (decompressed) binary, with the checksum and hash fields set to 0
|
||||
uint32_t fileChecksum;
|
||||
// SHA256 hash of the entire (decompressed) binary, with the checksum, hash, and create time fields set to 0
|
||||
bool skipDecompression;
|
||||
bool encryptedMode;
|
||||
std::array<uint8_t, 32> fileHash;
|
||||
std::chrono::time_point<std::chrono::system_clock> timestamp;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <chrono>
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/api/lifetime.h"
|
||||
#include "icsneo/device/neodevice.h"
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "icsneo/device/devicetype.h"
|
||||
#include "icsneo/device/deviceversion.h"
|
||||
#include "icsneo/device/founddevice.h"
|
||||
#include "icsneo/device/coremini.h"
|
||||
#include "icsneo/disk/diskreaddriver.h"
|
||||
#include "icsneo/disk/diskwritedriver.h"
|
||||
#include "icsneo/disk/nulldiskdriver.h"
|
||||
@@ -34,8 +36,10 @@
|
||||
#include "icsneo/communication/message/scriptstatusmessage.h"
|
||||
#include "icsneo/communication/message/supportedfeaturesmessage.h"
|
||||
#include "icsneo/communication/message/genericbinarystatusmessage.h"
|
||||
#include "icsneo/communication/message/hardwareinfo.h"
|
||||
#include "icsneo/communication/message/extendeddatamessage.h"
|
||||
#include "icsneo/communication/message/livedatamessage.h"
|
||||
#include "icsneo/communication/message/tc10statusmessage.h"
|
||||
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
|
||||
#include "icsneo/communication/packet/livedatapacket.h"
|
||||
#include "icsneo/device/extensions/flexray/controller.h"
|
||||
@@ -45,6 +49,8 @@
|
||||
#include "icsneo/platform/nodiscard.h"
|
||||
#include "icsneo/disk/vsa/vsa.h"
|
||||
#include "icsneo/disk/vsa/vsaparser.h"
|
||||
#include "icsneo/communication/message/versionmessage.h"
|
||||
|
||||
|
||||
#define ICSNEO_FINDABLE_DEVICE_BASE(className, type) \
|
||||
static constexpr DeviceType::Enum DEVICE_TYPE = type; \
|
||||
@@ -158,7 +164,8 @@ public:
|
||||
bool startScript(Disk::MemoryType memType = Disk::MemoryType::SD);
|
||||
bool stopScript();
|
||||
bool clearScript(Disk::MemoryType memType = Disk::MemoryType::SD);
|
||||
bool uploadCoremini(std::unique_ptr<std::istream>&& stream, Disk::MemoryType memType = Disk::MemoryType::SD);
|
||||
bool uploadCoremini(std::istream& stream, Disk::MemoryType memType = Disk::MemoryType::SD);
|
||||
std::optional<CoreminiHeader> readCoreminiHeader(Disk::MemoryType memType = Disk::MemoryType::SD);
|
||||
|
||||
bool eraseScriptMemory(Disk::MemoryType memType, uint64_t amount);
|
||||
|
||||
@@ -182,8 +189,14 @@ public:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
virtual bool supportsEraseMemory() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Message polling related functions
|
||||
|
||||
|
||||
bool enableMessagePolling();
|
||||
bool disableMessagePolling();
|
||||
bool isMessagePollingEnabled() { return messagePollingCallbackID != 0; };
|
||||
@@ -216,6 +229,7 @@ public:
|
||||
virtual size_t getNetworkCountByType(Network::Type) const;
|
||||
virtual Network getNetworkByNumber(Network::Type, size_t) const;
|
||||
|
||||
std::shared_ptr<HardwareInfo> getHardwareInfo(std::chrono::milliseconds timeout = std::chrono::milliseconds(100));
|
||||
|
||||
|
||||
/**
|
||||
@@ -424,6 +438,16 @@ public:
|
||||
IsEncrypted = 16,
|
||||
};
|
||||
|
||||
enum RootDirectoryEntryFlags : uint8_t {
|
||||
IsPrePost = 1,
|
||||
PrePostTriggered = (1 << 1),
|
||||
UploadPriority = (1 << 2) | (1 << 3),
|
||||
CellularEnabled = (1 << 4),
|
||||
WiFiEnabled = (1 << 5),
|
||||
Uploaded = (1 << 6),
|
||||
Unused = (1 << 7)
|
||||
};
|
||||
|
||||
typedef std::function< void(uint64_t value) > ScriptStatusCallback;
|
||||
|
||||
/**
|
||||
@@ -549,6 +573,7 @@ public:
|
||||
*/
|
||||
const std::vector<std::optional<DeviceAppVersion>>& getVersions() const { return versions; }
|
||||
const std::vector<ComponentVersion>& getComponentVersions() const { return componentVersions; }
|
||||
|
||||
/**
|
||||
* Some alternate communication protocols do not support DFU
|
||||
*/
|
||||
@@ -579,7 +604,19 @@ public:
|
||||
|
||||
std::optional<EthPhyMessage> sendEthPhyMsg(const EthPhyMessage& message, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
|
||||
|
||||
std::optional<bool> SetCollectionUploaded(uint32_t collectionEntryByteAddress);
|
||||
|
||||
/**
|
||||
* Set the flags of the root directory entry specified at given address
|
||||
*
|
||||
* Will not allow changes of IsPrePost and PrePostTriggered flags and will produce a warning
|
||||
* if there is an attempt to do so
|
||||
*
|
||||
* @param mask Flags to set, with each bit representing a different entry flag @RootDirectoryEntryFlags
|
||||
* @param values The values in which to set each flag, each bit corresponding to the flag in the same position
|
||||
* @param collectionEntryByteAddress The position of the root directory entry in which to set these flags
|
||||
* @return Success or failure
|
||||
*/
|
||||
std::optional<bool> SetRootDirectoryEntryFlags(uint8_t mask, uint8_t values, uint32_t collectionEntryByteAddress);
|
||||
|
||||
std::shared_ptr<Communication> com;
|
||||
std::unique_ptr<IDeviceSettings> settings;
|
||||
@@ -687,6 +724,16 @@ public:
|
||||
|
||||
virtual bool isOnlineSupported() const { return true; }
|
||||
|
||||
virtual bool supportsComponentVersions() const { return false; }
|
||||
|
||||
virtual bool supportsTC10() const { return false; }
|
||||
|
||||
bool requestTC10Wake(Network::NetID network);
|
||||
|
||||
bool requestTC10Sleep(Network::NetID network);
|
||||
|
||||
std::optional<TC10StatusMessage> getTC10Status(Network::NetID network);
|
||||
|
||||
protected:
|
||||
bool online = false;
|
||||
int messagePollingCallbackID = 0;
|
||||
@@ -792,8 +839,6 @@ protected:
|
||||
|
||||
neodevice_t& getWritableNeoDevice() { return data; }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
neodevice_t data;
|
||||
std::shared_ptr<ResetStatusMessage> latestResetStatus;
|
||||
|
||||
@@ -50,6 +50,10 @@ public:
|
||||
RADMoon3 = (0x00000023),
|
||||
RADComet = (0x00000024),
|
||||
FIRE3_FlexRay = (0x00000025),
|
||||
Connect = (0x00000026),
|
||||
RADComet3 = (0x00000027),
|
||||
RADMoonT1S = (0x00000028),
|
||||
RADGigastar2 = (0x00000029),
|
||||
RED = (0x00000040),
|
||||
ECU = (0x00000080),
|
||||
IEVB = (0x00000100),
|
||||
@@ -186,6 +190,14 @@ public:
|
||||
return "neoOBD2 SIM";
|
||||
case FIRE3_FlexRay:
|
||||
return "neoVI FIRE3 FlexRay";
|
||||
case RADComet3:
|
||||
return "RAD-Comet 3";
|
||||
case RADMoonT1S:
|
||||
return "RAD-Moon T1S";
|
||||
case Connect:
|
||||
return "neoVI Connect";
|
||||
case RADGigastar2:
|
||||
return "RAD-Gigastar 2";
|
||||
case DONT_REUSE0:
|
||||
case DONT_REUSE1:
|
||||
case DONT_REUSE2:
|
||||
@@ -239,6 +251,10 @@ private:
|
||||
#define ICSNEO_DEVICETYPE_RADMoon3 ((devicetype_t)0x00000023)
|
||||
#define ICSNEO_DEVICETYPE_RADCOMET ((devicetype_t)0x00000024)
|
||||
#define ICSNEO_DEVICETYPE_FIRE3FLEXRAY ((devicetype_t)0x00000025)
|
||||
#define ICSNEO_DEVICETYPE_CONNECT ((devicetype_t)0x00000026)
|
||||
#define ICSNEO_DEVICETYPE_RADCOMET3 ((devicetype_t)0x00000027)
|
||||
#define ICSNEO_DEVICETYPE_RADMOONT1S ((devicetype_t)0x00000028)
|
||||
#define ICSNEO_DEVICETYPE_RADGIGASTAR2 ((devicetype_t)0x00000029)
|
||||
#define ICSNEO_DEVICETYPE_RED ((devicetype_t)0x00000040)
|
||||
#define ICSNEO_DEVICETYPE_ECU ((devicetype_t)0x00000080)
|
||||
#define ICSNEO_DEVICETYPE_IEVB ((devicetype_t)0x00000100)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user