
简介针对GE Digital旗下Proficy Historian工业数据历史记录系统二次开发的C#示例项目面向需要掌握Historian API调用方法的工业信息化工程师与上位机开发者。通过ClientAccessAPI库及样例代码可快速上手数据采集、历史查询、报警事件处理与数据写入等核心接口并结合WPF界面展示查询结果。资源为rar压缩包共18个文件以C#源码8个cs、界面布局2个xaml、项目配置sln/csproj/config及API文档说明txt为主整体仅18KB结构精简。压缩包内ClientAccessAPI目录包含与Historian通信所需的dll库及示例工程开发者可对照学习初始化连接、执行查询等关键流程进而扩展到数据可视化、批量导入导出等定制功能。已有558人学习下载适合具备C#基础、希望快速入门Proficy Historian API的开发者参考实践。1. 用 C# 调 Proficy Historian API这套 Demo 让数据追溯少走三周弯路做工业数据追溯项目时我接手的第一个任务是去摸清生产线上某批产品的工艺参数是否超标。数据源是 Proficy Historian业务方要求出报表、做趋势、给 MES 留接口。当时手上没有现成代码文档又厚又散整整折腾了三周才把数据跑通。后来我把这套 C# proficy Historian API demo 整理成一个可复用的示例包把连接、查询、写入、导出这四个最常用的环节全部拆开每段代码都能独立跑。适合两类人刚接触 Historian 的工艺工程师以及要给追溯或报表系统接历史数据的开发。读完你至少能搭出一个能查数据、能写数据、能落地的 Demo 骨架。2. 开发前先搞懂 Historian 的数据模型三个概念直接影响 API 调用方式2.1 Tag 不是数据库字段理解采样方式才知道怎么查Proficy Historian 的核心数据单元是 Tag标签相当于一个带时间轴的数据通道。每个 Tag 对应一个采集点比如某条产线的反应釜温度、泵出口压力、电机电流。但 Tag 和关系数据库里的字段有个本质区别它自带采样属性和存储策略。我在实际项目中常用的 Tag 类型有三种模拟量Float64、数字量Boolean/Int32、字符串String。查询时要注意字符串 Tag 在 API 里单独有一套取值逻辑不能和模拟量混在一个查询条件下。还有一类是计算 TagCalculated Tag它本身不存储原始数据而是基于其他 Tag 实时计算这种 Tag 在查询时往往有额外的参数要求。除此之外还需要区分“原始采样数据”和“归档压缩数据”。Historian 默认会对采集到的数据做压缩存储如果直接用采样查询去拉数据拿到的可能是被压缩过的值。API 里对应两种查询模式原始查询Raw和采样查询Sampled。查报警和事故追责时用原始查询做趋势分析时用采样查询两者返回的记录密度差别很大。2.2 时间戳和质量码是查询结果里的两个隐藏字段每条 Historian 记录都带四个字段TagName、TimeStamp、Value、Quality。其中 Quality 是很多人第一次接触 API 时忽略的字段。它的取值范围包括 Good、Bad、Uncertain 等。设备断电、信号超量程、采集器重启都会让质量码变成非 Good 值。我在做批次追溯时吃过这个亏查询出来一批温度数据画到图表上才发现中间有一段毛刺。原因是那几分钟设备校准数据质量是 Bad但查询结果里没有过滤。请记住任何跨时间段的查询第一次跑通后先看结果里有没有非 Good 的记录做一个数据清洗逻辑。时间戳的精度是微秒级API 返回的时间是 UTC 还是本地时间取决于服务器的时区设置。这个我在后面踩坑章节里细说这里先记住一点查询时传时间范围要确认用的是服务器时间还是本地时间否则查出来的数据可能整体偏移几个小时。2.3 API 选型.NET SDK、REST API、OLE DB 怎么选Proficy Historian 对外提供了三种主流访问方式选型直接决定 Demo 的代码结构。我把它们的差异整理如下访问方式适用场景主要特点性能表现.NET SDK桌面应用、后台服务、批量处理类型安全、功能全、支持订阅和异步最高适合大量历史数据REST APIWeb 应用、跨平台系统HTTP 协议、JSON 格式、轻量易调试中等受网络开销影响OLE DB ProviderExcel 分析、临时查询、报表SQL 语法、无代码即可用一般适合中小体量如果只是做报表分析OLE DB Provider 配 Excel 就够了如果做 Web 平台优先考虑 REST API但本 Demo 用 .NET SDK 是合理的因为要演示写入和批量操作。SDK 方式可以控制消息集、批量提交、事务回滚这些在 REST API 里实现起来要绕弯。2.4 环境准备先确认 SDK 和授权再写第一行代码开发前按这个清单准备环境确认 Historian 服务器版本和 SDK 组件已安装检查当前用户有数据读取权限确认服务器连接端口开放准备一个可以用于测试的 Tag。// 引用核心程序集不同版本 SDK 程序集名略有差异以安装目录为准 using GE.Proficy.Historian; using GE.Proficy.Historian.Data;程序集一般位于 Historian 安装目录下的 SDK 文件夹。引用后用对象浏览器检查 GServer 类的构造函数能避免后续编译时找不到类型的问题。注意开发机和 Historian 服务器之间的网络延迟不能太高否则连接超时现象会很频繁。3. C# Demo 核心实现连接、查询、写入、导出一串跑通3.1 连接服务器一个连接对象管理整个会话连接是 Historian API 的第一步。SDK 里的 GServer 对象代表一个服务器会话连接成功后后续所有操作都用这个对象。这里给出最常用的连接写法。// 创建连接对象 GServer server new GServer(); // 方式一集成身份验证适用于域环境 server.Connect(historian-server-host); // 方式二显式账号密码验证适用于跨域或非域环境 // server.Connect(historian-server-host, domain\\user, password);用集成身份验证的前提是当前 Windows 用户有 Historian 的数据读取权限。显式账号密码方式会在字符串里暴露密码我一般配合配置文件加密使用。连接是一个有状态的操作用完要调用 server.Disconnect() 释放资源。实际开发中我习惯用 using 块或 try-finally 包裹连接与断开逻辑防止连接泄漏。3.2 查询采样数据拉最近 24 小时整点趋势查询数据是 Demo 的核心功能。下面这段代码查询某个 Tag 在过去 24 小时内每隔 1 小时的采样值返回结果可以直接画趋势图。// 创建数据连接 GDataConnection connection server.CreateDataConnection(); // 创建查询对象 GQuery query connection.CreateQuery(); // 指定要查询的 Tag query.AddTagNames(new string[] { Reactor_Temperature }); // 设置查询类型为采样查询采样周期 1 小时 query.QueryType GQueryType.GSampledQuery; query.SamplingInterval TimeSpan.FromHours(1); // 设置时间范围当前时间往前 24 小时 DateTime endTime DateTime.Now; DateTime startTime endTime.AddHours(-24); query.TimeRange new GTimeRange(startTime, endTime); // 执行查询并获取结果 GRecords records query.Execute();这里说三个参数细节。AddTagNames 支持一次传多个 Tag批量查询性能远好于循环单查。GSampledQuery 是插值采样查询适合画趋势如果是查报警事件改用 GRawQuery 拿原始数据。TimeRange 传入的时间要注意时区问题后面避坑章会给出具体原因。查询返回的 GRecords 对象内部是一个二维结构外层是 Tag内层是该 Tag 的所有记录点。读取时先按 Tag 维度遍历再取时间戳和数值。// 遍历查询结果 foreach (GRecord record in records) { string tagName record.TagName; DateTime timestamp record.TimeStamp; double value Convert.ToDouble(record.Value); string quality record.Quality.ToString(); Console.WriteLine(${tagName}, {timestamp}, {value}, {quality}); }遍历时注意 record.Value 的类型处理模拟量 Tag 时直接转 double 即可。字符串 Tag 用 record.Value.ToString() 获取。每一条记录都要取 Quality 字段做判断非 Good 的数据在统计前要剔除。3.3 写入历史数据把计算出的工艺参数回写到 Historian有些场景需要把分析结果或人工输入的数据写回 Historian。SDK 的写入逻辑用消息集MessageSet实现好处是支持批量提交和事务控制。// 创建消息集对象 GMessageSet messageSet connection.CreateMessageSet(); // 向消息集中添加数据点 messageSet.AddDataValue(Reactor_Temperature, DateTime.Now.AddMinutes(-5), 98.6, Quality.Good); messageSet.AddDataValue(Reactor_Temperature, DateTime.Now.AddMinutes(-4), 99.1, Quality.Good); // 批量提交到服务器 connection.OutputData(messageSet);这里提个建议写入的时间戳不要使用 DateTime.Now因为 Historian 对历史数据写入有一定的时间窗口限制某些版本不允许写入当前时间之后的数据。我一般写入过去几分钟内的数据或者从业务系统里拿到确定的工艺时间。消息集的机制类似于数据库事务先把多个数据点攒在缓冲区最后一次性提交。这样做比逐条写入效率高一个量级。如果写入的 Tag 不存在OutputData 时会报错或写入失败需要在写入前先用查询接口探测 Tag 是否存在。3.4 导出 CSV把查询结果落盘给下游做分析查询拿到数据后最常见的需求是导出成 CSV 给业务或算法团队用。这里给出一个简洁的导出代码片段注意加上质量码字段。// 导出查询结果为 CSV 文件 using (StreamWriter writer new StreamWriter(historian_export.csv)) { writer.WriteLine(TagName,TimeStamp,Value,Quality); foreach (GRecord record in records) { writer.WriteLine(string.Join(,, record.TagName, record.TimeStamp.ToString(yyyy-MM-dd HH:mm:ss.fff), record.Value, record.Quality.ToString())); } }时间戳格式化要精确到毫秒CSV 打开时才不会出现时间重复。如果数据量超过几十万行StreamWriter 默认缓冲够用数据量特别大时可以考虑用 CSV 库异步写入。这个导出逻辑加上前面 3.2 的查询逻辑就是一个完整的“查数据—落文件”工作流。4. 避坑记录连接超时、数据错位、权限不足的五个真实问题4.1 连接超时服务器没开门代码卡到天荒地老现象调用 Connect 后长时间无响应最后弹出超时异常。原因最常见的是防火墙未放行 Historian 服务端口其次是服务器名解析失败。部分环境里 Historian 服务依赖 Windows 服务管理器服务停了也会表现为连接超时。解决先用 telnet 验证端口连通性确认服务器上的 Historian 服务处于运行状态。如果使用主机名连接先测试 DNS 解析是否正常。我习惯在连接代码里设置较短的超时时间快速失败比长时间挂起容易定位问题。4.2 查询结果为空标签存在但一条记录都取不到现象Tag 名称确认无误用测试工具能看到历史数据但 API 查询返回空集合。原因大概率是查询类型与存储方式不匹配。比如用插值采样查询去查一个没有配置采样策略的 Tag或者时间范围刚好落在数据归档周期之外。解决先用原始查询验证该 Tag 在指定时间范围内确有数据再切换到采样查询。原始查询的代码只改一个 QueryType 属性query.QueryType GQueryType.GRawQuery;如果原始查询有数据而采样查询没数据检查 Tag 属性的存储间隔和可接受采样区间确需插值时就以原始数据为准自己在内存里算不依赖 Historian 的插值逻辑。4.3 数据整体偏移一小时时区问题把趋势图拉歪了现象查询得到的记录时间戳比实际工艺时间晚或早一小时且整个数据集偏移量一致。原因这是时区没有对齐的经典症状。Historian 服务器可能配置的是 UTC 时间而应用端用的是本地时间。代码里传入的 DateTime.Now 带本地时区偏移被 SDK 在传输层做了转换。解决统一约定时间基准。我一般把查询和写入的时间戳全部转成 UTC 后在应用层处理最后展示时再转回本地时间。代码里写一个工具方法DateTime serverTime DateTime.UtcNow; query.TimeRange new GTimeRange(serverTime.AddHours(-24), serverTime);这样能绕开大部分因时区设置产生的偏移问题。如果问题还在去看 Historian 服务器的时间区域配置把服务器的时区选项改到和业务系统一致。4.4 写入成功但查不到消息集没有真正落盘现象调用 OutputData 没有报错但重新查询时写入的数据不存在。原因消息集的事务没有提交或者写入的数据点质量码被服务器拒绝。还有一种可能是写入时间戳超出服务器允许写入的最大时间窗口。解决确认写入数据点的 Quality 是 Good 值非 Good 数据会被服务器丢弃。其次检查消息集的提交模式确认调用的是输出并提交的接口而不是只写入缓冲区。最后验证时间戳不要在未来时间轴写入过去 5 分钟内的数据点最稳妥。4.5 权限不足有数据源但 API 拒绝访问现象提示类似“Access Denied”或“Permission denied”的错误但用客户端工具可以正常查数据。原因Historian 的权限模型是分组的用户可能有界面查看权限但没有 SDK 的数据访问权限。企业环境中域用户的权限继承经常出问题。解决联系管理员确认当前账号属于哪个权限组申请对应数据操作权限。如果使用服务账号运行程序确保服务账号在 Historian 中也有显式授权不能因为它是域管理员就默认放行。5. 进阶用法把 Demo 改造成生产级数据服务5.1 查询逻辑改成异步任务界面不再卡死前面 3.2 的查询是同步阻塞式调用。在 WPF 或 WinForms 里跑几十秒的查询界面会直接冻结。我一般把查询包在 Task.Run 里配合 async-await 模式返回结果集。TaskGRecords queryTask Task.Run(() { using (GDataConnection asyncConn server.CreateDataConnection()) { GQuery asyncQuery asyncConn.CreateQuery(); // 参数与同步查询一致 return asyncQuery.Execute(); } }); GRecords result await queryTask;注意 GDataConnection 和 GQuery 都不是线程安全的不能在多线程间共享。Task.Run 内部必须重新创建连接对象用局部变量操作。异步化之后还要控制并发数量避免同时发起几十个查询把 Historian 服务器拖垮。一般我用信号量把并发度限制在 4 到 8 之间。5.2 批量写入的分批策略一万条数据一次提交会失败消息集虽然支持批量提交但单次提交的数据量不是无上限的。写入大量数据时我有一个习惯每 500 条分一批提交批与批之间留 100 毫秒间隔。// 分批写入示例 int batchSize 500; for (int i 0; i allValues.Count; i batchSize) { GMessageSet batchMessageSet connection.CreateMessageSet(); var batch allValues.Skip(i).Take(batchSize); foreach (var value in batch) { batchMessageSet.AddDataValue(value.TagName, value.TimeStamp, value.Value, Quality.Good); } connection.OutputData(batchMessageSet); Thread.Sleep(100); }分批策略能避免两个问题一个是消息集过大导致的内存溢出另一个是写入失败时回滚范围过大。每批数据独立提交失败时可以精确知道哪一批出了问题。这个批大小要根据服务器性能调整我测试过 200 到 1000 之间均有可用空间500 是稳定性较好的中间值。5.3 用插值查询对齐整点数据趋势曲线更干净原始数据的采样时间是不均匀的画趋势图时锯齿严重。除了 3.2 里的 GSampledQuerySDK 还提供了更细粒度的插值控制。设置采样间隔为 5 分钟配合历史插值模式可以得到一条平滑对齐的趋势线。query.QueryType GQueryType.GSampledQuery; query.SamplingInterval TimeSpan.FromMinutes(5); query.SamplingMode GSamplingMode.GLinearInterpolation;线性插值模式适合连续变化的模拟量比如温度、压力。离散量或开关量不适合线性插值要用阶梯式保持模式。这个细节对趋势图质量影响很大。我保存这段经验是因为有一次做批次分析用原始数据画的趋势曲线参差不齐客户要求按整点对齐。改用线性插值采样后图表效果立竿见影。5.4 验证手段用官方工具交叉核对查询结果无论查询还是写入代码跑通第一遍后我习惯用 Historian 自带的浏览器工具核对一批数据打开趋势图控件拉同一时间段同一 Tag目视对比 API 查出的数值是否落点一致。这个方法笨但能快速暴露时区偏移和数据压缩的问题。从那以后我每次写完历史数据的查询逻辑都强制走一遍“先看质量码、再对时间戳、最后对比官方工具”这三个步骤才敢把代码交给业务用。历史数据这东西出错不容易发现但一旦错了后续所有分析都是白做。希望帮到你。本文还有配套的精品资源点击获取