|
本帖最后由 5271081文字狱 于 2023-5-24 14:17 编辑
Part 2 - 数据读取
长文预告!
同步发表于https://zhuanlan.zhihu.com/p/631703156,如有不足之处欢迎指正,共同学习进步。系列教程/笔记目的在于帮助广大飞友以及开发者创造属于自己的插件,亦可协助科研人员进行自定义仿真模拟等。
相关链接:Part 1 - 插件开发介绍
XP的飞行数据读写官方称为 Data References,可以通过XPLMDataAccess 这个API获取。
数据访问 API 为您提供了一种通用、灵活、高性能的方式来 在 X-Plane 和其他插件中读取和写入数据。例如 此 API 允许您读取和设置导航无线电,获取飞机位置, 确定当前有效图形帧速率等。
该 API 使用不透明的数据引用工作。。。你不知道它来自哪里,但一旦你拥有它,你就可以阅读数据快速并可能写入它。
接下来这个项目将以获取XP的飞机俯仰角以及飞行时间数据为例展示如何进行数据读取。可以从【这里】下载即开即用的VS项目文件。
该插件运行的效果是在XP主程序目录内建立一个CustomDataRead.txt文件并保存飞行时间以及俯仰角信息,效果如下:
若打开CustomDataRead.txt发现无内容,请在XP中禁用DIY Data Read插件并再次打开txt文件即可。
主程序的代码如下:
- /*********************************************************************
- * @file main.cpp
- * @brief 记录并保存飞行数据。
- *
- * @author zswzy
- * @date 20230523
- *********************************************************************/
- #include <stdio.h>
- #include <string.h>
- #include "XPLMPlugin.h"
- #include "XPLMProcessing.h"
- #include "XPLMDataAccess.h"
- #include "XPLMUtilities.h"
- //======================================================================================
- static FILE* g_output_file;
- XPLMDataRef g_true_theta_deg_ref = NULL; //!< 相对地面的俯仰角 data ref
- XPLMDataRef g_total_flight_time_sec_ref = NULL; //!< 总飞行时间 data ref
- float true_theta_deg = 0.0;
- float total_flight_time_sec = 0.0;
- //======================================================================================
- float CallBackDataReader(float elapsedMe, float elapsedSim, int counter, void* refcon); // 主要功能所在的回调函数
- //======================================================================================
- /**
- * @brief 初始化函数。
- *
- * @param outName 插件名字
- * @param outSig 插件签名
- * @param outDesc 插件描述
- * @return 1: 正常
- */
- PLUGIN_API int XPluginStart(
- char* outName,
- char* outSig,
- char* outDesc)
- {
- // 插件信息
- strcpy(outName, "DIY Data Read");
- strcpy(outSig, "zswzy.dataread");
- strcpy(outDesc, "read flight attitude data");
- // 打开记录文件
- char outputPath[255];
- XPLMGetSystemPath(outputPath);
- strcat(outputPath, "CustomDataRead.txt");
- g_output_file = fopen(outputPath, "w");
- // 寻找 data ref 数据
- g_true_theta_deg_ref = XPLMFindDataRef("sim/flightmodel/position/true_theta"); // 相对地面的俯仰角
- g_total_flight_time_sec_ref = XPLMFindDataRef("sim/time/total_flight_time_sec"); // 总飞行时间
- // 注册回调函数:主要功能
- XPLMRegisterFlightLoopCallback(CallBackDataReader, 1, NULL);
- return 1;
- }
- //======================================================================================
- /**
- * @brief 游戏关闭时执行
- *
- * @return 无
- */
- PLUGIN_API void XPluginStop(void)
- {
- // 注销回调函数
- XPLMUnregisterFlightLoopCallback(CallBackDataReader, NULL);
- // 关闭文件
- fclose(g_output_file);
- }
- //======================================================================================
- /**
- * @brief 插件被Plugin Admin禁用时执行
- *
- * @return 无
- */
- PLUGIN_API void XPluginDisable(void)
- {
- // flush 文件,可在游戏运行时禁用插件以查看数据记录文件
- fflush(g_output_file);
- }
- //======================================================================================
- /**
- * @brief 插件启用时执行
- *
- * @return 无
- */
- PLUGIN_API int XPluginEnable(void)
- {
- return 1;
- }
- //======================================================================================
- /**
- * @brief 提供主要功能:记录并保存数据
- *
- * @param inElapsedSinceLastCall
- * @param inElapsedTimeSinceLastFlightLoop
- * @param inCounter
- * @param inRefcon
- * @return float,XP将在不早于该时间间隔后再次执行该函数。
- */
- float CallBackDataReader(
- float inElapsedSinceLastCall,
- float inElapsedTimeSinceLastFlightLoop,
- int inCounter,
- void* inRefcon)
- {
- // 获取数据
- true_theta_deg = XPLMGetDataf(g_true_theta_deg_ref);
- total_flight_time_sec = XPLMGetDataf(g_total_flight_time_sec_ref);
- // 保存数据至文件
- fprintf(g_output_file, "true pitch: %.4f deg, flight time: %.4f s.\n", true_theta_deg, total_flight_time_sec);
- return 1.0; //!< 执行间隔时间:XP将在不早于该时间间隔后再次执行该函数。
- }
复制代码
笔者已经添加了许多注释,代码基本是自带解释的,很容易能够看懂。下文详解一些重点步骤。
代码详解
主体结构
XPluginStart, XPluginStop,XPluginDisable和XPluginEnable组成了这个dll的主要接口。这四个函数必须要有。
- - XPluginStart:初始化函数,包含注册回调事件,初始化数据等。
- - XPluginStop:游戏关闭时执行的函数,销毁对象,释放内存等。
- - XPluginDisable:插件被Plugin Admin禁用时执行的函数。
- - XPluginEnable:插件被Plugin Admin启用时执行的函数
XPluginDisable和XPluginStop的不同在于,前者是插件被禁用时执行,后者时游戏关闭时执行。插件可以在游戏中多次被启用或者禁用。
数据读取接口
游戏数据的读取需要三个步骤,以俯仰角为例。
1. 声明DataRef全局变量:
- XPLMDataRef g_true_theta_deg_ref = NULL;
复制代码
XPLMDataRef 是XP内置的数据格式,可以理解为保存内部数据的指针,初始化为NULL。从下图可以看到,XPLMDataRef 实际上是空指针, 在XPLMDataAccess.h中定义。
2. 寻找对应数据——查找DataRef
- g_true_theta_deg_ref = XPLMFindDataRef("sim/flightmodel/position/true_theta");
复制代码函数 XPLMFindDataRef 参数为字符串,XP中的每一个内部数据都有唯一的字符串标识,如此处的”sim/flightmodel/position/true_theta“对应飞机相对于正下方地球的俯仰角。
也可以安装DataRefTool或者DataRefEditor插件查找(这俩个也可查找插件机的标识符)。比如,我们在Sim Innovations 查找俯仰角关键字 pitch, 可以发现如下结果。其中甚至包含正副驾驶仪表的俯仰角,并区分数据源(真空或者电子陀螺仪)。 如果搜索俯仰角的数学符号$\theta$对应的 theta,可以发现以下结果。与”相对于正下方地球的俯仰角“相对的还有:仪表显示的俯仰角,OpenGL坐标系俯仰角等,这些量有细微差异。
注意可能会有多种字面意思相近标识符,一定要搞清楚自己需要的是哪个。
3. 获取数据
调用XPLMGetDataf可将刚才获取到的DataRef指针对应的数据保存下来。注意相应的数据格式。(不同数据格式的函数末尾不同,具体请参考XP官方文档)
- float true_theta_deg;
- true_theta_deg = XPLMGetDataf(g_true_theta_deg_ref);
复制代码 回调函数回调函数包括了插件的主要功能,是程序中最重要的部分。函数能够保证插件以一定的时间间隔被执行。
- float CallBackDataReader(
- float inElapsedSinceLastCall,
- float inElapsedTimeSinceLastFlightLoop,
- int inCounter,
- void* inRefcon)
- {
- // 获取数据
- true_theta_deg = XPLMGetDataf(g_true_theta_deg_ref);
- total_flight_time_sec = XPLMGetDataf(g_total_flight_time_sec_ref);
- // 保存数据至文件
- fprintf(g_output_file, "true pitch: %.4f deg, flight time: %.4f s.\n", true_theta_deg, total_flight_time_sec);
- return 1.0; //!< 执行间隔时间:XP将在不早于该时间间隔后再次执行该函数。
- }
复制代码可以看到我们的回调函数会在每次执行的时候获取数据并且写入文件。函数返回值是执行间隔时间,代表XP将在不早于该时间间隔后再次执行该函数。函数的输入包含至上一次被调用的时间,至上一个飞行Loop的时间等等,我们目前并不关心这些参数。可以打开函数原型了解详细信息。
- /*
- * XPLMFlightLoop_f
- *
- * This is your flight loop callback. Each time the flight loop is iterated
- * through, you receive this call at the end.
- *
- * Flight loop callbacks receive a number of input timing parameters. These
- * input timing parameters are not particularly useful; you may need to track
- * your own timing data (e.g. by reading datarefs). The input parameters are:
- *
- * - inElapsedSinceLastCall: the wall time since your last callback.
- * - inElapsedTimeSinceLastFlightLoop: the wall time since any flight loop was
- * dispatched.
- * - inCounter: a monotonically increasing counter, bumped once per flight
- * loop dispatch from the sim.
- * - inRefcon: your own ptr constant from when you regitered yor callback.
- *
- * Your return value controls when you will next be called.
- *
- * - Return 0 to stop receiving callbacks.
- * - Pass a positive number to specify how many seconds until the next
- * callback. (You will be called at or after this time, not before.)
- * - Pass a negative number to specify how many loops must go by until you
- * are called. For example, -1.0 means call me the very next loop.
- *
- * Try to run your flight loop as infrequently as is practical, and suspend it
- * (using return value 0) when you do not need it; lots of flight loop
- * callbacks that do nothing lowers X-Plane's frame rate.
- *
- * Your callback will NOT be unregistered if you return 0; it will merely be
- * inactive.
- *
- */
- typedef float (* XPLMFlightLoop_f)(
- float inElapsedSinceLastCall,
- float inElapsedTimeSinceLastFlightLoop,
- int inCounter,
- void * inRefcon);
复制代码回调函数在XPluginStart中被注册,其中的参数1代表插件运行多久(秒)后注册该函数:
- XPLMRegisterFlightLoopCallback(CallBackDataReader, 1, NULL);
复制代码 小结本文主要展示了获取XP内部DataRef的一般步骤以及如何将数据保存至自定义文件中,并且展示了如何从0开始建立一个的数据读取并记录的插件。关键步骤:程序中包含的几个必要函数,如何查找并保存DataRef,以及如何编写回调函数。
|
|