Communication::Command refactored out of Communication

pull/4/head
Paul Hollinsky 2018-09-25 17:35:56 -04:00
parent 6284223650
commit d27b516894
6 changed files with 620 additions and 610 deletions

View File

@ -1,158 +1,159 @@
#include "communication/include/communication.h" #include "communication/include/communication.h"
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <queue> #include <queue>
#include <iomanip> #include <iomanip>
#include <cstring> #include <cstring>
#include <mutex> #include <mutex>
#include <condition_variable> #include <condition_variable>
#include "communication/include/messagedecoder.h" #include "communication/include/command.h"
#include "communication/include/packetizer.h" #include "communication/include/messagedecoder.h"
#include "communication/include/packetizer.h"
using namespace icsneo;
using namespace icsneo;
int Communication::messageCallbackIDCounter = 1;
int Communication::messageCallbackIDCounter = 1;
uint8_t Communication::ICSChecksum(const std::vector<uint8_t>& data) {
uint32_t checksum = 0; uint8_t Communication::ICSChecksum(const std::vector<uint8_t>& data) {
for(auto i = 0; i < data.size(); i++) uint32_t checksum = 0;
checksum += data[i]; for(auto i = 0; i < data.size(); i++)
checksum = ~checksum; checksum += data[i];
checksum++; checksum = ~checksum;
return (uint8_t)checksum; checksum++;
} return (uint8_t)checksum;
}
std::vector<uint8_t>& Communication::packetWrap(std::vector<uint8_t>& data, bool addChecksum) {
if(addChecksum) std::vector<uint8_t>& Communication::packetWrap(std::vector<uint8_t>& data, bool addChecksum) {
data.push_back(ICSChecksum(data)); if(addChecksum)
data.insert(data.begin(), 0xAA); data.push_back(ICSChecksum(data));
if(align16bit && data.size() % 2 == 1) data.insert(data.begin(), 0xAA);
data.push_back('A'); if(align16bit && data.size() % 2 == 1)
return data; data.push_back('A');
} return data;
}
bool Communication::open() {
if(isOpen) bool Communication::open() {
return true; if(isOpen)
return true;
spawnThreads();
isOpen = true; spawnThreads();
return impl->open(); isOpen = true;
} return impl->open();
}
void Communication::spawnThreads() {
readTaskThread = std::thread(&Communication::readTask, this); void Communication::spawnThreads() {
} readTaskThread = std::thread(&Communication::readTask, this);
}
void Communication::joinThreads() {
if(readTaskThread.joinable()) void Communication::joinThreads() {
readTaskThread.join(); if(readTaskThread.joinable())
} readTaskThread.join();
}
bool Communication::close() {
if(!isOpen) bool Communication::close() {
return false; if(!isOpen)
return false;
isOpen = false;
closing = true; isOpen = false;
joinThreads(); closing = true;
joinThreads();
return impl->close();
} return impl->close();
}
bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
return impl->write(Communication::packetWrap(bytes)); bool Communication::sendPacket(std::vector<uint8_t>& bytes) {
} return impl->write(Communication::packetWrap(bytes));
}
bool Communication::sendCommand(Communication::Command cmd, std::vector<uint8_t> arguments) {
std::vector<uint8_t> bytes; bool Communication::sendCommand(Command cmd, std::vector<uint8_t> arguments) {
bytes.push_back((uint8_t)cmd); std::vector<uint8_t> bytes;
for(auto& b : arguments) bytes.push_back((uint8_t)cmd);
bytes.push_back(b); for(auto& b : arguments)
bytes.insert(bytes.begin(), (uint8_t)Network::NetID::Main51 | ((uint8_t)bytes.size() << 4)); bytes.push_back(b);
return sendPacket(bytes); bytes.insert(bytes.begin(), (uint8_t)Network::NetID::Main51 | ((uint8_t)bytes.size() << 4));
} return sendPacket(bytes);
}
bool Communication::getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout) {
sendCommand(Command::GetSettings); bool Communication::getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout) {
std::shared_ptr<Message> msg = waitForMessageSync(MessageFilter(Network::NetID::RED_READ_BAUD_SETTINGS), timeout); sendCommand(Command::GetSettings);
if(!msg) std::shared_ptr<Message> msg = waitForMessageSync(MessageFilter(Network::NetID::RED_READ_BAUD_SETTINGS), timeout);
return false; if(!msg)
return false;
data = std::move(msg->data);
return true; data = std::move(msg->data);
} return true;
}
bool Communication::getSerialNumberSync(std::string& serial, std::chrono::milliseconds timeout) {
sendCommand(Command::RequestSerialNumber); bool Communication::getSerialNumberSync(std::string& serial, std::chrono::milliseconds timeout) {
std::shared_ptr<Message> msg = waitForMessageSync(MessageFilter(Network::NetID::RED_OLDFORMAT), timeout); sendCommand(Command::RequestSerialNumber);
if(!msg) std::shared_ptr<Message> msg = waitForMessageSync(MessageFilter(Network::NetID::RED_OLDFORMAT), timeout);
return false; 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 << "Got " << msg->data.size() << " bytes" << std::endl;
std::cout << std::hex << (int)msg->data[i] << ' ' << std::dec; for(size_t i = 0; i < msg->data.size(); i++) {
if(i % 16 == 15) std::cout << std::hex << (int)msg->data[i] << ' ' << std::dec;
std::cout << std::endl; if(i % 16 == 15)
} std::cout << std::endl;
return true; }
} return true;
}
int Communication::addMessageCallback(const MessageCallback& cb) {
messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb)); int Communication::addMessageCallback(const MessageCallback& cb) {
return messageCallbackIDCounter++; messageCallbacks.insert(std::make_pair(messageCallbackIDCounter, cb));
} return messageCallbackIDCounter++;
}
bool Communication::removeMessageCallback(int id) {
try { bool Communication::removeMessageCallback(int id) {
messageCallbacks.erase(id); try {
return true; messageCallbacks.erase(id);
} catch(...) { return true;
return false; } catch(...) {
} return false;
} }
}
std::shared_ptr<Message> Communication::waitForMessageSync(MessageFilter f, std::chrono::milliseconds timeout) {
std::mutex m; std::shared_ptr<Message> Communication::waitForMessageSync(MessageFilter f, std::chrono::milliseconds timeout) {
std::condition_variable cv; std::mutex m;
std::shared_ptr<Message> returnedMessage; std::condition_variable cv;
int cb = addMessageCallback(MessageCallback([&m, &returnedMessage, &cv](std::shared_ptr<Message> message) { std::shared_ptr<Message> returnedMessage;
{ int cb = addMessageCallback(MessageCallback([&m, &returnedMessage, &cv](std::shared_ptr<Message> message) {
std::lock_guard<std::mutex> lk(m); {
returnedMessage = message; std::lock_guard<std::mutex> lk(m);
} returnedMessage = message;
cv.notify_one(); }
}, f)); cv.notify_one();
}, f));
// We have now added the callback, wait for it to return from the other thread
std::unique_lock<std::mutex> lk(m); // We have now added the callback, wait for it to return from the other thread
cv.wait_for(lk, timeout, [&returnedMessage]{ return !!returnedMessage; }); // `!!shared_ptr` checks if the ptr has a value std::unique_lock<std::mutex> 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); // 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; // 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<uint8_t> readBytes; void Communication::readTask() {
Packetizer packetizer; std::vector<uint8_t> readBytes;
MessageDecoder decoder; Packetizer packetizer;
MessageDecoder decoder;
while(!closing) {
readBytes.clear(); while(!closing) {
if(impl->readWait(readBytes)) { readBytes.clear();
if(packetizer.input(readBytes)) { if(impl->readWait(readBytes)) {
for(auto& packet : packetizer.output()) { if(packetizer.input(readBytes)) {
auto msg = decoder.decodePacket(packet); for(auto& packet : packetizer.output()) {
for(auto& cb : messageCallbacks) { // We might have closed while reading or processing auto msg = decoder.decodePacket(packet);
if(!closing) { for(auto& cb : messageCallbacks) { // We might have closed while reading or processing
cb.second.callIfMatch(msg); if(!closing) {
} cb.second.callIfMatch(msg);
} }
} }
} }
} }
} }
} }
}

View File

@ -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

View File

@ -1,71 +1,65 @@
#ifndef __COMMUNICATION_H_ #ifndef __COMMUNICATION_H_
#define __COMMUNICATION_H_ #define __COMMUNICATION_H_
#include "communication/include/icommunication.h" #include "communication/include/icommunication.h"
#include "communication/include/network.h" #include "communication/include/command.h"
#include "communication/include/messagecallback.h" #include "communication/include/network.h"
#include <memory> #include "communication/include/messagecallback.h"
#include <vector> #include <memory>
#include <atomic> #include <vector>
#include <thread> #include <atomic>
#include <queue> #include <thread>
#include <map> #include <queue>
#include <map>
namespace icsneo {
namespace icsneo {
class Communication {
public: class Communication {
static uint8_t ICSChecksum(const std::vector<uint8_t>& data); public:
static uint8_t ICSChecksum(const std::vector<uint8_t>& data);
Communication(std::shared_ptr<ICommunication> com) : impl(com) {}
virtual ~Communication() { close(); } Communication(std::shared_ptr<ICommunication> com) : impl(com) {}
virtual ~Communication() { close(); }
bool open();
bool close(); bool open();
virtual void spawnThreads(); bool close();
virtual void joinThreads(); virtual void spawnThreads();
bool rawWrite(const std::vector<uint8_t>& bytes) { return impl->write(bytes); } virtual void joinThreads();
std::vector<uint8_t>& packetWrap(std::vector<uint8_t>& data, bool addChecksum = true); bool rawWrite(const std::vector<uint8_t>& bytes) { return impl->write(bytes); }
bool sendPacket(std::vector<uint8_t>& bytes); std::vector<uint8_t>& packetWrap(std::vector<uint8_t>& data, bool addChecksum = true);
bool sendPacket(std::vector<uint8_t>& bytes);
enum class Command : uint8_t {
EnableNetworkCommunication = 0x07, virtual bool sendCommand(Command cmd, bool boolean) { return sendCommand(cmd, std::vector<uint8_t>({ (uint8_t)boolean })); }
RequestSerialNumber = 0xA1, virtual bool sendCommand(Command cmd, std::vector<uint8_t> arguments = {});
SetSettings = 0xA4, // Previously known as RED_CMD_SET_BAUD_REQ, follow up with SaveSettings to write to EEPROM bool getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
GetSettings = 0xA5, // Previously known as RED_CMD_READ_BAUD_REQ bool getSerialNumberSync(std::string& serial, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
SaveSettings = 0xA6
}; int addMessageCallback(const MessageCallback& cb);
virtual bool sendCommand(Command cmd, bool boolean) { return sendCommand(cmd, std::vector<uint8_t>({ (uint8_t)boolean })); } bool removeMessageCallback(int id);
virtual bool sendCommand(Command cmd, std::vector<uint8_t> arguments = {}); std::shared_ptr<Message> waitForMessageSync(MessageFilter f = MessageFilter(), std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
bool getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout = std::chrono::milliseconds(50));
bool getSerialNumberSync(std::string& serial, std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); void setAlign16Bit(bool enable) { align16bit = enable; }
int addMessageCallback(const MessageCallback& cb); class Packet {
bool removeMessageCallback(int id); public:
std::shared_ptr<Message> waitForMessageSync(MessageFilter f = MessageFilter(), std::chrono::milliseconds timeout = std::chrono::milliseconds(50)); Network network;
std::vector<uint8_t> data;
void setAlign16Bit(bool enable) { align16bit = enable; } };
class Packet { protected:
public: std::shared_ptr<ICommunication> impl;
Network network; static int messageCallbackIDCounter;
std::vector<uint8_t> data; std::map<int, MessageCallback> messageCallbacks;
}; std::atomic<bool> closing{false};
protected: private:
std::shared_ptr<ICommunication> impl; bool isOpen = false;
static int messageCallbackIDCounter; bool align16bit = true; // Not needed for Gigalog, Galaxy, etc and newer
std::map<int, MessageCallback> messageCallbacks;
std::atomic<bool> closing{false}; std::thread readTaskThread;
void readTask();
private: };
bool isOpen = false;
bool align16bit = true; // Not needed for Gigalog, Galaxy, etc and newer };
std::thread readTaskThread;
void readTask();
};
};
#endif #endif

View File

@ -1,102 +1,103 @@
#ifndef __MULTICHANNELCOMMUNICATION_H_ #ifndef __MULTICHANNELCOMMUNICATION_H_
#define __MULTICHANNELCOMMUNICATION_H_ #define __MULTICHANNELCOMMUNICATION_H_
#include "communication/include/communication.h" #include "communication/include/communication.h"
#include "communication/include/icommunication.h" #include "communication/include/icommunication.h"
#include "communication/include/command.h"
namespace icsneo {
namespace icsneo {
class MultiChannelCommunication : public Communication {
public: class MultiChannelCommunication : public Communication {
MultiChannelCommunication(std::shared_ptr<ICommunication> com) : Communication(com) {} public:
void spawnThreads(); MultiChannelCommunication(std::shared_ptr<ICommunication> com) : Communication(com) {}
void joinThreads(); void spawnThreads();
bool sendCommand(Communication::Command cmd, std::vector<uint8_t> arguments); void joinThreads();
bool sendCommand(Command cmd, std::vector<uint8_t> arguments);
protected:
bool preprocessPacket(std::deque<uint8_t>& usbReadFifo); protected:
bool preprocessPacket(std::deque<uint8_t>& usbReadFifo);
private:
enum class CommandType : uint8_t { private:
PlasmaReadRequest = 0x10, // Status read request to HSC enum class CommandType : uint8_t {
PlasmaStatusResponse = 0x11, // Status response by HSC PlasmaReadRequest = 0x10, // Status read request to HSC
HostPC_to_Vnet1 = 0x20, // Host PC data to Vnet module-1 PlasmaStatusResponse = 0x11, // Status response by HSC
Vnet1_to_HostPC = 0x21, // Vnet module-1 data to host PC HostPC_to_Vnet1 = 0x20, // Host PC data to Vnet module-1
HostPC_to_Vnet2 = 0x30, // Host PC data to Vnet module-2 Vnet1_to_HostPC = 0x21, // Vnet module-1 data to host PC
Vnet2_to_HostPC = 0x31, // Vnet module-2 data to host PC HostPC_to_Vnet2 = 0x30, // Host PC data to Vnet module-2
HostPC_to_Vnet3 = 0x40, // Host PC data to Vnet module-3 Vnet2_to_HostPC = 0x31, // Vnet module-2 data to host PC
Vnet3_to_HostPC = 0x41, // Vnet module-3 data to host PC HostPC_to_Vnet3 = 0x40, // Host PC data to Vnet module-3
HostPC_to_SDCC1 = 0x50, // Host PC data to write to SDCC-1 Vnet3_to_HostPC = 0x41, // Vnet module-3 data to host PC
HostPC_from_SDCC1 = 0x51, // Host PC wants data read from SDCC-1 HostPC_to_SDCC1 = 0x50, // Host PC data to write to SDCC-1
SDCC1_to_HostPC = 0x52, // SDCC-1 data to host PC HostPC_from_SDCC1 = 0x51, // Host PC wants data read from SDCC-1
HostPC_to_SDCC2 = 0x60, // Host PC data to write to SDCC-2 SDCC1_to_HostPC = 0x52, // SDCC-1 data to host PC
HostPC_from_SDCC2 = 0x61, // Host PC wants data read from SDCC-2 HostPC_to_SDCC2 = 0x60, // Host PC data to write to SDCC-2
SDCC2_to_HostPC = 0x62, // SDCC-2 data to host PC HostPC_from_SDCC2 = 0x61, // Host PC wants data read from SDCC-2
PC_to_LSOC = 0x70, // Host PC data to LSOCC SDCC2_to_HostPC = 0x62, // SDCC-2 data to host PC
LSOCC_to_PC = 0x71, // LSOCC data to host PC PC_to_LSOC = 0x70, // Host PC data to LSOCC
HostPC_to_Microblaze = 0x80, // Host PC data to microblaze processor LSOCC_to_PC = 0x71, // LSOCC data to host PC
Microblaze_to_HostPC = 0x81 // Microblaze processor 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) { static bool CommandTypeIsValid(CommandType cmd) {
case CommandType::PlasmaReadRequest: switch(cmd) {
case CommandType::PlasmaStatusResponse: case CommandType::PlasmaReadRequest:
case CommandType::HostPC_to_Vnet1: case CommandType::PlasmaStatusResponse:
case CommandType::Vnet1_to_HostPC: case CommandType::HostPC_to_Vnet1:
case CommandType::HostPC_to_Vnet2: case CommandType::Vnet1_to_HostPC:
case CommandType::Vnet2_to_HostPC: case CommandType::HostPC_to_Vnet2:
case CommandType::HostPC_to_Vnet3: case CommandType::Vnet2_to_HostPC:
case CommandType::Vnet3_to_HostPC: case CommandType::HostPC_to_Vnet3:
case CommandType::HostPC_to_SDCC1: case CommandType::Vnet3_to_HostPC:
case CommandType::HostPC_from_SDCC1: case CommandType::HostPC_to_SDCC1:
case CommandType::SDCC1_to_HostPC: case CommandType::HostPC_from_SDCC1:
case CommandType::HostPC_to_SDCC2: case CommandType::SDCC1_to_HostPC:
case CommandType::HostPC_from_SDCC2: case CommandType::HostPC_to_SDCC2:
case CommandType::SDCC2_to_HostPC: case CommandType::HostPC_from_SDCC2:
case CommandType::PC_to_LSOC: case CommandType::SDCC2_to_HostPC:
case CommandType::LSOCC_to_PC: case CommandType::PC_to_LSOC:
case CommandType::HostPC_to_Microblaze: case CommandType::LSOCC_to_PC:
case CommandType::Microblaze_to_HostPC: case CommandType::HostPC_to_Microblaze:
return true; case CommandType::Microblaze_to_HostPC:
default: return true;
return false; default:
} return false;
} }
static bool CommandTypeHasAddress(CommandType cmd) { }
// Check CommandTypeIsValid before this, you will get false on an invalid command static bool CommandTypeHasAddress(CommandType cmd) {
switch(cmd) { // Check CommandTypeIsValid before this, you will get false on an invalid command
case CommandType::SDCC1_to_HostPC: switch(cmd) {
case CommandType::SDCC2_to_HostPC: case CommandType::SDCC1_to_HostPC:
return true; case CommandType::SDCC2_to_HostPC:
default: return true;
return false; default:
} return false;
} }
static uint16_t CommandTypeDefinesLength(CommandType cmd) { }
// Check CommandTypeIsValid before this, you will get 0 on an invalid command static uint16_t CommandTypeDefinesLength(CommandType cmd) {
switch(cmd) { // Check CommandTypeIsValid before this, you will get 0 on an invalid command
case CommandType::PlasmaStatusResponse: switch(cmd) {
return 2; case CommandType::PlasmaStatusResponse:
default: return 2;
return 0; // Length is defined by following bytes in message default:
} return 0; // Length is defined by following bytes in message
} }
}
enum class PreprocessState {
SearchForCommand, enum class PreprocessState {
ParseAddress, SearchForCommand,
ParseLength, ParseAddress,
GetData ParseLength,
}; GetData
PreprocessState state = PreprocessState::SearchForCommand; };
uint16_t currentCommandLength; PreprocessState state = PreprocessState::SearchForCommand;
CommandType currentCommandType; uint16_t currentCommandLength;
size_t currentReadIndex = 0; CommandType currentCommandType;
size_t currentReadIndex = 0;
std::thread mainChannelReadThread;
void readTask(); std::thread mainChannelReadThread;
}; void readTask();
};
};
};
#endif #endif

View File

@ -1,126 +1,127 @@
#include "communication/include/multichannelcommunication.h" #include "communication/include/multichannelcommunication.h"
#include "communication/include/messagedecoder.h" #include "communication/include/command.h"
#include "communication/include/packetizer.h" #include "communication/include/messagedecoder.h"
#include <iostream> #include "communication/include/packetizer.h"
#include <iomanip> #include <iostream>
#include <iomanip>
using namespace icsneo;
using namespace icsneo;
void MultiChannelCommunication::spawnThreads() {
mainChannelReadThread = std::thread(&MultiChannelCommunication::readTask, this); void MultiChannelCommunication::spawnThreads() {
} mainChannelReadThread = std::thread(&MultiChannelCommunication::readTask, this);
}
void MultiChannelCommunication::joinThreads() {
if(mainChannelReadThread.joinable()) void MultiChannelCommunication::joinThreads() {
mainChannelReadThread.join(); if(mainChannelReadThread.joinable())
} mainChannelReadThread.join();
}
bool MultiChannelCommunication::sendCommand(Communication::Command cmd, std::vector<uint8_t> arguments) {
std::vector<uint8_t> bytes; bool MultiChannelCommunication::sendCommand(Command cmd, std::vector<uint8_t> arguments) {
bytes.push_back((uint8_t)cmd); std::vector<uint8_t> bytes;
for(auto& b : arguments) bytes.push_back((uint8_t)cmd);
bytes.push_back(b); for(auto& b : arguments)
bytes.insert(bytes.begin(), 0xB | ((uint8_t)bytes.size() << 4)); bytes.push_back(b);
bytes = Communication::packetWrap(bytes); bytes.insert(bytes.begin(), 0xB | ((uint8_t)bytes.size() << 4));
bytes.insert(bytes.begin(), {(uint8_t)CommandType::HostPC_to_Vnet1, (uint8_t)bytes.size(), (uint8_t)(bytes.size() >> 8)}); bytes = Communication::packetWrap(bytes);
return rawWrite(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; void MultiChannelCommunication::readTask() {
std::deque<uint8_t> usbReadFifo; bool readMore = true;
std::vector<uint8_t> readBytes; std::deque<uint8_t> usbReadFifo;
std::vector<uint8_t> payloadBytes; std::vector<uint8_t> readBytes;
Packetizer packetizer; std::vector<uint8_t> payloadBytes;
MessageDecoder decoder; Packetizer packetizer;
MessageDecoder decoder;
while(!closing) {
if(readMore) { while(!closing) {
readBytes.clear(); if(readMore) {
if(impl->readWait(readBytes)) { readBytes.clear();
readMore = false; if(impl->readWait(readBytes)) {
usbReadFifo.insert(usbReadFifo.end(), std::make_move_iterator(readBytes.begin()), std::make_move_iterator(readBytes.end())); readMore = false;
} usbReadFifo.insert(usbReadFifo.end(), std::make_move_iterator(readBytes.begin()), std::make_move_iterator(readBytes.end()));
} else { }
switch(state) { } else {
case PreprocessState::SearchForCommand: switch(state) {
if(usbReadFifo.size() < 1) { case PreprocessState::SearchForCommand:
readMore = true; if(usbReadFifo.size() < 1) {
continue; readMore = true;
} continue;
}
currentCommandType = (CommandType)usbReadFifo[0];
currentCommandType = (CommandType)usbReadFifo[0];
if(!CommandTypeIsValid(currentCommandType)) {
std::cout << "cnv" << std::hex << (int)currentCommandType << ' ' << std::dec; if(!CommandTypeIsValid(currentCommandType)) {
usbReadFifo.pop_front(); std::cout << "cnv" << std::hex << (int)currentCommandType << ' ' << std::dec;
continue; usbReadFifo.pop_front();
} continue;
}
currentReadIndex = 1;
currentReadIndex = 1;
if(CommandTypeHasAddress(currentCommandType)) {
state = PreprocessState::ParseAddress; if(CommandTypeHasAddress(currentCommandType)) {
continue; // No commands which define an address also define a length, so we can just continue from there 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) { currentCommandLength = CommandTypeDefinesLength(currentCommandType);
state = PreprocessState::ParseLength; if(currentCommandLength == 0) {
continue; state = PreprocessState::ParseLength;
} continue;
}
state = PreprocessState::GetData;
continue; state = PreprocessState::GetData;
case PreprocessState::ParseAddress: continue;
// The address is represented by a 4 byte little endian case PreprocessState::ParseAddress:
// Don't care about it yet // The address is represented by a 4 byte little endian
currentReadIndex += 4; // Don't care about it yet
// Intentionally fall through currentReadIndex += 4;
case PreprocessState::ParseLength: // Intentionally fall through
state = PreprocessState::ParseLength; // Set state in case we've fallen through, but later need to go around again 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; if(usbReadFifo.size() < currentReadIndex + 2) { // Come back we have more data
continue; readMore = true;
} continue;
}
// The length is represented by a 2 byte little endian
currentCommandLength = usbReadFifo[currentReadIndex++]; // The length is represented by a 2 byte little endian
currentCommandLength |= usbReadFifo[currentReadIndex++] << 8; currentCommandLength = usbReadFifo[currentReadIndex++];
// Intentionally fall through currentCommandLength |= usbReadFifo[currentReadIndex++] << 8;
case PreprocessState::GetData: // Intentionally fall through
state = PreprocessState::GetData; // Set state in case we've fallen through, but later need to go around again 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; if(usbReadFifo.size() <= currentReadIndex + currentCommandLength) { // Come back we have more data
continue; readMore = true;
} continue;
}
for(auto i = 0; i < currentReadIndex; i++)
usbReadFifo.pop_front(); for(auto i = 0; i < currentReadIndex; i++)
usbReadFifo.pop_front();
payloadBytes.clear();
payloadBytes.resize(currentCommandLength); payloadBytes.clear();
for(auto i = 0; i < currentCommandLength; i++) { payloadBytes.resize(currentCommandLength);
payloadBytes[i] = usbReadFifo[0]; for(auto i = 0; i < currentCommandLength; i++) {
usbReadFifo.pop_front(); payloadBytes[i] = usbReadFifo[0];
} usbReadFifo.pop_front();
}
if(packetizer.input(payloadBytes)) {
for(auto& packet : packetizer.output()) { if(packetizer.input(payloadBytes)) {
auto msg = decoder.decodePacket(packet); for(auto& packet : packetizer.output()) {
for(auto& cb : messageCallbacks) { // We might have closed while reading or processing auto msg = decoder.decodePacket(packet);
if(!closing) { for(auto& cb : messageCallbacks) { // We might have closed while reading or processing
cb.second.callIfMatch(msg); if(!closing) {
} cb.second.callIfMatch(msg);
} }
} }
} }
}
state = PreprocessState::SearchForCommand;
} state = PreprocessState::SearchForCommand;
} }
}
}
}
} }

View File

@ -1,157 +1,154 @@
#include "include/device.h" #include "include/device.h"
#include "communication/include/messagecallback.h" #include "communication/include/command.h"
#include <string.h> #include <string.h>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
using namespace icsneo; 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, 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, 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, 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 }; 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', 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' }; '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 }; static const uint32_t toBase36Powers[7] = { 1, 36, 1296, 46656, 1679616, 60466176, 2176782336 };
#define MIN_BASE36_SERIAL (16796160) #define MIN_BASE36_SERIAL (16796160)
#define MAX_SERIAL (2176782335) #define MAX_SERIAL (2176782335)
std::string Device::SerialNumToString(uint32_t serial) { std::string Device::SerialNumToString(uint32_t serial) {
if(serial == 0 || serial > MAX_SERIAL) if(serial == 0 || serial > MAX_SERIAL)
return "0"; return "0";
std::stringstream ss; std::stringstream ss;
if(serial >= MIN_BASE36_SERIAL) { if(serial >= MIN_BASE36_SERIAL) {
for (auto i = 5; i >= 0; i--) { for (auto i = 5; i >= 0; i--) {
ss << toBase36Table[serial / toBase36Powers[i]]; ss << toBase36Table[serial / toBase36Powers[i]];
serial = serial % toBase36Powers[i]; serial = serial % toBase36Powers[i];
} }
} else { } else {
ss << serial; ss << serial;
} }
return ss.str(); return ss.str();
} }
uint32_t Device::SerialStringToNum(const std::string& serial) { uint32_t Device::SerialStringToNum(const std::string& serial) {
if(Device::SerialStringIsNumeric(serial)) { if(Device::SerialStringIsNumeric(serial)) {
try { try {
return std::stoi(serial); return std::stoi(serial);
} catch(...) { } catch(...) {
return 0; return 0;
} }
} }
if(serial.length() != 6) if(serial.length() != 6)
return 0; // Non-numeric serial numbers should be 6 characters return 0; // Non-numeric serial numbers should be 6 characters
uint32_t ret = 0; uint32_t ret = 0;
for (auto i = 0; i < 6; i++) { for (auto i = 0; i < 6; i++) {
ret *= 36; ret *= 36;
ret += fromBase36Table[(unsigned char)serial[i]]; ret += fromBase36Table[(unsigned char)serial[i]];
} }
return ret; return ret;
} }
bool Device::SerialStringIsNumeric(const std::string& serial) { bool Device::SerialStringIsNumeric(const std::string& serial) {
if(serial.length() == 0) if(serial.length() == 0)
return false; return false;
if(serial.length() == 1) if(serial.length() == 1)
return isdigit(serial[0]); return isdigit(serial[0]);
// Check the first two characters, at least one should be a character if we need to do a base36 conversion // 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]); return isdigit(serial[0]) && isdigit(serial[1]);
} }
void Device::enableMessagePolling() { void Device::enableMessagePolling() {
if(messagePollingCallbackID != 0) // We are already polling if(messagePollingCallbackID != 0) // We are already polling
return; return;
messagePollingCallbackID = com->addMessageCallback(MessageCallback([this](std::shared_ptr<Message> message) { messagePollingCallbackID = com->addMessageCallback(MessageCallback([this](std::shared_ptr<Message> message) {
pollingContainer.enqueue(message); pollingContainer.enqueue(message);
enforcePollingMessageLimit(); enforcePollingMessageLimit();
})); }));
} }
bool Device::disableMessagePolling() { bool Device::disableMessagePolling() {
if(messagePollingCallbackID == 0) if(messagePollingCallbackID == 0)
return true; // Not currently polling return true; // Not currently polling
auto ret = com->removeMessageCallback(messagePollingCallbackID); auto ret = com->removeMessageCallback(messagePollingCallbackID);
getMessages(); // Flush any messages still in the container getMessages(); // Flush any messages still in the container
messagePollingCallbackID = 0; messagePollingCallbackID = 0;
return ret; return ret;
} }
std::vector<std::shared_ptr<Message>> Device::getMessages() { std::vector<std::shared_ptr<Message>> Device::getMessages() {
std::vector<std::shared_ptr<Message>> ret; std::vector<std::shared_ptr<Message>> ret;
getMessages(ret); getMessages(ret);
return ret; return ret;
} }
bool Device::getMessages(std::vector<std::shared_ptr<Message>>& container, size_t limit) { bool Device::getMessages(std::vector<std::shared_ptr<Message>>& container, size_t limit) {
// A limit of zero indicates no limit // A limit of zero indicates no limit
if(limit == 0) if(limit == 0)
limit = (size_t)-1; limit = (size_t)-1;
if(limit > (pollingContainer.size_approx() + 4)) if(limit > (pollingContainer.size_approx() + 4))
limit = (pollingContainer.size_approx() + 4); limit = (pollingContainer.size_approx() + 4);
if(container.capacity() < limit) if(container.capacity() < limit)
container.resize(limit); container.resize(limit);
size_t actuallyRead = pollingContainer.try_dequeue_bulk(container.data(), limit); size_t actuallyRead = pollingContainer.try_dequeue_bulk(container.data(), limit);
container.resize(actuallyRead); container.resize(actuallyRead);
return true; return true;
} }
void Device::enforcePollingMessageLimit() { void Device::enforcePollingMessageLimit() {
while(pollingContainer.size_approx() > pollingMessageLimit) { while(pollingContainer.size_approx() > pollingMessageLimit) {
std::shared_ptr<Message> throwAway; std::shared_ptr<Message> throwAway;
pollingContainer.try_dequeue(throwAway); pollingContainer.try_dequeue(throwAway);
// TODO Flag an error for the user! // TODO Flag an error for the user!
} }
} }
bool Device::open() { bool Device::open() {
if(!com) if(!com)
return false; return false;
if(settings) if(settings)
settings->refresh(); settings->refresh();
return com->open(); return com->open();
} }
bool Device::close() { bool Device::close() {
if(!com) if(!com)
return false; return false;
settings = nullptr; settings = nullptr;
return com->close(); return com->close();
} }
bool Device::goOnline() { bool Device::goOnline() {
std::string serial; if(!com->sendCommand(Command::EnableNetworkCommunication, true))
while(!com->getSerialNumberSync(serial)) { return false;
std::cout << "Serial number not here yet" << std::endl;
} online = true;
return true;
if(!com->sendCommand(Communication::Command::EnableNetworkCommunication, true)) }
return false;
bool Device::goOffline() {
// if(!com->sendCommand(Communication::Command::RequestSerialNumber)) if(!com->sendCommand(Command::EnableNetworkCommunication, false))
// return false; return false;
return online = true; online = false;
} return true;
bool Device::goOffline() {
return com->sendCommand(Communication::Command::EnableNetworkCommunication, false);
} }