Skip to content

Commit

Permalink
node-c37.118: protocol parser
Browse files Browse the repository at this point in the history
  • Loading branch information
pjungkamp committed Jun 14, 2023
1 parent 530e189 commit 4ae7a31
Show file tree
Hide file tree
Showing 8 changed files with 1,193 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ cmake_dependent_option(WITH_TOOLS "Build auxilary tools"
cmake_dependent_option(WITH_WEB "Build with internal webserver" ON "LIBWEBSOCKETS_FOUND" OFF)

cmake_dependent_option(WITH_NODE_AMQP "Build with amqp node-type" ON "RABBITMQ_C_FOUND" OFF)
cmake_dependent_option(WITH_NODE_C37_118 "Build with c37.118 node-type" ON "" OFF)
cmake_dependent_option(WITH_NODE_CAN "Build with can node-type" ON "" OFF)
cmake_dependent_option(WITH_NODE_COMEDI "Build with comedi node-type" ON "COMEDILIB_FOUND" OFF)
cmake_dependent_option(WITH_NODE_ETHERCAT "Build with ethercat node-type" ON "ETHERLAB_FOUND;NOT WITHOUT_GPL" OFF)
Expand Down Expand Up @@ -270,6 +271,7 @@ add_feature_info(TOOLS WITH_TOOLS "Build auxil
add_feature_info(WEB WITH_WEB "Build with internal webserver")

add_feature_info(NODE_AMQP WITH_NODE_AMQP "Build with amqp node-type")
add_feature_info(NODE_C37_118 WITH_NODE_C37_118 "Build with c37.118 node-type")
add_feature_info(NODE_CAN WITH_NODE_CAN "Build with can node-type")
add_feature_info(NODE_COMEDI WITH_NODE_COMEDI "Build with comedi node-type")
add_feature_info(NODE_ETHERCAT WITH_NODE_ETHERCAT "Build with ethercat node-type")
Expand Down
48 changes: 48 additions & 0 deletions include/villas/nodes/c37_118.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @file
* @author Philipp Jungkamp <[email protected]>
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
* @license Apache 2.0
*********************************************************************************/

#pragma once

#include <string>
#include <optional>
#include <villas/nodes/c37_118/parser.hpp>
#include <villas/node/config.hpp>
#include <villas/node.hpp>
#include <villas/signal.hpp>

namespace villas {
namespace node {
namespace c37_118 {

class C37_118 : public Node {
protected:
struct Input {
std::string address;
} input;

virtual
int _read(struct Sample *smps[], unsigned cnt) override;

public:
C37_118(const std::string &name = "");

virtual
~C37_118() override;

virtual
int parse(json_t *json, const uuid_t sn_uuid) override;

virtual
int start() override;

virtual
int stop() override;
};

} /* namespace c37_118 */
} /* namespace node */
} /* namespace villas */
180 changes: 180 additions & 0 deletions include/villas/nodes/c37_118/parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/**
* @file
* @author Philipp Jungkamp <[email protected]>
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
* @license Apache 2.0
*********************************************************************************/

#pragma once

#include <optional>
#include <cstring>
#include <villas/nodes/c37_118/types.hpp>

namespace villas {
namespace node {
namespace c37_118 {
namespace parser {

using Result = std::intptr_t;

enum class Status : Result {
Ok = 0,
MissingBytes,
MissingConfig,
InvalidValue,
InvalidChecksum,
InvalidSlice,
Other,
};

template <typename T = Result>
Result ok(T t = 0);
Result error(Status);
Status status(Result);
bool is_ok(Result);

// context used to assemble and disassemble data frames with the information contained in config structs
class Context {
public:
using Config = std::variant<types::Config2 /*, struct Config3 */>;
Config config;

Context(Config config);
void reset();
void next_pmu();
uint16_t num_pmu() const;
uint16_t format() const;
uint16_t phnmr() const;
uint16_t annmr() const;
uint16_t dgnmr() const;

private:
size_t pmu_index;
};

// placeholder type allowing deferred assignment to a field during serialization
template <typename T>
class Placeholder;

class Parser {
public:
std::optional<Context> context;

using ssize_t = std::ptrdiff_t;

Parser() = default;
Parser(unsigned char *buffer, size_t length, std::optional<Context::Config> config = std::nullopt);
Result slice(ssize_t from, size_t length);
Result require(size_t length);
Parser subparser(unsigned char *buffer, size_t length);
size_t remaining() const;

template <typename T>
Result copy_from(T const *arg, size_t length = sizeof(T));

template <typename T>
Result copy_to(T *arg, size_t length = sizeof(T));

template <typename Arg1, typename Arg2, typename... Args>
Result deserialize(Arg1 *arg1, Arg2 *arg2, Args *...args);

Result deserialize(uint16_t *i);
Result deserialize(uint32_t *i);
Result deserialize(int16_t *i);
Result deserialize(int32_t *i);
Result deserialize(float *f);

template <typename T>
Result deserialize(std::vector<T> *v);

template <typename T, size_t N>
Result deserialize(std::array<T, N> *a);

template <typename Format>
Result deserialize(types::Rectangular<Format> *r);

template <typename Format>
Result deserialize(types::Polar<Format> *r);

Result deserialize(types::Phasor *p);
Result deserialize(types::Analog *a);
Result deserialize(types::Freq *f);
Result deserialize(types::PmuData *d);
Result deserialize(types::Data *d);
Result deserialize(types::Header *h);
Result deserialize(types::Name1 *n);
Result deserialize(types::ChannelInfo *c);
Result deserialize(types::DigitalInfo *d);
Result deserialize(types::PmuConfig1 *c);
Result deserialize(types::Config1 *c);
Result deserialize(types::Config2 *c);
Result deserialize(types::Name3 *n);
Result deserialize(types::Config3 *c);
Result deserialize(types::Command *c);
Result deserialize(types::Frame *f);

template <typename Arg1, typename Arg2, typename... Args>
Result serialize(Arg1 const *arg1, Arg2 const *arg2, Args const *...args);
Result serialize(uint8_t const *i);
Result serialize(uint16_t const *i);
Result serialize(uint32_t const *i);
Result serialize(int8_t const *i);
Result serialize(int16_t const *i);
Result serialize(int32_t const *i);
Result serialize(float const *f);

template <typename T>
Result serialize(Placeholder<T> const *p);

template <typename T>
Result serialize(std::vector<T> const *v);

template <typename T, size_t N>
Result serialize(std::array<T, N> const *a);

template <typename Format>
Result serialize(types::Rectangular<Format> const *r);

template <typename Format>
Result serialize(types::Polar<Format> const *r);

Result serialize(types::Phasor const *p);
Result serialize(types::Analog const *a);
Result serialize(types::Freq const *f);
Result serialize(types::PmuData const *d);
Result serialize(types::Data const *d);
Result serialize(types::Header const *h);
Result serialize(types::Name1 const *n);
Result serialize(types::ChannelInfo const *c);
Result serialize(types::DigitalInfo const *d);
Result serialize(types::PmuConfig1 const *c);
Result serialize(types::Config1 const *c);
Result serialize(types::Config2 const *c);
Result serialize(types::Name3 const *n);
Result serialize(types::Config3 const *c);
Result serialize(types::Command const *c);
Result serialize(types::Frame const *f);

private:
unsigned char *start, *cursor, *end;
};

// placeholder type allowing deferred assignment to a field during serialization
template <typename T>
class Placeholder
{
public:
Placeholder() = default;
Result replace(T *t);
private:
mutable std::optional<Parser> saved_parser;
friend Result Parser::serialize(Placeholder<T> const *p);
};

uint16_t calculate_crc(unsigned char *frame, uint16_t size);

} /* namespace parser */
} /* namespace c37_118 */
} /* namespace node */
} /* namespace villas */
145 changes: 145 additions & 0 deletions include/villas/nodes/c37_118/types.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#include <array>
#include <complex>
#include <stdint.h>
#include <type_traits>
#include <variant>
#include <vector>

namespace villas {
namespace node {
namespace c37_118 {
namespace types {

// cartesian phasor representation
template <typename Format>
struct Rectangular {
Format real;
Format imaginary;
};

// polar phasor representation
template <typename Format, bool Unsigned = std::is_integral_v<Format>>
struct Polar;

template <typename Format>
struct Polar<Format, true>
{
std::make_unsigned_t<Format> magnitude;
std::make_signed_t<Format> phase;
};

template <typename Format>
struct Polar<Format, false>
{
Format magnitude;
Format phase;
};

struct Phasor : public std::variant<Rectangular<int16_t>, Polar<int16_t>, Rectangular<float>, Polar<float>>
{
std::complex<float> to_complex() const;
};

struct Analog : public std::variant<int16_t, float>
{
float to_float() const;
};

struct Freq : public std::variant<int16_t, float>
{
};

struct PmuData
{
uint16_t stat;
std::vector<Phasor> phasor;
Freq freq;
Freq dfreq;
std::vector<Analog> analog;
std::vector<uint16_t> digital;
};

struct Data
{
std::vector<PmuData> pmus;
};

struct Header
{
std::string data;
};

struct Name1 : public std::string
{
};

struct ChannelInfo
{
Name1 nam;
uint32_t unit;
};

struct DigitalInfo
{
std::array<Name1, 16> nam;
uint32_t unit;
};

struct PmuConfig1
{
Name1 stn;
uint16_t idcode;
uint16_t format;
std::vector<ChannelInfo> phinfo;
std::vector<ChannelInfo> aninfo;
std::vector<DigitalInfo> dginfo;
uint16_t fnom;
uint16_t cfgcnt;
};

struct Config1
{
uint32_t time_base;
std::vector<PmuConfig1> pmus;
uint16_t data_rate;
};

struct Config2 : public Config1 {};

struct Name3 : public std::string
{
};

// ToDo
struct Config3
{
};

struct Command
{
uint16_t cmd;
std::vector<unsigned char> ext;

static constexpr uint16_t DATA_START = 0x1;
static constexpr uint16_t DATA_STOP = 0x2;
static constexpr uint16_t GET_HEADER = 0x3;
static constexpr uint16_t GET_CONFIG1 = 0x4;
static constexpr uint16_t GET_CONFIG2 = 0x5;
static constexpr uint16_t GET_CONFIG3 = 0x6;
};

using Message = std::variant<Data, Header, Config1, Config2, Command, Config3>;

struct Frame
{
uint16_t version;
uint16_t idcode;
uint32_t soc;
uint32_t fracsec;
Message message;
};

} /* namespace types */
} /* namespace c37_118 */
} /* namespace node */
} /* namespace villas */
Loading

0 comments on commit 4ae7a31

Please sign in to comment.