Compare commits
10 Commits
7debea2611
...
72eadcc05a
| Author | SHA1 | Date |
|---|---|---|
|
|
72eadcc05a | |
|
|
ca370a1310 | |
|
|
564933cb41 | |
|
|
cac8d760b0 | |
|
|
75af3220b0 | |
|
|
f25a0a4a81 | |
|
|
387c39d3a0 | |
|
|
4476bd8b71 | |
|
|
14588591e5 | |
|
|
cbcbcbdc5d |
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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([¤tSector, &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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
add_executable(libicsneocpp-app-error src/AppErrorExample.cpp)
|
||||
target_link_libraries(libicsneocpp-app-error icsneocpp)
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "icsneo/icsneocpp.h"
|
||||
#include "icsneo/communication/message/apperrormessage.h"
|
||||
#include "icsneo/communication/message/message.h"
|
||||
/*
|
||||
* App errors are responses from the device indicating internal runtime errors
|
||||
* NOTE: To trigger the app error in this example, disable the HSCAN network on the device
|
||||
* (e.g. with neoVI Explorer)
|
||||
*/
|
||||
int main() {
|
||||
std::cout << "Running libicsneo " << icsneo::GetVersion() << std::endl;
|
||||
std::cout << "\nFinding devices... " << std::flush;
|
||||
auto devices = icsneo::FindAllDevices();
|
||||
std::cout << "OK, " << devices.size() << " device" << (devices.size() == 1 ? "" : "s") << " found" << std::endl;
|
||||
|
||||
// List off the devices
|
||||
for(auto& device : devices)
|
||||
std::cout << '\t' << device->describe() << " @ Handle " << device->getNeoDevice().handle << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
for(auto device : devices) {
|
||||
std::cout << "Connecting to " << device->describe() << "... ";
|
||||
bool ret = device->open();
|
||||
if(!ret) { // Failed to open
|
||||
std::cout << "FAIL" << std::endl;
|
||||
std::cout << icsneo::GetLastError() << std::endl << std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl << std::endl;
|
||||
|
||||
// Create an app error message filter, including "internal" messages
|
||||
auto filter = std::make_shared<icsneo::MessageFilter>(icsneo::Message::Type::AppError);
|
||||
filter->includeInternalInAny = true;
|
||||
|
||||
// ...and register a callback with it.
|
||||
// Add your error handling here
|
||||
auto handler = device->addMessageCallback(std::make_shared<icsneo::MessageCallback>(filter, [](std::shared_ptr<icsneo::Message> message) {
|
||||
auto msg = std::static_pointer_cast<icsneo::AppErrorMessage>(message);
|
||||
if(icsneo::Network::NetID::RED_App_Error == msg->network.getNetID()) {
|
||||
std::cout << std::endl << "App error reported:" << std::endl;
|
||||
std::cout << msg->getAppErrorString() << std::endl << std::endl;
|
||||
}
|
||||
}));
|
||||
|
||||
std::cout << "Going online... ";
|
||||
ret = device->goOnline();
|
||||
if(!ret) {
|
||||
std::cout << "FAIL" << std::endl;
|
||||
device->close();
|
||||
continue;
|
||||
}
|
||||
std::cout << "OK" << std::endl;
|
||||
|
||||
// Prepare a CAN message
|
||||
std::cout << std::endl << "Transmitting a CAN frame... ";
|
||||
auto txMessage = std::make_shared<icsneo::CANMessage>();
|
||||
txMessage->network = icsneo::Network::NetID::HSCAN;
|
||||
txMessage->arbid = 0x22;
|
||||
txMessage->data.insert(txMessage->data.end(), {0xaa, 0xbb, 0xcc});
|
||||
// The DLC will come from the length of the data vector
|
||||
txMessage->isExtended = false;
|
||||
txMessage->isCANFD = false;
|
||||
|
||||
// Transmit a CAN message on HSCAN, even though HSCAN is disabled on the device!
|
||||
// Expect to see an app error caught in the callback defined above
|
||||
ret = device->transmit(txMessage);
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
|
||||
// Go offline, stop sending and receiving traffic
|
||||
device->removeMessageCallback(handler);
|
||||
std::cout << "Going offline... ";
|
||||
ret = device->goOffline();
|
||||
std::cout << (ret ? "OK" : "FAIL") << std::endl;
|
||||
std::cout << "Disconnecting... ";
|
||||
ret = device->close();
|
||||
std::cout << (ret ? "OK\n" : "FAIL\n") << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -108,6 +108,7 @@ public:
|
|||
LiveDataNotSupported = 0x2052,
|
||||
LINSettingsNotAvailable = 0x2053,
|
||||
ModeNotFound = 0x2054,
|
||||
AppErrorParsingFailed = 0x2055,
|
||||
|
||||
// Transport Events
|
||||
FailedToRead = 0x3000,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -40,6 +40,7 @@ public:
|
|||
LiveData = 0x800f,
|
||||
HardwareInfo = 0x8010,
|
||||
TC10Status = 0x8011,
|
||||
AppError = 0x8012,
|
||||
};
|
||||
|
||||
Message(Type t) : type(t) {}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
Loading…
Reference in New Issue