楼主: 浮梦旧书海
226 0

[学科前沿] 【Afsim插件开发指南】 [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

学前班

40%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
20 点
帖子
1
精华
0
在线时间
0 小时
注册时间
2018-9-29
最后登录
2018-9-29

楼主
浮梦旧书海 发表于 2025-12-8 16:35:38 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

插件的基本概念

插件(Plug-in),也被称为 addin、add-on、addon 或扩展模块,是一种依照特定接口规范开发的程序组件。它无法独立运行,必须依赖于主程序所提供的运行环境,通过调用宿主系统中的函数库或数据资源来实现功能。

插件的工作机制与原理

在软件架构设计中,插件技术将整体功能划分为两个核心部分:主程序和插件模块。主程序负责提供基础框架以及标准化的接口,而插件则用于实现具体的功能逻辑。这种结构使得软件具备高度灵活性——通过增删或更新插件,即可动态调整系统的功能范围,而无需修改主程序本身。

插件的主要分类

根据实现方式和技术层次的不同,插件通常可分为以下三类:

  • 文本插件:结构简单,类似于批处理脚本,适用于执行基本指令操作。
  • 脚本插件:基于专用脚本语言编写,具有一定的可编程能力,常用于快速功能扩展。
  • 程序插件:利用成熟开发环境构建,如C++或C#等语言编写的DLL模块,功能强大且性能优越。

插件的实际应用场景

插件技术被广泛应用于各类软件平台之中。例如,在早期IE浏览器中,用户可通过安装插件来支持特定文件格式的解析;在网站开发领域,Google Sitemaps 插件可用于生成站点地图,而类似“开心农场”的社交插件则增强了网页互动性与娱乐体验。

采用插件架构的技术优势

引入插件机制可在多个开发环节带来显著益处:

  • 结构清晰:各插件相互独立,职责分明,便于理解与管理。
  • 易于维护与修改:插件通过标准接口与主程序通信,支持热插拔式替换或升级。
  • 高可移植性:插件由小粒度功能单元组成,复用性强,易于迁移到其他项目中。
  • 灵活调整系统结构:新增或移除功能只需增减对应插件,不影响主程序稳定性。
  • 低耦合度:插件之间不直接交互,仅通过主程序协调,降低模块间依赖风险。
  • 开发策略灵活:可根据资源情况选择完整开发或分阶段实施,提升项目可控性。

如何创建 AFSim 插件

以下是基于 Visual Studio 开发 AFSim 兼容插件的具体步骤:

  1. 启动 Visual Studio 并打开开发环境。
  2. 选择“创建新项目”选项。
  3. 选用“空项目”模板进行初始化设置。
  4. 填写项目名称及存储路径后,点击【创建】按钮完成项目建立。
  5. 默认情况下,项目输出类型为可执行文件(.exe),需手动更改为动态链接库(DLL)格式以符合插件要求。
  6. 右键项目名称 → 属性 → 配置属性 → 常规 → 配置类型,将“应用程序(.exe)”更改为“动态库(.dll)”。

关键代码参考

可参照 AFSim 源码中的 wsf_air_combat 工程进行编码实现,主要包含以下核心文件:

  • 用于与宿主程序对接的注册接口定义:【RegisterPlugin.hpp】
  • #ifndef RegisterPlugin_HPP
    #define RegisterPlugin_HPP
    
    #include "WsfPluginTemplate.hpp"
    #include "WsfScenarioExtension.hpp"
    #include "WsfSimulation.hpp"
    #include "UtMemory.hpp"
    
    class RegisterPlugin : public WsfScenarioExtension
    {
    public:
        ~RegisterPlugin() noexcept override = default;
    
        void SimulationCreated(WsfSimulation & aSimulation) override
        {
            // Simulation对象创建完成后,注册Simulation扩展
            // 字符串需要保持唯一性
            aSimulation.RegisterExtension("wsf_plugin_demo", ut::make_unique<WsfPluginTemplate>());
        }
    };
    
    #endif
  • 注册逻辑的具体实现文件:【RegisterPlugin.cpp】
  • #include "wsfplugin_export.h"
    #include "RegisterPlugin.hpp"
    // wsf
    #include "WsfPlugin.hpp"
    #include "WsfApplication.hpp"
    #include "WsfApplicationExtension.hpp"
    #include "UtMemory.hpp"
    
    extern"C"
    {
        WSF_PLUGIN_EXPORT void WsfPluginVersion(UtPluginVersion& aVersion)
        {
            aVersion = UtPluginVersion(
                WSF_PLUGIN_API_MAJOR_VERSION,
                WSF_PLUGIN_API_MINOR_VERSION,
                WSF_PLUGIN_API_COMPILER_STRING
            );
        }
        WSF_PLUGIN_EXPORT void WsfPluginSetup(WsfApplication& aApplication)
        {
            // 注册本插件工程,字符串需要保持唯一性
            // 此处使用默认Application扩展
            aApplication.RegisterExtension("register_wsf_plugin_demo", ut::make_unique<WsfDefaultApplicationExtension<RegisterPlugin>>());
        }
    }
  • 插件模板头文件:【WsfPluginTemplate.hpp】
  • #ifndef WsfPluginTemplate_HPP
    #define WsfPluginTemplate_HPP
    
    #include "WsfSimulationExtension.hpp"
    #include "UtCallbackHolder.hpp"
    
    class  WsfPlatform;
    class  WsfSensor;
    class  WsfTrack;
    
    class WsfPluginTemplate: public WsfSimulationExtension
    {
    public:
        WsfPluginTemplate();
        ~WsfPluginTemplate() noexcept override;
        bool Initialize() override;
    private:
        void PlatformAdded(double aSimTime, WsfPlatform* aPlatformPtr);
        void PlatformDeleted(double aSimTime, WsfPlatform* aPlatformPtr);
        void SensorTrackUpdated(double aSimTime, WsfSensor* aSensorPtr, const WsfTrack* aTrackPtr);
        void AdvanceTime(double aSimTime);                  // 推进时间
    private:
        UtCallbackHolder    mCallbacks;
        double              mPreSimTime = 0.0;
    };
    #endif
  • 回调机制注册实现代码:【WsfPluginTemplate.cpp】
  • #include "WsfPluginTemplate.hpp"
    #include "RegisterPlugin.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"
            
    WsfPluginTemplate::WsfPluginTemplate()
    {}
    WsfPluginTemplate::~WsfPluginTemplate() noexcept
    {}
    bool WsfPluginTemplate::Initialize()
    {
        mCallbacks.Add(WsfObserver::SensorTrackUpdated(&GetSimulation()).Connect(&WsfPluginTemplate::SensorTrackUpdated, this));  
        mCallbacks.Add(WsfObserver::SensorTrackInitiated(&GetSimulation()).Connect(&WsfPluginTemplate::SensorTrackUpdated, this));
        mCallbacks.Add(WsfObserver::PlatformAdded(&GetSimulation()).Connect(&WsfPluginTemplate::PlatformAdded, this));
        mCallbacks.Add(WsfObserver::PlatformDeleted(&GetSimulation()).Connect(&WsfPluginTemplate::PlatformDeleted, this));
        mCallbacks.Add(WsfObserver::AdvanceTime(&GetSimulation()).Connect(&WsfPluginTemplate::AdvanceTime, this));
        std::cout << "WsfPluginTemplate plugin loaded!" << std::endl;
        return true;
    }
    void WsfPluginTemplate::PlatformAdded(double aSimTime, WsfPlatform* aPlatformPtr)
    {
        std::cout
            << "PlatformAdded: " << aSimTime << ", "
            << aPlatformPtr->GetName() << ", "
            << aPlatformPtr->GetType() << std::endl;
    }
    void WsfPluginTemplate::PlatformDeleted(double aSimTime, WsfPlatform* aPlatformPtr)
    {
        std::cout
            << "PlatformDeleted: " << aSimTime << ", "
            << aPlatformPtr->GetName() << ", "
            << aPlatformPtr->GetType() << std::endl;
    }
    void WsfPluginTemplate::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;
    }
    void WsfPluginTemplate::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;
        }
    }
  • DLL导出宏定义头文件:【wsfplugin_export.h】
  • #ifndef WSF_PLUGIN_EXPORT_H
    #define WSF_PLUGIN_EXPORT_H
    
    #ifdef WSF_PLUGIN_STATIC_DEFINE
    #  define WSF_PLUGIN_EXPORT
    #  define WSF_PLUGIN_NO_EXPORT
    #else
    #  ifndef WSF_PLUGIN_EXPORT
    #    ifdef wsfplugin_EXPORTS
    /* We are building this library */
    #      define WSF_PLUGIN_EXPORT __declspec(dllexport)
    #    else
    /* We are using this library */
    #      define WSF_PLUGIN_EXPORT __declspec(dllimport)
    #    endif
    #  endif
    
    #  ifndef WSF_PLUGIN_NO_EXPORT
    #    define WSF_PLUGIN_NO_EXPORT
    #  endif
    #endif
    
    #ifndef WSF_PLUGIN_DEPRECATED
    #  define WSF_PLUGIN_DEPRECATED __declspec(deprecated)
    #endif
    
    #ifndef WSF_PLUGIN_DEPRECATED_EXPORT
    #  define WSF_PLUGIN_DEPRECATED_EXPORTWSF_PLUGIN_EXPORTWSF_PLUGIN_DEPRECATED
    #endif
    
    #ifndef WSF_PLUGIN_DEPRECATED_NO_EXPORT
    #  define WSF_PLUGIN_DEPRECATED_NO_EXPORTWSF_PLUGIN_NO_EXPORTWSF_PLUGIN_DEPRECATED
    #endif
    
    #if 0 /* DEFINE_NO_DEPRECATED */
    #  ifndef WSF_PLUGIN_NO_DEPRECATED
    #    define WSF_PLUGIN_NO_DEPRECATED
    #  endif
    #endif
    
    
    #endif/* WSF_PLUGIN_EXPORT_H */

项目环境配置说明

为确保插件能正确编译并加载,需完成如下配置流程:

  • 创建名为 afsimsdk 的目录,用于存放从 AFSim 源码中提取的所有头文件和静态库文件。该目录是进行二次开发的核心依赖环境。
  • 可通过提供的 Python 脚本自动提取所需头文件:
  • 使用方法: python extract_hpp.py -s "C:\path\to\src" -d "C:\path\to\dst"

    其中,src 参数指向 AFSim 项目的源码路径(如:swdev\src)。

    import argparse
    import os
    import shutil
    import json
    from pathlib import Path
    
    def main():
        parser = argparse.ArgumentParser(description="Extract .hpp files preserving folder structure and print folders.")
        parser.add_argument("-s", "--src", default=".", help="Source root folder to scan (default: current directory)")
        parser.add_argument("-d", "--dst", default=r"c:\baidunetdiskdownload\LX\extracted_hpp", help="Destination root (default: c:\\baidunetdiskdownload\\LX\\extracted_hpp)")
        args = parser.parse_args()
    
        src_path = Path(args.src).resolve()
        dst_path = Path(args.dst).resolve()
        dst_path.mkdir(parents=True, exist_ok=True)
    
        # 创建一个用于存储提取后文件夹路径的缓存文件
        cache_file = dst_path / "extracted_paths.json"
        
        # 如果缓存文件存在,则加载已有的路径信息
        if cache_file.exists():
            with open(cache_file, 'r', encoding='utf-8') as f:
                cached_data = json.load(f)
            seen_dirs = cached_data.get("seen_dirs", [])
            copied_files = cached_data.get("copied_files", [])
        else:
            seen_dirs = []
            copied_files = []
    
        copied = 0
    
        for root, dirs, files in os.walk(src_path):
            root_path = Path(root)
            # 输出并记录所有遍历到的文件夹路径
            print(str(root_path))
            if str(root_path) not in seen_dirs:
                seen_dirs.append(str(root_path))
            
            for fname in files:
                if fname.lower().endswith(".hpp"):
                    src_file = root_path / fname
                    rel = src_file.relative_to(src_path).parent  # 相对目录
                    target_dir = dst_path / rel
                    target_dir.mkdir(parents=True, exist_ok=True)
                    
                    # 复制文件
                    shutil.copy2(src_file, target_dir / fname)
                    copied += 1
                    
                    # 记录复制的文件路径
                    copied_file_path = str(target_dir / fname)
                    if copied_file_path not in copied_files:
                        copied_files.append(copied_file_path)
    
        # 保存路径信息到缓存文件
        cache_data = {
            "seen_dirs": seen_dirs,
            "copied_files": copied_files
        }
        
        with open(cache_file, 'w', encoding='utf-8') as f:
            json.dump(cache_data, f, ensure_ascii=False, indent=2)
    
        print(f"\n扫描完成。总共复制 .hpp 文件: {copied}")
        print(f"目标位置: {dst_path}")
        print(f"缓存文件位置: {cache_file}")
    
    if __name__ == "__main__":
        main()
  • \bin\lib 目录下的所有 lib 文件复制到本地 sdk 库目录中。
  • 在项目中添加头文件搜索路径,依据编译错误提示定位缺失头文件,并关联至提取后的 afsimsdk 路径。

宏定义设置

  • wsfplugin_EXPORTS:此宏用于标识当前构建的是插件 DLL,在【wsfplugin_export.h】中起作用。
  • PROMOTE_HARDWARE_EXCEPTIONS:对应版本标识 win_1929_64bit_release-hwe,若未定义会导致生成的插件版本信息不完整,进而无法通过宿主程序的版本校验,最终导致加载失败。

库文件引用配置

  • 可以预先配置库搜索路径,也可在引用时直接指定完整路径+库名的方式避免全局配置。
  • 在编译过程中可能出现链接错误,应根据错误提示逐步添加所需的库文件。

插件编译与生成

完成代码和配置后,右键项目选择“生成”,输出的 DLL 文件将按设定路径生成。

插件验证方法

将生成的 DLL 文件复制到 AFSim 的插件目录:swdev\BUILD\Release\wsf_plugins

随后进入 swdev\BUILD\Release 目录,使用命令行工具运行 mission.exe -rt。该命令不会加载任何场景文件,但会尝试初始化所有插件,可用于检测插件是否成功加载。

若插件正常加载,控制台将显示相应日志信息。

总结

插件技术以其良好的扩展性、模块化结构和高效的维护特性,已成为现代软件开发的重要手段之一。通过合理运用插件架构,不仅可以提升开发效率,还能增强系统的灵活性与可持续演进能力。

插件DLL已成功加载,文件输出显示在插件初始化函数【WsfPluginTemplate::Initialize()】中打印了【WsfPluginTemplate plugin loaded!】,表明插件正常运行。

初始化完成并确认加载无误后,即可基于当前插件项目开展后续功能开发与扩展工作。

相关参考资料:

AFSim_二次开发_创建AFSim开发环境

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:sim Application Simulation Extension inversion

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
扫码
拉您进交流群
GMT+8, 2026-1-20 00:42