西门子 PLC 与 C# 通信_项目实战

发布时间:2026/9/13 18:33:54

西门子 PLC 与 C# 通信_项目实战 一、项目概述该项目是一个基于 WPF 和 .NET Framework 4.7.2 开发的西门子 S7-1200 PLC 数据采集应用程序用于某周期性数据采集场景。核心功能单路实时采集每秒读取 PLC 指定地址的数值实时刷新界面自动周期存盘按配置间隔默认 10 分钟将瞬时值和周期最低值写入 CSV手动单次采集用户可随时触发一次采集并独立存盘秒级高精度采集可选的秒级 CSV 异步写入不影响主采集路径长期稳定运行支持 8 小时以上连续运行异常不导致进程退出技术栈组件版本说明框架.NET Framework 4.7.2WPF 桌面应用PLC 通信S7netplus 0.20.0西门子 S7 协议库UI 框架WPF现代化界面设计二、项目结构S7DataCollector/ ├── Config/ │ └── AppSettings.cs # 集中配置读取App.config 默认值 ├── Models/ │ └── DataRecord.cs # 采集记录模型 ├── Services/ │ ├── CsvLogger.cs # 自动/手动 CSV 写入器 │ ├── ErrorLogger.cs # 错误日志写入器 │ ├── PlcService.cs # PLC 通信服务封装 │ └── SecondCsvWriter.cs # 秒级 CSV 异步写入器 ├── App.xaml / App.xaml.cs # 应用入口与全局异常处理 ├── MainWindow.xaml / MainWindow.xaml.cs # 主窗口与业务逻辑 ├── App.config # 运行时配置 └── S7DataCollector.csproj # 项目配置三、核心架构3.1 单路采集设计程序采用单路采集架构即只有一个定时器负责读取 PLC读取结果同时供给三个消费端UiTimer (1秒) ──→ Read PLC ──→ ① UI 实时显示 ──→ ② 周期最低值累加器 ──→ ③ 秒级 CSV 入队这种设计确保了每秒只读取一次 PLC降低通信负荷所有消费端使用同一数据源避免数据不一致读超时被压缩到刷新周期内避免一次慢读阻塞后续采集3.2 双定时器模型定时器间隔职责_uiTimerUiRefreshSeconds默认 1 秒唯一读 PLC 路径更新界面和周期最低值_logTimerIntervalMinutes默认 10 分钟触发自动存盘写入 CSV 和历史表3.3 三路 CSV 输出程序生成三类独立的 CSV 文件文件类型文件名触发方式特点自动采集DataLog_Auto_yyyyMMdd.csv定时器周期触发含瞬时值 周期最低值手动采集DataLog_Manual_yyyyMMdd.csv用户点击触发独立序号不含周期最低值秒级采集DataLog_Second_yyyyMMdd.csv每秒入队后台批量写异步写入不阻塞主流程四、配置说明配置文件位于App.config开发时或S7DataCollector.exe.config运行时。?xml version1.0 encodingutf-8?!-- 运行时配置PLC 连接与采集周期。 修改后需重新部署/覆盖 exe 同目录的 S7DataCollector.exe.config 才会生效。 业务代码通过 AppSettings 读取键缺失时使用 AppSettings 中的默认常量。 --configurationstartupsupportedRuntimeversionv4.0sku.NETFramework,Versionv4.7.2//startupappSettings!-- PLC 连接参数 --!-- PLC IP 地址 --addkeyPlcIpvalue192.168.0.1/!-- 机架号S7-1200 通常为 0 --addkeyPlcRackvalue0/!-- 插槽号S7-1200 通常为 1 --addkeyPlcSlotvalue1/!-- 读数地址与类型 --!-- 读取地址MD10 M区双字字节偏移10也可改为 DB1.DBD0 等 --addkeyPlcAddressvalueMD10/!-- Float/Real按 IEEE754 浮点解析读到 DInt 位型时会自动转 Float 其它可选DInt / DWord / Int / Word --addkeyPlcValueTypevalueFloat/!-- 采集周期 --!-- 自动写入 CSV 的间隔分钟仅影响存盘不影响界面刷新 --addkeyIntervalMinutesvalue10/!-- 界面「当前 PLC 数值」刷新间隔秒 单路读 PLC 周期与 CSV 存盘无关 --addkeyUiRefreshSecondsvalue1/!-- PLC 通信超时上限毫秒单路采集实际超时还会被压到「刷新周期-100ms」以内 --addkeyReadTimeoutMsvalue5000/!-- 秒级 CSV与 10 分钟 Auto 文件分离采集线程只入队后台批量写 --!-- true/false是否启用 DataLog_Second_yyyyMMdd.csv --addkeyEnableSecondLogvaluetrue/!-- 有界队列容量约等于可缓冲秒数写不过来时丢最旧并计数 --addkeySecondLogQueueCapacityvalue600/!-- 单次最多刷多少行 --addkeySecondLogBatchSizevalue50/!-- 最大刷盘间隔毫秒与 BatchSize 任一触发即写 --addkeySecondLogFlushIntervalMsvalue1000//appSettings/configuration4.1 PLC 连接参数配置项默认值说明PlcIp192.168.0.1PLC IP 地址PlcRack0机架号S7-1200 通常为 0PlcSlot1插槽号S7-1200 通常为 1usingSystem;usingSystem.Globalization;usingS7.Net;usingS7DataCollector.Config;namespaceS7DataCollector.Services{/// summary/// PLC 通信服务封装 S7netplus负责与西门子 S7-1200 的连接、读数、断开。/// para/// 默认读取 M 区双字地址 MD10数值类型由 see crefAppSettings.PlcValueType/ 决定///Float/Real 时按 IEEE754 解释 32 位原始数据。/// /para/// para本类方法均为线程安全内部 c_sync/c 锁调用方仍建议串行化对同一实例的访问。/para/// /summarypublicsealedclassPlcService:IDisposable{/// summary保护 _plc 实例的互斥锁/summaryprivatereadonlyobject_syncnewobject();/// summaryS7netplus 底层 PLC 对象未连接或已断开时为 null/summaryprivatePlc_plc;privatebool_disposed;/// summaryPLC IP 地址/summarypublicstringIpAddress{get;}/// summary机架号S7-1200 通常为 0/summarypublicshortRack{get;}/// summary插槽号S7-1200 通常为 1/summarypublicshortSlot{get;}/// summary读取地址如 MD10、MW10、DB1.DBW0/summarypublicstringAddress{get;}/// summary数值类型名Float / Real / DInt / DWord / Int 等/summarypublicstringValueTypeName{get;}/// summary读写超时毫秒/summarypublicintReadTimeoutMs{get;}/// summary当前是否已与 PLC 建立连接/summarypublicboolIsConnected{get{lock(_sync){return_plc!null_plc.IsConnected;}}}/// summary/// 创建 PLC 服务。参数为 null 时从 see crefAppSettings/ 读取默认配置。/// /summarypublicPlcService(stringipnull,short?racknull,short?slotnull,stringaddressnull,stringvalueTypenull,int?readTimeoutMsnull){IpAddressstring.IsNullOrWhiteSpace(ip)?AppSettings.PlcIp:ip.Trim();Rackrack??AppSettings.PlcRack;Slotslot??AppSettings.PlcSlot;Addressstring.IsNullOrWhiteSpace(address)?AppSettings.PlcAddress:address.Trim();ValueTypeNamestring.IsNullOrWhiteSpace(valueType)?AppSettings.PlcValueType:valueType.Trim();ReadTimeoutMsreadTimeoutMs??AppSettings.ReadTimeoutMs;}/// summary/// 连接到 PLC。若已有连接会先断开再重建。/// /summary/// exception crefInvalidOperationException连接失败时抛出/exceptionpublicvoidConnect(){ThrowIfDisposed();lock(_sync){DisconnectInternal();// CpuType.S71200适配本项目目标 PLCS7-1200_plcnewPlc(CpuType.S71200,IpAddress,Rack,Slot){ReadTimeoutReadTimeoutMs,WriteTimeoutReadTimeoutMs};_plc.Open();if(!_plc.IsConnected){thrownewInvalidOperationException($无法连接到 PLC{IpAddress}(Rack{Rack}, Slot{Slot}));}}}/// summary/// 按配置地址读取数值MD10 默认按 Float。/// 若底层读到 DInt/DWord 位型会按 IEEE754 转成 Float。/// /summary/// returns转换后的 double 数值/returns/// exception crefInvalidOperationException未连接或读空值时抛出/exceptionpublicdoubleReadValue(){ThrowIfDisposed();lock(_sync){if(_plcnull||!_plc.IsConnected){thrownewInvalidOperationException(PLC 未连接无法读取数据。);}varresult_plc.Read(Address);if(resultnull){thrownewInvalidOperationException($读取{Address}返回空值。);}returnConvertByValueType(result);}}/// summary/// 按 see crefValueTypeName/ 将底层读结果转换为 double。/// /summaryprivatedoubleConvertByValueType(objectresult){vartypeName(ValueTypeName??Float).Trim();// Float / Real真空等模拟量常用按 IEEE754 解释 32 位if(typeName.Equals(Real,StringComparison.OrdinalIgnoreCase)||typeName.Equals(Float,StringComparison.OrdinalIgnoreCase)){returnConvertDIntBitsToFloat(result);}if(typeName.Equals(DWord,StringComparison.OrdinalIgnoreCase)||typeName.Equals(UDInt,StringComparison.OrdinalIgnoreCase)){returnConvert.ToUInt32(result,CultureInfo.InvariantCulture);}if(typeName.Equals(Int,StringComparison.OrdinalIgnoreCase)||typeName.Equals(Word,StringComparison.OrdinalIgnoreCase)){returnConvert.ToInt16(result,CultureInfo.InvariantCulture);}if(typeName.Equals(DInt,StringComparison.OrdinalIgnoreCase)){returnConvert.ToInt32(result,CultureInfo.InvariantCulture);}// 未识别类型时按 FloatDInt 位型转浮点处理returnConvertDIntBitsToFloat(result);}/// summary/// 将 DInt/DWord 的 32 位原始数据按 IEEE754 解释为 Float。/// S7netplus 读 MD 区时有时返回 int/uint需用位型重解释而非数值强转。/// /summaryprivatestaticdoubleConvertDIntBitsToFloat(objectresult){if(resultisfloatf)returnf;if(resultisdoubled)returnd;if(resultisuintu)returnBitConverter.ToSingle(BitConverter.GetBytes(u),0);if(resultisinti)returnBitConverter.ToSingle(BitConverter.GetBytes(i),0);if(resultislongl)returnBitConverter.ToSingle(BitConverter.GetBytes(unchecked((int)l)),0);// 其它数值先落到 32 位整型位型再转 Floattry{varbitsConvert.ToInt32(result,CultureInfo.InvariantCulture);returnBitConverter.ToSingle(BitConverter.GetBytes(bits),0);}catch{returnConvert.ToSingle(result,CultureInfo.InvariantCulture);}}/// summary断开与 PLC 的连接可重复调用/summarypublicvoidDisconnect(){lock(_sync){DisconnectInternal();}}/// summary内部断开逻辑调用方须已持有 _sync/summaryprivatevoidDisconnectInternal(){if(_plcnull)return;try{if(_plc.IsConnected){_plc.Close();}}catch{// 断开时忽略异常避免掩盖上层重连逻辑}finally{_plcnull;}}privatevoidThrowIfDisposed(){if(_disposed)thrownewObjectDisposedException(nameof(PlcService));}/// summary释放资源断开连接并标记已释放/summarypublicvoidDispose(){if(_disposed)return;Disconnect();_disposedtrue;}}}4.2 读数参数配置项默认值说明PlcAddressMD10读取地址如 MD10, DB1.DBD0PlcValueTypeFloat值类型Float/Real/DInt/DWord/Int4.3 采集周期配置项默认值说明IntervalMinutes10自动 CSV 存盘间隔分钟UiRefreshSeconds1界面刷新间隔即单路采集周期秒ReadTimeoutMs5000PLC 通信超时上限毫秒4.4 秒级 CSV 参数配置项默认值说明EnableSecondLogtrue是否启用秒级 CSVSecondLogQueueCapacity600有界队列容量约缓冲秒数SecondLogBatchSize50单次最多刷盘行数SecondLogFlushIntervalMs1000最大刷盘间隔毫秒五、数据流程5.1 启动流程App.OnStartup └── StartApplication ├── RegisterGlobalExceptionHandlers() # 注册三类异常钩子 ├── EnsureLogsDirectory() # 确保 Logs 目录存在 ├── WriteSessionStart() # 写入会话启动日志 └── new MainWindow().Show() # 显示主窗口5.2 自动采集流程BtnStart_Click ├── EnsurePlcConnected(forceReconnect: true) ├── StartTimers() # 启动双定时器 ├── ResetIntervalMin() # 重置周期最低值累加器 ├── RefreshRealtimeAsync() # 立即刷新界面写入缓存 └── SaveAutoSampleAsync() # 立即记一条自动 CSV ┌── UiTimer_Elapsed (每秒触发) │ └── RefreshRealtimeAsync() │ ├── Read PLC │ ├── PublishSample() │ │ ├── UpdateLastSample() # 更新缓存 │ │ ├── UpdateIntervalMin() # 更新周期最低值 │ │ └── SecondCsvWriter.TryEnqueue() # 秒级入队 │ └── 更新 UI └── LogTimer_Elapsed (每10分钟触发) └── SaveAutoSampleAsync() ├── TryGetFreshInPeriodSample() # 取新鲜缓存瞬时值 ├── TakeAndClearIntervalMin() # 结算周期最低值 ├── CsvLogger.Append() # 写入 CSV ├── AddHistoryBounded() # 追加历史表 └── BeginNewPeriod() # 推进周期起点5.3 周期最低值机制程序在每个自动采集周期内默认 10 分钟维护一个最低值采样阶段每秒读取的数值与当前周期最低值比较保留较小值结算阶段自动存盘时取出周期最低值清空累加器回滚机制若写盘失败已取出的最低值会合并回当前周期周期边界拒绝上周期遗留的陈旧采样避免边界低压点污染六、并发设计6.1 同步原语锁对象用途说明_plcLockPLC 读写互斥所有 PlcService 调用须在此锁内_logGate存盘互斥使用SemaphoreSlim支持 async/await_intervalMinLock周期最低值保护保护_intervalMinValue,_intervalMinTime,_periodStart_lastSampleLock采样缓存保护保护_lastValue,_lastStamp_uiRefreshBusy界面刷新防重入使用Interlocked.CompareExchange6.2 秒级 CSV 异步写入SecondCsvWriter采用生产者-消费者模式采集线程只调用TryEnqueue()非阻塞入队后台工作线程按批BatchSize或按间隔FlushIntervalMs批量刷盘有界队列队列满时丢弃最旧数据不反压采集线程七、错误处理与容错7.1 全局异常防护程序注册了三类未处理异常钩子全部记录日志并尽量阻止进程退出异常类型处理方式DispatcherUnhandledException记日志标记Handled trueAppDomain.UnhandledException记日志尽力阻止终止TaskScheduler.UnobservedTaskException记日志调用SetObserved()7.2 自动存盘失败处理存盘冲突自动采集标记延后补写_pendingAutoSave不丢周期写盘失败回滚已取出的周期最低值预约重试_autoSaveRetryAfterTicks重试触发由下次秒级刷新路径顺带触发补写7.3 PLC 通信异常处理连接失败进入周期重试模式由秒级定时器持续尝试重连连续失败计数记录连续失败次数用于状态提示安全断开断开时忽略异常避免掩盖上层重连逻辑八、文件输出格式8.1 自动采集 CSVId,Data,Time,Source,MinData,MinTime 1,1.23456,2026-07-16 10:00:00,Auto,1.12345,2026-07-16 10:05:30 2,1.34567,2026-07-16 10:10:00,Auto,1.23456,2026-07-16 10:12:158.2 手动采集 CSVId,Data,Time,Source 1,1.45678,2026-07-16 10:03:00,Manual 2,1.56789,2026-07-16 10:08:00,Manual8.3 秒级采集 CSVId,Data,Time 1,1.23456,2026-07-16 10:00:00 2,1.23457,2026-07-16 10:00:01 3,1.23455,2026-07-16 10:00:02九、界面布局主窗口自上而下分为五个区域区域内容功能通信状态区指示灯 状态文字 配置摘要显示连接状态和当前配置实时数据区大字号数值 采样时间实时显示 PLC 读数历史表格区DataGrid 绑定ObservableCollectionDataRecord显示采集历史记录控制按钮区启动采集 / 停止采集 / 手动采集一次控制采集流程路径状态栏文件路径提示 运行状态消息显示输出路径和运行状态十、运行说明10.1 启动方式方式一直接运行最快双击S7DataCollector\bin\Release\net472\S7DataCollector.exe方式二Visual Studio打开 S7DataCollector.sln → F5调试或 CtrlF5直接运行方式三命令行cdS7DataCollectordotnet run-cRelease10.2 配置修改注意事项修改S7DataCollector.exe.config运行时而非源码中的App.config修改后必须关闭程序再重新打开配置才会生效PLC 需允许 PUT/GET 访问电脑防火墙需放行连不上时查看界面红色状态灯和Logs\ErrorLog.txt10.3 部署要求目标机器需安装 .NET Framework 4.7.2 或更高版本电脑与 PLC 需在同一网段建议先 ping 通 PLC IP 再启动程序十一、设计要点总结单路采集一个定时器读 PLC多路消费降低通信负荷缓存策略使用缓存瞬时值避免边界再读 PLC新鲜度阈值 2 秒周期最低值半开区间累加写盘失败回滚边界不污染异步秒级写入有界队列 后台批量写不阻塞主流程异常防护全局异常钩子 自动重试 状态恢复支持长期无人值守资源管理完整的IDisposable实现窗口关闭时释放所有资源
延伸阅读

更多相关文章

2026/9/12 15:37:58

远程访问 GitLab / SVN 的 5 个典型场景

很多企业的 GitLab、SVN 仍部署在内网环境中,研发人员一旦离开办公室,就会遇到代码无法提交、协作效率下降、访问权限难以控制等问题。本文结合五个典型场景,介绍 NexTunnel 如何在不开放整个企业内网的情况下,实现对 GitLab、SVN…

2026/9/12 3:05:16

2026年口碑好的白钢雕塑,哪家才是专业之选?

在白钢雕塑市场繁荣发展的2026年,客户在选择时往往会面临众多困惑,不知道哪家才是真正专业的选择。下面,我们就为大家深入剖析一些值得考虑的白钢雕塑厂家,并重点推荐奕锦不锈钢雕塑。奕锦不锈钢雕塑:品质与诚信的典范…

2026/9/13 18:32:59

MATLAB实现Mie散射的数值稳定性与精度控制

简介:本资源是一份基于MATLAB实现Mie散射理论的完整计算代码包,面向光学、大气科学、纳米材料及生物医学等领域的科研人员与高年级本科生/研究生,用于定量模拟球形粒子对入射光的散射行为。代码严格依据Mie理论框架编写,核心包含尺…

2026/9/13 18:32:59

可上线的微信小程序功能矩阵源码解析

简介:这是一套功能丰富的微信小程序源码合集,面向前端开发者、小程序学习者及轻量级工具类应用创作者,旨在提供开箱即用的多场景实战模板与功能模块参考。资源包含1116个文件,主体为280个JavaScript逻辑文件、156个WXML结构文件、…

2026/9/13 18:32:59

mootdx安装使用指南:从tar.gz到A股行情数据

简介:mootdx 是一个面向通达信金融数据接口的 Python 库,版本 0.8.7,专为量化交易、金融数据分析及后端服务开发者设计,帮助用户便捷获取行情、财务等数据。该压缩包共 38 个文件,整体大小仅 32KB,包含 23 …

2026/9/13 18:32:59

.NET 6 + Vue 3 教务系统全栈实践:从排课到课表可视化

简介:这是一套面向高校信息化开发者与.NET全栈学习者的教务管理系统完整源码,基于C#语言与前后端分离架构,解决教育机构学生、课程、教师等核心业务的数字化管理需求。资源共1147个文件,主体为1036个Vue组件文件(构建响…

2026/9/13 18:27:59

量化高频交易 FPGA 还是 GPU:三组实测+三年 TCO 账本

量化高频交易 FPGA 还是 GPU:三组实测三年 TCO 账本 【免费下载链接】gs-quant Python toolkit for quantitative finance 项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant 在高频交易里,延迟就是盈亏:比对手快 1 微秒可能…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/13 11:18:28

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码