From 202c69ad77bcf9c02969a52ea221b0e5f8478937 Mon Sep 17 00:00:00 2001 From: Erik Hofman <erik@ehofman.com> Date: Tue, 6 Apr 2021 14:52:37 +0200 Subject: [PATCH] Implement the start of a property server using DDS. --- src/Network/CMakeLists.txt | 7 +- src/Network/DDS/CMakeLists.txt | 2 + src/Network/DDS/dds_fwd.hxx | 10 ++ src/Network/DDS/dds_props.c | 46 +++++++++ src/Network/DDS/dds_props.h | 75 +++++++++++++++ src/Network/DDS/dds_props.idl | 85 +++++++++++++++++ src/Network/dds_props.cxx | 167 +++++++++++++++++++++++++++++++++ src/Network/dds_props.hxx | 57 +++++++++++ 8 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 src/Network/DDS/dds_fwd.hxx create mode 100644 src/Network/DDS/dds_props.c create mode 100644 src/Network/DDS/dds_props.h create mode 100644 src/Network/DDS/dds_props.idl create mode 100644 src/Network/dds_props.cxx create mode 100644 src/Network/dds_props.hxx diff --git a/src/Network/CMakeLists.txt b/src/Network/CMakeLists.txt index 274dd739a..e16e3ddf5 100644 --- a/src/Network/CMakeLists.txt +++ b/src/Network/CMakeLists.txt @@ -62,11 +62,16 @@ set(HEADERS rul.hxx ) +if (CycloneDDS_FOUND) + set(SOURCES ${SOURCES} dds_props.cxx) + set(HEADERS ${HEADERS} dds_props.hxx) +endif() + if(ENABLE_IAX) list(APPEND SOURCES fgcom.cxx) list(APPEND HEADERS fgcom.hxx) endif() - + flightgear_component(Network "${SOURCES}" "${HEADERS}") if (CycloneDDS_FOUND) diff --git a/src/Network/DDS/CMakeLists.txt b/src/Network/DDS/CMakeLists.txt index 8372f6d73..b96b64e60 100644 --- a/src/Network/DDS/CMakeLists.txt +++ b/src/Network/DDS/CMakeLists.txt @@ -4,12 +4,14 @@ set(SOURCES dds_ctrls.c dds_gui.c dds_fdm.c + dds_props.c ) set(HEADERS dds_ctrls.h dds_fdm.h dds_gui.h + dds_props.h ) add_executable(fg_dds_log diff --git a/src/Network/DDS/dds_fwd.hxx b/src/Network/DDS/dds_fwd.hxx new file mode 100644 index 000000000..ddbe9f44e --- /dev/null +++ b/src/Network/DDS/dds_fwd.hxx @@ -0,0 +1,10 @@ +// dds_fwd.hxx +// +// This file is in the Public Domain, and comes with no warranty. + +#pragma once + +#include "dds_gui.h" +#include "dds_fdm.h" +#include "dds_ctrls.h" + diff --git a/src/Network/DDS/dds_props.c b/src/Network/DDS/dds_props.c new file mode 100644 index 000000000..79ef5628d --- /dev/null +++ b/src/Network/DDS/dds_props.c @@ -0,0 +1,46 @@ +/**************************************************************** + + Generated by Eclipse Cyclone DDS IDL to C Translator + File name: dds_props.c + Source: dds_props.idl + Cyclone DDS: V0.7.0 + +*****************************************************************/ +#include "dds_props.h" + + +static const dds_key_descriptor_t FG_DDS_PROP_keys[1] = +{ + { "id", 0 } +}; + +static const uint32_t FG_DDS_PROP_ops [] = +{ + DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | DDS_OP_FLAG_KEY, offsetof (FG_DDS_PROP, id), + DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN, offsetof (FG_DDS_PROP, type), + DDS_OP_ADR | DDS_OP_TYPE_UNI | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_SGN, offsetof (FG_DDS_PROP, val._d), 9u, (31u << 16) + 4u, + DDS_OP_JEQ | DDS_OP_TYPE_BOO | 0, FG_DDS_BOOL, offsetof (FG_DDS_PROP, val._u.Bool), + DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | 0, FG_DDS_NONE, offsetof (FG_DDS_PROP, val._u.Int32), + DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | 0, FG_DDS_INT, offsetof (FG_DDS_PROP, val._u.Int32), + DDS_OP_JEQ | DDS_OP_TYPE_8BY | DDS_OP_FLAG_SGN | 0, FG_DDS_LONG, offsetof (FG_DDS_PROP, val._u.Int64), + DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP | 0, FG_DDS_FLOAT, offsetof (FG_DDS_PROP, val._u.Float32), + DDS_OP_JEQ | DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP | 0, FG_DDS_DOUBLE, offsetof (FG_DDS_PROP, val._u.Float64), + DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_ALIAS, offsetof (FG_DDS_PROP, val._u.String), + DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_STRING, offsetof (FG_DDS_PROP, val._u.String), + DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_UNSPECIFIED, offsetof (FG_DDS_PROP, val._u.String), + DDS_OP_ADR | DDS_OP_TYPE_STR, offsetof (FG_DDS_PROP, guid), + DDS_OP_RTS +}; + +const dds_topic_descriptor_t FG_DDS_PROP_desc = +{ + sizeof (FG_DDS_PROP), + 8u, + DDS_TOPIC_FIXED_KEY | DDS_TOPIC_NO_OPTIMIZE | DDS_TOPIC_CONTAINS_UNION, + 1u, + "FG::DDS_PROP", + FG_DDS_PROP_keys, + 14, + FG_DDS_PROP_ops, + "<MetaData version=\"1.0.0\"><Module name=\"FG\"><Enum name=\"propType\"><Element name=\"DDS_NONE\" value=\"0\"/><Element name=\"DDS_ALIAS\" value=\"1\"/><Element name=\"DDS_BOOL\" value=\"2\"/><Element name=\"DDS_INT\" value=\"3\"/><Element name=\"DDS_LONG\" value=\"4\"/><Element name=\"DDS_FLOAT\" value=\"5\"/><Element name=\"DDS_DOUBLE\" value=\"6\"/><Element name=\"DDS_STRING\" value=\"7\"/><Element name=\"DDS_UNSPECIFIED\" value=\"8\"/></Enum><Union name=\"propValue\"><SwitchType><Type name=\"propType\"/></SwitchType><Case name=\"Bool\"><Boolean/><Label value=\"DDS_BOOL\"/></Case><Case name=\"Int32\"><Long/><Label value=\"DDS_NONE\"/><Label value=\"DDS_INT\"/></Case><Case name=\"Int64\"><LongLong/><Label value=\"DDS_LONG\"/></Case><Case name=\"Float32\"><Float/><Label value=\"DDS_FLOAT\"/></Case><Case name=\"Float64\"><Double/><Label value=\"DDS_DOUBLE\"/></Case><Case name=\"String\"><String/><Label value=\"DDS_ALIAS\"/><Label value=\"DDS_STRING\"/><Label value=\"DDS_UNSPECIFIED\"/></Case></Union><Struct name=\"DDS_PROP\"><Member name=\"id\"><Long/></Member><Member name=\"type\"><Type name=\"propType\"/></Member><Member name=\"val\"><Type name=\"propValue\"/></Member><Member name=\"guid\"><String/></Member></Struct></Module></MetaData>" +}; diff --git a/src/Network/DDS/dds_props.h b/src/Network/DDS/dds_props.h new file mode 100644 index 000000000..eab4fa324 --- /dev/null +++ b/src/Network/DDS/dds_props.h @@ -0,0 +1,75 @@ +/**************************************************************** + + Generated by Eclipse Cyclone DDS IDL to C Translator + File name: dds_props.h + Source: dds_props.idl + Cyclone DDS: V0.7.0 + +*****************************************************************/ + +#include "dds/ddsc/dds_public_impl.h" + +#ifndef _DDSL_DDS_PROPS_H_ +#define _DDSL_DDS_PROPS_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#define FG_DDS_PROP_REQUEST -1 +typedef enum FG_propType +{ + FG_DDS_NONE, + FG_DDS_ALIAS, + FG_DDS_BOOL, + FG_DDS_INT, + FG_DDS_LONG, + FG_DDS_FLOAT, + FG_DDS_DOUBLE, + FG_DDS_STRING, + FG_DDS_UNSPECIFIED +} FG_propType; + +#define FG_propType__alloc() \ +((FG_propType*) dds_alloc (sizeof (FG_propType))); + + +typedef struct FG_propValue +{ + FG_propType _d; + union + { + bool Bool; + int32_t Int32; + int64_t Int64; + float Float32; + double Float64; + char * String; + } _u; +} FG_propValue; + +#define FG_propValue__alloc() \ +((FG_propValue*) dds_alloc (sizeof (FG_propValue))); + + +typedef struct FG_DDS_PROP +{ + int32_t id; + FG_propType type; + FG_propValue val; + char * guid; +} FG_DDS_PROP; + +extern const dds_topic_descriptor_t FG_DDS_PROP_desc; + +#define FG_DDS_PROP__alloc() \ +((FG_DDS_PROP*) dds_alloc (sizeof (FG_DDS_PROP))); + +#define FG_DDS_PROP_free(d,o) \ +dds_sample_free ((d), &FG_DDS_PROP_desc, (o)) + +#ifdef __cplusplus +} +#endif +#endif /* _DDSL_DDS_PROPS_H_ */ diff --git a/src/Network/DDS/dds_props.idl b/src/Network/DDS/dds_props.idl new file mode 100644 index 000000000..02716fd93 --- /dev/null +++ b/src/Network/DDS/dds_props.idl @@ -0,0 +1,85 @@ +// format dfescription: https://www.omg.org/spec/IDL/4.2/PDF + +// Adapted from net_fdm.hxx +module FG +{ + +// defining it this way also generates accompanying #defines in the header file. +const short DDS_PROP_REQUEST = -1; + +enum propType +{ + DDS_NONE, // The node hasn't been assigned a value yet + DDS_ALIAS, // The node "points" to another node + DDS_BOOL, + DDS_INT, // 32-bit integer + DDS_LONG, // 64-bit integer + DDS_FLOAT, // 32-bit floating point number + DDS_DOUBLE, // 64-bit floating point number + DDS_STRING, // UTF-8 string + DDS_UNSPECIFIED // Resolves to STRING +}; + +union propValue switch ( propType ) +{ + case DDS_BOOL: + boolean Bool; + + case DDS_NONE: + case DDS_INT: + long Int32; + + case DDS_LONG: + long long Int64; + + case DDS_FLOAT: + float Float32; + + case DDS_DOUBLE: + double Float64; + + case DDS_ALIAS: + case DDS_STRING: + case DDS_UNSPECIFIED: + string String; +}; + +// Initial property request sequence +// for properties where the id is not yet known: +// 1. Set id to FG_DDS_PROP_REQUEST +// 2. set guid to the 16-byte participants GUID +// 3. Set type to STRING +// 4. Set val.String to the propery path +// 5. Send the package. +// +// 6. wait for an answer +// * Check whether guid matches the participants GUID. +// * The index of the requested propery path is stored in the id variable +// which should be used by successive request as the id for that property. +// * The type variable indicates the type of the value of the property. +// * The val union holds the value of the propery. +// +// Successive property request sequence: +// 1. Set id to the id of the requested property +// 2. Send the package. +// +// 3. wait for an answer +// * Check whether id matches the requested property id. +// * The type variable indicates the type of the value of the property. +// * The val union holds the value of the propery. +// +// guid is not defined as an array of 16 unsigned characters to keep the +// sample small for successive requests. This way only a FG_DDS_PROP_REQUEST +// sample will reserve the full 16-bytes. +struct DDS_PROP +{ + long id; // property index and DDS id + + propType type; + propValue val; + + string guid; // requesters globally unique identifier +}; +#pragma keylist DDS_PROP id + +}; // module FG diff --git a/src/Network/dds_props.cxx b/src/Network/dds_props.cxx new file mode 100644 index 000000000..62ce7fa68 --- /dev/null +++ b/src/Network/dds_props.cxx @@ -0,0 +1,167 @@ +// dds_props.cxx -- FGFS "DDS" properties protocal class +// +// Written by Erik Hofman, started April 2021 +// +// Copyright (C) 2021 by Erik Hofman <erik@ehofman.com> +// +// 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 2 of the +// License, or (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <simgear/debug/logstream.hxx> +#include <simgear/io/iochannel.hxx> +#include <simgear/props/props.hxx> + +#include <simgear/io/SGDataDistributionService.hxx> + +#include <Main/fg_props.hxx> +#include <Main/globals.hxx> + +#include "dds_props.hxx" + +// open hailing frequencies +bool FGDDSProps::open() { + if (is_enabled()) { + SG_LOG(SG_IO, SG_ALERT, "This shouldn't happen, but the channel " + << "is already in use, ignoring"); + return false; + } + + SGIOChannel *io = get_io_channel(); + + SG_DDS_Topic *dds = static_cast<SG_DDS_Topic*>(io); + dds->setup<FG_DDS_PROP>(&FG_DDS_PROP_desc); + + // always send and recieve. + if (! io->open(SG_IO_BI)) { + SG_LOG(SG_IO, SG_ALERT, "Error opening channel communication layer."); + return false; + } + + set_enabled(true); + + return true; +} + +// process work for this port +bool FGDDSProps::process() { + SGIOChannel *io = get_io_channel(); + FG_DDS_PROP prop; + + int length = sizeof(prop); + char *buf = reinterpret_cast<char*>(&prop); + + if (get_direction() == SG_IO_IN) + { + // act as a client: send a request and wait for an answer. + + } + else if (get_direction() == SG_IO_OUT) + { + // act as a server: read requests and send the results. + while (io->read(buf, length) == length) + { + if (prop.id == FG_DDS_PROP_REQUEST) + { + auto it = by_path.find(prop.val._u.String); + if (it == by_path.end()) + { + SGPropertyNode_ptr props = globals->get_props(); + SGPropertyNode_ptr p = props->getNode(prop.val._u.String); + if (p) + { + prop.id = by_id.size(); + by_id[prop.id] = p; + by_path[prop.val._u.String] = p; + } + setProp(prop, p); + } + else + { + prop.id = std::distance(by_path.begin(), it); + setProp(prop, it->second); + } + } + else + { + SGPropertyNode_ptr p = by_id[prop.id]; + setProp(prop, p); + } + + // send the response. + if (! io->write(buf, length)) { + SG_LOG(SG_IO, SG_ALERT, "Error writing data."); + } + } // while + } + + return true; +} + +// close the channel +bool FGDDSProps::close() { + SGIOChannel *io = get_io_channel(); + + set_enabled(false); + + if (! io->close()) { + return false; + } + + return true; +} + +void FGDDSProps::setProp(FG_DDS_PROP& prop, SGPropertyNode_ptr p) +{ + if (p) + { + simgear::props::Type type = p->getType(); + if (type == simgear::props::BOOL) { + prop.type = FG_DDS_BOOL; + prop.val._u.Bool = p->getBoolValue(); + } else if (type == simgear::props::INT) { + prop.type = FG_DDS_INT; + prop.val._u.Int32 = p->getIntValue(); + } else if (type == simgear::props::LONG) { + prop.type = FG_DDS_LONG; + prop.val._u.Int64 = p->getLongValue(); + } else if (type == simgear::props::FLOAT) { + prop.type = FG_DDS_FLOAT; + prop.val._u.Float32 = p->getFloatValue(); + } else if (type == simgear::props::DOUBLE) { + prop.type = FG_DDS_DOUBLE; + prop.val._u.Float64 = p->getDoubleValue(); + } else if (type == simgear::props::ALIAS) { + prop.type = FG_DDS_ALIAS; + prop.val._u.String = const_cast<char*>(p->getStringValue()); + } else if (type == simgear::props::STRING) { + prop.type = FG_DDS_STRING; + prop.val._u.String = const_cast<char*>(p->getStringValue()); + } else if (type == simgear::props::UNSPECIFIED) { + prop.type = FG_DDS_UNSPECIFIED; + prop.val._u.String = const_cast<char*>(p->getStringValue()); + } else { + prop.type = FG_DDS_NONE; + prop.val._u.Int32 = 0; + } + } +} + + diff --git a/src/Network/dds_props.hxx b/src/Network/dds_props.hxx new file mode 100644 index 000000000..0c874d1d0 --- /dev/null +++ b/src/Network/dds_props.hxx @@ -0,0 +1,57 @@ +// dds_props.hxx -- FGFS "DDS" properties protocal class +// +// Written by Erik Hofman, started April 2021 +// +// Copyright (C) 2021 by Erik Hofman <erik@ehofman.com> +// +// 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 2 of the +// License, or (at your option) 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#pragma once + +#include <string> +#include <map> + +#include <simgear/compiler.h> + +#include <simgear/props/propsfwd.hxx> + +#include "protocol.hxx" +#include "DDS/dds_props.h" + + +class FGDDSProps : public FGProtocol { + + std::map<uint32_t,SGPropertyNode_ptr> by_id; + std::map<std::string,SGPropertyNode_ptr> by_path; + + static void setProp(FG_DDS_PROP& prop, SGPropertyNode_ptr p); + +public: + + FGDDSProps() = default; + ~FGDDSProps() = default; + + // open hailing frequencies + bool open(); + + // process work for this port + bool process(); + + // close the channel + bool close(); +};