From d27b516894e69f1b6b5584b58925740ae00c4a6e Mon Sep 17 00:00:00 2001 From: Paul Hollinsky Date: Tue, 25 Sep 2018 17:35:56 -0400 Subject: [PATCH] Communication::Command refactored out of Communication --- communication/communication.cpp | 317 +++++++++--------- communication/include/command.h | 16 + communication/include/communication.h | 134 ++++---- .../include/multichannelcommunication.h | 203 +++++------ communication/multichannelcommunication.cpp | 251 +++++++------- device/device.cpp | 309 +++++++++-------- 6 files changed, 620 insertions(+), 610 deletions(-) create mode 100644 communication/include/command.h diff --git a/communication/communication.cpp b/communication/communication.cpp index 36618ed..768e980 100644 --- a/communication/communication.cpp +++ b/communication/communication.cpp @@ -1,158 +1,159 @@ -#include "communication/include/communication.h" -#include -#include -#include -#include -#include -#include -#include -#include "communication/include/messagedecoder.h" -#include "communication/include/packetizer.h" - -using namespace icsneo; - -int Communication::messageCallbackIDCounter = 1; - -uint8_t Communication::ICSChecksum(const std::vector& data) { - uint32_t checksum = 0; - for(auto i = 0; i < data.size(); i++) - checksum += data[i]; - checksum = ~checksum; - checksum++; - return (uint8_t)checksum; -} - -std::vector& Communication::packetWrap(std::vector& data, bool addChecksum) { - if(addChecksum) - data.push_back(ICSChecksum(data)); - data.insert(data.begin(), 0xAA); - if(align16bit && data.size() % 2 == 1) - data.push_back('A'); - return data; -} - -bool Communication::open() { - if(isOpen) - return true; - - spawnThreads(); - isOpen = true; - return impl->open(); -} - -void Communication::spawnThreads() { - readTaskThread = std::thread(&Communication::readTask, this); -} - -void Communication::joinThreads() { - if(readTaskThread.joinable()) - readTaskThread.join(); -} - -bool Communication::close() { - if(!isOpen) - return false; - - isOpen = false; - closing = true; - joinThreads(); - - return impl->close(); -} - -bool Communication::sendPacket(std::vector& bytes) { - return impl->write(Communication::packetWrap(bytes)); -} - -bool Communication::sendCommand(Communication::Command cmd, std::vector arguments) { - std::vector bytes; - bytes.push_back((uint8_t)cmd); - for(auto& b : arguments) - bytes.push_back(b); - bytes.insert(bytes.begin(), (uint8_t)Network::NetID::Main51 | ((uint8_t)bytes.size() << 4)); - return sendPacket(bytes); -} - -bool Communication::getSettingsSync(std::vector& data, std::chrono::milliseconds timeout) { - sendCommand(Command::GetSettings); - std::shared_ptr msg = waitForMessageSync(MessageFilter(Network::NetID::RED_READ_BAUD_SETTINGS), timeout); - if(!msg) - return false; - - data = std::move(msg->data); - return true; -} - -bool Communication::getSerialNumberSync(std::string& serial, std::chrono::milliseconds timeout) { - sendCommand(Command::RequestSerialNumber); - std::shared_ptr msg = waitForMessageSync(MessageFilter(Network::NetID::RED_OLDFORMAT), timeout); - if(!msg) - return false; - - std::cout << "Got " << msg->data.size() << " bytes" << std::endl; - for(size_t i = 0; i < msg->data.size(); i++) { - std::cout << std::hex << (int)msg->data[i] << ' ' << std::dec; - if(i % 16 == 15) - std::cout << std::endl; - } - return true; -} - -int Communication::addMessageCallback(const MessageCallback& cb) { - messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb)); - return messageCallbackIDCounter++; -} - -bool Communication::removeMessageCallback(int id) { - try { - messageCallbacks.erase(id); - return true; - } catch(...) { - return false; - } -} - -std::shared_ptr Communication::waitForMessageSync(MessageFilter f, std::chrono::milliseconds timeout) { - std::mutex m; - std::condition_variable cv; - std::shared_ptr returnedMessage; - int cb = addMessageCallback(MessageCallback([&m, &returnedMessage, &cv](std::shared_ptr message) { - { - std::lock_guard lk(m); - returnedMessage = message; - } - cv.notify_one(); - }, f)); - - // We have now added the callback, wait for it to return from the other thread - std::unique_lock lk(m); - cv.wait_for(lk, timeout, [&returnedMessage]{ return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value - - // We don't actually check that we got a message, because either way we want to remove the callback (since it should only happen once) - removeMessageCallback(cb); - - // Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking - return returnedMessage; -} - -void Communication::readTask() { - std::vector readBytes; - Packetizer packetizer; - MessageDecoder decoder; - - while(!closing) { - readBytes.clear(); - if(impl->readWait(readBytes)) { - if(packetizer.input(readBytes)) { - for(auto& packet : packetizer.output()) { - auto msg = decoder.decodePacket(packet); - for(auto& cb : messageCallbacks) { // We might have closed while reading or processing - if(!closing) { - cb.second.callIfMatch(msg); - } - } - } - } - } - } -} +#include "communication/include/communication.h" +#include +#include +#include +#include +#include +#include +#include +#include "communication/include/command.h" +#include "communication/include/messagedecoder.h" +#include "communication/include/packetizer.h" + +using namespace icsneo; + +int Communication::messageCallbackIDCounter = 1; + +uint8_t Communication::ICSChecksum(const std::vector& data) { + uint32_t checksum = 0; + for(auto i = 0; i < data.size(); i++) + checksum += data[i]; + checksum = ~checksum; + checksum++; + return (uint8_t)checksum; +} + +std::vector& Communication::packetWrap(std::vector& data, bool addChecksum) { + if(addChecksum) + data.push_back(ICSChecksum(data)); + data.insert(data.begin(), 0xAA); + if(align16bit && data.size() % 2 == 1) + data.push_back('A'); + return data; +} + +bool Communication::open() { + if(isOpen) + return true; + + spawnThreads(); + isOpen = true; + return impl->open(); +} + +void Communication::spawnThreads() { + readTaskThread = std::thread(&Communication::readTask, this); +} + +void Communication::joinThreads() { + if(readTaskThread.joinable()) + readTaskThread.join(); +} + +bool Communication::close() { + if(!isOpen) + return false; + + isOpen = false; + closing = true; + joinThreads(); + + return impl->close(); +} + +bool Communication::sendPacket(std::vector& bytes) { + return impl->write(Communication::packetWrap(bytes)); +} + +bool Communication::sendCommand(Command cmd, std::vector arguments) { + std::vector bytes; + bytes.push_back((uint8_t)cmd); + for(auto& b : arguments) + bytes.push_back(b); + bytes.insert(bytes.begin(), (uint8_t)Network::NetID::Main51 | ((uint8_t)bytes.size() << 4)); + return sendPacket(bytes); +} + +bool Communication::getSettingsSync(std::vector& data, std::chrono::milliseconds timeout) { + sendCommand(Command::GetSettings); + std::shared_ptr msg = waitForMessageSync(MessageFilter(Network::NetID::RED_READ_BAUD_SETTINGS), timeout); + if(!msg) + return false; + + data = std::move(msg->data); + return true; +} + +bool Communication::getSerialNumberSync(std::string& serial, std::chrono::milliseconds timeout) { + sendCommand(Command::RequestSerialNumber); + std::shared_ptr msg = waitForMessageSync(MessageFilter(Network::NetID::RED_OLDFORMAT), timeout); + if(!msg) + return false; + + std::cout << "Got " << msg->data.size() << " bytes" << std::endl; + for(size_t i = 0; i < msg->data.size(); i++) { + std::cout << std::hex << (int)msg->data[i] << ' ' << std::dec; + if(i % 16 == 15) + std::cout << std::endl; + } + return true; +} + +int Communication::addMessageCallback(const MessageCallback& cb) { + messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb)); + return messageCallbackIDCounter++; +} + +bool Communication::removeMessageCallback(int id) { + try { + messageCallbacks.erase(id); + return true; + } catch(...) { + return false; + } +} + +std::shared_ptr Communication::waitForMessageSync(MessageFilter f, std::chrono::milliseconds timeout) { + std::mutex m; + std::condition_variable cv; + std::shared_ptr returnedMessage; + int cb = addMessageCallback(MessageCallback([&m, &returnedMessage, &cv](std::shared_ptr message) { + { + std::lock_guard lk(m); + returnedMessage = message; + } + cv.notify_one(); + }, f)); + + // We have now added the callback, wait for it to return from the other thread + std::unique_lock lk(m); + cv.wait_for(lk, timeout, [&returnedMessage]{ return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value + + // We don't actually check that we got a message, because either way we want to remove the callback (since it should only happen once) + removeMessageCallback(cb); + + // Then we either will return the message we got or we will return the empty shared_ptr, caller responsible for checking + return returnedMessage; +} + +void Communication::readTask() { + std::vector readBytes; + Packetizer packetizer; + MessageDecoder decoder; + + while(!closing) { + readBytes.clear(); + if(impl->readWait(readBytes)) { + if(packetizer.input(readBytes)) { + for(auto& packet : packetizer.output()) { + auto msg = decoder.decodePacket(packet); + for(auto& cb : messageCallbacks) { // We might have closed while reading or processing + if(!closing) { + cb.second.callIfMatch(msg); + } + } + } + } + } + } +} diff --git a/communication/include/command.h b/communication/include/command.h new file mode 100644 index 0000000..37a42e4 --- /dev/null +++ b/communication/include/command.h @@ -0,0 +1,16 @@ +#ifndef __COMMAND_H_ +#define __COMMAND_H_ + +namespace icsneo { + +enum class Command : uint8_t { + EnableNetworkCommunication = 0x07, + RequestSerialNumber = 0xA1, + SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM + GetSettings = 0xA5, // Previously known as RED_CMD_READ_BAUD_REQ + SaveSettings = 0xA6 +}; + +} + +#endif \ No newline at end of file diff --git a/communication/include/communication.h b/communication/include/communication.h index 7764bb4..60a89e6 100644 --- a/communication/include/communication.h +++ b/communication/include/communication.h @@ -1,71 +1,65 @@ -#ifndef __COMMUNICATION_H_ -#define __COMMUNICATION_H_ - -#include "communication/include/icommunication.h" -#include "communication/include/network.h" -#include "communication/include/messagecallback.h" -#include -#include -#include -#include -#include -#include - -namespace icsneo { - -class Communication { -public: - static uint8_t ICSChecksum(const std::vector& data); - - Communication(std::shared_ptr com) : impl(com) {} - virtual ~Communication() { close(); } - - bool open(); - bool close(); - virtual void spawnThreads(); - virtual void joinThreads(); - bool rawWrite(const std::vector& bytes) { return impl->write(bytes); } - std::vector& packetWrap(std::vector& data, bool addChecksum = true); - bool sendPacket(std::vector& bytes); - - enum class Command : uint8_t { - EnableNetworkCommunication = 0x07, - RequestSerialNumber = 0xA1, - SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM - GetSettings = 0xA5, // Previously known as RED_CMD_READ_BAUD_REQ - SaveSettings = 0xA6 - }; - virtual bool sendCommand(Command cmd, bool boolean) { return sendCommand(cmd, std::vector({ (uint8_t)boolean })); } - virtual bool sendCommand(Command cmd, std::vector arguments = {}); - bool getSettingsSync(std::vector& data, std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); - bool getSerialNumberSync(std::string& serial, std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); - - int addMessageCallback(const MessageCallback& cb); - bool removeMessageCallback(int id); - std::shared_ptr waitForMessageSync(MessageFilter f = MessageFilter(), std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); - - void setAlign16Bit(bool enable) { align16bit = enable; } - - class Packet { - public: - Network network; - std::vector data; - }; - -protected: - std::shared_ptr impl; - static int messageCallbackIDCounter; - std::map messageCallbacks; - std::atomic closing{false}; - -private: - bool isOpen = false; - bool align16bit = true; // Not needed for Gigalog, Galaxy, etc and newer - - std::thread readTaskThread; - void readTask(); -}; - -}; - +#ifndef __COMMUNICATION_H_ +#define __COMMUNICATION_H_ + +#include "communication/include/icommunication.h" +#include "communication/include/command.h" +#include "communication/include/network.h" +#include "communication/include/messagecallback.h" +#include +#include +#include +#include +#include +#include + +namespace icsneo { + +class Communication { +public: + static uint8_t ICSChecksum(const std::vector& data); + + Communication(std::shared_ptr com) : impl(com) {} + virtual ~Communication() { close(); } + + bool open(); + bool close(); + virtual void spawnThreads(); + virtual void joinThreads(); + bool rawWrite(const std::vector& bytes) { return impl->write(bytes); } + std::vector& packetWrap(std::vector& data, bool addChecksum = true); + bool sendPacket(std::vector& bytes); + + virtual bool sendCommand(Command cmd, bool boolean) { return sendCommand(cmd, std::vector({ (uint8_t)boolean })); } + virtual bool sendCommand(Command cmd, std::vector arguments = {}); + bool getSettingsSync(std::vector& data, std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); + bool getSerialNumberSync(std::string& serial, std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); + + int addMessageCallback(const MessageCallback& cb); + bool removeMessageCallback(int id); + std::shared_ptr waitForMessageSync(MessageFilter f = MessageFilter(), std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); + + void setAlign16Bit(bool enable) { align16bit = enable; } + + class Packet { + public: + Network network; + std::vector data; + }; + +protected: + std::shared_ptr impl; + static int messageCallbackIDCounter; + std::map messageCallbacks; + std::atomic closing{false}; + +private: + bool isOpen = false; + bool align16bit = true; // Not needed for Gigalog, Galaxy, etc and newer + + std::thread readTaskThread; + void readTask(); +}; + +}; + #endif \ No newline at end of file diff --git a/communication/include/multichannelcommunication.h b/communication/include/multichannelcommunication.h index 43d915b..2224a4a 100644 --- a/communication/include/multichannelcommunication.h +++ b/communication/include/multichannelcommunication.h @@ -1,102 +1,103 @@ -#ifndef __MULTICHANNELCOMMUNICATION_H_ -#define __MULTICHANNELCOMMUNICATION_H_ - -#include "communication/include/communication.h" -#include "communication/include/icommunication.h" - -namespace icsneo { - -class MultiChannelCommunication : public Communication { -public: - MultiChannelCommunication(std::shared_ptr com) : Communication(com) {} - void spawnThreads(); - void joinThreads(); - bool sendCommand(Communication::Command cmd, std::vector arguments); - -protected: - bool preprocessPacket(std::deque& usbReadFifo); - -private: - enum class CommandType : uint8_t { - PlasmaReadRequest = 0x10, // Status read request to HSC - PlasmaStatusResponse = 0x11, // Status response by HSC - HostPC_to_Vnet1 = 0x20, // Host PC data to Vnet module-1 - Vnet1_to_HostPC = 0x21, // Vnet module-1 data to host PC - HostPC_to_Vnet2 = 0x30, // Host PC data to Vnet module-2 - Vnet2_to_HostPC = 0x31, // Vnet module-2 data to host PC - HostPC_to_Vnet3 = 0x40, // Host PC data to Vnet module-3 - Vnet3_to_HostPC = 0x41, // Vnet module-3 data to host PC - HostPC_to_SDCC1 = 0x50, // Host PC data to write to SDCC-1 - HostPC_from_SDCC1 = 0x51, // Host PC wants data read from SDCC-1 - SDCC1_to_HostPC = 0x52, // SDCC-1 data to host PC - HostPC_to_SDCC2 = 0x60, // Host PC data to write to SDCC-2 - HostPC_from_SDCC2 = 0x61, // Host PC wants data read from SDCC-2 - SDCC2_to_HostPC = 0x62, // SDCC-2 data to host PC - PC_to_LSOC = 0x70, // Host PC data to LSOCC - LSOCC_to_PC = 0x71, // LSOCC data to host PC - HostPC_to_Microblaze = 0x80, // Host PC data to microblaze processor - Microblaze_to_HostPC = 0x81 // Microblaze processor data to host PC - }; - static bool CommandTypeIsValid(CommandType cmd) { - switch(cmd) { - case CommandType::PlasmaReadRequest: - case CommandType::PlasmaStatusResponse: - case CommandType::HostPC_to_Vnet1: - case CommandType::Vnet1_to_HostPC: - case CommandType::HostPC_to_Vnet2: - case CommandType::Vnet2_to_HostPC: - case CommandType::HostPC_to_Vnet3: - case CommandType::Vnet3_to_HostPC: - case CommandType::HostPC_to_SDCC1: - case CommandType::HostPC_from_SDCC1: - case CommandType::SDCC1_to_HostPC: - case CommandType::HostPC_to_SDCC2: - case CommandType::HostPC_from_SDCC2: - case CommandType::SDCC2_to_HostPC: - case CommandType::PC_to_LSOC: - case CommandType::LSOCC_to_PC: - case CommandType::HostPC_to_Microblaze: - case CommandType::Microblaze_to_HostPC: - return true; - default: - return false; - } - } - static bool CommandTypeHasAddress(CommandType cmd) { - // Check CommandTypeIsValid before this, you will get false on an invalid command - switch(cmd) { - case CommandType::SDCC1_to_HostPC: - case CommandType::SDCC2_to_HostPC: - return true; - default: - return false; - } - } - static uint16_t CommandTypeDefinesLength(CommandType cmd) { - // Check CommandTypeIsValid before this, you will get 0 on an invalid command - switch(cmd) { - case CommandType::PlasmaStatusResponse: - return 2; - default: - return 0; // Length is defined by following bytes in message - } - } - - enum class PreprocessState { - SearchForCommand, - ParseAddress, - ParseLength, - GetData - }; - PreprocessState state = PreprocessState::SearchForCommand; - uint16_t currentCommandLength; - CommandType currentCommandType; - size_t currentReadIndex = 0; - - std::thread mainChannelReadThread; - void readTask(); -}; - -}; - +#ifndef __MULTICHANNELCOMMUNICATION_H_ +#define __MULTICHANNELCOMMUNICATION_H_ + +#include "communication/include/communication.h" +#include "communication/include/icommunication.h" +#include "communication/include/command.h" + +namespace icsneo { + +class MultiChannelCommunication : public Communication { +public: + MultiChannelCommunication(std::shared_ptr com) : Communication(com) {} + void spawnThreads(); + void joinThreads(); + bool sendCommand(Command cmd, std::vector arguments); + +protected: + bool preprocessPacket(std::deque& usbReadFifo); + +private: + enum class CommandType : uint8_t { + PlasmaReadRequest = 0x10, // Status read request to HSC + PlasmaStatusResponse = 0x11, // Status response by HSC + HostPC_to_Vnet1 = 0x20, // Host PC data to Vnet module-1 + Vnet1_to_HostPC = 0x21, // Vnet module-1 data to host PC + HostPC_to_Vnet2 = 0x30, // Host PC data to Vnet module-2 + Vnet2_to_HostPC = 0x31, // Vnet module-2 data to host PC + HostPC_to_Vnet3 = 0x40, // Host PC data to Vnet module-3 + Vnet3_to_HostPC = 0x41, // Vnet module-3 data to host PC + HostPC_to_SDCC1 = 0x50, // Host PC data to write to SDCC-1 + HostPC_from_SDCC1 = 0x51, // Host PC wants data read from SDCC-1 + SDCC1_to_HostPC = 0x52, // SDCC-1 data to host PC + HostPC_to_SDCC2 = 0x60, // Host PC data to write to SDCC-2 + HostPC_from_SDCC2 = 0x61, // Host PC wants data read from SDCC-2 + SDCC2_to_HostPC = 0x62, // SDCC-2 data to host PC + PC_to_LSOC = 0x70, // Host PC data to LSOCC + LSOCC_to_PC = 0x71, // LSOCC data to host PC + HostPC_to_Microblaze = 0x80, // Host PC data to microblaze processor + Microblaze_to_HostPC = 0x81 // Microblaze processor data to host PC + }; + static bool CommandTypeIsValid(CommandType cmd) { + switch(cmd) { + case CommandType::PlasmaReadRequest: + case CommandType::PlasmaStatusResponse: + case CommandType::HostPC_to_Vnet1: + case CommandType::Vnet1_to_HostPC: + case CommandType::HostPC_to_Vnet2: + case CommandType::Vnet2_to_HostPC: + case CommandType::HostPC_to_Vnet3: + case CommandType::Vnet3_to_HostPC: + case CommandType::HostPC_to_SDCC1: + case CommandType::HostPC_from_SDCC1: + case CommandType::SDCC1_to_HostPC: + case CommandType::HostPC_to_SDCC2: + case CommandType::HostPC_from_SDCC2: + case CommandType::SDCC2_to_HostPC: + case CommandType::PC_to_LSOC: + case CommandType::LSOCC_to_PC: + case CommandType::HostPC_to_Microblaze: + case CommandType::Microblaze_to_HostPC: + return true; + default: + return false; + } + } + static bool CommandTypeHasAddress(CommandType cmd) { + // Check CommandTypeIsValid before this, you will get false on an invalid command + switch(cmd) { + case CommandType::SDCC1_to_HostPC: + case CommandType::SDCC2_to_HostPC: + return true; + default: + return false; + } + } + static uint16_t CommandTypeDefinesLength(CommandType cmd) { + // Check CommandTypeIsValid before this, you will get 0 on an invalid command + switch(cmd) { + case CommandType::PlasmaStatusResponse: + return 2; + default: + return 0; // Length is defined by following bytes in message + } + } + + enum class PreprocessState { + SearchForCommand, + ParseAddress, + ParseLength, + GetData + }; + PreprocessState state = PreprocessState::SearchForCommand; + uint16_t currentCommandLength; + CommandType currentCommandType; + size_t currentReadIndex = 0; + + std::thread mainChannelReadThread; + void readTask(); +}; + +}; + #endif \ No newline at end of file diff --git a/communication/multichannelcommunication.cpp b/communication/multichannelcommunication.cpp index d7a187b..017e828 100644 --- a/communication/multichannelcommunication.cpp +++ b/communication/multichannelcommunication.cpp @@ -1,126 +1,127 @@ -#include "communication/include/multichannelcommunication.h" -#include "communication/include/messagedecoder.h" -#include "communication/include/packetizer.h" -#include -#include - -using namespace icsneo; - -void MultiChannelCommunication::spawnThreads() { - mainChannelReadThread = std::thread(&MultiChannelCommunication::readTask, this); -} - -void MultiChannelCommunication::joinThreads() { - if(mainChannelReadThread.joinable()) - mainChannelReadThread.join(); -} - -bool MultiChannelCommunication::sendCommand(Communication::Command cmd, std::vector arguments) { - std::vector bytes; - bytes.push_back((uint8_t)cmd); - for(auto& b : arguments) - bytes.push_back(b); - bytes.insert(bytes.begin(), 0xB | ((uint8_t)bytes.size() << 4)); - bytes = Communication::packetWrap(bytes); - bytes.insert(bytes.begin(), {(uint8_t)CommandType::HostPC_to_Vnet1, (uint8_t)bytes.size(), (uint8_t)(bytes.size() >> 8)}); - return rawWrite(bytes); -} - -void MultiChannelCommunication::readTask() { - bool readMore = true; - std::deque usbReadFifo; - std::vector readBytes; - std::vector payloadBytes; - Packetizer packetizer; - MessageDecoder decoder; - - while(!closing) { - if(readMore) { - readBytes.clear(); - if(impl->readWait(readBytes)) { - readMore = false; - usbReadFifo.insert(usbReadFifo.end(), std::make_move_iterator(readBytes.begin()), std::make_move_iterator(readBytes.end())); - } - } else { - switch(state) { - case PreprocessState::SearchForCommand: - if(usbReadFifo.size() < 1) { - readMore = true; - continue; - } - - currentCommandType = (CommandType)usbReadFifo[0]; - - if(!CommandTypeIsValid(currentCommandType)) { - std::cout << "cnv" << std::hex << (int)currentCommandType << ' ' << std::dec; - usbReadFifo.pop_front(); - continue; - } - - currentReadIndex = 1; - - if(CommandTypeHasAddress(currentCommandType)) { - state = PreprocessState::ParseAddress; - continue; // No commands which define an address also define a length, so we can just continue from there - } - - currentCommandLength = CommandTypeDefinesLength(currentCommandType); - if(currentCommandLength == 0) { - state = PreprocessState::ParseLength; - continue; - } - - state = PreprocessState::GetData; - continue; - case PreprocessState::ParseAddress: - // The address is represented by a 4 byte little endian - // Don't care about it yet - currentReadIndex += 4; - // Intentionally fall through - case PreprocessState::ParseLength: - state = PreprocessState::ParseLength; // Set state in case we've fallen through, but later need to go around again - - if(usbReadFifo.size() < currentReadIndex + 2) { // Come back we have more data - readMore = true; - continue; - } - - // The length is represented by a 2 byte little endian - currentCommandLength = usbReadFifo[currentReadIndex++]; - currentCommandLength |= usbReadFifo[currentReadIndex++] << 8; - // Intentionally fall through - case PreprocessState::GetData: - state = PreprocessState::GetData; // Set state in case we've fallen through, but later need to go around again - - if(usbReadFifo.size() <= currentReadIndex + currentCommandLength) { // Come back we have more data - readMore = true; - continue; - } - - for(auto i = 0; i < currentReadIndex; i++) - usbReadFifo.pop_front(); - - payloadBytes.clear(); - payloadBytes.resize(currentCommandLength); - for(auto i = 0; i < currentCommandLength; i++) { - payloadBytes[i] = usbReadFifo[0]; - usbReadFifo.pop_front(); - } - - if(packetizer.input(payloadBytes)) { - for(auto& packet : packetizer.output()) { - auto msg = decoder.decodePacket(packet); - for(auto& cb : messageCallbacks) { // We might have closed while reading or processing - if(!closing) { - cb.second.callIfMatch(msg); - } - } - } - } - - state = PreprocessState::SearchForCommand; - } - } - - } +#include "communication/include/multichannelcommunication.h" +#include "communication/include/command.h" +#include "communication/include/messagedecoder.h" +#include "communication/include/packetizer.h" +#include +#include + +using namespace icsneo; + +void MultiChannelCommunication::spawnThreads() { + mainChannelReadThread = std::thread(&MultiChannelCommunication::readTask, this); +} + +void MultiChannelCommunication::joinThreads() { + if(mainChannelReadThread.joinable()) + mainChannelReadThread.join(); +} + +bool MultiChannelCommunication::sendCommand(Command cmd, std::vector arguments) { + std::vector bytes; + bytes.push_back((uint8_t)cmd); + for(auto& b : arguments) + bytes.push_back(b); + bytes.insert(bytes.begin(), 0xB | ((uint8_t)bytes.size() << 4)); + bytes = Communication::packetWrap(bytes); + bytes.insert(bytes.begin(), {(uint8_t)CommandType::HostPC_to_Vnet1, (uint8_t)bytes.size(), (uint8_t)(bytes.size() >> 8)}); + return rawWrite(bytes); +} + +void MultiChannelCommunication::readTask() { + bool readMore = true; + std::deque usbReadFifo; + std::vector readBytes; + std::vector payloadBytes; + Packetizer packetizer; + MessageDecoder decoder; + + while(!closing) { + if(readMore) { + readBytes.clear(); + if(impl->readWait(readBytes)) { + readMore = false; + usbReadFifo.insert(usbReadFifo.end(), std::make_move_iterator(readBytes.begin()), std::make_move_iterator(readBytes.end())); + } + } else { + switch(state) { + case PreprocessState::SearchForCommand: + if(usbReadFifo.size() < 1) { + readMore = true; + continue; + } + + currentCommandType = (CommandType)usbReadFifo[0]; + + if(!CommandTypeIsValid(currentCommandType)) { + std::cout << "cnv" << std::hex << (int)currentCommandType << ' ' << std::dec; + usbReadFifo.pop_front(); + continue; + } + + currentReadIndex = 1; + + if(CommandTypeHasAddress(currentCommandType)) { + state = PreprocessState::ParseAddress; + continue; // No commands which define an address also define a length, so we can just continue from there + } + + currentCommandLength = CommandTypeDefinesLength(currentCommandType); + if(currentCommandLength == 0) { + state = PreprocessState::ParseLength; + continue; + } + + state = PreprocessState::GetData; + continue; + case PreprocessState::ParseAddress: + // The address is represented by a 4 byte little endian + // Don't care about it yet + currentReadIndex += 4; + // Intentionally fall through + case PreprocessState::ParseLength: + state = PreprocessState::ParseLength; // Set state in case we've fallen through, but later need to go around again + + if(usbReadFifo.size() < currentReadIndex + 2) { // Come back we have more data + readMore = true; + continue; + } + + // The length is represented by a 2 byte little endian + currentCommandLength = usbReadFifo[currentReadIndex++]; + currentCommandLength |= usbReadFifo[currentReadIndex++] << 8; + // Intentionally fall through + case PreprocessState::GetData: + state = PreprocessState::GetData; // Set state in case we've fallen through, but later need to go around again + + if(usbReadFifo.size() <= currentReadIndex + currentCommandLength) { // Come back we have more data + readMore = true; + continue; + } + + for(auto i = 0; i < currentReadIndex; i++) + usbReadFifo.pop_front(); + + payloadBytes.clear(); + payloadBytes.resize(currentCommandLength); + for(auto i = 0; i < currentCommandLength; i++) { + payloadBytes[i] = usbReadFifo[0]; + usbReadFifo.pop_front(); + } + + if(packetizer.input(payloadBytes)) { + for(auto& packet : packetizer.output()) { + auto msg = decoder.decodePacket(packet); + for(auto& cb : messageCallbacks) { // We might have closed while reading or processing + if(!closing) { + cb.second.callIfMatch(msg); + } + } + } + } + + state = PreprocessState::SearchForCommand; + } + } + + } } \ No newline at end of file diff --git a/device/device.cpp b/device/device.cpp index 64cb091..cf03cb7 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -1,157 +1,154 @@ -#include "include/device.h" -#include "communication/include/messagecallback.h" -#include -#include -#include - -using namespace icsneo; - -static const uint8_t fromBase36Table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 }; - -static const char toBase36Table[36] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', - 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; - -static const uint32_t toBase36Powers[7] = { 1, 36, 1296, 46656, 1679616, 60466176, 2176782336 }; - -#define MIN_BASE36_SERIAL (16796160) -#define MAX_SERIAL (2176782335) - -std::string Device::SerialNumToString(uint32_t serial) { - if(serial == 0 || serial > MAX_SERIAL) - return "0"; - - std::stringstream ss; - if(serial >= MIN_BASE36_SERIAL) { - for (auto i = 5; i >= 0; i--) { - ss << toBase36Table[serial / toBase36Powers[i]]; - serial = serial % toBase36Powers[i]; - } - } else { - ss << serial; - } - return ss.str(); -} - -uint32_t Device::SerialStringToNum(const std::string& serial) { - if(Device::SerialStringIsNumeric(serial)) { - try { - return std::stoi(serial); - } catch(...) { - return 0; - } - } - - if(serial.length() != 6) - return 0; // Non-numeric serial numbers should be 6 characters - - uint32_t ret = 0; - for (auto i = 0; i < 6; i++) { - ret *= 36; - ret += fromBase36Table[(unsigned char)serial[i]]; - } - return ret; -} - -bool Device::SerialStringIsNumeric(const std::string& serial) { - if(serial.length() == 0) - return false; - - if(serial.length() == 1) - return isdigit(serial[0]); - - // Check the first two characters, at least one should be a character if we need to do a base36 conversion - return isdigit(serial[0]) && isdigit(serial[1]); -} - -void Device::enableMessagePolling() { - if(messagePollingCallbackID != 0) // We are already polling - return; - - messagePollingCallbackID = com->addMessageCallback(MessageCallback([this](std::shared_ptr message) { - pollingContainer.enqueue(message); - enforcePollingMessageLimit(); - })); -} - -bool Device::disableMessagePolling() { - if(messagePollingCallbackID == 0) - return true; // Not currently polling - - auto ret = com->removeMessageCallback(messagePollingCallbackID); - getMessages(); // Flush any messages still in the container - messagePollingCallbackID = 0; - return ret; -} - -std::vector> Device::getMessages() { - std::vector> ret; - getMessages(ret); - return ret; -} - -bool Device::getMessages(std::vector>& container, size_t limit) { - // A limit of zero indicates no limit - if(limit == 0) - limit = (size_t)-1; - - if(limit > (pollingContainer.size_approx() + 4)) - limit = (pollingContainer.size_approx() + 4); - - if(container.capacity() < limit) - container.resize(limit); - - size_t actuallyRead = pollingContainer.try_dequeue_bulk(container.data(), limit); - - container.resize(actuallyRead); - - return true; -} - -void Device::enforcePollingMessageLimit() { - while(pollingContainer.size_approx() > pollingMessageLimit) { - std::shared_ptr throwAway; - pollingContainer.try_dequeue(throwAway); - // TODO Flag an error for the user! - } -} - -bool Device::open() { - if(!com) - return false; - - if(settings) - settings->refresh(); - - return com->open(); -} - -bool Device::close() { - if(!com) - return false; - - settings = nullptr; - - return com->close(); -} - -bool Device::goOnline() { - std::string serial; - while(!com->getSerialNumberSync(serial)) { - std::cout << "Serial number not here yet" << std::endl; - } - - if(!com->sendCommand(Communication::Command::EnableNetworkCommunication, true)) - return false; - - // if(!com->sendCommand(Communication::Command::RequestSerialNumber)) - // return false; - - return online = true; -} - -bool Device::goOffline() { - return com->sendCommand(Communication::Command::EnableNetworkCommunication, false); +#include "include/device.h" +#include "communication/include/command.h" +#include +#include +#include + +using namespace icsneo; + +static const uint8_t fromBase36Table[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 }; + +static const char toBase36Table[36] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; + +static const uint32_t toBase36Powers[7] = { 1, 36, 1296, 46656, 1679616, 60466176, 2176782336 }; + +#define MIN_BASE36_SERIAL (16796160) +#define MAX_SERIAL (2176782335) + +std::string Device::SerialNumToString(uint32_t serial) { + if(serial == 0 || serial > MAX_SERIAL) + return "0"; + + std::stringstream ss; + if(serial >= MIN_BASE36_SERIAL) { + for (auto i = 5; i >= 0; i--) { + ss << toBase36Table[serial / toBase36Powers[i]]; + serial = serial % toBase36Powers[i]; + } + } else { + ss << serial; + } + return ss.str(); +} + +uint32_t Device::SerialStringToNum(const std::string& serial) { + if(Device::SerialStringIsNumeric(serial)) { + try { + return std::stoi(serial); + } catch(...) { + return 0; + } + } + + if(serial.length() != 6) + return 0; // Non-numeric serial numbers should be 6 characters + + uint32_t ret = 0; + for (auto i = 0; i < 6; i++) { + ret *= 36; + ret += fromBase36Table[(unsigned char)serial[i]]; + } + return ret; +} + +bool Device::SerialStringIsNumeric(const std::string& serial) { + if(serial.length() == 0) + return false; + + if(serial.length() == 1) + return isdigit(serial[0]); + + // Check the first two characters, at least one should be a character if we need to do a base36 conversion + return isdigit(serial[0]) && isdigit(serial[1]); +} + +void Device::enableMessagePolling() { + if(messagePollingCallbackID != 0) // We are already polling + return; + + messagePollingCallbackID = com->addMessageCallback(MessageCallback([this](std::shared_ptr message) { + pollingContainer.enqueue(message); + enforcePollingMessageLimit(); + })); +} + +bool Device::disableMessagePolling() { + if(messagePollingCallbackID == 0) + return true; // Not currently polling + + auto ret = com->removeMessageCallback(messagePollingCallbackID); + getMessages(); // Flush any messages still in the container + messagePollingCallbackID = 0; + return ret; +} + +std::vector> Device::getMessages() { + std::vector> ret; + getMessages(ret); + return ret; +} + +bool Device::getMessages(std::vector>& container, size_t limit) { + // A limit of zero indicates no limit + if(limit == 0) + limit = (size_t)-1; + + if(limit > (pollingContainer.size_approx() + 4)) + limit = (pollingContainer.size_approx() + 4); + + if(container.capacity() < limit) + container.resize(limit); + + size_t actuallyRead = pollingContainer.try_dequeue_bulk(container.data(), limit); + + container.resize(actuallyRead); + + return true; +} + +void Device::enforcePollingMessageLimit() { + while(pollingContainer.size_approx() > pollingMessageLimit) { + std::shared_ptr throwAway; + pollingContainer.try_dequeue(throwAway); + // TODO Flag an error for the user! + } +} + +bool Device::open() { + if(!com) + return false; + + if(settings) + settings->refresh(); + + return com->open(); +} + +bool Device::close() { + if(!com) + return false; + + settings = nullptr; + + return com->close(); +} + +bool Device::goOnline() { + if(!com->sendCommand(Command::EnableNetworkCommunication, true)) + return false; + + online = true; + return true; +} + +bool Device::goOffline() { + if(!com->sendCommand(Command::EnableNetworkCommunication, false)) + return false; + + online = false; + return true; } \ No newline at end of file