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;
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.
* O MKS057DCanService agora já segura throttle interno.
* Toda a manutenção da comunicação com os MKS fica concentrada
* 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(
leitura.EnderecoTx,
leitura.EnderecoRx,
MKS057DCanService.MksHandler.MksFuncCode.PulsosRecebidos
);
}
tmrRegistradorDIR.SetInterval(1000);
await Task.CompletedTask;
return;
}
MKS057DCanService.AtualizarDados();
bool deveEnviarControle = _ultimoEnvioControleDir == DateTime.MinValue || agora - _ultimoEnvioControleDir >= IntervaloControleDir;
bool deveRequisitarSensoresExternos = _ultimoSensoresExternosDir == DateTime.MinValue || agora - _ultimoSensoresExternosDir >= IntervaloSensoresExternosDir;
bool enviouControle = false;
@ -459,43 +453,43 @@ namespace AgroBase.Models.Modules
foreach (var modulo in Modulos)
{
if (modulo?.DirMotor == null || !modulo.DirMotor.Inicializado)
var driver = modulo?.DirMotor;
if (driver == null || !driver.Inicializado)
continue;
byte addrTx = modulo.DirMotor._EnderecoCAN_Tx;
byte addrRx = modulo.DirMotor._EnderecoCAN_Rx;
/*
* Agora é igual ao MOV/OID:
* o service escolhe PulsosRecebidos, IO, RPM, ErroAcumulado etc.
* com throttle, cursor e prioridade de referenciamento.
*/
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.
* Sensores externos do direcional.
*
* Mantemos o throttle de 500 ms para não gerar tráfego
* desnecessário no sensoriamento.
*/
if (deveRequisitarSensoresExternos)
{
modulo.DirMotor.RequisitarDadosSensoresExternos();
driver.RequisitarDadosSensoresExternos();
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.
* O MKS057DCanService já tem dedupe curto, mas ainda assim
* é melhor não transformar posição em metralhadora CAN.
* Durante referenciamento não interferimos no comando,
* pois o próprio fluxo de referência assume o controle do MKS.
*/
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;
}
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)
@ -504,6 +498,10 @@ namespace AgroBase.Models.Modules
if (requisitouSensoresExternos)
_ultimoSensoresExternosDir = agora;
/*
* Normalmente 80 ms.
* Durante referenciamento, 40 ms.
*/
tmrRegistradorDIR.SetInterval(MKS057DCanService.TaxaAmostragem);
await Task.CompletedTask;

View File

@ -48,10 +48,13 @@ namespace AgroBase.Services
*/
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 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:
* operação normal = ângulo rápido + IO lento.
@ -693,37 +696,73 @@ namespace AgroBase.Services
{
try
{
var modulos =
Variaveis
.OperacaoEmAndamento
.DispMvd?
.Dados?
.Modulos;
var modulos = Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos;
if (modulos == null)
return;
MarcarDriversInativosPorTimeout();
foreach (var mod in modulos)
{
var driver = mod?.DirMotor;
if (driver == null || !driver.Inicializado)
if (driver == null)
continue;
RequisitarProximoDado(
driver._EnderecoCAN_Tx,
driver._EnderecoCAN_Rx
);
if (driver.Inicializado)
{
RequisitarProximoDado(driver._EnderecoCAN_Tx, driver._EnderecoCAN_Rx);
continue;
}
RemoverDriversMuitoAntigos();
TentarReconectarDriver(driver._EnderecoCAN_Tx, driver._EnderecoCAN_Rx);
}
}
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)
{
DateTime agora = DateTime.Now;
@ -756,7 +795,7 @@ namespace AgroBase.Services
return null;
}
private static void RemoverDriversMuitoAntigos()
private static void MarcarDriversInativosPorTimeout()
{
DateTime agora = DateTime.Now;
@ -774,7 +813,17 @@ namespace AgroBase.Services
}
foreach (var antigo in antigos)
{
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)
@ -1130,9 +1179,7 @@ namespace AgroBase.Services
{
try
{
if (mensagem == null ||
mensagem.DataRx == null ||
mensagem.DataRx.Length < 1)
if (mensagem == null || mensagem.DataRx == null || mensagem.DataRx.Length < 1)
{
return;
}
@ -1142,6 +1189,8 @@ namespace AgroBase.Services
var item = ObterOuCriar(mensagem);
bool estavaOffline = !item.Iniciado && item.UltimoComandoRecebido != DateTime.MinValue;
switch (func)
{
case MksFuncCode.EncoderCarregado:
@ -1188,10 +1237,15 @@ namespace AgroBase.Services
item.UltimoComandoRecebido = DateTime.Now;
item.Iniciado = true;
if (estavaOffline)
{
Variaveis.MostrarLog($"[MKS057DCanService] [CONEXAO] Driver CAN 0x{item.EnderecoTx:X2} ONLINE novamente");
}
}
catch (Exception ex)
{
Console.WriteLine("[MKS] Erro ao interpretar resposta CAN: " + ex.Message);
Variaveis.MostrarLog("[MKS057DCanService] Erro ao interpretar resposta CAN: " + ex.Message);
}
}