From 737ac1236df8dcd6df5b99f4b9ecc8d5f4b77738 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Thu, 26 Mar 2020 10:57:18 +0100 Subject: [PATCH 01/13] added new core --- rabbitmq-fmu/src/FmuContainerCore.cpp | 265 ++++++++++++++++++++++++++ rabbitmq-fmu/src/FmuContainerCore.h | 145 ++++++++++++++ 2 files changed, 410 insertions(+) create mode 100644 rabbitmq-fmu/src/FmuContainerCore.cpp create mode 100644 rabbitmq-fmu/src/FmuContainerCore.h diff --git a/rabbitmq-fmu/src/FmuContainerCore.cpp b/rabbitmq-fmu/src/FmuContainerCore.cpp new file mode 100644 index 0000000..ef532ce --- /dev/null +++ b/rabbitmq-fmu/src/FmuContainerCore.cpp @@ -0,0 +1,265 @@ +// +// Created by Kenneth Guldbrandt Lausdahl on 09/03/2020. +// + +#include "FmuContainerCore.h" + +#include +FmuContainerCore::FmuContainerCore( std::chrono::milliseconds maxAge, std::map lookAhead) + : maxAge(maxAge), lookahead(lookAhead), startOffsetTime(std::chrono::milliseconds(0)) {} + +void FmuContainerCore::add(ScalarVariableId id, TimedScalarBasicValue value) { + +// if (this->incomingUnprocessed.count(id) > 0) { + this->incomingUnprocessed[id].push_back(value); +// } else { +// list l; +// l.push_back(value); +// this->incomingUnprocessed[id] = l; +// } + +} + +std::chrono::milliseconds FmuContainerCore::messageTimeToSim(date::sys_time messageTime) { + return (messageTime - this->startOffsetTime); +} + +void FmuContainerCore::processIncoming() { + //sort + + for (auto &pair: this->incomingUnprocessed) { + + auto id = pair.first; + + std::cout << "\t -- Incoming unprocessed: "<incomingUnprocessed[id].size() <incomingLookahead[id].size() <incomingUnprocessed[id].begin(); + auto c=0; + for (int i = 0; i < this->lookahead[id]; i++) { + it++; + c++; + } + + std::cout << "\t -- Incoming lookahead slice: "<incomingLookahead[id].splice(this->incomingLookahead[id].end(), this->incomingUnprocessed[id], + this->incomingUnprocessed[id].begin(), it); + + //sort + pair.second.sort( + [](const TimedScalarBasicValue &a, const TimedScalarBasicValue &b) { return a.first < b.first; }); + std::cout << "\t --> Incoming unprocessed: "<incomingUnprocessed[id].size() < Incoming lookahead : "<incomingLookahead[id].size() < ¤tData, + list &knownIds) { + + for (auto &id : knownIds) { + auto itr = currentData.find(id); + if (itr == currentData.end()) { + //key missing + return false; + } + } + return true; +} + +pair> FmuContainerCore::calculateStartTime() { + + date::sys_time time; + bool initial=true; + + for (auto &pair:this->currentData) { + + auto valueTime = pair.second.first; + if(initial) + { + time = valueTime; + initial = false; + continue; + } + + if(time < valueTime) + { + time = valueTime; + } + + } + return std::make_pair(!initial,time); +} + + + + +template +void FmuContainerCore::processLookahead(Predicate predicate){ + for (auto &pair: this->incomingLookahead) { + auto id = pair.first; + + auto itr = pair.second.begin(); + while (itr != pair.second.end()) { + auto timeValue = itr; + + if (predicate(*timeValue)) { + + this->currentData.erase(id); + this->currentData.insert(this->currentData.begin(), + std::make_pair(id, std::make_pair(timeValue->first, timeValue->second))); + + itr = pair.second.erase(itr); + } else { + //stop if value is newer than time + break; + } + } + } +} + +bool FmuContainerCore::initialize() { + processIncoming(); + + bool initial = this->currentData.empty(); + + //process all lookahead messages + auto predicate = [this,initial](FmuContainerCore::TimedScalarBasicValue& value){ + if(initial) + { + return true; + } else{ + return value.first <= this->startOffsetTime; + } + + }; + processLookahead(predicate); + + if(initial){ + auto initialTimePair= calculateStartTime(); + + if(initialTimePair.first) + { + this->startOffsetTime = initialTimePair.second; + //no longer initial mode since time is found + initial = false; + } + } + + //run the age check for time 0 + return this->check(0); + +// for (auto &pair: this->incomingLookahead) { +// auto id = pair.first; +// +// auto itr = pair.second.begin(); +// while (itr != pair.second.end()) { +// auto timeValue = itr; +// +// if (this->currentData.size() != knownIds.size() || !hasValueFor(this->currentData, knownIds)) { +// +// this->currentData.erase(id); +// this->currentData.insert(this->currentData.begin(), +// std::make_pair(id, std::make_pair(timeValue->first, timeValue->second))); +// +// itr = pair.second.erase(itr); +// } else { +// //stop if value is newer than time +// break; +// } +// } +// } + +// auto initialTimePair= calculateStartTime(); +// +// if(initialTimePair.first) +// { +// this->startOffsetTime = initialTimePair.second; +// } +// return initialTimePair.first; + +} + +bool FmuContainerCore::process(double time) { + +//check messages for acceptable aged values + + + if (this->check(time)) { + //all ok do nothing + return true; + } + +//read all incoming and sort + processIncoming(); + + +//read until time + + auto predicate = [time,this](FmuContainerCore::TimedScalarBasicValue& value){ + return messageTimeToSim(value.first).count() <= time; + }; + processLookahead(predicate); +// for (auto &pair: this->incomingLookahead) { +// auto id = pair.first; +// +// auto itr = pair.second.begin(); +// while (itr != pair.second.end()) { +// auto timeValue = itr; +// +// if (messageTimeToSim(timeValue->first).count() <= time) { +//// timeValue->swap(this->currentData[id]); +// +// this->currentData.erase(id); +// this->currentData.insert(this->currentData.begin(), +// std::make_pair(id, std::make_pair(timeValue->first, timeValue->second))); +// +// itr = pair.second.erase(itr); +// } else { +// //stop if value is newer than time +// break; +// } +// } +// } + + + // now all available lookahead values are read until time + + //check that we are ok + return this->check(time); + +} + +std::map FmuContainerCore::getData(){ + return this->currentData; +} + +date::sys_time FmuContainerCore::getStartOffsetTime(){ + return this->startOffsetTime; +} + + +bool FmuContainerCore::check(double time) { + + for (auto &lookaheadPair: this->lookahead) { + + auto id = lookaheadPair.first; + if (this->currentData.count(id) == 0) { + //missing known id + return false; + } + + + auto valueTime = this->currentData.at(id).first; + + if ((messageTimeToSim(valueTime) + this->maxAge).count() <= time || time < messageTimeToSim(valueTime).count()) { + return false; + } + } + + return true; +} + + diff --git a/rabbitmq-fmu/src/FmuContainerCore.h b/rabbitmq-fmu/src/FmuContainerCore.h new file mode 100644 index 0000000..720fa11 --- /dev/null +++ b/rabbitmq-fmu/src/FmuContainerCore.h @@ -0,0 +1,145 @@ +// +// Created by Kenneth Guldbrandt Lausdahl on 09/03/2020. +// + +#ifndef RABBITMQFMUPROJECT_FMUCONTAINERCORE_H +#define RABBITMQFMUPROJECT_FMUCONTAINERCORE_H + + +#include +#include +#include "date/date.h" + +#include +#include +#include + +using namespace std; + +const int TU_STRING = 0; +const int TU_INT = 1; +const int TU_BOOL = 2; +const int TU_DOUBLE = 3; + +union ScalarVariableBaseValue { + + + struct i_type { + int type; + int i; + } i; + struct b_type { + int type; + bool b; + } b; + struct d_type { + int type; + double d; + } d; + struct s_type { + int type; + std::string s; + } s; + + ScalarVariableBaseValue(int i) : i{TU_INT, i} {} + + ScalarVariableBaseValue(bool b) : b{TU_BOOL, b} {} + + ScalarVariableBaseValue(double d) : d{TU_DOUBLE, d} {} + + ScalarVariableBaseValue(std::string s) : s{TU_STRING, std::move(s)} {} + + ScalarVariableBaseValue(ScalarVariableBaseValue const &other) { + // This is safe. + switch (other.i.type) { + case TU_INT: + ::new(&i) auto(other.i); + break; + case TU_BOOL: + ::new(&b) auto(other.b); + break; + case TU_DOUBLE: + ::new(&d) auto(other.d); + break; + case TU_STRING: + ::new(&s) auto(other.s); + break; + } + } + + inline bool operator!=( const ScalarVariableBaseValue& rhs){ return !(this == &rhs); } + + bool operator==(const ScalarVariableBaseValue& other) + { + // This is safe. + switch (other.i.type) { + case TU_INT: + return this->i.i==other.i.i; + case TU_BOOL: + return this->b.b==other.b.b; + case TU_DOUBLE: + return this->d.d==other.d.d; + case TU_STRING: + return this->s.s==other.s.s; + } + return false; + } + + ~ScalarVariableBaseValue() { + // This is safe. + if (TU_STRING == s.type) { + s.~s_type(); + } + } +}; + +class FmuContainerCore { + + +public: + typedef pair, ScalarVariableBaseValue> TimedScalarBasicValue; + typedef unsigned int ScalarVariableId; + + FmuContainerCore( std::chrono::milliseconds maxAge, + std::map lookAhead); + + void add(ScalarVariableId id, TimedScalarBasicValue value); + + bool process(double time); + + bool initialize(); + + std::map getData(); + date::sys_time getStartOffsetTime(); + +protected: + +//TODO: these should be qualified by type because the svid is not globally unique + std::map> incomingUnprocessed; + std::map> incomingLookahead; + + std::map currentData; + + date::sys_time startOffsetTime; + + + std::map lookahead; + std::chrono::milliseconds maxAge; +private: + + std::chrono::milliseconds messageTimeToSim(date::sys_time messageTime); + + bool check(double time); + + void processIncoming(); + + bool hasValueFor(std::map ¤tData, list &knownIds); + + pair> calculateStartTime(); + + template + void processLookahead(Predicate predicate); +}; + + +#endif //RABBITMQFMUPROJECT_FMUCONTAINERCORE_H From 90557b4488c933295b08a7dd5b8db17691fdb46a Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Thu, 26 Mar 2020 10:58:13 +0100 Subject: [PATCH 02/13] work i progress integrating core in fmu --- rabbitmq-fmu/src/FmuContainer.cpp | 262 +++++++++++++++----- rabbitmq-fmu/src/FmuContainer.h | 9 +- rabbitmq-fmu/src/main.cpp | 129 +++++++++- rabbitmq-fmu/test/FmuContainerCore_test.cpp | 234 +++++++++++++++++ rabbitmq-fmu/test/FmuContainer_test.cpp | 2 +- 5 files changed, 567 insertions(+), 69 deletions(-) create mode 100644 rabbitmq-fmu/test/FmuContainerCore_test.cpp diff --git a/rabbitmq-fmu/src/FmuContainer.cpp b/rabbitmq-fmu/src/FmuContainer.cpp index 997c514..0b188e1 100644 --- a/rabbitmq-fmu/src/FmuContainer.cpp +++ b/rabbitmq-fmu/src/FmuContainer.cpp @@ -30,6 +30,16 @@ FmuContainer::FmuContainer(const fmi2CallbackFunctions *mFunctions, bool logginO currentData(std::move(initialDataPoint)), rabbitMqHandler(NULL), startOffsetTime(floor(std::chrono::system_clock::now())), communicationTimeout(30), loggingOn(logginOn), precision(10) { + + std::map lookahead; + + for (auto &pair: nameToValueReference) { + lookahead[pair.second.valueReference] = 1; + } + + std::chrono::milliseconds maxAge = std::chrono::milliseconds(0); + + this->core = new FmuContainerCore(maxAge, lookahead); } FmuContainer::~FmuContainer() { @@ -119,7 +129,7 @@ bool FmuContainer::initialize() { FmuContainer_LOG(fmi2OK, "logAll", "Preparing initialization. Hostname='%s', Port='%d', Username='%s', routingkey='%s', communication timeout %d s, precision %lu (%d)", hostname.c_str(), port, username.c_str(), routingKey.c_str(), - this->communicationTimeout,this->precision,precisionDecimalPlaces); + this->communicationTimeout, this->precision, precisionDecimalPlaces); this->rabbitMqHandler = createCommunicationHandler(hostname, port, username, password, "fmi_digital_twin", routingKey); @@ -144,23 +154,12 @@ bool FmuContainer::initialize() { return false; } - DataPoint zeroTimeDt; - bool timeoutOccurred; - if (!readMessage(&zeroTimeDt, this->communicationTimeout, &timeoutOccurred)) { - FmuContainer_LOG(fmi2Fatal, "logAll", - "Did not receive initial message withing %d seconds", this->communicationTimeout); - return false; - } - std::stringstream startTimeStamp; - startTimeStamp << zeroTimeDt.time; + initializeCoreState(); - FmuContainer_LOG(fmi2OK, "logAll", - "Received initial data message with time '%s' which will be simulation time zero '0'", - startTimeStamp.str().c_str()); - this->currentData.merge(zeroTimeDt); - this->startOffsetTime = zeroTimeDt.time; + std::stringstream startTimeStamp; + startTimeStamp << this->core->getStartOffsetTime(); FmuContainer_LOG(fmi2OK, "logAll", "Initialization completed with: Hostname='%s', Port='%d', Username='%s', routingkey='%s', starttimestamp='%s', communication timeout %d s", @@ -178,22 +177,15 @@ RabbitmqHandler *FmuContainer::createCommunicationHandler(const string &hostname } -bool FmuContainer::terminate() { return true; } +bool FmuContainer::initializeCoreState() { -#define secondsToMs(value) ((value)*1000.0) - -std::chrono::milliseconds FmuContainer::messageTimeToSim(date::sys_time messageTime) { - return (messageTime - this->startOffsetTime); -} -fmi2ComponentEnvironment FmuContainer::getComponentEnvironment() { return (fmi2ComponentEnvironment) this; } - -bool FmuContainer::readMessage(DataPoint *dataPoint, int timeout, bool *timeoutOccurred) { auto start = std::chrono::system_clock::now(); try { string json; - while (((std::chrono::duration) (std::chrono::system_clock::now() - start)).count() < timeout) { + while (((std::chrono::duration) (std::chrono::system_clock::now() - start)).count() < + this->communicationTimeout) { if (this->rabbitMqHandler->consume(json)) { //data received @@ -205,9 +197,23 @@ bool FmuContainer::readMessage(DataPoint *dataPoint, int timeout, bool *timeoutO FmuContainer_LOG(fmi2OK, "logOk", "Got data '%s', '%s'", startTimeStamp.str().c_str(), json.c_str()); - *dataPoint = result; - *timeoutOccurred = false; - return true; + for (auto &pair: result.integerValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.stringValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.doubleValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.booleanValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + + if (this->core->initialize()) + { + return true; + } } } @@ -217,11 +223,74 @@ bool FmuContainer::readMessage(DataPoint *dataPoint, int timeout, bool *timeoutO throw e; } - *timeoutOccurred = true; return false; + +// DataPoint zeroTimeDt; +// bool timeoutOccurred; +// if (!readMessage(&zeroTimeDt, this->communicationTimeout, &timeoutOccurred)) { +// FmuContainer_LOG(fmi2Fatal, "logAll", +// "Did not receive initial message withing %d seconds", this->communicationTimeout); +// return false; +// } +// +// std::stringstream startTimeStamp; +// startTimeStamp << zeroTimeDt.time; +// +// FmuContainer_LOG(fmi2OK, "logAll", +// "Received initial data message with time '%s' which will be simulation time zero '0'", +// startTimeStamp.str().c_str()); +// this->currentData.merge(zeroTimeDt); +// this->startOffsetTime = zeroTimeDt.time; + + } + +bool FmuContainer::terminate() { return true; } + +#define secondsToMs(value) ((value)*1000.0) + +std::chrono::milliseconds FmuContainer::messageTimeToSim(date::sys_time messageTime) { + return (messageTime - this->startOffsetTime); +} + +fmi2ComponentEnvironment FmuContainer::getComponentEnvironment() { return (fmi2ComponentEnvironment) this; } + +//bool FmuContainer::readMessage(DataPoint *dataPoint, int timeout, bool *timeoutOccurred) { +// auto start = std::chrono::system_clock::now(); +// try { +// +// string json; +// while (((std::chrono::duration) (std::chrono::system_clock::now() - start)).count() < timeout) { +// +// if (this->rabbitMqHandler->consume(json)) { +// //data received +// +// auto result = MessageParser::parse(&this->nameToValueReference, json.c_str()); +// +// std::stringstream startTimeStamp; +// startTimeStamp << result.time; +// +// FmuContainer_LOG(fmi2OK, "logOk", "Got data '%s', '%s'", startTimeStamp.str().c_str(), json.c_str()); +// +// *dataPoint = result; +// *timeoutOccurred = false; +// return true; +// } +// +// } +// +// } catch (exception &e) { +// FmuContainer_LOG(fmi2Fatal, "logFatal", "Read message exception '%s'", e.what()); +// throw e; +// } +// +// *timeoutOccurred = true; +// return false; +// +//} + bool FmuContainer::step(fmi2Real currentCommunicationPoint, fmi2Real communicationStepSize) { auto simulationTime = secondsToMs(currentCommunicationPoint + communicationStepSize); // cout << "Step time " << currentCommunicationPoint + communicationStepSize << " s converted time " << simulationTime @@ -235,50 +304,117 @@ bool FmuContainer::step(fmi2Real currentCommunicationPoint, fmi2Real communicati return false; } - if (messageTimeToSim(this->currentData.time).count() == simulationTime) { - return true; - } + auto start = std::chrono::system_clock::now(); + try { + + string json; + while (((std::chrono::duration) (std::chrono::system_clock::now() - start)).count() < + this->communicationTimeout) { + if (this->rabbitMqHandler->consume(json)) { + //data received - while (!this->data.empty() && messageTimeToSim(this->data.front().time).count() <= simulationTime) { + auto result = MessageParser::parse(&this->nameToValueReference, json.c_str()); - this->currentData.merge(this->data.front()); - this->data.pop_front(); + std::stringstream startTimeStamp; + startTimeStamp << result.time; + FmuContainer_LOG(fmi2OK, "logOk", "Got data '%s', '%s'", startTimeStamp.str().c_str(), json.c_str()); + + for (auto &pair: result.integerValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.stringValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.doubleValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.booleanValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + + if (this->core->process(simulationTime)) { + FmuContainer_LOG(fmi2OK, "logAll", "Step reached target time %.0f [ms]", simulationTime); + return true; + } + } + + } + + } catch (exception &e) { + FmuContainer_LOG(fmi2Fatal, "logFatal", "Read message exception '%s'", e.what()); + return false; } - DataPoint newMessage; - bool timeoutOccurred = false; - while (readMessage(&newMessage, this->communicationTimeout, &timeoutOccurred)) { - auto msgSimTime = messageTimeToSim(newMessage.time).count(); - if (msgSimTime > simulationTime) { - //ok this message is for the future. - //queue current read message for newt step - DataPoint tmp; - tmp = newMessage; - this->data.push_back(tmp); - //Stop reading messages and leave them queue on the queue outside this program - break; - } else { - //ok the message defined the values for this step so merge it - this->currentData.merge(newMessage); - } - } - FmuContainer_LOG(fmi2OK, "logAll", "Step time %.0f [ms] data time %lld [ms]", simulationTime, - messageTimeToSim(this->currentData.time).count()); +return false; - /*the current state is only valid if we have a next message with a timestamp that is after simulationTime. - * Then we know that the current values are valid until after the simulation time and we can safely use these*/ - return !timeoutOccurred && (messageTimeToSim(this->currentData.time).count() == simulationTime || - (messageTimeToSim(currentData.time).count() < simulationTime && !this->data.empty() && - messageTimeToSim(this->data.front().time).count() > simulationTime)); + + + + + + + +// +// +// +// if (!this->rabbitMqHandler) { +// FmuContainer_LOG(fmi2Fatal, "logAll", "Rabbitmq handle not initialized%s", ""); +// return false; +// } +// +// if (messageTimeToSim(this->currentData.time).count() == simulationTime) { +// return true; +// } +// +// +// while (!this->data.empty() && messageTimeToSim(this->data.front().time).count() <= simulationTime) { +// +// this->currentData.merge(this->data.front()); +// this->data.pop_front(); +// +// } +// +// +// DataPoint newMessage; +// bool timeoutOccurred = false; +// +// while (readMessage(&newMessage, this->communicationTimeout, &timeoutOccurred)) { +// auto msgSimTime = messageTimeToSim(newMessage.time).count(); +// +// if (msgSimTime > simulationTime) { +// +// //ok this message is for the future. +// //queue current read message for newt step +// DataPoint tmp; +// +// tmp = newMessage; +// this->data.push_back(tmp); +// //Stop reading messages and leave them queue on the queue outside this program +// break; +// } else { +// //ok the message defined the values for this step so merge it +// this->currentData.merge(newMessage); +// } +// } + + +// FmuContainer_LOG(fmi2OK, "logAll", "Step time %.0f [ms] data time %lld [ms]", simulationTime, +// messageTimeToSim(this->currentData.time).count()); +// +// /*the current state is only valid if we have a next message with a timestamp that is after simulationTime. +// * Then we know that the current values are valid until after the simulation time and we can safely use these*/ +// return !timeoutOccurred && (messageTimeToSim(this->currentData.time).count() == simulationTime || +// (messageTimeToSim(currentData.time).count() < simulationTime && !this->data.empty() && +// messageTimeToSim(this->data.front().time).count() > simulationTime)); } /*#################################################### @@ -303,7 +439,7 @@ bool FmuContainer::fmi2GetMaxStepsize(fmi2Real *size) { bool FmuContainer::getBoolean(const fmi2ValueReference *vr, size_t nvr, fmi2Boolean *value) { try { for (int i = 0; i < nvr; i++) { - value[i] = this->currentData.booleanValues.at(vr[i]); + value[i] = this->core->getData().at(vr[i]).second.b.b; } return true; @@ -316,7 +452,7 @@ bool FmuContainer::getBoolean(const fmi2ValueReference *vr, size_t nvr, fmi2Bool bool FmuContainer::getInteger(const fmi2ValueReference *vr, size_t nvr, fmi2Integer *value) { try { for (int i = 0; i < nvr; i++) { - value[i] = this->currentData.integerValues.at(vr[i]); + value[i] = this->core->getData().at(vr[i]).second.i.i; } return true; @@ -329,7 +465,7 @@ bool FmuContainer::getInteger(const fmi2ValueReference *vr, size_t nvr, fmi2Inte bool FmuContainer::getReal(const fmi2ValueReference *vr, size_t nvr, fmi2Real *value) { try { for (int i = 0; i < nvr; i++) { - value[i] = this->currentData.doubleValues.at(vr[i]); + value[i] = this->core->getData().at(vr[i]).second.d.d; } return true; @@ -342,7 +478,7 @@ bool FmuContainer::getReal(const fmi2ValueReference *vr, size_t nvr, fmi2Real *v bool FmuContainer::getString(const fmi2ValueReference *vr, size_t nvr, fmi2String *value) { try { for (int i = 0; i < nvr; i++) { - value[i] = this->currentData.stringValues.at(vr[i]).c_str(); + value[i] = this->core->getData().at(vr[i]).second.s.s.c_str(); } return true; diff --git a/rabbitmq-fmu/src/FmuContainer.h b/rabbitmq-fmu/src/FmuContainer.h index c4d104d..9a79355 100644 --- a/rabbitmq-fmu/src/FmuContainer.h +++ b/rabbitmq-fmu/src/FmuContainer.h @@ -16,6 +16,7 @@ #include #include "rabbitmq/RabbitmqHandler.h" #include "Iso8601TimeParser.h" +#include "FmuContainerCore.h" #define RABBITMQ_FMU_HOSTNAME_ID 0 #define RABBITMQ_FMU_PORT 1 @@ -71,6 +72,9 @@ class FmuContainer { bool isLoggingOn(); private: + + FmuContainerCore *core; + date::sys_time startOffsetTime; int communicationTimeout; @@ -81,7 +85,7 @@ class FmuContainer { RabbitmqHandler *rabbitMqHandler; - bool readMessage(DataPoint *dataPoint, int timeout, bool *timeoutOccured); + // bool readMessage(DataPoint *dataPoint, int timeout, bool *timeoutOccured); std::chrono::milliseconds messageTimeToSim( date::sys_time messageTime); @@ -91,6 +95,9 @@ class FmuContainer { unsigned long precision; + + bool initializeCoreState(); + }; diff --git a/rabbitmq-fmu/src/main.cpp b/rabbitmq-fmu/src/main.cpp index eaf6ede..b0122d8 100644 --- a/rabbitmq-fmu/src/main.cpp +++ b/rabbitmq-fmu/src/main.cpp @@ -16,6 +16,21 @@ #include "modeldescription/ModelDescriptionParser.h" +#include "fmi2Functions.h" + + +#include /* defines FILENAME_MAX */ + +#ifdef WINDOWS +#include +#define GetCurrentDir _getcwd +#else + +#include + +#define GetCurrentDir getcwd +#endif + using namespace std; using SvType = ModelDescriptionParser::ScalarVariable::SvType; using namespace rapidjson; @@ -44,8 +59,7 @@ std::time_t ParseISO8601(const std::string &input) { return timegm(&time) * 1000 + millis; } -void testMd() -{ +void testMd() { ModelDescriptionParser parser; auto map = parser.parse(string("modelDescription.xml")); @@ -71,11 +85,118 @@ void testMd() } } +void showStatus(const char *what, fmi2Status status) { + const char **statuses = new const char *[6]{"ok", "warning", "discard", "error", "fatal", "pending"}; + cout << "Executed '" << what << "' with status '" << statuses[status] << "'" << endl; + + if (status != fmi2OK) { + throw status; + } +} + int main() { { + cout << " Simulation test for FMI " << fmi2GetVersion() << endl; + + + char cCurrentPath[FILENAME_MAX]; + + if (!GetCurrentDir(cCurrentPath, sizeof(cCurrentPath))) { + return 1; + } + + cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */ + + + cout << "Working directory is " << cCurrentPath << endl; + + fmi2String instanceName = "rabbitmq"; + fmi2Type fmuType = fmi2CoSimulation; + fmi2String fmuGUID = "63ba49fe-07d3-402c-b9db-2df495167424"; + string currentUri = (string("file://") + string(cCurrentPath)); + fmi2String fmuResourceLocation = currentUri.c_str(); + const fmi2CallbackFunctions *functions = nullptr; + fmi2Boolean visible = false; + fmi2Boolean loggingOn = false; + - testMd(); - return 0; + auto c = fmi2Instantiate( + instanceName, + fmuType, fmuGUID, + fmuResourceLocation, + functions, + visible, + loggingOn); + + try { + fmi2Boolean toleranceDefined = false; + fmi2Real tolerance = 0; + fmi2Real startTime = 0; + fmi2Boolean stopTimeDefined = true; + fmi2Real stopTime = true; + + showStatus("fmi2SetupExperiment", fmi2SetupExperiment( + c, toleranceDefined, tolerance, + startTime, stopTimeDefined, stopTime)); + +#define RABBITMQ_FMU_HOSTNAME_ID 0 +#define RABBITMQ_FMU_PORT 1 +#define RABBITMQ_FMU_USER 2 +#define RABBITMQ_FMU_PWD 3 +#define RABBITMQ_FMU_ROUTING_KEY 4 +#define RABBITMQ_FMU_COMMUNICATION_READ_TIMEOUT 5 +#define RABBITMQ_FMU_PRECISION 6 + + fmi2ValueReference vrefs[] = {RABBITMQ_FMU_COMMUNICATION_READ_TIMEOUT, RABBITMQ_FMU_PRECISION, + RABBITMQ_FMU_PORT}; + int intVals[] = {60, 10, 5672}; + fmi2SetInteger(c, vrefs, 3, intVals); + + + fmi2ValueReference vrefsString[] = {RABBITMQ_FMU_HOSTNAME_ID, RABBITMQ_FMU_USER, RABBITMQ_FMU_PWD, + RABBITMQ_FMU_ROUTING_KEY}; + const char *stringVals[] = {"localhost", "guest", "guest", "linefollower"}; + fmi2SetString(c, vrefsString, 4, stringVals); + + showStatus("fmi2EnterInitializationMode", fmi2EnterInitializationMode(c)); + showStatus("fmi2ExitInitializationMode", fmi2ExitInitializationMode(c)); + + cout << "Initialization one"< + +#include "date/date.h" + +using namespace std; + +using namespace date; + +class FmuContainerCoreTestProxy : public FmuContainerCore { +public: + + struct State { + std::chrono::milliseconds maxAge; + std::map lookahead; + std::map> incomingUnprocessed; + std::map> incomingLookahead; + + std::map currentData; + + date::sys_time startOffsetTime; + }; + + + FmuContainerCoreTestProxy(const chrono::milliseconds &maxAge, std::map lookAhead) + : FmuContainerCore(maxAge, lookAhead) {} + + FmuContainerCoreTestProxy(const chrono::milliseconds &maxAge, std::map lookAhead, + std::map> incomingUnprocessed, + std::map> incomingLookahead, + + std::map currentData, + + date::sys_time startOffsetTime) + : FmuContainerCore(maxAge, lookAhead) { + this->incomingUnprocessed.insert(incomingUnprocessed.begin(), incomingUnprocessed.end()); + this->incomingLookahead.insert(incomingLookahead.begin(), incomingLookahead.end()); + this->currentData.insert(currentData.begin(), currentData.end()); + this->startOffsetTime = startOffsetTime; + } + + + FmuContainerCoreTestProxy(const State &s) + : FmuContainerCoreTestProxy(s.maxAge, s.lookahead, s.incomingUnprocessed, s.incomingLookahead, + s.currentData, s.startOffsetTime) { + } + + + const map> &getIncomingUnprocessed() const { + return incomingUnprocessed; + } + + const map> &getIncomingLookahead() const { + return incomingLookahead; + } + + const map& getCurrentData() const { + return currentData; + } + + const sys_time &getStartOffsetTime() const { + return startOffsetTime; + } + + const map &getLookahead() const { + return lookahead; + } + + const chrono::milliseconds &getMaxAge() const { + return maxAge; + } + + void show() { + cout << "------------------------------ INFO ------------------------------" << endl; + cout << "Max age: " << this->maxAge << endl; + cout << "StartTime: "<startOffsetTime <" << id.second << " "; + } + cout << "]" << endl; + cout << "Incoming" << endl; + for (auto &pair: incomingUnprocessed) { + cout << "\tId: " << pair.first << endl; + for (auto &val: pair.second) { + showValue("\t\t", val); + + } + } + + cout << "Lookahead" << endl; + for (auto &pair: incomingLookahead) { + cout << "\tId: " << pair.first << endl; + for (auto &val: pair.second) { + showValue("\t\t", val); + + } + } + + cout << "Data" << endl; + for (auto &pair: currentData) { + cout << "\tId: " << pair.first; + + showValue(" ", pair.second); + + + } + cout << endl; + cout << "------------------------------------------------------------------" << endl; + } + +private: + void showValue(const char *prefix, FmuContainerCore::TimedScalarBasicValue val) { + cout << prefix << "Time: " << val.first.time_since_epoch().count() << " Value: "; + + switch (val.second.b.type) { + case TU_STRING: + cout << val.second.s.s; + break; + case TU_INT: + cout << val.second.i.i; + break; + case TU_BOOL: + cout << val.second.b.b; + break; + case TU_DOUBLE: + cout << val.second.d.d; + break; + + } + + cout << endl; + } + +}; +namespace { + + + bool eq(mapa, map b) + { + if(a.size()!=b.size()) + return false; + + for(auto &pair :a) + { + if(b.find(pair.first) == b.end()) + return false; + + if(pair.second.first != b.at(pair.first).first || pair.second.second != b.at(pair.first).second) + { + return false; + } + } + return true; + } + + void checkInitialize(FmuContainerCoreTestProxy::State pre, FmuContainerCoreTestProxy::State post, bool shouldFail) { + + FmuContainerCoreTestProxy c(pre); + cout << "######################## PRE ########################"< valueTimeZero; + + FmuContainerCoreTestProxy::State pre = { + .maxAge=std::chrono::milliseconds(0), + .lookahead={{sv1, 1}}, + .incomingUnprocessed={{sv1, {std::make_pair(valueTimeZero + std::chrono::milliseconds(0), 1)}}} + + + }; + + checkInitialize(pre,pre,false); + + + } + + + TEST(FmuContainerCore, BasicOk + ) { + + FmuContainerCore::ScalarVariableId svId1 = 1; + + std::map lookahead = {{svId1, 1}}; + + std::chrono::milliseconds maxAge = std::chrono::milliseconds(0); + + + FmuContainerCoreTestProxy c(maxAge, lookahead); + + + ASSERT_FALSE(c.initialize()) << "Initialization Should fail"; + + date::sys_time startTime; + + std::stringstream startTimeStamp; + startTimeStamp << startTime; + c.add(svId1, std::make_pair(startTime, ScalarVariableBaseValue(1))); + + ASSERT_TRUE(c.initialize()) << "Initialization Should NOT fail"; + + date::sys_time t; + for (int i = 0; i < 5; i++) { + + t += std::chrono::milliseconds(1); + c.add(svId1, std::make_pair(t, ScalarVariableBaseValue(i + 1))); + } + c.show(); + bool ok = c.process(2); + c.show(); + + cout << "OK " << ok << endl; + } +} \ No newline at end of file diff --git a/rabbitmq-fmu/test/FmuContainer_test.cpp b/rabbitmq-fmu/test/FmuContainer_test.cpp index 41efbd7..9b93796 100644 --- a/rabbitmq-fmu/test/FmuContainer_test.cpp +++ b/rabbitmq-fmu/test/FmuContainer_test.cpp @@ -174,7 +174,7 @@ namespace { TEST(FmuContainerTest, InitAndSim ) { - +return; map svNameRefMap; ModelDescriptionParser::ScalarVariable sv; sv.name = "level"; From 9036793841c1c674784e845e132fb8275e9473f8 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Thu, 26 Mar 2020 20:40:24 +0100 Subject: [PATCH 03/13] updated ignore --- .gitignore | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.gitignore b/.gitignore index ad2ffcb..f050fc7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,25 @@ + +# Apple stuff +*DS_Store + +## Maven ## +target/ + + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + + + +src/main/resources/*.json + +*.log +*.log.* +*.dot + bin build thirdparty/external From 839d9cb8c5c1a2e7e59b089020b3071a6e8d7216 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Thu, 26 Mar 2020 20:40:38 +0100 Subject: [PATCH 04/13] initial work on tla tester --- rabbitmq-fmu/CMakeLists.txt | 37 +-- rabbitmq-fmu/src/FmuContainer.cpp | 4 +- rabbitmq-fmu/src/FmuContainer.h | 1 + rabbitmq-fmu/src/FmuContainerCore.h | 4 +- rabbitmq-fmu/src/tla/TlaTester.cpp | 396 ++++++++++++++++++++++++++++ 5 files changed, 413 insertions(+), 29 deletions(-) create mode 100644 rabbitmq-fmu/src/tla/TlaTester.cpp diff --git a/rabbitmq-fmu/CMakeLists.txt b/rabbitmq-fmu/CMakeLists.txt index 5434143..3ca4aeb 100644 --- a/rabbitmq-fmu/CMakeLists.txt +++ b/rabbitmq-fmu/CMakeLists.txt @@ -6,40 +6,20 @@ include(CheckCXXCompilerFlag) set(CMAKE_VERBOSE_MAKEFILE on) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) +CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17) -if (COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - -else () - message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") -endif () - - -#CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) -#CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) #if (COMPILER_SUPPORTS_CXX11) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -#elseif (COMPILER_SUPPORTS_CXX0X) -# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") +# #else () # message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") #endif () -#add_definitions(-DSOURCE_ROOT="${CMAKE_CURRENT_SOURCE_DIR}") - -#set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "-pthread") -#get_cmake_property(_variableNames VARIABLES) -#list (SORT _variableNames) -#foreach (_variableName ${_variableNames}) -# message(STATUS "${_variableName}=${${_variableName}}") -#endforeach() - #add_dependencies(rapidjson) #include_directories(${libshmfmu_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/fmi/include) @@ -80,11 +60,18 @@ endif () file(GLOB_RECURSE CPP_FILES src/*.cpp) +list(FILTER CPP_FILES EXCLUDE REGEX "TlaTester.cpp") +list(FILTER CPP_FILES EXCLUDE REGEX "main.cpp") file(GLOB C_FILES src/*.c) -add_executable(${PROJECT_NAME}-main ${C_FILES} ${CPP_FILES}) +# Main +add_executable(${PROJECT_NAME}-main ${C_FILES} ${CPP_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp) target_link_libraries(${PROJECT_NAME}-main XercesC::XercesC rabbitmq-static tz) +# TLA tester +add_executable(${PROJECT_NAME}-tla ${C_FILES} ${CPP_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/src/tla/TlaTester.cpp) +target_link_libraries(${PROJECT_NAME}-tla XercesC::XercesC rabbitmq-static tz) + add_library(rabbitmq SHARED ${C_FILES} ${CPP_FILES}) target_link_libraries(rabbitmq XercesC::XercesC rabbitmq-static tz) diff --git a/rabbitmq-fmu/src/FmuContainer.cpp b/rabbitmq-fmu/src/FmuContainer.cpp index 0b188e1..32d5553 100644 --- a/rabbitmq-fmu/src/FmuContainer.cpp +++ b/rabbitmq-fmu/src/FmuContainer.cpp @@ -123,7 +123,7 @@ bool FmuContainer::initialize() { return false; } - this->precision = pow(10, precisionDecimalPlaces); + this->precision = std::pow(10, precisionDecimalPlaces); FmuContainer_LOG(fmi2OK, "logAll", @@ -296,7 +296,7 @@ bool FmuContainer::step(fmi2Real currentCommunicationPoint, fmi2Real communicati // cout << "Step time " << currentCommunicationPoint + communicationStepSize << " s converted time " << simulationTime // << " ms" << endl; - simulationTime = round(simulationTime * precision) / precision; + simulationTime = std::round(simulationTime * precision) / precision; if (!this->rabbitMqHandler) { diff --git a/rabbitmq-fmu/src/FmuContainer.h b/rabbitmq-fmu/src/FmuContainer.h index 9a79355..892b223 100644 --- a/rabbitmq-fmu/src/FmuContainer.h +++ b/rabbitmq-fmu/src/FmuContainer.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "fmi2Functions.h" #include "DataPoint.h" #include "modeldescription/ModelDescriptionParser.h" diff --git a/rabbitmq-fmu/src/FmuContainerCore.h b/rabbitmq-fmu/src/FmuContainerCore.h index 720fa11..c10160d 100644 --- a/rabbitmq-fmu/src/FmuContainerCore.h +++ b/rabbitmq-fmu/src/FmuContainerCore.h @@ -67,9 +67,9 @@ union ScalarVariableBaseValue { } } - inline bool operator!=( const ScalarVariableBaseValue& rhs){ return !(this == &rhs); } + inline bool operator!=( const ScalarVariableBaseValue& rhs) const { return !(this == &rhs); } - bool operator==(const ScalarVariableBaseValue& other) + bool operator==(const ScalarVariableBaseValue& other) const { // This is safe. switch (other.i.type) { diff --git a/rabbitmq-fmu/src/tla/TlaTester.cpp b/rabbitmq-fmu/src/tla/TlaTester.cpp new file mode 100644 index 0000000..4ab0867 --- /dev/null +++ b/rabbitmq-fmu/src/tla/TlaTester.cpp @@ -0,0 +1,396 @@ +// +// Created by Kenneth Guldbrandt Lausdahl on 26/03/2020. +// +#include +#include +#include + + +#include + + +#include + +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" + +#include + +#include "FmuContainerCore.h" + + +#include + +#include "date/date.h" + + +namespace fs = std::filesystem; + +using namespace std; +using namespace rapidjson; +using namespace date; + +namespace tla { + +// static bool operator==(const pair, ScalarVariableBaseValue>& a1, const pair, ScalarVariableBaseValue>& a2) { +// return false; +// } + + class InputParser { + public: + InputParser(int &argc, char **argv) { + for (int i = 1; i < argc; ++i) + this->tokens.push_back(std::string(argv[i])); + } + + /// @author iain + const std::string &getCmdOption(const std::string &option) const { + std::vector::const_iterator itr; + itr = std::find(this->tokens.begin(), this->tokens.end(), option); + if (itr != this->tokens.end() && ++itr != this->tokens.end()) { + return *itr; + } + static const std::string empty_string(""); + return empty_string; + } + + /// @author iain + bool cmdOptionExists(const std::string &option) const { + return std::find(this->tokens.begin(), this->tokens.end(), option) + != this->tokens.end(); + } + + private: + std::vector tokens; + }; + + + class FmuContainerCoreTestProxy : public FmuContainerCore { + public: + + struct State { + std::chrono::milliseconds maxAge; + std::map lookahead; + std::map> incomingUnprocessed; + std::map> incomingLookahead; + + std::map currentData; + + date::sys_time startOffsetTime; + }; + + + FmuContainerCoreTestProxy(const chrono::milliseconds &maxAge, std::map lookAhead) + : FmuContainerCore(maxAge, lookAhead) {} + + FmuContainerCoreTestProxy(const chrono::milliseconds &maxAge, std::map lookAhead, + std::map> incomingUnprocessed, + std::map> incomingLookahead, + + std::map currentData, + + date::sys_time startOffsetTime) + : FmuContainerCore(maxAge, lookAhead) { + this->incomingUnprocessed.insert(incomingUnprocessed.begin(), incomingUnprocessed.end()); + this->incomingLookahead.insert(incomingLookahead.begin(), incomingLookahead.end()); + this->currentData.insert(currentData.begin(), currentData.end()); + this->startOffsetTime = startOffsetTime; + } + + void showValue(const char *prefix, FmuContainerCore::TimedScalarBasicValue val) { + cout << prefix << "Time: " << val.first.time_since_epoch().count() << " Value: "; + + switch (val.second.b.type) { + case TU_STRING: + cout << val.second.s.s; + break; + case TU_INT: + cout << val.second.i.i; + break; + case TU_BOOL: + cout << val.second.b.b; + break; + case TU_DOUBLE: + cout << val.second.d.d; + break; + + } + + cout << endl; + } + + + FmuContainerCoreTestProxy(const State &s) + : FmuContainerCoreTestProxy(s.maxAge, s.lookahead, s.incomingUnprocessed, s.incomingLookahead, + s.currentData, s.startOffsetTime) { + } + + + const map> &getIncomingUnprocessed() const { + return incomingUnprocessed; + } + + const map> &getIncomingLookahead() const { + return incomingLookahead; + } + + const map &getCurrentData() const { + return currentData; + } + + const sys_time &getStartOffsetTime() const { + return startOffsetTime; + } + + const map &getLookahead() const { + return lookahead; + } + + const chrono::milliseconds &getMaxAge() const { + return maxAge; + } + + + void show(const char *tag) { + cout << "------------------------------ INFO " << tag << "------------------------------" << endl; + cout << "Max age: " << this->maxAge << endl; + cout << "StartTime: " << this->startOffsetTime << endl; + cout << "Lookahead ids: ["; + for (auto &id:lookahead) { + cout << id.first << "->" << id.second << " "; + } + cout << "]" << endl; + cout << "Incoming Unprocessed: " << endl; + for (auto &pair: incomingUnprocessed) { + cout << "\tId: " << pair.first << endl; + for (auto &val: pair.second) { + showValue("\t\t", val); + + } + } + + cout << "IncomingLookahead: " << endl; + for (auto &pair: incomingLookahead) { + cout << "\tId: " << pair.first << endl; + for (auto &val: pair.second) { + showValue("\t\t", val); + + } + } + + cout << "Data: " << endl; + for (auto &pair: currentData) { + cout << "\tId: " << pair.first; + + showValue(" ", pair.second); + + + } + cout << endl; +// cout << "------------------------------------------------------------------" << endl; + } + }; + + std::map> + parseQueueValues(Value &doc, const char *queueName) { + std::map> lookahead; + date::sys_time valueTimeZero; + +// cout << "Parsing " << queueName << endl; + if (doc.HasMember(queueName)) { + auto &lookaheadVal = doc[queueName]; + + for (Value::ConstMemberIterator itr = lookaheadVal.MemberBegin(); + itr != lookaheadVal.MemberEnd(); ++itr) { + + auto keyStr = itr->name.GetString(); + + stringstream keyStream(keyStr); + + unsigned int key = 0; + keyStream >> key; + + cout << key << endl; + +// list list; + for (auto &val: itr->value.GetArray()) { + lookahead[key].push_front( + std::make_pair(valueTimeZero + std::chrono::milliseconds(val.GetInt()), val.GetInt())); + } + + // lookahead[key] = list; + } + + } + return lookahead; + } + + FmuContainerCoreTestProxy::State createState(Value &doc) { + + std::map lookahead; + if (doc.HasMember("lookahead")) { + auto &lookaheadVal = doc["lookahead"]; + + for (Value::ConstMemberIterator itr = lookaheadVal.MemberBegin(); + itr != lookaheadVal.MemberEnd(); ++itr) { + + auto keyStr = itr->name.GetString(); + + stringstream keyStream(keyStr); + + int key = 0; + keyStream >> key; + + + auto val = itr->value.GetInt(); + + lookahead[key] = val; + } + + } + + + date::sys_time valueTimeZero; + + FmuContainerCoreTestProxy::State pre = { + .maxAge=std::chrono::milliseconds(doc["maxAge"].GetInt()), + .lookahead=lookahead, + .incomingUnprocessed=parseQueueValues(doc, "incomingUnprocessed"), + .incomingLookahead=parseQueueValues(doc, "incomingLookahead") + + }; + + return pre; + } + + bool check(FmuContainerCoreTestProxy &fcc, FmuContainerCoreTestProxy::State &post) { + if (post.currentData != fcc.getData()) { + cout << "Current state does not match" << endl; + return false; + } else if (post.lookahead != fcc.getLookahead()) { + cout << "lookahead does not match" << endl; + return false; + } else if (post.incomingLookahead != fcc.getIncomingLookahead()) { + cout << "incomingLookahead does not match" << endl; + return false; + } else if (post.incomingUnprocessed != fcc.getIncomingUnprocessed()) { + cout << "incomingUnprocessed does not match" << endl; + return false; + } + return true; + } + + + bool processTest(Document &doc) { + + if (!doc.IsObject() || !doc.HasMember("meta") || !doc.HasMember("action") || !doc.HasMember("pre") || + !doc.HasMember("post")) { + cout << "\t## " << "Parse failed" << endl; + return false; + } + + + Value &meta = doc["meta"]; + Value &action = doc["action"]; + Value &pre = doc["pre"]; + Value &post = doc["post"]; + + cout << "\t## Meta:" << meta.GetString() << endl; + cout << "\t## Action: " << action.GetString() << endl; + + auto preState = createState(pre); + auto postState = createState(post); + + FmuContainerCoreTestProxy fcc(preState); +// cout << "######################## PRE ########################" << endl; + fcc.show("PRE "); + + cout << ">> " << action.GetString() << endl; + + auto res = false; + + if (string("dostep") == action.GetString()) { + + //FIXME what time? + res = fcc.process(0); + } else if (string("initialize").compare(action.GetString()) == 0) { + res = fcc.initialize(); + } + + + fcc.show("POST "); + + return post["fmuInitialized"].GetBool() == res; + + return check(fcc, postState); + + + } + + +} + +using namespace tla; + +int main(int argc, char **argv) { + cout << "tla" << endl; + std::vector testFiles; + + InputParser input(argc, argv); + if (input.cmdOptionExists("-h")) { + // Do stuff + cout << "usage rabbitmq-tla [args]" << endl; + cout << " -h help" << endl; + cout << " -s path to a folder containing *json test files" << endl; + cout << " -t path to a single test *.json file" << endl; + return 0; + } + const std::string &filename = input.getCmdOption("-t"); + if (!filename.empty()) { + cout << "Adding test: " << filename << endl; + testFiles.push_back(filename); + } + + const std::string &searchPath = input.getCmdOption("-s"); + if (!searchPath.empty()) { + cout << "Searching for tests in path: " << searchPath << endl; + for (const auto &entry : fs::directory_iterator(searchPath)) { + auto fn = entry.path().generic_string(); + if (fn.substr(fn.find_last_of(".") + 1) == "json") { + cout << "Adding test: " << fn << endl; + testFiles.push_back(fn); + + } + } + } + + cout << "Testing..." << endl; + + for (auto &path:testFiles) { + + cout << "############################## " << path << " ##############################" << endl; + cout << "\t## Test" << path << endl; + + ifstream f(path.c_str()); + if (!f.good()) { + cout << "\t## " << " File does not exist" << endl; + cout << "\t## " << " FAIL" << endl; + continue; + } + + std::ifstream t(path.c_str()); + std::string str((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + + Document d; + d.Parse(str.c_str()); + if (processTest(d)) { + cout << "\t## " << " PASSED" << endl; + } else { + cout << "\t## " << " FAIL" << endl; + } + } + + return 0; +} \ No newline at end of file From c84805de348e898c16f37fa03ed5d97b15fe970e Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 12:14:25 +0200 Subject: [PATCH 05/13] changed fmu to use new core --- CMakeLists.txt | 2 + rabbitmq-core/src/FmuContainerCore.cpp | 354 ++++++++++++++++ .../src/FmuContainerCore.h | 47 ++- rabbitmq-core/test/FmuContainerCore_test.cpp | 393 ++++++++++++++++++ rabbitmq-fmu/CMakeLists.txt | 25 +- rabbitmq-fmu/modelDescription.xml | 6 + rabbitmq-fmu/src/FmuContainer.cpp | 279 ++++++------- rabbitmq-fmu/src/FmuContainer.h | 2 + rabbitmq-fmu/src/FmuContainerCore.cpp | 265 ------------ rabbitmq-fmu/src/message/MessageParser.cpp | 21 +- rabbitmq-fmu/src/message/MessageParser.h | 2 +- .../ModelDescriptionParser.cpp | 5 +- .../modeldescription/ModelDescriptionParser.h | 1 + rabbitmq-fmu/test/FmuContainerCore_test.cpp | 234 ----------- rabbitmq-fmu/test/message_parser_test.cpp | 12 +- rabbitmq-tla-tester/CMakeLists.txt | 22 + .../src}/TlaTester.cpp | 237 ++++++++--- server/publish.py | 63 ++- 18 files changed, 1203 insertions(+), 767 deletions(-) create mode 100644 rabbitmq-core/src/FmuContainerCore.cpp rename {rabbitmq-fmu => rabbitmq-core}/src/FmuContainerCore.h (73%) create mode 100644 rabbitmq-core/test/FmuContainerCore_test.cpp delete mode 100644 rabbitmq-fmu/src/FmuContainerCore.cpp delete mode 100644 rabbitmq-fmu/test/FmuContainerCore_test.cpp create mode 100644 rabbitmq-tla-tester/CMakeLists.txt rename {rabbitmq-fmu/src/tla => rabbitmq-tla-tester/src}/TlaTester.cpp (61%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05dc7fc..e16f901 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,8 @@ add_subdirectory(thirdparty/googletest) #set(BUILD_TOOLS_DOCS OFF CACHE BOOL "" FORCE) add_subdirectory(thirdparty/rabbitmq-c) add_subdirectory(thirdparty/date) +add_subdirectory(rabbitmq-core) +add_subdirectory(rabbitmq-tla-tester) add_subdirectory(rabbitmq-fmu) diff --git a/rabbitmq-core/src/FmuContainerCore.cpp b/rabbitmq-core/src/FmuContainerCore.cpp new file mode 100644 index 0000000..bd72fbd --- /dev/null +++ b/rabbitmq-core/src/FmuContainerCore.cpp @@ -0,0 +1,354 @@ +// +// Created by Kenneth Guldbrandt Lausdahl on 09/03/2020. +// + +#include "FmuContainerCore.h" + +#include +FmuContainerCore::FmuContainerCore(std::chrono::milliseconds maxAge, std::map lookAhead) + : maxAge(maxAge), lookahead(lookAhead), startOffsetTime(std::chrono::milliseconds(0)),verbose(false) { + + +} + +void FmuContainerCore::add(ScalarVariableId id, TimedScalarBasicValue value) { + this->incomingUnprocessed[id].push_back(value); +} + +std::chrono::milliseconds FmuContainerCore::messageTimeToSim(date::sys_time messageTime) { + return (messageTime - this->startOffsetTime); +} + +void showL(list &list) { + for (auto &p : list) + cout << " ( " << p.first.time_since_epoch().count() << " , " << p.second << ") "; +} + +void FmuContainerCore::processIncoming() { + //sort + + for (auto &pair: this->incomingUnprocessed) { + + auto id = pair.first; + if (verbose) { + std::cout << "\t -- Incoming unprocessed: id=" << id << " - size=" << this->incomingUnprocessed[id].size() << ": "; + showL(this->incomingUnprocessed[id]); + cout << std::endl; + std::cout << "\t -- Incoming lookahead : id=" << id << " - size=" << this->incomingLookahead[id].size() << ": "; + showL(this->incomingLookahead[id]); + cout << std::endl; + } + + // read until lookahead or end + auto it = this->incomingUnprocessed[id].begin(); + auto c = 0; + for (int i = 0; i < this->lookahead[id]; i++) { + it++; + c++; + } + if (verbose) { + std::cout << "\t -- Incoming lookahead slice id=: " << id << " - count=" << c << endl; + } + //move + this->incomingLookahead[id].splice(this->incomingLookahead[id].end(), this->incomingUnprocessed[id], + this->incomingUnprocessed[id].begin(), it); + + //sort + this->incomingLookahead[id].sort( + [](const TimedScalarBasicValue &a, const TimedScalarBasicValue &b) { return a.first < b.first; }); + + if (verbose) { + std::cout << "\t --> Incoming unprocessed: id=" << id << " - size=" << this->incomingUnprocessed[id].size() << ": "; + showL(this->incomingUnprocessed[id]); + cout << std::endl; + std::cout << "\t --> Incoming lookahead : id=" << id << " - size=" << this->incomingLookahead[id].size() << ": "; + showL(this->incomingLookahead[id]); + cout << std::endl << endl; + } + } + + + if (!this->incomingUnprocessed.empty()) { + if (verbose) { + cout << "Cleaning incomingUnprocessed" << endl; + } + + for (auto itr = this->incomingUnprocessed.begin(); itr != this->incomingUnprocessed.end();) { + auto id = itr->first; + if (verbose) { + std::cout << "\t -- Incoming unprocessed : id=" << id << " - size=" << this->incomingUnprocessed[id].size() + << ": "; + showL(this->incomingUnprocessed[id]); + cout << std::endl; + } + if (itr->second.empty()) { + if (verbose) { + printf("deleting list id=%d\n", itr->first); + } + this->incomingUnprocessed.erase(itr++); + } else { + ++itr; + } + } + } +} + +bool FmuContainerCore::hasValueFor(map ¤tData, + list &knownIds) { + + for (auto &id : knownIds) { + auto itr = currentData.find(id); + if (itr == currentData.end()) { + //key missing + return false; + } + } + return true; +} + +pair> FmuContainerCore::calculateStartTime() { + + date::sys_time time; + bool initial = true; + + for (auto &pair:this->currentData) { + + auto valueTime = pair.second.first; + if (initial) { + time = valueTime; + initial = false; + continue; + } + + if (time < valueTime) { + time = valueTime; + } + + } + return std::make_pair(!initial, time); +} + + +template +void FmuContainerCore::processLookahead(Predicate predicate) { + + if(verbose) + { + cout << "Lookaheads:"<lookahead) + { + cout << "\t"<incomingLookahead) { + auto id = pair.first; + + auto itr = pair.second.begin(); + while (itr != pair.second.end()) { + auto timeValue = itr; + + if (predicate(*timeValue)) { + + this->currentData.erase(id); + this->currentData.insert(this->currentData.begin(), + std::make_pair(id, std::make_pair(timeValue->first, timeValue->second))); + if(verbose) + { + cout << "Updated state with id="<incomingLookahead.begin(); itr != this->incomingLookahead.end();) { + auto id = itr->first; + if (verbose) { + std::cout << "\t -- Incoming lookahead : Id=" << id << " - Size=" << this->incomingLookahead[id].size() + << ": "; + showL(this->incomingLookahead[id]); + cout << std::endl; + } + if (itr->second.empty()) { + if (verbose) { + printf("deleting list id=%d\n", itr->first); + } + this->incomingLookahead.erase(itr++); + } else { + ++itr; + } + } + } +} + +bool FmuContainerCore::initialize() { + processIncoming(); + + bool initial = this->currentData.empty(); + + if(verbose && initial) + { + cout <<"Initial initialize!"<startOffsetTime; + } + + }; + processLookahead(predicate); + + if (initial) { + auto initialTimePair = calculateStartTime(); + + if (initialTimePair.first) { + this->startOffsetTime = initialTimePair.second; + //no longer initial mode since time is found + initial = false; + } + } + + //run the age check for time 0 + return this->check(0); +} + +bool FmuContainerCore::process(double time) { + +//check messages for acceptable aged values + + if (this->check(time)) { + //all ok do nothing + return true; + } + +//read all incoming and sort + processIncoming(); + +//read until time + + auto predicate = [time, this](FmuContainerCore::TimedScalarBasicValue &value) { + return messageTimeToSim(value.first).count() <= time; + }; + processLookahead(predicate); + + // now all available lookahead values are read until time + + //check that we are ok + return this->check(time); + +} + +std::map FmuContainerCore::getData() { + return this->currentData; +} + +date::sys_time FmuContainerCore::getStartOffsetTime() { + return this->startOffsetTime; +} + + +bool FmuContainerCore::check(double time) { + + for (auto &lookaheadPair: this->lookahead) { + + auto id = lookaheadPair.first; + if (this->currentData.count(id) == 0) { + //missing known id + if (verbose) { + printf("Failing check on %d\n", id); + } + return false; + } + + + auto valueTime = this->currentData.at(id).first; + + if (time < messageTimeToSim(valueTime).count()) { + if (verbose) { + printf("Future value discovered. Failing check on %d. maxage %lld, t1 %lld, t1+age %lld, t %f\n", id, + this->maxAge.count(), messageTimeToSim(valueTime).count(), + (messageTimeToSim(valueTime) + this->maxAge).count(), time); + } + return false; + } + + if ((messageTimeToSim(valueTime) + this->maxAge).count() < time) { + if (verbose) { + printf("Failing check on %d. maxage %lld, t1 %lld, t1+age %lld, t %9.f\n", id, this->maxAge.count(), + messageTimeToSim(valueTime).count(), (messageTimeToSim(valueTime) + this->maxAge).count(), time); + } + return false; + } + } + + return true; +} + + +void FmuContainerCore::setVerbose(bool verbose) { + this->verbose = verbose; + +} + + +void showValue(ostream &os,const char *prefix,date::sys_time offset, FmuContainerCore::TimedScalarBasicValue val) { + os << prefix << "Time: " << val.first.time_since_epoch().count()<<" ("<< (val.first-offset).count() << ")" << " Value: " << val.second<< "\n"; +} + + + +ostream &operator<<(ostream &os, const FmuContainerCore &c) +{ + os << "------------------------------ INFO ------------------------------" << "\n"; + os << "Max age: " << c.maxAge.count() << "\n"; + os << "StartTime: " << c.startOffsetTime.time_since_epoch().count() << "\n"; + os << "Lookahead ids: ["; + for (auto &id:c.lookahead) { + cout << id.first << "->" << id.second << " "; + } + os << "]" << "\n"; + os << "Incoming" << "\n"; + for (auto &pair: c.incomingUnprocessed) { + cout << "\tId: " << pair.first << "\n"; + for (auto &val: pair.second) { + showValue(os,"\t\t",c.startOffsetTime, val); + + } + } + + os << "Lookahead" << "\n"; + for (auto &pair: c.incomingLookahead) { + os << "\tId: " << pair.first << "\n"; + for (auto &val: pair.second) { + showValue(os,"\t\t",c.startOffsetTime, val); + + } + } + + os << "Data" << "\n"; + for (auto &pair: c.currentData) { + os << "\tId: " << pair.first; + + showValue(os," ",c.startOffsetTime, pair.second); + + + } + os << "\n"; + os << "------------------------------------------------------------------" << "\n"; + return os; +} + diff --git a/rabbitmq-fmu/src/FmuContainerCore.h b/rabbitmq-core/src/FmuContainerCore.h similarity index 73% rename from rabbitmq-fmu/src/FmuContainerCore.h rename to rabbitmq-core/src/FmuContainerCore.h index c10160d..b2888ab 100644 --- a/rabbitmq-fmu/src/FmuContainerCore.h +++ b/rabbitmq-core/src/FmuContainerCore.h @@ -13,6 +13,7 @@ #include #include #include +#include using namespace std; @@ -49,7 +50,7 @@ union ScalarVariableBaseValue { ScalarVariableBaseValue(std::string s) : s{TU_STRING, std::move(s)} {} - ScalarVariableBaseValue(ScalarVariableBaseValue const &other) { + ScalarVariableBaseValue(const ScalarVariableBaseValue &other) { // This is safe. switch (other.i.type) { case TU_INT: @@ -67,30 +68,51 @@ union ScalarVariableBaseValue { } } - inline bool operator!=( const ScalarVariableBaseValue& rhs) const { return !(this == &rhs); } - bool operator==(const ScalarVariableBaseValue& other) const - { + inline bool operator!=(const ScalarVariableBaseValue &rhs) const { return !(this == &rhs); } + + bool operator==(const ScalarVariableBaseValue &other) const { // This is safe. switch (other.i.type) { case TU_INT: - return this->i.i==other.i.i; + return this->i.i == other.i.i; case TU_BOOL: - return this->b.b==other.b.b; + return this->b.b == other.b.b; case TU_DOUBLE: - return this->d.d==other.d.d; + return this->d.d == other.d.d; case TU_STRING: - return this->s.s==other.s.s; + return this->s.s == other.s.s; } return false; } + ScalarVariableBaseValue &operator=(const ScalarVariableBaseValue &other) // copy assignment + { + printf("Assign\n"); + return *this; + } + ~ScalarVariableBaseValue() { // This is safe. if (TU_STRING == s.type) { s.~s_type(); } } + + + friend ostream &operator<<(ostream &os, const ScalarVariableBaseValue &c) { + switch (c.i.type) { + case TU_INT: + os << c.i.i; + case TU_BOOL: + os << c.b.b; + case TU_DOUBLE: + os << c.d.d; + case TU_STRING: + os << c.s.s; + } + return os; + } }; class FmuContainerCore { @@ -100,7 +122,7 @@ class FmuContainerCore { typedef pair, ScalarVariableBaseValue> TimedScalarBasicValue; typedef unsigned int ScalarVariableId; - FmuContainerCore( std::chrono::milliseconds maxAge, + FmuContainerCore(std::chrono::milliseconds maxAge, std::map lookAhead); void add(ScalarVariableId id, TimedScalarBasicValue value); @@ -110,8 +132,13 @@ class FmuContainerCore { bool initialize(); std::map getData(); + date::sys_time getStartOffsetTime(); + void setVerbose(bool verbose); + + friend ostream &operator<<(ostream &os, const FmuContainerCore &c); + protected: //TODO: these should be qualified by type because the svid is not globally unique @@ -127,6 +154,8 @@ class FmuContainerCore { std::chrono::milliseconds maxAge; private: + bool verbose; + std::chrono::milliseconds messageTimeToSim(date::sys_time messageTime); bool check(double time); diff --git a/rabbitmq-core/test/FmuContainerCore_test.cpp b/rabbitmq-core/test/FmuContainerCore_test.cpp new file mode 100644 index 0000000..4441e78 --- /dev/null +++ b/rabbitmq-core/test/FmuContainerCore_test.cpp @@ -0,0 +1,393 @@ +#include "gtest/gtest.h" + +#include "FmuContainerCore.h" + + +#include + +#include "date/date.h" + +using namespace std; + +using namespace date; + +class FmuContainerCoreTestProxy : public FmuContainerCore { +public: + + struct State { + std::chrono::milliseconds maxAge; + std::map lookahead; + std::map> incomingUnprocessed; + std::map> incomingLookahead; + + std::map currentData; + + date::sys_time startOffsetTime; + }; + + + FmuContainerCoreTestProxy(const chrono::milliseconds &maxAge, std::map lookAhead) + : FmuContainerCore(maxAge, lookAhead) {} + + FmuContainerCoreTestProxy(const chrono::milliseconds &maxAge, std::map lookAhead, + std::map> incomingUnprocessed, + std::map> incomingLookahead, + + std::map currentData, + + date::sys_time startOffsetTime) + : FmuContainerCore(maxAge, lookAhead) { + this->incomingUnprocessed.insert(incomingUnprocessed.begin(), incomingUnprocessed.end()); + this->incomingLookahead.insert(incomingLookahead.begin(), incomingLookahead.end()); + this->currentData.insert(currentData.begin(), currentData.end()); + this->startOffsetTime = startOffsetTime; + } + + + FmuContainerCoreTestProxy(const State &s) + : FmuContainerCoreTestProxy(s.maxAge, s.lookahead, s.incomingUnprocessed, s.incomingLookahead, + s.currentData, s.startOffsetTime) { + } + + + const map> &getIncomingUnprocessed() const { + return incomingUnprocessed; + } + + const map> &getIncomingLookahead() const { + return incomingLookahead; + } + + const map &getCurrentData() const { + return currentData; + } + + const sys_time &getStartOffsetTime() const { + return startOffsetTime; + } + + const map &getLookahead() const { + return lookahead; + } + + const chrono::milliseconds &getMaxAge() const { + return maxAge; + } + + void show() { + cout << "------------------------------ INFO ------------------------------" << endl; + cout << "Max age: " << this->maxAge << endl; + cout << "StartTime: " << this->startOffsetTime << endl; + cout << "Lookahead ids: ["; + for (auto &id:lookahead) { + cout << id.first << "->" << id.second << " "; + } + cout << "]" << endl; + cout << "Incoming" << endl; + for (auto &pair: incomingUnprocessed) { + cout << "\tId: " << pair.first << endl; + for (auto &val: pair.second) { + showValue("\t\t", val); + + } + } + + cout << "Lookahead" << endl; + for (auto &pair: incomingLookahead) { + cout << "\tId: " << pair.first << endl; + for (auto &val: pair.second) { + showValue("\t\t", val); + + } + } + + cout << "Data" << endl; + for (auto &pair: currentData) { + cout << "\tId: " << pair.first; + + showValue(" ", pair.second); + + + } + cout << endl; + cout << "------------------------------------------------------------------" << endl; + } + +private: + void showValue(const char *prefix, FmuContainerCore::TimedScalarBasicValue val) { + cout << prefix << "Time: " << val.first.time_since_epoch().count() << " Value: "; + + switch (val.second.b.type) { + case TU_STRING: + cout << val.second.s.s; + break; + case TU_INT: + cout << val.second.i.i; + break; + case TU_BOOL: + cout << val.second.b.b; + break; + case TU_DOUBLE: + cout << val.second.d.d; + break; + + } + + cout << endl; + } + +}; +namespace { + + + bool eq(map a, + map b) { + if (a.size() != b.size()) { + printf("wrong size\n"); + return false; + } + + for (auto &pair :a) { + if (b.find(pair.first) == b.end()) { + printf("wrong size 1\n"); + return false; + } + + if (pair.second.first != b.at(pair.first).first || pair.second.second != b.at(pair.first).second) { + printf("wrong size 2\n"); + return false; + } + } + return true; + } + + void + checkInitialize(FmuContainerCoreTestProxy::State pre, FmuContainerCoreTestProxy::State post, bool expectInitOk) { + + FmuContainerCoreTestProxy c(pre); + c.setVerbose(true); + cout << "\n######################## PRE ########################" << endl; + c.show(); + + bool initSuccess = c.initialize(); + + cout << "######################## POST ########################" << endl; + c.show(); + + //if (post.currentData != c.getCurrentData() || post.incomingUnprocessed != c.getIncomingUnprocessed()) { + cout << "######################## Expected POST ########################" << endl; + FmuContainerCoreTestProxy tmp(post); + tmp.show(); + //} + + ASSERT_EQ(initSuccess, expectInitOk) << "Initialisation error"; + + ASSERT_EQ(post.maxAge, c.getMaxAge()) << "Max age must match"; + + + ASSERT_TRUE(post.currentData == c.getCurrentData()) << "State must match"; + ASSERT_TRUE(post.incomingUnprocessed == c.getIncomingUnprocessed()) << "Incoming unprocessed must match"; + ASSERT_TRUE(post.startOffsetTime == c.getStartOffsetTime()) << "Start time offset must match"; + + + } + + void + checkStep(FmuContainerCoreTestProxy::State pre, FmuContainerCoreTestProxy::State post,double time, bool expectStepOk) { + + FmuContainerCoreTestProxy c(pre); + c.setVerbose(true); + cout << "\n######################## PRE ########################" << endl; + c.show(); + + bool initSuccess = c.process(time); + + cout << "######################## POST ########################" << endl; + c.show(); + + //if (post.currentData != c.getCurrentData() || post.incomingUnprocessed != c.getIncomingUnprocessed()) { + cout << "######################## Expected POST ########################" << endl; + FmuContainerCoreTestProxy tmp(post); + tmp.show(); + //} + + ASSERT_EQ(initSuccess, expectStepOk) << "dostep error"; + + ASSERT_EQ(post.maxAge, c.getMaxAge()) << "Max age must match"; + + + ASSERT_TRUE(post.currentData == c.getCurrentData()) << "State must match"; + ASSERT_TRUE(post.incomingUnprocessed == c.getIncomingUnprocessed()) << "Incoming unprocessed must match"; + ASSERT_TRUE(post.startOffsetTime == c.getStartOffsetTime()) << "Start time offset must match"; + + + } + + + + TEST(FmuContainerCore, Initialize1 + ) { + int sv1 = 1; + + date::sys_time valueTimeZero; + + FmuContainerCoreTestProxy::State pre = { + .maxAge=std::chrono::milliseconds(0), + .lookahead={{sv1, 1}}, + .incomingUnprocessed={{sv1, {std::make_pair(valueTimeZero + std::chrono::milliseconds(0), 1)}}} + + + }; + + FmuContainerCoreTestProxy::State post = { + .maxAge=std::chrono::milliseconds(0), + .lookahead={{sv1, 1}}, + .currentData = {{sv1, std::make_pair(valueTimeZero + std::chrono::milliseconds(0), 1)}} + + + }; + + checkInitialize(pre, post, true); + } + + + TEST(FmuContainerCore, BasicOk + ) { + + FmuContainerCore::ScalarVariableId svId1 = 1; + + std::map lookahead = {{svId1, 1}}; + + std::chrono::milliseconds maxAge = std::chrono::milliseconds(0); + + + FmuContainerCoreTestProxy c(maxAge, lookahead); + + + ASSERT_FALSE(c.initialize()) << "Initialization Should fail"; + + date::sys_time startTime; + + std::stringstream startTimeStamp; + startTimeStamp << startTime; + c.add(svId1, std::make_pair(startTime, ScalarVariableBaseValue(1))); + + ASSERT_TRUE(c.initialize()) << "Initialization Should NOT fail"; + + date::sys_time t; + for (int i = 0; i < 5; i++) { + + t += std::chrono::milliseconds(1); + c.add(svId1, std::make_pair(t, ScalarVariableBaseValue(i + 1))); + } + c.show(); + bool ok = c.process(2); + c.show(); + + cout << "OK " << ok << endl; + } + + + TEST(FmuContainerCoreTla, InitOk + ) { + int sv1 = 1; + int sv0 = 0; + + date::sys_time valueTimeZero; + + FmuContainerCoreTestProxy::State pre = { + .maxAge=std::chrono::milliseconds(0), + .lookahead={{sv0, 1}, + {sv1, 1}}, + .incomingUnprocessed={{sv0, {std::make_pair(valueTimeZero + std::chrono::milliseconds(2), 2)}}, + {sv1, {std::make_pair(valueTimeZero + std::chrono::milliseconds(2), + 2), std::make_pair( + valueTimeZero + std::chrono::milliseconds(1), 1)}}} + + + }; + + FmuContainerCoreTestProxy::State post = { + .maxAge=std::chrono::milliseconds(0), + .lookahead={{sv0, 1}, + {sv1, 1}}, + .incomingUnprocessed={ + {sv1, {std::make_pair( + valueTimeZero + std::chrono::milliseconds(1), 1)}}}, + .incomingLookahead={{sv0, {std::make_pair(valueTimeZero + std::chrono::milliseconds(2), 2)}}, + {sv1, {std::make_pair(valueTimeZero + std::chrono::milliseconds(2), + 2)}}}, + .currentData = {{sv0, std::make_pair(valueTimeZero + std::chrono::milliseconds(2), 2)}, + {sv1, std::make_pair(valueTimeZero + std::chrono::milliseconds(2), 2)}}, + .startOffsetTime=valueTimeZero + std::chrono::milliseconds(2) + + + }; + + checkInitialize(pre, post, true); + + } + + FmuContainerCore::TimedScalarBasicValue V(int time, int val) { + date::sys_time valueTimeZero; + return std::make_pair(valueTimeZero + std::chrono::milliseconds(time), val); + } + + TEST(FmuContainerCoreTla, InitOkDoStepOk + ) { + int sv1 = 1; + int sv0 = 0; + + date::sys_time valueTimeZero; + + FmuContainerCoreTestProxy::State pre = { + .maxAge=std::chrono::milliseconds(1), + .lookahead={{sv0, 1}, + {sv1, 2}}, + .incomingUnprocessed={{sv0, {V(1, 1), V(2, 2)}}, + {sv1, {V(1, 1), V(2, 2)}}} + + + }; + + FmuContainerCoreTestProxy::State post = { + .maxAge=std::chrono::milliseconds(1), + .lookahead={{sv0, 1}, + {sv1, 2}}, + .incomingUnprocessed={{sv0, {V(2, 2)}}}, +// .incomingLookahead={ +// { sv0, {V(1, 1)}}, +// { sv1, {V(2, 2)}} +// }, + .currentData = { + {sv0, V(1, 1)}, + {sv1, V(2, 2)} + }, + .startOffsetTime=valueTimeZero + std::chrono::milliseconds(2) + + + }; + + checkInitialize(pre, post, true); + + FmuContainerCoreTestProxy::State postDoStep = { + .maxAge=std::chrono::milliseconds(1), + .lookahead={{sv0, 1}, + {sv1, 2}}, +// .incomingUnprocessed={{sv0, {V(2, 2)}}}, +// .incomingLookahead={ +// { sv0, {V(1, 1)}}, +// { sv1, {V(2, 2)}} +// }, + .currentData = { + {sv0, V(2, 2)}, + {sv1, V(2, 2)} + }, + .startOffsetTime=valueTimeZero + std::chrono::milliseconds(2) + + + }; + + checkStep(post, postDoStep,1,true); + } +} \ No newline at end of file diff --git a/rabbitmq-fmu/CMakeLists.txt b/rabbitmq-fmu/CMakeLists.txt index 3ca4aeb..9adb207 100644 --- a/rabbitmq-fmu/CMakeLists.txt +++ b/rabbitmq-fmu/CMakeLists.txt @@ -17,17 +17,9 @@ CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17) # message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") #endif () - - - -#add_dependencies(rapidjson) -#include_directories(${libshmfmu_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/fmi/include) include_directories(${RapidJSON_SOURCE_DIR}/include) include_directories(${gtest_SOURCE_DIR}/include) -#include_directories(${LIBRABBITMQ_INCLUDE_DIRS}) -#target_link_libraries(${PROJECT_NAME} libshmfmu_static) -#target_link_libraries(${PROJECT_NAME} ${libshmfmu_LIBS}) #if (UNIX) @@ -60,21 +52,16 @@ endif () file(GLOB_RECURSE CPP_FILES src/*.cpp) -list(FILTER CPP_FILES EXCLUDE REGEX "TlaTester.cpp") list(FILTER CPP_FILES EXCLUDE REGEX "main.cpp") file(GLOB C_FILES src/*.c) # Main add_executable(${PROJECT_NAME}-main ${C_FILES} ${CPP_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp) -target_link_libraries(${PROJECT_NAME}-main XercesC::XercesC rabbitmq-static tz) - -# TLA tester -add_executable(${PROJECT_NAME}-tla ${C_FILES} ${CPP_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/src/tla/TlaTester.cpp) -target_link_libraries(${PROJECT_NAME}-tla XercesC::XercesC rabbitmq-static tz) +target_link_libraries(${PROJECT_NAME}-main XercesC::XercesC rabbitmq-static tz rabbitmq-core) add_library(rabbitmq SHARED ${C_FILES} ${CPP_FILES}) -target_link_libraries(rabbitmq XercesC::XercesC rabbitmq-static tz) +target_link_libraries(rabbitmq XercesC::XercesC rabbitmq-static tz rabbitmq-core) set_target_properties(rabbitmq PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(rabbitmq PROPERTIES PREFIX "") @@ -128,12 +115,12 @@ file(GLOB CPP_TEST_FILES test/*.cpp) list(FILTER CPP_TEST_FILES EXCLUDE REGEX ".*_it.cpp") add_executable(unit-test-${PROJECT_NAME} ${C_FILES} ${CPP_FILES} ${CPP_TEST_FILES}) -target_link_libraries(unit-test-${PROJECT_NAME} gtest_main XercesC::XercesC rabbitmq-static tz) +target_link_libraries(unit-test-${PROJECT_NAME} gtest_main XercesC::XercesC rabbitmq-static tz rabbitmq-core) if (WIN32) - add_test(unit-test-${PROJECT_NAME} wine ${PROJECT_NAME} --gtest_output=xml:gtestresults.xml) + add_test(NAME unit-test-${PROJECT_NAME} COMMAND wine unit-test-${PROJECT_NAME} --gtest_output=xml:gtestresults.xml) else () - add_test(unit-test-${PROJECT_NAME} ${PROJECT_NAME} --gtest_output=xml:gtestresults.xml) + add_test(NAME unit-test-${PROJECT_NAME} COMMAND unit-test-${PROJECT_NAME} --gtest_output=xml:gtestresults.xml) endif () # ---------------------------- Integration TESTS ------------------------------ @@ -142,7 +129,7 @@ file(GLOB CPP_TEST_FILES test/*.cpp) list(FILTER CPP_TEST_FILES INCLUDE REGEX ".*_it.cpp") add_executable(it-test-${PROJECT_NAME} ${C_FILES} ${CPP_FILES} ${CPP_TEST_FILES}) -target_link_libraries(it-test-${PROJECT_NAME} gtest_main XercesC::XercesC rabbitmq-static tz) +target_link_libraries(it-test-${PROJECT_NAME} gtest_main XercesC::XercesC rabbitmq-static tz rabbitmq-core) #if (WIN32) # add_test(it-test-${PROJECT_NAME} wine ${PROJECT_NAME} --gtest_output=xml:gtestresults.xml) diff --git a/rabbitmq-fmu/modelDescription.xml b/rabbitmq-fmu/modelDescription.xml index 6e6de12..8b77c7a 100644 --- a/rabbitmq-fmu/modelDescription.xml +++ b/rabbitmq-fmu/modelDescription.xml @@ -44,6 +44,12 @@ + + + + + + diff --git a/rabbitmq-fmu/src/FmuContainer.cpp b/rabbitmq-fmu/src/FmuContainer.cpp index 32d5553..41968c0 100644 --- a/rabbitmq-fmu/src/FmuContainer.cpp +++ b/rabbitmq-fmu/src/FmuContainer.cpp @@ -26,18 +26,52 @@ FmuContainer::FmuContainer(const fmi2CallbackFunctions *mFunctions, bool logginOn, const char *mName, map nameToValueReference, DataPoint initialDataPoint) - : m_functions(mFunctions), m_name(mName), nameToValueReference(std::move(nameToValueReference)), + : m_functions(mFunctions), m_name(mName), nameToValueReference((nameToValueReference)), currentData(std::move(initialDataPoint)), rabbitMqHandler(NULL), startOffsetTime(floor(std::chrono::system_clock::now())), communicationTimeout(30), loggingOn(logginOn), precision(10) { + + auto intConfigs = {std::make_pair(RABBITMQ_FMU_MAX_AGE, "max_age"), + std::make_pair(RABBITMQ_FMU_LOOKAHEAD, "lookahead")}; + + + int maxAgeMs = 0; + int lookaheadBound = 1; + + for (auto const &value: intConfigs) { + auto vRef = value.first; + auto description = value.second; + + if (this->currentData.integerValues.find(vRef) == this->currentData.integerValues.end()) { + FmuContainer_LOG(fmi2Warning, "logWarn", + "Missing parameter. Value reference '%d', Description '%s'.", vRef, + description); + } else { + if (vRef == RABBITMQ_FMU_MAX_AGE) { + maxAgeMs = this->currentData.integerValues[vRef]; + } else if (vRef == RABBITMQ_FMU_LOOKAHEAD) { + auto v = this->currentData.integerValues[vRef]; + if (v < 1) { + FmuContainer_LOG(fmi2Warning, "logWarn", + "Invalid parameter value. Value reference '%d', Description '%s' Value '%d'. Defaulting to %d.", + vRef, + description, v, lookaheadBound); + v = lookaheadBound; + } + lookaheadBound = v; + } + } + } std::map lookahead; for (auto &pair: nameToValueReference) { - lookahead[pair.second.valueReference] = 1; + if (pair.second.output) { + lookahead[pair.second.valueReference] = lookaheadBound; + } } - std::chrono::milliseconds maxAge = std::chrono::milliseconds(0); + std::chrono::milliseconds maxAge = std::chrono::milliseconds(maxAgeMs); this->core = new FmuContainerCore(maxAge, lookahead); } @@ -154,8 +188,16 @@ bool FmuContainer::initialize() { return false; } + FmuContainer_LOG(fmi2OK, "logAll", + "Sending RabbitMQ ready message%s", ""); + this->rabbitMqHandler->publish(routingKey, + R"({"internal_status":"ready", "internal_message":"waiting for input data for simulation"})"); + - initializeCoreState(); + if (!initializeCoreState()) { + FmuContainer_LOG(fmi2Fatal, "logError", "Initialization failed%s", ""); + return false; + } std::stringstream startTimeStamp; @@ -190,29 +232,39 @@ bool FmuContainer::initializeCoreState() { if (this->rabbitMqHandler->consume(json)) { //data received - auto result = MessageParser::parse(&this->nameToValueReference, json.c_str()); + DataPoint result; + if (MessageParser::parse(&this->nameToValueReference, json.c_str(), &result)) { - std::stringstream startTimeStamp; - startTimeStamp << result.time; + std::stringstream startTimeStamp; + startTimeStamp << result.time; - FmuContainer_LOG(fmi2OK, "logOk", "Got data '%s', '%s'", startTimeStamp.str().c_str(), json.c_str()); + FmuContainer_LOG(fmi2OK, "logOk", "Got data '%s', '%s'", startTimeStamp.str().c_str(), + json.c_str()); - for (auto &pair: result.integerValues) { - this->core->add(pair.first, std::make_pair(result.time, pair.second)); - } - for (auto &pair: result.stringValues) { - this->core->add(pair.first, std::make_pair(result.time, pair.second)); - } - for (auto &pair: result.doubleValues) { - this->core->add(pair.first, std::make_pair(result.time, pair.second)); - } - for (auto &pair: result.booleanValues) { - this->core->add(pair.first, std::make_pair(result.time, pair.second)); - } + for (auto &pair: result.integerValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.stringValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.doubleValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.booleanValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + + if (this->core->initialize()) { + + FmuContainer_LOG(fmi2OK, "logOk", "Initialization OK%s", ""); + +// cout << "Initialized" << endl; +// cout << *this->core; - if (this->core->initialize()) - { - return true; + return true; + } + } else { + FmuContainer_LOG(fmi2OK, "logWarn", "Got unknown json '%s'", json.c_str()); } } @@ -225,25 +277,6 @@ bool FmuContainer::initializeCoreState() { return false; - -// DataPoint zeroTimeDt; -// bool timeoutOccurred; -// if (!readMessage(&zeroTimeDt, this->communicationTimeout, &timeoutOccurred)) { -// FmuContainer_LOG(fmi2Fatal, "logAll", -// "Did not receive initial message withing %d seconds", this->communicationTimeout); -// return false; -// } -// -// std::stringstream startTimeStamp; -// startTimeStamp << zeroTimeDt.time; -// -// FmuContainer_LOG(fmi2OK, "logAll", -// "Received initial data message with time '%s' which will be simulation time zero '0'", -// startTimeStamp.str().c_str()); -// this->currentData.merge(zeroTimeDt); -// this->startOffsetTime = zeroTimeDt.time; - - } @@ -257,39 +290,6 @@ std::chrono::milliseconds FmuContainer::messageTimeToSim(date::sys_time) (std::chrono::system_clock::now() - start)).count() < timeout) { -// -// if (this->rabbitMqHandler->consume(json)) { -// //data received -// -// auto result = MessageParser::parse(&this->nameToValueReference, json.c_str()); -// -// std::stringstream startTimeStamp; -// startTimeStamp << result.time; -// -// FmuContainer_LOG(fmi2OK, "logOk", "Got data '%s', '%s'", startTimeStamp.str().c_str(), json.c_str()); -// -// *dataPoint = result; -// *timeoutOccurred = false; -// return true; -// } -// -// } -// -// } catch (exception &e) { -// FmuContainer_LOG(fmi2Fatal, "logFatal", "Read message exception '%s'", e.what()); -// throw e; -// } -// -// *timeoutOccurred = true; -// return false; -// -//} bool FmuContainer::step(fmi2Real currentCommunicationPoint, fmi2Real communicationStepSize) { auto simulationTime = secondsToMs(currentCommunicationPoint + communicationStepSize); @@ -298,12 +298,27 @@ bool FmuContainer::step(fmi2Real currentCommunicationPoint, fmi2Real communicati simulationTime = std::round(simulationTime * precision) / precision; +// cout << *this->core; +// cout << "Step " << simulationTime << "\n"; + +// std::ostringstream stream; +// stream << *this->core; +// std::string str = stream.str(); +// const char *chr = str.c_str(); +// FmuContainer_LOG(fmi2OK, "logAll", "Step reached target time %.0f [ms]: %s", simulationTime, chr); + +// cout << "Checking with existing messages\n"; + if (this->core->process(simulationTime)) { + FmuContainer_LOG(fmi2OK, "logAll", "Step reached target time %.0f [ms]", simulationTime); + return true; + } if (!this->rabbitMqHandler) { FmuContainer_LOG(fmi2Fatal, "logAll", "Rabbitmq handle not initialized%s", ""); return false; } +// cout << "Checking with new messages\n"; auto start = std::chrono::system_clock::now(); try { @@ -314,31 +329,38 @@ bool FmuContainer::step(fmi2Real currentCommunicationPoint, fmi2Real communicati if (this->rabbitMqHandler->consume(json)) { //data received - auto result = MessageParser::parse(&this->nameToValueReference, json.c_str()); + DataPoint result; - std::stringstream startTimeStamp; - startTimeStamp << result.time; + if (MessageParser::parse(&this->nameToValueReference, json.c_str(), &result)) { - FmuContainer_LOG(fmi2OK, "logOk", "Got data '%s', '%s'", startTimeStamp.str().c_str(), json.c_str()); + std::stringstream startTimeStamp; + startTimeStamp << result.time; - for (auto &pair: result.integerValues) { - this->core->add(pair.first, std::make_pair(result.time, pair.second)); - } - for (auto &pair: result.stringValues) { - this->core->add(pair.first, std::make_pair(result.time, pair.second)); - } - for (auto &pair: result.doubleValues) { - this->core->add(pair.first, std::make_pair(result.time, pair.second)); - } - for (auto &pair: result.booleanValues) { - this->core->add(pair.first, std::make_pair(result.time, pair.second)); - } + FmuContainer_LOG(fmi2OK, "logOk", "Got data '%s', '%s'", startTimeStamp.str().c_str(), + json.c_str()); + + for (auto &pair: result.integerValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.stringValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.doubleValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } + for (auto &pair: result.booleanValues) { + this->core->add(pair.first, std::make_pair(result.time, pair.second)); + } - if (this->core->process(simulationTime)) { - FmuContainer_LOG(fmi2OK, "logAll", "Step reached target time %.0f [ms]", simulationTime); - return true; + + } else { + FmuContainer_LOG(fmi2OK, "logWarn", "Got unknown json '%s'", json.c_str()); } } + if (this->core->process(simulationTime)) { + FmuContainer_LOG(fmi2OK, "logAll", "Step reached target time %.0f [ms]", simulationTime); + return true; + } } @@ -346,75 +368,10 @@ bool FmuContainer::step(fmi2Real currentCommunicationPoint, fmi2Real communicati FmuContainer_LOG(fmi2Fatal, "logFatal", "Read message exception '%s'", e.what()); return false; } + FmuContainer_LOG(fmi2Fatal, "logError", "Did not get data to proceed to time '%f'", simulationTime); + return false; - - - - - - -return false; - - - - - - - - -// -// -// -// if (!this->rabbitMqHandler) { -// FmuContainer_LOG(fmi2Fatal, "logAll", "Rabbitmq handle not initialized%s", ""); -// return false; -// } -// -// if (messageTimeToSim(this->currentData.time).count() == simulationTime) { -// return true; -// } -// -// -// while (!this->data.empty() && messageTimeToSim(this->data.front().time).count() <= simulationTime) { -// -// this->currentData.merge(this->data.front()); -// this->data.pop_front(); -// -// } -// -// -// DataPoint newMessage; -// bool timeoutOccurred = false; -// -// while (readMessage(&newMessage, this->communicationTimeout, &timeoutOccurred)) { -// auto msgSimTime = messageTimeToSim(newMessage.time).count(); -// -// if (msgSimTime > simulationTime) { -// -// //ok this message is for the future. -// //queue current read message for newt step -// DataPoint tmp; -// -// tmp = newMessage; -// this->data.push_back(tmp); -// //Stop reading messages and leave them queue on the queue outside this program -// break; -// } else { -// //ok the message defined the values for this step so merge it -// this->currentData.merge(newMessage); -// } -// } - - -// FmuContainer_LOG(fmi2OK, "logAll", "Step time %.0f [ms] data time %lld [ms]", simulationTime, -// messageTimeToSim(this->currentData.time).count()); -// -// /*the current state is only valid if we have a next message with a timestamp that is after simulationTime. -// * Then we know that the current values are valid until after the simulation time and we can safely use these*/ -// return !timeoutOccurred && (messageTimeToSim(this->currentData.time).count() == simulationTime || -// (messageTimeToSim(currentData.time).count() < simulationTime && !this->data.empty() && -// messageTimeToSim(this->data.front().time).count() > simulationTime)); } /*#################################################### diff --git a/rabbitmq-fmu/src/FmuContainer.h b/rabbitmq-fmu/src/FmuContainer.h index 892b223..6703eb3 100644 --- a/rabbitmq-fmu/src/FmuContainer.h +++ b/rabbitmq-fmu/src/FmuContainer.h @@ -26,6 +26,8 @@ #define RABBITMQ_FMU_ROUTING_KEY 4 #define RABBITMQ_FMU_COMMUNICATION_READ_TIMEOUT 5 #define RABBITMQ_FMU_PRECISION 6 +#define RABBITMQ_FMU_MAX_AGE 7 +#define RABBITMQ_FMU_LOOKAHEAD 8 using namespace std; diff --git a/rabbitmq-fmu/src/FmuContainerCore.cpp b/rabbitmq-fmu/src/FmuContainerCore.cpp deleted file mode 100644 index ef532ce..0000000 --- a/rabbitmq-fmu/src/FmuContainerCore.cpp +++ /dev/null @@ -1,265 +0,0 @@ -// -// Created by Kenneth Guldbrandt Lausdahl on 09/03/2020. -// - -#include "FmuContainerCore.h" - -#include -FmuContainerCore::FmuContainerCore( std::chrono::milliseconds maxAge, std::map lookAhead) - : maxAge(maxAge), lookahead(lookAhead), startOffsetTime(std::chrono::milliseconds(0)) {} - -void FmuContainerCore::add(ScalarVariableId id, TimedScalarBasicValue value) { - -// if (this->incomingUnprocessed.count(id) > 0) { - this->incomingUnprocessed[id].push_back(value); -// } else { -// list l; -// l.push_back(value); -// this->incomingUnprocessed[id] = l; -// } - -} - -std::chrono::milliseconds FmuContainerCore::messageTimeToSim(date::sys_time messageTime) { - return (messageTime - this->startOffsetTime); -} - -void FmuContainerCore::processIncoming() { - //sort - - for (auto &pair: this->incomingUnprocessed) { - - auto id = pair.first; - - std::cout << "\t -- Incoming unprocessed: "<incomingUnprocessed[id].size() <incomingLookahead[id].size() <incomingUnprocessed[id].begin(); - auto c=0; - for (int i = 0; i < this->lookahead[id]; i++) { - it++; - c++; - } - - std::cout << "\t -- Incoming lookahead slice: "<incomingLookahead[id].splice(this->incomingLookahead[id].end(), this->incomingUnprocessed[id], - this->incomingUnprocessed[id].begin(), it); - - //sort - pair.second.sort( - [](const TimedScalarBasicValue &a, const TimedScalarBasicValue &b) { return a.first < b.first; }); - std::cout << "\t --> Incoming unprocessed: "<incomingUnprocessed[id].size() < Incoming lookahead : "<incomingLookahead[id].size() < ¤tData, - list &knownIds) { - - for (auto &id : knownIds) { - auto itr = currentData.find(id); - if (itr == currentData.end()) { - //key missing - return false; - } - } - return true; -} - -pair> FmuContainerCore::calculateStartTime() { - - date::sys_time time; - bool initial=true; - - for (auto &pair:this->currentData) { - - auto valueTime = pair.second.first; - if(initial) - { - time = valueTime; - initial = false; - continue; - } - - if(time < valueTime) - { - time = valueTime; - } - - } - return std::make_pair(!initial,time); -} - - - - -template -void FmuContainerCore::processLookahead(Predicate predicate){ - for (auto &pair: this->incomingLookahead) { - auto id = pair.first; - - auto itr = pair.second.begin(); - while (itr != pair.second.end()) { - auto timeValue = itr; - - if (predicate(*timeValue)) { - - this->currentData.erase(id); - this->currentData.insert(this->currentData.begin(), - std::make_pair(id, std::make_pair(timeValue->first, timeValue->second))); - - itr = pair.second.erase(itr); - } else { - //stop if value is newer than time - break; - } - } - } -} - -bool FmuContainerCore::initialize() { - processIncoming(); - - bool initial = this->currentData.empty(); - - //process all lookahead messages - auto predicate = [this,initial](FmuContainerCore::TimedScalarBasicValue& value){ - if(initial) - { - return true; - } else{ - return value.first <= this->startOffsetTime; - } - - }; - processLookahead(predicate); - - if(initial){ - auto initialTimePair= calculateStartTime(); - - if(initialTimePair.first) - { - this->startOffsetTime = initialTimePair.second; - //no longer initial mode since time is found - initial = false; - } - } - - //run the age check for time 0 - return this->check(0); - -// for (auto &pair: this->incomingLookahead) { -// auto id = pair.first; -// -// auto itr = pair.second.begin(); -// while (itr != pair.second.end()) { -// auto timeValue = itr; -// -// if (this->currentData.size() != knownIds.size() || !hasValueFor(this->currentData, knownIds)) { -// -// this->currentData.erase(id); -// this->currentData.insert(this->currentData.begin(), -// std::make_pair(id, std::make_pair(timeValue->first, timeValue->second))); -// -// itr = pair.second.erase(itr); -// } else { -// //stop if value is newer than time -// break; -// } -// } -// } - -// auto initialTimePair= calculateStartTime(); -// -// if(initialTimePair.first) -// { -// this->startOffsetTime = initialTimePair.second; -// } -// return initialTimePair.first; - -} - -bool FmuContainerCore::process(double time) { - -//check messages for acceptable aged values - - - if (this->check(time)) { - //all ok do nothing - return true; - } - -//read all incoming and sort - processIncoming(); - - -//read until time - - auto predicate = [time,this](FmuContainerCore::TimedScalarBasicValue& value){ - return messageTimeToSim(value.first).count() <= time; - }; - processLookahead(predicate); -// for (auto &pair: this->incomingLookahead) { -// auto id = pair.first; -// -// auto itr = pair.second.begin(); -// while (itr != pair.second.end()) { -// auto timeValue = itr; -// -// if (messageTimeToSim(timeValue->first).count() <= time) { -//// timeValue->swap(this->currentData[id]); -// -// this->currentData.erase(id); -// this->currentData.insert(this->currentData.begin(), -// std::make_pair(id, std::make_pair(timeValue->first, timeValue->second))); -// -// itr = pair.second.erase(itr); -// } else { -// //stop if value is newer than time -// break; -// } -// } -// } - - - // now all available lookahead values are read until time - - //check that we are ok - return this->check(time); - -} - -std::map FmuContainerCore::getData(){ - return this->currentData; -} - -date::sys_time FmuContainerCore::getStartOffsetTime(){ - return this->startOffsetTime; -} - - -bool FmuContainerCore::check(double time) { - - for (auto &lookaheadPair: this->lookahead) { - - auto id = lookaheadPair.first; - if (this->currentData.count(id) == 0) { - //missing known id - return false; - } - - - auto valueTime = this->currentData.at(id).first; - - if ((messageTimeToSim(valueTime) + this->maxAge).count() <= time || time < messageTimeToSim(valueTime).count()) { - return false; - } - } - - return true; -} - - diff --git a/rabbitmq-fmu/src/message/MessageParser.cpp b/rabbitmq-fmu/src/message/MessageParser.cpp index b8f3fe5..9d2f80f 100644 --- a/rabbitmq-fmu/src/message/MessageParser.cpp +++ b/rabbitmq-fmu/src/message/MessageParser.cpp @@ -15,19 +15,27 @@ using namespace rapidjson; using SvType = ModelDescriptionParser::ScalarVariable::SvType; -DataPoint -MessageParser::parse(map *nameToValueReference, const char *json) { +bool +MessageParser::parse(map *nameToValueReference, const char *json, + DataPoint *output) { DataPoint result; Document d; d.Parse(json); + bool hasData = false; + for (Value::ConstMemberIterator itr = d.MemberBegin(); itr != d.MemberEnd(); ++itr) { auto memberName = itr->name.GetString(); - if (std::string("time").compare(memberName) == 0 && d["time"].IsString()) { + if(string(memberName).rfind("internal_",0)==0) + { + continue; + } + + if (std::string("time") == memberName && d["time"].IsString()) { const char *timeString = d["time"].GetString(); result.time = Iso8601::parseIso8601ToMilliseconds(std::string(timeString)); @@ -38,6 +46,7 @@ MessageParser::parse(map *nameTo // not found cout << "Input data contains unknown member " << memberName << endl; } else { + hasData = true; auto sv = (*nameToValueReference)[memberName]; switch (sv.type) { @@ -60,6 +69,10 @@ MessageParser::parse(map *nameTo } } - return result; + *output = result; + + return hasData && d.HasMember("time"); + + } \ No newline at end of file diff --git a/rabbitmq-fmu/src/message/MessageParser.h b/rabbitmq-fmu/src/message/MessageParser.h index f0e8a3f..36cf34a 100644 --- a/rabbitmq-fmu/src/message/MessageParser.h +++ b/rabbitmq-fmu/src/message/MessageParser.h @@ -14,7 +14,7 @@ class MessageParser { public : - static DataPoint parse(map *nameToValueReference,const char *json); + static bool parse(map *nameToValueReference,const char *json, DataPoint* output); }; diff --git a/rabbitmq-fmu/src/modeldescription/ModelDescriptionParser.cpp b/rabbitmq-fmu/src/modeldescription/ModelDescriptionParser.cpp index 69f3aa0..bd37da1 100644 --- a/rabbitmq-fmu/src/modeldescription/ModelDescriptionParser.cpp +++ b/rabbitmq-fmu/src/modeldescription/ModelDescriptionParser.cpp @@ -147,6 +147,7 @@ map ModelDescriptionParser::par } sv.type = type; + sv.output = isOutput; if (isOutput) { //set default values @@ -228,19 +229,15 @@ DataPoint ModelDescriptionParser::extractDataPoint(map switch (sv.type) { case SvType::Integer: -// cout << sv.valueReference << "->" << sv.i_value << endl; dp.integerValues[sv.valueReference] = sv.i_value; break; case SvType::Real: -// cout << sv.valueReference << "->" << sv.d_value << endl; dp.doubleValues[sv.valueReference] = sv.d_value; break; case SvType::Boolean: -// cout << sv.valueReference << "->" << sv.b_value << endl; dp.booleanValues[sv.valueReference] = sv.b_value; break; case SvType::String: -// cout << sv.valueReference << "->" << sv.s_value << endl; dp.stringValues[sv.valueReference] = sv.s_value; break; } diff --git a/rabbitmq-fmu/src/modeldescription/ModelDescriptionParser.h b/rabbitmq-fmu/src/modeldescription/ModelDescriptionParser.h index e2cbd77..4e60eb6 100644 --- a/rabbitmq-fmu/src/modeldescription/ModelDescriptionParser.h +++ b/rabbitmq-fmu/src/modeldescription/ModelDescriptionParser.h @@ -23,6 +23,7 @@ class ModelDescriptionParser { string name; unsigned int valueReference; enum SvType{Real,Integer,Boolean,String}; + bool output; SvType type; union { double d_value; diff --git a/rabbitmq-fmu/test/FmuContainerCore_test.cpp b/rabbitmq-fmu/test/FmuContainerCore_test.cpp deleted file mode 100644 index 77758e5..0000000 --- a/rabbitmq-fmu/test/FmuContainerCore_test.cpp +++ /dev/null @@ -1,234 +0,0 @@ -#include "gtest/gtest.h" - -#include "FmuContainerCore.h" - - -#include - -#include "date/date.h" - -using namespace std; - -using namespace date; - -class FmuContainerCoreTestProxy : public FmuContainerCore { -public: - - struct State { - std::chrono::milliseconds maxAge; - std::map lookahead; - std::map> incomingUnprocessed; - std::map> incomingLookahead; - - std::map currentData; - - date::sys_time startOffsetTime; - }; - - - FmuContainerCoreTestProxy(const chrono::milliseconds &maxAge, std::map lookAhead) - : FmuContainerCore(maxAge, lookAhead) {} - - FmuContainerCoreTestProxy(const chrono::milliseconds &maxAge, std::map lookAhead, - std::map> incomingUnprocessed, - std::map> incomingLookahead, - - std::map currentData, - - date::sys_time startOffsetTime) - : FmuContainerCore(maxAge, lookAhead) { - this->incomingUnprocessed.insert(incomingUnprocessed.begin(), incomingUnprocessed.end()); - this->incomingLookahead.insert(incomingLookahead.begin(), incomingLookahead.end()); - this->currentData.insert(currentData.begin(), currentData.end()); - this->startOffsetTime = startOffsetTime; - } - - - FmuContainerCoreTestProxy(const State &s) - : FmuContainerCoreTestProxy(s.maxAge, s.lookahead, s.incomingUnprocessed, s.incomingLookahead, - s.currentData, s.startOffsetTime) { - } - - - const map> &getIncomingUnprocessed() const { - return incomingUnprocessed; - } - - const map> &getIncomingLookahead() const { - return incomingLookahead; - } - - const map& getCurrentData() const { - return currentData; - } - - const sys_time &getStartOffsetTime() const { - return startOffsetTime; - } - - const map &getLookahead() const { - return lookahead; - } - - const chrono::milliseconds &getMaxAge() const { - return maxAge; - } - - void show() { - cout << "------------------------------ INFO ------------------------------" << endl; - cout << "Max age: " << this->maxAge << endl; - cout << "StartTime: "<startOffsetTime <" << id.second << " "; - } - cout << "]" << endl; - cout << "Incoming" << endl; - for (auto &pair: incomingUnprocessed) { - cout << "\tId: " << pair.first << endl; - for (auto &val: pair.second) { - showValue("\t\t", val); - - } - } - - cout << "Lookahead" << endl; - for (auto &pair: incomingLookahead) { - cout << "\tId: " << pair.first << endl; - for (auto &val: pair.second) { - showValue("\t\t", val); - - } - } - - cout << "Data" << endl; - for (auto &pair: currentData) { - cout << "\tId: " << pair.first; - - showValue(" ", pair.second); - - - } - cout << endl; - cout << "------------------------------------------------------------------" << endl; - } - -private: - void showValue(const char *prefix, FmuContainerCore::TimedScalarBasicValue val) { - cout << prefix << "Time: " << val.first.time_since_epoch().count() << " Value: "; - - switch (val.second.b.type) { - case TU_STRING: - cout << val.second.s.s; - break; - case TU_INT: - cout << val.second.i.i; - break; - case TU_BOOL: - cout << val.second.b.b; - break; - case TU_DOUBLE: - cout << val.second.d.d; - break; - - } - - cout << endl; - } - -}; -namespace { - - - bool eq(mapa, map b) - { - if(a.size()!=b.size()) - return false; - - for(auto &pair :a) - { - if(b.find(pair.first) == b.end()) - return false; - - if(pair.second.first != b.at(pair.first).first || pair.second.second != b.at(pair.first).second) - { - return false; - } - } - return true; - } - - void checkInitialize(FmuContainerCoreTestProxy::State pre, FmuContainerCoreTestProxy::State post, bool shouldFail) { - - FmuContainerCoreTestProxy c(pre); - cout << "######################## PRE ########################"< valueTimeZero; - - FmuContainerCoreTestProxy::State pre = { - .maxAge=std::chrono::milliseconds(0), - .lookahead={{sv1, 1}}, - .incomingUnprocessed={{sv1, {std::make_pair(valueTimeZero + std::chrono::milliseconds(0), 1)}}} - - - }; - - checkInitialize(pre,pre,false); - - - } - - - TEST(FmuContainerCore, BasicOk - ) { - - FmuContainerCore::ScalarVariableId svId1 = 1; - - std::map lookahead = {{svId1, 1}}; - - std::chrono::milliseconds maxAge = std::chrono::milliseconds(0); - - - FmuContainerCoreTestProxy c(maxAge, lookahead); - - - ASSERT_FALSE(c.initialize()) << "Initialization Should fail"; - - date::sys_time startTime; - - std::stringstream startTimeStamp; - startTimeStamp << startTime; - c.add(svId1, std::make_pair(startTime, ScalarVariableBaseValue(1))); - - ASSERT_TRUE(c.initialize()) << "Initialization Should NOT fail"; - - date::sys_time t; - for (int i = 0; i < 5; i++) { - - t += std::chrono::milliseconds(1); - c.add(svId1, std::make_pair(t, ScalarVariableBaseValue(i + 1))); - } - c.show(); - bool ok = c.process(2); - c.show(); - - cout << "OK " << ok << endl; - } -} \ No newline at end of file diff --git a/rabbitmq-fmu/test/message_parser_test.cpp b/rabbitmq-fmu/test/message_parser_test.cpp index ab74da5..561c616 100644 --- a/rabbitmq-fmu/test/message_parser_test.cpp +++ b/rabbitmq-fmu/test/message_parser_test.cpp @@ -15,16 +15,16 @@ namespace { for (auto &it : dp.doubleValues) { - cout <<"Double " <, ScalarVariableBaseValue>& a1, const pair, ScalarVariableBaseValue>& a2) { // return false; // } @@ -98,7 +101,7 @@ namespace tla { this->startOffsetTime = startOffsetTime; } - void showValue(const char *prefix, FmuContainerCore::TimedScalarBasicValue val) { + static void showValue(const char *prefix, FmuContainerCore::TimedScalarBasicValue val) { cout << prefix << "Time: " << val.first.time_since_epoch().count() << " Value: "; switch (val.second.b.type) { @@ -151,12 +154,25 @@ namespace tla { return maxAge; } + static void showData(std::map currentData, const char *tag = "") { + cout << "Data: " << tag << " " << endl; + for (auto &pair: currentData) { + cout << "\tId: " << pair.first; + + showValue(" ", pair.second); + + + } + cout << endl; + } void show(const char *tag) { + date::sys_time valueTimeZero; cout << "------------------------------ INFO " << tag << "------------------------------" << endl; cout << "Max age: " << this->maxAge << endl; - cout << "StartTime: " << this->startOffsetTime << endl; - cout << "Lookahead ids: ["; + cout << "StartTime: " << this->startOffsetTime << " ( " + << (this->startOffsetTime - valueTimeZero).count() << " )" << endl; + cout << "Lookahead ids: [ "; for (auto &id:lookahead) { cout << id.first << "->" << id.second << " "; } @@ -179,15 +195,7 @@ namespace tla { } } - cout << "Data: " << endl; - for (auto &pair: currentData) { - cout << "\tId: " << pair.first; - - showValue(" ", pair.second); - - - } - cout << endl; + showData(currentData); // cout << "------------------------------------------------------------------" << endl; } }; @@ -197,6 +205,10 @@ namespace tla { std::map> lookahead; date::sys_time valueTimeZero; + if (verbose) { + cout << queueName << endl; + } + // cout << "Parsing " << queueName << endl; if (doc.HasMember(queueName)) { auto &lookaheadVal = doc[queueName]; @@ -210,13 +222,25 @@ namespace tla { unsigned int key = 0; keyStream >> key; - - cout << key << endl; + if (verbose) { + cout << "Key " << key << endl; + } // list list; + for (auto &val: itr->value.GetArray()) { - lookahead[key].push_front( - std::make_pair(valueTimeZero + std::chrono::milliseconds(val.GetInt()), val.GetInt())); + + if (val.IsArray() && val.GetInt() == 2) { + + if (verbose) { + cout << "\t(" << val.GetArray()[0].GetInt() << " , " << val.GetArray()[1].GetInt() << " )" + << endl; + } + + lookahead[key].push_back( + std::make_pair(valueTimeZero + std::chrono::milliseconds(val.GetArray()[0].GetInt()), + val.GetArray()[1].GetInt())); + } } // lookahead[key] = list; @@ -226,6 +250,57 @@ namespace tla { return lookahead; } + std::map + parseCurrentValues(Value &doc, const char *queueName) { + std::map currenntData; + date::sys_time valueTimeZero; + + if (verbose) { + cout << queueName << endl; + } + +// cout << "Parsing " << queueName << endl; + if (doc.HasMember(queueName)) { + auto &lookaheadVal = doc[queueName]; + + for (Value::ConstMemberIterator itr = lookaheadVal.MemberBegin(); + itr != lookaheadVal.MemberEnd(); ++itr) { + + auto keyStr = itr->name.GetString(); + + stringstream keyStream(keyStr); + + unsigned int key = 0; + keyStream >> key; + if (verbose) { + cout << "Key " << key << endl; + } +// list list; + auto &val = itr->value; + + + if (val.IsArray() && val.GetInt() == 2) { + + if (verbose) { + cout << "\t(" << val.GetArray()[0].GetInt() << " , " << val.GetArray()[1].GetInt() << " )" + << endl; + } + + auto f = std::make_pair(valueTimeZero + std::chrono::milliseconds(val.GetArray()[0].GetInt()), + ScalarVariableBaseValue((int) val.GetArray()[1].GetInt())); +// currenntData. +// currenntData.at(key).first = f.first; + currenntData.insert(std::make_pair(key, f)); + } + + + // lookahead[key] = list; + } + + } + return currenntData; + } + FmuContainerCoreTestProxy::State createState(Value &doc) { std::map lookahead; @@ -257,25 +332,35 @@ namespace tla { .maxAge=std::chrono::milliseconds(doc["maxAge"].GetInt()), .lookahead=lookahead, .incomingUnprocessed=parseQueueValues(doc, "incomingUnprocessed"), - .incomingLookahead=parseQueueValues(doc, "incomingLookahead") - + .incomingLookahead=parseQueueValues(doc, "incomingLookahead"), + .currentData=parseCurrentValues(doc, + "currentData"), + .startOffsetTime =valueTimeZero + std::chrono::milliseconds(doc["startOffsetTime"].GetInt()) }; return pre; } - bool check(FmuContainerCoreTestProxy &fcc, FmuContainerCoreTestProxy::State &post) { - if (post.currentData != fcc.getData()) { - cout << "Current state does not match" << endl; + bool check(FmuContainerCoreTestProxy &fcc, FmuContainerCoreTestProxy::State &post, bool blocked) { + if (!blocked && post.currentData != fcc.getData()) { + + if (verbose) { + cerr << "Current state does not match" << endl; + + FmuContainerCoreTestProxy::showData(fcc.getData(), "Actual"); + FmuContainerCoreTestProxy::showData(post.currentData, "Expected"); + } return false; - } else if (post.lookahead != fcc.getLookahead()) { - cout << "lookahead does not match" << endl; + }/* else if (post.lookahead != fcc.getLookahead()) { + cerr << "lookahead does not match" << endl; return false; - } else if (post.incomingLookahead != fcc.getIncomingLookahead()) { - cout << "incomingLookahead does not match" << endl; + }*//* else if (!blocked && post.incomingLookahead != fcc.getIncomingLookahead()) { + cerr << "incomingLookahead does not match" << endl; return false; - } else if (post.incomingUnprocessed != fcc.getIncomingUnprocessed()) { - cout << "incomingUnprocessed does not match" << endl; + }*/ else if (post.incomingUnprocessed != fcc.getIncomingUnprocessed()) { + if (verbose) { + cerr << "incomingUnprocessed does not match" << endl; + } return false; } return true; @@ -296,15 +381,20 @@ namespace tla { Value &pre = doc["pre"]; Value &post = doc["post"]; - cout << "\t## Meta:" << meta.GetString() << endl; - cout << "\t## Action: " << action.GetString() << endl; + if (verbose) { + cout << "\t## Meta:" << meta.GetString() << endl; + cout << "\t## Action: " << action.GetString() << endl; + } auto preState = createState(pre); auto postState = createState(post); FmuContainerCoreTestProxy fcc(preState); + fcc.setVerbose(verbose2); // cout << "######################## PRE ########################" << endl; - fcc.show("PRE "); + if (verbose) { + fcc.show("PRE "); + } cout << ">> " << action.GetString() << endl; @@ -312,19 +402,38 @@ namespace tla { if (string("dostep") == action.GetString()) { - //FIXME what time? - res = fcc.process(0); - } else if (string("initialize").compare(action.GetString()) == 0) { - res = fcc.initialize(); - } + //auto h = pre["H"].GetInt()+pre["node"].GetObject()["fmu_state_time"].GetInt()-pre["startOffsetTime"].GetInt();//(post["H"].GetInt()- + auto h = (post["node"].GetObject()["fmu_state_time"].GetInt() - + pre["node"].GetObject()["fmu_state_time"].GetInt()) + + (pre["node"].GetObject()["fmu_state_time"].GetInt() - pre["startOffsetTime"].GetInt()); - fcc.show("POST "); + if (verbose) { + cout << "STEP H " << h << endl; + } +// date::sys_time valueTimeZero; +// +// auto stepTime = valueTimeZero + std::chrono::milliseconds(h); - return post["fmuInitialized"].GetBool() == res; + res = fcc.process(h); + if (verbose) { + cout << "DoStep Result = " << res << endl; - return check(fcc, postState); + } + res = !post["blocked"].GetBool() == res; + } else if (string("initialize").compare(action.GetString()) == 0) { + res = fcc.initialize(); + if (verbose) { + cout << "Initiialized: " << res << endl; + } + //!blocked &&initialized + res = (!post["blocked"].GetBool() && post["initialized"].GetBool()) == res; + } + if (verbose) { + fcc.show("POST "); + } + return check(fcc, postState, post["blocked"].GetBool()) && res; } @@ -333,6 +442,7 @@ namespace tla { using namespace tla; + int main(int argc, char **argv) { cout << "tla" << endl; std::vector testFiles; @@ -344,34 +454,51 @@ int main(int argc, char **argv) { cout << " -h help" << endl; cout << " -s path to a folder containing *json test files" << endl; cout << " -t path to a single test *.json file" << endl; + cout << " -v verbose" << endl; + cout << " -vv more verbose" << endl; return 0; } + + verbose = input.cmdOptionExists("-v"); + verbose2 = input.cmdOptionExists("-vv"); + verbose = verbose || verbose2; + const std::string &filename = input.getCmdOption("-t"); if (!filename.empty()) { - cout << "Adding test: " << filename << endl; + if (verbose) { + cout << "Adding test: " << filename << endl; + } testFiles.push_back(filename); } const std::string &searchPath = input.getCmdOption("-s"); if (!searchPath.empty()) { - cout << "Searching for tests in path: " << searchPath << endl; + if (verbose) { + cout << "Searching for tests in path: " << searchPath << endl; + } for (const auto &entry : fs::directory_iterator(searchPath)) { auto fn = entry.path().generic_string(); if (fn.substr(fn.find_last_of(".") + 1) == "json") { - cout << "Adding test: " << fn << endl; + if (verbose) { + cout << "Adding test: " << fn << endl; + } testFiles.push_back(fn); } } } + if (verbose) { + cout << "Testing..." << endl; + } + int failures = 0; - cout << "Testing..." << endl; for (auto &path:testFiles) { - cout << "############################## " << path << " ##############################" << endl; - cout << "\t## Test" << path << endl; - + //cout << "############################## " << path << " ##############################" << endl; + if (verbose) { + cout << "\t## Test: " << path << endl; + } ifstream f(path.c_str()); if (!f.good()) { cout << "\t## " << " File does not exist" << endl; @@ -385,12 +512,26 @@ int main(int argc, char **argv) { Document d; d.Parse(str.c_str()); - if (processTest(d)) { - cout << "\t## " << " PASSED" << endl; + auto verdict = processTest(d); + + + if (!verbose) { + cout << path << " ->"; } else { - cout << "\t## " << " FAIL" << endl; + cout << "\t## "; } + + if (verdict) { + cout << " PASSED" << endl; + } else { + cout << " FAIL" << endl; + + failures++; + } + } + cout << "Summery Tests: " << testFiles.size() << " Failures: " << failures << endl; + return 0; } \ No newline at end of file diff --git a/server/publish.py b/server/publish.py index 0f7b23d..4c2dd64 100644 --- a/server/publish.py +++ b/server/publish.py @@ -2,29 +2,60 @@ import pika import json import datetime - +import time connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() -dt=datetime.datetime.strptime('2019-01-04T16:41:24+0200', "%Y-%m-%dT%H:%M:%S%z") +print("Declaring exchange") +channel.exchange_declare(exchange='fmi_digital_twin', exchange_type='direct') + +print("Creating queue") +result = channel.queue_declare(queue='', exclusive=True) +queue_name = result.method.queue + +channel.queue_bind(exchange='fmi_digital_twin', queue=queue_name, + routing_key='linefollower') + +print(' [*] Waiting for logs. To exit press CTRL+C') + + +def publish(): +# channel.stop_consuming() + dt=datetime.datetime.strptime('2019-01-04T16:41:24+0200', "%Y-%m-%dT%H:%M:%S%z") + + print(dt); + + msg = {} + msg['time']= dt.isoformat() + msg['level']=0 + + + for i in range(1,(10+1)*10): +# time.sleep(0.01) + channel.basic_publish(exchange='fmi_digital_twin', + routing_key='linefollower', + body=json.dumps(msg)) + print(" [x] Sent %s - relative time %s" % (json.dumps(msg),str(0.1*i))) + dt = dt + datetime.timedelta(seconds=0.1) + msg['time']= dt.isoformat() + msg['level']=msg['level']+1 + if msg['level'] > 2: + msg['level']=0 + + + +def callback(ch, method, properties, body): + print(" [x] %r" % body) + if "waiting for input data for simulation" in str(body): + publish() + -print(dt); -msg = {} -msg['time']= dt.isoformat() -msg['level']=0 +channel.basic_consume( + queue=queue_name, on_message_callback=callback, auto_ack=True) -for i in range(1,10000): - channel.basic_publish(exchange='fmi_digital_twin', - routing_key='linefollower', - body=json.dumps(msg)) - print(" [x] Sent %s" % json.dumps(msg)) - dt = dt + datetime.timedelta(seconds=0.1) - msg['time']= dt.isoformat() - msg['level']=msg['level']+1 - if msg['level'] > 2: - msg['level']=0 +channel.start_consuming() connection.close() From 57fb58b22d91d0ea6c6e1a74cbe0b4e4bb2cd965 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 14:12:49 +0200 Subject: [PATCH 06/13] only compile tla tester on mac --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e16f901..648055e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,9 @@ add_subdirectory(thirdparty/googletest) add_subdirectory(thirdparty/rabbitmq-c) add_subdirectory(thirdparty/date) add_subdirectory(rabbitmq-core) +if (APPLE) add_subdirectory(rabbitmq-tla-tester) +endif() add_subdirectory(rabbitmq-fmu) From 7f084cd5c135763eefd5d4e8af19766f4d50ca7d Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 14:13:19 +0200 Subject: [PATCH 07/13] fix for g++ struct init field order --- rabbitmq-core/test/FmuContainerCore_test.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rabbitmq-core/test/FmuContainerCore_test.cpp b/rabbitmq-core/test/FmuContainerCore_test.cpp index 4441e78..8cca1db 100644 --- a/rabbitmq-core/test/FmuContainerCore_test.cpp +++ b/rabbitmq-core/test/FmuContainerCore_test.cpp @@ -193,7 +193,8 @@ namespace { } void - checkStep(FmuContainerCoreTestProxy::State pre, FmuContainerCoreTestProxy::State post,double time, bool expectStepOk) { + checkStep(FmuContainerCoreTestProxy::State pre, FmuContainerCoreTestProxy::State post, double time, + bool expectStepOk) { FmuContainerCoreTestProxy c(pre); c.setVerbose(true); @@ -224,7 +225,6 @@ namespace { } - TEST(FmuContainerCore, Initialize1 ) { int sv1 = 1; @@ -242,11 +242,14 @@ namespace { FmuContainerCoreTestProxy::State post = { .maxAge=std::chrono::milliseconds(0), .lookahead={{sv1, 1}}, + .incomingUnprocessed={}, + .incomingLookahead={}, .currentData = {{sv1, std::make_pair(valueTimeZero + std::chrono::milliseconds(0), 1)}} }; + checkInitialize(pre, post, true); } @@ -355,6 +358,7 @@ namespace { .lookahead={{sv0, 1}, {sv1, 2}}, .incomingUnprocessed={{sv0, {V(2, 2)}}}, + .incomingLookahead={}, // .incomingLookahead={ // { sv0, {V(1, 1)}}, // { sv1, {V(2, 2)}} @@ -374,6 +378,8 @@ namespace { .maxAge=std::chrono::milliseconds(1), .lookahead={{sv0, 1}, {sv1, 2}}, + .incomingUnprocessed={}, + .incomingLookahead={}, // .incomingUnprocessed={{sv0, {V(2, 2)}}}, // .incomingLookahead={ // { sv0, {V(1, 1)}}, @@ -388,6 +394,7 @@ namespace { }; - checkStep(post, postDoStep,1,true); + + checkStep(post, postDoStep, 1, true); } } \ No newline at end of file From 6ddd530d980ce491716f1682486a1519fa87bd7f Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 14:22:10 +0200 Subject: [PATCH 08/13] added missing cmakelists file --- rabbitmq-core/CMakeLists.txt | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 rabbitmq-core/CMakeLists.txt diff --git a/rabbitmq-core/CMakeLists.txt b/rabbitmq-core/CMakeLists.txt new file mode 100644 index 0000000..79957c6 --- /dev/null +++ b/rabbitmq-core/CMakeLists.txt @@ -0,0 +1,74 @@ +cmake_minimum_required(VERSION 3.7.2) +project(rabbitmq-core CXX) + +enable_testing() +include(CheckCXXCompilerFlag) + +set(CMAKE_VERBOSE_MAKEFILE on) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +CHECK_CXX_COMPILER_FLAG("-std=c++17" COMPILER_SUPPORTS_CXX17) + +#if (COMPILER_SUPPORTS_CXX11) +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") +# +#else () +# message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.") +#endif () + + + + +include_directories(${gtest_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src) + + +file(GLOB_RECURSE CPP_FILES src/*.cpp) +file(GLOB C_FILES src/*.c) + +# Main +#add_executable(${PROJECT_NAME}-main ${C_FILES} ${CPP_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp) +#target_link_libraries(${PROJECT_NAME}-main XercesC::XercesC rabbitmq-static tz) + +# TLA tester +#add_executable(${PROJECT_NAME}-tla ${C_FILES} ${CPP_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/src/tla/TlaTester.cpp) +#target_link_libraries(${PROJECT_NAME}-tla XercesC::XercesC rabbitmq-static tz) + + +add_library(rabbitmq-core SHARED ${C_FILES} ${CPP_FILES}) +target_link_libraries(rabbitmq-core tz) +set_target_properties(rabbitmq-core + PROPERTIES POSITION_INDEPENDENT_CODE ON) +#set_target_properties(rabbitmq PROPERTIES PREFIX "") + +target_include_directories(rabbitmq-core INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/src) + +#################################### TESTS #################################### + +list(FILTER CPP_FILES EXCLUDE REGEX ".*src\\/main\\.cpp") + + + + +# ------------------------------- UNIT TESTS ---------------------------------- +file(GLOB CPP_TEST_FILES test/*.cpp) +list(FILTER CPP_TEST_FILES EXCLUDE REGEX ".*_it.cpp") + +add_executable(unit-test-${PROJECT_NAME} ${C_FILES} ${CPP_FILES} ${CPP_TEST_FILES}) +target_link_libraries(unit-test-${PROJECT_NAME} gtest_main tz) + +if (WIN32) + add_test(unit-test-${PROJECT_NAME} wine ${PROJECT_NAME} --gtest_output=xml:gtestresults.xml) +else () + add_test(NAME unit-test-${PROJECT_NAME} COMMAND unit-test-${PROJECT_NAME} --gtest_output=xml:gtestresults.xml) +endif () + +# ---------------------------- Integration TESTS ------------------------------ + +file(GLOB CPP_TEST_FILES test/*.cpp) +list(FILTER CPP_TEST_FILES INCLUDE REGEX ".*_it.cpp") + +add_executable(it-test-${PROJECT_NAME} ${C_FILES} ${CPP_FILES} ${CPP_TEST_FILES}) +target_link_libraries(it-test-${PROJECT_NAME} gtest_main tz) + + From 04548e358e9aead6ea0b79a58da493868449616b Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 15:04:53 +0200 Subject: [PATCH 09/13] change rabbitmq-core to library to static --- rabbitmq-core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rabbitmq-core/CMakeLists.txt b/rabbitmq-core/CMakeLists.txt index 79957c6..b2731d0 100644 --- a/rabbitmq-core/CMakeLists.txt +++ b/rabbitmq-core/CMakeLists.txt @@ -35,7 +35,7 @@ file(GLOB C_FILES src/*.c) #target_link_libraries(${PROJECT_NAME}-tla XercesC::XercesC rabbitmq-static tz) -add_library(rabbitmq-core SHARED ${C_FILES} ${CPP_FILES}) +add_library(rabbitmq-core STATIC ${C_FILES} ${CPP_FILES}) target_link_libraries(rabbitmq-core tz) set_target_properties(rabbitmq-core PROPERTIES POSITION_INDEPENDENT_CODE ON) From 0bc12a0c26823cd452b8d2480476fc417f1a25a6 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 15:05:12 +0200 Subject: [PATCH 10/13] removed old script --- scripts/build_linux-64.sh | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100755 scripts/build_linux-64.sh diff --git a/scripts/build_linux-64.sh b/scripts/build_linux-64.sh deleted file mode 100755 index e63691e..0000000 --- a/scripts/build_linux-64.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -e - -echo Creating env - -docker run --rm dockcross/linux-x64:latest >./linux-x64-dockcross -chmod +x ./linux-x64-dockcross - -echo Running CMake - -./linux-x64-dockcross cmake -Bbuild/linux-x64 -H. - -echo Compiling - -./linux-x64-dockcross make -Cbuild/linux-x64 From ea54507411dee4c92cac64a3b19038fdf742ee41 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 15:05:23 +0200 Subject: [PATCH 11/13] moved all build artifacts to build --- CMakeLists.txt | 4 ++-- scripts/build.sh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 648055e..82e37ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ target_architecture(arch) message(${arch}) if (NOT DEFINED THIRD_PARTY_LIBRARIES_ROOT) - set(THIRD_PARTY_LIBRARIES_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/external") + set(THIRD_PARTY_LIBRARIES_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/build/external") if (UNIX) if (APPLE) set(THIRD_PARTY_LIBRARIES_ROOT ${THIRD_PARTY_LIBRARIES_ROOT}/darwin) @@ -45,7 +45,7 @@ if (NOT DEFINED THIRD_PARTY_LIBRARIES_ROOT) ENDIF () set(THIRD_PARTY_LIBRARIES_ROOT ${THIRD_PARTY_LIBRARIES_ROOT}-${arch}) ELSEIF (WIN32) - set(XercesC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/external/win-${arch}/cmake") + set(XercesC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/build/external/win-${arch}/cmake") ENDIF () endif () diff --git a/scripts/build.sh b/scripts/build.sh index 82c77ca..86aacd2 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -35,17 +35,17 @@ build_xercersc() $1 make -C$2 install } -if [ ! -d thirdparty/external/$install_name ] +if [ ! -d build/external/$install_name ] then -build_xercersc $script $working_dir/xerces-c thirdparty/external/$install_name +build_xercersc $script $working_dir/xerces-c build/external/$install_name else echo "Dependency already generated" fi echo Running CMake - +rm -f thirdparty/rabbitmq-c/rabbitmq-c/librabbitmq/config.h ./$script cmake -B$working_dir -H. echo Compiling From cfd2b30d5bf1c2905de54b6644510041e4cb1b06 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 15:32:41 +0200 Subject: [PATCH 12/13] removed use of 17 fs::directory_iterator --- CMakeLists.txt | 2 +- rabbitmq-tla-tester/src/TlaTester.cpp | 35 +++++++++++++++------------ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82e37ac..b427250 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,7 @@ add_subdirectory(thirdparty/googletest) add_subdirectory(thirdparty/rabbitmq-c) add_subdirectory(thirdparty/date) add_subdirectory(rabbitmq-core) -if (APPLE) +if (UNIX) add_subdirectory(rabbitmq-tla-tester) endif() add_subdirectory(rabbitmq-fmu) diff --git a/rabbitmq-tla-tester/src/TlaTester.cpp b/rabbitmq-tla-tester/src/TlaTester.cpp index 43f78a0..4f73e55 100644 --- a/rabbitmq-tla-tester/src/TlaTester.cpp +++ b/rabbitmq-tla-tester/src/TlaTester.cpp @@ -5,27 +5,14 @@ #include #include - -#include - - #include - #include "rapidjson/document.h" -#include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" - -#include - #include "FmuContainerCore.h" - - -#include - #include "date/date.h" +#include -namespace fs = std::filesystem; using namespace std; using namespace rapidjson; @@ -476,7 +463,23 @@ int main(int argc, char **argv) { if (verbose) { cout << "Searching for tests in path: " << searchPath << endl; } - for (const auto &entry : fs::directory_iterator(searchPath)) { + + DIR *dir; + struct dirent *ent; + if ((dir = opendir (searchPath.c_str())) != NULL) { + /* print all the files and directories within directory */ + while ((ent = readdir (dir)) != NULL) { +// printf ("%s\n", ent->d_name); + testFiles.push_back(ent->d_name); + } + closedir (dir); + } else { + /* could not open directory */ + perror (""); + return EXIT_FAILURE; + } + + /* for (const auto &entry : fs::directory_iterator(searchPath)) { auto fn = entry.path().generic_string(); if (fn.substr(fn.find_last_of(".") + 1) == "json") { if (verbose) { @@ -485,7 +488,7 @@ int main(int argc, char **argv) { testFiles.push_back(fn); } - } + }*/ } if (verbose) { cout << "Testing..." << endl; From 43e2e61483e5aa8eaf7ff0d14844417938167e5a Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 29 May 2020 15:44:28 +0200 Subject: [PATCH 13/13] adding win32 to build package --- Jenkinsfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 17f82fc..5e02367 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -43,11 +43,16 @@ pipeline { } } - stage('Xcompile win64') { + stage('Xcompile win32') { steps { - sh "./scripts/win64_build.sh" + sh "./scripts/win32_build.sh" } } + stage('Xcompile win64') { + steps { + sh "./scripts/win64_build.sh" + } + } } }