agrobot_base/AgroBase/AgroBase/Services/PZEMService.cs

355 lines
13 KiB
C#

using AgroBase.Models;
using CefSharp.WinForms;
using CefSharp;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO.Ports;
using System.Linq;
using System.Windows.Forms;
using Newtonsoft.Json;
using Emgu.CV.CvEnum;
using Emgu.CV.Aruco;
using static AgroBase.Services.SerialService;
using System.Threading.Tasks;
using AgroBase.Forms;
namespace AgroBase.Services
{
public class PZEMService
{
public static bool Iniciado = false;
public static SerialPort PortaPZEM = null;
public static PZEMModel UltimaLeitura = new PZEMModel();
public static PZEMModel PenultimaLeitura = new PZEMModel();
public static double TensaoMinima { get; set; } = 30;
public static double TensaoMaxima { get; set; } = 42;
private static byte slaveAddress = 0x01;
private 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 void AtualizarPortaCOM(SerialPort Porta)
{
try
{
Porta.BaudRate = 9600;
Porta.DataReceived -= PortaPZEM_DataReceived;
Porta.DataReceived += PortaPZEM_DataReceived;
if (!Porta.IsOpen)
{
Porta.Open();
}
//var Comando = ComandoCalibragem();
var Comando = ComandoGetParametro(Parametros.First().Value);
string comandoHex = BitConverter.ToString(Comando).Replace("-", "");
EnviarComando(Porta, Comando);
}
catch (Exception ex)
{
Porta.Close();
}
}
public static void EnviarComando(SerialPort _Porta, byte[] Comando)
{
if (_Porta != null && _Porta.IsOpen)
{
_Porta.Write(Comando, 0, Comando.Length);
}
}
public static void PortaPZEM_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (Iniciado)
{
if (PortaPZEM.IsOpen)
{
PenultimaLeitura = UltimaLeitura;
int bytesToRead = PortaPZEM.BytesToRead;
byte[] buffer = new byte[bytesToRead];
PortaPZEM.Read(buffer, 0, bytesToRead);
DecifrarRespostaAquisicao(buffer);
}
}
else
{
var Porta = ((SerialPort)sender);
int bytesToRead = Porta.BytesToRead;
byte[] buffer = new byte[bytesToRead];
Porta.Read(buffer, 0, bytesToRead);
var Res = DecifrarRespostaAquisicao(buffer);
if (Res != null)
{
PortaPZEM = Porta;
Iniciado = true;
SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel()
{
PortaCOM = Porta.PortName,
Dispositivo = Enuns.T_Code.Pzm,
Erro = !Iniciado,
Versao = 17
});
ReceberDados().ConfigureAwait(false);
}
else
{
Porta.DataReceived -= PortaPZEM_DataReceived;
Porta.Close();
}
}
}
private static async Task<bool> ReceberDados()
{
EnviarComando(PortaPZEM, ComandoAquisicaoDados());
await Task.Delay(5000);
return await ReceberDados();
}
#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 = 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 = 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 = 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 = 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 = Crc16(bytesComando);
// Adicionando o CRC ao comando
List<byte> comandoCalibragem = new List<byte>(bytesComando);
comandoCalibragem.AddRange(crc);
return comandoCalibragem.ToArray();
}
#endregion
public static byte[] Crc16(byte[] data)
{
ushort crc = 0xFFFF;
foreach (byte pos in data)
{
crc ^= pos;
for (int i = 0; i < 8; i++)
{
if ((crc & 1) != 0)
{
crc >>= 1;
crc ^= 0xA001;
}
else
{
crc >>= 1;
}
}
}
return new byte[] { (byte)crc, (byte)(crc >> 8) };
}
public static bool VerificarCrc(string respostaHex)
{
byte[] respostaBytes = HexStringToByteArray(respostaHex);
byte[] crcRecebido = new byte[] { respostaBytes[respostaBytes.Length - 2], respostaBytes[respostaBytes.Length - 1] };
byte[] dados = new byte[respostaBytes.Length - 2];
Array.Copy(respostaBytes, 0, dados, 0, dados.Length);
byte[] crcCalculado = Crc16(dados);
return crcRecebido[0] == crcCalculado[0] && crcRecebido[1] == crcCalculado[1];
}
public static byte[] HexStringToByteArray(string hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
public static PZEMModel DecifrarRespostaAquisicao(byte[] resposta)
{
try
{
/*if (!VerificarCrc(resposta))
{
Console.Write("CRC inválido. A resposta pode estar corrompida.");
return null;
}*/
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";
double percent = FuncoesGlobais.Map(modelo.Tensao, TensaoMinima, TensaoMaxima, 0, 100);
Variaveis.OperacaoEmAndamento.Sensoriamento.PorcentagemBateria = percent;
frmInstancial.frmAcompanhamento.AtualizarSensores("Bateria: " + modelo.Tensao.ToString("0.00") + " V - " + modelo.Corrente.ToString("0.00") + " A - " + modelo.Potencia.ToString("0.00") + " W");
UltimaLeitura = modelo;
Console.WriteLine("PZEM Tensão: " + modelo.Tensao.ToString("0.00") + " - Corrente: " + modelo.Corrente.ToString("0.00")) ;
return modelo;
}
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;
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 new PZEMModel();
}
catch (Exception ex)
{
}
return null;
}
}
}