2023-01-31 11:12:51 +00:00
|
|
|
/** Node type: IEC 61850 - GOOSE
|
|
|
|
*
|
|
|
|
* @author Philipp Jungkamp <philipp.jungkamp@rwth-aachen.de>
|
|
|
|
* @copyright 2023, Institute for Automation of Complex Power Systems, EONERC
|
|
|
|
* @license Apache 2.0
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2023-03-28 13:16:09 +00:00
|
|
|
#include <array>
|
|
|
|
#include <mutex>
|
|
|
|
#include <condition_variable>
|
2023-01-31 11:12:51 +00:00
|
|
|
#include <cstdint>
|
|
|
|
#include <ctime>
|
|
|
|
#include <map>
|
2023-03-28 13:16:09 +00:00
|
|
|
#include <optional>
|
|
|
|
#include <string>
|
|
|
|
#include <thread>
|
2023-01-31 11:12:51 +00:00
|
|
|
#include <villas/node/config.hpp>
|
|
|
|
#include <villas/node.hpp>
|
|
|
|
#include <villas/pool.hpp>
|
|
|
|
#include <villas/queue_signalled.h>
|
|
|
|
#include <villas/signal.hpp>
|
|
|
|
#include <libiec61850/goose_receiver.h>
|
|
|
|
#include <libiec61850/goose_subscriber.h>
|
2023-02-17 15:21:05 +00:00
|
|
|
#include <libiec61850/goose_publisher.h>
|
2023-01-31 11:12:51 +00:00
|
|
|
|
|
|
|
namespace villas {
|
|
|
|
namespace node {
|
|
|
|
namespace iec61850 {
|
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
// A GooseSignal is a SignalData value with attached Metadata for the MmsType and SignalType
|
|
|
|
class GooseSignal {
|
2023-01-31 11:12:51 +00:00
|
|
|
public:
|
2023-02-17 15:21:05 +00:00
|
|
|
union Meta {
|
|
|
|
int size;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Descriptor {
|
|
|
|
std::string name;
|
2023-03-28 13:16:09 +00:00
|
|
|
SignalType signal_type;
|
2023-03-14 15:30:40 +00:00
|
|
|
MmsType mms_type;
|
2023-02-17 15:21:05 +00:00
|
|
|
Meta default_meta;
|
|
|
|
};
|
|
|
|
|
|
|
|
using Type = Descriptor const *;
|
2023-01-31 11:12:51 +00:00
|
|
|
|
|
|
|
// The config file identifier for this type
|
2023-02-17 15:21:05 +00:00
|
|
|
std::string const & name() const;
|
|
|
|
|
|
|
|
// The type of this value
|
|
|
|
Type type() const;
|
2023-01-31 11:12:51 +00:00
|
|
|
|
|
|
|
// Corresponding mms type
|
2023-03-14 15:30:40 +00:00
|
|
|
MmsType mmsType() const;
|
2023-01-31 11:12:51 +00:00
|
|
|
|
|
|
|
// Corresponding signal type
|
|
|
|
SignalType signalType() const;
|
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
// Create a GooseSignal from an MmsValue
|
|
|
|
static std::optional<GooseSignal> fromMmsValue(MmsValue *mms_value);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
// Create a GooseSignal from type name and SignalData value
|
2023-03-14 15:30:40 +00:00
|
|
|
static std::optional<GooseSignal> fromNameAndValue(char const *name, SignalData value, std::optional<Meta> meta = std::nullopt);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
// Create a MmsValue from this GooseSignal
|
2023-03-14 15:30:40 +00:00
|
|
|
MmsValue * toMmsValue() const;
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
static std::optional<Type> lookupMmsType(int mms_type);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
static std::optional<Type> lookupMmsTypeName(char const *name);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
GooseSignal(Type type, SignalData value, std::optional<Meta> meta = std::nullopt);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
SignalData signal_data;
|
|
|
|
Meta meta;
|
|
|
|
private:
|
2023-01-31 11:12:51 +00:00
|
|
|
inline static std::array const descriptors {
|
2023-03-28 13:16:09 +00:00
|
|
|
Descriptor { "boolean", SignalType::BOOLEAN, MmsType::MMS_BOOLEAN },
|
|
|
|
Descriptor { "int8", SignalType::INTEGER, MmsType::MMS_INTEGER, {.size = 8 } },
|
|
|
|
Descriptor { "int16", SignalType::INTEGER, MmsType::MMS_INTEGER, {.size = 16 } },
|
|
|
|
Descriptor { "int32", SignalType::INTEGER, MmsType::MMS_INTEGER, {.size = 32 } },
|
|
|
|
Descriptor { "int64", SignalType::INTEGER, MmsType::MMS_INTEGER, {.size = 64 } },
|
|
|
|
Descriptor { "int8u", SignalType::INTEGER, MmsType::MMS_UNSIGNED, {.size = 8 } },
|
|
|
|
Descriptor { "int16u", SignalType::INTEGER, MmsType::MMS_UNSIGNED, {.size = 16 } },
|
|
|
|
Descriptor { "int32u", SignalType::INTEGER, MmsType::MMS_UNSIGNED, {.size = 32 } },
|
|
|
|
Descriptor { "bitstring", SignalType::INTEGER, MmsType::MMS_BIT_STRING, {.size = 32 } },
|
|
|
|
Descriptor { "float32", SignalType::FLOAT, MmsType::MMS_FLOAT, {.size = 32 } },
|
|
|
|
Descriptor { "float64", SignalType::FLOAT, MmsType::MMS_FLOAT, {.size = 64 } },
|
2023-01-31 11:12:51 +00:00
|
|
|
};
|
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
static MmsValue * newMmsInteger(int64_t i, int size);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
static MmsValue * newMmsUnsigned(uint64_t i, int size);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
static MmsValue * newMmsBitString(uint32_t i, int size);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
static MmsValue * newMmsFloat(double i, int size);
|
2023-01-31 11:12:51 +00:00
|
|
|
|
|
|
|
// Descriptor within the descriptors table above
|
|
|
|
Descriptor const *descriptor;
|
|
|
|
};
|
|
|
|
|
2023-03-14 15:30:40 +00:00
|
|
|
bool operator==(GooseSignal &lhs, GooseSignal &rhs);
|
|
|
|
bool operator!=(GooseSignal &lhs, GooseSignal &rhs);
|
|
|
|
|
2023-01-31 11:12:51 +00:00
|
|
|
class GooseNode : public Node {
|
|
|
|
protected:
|
|
|
|
enum InputTrigger {
|
|
|
|
CHANGE,
|
|
|
|
ALWAYS,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct InputMapping {
|
|
|
|
std::string subscriber;
|
|
|
|
unsigned int index;
|
2023-03-14 15:30:40 +00:00
|
|
|
GooseSignal::Type type;
|
2023-01-31 11:12:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct SubscriberConfig {
|
|
|
|
std::string go_cb_ref;
|
|
|
|
InputTrigger trigger;
|
|
|
|
std::optional<std::array<uint8_t, 6>> dst_address;
|
|
|
|
std::optional<uint16_t> app_id;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct InputEventContext {
|
|
|
|
SubscriberConfig subscriber_config;
|
|
|
|
|
|
|
|
GooseNode *node;
|
2023-03-14 15:44:51 +00:00
|
|
|
std::vector<std::optional<GooseSignal>> values;
|
2023-03-14 15:30:40 +00:00
|
|
|
int last_state_num;
|
2023-01-31 11:12:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Input {
|
|
|
|
enum { NONE, STOPPED, READY } state;
|
|
|
|
GooseReceiver receiver;
|
|
|
|
CQueueSignalled queue;
|
|
|
|
Pool pool;
|
|
|
|
|
|
|
|
std::map<std::string, InputEventContext> contexts;
|
|
|
|
std::vector<InputMapping> mappings;
|
|
|
|
std::string interface_id;
|
|
|
|
bool with_timestamp;
|
|
|
|
unsigned int queue_length;
|
|
|
|
} input;
|
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
struct OutputData {
|
2023-03-14 15:30:40 +00:00
|
|
|
std::optional<unsigned int> signal;
|
2023-02-17 15:21:05 +00:00
|
|
|
GooseSignal default_value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct PublisherConfig {
|
|
|
|
std::string go_id;
|
|
|
|
std::string go_cb_ref;
|
|
|
|
std::string data_set_ref;
|
|
|
|
std::array<uint8_t, 6> dst_address;
|
|
|
|
uint16_t app_id;
|
|
|
|
uint32_t conf_rev;
|
|
|
|
uint32_t time_allowed_to_live;
|
2023-03-14 15:30:40 +00:00
|
|
|
int burst;
|
2023-02-17 15:21:05 +00:00
|
|
|
std::vector<OutputData> data;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct OutputContext {
|
|
|
|
PublisherConfig config;
|
2023-03-14 15:30:40 +00:00
|
|
|
std::vector<GooseSignal> values;
|
2023-02-17 15:21:05 +00:00
|
|
|
|
|
|
|
GoosePublisher publisher;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Output {
|
|
|
|
enum { NONE, STOPPED, READY } state;
|
|
|
|
std::vector<OutputContext> contexts;
|
|
|
|
std::string interface_id;
|
2023-04-11 15:04:41 +02:00
|
|
|
double resend_interval;
|
2023-03-28 13:16:09 +00:00
|
|
|
|
|
|
|
std::mutex send_mutex;
|
|
|
|
bool changed;
|
|
|
|
bool resend_thread_stop;
|
|
|
|
std::optional<std::thread> resend_thread;
|
|
|
|
std::condition_variable resend_thread_cv;
|
2023-02-17 15:21:05 +00:00
|
|
|
} output;
|
|
|
|
|
2023-01-31 11:12:51 +00:00
|
|
|
void createReceiver() noexcept;
|
|
|
|
void destroyReceiver() noexcept;
|
|
|
|
|
|
|
|
void startReceiver() noexcept(false);
|
|
|
|
void stopReceiver() noexcept;
|
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
void createPublishers() noexcept;
|
|
|
|
void destroyPublishers() noexcept;
|
|
|
|
|
|
|
|
void startPublishers() noexcept(false);
|
|
|
|
void stopPublishers() noexcept;
|
|
|
|
|
2023-01-31 11:12:51 +00:00
|
|
|
static void onEvent(GooseSubscriber subscriber, InputEventContext &context) noexcept;
|
|
|
|
|
|
|
|
void addSubscriber(InputEventContext &ctx) noexcept;
|
|
|
|
void pushSample(uint64_t timestamp) noexcept;
|
|
|
|
|
2023-03-28 13:16:09 +00:00
|
|
|
static void publish_values(GoosePublisher publisher, std::vector<GooseSignal> &values, bool changed, int burst = 1) noexcept;
|
|
|
|
static void resend_thread(GooseNode::Output *output) noexcept;
|
|
|
|
|
2023-04-18 13:18:14 +02:00
|
|
|
void parseInput(json_t *json);
|
|
|
|
void parseSubscriber(json_t *json, SubscriberConfig &sc);
|
|
|
|
void parseSubscribers(json_t *json, std::map<std::string, InputEventContext> &ctx);
|
|
|
|
void parseInputSignals(json_t *json, std::vector<InputMapping> &mappings);
|
|
|
|
|
|
|
|
void parseOutput(json_t *json);
|
|
|
|
void parsePublisherData(json_t *json, std::vector<OutputData> &data);
|
|
|
|
void parsePublisher(json_t *json, PublisherConfig &pc);
|
|
|
|
void parsePublishers(json_t *json, std::vector<OutputContext> &ctx);
|
2023-02-17 15:21:05 +00:00
|
|
|
|
2023-01-31 11:12:51 +00:00
|
|
|
virtual
|
|
|
|
int _read(struct Sample *smps[], unsigned cnt) override;
|
|
|
|
|
2023-02-17 15:21:05 +00:00
|
|
|
virtual
|
|
|
|
int _write(struct Sample *smps[], unsigned cnt) override;
|
|
|
|
|
2023-01-31 11:12:51 +00:00
|
|
|
public:
|
|
|
|
GooseNode(const std::string &name = "");
|
|
|
|
|
|
|
|
virtual
|
|
|
|
~GooseNode() override;
|
|
|
|
|
|
|
|
virtual
|
|
|
|
std::vector<int> getPollFDs() override;
|
|
|
|
|
|
|
|
virtual
|
|
|
|
int parse(json_t *json, const uuid_t sn_uuid) override;
|
|
|
|
|
|
|
|
virtual
|
|
|
|
int prepare() override;
|
|
|
|
|
|
|
|
virtual
|
|
|
|
int start() override;
|
|
|
|
|
|
|
|
virtual
|
|
|
|
int stop() override;
|
|
|
|
};
|
|
|
|
|
|
|
|
} /* namespace iec61850 */
|
|
|
|
} /* namespace node */
|
|
|
|
} /* namespace villas */
|