remove
This commit is contained in:
parent
45800fce2e
commit
385367879f
@ -1,21 +0,0 @@
|
||||
#include "CustomStringUtilities.h"
|
||||
#include <iostream>
|
||||
|
||||
const char* CustomStringUtilities::typeOfWhitespacesReturn = " \t\n\r\f\v";
|
||||
const char* CustomStringUtilities::typeOfWhitespaces = " \t\r\f\v";
|
||||
|
||||
void CustomStringUtilities::removeAllWhitespaces(std::string& str, bool trimReturn) {
|
||||
const char* whitespaces = trimReturn ? typeOfWhitespacesReturn : typeOfWhitespaces;
|
||||
size_t pos, offset = 0;
|
||||
while ((pos = str.find_first_of(whitespaces, offset)) != std::string::npos) {
|
||||
str.erase(pos, 1);
|
||||
offset = pos;
|
||||
}
|
||||
}
|
||||
|
||||
void CustomStringUtilities::trim(std::string& str) {
|
||||
str.erase(str.find_last_not_of(typeOfWhitespaces) + 1);
|
||||
str.erase(0, str.find_first_not_of(typeOfWhitespaces));
|
||||
}
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
class CustomStringUtilities
|
||||
{
|
||||
private:
|
||||
static const char* typeOfWhitespaces;
|
||||
static const char* typeOfWhitespacesReturn;
|
||||
public:
|
||||
//remove whitespaces from the passed string
|
||||
static void removeAllWhitespaces(std::string& str, bool trimReturn = true);
|
||||
static void trim(std::string&);
|
||||
};
|
||||
|
@ -1,163 +0,0 @@
|
||||
#include "DataAcquisition.h"
|
||||
#include "easylogging++.h"
|
||||
#include <iomanip>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
std::atomic_bool modbusThreadRunning = false;
|
||||
std::atomic_bool modbusThreadCancelled = false;
|
||||
|
||||
//Modbus worker thread, lock and notification variable
|
||||
std::condition_variable cvar_modbus_queue;
|
||||
std::mutex mux_modbus_queue;
|
||||
|
||||
DataAcquisition::DataAcquisition() : dataModel(DataModel::Instance())
|
||||
{
|
||||
}
|
||||
|
||||
DataAcquisition::~DataAcquisition()
|
||||
{
|
||||
LOG(INFO) << "Wait until modbus worker queue finishes...";
|
||||
modbusThreadCancelled = true;
|
||||
cvar_modbus_queue.notify_one();
|
||||
if(modbusThread.size() != 0 && modbusThread[0].joinable()){
|
||||
modbusThread[0].join();
|
||||
modbusThread.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Modbus thread
|
||||
//Execute the enqueued modbus register requests
|
||||
void modbusWorkerThread(DataAcquisition* this_p) {
|
||||
LOG(INFO) << "Modbus Worker Thread started";
|
||||
auto& queue = this_p->getModbusQueue();
|
||||
auto& publishers = this_p->getPublishers();
|
||||
bool first = true;
|
||||
try {
|
||||
//vector of connections which failed to open
|
||||
std::vector<u_int> failedConnections;
|
||||
|
||||
while (1) {
|
||||
|
||||
if (modbusThreadCancelled)
|
||||
return;
|
||||
|
||||
|
||||
//If this is the first execution or the thread has
|
||||
//been notified after being in sleep mode
|
||||
if (queue.size() == 0 || first){
|
||||
first = false;
|
||||
failedConnections.clear();
|
||||
//No work to do, wait for something to be enqueued
|
||||
std::unique_lock<std::mutex> threadLock(mux_modbus_queue);
|
||||
|
||||
//Wait till notification (happens when some registers are enqueued
|
||||
cvar_modbus_queue.wait(threadLock);
|
||||
|
||||
if (modbusThreadCancelled)
|
||||
return;
|
||||
|
||||
//open all of the registered connections
|
||||
for(unsigned int i = 0; i < publishers.size(); i++){
|
||||
if(!publishers[i]->open()){
|
||||
//write id to list if connection failed
|
||||
failedConnections.push_back(publishers[i]->getID());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (modbusThreadCancelled)
|
||||
return;
|
||||
|
||||
//Get next parameter from the queue
|
||||
ParameterSpecification modbusParameter = queue.pop_front();
|
||||
|
||||
//Look if the connection is in the list of the failed connections, if so , skip this parameter
|
||||
if(std::find(failedConnections.begin(), failedConnections.end(), modbusParameter.connection->getID()) != failedConnections.end()){
|
||||
continue;
|
||||
}
|
||||
|
||||
//Skip parameter if not a reading parameter or connection is not open
|
||||
if(!modbusParameter.isReadable())
|
||||
continue;
|
||||
|
||||
switch (modbusParameter.length) {
|
||||
case 1:
|
||||
modbusParameter.connection->readBit(modbusParameter);
|
||||
break;
|
||||
case 8:
|
||||
modbusParameter.connection->readByte(modbusParameter);
|
||||
break;
|
||||
case 16:
|
||||
modbusParameter.connection->readRegister(modbusParameter);
|
||||
break;
|
||||
case 32:
|
||||
modbusParameter.connection->readDRegister(modbusParameter);
|
||||
break;
|
||||
case 64:
|
||||
modbusParameter.connection->readQRegister(modbusParameter);
|
||||
break;
|
||||
default:
|
||||
modbusParameter.connection->readBits(modbusParameter);
|
||||
}
|
||||
|
||||
if(modbusParameter.error){
|
||||
//Rading of Modbus Parameter was not successful
|
||||
LOG(WARNING) << std::dec
|
||||
<< "[Modbus " << modbusParameter.connection->getID() << ": " << modbusParameter.connection->getConnectionType() << "] "
|
||||
<< "Failed reading parameter "<< (int)modbusParameter.description
|
||||
<< " at 0x"
|
||||
<< std::hex << std::setfill('0') << std::setw(4)
|
||||
<< modbusParameter.address;
|
||||
}
|
||||
else{
|
||||
|
||||
//LOG(INFO) << std::dec
|
||||
// << "[Modbus " << modbusParameter.connection->getConnectionType() << " "<<
|
||||
// modbusParameter.connection->getID() << "]" << " Readed param " << (int)modbusParameter.description
|
||||
// << " value: " << value;
|
||||
|
||||
DataModel::Instance()->saveModbusParameter(std::move(modbusParameter));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
LOG(FATAL) << "Error in modbus access, shutting down modbus thread: " << e.what();
|
||||
}
|
||||
modbusThreadRunning = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void DataAcquisition::startModbusThread() {
|
||||
if (modbusThreadRunning == false)
|
||||
{
|
||||
modbusThreadRunning = true;
|
||||
modbusThread.push_back(std::thread(modbusWorkerThread, this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Registers publisher and moves ownership to DataAcquisition class
|
||||
void DataAcquisition::registerPublisher(std::unique_ptr<PublisherInterface> publisher) {
|
||||
publishers.push_back(std::move(publisher));
|
||||
}
|
||||
|
||||
void DataAcquisition::enqueuePublisherRegister(){
|
||||
for (size_t i = 0; i < publishers.size(); i++) {
|
||||
//Collects the alert modbus registers and enqueue them
|
||||
publishers[i]->enqueueReadingRegisters(modbusAccessQueue, Category::Alert);
|
||||
}
|
||||
for (size_t i = 0; i < publishers.size(); i++) {
|
||||
//Collects the condition modbus registers and enqueue them
|
||||
publishers[i]->enqueueReadingRegisters(modbusAccessQueue, Category::Condition);
|
||||
}
|
||||
cvar_modbus_queue.notify_one();
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
#include "DataModel.h"
|
||||
#include "PublisherInterface.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include "ts_queue.h"
|
||||
#include "modbus_interface_lib.h"
|
||||
|
||||
class DataAcquisition
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<DataModel>& dataModel;
|
||||
|
||||
//Modbus Connections for POC and Bender, can be serial or IP
|
||||
std::vector<std::unique_ptr<PublisherInterface>> publishers;
|
||||
|
||||
//Modbus thread
|
||||
std::vector<std::thread> modbusThread;
|
||||
|
||||
ts_queue<ParameterSpecification> modbusAccessQueue;
|
||||
|
||||
|
||||
public:
|
||||
DataAcquisition();
|
||||
~DataAcquisition();
|
||||
|
||||
|
||||
void startModbusThread();
|
||||
ts_queue<ParameterSpecification>& getModbusQueue() { return modbusAccessQueue; }
|
||||
|
||||
//void registerPublisher(Publisher* publisher);
|
||||
void registerPublisher(std::unique_ptr<PublisherInterface> publisher);
|
||||
|
||||
std::vector<std::unique_ptr<PublisherInterface>>& getPublishers() { return publishers; }
|
||||
|
||||
void enqueuePublisherRegister();
|
||||
};
|
@ -1,364 +0,0 @@
|
||||
#include "DataModel.h"
|
||||
#include <easylogging++.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include "SystemConfig.h"
|
||||
#include <charconv>
|
||||
#include <mutex>
|
||||
|
||||
#include "PublisherPowercenter.h"
|
||||
#include "PublisherBenderRcm.h"
|
||||
|
||||
std::chrono::milliseconds timeoutMS = std::chrono::milliseconds(10'000);
|
||||
|
||||
long DataModel::permanentParamHistory = 0;
|
||||
int DataModel::narrowBlock = 0;
|
||||
|
||||
//Initialization of static member
|
||||
std::unique_ptr<DataModel> DataModel::instance = nullptr;
|
||||
|
||||
//Read file locker
|
||||
std::timed_mutex accessFilesMTX;
|
||||
|
||||
|
||||
constexpr unsigned long long DataModel::timepoint_to_sec_long(const std::chrono::system_clock::time_point& t) {
|
||||
return duration_to_sec_long(t.time_since_epoch());
|
||||
}
|
||||
|
||||
const ts_map<ModbusRegister, SavedData> &DataModel::getPublisherData() const
|
||||
{
|
||||
return temporaryStorage;
|
||||
}
|
||||
|
||||
CappedStorage* DataModel::getPermanentData(ModbusRegister reg){
|
||||
auto it = permanentStorage.find(reg);
|
||||
if(it != permanentStorage.end()){
|
||||
return &(it->second);
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
constexpr unsigned long long DataModel::duration_to_sec_long(const std::chrono::system_clock::duration& t) {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(t).count();
|
||||
}
|
||||
|
||||
inline void DataModel::Create() {
|
||||
instance = std::make_unique<DataModel>();
|
||||
|
||||
permanentParamHistory = SystemConfig::getIntConfigParameter("permanent_param_history");
|
||||
narrowBlock = SystemConfig::getIntConfigParameter("narrow_block");
|
||||
}
|
||||
|
||||
DataModel::~DataModel()
|
||||
{
|
||||
LOG(INFO) << "Save locally stored permanent data to permanent storage...";
|
||||
//Save permanent data to storage
|
||||
long long time = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
std::fstream file;
|
||||
std::stringstream ss;
|
||||
ss << dataDir << "data_" << time;
|
||||
std::string filename = ss.str();
|
||||
file.open(filename, std::ios::out);
|
||||
|
||||
if (file.is_open()) {
|
||||
for(auto &storage : permanentStorage){
|
||||
if(storage.second.size() == 0)
|
||||
continue;
|
||||
file << (int)storage.first << ":" << std::endl;
|
||||
file << storage.second << std::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
LOG(ERROR) << "Could not access file to store permanent data...";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::unique_ptr<DataModel>& DataModel::Instance() {
|
||||
if (!instance)
|
||||
Create();
|
||||
return instance;
|
||||
}
|
||||
|
||||
void DataModel::Destroy() {
|
||||
if (instance)
|
||||
instance.reset();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DataModel::makePermanent(ModbusRegister reg, bool biased)
|
||||
{
|
||||
permanentStorage.emplace(reg, CappedStorage(biased));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DataModel::saveModbusParameter(ParameterSpecification param)
|
||||
{
|
||||
if(param.cat == Category::Alert){
|
||||
short bitmask = 0;
|
||||
std::memcpy(&bitmask, param.readedBytes.data(), param.length);
|
||||
//Found an alert here
|
||||
if(bitmask != 0)
|
||||
LOG(WARNING) << "Received an alert from Modbus " << param.connection->getConnectionType() << ", ID " << param.connection->getID();
|
||||
alerts.push(param);
|
||||
return;
|
||||
}
|
||||
|
||||
//Temporary storage of last value
|
||||
temporaryStorage.emplaceOrOverwrite(param.description, SavedData(param.cat, param.readedBytes));
|
||||
|
||||
//If parameter is listet to be stored permanently
|
||||
auto res = permanentStorage.find(param.description);
|
||||
if(res != permanentStorage.end()){
|
||||
checkForFlushData();
|
||||
res->second.store(param.readedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void DataModel::checkForFlushData() {
|
||||
|
||||
while(1){
|
||||
auto permStorageIter = permanentStorage.begin();
|
||||
auto storageIter = permanentStorage.find(permStorageIter->first);
|
||||
auto& X = storageIter->second.getX();
|
||||
auto& y = storageIter->second.getY();
|
||||
//Rescale Matrix if data exceeds time limit
|
||||
storageIter->second.lock();
|
||||
if(X.size() > 1 && (X(0,1) <= (X(X.rows()-1, 1) - DataModel::permanentParamHistory))){
|
||||
if(X.size() <= narrowBlock)
|
||||
return;
|
||||
LOG(INFO) << "Shrink permanent storage of parameter " << std::dec << (int)permStorageIter->first;
|
||||
std::stringstream ss;
|
||||
ss << (int)storageIter->first << "(X);";
|
||||
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> temp(X.rows()-narrowBlock, X.cols());
|
||||
temp = X.block(narrowBlock, 0, X.rows() - narrowBlock, X.cols());
|
||||
//backup capped data to flush to file
|
||||
for(int i = 0; i < narrowBlock-1; i++){
|
||||
ss << std::fixed << std::setprecision(0) << X(i, X.cols()-1) << ";";
|
||||
}
|
||||
|
||||
X.resize(X.rows() - narrowBlock, X.cols());
|
||||
X = std::move(temp);
|
||||
|
||||
ss << std::fixed << std::setprecision(0) << std::endl << (int)storageIter->first << "(y);";
|
||||
|
||||
temp.resize(y.rows()-narrowBlock, 1);
|
||||
temp = y.block(narrowBlock, 0, y.rows() - narrowBlock, 1);
|
||||
//backup capped data to flush to file
|
||||
for(int i = 0; i < narrowBlock-1; i++){
|
||||
ss << std::fixed << std::setprecision(5) << y(i) << ";";
|
||||
}
|
||||
ss << std::endl;
|
||||
y.resize(y.rows() - narrowBlock, 1);
|
||||
y = std::move(temp);
|
||||
|
||||
flush(ss);
|
||||
}
|
||||
storageIter->second.unlock();
|
||||
if((++permStorageIter) == permanentStorage.end())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DataModel::flush(std::stringstream& ss)
|
||||
{
|
||||
//Data file lock condition
|
||||
if (!accessFilesMTX.try_lock_for(timeoutMS)) {
|
||||
LOG(ERROR) << "Timeout after waiting " << (long long)timeoutMS.count() << " for reading data files";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Flush data to local file";
|
||||
std::fstream file;
|
||||
auto now = std::chrono::system_clock::now();
|
||||
std::stringstream filename;
|
||||
filename << dataDir << "data_" << now.time_since_epoch().count();
|
||||
file.open(filename.str(), std::ios_base::out);
|
||||
if (file.is_open()) {
|
||||
file << ss.str();
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
LOG(ERROR) << "Could not open file for writing";
|
||||
accessFilesMTX.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
//reads in the log file to the supplied stringstream
|
||||
//define fromTime to get only logs older than fromTime (seconds after epoch)
|
||||
uintmax_t DataModel::readLogFile(std::stringstream& ss, long long fromTime) {
|
||||
el::Loggers::flushAll();
|
||||
|
||||
uintmax_t sizeRead = 0;
|
||||
|
||||
std::filesystem::path p{ logDir };
|
||||
std::ifstream file;
|
||||
std::string lineBuffer;
|
||||
std::filesystem::directory_iterator iterator(p);
|
||||
for (auto& currentFile : std::filesystem::directory_iterator(p))
|
||||
if (currentFile.is_regular_file()) {
|
||||
if (logFileName.compare(currentFile.path().filename().string()) == 0) {
|
||||
file.open(currentFile.path());
|
||||
if (file.is_open()) {
|
||||
sizeRead += currentFile.file_size();
|
||||
bool foundTimePoint = false;
|
||||
while (std::getline(file, lineBuffer)) {
|
||||
if (lineBuffer.size() == 0)
|
||||
continue;
|
||||
if (fromTime != 0 && foundTimePoint == false) {
|
||||
std::stringstream temp;
|
||||
temp << lineBuffer;
|
||||
std::tm tm{};
|
||||
temp >> std::get_time(&tm, dateFormatLogger.c_str());
|
||||
auto timePoint = std::chrono::system_clock::from_time_t(std::mktime(&tm));
|
||||
if (timePoint.time_since_epoch().count() >= fromTime) {
|
||||
foundTimePoint = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
ss << lineBuffer << std::endl;
|
||||
}
|
||||
file.close();
|
||||
LOG(INFO) << "Readed log file";
|
||||
}
|
||||
else
|
||||
LOG(WARNING) << "Couldn't open LOG file for writing";
|
||||
break;
|
||||
}
|
||||
}
|
||||
//If size read is 0, no tile at specified index found or file was empty
|
||||
return sizeRead;
|
||||
}
|
||||
|
||||
|
||||
uintmax_t DataModel::readAllDataFiles(std::stringstream& ss) {
|
||||
uintmax_t sizeRead = 0;
|
||||
|
||||
if (!accessFilesMTX.try_lock_for(timeoutMS)) {
|
||||
LOG(ERROR) << "Timeout after waiting " << (long long)timeoutMS.count() << " for reading data files - blocked by another thread";
|
||||
return 0;
|
||||
}
|
||||
std::filesystem::path p{ dataDir };
|
||||
std::ifstream file;
|
||||
std::string lineBuffer;
|
||||
std::filesystem::directory_iterator iterator(p);
|
||||
for (auto& currentFile : std::filesystem::directory_iterator(p)){
|
||||
if (currentFile.is_regular_file() && std::regex_match(currentFile.path().filename().string(), regexPatternFile)) {
|
||||
//look for a valid file with the specified index
|
||||
file.open(currentFile.path());
|
||||
if (file.is_open()) {
|
||||
sizeRead += currentFile.file_size();
|
||||
while (std::getline(file, lineBuffer)) {
|
||||
ss << lineBuffer << std::endl;
|
||||
}
|
||||
file.close();
|
||||
std::filesystem::remove(currentFile.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
accessFilesMTX.unlock();
|
||||
//If size read is 0, no tile at specified index found or file was empty
|
||||
return sizeRead;
|
||||
}
|
||||
|
||||
std::stringstream& DataModel::readPermanentData(std::stringstream& buffer, bool retain){
|
||||
for(auto &e: permanentStorage){
|
||||
buffer << (int)e.first << ":" << std::endl;
|
||||
buffer << e.second;
|
||||
}
|
||||
if(!retain)
|
||||
permanentStorage.clear();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ts_queue<ParameterSpecification>& DataModel::getAlerts()
|
||||
{
|
||||
return alerts;
|
||||
}
|
||||
|
||||
std::stringstream& DataModel::readTemporaryData(std::stringstream& buffer){
|
||||
for(auto &e: temporaryStorage){
|
||||
buffer << (int)e.first << ":";
|
||||
buffer << e.second;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//olderThan: files which are older than this specified time are deleted
|
||||
unsigned long DataModel::removeStoredData(seconds olderThan) {
|
||||
using namespace std::filesystem;
|
||||
auto timeNow = duration_cast<seconds>(system_clock::now().time_since_epoch());
|
||||
u_int filesDeleted = 0;
|
||||
for (auto& file : directory_iterator(path("data/"))) {
|
||||
if (file.is_regular_file()) {
|
||||
if (std::regex_match(file.path().stem().string(), regexPatternFile)) {
|
||||
std::string str = file.path().stem().string();
|
||||
str = str.substr(str.find_first_of("0123456789", 0));
|
||||
//time of file in seconds after 01/01/1970
|
||||
long long timeOfFile = 0;
|
||||
//if (auto [p, ec] = std::from_chars(str.data(), str.data() + str.length(), timeOfFile); ec == std::errc())
|
||||
timeOfFile = std::stoll(str, 0);
|
||||
if ((timeOfFile + olderThan.count()) < timeNow.count())
|
||||
if(remove(file))
|
||||
filesDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Deleted data files (" << filesDeleted << ") that were older than " << (long long)olderThan.count() <<"seconds on local storage";
|
||||
return filesDeleted;
|
||||
}
|
||||
|
||||
//remove every file that are stored locally
|
||||
unsigned long DataModel::removeStoredData() {
|
||||
using namespace std::filesystem;
|
||||
create_directory("data/");
|
||||
unsigned long filesDeleted = 0;
|
||||
try{
|
||||
|
||||
for (auto& file : directory_iterator(path("data/"))) {
|
||||
if (file.is_regular_file()) {
|
||||
if (std::regex_match(file.path().stem().string(), regexPatternFile)) {
|
||||
std::string str = file.path().stem().string();
|
||||
str = str.substr(str.find_first_of("0123456789", 0));
|
||||
filesDeleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(std::exception& e){
|
||||
LOG(ERROR) << "Can't access data directory";
|
||||
}
|
||||
LOG(INFO) << "Deleted all data files (" << filesDeleted << ") on local storage";
|
||||
return filesDeleted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const PublisherType type) {
|
||||
switch (type) {
|
||||
case PublisherType::RCMS_BENDER:
|
||||
os << "Bender RCMS";
|
||||
break;
|
||||
case PublisherType::POWERCENTER:
|
||||
os << "Siemens Powercenter";
|
||||
break;
|
||||
default:
|
||||
os << "<Unknown Publisher Type>";
|
||||
break;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const SavedData& savedData){
|
||||
float value = 0;
|
||||
std::memcpy(&value, savedData.data.data(), 4);
|
||||
os << value << std::endl;
|
||||
return os;
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "PublisherData.h"
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
#include <atomic>
|
||||
#include "ModbusDataPOC.h"
|
||||
#include "ModbusDataBender.h"
|
||||
#include "ts_map.h"
|
||||
#include "cappedstorage.h"
|
||||
|
||||
const Eigen::MatrixXd dummyMat(0,0);
|
||||
const Eigen::VectorXd dummyVec(0);
|
||||
|
||||
|
||||
struct SavedData{
|
||||
Category cat;
|
||||
std::vector<uint16_t> data;
|
||||
|
||||
SavedData(const Category c, const std::vector<uint16_t> d){
|
||||
cat = c;
|
||||
data = d;
|
||||
}
|
||||
friend std::ostream& operator<<(std::ostream& os, const SavedData& savedData);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class PublisherInterface;
|
||||
|
||||
constexpr auto READ_BUFFER_SIZE = 4096;
|
||||
|
||||
typedef std::vector<std::vector<float>> float_matrix;
|
||||
//Type for one dimensional permanent double values
|
||||
typedef ts_map<ModbusRegister, CappedStorage> permanent_data;
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
class DataModel
|
||||
{
|
||||
public:
|
||||
//Sigleton Methods
|
||||
static std::unique_ptr<DataModel>& Instance();
|
||||
static void Destroy();
|
||||
static void Create();
|
||||
|
||||
~DataModel();
|
||||
|
||||
//save Data inside application
|
||||
void savePublishedData(PublisherData&&);
|
||||
|
||||
void makePermanent(ModbusRegister reg, bool biased);
|
||||
|
||||
void saveModbusParameter(ParameterSpecification param);
|
||||
|
||||
//getter
|
||||
std::vector<u_int> getSavedPublishedDataIndexes(const u_int id, const seconds newerThan);
|
||||
|
||||
|
||||
//---local file storage methods---
|
||||
//Stores in-programm data to local file system
|
||||
bool flush(std::stringstream &ss);
|
||||
|
||||
uintmax_t readLogFile(std::stringstream& ss, long long fromTime);
|
||||
|
||||
//Reads in from all files their content to ss, or a certain file, specified by its index
|
||||
//TODO: define readSince to read al data after that time point
|
||||
//size_t readSingleFile(std::stringstream& ss, const size_t index);
|
||||
uintmax_t readFromAllFiles(std::stringstream& ss, std::chrono::system_clock::time_point readSince = std::chrono::system_clock::from_time_t(0));
|
||||
uintmax_t readAllDataFiles(std::stringstream& ss);
|
||||
|
||||
unsigned long removeStoredData(seconds olderThan);
|
||||
unsigned long removeStoredData();
|
||||
|
||||
//saves collected data after a certain amount of time points
|
||||
void checkForFlushData();
|
||||
|
||||
unsigned long long getStartTime(const unsigned int id);
|
||||
unsigned long long getStartTime();
|
||||
const char* getDataDir() { return dataDir; }
|
||||
const std::regex& getRegexPatternFile() { return regexPatternFile; }
|
||||
|
||||
constexpr unsigned long long duration_to_sec_long(const std::chrono::system_clock::duration& t);
|
||||
constexpr unsigned long long timepoint_to_sec_long(const std::chrono::system_clock::time_point& t);
|
||||
|
||||
const ts_map<ModbusRegister, SavedData> &getPublisherData() const;
|
||||
CappedStorage* getPermanentData(ModbusRegister reg);
|
||||
|
||||
std::stringstream &readTemporaryData(std::stringstream &buffer);
|
||||
std::stringstream &readPermanentData(std::stringstream &buffer, bool retain);
|
||||
ts_queue<ParameterSpecification> &getAlerts();
|
||||
|
||||
private:
|
||||
//Register to enqueue data wich are stored permanently
|
||||
permanent_data permanentStorage;
|
||||
//Temporary storage of the last readed parameter
|
||||
ts_map<ModbusRegister, SavedData> temporaryStorage;
|
||||
//Temporary saved alerts
|
||||
ts_queue<ParameterSpecification> alerts;
|
||||
|
||||
static long permanentParamHistory;
|
||||
static int narrowBlock;
|
||||
|
||||
const std::regex regexPatternFile = std::regex("data_\\w+");
|
||||
const std::string logFileName = "data_analysis.log";
|
||||
const std::string dateFormatLogger = "%Y-%m-%d %H:%M:%S";
|
||||
u_int nrOfDataPoints = 0;
|
||||
|
||||
//static instance which can be accessed by calling DataModel::Instance()
|
||||
static std::unique_ptr<DataModel> instance;
|
||||
|
||||
//local directory path to the data storage
|
||||
const char* dataDir = "data/";
|
||||
const char* logDir = "log/";
|
||||
|
||||
|
||||
//Data from publishers, one elemt in outer vector for one id
|
||||
//Each id consists of a vector of PublisherData (use map to make the id become a unique key)
|
||||
|
||||
|
||||
|
||||
//ostream operators (read and write dataModel data from streams)
|
||||
friend std::ostream& operator<<(std::ostream& os, DataModel& dataModel);
|
||||
friend std::istream& operator>>(std::istream& is, DataModel& dataModel);
|
||||
};
|
@ -1,37 +0,0 @@
|
||||
#include "Evaluator.h"
|
||||
#include <easylogging++.h>
|
||||
#include "SystemConfig.h"
|
||||
|
||||
|
||||
Evaluator::Evaluator()
|
||||
{
|
||||
//initialize Linear Regression Analysis on Residual Current Register
|
||||
CappedStorage* storageP = DataModel::Instance()->getPermanentData(ModbusRegister::BENDER_Residual_current);
|
||||
if(storageP != nullptr){
|
||||
MLAlgorithms.push_back(std::make_unique<MLLinReg>(storageP));
|
||||
}
|
||||
else
|
||||
LOG(ERROR) << "Tried to invoke ML-Algorithm on non permanent or non existing parameter storage";
|
||||
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, std::vector<float>& vec) {
|
||||
std::for_each(vec.begin(), vec.end(), [&os](float a) {os << a << " "; });
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
//update all registered models
|
||||
std::vector<MLAlert> Evaluator::evaluate()
|
||||
{
|
||||
std::vector<MLAlert> alerts;
|
||||
for(auto &a: MLAlgorithms){
|
||||
MLAlert alert = a->updateModel();
|
||||
if(alert.type != CustomAlertTypes::NO_ALERT)
|
||||
alerts.push_back(alert);
|
||||
}
|
||||
for(auto &a: alerts)
|
||||
LOG(WARNING) << a.message;
|
||||
return alerts;
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "MLAnalyzer.h"
|
||||
#include "MLLinReg.h"
|
||||
#include "MLAlert.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class Evaluator
|
||||
{
|
||||
private:
|
||||
|
||||
//Enqueued Algorithms to perform data analysis
|
||||
std::vector<std::unique_ptr<MLAnalyzer>> MLAlgorithms;
|
||||
|
||||
|
||||
public:
|
||||
Evaluator();
|
||||
|
||||
//Gets called if new data is available
|
||||
std::vector<MLAlert> evaluate();
|
||||
|
||||
};
|
||||
|
@ -1,17 +0,0 @@
|
||||
#ifndef MLALERT_H
|
||||
#define MLALERT_H
|
||||
#include <string>
|
||||
|
||||
enum CustomAlertTypes{
|
||||
NO_ALERT,
|
||||
CRIT_RCM_TENDENCY,
|
||||
};
|
||||
|
||||
struct MLAlert{
|
||||
CustomAlertTypes type;
|
||||
std::string message;
|
||||
|
||||
MLAlert(CustomAlertTypes type, std::string message): type(type), message(message){}
|
||||
};
|
||||
|
||||
#endif // MLALERT_H
|
@ -1,42 +0,0 @@
|
||||
#include "MLAnalyzer.h"
|
||||
#include <sstream>
|
||||
#include "DataModel.h"
|
||||
#include "SystemConfig.h"
|
||||
|
||||
MLAnalyzer::MLAnalyzer(CappedStorage *storage) :
|
||||
n(storage->getN()), m(storage->getM()), data(storage), updateRate(SystemConfig::getIntConfigParameter("update_model_rate"))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MLAnalyzer::addData(Eigen::VectorXd Px, double Py)
|
||||
{
|
||||
Eigen::Matrix<double, 1, 1> temp;
|
||||
temp << Py;
|
||||
addData(Px, temp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MLAnalyzer::addData(Eigen::MatrixXd Px, Eigen::VectorXd Py)
|
||||
{
|
||||
//if(Px.cols() != n || (Px.rows() != Py.rows())){
|
||||
// std::stringstream ss;
|
||||
// ss << "Invalid Matrix dimensions: ";
|
||||
// throw ss.str();
|
||||
//}
|
||||
//
|
||||
//X.conservativeResize(X.rows()+Px.rows(), Eigen::NoChange_t());
|
||||
//y.conservativeResize(y.rows()+Py.rows(), Eigen::NoChange_t());
|
||||
//
|
||||
//X.block(m, 0, Px.rows(), 1) = Eigen::MatrixXd::Ones(Px.rows(), 1);
|
||||
//X.block(m, 1, Px.rows(), Px.cols()) = Px;
|
||||
//
|
||||
//m += X.rows();
|
||||
//
|
||||
//if(++currentBatch >= trainAfterNPoints){
|
||||
// updateModel();
|
||||
// currentBatch = 0;
|
||||
//}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#ifndef MLANALYZER_H
|
||||
#define MLANALYZER_H
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <eigen3/Eigen/Dense>
|
||||
#include "DataModel.h"
|
||||
#include "MLAlert.h"
|
||||
|
||||
//Interface for Machine Learning algorithms
|
||||
class MLAnalyzer
|
||||
{
|
||||
protected:
|
||||
std::chrono::milliseconds updateTime;
|
||||
|
||||
const long long n;
|
||||
long long m;
|
||||
unsigned short currentBatch = 0;
|
||||
const unsigned short trainAfterNPoints = 10;
|
||||
|
||||
//Data references
|
||||
CappedStorage* data;
|
||||
|
||||
const int updateRate;
|
||||
int updateCounter = 0;
|
||||
|
||||
public:
|
||||
|
||||
MLAnalyzer(CappedStorage *storage);
|
||||
|
||||
void addData(Eigen::VectorXd Px, double Py);
|
||||
void addData(Eigen::MatrixXd Px, Eigen::VectorXd Py);
|
||||
|
||||
virtual MLAlert updateModel() = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // MLANALYZER_H
|
@ -1,74 +0,0 @@
|
||||
#include "MLLinReg.h"
|
||||
#include "SystemConfig.h"
|
||||
#include <easylogging++.h>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
MLLinReg::MLLinReg(CappedStorage *storage) : MLAnalyzer(storage)
|
||||
{
|
||||
criticalResidualCurrent = SystemConfig::getFloatConfigParameter("crit_residual_current");
|
||||
criticalTimeRangeSec = SystemConfig::getIntConfigParameter("crit_residual_timerange") * SECONDS_IN_WEEK;
|
||||
|
||||
Theta.resize(2, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
MLAlert MLLinReg::updateModel()
|
||||
{
|
||||
if(t0 == 0 && data->getX().rows() != 0) {LOG(INFO) << std::setprecision(12) << std::fixed << "t_0 = " << (data->getX())(0, 1); t0 = (data->getX())(0, 1);}
|
||||
if(++updateCounter < updateRate)
|
||||
return MLAlert(CustomAlertTypes::NO_ALERT, "");
|
||||
updateCounter = 0;
|
||||
LOG(INFO) << "Update Linear Regression Model";
|
||||
normalEquation();
|
||||
bool error = checkCritical();
|
||||
|
||||
if(error){
|
||||
//Return an alert object if tendency is critical
|
||||
std::stringstream ans;
|
||||
ans << "Received critical tendency in the next " << std::round(criticalTimeRangeSec/SECONDS_IN_WEEK) << " seconds";
|
||||
|
||||
//LOG(INFO) << "Theta values: " << Theta(0) << " " << Theta(1);
|
||||
//if(first){
|
||||
// std::ofstream file;
|
||||
// std::stringstream ss;
|
||||
// file.open("debug_lin_reg.txt", std::ios::out);
|
||||
// if(file.is_open()){
|
||||
// file << std::setprecision(12) << std::fixed << "Theta: " << Theta(0) << " " << Theta(1) << std::endl;
|
||||
// file << std::setprecision(12) << std::fixed << "X: " << data->getX()(data->getX().rows()-1, 1) << std::endl;
|
||||
// file << std::setprecision(12) << std::fixed << "y: " << data->getY()(data->getY().rows()-1) << std::endl;
|
||||
// file << std::setprecision(12) << std::fixed << "t0: " << t0 << std::endl;
|
||||
// file << std::setprecision(12) << std::fixed << std::endl;
|
||||
// file.close();
|
||||
// }
|
||||
// first = false;
|
||||
//}
|
||||
return MLAlert(CustomAlertTypes::CRIT_RCM_TENDENCY, ans.str());
|
||||
}
|
||||
else
|
||||
return MLAlert(CustomAlertTypes::NO_ALERT, "");
|
||||
}
|
||||
|
||||
|
||||
bool MLLinReg::checkCritical(){
|
||||
//Check, if the critical value is reached within the specified time range
|
||||
if(data->getM() == 0)
|
||||
return false;
|
||||
//Offset, to avoid checking with little data and receive faulty results
|
||||
if(data->getM() < 100)
|
||||
return false;
|
||||
return (Theta(0) + Theta(1) * ((data->getX())((data->getX()).rows()-1, 1) + criticalTimeRangeSec))
|
||||
>= criticalResidualCurrent;
|
||||
}
|
||||
|
||||
|
||||
//Numerical approach to solve univariate Linear Regression Model
|
||||
void MLLinReg::normalEquation() {
|
||||
Theta = Eigen::MatrixXd::Zero(2, 1);
|
||||
Theta = (data->getX().transpose() * data->getX()).inverse() * data->getX().transpose() * data->getY();
|
||||
if(data->getX().rows() > 500){
|
||||
LOG(INFO) << Theta(0) << " " << Theta(1);
|
||||
LOG(INFO) << data->getX().rows();
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
#ifndef MLLINREG_H
|
||||
#define MLLINREG_H
|
||||
#include "MLAnalyzer.h"
|
||||
|
||||
constexpr int SECONDS_IN_WEEK = 7*24*60*60;
|
||||
|
||||
class MLLinReg: public MLAnalyzer
|
||||
{
|
||||
private:
|
||||
Eigen::MatrixXd Theta;
|
||||
void normalEquation();
|
||||
bool first = true;
|
||||
|
||||
long t0 = 0;
|
||||
float criticalResidualCurrent;
|
||||
int criticalTimeRangeSec;
|
||||
public:
|
||||
MLLinReg(CappedStorage*);
|
||||
MLAlert updateModel() override;
|
||||
bool checkCritical();
|
||||
};
|
||||
|
||||
#endif // MLLINREG_H
|
@ -1,10 +0,0 @@
|
||||
#include "ModbusDataBender.h"
|
||||
|
||||
ModbusDataBender::ModbusDataBender(const unsigned int id) : ModbusDataInterface(id)
|
||||
{
|
||||
//Store Modbus reading registers
|
||||
//Identification
|
||||
//Dummy parameter to simulate continous reading
|
||||
modbusParamFP64.emplace_back(ModbusRegister::BENDER_Residual_current, 100, Access::R, Category::Condition);
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
#include "ModbusDataInterface.h"
|
||||
|
||||
|
||||
class ModbusDataBender : public ModbusDataInterface
|
||||
{
|
||||
public:
|
||||
ModbusDataBender(const unsigned int id);
|
||||
|
||||
};
|
||||
|
@ -1,20 +0,0 @@
|
||||
#include "ModbusDataInterface.h"
|
||||
#include <algorithm>
|
||||
|
||||
/*
|
||||
Collects all ModbusRegister which fulfill the specified category
|
||||
cat: specified category to look for
|
||||
queue: reference to a thread safe queue, which contains the readable registers
|
||||
return: the queue with the enqueued values
|
||||
*/
|
||||
ts_queue<ParameterSpecification>& ModbusDataInterface::modbusRegisterCat(const Category cat, ts_queue<ParameterSpecification>& queue, std::unique_ptr<ModbusInterface>& connection)
|
||||
{
|
||||
std::for_each(modbusParamFP32.begin(), modbusParamFP32.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); });
|
||||
std::for_each(modbusParamFP64.begin(), modbusParamFP64.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); });
|
||||
std::for_each(modbusParamU16.begin(), modbusParamU16.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); });
|
||||
std::for_each(modbusParamU32.begin(), modbusParamU32.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); });
|
||||
std::for_each(modbusParamS16.begin(), modbusParamS16.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); });
|
||||
std::for_each(modbusParamString.begin(), modbusParamString.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); });
|
||||
|
||||
return queue;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <any>
|
||||
#include <sstream>
|
||||
#include <optional>
|
||||
#include "ts_queue.h"
|
||||
#include "ParameterDouble.h"
|
||||
#include "ParameterFloat.h"
|
||||
#include "ParameterUInt16.h"
|
||||
#include "ParameterUInt32.h"
|
||||
#include "ParameterCharP.h"
|
||||
#include "ParameterS16.h"
|
||||
|
||||
class ModbusInterface;
|
||||
|
||||
class ModbusDataInterface
|
||||
{
|
||||
private:
|
||||
const unsigned int id;
|
||||
protected:
|
||||
std::vector<ParameterFloat> modbusParamFP32;
|
||||
std::vector<ParameterDouble> modbusParamFP64;
|
||||
std::vector<ParameterUInt16> modbusParamU16;
|
||||
std::vector<ParameterUInt32> modbusParamU32;
|
||||
std::vector<ParameterS16> modbusParamS16;
|
||||
std::vector<ParameterCharP> modbusParamString;
|
||||
public:
|
||||
ModbusDataInterface(const unsigned int id) : id(id) { }
|
||||
|
||||
ts_queue<ParameterSpecification>& modbusRegisterCat(const Category cat, ts_queue<ParameterSpecification>& queue, std::unique_ptr<ModbusInterface> &connection);
|
||||
|
||||
void readAll();
|
||||
};
|
@ -1,247 +0,0 @@
|
||||
#include "ModbusDataPOC.h"
|
||||
|
||||
|
||||
std::string ModbusDataPOC::getStatusMessage(unsigned short statusID)
|
||||
{
|
||||
switch (statusID)
|
||||
{
|
||||
case 1:
|
||||
return "Breaker off (without realeasing)";
|
||||
case 2:
|
||||
return "Breaker normal (on)";
|
||||
case 3:
|
||||
return "Breaker released (off)";
|
||||
case 4:
|
||||
return "Breaker released (Lever on and blocked)";
|
||||
default:
|
||||
return "Status unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ModbusDataPOC::ModbusDataPOC(const unsigned int id) : ModbusDataInterface(id){
|
||||
|
||||
//Store Modbus reading registers
|
||||
//Identification
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Manufacturer, 0x0002, Access::R);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Ident_Ordernumber, 0x0003, 20, Access::R);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Ident_Seriesnumber, 0x000D, 16, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Hardware_Version, 0x0015, Access::R);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Ident_Software_Version, 0x0016, 4, Access::R);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Ident_Plant_identification_code, 0x001D, 32, Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Ident_Installation_site, 0x002D, 22, Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Ident_Installation_date, 0x0038, 16, Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Ident_Firmware_Applicationcontroller, 0x0045, 10, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Market, 0x005E, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Main_device_rated_current, 0x005F, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Tripping_curve_characteristic, 0x0060, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Installation_place_fuse, 0x0062, Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Ident_MLFB_fuse, 0x0063, 20, Access::RW);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Hardware_Electronics, 0x006D, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Ident_rated_current_melting_part, 0x006E, Access::RW);
|
||||
|
||||
//IP
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_IP_Ethernet_MAC, 0x0200, 6, Access::R);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_Status_DHCP, 0x0203, Access::RW);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_SNTP_server_ip, 0x0205, Access::RW);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_SNTP_client_mode, 0x0207, Access::RW);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_Status_firewall, 0x0209, Access::RW);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_port_number, 0x020D, Access::RW);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_Static_IP, 0x020F, Access::RW);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_Subnetmask, 0x0211, Access::RW);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_Gateway, 0x029D, Access::RW);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_Current_IP, 0x029F, Access::R);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_Current_Subnet, 0x02A1, Access::R);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_IP_Current_Gateway, 0x02A3, Access::R);
|
||||
|
||||
//Bluetooth
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_BT_Status, 0x0301, Access::R);
|
||||
modbusParamS16.emplace_back(ModbusRegister::POC_BT_send_power, 0x0302, Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_BT_device_address, 0x0303, 6, Access::R);
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_BT_passkey, 0x0306, Access::RW);
|
||||
|
||||
//Radio
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_Radio_Date_time_utc, 0x02A3, Access::RW);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_1, 0x0402, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_2, 0x0403, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_3, 0x0404, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_4, 0x0405, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_5, 0x0406, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_6, 0x0407, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_7, 0x0408, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_8, 0x0409, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_9, 0x040A, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_10, 0x040B, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_11, 0x040C, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_12, 0x040D, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_13, 0x040E, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_14, 0x040F, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_15, 0x0410, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_16, 0x0411, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_17, 0x0412, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_18, 0x0413, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_19, 0x0414, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_20, 0x0415, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_21, 0x0416, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_22, 0x0417, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_23, 0x0418, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_24, 0x0419, Access::R);
|
||||
modbusParamS16.emplace_back(ModbusRegister::POC_Radio_transmit_power, 0x041A, Access::RW);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_1, 0x041B, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_2, 0x041C, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_3, 0x041D, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_4, 0x041E, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_5, 0x041F, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_6, 0x0420, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_7, 0x0421, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_8, 0x0422, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_9, 0x0423, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_10, 0x0424, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_11, 0x0425, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_12, 0x0426, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_13, 0x0427, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_14, 0x0428, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_15, 0x0429, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_16, 0x042A, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_17, 0x042B, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_18, 0x042C, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_19, 0x042D, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_20, 0x042E, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_21, 0x042F, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_22, 0x0430, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_23, 0x0431, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_24, 0x0432, Access::R);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Time_sync_to_POC, 0x0433, Access::R);
|
||||
|
||||
//Radio Communication
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_1, 0x0609, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_2, 0x0616, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_3, 0x0623, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_4, 0x0630, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_5, 0x063D, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_6, 0x064A, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_7, 0x0657, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_8, 0x0664, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_9, 0x0671, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_10, 0x067E, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_11, 0x068B, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_12, 0x0698, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_13, 0x06A5, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_14, 0x06B2, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_15, 0x06BF, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_16, 0x06CC, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_17, 0x06D9, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_18, 0x06E6, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_19, 0x06F3, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_20, 0x0700, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_21, 0x070D, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_22, 0x071A, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_23, 0x0727, 8 , Access::RW);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_24, 0x0734, 8 , Access::RW);
|
||||
|
||||
//Measurement and Condition
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Temp, 0x0C00 ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Temp_avg, 0x0C02 ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_I_Phase, 0x0C04 ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_I_Phase_avg, 0x0C06 ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_I_Phase_max, 0x0C08 ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_U_L_N, 0x0C0A ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Freq, 0x0C0C ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_P, 0x0C0E ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_S, 0x0C10 ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Q, 0x0C12 ,Access::R, Category::Condition);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Cos_phi, 0x0C14 ,Access::R, Category::Condition);
|
||||
modbusParamFP64.emplace_back(ModbusRegister::POC_Cond_P_in, 0x0C16 ,Access::R, Category::Condition);
|
||||
modbusParamFP64.emplace_back(ModbusRegister::POC_Cond_P_ab, 0x0C1A ,Access::R, Category::Condition);
|
||||
modbusParamFP64.emplace_back(ModbusRegister::POC_Cond_S_in, 0x0C1E ,Access::R, Category::Condition);
|
||||
modbusParamFP64.emplace_back(ModbusRegister::POC_Cond_S_ab, 0x0C22 ,Access::R, Category::Condition);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Cond_Status, 0x0C26 ,Access::R, Category::Condition);
|
||||
|
||||
//Measurement settings
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_Measurement_Time_period_temperature, 0x0E02 ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_Activate_temp_alert, 0x0E04 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_Limit_temperature_alert, 0x0E05 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_Hysteresis_temperature_alert, 0x0E07 ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_Averaging_interval_s, 0x0E0D ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_alert1_over_on_off, 0x0E0E ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert1_over_limit_percentage, 0x0E0F ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert1_over_hysteresis, 0x0E11 ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_alert2_over_on_off, 0x0E17 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert2_over_limit_percentage, 0x0E18 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert2_over_hysteresis, 0x0E1A ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_alert1_under_on_off, 0x0E20 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert1_under_limit_percentage, 0x0E21 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert1_under_hysteresis, 0x0E23 ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_alert2_under_on_off, 0x0E29 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert2_under_limit_percentage, 0x0E2A ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert2_under_hysteresis, 0x0E2C ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_U_alert1_over_on_off, 0x0E32 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert1_over_limit_percentage, 0x0E33 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert1_over_hysteresis, 0x0E35 ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_U_alert2_over_on_off, 0x0E3B ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert2_over_limit_percentage, 0x0E3C ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert2_over_hysteresis, 0x0E3E ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_U_alert1_under_on_off, 0x0E44 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert1_under_limit_percentage, 0x0E45 ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert1_under_hysteresis, 0x0E47 ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_U_alert2_under_on_off, 0x0E4D ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert2_under_limit_percentage, 0x0E4E ,Access::RW );
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert2_under_hysteresis, 0x0E50 ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_Energy_flow_direction, 0x0E5A ,Access::RW );
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_Alert_on_off_AFDD_threshold_shortfall, 0x0E5C ,Access::RW );
|
||||
|
||||
|
||||
//Diagnosis
|
||||
modbusParamU32.emplace_back(ModbusRegister::POC_Diag_Alert, 0x0A00 ,Access::R , Category::Alert);
|
||||
modbusParamFP64.emplace_back(ModbusRegister::POC_Diag_H_run_with_I, 0x0A02 ,Access::R , Category::Diagnosis);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_run_total_with_I, 0x0A06 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Min_load_current, 0x0A07 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamFP64.emplace_back(ModbusRegister::POC_Diag_Limit_run_hours_with_I_alert, 0x0A08 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamFP64.emplace_back(ModbusRegister::POC_Diag_H_run_total, 0x0A12 ,Access::R , Category::Diagnosis);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_run_total, 0x0A16 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamFP64.emplace_back(ModbusRegister::POC_Diag_Limit_run_hours_total_alert, 0x0A17 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Nr_of_mechanical_switchings, 0x0A21 ,Access::R , Category::Diagnosis);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_nr_mechanical_switchings, 0x0A23 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Limit_mechanical_switchings, 0x0A24 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Nr_of_triggered_switches, 0x0A2A ,Access::R , Category::Diagnosis);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_nr_triggered_switchings, 0x0A2C ,Access::RW , Category::Diagnosis);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Limit_triggered_switchings, 0x0A2D ,Access::RW , Category::Diagnosis);
|
||||
modbusParamS16.emplace_back(ModbusRegister::POC_Diag_RSSI_BLE, 0x0A3D ,Access::R , Category::Diagnosis);
|
||||
modbusParamS16.emplace_back(ModbusRegister::POC_Diag_RSSI_radio, 0x0A3E ,Access::R , Category::Diagnosis);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Nr_of_short_circuit_triggers, 0x0A40 ,Access::R , Category::Diagnosis);
|
||||
modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_nr_short_circuit_triggers, 0x0A42 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Nr_of_short_circuit_triggers, 0x0A43 ,Access::RW , Category::Diagnosis);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_Diag_Time_and_sync_status, 0x0A45, 8 ,Access::R , Category::Diagnosis);
|
||||
|
||||
|
||||
//Radio Communication
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_1, 0x0600, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_2, 0x060D, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_3, 0x061A, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_4, 0x0627, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_5, 0x0634, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_6, 0x0641, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_7, 0x064E, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_8, 0x065B, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_9, 0x0668, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_10, 0x0675, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_11, 0x0682, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_12, 0x068F, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_13, 0x069C, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_14, 0x06A9, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_15, 0x06B6, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_16, 0x06C3, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_17, 0x06D0, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_18, 0x06DD, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_19, 0x06EA, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_20, 0x06F7, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_21, 0x0704, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_22, 0x0711, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_23, 0x071E, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_24, 0x072B, 18, Access::W);
|
||||
modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Paring_Device, 0x0738, 18, Access::W);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <any>
|
||||
#include <map>
|
||||
#include "ModbusDataInterface.h"
|
||||
|
||||
|
||||
|
||||
class ModbusDataPOC : public ModbusDataInterface
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
ModbusDataPOC(const unsigned int id);
|
||||
|
||||
static std::string getStatusMessage(unsigned short statusID);
|
||||
};
|
@ -1,213 +0,0 @@
|
||||
#include "ModbusInterface.h"
|
||||
#include "ModbusInterface.h"
|
||||
#include <iostream>
|
||||
#include <easylogging++.h>
|
||||
|
||||
void ModbusInterface::assignSlave() {
|
||||
my_modbus->addSlave(slaveAddressID);
|
||||
my_slave = my_modbus->slavePtr(slaveAddressID);
|
||||
}
|
||||
|
||||
ModbusInterface::~ModbusInterface() {
|
||||
if(my_modbus != nullptr){
|
||||
my_modbus->close();
|
||||
delete my_modbus;
|
||||
}
|
||||
}
|
||||
|
||||
bool ModbusInterface::openConnection() {
|
||||
modbus_init();
|
||||
//if(!(my_modbus->isOpen()))
|
||||
open = my_modbus->open();
|
||||
if(!open){
|
||||
LOG(ERROR) << "Couldn't open Modbus " << getConnectionType() <<
|
||||
" connection at " << device;
|
||||
LOG(ERROR) << my_modbus->lastError();
|
||||
}
|
||||
return open;
|
||||
}
|
||||
|
||||
void ModbusInterface::disconnect() {
|
||||
if(my_modbus != nullptr)
|
||||
my_modbus->close();
|
||||
}
|
||||
|
||||
bool ModbusInterface::readBit(ParameterSpecification ¶m) {
|
||||
if(!param.connection->getIsOpen()){
|
||||
param.error = true;
|
||||
return false;
|
||||
}
|
||||
bool buf;
|
||||
if (my_slave->readCoil(param.address, buf) != 1) {
|
||||
param.error = true;
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
param.readedBytes.push_back(buf);
|
||||
param.error = false;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ModbusInterface::readBits(ParameterSpecification ¶m) {
|
||||
if(!param.connection->getIsOpen()){
|
||||
param.error = true;
|
||||
return false;
|
||||
}
|
||||
bool bufBool[param.length*8];
|
||||
int ans = my_slave->readCoils(param.address, bufBool, param.length*8);
|
||||
if (ans != param.length*8) {
|
||||
param.error = true;
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
//Big Endian decryption
|
||||
for(unsigned int i = 0; i < param.length; i++){
|
||||
uint8_t byte = 0;
|
||||
for(unsigned int j = 0; j < 8; j++){
|
||||
byte += bufBool[i*8 +j] << j;
|
||||
}
|
||||
param.readedBytes.push_back(byte);
|
||||
}
|
||||
param.error = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ModbusInterface::readByte(ParameterSpecification ¶m) {
|
||||
if(!param.connection->getIsOpen()){
|
||||
param.error = true;
|
||||
return false;
|
||||
}
|
||||
uint16_t buf;
|
||||
if (my_slave->readRegister(param.address, buf) != 1) {
|
||||
param.error = true;
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
param.readedBytes.push_back(buf & 0x00FF);
|
||||
param.error = false;
|
||||
return static_cast<uint8_t>(buf & 0x00FF);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t ModbusInterface::readRegister(ParameterSpecification ¶m) {
|
||||
if(!param.connection->getIsOpen()){
|
||||
param.error = true;
|
||||
return false;
|
||||
}
|
||||
uint16_t buf;
|
||||
if (my_slave->readRegister(param.address, buf) != 1) {
|
||||
param.error = true;
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
param.readedBytes.push_back(buf);
|
||||
param.error = false;
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ModbusInterface::readDRegister(ParameterSpecification ¶m) {
|
||||
if(!param.connection->getIsOpen()){
|
||||
param.error = true;
|
||||
return false;
|
||||
}
|
||||
uint16_t buf[2];
|
||||
|
||||
if (my_slave->readRegisters(param.address, buf, 2) != 2) {
|
||||
param.error = true;
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
param.readedBytes.push_back(*buf);
|
||||
param.readedBytes.push_back(*(buf+1));
|
||||
param.error = false;
|
||||
return _MODBUS_GET_INT32_FROM_INT16(buf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ModbusInterface::readQRegister(ParameterSpecification ¶m) {
|
||||
if(!param.connection->getIsOpen()){
|
||||
param.error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t buf[4];
|
||||
int ans = my_slave->readRegisters(param.address, buf, 4);
|
||||
if (ans != 4) {
|
||||
param.error = true;
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
param.readedBytes.push_back(*buf);
|
||||
param.readedBytes.push_back(*(buf+1));
|
||||
param.readedBytes.push_back(*(buf+2));
|
||||
param.readedBytes.push_back(*(buf+3));
|
||||
param.error = false;
|
||||
return _MODBUS_GET_INT64_FROM_INT16(buf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool ModbusInterface::writeBit(const uint16_t address, bool bit)
|
||||
{
|
||||
if(my_slave->writeCoil(address, bit) != 1){
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModbusInterface::writeByte(const uint16_t address, uint8_t byte)
|
||||
{
|
||||
bool buf[8];
|
||||
for(int i = 0; i < 8; i++){
|
||||
buf[i] = byte & 0x01;
|
||||
byte = byte > 1;
|
||||
}
|
||||
|
||||
if(my_slave->writeCoils(address, buf, 8) != 1){
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModbusInterface::writeRegister(const uint16_t address, uint16_t word)
|
||||
{
|
||||
if(my_slave->writeRegister(address, word) != 1){
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModbusInterface::writeDRegister(const uint16_t address, uint32_t dword)
|
||||
{
|
||||
uint16_t data[2];
|
||||
data[0] = (dword & 0x0000FFFF);
|
||||
data[0] = (dword & 0xFFFF0000)>>16;
|
||||
|
||||
if(my_slave->writeRegisters(address, data, 2) != 1){
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModbusInterface::writeQRegister(const uint16_t address, uint64_t qword)
|
||||
{
|
||||
uint16_t data[4];
|
||||
|
||||
data[0] = (qword & 0x0000'0000'0000'FFFF);
|
||||
data[1] = (qword & 0x0000'0000'FFFF'0000) >> 16;
|
||||
data[2] = (qword & 0x0000'FFFF'0000'0000) >> 32;
|
||||
data[3] = (qword & 0xFFFF'0000'0000'0000) >> 48;
|
||||
|
||||
if(my_slave->writeRegisters(address, data, 4) != 1){
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <modbuspp.h>
|
||||
#include "ParameterInterface.h"
|
||||
|
||||
class ModbusInterface {
|
||||
private:
|
||||
unsigned int slaveAddressID;
|
||||
bool open;
|
||||
protected:
|
||||
Modbus::Master* my_modbus = nullptr;
|
||||
Modbus::Slave* my_slave = nullptr;
|
||||
|
||||
//IP (TCP) or serial port (RTU)
|
||||
std::string device;
|
||||
|
||||
void assignSlave();
|
||||
|
||||
public:
|
||||
ModbusInterface(u_int _id, std::string _device) : slaveAddressID(_id), device(_device) { };
|
||||
virtual ~ModbusInterface();
|
||||
virtual void modbus_init() = 0;
|
||||
virtual std::string getConnectionType() = 0;
|
||||
|
||||
bool openConnection();
|
||||
void disconnect();
|
||||
bool getIsOpen() const { return open; }
|
||||
|
||||
unsigned int getID(){ return slaveAddressID; }
|
||||
|
||||
//Read and write functions
|
||||
bool readBit(ParameterSpecification ¶m);
|
||||
bool readBits(ParameterSpecification ¶m);
|
||||
uint8_t readByte(ParameterSpecification ¶m);
|
||||
uint16_t readRegister(ParameterSpecification ¶m);
|
||||
uint32_t readDRegister(ParameterSpecification ¶m);
|
||||
uint64_t readQRegister(ParameterSpecification ¶m);
|
||||
|
||||
bool writeBit(const uint16_t address, bool bit);
|
||||
bool writeByte(const uint16_t address, uint8_t byte);
|
||||
bool writeRegister(const uint16_t address, uint16_t word);
|
||||
bool writeDRegister(const uint16_t address, uint32_t dword);
|
||||
bool writeQRegister(const uint16_t address, uint64_t qword);
|
||||
};
|
||||
|
||||
|
||||
#define _MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \
|
||||
(((int64_t)tab_int16[(index) ] << 48) + \
|
||||
((int64_t)tab_int16[(index) + 1] << 32) + \
|
||||
((int64_t)tab_int16[(index) + 2] << 16) + \
|
||||
(int64_t)tab_int16[(index) + 3])
|
||||
#define _MODBUS_GET_INT32_FROM_INT16(tab_int16, index) ((tab_int16[(index)] << 16) + tab_int16[(index) + 1])
|
||||
#define _MODBUS_GET_INT16_FROM_INT8(tab_int8, index) ((tab_int8[(index)] << 8) + tab_int8[(index) + 1])
|
@ -1,283 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
enum class Category {
|
||||
//Category for collective reading accesses
|
||||
NONE,
|
||||
Condition,
|
||||
Diagnosis,
|
||||
Alert
|
||||
};
|
||||
|
||||
|
||||
enum class ModbusRegister {
|
||||
//---------------------Powercenter----------------------------
|
||||
//Information for identification
|
||||
POC_Ident_Manufacturer,
|
||||
POC_Ident_Ordernumber,
|
||||
POC_Ident_Seriesnumber,
|
||||
POC_Ident_Hardware_Version,
|
||||
POC_Ident_Software_Version,
|
||||
POC_Ident_Plant_identification_code,
|
||||
POC_Ident_Installation_site,
|
||||
POC_Ident_Installation_date,
|
||||
POC_Ident_Firmware_Applicationcontroller,
|
||||
POC_Ident_Market,
|
||||
POC_Ident_Main_device_rated_current,
|
||||
POC_Ident_Tripping_curve_characteristic,
|
||||
POC_Ident_Installation_place_fuse,
|
||||
POC_Ident_MLFB_fuse,
|
||||
POC_Ident_Hardware_Electronics,
|
||||
POC_Ident_rated_current_melting_part,
|
||||
//IP Information
|
||||
POC_IP_Ethernet_MAC,
|
||||
POC_IP_Status_DHCP,
|
||||
POC_IP_SNTP_server_ip,
|
||||
POC_IP_SNTP_client_mode,
|
||||
POC_IP_Status_firewall,
|
||||
POC_IP_port_number,
|
||||
POC_IP_Static_IP,
|
||||
POC_IP_Subnetmask,
|
||||
POC_IP_Gateway,
|
||||
POC_IP_Current_IP,
|
||||
POC_IP_Current_Subnet,
|
||||
POC_IP_Current_Gateway,
|
||||
//Bluetooth Information
|
||||
POC_BT_Status,
|
||||
POC_BT_send_power,
|
||||
POC_BT_device_address,
|
||||
POC_BT_passkey,
|
||||
//Radio Information
|
||||
POC_Radio_Date_time_utc,
|
||||
POC_Radio_Paring_status_1,
|
||||
POC_Radio_Paring_status_2,
|
||||
POC_Radio_Paring_status_3,
|
||||
POC_Radio_Paring_status_4,
|
||||
POC_Radio_Paring_status_5,
|
||||
POC_Radio_Paring_status_6,
|
||||
POC_Radio_Paring_status_7,
|
||||
POC_Radio_Paring_status_8,
|
||||
POC_Radio_Paring_status_9,
|
||||
POC_Radio_Paring_status_10,
|
||||
POC_Radio_Paring_status_11,
|
||||
POC_Radio_Paring_status_12,
|
||||
POC_Radio_Paring_status_13,
|
||||
POC_Radio_Paring_status_14,
|
||||
POC_Radio_Paring_status_15,
|
||||
POC_Radio_Paring_status_16,
|
||||
POC_Radio_Paring_status_17,
|
||||
POC_Radio_Paring_status_18,
|
||||
POC_Radio_Paring_status_19,
|
||||
POC_Radio_Paring_status_20,
|
||||
POC_Radio_Paring_status_21,
|
||||
POC_Radio_Paring_status_22,
|
||||
POC_Radio_Paring_status_23,
|
||||
POC_Radio_Paring_status_24,
|
||||
POC_Radio_transmit_power,
|
||||
POC_Radio_Device_status_1,
|
||||
POC_Radio_Device_status_2,
|
||||
POC_Radio_Device_status_3,
|
||||
POC_Radio_Device_status_4,
|
||||
POC_Radio_Device_status_5,
|
||||
POC_Radio_Device_status_6,
|
||||
POC_Radio_Device_status_7,
|
||||
POC_Radio_Device_status_8,
|
||||
POC_Radio_Device_status_9,
|
||||
POC_Radio_Device_status_10,
|
||||
POC_Radio_Device_status_11,
|
||||
POC_Radio_Device_status_12,
|
||||
POC_Radio_Device_status_13,
|
||||
POC_Radio_Device_status_14,
|
||||
POC_Radio_Device_status_15,
|
||||
POC_Radio_Device_status_16,
|
||||
POC_Radio_Device_status_17,
|
||||
POC_Radio_Device_status_18,
|
||||
POC_Radio_Device_status_19,
|
||||
POC_Radio_Device_status_20,
|
||||
POC_Radio_Device_status_21,
|
||||
POC_Radio_Device_status_22,
|
||||
POC_Radio_Device_status_23,
|
||||
POC_Radio_Device_status_24,
|
||||
POC_Radio_Time_sync_to_POC,
|
||||
//Radio Communication Information
|
||||
POC_RadioCom_Mac_address_device_1,
|
||||
POC_RadioCom_Mac_address_device_2,
|
||||
POC_RadioCom_Mac_address_device_3,
|
||||
POC_RadioCom_Mac_address_device_4,
|
||||
POC_RadioCom_Mac_address_device_5,
|
||||
POC_RadioCom_Mac_address_device_6,
|
||||
POC_RadioCom_Mac_address_device_7,
|
||||
POC_RadioCom_Mac_address_device_8,
|
||||
POC_RadioCom_Mac_address_device_9,
|
||||
POC_RadioCom_Mac_address_device_10,
|
||||
POC_RadioCom_Mac_address_device_11,
|
||||
POC_RadioCom_Mac_address_device_12,
|
||||
POC_RadioCom_Mac_address_device_13,
|
||||
POC_RadioCom_Mac_address_device_14,
|
||||
POC_RadioCom_Mac_address_device_15,
|
||||
POC_RadioCom_Mac_address_device_16,
|
||||
POC_RadioCom_Mac_address_device_17,
|
||||
POC_RadioCom_Mac_address_device_18,
|
||||
POC_RadioCom_Mac_address_device_19,
|
||||
POC_RadioCom_Mac_address_device_20,
|
||||
POC_RadioCom_Mac_address_device_21,
|
||||
POC_RadioCom_Mac_address_device_22,
|
||||
POC_RadioCom_Mac_address_device_23,
|
||||
POC_RadioCom_Mac_address_device_24,
|
||||
|
||||
//Measurement Settings
|
||||
POC_Measurement_Time_period_temperature,
|
||||
POC_Measurement_Activate_temp_alert,
|
||||
POC_Measurement_Limit_temperature_alert,
|
||||
POC_Measurement_Hysteresis_temperature_alert,
|
||||
POC_Measurement_I_Averaging_interval_s,
|
||||
//Current Alerts
|
||||
POC_Measurement_I_alert1_over_on_off,
|
||||
POC_Measurement_I_alert1_over_limit_percentage,
|
||||
POC_Measurement_I_alert1_over_hysteresis,
|
||||
POC_Measurement_I_alert2_over_on_off,
|
||||
POC_Measurement_I_alert2_over_limit_percentage,
|
||||
POC_Measurement_I_alert2_over_hysteresis,
|
||||
POC_Measurement_I_alert1_under_on_off,
|
||||
POC_Measurement_I_alert1_under_limit_percentage,
|
||||
POC_Measurement_I_alert1_under_hysteresis,
|
||||
POC_Measurement_I_alert2_under_on_off,
|
||||
POC_Measurement_I_alert2_under_limit_percentage,
|
||||
POC_Measurement_I_alert2_under_hysteresis,
|
||||
//Voltage Alerts
|
||||
POC_Measurement_U_alert1_over_on_off,
|
||||
POC_Measurement_U_alert1_over_limit_percentage,
|
||||
POC_Measurement_U_alert1_over_hysteresis,
|
||||
POC_Measurement_U_alert2_over_on_off,
|
||||
POC_Measurement_U_alert2_over_limit_percentage,
|
||||
POC_Measurement_U_alert2_over_hysteresis,
|
||||
POC_Measurement_U_alert1_under_on_off,
|
||||
POC_Measurement_U_alert1_under_limit_percentage,
|
||||
POC_Measurement_U_alert1_under_hysteresis,
|
||||
POC_Measurement_U_alert2_under_on_off,
|
||||
POC_Measurement_U_alert2_under_limit_percentage,
|
||||
POC_Measurement_U_alert2_under_hysteresis,
|
||||
|
||||
POC_Measurement_Energy_flow_direction,
|
||||
POC_Measurement_Alert_on_off_AFDD_threshold_shortfall,
|
||||
|
||||
//Switch gears condition
|
||||
POC_Cond_Temp, //°C
|
||||
POC_Cond_Temp_avg, //°C
|
||||
POC_Cond_I_Phase, //A
|
||||
POC_Cond_I_Phase_avg, //A
|
||||
POC_Cond_I_Phase_max, //A
|
||||
POC_Cond_U_L_N, //V
|
||||
POC_Cond_Freq, //Hz
|
||||
POC_Cond_P, //W
|
||||
POC_Cond_S, //W
|
||||
POC_Cond_Q, //Var
|
||||
POC_Cond_Cos_phi,
|
||||
POC_Cond_P_in, //Wh
|
||||
POC_Cond_P_ab, //Wh
|
||||
POC_Cond_S_in, //Varh
|
||||
POC_Cond_S_ab, //Varh
|
||||
POC_Cond_Status,
|
||||
|
||||
//Powercenter Diagnosis
|
||||
POC_Diag_Alert,
|
||||
//Hours run with current flow
|
||||
POC_Diag_H_run_with_I, //s
|
||||
POC_Diag_Alert_on_off_run_total_with_I,
|
||||
POC_Diag_Min_load_current,
|
||||
POC_Diag_Limit_run_hours_with_I_alert,
|
||||
POC_Diag_H_run_total, //s
|
||||
POC_Diag_Alert_on_off_run_total,
|
||||
POC_Diag_Limit_run_hours_total_alert,
|
||||
POC_Diag_Nr_of_mechanical_switchings,
|
||||
POC_Diag_Alert_on_off_nr_mechanical_switchings,
|
||||
POC_Diag_Limit_mechanical_switchings,
|
||||
POC_Diag_Nr_of_triggered_switches,
|
||||
POC_Diag_Alert_on_off_nr_triggered_switchings,
|
||||
POC_Diag_Limit_triggered_switchings,
|
||||
//Received Signal Strength Indicator in dbm
|
||||
POC_Diag_RSSI_BLE,
|
||||
POC_Diag_RSSI_radio,
|
||||
POC_Diag_Nr_of_short_circuit_triggers,
|
||||
POC_Diag_Alert_on_off_nr_short_circuit_triggers,
|
||||
POC_Diag_Limit_shoer_circuit_triggers,
|
||||
POC_Diag_Time_and_sync_status,
|
||||
|
||||
//Radio communication settings
|
||||
POC_RadioCom_Installation_Code_1,
|
||||
POC_RadioCom_Installation_Code_2,
|
||||
POC_RadioCom_Installation_Code_3,
|
||||
POC_RadioCom_Installation_Code_4,
|
||||
POC_RadioCom_Installation_Code_5,
|
||||
POC_RadioCom_Installation_Code_6,
|
||||
POC_RadioCom_Installation_Code_7,
|
||||
POC_RadioCom_Installation_Code_8,
|
||||
POC_RadioCom_Installation_Code_9,
|
||||
POC_RadioCom_Installation_Code_10,
|
||||
POC_RadioCom_Installation_Code_11,
|
||||
POC_RadioCom_Installation_Code_12,
|
||||
POC_RadioCom_Installation_Code_13,
|
||||
POC_RadioCom_Installation_Code_14,
|
||||
POC_RadioCom_Installation_Code_15,
|
||||
POC_RadioCom_Installation_Code_16,
|
||||
POC_RadioCom_Installation_Code_17,
|
||||
POC_RadioCom_Installation_Code_18,
|
||||
POC_RadioCom_Installation_Code_19,
|
||||
POC_RadioCom_Installation_Code_20,
|
||||
POC_RadioCom_Installation_Code_21,
|
||||
POC_RadioCom_Installation_Code_22,
|
||||
POC_RadioCom_Installation_Code_23,
|
||||
POC_RadioCom_Installation_Code_24,
|
||||
|
||||
POC_RadioCom_Paring_Device,
|
||||
|
||||
POC_RadioCom_Paring_device_1,
|
||||
POC_RadioCom_Paring_device_2,
|
||||
POC_RadioCom_Paring_device_3,
|
||||
POC_RadioCom_Paring_device_4,
|
||||
POC_RadioCom_Paring_device_5,
|
||||
POC_RadioCom_Paring_device_6,
|
||||
POC_RadioCom_Paring_device_7,
|
||||
POC_RadioCom_Paring_device_8,
|
||||
POC_RadioCom_Paring_device_9,
|
||||
POC_RadioCom_Paring_device_10,
|
||||
POC_RadioCom_Paring_device_11,
|
||||
POC_RadioCom_Paring_device_12,
|
||||
POC_RadioCom_Paring_device_13,
|
||||
POC_RadioCom_Paring_device_14,
|
||||
POC_RadioCom_Paring_device_15,
|
||||
POC_RadioCom_Paring_device_16,
|
||||
POC_RadioCom_Paring_device_17,
|
||||
POC_RadioCom_Paring_device_18,
|
||||
POC_RadioCom_Paring_device_19,
|
||||
POC_RadioCom_Paring_device_20,
|
||||
POC_RadioCom_Paring_device_21,
|
||||
POC_RadioCom_Paring_device_22,
|
||||
POC_RadioCom_Paring_device_23,
|
||||
POC_RadioCom_Paring_device_24,
|
||||
|
||||
|
||||
//---------------------Bender----------------------------
|
||||
//Test variable to be read from virtual RCM device
|
||||
BENDER_Residual_current,
|
||||
};
|
||||
|
||||
|
||||
namespace CMD {
|
||||
enum class Identification {
|
||||
Trigger_flash_light,
|
||||
};
|
||||
|
||||
enum class IP {
|
||||
Apply_ethernet_configuration_changes,
|
||||
};
|
||||
|
||||
enum class Bluetooth {
|
||||
Switch_on_off_BT_Reset_passkey,
|
||||
};
|
||||
|
||||
enum class MeasurementSettings {
|
||||
Reset_energy_counter,
|
||||
Reset_extrem_values,
|
||||
};
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
#include "ModbusRtu.h"
|
||||
#include <sstream>
|
||||
|
||||
inline std::string ModbusRTU::getSettingsString() const
|
||||
{
|
||||
return std::to_string(baud) + pairity + std::to_string(stopBit);
|
||||
}
|
||||
|
||||
ModbusRTU::~ModbusRTU()
|
||||
{
|
||||
//if(my_slave != nullptr){
|
||||
// delete my_slave;
|
||||
//}
|
||||
//if(my_modbus != nullptr){
|
||||
// my_modbus->close();
|
||||
// delete my_modbus;
|
||||
//}
|
||||
}
|
||||
|
||||
ModbusRTU::ModbusRTU(const u_int id, const std::string _device, const unsigned int _baud, const char _pairity, const unsigned int _stopBit)
|
||||
: ModbusInterface(id, _device), baud(_baud), pairity(_pairity), stopBit(_stopBit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ModbusRTU::modbus_init() {
|
||||
if(my_modbus == nullptr){
|
||||
my_modbus = new Modbus::Master(Modbus::Rtu, device, getSettingsString());
|
||||
assignSlave();
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
#include <modbuspp.h>
|
||||
#include "ModbusInterface.h"
|
||||
|
||||
class ModbusRTU :
|
||||
public ModbusInterface
|
||||
{
|
||||
private:
|
||||
unsigned int baud;
|
||||
char pairity;
|
||||
unsigned int stopBit;
|
||||
|
||||
|
||||
|
||||
//Create settings string
|
||||
std::string getSettingsString() const;
|
||||
|
||||
public:
|
||||
ModbusRTU(const u_int id, const std::string _device, const u_int _baud, const char _pairity, const u_int _stopBit);
|
||||
virtual ~ModbusRTU();
|
||||
virtual void modbus_init() override;
|
||||
|
||||
std::string getConnectionType()override { return "Rtu"; }
|
||||
|
||||
};
|
||||
|
@ -1,25 +0,0 @@
|
||||
#include "ModbusTcp.h"
|
||||
|
||||
ModbusTCP::ModbusTCP(const u_int id, const std::string _ip, const unsigned int _port)
|
||||
: ModbusInterface(id, _ip), port(std::to_string(_port))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ModbusTCP::~ModbusTCP()
|
||||
{
|
||||
//if(my_slave != nullptr){
|
||||
// delete my_slave;
|
||||
//}
|
||||
//if(my_modbus != nullptr){
|
||||
// my_modbus->close();
|
||||
// delete my_modbus;
|
||||
//}
|
||||
}
|
||||
|
||||
void ModbusTCP::modbus_init() {
|
||||
if (my_modbus == nullptr){
|
||||
my_modbus = new Modbus::Master(Modbus::Tcp, device, port);
|
||||
assignSlave();
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include <modbuspp.h>
|
||||
#include "ModbusInterface.h"
|
||||
|
||||
class ModbusTCP :
|
||||
public ModbusInterface
|
||||
{
|
||||
private:
|
||||
|
||||
std::string port = "502";
|
||||
|
||||
public:
|
||||
ModbusTCP(const u_int id, const std::string _ip, const u_int _port);
|
||||
virtual ~ModbusTCP();
|
||||
virtual void modbus_init() override;
|
||||
|
||||
std::string getConnectionType()override { return "Tcp"; }
|
||||
|
||||
};
|
||||
|
@ -1,128 +0,0 @@
|
||||
#include "NetServer.h"
|
||||
#include <easylogging++.h>
|
||||
#include "DataModel.h"
|
||||
|
||||
void NetServer::Stop()
|
||||
{
|
||||
net::ServerInterface<net::MessageTypes>::Stop();
|
||||
LOG(INFO) << "[SERVER] Stopped Server";
|
||||
serverRunning = false;
|
||||
}
|
||||
|
||||
bool NetServer::Start()
|
||||
{
|
||||
if(serverRunning){
|
||||
LOG(WARNING) << "[SERVER] tried to start ip tcp server, but it is already running";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool res = net::ServerInterface<net::MessageTypes>::Start();
|
||||
if(res){
|
||||
LOG(INFO) << "[SERVER] Started Server";
|
||||
serverRunning = true;
|
||||
return res;
|
||||
}
|
||||
LOG(ERROR) << "[SERVER] Couldn't start Server";
|
||||
serverRunning = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool NetServer::OnClientConnect(std::shared_ptr<net::Connection<net::MessageTypes>> client)
|
||||
{
|
||||
//Decide whether to accept or deny connection (always accept in this case)
|
||||
net::Message<net::MessageTypes> msg;
|
||||
msg.header.id = net::MessageTypes::ServerAccept;
|
||||
client->Send(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Called when a client appears to have disconnected
|
||||
void NetServer::OnClientDisconnect(std::shared_ptr<net::Connection<net::MessageTypes>> client)
|
||||
{
|
||||
LOG(INFO) << "Removing client [" << client->GetID() << "]";
|
||||
}
|
||||
|
||||
|
||||
// Called when a message arrives
|
||||
void NetServer::OnMessage(std::shared_ptr<net::Connection<net::MessageTypes>> client, net::Message<net::MessageTypes>& msgRcv)
|
||||
{
|
||||
net::Message<net::MessageTypes> msgSend;
|
||||
std::stringstream body;
|
||||
|
||||
switch (msgRcv.header.id)
|
||||
{
|
||||
case net::MessageTypes::ServerData:
|
||||
LOG(INFO) << "[Client " << client->GetID() << "]: Request Server Data";
|
||||
msgSend.header.id = net::MessageTypes::ServerData;
|
||||
getBodyData(body);
|
||||
msgSend << body.str();
|
||||
client->Send(msgSend);
|
||||
break;
|
||||
case net::MessageTypes::ServerCondition:
|
||||
LOG(INFO) << "[Client " << client->GetID() << "]: Request temporary Condition Data";
|
||||
msgSend.header.id = net::MessageTypes::ServerCondition;
|
||||
getBodyCondition(body);
|
||||
msgSend << body.str();
|
||||
client->Send(msgSend);
|
||||
break;
|
||||
case net::MessageTypes::ServerAlert:
|
||||
LOG(INFO) << "[Client " << client->GetID() << "]: Request Alert Data";
|
||||
msgSend.header.id = net::MessageTypes::ServerAlert;
|
||||
client->Send(msgSend);
|
||||
break;
|
||||
case net::MessageTypes::ServerLog:
|
||||
LOG(INFO) << "[Client " << client->GetID() << "]: Request Log Files";
|
||||
msgSend.header.id = net::MessageTypes::ServerLog;
|
||||
getBodyLog(body, msgRcv);
|
||||
msgSend << body.str();
|
||||
client->Send(msgSend);
|
||||
break;
|
||||
case net::MessageTypes::ServerCloseConnection:
|
||||
LOG(INFO) << "[Client " << client->GetID() << "]: Request disconnection";
|
||||
client->Disconnect();
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG(ERROR) << "[Client " << client->GetID() << "]: Invaild reuqest code";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NetServer::notifyAllConnections(net::Message<net::MessageTypes> &message)
|
||||
{
|
||||
LOG(INFO) << "Sent broadcast to ";
|
||||
unsigned int counter = 0;
|
||||
//Send message to all listed active connections
|
||||
for(auto &connection: m_deqConnections){
|
||||
connection->Send(message);
|
||||
counter++;
|
||||
}
|
||||
LOG(INFO) << "Sent broadcast to " << counter << " devices";
|
||||
}
|
||||
|
||||
|
||||
std::stringstream& NetServer::getBodyData(std::stringstream& buffer)
|
||||
{
|
||||
DataModel::Instance()->readAllDataFiles(buffer);
|
||||
DataModel::Instance()->readPermanentData(buffer, false);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::stringstream& NetServer::getBodyLog(std::stringstream& buffer, net::Message<net::MessageTypes> msgRcv)
|
||||
{
|
||||
unsigned long long fromTime;
|
||||
if (msgRcv.header.size == 0)
|
||||
fromTime = 0;
|
||||
else
|
||||
msgRcv >> fromTime;
|
||||
DataModel::Instance()->readLogFile(buffer, fromTime);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::stringstream& NetServer::getBodyCondition(std::stringstream& buffer)
|
||||
{
|
||||
DataModel::Instance()->readTemporaryData(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
#include <sstream>
|
||||
|
||||
#include <asio.hpp>
|
||||
#include "net_server_client.h"
|
||||
|
||||
namespace net {
|
||||
enum class MessageTypes : uint32_t
|
||||
{
|
||||
ServerAccept,
|
||||
ServerDeny,
|
||||
ServerData,
|
||||
ServerLog,
|
||||
ServerCondition,
|
||||
ServerAlert,
|
||||
ServerCloseConnection,
|
||||
};
|
||||
}
|
||||
|
||||
class NetServer : public net::ServerInterface<net::MessageTypes>
|
||||
{
|
||||
public:
|
||||
NetServer(uint16_t nPort) : net::ServerInterface<net::MessageTypes>(nPort) {}
|
||||
NetServer() = delete;
|
||||
NetServer(NetServer&) = delete;
|
||||
|
||||
void Stop() override;
|
||||
bool Start() override;
|
||||
|
||||
bool isRunning(){return serverRunning;}
|
||||
|
||||
void notifyAllConnections(net::Message<net::MessageTypes>& message);
|
||||
|
||||
bool serverRunning = false;
|
||||
protected:
|
||||
virtual bool OnClientConnect(std::shared_ptr<net::Connection<net::MessageTypes>> client) override;
|
||||
|
||||
// Called when a client appears to have disconnected
|
||||
virtual void OnClientDisconnect(std::shared_ptr<net::Connection<net::MessageTypes>> client) override;
|
||||
|
||||
// Called when a message arrives
|
||||
virtual void OnMessage(std::shared_ptr<net::Connection<net::MessageTypes>> client, net::Message<net::MessageTypes>& msg) override;
|
||||
|
||||
private:
|
||||
//Functions to collect the data for response message body
|
||||
|
||||
//Collects Data message body
|
||||
std::stringstream& getBodyData(std::stringstream& buffer);
|
||||
//Collects Lof files message body
|
||||
std::stringstream& getBodyLog(std::stringstream& buffer, net::Message<net::MessageTypes> msgRcv);
|
||||
//Collects Electrical Condition values message body
|
||||
std::stringstream& getBodyCondition(std::stringstream& buffer);
|
||||
|
||||
};
|
@ -1 +0,0 @@
|
||||
#include "ParameterCharP.h"
|
@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
#include "ParameterInterface.h"
|
||||
|
||||
class ParameterCharP : public ParameterInterface<char*>
|
||||
{
|
||||
private:
|
||||
char* param = nullptr;
|
||||
const uint8_t length;
|
||||
public:
|
||||
ParameterCharP(const ModbusRegister description, const uint16_t address, const uint8_t _length, const Access access) : ParameterInterface(description, address, access), length(_length) {};
|
||||
ParameterCharP(const ModbusRegister description, const unsigned short address, const uint8_t _length, const Access access, const Category cat) : ParameterInterface(description, address, access, cat), length(_length) {};
|
||||
virtual ~ParameterCharP(){}
|
||||
|
||||
uint8_t getSize() const override { return length; }
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
#include "ParameterDouble.h"
|
||||
|
@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
#include "ParameterInterface.h"
|
||||
typedef double FP64;
|
||||
|
||||
class ParameterDouble : public ParameterInterface<FP64>
|
||||
{
|
||||
private:
|
||||
FP64 param = 0.0;
|
||||
public:
|
||||
ParameterDouble(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {};
|
||||
ParameterDouble(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {};
|
||||
virtual ~ParameterDouble(){}
|
||||
|
||||
uint8_t getSize() const override { return 64; }
|
||||
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
#include "ParameterFloat.h"
|
||||
|
@ -1,17 +0,0 @@
|
||||
#pragma once
|
||||
#include "ParameterInterface.h"
|
||||
|
||||
typedef float FP32;
|
||||
|
||||
class ParameterFloat : public ParameterInterface<FP32>
|
||||
{
|
||||
private:
|
||||
public:
|
||||
ParameterFloat(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {};
|
||||
ParameterFloat(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {};
|
||||
virtual ~ParameterFloat(){}
|
||||
|
||||
uint8_t getSize() const override { return 32; }
|
||||
|
||||
};
|
||||
|
@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
#include "ModbusRegister.h"
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
class ModbusInterface;
|
||||
|
||||
enum class Access {
|
||||
//Access Permission of a given MODBUS Parameter
|
||||
R,
|
||||
W,
|
||||
RW,
|
||||
CMD
|
||||
};
|
||||
|
||||
struct ParameterSpecification {
|
||||
ModbusRegister description;
|
||||
Category cat;
|
||||
uint16_t address;
|
||||
uint8_t length;
|
||||
Access access;
|
||||
std::vector<uint16_t> readedBytes;
|
||||
bool error = true;
|
||||
std::unique_ptr<ModbusInterface>& connection;
|
||||
|
||||
ParameterSpecification(ModbusRegister d, Category c, uint16_t ad, uint8_t l, std::unique_ptr<ModbusInterface>& i, Access ac) :
|
||||
description(d), cat(c), address(ad), length(l), access(ac), connection(i) {}
|
||||
|
||||
bool isReadable(){ return access == Access::R || access == Access::RW;}
|
||||
bool isWritable(){ return access == Access::W || access == Access::RW || access == Access::CMD;}
|
||||
};
|
||||
|
||||
|
||||
template<typename S>
|
||||
class ParameterInterface
|
||||
{
|
||||
protected:
|
||||
S currentParam{};
|
||||
|
||||
const ModbusRegister valueDescription;
|
||||
const uint16_t modbusAddress;
|
||||
const Access access;
|
||||
const Category cat = Category::NONE;
|
||||
|
||||
public:
|
||||
ParameterInterface(const ModbusRegister description, const unsigned short address, const Access access) : valueDescription(description), modbusAddress(address), access(access) {};
|
||||
ParameterInterface(const ModbusRegister description, const unsigned short address, const Access access, const Category _cat) : valueDescription(description), modbusAddress(address), access(access), cat(_cat) {};
|
||||
virtual ~ParameterInterface(){}
|
||||
|
||||
bool isReadable() { return (access == Access::R || access == Access::RW); }
|
||||
S getParam()const { return currentParam; };
|
||||
uint16_t getModbusAddress() { return modbusAddress; }
|
||||
ModbusRegister getValueDescription() { return valueDescription; };
|
||||
Category getCategory()const {return cat;}
|
||||
virtual uint8_t getSize() const = 0;
|
||||
|
||||
ParameterSpecification getSpecification(std::unique_ptr<ModbusInterface>& connection) const { return ParameterSpecification(valueDescription, cat, modbusAddress, getSize(), connection, access); }
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
#include "ParameterS16.h"
|
||||
|
@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
#include "ParameterInterface.h"
|
||||
|
||||
class ParameterS16 : public ParameterInterface<int16_t>
|
||||
{
|
||||
private:
|
||||
int16_t param = 0;
|
||||
public:
|
||||
ParameterS16(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {};
|
||||
ParameterS16(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {};
|
||||
virtual ~ParameterS16(){}
|
||||
|
||||
uint8_t getSize() const override { return 16; }
|
||||
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
#include "ParameterUInt16.h"
|
||||
|
@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
#include "ParameterInterface.h"
|
||||
|
||||
class ParameterUInt16 : public ParameterInterface<uint16_t>
|
||||
{
|
||||
private:
|
||||
uint16_t param = 0;
|
||||
public:
|
||||
ParameterUInt16(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {};
|
||||
ParameterUInt16(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {};
|
||||
virtual ~ParameterUInt16(){}
|
||||
|
||||
uint8_t getSize() const override { return 16; }
|
||||
|
||||
};
|
@ -1,2 +0,0 @@
|
||||
#include "ParameterUInt32.h"
|
||||
|
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
#include "ParameterInterface.h"
|
||||
class ParameterUInt32 : public ParameterInterface<uint32_t>
|
||||
{
|
||||
private:
|
||||
uint32_t param = 0;
|
||||
public:
|
||||
ParameterUInt32(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {};
|
||||
ParameterUInt32(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {};
|
||||
virtual ~ParameterUInt32(){}
|
||||
|
||||
uint8_t getSize() const override { return 32; }
|
||||
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
#include "PublisherBenderRcm.h"
|
||||
#include "SystemConfig.h"
|
||||
|
||||
PublisherBenderRcm::PublisherBenderRcm() : PublisherInterface(PublisherType::RCMS_BENDER)
|
||||
{
|
||||
connection = std::make_unique<ModbusRTU>(SystemConfig::getIntConfigParameter("modbus_rtu_slave_address"),
|
||||
SystemConfig::getStringConfigParameter("modbus_rtu_device"),
|
||||
SystemConfig::getIntConfigParameter("modbus_rtu_baud"),
|
||||
SystemConfig::getStringConfigParameter("modbus_rtu_pairity").at(0),
|
||||
SystemConfig::getIntConfigParameter("modbus_rtu_stop_bits"));
|
||||
|
||||
modbusData = std::make_unique<ModbusDataBender>(this->id);
|
||||
}
|
||||
|
||||
std::string PublisherBenderRcm::getName() const
|
||||
{
|
||||
std::stringstream name;
|
||||
name << "RCM - Bender (ID: " << id << ")";
|
||||
return name.str();
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "PublisherType.h"
|
||||
#include "ModbusDataBender.h"
|
||||
#include "PublisherInterface.h"
|
||||
#include "modbus_interface_lib.h"
|
||||
|
||||
class PublisherBenderRcm :
|
||||
public PublisherInterface
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
PublisherBenderRcm();
|
||||
|
||||
std::string getName()const override;
|
||||
};
|
||||
|
@ -1,30 +0,0 @@
|
||||
#include "PublisherData.h"
|
||||
|
||||
PublisherData::PublisherData(const int _publisherID, PublisherType pubType, float _value) :
|
||||
publisherID(_publisherID), publisherType(pubType), values(std::vector<float>())
|
||||
{
|
||||
values.push_back(_value);
|
||||
}
|
||||
|
||||
PublisherData::PublisherData(const int _publisherID, PublisherType pubType, std::vector<float> _values) :
|
||||
publisherID(_publisherID), publisherType(pubType), values(_values)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PublisherData::PublisherData() :
|
||||
publisherID(0), publisherType(PublisherType::NA), values({ 0.0 })
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const PublisherData& data){
|
||||
for (u_int i = 0; i < data.values.size()-1; i++) {
|
||||
os << data.values[i] << ",";
|
||||
}
|
||||
os << data.values[data.values.size()-1] << ";";
|
||||
return os;
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "PublisherType.h"
|
||||
|
||||
using namespace std::chrono;
|
||||
typedef unsigned int u_int;
|
||||
|
||||
class PublisherData
|
||||
{
|
||||
private:
|
||||
u_int publisherID;
|
||||
const PublisherType publisherType;
|
||||
system_clock::time_point time_generated;
|
||||
std::vector<float> values;
|
||||
|
||||
public:
|
||||
//constructors
|
||||
PublisherData(const int _publisherID, PublisherType pubType, float value);
|
||||
PublisherData(const int _publisherID, PublisherType pubType, std::vector<float> _values);
|
||||
PublisherData();
|
||||
|
||||
//void updateTime() { this->time_generated = system_clock::now(); }
|
||||
inline void addData(const float data) { values.push_back(data); }
|
||||
|
||||
u_int getID() { return publisherID; }
|
||||
std::vector<float>& getValues() { return values; };
|
||||
|
||||
//getter/setter
|
||||
PublisherType getType()const { return publisherType; }
|
||||
system_clock::time_point getTimeGenerated()const { return time_generated; }
|
||||
void setTimeGenerated(const std::chrono::system_clock::time_point t) { time_generated = t; }
|
||||
|
||||
|
||||
bool hasData()const { return values.size() == 0 ? false : true; }
|
||||
|
||||
//operator
|
||||
friend std::ostream& operator<<(std::ostream&, const PublisherData&);
|
||||
friend std::istream& operator>>(std::istream&, PublisherData&);
|
||||
};
|
||||
|
@ -1,19 +0,0 @@
|
||||
#include "PublisherInterface.h"
|
||||
|
||||
#include <easylogging++.h>
|
||||
|
||||
u_int PublisherInterface::id_static = 0;
|
||||
|
||||
PublisherInterface::PublisherInterface(const PublisherType type)
|
||||
: id(id_static++), type(type)
|
||||
{}
|
||||
|
||||
|
||||
bool PublisherInterface::operator==(const PublisherInterface& p2) const{
|
||||
return this->id == p2.id;
|
||||
}
|
||||
|
||||
ts_queue<ParameterSpecification>& PublisherInterface::enqueueReadingRegisters(ts_queue< ParameterSpecification>& queue, Category cat){
|
||||
modbusData->modbusRegisterCat(cat, queue, connection);
|
||||
return queue;
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "PublisherData.h"
|
||||
#include "ModbusDataInterface.h"
|
||||
#include "modbus_interface_lib.h"
|
||||
#include "ts_queue.h"
|
||||
#include "SystemConfig.h"
|
||||
|
||||
|
||||
class PublisherInterface
|
||||
{
|
||||
private:
|
||||
static u_int id_static;
|
||||
|
||||
protected:
|
||||
const u_int id;
|
||||
const PublisherType type;
|
||||
std::unique_ptr<ModbusInterface> connection;
|
||||
std::unique_ptr<ModbusDataInterface> modbusData;
|
||||
|
||||
public:
|
||||
PublisherInterface(const PublisherType type);
|
||||
virtual ~PublisherInterface(){}
|
||||
|
||||
ts_queue<ParameterSpecification>& enqueueReadingRegisters(ts_queue<ParameterSpecification>& queue, Category cat);
|
||||
|
||||
virtual std::string getName()const = 0;
|
||||
|
||||
bool open(){ return connection->openConnection(); }
|
||||
bool isOpen() const { return connection->getIsOpen(); }
|
||||
|
||||
u_int getID()const { return id; }
|
||||
//operator overloading
|
||||
inline bool operator==(const PublisherInterface& p2)const;
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
#include "PublisherPowercenter.h"
|
||||
#include <easylogging++.h>
|
||||
|
||||
|
||||
PublisherPowercenter::PublisherPowercenter() : PublisherInterface(PublisherType::POWERCENTER)
|
||||
{
|
||||
connection = std::make_unique<ModbusTCP>(SystemConfig::getIntConfigParameter("modbus_tcp_slave_address"),
|
||||
SystemConfig::getStringConfigParameter("modbus_poc_ip"),
|
||||
SystemConfig::getIntConfigParameter("modbus_poc_port"));
|
||||
|
||||
modbusData = std::make_unique<ModbusDataPOC>(this->id);
|
||||
}
|
||||
|
||||
PublisherPowercenter::~PublisherPowercenter() {
|
||||
//modbus->disconnect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::string PublisherPowercenter::getName() const
|
||||
{
|
||||
std::stringstream name;
|
||||
name << "Powercenter - Siemens (ID: " << id << ")";
|
||||
return name.str();
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
#include "PublisherInterface.h"
|
||||
#include "ModbusDataPOC.h"
|
||||
#include <memory>
|
||||
|
||||
class PublisherPowercenter :
|
||||
public PublisherInterface
|
||||
{
|
||||
private:
|
||||
|
||||
public:
|
||||
PublisherPowercenter();
|
||||
virtual ~PublisherPowercenter();
|
||||
|
||||
std::string getName()const override;
|
||||
};
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
enum class PublisherType{
|
||||
NA = 0,
|
||||
RCMS_BENDER,
|
||||
POWERCENTER
|
||||
};
|
@ -1,137 +0,0 @@
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
|
||||
#include "SystemConfig.h"
|
||||
#include "CustomStringUtilities.h"
|
||||
|
||||
std::map<std::string, int> SystemConfig::intParameter;
|
||||
std::map<std::string, float> SystemConfig::floatParameter;
|
||||
std::map<std::string, std::string> SystemConfig::stringParameter;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, SystemConfig& config) {
|
||||
os << "System Configuration: " << std::endl;
|
||||
std::for_each(config.intParameter.begin(), config.intParameter.end(),
|
||||
[&config, &os](auto& param) { os << "\t"<< param.first << ":\t" << param.second << std::endl; });
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Read in the data from the config file
|
||||
void SystemConfig::readStandardConfig()
|
||||
{
|
||||
std::fstream fileStream(filePath, std::ios::in);
|
||||
if (fileStream.is_open()) {
|
||||
std::stringstream buf;
|
||||
while (std::getline(fileStream, fileContent))
|
||||
buf << fileContent << '\n';
|
||||
fileContent = buf.str();
|
||||
fileStream.close();
|
||||
parseConfFile();
|
||||
}
|
||||
else {
|
||||
LOG(ERROR) << "Couldn't open " << filePath << " for reading -> using standard configuration instead";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Place parameters to read in here
|
||||
SystemConfig::SystemConfig()
|
||||
{
|
||||
//define name and default value here
|
||||
intParameter.emplace("read_data_interval_s", 60);
|
||||
intParameter.emplace("tcp_server_port", 7777);
|
||||
stringParameter.emplace("modbus_poc_ip", "127.0.0.1");
|
||||
intParameter.emplace("modbus_poc_port", 502);
|
||||
intParameter.emplace("alert_read_interval", 5);
|
||||
stringParameter.emplace("modbus_rtu_device", "dev/tty0");
|
||||
intParameter.emplace("modbus_rtu_baud", 9800);
|
||||
intParameter.emplace("modbus_rtu_stop_bits", 1);
|
||||
stringParameter.emplace("modbus_rtu_pairity", "E");
|
||||
intParameter.emplace("modbus_rtu_slave_address", 1);
|
||||
intParameter.emplace("modbus_tcp_slave_address", 1);
|
||||
intParameter.emplace("permanent_param_history", 1000);
|
||||
floatParameter.emplace("crit_residual_current", 30.0);
|
||||
intParameter.emplace("crit_residual_timerange", 12);
|
||||
intParameter.emplace("update_model_rate", 100);
|
||||
intParameter.emplace("narrow_block", 100);
|
||||
}
|
||||
|
||||
|
||||
void SystemConfig::parseConfFile()
|
||||
{
|
||||
//remove comments
|
||||
std::regex patternComment("#(\\w|\\s)*\n+");
|
||||
fileContent = std::regex_replace(fileContent, patternComment, "\n");
|
||||
|
||||
auto posHeader = fileContent.find("SystemConfig:");
|
||||
if (posHeader == std::string::npos){
|
||||
LOG(ERROR) << "Found no Header for the configuration file -> using standard configuration instead";
|
||||
return;
|
||||
}
|
||||
for (auto& param : intParameter) {
|
||||
std::regex pattern{ param.first + "\\s*=\\s*\\w+" };
|
||||
std::smatch match;
|
||||
if (std::regex_search(fileContent, match, pattern)) {
|
||||
std::string numberString = match.str();
|
||||
CustomStringUtilities::removeAllWhitespaces(numberString);
|
||||
param.second = std::stoi(numberString.substr(numberString.find('=') + 1));
|
||||
}
|
||||
else {
|
||||
LOG(INFO) << "No specified config paramter (int) for " << param.first << ", using the default value " << param.second;
|
||||
}
|
||||
}
|
||||
for (auto& param : floatParameter) {
|
||||
std::regex pattern{ param.first + "\\s*=\\s*\\d+\\.\\d*" };
|
||||
std::smatch match;
|
||||
if (std::regex_search(fileContent, match, pattern)) {
|
||||
std::string numberString = match.str();
|
||||
CustomStringUtilities::removeAllWhitespaces(numberString);
|
||||
param.second = std::stof(numberString.substr(numberString.find('=') + 1));
|
||||
}
|
||||
else {
|
||||
LOG(INFO) << "No specified config paramter (float) for " << param.first << ", using the default value " << param.second;
|
||||
}
|
||||
}
|
||||
for (auto& param : stringParameter) {
|
||||
std::regex pattern{ param.first + "\\s*=\\s*(\\w|.)*" };
|
||||
std::smatch match;
|
||||
if (std::regex_search(fileContent, match, pattern)) {
|
||||
std::string paramString = match.str();
|
||||
CustomStringUtilities::removeAllWhitespaces(paramString);
|
||||
param.second = paramString.substr(paramString.find('=') + 1);
|
||||
}
|
||||
else {
|
||||
LOG(INFO) << "No specified config paramter (float) for " << param.first << ", using the default value " << param.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int SystemConfig::getIntConfigParameter(std::string paramName){
|
||||
auto it = intParameter.find(paramName.data());
|
||||
if (it != intParameter.end()) {
|
||||
return it->second;
|
||||
}
|
||||
LOG(ERROR) << "Tried to read non existant int config parameter " << paramName;
|
||||
return 0;
|
||||
}
|
||||
|
||||
float SystemConfig::getFloatConfigParameter(std::string paramName){
|
||||
auto it = floatParameter.find(paramName.data());
|
||||
if (it != floatParameter.end()) {
|
||||
return it->second;
|
||||
}
|
||||
LOG(ERROR) << "Tried to read non existant float config parameter " << paramName;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
std::string SystemConfig::getStringConfigParameter(std::string paramName)
|
||||
{
|
||||
auto it = stringParameter.find(paramName.data());
|
||||
if (it != stringParameter.end()) {
|
||||
return it->second;
|
||||
}
|
||||
LOG(ERROR) << "Tried to read non existant float config parameter " << paramName;
|
||||
return "";
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <easylogging++.h>
|
||||
#include <memory>
|
||||
|
||||
class SystemConfig
|
||||
{
|
||||
public:
|
||||
void readStandardConfig();
|
||||
SystemConfig();
|
||||
static int getIntConfigParameter(std::string paramName);
|
||||
static float getFloatConfigParameter(std::string paramName);
|
||||
static std::string getStringConfigParameter(std::string paramName);
|
||||
private:
|
||||
//Parameters to read from the file (name and default value)
|
||||
static std::map<std::string, int> intParameter;
|
||||
static std::map<std::string, float> floatParameter;
|
||||
static std::map<std::string, std::string> stringParameter;
|
||||
|
||||
const std::string filePath = "system_conf.conf";
|
||||
std::string fileContent;
|
||||
void parseConfFile();
|
||||
friend std::ostream& operator<<(std::ostream& os, SystemConfig& config);
|
||||
};
|
@ -1,203 +0,0 @@
|
||||
#include "SystemControler.h"
|
||||
#include <easylogging++.h>
|
||||
#include "SystemConfig.h"
|
||||
#include <optional>
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
std::atomic<bool> dataReadingCancelled = false;
|
||||
std::atomic<bool> updateServerCancelled = false;
|
||||
std::atomic<bool> threadRunning = false;
|
||||
std::thread* readDataThread = nullptr;
|
||||
|
||||
|
||||
SystemControler::SystemControler() :
|
||||
dataModel(DataModel::Instance()), dataAcquire(std::make_shared<DataAcquisition>())
|
||||
{
|
||||
this->alertReadInterval = SystemConfig::getIntConfigParameter("alert_read_interval");
|
||||
this->serverPort = SystemConfig::getIntConfigParameter("tcp_server_port");
|
||||
|
||||
initializePermanentStorage();
|
||||
}
|
||||
|
||||
SystemControler::~SystemControler()
|
||||
{
|
||||
LOG(INFO) << "Wait until continous modbus reading thread finishes...";
|
||||
if(readDataThread != nullptr && threadRunning){
|
||||
dataReadingCancelled = true;
|
||||
if(readDataThread->joinable())
|
||||
readDataThread->join();
|
||||
}
|
||||
|
||||
LOG(INFO) << "Wait until server update thread finishes...";
|
||||
if(serverUpdateThread != nullptr){
|
||||
updateServerCancelled = true;
|
||||
if(serverUpdateThread->joinable())
|
||||
serverUpdateThread->join();
|
||||
}
|
||||
server.reset();
|
||||
|
||||
if(readDataThread != nullptr)
|
||||
delete readDataThread;
|
||||
if(serverUpdateThread != nullptr)
|
||||
delete serverUpdateThread;
|
||||
|
||||
DataModel::Destroy();
|
||||
}
|
||||
|
||||
|
||||
void updateServer(std::shared_ptr<NetServer> server, std::chrono::milliseconds ms) {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
//Run as long as server exists and is not stopped
|
||||
while (server != nullptr && !server->isStopped()) {
|
||||
if(updateServerCancelled)
|
||||
return;
|
||||
|
||||
//Check for alerts
|
||||
auto& alerts = DataModel::Instance()->getAlerts();
|
||||
while(alerts.size() > 0){
|
||||
net::Message<net::MessageTypes> msg;
|
||||
auto a = alerts.pop_front();
|
||||
msg.header.id = net::MessageTypes::ServerAlert;
|
||||
short bitmask = 0;
|
||||
std::memcpy(&bitmask, a.readedBytes.data(), 2);
|
||||
std::stringstream tmp;
|
||||
tmp << "Modbus " << a.connection->getConnectionType() << " alert: " << ModbusDataPOC::getStatusMessage(bitmask);
|
||||
msg << tmp.str();
|
||||
server->MessageAllClients(msg);
|
||||
}
|
||||
|
||||
//check for messages, specify no max message count
|
||||
//return if no message is available
|
||||
server->Update(-1, false);
|
||||
std::this_thread::sleep_until(now + ms);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void SystemControler::startIpTcpServer() {
|
||||
try {
|
||||
//start server
|
||||
if(server == nullptr)
|
||||
server = std::make_shared<NetServer>(serverPort);
|
||||
|
||||
if(server->isRunning()){
|
||||
LOG(WARNING) << "Invoked server start, but server is already running";
|
||||
return;
|
||||
}
|
||||
|
||||
server->Start();
|
||||
|
||||
//Continous running update thread, that checks for incomming messages
|
||||
serverUpdateThread = new std::thread(updateServer, server, serverTickRateMs);
|
||||
}
|
||||
catch (asio::system_error& e) {
|
||||
LOG(FATAL) << "[SERVER]: Error "
|
||||
<< e.code() << " >> "
|
||||
<< e.what();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
thread whhich acquires data from all registered publishers
|
||||
->pointer to dataAcquisition, which handles the readiong requests
|
||||
->intervalSec: # of seconds
|
||||
*/
|
||||
void enqueueRegisterThread(std::shared_ptr<DataAcquisition> dataAcquisition, SystemControler* controler, const u_int intervalSec)
|
||||
{
|
||||
threadRunning = true;
|
||||
dataReadingCancelled = false;
|
||||
LOG(INFO) << "Started continous data reading";
|
||||
while (!dataReadingCancelled) {
|
||||
const auto currentTime = system_clock::now();
|
||||
|
||||
//read in the data from the publishers
|
||||
dataAcquisition->enqueuePublisherRegister();
|
||||
|
||||
controler->evaluate();
|
||||
for (u_int i = 1; i <= intervalSec; i++)
|
||||
{
|
||||
if (dataReadingCancelled) {
|
||||
LOG(INFO) << "Stopped continous data reading";
|
||||
threadRunning = false;
|
||||
dataReadingCancelled = false;
|
||||
return;
|
||||
}
|
||||
//Wait for 1 additional second and then check for cancelled thread
|
||||
std::this_thread::sleep_until(currentTime + std::chrono::seconds(1) * i);
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Stopped continous data reading";
|
||||
dataReadingCancelled = false;
|
||||
threadRunning = false;
|
||||
}
|
||||
|
||||
//Invokes continous modbus alert reading
|
||||
void SystemControler::startEnqueuingRegister()
|
||||
{
|
||||
if (threadRunning && !dataReadingCancelled) {
|
||||
LOG(ERROR) << "ILLEGAL STATE: Invoked starting readDataThread while thread is already running (Thread ID: " << readDataThread->get_id() << ")";
|
||||
return;
|
||||
}
|
||||
//Continue thread if it is already cancelled but still running
|
||||
else if (threadRunning && dataReadingCancelled) {
|
||||
dataReadingCancelled = false;
|
||||
}
|
||||
else {
|
||||
if(readDataThread != nullptr)
|
||||
delete readDataThread;
|
||||
readDataThread = new std::thread(enqueueRegisterThread, dataAcquire, this, alertReadInterval);
|
||||
}
|
||||
}
|
||||
|
||||
//Cancels continous modbus alert reading
|
||||
void SystemControler::cancelReadingModbusAlerts()
|
||||
{
|
||||
if (dataReadingCancelled){
|
||||
LOG(INFO) << "Thread is already cancelled, waiting for thread to stop";
|
||||
}
|
||||
else if (!threadRunning || readDataThread == nullptr){
|
||||
LOG(ERROR) << "ILLEGAL STATE: Invoke cancelling of readDataThread while thread is not running";
|
||||
}
|
||||
else{
|
||||
dataReadingCancelled = true;
|
||||
if(readDataThread->joinable())
|
||||
readDataThread->join();
|
||||
}
|
||||
}
|
||||
|
||||
//Check wether to flush or not, else counts up
|
||||
void SystemControler::flushAfterNData()
|
||||
{
|
||||
dataModel->checkForFlushData();
|
||||
}
|
||||
|
||||
//delegate register Publisher
|
||||
void SystemControler::registerPublisher(std::unique_ptr<PublisherInterface> publisher)
|
||||
{
|
||||
dataAcquire->registerPublisher(std::move(publisher));
|
||||
}
|
||||
|
||||
void SystemControler::evaluate(){
|
||||
if(evaluator == nullptr)
|
||||
return;
|
||||
auto alerts = evaluator->evaluate();
|
||||
for(auto &a : alerts){
|
||||
net::Message<net::MessageTypes> msg;
|
||||
msg.header.id = net::MessageTypes::ServerAlert;
|
||||
msg << a.message;
|
||||
server->MessageAllClients(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SystemControler::initializePermanentStorage(){
|
||||
//register Residual Current (dummy) to perform linear regression
|
||||
//set biased fo ML Algorithm
|
||||
dataModel->makePermanent(ModbusRegister::BENDER_Residual_current, true);
|
||||
|
||||
evaluator = std::make_unique<Evaluator>();
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include "DataAcquisition.h"
|
||||
#include "PublisherType.h"
|
||||
#include "Evaluator.h"
|
||||
#include "NetServer.h"
|
||||
|
||||
|
||||
class SystemControler
|
||||
{
|
||||
private:
|
||||
u_int serverPort;
|
||||
u_int alertReadInterval;
|
||||
|
||||
std::unique_ptr<DataModel>& dataModel;
|
||||
std::shared_ptr<DataAcquisition> dataAcquire;
|
||||
std::shared_ptr<Evaluator> evaluator;
|
||||
|
||||
//Estimated raw data which are collected over one hour of continous running in Bytes
|
||||
size_t dataSizePerHour = 0;
|
||||
std::shared_ptr<NetServer> server;
|
||||
|
||||
//Continous server updating with fixed tick rate, spcified in ms
|
||||
std::thread* serverUpdateThread = nullptr;
|
||||
std::chrono::milliseconds serverTickRateMs { 100 };
|
||||
|
||||
void initializePermanentStorage();
|
||||
|
||||
public:
|
||||
SystemControler();
|
||||
~SystemControler();
|
||||
|
||||
void startIpTcpServer();
|
||||
|
||||
//getter
|
||||
std::shared_ptr<DataAcquisition>& getDataAcquisitionRef() { return dataAcquire; }
|
||||
|
||||
//Delegate read requests threaded to to acquire unit
|
||||
void startEnqueuingRegister();
|
||||
|
||||
size_t getDataSizePerHour()const { return dataSizePerHour; };
|
||||
|
||||
void cancelReadingModbusAlerts();
|
||||
|
||||
void flushAfterNData();
|
||||
|
||||
void startModbusWorkerThread(){ dataAcquire->startModbusThread(); }
|
||||
|
||||
void test(){ }
|
||||
|
||||
//delegate register Publisher
|
||||
void registerPublisher(std::unique_ptr<PublisherInterface> publisher);
|
||||
|
||||
//call evaluation manually, delegates call
|
||||
void evaluate();
|
||||
|
||||
unsigned long deleteFiles(std::chrono::seconds olderThan) { return (olderThan == seconds(0)) ? dataModel->removeStoredData() : dataModel->removeStoredData(olderThan); }
|
||||
};
|
@ -1,157 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "net_common.h"
|
||||
#include "net_connection.h"
|
||||
namespace net
|
||||
{
|
||||
template <typename T>
|
||||
class ClientInterface
|
||||
{
|
||||
public:
|
||||
ClientInterface()
|
||||
{}
|
||||
|
||||
virtual ~ClientInterface()
|
||||
{
|
||||
// If the client is destroyed, always try and disconnect from server
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
public:
|
||||
// Connect to server with hostname/ip-address and port
|
||||
bool Connect(const std::string& host, const uint16_t port)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Resolve hostname/ip-address into tangiable physical address
|
||||
asio::ip::tcp::resolver resolver(m_context);
|
||||
asio::ip::tcp::resolver::results_type endpoints = resolver.resolve(host, std::to_string(port));
|
||||
|
||||
// Create connection
|
||||
m_connection = std::make_unique<Connection<T>>(Connection<T>::Owner::CLIENT, m_context, asio::ip::tcp::socket(m_context), m_qMessagesIn);
|
||||
|
||||
// Tell the connection object to connect to server
|
||||
m_connection->ConnectToServer(endpoints);
|
||||
|
||||
// Start Context Thread
|
||||
thrContext = std::thread([this]() { m_context.run(); });
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Client Exception: " << e.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disconnect from server
|
||||
void Disconnect()
|
||||
{
|
||||
// If connection exists, and it's connected then...
|
||||
if(IsConnected())
|
||||
{
|
||||
// ...disconnect from server gracefully
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
|
||||
// Either way, we're also done with the asio context...
|
||||
m_context.stop();
|
||||
// ...and its thread
|
||||
if (thrContext.joinable())
|
||||
thrContext.join();
|
||||
|
||||
// Destroy the connection object
|
||||
m_connection.release();
|
||||
}
|
||||
|
||||
// Check if client is actually connected to a server
|
||||
bool IsConnected()
|
||||
{
|
||||
if (m_connection)
|
||||
return m_connection->IsConnected();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
// Send message to server
|
||||
void Send(const Message<T>& msg)
|
||||
{
|
||||
if (IsConnected())
|
||||
m_connection->Send(msg);
|
||||
}
|
||||
|
||||
// Retrieve queue of messages from server
|
||||
net::ts_dequeue<OwnedMessage<T>>& Incoming()
|
||||
{
|
||||
return m_qMessagesIn;
|
||||
}
|
||||
|
||||
protected:
|
||||
// asio context handles the data transfer...
|
||||
asio::io_context m_context;
|
||||
// ...but needs a thread of its own to execute its work commands
|
||||
std::thread thrContext;
|
||||
// The client has a single instance of a "connection" object, which handles data transfer
|
||||
std::unique_ptr<Connection<T>> m_connection;
|
||||
|
||||
private:
|
||||
// This is the thread safe queue of incoming messages from server
|
||||
net::ts_dequeue<OwnedMessage<T>> m_qMessagesIn;
|
||||
};
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#ifdef _WIN32
|
||||
#define _WIN32_WINNT 0x0A00
|
||||
#endif
|
||||
|
||||
#define ASIO_STANDALONE
|
||||
#include <asio.hpp>
|
||||
#include <asio/ts/buffer.hpp>
|
||||
#include <asio/ts/internet.hpp>
|
@ -1,343 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net_common.h"
|
||||
#include "net_dequeue_ts.h"
|
||||
#include "net_message.h"
|
||||
|
||||
|
||||
|
||||
namespace net
|
||||
{
|
||||
template<typename T>
|
||||
class Connection : public std::enable_shared_from_this<Connection<T>>
|
||||
{
|
||||
public:
|
||||
// A connection is "owned" by either a server or a client, and its
|
||||
// behaviour is slightly different bewteen the two.
|
||||
enum class Owner
|
||||
{
|
||||
SERVER,
|
||||
CLIENT
|
||||
};
|
||||
|
||||
public:
|
||||
// Constructor: Specify Owner, connect to context, transfer the socket
|
||||
// Provide reference to incoming message queue
|
||||
Connection(Owner parent, asio::io_context& asioContext, asio::ip::tcp::socket socket, ts_dequeue<OwnedMessage<T>>& qIn)
|
||||
: m_socket(std::move(socket)), m_asioContext(asioContext), m_qMessagesIn(qIn)
|
||||
{
|
||||
m_nOwnerType = parent;
|
||||
}
|
||||
|
||||
virtual ~Connection()
|
||||
{}
|
||||
|
||||
// This ID is used system wide - its how clients will understand other clients
|
||||
// exist across the whole system.
|
||||
uint32_t GetID() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public:
|
||||
void ConnectToClient(uint32_t uid = 0)
|
||||
{
|
||||
if (m_nOwnerType == Owner::SERVER)
|
||||
{
|
||||
if (m_socket.is_open())
|
||||
{
|
||||
id = uid;
|
||||
ReadHeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectToServer(const asio::ip::tcp::resolver::results_type& endpoints)
|
||||
{
|
||||
// Only clients can connect to servers
|
||||
if (m_nOwnerType == Owner::CLIENT)
|
||||
{
|
||||
// Request asio attempts to connect to an endpoint
|
||||
asio::async_connect(m_socket, endpoints,
|
||||
[this](std::error_code ec, asio::ip::tcp::endpoint endpoint)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
ReadHeader();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Disconnect()
|
||||
{
|
||||
if (IsConnected())
|
||||
asio::post(m_asioContext, [this]() { m_socket.close(); });
|
||||
}
|
||||
|
||||
bool IsConnected() const
|
||||
{
|
||||
return m_socket.is_open();
|
||||
}
|
||||
|
||||
// Prime the connection to wait for incoming messages
|
||||
void StartListening()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
// ASYNC - Send a message, connections are one-to-one so no need to specifiy
|
||||
// the target, for a client, the target is the server and vice versa
|
||||
void Send(const Message<T>& msg)
|
||||
{
|
||||
asio::post(m_asioContext,
|
||||
[this, msg]()
|
||||
{
|
||||
// If the queue has a message in it, then we must
|
||||
// assume that it is in the process of asynchronously being written.
|
||||
// Either way add the message to the queue to be output. If no messages
|
||||
// were available to be written, then start the process of writing the
|
||||
// message at the front of the queue.
|
||||
bool bWritingMessage = !m_qMessagesOut.empty();
|
||||
m_qMessagesOut.push_back(msg);
|
||||
if (!bWritingMessage)
|
||||
{
|
||||
WriteHeader();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
// ASYNC - Prime context to write a message header
|
||||
void WriteHeader()
|
||||
{
|
||||
// If this function is called, we know the outgoing message queue must have
|
||||
// at least one message to send. So allocate a transmission buffer to hold
|
||||
// the message, and issue the work - asio, send these bytes
|
||||
asio::async_write(m_socket, asio::buffer(&m_qMessagesOut.front().header, sizeof(MessageHeader<T>)),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
// asio has now sent the bytes - if there was a problem
|
||||
// an error would be available...
|
||||
if (!ec)
|
||||
{
|
||||
// ... no error, so check if the message header just sent also
|
||||
// has a message body...
|
||||
if (m_qMessagesOut.front().body.size() > 0)
|
||||
{
|
||||
// ...it does, so issue the task to write the body bytes
|
||||
WriteBody();
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...it didnt, so we are done with this message. Remove it from
|
||||
// the outgoing message queue
|
||||
m_qMessagesOut.pop_front();
|
||||
|
||||
// If the queue is not empty, there are more messages to send, so
|
||||
// make this happen by issuing the task to send the next header.
|
||||
if (!m_qMessagesOut.empty())
|
||||
{
|
||||
WriteHeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...asio failed to write the message, we could analyse why but
|
||||
// for now simply assume the connection has died by closing the
|
||||
// socket. When a future attempt to write to this client fails due
|
||||
// to the closed socket, it will be tidied up.
|
||||
std::cout << "[" << id << "] Write Header Fail.\n";
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ASYNC - Prime context to write a message body
|
||||
void WriteBody()
|
||||
{
|
||||
// If this function is called, a header has just been sent, and that header
|
||||
// indicated a body existed for this message. Fill a transmission buffer
|
||||
// with the body data, and send it!
|
||||
asio::async_write(m_socket, asio::buffer(m_qMessagesOut.front().body.data(), m_qMessagesOut.front().body.size()),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// Sending was successful, so we are done with the message
|
||||
// and remove it from the queue
|
||||
m_qMessagesOut.pop_front();
|
||||
|
||||
// If the queue still has messages in it, then issue the task to
|
||||
// send the next messages' header.
|
||||
if (!m_qMessagesOut.empty())
|
||||
{
|
||||
WriteHeader();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sending failed, see WriteHeader() equivalent for description :P
|
||||
std::cout << "[" << id << "] Write Body Fail.\n";
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ASYNC - Prime context ready to read a message header
|
||||
void ReadHeader()
|
||||
{
|
||||
// asio to waits until it receives
|
||||
// enough bytes to form a header of a message. We know the headers are a fixed
|
||||
// size, so allocate a transmission buffer large enough to store it.
|
||||
//Call this function to set up an asynchronous listener for a certain Connection
|
||||
asio::async_read(m_socket, asio::buffer(&m_msgTemporaryIn.header, sizeof(MessageHeader<T>)),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
//Full header readed
|
||||
//Check for message body
|
||||
if (m_msgTemporaryIn.header.size > 0)
|
||||
{
|
||||
//Allocate storage
|
||||
m_msgTemporaryIn.body.resize(m_msgTemporaryIn.header.size);
|
||||
//Read if available
|
||||
ReadBody();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Bodyless message, add to queue
|
||||
AddToIncomingMessageQueue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Failure, probably a disconnection
|
||||
std::cout << "[" << id << "] Error: " << ec.message() << std::endl;
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ASYNC - Prime context to read a message body
|
||||
void ReadBody()
|
||||
{
|
||||
//Called after header has been read successfully
|
||||
//Read the body in the pre allocated storage
|
||||
asio::async_read(m_socket, asio::buffer(m_msgTemporaryIn.body.data(), m_msgTemporaryIn.body.size()),
|
||||
[this](std::error_code ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
// Complete message, add to queue
|
||||
AddToIncomingMessageQueue();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Failure, probably a disconnection
|
||||
std::cout << "[" << id << "] Error reading body: " << ec.message() << std::endl;
|
||||
m_socket.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add a complete message to the incoming queue, with or without body
|
||||
void AddToIncomingMessageQueue()
|
||||
{
|
||||
//Put it in the queue, put a owner to the object to let the server know who send the message (which connection)
|
||||
if(m_nOwnerType == Owner::SERVER)
|
||||
m_qMessagesIn.push_back({ this->shared_from_this(), m_msgTemporaryIn });
|
||||
else
|
||||
m_qMessagesIn.push_back({ nullptr, m_msgTemporaryIn });
|
||||
|
||||
//Done queueing the message, now initialize a new async read to wait for next message
|
||||
ReadHeader();
|
||||
}
|
||||
|
||||
protected:
|
||||
// unique socket to a remote
|
||||
asio::ip::tcp::socket m_socket;
|
||||
|
||||
// This context is shared with the whole asio instance
|
||||
asio::io_context& m_asioContext;
|
||||
|
||||
// This queue holds all messages to be sent to the remote side
|
||||
// of this connection
|
||||
ts_dequeue<Message<T>> m_qMessagesOut;
|
||||
|
||||
// This references the incoming queue of the parent object
|
||||
ts_dequeue<OwnedMessage<T>>& m_qMessagesIn;
|
||||
|
||||
// Incoming messages are constructed asynchronously, so we will
|
||||
// store the part assembled message here, until it is ready
|
||||
Message<T> m_msgTemporaryIn;
|
||||
|
||||
// The "owner" decides how some of the connection behaves
|
||||
Owner m_nOwnerType = Owner::SERVER;
|
||||
|
||||
uint32_t id = 0;
|
||||
|
||||
};
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net_common.h"
|
||||
|
||||
|
||||
namespace net
|
||||
{
|
||||
template<typename T>
|
||||
class ts_dequeue
|
||||
{
|
||||
public:
|
||||
ts_dequeue() = default;
|
||||
ts_dequeue(const ts_dequeue<T>&) = delete;
|
||||
virtual ~ts_dequeue() { clear(); }
|
||||
|
||||
public:
|
||||
// Returns and maintains item at front of Queue
|
||||
const T& front()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return deqQueue.front();
|
||||
}
|
||||
|
||||
// Returns and maintains item at back of Queue
|
||||
const T& back()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return deqQueue.back();
|
||||
}
|
||||
|
||||
// Removes and returns item from front of Queue
|
||||
T pop_front()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
auto t = std::move(deqQueue.front());
|
||||
deqQueue.pop_front();
|
||||
return t;
|
||||
}
|
||||
|
||||
// Removes and returns item from back of Queue
|
||||
T pop_back()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
auto t = std::move(deqQueue.back());
|
||||
deqQueue.pop_back();
|
||||
return t;
|
||||
}
|
||||
|
||||
// Adds an item to back of Queue
|
||||
void push_back(const T& item)
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
deqQueue.emplace_back(std::move(item));
|
||||
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.notify_one();
|
||||
}
|
||||
|
||||
// Adds an item to front of Queue
|
||||
void push_front(const T& item)
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
deqQueue.emplace_front(std::move(item));
|
||||
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.notify_one();
|
||||
}
|
||||
|
||||
// Returns true if Queue has no items
|
||||
bool empty()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return deqQueue.empty();
|
||||
}
|
||||
|
||||
// Returns number of items in Queue
|
||||
size_t count()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return deqQueue.size();
|
||||
}
|
||||
|
||||
// Clears Queue
|
||||
void clear()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
deqQueue.clear();
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
while (empty())
|
||||
{
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.wait(ul);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::mutex muxQueue;
|
||||
std::deque<T> deqQueue;
|
||||
std::condition_variable cvBlocking;
|
||||
std::mutex muxBlocking;
|
||||
};
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, <EFBFBD>OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "net_common.h"
|
||||
#include <string>
|
||||
namespace net
|
||||
{
|
||||
///[OLC_HEADERIFYIER] START "MESSAGE"
|
||||
|
||||
// Message Header is sent at start of all messages. The template allows us
|
||||
// to use "enum class" to ensure that the messages are valid at compile time
|
||||
template <typename T>
|
||||
struct MessageHeader
|
||||
{
|
||||
T id{};
|
||||
uint32_t size = 0;
|
||||
};
|
||||
|
||||
// Message Body contains a header and a std::vector, containing raw bytes
|
||||
// of infomation. This way the message can be variable length, but the size
|
||||
// in the header must be updated.
|
||||
template <typename T>
|
||||
struct Message
|
||||
{
|
||||
// Header & Body vector
|
||||
MessageHeader<T> header{};
|
||||
std::vector<uint8_t> body;
|
||||
|
||||
|
||||
// returns size of entire message packet in bytes
|
||||
size_t size() const
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
|
||||
// Override for std::cout compatibility - produces friendly description of message
|
||||
friend std::ostream& operator << (std::ostream& os, const Message<T>& msg)
|
||||
{
|
||||
os << "ID:" << int(msg.header.id) << " Size:" << msg.header.size;
|
||||
return os;
|
||||
}
|
||||
|
||||
// Convenience Operator overloads - These allow us to add and remove stuff from
|
||||
// the body vector as if it were a stack, so First in, Last Out. These are a
|
||||
// template in itself, because we dont know what data type the user is pushing or
|
||||
// popping, so lets allow them all. NOTE: It assumes the data type is fundamentally
|
||||
// Plain Old Data (POD). TLDR: Serialise & Deserialise into/from a vector
|
||||
|
||||
// Pushes any POD-like data into the message buffer
|
||||
template<typename DataType>
|
||||
friend Message<T>& operator << (Message<T>& msg, const DataType& data)
|
||||
{
|
||||
// Check that the type of the data being pushed is trivially copyable
|
||||
static_assert(std::is_standard_layout<DataType>::value, "Data is too complex to be pushed into vector");
|
||||
|
||||
// Cache current size of vector, as this will be the point we insert the data
|
||||
size_t i = msg.body.size();
|
||||
|
||||
// Resize the vector by the size of the data being pushed
|
||||
msg.body.resize(i + sizeof(DataType));
|
||||
|
||||
// Physically copy the data into the newly allocated vector space
|
||||
std::memcpy(msg.body.data() + i, &data, sizeof(DataType));
|
||||
|
||||
// Recalculate the message size
|
||||
msg.header.size = msg.size();
|
||||
|
||||
// Return the target message so it can be "chained"
|
||||
return msg;
|
||||
}
|
||||
|
||||
//Specified template to write string
|
||||
friend Message<T>& operator << (Message<T>& msg, const std::string& data)
|
||||
{
|
||||
// Cache current size of vector, as this will be the point we insert the data
|
||||
size_t i = msg.body.size();
|
||||
|
||||
// Resize the vector by the size of the data being pushed
|
||||
//msg.body.resize(i + sizeof(data));
|
||||
|
||||
// Physically copy the data of the string character by character
|
||||
msg.body.resize(i + data.size());
|
||||
|
||||
for (size_t index = 0; index < data.size(); index++) {
|
||||
msg.body[i+index] = data.at(index);
|
||||
//std::memcpy(msg.body.data() + i, &c, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
// Recalculate the message size
|
||||
msg.header.size = (uint32_t)msg.size();
|
||||
|
||||
// Return the target message so it can be "chained"
|
||||
return msg;
|
||||
}
|
||||
|
||||
// Pulls any POD-like data form the message buffer
|
||||
template<typename DataType>
|
||||
friend Message<T>& operator >> (Message<T>& msg, DataType& data)
|
||||
{
|
||||
// Check that the type of the data being pushed is trivially copyable
|
||||
static_assert(std::is_standard_layout<DataType>::value, "Data is too complex to be pulled from vector");
|
||||
|
||||
// Cache the location towards the end of the vector where the pulled data starts
|
||||
size_t i = msg.body.size() - sizeof(DataType);
|
||||
|
||||
// Physically copy the data from the vector into the user variable
|
||||
std::memcpy(&data, msg.body.data() + i, sizeof(DataType));
|
||||
|
||||
// Shrink the vector to remove read bytes, and reset end position
|
||||
msg.body.resize(i);
|
||||
|
||||
// Recalculate the message size
|
||||
msg.header.size = msg.size();
|
||||
|
||||
// Return the target message so it can be "chained"
|
||||
return msg;
|
||||
}
|
||||
|
||||
//Specified template to read string
|
||||
friend Message<T>& operator >> (Message<T>& msg, std::string& data)
|
||||
{
|
||||
// Cache the location towards the end of the vector where the pulled data starts
|
||||
size_t i = 0;
|
||||
|
||||
// Physically copy the data from the vector into the user variable
|
||||
std::memcpy(&data, msg.body.data(), msg.body.size());
|
||||
|
||||
// Shrink the vector to remove read bytes, and reset end position
|
||||
msg.body.resize(i);
|
||||
|
||||
// Return the target message so it can be "chained"
|
||||
return msg;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// An "owned" message is identical to a regular message, but it is associated with
|
||||
// a connection. On a server, the owner would be the client that sent the message,
|
||||
// on a client the owner would be the server.
|
||||
|
||||
// Forward declare the connection
|
||||
template <typename T>
|
||||
class Connection;
|
||||
|
||||
template <typename T>
|
||||
struct OwnedMessage
|
||||
{
|
||||
std::shared_ptr<Connection<T>> remote = nullptr;
|
||||
Message<T> msg;
|
||||
|
||||
// Again, a friendly string maker
|
||||
friend std::ostream& operator<<(std::ostream& os, const OwnedMessage<T>& msg)
|
||||
{
|
||||
os << msg.msg;
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,276 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ?OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "net_dequeue_ts.h"
|
||||
#include "net_common.h"
|
||||
#include "net_message.h"
|
||||
#include "net_connection.h"
|
||||
#include "easylogging++.h"
|
||||
|
||||
namespace net
|
||||
{
|
||||
template<typename T>
|
||||
class ServerInterface
|
||||
{
|
||||
public: // Create a server, ready to listen on specified port
|
||||
ServerInterface(uint16_t port)
|
||||
: m_asioAcceptor(m_asioContext, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)) { }
|
||||
|
||||
virtual ~ServerInterface() { Stop(); }
|
||||
|
||||
// Starts the server
|
||||
virtual bool Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Issue a task to the asio context - This is important
|
||||
// as it will prime the context with "work", and stop it
|
||||
// from exiting immediately. Since this is a server, we
|
||||
// want it primed ready to handle clients trying to
|
||||
// connect.
|
||||
WaitForClientConnection();
|
||||
// Launch the asio context in its own thread
|
||||
m_threadContext = std::thread([this]() { m_asioContext.run(); });
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// Something prohibited the server from listening
|
||||
LOG(ERROR) << "[SERVER] Exception: " << e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isStopped()const {
|
||||
return m_asioContext.stopped();
|
||||
}
|
||||
|
||||
// Stops the server!
|
||||
virtual void Stop()
|
||||
{
|
||||
// Request the context to close
|
||||
m_asioContext.stop();
|
||||
|
||||
// Tidy up the context thread
|
||||
if (m_threadContext.joinable()) m_threadContext.join();
|
||||
|
||||
// Inform someone, anybody, if they care...
|
||||
LOG(INFO) << "[SERVER] Stopped!\n";
|
||||
}
|
||||
|
||||
// ASYNC - Instruct asio to wait for connection
|
||||
void WaitForClientConnection()
|
||||
{
|
||||
// Prime context with an instruction to wait until a socket connects. This
|
||||
// is the purpose of an "acceptor" object. It will provide a unique socket
|
||||
// for each incoming connection attempt
|
||||
m_asioAcceptor.async_accept(
|
||||
[this](std::error_code ec, asio::ip::tcp::socket socket)
|
||||
{
|
||||
// Triggered by incoming connection request
|
||||
if (!ec)
|
||||
{
|
||||
LOG(INFO) << "[SERVER] New Connection: " << socket.remote_endpoint();
|
||||
|
||||
// Create a new connection to handle this client
|
||||
std::shared_ptr<Connection<T>> newconn =
|
||||
std::make_shared<Connection<T>>(Connection<T>::Owner::SERVER,
|
||||
m_asioContext, std::move(socket), m_qMessagesIn);
|
||||
|
||||
|
||||
// Give the user server a chance to deny connection
|
||||
if (OnClientConnect(newconn))
|
||||
{
|
||||
// Connection allowed, so add to container of new connections
|
||||
m_deqConnections.push_back(std::move(newconn));
|
||||
|
||||
// Issue a task to the connection's
|
||||
// asio context to sit and wait for bytes to arrive!
|
||||
m_deqConnections.back()->ConnectToClient(nIDCounter++);
|
||||
|
||||
LOG(INFO) << "[" << m_deqConnections.back()->GetID() << "] Connection Approved";
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(INFO) << "[SERVER] Connection Denied";
|
||||
|
||||
// Connection will go out of scope with no pending tasks, so will
|
||||
// get destroyed automatically (smart pointer)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error has occurred during acceptance
|
||||
LOG(INFO) << "[SERVER] New Connection Error: " << ec.message();
|
||||
}
|
||||
|
||||
// Prime the asio context with more work - again simply wait for
|
||||
// another connection...
|
||||
WaitForClientConnection();
|
||||
});
|
||||
}
|
||||
|
||||
// Send a message to a specific client
|
||||
void MessageClient(std::shared_ptr<Connection<T>> client, const Message<T>& msg)
|
||||
{
|
||||
// Check client is legitimate...
|
||||
if (client && client->IsConnected())
|
||||
{
|
||||
// ...and post the message via the connection
|
||||
client->Send(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we cant communicate with client then we may as
|
||||
// well remove the client - let the server know, it may
|
||||
// be tracking it somehow
|
||||
OnClientDisconnect(client);
|
||||
|
||||
// Off you go now, bye bye!
|
||||
client.reset();
|
||||
|
||||
// Then physically remove it from the container
|
||||
m_deqConnections.erase(
|
||||
std::remove(m_deqConnections.begin(), m_deqConnections.end(), client), m_deqConnections.end());
|
||||
}
|
||||
}
|
||||
|
||||
// Send message to all clients
|
||||
void MessageAllClients(const Message<T>& msg, std::shared_ptr<Connection<T>> pIgnoreClient = nullptr)
|
||||
{
|
||||
|
||||
bool bInvalidClientExists = false;
|
||||
|
||||
// Iterate through all clients in container
|
||||
for (auto& client : m_deqConnections)
|
||||
{
|
||||
// Check client is connected...
|
||||
if (client && client->IsConnected())
|
||||
{
|
||||
// ..it is!
|
||||
if(client != pIgnoreClient)
|
||||
client->Send(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The client couldnt be contacted, so assume it has
|
||||
// disconnected.
|
||||
OnClientDisconnect(client);
|
||||
client.reset();
|
||||
|
||||
// Set this flag to then remove dead clients from container
|
||||
bInvalidClientExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dead clients, all in one go - this way, we dont invalidate the
|
||||
// container as we iterated through it.
|
||||
if (bInvalidClientExists)
|
||||
m_deqConnections.erase(
|
||||
std::remove(m_deqConnections.begin(), m_deqConnections.end(), nullptr), m_deqConnections.end());
|
||||
}
|
||||
|
||||
// Force server to respond to incoming messages
|
||||
// size_t nmaxMessages: Assign -1 to unsigned to unspecify max message count
|
||||
// bool bWait: if queue is empty, wait synchronously until message arrives
|
||||
void Update(size_t nMaxMessages = -1, bool bWait = false)
|
||||
{
|
||||
if (bWait) m_qMessagesIn.wait();
|
||||
|
||||
// Process as many messages as you can up to the value
|
||||
// specified
|
||||
size_t nMessageCount = 0;
|
||||
while (nMessageCount < nMaxMessages && !m_qMessagesIn.empty())
|
||||
{
|
||||
// Grab the front message
|
||||
auto msg = m_qMessagesIn.pop_front();
|
||||
|
||||
// Pass to message handler
|
||||
OnMessage(msg.remote, msg.msg);
|
||||
|
||||
nMessageCount++;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
//Overwritable functions to customize server behaviour
|
||||
|
||||
// Called when a client connects, you can veto the connection by returning false
|
||||
virtual bool OnClientConnect(std::shared_ptr<Connection<T>> client) = 0;
|
||||
// Called when a client appears to have disconnected
|
||||
virtual void OnClientDisconnect(std::shared_ptr<Connection<T>> client) = 0;
|
||||
|
||||
// Called when a message arrives
|
||||
virtual void OnMessage(std::shared_ptr<Connection<T>> client, Message<T>& msg) = 0;
|
||||
|
||||
// Thread Safe Queue for incoming message packets
|
||||
ts_dequeue<OwnedMessage<T>> m_qMessagesIn;
|
||||
|
||||
// Container of active validated connections
|
||||
std::deque<std::shared_ptr<Connection<T>>> m_deqConnections;
|
||||
|
||||
// Order of declaration is important - it is also the order of initialisation
|
||||
asio::io_context m_asioContext;
|
||||
std::thread m_threadContext;
|
||||
|
||||
// These things need an asio context
|
||||
asio::ip::tcp::acceptor m_asioAcceptor; // Handles new incoming connection attempts...
|
||||
|
||||
// Clients will be identified in the "wider system" via an ID
|
||||
uint32_t nIDCounter = 10000;
|
||||
};
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
TEMPLATE = app
|
||||
CONFIG += console c++17
|
||||
CONFIG -= app_bundle
|
||||
CONFIG -= qt
|
||||
|
||||
#INCLUDEPATH += ../AsioNetClientServer
|
||||
#LIBS += -L../AsioNetClientServer/debug -lasio_inet_client_server
|
||||
#LIBS += -pthread
|
||||
|
||||
unix: CONFIG += link_pkgconfig
|
||||
unix: PKGCONFIG += libmodbuspp
|
||||
|
||||
|
||||
SOURCES += \
|
||||
CustomStringUtilities.cpp \
|
||||
DataAcquisition.cpp \
|
||||
DataModel.cpp \
|
||||
Evaluator.cpp \
|
||||
MLAnalyzer.cpp \
|
||||
MLLinReg.cpp \
|
||||
ModbusDataBender.cpp \
|
||||
ModbusDataInterface.cpp \
|
||||
ModbusDataPOC.cpp \
|
||||
ModbusInterface.cpp \
|
||||
ModbusRtu.cpp \
|
||||
ModbusTcp.cpp \
|
||||
NetServer.cpp \
|
||||
ParameterCharP.cpp \
|
||||
ParameterDouble.cpp \
|
||||
ParameterFloat.cpp \
|
||||
ParameterS16.cpp \
|
||||
ParameterUInt16.cpp \
|
||||
ParameterUInt32.cpp \
|
||||
PublisherBenderRcm.cpp \
|
||||
PublisherInterface.cpp \
|
||||
PublisherPowercenter.cpp \
|
||||
SystemConfig.cpp \
|
||||
SystemControler.cpp \
|
||||
cappedstorage.cpp \
|
||||
easylogging++.cc \
|
||||
main.cpp
|
||||
|
||||
HEADERS += \
|
||||
CustomStringUtilities.h \
|
||||
DataAcquisition.h \
|
||||
DataModel.h \
|
||||
Evaluator.h \
|
||||
MLAlert.h \
|
||||
MLAnalyzer.h \
|
||||
MLLinReg.h \
|
||||
ModbusDataBender.h \
|
||||
ModbusDataInterface.h \
|
||||
ModbusDataPOC.h \
|
||||
ModbusInterface.h \
|
||||
ModbusRegister.h \
|
||||
ModbusRtu.h \
|
||||
ModbusTcp.h \
|
||||
NetServer.h \
|
||||
ParameterCharP.h \
|
||||
ParameterDouble.h \
|
||||
ParameterFloat.h \
|
||||
ParameterInterface.h \
|
||||
ParameterS16.h \
|
||||
ParameterUInt16.h \
|
||||
ParameterUInt32.h \
|
||||
PublisherBenderRcm.h \
|
||||
PublisherInterface.h \
|
||||
PublisherPowercenter.h \
|
||||
PublisherType.h \
|
||||
SystemConfig.h \
|
||||
SystemControler.h \
|
||||
asioClientServer/net_client.h \
|
||||
asioClientServer/net_common.h \
|
||||
asioClientServer/net_connection.h \
|
||||
asioClientServer/net_dequeue_ts.h \
|
||||
asioClientServer/net_message.h \
|
||||
asioClientServer/net_server.h \
|
||||
cappedstorage.h \
|
||||
modbus_interface_lib.h \
|
||||
net_server_client.h \
|
||||
ts_map.h \
|
||||
ts_queue.h \
|
||||
easylogging++.h
|
||||
|
||||
DISTFILES += \
|
||||
build_baConditionMonitoring/system_conf.conf
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
#include "cappedstorage.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <easylogging++.h>
|
||||
#include "SystemConfig.h"
|
||||
|
||||
CappedStorage::CappedStorage(bool _biased): biased(_biased)
|
||||
{
|
||||
if(biased){
|
||||
X.resize(0, 2);
|
||||
}
|
||||
else
|
||||
X.resize(0, 1);
|
||||
accessMtx = std::make_unique<std::mutex>();
|
||||
}
|
||||
|
||||
void CappedStorage::lock(){
|
||||
accessMtx->lock();
|
||||
}
|
||||
|
||||
void CappedStorage::unlock(){
|
||||
accessMtx->unlock();
|
||||
}
|
||||
|
||||
void CappedStorage::store(const std::vector<uint16_t>& d)
|
||||
{
|
||||
std::scoped_lock lock(*accessMtx);
|
||||
float value = 0;
|
||||
std::memcpy(&value, d.data(), 4);
|
||||
|
||||
const double time = c::time_point_cast<c::seconds>(c::system_clock::now()).time_since_epoch().count();
|
||||
|
||||
X.conservativeResize(X.rows()+1, X.cols());
|
||||
y.conservativeResize(y.rows()+1, 1);
|
||||
y(y.rows()-1, 0) = value;
|
||||
|
||||
if(biased){
|
||||
X(X.rows()-1, 0) = 1;
|
||||
X(X.rows()-1, 1) = time;
|
||||
}
|
||||
else
|
||||
X(X.rows()-1, 0) = time;
|
||||
}
|
||||
|
||||
|
||||
void CappedStorage::clear()
|
||||
{
|
||||
std::scoped_lock lock(*accessMtx);
|
||||
X.resize(0, Eigen::NoChange_t());
|
||||
}
|
||||
|
||||
//Stream operator for 1 dimensional X Matrix
|
||||
std::ostream& operator<<(std::ostream& os, const CappedStorage& c){
|
||||
std::scoped_lock lock(*(c.accessMtx));
|
||||
int col =0;
|
||||
if(c.biased){
|
||||
col = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < c.X.rows(); i++) {
|
||||
os << c.X(i, col) << ",";
|
||||
}
|
||||
os << std::endl;
|
||||
for (int i = 0; i < c.y.size(); i++) {
|
||||
os << c.y(i, 0) << ",";
|
||||
}
|
||||
os << std::endl;
|
||||
|
||||
return os;
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
#ifndef CAPPEDSTORAGE_H
|
||||
#define CAPPEDSTORAGE_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <eigen3/Eigen/Core>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
namespace c = std::chrono;
|
||||
|
||||
//Class for one dimensional double values
|
||||
class CappedStorage
|
||||
{
|
||||
private:
|
||||
//Stored values
|
||||
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> X;
|
||||
Eigen::Matrix<double, Eigen::Dynamic, 1> y;
|
||||
|
||||
std::unique_ptr<std::mutex> accessMtx;
|
||||
|
||||
//If set, storage is extended by bias column (X(:, 1))
|
||||
//to perform ML Algorithms
|
||||
const bool biased;
|
||||
|
||||
public:
|
||||
CappedStorage(bool _biased);
|
||||
|
||||
void store(const std::vector<uint16_t> &d);
|
||||
|
||||
long size() const{ return X.rows(); };
|
||||
|
||||
void clear();
|
||||
|
||||
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>& getX(){return X;}
|
||||
Eigen::Matrix<double, Eigen::Dynamic, 1>& getY(){return y;}
|
||||
long long getN(){return X.cols();}
|
||||
long long getM(){return X.rows();}
|
||||
|
||||
friend std::ostream &operator<<(std::ostream& os, const CappedStorage& c);
|
||||
void lock();
|
||||
void unlock();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // CAPPEDSTORAGE_H
|
File diff suppressed because it is too large
Load Diff
@ -1,84 +0,0 @@
|
||||
//Entry point of application, crates central controller class and initializes the publishers
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include "SystemControler.h"
|
||||
#include "SystemConfig.h"
|
||||
#include <easylogging++.h>
|
||||
#include "PublisherBenderRcm.h"
|
||||
#include "PublisherPowercenter.h"
|
||||
INITIALIZE_EASYLOGGINGPP
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef std::unique_ptr<PublisherInterface> unique_publisher_ptr;
|
||||
typedef unsigned int u_int;
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
//Load global System configuration for parameters
|
||||
SystemConfig configuration;
|
||||
configuration.readStandardConfig();
|
||||
|
||||
|
||||
//Load config file and apply on all loggers
|
||||
std::filesystem::create_directory("log");
|
||||
el::Configurations conf("log/logger_config.conf");
|
||||
el::Loggers::reconfigureAllLoggers(conf);
|
||||
|
||||
LOG(INFO) << "Started execution";
|
||||
|
||||
//Create the System Controller
|
||||
SystemControler controler;
|
||||
|
||||
//register publisher at controler
|
||||
auto poc = std::make_unique<PublisherPowercenter>();
|
||||
auto bender = std::make_unique<PublisherBenderRcm>();
|
||||
|
||||
//controler.registerPublisher(std::move(poc));
|
||||
controler.registerPublisher(std::move(bender));
|
||||
|
||||
//Opens port for tcp/ip client connection
|
||||
controler.startIpTcpServer();
|
||||
|
||||
//Modbus Worker Thread, processes the modbus register queue
|
||||
controler.startModbusWorkerThread();
|
||||
|
||||
//Frequently enqueing of modbus register
|
||||
controler.startEnqueuingRegister();
|
||||
|
||||
|
||||
//Test environment on main thread
|
||||
while(true)
|
||||
{
|
||||
char key = cin.get();
|
||||
switch (key) {
|
||||
case 'l':
|
||||
controler.deleteFiles(std::chrono::seconds(0));
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
controler.cancelReadingModbusAlerts();
|
||||
break;
|
||||
case 'e':
|
||||
controler.evaluate();
|
||||
break;
|
||||
case 'q':
|
||||
return 0;
|
||||
case 'p':
|
||||
std::cout << configuration << std::endl;
|
||||
break;
|
||||
case 'f':
|
||||
controler.flushAfterNData();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO) << "End of execution\n";
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
#include "mltimeseries.h"
|
||||
|
||||
MLtimeSeries::MLtimeSeries()
|
||||
{
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#ifndef MLTIMESERIES_H
|
||||
#define MLTIMESERIES_H
|
||||
#include "MLAnalyzer.h"
|
||||
|
||||
template <typename T>
|
||||
class MLtimeSeries : public MLAnalyzer<T>
|
||||
{
|
||||
public:
|
||||
MLtimeSeries();
|
||||
};
|
||||
|
||||
#endif // MLTIMESERIES_H
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ModbusInterface.h"
|
||||
#include "ModbusRtu.h"
|
||||
#include "ModbusTcp.h"
|
||||
|
||||
#include <modbuspp/modbuspp.h>
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "asioClientServer/net_common.h"
|
||||
#include "asioClientServer/net_dequeue_ts.h"
|
||||
#include "asioClientServer/net_message.h"
|
||||
#include "asioClientServer/net_client.h"
|
||||
#include "asioClientServer/net_server.h"
|
||||
#include "asioClientServer/net_connection.h"
|
@ -1,90 +0,0 @@
|
||||
#ifndef TS_MAP_H
|
||||
#define TS_MAP_H
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
template <typename KEY, typename VALUE>
|
||||
class ts_map
|
||||
{
|
||||
public:
|
||||
ts_map() = default;
|
||||
ts_map(const ts_map<KEY, VALUE>&) = delete;
|
||||
virtual ~ts_map() { clear(); }
|
||||
|
||||
public:
|
||||
void emplaceOrOverwrite(KEY key, VALUE value){
|
||||
std::scoped_lock lock(muxMap);
|
||||
auto it = map.find(key);
|
||||
if(it != map.end()){
|
||||
it->second = value;
|
||||
}
|
||||
else{
|
||||
map.emplace(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
void emplace(KEY key, VALUE&& value){
|
||||
std::scoped_lock lock(muxMap);
|
||||
map.emplace(key, std::move(value));
|
||||
}
|
||||
|
||||
auto find(KEY key){
|
||||
std::scoped_lock lock(muxMap);
|
||||
|
||||
return map.find(key);
|
||||
}
|
||||
|
||||
|
||||
// Returns true if Queue has no items
|
||||
bool empty()
|
||||
{
|
||||
std::scoped_lock lock(muxMap);
|
||||
return map.empty();
|
||||
}
|
||||
|
||||
// Returns number of items in Queue
|
||||
size_t size()
|
||||
{
|
||||
std::scoped_lock lock(muxMap);
|
||||
return map.size();
|
||||
}
|
||||
|
||||
// Clears Queue
|
||||
void clear()
|
||||
{
|
||||
std::scoped_lock lock(muxMap);
|
||||
std::map<KEY, VALUE> empty;
|
||||
std::swap(map, empty);
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
while (empty())
|
||||
{
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.wait(ul);
|
||||
}
|
||||
}
|
||||
|
||||
auto begin() const{
|
||||
return map.begin();
|
||||
}
|
||||
|
||||
|
||||
auto end() const{
|
||||
return map.end();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::mutex muxMap;
|
||||
std::map<KEY, VALUE> map;
|
||||
std::condition_variable cvBlocking;
|
||||
std::mutex muxBlocking;
|
||||
|
||||
};
|
||||
|
||||
#endif // TS_MAP_H
|
@ -1,143 +0,0 @@
|
||||
/*
|
||||
MMO Client/Server Framework using ASIO
|
||||
"Happy Birthday Mrs Javidx9!" - javidx9
|
||||
|
||||
Videos:
|
||||
Part #1: https://youtu.be/2hNdkYInj4g
|
||||
Part #2: https://youtu.be/UbjxGvrDrbw
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018 - 2020 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, <EFBFBD>OneLoneCoder 2019, 2020
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
/*
|
||||
Threadsafe Queue to be accessed from different threads
|
||||
*/
|
||||
template <typename T>
|
||||
class ts_queue
|
||||
{
|
||||
public:
|
||||
ts_queue() = default;
|
||||
ts_queue(const ts_queue<T>&) = delete;
|
||||
virtual ~ts_queue() { clear(); }
|
||||
|
||||
public:
|
||||
// Returns and maintains item at front of Queue
|
||||
const T& front()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return queue.front();
|
||||
}
|
||||
|
||||
// Returns and maintains item at back of Queue
|
||||
const T& back()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return queue.back();
|
||||
}
|
||||
|
||||
// Removes and returns the first item from Queue
|
||||
T pop_front()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
auto t = std::move(queue.front());
|
||||
queue.pop();
|
||||
return t;
|
||||
}
|
||||
|
||||
// Adds an item to front of Queue
|
||||
void push(const T& item)
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
queue.push(std::move(item));
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.notify_one();
|
||||
}
|
||||
|
||||
// Returns true if Queue has no items
|
||||
bool empty()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return queue.empty();
|
||||
}
|
||||
|
||||
// Returns number of items in Queue
|
||||
size_t size()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
return queue.size();
|
||||
}
|
||||
|
||||
// Clears Queue
|
||||
void clear()
|
||||
{
|
||||
std::scoped_lock lock(muxQueue);
|
||||
std::queue<T> empty;
|
||||
std::swap(queue, empty);
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
while (empty())
|
||||
{
|
||||
std::unique_lock<std::mutex> ul(muxBlocking);
|
||||
cvBlocking.wait(ul);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::mutex muxQueue;
|
||||
std::queue<T> queue;
|
||||
std::condition_variable cvBlocking;
|
||||
std::mutex muxBlocking;
|
||||
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user