Author SHA1 Message Date
Kyle Schwarz a9157c82e5 Add device sharing support 2022-12-13 11:46:32 -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
378 changed files with 48724 additions and 8471 deletions
+2 -1
View File
@@ -8,4 +8,5 @@ CMakeSettings.json
third-party/concurrentqueue/benchmarks
third-party/concurrentqueue/tests
*.bak
.vs
.vs
.cache
+80 -39
View File
@@ -9,6 +9,8 @@ option(LIBICSNEO_BUILD_ICSNEOC_STATIC "Build static C library" ON)
option(LIBICSNEO_BUILD_ICSNEOLEGACY "Build icsnVC40 compatibility library" ON)
set(LIBICSNEO_NPCAP_INCLUDE_DIR "" CACHE STRING "Npcap include directory; set to build with Npcap")
option(LIBICSNEO_USE_DEVICE_SHARING "Interact with devices through the sharing server" ON)
# Device Drivers
# You almost certainly don't want firmio for your build,
# it is only relevant for communication between Linux and
@@ -18,13 +20,13 @@ option(LIBICSNEO_ENABLE_RAW_ETHERNET "Enable devices which communicate over raw
option(LIBICSNEO_ENABLE_CDCACM "Enable devices which communicate over USB CDC ACM" ON)
option(LIBICSNEO_ENABLE_FTDI "Enable devices which communicate over USB FTDI2XX" ON)
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
include(GNUInstallDirs)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Enable Warnings
if(MSVC)
@@ -34,6 +36,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 +98,7 @@ if(LIBICSNEO_BUILD_DOCS)
endif()
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
if(WIN32)
set(PLATFORM_SRC
platform/windows/registry.cpp
)
@@ -110,6 +115,11 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
platform/windows/vcp.cpp
)
endif()
list(APPEND PLATFORM_SRC
platform/windows/sharedmemory.cpp
platform/windows/sharedsemaphore.cpp
)
else() # Darwin or Linux
set(PLATFORM_SRC)
@@ -152,6 +162,11 @@ else() # Darwin or Linux
endif()
endif()
endif()
list(APPEND PLATFORM_SRC
platform/posix/sharedmemory.cpp
platform/posix/sharedsemaphore.cpp
)
endif()
if(LIBICSNEO_BUILD_EXAMPLES)
@@ -168,15 +183,20 @@ endforeach()
set(SRC_FILES
communication/message/flexray/control/flexraycontrolmessage.cpp
communication/message/callback/streamoutput/a2bwavoutput.cpp
communication/message/neomessage.cpp
communication/message/ethphymessage.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/logicaldiskinfopacket.cpp
communication/packet/wivicommandpacket.cpp
communication/packet/i2cpacket.cpp
communication/packet/scriptstatuspacket.cpp
communication/decoder.cpp
communication/encoder.cpp
communication/ethernetpacketizer.cpp
@@ -184,6 +204,9 @@ set(SRC_FILES
communication/multichannelcommunication.cpp
communication/communication.cpp
communication/driver.cpp
communication/interprocessmailbox.cpp
communication/sdio.cpp
communication/socket.cpp
device/extensions/flexray/extension.cpp
device/extensions/flexray/controller.cpp
device/idevicesettings.cpp
@@ -257,8 +280,7 @@ target_include_directories(icsneocpp
${CMAKE_CURRENT_SOURCE_DIR}/include
${LIBICSNEO_EXTENSION_INCLUDE_PATHS}
)
set_property(TARGET icsneocpp PROPERTY POSITION_INDEPENDENT_CODE ON)
target_compile_features(icsneocpp PUBLIC cxx_auto_type cxx_constexpr cxx_lambdas cxx_nullptr cxx_range_for cxx_rvalue_references cxx_sizeof_member cxx_strong_enums)
message("Loaded extensions: " ${LIBICSNEO_EXTENSION_TARGETS})
target_link_libraries(icsneocpp PUBLIC ${LIBICSNEO_EXTENSION_TARGETS})
if(LIBICSNEO_ENABLE_FIRMIO)
@@ -273,40 +295,66 @@ endif()
if(LIBICSNEO_ENABLE_FTDI)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_FTDI)
endif()
if(LIBICSNEO_USE_DEVICE_SHARING)
target_compile_definitions(icsneocpp PRIVATE ICSNEO_ENABLE_DEVICE_SHARING)
endif()
# socket
if(WIN32)
target_link_libraries(icsneocpp PRIVATE ws2_32)
endif()
# fatfs
add_subdirectory(third-party/fatfs)
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)
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(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
target_link_libraries(icsneocpp PUBLIC "-framework CoreFoundation" "-framework IOKit")
endif()
# For SharedMemory and SharedSemaphore
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_link_libraries(icsneocpp PUBLIC rt)
endif()
if(LIBICSNEO_BUILD_ICSNEOC)
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
target_include_directories(icsneoc
@@ -351,17 +399,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 +419,10 @@ if(LIBICSNEO_BUILD_TESTS)
test/diskdriverwritetest.cpp
test/eventmanagertest.cpp
test/ethernetpacketizertest.cpp
test/interprocessmailboxtest.cpp
test/sockettest.cpp
test/i2cencoderdecodertest.cpp
test/a2bencoderdecodertest.cpp
)
target_link_libraries(libicsneo-tests gtest gtest_main)
+7 -1
View File
@@ -14,6 +14,9 @@
- RADStar 2
- CAN works
- Ethernet works
- RADA2B
- CAN works
- Ethernet works
- Connecting over USB
- ValueCAN 4 series
@@ -36,4 +39,7 @@
- neoVI PLASMA
- CAN works
- neoVI ION
- CAN works
- CAN works
- RADA2B
- CAN works
- Ethernet works
+2 -2
View File
@@ -112,13 +112,13 @@ icsneo_closeDevice(myDevice);
## Building from Source
### Windows
Building will require Microsoft Visual Studio 2017+ and CMake to be installed.
Building will require MSVC 2017 version 15.7 or newer and CMake to be installed.
### macOS
Getting the dependencies is easiest with the Homebrew package manager. You will also need XCode installed. You can then install CMake, an up-to-date version of GCC or Clang, and `libusb-1.0`.
### Linux
The dependencies are as follows
- CMake 3.12 or above
- GCC 4.7 or above, 4.8+ recommended
- GCC 7 or above
- `libusb-1.0-0-dev`
- `libpcap0.8-dev`
- `build-essential` is recommended
+7 -2
View File
@@ -251,7 +251,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 +486,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 +647,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;
+113 -2
View File
@@ -4,7 +4,7 @@
using namespace icsneo;
APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device) : eventStruct({}) {
APIEvent::APIEvent(Type type, Severity severity, const Device* device) : eventStruct({}) {
this->device = device;
if(device) {
serial = device->getSerial();
@@ -14,6 +14,17 @@ APIEvent::APIEvent(Type type, APIEvent::Severity severity, const Device* device)
init(type, severity);
}
APIEvent::APIEvent(neosocketevent_t evStruct, const Device* device)
{
this->device = device;
timepoint = EventClock::from_time_t(evStruct.timestamp);
serial = std::string(evStruct.serial);
eventStruct.eventNumber = evStruct.eventNumber;
eventStruct.severity = evStruct.severity;
eventStruct.description = DescriptionForType(APIEvent::getType());
eventStruct.timestamp = evStruct.timestamp;
}
void APIEvent::init(Type event, APIEvent::Severity severity) {
timepoint = EventClock::now();
eventStruct.description = DescriptionForType(event);
@@ -45,6 +56,15 @@ std::string APIEvent::describe() const noexcept {
return ss.str();
}
neosocketevent_t APIEvent::getNeoSocketEvent() const noexcept {
neosocketevent_t neoSocketEvent;
neoSocketEvent.eventNumber = eventStruct.eventNumber;
neoSocketEvent.severity = eventStruct.severity;
std::memcpy(neoSocketEvent.serial, eventStruct.serial, sizeof(eventStruct.serial));
neoSocketEvent.timestamp = eventStruct.timestamp;
return neoSocketEvent;
}
void APIEvent::downgradeFromError() noexcept {
eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning;
}
@@ -71,6 +91,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 +130,9 @@ 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.";
// Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
@@ -122,8 +146,31 @@ static constexpr const char* PCAP_COULD_NOT_START = "The PCAP driver could not b
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* SHARED_MEMORY_DATA_IS_NULL = "data() was called on invalid object.";
static constexpr const char* SHARED_MEMORY_FAILED_TO_CLOSE = "The server failed to close a shared memory location.";
static constexpr const char* SHARED_MEMORY_FAILED_TO_OPEN = "The server failed to open a shared memory location.";
static constexpr const char* SHARED_MEMORY_FAILED_TO_UNLINK = "The server failed to unlink a shared memory location.";
static constexpr const char* SHARED_MEMORY_FILE_TRUNCATE_ERROR = "The server failed to truncate shared memory file.";
static constexpr const char* SHARED_MEMORY_MAPPING_ERROR = "The server failed to map a shared memory file.";
static constexpr const char* SHARED_MEMORY_UNMAP_ERROR = "The server failed to unmap a shared memory file.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_CLOSE = "The server failed to close a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_OPEN = "The server failed to open a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_POST = "A post() call failed on a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_UNLINK = "The server failed to unlink a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_FAILED_TO_WAIT = "A wait() call failed on a shared semaphore.";
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_POST = "post() was called on a shared semaphore that is not open.";
static constexpr const char* SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT = "wait() was called on a shared semaphore that is not open.";
static constexpr const char* SOCKET_FAILED_CONNECT = "A socket connection was attempted but failed.";
static constexpr const char* SOCKET_FAILED_OPEN = "A socket failed to open.";
static constexpr const char* SOCKET_FAILED_CLOSE = "A socket failed to close.";
static constexpr const char* SOCKET_FAILED_READ = "A socket read operation failed.";
static constexpr const char* SOCKET_FAILED_WRITE = "A socket write operation failed.";
static constexpr const char* ACCEPTOR_FAILED_BIND = "A socket acceptor failed to bind.";
static constexpr const char* ACCEPTOR_FAILED_LISTEN = "A socket acceptor failed to listen.";
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
static constexpr const char* NO_ERROR_FOUND = "No errors found.";
static constexpr const char* INVALID = "An invalid internal error occurred.";
const char* APIEvent::DescriptionForType(Type type) {
switch(type) {
@@ -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,6 +281,12 @@ 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;
// Transport Errors
case Type::FailedToRead:
@@ -254,12 +309,58 @@ const char* APIEvent::DescriptionForType(Type type) {
return PCAP_COULD_NOT_FIND_DEVICES;
case Type::PacketDecodingError:
return PACKET_DECODING;
// Device Sharing Server Events
case Type::SharedMemoryDataIsNull:
return SHARED_MEMORY_DATA_IS_NULL;
case Type::SharedMemoryFailedToClose:
return SHARED_MEMORY_FAILED_TO_CLOSE;
case Type::SharedMemoryFailedToOpen:
return SHARED_MEMORY_FAILED_TO_OPEN;
case Type::SharedMemoryFailedToUnlink:
return SHARED_MEMORY_FAILED_TO_UNLINK;
case Type::SharedMemoryFileTruncateError:
return SHARED_MEMORY_FILE_TRUNCATE_ERROR;
case Type::SharedMemoryMappingError:
return SHARED_MEMORY_MAPPING_ERROR;
case Type::SharedMemoryUnmapError:
return SHARED_MEMORY_UNMAP_ERROR;
case Type::SharedSemaphoreFailedToClose:
return SHARED_SEMAPHORE_FAILED_TO_CLOSE;
case Type::SharedSemaphoreFailedToOpen:
return SHARED_SEMAPHORE_FAILED_TO_OPEN;
case Type::SharedSemaphoreFailedToPost:
return SHARED_SEMAPHORE_FAILED_TO_POST;
case Type::SharedSemaphoreFailedToUnlink:
return SHARED_SEMAPHORE_FAILED_TO_UNLINK;
case Type::SharedSemaphoreFailedToWait:
return SHARED_SEMAPHORE_FAILED_TO_WAIT;
case Type::SharedSemaphoreNotOpenForPost:
return SHARED_SEMAPHORE_NOT_OPEN_FOR_POST;
case Type::SharedSemaphoreNotOpenForWait:
return SHARED_SEMAPHORE_NOT_OPEN_FOR_WAIT;
case Type::SocketFailedToOpen:
return SOCKET_FAILED_OPEN;
case Type::SocketFailedToClose:
return SOCKET_FAILED_CLOSE;
case Type::SocketFailedToConnect:
return SOCKET_FAILED_CONNECT;
case Type::SocketFailedToRead:
return SOCKET_FAILED_READ;
case Type::SocketFailedToWrite:
return SOCKET_FAILED_WRITE;
case Type::SocketAcceptorFailedToBind:
return ACCEPTOR_FAILED_BIND;
case Type::SocketAcceptorFailedToListen:
return ACCEPTOR_FAILED_LISTEN;
// Other Errors
case Type::TooManyEvents:
return TOO_MANY_EVENTS;
case Type::Unknown:
return UNKNOWN;
case Type::NoErrorFound:
return NO_ERROR_FOUND;
default:
return INVALID;
}
@@ -279,4 +380,14 @@ bool EventFilter::match(const APIEvent& event) const noexcept {
return false;
return true;
}
neosocketeventfilter_t EventFilter::getNeoSocketEventFilter() const noexcept {
neosocketeventfilter_t filterStruct;
filterStruct.eventNumber = static_cast<decltype(filterStruct.eventNumber)>(type);
filterStruct.severity = static_cast<decltype(filterStruct.severity)>(severity);
if((serial.length() + 1) == sizeof(filterStruct.serial)) {
std::memcpy(&filterStruct.serial[0], serial.c_str(), sizeof(filterStruct.serial));
}
return filterStruct;
}
+59 -16
View File
@@ -1,5 +1,11 @@
#include "icsneo/api/eventmanager.h"
#include "icsneo/api/event.h"
#include <memory>
#include <thread>
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#endif
using namespace icsneo;
@@ -40,7 +46,7 @@ void EventManager::add(APIEvent event) {
if(i != downgradedThreads.end() && i->second) {
event.downgradeFromError();
{
std::lock_guard<std::mutex> eventsLock(eventsMutex);
std::lock_guard<std::mutex> eventsLock{eventsMutex};
addEventInternal(event);
} // free the lock so that callbacks may modify events
runCallbacks(event);
@@ -146,23 +152,60 @@ bool EventManager::isDowngradingErrorsOnCurrentThread() const {
return false;
}
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
std::optional<std::vector<neosocketevent_t>> EventManager::getServerEvents(const size_t& max)
{
auto socket = lockSocket();
if(!(socket.writeTyped<RPC>(RPC::GET_EVENTS) && socket.writeTyped<size_t>(max)))
return std::nullopt;
size_t count;
if(!socket.readTyped(count) || count < 0 || count > eventLimit)
return std::nullopt;
std::optional<std::vector<neosocketevent_t>> ret;
if(count == size_t(0))
return ret;
auto& eventStructs = ret.emplace(count);
if(!socket.read(eventStructs.data(), (eventStructs.size() * sizeof(neosocketevent_t))))
return std::nullopt;
return ret;
}
#endif // ICSNEO_ENABLE_DEVICE_SHARING
void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilter filter) {
std::lock_guard<std::mutex> lk(eventsMutex);
if(max == 0) // A limit of 0 indicates no limit
max = (size_t)-1;
size_t count = 0;
eventOutput.clear();
auto it = events.begin();
while(it != events.end()) {
if(filter.match(*it)) {
eventOutput.push_back(*it);
it = events.erase(it);
if(++count >= max)
break; // We now have as many written to output as we can
} else {
it++;
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
{
auto serverEvents = getServerEvents(max);
if(serverEvents) {
std::scoped_lock eventsLock{eventsMutex};
for(neosocketevent_t& event : *serverEvents) {
eventOutput.emplace_back(event);
}
if(max != 0u)
max -= eventOutput.size();
}
}
#endif
std::scoped_lock eventsLock{eventsMutex};
{
if(max == 0) // A limit of 0 indicates no limit
max = (size_t)-1;
size_t count = 0;
auto it = events.begin();
while(it != events.end()) {
if(filter.match(*it)) {
eventOutput.push_back(*it);
it = events.erase(it);
if(++count >= max)
break; // We now have as many written to output as we can
} else {
it++;
}
}
}
}
+7 -2
View File
@@ -638,8 +638,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)
+46 -14
View File
@@ -14,6 +14,10 @@
#include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/versionmessage.h"
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#endif
using namespace icsneo;
int Communication::messageCallbackIDCounter = 1;
@@ -21,7 +25,8 @@ int Communication::messageCallbackIDCounter = 1;
Communication::~Communication() {
if(redirectingRead)
clearRedirectRead();
close();
if(isOpen())
close();
}
bool Communication::open() {
@@ -66,6 +71,11 @@ bool Communication::isDisconnected() {
return driver->isDisconnected();
}
void Communication::modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb) {
std::scoped_lock lk(rawCallbacksMutex);
cb(rawCallbacks);
}
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
// This is here so that other communication types (like multichannel) can override it
return rawWrite(bytes);
@@ -150,22 +160,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 +203,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 +222,24 @@ 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) {
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
auto socket = lockSocket();
int64_t ms = timeout.count();
if(!(socket.writeTyped(RPC::DEVICE_LOCK) && socket.writeString(driver->device.serial) && socket.writeTyped(ms)))
return nullptr;
if(bool ret; !(socket.readTyped(ret) && ret))
return nullptr;
#endif
std::unique_lock<std::mutex> fnLk(syncMessageMutex); // Only allow for one sync message at a time
std::unique_lock<std::mutex> cvLk(cvMutex); // Don't let the callback fire until we're waiting for it
int cb = addMessageCallback(std::make_shared<MessageCallback>([&cvMutex, &returnedMessage, &cv](std::shared_ptr<Message> message) {
{
std::lock_guard<std::mutex> lk(m);
std::lock_guard<std::mutex> lk(cvMutex);
returnedMessage = message;
}
cv.notify_all();
@@ -228,8 +248,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);
@@ -237,6 +257,13 @@ std::shared_ptr<Message> Communication::waitForMessageSync(std::function<bool(vo
if(fail) // The caller's function failed, so don't return a message
returnedMessage.reset();
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
if(!(socket.writeTyped(RPC::DEVICE_UNLOCK) && socket.writeString(driver->device.serial)))
return nullptr;
if(bool ret; !(socket.readTyped(ret) && ret))
return nullptr;
#endif
// Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking
return returnedMessage;
@@ -251,7 +278,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)
@@ -272,6 +299,11 @@ void Communication::readTask() {
}
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
{
std::lock_guard lk(rawCallbacksMutex);
for(auto& cb : rawCallbacks)
cb(readBytes);
}
if(redirectingRead) {
// redirectingRead is an atomic so it can be set without acquiring a mutex
// However, we do not clear it without the mutex. The idea is that if another
+64
View File
@@ -5,16 +5,25 @@
#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/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 <iostream>
using namespace icsneo;
@@ -115,6 +124,20 @@ 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::Internal: {
switch(packet->network.getNetID()) {
case Network::NetID::Reset_Status: {
@@ -193,6 +216,18 @@ 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());
if(resp.header.command != ExtendedCommand::GenericReturn)
break; // Handle as a raw message
const auto msg = std::make_shared<ExtendedResponseMessage>(resp.command, resp.returnCode);
result = msg;
return true;
}
case Network::NetID::FlexRayControl: {
auto frResult = std::make_shared<FlexRayControlMessage>(*packet);
if(!frResult->decoded) {
@@ -286,6 +321,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,9 +337,30 @@ 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;
}
default:
break;
}
break;
}
case Network::Type::A2B: {
result = HardwareA2BPacket::DecodeToMessage(packet->data);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // A nullptr was returned, the packet was not long enough to decode
}
A2BMessage& msg = *static_cast<A2BMessage*>(result.get());
msg.network = packet->network;
return true;
}
}
+28
View File
@@ -6,6 +6,10 @@
#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"
using namespace icsneo;
@@ -68,6 +72,30 @@ 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
default:
report(APIEvent::Type::UnexpectedNetworkType, APIEvent::Severity::Error);
return false;
-1
View File
@@ -3,7 +3,6 @@
#include <iterator>
#include <cstring>
#include <cassert>
#include <iostream>
using namespace icsneo;
+60
View File
@@ -0,0 +1,60 @@
#include <cstring>
#include "icsneo/communication/interprocessmailbox.h"
using namespace icsneo;
bool InterprocessMailbox::open(const std::string& name, bool create)
{
if(!queuedSem.open(name + "-qs", create))
return false;
if(!emptySem.open(name + "-es", create, MESSAGE_COUNT))
return false;
if(!sharedMem.open(name + "-sm", BLOCK_SIZE * MESSAGE_COUNT, create))
return false;
valid = true;
return true;
}
InterprocessMailbox::operator bool() const
{
return valid;
}
bool InterprocessMailbox::close()
{
valid = false;
return queuedSem.close() && emptySem.close() && sharedMem.close();
}
bool InterprocessMailbox::read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout)
{
if(!queuedSem.wait(timeout))
return false;
auto it = sharedMem.data() + (index * BLOCK_SIZE);
messageLength = *(LengthFieldType*)it;
it += LENGTH_FIELD_SIZE;
std::memcpy(data, it, std::min(messageLength, MAX_DATA_SIZE));
if(!emptySem.post())
return false;
++index;
index %= MESSAGE_COUNT;
return true;
}
bool InterprocessMailbox::write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout)
{
if(!emptySem.wait(timeout))
return false; // the buffer is full and we timed out
auto it = sharedMem.data() + (index * BLOCK_SIZE);
*(LengthFieldType*)it = messageLength;
it += LENGTH_FIELD_SIZE;
std::memcpy(it, data, messageLength);
if(!queuedSem.post())
return false;
++index;
index %= MESSAGE_COUNT;
return true;
}
@@ -0,0 +1,100 @@
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.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;
}
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::DownStream)) {
close();
return false;
}
if(!writeSamples(a2bmsg, A2BMessage::A2BDirection::UpStream)) {
close();
return false;
}
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;
}
bool A2BWAVOutput::writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessage::A2BDirection dir) const
{
uint8_t numChannels = msg->getNumChannels();
uint8_t channel = 0;
uint32_t frame = 0;
uint8_t bitDepth = msg->getBitDepth();
while(true) {
auto sample = msg->getSample(dir, channel, frame);
if(!sample) {
if(channel == 0) {
break;
}
return false;
}
uint32_t audioSample = sample.value() >> (32 - bitDepth);
write((void*)(&audioSample), A2BPCM_SAMPLE_SIZE);
channel = (channel + 1) % numChannels;
if(channel == 0) {
frame++;
}
}
return true;
}
}
+163
View File
@@ -0,0 +1,163 @@
#include "icsneo/communication/packet/a2bpacket.h"
#include <cstring>
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;
}
auto getSampleFromBytes = [](uint8_t bytesPerSample, const uint8_t *bytes) {
A2BPCMSample result = 0;
for(auto i = 0; i < bytesPerSample; i++) {
result |= static_cast<uint32_t>(bytes[i]) << (i * 8);
}
return result;
};
const HardwareA2BPacket *data = (const HardwareA2BPacket*)bytestream.data();
uint32_t totalPackedLength = static_cast<uint32_t>(bytestream.size()) - static_cast<uint32_t>(coreMiniMessageHeaderSize); // First 28 bytes are message header.
uint8_t bytesPerChannel = data->header.channelSize16 ? 2 : 4;
uint8_t numChannels = data->header.channelNum;
uint8_t bitDepth = data->header.channelSize16 ? A2BPCM_L16 : A2BPCM_L24;
std::shared_ptr<A2BMessage> msg = std::make_shared<A2BMessage>(bitDepth, bytesPerChannel, numChannels);
msg->channelSize16 = data->header.channelSize16;
msg->monitor = data->header.monitor;
msg->txmsg = data->header.txmsg;
msg->errIndicator = data->header.errIndicator;
msg->syncFrame = data->header.syncFrame;
msg->rfu2 = data->header.rfu2;
const uint8_t *bytes = bytestream.data();
bytes+=coreMiniMessageHeaderSize;
uint8_t channel = 0;
for(uint32_t i = 0; i < totalPackedLength; i += 2 * static_cast<uint32_t>(bytesPerChannel), bytes += 2 * bytesPerChannel, channel = (channel + 1) % numChannels) {
msg->addSample(
getSampleFromBytes(bytesPerChannel, bytes),
A2BMessage::A2BDirection::DownStream,
channel
);
msg->addSample(
getSampleFromBytes(bytesPerChannel, bytes + bytesPerChannel),
A2BMessage::A2BDirection::UpStream,
channel
);
}
return msg;
}
bool HardwareA2BPacket::EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
if(message.getBytesPerSample() != 2 && message.getBytesPerSample() != 4) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return false;
}
size_t sampleBytes = message.getNumSamples() * static_cast<size_t>(message.getBytesPerSample());
size_t totalSize = coreMiniMessageHeaderSize + sampleBytes;
if(totalSize > a2bMessageMaxLength) {
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
return false;
}
bytestream.reserve(totalSize);
bytestream.push_back(message.getNumChannels());
bytestream.push_back(message.channelSize16 ? 1 : 0);
uint8_t a2b2Bits = 0;
if(message.monitor) {
a2b2Bits = a2b2Bits | 1;
}
if(message.txmsg) {
a2b2Bits = a2b2Bits | (1 << 1);
}
if(message.errIndicator) {
a2b2Bits = a2b2Bits | (1 << 2);
}
if(message.syncFrame) {
a2b2Bits = a2b2Bits | (1 << 3);
}
bytestream.push_back(a2b2Bits);
bytestream.push_back(0);
bytestream.push_back(static_cast<uint8_t>(message.rfu2));
bytestream.push_back(static_cast<uint8_t>(message.rfu2 >> 8));
for(size_t i = 0; i < (coreMiniMessageHeaderSize - a2bHeaderSize); i++)
bytestream.push_back(0);
uint8_t numChannels = message.getNumChannels();
uint8_t channel = 0;
uint32_t frame = 0;
auto writeSample = [&](A2BPCMSample&& sample) {
for(uint32_t i = 0; i < static_cast<uint32_t>(message.getBytesPerSample()); i++) {
bytestream.push_back(static_cast<uint8_t>((sample >> (i*8))));
}
};
while(true) {
auto dsSample = message.getSample(A2BMessage::A2BDirection::DownStream, channel, frame);
auto usSample = message.getSample(A2BMessage::A2BDirection::UpStream, channel, frame);
// Check if getSample failed for both downstream and upstream
if(!dsSample && !usSample) {
if(channel != 0) {
//Incomplete frame, the frame we are currently on does not contain all channel samples
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
return false;
}
// Since no samples have been written for the current frame yet and there are no more
// samples in both upstream and downstream, we can break and end parsing.
break;
}
// Since the first case failed, at least one of the streams still has samples.
// This case checks to see if the other stream does not have a sample.
else if(!dsSample || !usSample) {
// Report an error since we must have a one to one correspondence between upstream
// and downstream.
report(APIEvent::Type::A2BMessageIncompleteFrame, APIEvent::Severity::Error);
return false;
}
writeSample(std::move(dsSample.value()));
writeSample(std::move(usSample.value()));
channel = (channel + 1) % numChannels;
if(channel == 0)
frame++;
}
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
+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;
}
}
@@ -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;
}
+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;
}
+179
View File
@@ -0,0 +1,179 @@
#include <cstring>
#include "icsneo/communication/sdio.h"
#include "icsneo/platform/sharedsemaphore.h"
#include "icsneo/platform/sharedmemory.h"
#include "icsneo/device/device.h"
#include "icsneo/communication/socket.h"
using namespace icsneo;
void SDIO::Find(std::vector<FoundDevice>& found) {
auto socket = lockSocket();
if(!socket.writeTyped(RPC::DEVICE_FINDER_FIND_ALL))
return;
uint16_t count;
if(!socket.readTyped(count))
return;
static constexpr auto serialSize = sizeof(FoundDevice::serial);
std::vector<std::array<char, sizeof(FoundDevice::serial)>> serials(count);
if(!socket.read(serials.data(), serials.size() * serialSize))
return;
for(const auto& serial : serials) {
auto& foundDevice = found.emplace_back();
for(std::size_t i = 0; i < serialSize - 1 /* omit '\0' */; i++)
foundDevice.serial[i] = static_cast<char>(std::toupper(serial[i]));
foundDevice.makeDriver = [](const device_eventhandler_t& r, neodevice_t& d) {
return std::unique_ptr<SDIO>(new SDIO(r, d));
};
}
}
bool SDIO::open() {
{
auto socket = lockSocket();
if(!(socket.writeTyped(RPC::SDIO_OPEN) && socket.writeString(device.device->getSerial())))
return false;
if(bool ret; !(socket.readTyped(ret) && ret))
return false;
{
std::string mailboxName;
if(!socket.readString(mailboxName))
return false;
if(!inboundIO.open(mailboxName))
return false;
}
{
std::string mailboxName;
if(!socket.readString(mailboxName))
return false;
if(!outboundIO.open(mailboxName))
return false;
}
}
readThread = std::thread(&SDIO::readTask, this);
writeThread = std::thread(&SDIO::writeTask, this);
deviceOpen = true;
return deviceOpen;
}
bool SDIO::close() {
if(!isOpen() && !isDisconnected()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return false;
}
closing = true;
// wait for the reader/writer threads to close
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// unblocks the reader/writer threads
if(!inboundIO.close())
return false;
if(!outboundIO.close())
return false;
if(readThread.joinable())
readThread.join();
if(writeThread.joinable())
writeThread.join();
{
auto socket = lockSocket();
if(!socket.writeTyped(RPC::SDIO_CLOSE))
return false;
if(!socket.writeString(device.device->getSerial()))
return false;
if(bool ret; !(socket.readTyped(ret) && ret))
return false;
}
uint8_t flush;
WriteOperation flushop;
while(readQueue.try_dequeue(flush)) {}
while(writeQueue.try_dequeue(flushop)) {}
closing = false;
disconnected = false;
deviceOpen = false;
return true;
}
bool SDIO::isOpen() {
return deviceOpen;
}
void SDIO::readTask() {
uint8_t data[MAX_DATA_SIZE];
uint16_t messageLength;
while(!closing) {
if(!inboundIO.read(data, messageLength, std::chrono::milliseconds(100))) {
if(!inboundIO)
break;
continue;
}
if(messageLength > 0) {
if(messageLength > MAX_DATA_SIZE) { // split message
std::vector<uint8_t> reassembled(messageLength);
std::memcpy(reassembled.data(), data, MAX_DATA_SIZE);
auto offset = reassembled.data() + MAX_DATA_SIZE;
for(auto remaining = messageLength - MAX_DATA_SIZE; remaining > 0; remaining -= messageLength) {
if(!inboundIO.read(offset, messageLength, std::chrono::milliseconds(10))) {
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
break;
}
offset += messageLength;
}
readQueue.enqueue_bulk(reassembled.data(), reassembled.size());
} else {
readQueue.enqueue_bulk(data, messageLength);
}
}
}
}
void SDIO::writeTask() {
WriteOperation writeOp;
while(!closing && !isDisconnected()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
const auto dataSize = static_cast<LengthFieldType>(writeOp.bytes.size());
const auto tryWrite = [&](const void* input, LengthFieldType length) -> bool {
for(int i = 0; i < 50; ++i) { // try to write for 5s, making sure we can close if need be
if(outboundIO.write(input, length, std::chrono::milliseconds(100)))
return true;
if(!outboundIO)
return false;
}
disconnected = true;
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
return false;
};
if(!tryWrite(writeOp.bytes.data(), dataSize))
continue;
if(writeOp.bytes.size() > MAX_DATA_SIZE) {
auto offset = writeOp.bytes.data() + MAX_DATA_SIZE;
for(LengthFieldType remaining = dataSize - MAX_DATA_SIZE; remaining > 0; ) {
const auto toWrite = std::min(MAX_DATA_SIZE, remaining);
if(!tryWrite(offset, toWrite))
break;
remaining -= toWrite;
offset += toWrite;
}
}
}
}
+257
View File
@@ -0,0 +1,257 @@
#include "icsneo/communication/socket.h"
#include "icsneo/api/event.h"
#include "icsneo/api/eventmanager.h"
namespace icsneo {
bool SocketBase::open() {
#ifdef _WIN32
WSADATA wsaData;
if(::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
return false;
}
#endif
if((sockFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) < 0) {
#ifdef _WIN32
::WSACleanup();
#endif
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToOpen, APIEvent::Severity::Error);
return false;
}
sockIsOpen = true;
return true;
}
bool SocketBase::close() {
#ifdef _WIN32
if(::closesocket(sockFileDescriptor) < 0) {
// should probably check for WSAEWOULDBLOCK as ::closesocket must be repeated to close in that case
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
#endif
return false;
}
::WSACleanup();
#else
// ignore ENOTCONN from ::shutdown as the peer may have already forcibly closed its socket (e.g. a crash)
if( ((::shutdown(sockFileDescriptor, SHUT_RDWR) < 0) && (ENOTCONN != errno)) ||
((::close(sockFileDescriptor) < 0) && (EBADF == errno)) )
{
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToClose, APIEvent::Severity::Error);
#endif
return false;
}
#endif
sockIsOpen = false;
sockIsConnected = false;
return true;
}
bool SocketBase::connect() {
sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if( (!isOpen() && !open()) ||
(::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
(::connect(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
{
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToConnect, APIEvent::Severity::Error);
return false;
}
#ifdef _WIN32
DWORD tv = 5000u; // 5 second receive timeout but in windows
#else
struct timeval tv;
tv.tv_sec = 5u; // 5 second receive timeout
tv.tv_usec = 0;
setIgnoreSIGPIPE();
#endif
// Set the 5 second timeout from above in the socket options
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
::setsockopt(sockFileDescriptor, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
sockIsConnected = true;
return true;
}
bool SocketBase::isOpen() {
return sockIsOpen;
}
bool SocketBase::isConnected() {
return sockIsConnected;
}
bool SocketBase::read(void* output, std::size_t length) {
if(!(isOpen() && isConnected()))
return false;
#ifdef _WIN32
return ::recv(sockFileDescriptor, (char*)output, (int)length, 0) > 0;
#else
if(::read(sockFileDescriptor, output, length) <= 0) {
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToRead, APIEvent::Severity::Error);
return false;
}
return true;
#endif
}
bool SocketBase::write(const void* input, std::size_t length) {
if(!(isOpen() && isConnected()))
return false;
#ifdef _WIN32
if(::send(sockFileDescriptor, (char*)input, (int)length, 0) < 0) {
switch(WSAGetLastError()) {
case WSAETIMEDOUT:
case WSAENOTCONN:
case WSAESHUTDOWN:
case WSAECONNRESET:
case WSAECONNABORTED:
{
sockIsOpen = false;
break;
}
default:
{
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
break;
}
}
return false;
}
#else
if(::write(sockFileDescriptor, input, length) < 0) {
switch(errno) {
case EPIPE:
case ETIMEDOUT:
{
sockIsOpen = false;
break;
}
default:
{
EventManager::GetInstance().add(APIEvent::Type::SocketFailedToWrite, APIEvent::Severity::Error);
break;
}
}
return false;
}
#endif
return true;
}
bool SocketBase::readString(std::string& str) {
size_t length;
if(!read(&length, sizeof(length)))
return false;
str.resize(length);
if(!read(str.data(), length))
return false;
return true;
}
bool SocketBase::writeString(const std::string& str) {
size_t length = str.size();
if(!write(&length, sizeof(length)))
return false;
if(!write(str.data(), length))
return false;
return true;
}
ActiveSocket::ActiveSocket(SocketFileDescriptor sockFD) {
sockFileDescriptor = sockFD;
sockIsOpen = true;
sockIsConnected = true;
}
ActiveSocket::ActiveSocket(Protocol protocol, uint16_t port) {
this->protocol = protocol;
this->port = port;
}
ActiveSocket::~ActiveSocket() {
if(isOpen())
close();
}
LockedSocket::LockedSocket(SocketBase& base, std::unique_lock<std::mutex>&& l) :
SocketBase(base), lock(std::move(l)) {
}
LockedSocket lockSocket() {
static ActiveSocket socket(SocketBase::Protocol::TCP, RPC_PORT);
if(!socket.isOpen())
socket.open();
if(!socket.isConnected())
socket.connect();
static std::mutex lock;
return LockedSocket(socket, std::unique_lock<std::mutex>(lock));
}
void SocketBase::setIgnoreSIGPIPE() {
#ifndef _WIN32
struct sigaction sa{};
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
::sigaction(SIGPIPE, &sa, NULL);
#endif
}
Acceptor::Acceptor(Protocol protocol, uint16_t port)
: ActiveSocket(protocol, port) {
}
bool Acceptor::initialize() {
if(open() && bind() && listen()) {
isValid = true;
return true;
}
return false;
}
std::shared_ptr<ActiveSocket> Acceptor::accept()
{
if(!isValid)
return nullptr;
const SocketFileDescriptor acceptFd = ::accept(sockFileDescriptor, (sockaddr*)NULL, NULL);
if(acceptFd < 0)
return nullptr;
return std::make_shared<ActiveSocket>(acceptFd);
}
bool Acceptor::bind()
{
sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if( (::inet_pton(addr.sin_family, "127.0.0.1", &addr.sin_addr) <= 0) ||
(::bind(sockFileDescriptor, (sockaddr*)&addr, sizeof(addr)) < 0) )
{
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToBind, APIEvent::Severity::Error);
return false;
}
return true;
}
bool Acceptor::listen()
{
if(::listen(sockFileDescriptor, UINT8_MAX) < 0) {
EventManager::GetInstance().add(APIEvent::Type::SocketAcceptorFailedToListen, APIEvent::Severity::Error);
return false;
}
return true;
}
} //namespace icsneo
+814 -85
View File
File diff suppressed because it is too large Load Diff
+120 -35
View File
@@ -1,8 +1,13 @@
#include <array>
#include "icsneo/device/devicefinder.h"
#include "icsneo/platform/devices.h"
#include "icsneo/device/founddevice.h"
#include "icsneo/communication/sdio.h"
#include "generated/extensions/builtin.h"
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
#include "icsneo/communication/socket.h"
#else
#ifdef ICSNEO_ENABLE_FIRMIO
#include "icsneo/platform/firmio.h"
#endif
@@ -18,6 +23,7 @@
#ifdef ICSNEO_ENABLE_FTDI
#include "icsneo/platform/ftdi.h"
#endif
#endif
using namespace icsneo;
@@ -40,135 +46,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_DEVICE_SHARING
SDIO::Find(newDriverFoundDevices);
#else
#ifdef ICSNEO_ENABLE_FIRMIO
FirmIO::Find(driverFoundDevices);
FirmIO::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_FTDI
FTDI::Find(driverFoundDevices);
FTDI::Find(newDriverFoundDevices);
#endif
#endif
std::vector<std::shared_ptr<Device>> foundDevices;
// 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 __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 __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 __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() {
static std::vector<DeviceType> supportedDevices = {
static std::vector<DeviceType> supportedDevices;
if (!supportedDevices.empty())
return supportedDevices;
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
{
auto socket = lockSocket();
if(!socket.writeTyped(RPC::DEVICE_FINDER_GET_SUPORTED_DEVICES))
return supportedDevices;
uint16_t count;
if(!socket.readTyped(count))
return supportedDevices;
std::vector<devicetype_t> devices(count);
socket.read(devices.data(), devices.size() * sizeof(devicetype_t));
supportedDevices.reserve(count);
for(auto& dev : devices)
supportedDevices.emplace_back(DeviceType(dev));
}
#else
supportedDevices = {
#ifdef __ETHERBADGE_H_
EtherBADGE::DEVICE_TYPE,
@@ -202,6 +282,10 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
NeoVIPLASMA::DEVICE_TYPE,
#endif
#ifdef __RADA2B_H_
RADA2B::DEVICE_TYPE,
#endif
#ifdef __RADEPSILON_H_
RADEpsilon::DEVICE_TYPE,
#endif
@@ -267,6 +351,7 @@ const std::vector<DeviceType>& DeviceFinder::GetSupportedDevices() {
#endif
};
#endif
return supportedDevices;
}
+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;
+49 -4
View File
@@ -4,19 +4,30 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> ReadDriver::readLogicalDisk(Communication& com, device_eventhandler_t report,
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) {
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 +47,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);
@@ -65,7 +76,41 @@ 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) {
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) {
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;
}
+15 -24
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,
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) {
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);
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(),
auto bytesTransferred = writeLogicalDiskAligned(com, report, currentBlock * idealBlockSize,
useAlignedWriteBuffer ? alignedWriteBuffer.data() : (from + fromOffset), idealBlockSize, timeout);
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);
return ret;
}
+153 -158
View File
@@ -12,202 +12,197 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> ExtExtractorDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
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) {
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);
if (ret.has_value())
break;
}
return ret;
}
optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
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) {
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();
cv.notify_all();
return;
}
std::mutex m;
std::condition_variable cv;
uint16_t receiving = 0; // How much are we about to get before another header or completion
uint64_t received = 0;
uint16_t receivedCurrent = 0;
size_t skipping = 0;
std::vector<uint8_t> header;
std::unique_lock<std::mutex> lk(m);
bool error = !com.redirectRead([&](std::vector<uint8_t>&& data) {
std::unique_lock<std::mutex> lk2(m);
if(error) {
lk2.unlock();
cv.notify_all();
return;
}
if(skipping > data.size()) {
skipping -= data.size();
return;
}
size_t offset = skipping;
skipping = 0;
while(offset < data.size()) {
size_t left = data.size() - offset;
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 << "Going to process " << left << " bytes" << std::endl;
std::cout << "Got " << int(headerLeft) << " bytes of header at " << offset << " (complete " <<
header.size() << ')' << std::endl;
#endif
if(header.size() != HeaderLength) {
if(header.empty() && left && data[offset] != 0xaa) {
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 header " << int(data[offset]) << ' ' << int(offset) << std::endl;
std::cout << "Incorrect extended response length " << int(length) << ' ' << 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
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;
}
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
offset += length;
continue;
}
left = data.size() - offset;
auto count = uint16_t(std::min<uint64_t>(std::min<uint64_t>(receiving - receivedCurrent, left), amount - received));
// 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 << "With " << int(left) << " bytes " << int(offset) << std::endl;
std::cout << "Started packet of size " << receiving << " bytes" << 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;
left = data.size() - offset;
auto count = uint16_t(std::min<uint64_t>(std::min<uint64_t>(receiving - receivedCurrent, left), amount - received));
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
std::cout << "With " << int(left) << " bytes " << int(offset) << std::endl;
#endif
memcpy(into + received, data.data() + offset, count);
received += count;
receivedCurrent += count;
offset += count;
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;
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(); });
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
if(hitTimeout || error)
return nullopt;
if(error)
return std::nullopt;
cachedAt = std::chrono::steady_clock::now();
cachePos = pos;
}
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 std::nullopt;
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
if(hitTimeout || error)
return std::nullopt;
memcpy(into, cache.data(), size_t(amount));
return amount;
}
+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);
}
+29 -45
View File
@@ -5,70 +5,54 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
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;
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);
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) {
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] {
@@ -86,7 +70,7 @@ optional<uint64_t> NeoMemoryDiskDriver::writeLogicalDiskAligned(Communication& c
}, NeoMemoryDone, timeout);
if(!msg)
return nullopt;
return std::nullopt;
return SectorSize;
}
+9 -9
View File
@@ -3,26 +3,26 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
std::optional<uint64_t> NullDriver::readLogicalDisk(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
std::optional<uint64_t> NullDriver::readLogicalDiskAligned(Communication&, device_eventhandler_t report,
uint64_t, uint8_t*, uint64_t, std::chrono::milliseconds) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
std::optional<uint64_t> NullDriver::writeLogicalDisk(Communication&, device_eventhandler_t report, ReadDriver&,
uint64_t, const uint8_t*, uint64_t, std::chrono::milliseconds) {
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) {
report(APIEvent::Type::DiskNotSupported, APIEvent::Severity::Error);
return nullopt;
return std::nullopt;
}
+42 -51
View File
@@ -6,73 +6,64 @@
using namespace icsneo;
using namespace icsneo::Disk;
optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
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) {
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;
}
+16
View File
@@ -100,3 +100,19 @@ Write Blocking Status
The write blocking status of the device determines the behavior of attempting to transmit to the device (likely via sending messages) with a large backlog of messages.
If write blocking is enabled, then the transmitting thread will wait for the entire buffer to be transmitted.
If write blocking is disabled, then the attempt to transmit will simply fail and an error will be logged on the calling thread.
A2B Wave Output
~~~~~~~~~~~~~~~~~~~~
Users may add a ``icsneo::A2BWAVOutput`` message callback to their device in order to write A2B PCM data to a WAVE file. The message callback listens for ``icsneo::A2BMessage``
messages and writes both downstream and upstream channels to a single wave file. If downstream and upstream each have ``32`` channels, the wave file will contain ``2*32 = 64``
total channels. The first half of the channels, channels ``0-31`` in the outputted wave file, represent downstream channel ``0-31``. Likewise, the second half of the channels,
channels ``32-63`` in the outputted wave file, represent upstream channel ``0-31``. Let ``NUM_CHANNELS`` be the total number of channels in a single stream. If we introduce a
variable ``IS_UPSTREAM`` which is ``0`` when downstream and ``1`` when upstream and desired a channel ``CHANNEL_NUM`` in either downstream or upstream the
channel ``IS_UPSTREAM * NUM_CHANNELS + CHANNEL_NUM`` would correspond to the channel in the outputted wave file.
Wave files may be split by channel using programs such as ``FFmpeg``. Consider a file ``out.wav`` which was generated using a ``icsneo::A2BWAVOutput`` object
and contains ``32`` channels per stream. The ``icsneo::A2BWavoutput`` object injested PCM data with a sample rate of ``44.1 kHz`` and bit depth of ``24``. The corresponding
channel of upstream channel ``8`` in ``out.wav`` would be ``1*32 + 8 = 40``. The following ``FFmpeg`` command may be ran in a linux environment to create a new wave
file ``out_upstream_ch8.wav`` which contains only PCM samples off of upstream channel ``8``.
``ffmpeg -i out.wav -ar 44100 -acodec pcm_s24le -map_channel 0.0.40 out_upstream_ch8.wav``
+6
View File
@@ -1,6 +1,8 @@
option(LIBICSNEO_BUILD_C_INTERACTIVE_EXAMPLE "Build the command-line interactive 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)
# Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
@@ -18,6 +20,10 @@ 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_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp)
# endif()
+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)
+48
View File
@@ -0,0 +1,48 @@
# libicsneo C++ Example
This is an example console application which uses libicsneo to connect to an Intrepid Control Systems hardware device. It has both interactive and simple examples for sending and receiving CAN & CAN FD traffic.
## Building
This example shows how to use the C++ version of libicsneo with CMake. It will build libicsneo along with your project.
First, you need to clone the repository onto your local machine. Run:
```shell
git clone https://github.com/intrepidcs/libicsneo-examples --recursive
```
Alternatively, if you cloned without the `--recursive flag`, you must enter the `libicsneo-examples` folder and run the following:
```shell
git submodule update --recursive --init
```
If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build!
### Windows using Visual Studio 2017+
1. Launch Visual Studio and open the `libicsneo-examples` folder.
2. Choose `File->Open->CMake...`
3. Navigate to the `libicsneocpp-example` folder and select the `CMakeLists.txt` there.
4. Visual Studio will process the CMake project.
5. Choose the dropdown attached to the green play button (labelled "select startup item...") in the toolbar.
6. Select `libicsneocpp-simple-example.exe`
7. Press the green play button to compile and run the example.
### Ubuntu 18.04 LTS
1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev`
2. Change directories to your `libicsneo-examples/libicsneocpp-example` folder and create a build directory by running `mkdir -p build`
3. Enter the build directory with `cd build`
4. Run `cmake ..` to generate your Makefile.
* Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on.
5. Run `make libicsneocpp-interactive-example` to build.
* Hint! Speed up your build by using multiple processors! Use `make libicsneocpp-interactive-example -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup.
6. Now run `sudo ./libicsneocpp-interactive-example` to run the example.
* Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices.
7. If you wish to run the simple example instead, replace any instances of "interactive" with "simple" in steps 5 and 6.
### macOS
Instructions coming soon&trade;
+52
View File
@@ -0,0 +1,52 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
const std::string rada2bSerial = "Your RADA2B serial number.";
int main() {
std::cout << "Start example\n";
auto devices = icsneo::FindAllDevices();
std::shared_ptr<icsneo::Device> rada2b;
for(auto& device : devices) {
if(device->getSerial() == rada2bSerial) {
rada2b = device;
}
}
rada2b->open();
rada2b->goOnline();
std::cout << rada2b->describe() << "\n";
auto handler1 = rada2b->addMessageCallback(std::make_shared<icsneo::A2BWAVOutput>("examples/cpp/a2b/src/out.wav")); // Starts writing A2B PCM data to out.wav
auto handler2 = rada2b->addMessageCallback(
std::make_shared<icsneo::MessageCallback>(
[](std::shared_ptr<icsneo::Message> message) {
std::cout << "Got in callback " << std::endl;
if(message->type == icsneo::Message::Type::Frame) {
std::shared_ptr<icsneo::Frame> frame = std::static_pointer_cast<icsneo::Frame>(message);
if(frame->network.getType() == icsneo::Network::Type::A2B) {
std::shared_ptr<icsneo::A2BMessage> msg = std::static_pointer_cast<icsneo::A2BMessage>(frame);
std::cout << "Got A2B Message" << std::endl;
}
}
}));
std::this_thread::sleep_for(std::chrono::seconds(5)); // captures 5 seconds of A2B data.
rada2b->removeMessageCallback(handler1);
rada2b->removeMessageCallback(handler2);
std::cout << "End A2B example\n";
rada2b->goOffline();
rada2b->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);
}));
+7 -1
View File
@@ -24,4 +24,10 @@ endif()
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third-party/libicsneo ${CMAKE_CURRENT_BINARY_DIR}/third-party/libicsneo)
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
target_link_libraries(libicsneocpp-simple-example icsneocpp)
target_link_libraries(libicsneocpp-simple-example icsneocpp)
add_executable(libicsneocpp-simple-rx src/SimpleRx.cpp)
add_executable(libicsneocpp-simple-tx src/SimpleTx.cpp)
target_link_libraries(libicsneocpp-simple-rx icsneocpp)
target_link_libraries(libicsneocpp-simple-tx icsneocpp)
add_executable(libicsneocpp-simple-events src/SimpleEvents.cpp)
target_link_libraries(libicsneocpp-simple-events icsneocpp)
+57
View File
@@ -0,0 +1,57 @@
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "icsneo/icsneocpp.h"
int main(int argc, char** argv) {
std::vector<std::string> args(argv, argv + argc);
if(args.size() != 2) {
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
return -1;
}
const auto& deviceSerial = args[1];
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
for(const auto& dev : icsneo::FindAllDevices()) {
if(serial == dev->getSerial())
return dev;
}
return nullptr;
};
std::cout << "Finding device... " << std::flush;
const auto device = findDevice(deviceSerial);
if(!device) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Opening device... " << std::flush;
if(!device->open()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Going online... " << std::flush;
if(!device->goOnline()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Getting events from server..." << std::endl;
icsneo::EventFilter ef = {};
auto events = icsneo::GetEvents(ef, 0UL);
if(events.size() == 0)
std::cout << "Event return is empty :(" << std::endl;
for(auto& event: events)
std::cout << event.describe() << std::endl;
return 0;
}
+8 -2
View File
@@ -9,6 +9,12 @@ int main() {
// Print version
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
// Register an event callback so we can see any errors that come in
icsneo::EventManager::GetInstance().downgradeErrorsOnCurrentThread();
icsneo::EventManager::GetInstance().addEventCallback(icsneo::EventCallback([](std::shared_ptr<icsneo::APIEvent> evt) {
std::cerr << evt->describe() << std::endl;
}));
std::cout<< "Supported devices:" << std::endl;
for(auto& dev : icsneo::GetSupportedDevices())
std::cout << '\t' << dev.getGenericProductName() << std::endl;
@@ -141,7 +147,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 +295,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;
+66
View File
@@ -0,0 +1,66 @@
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "icsneo/icsneocpp.h"
int main(int argc, char** argv) {
std::vector<std::string> args(argv, argv + argc);
if(args.size() != 2) {
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
return -1;
}
const auto& deviceSerial = args[1];
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
for(const auto& dev : icsneo::FindAllDevices()) {
if(serial == dev->getSerial())
return dev;
}
return nullptr;
};
std::cout << "Finding device... " << std::flush;
const auto device = findDevice(deviceSerial);
if(!device) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Opening device... " << std::flush;
if(!device->open()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Going online... " << std::flush;
if(!device->goOnline()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Streaming CAN messages in, enter anything to stop..." << std::endl;
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([&](std::shared_ptr<icsneo::Message> message) {
if(message->type == icsneo::Message::Type::Frame) {
auto frame = std::static_pointer_cast<icsneo::Frame>(message);
if(frame->network.getType() == icsneo::Network::Type::CAN) {
auto canMessage = std::static_pointer_cast<icsneo::CANMessage>(message);
std::cout << '\r';
for(auto& databyte : canMessage->data)
std::cout << std::hex << std::setw(2) << (uint32_t)databyte << ' ';
std::cout << std::flush;
}
}
}));
std::cin.ignore();
device->removeMessageCallback(handler);
return 0;
}
+75
View File
@@ -0,0 +1,75 @@
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <memory>
#include "icsneo/icsneocpp.h"
int main(int argc, char** argv) {
std::vector<std::string> args(argv, argv + argc);
if (args.size() != 2) {
std::cerr << "usage: " << args.front() << " <device serial>" << std::endl;
return -1;
}
const auto& deviceSerial = args[1];
const auto findDevice = [](const auto& serial) -> std::shared_ptr<icsneo::Device> {
for (const auto& dev : icsneo::FindAllDevices()) {
if (serial == dev->getSerial())
return dev;
}
return nullptr;
};
std::cout << "Finding device... " << std::flush;
const auto device = findDevice(deviceSerial);
if (!device) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Opening device... " << std::flush;
if (!device->open()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::cout << "Going online... " << std::flush;
if (!device->goOnline()) {
std::cerr << "FAIL" << std::endl;
return -1;
}
std::cout << "OK" << std::endl;
std::atomic<bool> stop = false;
std::thread thread([&] {
std::cin.ignore();
std::cout << "Stopping..." << std::endl;
stop = true;
});
auto txMessage = std::make_shared<icsneo::CANMessage>();
txMessage->network = icsneo::Network::NetID::HSCAN;
txMessage->arbid = 0x1C5001C5;
txMessage->data.insert(txMessage->data.begin(), sizeof(size_t), 0);
txMessage->isExtended = true;
txMessage->isCANFD = true;
std::cout << "Streaming CAN messages out, enter anything to stop..." << std::endl;
size_t& value = *(size_t*)txMessage->data.data();
while (!stop) {
device->transmit(txMessage);
value++;
}
thread.join();
return 0;
}
+74 -4
View File
@@ -4,15 +4,49 @@
#include <stdint.h>
#include <time.h>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4201) // nameless struct/union
#endif
typedef struct {
const char* description;
time_t timestamp;
uint32_t eventNumber;
uint8_t severity;
char serial[7];
} neoeventcontext_t;
typedef struct {
time_t timestamp;
union {
struct {
uint32_t eventNumber;
int8_t severity;
char serial[7];
};
neoeventcontext_t eventContext;
};
} neosocketevent_t;
typedef neoeventcontext_t neosocketeventfilter_t;
typedef struct {
const char* description;
union {
struct {
time_t timestamp;
uint32_t eventNumber;
uint8_t severity;
char serial[7];
};
neosocketevent_t socketEvent;
};
uint8_t reserved[16];
} neoevent_t;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#ifdef __cplusplus
#include <vector>
@@ -48,7 +82,9 @@ public:
MessageMaxLengthExceeded = 0x1012,
ValueNotYetPresent = 0x1013,
Timeout = 0x1014,
WiVINotSupported = 0x1015,
TestEvent = 0x1016,
// Device Events
PollingMessageOverflow = 0x2000,
NoSerialNumber = 0x2001, // api
@@ -86,6 +122,10 @@ public:
SettingsDefaultsUsed = 0x2033,
AtomicOperationRetried = 0x2034,
AtomicOperationCompletedNonatomically = 0x2035,
WiVIStackRefreshFailed = 0x2036,
WiVIUploadStackOverflow = 0x2037,
I2CMessageExceedsMaxLength = 0x2038,
A2BMessageIncompleteFrame = 0x2039,
// Transport Events
FailedToRead = 0x3000,
@@ -98,7 +138,31 @@ public:
PCAPCouldNotStart = 0x3102,
PCAPCouldNotFindDevices = 0x3103,
PacketDecodingError = 0x3104,
// Device Sharing Server Events
SharedMemoryDataIsNull = 0x4001,
SharedMemoryFailedToClose = 0x4002,
SharedMemoryFailedToOpen = 0x4003,
SharedMemoryFailedToUnlink = 0x4004,
SharedMemoryFileTruncateError = 0x4005,
SharedMemoryMappingError = 0x4006,
SharedMemoryUnmapError = 0x4007,
SharedSemaphoreFailedToClose = 0x4008,
SharedSemaphoreFailedToOpen = 0x4009,
SharedSemaphoreFailedToPost = 0x4010,
SharedSemaphoreFailedToUnlink = 0x4011,
SharedSemaphoreFailedToWait = 0x4012,
SharedSemaphoreNotOpenForPost = 0x4013,
SharedSemaphoreNotOpenForWait = 0x4014,
SocketFailedToOpen = 0x4015,
SocketFailedToClose = 0x4016,
SocketFailedToConnect = 0x4017,
SocketFailedToRead = 0x4018,
SocketFailedToWrite = 0x4019,
SocketAcceptorFailedToBind = 0x4020,
SocketAcceptorFailedToListen = 0x4021,
// Other Errors
NoErrorFound = 0xFFFFFFFD,
TooManyEvents = 0xFFFFFFFE,
Unknown = 0xFFFFFFFF
@@ -112,8 +176,10 @@ public:
APIEvent() : eventStruct({}), serial(), timepoint(), device(nullptr) {}
APIEvent(APIEvent::Type event, APIEvent::Severity severity, const Device* device = nullptr);
APIEvent(neosocketevent_t evStruct, const Device* device = nullptr);
const neoevent_t* getNeoEvent() const noexcept { return &eventStruct; }
neosocketevent_t getNeoSocketEvent() const noexcept;
Type getType() const noexcept { return Type(eventStruct.eventNumber); }
Severity getSeverity() const noexcept { return Severity(eventStruct.severity); }
std::string getDescription() const noexcept { return std::string(eventStruct.description); }
@@ -152,8 +218,12 @@ public:
EventFilter(const Device* device, APIEvent::Severity severity) : severity(severity), matchOnDevicePtr(true), device(device) {}
EventFilter(std::string serial, APIEvent::Type type = APIEvent::Type::Any, APIEvent::Severity severity = APIEvent::Severity::Any) : type(type), severity(severity), serial(serial) {}
EventFilter(std::string serial, APIEvent::Severity severity) : severity(severity), serial(serial) {}
EventFilter(neosocketeventfilter_t evFilterSt) : type(static_cast<APIEvent::Type>(evFilterSt.eventNumber)),
severity(static_cast<APIEvent::Severity>(evFilterSt.severity)),
serial(std::string(evFilterSt.serial)) {}
bool match(const APIEvent& event) const noexcept;
neosocketeventfilter_t getNeoSocketEventFilter() const noexcept;
APIEvent::Type type = APIEvent::Type::Any;
APIEvent::Severity severity = APIEvent::Severity::Any;
+7
View File
@@ -10,6 +10,7 @@
#include <map>
#include <thread>
#include <algorithm>
#include <optional>
#include "icsneo/api/event.h"
#include "icsneo/api/eventcallback.h"
@@ -34,6 +35,8 @@ public:
// If this thread exists in the map, turn off downgrading
void cancelErrorDowngradingOnCurrentThread();
void removeEventMirror(const std::thread::id& id);
bool isDowngradingErrorsOnCurrentThread() const;
int addEventCallback(const EventCallback &cb);
@@ -108,6 +111,10 @@ private:
bool enforceLimit(); // Returns whether the limit enforcement resulted in an overflow
void discardOldest(size_t count = 1);
#ifdef ICSNEO_ENABLE_DEVICE_SHARING
std::optional<std::vector<neosocketevent_t>> getServerEvents(const size_t& max);
#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;
};
+17
View File
@@ -10,6 +10,8 @@ enum class Command : uint8_t {
EnableNetworkCommunicationEx = 0x08,
NeoReadMemory = 0x40,
NeoWriteMemory = 0x41,
ClearCoreMini = 0x42,
LoadCoreMini = 0x43,
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 +25,18 @@ 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,
FlexRayControl = 0xF3,
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,6 +45,17 @@ enum class ExtendedCommand : uint16_t {
Extract = 0x0015,
StartDHCPServer = 0x0016,
StopDHCPServer = 0x0017,
Reboot = 0x001C,
SetUploadedFlag = 0x0027,
};
enum class ExtendedResponse : int32_t {
OK = 0,
InvalidCommand = -1,
InvalidState = -2,
OperationFailed = -3,
OperationPending = -4,
InvalidParameter = -5,
};
}
+10 -3
View File
@@ -21,11 +21,14 @@
#include <thread>
#include <queue>
#include <map>
#include <list>
namespace icsneo {
class Communication {
public:
typedef std::function<void(std::vector<uint8_t>&)> RawCallback;
// Note that the Packetizer is not created by the constructor,
// and should be done once the Communication module is in place.
Communication(
@@ -45,6 +48,7 @@ public:
void modeChangeIncoming() { driver->modeChangeIncoming(); }
void awaitModeChangeComplete() { driver->awaitModeChangeComplete(); }
bool rawWrite(const std::vector<uint8_t>& bytes) { return driver->write(bytes); }
void modifyRawCallbacks(std::function<void(std::list<Communication::RawCallback>&)>&& cb);
virtual bool sendPacket(std::vector<uint8_t>& bytes);
bool redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo);
void clearRedirectRead();
@@ -56,10 +60,10 @@ 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));
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 +87,14 @@ 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 rawCallbacksMutex;
std::list<std::function<void(std::vector<uint8_t>&)>> rawCallbacks;
std::mutex syncMessageMutex;
void dispatchMessage(const std::shared_ptr<Message>& msg);
void handleInput(Packetizer& p, std::vector<uint8_t>& readBytes);
+4 -1
View File
@@ -9,6 +9,7 @@
#include <thread>
#include <mutex>
#include <condition_variable>
#include "icsneo/device/neodevice.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
@@ -16,7 +17,7 @@ namespace icsneo {
class Driver {
public:
Driver(const device_eventhandler_t& handler) : report(handler) {}
Driver(const device_eventhandler_t& handler, neodevice_t& forDevice) : report(handler), device(forDevice) {}
virtual ~Driver() {}
virtual bool open() = 0;
virtual bool isOpen() = 0;
@@ -24,12 +25,14 @@ public:
virtual void awaitModeChangeComplete() {}
virtual bool isDisconnected() { return disconnected; };
virtual bool close() = 0;
virtual bool enableHeartbeat() const { return false; }
bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
bool write(const std::vector<uint8_t>& bytes);
virtual bool isEthernet() const { return false; }
device_eventhandler_t report;
neodevice_t& device;
size_t writeQueueSize = 50;
bool writeBlocks = true; // Otherwise it just fails when the queue is full
@@ -0,0 +1,42 @@
#ifndef __INTERPROCESSMAILBOX_H_
#define __INTERPROCESSMAILBOX_H_
#ifdef __cplusplus
#include <cstdint>
#include "icsneo/platform/sharedmemory.h"
#include "icsneo/platform/sharedsemaphore.h"
static constexpr uint16_t MESSAGE_COUNT = 1024;
static constexpr uint16_t BLOCK_SIZE = 2048;
using LengthFieldType = uint16_t;
static constexpr uint8_t LENGTH_FIELD_SIZE = sizeof(LengthFieldType);
static constexpr uint16_t MAX_DATA_SIZE = BLOCK_SIZE - LENGTH_FIELD_SIZE;
namespace icsneo {
class InterprocessMailbox {
public:
bool open(const std::string& name, bool create = false /* create the shared resources or not */);
bool close();
operator bool() const;
// data must be large enough to hold at least MAX_DATA_SIZE
// messageLength can be larger than MAX_DATA_SIZE if the message spans multiple blocks, only MAX_DATA_SIZE will be read
bool read(void* data, LengthFieldType& messageLength, const std::chrono::milliseconds& timeout);
// if messageLength is larger than MAX_DATA_SIZE it's expected that future write() calls will send the remaining data
bool write(const void* data, LengthFieldType messageLength, const std::chrono::milliseconds& timeout);
private:
icsneo::SharedSemaphore queuedSem;
icsneo::SharedSemaphore emptySem;
icsneo::SharedMemory sharedMem;
unsigned index = 0; // index into messages;
bool valid = false;
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,147 @@
#ifndef __A2BMESSAGE_H_
#define __A2BMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#define A2BMESSAGE_UPSTREAM 1
#define A2BMESSAGE_DOWNSTREAM 0
#define A2BPCM_SAMPLE_SIZE 4
#define A2BPCM_L16 16
#define A2BPCM_L24 24
#define A2BPCM_SAMPLERATE_44100 44100
#define A2BPCM_SAMPLERATE_48000 48000
namespace icsneo {
typedef uint32_t A2BPCMSample;
typedef std::vector<A2BPCMSample> ChannelBuffer;
class A2BMessage : public Frame {
public:
enum class A2BDirection : uint8_t {
DownStream = 0,
UpStream = 1
};
A2BMessage() = delete;
A2BMessage(uint8_t bitDepth, uint8_t bytesPerSample, uint8_t numChannels) :
bDepth(bitDepth),
bps(bytesPerSample)
{
downstream.resize(numChannels);
upstream.resize(numChannels);
}
void addSample(A2BPCMSample&& sample, A2BDirection dir, uint8_t channel) {
if(dir == A2BDirection::DownStream) {
downstream[channel].push_back(std::move(sample));
}
else {
upstream[channel].push_back(std::move(sample));
}
totalSamples++;
}
const A2BPCMSample* getSamples(A2BDirection dir, uint8_t channel) const {
if(channel >= getNumChannels()) {
return nullptr;
}
if(dir == A2BDirection::DownStream) {
return downstream[channel].data();
}
return upstream[channel].data();
}
std::optional<A2BPCMSample> getSample(A2BDirection dir, uint8_t channel, uint32_t frame) const {
const A2BPCMSample* samples = getSamples(dir, channel);
auto numSamplesInChannel = getNumSamplesInChannel(dir, channel);
if(
samples == nullptr ||
frame >= numSamplesInChannel.value_or(0)
) {
return std::nullopt;
}
return samples[frame];
}
std::optional<size_t> getNumSamplesInChannel(A2BDirection dir, uint8_t channel) const {
if(channel >= getNumChannels()) {
return std::nullopt;
}
if(dir == A2BDirection::DownStream) {
return downstream[channel].size();
}
return upstream[channel].size();
}
const std::vector<ChannelBuffer>& getDownstream() const {
return downstream;
}
const std::vector<ChannelBuffer>& getUpstream() const {
return upstream;
}
size_t getNumSamples() const {
return totalSamples;
}
uint8_t getNumChannels() const {
return static_cast<uint8_t>(downstream.size());
}
uint8_t getBitDepth() const {
return bDepth;
}
uint8_t getBytesPerSample() const {
return bps;
}
// Equals operation for testing.
bool operator==(const A2BMessage& rhs) const {
return channelSize16 == rhs.channelSize16 &&
monitor == rhs.monitor &&
txmsg == rhs.txmsg &&
errIndicator == rhs.errIndicator &&
syncFrame == rhs.syncFrame &&
rfu2 == rhs.rfu2 &&
downstream == rhs.downstream &&
upstream == rhs.upstream &&
totalSamples == rhs.totalSamples &&
bDepth == rhs.bDepth &&
bps == rhs.bps;
}
bool channelSize16;
bool monitor;
bool txmsg;
bool errIndicator;
bool syncFrame;
uint16_t rfu2;
private:
std::vector<ChannelBuffer> downstream;
std::vector<ChannelBuffer> upstream;
size_t totalSamples = 0;
uint8_t bDepth;
uint8_t bps;
};
}
#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,47 @@
#ifndef __A2BWAVOUTPUT_H_
#define __A2BWAVOUTPUT_H_
#ifdef __cplusplus
#include "icsneo/communication/message/callback/streamoutput/streamoutput.h"
#include "icsneo/communication/message/a2bmessage.h"
#include <memory>
#include <functional>
namespace icsneo {
class A2BWAVOutput : public StreamOutput {
public:
A2BWAVOutput(const char* filename, uint32_t sampleRate = A2BPCM_SAMPLERATE_44100)
: StreamOutput(filename), wavSampleRate(sampleRate) {}
A2BWAVOutput(std::unique_ptr<std::ostream>&& os, uint32_t sampleRate = A2BPCM_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:
bool writeSamples(const std::shared_ptr<A2BMessage>& msg, A2BMessage::A2BDirection dir) const;
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,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
@@ -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,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
@@ -28,6 +28,9 @@ public:
FlexRayControl = 0x8006,
EthernetPhyRegister = 0x8007,
LogicalDiskInfo = 0x8008,
ExtendedResponse = 0x8009,
WiVICommandResponse = 0x800a,
ScriptStatus = 0x800b,
};
Message(Type t) : type(t) {}
@@ -71,6 +74,9 @@ public:
#define ICSNEO_MESSAGE_TYPE_DEVICE_VERSION (0x8004)
#define ICSNEO_MESSAGE_TYPE_MAIN51 (0x8005)
#define ICSNEO_MESSAGE_TYPE_FLEXRAY_CONTROL (0x8006)
#define ICSNEO_MESSAGE_TYPE_ETHERNET_PHY_REGISTER (0x8007)
#define ICSNEO_MESSAGE_TYPE_LOGICAL_DISK_INFO (0x8008)
#define ICSNEO_MESSAGE_TYPE_EXTENDED_RESPONSE (0x8009)
#endif // __ICSNEOC_H_
@@ -5,7 +5,7 @@
#include "icsneo/communication/message/main51message.h"
#include "icsneo/communication/command.h"
#include "icsneo/platform/optional.h"
#include <optional>
#include <string>
namespace icsneo {
@@ -29,8 +29,8 @@ public:
bool cmTooBig;
bool hidUsbState;
bool fpgaUsbState;
icsneo::optional<uint16_t> busVoltage;
icsneo::optional<uint16_t> deviceTemperature;
std::optional<uint16_t> busVoltage;
std::optional<uint16_t> deviceTemperature;
};
}
@@ -0,0 +1,37 @@
#ifndef __SCRIPTSTATUSMESSAGE_H
#define __SCRIPTSTATUSMESSAGE_H
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/packet/scriptstatuspacket.h"
namespace icsneo
{
//Response to Command::ScriptStatus
class ScriptStatusMessage : public Message {
public:
ScriptStatusMessage() : Message( Message::Type::ScriptStatus ) {}
bool isEncrypted = false;
bool isCoreminiRunning = false;
uint32_t sectorOverflows = 0;
uint32_t numRemainingSectorBuffers = 0;
int32_t lastSector = 0;
int32_t readBinSize = 0;
int32_t minSector = 0;
int32_t maxSector = 0;
int32_t currentSector = 0;
uint64_t coreminiCreateTime = 0;
uint16_t fileChecksum = 0;
uint16_t coreminiVersion = 0;
uint16_t coreminiHeaderSize = 0;
uint8_t diagnosticErrorCode = 0;
uint8_t diagnosticErrorCodeCount = 0;
uint16_t maxCoreminiSizeKB = 0;
};
}
#endif // __cplusplus
#endif //__SCRIPTSTATUSMESSAGE_H
@@ -5,7 +5,7 @@
#include "icsneo/communication/message/message.h"
#include "icsneo/device/deviceversion.h"
#include "icsneo/platform/optional.h"
#include <optional>
namespace icsneo {
@@ -19,7 +19,7 @@ public:
VersionMessage(Chip chip) : Message(Message::Type::DeviceVersion), ForChip(chip) {}
// nullopt here indicates invalid
std::vector< optional<DeviceAppVersion> > Versions;
std::vector< std::optional<DeviceAppVersion> > Versions;
// What chips the versions are for
const Chip ForChip;
@@ -0,0 +1,38 @@
#ifndef __WIVIRESPONSEMESSAGE_H_
#define __WIVIRESPONSEMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include "icsneo/communication/command.h"
#include "icsneo/communication/packet/wivicommandpacket.h"
#include <optional>
#include <string>
namespace icsneo {
namespace WiVI {
struct Info {
uint8_t sleepRequest;
uint16_t connectionTimeoutMinutes;
std::vector<CaptureInfo> captures;
};
// The response for Command::WiVICommand
class ResponseMessage : public Message {
public:
ResponseMessage() : Message(Message::Type::WiVICommandResponse) {}
bool success = true;
std::optional<Command> responseTo;
std::optional<int32_t> value;
std::optional<Info> info;
};
} // namespace WiVI
} // namespace icsneo
#endif // __cplusplus
#endif
+36 -4
View File
@@ -8,7 +8,7 @@ typedef uint8_t neonettype_t;
#ifdef __cplusplus
#include <ostream>
#include "icsneo/platform/optional.h"
#include <optional>
namespace icsneo {
@@ -118,8 +118,12 @@ public:
LIN6 = 98,
LSFTCAN2 = 99,
LogicalDiskInfo = 187,
WiVICommand = 221,
ScriptStatus = 224,
EthPHYControl = 239,
ExtendedCommand = 240,
FlexRayControl = 243,
CoreMiniPreLoad = 244,
HW_COM_Latency_Test = 512,
DeviceStatus = 513,
UDP = 514,
@@ -128,6 +132,8 @@ public:
I2C3 = 518,
I2C4 = 519,
Ethernet2 = 520,
A2B1 = 522,
A2B2 = 523,
Any = 0xfffe, // Never actually set as type, but used as flag for filtering
Invalid = 0xffff
};
@@ -143,6 +149,7 @@ public:
SWCAN = 8,
ISO9141 = 9,
I2C = 10,
A2B = 11,
Any = 0xFE, // Never actually set as type, but used as flag for filtering
Other = 0xFF
};
@@ -230,6 +237,8 @@ public:
return "Single Wire CAN";
case Type::I2C:
return "I²C";
case Type::A2B:
return "A2B";
case Type::Invalid:
default:
return "Invalid Type";
@@ -273,7 +282,11 @@ public:
case NetID::Main51:
case NetID::ReadSettings:
case NetID::LogicalDiskInfo:
case NetID::WiVICommand:
case NetID::ScriptStatus:
case NetID::EthPHYControl:
case NetID::CoreMiniPreLoad:
case NetID::ExtendedCommand:
case NetID::NeoMemorySDRead:
case NetID::NeoMemoryWriteDone:
return Type::Internal;
@@ -312,6 +325,9 @@ public:
case NetID::I2C3:
case NetID::I2C4:
return Type::I2C;
case NetID::A2B1:
case NetID::A2B2:
return Type::A2B;
default:
return Type::Other;
}
@@ -512,8 +528,16 @@ public:
return "LSFTCAN 2";
case NetID::LogicalDiskInfo:
return "Logical Disk Information";
case NetID::WiVICommand:
return "WiVI Command";
case NetID::ScriptStatus:
return "Script Status";
case NetID::CoreMiniPreLoad:
return "CoreMini PreLoad";
case NetID::EthPHYControl:
return "Ethernet PHY Register Control";
case NetID::ExtendedCommand:
return "Extended Command";
case NetID::FlexRayControl:
return "FlexRay Control";
case NetID::HW_COM_Latency_Test:
@@ -532,13 +556,17 @@ public:
return "I2C 4";
case NetID::Ethernet2:
return "Ethernet 2";
case NetID::A2B1:
return "A2B 1";
case NetID::A2B2:
return "A2B 2";
case NetID::Any:
case NetID::Invalid:
break;
}
return "Invalid Network";
}
static optional<CoreMini> GetCoreMiniNetworkFromNetID(NetID netid) {
static std::optional<CoreMini> GetCoreMiniNetworkFromNetID(NetID netid) {
switch(netid) {
case NetID::HSCAN:
return CoreMini::HSCAN;
@@ -657,7 +685,7 @@ public:
case NetID::Ethernet2:
return CoreMini::Ethernet2;
default:
return nullopt;
return std::nullopt;
}
}
static NetID GetNetIDFromCoreMiniNetwork(CoreMini cm) {
@@ -788,7 +816,7 @@ public:
Network(CoreMini cm) { setValue(GetNetIDFromCoreMiniNetwork(cm)); }
NetID getNetID() const { return value; }
Type getType() const { return type; }
optional<CoreMini> getCoreMini() const { return GetCoreMiniNetworkFromNetID(getNetID()); }
std::optional<CoreMini> getCoreMini() const { return GetCoreMiniNetworkFromNetID(getNetID()); }
friend std::ostream& operator<<(std::ostream& os, const Network& network) {
os << GetNetIDString(network.getNetID());
return os;
@@ -913,8 +941,12 @@ private:
#define ICSNEO_NETID_LIN6 98
#define ICSNEO_NETID_LSFTCAN2 99
#define ICSNEO_NETID_LOGICAL_DISK_INFO 187
#define ICSNEO_NETID_WIVI_COMMAND 221
#define ICSNEO_NETID_SCRIPT_STATUS 224
#define ICSNEO_NETID_ETH_PHY_CONTROL 239
#define ICSNEO_NETID_EXTENDED_COMMAND 240
#define ICSNEO_NETID_FLEXRAY_CONTROL 243
#define ICSNEO_NETID_COREMINI_PRELOAD 244
#define ICSNEO_NETID_HW_COM_LATENCY_TEST 512
#define ICSNEO_NETID_DEVICE_STATUS 513
#define ICSNEO_NETID_UDP 514
@@ -0,0 +1,46 @@
#ifndef __A2BPACKET_H__
#define __A2BPACKET_H__
#ifdef __cplusplus
#include "icsneo/api/eventmanager.h"
#include <cstdint>
#include <memory>
#include <optional>
#include "icsneo/communication/message/a2bmessage.h"
namespace icsneo {
typedef uint16_t icscm_bitfield;
struct HardwareA2BPacket {
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
static bool EncodeFromMessage(const A2BMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
struct {
// CxA2B
icscm_bitfield channelNum : 8;
icscm_bitfield channelSize16 : 1;
icscm_bitfield : 7;
// CxA2B2
icscm_bitfield monitor : 1;
icscm_bitfield txmsg : 1;
icscm_bitfield errIndicator : 1;
icscm_bitfield syncFrame : 1;
icscm_bitfield upstream : 1;
icscm_bitfield : 11;
icscm_bitfield rfu2;
} header;
static const size_t coreMiniMessageHeaderSize;
static const size_t a2bMessageMaxLength;
static const size_t a2bHeaderSize;
};
}
#endif // __cplusplus
#endif
@@ -7,6 +7,7 @@
#include "icsneo/api/eventmanager.h"
#include <cstdint>
#include <memory>
#include <optional>
namespace icsneo {
@@ -0,0 +1,61 @@
#ifndef __I2CPACKET_H__
#define __I2CPACKET_H__
#ifdef __cplusplus
#include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/communication/network.h"
#include <cstdint>
#include <memory>
namespace icsneo {
static constexpr size_t I2CMaxLength = 1024u;
#pragma pack(push, 2)
struct I2CHeader {
struct {
// C1xI2C
uint16_t ID : 10;// i2c address, 7-bit or 10-bit
uint16_t EID : 1;// using extended addressing, i.e. 10-bit
uint16_t CT : 1;// Controller/Target
uint16_t DIR : 1;// read/write
uint16_t RESERVED_0 : 3;
// C2xI2C
uint16_t TXMsg: 1;
uint16_t CBLen: 11;
uint16_t RESERVED_1: 4;
// C3xI2C
uint16_t TXTimeout : 1;
uint16_t TXNack : 1;
uint16_t TXAborted : 1;
uint16_t TXLostArb : 1;
uint16_t TXError : 1;
uint16_t RESERVED_2: 11;
} CoreMiniBitsI2C;
uint8_t coreMiniMessageData[8];
uint16_t stats;
uint64_t timestamp;
uint16_t networkID;
uint16_t length;
};
#pragma pack(pop)
struct HardwareI2CPacket {
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream);
static bool EncodeFromMessage(const I2CMessage& message, std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
};
}
#endif //_cplusplus
#endif
@@ -0,0 +1,86 @@
#ifndef __SCRIPTSTATUSPACKET_H
#define __SCRIPTSTATUSPACKET_H
#ifdef __cplusplus
#include "icsneo/communication/packet.h"
#include <cstdint>
#include <memory>
#pragma pack(push,2)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union
#pragma warning(disable: 4200) // nonstandard extension used: zero-sized array in struct/union
#endif // _MSC_VER
namespace icsneo
{
class ScriptStatusMessage;
struct CoreMiniStatus
{
uint32_t justReset : 1;
uint32_t communicationEnabled : 1;
uint32_t isRunning : 1;
uint32_t checksumFailed : 1;
uint32_t licenseFailed : 1;
uint32_t versionMismatch : 1;
uint32_t bootOff : 1;
uint32_t hardwareFailure : 1; // To check SRAM failure (for now)
uint32_t isPassiveConnect : 1; // Always zero. Set to one when neoVI connection is passive,i.e. no async traffic
uint32_t usbCommunicationEnabled : 1; // Set to one when USB Host PC has enabled communication.
uint32_t linuxCommunicationEnabled : 1; // Set to one when Android (Linux) has enabled communication.
uint32_t tooBig : 1;
uint32_t hidUsbState : 1;
uint32_t fpgaUsbState : 1;
uint32_t filesystem : 1;
uint32_t isEncrypted : 1;
uint32_t reserved : 16;
};
struct ScriptStatus
{
static std::shared_ptr<ScriptStatusMessage> DecodeToMessage(const std::vector<uint8_t>& bytestream);
CoreMiniStatus status;
uint32_t sectorOverflows;
uint32_t numRemainingSectorBuffers;
int32_t lastSector;
int32_t readBinSize;
int32_t minSector;
int32_t maxSector;
int32_t currentSector;
uint32_t coreminiCreateTimeMsb;
uint32_t coreminiCreateTimeLsb;
uint16_t zero2;
uint16_t zero3;
uint16_t fileChecksum;
uint16_t coreminiVersion;
uint16_t coreminiHeaderSize;
uint16_t coreminiEngineMinVersion; // This firmware min version
uint16_t coreminiScriptMinEngineVers; // Current loaded script's min engine requirements.
uint8_t eolResults1;
uint8_t eolResults2;
uint8_t eolResults3;
uint8_t zero6;
uint8_t diagErrCode;
uint8_t diagErrCodeCount;
/* FPGA version Fpga reports to HID over SPI */
uint8_t spiFpgaVers1;
uint8_t spiFpgaVers2;
uint16_t maxCoreminiSizeKB;
uint16_t maxCoreminiFlashSizeKB;
};
} // namespace icsneo
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
#pragma pack(pop)
#endif // __cplusplus
#endif //__SCRIPTSTATUSPACKET_H
@@ -0,0 +1,158 @@
#ifndef __WIVICOMMANDPACKET_H_
#define __WIVICOMMANDPACKET_H_
#ifdef __cplusplus
#include "icsneo/communication/packet.h"
#include <cstdint>
#include <vector>
#include <memory>
#pragma pack(push,2)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4201) // nonstandard extension used: nameless struct/union
#pragma warning(disable: 4200) // nonstandard extension used: zero-sized array in struct/union
#endif // _MSC_VER
namespace icsneo {
union CoreMiniFixedPointValue {
CoreMiniFixedPointValue() {}
CoreMiniFixedPointValue(int32_t integralValue) { ValueInt32 = integralValue; }
struct {
union {
uint32_t ValueFractionPart;
struct {
int16_t ValueInt16FractionLSB;
int16_t ValueInt16FractionMSB;
};
};
union {
int32_t ValueInt32;
struct {
int16_t ValueInt16;
int16_t ValueInt16PartMSB;
};
struct{
uint8_t ValueInt8;
uint8_t ValueInt8PartMSB;
int16_t ValueInt8Part16MSB;
};
};
};
int64_t ValueLarge;
};
namespace WiVI {
class ResponseMessage; // Forward declaration to avoid cyclic includes
enum class Command : uint16_t {
GetAll = 0x0010,
ClearUploads = 0x0011,
SetSignal = 0x0012,
GetSignal = 0x0013,
Result = 0x0014,
GetPhysicalSignal = 0x0015,
};
enum class SignalType : uint16_t { // enumCoreMiniValueMiscValueType
WokeUpOnSMS = 0x0067,
AvailableDiskSpaceKB = 0x0069,
ManualTrigger = 0x006c,
SleepRequest = 0x006d,
ConnectionTimeout = 0x006e,
TimeSinceLastMessageMs = 0x006f,
UploadsPending = 0x0077,
};
struct Upload {
uint32_t startSector;
uint32_t endSector;
struct {
uint16_t pending : 1;
uint16_t started : 1;
uint16_t reserved : 14;
} flags;
};
struct CaptureInfo {
uint16_t captureBlockIndex; // What capture block is this for
struct {
uint16_t isPrePost : 1;
uint16_t isPreTime : 1; // Only valid if the capture block's bPrePostExtract is set
uint16_t uploadOverCellular : 1;
uint16_t uploadOverWiFi : 1;
uint16_t uploadStackSize : 3; // 0 => size of 1
uint16_t uploadOverflow : 1;
uint16_t uploadPriority : 4;
uint16_t reserved : 4;
} flags;
// Only valid if the capture block's bPrePostExtract is set
uint32_t preTriggerSize;
Upload uploadStage;
Upload uploadStack[2];
};
struct CommandPacket {
static std::shared_ptr<WiVI::ResponseMessage> DecodeToMessage(const std::vector<uint8_t>& bytestream);
struct Header {
WiVI::Command cmd;
uint16_t length;
};
struct Result {
Header header;
WiVI::Command responseTo;
uint16_t result;
};
struct GetSignal {
static std::vector<uint8_t> Encode(WiVI::SignalType type);
Header header;
WiVI::SignalType type;
};
struct SetSignal {
static std::vector<uint8_t> Encode(WiVI::SignalType type, CoreMiniFixedPointValue value);
Header header;
WiVI::SignalType type;
CoreMiniFixedPointValue value;
};
struct GetAll {
static std::vector<uint8_t> Encode();
Header header;
uint8_t version;
uint8_t sleepRequest;
uint16_t connectionTimeoutMinutes;
uint16_t numCaptureInfos;
CaptureInfo captureInfos[0];
};
struct ClearUploads {
static std::vector<uint8_t> Encode(const std::vector<uint8_t>& bitmask);
Header header;
uint8_t bitmask[0];
};
};
} // namespace WiVI
} // namespace icsneo
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
#pragma pack(pop)
#endif // __cplusplus
#endif
+34
View File
@@ -0,0 +1,34 @@
#ifndef __SDIO_H_
#define __SDIO_H_
#ifdef __cplusplus
#include "icsneo/communication/driver.h"
#include "icsneo/communication/interprocessmailbox.h"
namespace icsneo {
class SDIO : public Driver {
public:
static void Find(std::vector<FoundDevice>& found);
SDIO(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice) {}
~SDIO() { if(isOpen()) close(); }
bool open() override;
bool close() override;
bool isOpen() override;
bool enableHeartbeat() const override { return true; }
private:
void readTask() override;
void writeTask() override;
bool deviceOpen = false;
InterprocessMailbox outboundIO;
InterprocessMailbox inboundIO;
};
}
#endif // __cplusplus
#endif
+124
View File
@@ -0,0 +1,124 @@
#ifndef __SOCKET_H_
#define __SOCKET_H_
#ifdef __cplusplus
#include <vector>
#include <optional>
#include <string>
#include <memory>
#include <functional>
#include <mutex>
#include <atomic>
#ifdef _WIN32
#define NOMINMAX
#include <winsock2.h>
#include <ws2tcpip.h>
typedef SOCKET SocketFileDescriptor;
#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <cerrno>
#include <string.h>
typedef int SocketFileDescriptor;
#endif
namespace icsneo {
enum class RPC {
DEVICE_FINDER_FIND_ALL,
DEVICE_FINDER_GET_SUPORTED_DEVICES,
DEVICE_OPEN,
DEVICE_GO_ONLINE,
DEVICE_GO_OFFLINE,
DEVICE_CLOSE,
DEVICE_LOCK,
DEVICE_UNLOCK,
SDIO_OPEN,
SDIO_CLOSE,
GET_EVENTS,
GET_LAST_ERROR,
GET_EVENT_COUNT,
DISCARD_EVENTS,
SET_EVENT_LIMIT,
GET_EVENT_LIMIT
};
static constexpr uint16_t RPC_PORT = 54949;
class SocketBase {
public:
enum class Protocol {
TCP = SOCK_STREAM,
};
bool open();
bool close();
bool connect();
bool isOpen();
bool isConnected();
bool read(void* output, std::size_t length);
bool write(const void* input, std::size_t length);
bool writeString(const std::string& str);
bool readString(std::string& str);
template<typename... Ts>
bool writeTyped(Ts... input) {
return (... && write(&input, sizeof(input)));
}
template<typename... Ts>
bool readTyped(Ts&... output) {
return (... && read(&output, sizeof(output)));
}
protected:
Protocol protocol;
uint16_t port;
SocketFileDescriptor sockFileDescriptor;
bool sockIsOpen = false;
bool sockIsConnected = false;
void setIgnoreSIGPIPE();
};
// RAII Socket
class ActiveSocket : public SocketBase {
public:
ActiveSocket(SocketFileDescriptor sockFD);
ActiveSocket(Protocol protocol, uint16_t port);
~ActiveSocket();
};
// RAII Socket IO
class LockedSocket : public SocketBase {
public:
LockedSocket(SocketBase& socket, std::unique_lock<std::mutex>&& lock);
private:
std::unique_lock<std::mutex> lock;
};
class Acceptor : public ActiveSocket {
public:
Acceptor(Protocol protocol, uint16_t port);
bool initialize();
std::shared_ptr<ActiveSocket> accept();
private:
bool isValid = false;
bool bind();
bool listen();
};
LockedSocket lockSocket();
}
#endif // __cplusplus
#endif
+248 -29
View File
@@ -10,6 +10,8 @@
#include <cstdint>
#include <atomic>
#include <type_traits>
#include <optional>
#include <unordered_map>
#include "icsneo/api/eventmanager.h"
#include "icsneo/api/lifetime.h"
#include "icsneo/device/neodevice.h"
@@ -27,11 +29,12 @@
#include "icsneo/communication/decoder.h"
#include "icsneo/communication/io.h"
#include "icsneo/communication/message/resetstatusmessage.h"
#include "icsneo/communication/message/wiviresponsemessage.h"
#include "icsneo/communication/message/scriptstatusmessage.h"
#include "icsneo/device/extensions/flexray/controller.h"
#include "icsneo/communication/message/flexray/control/flexraycontrolmessage.h"
#include "icsneo/communication/message/ethphymessage.h"
#include "icsneo/third-party/concurrentqueue/concurrentqueue.h"
#include "icsneo/platform/optional.h"
#include "icsneo/platform/nodiscard.h"
#define ICSNEO_FINDABLE_DEVICE_BASE(className, type) \
@@ -113,10 +116,10 @@ public:
Progress
};
using OpenStatusHandler = std::function<Device::OpenDirective(OpenStatusType type, const std::string& status, optional<double> progress)>;
using OpenStatusHandler = std::function<Device::OpenDirective(OpenStatusType type, const std::string& status, std::optional<double> progress)>;
bool open(OpenFlags flags = {}, OpenStatusHandler handler =
[](OpenStatusType, const std::string&, optional<double>) { return Device::OpenDirective::Continue; });
[](OpenStatusType, const std::string&, std::optional<double>) { return Device::OpenDirective::Continue; });
virtual bool close();
virtual bool isOnline() const { return online; }
virtual bool isOpen() const { return com->isOpen(); }
@@ -124,6 +127,19 @@ public:
virtual bool goOnline();
virtual bool goOffline();
enum class PreloadReturn : uint8_t
{
Pending,
Ok,
OKEncrypted,
NoScript,
};
int8_t prepareScriptLoad();
bool startScript();
bool stopScript();
bool clearScript();
// Message polling related functions
bool enableMessagePolling();
bool disableMessagePolling();
@@ -137,7 +153,7 @@ public:
enforcePollingMessageLimit();
}
int addMessageCallback(const MessageCallback& cb) { return com->addMessageCallback(cb); }
int addMessageCallback(const std::shared_ptr<MessageCallback>& cb) { return com->addMessageCallback(cb); }
bool removeMessageCallback(int id) { return com->removeMessageCallback(id); }
bool transmit(std::shared_ptr<Frame> frame);
@@ -171,7 +187,7 @@ public:
* Upon failure, icsneo::nullopt will be returned and an error will be
* set in icsneo::GetLastError().
*/
optional<uint64_t> readLogicalDisk(uint64_t pos, uint8_t* into, uint64_t amount,
std::optional<uint64_t> readLogicalDisk(uint64_t pos, uint8_t* into, uint64_t amount,
std::chrono::milliseconds timeout = Disk::DefaultTimeout);
/**
@@ -188,7 +204,7 @@ public:
* Upon failure, icsneo::nullopt will be returned and an error will be
* set in icsneo::GetLastError().
*/
optional<uint64_t> writeLogicalDisk(uint64_t pos, const uint8_t* from, uint64_t amount,
std::optional<uint64_t> writeLogicalDisk(uint64_t pos, const uint8_t* from, uint64_t amount,
std::chrono::milliseconds timeout = Disk::DefaultTimeout);
/**
@@ -201,7 +217,7 @@ public:
* `icsneo::nullopt` will be returned if the device does not respond in a
* timely manner.
*/
optional<bool> isLogicalDiskConnected();
std::optional<bool> isLogicalDiskConnected();
/**
* Get the size of the connected logical disk in bytes.
@@ -211,7 +227,7 @@ public:
* `icsneo::nullopt` will be returned if the device does not respond in a
* timely manner, or if the disk is disconnected/improperly configured.
*/
optional<uint64_t> getLogicalDiskSize();
std::optional<uint64_t> getLogicalDiskSize();
/**
* Get the offset to the VSA filesystem within the logical disk, represented
@@ -223,7 +239,7 @@ public:
* `icsneo::nullopt` will be returned if the device does not respond in a
* timely manner, or if the disk is disconnected/improperly configured.
*/
optional<uint64_t> getVSAOffsetInLogicalDisk();
std::optional<uint64_t> getVSAOffsetInLogicalDisk();
/**
* Retrieve the number of Ethernet (DoIP) Activation lines present
@@ -263,7 +279,7 @@ public:
* The index number starts counting at 1 to keep the numbers in sync
* with the numbering on the device, and is set to 1 by default.
*/
optional<bool> getDigitalIO(IO type, size_t number = 1);
std::optional<bool> getDigitalIO(IO type, size_t number = 1);
/**
* Set a digital IO to either a 1, if value is true, or 0 otherwise.
@@ -287,7 +303,179 @@ public:
* The index number starts counting at 1 to keep the numbers in sync
* with the numbering on the device, and is set to 1 by default.
*/
optional<double> getAnalogIO(IO type, size_t number = 1);
std::optional<double> getAnalogIO(IO type, size_t number = 1);
typedef std::function< void(uint32_t startSector, uint32_t endSector) > NewCaptureCallback;
/**
* Add a callback which will be called for all new captures.
*
* This is invalid for devices which are not running the Wireless neoVI stack.
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addNewCaptureCallback(NewCaptureCallback cb);
typedef std::function< void(uint16_t connectionTimeoutMinutes) > SleepRequestedCallback;
/**
* Add a callback which will be called when a Wireless neoVI device is
* ready for sleep, pending any uploads we might want to complete first.
*
* Call Device::allowSleep() once ready to signal that status to the device.
*
* Check Device::isSleepRequested() to check if the sleep request was interrupted.
* In that case, the sleep requested callbacks will be called again.
*
* This is invalid for devices which are not running the Wireless neoVI stack.
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addSleepRequestedCallback(SleepRequestedCallback cb);
/**
* Check whether sleep has been requested by a VSSAL Wireless neoVI script.
*/
std::optional<bool> isSleepRequested() const;
/**
* Signal to a running VSSAL Wireless neoVI script that we are ready for
* sleep.
*
* If @param remoteWakeup is specified, the modem will be kept running in sleep
* mode, where supported.
*
* This is invalid for devices which are not running the Wireless neoVI stack.
*/
bool allowSleep(bool remoteWakeup = false);
enum class ScriptStatus {
CoreMiniRunning = 0,
SectorOverflow = 1,
RemainingSectors = 2,
LastSector = 3,
ReadBinSize = 4,
MinSector = 5,
MaxSector = 6,
CurrentSector = 7,
CoreMiniCreateTime = 8,
FileChecksum = 9,
CoreMiniVersion = 10,
CoreMiniHeaderSize = 11,
DiagnosticErrorCode = 12,
DiagnosticErrorCodeCount = 13,
MaxCoreMiniSize = 14,
Logging = 15,
IsEncrypted = 16,
};
typedef std::function< void(uint64_t value) > ScriptStatusCallback;
/**
* Get all current script status values
*/
std::shared_ptr<ScriptStatusMessage> getScriptStatus() const;
/**
* Add a callback to be called when VSSAL script running state changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addCoreMiniRunningCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CoreMiniRunning, std::move(cb)); }
/**
* Add a callback to be called when the VSSAL script encryption mode changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addEncryptedModeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::IsEncrypted, std::move(cb)); }
/**
* Add a callback to be called when the number of times a sector was dropped due to lack of space
* in firmware's filesystem buffer changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addSectorOverflowsCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::SectorOverflow, std::move(cb)); }
/**
* Add a callback to be called when number of sectors of space left in firmware's local file system buffer changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addNumberRemainingSectorsCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::RemainingSectors, std::move(cb)); }
/**
* Add a callback to be called when last sector that was written to changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addLastSectorCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::LastSector, std::move(cb)); }
/**
* Add a callback to be called when the size of the ReadBin changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addReadBinSizeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::ReadBinSize, std::move(cb)); }
/**
* Add a callback to be called when the first sector address of logged data changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addMinSectorCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::MinSector, std::move(cb)); }
/**
* Add a callback to be called when the last sector address of logged data changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addMaxSectorCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::MaxSector, std::move(cb)); }
/**
* Add a callback to be called when the sector that is about to be written changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addCurrentSectorCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CurrentSector, std::move(cb)); }
/**
* Add a callback to be called when the VSSAL script create time changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addCoreMiniCreateTimeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CoreMiniCreateTime, std::move(cb)); }
/**
* Add a callback to be called when the VSSAL script checksum changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addFileChecksumCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::FileChecksum, std::move(cb)); }
/**
* Add a callback to be called when the VSSAL script version changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addCoreMiniVersionCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CoreMiniVersion, std::move(cb)); }
/**
* Add a callback to be called when the VSSAL script header size changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addCoreMiniHeaderSizeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::CoreMiniHeaderSize, std::move(cb)); }
/**
* Add a callback to be called when the firmware diagnostic error code changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addDiagnosticErrorCodeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::DiagnosticErrorCode, std::move(cb)); }
/**
* Add a callback to be called when the firmware diagnostic error code count changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addDiagnosticErrorCodeCountCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::DiagnosticErrorCodeCount, std::move(cb)); }
/**
* Add a callback to be called when the maximum size a VSSAL script can be changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addMaxCoreMiniSizeCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::MaxCoreMiniSize, std::move(cb)); }
/**
* Add a callback to be called when the device logging state changes
*/
NODISCARD("If the Lifetime is not held, the callback will be immediately removed")
Lifetime addLoggingCallback(ScriptStatusCallback cb) { return addScriptStatusCallback(ScriptStatus::Logging, std::move(cb)); }
virtual std::vector<std::shared_ptr<FlexRay::Controller>> getFlexRayControllers() const { return {}; }
@@ -302,7 +490,7 @@ public:
/**
* For use by extensions only. A more stable API will be provided in the future.
*/
const std::vector<optional<DeviceAppVersion>>& getVersions() const { return versions; }
const std::vector<std::optional<DeviceAppVersion>>& getVersions() const { return versions; }
/**
* Some alternate communication protocols do not support DFU
@@ -317,8 +505,15 @@ public:
*/
virtual bool getEthPhyRegControlSupported() const { return false; }
optional<EthPhyMessage> sendEthPhyMsg(const EthPhyMessage& message, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
/**
* Returns true if this device supports the Wireless neoVI featureset
*/
virtual bool supportsWiVI() const { return false; }
std::optional<EthPhyMessage> sendEthPhyMsg(const EthPhyMessage& message, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
std::optional<bool> SetCollectionUploaded(uint32_t collectionEntryByteAddress);
std::shared_ptr<Communication> com;
std::unique_ptr<IDeviceSettings> settings;
@@ -329,12 +524,12 @@ protected:
device_eventhandler_t report;
std::mutex ioMutex;
optional<bool> ethActivationStatus;
optional<bool> usbHostPowerStatus;
optional<bool> backupPowerEnabled;
optional<bool> backupPowerGood;
std::array<optional<bool>, 6> miscDigital;
std::array<optional<double>, 2> miscAnalog;
std::optional<bool> ethActivationStatus;
std::optional<bool> usbHostPowerStatus;
std::optional<bool> backupPowerEnabled;
std::optional<bool> backupPowerGood;
std::array<std::optional<bool>, 6> miscDigital;
std::array<std::optional<double>, 2> miscAnalog;
// START Initialization Functions
Device(neodevice_t neodevice) : data(neodevice) {
@@ -350,7 +545,11 @@ protected:
setupDecoder(*decoder);
com = makeCommunication(
makeDriver(report, getWritableNeoDevice()),
std::bind(&Device::makeConfiguredPacketizer, this),
[this]() -> std::unique_ptr<Packetizer> {
auto packetizer = makePacketizer();
setupPacketizer(*packetizer);
return packetizer;
},
std::move(encoder),
std::move(decoder)
);
@@ -372,11 +571,6 @@ protected:
virtual std::unique_ptr<Packetizer> makePacketizer() { return std::unique_ptr<Packetizer>(new Packetizer(report)); }
virtual void setupPacketizer(Packetizer&) {}
std::unique_ptr<Packetizer> makeConfiguredPacketizer() {
auto packetizer = makePacketizer();
setupPacketizer(*packetizer);
return packetizer;
}
virtual std::unique_ptr<Encoder> makeEncoder() { return std::unique_ptr<Encoder>(new Encoder(report)); }
virtual void setupEncoder(Encoder&) {}
@@ -394,8 +588,8 @@ protected:
}
template<typename Settings>
std::unique_ptr<IDeviceSettings> makeSettings(std::shared_ptr<Communication> com) {
return std::unique_ptr<IDeviceSettings>(new Settings(com));
std::unique_ptr<IDeviceSettings> makeSettings(std::shared_ptr<Communication> comm) {
return std::unique_ptr<IDeviceSettings>(new Settings(comm));
}
virtual void setupSettings(IDeviceSettings&) {}
@@ -409,7 +603,7 @@ protected:
virtual bool afterCommunicationOpen() { return true; }
virtual bool requiresVehiclePower() const { return true; }
template<typename Extension>
std::shared_ptr<Extension> getExtension() const {
std::shared_ptr<Extension> ret;
@@ -431,7 +625,7 @@ protected:
private:
neodevice_t data;
std::shared_ptr<ResetStatusMessage> latestResetStatus;
std::vector<optional<DeviceAppVersion>> versions;
std::vector<std::optional<DeviceAppVersion>> versions;
mutable std::mutex diskLock;
std::unique_ptr<Disk::ReadDriver> diskReadDriver;
@@ -471,6 +665,31 @@ private:
std::atomic<bool> stopHeartbeatThread{false};
std::mutex heartbeatMutex;
std::thread heartbeatThread;
// Wireless neoVI Stack
std::atomic<bool> stopWiVIThread{false};
std::condition_variable stopWiVIcv;
mutable std::mutex wiviMutex;
std::thread wiviThread;
std::atomic<bool> wiviSleepRequested{false};
std::vector<NewCaptureCallback> newCaptureCallbacks;
std::vector< std::pair<SleepRequestedCallback, bool /* notified */> > sleepRequestedCallbacks;
void wiviThreadBody();
void stopWiVIThreadIfNecessary(std::unique_lock<std::mutex> lk);
//Script status
std::atomic<bool> stopScriptStatusThread{false};
std::condition_variable stopScriptStatusCv;
mutable std::mutex scriptStatusMutex;
std::thread scriptStatusThread;
std::unordered_map<ScriptStatus, std::vector<ScriptStatusCallback>> scriptStatusCallbacks;
std::unordered_map<ScriptStatus, uint64_t> scriptStatusValues;
Lifetime addScriptStatusCallback(ScriptStatus, ScriptStatusCallback);
bool updateScriptStatusValue(ScriptStatus, uint64_t newValue);
void notifyScriptStatusCallback(ScriptStatus, uint64_t);
void scriptStatusThreadBody();
void stopScriptStatusThreadIfNecessary(std::unique_lock<std::mutex> lk);
};
}
+9 -1
View File
@@ -3,7 +3,7 @@
// Hold the length of the longest name, so that C applications can allocate memory accordingly
// Currently the longest is "Intrepid Ethernet Evaluation Board"
#define ICSNEO_DEVICETYPE_LONGEST_NAME (35 + 1) // Add 1 so that if someone forgets, they still have space for null terminator
#define ICSNEO_DEVICETYPE_LONGEST_NAME (145 + 1) // Add 1 so that if someone forgets, they still have space for null terminator
#define ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION (ICSNEO_DEVICETYPE_LONGEST_NAME + 7) // 6 character serial, plus space
#ifndef __cplusplus
@@ -38,12 +38,14 @@ public:
NEOECU12 = (0x0000000c),
OBD2_LCBADGE = (0x0000000d),
RADMoonDuo = (0x0000000e),
FIRE3 = (0x0000000f),
VCAN3 = (0x00000010),
RADJupiter = (0x00000011),
VCAN4_IND = (0x00000012),
RADGigastar = (0x00000013),
RED2 = (0x00000014),
EtherBADGE = (0x00000016),
RAD_A2B = (0x00000017),
RADEpsilon = (0x00000018),
RED = (0x00000040),
ECU = (0x00000080),
@@ -113,6 +115,8 @@ public:
return "neoOBD2 LC BADGE";
case RADMoonDuo:
return "RAD-Moon Duo";
case FIRE3:
return "neoVI FIRE 3";
case VCAN3:
return "ValueCAN 3";
case RADJupiter:
@@ -125,6 +129,8 @@ public:
return "neoVI RED 2";
case EtherBADGE:
return "EtherBADGE";
case RAD_A2B:
return "RAD-A2B";
case RADEpsilon:
return "RAD-Epsilon";
case RED:
@@ -212,12 +218,14 @@ private:
#define ICSNEO_DEVICETYPE_NEOECU12 ((devicetype_t)0x0000000c)
#define ICSNEO_DEVICETYPE_OBD2_LCBADGE ((devicetype_t)0x0000000d)
#define ICSNEO_DEVICETYPE_RADMOONDUO ((devicetype_t)0x0000000e)
#define ICSNEO_DEVICETYPE_FIRE3 ((devicetype_t)0x0000000f)
#define ICSNEO_DEVICETYPE_VCAN3 ((devicetype_t)0x00000010)
#define ICSNEO_DEVICETYPE_RADJUPITER ((devicetype_t)0x00000011)
#define ICSNEO_DEVICETYPE_VCAN4_IND ((devicetype_t)0x00000012)
#define ICSNEO_DEVICETYPE_RADGIGASTAR ((devicetype_t)0x00000013)
#define ICSNEO_DEVICETYPE_RED2 ((devicetype_t)0x00000014)
#define ICSNEO_DEVICETYPE_ETHERBADGE ((devicetype_t)0x00000016)
#define ICSNEO_DEVICETYPE_RAD_A2B ((devicetype_t)0x00000017)
#define ICSNEO_DEVICETYPE_RADEPSILON ((devicetype_t)0x00000018)
#define ICSNEO_DEVICETYPE_RED ((devicetype_t)0x00000040)
#define ICSNEO_DEVICETYPE_ECU ((devicetype_t)0x00000080)
+1 -1
View File
@@ -3,7 +3,7 @@
#ifdef __cplusplus
#include "icsneo/platform/optional.h"
#include <optional>
#include <cstdint>
#include <array>
+1 -1
View File
@@ -11,7 +11,7 @@ typedef std::function< std::unique_ptr<Driver>(device_eventhandler_t err, neodev
class FoundDevice {
public:
neodevice_handle_t handle = 0;
char serial[7] = {};
deviceserial_t serial = {};
uint16_t productId = 0;
driver_factory_t makeDriver;
};
+3 -3
View File
@@ -601,7 +601,7 @@ typedef struct {
#ifdef __cplusplus
#include "icsneo/communication/communication.h"
#include "icsneo/platform/optional.h"
#include <optional>
#include <iostream>
#include <atomic>
@@ -612,7 +612,7 @@ public:
using TerminationGroup = std::vector<Network>;
static constexpr uint16_t GS_VERSION = 5;
static optional<uint16_t> CalculateGSChecksum(const std::vector<uint8_t>& settings, optional<size_t> knownSize = nullopt);
static std::optional<uint16_t> CalculateGSChecksum(const std::vector<uint8_t>& settings, std::optional<size_t> knownSize = std::nullopt);
static CANBaudrate GetEnumValueForBaudrate(int64_t baudrate);
static int64_t GetBaudrateValueForEnum(CANBaudrate enumValue);
@@ -710,7 +710,7 @@ public:
* applied to the device, the current device status will be
* reflected here rather than the pending status.
*/
optional<bool> isTerminationEnabledFor(Network net) const;
std::optional<bool> isTerminationEnabledFor(Network net) const;
/**
* Enable or disable software switchable termination for a
+2 -1
View File
@@ -18,6 +18,7 @@ typedef void* devicehandle_t;
#endif
typedef int32_t neodevice_handle_t;
typedef char deviceserial_t[7];
#pragma pack(push, 1)
@@ -31,7 +32,7 @@ typedef struct {
devicehandle_t device; // Pointer back to the C++ device object
neodevice_handle_t handle; // Handle for use by the underlying driver
devicetype_t type;
char serial[7];
deviceserial_t serial;
} neodevice_t;
#pragma pack(pop)
@@ -0,0 +1,65 @@
#ifndef __NEOVIFIRE3_H_
#define __NEOVIFIRE3_H_
#include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h"
#include "icsneo/disk/extextractordiskreaddriver.h"
#include "icsneo/disk/neomemorydiskdriver.h"
#include "icsneo/device/tree/neovifire3/neovifire3settings.h"
namespace icsneo {
class NeoVIFIRE3 : public Device {
public:
// Serial numbers start with ON
// Ethernet MAC allocation is 0x0E, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(NeoVIFIRE3, DeviceType::FIRE3, "ON");
static const std::vector<Network>& GetSupportedNetworks() {
static std::vector<Network> supportedNetworks = {
Network::NetID::HSCAN,
Network::NetID::MSCAN,
Network::NetID::HSCAN2,
Network::NetID::HSCAN3,
Network::NetID::HSCAN4,
Network::NetID::HSCAN5,
Network::NetID::HSCAN6,
Network::NetID::HSCAN7,
Network::NetID::Ethernet,
Network::NetID::LIN,
Network::NetID::LIN2
};
return supportedNetworks;
}
protected:
NeoVIFIRE3(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<NeoVIFIRE3Settings, Disk::ExtExtractorDiskReadDriver, Disk::NeoMemoryDiskDriver>(makeDriver);
}
virtual void setupEncoder(Encoder& encoder) override {
Device::setupEncoder(encoder);
encoder.supportCANFD = true;
}
void setupPacketizer(Packetizer& packetizer) override {
Device::setupPacketizer(packetizer);
packetizer.align16bit = false;
}
void setupSupportedRXNetworks(std::vector<Network>& rxNetworks) override {
for(auto& netid : GetSupportedNetworks())
rxNetworks.emplace_back(netid);
}
// The supported TX networks are the same as the supported RX networks for this device
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
bool supportsWiVI() const override { return true; }
};
}
#endif
@@ -0,0 +1,186 @@
#ifndef __NEOVIFIRE3SETTINGS_H_
#define __NEOVIFIRE3SETTINGS_H_
#include <stdint.h>
#include "icsneo/device/idevicesettings.h"
#ifdef __cplusplus
namespace icsneo {
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4201) // nameless struct/union
#endif
#pragma pack(push, 2)
typedef struct {
uint16_t perf_en;
uint16_t network_enabled_on_boot;
uint16_t misc_io_on_report_events;
uint16_t pwr_man_enable;
int16_t iso15765_separation_time_offset;
uint16_t slaveVnetA;
uint32_t reserved;
uint64_t termination_enables;
union {
uint64_t word;
struct
{
uint16_t network_enables;
uint16_t network_enables_2;
uint16_t network_enables_3;
};
} network_enables;
uint32_t pwr_man_timeout;
CAN_SETTINGS can1;
CANFD_SETTINGS canfd1;
CAN_SETTINGS can2;
CANFD_SETTINGS canfd2;
CAN_SETTINGS can3;
CANFD_SETTINGS canfd3;
CAN_SETTINGS can4;
CANFD_SETTINGS canfd4;
CAN_SETTINGS can5;
CANFD_SETTINGS canfd5;
CAN_SETTINGS can6;
CANFD_SETTINGS canfd6;
CAN_SETTINGS can7;
CANFD_SETTINGS canfd7;
CAN_SETTINGS can8;
CANFD_SETTINGS canfd8;
LIN_SETTINGS lin1;
LIN_SETTINGS lin2;
ISO9141_KEYWORD2000_SETTINGS iso9141_kwp_settings_1;
uint16_t iso_parity_1;
uint16_t iso_msg_termination_1;
ISO9141_KEYWORD2000_SETTINGS iso9141_kwp_settings_2;
uint16_t iso_parity_2;
uint16_t iso_msg_termination_2;
ETHERNET_SETTINGS ethernet;
TIMESYNC_ICSHARDWARE_SETTINGS timeSync;
STextAPISettings text_api;
struct {
uint32_t disableUsbCheckOnBoot : 1;
uint32_t enableLatencyTest : 1;
uint32_t busMessagesToAndroid : 1;
uint32_t enablePcEthernetComm : 1;
uint32_t enableDefaultLogger : 1;
uint32_t enableDefaultUpload : 1;
uint32_t reserved : 26;
} flags;
DISK_SETTINGS disk;
uint16_t misc_io_report_period;
uint16_t ain_threshold;
uint16_t misc_io_analog_enable;
uint16_t digitalIoThresholdTicks;
uint16_t digitalIoThresholdEnable;
uint16_t misc_io_initial_ddr;
uint16_t misc_io_initial_latch;
ETHERNET_SETTINGS2 ethernet2;
} neovifire3_settings_t;
typedef struct {
uint8_t backupPowerGood;
uint8_t backupPowerEnabled;
uint8_t usbHostPowerEnabled;
uint8_t ethernetActivationLineEnabled;
EthernetNetworkStatus ethernetStatus;
} neovifire3_status_t;
#pragma pack(pop)
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#ifdef __cplusplus
#include <iostream>
class NeoVIFIRE3Settings : public IDeviceSettings {
public:
NeoVIFIRE3Settings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(neovifire3_settings_t)) {}
const CAN_SETTINGS* getCANSettingsFor(Network net) const override {
auto cfg = getStructurePointer<neovifire3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::HSCAN:
return &(cfg->can1);
case Network::NetID::MSCAN:
return &(cfg->can2);
case Network::NetID::HSCAN2:
return &(cfg->can3);
case Network::NetID::HSCAN3:
return &(cfg->can4);
case Network::NetID::HSCAN4:
return &(cfg->can5);
case Network::NetID::HSCAN5:
return &(cfg->can6);
case Network::NetID::HSCAN6:
return &(cfg->can7);
case Network::NetID::HSCAN7:
return &(cfg->can8);
default:
return nullptr;
}
}
const CANFD_SETTINGS* getCANFDSettingsFor(Network net) const override {
auto cfg = getStructurePointer<neovifire3_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::HSCAN:
return &(cfg->canfd1);
case Network::NetID::MSCAN:
return &(cfg->canfd2);
case Network::NetID::HSCAN2:
return &(cfg->canfd3);
case Network::NetID::HSCAN3:
return &(cfg->canfd4);
case Network::NetID::HSCAN4:
return &(cfg->canfd5);
case Network::NetID::HSCAN5:
return &(cfg->canfd6);
case Network::NetID::HSCAN6:
return &(cfg->canfd7);
case Network::NetID::HSCAN7:
return &(cfg->canfd8);
default:
return nullptr;
}
}
virtual std::vector<TerminationGroup> getTerminationGroups() const override {
return {
{
Network(Network::NetID::HSCAN),
Network(Network::NetID::HSCAN3),
Network(Network::NetID::HSCAN5),
Network(Network::NetID::HSCAN7)
},
{
Network(Network::NetID::MSCAN),
Network(Network::NetID::HSCAN2),
Network(Network::NetID::HSCAN4),
Network(Network::NetID::HSCAN6)
}
};
}
protected:
ICSNEO_UNALIGNED(const uint64_t*) getTerminationEnables() const override {
auto cfg = getStructurePointer<neovifire3_settings_t>();
if(cfg == nullptr)
return nullptr;
return &cfg->termination_enables;
}
};
}
#endif // __cplusplus
#endif
@@ -27,6 +27,7 @@ public:
Network::NetID::HSCAN7,
Network::NetID::Ethernet,
Network::NetID::Ethernet2,
Network::NetID::LIN,
Network::NetID::LIN2
@@ -56,6 +57,8 @@ protected:
// The supported TX networks are the same as the supported RX networks for this device
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
bool supportsWiVI() const override { return true; }
};
}
@@ -88,6 +88,8 @@ protected:
const fire2vnet_status_t* status = reinterpret_cast<const fire2vnet_status_t*>(message->data.data());
ethActivationStatus = status->ethernetActivationLineEnabled;
}
bool supportsWiVI() const override { return true; }
};
}
@@ -0,0 +1,75 @@
#ifndef __RADA2B_H_
#define __RADA2B_H_
#ifdef __cplusplus
#include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/communication/decoder.h"
#include "icsneo/device/tree/rada2b/rada2bsettings.h"
namespace icsneo {
class RADA2B : public Device {
public:
// Serial numbers start with AB
// USB PID is 0x0006, standard driver is FTDI
// Ethernet MAC allocation is 0x18, standard driver is Raw
ICSNEO_FINDABLE_DEVICE(RADA2B, DeviceType::RAD_A2B, "AB");
static const std::vector<Network>& GetSupportedNetworks() {
static std::vector<Network> supportedNetworks = {
Network::NetID::HSCAN,
Network::NetID::HSCAN2,
Network::NetID::Ethernet,
Network::NetID::LIN,
Network::NetID::A2B1,
Network::NetID::A2B2,
Network::NetID::I2C,
Network::NetID::I2C2
};
return supportedNetworks;
}
size_t getEthernetActivationLineCount() const override { return 1; }
protected:
RADA2B(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADA2BSettings>(makeDriver);
}
void setupPacketizer(Packetizer& packetizer) override {
Device::setupPacketizer(packetizer);
packetizer.disableChecksum = true;
packetizer.align16bit = false;
}
void setupEncoder(Encoder& encoder) override {
Device::setupEncoder(encoder);
encoder.supportCANFD = true;
}
void setupDecoder(Decoder& decoder) override {
Device::setupDecoder(decoder);
decoder.timestampResolution = 10; // Timestamps are in 10ns increments instead of the usual 25ns
}
void setupSupportedRXNetworks(std::vector<Network>& rxNetworks) override {
for(auto& netid : GetSupportedNetworks())
rxNetworks.emplace_back(netid);
}
// The supported TX networks are the same as the supported RX networks for this device
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,97 @@
#ifndef __RADA2BSETTINGS_H_
#define __RADA2BSETTINGS_H_
#include <stdint.h>
#include "icsneo/device/idevicesettings.h"
#ifdef __cplusplus
namespace icsneo {
#endif
#pragma pack(push, 2)
typedef struct
{
uint8_t tdmMode;
uint8_t upstreamChannelOffset;
uint8_t downstreamChannelOffset;
uint8_t nodeType;
/*
* bit0: 16-bit channel width
*/
uint8_t flags;
uint8_t reserved[15];
} rada2b_monitor_settings_t;
typedef struct {
uint16_t perf_en;
struct
{
uint16_t hwComLatencyTestEn : 1;
uint16_t : 15;
} flags;
uint16_t network_enabled_on_boot;
CAN_SETTINGS can1;
CANFD_SETTINGS canfd1;
CAN_SETTINGS can2;
CANFD_SETTINGS canfd2;
LIN_SETTINGS lin1;
ISO9141_KEYWORD2000_SETTINGS iso9141_kwp_settings_1;
uint16_t iso_parity_1;
uint16_t iso_msg_termination_1;
uint64_t network_enables;
uint64_t termination_enables;
TIMESYNC_ICSHARDWARE_SETTINGS timeSyncSettings;
RAD_REPORTING_SETTINGS reporting;
DISK_SETTINGS disk;
LOGGER_SETTINGS logger;
int16_t iso15765_separation_time_offset;
rada2b_monitor_settings_t a2b_monitor;
rada2b_monitor_settings_t a2b_node;
uint32_t pwr_man_timeout;
uint16_t pwr_man_enable;
ETHERNET_SETTINGS2 ethernet;
} rada2b_settings_t;
#pragma pack(pop)
#ifdef __cplusplus
#include <iostream>
class RADA2BSettings : public IDeviceSettings {
public:
RADA2BSettings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(rada2b_settings_t)) {}
const CAN_SETTINGS* getCANSettingsFor(Network net) const override {
auto cfg = getStructurePointer<rada2b_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::HSCAN:
return &(cfg->can1);
case Network::NetID::HSCAN2:
return &(cfg->can2);
default:
return nullptr;
}
}
const CANFD_SETTINGS* getCANFDSettingsFor(Network net) const override {
auto cfg = getStructurePointer<rada2b_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::HSCAN:
return &(cfg->canfd1);
case Network::NetID::HSCAN2:
return &(cfg->canfd2);
default:
return nullptr;
}
}
};
}
#endif // __cplusplus
#endif
@@ -59,7 +59,12 @@ protected:
Network::NetID::LIN,
Network::NetID::FlexRay
Network::NetID::FlexRay,
Network::NetID::I2C,
Network::NetID::I2C2,
Network::NetID::I2C3,
Network::NetID::I2C4,
};
rxNetworks.insert(rxNetworks.end(), supportedRxNetworks.begin(), supportedRxNetworks.end());
}
@@ -0,0 +1,58 @@
#ifndef __RADJUPITER_H_
#define __RADJUPITER_H_
#ifdef __cplusplus
#include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h"
#include "icsneo/device/tree/radjupiter/radjupitersettings.h"
namespace icsneo {
class RADJupiter : public Device {
public:
// Serial numbers start with RJ
// USB PID is 1105, standard driver is CDCACM
ICSNEO_FINDABLE_DEVICE(RADJupiter, DeviceType::RADJupiter, "RJ");
static const std::vector<Network>& GetSupportedNetworks() {
static std::vector<Network> supportedNetworks = {
Network::NetID::HSCAN,
Network::NetID::HSCAN2,
Network::NetID::LIN,
Network::NetID::Ethernet // Connected to port 6 on the switch
};
return supportedNetworks;
}
bool getEthPhyRegControlSupported() const override { return true; }
protected:
RADJupiter(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADJupiterSettings>(makeDriver);
}
virtual void setupEncoder(Encoder& encoder) override {
Device::setupEncoder(encoder);
encoder.supportCANFD = true;
encoder.supportEthPhy = true;
}
void setupSupportedRXNetworks(std::vector<Network>& rxNetworks) override {
for(auto& netid : GetSupportedNetworks())
rxNetworks.emplace_back(netid);
}
// The supported TX networks are the same as the supported RX networks for this device
void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
bool requiresVehiclePower() const override { return false; }
};
}
#endif // __cplusplus
#endif
@@ -0,0 +1,127 @@
#ifndef __RADJUPITERSETTINGS_H_
#define __RADJUPITERSETTINGS_H_
#include <stdint.h>
#include "icsneo/device/idevicesettings.h"
#ifdef __cplusplus
namespace icsneo {
#endif
#define RADJUPITER_NUM_PORTS 8
#define JUPITER_PTP_ROLE_DISABLED 0
#define JUPITER_PTP_ROLE_MASTER 1
#define JUPITER_PTP_ROLE_SLAVE 2
#pragma pack(push, 2)
typedef struct {
uint32_t neighborPropDelay; //ns
int8_t initLogPDelayReqInterval; // log2ms
int8_t initLogSyncInterval; // log2ms
int8_t operationLogPDelayReqInterval; // log2ms
int8_t operationLogSyncInterval; // log2ms
uint8_t gPTPportRole[RADJUPITER_NUM_PORTS]; // The 6th port is used for CoreMini
} radjupiter_ptp_params_t;
typedef struct {
uint8_t phyMode[RADJUPITER_NUM_PORTS];
uint8_t enablePhy[RADJUPITER_NUM_PORTS];
uint8_t port7Select;
uint8_t port8Select;
uint8_t port8Speed;
uint8_t port8Legacy;
uint8_t spoofMacFlag;
uint8_t spoofedMac[6];
uint8_t pad;
radjupiter_ptp_params_t ptpParams;
} radjupiter_switch_settings_t;
typedef struct {
/* Performance Test */
uint16_t perf_en;
CAN_SETTINGS can1;
CANFD_SETTINGS canfd1;
CAN_SETTINGS can2;
CANFD_SETTINGS canfd2;
LIN_SETTINGS lin1;
uint16_t network_enables;
uint16_t network_enables_2;
uint16_t network_enables_3;
uint64_t termination_enables;
uint16_t misc_io_analog_enable;
uint32_t pwr_man_timeout;
uint16_t pwr_man_enable;
uint16_t network_enabled_on_boot;
/* ISO15765-2 Transport Layer */
int16_t iso15765_separation_time_offset;
uint16_t iso9141_kwp_enable_reserved;
uint16_t iso_tester_pullup_enable;
uint16_t iso_parity;
uint16_t iso_msg_termination;
ISO9141_KEYWORD2000_SETTINGS iso9141_kwp_settings;
ETHERNET_SETTINGS ethernet;
STextAPISettings text_api;
struct {
uint32_t disableUsbCheckOnBoot : 1;
uint32_t enableLatencyTest : 1;
uint32_t enablePcEthernetComm : 1;
uint32_t reserved : 29;
} flags;
radjupiter_switch_settings_t switchSettings;
ETHERNET_SETTINGS2 ethernet2;
} radjupiter_settings_t;
#pragma pack(pop)
#ifdef __cplusplus
static_assert(sizeof(radjupiter_settings_t) == 348, "RAD-Jupiter Settings are not packed correctly!");
#include <iostream>
class RADJupiterSettings : public IDeviceSettings {
public:
RADJupiterSettings(std::shared_ptr<Communication> com) : IDeviceSettings(com, sizeof(radjupiter_settings_t)) {}
const CAN_SETTINGS* getCANSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radjupiter_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::HSCAN:
return &(cfg->can1);
case Network::NetID::HSCAN2:
return &(cfg->can2);
default:
return nullptr;
}
}
const CANFD_SETTINGS* getCANFDSettingsFor(Network net) const override {
auto cfg = getStructurePointer<radjupiter_settings_t>();
if(cfg == nullptr)
return nullptr;
switch(net.getNetID()) {
case Network::NetID::HSCAN:
return &(cfg->canfd1);
case Network::NetID::HSCAN2:
return &(cfg->canfd2);
default:
return nullptr;
}
}
};
}
#endif // __cplusplus
#endif
@@ -5,7 +5,6 @@
#include "icsneo/device/device.h"
#include "icsneo/device/devicetype.h"
#include "icsneo/platform/ftdi.h"
#include "icsneo/device/tree/valuecan3/valuecan3settings.h"
namespace icsneo {
@@ -13,7 +12,7 @@ namespace icsneo {
class ValueCAN3 : public Device {
public:
// USB PID is 0x0601, standard driver is FTDI
ICSNEO_FINDABLE_DEVICE_BY_PID(ValueCAN3, DeviceType::VCAN3, 0x0701);
ICSNEO_FINDABLE_DEVICE_BY_PID(ValueCAN3, DeviceType::VCAN3, 0x0601);
static const std::vector<Network>& GetSupportedNetworks() {
static std::vector<Network> supportedNetworks = {
+15 -3
View File
@@ -3,10 +3,10 @@
#ifdef __cplusplus
#include "icsneo/platform/optional.h"
#include "icsneo/communication/communication.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/disk/diskdriver.h"
#include <optional>
#include <cstdint>
#include <chrono>
@@ -19,9 +19,12 @@ namespace Disk {
*/
class ReadDriver : public virtual Driver {
public:
virtual optional<uint64_t> readLogicalDisk(Communication& com, device_eventhandler_t report,
virtual std::optional<uint64_t> readLogicalDisk(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout = DefaultTimeout);
void invalidateCache(uint64_t pos = 0,
uint64_t amount = std::numeric_limits<uint32_t>::max() /* large value, but avoid overflow */);
protected:
/**
* Perform a read which the driver can do in one shot.
@@ -29,8 +32,17 @@ protected:
* The `pos` requested must be sector-aligned, and the `amount` must be
* within the block size bounds provided by the driver.
*/
virtual optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
virtual std::optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) = 0;
private:
std::vector<uint8_t> cache;
uint64_t cachePos = 0;
std::chrono::time_point<std::chrono::steady_clock> cachedAt;
static constexpr const std::chrono::milliseconds CacheTime = std::chrono::milliseconds(1000);
std::optional<uint64_t> readFromCache(uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds staleAfter = CacheTime);
};
} // namespace Disk
+4 -28
View File
@@ -3,12 +3,12 @@
#ifdef __cplusplus
#include "icsneo/platform/optional.h"
#include "icsneo/communication/communication.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/disk/diskreaddriver.h"
#include <cstdint>
#include <chrono>
#include <optional>
namespace icsneo {
@@ -19,42 +19,18 @@ namespace Disk {
*/
class WriteDriver : public virtual Driver {
public:
virtual optional<uint64_t> writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
virtual std::optional<uint64_t> writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout = DefaultTimeout);
protected:
/**
* Flag returned from writeLogicalDiskAligned when the
* operation failed to be performed atomically and can
* be retried after rereading.
*/
static const uint64_t RetryAtomic;
/**
* The severity to report with when an atomic operation
* is requested that the driver is unable to attempt.
*/
static const APIEvent::Severity NonatomicSeverity;
/**
* Perform a write which the driver can do in one shot.
*
* The `pos` requested must be sector-aligned, and the `amount` must be
* within the block size bounds provided by the driver.
*
* If `atomicBuf` is provided, it will be used to ensure that the disk
* data changes from `atomicBuf` to `from` without trampling any reads
* that may have happened while modifying the data.
*
* The flag `RetryAtomic` is returned if the operation was attempted
* atomically but failed.
*
* If the driver does not support atomic operations, but `atomicBuf`
* is non-null, an APIEvent::AtomicOperationCompletedNonatomically
* should be reported with `NonatomicSeverity`.
*/
virtual optional<uint64_t> 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) = 0;
virtual std::optional<uint64_t> writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) = 0;
};
} // namespace Disk
@@ -24,19 +24,14 @@ public:
private:
static constexpr const uint32_t MaxSize = Disk::SectorSize * 512;
static constexpr const std::chrono::seconds CacheTime = std::chrono::seconds(1);
static constexpr const uint8_t HeaderLength = 7;
std::array<uint8_t, MaxSize> cache;
uint64_t cachePos = 0;
std::chrono::time_point<std::chrono::steady_clock> cachedAt;
Access getPossibleAccess() const override { return Access::EntireCard; }
optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override;
optional<uint64_t> attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> attemptReadLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout);
};
+2 -2
View File
@@ -3,7 +3,7 @@
#ifdef __cplusplus
#include "icsneo/platform/optional.h"
#include <optional>
#include <cstdint>
#include <functional>
@@ -11,7 +11,7 @@ namespace icsneo {
namespace Disk {
optional<uint64_t> FindVSAInFAT(std::function< optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskRead);
std::optional<uint64_t> FindVSAInFAT(std::function< std::optional<uint64_t>(uint64_t pos, uint8_t* into, uint64_t amount) > diskRead);
} // namespace Disk
+3 -8
View File
@@ -27,19 +27,14 @@ public:
private:
static constexpr const uint8_t MemoryTypeSD = 0x01; // Logical Disk
static constexpr const std::chrono::seconds CacheTime = std::chrono::seconds(1);
std::array<uint8_t, SectorSize> cache;
uint64_t cachePos = 0;
std::chrono::time_point<std::chrono::steady_clock> cachedAt;
Access getPossibleAccess() const override { return Access::VSA; }
optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override;
optional<uint64_t> 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) override;
std::optional<uint64_t> writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) override;
};
} // namespace Disk
+5 -5
View File
@@ -18,9 +18,9 @@ namespace Disk {
*/
class NullDriver : public ReadDriver, public WriteDriver {
public:
optional<uint64_t> readLogicalDisk(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> readLogicalDisk(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout = DefaultTimeout) override;
optional<uint64_t> writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
std::optional<uint64_t> writeLogicalDisk(Communication& com, device_eventhandler_t report, ReadDriver& readDriver,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout = DefaultTimeout) override;
std::pair<uint32_t, uint32_t> getBlockSizeBounds() const override {
static_assert(SectorSize <= std::numeric_limits<uint32_t>::max(), "Incorrect sector size");
@@ -31,10 +31,10 @@ public:
private:
Access getPossibleAccess() const override { return Access::None; }
optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override;
optional<uint64_t> 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) override;
std::optional<uint64_t> writeLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, const uint8_t* from, uint64_t amount, std::chrono::milliseconds timeout) override;
};
} // namespace Disk
+1 -6
View File
@@ -24,15 +24,10 @@ public:
private:
static constexpr const uint32_t MaxSize = 65024;
static constexpr const std::chrono::seconds CacheTime = std::chrono::seconds(1);
std::array<uint8_t, MaxSize> cache;
uint64_t cachePos = 0;
std::chrono::time_point<std::chrono::steady_clock> cachedAt;
Access getPossibleAccess() const override { return Access::EntireCard; }
optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
std::optional<uint64_t> readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout) override;
};
+4
View File
@@ -16,6 +16,10 @@
#include "icsneo/communication/message/iso9141message.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/ethphymessage.h"
#include "icsneo/communication/message/i2cmessage.h"
#include "icsneo/communication/message/a2bmessage.h"
#include "icsneo/communication/message/callback/streamoutput/a2bwavoutput.h"
namespace icsneo {
-23
View File
@@ -1,23 +0,0 @@
#ifndef __ICSNEO_OPTIONAL_H_
#define __ICSNEO_OPTIONAL_H_
#include "icsneo/third-party/optional-lite/include/nonstd/optional.hpp"
/**
* We use icsneo::optional throughout the C++ API to allow for polyfilling
* std::optional into C++11 and C++14 environments. In a C++17 or better
* environment, icsneo::optional will be an alias of std::optional.
*/
namespace icsneo {
using nonstd::optional;
using nonstd::bad_optional_access;
using nonstd::make_optional;
using nonstd::nullopt;
using nonstd::nullopt_t;
} // namespace icsneo
#endif
+3 -4
View File
@@ -6,7 +6,7 @@
#include "icsneo/communication/driver.h"
#include "icsneo/device/neodevice.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/platform/optional.h"
#include <optional>
#include <chrono>
#include <sys/stat.h>
#include <stdint.h>
@@ -25,7 +25,7 @@ public:
* in cdcacmlinux.cpp and cdcacmdarwin.cpp respectively
* Other POSIX systems (BSDs, QNX, etc) will need bespoke code written in the future
*/
CDCACM(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err), device(forDevice) {}
CDCACM(const device_eventhandler_t& err, neodevice_t& forDevice) : Driver(err, forDevice) {}
~CDCACM();
static void Find(std::vector<FoundDevice>& found);
@@ -37,9 +37,8 @@ public:
void awaitModeChangeComplete() override;
private:
neodevice_t& device;
int fd = -1;
optional<ino_t> disallowedInode;
std::optional<ino_t> disallowedInode;
std::atomic<bool> modeChanging{false};
std::thread modeChangeThread;
std::mutex modeChangeMutex;
+3
View File
@@ -6,12 +6,15 @@
#include "icsneo/device/tree/neoobd2sim/neoobd2sim.h"
#include "icsneo/device/tree/neovifire/neovifire.h"
#include "icsneo/device/tree/neovifire2/neovifire2.h"
#include "icsneo/device/tree/neovifire3/neovifire3.h"
#include "icsneo/device/tree/neovired2/neovired2.h"
#include "icsneo/device/tree/plasion/neoviion.h"
#include "icsneo/device/tree/plasion/neoviplasma.h"
#include "icsneo/device/tree/rada2b/rada2b.h"
#include "icsneo/device/tree/radepsilon/radepsilon.h"
#include "icsneo/device/tree/radgalaxy/radgalaxy.h"
#include "icsneo/device/tree/radgigastar/radgigastar.h"
#include "icsneo/device/tree/radjupiter/radjupiter.h"
#include "icsneo/device/tree/radmars/radmars.h"
#include "icsneo/device/tree/radmoon2/radmoon2.h"
#include "icsneo/device/tree/radmoonduo/radmoonduo.h"
+12 -10
View File
@@ -7,7 +7,7 @@
#include "icsneo/device/founddevice.h"
#include "icsneo/communication/driver.h"
#include "icsneo/api/eventmanager.h"
#include "icsneo/platform/optional.h"
#include <optional>
#include <string>
namespace icsneo {
@@ -48,6 +48,7 @@ private:
};
struct Msg {
using Ref = uintptr_t;
enum class Command : uint32_t {
ComData = 0xAA000000,
ComFree = 0xAA000001,
@@ -56,15 +57,15 @@ private:
struct Data {
uint32_t addr;
uint32_t len;
uint32_t ref;
Ref ref;
uint32_t addr1;
uint32_t len1;
uint32_t ref1;
Ref ref1;
uint32_t reserved;
};
struct Free {
uint32_t refCount;
uint32_t ref[6];
Ref ref[6];
};
union Payload {
Data data;
@@ -97,12 +98,13 @@ private:
class Mempool {
public:
using PhysicalAddress = uintptr_t;
static constexpr const size_t BlockSize = 4096;
Mempool(uint8_t* start, uint32_t size, uint8_t* virt, uint32_t phys);
Mempool(uint8_t* start, uint32_t size, uint8_t* virt, PhysicalAddress phys);
uint8_t* alloc(uint32_t size);
bool free(uint8_t* addr);
uint32_t translate(uint8_t* addr) const;
PhysicalAddress translate(uint8_t* addr) const;
private:
struct BlockInfo {
@@ -118,18 +120,18 @@ private:
std::atomic<uint32_t> usedBlocks;
uint8_t* const virtualAddress;
const uint32_t physicalAddress;
const PhysicalAddress physicalAddress;
};
int fd = -1;
uint8_t* vbase = nullptr;
volatile ComHeader* header = nullptr;
optional<MsgQueue> in;
std::optional<MsgQueue> in;
std::mutex outMutex;
optional<MsgQueue> out;
optional<Mempool> outMemory;
std::optional<MsgQueue> out;
std::optional<Mempool> outMemory;
};
}
-2
View File
@@ -60,8 +60,6 @@ private:
void readTask();
void writeTask();
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
neodevice_t& device;
};
}
+1 -1
View File
@@ -24,9 +24,9 @@ public:
bool isOpen() override;
bool close() override;
bool isEthernet() const override { return true; }
bool enableHeartbeat() const override { return true; }
private:
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
neodevice_t& device;
uint8_t deviceMAC[6];
bool openable = true;
EthernetPacketizer ethPacketizer;

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