6 #include <dpsim-villas/InterfaceVillasQueueless.h>
7 #include <dpsim-villas/InterfaceWorkerVillas.h>
9 #include <spdlog/spdlog.h>
11 #include <villas/node.h>
12 #include <villas/node/memory.hpp>
13 #include <villas/node/memory_type.hpp>
14 #include <villas/path.hpp>
15 #include <villas/signal_type.hpp>
18 using namespace villas;
22 InterfaceVillasQueueless::InterfaceVillasQueueless(
23 const String &nodeConfig,
const String &name,
24 spdlog::level::level_enum logLevel)
25 :
Interface(name, logLevel), mNodeConfig(nodeConfig), mNode(nullptr),
26 mSamplePool(), mSequenceToDpsim(0), mSequenceFromDpsim(0) {}
28 void InterfaceVillasQueueless::createNode() {
29 if (villas::node::memory::init(100) != 0) {
30 SPDLOG_LOGGER_ERROR(mLog,
"Error: Failed to initialize memory subsystem!");
34 json_t *config = json_loads(mNodeConfig.c_str(), 0, &error);
35 if (config ==
nullptr) {
36 SPDLOG_LOGGER_ERROR(mLog,
"Error: Failed to parse node config! Error: {}",
38 throw JsonError(config, error);
41 const json_t *nodeType = json_object_get(config,
"type");
42 if (nodeType ==
nullptr) {
43 SPDLOG_LOGGER_ERROR(mLog,
"Error: Node config does not contain type-key!");
46 String nodeTypeString = json_string_value(nodeType);
48 mNode = node::NodeFactory::make(nodeTypeString);
51 ret = mNode->parse(config);
53 SPDLOG_LOGGER_ERROR(mLog,
54 "Error: Node in InterfaceVillas failed to parse "
55 "config. Parse returned code {}",
63 "Error: Node in InterfaceVillas failed check. Check returned code {}",
67 struct villas::node::memory::Type *pool_mt = &villas::node::memory::heap;
68 ret = node::pool_init(&mSamplePool, 16,
69 sizeof(node::Sample) + SAMPLE_DATA_LENGTH(64), pool_mt);
71 SPDLOG_LOGGER_ERROR(mLog,
72 "Error: InterfaceVillas failed to init sample pool. "
73 "pool_init returned code {}",
78 ret = mNode->prepare();
80 SPDLOG_LOGGER_ERROR(mLog,
81 "Error: Node in InterfaceVillas failed to prepare. "
82 "Prepare returned code {}",
86 SPDLOG_LOGGER_INFO(mLog,
"Node: {}", mNode->getNameFull());
89 static node::SignalType stdTypeToNodeType(
const std::type_info &type) {
90 if (type ==
typeid(Real)) {
91 return node::SignalType::FLOAT;
92 }
else if (type ==
typeid(Int)) {
93 return node::SignalType::INTEGER;
94 }
else if (type ==
typeid(Bool)) {
95 return node::SignalType::BOOLEAN;
96 }
else if (type ==
typeid(Complex)) {
97 return node::SignalType::COMPLEX;
99 return node::SignalType::INVALID;
103 void InterfaceVillasQueueless::createSignals() {
104 mNode->out.path =
new node::Path();
105 mNode->out.path->signals = std::make_shared<node::SignalList>();
106 node::SignalList::Ptr nodeOutputSignals =
107 mNode->out.path->getOutputSignals(
false);
108 nodeOutputSignals->clear();
109 unsigned int idx = 0;
110 for (
const auto &[attr,
id] : mExportAttrsDpsim) {
112 nodeOutputSignals->push_back(
113 std::make_shared<node::Signal>(
"",
"", node::SignalType::INVALID));
116 nodeOutputSignals->push_back(std::make_shared<node::Signal>(
117 "",
"", stdTypeToNodeType(attr->getType())));
120 node::SignalList::Ptr nodeInputSignals = mNode->getInputSignals(
true);
121 if (nodeInputSignals ==
nullptr) {
122 nodeInputSignals = std::make_shared<node::SignalList>();
124 nodeInputSignals->clear();
127 for (
const auto &[attr,
id, blockOnRead, syncOnSimulationStart] :
130 nodeInputSignals->push_back(
131 std::make_shared<node::Signal>(
"",
"", node::SignalType::INVALID));
134 nodeInputSignals->push_back(std::make_shared<node::Signal>(
135 "",
"", stdTypeToNodeType(attr->getType())));
140 void InterfaceVillasQueueless::open() {
145 mNode->getFactory()->start(
nullptr);
147 auto ret = mNode->start();
149 SPDLOG_LOGGER_ERROR(mLog,
150 "Fatal error: failed to start node in InterfaceVillas. "
151 "Start returned code {}",
157 mSequenceFromDpsim = 0;
158 mSequenceToDpsim = 0;
161 void InterfaceVillasQueueless::close() {
162 SPDLOG_LOGGER_INFO(mLog,
"Closing InterfaceVillas...");
163 int ret = mNode->stop();
167 "Error: failed to stop node in InterfaceVillas. Stop returned code {}",
172 ret = node::pool_destroy(&mSamplePool);
174 SPDLOG_LOGGER_ERROR(mLog,
175 "Error: failed to destroy SamplePool in "
176 "InterfaceVillas. pool_destroy returned code {}",
181 mNode->getFactory()->stop();
187 CPS::Task::List InterfaceVillasQueueless::getTasks() {
188 auto tasks = CPS::Task::List();
189 if (!mImportAttrsDpsim.empty()) {
190 tasks.push_back(std::make_shared<InterfaceVillasQueueless::PreStep>(*
this));
192 if (!mExportAttrsDpsim.empty()) {
194 std::make_shared<InterfaceVillasQueueless::PostStep>(*
this));
199 void InterfaceVillasQueueless::PreStep::execute(Real time, Int timeStepCount) {
200 auto seqnum = mIntf.readFromVillas();
201 static size_t overrunCounter = 0;
202 if (seqnum != mIntf.mSequenceToDpsim + 1) {
205 if (overrunCounter > 10000) {
206 SPDLOG_LOGGER_WARN(mIntf.mLog,
"{} Overrun(s) detected!", overrunCounter);
209 mIntf.mSequenceToDpsim = seqnum;
212 Int InterfaceVillasQueueless::readFromVillas() {
213 node::Sample *sample =
nullptr;
216 if (mImportAttrsDpsim.size() == 0) {
220 sample = node::sample_alloc(&mSamplePool);
223 ret = mNode->read(&sample, 1);
225 SPDLOG_LOGGER_ERROR(mLog,
226 "Fatal error: failed to read sample from "
227 "InterfaceVillas. Read returned code {}",
231 }
else if (ret == 0) {
232 SPDLOG_LOGGER_WARN(mLog,
233 "InterfaceVillas read returned 0. Retrying...");
237 if (sample->length != mImportAttrsDpsim.size()) {
238 SPDLOG_LOGGER_ERROR(mLog,
239 "Error: Received Sample length ({}) does not match "
240 "configured attributes length ({})",
241 sample->length, mImportAttrsDpsim.size());
243 "Received Sample length does not match configured attributes length");
246 for (
size_t i = 0; i < mImportAttrsDpsim.size(); i++) {
247 auto attr = std::get<0>(mImportAttrsDpsim[i]);
248 if (attr->getType() ==
typeid(Real)) {
250 std::dynamic_pointer_cast<Attribute<Real>>(attr.getPtr());
251 attrReal->set(sample->data[i].f);
252 }
else if (attr->getType() ==
typeid(Int)) {
253 auto attrInt = std::dynamic_pointer_cast<Attribute<Int>>(attr.getPtr());
254 attrInt->set(sample->data[i].i);
256 seqnum = sample->data[i].i;
258 }
else if (attr->getType() ==
typeid(Bool)) {
260 std::dynamic_pointer_cast<Attribute<Bool>>(attr.getPtr());
261 attrBool->set(sample->data[i].b);
262 }
else if (attr->getType() ==
typeid(Complex)) {
264 std::dynamic_pointer_cast<Attribute<Complex>>(attr.getPtr());
266 Complex(sample->data[i].z.real(), sample->data[i].z.imag()));
268 SPDLOG_LOGGER_ERROR(mLog,
"Error: Unsupported attribute type!");
269 throw RuntimeError(
"Unsupported attribute type!");
273 sample_decref(sample);
274 }
catch (
const std::exception &) {
276 sample_decref(sample);
283 void InterfaceVillasQueueless::PostStep::execute(Real time, Int timeStepCount) {
284 mIntf.writeToVillas();
287 void InterfaceVillasQueueless::writeToVillas() {
288 if (mExportAttrsDpsim.size() == 0) {
291 node::Sample *sample =
nullptr;
294 sample = node::sample_alloc(&mSamplePool);
295 if (sample ==
nullptr) {
296 SPDLOG_LOGGER_ERROR(mLog,
"InterfaceVillas could not allocate a new "
297 "sample! Not sending any data!");
301 sample->signals = mNode->getOutputSignals(
false);
303 for (
size_t i = 0; i < mExportAttrsDpsim.size(); i++) {
304 auto attr = std::get<0>(mExportAttrsDpsim[i]);
305 if (attr->getType() ==
typeid(Real)) {
307 std::dynamic_pointer_cast<Attribute<Real>>(attr.getPtr());
308 sample->data[i].f = attrReal->get();
309 }
else if (attr->getType() ==
typeid(Int)) {
310 auto attrInt = std::dynamic_pointer_cast<Attribute<Int>>(attr.getPtr());
311 sample->data[i].i = attrInt->get();
312 }
else if (attr->getType() ==
typeid(Bool)) {
314 std::dynamic_pointer_cast<Attribute<Bool>>(attr.getPtr());
315 sample->data[i].b = attrBool->get();
316 }
else if (attr->getType() ==
typeid(Complex)) {
318 std::dynamic_pointer_cast<Attribute<Complex>>(attr.getPtr());
319 sample->data[i].z = std::complex<float>(attrComplex->get().real(),
320 attrComplex->get().imag());
322 SPDLOG_LOGGER_ERROR(mLog,
"Error: Unsupported attribute type!");
323 throw RuntimeError(
"Unsupported attribute type!");
327 sample->length = mExportAttrsDpsim.size();
328 sample->sequence = mSequenceFromDpsim++;
329 sample->flags |= (int)villas::node::SampleFlags::HAS_SEQUENCE;
330 sample->flags |= (int)villas::node::SampleFlags::HAS_DATA;
331 clock_gettime(CLOCK_REALTIME, &sample->ts.origin);
332 sample->flags |= (int)villas::node::SampleFlags::HAS_TS_ORIGIN;
335 ret = mNode->write(&sample, 1);
338 SPDLOG_LOGGER_ERROR(mLog,
339 "Failed to write samples to InterfaceVillas. Write "
343 sample_decref(sample);
344 }
catch (
const std::exception &) {
345 sample_decref(sample);
350 "Failed to write samples to InterfaceVillas. Write returned code {}",
359 mSequenceToDpsim = this->readFromVillas();
362 void InterfaceVillasQueueless::syncExports() {
364 this->writeToVillas();
367 void InterfaceVillasQueueless::printVillasSignals()
const {
368 SPDLOG_LOGGER_INFO(mLog,
"Export signals:");
369 for (
const auto &signal : *mNode->getOutputSignals(
true)) {
370 SPDLOG_LOGGER_INFO(mLog,
"Name: {}, Unit: {}, Type: {}", signal->name,
371 signal->unit, node::signalTypeToString(signal->type));
374 SPDLOG_LOGGER_INFO(mLog,
"Import signals:");
375 for (
const auto &signal : *mNode->getInputSignals(
true)) {
376 SPDLOG_LOGGER_INFO(mLog,
"Name: {}, Unit: {}, Type: {}", signal->name,
377 signal->unit, node::signalTypeToString(signal->type));
virtual void syncImports() override
Function called by the Simulation to perform interface synchronization.