提示1:参照本文,你将通过一个实际案例深入了解如何实现一个通讯交互方案的功能。 提示2:如果你希望先完成一个简单的项目原型,请跳转C#与西门子PLC通讯——熟手快速入门。 提示3:如果你第一次来,请跳转到C#与西门子PLC通讯——新手快速入门以了解背景设定。
C#与西门子PLC通讯 系列文章目录
往期博客参考
建议先看下面这两篇,了解预设背景。 C#与西门子PLC通讯——新手快速入门 C#与西门子PLC通讯——熟手快速入门
代码已同步至: Gitee:https://gitee.com/lukailin/auto-parking-system Github:https://github.com/Millance/AutoParkingSystem
文章目录
前言
又过三日,斯电气之士志在不凡,再请吾赐以开发之微言,吾思之经久,遂书笔记一篇,以赞其志。
本文基于C# .Net Core和西门子博图TIA Portal V17搭建。由于手边没有西门子PLC实物,所以采用S7-PLCSIM Advanced V4.0作为模拟PLC,以实现0成本完成通讯测试实例。
本文将以一个自动化立体车库作为案例入手,完成基于领域驱动设计(DDD)的项目设计。
一、需求分析
1.1 背景
随着城市交通日益拥堵,自动化立体车库系统成为解决停车难题的创新方案。该系统涉及到大量的设备,如PLC控制器用于车库的自动化操作,数据库用于存储车辆信息,用户界面用于用户与系统的交互。 在自动化立体车库系统中,与西门子PLC的高效通讯是确保系统稳定运行的关键之一。本博客将带领读者深入探讨如何使用C#与西门子PLC进行通讯,实现对车库设备的灵活控制。
1.2 主要功能
- PLC通讯: 实现C#与西门子PLC的通讯,包括读取PLC中的状态信息和发送控制指令。 + 数据库访问: 通过Entity Framework Core实现对车辆信息的持久化存储,包括车辆的入库和出库记录。 + 业务逻辑: 实现车库的业务逻辑,包括车位管理、车辆入库和出库等操作。 + 用户界面: 使用ASP.NET Core MVC或Blazor实现用户友好的界面,显示车库状态,提供用户操作界面。
1.3 技术选型
- PLC通讯服务: 使用S7netplus等通讯库实现C#与西门子PLC的通讯。 + 数据库访问: 利用Entity Framework Core进行数据库操作,支持多种数据库后端。 + 业务逻辑: 基于领域驱动设计(DDD)的概念,实现业务逻辑服务接口。 + 用户界面: 使用ASP.NET Core MVC,与业务逻辑和PLC通讯服务集成。
1.4 系统架构
- AutoParkingSystem.Core: 实现领域模型和业务逻辑。 + AutoParkingSystem.Infrastructure: 实现数据库访问操作和PLC通讯服务,使用Entity Framework Core和S7netplus等库。 + AutoParkingSystem.Model: 存放所有的数据库对象实体。 + AutoParkingSystem.Repository: 存放数据库上下文和迁移文件。 + AutoParkingSystem.Web: 实现用户界面,集成业务逻辑和PLC通讯服务的接口,展示车库状态和实现用户操作。 + AutoParkingSystem.Tests: 编写单元测试和集成测试,确保系统的稳定性和可靠性。
1.5 依赖注入和反射
通过在Program.cs
中配置依赖注入容器,实现各个服务和组件的动态加载和实例化,提高系统的灵活性和可扩展性。
二、业务实现
2.1 PLC通讯服务
在AutoParkingSystem.Infrastructure
项目中实现与西门子PLC的通讯服务。首先,创建接口定义:
// AutoParkingSystem.Core/Services/IPlcCommunicationService.cs
public interface IPlcCommunicationService
{
bool IsConnected {
get; }
Task<bool> ConnectAsync(string ipAddress, int rack, int slot);
Task<int> ReadIntAsync(int dbArea, int offset);
Task WriteIntAsync(int dbArea, int offset, short value);
// 其他通讯方法...
}
然后,在AutoParkingSystem.Infrastructure
项目中实现该接口:
// AutoParkingSystem.Infrastructure/Services/SiemensPlcCommunicationService.cs
public class SiemensPlcCommunicationService : IPlcCommunicationService
{
private Plc? plc;
public bool IsConnected
{
get
{
if (plc == null) {
return false; }
return plc.IsConnected;
}
}
public async Task<bool> ConnectAsync(string ipAddress, int rack, int slot)
{
plc = new Plc(CpuType.S71500, ipAddress, (short)rack, (short)slot);
await plc.OpenAsync();
return plc.IsConnected;
}
public async Task<int> ReadIntAsync(int dbArea, int offset)
{
if (plc == null || !plc.IsConnected)
{
throw new InvalidOperationException("PLC is not connected.");
}
var result = await plc.ReadAsync(DataType.DataBlock, dbArea, offset, VarType.Int, 1);
if (result == null)
{
return 0;
}
else
{
return (short)result;
}
}
public async Task WriteIntAsync(int dbArea, int offset, short value)
{
if (plc == null || !plc.IsConnected)
{
throw new InvalidOperationException("PLC is not connected.");
}
await plc.WriteAsync(DataType.DataBlock, dbArea, offset, value);
}
// 其他通讯方法的实现...
}
2.2 数据库创建
2.2.1 定义数据库对象
使用Entity Framework Core实现对车辆信息的数据库访问。
在AutoParkingSystem.Model
项目中定义数据库对象ParkPosition:
namespace AutoParkingSystem.Model
{
[Table("park_position")]
public class ParkPosition
{
/// <summary>
/// 位置id
/// </summary>
[Key]
[Column("id")]
public int Id {
get; set; }
/// <summary>
/// 位置名称
/// </summary>
[Column("position_name")]
[MaxLength(10)]
public string PositionName {
get; set; } = string.Empty;
/// <summary>
/// 停放车辆的车牌号
/// </summary>
[Column("license_plate_number")]
[MaxLength(10)]
public string LicensePlateNumber {
get; set; } = string.Empty;
/// <summary>
/// 进入位置的时间
/// </summary>
[Column("park_in_time")]
public DateTime ParkInTime {
get; set; } = DateTime.Now;
}
}
2.2.2 定义数据库上下文
在AutoParkingSystem.Repository
中添加Entity Framework Core的NuGet包。
注:这里用的是MySql数据库,可以根据本地数据库类型找到对应的包
创建数据库上下文ParkDbContext.cs
。
namespace AutoParkingSystem.Repository
{
public class ParkDbContext : DbContext
{
// 添加车位累
public DbSet<ParkPosition> ParkPositions {
get; set; }
public ParkDbContext(DbContextOptions<ParkDbContext> options) : base(options) {
}
}
}