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 RegisterHObserver: public 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 HObserver: public 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.0000001) return; // 未推进,则不处理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来自己摸索了。
往
期
推
荐

夜雨聆风