mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
add new format-type for OPAL-RT's AsyncIP example
This commit is contained in:
parent
ca533ac8e0
commit
d4f74a5a51
8 changed files with 256 additions and 33 deletions
93
include/villas/formats/opal_asyncip.hpp
Normal file
93
include/villas/formats/opal_asyncip.hpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
/** A custom format for OPAL-RTs AsyncIP example
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2021, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @license GNU General Public License (version 3)
|
||||
*
|
||||
* VILLASnode
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <villas/format.hpp>
|
||||
|
||||
namespace villas {
|
||||
namespace node {
|
||||
|
||||
/* Forward declarations. */
|
||||
struct Sample;
|
||||
|
||||
class OpalAsyncIPFormat : public BinaryFormat {
|
||||
|
||||
protected:
|
||||
const int MAXSIZE = 64;
|
||||
|
||||
struct Payload {
|
||||
int16_t dev_id; // (2 bytes) Sender device ID
|
||||
int32_t msg_id; // (4 bytes) Message ID
|
||||
int16_t msg_len; // (2 bytes) Message length (data only)
|
||||
double data[]; // Up to MAXSIZE doubles (8 bytes each)
|
||||
} __attribute__((packed));
|
||||
|
||||
int16_t dev_id;
|
||||
|
||||
public:
|
||||
OpalAsyncIPFormat(int fl, uint8_t did = 0) :
|
||||
BinaryFormat(fl),
|
||||
dev_id(did)
|
||||
{ }
|
||||
|
||||
virtual
|
||||
int sscan(const char *buf, size_t len, size_t *rbytes, struct Sample * const smps[], unsigned cnt);
|
||||
virtual
|
||||
int sprint(char *buf, size_t len, size_t *wbytes, const struct Sample * const smps[], unsigned cnt);
|
||||
|
||||
virtual
|
||||
void parse(json_t *json);
|
||||
};
|
||||
|
||||
|
||||
class OpalAsyncIPFormatPlugin : public FormatFactory {
|
||||
|
||||
public:
|
||||
using FormatFactory::FormatFactory;
|
||||
|
||||
virtual
|
||||
Format * make()
|
||||
{
|
||||
return new OpalAsyncIPFormat((int) SampleFlags::HAS_TS_ORIGIN | (int) SampleFlags::HAS_SEQUENCE | (int) SampleFlags::HAS_DATA);
|
||||
}
|
||||
|
||||
/// Get plugin name
|
||||
virtual
|
||||
std::string getName() const
|
||||
{
|
||||
return "opal.asyncip";
|
||||
}
|
||||
|
||||
/// Get plugin description
|
||||
virtual
|
||||
std::string getDescription() const
|
||||
{
|
||||
return "OPAL-RTs AsyncIP example format";
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace node */
|
||||
} /* namespace villas */
|
|
@ -63,7 +63,7 @@ union SignalData {
|
|||
}
|
||||
|
||||
/** Convert signal data from one description/format to another. */
|
||||
void cast(enum SignalType type, enum SignalType to);
|
||||
SignalData cast(enum SignalType type, enum SignalType to) const;
|
||||
|
||||
/** Set data from double */
|
||||
void set(enum SignalType type, double val);
|
||||
|
|
|
@ -68,6 +68,7 @@ list(APPEND FORMAT_SRC
|
|||
json.cpp
|
||||
line.cpp
|
||||
msg.cpp
|
||||
opal_asyncip.cpp
|
||||
raw.cpp
|
||||
value.cpp
|
||||
villas_binary.cpp
|
||||
|
|
132
lib/formats/opal_asyncip.cpp
Normal file
132
lib/formats/opal_asyncip.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
/** A custom format for OPAL-RTs AsyncIP example
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2021, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @license GNU General Public License (version 3)
|
||||
*
|
||||
* VILLASnode
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
#include <villas/formats/opal_asyncip.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/sample.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
using namespace villas;
|
||||
using namespace villas::node;
|
||||
|
||||
int OpalAsyncIPFormat::sprint(char *buf, size_t len, size_t *wbytes, const struct Sample * const smps[], unsigned cnt)
|
||||
{
|
||||
unsigned i;
|
||||
auto *ptr = buf;
|
||||
ssize_t slen = len;
|
||||
|
||||
for (i = 0; i < cnt && ptr - buf < slen; i++) {
|
||||
auto *pl = (struct Payload *) ptr;
|
||||
auto *smp = smps[i];
|
||||
|
||||
auto wlen = smp->length * sizeof(double) + sizeof(struct Payload);
|
||||
if (wlen > len)
|
||||
return -1;
|
||||
|
||||
pl->dev_id = htole16(dev_id);
|
||||
pl->msg_id = htole32(smp->sequence);
|
||||
pl->msg_len = htole16(smp->length * sizeof(double));
|
||||
|
||||
for (unsigned j = 0; j < smp->length; j++) {
|
||||
auto sig = smp->signals->getByIndex(j);
|
||||
auto d = smp->data[j];
|
||||
|
||||
d = d.cast(sig->type, SignalType::FLOAT);
|
||||
d.i = htole64(d.i);
|
||||
|
||||
pl->data[j] = d.f;
|
||||
}
|
||||
|
||||
ptr += wlen;
|
||||
}
|
||||
|
||||
if (wbytes)
|
||||
*wbytes = ptr - buf;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int OpalAsyncIPFormat::sscan(const char *buf, size_t len, size_t *rbytes, struct Sample * const smps[], unsigned cnt)
|
||||
{
|
||||
unsigned i;
|
||||
auto *ptr = buf;
|
||||
ssize_t slen = len;
|
||||
|
||||
if (len % 8 != 0)
|
||||
return -1; /* Packet size is invalid: Must be multiple of 4 bytes */
|
||||
|
||||
for (i = 0; i < cnt && ptr - buf + sizeof(struct Payload) < slen; i++) {
|
||||
if (len < 8)
|
||||
return -1; /* Packet size is invalid: Must be multiple of 4 bytes */
|
||||
|
||||
auto *pl = (struct Payload *) ptr;
|
||||
auto *smp = smps[i];
|
||||
|
||||
auto rlen = le16toh(pl->msg_len);
|
||||
if (len < ptr - buf + rlen + sizeof(struct Payload))
|
||||
return -2;
|
||||
|
||||
smp->sequence = le32toh(pl->msg_id);
|
||||
smp->length = rlen / sizeof(double);
|
||||
smp->flags = (int) SampleFlags::HAS_SEQUENCE;
|
||||
smp->signals = signals;
|
||||
|
||||
for (unsigned j = 0; j < MIN(smp->length, smp->capacity); j++) {
|
||||
auto sig = signals->getByIndex(j);
|
||||
|
||||
SignalData d;
|
||||
d.f = pl->data[j];
|
||||
d.i = le64toh(d.i);
|
||||
|
||||
smp->data[j] = d.cast(SignalType::FLOAT, sig->type);
|
||||
}
|
||||
|
||||
ptr += rlen + sizeof(struct Payload);
|
||||
}
|
||||
|
||||
if (rbytes)
|
||||
*rbytes = ptr - buf;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void OpalAsyncIPFormat::parse(json_t *json)
|
||||
{
|
||||
int ret;
|
||||
json_error_t err;
|
||||
int did = -1;
|
||||
|
||||
ret = json_unpack_ex(json, &err, 0, "{ s?: i }",
|
||||
"dev_id", &did
|
||||
);
|
||||
if (ret)
|
||||
throw ConfigError(json, err, "node-config-format-opal-asyncip", "Failed to parse format configuration");
|
||||
|
||||
if (did >= 0)
|
||||
dev_id = did;
|
||||
|
||||
Format::parse(json);
|
||||
}
|
||||
|
||||
static OpalAsyncIPFormatPlugin p;
|
|
@ -109,7 +109,7 @@ public:
|
|||
auto orig_sig = smp->signals->getByIndex(index);
|
||||
auto new_sig = signals->getByIndex(index);
|
||||
|
||||
smp->data[index].cast(orig_sig->type, new_sig->type);
|
||||
smp->data[index] = smp->data[index].cast(orig_sig->type, new_sig->type);
|
||||
}
|
||||
|
||||
return Reason::OK;
|
||||
|
|
|
@ -164,7 +164,7 @@ void lua_tosignaldata(lua_State *L, union SignalData *data, enum SignalType targ
|
|||
return;
|
||||
}
|
||||
|
||||
data->cast(type, targetType);
|
||||
*data = data->cast(type, targetType);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
|
@ -53,98 +53,95 @@ void SignalData::set(enum SignalType type, double val)
|
|||
}
|
||||
}
|
||||
|
||||
void SignalData::cast(enum SignalType from, enum SignalType to)
|
||||
SignalData SignalData::cast(enum SignalType from, enum SignalType to) const
|
||||
{
|
||||
if (from == to) /* Nothing to do */
|
||||
return;
|
||||
SignalData n = *this;
|
||||
|
||||
switch (to) {
|
||||
case SignalType::BOOLEAN:
|
||||
switch(from) {
|
||||
case SignalType::BOOLEAN:
|
||||
break;
|
||||
|
||||
case SignalType::INTEGER:
|
||||
this->b = this->i;
|
||||
n.b = this->i;
|
||||
break;
|
||||
|
||||
case SignalType::FLOAT:
|
||||
this->b = this->f;
|
||||
n.b = this->f;
|
||||
break;
|
||||
|
||||
case SignalType::COMPLEX:
|
||||
this->b = std::real(this->z);
|
||||
n.b = std::real(this->z);
|
||||
break;
|
||||
|
||||
default: { }
|
||||
case SignalType::INVALID:
|
||||
case SignalType::BOOLEAN:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SignalType::INTEGER:
|
||||
switch(from) {
|
||||
case SignalType::BOOLEAN:
|
||||
this->i = this->b;
|
||||
break;
|
||||
|
||||
case SignalType::INTEGER:
|
||||
n.i = this->b;
|
||||
break;
|
||||
|
||||
case SignalType::FLOAT:
|
||||
this->i = this->f;
|
||||
n.i = this->f;
|
||||
break;
|
||||
|
||||
case SignalType::COMPLEX:
|
||||
this->i = std::real(this->z);
|
||||
n.i = std::real(this->z);
|
||||
break;
|
||||
|
||||
default: { }
|
||||
case SignalType::INVALID:
|
||||
case SignalType::INTEGER:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SignalType::FLOAT:
|
||||
switch(from) {
|
||||
case SignalType::BOOLEAN:
|
||||
this->f = this->b;
|
||||
n.f = this->b;
|
||||
break;
|
||||
|
||||
case SignalType::INTEGER:
|
||||
this->f = this->i;
|
||||
break;
|
||||
|
||||
case SignalType::FLOAT:
|
||||
n.f = this->i;
|
||||
break;
|
||||
|
||||
case SignalType::COMPLEX:
|
||||
this->f = std::real(this->z);
|
||||
n.f = std::real(this->z);
|
||||
break;
|
||||
|
||||
default: { }
|
||||
case SignalType::INVALID:
|
||||
case SignalType::FLOAT:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SignalType::COMPLEX:
|
||||
switch(from) {
|
||||
case SignalType::BOOLEAN:
|
||||
this->z = this->b;
|
||||
n.z = this->b;
|
||||
break;
|
||||
|
||||
case SignalType::INTEGER:
|
||||
this->z = this->i;
|
||||
n.z = this->i;
|
||||
break;
|
||||
|
||||
case SignalType::FLOAT:
|
||||
this->z = this->f;
|
||||
n.z = this->f;
|
||||
break;
|
||||
|
||||
case SignalType::INVALID:
|
||||
case SignalType::COMPLEX:
|
||||
break;
|
||||
|
||||
default: { }
|
||||
}
|
||||
break;
|
||||
|
||||
default: { }
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int SignalData::parseString(enum SignalType type, const char *ptr, char **end)
|
||||
|
|
|
@ -33,7 +33,7 @@ function finish {
|
|||
}
|
||||
trap finish EXIT
|
||||
|
||||
FORMATS="villas.human csv tsv json"
|
||||
FORMATS="villas.human csv tsv json opal.asyncip"
|
||||
|
||||
villas signal -v5 -n -l20 mixed > input.dat
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue