using Aitex.Core.RT.Log; using Aitex.Core.Util; using DocumentFormat.OpenXml.InkML; using MECF.Framework.Common.CommonData.PowerSupplier; using MECF.Framework.Common.Communications; using MECF.Framework.Common.Net; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace MECF.Framework.Common.Device.PowerSupplier { public class PowerSupplierSerialPortModbusDevice { #region 常量 private const short CURRENT_SETTING_ADDRESS = 0x0101; private const short OUTPUT_CONTROL_ADDRESS = 0x0110; private const short STEP_PERIOD_ADDRESS = 0x1400; private const short STEP_PERIOD_START_ADDRESS = 0x1640; private const short VOLTAGE_OUTPUT_ADDRESS = 0x0201; private const short POWER_CONTROL_ADDRESS = 0x0113; private const short POWER_RUN_MODEL_ADDRESS = 0x0111; /// /// 电源状态(00-cv输出,01-cc输出) /// private const short POWER_STATUS_ADDRESS = 0x0200; private const string SET_POINT = "SetPoint"; private const string CURRENT = "Current"; private const string VOLTAGE = "Voltage"; private const string ENABLED = "Enabled"; private const string POWER_STATUS = "PowerStatus"; private const string POWER_CONTROL = "PowerControl"; private const string POWER_RUN_MODEL = "PowerRunModel"; /// /// 步阶数据数量 /// private const int STEP_PERIOD_LENGTH = 6; #endregion #region 内部变量 private string _name; private ConcurrentQueue _commandQueue=new ConcurrentQueue(); private SerialPort _serialPort; private bool _connected; private object _locker = new object(); private int _lockTimeout = 1000; private PowerSupplierMessage _netMessage = new PowerSupplierMessage(); private object _sendLocker = new object(); private object _receiveLocker = new object(); private int _receiveTimeout = 1000; private int _sendTimeout = 1000; /// /// 错误 /// private string _errmsg; /// /// 离线时间 /// private DateTime _offlineDateTime = DateTime.Now; /// /// 是否重连 /// private bool _reconnect = false; /// /// 首次连接成功 /// private bool _isFirstConnected = false; #endregion #region 属性 public bool Connected { get { return _connected; } } #endregion /// /// 构造函数 /// /// /// public PowerSupplierSerialPortModbusDevice(string name, string portName, int baudRate = 9600, StopBits stopBits = StopBits.One, int dataBits = 8, Parity parity = Parity.None, bool reconnect = false) { _name = name; _serialPort = new SerialPort(); _serialPort.BaudRate = baudRate; _serialPort.StopBits = stopBits; _serialPort.DataBits = dataBits; _serialPort.Parity = parity; _serialPort.PortName = portName; _serialPort.ReadTimeout = _receiveTimeout; _serialPort.WriteTimeout = _sendTimeout; _serialPort.ErrorReceived += SerialPort_ErrorReceived; _reconnect = reconnect; PeriodicJob periodicJob = new PeriodicJob(20, OnTimer, $"{name}.ModbusDevice.Thread", true); } /// /// 出现错误 /// /// /// private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e) { LOG.WriteLog(eEvent.ERR_POWERSUPPLIER, _name, e.EventType.ToString()); } /// /// 定时器 /// /// private bool OnTimer() { if (!_connected) { if (DateTime.Now.Subtract(_offlineDateTime).TotalSeconds >= 5 && _commandQueue.Count != 0) { ClearSendQueue(); } if (_reconnect&&_isFirstConnected) { Start(); } return true; } if (_commandQueue.Count!=0) { if(_commandQueue.TryDequeue(out PowerSupplierCommand command)) { if (_connected) { if (command.CommandCode == 0x03) { ApplyDataOperation(command); } } } } return true; } /// /// 清空发送队列 /// private void ClearSendQueue() { try { while (_commandQueue.Count != 0) { _commandQueue.TryDequeue(out var result); } } catch { } } /// /// 连接 /// /// public bool Start() { if (!_connected) { try { _serialPort.Open(); LOG.WriteLog(eEvent.INFO_LINMOT, _name, $"connect port[{_serialPort.PortName}] success"); _connected = true; if(!_isFirstConnected) { _isFirstConnected = true; } return true; } catch (Exception ex) { return false; } } return true; } /// /// 设置通道输出开关控制 /// /// /// /// public void SetChannelOutputSwitchControl(byte channel, bool enabled) { if (Connected) { PowerSupplierCommand command = new PowerSupplierCommand(); command.Channel = channel; command.CommandCode = 0x06; command.Address = (ushort)(OUTPUT_CONTROL_ADDRESS); command.Datas = new ushort[] { enabled ? (ushort)01 : (ushort)00 }; SetOperation(command); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 设置电源控制 /// /// /// /// public void SetChannelPowerControl(byte channel,byte remoteControl) { if (Connected) { PowerSupplierCommand command = new PowerSupplierCommand(); command.Channel = channel; command.CommandCode = 0x06; command.Address = (ushort)(POWER_CONTROL_ADDRESS); command.Datas = new ushort[] { remoteControl }; SetOperation(command); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 设置电源运行模式 /// /// /// /// public void SetChannelPowerRunmodelControl(byte channel,byte model) { if (Connected) { PowerSupplierCommand command = new PowerSupplierCommand(); command.Channel = channel; command.CommandCode = 0x06; command.Address = (ushort)(POWER_RUN_MODEL_ADDRESS); command.Datas = new ushort[] { model }; SetOperation(command); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 设置步阶数据 /// /// /// public bool SetStepPeriod(byte channel,List stepDatas,int scale) { if(Connected) { PowerSupplierCommand command = new PowerSupplierCommand(); command.Channel = channel; command.CommandCode = 0x10; command.Address = (ushort)STEP_PERIOD_ADDRESS; command.RegisterCount =(ushort)(STEP_PERIOD_LENGTH * stepDatas.Count); command.Datas = new ushort[STEP_PERIOD_LENGTH * stepDatas.Count]; for(int i = 0;i /// 启动步阶 /// /// /// /// /// public bool StartStepPeriod(byte channel,ushort startStep,ushort endStep,ushort cycle) { if (Connected) { PowerSupplierCommand command = new PowerSupplierCommand(); command.Channel = channel; command.CommandCode = 0x10; command.Address = (ushort)STEP_PERIOD_START_ADDRESS; command.RegisterCount = 3; command.Datas = new ushort[3] { startStep,endStep,cycle }; return SetOperation(command); } else { WriteErrorMsg($"{_name} is not connected"); } return false; } /// /// 设置电流 /// /// /// /// public void SetCurrentValue(byte channel,ushort currentValue) { if (Connected) { PowerSupplierCommand command = new PowerSupplierCommand(); command.Channel = channel; command.CommandCode = 0x06; command.Address = (ushort)(CURRENT_SETTING_ADDRESS); command.Datas = new ushort[] { currentValue }; SetOperation(command); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 设置电源状态 /// /// /// /// public void SetChannelPowerStatus(byte channel, byte powerStatus) { if (Connected) { PowerSupplierCommand command = new PowerSupplierCommand(); command.Channel = channel; command.CommandCode = 0x06; command.Address = (ushort)(POWER_STATUS_ADDRESS); command.Datas = new ushort[] { powerStatus }; SetOperation(command); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 获取通道输出开关控制 /// /// /// public void GetChannelOutput(byte channel) { if (Connected) { PowerSupplierCommand applyCommand = new PowerSupplierCommand(); applyCommand.Channel = channel; applyCommand.CommandCode = 0x03; applyCommand.Address = (ushort)(OUTPUT_CONTROL_ADDRESS); applyCommand.RegisterCount = 1; applyCommand.Variables.Add(ENABLED,(0,1)); _commandQueue.Enqueue(applyCommand); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 获取通道电源控制 /// /// /// public void GetChannelPowerControl(byte channel) { if (Connected) { PowerSupplierCommand applyCommand = new PowerSupplierCommand(); applyCommand.Channel = channel; applyCommand.CommandCode = 0x03; applyCommand.Address = (ushort)(POWER_CONTROL_ADDRESS); applyCommand.RegisterCount = 1; applyCommand.Variables.Add(POWER_CONTROL, (0, 1)); _commandQueue.Enqueue(applyCommand); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 获取通道电流设置数值 /// /// /// public void GetChannelCurrentSetting(byte channel) { if (Connected) { PowerSupplierCommand applyCommand = new PowerSupplierCommand(); applyCommand.Channel = channel; applyCommand.CommandCode = 0x03; applyCommand.Address = (ushort)(CURRENT_SETTING_ADDRESS); applyCommand.RegisterCount = 1; applyCommand.Variables.Add(SET_POINT,(0,1)); _commandQueue.Enqueue(applyCommand); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 获取电源状态设置数值 /// /// /// public void GetChannelPowerStatus(byte channel) { if (Connected) { PowerSupplierCommand applyCommand = new PowerSupplierCommand(); applyCommand.Channel = channel; applyCommand.CommandCode = 0x03; applyCommand.Address = (ushort)(POWER_STATUS_ADDRESS); applyCommand.RegisterCount = 1; applyCommand.Variables.Add(POWER_STATUS, (0,1)); _commandQueue.Enqueue(applyCommand); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 获取电源运行模式 /// /// /// public void GetChannelPowerRunModel(byte channel) { if (Connected) { PowerSupplierCommand applyCommand = new PowerSupplierCommand(); applyCommand.Channel = channel; applyCommand.CommandCode = 0x03; applyCommand.Address = (ushort)(POWER_RUN_MODEL_ADDRESS); applyCommand.RegisterCount = 1; applyCommand.Variables.Add(POWER_RUN_MODEL, (0, 1)); _commandQueue.Enqueue(applyCommand); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 申请电压和电流数值 /// /// /// public void GetChannelVoltageAndCurrent(byte channel) { if (Connected) { PowerSupplierCommand applyCommand = new PowerSupplierCommand(); applyCommand.Channel = channel; applyCommand.CommandCode = 0x03; applyCommand.Address = (ushort)(VOLTAGE_OUTPUT_ADDRESS); applyCommand.RegisterCount = 4; applyCommand.Variables.Add(VOLTAGE,(0,2)); applyCommand.Variables.Add(CURRENT,(2,2)); _commandQueue.Enqueue(applyCommand); } else { WriteErrorMsg($"{_name} is not connected"); } } /// /// 设置操作 /// /// /// private bool SetOperation(PowerSupplierCommand command) { NetResult netResult = SetData(command); if (!netResult.IsSuccess) { LOG.WriteLog(eEvent.ERR_POWERSUPPLIER, _name, $"write value {command.Datas[0]} failed,{netResult.Message}"); return false; } return true; } /// /// 设置数据 /// /// /// /// public NetResult SetData(PowerSupplierCommand data) { if (Monitor.TryEnter(_locker, _lockTimeout)) { NetResult result = ReadFromServer(data); if (!result.IsSuccess) { Monitor.Exit(_locker); return NetResult.CreateFailedResult(result.ErrorCode, result.Message); } bool confirmResult = _netMessage.ConfirmResponseResult(); if (!confirmResult) { Monitor.Exit(_locker); return NetResult.CreateFailedResult(_netMessage.ErrorCode, _netMessage.ErrorMsg); } Monitor.Exit(_locker); return NetResult.CreateSuccessResult(); } else { return NetResult.CreateFailedResult(NetErrorCode.GetLockTimeout); } } /// /// 从服务端读取数据 /// /// /// /// private NetResult ReadFromServer(PowerSupplierCommand data) { byte[] buffer = _netMessage.Code(data); NetResult sendResult = Send(buffer); if (!sendResult.IsSuccess) { return NetResult.CreateFailedResult(sendResult.ErrorCode, sendResult.Message); } _netMessage.SendBytes = buffer; _netMessage.SetProtocolHeadBytesLength(); NetResult headerResult = Receive(_netMessage.ProtocolHeadBytesLength); if (!headerResult.IsSuccess) { return NetResult.CreateFailedResult(headerResult.ErrorCode, headerResult.Message); } _netMessage.HeadBytes = headerResult.Data; if (!_netMessage.CheckHeadBytesLegal()) { return NetResult.CreateFailedResult(NetErrorCode.InvalidHeader); } NetResult contentResult = Receive(_netMessage.GetContentLengthByHeadBytes()); if (!contentResult.IsSuccess) { return NetResult.CreateFailedResult(contentResult.ErrorCode, contentResult.Message); } _netMessage.ContentBytes = contentResult.Data; bool dataValid = _netMessage.CheckDataLegal(); if (!dataValid) { return NetResult.CreateFailedResult(_netMessage.ErrorCode, _netMessage.ErrorMsg); } return NetResult.CreateSuccessResult(); } /// /// 申请数据操作 /// /// private void ApplyDataOperation(PowerSupplierCommand command) { NetResult netResult = ApplyData(command); if (!netResult.IsSuccess) { List keys = command.Variables.Keys.ToList(); string str = String.Join(" ", keys); WriteErrorMsg($"apply {str} error"); return; } if(netResult.Data.Datas!=null) { Dictionary dictionary = command.Variables; List keys = dictionary.Keys.ToList(); foreach(string item in keys) { var result = dictionary[item]; if(item==ENABLED) { PowerSupplierDeviceConfigManager.Instance.UpdateModuleVariable(_name, command.Channel, ENABLED, netResult.Data.Datas[result.Item1] == 0x01); } else { if(result.Item2==1) { PowerSupplierDeviceConfigManager.Instance.UpdateModuleVariable(_name, command.Channel, item, netResult.Data.Datas[result.Item1]); } else if(result.Item2==2) { int value = netResult.Data.Datas[result.Item1] * 255 + netResult.Data.Datas[result.Item1+1]; PowerSupplierDeviceConfigManager.Instance.UpdateModuleVariable(_name, command.Channel, item, value); } } } } } /// /// 申请数据 /// /// 申请指令类型 /// 指令对象 /// 返回数据对象 public NetResult ApplyData(PowerSupplierCommand data) { if (Monitor.TryEnter(_locker, _lockTimeout)) { NetResult result = ReadFromServer(data); if (!result.IsSuccess) { Monitor.Exit(_locker); return NetResult.CreateFailedResult(result.ErrorCode, result.Message); } Monitor.Exit(_locker); return NetResult.CreateSuccessResult(_netMessage.Decode()); } else { return NetResult.CreateFailedResult(NetErrorCode.GetLockTimeout); } } /// /// 发送数据 /// /// /// public NetResult Send(byte[] data) { if (!Connected) { return NetResult.CreateFailedResult(NetErrorCode.NetOffline); } //清除缓存数据 ClearPreData(); //进入发送 if (Monitor.TryEnter(_sendLocker, _sendTimeout)) { if (_serialPort == null) { return NetResult.CreateFailedResult(NetErrorCode.NullSocketObject); } try { _serialPort.Write(data,0,data.Length); Monitor.Exit(_sendLocker); return NetResult.CreateSuccessResult(); } catch (Exception ex) { Monitor.Exit(_sendLocker); WriteErrorMsg(ex.Message); return NetResult.CreateFailedResult((int)NetErrorCode.InnerException, ex.Message); } } else { return NetResult.CreateFailedResult(NetErrorCode.GetLockTimeout); } } /// /// 接收数据 /// /// /// public NetResult Receive(int length) { if (!Connected) { return NetResult.CreateFailedResult(NetErrorCode.NetOffline); } if (Monitor.TryEnter(_receiveLocker, _receiveTimeout)) { if (_serialPort == null) { return NetResult.CreateFailedResult(NetErrorCode.NullSocketObject); } try { byte[] buffer = new byte[length]; DateTime dt = DateTime.Now; while(true) { if(_serialPort.BytesToRead>=length) { _serialPort.Read(buffer, 0, length); break; } if(DateTime.Now.Subtract(dt).TotalMilliseconds>=_receiveTimeout) { Monitor.Exit(_receiveLocker); return NetResult.CreateFailedResult(NetErrorCode.ReceiveTimeout); } } Monitor.Exit(_receiveLocker); return NetResult.CreateSuccessResult(buffer); } catch (SocketException ex) { Monitor.Exit(_receiveLocker); return NetResult.CreateFailedResult((int)NetErrorCode.InnerException, ex.Message); } catch (Exception ex) { Monitor.Exit(_receiveLocker); return NetResult.CreateFailedResult((int)NetErrorCode.InnerException, ex.Message); } } else { return NetResult.CreateFailedResult(NetErrorCode.GetLockTimeout); } } /// /// 清除先前的数据 /// public void ClearPreData() { if (!Connected) { return; } if (Monitor.TryEnter(_receiveLocker, _receiveTimeout)) { try { while (_serialPort.BytesToRead != 0) { byte[] buffer = new byte[_serialPort.BytesToRead]; _serialPort.Read(buffer,0, buffer.Length); } Monitor.Exit(_receiveLocker); } catch (SocketException ex) { Monitor.Exit(_receiveLocker); } catch (Exception ex) { Monitor.Exit(_receiveLocker); } } } /// /// 记录错误信息 /// /// private void WriteErrorMsg(string msg, bool disConnected = true) { if (disConnected) { _connected = false; _offlineDateTime = DateTime.Now; } if (_errmsg != msg) { _errmsg = msg; LOG.WriteLog(eEvent.ERR_POWERSUPPLIER, _name, msg); } } } }