ajustes para nao deixar a saude do atuador falhar por incoerencia

This commit is contained in:
Diego Freitas 2026-07-02 14:01:53 -03:00
parent 912773fea2
commit b953ff524e
10 changed files with 452 additions and 192 deletions

View File

@ -150,21 +150,36 @@ namespace AgroBase.Forms.Atuador
private void nudPressaoSP_ValueChanged(object sender, EventArgs e) private void nudPressaoSP_ValueChanged(object sender, EventArgs e)
{ {
var Bomba = Variaveis.OperacaoEmAndamento.DispAtu.Dados.BombaPressurizadora; var op = Variaveis.OperacaoEmAndamento;
Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha = Convert.ToInt32(((NumericUpDown)sender).Value); var Bomba = op.DispAtu.Dados.BombaPressurizadora;
GeneralJoystick.EnviarComandoAtuador(S_Code.sBMB, Bomba.ID, Status: Bomba.ComandoAtuar, Comando: Services.CanMessagePosicaoDados.Command1); op.Parametros.Controle.AtuPressaoLinha = Convert.ToInt32(((NumericUpDown)sender).Value);
op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Bomba,
Services.CanMessagePosicaoDados.Command1,
Bomba.ComandoAtuar
);
} }
private void btnLigarBomba_Click(object sender, EventArgs e) private void btnLigarBomba_Click(object sender, EventArgs e)
{ {
var Bomba = Variaveis.OperacaoEmAndamento.DispAtu.Dados.BombaPressurizadora; var op = Variaveis.OperacaoEmAndamento;
GeneralJoystick.EnviarComandoAtuador(S_Code.sBMB, Bomba.ID, Status: true, Comando: Services.CanMessagePosicaoDados.Command1, Forcar: true); var Bomba = op.DispAtu.Dados.BombaPressurizadora;
op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Bomba,
Services.CanMessagePosicaoDados.Command1,
true
);
} }
private void btnDesligarBomba_Click(object sender, EventArgs e) private void btnDesligarBomba_Click(object sender, EventArgs e)
{ {
var Bomba = Variaveis.OperacaoEmAndamento.DispAtu.Dados.BombaPressurizadora; var op = Variaveis.OperacaoEmAndamento;
GeneralJoystick.EnviarComandoAtuador(S_Code.sBMB, Bomba.ID, Status: false, Comando: Services.CanMessagePosicaoDados.Command1, Forcar: true); var Bomba = op.DispAtu.Dados.BombaPressurizadora;
op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Bomba,
Services.CanMessagePosicaoDados.Command1,
false
);
} }
private void pnlBico_Paint(object sender, PaintEventArgs e) private void pnlBico_Paint(object sender, PaintEventArgs e)
@ -188,7 +203,13 @@ namespace AgroBase.Forms.Atuador
{ {
Button btn = (Button)sender; Button btn = (Button)sender;
string ID = btn.Name.Replace("btn0_", ""); string ID = btn.Name.Replace("btn0_", "");
GeneralJoystick.EnviarComandoAtuador(S_Code.sBIC, ID, Status: false, Forcar: true); var op = Variaveis.OperacaoEmAndamento;
var Bico = op?.DispAtu?.Dados?.BicosPulverizadores?.FirstOrDefault(x => x.ID == ID);
Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.EnviarComandoFisicoBico(
Bico,
Services.CanMessagePosicaoDados.Command1,
false
);
pnlBicos.Controls.OfType<Panel>().First(x => x.Name == "pnlB_" + ID).Invalidate(); pnlBicos.Controls.OfType<Panel>().First(x => x.Name == "pnlB_" + ID).Invalidate();
pnlBicos.Controls.OfType<Panel>().First(x => x.Name == "pnlB_" + ID).Controls.OfType<Label>().First(x => x.Name == "lblCmd_" + ID).Text = "Comando: Desligado"; pnlBicos.Controls.OfType<Panel>().First(x => x.Name == "pnlB_" + ID).Controls.OfType<Label>().First(x => x.Name == "lblCmd_" + ID).Text = "Comando: Desligado";
} }
@ -197,7 +218,13 @@ namespace AgroBase.Forms.Atuador
{ {
Button btn = (Button)sender; Button btn = (Button)sender;
string ID = btn.Name.Replace("btn1_", ""); string ID = btn.Name.Replace("btn1_", "");
GeneralJoystick.EnviarComandoAtuador(S_Code.sBIC, ID, Status: true, Forcar: true); var op = Variaveis.OperacaoEmAndamento;
var Bico = op?.DispAtu?.Dados?.BicosPulverizadores?.FirstOrDefault(x => x.ID == ID);
Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.EnviarComandoFisicoBico(
Bico,
Services.CanMessagePosicaoDados.Command1,
true
);
pnlBicos.Controls.OfType<Panel>().First(x => x.Name == "pnlB_" + ID).Invalidate(); pnlBicos.Controls.OfType<Panel>().First(x => x.Name == "pnlB_" + ID).Invalidate();
pnlBicos.Controls.OfType<Panel>().First(x => x.Name == "pnlB_" + ID).Controls.OfType<Label>().First(x => x.Name == "lblCmd_" + ID).Text = "Comando: Ligado"; pnlBicos.Controls.OfType<Panel>().First(x => x.Name == "pnlB_" + ID).Controls.OfType<Label>().First(x => x.Name == "lblCmd_" + ID).Text = "Comando: Ligado";
} }

View File

@ -525,9 +525,15 @@ namespace AgroBase.Forms.IHM
private async Task EnviarComandoBomba(bool Ligar, double Pressao, int Timeout, AtuadorModel disp = null) private async Task EnviarComandoBomba(bool Ligar, double Pressao, int Timeout, AtuadorModel disp = null)
{ {
var Bomba = Variaveis.OperacaoEmAndamento.DispAtu.Dados.BombaPressurizadora; var op = Variaveis.OperacaoEmAndamento;
Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha = Pressao + 10; var Bomba = op.DispAtu.Dados.BombaPressurizadora;
GeneralJoystick.EnviarComandoAtuador(S_Code.sBMB, Bomba.ID, Status: Ligar, Comando: CanMessagePosicaoDados.Command1); op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Bomba,
CanMessagePosicaoDados.Command1,
Ligar,
potencia: 100,
pressao: Pressao + 10
);
DateTime inicio = DateTime.Now; DateTime inicio = DateTime.Now;
while (DateTime.Now < inicio.AddMilliseconds(Timeout)) while (DateTime.Now < inicio.AddMilliseconds(Timeout))
@ -544,7 +550,11 @@ namespace AgroBase.Forms.IHM
private async Task EnviarComandoBico(AtuadorBicoModel Bico, bool Ligar, int Timeout, double ValorComparar = 0, AtuadorModel disp = null) private async Task EnviarComandoBico(AtuadorBicoModel Bico, bool Ligar, int Timeout, double ValorComparar = 0, AtuadorModel disp = null)
{ {
GeneralJoystick.EnviarComandoAtuador(S_Code.sBIC, Bico.ID, Status: Ligar); Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.EnviarComandoFisicoBico(
Bico,
CanMessagePosicaoDados.Command1,
Ligar
);
DateTime inicio = DateTime.Now; DateTime inicio = DateTime.Now;
while (DateTime.Now < inicio.AddMilliseconds(Timeout)) while (DateTime.Now < inicio.AddMilliseconds(Timeout))
{ {

View File

@ -672,7 +672,11 @@ namespace AgroBase.Forms.Operacoes
if (Bico != null) if (Bico != null)
{ {
bool comando = !Bico.ComandoAtuar; bool comando = !Bico.ComandoAtuar;
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: comando, Forcar: true); op.DispAtu?.Dados?.EnviarComandoFisicoBico(
Bico,
CanMessagePosicaoDados.Command1,
comando
);
pnlAtuadores.Controls.OfType<Label>().First(x => x.Name == "lblCmd_" + ID).Text = "Comando: " + (comando ? "Ligado" : "Desligado"); pnlAtuadores.Controls.OfType<Label>().First(x => x.Name == "lblCmd_" + ID).Text = "Comando: " + (comando ? "Ligado" : "Desligado");
btn.Text = comando ? "Desligar" : "Ligar"; btn.Text = comando ? "Desligar" : "Ligar";
@ -692,8 +696,25 @@ namespace AgroBase.Forms.Operacoes
double Kp = Convert.ToDouble(txtKp.Text); double Kp = Convert.ToDouble(txtKp.Text);
double Ki = Convert.ToDouble(txtKi.Text); double Ki = Convert.ToDouble(txtKi.Text);
double Kd = Convert.ToDouble(txtKd.Text); double Kd = Convert.ToDouble(txtKd.Text);
GeneralJoystick.EnviarComandoAtuador(Bomba.Componente, Bomba.ID, Status: true, Valores: new System.Collections.Generic.List<double?>() { cmbModoBomba.SelectedIndex, potMin, Kp, Ki, Kd }, Comando: CanMessagePosicaoDados.Command2, Forcar: true);
GeneralJoystick.EnviarComandoAtuador(Bomba.Componente, Bomba.ID, Status: true, Valores: new System.Collections.Generic.List<double?>() { potMax, pressao }, Comando: CanMessagePosicaoDados.Command1, Forcar: true); op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Bomba,
CanMessagePosicaoDados.Command2,
true,
modo: (ModoControleBomba)cmbModoBomba.SelectedIndex,
potenciaMinPid: potMin,
Kp: Kp,
Ki: Ki,
Kd: Kd
);
op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Bomba,
CanMessagePosicaoDados.Command1,
true,
potencia: potMax,
pressao: pressao
);
} }
} }
@ -701,7 +722,11 @@ namespace AgroBase.Forms.Operacoes
{ {
var Bomba = op.DispAtu.Dados.BombaPressurizadora; var Bomba = op.DispAtu.Dados.BombaPressurizadora;
if (Bomba != null) if (Bomba != null)
GeneralJoystick.EnviarComandoAtuador(Bomba.Componente, Bomba.ID, Status: false, Comando: CanMessagePosicaoDados.Command1, Forcar: true); op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Bomba,
CanMessagePosicaoDados.Command1,
false
);
} }
private void btnLigarAgitador_Click(object sender, EventArgs e) private void btnLigarAgitador_Click(object sender, EventArgs e)
@ -710,9 +735,25 @@ namespace AgroBase.Forms.Operacoes
if (Agitador != null) if (Agitador != null)
{ {
op.Parametros.Controle.AtuAgitadorModo = (ModoAgitadorCalda)cmbModoAgitador.SelectedIndex; op.Parametros.Controle.AtuAgitadorModo = (ModoAgitadorCalda)cmbModoAgitador.SelectedIndex;
double potencia = Convert.ToInt32(nudPotenciaAgitador.Value); double potencia = Convert.ToInt32(nudPotenciaAgitador.Value);
GeneralJoystick.EnviarComandoAtuador(Agitador.Componente, Agitador.ID, Status: true, Valores: new System.Collections.Generic.List<double?>() { potencia }, Comando: CanMessagePosicaoDados.Command1, Forcar: true);
op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Agitador,
CanMessagePosicaoDados.Command2,
true,
modo: Agitador.Controle.Modo,
potenciaMinPid: Agitador.Controle.PotenciaMinPID,
Kp: Agitador.Controle.Kp,
Ki: Agitador.Controle.Ki,
Kd: Agitador.Controle.Kd
);
op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Agitador,
CanMessagePosicaoDados.Command1,
true,
potencia: potencia
);
} }
} }
@ -720,7 +761,11 @@ namespace AgroBase.Forms.Operacoes
{ {
var Agitador = op.DispAtu.Dados.AgitadorDeCalda; var Agitador = op.DispAtu.Dados.AgitadorDeCalda;
if (Agitador != null) if (Agitador != null)
GeneralJoystick.EnviarComandoAtuador(Agitador.Componente, Agitador.ID, Status: false, Comando: CanMessagePosicaoDados.Command1, Forcar: true); op.DispAtu?.Dados?.EnviarComandoFisicoBomba(
Agitador,
CanMessagePosicaoDados.Command1,
false
);
} }
private void nudPressaoSP_ValueChanged(object sender, EventArgs e) private void nudPressaoSP_ValueChanged(object sender, EventArgs e)

View File

@ -279,7 +279,7 @@ namespace AgroBase.Forms.Sensoriamento
if (MessageBox.Show($"Tem certeza que deseja reiniciar o módulo {Dispositivo}?", "Confirmação", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) if (MessageBox.Show($"Tem certeza que deseja reiniciar o módulo {Dispositivo}?", "Confirmação", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{ {
if (Dispositivo == T_Code.Atu) if (Dispositivo == T_Code.Atu)
GeneralJoystick.EnviarComandoAtuador(S_Code.sMOD, "", true); (Disp as AtuadorModel).EnviarComandoFisicoReset();
if (Dispositivo == T_Code.Sen) if (Dispositivo == T_Code.Sen)
GeneralJoystick.EnviarComandoSensoriamento(S_Code.sMOD, "", true); GeneralJoystick.EnviarComandoSensoriamento(S_Code.sMOD, "", true);
} }

View File

@ -625,75 +625,24 @@ namespace AgroBase
var bomba = op.DispAtu?.Dados?.BombasPressurizadoras?.FirstOrDefault(x => x.ID == Comando.IDcontrolar); var bomba = op.DispAtu?.Dados?.BombasPressurizadoras?.FirstOrDefault(x => x.ID == Comando.IDcontrolar);
S_Code comp = bico?.Componente ?? bomba?.Componente ?? S_Code.sVZO; S_Code comp = bico?.Componente ?? bomba?.Componente ?? S_Code.sVZO;
for (int i = 0; i < 1; i++) switch (comp)
{ {
EnviarComandoAtuador(comp, Comando.IDcontrolar, Status: Comando.EstadoAplicar, Comando: Comando.Comando);
}
}
public static void EnviarComandoAtuador(S_Code Componente, string ID, bool? Status = null, List<double?> Valores = null, CanMessagePosicaoDados Comando = CanMessagePosicaoDados.Command1, bool Forcar = false)
{
var op = Variaveis.OperacaoEmAndamento;
var DispAtu = op.DispAtu;
if (!DispAtu.Dados.Conectado)
return;
if (!Forcar)
{
if (!op.Sensoriamento.Operacao.OperacaoIniciada) return;
bool componente_em_uso = op.Parametros.ModulosMandatorios.Any(x => x.Dispositivo == T_Code.Atu && (x.ComponentesEmUso?.ContainsKey(ID) ?? false) && x.ComponentesEmUso[ID].Item2);
if (!componente_em_uso && (Status ?? false)) return; // Impede execucao de comandos que liguem componentes quando estiver marcado para nao utilizar
}
int id_num = -1;
CanMessagePosicaoDados comando = Comando;
byte[] data = null;
switch (Componente)
{
case S_Code.sMOD:
if (DispAtu?.Dados != null && Status == true)
{
(id_num, comando, data) = DispAtu.Dados.ProtocoloComandoReset();
}
break;
case S_Code.sBMB:
var Bomba = DispAtu?.Dados?.BombasPressurizadoras?.FirstOrDefault(x => x.ID == ID);
if (Bomba != null)
{
switch (comando)
{
case CanMessagePosicaoDados.Command1:
if (Status != null) Bomba.ComandoAtuar = (bool)Status;
if (Valores?.Count() > 0 && Valores[0] is double) Bomba.PotenciaSP = (double)Valores[0];
if (Valores?.Count() > 1 && Valores[1] is double) Bomba.PressaoSP = (double)Valores[1];
(id_num, comando, data) = Bomba.ProtocoloComando(comando);
break;
case CanMessagePosicaoDados.Command2:
if (Valores?.Count() > 0 && Valores[0] is double) Bomba.Controle.Modo = (ModoControleBomba)((int)Valores[0]);
if (Valores?.Count() > 1 && Valores[1] is double) Bomba.Controle.PotenciaMinPID = (double)Valores[1];
if (Valores?.Count() > 2 && Valores[2] is double) Bomba.Controle.Kp = (double)Valores[2];
if (Valores?.Count() > 3 && Valores[3] is double) Bomba.Controle.Ki = (double)Valores[3];
if (Valores?.Count() > 4 && Valores[4] is double) Bomba.Controle.Kd = (double)Valores[4];
(id_num, comando, data) = Bomba.ProtocoloComando(comando);
break;
}
}
break;
case S_Code.sBIC: case S_Code.sBIC:
var Bico = DispAtu.Dados.BicosPulverizadores.FirstOrDefault(x => x.ID == ID); op?.DispAtu?.Dados?.EnviarComandoFisicoBico(
if (Bico != null) bico,
{ Comando.Comando,
if (Status != null) Bico.ComandoAtuar = (bool)Status; Comando.EstadoAplicar
if (Valores?.Count() > 0) Bico.AnguloControle = (int)Valores[0]; );
(id_num, comando, data) = Bico.ProtocoloComando(); return;
}
break; case S_Code.sBMB:
op?.DispAtu?.Dados?.EnviarComandoFisicoBomba(
bomba,
Comando.Comando,
Comando.EstadoAplicar
);
return;
} }
DispAtu.AdicionarMensagemFila(id_num, comando, comando, data, false);
} }
public static void EnviarComandoSensoriamento(S_Code Componente, string ID, dynamic Status) //, bool Forcar = false public static void EnviarComandoSensoriamento(S_Code Componente, string ID, dynamic Status) //, bool Forcar = false

View File

@ -1331,6 +1331,17 @@ namespace AgroBase.Models.Modules
bomba.PotenciaSP = 100; bomba.PotenciaSP = 100;
bomba.PressaoSP = controle.AtuPressaoLinha; bomba.PressaoSP = controle.AtuPressaoLinha;
bomba.Controle.Modo = controle.AtuModoControle; bomba.Controle.Modo = controle.AtuModoControle;
EnviarComandoFisicoBomba(
bomba,
CanMessagePosicaoDados.Command2,
true,
modo: bomba.Controle.Modo,
potenciaMinPid: bomba.Controle.PotenciaMinPID,
Kp: bomba.Controle.Kp,
Ki: bomba.Controle.Ki,
Kd: bomba.Controle.Kd
);
} }
var agitador = dadosAtuador.AgitadorDeCalda; var agitador = dadosAtuador.AgitadorDeCalda;
@ -1346,6 +1357,17 @@ namespace AgroBase.Models.Modules
*/ */
agitador.PotenciaSP = 0; agitador.PotenciaSP = 0;
agitador.PressaoSP = -1; agitador.PressaoSP = -1;
EnviarComandoFisicoBomba(
agitador,
CanMessagePosicaoDados.Command2,
true,
modo: agitador.Controle.Modo,
potenciaMinPid: agitador.Controle.PotenciaMinPID,
Kp: agitador.Controle.Kp,
Ki: agitador.Controle.Ki,
Kd: agitador.Controle.Kd
);
} }
} }
@ -1408,9 +1430,10 @@ namespace AgroBase.Models.Modules
{ {
EnviarComandoFisicoBomba( EnviarComandoFisicoBomba(
bomba, bomba,
CanMessagePosicaoDados.Command1,
statusDesejado, statusDesejado,
potenciaEfetiva, potencia: potenciaEfetiva,
pressaoEfetiva pressao: pressaoEfetiva
); );
} }
} }
@ -1714,8 +1737,9 @@ namespace AgroBase.Models.Modules
{ {
EnviarComandoFisicoBico( EnviarComandoFisicoBico(
bico, bico,
CanMessagePosicaoDados.Command1,
statusDesejado, statusDesejado,
anguloDesejado angulo: anguloDesejado
); );
} }
} }
@ -1806,65 +1830,84 @@ namespace AgroBase.Models.Modules
{ {
EnviarComandoFisicoBomba( EnviarComandoFisicoBomba(
bomba, bomba,
CanMessagePosicaoDados.Command1,
statusDesejado, statusDesejado,
potenciaEfetiva, potencia: potenciaEfetiva,
pressaoEfetiva pressao: pressaoEfetiva
); );
} }
} }
} }
private void EnviarComandoFisicoBico(AtuadorBicoModel bico, bool status, double angulo) public void EnviarComandoFisicoReset()
{
var dispAtu = Variaveis.OperacaoEmAndamento?.DispAtu;
if (dispAtu?.Dados?.Conectado != true)
return;
(int id_num, CanMessagePosicaoDados dado, byte[] data) = dispAtu.Dados.ProtocoloComandoReset();
dispAtu.AdicionarMensagemFila(id_num, dado, dado, data, false);
}
public void EnviarComandoFisicoBico(
AtuadorBicoModel bico,
CanMessagePosicaoDados dado,
bool status,
double? angulo = null
)
{ {
var dispAtu = Variaveis.OperacaoEmAndamento?.DispAtu; var dispAtu = Variaveis.OperacaoEmAndamento?.DispAtu;
if (dispAtu?.Dados?.Conectado != true || bico == null) if (dispAtu?.Dados?.Conectado != true || bico == null)
return; return;
byte anguloByte = (byte)Math.Round(FuncoesMatematicas.Clamp(angulo, 0, 255)); bico.ComandoAtuar = status;
if (angulo.HasValue) bico.AnguloControle = angulo.Value;
byte[] data = (_, _, byte[] data) = bico.ProtocoloComando();
{
(byte)(status ? Estado.Ligado : Estado.Desligado),
anguloByte
};
dispAtu.AdicionarMensagemFila(bico.ID_Num, CanMessagePosicaoDados.Command1, CanMessagePosicaoDados.Command1, data, false); dispAtu.AdicionarMensagemFila(bico.ID_Num, dado, dado, data, false);
bico.UltimoComandoEnviado = DateTime.Now; bico.UltimoComandoEnviado = DateTime.Now;
bico.UltimoStatusDesejado = status; bico.UltimoStatusDesejado = status;
bico.UltimoAnguloDesejado = angulo; if (angulo.HasValue) bico.UltimoAnguloDesejado = angulo.Value;
} }
private void EnviarComandoFisicoBomba(AtuadorBombaModel bomba, bool status, double potencia, double pressao) public void EnviarComandoFisicoBomba(
AtuadorBombaModel bomba,
CanMessagePosicaoDados dado,
bool status,
double? potencia = null,
double? pressao = null,
ModoControleBomba? modo = null,
double? potenciaMinPid = null,
double? Kp = null,
double? Ki = null,
double? Kd = null
)
{ {
var dispAtu = Variaveis.OperacaoEmAndamento?.DispAtu; var dispAtu = Variaveis.OperacaoEmAndamento?.DispAtu;
if (dispAtu?.Dados?.Conectado != true || bomba == null) if (dispAtu?.Dados?.Conectado != true || bomba == null)
return; return;
byte potenciaByte = (byte)Math.Round(FuncoesMatematicas.Clamp(potencia, 0, 100)); bomba.ComandoAtuar = status;
if (potencia.HasValue) bomba.PotenciaSP = potencia.Value;
if (pressao.HasValue) bomba.PressaoSP = pressao.Value;
if (modo.HasValue) bomba.Controle.Modo = modo.Value;
if (potenciaMinPid.HasValue) bomba.Controle.PotenciaMinPID = potenciaMinPid.Value;
if (Kp.HasValue) bomba.Controle.Kp = Kp.Value;
if (Ki.HasValue) bomba.Controle.Ki = Ki.Value;
if (Kd.HasValue) bomba.Controle.Kd = Kd.Value;
/* (_, _, byte[] data) = bomba.ProtocoloComando(dado);
* -1 é usado para indicar ausência de sensor
* de pressão. No protocolo atual, representa 255.
*/
byte pressaoByte = pressao < 0 ? byte.MaxValue : (byte)Math.Round(FuncoesMatematicas.Clamp(pressao, 0, 255));
byte[] data = dispAtu.AdicionarMensagemFila(bomba.ID_Num, dado, dado, data, false);
{
(byte)(status ? Estado.Ligado : Estado.Desligado),
potenciaByte,
pressaoByte
};
dispAtu.AdicionarMensagemFila(bomba.ID_Num, CanMessagePosicaoDados.Command1, CanMessagePosicaoDados.Command1, data, false);
Variaveis.MostrarLog($"Enviando comando para a bomba {bomba.ID}: potencia={potencia}, pressao={pressao}");
bomba.UltimoComandoEnviado = DateTime.Now; bomba.UltimoComandoEnviado = DateTime.Now;
bomba.UltimoStatusDesejado = status; bomba.UltimoStatusDesejado = status;
bomba.UltimaPotenciaDesejada = potencia; if (potencia.HasValue) bomba.UltimaPotenciaDesejada = potencia.Value;
bomba.UltimaPressaoDesejada = pressao; if (pressao.HasValue) bomba.UltimaPressaoDesejada = pressao.Value;
} }
@ -1917,19 +1960,37 @@ namespace AgroBase.Models.Modules
continue; continue;
} }
Bomba.PotenciaSP = 100;
if (Bomba.ID == BombaPressurizadora.ID) if (Bomba.ID == BombaPressurizadora.ID)
{ {
Bomba.Controle.Modo = op.Parametros.Controle.AtuModoControle; EnviarComandoFisicoBomba(
Bomba.PressaoSP = op.Parametros.Controle.AtuPressaoLinha; Bomba,
GeneralJoystick.EnviarComandoAtuador(Bomba.Componente, Bomba.ID, Status: true, Valores: new List<double?>() { (int)Bomba.Controle.Modo, Bomba.Controle.PotenciaMinPID, Bomba.Controle.Kp, Bomba.Controle.Ki, Bomba.Controle.Kd }, Comando: CanMessagePosicaoDados.Command2, Forcar: true); CanMessagePosicaoDados.Command2,
GeneralJoystick.EnviarComandoAtuador(Bomba.Componente, Bomba.ID, Status: true, Valores: new List<double?>() { Bomba.PotenciaSP, Bomba.PressaoSP }, Comando: CanMessagePosicaoDados.Command1, Forcar: true); true,
modo: op.Parametros.Controle.AtuModoControle,
potenciaMinPid: Bomba.Controle.PotenciaMinPID,
Kp: Bomba.Controle.Kp,
Ki: Bomba.Controle.Ki,
Kd: Bomba.Controle.Kd
);
EnviarComandoFisicoBomba(
Bomba,
CanMessagePosicaoDados.Command1,
true,
potencia: 100,
pressao: op.Parametros.Controle.AtuPressaoLinha
);
} }
else else
{ {
Bomba.PressaoSP = -1;
bool ligar = op.Parametros.Controle.AtuAgitadorModo != ModoAgitadorCalda.SemAgitacao; bool ligar = op.Parametros.Controle.AtuAgitadorModo != ModoAgitadorCalda.SemAgitacao;
GeneralJoystick.EnviarComandoAtuador(Bomba.Componente, Bomba.ID, Status: ligar, Valores: new List<double?>() { Bomba.PotenciaSP, Bomba.PressaoSP }, Comando: CanMessagePosicaoDados.Command1, Forcar: true); EnviarComandoFisicoBomba(
Bomba,
CanMessagePosicaoDados.Command1,
ligar,
potencia: 100,
pressao: -1
);
} }
if (Bomba.Sensor_ID_Num == -1) if (Bomba.Sensor_ID_Num == -1)
@ -1951,14 +2012,22 @@ namespace AgroBase.Models.Modules
{ {
foreach (var Bico in BicosPulverizadores.Where(x => x.Comandar && x.Inicializado)) foreach (var Bico in BicosPulverizadores.Where(x => x.Comandar && x.Inicializado))
{ {
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: true, Forcar: true); EnviarComandoFisicoBico(
Bico,
CanMessagePosicaoDados.Command1,
true
);
} }
await Task.Delay(3000); await Task.Delay(3000);
foreach (var Bico in BicosPulverizadores.Where(x => x.Comandar && x.Inicializado)) foreach (var Bico in BicosPulverizadores.Where(x => x.Comandar && x.Inicializado))
{ {
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: false, Forcar: true); EnviarComandoFisicoBico(
Bico,
CanMessagePosicaoDados.Command1,
false
);
} }
await FuncoesGlobais.AguardarCondicaoAsync(Bomba.AtingiuPressao, 4000, 200); await FuncoesGlobais.AguardarCondicaoAsync(Bomba.AtingiuPressao, 4000, 200);
@ -1978,11 +2047,19 @@ namespace AgroBase.Models.Modules
if (Bico.Inicializado) if (Bico.Inicializado)
{ {
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: true, Forcar: true); EnviarComandoFisicoBico(
Bico,
CanMessagePosicaoDados.Command1,
true
);
await Task.Delay(200); await Task.Delay(200);
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: false, Forcar: true); EnviarComandoFisicoBico(
Bico,
CanMessagePosicaoDados.Command1,
false
);
await Task.Delay(200); await Task.Delay(200);

View File

@ -1152,6 +1152,12 @@ namespace AgroBase.Models
if (op.Sensoriamento.Operacao.Finalizando) if (op.Sensoriamento.Operacao.Finalizando)
return; return;
if (op.Sensoriamento?.Operacao?.OperacaoIniciada != true)
{
Variaveis.MostrarLog("[OperacaoModel.FinalizarOperacao] Ignorado: operação não iniciada.");
return;
}
op.Sensoriamento.Operacao.Finalizando = true; op.Sensoriamento.Operacao.Finalizando = true;
op.Trajetoria?.LoopAtualizaDados(); op.Trajetoria?.LoopAtualizaDados();
@ -1785,7 +1791,11 @@ namespace AgroBase.Models
Estado comandoEstado = bicos[i].ComandoAtuar ? Estado.Ligado : Estado.Desligado; Estado comandoEstado = bicos[i].ComandoAtuar ? Estado.Ligado : Estado.Desligado;
if (ForcarEnvio || comandoEstado != bicos[i]._EstadoLeitura) if (ForcarEnvio || comandoEstado != bicos[i]._EstadoLeitura)
{ {
GeneralJoystick.EnviarComandoAtuador(bicos[i].Componente, bicos[i].ID, Status: bicos[i].ComandoAtuar); op.DispAtu?.Dados?.EnviarComandoFisicoBico(
bicos[i],
CanMessagePosicaoDados.Command1,
bicos[i].ComandoAtuar
);
} }
} }
@ -2360,8 +2370,6 @@ namespace AgroBase.Models
return; return;
} }
List<double?> valores = null;
switch (comp) switch (comp)
{ {
case S_Code.sMOD: case S_Code.sMOD:
@ -2369,39 +2377,37 @@ namespace AgroBase.Models
{ {
op.Controle.AlturaBarra = (double)controleBase.Controle.Altura; op.Controle.AlturaBarra = (double)controleBase.Controle.Altura;
} }
break;
dadosAtu?.EnviarComandoFisicoReset();
return;
case S_Code.sBIC: case S_Code.sBIC:
var bico = dadosAtu.BicosPulverizadores?.FirstOrDefault(x => x.ID == controleBase._comp_id);
if (controleBase._comp_value != null) if (controleBase._comp_value != null)
{ {
int angulo; int angulo;
if (TryDeserializeValue(controleBase._comp_value, out angulo)) if (TryDeserializeValue(controleBase._comp_value, out angulo))
{ {
var bico = dadosAtu.BicosPulverizadores?.FirstOrDefault(x => x.ID == controleBase._comp_id);
if (bico != null) if (bico != null)
bico.AnguloAbertura = angulo; bico.AnguloAbertura = angulo;
} }
valores = new List<double?>
{
controleBase.Controle.AnguloSP
};
} }
break;
dadosAtu?.EnviarComandoFisicoBico(
bico,
CanMessagePosicaoDados.Command1,
controleBase.Controle.Estado == true,
angulo: controleBase.Controle.AnguloSP
);
return;
case S_Code.sBMB: case S_Code.sBMB:
var bomba = dadosAtu.BombasPressurizadoras?.FirstOrDefault(x => x.ID == controleBase._comp_id);
switch (controleBase.Controle.Comando) switch (controleBase.Controle.Comando)
{ {
case CanMessagePosicaoDados.Command1:
valores = new List<double?>
{
controleBase.Controle.Potencia,
controleBase.Controle.Pressao
};
break;
case CanMessagePosicaoDados.Command2: case CanMessagePosicaoDados.Command2:
ModoControleBomba modo; ModoControleBomba modo;
@ -2411,26 +2417,20 @@ namespace AgroBase.Models
{ {
op.Parametros.Controle.AtuModoControle = modo; op.Parametros.Controle.AtuModoControle = modo;
} }
valores = new List<double?>
{
(int)modo,
controleBase.Controle.Potencia
};
} }
break; break;
} }
break;
}
GeneralJoystick.EnviarComandoAtuador( dadosAtu?.EnviarComandoFisicoBomba(
comp, bomba,
controleBase._comp_id, CanMessagePosicaoDados.Command1,
Status: controleBase.Controle.Estado, controleBase.Controle.Estado == true,
Valores: valores, potencia: controleBase.Controle.Potencia,
Comando: controleBase.Controle.Comando, pressao: controleBase.Controle.Pressao,
Forcar: true modo: op.Parametros.Controle.AtuModoControle
); );
return;
}
} }
private void ExecutarComandoSensoriamento(OperacaoComandoBaseModel controleBase, OperacaoModel op) private void ExecutarComandoSensoriamento(OperacaoComandoBaseModel controleBase, OperacaoModel op)

View File

@ -298,7 +298,7 @@ namespace AgroBase.Comum
if (Dispositivo == T_Code.Sen) if (Dispositivo == T_Code.Sen)
GeneralJoystick.EnviarComandoSensoriamento(S_Code.sMOD, "", Status: true); GeneralJoystick.EnviarComandoSensoriamento(S_Code.sMOD, "", Status: true);
else if (Dispositivo == T_Code.Atu) else if (Dispositivo == T_Code.Atu)
GeneralJoystick.EnviarComandoAtuador(S_Code.sMOD, "", Status: true); (Dados as AtuadorModel)?.EnviarComandoFisicoReset();
} }
private void btnPinout_Click(object sender, EventArgs e) private void btnPinout_Click(object sender, EventArgs e)

View File

@ -974,9 +974,6 @@ namespace AgroBase.Services.Operadores
angulo_controle = bico.AnguloControle, angulo_controle = bico.AnguloControle,
angulo_abertura = bico.AnguloAbertura, angulo_abertura = bico.AnguloAbertura,
ultimo_comando_ms = idadeUltimoComandoBicoMs,
ultima_resposta_ms = idadeUltimaRespostaBicoMs,
atuacoes = bico.Atuacoes, atuacoes = bico.Atuacoes,
tempo_atuado = bico.TempoAtuado, tempo_atuado = bico.TempoAtuado,
tempo_atuado_em_movimento = bico.TempoAtuadoEmMovimento, tempo_atuado_em_movimento = bico.TempoAtuadoEmMovimento,
@ -984,6 +981,16 @@ namespace AgroBase.Services.Operadores
vazao_instantanea_mls = bico.VazaoInstantaneaMLs, vazao_instantanea_mls = bico.VazaoInstantaneaMLs,
vazao_media_mls = bico.VazaoMediaMLs, vazao_media_mls = bico.VazaoMediaMLs,
volume_vazado_ml = bico.VolumeVazadoML, volume_vazado_ml = bico.VolumeVazadoML,
comando_solicitado = bico.ComandoAtuar,
status_desejado_fisico = bico.UltimoStatusDesejado,
angulo_desejado_fisico = bico.UltimoAnguloDesejado,
ultimo_comando_ms = idadeUltimoComandoBicoMs,
ultima_resposta_ms = idadeUltimaRespostaBicoMs,
telemetria_pos_comando =
bico.UltimoComandoEnviado != DateTime.MinValue &&
bico.UltimaLeitura != DateTime.MinValue &&
bico.UltimaLeitura >= bico.UltimoComandoEnviado
}; };
atuBicosAtualizados.Add(($"{basePath}", bicoObj)); atuBicosAtualizados.Add(($"{basePath}", bicoObj));
@ -1056,13 +1063,21 @@ namespace AgroBase.Services.Operadores
modo_controle = bomba.Controle?.Modo, modo_controle = bomba.Controle?.Modo,
sensor_id_num = bomba.Sensor_ID_Num, sensor_id_num = bomba.Sensor_ID_Num,
ultimo_comando_ms = idadeUltimoComandoBombaMs,
ultima_resposta_ms = idadeUltimaRespostaBombaMs,
atuacoes = bomba.Atuacoes, atuacoes = bomba.Atuacoes,
tempo_atuado = bomba.TempoAtuado, tempo_atuado = bomba.TempoAtuado,
tempo_com_potencia = bomba.TempoComPotencia, tempo_com_potencia = bomba.TempoComPotencia,
potencia_media_ativa = bomba.PotenciaMediaAtiva, potencia_media_ativa = bomba.PotenciaMediaAtiva,
comando_solicitado = bomba.ComandoAtuar,
status_desejado_fisico = bomba.UltimoStatusDesejado,
potencia_desejada_fisica = bomba.UltimaPotenciaDesejada,
pressao_desejada_fisica = bomba.UltimaPressaoDesejada,
ultimo_comando_ms = idadeUltimoComandoBombaMs,
ultima_resposta_ms = idadeUltimaRespostaBombaMs,
telemetria_pos_comando =
bomba.UltimoComandoEnviado != DateTime.MinValue &&
bomba.UltimaLeitura != DateTime.MinValue &&
bomba.UltimaLeitura >= bomba.UltimoComandoEnviado
}; };
atuBombasAtivos.Add($"{tipo}.{id}"); atuBombasAtivos.Add($"{tipo}.{id}");

View File

@ -562,7 +562,7 @@ class ModuloAtuador(ModuloDiagnosticoBase):
"ultimo_status_desejado" in dados "ultimo_status_desejado" in dados
) )
comando = self._estado_desejado_ligado(dados) comando = self._estado_fisico_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)
@ -583,7 +583,13 @@ class ModuloAtuador(ModuloDiagnosticoBase):
angulo_controle = self._float(dados.get("angulo_controle", dados.get("angulo_sp", 90)), 90) angulo_controle = self._float(dados.get("angulo_controle", dados.get("angulo_sp", 90)), 90)
angulo_desejado = self._float( angulo_desejado = self._float(
dados.get("angulo_desejado", angulo_controle if comando else angulo_neutro), dados.get(
"angulo_desejado_fisico",
dados.get(
"angulo_desejado",
angulo_controle if comando else angulo_neutro
)
),
angulo_controle if comando else angulo_neutro, angulo_controle if comando else angulo_neutro,
) )
@ -610,6 +616,21 @@ class ModuloAtuador(ModuloDiagnosticoBase):
"angulo_desejado": angulo_desejado, "angulo_desejado": angulo_desejado,
"ultimo_comando_ms": ultimo_comando_ms, "ultimo_comando_ms": ultimo_comando_ms,
} }
if self._aguardando_telemetria_pos_comando(dados):
return 94, {
"comando_disponivel": True,
"aguardando_telemetria_pos_comando": True,
"comando_fisico": comando,
"comando_logico": self._comando_logico_ligado(dados),
"estado_on": estado_on,
"usa_servo": usa_servo,
"angulo": angulo,
"angulo_desejado": angulo_desejado,
"ultimo_comando_ms": ultimo_comando_ms,
"ultima_resposta_ms": self._float(dados.get("ultima_resposta_ms", 999999), 999999),
"telemetria_pos_comando": self._telemetria_depois_do_comando(dados),
}
if comando and not estado_on: if comando and not estado_on:
severidade = 85 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 70 severidade = 85 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 70
@ -631,14 +652,20 @@ class ModuloAtuador(ModuloDiagnosticoBase):
score = min(score, 55 if severidade >= 85 else 75) score = min(score, 55 if severidade >= 85 else 75)
if not comando and estado_on: if not comando and estado_on:
severidade = 95 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 82 falha_confirmada = (
tem_idade_comando and
ultimo_comando_ms >= self.COMANDO_FALHA_MS and
self._telemetria_depois_do_comando(dados)
)
severidade = 95 if falha_confirmada else 82
motivos.append(f"Bico ligado sem comando ({label})") motivos.append(f"Bico ligado sem comando ({label})")
condicoes.append({ condicoes.append({
"label": "Bico", "label": "Bico",
"valor": 1, "valor": 1,
"severidade": severidade, "severidade": severidade,
"classe": "falha_hardware" if severidade > 90 else "processo", "classe": "falha_hardware" if falha_confirmada else "processo",
"descricao": f"Bico {label} aparenta estar ligado sem comando", "descricao": f"Bico {label} aparenta estar ligado sem comando",
"acoes": [ "acoes": [
"Verificar travamento da solenoide.", "Verificar travamento da solenoide.",
@ -646,7 +673,7 @@ class ModuloAtuador(ModuloDiagnosticoBase):
], ],
}) })
score = min(score, 25 if severidade > 90 else 65) score = min(score, 25 if falha_confirmada else 65)
if usa_servo and angulo is not None and angulo_desejado is not None: if usa_servo and angulo is not None and angulo_desejado is not None:
erro_ang = abs(angulo - angulo_desejado) erro_ang = abs(angulo - angulo_desejado)
@ -734,7 +761,7 @@ class ModuloAtuador(ModuloDiagnosticoBase):
"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_fisico_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)
@ -768,6 +795,23 @@ class ModuloAtuador(ModuloDiagnosticoBase):
"pressao_sp": pressao_sp, "pressao_sp": pressao_sp,
"ultimo_comando_ms": ultimo_comando_ms, "ultimo_comando_ms": ultimo_comando_ms,
} }
if self._aguardando_telemetria_pos_comando(dados):
return 94, {
"comando_disponivel": True,
"aguardando_telemetria_pos_comando": True,
"comando_fisico": comando,
"comando_logico": self._comando_logico_ligado(dados),
"estado_on": estado_on,
"bomba_ligada": bomba_ligada,
"potencia": potencia,
"potencia_sp": potencia_sp,
"pressao": pressao,
"pressao_sp": pressao_sp,
"ultimo_comando_ms": ultimo_comando_ms,
"ultima_resposta_ms": self._float(dados.get("ultima_resposta_ms", 999999), 999999),
"telemetria_pos_comando": self._telemetria_depois_do_comando(dados),
}
if comando and not bomba_ligada: if comando and not bomba_ligada:
severidade = 85 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 70 severidade = 85 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 70
@ -789,14 +833,25 @@ class ModuloAtuador(ModuloDiagnosticoBase):
score = min(score, 55 if severidade >= 85 else 75) score = min(score, 55 if severidade >= 85 else 75)
if not comando and bomba_ligada: if not comando and bomba_ligada:
severidade = 95 if tem_idade_comando and ultimo_comando_ms >= self.COMANDO_FALHA_MS else 82 falha_confirmada = (
tem_idade_comando and
ultimo_comando_ms >= self.COMANDO_FALHA_MS and
self._telemetria_depois_do_comando(dados)
)
# O agitador não deve parar a operação por esse caso sozinho.
# Ele pode alertar, mas não virar falha crítica do ATU.
if eh_agitador:
falha_confirmada = False
severidade = 95 if falha_confirmada else 82
motivos.append(f"Bomba {label} ligada sem comando") motivos.append(f"Bomba {label} ligada sem comando")
condicoes.append({ condicoes.append({
"label": "Bomba", "label": "Bomba",
"valor": potencia, "valor": potencia,
"severidade": severidade, "severidade": severidade,
"classe": "falha_hardware" if severidade > 90 else "processo", "classe": "falha_hardware" if falha_confirmada else "processo",
"descricao": f"Bomba {label} aparenta ligada sem comando", "descricao": f"Bomba {label} aparenta ligada sem comando",
"acoes": [ "acoes": [
"Verificar travamento de relé/saída.", "Verificar travamento de relé/saída.",
@ -804,7 +859,7 @@ class ModuloAtuador(ModuloDiagnosticoBase):
], ],
}) })
score = min(score, 25 if severidade > 90 else 65) score = min(score, 25 if falha_confirmada else 65)
if eh_principal and comando and bomba_ligada: if eh_principal and comando and bomba_ligada:
pressao_ref = pressao if pressao > 0 else ctx["pressao_linha"] pressao_ref = pressao if pressao > 0 else ctx["pressao_linha"]
@ -1328,8 +1383,27 @@ class ModuloAtuador(ModuloDiagnosticoBase):
def _comando_em_transicao(self, dados, janela_ms=None): def _comando_em_transicao(self, dados, janela_ms=None):
janela_ms = janela_ms if janela_ms is not None else self.PROCESSO_GRACE_MS janela_ms = janela_ms if janela_ms is not None else self.PROCESSO_GRACE_MS
ultimo_comando_ms = self._float(dados.get("ultimo_comando_ms", 999999), 999999)
return ultimo_comando_ms < 900000 and ultimo_comando_ms < janela_ms ultimo_comando_ms = self._float(
dados.get("ultimo_comando_ms", 999999),
999999
)
if ultimo_comando_ms >= 900000:
return False
if ultimo_comando_ms < janela_ms:
return True
# Mesmo fora da janela curta, se ainda não chegou telemetria
# posterior ao comando, não dá para julgar a atuação como falha real.
if (
ultimo_comando_ms < self.COMANDO_FALHA_MS and
not self._telemetria_depois_do_comando(dados)
):
return True
return False
def _pulverizacao_em_transicao(self, ctx): def _pulverizacao_em_transicao(self, ctx):
bomba = ctx.get("bomba_principal") bomba = ctx.get("bomba_principal")
@ -1413,22 +1487,85 @@ class ModuloAtuador(ModuloDiagnosticoBase):
return texto in ["1", "ligado", "true", "on", "sim", "yes"] return texto in ["1", "ligado", "true", "on", "sim", "yes"]
def _estado_desejado_ligado(self, dados): def _estado_desejado_ligado(self, dados):
"""
Compatibilidade com código antigo.
A partir de agora, 'estado desejado' no health significa
último estado físico enviado ao ATU, não intenção lógica instantânea.
"""
return self._estado_fisico_desejado_ligado(dados)
def _estado_fisico_desejado_ligado(self, dados):
if not isinstance(dados, dict): if not isinstance(dados, dict):
return False return False
if "comando_atuar" in dados:
return self._bool(dados.get("comando_atuar", False))
for key in [ for key in [
"estado_desejado",
"status_desejado",
"status_desejado_fisico", "status_desejado_fisico",
"estado_desejado_fisico",
"estado_desejado",
"ultimo_status_desejado", "ultimo_status_desejado",
"status_desejado",
]: ]:
if key in dados: if key in dados and dados.get(key) is not None:
return self._estado_ligado(dados.get(key)) return self._estado_ligado(dados.get(key))
return False # Fallback para versões antigas do Redis.
return self._bool(
dados.get(
"comando_solicitado",
dados.get("comando_atuar", False)
)
)
def _comando_logico_ligado(self, dados):
if not isinstance(dados, dict):
return False
return self._bool(
dados.get(
"comando_solicitado",
dados.get("comando_atuar", False)
)
)
def _tem_comando_fisico_registrado(self, dados):
ultimo_comando_ms = self._float(
dados.get("ultimo_comando_ms", 999999),
999999
)
return ultimo_comando_ms < 900000
def _telemetria_depois_do_comando(self, dados, tolerancia_ms=150):
if not isinstance(dados, dict):
return True
if not self._tem_comando_fisico_registrado(dados):
return True
if "telemetria_pos_comando" in dados:
return self._bool(dados.get("telemetria_pos_comando", False))
ultimo_comando_ms = self._float(dados.get("ultimo_comando_ms", 999999), 999999)
ultima_resposta_ms = self._float(dados.get("ultima_resposta_ms", 999999), 999999)
if ultimo_comando_ms >= 900000 or ultima_resposta_ms >= 900000:
return False
# idade menor = evento mais recente
return ultima_resposta_ms <= ultimo_comando_ms + tolerancia_ms
def _aguardando_telemetria_pos_comando(self, dados):
ultimo_comando_ms = self._float(
dados.get("ultimo_comando_ms", 999999),
999999
)
return (
ultimo_comando_ms < 900000 and
ultimo_comando_ms < self.COMANDO_FALHA_MS and
not self._telemetria_depois_do_comando(dados)
)
@staticmethod @staticmethod