欢迎光临
我们一直在努力

从零搭建工业控制系统(二十九):项目打包与部署,从开发机到工厂

项目打包与部署:从开发机到工厂

这是「从零搭建工业控制系统」系列第29篇,最后一篇。代码写完了,测试过了,但送到工厂能跑起来才是真的完成。这篇说打包、混淆、部署那些事。


第一次现场部署的尴尬

第一次去现场部署,我把Debug目录打包拷过去,双击exe就启动了。看起来没问题。

然后客户问:“这个.NET Framework是什么?要我装吗?”

工厂的电脑是IT统一管控的,没有管理员权限装不了运行时。而且Debug目录里一堆pdb文件和源码引用,体积300多MB。

那次之后我做了完整的部署方案:Release编译 + 代码混淆 + 配置文件打包 + 运行时检测脚本。


Release编译优化

vsproj里调整Release配置:

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
<Optimize>true</Optimize>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup>

  • DebugType=none:不生成pdb文件
  • Optimize=true:启用编译器优化
  • UseVSHostingProcess=false:不使用VS宿主进程

代码混淆

项目用混淆器保护源码。关键点是要排除被XAML绑定的属性名:

[Obfuscation(Exclude = true, ApplyToMembers = true)]
public class PermissionService : ObservableObject { ... }

[Obfuscation(Exclude = true, ApplyToMembers = true)]
public class AppConfig { ... }

混淆后 IsEngineerOrAbove 这种属性名不能变,变了XAML绑定路径就找不到。枚举也一样:

[Obfuscation(Exclude = true, ApplyToMembers = true)]
public enum AlarmSeverity { Critical, Warning, Info }

枚举名用于数据库存储和反射,混淆后存进去的值读出来对不上。

踩过的坑:有一次忘了给 AlarmHistory 加混淆排除,数据库里Severity列存的变成了数字而不是"Critical"。后来统一给所有数据库实体类加 Obfuscation(Exclude=true)。


配置文件打包

部署包里必须包含这些配置文件:

图1:部署包目录结构

Deploy/
├── MyApp.exe
├── Configs/
│ ├── Config.ini # 全局配置(带默认值)
│ ├── Log4netConfig/
│ │ └── log4net.config # 日志配置
│ ├── CmdParameter/
│ │ ├── ValveActions.jsonc # 阀门动作
│ │ ├── MotionCommands.jsonc # 运动命令
│ │ ├── HardwareCommands.jsonc # 硬件命令
│ │ ├── TrackingCommands.jsonc # 追踪命令
│ │ └── RecipeCommands.jsonc # 配方流程
│ └── ErrorCodeInfo/
│ └── *.json # 错误码定义
├── Libraries/ # 第三方DLL
│ ├── NModbus.dll
│ ├── log4net.dll
│ └── …
└── Start.bat # 启动脚本

配置文件设为 Copy to Output Directory = Copy if newer,编译时自动复制到输出目录。


Config.ini默认模板

部署时带一份默认Config.ini,现场修改IP和端口:

[General]
AutoStart = True
ShowUILog = True
DatabaseAccessType = 0

[Devices]
IoBoardIp = 192.168.1.10
IoBoardPort = 502
IoBoardReadTimeout = 5000

ValveIslandIp = 192.168.1.11
ValveIslandPort = 503
ValveIslandReadTimeout = 5000

AnalogBoardIp = 192.168.1.12
AnalogBoardPort = 504
AnalogBoardReadTimeout = 5000

[Indicator]
IndicatorServiceEnable = True
BuzzerDurationSeconds = 5

[ProcessControl]
AutoControlEnabled = True
ManualControlEnabled = True

[Heater]
HeaterZone1Enabled = True
HeaterZone2Enabled = False
HeaterZone3Enabled = False
HeaterCoolingEnabled = False

每个配置项都带注释说明默认值和可选值。 现场人员不需要看代码就能改配置。


启动脚本

Start.bat 做运行时检测:

@echo off
echo 正在启动控制系统控制系统…

REM 检查.NET Framework
reg query "HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full" /v Release 2>nul
if %errorlevel% neq 0 (
echo [错误] 未检测到.NET Framework 4.8
echo 请联系IT部门安装运行时
pause
exit /b 1
)

REM 检查配置文件
if not exist "Configs\\Config.ini" (
echo [错误] 配置文件缺失: Configs\\Config.ini
pause
exit /b 1
)

REM 创建日志目录
if not exist "Logs" mkdir "Logs"

REM 启动程序
start "" "MyApp.exe"


PowerShell发布脚本

项目根目录放了一个发布脚本,一键编译+混淆+打包:

图2:PowerShell一键发布流程

# publish.ps1
$configuration = "Release"
$projectPath = "MyApp\\MyApp.csproj"
$outputPath = "Deploy"

# 清理旧文件
Remove-Item Path $outputPath Recurse Force ErrorAction SilentlyContinue

# 编译
dotnet build $projectPath c $configuration o $outputPath

# 运行混淆(如果有混淆工具)
# obfuscator.exe $outputPath\\MyApp.exe

# 清理不需要的文件
Remove-Item Path "$outputPath\\*.pdb" Force ErrorAction SilentlyContinue
Remove-Item Path "$outputPath\\*.xml" Force ErrorAction SilentlyContinue

# 复制启动脚本
Copy-Item Path "Start.bat" Destination $outputPath

Write-Host "发布完成: $outputPath"


部署清单

每次去现场前过一遍这个清单:

图4:部署前检查清单(9项)

  • Release编译通过
  • 混淆排除已配置(XAML绑定属性、数据库实体、枚举)
  • Config.ini模板已更新(IP/端口留空或默认值)
  • log4net.config已包含
  • 命令配置JSONC文件已包含
  • Logs目录在启动脚本中创建
  • 第三方DLL齐全
  • Start.bat运行时检测正常
  • 在干净环境测试启动(没有开发工具的机器)

现场部署流程

到现场后的标准流程:

图3:现场部署标准流程(8步)

1. 安装运行时:如果IT没预装.NET Framework 4.8,用离线安装包装

2. 解压部署包:拷到固定目录比如 C:\\\\MyApp\\\\

3. 修改Config.ini:根据现场设备IP修改通信参数

4. 首次启动:双击Start.bat,观察UI日志面板

5. 验证通信:连接设备,看蓝灯是否亮起

6. 验证安全服务:检查腔体安全和泵口安全控制是否自动启动

7. 跑一个空工艺:验证Sequence执行正常

8. 交付操作员:简单培训权限切换和日常操作


部署踩坑清单

坑现象解决
没装运行时 exe打不开 Start.bat检测.NET
pdb没删 体积大+暴露源码 DebugType=none
混淆绑定点 Release版界面空白 Obfuscation排除
配置没打包 启动报错 Copy if newer
日志目录不存在 第一次写日志失败 启动脚本mkdir Logs
枚举被混淆 数据库值对不上 Obfuscation排除枚举

本篇小结

知识点关键做法
Release编译 关DebugType、开Optimize
代码混淆 Obfuscation排除绑定点和数据库实体
配置打包 Copy if newer + 模板Config.ini
启动脚本 检测运行时 + 创建目录
部署清单 9项检查,逐项确认
现场流程 装运行时→改配置→验证通信→交付

系列完结

29篇到此结束。从空白项目到MVVM骨架,从Modbus通信到阀门控制,从报警系统到安全监控,从配置管理到现场部署——一套工业控制系统的完整搭建过程。

回头看,技术栈其实不复杂:WPF + MVVM + Prism + NModbus + log4net。难的是把这些技术组合成一个在工厂能稳定运行的系统。每个模块单独看都不难,组合起来要处理通信时序、线程安全、权限控制、安全联锁、配置管理、日志追踪……

工业系统的核心不是用了什么高级技术,是每个细节都考虑了"出错了怎么办"。

希望这个系列对你有帮助。有问题欢迎留言讨论。

赞(0)
未经允许不得转载:171主机测评 » 从零搭建工业控制系统(二十九):项目打包与部署,从开发机到工厂
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址