Author SHA1 Message Date
Kyle Johannes aa77bbbc9e first iteration cleanup 2023-11-29 17:05:08 -05:00
Kyle Johannes 0b9491e5e3 android: first working device comm no root 2023-11-18 06:03:30 -05:00
Kyle Johannes ca630b4569 wip: native libusb can fetch product ID, serial, and android system FDs can be wrapped 2023-11-18 01:47:34 -05:00
Kyle Johannes b2f67f7ce1 wip: android driver 2023-11-14 14:09:56 -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
Yasser Yassine 78465e0f20 A2B: Add A2BMessage transmit support 2022-12-02 12:14:44 -05:00
Emily BrooksandKyle Schwarz ed1632c652 DiskWriteDriver: Remove use of atomic buffer 2022-12-02 00:09:49 +00:00
Joe Niksa c97db0f35f icsneolegacy: implemented icsneoGetLastAPIError() 2022-11-25 12:26:41 -05:00
Emily BrooksandKyle Schwarz c9d8a4f0a9 Device: Disk lock the script/wivi commands 2022-11-16 19:55:53 +00:00
Emily BrooksandKyle Schwarz 96fe8895fc Device: Add isEncrypted to script status 2022-11-16 19:55:53 +00:00
Kyle Schwarz e46755f120 Device: RED2: Add missing Ethernet2 2022-11-15 12:37:41 -05:00
Emily Brooks 0619c37a90 Decoder: Add break to end of Internal network switch case 2022-11-08 22:05:01 +00:00
David Rebbe de58b2d639 libicsneoc: Fixed access violation 2022-11-07 20:22:45 +00:00
David Rebbe 5ff1707c47 libicsneoc: Resolve DeviceCurrentlyClosed errors 2022-11-03 23:46:14 +00:00
Jonathan Schwartz 4c0d49f09a Device: Add a mutex against the diskLock to some commands that are on threads 2022-10-24 16:02:53 -04:00
Jonathan Schwartz b32f58da38 Device: Add new method to mark collections as uploaded in CM root directory 2022-10-24 16:02:48 -04:00
Kyle Schwarz 0101467154 Communication: Atomic sync messages
If waitForMessageSync() is called in two threads for the same message
the callback for both will be invoked with the first send.
2022-10-21 14:49:03 +00:00
Emily Brooks 17b3018499 Device: Continue script status loop if no response 2022-10-21 13:23:46 +00:00
Kyle Schwarz 0fc676e576 A2B: Resolve warnings 2022-10-20 19:38:34 -04:00
Yasser Yassine 7b2544864b A2B: Add initial WAV streaming support 2022-10-19 18:44:05 -04:00
Kyle Schwarz 7f22286838 Build: Only search for enabled drivers 2022-10-17 21:01:24 -04:00
Kyle Schwarz 643768fb6a Device: ValueCAN3: Drop ftdi.h include 2022-10-17 21:01:24 -04:00
Kyle Johannes 9817887523 I2C: Network support 2022-10-18 00:12:16 +00:00
Jonathan Schwartz 13ec2bca98 Merge remote-tracking branch 'origin/master' into js-add-fire3-to-finder 2022-10-12 10:55:56 -04:00
Jonathan Schwartz 711976cce0 DeviceFinder: Add serial match for FIRE3 2022-10-12 10:53:19 -04:00
Emily Brooks e02203a974 Device: Creates script status thread, callback for each variable 2022-10-11 14:48:39 -04:00
Emily Brooks 5b14ec7246 Device: Add start/stop/clear script, script status, and preload coremini commands 2022-10-11 14:48:39 -04:00
Yasser Yassine 3b80746fb8 A2B: Add A2B message initial support 2022-10-11 11:20:04 -04:00
Emily Brooks 3b9a31ef51 RAD-A2B: Add netID to string support 2022-10-05 13:50:00 -04:00
Emily Brooks 124bd3b310 WiVICommandPacket: Remove length check 2022-10-05 14:08:39 +00:00
Kyle Schwarz 9871430288 RAD-A2B: Initial support 2022-09-15 11:13:45 -04:00
Kyle Schwarz 2e296dc8d3 ConcurrentQueue: Update to HEAD 2022-09-09 15:46:46 -04:00
Kyle Schwarz b35fab754c Device: ValueCAN3: Fix PID 2022-08-02 14:33:43 -04:00
Kyle Schwarz 9ef01e2d3d Optional: nonstd to std 2022-07-22 01:27:39 -04:00
Kyle Schwarz 1bb33156f7 Device: Use lambda instead of std::bind 2022-07-22 02:47:10 +00:00
Kyle Schwarz a0f73755e8 Build: Bump required C++ to 17 2022-07-21 16:22:52 -04:00
Kyle Schwarz ab54697745 Device: Retain Device objects between FindAll() calls
FindAll() now retains a list of Devices that have already been created
and returns their existing object instead of a new one.
2022-07-15 16:03:54 -04:00
Kyle Schwarz 0d0b7f00bd Merge branch 'master' into v0.3.0-dev 2022-07-15 13:02:26 -04:00
Kyle Schwarz 1a28fbf5ad ThirdParty: FatFs: Enable PIC
Fixes linking with GCC by adding -fPIC
2022-07-15 12:34:07 -04:00
Emily Brooks b50706846c WiVICommandPacket: Add missing cstring header 2022-06-08 16:49:39 -04:00
Paul Hollinsky d6d34eb7bc WiVICommandPacket: Only disable warnings for MSVC
This addresses a warning in GCC and Clang
2022-06-07 13:48:51 +00:00
Paul Hollinsky a928a1d879 Device: Implement Wireless neoVI Stack
This reverts commit cb47065a47.
2022-06-07 13:48:51 +00:00
Paul Hollinsky de3d8bf870 RAD-Jupiter: Add initial support 2022-05-28 14:42:09 -04:00
Paul Hollinsky 407ccccedd Drivers: Ensure serial numbers are uppercase
This prevents mismatches if the device sends a lowercase
serial number, which may happen due to an oversight in
production.
2022-05-28 14:41:41 -04:00
Paul Hollinsky 00024990e9 WiVICommandPacket: Resolve GCC build error 2022-04-26 13:54:03 -04:00
Paul Hollinsky cb47065a47 WiVICommandPacket: Temporarily remove GetAll 2022-04-26 12:58:54 -04:00
Paul Hollinsky 2b819065ad WiVICommandPacket: Disable MSVC struct warnings 2022-04-26 12:48:17 -04:00
Paul Hollinsky 58700afc73 Lifetime: Allow checking for empty lifetimes 2022-04-26 12:48:17 -04:00
Paul Hollinsky 55d7d5bf17 Device: Implement allowSleep for Wireless neoVI support 2022-04-26 12:48:17 -04:00
Jonathan SchwartzandPaul Hollinsky c08c0dd893 Disk: Allow full timeout for each disk read attempt 2022-04-22 17:30:53 +00:00
Jonathan SchwartzandPaul Hollinsky 44ad06d4bb Device: Add mutex for disk info requests to temporarily work around WiVIClient timeout issues 2022-04-22 17:30:53 +00:00
Jonathan SchwartzandPaul Hollinsky c2de1dfdf1 Disk: ExtExtractorDiskReadDriver: Increase timeout for individual read attempts 2022-04-22 17:30:53 +00:00
Paul Hollinsky a2cfc50b7c Message Filter: Fix filtering on NetID or NetType if message doesn't have one 2022-04-15 21:35:40 -04:00
Paul Hollinsky c398afc4e3 Extended Commands: Fix struct packing 2022-04-15 21:35:40 -04:00
Paul Hollinsky 8d495aa916 neoVI FIRE 3: Initial support 2022-04-15 21:35:40 -04:00
Paul Hollinsky 91abf378f8 FirmIO: Use uintptr_t for 64-bit compatibility 2022-04-15 21:35:40 -04:00
Paul Hollinsky 2e3b738e76 Extended Commands: Decode generic responses 2022-04-15 21:35:40 -04:00
Paul Hollinsky 103f938d69 Disk: ReadDriver: Add unified cache
Previously, we had to copy an entire block out of the
old cache every time we wanted to read even a single
byte from it.

This ended up being a fairly significant performance
issue, in addition to the fact that the caching code
was duplicated.
2022-04-14 18:26:44 -04:00
Paul Hollinsky f1d9be1a81 Build: Don't add libusb include until after libftdi
This fixes GH-46 and a regression caused by 82954d9 where the project
would fail to build until configured a second time on Unix-y platforms.

LibFTDI adds libusb, so we need to make sure that's included first.

Windows does not use libusb so it does not need the include directory.
2022-02-24 16:07:56 -05:00
Paul Hollinsky 2c282fe396 Tests: Resolve MSVC x86 warnings 2022-02-22 10:46:30 -05:00
Paul Hollinsky 95de93aa84 Examples: C Interactive: Resolve warnings 2022-02-22 10:46:27 -05:00
Paul Hollinsky 733312628c CI: Also set warnings as errors in C code 2022-02-22 10:37:37 -05:00
Paul Hollinsky b177617940 CI: Add Windows 32-bit 2022-02-22 10:31:46 -05:00
Paul Hollinsky 28d3730375 Windows: Disable unaligned on x86
Closes GH-44
2022-02-22 10:24:13 -05:00
Paul Hollinsky c451fc2b7c CI: Warnings are errors 2022-02-21 21:23:58 -05:00
Paul Hollinsky c620b2ee8b MSVC: Resolve warnings 2022-02-21 21:16:15 -05:00
Paul Hollinsky 111d377d4a Legacy: Fix icsneoGetVnetSimpleNetid
The NetID is written to the out* parameter, not returned.
2022-02-21 21:16:12 -05:00
Paul Hollinsky c3469f5fb6 Legacy: Fix icsneoGetNetidforSlaveVNETs
The NetID is written to the out* parameter, not returned.
2022-02-21 21:16:07 -05:00
Paul Hollinsky 0219eccc94 Legacy: Formatting 2022-02-21 21:14:31 -05:00
Paul Hollinsky e22f70a35a Legacy: Correct Ethernet message translation
* Fixes NetIDs over 255
* Fixes data lengths over 255
* Avoids writing unexpected messages to client
2022-02-21 21:14:31 -05:00
Paul Hollinsky b019c20ad0 Legacy: Prevent incorrect access to NetID maps 2022-02-21 21:14:31 -05:00
Paul Hollinsky 6fa469de5f Tests: Spaces to Tabs 2022-02-21 20:26:57 -05:00
Paul Hollinsky be219288dc CI: Add Windows 2022-02-21 20:13:28 -05:00
Paul Hollinsky d35653e3d0 Windows: PCAP: Fix WinPCAP loading
Used when LIBICSNEO_NPCAP_INCLUDE_DIR is not defined
2022-02-14 19:37:41 -05:00
Paul Hollinsky 0ce7326b83 Windows: PCAP: Fix a race which could cause transmit delays
If you had a chain of packets being sent all at once, the latter
section of packets could be delayed, theoretically infinitely.

If queue1 was filled and enqueued for transmit, then queue2
had packets enqueued in it while queue1 was still transmitting,
we'd try to fill queue2 further rather than waiting for queue1's
transmit to finish.

However, in that case, we wouldn't check if we could transmit
queue2 again until the next packet. If the user application
was waiting for the response from something in queue2
before pushing more packets, it could hang indefinitely.

This also fixes a subtle bug where hitting the "not safe to try
to fit any more packets in this queue" limit would cause a
packet to drop, as it would be dequeued and then tossed.

Closes GH-42
2022-02-14 19:37:41 -05:00
Paul Hollinsky 714db03a05 CMake: Set CXX_STANDARD to C++11 if not set elsewhere
We need at least C++11.

If we're statically compiling into an application, we want to be using the same
CXX_STANDARD as it for ABI compatibility (particularly with icsneo::optional),
hence having the check around it.
2021-11-16 19:49:08 -05:00
Paul Hollinsky d097fb9a74 CANPacket: Remove unused byte from datastream 2021-11-16 19:48:55 -05:00
Paul Hollinsky 9d5bad94d4 Encoder: Add 1 to host-to-device long format packets
Vehicle Spy 3 does this, it's a long-standing firmware idiosyncrasy.

Due to the way the device handles packets, this didn't cause a loss of
communication, only a "host to device byte" app error under certain
circumstances.
2021-11-16 19:35:20 -05:00
Paul Hollinsky 181223375a ValueCAN 4 Industrial: Disable 16-bit alignment over Ethernet
This is a continuation of a1a544045b
2021-11-16 19:27:38 -05:00
Paul Hollinsky 51626b9e63 POSIX: PCAP: Use EthernetPacketizer 2021-11-16 18:00:04 -05:00
Paul Hollinsky a1a544045b Ethernet Comm Devices: Disable unnecessary align16bit 2021-11-16 18:00:04 -05:00
Paul Hollinsky 719dbcefc8 EthernetPacketizer: Coalesce small PC-to-device packets 2021-11-16 18:00:04 -05:00
Kyle Schwarz c5314884ce Build: Associate pcap include dir with icsneocpp
If an external project links to the icsneocpp target and pcap has
been installed in a non-standard location, the build will fail to find
pcap.h because PCAP_INCLUDE_DIR is not associated with the
target.

Closes #58
2021-09-20 21:38:34 -04:00
Kyle Schwarz 82954d9190 Build: Associate libusb include dir with icsneocpp
If an external project links to the icsneocpp target and libusb has
been installed in a non-standard location, the build will fail to find
libusb.h because LIBUSB_INCLUDE_DIR is not associated with the
target.

Closes #57
2021-09-20 21:31:13 -04:00
455 changed files with 60100 additions and 10112 deletions
+6 -1
View File
@@ -8,4 +8,9 @@ CMakeSettings.json
third-party/concurrentqueue/benchmarks
third-party/concurrentqueue/tests
*.bak
.vs
.vs
.cache
*.wav
*.orig
examples/csharp/bin
examples/csharp/obj
+201 -2
View File
@@ -1,7 +1,14 @@
variables:
DEBIAN_FRONTEND: noninteractive
stages:
- build
- test
#-------------------------------------------------------------------------------
# Windows
#-------------------------------------------------------------------------------
build windows/x64:
stage: build
script:
@@ -24,7 +31,7 @@ test windows/x64:
- build windows/x64
tags:
- icsneo-windows
timeout: 3m
timeout: 5m
build windows/x86:
stage: build
@@ -48,4 +55,196 @@ test windows/x86:
- 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
- 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: test
script:
- apt update -y
- apt upgrade -y
- apt install -y libusb-1.0-0-dev libpcap-dev
- build/libicsneo-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
- 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: test
script:
- apt update -y
- apt upgrade -y
- apt install -y libusb-1.0-0-dev libpcap-dev
- build/libicsneo-tests
tags:
- linux-build
timeout: 5m
build linux/ubuntu/2004/amd64/gcc:
<<: *build_linux_ubuntu_gcc
image: ubuntu:20.04
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
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
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
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:
- dnf upgrade -y
- dnf install -y g++ libpcap-devel cmake ninja-build libusb1-devel
- 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: test
cache:
paths:
- /var/cache/dnf
script:
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- build/libicsneo-tests
tags:
- linux-build
timeout: 5m
.build_linux_fedora_clang: &build_linux_fedora_clang
stage: build
cache:
paths:
- /var/cache/dnf
script:
- dnf upgrade -y
- dnf install -y clang lld libpcap-devel cmake ninja-build libusb1-devel
- 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: test
cache:
paths:
- /var/cache/dnf
script:
- dnf upgrade -y
- dnf install -y libpcap-devel libusb1-devel
- build/libicsneo-tests
tags:
- linux-build
timeout: 5m
build linux/fedora/37/amd64/gcc:
<<: *build_linux_fedora_gcc
image: fedora:37
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
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
+150 -39
View File
@@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.12)
project(libicsneo VERSION 0.3.0)
cmake_policy(SET CMP0074 NEW)
option(LIBICSNEO_BUILD_TESTS "Build all tests." OFF)
option(LIBICSNEO_BUILD_DOCS "Build documentation. Don't use in Visual Studio." OFF)
option(LIBICSNEO_BUILD_EXAMPLES "Build examples." ON)
@@ -16,10 +18,13 @@ set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to
option(LIBICSNEO_ENABLE_FIRMIO "Enable communication between Linux and CoreMini within the same device" OFF)
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_ANDROIDUSB "Enable devices which communicate over USB CDC ACM on Android" 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 11)
set(CMAKE_CXX_STANDARD 17)
endif()
include(GNUInstallDirs)
@@ -34,6 +39,9 @@ if(MSVC)
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
# http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0618r0.html
# Still supported until a suitable replacement is standardized
add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
@@ -93,7 +101,7 @@ if(LIBICSNEO_BUILD_DOCS)
endif()
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
if(WIN32)
set(PLATFORM_SRC
platform/windows/registry.cpp
)
@@ -110,6 +118,12 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
platform/windows/vcp.cpp
)
endif()
elseif(LIBICSNEO_ENABLE_ANDROIDUSB)
list(APPEND PLATFORM_SRC
platform/android/androidusb.cpp
)
else() # Darwin or Linux
set(PLATFORM_SRC)
@@ -154,6 +168,61 @@ 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/23.28.0/libftd3xx-1.3.0.4-win-x64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=ee1289cdd5023de67275aaa1712e8e32e73e825a8392efb0a76f161e357fbdc9")
elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.3.0.4-win-i686.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=bcd31f5c3cb39ecb9b09db5ce722bd79de6e0f813130e7b8f2c86879a60d8ff0")
elseif(APPLE AND CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-macos-universal2.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=74c0d35f04242d0841532c6325eb2932b8f627e3c395382a15d9f39974d27a90")
elseif(UNIX)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-x64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=a9dc7eb6948c8977fbd79f6700bec6f882d3da5667aea8f2175b8d1f6f08e456")
endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm.*|aarch64")
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-aarch64.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=da4b90ea1cbb905874cd159ad2ab8c1bdde65cc22b3aa55bf2b5fd85ca6efd22")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(LIBICSNEO_FTD3XX_URL "https://github.com/intrepidcs/libftd3xx-repack/releases/download/23.28.0/libftd3xx-1.0.14-linux-armhf.zip")
set(LIBICSNEO_FTD3XX_URL_HASH "SHA256=d813008117422cae958f7c71a065cdac0d31dca3b24809d3ab5e13604a9c3fb1")
endif()
endif()
endif()
if(NOT LIBICSNEO_FTD3XX_URL)
message(FATAL_ERROR "Unsupported platform for FTD3XX driver")
endif()
FetchContent_Declare(
ftdi3xx
URL ${LIBICSNEO_FTD3XX_URL}
URL_HASH ${LIBICSNEO_FTD3XX_URL_HASH}
)
FetchContent_GetProperties(ftdi3xx)
if(NOT ftdi3xx_POPULATED)
FetchContent_Populate(ftdi3xx)
endif()
set(FTD3XX_ROOT "${ftdi3xx_SOURCE_DIR}")
endif()
find_package(FTD3XX REQUIRED)
list(APPEND PLATFORM_SRC
platform/ftd3xx.cpp
)
endif()
if(LIBICSNEO_ENABLE_TCP)
list(APPEND PLATFORM_SRC
platform/tcp.cpp
)
endif()
if(LIBICSNEO_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
@@ -168,15 +237,29 @@ endforeach()
set(SRC_FILES
communication/message/flexray/control/flexraycontrolmessage.cpp
communication/message/callback/streamoutput/a2bwavoutput.cpp
communication/message/callback/streamoutput/a2bdecoder.cpp
communication/message/neomessage.cpp
communication/message/ethphymessage.cpp
communication/message/linmessage.cpp
communication/message/livedatamessage.cpp
communication/packet/flexraypacket.cpp
communication/packet/canpacket.cpp
communication/packet/a2bpacket.cpp
communication/packet/ethernetpacket.cpp
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/decoder.cpp
communication/encoder.cpp
communication/ethernetpacketizer.cpp
@@ -184,6 +267,7 @@ set(SRC_FILES
communication/multichannelcommunication.cpp
communication/communication.cpp
communication/driver.cpp
communication/livedata.cpp
device/extensions/flexray/extension.cpp
device/extensions/flexray/controller.cpp
device/idevicesettings.cpp
@@ -273,39 +357,67 @@ 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)
endif()
endif()
if(LIBICSNEO_ENABLE_ANDROIDUSB)
add_library(libusb SHARED IMPORTED)
set_target_properties(libusb PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/third-party/libusb/android/libs/${ANDROID_ABI}/libusb1.0.so)
target_include_directories(icsneocpp PRIVATE third-party/libusb/libusb)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_ANDROIDUSB)
target_link_libraries(icsneocpp PRIVATE android log libusb)
endif()
# fatfs
add_subdirectory(third-party/fatfs)
set_property(TARGET fatfs PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(icsneocpp PRIVATE fatfs)
# libftdi
if(NOT WIN32)
target_include_directories(icsneocpp PUBLIC third-party/libftdi/src)
set(LIBFTDI_DOCUMENTATION OFF CACHE INTERNAL "")
set(LIBFTDI_BUILD_TESTS OFF CACHE INTERNAL "")
set(LIBFTDI_INSTALL OFF CACHE INTERNAL "")
set(LIBFTDI_PYTHON_BINDINGS OFF CACHE INTERNAL "")
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF CACHE INTERNAL "")
set(FTDIPP OFF CACHE INTERNAL "")
set(FTDI_EEPROM OFF CACHE INTERNAL "")
add_subdirectory(third-party/libftdi)
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
endif(NOT WIN32)
if(LIBICSNEO_ENABLE_FTDI)
if(NOT WIN32)
target_include_directories(icsneocpp PUBLIC third-party/libftdi/src)
set(LIBFTDI_DOCUMENTATION OFF CACHE INTERNAL "")
set(LIBFTDI_BUILD_TESTS OFF CACHE INTERNAL "")
set(LIBFTDI_INSTALL OFF CACHE INTERNAL "")
set(LIBFTDI_PYTHON_BINDINGS OFF CACHE INTERNAL "")
set(LIBFTDI_LINK_PYTHON_LIBRARY OFF CACHE INTERNAL "")
set(FTDIPP OFF CACHE INTERNAL "")
set(FTDI_EEPROM OFF CACHE INTERNAL "")
add_subdirectory(third-party/libftdi)
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
# winpcap
if(WIN32)
if(LIBICSNEO_NPCAP_INCLUDE_DIR STREQUAL "")
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
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})
endif(NOT WIN32)
endif(LIBICSNEO_ENABLE_FTDI)
# pcap
if(LIBICSNEO_ENABLE_RAW_ETHERNET)
if(WIN32)
if(LIBICSNEO_NPCAP_INCLUDE_DIR STREQUAL "")
target_include_directories(icsneocpp PUBLIC AFTER third-party/winpcap/include)
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
else()
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
add_definitions(-DNPCAP -DWIN32_LEAN_AND_MEAN)
endif()
else()
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
add_definitions(-DNPCAP -DWIN32_LEAN_AND_MEAN)
endif()
endif(WIN32)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
endif()
find_package(PCAP REQUIRED)
target_include_directories(icsneocpp PUBLIC ${PCAP_INCLUDE_DIR})
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
endif(WIN32)
endif(LIBICSNEO_ENABLE_RAW_ETHERNET)
if(LIBICSNEO_BUILD_ICSNEOC)
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
@@ -318,6 +430,11 @@ if(LIBICSNEO_BUILD_ICSNEOC)
)
target_link_libraries(icsneoc PRIVATE icsneocpp)
target_compile_features(icsneoc PRIVATE cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
if(LIBICSNEO_ENABLE_ANDROIDUSB)
target_link_libraries(icsneoc PRIVATE android log)
target_include_directories(icsneoc PRIVATE third-party/libusb/libusb)
target_compile_definitions(icsneoc PRIVATE ICSNEO_ENABLE_ANDROIDUSB)
endif()
endif()
if(LIBICSNEO_BUILD_ICSNEOC_STATIC)
@@ -351,17 +468,6 @@ 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()
# libftdi
if(NOT WIN32)
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})
find_package(PCAP REQUIRED)
target_include_directories(icsneocpp PUBLIC ${PCAP_INCLUDE_DIR})
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
endif()
# googletest
if(LIBICSNEO_BUILD_TESTS)
if(WIN32)
@@ -382,6 +488,11 @@ if(LIBICSNEO_BUILD_TESTS)
test/diskdriverwritetest.cpp
test/eventmanagertest.cpp
test/ethernetpacketizertest.cpp
test/i2cencoderdecodertest.cpp
test/linencoderdecodertest.cpp
test/a2bencoderdecodertest.cpp
test/mdioencoderdecodertest.cpp
test/livedataencoderdecodertest.cpp
)
target_link_libraries(libicsneo-tests gtest gtest_main)
+12 -1
View File
@@ -14,6 +14,13 @@
- RADStar 2
- CAN works
- Ethernet works
- RADA2B
- CAN works
- Ethernet works
- RADComet
- CAN works
- CAN FD works
- Ethernet works
- Connecting over USB
- ValueCAN 4 series
@@ -36,4 +43,8 @@
- neoVI PLASMA
- CAN works
- neoVI ION
- CAN works
- CAN works
- RADA2B
- CAN works
- Ethernet works
- RADMoon3
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2018-2020 Intrepid Control Systems, Inc.
Copyright 2018-2023 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:
+51 -5
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
Building will require Microsoft Visual Studio 2017+ and CMake to be installed.
- Open a terminal and install the following:
```
winget install Microsoft.VisualStudio.2022.Community
winget install Kitware.CMake
winget install Git.Git
winget install Ninja-build.Ninja
```
- Reboot so cmake is in the system path
- Open a developer Powershell for VS2022 and run the following:
```
git clone https://github.com/intrepidcs/libicsneo
cd libicsneo
cmake -S . -B build -G "Ninja"
cmake --build build
# All dlls and libs are now inside the build directory
```
Building will require MSVC 2017 version 15.7 or newer and CMake to be installed.
### macOS
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 4.7 or above, 4.8+ recommended
- 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
```
+75 -6
View File
@@ -111,11 +111,15 @@ bool icsneo_isValidNeoDevice(const neodevice_t* device) {
}
bool icsneo_openDevice(const neodevice_t* device) {
if(!icsneo_isValidNeoDevice(device))
if(!icsneo_isValidNeoDevice(device)) {
//todo error
return false;
}
if(!device->device->open())
return false;
if(!device->device->open()) {
//todo error
return false;
}
// We connected successfully, move the device to connected devices
std::vector<std::vector<std::shared_ptr<Device>>::iterator> itemsToMove;
@@ -220,7 +224,6 @@ bool icsneo_getMessages(const neodevice_t* device, neomessage_t* messages, size_
return false;
*items = storage.size();
for(size_t i = 0; i < *items; i++) {
// For each message, copy into neomessage_t buffer given
messages[i] = CreateNeoMessage(storage[i]);
@@ -251,7 +254,7 @@ int icsneo_addMessageCallback(const neodevice_t* device, void (*callback)(neomes
return -1;
return device->device->addMessageCallback(
MessageCallback(
std::make_shared<MessageCallback>(
[=](std::shared_ptr<icsneo::Message> msg) {
return callback(CreateNeoMessage(msg));
}
@@ -486,6 +489,11 @@ bool icsneo_describeDevice(const neodevice_t* device, char* str, size_t* maxLeng
if(!icsneo_isValidNeoDevice(device))
return false;
if(!str) {
*maxLength = device->device->describe().length();
return false;
}
std::string output = device->device->describe();
*maxLength = output.copy(str, *maxLength);
@@ -642,7 +650,7 @@ bool icsneo_getDigitalIO(const neodevice_t* device, neoio_t type, uint32_t numbe
return false;
}
const optional<bool> val = device->device->getDigitalIO(static_cast<icsneo::IO>(type), number);
const std::optional<bool> val = device->device->getDigitalIO(static_cast<icsneo::IO>(type), number);
if(!val.has_value())
return false;
@@ -684,3 +692,64 @@ 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;
}
#ifdef ICSNEO_ENABLE_ANDROIDUSB
bool icsneo_addSysFileDescriptor(int sysFd) {
return icsneo::ANDROIDUSB::addSystemFD(sysFd);
}
bool icsneo_removeSysFileDescriptor(int sysFd) {
return icsneo::ANDROIDUSB::removeSystemFD(sysFd);
}
#endif
+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-2023"
VALUE "OriginalFilename", "icsneoc.dll"
VALUE "ProductName", "libicsneo"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
+140 -2
View File
@@ -71,6 +71,7 @@ static constexpr const char* UNSUPPORTED_TX_NETWORK = "Message network is not a
static constexpr const char* MESSAGE_MAX_LENGTH_EXCEEDED = "The message was too long.";
static constexpr const char* 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.";
// Device Errors
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
@@ -109,6 +110,12 @@ static constexpr const char* EOF_REACHED = "The requested length exceeds the ava
static constexpr const char* SETTINGS_DEFAULTS_USED = "The device settings could not be loaded, the default settings have been applied.";
static constexpr const char* ATOMIC_OPERATION_RETRIED = "An operation failed to be atomically completed, but will be retried.";
static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An ideally-atomic operation was completed nonatomically.";
static constexpr const char* 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.";
// Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
@@ -121,6 +128,46 @@ 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.";
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.";
@@ -158,6 +205,8 @@ const char* APIEvent::DescriptionForType(Type type) {
return VALUE_NOT_YET_PRESENT;
case Type::Timeout:
return TIMEOUT;
case Type::WiVINotSupported:
return WIVI_NOT_SUPPORTED;
// Device Errors
case Type::PollingMessageOverflow:
@@ -232,7 +281,18 @@ const char* APIEvent::DescriptionForType(Type type) {
return ATOMIC_OPERATION_RETRIED;
case Type::AtomicOperationCompletedNonatomically:
return ATOMIC_OPERATION_COMPLETED_NONATOMICALLY;
case Type::WiVIStackRefreshFailed:
return WIVI_STACK_REFRESH_FAILED;
case Type::WiVIUploadStackOverflow:
return WIVI_UPLOAD_STACK_OVERFLOW;
case Type::A2BMessageIncompleteFrame:
return A2B_MESSAGE_INCOMPLETE_FRAME;
case Type::CoreminiUploadVersionMismatch:
return COREMINI_UPLOAD_VERSION_MISMATCH;
case Type::DiskNotConnected:
return DISK_NOT_CONNECTED;
case Type::UnexpectedResponse:
return UNEXPECTED_RESPONSE;
// Transport Errors
case Type::FailedToRead:
return FAILED_TO_READ;
@@ -254,7 +314,85 @@ 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;
// 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);
+2 -1
View File
@@ -1,5 +1,6 @@
#include "icsneo/icsneocpp.h"
#include "icsneo/device/devicefinder.h"
#include <jni.h>
using namespace icsneo;
@@ -45,4 +46,4 @@ void icsneo::SetEventLimit(size_t newLimit) {
size_t icsneo::GetEventLimit() {
return EventManager::GetInstance().getEventLimit();
}
}
+250 -36
View File
@@ -20,6 +20,7 @@
#ifdef _MSC_VER
#pragma warning(disable : 4100) // unreferenced formal parameter
#pragma warning(disable : 4996) // STL time functions
#endif
using namespace icsneo;
@@ -73,6 +74,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 +101,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 +161,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));
@@ -396,25 +514,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 +566,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)
{
@@ -638,8 +788,13 @@ int LegacyDLLExport icsneoSetDeviceParameters(void* hObject, char* pParmValue, i
// Error Functions
int LegacyDLLExport icsneoGetLastAPIError(void* hObject, unsigned long* pErrorNumber)
{
// TODO Implement
return false;
if(!icsneoValidateHObject(hObject))
return false;
neoevent_t error;
icsneo_getLastError(&error);
*pErrorNumber = (unsigned long)error.eventNumber;
return true;
}
int LegacyDLLExport icsneoGetErrorMessages(void* hObject, int* pErrorMsgs, int* pNumberOfErrors)
@@ -648,11 +803,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
@@ -731,6 +918,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)
{
@@ -910,6 +1109,12 @@ int LegacyDLLExport icsneoGetDeviceSettingsType(void* hObject, EPlasmaIonVnetCha
case NEODEVICE_RADGIGALOG:
*pDeviceSettingsType = DeviceRADGigalogSettingsType;
break;
case NEODEVICE_RADMOON3:
*pDeviceSettingsType = DeviceRADMoon3SettingsType;
break;
case NEODEVICE_RED2:
*pDeviceSettingsType = DeviceRed2SettingsType;
break;
default:
return 0;
}
@@ -947,6 +1152,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))
@@ -1287,7 +1501,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_TESTS=ON -DLIBICSNEO_ENABLE_TCP=ON || 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_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()
+32 -14
View File
@@ -13,6 +13,7 @@
#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;
@@ -21,7 +22,8 @@ int Communication::messageCallbackIDCounter = 1;
Communication::~Communication() {
if(redirectingRead)
clearRedirectRead();
close();
if(isOpen())
close();
}
bool Communication::open() {
@@ -150,22 +152,22 @@ std::shared_ptr<SerialNumberMessage> Communication::getSerialNumberSync(std::chr
return std::dynamic_pointer_cast<SerialNumberMessage>(m51);
}
optional< std::vector< optional<DeviceAppVersion> > > Communication::getVersionsSync(std::chrono::milliseconds timeout) {
std::optional< std::vector< std::optional<DeviceAppVersion> > > Communication::getVersionsSync(std::chrono::milliseconds timeout) {
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Message::Type::DeviceVersion);
std::vector< optional<DeviceAppVersion> > ret;
std::vector< std::optional<DeviceAppVersion> > ret;
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
return sendCommand(Command::GetMainVersion);
}, filter, timeout);
if(!msg) // Did not receive a message
return nullopt;
return std::nullopt;
auto ver = std::dynamic_pointer_cast<VersionMessage>(msg);
if(!ver) // Could not upcast for some reason
return nullopt;
return std::nullopt;
if(ver->ForChip != VersionMessage::MainChip || ver->Versions.size() != 1)
return nullopt;
return std::nullopt;
ret.push_back(ver->Versions.front());
@@ -193,7 +195,7 @@ std::shared_ptr<LogicalDiskInfoMessage> Communication::getLogicalDiskInfoSync(st
return std::dynamic_pointer_cast<LogicalDiskInfoMessage>(msg);
}
int Communication::addMessageCallback(const MessageCallback& cb) {
int Communication::addMessageCallback(const std::shared_ptr<MessageCallback>& cb) {
std::lock_guard<std::mutex> lk(messageCallbacksLock);
messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb));
return messageCallbackIDCounter++;
@@ -212,14 +214,15 @@ bool Communication::removeMessageCallback(int id) {
std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(void)> onceWaitingDo,
const std::shared_ptr<MessageFilter>& f, std::chrono::milliseconds timeout) {
std::mutex m;
std::mutex cvMutex;
std::condition_variable cv;
std::shared_ptr<Message> returnedMessage;
std::unique_lock<std::mutex> lk(m); // Don't let the callback fire until we're waiting for it
int cb = addMessageCallback(MessageCallback([&m, &returnedMessage, &cv](std::shared_ptr<Message> message) {
std::unique_lock<std::mutex> fnLk(syncMessageMutex); // Only allow for one sync message at a time
std::unique_lock<std::mutex> cvLk(cvMutex); // Don't let the callback fire until we're waiting for it
int cb = addMessageCallback(std::make_shared<MessageCallback>([&cvMutex, &returnedMessage, &cv](std::shared_ptr<Message> message) {
{
std::lock_guard<std::mutex> lk(m);
std::lock_guard<std::mutex> lk(cvMutex);
returnedMessage = message;
}
cv.notify_all();
@@ -228,8 +231,8 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
// We have now added the callback, do whatever the caller wanted to do
bool fail = !onceWaitingDo();
if(!fail)
cv.wait_for(lk, timeout, [&returnedMessage] { return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value
lk.unlock(); // Ensure callbacks can complete even if we didn't wait for them
cv.wait_for(cvLk, timeout, [&returnedMessage] { return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value
cvLk.unlock(); // Ensure callbacks can complete even if we didn't wait for them
// We don't actually check that we got a message, because either way we want to remove the callback (since it should only happen once)
removeMessageCallback(cb);
@@ -251,7 +254,7 @@ void Communication::dispatchMessage(const std::shared_ptr<Message>& msg) {
EventManager::GetInstance().cancelErrorDowngradingOnCurrentThread();
for(auto& cb : messageCallbacks) {
if(!closing) { // We might have closed while reading or processing
cb.second.callIfMatch(msg);
cb.second->callIfMatch(msg);
}
}
if(downgrade)
@@ -299,3 +302,18 @@ void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes)
}
}
}
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));
}
+142 -1
View File
@@ -5,16 +5,36 @@
#include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/communication/message/wiviresponsemessage.h"
#include "icsneo/communication/message/scriptstatusmessage.h"
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
#include "icsneo/communication/message/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/command.h"
#include "icsneo/device/device.h"
#include "icsneo/communication/packet/canpacket.h"
#include "icsneo/communication/packet/a2bpacket.h"
#include "icsneo/communication/packet/ethernetpacket.h"
#include "icsneo/communication/packet/flexraypacket.h"
#include "icsneo/communication/packet/iso9141packet.h"
#include "icsneo/communication/packet/versionpacket.h"
#include "icsneo/communication/packet/ethphyregpacket.h"
#include "icsneo/communication/packet/logicaldiskinfopacket.h"
#include "icsneo/communication/packet/wivicommandpacket.h"
#include "icsneo/communication/packet/i2cpacket.h"
#include "icsneo/communication/packet/scriptstatuspacket.h"
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/packet/componentversionpacket.h"
#include "icsneo/communication/packet/supportedfeaturespacket.h"
#include "icsneo/communication/packet/mdiopacket.h"
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
#include "icsneo/communication/packet/livedatapacket.h"
#include <iostream>
using namespace icsneo;
@@ -115,6 +135,56 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
iso.network = packet->network;
return true;
}
case Network::Type::I2C: {
if(packet->data.size() < sizeof(HardwareI2CPacket)) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false;
}
result = HardwareI2CPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; //malformed packet indicated by a nullptr return
}
return true;
}
case Network::Type::A2B: {
result = HardwareA2BPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
msg.network = packet->network;
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: {
@@ -193,6 +263,56 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
msg->data.insert(msg->data.end(), packet->data.begin() + 4, packet->data.end());
return true;
}
case Network::NetID::ExtendedCommand: {
if(packet->data.size() < sizeof(ExtendedResponseMessage::PackedGenericResponse))
break; // Handle as a raw message, might not be a generic response
const auto& resp = *reinterpret_cast<ExtendedResponseMessage::PackedGenericResponse*>(packet->data.data());
switch(resp.header.command) {
case ExtendedCommand::GetComponentVersions:
result = ComponentVersionPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GetSupportedFeatures:
result = SupportedFeaturesPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GenericBinaryInfo:
result = GenericBinaryStatusPacket::DecodeToMessage(packet->data);
return true;
case ExtendedCommand::GenericReturn:
result = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
return true;
case ExtendedCommand::LiveData:
result = HardwareLiveDataPacket::DecodeToMessage(packet->data, report);
return true;
default:
// No defined handler, treat this as a RawMessage
break;
}
break;
}
case Network::NetID::ExtendedData: {
if(packet->data.size() < sizeof(ExtendedDataMessage::ExtendedDataHeader))
break;
const auto& header = *reinterpret_cast<ExtendedDataMessage::ExtendedDataHeader*>(packet->data.data());
switch(header.subCommand) {
case ExtendedDataSubCommand::GenericBinaryRead: {
result = std::make_shared<ExtendedDataMessage>(header);
auto extDataMsg = std::static_pointer_cast<ExtendedDataMessage>(result);
size_t numRead = std::min(ExtendedDataMessage::MaxExtendedDataBufferSize, (size_t)header.length);
extDataMsg->data.resize(numRead);
std::copy(packet->data.begin() + sizeof(header), packet->data.begin() + sizeof(header) + numRead, extDataMsg->data.begin());
return true;
}
default:
break;
}
break;
}
case Network::NetID::FlexRayControl: {
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
if(!frResult->decoded) {
@@ -286,6 +406,14 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
}
return true;
}
case Network::NetID::WiVICommand: {
result = WiVI::CommandPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
return false;
}
return true;
}
case Network::NetID::EthPHYControl: {
result = HardwareEthernetPhyRegisterPacket::DecodeToMessage(packet->data, report);
if(!result) {
@@ -294,13 +422,26 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
}
return true;
}
case Network::NetID::ScriptStatus: {
result = ScriptStatus::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
return false;
}
return true;
}
case Network::NetID::DiskData: {
result = std::make_shared<DiskDataMessage>(std::move(packet->data));
return true;
}
default:
break;
}
break;
}
}
// 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;
}
}
+68 -1
View File
@@ -1,11 +1,18 @@
#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"
#include "icsneo/communication/packet/ethphyregpacket.h"
#include "icsneo/communication/message/ethphymessage.h"
#include "icsneo/communication/packet/i2cpacket.h"
#include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/packet/a2bpacket.h"
#include "icsneo/communication/packet/linpacket.h"
#include "icsneo/communication/packet/mdiopacket.h"
using namespace icsneo;
@@ -68,6 +75,54 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
// packets to the device. This function just encodes them back to back into `result`
return HardwareISO9141Packet::EncodeFromMessage(*isomsg, result, report, packetizer);
} // End of Network::Type::ISO9141
case Network::Type::A2B: {
auto a2bmsg = std::dynamic_pointer_cast<A2BMessage>(message);
if(!a2bmsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareA2BPacket::EncodeFromMessage(*a2bmsg, result, report)) {
return false;
}
break;
} // End of Network::Type::A2B
case Network::Type::I2C: {
auto i2cmsg = std::dynamic_pointer_cast<I2CMessage>(message);
if(!i2cmsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareI2CPacket::EncodeFromMessage(*i2cmsg, result, report)) {
return false;
}
break;
} // End of Network::Type::I2C
case Network::Type::LIN: {
auto linmsg = std::dynamic_pointer_cast<LINMessage>(message);
if(!linmsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareLINPacket::EncodeFromMessage(*linmsg, result, report)) {
return false;
}
break;
} // End of Network::Type::LIN
case Network::Type::MDIO: {
auto mdiomsg = std::dynamic_pointer_cast<MDIOMessage>(message);
if(!mdiomsg) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
buffer = &result;
if(!HardwareMDIOPacket::EncodeFromMessage(*mdiomsg, result, report)) {
return false;
}
break;
} // End of Network::Type::MDIO
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
@@ -144,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;
}
@@ -154,7 +220,8 @@ bool Encoder::encode(const Packetizer& packetizer, std::vector<uint8_t>& result,
// Size for the host-to-device long format is the size of the entire packet + 1
// So +1 for AA header, +1 for short format header, +2 for long format size, and +2 for long format NetID
// Then an extra +1, due to a firmware idiosyncrasy
uint16_t size = uint16_t(buffer->size()) + 1 + 1 + 2 + 2 + 1;
uint16_t size = static_cast<uint16_t>(buffer->size()) + 1 + 1 + 2 + 2 + 1;
buffer->insert(buffer->begin(), {
(uint8_t)Network::NetID::RED, // 0x0C for long message
(uint8_t)size, // Size, little endian 16-bit
-1
View File
@@ -3,7 +3,6 @@
#include <iterator>
#include <cstring>
#include <cassert>
#include <iostream>
using namespace icsneo;
+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
@@ -0,0 +1,156 @@
#include "icsneo/communication/message/callback/streamoutput/a2bdecoder.h"
#include <chrono>
#include "icsneo/icsneocpp.h"
namespace icsneo {
static constexpr uint8_t maxChannel = 255;
size_t A2BAudioChannelMap::getChannelIndex(Channel channel, A2BMessage::A2BDirection dir) const {
size_t output = (size_t)channel;
if(dir == A2BMessage::A2BDirection::Upstream) {
output++;
}
return output;
}
A2BAudioChannelMap::A2BAudioChannelMap(uint8_t tdm) {
rawMap.resize(2*tdm, maxChannel);
}
void A2BAudioChannelMap::set(Channel outChannel, A2BMessage::A2BDirection dir, Channel inChannel) {
auto index = getChannelIndex(outChannel, dir);
rawMap[index] = inChannel;
}
void A2BAudioChannelMap::setAll(Channel inChannel) {
std::fill(rawMap.begin(), rawMap.end(), inChannel);
}
Channel A2BAudioChannelMap::get(Channel outChannel, A2BMessage::A2BDirection dir) const {
auto index = getChannelIndex(outChannel, dir);
return rawMap[index];
}
size_t A2BAudioChannelMap::A2BAudioChannelMap::size() const {
return rawMap.size();
}
uint8_t A2BAudioChannelMap::getTDM() const {
return (uint8_t)(rawMap.size() / 2);
}
Channel& A2BAudioChannelMap::operator[](size_t idx) {
return rawMap[idx];
}
A2BAudioChannelMap::operator const std::vector<Channel>&() const {
return rawMap;
}
A2BDecoder::A2BDecoder(
std::unique_ptr<std::istream>&& streamOut,
bool chSize16,
const A2BAudioChannelMap& chMap
) : channelSize16(chSize16), channelMap(chMap) {
stream = std::move(streamOut);
tdm = chMap.getTDM();
initializeFromHeader();
}
A2BDecoder::A2BDecoder(
const char* filename,
bool chSize16,
const A2BAudioChannelMap& chMap
) : A2BDecoder(std::make_unique<std::ifstream>(filename, std::ios::binary), chSize16, chMap) { }
A2BDecoder::operator bool() const {
return initialized && stream->good() && !stream->eof();
}
void A2BDecoder::initializeFromHeader() {
WaveFileHeader header;
if(!stream->read((char*)&header, sizeof(header))) {
initialized = false;
return;
}
// Only allow 16 or 24 bit samples
if(header.bitsPerSample != 16 && header.bitsPerSample != 24) {
initialized = false;
return;
}
audioBytesPerSample = header.bitsPerSample == 16 ? 2 : 3;
channelsInWave = (uint8_t)header.numChannels;
size_t bytesPerSample = channelSize16 ? 2 : 4;
size_t frameSize = 2*tdm*bytesPerSample;
size_t frameSizeWave = (size_t)(channelsInWave) * (size_t)(audioBytesPerSample);
frame.resize(frameSize, 0);
frameWave.resize(frameSizeWave, 0);
initialized = true;
}
std::shared_ptr<A2BMessage> A2BDecoder::decode() {
if(!*(this)) {
return nullptr;
}
auto a2bMessagePtr = std::make_shared<icsneo::A2BMessage>(
tdm,
channelSize16,
2048
);
A2BMessage& a2bMessage = *a2bMessagePtr.get();
a2bMessage.setMonitorBit(false); // Probably not necessary
a2bMessage.setTxMsgBit(true);
a2bMessage.network = Network(Network::NetID::A2B2);
for(uint32_t frameIndex = 0; frameIndex < a2bMessage.getNumFrames(); frameIndex++) {
if(!stream->read((char*)frameWave.data(), frameWave.size())) {
break;
}
for(size_t icsChannel = 0; icsChannel < channelMap.size(); icsChannel++) {
if(channelMap[icsChannel] >= maxChannel) {
continue;
}
size_t wBegin = audioBytesPerSample * channelMap[icsChannel];
A2BPCMSample sample = 0;
uint8_t* sampBytes = (uint8_t*)&sample;
std::copy(frameWave.begin() + wBegin, frameWave.begin() + wBegin + audioBytesPerSample, sampBytes);
a2bMessage[frameIndex][icsChannel] = sample;
}
}
return a2bMessagePtr;
}
bool A2BDecoder::outputAll(std::shared_ptr<Device>& device) {
const auto& networks = device->getSupportedTXNetworks();
if(std::none_of(networks.begin(), networks.end(), [](const Network& net) { return net.getNetID() == Network::NetID::A2B2; })) {
return false;
}
while(*this) {
device->transmit(decode());
}
return true;
}
}
@@ -0,0 +1,69 @@
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
#include "icsneo/device/tree/rada2b/rada2b.h"
#include "icsneo/icsneocpp.h"
namespace icsneo {
void A2BWAVOutput::writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const {
WaveFileHeader header = WaveFileHeader(2 * firstMsg->getNumChannels(), wavSampleRate, firstMsg->getBitDepth());
header.write(stream);
streamStartPos = static_cast<uint32_t>(stream->tellp());
}
bool A2BWAVOutput::callIfMatch(const std::shared_ptr<Message>& message) const {
if(closed) {
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;
}
// Might need to readd this block of code later if sample alignment fix is necessary
/*
std::streamsize bps = (std::streamsize)a2bmsg->getBytesPerSample();
for(size_t i=0; i<a2bmsg->getNumSamples(); i++) {
A2BPCMSample samp = *(a2bmsg->getSample(i));
write((void*)&samp, bps);
}
*/
write((void*)a2bmsg->getAudioBuffer(), a2bmsg->getAudioBufferSize());
return true;
}
void A2BWAVOutput::close() const {
if(closed) {
return;
}
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->seekp(4, std::ios::beg);
write((void*)&chunkSize, 4);
closed = 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;
+64
View File
@@ -0,0 +1,64 @@
#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;
std::shared_ptr<Message> HardwareA2BPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < coreMiniMessageHeaderSize)
{
return nullptr;
}
const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data();
size_t totalPackedLength = static_cast<size_t>(bytestream.size()) - static_cast<size_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(
(uint8_t)data->header.channelNum,
data->header.channelSize16,
totalPackedLength
);
msg->setMonitorBit(data->header.monitor);
msg->setTxMsgBit(data->header.txmsg);
msg->setErrIndicatorBit(data->header.errIndicator);
msg->setSyncFrameBit(data->header.syncFrame);
msg->setRFU2(data->header.rfu2);
msg->setAudioBuffer(bytestream.begin() + coreMiniMessageHeaderSize, bytestream.end());
return msg;
}
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
constexpr size_t a2btxMessageHeaderSize = 6;
if(message.getBytesPerSample() != 2 && message.getBytesPerSample() != 4) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
size_t sampleBytes = message.getAudioBufferSize();
size_t totalSize = a2btxMessageHeaderSize + sampleBytes;
bytestream.resize(totalSize, 0);
uint32_t offset = 0;
bytestream[offset++] = 0;
bytestream[offset++] = 0;
bytestream[offset++] = (uint8_t)(sampleBytes & 0xFF);
bytestream[offset++] = (uint8_t)((sampleBytes >> 8) & 0xFF);
bytestream[offset++] = (uint8_t)((message.description >> 8) & 0xFF);
bytestream[offset++] = (uint8_t)(message.description & 0xFF);
std::copy(message.data.begin(), message.data.end(), bytestream.begin() + offset);
return true;
}
}
+6 -7
View File
@@ -1,10 +1,9 @@
#include "icsneo/communication/packet/canpacket.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/platform/optional.h"
using namespace icsneo;
static optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
static std::optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
if (length <= 8)
return length;
@@ -27,10 +26,10 @@ static optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
}
}
return nullopt;
return std::nullopt;
}
static optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
static std::optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
{
if (dataLength <= 8)
return uint8_t(dataLength);
@@ -52,7 +51,7 @@ static optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
return uint8_t(0xF);
}
return nullopt;
return std::nullopt;
}
std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
@@ -94,7 +93,7 @@ std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<ui
msg->isCANFD = true;
msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch
msg->errorStateIndicator = data->header.ESI;
const optional<uint8_t> lenFromDLC = CAN_DLCToLength(length, true);
const std::optional<uint8_t> lenFromDLC = CAN_DLCToLength(length, true);
if (lenFromDLC)
length = *lenFromDLC;
} else if(length > 8) { // This is a standard CAN frame with a length of more than 8
@@ -133,7 +132,7 @@ bool HardwareCANPacket::EncodeFromMessage(const CANMessage& message, std::vector
}
const size_t dataSize = message.data.size();
optional<uint8_t> dlc = CAN_LengthToDLC(dataSize, message.isCANFD);
std::optional<uint8_t> dlc = CAN_LengthToDLC(dataSize, message.isCANFD);
if (!dlc.has_value()) {
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
return false; // Too much data for the protocol
@@ -0,0 +1,45 @@
#include "icsneo/communication/packet/componentversionpacket.h"
#include "icsneo/communication/message/componentversionsmessage.h"
using namespace icsneo;
#pragma pack(push, 2)
struct PackedComponentVersion {
uint8_t valid;
uint8_t expansionSlot;
uint8_t componentInfo; // Component specific data (e.g. Linux: boot device)
uint8_t reserved;
uint32_t identifier;
uint32_t dotVersion; // Represents a.b.c.d, a.b.c, or a.b, depending on leading zeros.
uint32_t commitHash;
};
static constexpr size_t MaxReportedVersions = 16;
struct ComponentVersionsResponse {
ExtendedResponseMessage::ResponseHeader header;
uint16_t numVersions;
PackedComponentVersion versions[MaxReportedVersions];
};
#pragma pack(pop)
std::shared_ptr<ComponentVersionsMessage> ComponentVersionPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
auto msg = std::make_shared<ComponentVersionsMessage>();
// Length checks: At least a header and numVersions field.
if(bytes.size() < sizeof(ExtendedResponseMessage::ResponseHeader) + 2) {
return msg; // Empty
}
// Get a reference to the payload to fully validate the length
const auto& response = *reinterpret_cast<const ComponentVersionsResponse*>(bytes.data());
// Expected size is the header, numVersions field, and numVersions ComponentVersion objects.
auto expectedSize = sizeof(ExtendedResponseMessage::ResponseHeader) + 2 + (response.numVersions * sizeof(ComponentVersion));
// If the response is malformed (too small), return an empty message.
if(bytes.size() < expectedSize) {
return msg; // Empty
}
// Unpack into the portable class
for(unsigned int i = 0; i < response.numVersions; ++i) {
const auto& packedVersion = response.versions[i];
msg->versions.emplace_back(packedVersion.valid, packedVersion.componentInfo, packedVersion.identifier, packedVersion.dotVersion, packedVersion.commitHash);
}
return msg;
}
@@ -0,0 +1,38 @@
#include "icsneo/communication/packet/genericbinarystatuspacket.h"
#include "icsneo/communication/message/genericbinarystatusmessage.h"
using namespace icsneo;
#pragma pack(push, 2)
struct GenericBinaryStatusResponse {
ExtendedResponseMessage::ResponseHeader header;
size_t size;
uint16_t index;
uint16_t status;
};
#pragma pack(pop)
std::shared_ptr<GenericBinaryStatusMessage> GenericBinaryStatusPacket::DecodeToMessage(const std::vector<uint8_t>& bytes) {
if(bytes.size() < sizeof(GenericBinaryStatusResponse)) {
return nullptr;
}
auto msg = std::make_shared<GenericBinaryStatusMessage>();
const auto& response = *reinterpret_cast<const GenericBinaryStatusResponse*>(bytes.data());
msg->binarySize = response.size;
msg->binaryIndex = response.index;
msg->binaryStatus = response.status;
return msg;
}
std::vector<uint8_t> GenericBinaryStatusPacket::EncodeArguments(uint16_t binaryIndex) {
std::vector<uint8_t> bytestream(sizeof(GenericBinaryStatusResponse));
auto& parameters = *reinterpret_cast<GenericBinaryStatusResponse*>(bytestream.data());
parameters.index = binaryIndex;
return bytestream;
}
+83
View File
@@ -0,0 +1,83 @@
#include "icsneo/communication/packet/i2cpacket.h"
namespace icsneo
{
std::shared_ptr<Message> HardwareI2CPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream)
{
auto msg = std::make_shared<I2CMessage>();
const I2CHeader* packet = reinterpret_cast<const I2CHeader*>(bytestream.data());
const size_t numPayloadBytes = packet->length;
const size_t numControlBytes = packet->CoreMiniBitsI2C.CBLen;
const size_t numDataBytes = numPayloadBytes - numControlBytes;
if( (numPayloadBytes == 0) || (numDataBytes > I2CMaxLength) ||
(sizeof(I2CHeader) != (bytestream.size() - numPayloadBytes)) )
{ return nullptr; }
msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast<Network::CoreMini>(packet->networkID));
msg->address = (packet->CoreMiniBitsI2C.ID & 0x3FFu);
msg->deviceMode = static_cast<I2CMessage::DeviceMode>(packet->CoreMiniBitsI2C.CT);
msg->direction = static_cast<I2CMessage::Direction>(packet->CoreMiniBitsI2C.DIR);
msg->isExtendedID = static_cast<bool>(packet->CoreMiniBitsI2C.EID & 0x01u);
msg->isTXMsg = static_cast<bool>(packet->CoreMiniBitsI2C.TXMsg & 0x01u);
msg->txTimeout = static_cast<bool>(packet->CoreMiniBitsI2C.TXTimeout & 0x01u);
msg->txNack = static_cast<bool>(packet->CoreMiniBitsI2C.TXNack & 0x01u);
msg->txAborted = static_cast<bool>(packet->CoreMiniBitsI2C.TXAborted & 0x01u);
msg->txLostArb = static_cast<bool>(packet->CoreMiniBitsI2C.TXLostArb & 0x01u);
msg->txError = static_cast<bool>(packet->CoreMiniBitsI2C.TXError & 0x01u);
//We don't care about 0xTRB0Dx in this case...
//copy 0xTRB0STAT even though we likely won't use it either
msg->stats = packet->stats;
msg->timestamp = (packet->timestamp & (0x7FFFFFFFFFFFFFFFull));
//The device will combine the 'control' bytes and data bytes into one payload
//The control bytes will always come before the data
auto cbStart = bytestream.begin() + sizeof(I2CHeader);
auto dataStart = cbStart + numControlBytes;
std::copy(cbStart, dataStart, std::back_inserter(msg->controlBytes));
std::copy(dataStart, bytestream.end(), std::back_inserter(msg->dataBytes));
return msg;
}
bool HardwareI2CPacket::EncodeFromMessage(const I2CMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report)
{
const size_t numControlBytes = message.controlBytes.size();
const size_t numDataBytes = message.dataBytes.size();
if(I2CMaxLength < numDataBytes)
{
report(APIEvent::Type::I2CMessageExceedsMaxLength, APIEvent::Severity::Error);
return false;
}
if(message.controlBytes.empty() || message.dataBytes.empty())
{
//You'll need to provide a target R/W register in controlBytes
//alternatively, you're expecting to read without providing a dataBytes payload
report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error);
return false;
}
bytestream.push_back(static_cast<uint8_t>(numControlBytes & 0xFFu));
bytestream.push_back(static_cast<uint8_t>((numControlBytes) >> 8) & 0xFFu);
bytestream.push_back(static_cast<uint8_t>(numDataBytes & 0xFFu));
bytestream.push_back(static_cast<uint8_t>((numDataBytes) >> 8) & 0xFFu);
bytestream.push_back(static_cast<uint8_t>((message.stats) >> 8) & 0xFFu);
bytestream.push_back(static_cast<uint8_t>(message.stats & 0xFFu));
if(message.isExtendedID)
{
bytestream.push_back(static_cast<uint8_t>(message.address & 0xFFu));
bytestream.push_back(static_cast<uint8_t>(((message.address) >> 8) & 0x03u) | 0x04u);
} else {
bytestream.push_back(static_cast<uint8_t>(message.address & 0xFFu));
bytestream.push_back(static_cast<uint8_t>(0x00u));
}
if(I2CMessage::Direction::Read == message.direction)
{ bytestream.back() |= static_cast<uint8_t>(0x10u); }
std::copy(message.controlBytes.begin(), message.controlBytes.end(), std::back_inserter(bytestream));
std::copy(message.dataBytes.begin(), message.dataBytes.end(), std::back_inserter(bytestream));
return true;
}
}
+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,31 @@
#include <iostream>
#include "icsneo/communication/packet/scriptstatuspacket.h"
#include "icsneo/communication/message/scriptstatusmessage.h"
using namespace icsneo;
std::shared_ptr<ScriptStatusMessage> ScriptStatus::DecodeToMessage(const std::vector<uint8_t>& bytestream){
if(bytestream.size() != sizeof(ScriptStatus))
return {};
auto msg = std::make_shared<ScriptStatusMessage>();
const auto& decoded = *reinterpret_cast<const ScriptStatus*>(bytestream.data());
msg->isCoreminiRunning = decoded.status.isRunning;
msg->isEncrypted = decoded.status.isEncrypted;
msg->sectorOverflows = decoded.sectorOverflows;
msg->numRemainingSectorBuffers = decoded.numRemainingSectorBuffers;
msg->lastSector = decoded.lastSector;
msg->readBinSize = decoded.readBinSize;
msg->minSector = decoded.minSector;
msg->maxSector = decoded.maxSector;
msg->currentSector = decoded.currentSector;
msg->coreminiCreateTime = ((uint64_t)decoded.coreminiCreateTimeMsb << 32) | decoded.coreminiCreateTimeLsb;
msg->fileChecksum = decoded.fileChecksum;
msg->coreminiVersion = decoded.coreminiVersion;
msg->coreminiHeaderSize = decoded.coreminiHeaderSize;
msg->diagnosticErrorCode = decoded.diagErrCode;
msg->diagnosticErrorCodeCount = decoded.diagErrCodeCount;
msg->maxCoreminiSizeKB = decoded.maxCoreminiSizeKB;
return msg;
}
@@ -0,0 +1,41 @@
#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());
// 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;
}
+2 -2
View File
@@ -9,7 +9,7 @@ std::shared_ptr<VersionMessage> HardwareVersionPacket::DecodeMainToMessage(const
auto msg = std::make_shared<VersionMessage>(VersionMessage::MainChip);
msg->Versions.emplace_back();
optional<DeviceAppVersion>& version = msg->Versions.back();
std::optional<DeviceAppVersion>& version = msg->Versions.back();
version.emplace();
version->major = bytestream[1];
version->minor = bytestream[2];
@@ -26,7 +26,7 @@ std::shared_ptr<VersionMessage> HardwareVersionPacket::DecodeSecondaryToMessage(
while(bytesLeft >= 3) {
const bool versionValid = bytestream[bytestream.size() - bytesLeft + 0];
msg->Versions.emplace_back();
optional<DeviceAppVersion>& version = msg->Versions.back();
std::optional<DeviceAppVersion>& version = msg->Versions.back();
if(versionValid) {
version.emplace();
version->major = bytestream[bytestream.size() - bytesLeft + 1];
+100
View File
@@ -0,0 +1,100 @@
#include "icsneo/communication/packet/wivicommandpacket.h"
#include "icsneo/communication/message/wiviresponsemessage.h"
#include <cstring>
using namespace icsneo;
std::shared_ptr<WiVI::ResponseMessage> WiVI::CommandPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
if(bytestream.size() < sizeof(WiVI::CommandPacket::Header))
return {};
auto msg = std::make_shared<WiVI::ResponseMessage>();
const auto& header = *reinterpret_cast<const WiVI::CommandPacket::Header*>(bytestream.data());
switch(header.cmd) {
case WiVI::Command::Result: {
if(bytestream.size() < sizeof(WiVI::CommandPacket::Result))
return {};
const auto& decoded = *reinterpret_cast<const WiVI::CommandPacket::Result*>(bytestream.data());
msg->responseTo = decoded.responseTo;
msg->success = decoded.result != 0;
break;
}
case WiVI::Command::GetSignal: {
// Use the SetSignal structure since it matches the response
if(bytestream.size() < sizeof(WiVI::CommandPacket::SetSignal))
return {};
const auto& setSignal = *reinterpret_cast<const WiVI::CommandPacket::SetSignal*>(bytestream.data());
msg->responseTo = WiVI::Command::GetSignal;
msg->value = setSignal.value.ValueInt32;
break;
}
case WiVI::Command::GetAll: {
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll))
return {};
const auto& getAll = *reinterpret_cast<const WiVI::CommandPacket::GetAll*>(bytestream.data());
msg->responseTo = WiVI::Command::GetAll;
msg->info.emplace();
msg->info->sleepRequest = getAll.sleepRequest;
msg->info->connectionTimeoutMinutes = getAll.connectionTimeoutMinutes;
// Check that we have enough data for the capture infos
if(bytestream.size() < sizeof(WiVI::CommandPacket::GetAll) + (sizeof(WiVI::CaptureInfo) * getAll.numCaptureInfos))
return {};
msg->info->captures.resize(getAll.numCaptureInfos);
for(uint16_t i = 0; i < getAll.numCaptureInfos; i++)
msg->info->captures[i] = getAll.captureInfos[i];
break;
}
default: // Unknown command response
return {};
}
return msg;
}
std::vector<uint8_t> WiVI::CommandPacket::GetSignal::Encode(WiVI::SignalType type) {
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetSignal));
auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetSignal*>(ret.data());
frame.header.cmd = WiVI::Command::GetSignal;
frame.header.length = sizeof(frame) - sizeof(frame.header);
frame.type = type;
return ret;
}
std::vector<uint8_t> WiVI::CommandPacket::SetSignal::Encode(WiVI::SignalType type, CoreMiniFixedPointValue value) {
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::SetSignal));
auto& frame = *reinterpret_cast<WiVI::CommandPacket::SetSignal*>(ret.data());
frame.header.cmd = WiVI::Command::SetSignal;
frame.header.length = sizeof(frame) - sizeof(frame.header);
frame.type = type;
frame.value = value;
return ret;
}
std::vector<uint8_t> WiVI::CommandPacket::GetAll::Encode() {
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::GetAll));
auto& frame = *reinterpret_cast<WiVI::CommandPacket::GetAll*>(ret.data());
frame.header.cmd = WiVI::Command::GetAll;
frame.header.length = sizeof(frame) - sizeof(frame.header);
return ret;
}
std::vector<uint8_t> WiVI::CommandPacket::ClearUploads::Encode(const std::vector<uint8_t>& bitmask) {
std::vector<uint8_t> ret(sizeof(WiVI::CommandPacket::ClearUploads) + bitmask.size());
auto& frame = *reinterpret_cast<WiVI::CommandPacket::ClearUploads*>(ret.data());
frame.header.cmd = WiVI::Command::ClearUploads;
frame.header.length = uint16_t(ret.size() - sizeof(frame.header));
memcpy(frame.bitmask, bitmask.data(), bitmask.size());
return ret;
}
+82 -27
View File
@@ -1,5 +1,4 @@
#include "icsneo/communication/packetizer.h"
#include <iostream>
#include <iomanip>
using namespace icsneo;
@@ -27,7 +26,8 @@ std::vector<uint8_t>& Packetizer::packetWrap(std::vector<uint8_t>& data, bool sh
bool Packetizer::input(const std::vector<uint8_t>& inputBytes) {
bool haveEnoughData = true;
bytes.insert(bytes.end(), inputBytes.begin(), inputBytes.end());
bytes.Copy(inputBytes);
while(haveEnoughData) {
switch(state) {
@@ -49,44 +49,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 +151,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.CopyTo(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.Erase_front(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;
+1128 -59
View File
File diff suppressed because it is too large Load Diff
+145 -35
View File
@@ -15,10 +15,22 @@
#include "icsneo/platform/cdcacm.h"
#endif
#ifdef ICSNEO_ENABLE_ANDROIDUSB
#include "icsneo/platform/android/androidusb.h"
#endif
#ifdef ICSNEO_ENABLE_FTDI
#include "icsneo/platform/ftdi.h"
#endif
#ifdef ICSNEO_ENABLE_FTD3XX
#include "icsneo/platform/ftd3xx.h"
#endif
#ifdef ICSNEO_ENABLE_TCP
#include "icsneo/platform/tcp.h"
#endif
using namespace icsneo;
template<typename T>
@@ -40,131 +52,209 @@ static void makeIfPIDMatches(const FoundDevice& dev, std::vector<std::shared_ptr
}
std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
static std::vector<FoundDevice> driverFoundDevices;
driverFoundDevices.clear();
static std::vector<FoundDevice> newDriverFoundDevices;
newDriverFoundDevices.clear();
#ifdef ICSNEO_ENABLE_FIRMIO
FirmIO::Find(driverFoundDevices);
FirmIO::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_TCP
TCP::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_RAW_ETHERNET
PCAP::Find(driverFoundDevices);
PCAP::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_CDCACM
CDCACM::Find(driverFoundDevices);
CDCACM::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_ANDROIDUSB
ANDROIDUSB::Find(newDriverFoundDevices);
#endif
#ifdef ICSNEO_ENABLE_FTDI
FTDI::Find(driverFoundDevices);
FTDI::Find(newDriverFoundDevices);
#endif
std::vector<std::shared_ptr<Device>> foundDevices;
#ifdef ICSNEO_ENABLE_FTD3XX
FTD3XX::Find(newDriverFoundDevices);
#endif
// Weak because we don't want to keep devices open if they go out of scope elsewhere
static std::vector<std::weak_ptr<Device>> foundDevices;
// Remove Devices that have dropped out of scope or are no longer present
for (auto it = foundDevices.begin(); it != foundDevices.end(); ) {
if (const auto device = it->lock()) {
if (std::none_of(newDriverFoundDevices.begin(), newDriverFoundDevices.end(),
[&](const auto& driverDevice) {
return std::string(driverDevice.serial) == device->getSerial();
}
)) {
it = foundDevices.erase(it); // Device not found by drivers but pointer has a >0 use_count, error?
} else {
++it; // Valid weak pointer and device found by drivers
}
} else {
it = foundDevices.erase(it); // Weak pointer has a zero use_count
}
}
// Remove existing driver devices so we only create new ones
for (auto it = newDriverFoundDevices.begin(); it != newDriverFoundDevices.end(); ) {
if (std::any_of(foundDevices.begin(), foundDevices.end(),
[&](const auto& weakDevice) {
const auto device = weakDevice.lock();
return device && std::string(it->serial) == device->getSerial();
}
)) {
it = newDriverFoundDevices.erase(it);
} else {
++it;
}
}
std::vector<std::shared_ptr<Device>> newFoundDevices;
newFoundDevices.reserve(newDriverFoundDevices.size());
// Offer found devices to each of the subclasses
for (const FoundDevice& dev : driverFoundDevices) {
for (const FoundDevice& dev : newDriverFoundDevices) {
#ifdef __ETHERBADGE_H_
makeIfSerialMatches<EtherBADGE>(dev, foundDevices);
makeIfSerialMatches<EtherBADGE>(dev, newFoundDevices);
#endif
#ifdef __NEOOBD2PRO_H_
makeIfSerialMatches<NeoOBD2PRO>(dev, foundDevices);
makeIfSerialMatches<NeoOBD2PRO>(dev, newFoundDevices);
#endif
#ifdef __NEOOBD2SIM_H_
makeIfSerialMatches<NeoOBD2SIM>(dev, foundDevices);
makeIfSerialMatches<NeoOBD2SIM>(dev, newFoundDevices);
#endif
#ifdef __NEOVIFIRE_H_
makeIfPIDMatches<NeoVIFIRE>(dev, foundDevices);
makeIfPIDMatches<NeoVIFIRE>(dev, newFoundDevices);
#endif
#ifdef __NEOVIFIRE2_H_
makeIfSerialMatches<NeoVIFIRE2>(dev, foundDevices);
makeIfSerialMatches<NeoVIFIRE2>(dev, newFoundDevices);
#endif
#ifdef __NEOVIFIRE3_H_
makeIfSerialMatches<NeoVIFIRE3>(dev, newFoundDevices);
#endif
#ifdef __NEOVIFIRE3FLEXRAY_H_
makeIfSerialMatches<NeoVIFIRE3FlexRay>(dev, newFoundDevices);
#endif
#ifdef __NEOVIRED2_H_
makeIfSerialMatches<NeoVIRED2>(dev, foundDevices);
makeIfSerialMatches<NeoVIRED2>(dev, newFoundDevices);
#endif
#ifdef __NEOVIION_H_
makeIfPIDMatches<NeoVIION>(dev, foundDevices);
makeIfPIDMatches<NeoVIION>(dev, newFoundDevices);
#endif
#ifdef __NEOVIPLASMA_H_
makeIfPIDMatches<NeoVIPLASMA>(dev, foundDevices);
makeIfPIDMatches<NeoVIPLASMA>(dev, newFoundDevices);
#endif
#ifdef __RADA2B_H_
makeIfSerialMatches<RADA2B>(dev, newFoundDevices);
#endif
#ifdef __RADCOMET_H_
makeIfSerialMatches<RADCOMET>(dev, newFoundDevices);
#endif
#ifdef __RADEPSILON_H_
makeIfSerialMatches<RADEpsilon>(dev, foundDevices);
makeIfSerialMatches<RADEpsilon>(dev, newFoundDevices);
#endif
#ifdef __RADGALAXY_H_
makeIfSerialMatches<RADGalaxy>(dev, foundDevices);
makeIfSerialMatches<RADGalaxy>(dev, newFoundDevices);
#endif
#ifdef __RADMARS_H_
makeIfSerialMatches<RADMars>(dev, foundDevices);
makeIfSerialMatches<RADMars>(dev, newFoundDevices);
#endif
#ifdef __RADGIGASTAR_H_
makeIfSerialMatches<RADGigastar>(dev, foundDevices);
makeIfSerialMatches<RADGigastar>(dev, newFoundDevices);
#endif
#ifdef __RADJUPITER_H_
makeIfSerialMatches<RADJupiter>(dev, newFoundDevices);
#endif
#ifdef __RADMOON2_H_
makeIfSerialMatches<RADMoon2>(dev, foundDevices);
makeIfSerialMatches<RADMoon2>(dev, newFoundDevices);
#endif
#ifdef __RADMOON2ZL_H_
makeIfSerialMatches<RADMoon2ZL>(dev, newFoundDevices);
#endif
#ifdef __RADMOON3_H_
makeIfSerialMatches<RADMoon3>(dev, newFoundDevices);
#endif
#ifdef __RADMOONDUO_H_
makeIfSerialMatches<RADMoonDuo>(dev, foundDevices);
makeIfSerialMatches<RADMoonDuo>(dev, newFoundDevices);
#endif
#ifdef __RADPLUTO_H_
makeIfSerialMatches<RADPluto>(dev, foundDevices);
makeIfSerialMatches<RADPluto>(dev, newFoundDevices);
#endif
#ifdef __RADSTAR2_H_
makeIfSerialMatches<RADStar2>(dev, foundDevices);
makeIfSerialMatches<RADStar2>(dev, newFoundDevices);
#endif
#ifdef __RADSUPERMOON_H_
makeIfSerialMatches<RADSupermoon>(dev, foundDevices);
makeIfSerialMatches<RADSupermoon>(dev, newFoundDevices);
#endif
#ifdef __VALUECAN3_H_
makeIfPIDMatches<ValueCAN3>(dev, foundDevices);
makeIfPIDMatches<ValueCAN3>(dev, newFoundDevices);
#endif
#ifdef __VALUECAN4_1_H_
makeIfSerialMatches<ValueCAN4_1>(dev, foundDevices);
makeIfSerialMatches<ValueCAN4_1>(dev, newFoundDevices);
#endif
#ifdef __VALUECAN4_2_H_
makeIfSerialMatches<ValueCAN4_2>(dev, foundDevices);
makeIfSerialMatches<ValueCAN4_2>(dev, newFoundDevices);
#endif
#ifdef __VALUECAN4_2EL_H_
makeIfSerialMatches<ValueCAN4_2EL>(dev, foundDevices);
makeIfSerialMatches<ValueCAN4_2EL>(dev, newFoundDevices);
#endif
#ifdef __VALUECAN4_4_H_
makeIfSerialMatches<ValueCAN4_4>(dev, foundDevices);
makeIfSerialMatches<ValueCAN4_4>(dev, newFoundDevices);
#endif
#ifdef __VALUECAN4INDUSTRIAL_H_
makeIfSerialMatches<ValueCAN4Industrial>(dev, foundDevices);
makeIfSerialMatches<ValueCAN4Industrial>(dev, newFoundDevices);
#endif
#ifdef __VIVIDCAN_H_
makeIfSerialMatches<VividCAN>(dev, foundDevices);
makeIfSerialMatches<VividCAN>(dev, newFoundDevices);
#endif
}
for(auto& device : foundDevices) {
for(auto& device : newFoundDevices) {
AddBuiltInExtensionsTo(device);
}
return foundDevices;
// Grab a weak pointer from the new devices
foundDevices.insert(foundDevices.end(), newFoundDevices.begin(), newFoundDevices.end());
// Upgrade to shared for the return
return std::vector<std::shared_ptr<Device>>(foundDevices.begin(), foundDevices.end());
}
const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
@@ -194,6 +284,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
@@ -202,6 +300,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
NeoVIPLASMA::DEVICE_TYPE,
#endif
#ifdef __RADA2B_H_
RADA2B::DEVICE_TYPE,
#endif
#ifdef __RADCOMET_H_
RADCOMET::DEVICE_TYPE,
#endif
#ifdef __RADEPSILON_H_
RADEpsilon::DEVICE_TYPE,
#endif
@@ -218,10 +324,14 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
RADGigastar::DEVICE_TYPE,
#endif
#ifdef __RADMOON2_H_
#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
+9 -9
View File
@@ -4,11 +4,11 @@
using namespace icsneo;
optional<uint16_t> IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settings, optional<size_t> knownSize) {
std::optional<uint16_t> IDeviceSettings::CalculateGSChecksum(const std::vector<uint8_t>& settings, std::optional<size_t> knownSize) {
const uint16_t* p = reinterpret_cast<const uint16_t*>(settings.data());
size_t words = std::min(knownSize.value_or(0), settings.size());
if(words % 2 == 1)
return nullopt; // Somehow settings is not word aligned
return std::nullopt; // Somehow settings is not word aligned
words /= 2;
uint16_t gsCrc = 0;
@@ -215,7 +215,7 @@ bool IDeviceSettings::apply(bool temporary) {
bytestream[2] = GS_VERSION >> 8;
bytestream[3] = (uint8_t)settings.size();
bytestream[4] = (uint8_t)(settings.size() >> 8);
optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
std::optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
if(!gsChecksum) {
// Could not calculate the checksum for some reason
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
@@ -321,7 +321,7 @@ bool IDeviceSettings::applyDefaults(bool temporary) {
bytestream[2] = GS_VERSION >> 8;
bytestream[3] = (uint8_t)settings.size();
bytestream[4] = (uint8_t)(settings.size() >> 8);
const optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
const std::optional<uint16_t> gsChecksum = CalculateGSChecksum(settings);
if(!gsChecksum) {
// Could not calculate the checksum for some reason
report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error);
@@ -638,27 +638,27 @@ bool IDeviceSettings::canTerminationBeEnabledFor(Network net) const {
return false;
}
optional<bool> IDeviceSettings::isTerminationEnabledFor(Network net) const {
std::optional<bool> IDeviceSettings::isTerminationEnabledFor(Network net) const {
if(!settingsLoaded) {
report(APIEvent::Type::SettingsReadError, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
if(disabled) {
report(APIEvent::Type::SettingsNotAvailable, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
ICSNEO_UNALIGNED(const uint64_t*) terminationEnables = getTerminationEnables();
if(terminationEnables == nullptr) {
report(APIEvent::Type::TerminationNotSupportedDevice, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
const auto cmNet = net.getCoreMini();
if(!cmNet.has_value() || uint64_t(*cmNet) >= 64 || !isTerminationSupportedFor(net)) {
report(APIEvent::Type::TerminationNotSupportedNetwork, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
return (*terminationEnables >> uint64_t(*cmNet)) & 0x1;
+62 -6
View File
@@ -4,19 +4,34 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
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, 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;
optional<uint64_t> ret;
pos += vsaOffset;
// First read from the cache
std::optional<uint64_t> ret = readFromCache(pos, into, amount);
if(ret == amount) // Full cache hit, we're done
return ret;
const uint64_t totalAmount = amount;
if(ret.has_value()) { // Partial cache hit
pos += *ret;
into += *ret;
amount -= *ret;
}
// Read into here if we can't read directly into the user buffer
// That would be the case either if we don't want some at the
// beginning or end of the block.
std::vector<uint8_t> alignedReadBuffer;
pos += vsaOffset;
const uint32_t idealBlockSize = getBlockSizeBounds().second;
const uint64_t startBlock = pos / idealBlockSize;
const uint32_t posWithinFirstBlock = static_cast<uint32_t>(pos % idealBlockSize);
@@ -36,7 +51,7 @@ optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventh
const uint32_t posWithinCurrentBlock = (blocksProcessed ? 0 : posWithinFirstBlock);
uint32_t curAmt = idealBlockSize - posWithinCurrentBlock;
const auto amountLeft = amount - ret.value_or(0);
const auto amountLeft = totalAmount - ret.value_or(0);
if(curAmt > amountLeft)
curAmt = static_cast<uint32_t>(amountLeft);
@@ -46,7 +61,7 @@ optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventh
auto start = std::chrono::high_resolution_clock::now();
auto 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) {
@@ -65,7 +80,48 @@ optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventh
ret.emplace();
*ret += std::min<uint64_t>(*readAmount, curAmt);
blocksProcessed++;
if(blocksProcessed == blocks) {
// Last block, add to the cache
if(useAlignedReadBuffer) {
cache = std::move(alignedReadBuffer);
} else {
if(cache.size() != idealBlockSize)
cache.resize(idealBlockSize);
memcpy(cache.data(), into + intoOffset, idealBlockSize);
}
cachePos = currentBlock * idealBlockSize;
cachedAt = std::chrono::steady_clock::now();
}
}
return ret;
}
void ReadDriver::invalidateCache(uint64_t pos, uint64_t amount, MemoryType memType) {
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
if(pos <= cachePos + cache.size() && pos + amount >= cachePos)
cache.clear();
}
std::optional<uint64_t> ReadDriver::readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter, MemoryType memType) {
std::vector<uint8_t>& cache = memType == Disk::MemoryType::SD ? cacheSD : cacheEEPROM;
uint64_t cachePos = memType == Disk::MemoryType::SD ? cachePosSD : cachePosEEPROM;
if(cache.empty())
return std::nullopt; // Nothing in the cache
if(cachedAt + staleAfter < std::chrono::steady_clock::now())
return std::nullopt; // Cache is stale
if(pos > cachePos + cache.size() || pos < cachePos)
return std::nullopt; // Cache miss
const auto cacheOffset = pos - cachePos;
const auto copyAmount = std::min<uint64_t>(cache.size() - cacheOffset, amount);
memcpy(into, cache.data() + cacheOffset, static_cast<size_t>(copyAmount));
return copyAmount;
}
+17 -26
View File
@@ -4,26 +4,19 @@
using namespace icsneo;
using namespace icsneo::Disk;
const uint64_t WriteDriver::RetryAtomic = std::numeric_limits<uint64_t>::max();
const APIEvent::Severity WriteDriver::NonatomicSeverity = APIEvent::Severity::EventInfo;
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) {
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, MemoryType memType) {
if(amount == 0)
return 0;
optional<uint64_t> ret;
std::optional<uint64_t> ret;
const uint32_t idealBlockSize = getBlockSizeBounds().second;
// Write from here if we need to read-modify-write a block
// That would be the case either if we don't want some at the
// beginning or end of the block.
std::vector<uint8_t> alignedWriteBuffer;
// Read to here, ideally this can be sent back to the device to
// ensure an operation is atomic
std::vector<uint8_t> atomicBuffer(idealBlockSize);
std::vector<uint8_t> alignedWriteBuffer(idealBlockSize);
pos += vsaOffset;
const uint64_t startBlock = pos / idealBlockSize;
@@ -54,30 +47,24 @@ optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_even
t = APIEvent::Type::EOFReached;
report(t, s);
};
auto bytesTransferred = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
atomicBuffer.data(), idealBlockSize, timeout);
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
if(bytesTransferred != idealBlockSize)
break; // readLogicalDisk reports its own errors
const bool useAlignedWriteBuffer = (posWithinCurrentBlock != 0 || curAmt != idealBlockSize);
if(useAlignedWriteBuffer) {
alignedWriteBuffer = atomicBuffer;
auto read = readDriver.readLogicalDisk(com, reportFromRead, currentBlock * idealBlockSize,
alignedWriteBuffer.data(), idealBlockSize, timeout, memType);
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
if(read != idealBlockSize)
break; // readLogicalDisk reports its own errors
memcpy(alignedWriteBuffer.data() + posWithinCurrentBlock, from + fromOffset, curAmt);
}
start = std::chrono::high_resolution_clock::now();
bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize, atomicBuffer.data(),
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout);
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
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 == RetryAtomic) {
// The user may want to log these events in order to see how many atomic misses they are getting
report(APIEvent::Type::AtomicOperationRetried, APIEvent::Severity::EventInfo);
continue;
}
if(!bytesTransferred.has_value() || *bytesTransferred < curAmt) {
if(timeout < std::chrono::milliseconds::zero())
report(APIEvent::Type::Timeout, APIEvent::Severity::Error);
@@ -93,5 +80,9 @@ optional<uint64_t> WriteDriver::writeLogicalDisk(Communication& com, device_even
blocksProcessed++;
}
// No matter how much succeeded, to be safe, we'll invalidate anything
// we may have even tried to write, since it may have succeeded without
// notifying, etc.
readDriver.invalidateCache(pos, amount, memType);
return ret;
}
+66 -182
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
@@ -12,202 +9,89 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
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, MemoryType memType) {
if(amount > getBlockSizeBounds().second)
return nullopt;
return std::nullopt;
if(amount % getBlockSizeBounds().first != 0)
return nullopt;
return std::nullopt;
if(pos % getBlockSizeBounds().first != 0)
return nullopt;
return std::nullopt;
optional<uint64_t> ret;
while(timeout > std::chrono::milliseconds(0) && !ret.has_value()) {
auto start = std::chrono::steady_clock::now();
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, std::chrono::milliseconds(100));
timeout -= std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
std::optional<uint64_t> ret;
unsigned int attempts = 4;
while (attempts-- > 0)
{
ret = attemptReadLogicalDiskAligned(com, report, pos, into, amount, timeout, memType);
if (ret.has_value())
break;
}
return ret;
}
optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
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, MemoryType) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
if(cachePos != pos || std::chrono::steady_clock::now() > cachedAt + CacheTime) {
uint64_t sector = pos / SectorSize;
uint64_t sector = pos / SectorSize;
uint64_t largeSectorCount = amount / SectorSize;
uint32_t sectorCount = uint32_t(largeSectorCount);
if (largeSectorCount != uint64_t(sectorCount))
return nullopt;
uint64_t largeSectorCount = amount / SectorSize;
uint32_t sectorCount = uint32_t(largeSectorCount);
if (largeSectorCount != uint64_t(sectorCount))
return std::nullopt;
// The cache does not have this data, go get it
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();
std::condition_variable cv;
std::mutex mutex;
uint8_t* intoOffset = into;
int64_t remaining = amount;
bool complete = false;
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;
}
cv.notify_all();
return;
}
}
}, std::make_shared<MessageFilter>(Network::NetID::DiskData)));
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;
}
// Did we get a correct header and at least one byte of data?
const auto begin = data.begin() + offset;
int32_t headerLeft = int32_t(HeaderLength - header.size());
if(int32_t(left) < headerLeft) {
// Not enough data here, grab what header we can and continue
header.insert(header.end(), begin, data.end());
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << int(left) << " bytes of header at " << offset << " (incomplete " <<
header.size() << ')' << std::endl;
#endif
return;
}
header.insert(header.end(), begin, begin + headerLeft);
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << int(headerLeft) << " bytes of header at " << offset << " (complete " <<
header.size() << ')' << std::endl;
#endif
offset += headerLeft;
if(header[1] == uint8_t(Network::NetID::RED)) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got extended response " << int(offset) << std::endl;
#endif
// This is the extended command response, not all devices send this
// If we got it, we need to figure out how much more data to ignore
uint16_t length = (header[2] + (header[3] << 8));
// Try for another header after this, regardless how much we choose
// to skip and how we skip it
header.clear();
if(length <= 6) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Incorrect extended response length " << int(length) << ' ' << int(offset) << std::endl;
#endif
error = true;
lk2.unlock();
cv.notify_all();
return;
}
length -= 7;
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Skipping " << int(length) << ' ' << int(left) << std::endl;
#endif
if(left < length) {
skipping = length - left;
return;
}
offset += length;
continue;
}
// The device tells us how much it's sending us before the next header
receiving = (header[5] | (header[6] << 8));
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Started packet of size " << receiving << " bytes" << std::endl;
#endif
}
left = data.size() - offset;
auto count = uint16_t(std::min<uint64_t>(std::min<uint64_t>(receiving - receivedCurrent, left), amount - received));
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "With " << int(left) << " bytes " << int(offset) << std::endl;
#endif
memcpy(cache.data() + received, data.data() + offset, count);
received += count;
receivedCurrent += count;
offset += count;
if(amount == received) {
if(receivedCurrent % 2 == 0)
offset++;
header.clear(); // Now we will need another header
lk2.unlock();
cv.notify_all();
lk2.lock();
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Finished!" << std::endl;
#endif
}
else if(receivedCurrent == receiving) {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << count << " bytes, " << receivedCurrent << " byte packet " << received <<
" complete of " << amount << std::endl;
#endif
if(receivedCurrent % 2 == 0)
offset++;
header.clear(); // Now we will need another header
receivedCurrent = 0;
} else {
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "Got " << count << " bytes, incomplete (of " << receiving << " bytes)" << std::endl;
#endif
}
}
});
Lifetime clearRedirect([&com, &lk] { lk.unlock(); com.clearRedirectRead(); });
if(error)
return nullopt;
error = !com.sendCommand(ExtendedCommand::Extract, {
uint8_t(sector & 0xff),
uint8_t((sector >> 8) & 0xff),
uint8_t((sector >> 16) & 0xff),
uint8_t((sector >> 24) & 0xff),
uint8_t((sector >> 32) & 0xff),
uint8_t((sector >> 40) & 0xff),
uint8_t((sector >> 48) & 0xff),
uint8_t((sector >> 56) & 0xff),
uint8_t(sectorCount & 0xff),
uint8_t((sectorCount >> 8) & 0xff),
uint8_t((sectorCount >> 16) & 0xff),
uint8_t((sectorCount >> 24) & 0xff),
});
if(error)
return nullopt;
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
if(hitTimeout || error)
return nullopt;
cachedAt = std::chrono::steady_clock::now();
cachePos = pos;
if(!com.sendCommand(ExtendedCommand::Extract, {
uint8_t(sector & 0xff),
uint8_t((sector >> 8) & 0xff),
uint8_t((sector >> 16) & 0xff),
uint8_t((sector >> 24) & 0xff),
uint8_t((sector >> 32) & 0xff),
uint8_t((sector >> 40) & 0xff),
uint8_t((sector >> 48) & 0xff),
uint8_t((sector >> 56) & 0xff),
uint8_t(sectorCount & 0xff),
uint8_t((sectorCount >> 8) & 0xff),
uint8_t((sectorCount >> 16) & 0xff),
uint8_t((sectorCount >> 24) & 0xff),
})) {
return std::nullopt;
}
memcpy(into, cache.data(), size_t(amount));
return amount;
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;
return amount - remaining;
}
+4 -4
View File
@@ -8,7 +8,7 @@ using namespace icsneo;
// The FAT driver can only be accessed by one caller at a time, since it relies on globals
static std::mutex fatDriverMutex;
static std::function< optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskReadFn;
static std::function< std::optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskReadFn;
extern "C" DRESULT disk_read(BYTE, BYTE* buff, LBA_t sector, UINT count) {
static_assert(Disk::SectorSize == 512, "FatFs expects 512 byte sectors");
@@ -32,17 +32,17 @@ static uint64_t ClusterToSector(const FATFS& fs, DWORD cluster) {
return fs.database + (LBA_t)fs.csize * (cluster - 2);
}
optional<uint64_t> Disk::FindVSAInFAT(std::function< optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskRead) {
std::optional<uint64_t> Disk::FindVSAInFAT(std::function< std::optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskRead) {
std::lock_guard<std::mutex> lk(fatDriverMutex);
diskReadFn = diskRead;
FATFS fs = {};
if (f_mount(&fs, (const TCHAR*)_TEXT(""), 0) != FR_OK)
return nullopt;
return std::nullopt;
FIL logData = {};
if (f_open(&logData, (const TCHAR*)_TEXT("0:\\LOG_DATA.VSA"), FA_READ) != FR_OK)
return nullopt;
return std::nullopt;
return ClusterToSector(fs, logData.obj.sclust) * uint64_t(Disk::SectorSize);
}
+37 -48
View File
@@ -1,79 +1,68 @@
#include "icsneo/disk/neomemorydiskdriver.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include <cstring>
#include <iostream>
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
if(pos % SectorSize != 0)
return nullopt;
return std::nullopt;
if(amount != SectorSize)
return nullopt;
return std::nullopt;
if(cachePos != pos || std::chrono::steady_clock::now() > cachedAt + CacheTime) {
// The cache does not have this data, go get it
const uint64_t currentSector = pos / SectorSize;
auto msg = com.waitForMessageSync([&currentSector, &com] {
return com.sendCommand(Command::NeoReadMemory, {
MemoryTypeSD,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
uint8_t((currentSector >> 24) & 0xFF),
uint8_t(SectorSize & 0xFF),
uint8_t((SectorSize >> 8) & 0xFF),
uint8_t((SectorSize >> 16) & 0xFF),
uint8_t((SectorSize >> 24) & 0xFF)
});
}, NeoMemorySDRead, timeout);
const uint64_t currentSector = pos / SectorSize;
const uint8_t memLocation = (uint8_t)memType;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com] {
return com.sendCommand(Command::NeoReadMemory, {
memLocation,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
uint8_t((currentSector >> 24) & 0xFF),
uint8_t(SectorSize & 0xFF),
uint8_t((SectorSize >> 8) & 0xFF),
uint8_t((SectorSize >> 16) & 0xFF),
uint8_t((SectorSize >> 24) & 0xFF)
});
}, NeoMemorySDRead, timeout);
if(!msg)
return 0;
if(!msg)
return 0;
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!sdmsg || sdmsg->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return nullopt;
}
memcpy(cache.data(), sdmsg->data.data(), SectorSize);
cachedAt = std::chrono::steady_clock::now();
cachePos = pos;
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!sdmsg || sdmsg->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
}
memcpy(into, cache.data(), SectorSize);
memcpy(into, sdmsg->data.data(), SectorSize);
return SectorSize;
}
optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, const uint8_t* atomicBuf, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) {
std::optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
static std::shared_ptr<MessageFilter> NeoMemoryDone = std::make_shared<MessageFilter>(Network::NetID::NeoMemoryWriteDone);
if(pos % SectorSize != 0)
return nullopt;
return std::nullopt;
if(amount != SectorSize)
return nullopt;
// Clear the cache if we're writing to the cached sector
if(pos == cachePos)
cachedAt = std::chrono::time_point<std::chrono::steady_clock>();
// Requesting an atomic operation, but neoMemory does not support it
// Continue on anyway but warn the caller
if(atomicBuf != nullptr)
report(APIEvent::Type::AtomicOperationCompletedNonatomically, NonatomicSeverity);
return std::nullopt;
const uint64_t currentSector = pos / SectorSize;
auto msg = com.waitForMessageSync([&currentSector, &com, from, amount] {
const uint8_t memLocation = (uint8_t)memType;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, from, amount] {
std::vector<uint8_t> command = {
MemoryTypeSD,
memLocation,
uint8_t(currentSector & 0xFF),
uint8_t((currentSector >> 8) & 0xFF),
uint8_t((currentSector >> 16) & 0xFF),
@@ -86,7 +75,7 @@ optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& c
}, NeoMemoryDone, timeout);
if(!msg)
return nullopt;
return std::nullopt;
return SectorSize;
}
+12 -12
View File
@@ -3,26 +3,26 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
std::optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
std::optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
std::optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, const uint8_t*, const uint8_t*, uint64_t, std::chrono::milliseconds) {
std::optional<uint64_t> NullDriver::writeLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds, MemoryType) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
+43 -52
View File
@@ -6,73 +6,64 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
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, MemoryType) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
if(amount > getBlockSizeBounds().second)
return nullopt;
return std::nullopt;
if(amount % getBlockSizeBounds().first != 0)
return nullopt;
return std::nullopt;
if(pos % getBlockSizeBounds().first != 0)
return nullopt;
return std::nullopt;
if(cachePos != pos || std::chrono::steady_clock::now() > cachedAt + CacheTime) {
uint64_t largeSector = pos / SectorSize;
uint32_t sector = uint32_t(largeSector);
if (largeSector != uint64_t(sector))
return nullopt;
uint64_t largeSector = pos / SectorSize;
uint32_t sector = uint32_t(largeSector);
if (largeSector != uint64_t(sector))
return std::nullopt;
// The cache does not have this data, go get it
std::mutex m;
std::condition_variable cv;
uint32_t copied = 0;
bool error = false;
std::mutex m;
std::condition_variable cv;
uint32_t copied = 0;
bool error = false;
std::unique_lock<std::mutex> lk(m);
auto cb = com.addMessageCallback(std::make_shared<MessageCallback>([&](std::shared_ptr<Message> msg) {
std::unique_lock<std::mutex> lk(m);
auto cb = com.addMessageCallback(MessageCallback([&](std::shared_ptr<Message> msg) {
std::unique_lock<std::mutex> lk(m);
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!sdmsg || cache.size() < copied + sdmsg->data.size()) {
error = true;
lk.unlock();
cv.notify_all();
return;
}
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!sdmsg || amount < copied + sdmsg->data.size()) {
error = true;
lk.unlock();
cv.notify_all();
return;
}
// Invalidate the cache here in case we fail half-way through
cachedAt = std::chrono::steady_clock::time_point();
memcpy(into + copied, sdmsg->data.data(), sdmsg->data.size());
copied += uint32_t(sdmsg->data.size());
if(copied == amount) {
lk.unlock();
cv.notify_all();
}
}, NeoMemorySDRead));
memcpy(cache.data() + copied, sdmsg->data.data(), sdmsg->data.size());
copied += uint32_t(sdmsg->data.size());
if(copied == amount) {
lk.unlock();
cv.notify_all();
}
}, NeoMemorySDRead));
com.rawWrite({
uint8_t(MultiChannelCommunication::CommandType::HostPC_from_SDCC1),
uint8_t(sector & 0xFF),
uint8_t((sector >> 8) & 0xFF),
uint8_t((sector >> 16) & 0xFF),
uint8_t((sector >> 24) & 0xFF),
uint8_t(amount & 0xFF),
uint8_t((amount >> 8) & 0xFF),
});
com.rawWrite({
uint8_t(MultiChannelCommunication::CommandType::HostPC_from_SDCC1),
uint8_t(sector & 0xFF),
uint8_t((sector >> 8) & 0xFF),
uint8_t((sector >> 16) & 0xFF),
uint8_t((sector >> 24) & 0xFF),
uint8_t(amount & 0xFF),
uint8_t((amount >> 8) & 0xFF),
});
bool hitTimeout = !cv.wait_for(lk, timeout, [&copied, &error, &amount] { return error || copied == amount; });
com.removeMessageCallback(cb);
bool hitTimeout = !cv.wait_for(lk, timeout, [&copied, &error, &amount] { return error || copied == amount; });
com.removeMessageCallback(cb);
if(hitTimeout)
return std::nullopt;
if(hitTimeout)
return nullopt;
cachedAt = std::chrono::steady_clock::now();
cachePos = pos;
}
memcpy(into, cache.data(), size_t(amount));
return amount;
}
+15
View File
@@ -100,3 +100,18 @@ Write Blocking Status
The write blocking status of the device determines the behavior of attempting to transmit to the device (likely via sending messages) with a large backlog of messages.
If write blocking is enabled, then the transmitting thread will wait for the entire buffer to be transmitted.
If write blocking is disabled, then the attempt to transmit will simply fail and an error will be logged on the calling thread.
A2B Wave Output
~~~~~~~~~~~~~~~~~~~~
Users may add a ``icsneo::A2BWAVOutput`` message callback to their device in order to write A2B PCM data to a WAVE file. The message callback listens for ``icsneo::A2BMessage``
messages and writes both downstream and upstream channels to a single wave file. If downstream and upstream each have ``32`` channels, the wave file will contain ``2*32 = 64``
total channels. Channels are indexed at 0 and interleaved such that downstream are on even number channels and upstream on odd number channels. If we introduce a
variable ``IS_UPSTREAM`` which is ``0`` when downstream and ``1`` when upstream and desired a channel ``CHANNEL_NUM`` the corresponding channel in the wave file would be
``2*CHANNEL_NUM + IS_UPSTREAM``.
Wave files may be split by channel using programs such as ``FFmpeg``. Consider a file ``out.wav`` which was generated using a ``icsneo::A2BWAVOutput`` object
and contains ``32`` channels per stream. The ``icsneo::A2BWavoutput`` object injested PCM data with a sample rate of ``44.1 kHz`` and bit depth of ``24``. The corresponding
channel of upstream channel ``8`` in ``out.wav`` would be ``2*CHANNEL_NUM + IS_UPSTREAM = 2*8 + 1 = 17``. The following ``FFmpeg`` command may be ran in a linux environment to create a new wave
file ``out_upstream_ch8.wav`` which contains only PCM samples off of upstream channel ``8``.
``ffmpeg -i out.wav -ar 44100 -acodec pcm_s24le -map_channel 0.0.17 out_upstream_ch8.wav``
+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-2023, 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-2023, Intrepid Control Systems, Inc.'
author = 'Intrepid Control Systems, Inc.'
# The version info for the project you're documenting, acts as replacement for
+35
View File
@@ -1,6 +1,13 @@
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)
# Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
@@ -10,6 +17,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()
@@ -18,6 +33,26 @@ if(LIBICSNEO_BUILD_CPP_INTERACTIVE_EXAMPLE)
add_subdirectory(cpp/interactive)
endif()
if(LIBICSNEO_BUILD_CPP_A2B_EXAMPLE)
add_subdirectory(cpp/a2b)
endif()
if(LIBICSNEO_BUILD_CPP_LIN_EXAMPLE)
add_subdirectory(cpp/lin)
endif()
if(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE)
add_subdirectory(cpp/livedata)
endif()
if(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE)
add_subdirectory(cpp/coremini)
endif()
if(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE)
add_subdirectory(cpp/mdio)
endif()
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp)
# endif()
+2 -1
View File
@@ -13,4 +13,5 @@ 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)
+42 -17
View File
@@ -1,5 +1,5 @@
// Signal to dynamically load the library
#define ICSNEOC_DYNAMICLOAD
//#define ICSNEOC_DYNAMICLOAD
#include <stdio.h>
#include <stdlib.h>
@@ -219,21 +219,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 +487,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 +787,7 @@ int main() {
case 'X':
case 'x':
printf("Exiting program\n");
return !icsneo_close();
return 0;
default:
printf("Unexpected input, exiting!\n");
return 1;
+7
View File
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneoc-legacy-lin-example VERSION 0.2.0)
add_executable(libicsneoc-legacy-lin-example lin/main.c)
add_executable(libicsneoc-legacy-device-settings-example deviceSettings/main.c)
target_link_libraries(libicsneoc-legacy-lin-example icsneolegacy)
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;
+96
View File
@@ -0,0 +1,96 @@
/*
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 = 10;
NeoDevice devices[10];
void* hObject; // holds a handle to the neoVI object
int iRetVal = 0;
int deviceTypes = 0;
int iResult = 0;
SDeviceSettings pSettings;
iRetVal = icsneoFindNeoDevices(deviceTypes, devices, &numDevices);
if(iRetVal) {
// Attempt to open the selected device, enable message polling, and go online
iRetVal = icsneoOpenNeoDevice(&devices[0], &hObject, NULL, 1, 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;
}
+165
View File
@@ -0,0 +1,165 @@
#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 = 10;
NeoDevice devices[10];
void* hObject; // holds a handle to the neoVI object
int iRetVal = 0;
int deviceTypes = 0;
int iResult = 0;
iRetVal = icsneoFindNeoDevices(deviceTypes, devices, &numDevices);
if(iRetVal) {
// Attempt to open the selected device, enable message polling, and go online
iRetVal = icsneoOpenNeoDevice(&devices[0], &hObject, NULL, 1, 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;
}
+17
View File
@@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneoc-simple-lin-example VERSION 0.2.0)
include(GNUInstallDirs)
# Include libicsneo's include directory
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
if(UNIX)
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
endif()
add_executable(libicsneoc-simple-lin-example lin/main.c)
if(UNIX)
target_link_libraries(libicsneoc-simple-lin-example ${CMAKE_DL_LIBS})
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;
+427
View File
@@ -0,0 +1,427 @@
// 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) {
fgets(input, 99, stdin);
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;
}
+27
View File
@@ -0,0 +1,27 @@
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)
+356
View File
@@ -0,0 +1,356 @@
// libicsneo A2B example
// Example must be ran with rada2b as slave on TDM4 32 bit channel size and one ADI master node
// Options:
// -h, --help Display help message.
// -e, --example [EXAMPLE_NUM] Example to run.
// Example usage: ./libicsneocpp-a2b.exe --example 1
// Example usage: ./libicsneocpp-a2b.exe -h
#include <iostream>
#include <fstream>
#include <icsneo/icsneocpp.h>
#include <icsneo/device/tree/rada2b/rada2bsettings.h>
#include <icsneo/communication/message/callback/streamoutput/a2bdecoder.h>
#include <string>
#include <math.h>
static constexpr size_t numFramesInWave = 48;
std::string makeWave() {
icsneo::WaveFileHeader header = icsneo::WaveFileHeader(1, 48000, 24);
std::vector<uint8_t> sineWaveSamples = {
0x00, 0x2B, 0x98, 0x08, 0x25, 0x01, 0x10, 0xD3, 0xEF, 0x18, 0x40, 0xA3, 0x20, 0x33, 0x4C, 0x26,
0xCE, 0xCA, 0x2D, 0x5B, 0x41, 0x32, 0xB9, 0x3C, 0x37, 0x6E, 0x50, 0x3B, 0x29, 0x18, 0x3D, 0xC2,
0x96, 0x3F, 0x86, 0xD3, 0x3F, 0xEC, 0x35, 0x3F, 0x85, 0xA7, 0x3D, 0xC4, 0x19, 0x3B, 0x28, 0xC9,
0x37, 0x6B, 0x5A, 0x32, 0xC1, 0xC4, 0x2D, 0x4A, 0xEE, 0x26, 0xE8, 0xB1, 0x20, 0x0E, 0xCF, 0x18,
0x6F, 0xAA, 0x10, 0x9C, 0x17, 0x08, 0x53, 0x35, 0x00, 0x01, 0xFD, 0xF7, 0xA9, 0x29, 0xEF, 0x66,
0x87, 0xE7, 0x8F, 0x37, 0xDF, 0xF0, 0x8A, 0xD9, 0x19, 0xB3, 0xD2, 0xB1, 0x3E, 0xCD, 0x42, 0xE9,
0xC8, 0x8F, 0xE0, 0xC4, 0xDB, 0x39, 0xC2, 0x39, 0x78, 0xC0, 0x7A, 0x8A, 0xC0, 0x16, 0x38, 0xC0,
0x74, 0x18, 0xC2, 0x44, 0xBF, 0xC4, 0xCE, 0x26, 0xC8, 0x9A, 0x99, 0xCD, 0x3F, 0x53, 0xD2, 0xA8,
0xBD, 0xD9, 0x32, 0xF5, 0xDF, 0xC2, 0x68, 0xE7, 0xD5, 0xFD, 0xEF, 0x02, 0x15, 0xF8, 0x3B, 0x33,
};
std::vector<uint8_t> sineWave;
sineWave.reserve(sineWaveSamples.size() + sizeof(header));
sineWave.insert(sineWave.begin(), (uint8_t*)&header, (uint8_t*)(&header) + sizeof(header));
std::copy(sineWaveSamples.begin(), sineWaveSamples.end(), std::back_inserter(sineWave));
return std::string(sineWave.begin(), sineWave.end());
}
// Example 0: TX
void example0(std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Transmitting a sine wave..." << std::endl;
// Create sine tone in wave format
std::string waveString = makeWave();
// Audio map to map which channel in wave to stream on a2b bus.
icsneo::A2BAudioChannelMap a2bmap(4);
a2bmap.set(
2, // Channel on a2b bus
icsneo::A2BMessage::A2BDirection::Downstream, // Direction
0 // Channel in wave file
);
a2bmap.setAll(0);
icsneo::A2BDecoder decoder(
std::make_unique<std::istringstream>(waveString), // Wave file stream
false, // True when using 16 bit samples
a2bmap
);
double elapsedTime = 0.0;
// Play roughly 5 seconds of sine tone.
while(elapsedTime < 5.0) {
decoder.outputAll(rada2b); // Output entire wave file
elapsedTime += (static_cast<double>(numFramesInWave)) * 1.0/48000.0;
decoder.stream->clear();
decoder.stream->seekg(0, std::ios::beg);
// Also outputs entire wave file
while(decoder && elapsedTime < 5.0) {
auto msg = decoder.decode();
rada2b->transmit(msg);
elapsedTime += (static_cast<double>(msg->getNumFrames()))*1.0/48000.0;
}
decoder.stream->clear();
decoder.stream->seekg(0, std::ios::beg);
}
}
// Example 1: RX
void example1(std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Receiving 5 seconds of audio data..." << std::endl;
// Add WAV output message callback
// Saves samples to "out.wav"
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("out.wav", 48000));
// Sleep this thread for 5 seconds, message callback still runs
std::this_thread::sleep_for(std::chrono::seconds(5));
// Remove callback
rada2b->removeMessageCallback(handler);
}
// Example 2: RADA2B settings
void example2(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::TDM8);
// Apply local settings to device
rada2bSettings->apply();
}
}
// Example 3: A2BMessage API
void example3() {
icsneo::A2BMessage msg = icsneo::A2BMessage(4, false, 2048); // Create new A2BMessage
msg[0][0] = 60; // Set sample using operator[][]
msg[0][3] = 60; // Frame 0, channel 2 upstream
msg[6][2] = 32; // Frame 6, channel 1 downstream
// Equivalent to last line
msg.setSample(icsneo::A2BMessage::A2BDirection::Downstream, 1, 6, 32);
// Get sample
std::cout << "Channel 1 downstream sample for frame 6: " << msg.getSample(icsneo::A2BMessage::A2BDirection::Downstream, 1, 6).value() << std::endl;
// Get number of frames
auto numFrames = msg.getNumFrames();
std::cout << "Num frames: " << numFrames << std::endl;
icsneo::A2BPCMSample sample1 = 40;
icsneo::A2BPCMSample sample2 = 60;
msg.fill(sample1); // Fill whole message with sample 40
msg.fillFrame(sample2, numFrames/2); // Fill frame numFrames/2 with sample2
// Print msg sample contents
std::cout << "A2B message contents:" << std::endl;
for(size_t y = 0; y < numFrames; y++) {
for(size_t x = 0; x < ((size_t)(msg.getNumChannels())*2); x++) { // Num channels including upstream and downstream
std::cout << msg[y][x] << " ";
}
std::cout << std::endl;
}
// Set and get bits
msg.setSyncFrameBit(true);
std::cout << "Was received from monitor: " << msg.isMonitorMsg() << std::endl;
}
// Example 4: Packaging and transmitting sine wave using A2BMessage API
void example4(std::shared_ptr<icsneo::Device>& rada2b) {
std::cout << "Transmitting a 1000 hz sine wave." << std::endl;
float deltaTime = static_cast<float>(1.0/48000.0);
float elapsedTime = 0.0;
float twoPI = static_cast<float>(2.0*atan(1.0)*4.0);
float frequency = 1000;
float amplitude = static_cast<float>((1 << 23) - 1);
uint8_t icsChannel = 0; // Play audio on channel 2, upstream, see docs for details
// Play for roughly 5 seconds
while(elapsedTime < 5.0) {
// Allocate message
std::shared_ptr<icsneo::A2BMessage> a2bmsgPtr = std::make_shared<icsneo::A2BMessage>(static_cast<uint8_t>(4), false, static_cast<size_t>(2048));
icsneo::A2BMessage& a2bmsg = *a2bmsgPtr.get();
a2bmsg.network = icsneo::Network(icsneo::Network::NetID::A2B2);
for(size_t frame = 0; frame < a2bmsg.getNumFrames(); frame++) {
// Sine wave sample, amplitude 1000, frequency 1000 hz
float contSample = amplitude*sin(twoPI*frequency*elapsedTime);
icsneo::A2BPCMSample sample = static_cast<icsneo::A2BPCMSample>(contSample);
// Set sample for each frame in message
a2bmsg[frame][icsChannel] = sample;
elapsedTime+=deltaTime;
}
// Transmit message to device
if(!rada2b->transmit(a2bmsgPtr)) {
std::cout << "Failed to transmit." << std::endl;
}
}
}
// Example 5: Wave loop back
void example5(std::shared_ptr<icsneo::Device>& rada2b) {
auto listener = [&rada2b]() {
auto handler = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("looped.wav", 48000));
std::this_thread::sleep_for(std::chrono::seconds(5));
rada2b->removeMessageCallback(handler);
};
// Listen on another thread
std::thread listenerThread{listener};
// Transmit wave file using example0
example0(rada2b);
listenerThread.join();
}
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 RADA2B settings" << std::endl;
std::cout << "3\tA2BMessage API" << std::endl;
std::cout << "4\tPackaging and transmitting sine wave using A2BMessage API" << std::endl;
std::cout << "5\tWave loopback" << std::endl;
}
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 RADA2B." << std::endl;
return EXIT_FAILURE;
}
std::shared_ptr<icsneo::Device> rada2b = *it;
if(!rada2b->open()) {
std::cout << "Failed to open RADA2B." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "Opened RADA2B." << std::endl;
}
if(!rada2b->goOnline()) {
std::cout << "Failed to go online with RADA2B." << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
return EXIT_FAILURE;
}
else {
std::cout << "RADA2B online." << std::endl;
}
switch(option) {
case 0:
example0(rada2b);
break;
case 1:
example1(rada2b);
break;
case 2:
example2(rada2b);
break;
case 3:
example3();
break;
case 4:
example4(rada2b);
break;
case 5:
example5(rada2b);
break;
default:
break;
}
rada2b->goOffline();
rada2b->close();
return EXIT_SUCCESS;
}
+27
View File
@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-coremini VERSION 0.2.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-coremini src/coremini.cpp)
target_link_libraries(libicsneocpp-coremini icsneocpp)
+68
View File
@@ -0,0 +1,68 @@
// Usage:
// ./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH]
#include <iostream>
#include <fstream>
#include <icsneo/icsneocpp.h>
void displayUsage() {
std::cout << "Usage:\n";
std::cout << "./libicsneocpp-coremini [DEVICE_SERIAL] [COREMINI_SCRIPT_PATH]\n";
}
int main(int argc, char** argv) {
std::vector<std::string> arguments(argv, argv + argc);
if(arguments.size() != 3) {
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;
}
if (!device->uploadCoremini(std::make_unique<std::ifstream>(arguments[2], std::ios::binary), icsneo::Disk::MemoryType::Flash))
{
std::cout << "Failed to upload coremini" << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
}
if (!device->startScript(icsneo::Disk::MemoryType::Flash))
{
std::cout << "Failed to start script" << std::endl;
std::cout << icsneo::GetLastError() << std::endl;
}
device->goOffline();
device->close();
return 0;
}
@@ -614,7 +614,7 @@ int main() {
switch(selection) {
case '1':
{
int callbackID = selectedDevice->addMessageCallback(icsneo::MessageCallback([](std::shared_ptr<icsneo::Message> msg){
int callbackID = selectedDevice->addMessageCallback(std::make_shared<icsneo::MessageCallback>([](std::shared_ptr<icsneo::Message> msg){
printMessage(msg);
}));
+27
View File
@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-lin VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-lin src/LINExample.cpp)
target_link_libraries(libicsneocpp-lin icsneocpp)
+151
View File
@@ -0,0 +1,151 @@
#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 */
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::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 << "\tTransmitting 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 << "\tTransmitting a LIN commander frame... ";
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::cout << "\tTransmitting 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::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;
}
+27
View File
@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-livedata VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-livedata src/LiveDataExample.cpp)
target_link_libraries(libicsneocpp-livedata icsneocpp)
@@ -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;
}
+27
View File
@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 3.2)
project(libicsneocpp-mdio VERSION 0.1.0)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include(GNUInstallDirs)
# Add an include directory like so if desired
#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Enable Warnings
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
else() #if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-unknown-pragmas")
endif()
# Add libicsneo, usually a git submodule within your project works well
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-mdio src/MDIOExample.cpp)
target_link_libraries(libicsneocpp-mdio icsneocpp)
+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;
}
+30 -4
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;
@@ -141,7 +167,7 @@ int main() {
// We can also register a handler
std::cout << "\tStreaming messages in for 3 seconds... " << std::endl;
// MessageCallbacks are powerful, and can filter on things like ArbID for you. See the documentation
auto handler = device->addMessageCallback(icsneo::MessageCallback([](std::shared_ptr<icsneo::Message> message) {
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([](std::shared_ptr<icsneo::Message> message) {
switch(message->type) {
case icsneo::Message::Type::Frame: {
// A message of type Frame is guaranteed to be a Frame, so we can static cast safely
@@ -289,7 +315,7 @@ int main() {
if(!emisc.empty()) {
std::cout << "\tReading EMisc values..." << std::endl;
for(const auto& io : emisc) {
icsneo::optional<double> val = device->getAnalogIO(icsneo::IO::EMisc, io.number);
std::optional<double> val = device->getAnalogIO(icsneo::IO::EMisc, io.number);
std::cout << "\t\tEMISC" << io.number;
if(val.has_value())
std::cout << " - OK (" << val.value() << "V)" << std::endl;
-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();
+727 -739
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,3 +1,4 @@
package com.example.icsneojava;
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 4.0.0
@@ -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();
-1
View File
@@ -283,7 +283,6 @@ public class icsneojavaJNI {
public final static native int ICSNEO_NETID_AUX_get();
public final static native int ICSNEO_NETID_J1850VPW_get();
public final static native int ICSNEO_NETID_ISO_get();
public final static native int ICSNEO_NETID_ISOPIC_get();
public final static native int ICSNEO_NETID_MAIN51_get();
public final static native int ICSNEO_NETID_RED_get();
public final static native int ICSNEO_NETID_SCI_get();
+73 -4
View File
@@ -48,7 +48,8 @@ public:
MessageMaxLengthExceeded = 0x1012,
ValueNotYetPresent = 0x1013,
Timeout = 0x1014,
WiVINotSupported = 0x1015,
// Device Events
PollingMessageOverflow = 0x2000,
NoSerialNumber = 0x2001, // api
@@ -86,6 +87,23 @@ public:
SettingsDefaultsUsed = 0x2033,
AtomicOperationRetried = 0x2034,
AtomicOperationCompletedNonatomically = 0x2035,
WiVIStackRefreshFailed = 0x2036,
WiVIUploadStackOverflow = 0x2037,
I2CMessageExceedsMaxLength = 0x2038,
A2BMessageIncompleteFrame = 0x2039,
CoreminiUploadVersionMismatch = 0x2040,
DiskNotConnected = 0x2041,
UnexpectedResponse = 0x2042,
LiveDataInvalidHandle = 0x2043,
LiveDataInvalidCommand = 0x2044,
LiveDataInvalidArgument = 0x2045,
LiveDataVersionMismatch = 0x2046,
LiveDataNoDeviceResponse = 0x2047,
LiveDataMaxSignalsReached = 0x2048,
LiveDataCommandFailed = 0x2049,
LiveDataEncoderError = 0x2050,
LiveDataDecoderError = 0x2051,
LiveDataNotSupported = 0x2052,
// Transport Events
FailedToRead = 0x3000,
@@ -98,11 +116,62 @@ public:
PCAPCouldNotStart = 0x3102,
PCAPCouldNotFindDevices = 0x3103,
PacketDecodingError = 0x3104,
SocketFailedToOpen = 0x3105,
FailedToBind = 0x3106,
ErrorSettingSocketOption = 0x3107,
GetIfAddrsError = 0x3108,
SendToError = 0x3109,
MDIOMessageExceedsMaxLength = 0x3110,
DriverTTYPathEmpty = 0x3120,
DriverWasNotNegOne = 0x3121,
DriverTCGetAddrFail = 0x3122,
DriverTCSetAddrFail = 0x3123,
// FTD3XX
FTOK = 0x4000, // placeholder
FTInvalidHandle = FTOK + 1,
FTDeviceNotFound = FTOK + 2,
FTDeviceNotOpened = FTOK + 3,
FTIOError = FTOK + 4,
FTInsufficientResources = FTOK + 5,
FTInvalidParameter = FTOK + 6,
FTInvalidBaudRate = FTOK + 7,
FTDeviceNotOpenedForErase = FTOK + 8,
FTDeviceNotOpenedForWrite = FTOK + 9,
FTFailedToWriteDevice = FTOK + 10,
FTEEPROMReadFailed = FTOK + 11,
FTEEPROMWriteFailed = FTOK + 12,
FTEEPROMEraseFailed = FTOK + 13,
FTEEPROMNotPresent = FTOK + 14,
FTEEPROMNotProgrammed = FTOK + 15,
FTInvalidArgs = FTOK + 16,
FTNotSupported = FTOK + 17,
FTNoMoreItems = FTOK + 18,
FTTimeout = FTOK + 19,
FTOperationAborted = FTOK + 20,
FTReservedPipe = FTOK + 21,
FTInvalidControlRequestDirection = FTOK + 22,
FTInvalidControlRequestType = FTOK + 23,
FTIOPending = FTOK + 24,
FTIOIncomplete = FTOK + 25,
FTHandleEOF = FTOK + 26,
FTBusy = FTOK + 27,
FTNoSystemResources = FTOK + 28,
FTDeviceListNotReady = FTOK + 29,
FTDeviceNotConnected = FTOK + 30,
FTIncorrectDevicePath = FTOK + 31,
FTOtherError = FTOK + 32,
// Android USB
AndroidUSBLibusbInitFailed = 0x5000,
AndroidUSBFDWrapFailed = 0x5001,
NoErrorFound = 0xFFFFFFFD,
TooManyEvents = 0xFFFFFFFE,
Unknown = 0xFFFFFFFF
Unknown = 0xFFFFFFFF,
};
enum class Severity : uint8_t {
Any = 0, // Used for filtering, should not appear in data
EventInfo = 0x10,
@@ -166,4 +235,4 @@ public:
#endif // __cplusplus
#endif
#endif
+4
View File
@@ -25,6 +25,10 @@ public:
rhs.fnOnDeath = std::function<void(void)>();
return *this;
}
// Allow checking for empty Lifetimes
bool empty() const { return fnOnDeath.operator bool(); }
operator bool() const { return empty(); }
private:
std::function<void(void)> fnOnDeath;
};
+40 -2
View File
@@ -10,6 +10,10 @@ enum class Command : uint8_t {
EnableNetworkCommunicationEx = 0x08,
NeoReadMemory = 0x40,
NeoWriteMemory = 0x41,
ClearCoreMini = 0x42,
LoadCoreMini = 0x43,
GetRTC = 0x49,
SetRTC = 0x50,
RequestSerialNumber = 0xA1,
GetMainVersion = 0xA3, // Previously known as RED_CMD_APP_VERSION_REQ
SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM
@@ -23,14 +27,19 @@ enum class Command : uint8_t {
ReadSettings = 0xC7, // Previously known as 3G_READ_SETTINGS_EX
SetVBattMonitor = 0xDB, // Previously known as RED_CMD_CM_VBATT_MONITOR
RequestBitSmash = 0xDC, // Previously known as RED_CMD_CM_BITSMASH
WiVICommand = 0xDD, // Previously known as RED_CMD_WIVI_COMM
GetVBattReq = 0xDF, // Previously known as RED_CMD_VBATT_REQUEST
ScriptStatus = 0xE0, // Previously known as RED_CMD_SCRIPT_STATUS
MiscControl = 0xE7,
Extended = 0xF0,
Extended = 0xF0, // Previously known as RED_CMD_EXT_COMM
ExtendedData = 0xF2, // Previously known as RED_CMD_EXTENDED_DATA
FlexRayControl = 0xF3,
PHYControlRegisters = 0xEF
CoreMiniPreload = 0xF4, // Previously known as RED_CMD_COREMINI_PRELOAD
PHYControlRegisters = 0xEF,
};
enum class ExtendedCommand : uint16_t {
GenericReturn = 0x0000,
GetDiskDetails = 0x0010,
DiskFormatStart = 0x0011,
DiskFormatCancel = 0x0012,
@@ -39,8 +48,37 @@ enum class ExtendedCommand : uint16_t {
Extract = 0x0015,
StartDHCPServer = 0x0016,
StopDHCPServer = 0x0017,
GetSupportedFeatures = 0x0018,
GetComponentVersions = 0x001A,
Reboot = 0x001C,
SetUploadedFlag = 0x0027,
GenericBinaryInfo = 0x0030,
LiveData = 0x0035,
};
enum class ExtendedResponse : int32_t {
OK = 0,
InvalidCommand = -1,
InvalidState = -2,
OperationFailed = -3,
OperationPending = -4,
InvalidParameter = -5,
};
enum class ExtendedDataSubCommand : uint32_t {
GenericBinaryRead = 13,
};
#pragma pack(push,1)
struct ExtendedCommandHeader {
uint8_t netid; // should be Main51
uint16_t fullLength;
uint8_t command; // should be Command::Extended
uint16_t extendedCommand;
uint16_t payloadLength;
};
#pragma pack(pop)
}
#endif // __cplusplus
+7 -3
View File
@@ -10,6 +10,8 @@
#include "icsneo/communication/message/callback/messagecallback.h"
#include "icsneo/communication/message/serialnumbermessage.h"
#include "icsneo/communication/message/logicaldiskinfomessage.h"
#include "icsneo/communication/message/componentversionsmessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/device/deviceversion.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/communication/packetizer.h"
@@ -56,10 +58,11 @@ public:
bool sendCommand(ExtendedCommand cmd, std::vector<uint8_t> arguments = {});
bool getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
std::shared_ptr<SerialNumberMessage> getSerialNumberSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
optional< std::vector< optional<DeviceAppVersion> > > getVersionsSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
std::optional< std::vector< std::optional<DeviceAppVersion> > > getVersionsSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
std::shared_ptr<LogicalDiskInfoMessage> getLogicalDiskInfoSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
std::optional< std::vector<ComponentVersion> > getComponentVersionsSync(std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
int addMessageCallback(const MessageCallback& cb);
int addMessageCallback(const std::shared_ptr<MessageCallback>& cb);
bool removeMessageCallback(int id);
std::shared_ptr<Message> waitForMessageSync(
const std::shared_ptr<MessageFilter>& f = {},
@@ -83,11 +86,12 @@ public:
protected:
static int messageCallbackIDCounter;
std::mutex messageCallbacksLock;
std::map<int, MessageCallback> messageCallbacks;
std::map<int, std::shared_ptr<MessageCallback>> messageCallbacks;
std::atomic<bool> closing{false};
std::atomic<bool> redirectingRead{false};
std::function<void(std::vector<uint8_t>&&)> redirectionFn;
std::mutex redirectingReadMutex; // Don't allow read to be disabled while in the redirectionFn
std::mutex syncMessageMutex;
void dispatchMessage(const std::shared_ptr<Message>& msg);
void handleInput(Packetizer& p, std::vector<uint8_t>& readBytes);
+147
View File
@@ -0,0 +1,147 @@
#ifndef __LIVEDATA_H__
#define __LIVEDATA_H__
#ifdef __cplusplus
#include <cstdint>
#include <vector>
#include <memory>
#include "icsneo/communication/command.h"
#include "icsneo/api/eventmanager.h"
namespace icsneo {
typedef uint32_t LiveDataHandle;
static constexpr size_t MAX_LIVE_DATA_ENTRIES = 20;
enum class LiveDataCommand : uint32_t {
STATUS = 0,
SUBSCRIBE,
UNSUBSCRIBE,
RESPONSE,
CLEAR_ALL,
};
enum class LiveDataStatus : uint32_t {
SUCCESS = 0,
ERR_UNKNOWN_COMMAND,
ERR_HANDLE,
ERR_DUPLICATE,
ERR_FULL
};
enum LiveDataObjectType : uint16_t {
MISC = 8,
SNA = UINT16_MAX,
};
enum class LiveDataValueType : uint32_t {
GPS_LATITUDE = 2,
GPS_LONGITUDE,
GPS_ALTITUDE,
GPS_SPEED,
GPS_VALID,
GPS_ENABLE = 62,
GPS_ACCURACY = 120,
GPS_BEARING = 121,
GPS_TIME = 122,
GPS_TIME_VALID = 123,
};
inline std::ostream& operator<<(std::ostream& os, const LiveDataCommand cmd) {
switch (cmd) {
case LiveDataCommand::STATUS: return os << "Status";
case LiveDataCommand::SUBSCRIBE: return os << "Subscribe";
case LiveDataCommand::UNSUBSCRIBE: return os << "Unsubscribe";
case LiveDataCommand::RESPONSE: return os << "Response";
case LiveDataCommand::CLEAR_ALL: return os << "Clear All";
}
return os;
}
inline std::ostream& operator<<(std::ostream& os, const LiveDataStatus cmd) {
switch (cmd) {
case LiveDataStatus::SUCCESS: return os << "Success";
case LiveDataStatus::ERR_UNKNOWN_COMMAND: return os << "Error: Unknown Command";
case LiveDataStatus::ERR_HANDLE: return os << "Error: Handle";
case LiveDataStatus::ERR_DUPLICATE: return os << "Error: Duplicate";
case LiveDataStatus::ERR_FULL: return os << "Error: Argument limit reached";
}
return os;
}
inline std::ostream& operator<<(std::ostream& os, const LiveDataValueType cmd) {
switch (cmd) {
case LiveDataValueType::GPS_LATITUDE: return os << "GPS Latitude";
case LiveDataValueType::GPS_LONGITUDE: return os << "GPS Longitude";
case LiveDataValueType::GPS_ALTITUDE: return os << "GPS Altitude";
case LiveDataValueType::GPS_SPEED: return os << "GPS Speed";
case LiveDataValueType::GPS_VALID: return os << "GPS Valid";
case LiveDataValueType::GPS_ENABLE: return os << "GPS Enabled";
case LiveDataValueType::GPS_ACCURACY: return os << "GPS Accuracy";
case LiveDataValueType::GPS_BEARING: return os << "GPS Bearing";
case LiveDataValueType::GPS_TIME: return os << "GPS Time";
case LiveDataValueType::GPS_TIME_VALID: return os << "GPS Time Valid";
}
return os;
}
#pragma pack(push,2)
struct LiveDataHeader {
uint32_t version; // See LiveDataVersion
uint32_t cmd;
uint32_t handle;
};
struct LiveDataArgument {
LiveDataObjectType objectType;
uint32_t objectIndex;
uint32_t signalIndex;
LiveDataValueType valueType;
};
struct LiveDataValueHeader {
uint16_t length; // Number of bytes to follow header
uint8_t reserved[2];
};
typedef struct
{
LiveDataValueHeader header;
int64_t value;
} LiveDataValue;
struct LiveDataValueResponse : public LiveDataHeader {
uint32_t numArgs;
LiveDataValue values[1];
};
struct LiveDataStatusResponse : public LiveDataHeader {
LiveDataCommand requestedCommand;
LiveDataStatus status;
};
struct LiveDataSubscribe : public LiveDataHeader {
uint32_t numArgs;
uint32_t freqMs;
uint32_t expireMs;
LiveDataArgument args[1];
};
struct ExtResponseHeader {
ExtendedCommand command;
uint16_t length;
};
#pragma pack(pop)
namespace LiveDataUtil
{
LiveDataHandle getNewHandle();
double liveDataValueToDouble(const LiveDataValue& val);
static constexpr uint32_t LiveDataVersion = 1;
} // namespace LiveDataUtil
} // namespace icsneo
#endif // _cplusplus
#endif // __LIVEDATA_H__
@@ -0,0 +1,443 @@
#ifndef __A2BMESSAGE_H_
#define __A2BMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/api/eventmanager.h"
#include <algorithm>
#include <cstring>
#include <iostream>
namespace icsneo {
typedef uint32_t A2BPCMSample;
class A2BMessage : public Frame {
private:
class FrameView {
private:
class SampleView {
public:
SampleView(uint8_t* vPtr, uint8_t bps, size_t ind) :
index(ind), viewPtr(vPtr), bytesPerSample(bps) {}
operator A2BPCMSample() const {
if(!viewPtr) {
return 0;
}
A2BPCMSample sample = 0;
std::copy(viewPtr+index*bytesPerSample, viewPtr+(index+1)*bytesPerSample, (uint8_t*)&sample);
if(bytesPerSample == 4) {
sample = sample >> 8;
}
return sample;
}
SampleView& operator=(A2BPCMSample sample) {
if(!viewPtr) {
return *this;
}
if(bytesPerSample == 4) {
sample = sample << 8;
}
std::copy((uint8_t*)&sample, (uint8_t*)&sample + bytesPerSample, viewPtr + index*bytesPerSample);
return *this;
}
SampleView(const SampleView&) = delete;
SampleView& operator=(const SampleView&) = delete;
private:
size_t index;
uint8_t* viewPtr;
uint8_t bytesPerSample;
};
public:
FrameView(uint8_t* vPtr, uint8_t nChannels, uint8_t bps) : viewPtr(vPtr), tdm(nChannels), bytesPerSample(bps) {}
SampleView operator[](size_t index) {
if(index >= ((size_t)tdm) * 2) {
EventManager::GetInstance().add(APIEvent(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error));
return SampleView(nullptr, 0, 0);
}
return SampleView(viewPtr, bytesPerSample, index);
}
FrameView& operator=(const std::vector<A2BPCMSample>& samples) {
if(!viewPtr) {
return *this;
}
if(samples.size() != (size_t)(tdm)*2) {
EventManager::GetInstance().add(APIEvent(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error));
return *this;
}
for(size_t icsChannel = 0; icsChannel < ((size_t)(tdm) * 2); icsChannel++) {
operator[](icsChannel) = samples[icsChannel];
}
return *this;
}
FrameView(const FrameView&) = delete;
FrameView& operator=(const FrameView&) = delete;
private:
uint8_t* viewPtr;
uint8_t tdm;
uint8_t bytesPerSample;
};
public:
enum class A2BDirection : uint8_t {
Downstream = 0,
Upstream = 1
};
A2BMessage(uint8_t nChannels, bool chSize16, size_t size) :
numChannels(nChannels),
channelSize16(chSize16)
{
data.resize(std::min(roundNextMultiple(size, getFrameSize()),(size_t)maxSize), 0);
}
bool allocateSpace(size_t numSpaceToAdd) {
size_t spaceToAdd = roundNextMultiple(numSpaceToAdd, getFrameSize());
if(spaceToAdd + data.size() > maxSize) {
return false;
}
data.resize(data.size() + numSpaceToAdd, 0);
return true;
}
bool addFrame(const std::vector<A2BPCMSample>& frame) {
if(frame.size() != ((size_t)numChannels)*2) {
return false;
}
size_t oldSize = data.size();
if(!allocateSpace(getFrameSize())) {
return false;
}
auto it = data.begin() + oldSize;
size_t offset = 0;
for(A2BPCMSample sample: frame) {
if(!channelSize16) {
sample = sample << 8;
}
std::copy((uint8_t*)&sample, (uint8_t*)&sample + getBytesPerSample(), it + offset);
offset+=getBytesPerSample();
}
return true;
}
bool setFrame(const std::vector<A2BPCMSample>& frame, size_t frameNum) {
if(frame.size() != ((size_t)numChannels)*2 || frameNum >= getNumFrames()) {
return false;
}
auto it = data.begin() + frameNum*getFrameSize();
size_t offset = 0;
for(A2BPCMSample sample: frame) {
if(!channelSize16) {
sample = sample << 8;
}
std::copy((uint8_t*)&sample, (uint8_t*)&sample + getBytesPerSample(), it + offset);
offset+=getBytesPerSample();
}
return true;
}
bool fillChannelAudioBuffer(A2BDirection dir, uint8_t channel, std::vector<uint8_t>& channelBuffer) const {
if(channel >= numChannels) {
return false;
}
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample();
for(size_t frame = 0; frame < getNumFrames(); frame++, offset += getFrameSize()) {
std::copy(data.begin() + offset, data.end() + offset + getBytesPerSample(), std::back_inserter(channelBuffer));
}
return true;
}
bool fillChannelStream(A2BDirection dir, uint8_t channel, std::unique_ptr<std::ostream>& channelStream) const {
if(channel >= numChannels) {
return false;
}
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample();
for(size_t frame = 0; frame < getNumFrames(); frame++, offset += getFrameSize()) {
channelStream->write((const char*)(data.data() + offset), getBytesPerSample());
}
return true;
}
void fill(A2BPCMSample sample) {
uint8_t* buf = data.data();
if(channelSize16) {
uint16_t sample16bit = sample & 0xFF;
uint16_t* samps = (uint16_t*)buf;
std::fill(samps, samps + data.size()/2, sample16bit);
}
else {
A2BPCMSample* samps = (A2BPCMSample*)buf;
sample = sample << 8;
std::fill(samps, samps + data.size()/4, sample);
}
}
bool fillFrame(A2BPCMSample sample, size_t frame) {
if(frame >= getNumFrames()) {
return false;
}
uint8_t* buf = data.data();
size_t start = 2 * numChannels * frame;
size_t end = 2 * numChannels * (frame+1);
if(channelSize16) {
uint16_t sample16bit = sample & 0xFF;
uint16_t* samps = (uint16_t*)buf;
std::fill(samps+start, samps + end, sample16bit);
}
else {
A2BPCMSample* samps = (A2BPCMSample*)buf;
sample = sample << 8;
std::fill(samps+start, samps + end, sample);
}
return true;
}
template<typename Iterator>
bool setAudioBuffer(Iterator begin, Iterator end, A2BDirection dir, uint8_t channel, uint32_t frame) {
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
size_t dist = (size_t)(std::distance(begin, end));
if(dist > (data.size() - offset)) {
return false;
}
std::copy(begin, end, data.begin() + offset);
return true;
}
template<typename Iterator>
bool setAudioBuffer(Iterator begin, Iterator end) {
return setAudioBuffer(begin, end, A2BMessage::A2BDirection::Downstream, 0, 0);
}
std::optional<A2BPCMSample> getSample(A2BDirection dir, uint8_t channel, uint32_t frame) const {
if(
channel >= numChannels ||
frame >= getNumFrames()
) {
return std::nullopt;
}
A2BPCMSample sample = 0;
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
std::copy(data.begin() + offset, data.begin() + offset + getBytesPerSample(), (uint8_t*)&sample);
if(channelSize16) {
sample = sample >> 8;
}
return sample;
}
std::optional<A2BPCMSample> getSample(size_t sampleNum) const {
if(sampleNum >= getNumSamples()) {
return std::nullopt;
}
A2BPCMSample sample = 0;
size_t offset = sampleNum*getBytesPerSample();
std::copy(data.begin() + offset, data.begin() + offset + getBytesPerSample(), (uint8_t*)&sample);
if(channelSize16) {
sample = sample >> 8;
}
return sample;
}
bool setSample(A2BDirection dir, uint8_t channel, uint32_t frame, A2BPCMSample sample) {
if(
channel >= numChannels ||
frame >= getNumFrames()
) {
return false;
}
size_t offset = getChannelIndex(dir, channel)*getBytesPerSample() + frame * getFrameSize();
if(!channelSize16) {
sample = sample << 8;
}
uint8_t* sampToBytes = (uint8_t*)&sample;
std::copy(sampToBytes,sampToBytes+getBytesPerSample(), data.begin() + offset);
return true;
}
bool setSample(uint8_t icsChannel, uint32_t frame, A2BPCMSample sample) {
if(
icsChannel >= (2*numChannels) ||
frame >= getNumFrames()
) {
return false;
}
size_t offset = ((size_t)icsChannel)*getBytesPerSample() + frame * getFrameSize();
if(!channelSize16) {
sample = sample << 8;
}
uint8_t* sampToBytes = (uint8_t*)&sample;
std::copy(sampToBytes,sampToBytes+getBytesPerSample(), data.begin() + offset);
return true;
}
FrameView operator[](size_t index) {
if(index >= getNumFrames()) {
EventManager::GetInstance().add(APIEvent(APIEvent::Type::ParameterOutOfRange, APIEvent::Severity::Error));
return FrameView(nullptr, 0, 0);
}
return FrameView(data.data() + index*getFrameSize(), numChannels, getBytesPerSample());
}
size_t getNumSamples() const {
return data.size()/((size_t)getBytesPerSample());
}
uint8_t getNumChannels() const {
return numChannels;
}
uint8_t getBitDepth() const {
return channelSize16 ? 16 : 24;
}
uint8_t getBytesPerSample() const {
return channelSize16 ? 2 : 4;
}
bool isTxMsg() const {
return txmsg;
}
void setTxMsgBit(bool bit) {
txmsg = bit;
}
bool isMonitorMsg() const {
return monitor;
}
void setMonitorBit(bool bit) {
monitor = bit;
}
bool isErrIndicator() const {
return errIndicator;
}
void setErrIndicatorBit(bool bit) {
errIndicator = bit;
}
bool isSyncFrame() const {
return syncFrame;
}
void setSyncFrameBit(bool bit) {
syncFrame = bit;
}
uint16_t getRFU2() const {
return rfu2;
}
void setRFU2(uint16_t newRfu2) {
rfu2 = newRfu2;
}
size_t getFrameSize() const {
return 2*((size_t)numChannels) * ((size_t)getBytesPerSample());
}
size_t getNumFrames() const {
return data.size() / getFrameSize();
}
size_t getAudioBufferSize() const {
return data.size();
}
const uint8_t* getAudioBuffer() const {
return data.data();
}
static constexpr uint32_t maxSize = 2048;
private:
uint8_t numChannels = 0;
bool channelSize16 = false;
bool monitor = false;
bool txmsg = false;
bool errIndicator = false;
bool syncFrame = false;
uint16_t rfu2 = 0;
size_t roundNextMultiple(size_t x, size_t y) const {
if(y==0) {
return 0;
}
else if(x%y == 0) {
return x;
}
return x + y - (x%y);
}
size_t getChannelIndex(A2BDirection dir, uint8_t channel) const {
size_t channelIndex = 2 * ((size_t)channel);
if(dir == A2BDirection::Upstream) {
channelIndex++;
}
return channelIndex;
}
};
}
#endif // __cplusplus
#endif
@@ -28,6 +28,8 @@ public:
: MessageCallback(cb, f) {}
MessageCallback(MessageFilter f, fn_messageCallback cb)
: MessageCallback(cb, std::make_shared<MessageFilter>(f)) {}
virtual ~MessageCallback() = default;
virtual bool callIfMatch(const std::shared_ptr<Message>& message) const {
bool ret = filter->match(message);
@@ -0,0 +1,85 @@
#ifndef __A2BDECODER_H_
#define __A2BDECODER_H_
#ifdef __cplusplus
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/device/device.h"
namespace icsneo {
typedef uint8_t Channel;
class A2BAudioChannelMap {
public:
A2BAudioChannelMap(uint8_t tdm);
void set(Channel outChannel, A2BMessage::A2BDirection dir, Channel inChannel);
void setAll(Channel inChannel);
Channel get(Channel outChannel, A2BMessage::A2BDirection dir) const;
size_t size() const;
uint8_t getTDM() const;
Channel& operator[](size_t idx);
operator const std::vector<Channel>&() const;
private:
size_t getChannelIndex(Channel channel, A2BMessage::A2BDirection dir) const;
std::vector<Channel> rawMap;
};
class A2BDecoder {
public:
A2BDecoder(
std::unique_ptr<std::istream>&& streamOut,
bool chSize16,
const A2BAudioChannelMap& chMap
);
A2BDecoder(
const char* filename,
bool chSize16,
const A2BAudioChannelMap& chMap
);
operator bool() const;
std::shared_ptr<A2BMessage> decode();
bool outputAll(std::shared_ptr<Device> &device);
std::unique_ptr<std::istream> stream;
private:
void initializeFromHeader();
uint8_t tdm;
uint8_t audioBytesPerSample;
uint8_t channelsInWave;
bool channelSize16;
A2BAudioChannelMap channelMap;
std::vector<uint8_t> frame;
std::vector<uint8_t> frameWave;
bool initialized = false;
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,45 @@
#ifndef __A2BWAVOUTPUT_H_
#define __A2BWAVOUTPUT_H_
#ifdef __cplusplus
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/device/device.h"
namespace icsneo {
class A2BWAVOutput : public StreamOutput {
public:
A2BWAVOutput(const char* filename, uint32_t sampleRate = 44100)
: StreamOutput(filename), wavSampleRate(sampleRate) {}
A2BWAVOutput(std::unique_ptr<std::ostream>&& os, uint32_t sampleRate = 44100)
: StreamOutput(std::move(os)), wavSampleRate(sampleRate) {}
void writeHeader(const std::shared_ptr<A2BMessage>& firstMsg) const;
bool callIfMatch(const std::shared_ptr<Message>& message) const override;
void close() const;
~A2BWAVOutput() override {
if(!closed) {
close();
}
}
protected:
uint32_t wavSampleRate;
mutable uint32_t streamStartPos;
mutable bool firstMessageFlag = true;
mutable bool closed = false;
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,110 @@
#ifndef __STREAMOUTPUT_H_
#define __STREAMOUTPUT_H_
#ifdef __cplusplus
#include "icsneo/communication/message/callback/messagecallback.h"
#include <memory>
#include <functional>
#include <iostream>
#include <fstream>
#define WAV_SAMPLE_RATE_44100 44100
#define WAV_SAMPLE_RATE_48000 48000
namespace icsneo {
struct WaveFileHeader {
static constexpr uint32_t WAVE_CHUNK_ID = 0x46464952; // "RIFF"
static constexpr uint32_t WAVE_FORMAT = 0x45564157; // "WAVE"
static constexpr uint32_t WAVE_SUBCHUNK1_ID = 0x20746d66; // "fmt "
static constexpr uint32_t WAVE_SUBCHUNK2_ID = 0x61746164; // "data"
static constexpr uint16_t WAVE_SUBCHUNK1_SIZE = 16;
static constexpr uint16_t WAVE_AUDIO_FORMAT_PCM = 1;
static constexpr uint32_t WAVE_DEFAULT_SIZE = 0; // Default size for streamed wav
uint32_t chunkId = WAVE_CHUNK_ID; // "RIFF"
uint32_t chunkSize = WAVE_DEFAULT_SIZE; // number of bytes to follow
uint32_t format = WAVE_FORMAT; // "WAVE"
uint32_t subchunk1Id = WAVE_SUBCHUNK1_ID; // "fmt "
uint32_t subchunk1Size = WAVE_SUBCHUNK1_SIZE; // number of bytes in *this* subchunk (always 16)
uint16_t audioFormat = WAVE_AUDIO_FORMAT_PCM; // 1 for PCM
uint16_t numChannels; // number of channels
uint32_t sampleRate; // sample rate in Hz
uint32_t byteRate; // bytes per second of audio: sampleRate * numChannels * (bitsPerSample / 8)
uint16_t blockAlign; // alignment of each block in bytes: numChannels * (bitsPerSample / 8)
uint16_t bitsPerSample; // number of bits in each sample
uint32_t subchunk2Id = WAVE_SUBCHUNK2_ID; // "data"
uint32_t subchunk2Size = WAVE_DEFAULT_SIZE; // number of bytes to follow
WaveFileHeader() = default;
WaveFileHeader(uint16_t nChannels, uint32_t sRate, uint16_t bps, uint32_t nSamples = 0) {
setHeader(nChannels, sRate, bps, nSamples);
}
void setHeader(uint16_t newNumChannels, uint32_t newSampleRate, uint16_t newBitsPerSample, uint32_t numSamples = 0) {
numChannels = newNumChannels;
sampleRate = newSampleRate;
bitsPerSample = newBitsPerSample;
blockAlign = numChannels * (bitsPerSample / 8);
byteRate = sampleRate * numChannels * (bitsPerSample / 8);
if(numSamples != 0) {
setNumSamples(numSamples);
}
}
void setNumSamples(uint32_t numSamples) {
subchunk2Size = numSamples * numChannels * (bitsPerSample / 8);
chunkSize = subchunk2Size + 36;
}
void write(const std::unique_ptr<std::ostream>& stream) {
stream->write(reinterpret_cast<const char*>(&chunkId), 4);
stream->write(reinterpret_cast<const char*>(&chunkSize), 4);
stream->write(reinterpret_cast<const char*>(&format), 4);
stream->write(reinterpret_cast<const char*>(&subchunk1Id), 4);
stream->write(reinterpret_cast<const char*>(&subchunk1Size), 4);
stream->write(reinterpret_cast<const char*>(&audioFormat), 2);
stream->write(reinterpret_cast<const char*>(&numChannels), 2);
stream->write(reinterpret_cast<const char*>(&sampleRate), 4);
stream->write(reinterpret_cast<const char*>(&byteRate), 4);
stream->write(reinterpret_cast<const char*>(&blockAlign), 2);
stream->write(reinterpret_cast<const char*>(&bitsPerSample), 2);
stream->write(reinterpret_cast<const char*>(&subchunk2Id), 4);
stream->write(reinterpret_cast<const char*>(&subchunk2Size), 4);
}
};
class StreamOutput : public MessageCallback {
public:
StreamOutput(std::unique_ptr<std::ostream>&& os, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
: MessageCallback(cb, f), stream(std::move(os)) {}
StreamOutput(const char* filename, fn_messageCallback cb, std::shared_ptr<MessageFilter> f)
: MessageCallback(cb, f) {
stream = std::make_unique<std::ofstream>(filename, std::ios::binary);
}
StreamOutput(const char* filename) : MessageCallback([](std::shared_ptr<Message> msg) {}) {
stream = std::make_unique<std::ofstream>(filename, std::ios::binary);
}
StreamOutput(std::unique_ptr<std::ostream>&& os) : MessageCallback([](std::shared_ptr<Message> msg) {}), stream(std::move(os)) {}
protected:
std::unique_ptr<std::ostream> stream;
void write(void* msg, std::streamsize size) const {
stream->write(reinterpret_cast<const char*>(msg), size);
}
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,29 @@
#ifndef _EXTENDED_COMPONENT_VERSIONS_RESPONSE_MESSAGE_H_
#define _EXTENDED_COMPONENT_VERSIONS_RESPONSE_MESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/extendedresponsemessage.h"
namespace icsneo {
class ComponentVersion {
public:
ComponentVersion(uint8_t valid, uint8_t componentInfo, uint32_t identifier, uint32_t dotVersion, uint32_t commitHash) :
valid(valid), componentInfo(componentInfo), identifier(identifier), dotVersion(dotVersion), commitHash(commitHash) {}
const bool valid;
const uint8_t componentInfo;
const uint32_t identifier;
const uint32_t dotVersion;
const uint32_t commitHash;
};
class ComponentVersionsMessage : public Message {
public:
ComponentVersionsMessage() : Message(Message::Type::ComponentVersions) {}
std::vector<ComponentVersion> versions;
};
}
#endif // __cplusplus
#endif // _EXTENDED_COMPONENT_VERSIONS_RESPONSE_MESSAGE_H_
@@ -0,0 +1,21 @@
#ifndef __DISKDATAMESSAGE_H_
#define __DISKDATAMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
namespace icsneo {
class DiskDataMessage : public RawMessage {
public:
DiskDataMessage(std::vector<uint8_t>&& d) : RawMessage(Network::NetID::DiskData) {
data = std::move(d);
}
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,34 @@
#ifndef __EXTENDEDDATAMESSAGE_H_
#define __EXTENDEDDATAMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/command.h"
namespace icsneo {
class ExtendedDataMessage : public RawMessage {
public:
#pragma pack(push, 2)
struct ExtendedDataHeader {
ExtendedDataSubCommand subCommand;
uint32_t userValue;
uint32_t offset;
uint32_t length;
};
#pragma pack(pop)
static constexpr size_t MaxExtendedDataBufferSize = 2048;
const ExtendedDataHeader header;
ExtendedDataMessage(ExtendedDataHeader params) : RawMessage(Message::Type::RawMessage, Network::NetID::ExtendedData), header{params} {}
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,37 @@
#ifndef __EXTENDEDRESPONSEMESSAGE_H_
#define __EXTENDEDRESPONSEMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/command.h"
namespace icsneo {
class ExtendedResponseMessage : public Message {
public:
ExtendedResponseMessage(ExtendedCommand cmd, ExtendedResponse resp)
: Message(Message::Type::ExtendedResponse), command(cmd), response(resp) {}
const ExtendedCommand command;
const ExtendedResponse response;
#pragma pack(push, 2)
struct ResponseHeader {
ExtendedCommand command;
uint16_t length;
};
struct PackedGenericResponse {
ResponseHeader header;
ExtendedCommand command; // `header.command` is ExtendedCommand::GenericReturn, this is the real command
ExtendedResponse returnCode;
};
#pragma pack(pop)
};
}
#endif // __cplusplus
#endif
@@ -13,7 +13,7 @@ class MessageFilter {
public:
MessageFilter() {}
MessageFilter(Message::Type type) : includeInternalInAny(neomessagetype_t(type) & 0x8000), messageType(type) {}
MessageFilter(Network::NetID netid) : MessageFilter(Network::GetTypeOfNetID(netid), netid) {}
MessageFilter(Network::NetID netid) : MessageFilter(Network::GetTypeOfNetID(netid, false), netid) {} // Messages on the communication layer are never encoded as VNET ID + common ID, so skip the expansion step
MessageFilter(Network::Type type, Network::NetID net = Network::NetID::Any) : networkType(type), netid(net) {
// If a Network::Type::Internal is used, we want to also get internal Message::Types
// The NetID we want may be in there
@@ -34,6 +34,8 @@ public:
return false;
if(!matchNetID(frame.network.getNetID()))
return false;
} else if (netid != Network::NetID::Any || networkType != Network::Type::Any) {
return false; // Filtering on a NetID or Type, but this message doesn't have one
}
return true;
}
@@ -0,0 +1,22 @@
#ifndef _EXTENDED_GENERIC_BINARY_STATUS_MESSAGE_H_
#define _EXTENDED_GENERIC_BINARY_STATUS_MESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/extendedresponsemessage.h"
namespace icsneo {
class GenericBinaryStatusMessage : public Message {
public:
GenericBinaryStatusMessage() : Message(Message::Type::GenericBinaryStatus) {}
size_t binarySize;
uint16_t binaryIndex;
uint16_t binaryStatus;
};
}
#endif // __cplusplus
#endif // _EXTENDED_GENERIC_BINARY_STATUS_RESPONSE_MESSAGE_H_
@@ -0,0 +1,48 @@
#ifndef __I2CMESSAGE_H_
#define __I2CMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include <vector>
namespace icsneo {
class I2CMessage : public Frame {
public:
enum class DeviceMode : uint8_t {
Target = 0,
Controller = 1
};
enum class Direction : uint8_t {
Write = 0,
Read = 1
};
bool isExtendedID = false;
bool isTXMsg = false;
bool txError = false;
bool txLostArb = false;
bool txAborted = false;
bool txNack = false;
bool txTimeout = false;
uint16_t stats = static_cast<uint16_t>(0x0000u);
uint16_t address;
Direction direction;
DeviceMode deviceMode;
//Must contain the target register address to read or write
std::vector<uint8_t> controlBytes;
//The device expects a dataBytes payload even if you're reading
//In the case of a read these bytes aren't interesting, but they have to be there
//Add bytes to write, or the same number of junk bytes you expect the device send back
std::vector<uint8_t> dataBytes;
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,68 @@
#ifndef __LINMESSAGE_H_
#define __LINMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/network.h"
#include <vector>
namespace icsneo {
struct LINErrorFlags {
bool ErrRxBreakOnly = false;
bool ErrRxBreakSyncOnly = false;
bool ErrTxRxMismatch = false;
bool ErrRxBreakNotZero = false;
bool ErrRxBreakTooShort = false;
bool ErrRxSyncNot55 = false;
bool ErrRxDataLenOver8 = false;
bool ErrFrameSync = false;
bool ErrFrameMessageID = false;
bool ErrFrameResponderData = false;
bool ErrChecksumMatch = false;
};
struct LINStatusFlags {
bool TxChecksumEnhanced = false;
bool TxCommander = false;
bool TxResponder = false;
bool TxAborted = false;
bool UpdateResponderOnce = false;
bool HasUpdatedResponderOnce = false;
bool BusRecovered = false;
bool BreakOnly = false;
};
class LINMessage : public Frame {
public:
enum class Type : uint8_t {
NOT_SET = 0,
LIN_COMMANDER_MSG,
LIN_HEADER_ONLY,
LIN_BREAK_ONLY,
LIN_SYNC_ONLY,
LIN_UPDATE_RESPONDER,
LIN_ERROR
};
static void calcChecksum(LINMessage& message);
uint8_t calcProtectedID(uint8_t& id);
LINMessage() {};
LINMessage(uint8_t id) : ID(id & 0x3Fu), protectedID(calcProtectedID(ID)) {};
uint8_t ID = 0;
uint8_t protectedID = 0;
uint8_t checksum = 0;
LINMessage::Type linMsgType = Type::NOT_SET;
bool isEnhancedChecksum = false;
LINErrorFlags errFlags;
LINStatusFlags statusFlags;
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,44 @@
#ifndef __EXTENDEDLIVEDATAMESSAGE_H_
#define __EXTENDEDLIVEDATAMESSAGE_H_
#ifdef __cplusplus
#include <chrono>
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/command.h"
#include "icsneo/communication/livedata.h"
namespace icsneo {
class LiveDataMessage : public RawMessage {
public:
LiveDataMessage() : RawMessage(Message::Type::LiveData, Network::NetID::ExtendedCommand) {}
LiveDataHandle handle;
LiveDataCommand cmd;
};
class LiveDataCommandMessage : public LiveDataMessage {
public:
LiveDataCommandMessage() {}
std::chrono::milliseconds updatePeriod;
std::chrono::milliseconds expirationTime;
std::vector<std::shared_ptr<LiveDataArgument>> args;
void appendSignalArg(LiveDataValueType valueType);
};
class LiveDataValueMessage : public LiveDataMessage {
public:
LiveDataValueMessage() {}
uint32_t numArgs;
std::vector<std::shared_ptr<LiveDataValue>> values;
};
class LiveDataStatusMessage : public LiveDataMessage {
public:
LiveDataStatusMessage() {}
LiveDataCommand requestedCommand;
LiveDataStatus status;
};
} // namespace icsneo
#endif // __cplusplus
#endif // __EXTENDEDLIVEDATAMESSAGE_H_
@@ -0,0 +1,41 @@
#ifndef __MDIOMESSAGE_H_
#define __MDIOMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
namespace icsneo {
class MDIOMessage : public Frame {
public:
enum class Clause : uint8_t {
Clause45 = 0,
Clause22 = 1
};
enum class Direction : uint8_t {
Write = 0,
Read = 1
};
bool isTXMsg = false;
bool txTimeout = false;
bool txAborted = false;
bool txInvalidBus = false;
bool txInvalidPhyAddr = false;
bool txInvalidRegAddr = false;
bool txInvalidClause = false;
bool txInvalidOpcode = false;
uint8_t phyAddress;
uint8_t devAddress;
uint16_t regAddress;
Direction direction;
Clause clause;
};
}
#endif // __cplusplus
#endif

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