-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C37.118 synchrophasor protocol node #652
Draft
pjungkamp
wants to merge
2
commits into
master
Choose a base branch
from
node-c37.118
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,6 +95,7 @@ | |
boxfort | ||
clang-tools | ||
criterion | ||
gdb | ||
jq | ||
libffi | ||
libgit2 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* Node type: 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 <cstdint> | ||
#include <string> | ||
#include <optional> | ||
#include <variant> | ||
#include <vector> | ||
#include <villas/node.hpp> | ||
#include <villas/node/config.hpp> | ||
#include <villas/nodes/c37_118/parser.hpp> | ||
#include <villas/queue_signalled.h> | ||
#include <villas/signal.hpp> | ||
#include <villas/socket_addr.hpp> | ||
#include <villas/timing.hpp> | ||
|
||
namespace villas::node::c37_118 { | ||
|
||
using parser::Parser; | ||
using types::Frame; | ||
using types::Config; | ||
|
||
class C37_118 final : public Node { | ||
private: | ||
struct Server { | ||
std::string addr; | ||
uint16_t port; | ||
int socktype; | ||
Config config; | ||
|
||
int listener_fd; | ||
std::vector<int> connection_fds; | ||
}; | ||
|
||
struct Client { | ||
std::string addr; | ||
uint16_t port; | ||
uint16_t idcode; | ||
int socktype; | ||
|
||
std::optional<Config> config; | ||
int connection_fd; | ||
std::vector<unsigned char> recv_buf = std::vector<unsigned char>(1024); | ||
Parser parser; | ||
|
||
void send(Frame::Variant message, timespec ts = time_now()); | ||
std::optional<Frame> recv(); | ||
}; | ||
|
||
std::variant<Server, Client> role; | ||
|
||
void parseServer(json_t *json); | ||
void parseClient(json_t *json); | ||
|
||
void prepareServer(Server &server); | ||
void prepareClient(Client &client); | ||
|
||
virtual int _read(struct Sample *smps[], unsigned cnt) override; | ||
|
||
public: | ||
C37_118(const uuid_t &id = {}, const std::string &name = "") : Node{id, name} {} | ||
|
||
virtual ~C37_118() override; | ||
virtual int parse(json_t *json) override; | ||
virtual int prepare() override; | ||
virtual int start() override; | ||
virtual int stop() override; | ||
virtual std::vector<int> getPollFDs() override; | ||
}; | ||
|
||
} // namespace villas::node::c37_118 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* 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: | ||
std::optional<Frame> deserialize(const unsigned char *buffer, | ||
std::size_t length, | ||
const Config *config); | ||
|
||
std::vector<unsigned char> serialize(const Frame &frame, | ||
const Config *config); | ||
|
||
private: | ||
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(const T *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, (const void *)value, count); | ||
} | ||
|
||
uint16_t deserialize_uint16_t(); | ||
uint32_t deserialize_uint32_t(); | ||
int16_t deserialize_int16_t(); | ||
float deserialize_float(); | ||
std::string deserialize_name1(); | ||
std::complex<float> deserialize_phasor(uint16_t format, const PhasorInfo &info); | ||
float deserialize_freq(const PmuConfig &pmu); | ||
float deserialize_dfreq(uint16_t format); | ||
float deserialize_analog(uint16_t format, const AnalogInfo &aninfo); | ||
uint16_t deserialize_digital(const DigitalInfo &dginfo); | ||
PmuData deserialize_pmu_data(const PmuConfig &pmu_config); | ||
PmuConfig deserialize_pmu_config_simple(); | ||
Config deserialize_config_simple(); | ||
Config1 deserialize_config1(); | ||
Config2 deserialize_config2(); | ||
Data deserialize_data(const Config &config); | ||
Header deserialize_header(); | ||
Command deserialize_command(); | ||
std::optional<Frame> try_deserialize_frame(const Config *config); | ||
|
||
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 std::complex<float> &value, uint16_t format, const PhasorInfo &phinfo); | ||
void serialize_freq(const float &value, const PmuConfig &pmu); | ||
void serialize_dfreq(const float &value, uint16_t format); | ||
void serialize_analog(const float &value, uint16_t format, const AnalogInfo &aninfo); | ||
void serialize_digital(const uint16_t &value, const DigitalInfo &dginfo); | ||
void serialize_pmu_data(const PmuData &value, const PmuConfig &pmu_config); | ||
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, const Config &config); | ||
void serialize_header(const Header &value); | ||
void serialize_command(const Command &value); | ||
void serialize_frame(const Frame &value, const Config *config); | ||
|
||
const unsigned char *de_cursor; | ||
const unsigned char *de_end; | ||
std::vector<unsigned char> se_buffer; | ||
}; | ||
|
||
uint16_t calculate_crc(const unsigned char *frame, uint16_t size); | ||
|
||
} // namespace villas::node::c37_118::parser |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
/* Node type: 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 | ||
*/ | ||
|
||
#include <array> | ||
#include <complex> | ||
#include <stdint.h> | ||
#include <variant> | ||
#include <vector> | ||
|
||
using namespace std::literals; | ||
|
||
namespace villas::node::c37_118::types { | ||
|
||
struct PmuData final { | ||
uint16_t stat; | ||
std::vector<std::complex<float>> phasor; | ||
float freq; | ||
float dfreq; | ||
std::vector<float> analog; | ||
std::vector<uint16_t> digital; | ||
}; | ||
|
||
struct Data final { | ||
std::vector<PmuData> pmus; | ||
}; | ||
|
||
struct Header final { | ||
std::string data; | ||
}; | ||
|
||
struct PhasorInfo final { | ||
static constexpr uint8_t UNIT_VOLT = 0; | ||
static constexpr uint8_t UNIT_AMPERE = 1; | ||
|
||
std::string chnam; | ||
uint32_t phunit; | ||
|
||
uint8_t unit() const noexcept { | ||
return phunit >> 24; | ||
} | ||
|
||
std::string unit_str() const noexcept { | ||
switch (unit()) { | ||
case UNIT_VOLT: | ||
return "volt"s; | ||
case UNIT_AMPERE: | ||
return "ampere"s; | ||
default: | ||
return "other"s; | ||
} | ||
} | ||
|
||
float scale() const noexcept { | ||
return static_cast<float>(phunit & 0xFFFFFF) / 10'000; | ||
} | ||
}; | ||
|
||
struct AnalogInfo final { | ||
static constexpr uint8_t UNIT_POINT_ON_WAVE = 0; | ||
static constexpr uint8_t UNIT_RMS = 1; | ||
static constexpr uint8_t UNIT_PEAK = 2; | ||
|
||
std::string chnam; | ||
uint32_t anunit; | ||
|
||
uint8_t unit() const noexcept { | ||
return anunit >> 24; | ||
} | ||
|
||
std::string unit_str() const noexcept { | ||
switch (unit()) { | ||
case UNIT_POINT_ON_WAVE: | ||
return "point-on-wave"s; | ||
case UNIT_RMS: | ||
return "rms"s; | ||
case UNIT_PEAK: | ||
return "peak"s; | ||
default: | ||
return "other"s; | ||
} | ||
} | ||
|
||
float scale() const noexcept { | ||
return static_cast<float>(anunit & 0xFFFFFF); | ||
} | ||
}; | ||
|
||
struct DigitalInfo final { | ||
std::array<std::string, 16> chnam; | ||
uint32_t dgunit; | ||
}; | ||
|
||
struct PmuConfig final { | ||
std::string stn; | ||
uint16_t idcode; | ||
uint16_t format; | ||
std::vector<PhasorInfo> phinfo; | ||
std::vector<AnalogInfo> 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_STOP = 0x1; | ||
static constexpr uint16_t DATA_START = 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing file header