简介:这份文档面向飞行试验数据处理人员、系统管理员及软件开发者,围绕飞机试飞数据处理管理系统(FTDPMS)展开完整的设计方案论述,重点解决飞行数据种类繁多、命名不一致、检索困难以及安全性与完整性难以保障等问题。内容涵盖需求分析、C/S三层架构设计、系统开发与运行环境、数据库表结构组成以及客户端与应用服务器端的数据库访问流程,并涉及DCOM、MTS等关键技术的实现思路。资源包内为1个docx文档,大小约15KB,属于纯文档型技术资料,适合作为课程设计、毕业设计或工程方案撰写的参考模板。文中对机型机号表、飞行数据表、用户权限表等8个数据库表的设计,以及服务器热备、负载均衡、任务调度等部署模式均有具体说明,可帮助读者快速理解试飞数据管理系统的整体框架与落地要点。目前已有129人学习下载,适合具备一定数据库与C/S开发基础的技术人员研读。
1. 飞机试飞数据处理管理系统:从数据孤岛到统一管控的落地拆解
试飞数据管理最头疼的不是数据量大,而是同一架次的数据散落在不同人的硬盘里,命名规则各搞一套,三个月后连原始文件都找不到。飞机试飞数据处理管理系统(FTDPMS)就是冲着这个问题去的——它把机型机号、飞行数据、处理软件、用户权限、上传下载记录全部收进一个基于 C/S 三层架构的数据库里,用统一入口管起来。这套方案适合试飞院、飞行试验基地的数据管理员和事后数据处理工程师,尤其是那些还在用共享文件夹加 Excel 台账管数据的团队。它不解决实时遥测,只干事后的分类、索引、存储和权限控制,但恰恰是这块最容易被忽视,也最容易在型号任务密集时翻车。
2. C/S 三层架构选型:为什么不用 B/S 而用 DCOM 加 MTS
2.1 三层架构的职责切分与试飞场景的匹配逻辑
试飞数据处理有个硬约束:单次架次产生的原始数据动辄几十 GB,PCM、视频、遥测参数混在一起,客户端需要直接访问磁盘阵列上的大文件。B/S 架构下浏览器没法高效处理这种量级的本地文件操作,而 C/S 的客户端可以拿到文件系统句柄,直接做分块读取和本地缓存。FTDPMS 把系统切成三层:界面层跑在客户端,负责用户交互和本地文件操作;中间层跑在应用服务器上,用 DCOM 做远程调用、MTS 做事务管理;数据层就是 SQL Server 2005,管元数据和索引。
这种切法的好处是,界面层改版不影响业务逻辑,业务逻辑调整不动数据库结构。试飞院常见的情况是,不同型号的数据处理流程有差异,但底层数据表结构可以复用。把业务逻辑抽到中间层,换型号时只改中间层的处理模块,客户端和数据层基本不动。代价是 DCOM 配置在跨网段时容易出玄学问题,后面避坑章节会细说。
2.2 应用服务器端的 DCOM 与 MTS 配置实操
中间层的核心是 DCOM 通信和 MTS 事务。DCOM 负责把客户端的请求序列化后传到应用服务器,MTS 负责保证多个数据库操作要么全成功要么全回滚。配置分两步:先注册 DCOM 组件,再在 MTS 里配置事务属性。
# 在应用服务器上注册 DCOM 组件(以管理员身份运行) regsvr32 "C:\FTDPMS\Server\FTDPMSSvr.dll" # 打开组件服务管理器,配置 DCOM 权限 dcomcnfg # 在"组件服务" -> "计算机" -> "我的电脑" -> "DCOM 配置"中找到 FTDPMSSvr # 右键属性 -> "标识"选项卡 -> 选择"交互式用户"或指定专用账户 # "安全"选项卡 -> 启动和激活权限 -> 自定义 -> 添加客户端运行账户注册完成后,在 MTS 里新建包,把 FTDPMSSvr 组件拖进去,设置事务属性为“需要事务”。这一步决定了数据上传时,如果文件写入磁盘阵列成功但数据库记录失败,MTS 会自动回滚文件写入操作,避免出现“有文件没记录”的脏数据。
-- 在 SQL Server 2005 中创建上传下载记录表,用于 MTS 事务跟踪 CREATE TABLE UploadDownloadLog ( LogID INT IDENTITY(1,1) PRIMARY KEY, UserID INT NOT NULL, FileName NVARCHAR(255) NOT NULL, FileSize BIGINT, UploadTime DATETIME DEFAULT GETDATE(), Status TINYINT DEFAULT 0 -- 0:进行中 1:成功 2:失败 );参数说明:UserID 关联用户表的用户编号,FileName 存原始文件名(含架次号和参数类型),Status 字段配合 MTS 事务,在文件写入前插入状态 0,写入成功后更新为 1,失败则 MTS 回滚整个事务。常见做法是再加一个触发器,当 Status 变为 2 时自动清理磁盘上的残留文件。
2.3 客户端数据访问流程与连接管理
客户端不直接连数据库,所有 SQL 请求都通过 DCOM 发给应用服务器。这样做的好处是数据库连接池集中在中间层管理,客户端数量增加时不会把 SQL Server 的连接数打满。客户端的数据模块用 Delphi 2007 的 MIDAS 技术,通过 TClientDataSet 和 TDataSetProvider 做数据传递。
// Delphi 客户端通过 DCOM 连接应用服务器的核心代码 procedure TfrmMain.ConnectToAppServer; var Proxy: IFTDPMSSvr; begin try // 创建 DCOM 代理,指定应用服务器 IP Proxy := CoFTDPMSSvr.CreateRemote('192.168.1.100'); // 设置连接超时,试飞院网络环境复杂,建议 30 秒 Proxy.SetTimeout(30000); // 验证用户权限,传入加密后的用户凭证 if Proxy.ValidateUser(edtUser.Text, edtPass.Text) then FConnected := True else ShowMessage('用户验证失败,请检查权限配置'); except on E: Exception do ShowMessage('连接应用服务器失败:' + E.Message); end; end;逻辑说明:CreateRemote 指定应用服务器的 IP 地址,ValidateUser 在中间层做权限校验,返回布尔值。参数 30000 是超时毫秒数,试飞院内部网络如果跨了防火墙或者走了光纤通道,建议调到 60000。注意 DCOM 调用失败时异常信息往往很模糊,常见的是“拒绝访问”或“RPC 服务器不可用”,前者查 DCOM 权限配置,后者查网络和防火墙的 135 端口。
3. 八个核心数据表的设计与 SQL Server 2005 落地
3.1 机型机号表与飞行数据表的主外键关系
机型机号表是整条数据链的起点,飞行数据表通过机号外键关联到具体飞机。试飞数据的特点是同一架飞机在不同架次、不同科目下产生的数据要能按机号聚合,所以机号表的主键设计成“机型代码+机号”的复合主键,避免不同机型出现相同机号时冲突。
-- 机型机号表 CREATE TABLE AircraftInfo ( AircraftType CHAR(4) NOT NULL, -- 机型代码,如 Y20、J20 AircraftNo CHAR(6) NOT NULL, -- 机号,如 001、002 Description NVARCHAR(200), CONSTRAINT PK_Aircraft PRIMARY KEY (AircraftType, AircraftNo) ); -- 飞行数据表,通过复合外键关联机型机号表 CREATE TABLE FlightData ( DataID INT IDENTITY(1,1) PRIMARY KEY, AircraftType CHAR(4) NOT NULL, AircraftNo CHAR(6) NOT NULL, FlightDate DATE NOT NULL, SortieNo INT NOT NULL, -- 架次号 DataType TINYINT NOT NULL, -- 1:PCM 2:视频 3:遥测参数 4:其他 FilePath NVARCHAR(500) NOT NULL, -- 磁盘阵列上的相对路径 FileSize BIGINT, UploadUserID INT NOT NULL, CONSTRAINT FK_FlightData_Aircraft FOREIGN KEY (AircraftType, AircraftNo) REFERENCES AircraftInfo(AircraftType, AircraftNo) );参数说明:DataType 用 TINYINT 而不是字符串,是为了在索引和查询时减少 I/O。FilePath 存相对路径,比如\2024\Y20\001\sortie_015\,这样磁盘阵列迁移时不用改数据库。UploadUserID 关联用户表,用于追溯数据来源。常见做法是在 FlightData 表上建一个组合索引(AircraftType, AircraftNo, FlightDate),试飞数据查询十有八九是按机号加日期范围来的。
3.2 用户权限表与软件库信息表的联动设计
用户处理权限表控制的是“谁能处理哪架飞机的数据”,这个粒度在试飞院很关键——不同型号的数据处理人员往往有严格的隔离要求。权限表不直接存用户 ID 和机号的笛卡尔积,而是用角色做中间层,减少数据量。
-- 用户表 CREATE TABLE UserInfo ( UserID INT IDENTITY(1,1) PRIMARY KEY, UserName NVARCHAR(50) NOT NULL UNIQUE, PasswordHash NVARCHAR(128) NOT NULL, -- SHA1 加盐哈希 RoleID INT NOT NULL, -- 1:管理员 2:数据处理员 3:只读用户 IsActive BIT DEFAULT 1 ); -- 用户处理权限表,按角色+机型授权 CREATE TABLE UserPermission ( PermissionID INT IDENTITY(1,1) PRIMARY KEY, RoleID INT NOT NULL, AircraftType CHAR(4) NOT NULL, CanUpload BIT DEFAULT 0, CanDownload BIT DEFAULT 1, CanDelete BIT DEFAULT 0, CONSTRAINT FK_Permission_Role FOREIGN KEY (RoleID) REFERENCES UserInfo(RoleID) );逻辑说明:RoleID 在 UserInfo 里是单个值,在 UserPermission 里可以有多条记录,实现“一个角色对多个机型有不同权限”。CanUpload、CanDownload、CanDelete 三个位控制具体操作。软件库信息表则存上传的算法控件和处理软件,字段包括软件名称、版本、适用机型、上传用户、文件路径。这两张表通过 RoleID 和 AircraftType 联动,客户端登录后先查权限表,再决定界面上哪些按钮可点。
3.3 上传下载信息表与 CA 提示信息表的审计用途
上传下载信息表是审计的核心,每次文件操作都要留痕。CA 提示信息表用于在网用户之间的广播或点对点消息,比如“Y20 第 15 架次数据已上传,请处理人员注意查收”。这两张表在试飞任务密集时写入频率很高,需要注意索引和分区。
-- 上传下载信息表,按月分区存储 CREATE TABLE UploadDownloadLog ( LogID BIGINT IDENTITY(1,1) PRIMARY KEY, UserID INT NOT NULL, DataID INT, -- 关联 FlightData,可为空(软件上传时为空) OperationType TINYINT NOT NULL, -- 1:上传 2:下载 3:删除 OperationTime DATETIME DEFAULT GETDATE(), ClientIP VARCHAR(15), Remark NVARCHAR(200) ); -- 按操作时间建索引,审计查询通常按时间范围 CREATE NONCLUSTERED INDEX IX_UploadDownload_Time ON UploadDownloadLog(OperationTime DESC) INCLUDE (UserID, OperationType);参数说明:ClientIP 存客户端 IP,用于追溯操作来源。Remark 字段留给用户填备注,比如“补传第 3 段 PCM 数据”。索引用了 DESC 排序,因为审计查询通常看最近的操作。CA 提示信息表结构类似,多了 TargetUserID 字段区分广播和点对点。常见做法是给这两张表设一个定期归档作业,比如每月 1 号把 3 个月前的记录移到历史表,避免主表过大影响写入性能。
4. 避坑与排查:DCOM 配置、权限继承和大文件上传的五个血泪教训
4.1 坑一:DCOM 跨网段调用报“RPC 服务器不可用”
现象:客户端和服务器在同一网段时正常,跨到另一个 VLAN 后连接超时,事件查看器里报 RPC 错误。
原因:DCOM 默认使用动态端口范围,跨网段时防火墙只开了 135 端口,后续的动态端口被拦了。
解决:在应用服务器上把 DCOM 的端口限制为固定值。打开 dcomcnfg,找到 FTDPMSSvr 的属性,在“终结点”里添加一个固定端口,比如 50001,然后在防火墙里放行 135 和 50001。客户端不需要改配置,DCOM 会自动协商。
4.2 坑二:MTS 事务超时导致大文件上传回滚
现象:上传超过 2GB 的 PCM 文件时,数据库记录写入成功但文件没传完,MTS 报事务超时。
原因:MTS 默认事务超时是 60 秒,大文件传输时间远超这个值。
解决:在 MTS 包的属性里把事务超时改成 0(无限等待),或者在代码里把文件传输和数据库写入拆成两个阶段——先传文件到临时目录,传完后在一个短事务里做文件移动和记录插入。我一般用后者,因为无限等待会把应用服务器的线程占死。
4.3 坑三:用户权限表更新后客户端不生效
现象:管理员在数据库里改了某个角色的下载权限,但客户端用户重新登录后还是能下载。
原因:客户端在登录时把权限查出来缓存在本地内存,后续操作不再查库。
解决:在中间层加一个权限版本号,每次权限表更新时版本号加一。客户端每次操作前对比本地版本号和服务器版本号,不一致就重新拉取权限。这个改动很小,但能避免很多“我明明改了权限怎么还能用”的扯皮。
4.4 坑四:磁盘阵列路径变更导致所有文件链接失效
现象:磁盘阵列扩容后挂载点从D:\Data变成E:\Data,数据库里存的绝对路径全部失效。
原因:FlightData 表的 FilePath 字段存了绝对路径。
解决:改成存相对路径,客户端拼接时用一个配置文件里的根路径。如果已经存了绝对路径,写一个批量更新脚本,把D:\Data替换成E:\Data。从那以后我每次设计文件存储表都强制用相对路径,这个后悔药吃一次就够了。
4.5 坑五:SQL Server 2005 连接池被客户端耗尽
现象:同时在线客户端超过 50 个后,新客户端连接报“超时时间已到,但是尚未从池中获取连接”。
原因:客户端直连数据库,每个客户端开多个连接,SQL Server 默认最大连接数不够用。
解决:强制所有客户端走中间层的连接池,中间层配置最大连接数为 100,客户端数量再多也只消耗中间层的连接。另外检查客户端代码里有没有忘记关闭 TClientDataSet 的情况,这种泄漏在 Delphi 里很常见。
5. 进阶技巧:用视图和存储过程把数据处理效率再提一档
5.1 用分区视图按机型拆分飞行数据查询
试飞数据积累到一定量后,FlightData 表会变得很大,按机型查询时全表扫描很慢。一个实用的技巧是按机型建分区视图,把不同机型的数据物理上分到不同的表里,逻辑上用视图合并。
-- 为 Y20 和 J20 分别建表,结构同 FlightData CREATE TABLE FlightData_Y20 (...); CREATE TABLE FlightData_J20 (...); -- 建分区视图,客户端查询时不用改 SQL CREATE VIEW v_FlightData AS SELECT * FROM FlightData_Y20 UNION ALL SELECT * FROM FlightData_J20; -- 查询 Y20 的数据时,SQL Server 会自动只扫 FlightData_Y20 SELECT * FROM v_FlightData WHERE AircraftType = 'Y20';逻辑说明:分区视图的关键是每个子表上要有 CHECK 约束,比如CHECK (AircraftType = 'Y20'),这样 SQL Server 才能做分区消除。参数上注意 UNION ALL 不要写成 UNION,后者会去重,性能差很多。这个技巧在试飞院这种机型固定的场景下特别好用,新机型来了就加一张表,视图改一下就行。
5.2 用存储过程封装上传事务,减少网络往返
客户端上传文件时,如果分多步调用中间层(先插记录、再传文件、再更新状态),网络往返次数多,失败概率也大。把整个流程封装成一个存储过程,在数据库端一次调用完成。
CREATE PROCEDURE sp_UploadFlightData @AircraftType CHAR(4), @AircraftNo CHAR(6), @FlightDate DATE, @SortieNo INT, @DataType TINYINT, @FilePath NVARCHAR(500), @FileSize BIGINT, @UserID INT, @NewDataID INT OUTPUT AS BEGIN BEGIN TRANSACTION; BEGIN TRY INSERT INTO FlightData (AircraftType, AircraftNo, FlightDate, SortieNo, DataType, FilePath, FileSize, UploadUserID) VALUES (@AircraftType, @AircraftNo, @FlightDate, @SortieNo, @DataType, @FilePath, @FileSize, @UserID); SET @NewDataID = SCOPE_IDENTITY(); INSERT INTO UploadDownloadLog (UserID, DataID, OperationType, ClientIP) VALUES (@UserID, @NewDataID, 1, CONVERT(VARCHAR(15), CONNECTIONPROPERTY('client_net_address'))); COMMIT TRANSACTION; END TRY BEGIN CATCH ROLLBACK TRANSACTION; THROW; END CATCH END;参数说明:@NewDataID 是输出参数,返回新插入的 DataID,客户端拿到后可以立即用于后续操作。CONNECTIONPROPERTY 函数在 SQL Server 2005 里可能不支持,如果报错就改成从中间层传客户端 IP 进来。存储过程的好处是把事务边界放在数据库端,中间层只负责调用,减少了 DCOM 往返次数。我一般会在中间层加一个重试机制,存储过程调用失败时自动重试两次,间隔 500 毫秒,能解决大部分网络抖动导致的偶发失败。
从那以后我每次设计数据管理系统,都强制把文件路径存相对值、把事务封装在存储过程里、把权限校验放在中间层,这三条已经成了肌肉记忆。希望帮到你。
本文还有配套的精品资源,点击获取