agrobot_base/AgroBase/AgroBase/Services/BLD300RService.cs

760 lines
27 KiB
C#

using AgroBase.Forms;
using AgroBase.Models;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static AgroBase.Models.Enums;
using System.Windows.Forms;
using Emgu.CV.Structure;
using System.Drawing.Printing;
using System.Threading;
using System.IO;
using Newtonsoft.Json;
using System.Collections;
namespace AgroBase.Services
{
public class BLD300RService
{
public static bool Iniciado
{
get
{
if (!SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Bld) && DadosLeitura.All(x => !x.Iniciado))
{
return false;
}
if (_PortaBLD != null && !_PortaBLD.IsOpen)
{
try
{
_PortaBLD.Open();
}
catch (Exception ex)
{
RegisrarErrosComunicacao(0, $"Erro ao abrir porta {_PortaBLD.PortName}. Erro={ex.Message}");
//_PortaBLD = null;
}
}
return _PortaBLD != null && _PortaBLD.IsOpen && DadosLeitura.Any(x => x.Iniciado);
}
}
public static int TaxaAmostragem { get; set; } = 1000;
public static SerialPort _PortaBLD = new SerialPort();
public static List<BLD300RModel> DadosLeitura = new List<BLD300RModel>();
public static List<ModbusParametrosModel> Parametros = new List<ModbusParametrosModel>()
{
new ModbusParametrosModel()
{
Chave = "Versao",
Descricao = "Verifica a versão do driver",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x00BB
},
new ModbusParametrosModel()
{
Chave = "ControleRS485",
Descricao = "Habilita/Desabilita controle através da interface RS485",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x00B6
},
new ModbusParametrosModel()
{
Chave = "AlarmeCode",
Descricao = "Verifica o código de alarme",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x0076
},
new ModbusParametrosModel()
{
Chave = "NumPolos",
Descricao = "Verifica/Define o número de polos do motor definido no driver",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x0086
},
new ModbusParametrosModel()
{
Chave = "RPM_Max",
Descricao = "Verifica/Define o RPM máximo do motor através do controle",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x0092
},
new ModbusParametrosModel()
{
Chave = "Rampa",
Descricao = "Verifica/Define o tempo de rampa de aceleração/desaceleração (x 100 ms)",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x008A
},
new ModbusParametrosModel()
{
Chave = "RPM_SP",
Descricao = "Verifica/Define o RPM de Set Point",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x0056
},
new ModbusParametrosModel()
{
Chave = "RPM",
Descricao = "Verifica o RPM atual do motor",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 0.27,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x005F
},
new ModbusParametrosModel()
{
Chave = "Enabled",
Descricao = "Verifica/Define o status do motor",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x0066
},
new ModbusParametrosModel()
{
Chave = "Breaked",
Descricao = "Verifica/Define o status do freio do motor",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x006A
},
new ModbusParametrosModel()
{
Chave = "Direction",
Descricao = "Verifica/Define o sentido de giro do motor",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x006D
},
new ModbusParametrosModel()
{
Chave = "LimiteCorrente",
Descricao = "Verifica o limite de corrente definido",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 0.01,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0002,
Registro = 0x0096
},
new ModbusParametrosModel()
{
Chave = "Tensao",
Descricao = "Verifica a tensão de alimentação no driver",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 0.01,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0002,
Registro = 0x00C8
},
new ModbusParametrosModel()
{
Chave = "Temperatura",
Descricao = "Verifica a temperatura do driver",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 0.01,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0002,
Registro = 0x00d2
},
new ModbusParametrosModel()
{
Chave = "Restaurar",
Descricao = "Restaura os parâmetros de fábrica",
FuncaoGet = 0x03,
FuncaoSet = 0x06,
Escala = 1,
LeituraLoop = false,
LeituraGeral = false,
N_Registros = 0x0001,
Registro = 0x00CC
},
};
private static List<BLDComandoModel> FilaComandos = new List<BLDComandoModel>();
private static readonly object FilaLock = new object();
private static System.Windows.Forms.Timer tmrLoopComandos = new System.Windows.Forms.Timer() { Interval = 100 };
private static bool EnvioLiberado { get; set; } = true;
public static async Task<bool> VerificaPortaBLD(SerialPort porta)
{
if (!Iniciado)
{
porta.Close();
porta.BaudRate = 9600;
AtualizarPortaCOM(porta);
}
if (Iniciado)
{
return true;
}
EnvioLiberado = false;
var Parametro = Parametros.First(x => x.Chave == "Versao");
List<string> Mod_IDs = new List<string>();
foreach (var Modulo in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Where(x => !x.MovMotor.BLD_Inicializado))
{
byte Addr = 0;
try
{
Addr = Convert.ToByte(Modulo.MovMotor.Endereco, 16);
}
catch
{
RegisrarErrosComunicacao(Addr, $"Erro ao definir endereço");
}
if (DadosLeitura.Any(x => x.Endereco == Addr && x.Iniciado))
{
continue;
}
var comando = ModbusService.ComandoGetParametro(Addr, Parametro);
EnviarComando(_PortaBLD, comando);
var resposta = await SerialService.LerDadosDaPortaSerialAsync(_PortaBLD, 7, 1000);
DecifrarResposta(resposta, Parametro, Addr);
if (DadosLeitura.Any(x => x.Endereco == Addr && x.Iniciado))
{
Mod_IDs.Add(Modulo.Modulo_ID);
}
}
if (Iniciado)
{
DefinirDispositivo(Mod_IDs);
}
else
{
_PortaBLD.Close();
_PortaBLD = null;
}
EnvioLiberado = true;
return Iniciado;
}
public static void AtualizarPortaCOM(SerialPort Porta)
{
_PortaBLD = new SerialPort();
_PortaBLD.PortName = Porta.PortName;
_PortaBLD.BaudRate = Porta.BaudRate;
_PortaBLD.Open();
}
private static void ReconectarPortaCOM()
{
RegisrarErrosComunicacao(0, $"Reiniciando conexão com a porta {_PortaBLD.PortName}...");
try
{
_PortaBLD.Close();
}
catch (Exception ex)
{
RegisrarErrosComunicacao(0, $"Erro ao reiniciar conexão. Erro={ex.Message}");
}
if (Iniciado)
{
RegisrarErrosComunicacao(0, $"Reconexão com a porta {_PortaBLD.PortName} reestabelecida");
}
else
{
RegisrarErrosComunicacao(0, $"Erro ao reestabelecer conexão com a porta {_PortaBLD.PortName}");
}
}
private static void DefinirDispositivo(List<string> Mod_IDs)
{
if (Iniciado)
{
foreach (var Mod_ID in Mod_IDs)
{
var Modulo = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.FirstOrDefault(x => x.Modulo_ID == Mod_ID);
byte Addr = Convert.ToByte(Modulo.MovMotor.Endereco.Replace("0x", ""), 16);
if (DadosLeitura.Any(x => x.Endereco == Addr && !x.Configurado))
{
ConfigurarDriver(Addr);
}
if (!SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Bld && x.Mod_ID == Mod_ID))
{
var leitura = DadosLeitura.FirstOrDefault(x => x.Endereco == Addr);
SerialService.DispositivosMapeados.Add(new SerialService.DispositivoDetalhesModel()
{
PortaCOM = _PortaBLD.PortName,
Dispositivo = T_Code.Bld,
Erro = leitura.CodigoAlarme > 0,
Versao = leitura.Versao.ToString(),
Mod_ID = Mod_ID,
});
}
}
}
}
public static void IniciarRotinas()
{
//Task.Run(() => LoopEnvioComandos()); // Inicia o loop de comandos em segundo plano
Task.Run(() => ReceberDados()); // Inicia a recepção de dados em segundo plano
if (!tmrLoopComandos.Enabled)
{
tmrLoopComandos.Tick += TmrLoopComandos_Tick;
tmrLoopComandos.Start();
}
}
private static void TmrLoopComandos_Tick(object sender, EventArgs e)
{
if (Iniciado && EnvioLiberado)
{
Task.Run(() => LoopEnvioComandos());
}
}
private static void EnviarComando(SerialPort _Porta, byte[] Comando)
{
lock (FilaLock)
{
try
{
if (_Porta != null)
{
if (!_Porta.IsOpen)
{
_Porta.Open();
}
if (_Porta.IsOpen)
{
_Porta.Write(Comando, 0, Comando.Length);
}
}
}
catch (Exception ex)
{
throw ex;
}
}
}
private static async Task ReceberDados()
{
while (true)
{
await Task.Delay(TaxaAmostragem);
if (!Iniciado)
{
continue;
}
foreach (var Leitura in DadosLeitura.Where(x => x.Iniciado).OrderBy(x => x.Endereco))
{
foreach (var Parametro in Parametros.Where(x => x.LeituraLoop))
{
var Comando = new BLDComandoModel()
{
Endereco = Leitura.Endereco,
AguardaResposta = true,
Parametro = Parametro,
Get = true,
TimeoutResposta = 300
};
AdicionarComandoNaFila(Comando);
}
var Modulo = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.FirstOrDefault(x => Convert.ToByte(x.MovMotor.Endereco.Replace("0x", ""), 16) == Leitura.Endereco);
if (Modulo != null)
{
Modulo.MovMotor.RPM_Motor = Leitura.RPM;
Modulo.MovMotor.TemperaturaDriver = Math.Round(Leitura.Temperatura, 2);
Modulo.MovMotor.Corrente_Max = Math.Round(Leitura.CorrenteMax, 2);
Modulo.MovMotor.Tensao = Math.Round(Leitura.Tensao, 2);
Modulo.MovMotor.Ligado = Leitura.Ativado;
Modulo.MovMotor.Freio = Leitura.Freado;
Modulo.MovMotor.Sentido = Leitura.Direcao;
Modulo.MovMotor.CodigoAlarme = Leitura.CodigoAlarme;
Variaveis.OperacaoEmAndamento.DispMvd.Dados.RegistrarLogMov(Modulo);
if (Modulo.MovMotor.CodigoAlarme > 0 && Modulo.MovMotor.CodigoAlarme <= 99)
{
RegisrarErrosComunicacao(Leitura.Endereco, $"Módulo com código de alarme {Modulo.MovMotor.CodigoAlarme}, reiniciando o módulo...");
AdicionarComandoNaFila(new BLDComandoModel()
{
Endereco = Leitura.Endereco,
AguardaResposta = true,
TimeoutResposta = 200,
Get = false,
Parametro = Parametros.First(x => x.Chave == "AlarmeCode"),
Valor = 0
});
}
var Controle = Variaveis.OperacaoEmAndamento.Controle.TiposControle.FirstOrDefault(x => x.Tipo == T_Code.Mov);
Modulo.MovMotor.ProtocoloComando(Controle.UltimaDirecao);
}
}
}
}
private static bool DecifrarResposta(byte[] resposta, ModbusParametrosModel Parametro, byte Addr)
{
try
{
var Leitura = DadosLeitura.FirstOrDefault(x => x.Endereco == Addr);
if (Leitura == null)
{
Leitura = new BLD300RModel()
{
Endereco = Addr,
UltimoComandoRecebido = DateTime.MinValue,
parametros = new List<BLDParametrosRequisitar>()
{
new BLDParametrosRequisitar()
{
Registro = Parametro.Registro,
Escala = Parametro.Escala,
Valor = null
}
}
};
DadosLeitura.Add(Leitura);
}
var ParametroLeitura = Leitura.parametros.FirstOrDefault(x => x.Registro == Parametro.Registro);
if (ParametroLeitura == null)
{
ParametroLeitura = new BLDParametrosRequisitar()
{
Registro = Parametro.Registro,
Escala = Parametro.Escala,
Valor = null
};
Leitura.parametros.Add(ParametroLeitura);
}
if (resposta.Length > 3)
{
if (Parametros.Any(x => x.FuncaoGet == resposta[1]))
{
int num_registros = resposta[2];
byte[] bytes_resposta = resposta.Skip(3).Take(num_registros).ToArray(); // Ajustando para pegar 2 bytes por registro
Parametro.Valor = bytes_resposta;
Parametro.UltimaRequisicao = DateTime.MaxValue;
}
else if (Parametros.Any(x => x.FuncaoSet == resposta[1]))
{
Parametro.Valor = resposta.Skip(4).Take(2).ToArray();
}
else
{
RegisrarErrosComunicacao(Addr, $"Resposta inválida ou código de função incorreto. {BitConverter.ToString(resposta)}");
}
ParametroLeitura.Valor = Parametro.Valor;
Leitura.UltimoComandoRecebido = DateTime.Now;
return true;
}
RegisrarErrosComunicacao(Addr, $"Resposta inválida. {BitConverter.ToString(resposta)}");
return false;
}
catch (Exception ex)
{
RegisrarErrosComunicacao(Addr, $"Erro ao decifrar resposta. {BitConverter.ToString(resposta)}");
return false;
}
}
private static void ConfigurarDriver(byte Addr)
{
var Modulo = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.FirstOrDefault(x => Convert.ToByte(x.MovMotor.Endereco.Replace("0x", ""), 16) == Addr);
AdicionarComandoNaFila(new BLDComandoModel()
{
Endereco = Addr,
AguardaResposta = true,
TimeoutResposta = 200,
Get = false,
Parametro = Parametros.First(x => x.Chave == "ControleRS485"),
Valor = 1
});
AdicionarComandoNaFila(new BLDComandoModel()
{
Endereco = Addr,
AguardaResposta = true,
TimeoutResposta = 200,
Get = false,
Parametro = Parametros.First(x => x.Chave == "AlarmeCode"),
Valor = 0
});
AdicionarComandoNaFila(new BLDComandoModel()
{
Endereco = Addr,
AguardaResposta = true,
TimeoutResposta = 200,
Get = false,
Parametro = Parametros.First(x => x.Chave == "NumPolos"),
Valor = Modulo.MovMotor.NumeroPolos
});
AdicionarComandoNaFila(new BLDComandoModel()
{
Endereco = Addr,
AguardaResposta = true,
TimeoutResposta = 200,
Get = false,
Parametro = Parametros.First(x => x.Chave == "RPM_Max"),
Valor = Modulo.MovMotor.RPM_Max
});
AdicionarComandoNaFila(new BLDComandoModel()
{
Endereco = Addr,
AguardaResposta = true,
TimeoutResposta = 200,
Get = false,
Parametro = Parametros.First(x => x.Chave == "Rampa"),
Valor = Modulo.MovMotor.Rampa
});
AdicionarComandoNaFila(new BLDComandoModel()
{
Endereco = Addr,
AguardaResposta = true,
TimeoutResposta = 200,
Get = false,
Parametro = Parametros.First(x => x.Chave == "Enabled"),
Valor = 0
});
DadosLeitura.Where(x => x.Endereco == Addr).First().Configurado = true;
}
public static async Task EnviarComandoControle(BLDComandoModel Comando)
{
try
{
byte[] comando = null;
if (Comando.Get)
{
comando = ModbusService.ComandoGetParametro(Comando.Endereco, Comando.Parametro);
}
else
{
comando = ModbusService.ComandoSetParametro(Comando.Endereco, Comando.Parametro, Comando.Valor);
}
if (comando != null && comando.Length > 0)
{
//Console.WriteLine("Enviando comando para o endereço: " + Comando.Endereco);
EnviarComando(_PortaBLD, comando);
Comando.Enviado = true;
if (Comando.AguardaResposta)
{
byte[] resposta = await SerialService.LerDadosDaPortaSerialAsync(_PortaBLD, 7, Comando.TimeoutResposta);
bool Respondido = DecifrarResposta(resposta, Comando.Parametro, Comando.Endereco);
if (!Respondido)
{
RegisrarErrosComunicacao(Comando.Endereco, $"Timeout ao receber resposta. GET={Comando.Get}, Parametro={Comando.Parametro.Chave}, Valor={Comando.Valor}");
Comando.Enviado = false;
Comando.Momento = DateTime.Now.AddMilliseconds(500);
//ReconectarPortaCOM();
}
}
}
}
catch (IOException ex)
{
RegisrarErrosComunicacao(Comando.Endereco, $"Erro ao enviar comando. GET={Comando.Get}, Parametro={Comando.Parametro.Chave}, Valor={Comando.Valor}, Erro={ex.Message}");
Comando.Enviado = false;
Comando.Momento = DateTime.Now.AddMilliseconds(500);
ReconectarPortaCOM();
}
}
public static void AdicionarComandoNaFila(BLDComandoModel Comando)
{
lock (FilaLock)
{
if (!FilaComandos.Any(x => x.Endereco == Comando.Endereco && x.Parametro.Registro == Comando.Parametro.Registro && x.Get == Comando.Get && !x.Enviado))
{
FilaComandos.Add(Comando);
}
}
}
private static async Task LoopEnvioComandos()
{
EnvioLiberado = false;
try
{
await ProcessarFilaPrioridade();
List<BLDComandoModel> comandosParaEnviar;
lock (FilaLock)
{
comandosParaEnviar = FilaComandos.Where(x => !x.Enviado && x.Get).OrderBy(x => x.Momento).ToList();
}
foreach (var Comando in comandosParaEnviar)
{
await EnviarComandoControle(Comando);
await ProcessarFilaPrioridade();
}
}
catch (Exception ex)
{
RegisrarErrosComunicacao(0, $"Erro ao processar fila de comandos. Erro={ex.Message}");
}
finally
{
EnvioLiberado = true;
}
}
private static async Task ProcessarFilaPrioridade()
{
var FilaPrioridade = new List<BLDComandoModel>();
lock (FilaLock)
{
FilaPrioridade = FilaComandos.Where(x => !x.Enviado && !x.Get).OrderBy(x => x.Momento).ToList();
}
while (FilaPrioridade.Count > 0)
{
foreach (var Comando in FilaPrioridade)
{
await EnviarComandoControle(Comando);
}
lock (FilaLock)
{
FilaPrioridade = FilaComandos.Where(x => !x.Enviado && !x.Get).OrderBy(x => x.Momento).ToList();
}
}
}
private static void RegisrarErrosComunicacao(byte addr, string mensagem)
{
var Caminho = Variaveis.CaminhoLogsDispositivos;
if (!Directory.Exists(Caminho))
{
Directory.CreateDirectory(Caminho);
}
var Arquivo = T_Code.Bld.ToString() + "_err.txt";
Caminho += "/" + Arquivo;
mensagem = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:fff") + " - Porta " + _PortaBLD.PortName + " - " + addr + ": " + mensagem + Environment.NewLine;
if (!File.Exists(Caminho))
{
File.WriteAllText(Caminho, mensagem);
}
else
{
File.AppendAllText(Caminho, mensagem);
}
}
public class BLDParametrosRequisitar
{
public byte Registro { get; set; }
public byte[] Valor { get; set; }
public double Escala { get; set; }
}
public class BLDComandoModel
{
public DateTime Momento { get; set; } = DateTime.Now;
public bool Enviado { get; set; } = false;
public byte Endereco { get; set; }
public ModbusParametrosModel Parametro { get; set; }
public float Valor { get; set; }
public bool Get { get; set; }
public bool AguardaResposta { get; set; } = false;
public int TimeoutResposta { get; set; } = 1000;
}
}
}