melhoria na saude do atuador, bicos com ou sem movimento

This commit is contained in:
Diego Freitas 2026-07-01 13:26:08 -03:00
parent 64fce863d5
commit c9f3bd69d7
9 changed files with 230 additions and 128 deletions

View File

@ -135,7 +135,7 @@ namespace AgroBase.Forms
* Hoje pode ficar comentado se UDP não for usado no rover, * Hoje pode ficar comentado se UDP não for usado no rover,
* mas o método fica aqui no lugar certo do lifecycle. * mas o método fica aqui no lugar certo do lifecycle.
*/ */
Variaveis.IniciarUDP(); //Variaveis.IniciarUDP();
APIService.IniciarRotinas(); APIService.IniciarRotinas();
VersionamentoService.IniciarRotinas(); VersionamentoService.IniciarRotinas();

View File

@ -764,7 +764,7 @@ namespace AgroBase.Models.Modules
posicao = CanMessagePosicaoDados.Dados1, posicao = CanMessagePosicaoDados.Dados1,
indice = 2, indice = 2,
analise_health = true, analise_health = true,
maximo = 100, maximo = 120,
minimo = 0, minimo = 0,
nominal = 0, nominal = 0,
unidade_medida = "mL/s" unidade_medida = "mL/s"
@ -1181,6 +1181,7 @@ namespace AgroBase.Models.Modules
Componente = S_Code.sBIC, Componente = S_Code.sBIC,
Posicao = posicao, Posicao = posicao,
Inicializado = false, Inicializado = false,
ComMovimento = false,
AnguloNeutro = 90, AnguloNeutro = 90,
AnguloControle = 90, AnguloControle = 90,
AnguloAbertura = 30, AnguloAbertura = 30,
@ -2074,6 +2075,7 @@ namespace AgroBase.Models.Modules
public int Posicao { get; set; } public int Posicao { get; set; }
public S_Code Componente { get; set; } = S_Code.sBIC; public S_Code Componente { get; set; } = S_Code.sBIC;
public bool AtuacaoNivelAlto { get; set; } public bool AtuacaoNivelAlto { get; set; }
public bool ComMovimento { get; set; }
public double AnguloNeutro { get; set; } public double AnguloNeutro { get; set; }
public double AnguloControle { get; set; } public double AnguloControle { get; set; }
public double AnguloAbertura { get; set; } public double AnguloAbertura { get; set; }
@ -2250,8 +2252,12 @@ namespace AgroBase.Models.Modules
AtuacaoNivelAlto = AtuacaoNivelAlto, AtuacaoNivelAlto = AtuacaoNivelAlto,
Atuacoes = Atuacoes, Atuacoes = Atuacoes,
ComandoAtuar = ComandoAtuar, ComandoAtuar = ComandoAtuar,
ComMovimento = ComMovimento,
AnguloAbertura = AnguloAbertura, AnguloAbertura = AnguloAbertura,
AnguloControle = AnguloControle, AnguloControle = AnguloControle,
UltimoAnguloDesejado = UltimoAnguloDesejado,
UltimoComandoEnviado = UltimoComandoEnviado,
UltimoStatusDesejado = UltimoStatusDesejado,
Inicializado = Inicializado, Inicializado = Inicializado,
Mandatorio = Mandatorio, Mandatorio = Mandatorio,
Componente = Componente, Componente = Componente,

View File

@ -602,9 +602,9 @@ namespace AgroBase.Models.Modules
posicao = CanMessagePosicaoDados.Dados1, posicao = CanMessagePosicaoDados.Dados1,
indice = 1, indice = 1,
analise_health = true, analise_health = true,
maximo = 2200, maximo = 2500,
minimo = 1000, minimo = 400,
nominal = 1700, nominal = 700,
unidade_medida = "mA" unidade_medida = "mA"
}, },
new SensorValoresLeituraModel() new SensorValoresLeituraModel()

View File

@ -14,7 +14,6 @@ using System.Drawing;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -30,15 +29,16 @@ namespace AgroBase.Models
{ {
get get
{ {
return tmrLogs.Interval; return _tmrLogsOperacao.Interval;
} }
set set
{ {
tmrLogs.SetInterval((int)value); _tmrLogsOperacao.SetInterval((int)value);
} }
} }
private static AsyncTaskTimerModel tmrLogs; private AsyncTaskTimerModel _tmrLogsOperacao;
private int idxLog = 0; private int idxLog = 0;
private readonly SemaphoreSlim _salvarLogsGate = new SemaphoreSlim(1, 1);
public double tmrLeiturasInterval public double tmrLeiturasInterval
{ {
get get
@ -55,8 +55,13 @@ namespace AgroBase.Models
public OperacaoModel() public OperacaoModel()
{ {
tmrLogs?.Dispose(); Variaveis.MostrarLog("[OperacaoModel.ctor] StackTrace: " + Environment.StackTrace);
tmrLogs = new AsyncTaskTimerModel("tmrLogs", tmrLogs_Tick, 1000);
Variaveis.MostrarLog(
$"[OperacaoModel.ctor] Nova instância. Hash={GetHashCode()}, " +
$"OperacaoAtual={Variaveis.OperacaoEmAndamento?.ID}, " +
$"HashAtual={Variaveis.OperacaoEmAndamento?.GetHashCode()}"
);
tmrLeituras?.Dispose(); tmrLeituras?.Dispose();
tmrLeituras = new AsyncTaskTimerModel("tmrLeitura", tmrLeituras_Tick, 500); tmrLeituras = new AsyncTaskTimerModel("tmrLeitura", tmrLeituras_Tick, 500);
@ -200,8 +205,6 @@ namespace AgroBase.Models
if (CarregarModulos) if (CarregarModulos)
{ {
tmrLogs?.Stop();
var SenConectado = (DispositivosService<SensoriamentoModel>)Variaveis.DispositivosConectados.FirstOrDefault(x => x.Dispositivo == T_Code.Sen); var SenConectado = (DispositivosService<SensoriamentoModel>)Variaveis.DispositivosConectados.FirstOrDefault(x => x.Dispositivo == T_Code.Sen);
var AtuConectado = (DispositivosService<AtuadorModel>)Variaveis.DispositivosConectados.FirstOrDefault(x => x.Dispositivo == T_Code.Atu); var AtuConectado = (DispositivosService<AtuadorModel>)Variaveis.DispositivosConectados.FirstOrDefault(x => x.Dispositivo == T_Code.Atu);
var MvdConectado = (DispositivosService<MovimentacaoUnificadaModel>)Variaveis.DispositivosConectados.FirstOrDefault(x => x.Dispositivo == T_Code.Mvd); var MvdConectado = (DispositivosService<MovimentacaoUnificadaModel>)Variaveis.DispositivosConectados.FirstOrDefault(x => x.Dispositivo == T_Code.Mvd);
@ -1059,7 +1062,7 @@ namespace AgroBase.Models
op.Sensoriamento.UltimoRegistroLog = agora; op.Sensoriamento.UltimoRegistroLog = agora;
op.idxLog = 0; op.idxLog = 0;
tmrLogs?.Start(); op.IniciarTimerLogsOperacao();
await op.RealizarCalibragemInicialAsync(true); await op.RealizarCalibragemInicialAsync(true);
@ -1087,9 +1090,9 @@ namespace AgroBase.Models
op.Sensoriamento.Operacao.DataFim = DateTime.Now; op.Sensoriamento.Operacao.DataFim = DateTime.Now;
tmrLogs?.Stop(); op.PararTimerLogsOperacao();
op.SalvarLogs(false); op.SalvarLogs(true);
op.Sensoriamento.Operacao.Finalizando = false; op.Sensoriamento.Operacao.Finalizando = false;
op.Trajetoria?.AutonomiaCorredor?.Parar(); op.Trajetoria?.AutonomiaCorredor?.Parar();
@ -1194,6 +1197,33 @@ namespace AgroBase.Models
} }
private void IniciarTimerLogsOperacao()
{
_tmrLogsOperacao?.Stop();
_tmrLogsOperacao?.Dispose();
_tmrLogsOperacao = new AsyncTaskTimerModel($"tmrLogs_{ID}", tmrLogs_Tick, 1000);
_tmrLogsOperacao.Start();
Variaveis.MostrarLog($"[OperacaoModel.IniciarTimerLogsOperacao] Timer de logs iniciado. Operacao={ID}, Hash={GetHashCode()}");
}
private void PararTimerLogsOperacao()
{
try
{
_tmrLogsOperacao?.Stop();
Variaveis.MostrarLog($"[OperacaoModel.PararTimerLogsOperacao] Timer de logs parado. Operacao={ID}, Hash={GetHashCode()}, idxLog={idxLog}");
}
catch (Exception ex)
{
Variaveis.MostrarLog("[OperacaoModel.PararTimerLogsOperacao] Erro ao parar timer de logs: " + ex.Message);
}
}
public static string DeserializarDadosOperacao(string caminho, string arquivo) public static string DeserializarDadosOperacao(string caminho, string arquivo)
{ {
string pastaBase = Path.GetDirectoryName(caminho) ?? ""; string pastaBase = Path.GetDirectoryName(caminho) ?? "";
@ -2628,7 +2658,24 @@ namespace AgroBase.Models
{ {
var op = this; var op = this;
if (op.Sensoriamento.Operacao.OperacaoIniciada) if (!ReferenceEquals(Variaveis.OperacaoEmAndamento, op))
{
Variaveis.MostrarLog($"[OperacaoModel.tmrLogs_Tick] Timer órfão parado. TimerHash={GetHashCode()}, OperacaoTimer={ID}, OperacaoAtual={Variaveis.OperacaoEmAndamento?.ID}");
PararTimerLogsOperacao();
return;
}
if (op.Sensoriamento?.Operacao?.OperacaoIniciada != true)
return;
if (!await _salvarLogsGate.WaitAsync(0))
{
Variaveis.MostrarLog($"[OperacaoModel.tmrLogs_Tick] Tick ignorado porque SalvarLogs ainda está em execução. Operacao={ID}, idxLog={idxLog}");
return;
}
try
{ {
op.SalvarLogs(false); op.SalvarLogs(false);
@ -2639,9 +2686,13 @@ namespace AgroBase.Models
if (op.idxLog % 5 == 0 && !op.Sensoriamento.Operacao.Calibrando) if (op.idxLog % 5 == 0 && !op.Sensoriamento.Operacao.Calibrando)
{ {
Task.Run(async () => await op.RealizarCalibragemInicialAsync(false)); _ = Task.Run(async () => await op.RealizarCalibragemInicialAsync(false));
} }
} }
finally
{
_salvarLogsGate.Release();
}
} }
public void SalvarLogs(bool Fim) public void SalvarLogs(bool Fim)
@ -3594,6 +3645,7 @@ namespace AgroBase.Models
Atuacoes = bico.Atuacoes, Atuacoes = bico.Atuacoes,
TempoAtuado = bico.TempoAtuado, TempoAtuado = bico.TempoAtuado,
AnguloAbertura = bico.AnguloAbertura, AnguloAbertura = bico.AnguloAbertura,
ComMovimento = bico.ComMovimento,
AnguloControle = bico.AnguloControle AnguloControle = bico.AnguloControle
}).ToList() }).ToList()
}; };
@ -4649,7 +4701,7 @@ namespace AgroBase.Models
DadosPerformance = dadosPerformance, DadosPerformance = dadosPerformance,
LivoxLidar = dadosLivox, LivoxLidar = dadosLivox,
CoolerControl = dadosCooler, CoolerControl = dadosCooler,
Logs = Logs != null ? Logs.Where(x => x?.Momento >= ultimoRegistroLog).ToList() : new List<OperacaoSensoriamentoLogErrosModel>(), Logs = Logs != null ? Logs.Where(x => x != null && x.Momento > ultimoRegistroLog).ToList() : new List<OperacaoSensoriamentoLogErrosModel>(),
UltimoRegistroLog = ultimoRegistroLog, UltimoRegistroLog = ultimoRegistroLog,
}; };
@ -5108,6 +5160,7 @@ namespace AgroBase.Models
public int Atuacoes { get; set; } public int Atuacoes { get; set; }
public double TempoAtuado { get; set; } public double TempoAtuado { get; set; }
public double AnguloAbertura { get; set; } public double AnguloAbertura { get; set; }
public bool ComMovimento { get; set; }
public double AnguloControle { get; set; } public double AnguloControle { get; set; }
public AtuadorBicoLogModel Clone() public AtuadorBicoLogModel Clone()
@ -5124,6 +5177,7 @@ namespace AgroBase.Models
Atuacoes = Atuacoes, Atuacoes = Atuacoes,
TempoAtuado = TempoAtuado, TempoAtuado = TempoAtuado,
AnguloAbertura = AnguloAbertura, AnguloAbertura = AnguloAbertura,
ComMovimento = ComMovimento,
AnguloControle = AnguloControle AnguloControle = AnguloControle
}; };
} }

View File

@ -610,8 +610,7 @@ namespace AgroBase.Services
return; return;
} }
var agitador = var agitador = op?.DispAtu?.Dados?.AgitadorDeCalda;
op?.DispAtu?.Dados?.AgitadorDeCalda;
if (!(agitador?.Inicializado ?? false)) if (!(agitador?.Inicializado ?? false))
return; return;
@ -634,32 +633,20 @@ namespace AgroBase.Services
!operacaoConcluida && !operacaoConcluida &&
PulverizadorLiberado; PulverizadorLiberado;
bool bicosAtuando = bool bicosAtuando = op.DispAtu?.Dados?.BicosPulverizadores?.Any(x => x.Comandar && x.ComandoAtuar) ?? false;
op.DispAtu?.Dados?.BicosPulverizadores?
.Any(x =>
x.Comandar &&
x.ComandoAtuar)
?? false;
double potenciaFinal = 0; double potenciaFinal = 0;
DateTime agora = DateTime.Now; DateTime agora = DateTime.Now;
if ( if (operacaoAtiva && !_agitadorOperacaoAtivaAnterior)
operacaoAtiva &&
!_agitadorOperacaoAtivaAnterior
)
{ {
_agitadorBoostAte = _agitadorBoostAte = agora.AddMilliseconds(AtuAgitadorBoostRetomadaMs);
agora.AddMilliseconds(
AtuAgitadorBoostRetomadaMs
);
_agitadorUltimaTroca = agora; _agitadorUltimaTroca = agora;
_agitadorIntermitenteLigado = true; _agitadorIntermitenteLigado = true;
} }
bool emBoost = bool emBoost = agora < _agitadorBoostAte;
agora < _agitadorBoostAte;
switch (modo) switch (modo)
{ {
@ -672,18 +659,15 @@ namespace AgroBase.Services
{ {
if (emBoost) if (emBoost)
{ {
potenciaFinal = potenciaFinal = AtuAgitadorPotenciaBoost;
AtuAgitadorPotenciaBoost;
} }
else if (bicosAtuando) else if (bicosAtuando)
{ {
potenciaFinal = potenciaFinal = AtuAgitadorPotenciaContinua;
AtuAgitadorPotenciaContinua;
} }
else else
{ {
potenciaFinal = potenciaFinal = AtuAgitadorPotenciaManutencao;
AtuAgitadorPotenciaManutencao;
} }
} }
break; break;
@ -697,44 +681,27 @@ namespace AgroBase.Services
} }
else if (emBoost) else if (emBoost)
{ {
potenciaFinal = potenciaFinal = AtuAgitadorPotenciaBoost;
AtuAgitadorPotenciaBoost;
} }
else else
{ {
int tempoAtualMs = int tempoAtualMs = _agitadorIntermitenteLigado ? AtuAgitadorTempoLigadoMs : AtuAgitadorTempoDesligadoMs;
_agitadorIntermitenteLigado
? AtuAgitadorTempoLigadoMs
: AtuAgitadorTempoDesligadoMs;
if ( if ((agora - _agitadorUltimaTroca).TotalMilliseconds >= tempoAtualMs)
(agora - _agitadorUltimaTroca)
.TotalMilliseconds >=
tempoAtualMs
)
{ {
_agitadorIntermitenteLigado = _agitadorIntermitenteLigado = !_agitadorIntermitenteLigado;
!_agitadorIntermitenteLigado;
_agitadorUltimaTroca = agora; _agitadorUltimaTroca = agora;
} }
potenciaFinal = potenciaFinal = _agitadorIntermitenteLigado ? AtuAgitadorPotenciaIntermitente : 0;
_agitadorIntermitenteLigado
? AtuAgitadorPotenciaIntermitente
: 0;
} }
break; break;
} }
potenciaFinal = potenciaFinal = Math.Min(potenciaFinal, PotenciaLimitePorNivel(PercentualAtual));
Math.Min(
potenciaFinal,
PotenciaLimitePorNivel(PercentualAtual)
);
potenciaFinal = potenciaFinal = Clamp(potenciaFinal, 0, 100);
Clamp(potenciaFinal, 0, 100);
/* /*
* Este método é a fonte de verdade do agitador. * Este método é a fonte de verdade do agitador.
@ -742,12 +709,9 @@ namespace AgroBase.Services
agitador.PotenciaSP = potenciaFinal; agitador.PotenciaSP = potenciaFinal;
agitador.PressaoSP = -1; agitador.PressaoSP = -1;
agitador.ComandoAtuar = agitador.ComandoAtuar = agitador.Comandar && potenciaFinal > 0;
agitador.Comandar &&
potenciaFinal > 0;
_agitadorOperacaoAtivaAnterior = _agitadorOperacaoAtivaAnterior = operacaoAtiva;
operacaoAtiva;
} }
} }
} }

View File

@ -969,6 +969,7 @@ namespace AgroBase.Services.Operadores
estado_leitura = bico._EstadoLeitura, estado_leitura = bico._EstadoLeitura,
angulo_leitura = bico._AnguloBicoLeitura, angulo_leitura = bico._AnguloBicoLeitura,
com_movimento = bico.ComMovimento,
angulo_neutro = bico.AnguloNeutro, angulo_neutro = bico.AnguloNeutro,
angulo_controle = bico.AnguloControle, angulo_controle = bico.AnguloControle,
angulo_abertura = bico.AnguloAbertura, angulo_abertura = bico.AnguloAbertura,

View File

@ -57,54 +57,121 @@ public sealed class UdpReliableChannel : IDisposable
} }
} }
public void Start(int localPort) public bool Start(int localPort)
{ {
if (_udp != null) return; lock (_sockLock)
{
if (_udp != null || _running || _stopping)
return true;
_seqTx = 0; // opcional try
_lastSeqRx = 0; // importante {
_running = true; _seqTx = 0;
_lastSeqRx = 0;
_running = true;
_stopping = false;
_udp = new UdpClient(localPort); _udp = new UdpClient(localPort);
_udp.Client.ReceiveBufferSize = 1 << 20; _udp.Client.ReceiveBufferSize = 1 << 20;
_udp.Client.SendBufferSize = 1 << 20; _udp.Client.SendBufferSize = 1 << 20;
_rxThread = new Thread(RxLoop) { IsBackground = true, Name = "UdpReliableChannel.Rx" }; _rxThread = new Thread(RxLoop)
_rxThread.Start(); {
IsBackground = true,
Name = "UdpReliableChannel.Rx"
};
if (EnableHeartbeat) _rxThread.Start();
StartHeartbeat();
if (EnableHeartbeat)
StartHeartbeat();
return true;
}
catch (SocketException ex)
{
_running = false;
_stopping = false;
try { _udp?.Close(); } catch { }
try { _udp?.Dispose(); } catch { }
_udp = null;
_rxThread = null;
// 10048 = address already in use
if (ex.SocketErrorCode == SocketError.AddressAlreadyInUse)
{
// Não relança. Quem chamou decide tentar depois.
return false;
}
return false;
}
catch
{
_running = false;
_stopping = false;
try { _udp?.Close(); } catch { }
try { _udp?.Dispose(); } catch { }
_udp = null;
_rxThread = null;
return false;
}
}
} }
public void Stop() public void Stop()
{ {
UdpClient udpParaFechar = null;
Thread threadParaAguardar = null;
lock (_sockLock) lock (_sockLock)
{ {
if (_stopping)
return;
_stopping = true; _stopping = true;
_running = false; _running = false;
udpParaFechar = _udp;
threadParaAguardar = _rxThread;
} }
// pare heartbeat antes de matar socket
StopHeartbeat(); StopHeartbeat();
try { _udp?.Close(); } catch { } try { udpParaFechar?.Close(); } catch { }
try { _udp?.Dispose(); } catch { } try { udpParaFechar?.Dispose(); } catch { }
try try
{ {
if (_rxThread != null && _rxThread.IsAlive) if (
_rxThread.Join(2000); // ou sem timeout se preferir travar até fechar threadParaAguardar != null &&
threadParaAguardar.IsAlive &&
Thread.CurrentThread.ManagedThreadId != threadParaAguardar.ManagedThreadId
)
{
threadParaAguardar.Join(1000);
}
} }
catch { } catch { }
lock (_sockLock) lock (_sockLock)
{ {
_udp = null; if (ReferenceEquals(_udp, udpParaFechar))
_rxThread = null; _udp = null;
if (ReferenceEquals(_rxThread, threadParaAguardar))
_rxThread = null;
_lastSeqRx = 0; _lastSeqRx = 0;
foreach (var kv in _pendingAck) foreach (var kv in _pendingAck)
kv.Value.TrySetResult(false); kv.Value.TrySetResult(false);
_pendingAck.Clear(); _pendingAck.Clear();
_stopping = false; _stopping = false;

View File

@ -554,27 +554,38 @@ class ModuloAtuador(ModuloDiagnosticoBase):
def _avaliar_comando_bico(self, item, ctx, motivos, condicoes): def _avaliar_comando_bico(self, item, ctx, motivos, condicoes):
dados = item["dados"] dados = item["dados"]
label = dados.get("label") label = dados.get("label")
comando_presente = ( comando_presente = (
"comando_atuar" in dados or "comando_atuar" in dados or
"estado_desejado" in dados or "estado_desejado" in dados or
"status_desejado_fisico" in dados or "status_desejado_fisico" in dados or
"ultimo_status_desejado" in dados "ultimo_status_desejado" in dados
) )
comando = self._estado_desejado_ligado(dados) comando = self._estado_desejado_ligado(dados)
estado = self._get(dados, "estado_leitura", self._campo_atual(dados, "EstadoLeitura", 0)) estado = self._get(dados, "estado_leitura", self._campo_atual(dados, "EstadoLeitura", 0))
estado_on = self._estado_ligado(estado) estado_on = self._estado_ligado(estado)
angulo = self._float( usa_servo = dados.get("com_movimento", False)
self._get(dados, "angulo_leitura", self._campo_atual(dados, "ServoAnguloLeitura", 0)),
0,
)
angulo_neutro = self._float(dados.get("angulo_neutro", 90), 90) angulo = None
angulo_controle = self._float(dados.get("angulo_controle", dados.get("angulo_sp", 90)), 90) angulo_desejado = None
angulo_desejado = self._float( angulo_neutro = None
dados.get("angulo_desejado", angulo_controle if comando else angulo_neutro), angulo_controle = None
angulo_controle if comando else angulo_neutro,
) if usa_servo:
angulo = self._float(
self._get(dados, "angulo_leitura", self._campo_atual(dados, "ServoAnguloLeitura", 0)),
0,
)
angulo_neutro = self._float(dados.get("angulo_neutro", 90), 90)
angulo_controle = self._float(dados.get("angulo_controle", dados.get("angulo_sp", 90)), 90)
angulo_desejado = self._float(
dados.get("angulo_desejado", angulo_controle if comando else angulo_neutro),
angulo_controle if comando else angulo_neutro,
)
ultimo_comando_ms = self._float(dados.get("ultimo_comando_ms", 999999), 999999) ultimo_comando_ms = self._float(dados.get("ultimo_comando_ms", 999999), 999999)
tem_idade_comando = ultimo_comando_ms < 900000 tem_idade_comando = ultimo_comando_ms < 900000
@ -585,13 +596,16 @@ class ModuloAtuador(ModuloDiagnosticoBase):
return 92, { return 92, {
"comando_disponivel": False, "comando_disponivel": False,
"estado_on": estado_on, "estado_on": estado_on,
"usa_servo": usa_servo,
"angulo": angulo, "angulo": angulo,
"angulo_desejado": angulo_desejado,
} }
if tem_idade_comando and ultimo_comando_ms < self.COMANDO_GRACE_MS: if tem_idade_comando and ultimo_comando_ms < self.COMANDO_GRACE_MS:
return 96, { return 96, {
"comando_atuar": comando, "comando_atuar": comando,
"estado_on": estado_on, "estado_on": estado_on,
"usa_servo": usa_servo,
"angulo": angulo, "angulo": angulo,
"angulo_desejado": angulo_desejado, "angulo_desejado": angulo_desejado,
"ultimo_comando_ms": ultimo_comando_ms, "ultimo_comando_ms": ultimo_comando_ms,
@ -634,32 +648,36 @@ class ModuloAtuador(ModuloDiagnosticoBase):
score = min(score, 25 if severidade > 90 else 65) score = min(score, 25 if severidade > 90 else 65)
erro_ang = abs(angulo - angulo_desejado) if usa_servo and angulo is not None and angulo_desejado is not None:
erro_ang = abs(angulo - angulo_desejado)
if erro_ang > self.ANGULO_TOL_BICO_GRAUS and (comando or estado_on): if erro_ang > self.ANGULO_TOL_BICO_GRAUS and (comando or estado_on):
motivos.append(f"Ângulo do bico distante do desejado ({angulo:.1f}°/{angulo_desejado:.1f}°)") motivos.append(f"Ângulo do bico distante do desejado ({angulo:.1f}°/{angulo_desejado:.1f}°)")
condicoes.append({ condicoes.append({
"label": "Ângulo bico", "label": "Ângulo bico",
"valor": angulo, "valor": angulo,
"severidade": min(75, max(30, int(erro_ang * 3))), "severidade": min(75, max(30, int(erro_ang * 3))),
"classe": "processo", "classe": "processo",
"descricao": f"Ângulo do bico {label} distante do desejado", "descricao": f"Ângulo do bico {label} distante do desejado",
"acoes": [ "acoes": [
"Verificar servo/posição mecânica do bico.", "Verificar servo/posição mecânica do bico.",
"Conferir neutro e ângulo de abertura configurados.", "Conferir neutro e ângulo de abertura configurados.",
], ],
}) })
score = min(score, 78) score = min(score, 78)
return int(max(0, min(100, round(score)))), { return int(max(0, min(100, round(score)))), {
"comando_disponivel": True, "comando_disponivel": True,
"comando_atuar": comando, "comando_atuar": comando,
"estado_on": estado_on, "estado_on": estado_on,
"usa_servo": usa_servo,
"angulo": angulo, "angulo": angulo,
"angulo_desejado": angulo_desejado, "angulo_desejado": angulo_desejado,
"angulo_neutro": angulo_neutro,
"angulo_controle": angulo_controle,
"ultimo_comando_ms": ultimo_comando_ms, "ultimo_comando_ms": ultimo_comando_ms,
} }
# ============================================================ # ============================================================
# Bombas # Bombas
# ============================================================ # ============================================================

View File

@ -350,23 +350,15 @@ namespace OperationControl.Models
HeartbeatPayload = new byte[] { 0 } HeartbeatPayload = new byte[] { 0 }
}; };
channel.Start( channel.Start(VariaveisPortas.Ethernet_UDP_TX);
VariaveisPortas.Ethernet_UDP_TX
);
UdpChannel = channel; UdpChannel = channel;
ControlSenderDir = new ManualControlSender( ControlSenderDir = new ManualControlSender(UdpChannel, (byte)AgroBase.Models.Enums.T_Code.Dir);
UdpChannel,
(byte)AgroBase.Models.Enums.T_Code.Dir
);
ControlSenderDir.Start(); ControlSenderDir.Start();
ControlSenderMov = new ManualControlSender( ControlSenderMov = new ManualControlSender(UdpChannel, (byte)AgroBase.Models.Enums.T_Code.Mov);
UdpChannel,
(byte)AgroBase.Models.Enums.T_Code.Mov
);
ControlSenderMov.Start(); ControlSenderMov.Start();