agrobot_base/AgroBase/AgroBase/Services/BLD300RService.cs

660 lines
24 KiB
C#

using AgroBase.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static AgroBase.Models.Enums;
namespace AgroBase.Services
{
public class BLD300RService
{
private static EthernetService _ethernetService;
private static int ComandosNaoRespondidos = 0;
private static int ComandosNaoRespondidosMax = 24;
public static bool Iniciado
{
get
{
return _ethernetService != null && _ethernetService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Bld) && DadosLeitura.Any(x => x.Iniciado);
}
}
public static int TaxaAmostragem { get; set; } = 1000;
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",
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x00BB
},
new ModbusParametrosModel()
{
Chave = "ControleRS485",
Descricao = "Habilita/Desabilita controle através da interface RS485",
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x00B6
},
new ModbusParametrosModel()
{
Chave = "AlarmeCode",
Descricao = "Verifica o código de alarme",
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",
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",
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)",
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x008A
},
new ModbusParametrosModel()
{
Chave = "RPM_SP",
Descricao = "Verifica/Define o RPM de Set Point",
Escala = 1,
LeituraLoop = false,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x0056
},
new ModbusParametrosModel()
{
Chave = "RPM",
Descricao = "Verifica o RPM atual do motor",
Escala = 0.27,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x005F
},
new ModbusParametrosModel()
{
Chave = "Enabled",
Descricao = "Verifica/Define o status do motor",
Escala = 1,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x0066
},
new ModbusParametrosModel()
{
Chave = "Breaked",
Descricao = "Verifica/Define o status do freio do motor",
Escala = 1,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x006A
},
new ModbusParametrosModel()
{
Chave = "Direction",
Descricao = "Verifica/Define o sentido de giro do motor",
Escala = 1,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0001,
Registro = 0x006D
},
new ModbusParametrosModel()
{
Chave = "LimiteCorrente",
Descricao = "Verifica o limite de corrente definido",
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",
Escala = 0.01,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0002,
Registro = 0x00C8
},
new ModbusParametrosModel()
{
Chave = "Temperatura",
Descricao = "Verifica a temperatura do driver",
Escala = 0.01,
LeituraLoop = true,
LeituraGeral = true,
N_Registros = 0x0002,
Registro = 0x00d2
},
new ModbusParametrosModel()
{
Chave = "Restaurar",
Descricao = "Restaura os parâmetros de fábrica",
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 AsyncTaskTimerModel tmrLoopComandos;
private static List<string> ComandosComunicacao = new List<string>();
private static List<string> ErrosComunicacao = new List<string>();
private static bool EnvioLiberado { get; set; } = true;
public static void IniciarRotinas()
{
PararRotinas();
tmrLoopComandos = new AsyncTaskTimerModel("tmrLoopComandos", tmrLoopComandos_Tick, 100);
tmrLoopComandos.Start();
}
public static void PararRotinas()
{
tmrLoopComandos?.Dispose();
}
// Inicializa o serviço Ethernet com IP e porta específicos
public static async Task<bool> InicializarEthernetService()
{
string ipAddress = Variaveis.OperacaoEmAndamento.DispMvd.Dados.IP_Mov;
int port = VariaveisPortas.Ethernet_TCP;
_ethernetService?.Disconnect();
await Task.Delay(1000);
_ethernetService = new EthernetService(ipAddress, port);
return await _ethernetService.ConnectAsync();
}
public static async Task<bool> VerificaPortaBLD()
{
EnvioLiberado = false;
bool connect = await InicializarEthernetService();
if (!connect)
{
Console.WriteLine("Falha ao inicializar o serviço Ethernet.");
_ethernetService = null;
EnvioLiberado = true;
return false;
}
if (_ethernetService._isReconnecting)
{
return false;
}
if (!Iniciado && !await _ethernetService.CheckConnectionAsync())
{
Console.WriteLine("Falha ao conectar ao dispositivo via Ethernet.");
EnvioLiberado = true;
return 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.Inicializado))
{
byte Addr = 0;
try
{
Addr = Modulo.MovMotor._EnderecoCAN_Tx;
}
catch
{
RegisrarErrosComunicacao(Addr, $"Erro ao definir endereço");
}
if (DadosLeitura.Any(x => x.Endereco == Addr && x.Iniciado))
{
continue;
}
var comando = ModbusService.ComandoGetParametro(Addr, Parametro);
bool envioBemSucedido = await EnviarComando(_ethernetService, comando);
if (!envioBemSucedido)
{
Console.WriteLine($"Falha ao enviar dados para o dispositivo de endereço {Addr}.");
continue;
}
// Recebe a resposta via Ethernet
var resposta = await _ethernetService.ReceiveDataAsync(7);
//SerialService.MostrarComandoResposta(T_Code.Bld, comando, resposta);
DecifrarResposta(resposta, Parametro, Addr);
if (DadosLeitura.Any(x => x.Endereco == Addr && x.Iniciado))
{
Mod_IDs.Add(Modulo.Modulo_ID);
}
}
if (Mod_IDs.Any())
{
DefinirDispositivo(Mod_IDs);
}
EnvioLiberado = true;
return Iniciado;
}
private static async Task<bool> EnviarComando(EthernetService ethernetService, byte[] comando)
{
try
{
bool sucesso = await ethernetService.SendDataAsync(comando);
RegisrarComandosComunicacao(comando[0], $"TX: {sucesso} - {BitConverter.ToString(comando).Replace("-", " ")}");
return sucesso;
}
catch (Exception ex)
{
Console.WriteLine($"Erro ao enviar comando: {ex.Message}");
return false;
}
}
public static async Task EnviarComandoControle(BLDComandoModel Comando)
{
try
{
byte[] comando = Comando.Get
? ModbusService.ComandoGetParametro(Comando.Endereco, Comando.Parametro)
: ModbusService.ComandoSetParametro(Comando.Endereco, Comando.Parametro, Comando.Valor);
if (comando != null && comando.Length > 0)
{
// Envia o comando via Ethernet
Comando.ErroEnvio = !await EnviarComando(_ethernetService, comando);
Comando.Enviado = true;
// Recebe a resposta via Ethernet, independentemente de precisar processá-la ou não
byte[] resposta = await _ethernetService.ReceiveDataAsync(7, Comando.TimeoutResposta); // Timeout ajustável, aqui 1 segundo
//SerialService.MostrarComandoResposta(T_Code.Bld, comando, resposta);
if (Comando.AguardaResposta)
{
// Processa a resposta, pois é esperada
bool Respondido = DecifrarResposta(resposta, Comando.Parametro, Comando.Endereco);
Comando.Respondido = Respondido;
if (!Respondido)
{
RegisrarErrosComunicacao(Comando.Endereco, $"Timeout ou erro ao receber resposta. GET={Comando.Get}, Parametro={Comando.Parametro.Chave}, Valor={Comando.Valor}");
//Comando.Momento = DateTime.Now.AddMilliseconds(500); // Retenta após um pequeno intervalo
if (!Comando.Get)
{
ComandosNaoRespondidos++;
if (ComandosNaoRespondidos > ComandosNaoRespondidosMax)
{
ComandosNaoRespondidos = 0;
await InicializarEthernetService();
}
}
}
}
else
{
// Descarte da resposta, pois não era esperada
Console.WriteLine($"Resposta descartada para comando que não aguardava resposta.");
}
//await Task.Delay(100);
}
}
catch (Exception ex)
{
// Log de erro
RegisrarErrosComunicacao(Comando.Endereco, $"Erro ao enviar comando: {ex.Message}");
Comando.Enviado = false;
Comando.Momento = DateTime.Now.AddMilliseconds(500);
}
}
private static void DefinirDispositivo(List<string> Mod_IDs)
{
if (!SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Bld))
{
IniciarRotinas();
}
foreach (var Mod_ID in Mod_IDs)
{
var Modulo = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.FirstOrDefault(x => x.Modulo_ID == Mod_ID);
byte Addr = Modulo.MovMotor._EnderecoCAN_Tx;
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 DispositivoDetalhesModel()
{
Endereco = _ethernetService != null ? _ethernetService._ipAddress : "N/A",
Dispositivo = T_Code.Bld,
Erro = leitura.CodigoAlarme > 0,
Versao = leitura.Versao.ToString(),
Mod_ID = Mod_ID,
});
}
}
}
private static void ConfigurarDriver(byte Addr)
{
var Modulo = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.FirstOrDefault(x => x.MovMotor._EnderecoCAN_Tx == 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 = (int)Variaveis.OperacaoEmAndamento.DispMvd.Dados.RPM_Max_MotorBLDC
});
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;
}
private static bool DecifrarResposta(byte[] resposta, ModbusParametrosModel Parametro, byte Addr)
{
try
{
RegisrarComandosComunicacao(Addr, $"RX: - {BitConverter.ToString(resposta ?? new byte[] { }).Replace("-", " ")}");
if (resposta == null || resposta.Length == 0)
{
return false;
}
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 = (byte)Parametro.Registro,
Escala = Parametro.Escala,
Valor = null
}
}
};
DadosLeitura.Add(Leitura);
}
BLDParametrosRequisitar _parametro = Leitura.parametros.FirstOrDefault(x => x.Registro == Parametro.Registro);
if (_parametro == null)
{
_parametro = new BLDParametrosRequisitar()
{
Registro = (byte)Parametro.Registro,
Escala = Parametro.Escala,
Valor = null
};
Leitura.parametros.Add(_parametro);
}
if (resposta.Length > 3)
{
// GET
if (resposta[1] == 0x03)
{
int num_registros = resposta[2];
byte[] bytes_resposta = resposta.Skip(3).Take(num_registros).ToArray();
Parametro.Valor = bytes_resposta;
_parametro.Valor = bytes_resposta;
Leitura.UltimoComandoRecebido = DateTime.Now;
return true;
}
// SET
else if (resposta[1] == 0x06)
{
Parametro.Valor = resposta.Skip(4).Take(2).ToArray();
_parametro.Valor = resposta.Skip(4).Take(2).ToArray();
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;
}
}
public static void RegisrarErrosComunicacao(byte addr, string mensagem)
{
mensagem = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:fff") + " - Endereço " + addr + " (" + _ethernetService._ipAddress + "): " + mensagem + Environment.NewLine;
ErrosComunicacao.Add(mensagem);
VariaveisOperacao.RegistrarLogDispositivo(ErrosComunicacao, T_Code.Bld, "_err.txt", 50);
}
public static void RegisrarComandosComunicacao(byte addr, string mensagem)
{
mensagem = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:fff") + " - Endereço " + addr + " (" + _ethernetService._ipAddress + "): " + mensagem + Environment.NewLine;
ComandosComunicacao.Add(mensagem);
VariaveisOperacao.RegistrarLogDispositivo(ComandosComunicacao, T_Code.Bld, "_log.txt", 1000);
}
private static async Task tmrLoopComandos_Tick()
{
if (!EnvioLiberado)
{
return;
}
EnvioLiberado = false;
// Verifica se a conexão foi perdida e tenta reconectar
if (_ethernetService != null && !_ethernetService.IsConnected)
{
if (!await _ethernetService.ReconnectAsync())
{
EnvioLiberado = true;
return;
}
}
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;
}
}
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 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();
}
}
}
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 bool ErroEnvio { get; set; } = false;
public bool Respondido { 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;
}
}
}