355 lines
13 KiB
C#
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
}
|