Compare commits
2 Commits
7a1b6bd51d
...
19392738f7
| Author | SHA1 | Date |
|---|---|---|
|
|
19392738f7 | |
|
|
42b9058df1 |
|
|
@ -206,7 +206,7 @@ namespace AgroBase.Forms.IHM.Operacao
|
||||||
?? operacaoParametrizando.Mapa?.RuasPercorrer?.Count
|
?? operacaoParametrizando.Mapa?.RuasPercorrer?.Count
|
||||||
?? 0;
|
?? 0;
|
||||||
|
|
||||||
if (qtdRuas == 0)
|
if (qtdRuas < 2)
|
||||||
{
|
{
|
||||||
FuncoesIHM.MostrarAlerta(
|
FuncoesIHM.MostrarAlerta(
|
||||||
"Trajetória não parametrizada",
|
"Trajetória não parametrizada",
|
||||||
|
|
@ -217,12 +217,29 @@ namespace AgroBase.Forms.IHM.Operacao
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operacaoIniciada)
|
try
|
||||||
op?.AtualizarParametrosParciais(operacaoParametrizando.Parametros, op);
|
{
|
||||||
else
|
if (operacaoIniciada)
|
||||||
OperacaoModel.CarregarParamerosOperacaoBase(operacaoParametrizando.Parametros);
|
{
|
||||||
|
op?.AtualizarParametrosParciais(operacaoParametrizando.Parametros, op);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OperacaoModel.CarregarParamerosOperacaoBase(operacaoParametrizando.Parametros);
|
||||||
|
}
|
||||||
|
|
||||||
ParametrosSalvos?.Invoke(this, EventArgs.Empty);
|
ParametrosSalvos?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog("[ucOperacaoParametros.BtnSalvar] " + ex);
|
||||||
|
FuncoesIHM.MostrarAlerta("Não foi possível salvar a operação", ex.Message, MessageBoxIcon.Warning);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Variaveis.MostrarLog("[ucOperacaoParametros.BtnSalvar][ERRO] " + ex);
|
||||||
|
FuncoesIHM.MostrarAlerta("Erro ao salvar parâmetros", "Não foi possível aplicar os parâmetros da operação. Consulte os logs para mais detalhes.", MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void BtnRedefinir_Click(object sender, EventArgs e)
|
private async void BtnRedefinir_Click(object sender, EventArgs e)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,10 @@ namespace AgroBase.Forms.IHM
|
||||||
this.fldEntreEixos = new AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField();
|
this.fldEntreEixos = new AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField();
|
||||||
this.fldAnguloFrontal = new AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField();
|
this.fldAnguloFrontal = new AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField();
|
||||||
this.fldAnguloLateral = new AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField();
|
this.fldAnguloLateral = new AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField();
|
||||||
|
this.pnlReferenciaImu = new System.Windows.Forms.Panel();
|
||||||
|
this.lblOffsetImuRoll = new System.Windows.Forms.Label();
|
||||||
|
this.lblOffsetImuPitch = new System.Windows.Forms.Label();
|
||||||
|
this.btnReferenciarImu = new AgroBase.Forms.IHM.Controls.HmiSidebarButton();
|
||||||
this.pnlBateria = new AgroBase.Forms.IHM.Controls.HmiRoundedPanel();
|
this.pnlBateria = new AgroBase.Forms.IHM.Controls.HmiRoundedPanel();
|
||||||
this.bateriaLayout = new System.Windows.Forms.TableLayoutPanel();
|
this.bateriaLayout = new System.Windows.Forms.TableLayoutPanel();
|
||||||
this.lblBateria = new System.Windows.Forms.Label();
|
this.lblBateria = new System.Windows.Forms.Label();
|
||||||
|
|
@ -94,6 +98,7 @@ namespace AgroBase.Forms.IHM
|
||||||
this.rightLayout.SuspendLayout();
|
this.rightLayout.SuspendLayout();
|
||||||
this.pnlGeometria.SuspendLayout();
|
this.pnlGeometria.SuspendLayout();
|
||||||
this.geometriaLayout.SuspendLayout();
|
this.geometriaLayout.SuspendLayout();
|
||||||
|
this.pnlReferenciaImu.SuspendLayout();
|
||||||
this.pnlBateria.SuspendLayout();
|
this.pnlBateria.SuspendLayout();
|
||||||
this.bateriaLayout.SuspendLayout();
|
this.bateriaLayout.SuspendLayout();
|
||||||
this.pnlReservatorio.SuspendLayout();
|
this.pnlReservatorio.SuspendLayout();
|
||||||
|
|
@ -524,6 +529,7 @@ namespace AgroBase.Forms.IHM
|
||||||
this.geometriaLayout.Controls.Add(this.fldEntreEixos, 2, 1);
|
this.geometriaLayout.Controls.Add(this.fldEntreEixos, 2, 1);
|
||||||
this.geometriaLayout.Controls.Add(this.fldAnguloFrontal, 0, 2);
|
this.geometriaLayout.Controls.Add(this.fldAnguloFrontal, 0, 2);
|
||||||
this.geometriaLayout.Controls.Add(this.fldAnguloLateral, 1, 2);
|
this.geometriaLayout.Controls.Add(this.fldAnguloLateral, 1, 2);
|
||||||
|
this.geometriaLayout.Controls.Add(this.pnlReferenciaImu, 2, 2);
|
||||||
this.geometriaLayout.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.geometriaLayout.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.geometriaLayout.Location = new System.Drawing.Point(10, 6);
|
this.geometriaLayout.Location = new System.Drawing.Point(10, 6);
|
||||||
this.geometriaLayout.Margin = new System.Windows.Forms.Padding(0);
|
this.geometriaLayout.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
|
@ -660,6 +666,58 @@ namespace AgroBase.Forms.IHM
|
||||||
this.fldAnguloLateral.Titulo = "Ângulo máx. lateral";
|
this.fldAnguloLateral.Titulo = "Ângulo máx. lateral";
|
||||||
this.fldAnguloLateral.Unidade = "°";
|
this.fldAnguloLateral.Unidade = "°";
|
||||||
//
|
//
|
||||||
|
// pnlReferenciaImu
|
||||||
|
//
|
||||||
|
this.pnlReferenciaImu.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(36)))), ((int)(((byte)(41)))));
|
||||||
|
this.pnlReferenciaImu.Controls.Add(this.lblOffsetImuRoll);
|
||||||
|
this.pnlReferenciaImu.Controls.Add(this.lblOffsetImuPitch);
|
||||||
|
this.pnlReferenciaImu.Controls.Add(this.btnReferenciarImu);
|
||||||
|
this.pnlReferenciaImu.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.pnlReferenciaImu.Location = new System.Drawing.Point(247, 84);
|
||||||
|
this.pnlReferenciaImu.Margin = new System.Windows.Forms.Padding(3, 2, 2, 2);
|
||||||
|
this.pnlReferenciaImu.Name = "pnlReferenciaImu";
|
||||||
|
this.pnlReferenciaImu.Padding = new System.Windows.Forms.Padding(4, 2, 4, 3);
|
||||||
|
this.pnlReferenciaImu.Size = new System.Drawing.Size(119, 54);
|
||||||
|
this.pnlReferenciaImu.TabIndex = 5;
|
||||||
|
//
|
||||||
|
// lblOffsetImuRoll
|
||||||
|
//
|
||||||
|
this.lblOffsetImuRoll.Font = new System.Drawing.Font("Segoe UI", 7F);
|
||||||
|
this.lblOffsetImuRoll.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(204)))), ((int)(((byte)(218)))), ((int)(((byte)(211)))));
|
||||||
|
this.lblOffsetImuRoll.Location = new System.Drawing.Point(5, 2);
|
||||||
|
this.lblOffsetImuRoll.Name = "lblOffsetImuRoll";
|
||||||
|
this.lblOffsetImuRoll.Size = new System.Drawing.Size(54, 15);
|
||||||
|
this.lblOffsetImuRoll.TabIndex = 0;
|
||||||
|
this.lblOffsetImuRoll.Text = "Roll: 0.00°";
|
||||||
|
this.lblOffsetImuRoll.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||||
|
//
|
||||||
|
// lblOffsetImuPitch
|
||||||
|
//
|
||||||
|
this.lblOffsetImuPitch.Font = new System.Drawing.Font("Segoe UI", 7F);
|
||||||
|
this.lblOffsetImuPitch.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(204)))), ((int)(((byte)(218)))), ((int)(((byte)(211)))));
|
||||||
|
this.lblOffsetImuPitch.Location = new System.Drawing.Point(59, 2);
|
||||||
|
this.lblOffsetImuPitch.Name = "lblOffsetImuPitch";
|
||||||
|
this.lblOffsetImuPitch.Size = new System.Drawing.Size(56, 15);
|
||||||
|
this.lblOffsetImuPitch.TabIndex = 1;
|
||||||
|
this.lblOffsetImuPitch.Text = "Pitch: 0.00°";
|
||||||
|
this.lblOffsetImuPitch.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||||
|
//
|
||||||
|
// btnReferenciarImu
|
||||||
|
//
|
||||||
|
this.btnReferenciarImu.AccentColor = System.Drawing.Color.FromArgb(((int)(((byte)(155)))), ((int)(((byte)(106)))), ((int)(((byte)(222)))));
|
||||||
|
this.btnReferenciarImu.BackColor = System.Drawing.Color.Transparent;
|
||||||
|
this.btnReferenciarImu.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(86)))), ((int)(((byte)(170)))));
|
||||||
|
this.btnReferenciarImu.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
|
this.btnReferenciarImu.FillColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(36)))), ((int)(((byte)(41)))));
|
||||||
|
this.btnReferenciarImu.IconText = "↻";
|
||||||
|
this.btnReferenciarImu.Location = new System.Drawing.Point(4, 18);
|
||||||
|
this.btnReferenciarImu.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
this.btnReferenciarImu.MinimumSize = new System.Drawing.Size(60, 34);
|
||||||
|
this.btnReferenciarImu.Name = "btnReferenciarImu";
|
||||||
|
this.btnReferenciarImu.Size = new System.Drawing.Size(111, 34);
|
||||||
|
this.btnReferenciarImu.TabIndex = 2;
|
||||||
|
this.btnReferenciarImu.Text = "ZERAR";
|
||||||
|
//
|
||||||
// pnlBateria
|
// pnlBateria
|
||||||
//
|
//
|
||||||
this.pnlBateria.AccentColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(210)))), ((int)(((byte)(122)))));
|
this.pnlBateria.AccentColor = System.Drawing.Color.FromArgb(((int)(((byte)(72)))), ((int)(((byte)(210)))), ((int)(((byte)(122)))));
|
||||||
|
|
@ -1019,6 +1077,7 @@ namespace AgroBase.Forms.IHM
|
||||||
this.rightLayout.ResumeLayout(false);
|
this.rightLayout.ResumeLayout(false);
|
||||||
this.pnlGeometria.ResumeLayout(false);
|
this.pnlGeometria.ResumeLayout(false);
|
||||||
this.geometriaLayout.ResumeLayout(false);
|
this.geometriaLayout.ResumeLayout(false);
|
||||||
|
this.pnlReferenciaImu.ResumeLayout(false);
|
||||||
this.pnlBateria.ResumeLayout(false);
|
this.pnlBateria.ResumeLayout(false);
|
||||||
this.bateriaLayout.ResumeLayout(false);
|
this.bateriaLayout.ResumeLayout(false);
|
||||||
this.pnlReservatorio.ResumeLayout(false);
|
this.pnlReservatorio.ResumeLayout(false);
|
||||||
|
|
@ -1065,6 +1124,10 @@ namespace AgroBase.Forms.IHM
|
||||||
private AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField fldEntreEixos;
|
private AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField fldEntreEixos;
|
||||||
private AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField fldAnguloFrontal;
|
private AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField fldAnguloFrontal;
|
||||||
private AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField fldAnguloLateral;
|
private AgroBase.Forms.IHM.Operacao.Parametros.Controls.HmiCompactNumericField fldAnguloLateral;
|
||||||
|
private System.Windows.Forms.Panel pnlReferenciaImu;
|
||||||
|
private System.Windows.Forms.Label lblOffsetImuRoll;
|
||||||
|
private System.Windows.Forms.Label lblOffsetImuPitch;
|
||||||
|
private AgroBase.Forms.IHM.Controls.HmiSidebarButton btnReferenciarImu;
|
||||||
|
|
||||||
private AgroBase.Forms.IHM.Controls.HmiRoundedPanel pnlBateria;
|
private AgroBase.Forms.IHM.Controls.HmiRoundedPanel pnlBateria;
|
||||||
private System.Windows.Forms.TableLayoutPanel bateriaLayout;
|
private System.Windows.Forms.TableLayoutPanel bateriaLayout;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using AgroBase.Forms.IHM.Controls;
|
using AgroBase.Forms.IHM.Controls;
|
||||||
using AgroBase.Forms.IHM.Operacao.Parametros.Controls;
|
using AgroBase.Forms.IHM.Operacao.Parametros.Controls;
|
||||||
using AgroBase.Models;
|
using AgroBase.Models;
|
||||||
|
using AgroBase.Models.Components;
|
||||||
using AgroBase.Services.Operadores;
|
using AgroBase.Services.Operadores;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -56,6 +57,7 @@ namespace AgroBase.Forms.IHM
|
||||||
public void AtualizarDadosTela()
|
public void AtualizarDadosTela()
|
||||||
{
|
{
|
||||||
lblRover.Text = $"ROVER {VariaveisEquipamento.Parametros.serial_number}\r\nConfiguração local";
|
lblRover.Text = $"ROVER {VariaveisEquipamento.Parametros.serial_number}\r\nConfiguração local";
|
||||||
|
AtualizarReferenciaImuTela();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -102,6 +104,11 @@ namespace AgroBase.Forms.IHM
|
||||||
await ExecutarSeguroAsync("Erro ao testar comunicação!", TestarComunicacaoAsync);
|
await ExecutarSeguroAsync("Erro ao testar comunicação!", TestarComunicacaoAsync);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
btnReferenciarImu.Click += async delegate
|
||||||
|
{
|
||||||
|
await ExecutarSeguroAsync("Erro ao referenciar IMU!", ReferenciarImuAsync);
|
||||||
|
};
|
||||||
|
|
||||||
cameraFrontal.CameraChanged += cameraFrontal_CameraChanged;
|
cameraFrontal.CameraChanged += cameraFrontal_CameraChanged;
|
||||||
|
|
||||||
cameraErvas.CameraChanged += cameraErvas_CameraChanged;
|
cameraErvas.CameraChanged += cameraErvas_CameraChanged;
|
||||||
|
|
@ -179,6 +186,94 @@ namespace AgroBase.Forms.IHM
|
||||||
campo.DecimalPlaces = casas;
|
campo.DecimalPlaces = casas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void AtualizarReferenciaImuTela()
|
||||||
|
{
|
||||||
|
lblOffsetImuRoll.Text = $"L: {VariaveisEquipamento.Parametros.imu_roll_offset_deg:+0.00;-0.00;0.00}°";
|
||||||
|
lblOffsetImuPitch.Text = $"F: {VariaveisEquipamento.Parametros.imu_pitch_offset_deg:+0.00;-0.00;0.00}°";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReferenciarImuAsync()
|
||||||
|
{
|
||||||
|
var resposta = CustomDialog.ShowDialog(
|
||||||
|
"Referenciar IMU",
|
||||||
|
"Posicione o rover em uma superfície nivelada e mantenha-o imóvel.\r\n\r\n" +
|
||||||
|
"A referência atual de inclinação será recalculada.",
|
||||||
|
MessageBoxIcon.Information,
|
||||||
|
new Dictionary<(string, DialogResult?), Action>()
|
||||||
|
{
|
||||||
|
{ ("OK", DialogResult.OK), () => { } },
|
||||||
|
{ ("Cancelar", DialogResult.Cancel), () => { } },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resposta != DialogResult.OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
btnReferenciarImu.Enabled = false;
|
||||||
|
btnReferenciarImu.Text = "LENDO...";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const int quantidadeAmostras = 12;
|
||||||
|
const int intervaloMs = 100;
|
||||||
|
var rolls = new List<double>();
|
||||||
|
var pitches = new List<double>();
|
||||||
|
|
||||||
|
for (int i = 0; i < quantidadeAmostras; i++)
|
||||||
|
{
|
||||||
|
var imu = Variaveis.OperacaoEmAndamento?.Sensoriamento?.IMU;
|
||||||
|
|
||||||
|
if (imu != null && imu.valido && imu.IdadeSegundos <= 1.0)
|
||||||
|
{
|
||||||
|
rolls.Add(imu.roll);
|
||||||
|
pitches.Add(imu.pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(intervaloMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rolls.Count < 8)
|
||||||
|
throw new InvalidOperationException("Não há amostras válidas e recentes suficientes do IMU para realizar a referência.");
|
||||||
|
|
||||||
|
double mediaRoll = rolls.Average();
|
||||||
|
double mediaPitch = pitches.Average();
|
||||||
|
|
||||||
|
double desvioRoll = Math.Sqrt(rolls.Average(x => Math.Pow(x - mediaRoll, 2)));
|
||||||
|
double desvioPitch = Math.Sqrt(pitches.Average(x => Math.Pow(x - mediaPitch, 2)));
|
||||||
|
|
||||||
|
if (desvioRoll > 0.50 || desvioPitch > 0.50)
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"O rover parece estar em movimento. Estabilidade insuficiente (σ roll {desvioRoll:0.00}°, σ pitch {desvioPitch:0.00}°). Mantenha-o imóvel e tente novamente.");
|
||||||
|
|
||||||
|
double novoRoll = VariaveisEquipamento.Parametros.imu_roll_offset_deg + mediaRoll;
|
||||||
|
double novoPitch = VariaveisEquipamento.Parametros.imu_pitch_offset_deg + mediaPitch;
|
||||||
|
|
||||||
|
var parametros = VariaveisEquipamento.Parametros;
|
||||||
|
DefinirParametroGlobal(parametros, novoRoll, "imu_roll_offset_deg");
|
||||||
|
DefinirParametroGlobal(parametros, novoPitch, "imu_pitch_offset_deg");
|
||||||
|
|
||||||
|
if (!parametros.SalvarParametros())
|
||||||
|
throw new InvalidOperationException("Não foi possível salvar os novos offsets do IMU.");
|
||||||
|
|
||||||
|
parametros.AtualizarParametros();
|
||||||
|
HealthWorkerService.AtualizarDadosOperacao();
|
||||||
|
AtualizarReferenciaImuTela();
|
||||||
|
|
||||||
|
FuncoesIHM.MostrarAlerta(
|
||||||
|
"IMU referenciado",
|
||||||
|
$"Referência atualizada com sucesso.\r\n\r\n" +
|
||||||
|
$"Roll: {novoRoll:+0.00;-0.00;0.00}°\r\n" +
|
||||||
|
$"Pitch: {novoPitch:+0.00;-0.00;0.00}°",
|
||||||
|
MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
btnReferenciarImu.Text = "ZERAR";
|
||||||
|
btnReferenciarImu.Enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region Câmeras
|
#region Câmeras
|
||||||
|
|
||||||
private async Task AtualizarCameras()
|
private async Task AtualizarCameras()
|
||||||
|
|
|
||||||
|
|
@ -3197,15 +3197,55 @@ namespace AgroBase.Models
|
||||||
|
|
||||||
private void ExecutarComandoMovDirGenerico(OperacaoComandoBaseModel controleBase, OperacaoModel op)
|
private void ExecutarComandoMovDirGenerico(OperacaoComandoBaseModel controleBase, OperacaoModel op)
|
||||||
{
|
{
|
||||||
op.DefinirEmergencia(controleBase.Emergencia);
|
if (op == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ============================================================
|
||||||
|
* COMANDO OPERACIONAL DE PARADA
|
||||||
|
* ============================================================
|
||||||
|
*
|
||||||
|
* Pausa/Emergência só podem ser alteradas por um comando
|
||||||
|
* explicitamente destinado a isso.
|
||||||
|
*
|
||||||
|
* MOV/DIR nunca devem limpar esses estados.
|
||||||
|
*/
|
||||||
|
if (controleBase.Dispositivo == T_Code.Vzo)
|
||||||
|
{
|
||||||
|
op.DefinirEmergencia(controleBase.Emergencia);
|
||||||
|
|
||||||
|
if (op.Sensoriamento.Operacao.Emergencia)
|
||||||
|
return;
|
||||||
|
|
||||||
|
op.DefinirPausa(controleBase.Pausa);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emergência sempre possui autoridade máxima.
|
||||||
|
*/
|
||||||
if (op.Sensoriamento.Operacao.Emergencia)
|
if (op.Sensoriamento.Operacao.Emergencia)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
op.DefinirPausa(controleBase.Pausa);
|
bool controleManualAcionado =
|
||||||
|
op.Parametros?.Controle?.ControleManualAcionado == true;
|
||||||
|
|
||||||
if (op.Sensoriamento.Operacao.Pausa)
|
/*
|
||||||
|
* ============================================================
|
||||||
|
* PAUSA OPERACIONAL
|
||||||
|
* ============================================================
|
||||||
|
*
|
||||||
|
* Pausa impede o AUTOMÁTICO, mas não impede uma intervenção
|
||||||
|
* manual explicitamente assumida pelo operador.
|
||||||
|
*
|
||||||
|
* Também permitimos comandos de soltura/neutro mesmo após
|
||||||
|
* retirar o controle manual, para nunca deixar comando preso.
|
||||||
|
*/
|
||||||
|
if (op.Sensoriamento.Operacao.Pausa && !controleManualAcionado && !controleBase.Solto)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (controleBase.Controle == null)
|
if (controleBase.Controle == null)
|
||||||
return;
|
return;
|
||||||
|
|
@ -3215,12 +3255,20 @@ namespace AgroBase.Models
|
||||||
if (cmdRover == null)
|
if (cmdRover == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (cmdRover.Dispositivo == T_Code.Mov && (op.Parametros?.Controle?.MovimentoAutomatico ?? false) && op.Sensoriamento.Operacao.OperacaoIniciada)
|
if (
|
||||||
|
cmdRover.Dispositivo == T_Code.Mov &&
|
||||||
|
(op.Parametros?.Controle?.MovimentoAutomatico ?? false) &&
|
||||||
|
op.Sensoriamento.Operacao.OperacaoIniciada
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmdRover.Dispositivo == T_Code.Dir && (op.Parametros?.Controle?.DirecionalAutomatico ?? false) && op.Sensoriamento.Operacao.OperacaoIniciada)
|
if (
|
||||||
|
cmdRover.Dispositivo == T_Code.Dir &&
|
||||||
|
(op.Parametros?.Controle?.DirecionalAutomatico ?? false) &&
|
||||||
|
op.Sensoriamento.Operacao.OperacaoIniciada
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -3231,16 +3279,24 @@ namespace AgroBase.Models
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (controleBase.Controle.PercentualVelocidadeSP != null)
|
if (controleBase.Controle.PercentualVelocidadeSP != null)
|
||||||
controle.PercentualVelocidadeSP = (double)controleBase .Controle.PercentualVelocidadeSP;
|
controle.PercentualVelocidadeSP =
|
||||||
|
(double)controleBase.Controle.PercentualVelocidadeSP;
|
||||||
|
|
||||||
if (controleBase.Controle.AnguloSP != null)
|
if (controleBase.Controle.AnguloSP != null)
|
||||||
controle.Angulo = (double)controleBase.Controle.AnguloSP;
|
controle.Angulo =
|
||||||
|
(double)controleBase.Controle.AnguloSP;
|
||||||
|
|
||||||
if (controleBase.Controle.TipoMovimentoDirecional != null)
|
if (controleBase.Controle.TipoMovimentoDirecional != null)
|
||||||
op.DefinirTipoMovimentoControle((TipoMovimentoDirecional)controleBase.Controle.TipoMovimentoDirecional);
|
{
|
||||||
|
op.DefinirTipoMovimentoControle(
|
||||||
|
(TipoMovimentoDirecional)
|
||||||
|
controleBase.Controle.TipoMovimentoDirecional
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (controleBase.Controle.EmFreio != null)
|
if (controleBase.Controle.EmFreio != null)
|
||||||
controle.EmFreio = (bool)controleBase.Controle.EmFreio;
|
controle.EmFreio =
|
||||||
|
(bool)controleBase.Controle.EmFreio;
|
||||||
|
|
||||||
switch (cmdRover.Dispositivo)
|
switch (cmdRover.Dispositivo)
|
||||||
{
|
{
|
||||||
|
|
@ -3248,7 +3304,8 @@ namespace AgroBase.Models
|
||||||
GeneralJoystick.ProcessarDadosControle(
|
GeneralJoystick.ProcessarDadosControle(
|
||||||
cmdRover.botaoJoy,
|
cmdRover.botaoJoy,
|
||||||
controleBase.Solto,
|
controleBase.Solto,
|
||||||
valorDesejado: controle.PercentualVelocidadeSP,
|
valorDesejado:
|
||||||
|
controle.PercentualVelocidadeSP,
|
||||||
ForcarComando: true
|
ForcarComando: true
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
@ -3257,7 +3314,8 @@ namespace AgroBase.Models
|
||||||
GeneralJoystick.ProcessarDadosControle(
|
GeneralJoystick.ProcessarDadosControle(
|
||||||
cmdRover.botaoJoy,
|
cmdRover.botaoJoy,
|
||||||
controleBase.Solto,
|
controleBase.Solto,
|
||||||
valorDesejado: controle.Angulo,
|
valorDesejado:
|
||||||
|
controle.Angulo,
|
||||||
ForcarComando: true
|
ForcarComando: true
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1123,7 +1123,7 @@ namespace AgroBase.Models
|
||||||
{ TipoMovimentoDirecional.Diagnostico, 25 },
|
{ TipoMovimentoDirecional.Diagnostico, 25 },
|
||||||
};
|
};
|
||||||
public static double AnguloInclinacaoRollMax { get; set; } = 22.0; // Lateral
|
public static double AnguloInclinacaoRollMax { get; set; } = 22.0; // Lateral
|
||||||
public static double AnguloInclinacaoPitchMax { get; set; } = 30.0; // Frontal 40 graus max
|
public static double AnguloInclinacaoPitchMax { get; set; } = 30.0; // Frontal
|
||||||
public static string VozAlerta { get; set; } = "Microsoft Maria Desktop";
|
public static string VozAlerta { get; set; } = "Microsoft Maria Desktop";
|
||||||
public static bool Pronto => Variaveis.OperacaoEmAndamento?.Parametros?.Controle != null;
|
public static bool Pronto => Variaveis.OperacaoEmAndamento?.Parametros?.Controle != null;
|
||||||
|
|
||||||
|
|
@ -1527,6 +1527,9 @@ namespace AgroBase.Models
|
||||||
public string can_gateway { get; set; }
|
public string can_gateway { get; set; }
|
||||||
public string can_submask { get; set; }
|
public string can_submask { get; set; }
|
||||||
|
|
||||||
|
public double imu_roll_offset_deg { get; set; } = 0.0; // Lateral
|
||||||
|
public double imu_pitch_offset_deg { get; set; } = 0.0; // Frontal
|
||||||
|
|
||||||
public void AtualizarParametros()
|
public void AtualizarParametros()
|
||||||
{
|
{
|
||||||
string config_path = Path.Combine(Variaveis.CaminhoSistema, Variaveis.CaminhoParametros, "config.json");
|
string config_path = Path.Combine(Variaveis.CaminhoSistema, Variaveis.CaminhoParametros, "config.json");
|
||||||
|
|
@ -1558,6 +1561,9 @@ namespace AgroBase.Models
|
||||||
camera_caminhho_id = par.camera_caminhho_id;
|
camera_caminhho_id = par.camera_caminhho_id;
|
||||||
camera_ervas_id = par.camera_ervas_id;
|
camera_ervas_id = par.camera_ervas_id;
|
||||||
|
|
||||||
|
imu_pitch_offset_deg = par.imu_pitch_offset_deg;
|
||||||
|
imu_roll_offset_deg = par.imu_roll_offset_deg;
|
||||||
|
|
||||||
Carregado = true;
|
Carregado = true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -429,7 +429,9 @@ namespace AgroBase.Services.Operadores
|
||||||
("dir_angulo_direita_sinal", 1),
|
("dir_angulo_direita_sinal", 1),
|
||||||
("imu_dir_roll_min_aux", 6.0),
|
("imu_dir_roll_min_aux", 6.0),
|
||||||
("imu_dir_bias_attention_deg", 4.0),
|
("imu_dir_bias_attention_deg", 4.0),
|
||||||
("imu_dir_bias_risk_deg", 9.0)
|
("imu_dir_bias_risk_deg", 9.0),
|
||||||
|
("imu_roll_offset_deg", VariaveisEquipamento.Parametros.imu_roll_offset_deg),
|
||||||
|
("imu_pitch_offset_deg", VariaveisEquipamento.Parametros.imu_pitch_offset_deg)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"Carregado":true,"serial_number":"01","base_ip":"20.1.0.2","base_porta_caminho":5000,"base_porta_ervas":5002,"comunicacao_interface":"AG_Hallow","rover_ip":"20.1.0.10","rover_gateway":"20.1.0.1","rover_submask":"255.255.255.0","camera_caminhho_id":"14442C10C143E2D600","camera_ervas_id":["194430108133AC2F00"],"can_interface":"AG_Can","can_ip_pc":"192.168.0.5","can_ip_modulo":"192.168.0.7","can_porta":8235,"can_gateway":"192.168.0.1","can_submask":"255.255.255.0"}
|
{"Carregado":true,"serial_number":"01","base_ip":"20.1.0.2","base_porta_caminho":5000,"base_porta_ervas":5002,"comunicacao_interface":"AG_Hallow","rover_ip":"20.1.0.10","rover_gateway":"20.1.0.1","rover_submask":"255.255.255.0","camera_caminhho_id":"14442C10C143E2D600","camera_ervas_id":["194430108133AC2F00"],"can_interface":"AG_Can","can_ip_pc":"192.168.0.5","can_ip_modulo":"192.168.0.7","can_porta":8235,"can_gateway":"192.168.0.1","can_submask":"255.255.255.0","imu_roll_offset_deg":0.0,"imu_pitch_offset_deg":0.0}
|
||||||
|
|
@ -159,14 +159,27 @@ class ModuloDirecional(ModuloDiagnosticoBase):
|
||||||
condicoes = []
|
condicoes = []
|
||||||
|
|
||||||
if not conectado:
|
if not conectado:
|
||||||
|
condicoes = []
|
||||||
|
|
||||||
|
if comandar:
|
||||||
|
condicoes.append({
|
||||||
|
"valor": 0,
|
||||||
|
"severidade": 100,
|
||||||
|
"descricao": (
|
||||||
|
f"Driver direcional {endereco_str} desconectado "
|
||||||
|
"enquanto está em uso"
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": endereco_str,
|
"id": endereco_str,
|
||||||
"label": mod_id,
|
"label": mod_id,
|
||||||
"status": StatusModulo.DESCONECTADO.value,
|
"status": StatusModulo.DESCONECTADO.value,
|
||||||
"saude": 0,
|
"saude": 0,
|
||||||
"motivos": ["Desconectado"],
|
"motivos": ["Desconectado"],
|
||||||
"condicoes_operacionais": [],
|
"condicoes_operacionais": condicoes,
|
||||||
"em_uso": comandar,
|
"em_uso": comandar,
|
||||||
|
"referenciando": referenciando,
|
||||||
"componentes": {
|
"componentes": {
|
||||||
"conectividade": 0,
|
"conectividade": 0,
|
||||||
"feedback": 0,
|
"feedback": 0,
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
angulo_risk_factor=0.75,
|
angulo_risk_factor=0.75,
|
||||||
angulo_emergency_factor=1.20,
|
angulo_emergency_factor=1.20,
|
||||||
limites_refresh_s=1.0,
|
limites_refresh_s=1.0,
|
||||||
|
offsets_refresh_s=1.0,
|
||||||
|
|
||||||
# Taxas continuam independentes do ângulo máximo.
|
# Taxas continuam independentes do ângulo máximo.
|
||||||
roll_rate_attention_dps=25.0,
|
roll_rate_attention_dps=25.0,
|
||||||
|
|
@ -87,6 +88,13 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
self.limites_refresh_s = max(float(limites_refresh_s), 0.25)
|
self.limites_refresh_s = max(float(limites_refresh_s), 0.25)
|
||||||
self._ultimo_refresh_limites = 0.0
|
self._ultimo_refresh_limites = 0.0
|
||||||
|
|
||||||
|
# Offsets persistentes de atitude, definidos no equipamento.
|
||||||
|
# Convenção: valor corrigido = valor medido - offset.
|
||||||
|
self.offsets_refresh_s = max(float(offsets_refresh_s), 0.25)
|
||||||
|
self._ultimo_refresh_offsets = 0.0
|
||||||
|
self.roll_offset_deg = 0.0
|
||||||
|
self.pitch_offset_deg = 0.0
|
||||||
|
|
||||||
# Valores iniciais seguros. Serão substituídos pelos dados do Redis.
|
# Valores iniciais seguros. Serão substituídos pelos dados do Redis.
|
||||||
self.roll_max_deg = self.roll_max_default_deg
|
self.roll_max_deg = self.roll_max_default_deg
|
||||||
self.pitch_max_deg = self.pitch_max_default_deg
|
self.pitch_max_deg = self.pitch_max_default_deg
|
||||||
|
|
@ -154,6 +162,10 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
self.max_skew_imu_s = float(max_skew_imu_s)
|
self.max_skew_imu_s = float(max_skew_imu_s)
|
||||||
|
|
||||||
|
# Carrega configuração persistente antes da primeira atitude publicada.
|
||||||
|
self._atualizar_limites_equipamento(forcar=True)
|
||||||
|
self._atualizar_offsets_equipamento(forcar=True)
|
||||||
|
|
||||||
self.thread = threading.Thread(
|
self.thread = threading.Thread(
|
||||||
target=self.imu_task_loop,
|
target=self.imu_task_loop,
|
||||||
name="CameraIMUFinalWorker",
|
name="CameraIMUFinalWorker",
|
||||||
|
|
@ -789,6 +801,77 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
f"{self.pitch_emergency_deg:.1f}°)"
|
f"{self.pitch_emergency_deg:.1f}°)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _validar_offset_angulo(self, valor, valor_atual):
|
||||||
|
"""Valida offset persistente de roll/pitch vindo do equipamento."""
|
||||||
|
try:
|
||||||
|
valor = float(valor)
|
||||||
|
if not math.isfinite(valor):
|
||||||
|
return float(valor_atual)
|
||||||
|
# Guard rail: offset mecânico maior que isso indica configuração errada.
|
||||||
|
if valor < -45.0 or valor > 45.0:
|
||||||
|
return float(valor_atual)
|
||||||
|
return valor
|
||||||
|
except Exception:
|
||||||
|
return float(valor_atual)
|
||||||
|
|
||||||
|
def _atualizar_offsets_equipamento(self, forcar=False):
|
||||||
|
"""
|
||||||
|
Atualiza os offsets persistentes da referência mecânica do rover.
|
||||||
|
|
||||||
|
Campos esperados em equipamento:
|
||||||
|
imu_roll_offset_deg
|
||||||
|
imu_pitch_offset_deg
|
||||||
|
|
||||||
|
Convenção:
|
||||||
|
roll_corrigido = roll_fundido - imu_roll_offset_deg
|
||||||
|
pitch_corrigido = pitch_fundido - imu_pitch_offset_deg
|
||||||
|
"""
|
||||||
|
agora = time.perf_counter()
|
||||||
|
if (
|
||||||
|
not forcar
|
||||||
|
and (agora - self._ultimo_refresh_offsets) < self.offsets_refresh_s
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
self._ultimo_refresh_offsets = agora
|
||||||
|
equipamento = ContextoGlobalRedis.get_equipamento() or {}
|
||||||
|
|
||||||
|
roll_anterior = self.roll_offset_deg
|
||||||
|
pitch_anterior = self.pitch_offset_deg
|
||||||
|
|
||||||
|
self.roll_offset_deg = self._validar_offset_angulo(
|
||||||
|
equipamento.get("imu_roll_offset_deg", 0.0),
|
||||||
|
self.roll_offset_deg,
|
||||||
|
)
|
||||||
|
self.pitch_offset_deg = self._validar_offset_angulo(
|
||||||
|
equipamento.get("imu_pitch_offset_deg", 0.0),
|
||||||
|
self.pitch_offset_deg,
|
||||||
|
)
|
||||||
|
|
||||||
|
mudou = (
|
||||||
|
abs(self.roll_offset_deg - roll_anterior) > 1e-6
|
||||||
|
or abs(self.pitch_offset_deg - pitch_anterior) > 1e-6
|
||||||
|
)
|
||||||
|
if mudou:
|
||||||
|
# Não deixa o EMA carregar a referência antiga depois de recalibrar.
|
||||||
|
self._resetar_filtro_fusao()
|
||||||
|
self.mostrar_log(
|
||||||
|
"Offsets IMU atualizados | "
|
||||||
|
f"roll={roll_anterior:.3f}°→{self.roll_offset_deg:.3f}° | "
|
||||||
|
f"pitch={pitch_anterior:.3f}°→{self.pitch_offset_deg:.3f}°"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _aplicar_offsets_fusao(self, fusao):
|
||||||
|
"""Aplica a referência mecânica depois da fusão e antes do filtro/risco."""
|
||||||
|
if fusao is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
corrigida = dict(fusao)
|
||||||
|
corrigida["roll"] = float(fusao.get("roll", 0.0)) - self.roll_offset_deg
|
||||||
|
corrigida["pitch"] = float(fusao.get("pitch", 0.0)) - self.pitch_offset_deg
|
||||||
|
# Offset constante não altera taxas angulares nem yaw.
|
||||||
|
return corrigida
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Fusão
|
# Fusão
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
@ -1384,6 +1467,16 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
"roll_max_configurado_deg": round(self.roll_max_deg, 3),
|
"roll_max_configurado_deg": round(self.roll_max_deg, 3),
|
||||||
"pitch_max_configurado_deg": round(self.pitch_max_deg, 3),
|
"pitch_max_configurado_deg": round(self.pitch_max_deg, 3),
|
||||||
|
|
||||||
|
# Referência mecânica persistente aplicada à atitude final.
|
||||||
|
"offsets_imu": {
|
||||||
|
"roll_deg": round(self.roll_offset_deg, 4),
|
||||||
|
"pitch_deg": round(self.pitch_offset_deg, 4),
|
||||||
|
"convencao": "corrigido=medido-offset",
|
||||||
|
"origem": "equipamento",
|
||||||
|
},
|
||||||
|
"roll_offset_deg": round(self.roll_offset_deg, 4),
|
||||||
|
"pitch_offset_deg": round(self.pitch_offset_deg, 4),
|
||||||
|
|
||||||
"roll_attention_deg": round(self.roll_attention_deg, 3),
|
"roll_attention_deg": round(self.roll_attention_deg, 3),
|
||||||
"roll_risk_deg": round(self.roll_risk_deg, 3),
|
"roll_risk_deg": round(self.roll_risk_deg, 3),
|
||||||
"roll_critical_deg": round(self.roll_critical_deg, 3),
|
"roll_critical_deg": round(self.roll_critical_deg, 3),
|
||||||
|
|
@ -1487,6 +1580,7 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
agora = time.time()
|
agora = time.time()
|
||||||
|
|
||||||
self._atualizar_limites_equipamento()
|
self._atualizar_limites_equipamento()
|
||||||
|
self._atualizar_offsets_equipamento()
|
||||||
|
|
||||||
fontes, ignoradas, desabilitadas = self._ler_fontes_imu()
|
fontes, ignoradas, desabilitadas = self._ler_fontes_imu()
|
||||||
|
|
||||||
|
|
@ -1520,6 +1614,11 @@ class CameraIMU(ModuloDiagnosticoBase):
|
||||||
|
|
||||||
fusao = self._fundir_fontes(fontes, sensores_agree=divergencia["sensors_agree"])
|
fusao = self._fundir_fontes(fontes, sensores_agree=divergencia["sensors_agree"])
|
||||||
|
|
||||||
|
# Corrige a atitude final pela referência mecânica persistente do rover.
|
||||||
|
# A divergência entre sensores continua sendo avaliada antes do offset,
|
||||||
|
# pois um offset global não deve mascarar desacordo entre IMUs.
|
||||||
|
fusao = self._aplicar_offsets_fusao(fusao)
|
||||||
|
|
||||||
# Atualiza o filtro antes de avaliar o risco.
|
# Atualiza o filtro antes de avaliar o risco.
|
||||||
atitude_filtrada = self._atualizar_filtro_fusao(fusao)
|
atitude_filtrada = self._atualizar_filtro_fusao(fusao)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -318,13 +318,31 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
|
||||||
)
|
)
|
||||||
|
|
||||||
if not conectado and estado_desconectado["nivel"] == "confirmado":
|
if not conectado and estado_desconectado["nivel"] == "confirmado":
|
||||||
|
condicoes_desconectado = []
|
||||||
|
|
||||||
|
if comandar:
|
||||||
|
condicoes_desconectado.append({
|
||||||
|
"label": "Conexão OID",
|
||||||
|
"valor": estado_desconectado["duracao_s"],
|
||||||
|
"severidade": 100,
|
||||||
|
"descricao": (
|
||||||
|
f"Driver de movimentação {mod_id} desconectado "
|
||||||
|
"enquanto está em uso"
|
||||||
|
),
|
||||||
|
"acoes": [
|
||||||
|
"Verificar comunicação CAN com o OID.",
|
||||||
|
"Verificar alimentação do driver.",
|
||||||
|
"Inspecionar conectores e cabeamento do motor.",
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": endereco_str,
|
"id": endereco_str,
|
||||||
"label": mod_id,
|
"label": mod_id,
|
||||||
"status": StatusModulo.DESCONECTADO.value,
|
"status": StatusModulo.DESCONECTADO.value,
|
||||||
"saude": 0,
|
"saude": 0,
|
||||||
"motivos": ["Desconectado"],
|
"motivos": ["Desconectado"],
|
||||||
"condicoes_operacionais": [],
|
"condicoes_operacionais": condicoes_desconectado,
|
||||||
"em_uso": comandar,
|
"em_uso": comandar,
|
||||||
"conectado_efetivo": False,
|
"conectado_efetivo": False,
|
||||||
"componentes": self._componentes_zero(),
|
"componentes": self._componentes_zero(),
|
||||||
|
|
|
||||||
|
|
@ -713,36 +713,62 @@ class ContextoGlobalRedis:
|
||||||
ultimo_publicado == cls._int(ultimo_esperado, -999999)
|
ultimo_publicado == cls._int(ultimo_esperado, -999999)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ack_mapa_exato = (
|
||||||
|
status_mapa == "aplicado"
|
||||||
|
and revisao_solicitada > 0
|
||||||
|
and health_rev == revisao_solicitada
|
||||||
|
and manager_rev == revisao_solicitada
|
||||||
|
and command_solicitado != ""
|
||||||
|
and health_cmd == command_solicitado
|
||||||
|
and manager_cmd == command_solicitado
|
||||||
|
and health_status == "aplicado"
|
||||||
|
and manager_status == "aplicado"
|
||||||
|
and qtd_esperada > 0
|
||||||
|
and qtd_publicada == qtd_esperada
|
||||||
|
and manager_qtd == qtd_esperada
|
||||||
|
and indices_extremos_ok
|
||||||
|
)
|
||||||
|
|
||||||
mapa_mpc_ok = (
|
mapa_mpc_ok = (
|
||||||
not requer_confirmacao_mapa_mpc
|
not requer_confirmacao_mapa_mpc
|
||||||
or (
|
or (
|
||||||
not mapa_barreira_critica
|
ack_mapa_exato
|
||||||
and mapa_confirmado_flag
|
and mapa_confirmado_flag
|
||||||
and status_mapa == "aplicado"
|
and not mapa_barreira_critica
|
||||||
and revisao_solicitada > 0
|
|
||||||
and health_rev == revisao_solicitada
|
|
||||||
and manager_rev == revisao_solicitada
|
|
||||||
and command_solicitado != ""
|
|
||||||
and health_cmd == command_solicitado
|
|
||||||
and manager_cmd == command_solicitado
|
|
||||||
and health_status == "aplicado"
|
|
||||||
and manager_status == "aplicado"
|
|
||||||
and qtd_esperada > 0
|
|
||||||
and qtd_publicada == qtd_esperada
|
|
||||||
and manager_qtd == qtd_esperada
|
|
||||||
and indices_extremos_ok
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not mapa_mpc_ok:
|
if not mapa_mpc_ok:
|
||||||
motivos.append(
|
motivos.append(
|
||||||
"Mapa/MPC ainda não confirmado: "
|
"Mapa/MPC ainda não confirmado: "
|
||||||
f"rev solicitada/health/manager={revisao_solicitada}/{health_rev}/{manager_rev}; "
|
f"confirmado={mapa_confirmado_flag}; "
|
||||||
f"cmd solicitado/health/manager={command_solicitado}/{health_cmd}/{manager_cmd}; "
|
f"barreira={mapa_barreira_critica}; "
|
||||||
f"pontos publicados/esperados/manager={qtd_publicada}/{qtd_esperada}/{manager_qtd}; "
|
f"rev solicitada/health/manager="
|
||||||
|
f"{revisao_solicitada}/{health_rev}/{manager_rev}; "
|
||||||
|
f"cmd solicitado/health/manager="
|
||||||
|
f"{command_solicitado}/{health_cmd}/{manager_cmd}; "
|
||||||
|
f"pontos publicados/esperados/manager="
|
||||||
|
f"{qtd_publicada}/{qtd_esperada}/{manager_qtd}; "
|
||||||
|
f"indices publicado={primeiro_publicado}->{ultimo_publicado}, "
|
||||||
|
f"esperado={primeiro_esperado}->{ultimo_esperado}, "
|
||||||
|
f"ok={indices_extremos_ok}; "
|
||||||
f"status={status_mapa}/{health_status}/{manager_status}"
|
f"status={status_mapa}/{health_status}/{manager_status}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ack_mapa_exato and not mapa_confirmado_flag:
|
||||||
|
mostrar_log(
|
||||||
|
"[MAPSYNC/INCONSISTENCIA] "
|
||||||
|
f"ACK exato confirmado, mas mapa_confirmado=false. "
|
||||||
|
f"rev={revisao_solicitada}; cmd={command_solicitado}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ack_mapa_exato and mapa_barreira_critica:
|
||||||
|
mostrar_log(
|
||||||
|
"[MAPSYNC/INCONSISTENCIA] "
|
||||||
|
f"ACK exato confirmado, mas mapa_barreira_critica permanece ativa. "
|
||||||
|
f"rev={revisao_solicitada}; cmd={command_solicitado}"
|
||||||
|
)
|
||||||
|
|
||||||
detalhes_regras["mapa_mpc"] = {
|
detalhes_regras["mapa_mpc"] = {
|
||||||
"requer_confirmacao": requer_confirmacao_mapa_mpc,
|
"requer_confirmacao": requer_confirmacao_mapa_mpc,
|
||||||
"ok": mapa_mpc_ok,
|
"ok": mapa_mpc_ok,
|
||||||
|
|
|
||||||
|
|
@ -32,34 +32,60 @@ namespace OperationControl.ViewModels.Views.Operacao.Monitoramento
|
||||||
get => _habilitado;
|
get => _habilitado;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (_habilitado != value)
|
if (_habilitado == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var r =
|
||||||
|
VariaveisControleOperacao.RoverEmFoco;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ao abandonar o manual, primeiro neutralizamos tudo
|
||||||
|
* enquanto o rover ainda reconhece autoridade manual.
|
||||||
|
*/
|
||||||
|
if (!value)
|
||||||
{
|
{
|
||||||
_habilitado = value;
|
LimparTeclas();
|
||||||
OnPropertyChanged(nameof(StatusTexto));
|
EnviarNeutros();
|
||||||
OnPropertyChanged(nameof(TextoAjuda));
|
}
|
||||||
OnPropertyChanged(nameof(StatusBrush));
|
|
||||||
|
|
||||||
var r = VariaveisControleOperacao.RoverEmFoco;
|
_habilitado = value;
|
||||||
if (r != null)
|
|
||||||
{
|
|
||||||
r.Controle.ControleManualAcionado = value;
|
|
||||||
r.Controle.MovimentoAutomatico = value ? false : r.ControleAutomatico;
|
|
||||||
r.Controle.DirecionalAutomatico = value ? false : r.ControleAutomatico;
|
|
||||||
VariaveisControleOperacao.EnviarParametrosOperacao(new AgroBase.Models.Operacoes.OperacaoParametrosModel()
|
|
||||||
{
|
|
||||||
Controle = VariaveisControleOperacao.RoverEmFoco?.Controle
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value)
|
OnPropertyChanged(nameof(StatusTexto));
|
||||||
{
|
OnPropertyChanged(nameof(TextoAjuda));
|
||||||
LimparTeclas();
|
OnPropertyChanged(nameof(StatusBrush));
|
||||||
EnviarNeutros();
|
|
||||||
}
|
if (r != null)
|
||||||
else
|
{
|
||||||
{
|
r.Controle.ControleManualAcionado =
|
||||||
VariaveisControleOperacao.EnviarComandoIniciarUDP(VariaveisControleOperacao.RoverEmFoco.IP, true);
|
value;
|
||||||
}
|
|
||||||
|
r.Controle.MovimentoAutomatico =
|
||||||
|
value
|
||||||
|
? false
|
||||||
|
: r.ControleAutomatico;
|
||||||
|
|
||||||
|
r.Controle.DirecionalAutomatico =
|
||||||
|
value
|
||||||
|
? false
|
||||||
|
: r.ControleAutomatico;
|
||||||
|
|
||||||
|
VariaveisControleOperacao
|
||||||
|
.EnviarParametrosOperacao(
|
||||||
|
new AgroBase.Models.Operacoes
|
||||||
|
.OperacaoParametrosModel()
|
||||||
|
{
|
||||||
|
Controle = r.Controle
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value && r != null)
|
||||||
|
{
|
||||||
|
VariaveisControleOperacao
|
||||||
|
.EnviarComandoIniciarUDP(
|
||||||
|
r.IP,
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -653,6 +653,27 @@ namespace OperationControl.ViewModels
|
||||||
return "A operação não foi liberada pelo rover.";
|
return "A operação não foi liberada pelo rover.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AdicionarEventoTrajetoria(
|
||||||
|
string descricao,
|
||||||
|
int prioridade = 700,
|
||||||
|
StatusModulo status = StatusModulo.Alerta)
|
||||||
|
{
|
||||||
|
descricao = NormalizarTexto(descricao);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(descricao))
|
||||||
|
return;
|
||||||
|
|
||||||
|
eventos.Add(new MotivoTopBarModel
|
||||||
|
{
|
||||||
|
Prioridade = prioridade,
|
||||||
|
Fonte = "Trajetoria",
|
||||||
|
Titulo = "TRAJETÓRIA BLOQUEADA",
|
||||||
|
Descricao = descricao,
|
||||||
|
StatusVisual = status,
|
||||||
|
Modulo = T_Code.Trj
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 0) Comunicação
|
// 0) Comunicação
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -752,22 +773,22 @@ namespace OperationControl.ViewModels
|
||||||
// Parâmetro obrigatório ausente
|
// Parâmetro obrigatório ausente
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
if (
|
//if (
|
||||||
obj.Operacao != null &&
|
// obj.Operacao != null &&
|
||||||
!obj.Operacao.Liberada &&
|
// !obj.Operacao.Liberada &&
|
||||||
obj.Operacao.Pausa != true &&
|
// obj.Operacao.Pausa != true &&
|
||||||
obj.Operacao.Emergencia != true
|
// obj.Operacao.Emergencia != true
|
||||||
)
|
//)
|
||||||
{
|
//{
|
||||||
eventos.Add(new MotivoTopBarModel
|
// eventos.Add(new MotivoTopBarModel
|
||||||
{
|
// {
|
||||||
Prioridade = 850,
|
// Prioridade = 850,
|
||||||
Fonte = "Operacao",
|
// Fonte = "Operacao",
|
||||||
Titulo = "OPERAÇÃO BLOQUEADA",
|
// Titulo = "OPERAÇÃO BLOQUEADA",
|
||||||
Descricao = MotivoOperacaoAtual(),
|
// Descricao = MotivoOperacaoAtual(),
|
||||||
StatusVisual = StatusModulo.Falha
|
// StatusVisual = StatusModulo.Falha
|
||||||
});
|
// });
|
||||||
}
|
//}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 5) Controle
|
// 5) Controle
|
||||||
|
|
@ -809,14 +830,42 @@ namespace OperationControl.ViewModels
|
||||||
|
|
||||||
foreach (var motivo in motivosTrajetoria)
|
foreach (var motivo in motivosTrajetoria)
|
||||||
{
|
{
|
||||||
eventos.Add(new MotivoTopBarModel
|
AdicionarEventoTrajetoria(motivo);
|
||||||
|
}
|
||||||
|
|
||||||
|
var impedimentoOperacao = obj.Operacao?.ImpedimentoErro ?? "";
|
||||||
|
|
||||||
|
var linhasImpedimento =
|
||||||
|
impedimentoOperacao
|
||||||
|
.Split(
|
||||||
|
new[] { "\r\n", "\n", "\r" },
|
||||||
|
StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(x => x.Trim())
|
||||||
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var linha in linhasImpedimento)
|
||||||
|
{
|
||||||
|
bool relacionadoTrajetoria =
|
||||||
|
linha.Contains(
|
||||||
|
"Mapa/MPC",
|
||||||
|
StringComparison.OrdinalIgnoreCase)
|
||||||
|
||
|
||||||
|
linha.Contains(
|
||||||
|
"trajetória",
|
||||||
|
StringComparison.OrdinalIgnoreCase)
|
||||||
|
||
|
||||||
|
linha.Contains(
|
||||||
|
"trajetoria",
|
||||||
|
StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (relacionadoTrajetoria)
|
||||||
{
|
{
|
||||||
Prioridade = 700,
|
AdicionarEventoTrajetoria(
|
||||||
Fonte = "Trajetoria",
|
linha,
|
||||||
Titulo = "TRAJETÓRIA BLOQUEADA",
|
prioridade: 710);
|
||||||
Descricao = motivo,
|
}
|
||||||
StatusVisual = StatusModulo.Alerta
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue