Author SHA1 Message Date
Bryant JonesandKyle Schwarz e73f61bfcc Device: Add RADGigastar2 2024-10-21 20:00:29 +00:00
Yaroslav StetsykandKyle Schwarz f552df372b Device: Add RADMoonT1S 2024-10-18 18:23:58 +00:00
Vedant NaikandKyle Schwarz 80904dd84c Device: Gigastar: Add TC10 2024-10-03 19:14:33 +00:00
Kyle Schwarz e2bd0b5a3e Device: Fix getHardwareInfo() timeout 2024-10-02 22:19:54 +00:00
Bryant JonesandKyle Schwarz 24f291dc83 Device: Comet: Update settings 2024-09-26 20:42:41 +00:00
Bryant Jones ee7b66625a Network: Add support for new networks on LIN, SPI, and OP Eth 2024-09-25 14:09:38 -04:00
Bryant Jones 99183a89f9 Communication: Add new app errors 2024-08-29 12:28:01 -04:00
Kyle Schwarz 7f30179cc4 Build: Update D3XX libraries 2024-08-29 15:13:38 +00:00
Kyle Johannes 39bcef0230 Communication: warning fix 2024-08-29 15:01:34 +00:00
Bryant JonesandKyle Schwarz ca370a1310 Device: Comet2: Enable TC10 2024-08-22 16:06:13 +00:00
Kyle Schwarz 564933cb41 Device: Add readCoreminiHeader()
- Fixes NeoMemoryDiskDriver::readLogicalDiskAligned() for flash
- Adds FlashMemoryMessage
2024-08-21 10:42:04 -04:00
Kyle JohannesandKyle Schwarz cac8d760b0 Communication: Add AppErrorMessage support
App errors are responses from the device indicating internal runtime errors.
2024-08-15 16:47:35 +00:00
Kurt WachowskiandKyle Schwarz 75af3220b0 Driver: Block between read attempts
Driver:

* Refactored to limit accessibility of member fields;

Communication:

* readTask() now calls for a blocking wait;
2024-08-13 13:55:12 +00:00
Kyle Schwarz f25a0a4a81 Device: Remove const from bool return type 2024-08-02 11:10:20 -04:00
Kyle Schwarz 387c39d3a0 Platform: TCP: Guard against negative duration 2024-07-29 22:50:16 -04:00
Kyle Schwarz 4476bd8b71 Platform: TCP: Add poll function to Socket 2024-07-30 02:09:24 +00:00
Bryant JonesandKyle Schwarz 14588591e5 Device: Comet2/Comet3: Add support for TC10 API 2024-07-08 14:00:24 +00:00
Kyle Johannes cbcbcbdc5d System Test: FIRE 3 and VCAN 4-2EL 2024-06-11 22:04:09 +00:00
Kyle Schwarz cf1b4778a1 Device: RADMoon2ZL: Add TC10 commands 2024-06-03 08:31:17 -04:00
Yasser Yassine 77928dc93d Driver: Add general predicate parameter for waitForRx 2024-05-28 18:21:22 +00:00
Yasser Yassine 659fcf633c Communication: Add pause feature
Removed redirect read
2024-05-23 21:23:45 +00:00
Yasser Yassine 9a1cd1124d Device: Add device hardware info retrieval support 2024-05-14 19:52:00 +00:00
Jonathan SchwartzandKyle Schwarz 3782e12ef6 Device: Add neoVI Connect support 2024-04-30 12:48:31 +00:00
Kyle Schwarz bc6f5eca9c Device: Conditionally retrieve component versions 2024-04-17 14:36:42 -04:00
Kyle Schwarz 53ba297a44 RADComet: Suppress C4201 for nameless struct/union 2024-04-17 14:35:40 -04:00
Kyle Schwarz d9bf4f423e Device: Fix DFU supported check 2024-04-17 14:25:45 -04:00
Kyle Schwarz ba3bceaee0 RADComet: Update settings 2024-04-17 14:24:56 -04:00
Kyle Schwarz 31d47613e7 RADGigastar: Update settings 2024-04-17 14:24:36 -04:00
Bryant Jones 9c830a91a2 Device: Add RAD-Comet3 support 2024-04-11 21:40:38 +00:00
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
Kurt Wachowski 90e1aa223d Packetizer: 2023-10-12 17:33:05 +00:00
Jonathan Schwartz e0cef880f0 Firmio: Improve exit from Find loop
Device: Null check return from std::dynamic_pointer_cast
2023-10-03 16:24:28 +00:00
Jonathan SchwartzandKyle Schwarz cc7cffe068 Device: FIRE3 Flexray: Add to supported devices for Live Data 2023-09-21 21:28:23 +00:00
Kyle Schwarz e97b307c4d Device: Wait up to 1s for GetAll response
If there's an active disk read in progress the default 50ms timeout is too short.
2023-09-21 16:52:30 +00:00
Kyle Schwarz 0c436621a0 Disk: Refactor ExtExtractorDiskReadDriver
Reading disk data is currently accomplished by redirecting the raw input stream
for the duration of the acquisition, during which no other operation can be
carried out. This change moves disk data reading into the packetizer so the
familiar request/reply with message filters can be used. To accomplish this the
deprecated ISOPIC network type was dropped because the two messages share this
network ID.

Also fixes live data packet lengths which were off-by-one.
2023-09-18 15:44:28 +00:00
Kyle Schwarz 508013baf4 LiveData: Fix unhandled enum warning 2023-08-22 17:55:38 -04:00
Kyle JohannesandKyle Schwarz 8d704b1bbb LiveData: Initial implementation
Add support for live data subscription via Device::subscribeLiveData() and
Device::unsubscribeLiveData(). The live data API can be used to subscribe to
individual "signals", a full list of which can be found in LiveDataValueType.
2023-08-22 16:20:48 -04:00
Max BrombachandKyle Schwarz 018f1fac8e Device: Add disk drivers to RADGigastar, RADMars, and RADGalaxy 2023-08-15 20:21:57 +00:00
Emily Brooks 188a087c51 Device: Wait longer to receive message in heart beat thread 2023-08-15 19:43:08 +00:00
Jonathan SchwartzandKyle Schwarz 53e66b8772 Communication: Fix NetID VNET conflict
There exists a potential conflict with some existing NetIDs and VNET NetIDs in the 200 range. To resolve this there is now an additional optional "expand" argument in various functions that could conflict. In most situations this should be false, and is false for MessageFilters to retain the existing behavior.
2023-08-14 21:47:35 +00:00
Kurt Wachowski 41d6927496 Issue #117 2023-08-02 13:52:52 +00:00
Kyle Schwarz 237bed1b72 Device: Wait for device response in setRTC() 2023-07-18 18:40:41 +00:00
Kyle Schwarz 6c93fc839b FTD3XX: Update library to 1.0.14
Also tweaks FindUSB1.cmake to support a custom prefix libusb.
2023-07-17 19:03:17 +00:00
Kyle Johannes 4cf35acf6c LIN: Fix protected ID not assigned 2023-07-12 00:05:12 +00:00
Kyle Johannes 9ef93eb73e Legacy: SpyMessageToNeoMessage length fix for CAN FD 2023-06-28 17:30:09 -04:00
Kyle Johannes 5b3e21fa7c LIN: C API protected ID automatic calculation 2023-06-22 18:17:00 +00:00
David Rebbe a36d114e1f Updated readme for windows builds 2023-06-21 13:17:05 -04:00
David Rebbe ee9cf26eb2 Updated readme for better linux instructions 2023-06-20 18:29:07 -04:00
Kyle Johannes d7d98855ea Legacy: Update icsnVC40 to support Red 2 settings transactions 2023-06-14 14:39:44 +00:00
Kyle Johannes 245f5089da Network: Add ExtendedData to GetNetIDString 2023-06-05 15:52:29 -04:00
Yasser Yassine d9c12bffe7 Device: Add device binary export support 2023-05-30 21:22:53 +00:00
Joseph Niksa f907d6759f icsneolegacy: implemented getDeviceStatus in C and legacy
-Added missing DLL asserts for getRTC() and setRTC() in icsneoc.h
2023-05-25 16:42:38 +00:00
Jonathan Schwartz 1e773ba9ab Communication: Fix loopLimit when parsing SupportedFeaturesPacket 2023-05-10 23:18:04 +00:00
Bryant Jones f5096b879c MDIO: Network support 2023-05-09 18:14:36 +00:00
Kyle Schwarz 0de23f2817 Driver: FTD3XX: Add Arm support 2023-05-09 16:31:49 +00:00
Jonathan Schwartz 32900ae263 Device: Add GetComponentVersions and GetSupportedFeatures commands
Driver: Fix re-open and failed open cases for TCP

Also enforces even length packets for the RED2, FIRE3, and FIRE3 FlexRay devices.
2023-05-08 21:07:43 +00:00
Kyle Schwarz b6d9ef4c7e EventManager: Add optional debug printing 2023-04-27 18:52:17 +00:00
Kyle Schwarz 2f7c3a2dda Driver: FTD3XX: Use CMake FetchContent for lib 2023-04-25 15:16:15 +00:00
Kyle Schwarz bbf348a6ab Device: Add RADMoon2ZL
Also adds a base class for both Moon2 device types.
2023-04-24 22:39:35 +00:00
Kyle Schwarz 73744bf6d9 Examples: Refactor RTC example 2023-04-21 18:30:13 -04:00
Kyle Schwarz 6b0c588a46 Driver: Add FTD3XX 2023-04-20 20:42:36 +00:00
Joseph Niksa b3bbf91e8c icsneolegacy: Implemented get() and set() RTC functions 2023-04-20 18:37:05 +00:00
Bryant Jones af31aa23ad RAD-Moon3: Add initial support 2023-04-20 17:14:22 +00:00
Joseph Niksa 83f6c611fe Implemented icsneoLoadDefaultSettings to apply the default settings to the device. 2023-04-20 05:00:46 +00:00
Bryant Jones 4fa813ccbc Device: Fix supported device networks for several devices 2023-04-18 13:21:39 +00:00
Yasser Yassine 3ddb832708 A2B: Add additional examples
Device: Add coremini loading example
2023-04-12 16:29:48 +00:00
Kyle Johannes 63c81b1c3d LIN: Legacy API 2023-04-05 15:43:26 +00:00
Kyle Schwarz 0fa7494eab CI: Add Linux platforms 2023-04-04 18:57:42 +00:00
Kyle Schwarz cd7b0b0a7b Driver: PCAP: Properly handle RX timeout
If res == 0 the header and data will not be populated because no message
was RXed.
2023-03-29 00:51:38 -04:00
Bryant Jones d9cdd03618 RAD-Comet: Add initial support 2023-03-28 20:58:12 +00:00
Joseph Niksa 832cf9c84b icsneolegacy: implemented icsneoGetErrorInfo() 2023-03-22 18:39:40 +00:00
Yasser Yassine bf6a059820 Device: Add Coremini script upload function 2023-03-08 18:58:38 +00:00
Kyle Schwarz 9b46d486cb Driver: Add TCP support
Device: Close Driver in heartbeat thread on disconnection
2023-03-08 18:32:26 +00:00
Yasser Yassine ddee1254a0 A2B: Add A2B Tx streaming support
A2B: Add A2BDecoder for streaming wave to A2B device
RADA2B: Add functions to configure settings
2023-03-08 18:22:14 +00:00
Kyle Johannes 539cfa511b LIN: Network support 2023-02-03 18:27:08 +00:00
Emily Brooks 4229d8b66a Device: Update the capture callback to include all of the wivi capture information 2023-01-18 23:17:59 +00:00
jschwartz dc1c4ede9d Device: Add FIRE3 Flexray device type and its settings
Device: Include FIRE3 Flexray in device finder
Device: Correct FIRE3 settings and add additional supported networks
Network: Add additional network enumerations

Communication: Add networks to all switch statements
2023-01-17 09:12:10 -05:00
Kyle Schwarz 9e7d442e83 Update copyright year 2023-01-03 20:57:10 -05:00
Kyle Schwarz 41a569fc2d Device: Consider VSA when calculating logical disk size
When the underlying disk driver has VSA access the total size must be
calculated with respect to the VSA offset.
2022-12-19 14:56:08 +00:00
Kyle Schwarz f4e4a103ad POSIX: PCAP: Check header & data before using 2022-12-19 03:03:12 -05:00
263 changed files with 21102 additions and 2450 deletions
+5
View File
@@ -10,3 +10,8 @@ third-party/concurrentqueue/tests
*.bak
.vs
.cache
*.wav
*.orig
examples/csharp/bin
examples/csharp/obj
test/system
+377 -9
View File
@@ -1,6 +1,14 @@
variables:
DEBIAN_FRONTEND: noninteractive
stages:
- build
- test
- unit_test
- hardware_test
#-------------------------------------------------------------------------------
# Windows
#-------------------------------------------------------------------------------
build windows/x64:
stage: build
@@ -14,17 +22,17 @@ build windows/x64:
tags:
- icsneo-windows
test windows/x64:
stage: test
unit_test windows/x64:
stage: unit_test
script:
- build\libicsneo-tests.exe
- build\libicsneo-unit-tests.exe
dependencies:
- build windows/x64
needs:
- build windows/x64
tags:
- icsneo-windows
timeout: 3m
timeout: 5m
build windows/x86:
stage: build
@@ -38,14 +46,374 @@ build windows/x86:
tags:
- icsneo-windows
test windows/x86:
stage: test
unit_test windows/x86:
stage: unit_test
script:
- build\libicsneo-tests.exe
- build\libicsneo-unit-tests.exe
dependencies:
- build windows/x86
needs:
- build windows/x86
tags:
- icsneo-windows
timeout: 3m
timeout: 5m
#-------------------------------------------------------------------------------
# Ubuntu
#-------------------------------------------------------------------------------
.build_linux_ubuntu_gcc: &build_linux_ubuntu_gcc
stage: build
script:
- apt update -y
- apt upgrade -y
- apt install -y g++ ninja-build cmake libusb-1.0-0-dev libpcap-dev git
- sh ci/build-posix.sh
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- linux-build
.test_linux_ubuntu_gcc: &test_linux_ubuntu_gcc
stage: unit_test
script:
- apt update -y
- apt upgrade -y
- apt install -y libusb-1.0-0-dev libpcap-dev
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
.build_linux_ubuntu_clang: &build_linux_ubuntu_clang
stage: build
script:
- apt update -y
- apt upgrade -y
- apt install -y clang lld ninja-build cmake libusb-1.0-0-dev libpcap-dev git
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- linux-build
.test_linux_ubuntu_clang: &test_linux_ubuntu_clang
stage: unit_test
script:
- apt update -y
- apt upgrade -y
- apt install -y libusb-1.0-0-dev libpcap-dev
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
build linux/ubuntu/2004/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:20.04
unit_test linux/ubuntu/2004/amd64/gcc:
<<: *test_linux_ubuntu_gcc
image: ubuntu:20.04
dependencies:
- build linux/ubuntu/2004/amd64/gcc
needs:
- build linux/ubuntu/2004/amd64/gcc
build linux/ubuntu/2004/amd64/clang:
<<: *build_linux_ubuntu_clang
image: ubuntu:20.04
unit_test linux/ubuntu/2004/amd64/clang:
<<: *test_linux_ubuntu_clang
image: ubuntu:20.04
dependencies:
- build linux/ubuntu/2004/amd64/clang
needs:
- build linux/ubuntu/2004/amd64/clang
build linux/ubuntu/2204/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:22.04
unit_test linux/ubuntu/2204/amd64/gcc:
<<: *test_linux_ubuntu_gcc
image: ubuntu:22.04
dependencies:
- build linux/ubuntu/2204/amd64/gcc
needs:
- build linux/ubuntu/2204/amd64/gcc
build linux/ubuntu/2204/amd64/clang:
<<: *build_linux_ubuntu_clang
image: ubuntu:22.04
unit_test linux/ubuntu/2204/amd64/clang:
<<: *test_linux_ubuntu_clang
image: ubuntu:22.04
dependencies:
- build linux/ubuntu/2204/amd64/clang
needs:
- build linux/ubuntu/2204/amd64/clang
#-------------------------------------------------------------------------------
# Fedora
#-------------------------------------------------------------------------------
.build_linux_fedora_gcc: &build_linux_fedora_gcc
stage: build
cache:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel git
- sh ci/build-posix.sh
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- linux-build
.test_linux_fedora_gcc: &test_linux_fedora_gcc
stage: unit_test
cache:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
.build_linux_fedora_clang: &build_linux_fedora_clang
stage: build
cache:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y clang lld libpcap-devel cmake ninja-build libusb1-devel git
- CC=clang CXX=clang++ LDFLAGS=-fuse-ld=lld sh ci/build-posix.sh
artifacts:
when: always
paths:
- build
expire_in: 3 days
tags:
- linux-build
.test_linux_fedora_clang: &test_linux_fedora_clang
stage: unit_test
cache:
paths:
- /var/cache/dnf
script:
- echo max_parallel_downloads=10 >>/etc/dnf/dnf.conf
- echo fastestmirror=True >>/etc/dnf/dnf.conf
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- build/libicsneo-unit-tests
tags:
- linux-build
timeout: 5m
build linux/fedora/37/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:37
unit_test linux/fedora/37/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:37
dependencies:
- build linux/fedora/37/amd64/gcc
needs:
- build linux/fedora/37/amd64/gcc
build linux/fedora/37/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:37
unit_test linux/fedora/37/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:37
dependencies:
- build linux/fedora/37/amd64/clang
needs:
- build linux/fedora/37/amd64/clang
build linux/fedora/38/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:38
unit_test linux/fedora/38/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:38
dependencies:
- build linux/fedora/38/amd64/gcc
needs:
- build linux/fedora/38/amd64/gcc
build linux/fedora/38/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:38
unit_test linux/fedora/38/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:38
dependencies:
- build linux/fedora/38/amd64/clang
needs:
- build linux/fedora/38/amd64/clang
build linux/fedora/39/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:39
unit_test linux/fedora/39/amd64/gcc:
<<: *test_linux_fedora_gcc
image: fedora:39
dependencies:
- build linux/fedora/39/amd64/gcc
needs:
- build linux/fedora/39/amd64/gcc
build linux/fedora/39/amd64/clang:
<<: *build_linux_fedora_clang
image: fedora:39
unit_test linux/fedora/39/amd64/clang:
<<: *test_linux_fedora_clang
image: fedora:39
dependencies:
- build linux/fedora/39/amd64/clang
needs:
- build linux/fedora/39/amd64/clang
.hw_test: &hw_test
stage: hardware_test
tags:
- libicsneo_hil
timeout: 5m
script:
- echo $GUEST_OS_TAG
- echo $DEVICE_PORT
- /opt/libvirt-driver/prepare.sh
- /opt/libvirt-driver/run.sh
after_script:
- /opt/libvirt-driver/cleanup.sh
.fedora38_needs: &fedora38_needs
needs:
- job: build linux/fedora/38/amd64/clang
artifacts: true
hardware_test fedora38-red2:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: ETH_A
hardware_test fedora38-vcan42:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: USB_D
hardware_test fedora38-fire3:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: ETH_B
hardware_test fedora38-vcan42-EL:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: USB_C
.ubuntu2204_needs: &ubuntu2204_needs
needs:
- job: build linux/ubuntu/2204/amd64/clang
artifacts: true
hardware_test ubuntu2204-red2:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: ETH_A
hardware_test ubuntu2204-vcan42:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: USB_D
hardware_test ubuntu2204-fire3:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: ETH_B
hardware_test ubuntu2204-vcan42-EL:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: USB_C
.win10_needs: &win10_needs
needs:
- job: build windows/x64
artifacts: true
hardware_test win10-red2:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: ETH_A
hardware_test win10-vcan42:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: USB_D
hardware_test win10-fire3:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: ETH_B
hardware_test win10-vcan42-EL:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: USB_C
+162 -20
View File
@@ -1,12 +1,19 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.12)
project(libicsneo VERSION 0.3.0)
option(LIBICSNEO_BUILD_TESTS "Build all tests." OFF)
cmake_policy(SET CMP0074 NEW)
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
endif()
option(LIBICSNEO_BUILD_UNIT_TESTS "Build unit tests." OFF)
option(LIBICSNEO_BUILD_SYSTEM_TESTS "Build system tests." OFF)
option(LIBICSNEO_BUILD_DOCS "Build documentation. Don't use in Visual Studio." OFF)
option(LIBICSNEO_BUILD_EXAMPLES "Build examples." ON)
option(LIBICSNEO_BUILD_ICSNEOC "Build dynamic C library" ON)
option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY_STATIC "Build static icsnVC40 compatibility library" ON)
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
# Device Drivers
@@ -17,6 +24,8 @@ option(LIBICSNEO_ENABLE_FIRMIO "Enable communication between Linux and CoreMini
option(LIBICSNEO_ENABLE_RAW_ETHERNET "Enable devices which communicate over raw ethernet" ON)
option(LIBICSNEO_ENABLE_CDCACM "Enable devices which communicate over USB CDC ACM" ON)
option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX" ON)
option(LIBICSNEO_ENABLE_TCP "Enable devices which communicate over TCP" OFF)
option(LIBICSNEO_ENABLE_FTD3XX "Enable devices which communicate over USB FTD3XX" ON)
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
@@ -41,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)
@@ -157,6 +168,60 @@ else() # Darwin or Linux
endif()
endif()
if(LIBICSNEO_ENABLE_FTD3XX)
if(NOT FTD3XX_ROOT) # allow system override
include(FetchContent)
if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.3.0.10-win-x64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=459e635496ab47d6069c9d3515fdd6d82cba3d95e7ae34f794d66ffdf336e9d1")
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.3.0.10-win-i686.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ce4259ae11772d6ede7d217172156fa392f329b29d9455131f4126a2fb89dad1")
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-macos-universal2.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=0904ac5eda8e1dc4b5aac3714383bcc7792b42dfeb585dce6cbfb8b67b8c0c51")
elseif(UNIX)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-linux-x64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=cf66bf299fc722f050cdd3c36998a670f1df69f7c0df18afa73707277067114b")
endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-linux-aarch64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=66341b5112b9841e959e81400b51711be96fec91894477c5cbfc29b10a0c00a6")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/24.34.0/libftd3xx-1.0.16-linux-armhf.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=cec1f959b48a11eb6b829ed43c81b6ba1c0bcf3e797bafcc84a6376e5ffc3c47")
endif()
endif()
endif()
if(NOT LIBICSNEO_FTD3XX_URL)
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
endif()
FetchContent_Declare(
ftdi3xx
URL ${LIBICSNEO_FTD3XX_URL}
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
)
FetchContent_GetProperties(ftdi3xx)
if(NOT ftdi3xx_POPULATED)
FetchContent_Populate(ftdi3xx)
endif()
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
endif()
find_package(FTD3XX REQUIRED)
list(APPEND PLATFORM_SRC
platform/ftd3xx.cpp
)
endif()
if(LIBICSNEO_ENABLE_TCP)
list(APPEND PLATFORM_SRC
platform/tcp.cpp
)
endif()
if(LIBICSNEO_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
@@ -172,8 +237,13 @@ endforeach()
set(SRC_FILES
communication/message/flexray/control/flexraycontrolmessage.cpp
communication/message/callback/streamoutput/a2bwavoutput.cpp
communication/message/a2bmessage.cpp
communication/message/apperrormessage.cpp
communication/message/neomessage.cpp
communication/message/ethphymessage.cpp
communication/message/linmessage.cpp
communication/message/livedatamessage.cpp
communication/message/tc10statusmessage.cpp
communication/packet/flexraypacket.cpp
communication/packet/canpacket.cpp
communication/packet/a2bpacket.cpp
@@ -181,10 +251,17 @@ set(SRC_FILES
communication/packet/versionpacket.cpp
communication/packet/iso9141packet.cpp
communication/packet/ethphyregpacket.cpp
communication/packet/livedatapacket.cpp
communication/packet/logicaldiskinfopacket.cpp
communication/packet/wivicommandpacket.cpp
communication/packet/i2cpacket.cpp
communication/packet/linpacket.cpp
communication/packet/mdiopacket.cpp
communication/packet/scriptstatuspacket.cpp
communication/packet/componentversionpacket.cpp
communication/packet/supportedfeaturespacket.cpp
communication/packet/genericbinarystatuspacket.cpp
communication/packet/hardwareinfopacket.cpp
communication/decoder.cpp
communication/encoder.cpp
communication/ethernetpacketizer.cpp
@@ -192,6 +269,8 @@ set(SRC_FILES
communication/multichannelcommunication.cpp
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
@@ -205,6 +284,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}
)
@@ -265,6 +360,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})
@@ -281,6 +377,16 @@ endif()
if(LIBICSNEO_ENABLE_FTDI)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
endif()
if(LIBICSNEO_ENABLE_FTD3XX)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTD3XX)
target_link_libraries(icsneocpp PRIVATE FTD3XX::FTD3XX)
endif()
if(LIBICSNEO_ENABLE_TCP)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_TCP)
if(WIN32)
target_link_libraries(icsneocpp PRIVATE ws2_32 iphlpapi)
endif()
endif()
# fatfs
add_subdirectory(third-party/fatfs)
@@ -301,7 +407,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})
@@ -325,10 +430,6 @@ if(LIBICSNEO_ENABLE_RAW_ETHERNET)
endif(WIN32)
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
endif()
if(LIBICSNEO_BUILD_ICSNEOC)
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
target_include_directories(icsneoc
@@ -353,6 +454,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)
@@ -373,8 +475,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()
@@ -387,24 +508,45 @@ if(LIBICSNEO_BUILD_TESTS)
include_directories("${gtest_SOURCE_DIR}/include")
endif()
add_executable(libicsneo-tests
test/main.cpp
test/diskdriverreadtest.cpp
test/diskdriverwritetest.cpp
test/eventmanagertest.cpp
test/ethernetpacketizertest.cpp
test/i2cencoderdecodertest.cpp
test/a2bencoderdecodertest.cpp
add_executable(libicsneo-unit-tests
test/unit/main.cpp
test/unit/diskdriverreadtest.cpp
test/unit/diskdriverwritetest.cpp
test/unit/eventmanagertest.cpp
test/unit/ethernetpacketizertest.cpp
test/unit/i2cencoderdecodertest.cpp
test/unit/linencoderdecodertest.cpp
test/unit/a2bencoderdecodertest.cpp
test/unit/mdioencoderdecodertest.cpp
test/unit/livedataencoderdecodertest.cpp
test/unit/ringbuffertest.cpp
test/unit/apperrordecodertest.cpp
)
target_link_libraries(libicsneo-tests gtest gtest_main)
target_link_libraries(libicsneo-tests icsneocpp)
target_link_libraries(libicsneo-unit-tests gtest gtest_main)
target_link_libraries(libicsneo-unit-tests icsneocpp)
target_include_directories(libicsneo-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
target_include_directories(libicsneo-unit-tests PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
enable_testing()
add_test(NAME libicsneo-test-suite COMMAND libicsneo-tests)
add_test(NAME libicsneo-unit-test-suite COMMAND libicsneo-unit-tests)
endif()
if(LIBICSNEO_BUILD_SYSTEM_TESTS)
if(DEFINED ENV{LIBICSNEO_SYSTEM_TESTS})
include(FetchContent)
file(MAKE_DIRECTORY test/system)
FetchContent_Declare(
SystemTests
GIT_REPOSITORY $ENV{LIBICSNEO_SYSTEM_TESTS}
GIT_TAG main
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test/system
)
FetchContent_MakeAvailable(SystemTests)
else()
message("System test repo not defined!")
endif()
endif()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
+20 -1
View File
@@ -17,6 +17,18 @@
- RADA2B
- CAN works
- Ethernet works
- RADComet
- CAN works
- CAN FD works
- Ethernet works
- RADComet3
- CAN works
- Ethernet works
- neoVI Connect
- CAN works
- CAN FD works
- Ethernet works
- RADGigastar2
- Connecting over USB
- ValueCAN 4 series
@@ -42,4 +54,11 @@
- CAN works
- RADA2B
- CAN works
- Ethernet works
- Ethernet works
- RADMoon3
- RADComet3
- CAN works
- Ethernet works
- RADMoonT1S
- Ethernet works
- RADGigastar2
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2018-2020 Intrepid Control Systems, Inc.
Copyright 2018-2024 Intrepid Control Systems, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+49 -3
View File
@@ -110,21 +110,67 @@ for(size_t i = 0; i < messageCount; i++) {
icsneo_closeDevice(myDevice);
```
### Debugging
To enable debug printing set the `LIBICSNEO_PRINT_EVENTS` environmental variable to the desired `APIEvent::Severity` level, all `Event`s greater than or equal to that level will be printed to stderr. For example, to print all warnings and errors: `LIBICSNEO_PRINT_EVENTS=32`.
## Building from Source
### FTD3XX
Some devices require FTD3XX for USB communication so the [FTDI D3XX library](https://ftdichip.com/drivers/d3xx-drivers/) will be automatically downloaded and included. If you would like to use a system copy of D3XX instead you can set `FTD3XX_ROOT` to the path containing `f3d3xx.h` (`-DFTD3XX_ROOT=<path to directory containing ftd3xx.h>`).
### Windows
- Open a terminal and install the following:
```
winget install Microsoft.VisualStudio.2022.Community
winget install Kitware.CMake
winget install Git.Git
winget install Ninja-build.Ninja
```
- Reboot so cmake is in the system path
- Open a developer Powershell for VS2022 and run the following:
```
git clone https://github.com/intrepidcs/libicsneo
cd libicsneo
cmake -S . -B build -G "Ninja"
cmake --build build
# All dlls and libs are now inside the build directory
```
Building will require MSVC 2017 version 15.7 or newer and CMake to be installed.
### macOS
Getting the dependencies is easiest with the Homebrew package manager. You will also need XCode installed. You can then install CMake, an up-to-date version of GCC or Clang, and `libusb-1.0`.
### Linux
The dependencies are as follows
#### General Dependencies
- CMake 3.12 or above
- GCC 7 or above
- `libusb-1.0-0-dev`
- `libpcap0.8-dev`
- `build-essential` is recommended
If you'd like to be able to run programs that use this library without being root, consider using the included udev rules
#### Fedora
```
dnf install git @development-tools gcc-c++ libpcap-devel libusb1-devel cmake
```
#### Debian/Ubuntu
```
apt install git build-essential libpcap0.8-dev libusb-1.0-0-dev cmake
```
#### Building
```
git clone https://github.com/intrepidcs/libicsneo
cd libicsneo
cmake -S . -B build
cmake --build build
# Optional: Install globally:
cp *.so /usr/local/lib/
```
#### udev
If you'd like to be able to run programs that use this library without being root, consider using the included udev rules:
```
$ sudo cp 99-intrepidcs.rules /etc/udev/rules.d/
cp 99-intrepidcs.rules /etc/udev/rules.d/
udevadm control --reload-rules && udevadm trigger
```
+69 -1
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;
}
@@ -689,3 +698,62 @@ bool icsneo_setTerminationFor(const neodevice_t* device, neonetid_t netid, bool
return device->device->settings->setTerminationFor(Network(netid), enabled);
}
bool icsneo_getRTC(const neodevice_t* device, uint64_t* output)
{
if(!icsneo_isValidNeoDevice(device))
return false;
const std::optional<std::chrono::time_point<std::chrono::system_clock>> rtc = device->device->getRTC();
if(!rtc)
return false;
auto duration = std::chrono::duration_cast<std::chrono::seconds>(rtc->time_since_epoch());
*output = static_cast<uint64_t>(duration.count());
return true;
}
bool icsneo_setRTC(const neodevice_t* device, uint64_t input)
{
if(!icsneo_isValidNeoDevice(device))
return false;
std::chrono::seconds duration(input);
const std::chrono::system_clock::time_point time(duration);
return device->device->setRTC(time);
}
int icsneo_getDeviceStatus(const neodevice_t* device, void* status, size_t* size) {
if(!icsneo_isValidNeoDevice(device))
return false;
if(status == nullptr || size == nullptr)
return false;
std::shared_ptr<Message> msg = device->device->com->waitForMessageSync([&]() {
return device->device->com->sendCommand(Command::RequestStatusUpdate);
}, std::make_shared<MessageFilter>(Network::NetID::DeviceStatus), std::chrono::milliseconds(100));
if(!msg) // Did not receive a message
return false;
auto rawMessage = std::static_pointer_cast<RawMessage>(msg);
if(!rawMessage || (rawMessage->network.getNetID() != Network::NetID::DeviceStatus))
return false;
if(*size < rawMessage->data.size())
return false;
std::copy(rawMessage->data.begin(), rawMessage->data.end(), static_cast<uint8_t*>(status));
*size = rawMessage->data.size();
return true;
}
bool icsneo_isOnlineSupported(const neodevice_t* device) {
if(!icsneo_isValidNeoDevice(device))
return false;
return device->device->isOnlineSupported();
}
+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-2019"
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2024"
VALUE "OriginalFilename", "icsneoc.dll"
VALUE "ProductName", "libicsneo"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+165 -2
View File
@@ -72,6 +72,8 @@ static constexpr const char* MESSAGE_MAX_LENGTH_EXCEEDED = "The message was too
static constexpr const char* VALUE_NOT_YET_PRESENT = "The value is not yet present.";
static constexpr const char* TIMEOUT = "The timeout was reached.";
static constexpr const char* WIVI_NOT_SUPPORTED = "Wireless neoVI functions are not supported on this device.";
static constexpr const char* RESTRICTED_ENTRY_FLAG = "Attempted to set a restricted flag in a Root Directory entry.";
static constexpr const char* NOT_SUPPORTED = "The requested feature is not supported.";
// Device Errors
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
@@ -113,6 +115,11 @@ static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An idea
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same.";
static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected.";
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device.";
static constexpr const char* MODE_NOT_FOUND = "The mode was not found.";
// Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
@@ -125,6 +132,55 @@ static constexpr const char* DEVICE_IN_USE = "The device is currently in use by
static constexpr const char* PCAP_COULD_NOT_START = "The PCAP driver could not be started. Ethernet devices will not be found.";
static constexpr const char* PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
static constexpr const char* PACKET_DECODING = "There was an error decoding a packet from the device.";
static constexpr const char* SOCKET_FAILED_TO_OPEN = "Unable to open new socket.";
static constexpr const char* FAILED_TO_BIND = "Unable to bind socket.";
static constexpr const char* ERROR_SETTING_SOCKET_OPTION = "A call to setsockopt() failed.";
static constexpr const char* GETIFADDRS_ERROR = "A call to getifaddrs() failed.";
static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed.";
// FTD3XX
static constexpr const char* FT_OK = "FTD3XX success.";
static constexpr const char* FT_INVALID_HANDLE = "Invalid FTD3XX handle.";
static constexpr const char* FT_DEVICE_NOT_FOUND = "FTD3XX device not found.";
static constexpr const char* FT_DEVICE_NOT_OPENED = "FTD3XX device not opened.";
static constexpr const char* FT_IO_ERROR = "FTD3XX IO error.";
static constexpr const char* FT_INSUFFICIENT_RESOURCES = "Insufficient resources for FTD3XX.";
static constexpr const char* FT_INVALID_PARAMETER = "Invalid FTD3XX parameter.";
static constexpr const char* FT_INVALID_BAUD_RATE = "Invalid FTD3XX baud rate.";
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_ERASE = "FTD3XX device not opened for erase.";
static constexpr const char* FT_DEVICE_NOT_OPENED_FOR_WRITE = "FTD3XX not opened for write.";
static constexpr const char* FT_FAILED_TO_WRITE_DEVICE = "FTD3XX failed to write device.";
static constexpr const char* FT_EEPROM_READ_FAILED = "FTD3XX EEPROM read failed.";
static constexpr const char* FT_EEPROM_WRITE_FAILED = "FTD3XX EEPROM write failed.";
static constexpr const char* FT_EEPROM_ERASE_FAILED = "FTD3XX EEPROM erase failed.";
static constexpr const char* FT_EEPROM_NOT_PRESENT = "FTD3XX EEPROM not present.";
static constexpr const char* FT_EEPROM_NOT_PROGRAMMED = "FTD3XX EEPROM not programmed.";
static constexpr const char* FT_INVALID_ARGS = "Invalid FTD3XX arguments.";
static constexpr const char* FT_NOT_SUPPORTED = "FTD3XX not supported.";
static constexpr const char* FT_NO_MORE_ITEMS = "No more FTD3XX items.";
static constexpr const char* FT_TIMEOUT = "FTD3XX timeout.";
static constexpr const char* FT_OPERATION_ABORTED = "FTD3XX operation aborted.";
static constexpr const char* FT_RESERVED_PIPE = "Reserved FTD3XX pipe.";
static constexpr const char* FT_INVALID_CONTROL_REQUEST_DIRECTION = "Invalid FTD3XX control request direction.";
static constexpr const char* FT_INVALID_CONTROL_REQUEST_TYPE = "Invalid FTD3XX control request type.";
static constexpr const char* FT_IO_PENDING = "FTD3XX IO pending.";
static constexpr const char* FT_IO_INCOMPLETE = "FTD3XX IO incomplete.";
static constexpr const char* FT_HANDLE_EOF = "Handle FTD3XX EOF.";
static constexpr const char* FT_BUSY = "FTD3XX busy.";
static constexpr const char* FT_NO_SYSTEM_RESOURCES = "No FTD3XX system resources.";
static constexpr const char* FT_DEVICE_LIST_NOT_READY = "FTD3XX device list not ready.";
static constexpr const char* FT_DEVICE_NOT_CONNECTED = "FTD3XX device not connected.";
static constexpr const char* FT_INCORRECT_DEVICE_PATH = "Incorrect FTD3XX device path.";
static constexpr const char* FT_OTHER_ERROR = "Other FTD3XX error.";
// VSA
static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted.";
static constexpr const char* VSA_TIMESTAMP_NOT_FOUND = "Unable to find a VSA record with a valid timestamp.";
static constexpr const char* VSA_BUFFER_FORMAT_ERROR = "VSA record buffer is formatted incorrectly.";
static constexpr const char* VSA_MAX_READ_ATTEMPTS_REACHED = "Reached max attempts to read VSA records before exit.";
static constexpr const char* VSA_BYTE_PARSE_FAILURE = "Failure to parse record bytes from VSA buffer.";
static constexpr const char* VSA_EXTENDED_MESSAGE_ERROR = "Failure to parse extended message record sequence";
static constexpr const char* VSA_OTHER_ERROR = "Unknown error in VSA read API.";
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
@@ -164,6 +220,10 @@ const char* APIEvent::DescriptionForType(Type type) {
return TIMEOUT;
case Type::WiVINotSupported:
return WIVI_NOT_SUPPORTED;
case Type::RestrictedEntryFlag:
return RESTRICTED_ENTRY_FLAG;
case Type::NotSupported:
return NOT_SUPPORTED;
// Device Errors
case Type::PollingMessageOverflow:
@@ -244,7 +304,16 @@ const char* APIEvent::DescriptionForType(Type type) {
return WIVI_UPLOAD_STACK_OVERFLOW;
case Type::A2BMessageIncompleteFrame:
return A2B_MESSAGE_INCOMPLETE_FRAME;
case Type::CoreminiUploadVersionMismatch:
return COREMINI_UPLOAD_VERSION_MISMATCH;
case Type::DiskNotConnected:
return DISK_NOT_CONNECTED;
case Type::UnexpectedResponse:
return UNEXPECTED_RESPONSE;
case Type::LINSettingsNotAvailable:
return LIN_SETTINGS_NOT_AVAILABLE;
case Type::ModeNotFound:
return MODE_NOT_FOUND;
// Transport Errors
case Type::FailedToRead:
return FAILED_TO_READ;
@@ -266,7 +335,101 @@ const char* APIEvent::DescriptionForType(Type type) {
return PCAP_COULD_NOT_FIND_DEVICES;
case Type::PacketDecodingError:
return PACKET_DECODING;
case Type::SocketFailedToOpen:
return SOCKET_FAILED_TO_OPEN;
case Type::FailedToBind:
return FAILED_TO_BIND;
case Type::ErrorSettingSocketOption:
return ERROR_SETTING_SOCKET_OPTION;
case Type::GetIfAddrsError:
return GETIFADDRS_ERROR;
case Type::SendToError:
return SEND_TO_ERROR;
// FTD3XX
case Type::FTOK:
return FT_OK;
case Type::FTInvalidHandle:
return FT_INVALID_HANDLE;
case Type::FTDeviceNotFound:
return FT_DEVICE_NOT_FOUND;
case Type::FTDeviceNotOpened:
return FT_DEVICE_NOT_OPENED;
case Type::FTIOError:
return FT_IO_ERROR;
case Type::FTInsufficientResources:
return FT_INSUFFICIENT_RESOURCES;
case Type::FTInvalidParameter:
return FT_INVALID_PARAMETER;
case Type::FTInvalidBaudRate:
return FT_INVALID_BAUD_RATE;
case Type::FTDeviceNotOpenedForErase:
return FT_DEVICE_NOT_OPENED_FOR_ERASE;
case Type::FTDeviceNotOpenedForWrite:
return FT_DEVICE_NOT_OPENED_FOR_WRITE;
case Type::FTFailedToWriteDevice:
return FT_FAILED_TO_WRITE_DEVICE;
case Type::FTEEPROMReadFailed:
return FT_EEPROM_READ_FAILED;
case Type::FTEEPROMWriteFailed:
return FT_EEPROM_WRITE_FAILED;
case Type::FTEEPROMEraseFailed:
return FT_EEPROM_ERASE_FAILED;
case Type::FTEEPROMNotPresent:
return FT_EEPROM_NOT_PRESENT;
case Type::FTEEPROMNotProgrammed:
return FT_EEPROM_NOT_PROGRAMMED;
case Type::FTInvalidArgs:
return FT_INVALID_ARGS;
case Type::FTNotSupported:
return FT_NOT_SUPPORTED;
case Type::FTNoMoreItems:
return FT_NO_MORE_ITEMS;
case Type::FTTimeout:
return FT_TIMEOUT;
case Type::FTOperationAborted:
return FT_OPERATION_ABORTED;
case Type::FTReservedPipe:
return FT_RESERVED_PIPE;
case Type::FTInvalidControlRequestDirection:
return FT_INVALID_CONTROL_REQUEST_DIRECTION;
case Type::FTInvalidControlRequestType:
return FT_INVALID_CONTROL_REQUEST_TYPE;
case Type::FTIOPending:
return FT_IO_PENDING;
case Type::FTIOIncomplete:
return FT_IO_INCOMPLETE;
case Type::FTHandleEOF:
return FT_HANDLE_EOF;
case Type::FTBusy:
return FT_BUSY;
case Type::FTNoSystemResources:
return FT_NO_SYSTEM_RESOURCES;
case Type::FTDeviceListNotReady:
return FT_DEVICE_LIST_NOT_READY;
case Type::FTDeviceNotConnected:
return FT_DEVICE_NOT_CONNECTED;
case Type::FTIncorrectDevicePath:
return FT_INCORRECT_DEVICE_PATH;
case Type::FTOtherError:
return FT_OTHER_ERROR;
// VSA
case Type::VSABufferCorrupted:
return VSA_BUFFER_CORRUPTED;
case Type::VSATimestampNotFound:
return VSA_TIMESTAMP_NOT_FOUND;
case Type::VSABufferFormatError:
return VSA_BUFFER_FORMAT_ERROR;
case Type::VSAMaxReadAttemptsReached:
return VSA_MAX_READ_ATTEMPTS_REACHED;
case Type::VSAByteParseFailure:
return VSA_BYTE_PARSE_FAILURE;
case Type::VSAExtendedMessageError:
return VSA_EXTENDED_MESSAGE_ERROR;
case Type::VSAOtherError:
return VSA_OTHER_ERROR;
// Other Errors
case Type::TooManyEvents:
return TOO_MANY_EVENTS;
+22
View File
@@ -1,5 +1,8 @@
#include "icsneo/api/eventmanager.h"
#include <memory>
#include <optional>
#include <iostream>
#include <cstdlib>
using namespace icsneo;
@@ -33,6 +36,25 @@ void EventManager::cancelErrorDowngradingOnCurrentThread() {
void EventManager::add(APIEvent event) {
if(destructing)
return;
static const auto printLevel = []() -> std::optional<uint8_t> {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
const auto level = std::getenv("LIBICSNEO_PRINT_EVENTS");
#ifdef _MSC_VER
#pragma warning(pop)
#endif
if(!level)
return std::nullopt;
try {
return (uint8_t)std::stoi(level);
} catch (std::invalid_argument const&) {
return std::nullopt;
}
}();
if(printLevel && (uint8_t)event.getSeverity() >= *printLevel)
std::cerr << event.describe() << std::endl;
if(event.getSeverity() == APIEvent::Severity::Error) {
// if the error was added on a thread that downgrades errors (non-user thread)
std::lock_guard<std::mutex> lk(downgradedThreadsMutex);
@@ -3,8 +3,6 @@
#include <Tchar.h>
//Basic Functions
FINDNEODEVICES icsneoFindNeoDevices;
OPENNEODEVICE icsneoOpenNeoDevice;
OPENDEVICE icsneoOpenDevice;
CLOSEPORT icsneoClosePort;
FREEOBJECT icsneoFreeObject;
@@ -134,10 +132,6 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
if((hAPIDLL = LoadLibrary(_T("icsneo40.dll"))) == NULL)
return false;
icsneoFindNeoDevices = (FINDNEODEVICES) GetProcAddress(hAPIDLL, "icsneoFindNeoDevices");
icsneoOpenNeoDevice = (OPENNEODEVICE) GetProcAddress(hAPIDLL, "icsneoOpenNeoDevice");
icsneoOpenDevice = (OPENDEVICE) GetProcAddress(hAPIDLL, "icsneoOpenDevice");
icsneoClosePort = (CLOSEPORT) GetProcAddress(hAPIDLL, "icsneoClosePort");
icsneoFreeObject = (FREEOBJECT) GetProcAddress(hAPIDLL, "icsneoFreeObject");
@@ -206,7 +200,7 @@ bool LoadDLLAPI(HINSTANCE &hAPIDLL)
icsneoEnableDOIPLine = (ENABLEDOIPACTIVATIONLINE)GetProcAddress(hAPIDLL, "icsneoEnableDOIPLine");
if(!icsneoFindNeoDevices || !icsneoOpenNeoDevice || !icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
if(!icsneoOpenDevice || !icsneoClosePort || !icsneoFreeObject ||
!icsneoTxMessages || !icsneoGetMessages || !icsneoWaitForRxMessagesWithTimeOut ||
!icsneoGetTimeStampForMsg || !icsneoEnableNetworkRXQueue || !icsneoGetISO15765Status || !icsneoTxMessagesEx ||
!icsneoSetISO15765RxParameters || !icsneoGetConfiguration || !icsneoSendConfiguration ||
@@ -114,8 +114,6 @@ typedef int (__stdcall *SCRIPTWRITEISO15765TXMESSAGE)(void * hObject, unsigned
//Basic Functions
extern FINDNEODEVICES icsneoFindNeoDevices;
extern OPENNEODEVICE icsneoOpenNeoDevice;
extern OPENDEVICE icsneoOpenDevice;
extern CLOSEPORT icsneoClosePort;
extern FREEOBJECT icsneoFreeObject;
+350 -170
View File
@@ -14,18 +14,19 @@
#include "icsneo/communication/network.h"
#include <map>
#include <algorithm>
#include <cstring>
#include <climits>
#ifdef _MSC_VER
#pragma warning(disable : 4100) // unreferenced formal parameter
#pragma warning(disable : 4996) // STL time functions
#endif
using namespace icsneo;
typedef uint64_t legacymaphandle_t;
static std::map<legacymaphandle_t, neodevice_t> neodevices;
static std::map<neodevice_t*, NeoDeviceEx*> openneodevices;
static const std::map<size_t, size_t> mp_netIDToVnetOffSet = {
{NETID_HSCAN, 1},
@@ -51,18 +52,6 @@ static const std::map<size_t, size_t> mp_HWnetIDToCMnetID = {
static unsigned long vnet_table[] = {0, PLASMA_SLAVE1_OFFSET, PLASMA_SLAVE2_OFFSET};
static NeoDevice OldNeoDeviceFromNew(const neodevice_t* newnd)
{
NeoDevice oldnd = {0};
oldnd.DeviceType = newnd->type;
oldnd.SerialNumber = icsneo_serialStringToNum(newnd->serial);
oldnd.NumberOfClients = 0;
oldnd.MaxAllowedClients = 1;
static_assert(sizeof(neodevice_handle_t) == sizeof(oldnd.Handle),
"neodevice_handle_t size must be sizeof(int) for compatibility reasons");
oldnd.Handle = newnd->handle;
return oldnd;
}
static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t& newmsg, icsSpyMessage& oldmsg)
{
@@ -73,6 +62,25 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
return false;
const neomessage_frame_t& frame = *reinterpret_cast<const neomessage_frame_t*>(&newmsg);
auto copyStatusData = [&]() {
oldmsg.NetworkID = static_cast<uint8_t>(frame.netid); // Note: NetID remapping from the original API is not supported
oldmsg.NetworkID2 = static_cast<uint8_t>(frame.netid >> 8);
oldmsg.DescriptionID = frame.description;
oldmsg.StatusBitField = frame.status.statusBitfield[0];
oldmsg.StatusBitField2 = frame.status.statusBitfield[1];
oldmsg.StatusBitField3 = frame.status.statusBitfield[2];
oldmsg.StatusBitField4 = frame.status.statusBitfield[3];
};
auto copyFrameData = [&]() {
oldmsg.ExtraDataPtr = (void*)frame.data;
oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0;
memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8));
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
copyStatusData();
};
switch (Network::Type(frame.type))
{
case Network::Type::CAN:
@@ -81,28 +89,42 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
oldmsg.Protocol = frame.status.canfdFDF ? SPY_PROTOCOL_CANFD : SPY_PROTOCOL_CAN;
oldmsg.NumberBytesData = static_cast<uint8_t>(std::min(frame.length, (size_t)255));
oldmsg.NumberBytesHeader = 4;
copyFrameData();
break;
case Network::Type::Ethernet:
oldmsg.Protocol = SPY_PROTOCOL_ETHERNET;
oldmsg.NumberBytesData = static_cast<uint8_t>(frame.length & 0xFF);
oldmsg.NumberBytesHeader = static_cast<uint8_t>(frame.length >> 8);
copyFrameData();
break;
case Network::Type::LIN:
{
const neomessage_lin_t& linFrame = *reinterpret_cast<const neomessage_lin_t*>(&frame);
icsSpyMessageJ1850& linSpyMsg = *reinterpret_cast<icsSpyMessageJ1850*>(&oldmsg);
linSpyMsg.Protocol = SPY_PROTOCOL_LIN;
linSpyMsg.NumberBytesHeader = static_cast<uint8_t>(std::min(linFrame.length, static_cast<size_t>(3)));
linSpyMsg.NumberBytesData = static_cast<uint8_t>(linFrame.length - linSpyMsg.NumberBytesHeader);
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
copyStatusData();
if ((2 < linFrame.length) && (linFrame.length <= 10)) {
auto copyBytes = std::min(linSpyMsg.NumberBytesData, static_cast<uint8_t>(6));
std::memcpy(oldmsg.Data, frame.data, copyBytes);
oldmsg.Data[copyBytes] = linFrame.checksum;
} else if (2 == linFrame.length) {
std::memset(oldmsg.Data, 0, 8);
linSpyMsg.Header[linSpyMsg.NumberBytesHeader] = linFrame.checksum;
++linSpyMsg.NumberBytesHeader;
} else {
std::memset(oldmsg.Data, 0, 8);
}
if (linFrame.linStatus.txCommander)
linSpyMsg.StatusBitField |= SPY_STATUS_INIT_MESSAGE;
break;
}
default:
return false;
}
oldmsg.ExtraDataPtr = (void*)frame.data;
oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0;
memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8));
oldmsg.ArbIDOrHeader = *reinterpret_cast<const uint32_t*>(frame.header);
oldmsg.NetworkID = static_cast<uint8_t>(frame.netid); // Note: NetID remapping from the original API is not supported
oldmsg.NetworkID2 = static_cast<uint8_t>(frame.netid >> 8);
oldmsg.DescriptionID = frame.description;
oldmsg.StatusBitField = frame.status.statusBitfield[0];
oldmsg.StatusBitField2 = frame.status.statusBitfield[1];
oldmsg.StatusBitField3 = frame.status.statusBitfield[2];
oldmsg.StatusBitField4 = frame.status.statusBitfield[3];
// Timestamp - epoch = 1/1/2007 - 25ns per tick most of the time
uint64_t t = frame.timestamp;
uint16_t res = 0;
@@ -127,6 +149,90 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t
return true;
}
static bool SpyMessageToNeoMessage(const icsSpyMessage& oldmsg, neomessage_frame_t& frame, unsigned int& lNetworkID)
{
frame.netid = static_cast<uint16_t>(lNetworkID);
frame.description = oldmsg.DescriptionID;
frame.status.statusBitfield[0] = oldmsg.StatusBitField;
frame.status.statusBitfield[1] = oldmsg.StatusBitField2;
frame.status.statusBitfield[2] = oldmsg.StatusBitField3;
frame.status.statusBitfield[3] = oldmsg.StatusBitField4;
auto copyFrameDataPtr = [&]() {
memcpy(frame.header, &oldmsg.ArbIDOrHeader, sizeof(frame.header));
if ((oldmsg.ExtraDataPtr != nullptr) && (oldmsg.ExtraDataPtrEnabled == 1))
frame.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
else
frame.data = oldmsg.Data;
};
switch(oldmsg.Protocol)
{
case SPY_PROTOCOL_ETHERNET:
{
frame.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
copyFrameDataPtr();
break;
}
case SPY_PROTOCOL_LIN:
{
neomessage_lin_t& linFrame = *reinterpret_cast<neomessage_lin_t*>(&frame);
const uint8_t numberBytesHeader = std::min(oldmsg.NumberBytesHeader, static_cast<uint8_t>(3));
const uint8_t numberBytesData = std::min(oldmsg.NumberBytesData, static_cast<uint8_t>(7));
frame.length = numberBytesHeader + numberBytesData;
linFrame.type = ICSNEO_NETWORK_TYPE_LIN;
if (oldmsg.StatusBitField & SPY_STATUS_INIT_MESSAGE)
linFrame.linStatus.txCommander = true;
else
linFrame.linStatus.txResponder = true;
copyFrameDataPtr();
uint8_t protID = linFrame.header[0] & 0x3Fu;
auto bit = [&](uint8_t pos)->uint8_t { return ((protID >> pos) & 0x1u); };
protID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7);
protID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6);
linFrame.header[0] = protID;
if (frame.length > 2) {
size_t checksum = 0;
uint8_t* lastByte = nullptr;
for(size_t idx = 1; idx < (frame.length - 1); ++idx) {
if (idx < oldmsg.NumberBytesHeader) {
checksum += frame.header[idx];
lastByte = (frame.header + idx + 1);
} else {
checksum += frame.data[idx-numberBytesHeader];
lastByte = const_cast<uint8_t*>(frame.data) + idx - numberBytesHeader + 1;
}
if (checksum > 255) { checksum -= 255; }
}
size_t enhanced = frame.header[0] + checksum;
if (enhanced > 255) { enhanced -= 255; }
checksum ^= 0xff;
enhanced ^= 0xff;
if ((lastByte != nullptr) && (*lastByte != checksum) && (*lastByte == enhanced))
linFrame.linStatus.txChecksumEnhanced = true;
}
break;
}
case SPY_PROTOCOL_CANFD:
{
frame.length = oldmsg.NumberBytesData;
frame.status.canfdFDF = true;
copyFrameDataPtr();
break;
}
default:
{
frame.length = oldmsg.NumberBytesData;
copyFrameDataPtr();
break;
}
}
return true;
}
static inline bool Within(size_t value, size_t min, size_t max)
{
return ((min <= value) && (value < max));
@@ -200,107 +306,79 @@ static inline size_t GetVnetAgnosticNetid(size_t fullNetid)
int LegacyDLLExport icsneoFindDevices(NeoDeviceEx* devs, int* devCount, unsigned int* devTypes, unsigned int devTypeCount,
POptionsFindNeoEx* POptionsFindNeoEx, unsigned int* zero)
{
if (!devs || !devCount)
// Match the legacy API maximum, this derives from the maximum COM Port on old windows versions.
constexpr int MAX_NEO_DEVICES = 255;
// Validate arguments
if (!devCount && *devCount < 0) {
return 0;
if (*devCount < 0 || *devCount > 255)
return 0;
// Find the devices without filtering by the device type
// We allow this to find more than the requested number,
// as we may filter out some devices.
constexpr const size_t MAX_DEVICES = 255;
NeoDevice foundDevices[MAX_DEVICES];
int NumDevices = MAX_DEVICES;
int filteredDeviceCount = 0;
if (!icsneoFindNeoDevices(0, foundDevices, &NumDevices))
return 0;
for (auto i = 0; i < NumDevices; i++)
{
// Check if the next device would overrun the user's buffer
// We check this up here since the documentation allows zero
// to be specified.
if (filteredDeviceCount >= *devCount)
break;
if (devTypes && devTypeCount)
{
for (unsigned int j = 0; j < devTypeCount; j++)
{
if (foundDevices[i].DeviceType == devTypes[j])
{
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
break;
}
}
}
else
{
devs[filteredDeviceCount++].neoDevice = foundDevices[i];
}
}
*devCount = filteredDeviceCount;
return 1; // If the function succeeds but no devices are found 1 will still be returned and devCount will equal 0
}
int LegacyDLLExport icsneoFindNeoDevices(unsigned long DeviceTypes, NeoDevice* pNeoDevice, int* pNumDevices)
{
constexpr size_t MAX_DEVICES = 255;
size_t count = MAX_DEVICES;
if (pNumDevices == nullptr)
return 0;
if (pNeoDevice == nullptr)
// return the size only if devs is NULL.
if (!devs)
{
icsneo_findAllDevices(nullptr, &count);
*pNumDevices = (int)count;
icsneo_findAllDevices(nullptr, (size_t*)devCount);
return 1;
}
size_t bufferSize = (size_t)*pNumDevices;
if (*pNumDevices < 0 || bufferSize > MAX_DEVICES)
return 0;
neodevice_t devices[MAX_DEVICES];
icsneo_findAllDevices(devices, &count);
if (bufferSize < count)
count = bufferSize;
*pNumDevices = (int)count;
for (size_t i = 0; i < count; i++)
{
pNeoDevice[i] = OldNeoDeviceFromNew(&devices[i]); // Write out into user memory
neodevices[uint64_t(devices[i].handle) << 32 | icsneo_serialStringToNum(devices[i].serial)] = devices[i]; // Fill the look up table
// shrink the number of devices allowed to find
if (*devCount > MAX_NEO_DEVICES) {
*devCount = MAX_NEO_DEVICES;
}
return 1;
}
int LegacyDLLExport icsneoOpenNeoDevice(NeoDevice* pNeoDevice, void** hObject, unsigned char* bNetworkIDs, int bConfigRead, int bSyncToPC)
{
if (pNeoDevice == nullptr || hObject == nullptr)
return false;
neodevice_t *device;
try
{
device = &neodevices.at(uint64_t(pNeoDevice->Handle) << 32 | pNeoDevice->SerialNumber);
// Find all the neodevice_t devices
std::vector<neodevice_t> neoDevices(*devCount);
auto neoDevicesSize = neoDevices.size();
icsneo_findAllDevices(neoDevices.data(), &neoDevicesSize);
neoDevices.resize(neoDevicesSize);
// Filter out the devices if needed
// No filtering needed
if (devTypes && devTypeCount > 0) {
neoDevices.erase(
std::remove_if(
neoDevices.begin(),
neoDevices.end(),
[&](const auto& iter) {
for (unsigned int i=0; i < devTypeCount; ++i) {
if (iter.type == devTypes[i]) {
return false;
}
}
return true;
}
),
neoDevices.end()
);
}
catch (const std::out_of_range&)
{
return false;
}
*hObject = device;
if (!icsneo_openDevice(device))
return false;
// Create a NeoDeviceEx From a neodevice_t
auto _createNeoDeviceExFrom = [](const neodevice_t* neoDevice) -> NeoDeviceEx {
NeoDeviceEx nde = {};
nde.neoDevice.DeviceType = neoDevice->type;
nde.neoDevice.SerialNumber = icsneo_serialStringToNum(neoDevice->serial);
nde.neoDevice.NumberOfClients = 0;
nde.neoDevice.MaxAllowedClients = 1;
static_assert(sizeof(neodevice_handle_t) == sizeof(nde.neoDevice.Handle),
"neodevice_handle_t size must be sizeof(int) for compatibility reasons");
nde.neoDevice.Handle = neoDevice->handle;
return nde;
};
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
// Create the NeoDeviceEx from the neodevice_t
auto i = 0;
for (const auto& neoDevice : neoDevices) {
// Fill the look up table
neodevices[uint64_t(neoDevice.handle) << 32 | icsneo_serialStringToNum(neoDevice.serial)] = neoDevice;
// Create the NeoDeviceEx
devs[i] = _createNeoDeviceExFrom(&neoDevice);
NeoDeviceEx* nde = &devs[i];
++i;
// Lookup the open NeoDeviceEx devices and match the NumberOfClients value if available.
for (auto& [neo_device, open_nde]: openneodevices) {
// SerialNumber should always be unique so lets compare against that.
if (nde->neoDevice.SerialNumber == open_nde->neoDevice.SerialNumber) {
nde->neoDevice.NumberOfClients = open_nde->neoDevice.NumberOfClients;
}
}
}
*devCount = (int)neoDevices.size();
return 1;
}
int LegacyDLLExport icsneoOpenDevice(
@@ -325,19 +403,50 @@ int LegacyDLLExport icsneoOpenDevice(
return false;
}
*hObject = device;
if(!icsneo_openDevice(device))
if (pNeoDeviceEx->neoDevice.NumberOfClients >= pNeoDeviceEx->neoDevice.MaxAllowedClients) {
return false;
}
*hObject = device;
if(!icsneo_openDevice(device)) {
return false;
}
return icsneo_setPollingMessageLimit(device, 20000) && icsneo_enableMessagePolling(device) && icsneo_goOnline(device);
if (icsneo_isOnlineSupported(device)) {
if (!icsneo_setPollingMessageLimit(device, 20000)) {
icsneo_closeDevice(device);
return false;
}
if (!icsneo_enableMessagePolling(device)) {
icsneo_closeDevice(device);
return false;
}
if (!icsneo_goOnline(device)) {
icsneo_closeDevice(device);
return false;
}
}
pNeoDeviceEx->neoDevice.NumberOfClients = 1;
// Add the open NeoDevice to the container so we can decrement NumberOfClients on close
openneodevices[device] = pNeoDeviceEx;
return true;
}
int LegacyDLLExport icsneoClosePort(void* hObject, int* pNumberOfErrors)
{
if (!icsneoValidateHObject(hObject))
return false;
if (pNumberOfErrors) {
*pNumberOfErrors = 0;
}
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
if (openneodevices.find(device) != openneodevices.end()) {
openneodevices[device]->neoDevice.NumberOfClients -= 1;
openneodevices.erase(device);
}
return icsneo_closeDevice(device);
}
@@ -396,25 +505,9 @@ int LegacyDLLExport icsneoTxMessagesEx(void* hObject, icsSpyMessage* pMsg, unsig
*NumTxed = 0;
for (unsigned int i = 0; i < lNumMessages; i++)
{
const icsSpyMessage& oldmsg = pMsg[i];
newmsg = {};
newmsg.netid = (uint16_t)lNetworkID;
newmsg.description = oldmsg.DescriptionID;
memcpy(newmsg.header, &oldmsg.ArbIDOrHeader, sizeof(newmsg.header));
if (oldmsg.Protocol != SPY_PROTOCOL_ETHERNET)
newmsg.length = oldmsg.NumberBytesData;
else
newmsg.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255);
if (oldmsg.ExtraDataPtr != nullptr && oldmsg.ExtraDataPtrEnabled == 1)
newmsg.data = reinterpret_cast<const uint8_t *>(oldmsg.ExtraDataPtr);
else
newmsg.data = oldmsg.Data;
newmsg.status.statusBitfield[0] = oldmsg.StatusBitField;
newmsg.status.statusBitfield[1] = oldmsg.StatusBitField2;
newmsg.status.statusBitfield[2] = oldmsg.StatusBitField3;
newmsg.status.statusBitfield[3] = oldmsg.StatusBitField4;
if (oldmsg.Protocol == SPY_PROTOCOL_CANFD)
newmsg.status.canfdFDF = true;
const icsSpyMessage& oldMsg = pMsg[i];
SpyMessageToNeoMessage(oldMsg, newmsg, lNetworkID);
if (icsneo_transmit(device, reinterpret_cast<neomessage_t*>(&newmsg)))
(*NumTxed)++;
}
@@ -464,6 +557,54 @@ void LegacyDLLExport icsneoSetISO15765RxParameters(void* hObject, int lNetwork,
return;
}
int LegacyDLLExport icsneoGetRTC(void* hObject, icsSpyTime* time)
{
if(!icsneoValidateHObject(hObject))
return false;
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
uint64_t time64 = 0;
if(!icsneo_getRTC(device, &time64))
return false;
std::time_t seconds = time64;
/* To accommodate local time bugzilla #6600 https://intrepidcs.homeip.net:100/bugzilla/show_bug.cgi?id=6600 */
// local time must be used here
const auto timeInfo = std::localtime(&seconds);
if(!timeInfo)
return false;
time->sec = (unsigned char)timeInfo->tm_sec; // Will never hit 60 (leap second) because tm_sec comes from RTCCTIME
time->min = (unsigned char)timeInfo->tm_min;
time->hour = (unsigned char)timeInfo->tm_hour;
time->day = (unsigned char)timeInfo->tm_mday;
time->month = (unsigned char)timeInfo->tm_mon + 1;
time->year = (unsigned char)timeInfo->tm_year % 100;
return true;
}
int LegacyDLLExport icsneoSetRTC(void* hObject, const icsSpyTime* time)
{
if(!icsneoValidateHObject(hObject))
return false;
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
std::tm timeInfo{};
timeInfo.tm_sec = time->sec;
timeInfo.tm_min = time->min;
timeInfo.tm_hour = time->hour;
timeInfo.tm_mday = time->day;
timeInfo.tm_mon = time->month - 1;
timeInfo.tm_year = time->year + 100;
#ifdef _MSC_VER
#define timegm _mkgmtime
#endif
return icsneo_setRTC(device, (uint64_t)timegm(&timeInfo));
}
//Device Functions
int LegacyDLLExport icsneoGetConfiguration(void* hObject, unsigned char* pData, int* lNumBytes)
{
@@ -653,11 +794,43 @@ int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int*
return false;
}
int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, TCHAR* szErrorDescriptionShort, TCHAR* szErrorDescriptionLong,
int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, char* szErrorDescriptionShort, char* szErrorDescriptionLong,
int* lMaxLengthShort, int* lMaxLengthLong, int* lErrorSeverity, int* lRestartNeeded)
{
// TODO Implement
return false;
if (szErrorDescriptionShort == nullptr || szErrorDescriptionLong == nullptr
|| lMaxLengthShort == nullptr || lMaxLengthLong == nullptr || lErrorSeverity == nullptr
|| lRestartNeeded == nullptr)
{
return false;
}
//Set and send back 0. We will not restart the software.
*lRestartNeeded = 0;
//Using the error number, get the description from the event.
const char* tempDescription = APIEvent::DescriptionForType(APIEvent::Type(lErrorNumber));
int descrLength = int(std::strlen(tempDescription));
//Check to make sure the length of the error is not >= the buffer.
if (descrLength >= *lMaxLengthShort || descrLength >= *lMaxLengthLong)
{
return false;
}
//Copy the error description to the inout Short and Long arguments.
std::copy(tempDescription, tempDescription + descrLength, szErrorDescriptionShort);
std::copy(tempDescription, tempDescription + descrLength, szErrorDescriptionLong);
//Add the null terminator.
szErrorDescriptionShort[descrLength] = '\0';
szErrorDescriptionLong[descrLength] = '\0';
//Update the inout lengths to what the actual length of the error is
*lMaxLengthShort = *lMaxLengthLong = descrLength;
//Update the inout severity argument.
*lErrorSeverity = int(APIEvent::Severity::Any);
return true;
}
//ISO15765-2 Functions
@@ -736,6 +909,18 @@ int LegacyDLLExport icsneoStopSockServer(void* hObject)
return false;
}
int LegacyDLLExport icsneoGetDeviceStatus(void* hObject, icsDeviceStatus* deviceStatus, size_t* deviceStatusSize)
{
if (!icsneoValidateHObject(hObject))
return false;
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
if (deviceStatus == nullptr || deviceStatusSize == nullptr)
return false;
return icsneo_getDeviceStatus(device, deviceStatus, deviceStatusSize);
}
//CoreMini Script functions
int LegacyDLLExport icsneoScriptStart(void* hObject, int iLocation)
{
@@ -797,20 +982,7 @@ int LegacyDLLExport icsneoScriptWriteAppSignal(void* hObject, unsigned int iInde
return false;
}
//Deprecated (but still suppored in the DLL)
int LegacyDLLExport icsneoOpenPortEx(void* lPortNumber, int lPortType, int lDriverType, int lIPAddressMSB,
int lIPAddressLSBOrBaudRate, int bConfigRead, unsigned char* bNetworkID, int* hObject)
{
// TODO Implement
return false;
}
int LegacyDLLExport icsneoOpenPort(int lPortNumber, int lPortType, int lDriverType, unsigned char *bNetworkID,
unsigned char* bSCPIDs, int* hObject)
{
// TODO Implement
return false;
}
int LegacyDLLExport icsneoEnableNetworkCom(void* hObject, int Enable)
{
@@ -824,19 +996,6 @@ int LegacyDLLExport icsneoEnableNetworkCom(void* hObject, int Enable)
return icsneo_goOffline(device);
}
int LegacyDLLExport icsneoFindAllCOMDevices(int lDriverType, int lGetSerialNumbers, int lStopAtFirst, int lUSBCommOnly,
int* p_lDeviceTypes, int* p_lComPorts, int* p_lSerialNumbers, int* lNumDevices)
{
// TODO Implement
return false;
}
int LegacyDLLExport icsneoOpenNeoDeviceByChannels(NeoDevice* pNeoDevice, void** hObject, unsigned char* uChannels, int iSize,
int bConfigRead, int iOptions)
{
// TODO Implement
return false;
}
int LegacyDLLExport icsneoGetVCAN4Settings(void* hObject, SVCAN4Settings* pSettings, int iNumBytes)
{
@@ -915,6 +1074,18 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
case NEODEVICE_RADGIGALOG:
*pDeviceSettingsType = DeviceRADGigalogSettingsType;
break;
case NEODEVICE_RADMOON3:
*pDeviceSettingsType = DeviceRADMoon3SettingsType;
break;
case NEODEVICE_RED2:
*pDeviceSettingsType = DeviceRed2SettingsType;
break;
case NEODEVICE_FIRE3:
*pDeviceSettingsType = DeviceFire3SettingsType;
break;
case NEODEVICE_FIRE3_FLEXRAY:
*pDeviceSettingsType = DeviceFire3FlexraySettingsType;
break;
default:
return 0;
}
@@ -952,6 +1123,15 @@ int LegacyDLLExport icsneoGetDeviceSettings(void* hObject, SDeviceSettings* pSet
return !!icsneo_settingsReadStructure(device, &pSettings->Settings, iNumBytes - offset);
}
int LegacyDLLExport icsneoLoadDefaultSettings(void* hObject)
{
if (!icsneoValidateHObject(hObject))
return false;
neodevice_t* device = reinterpret_cast<neodevice_t*>(hObject);
return icsneo_settingsApplyDefaults(device);
}
int LegacyDLLExport icsneoSetBitRateEx(void* hObject, unsigned long BitRate, int NetworkID, int iOptions)
{
if (!icsneoValidateHObject(hObject))
@@ -1292,7 +1472,7 @@ int LegacyDLLExport icsneoSerialNumberFromString(unsigned long* serial, char* da
return false;
}
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NETWORK_ADAPTER_INFO* aInfo)
int LegacyDLLExport icsneoGetMiniportAdapterInfo(void* hObject, NDIS_ADAPTER_INFORMATION* aInfo)
{
return false;
}
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Release -DLIBICSNEO_BUILD_EXAMPLES=ON \
-DLIBICSNEO_BUILD_UNIT_TESTS=ON -DLIBICSNEO_BUILD_SYSTEM_TESTS=ON -DLIBICSNEO_ENABLE_TCP=OFF || exit 1
cmake --build build || exit 1
exit 0
+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 ..
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%
+22
View File
@@ -0,0 +1,22 @@
find_path(FTD3XX_INCLUDE_DIR
NAMES ftd3xx.h FTD3XX.h
)
find_library(FTD3XX_LIBRARY
NAMES libftd3xx.a libftd3xx-static.a FTD3XX.lib
PATH_SUFFIXES x64/Static
)
mark_as_advanced(FTD3XX_FOUND FTD3XX_INCLUDE_DIR FTD3XX_LIBRARY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FTD3XX
REQUIRED_VARS FTD3XX_INCLUDE_DIR FTD3XX_LIBRARY
)
if(FTD3XX_FOUND AND NOT TARGET D3XX::D3XX)
add_library(FTD3XX::FTD3XX INTERFACE IMPORTED)
set_target_properties(FTD3XX::FTD3XX PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${FTD3XX_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${FTD3XX_LIBRARY}"
)
endif()
+64 -51
View File
@@ -13,14 +13,13 @@
#include "icsneo/communication/message/filter/main51messagefilter.h"
#include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/versionmessage.h"
#include "icsneo/communication/message/componentversionsmessage.h"
using namespace icsneo;
int Communication::messageCallbackIDCounter = 1;
Communication::~Communication() {
if(redirectingRead)
clearRedirectRead();
if(isOpen())
close();
}
@@ -43,6 +42,11 @@ void Communication::spawnThreads() {
void Communication::joinThreads() {
closing = true;
if(pauseReadTask) {
resumeReads();
}
if(readTaskThread.joinable())
readTaskThread.join();
closing = false;
@@ -95,23 +99,6 @@ bool Communication::sendCommand(ExtendedCommand cmd, std::vector<uint8_t> argume
return sendCommand(Command::Extended, arguments);
}
bool Communication::redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo) {
if(redirectingRead)
return false;
redirectionFn = redirectTo;
redirectingRead = true;
return true;
}
void Communication::clearRedirectRead() {
if(!redirectingRead)
return;
// The mutex is required to clear the redirection, but not to set it
std::lock_guard<std::mutex> lk(redirectingReadMutex);
redirectingRead = false;
redirectionFn = std::function<void(std::vector<uint8_t>&&)>();
}
bool Communication::getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout) {
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::ReadSettings);
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
@@ -260,44 +247,70 @@ void Communication::dispatchMessage(const std::shared_ptr<Message>& msg) {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
}
void Communication::readTask() {
std::vector<uint8_t> readBytes;
void Communication::pauseReads() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
pauseReadTask = true;
}
void Communication::resumeReads() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
if(!pauseReadTask) {
return;
}
pauseReadTask = false;
lk.unlock();
pauseReadTaskCv.notify_one();
}
bool Communication::readsArePaused() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
return pauseReadTask;
}
void Communication::readTask() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing) {
readBytes.clear();
if(driver->readWait(readBytes)) {
handleInput(*packetizer, readBytes);
if(pauseReadTask) {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
pauseReadTaskCv.wait(lk, [this]() { return !pauseReadTask; });
}
}
}
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
if(redirectingRead) {
// redirectingRead is an atomic so it can be set without acquiring a mutex
// However, we do not clear it without the mutex. The idea is that if another
// thread calls clearRedirectRead(), it will block until the redirectionFn
// finishes, and after that the redirectionFn will not be called again.
std::unique_lock<std::mutex> lk(redirectingReadMutex);
// So after we acquire the mutex, we need to check the atomic again, and
// if it has become cleared, we *can not* run the redirectionFn.
if(redirectingRead) {
redirectionFn(std::move(readBytes));
} else {
// The redirectionFn got cleared while we were acquiring the lock
lk.unlock(); // We don't need the lock anymore
handleInput(p, readBytes); // and we might as well process this input ourselves
}
} else {
if(p.input(readBytes)) {
for(const auto& packet : p.output()) {
std::shared_ptr<Message> msg;
if(!decoder->decode(msg, packet))
continue;
dispatchMessage(msg);
if(driver->waitForRx(readTaskWakeLimit, readTaskWakeTimeout)) {
if(pauseReadTask) {
/**
* Reads could have paused while the driver was not available
*/
continue;
}
handleInput(*packetizer);
}
}
}
void Communication::handleInput(Packetizer& p) {
if(p.input(driver->getReadBuffer())) {
for(const auto& packet : p.output()) {
std::shared_ptr<Message> msg;
if(!decoder->decode(msg, packet))
continue;
dispatchMessage(msg);
}
}
}
std::optional< std::vector<ComponentVersion> > Communication::getComponentVersionsSync(std::chrono::milliseconds timeout) {
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::ComponentVersions);
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
return sendCommand(ExtendedCommand::GetComponentVersions, {});
}, filter, timeout);
if(!msg) // Did not receive a message
return std::nullopt;
auto ver = std::dynamic_pointer_cast<ComponentVersionsMessage>(msg);
if(!ver) // Could not upcast for some reason
return std::nullopt;
return std::make_optional< std::vector<ComponentVersion> >(std::move(ver->versions));
}
+136 -18
View File
@@ -5,12 +5,21 @@
#include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/message/flashmemorymessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/communication/message/wiviresponsemessage.h"
#include "icsneo/communication/message/scriptstatusmessage.h"
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
#include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/message/mdiomessage.h"
#include "icsneo/communication/message/extendeddatamessage.h"
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/message/diskdatamessage.h"
#include "icsneo/communication/message/hardwareinfo.h"
#include "icsneo/communication/message/tc10statusmessage.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/command.h"
#include "icsneo/device/device.h"
#include "icsneo/communication/packet/canpacket.h"
@@ -24,6 +33,14 @@
#include "icsneo/communication/packet/wivicommandpacket.h"
#include "icsneo/communication/packet/i2cpacket.h"
#include "icsneo/communication/packet/scriptstatuspacket.h"
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/packet/componentversionpacket.h"
#include "icsneo/communication/packet/supportedfeaturespacket.h"
#include "icsneo/communication/packet/mdiopacket.h"
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
#include "icsneo/communication/packet/livedatapacket.h"
#include "icsneo/communication/packet/hardwareinfopacket.h"
#include <iostream>
using namespace icsneo;
@@ -138,6 +155,43 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
return true;
}
case Network::Type::A2B: {
result = HardwareA2BPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
msg.network = packet->network;
msg.timestamp *= timestampResolution;
return true;
}
case Network::Type::LIN: {
result = HardwareLINPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
LINMessage& msg = *static_cast<LINMessage*>(result.get());
msg.network = packet->network;
return true;
}
case Network::Type::MDIO: {
result = HardwareMDIOPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
MDIOMessage& msg = *static_cast<MDIOMessage*>(result.get());
msg.network = packet->network;
return true;
}
case Network::Type::Internal: {
switch(packet->network.getNetID()) {
case Network::NetID::Reset_Status: {
@@ -204,6 +258,18 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
result = std::make_shared<RawMessage>(packet->network, packet->data);
return true;
}
case Network::NetID::RED_INT_MEMORYREAD: {
if(packet->data.size() != 512 + sizeof(uint16_t)) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // Should get enough data for a start address and sector
}
const auto msg = std::make_shared<FlashMemoryMessage>();
result = msg;
msg->startAddress = *reinterpret_cast<uint16_t*>(packet->data.data());
msg->data.insert(msg->data.end(), packet->data.begin() + 2, packet->data.end());
return true;
}
case Network::NetID::NeoMemorySDRead: {
if(packet->data.size() != 512 + sizeof(uint32_t)) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
@@ -217,16 +283,58 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
return true;
}
case Network::NetID::ExtendedCommand: {
if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse))
break; // Handle as a raw message, might not be a generic response
const auto& resp = *reinterpret_cast<ExtendedResponseMessage::PackedGenericResponse*>(packet->data.data());
if(resp.header.command != ExtendedCommand::GenericReturn)
break; // Handle as a raw message
switch(resp.header.command) {
case ExtendedCommand::GetComponentVersions:
result = ComponentVersionPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GetSupportedFeatures:
result = SupportedFeaturesPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GenericBinaryInfo:
result = GenericBinaryStatusPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GenericReturn:
result = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
return true;
case ExtendedCommand::LiveData:
result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report);
return true;
case ExtendedCommand::GetTC10Status:
result = TC10StatusMessage::DecodeToMessage(packet->data);
return true;
default:
// No defined handler, treat this as a RawMessage
break;
}
break;
}
case Network::NetID::ExtendedData: {
if(packet->data.size() < sizeof(ExtendedDataMessage::ExtendedDataHeader))
break;
const auto& header = *reinterpret_cast<ExtendedDataMessage::ExtendedDataHeader*>(packet->data.data());
const auto msg = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
result = msg;
return true;
switch(header.subCommand) {
case ExtendedDataSubCommand::GenericBinaryRead: {
result = std::make_shared<ExtendedDataMessage>(header);
auto extDataMsg = std::static_pointer_cast<ExtendedDataMessage>(result);
size_t numRead = std::min(ExtendedDataMessage::MaxExtendedDataBufferSize, (size_t)header.length);
extDataMsg->data.resize(numRead);
std::copy(packet->data.begin() + sizeof(header), packet->data.begin() + sizeof(header) + numRead, extDataMsg->data.begin());
extDataMsg->network = Network(static_cast<uint16_t>(Network::NetID::ExtendedData), false);
return true;
}
default:
break;
}
break;
}
case Network::NetID::FlexRayControl: {
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
@@ -271,6 +379,16 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
return true;
}
case Command::GetHardwareInfo: {
result = HardwareInfoPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false;
}
return true;
}
default:
auto msg = std::make_shared<Main51Message>();
msg->command = Command(packet->data[0]);
@@ -294,6 +412,14 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
packet->data.resize(length);
return decode(result, packet);
}
case Network::NetID::RED_App_Error: {
result = AppErrorMessage::DecodeToMessage(packet->data, report);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
return false;
}
return true;
}
case Network::NetID::ReadSettings: {
auto msg = std::make_shared<ReadSettingsMessage>();
msg->response = ReadSettingsMessage::Response(packet->data[0]);
@@ -345,26 +471,18 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
}
return true;
}
case Network::NetID::DiskData: {
result = std::make_shared<DiskDataMessage>(std::move(packet->data));
return true;
}
default:
break;
}
break;
}
case Network::Type::A2B: {
result = HardwareA2BPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
msg.network = packet->network;
return true;
}
}
// For the moment other types of messages will automatically be decoded as raw messages
result = std::make_shared<RawMessage>(packet->network, packet->data);
return true;
}
}
+32 -17
View File
@@ -8,23 +8,33 @@
using namespace icsneo;
bool Driver::read(std::vector<uint8_t>& bytes, size_t limit) {
// A limit of zero indicates no limit
if(limit == 0)
limit = (size_t)-1;
bool Driver::pushRx(const uint8_t* buf, size_t numReceived) {
bool ret = readBuffer.write(buf, numReceived);
if(limit > (readQueue.size_approx() + 4))
limit = (readQueue.size_approx() + 4);
rxWaitCv.notify_all();
if(bytes.capacity() < limit)
bytes.resize(limit);
return ret;
}
size_t actuallyRead = readQueue.try_dequeue_bulk(bytes.data(), limit);
void Driver::clearBuffers()
{
WriteOperation flushop;
if(bytes.size() > actuallyRead)
bytes.resize(actuallyRead);
readBuffer.clear();
rxWaitCv.notify_all();
return true;
while (writeQueue.try_dequeue(flushop)) {}
}
bool Driver::waitForRx(size_t limit, std::chrono::milliseconds timeout) {
return waitForRx([limit, this]() {
return readBuffer.size() >= limit;
}, timeout);
}
bool Driver::waitForRx(std::function<bool()> predicate, std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lk(rxWaitMutex);
return rxWaitCv.wait_for(lk, timeout, predicate);
}
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
@@ -32,13 +42,18 @@ bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds tim
if(limit == 0)
limit = (size_t)-1;
if(limit > (readQueue.size_approx() + 4))
limit = (readQueue.size_approx() + 4);
if(limit > (readBuffer.size() + 4))
limit = (readBuffer.size() + 4);
bytes.resize(limit);
size_t actuallyRead = readQueue.wait_dequeue_bulk_timed(bytes.data(), limit, timeout);
// wait until we have enough data, or the timout occurs
waitForRx(limit, timeout);
size_t actuallyRead = std::min(readBuffer.size(), limit);
bytes.resize(actuallyRead);
readBuffer.read(bytes.data(), 0, actuallyRead);
readBuffer.pop(actuallyRead);
bytes.resize(actuallyRead);
#ifdef ICSNEO_DRIVER_DEBUG_PRINTS
@@ -79,4 +94,4 @@ bool Driver::write(const std::vector<uint8_t>& bytes) {
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
return ret;
}
}
+41 -2
View File
@@ -1,6 +1,8 @@
#include "icsneo/communication/encoder.h"
#include "icsneo/communication/message/ethernetmessage.h"
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/message/main51message.h"
#include "icsneo/communication/packet/livedatapacket.h"
#include "icsneo/communication/packet/ethernetpacket.h"
#include "icsneo/communication/packet/iso9141packet.h"
#include "icsneo/communication/packet/canpacket.h"
@@ -9,7 +11,8 @@
#include "icsneo/communication/packet/i2cpacket.h"
#include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/packet/a2bpacket.h"
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/packet/mdiopacket.h"
using namespace icsneo;
@@ -96,6 +99,30 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
}
break;
} // End of Network::Type::I2C
case Network::Type::LIN: {
auto linmsg = std::dynamic_pointer_cast<LINMessage>(message);
if(!linmsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareLINPacket::EncodeFromMessage(*linmsg, result, report)) {
return false;
}
break;
} // End of Network::Type::LIN
case Network::Type::MDIO: {
auto mdiomsg = std::dynamic_pointer_cast<MDIOMessage>(message);
if(!mdiomsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareMDIOPacket::EncodeFromMessage(*mdiomsg, result, report)) {
return false;
}
break;
} // End of Network::Type::MDIO
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
@@ -172,6 +199,17 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
return false;
break;
}
case Message::Type::LiveData: {
auto liveDataMsg = std::dynamic_pointer_cast<LiveDataMessage>(message);
if(!liveDataMsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false; // The message was not a properly formed LiveDataMessage
}
if(!HardwareLiveDataPacket::EncodeFromMessage(*liveDataMsg, result, report))
return false;
result = packetizer.packetWrap(result, false);
return true;
}
break;
}
@@ -182,7 +220,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
// Size for the host-to-device long format is the size of the entire packet + 1
// So +1 for AA header, +1 for short format header, +2 for long format size, and +2 for long format NetID
// Then an extra +1, due to a firmware idiosyncrasy
uint16_t size = uint16_t(buffer->size()) + 1 + 1 + 2 + 2 + 1;
uint16_t size = static_cast<uint16_t>(buffer->size()) + 1 + 1 + 2 + 2 + 1;
buffer->insert(buffer->begin(), {
(uint8_t)Network::NetID::RED, // 0x0C for long message
(uint8_t)size, // Size, little endian 16-bit
+22
View File
@@ -0,0 +1,22 @@
#include "icsneo/communication/livedata.h"
namespace icsneo {
namespace LiveDataUtil {
LiveDataHandle getNewHandle() {
static LiveDataHandle currentHandle = 0;
++currentHandle;
if(currentHandle == std::numeric_limits<LiveDataHandle>::max()) {
EventManager::GetInstance().add(APIEvent::Type::LiveDataInvalidHandle, APIEvent::Severity::Error);
currentHandle = 1;
}
return currentHandle;
}
double liveDataValueToDouble(const LiveDataValue& val) {
constexpr double liveDataFixedPointToDouble = 0.00000000023283064365386962890625;
return val.value * liveDataFixedPointToDouble;
}
} // namespace LiveDataUtil
} // namespace icsneo
+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;
}
+148
View File
@@ -0,0 +1,148 @@
#include <icsneo/communication/message/apperrormessage.h>
namespace icsneo {
#pragma pack(push, 2)
typedef struct {
uint16_t error_type;
uint16_t network_id;
uint32_t uiTimeStamp10uS;
uint32_t uiTimeStamp10uSMSB;
} AppErrorData;
#pragma pack(pop)
std::shared_ptr<Message> AppErrorMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
const AppErrorData* data = reinterpret_cast<const AppErrorData*>(bytestream.data());
if(!data) {
report(APIEvent::Type::AppErrorParsingFailed, APIEvent::Severity::Error);
return nullptr;
}
auto appErr = std::make_shared<AppErrorMessage>();
appErr->errorType = data->error_type;
appErr->errorNetID = static_cast<Network::NetID>(data->network_id);
appErr->timestamp10us = data->uiTimeStamp10uS;
appErr->timestamp10usMSB = data->uiTimeStamp10uSMSB;
appErr->network = Network::NetID::RED_App_Error;
return appErr;
}
AppErrorType AppErrorMessage::getAppErrorType() {
AppErrorType errType = static_cast<AppErrorType>(errorType);
if(errType > AppErrorType::AppNoError) {
return AppErrorType::AppNoError;
}
return errType;
}
std::string AppErrorMessage::getAppErrorString() {
auto netIDString = Network::GetNetIDString(errorNetID);
AppErrorType errType = static_cast<AppErrorType>(errorType);
switch (errType) {
case AppErrorType::AppErrorRxMessagesFull:
return std::string(netIDString) + ": RX message buffer full";
case AppErrorType::AppErrorTxMessagesFull:
return std::string(netIDString) + ": TX message buffer full";
case AppErrorType::AppErrorTxReportMessagesFull:
return std::string(netIDString) + ": TX report buffer full";
case AppErrorType::AppErrorBadCommWithDspIC:
return "Received bad packet from DSP IC";
case AppErrorType::AppErrorDriverOverflow:
return std::string(netIDString) + ": Driver overflow";
case AppErrorType::AppErrorPCBuffOverflow:
return "PC buffer overflow";
case AppErrorType::AppErrorPCChksumError:
return "PC checksum error";
case AppErrorType::AppErrorPCMissedByte:
return "PC missed byte";
case AppErrorType::AppErrorPCOverrunError:
return "PC overrun error";
case AppErrorType::AppErrorSettingFailure:
return std::string(netIDString) + ": Settings incorrectly set";
case AppErrorType::AppErrorTooManySelectedNetworks:
return "Too many selected networks";
case AppErrorType::AppErrorNetworkNotEnabled:
return std::string(netIDString) + ": Network not enabled";
case AppErrorType::AppErrorRtcNotCorrect:
return "RTC not correct";
case AppErrorType::AppErrorLoadedDefaultSettings:
return "Loaded default settings";
case AppErrorType::AppErrorFeatureNotUnlocked:
return "Feature not unlocked";
case AppErrorType::AppErrorFeatureRtcCmdDropped:
return "RTC command dropped";
case AppErrorType::AppErrorTxMessagesFlushed:
return "TX message buffer flushed";
case AppErrorType::AppErrorTxMessagesHalfFull:
return "TX message buffer half full";
case AppErrorType::AppErrorNetworkNotValid:
return "Network is not valid";
case AppErrorType::AppErrorTxInterfaceNotImplemented:
return "TX interface is not implemented";
case AppErrorType::AppErrorTxMessagesCommEnableIsOff:
return "TX message communication is disabled";
case AppErrorType::AppErrorRxFilterMatchCountExceeded:
return "RX filter match count exceeded";
case AppErrorType::AppErrorEthPreemptionNotEnabled:
return std::string(netIDString) + ": Ethernet preemption not enabled";
case AppErrorType::AppErrorTxNotSupportedInMode:
return std::string(netIDString) + ": Transmit is not supported in this mode";
case AppErrorType::AppErrorJumboFramesNotSupported:
return std::string(netIDString) + ": Jumbo frames not supported";
case AppErrorType::AppErrorEthernetIpFragment:
return "Ethernet IP fragment received";
case AppErrorType::AppErrorTxMessagesUnderrun:
return std::string(netIDString) + ": Transmit buffer underrun";
case AppErrorType::AppErrorDeviceFanFailure:
return "Device fan failure";
case AppErrorType::AppErrorDeviceOvertemperature:
return "Device overtemperature";
case AppErrorType::AppErrorTxMessageIndexOutOfRange:
return "Transmit message index out of range";
case AppErrorType::AppErrorUndersizedFrameDropped:
return std::string(netIDString) + ": Undersized frame dropped";
case AppErrorType::AppErrorOversizedFrameDropped:
return std::string(netIDString) + ": Oversized frame dropped";
case AppErrorType::AppErrorWatchdogEvent:
return "Watchdog event occured";
case AppErrorType::AppErrorSystemClockFailure:
return "Device clock failed";
case AppErrorType::AppErrorSystemClockRecovered:
return "Device clock recovered";
case AppErrorType::AppErrorSystemPeripheralReset:
return "Device peripheral reset";
case AppErrorType::AppErrorSystemCommunicationFailure:
return "Device communication failure";
case AppErrorType::AppErrorTxMessagesUnsupportedSourceOrPacketId:
return std::string(netIDString) + ": Transmit unsupported source or packet ID";
case AppErrorType::AppErrorWbmsManagerConnectFailed:
return std::string(netIDString) + ": Failed to connect to managers with settings";
case AppErrorType::AppErrorWbmsManagerConnectBadState:
return std::string(netIDString) + ": Connected to managers in a invalid state";
case AppErrorType::AppErrorWbmsManagerConnectTimeout:
return std::string(netIDString) + ": Timeout while attempting to connect to managers";
case AppErrorType::AppErrorFailedToInitializeLoggerDisk:
return "Device failed to initialize storage disk";
case AppErrorType::AppErrorInvalidSetting:
return std::string(netIDString) + ": Invalid settings";
case AppErrorType::AppErrorSystemFailureRequestedReset:
return "Device rebooted to recover from an unexpected error condition";
case AppErrorType::AppErrorPortKeyMistmatch:
return std::string(netIDString) + ": Mismatch between key in manager and stored key";
case AppErrorType::AppErrorBusFailure:
return std::string(netIDString) + ": Bus failure";
case AppErrorType::AppErrorTapOverflow:
return std::string(netIDString) + ": Tap overflow";
case AppErrorType::AppErrorEthTxNoLink:
return std::string(netIDString) + ": Attempted Ethernet transmit without link";
case AppErrorType::AppErrorErrorBufferOverflow:
return "Device error buffer overflow";
case AppErrorType::AppNoError:
return "No error";
default:
return "Unknown error";
}
}
} // namespace icsneo
@@ -1,100 +1,234 @@
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
#include "icsneo/device/tree/rada2b/rada2b.h"
#include "icsneo/icsneocpp.h"
namespace icsneo
{
namespace icsneo {
void A2BWAVOutput::writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const {
A2BWAVOutput::A2BWAVOutput(
const char* filename,
const ChannelMap& channelMap,
PCMType bitDepth,
size_t numWAVChannels,
uint32_t sampleRate
)
: StreamOutput(filename), wavSampleRate(sampleRate), numChannelsWAV(numWAVChannels), chMap(channelMap) {
switch(bitDepth) {
case PCMType::L16:
bytesPerSampleWAV = 2;
break;
case PCMType::L24:
bytesPerSampleWAV = 3;
break;
case PCMType::L32:
bytesPerSampleWAV = 4;
break;
}
if(initialize()) {
initialized = true;
}
}
A2BWAVOutput::A2BWAVOutput(
std::ostream& os,
const ChannelMap& channelMap,
PCMType bitDepth,
size_t numWAVChannels,
uint32_t sampleRate
)
: StreamOutput(os), wavSampleRate(sampleRate), numChannelsWAV(numWAVChannels), chMap(channelMap) {
switch(bitDepth) {
case PCMType::L16:
bytesPerSampleWAV = 2;
break;
case PCMType::L24:
bytesPerSampleWAV = 3;
break;
case PCMType::L32:
bytesPerSampleWAV = 4;
break;
}
if(initialize()) {
initialized = true;
}
}
A2BWAVOutput::~A2BWAVOutput() {
if(!closed) {
close();
}
}
bool A2BWAVOutput::initialize() {
static constexpr size_t maxWAVChannels = 256;
if(numChannelsWAV > maxWAVChannels) {
return false;
}
maxMessageChannel = 0;
// Check if the inputted channel map has invalid mappings and compute maxMessageChannel
for(auto [wavChannel, messageChannel] : chMap) {
maxMessageChannel = std::max<size_t>(maxMessageChannel, messageChannel);
if(wavChannel >= numChannelsWAV) {
return false;
}
}
WAVHeader header = WAVHeader(
static_cast<uint16_t>(chMap.size()),
wavSampleRate,
static_cast<uint16_t>(bytesPerSampleWAV * 8)
);
if(!stream->write(reinterpret_cast<const char*>(&header), sizeof(WAVHeader))) {
return false;
}
WaveFileHeader header = WaveFileHeader(2 * firstMsg->getNumChannels(), wavSampleRate, firstMsg->getBitDepth());
header.write(stream);
streamStartPos = static_cast<uint32_t>(stream->tellp());
wavBuffer = std::vector<uint8_t>(wavBufferSize, 0);
wavBufferOffset = 0;
return true;
}
bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
if(closed)
{
if(!initialized) {
return false;
}
if(message->type != Message::Type::Frame)
return false;
const auto& frame = std::static_pointer_cast<Frame>(message);
if(frame->network.getType() != Network::Type::A2B)
return false;
const auto& a2bmsg = std::static_pointer_cast<A2BMessage>(frame);
if(firstMessageFlag) {
writeHeader(a2bmsg);
firstMessageFlag = false;
}
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::DownStream)) {
close();
if(closed) {
return false;
}
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::UpStream)) {
close();
if(message->type != Message::Type::Frame) {
return false;
}
const auto& frameMsg = std::dynamic_pointer_cast<Frame>(message);
if(!frameMsg) {
return false;
}
if(frameMsg->network.getType() != Network::Type::A2B)
return false;
const auto& a2bMsg = std::dynamic_pointer_cast<A2BMessage>(frameMsg);
if(!a2bMsg) {
return false;
}
size_t frameSize = a2bMsg->getFrameSize();
size_t wavFrameSize = numChannelsWAV * bytesPerSampleWAV;
size_t bytesPerChannel = static_cast<size_t>(a2bMsg->getBytesPerChannel());
size_t numMessageChannels = 2 * a2bMsg->numChannels;
size_t numFrames = a2bMsg->getNumFrames();
const uint8_t* audioBuffer = a2bMsg->data.data();
if(maxMessageChannel >= numMessageChannels) {
// The max message channel in our channel map is larger than the number of channels in this message
// this is likely due to the user inputting incorrect settings
return false;
}
for(size_t frame = 0; frame < numFrames; frame++) {
// Check to see if we can read another frame in wavBuffer, otherwise write and clear the buffer
if(wavBufferOffset + wavFrameSize >= wavBufferSize) {
if(!writeCurrentBuffer()) {
return false;
}
}
for(size_t wavChannel = 0; wavChannel < numChannelsWAV; wavChannel++) {
if(auto iter = chMap.find(static_cast<uint8_t>(wavChannel)); iter != chMap.end()) {
auto messageChannel = iter->second;
size_t messageChannelOffset = messageChannel * bytesPerChannel + frameSize* frame;
// Samples in the WAV are little endian signed integers
// Samples in the message channels are little endian signed integers that are
// most significant bit aligned
if(a2bMsg->channelSize16) {
// In this case, the channel size will be less than or equal to the sample we are writing
// so we zero out any of the least significant bytes which won't be occupied by a sample byte
for(size_t zeroByte = 0; zeroByte < bytesPerSampleWAV - bytesPerChannel; zeroByte++) {
wavBuffer[wavBufferOffset++] = 0;
}
// Write the channel data in the most signifant bytes of the wav sample, this effectively
// writes a sample which is scaled up.
for(size_t channelByte = 0; channelByte < bytesPerChannel; channelByte++) {
wavBuffer[wavBufferOffset++] = audioBuffer[messageChannelOffset + channelByte];
}
} else {
// In this case, the channel size will be greater than or equal to the sample we are reading
// Align the wav sample with the most significant bytes of the channel
size_t channelByte = messageChannelOffset + (bytesPerChannel - bytesPerSampleWAV);
// Read the most significant bytes of the channel into the wavBuffer
for(size_t sampleByte = 0; sampleByte < bytesPerSampleWAV; sampleByte++, channelByte++) {
wavBuffer[wavBufferOffset++] = audioBuffer[channelByte];
}
}
} else {
// If this channel wasn't specified in the channel map, set a zero sample
for(
size_t sampleByte = 0;
sampleByte < bytesPerSampleWAV;
sampleByte++
) {
wavBuffer[wavBufferOffset++] = 0;
}
}
}
}
return true;
}
void A2BWAVOutput::close() const
{
void A2BWAVOutput::close() const {
if(closed) {
return;
}
if(!initialized) {
return;
}
// Write any left over data in the buffer
if(wavBufferOffset > 0) {
writeCurrentBuffer();
}
// Seek back in the output stream and write the WAV chunk sizes
uint32_t streamEndPos = static_cast<uint32_t>(stream->tellp());
uint32_t subChunk2Size = streamEndPos - streamStartPos;
uint32_t chunkSize = streamEndPos - 8;
stream->seekp(streamStartPos - 4);
write((void*)&subChunk2Size, 4);
stream->write(reinterpret_cast<const char*>(&subChunk2Size), 4);
stream->seekp(4, std::ios::beg);
write((void*)&chunkSize, 4);
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
closed = true;
}
bool A2BWAVOutput::writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessage::A2BDirection dir) const
{
uint8_t numChannels = msg->getNumChannels();
bool A2BWAVOutput::writeCurrentBuffer() const {
uint8_t channel = 0;
uint32_t frame = 0;
uint8_t bitDepth = msg->getBitDepth();
while(true) {
auto sample = msg->getSample(dir, channel, frame);
if(!sample) {
if(channel == 0) {
break;
}
return false;
}
uint32_t audioSample = sample.value() >> (32 - bitDepth);
write((void*)(&audioSample), A2BPCM_SAMPLE_SIZE);
channel = (channel + 1) % numChannels;
if(channel == 0) {
frame++;
}
if(!stream->write(reinterpret_cast<const char*>(wavBuffer.data()), wavBufferOffset)) {
return false;
}
wavBufferOffset = 0;
return true;
}
}
+30
View File
@@ -0,0 +1,30 @@
#include "icsneo/communication/message/linmessage.h"
#include <numeric>
namespace icsneo {
void LINMessage::calcChecksum(LINMessage& message) {
uint16_t sum = 0;
auto limitFunc = [](uint16_t x, uint16_t y) -> uint16_t {
if ((x + y) > 0xFFu)
return ((x + y) - 0xFFu);
else
return (x + y);
};
message.checksum = static_cast<uint8_t>(std::accumulate(message.data.begin(), message.data.end(), sum, limitFunc));
if(message.isEnhancedChecksum)
message.checksum = static_cast<uint8_t>(limitFunc(message.checksum, message.protectedID));
message.checksum ^= 0xFFu;
}
uint8_t LINMessage::calcProtectedID(uint8_t& id) {
uint8_t protID = id;
auto bit = [&](uint8_t pos)->uint8_t { return ((protID >> pos) & 0x1u); };
protID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7);
protID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6);
return protID;
}
} //namespace icsneo
+15
View File
@@ -0,0 +1,15 @@
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/livedata.h"
namespace icsneo
{
void LiveDataCommandMessage::appendSignalArg(LiveDataValueType valueType) {
auto& arg = args.emplace_back(std::make_shared<LiveDataArgument>());
arg->objectType = LiveDataObjectType::MISC;
arg->objectIndex = 0u;
arg->signalIndex = 0u;
arg->valueType = valueType;
}
} // namespace icsneo
+90
View File
@@ -2,6 +2,7 @@
#include "icsneo/communication/message/canmessage.h"
#include "icsneo/communication/message/ethernetmessage.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/linmessage.h"
using namespace icsneo;
@@ -17,6 +18,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg;
auto framemsg = std::static_pointer_cast<Frame>(message);
const auto netType = framemsg->network.getType();
frame.netid = (neonetid_t)framemsg->network.getNetID();
frame.type = (neonettype_t)netType;
frame.description = framemsg->description;
@@ -53,6 +55,47 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr<Message> message) {
//eth.status.xyz = ethmsg->noPadding;
break;
}
case Network::Type::LIN: {
neomessage_lin_t& lin = *(neomessage_lin_t*)&neomsg;
auto linmsg = std::static_pointer_cast<LINMessage>(message);
if(!linmsg) { break; }
const auto linHdrBytes = std::min(linmsg->data.size(), static_cast<size_t>(2));
lin.header[0] = linmsg->protectedID;
std::copy(linmsg->data.begin(), linmsg->data.begin() + linHdrBytes, lin.header + 1);
linmsg->calcChecksum(*linmsg);
lin.checksum = linmsg->checksum;
lin.data = linmsg->data.data() + linHdrBytes;
if(linmsg->isEnhancedChecksum != linmsg->statusFlags.TxChecksumEnhanced) {
linmsg->isEnhancedChecksum = true;
linmsg->statusFlags.TxChecksumEnhanced = true;
}
if(linmsg->data.size())
lin.length = linmsg->data.size() + 2;
else
lin.length = 1;
lin.linStatus = {
linmsg->statusFlags.TxChecksumEnhanced,
linmsg->statusFlags.TxCommander,
linmsg->statusFlags.TxResponder,
linmsg->statusFlags.UpdateResponderOnce,
linmsg->statusFlags.HasUpdatedResponderOnce,
linmsg->statusFlags.BusRecovered,
linmsg->statusFlags.BreakOnly
};
lin.status.linJustBreakSync = linmsg->errFlags.ErrRxBreakSyncOnly;
lin.status.linErrorTXRXMismatch = linmsg->errFlags.ErrTxRxMismatch;
lin.status.linErrorRXBreakNotZero = linmsg->errFlags.ErrRxBreakNotZero;
lin.status.linErrorRXBreakTooShort = linmsg->errFlags.ErrRxBreakTooShort;
lin.status.linErrorRXSyncNot55 = linmsg->errFlags.ErrRxSyncNot55;
lin.status.linErrorRXDataGreaterEight = linmsg->errFlags.ErrRxDataLenOver8;
lin.status.linSyncFrameError = linmsg->errFlags.ErrFrameSync;
lin.status.linIDFrameError = linmsg->errFlags.ErrFrameMessageID;
lin.status.linSlaveByteError = linmsg->errFlags.ErrFrameResponderData;
lin.status.checksumError = linmsg->errFlags.ErrChecksumMatch;
break;
}
default:
// TODO Implement others
break;
@@ -105,6 +148,53 @@ std::shared_ptr<Message> icsneo::CreateMessageFromNeoMessage(const neomessage_t*
ethmsg->data.insert(ethmsg->data.end(), eth.data, eth.data + eth.length);
return ethmsg;
}
case Network::Type::LIN: {
neomessage_lin_t& lin = *(neomessage_lin_t*)neomessage;
auto linmsg = std::make_shared<LINMessage>();
linmsg->network = network;
linmsg->description = lin.description;
linmsg->protectedID = lin.header[0];
linmsg->ID = linmsg->protectedID & 0x3F;
linmsg->statusFlags = {
static_cast<bool>(lin.linStatus.txChecksumEnhanced),
static_cast<bool>(lin.linStatus.txCommander),
static_cast<bool>(lin.linStatus.txResponder),
static_cast<bool>(lin.linStatus.updateResponderOnce),
static_cast<bool>(lin.linStatus.hasUpdatedResponderOnce),
static_cast<bool>(lin.linStatus.busRecovered),
static_cast<bool>(lin.linStatus.breakOnly)
};
linmsg->isEnhancedChecksum = linmsg->statusFlags.TxChecksumEnhanced;
linmsg->errFlags = {
static_cast<bool>(lin.linStatus.breakOnly),
static_cast<bool>(lin.status.linJustBreakSync),
static_cast<bool>(lin.status.linErrorTXRXMismatch),
static_cast<bool>(lin.status.linErrorRXBreakNotZero),
static_cast<bool>(lin.status.linErrorRXBreakTooShort),
static_cast<bool>(lin.status.linErrorRXSyncNot55),
static_cast<bool>(lin.status.linErrorRXDataGreaterEight),
static_cast<bool>(lin.status.linSyncFrameError),
static_cast<bool>(lin.status.linIDFrameError),
static_cast<bool>(lin.status.linSlaveByteError),
static_cast<bool>(lin.status.checksumError)
};
if(lin.length > 1) {
auto numHeaderBytes = std::min(lin.length, static_cast<size_t>(3));
linmsg->data.insert(linmsg->data.end(), (lin.header + 1), (lin.header + numHeaderBytes));
linmsg->data.insert(linmsg->data.end(), lin.data, (lin.data + (lin.length - numHeaderBytes)));
linmsg->checksum = linmsg->data.back();
linmsg->data.pop_back();
}
if (linmsg->statusFlags.TxCommander) {
if (linmsg->data.size())
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
else
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
} else if (linmsg->statusFlags.TxResponder) {
linmsg->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
}
return linmsg;
}
default: break;
}
break;
@@ -0,0 +1,31 @@
#include "icsneo/communication/message/tc10statusmessage.h"
#include "icsneo/communication/command.h"
using namespace icsneo;
#pragma pack(push, 2)
struct Header {
ExtendedCommand command;
uint16_t length;
};
struct Packet {
Header header;
TC10WakeStatus wakeStatus;
TC10SleepStatus sleepStatus;
};
#pragma pack(pop)
std::shared_ptr<TC10StatusMessage> TC10StatusMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(Packet)) {
return nullptr;
}
const Packet* packet = (Packet*)bytestream.data();
if (packet->header.command != ExtendedCommand::GetTC10Status) {
return nullptr;
}
if (packet->header.length < sizeof(Packet) - sizeof(Header)) {
return nullptr;
}
return std::make_shared<TC10StatusMessage>(packet->wakeStatus, packet->sleepStatus);
}
+5 -2
View File
@@ -177,8 +177,11 @@ void MultiChannelCommunication::vnetReadTask(size_t vnetIndex) {
if(queue.wait_dequeue_timed(payloadBytes, std::chrono::milliseconds(250))) {
if(closing)
break;
handleInput(*vnetPacketizer, payloadBytes);
auto& ringBuffer = driver->getReadBuffer();
ringBuffer.write(payloadBytes);
handleInput(*vnetPacketizer);
}
}
}
+25 -128
View File
@@ -1,161 +1,58 @@
#include "icsneo/communication/packet/a2bpacket.h"
#include <cstring>
#include <vector>
namespace icsneo {
const size_t HardwareA2BPacket::coreMiniMessageHeaderSize = 28;
const size_t HardwareA2BPacket::a2bMessageMaxLength = (size_t)HardwareA2BPacket::coreMiniMessageHeaderSize + 1024;
const size_t HardwareA2BPacket::a2bHeaderSize = 6;
const size_t HardwareA2BPacket::a2bMessageMaxLength = sizeof(HardwareA2BPacket) + 1024;
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < coreMiniMessageHeaderSize)
if(bytestream.size() < sizeof(HardwareA2BPacket))
{
return nullptr;
}
auto getSampleFromBytes = [](uint8_t bytesPerSample, const uint8_t *bytes) {
A2BPCMSample result = 0;
for(auto i = 0; i < bytesPerSample; i++) {
result |= static_cast<uint32_t>(bytes[i]) << (i * 8);
}
const HardwareA2BPacket* data = (const HardwareA2BPacket*)bytestream.data();
return result;
};
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - sizeof(HardwareA2BPacket); // First 28 bytes are message header.
if(totalPackedLength == 0) {
return nullptr;
}
const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data();
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>();
uint32_t totalPackedLength = static_cast<uint32_t>(bytestream.size()) - static_cast<uint32_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
uint8_t bytesPerChannel = data->header.channelSize16 ? 2 : 4;
uint8_t numChannels = data->header.channelNum;
uint8_t bitDepth = data->header.channelSize16 ? A2BPCM_L16 : A2BPCM_L24;
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(bitDepth, bytesPerChannel, numChannels);
msg->numChannels = data->header.channelNum;
msg->channelSize16 = data->header.channelSize16;
msg->monitor = data->header.monitor;
msg->txmsg = data->header.txmsg;
msg->errIndicator = data->header.errIndicator;
msg->syncFrame = data->header.syncFrame;
msg->rfu2 = data->header.rfu2;
msg->timestamp = data->timestamp.TS;
const uint8_t *bytes = bytestream.data();
bytes+=coreMiniMessageHeaderSize;
uint8_t channel = 0;
for(uint32_t i = 0; i < totalPackedLength; i += 2 * static_cast<uint32_t>(bytesPerChannel), bytes += 2 * bytesPerChannel, channel = (channel + 1) % numChannels) {
msg->addSample(
getSampleFromBytes(bytesPerChannel, bytes),
A2BMessage::A2BDirection::DownStream,
channel
);
msg->addSample(
getSampleFromBytes(bytesPerChannel, bytes + bytesPerChannel),
A2BMessage::A2BDirection::UpStream,
channel
);
}
msg->data = std::vector(bytestream.begin() + sizeof(HardwareA2BPacket), bytestream.end());
return msg;
}
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& /*report*/) {
constexpr size_t a2btxMessageHeaderSize = 6;
if(message.getBytesPerSample() != 2 && message.getBytesPerSample() != 4) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
size_t audioBufferSize = message.data.size();
size_t totalSize = a2btxMessageHeaderSize + audioBufferSize;
size_t sampleBytes = message.getNumSamples() * static_cast<size_t>(message.getBytesPerSample());
size_t totalSize = coreMiniMessageHeaderSize + sampleBytes;
if(totalSize > a2bMessageMaxLength) {
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
return false;
}
bytestream.reserve(totalSize);
bytestream.push_back(message.getNumChannels());
bytestream.push_back(message.channelSize16 ? 1 : 0);
bytestream.resize(totalSize, 0);
uint32_t offset = 0;
uint8_t a2b2Bits = 0;
if(message.monitor) {
a2b2Bits = a2b2Bits | 1;
}
bytestream[offset++] = 0;
bytestream[offset++] = 0;
bytestream[offset++] = (uint8_t)(audioBufferSize & 0xFF);
bytestream[offset++] = (uint8_t)((audioBufferSize >> 8) & 0xFF);
bytestream[offset++] = (uint8_t)((message.description >> 8) & 0xFF);
bytestream[offset++] = (uint8_t)(message.description & 0xFF);
if(message.txmsg) {
a2b2Bits = a2b2Bits | (1 << 1);
}
if(message.errIndicator) {
a2b2Bits = a2b2Bits | (1 << 2);
}
if(message.syncFrame) {
a2b2Bits = a2b2Bits | (1 << 3);
}
bytestream.push_back(a2b2Bits);
bytestream.push_back(0);
bytestream.push_back(static_cast<uint8_t>(message.rfu2));
bytestream.push_back(static_cast<uint8_t>(message.rfu2 >> 8));
for(size_t i = 0; i < (coreMiniMessageHeaderSize - a2bHeaderSize); i++)
bytestream.push_back(0);
uint8_t numChannels = message.getNumChannels();
uint8_t channel = 0;
uint32_t frame = 0;
auto writeSample = [&](A2BPCMSample&& sample) {
for(uint32_t i = 0; i < static_cast<uint32_t>(message.getBytesPerSample()); i++) {
bytestream.push_back(static_cast<uint8_t>((sample >> (i*8))));
}
};
while(true) {
auto dsSample = message.getSample(A2BMessage::A2BDirection::DownStream, channel, frame);
auto usSample = message.getSample(A2BMessage::A2BDirection::UpStream, channel, frame);
// Check if getSample failed for both downstream and upstream
if(!dsSample && !usSample) {
if(channel != 0) {
//Incomplete frame, the frame we are currently on does not contain all channel samples
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
return false;
}
// Since no samples have been written for the current frame yet and there are no more
// samples in both upstream and downstream, we can break and end parsing.
break;
}
// Since the first case failed, at least one of the streams still has samples.
// This case checks to see if the other stream does not have a sample.
else if(!dsSample || !usSample) {
// Report an error since we must have a one to one correspondence between upstream
// and downstream.
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
return false;
}
writeSample(std::move(dsSample.value()));
writeSample(std::move(usSample.value()));
channel = (channel + 1) % numChannels;
if(channel == 0)
frame++;
}
std::copy(message.data.begin(), message.data.end(), bytestream.begin() + offset);
return true;
}
@@ -0,0 +1,45 @@
#include "icsneo/communication/packet/componentversionpacket.h"
#include "icsneo/communication/message/componentversionsmessage.h"
using namespace icsneo;
#pragma pack(push, 2)
struct PackedComponentVersion {
uint8_t valid;
uint8_t expansionSlot;
uint8_t componentInfo; // Component specific data (e.g. Linux: boot device)
uint8_t reserved;
uint32_t identifier;
uint32_t dotVersion; // Represents a.b.c.d, a.b.c, or a.b, depending on leading zeros.
uint32_t commitHash;
};
static constexpr size_t MaxReportedVersions = 16;
struct ComponentVersionsResponse {
ExtendedResponseMessage::ResponseHeader header;
uint16_t numVersions;
PackedComponentVersion versions[MaxReportedVersions];
};
#pragma pack(pop)
std::shared_ptr<ComponentVersionsMessage> ComponentVersionPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
auto msg = std::make_shared<ComponentVersionsMessage>();
// Length checks: At least a header and numVersions field.
if(bytes.size() < sizeof(ExtendedResponseMessage::ResponseHeader) + 2) {
return msg; // Empty
}
// Get a reference to the payload to fully validate the length
const auto& response = *reinterpret_cast<const ComponentVersionsResponse*>(bytes.data());
// Expected size is the header, numVersions field, and numVersions ComponentVersion objects.
auto expectedSize = sizeof(ExtendedResponseMessage::ResponseHeader) + 2 + (response.numVersions * sizeof(ComponentVersion));
// If the response is malformed (too small), return an empty message.
if(bytes.size() < expectedSize) {
return msg; // Empty
}
// Unpack into the portable class
for(unsigned int i = 0; i < response.numVersions; ++i) {
const auto& packedVersion = response.versions[i];
msg->versions.emplace_back(packedVersion.valid, packedVersion.componentInfo, packedVersion.identifier, packedVersion.dotVersion, packedVersion.commitHash);
}
return msg;
}
@@ -0,0 +1,38 @@
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
#include "icsneo/communication/message/genericbinarystatusmessage.h"
using namespace icsneo;
#pragma pack(push, 2)
struct GenericBinaryStatusResponse {
ExtendedResponseMessage::ResponseHeader header;
size_t size;
uint16_t index;
uint16_t status;
};
#pragma pack(pop)
std::shared_ptr<GenericBinaryStatusMessage> GenericBinaryStatusPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
if(bytes.size() < sizeof(GenericBinaryStatusResponse)) {
return nullptr;
}
auto msg = std::make_shared<GenericBinaryStatusMessage>();
const auto& response = *reinterpret_cast<const GenericBinaryStatusResponse*>(bytes.data());
msg->binarySize = response.size;
msg->binaryIndex = response.index;
msg->binaryStatus = response.status;
return msg;
}
std::vector<uint8_t> GenericBinaryStatusPacket::EncodeArguments(uint16_t binaryIndex) {
std::vector<uint8_t> bytestream(sizeof(GenericBinaryStatusResponse));
auto& parameters = *reinterpret_cast<GenericBinaryStatusResponse*>(bytestream.data());
parameters.index = binaryIndex;
return bytestream;
}
@@ -0,0 +1,50 @@
#include "icsneo/communication/packet/hardwareinfopacket.h"
#include "icsneo/communication/message/hardwareinfo.h"
#include <iostream>
using namespace icsneo;
#pragma pack(push, 1)
typedef struct
{
uint8_t valid;
struct
{
uint8_t day;
uint8_t month;
uint16_t year;
} manufactureDate;
struct
{
uint8_t major;
uint8_t minor;
} hwRev;
uint8_t deviceId;
struct
{
uint8_t major;
uint8_t minor;
} blVersion;
} HardwareInfoFrame;
#pragma pack(pop)
std::shared_ptr<HardwareInfo> HardwareInfoPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
if(bytes.size() < (sizeof(HardwareInfoFrame) + 1)) {
return nullptr;
}
const auto* frame = reinterpret_cast<const HardwareInfoFrame*>(&bytes[1]);
auto msg = std::make_shared<HardwareInfo>();
msg->manufactureDate.day = frame->manufactureDate.day;
msg->manufactureDate.year = frame->manufactureDate.year;
msg->manufactureDate.month = frame->manufactureDate.month;
msg->hardwareRevision.major = frame->hwRev.major;
msg->hardwareRevision.minor = frame->hwRev.minor;
msg->bootloaderVersion.major = frame->blVersion.major;
msg->bootloaderVersion.minor = frame->blVersion.minor;
return msg;
}
+147
View File
@@ -0,0 +1,147 @@
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/message/linmessage.h"
#include "icsneo/communication/packetizer.h"
namespace icsneo {
std::shared_ptr<Message> HardwareLINPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
const HardwareLINPacket* packet = reinterpret_cast<const HardwareLINPacket*>(bytestream.data());
size_t numDataBytes = packet->CoreMiniBitsLIN.len;
size_t numHeaderBytes = sizeof(HardwareLINPacket::CoreMiniBitsLIN);
if( (sizeof(HardwareLINPacket) != bytestream.size()) ||
((numDataBytes + numHeaderBytes) > bytestream.size()) )
return nullptr;
if(numDataBytes)
--numDataBytes; //If data is present, there will be a checksum included
auto msg = std::make_shared<LINMessage>(static_cast<uint8_t>(packet->CoreMiniBitsLIN.ID));
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
msg->isEnhancedChecksum = static_cast<bool>(packet->CoreMiniBitsLIN.TxChkSumEnhanced);
/* Minimum one responder byte and one checksum byte. */
if(2u > packet->CoreMiniBitsLIN.len)
msg->linMsgType = LINMessage::Type::LIN_ERROR;
auto dataStart = bytestream.begin() + numHeaderBytes;
std::copy(dataStart, (dataStart+numDataBytes), std::back_inserter(msg->data));
/* If OK, validate the checksum*/
auto isChecksumInvalid = [&]() -> bool {
/* messages with no data have no checksum (e.g. header only) */
if(!msg->data.size())
return true;
uint8_t checkSum = (8 > numDataBytes) ? *(dataStart + numDataBytes) : packet->CoreMiniBitsLIN.LINByte9;
LINMessage::calcChecksum(*msg);
if(checkSum != msg->checksum) {
msg->isEnhancedChecksum = true;
LINMessage::calcChecksum(*msg);
if(checkSum != msg->checksum) {
msg->isEnhancedChecksum = false;
msg->checksum = checkSum;
return true;
}
}
return false;
};
/* if any of the status bits are set, then this is
either a failed reception or a bus status update. */
msg->errFlags =
{
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxOnlyBreak),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxOnlyBreakSync),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrTxRxMismatch),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxBreakNotZero),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxBreakTooShort),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxSyncNot55),
static_cast<bool>(packet->CoreMiniBitsLIN.ErrRxDataGreater8),
static_cast<bool>(packet->CoreMiniBitsLIN.SyncFerr),
static_cast<bool>(packet->CoreMiniBitsLIN.MidFerr),
static_cast<bool>(packet->CoreMiniBitsLIN.ResponderByteFerr),
isChecksumInvalid(), /* ErrChecksumMatch */
};
msg->statusFlags =
{
static_cast<bool>(packet->CoreMiniBitsLIN.TxChkSumEnhanced),
static_cast<bool>(packet->CoreMiniBitsLIN.TXCommander),
static_cast<bool>(packet->CoreMiniBitsLIN.TXResponder),
static_cast<bool>(packet->CoreMiniBitsLIN.TxAborted),
static_cast<bool>(packet->CoreMiniBitsLIN.UpdateResponderOnce),
static_cast<bool>(packet->CoreMiniBitsLIN.HasUpdatedResponderOnce),
static_cast<bool>(packet->CoreMiniBitsLIN.BusRecovered),
static_cast<bool>(packet->CoreMiniBitsLIN.BreakOnly)
};
if(msg->statusFlags.TxCommander || msg->statusFlags.TxResponder)
msg->linMsgType = LINMessage::Type::LIN_COMMANDER_MSG;
else if(msg->statusFlags.BreakOnly)
msg->linMsgType = LINMessage::Type::LIN_BREAK_ONLY;
if( msg->errFlags.ErrRxBreakOnly || msg->errFlags.ErrRxBreakSyncOnly ||
msg->errFlags.ErrTxRxMismatch || msg->errFlags.ErrRxBreakNotZero ||
msg->errFlags.ErrRxBreakTooShort || msg->errFlags.ErrRxSyncNot55 ||
msg->errFlags.ErrRxDataLenOver8 || msg->errFlags.ErrFrameSync ||
msg->errFlags.ErrFrameMessageID || msg->errFlags.ErrChecksumMatch ||
msg->errFlags.ErrFrameResponderData )
{ msg->linMsgType = LINMessage::Type::LIN_ERROR; }
msg->timestamp = packet->timestamp;
return msg;
}
bool HardwareLINPacket::EncodeFromMessage(LINMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
{
uint8_t size = ((std::min<size_t>(8ul, message.data.size()) + 3ul) & 0xFu);
if(size > 3) { ++size; } // add a checksum byte if there's data
switch(message.linMsgType) {
case LINMessage::Type::LIN_HEADER_ONLY:
case LINMessage::Type::LIN_COMMANDER_MSG:
{
size |= 0x80u;
break;
}
case LINMessage::Type::LIN_BREAK_ONLY:
{
size |= 0x20u;
break;
}
case LINMessage::Type::NOT_SET:
{
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
return false;
}
default:
break;
}
message.protectedID = message.calcProtectedID(message.ID);
bytestream.insert(bytestream.end(),
{
static_cast<uint8_t>(0x00u),
static_cast<uint8_t>(size),
static_cast<uint8_t>((message.description >> 8) & 0xFF),
static_cast<uint8_t>(message.description & 0xFF),
static_cast<uint8_t>(message.protectedID)
});
switch(message.linMsgType) {
case(LINMessage::Type::LIN_COMMANDER_MSG):
case(LINMessage::Type::LIN_UPDATE_RESPONDER):
{
std::copy(message.data.begin(), message.data.end(), std::back_inserter(bytestream));
LINMessage::calcChecksum(message);
bytestream.push_back(message.checksum);
break;
}
default:
break;
}
if(bytestream.size() % 2)
bytestream.push_back(0x41); //padding
return true;
}
} //namespace icsneo
+125
View File
@@ -0,0 +1,125 @@
#include "icsneo/communication/packet/livedatapacket.h"
#include "icsneo/communication/message/livedatamessage.h"
#include <cstring>
#include <vector>
namespace icsneo {
std::shared_ptr<Message> HardwareLiveDataPacket::DecodeToMessage(const std::vector<uint8_t>& bytes, const device_eventhandler_t& report) {
if(bytes.empty() || (bytes.size() < (sizeof(LiveDataHeader) + sizeof(ExtResponseHeader)))) {
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
return nullptr;
}
const auto header = reinterpret_cast<const ExtResponseHeader*>(bytes.data());
if(ExtendedCommand::LiveData != static_cast<ExtendedCommand>(header->command)) {
report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error);
return nullptr;
}
const auto ldHeader = reinterpret_cast<const LiveDataHeader*>(bytes.data() + sizeof(ExtResponseHeader));
// Versioning check to avoid bad data interpretation between disparate libicsneo and firmware versions
if(icsneo::LiveDataUtil::LiveDataVersion != ldHeader->version) {
report(APIEvent::Type::LiveDataVersionMismatch, APIEvent::Severity::Error);
return nullptr;
}
switch(LiveDataCommand(ldHeader->cmd)) {
case LiveDataCommand::RESPONSE: {
auto retMsg = std::make_shared<LiveDataValueMessage>();
const auto responseBytes = reinterpret_cast<const LiveDataValueResponse*>(ldHeader);
retMsg->handle = responseBytes->handle;
retMsg->cmd = static_cast<LiveDataCommand>(responseBytes->cmd);
retMsg->numArgs = responseBytes->numArgs;
for(uint32_t i = 0; i < retMsg->numArgs; ++i) {
retMsg->values.emplace_back(std::make_shared<LiveDataValue>(responseBytes->values[i]));
}
return retMsg;
}
case LiveDataCommand::STATUS: {
auto retMsg = std::make_shared<LiveDataStatusMessage>();
const auto responseBytes = reinterpret_cast<const LiveDataStatusResponse*>(ldHeader);
retMsg->handle = responseBytes->handle;
retMsg->cmd = static_cast<LiveDataCommand>(responseBytes->cmd);
retMsg->status = responseBytes->status;
retMsg->requestedCommand = static_cast<LiveDataCommand>(responseBytes->requestedCommand);
return retMsg;
}
default: {
report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error);
break;
}
}
return nullptr;
}
bool HardwareLiveDataPacket::EncodeFromMessage(LiveDataMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
uint16_t payloadSize = 0;
switch(message.cmd) {
case LiveDataCommand::SUBSCRIBE: {
auto commandMsg = reinterpret_cast<LiveDataCommandMessage*>(&message);
const auto numArgs = commandMsg->args.size();
if(numArgs) {
payloadSize = static_cast<uint16_t>(sizeof(LiveDataSubscribe) + (sizeof(LiveDataArgument) * (numArgs-1)));
bytestream.resize((payloadSize + sizeof(ExtendedCommandHeader)),0);
LiveDataSubscribe* out = reinterpret_cast<LiveDataSubscribe*>(bytestream.data() + sizeof(ExtendedCommandHeader));
out->version = icsneo::LiveDataUtil::LiveDataVersion;
out->cmd = static_cast<uint32_t>(commandMsg->cmd);
if(!commandMsg->handle)
commandMsg->handle = LiveDataUtil::getNewHandle();
out->handle = commandMsg->handle;
out->numArgs = static_cast<uint32_t>(commandMsg->args.size());
out->freqMs = static_cast<uint32_t>(commandMsg->updatePeriod.count());
out->expireMs = static_cast<uint32_t>(commandMsg->expirationTime.count());
for(size_t i = 0; i < numArgs; ++i) {
out->args[i].objectType = commandMsg->args[i]->objectType;
out->args[i].objectIndex = commandMsg->args[i]->objectIndex;
out->args[i].signalIndex = commandMsg->args[i]->signalIndex;
out->args[i].valueType = commandMsg->args[i]->valueType;
}
} else {
report(APIEvent::Type::LiveDataInvalidArgument, APIEvent::Severity::Error);
return false;
}
break;
}
case LiveDataCommand::UNSUBSCRIBE: {
payloadSize = sizeof(LiveDataHeader);
bytestream.resize((payloadSize + sizeof(ExtendedCommandHeader)),0);
auto ldUnsubMsg = reinterpret_cast<LiveDataHeader*>(bytestream.data() + sizeof(ExtendedCommandHeader));
ldUnsubMsg->version = static_cast<uint32_t>(icsneo::LiveDataUtil::LiveDataVersion);
ldUnsubMsg->cmd = static_cast<uint32_t>(message.cmd);
ldUnsubMsg->handle = static_cast<uint32_t>(message.handle);
break;
}
case LiveDataCommand::CLEAR_ALL: {
payloadSize = sizeof(LiveDataHeader);
bytestream.resize((payloadSize + sizeof(ExtendedCommandHeader)),0);
auto clearMsg = reinterpret_cast<LiveDataHeader*>(bytestream.data() + sizeof(ExtendedCommandHeader));
clearMsg->version = static_cast<uint32_t>(icsneo::LiveDataUtil::LiveDataVersion);
clearMsg->cmd = static_cast<uint32_t>(message.cmd);
break;
}
default: {
report(APIEvent::Type::LiveDataInvalidCommand, APIEvent::Severity::Error);
return false;
}
}
// +1 for AA, another +1 for firmware nuance
uint16_t fullSize = static_cast<uint16_t>(1 + sizeof(ExtendedCommandHeader) + payloadSize) + 1;
ExtendedCommandHeader* header = reinterpret_cast<ExtendedCommandHeader*>(bytestream.data());
if(!header) {
report(APIEvent::Type::LiveDataEncoderError, APIEvent::Severity::Error);
return false;
}
header->netid = static_cast<uint8_t>(Network::NetID::Main51);
header->fullLength = fullSize;
header->command = static_cast<uint8_t>(Command::Extended);
header->extendedCommand = static_cast<uint16_t>(ExtendedCommand::LiveData);
header->payloadLength = payloadSize;
return true;
}
} // namespace icsneo
+79
View File
@@ -0,0 +1,79 @@
#include "icsneo/communication/packet/mdiopacket.h"
namespace icsneo
{
const size_t HardwareMDIOPacket::mdioDataSize = 2;
std::shared_ptr<Message> HardwareMDIOPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream)
{
auto msg = std::make_shared<MDIOMessage>();
const HardwareMDIOPacket* packet = reinterpret_cast<const HardwareMDIOPacket*>(bytestream.data());
if((sizeof(HardwareMDIOPacket) != (bytestream.size())) || (packet->length != 0))
{
return nullptr;
}
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
msg->clause = static_cast<MDIOMessage::Clause>(packet->header.ST);
msg->direction = static_cast<MDIOMessage::Direction>((packet->header.OP & 0x2) ? 1 : 0);
msg->isTXMsg = static_cast<bool>(packet->header.TRANSMIT & 0x01u);
msg->phyAddress = (packet->header.PHY_ADDR & 0x1Fu);
if (msg->clause == MDIOMessage::Clause::Clause45)
{ // 16-bit register address
msg->devAddress = (packet->header.C45_DEVTYPE & 0x1Fu);
msg->regAddress = (packet->header.REG_ADDR & 0xFFFFu);
}
else
{ // 5-bit register address
msg->devAddress = 0;
msg->regAddress = (packet->header.REG_ADDR & 0x1Fu);
}
msg->isTXMsg = static_cast<bool>(packet->header.TRANSMIT & 0x01u);
msg->txTimeout = static_cast<bool>(packet->header.ERR_TIMEOUT & 0x01u);
msg->txAborted = static_cast<bool>(packet->header.ERR_JOB_CANCELLED & 0x01u);
msg->txInvalidBus = static_cast<bool>(packet->header.ERR_INVALID_BUS & 0x01u);
msg->txInvalidPhyAddr = static_cast<bool>(packet->header.ERR_INVALID_PHYADDR & 0x01u);
msg->txInvalidRegAddr = static_cast<bool>(packet->header.ERR_INVALID_REGADDR & 0x01u);
msg->txInvalidClause = static_cast<bool>(packet->header.ERR_UNSUPPORTED_CLAUSE & 0x01u);
msg->txInvalidOpcode = static_cast<bool>(packet->header.ERR_UNSUPPORTED_OPCODE & 0x01u);
//We don't care about 0xTRB0Dx in this case...
//copy 0xTRB0STAT even though we likely won't use it either
msg->description = packet->stats;
msg->timestamp = (packet->timestamp & (0x7FFFFFFFFFFFFFFFull));
std::copy(packet->data, packet->data + mdioDataSize, std::back_inserter(msg->data));
return msg;
}
bool HardwareMDIOPacket::EncodeFromMessage(const MDIOMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
{
const size_t numDataBytes = message.data.size();
if(mdioDataSize < numDataBytes)
{
report(APIEvent::Type::MDIOMessageExceedsMaxLength, APIEvent::Severity::Error);
return false;
}
uint8_t st = (message.clause == MDIOMessage::Clause::Clause45) ? 0x0 : 0x1;
uint8_t op = (message.direction == MDIOMessage::Direction::Read) ? 0x2 : 0x1;
uint8_t phyAddr = message.phyAddress & 0x1F;
uint16_t regAddr = (message.clause == MDIOMessage::Clause::Clause45) ? message.regAddress : message.regAddress & 0x1F;
uint8_t c45DevType = (message.clause == MDIOMessage::Clause::Clause45) ? message.devAddress & 0x1F : 0;
bytestream.push_back(static_cast<uint8_t>((message.description >> 8) & 0xFF)); // MSB first
bytestream.push_back(static_cast<uint8_t>(message.description & 0xFF)); // LSB
bytestream.push_back(op); // opcode
bytestream.push_back(st); // st (clause)
bytestream.push_back(phyAddr); // st (clause)
bytestream.push_back(c45DevType); // clause 45 device type
bytestream.push_back(regAddr & 0xFF); // reg addr LSB
bytestream.push_back((regAddr >> 8) & 0xFF); // reg addr MSB
std::copy(message.data.begin(), message.data.end(), std::back_inserter(bytestream));
return true;
}
}
@@ -0,0 +1,44 @@
#include "icsneo/communication/packet/supportedfeaturespacket.h"
#include "icsneo/communication/message/supportedfeaturesmessage.h"
using namespace icsneo;
static constexpr uint16_t SupportedFeaturesCommandVersion = 1;
static constexpr size_t NumSupportedFeaturesFields =
(static_cast<size_t>(SupportedFeature::numSupportedFeatures) + 31) / 32;
#pragma pack(push, 2)
struct SupportedFeaturesResponse {
ExtendedResponseMessage::ResponseHeader header;
uint16_t cmdVersion;
uint16_t numValidBits;
uint32_t featuresFields[NumSupportedFeaturesFields];
};
#pragma pack(pop)
std::shared_ptr<SupportedFeaturesMessage> SupportedFeaturesPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
auto msg = std::make_shared<SupportedFeaturesMessage>();
// Length check: At least a header, a 2-byte cmdVersion field, and a 2-byte numValidBits field.
if(bytes.size() < sizeof(ExtendedResponseMessage::ResponseHeader) + 4) {
return msg; // Empty
}
// Get a reference to the payload to fully validate the length
const auto& response = *reinterpret_cast<const SupportedFeaturesResponse*>(bytes.data());
if(response.cmdVersion != SupportedFeaturesCommandVersion) {
return msg;
}
// Expected size is the header, cmdVersion and numValidBits fields, plus the number of 32-bit bitfields in the response based on numValidBits
auto expectedSize = sizeof(ExtendedResponseMessage::ResponseHeader) + 4 + ((response.numValidBits + 31) / 32) * 4;
// If the response is malformed (too small), return an empty message
if(bytes.size() < expectedSize) {
return msg; // Empty
}
unsigned int loopLimit = std::min<unsigned int>(response.numValidBits, static_cast<unsigned int>(SupportedFeature::numSupportedFeatures));
for(unsigned int i = 0; i < loopLimit; ++i) {
uint32_t wordOffset = i / 32;
uint32_t bitOffset = i % 32;
if((response.featuresFields[wordOffset] >> bitOffset) & 1) {
msg->features.insert(static_cast<SupportedFeature>(i));
}
}
return msg;
}
+81 -28
View File
@@ -1,5 +1,4 @@
#include "icsneo/communication/packetizer.h"
#include <iostream>
#include <iomanip>
using namespace icsneo;
@@ -25,9 +24,8 @@ std::vector<uint8_t>& Packetizer::packetWrap(std::vector<uint8_t>& data, bool sh
return data;
}
bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
bool Packetizer::input(RingBuffer& bytes) {
bool haveEnoughData = true;
bytes.insert(bytes.end(), inputBytes.begin(), inputBytes.end());
while(haveEnoughData) {
switch(state) {
@@ -49,44 +47,97 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
haveEnoughData = false;
break;
}
packetLength = bytes[1] >> 4 & 0xF;
packet.network = Network(bytes[1] & 0xF); // Lower nibble of the second byte is the network ID
packetLength = bytes[1] >> 4 & 0xf; // Upper nibble of the second byte denotes the packet length
packet.network = Network(bytes[1] & 0xf); // Lower nibble of the second byte is the network ID
if(packetLength == 0) { // A length of zero denotes a long style packet
if(packetLength == 0) {
state = ReadState::ParseLongStylePacketHeader;
checksum = false;
headerSize = 6;
} else {
state = ReadState::GetData;
checksum = true; // Even if checksum is not explicitly disallowed, we enable it here, as this goes into length calculation
headerSize = 2;
packetLength += 2; // The packet length given in short packets does not include header
break;
} else if(packetLength == 0xA && packet.network == Network::NetID::DiskData) {
state = ReadState::ParseDiskDataHeader;
break;
}
checksum = true; // Even if checksum is not explicitly disallowed, we enable it here, as this goes into length calculation
headerSize = 2;
packetLength += 2; // The packet length given in short packets does not include header
currentIndex++;
state = ReadState::GetData;
break;
case ReadState::ParseLongStylePacketHeader:
if(bytes.size() < 6) {
haveEnoughData = false;
break;
}
packetLength = bytes[2]; // Long packets have a little endian length on bytes 3 and 4
packetLength |= bytes[3] << 8;
packet.network = Network((bytes[5] << 8) | bytes[4]); // Long packets have their netid stored as little endian on bytes 5 and 6
currentIndex += 4;
packet.network = Network(((bytes[5] << 8) | bytes[4]), false); // Long packets have their netid stored as little endian on bytes 5 and 6. Devices never send actual VNET IDs so we must not perform ID expansion here.
/* Long packets can't have a length less than 6, because that would indicate a negative payload size.
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
* payload, and not the header or checksum.
*/
* Unlike the short packet length, the long packet length encompasses everything from the 0xAA to the
* end of the payload. The short packet length, for reference, only encompasses the length of the actual
* payload, and not the header or checksum.
*/
if(packetLength < 6 || packetLength > 4000) {
bytes.pop_front();
EventManager::GetInstance().add(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
state = ReadState::SearchForHeader;
} else {
state = ReadState::GetData;
break;
}
checksum = false;
headerSize = 6;
currentIndex += 5;
state = ReadState::GetData;
break;
case ReadState::ParseDiskDataHeader:
if(bytes.size() < 3) {
haveEnoughData = false;
break;
}
if(bytes[2] != 0xAA) {
bytes.pop_front();
state = ReadState::SearchForHeader;
break;
}
if(bytes.size() < 4) {
haveEnoughData = false;
break;
}
if(bytes[3] != 0x55) {
bytes.pop_front();
state = ReadState::SearchForHeader;
break;
}
if(bytes.size() < 5) {
haveEnoughData = false;
break;
}
if(bytes[4] != 0x55) {
bytes.pop_front();
state = ReadState::SearchForHeader;
break;
}
if(bytes.size() < 7) {
haveEnoughData = false;
break;
}
packetLength = bytes[5] | (bytes[6] << 8);
checksum = false;
headerSize = 7;
packetLength += headerSize;
currentIndex += 6;
state = ReadState::GetData;
break;
case ReadState::GetData:
// We do not include the checksum in packetLength so it doesn't get copied into the payload buffer
@@ -98,23 +149,25 @@ bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
packet.data.clear();
if(packetLength > 0)
packet.data.resize(packetLength - headerSize);
auto i = 0;
while(currentIndex < packetLength)
packet.data[i++] = bytes[currentIndex++];
bytes.read(packet.data.data(), currentIndex, (packetLength - currentIndex));
currentIndex = packetLength;
if(disableChecksum || !checksum || bytes[currentIndex] == ICSChecksum(packet.data)) {
// Got a good packet
gotGoodPackets = true;
processedPackets.push_back(std::make_shared<Packet>(packet));
for (auto a = 0; a < packetLength; a++)
bytes.pop(packetLength);
if(packet.network == Network::NetID::DiskData && (packetLength - headerSize) % 2 == 0) {
bytes.pop_front();
}
} else {
if(gotGoodPackets) // Don't complain unless we've already gotten a good packet, in case we started in the middle of a stream
report(APIEvent::Type::PacketChecksumError, APIEvent::Severity::Error);
bytes.pop_front(); // Drop the first byte so it doesn't get picked up again
}
// Reset for the next packet
currentIndex = 0;
state = ReadState::SearchForHeader;
+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());
}
}
+1819 -87
View File
File diff suppressed because it is too large Load Diff
+97 -1
View File
@@ -19,6 +19,14 @@
#include "icsneo/platform/ftdi.h"
#endif
#ifdef ICSNEO_ENABLE_FTD3XX
#include "icsneo/platform/ftd3xx.h"
#endif
#ifdef ICSNEO_ENABLE_TCP
#include "icsneo/platform/tcp.h"
#endif
using namespace icsneo;
template<typename T>
@@ -39,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();
@@ -47,6 +67,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
FirmIO::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_TCP
TCP::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
PCAP::Find(newDriverFoundDevices);
#endif
@@ -59,6 +83,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
FTDI::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_FTD3XX
FTD3XX::Find(newDriverFoundDevices);
#endif
// Weak because we don't want to keep devices open if they go out of scope elsewhere
static std::vector<std::weak_ptr<Device>> foundDevices;
@@ -109,6 +137,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<NeoOBD2SIM>(dev, newFoundDevices);
#endif
#ifdef __NEOVICONNECT_H_
makeIfSerialMatches<NeoVIConnect>(dev, newFoundDevices);
#endif
#ifdef __NEOVIFIRE_H_
makeIfPIDMatches<NeoVIFIRE>(dev, newFoundDevices);
#endif
@@ -121,6 +153,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<NeoVIFIRE3>(dev, newFoundDevices);
#endif
#ifdef __NEOVIFIRE3FLEXRAY_H_
makeIfSerialMatches<NeoVIFIRE3FlexRay>(dev, newFoundDevices);
#endif
#ifdef __NEOVIRED2_H_
makeIfSerialMatches<NeoVIRED2>(dev, newFoundDevices);
#endif
@@ -137,6 +173,22 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<RADA2B>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET_H_
makeIfSerialRangeMatches<RADComet>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET2_H_
makeIfSerialRangeMatches<RADComet2>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET3_H_
makeIfSerialMatches<RADComet3>(dev, newFoundDevices);
#endif
#ifdef __RADMOONT1S_H_
makeIfSerialMatches<RADMoonT1S>(dev, newFoundDevices);
#endif
#ifdef __RADEPSILON_H_
makeIfSerialMatches<RADEpsilon>(dev, newFoundDevices);
#endif
@@ -152,6 +204,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
#ifdef __RADGIGASTAR_H_
makeIfSerialMatches<RADGigastar>(dev, newFoundDevices);
#endif
#ifdef __RADGIGASTAR2_H_
makeIfSerialMatches<RADGigastar2>(dev, newFoundDevices);
#endif
#ifdef __RADJUPITER_H_
makeIfSerialMatches<RADJupiter>(dev, newFoundDevices);
@@ -161,6 +217,14 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
makeIfSerialMatches<RADMoon2>(dev, newFoundDevices);
#endif
#ifdef __RADMOON2ZL_H_
makeIfSerialMatches<RADMoon2ZL>(dev, newFoundDevices);
#endif
#ifdef __RADMOON3_H_
makeIfSerialMatches<RADMoon3>(dev, newFoundDevices);
#endif
#ifdef __RADMOONDUO_H_
makeIfSerialMatches<RADMoonDuo>(dev, newFoundDevices);
#endif
@@ -236,6 +300,10 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
NeoVIRED2::DEVICE_TYPE,
#endif
#ifdef __NEOVICONNECT_H_
NeoVIConnect::DEVICE_TYPE,
#endif
#ifdef __NEOVIFIRE_H_
NeoVIFIRE::DEVICE_TYPE,
#endif
@@ -244,6 +312,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
NeoVIFIRE2::DEVICE_TYPE,
#endif
#ifdef __NEOVIFIRE3_H_
NeoVIFIRE3::DEVICE_TYPE,
#endif
#ifdef __NEOVIFIRE3FLEXRAY_H_
NeoVIFIRE3FlexRay::DEVICE_TYPE,
#endif
#ifdef __NEOVIION_H_
NeoVIION::DEVICE_TYPE,
#endif
@@ -256,6 +332,18 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
RADA2B::DEVICE_TYPE,
#endif
#ifdef __RADCOMET_H_
RADComet::DEVICE_TYPE,
#endif
#ifdef __RADCOMET3_H_
RADComet3::DEVICE_TYPE,
#endif
#ifdef __RADMOONT1S_H_
RADMoonT1S::DEVICE_TYPE,
#endif
#ifdef __RADEPSILON_H_
RADEpsilon::DEVICE_TYPE,
#endif
@@ -272,10 +360,18 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
RADGigastar::DEVICE_TYPE,
#endif
#ifdef __RADMOON2_H_
#ifdef __RADGIGASTAR2_H_
RADGigastar2::DEVICE_TYPE,
#endif
#if defined __RADMOON2_H_ || defined __RADMOON2ZL_H_
RADMoon2::DEVICE_TYPE,
#endif
#ifdef __RADMOON3_H_
RADMoon3::DEVICE_TYPE,
#endif
#ifdef __RADMOONDUO_H_
RADMoonDuo::DEVICE_TYPE,
#endif
+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);
+15 -4
View File
@@ -5,7 +5,11 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, Disk::MemoryType memType) {
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
uint64_t& cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
if(amount == 0)
return 0;
@@ -57,7 +61,7 @@ std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_e
auto start = std::chrono::high_resolution_clock::now();
auto readAmount = readLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
useAlignedReadBuffer ? alignedReadBuffer.data() : (into + intoOffset), idealBlockSize, timeout);
useAlignedReadBuffer ? alignedReadBuffer.data() : (into + intoOffset), idealBlockSize, timeout, memType);
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
if(!readAmount.has_value() || *readAmount < curAmt) {
@@ -94,12 +98,19 @@ std::optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_e
return ret;
}
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount) {
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount, MemoryType memType) {
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
if(pos <= cachePos + cache.size() && pos + amount >= cachePos)
cache.clear();
}
std::optional<uint64_t> ReadDriver::readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter) {
std::optional<uint64_t> ReadDriver::readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter, MemoryType memType) {
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
if(cache.empty())
return std::nullopt; // Nothing in the cache
+4 -4
View File
@@ -5,7 +5,7 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
if(amount == 0)
return 0;
@@ -51,7 +51,7 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
if(useAlignedWriteBuffer) {
auto read = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
alignedWriteBuffer.data(), idealBlockSize, timeout);
alignedWriteBuffer.data(), idealBlockSize, timeout, memType);
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
if(read != idealBlockSize)
@@ -62,7 +62,7 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
start = std::chrono::high_resolution_clock::now();
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout);
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout, memType);
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
if(!bytesTransferred.has_value() || *bytesTransferred < curAmt) {
@@ -83,6 +83,6 @@ std::optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device
// No matter how much succeeded, to be safe, we'll invalidate anything
// we may have even tried to write, since it may have succeeded without
// notifying, etc.
readDriver.invalidateCache(pos, amount);
readDriver.invalidateCache(pos, amount, memType);
return ret;
}
+34 -145
View File
@@ -1,8 +1,5 @@
#include "icsneo/disk/extextractordiskreaddriver.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/multichannelcommunication.h"
#include "icsneo/api/lifetime.h"
#include <cstring>
#include "icsneo/communication/message/diskdatamessage.h"
//#define ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
@@ -13,7 +10,7 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
if(amount > getBlockSizeBounds().second)
return std::nullopt;
@@ -28,7 +25,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
unsigned int attempts = 4;
while (attempts-- > 0)
{
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout);
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout, memType);
if (ret.has_value())
break;
}
@@ -36,7 +33,7 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Commu
}
std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
uint64_t sector = pos / SectorSize;
@@ -46,144 +43,33 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligne
if (largeSectorCount != uint64_t(sectorCount))
return std::nullopt;
std::mutex m;
std::condition_variable cv;
uint16_t receiving = 0; // How much are we about to get before another header or completion
uint64_t received = 0;
uint16_t receivedCurrent = 0;
size_t skipping = 0;
std::vector<uint8_t> header;
std::unique_lock<std::mutex> lk(m);
bool error = !com.redirectRead([&](std::vector<uint8_t>&& data) {
std::unique_lock<std::mutex> lk2(m);
if(error) {
lk2.unlock();
cv.notify_all();
return;
}
std::mutex mutex;
uint8_t* intoOffset = into;
int64_t remaining = amount;
bool complete = false;
if(skipping > data.size()) {
skipping -= data.size();
return;
}
size_t offset = skipping;
skipping = 0;
while(offset < data.size()) {
size_t left = data.size() - offset;
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Going to process " << left << " bytes" << std::endl;
#endif
if(header.size() != HeaderLength) {
if(header.empty() && left && data[offset] != 0xaa) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Incorrect header " << int(data[offset]) << ' ' << int(offset) << std::endl;
#endif
error = true;
lk2.unlock();
cv.notify_all();
return;
const auto handle = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> message) {
if(remaining > 0) {
const auto diskdata = std::static_pointer_cast<DiskDataMessage>(message);
const auto& data = diskdata->data;
std::copy(data.data(), data.data() + data.size(), intoOffset);
intoOffset += data.size();
remaining -= data.size();
if(remaining == 0) {
{
std::scoped_lock<std::mutex> lk(mutex);
complete = true;
}
// Did we get a correct header and at least one byte of data?
const auto begin = data.begin() + offset;
int32_t headerLeft = int32_t(HeaderLength - header.size());
if(int32_t(left) < headerLeft) {
// Not enough data here, grab what header we can and continue
header.insert(header.end(), begin, data.end());
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << int(left) << " bytes of header at " << offset << " (incomplete " <<
header.size() << ')' << std::endl;
#endif
return;
}
header.insert(header.end(), begin, begin + headerLeft);
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << int(headerLeft) << " bytes of header at " << offset << " (complete " <<
header.size() << ')' << std::endl;
#endif
offset += headerLeft;
if(header[1] == uint8_t(Network::NetID::RED)) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got extended response " << int(offset) << std::endl;
#endif
// This is the extended command response, not all devices send this
// If we got it, we need to figure out how much more data to ignore
uint16_t length = (header[2] + (header[3] << 8));
// Try for another header after this, regardless how much we choose
// to skip and how we skip it
header.clear();
if(length <= 6) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Incorrect extended response length " << int(length) << ' ' << int(offset) << std::endl;
#endif
error = true;
lk2.unlock();
cv.notify_all();
return;
}
length -= 7;
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Skipping " << int(length) << ' ' << int(left) << std::endl;
#endif
if(left < length) {
skipping = length - left;
return;
}
offset += length;
continue;
}
// The device tells us how much it's sending us before the next header
receiving = (header[5] | (header[6] << 8));
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Started packet of size " << receiving << " bytes" << std::endl;
#endif
}
left = data.size() - offset;
auto count = uint16_t(std::min<uint64_t>(std::min<uint64_t>(receiving - receivedCurrent, left), amount - received));
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "With " << int(left) << " bytes " << int(offset) << std::endl;
#endif
memcpy(into + received, data.data() + offset, count);
received += count;
receivedCurrent += count;
offset += count;
if(amount == received) {
if(receivedCurrent % 2 == 0)
offset++;
header.clear(); // Now we will need another header
lk2.unlock();
cv.notify_all();
lk2.lock();
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Finished!" << std::endl;
#endif
}
else if(receivedCurrent == receiving) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << count << " bytes, " << receivedCurrent << " byte packet " << received <<
" complete of " << amount << std::endl;
#endif
if(receivedCurrent % 2 == 0)
offset++;
header.clear(); // Now we will need another header
receivedCurrent = 0;
} else {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << count << " bytes, incomplete (of " << receiving << " bytes)" << std::endl;
#endif
}
}
});
Lifetime clearRedirect([&com, &lk] { lk.unlock(); com.clearRedirectRead(); });
}, std::make_shared<MessageFilter>(Network::NetID::DiskData)));
if(error)
return std::nullopt;
error = !com.sendCommand(ExtendedCommand::Extract, {
if(!com.sendCommand(ExtendedCommand::Extract, {
uint8_t(sector & 0xff),
uint8_t((sector >> 8) & 0xff),
uint8_t((sector >> 16) & 0xff),
@@ -196,13 +82,16 @@ std::optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligne
uint8_t((sectorCount >> 8) & 0xff),
uint8_t((sectorCount >> 16) & 0xff),
uint8_t((sectorCount >> 24) & 0xff),
});
if(error)
})) {
return std::nullopt;
}
std::unique_lock<std::mutex> lk(mutex);
const auto successful = cv.wait_for(lk, timeout, [&](){ return complete; });
com.removeMessageCallback(handle);
if(!successful)
return std::nullopt;
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
if(hitTimeout || error)
return std::nullopt;
return amount;
return amount - remaining;
}
+39 -20
View File
@@ -1,13 +1,16 @@
#include "icsneo/disk/neomemorydiskdriver.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/message/flashmemorymessage.h"
#include <cstring>
#include <iostream>
using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
const auto filter = std::make_shared<MessageFilter>((memType == MemoryType::SD ? Network::NetID::NeoMemorySDRead : Network::NetID::RED_INT_MEMORYREAD));
filter->includeInternalInAny = true;
if(pos % SectorSize != 0)
return std::nullopt;
@@ -16,35 +19,47 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
return std::nullopt;
const uint64_t currentSector = pos / SectorSize;
auto msg = com.waitForMessageSync([&currentSector, &com] {
const uint8_t memLocation = (uint8_t)memType;
uint64_t numWords = amount / 2;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, &numWords] {
return com.sendCommand(Command::NeoReadMemory, {
MemoryTypeSD,
memLocation,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
uint8_t((currentSector >> 24) & 0xFF),
uint8_t(SectorSize & 0xFF),
uint8_t((SectorSize >> 8) & 0xFF),
uint8_t((SectorSize >> 16) & 0xFF),
uint8_t((SectorSize >> 24) & 0xFF)
uint8_t(numWords & 0xFF),
uint8_t((numWords >> 8) & 0xFF),
uint8_t((numWords >> 16) & 0xFF),
uint8_t((numWords >> 24) & 0xFF)
});
}, NeoMemorySDRead, timeout);
}, filter, timeout);
if(!msg)
return 0;
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!sdmsg || sdmsg->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
if(memType == MemoryType::SD) {
const auto mem = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!mem || mem->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
}
memcpy(into, mem->data.data(), SectorSize);
} else { // flash
const auto mem = std::dynamic_pointer_cast<FlashMemoryMessage>(msg);
if(!mem || mem->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
}
memcpy(into, mem->data.data(), SectorSize);
}
memcpy(into, sdmsg->data.data(), SectorSize);
return SectorSize;
}
std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
@@ -55,15 +70,19 @@ std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communicati
return std::nullopt;
const uint64_t currentSector = pos / SectorSize;
auto msg = com.waitForMessageSync([&currentSector, &com, from, amount] {
const uint8_t memLocation = (uint8_t)memType;
uint64_t numWords = amount / 2;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, from, amount, &numWords] {
std::vector<uint8_t> command = {
MemoryTypeSD,
memLocation,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
uint8_t((currentSector >> 24) & 0xFF),
uint8_t(SectorSize & 0xFF),
uint8_t((SectorSize >> 8) & 0xFF),
uint8_t(numWords & 0xFF),
uint8_t((numWords >> 8) & 0xFF),
};
command.insert(command.end(), from, from + amount);
return com.sendCommand(Command::NeoWriteMemory, command);
+4 -4
View File
@@ -4,25 +4,25 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
std::optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
std::optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
std::optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return std::nullopt;
}
+1 -1
View File
@@ -7,7 +7,7 @@ using namespace icsneo;
using namespace icsneo::Disk;
std::optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
if(amount > getBlockSizeBounds().second)
+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 -13
View File
@@ -101,18 +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. The first half of the channels, channels ``0-31`` in the outputted wave file, represent downstream channel ``0-31``. Likewise, the second half of the channels,
channels ``32-63`` in the outputted wave file, represent upstream channel ``0-31``. Let ``NUM_CHANNELS`` be the total number of channels in a single stream. If we introduce a
variable ``IS_UPSTREAM`` which is ``0`` when downstream and ``1`` when upstream and desired a channel ``CHANNEL_NUM`` in either downstream or upstream the
channel ``IS_UPSTREAM * NUM_CHANNELS + CHANNEL_NUM`` would correspond to the channel in the outputted wave file.
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 ``1*32 + 8 = 40``. The following ``FFmpeg`` command may be ran in a linux environment to create a new wave
file ``out_upstream_ch8.wav`` which contains only PCM samples off of upstream channel ``8``.
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.40 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-2020, 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-2020, 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
+40 -1
View File
@@ -1,8 +1,15 @@
option(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE "Build the command-line interactive C example." ON)
option(LIBICSNEO_BUILD_C_SIMPLE_EXAMPLE "Build the command-line simple C example." ON)
option(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE "Build the command-line simple C example." ON)
option(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE "Build the simple C++ example." ON)
option(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE "Build the command-line interactive C++ example." ON)
option(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE "Build the A2B example." ON)
option(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE "Build the LIN example." ON)
option(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE "Build the Live Data example." ON)
option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON)
option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON)
option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON)
option(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE "Build the app error example." ON)
# Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
@@ -12,6 +19,14 @@ if(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE)
add_subdirectory(c/interactive)
endif()
if(LIBICSNEO_BUILD_C_SIMPLE_EXAMPLE)
add_subdirectory(c/simple)
endif()
if(LIBICSNEO_BUILD_C_LEGACY_EXAMPLE)
add_subdirectory(c/legacy)
endif()
if(LIBICSNEO_BUILD_CPP_SIMPLE_EXAMPLE)
add_subdirectory(cpp/simple)
endif()
@@ -24,6 +39,30 @@ if(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE)
add_subdirectory(cpp/a2b)
endif()
if(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE)
add_subdirectory(cpp/lin)
endif()
if(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE)
add_subdirectory(cpp/livedata)
endif()
if(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE)
add_subdirectory(cpp/coremini)
endif()
if(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE)
add_subdirectory(cpp/mdio)
endif()
if(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE)
add_subdirectory(cpp/vsa)
endif()
if(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE)
add_subdirectory(cpp/apperror)
endif()
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp)
# endif()
+2 -13
View File
@@ -1,16 +1,5 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneoc-interactive-example VERSION 0.2.0)
include(GNUInstallDirs)
# Include libicsneo's include directory
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
if(UNIX)
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
endif()
add_executable(libicsneoc-interactive-example src/main.c)
if(UNIX)
target_link_libraries(libicsneoc-interactive-example ${CMAKE_DL_LIBS})
endif()
endif()
target_link_libraries(libicsneoc-interactive-example icsneoc)
+45 -18
View File
@@ -1,5 +1,5 @@
// Signal to dynamically load the library
#define ICSNEOC_DYNAMICLOAD
//#define ICSNEOC_DYNAMICLOAD
#include <stdio.h>
#include <stdlib.h>
@@ -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)) {
@@ -219,21 +221,21 @@ int main() {
// Attempt to initialize the library and access its functions
// This call searches for icsneoc.dll according to the standard dynamic-link library search order
int ret = icsneo_init();
if(ret == 1) {
printf("The library was already initialized!\n");
return ret;
}
if(ret == 2) {
printf("The library could not be found!\n");
return ret;
}
if(ret == 3) {
printf("The library is missing functions!\n");
return ret;
}
//int ret = icsneo_init();
//if(ret == 1) {
// printf("The library was already initialized!\n");
// return ret;
//}
//
//if(ret == 2) {
// printf("The library could not be found!\n");
// return ret;
//}
//
//if(ret == 3) {
// printf("The library is missing functions!\n");
// return ret;
//}
neoversion_t ver = icsneo_getVersion();
printf("ICS icsneoc.dll version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
@@ -487,6 +489,31 @@ int main() {
printf("(%"PRIu64")\n", canMsg->timestamp);
break;
}
case ICSNEO_NETWORK_TYPE_LIN: {
neomessage_lin_t* linMsg = (neomessage_lin_t*)frame;
size_t frameLen = linMsg->length;
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
size_t numberBytesHeader = (dataLen > 1) ? 3 : 1;
size_t numberBytesData = frameLen - numberBytesHeader;
if(linMsg->netid == ICSNEO_NETID_LIN) {
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
}
else if (linMsg->netid == ICSNEO_NETID_LIN2) {
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
}
for(size_t i = 0; i < dataLen; ++i) {
if (i < 2) {
printf("%02x ", linMsg->header[i+1]);
} else {
printf("%02x ", linMsg->data[i-2]);
}
}
if(numberBytesData > 0)
printf("| Checksum: 0x%02x\n", linMsg->data[numberBytesData-1]);
else
printf("| Checksum: 0x%02x\n", linMsg->header[numberBytesHeader-1]);
}
default:
printf("\tMessage on netid %d with length %zu\n", frame->netid, frame->length);
break;
@@ -762,7 +789,7 @@ int main() {
case 'X':
case 'x':
printf("Exiting program\n");
return !icsneo_close();
return 0;
default:
printf("Unexpected input, exiting!\n");
return 1;
+4
View File
@@ -0,0 +1,4 @@
add_executable(libicsneoc-legacy-lin-example lin/main.c)
add_executable(libicsneoc-legacy-device-settings-example deviceSettings/main.c)
target_link_libraries(libicsneoc-legacy-lin-example icsneolegacy)
target_link_libraries(libicsneoc-legacy-device-settings-example icsneolegacy)
+80
View File
@@ -0,0 +1,80 @@
# libicsneo C Example
This is an example console application that uses the icsneoc library to control an Intrepid Control Systems hardware device.
## Cloning
This will create a copy of the repository on your local machine.
Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples -b v0.2.0-dev --recursive
```
Alternatively, if you cloned without the `--recursive` flag, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
## Windows using Visual Studio 2017+
### Building the DLL
First, we are going to build the icsneoc library into a .dll file that we can later use in order to access the library functions.
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->Cmake...`
3. Navigate to `third-party/libicsneo` and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Select `Build->Rebuild All`
6. Visual Studio will generate the `icsneoc.dll` file, which can then be found by selecting `Project->Cmake Cache (x64-Debug Only)->Open in Explorer`. If the file cannot be found, search in `libicsneo-examples/third-party/libicsneo/out/build/x64-Debug` and double-check that the build succeeded in step 5.
7. Move the `icsneoc.dll` file to the `/C/Windows/System32` folder. This will allow it to be found by icsneo_init(), which loads all the library functions.
* Alternatively, the `icsneoc.dll` file can be placed in the same directory as `libicsneoc-example.exe`, which is typically `libicsneo-examples/libicsneoc-example/out/build/x64-Debug`, although this is not recommended. For more information, refer to [the Microsoft documentation](https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order).
### Building the example program
Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions.
1. Choose `File->Open->Cmake...`
2. Navigate to `libicsneo-examples/libicsneoc-example` and select the `CMakeLists.txt` there.
3. Visual Studio will process the CMake project.
4. Select `Build->Rebuild All`
5. Click on the dropdown arrow attached to the green play button (labelled "Select Startup Item") and select `libicsneoc-example.exe`
6. Click on the green play button to run the example.
## Ubuntu 18.04 LTS
### Building the .so
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo cp libicsneoc.so /usr/lib` so that it can be found via the default ubuntu .so search path. For more information, see the [ld.so.8 man page](http://man7.org/linux/man-pages/man8/ld.so.8.html).
### Building the example program
Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions.
1. Change directories to `libicsneo-examples/libicsneoc-example`
2. Create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo ./libicsneoc-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
## macOS
Instructions coming soon&trade;
+95
View File
@@ -0,0 +1,95 @@
/*
NOTE: This example is written for a RED 2 device. To use another device type,
change the union lookup type in the pSettings structure to match your device!
e.g. pSettings.Settings.red2 -> pSettings.Settings.fire3
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#if defined _WIN32
#include "icsneo/platform/windows.h"
#define SLEEP(msecs) Sleep(msecs)
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <time.h>
#define SLEEP(msecs) do { \
struct timespec ts; \
ts.tv_sec = msecs/1000; \
ts.tv_nsec = msecs%1000*1000; \
nanosleep(&ts, NULL); \
} while (0)
#else
#error "Platform unknown"
#endif
// Get the PRIu64 macro for timestamps
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
// Include icsneo/icsneolegacy.h to access library functions
#include "icsneo/icsneolegacy.h"
int main() {
int ver = icsneoGetDLLVersion();
printf("ICS icsneolegacy.dll version %u\n\n", ver);
// Find and attempt to open device
//legacy open device
int numDevices = 255;
NeoDeviceEx devices[255] = {0};
void* hObject = NULL; // holds a handle to the neoVI object
int iRetVal = 0;
int iResult = 0;
SDeviceSettings pSettings = {0};
iRetVal = icsneoFindDevices(devices, &numDevices, NULL, 0, NULL, 0);
if(iRetVal && numDevices > 0) {
// Attempt to open the selected device, enable message polling, and go online
iRetVal = icsneoOpenDevice(&devices[0], &hObject, NULL, 1, 0, NULL, 0);
if(iRetVal) {
puts("Device found and opened!\n");
} else {
puts("Device found but failed to open!\n");
}
} else {
puts("No new devices found!\n");
}
// Get device settings
{
iRetVal = icsneoValidateHObject(hObject);
iRetVal = icsneoGetDeviceSettings(hObject, &pSettings, sizeof(SRed2Settings), 0);
if(iRetVal) {
puts("Settings read successfully\n");
printf("Baudrate: %u\n", pSettings.Settings.red2.lin1.Baudrate);
printf("Master resistor: %s \n", (pSettings.Settings.red2.lin1.MasterResistor == 0 ? "true" : "false"));
} else {
puts("Settings not read!\n");
}
}
SLEEP(1000);
// set device settings
{
pSettings.Settings.red2.lin1.Baudrate = 19200;
pSettings.Settings.red2.lin1.MasterResistor = 1;
iRetVal = icsneoSetDeviceSettings(hObject, &pSettings, sizeof(pSettings), 1, 0);
if(iRetVal) {
puts("Settings set successfully\n");
iRetVal = icsneoGetDeviceSettings(hObject, &pSettings, sizeof(SRed2Settings), 0);
printf("Baudrate: %u\n", pSettings.Settings.red2.lin1.Baudrate);
printf("Master resistor: %s\n", (pSettings.Settings.red2.lin1.MasterResistor == 0 ? "true" : "false"));
} else {
puts("Settings not set!\n");
}
}
int iNumberOfErrors = 0;
// Attempt to close the device
{
// Close Communication
iResult = icsneoClosePort(hObject, &iNumberOfErrors);
}
puts("Exiting program\n");
return iResult;
}
+164
View File
@@ -0,0 +1,164 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdbool.h>
#if defined _WIN32
#include "icsneo/platform/windows.h"
#define SLEEP(msecs) Sleep(msecs)
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <time.h>
#define SLEEP(msecs) do { \
struct timespec ts; \
ts.tv_sec = msecs/1000; \
ts.tv_nsec = msecs%1000*1000; \
nanosleep(&ts, NULL); \
} while (0)
#else
#error "Platform unknown"
#endif
// Get the PRIu64 macro for timestamps
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
// Include icsneo/icsneolegacy.h to access library functions
#include "icsneo/icsneolegacy.h"
int main() {
int ver = icsneoGetDLLVersion();
printf("ICS icsneolegacy.dll version %u\n\n", ver);
// Find and attempt to open device
//legacy open device
int numDevices = 255;
NeoDeviceEx devices[255] = {0};
void* hObject = NULL; // holds a handle to the neoVI object
int iRetVal = 0;
int iResult = 0;
iRetVal = icsneoFindDevices(devices, &numDevices, NULL, 0, NULL, 0);
if(iRetVal && numDevices < 0) {
// Attempt to open the selected device, enable message polling, and go online
iRetVal = icsneoOpenDevice(&devices[0], &hObject, NULL, 1, 0, NULL, 0);
if(iRetVal) {
printf("Device found and opened!\n");
} else {
printf("Device found but failed to open!\n");
}
} else {
printf("No new devices found!\n");
}
// Send message LIN
{
//lin responder frame
icsSpyMessageJ1850 msg1 = {0};
int lNetworkID;
msg1.Protocol = SPY_PROTOCOL_LIN;
msg1.StatusBitField = 0;
msg1.StatusBitField2 = 0;
lNetworkID = NETID_LIN2;
msg1.Header[0] = 0x11; //protected ID
msg1.Header[1] = 0xaa;
msg1.Header[2] = 0xbb;
msg1.Data[0] = 0xcc;
msg1.Data[1] = 0xdd;
msg1.Data[2] = 0x11;
msg1.Data[3] = 0x22;
msg1.Data[4] = 0x33;
msg1.Data[5] = 0x44;
msg1.Data[6] = 0x44; //checksum 0x33 enhanced
msg1.NumberBytesData = 7;
msg1.NumberBytesHeader = 3;
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg1, lNetworkID, 1);
if(!iRetVal)
printf("Device failed to transmit LIN responder update\n");
else
printf("Transmitted successfully!\n");
icsSpyMessageJ1850 msg2 = {0};
msg2.Protocol = SPY_PROTOCOL_LIN;
msg2.StatusBitField = SPY_STATUS_INIT_MESSAGE;
lNetworkID = NETID_LIN;
msg2.Header[0] = 0x11; //protected ID
msg2.NumberBytesData = 0;
msg2.NumberBytesHeader = 1;
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg2, lNetworkID, 1);
if(!iRetVal)
printf("Device failed to transmit LIN commander header\n");
else
printf("Transmitted successfully!\n");
SLEEP(250);
icsSpyMessageJ1850 msg3 = {0};
msg3.Protocol = SPY_PROTOCOL_LIN;
msg3.StatusBitField = SPY_STATUS_INIT_MESSAGE;
msg3.StatusBitField2 = 0;
lNetworkID = NETID_LIN;
msg3.Header[0] = 0xe2; //protected ID
msg3.Header[1] = 0x44;
msg3.Header[2] = 0x33;
msg3.Data[0] = 0x22;
msg3.Data[1] = 0x11;
msg3.Data[2] = 0x11;
msg3.Data[3] = 0x22;
msg3.Data[4] = 0x33;
msg3.Data[5] = 0x44;
msg3.Data[6] = 0xc7; //checksum
msg3.NumberBytesData = 7;
msg3.NumberBytesHeader = 3;
iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg3, lNetworkID, 1);
if(!iRetVal)
printf("Device failed to transmit LIN commander message\n");
else
printf("Transmitted successfully!\n");
}
SLEEP(1000);
// Get messages
{
static icsSpyMessage rxMsg[30000];
int numMessages = 0;
int numErrors = 0;
iRetVal = icsneoGetMessages(hObject, rxMsg, &numMessages, &numErrors);
if(!iRetVal)
printf("Get Messages failed!\n");
else
for(int idx = 0; idx < numMessages; ++idx)
{
if(rxMsg[idx].Protocol == SPY_PROTOCOL_LIN) {
const icsSpyMessageJ1850* linMsg = (icsSpyMessageJ1850*)&rxMsg[idx];
size_t frameLen = (linMsg->NumberBytesHeader + linMsg->NumberBytesData);
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
if(linMsg->NetworkID == NETID_LIN) {
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen);
}
else if (linMsg->NetworkID == NETID_LIN2) {
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen);
}
for(size_t i = 0; i < dataLen; ++i) {
if (i < 2) {
printf("%02x ", linMsg->Header[i+1]);
} else {
printf("%02x ", linMsg->Data[i-2]);
}
}
if(linMsg->NumberBytesData > 0)
printf("| Checksum: 0x%02x\n", linMsg->Data[linMsg->NumberBytesData-1]);
else
printf("| Checksum: 0x%02x\n", linMsg->Header[linMsg->NumberBytesHeader-1]);
}
}
}
int iNumberOfErrors = 0;
// Attempt to close the device
{
// Close Communication
iResult = icsneoClosePort(hObject, &iNumberOfErrors);
}
printf("Exiting program\n");
return iResult;
}
+5
View File
@@ -0,0 +1,5 @@
add_executable(libicsneoc-simple-lin-example lin/main.c)
if(UNIX)
target_link_libraries(libicsneoc-simple-lin-example ${CMAKE_DL_LIBS})
endif()
target_link_libraries(libicsneoc-simple-lin-example icsneoc)
+80
View File
@@ -0,0 +1,80 @@
# libicsneo C Example
This is an example console application that uses the icsneoc library to control an Intrepid Control Systems hardware device.
## Cloning
This will create a copy of the repository on your local machine.
Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples -b v0.2.0-dev --recursive
```
Alternatively, if you cloned without the `--recursive` flag, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
## Windows using Visual Studio 2017+
### Building the DLL
First, we are going to build the icsneoc library into a .dll file that we can later use in order to access the library functions.
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->Cmake...`
3. Navigate to `third-party/libicsneo` and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Select `Build->Rebuild All`
6. Visual Studio will generate the `icsneoc.dll` file, which can then be found by selecting `Project->Cmake Cache (x64-Debug Only)->Open in Explorer`. If the file cannot be found, search in `libicsneo-examples/third-party/libicsneo/out/build/x64-Debug` and double-check that the build succeeded in step 5.
7. Move the `icsneoc.dll` file to the `/C/Windows/System32` folder. This will allow it to be found by icsneo_init(), which loads all the library functions.
* Alternatively, the `icsneoc.dll` file can be placed in the same directory as `libicsneoc-example.exe`, which is typically `libicsneo-examples/libicsneoc-example/out/build/x64-Debug`, although this is not recommended. For more information, refer to [the Microsoft documentation](https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order).
### Building the example program
Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions.
1. Choose `File->Open->Cmake...`
2. Navigate to `libicsneo-examples/libicsneoc-example` and select the `CMakeLists.txt` there.
3. Visual Studio will process the CMake project.
4. Select `Build->Rebuild All`
5. Click on the dropdown arrow attached to the green play button (labelled "Select Startup Item") and select `libicsneoc-example.exe`
6. Click on the green play button to run the example.
## Ubuntu 18.04 LTS
### Building the .so
First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions.
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo cp libicsneoc.so /usr/lib` so that it can be found via the default ubuntu .so search path. For more information, see the [ld.so.8 man page](http://man7.org/linux/man-pages/man8/ld.so.8.html).
### Building the example program
Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions.
1. Change directories to `libicsneo-examples/libicsneoc-example`
2. Create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make` to build the library.
* Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Run `sudo ./libicsneoc-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
## macOS
Instructions coming soon&trade;
+429
View File
@@ -0,0 +1,429 @@
// Signal to dynamically load the library
//#define ICSNEOC_DYNAMICLOAD
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
// Get the PRIu64 macro for timestamps
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
// Include icsneo/icsneoc.h to access library functions
#include "icsneo/icsneoc.h"
#ifdef _WIN32
#define SLEEP(msecs) Sleep(msecs)
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <time.h>
#define SLEEP(msecs) do { \
struct timespec ts; \
ts.tv_sec = msecs/1000; \
ts.tv_nsec = msecs%1000*1000; \
nanosleep(&ts, NULL); \
} while (0)
#else
#error "Platform unknown"
#endif
size_t msgLimit = 50000;
size_t numDevices = 0;
neodevice_t devices[99];
const neodevice_t* selectedDevice = NULL;
/**
* \brief Prints all current known devices to output in the following format:
* [num] DeviceType SerialNum Connected: Yes/No Online: Yes/No Msg Polling: On/Off
*
* If any devices could not be described due to an error, they will appear in the following format:
* Description for device num not available!
*/
void printAllDevices() {
if(numDevices == 0) {
printf("No devices found! Please scan for new devices.\n");
}
for(size_t i = 0; i < numDevices; i++) {
char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 };
size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
// Updates productDescription and descriptionLength for each device
if(icsneo_describeDevice(devices + i, productDescription, &descriptionLength)) {
printf("[%zd] %s\tConnected: ", i + 1, productDescription);
if(icsneo_isOpen(devices + i)) {
printf("Yes\t");
} else printf("No\t");
printf("Online: ");
if(icsneo_isOnline(devices + i)) {
printf("Yes\t");
} else printf("No\t");
printf("Msg Polling: ");
if(icsneo_isMessagePollingEnabled(devices + i)) {
printf("On\n");
} else printf("Off\n");
} else {
printf("Description for device %zd not available!\n", i + 1);
}
}
}
/**
* \brief Scans for any new devices, adding them to devices and updating numDevices accordingly
* A total of 99 devices may be stored at once
*/
size_t scanNewDevices() {
neodevice_t newDevices[99];
size_t numNewDevices = 99;
icsneo_findAllDevices(newDevices, &numNewDevices);
for(size_t i = 0; i < numNewDevices; ++i) {
devices[numDevices + i] = newDevices[i];
}
numDevices += numNewDevices;
return numNewDevices;
}
void printLastError() {
neoevent_t error;
if(icsneo_getLastError(&error))
printf("Error 0x%u: %s\n", error.eventNumber, error.description);
else
printf("No errors found!\n");
}
/**
* \brief Gets all current API events and prints them to output
* Flushes the API event cache, meaning future calls (barring any new events) will not detect any further API events
*/
void printAPIEvents() {
neoevent_t events[99];
size_t eventCount = 99;
if(icsneo_getEvents(events, &eventCount)) {
if(eventCount == 1) {
printf("1 API event found!\n");
printf("Event 0x%u: %s\n", events[0].eventNumber, events[0].description);
} else {
printf("%d API events found!\n", (int) eventCount);
for(size_t i = 0; i < eventCount; ++i) {
printf("Event 0x%u: %s\n", events[i].eventNumber, events[i].description);
}
}
} else {
printf("Failed to get API events!\n");
}
}
/**
* \brief Gets all current device events and prints them to output. If no device events were found, printAPIEvents() is called
* Flushes the device event cache, meaning future calls (barring any new events) will not detect any further device events for this device
*/
void printDeviceEvents(neodevice_t* device) {
neoevent_t events[99];
size_t eventCount = 99;
if(icsneo_getDeviceEvents(selectedDevice, events, &eventCount)) {
if(eventCount == 1) {
printf("1 device event found!\n");
printf("Event 0x%x: %s\n", events[0].eventNumber, events[0].description);
} else {
printf("%d device events found!\n", (int) eventCount);
for(size_t i = 0; i < eventCount; ++i) {
printf("Event 0x%x: %s\n", events[i].eventNumber, events[i].description);
}
}
}
}
/**
* \brief Used to check character inputs for correctness (if they are found in an expected list)
* \param[in] numArgs the number of possible options for the expected character
* \param[in] ... the possible options for the expected character
* \returns the entered character
*
* This function repeatedly prompts the user for input until a matching input is entered
* Example usage: char input = getCharInput(5, 'F', 'u', 'b', 'a', 'r');
*/
char getCharInput(int numArgs, ...) {
// 99 chars shold be more than enough to catch any typos
char input[99];
bool found = false;
va_list vaList;
va_start(vaList, numArgs);
char* list = (char*) calloc(numArgs, sizeof(char));
for(int i = 0; i < numArgs; ++i) {
*(list + i) = va_arg(vaList, int);
}
va_end(vaList);
while(!found) {
if(fgets(input, 99, stdin) == NULL) {
break;
}
if(strlen(input) == 2) {
for(int i = 0; i < numArgs; ++i) {
if(input[0] == *(list + i)) {
found = true;
break;
}
}
}
if(!found) {
printf("Input did not match expected options. Please try again.\n");
}
}
free(list);
return input[0];
}
/**
* \brief Prompts the user to select a device from the list of currently known devices
* \returns a pointer to the device in devices[] selected by the user
* Requires an input from 1-9, so a maximum of 9 devices are supported
*/
const neodevice_t* selectDevice() {
printf("Please select a device:\n");
printAllDevices();
printf("\n");
size_t selectedDeviceNum = 10;
while(selectedDeviceNum > numDevices) {
char deviceSelection = getCharInput(9, '1', '2', '3', '4', '5', '6', '7', '8', '9');
if(deviceSelection < '0') {
printf("Selected device out of range!\n");
continue;
}
selectedDeviceNum = deviceSelection - '0';
if(selectedDeviceNum > numDevices) {
printf("Selected device out of range!\n");
}
}
printf("\n");
return devices + selectedDeviceNum - 1;
}
int main() {
neoversion_t ver = icsneo_getVersion();
printf("ICS icsneoc version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch);
// Find and attempt to open device
size_t numNewDevices = scanNewDevices();
if(numNewDevices == 1) {
printf("1 new device found!\n");
} else {
printf("%d new devices found!\n", (int) numNewDevices);
}
printAllDevices();
printf("\n");
// Select a device and get its description
if(numDevices == 0) {
printf("No devices found! Please scan for new devices.\n\n");
return 1;
}
selectedDevice = &devices[0];
// Get the product description for the device
char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 };
size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION;
icsneo_describeDevice(selectedDevice, productDescription, &descriptionLength);
// Attempt to open the selected device
{
if(icsneo_openDevice(selectedDevice)) {
printf("%s successfully opened!\n\n", productDescription);
} else {
printf("%s failed to open!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Attempt to go online
{
if(icsneo_goOnline(selectedDevice)) {
printf("%s successfully went online!\n\n", productDescription);
} else {
printf("%s failed to go online!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Attempt to enable message polling
{
if(icsneo_enableMessagePolling(selectedDevice)) {
printf("Successfully enabled message polling for %s!\n\n", productDescription);
} else {
printf("Failed to enable message polling for %s!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Send message LIN
{
// Start generating sample msg
uint8_t sendMessageData[8];
sendMessageData[0] = 0x33;
sendMessageData[1] = 0x44;
sendMessageData[2] = 0x55;
sendMessageData[3] = 0x66;
sendMessageData[4] = 0x77;
sendMessageData[5] = 0x88;
sendMessageData[6] = 0x88;
neomessage_lin_t msg = {0};
msg.header[0] = 0x11; //protected ID
msg.header[1] = 0x11;
msg.header[2] = 0x22;
msg.length = 10;
msg.netid = ICSNEO_NETID_LIN;
msg.data = sendMessageData;
msg.linStatus.txCommander = 1;
msg.linStatus.txChecksumEnhanced = 1;
msg.type = ICSNEO_NETWORK_TYPE_LIN;
msg.checksum = 0x88;
// Attempt to transmit the sample msg
if(icsneo_transmit(selectedDevice, (const neomessage_t*) &msg)) {
printf("Message transmit successful!\n\n");
} else {
printf("Failed to transmit message to %s!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Wait for a moment
SLEEP(1000);
// Get messages
{
// Prepare the array of neomessage_t ptrs for reading in the messages
neomessage_t* msgs = (neomessage_t*) malloc(msgLimit * sizeof(neomessage_t));
// Get messages
size_t msgCount = msgLimit;
// Attempt to get messages
if(!icsneo_getMessages(selectedDevice, msgs, &msgCount, (uint64_t) 0)) {
printf("Failed to get messages for %s!\n\n", productDescription);
printLastError();
free(msgs);
printf("\n");
}
if(msgCount == 1) {
printf("1 message received from %s!\n", productDescription);
} else {
printf("%d messages received from %s!\n", (int) msgCount, productDescription);
}
// Print out the received messages
for(size_t i = 0; i < msgCount; i++) {
const neomessage_t* msg = &msgs[i];
switch(msg->messageType) {
case ICSNEO_MESSAGE_TYPE_FRAME: {
const neomessage_frame_t* frame = (neomessage_frame_t*)msg;
switch(frame->type) {
case ICSNEO_NETWORK_TYPE_CAN: {
neomessage_can_t* canMsg = (neomessage_can_t*)frame;
printf("\t0x%03x [%zu] ", canMsg->arbid, canMsg->length);
for(size_t i = 0; i < canMsg->length; i++) {
printf("%02x ", canMsg->data[i]);
}
if(canMsg->status.transmitMessage)
printf("TX%s %04x ", canMsg->status.globalError ? " ERR" : "", canMsg->description);
printf("(%"PRIu64")\n", canMsg->timestamp);
break;
}
case ICSNEO_NETWORK_TYPE_LIN: {
neomessage_lin_t* linMsg = (neomessage_lin_t*)frame;
size_t frameLen = linMsg->length;
size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0;
size_t numberBytesHeader = (dataLen > 1) ? 3 : 1;
size_t numberBytesData = frameLen - numberBytesHeader;
if(linMsg->netid == ICSNEO_NETID_LIN) {
printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
}
else if (linMsg->netid == ICSNEO_NETID_LIN2) {
printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen);
}
for(size_t i = 0; i < dataLen; ++i) {
if (i < 2) {
printf("%02x ", linMsg->header[i+1]);
} else {
printf("%02x ", linMsg->data[i-2]);
}
}
printf("| Checksum: 0x%02x\n", linMsg->checksum);
break;
}
}
break;
}
case ICSNEO_MESSAGE_TYPE_CAN_ERROR_COUNT: {
const neomessage_can_error_t* cec = (neomessage_can_error_t*)msg;
printf("\tCAN error counts changed, TEC=%d, REC=%d%s", cec->transmitErrorCount, cec->receiveErrorCount,
cec->status.canBusOff ? " (Bus Off)" : "");
break;
}
}
}
printf("\n");
free(msgs);
}
// Attempt to disable message polling
{
if(icsneo_disableMessagePolling(selectedDevice)) {
printf("Successfully disabled message polling for %s!\n\n", productDescription);
} else {
printf("Failed to disable message polling limit for %s!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Attempt to go offline
{
if(icsneo_goOffline(selectedDevice)) {
printf("%s successfully went offline!\n\n", productDescription);
} else {
printf("%s failed to go offline!\n\n", productDescription);
printLastError();
printf("\n");
}
}
// Attempt to close the device
{
if(icsneo_closeDevice(selectedDevice)) {
numDevices--;
printf("Successfully closed %s!\n\n", productDescription);
// Shifts everything after the removed device 1 index to the left
bool startResizing = false;
for(size_t i = 0; i < numDevices; ++i) {
if(selectedDevice == devices + i)
startResizing = true;
if(startResizing)
devices[i] = devices[i + 1];
}
selectedDevice = NULL;
} else {
printf("Failed to close %s!\n\n", productDescription);
printLastError();
printf("\n");
}
}
//exit
printf("Exiting program\n");
return 0;
}
-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)
-48
View File
@@ -1,48 +0,0 @@
# libicsneo C++ Example
This is an example console application which uses libicsneo to connect to an Intrepid Control Systems hardware device. It has both interactive and simple examples for sending and receiving CAN & CAN FD traffic.
## Building
This example shows how to use the C++ version of libicsneo with CMake. It will build libicsneo along with your project.
First, you need to clone the repository onto your local machine. Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples --recursive
```
Alternatively, if you cloned without the `--recursive flag`, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
### Windows using Visual Studio 2017+
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->CMake...`
3. Navigate to the `libicsneocpp-example` folder and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Choose the dropdown attached to the green play button (labelled "select startup item...") in the toolbar.
6. Select `libicsneocpp-simple-example.exe`
7. Press the green play button to compile and run the example.
### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make libicsneocpp-interactive-example` to build.
* Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Now run `sudo ./libicsneocpp-interactive-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6.
### macOS
Instructions coming soon&trade;
+438 -32
View File
@@ -1,52 +1,458 @@
/**
* libicsneo A2B example
*
* Example were made to be ran with RAD-A2B as main node on TDM4 16 bit channel size and one additional sub node (either an ADI board or an additional RAD-A2B).
* Follow the specific hardware instructions per example to ensure expected output. Be sure to configure the A2B network before running these examples, especially
* ones which Tx or Rx audio.
*
* Options:
* -h, --help Display help message.
* -e, --example [EXAMPLE_NUM] Example to run.
* Example usage: ./libicsneocpp-a2b.exe --example 1
* Example usage: ./libicsneocpp-a2b.exe -h
*/
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include <fstream>
#include <icsneo/icsneocpp.h>
#include <icsneo/device/tree/rada2b/rada2bsettings.h>
#include <icsneo/communication/message/callback/streamoutput/a2bwavoutput.h>
#include <string>
#include <math.h>
#include "icsneo/icsneocpp.h"
std::string makeWAV() {
icsneo::WAVHeader header = icsneo::WAVHeader(1, 48000, 16);
std::vector<uint8_t> sineWAVSamples = {
0xFF, 0x3F, 0x81, 0x5A, 0xD9, 0x6E, 0xA2, 0x7B, 0xFF, 0x7F, 0xA2, 0x7B, 0xD9, 0x6E, 0x81, 0x5A,
0xFF, 0x3F, 0x20, 0x21, 0x00, 0x00, 0xE0, 0xDE, 0x01, 0xC0, 0x7F, 0xA5, 0x27, 0x91, 0x5E, 0x84,
0x01, 0x80, 0x5E, 0x84, 0x27, 0x91, 0x7F, 0xA5, 0x01, 0xC0, 0xE0, 0xDe, 0x00, 0x00, 0x20, 0x21
};
const std::string rada2bSerial = "Your RADA2B serial number.";
std::vector<uint8_t> sineWAV;
sineWAV.reserve(sineWAVSamples.size() + sizeof(header));
int main() {
std::cout << "Start example\n";
auto devices = icsneo::FindAllDevices();
sineWAV.insert(sineWAV.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header));
std::copy(sineWAVSamples.begin(), sineWAVSamples.end(), std::back_inserter(sineWAV));
std::shared_ptr<icsneo::Device> rada2b;
for(auto& device : devices) {
if(device->getSerial() == rada2bSerial) {
rada2b = device;
return std::string(sineWAV.begin(), sineWAV.end());
}
/**
* Example 0: TX
*/
void example0(const std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Transmitting a sine tone..." << std::endl;
// Create sine tone in wav format
std::string wavString = makeWAV();
std::istringstream sineWAV(wavString);
double elapsedTime = 0.0;
// Create a IWAVStream object which represents a WAV data stream
// the IWAVStream object here is initialized with an outside std::ostream,
// so it holds a reference pointer to this stream.
icsneo::IWAVStream wavStream(sineWAV);
// Create a channel map which maps each message channel to a channel in the input WAV file
icsneo::ChannelMap channelMap;
// Here we will just set every message channel to channel 0 in the WAV file
// We have 8 channels since this is TDM4 and we include both upstream and downstream
// see docs for specific message channel labeling information
for(uint8_t messageChannel = 0; messageChannel < 8; messageChannel++) {
channelMap[messageChannel] = 0;
}
// Play roughly 5 seconds of sine tone.
while(elapsedTime < 5.0) {
while(elapsedTime < 5.0) {
// If WAVStream is invalid (at EOF) break out of loop
if(!wavStream) {
break;
}
// Creates a new message with the maximum amount of allocated frames
auto msg = std::make_shared<icsneo::A2BMessage>(
icsneo::A2BMessage::TDMMode::TDM4, /* TDM mode of the message, we use TDM4 for this whole example*/
true /* true if we want 16 bit channels in the message, false for 32 bit. This should match the RAD-A2B device setting */
);
msg->txmsg = true;
msg->network = icsneo::Network(icsneo::Network::NetID::A2B2);
// Load the WAV audio data into the desired channel, break if we fail to load
if(!msg->loadAudioBuffer(wavStream, channelMap)) {
break;
}
// Transmit the message
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
break;
}
elapsedTime += (static_cast<double>(msg->getNumFrames()))*1.0/48000.0;
}
// Reset the WAV stream
wavStream.reset();
}
}
/**
* Example 1: RX
*/
void example1(const std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Receiving 5 seconds of audio data..." << std::endl;
// Add WAV output message callback
// Saves samples to "out.wav"
auto handler = rada2b->addMessageCallback(
std::make_shared<icsneo::A2BWAVOutput>(
"audio16bit.wav", /* output file name */
icsneo::ChannelMap( /** channel mapping which maps our output WAV channels to the message channels from incoming messages */
{ /* See docs for specific A2B channel indexing information */
{static_cast<uint8_t>(3u), static_cast<uint8_t>(0u)}, /* Map output WAV channel 3 to channel 0 downstream of the A2B network/A2BMessage */
{static_cast<uint8_t>(2u), static_cast<uint8_t>(1u)}, /* Map output WAV channel 2 to channel 0 upstream of the A2B network/A2BMessage */
{static_cast<uint8_t>(1u), static_cast<uint8_t>(2u)}, /* Map output WAV channel 1 to channel 1 downstream of the A2B network/A2BMessage */
{static_cast<uint8_t>(0u), static_cast<uint8_t>(3u)} /* Map output WAV channel 0 to channel 1 upstream of the A2B network/A2BMessage */
}
),
icsneo::PCMType::L16, /* store samples with 16 bit resolution*/
2u, /* Number of channels in the output WAV file */
48000 /* Sample rate of WAV file */
)
);
// Sleep this thread for 5 seconds, message callback still runs
std::this_thread::sleep_for(std::chrono::seconds(5));
// Make sure you send 16 bit audio data on the above message channels in the channel map
// to the RAD-A2B main node through a microphone or a different modem.
// You can configure the message channels by changing the stream config in the A2B schematic
// Remove callback
rada2b->removeMessageCallback(handler);
}
/**
* Example 2: RAD-A2B settings
*/
void example2(const std::shared_ptr<icsneo::Device>& rada2b) {
uint8_t numChannels;
{
// Get device settings
auto* settings = rada2b->settings.get();
auto* rada2bSettings = static_cast<icsneo::RADA2BSettings*>(settings);
// Check if monitor mode is enabled
auto type = rada2bSettings->getNodeType(icsneo::RADA2BSettings::RADA2BDevice::Monitor);
if(type == icsneo::RADA2BSettings::NodeType::Monitor) {
std::cout << "Device is in monitor mode" << std::endl;
}
else {
std::cout << "Device is not in monitor mode" << std::endl;
}
// Get current tdm mode
numChannels = rada2bSettings->getNumChannels(icsneo::RADA2BSettings::RADA2BDevice::Node);
std::cout << "Current num channels: " << static_cast<uint32_t>(numChannels) << std::endl;
// Set node type to master node.
rada2bSettings->setNodeType(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::NodeType::Master);
// Set TDM mode to TDM8
rada2bSettings->setTDMMode(icsneo::RADA2BSettings::RADA2BDevice::Node, icsneo::RADA2BSettings::TDMMode::TDM4);
// Apply local settings to device
rada2bSettings->apply();
}
}
/**
* Example 3: Packaging and transmitting sine tone using A2BMessage API
*/
void example3(const std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Transmitting a 1000 hz sine tone." << std::endl;
float deltaTime = static_cast<float>(1.0/48000.0);
float elapsedTime = 0.0;
float twoPI = static_cast<float>(2.0*atan(1.0)*4.0);
float frequency = 1000;
float amplitude = static_cast<float>((1 << 15) - 1);
size_t tdm = 4;
size_t bytesPerSample = 2;
size_t numFrames = 2048 / (2 * tdm * bytesPerSample);
// Play for roughly 5 seconds
while(elapsedTime < 5.0) {
// Allocate message
std::shared_ptr<icsneo::A2BMessage> a2bmsgPtr = std::make_shared<icsneo::A2BMessage>(numFrames, icsneo::A2BMessage::TDMMode::TDM4, true);
icsneo::A2BMessage& a2bmsg = *a2bmsgPtr.get();
a2bmsg.network = icsneo::Network(icsneo::Network::NetID::A2B2);
a2bmsg.txmsg = true;
for(size_t frame = 0; frame < a2bmsg.getNumFrames(); frame++) {
// Sine tone sample, amplitude 1000, frequency 1000 hz
float contSample = amplitude*sin(twoPI*frequency*elapsedTime);
icsneo::PCMSample sample = static_cast<icsneo::PCMSample>(contSample);
// Send this sine wave sample downstream on channels 0, 1, and 2
a2bmsg.setChannelSample(
icsneo::A2BMessage::Direction::Downstream,
0,
frame,
sample,
icsneo::PCMType::L16
);
a2bmsg.setChannelSample(
icsneo::A2BMessage::Direction::Downstream,
1,
frame,
sample,
icsneo::PCMType::L16
);
a2bmsg.setChannelSample(
icsneo::A2BMessage::Direction::Downstream,
2,
frame,
sample,
icsneo::PCMType::L16
);
elapsedTime+=deltaTime;
}
// Transmit message to device
if(!rada2b->transmit(a2bmsgPtr)) {
std::cout << "Failed to transmit." << std::endl;
}
}
rada2b->open();
rada2b->goOnline();
std::cout << rada2b->describe() << "\n";
auto handler1 = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("examples/cpp/a2b/src/out.wav")); // Starts writing A2B PCM data to out.wav
}
auto handler2 = rada2b->addMessageCallback(
std::make_shared<icsneo::MessageCallback>(
[](std::shared_ptr<icsneo::Message> message) {
std::cout << "Got in callback " << std::endl;
if(message->type == icsneo::Message::Type::Frame) {
std::shared_ptr<icsneo::Frame> frame = std::static_pointer_cast<icsneo::Frame>(message);
/**
* 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);
if(frame->network.getType() == icsneo::Network::Type::A2B) {
std::shared_ptr<icsneo::A2BMessage> msg = std::static_pointer_cast<icsneo::A2BMessage>(frame);
std::cout << "Got A2B Message" << std::endl;
msg->network = icsneo::Network(icsneo::Network::NetID::I2C2);
msg->controlBytes.resize(1);
msg->controlBytes[0] = static_cast<uint8_t>(0x17u); // Register address for A2B INTTYPE
msg->dataBytes.resize(1, 0);
msg->direction = icsneo::I2CMessage::Direction::Read;
msg->deviceMode = icsneo::I2CMessage::DeviceMode::Controller;
msg->address = static_cast<uint16_t>(0x68); // A2B master node address.
msg->isTXMsg = true;
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::MessageCallback>(
[] (std::shared_ptr<icsneo::Message> newMsg) {
if(newMsg->type == icsneo::Message::Type::Frame) {
const auto& frame = std::dynamic_pointer_cast<icsneo::Frame>(newMsg);
if(frame && frame->network.getNetID() == icsneo::Network::NetID::I2C2) {
const auto& i2cMessage = std::dynamic_pointer_cast<icsneo::I2CMessage>(frame);
if(!i2cMessage) {
return;
}
if(i2cMessage->controlBytes.size() == 1 && i2cMessage->direction == icsneo::I2CMessage::Direction::Read) {
if(i2cMessage->controlBytes[0] == 0x17u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "Current A2B bus status INTTYPE code: " << static_cast<int>(i2cMessage->dataBytes[0]) << '\n';
}
} else if(i2cMessage->controlBytes[0] == 0x03u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_PRODUCT register: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
} else if(i2cMessage->controlBytes[0] == 0x02u) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_VENDOR register: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
} else if(i2cMessage->controlBytes[0] == 0x1C) {
if(i2cMessage->dataBytes.size() == 1) {
std::cout << "A2B_INTMSK1 register value: " << static_cast<int>(i2cMessage->dataBytes[0]) << std::endl;
}
}
}
}
}));
}
}
, msgFilter));
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
msg->controlBytes[0] = 0x03; // Address for A2B_PRODUCT register
std::this_thread::sleep_for(std::chrono::seconds(5)); // captures 5 seconds of A2B data.
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
rada2b->removeMessageCallback(handler1);
rada2b->removeMessageCallback(handler2);
msg->controlBytes[0] = 0x02; // Address for A2B_VENDOR register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
std::cout << "End A2B example\n";
msg->controlBytes[0] = 0x1C ; // Address for A2B_INTMSK1 register
msg->dataBytes[0] = 0x11;
msg->direction = icsneo::I2CMessage::Direction::Write;
// Write register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit" << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
msg->direction = icsneo::I2CMessage::Direction::Read;
// Read register
if(!rada2b->transmit(msg)) {
std::cout << "Failed to transmit." << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
rada2b->removeMessageCallback(handler);
}
/**
* Example 5: Reading A2B sequence chart .puml file
*/
void example5(const std::shared_ptr<icsneo::Device>& rada2b) {
// The A2B sequence chart is located at binary index 0
constexpr uint16_t a2bSequenceChartIndex = 0;
// Create a ostream object to capture sequence chart data
std::ofstream a2bSequenceChart("a2b_sequence_chart.puml", std::ios::out | std::ios::binary);
if(!rada2b->readBinaryFile(a2bSequenceChart, a2bSequenceChartIndex)) {
std::cout << "Failed to read A2B sequence chart" << std::endl;
}
}
void displayUsage() {
std::cout << "libicsneo A2B example" << std::endl;
std::cout << "Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << "-h, --help\tDisplay help message." << std::endl;
std::cout << "-e, --example [EXAMPLE_NUM]\tExample to run." << std::endl;
std::cout << "Example usage: ./libicsneocpp-a2b.exe --example 1" << std::endl;
std::cout << "Example usage: ./libicsneocpp-a2b.exe -h" << std::endl;
std::cout << std::endl;
std::cout << "Example options:" << std::endl;
std::cout << "0\ttx" << std::endl;
std::cout << "1\trx" << std::endl;
std::cout << "2\tSet RAD-A2B settings" << std::endl;
std::cout << "3\tPackaging and transmitting sine wav using A2BMessage API" << std::endl;
std::cout << "4\tRead/write I2C registers on A2B board" << std::endl;
std::cout << "5\tReading out A2B sequence chart .puml file" << std::endl;
}
int main(int argc, char** argv) {
std::vector<std::string> arguments(argv, argv + argc);
if(argc > 4 || argc == 1) {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
if(std::any_of(arguments.begin(), arguments.end(), [](const std::string& arg) { return arg == "-h" || arg == "--help"; })) {
displayUsage();
return EXIT_SUCCESS;
}
if(arguments[1] != "-e" && arguments[1] != "--example") {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
int option = atoi(arguments[2].c_str());
if(option < 0 || option > 5) {
std::cerr << "Invalid usage." << std::endl;
displayUsage();
return EXIT_FAILURE;
}
std::cout << icsneo::GetVersion() << std::endl;
const auto& devices = icsneo::FindAllDevices();
auto it = std::find_if(devices.begin(), devices.end(), [&](const auto& dev) {
const auto& txNetworks = dev->getSupportedTXNetworks();
const auto& rxNetworks = dev->getSupportedRXNetworks();
if(std::none_of(txNetworks.begin(), txNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
return false;
}
if(std::none_of(rxNetworks.begin(), rxNetworks.end(), [](const icsneo::Network& net) { return net.getType() == icsneo::Network::Type::A2B; })) {
return false;
}
return true;
}
);
if(it == devices.end()) {
std::cerr << "Could not find RAD-A2B." << std::endl;
return EXIT_FAILURE;
}
std::shared_ptr<icsneo::Device> rada2b = *it;
if(!rada2b->open()) {
std::cout << "Failed to open RAD-A2B." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Opened RAD-A2B." << std::endl;
}
if(!rada2b->goOnline()) {
std::cout << "Failed to go online with RAD-A2B." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "RAD-A2B online." << std::endl;
}
switch(option) {
case 0:
example0(rada2b);
break;
case 1:
example1(rada2b);
break;
case 2:
example2(rada2b);
break;
case 3:
example3(rada2b);
break;
case 4:
example4(rada2b);
break;
case 5:
example5(rada2b);
break;
default:
break;
}
rada2b->goOffline();
rada2b->close();
return 0;
}
return EXIT_SUCCESS;
}
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-app-error src/AppErrorExample.cpp)
target_link_libraries(libicsneocpp-app-error icsneocpp)
@@ -0,0 +1,85 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/message/message.h"
/*
* App errors are responses from the device indicating internal runtime errors
* NOTE: To trigger the app error in this example, disable the HSCAN network on the device
* (e.g. with neoVI Explorer)
*/
int main() {
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices();
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl << std::endl;
// Create an app error message filter, including "internal" messages
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::AppError);
filter->includeInternalInAny = true;
// ...and register a callback with it.
// Add your error handling here
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>(filter, [](std::shared_ptr<icsneo::Message> message) {
auto msg = std::static_pointer_cast<icsneo::AppErrorMessage>(message);
if(icsneo::Network::NetID::RED_App_Error == msg->network.getNetID()) {
std::cout << std::endl << "App error reported:" << std::endl;
std::cout << msg->getAppErrorString() << std::endl << std::endl;
}
}));
std::cout << "Going online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// Prepare a CAN message
std::cout << std::endl << "Transmitting a CAN frame... ";
auto txMessage = std::make_shared<icsneo::CANMessage>();
txMessage->network = icsneo::Network::NetID::HSCAN;
txMessage->arbid = 0x22;
txMessage->data.insert(txMessage->data.end(), {0xaa, 0xbb, 0xcc});
// The DLC will come from the length of the data vector
txMessage->isExtended = false;
txMessage->isCANFD = false;
// Transmit a CAN message on HSCAN, even though HSCAN is disabled on the device!
// Expect to see an app error caught in the callback defined above
ret = device->transmit(txMessage);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Go offline, stop sending and receiving traffic
device->removeMessageCallback(handler);
std::cout << "Going offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Disconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
}
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-coremini src/coremini.cpp)
target_link_libraries(libicsneocpp-coremini icsneocpp)
+81
View File
@@ -0,0 +1,81 @@
// Usage:
// ./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] [FLASH | SD]
#include <iostream>
#include <fstream>
#include <icsneo/icsneocpp.h>
void displayUsage() {
std::cout << "Usage:\n";
std::cout << "./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH] [FLASH | SD]\n";
}
int main(int argc, char** argv) {
std::vector<std::string> arguments(argv, argv + argc);
if(arguments.size() != 4) {
displayUsage();
return EXIT_FAILURE;
}
std::cout << icsneo::GetVersion() << std::endl;
const auto& devices = icsneo::FindAllDevices();
auto it = std::find_if(
devices.begin(),
devices.end(),
[&arguments](const auto &d)
{ return d->getSerial() == arguments[1]; });
if(it == devices.end()) {
std::cout << "Failed to find device." << std::endl;
return EXIT_FAILURE;
}
std::shared_ptr<icsneo::Device> device = *it;
if(!device->open()) {
std::cout << "Failed to open device." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
if(!device->goOnline()) {
std::cout << "Failed to go online." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
std::string memTypeString = arguments[3];
icsneo::Disk::MemoryType type;
if(memTypeString == "FLASH") {
type = icsneo::Disk::MemoryType::Flash;
} else if(memTypeString == "SD") {
type = icsneo::Disk::MemoryType::SD;
} else {
std::cout << "Incorrect memory type option" << std::endl;
displayUsage();
return EXIT_FAILURE;
}
std::ifstream coreminiFile(arguments[2], std::ios::binary);
if (!device->uploadCoremini(coreminiFile, type)) {
std::cout << "Failed to upload coremini" << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
}
if (!device->startScript(type)) {
std::cout << "Failed to start script" << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
}
device->goOffline();
device->close();
return 0;
}
-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)
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-lin src/LINExample.cpp)
target_link_libraries(libicsneocpp-lin icsneocpp)
+157
View File
@@ -0,0 +1,157 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/linmessage.h"
/* Note: This example requires LIN 1 and LIN 2 channels to be connected on the device */
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl << std::endl;
int64_t baud = 19200;
std::cout << "Enable LIN commander resistor... ";
ret = device->settings->setCommanderResistorFor(icsneo::Network::NetID::LIN, true);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Disable LIN2 commander resistor... ";
ret = device->settings->setCommanderResistorFor(icsneo::Network::NetID::LIN2, false);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN to operate at " << baud << "bit/s... ";
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::LIN, baud);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN2 to operate at " << baud << "bit/s... ";
ret = device->settings->setBaudrateFor(icsneo::Network::NetID::LIN2, baud);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN mode to NORMAL... ";
ret = device->settings->setLINModeFor(icsneo::Network::NetID::LIN, NORMAL_MODE);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Setting LIN2 mode to NORMAL... ";
ret = device->settings->setLINModeFor(icsneo::Network::NetID::LIN2, NORMAL_MODE);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Applying settings... ";
ret = device->settings->apply();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Getting LIN Baudrate... ";
int64_t readBaud = device->settings->getBaudrateFor(icsneo::Network::NetID::LIN);
if(readBaud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (readBaud) << "bit/s" << std::endl;
std::cout << "Getting LIN2 Baudrate... ";
readBaud = device->settings->getBaudrateFor(icsneo::Network::NetID::LIN2);
if(readBaud < 0)
std::cout << "FAIL" << std::endl;
else
std::cout << "OK, " << (readBaud) << "bit/s" << std::endl << std::endl;
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "Going online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// A real application would just check the result of icsneo_goOnline() rather than calling this
// This function is intended to be called later on if needed
std::cout << "Checking online status... ";
ret = device->isOnline();
if(!ret) {
std::cout << "FAIL\n" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Network::Type::LIN == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::LINMessage>(message);
std::cout << msg->network << " RX frame | ID: 0x" << std::hex << static_cast<int>(msg->ID) << " | ";
std::cout << "Protected ID: 0x" << static_cast<int>(msg->protectedID) << "\n" << "Data: ";
for(uint8_t& each : msg->data) {
std::cout << "0x" << static_cast<int>(each) << " ";
}
std::cout << "\nChecksum type: " << (msg->isEnhancedChecksum ? "Enhanced" : "Classic");
std::cout << "\nChecksum: 0x" << static_cast<int>(msg->checksum) << "\n";
std::cout << "Is checksum valid: " << ((!msg->errFlags.ErrChecksumMatch) ? "yes" : "no") << "\n\n";
}
}
}));
// We can transmit messages
std::cout << "Transmitting a LIN responder data frame... ";
auto lin_r = std::make_shared<icsneo::LINMessage>();
lin_r->network = icsneo::Network::NetID::LIN2;
lin_r->ID = 0x11;
lin_r->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER;
lin_r->data = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44};
ret = device->transmit(lin_r); // This will return false if the device does not support LIN
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Transmitting a LIN commander header... ";
auto lin_c = std::make_shared<icsneo::LINMessage>();
lin_c->network = icsneo::Network::NetID::LIN;
lin_c->ID = 0x11;
lin_c->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY;
ret = device->transmit(lin_c);
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Transmitting a LIN commander frame with responder data... ";
auto lin_d = std::make_shared<icsneo::LINMessage>();
lin_d->network = icsneo::Network::NetID::LIN;
lin_d->ID = 0x22;
lin_d->isEnhancedChecksum = true;
lin_d->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG;
lin_d->data = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd};
ret = device->transmit(lin_d);
std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Go offline, stop sending and receiving traffic
device->removeMessageCallback(handler);
std::cout << "Going offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Disconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
}
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-livedata src/LiveDataExample.cpp)
target_link_libraries(libicsneocpp-livedata icsneocpp)
@@ -0,0 +1,91 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/livedatamessage.h"
#include "icsneo/communication/livedata.h"
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto& device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl;
// Create a subscription message for the GPS signals
std::cout << "\tSending a live data subscribe command... ";
auto msg = std::make_shared<icsneo::LiveDataCommandMessage>();
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LATITUDE);
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_LONGITUDE);
msg->appendSignalArg(icsneo::LiveDataValueType::GPS_ACCURACY);
msg->cmd = icsneo::LiveDataCommand::SUBSCRIBE;
msg->handle = icsneo::LiveDataUtil::getNewHandle();
msg->updatePeriod = std::chrono::milliseconds(100);
msg->expirationTime = std::chrono::milliseconds(0);
// Transmit the subscription message
ret = device->subscribeLiveData(msg);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// Register a handler that uses the data after it arrives every ~100ms
std::cout << "\tStreaming messages for 3 seconds... " << std::endl << std::endl;
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::LiveData);
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>(filter, [&msg](std::shared_ptr<icsneo::Message> message) {
auto ldMsg = std::dynamic_pointer_cast<icsneo::LiveDataMessage>(message);
switch(ldMsg->cmd) {
case icsneo::LiveDataCommand::STATUS: {
auto msg2 = std::dynamic_pointer_cast<icsneo::LiveDataStatusMessage>(message);
std::cout << "[Handle] " << ldMsg->handle << std::endl;
std::cout << "[Requested Command] " << msg2->requestedCommand << std::endl;
std::cout << "[Status] " << msg2->status << std::endl << std::endl;
break;
}
case icsneo::LiveDataCommand::RESPONSE: {
auto valueMsg = std::dynamic_pointer_cast<icsneo::LiveDataValueMessage>(message);
if((valueMsg->handle == msg->handle) && (valueMsg->values.size() == msg->args.size()))
{
std::cout << "[Handle] " << msg->handle << std::endl;
std::cout << "[Values] " << valueMsg->numArgs << std::endl;
for(uint32_t i = 0; i < valueMsg->numArgs; ++i) {
std::cout << "[" << msg->args[i]->valueType << "] ";
auto scaledValue = icsneo::LiveDataUtil::liveDataValueToDouble(*valueMsg->values[i]);
std::cout << scaledValue << std::endl;
}
std::cout << std::endl;
}
break;
}
default: // Ignoring other commands
break;
}
}));
// Run handler for three seconds to observe the signal data
std::this_thread::sleep_for(std::chrono::seconds(3));
// Unsubscribe from the GPS signals and run handler for one more second
// Unsubscription only requires a valid in-use handle, in this case from our previous subscription
ret = device->unsubscribeLiveData(msg->handle);
// The handler should no longer print values
std::this_thread::sleep_for(std::chrono::seconds(1));
device->removeMessageCallback(handler);
std::cout << "\tDisconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
}
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
add_executable(libicsneocpp-mdio src/MDIOExample.cpp)
target_link_libraries(libicsneocpp-mdio icsneocpp)
+221
View File
@@ -0,0 +1,221 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/mdiomessage.h"
/* Note: This example requires MDIO 1 channels to be connected on the device, and uses RADGalaxy or RADStar2 */
char getCharInput(std::vector<char> allowed)
{
bool found = false;
std::string input;
while (!found)
{
std::cin >> input;
if (input.length() == 1)
{
for (char compare : allowed)
{
if (compare == input.c_str()[0])
{
found = true;
break;
}
}
}
if (!found)
{
std::cout << "Input did not match expected options. Please try again." << std::endl;
std::cout << "<X or x to quit>" << std::endl;
}
}
return input.c_str()[0];
}
int main()
{
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices(); // This is type std::vector<std::shared_ptr<icsneo::Device>>
// You now hold the shared_ptrs for these devices, you are considered to "own" these devices from a memory perspective
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for (auto &device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for (auto &device : devices)
{
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if (!ret)
{ // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl
<< std::endl;
continue;
}
std::cout << "OK" << std::endl;
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "\tGoing online... ";
ret = device->goOnline();
if (!ret)
{
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// A real application would just check the result of icsneo_goOnline() rather than calling this
// This function is intended to be called later on if needed
std::cout << "\tChecking online status... ";
ret = device->isOnline();
if (!ret)
{
std::cout << "FAIL\n"
<< std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message)
{
if(icsneo::Message::Type::Frame == message->type) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(icsneo::Network::Type::MDIO == frame->network.getType()) {
auto msg = std::static_pointer_cast<icsneo::MDIOMessage>(message);
std::cout << msg->network << " " << ((msg->isTXMsg)? "TX" : "RX") << " frame\n";
std::cout << "Clause: " << ((msg->clause == icsneo::MDIOMessage::Clause::Clause22) ? "22" : "45") << "\n";
std::cout << "Direction: " << ((msg->direction == icsneo::MDIOMessage::Direction::Read) ? "Read" : "Write") << "\n";
std::cout << std::hex << "PHY Address: 0x" << static_cast<int>(msg->phyAddress) << "\n";
if(msg->clause == icsneo::MDIOMessage::Clause::Clause45)
std::cout << std::hex << "Dev Address: 0x" << static_cast<int>(msg->devAddress) << "\n";
std::cout << std::hex << "Reg Address: 0x" << static_cast<int>(msg->regAddress) << "\n";
std::cout << "Data: \n";
for(uint8_t& each : msg->data) {
std::cout << std::hex << "0x" << static_cast<int>(each) << " ";
}
std::cout << "\n";
}
} }));
/*
* BCM89810 on RADGalaxy/RADGigastar
*/
// We can transmit messages to read the PHY ID of BCM89810 PHY
std::cout << "\tTransmitting a MDIO request to read ID on BCM89810...\n";
auto mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x02u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to write to arbitrary register
std::cout << "\tTransmitting a MDIO request to write register on BCM89810...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x1Bu;
mdio_r->data = {0xAA, 0xAF};
mdio_r->direction = icsneo::MDIOMessage::Direction::Write;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to read back to arbitrary register
std::cout << "\tTransmitting a MDIO request to read register on BCM89810...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x00u;
mdio_r->regAddress = 0x1Bu;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause22;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
/*
* 88Q2112 on RADGigastar, RADSupermoon, RADMoon2
*/
// We can transmit messages to read the PHY ID of BCM89810 PHY
std::cout << "\tTransmitting a MDIO request to read ID on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0002u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to write to arbitrary register
std::cout << "\tTransmitting a MDIO request to write register on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0902u;
mdio_r->data = {0xA3, 0x02};
mdio_r->direction = icsneo::MDIOMessage::Direction::Write;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
// We can transmit messages to read back to arbitrary register
std::cout << "\tTransmitting a MDIO request to read register on 88Q2112...\n";
mdio_r = std::make_shared<icsneo::MDIOMessage>();
mdio_r->network = icsneo::Network::NetID::MDIO1;
mdio_r->phyAddress = 0x06u;
mdio_r->devAddress = 0x01u;
mdio_r->regAddress = 0x0902u;
mdio_r->direction = icsneo::MDIOMessage::Direction::Read;
mdio_r->clause = icsneo::MDIOMessage::Clause::Clause45;
ret = device->transmit(mdio_r); // This will return false if the device does not support MDIO
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "<X or x to quit>\n\n";
// Go offline, stop sending and receiving traffic
auto shutdown = [&]()
{
device->removeMessageCallback(handler);
std::cout << "\tGoing offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "\tDisconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
};
while (true)
{
char input = getCharInput(std::vector<char>{'X', 'x'});
switch (input)
{
case 'X':
case 'x':
shutdown();
printf("Exiting program\n");
return 0;
default:
break;
}
}
}
return 0;
}
-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)
+28 -2
View File
@@ -5,6 +5,10 @@
#include "icsneo/icsneocpp.h"
#ifdef _MSC_VER
#pragma warning(disable : 4996) // STL time functions
#endif
int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
@@ -96,9 +100,31 @@ int main() {
std::cout << "\tSetting settings permanently... ";
ret = device->settings->apply();
std::cout << (ret ? "OK\n\n" : "FAIL\n\n");
const auto getRTC = [&]() {
std::cout << "\tGetting RTC... ";
const auto rtc = device->getRTC();
if(!rtc) {
std::cout << "FAIL" << std::endl;
return;
}
const auto time = std::chrono::system_clock::to_time_t(*rtc);
const auto timeInfo = std::gmtime(&time);
std::cout << "OK, " << std::put_time(timeInfo, "%Y-%m-%d %H:%M:%S") << std::endl;
};
// Set the real time clock on the device using the system clock
// First, let's see if we can get the time from the device (if it has an RTC)
getRTC();
// Now, set the time using the system's clock so that we can check it again to ensure it's set
std::cout << "\tSetting RTC to system_clock::now()... ";
std::cout << (device->setRTC(std::chrono::system_clock::now()) ? "OK" : "FAIL") << std::endl;
// Get the time again after setting
getRTC();
// The concept of going "online" tells the connected device to start listening, i.e. ACKing traffic and giving it to us
std::cout << "\tGoing online... ";
std::cout << "\n\tGoing online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
@@ -127,7 +153,7 @@ int main() {
// Keep in mind that 20k messages comes quickly at high bus loads!
// We can transmit messages
std::cout << "\tTransmitting an extended CAN FD frame... ";
std::cout << "\n\tTransmitting an extended CAN FD frame... ";
auto txMessage5 = std::make_shared<icsneo::CANMessage>();
txMessage5->network = icsneo::Network::NetID::HSCAN;
txMessage5->arbid = 0x1C5001C5;
+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;
}
-10
View File
@@ -3511,16 +3511,6 @@ SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_ISO_get() {
}
SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_ISOPIC_get() {
int jresult ;
int result;
result = (int)(10);
jresult = result;
return jresult;
}
SWIGEXPORT int SWIGSTDCALL CSharp_ICSNEO_NETID_MAIN51_get() {
int jresult ;
int result;
-1
View File
@@ -325,7 +325,6 @@ public class icsneocsharp {
public static readonly int ICSNEO_NETID_AUX = icsneocsharpPINVOKE.ICSNEO_NETID_AUX_get();
public static readonly int ICSNEO_NETID_J1850VPW = icsneocsharpPINVOKE.ICSNEO_NETID_J1850VPW_get();
public static readonly int ICSNEO_NETID_ISO = icsneocsharpPINVOKE.ICSNEO_NETID_ISO_get();
public static readonly int ICSNEO_NETID_ISOPIC = icsneocsharpPINVOKE.ICSNEO_NETID_ISOPIC_get();
public static readonly int ICSNEO_NETID_MAIN51 = icsneocsharpPINVOKE.ICSNEO_NETID_MAIN51_get();
public static readonly int ICSNEO_NETID_RED = icsneocsharpPINVOKE.ICSNEO_NETID_RED_get();
public static readonly int ICSNEO_NETID_SCI = icsneocsharpPINVOKE.ICSNEO_NETID_SCI_get();
-3
View File
@@ -1017,9 +1017,6 @@ class icsneocsharpPINVOKE {
[global::System.Runtime.InteropServices.DllImport("icsneocsharp.dll", EntryPoint="CSharp_ICSNEO_NETID_ISO_get")]
public static extern int ICSNEO_NETID_ISO_get();
[global::System.Runtime.InteropServices.DllImport("icsneocsharp.dll", EntryPoint="CSharp_ICSNEO_NETID_ISOPIC_get")]
public static extern int ICSNEO_NETID_ISOPIC_get();
[global::System.Runtime.InteropServices.DllImport("icsneocsharp.dll", EntryPoint="CSharp_ICSNEO_NETID_MAIN51_get")]
public static extern int ICSNEO_NETID_MAIN51_get();
-12
View File
@@ -5506,18 +5506,6 @@ SWIGEXPORT jint JNICALL Java_icsneojavaJNI_ICSNEO_1NETID_1ISO_1get(JNIEnv *jenv,
}
SWIGEXPORT jint JNICALL Java_icsneojavaJNI_ICSNEO_1NETID_1ISOPIC_1get(JNIEnv *jenv, jclass jcls) {
jint jresult = 0 ;
int result;
(void)jenv;
(void)jcls;
result = (int)(10);
jresult = (jint)result;
return jresult;
}
SWIGEXPORT jint JNICALL Java_icsneojavaJNI_ICSNEO_1NETID_1MAIN51_1get(JNIEnv *jenv, jclass jcls) {
jint jresult = 0 ;
int result;
@@ -20,7 +20,6 @@ public interface icsneojavaConstants {
public final static int ICSNEO_NETID_AUX = icsneojavaJNI.ICSNEO_NETID_AUX_get();
public final static int ICSNEO_NETID_J1850VPW = icsneojavaJNI.ICSNEO_NETID_J1850VPW_get();
public final static int ICSNEO_NETID_ISO = icsneojavaJNI.ICSNEO_NETID_ISO_get();
public final static int ICSNEO_NETID_ISOPIC = icsneojavaJNI.ICSNEO_NETID_ISOPIC_get();
public final static int ICSNEO_NETID_MAIN51 = icsneojavaJNI.ICSNEO_NETID_MAIN51_get();
public final static int ICSNEO_NETID_RED = icsneojavaJNI.ICSNEO_NETID_RED_get();
public final static int ICSNEO_NETID_SCI = icsneojavaJNI.ICSNEO_NETID_SCI_get();

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