revisado fluxo completo do direcional

This commit is contained in:
Diego Freitas 2026-08-31 10:38:26 -03:00
parent e8cdb27687
commit a5586d3ed1
2 changed files with 114 additions and 62 deletions

View File

@ -428,28 +428,22 @@ namespace AgroBase.Models.Modules
DateTime agora = DateTime.Now; DateTime agora = DateTime.Now;
if (!MKS057DCanService.Iniciado && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Dir))
{
foreach (var leitura in MKS057DCanService.DadosLeitura.Where(x => x.UltimoComandoRecebido.AddSeconds(10) < agora))
{
/* /*
* Poke leve para tentar recuperar leitura. * Toda a manutenção da comunicação com os MKS fica concentrada
* O MKS057DCanService agora já segura throttle interno. * no MKS057DCanService:
*
* - polling dos drivers online;
* - throttle por driver/parâmetro;
* - prioridade durante referenciamento;
* - identificação de drivers sem resposta;
* - marcação individual como offline;
* - tentativa periódica de reconexão dos drivers offline.
*
* O timer não precisa mais fazer poke ou polling individual.
*/ */
MKS057DCanService.RequisitarDado( MKS057DCanService.AtualizarDados();
leitura.EnderecoTx,
leitura.EnderecoRx,
MKS057DCanService.MksHandler.MksFuncCode.PulsosRecebidos
);
}
tmrRegistradorDIR.SetInterval(1000);
await Task.CompletedTask;
return;
}
bool deveEnviarControle = _ultimoEnvioControleDir == DateTime.MinValue || agora - _ultimoEnvioControleDir >= IntervaloControleDir; bool deveEnviarControle = _ultimoEnvioControleDir == DateTime.MinValue || agora - _ultimoEnvioControleDir >= IntervaloControleDir;
bool deveRequisitarSensoresExternos = _ultimoSensoresExternosDir == DateTime.MinValue || agora - _ultimoSensoresExternosDir >= IntervaloSensoresExternosDir; bool deveRequisitarSensoresExternos = _ultimoSensoresExternosDir == DateTime.MinValue || agora - _ultimoSensoresExternosDir >= IntervaloSensoresExternosDir;
bool enviouControle = false; bool enviouControle = false;
@ -459,43 +453,43 @@ namespace AgroBase.Models.Modules
foreach (var modulo in Modulos) foreach (var modulo in Modulos)
{ {
if (modulo?.DirMotor == null || !modulo.DirMotor.Inicializado) var driver = modulo?.DirMotor;
if (driver == null || !driver.Inicializado)
continue; continue;
byte addrTx = modulo.DirMotor._EnderecoCAN_Tx;
byte addrRx = modulo.DirMotor._EnderecoCAN_Rx;
/* /*
* Agora é igual ao MOV/OID: * Sensores externos do direcional.
* o service escolhe PulsosRecebidos, IO, RPM, ErroAcumulado etc. *
* com throttle, cursor e prioridade de referenciamento. * Mantemos o throttle de 500 ms para não gerar tráfego
*/ * desnecessário no sensoriamento.
MKS057DCanService.RequisitarProximoDado(addrTx, addrRx);
/*
* Se isso também gera CAN, melhor não chamar em todo tick.
* Se for leitura local/barata, pode voltar para todo tick.
*/ */
if (deveRequisitarSensoresExternos) if (deveRequisitarSensoresExternos)
{ {
modulo.DirMotor.RequisitarDadosSensoresExternos(); driver.RequisitarDadosSensoresExternos();
requisitouSensoresExternos = true; requisitouSensoresExternos = true;
} }
/* /*
* Reenvio de controle direcional. * Reenvio periódico do comando enquanto o ângulo solicitado
* ainda não foi atingido.
* *
* Não precisa mais reenviar no ritmo da TaxaAmostragem. * Durante referenciamento não interferimos no comando,
* O MKS057DCanService já tem dedupe curto, mas ainda assim * pois o próprio fluxo de referência assume o controle do MKS.
* é melhor não transformar posição em metralhadora CAN.
*/ */
if (!MKS057DCanService.Referenciando && deveEnviarControle && controleDir != null && !modulo.DirMotor.AtingiuAnguloSP) if (!MKS057DCanService.Referenciando && deveEnviarControle && controleDir != null && !driver.AtingiuAnguloSP)
{ {
modulo.DirMotor.EnviarComandoControle(controleDir.UltimaDirecao); driver.EnviarComandoControle(controleDir.UltimaDirecao);
enviouControle = true; enviouControle = true;
} }
modulo.DirMotor.AtualizarSaudeSeguimentoAngular(agora); /*
* Atualização do acompanhamento angular.
*
* Saúde global será validada em outro tópico, mas mantemos
* aqui o comportamento já existente.
*/
driver.AtualizarSaudeSeguimentoAngular(agora);
} }
if (enviouControle) if (enviouControle)
@ -504,6 +498,10 @@ namespace AgroBase.Models.Modules
if (requisitouSensoresExternos) if (requisitouSensoresExternos)
_ultimoSensoresExternosDir = agora; _ultimoSensoresExternosDir = agora;
/*
* Normalmente 80 ms.
* Durante referenciamento, 40 ms.
*/
tmrRegistradorDIR.SetInterval(MKS057DCanService.TaxaAmostragem); tmrRegistradorDIR.SetInterval(MKS057DCanService.TaxaAmostragem);
await Task.CompletedTask; await Task.CompletedTask;

View File

@ -48,10 +48,13 @@ namespace AgroBase.Services
*/ */
private static readonly TimeSpan TempoRespostaRecente = TimeSpan.FromSeconds(3); private static readonly TimeSpan TempoRespostaRecente = TimeSpan.FromSeconds(3);
private static readonly TimeSpan TempoRemoverDriver = TimeSpan.FromSeconds(15); private static readonly TimeSpan TempoRemoverDriver = TimeSpan.FromSeconds(5);
private static readonly TimeSpan TempoRespostaDescoberta = TimeSpan.FromMilliseconds(700); private static readonly TimeSpan TempoRespostaDescoberta = TimeSpan.FromMilliseconds(700);
private static readonly TimeSpan JanelaDedupeComandoControle = TimeSpan.FromMilliseconds(20); private static readonly TimeSpan JanelaDedupeComandoControle = TimeSpan.FromMilliseconds(20);
private static readonly Dictionary<byte, DateTime> _ultimaTentativaReconexaoPorDriver = new Dictionary<byte, DateTime>();
private static readonly TimeSpan IntervaloTentativaReconexao = TimeSpan.FromSeconds(1);
/* /*
* Pooling de produção: * Pooling de produção:
* operação normal = ângulo rápido + IO lento. * operação normal = ângulo rápido + IO lento.
@ -693,37 +696,73 @@ namespace AgroBase.Services
{ {
try try
{ {
var modulos = var modulos = Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos;
Variaveis
.OperacaoEmAndamento
.DispMvd?
.Dados?
.Modulos;
if (modulos == null) if (modulos == null)
return; return;
MarcarDriversInativosPorTimeout();
foreach (var mod in modulos) foreach (var mod in modulos)
{ {
var driver = mod?.DirMotor; var driver = mod?.DirMotor;
if (driver == null || !driver.Inicializado) if (driver == null)
continue; continue;
RequisitarProximoDado( if (driver.Inicializado)
driver._EnderecoCAN_Tx, {
driver._EnderecoCAN_Rx RequisitarProximoDado(driver._EnderecoCAN_Tx, driver._EnderecoCAN_Rx);
); continue;
} }
RemoverDriversMuitoAntigos(); TentarReconectarDriver(driver._EnderecoCAN_Tx, driver._EnderecoCAN_Rx);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[MKS] Erro em AtualizarDados: " + ex.Message); Variaveis.MostrarLog("[MKS057DCanService] Erro em AtualizarDados: " + ex.Message);
} }
} }
private static void TentarReconectarDriver(byte idTx, byte idRx)
{
/*
* Só tentamos recuperar um driver que já existiu anteriormente.
*
* Descoberta inicial continua sendo responsabilidade de
* VerificaDispositivoConectado().
*/
bool driverConhecido;
lock (DadosLeitura)
{
driverConhecido = DadosLeitura.Any(x => x.EnderecoTx == idTx);
}
if (!driverConhecido)
return;
DateTime agora = DateTime.Now;
lock (_sync)
{
if (_ultimaTentativaReconexaoPorDriver.TryGetValue(idTx, out DateTime ultima) && agora - ultima < IntervaloTentativaReconexao)
{
return;
}
_ultimaTentativaReconexaoPorDriver[idTx] = agora;
}
/*
* Versão é uma consulta pequena e já é utilizada
* pela descoberta.
*
* Não contamina timeout do health.
*/
EnviarComandoDescoberta(idTx, idRx);
}
private static MksFuncCode? EscolherProximoParametro(byte idTx) private static MksFuncCode? EscolherProximoParametro(byte idTx)
{ {
DateTime agora = DateTime.Now; DateTime agora = DateTime.Now;
@ -756,7 +795,7 @@ namespace AgroBase.Services
return null; return null;
} }
private static void RemoverDriversMuitoAntigos() private static void MarcarDriversInativosPorTimeout()
{ {
DateTime agora = DateTime.Now; DateTime agora = DateTime.Now;
@ -774,7 +813,17 @@ namespace AgroBase.Services
} }
foreach (var antigo in antigos) foreach (var antigo in antigos)
{
antigo.Iniciado = false; antigo.Iniciado = false;
double tempoSemResposta = (agora - antigo.UltimoComandoRecebido).TotalSeconds;
Variaveis.MostrarLog(
$"[MKS057DCanService] [CONEXAO] " +
$"Driver CAN 0x{antigo.EnderecoTx:X2} OFFLINE | " +
$"sem resposta há {tempoSemResposta:F1}s"
);
}
} }
public static void SetZero(byte idTx, byte idRx) public static void SetZero(byte idTx, byte idRx)
@ -1130,9 +1179,7 @@ namespace AgroBase.Services
{ {
try try
{ {
if (mensagem == null || if (mensagem == null || mensagem.DataRx == null || mensagem.DataRx.Length < 1)
mensagem.DataRx == null ||
mensagem.DataRx.Length < 1)
{ {
return; return;
} }
@ -1142,6 +1189,8 @@ namespace AgroBase.Services
var item = ObterOuCriar(mensagem); var item = ObterOuCriar(mensagem);
bool estavaOffline = !item.Iniciado && item.UltimoComandoRecebido != DateTime.MinValue;
switch (func) switch (func)
{ {
case MksFuncCode.EncoderCarregado: case MksFuncCode.EncoderCarregado:
@ -1188,10 +1237,15 @@ namespace AgroBase.Services
item.UltimoComandoRecebido = DateTime.Now; item.UltimoComandoRecebido = DateTime.Now;
item.Iniciado = true; item.Iniciado = true;
if (estavaOffline)
{
Variaveis.MostrarLog($"[MKS057DCanService] [CONEXAO] Driver CAN 0x{item.EnderecoTx:X2} ONLINE novamente");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("[MKS] Erro ao interpretar resposta CAN: " + ex.Message); Variaveis.MostrarLog("[MKS057DCanService] Erro ao interpretar resposta CAN: " + ex.Message);
} }
} }