using HslCommunication; using HslCommunication.ModBus; using OrpaonEMS.App.Models; using OrpaonEMS.Core.DbModel; using OrpaonEMS.Core.Enums; using OrpaonEMS.Core.Model; using Prism.Mvvm; using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using System.Windows; namespace OrpaonEMS.App.Services { /// /// PCS数据服务 /// public class InPowerPCSDataService : BindableBase { /// /// PCS运行状态模型 /// public PCSStateModel PcsRunState { get; set; } /// /// 英博ModbusTcp驱动 /// private ModbusTcpNet ModbusTcpNetDrive { get; set; } /// /// 扫描设备线程数据 /// public Thread ScanDeviceThread { set; get; } /// /// 扫描线程使能 /// public bool ThreadEnable { get; set; } = true; /// /// 读取的字节数据集合 /// public OperateResult? OperateResultBytes1 { get; set; } /// /// 读取的字节数据集合 /// public OperateResult? OperateResultBytes2 { get; set; } /// /// 读取的布尔数据集合 /// public OperateResult? OperateResultBool3 { get; set; } private string _Charg_DisChargInfo = string.Empty; /// /// 充放电信息 /// public string Charg_DisChargInfo { get { return _Charg_DisChargInfo; } set { _Charg_DisChargInfo = value; RaisePropertyChanged(); } } /// /// 定时器 /// static System.Timers.Timer CurTimer { get; set; } public InPowerPCSDataService(ILogService logService, IFreeSql freeSql, BmsDataService bmsDataService, ConfigDataService configDataService) { LogService = logService; FreeSql = freeSql; BmsDataService = bmsDataService; ConfigDataService = configDataService; //ModbusTcpNetDrive = new ModbusTcpNet("192.168.0.20", 502); ModbusTcpNetDrive = new ModbusTcpNet(ConfigDataService.PCSIP, 502); ModbusTcpNetDrive.AddressStartWithZero = true; ModbusTcpNetDrive.SetPersistentConnection(); var DATA = ModbusTcpNetDrive.ConnectServer(); if (!DATA.IsSuccess) { MessageBox.Show("PCS 连接失败"); } PwTranceCmdValues = new TranceCmdValue(0.7); PwTranceCmdValues.CmdValueChanged += PwTranceCmdValues_CmdValueChanged; //CurPcsAlarmModel = new PcsAlarmModel(); //BMS目前通信来自于PCS,所以封装在英博PCS中 //bMSRtCtrCell = bMSRtCtrCellInfo; //PCS运行状态模型实例化 PcsRunState = new PCSStateModel(this); ScanDeviceThread = new Thread(new ThreadStart(ScanDeviceThreadMethond)); ScanDeviceThread.Start(); CurPcsAlarmModel = new PcsAlarmModel(ChannelInfo); CurTimer = new System.Timers.Timer(); CurTimer.Elapsed += CurTimer_Elapsed;//到达时间的时候执行事件; CurTimer.AutoReset = true;//设置是执行一次(false)还是一直执行(true); CurTimer.Interval = 600;//设置是时钟 CurTimer.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件; CurTimer.Start(); //启动定时器 //报警消费 Task.Run(() => AlarmChannelAction()); } /// /// PCS报警模型 /// public PcsAlarmModel CurPcsAlarmModel { get; set; } /// /// 扫描方法 /// x=4;201 寄存器信息 只读 /// x=3;301 PCS 读取保持寄存器信息 /// /// private void ScanDeviceThreadMethond() { Thread.Sleep(500); while (ThreadEnable) { Thread.Sleep(100); //地址和手册错位1个字,那么转为Byte后就是2个字节,请参考地址 OperateResultBytes1 = ModbusTcpNetDrive.Read("x=4;201", 100); if (OperateResultBytes1.IsSuccess) { Power = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 20) * 0.1; AVol = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 0) * 0.1; BVol = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 2) * 0.1; CVol = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 4) * 0.1; ACur = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 6) * 0.1; BCur = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 8) * 0.1; CCur = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 10) * 0.1; NetFreq = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 12) * 0.01; TotalReactivePw = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 28) * 0.1; TotalApparentPw = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 36) * 0.1; InputVol = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 48) * 0.1; InputCur = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 50) * 0.1; InputPw = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 46) * 0.1; PwNetFactor = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 44) * 0.001; FinTemp = ModbusTcpNetDrive.ByteTransform.TransInt16(OperateResultBytes1.Content, 52); CurPcsAlarmModel.AlarmWord1 = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 142); CurPcsAlarmModel.AlarmWord2 = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 144); CurPcsAlarmModel.AlarmWord3 = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 146); CurPcsAlarmModel.AlarmWord4 = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 148); CurPcsAlarmModel.AlarmWord5 = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 110); //var dd= ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 50); //var dd2= ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 52); //var dd3= ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 54); //var dd4= ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 56); //MaxChargePower = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 186) * 0.1; //MaxDisChargePower = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 188) * 0.1; //bMSRtCtrCell.BMSStateValue = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 158); //bMSRtCtrCell.SOC = ModbusTcpNetDrive.ByteTransform.TransUInt16(OperateResultBytes1.Content, 164) * 0.1; AccChargPw = ModbusTcpNetDrive.ByteTransform.TransUInt32(OperateResultBytes1.Content, 58) * 0.001; AccDisChargPw = ModbusTcpNetDrive.ByteTransform.TransUInt32(OperateResultBytes1.Content, 62) * 0.001; //通过PCS交流侧的电压判断交流主断是否闭合 BmsDataService.BmsIO.PcsAcCircuitBreaker = PcsAcCircuitBreaker; PcsRunState.LinkBitValue = 1; //PCSLinkState = true; } else { PcsRunState.LinkBitValue = 2; //通信失败 //PCSLinkState = false; } OperateResultBool3 = ModbusTcpNetDrive.ReadDiscrete("81", 20); if (OperateResultBool3.IsSuccess) { PcsRunState.PCSStateValue = OperateResultBool3.Content.Skip(0).Take(3).ToArray(); PcsRunState.PcsSysFaultStateVaue = OperateResultBool3.Content[3]; PcsRunState.PcsSysAlarmVaue = OperateResultBool3.Content[4]; PcsRunState.PcsRemoteLocationStateValue = OperateResultBool3.Content[5]; PcsRunState.PcsOffLineStateValue = OperateResultBool3.Content[7]; } } } /// /// 指令追踪模型 /// public TranceCmdValue PwTranceCmdValues { get; set; } /// /// 功率指令的值超过变化的阀值 /// /// /// /// private void PwTranceCmdValues_CmdValueChanged(object sender, double targetpw) { //Console.WriteLine($"datetime:{DateTime.Now.ToString()} 指令变化 发送储能功率:{targetpw}"); if (targetpw == 0) { //Charg_DisChargInfo = ""; StandbyPwCmd(); } else if (targetpw > 0) { //Charg_DisChargInfo = "充电中"; SendPcsChargCmd(targetpw); } else { //Charg_DisChargInfo = "放电中"; SendPcsDisChargCmd(targetpw); } } #region Pcs的数据 /// /// PCS 交流侧的主断路器 /// 通过PCS交流侧的电压判断交流主断是否闭合 /// public bool PcsAcCircuitBreaker { get; set; } private double power; /// /// 储能PCS的实时功率 /// 正:代表放电 /// 负:代表充电 /// public double Power { get { return power; } set { power = value; if (value > 0) { Charg_DisChargInfo = "放电"; } else if (value < 0) { Charg_DisChargInfo = "充电"; } else { Charg_DisChargInfo = ""; } RaisePropertyChanged(); } } private double accChargPw; /// /// 交流累计充电 /// public double AccChargPw { get { return accChargPw; } set { accChargPw = value; RaisePropertyChanged(); } } private double accDisChargPw; /// /// 交流累计放电 /// public double AccDisChargPw { get { return accDisChargPw; } set { accDisChargPw = value; RaisePropertyChanged(); } } private double aVol; /// /// A相电压 /// public double AVol { get { return aVol; } set { aVol = value; if (value<5) { PcsAcCircuitBreaker = false; } else { PcsAcCircuitBreaker = true; } RaisePropertyChanged(); } } private double bVol; /// /// B相电压 /// public double BVol { get { return bVol; } set { bVol = value; RaisePropertyChanged(); } } private double cVol; /// /// C相电压 /// public double CVol { get { return cVol; } set { cVol = value; RaisePropertyChanged(); } } private double aCur; /// /// A相电流 /// public double ACur { get { return aCur; } set { aCur = value; RaisePropertyChanged(); } } private double bCur; /// /// B相电流 /// public double BCur { get { return bCur; } set { bCur = value; RaisePropertyChanged(); } } private double cCur; /// /// C相电流 /// public double CCur { get { return cCur; } set { cCur = value; RaisePropertyChanged(); } } private double netFreq; /// /// 电网频率 /// public double NetFreq { get { return netFreq; } set { netFreq = value; RaisePropertyChanged(); } } private double totalReactivePw; /// /// 电网无功功率 /// public double TotalReactivePw { get { return totalReactivePw; } set { totalReactivePw = value; RaisePropertyChanged(); } } private double totalApparentPw; /// /// 电网视在功率 /// public double TotalApparentPw { get { return totalApparentPw; } set { totalApparentPw = value; RaisePropertyChanged(); } } private double inputVol; /// /// 输入电压 /// public double InputVol { get { return inputVol; } set { inputVol = value; RaisePropertyChanged(); } } private double inputCur; /// /// 输入电流 /// public double InputCur { get { return inputCur; } set { inputCur = value; RaisePropertyChanged(); } } private double inputPw; /// /// 输入功率 /// public double InputPw { get { return inputPw; } set { inputPw = value; RaisePropertyChanged(); } } private double pwNetFactor; /// /// 功率因数 /// public double PwNetFactor { get { return pwNetFactor; } set { pwNetFactor = value; RaisePropertyChanged(); } } private double finTemp; /// /// 散热器温度 /// public double FinTemp { get { return finTemp; } set { finTemp = value; RaisePropertyChanged(); } } private double curCmdPw; /// /// 当前的指令Pw /// 正为充电 /// 负为放电 /// public double CurCmdPw { get { return curCmdPw; } set { curCmdPw = value; RaisePropertyChanged(); } } #endregion #region PCS实时曲线 public Random rand = new Random(); /// /// 定时器执行方法 /// /// /// /// private void CurTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e) { //如果Execute执行的是一个很耗时的方法,会导致方法未执行完毕,定时器又启动了一个线程来执行Execute方法 CurTimer.Stop(); //先关闭定时器 Application.Current.Dispatcher.Invoke(() => { if (RtTrend.Count < 100)//2分钟 { RtTrend.Add(new RtPcsPwChartModel() { QPw = Power, CmdPw = CurCmdPw, CurTime = DateTime.Now });//Power } else if (RtTrend.Count >= 100) { RtTrend.RemoveAt(0); RtTrend.Add(new RtPcsPwChartModel() { QPw = Power, CmdPw = CurCmdPw, CurTime = DateTime.Now });//Power } }); CurTimer.Start(); //执行完毕后再开启器 } private ObservableCollection _RtTrend = new ObservableCollection(); /// /// 实时曲线 数据集合 /// public ObservableCollection RtTrend { get { return _RtTrend; } set { _RtTrend = value; RaisePropertyChanged(); } } public ILogService LogService { get; } public IFreeSql FreeSql { get; } public BmsDataService BmsDataService { get; } public ConfigDataService ConfigDataService { get; } #endregion #region PCS封装功能 /// /// 发送PCS充电功率 /// 正是充电 /// /// public void SendPcsChargCmd(double targetpw) { CurCmdPw = targetpw; //Console.WriteLine($"datetime:{DateTime.Now.ToString()} 发送储能功率【充电】:{targetpw}"); var result1 = ModbusTcpNetDrive.Write("309", (short)(targetpw / 3.0 * 10)); if (!result1.IsSuccess) { Console.WriteLine($"datetime:{DateTime.Now.ToString()} 发送【充电】储能功率【失败】:{targetpw}"); } ModbusTcpNetDrive.Write("310", (short)(targetpw / 3.0 * 10)); ModbusTcpNetDrive.Write("311", (short)(targetpw / 3.0 * 10)); } /// /// 发送PCS充电功率 /// 正是充电 /// /// public async Task SendPcsChargCmdAsync(double targetpw) { CurCmdPw = targetpw; Console.WriteLine($"datetime:{DateTime.Now.ToString()} 发送储能功率【充电】:{targetpw}"); var result1 = await ModbusTcpNetDrive.WriteAsync("309", (short)(targetpw / 3.0 * 10)); if (!result1.IsSuccess) { Console.WriteLine($"datetime:{DateTime.Now.ToString()} 发送【充电】储能功率【失败】:{targetpw}"); } await ModbusTcpNetDrive.WriteAsync("310", (short)(targetpw / 3.0 * 10)); await ModbusTcpNetDrive.WriteAsync("311", (short)(targetpw / 3.0 * 10)); } /// /// 发送PCS放电功率 /// 负是放电 /// /// public void SendPcsDisChargCmd(double targetpw) { CurCmdPw = targetpw; //Console.WriteLine($"datetime:{DateTime.Now.ToString()} 发送储能功率【放电】:{targetpw}"); var result1 = ModbusTcpNetDrive.Write("309", (short)(targetpw / 3.0 * 10)); if (!result1.IsSuccess) { Console.WriteLine($"datetime:{DateTime.Now.ToString()} 发送【放电】储能功率【失败】:{targetpw}"); } ModbusTcpNetDrive.Write("310", (short)(targetpw / 3.0 * 10)); ModbusTcpNetDrive.Write("311", (short)(targetpw / 3.0 * 10)); } /// /// 发送PCS放电功率 /// 负是放电 /// /// public async Task SendPcsDisChargCmdAsync(double targetpw) { CurCmdPw = targetpw; Console.WriteLine($"datetime:{DateTime.Now.ToString()} 发送储能功率【放电】:{targetpw}"); var result1 = await ModbusTcpNetDrive.WriteAsync("309", (short)(targetpw / 3.0 * 10)); if (!result1.IsSuccess) { Console.WriteLine($"datetime:{DateTime.Now.ToString()} 发送【放电】储能功率【失败】:{targetpw}"); } await ModbusTcpNetDrive.WriteAsync("310", (short)(targetpw / 3.0 * 10)); await ModbusTcpNetDrive.WriteAsync("311", (short)(targetpw / 3.0 * 10)); } /// /// 发送PCS待机指令 /// 就是给0的数据值 /// public void StandbyPwCmd() { CurCmdPw = 0; //Console.WriteLine($"时间:{DateTime.Now.ToString()}:发送0功率值,PCS待机数据 "); ModbusTcpNetDrive.Write("309", (short)(0 / 3.0 * 10)); ModbusTcpNetDrive.Write("310", (short)(0 / 3.0 * 10)); ModbusTcpNetDrive.Write("311", (short)(0 / 3.0 * 10)); } /// /// 发送PCS待机指令 /// 就是给0的数据值 /// public async Task StandbyPwCmdAsync() { CurCmdPw = 0; Console.WriteLine($"时间:{DateTime.Now.ToString()}:发送0功率值,PCS待机数据 "); await ModbusTcpNetDrive.WriteAsync("309", (short)(0 / 3.0 * 10)); await ModbusTcpNetDrive.WriteAsync("310", (short)(0 / 3.0 * 10)); await ModbusTcpNetDrive.WriteAsync("311", (short)(0 / 3.0 * 10)); } /// /// 并离网的配置 /// public void PcsOffLineCmd(PCSOffLineInfo pCSOffLineInfo) { switch (pCSOffLineInfo) { case PCSOffLineInfo.OnLine: ModbusTcpNetDrive.Write("306", (short)1); break; case PCSOffLineInfo.OffLine: ModbusTcpNetDrive.Write("306", (short)0); break; default: break; } } /// /// 并离网的配置 /// public async Task PcsOffLineCmdAsync(PCSOffLineInfo pCSOffLineInfo) { switch (pCSOffLineInfo) { case PCSOffLineInfo.OnLine: await ModbusTcpNetDrive.WriteAsync("306", (short)1); break; case PCSOffLineInfo.OffLine: await ModbusTcpNetDrive.WriteAsync("306", (short)0); break; default: break; } } /// /// PCS启动 异步 /// public async Task PCSStartAsync() { var Result = await ModbusTcpNetDrive.WriteAsync("2", true); if (Result.IsSuccess) { return true; } return false; } /// /// PCS启动 /// public bool PCSStart() { var Result = ModbusTcpNetDrive.Write("2", true); if (Result.IsSuccess) { return true; } return false; } /// /// PCS停止 /// public bool PCSStop() { var Result = ModbusTcpNetDrive.Write("3", true); if (Result.IsSuccess) { return true; } return false; } /// /// PCS停止 异步 /// public async Task PCSStopAsync() { var Result = await ModbusTcpNetDrive.WriteAsync("3", true); if (Result.IsSuccess) { return true; } return false; } /// /// PCS故障复位 /// public bool PCSFaultReset() { var Result = ModbusTcpNetDrive.Write("1", true); if (Result.IsSuccess) { return true; } return false; } /// /// PCS故障复位 /// public async Task PCSFaultResetAsync() { var Result = await ModbusTcpNetDrive.WriteAsync("1", true); if (Result.IsSuccess) { return true; } return false; } /// /// PCS远程和本地切换 /// public async Task PCSRemoteLocation(bool value) { var Result = await ModbusTcpNetDrive.WriteAsync("7", value); if (Result.IsSuccess) { return true; } return false; } /// /// PCS远程和本地切换 /// public async Task PCSRemoteLocationAsync(bool value) { var Result = await ModbusTcpNetDrive.WriteAsync("7", value); if (Result.IsSuccess) { return true; } return false; } /// ///关闭驱动链接 /// public void CloseModbusRtu() { ThreadEnable = false; ModbusTcpNetDrive.ConnectClose(); } /// ///关闭驱动链接 /// public async Task CloseModbusRtuAsync() { ThreadEnable = false; await ModbusTcpNetDrive.ConnectCloseAsync(); } #endregion #region 报警 /// /// 队列通道 /// 当前队列消费当前产线的报警数据 /// public Channel ChannelInfo = Channel.CreateUnbounded(new UnboundedChannelOptions() { SingleWriter = false,//允许一次写入多条数据 SingleReader = true //一次只能读取一条消息 }); /// /// 报警通道任务执行 /// /// private async void AlarmChannelAction() { while (await ChannelInfo.Reader.WaitToReadAsync()) { if (ChannelInfo.Reader.TryRead(out var msgData)) { //FSqlContext.FDb.Select //统一的处理报警信息 FreeSql.Insert(new HistoryAlarm() { Category = "PCS", StartTime = msgData.alarmChannel.StartTime, EndTime = msgData.alarmChannel.EndTime, Level = msgData.alarmChannel.Level, StopDur = msgData.alarmChannel.AlarmDur, Content = msgData.alarmChannel.Content, WorkDay = DateTime.Now.ToString("yyyy-MM-dd") }).ExecuteAffrows(); Console.WriteLine($"时间:{DateTime.Now.ToString()}-内容:{msgData.alarmChannel.Content}"); } } } #endregion } }