mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-30 21:02:08 +02:00
Device: Improve TC10 message filtering
This commit is contained in:
committed by
Kyle Schwarz
parent
4c0781190e
commit
7ef57e6737
@@ -575,6 +575,7 @@ if(LIBICSNEO_BUILD_UNIT_TESTS)
|
|||||||
test/unit/apperrordecodertest.cpp
|
test/unit/apperrordecodertest.cpp
|
||||||
test/unit/icsneoc2.cpp
|
test/unit/icsneoc2.cpp
|
||||||
test/unit/ethphyregistertest.cpp
|
test/unit/ethphyregistertest.cpp
|
||||||
|
test/unit/tc10test.cpp
|
||||||
test/unit/windowsstrings.cpp
|
test/unit/windowsstrings.cpp
|
||||||
test/unit/periodictest.cpp
|
test/unit/periodictest.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is
|
|||||||
static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device";
|
static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device";
|
||||||
static constexpr const char* DISK_FORMAT_NOT_SUPPORTED = "Disk formatting is not supported on this device.";
|
static constexpr const char* DISK_FORMAT_NOT_SUPPORTED = "Disk formatting is not supported on this device.";
|
||||||
static constexpr const char* DISK_FORMAT_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count.";
|
static constexpr const char* DISK_FORMAT_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count.";
|
||||||
|
static constexpr const char* TC10_REQUEST_FAILED = "The device rejected the TC10 request.";
|
||||||
|
|
||||||
// Transport Errors
|
// Transport Errors
|
||||||
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
|
||||||
@@ -381,6 +382,8 @@ const char* APIEvent::DescriptionForType(Type type) {
|
|||||||
return DISK_FORMAT_NOT_SUPPORTED;
|
return DISK_FORMAT_NOT_SUPPORTED;
|
||||||
case Type::DiskFormatInvalidCount:
|
case Type::DiskFormatInvalidCount:
|
||||||
return DISK_FORMAT_INVALID_COUNT;
|
return DISK_FORMAT_INVALID_COUNT;
|
||||||
|
case Type::TC10RequestFailed:
|
||||||
|
return TC10_REQUEST_FAILED;
|
||||||
|
|
||||||
// VSA
|
// VSA
|
||||||
case Type::VSABufferCorrupted:
|
case Type::VSABufferCorrupted:
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ void init_event(pybind11::module_& m) {
|
|||||||
.value("SettingNotAvaiableDevice", APIEvent::Type::SettingNotAvaiableDevice)
|
.value("SettingNotAvaiableDevice", APIEvent::Type::SettingNotAvaiableDevice)
|
||||||
.value("DiskFormatNotSupported", APIEvent::Type::DiskFormatNotSupported)
|
.value("DiskFormatNotSupported", APIEvent::Type::DiskFormatNotSupported)
|
||||||
.value("DiskFormatInvalidCount", APIEvent::Type::DiskFormatInvalidCount)
|
.value("DiskFormatInvalidCount", APIEvent::Type::DiskFormatInvalidCount)
|
||||||
|
.value("TC10RequestFailed", APIEvent::Type::TC10RequestFailed)
|
||||||
.value("FailedToRead", APIEvent::Type::FailedToRead)
|
.value("FailedToRead", APIEvent::Type::FailedToRead)
|
||||||
.value("FailedToWrite", APIEvent::Type::FailedToWrite)
|
.value("FailedToWrite", APIEvent::Type::FailedToWrite)
|
||||||
.value("DriverFailedToOpen", APIEvent::Type::DriverFailedToOpen)
|
.value("DriverFailedToOpen", APIEvent::Type::DriverFailedToOpen)
|
||||||
|
|||||||
+12
-4
@@ -3659,7 +3659,7 @@ bool Device::requestTC10Wake(Network::NetID network) {
|
|||||||
*(Network::NetID*)args.data() = network;
|
*(Network::NetID*)args.data() = network;
|
||||||
auto msg = com->waitForMessageSync([&] {
|
auto msg = com->waitForMessageSync([&] {
|
||||||
return com->sendCommand(ExtendedCommand::RequestTC10Wake, args);
|
return com->sendCommand(ExtendedCommand::RequestTC10Wake, args);
|
||||||
}, std::make_shared<MessageFilter>(Message::Type::ExtendedResponse), std::chrono::milliseconds(1000));
|
}, std::make_shared<ExtendedResponseFilter>(ExtendedCommand::RequestTC10Wake), std::chrono::milliseconds(1000));
|
||||||
|
|
||||||
if(!msg) {
|
if(!msg) {
|
||||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||||
@@ -3672,7 +3672,11 @@ bool Device::requestTC10Wake(Network::NetID network) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp->response == ExtendedResponse::OK;
|
if(resp->response != ExtendedResponse::OK) {
|
||||||
|
report(APIEvent::Type::TC10RequestFailed, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Device::requestTC10Sleep(Network::NetID network) {
|
bool Device::requestTC10Sleep(Network::NetID network) {
|
||||||
@@ -3684,7 +3688,7 @@ bool Device::requestTC10Sleep(Network::NetID network) {
|
|||||||
*(Network::NetID*)args.data() = network;
|
*(Network::NetID*)args.data() = network;
|
||||||
auto msg = com->waitForMessageSync([&] {
|
auto msg = com->waitForMessageSync([&] {
|
||||||
return com->sendCommand(ExtendedCommand::RequestTC10Sleep, args);
|
return com->sendCommand(ExtendedCommand::RequestTC10Sleep, args);
|
||||||
}, std::make_shared<MessageFilter>(Message::Type::ExtendedResponse), std::chrono::milliseconds(1000));
|
}, std::make_shared<ExtendedResponseFilter>(ExtendedCommand::RequestTC10Sleep), std::chrono::milliseconds(1000));
|
||||||
|
|
||||||
if(!msg) {
|
if(!msg) {
|
||||||
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error);
|
||||||
@@ -3697,7 +3701,11 @@ bool Device::requestTC10Sleep(Network::NetID network) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return typed->response == ExtendedResponse::OK;
|
if(typed->response != ExtendedResponse::OK) {
|
||||||
|
report(APIEvent::Type::TC10RequestFailed, APIEvent::Severity::Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Device::reboot(bool safe) {
|
bool Device::reboot(bool safe) {
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ public:
|
|||||||
SettingNotAvaiableDevice = 0x2057,
|
SettingNotAvaiableDevice = 0x2057,
|
||||||
DiskFormatNotSupported = 0x2058,
|
DiskFormatNotSupported = 0x2058,
|
||||||
DiskFormatInvalidCount = 0x2059,
|
DiskFormatInvalidCount = 0x2059,
|
||||||
|
TC10RequestFailed = 0x205A,
|
||||||
|
|
||||||
// Transport Events
|
// Transport Events
|
||||||
FailedToRead = 0x3000,
|
FailedToRead = 0x3000,
|
||||||
|
|||||||
@@ -0,0 +1,221 @@
|
|||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "icsneo/icsneocpp.h"
|
||||||
|
#include "icsneo/communication/driver.h"
|
||||||
|
#include "icsneo/communication/message/extendedresponsemessage.h"
|
||||||
|
#include "icsneo/communication/message/filter/extendedresponsefilter.h"
|
||||||
|
#include "icsneo/communication/message/tc10statusmessage.h"
|
||||||
|
#include "icsneo/device/tree/radmoon2/radmoon2.h"
|
||||||
|
#include "icsneo/device/tree/radmoon2/radmoon2zl.h"
|
||||||
|
|
||||||
|
using namespace icsneo;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TC10TestDriver : public Driver {
|
||||||
|
public:
|
||||||
|
explicit TC10TestDriver(const device_eventhandler_t& handler) : Driver(handler) {}
|
||||||
|
|
||||||
|
bool open() override { opened = true; return true; }
|
||||||
|
bool isOpen() override { return opened; }
|
||||||
|
bool close() override { opened = false; return true; }
|
||||||
|
driver_finder_t getFinder() override { return [](std::vector<FoundDevice>&) {}; }
|
||||||
|
|
||||||
|
int writes = 0;
|
||||||
|
// Replies pushed, in order, when the host writes a command.
|
||||||
|
std::vector<std::vector<uint8_t>> replies;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool writeInternal(const std::vector<uint8_t>&) override {
|
||||||
|
++writes;
|
||||||
|
for(const auto& reply : replies)
|
||||||
|
pushRx(reply.data(), reply.size());
|
||||||
|
replies.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool opened = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<uint8_t> longPacket(Network::NetID netid, const void* payload, size_t payloadSize) {
|
||||||
|
const uint16_t packetLength = static_cast<uint16_t>(6 + payloadSize);
|
||||||
|
std::vector<uint8_t> bytes(packetLength);
|
||||||
|
bytes[0] = 0xAA;
|
||||||
|
bytes[1] = 0x00;
|
||||||
|
bytes[2] = static_cast<uint8_t>(packetLength & 0xff);
|
||||||
|
bytes[3] = static_cast<uint8_t>((packetLength >> 8) & 0xff);
|
||||||
|
const auto id = static_cast<uint16_t>(netid);
|
||||||
|
bytes[4] = static_cast<uint8_t>(id & 0xff);
|
||||||
|
bytes[5] = static_cast<uint8_t>((id >> 8) & 0xff);
|
||||||
|
std::memcpy(bytes.data() + 6, payload, payloadSize);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> genericReturn(ExtendedCommand command, ExtendedResponse code) {
|
||||||
|
ExtendedResponseMessage::PackedGenericResponse packed{};
|
||||||
|
packed.header.command = ExtendedCommand::GenericReturn;
|
||||||
|
packed.header.length = static_cast<uint16_t>(sizeof(packed) - sizeof(packed.header));
|
||||||
|
packed.command = command;
|
||||||
|
packed.returnCode = code;
|
||||||
|
return longPacket(Network::NetID::ExtendedCommand, &packed, sizeof(packed));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extended body of a GetTC10Status reply. `tail` is extra payload past the two status bytes.
|
||||||
|
std::vector<uint8_t> statusBody(uint8_t wake, uint8_t sleep, std::initializer_list<uint8_t> tail = {}) {
|
||||||
|
const auto payloadLength = static_cast<uint16_t>(2 + tail.size());
|
||||||
|
std::vector<uint8_t> body = {
|
||||||
|
0x3f, 0x00,
|
||||||
|
static_cast<uint8_t>(payloadLength & 0xff),
|
||||||
|
static_cast<uint8_t>((payloadLength >> 8) & 0xff),
|
||||||
|
wake,
|
||||||
|
sleep,
|
||||||
|
};
|
||||||
|
body.insert(body.end(), tail);
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenTC10Device {
|
||||||
|
public:
|
||||||
|
TC10TestDriver* driver = nullptr;
|
||||||
|
std::shared_ptr<RADMoon2ZL> device;
|
||||||
|
|
||||||
|
OpenTC10Device() {
|
||||||
|
FoundDevice found;
|
||||||
|
std::memcpy(found.serial, "RN0001", 6);
|
||||||
|
found.makeDriver = [this](device_eventhandler_t handler, neodevice_t&) {
|
||||||
|
auto result = std::make_unique<TC10TestDriver>(handler);
|
||||||
|
driver = result.get();
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
device = std::make_shared<RADMoon2ZL>(found);
|
||||||
|
device->com->readTaskWakeTimeout = std::chrono::milliseconds(20);
|
||||||
|
EXPECT_TRUE(device->com->open());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FoundDevice moon2Found() {
|
||||||
|
FoundDevice found;
|
||||||
|
std::memcpy(found.serial, "RM0001", 6);
|
||||||
|
found.makeDriver = [](device_eventhandler_t handler, neodevice_t&) {
|
||||||
|
return std::make_unique<TC10TestDriver>(handler);
|
||||||
|
};
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10, WakeFilterMatchesOnlyThatCommand) {
|
||||||
|
ExtendedResponseFilter filter(ExtendedCommand::RequestTC10Wake);
|
||||||
|
EXPECT_TRUE(filter.match(std::make_shared<ExtendedResponseMessage>(
|
||||||
|
ExtendedCommand::RequestTC10Wake, ExtendedResponse::OperationFailed)));
|
||||||
|
EXPECT_FALSE(filter.match(std::make_shared<ExtendedResponseMessage>(
|
||||||
|
ExtendedCommand::RequestTC10Sleep, ExtendedResponse::OK)));
|
||||||
|
EXPECT_FALSE(filter.match(std::make_shared<ExtendedResponseMessage>(
|
||||||
|
ExtendedCommand::GetDiskDetails, ExtendedResponse::OK)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10, SleepFilterMatchesOnlyThatCommand) {
|
||||||
|
ExtendedResponseFilter filter(ExtendedCommand::RequestTC10Sleep);
|
||||||
|
EXPECT_TRUE(filter.match(std::make_shared<ExtendedResponseMessage>(
|
||||||
|
ExtendedCommand::RequestTC10Sleep, ExtendedResponse::OK)));
|
||||||
|
EXPECT_FALSE(filter.match(std::make_shared<ExtendedResponseMessage>(
|
||||||
|
ExtendedCommand::RequestTC10Wake, ExtendedResponse::OK)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10, UnsupportedDeviceReportsNotSupported) {
|
||||||
|
auto device = std::make_shared<RADMoon2>(moon2Found());
|
||||||
|
EXPECT_FALSE(device->requestTC10Wake(Network::NetID::AE_01));
|
||||||
|
EXPECT_EQ(GetLastError().getType(), APIEvent::Type::NotSupported);
|
||||||
|
EXPECT_FALSE(device->requestTC10Sleep(Network::NetID::AE_01));
|
||||||
|
EXPECT_EQ(GetLastError().getType(), APIEvent::Type::NotSupported);
|
||||||
|
EXPECT_FALSE(device->getTC10Status(Network::NetID::AE_01).has_value());
|
||||||
|
EXPECT_EQ(GetLastError().getType(), APIEvent::Type::NotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10, WakeAcceptsItsOwnOk) {
|
||||||
|
OpenTC10Device tc10;
|
||||||
|
(void)GetLastError();
|
||||||
|
tc10.driver->replies = {
|
||||||
|
genericReturn(ExtendedCommand::RequestTC10Sleep, ExtendedResponse::OperationFailed),
|
||||||
|
genericReturn(ExtendedCommand::RequestTC10Wake, ExtendedResponse::OK),
|
||||||
|
};
|
||||||
|
EXPECT_TRUE(tc10.device->requestTC10Wake(Network::NetID::AE_02));
|
||||||
|
EXPECT_EQ(GetLastError().getType(), APIEvent::Type::NoErrorFound);
|
||||||
|
EXPECT_EQ(tc10.driver->writes, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10, SleepAcceptsItsOwnOk) {
|
||||||
|
OpenTC10Device tc10;
|
||||||
|
(void)GetLastError();
|
||||||
|
tc10.driver->replies = {
|
||||||
|
genericReturn(ExtendedCommand::RequestTC10Wake, ExtendedResponse::OperationFailed),
|
||||||
|
genericReturn(ExtendedCommand::RequestTC10Sleep, ExtendedResponse::OK),
|
||||||
|
};
|
||||||
|
EXPECT_TRUE(tc10.device->requestTC10Sleep(Network::NetID::AE_01));
|
||||||
|
EXPECT_EQ(GetLastError().getType(), APIEvent::Type::NoErrorFound);
|
||||||
|
EXPECT_EQ(tc10.driver->writes, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10, WakeReportsDeviceRejection) {
|
||||||
|
OpenTC10Device tc10;
|
||||||
|
tc10.driver->replies = {
|
||||||
|
genericReturn(ExtendedCommand::RequestTC10Sleep, ExtendedResponse::OK),
|
||||||
|
genericReturn(ExtendedCommand::RequestTC10Wake, ExtendedResponse::OperationFailed),
|
||||||
|
};
|
||||||
|
EXPECT_FALSE(tc10.device->requestTC10Wake(Network::NetID::AE_02));
|
||||||
|
EXPECT_EQ(GetLastError().getType(), APIEvent::Type::TC10RequestFailed);
|
||||||
|
EXPECT_EQ(tc10.driver->writes, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10, SleepReportsDeviceRejection) {
|
||||||
|
OpenTC10Device tc10;
|
||||||
|
tc10.driver->replies = {
|
||||||
|
genericReturn(ExtendedCommand::RequestTC10Wake, ExtendedResponse::OK),
|
||||||
|
genericReturn(ExtendedCommand::RequestTC10Sleep, ExtendedResponse::InvalidParameter),
|
||||||
|
};
|
||||||
|
EXPECT_FALSE(tc10.device->requestTC10Sleep(Network::NetID::AE_01));
|
||||||
|
EXPECT_EQ(GetLastError().getType(), APIEvent::Type::TC10RequestFailed);
|
||||||
|
EXPECT_EQ(tc10.driver->writes, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10Status, DecodesTheStatusPair) {
|
||||||
|
auto msg = TC10StatusMessage::DecodeToMessage(statusBody(1, 1));
|
||||||
|
ASSERT_NE(msg, nullptr);
|
||||||
|
EXPECT_EQ(msg->wakeStatus, TC10WakeStatus::WakeReceived);
|
||||||
|
EXPECT_EQ(msg->sleepStatus, TC10SleepStatus::SleepReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10Status, RejectsTruncatedOrForeignPayloads) {
|
||||||
|
EXPECT_EQ(TC10StatusMessage::DecodeToMessage({}), nullptr);
|
||||||
|
auto bytes = statusBody(0, 0);
|
||||||
|
EXPECT_EQ(TC10StatusMessage::DecodeToMessage({bytes.begin(), bytes.begin() + 5}), nullptr);
|
||||||
|
std::vector<uint8_t> shortLength = {0x3f, 0x00, 0x01, 0x00, 0x00, 0x00};
|
||||||
|
EXPECT_EQ(TC10StatusMessage::DecodeToMessage(shortLength), nullptr);
|
||||||
|
std::vector<uint8_t> foreign = {0x3d, 0x00, 0x02, 0x00, 0x00, 0x00};
|
||||||
|
EXPECT_EQ(TC10StatusMessage::DecodeToMessage(foreign), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10Status, DropsBytesAfterTheStatusPair) {
|
||||||
|
// Tail is little-endian AE_02 (37). The decoded message has no network, so this still succeeds.
|
||||||
|
auto msg = TC10StatusMessage::DecodeToMessage(statusBody(0, 1, {37, 0}));
|
||||||
|
ASSERT_NE(msg, nullptr);
|
||||||
|
EXPECT_EQ(msg->wakeStatus, TC10WakeStatus::NoWakeReceived);
|
||||||
|
EXPECT_EQ(msg->sleepStatus, TC10SleepStatus::SleepReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TC10, StatusReturnsTheDecodedPair) {
|
||||||
|
OpenTC10Device tc10;
|
||||||
|
(void)GetLastError();
|
||||||
|
const auto body = statusBody(1, 0);
|
||||||
|
tc10.driver->replies = {longPacket(Network::NetID::ExtendedCommand, body.data(), body.size())};
|
||||||
|
const auto status = tc10.device->getTC10Status(Network::NetID::AE_01);
|
||||||
|
ASSERT_TRUE(status.has_value());
|
||||||
|
EXPECT_EQ(status->wakeStatus, TC10WakeStatus::WakeReceived);
|
||||||
|
EXPECT_EQ(status->sleepStatus, TC10SleepStatus::NoSleepReceived);
|
||||||
|
EXPECT_EQ(GetLastError().getType(), APIEvent::Type::NoErrorFound);
|
||||||
|
EXPECT_EQ(tc10.driver->writes, 1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user