提示1:参照本文,你将通过一个实际案例深入了解如何实现一个通讯交互方案的功能。
提示2:如果你希望先完成一个简单的项目原型,请跳转C#与西门子PLC通讯——熟手快速入门。
提示3:如果你第一次来,请跳转到C#与西门子PLC通讯——新手快速入门以了解背景设定。
往期博客参考
建议先看下面这两篇,了解预设背景。
C#与西门子PLC通讯——新手快速入门
C#与西门子PLC通讯——熟手快速入门
番外篇
C#与西门子PLC通讯——手搓S7通讯协议
又过三日,斯电气之士志在不凡,再请吾赐以开发之微言,吾思之经久,遂书笔记一篇,以赞其志。
本文基于C# .Net Core和西门子博图TIA Portal V17搭建。由于手边没有西门子PLC实物,所以采用S7-PLCSIM Advanced V4.0作为模拟PLC,以实现0成本完成通讯测试实例。
本文将以一个自动化立体车库作为案例入手,完成基于领域驱动设计(DDD)的项目设计。
随着城市交通日益拥堵,自动化立体车库系统成为解决停车难题的创新方案。该系统涉及到大量的设备,如PLC控制器用于车库的自动化操作,数据库用于存储车辆信息,用户界面用于用户与系统的交互。
在自动化立体车库系统中,与西门子PLC的高效通讯是确保系统稳定运行的关键之一。本博客将带领读者深入探讨如何使用C#与西门子PLC进行通讯,实现对车库设备的灵活控制。
PLC通讯: 实现C#与西门子PLC的通讯,包括读取PLC中的状态信息和发送控制指令。
数据库访问: 通过Entity Framework Core实现对车辆信息的持久化存储,包括车辆的入库和出库记录。
业务逻辑: 实现车库的业务逻辑,包括车位管理、车辆入库和出库等操作。
用户界面: 使用ASP.NET Core MVC或Blazor实现用户友好的界面,显示车库状态,提供用户操作界面。
PLC通讯服务: 使用S7netplus等通讯库实现C#与西门子PLC的通讯。
数据库访问: 利用Entity Framework Core进行数据库操作,支持多种数据库后端。
业务逻辑: 基于领域驱动设计(DDD)的概念,实现业务逻辑服务接口。
用户界面: 使用ASP.NET Core MVC,与业务逻辑和PLC通讯服务集成。
AutoParkingSystem.Core: 实现领域模型和业务逻辑,定义PLC通讯服务接口。
AutoParkingSystem.Infrastructure: 实现数据库访问和PLC通讯服务,使用Entity Framework Core和S7netplus等库。
AutoParkingSystem.Web: 实现用户界面,集成业务逻辑和PLC通讯服务,展示车库状态和实现用户操作。
AutoParkingSystem.Tests: 编写单元测试和集成测试,确保系统的稳定性和可靠性。
通过在Program.cs
中配置依赖注入容器,实现各个服务和组件的动态加载和实例化,提高系统的灵活性和可扩展性。
在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);
}
// 其他通讯方法的实现...
}
使用Entity Framework Core实现对车辆信息的数据库访问。首先,在AutoParkingSystem.Core
项目中定义领域模型:
尚未完成。。。敬请期待
在AutoParkingSystem.Core
项目中实现车库的业务逻辑服务:
// AutoParkingSystem.Core/Services/IParkingService.cs
public interface IParkingService
{
Task<bool> ParkVehicleAsync(string vehicleId);
Task<bool> ReleaseParkingSpaceAsync(string vehicleId);
// 其他服务接口...
}
然后,在AutoParkingSystem.Core
项目中实现该接口的具体逻辑:
// AutoParkingSystem.Core/Services/ParkingService.cs
public class ParkingService : IParkingService
{
private readonly IPlcCommunicationService plcCommunicationService;
public ParkingService(IPlcCommunicationService plcCommunicationService)
{
this.plcCommunicationService = plcCommunicationService;
}
public async Task<bool> ParkVehicleAsync(string vehicleId)
{
// 调用PLC通讯服务进行停车操作
await plcCommunicationService.WriteIntAsync(1, 2, 1);
// 其他停车逻辑...
return true;
}
public async Task<bool> ReleaseParkingSpaceAsync(string vehicleId)
{
// 调用PLC通讯服务进行释放停车位操作
await plcCommunicationService.WriteIntAsync(1, 2, 0);
// 其他释放停车位逻辑...
return true;
}
// 其他服务方法...
}
在AutoParkingSystem.Web
项目的Program.cs
中配置依赖注入容器,注册各个服务和组件,以及使用反射机制实现动态加载和实例化:
// AutoParkingSystem.Web/Program.cs
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// 注册业务逻辑服务
builder.Services.AddScoped<IParkingService, ParkingService>();
// 注册PLC通信服务,这里注册的是SiemensPlcCommunicationService
builder.Services.AddSingleton<IPlcCommunicationService, SiemensPlcCommunicationService>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
}
}
2.5 测试接口实现
// AutoParkingSystem.Web/Controllers/PlcConnectionController.cs
[ApiController]
[Route("[controller]")]
public class PlcConnectionController : ControllerBase
{
private readonly ILogger<PlcConnectionController> _logger;
private readonly IPlcCommunicationService _plcCommunicationService;
public PlcConnectionController(ILogger<PlcConnectionController> logger, IPlcCommunicationService plcCommunicationService)
{
_plcCommunicationService = plcCommunicationService;
_logger = logger;
}
[HttpPost("ConnectPlc")]
public async Task<IActionResult> ConnectAsync()
{
bool val = await _plcCommunicationService.ConnectAsync("192.168.0.100", 0, 1);
return Ok(val);
}
[HttpGet("ReadInt")]
public async Task<IActionResult> ReadIntAsync(int dbArea, int offset)
{
if (_plcCommunicationService.IsConnected == false)
{
await _plcCommunicationService.ConnectAsync("192.168.0.100", 0, 1);
}
var val = await _plcCommunicationService.ReadIntAsync(dbArea, offset);
return Ok(val);
}
[HttpPost("WriteInt")]
public async Task<IActionResult> WriteIntAsync(int dbArea, int offset, short value)
{
if (_plcCommunicationService.IsConnected == false)
{
await _plcCommunicationService.ConnectAsync("192.168.0.100", 0, 1);
}
await _plcCommunicationService.WriteIntAsync(dbArea, offset, value);
return Ok("OK");
}
}
接着,我们读取一下原来PLC中的一个整形量,请参考C#与西门子PLC通讯——熟手快速入门。
然后,我们尝试对这个整形量写入一个值,再读取下整形量。
可以看到,已经写入成功,读取到的也是对应的值。
我们已经建立了一个自动化立体车库系统的基本的DDD框架,实现了C#与西门子PLC的通讯。
PLC通讯服务: 通过实现IPlcCommunicationService
接口,我们建立了与西门子PLC的通讯服务,并使用S7netplus等库实现了读取PLC状态和发送控制指令的功能。这为系统与车库设备的紧密集成提供了基础。
业务逻辑: 在IParkingService
接口和ParkingService
实现中,我们通过领域驱动设计(DDD)的概念,定义了车库的业务逻辑,包括车辆入库、出库等操作。这为系统的稳定运行提供了可靠的业务逻辑支持。
依赖注入和反射: 通过在Program.cs
中配置依赖注入容器,我们注册了各个服务和组件,使用反射机制实现了动态加载和实例化。这增强了系统的灵活性和可扩展性。
数据库访问: 利用Entity Framework Core,我们实现了对车辆信息的数据库持久化存储。通过定义ParkingRecord
领域模型和相应的数据库上下文,我们实现了车辆入库和出库记录的管理。
用户界面: 使用ASP.NET Core MVC或Blazor,我们实现了用户友好的界面,展示了车库的实时状态,并提供了用户操作界面。这使得系统更易用、直观,提高了用户体验。
异步编程: 在系统的关键地方,如IO操作,采用异步编程模型,提高系统的性能和响应性。
安全性: 加强系统的安全性,通过使用ASP.NET Core提供的身份验证和授权中间件,确保系统只对授权用户开放,并实施必要的安全审计。
性能优化: 进一步优化系统性能,考虑缓存、数据库索引等方面的优化措施,以确保系统的高效运行。
测试与调优: 持续进行单元测试和集成测试,确保系统的稳定性和可靠性。根据测试结果,进行必要的调优和改进。
日志记录: 引入日志记录机制,记录系统运行中的关键信息,以便及时发现和解决问题。
通过不断的迭代和优化,我们可以构建一个功能完善、性能卓越的自动化立体车库系统。希望为读者提供了在自动化领域深入探索的基础,希望通过本系列能够学到更多有关工业自动化的知识和技能。
本篇博文将不断优化完善,敬请期待。