From 1e5e714f5bce9e348df87be3e39ffab4563cf1c9 Mon Sep 17 00:00:00 2001 From: Yasser Yassine Date: Tue, 22 Jul 2025 20:02:06 +0000 Subject: [PATCH] Device: Add disk formatting --- CMakeLists.txt | 1 + api/icsneocpp/event.cpp | 8 ++ bindings/python/icsneopy/api/event.cpp | 2 + communication/decoder.cpp | 11 ++ device/device.cpp | 127 ++++++++++++++++++ disk/diskdetails.cpp | 81 +++++++++++ include/icsneo/api/event.h | 2 + .../message/extendedresponsemessage.h | 3 +- .../message/filter/extendedresponsefilter.h | 26 +++- include/icsneo/device/device.h | 17 +++ include/icsneo/device/idevicesettings.h | 3 +- .../device/tree/neoviconnect/neoviconnect.h | 4 + .../device/tree/neovifire2/neovifire2.h | 4 + .../device/tree/neovifire3/neovifire3.h | 4 + .../icsneo/device/tree/neovired2/neovired2.h | 4 + .../device/tree/radepsilon/radepsilon.h | 4 + .../icsneo/device/tree/radgalaxy/radgalaxy.h | 4 + .../device/tree/radgigastar/radgigastar.h | 4 + .../device/tree/radgigastar2/radgigastar2.h | 5 + include/icsneo/disk/diskdetails.h | 44 ++++++ 20 files changed, 351 insertions(+), 7 deletions(-) create mode 100644 disk/diskdetails.cpp create mode 100644 include/icsneo/disk/diskdetails.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fea0b04..67d9506 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,6 +292,7 @@ set(SRC_FILES disk/plasiondiskreaddriver.cpp disk/extextractordiskreaddriver.cpp disk/fat.cpp + disk/diskdetails.cpp disk/vsa/vsa.cpp disk/vsa/vsa02.cpp disk/vsa/vsa03.cpp diff --git a/api/icsneocpp/event.cpp b/api/icsneocpp/event.cpp index dfdc409..6983b32 100644 --- a/api/icsneocpp/event.cpp +++ b/api/icsneocpp/event.cpp @@ -124,6 +124,10 @@ static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not static constexpr const char* MODE_NOT_FOUND = "The mode was not found."; static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is not supported 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_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count."; + + // Transport Errors static constexpr const char* FAILED_TO_READ = "A read operation failed."; @@ -371,6 +375,10 @@ const char* APIEvent::DescriptionForType(Type type) { return SEND_TO_ERROR; case Type::GPTPNotSupported: return GPTP_NOT_SUPPORTED; + case Type::DiskFormatNotSupported: + return DISK_FORMAT_NOT_SUPPORTED; + case Type::DiskFormatInvalidCount: + return DISK_FORMAT_INVALID_COUNT; // FTD3XX case Type::FTOK: diff --git a/bindings/python/icsneopy/api/event.cpp b/bindings/python/icsneopy/api/event.cpp index aa0db32..18f1b0d 100644 --- a/bindings/python/icsneopy/api/event.cpp +++ b/bindings/python/icsneopy/api/event.cpp @@ -36,6 +36,8 @@ void init_event(pybind11::module_& m) { .value("SettingsLengthError", APIEvent::Type::SettingsLengthError) .value("SettingsChecksumError", APIEvent::Type::SettingsChecksumError) .value("SettingsNotAvailable", APIEvent::Type::SettingsNotAvailable) + .value("DiskFormatNotSupported", APIEvent::Type::DiskFormatNotSupported) + .value("DiskFormatInvalidCount", APIEvent::Type::DiskFormatInvalidCount) .value("SettingsReadOnly", APIEvent::Type::SettingsReadOnly) .value("CANSettingsNotAvailable", APIEvent::Type::CANSettingsNotAvailable) .value("CANFDSettingsNotAvailable", APIEvent::Type::CANFDSettingsNotAvailable) diff --git a/communication/decoder.cpp b/communication/decoder.cpp index ce59267..0b95b5d 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -324,6 +324,17 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata, report); return true; } + case ExtendedCommand::GetDiskDetails: + case ExtendedCommand::DiskFormatProgress: { + std::vector responseBody( + packet->data.begin() + sizeof(ExtendedResponseMessage::ResponseHeader), + packet->data.end() + ); + auto response = std::make_shared(resp.command); + response->data = std::move(responseBody); + result = response; + return true; + } default: // No defined handler, treat this as a RawMessage break; diff --git a/device/device.cpp b/device/device.cpp index e8c9ade..edc4607 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -5,6 +5,7 @@ #include "icsneo/device/device.h" #include "icsneo/device/extensions/deviceextension.h" #include "icsneo/disk/fat.h" +#include "icsneo/communication/message/filter/extendedresponsefilter.h" #ifdef _MSC_VER #pragma warning(disable : 4996) // STL time functions @@ -3487,3 +3488,129 @@ bool Device::enableNetworkCommunication(bool enable) { } return true; } + + + +bool Device::formatDisk(const DiskDetails& config, const DiskFormatProgress& handler, std::chrono::milliseconds interval) { +#pragma pack(push, 2) + struct DiskFormatProgressResponse { + uint16_t state; + uint64_t sectorsRemaining; + }; +#pragma pack(pop) + + auto diskCount = getDiskCount(); + + if(!diskCount) { + report(APIEvent::Type::DiskFormatNotSupported, APIEvent::Severity::Error); + return false; + } + if(config.disks.size() != diskCount) { + report(APIEvent::Type::DiskFormatInvalidCount, APIEvent::Severity::Error); + return false; + } + + std::vector payload = DiskDetails::Encode(config); + if(!com->sendCommand(ExtendedCommand::DiskFormatStart, payload)) { + return false; + } + + uint64_t sectorsFormatted = 0; + uint64_t sectorsTotal = 0; + uint16_t lastState = 1; + do { + std::shared_ptr response = com->waitForMessageSync( + [this](){ + return com->sendCommand(ExtendedCommand::DiskFormatProgress, {}); + }, + std::make_shared(ExtendedCommand::DiskFormatProgress), + std::chrono::milliseconds(200) + ); + + if(!response) { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return false; + } + + auto extResponse = std::dynamic_pointer_cast(response); + if(!extResponse) { + // Should never happen + return false; + } + + if(extResponse->data.size() < sizeof(DiskFormatProgressResponse)) { + report(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error); + return false; + } + + auto* progress = reinterpret_cast(extResponse->data.data()); + lastState = progress->state; + + if(sectorsTotal == 0) { + sectorsTotal = progress->sectorsRemaining; + } else { + sectorsFormatted = sectorsTotal - progress->sectorsRemaining; + + if(handler) { + auto directive = handler(sectorsFormatted, sectorsTotal); + + if(directive == DiskFormatDirective::Stop) { + return com->sendCommand(ExtendedCommand::DiskFormatCancel); + } + } + } + + std::this_thread::sleep_for(interval); + } while(lastState); + + return true; +} + +bool Device::forceDiskConfigUpdate(const DiskDetails& config) { + auto diskCount = getDiskCount(); + + if(!diskCount) { + report(APIEvent::Type::DiskFormatNotSupported, APIEvent::Severity::Error); + return false; + } + if(config.disks.size() != diskCount) { + report(APIEvent::Type::DiskFormatInvalidCount, APIEvent::Severity::Error); + return false; + } + + return com->sendCommand(ExtendedCommand::DiskFormatUpdate, DiskDetails::Encode(config)); +} + +std::shared_ptr Device::getDiskDetails(std::chrono::milliseconds timeout) { + if(!supportsDiskFormatting()) { + report(APIEvent::Type::DiskFormatNotSupported, APIEvent::Severity::Error); + return nullptr; + } + + if(!isOpen()) { + report(APIEvent::Type::DeviceCurrentlyClosed, APIEvent::Severity::Error); + return nullptr; + } + + std::shared_ptr response = com->waitForMessageSync( + [this](){ + return com->sendCommand(ExtendedCommand::GetDiskDetails, {}); + }, + std::make_shared(ExtendedCommand::GetDiskDetails), + timeout + ); + + if(!response) { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return nullptr; + } + + auto extResponse = std::dynamic_pointer_cast(response); + if(!extResponse) { + // Should never happen + return nullptr; + } + + return DiskDetails::Decode(extResponse->data, getDiskCount(), report); +} + diff --git a/disk/diskdetails.cpp b/disk/diskdetails.cpp new file mode 100644 index 0000000..85a1062 --- /dev/null +++ b/disk/diskdetails.cpp @@ -0,0 +1,81 @@ +#include +#include + +using namespace icsneo; + +#pragma pack(push, 2) + +struct DISK_STATUS { + uint16_t status; + uint64_t sectors; + uint32_t bytesPerSector; +}; + +struct DISK_DETAILS { + DISK_SETTINGS settings; + uint16_t options; + DISK_STATUS status[12]; +}; + +#pragma pack(pop) + +static constexpr uint8_t DISK_FORMAT_FAT32 = 0x01u; +static constexpr uint8_t DISK_STATUS_PRESENT = 0x01u; +static constexpr uint8_t DISK_STATUS_INITIALIZED = 0x02u; +static constexpr uint16_t DISK_DETAILS_FULL_FORMAT = 0x01u; + +std::vector DiskDetails::Encode(const DiskDetails& config) { + std::vector result(sizeof(DISK_DETAILS), 0); + DISK_DETAILS* details = reinterpret_cast(result.data()); + details->settings.disk_layout = static_cast(config.layout); + details->settings.disk_format = DISK_FORMAT_FAT32; // Always FAT32 + details->options = config.fullFormat ? DISK_DETAILS_FULL_FORMAT : 0; + uint32_t& enables = details->settings.disk_enables; + enables = 0u; + for(size_t i = 0; i < config.disks.size(); i++) { + auto& disk = config.disks[i]; + details->status[i].sectors = disk.sectors; + details->status[i].bytesPerSector = disk.bytesPerSector; + + uint16_t& status = details->status[i].status; + status = 0u; + if(disk.initialized) { + status |= DISK_STATUS_INITIALIZED; + } + if(disk.present) { + status |= DISK_STATUS_PRESENT; + } + if(disk.formatted) { + enables |= (1u << i); + } + } + + return result; +} + +std::shared_ptr DiskDetails::Decode(const std::vector& bytes, size_t diskCount, device_eventhandler_t report) { + if(bytes.size() < sizeof(DISK_DETAILS)) { + report(APIEvent::Type::BufferInsufficient, APIEvent::Severity::Error); + return nullptr; + } + + auto result = std::make_shared(); + const DISK_DETAILS* details = reinterpret_cast(bytes.data()); + result->layout = static_cast(details->settings.disk_layout); + result->fullFormat = details->options & DISK_DETAILS_FULL_FORMAT; + + result->disks.reserve(diskCount); + + for(size_t i = 0; i < diskCount; i++) { + auto& disk = details->status[i]; + result->disks.emplace_back(); + auto& info = result->disks.back(); + info.present = static_cast(disk.status & DISK_STATUS_PRESENT); + info.initialized = static_cast(disk.status & DISK_STATUS_INITIALIZED); + info.formatted = static_cast(details->settings.disk_enables & (1u << i)); + info.sectors = disk.sectors; + info.bytesPerSector = disk.bytesPerSector; + } + + return result; +} diff --git a/include/icsneo/api/event.h b/include/icsneo/api/event.h index f217671..9aa7fee 100644 --- a/include/icsneo/api/event.h +++ b/include/icsneo/api/event.h @@ -113,6 +113,8 @@ public: AppErrorParsingFailed = 0x2055, GPTPNotSupported = 0x2056, SettingNotAvaiableDevice = 0x2057, + DiskFormatNotSupported = 0x2058, + DiskFormatInvalidCount = 0x2059, // Transport Events FailedToRead = 0x3000, diff --git a/include/icsneo/communication/message/extendedresponsemessage.h b/include/icsneo/communication/message/extendedresponsemessage.h index 04361ad..58c9620 100644 --- a/include/icsneo/communication/message/extendedresponsemessage.h +++ b/include/icsneo/communication/message/extendedresponsemessage.h @@ -10,11 +10,12 @@ namespace icsneo { class ExtendedResponseMessage : public Message { public: - ExtendedResponseMessage(ExtendedCommand cmd, ExtendedResponse resp) + ExtendedResponseMessage(ExtendedCommand cmd, ExtendedResponse resp = ExtendedResponse::OK) : Message(Message::Type::ExtendedResponse), command(cmd), response(resp) {} const ExtendedCommand command; const ExtendedResponse response; + std::vector data; #pragma pack(push, 2) struct ResponseHeader { diff --git a/include/icsneo/communication/message/filter/extendedresponsefilter.h b/include/icsneo/communication/message/filter/extendedresponsefilter.h index 4ae8741..a44deee 100644 --- a/include/icsneo/communication/message/filter/extendedresponsefilter.h +++ b/include/icsneo/communication/message/filter/extendedresponsefilter.h @@ -15,20 +15,36 @@ namespace icsneo { class ExtendedResponseFilter : public MessageFilter { public: - ExtendedResponseFilter(icsneo::ExtendedResponse resp) : MessageFilter(Message::Type::ExtendedResponse), response(resp) {} + ExtendedResponseFilter(icsneo::ExtendedCommand cmd) : MessageFilter(Message::Type::ExtendedResponse), command(cmd), response(std::nullopt) {} + ExtendedResponseFilter(icsneo::ExtendedResponse resp) : MessageFilter(Message::Type::ExtendedResponse), command(std::nullopt), response(resp) {} + ExtendedResponseFilter(icsneo::ExtendedCommand cmd, icsneo::ExtendedResponse resp) + : MessageFilter(Message::Type::ExtendedResponse), command(cmd), response(resp) {} bool match(const std::shared_ptr& message) const override { if(!MessageFilter::match(message)) { return false; } const auto respMsg = std::static_pointer_cast(message); - return respMsg && matchResponse(respMsg->response); + return respMsg && matchResponse(respMsg->command, respMsg->response); } private: - icsneo::ExtendedResponse response; - bool matchResponse(icsneo::ExtendedResponse resp) const { - return response == resp; + std::optional command = std::nullopt; + std::optional response = std::nullopt; + + bool matchResponse(icsneo::ExtendedCommand cmd, icsneo::ExtendedResponse resp) const { + if(command) { + if(*command != cmd) { + return false; + } + } + if(response) { + if(*response != resp) { + return false; + } + } + + return true; } }; diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index a520e13..96cd334 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -26,6 +26,7 @@ #include "icsneo/disk/diskreaddriver.h" #include "icsneo/disk/diskwritedriver.h" #include "icsneo/disk/nulldiskdriver.h" +#include "icsneo/disk/diskdetails.h" #include "icsneo/communication/communication.h" #include "icsneo/communication/packetizer.h" #include "icsneo/communication/encoder.h" @@ -637,6 +638,22 @@ public: bool unsubscribeLiveData(const LiveDataHandle& handle); bool clearAllLiveData(); bool setValueLiveData(std::shared_ptr message); + + enum class DiskFormatDirective : uint8_t { + Continue, + Stop + }; + + using DiskFormatProgress = std::function; + bool formatDisk( + const DiskDetails& config, + const DiskFormatProgress& handler = {}, + std::chrono::milliseconds interval = std::chrono::milliseconds(500) /** Send updates at an interval of 500ms */ + ); + bool forceDiskConfigUpdate(const DiskDetails& config); // Forces a disk layout and enables change without formatting + std::shared_ptr getDiskDetails(std::chrono::milliseconds timeout = std::chrono::milliseconds(100)); + virtual size_t getDiskCount() const { return 0; } + bool supportsDiskFormatting() const { return getDiskCount() != 0; } // VSA Read functions diff --git a/include/icsneo/device/idevicesettings.h b/include/icsneo/device/idevicesettings.h index 2f94158..73f37d1 100644 --- a/include/icsneo/device/idevicesettings.h +++ b/include/icsneo/device/idevicesettings.h @@ -924,7 +924,8 @@ public: template T getStructure() const { return *getStructurePointer(); } template bool applyStructure(const T& newStructure); - const size_t& getSize() const { return structSize; } + size_t getSize() const { return structSize; } + size_t getSerialSize() const { return settings.size(); } // if settings are disabled for this device. always false unless constructed null bool disabled = false; diff --git a/include/icsneo/device/tree/neoviconnect/neoviconnect.h b/include/icsneo/device/tree/neoviconnect/neoviconnect.h index 9dc012b..15141a1 100644 --- a/include/icsneo/device/tree/neoviconnect/neoviconnect.h +++ b/include/icsneo/device/tree/neoviconnect/neoviconnect.h @@ -70,6 +70,10 @@ protected: } bool supportsGPTP() const override { return true; } + + size_t getDiskCount() const override { + return 2; + } }; diff --git a/include/icsneo/device/tree/neovifire2/neovifire2.h b/include/icsneo/device/tree/neovifire2/neovifire2.h index 2cb83b5..7eedbd6 100644 --- a/include/icsneo/device/tree/neovifire2/neovifire2.h +++ b/include/icsneo/device/tree/neovifire2/neovifire2.h @@ -141,6 +141,10 @@ protected: bool supportsEraseMemory() const override { return true; } + + size_t getDiskCount() const override { + return 1; + } }; } diff --git a/include/icsneo/device/tree/neovifire3/neovifire3.h b/include/icsneo/device/tree/neovifire3/neovifire3.h index 51e92bf..61282de 100644 --- a/include/icsneo/device/tree/neovifire3/neovifire3.h +++ b/include/icsneo/device/tree/neovifire3/neovifire3.h @@ -91,6 +91,10 @@ protected: return true; } + size_t getDiskCount() const override { + return 2; + } + }; } diff --git a/include/icsneo/device/tree/neovired2/neovired2.h b/include/icsneo/device/tree/neovired2/neovired2.h index d8070c7..8be3aaa 100644 --- a/include/icsneo/device/tree/neovired2/neovired2.h +++ b/include/icsneo/device/tree/neovired2/neovired2.h @@ -75,6 +75,10 @@ protected: bool supportsEraseMemory() const override { return true; } + + size_t getDiskCount() const override { + return 2; + } }; } diff --git a/include/icsneo/device/tree/radepsilon/radepsilon.h b/include/icsneo/device/tree/radepsilon/radepsilon.h index 2731c4c..c3cd594 100644 --- a/include/icsneo/device/tree/radepsilon/radepsilon.h +++ b/include/icsneo/device/tree/radepsilon/radepsilon.h @@ -54,6 +54,10 @@ protected: bool supportsEraseMemory() const override { return true; } + + size_t getDiskCount() const override { + return 1; + } }; } diff --git a/include/icsneo/device/tree/radgalaxy/radgalaxy.h b/include/icsneo/device/tree/radgalaxy/radgalaxy.h index ac01a25..a79a36b 100644 --- a/include/icsneo/device/tree/radgalaxy/radgalaxy.h +++ b/include/icsneo/device/tree/radgalaxy/radgalaxy.h @@ -107,6 +107,10 @@ protected: std::optional getCoreminiStartAddressSD() const override { return 0; } + + size_t getDiskCount() const override { + return 1; + } }; } diff --git a/include/icsneo/device/tree/radgigastar/radgigastar.h b/include/icsneo/device/tree/radgigastar/radgigastar.h index 2c82d70..00dce12 100644 --- a/include/icsneo/device/tree/radgigastar/radgigastar.h +++ b/include/icsneo/device/tree/radgigastar/radgigastar.h @@ -120,6 +120,10 @@ protected: std::optional getCoreminiStartAddressSD() const override { return 0; } + + size_t getDiskCount() const override { + return 1; + } }; } diff --git a/include/icsneo/device/tree/radgigastar2/radgigastar2.h b/include/icsneo/device/tree/radgigastar2/radgigastar2.h index 5239766..9d5ed85 100644 --- a/include/icsneo/device/tree/radgigastar2/radgigastar2.h +++ b/include/icsneo/device/tree/radgigastar2/radgigastar2.h @@ -137,6 +137,11 @@ namespace icsneo { return 0; } + + size_t getDiskCount() const override + { + return 1; + } }; } diff --git a/include/icsneo/disk/diskdetails.h b/include/icsneo/disk/diskdetails.h new file mode 100644 index 0000000..7bc5451 --- /dev/null +++ b/include/icsneo/disk/diskdetails.h @@ -0,0 +1,44 @@ +#ifndef __DISKDETAILS_H__ +#define __DISKDETAILS_H__ + +#ifdef __cplusplus + +#include +#include +#include + +namespace icsneo { + +enum class DiskLayout : uint8_t { + Spanned = 0, + RAID0 = 1 +}; + +struct DiskInfo { + bool present; // Disk is connected + bool initialized; // Disk is initialized + bool formatted; // Disk is formatted + + uint64_t sectors; + uint32_t bytesPerSector; + + uint64_t size() const { + return sectors * bytesPerSector; + } +}; + +struct DiskDetails { + DiskLayout layout; + bool fullFormat; + std::vector disks; + + static std::vector Encode(const DiskDetails& config); + static std::shared_ptr Decode(const std::vector& bytes, size_t diskCount, device_eventhandler_t report); +}; + + +} // namespace icsneo + +#endif // __cplusplus + +#endif \ No newline at end of file