Compare commits

...

10 Commits

Author SHA1 Message Date
vits71 72eadcc05a
Merge e233233b94 into ca370a1310 2024-08-27 15:04:57 -04:00
Bryant Jones ca370a1310 Device: Comet2: Enable TC10 2024-08-22 16:06:13 +00:00
Kyle Schwarz 564933cb41 Device: Add readCoreminiHeader()
- Fixes NeoMemoryDiskDriver::readLogicalDiskAligned() for flash
- Adds FlashMemoryMessage
2024-08-21 10:42:04 -04:00
Kyle Johannes cac8d760b0 Communication: Add AppErrorMessage support
App errors are responses from the device indicating internal runtime errors.
2024-08-15 16:47:35 +00:00
Kurt Wachowski 75af3220b0 Driver: Block between read attempts
Driver:

* Refactored to limit accessibility of member fields;

Communication:

* readTask() now calls for a blocking wait;
2024-08-13 13:55:12 +00:00
Kyle Schwarz f25a0a4a81 Device: Remove const from bool return type 2024-08-02 11:10:20 -04:00
Kyle Schwarz 387c39d3a0 Platform: TCP: Guard against negative duration 2024-07-29 22:50:16 -04:00
Kyle Schwarz 4476bd8b71 Platform: TCP: Add poll function to Socket 2024-07-30 02:09:24 +00:00
Bryant Jones 14588591e5 Device: Comet2/Comet3: Add support for TC10 API 2024-07-08 14:00:24 +00:00
Kyle Johannes cbcbcbdc5d System Test: FIRE 3 and VCAN 4-2EL 2024-06-11 22:04:09 +00:00
39 changed files with 808 additions and 169 deletions

View File

@ -319,56 +319,101 @@ unit_test linux/fedora/39/amd64/clang:
after_script:
- /opt/libvirt-driver/cleanup.sh
hardware_test system-test-fedora38-red2:
<<: *hw_test
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: ETH_A
.fedora38_needs: &fedora38_needs
needs:
- job: build linux/fedora/38/amd64/clang
artifacts: true
hardware_test system-test-fedora38-vcan42:
hardware_test fedora38-red2:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: ETH_A
hardware_test fedora38-vcan42:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: USB_D
needs:
- job: build linux/fedora/38/amd64/clang
artifacts: true
hardware_test system-test-ubuntu2204-red2:
hardware_test fedora38-fire3:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: ETH_A
GUEST_OS_TAG: fedora38
DEVICE_PORT: ETH_B
hardware_test fedora38-vcan42-EL:
<<: *hw_test
<<: *fedora38_needs
variables:
GUEST_OS_TAG: fedora38
DEVICE_PORT: USB_C
.ubuntu2204_needs: &ubuntu2204_needs
needs:
- job: build linux/ubuntu/2204/amd64/clang
artifacts: true
hardware_test system-test-ubuntu2204-vcan42:
hardware_test ubuntu2204-red2:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: ETH_A
hardware_test ubuntu2204-vcan42:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: USB_D
hardware_test ubuntu2204-fire3:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: ETH_B
hardware_test ubuntu2204-vcan42-EL:
<<: *hw_test
<<: *ubuntu2204_needs
variables:
GUEST_OS_TAG: ubuntu22.04
DEVICE_PORT: USB_C
.win10_needs: &win10_needs
needs:
- job: build linux/ubuntu/2204/amd64/clang
- job: build windows/x64
artifacts: true
hardware_test system-test-win10-red2:
hardware_test win10-red2:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: ETH_A
needs:
- job: build windows/x64
artifacts: true
hardware_test system-test-win10-vcan42:
hardware_test win10-vcan42:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: USB_D
needs:
- job: build windows/x64
artifacts: true
hardware_test win10-fire3:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: ETH_B
hardware_test win10-vcan42-EL:
<<: *hw_test
<<: *win10_needs
variables:
GUEST_OS_TAG: win10
DEVICE_PORT: USB_C

View File

@ -238,6 +238,7 @@ set(SRC_FILES
communication/message/flexray/control/flexraycontrolmessage.cpp
communication/message/callback/streamoutput/a2bwavoutput.cpp
communication/message/a2bmessage.cpp
communication/message/apperrormessage.cpp
communication/message/neomessage.cpp
communication/message/ethphymessage.cpp
communication/message/linmessage.cpp
@ -519,6 +520,7 @@ if(LIBICSNEO_BUILD_UNIT_TESTS)
test/unit/mdioencoderdecodertest.cpp
test/unit/livedataencoderdecodertest.cpp
test/unit/ringbuffertest.cpp
test/unit/apperrordecodertest.cpp
)
target_link_libraries(libicsneo-unit-tests gtest gtest_main)

View File

@ -276,7 +276,7 @@ void Communication::readTask() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
pauseReadTaskCv.wait(lk, [this]() { return !pauseReadTask; });
}
if(driver->readAvailable()) {
if(driver->waitForRx(readTaskWakeLimit, readTaskWakeTimeout)) {
if(pauseReadTask) {
/**
* Reads could have paused while the driver was not available

View File

@ -5,6 +5,7 @@
#include "icsneo/communication/message/readsettingsmessage.h"
#include "icsneo/communication/message/canerrorcountmessage.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/message/flashmemorymessage.h"
#include "icsneo/communication/message/extendedresponsemessage.h"
#include "icsneo/communication/message/wiviresponsemessage.h"
#include "icsneo/communication/message/scriptstatusmessage.h"
@ -18,6 +19,7 @@
#include "icsneo/communication/message/diskdatamessage.h"
#include "icsneo/communication/message/hardwareinfo.h"
#include "icsneo/communication/message/tc10statusmessage.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/command.h"
#include "icsneo/device/device.h"
#include "icsneo/communication/packet/canpacket.h"
@ -256,6 +258,18 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
result = std::make_shared<RawMessage>(packet->network, packet->data);
return true;
}
case Network::NetID::RED_INT_MEMORYREAD: {
if(packet->data.size() != 512 + sizeof(uint16_t)) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return false; // Should get enough data for a start address and sector
}
const auto msg = std::make_shared<FlashMemoryMessage>();
result = msg;
msg->startAddress = *reinterpret_cast<uint16_t*>(packet->data.data());
msg->data.insert(msg->data.end(), packet->data.begin() + 2, packet->data.end());
return true;
}
case Network::NetID::NeoMemorySDRead: {
if(packet->data.size() != 512 + sizeof(uint32_t)) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
@ -398,6 +412,14 @@ bool Decoder::decode(std::shared_ptr<Message>& result, const std::shared_ptr<Pac
packet->data.resize(length);
return decode(result, packet);
}
case Network::NetID::RED_App_Error: {
result = AppErrorMessage::DecodeToMessage(packet->data, report);
if(!result) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::EventWarning);
return false;
}
return true;
}
case Network::NetID::ReadSettings: {
auto msg = std::make_shared<ReadSettingsMessage>();
msg->response = ReadSettingsMessage::Response(packet->data[0]);

View File

@ -11,13 +11,21 @@ using namespace icsneo;
bool Driver::pushRx(const uint8_t* buf, size_t numReceived) {
bool ret = readBuffer.write(buf, numReceived);
if(hasRxWaitRequest) {
rxWaitRequestCv.notify_one();
}
rxWaitCv.notify_all();
return ret;
}
void Driver::clearBuffers()
{
WriteOperation flushop;
readBuffer.clear();
rxWaitCv.notify_all();
while (writeQueue.try_dequeue(flushop)) {}
}
bool Driver::waitForRx(size_t limit, std::chrono::milliseconds timeout) {
return waitForRx([limit, this]() {
return readBuffer.size() >= limit;
@ -26,13 +34,7 @@ bool Driver::waitForRx(size_t limit, std::chrono::milliseconds timeout) {
bool Driver::waitForRx(std::function<bool()> predicate, std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lk(rxWaitMutex);
hasRxWaitRequest = true;
auto ret = rxWaitRequestCv.wait_for(lk, timeout, predicate);
hasRxWaitRequest = false;
return ret;
return rxWaitCv.wait_for(lk, timeout, predicate);
}
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
@ -92,4 +94,4 @@ bool Driver::write(const std::vector<uint8_t>& bytes) {
report(APIEvent::Type::Unknown, APIEvent::Severity::Error);
return ret;
}
}

View File

@ -0,0 +1,143 @@
#include <icsneo/communication/message/apperrormessage.h>
namespace icsneo {
#pragma pack(push, 2)
typedef struct {
uint16_t error_type;
uint16_t network_id;
uint32_t uiTimeStamp10uS;
uint32_t uiTimeStamp10uSMSB;
} AppErrorData;
#pragma pack(pop)
std::shared_ptr<Message> AppErrorMessage::DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report) {
const AppErrorData* data = reinterpret_cast<const AppErrorData*>(bytestream.data());
if(!data) {
report(APIEvent::Type::AppErrorParsingFailed, APIEvent::Severity::Error);
return nullptr;
}
auto appErr = std::make_shared<AppErrorMessage>();
appErr->errorType = data->error_type;
appErr->errorNetID = static_cast<Network::NetID>(data->network_id);
appErr->timestamp10us = data->uiTimeStamp10uS;
appErr->timestamp10usMSB = data->uiTimeStamp10uSMSB;
appErr->network = Network::NetID::RED_App_Error;
return appErr;
}
AppErrorType AppErrorMessage::getAppErrorType() {
AppErrorType errType = static_cast<AppErrorType>(errorType);
if(errType > AppErrorType::AppNoError) {
return AppErrorType::AppNoError;
}
return errType;
}
std::string AppErrorMessage::getAppErrorString() {
auto netIDString = Network::GetNetIDString(errorNetID);
AppErrorType errType = static_cast<AppErrorType>(errorType);
switch (errType) {
case AppErrorType::AppErrorRxMessagesFull:
return std::string(netIDString) + ": RX message buffer full";
case AppErrorType::AppErrorTxMessagesFull:
return std::string(netIDString) + ": TX message buffer full";
case AppErrorType::AppErrorTxReportMessagesFull:
return std::string(netIDString) + ": TX report buffer full";
case AppErrorType::AppErrorBadCommWithDspIC:
return "Received bad packet from DSP IC";
case AppErrorType::AppErrorDriverOverflow:
return std::string(netIDString) + ": Driver overflow";
case AppErrorType::AppErrorPCBuffOverflow:
return "PC buffer overflow";
case AppErrorType::AppErrorPCChksumError:
return "PC checksum error";
case AppErrorType::AppErrorPCMissedByte:
return "PC missed byte";
case AppErrorType::AppErrorPCOverrunError:
return "PC overrun error";
case AppErrorType::AppErrorSettingFailure:
return std::string(netIDString) + ": Settings incorrectly set";
case AppErrorType::AppErrorTooManySelectedNetworks:
return "Too many selected networks";
case AppErrorType::AppErrorNetworkNotEnabled:
return std::string(netIDString) + ": Network not enabled";
case AppErrorType::AppErrorRtcNotCorrect:
return "RTC not correct";
case AppErrorType::AppErrorLoadedDefaultSettings:
return "Loaded default settings";
case AppErrorType::AppErrorFeatureNotUnlocked:
return "Feature not unlocked";
case AppErrorType::AppErrorFeatureRtcCmdDropped:
return "RTC command dropped";
case AppErrorType::AppErrorTxMessagesFlushed:
return "TX message buffer flushed";
case AppErrorType::AppErrorTxMessagesHalfFull:
return "TX message buffer half full";
case AppErrorType::AppErrorNetworkNotValid:
return "Network is not valid";
case AppErrorType::AppErrorTxInterfaceNotImplemented:
return "TX interface is not implemented";
case AppErrorType::AppErrorTxMessagesCommEnableIsOff:
return "TX message communication is disabled";
case AppErrorType::AppErrorRxFilterMatchCountExceeded:
return "RX filter match count exceeded";
case AppErrorType::AppErrorEthPreemptionNotEnabled:
return std::string(netIDString) + ": Ethernet preemption not enabled";
case AppErrorType::AppErrorTxNotSupportedInMode:
return std::string(netIDString) + ": Transmit is not supported in this mode";
case AppErrorType::AppErrorJumboFramesNotSupported:
return std::string(netIDString) + ": Jumbo frames not supported";
case AppErrorType::AppErrorEthernetIpFragment:
return "Ethernet IP fragment received";
case AppErrorType::AppErrorTxMessagesUnderrun:
return std::string(netIDString) + ": Transmit buffer underrun";
case AppErrorType::AppErrorDeviceFanFailure:
return "Device fan failure";
case AppErrorType::AppErrorDeviceOvertemperature:
return "Device overtemperature";
case AppErrorType::AppErrorTxMessageIndexOutOfRange:
return "Transmit message index out of range";
case AppErrorType::AppErrorUndersizedFrameDropped:
return std::string(netIDString) + ": Undersized frame dropped";
case AppErrorType::AppErrorOversizedFrameDropped:
return std::string(netIDString) + ": Oversized frame dropped";
case AppErrorType::AppErrorWatchdogEvent:
return "Watchdog event occured";
case AppErrorType::AppErrorSystemClockFailure:
return "Device clock failed";
case AppErrorType::AppErrorSystemClockRecovered:
return "Device clock recovered";
case AppErrorType::AppErrorSystemPeripheralReset:
return "Device peripheral reset";
case AppErrorType::AppErrorSystemCommunicationFailure:
return "Device communication failure";
case AppErrorType::AppErrorTxMessagesUnsupportedSourceOrPacketId:
return std::string(netIDString) + ": Transmit unsupported source or packet ID";
case AppErrorType::AppErrorWbmsManagerConnectFailed:
return std::string(netIDString) + ": Failed to connect to managers with settings";
case AppErrorType::AppErrorWbmsManagerConnectBadState:
return std::string(netIDString) + ": Connected to managers in a invalid state";
case AppErrorType::AppErrorWbmsManagerConnectTimeout:
return std::string(netIDString) + ": Timeout while attempting to connect to managers";
case AppErrorType::AppErrorFailedToInitializeLoggerDisk:
return "Device failed to initialize storage disk";
case AppErrorType::AppErrorInvalidSetting:
return std::string(netIDString) + ": Invalid settings";
case AppErrorType::AppErrorSystemFailureRequestedReset:
return "Device rebooted to recover from an unexpected error condition";
case AppErrorType::AppErrorPortKeyMistmatch:
return std::string(netIDString) + ": Mismatch between key in manager and stored key";
case AppErrorType::AppErrorErrorBufferOverflow:
return "Device error buffer overflow";
case AppErrorType::AppNoError:
return "No error";
default:
return "Unknown error";
}
return "Unknown error";
}
} // namespace icsneo

View File

@ -656,6 +656,85 @@ bool Device::clearScript(Disk::MemoryType memType)
return true;
}
std::optional<CoreminiHeader> Device::readCoreminiHeader(Disk::MemoryType memType) {
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
return std::nullopt;
}
auto startAddress = getCoreminiStartAddress(memType);
if(!startAddress) {
return std::nullopt;
}
auto connected = isLogicalDiskConnected();
if(!connected) {
return std::nullopt; // Already added an API error
}
#pragma pack(push, 2)
struct RawCoreminiHeader {
uint16_t fileType;
uint16_t fileVersion;
uint32_t storedFileSize;
uint32_t fileChecksum;
union
{
struct
{
uint32_t skipDecompression : 1;
uint32_t encryptedMode : 1;
uint32_t reserved : 30;
} bits;
uint32_t word;
} flags;
uint8_t fileHash[32];
union
{
struct
{
uint32_t lsb;
uint32_t msb;
} words;
uint64_t time64;
} createTime;
uint8_t reserved[8];
};
#pragma pack(pop)
RawCoreminiHeader header = {};
auto numRead = readLogicalDisk(*startAddress, (uint8_t*)&header, sizeof(header), std::chrono::milliseconds(2000), memType);
if(!numRead) {
return std::nullopt; // Already added an API error
}
if(*numRead != sizeof(header)) {
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
return std::nullopt;
}
if(header.fileType != 0x0907) {
report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error);
return std::nullopt;
}
std::optional<CoreminiHeader> ret;
ret.emplace();
ret->coreminiVersion = header.fileVersion;
ret->storedFileSize = header.storedFileSize;
ret->fileChecksum = header.fileChecksum;
ret->skipDecompression = static_cast<bool>(header.flags.bits.skipDecompression);
ret->encryptedMode = static_cast<bool>(header.flags.bits.encryptedMode);
std::copy(std::begin(header.fileHash), std::end(header.fileHash), ret->fileHash.begin());
static constexpr std::chrono::seconds icsEpochDelta(1167609600);
static constexpr uint8_t timestampResolution = 25;
static constexpr uint16_t nsInUs = 1'000;
ret->timestamp += icsEpochDelta + std::chrono::microseconds(header.createTime.time64 * timestampResolution / nsInUs);
return ret;
}
bool Device::transmit(std::shared_ptr<Frame> frame) {
if(!isOpen()) {
report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error);
@ -2116,7 +2195,7 @@ bool Device::readVSA(const VSAExtractionSettings& extractionSettings) {
if(isOnline()) {
goOffline();
}
auto innerReadVSA = [&](uint64_t diskSize) -> const bool {
auto innerReadVSA = [&](uint64_t diskSize) -> bool {
// Adjust driver to offset to start of VSA file
const auto& offset = getVSAOffsetInLogicalDisk();
if(!offset) {

View File

@ -1,5 +1,6 @@
#include "icsneo/disk/neomemorydiskdriver.h"
#include "icsneo/communication/message/neoreadmemorysdmessage.h"
#include "icsneo/communication/message/flashmemorymessage.h"
#include <cstring>
#include <iostream>
@ -8,7 +9,8 @@ using namespace icsneo::Disk;
std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communication& com, device_eventhandler_t report,
uint64_t pos, uint8_t* into, uint64_t amount, std::chrono::milliseconds timeout, MemoryType memType) {
static std::shared_ptr<MessageFilter> NeoMemorySDRead = std::make_shared<MessageFilter>(Network::NetID::NeoMemorySDRead);
const auto filter = std::make_shared<MessageFilter>((memType == MemoryType::SD ? Network::NetID::NeoMemorySDRead : Network::NetID::RED_INT_MEMORYREAD));
filter->includeInternalInAny = true;
if(pos % SectorSize != 0)
return std::nullopt;
@ -20,7 +22,7 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
const uint8_t memLocation = (uint8_t)memType;
uint64_t numWords = amount / 2;
auto msg = com.waitForMessageSync([&currentSector, &memLocation, &com, &numWords] {
return com.sendCommand(Command::NeoReadMemory, {
memLocation,
@ -33,18 +35,26 @@ std::optional<uint64_t> NeoMemoryDiskDriver::readLogicalDiskAligned(Communicatio
uint8_t((numWords >> 16) & 0xFF),
uint8_t((numWords >> 24) & 0xFF)
});
}, NeoMemorySDRead, timeout);
}, filter, timeout);
if(!msg)
return 0;
const auto sdmsg = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!sdmsg || sdmsg->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
if(memType == MemoryType::SD) {
const auto mem = std::dynamic_pointer_cast<NeoReadMemorySDMessage>(msg);
if(!mem || mem->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
}
memcpy(into, mem->data.data(), SectorSize);
} else { // flash
const auto mem = std::dynamic_pointer_cast<FlashMemoryMessage>(msg);
if(!mem || mem->data.size() != SectorSize) {
report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error);
return std::nullopt;
}
memcpy(into, mem->data.data(), SectorSize);
}
memcpy(into, sdmsg->data.data(), SectorSize);
return SectorSize;
}

View File

@ -9,6 +9,7 @@ option(LIBICSNEO_BUILD_CPP_LIVEDATA_EXAMPLE "Build the Live Data example." ON)
option(LIBICSNEO_BUILD_CPP_COREMINI_EXAMPLE "Build the Coremini example." ON)
option(LIBICSNEO_BUILD_CPP_MDIO_EXAMPLE "Build the MDIO example." ON)
option(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE "Build the VSA example." ON)
option(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE "Build the app error example." ON)
# Disabled until we properly build these in-tree
# option(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE "Build the command-line interactive C# example." OFF)
@ -58,6 +59,10 @@ if(LIBICSNEO_BUILD_CPP_VSA_EXAMPLE)
add_subdirectory(cpp/vsa)
endif()
if(LIBICSNEO_BUILD_CPP_APP_ERROR_EXAMPLE)
add_subdirectory(cpp/apperror)
endif()
# if(LIBICSNEO_BUILD_CSHARP_INTERACTIVE_EXAMPLE)
# add_subdirectory(csharp)
# endif()

View File

@ -0,0 +1,2 @@
add_executable(libicsneocpp-app-error src/AppErrorExample.cpp)
target_link_libraries(libicsneocpp-app-error icsneocpp)

View File

@ -0,0 +1,85 @@
#include <iostream>
#include <iomanip>
#include <thread>
#include <chrono>
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/message/message.h"
/*
* App errors are responses from the device indicating internal runtime errors
* NOTE: To trigger the app error in this example, disable the HSCAN network on the device
* (e.g. with neoVI Explorer)
*/
int main() {
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
std::cout << "\nFinding devices... " << std::flush;
auto devices = icsneo::FindAllDevices();
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
// List off the devices
for(auto& device : devices)
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
std::cout << std::endl;
for(auto device : devices) {
std::cout << "Connecting to " << device->describe() << "... ";
bool ret = device->open();
if(!ret) { // Failed to open
std::cout << "FAIL" << std::endl;
std::cout << icsneo::GetLastError() << std::endl << std::endl;
continue;
}
std::cout << "OK" << std::endl << std::endl;
// Create an app error message filter, including "internal" messages
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::AppError);
filter->includeInternalInAny = true;
// ...and register a callback with it.
// Add your error handling here
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>(filter, [](std::shared_ptr<icsneo::Message> message) {
auto msg = std::static_pointer_cast<icsneo::AppErrorMessage>(message);
if(icsneo::Network::NetID::RED_App_Error == msg->network.getNetID()) {
std::cout << std::endl << "App error reported:" << std::endl;
std::cout << msg->getAppErrorString() << std::endl << std::endl;
}
}));
std::cout << "Going online... ";
ret = device->goOnline();
if(!ret) {
std::cout << "FAIL" << std::endl;
device->close();
continue;
}
std::cout << "OK" << std::endl;
// Prepare a CAN message
std::cout << std::endl << "Transmitting a CAN frame... ";
auto txMessage = std::make_shared<icsneo::CANMessage>();
txMessage->network = icsneo::Network::NetID::HSCAN;
txMessage->arbid = 0x22;
txMessage->data.insert(txMessage->data.end(), {0xaa, 0xbb, 0xcc});
// The DLC will come from the length of the data vector
txMessage->isExtended = false;
txMessage->isCANFD = false;
// Transmit a CAN message on HSCAN, even though HSCAN is disabled on the device!
// Expect to see an app error caught in the callback defined above
ret = device->transmit(txMessage);
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
// Go offline, stop sending and receiving traffic
device->removeMessageCallback(handler);
std::cout << "Going offline... ";
ret = device->goOffline();
std::cout << (ret ? "OK" : "FAIL") << std::endl;
std::cout << "Disconnecting... ";
ret = device->close();
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
}
return 0;
}

View File

@ -108,6 +108,7 @@ public:
LiveDataNotSupported = 0x2052,
LINSettingsNotAvailable = 0x2053,
ModeNotFound = 0x2054,
AppErrorParsingFailed = 0x2055,
// Transport Events
FailedToRead = 0x3000,

View File

@ -87,6 +87,9 @@ public:
std::unique_ptr<Driver> driver;
device_eventhandler_t report;
size_t readTaskWakeLimit = 1;
std::chrono::milliseconds readTaskWakeTimeout = std::chrono::milliseconds(1000);
protected:
static int messageCallbackIDCounter;
std::mutex messageCallbacksLock;

View File

@ -24,9 +24,11 @@ public:
virtual bool isOpen() = 0;
virtual void modeChangeIncoming() {}
virtual void awaitModeChangeComplete() {}
virtual bool isDisconnected() { return disconnected; };
virtual bool close() = 0;
inline bool isDisconnected() const { return disconnected; };
inline bool isClosing() const { return closing; }
bool waitForRx(size_t limit, std::chrono::milliseconds timeout);
bool waitForRx(std::function<bool()> predicate, std::chrono::milliseconds timeout);
bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
@ -52,8 +54,8 @@ protected:
WAIT
};
virtual void readTask() = 0;
virtual void writeTask() = 0;
inline void setIsClosing(bool isClosing) { closing = isClosing; }
inline void setIsDisconnected(bool isDisconnected) { disconnected = isDisconnected; }
// Overridable in case the driver doesn't want to use writeTask and writeQueue
virtual bool writeQueueFull() { return writeQueue.size_approx() > writeQueueSize; }
@ -61,13 +63,15 @@ protected:
virtual bool writeInternal(const std::vector<uint8_t>& b) { return writeQueue.enqueue(WriteOperation(b)); }
bool pushRx(const uint8_t* buf, size_t numReceived);
RingBuffer readBuffer = RingBuffer(ICSNEO_DRIVER_RINGBUFFER_SIZE);
std::atomic<bool> hasRxWaitRequest = false;
std::condition_variable rxWaitRequestCv;
std::mutex rxWaitMutex;
void clearBuffers();
moodycamel::BlockingConcurrentQueue<WriteOperation> writeQueue;
std::thread readThread, writeThread;
private:
RingBuffer readBuffer = RingBuffer(ICSNEO_DRIVER_RINGBUFFER_SIZE);
std::condition_variable rxWaitCv;
std::mutex rxWaitMutex;
std::atomic<bool> closing{false};
std::atomic<bool> disconnected{false};
};

View File

@ -0,0 +1,80 @@
#ifndef __APPERRORMESSAGE_H_
#define __APPERRORMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
#include <unordered_set>
#include <memory>
#include "icsneo/api/eventmanager.h"
namespace icsneo {
enum class AppErrorType : uint16_t {
AppErrorRxMessagesFull = 0,
AppErrorTxMessagesFull = 1,
AppErrorTxReportMessagesFull = 2,
AppErrorBadCommWithDspIC = 3,
AppErrorDriverOverflow = 4,
AppErrorPCBuffOverflow = 5,
AppErrorPCChksumError = 6,
AppErrorPCMissedByte = 7,
AppErrorPCOverrunError = 8,
AppErrorSettingFailure = 9,
AppErrorTooManySelectedNetworks = 10,
AppErrorNetworkNotEnabled = 11,
AppErrorRtcNotCorrect = 12,
AppErrorLoadedDefaultSettings = 13,
AppErrorFeatureNotUnlocked = 14,
AppErrorFeatureRtcCmdDropped = 15,
AppErrorTxMessagesFlushed = 16,
AppErrorTxMessagesHalfFull = 17,
AppErrorNetworkNotValid = 18,
AppErrorTxInterfaceNotImplemented = 19,
AppErrorTxMessagesCommEnableIsOff = 20,
AppErrorRxFilterMatchCountExceeded = 21,
AppErrorEthPreemptionNotEnabled = 22,
AppErrorTxNotSupportedInMode = 23,
AppErrorJumboFramesNotSupported = 24,
AppErrorEthernetIpFragment = 25,
AppErrorTxMessagesUnderrun = 26,
AppErrorDeviceFanFailure = 27,
AppErrorDeviceOvertemperature = 28,
AppErrorTxMessageIndexOutOfRange = 29,
AppErrorUndersizedFrameDropped = 30,
AppErrorOversizedFrameDropped = 31,
AppErrorWatchdogEvent = 32,
AppErrorSystemClockFailure = 33,
AppErrorSystemClockRecovered = 34,
AppErrorSystemPeripheralReset = 35,
AppErrorSystemCommunicationFailure = 36,
AppErrorTxMessagesUnsupportedSourceOrPacketId = 37,
AppErrorWbmsManagerConnectFailed = 38,
AppErrorWbmsManagerConnectBadState = 39,
AppErrorWbmsManagerConnectTimeout = 40,
AppErrorFailedToInitializeLoggerDisk = 41,
AppErrorInvalidSetting = 42,
AppErrorSystemFailureRequestedReset = 43,
AppErrorPortKeyMistmatch = 45,
AppErrorErrorBufferOverflow = 254,
AppNoError = 255
};
class AppErrorMessage : public RawMessage {
public:
AppErrorMessage() : RawMessage(Message::Type::AppError, Network::NetID::RED_App_Error) {}
uint16_t errorType;
Network::NetID errorNetID;
uint32_t timestamp10us;
uint32_t timestamp10usMSB;
static std::shared_ptr<Message> DecodeToMessage(const std::vector<uint8_t>& bytestream, const device_eventhandler_t& report);
AppErrorType getAppErrorType();
std::string getAppErrorString();
};
} // namespace icsneo
#endif // __cplusplus
#endif

View File

@ -0,0 +1,20 @@
#ifndef __FLASHMEMORYMESSAGE_H_
#define __FLASHMEMORYMESSAGE_H_
#ifdef __cplusplus
#include "icsneo/communication/message/message.h"
namespace icsneo {
class FlashMemoryMessage : public RawMessage {
public:
FlashMemoryMessage() : RawMessage(Message::Type::RawMessage, Network::NetID::RED_INT_MEMORYREAD) {}
uint16_t startAddress = 0;
};
}
#endif // __cplusplus
#endif

View File

@ -40,6 +40,7 @@ public:
LiveData = 0x800f,
HardwareInfo = 0x8010,
TC10Status = 0x8011,
AppError = 0x8012,
};
Message(Type t) : type(t) {}

View File

@ -533,10 +533,12 @@ public:
case NetID::CoreMiniPreLoad:
case NetID::ExtendedCommand:
case NetID::ExtendedData:
case NetID::RED_INT_MEMORYREAD:
case NetID::NeoMemorySDRead:
case NetID::NeoMemoryWriteDone:
case NetID::RED_GET_RTC:
case NetID::DiskData:
case NetID::RED_App_Error:
return Type::Internal;
case NetID::Invalid:
case NetID::Any:

View File

@ -0,0 +1,28 @@
#ifndef __COREMINI_H_
#define __COREMINI_H_
#ifdef __cplusplus
#include <cstdint>
#include <array>
#include <chrono>
namespace icsneo {
struct CoreminiHeader {
uint16_t coreminiVersion;
uint32_t storedFileSize;
// 32-bit word checksum on the entire (decompressed) binary, with the checksum and hash fields set to 0
uint32_t fileChecksum;
// SHA256 hash of the entire (decompressed) binary, with the checksum, hash, and create time fields set to 0
bool skipDecompression;
bool encryptedMode;
std::array<uint8_t, 32> fileHash;
std::chrono::time_point<std::chrono::system_clock> timestamp;
};
}
#endif // __cplusplus
#endif

View File

@ -13,6 +13,7 @@
#include <optional>
#include <unordered_map>
#include <set>
#include <chrono>
#include "icsneo/api/eventmanager.h"
#include "icsneo/api/lifetime.h"
#include "icsneo/device/neodevice.h"
@ -21,6 +22,7 @@
#include "icsneo/device/devicetype.h"
#include "icsneo/device/deviceversion.h"
#include "icsneo/device/founddevice.h"
#include "icsneo/device/coremini.h"
#include "icsneo/disk/diskreaddriver.h"
#include "icsneo/disk/diskwritedriver.h"
#include "icsneo/disk/nulldiskdriver.h"
@ -163,6 +165,7 @@ public:
bool stopScript();
bool clearScript(Disk::MemoryType memType = Disk::MemoryType::SD);
bool uploadCoremini(std::istream& stream, Disk::MemoryType memType = Disk::MemoryType::SD);
std::optional<CoreminiHeader> readCoreminiHeader(Disk::MemoryType memType = Disk::MemoryType::SD);
bool eraseScriptMemory(Disk::MemoryType memType, uint64_t amount);

View File

@ -27,6 +27,8 @@ public:
return supportedNetworks;
}
bool supportsTC10() const override { return true; }
protected:
RADComet2(neodevice_t neodevice, const driver_factory_t& makeDriver) : RADCometBase(neodevice) {
initialize<RADCometSettings>(makeDriver);

View File

@ -44,6 +44,8 @@ public:
bool getEthPhyRegControlSupported() const override { return true; }
bool supportsTC10() const override { return true; }
protected:
RADComet3(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) {
initialize<RADComet3Settings>(makeDriver);

View File

@ -22,8 +22,10 @@ public:
private:
neodevice_t& device;
std::optional<void*> handle;
void readTask() override;
void writeTask() override;
std::thread readThread, writeThread;
void readTask();
void writeTask();
};
}

View File

@ -47,8 +47,10 @@ private:
static std::string HandleToTTY(neodevice_handle_t handle);
void readTask() override;
void writeTask() override;
std::thread readThread, writeThread;
void readTask();
void writeTask();
bool fdIsValid();
};

View File

@ -25,8 +25,11 @@ public:
bool isOpen() override;
bool close() override;
private:
void readTask() override;
void writeTask() override;
std::thread readThread, writeThread;
void readTask();
void writeTask();
bool writeQueueFull() override;
bool writeQueueAlmostFull() override;
bool writeInternal(const std::vector<uint8_t>& bytes) override;

View File

@ -57,8 +57,11 @@ private:
static std::vector<std::string> handles;
static bool ErrorIsDisconnection(int errorCode);
std::thread readThread, writeThread;
void readTask();
void writeTask();
bool openable; // Set to false in the constructor if the object has not been found in searchResultDevices
neodevice_t& device;

View File

@ -30,8 +30,10 @@ private:
uint8_t deviceMAC[6];
bool openable = true;
EthernetPacketizer ethPacketizer;
void readTask() override;
void writeTask() override;
std::thread readThread, writeThread;
void readTask();
void writeTask();
class NetworkInterface {
public:

View File

@ -38,6 +38,7 @@ private:
~Socket();
explicit operator bool() const { return fd != -1; }
operator SocketFileDescriptor() const { return fd; }
void poll(uint16_t event, uint32_t msTimeout);
private:
SocketFileDescriptor fd;
};
@ -46,8 +47,10 @@ private:
uint32_t dstIP;
uint16_t dstPort;
std::unique_ptr<Socket> socket;
void readTask() override;
void writeTask() override;
std::thread readThread, writeThread;
void readTask();
void writeTask();
};
}

View File

@ -32,6 +32,7 @@ private:
bool openable = true;
EthernetPacketizer ethPacketizer;
std::thread readThread, writeThread;
std::thread transmitThread;
pcap_send_queue* transmitQueue = nullptr;
std::condition_variable transmitQueueCV;

View File

@ -37,6 +37,7 @@ private:
std::shared_ptr<Detail> detail;
std::vector<std::shared_ptr<std::thread>> threads;
std::thread readThread, writeThread;
void readTask();
void writeTask();
};

View File

@ -64,7 +64,7 @@ bool FTD3XX::open() {
}
handle.emplace(tmpHandle);
closing = false;
setIsClosing(false);
readThread = std::thread(&FTD3XX::readTask, this);
writeThread = std::thread(&FTD3XX::writeTask, this);
@ -81,23 +81,21 @@ bool FTD3XX::close() {
return false;
}
closing = true;
disconnected = false;
setIsClosing(true);
setIsDisconnected(false);
if(readThread.joinable())
readThread.join();
if(writeThread.joinable())
writeThread.join();
WriteOperation flushop;
readBuffer.pop(readBuffer.size());
while(writeQueue.try_dequeue(flushop)) {}
clearBuffers();
if(const auto ret = FT_Close(*handle); ret != FT_OK) {
addEvent(ret, APIEvent::Severity::EventWarning);
}
closing = false;
setIsClosing(false);
return true;
}
@ -110,7 +108,7 @@ void FTD3XX::readTask() {
FT_SetStreamPipe(*handle, false, false, READ_PIPE_ID, bufferSize);
FT_SetPipeTimeout(*handle, READ_PIPE_ID, 1);
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
ULONG received = 0;
OVERLAPPED overlap = {};
FT_InitializeOverlapped(*handle, &overlap);
@ -119,13 +117,13 @@ void FTD3XX::readTask() {
#else
FT_ReadPipeAsync(*handle, 0, buffer, bufferSize, &received, &overlap);
#endif
while(!closing) {
while(!isClosing()) {
const auto ret = FT_GetOverlappedResult(*handle, &overlap, &received, true);
if(ret == FT_IO_PENDING)
continue;
if(ret != FT_OK) {
if(ret == FT_IO_ERROR) {
disconnected = true;
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
} else {
addEvent(ret, APIEvent::Severity::Error);
@ -146,7 +144,7 @@ void FTD3XX::writeTask() {
FT_SetPipeTimeout(*handle, WRITE_PIPE_ID, 100);
WriteOperation writeOp;
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
@ -160,13 +158,13 @@ void FTD3XX::writeTask() {
#else
FT_WritePipeAsync(*handle, 0, writeOp.bytes.data(), size, &sent, &overlap);
#endif
while(!closing) {
while(!isClosing()) {
const auto ret = FT_GetOverlappedResult(*handle, &overlap, &sent, true);
if(ret == FT_IO_PENDING)
continue;
if(ret != FT_OK) {
if(ret == FT_IO_ERROR) {
disconnected = true;
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
} else {
addEvent(ret, APIEvent::Severity::Error);

View File

@ -117,7 +117,7 @@ bool CDCACM::close() {
return false;
}
closing = true;
setIsClosing(true);
if(readThread.joinable())
readThread.join();
@ -125,8 +125,8 @@ bool CDCACM::close() {
if(writeThread.joinable())
writeThread.join();
closing = false;
disconnected = false;
setIsClosing(false);
setIsDisconnected(false);
if(modeChanging) {
// We're expecting this inode to go away after we close the device
@ -140,9 +140,7 @@ bool CDCACM::close() {
int ret = ::close(fd);
fd = -1;
WriteOperation flushop;
readBuffer.clear();
while (writeQueue.try_dequeue(flushop)) {}
clearBuffers();
if(modeChanging) {
modeChanging = false;
@ -173,7 +171,7 @@ void CDCACM::readTask() {
constexpr size_t READ_BUFFER_SIZE = 2048;
uint8_t readbuf[READ_BUFFER_SIZE];
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
fd_set rfds = {0};
struct timeval tv = {0};
FD_SET(fd, &rfds);
@ -199,8 +197,8 @@ void CDCACM::readTask() {
// Requesting thread is responsible for calling close. This allows for more flexibility
});
break;
} else if(!closing && !fdIsValid() && !isDisconnected()) {
disconnected = true;
} else if(!isClosing() && !fdIsValid() && !isDisconnected()) {
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
}
@ -210,7 +208,7 @@ void CDCACM::readTask() {
void CDCACM::writeTask() {
WriteOperation writeOp;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
@ -233,7 +231,7 @@ void CDCACM::writeTask() {
} else if (actualWritten < 0) {
if(!fdIsValid()) {
if(!isDisconnected()) {
disconnected = true;
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
} else

View File

@ -166,13 +166,13 @@ bool FirmIO::close() {
return false;
}
closing = true;
setIsClosing(true);
if(readThread.joinable())
readThread.join();
closing = false;
disconnected = false;
setIsClosing(false);
setIsDisconnected(false);
int ret = 0;
if(vbase != nullptr) {
@ -202,7 +202,7 @@ void FirmIO::readTask() {
std::cerr << "FirmIO::readTask setpriority failed : " << strerror(errno) << std::endl;
}
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
fd_set rfds = {0};
struct timeval tv = {0};
FD_SET(fd, &rfds);
@ -244,7 +244,7 @@ void FirmIO::readTask() {
uint8_t* addr = reinterpret_cast<uint8_t*>(msg.payload.data.addr - PHY_ADDR_BASE + vbase);
while (!pushRx(addr, msg.payload.data.len)) {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // back-off so reading thread can empty the buffer
if (closing || isDisconnected()) {
if (isClosing() || isDisconnected()) {
break;
}
}

View File

@ -81,7 +81,7 @@ bool FTDI::open() {
ftdi.flush();
// Create threads
closing = false;
setIsClosing(false);
readThread = std::thread(&FTDI::readTask, this);
writeThread = std::thread(&FTDI::writeTask, this);
@ -94,7 +94,7 @@ bool FTDI::close() {
return false;
}
closing = true;
setIsClosing(true);
if(readThread.joinable())
readThread.join();
@ -109,12 +109,10 @@ bool FTDI::close() {
report(APIEvent::Type::DriverFailedToClose, APIEvent::Severity::Error);
}
WriteOperation flushop;
readBuffer.clear();
while(writeQueue.try_dequeue(flushop)) {}
clearBuffers();
closing = false;
disconnected = false;
setIsClosing(false);
setIsDisconnected(false);
return ret;
}
@ -202,12 +200,12 @@ void FTDI::readTask() {
constexpr size_t READ_BUFFER_SIZE = 8;
uint8_t readbuf[READ_BUFFER_SIZE];
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
auto readBytes = ftdi.read(readbuf, READ_BUFFER_SIZE);
if(readBytes < 0) {
if(ErrorIsDisconnection(readBytes)) {
if(!isDisconnected()) {
disconnected = true;
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
} else
@ -220,7 +218,7 @@ void FTDI::readTask() {
void FTDI::writeTask() {
WriteOperation writeOp;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
@ -230,7 +228,7 @@ void FTDI::writeTask() {
if(writeBytes < 0) {
if(ErrorIsDisconnection(writeBytes)) {
if(!isDisconnected()) {
disconnected = true;
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
break;

View File

@ -258,28 +258,26 @@ bool PCAP::close() {
if(!isOpen())
return false;
closing = true; // Signal the threads that we are closing
setIsClosing(true); // Signal the threads that we are closing
pcap_breakloop(iface.fp);
#ifndef __linux__
pthread_cancel(readThread.native_handle());
#endif
readThread.join();
writeThread.join();
closing = false;
setIsClosing(false);
pcap_close(iface.fp);
iface.fp = nullptr;
WriteOperation flushop;
readBuffer.clear();
while(writeQueue.try_dequeue(flushop)) {}
clearBuffers();
return true;
}
void PCAP::readTask() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while (!closing) {
while (!isClosing()) {
pcap_dispatch(iface.fp, -1, [](uint8_t* obj, const struct pcap_pkthdr* header, const uint8_t* data) {
PCAP* driver = reinterpret_cast<PCAP*>(obj);
if(driver->ethPacketizer.inputUp({data, data + header->caplen})) {
@ -294,7 +292,7 @@ void PCAP::writeTask() {
WriteOperation writeOp;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing) {
while(!isClosing()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;

View File

@ -6,6 +6,7 @@
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <poll.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <net/if.h>
@ -72,6 +73,20 @@ TCP::Socket::~Socket() {
#endif
}
void TCP::Socket::poll(uint16_t event, uint32_t msTimeout) {
#ifdef _WIN32
WSAPOLLFD pfd;
pfd.fd = fd;
pfd.events = event;
::WSAPoll(&pfd, 1, msTimeout);
#else
struct pollfd pfd;
pfd.fd = fd;
pfd.events = event;
::poll(&pfd, 1, msTimeout);
#endif
}
void TCP::Find(std::vector<FoundDevice>& found) {
static const auto MDNS_PORT = htons((unsigned short)5353);
static const auto MDNS_IP = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U));
@ -256,16 +271,16 @@ void TCP::Find(std::vector<FoundDevice>& found) {
continue;
}
timeval timeout = {};
timeout.tv_usec = 50000;
fd_set readfs;
FD_ZERO(&readfs);
int nfds = WIN_INT(socket) + 1;
FD_SET(socket, &readfs);
while(true) {
const auto rxTill = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(std::chrono::steady_clock::now() < rxTill) {
static constexpr size_t bufferLen = 2048;
uint8_t buffer[bufferLen];
::select(nfds, &readfs, 0, 0, &timeout); // timeout is intentially not reset, we want timeout.tv_usec _total_
// keep trying till the timeout
const auto msWait = std::chrono::duration_cast<std::chrono::milliseconds>(rxTill - std::chrono::steady_clock::now()).count();
if(msWait < 0) {
break;
}
socket.poll(POLLIN, static_cast<uint32_t>(msWait));
const auto recvRet = ::recv(socket, (char*)buffer, bufferLen, 0);
static constexpr auto headerLength = 12;
if(recvRet < headerLength) {
@ -460,13 +475,7 @@ bool TCP::open() {
}
#endif
timeval timeout = {};
timeout.tv_sec = 1;
fd_set writefs;
FD_ZERO(&writefs);
int nfds = WIN_INT(*partiallyOpenSocket) + 1;
FD_SET(*partiallyOpenSocket, &writefs);
::select(nfds, 0, &writefs, 0, &timeout);
partiallyOpenSocket->poll(POLLOUT, 1000);
if(::connect(*partiallyOpenSocket, (sockaddr*)&addr, sizeof(addr)) < 0) {
#ifdef _WIN32
@ -502,20 +511,18 @@ bool TCP::close() {
return false;
}
closing = true;
disconnected = false;
setIsClosing(true);
setIsDisconnected(false);
if(readThread.joinable())
readThread.join();
if(writeThread.joinable())
writeThread.join();
WriteOperation flushop;
readBuffer.pop(readBuffer.size());
while(writeQueue.try_dequeue(flushop)) {}
clearBuffers();
socket.reset();
closing = false;
setIsClosing(false);
return true;
}
@ -523,21 +530,13 @@ bool TCP::close() {
void TCP::readTask() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
const int nfds = WIN_INT(*socket) + 1;
fd_set readfs;
FD_ZERO(&readfs);
FD_SET(*socket, &readfs);
timeval timeout;
constexpr size_t READ_BUFFER_SIZE = 2048;
uint8_t readbuf[READ_BUFFER_SIZE];
while(!closing) {
while(!isClosing()) {
if(const auto received = ::recv(*socket, (char*)readbuf, READ_BUFFER_SIZE, 0); received > 0) {
pushRx(readbuf, received);
} else {
timeout.tv_sec = 0;
timeout.tv_usec = 50'000;
::select(nfds, &readfs, 0, 0, &timeout);
socket->poll(POLLIN, 100);
}
}
}
@ -545,23 +544,15 @@ void TCP::readTask() {
void TCP::writeTask() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
const int nfds = WIN_INT(*socket) + 1;
fd_set writefs;
FD_ZERO(&writefs);
FD_SET(*socket, &writefs);
timeval timeout;
WriteOperation writeOp;
while(!closing) {
while(!isClosing()) {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
continue;
while(!closing) {
while(!isClosing()) {
if(::send(*socket, (char*)writeOp.bytes.data(), WIN_INT(writeOp.bytes.size()), 0) > 0)
break;
timeout.tv_sec = 0;
timeout.tv_usec = 100'000;
::select(nfds, 0, &writefs, 0, &timeout);
socket->poll(POLLOUT, 100);
}
}
}

View File

@ -246,18 +246,17 @@ bool PCAP::close() {
return false;
}
closing = true; // Signal the threads that we are closing
setIsClosing(true); // Signal the threads that we are closing
readThread.join();
writeThread.join();
transmitThread.join();
closing = false;
setIsClosing(false);
pcap.close(iface.fp);
iface.fp = nullptr;
WriteOperation flushop;
readBuffer.clear();
while(writeQueue.try_dequeue(flushop)) {}
clearBuffers();
transmitQueue = nullptr;
return true;
@ -267,7 +266,7 @@ void PCAP::readTask() {
struct pcap_pkthdr* header;
const uint8_t* data;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing) {
while(!isClosing()) {
auto readBytes = pcap.next_ex(iface.fp, &header, &data);
if(readBytes < 0) {
report(APIEvent::Type::FailedToRead, APIEvent::Severity::Error);
@ -291,7 +290,7 @@ void PCAP::writeTask() {
pcap_send_queue* queue2 = pcap.sendqueue_alloc(128000);
pcap_send_queue* queue = queue1;
while(!closing) {
while(!isClosing()) {
// Potentially, we added frames to a second queue faster than the other thread was able to hand the first
// off to the kernel. In that case, wait for a minimal amount of time before checking whether we can
// transmit it again.
@ -342,9 +341,9 @@ void PCAP::writeTask() {
}
void PCAP::transmitTask() {
while(!closing) {
while(!isClosing()) {
std::unique_lock<std::mutex> lk(transmitQueueMutex);
if(transmitQueueCV.wait_for(lk, std::chrono::milliseconds(100), [this] { return !!transmitQueue; }) && !closing && transmitQueue) {
if(transmitQueueCV.wait_for(lk, std::chrono::milliseconds(100), [this] { return !!transmitQueue; }) && !isClosing() && transmitQueue) {
pcap_send_queue* current = transmitQueue;
lk.unlock();
pcap.sendqueue_transmit(iface.fp, current, 0);

View File

@ -326,12 +326,12 @@ bool VCP::close() {
return false;
}
closing = true; // Signal the threads that we are closing
setIsClosing(true); // Signal the threads that we are closing
for(auto& t : threads)
t->join(); // Wait for the threads to close
readThread.join();
writeThread.join();
closing = false;
setIsClosing(false);
if(!CloseHandle(detail->handle)) {
report(APIEvent::Type::DriverFailedToClose, APIEvent::Severity::Error);
@ -357,9 +357,7 @@ bool VCP::close() {
detail->overlappedWait.hEvent = INVALID_HANDLE_VALUE;
}
WriteOperation flushop;
readBuffer.clear();
while(writeQueue.try_dequeue(flushop)) {}
clearBuffers();
if(!ret)
report(APIEvent::Type::DriverFailedToClose, APIEvent::Severity::Error);
@ -379,7 +377,7 @@ void VCP::readTask() {
IOTaskState state = LAUNCH;
DWORD bytesRead = 0;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
switch(state) {
case LAUNCH: {
COMSTAT comStatus;
@ -401,7 +399,7 @@ void VCP::readTask() {
else if(lastError != ERROR_SUCCESS) {
if(lastError == ERROR_ACCESS_DENIED) {
if(!isDisconnected()) {
disconnected = true;
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
} else
@ -432,7 +430,7 @@ void VCP::writeTask() {
VCP::WriteOperation writeOp;
DWORD bytesWritten = 0;
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing && !isDisconnected()) {
while(!isClosing() && !isDisconnected()) {
switch(state) {
case LAUNCH: {
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
@ -448,7 +446,7 @@ void VCP::writeTask() {
}
else if(winerr == ERROR_ACCESS_DENIED) {
if(!isDisconnected()) {
disconnected = true;
setIsDisconnected(true);
report(APIEvent::Type::DeviceDisconnected, APIEvent::Severity::Error);
}
} else

View File

@ -0,0 +1,101 @@
#include "icsneo/icsneocpp.h"
#include "icsneo/communication/encoder.h"
#include "icsneo/communication/message/apperrormessage.h"
#include "icsneo/communication/packetizer.h"
#include "icsneo/device/tree/neovired2/neovired2.h"
#include "icsneo/api/eventmanager.h"
#include "gtest/gtest.h"
#include <vector>
#include <iostream>
using namespace icsneo;
class REDAppErrorDecoderTest : public ::testing::Test {
protected:
void SetUp() override {
report = [](APIEvent::Type, APIEvent::Severity) {
// Unless caught by the test, the packetizer should not throw errors
EXPECT_TRUE(false);
};
packetizer.emplace([this](APIEvent::Type t, APIEvent::Severity s) { report(t, s); });
packetEncoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) { report(t, s); });
packetDecoder.emplace([this](APIEvent::Type t, APIEvent::Severity s) { report(t, s); });
}
device_eventhandler_t report;
std::optional<Encoder> packetEncoder;
std::optional<Packetizer> packetizer;
std::optional<Decoder> packetDecoder;
RingBuffer ringBuffer = RingBuffer(128);
std::vector<uint8_t> testErrorData =
{0xaa, 0x0c,
0x12, 0x00, //size
0x34, 0x00, //netID
0x0b, 0x00, //error_type
0x01, 0x00, //network_id
0x33, 0x44, 0x55, 0x66, //uiTime10us
0x77, 0x88, 0x99, 0xAA, //uiTime10usMSB
};
};
TEST_F(REDAppErrorDecoderTest, PacketDecoderTest) {
std::shared_ptr<icsneo::Message> decodeMsg;
auto msg1 = std::make_shared<icsneo::AppErrorMessage>();
msg1->errorType = static_cast<uint16_t>(AppErrorType::AppErrorNetworkNotEnabled);
msg1->errorNetID = Network::NetID::HSCAN;
msg1->timestamp10us = 0x66554433;
msg1->timestamp10usMSB = 0xAA998877;
msg1->network = icsneo::Network::NetID::RED_App_Error;
ringBuffer.clear();
ringBuffer.write(testErrorData);
EXPECT_TRUE(packetizer->input(ringBuffer));
auto packets = packetizer->output();
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
EXPECT_NE(decodeMsg, nullptr);
auto testMessage = std::dynamic_pointer_cast<icsneo::AppErrorMessage>(decodeMsg);
EXPECT_EQ(msg1->network, testMessage->network);
EXPECT_EQ(msg1->errorType, testMessage->errorType);
EXPECT_EQ(msg1->errorNetID, testMessage->errorNetID);
EXPECT_EQ(msg1->timestamp10us, testMessage->timestamp10us);
EXPECT_EQ(msg1->timestamp10usMSB, testMessage->timestamp10usMSB);
packets.pop_back();
}
TEST_F(REDAppErrorDecoderTest, GetErrorStringTest) {
std::shared_ptr<icsneo::Message> decodeMsg;
ringBuffer.clear();
ringBuffer.write(testErrorData);
EXPECT_TRUE(packetizer->input(ringBuffer));
auto packets = packetizer->output();
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
EXPECT_NE(decodeMsg, nullptr);
auto testMessage = std::dynamic_pointer_cast<icsneo::AppErrorMessage>(decodeMsg);
EXPECT_EQ("HSCAN: Network not enabled", testMessage->getAppErrorString());
packets.pop_back();
}
TEST_F(REDAppErrorDecoderTest, GetTypeTest) {
std::shared_ptr<icsneo::Message> decodeMsg;
ringBuffer.clear();
ringBuffer.write(testErrorData);
EXPECT_TRUE(packetizer->input(ringBuffer));
auto packets = packetizer->output();
EXPECT_TRUE(packetDecoder->decode(decodeMsg, packets.back()));
EXPECT_NE(decodeMsg, nullptr);
auto testMessage = std::dynamic_pointer_cast<icsneo::AppErrorMessage>(decodeMsg);
EXPECT_EQ(AppErrorType::AppErrorNetworkNotEnabled, testMessage->getAppErrorType());
packets.pop_back();
}