Browse Source

remove

master
Michael Stuffer 3 years ago
parent
commit
385367879f
71 changed files with 0 additions and 9371 deletions
  1. 0
    21
      ConditionMonitoring/CustomStringUtilities.cpp
  2. 0
    14
      ConditionMonitoring/CustomStringUtilities.h
  3. 0
    163
      ConditionMonitoring/DataAcquisition.cpp
  4. 0
    38
      ConditionMonitoring/DataAcquisition.h
  5. 0
    364
      ConditionMonitoring/DataModel.cpp
  6. 0
    129
      ConditionMonitoring/DataModel.h
  7. 0
    37
      ConditionMonitoring/Evaluator.cpp
  8. 0
    25
      ConditionMonitoring/Evaluator.h
  9. 0
    17
      ConditionMonitoring/MLAlert.h
  10. 0
    42
      ConditionMonitoring/MLAnalyzer.cpp
  11. 0
    37
      ConditionMonitoring/MLAnalyzer.h
  12. 0
    74
      ConditionMonitoring/MLLinReg.cpp
  13. 0
    23
      ConditionMonitoring/MLLinReg.h
  14. 0
    10
      ConditionMonitoring/ModbusDataBender.cpp
  15. 0
    11
      ConditionMonitoring/ModbusDataBender.h
  16. 0
    20
      ConditionMonitoring/ModbusDataInterface.cpp
  17. 0
    34
      ConditionMonitoring/ModbusDataInterface.h
  18. 0
    247
      ConditionMonitoring/ModbusDataPOC.cpp
  19. 0
    20
      ConditionMonitoring/ModbusDataPOC.h
  20. 0
    213
      ConditionMonitoring/ModbusInterface.cpp
  21. 0
    54
      ConditionMonitoring/ModbusInterface.h
  22. 0
    283
      ConditionMonitoring/ModbusRegister.h
  23. 0
    31
      ConditionMonitoring/ModbusRtu.cpp
  24. 0
    26
      ConditionMonitoring/ModbusRtu.h
  25. 0
    25
      ConditionMonitoring/ModbusTcp.cpp
  26. 0
    20
      ConditionMonitoring/ModbusTcp.h
  27. 0
    128
      ConditionMonitoring/NetServer.cpp
  28. 0
    54
      ConditionMonitoring/NetServer.h
  29. 0
    1
      ConditionMonitoring/ParameterCharP.cpp
  30. 0
    15
      ConditionMonitoring/ParameterCharP.h
  31. 0
    2
      ConditionMonitoring/ParameterDouble.cpp
  32. 0
    16
      ConditionMonitoring/ParameterDouble.h
  33. 0
    2
      ConditionMonitoring/ParameterFloat.cpp
  34. 0
    17
      ConditionMonitoring/ParameterFloat.h
  35. 0
    58
      ConditionMonitoring/ParameterInterface.h
  36. 0
    2
      ConditionMonitoring/ParameterS16.cpp
  37. 0
    15
      ConditionMonitoring/ParameterS16.h
  38. 0
    2
      ConditionMonitoring/ParameterUInt16.cpp
  39. 0
    15
      ConditionMonitoring/ParameterUInt16.h
  40. 0
    2
      ConditionMonitoring/ParameterUInt32.cpp
  41. 0
    14
      ConditionMonitoring/ParameterUInt32.h
  42. 0
    20
      ConditionMonitoring/PublisherBenderRcm.cpp
  43. 0
    18
      ConditionMonitoring/PublisherBenderRcm.h
  44. 0
    30
      ConditionMonitoring/PublisherData.cpp
  45. 0
    42
      ConditionMonitoring/PublisherData.h
  46. 0
    19
      ConditionMonitoring/PublisherInterface.cpp
  47. 0
    35
      ConditionMonitoring/PublisherInterface.h
  48. 0
    26
      ConditionMonitoring/PublisherPowercenter.cpp
  49. 0
    16
      ConditionMonitoring/PublisherPowercenter.h
  50. 0
    7
      ConditionMonitoring/PublisherType.h
  51. 0
    137
      ConditionMonitoring/SystemConfig.cpp
  52. 0
    28
      ConditionMonitoring/SystemConfig.h
  53. 0
    203
      ConditionMonitoring/SystemControler.cpp
  54. 0
    60
      ConditionMonitoring/SystemControler.h
  55. 0
    157
      ConditionMonitoring/asioClientServer/net_client.h
  56. 0
    76
      ConditionMonitoring/asioClientServer/net_common.h
  57. 0
    343
      ConditionMonitoring/asioClientServer/net_connection.h
  58. 0
    161
      ConditionMonitoring/asioClientServer/net_dequeue_ts.h
  59. 0
    214
      ConditionMonitoring/asioClientServer/net_message.h
  60. 0
    276
      ConditionMonitoring/asioClientServer/net_server.h
  61. 0
    88
      ConditionMonitoring/baConditionMonitoring.pro
  62. 0
    70
      ConditionMonitoring/cappedstorage.cpp
  63. 0
    49
      ConditionMonitoring/cappedstorage.h
  64. 0
    4569
      ConditionMonitoring/easylogging++.h
  65. 0
    84
      ConditionMonitoring/main.cpp
  66. 0
    6
      ConditionMonitoring/mltimeseries.cpp
  67. 0
    12
      ConditionMonitoring/mltimeseries.h
  68. 0
    7
      ConditionMonitoring/modbus_interface_lib.h
  69. 0
    64
      ConditionMonitoring/net_server_client.h
  70. 0
    90
      ConditionMonitoring/ts_map.h
  71. 0
    143
      ConditionMonitoring/ts_queue.h

+ 0
- 21
ConditionMonitoring/CustomStringUtilities.cpp View File

#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));
}



+ 0
- 14
ConditionMonitoring/CustomStringUtilities.h View File

#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&);
};


+ 0
- 163
ConditionMonitoring/DataAcquisition.cpp View File

#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();
}


+ 0
- 38
ConditionMonitoring/DataAcquisition.h View File

#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();
};

+ 0
- 364
ConditionMonitoring/DataModel.cpp View File

#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;
}

+ 0
- 129
ConditionMonitoring/DataModel.h View File

#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);
};

+ 0
- 37
ConditionMonitoring/Evaluator.cpp View File

#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;
}


+ 0
- 25
ConditionMonitoring/Evaluator.h View File

#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();

};


+ 0
- 17
ConditionMonitoring/MLAlert.h View File

#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

+ 0
- 42
ConditionMonitoring/MLAnalyzer.cpp View File

#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;
//}
}

+ 0
- 37
ConditionMonitoring/MLAnalyzer.h View File

#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

+ 0
- 74
ConditionMonitoring/MLLinReg.cpp View File

#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();
}
}

+ 0
- 23
ConditionMonitoring/MLLinReg.h View File

#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

+ 0
- 10
ConditionMonitoring/ModbusDataBender.cpp View File

#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);

}

+ 0
- 11
ConditionMonitoring/ModbusDataBender.h View File

#pragma once
#include "ModbusDataInterface.h"


class ModbusDataBender : public ModbusDataInterface
{
public:
ModbusDataBender(const unsigned int id);

};


+ 0
- 20
ConditionMonitoring/ModbusDataInterface.cpp View File

#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;
}

+ 0
- 34
ConditionMonitoring/ModbusDataInterface.h View File

#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();
};

+ 0
- 247
ConditionMonitoring/ModbusDataPOC.cpp View File

#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);

}



+ 0
- 20
ConditionMonitoring/ModbusDataPOC.h View File

#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);
};

+ 0
- 213
ConditionMonitoring/ModbusInterface.cpp View File

#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 &param) {
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 &param) {
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 &param) {
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 &param) {
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 &param) {
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 &param) {
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;
}

+ 0
- 54
ConditionMonitoring/ModbusInterface.h View File

#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 &param);
bool readBits(ParameterSpecification &param);
uint8_t readByte(ParameterSpecification &param);
uint16_t readRegister(ParameterSpecification &param);
uint32_t readDRegister(ParameterSpecification &param);
uint64_t readQRegister(ParameterSpecification &param);

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])

+ 0
- 283
ConditionMonitoring/ModbusRegister.h View File

#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,
};
}

+ 0
- 31
ConditionMonitoring/ModbusRtu.cpp View File

#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();
}
}

+ 0
- 26
ConditionMonitoring/ModbusRtu.h View File

#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"; }

};


+ 0
- 25
ConditionMonitoring/ModbusTcp.cpp View File

#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();
}
}

+ 0
- 20
ConditionMonitoring/ModbusTcp.h View File

#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"; }

};


+ 0
- 128
ConditionMonitoring/NetServer.cpp View File

#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;
}


+ 0
- 54
ConditionMonitoring/NetServer.h View File

#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);

};

+ 0
- 1
ConditionMonitoring/ParameterCharP.cpp View File

#include "ParameterCharP.h"

+ 0
- 15
ConditionMonitoring/ParameterCharP.h View File

#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; }
};

+ 0
- 2
ConditionMonitoring/ParameterDouble.cpp View File

#include "ParameterDouble.h"


+ 0
- 16
ConditionMonitoring/ParameterDouble.h View File

#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; }

};

+ 0
- 2
ConditionMonitoring/ParameterFloat.cpp View File

#include "ParameterFloat.h"


+ 0
- 17
ConditionMonitoring/ParameterFloat.h View File

#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; }

};


+ 0
- 58
ConditionMonitoring/ParameterInterface.h View File

#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); }
};

+ 0
- 2
ConditionMonitoring/ParameterS16.cpp View File

#include "ParameterS16.h"


+ 0
- 15
ConditionMonitoring/ParameterS16.h View File

#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; }

};

+ 0
- 2
ConditionMonitoring/ParameterUInt16.cpp View File

#include "ParameterUInt16.h"


+ 0
- 15
ConditionMonitoring/ParameterUInt16.h View File

#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; }

};

+ 0
- 2
ConditionMonitoring/ParameterUInt32.cpp View File

#include "ParameterUInt32.h"


+ 0
- 14
ConditionMonitoring/ParameterUInt32.h View File

#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; }

};

+ 0
- 20
ConditionMonitoring/PublisherBenderRcm.cpp View File

#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();
}

+ 0
- 18
ConditionMonitoring/PublisherBenderRcm.h View File

#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;
};


+ 0
- 30
ConditionMonitoring/PublisherData.cpp View File

#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;
}

+ 0
- 42
ConditionMonitoring/PublisherData.h View File

#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&);
};


+ 0
- 19
ConditionMonitoring/PublisherInterface.cpp View File

#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;
}

+ 0
- 35
ConditionMonitoring/PublisherInterface.h View File

#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;
};

+ 0
- 26
ConditionMonitoring/PublisherPowercenter.cpp View File

#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();
}

+ 0
- 16
ConditionMonitoring/PublisherPowercenter.h View File

#pragma once
#include "PublisherInterface.h"
#include "ModbusDataPOC.h"
#include <memory>

class PublisherPowercenter :
public PublisherInterface
{
private:

public:
PublisherPowercenter();
virtual ~PublisherPowercenter();

std::string getName()const override;
};

+ 0
- 7
ConditionMonitoring/PublisherType.h View File

#pragma once

enum class PublisherType{
NA = 0,
RCMS_BENDER,
POWERCENTER
};

+ 0
- 137
ConditionMonitoring/SystemConfig.cpp View File

#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 "";
}

+ 0
- 28
ConditionMonitoring/SystemConfig.h View File

#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);
};

+ 0
- 203
ConditionMonitoring/SystemControler.cpp View File

#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>();
}

+ 0
- 60
ConditionMonitoring/SystemControler.h View File

#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); }
};

+ 0
- 157
ConditionMonitoring/asioClientServer/net_client.h View File

/*
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;
};
}

+ 0
- 76
ConditionMonitoring/asioClientServer/net_common.h View File

/*
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>

+ 0
- 343
ConditionMonitoring/asioClientServer/net_connection.h View File

/*
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;

};
}

+ 0
- 161
ConditionMonitoring/asioClientServer/net_dequeue_ts.h View File

/*
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;
};
}

+ 0
- 214
ConditionMonitoring/asioClientServer/net_message.h View File

/*
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 <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;
}
};

}

+ 0
- 276
ConditionMonitoring/asioClientServer/net_server.h View File

/*
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;
};
}

+ 0
- 88
ConditionMonitoring/baConditionMonitoring.pro View File

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



+ 0
- 70
ConditionMonitoring/cappedstorage.cpp View File

#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;
}

+ 0
- 49
ConditionMonitoring/cappedstorage.h View File

#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

+ 0
- 4569
ConditionMonitoring/easylogging++.h
File diff suppressed because it is too large
View File


+ 0
- 84
ConditionMonitoring/main.cpp View File

//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;
}

+ 0
- 6
ConditionMonitoring/mltimeseries.cpp View File

#include "mltimeseries.h"

MLtimeSeries::MLtimeSeries()
{

}

+ 0
- 12
ConditionMonitoring/mltimeseries.h View File

#ifndef MLTIMESERIES_H
#define MLTIMESERIES_H
#include "MLAnalyzer.h"

template <typename T>
class MLtimeSeries : public MLAnalyzer<T>
{
public:
MLtimeSeries();
};

#endif // MLTIMESERIES_H

+ 0
- 7
ConditionMonitoring/modbus_interface_lib.h View File

#pragma once

#include "ModbusInterface.h"
#include "ModbusRtu.h"
#include "ModbusTcp.h"

#include <modbuspp/modbuspp.h>

+ 0
- 64
ConditionMonitoring/net_server_client.h View File

/*
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"

+ 0
- 90
ConditionMonitoring/ts_map.h View File

#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

+ 0
- 143
ConditionMonitoring/ts_queue.h View File

/*
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 <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…
Cancel
Save