HLA Servers Examples¶
Here we implement a terrain server (and a client) and a weather server.
Terrain Server¶
Interface¶
The terrain server interface is defined like this:
import "basic_types.stl"
package terrain_server;
// Provides information about the terrain.
class TerrainServer
{
// Where to publish the objects that might be created by this server.
var targetBus : string [static];
// Get information about a line of sight.
//
// @param ref the place for the query.
// @param range maximum distance for the test.
// @param noClouds if true, clouds are ignored.
// @param asObject if true, an LosQuery object is created.
fn requestLos(ref: netn.GeoReferenceVariant, range: Range, noClouds: bool, asObject: bool) -> LoS;
// Get information about the height of the terrain.
//
// @param ref the place for the query.
// @param asObject if true, an HatHotQuery object is created.
fn requestHatHot(ref: netn.GeoReferenceVariant, asObject: bool) -> HatHot;
}
The main functions are requestLos and requestHatHot.
This class uses the types defined in the following STL file:
import "rpr/RPR-Base_v2.0.xml"
import "netn/NETN-BASE.xml"
import "netn/NETN-METOC.xml"
package terrain_server;
// Helper to encode azimuth and elevation information (degrees)
struct AzimElev
{
azimuth : netn.DirectionDegreesFloat32,
elevation : netn.DirectionDegreesFloat32
}
// Helper to encode min/max information (meters)
struct Range
{
min : rpr.MeterFloat64,
max : rpr.MeterFloat64
}
// Line of Sight information/
struct LoS
{
intersection : netn.GeodeticPoint, // point of intersection
range : rpr.MeterFloat32, // distance from the start to the intersection
normalVector : AzimElev, // normal vector at the intersection point
materialCode : u32 // material of the object/terrain at the intersection
}
// Height of/above terrain information
struct HatHot
{
point : netn.GeodeticPoint, // geographic location
hat : netn.AltitudeMeterFloat64, // height above terrain at the point
hot : netn.AltitudeMeterFloat64, // height of terrain at the point
normalVector : AzimElev, // unitary normal vector of the terrain
materialCode : u32 // material of the terrain at the point
}
// Represents a HaT/HoT query that will be continuously updated by its owner
class HatHotQuery
{
var ref : netn.GeoReferenceVariant [static]; // tracked object, position or entity
var hatHot : HatHot; // HaT/HoT information
}
// Represents a line of sight query that will be continuously updated by its owner
class LosQuery
{
var ref : netn.GeoReferenceVariant [static]; // tracked object, position or entity
var los : LoS; // line of sight information
}
Server Implementation¶
This terrain server implementation provides answers with random values.
// === terrain_server_impl.cpp =========================================================================================
// Sen Infrastructure
// Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
// See the LICENSE.txt file for more information.
// © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
// =====================================================================================================================
// generated code
#include "netn/netn-base.xml.h"
#include "netn/netn-metoc.xml.h"
#include "stl/terrain_server.stl.h"
// sen
#include "sen/core/base/compiler_macros.h"
#include "sen/core/base/numbers.h"
#include "sen/core/base/uuid.h"
#include "sen/core/meta/class_type.h"
#include "sen/core/obj/native_object.h"
#include "sen/core/obj/object_source.h"
#include "sen/kernel/component_api.h"
// std
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace terrain_server
{
//------------------------------------------------------------------------------------------------------------//
// Random data generators
//------------------------------------------------------------------------------------------------------------//
/// A very basic random number generator.
template <typename T = float32_t>
[[nodiscard]] T getRand(T low, T high) noexcept
{
return low + static_cast<T>(rand()) / (static_cast<T>(static_cast<T>(RAND_MAX) / (high - low))); // NOLINT
}
/// Returns a HatHot structure filled in with random data.
HatHot makeRandomHatHot()
{
HatHot hatHot {};
hatHot.hat = getRand<float64_t>(0.0, 10000.0);
hatHot.hot = getRand<float64_t>(0.0, 10000.0);
hatHot.materialCode = getRand<uint32_t>(0, 127);
hatHot.normalVector.azimuth = getRand<float32_t>(-180.0f, 180.0f);
hatHot.normalVector.elevation = getRand<float32_t>(-90.0f, 90.0f);
hatHot.point.altitude = getRand<float64_t>(0.0, 10000.0);
hatHot.point.latitude = getRand<float64_t>(-90.0, 90.0);
hatHot.point.longitude = getRand<float64_t>(-180.0, 180.0);
return hatHot;
}
/// Returns a LoS structure filled in with random data.
LoS makeRandomLoS()
{
LoS los {};
los.range = getRand<float64_t>(0.0, 10000.0);
los.materialCode = getRand<uint32_t>(0, 127);
los.normalVector.azimuth = getRand<float32_t>(-180.0f, 180.0f);
los.normalVector.elevation = getRand<float32_t>(-90.0f, 90.0f);
los.intersection.altitude = getRand<float64_t>(0.0, 10000.0);
los.intersection.latitude = getRand<float64_t>(-90.0, 90.0);
los.intersection.longitude = getRand<float64_t>(-180.0, 180.0);
return los;
}
/// Implementation of a HatHotQuery that generates random data.
class HatHotQuery: public HatHotQueryBase
{
public:
SEN_NOCOPY_NOMOVE(HatHotQuery)
public:
using HatHotQueryBase::HatHotQueryBase;
~HatHotQuery() override = default;
public:
void update(sen::kernel::RunApi& /*api*/) override { setNextHatHot(makeRandomHatHot()); }
};
/// Implementation of a LineOfSightQuery that generates random data.
class LineOfSightQuery: public LosQueryBase
{
public:
SEN_NOCOPY_NOMOVE(LineOfSightQuery)
public:
using LosQueryBase::LosQueryBase;
~LineOfSightQuery() override = default;
public:
void update(sen::kernel::RunApi& /*api*/) override { setNextLos(makeRandomLoS()); }
};
//------------------------------------------------------------------------------------------------------------//
// TerrainServerImpl
//------------------------------------------------------------------------------------------------------------//
class TerrainServerImpl final: public TerrainServerBase
{
public:
SEN_NOCOPY_NOMOVE(TerrainServerImpl)
public:
using TerrainServerBase::TerrainServerBase;
~TerrainServerImpl() override
{
env_->remove(objs_); // remove all the objects that we published.
}
protected:
void registered(sen::kernel::RegistrationApi& api) override
{
env_ = api.getSource(getTargetBus()); // open and store the bus where we will publish
}
protected: // TerrainServer methods
[[nodiscard]] LoS requestLosImpl(const netn::GeoReferenceVariant& ref,
const Range& /*range*/,
bool /*noClouds*/,
bool asObject) override
{
addObjectIfNeeded<LineOfSightQuery>(asObject, ref);
return makeRandomLoS();
}
[[nodiscard]] HatHot requestHatHotImpl(const netn::GeoReferenceVariant& ref, bool asObject) override
{
addObjectIfNeeded<HatHotQuery>(asObject, ref);
return makeRandomHatHot();
}
private:
template <typename T>
void addObjectIfNeeded(bool needed, const netn::GeoReferenceVariant& geoReference)
{
if (needed)
{
objs_.push_back(std::make_shared<T>(getName() + ".object" + std::to_string(objs_.size()), geoReference));
env_->add(objs_.back());
}
}
private:
std::shared_ptr<sen::ObjectSource> env_;
std::vector<std::shared_ptr<sen::NativeObject>> objs_;
};
SEN_EXPORT_CLASS(TerrainServerImpl)
} // namespace terrain_server
Client Implementation¶
This terrain client implementation looks for all the servers and continuously requests information.
// === terrain_client_impl.cpp =========================================================================================
// Sen Infrastructure
// Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
// See the LICENSE.txt file for more information.
// © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
// =====================================================================================================================
// generated code
#include "netn/netn-metoc.xml.h"
#include "stl/terrain_client.stl.h"
#include "stl/terrain_server.stl.h"
// sen
#include "sen/core/base/compiler_macros.h"
#include "sen/core/meta/class_type.h"
#include "sen/core/obj/subscription.h"
#include "sen/kernel/component_api.h"
// std
#include <cstdio>
#include <iostream>
#include <memory>
#include <utility>
namespace terrain_server
{
class TerrainClientImpl: public TerrainClientBase
{
SEN_NOCOPY_NOMOVE(TerrainClientImpl)
public:
using TerrainClientBase::TerrainClientBase;
~TerrainClientImpl() override = default;
public:
void registered(sen::kernel::RegistrationApi& api) override
{
sub_ = api.selectAllFrom<TerrainServerInterface>(getServerBus()); // declare an interest on all terrain servers
}
void update(sen::kernel::RunApi& api) override
{
for (auto server: sub_->list.getObjects())
{
std::puts("requesting hat/hot");
server->requestHatHot(netn::GeoReferenceVariant(),
false,
{api.getWorkQueue(),
[](const auto& response)
{
if (response)
{
std::cout << response.getValue() << "\n";
}
}});
}
}
private:
std::shared_ptr<sen::Subscription<TerrainServerInterface>> sub_;
};
SEN_EXPORT_CLASS(TerrainClientImpl)
} // namespace terrain_server
How to run it¶
In one terminal:
In another terminal:
You should see how the client detects the server and prints the query results.
Weather Server¶
Interface¶
The weather server interface is structured similarly to the terrain example:
import "rpr/RPR-Base_v2.0.xml"
import "netn/NETN-BASE.xml"
import "netn/NETN-METOC.xml"
package weather_server;
// Provides information about weather conditions.
class WeatherServer
{
var targetBus : string [static]; // Where to publish the objects created by this server
// Request information about the weather.
//
// @param ref the place where we want to get the information.
// @param asObject true if we also want the response to be an object that auto-updates.
fn reqWeather(ref: netn.GeoReferenceVariant, asObject: bool) -> netn.WeatherCondition;
// Request information about the land surface.
//
// @param ref the place where we want to get the information.
// @param asObject true if we also want the response to be an object that auto-updates.
fn reqLandSurface(ref: netn.GeoReferenceVariant, asObject: bool) -> netn.LandSurfaceCondition;
// Request information about the troposphere.
//
// @param ref the place where we want to get the information.
// @param asObject true if we also want the response to be an object that auto-updates.
fn reqTroposphereLayer(ref: netn.GeoReferenceVariant, asObject: bool, layer: netn.LayerStruct) -> netn.TroposphereLayerCondition;
// Request information about a surface of water.
//
// @param ref the place where we want to get the information.
// @param asObject true if we also want the response to be an object that auto-updates.
fn reqWaterSurface(ref: netn.GeoReferenceVariant, asObject: bool) -> netn.WaterSurfaceCondition;
// Request information about underwater conditions.
//
// @param ref the place where we want to get the information.
// @param asObject true if we also want the response to be an object that auto-updates.
// @param layer the particular layer for which we want information.
fn reqSubsurfaceLayer(ref: netn.GeoReferenceVariant, asObject: bool, layer: netn.LayerStruct) -> netn.SubsurfaceLayerCondition;
}
Implementation¶
The implementation is also very simple. We generate random data as well, but we need to take care of more object types and handle UUIDs.
// === weather_server.cpp ==============================================================================================
// Sen Infrastructure
// Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
// See the LICENSE.txt file for more information.
// © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
// =====================================================================================================================
// generated code
#include "netn/netn-base.xml.h"
#include "netn/netn-metoc.xml.h"
#include "stl/weather_server.stl.h"
// sen
#include "sen/core/base/compiler_macros.h"
#include "sen/core/base/uuid.h"
#include "sen/core/meta/class_type.h"
#include "sen/core/obj/object_source.h"
#include "sen/kernel/component_api.h"
// package
#include "randomize.h"
// std
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace weather_server // NOLINT
{
/// The skeleton of a weather server that produces random data
class WeatherServerImpl final: public WeatherServerBase
{
public:
SEN_NOCOPY_NOMOVE(WeatherServerImpl)
public:
using WeatherServerBase::WeatherServerBase;
~WeatherServerImpl() final { env_->remove(objs_); }
public:
void registered(sen::kernel::RegistrationApi& api) final
{
sen::UuidRandomGenerator()().copy(serviceId_); // create a UUID for us
env_ = api.getSource(getTargetBus()); // get the bus we will use to publish our objects
}
protected: // WeatherServer methods
netn::WeatherCondition reqWeatherImpl(const netn::GeoReferenceVariant& ref, bool asObject) final
{
return makeResponse<netn::WeatherCondition, netn::WeatherBase<>>(ref, asObject);
}
netn::LandSurfaceCondition reqLandSurfaceImpl(const netn::GeoReferenceVariant& ref, bool asObject) final
{
return makeResponse<netn::LandSurfaceCondition, netn::LandSurfaceBase<>>(ref, asObject);
}
netn::TroposphereLayerCondition reqTroposphereLayerImpl(const netn::GeoReferenceVariant& ref,
bool asObject,
const netn::LayerStruct& layer) final
{
std::ignore = layer;
return makeResponse<netn::TroposphereLayerCondition, netn::TroposphereLayerBase<>>(ref, asObject);
}
netn::WaterSurfaceCondition reqWaterSurfaceImpl(const netn::GeoReferenceVariant& ref, bool asObject) final
{
return makeResponse<netn::WaterSurfaceCondition, netn::WaterSurfaceBase<>>(ref, asObject);
}
netn::SubsurfaceLayerCondition reqSubsurfaceLayerImpl(const netn::GeoReferenceVariant& ref,
bool asObject,
const netn::LayerStruct& layer) final
{
std::ignore = layer;
return makeResponse<netn::SubsurfaceLayerCondition, netn::SubsurfaceLayerBase<>>(ref, asObject);
}
private:
template <typename R, typename O>
[[nodiscard]] R makeResponse(const netn::GeoReferenceVariant& ref, bool asObject)
{
auto response = MakeRandom<R>::make(ref); // create a random response
response.environmentObjectId = asObject ? addObject<O>(ref) : netn::UUID {}; // if needed, create the object
return response;
}
template <typename T>
[[nodiscard]] netn::UUID addObject(const netn::GeoReferenceVariant& ref)
{
netn::UUID id;
sen::UuidRandomGenerator()().copy(id); // create an id for our object
const auto name = getName() + ".object" + std::to_string(objs_.size()); // create a name
objs_.push_back(std::make_shared<T>(name, id, name, serviceId_, ref)); // save our new instance
env_->add(objs_.back()); // publish it to the bus
return id;
}
private:
std::shared_ptr<sen::ObjectSource> env_;
std::vector<std::shared_ptr<netn::METOCRootBase<>>> objs_;
netn::UUID serviceId_;
};
SEN_EXPORT_CLASS(WeatherServerImpl)
} // namespace weather_server
How to run it¶
In this case you need to populate the parameters in a more involved manner due to the nature of the data model: