乐于分享
好东西不私藏

AFSim_二次开发_wsf插件及Observer开发指南

AFSim_二次开发_wsf插件及Observer开发指南

wsf插件及Observer开发指南

1

 说明

Observer插件开发是基于AFSim的培训PPT(见翻译版《8_AFSIM_开发培训_观察者》)进行一些简化并增加一些输出内容完成的一个wsf的Demo插件,在此插件基础上可以增加其他内容

1)删除了定义从txt文件中获取UDP组播地址和端口的功能

2)删除了UDP发送到外部接收端的功能(采用控制台输出)

3)增加了AdvanceTime事件监听函数,输出当前时间

4)增加了获取场景所有平台的位置和姿态的方法(在AdvanceTime回调函数中获取

2

 AFSim中的Observer

AFSim提供的Observer机制,允许AFSim的WSF框架来通知某些事件,扩展的插件可以监听这些事件并添加回调处理,这样就不必修改核心框架(即WSF源码),常用于将仿真数据进行收集和记录,并发送到外部处理程序。

AFSim提供的可以订阅的事件都在WsfObserver namespace中,如下图是AFSim项目工程中wsf核心框架提供的所有Observer,当然还有其他插件的项目自定义扩展了Observer,每个Observer中都定义了相关的回调函数原型:

每个Observer中定义的回调原型可以通过以下订阅语法与一个处理函数进行绑定:

mCallbacks.Add(WsfObserver::AdvanceTime(&GetSimulation()).Connect(&JCSObserver::AdvanceTime, this));mCallbacks:JCSObserver的成员变量,类型为UtCallbackHolderAdd:订阅函数,参数为WsfObserver及其子类中声明的可订阅的事件Connect:用于连接事件发生后,需要通知的回调函数,参数为JCSObserver中定义的成员函数

下面来根据文档用vs2019来搭建一个wsf插件,此插件可作为开发其他wsf插件的模板框架。

3

 插件开发步骤

我这里是用VS进行开发的,需要创建dll工程,并引入AFSim的头文件和库文件

1)插件导出宏定义

这里导出宏就沿用示例教程中给的代码即可,并在VS的预处理器中添加hobserver_EXPORTS宏:

// hobserver_export.h#ifndef HOBSERVER_EXPORT_H#define HOBSERVER_EXPORT_H#ifdef HOBSERVER_STATIC_DEFINE#  define HOBSERVER_EXPORT#  define HOBSERVER_NO_EXPORT#else#  ifndef HOBSERVER_EXPORT#    ifdef hobserver_EXPORTS        /* We are building this library */#      define HOBSERVER_EXPORT __declspec(dllexport)#    else        /* We are using this library */#      define HOBSERVER_EXPORT __declspec(dllimport)#    endif#  endif#  ifndef HOBSERVER_NO_EXPORT#    define HOBSERVER_NO_EXPORT #  endif#endif#ifndef HOBSERVER_DEPRECATED#  define HOBSERVER_DEPRECATED __declspec(deprecated)#endif#ifndef HOBSERVER_DEPRECATED_EXPORT#  define HOBSERVER_DEPRECATED_EXPORT HOBSERVER_EXPORT HOBSERVER_DEPRECATED#endif#ifndef HOBSERVER_DEPRECATED_NO_EXPORT#  define HOBSERVER_DEPRECATED_NO_EXPORT HOBSERVER_NO_EXPORT HOBSERVER_DEPRECATED#endif#if 0 /* DEFINE_NO_DEPRECATED */#  ifndef HOBSERVER_NO_DEPRECATED#    define HOBSERVER_NO_DEPRECATED#  endif#endif#endif/* HOBSERVER_EXPORT_H */

2)插件注册类

插件注册一般来说是比较固定的写法,除非有特殊的处理。这里按简单的插件注册书写代码:

// PluginRegistration.cpp#include"hobserver_export.h"#include"RegisterHObserver.hpp"#include"WsfPlugin.hpp"#include"WsfApplication.hpp"#include"WsfApplicationExtension.hpp"#include"UtMemory.hpp"extern "C"{   HOBSERVER_EXPORT voidWsfPluginVersion(UtPluginVersion& aVersion)   {       aVersion = UtPluginVersion(           WSF_PLUGIN_API_MAJOR_VERSION,           WSF_PLUGIN_API_MINOR_VERSION,           WSF_PLUGIN_API_COMPILER_STRING           );   }   HOBSERVER_EXPORT voidWsfPluginSetup(WsfApplication& aApplication)   {       // 注册本插件工程       // 因为RegisterJCSObserver是Scenario扩展类,因此此处使用默认Application扩展       aApplication.RegisterExtension("register_hobserver", ut::make_unique<WsfDefaultApplicationExtension<RegisterHObserver>>());   }}

以上代码即告知WSF框架需要加载我们自己开发的插件。(运行流程如果不清楚,请查看《4_AFSIM_开发培训_传感器》第31页开始的关于AFSim的启动加载流程内容)

上述代码中出现的RegisterHObserver是一个Scenario扩展类,为了将我们的Simulation扩展类HObserver注册到Simulation对象中,代码如下:

// RegisterHObserver.hpp#ifndef RegisterHObserver_HPP#define RegisterHObserver_HPP#include"HObserver.hpp"#include"WsfScenarioExtension.hpp"#include"WsfSimulation.hpp"#include"UtMemory.hpp"class RegisterHObserverpublic WsfScenarioExtension{public:    ~RegisterHObserver() noexcept override = default;    voidSimulationCreated(WsfSimulation& aSimulation)override    {        // Simulation对象创建完成后,注册Simulation扩展        aSimulation.RegisterExtension("hobserver", ut::make_unique<HObserver>());    }};#endif

3)添加回调函数

上面已经将插件注册的代码完成,此时需要完成HObserver类,用于绑定事件回调处理函数。此类的主要功能:

初始化时添加回调函数的绑定

在不同的回调函数中完成数据获取并打印到控制台

// HObserver.hpp#ifndef HObserver_HPP#define HObserver_HPP#include"WsfSimulationExtension.hpp"#include"UtCallbackHolder.hpp"struct UtPluginObjectParameters;class  WsfPlatform;class  WsfSensor;class  WsfTrack;class HObserverpublic WsfSimulationExtension{public:    HObserver();    ~HObserver() noexcept override;    boolInitialize()override;private:    // 以下三个是AFSim教程中的回调函数    voidPlatformAdded(double aSimTime, WsfPlatform* aPlatformPtr);    voidPlatformDeleted(double aSimTime, WsfPlatform* aPlatformPtr);    voidSensorTrackUpdated(double aSimTime, WsfSensor* aSensorPtr, const WsfTrack* aTrackPtr);    // AdvanceTime是新增的回调函数    voidAdvanceTime(double aSimTime);     // 推进时间private:    UtCallbackHolder    mCallbacks;    double              mPreSimTime = 0.0// 记录上一次的仿真时间,时间未推进时不做任何处理};#endif
// HObserver.cpp#include"HObserver.hpp"#include"RegisterHObserver.hpp"// WSF#include"WsfApplication.hpp"#include"observer/WsfPlatformObserver.hpp"#include"observer/WsfTrackObserver.hpp"#include"observer/WsfSimulationObserver.hpp"#include"WsfPlatform.hpp"#include"sensor/WsfSensor.hpp"#include"WsfSimulation.hpp"#include"WsfTrack.hpp"#include<iostream>HObserver::HObserver(){}HObserver::~HObserver() noexcept{}boolHObserver::Initialize(){    mCallbacks.Add(WsfObserver::SensorTrackUpdated(&GetSimulation()).Connect(&HObserver::SensorTrackUpdated, this));    mCallbacks.Add(WsfObserver::SensorTrackInitiated(&GetSimulation()).Connect(&HObserver::SensorTrackUpdated, this));    mCallbacks.Add(WsfObserver::PlatformAdded(&GetSimulation()).Connect(&HObserver::PlatformAdded, this));    mCallbacks.Add(WsfObserver::PlatformDeleted(&GetSimulation()).Connect(&HObserver::PlatformDeleted, this));    mCallbacks.Add(WsfObserver::AdvanceTime(&GetSimulation()).Connect(&HObserver::AdvanceTime, this));    return true;}voidHObserver::PlatformAdded(double aSimTime, WsfPlatform* aPlatformPtr){    std::cout         << "PlatformAdded: " << aSimTime << ", "         << aPlatformPtr->GetName() << ", "         << aPlatformPtr->GetType() << std::endl;}voidHObserver::PlatformDeleted(double aSimTime, WsfPlatform* aPlatformPtr){    std::cout        << "PlatformDeleted: " << aSimTime << ", "         << aPlatformPtr->GetName() << ", "         << aPlatformPtr->GetType() << std::endl;}voidHObserver::SensorTrackUpdated(double aSimTime, WsfSensor* aSensorPtr, const WsfTrack* aTrackPtr){    double longitude, latitude, altitude;    aTrackPtr->GetLocationLLA(latitude, longitude, altitude);    std::cout        << "SensorTrackUpdated: " << aSimTime << ", "        << aSensorPtr->GetName() << ", "        << aSensorPtr->GetPlatform()->GetIndex() << ", "        << aTrackPtr->GetTargetIndex() << ", "        << latitude << ", "        << longitude << ", "        << altitude << std::endl;}voidHObserver::AdvanceTime(double aSimTime){    double deltaTime = aSimTime - mPreSimTime;    if (deltaTime < 0.0000001return// 未推进,则不处理    mPreSimTime = aSimTime;    std::cout << "AdvanceTime: " << aSimTime << std::endl;    // 获取场景所有平台,并获取平台的位置和姿态    int platformCount = GetSimulation().GetPlatformCount();    for (int i = 0; i < platformCount; ++i)     {        auto platform = GetSimulation().GetPlatformEntry(i);        std::cout << "PlatformName: " << platform->GetName() << std::endl;        std::cout << "PlatformType: " << platform->GetType() << std::endl;        // 位置(经纬高)        auto lla = platform->GetLocationLLA();        std::cout   << "PlatformLocation: ";        std::cout   << "    Longitude: " << lla.mLon                    << "    Latitude: " << lla.mLat                    << "    Altitude: " << lla.mAlt << std::endl;        // 姿态(横滚 俯仰 航向)        auto ned = platform->GetOrientationNED();        std::cout   << "PlatformOrientation: ";        std::cout   << "    Roll: " << ned.mPhi                    << "    Pitch: " << ned.mTheta                    << "    Heading: " << ned.mPsi << std::endl;    }}

4)输出插件到指定目录

将代码进行编译生成dll文件。由于wsf框架在加载插件时,会主动查询wsf_plugins目录下的所有插件并加载,因此需将生成的dll拷贝到可执行程序的wsf_plugins目录下(当然也可以直接生成到此目录,减少每次修改后需要手动拷贝的麻烦)

4

 测试

准备工作做完并成功编译出插件dll后,可通过mission.exe程序加载运行,这里通过加载AFSim自带的demos中的simple_scenario场景来测试运行,此场景中只有一个平台,名称是SimpleStriker,类型是BLUE_STRiKER

通过mission.exe以实时仿真方式启动:

启动后会加载我们开发的demo插件:

初始化完成后,控制台输出结果为:

说明:这里面的SensorTrackUpdated回调没有触发,是因为我们选择的场景不涉及Track,因此不会产生Track事件,也就无法触发此回调函数。

4

 后记

上述即完成了插件的开发,完成了获取仿真过程的当前时间和平台信息并打印到控制台中。当然这个插件也可以被仿真引擎加载,与mission.exe加载运行的效果是一样的。

另外,这里只是将获取到的信息打印到控制台,那么既然数据都已经获取到了,当然也可以通过其他方式进行处理,如发送到可视化界面展示、保存到数据库、记录到文件等等。

这里也仅仅是搭建了流程,其他更多仿真过程的内容和数据,需要查看本文第3节所示的Wsf本身提供的那些Observer来自己摸索了。

WsfSimulation添加附加属性

无界面服务端仿真引擎创建方法

可控帧率仿真推演改造和速度控制

wsf框架开发文档生成