1. 为什么要无界面
AFSim整个框架以wizard、warlock、mission、mystic等几个主要工具构成。wizard为集成开发框架,用于脚本编程后实时展示编辑的场景(scenario)内容,编辑完成后可使用warlock和mission进行仿真模拟,其中warlock可对仿真推演速度进行控制,而mission以尽可能快的方式完成仿真场景的推演,并支持设置仿真次数。warlock是带态势图的应用,mission不带界面只有一个控制台。
在实际项目中,一般会将仿真引擎和态势显示进行分离,即仿真引擎在服务器上跑,而界面显示在客户端上,这样可以利用服务器的高性能,同时执行多个仿真推演。而态势显示可根据需要,采用WebGis或高性能的C++版GIS客户端,并支持更高程度的定制,而不用影响仿真推演的核心部分。
2. 源码分析
对mission源码进行分析,整个仿真的创建包含主要的三个内容:1、创建Application,2、创建Scenario,3、创建Simulation,4、调用Simulation的RunEventLoop进行仿真。如下图:

上图是AFSim的mission的源码,它支持命令行参数读取和解析、启动场景文件配置、性能(profiling)分析配置、多次仿真循环等功能。
3. 最小化改造
服务器部署仿真引擎时,往往都是长时间运行,通过客户端的请求来执行不同的仿真任务,因此mission中的一些功能在服务器上不会用得上,如命令行参数、启动文件配置等。
一般的流程是:
1)客户端选择一个场景,并告知服务端仿真引擎使用此场景文件
2)客户端请求服务端初始化一个仿真
3)服务端根据场景文件创建一个仿真并返回仿真唯一标识给客户端
4)客户端通过标识对服务端仿真进行控制(启动、暂停、恢复、停止、速度控制、FPS控制等)(FPS控制AFSim本身不支持,见后续源码改造内容^_^)
5)服务端仿真引擎在运行过程中通过定义的接口(如XIO、DIS或自定义的接口)对外发送数据(见后续数据推送二次开发)
6)客户端(如态势、仿真状态监控或其他系统)接收到推送的数据进行处理或显示。
根据以上流程,需要建立一个能够支持客户端请求的服务的仿真引擎,因此第一步应该抽出一个比较简单的仿真框架,这里以mission的源码进行简化后得到一个引擎代码,如下图:

以上代码,s_app是AFSim的WsfStandardApplication,当然也可以是自己扩展的Applicatin,但应该保持全局唯一。另外,将1/2/3步放到类的初始化函数中,将第4步放到线程中即可实现多仿真推演过程。以下代码为示例:
HSimApp.h
#ifndef HSimApp_h__
#define HSimApp_h__
#include <QThread>
// AFSim
#include "WsfStandardApplication.hpp"
#include "WsfScenario.hpp"
#include "WsfFrameStepSimulation.hpp"
class HSimApp : public QThread
{
Q_OBJECT
public:
enum State
{
E_Unknown,
E_Initialized,
E_Running,
E_Pausing,
};
enum ControlType
{
E_Start = 1,
E_Pause = 2,
E_Resume = 3,
E_Stop = 4,
E_SetSpeed = 5,
E_SetFPS = 6,
};
HSimApp(const QString &sceneFile, QObject* parent = nullptr);
~HSimApp();
bool init();
void control(ControlType controlType, double value);
// 设置刷新率,默认为25, 1,2,4,10,25,60,90
void setFPS(int fps);
// 设置仿真速度默认为1,倍速,1/64, 1/32, 1/16, 1/8, 1/4, 1/2 1,2,4,8,16,32,64
void setSpeed(double speed);
void start();
void pause();
void resume();
void stop();
private:
static std::unique_ptr<WsfStandardApplication> s_app;
WsfStandardApplication::Options m_options;
std::unique_ptr<WsfScenario> m_scenario = nullptr;
std::unique_ptr<WsfFrameStepSimulation> m_wsfSim = nullptr;
protected:
virtual void run() override;
private:
Property_ReadOnly(QString, Id, ""); // 仿真标识
Property_ReadOnly(double, Speed, 1.0); // 仿真倍速
Property_ReadOnly(int, FPS, 1.0); // 刷新率
Property_ReadOnly(State, State, E_Unknown); // 状态
Property_ReadOnly(QString, SceneFile, ""); // 场景文件
};
#endif // HSimApp_h__HSimApp.cpp
#include "HSimApp.h"
#include <QCoreApplication>
#include <QFileInfo>
#include <QDir>
#include "Util.h"
#include "Log.h"
// afsim
#include "UtException.hpp"
#include "UtLog.hpp"
#include "WsfScenario.hpp"
#include "WsfSimulation.hpp"
#include "WsfSimulationInput.hpp"
#include "WsfStandardApplication.hpp"
// Includes all of the optional projects which can be compiled with WSF
#include "ProfilingCommon.hpp"
#include "ProfilingRegion.hpp"
#include "ProfilingSystem.hpp"
#include "wsf_extensions.hpp"
#include "wsf_version_defines.hpp"
// 全局唯一APP
std::unique_ptr<WsfStandardApplication> HSimApp::s_app = nullptr;
HSimApp::HSimApp(const QString& sceneFile, QObject* parent /*= nullptr*/)
: QThread(parent)
, m_Id(Util::getUid())
{
this->moveToThread(this);
m_SceneFile = sceneFile ;
}
HSimApp::~JCSimApp()
{
stop();
}
bool HSimApp::init()
{
if (s_app == nullptr)
{
ut::SetupApplicationLog("mission", WSF_VERSION, "mission-exception.log");
auto appName = QCoreApplication::instance()->arguments()[0];
char* argv[1];
char* exeName = new char[appName.size() + 1];
memset(exeName, 0, appName.size() + 1);
memcpy(exeName, appName.toStdString().c_str(), appName.size());
argv[0] = exeName;
// 创建仿真应用对象
s_app = std::make_unique<WsfStandardApplication>("mission", 1, argv);
// Load built-in extensions (defined in wsf_extensions.hpp)
// 加载内置扩展
RegisterBuiltinExtensions(*s_app);
// Load optional extensions (defined in wsf_extensions.hpp)
// 加载自定义扩展
RegisterOptionalExtensions(*s_app);
// Register the XIO simulation interface.
// 加载内部XIO通信接口
WSF_REGISTER_EXTENSION(*s_app, xio_interface);
}
// 创建一个场景对象
m_scenario = std::make_unique<WsfScenario>(*s_app);
m_options.mSimType = WsfStandardApplication::cFRAME_STEPPED;
m_options.mInputFiles.push_back(m_SceneFile.toStdString());
try
{
// 读取.txt文件
s_app->ProcessInputFiles(*m_scenario, m_options.mInputFiles);
}
catch (WsfApplication::Exception& e)
{
auto out = ut::log::fatal() << "Could not process input files.";
out.AddNote() << e.what();
return false;
}
m_wsfSim = ut::make_unique<WsfFrameStepSimulation>(*m_scenario, 1);
m_wsfSim->SetRealtime(0, false);
m_wsfSim->SetClockRate(m_Speed);
m_wsfSim->setFrameTime(m_FPS);
if (!s_app->InitializeSimulation(m_wsfSim.get())) // 初始化仿真对象
{
return false;
}
m_State = E_Initialized;
return true;
}
void HSimApp::control(ControlType controlType, double value)
{
switch (controlType)
{
case HSimApp::E_Start:
this->start();
break;
case HSimApp::E_Pause:
this->pause();
break;
case HSimApp::E_Resume:
this->resume();
break;
case HSimApp::E_Stop:
this->stop();
break;
case HSimApp::E_SetSpeed:
this->setSpeed(value);
break;
case HSimApp::E_SetFPS:
this->setFPS(value);
break;
default:
break;
}
}
void HSimApp::setFPS(int fps)
{
Q_ASSERT(fps > 0);
if (m_FPS != fps)
{
m_FPS = fps;
// m_wsfSim->setFrameTime(1.0 / m_FPS);(这个函数需要改造源码,后续分析)
}
}
void HSimApp::setSpeed(double speed)
{
Q_ASSERT(speed > 0);
if (abs(m_Speed - speed) > 0.000001)
{
m_Speed = speed;
m_wsfSim->SetClockRate(m_Speed);
}
}
void HSimApp::start()
{
if (m_State == E_Initialized)
{
m_wsfSim->Start();
m_State = E_Running;
}
}
void HSimApp::pause()
{
if (m_State == E_Running)
{
m_wsfSim->Pause();
m_State = E_Pausing;
}
}
void HSimApp::resume()
{
if (m_State == E_Pausing)
{
m_wsfSim->Resume();
m_State = E_Running;
}
}
void HSimApp::stop()
{
if (m_State == E_Initialized)
{
m_wsfSim->Complete(m_wsfSim->GetSimTime());
}
else if (m_State == E_Running)
{
m_wsfSim->RequestTermination();
wait();
}
else if (m_State == E_Pausing)
{
m_wsfSim->SetEndTime(m_wsfSim->GetSimTime());
m_wsfSim->Resume();
wait();
}
}
void HSimApp::run()
{
WsfStandardApplication::SimulationResult result;
result = s_app->RunEventLoop(m_wsfSim.get(), m_options);
syslog->info(QStringLiteral("仿真执行完成 <%1>").arg(m_Id));
}代码仅供参考,不具有工程价值!
往期推荐
飞腾D2000麒麟V10国防版下编译
基于wsf插件扩展内置platform
服务端仿真引擎框架
arm64版的麒麟V10服务器docker容器集成后台仿真引擎

评论