agrobot_base/AgroBase/AgroBase/Services/PZEMService.cs

304 lines
11 KiB
C#

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;
public static Dictionary<string, byte> CodigosFuncoes = new Dictionary<string, byte>()
{
{ "LeituraParametro", 0x03 },
{ "AquisicaoDados", 0x04 },
{ "GravacaoParametro", 0x06 },
{ "ResetEnergia", 0x42 },
{ "ErroGravaParametro", 0x86 },
{ "Calibrar", 0x41 },
{ "ErroCalibrar", 0xC1 },
};
public static Dictionary<string, byte> Parametros = new Dictionary<string, byte>()
{
{ "AlarmeTensaoMaxima", 0x0000 },
{ "AlarmeTensaoMinima", 0x0001 },
{ "EnderecoRTU", 0x0002 },
{ "EscalaCorrenteAtual", 0x0003 },
};
public static CoulombCounterService ContadorCarga = new CoulombCounterService(CorrenteMax);
public static async Task<bool> 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) * 0.02; // 0.01;
var corrente_raw = "0x" + resposta[5].ToString("X") + resposta[6].ToString("X");
modelo.Corrente = Convert.ToInt32(corrente_raw, 16) * 0.002; // 0.0015;
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) * 0.0001; // 0.1;
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<byte> command = new List<byte>();
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<byte> command = new List<byte>();
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<byte> command = new List<byte>();
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<byte> command = new List<byte>();
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<byte> comandoCalibragem = new List<byte>(bytesComando);
comandoCalibragem.AddRange(crc);
return comandoCalibragem.ToArray();
}
#endregion
}
}