mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-21 16:38:39 +02:00
Compare commits
80
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9157c82e5 | ||
|
|
78465e0f20 | ||
|
|
ed1632c652 | ||
|
|
c97db0f35f | ||
|
|
c9d8a4f0a9 | ||
|
|
96fe8895fc | ||
|
|
e46755f120 | ||
|
|
0619c37a90 | ||
|
|
de58b2d639 | ||
|
|
5ff1707c47 | ||
|
|
4c0d49f09a | ||
|
|
b32f58da38 | ||
|
|
0101467154 | ||
|
|
17b3018499 | ||
|
|
0fc676e576 | ||
|
|
7b2544864b | ||
|
|
7f22286838 | ||
|
|
643768fb6a | ||
|
|
9817887523 | ||
|
|
13ec2bca98 | ||
|
|
711976cce0 | ||
|
|
e02203a974 | ||
|
|
5b14ec7246 | ||
|
|
3b80746fb8 | ||
|
|
3b9a31ef51 | ||
|
|
124bd3b310 | ||
|
|
9871430288 | ||
|
|
2e296dc8d3 | ||
|
|
b35fab754c | ||
|
|
9ef01e2d3d | ||
|
|
1bb33156f7 | ||
|
|
a0f73755e8 | ||
|
|
ab54697745 | ||
|
|
0d0b7f00bd | ||
|
|
1a28fbf5ad | ||
|
|
b50706846c | ||
|
|
d6d34eb7bc | ||
|
|
a928a1d879 | ||
|
|
de3d8bf870 | ||
|
|
407ccccedd | ||
|
|
00024990e9 | ||
|
|
cb47065a47 | ||
|
|
2b819065ad | ||
|
|
58700afc73 | ||
|
|
55d7d5bf17 | ||
|
|
c08c0dd893 | ||
|
|
44ad06d4bb | ||
|
|
c2de1dfdf1 | ||
|
|
a2cfc50b7c | ||
|
|
c398afc4e3 | ||
|
|
8d495aa916 | ||
|
|
91abf378f8 | ||
|
|
2e3b738e76 | ||
|
|
103f938d69 | ||
|
|
f1d9be1a81 | ||
|
|
2c282fe396 | ||
|
|
95de93aa84 | ||
|
|
733312628c | ||
|
|
b177617940 | ||
|
|
28d3730375 | ||
|
|
c451fc2b7c | ||
|
|
c620b2ee8b | ||
|
|
111d377d4a | ||
|
|
c3469f5fb6 | ||
|
|
0219eccc94 | ||
|
|
e22f70a35a | ||
|
|
b019c20ad0 | ||
|
|
6fa469de5f | ||
|
|
be219288dc | ||
|
|
d35653e3d0 | ||
|
|
0ce7326b83 | ||
|
|
714db03a05 | ||
|
|
d097fb9a74 | ||
|
|
9d5bad94d4 | ||
|
|
181223375a | ||
|
|
51626b9e63 | ||
|
|
a1a544045b | ||
|
|
719dbcefc8 | ||
|
|
c5314884ce | ||
|
|
82954d9190 |
@@ -9,3 +9,4 @@ third-party/concurrentqueue/benchmarks
|
||||
third-party/concurrentqueue/tests
|
||||
*.bak
|
||||
.vs
|
||||
.cache
|
||||
|
||||
+64
-23
@@ -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,13 +295,22 @@ 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)
|
||||
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 "")
|
||||
@@ -290,10 +321,17 @@ if(NOT WIN32)
|
||||
set(FTDI_EEPROM OFF CACHE INTERNAL "")
|
||||
add_subdirectory(third-party/libftdi)
|
||||
target_include_directories(icsneocpp PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
endif(NOT WIN32)
|
||||
|
||||
# winpcap
|
||||
if(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})
|
||||
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)
|
||||
@@ -301,12 +339,22 @@ if(WIN32)
|
||||
target_include_directories(icsneocpp PUBLIC AFTER ${LIBICSNEO_NPCAP_INCLUDE_DIR})
|
||||
add_definitions(-DNPCAP -DWIN32_LEAN_AND_MEAN)
|
||||
endif()
|
||||
endif(WIN32)
|
||||
else()
|
||||
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)
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
- RADStar 2
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
- RADA2B
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
|
||||
- Connecting over USB
|
||||
- ValueCAN 4 series
|
||||
@@ -37,3 +40,6 @@
|
||||
- CAN works
|
||||
- neoVI ION
|
||||
- CAN works
|
||||
- RADA2B
|
||||
- CAN works
|
||||
- Ethernet works
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+112
-1
@@ -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:
|
||||
@@ -255,11 +310,57 @@ const char* APIEvent::DescriptionForType(Type type) {
|
||||
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;
|
||||
}
|
||||
@@ -280,3 +381,13 @@ bool EventFilter::match(const APIEvent& event) const noexcept {
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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,14 +152,50 @@ bool EventManager::isDowngradingErrorsOnCurrentThread() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilter filter) {
|
||||
std::lock_guard<std::mutex> lk(eventsMutex);
|
||||
#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) {
|
||||
eventOutput.clear();
|
||||
#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;
|
||||
eventOutput.clear();
|
||||
auto it = events.begin();
|
||||
while(it != events.end()) {
|
||||
if(filter.match(*it)) {
|
||||
@@ -165,6 +207,7 @@ void EventManager::get(std::vector<APIEvent>& eventOutput, size_t max, EventFilt
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -638,8 +638,13 @@ int LegacyDLLExport icsneoSetDeviceParameters(void* hObject, char* pParmValue, i
|
||||
// Error Functions
|
||||
int LegacyDLLExport icsneoGetLastAPIError(void* hObject, unsigned long* pErrorNumber)
|
||||
{
|
||||
// TODO Implement
|
||||
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)
|
||||
|
||||
@@ -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,6 +25,7 @@ int Communication::messageCallbackIDCounter = 1;
|
||||
Communication::~Communication() {
|
||||
if(redirectingRead)
|
||||
clearRedirectRead();
|
||||
if(isOpen())
|
||||
close();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -238,6 +258,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <iterator>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
#include "icsneo/communication/packet/canpacket.h"
|
||||
#include "icsneo/communication/message/canerrorcountmessage.h"
|
||||
#include "icsneo/platform/optional.h"
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
|
||||
static std::optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
|
||||
if (length <= 8)
|
||||
return length;
|
||||
|
||||
@@ -27,10 +26,10 @@ static optional<uint8_t> CAN_DLCToLength(uint8_t length, bool fd) {
|
||||
}
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
|
||||
static std::optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
|
||||
{
|
||||
if (dataLength <= 8)
|
||||
return uint8_t(dataLength);
|
||||
@@ -52,7 +51,7 @@ static optional<uint8_t> CAN_LengthToDLC(size_t dataLength, bool fd)
|
||||
return uint8_t(0xF);
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<uint8_t>& bytestream) {
|
||||
@@ -94,7 +93,7 @@ std::shared_ptr<Message> HardwareCANPacket::DecodeToMessage(const std::vector<ui
|
||||
msg->isCANFD = true;
|
||||
msg->baudrateSwitch = data->header.BRS; // CAN FD Baudrate Switch
|
||||
msg->errorStateIndicator = data->header.ESI;
|
||||
const optional<uint8_t> lenFromDLC = CAN_DLCToLength(length, true);
|
||||
const std::optional<uint8_t> lenFromDLC = CAN_DLCToLength(length, true);
|
||||
if (lenFromDLC)
|
||||
length = *lenFromDLC;
|
||||
} else if(length > 8) { // This is a standard CAN frame with a length of more than 8
|
||||
@@ -133,7 +132,7 @@ bool HardwareCANPacket::EncodeFromMessage(const CANMessage& message, std::vector
|
||||
}
|
||||
|
||||
const size_t dataSize = message.data.size();
|
||||
optional<uint8_t> dlc = CAN_LengthToDLC(dataSize, message.isCANFD);
|
||||
std::optional<uint8_t> dlc = CAN_LengthToDLC(dataSize, message.isCANFD);
|
||||
if (!dlc.has_value()) {
|
||||
report(APIEvent::Type::MessageMaxLengthExceeded, APIEvent::Severity::Error);
|
||||
return false; // Too much data for the protocol
|
||||
|
||||
@@ -0,0 +1,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;
|
||||
}
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
+769
-40
File diff suppressed because it is too large
Load Diff
+120
-35
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -12,40 +12,40 @@
|
||||
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 largeSectorCount = amount / SectorSize;
|
||||
uint32_t sectorCount = uint32_t(largeSectorCount);
|
||||
if (largeSectorCount != uint64_t(sectorCount))
|
||||
return nullopt;
|
||||
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
|
||||
@@ -146,7 +146,7 @@ optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Com
|
||||
#ifdef ICSNEO_EXTENDED_EXTRACTOR_DEBUG_PRINTS
|
||||
std::cout << "With " << int(left) << " bytes " << int(offset) << std::endl;
|
||||
#endif
|
||||
memcpy(cache.data() + received, data.data() + offset, count);
|
||||
memcpy(into + received, data.data() + offset, count);
|
||||
received += count;
|
||||
receivedCurrent += count;
|
||||
offset += count;
|
||||
@@ -181,7 +181,7 @@ optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Com
|
||||
Lifetime clearRedirect([&com, &lk] { lk.unlock(); com.clearRedirectRead(); });
|
||||
|
||||
if(error)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
error = !com.sendCommand(ExtendedCommand::Extract, {
|
||||
uint8_t(sector & 0xff),
|
||||
@@ -198,16 +198,11 @@ optional<uint64_t> ExtExtractorDiskReadDriver::attemptReadLogicalDiskAligned(Com
|
||||
uint8_t((sectorCount >> 24) & 0xff),
|
||||
});
|
||||
if(error)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
bool hitTimeout = !cv.wait_for(lk, timeout, [&]() { return error || amount == received; });
|
||||
if(hitTimeout || error)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
cachedAt = std::chrono::steady_clock::now();
|
||||
cachePos = pos;
|
||||
}
|
||||
|
||||
memcpy(into, cache.data(), size_t(amount));
|
||||
return amount;
|
||||
}
|
||||
|
||||
+4
-4
@@ -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);
|
||||
}
|
||||
@@ -5,18 +5,16 @@
|
||||
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([¤tSector, &com] {
|
||||
return com.sendCommand(Command::NeoReadMemory, {
|
||||
@@ -38,37 +36,23 @@ optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& co
|
||||
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
|
||||
if(!sdmsg || sdmsg->data.size() != SectorSize) {
|
||||
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
memcpy(cache.data(), sdmsg->data.data(), SectorSize);
|
||||
cachedAt = std::chrono::steady_clock::now();
|
||||
cachePos = pos;
|
||||
}
|
||||
|
||||
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([¤tSector, &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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -6,50 +6,46 @@
|
||||
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;
|
||||
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::unique_lock<std::mutex> lk(m);
|
||||
auto cb = com.addMessageCallback(MessageCallback([&](std::shared_ptr<Message> msg) {
|
||||
auto cb = com.addMessageCallback(std::make_shared<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()) {
|
||||
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(cache.data() + copied, sdmsg->data.data(), sdmsg->data.size());
|
||||
memcpy(into + copied, sdmsg->data.data(), sdmsg->data.size());
|
||||
copied += uint32_t(sdmsg->data.size());
|
||||
if(copied == amount) {
|
||||
lk.unlock();
|
||||
cv.notify_all();
|
||||
|
||||
}
|
||||
}, NeoMemorySDRead));
|
||||
|
||||
@@ -67,12 +63,7 @@ optional<uint64_t> PlasionDiskReadDriver::readLogicalDiskAligned(Communication&
|
||||
com.removeMessageCallback(cb);
|
||||
|
||||
if(hitTimeout)
|
||||
return nullopt;
|
||||
return std::nullopt;
|
||||
|
||||
cachedAt = std::chrono::steady_clock::now();
|
||||
cachePos = pos;
|
||||
}
|
||||
|
||||
memcpy(into, cache.data(), size_t(amount));
|
||||
return amount;
|
||||
}
|
||||
@@ -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``
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
@@ -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™
|
||||
@@ -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);
|
||||
}));
|
||||
|
||||
|
||||
@@ -25,3 +25,9 @@ endif()
|
||||
|
||||
add_executable(libicsneocpp-simple-example src/SimpleExample.cpp)
|
||||
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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 {
|
||||
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,6 +82,8 @@ public:
|
||||
MessageMaxLengthExceeded = 0x1012,
|
||||
ValueNotYetPresent = 0x1013,
|
||||
Timeout = 0x1014,
|
||||
WiVINotSupported = 0x1015,
|
||||
TestEvent = 0x1016,
|
||||
|
||||
// Device Events
|
||||
PollingMessageOverflow = 0x2000,
|
||||
@@ -86,6 +122,10 @@ public:
|
||||
SettingsDefaultsUsed = 0x2033,
|
||||
AtomicOperationRetried = 0x2034,
|
||||
AtomicOperationCompletedNonatomically = 0x2035,
|
||||
WiVIStackRefreshFailed = 0x2036,
|
||||
WiVIUploadStackOverflow = 0x2037,
|
||||
I2CMessageExceedsMaxLength = 0x2038,
|
||||
A2BMessageIncompleteFrame = 0x2039,
|
||||
|
||||
// Transport Events
|
||||
FailedToRead = 0x3000,
|
||||
@@ -99,6 +139,30 @@ public:
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -29,6 +29,8 @@ public:
|
||||
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);
|
||||
if(ret)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
+247
-28
@@ -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,7 +505,14 @@ 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&) {}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/platform/optional.h"
|
||||
#include <optional>
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user