MitsubishiPlc.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. using System;
  2. using System.ComponentModel;
  3. using System.Threading.Tasks;
  4. using HslCommunication;
  5. using HslCommunication.Profinet.Melsec;
  6. using Newtonsoft.Json;
  7. using TeamAAS.Communication.Attributes;
  8. using TeamAAS.Communication.Base;
  9. using TeamAAS.Communication.Interfaces;
  10. namespace TeamAAS.Communication.PLCs
  11. {
  12. [Communication("三菱 PLC", "PLC", "HslCommunication MC协议 TCP")]
  13. public class MitsubishiPlc : PlcCommunicationBase
  14. {
  15. [JsonIgnore] private MelsecMcNet _mc;
  16. [JsonIgnore] private bool _connected;
  17. public override string PlcVendor => "三菱 MC";
  18. private string _ipAddress = "127.0.0.1";
  19. [Category("II.连接配置"), DisplayName("IP 地址"), Description("PLC IP 地址")]
  20. public string IpAddress { get { return _ipAddress; } set { if (SetProperty(ref _ipAddress, value)) Notify(nameof(EndpointUrl)); } }
  21. private int _port = 6000;
  22. [Category("II.连接配置"), DisplayName("端口"), Description("PLC 端口(默认 6000)")]
  23. public int Port { get { return _port; } set { if (SetProperty(ref _port, value)) Notify(nameof(EndpointUrl)); } }
  24. public override string EndpointUrl
  25. {
  26. get { return $"tcp://{IpAddress}:{Port}"; }
  27. set
  28. {
  29. if (!string.IsNullOrWhiteSpace(value) && value.StartsWith("tcp://"))
  30. {
  31. var uri = new Uri(value);
  32. IpAddress = uri.Host;
  33. Port = uri.Port;
  34. }
  35. }
  36. }
  37. [JsonIgnore, Browsable(false)]
  38. public override bool IsConnected => _connected;
  39. public override event Action<object, bool> ConnectChangedEvent;
  40. public override event Action<object, string> DataReceivedEvent;
  41. public MitsubishiPlc() { }
  42. public override void Connect()
  43. {
  44. if (string.IsNullOrWhiteSpace(IpAddress))
  45. throw new InvalidOperationException("三菱 PLC IP 不能为空。");
  46. if (Port <= 0)
  47. throw new InvalidOperationException("三菱 PLC 端口必须大于 0。");
  48. Disconnect();
  49. _mc = new MelsecMcNet { IpAddress = IpAddress, Port = Port, ConnectTimeOut = 2000 };
  50. var result = _mc.ConnectServer();
  51. if (!result.IsSuccess)
  52. {
  53. _mc = null;
  54. throw new InvalidOperationException("三菱 PLC 连接失败: " + result.Message);
  55. }
  56. Driver = _mc;
  57. _connected = true;
  58. ConnectChangedEvent?.Invoke(this, true);
  59. Notify(nameof(IsConnected));
  60. }
  61. public override Task ConnectAsync() => Task.Run(() => Connect());
  62. public override void Disconnect()
  63. {
  64. _connected = false;
  65. Driver = null;
  66. if (_mc != null)
  67. {
  68. try { _mc.ConnectClose(); } catch { }
  69. _mc = null;
  70. }
  71. ConnectChangedEvent?.Invoke(this, false);
  72. Notify(nameof(IsConnected));
  73. }
  74. public override void Dispose() => Disconnect();
  75. private static string GetPrefix(string address)
  76. {
  77. var t = address.Trim().ToUpperInvariant();
  78. int i = 0;
  79. while (i < t.Length && char.IsLetter(t[i])) i++;
  80. if (i == 0) throw new FormatException($"无效的三菱地址: {address}");
  81. return t.Substring(0, i);
  82. }
  83. private static bool IsBitDevice(string address)
  84. {
  85. switch (GetPrefix(address))
  86. {
  87. case "M": case "X": case "Y": case "B": case "F": case "T": case "C": case "SM": case "L": case "S":
  88. return true;
  89. default:
  90. return false;
  91. }
  92. }
  93. public override object ReadValue(string address, DebugDataType type, ushort length = 1)
  94. {
  95. if (Driver == null) throw new InvalidOperationException("三菱 PLC 尚未连接。");
  96. bool isBit = IsBitDevice(address);
  97. if (isBit && type != DebugDataType.Bool)
  98. throw new InvalidOperationException($"{address} 是位设备({GetPrefix(address)}),只支持 Bool 类型读取");
  99. if (!isBit && type == DebugDataType.Bool)
  100. throw new InvalidOperationException($"{address} 是字设备({GetPrefix(address)}),不支持 Bool 类型读取");
  101. return base.ReadValue(address, type, length);
  102. }
  103. public override void WriteValue(string address, object value, DebugDataType type, ushort length = 1)
  104. {
  105. if (Driver == null) throw new InvalidOperationException("三菱 PLC 尚未连接。");
  106. bool isBit = IsBitDevice(address);
  107. if (isBit && type != DebugDataType.Bool)
  108. throw new InvalidOperationException($"{address} 是位设备({GetPrefix(address)}),只支持 Bool 类型写入");
  109. if (!isBit && type == DebugDataType.Bool)
  110. throw new InvalidOperationException($"{address} 是字设备({GetPrefix(address)}),不支持 Bool 类型写入");
  111. base.WriteValue(address, value, type, length);
  112. }
  113. }
  114. }