资讯动态

打造跨平台数采框架【二】・服务化与插件化

发布时间:2026/9/8 19:50:01 来源:尧图企业网站定制
服务化相较于DATSv4DATSv5最明显的变化在于引入了前后端分离架构像开发Web一样开发数采框架UI跟数采服务基于JSON格式的数据进行交互不同产品可选择不同的UI技术路线。另一方面数采主程序支持部署为Windows服务/Systemd服务一来可在进入操作系统后自动启动以实现无人值守运行二来还可以在不支持UI的操作系统中静默运行。插件化DATSv5延续了插件化思想在前代驱动库的基础上增加支持门禁、视频等插件。仪器库保留仪器驱动考虑到对接的仪器中百分之五十以上都是Modbus协议此前为每种仪器都要开发一个驱动的做法不够灵活直接创建Modbus驱动的仪器再配置因子和状态又太过繁琐于是引入「仪器模板」的概念将原先的驱动库升级为仪器库通过XML文件配置驱动、仪器名称、地址、因子和状态等信息。!-- Config 读取最大个数,写入最大个数,16位字节高低位,32位字节高低位,是否只读配置的寄存器,是否只读配置的寄存器,是否标准写入--DeviceTemplateIdKXHL_CX-IR002SName空调控制器DriverModicon_ModbusRTUAddress1Config120,32,21,2143,1,1Brand科星互联ModelCX-IR002SDescription红外控制器DataChannelsFactorName空调状态LinkInfo3,4,66,0//DataChannelsStatusChannelGroupsStatusChannelGroupName状态StatusChannelsStatusName空调电源LinkInfo3,4,66,0ParseWayValDescriptionMappersMapperValue0Description电源关闭/MapperValue1Description电源开启//DescriptionMappers/StatusStatusName空调温度LinkInfo3,4,67,0Unit℃/StatusName空调模式LinkInfo3,4,68,0ParseWayValDescriptionMappersMapperValue0Description自动/MapperValue1Description制冷/MapperValue2Description除湿/MapperValue3Description送风/MapperValue4Description制热//DescriptionMappers/StatusStatusName空调风速LinkInfo3,4,69,0ParseWayValDescriptionMappersMapperValue0Description自动风速/MapperValue1Description一档风/MapperValue2Description二档风/MapperValue3Description三档风//DescriptionMappers/StatusStatusName灯光状态LinkInfo3,4,70,0ParseWayValDescriptionMappersMapperValue0Description关灯/MapperValue1Description开灯//DescriptionMappers/Status/StatusChannels/StatusChannelGroup/StatusChannelGroups/DeviceTemplate通过类DeviceTemplateLoader加载XML模板后创建驱动实例挂载到仪器。状态值有多中解析方式数值按位解析成字符按数值解析成字符日期当采集到状态值后查找XML中对应的DescriptionMappers结合解析方式将值翻译成描述。门禁视频插件对于站房来说门禁设备和视频监控的品牌都是固定的常见的品牌有海康威视HikVision、大华等。DATSv4中虽然也定义了接口却依然在主程序中强引用了不同品牌的实现项目并没有做到物理上的隔离 导致发布后的程序包中携带了所有的SDK异常庞大。因此在DATSv5中将门禁和视频服务抽象为插件动态加载public interface IAccessControlService : IDisposable { bool IsLogin { get; } bool Init(); bool Login(LoginConfig loginConfig, out string errMsg); bool Logout(LoginConfig loginConfig, out string errMsg); …… }/// summary /// 视频插件接口 /// /summary public interface IVideoPlugin : IDisposable { /// summary /// 初始化 /// /summary /// returns/returns bool Init(); /// summary /// 读取硬盘录像机中的摄像头配置返回摄像头配置源DTO列表 /// /summary /// param namevideoRecorderConfig硬盘录像机配置/param /// returns摄像头配置源DTO列表/returns TaskListCameraConfigSourceDTO FindCamerasFromRecoder(VideoRecorderConfigDTO videoRecorderConfig); /// summary /// 获取画面截图 /// /summary /// param namecameraConfig摄像头配置/param /// param namesavePath图片保存路径/param void GetCapture(CameraConfigDTO cameraConfig, string savePath); /// summary /// 通过录像机保存视频 /// /summary /// param namecameraId摄像头ID/param /// param namevideoRecorderConfig录像机配置/param /// param nameseconds录制时长/param /// param namesavePath视频保存路径/param /// returns/returns void SaveVideo(string cameraId, VideoRecorderConfigDTO videoRecorderConfig, int seconds, string savePath); }主程序根据品牌加载相应的插件以视频插件为例发现视频插件/// summary /// 从指定目录发现插件 /// /summary /// returns有效插件的数量/returns public int LoadFromDirectory() { Type interfaceType typeof(IVideoPlugin); var pluginRootDir new DirectoryInfo(_pluginDir); if (pluginRootDir.Exists) { _supportedPlugins.Clear(); var log new StringBuilder(); FileInfo[] files pluginRootDir.GetFiles(DATS.VideoManage.*.dll, SearchOption.AllDirectories); foreach (var file in files) { try { var assembly Assembly.LoadFrom(file.FullName); // 得到接口和实现类 var types assembly.GetTypes().Where(p interfaceType.IsAssignableFrom(p) !p.IsAbstract).ToList(); foreach (var type in types) { var metadataAttribute AttributeHelper.ParseVideoPluginMetadataAttribute(type); if (metadataAttribute null) { continue; } if (_supportedPlugins.ContainsKey(metadataAttribute.Id)) { if (log.Length 0) { log.Append(、); } log.Append($[{metadataAttribute.Id}:{type.Assembly.FullName}]); continue; } var pluginMetadata new VideoPluginMetadata(metadataAttribute, type); _supportedPlugins.Add(pluginMetadata.Id, pluginMetadata); } } catch { } } if (log.Length 0) { LogUtil.WriteLog(${this.GetType()}.{nameof(LoadFromDirectory)},插件{log}名称重复); } } return _supportedPlugins.Count; }创建插件/// summary /// 创建插件实例 /// /summary /// param namepluginName插件名/param /// param nameerrMsg异常信息/param /// returnsIVideoPlugin/returns public IVideoPlugin CreatePlugin(string pluginName, out string errMsg) { // 初始化输出参数 errMsg null; // 参数Check if (string.IsNullOrEmpty(pluginName)) { errMsg 插件名称不能为空; return null; } // 找到插件文件信息 if (!_supportedPlugins.TryGetValue(pluginName, out var pluginMetadata) || !File.Exists(pluginMetadata.TargetType.Assembly.Location)) { errMsg $插件[{pluginName}]无效; return null; } // 根据给定的类型创建一个对象 var instance TryCreateInstanceIVideoPlugin(pluginMetadata.TargetType, out var err); if (instance null) { errMsg err.Message; } return instance; }

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价