agrobot_base/AgroBase/AgroBase/Services/BLD300RService.cs

726 lines
27 KiB
C#

using AgroBase.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static AgroBase.Models.Enums;
using System.IO;
namespace AgroBase.Services
{
public class BLD300RService
{
private static EthernetService _ethernetService;
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 AsyncTaskTimerModel tmrReceberDados;
private static bool EnvioLiberado { get; set; } = true;
public static void IniciarRotinas()
{
PararRotinas();
tmrLoopComandos = new AsyncTaskTimerModel("tmrLoopComandos", tmrLoopComandos_Tick, 100);
tmrLoopComandos.Start();
tmrReceberDados = new AsyncTaskTimerModel("tmrReceberDados", tmrReceberDados_Tick, TaxaAmostragem);
tmrReceberDados.Start();
}
public static void PararRotinas()
{
tmrLoopComandos?.Dispose();
tmrReceberDados?.Dispose();
}
// Inicializa o serviço Ethernet com IP e porta específicos
public static async Task<bool> InicializarEthernetService(string ipAddress, int port)
{
_ethernetService = new EthernetService(ipAddress, port);
return await _ethernetService.ConnectAsync();
}
public static async Task<bool> VerificaPortaBLD()
{
EnvioLiberado = false;
if (_ethernetService == null)
{
bool connect = await InicializarEthernetService(Variaveis.OperacaoEmAndamento.DispMvd.Dados.IP_Bld, VariaveisPortas.Ethernet_TCP);
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.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);
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
{
return await ethernetService.SendDataAsync(comando);
}
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 = !Comando.AguardaResposta;
// 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.Enviado = 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
}
}
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 = 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 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 => 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 = (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
{
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 = 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 = 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;
}
}
private static void RegisrarErrosComunicacao(byte addr, string mensagem)
{
try
{
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") + " - Endereço " + addr + " (" + _ethernetService._ipAddress + "): " + mensagem + Environment.NewLine;
if (!File.Exists(Caminho))
{
File.WriteAllText(Caminho, mensagem);
}
else
{
File.AppendAllText(Caminho, mensagem);
}
}
catch
{
Console.WriteLine("Erro ao salvar log de erro BLD");
}
}
private static async Task tmrReceberDados_Tick()
{
if (!Iniciado && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Bld))
{
foreach (var Leitura in DadosLeitura.Where(x => x.UltimoComandoRecebido.AddSeconds(10) < DateTime.Now))
{
AdicionarComandoNaFila(new BLDComandoModel()
{
Endereco = Leitura.Endereco,
Parametro = Parametros.First(x => x.Chave == "Versao"),
Get = true,
AguardaResposta = true
});
}
TaxaAmostragem = 3000;
tmrReceberDados.SetInterval(TaxaAmostragem);
return;
}
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 = 500
};
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.RPM_Roda = Leitura.RPM * Modulo.MovMotor.OffsetLeitura;
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.ConfigFreio.Leitura = Leitura.Freado;
Modulo.MovMotor.Sentido = Leitura.Direcao;
Modulo.MovMotor.CodigoAlarme = Leitura.CodigoAlarme;
Variaveis.OperacaoEmAndamento.DispMvd.Dados.RegistrarLogMov(Modulo);
// Tenta reiniciar o alarme dos modulos se a operacao nao estiver liberada
if (Modulo.MovMotor.CodigoAlarme > 0 && Modulo.MovMotor.CodigoAlarme <= 99 && !Variaveis.OperacaoEmAndamento.OperacaoLiberada)
{
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 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 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;
}
}
}