using System; using System.ComponentModel; using System.Threading.Tasks; using HslCommunication; using HslCommunication.Profinet.Melsec; using Newtonsoft.Json; using TeamAAS.Communication.Attributes; using TeamAAS.Communication.Base; using TeamAAS.Communication.Interfaces; namespace TeamAAS.Communication.PLCs { [Communication("三菱 PLC", "PLC", "HslCommunication MC协议 TCP")] public class MitsubishiPlc : PlcCommunicationBase { [JsonIgnore] private MelsecMcNet _mc; [JsonIgnore] private bool _connected; public override string PlcVendor => "三菱 MC"; private string _ipAddress = "127.0.0.1"; [Category("II.连接配置"), DisplayName("IP 地址"), Description("PLC IP 地址")] public string IpAddress { get { return _ipAddress; } set { if (SetProperty(ref _ipAddress, value)) Notify(nameof(EndpointUrl)); } } private int _port = 6000; [Category("II.连接配置"), DisplayName("端口"), Description("PLC 端口(默认 6000)")] public int Port { get { return _port; } set { if (SetProperty(ref _port, value)) Notify(nameof(EndpointUrl)); } } public override string EndpointUrl { get { return $"tcp://{IpAddress}:{Port}"; } set { if (!string.IsNullOrWhiteSpace(value) && value.StartsWith("tcp://")) { var uri = new Uri(value); IpAddress = uri.Host; Port = uri.Port; } } } [JsonIgnore, Browsable(false)] public override bool IsConnected => _connected; public override event Action ConnectChangedEvent; public override event Action DataReceivedEvent; public MitsubishiPlc() { } public override void Connect() { if (string.IsNullOrWhiteSpace(IpAddress)) throw new InvalidOperationException("三菱 PLC IP 不能为空。"); if (Port <= 0) throw new InvalidOperationException("三菱 PLC 端口必须大于 0。"); Disconnect(); _mc = new MelsecMcNet { IpAddress = IpAddress, Port = Port, ConnectTimeOut = 2000 }; var result = _mc.ConnectServer(); if (!result.IsSuccess) { _mc = null; throw new InvalidOperationException("三菱 PLC 连接失败: " + result.Message); } Driver = _mc; _connected = true; ConnectChangedEvent?.Invoke(this, true); Notify(nameof(IsConnected)); } public override Task ConnectAsync() => Task.Run(() => Connect()); public override void Disconnect() { _connected = false; Driver = null; if (_mc != null) { try { _mc.ConnectClose(); } catch { } _mc = null; } ConnectChangedEvent?.Invoke(this, false); Notify(nameof(IsConnected)); } public override void Dispose() => Disconnect(); private static string GetPrefix(string address) { var t = address.Trim().ToUpperInvariant(); int i = 0; while (i < t.Length && char.IsLetter(t[i])) i++; if (i == 0) throw new FormatException($"无效的三菱地址: {address}"); return t.Substring(0, i); } private static bool IsBitDevice(string address) { switch (GetPrefix(address)) { case "M": case "X": case "Y": case "B": case "F": case "T": case "C": case "SM": case "L": case "S": return true; default: return false; } } public override object ReadValue(string address, DebugDataType type, ushort length = 1) { if (Driver == null) throw new InvalidOperationException("三菱 PLC 尚未连接。"); bool isBit = IsBitDevice(address); if (isBit && type != DebugDataType.Bool) throw new InvalidOperationException($"{address} 是位设备({GetPrefix(address)}),只支持 Bool 类型读取"); if (!isBit && type == DebugDataType.Bool) throw new InvalidOperationException($"{address} 是字设备({GetPrefix(address)}),不支持 Bool 类型读取"); return base.ReadValue(address, type, length); } public override void WriteValue(string address, object value, DebugDataType type, ushort length = 1) { if (Driver == null) throw new InvalidOperationException("三菱 PLC 尚未连接。"); bool isBit = IsBitDevice(address); if (isBit && type != DebugDataType.Bool) throw new InvalidOperationException($"{address} 是位设备({GetPrefix(address)}),只支持 Bool 类型写入"); if (!isBit && type == DebugDataType.Bool) throw new InvalidOperationException($"{address} 是字设备({GetPrefix(address)}),不支持 Bool 类型写入"); base.WriteValue(address, value, type, length); } } }