first commit

This commit is contained in:
2026-09-02 16:31:50 +08:00
commit a461727193
911 changed files with 692450 additions and 0 deletions
+156
View File
@@ -0,0 +1,156 @@
namespace JSMachine.WMS.PLC
{
/// <summary>
/// PLC Modbus 通信引擎契约,统一提供连接、线圈和寄存器读写能力。
/// </summary>
public interface IModbusEngine
{
/// <summary>
/// 初始化PLC参数
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <returns></returns>
IModbusEngine Init(string ip, int port);
/// <summary>
/// 连接PLC
/// </summary>
/// <returns></returns>
IModbusEngine Connet();
/// <summary>
/// 批量读取线圈值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
bool[] ReadCoils(byte slaveAddress, ushort startAddress, ushort numberOfPoints);
/// <summary>
/// 批量读取线圈值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
Task<bool[]> ReadCoilsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints);
/// <summary>
/// 无符号读取
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
ushort[] ReadUshorts(byte slaveAddress, ushort startAddress, ushort numberOfPoints);
/// <summary>
/// 无符号读取(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
Task<ushort[]> ReadUshortsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints);
/// <summary>
/// 有符号读取
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
short[] ReadShorts(byte slaveAddress, ushort startAddress, ushort numberOfPoints);
/// <summary>
/// 有符号读取(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
Task<short[]> ReadShortsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints);
/// <summary>
/// 写单个无符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
bool WriteUshortOne(byte slaveAddress, ushort startAddress, ushort data);
/// <summary>
/// 写单个无符号值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
Task<bool> WriteUshortOneAsync(byte slaveAddress, ushort startAddress, ushort data);
/// <summary>
/// 写单个有符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
bool WriteShortOne(byte slaveAddress, ushort startAddress, short data);
/// <summary>
/// 写单个有符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
bool WriteCoilOne(byte slaveAddress, ushort startAddress, bool data);
/// <summary>
/// 写单个有符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
Task<bool> WriteCoilOneAsync(byte slaveAddress, ushort startAddress, bool data);
/// <summary>
/// 写单个有符号值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
Task<bool> WriteShortOneAsync(byte slaveAddress, ushort startAddress, short data);
/// <summary>
/// 写多个无符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
bool WriteUshortsMany(byte slaveAddress, ushort startAddress, ushort[] data);
/// <summary>
/// 写多个无符号值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
Task<bool> WriteUshortsManyAsync(byte slaveAddress, ushort startAddress, ushort[] data);
/// <summary>
/// 写多个有符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
bool WriteShortsMany(byte slaveAddress, ushort startAddress, short[] data);
/// <summary>
/// 写多个有符号值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
Task<bool> WriteShortsManyAsync(byte slaveAddress, ushort startAddress, short[] data);
/// <summary>
/// PLC是否已连接
/// </summary>
/// <returns></returns>
bool IsConneted();
}
}
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Model\**" />
<EmbeddedResource Remove="Model\**" />
<None Remove="Model\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NModbus" Version="3.0.72" />
<PackageReference Include="Polly" Version="7.2.3" />
<PackageReference Include="Prism.Core" Version="8.1.97" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JSMachine.WMS.Common\JSMachine.WMS.Common.csproj" />
<ProjectReference Include="..\JSMachine.WMS.Infrastructure\JSMachine.WMS.Infrastructure.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,547 @@
using JSMachine.WMS.Infrastructure.Helper;
using NModbus;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.PLC
{
/// <summary>
/// Modbus TCP 通信引擎基类,封装 PLC 连接、重连以及寄存器/线圈读写操作。
/// </summary>
public abstract class ModbusEngineBase : IModbusEngine
{
private IModbusMaster _modbusMaster { get; set; }
private string _ip { get; set; }
private int _port { get; set; }
/// <summary>
/// 保存 PLC 网络端点,实际连接在首次通信或显式连接时建立。
/// </summary>
/// <param name="ip">PLC IP 地址。</param>
/// <param name="port">PLC TCP 端口。</param>
/// <returns>当前通信引擎实例。</returns>
public virtual IModbusEngine Init(string ip, int port)
{
_ip = ip;
_port = port;
return this;
}
private object _locker = new();
private TcpClient _tcpClient = new();
/// <summary>
/// 建立 PLC TCP 连接;连接过程加锁以避免并发重连,超时时间为 2 秒。
/// </summary>
/// <returns>当前通信引擎实例。</returns>
public IModbusEngine Connet()
{
lock (_locker)
{
if (!_tcpClient.Connected)
{
try
{
_tcpClient = new();
IAsyncResult asyncResult = _tcpClient.BeginConnect(IPAddress.Parse(_ip), _port, null, null);
if (asyncResult.AsyncWaitHandle.WaitOne(2000))
{
_tcpClient.EndConnect(asyncResult);
ModbusFactory factory = new();
_modbusMaster = factory.CreateMaster(_tcpClient);
}
else
{
_tcpClient.Close();
LogHelper.Error($"连接 PLC {_ip}:{_port} 超时");
}
}
catch (Exception ex)
{
_tcpClient.Close();
_modbusMaster = null;
}
}
}
return this;
}
/// <summary>
/// 判断 Modbus 主站是否已初始化。
/// </summary>
/// <returns>主站对象不为空时返回 <see langword="true"/>。</returns>
public bool IsConneted()
{
return !(_modbusMaster == null);
}
/// <summary>
/// 批量读取线圈值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
public bool[] ReadCoils(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
if (_modbusMaster == null)
{
this.Connet();
return null;
}
try
{
return _modbusMaster.ReadCoils(slaveAddress, startAddress, numberOfPoints);
}
catch (Exception ex)
{
LogHelper.Error($"批量读取PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return null;
}
}
/// <summary>
/// 批量读取线圈值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
public async Task<bool[]> ReadCoilsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
if (_modbusMaster == null)
{
this.Connet();
return null;
}
try
{
return await _modbusMaster.ReadCoilsAsync(slaveAddress, startAddress, numberOfPoints);
}
catch (Exception ex)
{
LogHelper.Error($"批量读取PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return null;
}
}
/// <summary>
/// 无符号读取
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
public ushort[] ReadUshorts(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
if (_modbusMaster == null)
{
this.Connet();
return null;
}
try
{
ushort[] data = _modbusMaster.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints);
return data;
}
catch (Exception ex)
{
LogHelper.Error($"无符号读取PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return null;
}
}
/// <summary>
/// 无符号读取(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
public async Task<ushort[]> ReadUshortsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
if (_modbusMaster == null)
{
this.Connet();
return null;
}
try
{
ushort[] data = await _modbusMaster.ReadHoldingRegistersAsync(slaveAddress, startAddress, numberOfPoints);
return data;
}
catch (Exception ex)
{
LogHelper.Error($"无符号读取PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return null;
}
}
/// <summary>
/// 有符号读取
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
public short[] ReadShorts(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
try
{
ushort[] data = ReadUshorts(slaveAddress, startAddress, numberOfPoints);
if (data == null)
return null;
//计算补码
short[] rtn = data.Select(p => BitConverter.ToInt16(BitConverter.GetBytes(p))).ToArray();
return rtn;
}
catch (Exception ex)
{
LogHelper.Error($"有符号读取PLC信息失败,错误消息:{ex.Message}");
return null;
}
}
/// <summary>
/// 有符号读取(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="numberOfPoints"></param>
/// <returns></returns>
public async Task<short[]> ReadShortsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints)
{
try
{
ushort[] data = await ReadUshortsAsync(slaveAddress, startAddress, numberOfPoints);
if (data == null)
return null;
//计算补码
short[] rtn = data.Select(p => BitConverter.ToInt16(BitConverter.GetBytes(p))).ToArray();
return rtn;
}
catch (Exception ex)
{
LogHelper.Error($"有符号读取PLC信息失败,错误消息:{ex.Message}");
return null;
}
}
/// <summary>
/// 写单个无符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool WriteUshortOne(byte slaveAddress, ushort startAddress, ushort data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
try
{
_modbusMaster.WriteSingleRegister(slaveAddress, startAddress, data);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写单个PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写单个无符号值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task<bool> WriteUshortOneAsync(byte slaveAddress, ushort startAddress, ushort data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
try
{
await _modbusMaster.WriteSingleRegisterAsync(slaveAddress, startAddress, data);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写单个PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写单个有符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool WriteShortOne(byte slaveAddress, ushort startAddress, short data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
try
{
ushort writeData = BitConverter.ToUInt16(new ReadOnlySpan<byte>(BitConverter.GetBytes(data)));
_modbusMaster.WriteSingleRegister(slaveAddress, startAddress, writeData);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写单个PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写单个线圈值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool WriteCoilOne(byte slaveAddress, ushort startAddress, bool data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
try
{
_modbusMaster.WriteSingleCoil(slaveAddress, startAddress, data);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写单个线圈值失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写单个线圈值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task<bool> WriteCoilOneAsync(byte slaveAddress, ushort startAddress, bool data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
try
{
await _modbusMaster.WriteSingleCoilAsync(slaveAddress, startAddress, data);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写单个线圈值失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写单个有符号值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task<bool> WriteShortOneAsync(byte slaveAddress, ushort startAddress, short data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
try
{
ushort writeData = BitConverter.ToUInt16(new ReadOnlySpan<byte>(BitConverter.GetBytes(data)));
await _modbusMaster.WriteSingleRegisterAsync(slaveAddress, startAddress, writeData);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写单个PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写多个无符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool WriteUshortsMany(byte slaveAddress, ushort startAddress, ushort[] data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
try
{
_modbusMaster.WriteMultipleRegisters(slaveAddress, startAddress, data);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写多个PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写多个无符号值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task<bool> WriteUshortsManyAsync(byte slaveAddress, ushort startAddress, ushort[] data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
try
{
await _modbusMaster.WriteMultipleRegistersAsync(slaveAddress, startAddress, data);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写多个PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写多个有符号值
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public bool WriteShortsMany(byte slaveAddress, ushort startAddress, short[] data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
if (data == null || data.Length == 0)
{
return false;
}
try
{
ushort[] writeData = data.Select(p => BitConverter.ToUInt16(new ReadOnlySpan<byte>(BitConverter.GetBytes(p)))).ToArray();
_modbusMaster.WriteMultipleRegisters(slaveAddress, startAddress, writeData);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写多个PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
/// <summary>
/// 写多个有符号值(异步)
/// </summary>
/// <param name="slaveAddress"></param>
/// <param name="startAddress"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task<bool> WriteShortsManyAsync(byte slaveAddress, ushort startAddress, short[] data)
{
if (_modbusMaster == null)
{
this.Connet();
return false;
}
if (data == null || data.Length == 0)
{
return false;
}
try
{
ushort[] writeData = data.Select(p => BitConverter.ToUInt16(new ReadOnlySpan<byte>(BitConverter.GetBytes(p)))).ToArray();
await _modbusMaster.WriteMultipleRegistersAsync(slaveAddress, startAddress, writeData);
return true;
}
catch (Exception ex)
{
LogHelper.Error($"写多个PLC信息失败,错误消息:{ex.Message}");
this.Connet();
return false;
}
}
}
}
@@ -0,0 +1,36 @@
using JSMachine.WMS.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.PLC.ModbusImpl.Elevator
{
/// <summary>
/// 电梯 PLC 通信实现,初始化时将连接实例注册到 PLC 管理器供后台任务使用。
/// </summary>
public class ElevatorPlc : ModbusEngineBase, IModbusEngine
{
/// <summary>
/// 初始化并连接
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
/// <param name="plc_key"></param>
/// <returns></returns>
public override IModbusEngine Init(string ip, int port)
{
int elevatorNo = Global.AppSettings.ElevatorConfig.First(p => p.IP == ip).ElevatorNo;
IModbusEngine modbus = base.Init(ip, port);
//必须先将PLC实例添加到字典,否则job先执行后无法拿到实例会导致无法重连
PLCManager.AddPLC(elevatorNo, modbus);
modbus.Connet();
return modbus;
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.PLC.ModbusImpl.Elevator.Enums
{
public enum ElevatorType
{
/// <summary>
/// 一号提升机
/// </summary>
ElevatorOne=1,
/// <summary>
/// 二号提升机
/// </summary>
ElevatorTwo=2
}
}
@@ -0,0 +1,32 @@
using JSMachine.WMS.Common;
using JSMachine.WMS.Infrastructure.AppConfiguration;
using Microsoft.AspNetCore.Builder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.PLC.ModbusImpl.Elevator.Exstension
{
/// <summary>
/// 电梯 PLC 的 ASP.NET Core 应用构建扩展。
/// </summary>
public static class ElevatorPlcExstension
{
/// <summary>
/// 按配置异步初始化每台电梯的 PLC 连接。
/// </summary>
/// <param name="app">当前应用构建器。</param>
public static void UseElevatorPlc(this IApplicationBuilder app)
{
foreach (ElevatorConfig elevatorConfig in Global.AppSettings.ElevatorConfig)
{
Task.Run(() =>
{
new ElevatorPlc().Init(elevatorConfig.IP, elevatorConfig.Port);
});
}
}
}
}
@@ -0,0 +1,65 @@
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Enums;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.PLC.ModbusImpl.Elevator.ModbusIO.Read
{
public static class ElevatorStatusRead
{
/// <summary>
/// 读取所有提升机状态
/// </summary>
/// <returns></returns>
public static async Task<List<ElevatorPlcStatus>> ReadAllElevatorStatus()
{
ElevatorPlcStatus elevatorStatusOne = await ReadElevatorStatus(1);
ElevatorPlcStatus elevatorStatusTwo = await ReadElevatorStatus(2);
elevatorStatusOne ??= new() { ElevatorNo = 1 };
elevatorStatusTwo ??= new() { ElevatorNo = 2 };
return [elevatorStatusOne, elevatorStatusTwo];
}
/// <summary>
/// 读取指定提升机状态
/// </summary>
/// <param name="elevatorNo"></param>
/// <returns></returns>
public static async Task<ElevatorPlcStatus> ReadElevatorStatus(int elevatorNo)
{
IModbusEngine modbusEngine = PLCManager.GetPLC(elevatorNo);
if (modbusEngine == null)
return null;
ushort[] data = await modbusEngine.ReadUshortsAsync(1, 0, 20);
if (data == null)
{
return null;
}
ElevatorPlcStatus elevatorStatus = new()
{
ElevatorNo = elevatorNo,
StockInPutdownChanelOne = data[2] == 1,
StockInPutdownChanelTwo = data[0] == 1,
StockInPickupChannelOne = data[7] == 1,
StockInPickupChannelTwo = data[5] == 1,
StockOutPutdownChanelOne = data[6] == 1,
StockOutPutdownChanelTwo = data[4] == 1,
StockOutPickupChanelOne = data[3] == 1,
StockOutPickupChanelTwo = data[1] == 1,
};
return elevatorStatus;
}
}
}
@@ -0,0 +1,40 @@
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
namespace JSMachine.WMS.PLC.ModbusImpl.PaperFeedGroundRack.ModbusIO.Write
{
public static class ElevatorWrite
{
public async static Task<bool> WriteElevator(ElevatorPlcStatus elevatorStatus)
{
IModbusEngine modbusEngine = PLCManager.GetPLC(elevatorStatus.ElevatorNo);
if (modbusEngine == null)
return false;
if (elevatorStatus.StockInPutdownChanelOne)
await modbusEngine.WriteUshortOneAsync(1, 10, elevatorStatus.StockInPutdownChanelOne ? (ushort)1 : (ushort)0);
if (elevatorStatus.StockInPutdownChanelTwo)
await modbusEngine.WriteUshortOneAsync(1, 8, elevatorStatus.StockInPutdownChanelTwo ? (ushort)1 : (ushort)0);
if (elevatorStatus.StockInPickupChannelOne)
await modbusEngine.WriteUshortOneAsync(1, 15, elevatorStatus.StockInPickupChannelOne ? (ushort)1 : (ushort)0);
if (elevatorStatus.StockInPickupChannelTwo)
await modbusEngine.WriteUshortOneAsync(1, 13, elevatorStatus.StockInPickupChannelTwo ? (ushort)1 : (ushort)0);
if (elevatorStatus.StockOutPutdownChanelOne)
await modbusEngine.WriteUshortOneAsync(1, 14, elevatorStatus.StockOutPutdownChanelOne ? (ushort)1 : (ushort)0);
if (elevatorStatus.StockOutPutdownChanelTwo)
await modbusEngine.WriteUshortOneAsync(1, 12, elevatorStatus.StockOutPutdownChanelTwo ? (ushort)1 : (ushort)0);
if (elevatorStatus.StockOutPickupChanelOne)
await modbusEngine.WriteUshortOneAsync(1, 11, elevatorStatus.StockOutPickupChanelOne ? (ushort)1 : (ushort)0);
if (elevatorStatus.StockOutPickupChanelTwo)
await modbusEngine.WriteUshortOneAsync(1, 9, elevatorStatus.StockOutPickupChanelTwo ? (ushort)1 : (ushort)0);
return true;
}
}
}
@@ -0,0 +1,53 @@
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.PLC.ModbusImpl.Elevator.Model
{
/// <summary>
/// 提升机状态,用于读取PLC的时候表明提升机允许干什么,用于写PLC的时候表示动作已经完成给PLC反馈
/// </summary>
public class ElevatorPlcStatus
{
/// <summary>
/// 提升机编号 1,2
/// </summary>
public int ElevatorNo { get; set; }
/// <summary>
/// 入库放货通道2 网带号1001 读取数据位0 写入数据位8
/// </summary>
public bool StockInPutdownChanelTwo { get; set; }
/// <summary>
/// 入库放货通道1 网带号1011 读取数据位2 写入数据位10
/// </summary>
public bool StockInPutdownChanelOne { get; set; }
/// <summary>
/// 入库取货通道2 网带号2004 读取数据位5 写入数据位13
/// </summary>
public bool StockInPickupChannelTwo { get; set; }
/// <summary>
/// 入库取货通道1 网带号2014 读取数据位7 写入数据位15
/// </summary>
public bool StockInPickupChannelOne { get; set; }
/// <summary>
/// 出库放货通道2 网带号2001 读取数据位4 写入数据位12
/// </summary>
public bool StockOutPutdownChanelTwo { get; set; }
/// <summary>
/// 出库放货通道1 网带号2011 读取数据位6 写入数据位14
/// </summary>
public bool StockOutPutdownChanelOne { get; set; }
/// <summary>
/// 出库取货通道2 网带号1006 读取数据位1 写入数据位9
/// </summary>
public bool StockOutPickupChanelTwo { get; set; }
/// <summary>
/// 出库取货通道1 网带号1016 读取数据位3 写入数据位11
/// </summary>
public bool StockOutPickupChanelOne { get; set; }
}
}
@@ -0,0 +1,79 @@
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.ModbusIO.Read;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
namespace JSMachine.WMS.PLC.ModbusImpl.Elevator.Util
{
public static class ElevatorUtil
{
private static IStorageRackService IStorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
private static IERPTaskService IERPTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
/// <summary>
/// 获取可用的提升机库位
/// </summary>
/// <param name="floor"></param>
/// <returns></returns>
public static async Task<StorageRackDto> GetUsableElevatorChannel(Guid erpTaskId,FloorEnum floor)
{
List<StorageRackDto> usableElevatorWareHouse = await IStorageRackService.GetUsableElevatorWareHouse(floor);
if (usableElevatorWareHouse.IsNullOrEmpty())
{
string msg = $"{(floor == FloorEnum.FirstFloor ? 1 : 2)}楼所有提升机库位均被WMS锁定";
LogHelper.Warn(msg);
_ = IERPTaskService.UpdateTaskMsg(erpTaskId, msg);
return null;
}
List<ElevatorPlcStatus> elevatorPlcStatuses = await ElevatorStatusRead.ReadAllElevatorStatus();
StorageRackDto elevator = null;
foreach (StorageRackDto elvatorWarehouse in usableElevatorWareHouse)
{
int elevatorNo = elvatorWarehouse.StorageRackName.Contains("1号") ? 1 : 2;
ElevatorPlcStatus elevatorPlcStatus = elevatorPlcStatuses.First(p => p.ElevatorNo == elevatorNo);
if (floor == FloorEnum.FirstFloor)
{
if (elvatorWarehouse.ElevatorChannel == 1011 && elevatorPlcStatus.StockInPutdownChanelOne ||
elvatorWarehouse.ElevatorChannel == 1001 && elevatorPlcStatus.StockInPutdownChanelTwo)
{
elevator = elvatorWarehouse;
break;
}
}
else
{
if (elvatorWarehouse.ElevatorChannel == 2011 && elevatorPlcStatus.StockOutPutdownChanelOne ||
elvatorWarehouse.ElevatorChannel == 2001 && elevatorPlcStatus.StockOutPutdownChanelTwo)
{
elevator = elvatorWarehouse;
break;
}
}
}
if(elevator == null)
{
string msg = $"{(floor == FloorEnum.FirstFloor ? 1 : 2)}楼 提升机库位不允许放货或者被WMS锁定";
LogHelper.Warn(msg);
_ = IERPTaskService.UpdateTaskMsg(erpTaskId, msg);
}
return elevator;
}
}
}
@@ -0,0 +1,55 @@
using JSMachine.WMS.Common;
using JSMachine.WMS.Infrastructure.AppConfiguration;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.PLC.ModbusImpl.Elevator;
using System.Collections.Concurrent;
namespace JSMachine.WMS.PLC.ModbusImpl
{
/// <summary>
/// 进程内 PLC 引擎注册表,按提升机编号管理通信实例。
/// </summary>
public static class PLCManager
{
private static readonly ConcurrentDictionary<int, IModbusEngine> PLC_Dic = new();
/// <summary>
/// 添加或替换指定提升机的 PLC 引擎。
/// </summary>
/// <param name="elevatorNo">提升机编号。</param>
/// <param name="modbusEngine">对应的 Modbus 引擎。</param>
public static void AddPLC(int elevatorNo, IModbusEngine modbusEngine)
{
if (PLC_Dic.ContainsKey(elevatorNo))
{
PLC_Dic[elevatorNo] = modbusEngine;
}
else
{
PLC_Dic.TryAdd(elevatorNo, modbusEngine);
}
}
/// <summary>
/// 获取指定提升机的 PLC 引擎;未注册时记录警告并返回空值。
/// </summary>
/// <param name="elevatorNo">提升机编号。</param>
/// <returns>已注册的通信引擎,未连接时返回空值。</returns>
public static IModbusEngine GetPLC(int elevatorNo)
{
if (!PLC_Dic.ContainsKey(elevatorNo))
{
LogHelper.Warn($"{elevatorNo} 号提升机PLC尚未连接");
//ElevatorConfig elevatorConfig = Global.AppSettings.ElevatorConfig.First(p => p.ElevatorNo == elevatorNo);
//new ElevatorPlc().Init(elevatorConfig.IP, elevatorConfig.Port);
return null;
}
return PLC_Dic[elevatorNo];
}
}
}
@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.PLC.Utils
{
/// <summary>
/// PLC字解析辅助类
/// </summary>
public static class WordResolveHelper
{
/// <summary>
/// 解析PLC的字为枚举
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="word"></param>
/// <returns></returns>
public static List<T> ResolveWord<T>(short word) where T : Enum
{
List<T> machineUnitConfigTwoTypes = new();
//注意,枚举类型没有属性,只有字段
int enumCount = typeof(T).GetFields().Length;
int max = 1;
for (int i = 1; i < enumCount; i++)
{
max *= 2;
}
//位与运算定义:只有1和1的位于运算结果为1,其他情况均为0
for (int i = 1; i <= max; i *= 2)
{
short ret = (short)(word & i);
if (ret != 0)
{
machineUnitConfigTwoTypes.Add((T)Enum.Parse(typeof(T), i.ToString()));
}
}
return machineUnitConfigTwoTypes;
}
/// <summary>
/// 解析PLC的字为枚举
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="word"></param>
/// <returns></returns>
public static List<T> ResolveWord<T>(ushort word) where T : Enum
{
List<T> machineUnitConfigTwoTypes = new();
//注意,枚举类型没有属性,只有字段
int enumCount = typeof(T).GetFields().Length;
int max = 1;
for (int i = 1; i < enumCount; i++)
{
max *= 2;
}
//位与运算定义:只有1和1的位于运算结果为1,其他情况均为0
for (int i = 1; i <= max; i *= 2)
{
short ret = (short)(word & i);
if (ret != 0)
{
machineUnitConfigTwoTypes.Add((T)Enum.Parse(typeof(T), i.ToString()));
}
}
return machineUnitConfigTwoTypes;
}
}
}