using AgroBase.Models; using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Windows.Forms; using System.Threading.Tasks; using static AgroBase.Models.Enums; namespace AgroBase.Services { public class PZEMService { public static bool Iniciado { get { return UltimaLeitura.DataHora.AddMilliseconds(ModbusService.TempoLimiteConexao) > DateTime.Now; } } public static byte slaveAddress = 0x01; public static PZEMModel UltimaLeitura = new PZEMModel(); public static PZEMModel PenultimaLeitura = new PZEMModel(); public static double TensaoMinima { get; set; } = 36.0; public static double TensaoMaxima { get; set; } = 42.0; public static double CorrenteMax { get; set; } = 20.0; public static bool ModoLeitura = false; private static double FatorLeituraTensao = 0.01; private static double FatorLeituraCorrente = 0.025; private static double FatorLeituraPotencia = 0.001; public static Dictionary CodigosFuncoes = new Dictionary() { { "LeituraParametro", 0x03 }, { "AquisicaoDados", 0x04 }, { "GravacaoParametro", 0x06 }, { "ResetEnergia", 0x42 }, { "ErroGravaParametro", 0x86 }, { "Calibrar", 0x41 }, { "ErroCalibrar", 0xC1 }, }; public static Dictionary Parametros = new Dictionary() { { "AlarmeTensaoMaxima", 0x0000 }, { "AlarmeTensaoMinima", 0x0001 }, { "EnderecoRTU", 0x0002 }, { "EscalaCorrenteAtual", 0x0003 }, }; public static CoulombCounterService ContadorCarga = new CoulombCounterService(CorrenteMax); public static async Task VerificaPortaPZEM(SerialPort porta) { ModbusService.DefinirPortaCOM(porta); var Comando = new ModbusComandoModel() { Dispositivo = T_Code.Pzm, Endereco = slaveAddress, Comando = ComandoAquisicaoDados(), Verificacao = true, TamanhoEsperado = 16 }; ModbusService.AdicionarComandoNaFila(Comando); while (Comando.RespondidoEm == DateTime.MinValue) { await Task.Delay(200); } DecifrarRespostaAquisicao(Comando.Resposta); if (Iniciado) { DefinirDispositivo(); return Iniciado; } if (!ModbusService.Iniciado) { ModbusService.LimparDadosPorta(); } return false; } private static void DefinirDispositivo() { if (Iniciado) { SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel() { Endereco = ModbusService._PortaModbus.PortName, Dispositivo = T_Code.Pzm, Erro = !Iniciado, Versao = "17" }); } } public static bool DecifrarRespostaAquisicao(byte[] resposta) { try { if (resposta == null || !resposta.Any() || resposta.Length < 16) { return false; } if (resposta[1] == CodigosFuncoes["AquisicaoDados"]) { int numBytes = resposta[2]; if (numBytes == 16) // Se o número de bytes de dados for 16 { PZEMModel modelo = new PZEMModel() { DataHora = DateTime.Now, }; var tensao_raw = "0x" + resposta[3].ToString("X") + resposta[4].ToString("X"); modelo.Tensao = Convert.ToInt32(tensao_raw, 16) * FatorLeituraTensao; var corrente_raw = "0x" + resposta[5].ToString("X") + resposta[6].ToString("X"); modelo.Corrente = Convert.ToInt32(corrente_raw, 16) * FatorLeituraCorrente; var potencia_raw = "0x" + resposta[7].ToString("X") + resposta[8].ToString("X") + resposta[9].ToString("X") + resposta[10].ToString("X"); modelo.Potencia = Convert.ToInt32(potencia_raw, 16) * FatorLeituraPotencia; var energia_raw = "0x" + resposta[11].ToString("X") + resposta[12].ToString("X") + resposta[13].ToString("X") + resposta[14].ToString("X"); modelo.Energia = Convert.ToInt32(energia_raw, 16) * 1.0; var almMax_raw = "0x" + resposta[15].ToString("X") + resposta[16].ToString("X"); modelo.AlarmeTensaoMax = almMax_raw == "0xFFFF"; var almMin_raw = "0x" + resposta[17].ToString("X") + resposta[18].ToString("X"); modelo.AlarmeTensaoMin = almMin_raw == "0xFFFF"; UltimaLeitura = modelo; return true; } else { Console.WriteLine("Número de bytes de dados não esperado."); } } else if (resposta[1] == CodigosFuncoes["LeituraParametro"]) { var parametro_raw = "0x" + resposta[3].ToString("X") + resposta[4].ToString("X"); double Valor = Convert.ToInt16(parametro_raw, 16) * 0.01; if (ModoLeitura) { MessageBox.Show("Valor do parâmetro: " + Valor); } } else if (resposta[1] == CodigosFuncoes["GravacaoParametro"]) { MessageBox.Show("Parâmetro gravado com sucesso!"); } else if (resposta[1] == CodigosFuncoes["ErroGravaParametro"]) { MessageBox.Show("Erro ao gravar parâmetro!"); } else if (resposta[1] == CodigosFuncoes["Calibrar"]) { MessageBox.Show("Calibragem do PZEM-017 finalizada com sucesso!"); } else if (resposta[1] == CodigosFuncoes["ErroCalibrar"]) { MessageBox.Show("Erro ao calibrar o PZEM-017!"); } else { Console.WriteLine("Resposta inválida ou código de função incorreto."); } return false; } catch (Exception ex) { } return true; } public static bool ProcessarResposta(byte[] Resposta) { PenultimaLeitura = new PZEMModel() { AlarmeTensaoMax = UltimaLeitura.AlarmeTensaoMax, AlarmeTensaoMin = UltimaLeitura.AlarmeTensaoMin, Corrente = UltimaLeitura.Corrente, DataHora = UltimaLeitura.DataHora, Energia = UltimaLeitura.Energia, Potencia = UltimaLeitura.Potencia, Tensao = UltimaLeitura.Tensao }; bool Sucesso = DecifrarRespostaAquisicao(Resposta); ContadorCarga.AtualizarConsumo(); return Sucesso; } #region Comandos public static byte[] ComandoAquisicaoDados() { byte functionCode = CodigosFuncoes["AquisicaoDados"]; ushort startAddress = 0x0000; ushort numOfRegisters = 0x0008; List command = new List(); command.Add(slaveAddress); command.Add(functionCode); command.AddRange(BitConverter.GetBytes(startAddress).Reverse()); // Big endian command.AddRange(BitConverter.GetBytes(numOfRegisters).Reverse()); // Big endian byte[] crc = ModbusService.Crc16(command.ToArray()); // Supondo que você tenha a implementação do CRC16 command.AddRange(crc); return command.ToArray(); } public static byte[] ComandoSetParametro(byte Parametro, float valor) { // Convertendo o valor para o formato correto (LSB = 0.01) int valorInt = (int)(valor * 100); // Convertendo para inteiro conforme a resolução byte functionCode = CodigosFuncoes["GravacaoParametro"]; ushort registerAddress = Parametro; // Construindo o comando List command = new List(); command.Add(slaveAddress); command.Add(functionCode); command.AddRange(BitConverter.GetBytes(registerAddress).Reverse()); // Little endian command.AddRange(BitConverter.GetBytes((ushort)valorInt).Reverse()); // Little endian byte[] crc = ModbusService.Crc16(command.ToArray()); // Supondo que você tenha a implementação do CRC16 command.AddRange(crc); return command.ToArray(); } public static byte[] ComandoGetParametro(byte Parametro) { byte functionCode = CodigosFuncoes["LeituraParametro"]; ushort registerAddress = Parametro; ushort numberOfRegisters = 0x0001; // Lendo apenas um registro // Construindo o comando List command = new List(); command.Add(slaveAddress); command.Add(functionCode); command.AddRange(BitConverter.GetBytes(registerAddress).Reverse()); // Big endian command.AddRange(BitConverter.GetBytes(numberOfRegisters).Reverse()); // Big endian byte[] crc = ModbusService.Crc16(command.ToArray()); // Supondo que você tenha a implementação do CRC16 command.AddRange(crc); return command.ToArray(); } public static byte[] ComandoResetEnergia() { // Código de função específico para reset de energia byte functionCode = CodigosFuncoes["ResetEnergia"]; // Construindo o comando List command = new List(); command.Add(slaveAddress); command.Add(functionCode); // Adicionando o CRC ao comando byte[] crc = ModbusService.Crc16(command.ToArray()); // Supondo que você tenha a implementação do CRC16 command.AddRange(crc); return command.ToArray(); } public static byte[] ComandoCalibragem() { byte functionCode = CodigosFuncoes["Calibrar"]; // Bytes fixos para o comando de calibragem byte[] bytesComando = new byte[] { 0xF8, functionCode, 0x37, 0x21 }; // Calculando o CRC para os bytes do comando byte[] crc = ModbusService.Crc16(bytesComando); // Adicionando o CRC ao comando List comandoCalibragem = new List(bytesComando); comandoCalibragem.AddRange(crc); return comandoCalibragem.ToArray(); } #endregion } }