Author SHA1 Message Date
Kyle Johannes 46e244fbab System Test: Hardware test infrastructure 2024-04-05 21:10:49 +00:00
Jonathan Schwartz 63f0516318 Replace concurrentqueue with ringbuffer 2024-04-05 17:24:53 +00:00
Kyle Schwarz 7d2d12c5cd Revert "Communication: Move NetID::Device to Type::CAN" 2024-04-04 21:48:42 +00:00
Yasser Yassine bcf9d62be9 A2B: Fix warnings 2024-04-02 16:02:12 -04:00
Jonathan SchwartzandKyle Schwarz a01aa90740 Communication: Move NetID::Device to Type::CAN
NetID::Device is used as a "virtual" CAN network by the device. All messages on this network are packed identically to CAN messages
2024-04-01 12:28:46 +00:00
Yasser YassineandKyle Schwarz 85178368d7 Build: Add static icsneo40 target 2024-03-19 20:59:33 +00:00
Yasser YassineandKyle Schwarz 16b60d5ca0 A2B: Fix get & set with upstream direction 2024-03-19 20:36:21 +00:00
Yasser YassineandKyle Schwarz cb22e622b3 Device: Refactor A2B APIs
* Removes features in `A2BMessage` class to support API for reading 16, 24, and 32 bit samples from A2B channels
* Re-organizes WAV receiving and transmitting code and API
* Creates API for mapping message channels to WAV channels and vice versa for transmitting and receiving
* Fixes `icsneo::Network::NetID::ExtendedData` VnetID bug for `icsneo::ExtendedDataMessage` decoding
* Creates RAD-A2B sequence chart example
* Fixes coremini uploading for certain devices in EEPROM by introducing `icsneo::Device::supportsEraseMemory`
2024-03-12 12:06:49 +00:00
David RebbeandKyle Schwarz 06f6861130 Legacy: Drop deprecated APIs
Drop icsneoFindNeoDevices() and icsneoOpenNeoDevice() in favor of icsneoFindDevices() and icsneoOpenDevice(), respectively.

Also fixes:
- Failure to re-open a device after it has been closed with the C/legacy APIs
- NumberOfClients not being updated
- FIRE3 settings missing in icsneoGetDeviceSettingsType()
2024-03-07 19:45:46 +00:00
Jonathan Schwartz 219a5edbd8 Fixes Windows build with TCP enabled 2024-02-23 14:16:15 +00:00
Emily Brooks 05888e5c20 Device: Set multiple root directory entry flags at once 2024-02-14 18:06:59 -05:00
Bryant Jones e37d939699 Build: Fix warning on Comet2 missing override keyword 2024-02-14 15:37:09 +00:00
Bryant Jones 2a94b4566b RAD-A2B: Update settings structure 2024-02-14 10:04:14 -05:00
Kyle SchwarzandKyle Johannes 4782e26bed Device: Add isOnlineSupported() 2024-02-13 21:24:51 +00:00
Yasser Yassine 3264a1ecbe RAD-A2B: Fix RADA2BSettings::getChannelSize function 2024-01-23 23:18:01 +00:00
Yasser Yassine 1dfed4c9c4 A2B: Set timestamp field in HardwareA2BPacket::DecodeToMessage 2024-01-22 22:44:06 +00:00
Max Brombach a44952be13 Disk: Update VSA Example and Fix VSA CAN-FD Decode
- Send and validate CAN/CAN-FD/Eth frames in VSA example (two devices)
- Fix failure to decode CAN-FD frames from VSA records
2024-01-16 18:01:10 +00:00
Yasser Yassine 75e9319c32 A2B: Add example for reading/writing A2B board I2C registers 2024-01-12 20:34:11 +00:00
Bryant Jones 9df4aed19f Device: Add RAD-Comet2 support 2024-01-04 14:48:13 +00:00
Kyle Schwarz d56e66afd3 All: Bump copyright to 2024 2024-01-02 08:23:51 -05:00
jschwartz 26b52237e4 Communication: Increase device timeout in overloaded waitForMessageSync 2023-12-07 16:03:31 -05:00
Jonathan Schwartz 28ba94fa25 Remove RTTI dependency, increase default timeout for communication 2023-12-06 22:35:17 +00:00
Kyle Johannes e56c714a0c LIN: update example 2023-12-05 17:30:40 -05:00
Jonathan SchwartzandKyle Johannes 58f53edea8 Update device timeouts 2023-12-01 23:29:42 +00:00
Kyle Schwarz 3d2d3cb43d CI: Add Fedora 38 & 39 jobs 2023-12-01 22:39:46 +00:00
Kyle SchwarzandKyle Johannes bddbcfcf6d CI: Fix warnings 2023-12-01 22:20:41 +00:00
0497b361ef LIN: Settings API (#62)
* Settings: add APIs for LIN configuration

Add getter/setter for LIN configuration:
- baudrate
- commander resistor ON/OFF
- mode (SLEEP, SLOW, NORMAL, FAST)

* Device: add LIN settings getter for devices with LIN
* LIN: add setup to LIN example
* LIN: settings minor tweaks from PR

---------

Co-authored-by: Francesco Valla <francesco.valla@mta.it>
2023-11-30 15:56:18 -05:00
Max Brombach 02f1b4592e Device/Disk: Add VSA read and parse functionality
Implement ability to extract network traffic (CAN, LIN, Ethernet, etc.) from VSA message records on disk. Add a method to Device class that uses the VSAParser and the individual record types to extract messages from the VSA message records and pass them back to the communication system. This routes messages such that it appears as if they were discovered live instead of read from disk. The parse process (in Device) requires determination of metadata about the VSA file system on a device before it can begin extracting messages. This currently only handles data captured from the current coremini script on a device.
2023-11-15 16:02:47 +00:00
Yasser Yassine 4248c1a538 Device: Add CoreMini flashing support 2023-11-08 18:47:03 -05:00
Kyle Schwarz ec51393dfc CMake: Require Threads package 2023-11-06 17:39:11 -05:00
163 changed files with 7189 additions and 1828 deletions
+2 -1
View File
@@ -13,4 +13,5 @@ third-party/concurrentqueue/tests
*.wav
*.orig
examples/csharp/bin
examples/csharp/obj
examples/csharp/obj
test/system
+149 -25
View File
@@ -3,7 +3,8 @@ variables:
stages:
- build
- test
- unit_test
- hardware_test
#-------------------------------------------------------------------------------
# Windows
@@ -21,10 +22,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 +46,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 +67,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 +78,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 +93,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 +104,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 +118,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 +130,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 +142,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 +154,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:
@@ -171,8 +172,10 @@ test linux/ubuntu/2204/amd64/clang:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel git
- sh ci/build-posix.sh
artifacts:
when: always
@@ -183,14 +186,16 @@ 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
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- build/libicsneo-tests
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
@@ -201,8 +206,10 @@ test linux/ubuntu/2204/amd64/clang:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y clang lld libpcap-devel cmake ninja-build libusb1-devel
- 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
@@ -213,14 +220,16 @@ 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
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- build/libicsneo-tests
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
@@ -229,7 +238,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:
@@ -241,10 +250,125 @@ 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:
- build linux/fedora/37/amd64/clang
needs:
- build linux/fedora/37/amd64/clang
build linux/fedora/38/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:38
unit_test linux/fedora/38/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:38
dependencies:
- build linux/fedora/38/amd64/gcc
needs:
- build linux/fedora/38/amd64/gcc
build linux/fedora/38/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:38
unit_test linux/fedora/38/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:38
dependencies:
- build linux/fedora/38/amd64/clang
needs:
- build linux/fedora/38/amd64/clang
build linux/fedora/39/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:39
unit_test linux/fedora/39/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:39
dependencies:
- build linux/fedora/39/amd64/gcc
needs:
- build linux/fedora/39/amd64/gcc
build linux/fedora/39/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:39
unit_test linux/fedora/39/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:39
dependencies:
- build linux/fedora/39/amd64/clang
needs:
- build linux/fedora/39/amd64/clang
.hw_test: &hw_test
stage: hardware_test
tags:
- libicsneo_hil
timeout: 5m
script:
- echo $GUEST_OS_TAG
- echo $DEVICE_PORT
- /opt/libvirt-driver/prepare.sh
- /opt/libvirt-driver/run.sh
after_script:
- /opt/libvirt-driver/cleanup.sh
hardware_test system-test-fedora38-red2:
<<: *hw_test
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: ETH_A
needs:
- job: build linux/fedora/38/amd64/clang
artifacts: true
hardware_test system-test-fedora38-vcan42:
<<: *hw_test
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: USB_D
needs:
- job: build linux/fedora/38/amd64/clang
artifacts: true
hardware_test system-test-ubuntu2204-red2:
<<: *hw_test
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: ETH_A
needs:
- job: build linux/ubuntu/2204/amd64/clang
artifacts: true
hardware_test system-test-ubuntu2204-vcan42:
<<: *hw_test
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: USB_D
needs:
- job: build linux/ubuntu/2204/amd64/clang
artifacts: true
hardware_test system-test-win10-red2:
<<: *hw_test
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: ETH_A
needs:
- job: build windows/x64
artifacts: true
hardware_test system-test-win10-vcan42:
<<: *hw_test
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: USB_D
needs:
- job: build windows/x64
artifacts: true
+81 -20
View File
@@ -2,13 +2,18 @@ cmake_minimum_required(VERSION 3.12)
project(libicsneo VERSION 0.3.0)
cmake_policy(SET CMP0074 NEW)
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
@@ -45,6 +50,8 @@ else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
find_package(Threads REQUIRED)
# doxygen
find_package(Doxygen)
if(DOXYGEN_FOUND)
@@ -230,7 +237,7 @@ 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/neomessage.cpp
communication/message/ethphymessage.cpp
communication/message/linmessage.cpp
@@ -260,6 +267,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
@@ -273,6 +281,22 @@ set(SRC_FILES
disk/plasiondiskreaddriver.cpp
disk/extextractordiskreaddriver.cpp
disk/fat.cpp
disk/vsa/vsa.cpp
disk/vsa/vsa02.cpp
disk/vsa/vsa03.cpp
disk/vsa/vsa04.cpp
disk/vsa/vsa05.cpp
disk/vsa/vsa06.cpp
disk/vsa/vsa07.cpp
disk/vsa/vsa08.cpp
disk/vsa/vsa09.cpp
disk/vsa/vsa0b.cpp
disk/vsa/vsa0c.cpp
disk/vsa/vsa0d.cpp
disk/vsa/vsa0e.cpp
disk/vsa/vsa0f.cpp
disk/vsa/vsa6a.cpp
disk/vsa/vsaparser.cpp
${PLATFORM_SRC}
)
@@ -333,6 +357,7 @@ target_include_directories(icsneocpp
${CMAKE_CURRENT_SOURCE_DIR}/include
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
)
target_link_libraries(icsneocpp PUBLIC Threads::Threads)
set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
@@ -356,7 +381,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()
@@ -379,7 +404,6 @@ if(LIBICSNEO_ENABLE_FTDI)
add_subdirectory(third-party/libftdi)
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
find_package(Threads)
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
@@ -427,6 +451,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)
@@ -447,8 +472,27 @@ if(LIBICSNEO_BUILD_ICSNEOLEGACY)
target_compile_features(icsneolegacy PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
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()
# googletest
if(LIBICSNEO_BUILD_TESTS)
if(LIBICSNEO_BUILD_UNIT_TESTS)
if(WIN32)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
endif()
@@ -461,27 +505,44 @@ 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
)
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})
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2018-2023 Intrepid Control Systems, Inc.
Copyright 2018-2024 Intrepid Control Systems, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+18 -2
View File
@@ -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;
}
@@ -740,4 +749,11 @@ int icsneo_getDeviceStatus(const neodevice_t* device, void* status, size_t* size
*size = rawMessage->data.size();
return true;
}
}
bool icsneo_isOnlineSupported(const neodevice_t* device) {
if(!icsneo_isValidNeoDevice(device))
return false;
return device->device->isOnlineSupported();
}
+1 -1
View File
@@ -29,7 +29,7 @@ BEGIN
VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", "icsneoc.dll"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2023"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024"
VALUE "OriginalFilename", "icsneoc.dll"
VALUE "ProductName", "libicsneo"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+34
View File
@@ -72,6 +72,7 @@ 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.";
// Device Errors
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
@@ -116,6 +117,8 @@ static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the
static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same.";
static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected.";
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device.";
static constexpr const char* MODE_NOT_FOUND = "The mode was not found.";
// Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
@@ -169,6 +172,15 @@ static constexpr const char* FT_DEVICE_NOT_CONNECTED = "FTD3XX device not connec
static constexpr const char* FT_INCORRECT_DEVICE_PATH = "Incorrect FTD3XX device path.";
static constexpr const char* FT_OTHER_ERROR = "Other FTD3XX error.";
// VSA
static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted.";
static constexpr const char* VSA_TIMESTAMP_NOT_FOUND = "Unable to find a VSA record with a valid timestamp.";
static constexpr const char* VSA_BUFFER_FORMAT_ERROR = "VSA record buffer is formatted incorrectly.";
static constexpr const char* VSA_MAX_READ_ATTEMPTS_REACHED = "Reached max attempts to read VSA records before exit.";
static constexpr const char* VSA_BYTE_PARSE_FAILURE = "Failure to parse record bytes from VSA buffer.";
static constexpr const char* VSA_EXTENDED_MESSAGE_ERROR = "Failure to parse extended message record sequence";
static constexpr const char* VSA_OTHER_ERROR = "Unknown error in VSA read API.";
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
static constexpr const char* INVALID = "An invalid internal error occurred.";
@@ -207,6 +219,8 @@ const char* APIEvent::DescriptionForType(Type type) {
return TIMEOUT;
case Type::WiVINotSupported:
return WIVI_NOT_SUPPORTED;
case Type::RestrictedEntryFlag:
return RESTRICTED_ENTRY_FLAG;
// Device Errors
case Type::PollingMessageOverflow:
@@ -293,6 +307,10 @@ const char* APIEvent::DescriptionForType(Type type) {
return DISK_NOT_CONNECTED;
case Type::UnexpectedResponse:
return UNEXPECTED_RESPONSE;
case Type::LINSettingsNotAvailable:
return LIN_SETTINGS_NOT_AVAILABLE;
case Type::ModeNotFound:
return MODE_NOT_FOUND;
// Transport Errors
case Type::FailedToRead:
return FAILED_TO_READ;
@@ -393,6 +411,22 @@ const char* APIEvent::DescriptionForType(Type type) {
case Type::FTOtherError:
return FT_OTHER_ERROR;
// VSA
case Type::VSABufferCorrupted:
return VSA_BUFFER_CORRUPTED;
case Type::VSATimestampNotFound:
return VSA_TIMESTAMP_NOT_FOUND;
case Type::VSABufferFormatError:
return VSA_BUFFER_FORMAT_ERROR;
case Type::VSAMaxReadAttemptsReached:
return VSA_MAX_READ_ATTEMPTS_REACHED;
case Type::VSAByteParseFailure:
return VSA_BYTE_PARSE_FAILURE;
case Type::VSAExtendedMessageError:
return VSA_EXTENDED_MESSAGE_ERROR;
case Type::VSAOtherError:
return VSA_OTHER_ERROR;
// Other Errors
case Type::TooManyEvents:
return TOO_MANY_EVENTS;
@@ -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;
+107 -136
View File
@@ -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,107 +306,79 @@ static inline size_t GetVnetAgnosticNetid(size_t fullNetid)
int LegacyDLLExport icsneoFindDevices(NeoDeviceEx* devs, int* devCount, unsigned int* devTypes, unsigned int devTypeCount,
POptionsFindNeoEx* POptionsFindNeoEx, unsigned int* zero)
{
if (!devs || !devCount)
// Match the legacy API maximum, this derives from the maximum COM Port on old windows versions.
constexpr int MAX_NEO_DEVICES = 255;
// Validate arguments
if (!devCount && *devCount < 0) {
return 0;
if (*devCount < 0 || *devCount > 255)
return 0;
// Find the devices without filtering by the device type
// We allow this to find more than the requested number,
// as we may filter out some devices.
constexpr const size_t MAX_DEVICES = 255;
NeoDevice foundDevices[MAX_DEVICES];
int NumDevices = MAX_DEVICES;
int filteredDeviceCount = 0;
if (!icsneoFindNeoDevices(0, foundDevices, &NumDevices))
return 0;
for (auto i = 0; i < NumDevices; i++)
{
// Check if the next device would overrun the user's buffer
// We check this up here since the documentation allows zero
// to be specified.
if (filteredDeviceCount >= *devCount)
break;
if (devTypes && devTypeCount)
{
for (unsigned int j = 0; j < devTypeCount; j++)
{
if (foundDevices[i].DeviceType == devTypes[j])
{
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
break;
}
}
}
else
{
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
}
}
*devCount = filteredDeviceCount;
return 1; // If the function succeeds but no devices are found 1 will still be returned and devCount will equal 0
}
int LegacyDLLExport icsneoFindNeoDevices(unsigned long DeviceTypes, NeoDevice* pNeoDevice, int* pNumDevices)
{
constexpr size_t MAX_DEVICES = 255;
size_t count = MAX_DEVICES;
if (pNumDevices == nullptr)
return 0;
if (pNeoDevice == nullptr)
// return the size only if devs is NULL.
if (!devs)
{
icsneo_findAllDevices(nullptr, &count);
*pNumDevices = (int)count;
icsneo_findAllDevices(nullptr, (size_t*)devCount);
return 1;
}
size_t bufferSize = (size_t)*pNumDevices;
if (*pNumDevices < 0 || bufferSize > MAX_DEVICES)
return 0;
neodevice_t devices[MAX_DEVICES];
icsneo_findAllDevices(devices, &count);
if (bufferSize < count)
count = bufferSize;
*pNumDevices = (int)count;
for (size_t i = 0; i < count; i++)
{
pNeoDevice[i] = OldNeoDeviceFromNew(&devices[i]); // Write out into user memory
neodevices[uint64_t(devices[i].handle) << 32 | icsneo_serialStringToNum(devices[i].serial)] = devices[i]; // Fill the look up table
// shrink the number of devices allowed to find
if (*devCount > MAX_NEO_DEVICES) {
*devCount = MAX_NEO_DEVICES;
}
return 1;
}
int LegacyDLLExport icsneoOpenNeoDevice(NeoDevice* pNeoDevice, void** hObject, unsigned char* bNetworkIDs, int bConfigRead, int bSyncToPC)
{
if (pNeoDevice == nullptr || hObject == nullptr)
return false;
neodevice_t *device;
try
{
device = &neodevices.at(uint64_t(pNeoDevice->Handle) << 32 | pNeoDevice->SerialNumber);
// Find all the neodevice_t devices
std::vector<neodevice_t> neoDevices(*devCount);
auto neoDevicesSize = neoDevices.size();
icsneo_findAllDevices(neoDevices.data(), &neoDevicesSize);
neoDevices.resize(neoDevicesSize);
// Filter out the devices if needed
// No filtering needed
if (devTypes && devTypeCount > 0) {
neoDevices.erase(
std::remove_if(
neoDevices.begin(),
neoDevices.end(),
[&](const auto& iter) {
for (unsigned int i=0; i < devTypeCount; ++i) {
if (iter.type == devTypes[i]) {
return false;
}
}
return true;
}
),
neoDevices.end()
);
}
catch (const std::out_of_range&)
{
return false;
}
*hObject = device;
if (!icsneo_openDevice(device))
return false;
// Create a NeoDeviceEx From a neodevice_t
auto _createNeoDeviceExFrom = [](const neodevice_t* neoDevice) -> NeoDeviceEx {
NeoDeviceEx nde = {};
nde.neoDevice.DeviceType = neoDevice->type;
nde.neoDevice.SerialNumber = icsneo_serialStringToNum(neoDevice->serial);
nde.neoDevice.NumberOfClients = 0;
nde.neoDevice.MaxAllowedClients = 1;
static_assert(sizeof(neodevice_handle_t) == sizeof(nde.neoDevice.Handle),
"neodevice_handle_t size must be sizeof(int) for compatibility reasons");
nde.neoDevice.Handle = neoDevice->handle;
return nde;
};
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
// Create the NeoDeviceEx from the neodevice_t
auto i = 0;
for (const auto& neoDevice : neoDevices) {
// Fill the look up table
neodevices[uint64_t(neoDevice.handle) << 32 | icsneo_serialStringToNum(neoDevice.serial)] = neoDevice;
// Create the NeoDeviceEx
devs[i] = _createNeoDeviceExFrom(&neoDevice);
NeoDeviceEx* nde = &devs[i];
++i;
// Lookup the open NeoDeviceEx devices and match the NumberOfClients value if available.
for (auto& [neo_device, open_nde]: openneodevices) {
// SerialNumber should always be unique so lets compare against that.
if (nde->neoDevice.SerialNumber == open_nde->neoDevice.SerialNumber) {
nde->neoDevice.NumberOfClients = open_nde->neoDevice.NumberOfClients;
}
}
}
*devCount = (int)neoDevices.size();
return 1;
}
int LegacyDLLExport icsneoOpenDevice(
@@ -443,19 +403,50 @@ int LegacyDLLExport icsneoOpenDevice(
return false;
}
*hObject = device;
if(!icsneo_openDevice(device))
if (pNeoDeviceEx->neoDevice.NumberOfClients >= pNeoDeviceEx->neoDevice.MaxAllowedClients) {
return false;
}
*hObject = device;
if(!icsneo_openDevice(device)) {
return false;
}
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
if (icsneo_isOnlineSupported(device)) {
if (!icsneo_setPollingMessageLimit(device, 20000)) {
icsneo_closeDevice(device);
return false;
}
if (!icsneo_enableMessagePolling(device)) {
icsneo_closeDevice(device);
return false;
}
if (!icsneo_goOnline(device)) {
icsneo_closeDevice(device);
return false;
}
}
pNeoDeviceEx->neoDevice.NumberOfClients = 1;
// Add the open NeoDevice to the container so we can decrement NumberOfClients on close
openneodevices[device] = pNeoDeviceEx;
return true;
}
int LegacyDLLExport icsneoClosePort(void* hObject, int* pNumberOfErrors)
{
if (!icsneoValidateHObject(hObject))
return false;
if (pNumberOfErrors) {
*pNumberOfErrors = 0;
}
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
if (openneodevices.find(device) != openneodevices.end()) {
openneodevices[device]->neoDevice.NumberOfClients -= 1;
openneodevices.erase(device);
}
return icsneo_closeDevice(device);
}
@@ -991,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)
{
@@ -1018,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)
{
@@ -1115,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;
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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%
+2 -2
View File
@@ -268,7 +268,7 @@ void Communication::readTask() {
while(!closing) {
readBytes.clear();
if(driver->readWait(readBytes)) {
if(driver->readAvailable()) {
handleInput(*packetizer, readBytes);
}
}
@@ -291,7 +291,7 @@ void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes)
handleInput(p, readBytes); // and we might as well process this input ourselves
}
} else {
if(p.input(readBytes)) {
if(p.input(driver->getReadBuffer())) {
for(const auto& packet : p.output()) {
std::shared_ptr<Message> msg;
if(!decoder->decode(msg, packet))
+3 -1
View File
@@ -159,6 +159,7 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
msg.network = packet->network;
msg.timestamp *= timestampResolution;
return true;
}
case Network::Type::LIN: {
@@ -294,7 +295,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) {
@@ -306,6 +306,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:
+10 -23
View File
@@ -8,37 +8,24 @@
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;
if(limit > (readQueue.size_approx() + 4))
limit = (readQueue.size_approx() + 4);
if(bytes.capacity() < limit)
bytes.resize(limit);
size_t actuallyRead = readQueue.try_dequeue_bulk(bytes.data(), limit);
if(bytes.size() > actuallyRead)
bytes.resize(actuallyRead);
return true;
}
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
// A limit of zero indicates no limit
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
const auto timeoutTime = std::chrono::steady_clock::now() + timeout;
while (readBuffer.size() < limit && std::chrono::steady_clock::now() < timeoutTime) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
size_t actuallyRead = std::min(readBuffer.size(), limit);
readBuffer.read(bytes.data(), 0, actuallyRead);
readBuffer.pop(actuallyRead);
bytes.resize(actuallyRead);
#ifdef ICSNEO_DRIVER_DEBUG_PRINTS
+257
View File
@@ -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;
}
@@ -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,16 +4,100 @@
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;
}
WaveFileHeader header = WaveFileHeader(2 * firstMsg->getNumChannels(), wavSampleRate, firstMsg->getBitDepth());
header.write(stream);
streamStartPos = static_cast<uint32_t>(stream->tellp());
if(initialize()) {
initialized = true;
}
}
A2BWAVOutput::A2BWAVOutput(
std::ostream& os,
const ChannelMap& channelMap,
PCMType bitDepth,
size_t numWAVChannels,
uint32_t sampleRate
)
: StreamOutput(os), wavSampleRate(sampleRate), numChannelsWAV(numWAVChannels), chMap(channelMap) {
switch(bitDepth) {
case PCMType::L16:
bytesPerSampleWAV = 2;
break;
case PCMType::L24:
bytesPerSampleWAV = 3;
break;
case PCMType::L32:
bytesPerSampleWAV = 4;
break;
}
if(initialize()) {
initialized = true;
}
}
A2BWAVOutput::~A2BWAVOutput() {
if(!closed) {
close();
}
}
bool A2BWAVOutput::initialize() {
static constexpr size_t maxWAVChannels = 256;
if(numChannelsWAV > maxWAVChannels) {
return false;
}
maxMessageChannel = 0;
// Check if the inputted channel map has invalid mappings and compute maxMessageChannel
for(auto [wavChannel, messageChannel] : chMap) {
maxMessageChannel = std::max<size_t>(maxMessageChannel, messageChannel);
if(wavChannel >= numChannelsWAV) {
return false;
}
}
WAVHeader header = WAVHeader(
static_cast<uint16_t>(chMap.size()),
wavSampleRate,
static_cast<uint16_t>(bytesPerSampleWAV * 8)
);
if(!stream->write(reinterpret_cast<const char*>(&header), sizeof(WAVHeader))) {
return false;
}
streamStartPos = static_cast<uint32_t>(stream->tellp());
wavBuffer = std::vector<uint8_t>(wavBufferSize, 0);
wavBufferOffset = 0;
return true;
}
bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
if(!initialized) {
return false;
}
if(closed) {
return false;
}
@@ -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;
}
}
+23 -27
View File
@@ -4,55 +4,51 @@
namespace icsneo {
const size_t HardwareA2BPacket::coreMiniMessageHeaderSize = 28;
const size_t HardwareA2BPacket::a2bMessageMaxLength = (size_t)HardwareA2BPacket::coreMiniMessageHeaderSize + 1024;
const size_t HardwareA2BPacket::a2bHeaderSize = 6;
const size_t HardwareA2BPacket::a2bMessageMaxLength = sizeof(HardwareA2BPacket) + 1024;
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < coreMiniMessageHeaderSize)
if(bytestream.size() < sizeof(HardwareA2BPacket))
{
return nullptr;
}
const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data();
const HardwareA2BPacket* data = (const HardwareA2BPacket*)bytestream.data();
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - static_cast<size_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
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);
msg->setAudioBuffer(bytestream.begin() + coreMiniMessageHeaderSize, bytestream.end());
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>();
msg->numChannels = data->header.channelNum;
msg->channelSize16 = data->header.channelSize16;
msg->monitor = data->header.monitor;
msg->txmsg = data->header.txmsg;
msg->errIndicator = data->header.errIndicator;
msg->syncFrame = data->header.syncFrame;
msg->rfu2 = data->header.rfu2;
msg->timestamp = data->timestamp.TS;
msg->data = std::vector(bytestream.begin() + sizeof(HardwareA2BPacket), bytestream.end());
return msg;
}
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
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);
@@ -23,6 +23,9 @@ std::shared_ptr<SupportedFeaturesMessage> SupportedFeaturesPacket::DecodeToMessa
}
// Get a reference to the payload to fully validate the length
const auto& response = *reinterpret_cast<const SupportedFeaturesResponse*>(bytes.data());
if(response.cmdVersion != SupportedFeaturesCommandVersion) {
return msg;
}
// Expected size is the header, cmdVersion and numValidBits fields, plus the number of 32-bit bitfields in the response based on numValidBits
auto expectedSize = sizeof(ExtendedResponseMessage::ResponseHeader) + 4 + ((response.numValidBits + 31) / 32) * 4;
// If the response is malformed (too small), return an empty message
+3 -5
View File
@@ -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();
+91
View File
@@ -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());
}
}
+1204 -49
View File
File diff suppressed because it is too large Load Diff
+18 -2
View File
@@ -47,6 +47,18 @@ static void makeIfPIDMatches(const FoundDevice& dev, std::vector<std::shared_ptr
into.push_back(std::make_shared<T>(dev));
}
template<typename T>
static void makeIfSerialRangeMatches(const FoundDevice& dev, std::vector<std::shared_ptr<Device>>& into) {
// Relies on the subclass to have
// `static constexpr uint32_t SERIAL_RANGE_LOW = 0x12345678`
// `static constexpr uint32_t SERIAL_RANGE_HIGH = 0x12345678`
// and also a public constructor `T(const FoundDevice& dev)`
// Use macro ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE() to create these
uint32_t serialNum = Device::SerialStringToNum(dev.serial);
if(serialNum >= Device::SerialStringToNum(T::SERIAL_RANGE_LOW) && serialNum <= Device::SerialStringToNum(T::SERIAL_RANGE_HIGH))
into.push_back(std::make_shared<T>(dev));
}
std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
static std::vector<FoundDevice> newDriverFoundDevices;
newDriverFoundDevices.clear();
@@ -158,7 +170,11 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
#endif
#ifdef __RADCOMET_H_
makeIfSerialMatches<RADCOMET>(dev, newFoundDevices);
makeIfSerialRangeMatches<RADComet>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET2_H_
makeIfSerialRangeMatches<RADComet2>(dev, newFoundDevices);
#endif
#ifdef __RADEPSILON_H_
@@ -297,7 +313,7 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
#endif
#ifdef __RADCOMET_H_
RADCOMET::DEVICE_TYPE,
RADComet::DEVICE_TYPE,
#endif
#ifdef __RADEPSILON_H_
+235
View File
@@ -128,6 +128,37 @@ int64_t IDeviceSettings::GetBaudrateValueForEnum(CANBaudrate enumValue) {
}
}
bool IDeviceSettings::ValidateLINBaudrate(int64_t baudrate) {
switch(baudrate) {
case 4800:
// fallthrough
case 9600:
// fallthrough
case 10400:
// fallthrough
case 10417:
// fallthrough
case 10504:
// fallthrough
case 10593:
// fallthrough
case 10684:
// fallthrough
case 10776:
// fallthrough
case 10870:
// fallthrough
case 10965:
// fallthrough
case 11062:
// fallthrough
case 19200:
return true;
default:
return false;
}
}
bool IDeviceSettings::refresh(bool ignoreChecksum) {
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
@@ -414,6 +445,15 @@ int64_t IDeviceSettings::getBaudrateFor(Network net) const {
}
return baudrate;
}
case Network::Type::LIN: {
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return -1;
}
return cfg->Baudrate;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return -1;
@@ -493,6 +533,21 @@ bool IDeviceSettings::setBaudrateFor(Network net, int64_t baudrate) {
cfg->SetBaudrate = AUTO; // Device will use the baudrate value to set the TQ values
return true;
}
case Network::Type::LIN: {
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
bool valid = ValidateLINBaudrate(baudrate);
if(!valid) {
report(APIEvent::Type::BaudrateNotFound, APIEvent::Severity::Error);
return false;
}
cfg->Baudrate = (uint32_t)baudrate;
return true;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
@@ -704,6 +759,186 @@ bool IDeviceSettings::setTerminationFor(Network net, bool enabled) {
return true;
}
std::optional<bool> IDeviceSettings::isCommanderResistorEnabledFor(Network net) const {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return std::nullopt;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
switch(net.getType()) {
case Network::Type::LIN: {
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
return (cfg->CommanderResistor != RESISTOR_OFF);
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return std::nullopt;
}
}
bool IDeviceSettings::setCommanderResistorFor(Network net, bool resistor_on) {
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false;
}
if(readonly) {
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
return false;
}
switch(net.getType()) {
case Network::Type::LIN: {
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
cfg->CommanderResistor = resistor_on ? RESISTOR_ON : RESISTOR_OFF;
return true;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
}
}
std::optional<LINMode> IDeviceSettings::getLINModeFor(Network net) const {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return std::nullopt;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
switch(net.getType()) {
case Network::Type::LIN: {
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
return static_cast<LINMode>(cfg->Mode);
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return std::nullopt;
}
}
bool IDeviceSettings::setLINModeFor(Network net, LINMode mode) {
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false;
}
if(readonly) {
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
return false;
}
switch(net.getType()) {
case Network::Type::LIN: {
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
cfg->Mode = static_cast<uint8_t>(mode);
return true;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
}
}
std::optional<uint8_t> IDeviceSettings::getLINCommanderResponseTimeFor(Network net) const {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return std::nullopt;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
switch(net.getType()) {
case Network::Type::LIN: {
const LIN_SETTINGS* cfg = getLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return std::nullopt;
}
return cfg->numBitsDelay;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return std::nullopt;
}
}
bool IDeviceSettings::setLINCommanderResponseTimeFor(Network net, uint8_t bits) {
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return false;
}
if(readonly) {
report(APIEvent::Type::SettingsReadOnly, APIEvent::Severity::Error);
return false;
}
switch(net.getType()) {
case Network::Type::LIN: {
LIN_SETTINGS* cfg = getMutableLINSettingsFor(net);
if(cfg == nullptr) {
report(APIEvent::Type::LINSettingsNotAvailable, APIEvent::Severity::Error);
return false;
}
cfg->numBitsDelay = bits;
return true;
}
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
}
}
template<typename T> bool IDeviceSettings::applyStructure(const T& newStructure) {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
+12 -8
View File
@@ -18,18 +18,20 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
const uint64_t currentSector = pos / SectorSize;
const uint8_t memLocation = (uint8_t)memType;
uint64_t numWords = amount / 2;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com] {
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, &numWords] {
return com.sendCommand(Command::NeoReadMemory, {
memLocation,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
uint8_t((currentSector >> 24) & 0xFF),
uint8_t(SectorSize & 0xFF),
uint8_t((SectorSize >> 8) & 0xFF),
uint8_t((SectorSize >> 16) & 0xFF),
uint8_t((SectorSize >> 24) & 0xFF)
uint8_t(numWords & 0xFF),
uint8_t((numWords >> 8) & 0xFF),
uint8_t((numWords >> 16) & 0xFF),
uint8_t((numWords >> 24) & 0xFF)
});
}, NeoMemorySDRead, timeout);
@@ -60,15 +62,17 @@ std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communicati
const uint64_t currentSector = pos / SectorSize;
const uint8_t memLocation = (uint8_t)memType;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, from, amount] {
uint64_t numWords = amount / 2;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, from, amount, &numWords] {
std::vector<uint8_t> command = {
memLocation,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
uint8_t((currentSector >> 24) & 0xFF),
uint8_t(SectorSize & 0xFF),
uint8_t((SectorSize >> 8) & 0xFF),
uint8_t(numWords & 0xFF),
uint8_t((numWords >> 8) & 0xFF),
};
command.insert(command.end(), from, from + amount);
return com.sendCommand(Command::NeoWriteMemory, command);
+43
View File
@@ -0,0 +1,43 @@
#include "icsneo/disk/vsa/vsa.h"
#include "icsneo/communication/packet/ethernetpacket.h"
#include <iostream>
using namespace icsneo;
// VSA Base Class Functions
// VSAMessage Class Functions
std::shared_ptr<Packet> VSAMessage::getPacket() const
{
auto packet = std::make_shared<Packet>();
packet->network = network;
reservePacketData(packet);
packet->data.insert(packet->data.end(), payload.begin(), payload.end());
return packet;
}
// VSAExtendedMessage Class Functions
void VSAExtendedMessage::appendPacket(std::shared_ptr<Packet> packet) const
{
packet->data.insert(packet->data.end(), payload.begin(), payload.end());
// Set the network if not already set (Happens in AA0F records)
if(packet->network.getNetID() == Network::NetID::Invalid) {
packet->network = network;
}
}
void VSAExtendedMessage::truncatePacket(std::shared_ptr<Packet> packet)
{
static constexpr auto EthernetLengthOffset = 26u;
switch(packet->network.getType()) {
case Network::Type::Ethernet:
{
const auto& packetLength = *reinterpret_cast<uint16_t*>(packet->data.data() + EthernetLengthOffset);
const size_t ethernetFrameSize = packetLength - (sizeof(uint16_t) * 2);
const size_t bytestreamExpectedSize = sizeof(HardwareEthernetPacket) + ethernetFrameSize;
packet->data.resize(bytestreamExpectedSize);
}
break;
}
}
+26
View File
@@ -0,0 +1,26 @@
#include "icsneo/disk/vsa/vsa02.h"
using namespace icsneo;
VSA02::VSA02(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA02);
constantIndex = *reinterpret_cast<uint16_t*>(recordBytes + 2);
flags = *reinterpret_cast<Flags*>(recordBytes + 4);
pieceCount = recordBytes[5];
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
samples.insert(samples.end(), recordBytes + 14, recordBytes + 30);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA02::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+24
View File
@@ -0,0 +1,24 @@
#include "icsneo/disk/vsa/vsa03.h"
using namespace icsneo;
VSA03::VSA03(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA03);
eventType = static_cast<EventType>(*reinterpret_cast<uint16_t*>(recordBytes + 2));
eventData = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
doChecksum(recordBytes);
}
void VSA03::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+24
View File
@@ -0,0 +1,24 @@
#include "icsneo/disk/vsa/vsa04.h"
using namespace icsneo;
VSA04::VSA04(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA04);
flags = *reinterpret_cast<Flags*>(recordBytes + 2);
partitionIndex = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
doChecksum(recordBytes);
}
void VSA04::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+23
View File
@@ -0,0 +1,23 @@
#include "icsneo/disk/vsa/vsa05.h"
using namespace icsneo;
VSA05::VSA05(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA05);
errorType = static_cast<ErrorType>(*reinterpret_cast<uint16_t*>(recordBytes + 2));
errorNetwork = *reinterpret_cast<uint16_t*>(recordBytes + 4);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 6) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 14);
}
void VSA05::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 7; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+25
View File
@@ -0,0 +1,25 @@
#include "icsneo/disk/vsa/vsa06.h"
using namespace icsneo;
VSA06::VSA06(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA06);
savedSectors.insert(savedSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 2), reinterpret_cast<uint32_t*>(recordBytes + 18));
error = *reinterpret_cast<uint16_t*>(recordBytes + 18);
savedSectorsHigh = *reinterpret_cast<uint16_t*>(recordBytes + 20);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA06::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+25
View File
@@ -0,0 +1,25 @@
#include "icsneo/disk/vsa/vsa07.h"
using namespace icsneo;
VSA07::VSA07(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA07);
lastSector = *reinterpret_cast<uint32_t*>(recordBytes + 2);
currentSector = *reinterpret_cast<uint32_t*>(recordBytes + 6);
reserved.insert(reserved.end(), recordBytes + 10, recordBytes + 22);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA07::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+24
View File
@@ -0,0 +1,24 @@
#include "icsneo/disk/vsa/vsa08.h"
using namespace icsneo;
VSA08::VSA08(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA08);
troubleSramCount.insert(troubleSramCount.end(), recordBytes + 2, recordBytes + 6);
troubleSectors.insert(troubleSectors.end(), reinterpret_cast<uint32_t*>(recordBytes + 6), reinterpret_cast<uint32_t*>(recordBytes + 20));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA08::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+32
View File
@@ -0,0 +1,32 @@
#include "icsneo/disk/vsa/vsa09.h"
using namespace icsneo;
VSA09::VSA09(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA09);
serialNumber = *reinterpret_cast<uint32_t*>(recordBytes + 2);
firmwareMajorVersion = recordBytes[6];
firmwareMinorVersion = recordBytes[7];
manufactureMajorRevision = recordBytes[8];
manufactureMinorRevision = recordBytes[9];
bootloaderMajorVersion = recordBytes[10];
bootloaderMinorVersion = recordBytes[11];
reserved0.insert(reserved0.end(), recordBytes + 12, recordBytes + 18);
hardwareID = static_cast<HardwareID>(recordBytes[18]);
reserved1.insert(reserved1.end(), recordBytes + 19, recordBytes + 22);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 22) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA09::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+38
View File
@@ -0,0 +1,38 @@
#include "icsneo/disk/vsa/vsa0b.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto PayloadOffset = 4;
VSA0B::VSA0B(uint8_t* const recordBytes)
: VSAMessage(recordBytes + PayloadOffset, CoreMiniPayloadSize, static_cast<Network::CoreMini>(recordBytes[29]))
{
setType(VSA::Type::AA0B);
captureBitfield = reinterpret_cast<uint16_t*>(recordBytes)[1];
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
reserved = recordBytes[28];
checksum = reinterpret_cast<uint16_t*>(recordBytes)[15];
doChecksum(recordBytes);
}
void VSA0B::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
bool VSA0B::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
+27
View File
@@ -0,0 +1,27 @@
#include "icsneo/disk/vsa/vsa0c.h"
using namespace icsneo;
VSA0C::VSA0C(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA0C);
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 2);
audioPreamble = recordBytes[4];
audioHeader = recordBytes[5];
pcmData.insert(pcmData.end(), recordBytes + 6, recordBytes + 20);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
vNetBitfield = *reinterpret_cast<VSA0C::VNet*>(recordBytes + 28);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA0C::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
+107
View File
@@ -0,0 +1,107 @@
#include "icsneo/disk/vsa/vsa0d.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 8;
static constexpr auto FirstPayloadSize = 12;
static constexpr auto ConsecutivePayloadOffset = 4;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0D::VSA0D(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0D);
setIndex(*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0x01FFu);
setSequenceNum((*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0xFE00u) >> 9);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data());
for(size_t i = 0; i < payload.size() / DWordSize; i++) {
runningChecksum += dwords[i];
}
}
// First Record Functions
VSA0DFirst::VSA0DFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0D(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum, static_cast<Network::CoreMini>(recordBytes[29]))
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
setRecordCount(*reinterpret_cast<uint16_t*>(recordBytes + 6));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
vNetInfo = *reinterpret_cast<VNet*>(recordBytes + 28);
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
uint32_t* const timestampDWords = reinterpret_cast<uint32_t*>(timestamp);
runningChecksum += timestampDWords[0];
runningChecksum += timestampDWords[1];
}
void VSA0DFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0DFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
void VSA0DFirst::reorderPayload(std::vector<uint8_t>& secondPayload)
{
std::vector<uint8_t> tempPayload;
tempPayload.insert(tempPayload.end(), secondPayload.begin(), secondPayload.begin() + 4);
uint8_t* timestampBytes = reinterpret_cast<uint8_t*>(&timestamp);
if(timestampIsExtended) {
timestampBytes[7] += 0x80;
}
tempPayload.insert(tempPayload.end(), timestampBytes, timestampBytes + 8);
tempPayload.insert(tempPayload.end(), secondPayload.begin() + 4, secondPayload.end());
payload.clear();
secondPayload.clear();
payload.insert(payload.end(), tempPayload.begin(), tempPayload.begin() + 12); // This is done because the capacity of payload is already 12
secondPayload.insert(secondPayload.end(), tempPayload.begin() + 12, tempPayload.end());
}
bool VSA0DFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
// Consecutive Record Functions
VSA0DConsecutive::VSA0DConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0DFirst> first, bool isLastRecord)
: VSA0D(recordBytes, recordBytes + ConsecutivePayloadOffset, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(getIndex() == 1) {
first->reorderPayload(payload);
} else if(isLastRecord) {
recordChecksum = *reinterpret_cast<uint32_t*>(recordBytes + 28);
doChecksum(recordBytes);
} else {
setChecksumFailed(first->getChecksumFailed());
}
setRecordCount(first->getRecordCount());
}
void VSA0DConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != recordChecksum);
}
+114
View File
@@ -0,0 +1,114 @@
#include "icsneo/disk/vsa/vsa0e.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 10;
static constexpr auto FirstPayloadSize = 10;
static constexpr auto ConsecutivePayloadOffset = 4;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0E::VSA0E(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0E);
setIndex(static_cast<uint16_t>(recordBytes[2]));
setSequenceNum(static_cast<uint16_t>(recordBytes[3]));
if(getIndex() == 0) {
runningChecksum = (static_cast<uint32_t>(payload[0]) << 16) | (static_cast<uint32_t>(payload[1]) << 24);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data() + 2);
for(size_t i = 0; i < (payload.size() - 2) / DWordSize; i++) {
runningChecksum += dwords[i];
}
} else {
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data());
for(size_t i = 0; i < payload.size() / DWordSize; i++) {
runningChecksum += dwords[i];
}
}
}
// First Record Functions
VSA0EFirst::VSA0EFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0E(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum,
static_cast<Network::CoreMini>(*reinterpret_cast<uint16_t*>(recordBytes + 28)))
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
setRecordCount(*reinterpret_cast<uint32_t*>(recordBytes + 6));
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 20) & UINT63_MAX;
timestampIsExtended = (bool)(*reinterpret_cast<uint64_t*>(recordBytes + 20) & (0x8000000000000000));
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 30);
doChecksum(recordBytes);
}
void VSA0EFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for(size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0EFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
bool VSA0EFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if((filter->captureBitfield != captureBitfield && filter->captureBitfield != UINT16_MAX) ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
void VSA0EFirst::reorderPayload(std::vector<uint8_t>& secondPayload)
{
std::vector<uint8_t> tempPayload;
tempPayload.insert(tempPayload.end(), payload.begin(), payload.end());
tempPayload.insert(tempPayload.end(), secondPayload.begin(), secondPayload.begin() + 6);
uint8_t* timestampBytes = reinterpret_cast<uint8_t*>(&timestamp);
if(timestampIsExtended) {
timestampBytes[7] += 0x80;
}
tempPayload.insert(tempPayload.end(), timestampBytes, timestampBytes + 8);
tempPayload.insert(tempPayload.end(), secondPayload.begin() + 6, secondPayload.end());
payload.clear();
secondPayload.clear();
payload.insert(payload.end(), tempPayload.begin(), tempPayload.begin() + 10); // This is done because the capacity of payload is already 10
secondPayload.insert(secondPayload.end(), tempPayload.begin() + 10, tempPayload.end());
}
// Consecutive Record Functions
VSA0EConsecutive::VSA0EConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0EFirst> first, bool isLastRecord)
: VSA0E(recordBytes, recordBytes + ConsecutivePayloadOffset, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(getIndex() == 1) {
first->reorderPayload(payload);
} else if(isLastRecord) {
recordChecksum = *reinterpret_cast<uint32_t*>(recordBytes + 28);
doChecksum(recordBytes);
} else {
setChecksumFailed(first->getChecksumFailed());
}
setRecordCount(first->getRecordCount());
}
void VSA0EConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != recordChecksum);
}
+106
View File
@@ -0,0 +1,106 @@
#include "icsneo/disk/vsa/vsa0f.h"
#include <algorithm>
using namespace icsneo;
static constexpr auto FirstPayloadOffset = 18;
static constexpr auto FirstPayloadSize = 14;
static constexpr auto LastPayloadSize = 24;
static constexpr auto OtherPayloadSize = 28;
// Parent class functions
VSA0F::VSA0F(uint8_t* const recordBytes, uint8_t* const messageBytes, size_t numBytes, uint32_t& runningChecksum, Network::CoreMini networkId)
: VSAExtendedMessage(messageBytes, numBytes, networkId)
{
static constexpr auto DWordSize = 4;
setType(VSA::Type::AA0F);
setIndex(*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0x01FFu);
setSequenceNum((*reinterpret_cast<uint16_t*>(recordBytes + 2) & 0xFE00u) >> 9);
if(getIndex() == 0) {
runningChecksum = (static_cast<uint32_t>(payload[0]) << 16) | (static_cast<uint32_t>(payload[1]) << 24);
uint32_t* dwords = reinterpret_cast<uint32_t*>(payload.data() + 2);
for (size_t i = 0; i < (payload.size() - 2) / DWordSize; i++) {
runningChecksum += dwords[i];
}
} else {
uint32_t* dwords = reinterpret_cast<uint32_t*>(recordBytes);
for (size_t i = 0; i < 8; i++) {
runningChecksum += dwords[i];
}
}
}
// First Record Functions
VSA0FFirst::VSA0FFirst(uint8_t* const recordBytes, uint32_t& runningChecksum)
: VSA0F(recordBytes, recordBytes + FirstPayloadOffset, FirstPayloadSize, runningChecksum)
{
captureBitfield = *reinterpret_cast<uint16_t*>(recordBytes + 4);
uint16_t byteCount = *reinterpret_cast<uint16_t*>(recordBytes + 6);
uint16_t recordCount;
if(byteCount <= FirstPayloadSize) {
recordCount = 1;
} else if(byteCount <= FirstPayloadSize + LastPayloadSize) {
recordCount = 2;
} else {
byteCount -= FirstPayloadSize + LastPayloadSize;
recordCount = 2 + byteCount / OtherPayloadSize;
if (byteCount % OtherPayloadSize != 0) {
recordCount += 1;
}
}
setRecordCount(recordCount);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 8) & UINT63_MAX;
checksum = *reinterpret_cast<uint16_t*>(recordBytes + 16);
doChecksum(recordBytes);
// Network ID is not found in first record for AA0F
// Only the subsequent records have the Network ID in the payload
}
void VSA0FFirst::doChecksum(uint8_t* recordBytes)
{
uint16_t* words = reinterpret_cast<uint16_t*>(recordBytes);
uint16_t sum = 0;
for (size_t i = 0; i < 15; i++) {
sum += words[i];
}
setChecksumFailed(sum != checksum);
}
void VSA0FFirst::reservePacketData(std::shared_ptr<Packet>& packet) const
{
uint32_t numMessageBytes = (getRecordCount() - 2) * OtherPayloadSize + FirstPayloadSize + LastPayloadSize;
packet->data.reserve(numMessageBytes);
}
bool VSA0FFirst::filter(const std::shared_ptr<VSAMessageReadFilter> filter)
{
if(filter->captureBitfield != captureBitfield ||
getICSTimestampFromTimepoint(filter->readRange.first) > timestamp ||
getICSTimestampFromTimepoint(filter->readRange.second) < timestamp) {
return false;
}
return true;
}
// Consecutive Record Functions
VSA0FConsecutive::VSA0FConsecutive(uint8_t* const recordBytes, uint32_t& runningChecksum, std::shared_ptr<VSA0FFirst> first, bool isLastRecord)
: VSA0F(recordBytes, recordBytes + 4, isLastRecord ? LastPayloadSize : OtherPayloadSize, runningChecksum)
{
this->first = first;
calculatedChecksum = runningChecksum;
if(isLastRecord) {
doChecksum(recordBytes);
} else {
network = Network(static_cast<Network::CoreMini>(*reinterpret_cast<uint16_t*>(recordBytes + 28))); // Network ID is stored in 25th and 26th recordBytes of payload
}
setRecordCount(first->getRecordCount());
}
void VSA0FConsecutive::doChecksum(uint8_t* recordBytes)
{
setChecksumFailed(recordBytes && calculatedChecksum != 0);
}
+37
View File
@@ -0,0 +1,37 @@
#include "icsneo/disk/vsa/vsa6a.h"
#include "icsneo/disk/diskdriver.h"
using namespace icsneo;
static constexpr auto PayloadOffset = 56;
static constexpr auto PayloadSize = 452;
static constexpr auto TimestampOffset = 48;
static constexpr auto TimestampSize = 8;
VSA6A::VSA6A(uint8_t* const recordBytes)
: VSA()
{
setType(VSA::Type::AA6A);
sequenceNum = *reinterpret_cast<uint32_t*>(recordBytes + 34);
totalSectors = *reinterpret_cast<uint32_t*>(recordBytes + 38);
reserved = *reinterpret_cast<uint32_t*>(recordBytes + 42);
timestamp = *reinterpret_cast<uint64_t*>(recordBytes + 46) & UINT63_MAX;
timestampSum = *reinterpret_cast<uint16_t*>(recordBytes + 54);
data.insert(data.end(), recordBytes + 56, recordBytes + 508);
checksum = *reinterpret_cast<uint32_t*>(recordBytes + 508);
doChecksum(recordBytes);
}
void VSA6A::doChecksum(uint8_t* recordBytes)
{
uint32_t sum = 0;
for(size_t i = PayloadOffset; i < PayloadOffset+ PayloadSize; i++) {
sum += recordBytes[i];
}
uint16_t tSum = 0;
for(size_t i = TimestampOffset; i < TimestampOffset + TimestampSize; i++) {
tSum += recordBytes[i];
}
setChecksumFailed(sum != checksum || tSum != timestampSum);
}
+460
View File
@@ -0,0 +1,460 @@
#include "icsneo/disk/vsa/vsaparser.h"
#include "icsneo/disk/vsa/vsa02.h"
#include "icsneo/disk/vsa/vsa03.h"
#include "icsneo/disk/vsa/vsa04.h"
#include "icsneo/disk/vsa/vsa05.h"
#include "icsneo/disk/vsa/vsa06.h"
#include "icsneo/disk/vsa/vsa07.h"
#include "icsneo/disk/vsa/vsa08.h"
#include "icsneo/disk/vsa/vsa09.h"
#include "icsneo/disk/vsa/vsa0b.h"
#include "icsneo/disk/vsa/vsa0c.h"
#include "icsneo/disk/vsa/vsa0d.h"
#include "icsneo/disk/vsa/vsa0e.h"
#include "icsneo/disk/vsa/vsa0f.h"
#include "icsneo/disk/vsa/vsa6a.h"
#include "icsneo/disk/diskdriver.h"
#include <iostream>
using namespace icsneo;
bool VSAParser::parseBytes(uint8_t* const bytes, uint64_t arrLen)
{
uint64_t bytesOffset = 0;
while(bytesOffset + VSA::StandardRecordSize <= arrLen) { // Enough bytes to read for Standard Record
if(bytes[bytesOffset] != 0xAAu) {
// Invalid Input
return false;
}
switch(bytes[bytesOffset + 1]) {
case 0x00u: // Pad Record
bytesOffset += VSA::StandardRecordSize;
break;
case 0x01u: // Message Data (Deprecated)
hasDeprecatedRecords = true;
bytesOffset += VSA::StandardRecordSize;
break;
case 0x02u: // Logdata Record
if(settings.extractAA02) {
vsaRecords.push_back(std::make_shared<VSA02>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x03u: // Event Record
if(settings.extractAA03) {
vsaRecords.push_back(std::make_shared<VSA03>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x04u: // Partition Info Record
if(settings.extractAA04) {
vsaRecords.push_back(std::make_shared<VSA04>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x05u: // Application Error Record
if(settings.extractAA05) {
vsaRecords.push_back(std::make_shared<VSA05>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x06u: // Debug/Internal
if(settings.extractAA06) {
vsaRecords.push_back(std::make_shared<VSA06>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x07u: // Debug/Internal
if(settings.extractAA07) {
vsaRecords.push_back(std::make_shared<VSA07>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x08u: // Buffer Info Record
if(settings.extractAA08) {
vsaRecords.push_back(std::make_shared<VSA08>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x09u: // Device Info Record
if(settings.extractAA09) {
vsaRecords.push_back(std::make_shared<VSA09>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Au: // Logger Info Configuration (Deprecated)
hasDeprecatedRecords = true;
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Bu: // Message Data
if(settings.extractAA0B) {
auto record = std::make_shared<VSA0B>(bytes + bytesOffset);
vsaRecords.push_back(record);
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Cu: // PCM Audio Data
if(settings.extractAA0C) {
vsaRecords.push_back(std::make_shared<VSA0C>(bytes + bytesOffset));
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Du: // Message Data (Extended)
if(settings.extractAA0D) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0D)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Eu: // Message Data (Extended)
if(settings.extractAA0E) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0E)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x0Fu: // Message Data (Extended)
if(settings.extractAA0F) {
if(!handleExtendedRecord(bytes, bytesOffset, VSA::Type::AA0F)) {
return false;
}
}
bytesOffset += VSA::StandardRecordSize;
break;
case 0x6Au: // Logger Configuration Backup
if(bytesOffset + Disk::SectorSize <= arrLen) {
if(settings.extractAA6A) {
vsaRecords.push_back(std::make_shared<VSA6A>(bytes + bytesOffset));
}
}
bytesOffset += Disk::SectorSize;
break;
default:
// Unhandled VSA Record Type
return false;
break;
}
}
return true;
}
bool VSAParser::handleExtendedRecord(uint8_t* const bytes, uint64_t& bytesOffset, VSA::Type type)
{
// Gather info about the extended record sequence of the record contained in bytes
std::shared_ptr<VSAExtendedMessage> first;
uint16_t seqNum;
ExtendedMessageState::ExtendedRecordSeqInfo* seqInfo;
uint32_t runningChecksum = 0;
switch(type) {
case VSA::Type::AA0D:
first = std::make_shared<VSA0DFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0DSeqInfo[seqNum];
break;
case VSA::Type::AA0E:
first = std::make_shared<VSA0EFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0ESeqInfo[seqNum];
break;
case VSA::Type::AA0F:
first = std::make_shared<VSA0FFirst>(bytes + bytesOffset, runningChecksum);
seqNum = first->getSequenceNum();
seqInfo = &state.vsa0FSeqInfo[seqNum];
break;
default:
return false; // Invalid type was passed
}
if(seqInfo->nextIndex == 0 && seqInfo->records.size() == 0) { // This is the first record in the sequence
if(first->getIndex() != 0) {
seqInfo->clear();
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
return true; // This is not actually the first record
}
seqInfo->records.push_back(first);
seqInfo->totalRecordCount = first->getRecordCount();
seqInfo->nextIndex++;
seqInfo->runningChecksum = runningChecksum;
} else if(seqInfo->nextIndex < seqInfo->totalRecordCount && seqInfo->records.size() > 0) { // Consecutive Record
std::shared_ptr<VSAExtendedMessage> consecutive;
bool isLast = seqInfo->nextIndex == seqInfo->totalRecordCount - 1;
// Construct the consecutive record from bytes
switch(type) {
case VSA::Type::AA0D:
consecutive = std::make_shared<VSA0DConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0DFirst>(seqInfo->records[0]),
isLast
);
break;
case VSA::Type::AA0E:
consecutive = std::make_shared<VSA0EConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0EFirst>(seqInfo->records[0]),
isLast
);
break;
case VSA::Type::AA0F:
consecutive = std::make_shared<VSA0FConsecutive>(
bytes + bytesOffset,
seqInfo->runningChecksum,
std::dynamic_pointer_cast<VSA0FFirst>(seqInfo->records[0]),
isLast
);
break;
default:
return false;
}
if(consecutive->getIndex() == seqInfo->nextIndex && consecutive->getSequenceNum() == seqNum) { // This record is valid in the sequence
seqInfo->records.push_back(consecutive);
seqInfo->nextIndex++;
} else { // Sequence is out of order/invalid
// Throw away incomplete sequence and report warning
seqInfo->clear();
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::EventWarning);
// Save data for new sequence
if(first->getIndex() == 0) {
seqInfo->records.push_back(first);
seqInfo->totalRecordCount = first->getRecordCount();
seqInfo->nextIndex++;
seqInfo->runningChecksum = runningChecksum;
}
return true;
}
if(seqInfo->nextIndex == seqInfo->totalRecordCount) { // This is the last record in the sequence
if(consecutive->getChecksumFailed()) {
// Fail out if checksum fails
seqInfo->clear();
return false;
}
vsaRecords.insert(vsaRecords.end(), seqInfo->records.begin(), seqInfo->records.end());
seqInfo->clear();
}
} else {
return false; // Undefined behavior
}
return true;
}
VSAParser::RecordParseStatus VSAParser::getRecordFromBytes(uint8_t* const bytes, size_t arrLen, std::shared_ptr<VSA>& record)
{
record = nullptr;
if(arrLen < VSA::StandardRecordSize) {
// Not enough bytes
return VSAParser::RecordParseStatus::InsufficientData;
} else if(bytes[0] != 0xAAu) {
return VSAParser::RecordParseStatus::NotARecordStart;
} else {
switch(bytes[1]) {
case 0x00u: // Pad Record
return VSAParser::RecordParseStatus::Pad;
case 0x01u: // Message Data (Deprecated)
return VSAParser::RecordParseStatus::Deprecated;
case 0x02u: // Logdata Record
if(settings.extractAA02) {
record = std::make_shared<VSA02>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x03u: // Event Record
if(settings.extractAA03) {
record = std::make_shared<VSA03>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x04u: // Partition Info Record
if(settings.extractAA04) {
record = std::make_shared<VSA04>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x05u: // Application Error Record
if(settings.extractAA05) {
record = std::make_shared<VSA05>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x06u: // Debug/Internal
if(settings.extractAA06) {
record = std::make_shared<VSA06>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x07u: // Debug/Internal
if(settings.extractAA07) {
record = std::make_shared<VSA07>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x08u: // Buffer Info Record
if(settings.extractAA08) {
record = std::make_shared<VSA08>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x09u: // Device Info Record
if(settings.extractAA09) {
record = std::make_shared<VSA09>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Au:
return VSAParser::RecordParseStatus::Deprecated;
case 0x0Bu: // Message Data
if(settings.extractAA0B) {
record = std::make_shared<VSA0B>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Cu: // PCM Audio Data
if(settings.extractAA0C) {
record = std::make_shared<VSA0C>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
case 0x0Du: // Message Data (Extended)
if(settings.extractAA0D) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0DFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x0Eu: // Message Data (Extended)
if(settings.extractAA0E) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0EFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x0Fu: // Message Data (Extended)
if(settings.extractAA0F) {
uint32_t payloadChecksum = 0;
const auto& vsa = std::make_shared<VSA0FFirst>(bytes, payloadChecksum);
record = vsa;
if(vsa->getIndex() == 0) {
return VSAParser::RecordParseStatus::Success;
}
// This returns the consecutive record as a first record
return VSAParser::RecordParseStatus::ConsecutiveExtended;
}
break;
case 0x6Au: // Logger Configuration Backup
if(settings.extractAA6A) {
if(arrLen < Disk::SectorSize) {
return VSAParser::RecordParseStatus::InsufficientData;
}
record = std::make_shared<VSA6A>(bytes);
return VSAParser::RecordParseStatus::Success;
}
break;
default:
// Unhandled VSA Record Type
return VSAParser::RecordParseStatus::UnknownRecordType;
break;
}
}
return VSAParser::RecordParseStatus::FilteredOut;
}
void VSAParser::clearParseState()
{
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
state.vsa0DSeqInfo[i].clear();
}
for(size_t i = 0; i < state.vsa0ESeqInfo.size(); i++) {
state.vsa0ESeqInfo[i].clear();
}
for(size_t i = 0; i < state.vsa0DSeqInfo.size(); i++) {
state.vsa0ESeqInfo[i].clear();
}
}
bool VSAParser::extractMessagePackets(std::vector<std::shared_ptr<Packet>>& packets)
{
if(settings != Settings::messageRecords()) {
report(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error);
return false; // We do not have exclusively message records
}
std::shared_ptr<Packet> packet;
bool activeExtendedMessage = false;
VSA::Type previousRecordType = VSA::Type::Invalid;
for(const auto& record : vsaRecords) {
VSA::Type activeRecordType = record->getType();
switch(activeRecordType) {
// Handle standard message records
case VSA::Type::AA0B: {
if(activeExtendedMessage) {
// Non-terminated extended message record
// There was a failure/unexpected behavior in the parsing process
report(APIEvent::Type::VSAExtendedMessageError, APIEvent::Severity::Error);
return false;
}
std::shared_ptr<VSAMessage> messageRecord = std::dynamic_pointer_cast<VSAMessage>(record);
if(!settings.messageFilter || messageRecord->filter(settings.messageFilter)) {
packet = messageRecord->getPacket();
packets.push_back(packet);
}
packet = nullptr;
break;
}
// Handle extended message records
case VSA::Type::AA0D:
case VSA::Type::AA0E:
case VSA::Type::AA0F: {
std::shared_ptr<VSAExtendedMessage> extendedMessageRecord = std::dynamic_pointer_cast<VSAExtendedMessage>(record);
if(!activeExtendedMessage) { // Start new extended message packet
packet = extendedMessageRecord->getPacket();
activeExtendedMessage = true;
previousRecordType = extendedMessageRecord->getType();
} else if(previousRecordType == activeRecordType) { // Continue existing extended message packet
extendedMessageRecord->appendPacket(packet);
if(extendedMessageRecord->getRecordCount() == static_cast<uint32_t>(extendedMessageRecord->getIndex() + 1)) { // Last record in sequence
if(!settings.messageFilter || extendedMessageRecord->filter(settings.messageFilter)) {
VSAExtendedMessage::truncatePacket(packet);
packets.push_back(packet);
}
activeExtendedMessage = false;
packet = nullptr;
previousRecordType = activeRecordType;
}
} else {
// Non-terminated extended message record
// There was a failure/unexpected behavior in the parsing process
activeExtendedMessage = false;
packet = nullptr;
previousRecordType = VSA::Type::Invalid;
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
return false;
}
break;
}
default:
// Non-message record discovered
report(APIEvent::Type::VSAOtherError, APIEvent::Severity::Error);
return false;
}
}
vsaRecords.clear();
return true;
}
+18 -12
View File
@@ -101,17 +101,23 @@ The write blocking status of the device determines the behavior of attempting to
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``.
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.
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``.
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.
``ffmpeg -i out.wav -ar 44100 -acodec pcm_s24le -map_channel 0.0.17 out_upstream_ch8.wav``
EX: If we want to transmit, we will need to construct a mapping from message channels to the input WAV file. So, if we have a monotone WAV file that we are inputting
through the API, then we can map channel ``2`` upstream to channel 0 in the input WAV (the only channel in the WAV file) with the following:
``icsneo::ChannelMap chMap``
``chMap[5] = 0``
Since we are transmitting, we must map our desired A2B message channels to the input WAV file. We have ``0`` representing the single channel in the WAV file and
``5`` representing channel ``2`` upstream in the A2B message from using the formula above ``2 * CHANNEL + IS_UPSTREAM = 2 * 2 + 1 = 5``.
+1 -1
View File
@@ -64,7 +64,7 @@ master_doc = 'index'
# General information about the project.
project = 'libicsneo'
copyright = '2018-2023, Intrepid Control Systems, Inc.'
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
+1 -1
View File
@@ -64,7 +64,7 @@ master_doc = 'index'
# General information about the project.
project = 'libicsneo'
copyright = '2018-2023, Intrepid Control Systems, Inc.'
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
+5
View File
@@ -8,6 +8,7 @@ option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON)
option(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE "Build the Live Data example." ON)
option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON)
option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON)
option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON)
# Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
@@ -53,6 +54,10 @@ if(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE)
add_subdirectory(cpp/mdio)
endif()
if(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE)
add_subdirectory(cpp/vsa)
endif()
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp)
# endif()
-12
View File
@@ -1,15 +1,3 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneoc-interactive-example VERSION 0.2.0)
include(GNUInstallDirs)
# Include libicsneo's include directory
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
if(UNIX)
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
endif()
add_executable(libicsneoc-interactive-example src/main.c)
if(UNIX)
target_link_libraries(libicsneoc-interactive-example ${CMAKE_DL_LIBS})
+3 -1
View File
@@ -166,7 +166,9 @@ char getCharInput(int numArgs, ...) {
va_end(vaList);
while(!found) {
fgets(input, 99, stdin);
if(fgets(input, 99, stdin) == NULL) {
break;
}
if(strlen(input) == 2) {
for(int i = 0; i < numArgs; ++i) {
if(input[0] == *(list + i)) {
-3
View File
@@ -1,6 +1,3 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneoc-legacy-lin-example VERSION 0.2.0)
add_executable(libicsneoc-legacy-lin-example lin/main.c)
add_executable(libicsneoc-legacy-device-settings-example deviceSettings/main.c)
target_link_libraries(libicsneoc-legacy-lin-example icsneolegacy)
+7 -8
View File
@@ -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 {
+6 -7
View File
@@ -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 {
-12
View File
@@ -1,15 +1,3 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneoc-simple-lin-example VERSION 0.2.0)
include(GNUInstallDirs)
# Include libicsneo's include directory
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
if(UNIX)
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
endif()
add_executable(libicsneoc-simple-lin-example lin/main.c)
if(UNIX)
target_link_libraries(libicsneoc-simple-lin-example ${CMAKE_DL_LIBS})
+3 -1
View File
@@ -161,7 +161,9 @@ char getCharInput(int numArgs, ...) {
va_end(vaList);
while(!found) {
fgets(input, 99, stdin);
if(fgets(input, 99, stdin) == NULL) {
break;
}
if(strlen(input) == 2) {
for(int i = 0; i < numArgs; ++i) {
if(input[0] == *(list + i)) {
-25
View File
@@ -1,27 +1,2 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-a2b VERSION 0.2.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-a2b src/a2b.cpp)
target_link_libraries(libicsneocpp-a2b icsneocpp)
+242 -140
View File
@@ -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,23 +240,104 @@ void example4(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);
// Example 5: Wave loop back
void example5(std::shared_ptr<icsneo::Device>& rada2b) {
msg->network = icsneo::Network(icsneo::Network::NetID::I2C2);
msg->controlBytes.resize(1);
msg->controlBytes[0] = static_cast<uint8_t>(0x17u); // Register address for A2B INTTYPE
msg->dataBytes.resize(1, 0);
msg->direction = icsneo::I2CMessage::Direction::Read;
msg->deviceMode = icsneo::I2CMessage::DeviceMode::Controller;
msg->address = static_cast<uint16_t>(0x68); // A2B master node address.
msg->isTXMsg = true;
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::MessageCallback>(
[] (std::shared_ptr<icsneo::Message> newMsg) {
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);
};
if(newMsg->type == icsneo::Message::Type::Frame) {
const auto& frame = std::dynamic_pointer_cast<icsneo::Frame>(newMsg);
if(frame && frame->network.getNetID() == icsneo::Network::NetID::I2C2) {
const auto& i2cMessage = std::dynamic_pointer_cast<icsneo::I2CMessage>(frame);
// Listen on another thread
std::thread listenerThread{listener};
if(!i2cMessage) {
return;
}
// Transmit wave file using example0
if(i2cMessage->controlBytes.size() == 1 && i2cMessage->direction == icsneo::I2CMessage::Direction::Read) {
if(i2cMessage->controlBytes[0] == 0x17u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "Current A2B bus status INTTYPE code: " << static_cast<int>(i2cMessage->dataBytes[0]) << '\n';
}
} else if(i2cMessage->controlBytes[0] == 0x03u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_PRODUCT register: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
} else if(i2cMessage->controlBytes[0] == 0x02u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_VENDOR register: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
} else if(i2cMessage->controlBytes[0] == 0x1C) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_INTMSK1 register value: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
}
}
}
}
}
, msgFilter));
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
msg->controlBytes[0] = 0x03; // Address for A2B_PRODUCT register
example0(rada2b);
listenerThread.join();
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
msg->controlBytes[0] = 0x02; // Address for A2B_VENDOR register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
msg->controlBytes[0] = 0x1C ; // Address for A2B_INTMSK1 register
msg->dataBytes[0] = 0x11;
msg->direction = icsneo::I2CMessage::Direction::Write;
// Write register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit" << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
msg->direction = icsneo::I2CMessage::Direction::Read;
// Read register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
rada2b->removeMessageCallback(handler);
}
/**
* Example 5: Reading A2B sequence chart .puml file
*/
void example5(const std::shared_ptr<icsneo::Device>& rada2b) {
// The A2B sequence chart is located at binary index 0
constexpr uint16_t a2bSequenceChartIndex = 0;
// Create a ostream object to capture sequence chart data
std::ofstream a2bSequenceChart("a2b_sequence_chart.puml", std::ios::out | std::ios::binary);
if(!rada2b->readBinaryFile(a2bSequenceChart, a2bSequenceChartIndex)) {
std::cout << "Failed to read A2B sequence chart" << std::endl;
}
}
void displayUsage() {
@@ -250,10 +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 << "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) {
@@ -303,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) {
@@ -337,7 +439,7 @@ int main(int argc, char** argv) {
example2(rada2b);
break;
case 3:
example3();
example3(rada2b);
break;
case 4:
example4(rada2b);
-25
View File
@@ -1,27 +1,2 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-coremini VERSION 0.2.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-coremini src/coremini.cpp)
target_link_libraries(libicsneocpp-coremini icsneocpp)
+20 -7
View File
@@ -1,5 +1,5 @@
// Usage:
// ./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH]
// ./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] [FLASH | SD]
#include <iostream>
@@ -8,14 +8,14 @@
void displayUsage() {
std::cout << "Usage:\n";
std::cout << "./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH]\n";
std::cout << "./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] [FLASH | SD]\n";
}
int main(int argc, char** argv) {
std::vector<std::string> arguments(argv, argv + argc);
if(arguments.size() != 3) {
if(arguments.size() != 4) {
displayUsage();
return EXIT_FAILURE;
}
@@ -50,14 +50,27 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
if (!device->uploadCoremini(std::make_unique<std::ifstream>(arguments[2], std::ios::binary), icsneo::Disk::MemoryType::Flash))
{
std::string memTypeString = arguments[3];
icsneo::Disk::MemoryType type;
if(memTypeString == "FLASH") {
type = icsneo::Disk::MemoryType::Flash;
} else if(memTypeString == "SD") {
type = icsneo::Disk::MemoryType::SD;
} else {
std::cout << "Incorrect memory type option" << std::endl;
displayUsage();
return EXIT_FAILURE;
}
std::ifstream coreminiFile(arguments[2], std::ios::binary);
if (!device->uploadCoremini(coreminiFile, type)) {
std::cout << "Failed to upload coremini" << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
}
if (!device->startScript(icsneo::Disk::MemoryType::Flash))
{
if (!device->startScript(type)) {
std::cout << "Failed to start script" << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
}
-25
View File
@@ -1,27 +1,2 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-interactive-example VERSION 0.2.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-interactive-example src/InteractiveExample.cpp)
target_link_libraries(libicsneocpp-interactive-example icsneocpp)
-25
View File
@@ -1,27 +1,2 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-lin VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-lin src/LINExample.cpp)
target_link_libraries(libicsneocpp-lin icsneocpp)
+63 -57
View File
@@ -7,32 +7,6 @@
#include "icsneo/communication/message/linmessage.h"
/* Note: This example requires LIN 1 and LIN 2 channels to be connected on the device */
char getCharInput(std::vector<char> allowed) {
bool found = false;
std::string input;
while(!found) {
std::cin >> input;
if(input.length() == 1) {
for(char compare : allowed) {
if(compare == input.c_str()[0]) {
found = true;
break;
}
}
}
if(!found) {
std::cout << "Input did not match expected options. Please try again." << std::endl;
std::cout << "<X or x to quit>" << std::endl;
}
}
return input.c_str()[0];
}
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
@@ -46,7 +20,7 @@ int main() {
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto& device : devices) {
for(auto device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
@@ -54,10 +28,54 @@ int main() {
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl;
std::cout << "OK" << std::endl << std::endl;
int64_t baud = 19200;
std::cout << "Enable LIN commander resistor... ";
ret = device->settings->setCommanderResistorFor(icsneo::Network::NetID::LIN, true);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Disable LIN2 commander resistor... ";
ret = device->settings->setCommanderResistorFor(icsneo::Network::NetID::LIN2, false);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN to operate at " << baud << "bit/s... ";
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::LIN, baud);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN2 to operate at " << baud << "bit/s... ";
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::LIN2, baud);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN mode to NORMAL... ";
ret = device->settings->setLINModeFor(icsneo::Network::NetID::LIN, NORMAL_MODE);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN2 mode to NORMAL... ";
ret = device->settings->setLINModeFor(icsneo::Network::NetID::LIN2, NORMAL_MODE);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Applying settings... ";
ret = device->settings->apply();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Getting LIN Baudrate... ";
int64_t readBaud = device->settings->getBaudrateFor(icsneo::Network::NetID::LIN);
if(readBaud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (readBaud) << "bit/s" << std::endl;
std::cout << "Getting LIN2 Baudrate... ";
readBaud = device->settings->getBaudrateFor(icsneo::Network::NetID::LIN2);
if(readBaud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (readBaud) << "bit/s" << std::endl << std::endl;
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "\tGoing online... ";
std::cout << "Going online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
@@ -68,14 +86,14 @@ int main() {
// A real application would just check the result of icsneo_goOnline() rather than calling this
// This function is intended to be called later on if needed
std::cout << "\tChecking online status... ";
std::cout << "Checking online status... ";
ret = device->isOnline();
if(!ret) {
std::cout << "FAIL\n" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
std::cout << "OK" << std::endl << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
if(icsneo::Message::Type::Frame == message->type) {
@@ -95,7 +113,7 @@ int main() {
}));
// We can transmit messages
std::cout << "\tTransmitting a LIN responder data frame... ";
std::cout << "Transmitting a LIN responder data frame... ";
auto lin_r = std::make_shared<icsneo::LINMessage>();
lin_r->network = icsneo::Network::NetID::LIN2;
lin_r->ID = 0x11;
@@ -104,7 +122,7 @@ int main() {
ret = device->transmit(lin_r); // This will return false if the device does not support LIN
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tTransmitting a LIN commander frame... ";
std::cout << "Transmitting a LIN commander header... ";
auto lin_c = std::make_shared<icsneo::LINMessage>();
lin_c->network = icsneo::Network::NetID::LIN;
lin_c->ID = 0x11;
@@ -112,7 +130,9 @@ int main() {
ret = device->transmit(lin_c);
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
std::cout << "\tTransmitting a LIN commander frame with responder data... ";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Transmitting a LIN commander frame with responder data... ";
auto lin_d = std::make_shared<icsneo::LINMessage>();
lin_d->network = icsneo::Network::NetID::LIN;
lin_d->ID = 0x22;
@@ -121,31 +141,17 @@ int main() {
lin_d->data = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd};
ret = device->transmit(lin_d);
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
std::cout << "<X or x to quit>\n\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Go offline, stop sending and receiving traffic
auto shutdown = [&](){
device->removeMessageCallback(handler);
std::cout << "\tGoing offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tDisconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
};
while(true) {
char input = getCharInput(std::vector<char> {'X', 'x'});
switch(input) {
case 'X':
case 'x':
shutdown();
printf("Exiting program\n");
return 0;
default:
break;
}
}
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;
}
-25
View File
@@ -1,27 +1,2 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-livedata VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-livedata src/LiveDataExample.cpp)
target_link_libraries(libicsneocpp-livedata icsneocpp)
-25
View File
@@ -1,27 +1,2 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-mdio VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-mdio src/MDIOExample.cpp)
target_link_libraries(libicsneocpp-mdio icsneocpp)
-25
View File
@@ -1,27 +1,2 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-simple-example VERSION 0.2.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
target_link_libraries(libicsneocpp-simple-example icsneocpp)
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-vsa-example src/VSAExample.cpp)
target_link_libraries(libicsneocpp-vsa-example icsneocpp)
+251
View File
@@ -0,0 +1,251 @@
#include <iostream>
#include <random>
#include "icsneo/icsneocpp.h"
enum class MessageType {
ShortEth,
LongEth,
CAN,
CANFD
};
const std::vector<std::string> MessageTypeLabels = {"short Ethernet", "long Ethernet", "CAN", "CAN-FD"};
void onEvent(std::shared_ptr<icsneo::APIEvent> event) {
std::cout << event->describe() << std::endl;
}
std::vector<std::shared_ptr<icsneo::Frame>> constructRandomFrames(size_t frameCount, MessageType frameType) {
static constexpr size_t ClassicCANSize = 8;
static constexpr size_t CANFDSize = 64;
static constexpr size_t ShortEthSize = 500;
static constexpr size_t LongEthSize = 1500;
std::vector<std::shared_ptr<icsneo::Frame>> frames;
std::random_device randDev;
std::mt19937 randEngine(randDev());
std::uniform_int_distribution randByteDist(0,255);
auto randByteGen = [&]() -> uint8_t {
return static_cast<uint8_t>(randByteDist(randEngine));
};
for(size_t i = 0; i < frameCount; i++) {
switch(frameType) {
case MessageType::ShortEth:
// Short Ethernet
{
auto frame = std::make_shared<icsneo::EthernetMessage>();
frame->network = icsneo::Network::NetID::Ethernet;
frames.push_back(frame);
frame->data.resize(ShortEthSize);
std::generate(frame->data.begin(), frame->data.end(), randByteGen);
}
break;
case MessageType::LongEth:
// Long Ethernet
{
auto frame = std::make_shared<icsneo::EthernetMessage>();
frame->network = icsneo::Network::NetID::Ethernet;
frames.push_back(frame);
frame->data.resize(LongEthSize);
std::generate(frame->data.begin(), frame->data.end(), randByteGen);
}
break;
case MessageType::CAN:
// Classic CAN
{
auto frame = std::make_shared<icsneo::CANMessage>();
frame->network = icsneo::Network::NetID::HSCAN2;
frames.push_back(frame);
frame->data.resize(ClassicCANSize);
std::generate(frame->data.begin(), frame->data.end(), randByteGen);
}
break;
case MessageType::CANFD:
// CAN FD
{
auto frame = std::make_shared<icsneo::CANMessage>();
frame->network = icsneo::Network::NetID::HSCAN3;
frames.push_back(frame);
frame->data.resize(CANFDSize);
std::generate(frame->data.begin(), frame->data.end(), randByteGen);
frame->isCANFD = true;
}
break;
}
}
return frames;
}
void resetScriptStatus(std::shared_ptr<icsneo::Device> rxDevice, std::shared_ptr<icsneo::Device> txDevice, bool rxInit, bool txInit) {
if(rxInit) {
rxDevice->startScript();
} else {
rxDevice->stopScript();
}
if(txInit) {
txDevice->startScript();
} else {
txDevice->stopScript();
}
}
int main(int argc, char* argv[]) {
if(argc != 3) {
std::cout << "Usage: libicsneocpp-vsa-example <Tx device serial> <Rx device serial>" << std::endl;
return -1;
}
std::string txSerial = argv[1];
std::string rxSerial = argv[2];
// register an event callback so we can see all logged events
icsneo::EventManager::GetInstance().addEventCallback(icsneo::EventCallback(onEvent));
auto devices = icsneo::FindAllDevices();
if(devices.empty()) {
std::cout << "error: no devices found" << std::endl;
return -1;
}
std::shared_ptr<icsneo::Device> txDevice;
std::shared_ptr<icsneo::Device> rxDevice;
for(auto&& d : devices) {
if(txSerial == d->getSerial()) {
txDevice = d;
}
if(rxSerial == d->getSerial()) {
rxDevice = d;
}
}
if(!txDevice) {
std::cout << "error: failed to find a device with serial number: " << txSerial << std::endl;
return -1;
}
if(!rxDevice) {
std::cout << "error: failed to find a device with serial number: " << rxSerial << std::endl;
return -1;
}
std::cout << "info: found " << txDevice->describe() << std::endl;
std::cout << "info: found " << rxDevice->describe() << std::endl;
if(!txDevice->open()) {
std::cout << "error: unable to open device" << std::endl;
}
if(!rxDevice->open()) {
std::cout << "error: unable to open device" << std::endl;
}
const auto& rxInitialCoreminiStatus = rxDevice->getScriptStatus()->isCoreminiRunning;
const auto& txInitialCoreminiStatus = txDevice->getScriptStatus()->isCoreminiRunning;
rxDevice->stopScript();
txDevice->stopScript();
uint64_t origFirstOffset;
std::shared_ptr<icsneo::VSA> origFirstRecord;
if(!rxDevice->findFirstVSARecord(origFirstOffset, origFirstRecord)) {
std::cout << "error: unable to find first VSA record" << std::endl;
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
return -1;
}
std::cout << "info: found first VSA record at " << origFirstOffset << std::endl;
uint64_t origLastOffset;
std::shared_ptr<icsneo::VSA> origLastRecord;
if(!rxDevice->findLastVSARecord(origLastOffset, origLastRecord)) {
std::cout << "error: unable to find last VSA record" << std::endl;
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
return -1;
}
std::cout << "info: found last VSA record at " << origLastOffset << std::endl;
uint64_t canFrameCount = 0;
uint64_t ethFrameCount = 0;
rxDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
if(msg->type != icsneo::Message::Type::Frame) {
return;
}
const auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
if(frame->network.getType() == icsneo::Network::Type::CAN) {
++canFrameCount;
} else if(frame->network.getType() == icsneo::Network::Type::Ethernet) {
++ethFrameCount;
}
}));
icsneo::VSAExtractionSettings settings;
{
auto& filter = settings.filters.emplace_back();
filter.readRange.first = origLastRecord->getTimestampICSClock() - std::chrono::seconds(20);
filter.readRange.second = origLastRecord->getTimestampICSClock() - std::chrono::seconds(10);
}
{
auto& filter = settings.filters.emplace_back();
filter.readRange.first = origFirstRecord->getTimestampICSClock() + std::chrono::seconds(0);
filter.readRange.second = origFirstRecord->getTimestampICSClock() + std::chrono::seconds(10);
}
std::cout << "info: reading two blocks of VSA, 10s from the start and 10s from the end..." << std::endl;
if(!rxDevice->readVSA(settings)) {
std::cout << "error: unable to read VSA" << std::endl;
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
return -1;
}
std::cout << "info: processed " << canFrameCount << " CAN frames and " << ethFrameCount << " Ethernet frames" << std::endl;
rxDevice->startScript();
txDevice->goOnline();
const uint8_t NumFrameTypes = 4;
const size_t FrameCountPerType = 2500;
std::vector<std::shared_ptr<icsneo::Frame>> frames;
for(uint8_t i = 0; i < NumFrameTypes; i++) {
std::cout << "info: transmitting " << FrameCountPerType << " random " << MessageTypeLabels[i] << " frames" << std::endl;
auto tempFrames = constructRandomFrames(FrameCountPerType, static_cast<MessageType>(i));
frames.insert(frames.end(), tempFrames.begin(), tempFrames.end());
if(!txDevice->transmit(tempFrames)) {
std::cout << "error: failed to transmit frames" << std::endl;
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
return -1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(600));
}
size_t currentMessage = 0;
rxDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> msg) {
if(msg->type != icsneo::Message::Type::Frame) {
return;
}
auto frame = std::static_pointer_cast<icsneo::Frame>(msg);
if(frames[currentMessage]->data == frame->data) {
currentMessage++;
}
}));
// Read from original last frame until end of buffer
icsneo::VSAExtractionSettings transmitSettings;
{
auto& filter = transmitSettings.filters.emplace_back();
filter.readRange.first = origLastRecord->getTimestampICSClock();
}
std::cout << "info: reading transmitted random frames..." << std::endl;
if(!rxDevice->readVSA(transmitSettings)) {
std::cout << "error: unable to read VSA" << std::endl;
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
return -1;
}
std::cout << "info: " << currentMessage << " transmitted frames found" << std::endl;
resetScriptStatus(rxDevice, txDevice, rxInitialCoreminiStatus, txInitialCoreminiStatus);
if(currentMessage != FrameCountPerType * NumFrameTypes) {
std::cout << "error: unable to find all transmitted frames" << std::endl;
return -1;
}
return 0;
}
+12
View File
@@ -49,6 +49,7 @@ public:
ValueNotYetPresent = 0x1013,
Timeout = 0x1014,
WiVINotSupported = 0x1015,
RestrictedEntryFlag = 0x1016,
// Device Events
PollingMessageOverflow = 0x2000,
@@ -104,6 +105,8 @@ public:
LiveDataEncoderError = 0x2050,
LiveDataDecoderError = 0x2051,
LiveDataNotSupported = 0x2052,
LINSettingsNotAvailable = 0x2053,
ModeNotFound = 0x2054,
// Transport Events
FailedToRead = 0x3000,
@@ -158,6 +161,15 @@ public:
FTIncorrectDevicePath = FTOK + 31,
FTOtherError = FTOK + 32,
// VSA
VSABufferCorrupted = 0x5000,
VSATimestampNotFound = VSABufferCorrupted + 1,
VSABufferFormatError = VSABufferCorrupted + 2,
VSAMaxReadAttemptsReached = VSABufferCorrupted + 3,
VSAByteParseFailure = VSABufferCorrupted + 4,
VSAExtendedMessageError = VSABufferCorrupted + 5,
VSAOtherError = VSABufferCorrupted + 6,
NoErrorFound = 0xFFFFFFFD,
TooManyEvents = 0xFFFFFFFE,
Unknown = 0xFFFFFFFF
+2 -1
View File
@@ -31,6 +31,7 @@ enum class Command : uint8_t {
GetVBattReq = 0xDF, // Previously known as RED_CMD_VBATT_REQUEST
ScriptStatus = 0xE0, // Previously known as RED_CMD_SCRIPT_STATUS
MiscControl = 0xE7,
NeoEraseMemory = 0xEA, // Previously known as RED_CMD_ERASE_MEMORY
Extended = 0xF0, // Previously known as RED_CMD_EXT_COMM
ExtendedData = 0xF2, // Previously known as RED_CMD_EXTENDED_DATA
FlexRayControl = 0xF3,
@@ -51,7 +52,7 @@ enum class ExtendedCommand : uint16_t {
GetSupportedFeatures = 0x0018,
GetComponentVersions = 0x001A,
Reboot = 0x001C,
SetUploadedFlag = 0x0027,
SetRootFSEntryFlags = 0x0027,
GenericBinaryInfo = 0x0030,
LiveData = 0x0035,
};
+4 -3
View File
@@ -66,7 +66,7 @@ public:
bool removeMessageCallback(int id);
std::shared_ptr<Message> waitForMessageSync(
const std::shared_ptr<MessageFilter>& f = {},
std::chrono::milliseconds timeout = std::chrono::milliseconds(50)) {
std::chrono::milliseconds timeout = std::chrono::milliseconds(500)) {
return waitForMessageSync([](){ return true; }, f, timeout);
}
// onceWaitingDo is a way to avoid race conditions.
@@ -74,7 +74,9 @@ public:
std::shared_ptr<Message> waitForMessageSync(
std::function<bool(void)> onceWaitingDo,
const std::shared_ptr<MessageFilter>& f = {},
std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
std::chrono::milliseconds timeout = std::chrono::milliseconds(500));
void dispatchMessage(const std::shared_ptr<Message>& msg);
std::function<std::unique_ptr<Packetizer>()> makeConfiguredPacketizer;
std::unique_ptr<Packetizer> packetizer;
@@ -93,7 +95,6 @@ protected:
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the redirectionFn
std::mutex syncMessageMutex;
void dispatchMessage(const std::shared_ptr<Message>& msg);
void handleInput(Packetizer& p, std::vector<uint8_t>& readBytes);
private:
+6 -2
View File
@@ -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) {}
@@ -24,10 +26,11 @@ public:
virtual void awaitModeChangeComplete() {}
virtual bool isDisconnected() { return disconnected; };
virtual bool close() = 0;
bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
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;
@@ -54,7 +57,8 @@ protected:
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;
RingBuffer readBuffer = RingBuffer(ICSNEO_DRIVER_RINGBUFFER_SIZE);
moodycamel::BlockingConcurrentQueue<WriteOperation> writeQueue;
std::thread readThread, writeThread;
std::atomic<bool> closing{false};
+75 -406
View File
@@ -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) {}
public:
static constexpr size_t maxAudioBufferSize = 2048;
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;
enum class TDMMode : uint8_t {
TDM2 = 0,
TDM4 = 1,
TDM8 = 2,
TDM12 = 3,
TDM16 = 4,
TDM20 = 5,
TDM24 = 6,
TDM32 = 7,
};
public:
enum class A2BDirection : uint8_t {
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);
if(dir == A2BDirection::Upstream) {
channelIndex++;
}
/**
* 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);
return 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;
/**
* 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;
};
}
@@ -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,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} {}
};
@@ -29,10 +29,10 @@ public:
if(message->type == Message::Type::Frame || message->type == Message::Type::Main51 ||
message->type == Message::Type::RawMessage || message->type == Message::Type::ReadSettings) {
RawMessage& frame = *static_cast<RawMessage*>(message.get());
if(!matchNetworkType(frame.network.getType()))
const auto frame = std::static_pointer_cast<RawMessage>(message);
if(!matchNetworkType(frame->network.getType()))
return false;
if(!matchNetID(frame.network.getNetID()))
if(!matchNetID(frame->network.getNetID()))
return false;
} else if (netid != Network::NetID::Any || networkType != Network::Type::Any) {
return false; // Filtering on a NetID or Type, but this message doesn't have one
@@ -13,6 +13,9 @@ namespace icsneo {
typedef uint16_t icscm_bitfield;
#pragma pack(push, 2)
struct HardwareA2BPacket {
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
@@ -33,12 +36,21 @@ struct HardwareA2BPacket {
icscm_bitfield : 11;
icscm_bitfield rfu2;
} header;
uint8_t offset[8];
uint16_t stats;
struct {
uint64_t TS : 60;
uint64_t : 3; // Reserved for future status bits
uint64_t IsExtended : 1;
} timestamp;
uint16_t networkID;
uint16_t length;
static const size_t coreMiniMessageHeaderSize;
static const size_t a2bMessageMaxLength;
static const size_t a2bHeaderSize;
};
#pragma pack(pop)
}
#endif // __cplusplus
+2 -93
View File
@@ -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;
+75
View File
@@ -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
+230 -4
View File
@@ -43,6 +43,8 @@
#include "icsneo/communication/message/ethphymessage.h"
#include "icsneo/third-party/concurrentqueue/concurrentqueue.h"
#include "icsneo/platform/nodiscard.h"
#include "icsneo/disk/vsa/vsa.h"
#include "icsneo/disk/vsa/vsaparser.h"
#define ICSNEO_FINDABLE_DEVICE_BASE(className, type) \
static constexpr DeviceType::Enum DEVICE_TYPE = type; \
@@ -57,6 +59,13 @@
#define ICSNEO_FINDABLE_DEVICE_BY_PID(className, type, pid) \
static constexpr const uint16_t PRODUCT_ID = pid; \
ICSNEO_FINDABLE_DEVICE_BASE(className, type)
// Devices which are discernable by a serial range
#define ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE(className, type, serialLow, serialHigh) \
static constexpr const char* SERIAL_RANGE_LOW = serialLow; \
static constexpr const char* SERIAL_RANGE_HIGH = serialHigh; \
ICSNEO_FINDABLE_DEVICE_BASE(className, type)
namespace icsneo {
class DeviceExtension;
@@ -148,8 +157,10 @@ public:
int8_t prepareScriptLoad();
bool startScript(Disk::MemoryType memType = Disk::MemoryType::SD);
bool stopScript();
bool clearScript();
bool uploadCoremini(std::unique_ptr<std::istream>&& stream, Disk::MemoryType memType = Disk::MemoryType::SD);
bool clearScript(Disk::MemoryType memType = Disk::MemoryType::SD);
bool uploadCoremini(std::istream& stream, Disk::MemoryType memType = Disk::MemoryType::SD);
bool eraseScriptMemory(Disk::MemoryType memType, uint64_t amount);
virtual std::optional<MemoryAddress> getCoreminiStartAddressFlash() const {
return std::nullopt;
@@ -171,8 +182,14 @@ public:
return std::nullopt;
}
virtual bool supportsEraseMemory() const {
return false;
}
// Message polling related functions
bool enableMessagePolling();
bool disableMessagePolling();
bool isMessagePollingEnabled() { return messagePollingCallbackID != 0; };
@@ -413,6 +430,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;
/**
@@ -568,8 +595,20 @@ 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;
@@ -579,6 +618,103 @@ public:
bool unsubscribeLiveData(const LiveDataHandle& handle);
bool clearAllLiveData();
// VSA Read functions
/**
* Read VSA message records from disk and dispatch the messages via Communication object. Default behavior excludes
* records older than the current CoreMini script and performs a full disk dump of other records. The CoreMini script is
* also stopped by default.
*
* @param extractionSettings Contains filters and other advanced settings for extraction process
*
* @return Returns false if there were failures during the read or parse processes or issues with record formatting, else true
*/
bool readVSA(const VSAExtractionSettings& extractionSettings = VSAExtractionSettings());
/**
* Determines important metadata about VSA record storage on the disk. Terminates at first failed attempt to retrieve information
*
* @param metadata The metadata object to store the probed information into
* @param extractionSettings The settings for this extraction of VSA data
*
* @return True if all metadata information was successfully found
*/
bool probeVSA(VSAMetadata& metadata, const VSAExtractionSettings& extractionSettings);
/**
* Find the first VSA record chronologically from ring buffer in the VSA log file on disk
*
* @param firstOffset Variable used to pass out offset of first record on the disk
* @param firstRecord Variable used to pass out the first record in the buffer
* @param extractionSettings The settings for this extraction of VSA data
* @param optMetadata Metadata about the current state of the VSA log file
* (Must include valid CoreMini timestamp, disk size, and isOverlapped values)
*
* @return True if the first record was found successfully
*/
bool findFirstVSARecord(uint64_t& firstOffset, std::shared_ptr<VSA>& firstRecord,
const VSAExtractionSettings& extractionSettings = VSAExtractionSettings(), std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Find the last record chronologically from ring buffer in the VSA log file with a valid timestamp
*
* @param lastOffset Variable used to pass out the offset of the last record on the disk
* @param lastRecord Variable used to pass out the last record with a valid timestamp
* @param extractionSettings The settings for this extraction of VSA data
* @param optMetadata Metadata about the current state of the VSA log file
* (Must include valid CoreMini timestamp, disk size, and isOverlapped values)
*
* @return True if the last record was found successfully
*/
bool findLastVSARecord(uint64_t& lastOffset, std::shared_ptr<VSA>& lastRecord,
const VSAExtractionSettings& extractionSettings = VSAExtractionSettings(), std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Find the closest VSA record to the desired time_point
*
* @param point The desired time_point of the record
* @param vsaOffset Variable used to pass out offset of record closest to the desired time_point
* @param record Variable used to pass out the record closest to the desired time_point
* @param extractionSettings Settings for this extraction of VSA data
* @param optMetadata Optional param to include metadata about the VSA log file on disk
*
* @return Pair containing the location of the record closest to the desired time_point (in bytes from the beginning of VSA log file) and the record itself
*/
bool findVSAOffsetFromTimepoint(
ICSClock::time_point point, uint64_t& vsaOffset, std::shared_ptr<VSA>& record, const VSAExtractionSettings& extractionSettings = VSAExtractionSettings(),
std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Parse VSA message records with the given filter and dispatch them with this device's com channel
*
* @param metadata Important information about the VSA logfile (including first record location)
* @param extractionSettings Settings for this extraction of VSA data
* @param filter Struct used to determine which bytes to read and to filter out undesired VSA records
*
* @return True if there were no failures reading from disk, parsing VSA records, or dispatching VSA records
*/
bool parseVSA(
VSAMetadata& metadata, const VSAExtractionSettings& extractionSettings = VSAExtractionSettings(),
const VSAMessageReadFilter& filter = VSAMessageReadFilter());
/**
* Wrapper function for Device::readLogicalDisk(pos, into, amount, ...) that handles the VSA record ring buffer.
* Handles pos that is before the VSA::RecordStartOffset or is larger than the diskSize.
* Sets amount to maximum size of ring buffer if given amount is too large.
*
* @param pos Position to start read from in relation to VSA file start
* @param into The buffer to read bytes into from the disk
* @param amount The number of bytes to read into the buffer
* @param metadata Optional metadata param (used to determine disk size and if disk is overlapped)
*
* @return Returns value of return from readLogicalDisk with the given inputs
*/
std::optional<uint64_t> vsaReadLogicalDisk(
uint64_t pos, uint8_t* into, uint64_t amount, std::optional<VSAMetadata> metadata = std::nullopt
);
virtual bool isOnlineSupported() const { return true; }
protected:
bool online = false;
int messagePollingCallbackID = 0;
@@ -756,6 +892,96 @@ private:
void scriptStatusThreadBody();
void stopScriptStatusThreadIfNecessary(std::unique_lock<std::mutex> lk);
// VSA Read functions
/**
* Read the timestamp from disk of the VSA record stored at pos. If the timestamp is unparsable, attempt to read from
* previous records up to minPos
*
* @param parser The parser that is used to create a VSA record from the given buffer
* @param buffer Vector of bytes that stores a sector from the disk
* @param pos The location that the buffer was read from
* @param minPos The leftmost (minimum) offset from the beginning of the VSA log file to attempt to read from
* @param optMetadata Optional param to include metadata about the VSA log file on disk
*
* @return The timestamp of the first valid record found at or before the given position
*/
std::optional<uint64_t> getVSATimestampOrBefore(VSAParser& parser, std::vector<uint8_t>& buffer, uint64_t pos, uint64_t minPos,
std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Read the timestamp from disk of the VSA record stored at pos. If the timestamp is unparsable, attempt to read from
* previous records up to maxPos
*
* @param parser The parser that is used to create a VSA record from the given buffer
* @param buffer Vector of bytes that stores a sector that was previously read from the disk
* @param pos The location that data in the buffer was read from
* @param maxPos The rightmost (maximum) offset from the beginning of the VSA log file to attempt to read from
* @param optMetadata Optional param to include metadata about the VSA log file on disk
*
* @return The timestamp of the first valid record found at or after the given position
*/
std::optional<uint64_t> getVSATimestampOrAfter(VSAParser& parser, std::vector<uint8_t>& buffer, uint64_t pos, uint64_t maxPos,
std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Iterate over VSA records and dispatch the messages contained within them. For extended message records, we concatenate the payloads of all of
* the records together before dispatching. Dispatch is performed by Communication::dispatchMessage(...).
*
* @param parser The parser that holds the VSAMessage records to be dispatched
*
* @return True if dispatching of records is successful without unhandled issues from record parse, else false
*/
bool dispatchVSAMessages(VSAParser& parser);
/**
* Determine if the ring buffer for VSA records has filled entirely and looped to the beginning.
*
* @param optMetadata Optional metadata param with partial information about current state of VSA log file
* (Must contain valid disk size and CoreMini timestamp)
*
* @return True if the buffer has looped; Returns std::nullopt if unable to determine
*/
std::optional<bool> isVSAOverlapped(std::optional<VSAMetadata> optMetadata = std::nullopt);
/**
* Find the first extended message record in the sequence of the given extended message record by backtracking in the disk.
* Results are returned by reference (not through the return value)
*
* @param record The extended message record whose sequence for which to find the first record
* @param pos The position of the given record in the VSA log file (in bytes)
* @param parser Used to parse indices and sequence numbers from the extended message records
* @param metadata Optional param to include metadata about the VSA log file on disk
*
* @return True if the first extended message record in the sequence was successfully found
*/
bool findFirstExtendedVSAFromConsecutive(std::shared_ptr<VSAExtendedMessage>& record, uint64_t& pos,
VSAParser& parser, std::optional<VSAMetadata> metadata = std::nullopt);
/**
* Find the first record before the given position that contains a valid timestamp. Results are returned by reference.
*
* @param record The record from which to backtrack in the VSA buffer
* @param pos The position of the given record in the VSA log file (in bytes)
* @param parser Used to parse records from the VSA buffer
*
* @return True if a record with valid timestamp is found within a set number of reads
*/
bool findPreviousRecordWithTimestamp(std::shared_ptr<VSA>& record, uint64_t& pos, VSAParser& parser);
/**
* Get the creation timestamp of the CoreMini script from VSA log file
*
* @return Timestamp in 25 nanosecond ticks since January 1, 2007
*/
std::optional<uint64_t> getCoreMiniScriptTimestamp();
/**
* Get the size of the VSA file storage system from the CoreMini script
*
* @return The size of the vsa log files on the disk
*/
std::optional<uint64_t> getVSADiskSize();
};
}
+62 -4
View File
@@ -536,15 +536,15 @@ enum
BPS117647
};
/* MasterResistor in LIN_SETTINGS */
enum
/* CommanderResistor in LIN_SETTINGS */
enum : uint8_t
{
RESISTOR_ON,
RESISTOR_OFF
};
/* Mode in LIN_SETTINGS */
enum
enum LINMode
{
SLEEP_MODE,
SLOW_MODE,
@@ -558,7 +558,7 @@ typedef struct _LIN_SETTINGS
uint16_t spbrg; /* Precompiled to be 40Mhz/Baudrate/16 - 1. Only used in neoVI FIRE/FIREVNET(4dw) */
uint8_t brgh; /* Must be zero */
uint8_t numBitsDelay;
uint8_t MasterResistor;
uint8_t CommanderResistor;
uint8_t Mode;
} LIN_SETTINGS;
#define LIN_SETTINGS_SIZE 10
@@ -643,6 +643,7 @@ public:
static std::optional<uint16_t> CalculateGSChecksum(const std::vector<uint8_t>& settings, std::optional<size_t> knownSize = std::nullopt);
static CANBaudrate GetEnumValueForBaudrate(int64_t baudrate);
static int64_t GetBaudrateValueForEnum(CANBaudrate enumValue);
static bool ValidateLINBaudrate(int64_t baudrate);
IDeviceSettings(std::shared_ptr<Communication> com, size_t size) : com(com), report(com->report), structSize(size) {}
virtual ~IDeviceSettings() {}
@@ -700,6 +701,16 @@ public:
return reinterpret_cast<SWCAN_SETTINGS*>((void*)(settings.data() + (offset - settingsInDeviceRAM.data())));
}
virtual const LIN_SETTINGS* getLINSettingsFor(Network net) const { (void)net; return nullptr; }
LIN_SETTINGS* getMutableLINSettingsFor(Network net) {
if(disabled || readonly)
return nullptr;
const uint8_t* offset = (const uint8_t*)getLINSettingsFor(net);
if(offset == nullptr)
return nullptr;
return reinterpret_cast<LIN_SETTINGS*>((void*)(settings.data() + (offset - settingsInDeviceRAM.data())));
}
/**
* Some devices have groupings of networks, where software
* switchable termination can only be applied to one network
@@ -753,6 +764,53 @@ public:
*/
bool setTerminationFor(Network net, bool enabled);
/**
* Check whether software switchable commander resistor is currently
* enabled for a given network in the currently active device settings.
*
* Returns true if the call was successful, otherwise an error
* will have been reported in icsneo::getLastError().
*/
std::optional<bool> isCommanderResistorEnabledFor(Network net) const;
/**
* Enable or disable software switchable commander resistor for a given
* network.
*
* Returns true if the call was successful, otherwise an error
* will have been reported in icsneo::getLastError().
*/
bool setCommanderResistorFor(Network net, bool resistor_on);
/**
* Get LIN mode for a given network in the currently active device
* settings.
*/
std::optional<enum LINMode> getLINModeFor(Network net) const;
/**
* Set LIN mode for a given network.
*
* Returns true if the call was successful, otherwise an error
* will have been reported in icsneo::getLastError().
*/
bool setLINModeFor(Network net, enum LINMode mode);
/**
* Get number of bit delays between commander ID and first responder byte for
* a given network in the currently active device settings.
*/
std::optional<uint8_t> getLINCommanderResponseTimeFor(Network net) const;
/**
* Set number of bit delays between commander ID and first responder byte for
* a given network
*
* Returns true if the call was successful, otherwise an error
* will have been reported in icsneo::getLastError().
*/
bool setLINCommanderResponseTimeFor(Network net, uint8_t bits);
const void* getRawStructurePointer() const { return settingsInDeviceRAM.data(); }
void* getMutableRawStructurePointer() { return settings.data(); }
template<typename T> const T* getStructurePointer() const { return reinterpret_cast<const T*>(getRawStructurePointer()); }
@@ -54,6 +54,10 @@ protected:
return 0;
}
bool supportsEraseMemory() const override {
return true;
}
};
}
@@ -88,6 +88,17 @@ public:
return nullptr;
}
}
const LIN_SETTINGS* getLINSettingsFor(Network net) const override {
auto cfg = getStructurePointer<etherbadge_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::LIN:
return &(cfg->lin1);
default:
return nullptr;
}
}
};
}
@@ -44,6 +44,10 @@ private:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
bool supportsEraseMemory() const override {
return true;
}
};
}
@@ -131,6 +131,23 @@ public:
return nullptr;
}
}
const LIN_SETTINGS* getLINSettingsFor(Network net) const override {
auto cfg = getStructurePointer<neovifire_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::LIN:
return &(cfg->lin1);
case Network::NetID::LIN2:
return &(cfg->lin2);
case Network::NetID::LIN3:
return &(cfg->lin3);
case Network::NetID::LIN4:
return &(cfg->lin4);
default:
return nullptr;
}
}
};
}
@@ -6,7 +6,7 @@
#include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h"
#include "icsneo/device/tree/neovifire2/neovifire2settings.h"
#include "icsneo/disk/neomemorydiskdriver.h"
namespace icsneo {
class NeoVIFIRE2 : public Device {
@@ -89,7 +89,7 @@ public:
protected:
NeoVIFIRE2(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<NeoVIFIRE2Settings>(makeDriver);
initialize<NeoVIFIRE2Settings, Disk::NeoMemoryDiskDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
}
void setupSettings(IDeviceSettings& ssettings) override {
@@ -137,6 +137,10 @@ protected:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
bool supportsEraseMemory() const override {
return true;
}
};
}
@@ -209,6 +209,28 @@ public:
};
}
const LIN_SETTINGS* getLINSettingsFor(Network net) const override {
auto cfg = getStructurePointer<neovifire2_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::LIN:
return &(cfg->lin1);
case Network::NetID::LIN2:
return &(cfg->lin2);
case Network::NetID::LIN3:
return &(cfg->lin3);
case Network::NetID::LIN4:
return &(cfg->lin4);
case Network::NetID::LIN5:
return &(cfg->lin5);
case Network::NetID::LIN6:
return &(cfg->lin6);
default:
return nullptr;
}
}
protected:
ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override {
auto cfg = getStructurePointer<neovifire2_settings_t>();
@@ -84,6 +84,10 @@ protected:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
bool supportsEraseMemory() const override {
return true;
}
};
}
@@ -229,6 +229,32 @@ public:
};
}
const LIN_SETTINGS* getLINSettingsFor(Network net) const override {
auto cfg = getStructurePointer<neovifire3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::LIN:
return &(cfg->lin1);
case Network::NetID::LIN2:
return &(cfg->lin2);
case Network::NetID::LIN3:
return &(cfg->lin3);
case Network::NetID::LIN4:
return &(cfg->lin4);
case Network::NetID::LIN5:
return &(cfg->lin5);
case Network::NetID::LIN6:
return &(cfg->lin6);
case Network::NetID::LIN7:
return &(cfg->lin7);
case Network::NetID::LIN8:
return &(cfg->lin8);
default:
return nullptr;
}
}
protected:
ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override {
auto cfg = getStructurePointer<neovifire3_settings_t>();
@@ -86,6 +86,10 @@ protected:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
bool supportsEraseMemory() const override {
return true;
}
};
}
@@ -212,6 +212,24 @@ public:
};
}
const LIN_SETTINGS* getLINSettingsFor(Network net) const override {
auto cfg = getStructurePointer<neovifire3flexray_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::LIN:
return &(cfg->lin1);
case Network::NetID::LIN2:
return &(cfg->lin2);
case Network::NetID::LIN3:
return &(cfg->lin3);
case Network::NetID::LIN4:
return &(cfg->lin4);
default:
return nullptr;
}
}
protected:
ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override {
auto cfg = getStructurePointer<neovifire3flexray_settings_t>();
@@ -69,6 +69,10 @@ protected:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
bool supportsEraseMemory() const override {
return true;
}
};
}
@@ -109,19 +109,19 @@ public:
switch(net.getNetID()) {
case Network::NetID::HSCAN:
return &(cfg->can1);
case Network::NetID::MSCAN:
return &(cfg->can2);
case Network::NetID::HSCAN2:
return &(cfg->can3);
return &(cfg->can2);
case Network::NetID::HSCAN3:
return &(cfg->can4);
return &(cfg->can3);
case Network::NetID::HSCAN4:
return &(cfg->can5);
return &(cfg->can4);
case Network::NetID::HSCAN5:
return &(cfg->can6);
return &(cfg->can5);
case Network::NetID::HSCAN6:
return &(cfg->can7);
return &(cfg->can6);
case Network::NetID::HSCAN7:
return &(cfg->can7);
case Network::NetID::MSCAN:
return &(cfg->can8);
default:
return nullptr;
@@ -134,19 +134,19 @@ public:
switch(net.getNetID()) {
case Network::NetID::HSCAN:
return &(cfg->canfd1);
case Network::NetID::MSCAN:
return &(cfg->canfd2);
case Network::NetID::HSCAN2:
return &(cfg->canfd3);
return &(cfg->canfd2);
case Network::NetID::HSCAN3:
return &(cfg->canfd4);
return &(cfg->canfd3);
case Network::NetID::HSCAN4:
return &(cfg->canfd5);
return &(cfg->canfd4);
case Network::NetID::HSCAN5:
return &(cfg->canfd6);
return &(cfg->canfd5);
case Network::NetID::HSCAN6:
return &(cfg->canfd7);
return &(cfg->canfd6);
case Network::NetID::HSCAN7:
return &(cfg->canfd7);
case Network::NetID::MSCAN:
return &(cfg->canfd8);
default:
return nullptr;
@@ -170,6 +170,20 @@ public:
};
}
const LIN_SETTINGS* getLINSettingsFor(Network net) const override {
auto cfg = getStructurePointer<neovired2_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::LIN:
return &(cfg->lin1);
case Network::NetID::LIN2:
return &(cfg->lin2);
default:
return nullptr;
}
}
protected:
ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override {
auto cfg = getStructurePointer<neovired2_settings_t>();
@@ -33,7 +33,8 @@ typedef struct {
struct
{
uint16_t hwComLatencyTestEn : 1;
uint16_t : 15;
uint16_t disableUsbCheckOnBoot : 1;
uint16_t : 14;
} flags;
uint16_t network_enabled_on_boot;
CAN_SETTINGS can1;
@@ -56,11 +57,15 @@ typedef struct {
uint32_t pwr_man_timeout;
uint16_t pwr_man_enable;
ETHERNET_SETTINGS2 ethernet;
RAD_GPTP_SETTINGS gPTP;
uint64_t network_enables_5;
} rada2b_settings_t;
#pragma pack(pop)
#ifdef __cplusplus
static_assert(sizeof(rada2b_settings_t) == 340, "RAD-A2B settings size mismatch");
#include <iostream>
class RADA2BSettings : public IDeviceSettings {
@@ -84,8 +89,8 @@ public:
};
enum class ChannelSize : uint8_t {
chSize16 = 0,
chSize32 = 1
chSize32 = 0,
chSize16 = 1
};
enum class RADA2BDevice : uint8_t {
@@ -121,6 +126,18 @@ public:
}
}
const LIN_SETTINGS* getLINSettingsFor(Network net) const override {
auto cfg = getStructurePointer<rada2b_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::LIN:
return &(cfg->lin1);
default:
return nullptr;
}
}
TDMMode getTDMMode(RADA2BDevice device) const {
auto cfg = getStructurePointer<rada2b_settings_t>();
auto &deviceSettings = device == RADA2BDevice::Monitor ? cfg->a2b_monitor : cfg->a2b_node;
@@ -136,14 +153,14 @@ public:
auto cfg = getStructurePointer<rada2b_settings_t>();
auto &deviceSettings = device == RADA2BDevice::Monitor ? cfg->a2b_monitor : cfg->a2b_node;
return static_cast<ChannelSize>(~(deviceSettings.flags & a2bSettingsFlag16bit));
return static_cast<ChannelSize>(deviceSettings.flags & a2bSettingsFlag16bit);
}
uint8_t getChannelOffset(RADA2BDevice device, A2BMessage::A2BDirection dir) const {
uint8_t getChannelOffset(RADA2BDevice device, A2BMessage::Direction dir) const {
auto cfg = getStructurePointer<rada2b_settings_t>();
auto &deviceSettings = device == RADA2BDevice::Monitor ? cfg->a2b_monitor : cfg->a2b_node;
if(dir == A2BMessage::A2BDirection::Upstream) {
if(dir == A2BMessage::Direction::Upstream) {
return deviceSettings.upstreamChannelOffset;
}
@@ -171,11 +188,11 @@ public:
deviceSettings.tdmMode = static_cast<uint8_t>(newMode);
}
void setChannelOffset(RADA2BDevice device, A2BMessage::A2BDirection dir, uint8_t newOffset) {
void setChannelOffset(RADA2BDevice device, A2BMessage::Direction dir, uint8_t newOffset) {
auto cfg = getMutableStructurePointer<rada2b_settings_t>();
auto &deviceSettings = device == RADA2BDevice::Monitor ? cfg->a2b_monitor : cfg->a2b_node;
if(dir == A2BMessage::A2BDirection::Upstream) {
if(dir == A2BMessage::Direction::Upstream) {
deviceSettings.upstreamChannelOffset = newOffset;
}
else {
+9 -47
View File
@@ -3,70 +3,32 @@
#ifdef __cplusplus
#include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/communication/decoder.h"
#include "icsneo/device/tree/radcomet/radcometbase.h"
#include "icsneo/device/tree/radcomet/radcometsettings.h"
namespace icsneo {
class RADCOMET : public Device {
class RADComet : public RADCometBase {
public:
// Serial numbers start with RC
// USB PID is 0x1207, standard driver is FTDI3
// Ethernet MAC allocation is 0x1D, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADCOMET, DeviceType::RADComet, "RC");
ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE(RADComet, DeviceType::RADComet, "RC0000", "RC0299");
static const std::vector<Network>& GetSupportedNetworks() {
static std::vector<Network> supportedNetworks = {
Network::NetID::HSCAN,
Network::NetID::HSCAN2,
Network::NetID::Ethernet,
Network::NetID::OP_Ethernet1,
Network::NetID::OP_Ethernet2,
};
return supportedNetworks;
std::string getProductName() const override {
return "RAD-Comet";
}
bool getEthPhyRegControlSupported() const override { return true; }
protected:
RADCOMET(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADCOMETSettings>(makeDriver);
RADComet(neodevice_t neodevice, const driver_factory_t& makeDriver) : RADCometBase(neodevice) {
initialize<RADCometSettings>(makeDriver);
}
void setupPacketizer(Packetizer& packetizer) override {
Device::setupPacketizer(packetizer);
packetizer.disableChecksum = true;
packetizer.align16bit = false;
}
void setupEncoder(Encoder& encoder) override {
Device::setupEncoder(encoder);
encoder.supportCANFD = true;
encoder.supportEthPhy = true;
}
void setupDecoder(Decoder& decoder) override {
Device::setupDecoder(decoder);
decoder.timestampResolution = 10; // Timestamps are in 10ns increments instead of the usual 25ns
}
void setupSupportedRXNetworks(std::vector<Network>& rxNetworks) override {
for(auto& netid : GetSupportedNetworks())
rxNetworks.emplace_back(netid);
}
// The supported TX networks are the same as the supported RX networks for this device
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
};
}
#endif // __cplusplus
#endif
#endif
@@ -0,0 +1,41 @@
#ifndef __RADCOMET2_H_
#define __RADCOMET2_H_
#ifdef __cplusplus
#include "icsneo/device/tree/radcomet/radcometbase.h"
#include "icsneo/device/tree/radcomet/radcometsettings.h"
namespace icsneo {
class RADComet2 : public RADCometBase {
public:
// Serial numbers start with RC, Comet2 starts at RC0300
// USB PID is 0x1207, standard driver is FTDI3
// Ethernet MAC allocation is 0x1D, standard driver is Raw
ICSNEO_FINDABLE_DEVICE_BY_SERIAL_RANGE(RADComet2, DeviceType::RADComet, "RC0300", "RCZZZZ");
std::string getProductName() const override {
return "RAD-Comet 2";
}
const std::vector<Network>& GetSupportedNetworks() override {
static std::vector<Network> supportedNetworks = RADCometBase::GetSupportedNetworks();
supportedNetworks.push_back(Network::NetID::OP_Ethernet3);
supportedNetworks.push_back(Network::NetID::MDIO4);
return supportedNetworks;
}
protected:
RADComet2(neodevice_t neodevice, const driver_factory_t& makeDriver) : RADCometBase(neodevice) {
initialize<RADCometSettings>(makeDriver);
}
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,73 @@
#ifndef __RADCOMETBASE_H_
#define __RADCOMETBASE_H_
#ifdef __cplusplus
#include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h"
namespace icsneo {
class RADCometBase : public Device {
public:
virtual const std::vector<Network>& GetSupportedNetworks() {
static std::vector<Network> supportedNetworks = {
Network::NetID::HSCAN,
Network::NetID::HSCAN2,
Network::NetID::Ethernet,
Network::NetID::OP_Ethernet1,
Network::NetID::OP_Ethernet2,
Network::NetID::LIN,
Network::NetID::ISO9141,
Network::NetID::MDIO1,
Network::NetID::MDIO2,
Network::NetID::MDIO3,
};
return supportedNetworks;
}
bool getEthPhyRegControlSupported() const override { return true; }
protected:
using Device::Device;
void setupPacketizer(Packetizer& packetizer) override {
Device::setupPacketizer(packetizer);
packetizer.disableChecksum = true;
packetizer.align16bit = false;
}
void setupEncoder(Encoder& encoder) override {
Device::setupEncoder(encoder);
encoder.supportCANFD = true;
encoder.supportEthPhy = true;
}
void setupDecoder(Decoder& decoder) override {
Device::setupDecoder(decoder);
decoder.timestampResolution = 10; // Timestamps are in 10ns increments instead of the usual 25ns
}
void setupSupportedRXNetworks(std::vector<Network>& rxNetworks) override {
for(auto& netid : GetSupportedNetworks())
rxNetworks.emplace_back(netid);
}
// The supported TX networks are the same as the supported RX networks for this device
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
std::optional<MemoryAddress> getCoreminiStartAddressFlash() const override {
return 32*1024*1024;
}
};
}
#endif // __cplusplus
#endif
@@ -54,6 +54,8 @@ typedef struct {
// 10T1S
ETHERNET_SETTINGS2 ethT1s2;
ETHERNET10T1S_SETTINGS t1s2;
uint64_t network_enables_5;
LIN_SETTINGS lin1;
} radcomet_settings_t;
#pragma pack(pop)
@@ -61,9 +63,9 @@ typedef struct {
#include <iostream>
class RADCOMETSettings : public IDeviceSettings {
class RADCometSettings : public IDeviceSettings {
public:
RADCOMETSettings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(radcomet_settings_t)) {}
RADCometSettings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(radcomet_settings_t)) {}
const CAN_SETTINGS* getCANSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radcomet_settings_t>();
if(cfg == nullptr)
@@ -49,6 +49,10 @@ protected:
std::optional<MemoryAddress> getCoreminiStartAddressSD() const override {
return 0;
}
bool supportsEraseMemory() const override {
return true;
}
};
}
@@ -170,6 +170,18 @@ public:
return nullptr;
}
}
const LIN_SETTINGS* getLINSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radgalaxy_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::LIN:
return &(cfg->lin1);
default:
return nullptr;
}
}
};
}
@@ -24,7 +24,7 @@ public:
protected:
RADGigastar(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADGigastarSettings, Disk::ExtExtractorDiskReadDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
initialize<RADGigastarSettings>(makeDriver);
}
void setupPacketizer(Packetizer& packetizer) override {

Some files were not shown because too many files have changed in this diff Show More