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 Aug 26, 2024
1 parent 5d68a18 commit 27141a1
Show file tree
Hide file tree
Showing 10 changed files with 1,037 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ cmake_dependent_option(WITH_TOOLS "Build auxilary tools"
cmake_dependent_option(WITH_WEB "Build with internal webserver" "${WITH_DEFAULTS}" "LIBWEBSOCKETS_FOUND" OFF)

cmake_dependent_option(WITH_NODE_AMQP "Build with amqp node-type" "${WITH_DEFAULTS}" "RABBITMQ_C_FOUND" OFF)
cmake_dependent_option(WITH_NODE_C37_118 "Build with c37.118 node-type" "${WITH_DEFAULTS}" "" OFF)
cmake_dependent_option(WITH_NODE_CAN "Build with can node-type" "${WITH_DEFAULTS}" "" OFF)
cmake_dependent_option(WITH_NODE_COMEDI "Build with comedi node-type" "${WITH_DEFAULTS}" "COMEDILIB_FOUND" OFF)
cmake_dependent_option(WITH_NODE_ETHERCAT "Build with ethercat node-type" "${WITH_DEFAULTS}" "ETHERLAB_FOUND; NOT WITHOUT_GPL" OFF)
Expand Down Expand Up @@ -281,6 +282,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 */
95 changes: 95 additions & 0 deletions include/villas/nodes/c37_118/parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/* Parser for C37-118.
*
* Author: Philipp Jungkamp <[email protected]>
* SPDX-FileCopyrightText: 2014-2024 Institute for Automation of Complex Power Systems, RWTH Aachen University
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

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

namespace villas::node::c37_118::parser {
using namespace villas::node::c37_118::types;

class Parser {
public:
Parser(Config config = {});
void set_config(Config config);
Config const *get_config() const;
std::optional<Frame> deserialize(unsigned char const *buffer,
std::size_t length);
std::vector<unsigned char> serialize(Frame const &frame);

private:
uint16_t config_num_pmu() const;
uint16_t config_format(uint16_t pmu) const;
uint16_t config_phnmr(uint16_t pmu) const;
uint16_t config_annmr(uint16_t pmu) const;
uint16_t config_dgnmr(uint16_t pmu) const;

template <typename T> void de_copy(T *value, std::size_t count = sizeof(T)) {
if (de_cursor + count > de_end)
throw RuntimeError{"c37_118: broken frame"};

std::memcpy((void *)value, de_cursor, count);
de_cursor += count;
}

template <typename T>
void se_copy(T const *value, std::size_t count = sizeof(T)) {
auto index = se_buffer.size();
se_buffer.insert(se_buffer.end(), count, 0);
std::memcpy(se_buffer.data() + index, (void const *)value, count);
}

uint16_t deserialize_uint16_t();
uint32_t deserialize_uint32_t();
int16_t deserialize_int16_t();
float deserialize_float();
std::string deserialize_name1();
Phasor deserialize_phasor(uint16_t pmu);
Analog deserialize_analog(uint16_t pmu);
Freq deserialize_freq(uint16_t pmu);
PmuData deserialize_pmu_data(uint16_t pmu);
PmuConfig deserialize_pmu_config_simple();
Config deserialize_config_simple();
Config1 deserialize_config1();
Config2 deserialize_config2();
Data deserialize_data();
Header deserialize_header();
Command deserialize_command();
std::optional<Frame> try_deserialize_frame();

void serialize_uint16_t(const uint16_t &value);
void serialize_uint32_t(const uint32_t &value);
void serialize_int16_t(const int16_t &value);
void serialize_float(const float &value);
void serialize_name1(const std::string &value);
void serialize_phasor(const Phasor &value, uint16_t pmu);
void serialize_analog(const Analog &value, uint16_t pmu);
void serialize_freq(const Freq &value, uint16_t pmu);
void serialize_pmu_data(const PmuData &value, uint16_t pmu);
void serialize_pmu_config_simple(const PmuConfig &value);
void serialize_config_simple(const Config &value);
void serialize_config1(const Config1 &value);
void serialize_config2(const Config2 &value);
void serialize_data(const Data &value);
void serialize_header(const Header &value);
void serialize_command(const Command &value);
void serialize_frame(const Frame &value);

std::optional<Config> config;

unsigned char const *de_cursor;
unsigned char const *de_end;

std::vector<unsigned char> se_buffer;
};

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

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

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

class Phasor final {
public:
enum Notation {
RectangularInt = 0,
PolarInt,
RectangularFloat,
PolarFloat,
};
using RectangularInt_t = std::tuple<int16_t, int16_t>;
using PolarInt_t = std::tuple<uint16_t, int16_t>;
using RectangularFloat_t = std::tuple<float, float>;
using PolarFloat_t = std::tuple<float, float>;
using Variant = std::variant<RectangularInt_t, PolarInt_t, RectangularFloat_t,
PolarFloat_t>;

Phasor() = default;

Notation notation() const;
std::complex<float> to_complex() const;
static Phasor from_complex(std::complex<float> value, Notation notation);

template <Notation N, typename Tuple = std::variant_alternative_t<N, Variant>,
typename First = std::tuple_element<0, Tuple>,
typename Second = std::tuple_element<1, Tuple>>
static Phasor make(First first, Second second);

template <Notation N, typename Tuple = std::variant_alternative_t<N, Variant>>
Tuple get() const;

private:
Phasor(Variant variant) : variant{std::move(variant)} {}
Variant variant;
};

template <Phasor::Notation N, typename Tuple, typename First, typename Second>
Phasor Phasor::make(First first, Second second) {
return Variant{std::in_place_index<N>, first, second};
}

template <Phasor::Notation N, typename Tuple> Tuple Phasor::get() const {
return std::get<N>(this->variant);
}

class Number final {
public:
enum Notation {
Int,
Float,
};
using Int_t = int16_t;
using Float_t = float;
using Variant = std::variant<Int_t, Float_t>;

Number() = default;

Notation notation() const;
float to_float() const;
static Number from_float(float value, Notation notation);

template <Notation N, typename Value = std::variant_alternative_t<N, Variant>>
static Number make(Value value);

template <Notation N, typename Value = std::variant_alternative_t<N, Variant>>
Value get() const;

private:
Number(Variant variant) : variant{std::move(variant)} {}
Variant variant;
};

using Analog = Number;
using Freq = Number;

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

template <Number::Notation N, typename Value> Number Number::make(Value value) {
return Variant{std::in_place_index<N>, value};
}

template <Number::Notation N, typename Value> Value Number::get() const {
return std::get<N>(this->variant);
}

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

struct Header final {
std::string data;
};

struct ChannelInfo final {
std::string nam;
uint32_t unit;
};

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

struct PmuConfig final {
std::string 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 Config {
uint32_t time_base;
std::vector<PmuConfig> pmus;
uint16_t data_rate;
};

class Config1 {
private:
Config inner;

public:
Config1() = delete;
Config1(Config config) noexcept : inner(config) {}
operator Config&() noexcept {return inner;}
operator Config const&() const noexcept {return inner;}
Config* operator ->() { return &inner; }
Config const* operator ->() const { return &inner; }
};

class Config2 {
private:
Config inner;

public:
Config2() = delete;
Config2(Config config) noexcept : inner(config) {}
operator Config&() noexcept {return inner;}
operator Config const&() const noexcept {return inner;}
Config* operator ->() { return &inner; }
Config const* operator ->() const { return &inner; }
};

struct Command final {
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;
};

struct Frame final {
using Variant = std::variant<Data, Header, Config1, Config2, Command>;

uint16_t version;
uint16_t framesize;
uint16_t idcode;
uint32_t soc;
uint32_t fracsec;
Variant message;
};

} // namespace villas::node::c37_118::types
4 changes: 4 additions & 0 deletions lib/nodes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ if(WITH_NODE_SOCKET)
list(APPEND NODE_SRC socket.cpp)
endif()

if(WITH_NODE_C37_118)
list(APPEND NODE_SRC c37_118.cpp c37_118/parser.cpp c37_118/types.cpp)
endif()

if(WITH_NODE_FILE)
list(APPEND NODE_SRC file.cpp)
endif()
Expand Down
3 changes: 3 additions & 0 deletions lib/nodes/c37_118.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include <villas/nodes/c37_118.hpp>

using namespace villas::node::c37_118;
Loading

0 comments on commit 27141a1

Please sign in to comment.