3353 lines
113 KiB
C#
3353 lines
113 KiB
C#
using AgroBase.Models;
|
|
using AgroBase.Models.Operacoes;
|
|
using AgroBase.Services;
|
|
using Mapsui;
|
|
using Mapsui.Layers;
|
|
using NetTopologySuite.Geometries;
|
|
using Newtonsoft.Json.Linq;
|
|
using OperationControl.Controls;
|
|
using OperationControl.Helpers;
|
|
using OperationControl.Mapas;
|
|
using OperationControl.Mapas.Editing;
|
|
using OperationControl.Mapas.Interaction;
|
|
using OperationControl.Mapas.Models;
|
|
using OperationControl.Mapas.Rendering;
|
|
using OperationControl.Mapas.Services;
|
|
using OperationControl.Models;
|
|
using OperationControl.ViewModels.Views.Operacao;
|
|
using OperationControl.Views;
|
|
using OperationControl.Views.Operacao;
|
|
using System.ComponentModel;
|
|
using System.Windows;
|
|
using System.Windows.Input;
|
|
using static AgroBase.Models.Enums;
|
|
using static OperationControl.Services.BaseFixService;
|
|
using static OperationControl.Services.GpsService;
|
|
using MessageBox = System.Windows.MessageBox;
|
|
|
|
namespace OperationControl.ViewModels
|
|
{
|
|
public class DockWindowViewModel : INotifyPropertyChanged
|
|
{
|
|
private readonly ConfigConexaoView _viewConfig;
|
|
|
|
private readonly PreparacaoMapaView _viewPreparacaoMapa;
|
|
public readonly PreparacaoMapaTopView _viewPreparacaoMapaTop;
|
|
public readonly PreparacaoMapaBottomView _viewPreparacaoMapaBottom;
|
|
private readonly PreparacaoMapaStatusView _viewPreparacaoMapaStatus;
|
|
|
|
private readonly OperacaoTopView _viewOperacaoTop;
|
|
public readonly OperacaoCenterView _viewOperacaoCenter;
|
|
public readonly OperacaoLeftView _viewOperacaoLeft;
|
|
public readonly OperacaoRightView _viewOperacaoRight;
|
|
public readonly OperacaoBottomView _viewOperacaoBottom;
|
|
|
|
public OperacaoBottomViewModel BottomVM => _viewOperacaoBottom?._vm;
|
|
|
|
private DockStep _currentStep;
|
|
|
|
public DockWindowViewModel()
|
|
{
|
|
// Views
|
|
_viewConfig = new ConfigConexaoView();
|
|
_viewConfig._vm.TudoOkChanged += OnTudoOkChanged;
|
|
|
|
_viewPreparacaoMapa = new PreparacaoMapaView();
|
|
|
|
_viewPreparacaoMapaTop = new PreparacaoMapaTopView();
|
|
|
|
_viewPreparacaoMapaBottom = new PreparacaoMapaBottomView();
|
|
_viewPreparacaoMapaBottom._vm.CarregarMapaSolicitado += OnCarregarMapaSolicitado;
|
|
_viewPreparacaoMapaBottom._vm.EditarMapaSolicitado += OnEditarMapaPreparacaoSolicitado;
|
|
_viewPreparacaoMapaBottom._vm.FixarBaseSolicitado += OnFixarBaseSolicitado;
|
|
_viewPreparacaoMapaBottom._vm.AplicarOffsetSolicitado += OnAplicarOffsetSolicitado;
|
|
_viewPreparacaoMapaBottom._vm.UsarGnssMovelSolicitado += OnUsarGnssMovelSolicitado;
|
|
_viewPreparacaoMapaBottom._vm.ContinuarSolicitado += OnContinuarPreparacaoSolicitado;
|
|
_viewPreparacaoMapaBottom._vm.VoltarSolicitado += OnVoltarPreparacaoSolicitado;
|
|
|
|
_viewPreparacaoMapaStatus = new PreparacaoMapaStatusView
|
|
{
|
|
DataContext = _viewPreparacaoMapaBottom._vm
|
|
};
|
|
|
|
_viewOperacaoTop = new OperacaoTopView();
|
|
|
|
_viewOperacaoCenter = new OperacaoCenterView();
|
|
_viewOperacaoCenter._vm.RoverSelecionadoChanged += OnRoverSelecionadoChanged;
|
|
|
|
/*
|
|
* Salvar arquivo é uma intenção de UI.
|
|
* O cockpit só dispara o evento, o Dock abre o diálogo
|
|
* e o Controller continua dono do documento e da exportação.
|
|
*/
|
|
_viewOperacaoCenter
|
|
.Mapa
|
|
.EditorOverlay
|
|
.SalvarMapaEditadoSolicitado +=
|
|
OnSalvarMapaEditadoSolicitado;
|
|
|
|
/*
|
|
* O mesmo cockpit também existe no mapa da preparação.
|
|
* Assim o botão "Salvar versão editada" funciona antes
|
|
* mesmo de existir Rover selecionado/conectado.
|
|
*/
|
|
_viewPreparacaoMapa
|
|
.MapaPreparacao
|
|
.EditorOverlay
|
|
.SalvarMapaEditadoSolicitado +=
|
|
OnSalvarMapaEditadoSolicitado;
|
|
|
|
_viewOperacaoLeft = new OperacaoLeftView();
|
|
|
|
_viewOperacaoRight = new OperacaoRightView();
|
|
_viewOperacaoRight._vm.PropertyChanged += OperacaoRightVm_PropertyChanged;
|
|
_viewOperacaoRight._vm.RoverSelecionadoChanged += OnRoverSelecionadoChanged;
|
|
_viewOperacaoRight._vm.SecaoSelecionadaChanged += OnSecaoSelecionadaChanged;
|
|
_viewOperacaoRight.areaParametrizacao._vm.LerParametrosSolicitado += OnLerParametrosSolicitado;
|
|
_viewOperacaoRight.areaParametrizacao._vm.SalvarParametrosSolicitado += OnSalvarParametrosSolicitado;
|
|
_viewOperacaoRight.areaParametrizacao._vm.ModoOperacaoAlterado += OnModoOperacaoParametrizacaoAlterado;
|
|
_viewOperacaoRight.areaParametrizacao._vm.EditarMapaSolicitado += OnEditarMapaSolicitado;
|
|
_viewOperacaoRight.areaParametrizacao._vm.CancelarEdicaoMapaSolicitado += OnCancelarEdicaoMapaSolicitado;
|
|
|
|
_viewOperacaoBottom = new OperacaoBottomView();
|
|
_viewOperacaoBottom._vm.SolicitarAbrirDiagnostico += AbrirDiagnosticoPeloAlerta;
|
|
|
|
// Subsistema de mapas
|
|
_mapas.LogGerado += OnMapaLogGerado;
|
|
_mapas.EntidadeClicada += OnMapaEntidadeClicada;
|
|
_mapas.ValidacaoEdicaoFalhou += OnMapaValidacaoEdicaoFalhou;
|
|
_mapas.EstadoMapaAlterado += OnEstadoMapaAlterado;
|
|
_mapas.EstadoEdicaoAlterado += OnEstadoMapaAlterado;
|
|
|
|
// Commands
|
|
BackCommand = new RelayCommand(_ => GoBack(), _ => CanGoBack);
|
|
NextCommand = new RelayCommand(_ => GoNext(), _ => CanGoNext);
|
|
|
|
// Estado inicial
|
|
ConfigConexaoOk = _viewConfig._vm.TudoOk;
|
|
CurrentStep = DockStep.ConfigConexao;
|
|
}
|
|
|
|
private void OnTudoOkChanged(bool ok)
|
|
{
|
|
ConfigConexaoOk = ok;
|
|
|
|
OnPropertyChanged(nameof(CanGoNext));
|
|
CommandManager.InvalidateRequerySuggested();
|
|
}
|
|
|
|
// ====== ETAPA ATUAL ======
|
|
public ModoOperacao ModoOperacaoAtual => _viewConfig._vm.TipoOperacaoSelecionado;
|
|
|
|
public DockStep CurrentStep
|
|
{
|
|
get => _currentStep;
|
|
set
|
|
{
|
|
if (_currentStep != value)
|
|
{
|
|
_currentStep = value;
|
|
OnPropertyChanged(nameof(CurrentStep));
|
|
OnPropertyChanged(nameof(CanGoBack));
|
|
OnPropertyChanged(nameof(CanGoNext));
|
|
OnPropertyChanged(nameof(MostrarPainelEsquerdo));
|
|
OnPropertyChanged(nameof(MostrarBotaoVoltarEsquerda));
|
|
OnPropertyChanged(nameof(RightPanelWidth));
|
|
OnPropertyChanged(nameof(LeftPanelWidth));
|
|
|
|
AtualizarConteudo();
|
|
CommandManager.InvalidateRequerySuggested();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ====== ESTADO ======
|
|
private bool _configConexaoOk;
|
|
public bool ConfigConexaoOk
|
|
{
|
|
get => _configConexaoOk;
|
|
set
|
|
{
|
|
if (_configConexaoOk != value)
|
|
{
|
|
_configConexaoOk = value;
|
|
OnPropertyChanged(nameof(ConfigConexaoOk));
|
|
OnPropertyChanged(nameof(CanGoNext));
|
|
CommandManager.InvalidateRequerySuggested();
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool _mapaCarregado;
|
|
public bool MapaCarregado
|
|
{
|
|
get =>
|
|
_mapaCarregado;
|
|
|
|
set
|
|
{
|
|
if (
|
|
_mapaCarregado ==
|
|
value
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
_mapaCarregado =
|
|
value;
|
|
|
|
|
|
OnPropertyChanged(
|
|
nameof(MapaCarregado)
|
|
);
|
|
|
|
OnPropertyChanged(
|
|
nameof(CanGoNext)
|
|
);
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.DefinirMapaCarregado(
|
|
value
|
|
);
|
|
|
|
|
|
CommandManager
|
|
.InvalidateRequerySuggested();
|
|
}
|
|
}
|
|
|
|
private bool _baseFixada = AppShell.Mock;
|
|
public bool BaseFixada
|
|
{
|
|
get => _baseFixada;
|
|
set
|
|
{
|
|
if (_baseFixada != value)
|
|
{
|
|
_baseFixada = value;
|
|
OnPropertyChanged(nameof(BaseFixada));
|
|
OnPropertyChanged(nameof(CanGoNext));
|
|
CommandManager.InvalidateRequerySuggested();
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool CanGoBack =>
|
|
CurrentStep != DockStep.ConfigConexao &&
|
|
!(
|
|
CurrentStep == DockStep.PreparacaoMapa &&
|
|
_mapas.EdicaoAtiva
|
|
);
|
|
|
|
public bool CanGoNext
|
|
{
|
|
get
|
|
{
|
|
return CurrentStep switch
|
|
{
|
|
DockStep.ConfigConexao =>
|
|
PodeAvancarConfigConexao(),
|
|
|
|
DockStep.PreparacaoMapa =>
|
|
MapaCarregado &&
|
|
!_mapas.EdicaoAtiva,
|
|
|
|
_ =>
|
|
false
|
|
};
|
|
}
|
|
}
|
|
private bool PodeAvancarConfigConexao()
|
|
{
|
|
return ModoOperacaoAtual switch
|
|
{
|
|
ModoOperacao.Automatico => _viewConfig._vm.RedeOk && _viewConfig._vm.GnssOk,
|
|
ModoOperacao.Manual => _viewConfig._vm.RedeOk,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
|
|
public bool MostrarPainelEsquerdo => CurrentStep == DockStep.ParametrizacaoRover;
|
|
public bool MostrarBotaoVoltarEsquerda =>
|
|
CurrentStep != DockStep.ParametrizacaoRover &&
|
|
CurrentStep != DockStep.PreparacaoMapa;
|
|
|
|
public GridLength LeftPanelWidth
|
|
{
|
|
get
|
|
{
|
|
return CurrentStep switch
|
|
{
|
|
/*
|
|
* A preparação agora possui sua própria
|
|
* navegação dentro do painel direito.
|
|
*/
|
|
DockStep.PreparacaoMapa =>
|
|
new GridLength(0),
|
|
|
|
|
|
DockStep.ParametrizacaoRover =>
|
|
new GridLength(280),
|
|
|
|
|
|
_ =>
|
|
new GridLength(60)
|
|
};
|
|
}
|
|
}
|
|
public GridLength RightPanelWidth
|
|
{
|
|
get
|
|
{
|
|
return CurrentStep switch
|
|
{
|
|
DockStep.PreparacaoMapa =>
|
|
new GridLength(360),
|
|
|
|
|
|
DockStep.ParametrizacaoRover =>
|
|
_viewOperacaoRight?._vm?.IsCollapsed == true
|
|
? new GridLength(44)
|
|
: new GridLength(320),
|
|
|
|
|
|
_ =>
|
|
new GridLength(60)
|
|
};
|
|
}
|
|
}
|
|
private void OperacaoRightVm_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == nameof(OperacaoRightViewModel.IsCollapsed))
|
|
{
|
|
OnPropertyChanged(nameof(RightPanelWidth));
|
|
}
|
|
}
|
|
|
|
// ====== CONTEÚDOS ======
|
|
private object _centerContent;
|
|
public object CenterContent
|
|
{
|
|
get => _centerContent;
|
|
set
|
|
{
|
|
_centerContent = value;
|
|
OnPropertyChanged(nameof(CenterContent));
|
|
}
|
|
}
|
|
|
|
private object _topContent;
|
|
public object TopContent
|
|
{
|
|
get => _topContent;
|
|
set
|
|
{
|
|
_topContent = value;
|
|
OnPropertyChanged(nameof(TopContent));
|
|
}
|
|
}
|
|
|
|
private object _bottomContent;
|
|
public object BottomContent
|
|
{
|
|
get => _bottomContent;
|
|
set
|
|
{
|
|
_bottomContent = value;
|
|
OnPropertyChanged(nameof(BottomContent));
|
|
}
|
|
}
|
|
private object _leftContent;
|
|
public object LeftContent
|
|
{
|
|
get => _leftContent;
|
|
set
|
|
{
|
|
_leftContent = value;
|
|
OnPropertyChanged(nameof(LeftContent));
|
|
}
|
|
}
|
|
private object _rightContent;
|
|
public object RightContent
|
|
{
|
|
get => _rightContent;
|
|
set
|
|
{
|
|
_rightContent = value;
|
|
OnPropertyChanged(nameof(RightContent));
|
|
}
|
|
}
|
|
|
|
// ====== COMMANDS ======
|
|
public ICommand BackCommand { get; }
|
|
public ICommand NextCommand { get; }
|
|
|
|
private void GoBack()
|
|
{
|
|
switch (CurrentStep)
|
|
{
|
|
case DockStep.PreparacaoMapa:
|
|
CurrentStep = DockStep.ConfigConexao;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void GoNext()
|
|
{
|
|
if (!CanGoNext) return;
|
|
|
|
switch (CurrentStep)
|
|
{
|
|
case DockStep.ConfigConexao:
|
|
if (ModoOperacaoAtual == ModoOperacao.Manual)
|
|
CurrentStep = DockStep.ParametrizacaoRover;
|
|
else
|
|
CurrentStep = DockStep.PreparacaoMapa;
|
|
break;
|
|
|
|
case DockStep.PreparacaoMapa:
|
|
CurrentStep = DockStep.ParametrizacaoRover;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private FrameworkElement CriarBotaoNextSimples()
|
|
{
|
|
return new System.Windows.Controls.Button
|
|
{
|
|
Content = "⮞",
|
|
FontSize = 24,
|
|
Width = 40,
|
|
Height = 40,
|
|
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
|
VerticalAlignment = VerticalAlignment.Center,
|
|
Command = NextCommand
|
|
};
|
|
}
|
|
|
|
private void AtualizarConteudo()
|
|
{
|
|
TopContent = null;
|
|
BottomContent = null;
|
|
LeftContent = null;
|
|
RightContent = null;
|
|
CenterContent = null;
|
|
|
|
switch (CurrentStep)
|
|
{
|
|
case DockStep.ConfigConexao:
|
|
CenterContent = _viewConfig;
|
|
RightContent = CriarBotaoNextSimples();
|
|
break;
|
|
|
|
case DockStep.PreparacaoMapa:
|
|
|
|
TopContent =
|
|
_viewPreparacaoMapaTop;
|
|
|
|
|
|
CenterContent =
|
|
_viewPreparacaoMapa;
|
|
|
|
|
|
/*
|
|
* Status enxuto na faixa inferior.
|
|
*/
|
|
BottomContent =
|
|
_viewPreparacaoMapaStatus;
|
|
|
|
|
|
/*
|
|
* Controles principais agora possuem
|
|
* uma lateral própria.
|
|
*/
|
|
RightContent =
|
|
_viewPreparacaoMapaBottom;
|
|
|
|
|
|
_mapas.PrepararMapa(
|
|
_viewPreparacaoMapa?
|
|
.MapaPreparacao,
|
|
|
|
modoInteracao:
|
|
MapaModoInteracao
|
|
.Visualizacao,
|
|
|
|
enquadrarDocumento:
|
|
true
|
|
);
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.DefinirMapaCarregado(
|
|
MapaCarregado
|
|
);
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.DefinirEdicaoMapaAtiva(
|
|
_mapas.EdicaoAtiva
|
|
);
|
|
|
|
SincronizarEstadoGnssPreparacao();
|
|
break;
|
|
|
|
case DockStep.ParametrizacaoRover:
|
|
TopContent = _viewOperacaoTop;
|
|
LeftContent = _viewOperacaoLeft;
|
|
CenterContent = _viewOperacaoCenter;
|
|
RightContent = _viewOperacaoRight;
|
|
BottomContent = _viewOperacaoBottom;
|
|
|
|
_mapas.PrepararMapa(
|
|
_viewOperacaoCenter?.Mapa,
|
|
modoInteracao:
|
|
ObterModoInteracaoMapaOperacao(),
|
|
enquadrarDocumento: true
|
|
);
|
|
|
|
AtualizarBarraSuperior(
|
|
StatusModulo.Conectado,
|
|
"BASE",
|
|
"VISÃO GERAL",
|
|
"Selecione um equipamento na lista à direita para configurá-lo",
|
|
"NORMAL",
|
|
"Aguardando"
|
|
);
|
|
break;
|
|
}
|
|
|
|
OnPropertyChanged(nameof(MostrarPainelEsquerdo));
|
|
OnPropertyChanged(nameof(MostrarBotaoVoltarEsquerda));
|
|
}
|
|
|
|
// ====== INotifyPropertyChanged ======
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
private void OnPropertyChanged(string nome)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nome));
|
|
}
|
|
|
|
public void AtualizarBarraSuperior(OperacaoParametrosModel rover)
|
|
{
|
|
var obj = rover?.DadosLeitura;
|
|
|
|
if (rover == null || obj == null)
|
|
return;
|
|
|
|
if (rover.RoverId != VariaveisControleOperacao.SelectedRoverId)
|
|
return;
|
|
|
|
var eventos = new List<MotivoTopBarModel>();
|
|
|
|
// ============================================================
|
|
// Helpers
|
|
// ============================================================
|
|
|
|
string NormalizarTexto(string texto)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(texto))
|
|
return null;
|
|
|
|
var partes = texto
|
|
.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(x => x?.Trim())
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
return partes.Count > 0
|
|
? string.Join(" • ", partes)
|
|
: null;
|
|
}
|
|
|
|
bool EhModuloMandatorio(T_Code modulo)
|
|
{
|
|
// A telemetria recebida do rover é a fonte principal.
|
|
// Só cai para a configuração local caso o snapshot não exista.
|
|
var modulosMandatorios =
|
|
obj.Operacao?.ModulosMandatorios
|
|
?? rover.ModulosMandatorios;
|
|
|
|
return modulosMandatorios?.Any(x =>
|
|
(x?.Dispositivo ?? T_Code.Vzo) == modulo &&
|
|
(x?.Utilizar ?? false) &&
|
|
(x?.Mandatorio ?? false)
|
|
) ?? false;
|
|
}
|
|
|
|
string MotivoOperacaoAtual()
|
|
{
|
|
// 1) Motivo autoritativo de NÃO liberação.
|
|
var motivo = NormalizarTexto(obj.Operacao?.ImpedimentoErro);
|
|
|
|
if (!string.IsNullOrWhiteSpace(motivo))
|
|
return motivo;
|
|
|
|
// 2) Motivo do estado/status atual.
|
|
motivo = NormalizarTexto(obj.Operacao?.MotivoStatusOperacao);
|
|
|
|
if (!string.IsNullOrWhiteSpace(motivo))
|
|
return motivo;
|
|
|
|
// 3) Último motivo de parada apenas como fallback.
|
|
motivo = NormalizarTexto(obj.Operacao?.UltimoMotivoParada);
|
|
|
|
if (!string.IsNullOrWhiteSpace(motivo))
|
|
return motivo;
|
|
|
|
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
|
|
// ============================================================
|
|
|
|
double tempoSemResposta =
|
|
(DateTime.Now - rover.UltimoContato).TotalSeconds;
|
|
|
|
bool semComunicacao =
|
|
!AppShell.Mock &&
|
|
tempoSemResposta >= VariaveisControleOperacao.TempoRoverVivo;
|
|
|
|
if (semComunicacao)
|
|
{
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 2000,
|
|
Fonte = "Comunicacao",
|
|
Titulo = "PERDA DE COMUNICAÇÃO COM O ROVER",
|
|
Descricao =
|
|
$"Sem resposta do rover há {tempoSemResposta:F1} s. " +
|
|
"Tentando reconectar automaticamente.",
|
|
StatusVisual = StatusModulo.Desconectado
|
|
});
|
|
}
|
|
|
|
// ============================================================
|
|
// 1) NPC / núcleo central
|
|
// ============================================================
|
|
|
|
bool todosModulosDesconectados =
|
|
obj.ModulosSaude?.Any() == true &&
|
|
obj.ModulosSaude.All(x =>
|
|
(x?.status ?? StatusModulo.Desconectado) ==
|
|
StatusModulo.Desconectado);
|
|
|
|
if (todosModulosDesconectados)
|
|
{
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 1100,
|
|
Fonte = "NCP",
|
|
Titulo = "NÚCLEO CENTRAL DE PROCESSAMENTO DESCONECTADO",
|
|
Descricao =
|
|
"O núcleo central de processamento foi desconectado. " +
|
|
"Tentando reconectar...",
|
|
StatusVisual = StatusModulo.Falha
|
|
});
|
|
}
|
|
|
|
// ============================================================
|
|
// 2) Emergência
|
|
// ============================================================
|
|
|
|
if (obj.Operacao?.Emergencia == true)
|
|
{
|
|
var motivo =
|
|
NormalizarTexto(obj.Operacao.MotivoStatusOperacao)
|
|
?? "Parada de emergência solicitada.";
|
|
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 1000,
|
|
Fonte = "Emergencia",
|
|
Titulo = "OPERAÇÃO BLOQUEADA POR EMERGÊNCIA",
|
|
Descricao = motivo,
|
|
StatusVisual = StatusModulo.Falha
|
|
});
|
|
}
|
|
|
|
// ============================================================
|
|
// 3) Pausa
|
|
// ============================================================
|
|
|
|
if (obj.Operacao?.Pausa == true)
|
|
{
|
|
var motivo =
|
|
NormalizarTexto(obj.Operacao.MotivoStatusOperacao)
|
|
?? "Pausa operacional solicitada.";
|
|
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 900,
|
|
Fonte = "Pausa",
|
|
Titulo = "OPERAÇÃO PAUSADA",
|
|
Descricao = motivo,
|
|
StatusVisual = StatusModulo.Alerta
|
|
});
|
|
}
|
|
|
|
// ============================================================
|
|
// 4) Operação não liberada
|
|
//
|
|
// Essa é a informação autoritativa vinda do rover.
|
|
// Exemplo:
|
|
// Sen: Desconectado
|
|
// Gps: Falha
|
|
// Parâmetro obrigatório ausente
|
|
// ============================================================
|
|
|
|
//if (
|
|
// obj.Operacao != null &&
|
|
// !obj.Operacao.Liberada &&
|
|
// obj.Operacao.Pausa != true &&
|
|
// obj.Operacao.Emergencia != true
|
|
//)
|
|
//{
|
|
// eventos.Add(new MotivoTopBarModel
|
|
// {
|
|
// Prioridade = 850,
|
|
// Fonte = "Operacao",
|
|
// Titulo = "OPERAÇÃO BLOQUEADA",
|
|
// Descricao = MotivoOperacaoAtual(),
|
|
// StatusVisual = StatusModulo.Falha
|
|
// });
|
|
//}
|
|
|
|
// ============================================================
|
|
// 5) Controle
|
|
// ============================================================
|
|
|
|
var motivosControle =
|
|
obj.Controle?.Motivos?
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Select(NormalizarTexto)
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Distinct()
|
|
.ToList()
|
|
?? new List<string>();
|
|
|
|
foreach (var motivo in motivosControle)
|
|
{
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 800,
|
|
Fonte = "Controle",
|
|
Titulo = "CONTROLE SEM LIBERAÇÃO",
|
|
Descricao = motivo,
|
|
StatusVisual = StatusModulo.Alerta
|
|
});
|
|
}
|
|
|
|
// ============================================================
|
|
// 6) Trajetória / autonomia
|
|
// ============================================================
|
|
|
|
var motivosTrajetoria =
|
|
obj.Trajetoria?.AutonomiaCorredor?.Motivos?
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Select(NormalizarTexto)
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Distinct()
|
|
.ToList()
|
|
?? new List<string>();
|
|
|
|
foreach (var motivo in motivosTrajetoria)
|
|
{
|
|
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)
|
|
{
|
|
AdicionarEventoTrajetoria(
|
|
linha,
|
|
prioridade: 710);
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// 7) Estado operacional
|
|
//
|
|
// Aqui pegamos estados legítimos em que a operação está
|
|
// liberada, porém ainda não está executando.
|
|
//
|
|
// Exemplos:
|
|
// Aguardando retomada segura
|
|
// Parametrizando
|
|
// Finalizando
|
|
// ============================================================
|
|
|
|
if (
|
|
obj.Operacao != null &&
|
|
obj.Operacao.Liberada &&
|
|
obj.Operacao.Pausa != true &&
|
|
obj.Operacao.Emergencia != true &&
|
|
obj.Operacao.Status != StatusOperacao.EmAndamento &&
|
|
!string.IsNullOrWhiteSpace(obj.Operacao.MotivoStatusOperacao)
|
|
)
|
|
{
|
|
var motivo =
|
|
NormalizarTexto(obj.Operacao.MotivoStatusOperacao);
|
|
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 650,
|
|
Fonte = "StatusOperacao",
|
|
Titulo =
|
|
$"OPERAÇÃO {obj.Operacao.Status.ToString().ToUpper()}",
|
|
Descricao = motivo,
|
|
StatusVisual =
|
|
obj.Operacao.Status == StatusOperacao.Erro
|
|
? StatusModulo.Falha
|
|
: StatusModulo.Alerta
|
|
});
|
|
}
|
|
|
|
// ============================================================
|
|
// 8) Módulos críticos
|
|
// ============================================================
|
|
|
|
var modulosCriticos =
|
|
obj.ModulosSaude?
|
|
.Where(mod =>
|
|
{
|
|
if (mod == null)
|
|
return false;
|
|
|
|
var status =
|
|
mod?.status ?? StatusModulo.Desconectado;
|
|
|
|
bool temCondicaoSevera =
|
|
mod?.condicoes_operacionais?.Any(
|
|
cond => cond?.severidade > 90
|
|
) ?? false;
|
|
|
|
bool mandatorio =
|
|
EhModuloMandatorio(mod?.modulo ?? T_Code.Vzo);
|
|
|
|
bool naoOperante =
|
|
!new[]
|
|
{
|
|
StatusModulo.Operante,
|
|
StatusModulo.Alerta
|
|
}.Contains(status);
|
|
|
|
bool emAlerta =
|
|
status == StatusModulo.Alerta;
|
|
|
|
return
|
|
temCondicaoSevera ||
|
|
(mandatorio && naoOperante) ||
|
|
(mandatorio && emAlerta);
|
|
})
|
|
.ToList()
|
|
?? new List<AgroBase.Models.Operadores.ManagerWorkerMessageResponseModulosPendentesModel>();
|
|
|
|
foreach (var modulo in modulosCriticos)
|
|
{
|
|
var codigoModulo =
|
|
modulo?.modulo ?? T_Code.Vzo;
|
|
|
|
var nomeModulo =
|
|
codigoModulo.ToString();
|
|
|
|
var status =
|
|
modulo?.status ?? StatusModulo.Desconectado;
|
|
|
|
bool mandatorio =
|
|
EhModuloMandatorio(codigoModulo);
|
|
|
|
bool temCondicaoSevera =
|
|
modulo?.condicoes_operacionais?.Any(
|
|
cond => cond?.severidade > 90
|
|
) ?? false;
|
|
|
|
bool moduloMandatorioNaoOperante =
|
|
mandatorio &&
|
|
status != StatusModulo.Operante &&
|
|
status != StatusModulo.Alerta;
|
|
|
|
bool moduloMandatorioAlerta =
|
|
mandatorio &&
|
|
status == StatusModulo.Alerta;
|
|
|
|
// --------------------------------------------------------
|
|
// 8.1) Mandatório não operacional
|
|
// --------------------------------------------------------
|
|
|
|
if (moduloMandatorioNaoOperante)
|
|
{
|
|
List<string> motivos =
|
|
modulo?.motivos?
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Select(NormalizarTexto)
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Distinct()
|
|
.ToList()
|
|
?? new List<string>();
|
|
|
|
string motivoTexto =
|
|
motivos.Count > 0
|
|
? string.Join("; ", motivos)
|
|
: "Sem motivo detalhado informado.";
|
|
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 620,
|
|
Fonte = "ModuloCritico",
|
|
Titulo = "MÓDULO MANDATÓRIO NÃO OPERACIONAL",
|
|
Descricao =
|
|
$"{nomeModulo}: módulo mandatório em estado " +
|
|
$"'{status}': {motivoTexto}",
|
|
StatusVisual = StatusModulo.Falha,
|
|
Modulo = codigoModulo
|
|
});
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// 8.2) Condições operacionais severas
|
|
// --------------------------------------------------------
|
|
|
|
if (temCondicaoSevera)
|
|
{
|
|
var descricoesCriticas =
|
|
modulo?.condicoes_operacionais?
|
|
.Where(cond => cond?.severidade > 90)
|
|
.Select(cond =>
|
|
NormalizarTexto(cond?.descricao))
|
|
.Where(desc =>
|
|
!string.IsNullOrWhiteSpace(desc))
|
|
.Distinct()
|
|
.ToList()
|
|
?? new List<string>();
|
|
|
|
foreach (var descricao in descricoesCriticas)
|
|
{
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 610,
|
|
Fonte = "ModuloCritico",
|
|
Titulo = "CONDIÇÕES OPERACIONAIS CRÍTICAS",
|
|
Descricao =
|
|
$"{nomeModulo}: {descricao}",
|
|
StatusVisual = StatusModulo.Alerta,
|
|
Modulo = codigoModulo
|
|
});
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// 8.3) Mandatório em alerta
|
|
// --------------------------------------------------------
|
|
|
|
if (moduloMandatorioAlerta)
|
|
{
|
|
List<string> motivos =
|
|
modulo?.motivos?
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Select(NormalizarTexto)
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Distinct()
|
|
.ToList()
|
|
?? new List<string>();
|
|
|
|
string motivoTexto =
|
|
motivos.Count > 0
|
|
? string.Join("; ", motivos)
|
|
: "Sem motivo detalhado informado.";
|
|
|
|
eventos.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 600,
|
|
Fonte = "ModuloCritico",
|
|
Titulo = "MÓDULO MANDATÓRIO EM ALERTA",
|
|
Descricao =
|
|
$"{nomeModulo}: módulo mandatório em estado " +
|
|
$"'{status}': {motivoTexto}",
|
|
StatusVisual = StatusModulo.Alerta,
|
|
Modulo = codigoModulo
|
|
});
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// 9) Ordenação dos módulos
|
|
// ============================================================
|
|
|
|
var ordemModulos = new Dictionary<T_Code, int>
|
|
{
|
|
{ T_Code.Ipb, 1 },
|
|
{ T_Code.Npc, 2 },
|
|
{ T_Code.Can, 3 },
|
|
{ T_Code.Bat, 4 },
|
|
{ T_Code.Gps, 5 },
|
|
{ T_Code.Atu, 6 },
|
|
{ T_Code.Sen, 7 }
|
|
};
|
|
|
|
// ============================================================
|
|
// 10) Limpeza / ordenação dos eventos
|
|
// ============================================================
|
|
|
|
var eventosOrdenados =
|
|
eventos
|
|
.Where(x =>
|
|
!string.IsNullOrWhiteSpace(x.Titulo) &&
|
|
!string.IsNullOrWhiteSpace(x.Descricao))
|
|
.GroupBy(x => new
|
|
{
|
|
x.Fonte,
|
|
x.Titulo,
|
|
x.Descricao,
|
|
x.Prioridade,
|
|
x.StatusVisual
|
|
})
|
|
.Select(g => g.First())
|
|
.OrderByDescending(x => x.Prioridade)
|
|
.ThenBy(x =>
|
|
x.Modulo.HasValue &&
|
|
ordemModulos.TryGetValue(
|
|
x.Modulo.Value,
|
|
out var ordem)
|
|
? ordem
|
|
: int.MaxValue)
|
|
.ThenBy(x => x.Fonte)
|
|
.ToList();
|
|
|
|
// ============================================================
|
|
// 11) Estado normal
|
|
// ============================================================
|
|
|
|
if (!eventosOrdenados.Any())
|
|
{
|
|
eventosOrdenados.Add(new MotivoTopBarModel
|
|
{
|
|
Prioridade = 0,
|
|
Fonte = "Normal",
|
|
Titulo = "ROVER OPERANDO NORMALMENTE",
|
|
Descricao =
|
|
"Trajetória, controle e operação em condição normal.",
|
|
StatusVisual =
|
|
obj.StatusRover == StatusModulo.Desconectado
|
|
? StatusModulo.Desconectado
|
|
: StatusModulo.Operante
|
|
});
|
|
}
|
|
|
|
// ============================================================
|
|
// 12) Montagem visual
|
|
// ============================================================
|
|
|
|
var principal =
|
|
eventosOrdenados.First();
|
|
|
|
StatusModulo statusVisual =
|
|
principal.StatusVisual;
|
|
|
|
string linha1 =
|
|
principal.Titulo;
|
|
|
|
string linha2;
|
|
|
|
// Se perdeu comunicação, não exibimos diagnóstico antigo
|
|
// recebido antes da queda junto com a perda de comunicação.
|
|
if (principal.Fonte == "Comunicacao")
|
|
{
|
|
linha2 = principal.Descricao;
|
|
}
|
|
else
|
|
{
|
|
var descricoes =
|
|
eventosOrdenados
|
|
.Select(x => x.Descricao?.Trim())
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Distinct()
|
|
.Take(3)
|
|
.ToList();
|
|
|
|
linha2 =
|
|
string.Join(" • ", descricoes);
|
|
}
|
|
|
|
string badge =
|
|
principal.Fonte switch
|
|
{
|
|
"Comunicacao" => "DESCONECTADO",
|
|
"NCP" => "DESCONECTADO",
|
|
"Emergencia" => "EMERGÊNCIA",
|
|
"Pausa" => "PAUSADO",
|
|
"Operacao" => "BLOQUEADO",
|
|
"Controle" => "CONTROLE",
|
|
"Trajetoria" => "TRAJETÓRIA",
|
|
"StatusOperacao"
|
|
=> obj.Operacao?.Status
|
|
.ToString()
|
|
.ToUpper() ?? "OPERAÇÃO",
|
|
"ModuloCritico" => "ALERTA",
|
|
"Normal" => statusVisual
|
|
.ToString()
|
|
.ToUpper(),
|
|
_ => statusVisual
|
|
.ToString()
|
|
.ToUpper()
|
|
};
|
|
|
|
string resumo =
|
|
principal.Fonte switch
|
|
{
|
|
"Comunicacao" =>
|
|
"Sem telemetria",
|
|
|
|
"NCP" =>
|
|
"Núcleo central desconectado",
|
|
|
|
"Emergencia" =>
|
|
"Parada imediata",
|
|
|
|
"Pausa" =>
|
|
"Aguardando retomada",
|
|
|
|
"Operacao" =>
|
|
"Aguardando liberação",
|
|
|
|
"Controle" =>
|
|
"Controle bloqueado",
|
|
|
|
"Trajetoria" =>
|
|
"Trajetória bloqueada",
|
|
|
|
"StatusOperacao" =>
|
|
NormalizarTexto(
|
|
obj.Operacao?.MotivoStatusOperacao)
|
|
?? obj.Operacao?.Status.ToString()
|
|
?? "Estado operacional",
|
|
|
|
"ModuloCritico" =>
|
|
"Atenção operacional",
|
|
|
|
"Normal" =>
|
|
obj.Operacao?.Status.ToString()
|
|
?? StatusOperacao.NaoIniciado.ToString(),
|
|
|
|
_ =>
|
|
obj.Operacao?.Status.ToString()
|
|
?? StatusOperacao.NaoIniciado.ToString()
|
|
};
|
|
|
|
// ============================================================
|
|
// 13) Histórico
|
|
// ============================================================
|
|
|
|
_viewOperacaoCenter?
|
|
.Historico?
|
|
._vm?
|
|
.CompararERegistrarMudancasEventos(
|
|
rover.RoverId,
|
|
eventosOrdenados);
|
|
|
|
// ============================================================
|
|
// 14) Segurança visual
|
|
// ============================================================
|
|
|
|
if (string.IsNullOrWhiteSpace(linha1))
|
|
linha1 = "ESTADO DO ROVER";
|
|
|
|
if (string.IsNullOrWhiteSpace(linha2))
|
|
linha2 = "Sem detalhes adicionais.";
|
|
|
|
if (string.IsNullOrWhiteSpace(badge))
|
|
badge = "STATUS";
|
|
|
|
if (string.IsNullOrWhiteSpace(resumo))
|
|
resumo =
|
|
obj.Operacao?.Status.ToString()
|
|
?? StatusOperacao.NaoIniciado.ToString();
|
|
|
|
// ============================================================
|
|
// 15) Atualização da UI
|
|
// ============================================================
|
|
|
|
System.Windows.Application.Current.Dispatcher.BeginInvoke(
|
|
new Action(() =>
|
|
{
|
|
_viewOperacaoTop?._vm?.AtualizarStatus(
|
|
statusVisual,
|
|
rover.RoverId,
|
|
obj.Operacao?.Status
|
|
?? StatusOperacao.NaoIniciado,
|
|
linha1,
|
|
linha2,
|
|
badge,
|
|
resumo
|
|
);
|
|
})
|
|
);
|
|
}
|
|
|
|
public void AtualizarBarraSuperior(StatusModulo status, string titulo, string linha1, string linha2, string status_str, string resumo)
|
|
{
|
|
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
|
{
|
|
_viewOperacaoTop?._vm?.AtualizarStatus(status, titulo, StatusOperacao.Parametrizando, linha1, linha2.Replace("\n\n", " • "), status_str, resumo);
|
|
}));
|
|
}
|
|
|
|
public void AtualizarDadosGnss(AgroBase.Models.GPSModel dados)
|
|
{
|
|
var bf = Models.Variaveis.GpsService.BaseFix;
|
|
|
|
if (bf.CorrecaoEmAndamento)
|
|
{
|
|
AtualizarProgressoFixacaoBase(
|
|
bf.Progresso,
|
|
bf.ProgressoStr
|
|
);
|
|
|
|
if (bf.Progresso >= 100)
|
|
{
|
|
FinalizarFixacaoBase(
|
|
true,
|
|
bf.ProgressoStr
|
|
);
|
|
}
|
|
}
|
|
|
|
MapViewControl? mapaAtual =
|
|
CurrentStep == DockStep.PreparacaoMapa
|
|
? _viewPreparacaoMapa?.MapaPreparacao
|
|
: CurrentStep == DockStep.ParametrizacaoRover
|
|
? _viewOperacaoCenter?.Mapa
|
|
: null;
|
|
|
|
if (CurrentStep == DockStep.PreparacaoMapa)
|
|
{
|
|
_viewPreparacaoMapaTop?
|
|
._vm?
|
|
.AtualizarDados(
|
|
dados
|
|
);
|
|
|
|
|
|
var gps =
|
|
Models.Variaveis.GpsService;
|
|
|
|
|
|
if (gps != null)
|
|
{
|
|
var papel =
|
|
gps.PapelEstacaoAtual;
|
|
|
|
|
|
if (
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.PapelGnssAtual !=
|
|
papel
|
|
)
|
|
{
|
|
BaseFixada =
|
|
papel ==
|
|
GnssPapelEstacao
|
|
.BaseFixa;
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.AtualizarPapelGnss(
|
|
papel
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
_mapas.AtualizarBase(
|
|
dados,
|
|
mapaAtual
|
|
);
|
|
}
|
|
|
|
public void AtualizarParametrosRover(OperacaoParametrosModel? rover)
|
|
{
|
|
if (rover == null)
|
|
return;
|
|
|
|
bool roverEmFoco = string.Equals(rover.RoverId, VariaveisControleOperacao.SelectedRoverId, StringComparison.OrdinalIgnoreCase);
|
|
|
|
if (roverEmFoco)
|
|
{
|
|
_viewOperacaoRight?.areaParametrizacao?._vm?.AtualizarParametros(rover);
|
|
|
|
bool respostaSolicitada = string.Equals(rover.RoverId, _roverAguardandoParametrosId, StringComparison.OrdinalIgnoreCase);
|
|
|
|
if (respostaSolicitada)
|
|
{
|
|
_roverAguardandoParametrosId = null;
|
|
AtualizarDadosMapaDoRover(rover);
|
|
}
|
|
}
|
|
|
|
AtualizarDadosTela(rover);
|
|
}
|
|
|
|
private void SincronizarEstadoGnssPreparacao()
|
|
{
|
|
var gps =
|
|
Models.Variaveis.GpsService;
|
|
|
|
|
|
if (gps == null)
|
|
{
|
|
BaseFixada =
|
|
false;
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.AtualizarPapelGnss(
|
|
GnssPapelEstacao
|
|
.Indefinido
|
|
);
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
GnssPapelEstacao papel =
|
|
gps.PapelEstacaoAtual;
|
|
|
|
|
|
BaseFixada =
|
|
papel ==
|
|
GnssPapelEstacao.BaseFixa;
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.AtualizarPapelGnss(
|
|
papel
|
|
);
|
|
}
|
|
|
|
#region MAPAS
|
|
|
|
/*
|
|
* O Dock conhece somente a fachada do subsistema de mapas.
|
|
* Workspace, services, renderers, layers, movimento e edição
|
|
* ficam encapsulados dentro do MapaOperacaoController.
|
|
*/
|
|
public readonly MapaOperacaoController _mapas = new();
|
|
|
|
|
|
/// <summary>
|
|
/// Política da aplicação para a interação do mapa central.
|
|
///
|
|
/// O MapViewControl não consulta mais Dock, Rover ou modo
|
|
/// operacional por conta própria. Toda essa decisão fica aqui.
|
|
/// </summary>
|
|
private MapaModoInteracao ObterModoInteracaoMapaOperacao()
|
|
{
|
|
if (CurrentStep != DockStep.ParametrizacaoRover)
|
|
{
|
|
return MapaModoInteracao.Visualizacao;
|
|
}
|
|
|
|
|
|
var rover = VariaveisControleOperacao.RoverEmFoco;
|
|
|
|
|
|
/*
|
|
* Sem Rover selecionado, o mapa central serve
|
|
* para escolher Base/Rover pelas entidades.
|
|
*/
|
|
if (rover == null)
|
|
{
|
|
return MapaModoInteracao.SelecaoEntidade;
|
|
}
|
|
|
|
|
|
if (_viewOperacaoRight?._vm?.SecaoAtual != SecaoRightBar.Parametrizacao)
|
|
{
|
|
return MapaModoInteracao.Visualizacao;
|
|
}
|
|
|
|
|
|
var operacao = rover.DadosLeitura?.Operacao;
|
|
|
|
|
|
/*
|
|
* Preserva a mesma trava que antes estava
|
|
* escondida dentro do MapViewControl.
|
|
*/
|
|
if (
|
|
(operacao?.Iniciada ?? false) ||
|
|
(operacao?.Status ?? StatusOperacao.NaoIniciado) != StatusOperacao.Parametrizando
|
|
)
|
|
{
|
|
return MapaModoInteracao.Visualizacao;
|
|
}
|
|
|
|
|
|
AgroBase.Models.Enums.ModoOperacao modo = _viewOperacaoRight?.areaParametrizacao?._vm?.Parametros?.Modo ?? operacao.Modo;
|
|
|
|
|
|
return modo switch
|
|
{
|
|
AgroBase.Models.Enums.ModoOperacao.MapaGPS => MapaModoInteracao.SelecaoOperacao,
|
|
|
|
AgroBase.Models.Enums.ModoOperacao.RetornoBase => MapaModoInteracao.RetornoManual,
|
|
|
|
_ => MapaModoInteracao.Visualizacao
|
|
};
|
|
}
|
|
|
|
|
|
private void AtualizarModoInteracaoMapaOperacao()
|
|
{
|
|
_mapas.DefinirModoInteracao(
|
|
_viewOperacaoCenter?.Mapa,
|
|
ObterModoInteracaoMapaOperacao()
|
|
);
|
|
}
|
|
|
|
|
|
private void OnModoOperacaoParametrizacaoAlterado(AgroBase.Models.Enums.ModoOperacao modo)
|
|
{
|
|
/*
|
|
* Mantém o estado desejado do Rover sincronizado
|
|
* antes de recalcular a política de interação.
|
|
*/
|
|
var rover = VariaveisControleOperacao.RoverEmFoco;
|
|
|
|
|
|
if (rover != null)
|
|
{
|
|
rover.Modo = modo;
|
|
}
|
|
|
|
|
|
AtualizarModoInteracaoMapaOperacao();
|
|
}
|
|
|
|
|
|
private void OnMapaLogGerado(string mensagem)
|
|
{
|
|
Models.Variaveis.MostrarLog(mensagem);
|
|
}
|
|
|
|
private void OnMapaEntidadeClicada(string entidadeId)
|
|
{
|
|
try
|
|
{
|
|
var entidade = _mapas.ObterEntidade(entidadeId);
|
|
|
|
if (entidade == null)
|
|
return;
|
|
|
|
if (entidade.Tipo == MapaTipoEntidadeDinamica.Base)
|
|
{
|
|
OnRoverSelecionadoChanged((RoverCardModel)null);
|
|
return;
|
|
}
|
|
|
|
_viewOperacaoRight?._vm?.SelecionarRover(entidade.Id);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Models.Variaveis.MostrarLog("[MAPA][ENTIDADE][DOCK][ERRO] " + ex.Message);
|
|
}
|
|
}
|
|
|
|
private void OnMapaValidacaoEdicaoFalhou(MapaResultadoValidacao resultado)
|
|
{
|
|
if (resultado == null)
|
|
return;
|
|
|
|
|
|
const int maxErrosExibidos =
|
|
8;
|
|
|
|
|
|
List<string> erros =
|
|
resultado
|
|
.Erros
|
|
.Take(
|
|
maxErrosExibidos
|
|
)
|
|
.ToList();
|
|
|
|
|
|
string detalhes =
|
|
string.Join(
|
|
Environment.NewLine,
|
|
erros.Select(
|
|
x =>
|
|
"• " + x
|
|
)
|
|
);
|
|
|
|
|
|
int restantes =
|
|
resultado.QuantidadeErros -
|
|
erros.Count;
|
|
|
|
|
|
if (restantes > 0)
|
|
{
|
|
detalhes +=
|
|
Environment.NewLine +
|
|
$"• ... e mais {restantes} " +
|
|
$"{(restantes == 1 ? "erro" : "erros")}.";
|
|
}
|
|
|
|
|
|
string mensagem =
|
|
"A ação foi cancelada porque " +
|
|
"o mapa editado não passou na validação." +
|
|
Environment.NewLine +
|
|
Environment.NewLine +
|
|
detalhes +
|
|
Environment.NewLine +
|
|
Environment.NewLine +
|
|
"O mapa oficial não foi alterado. " +
|
|
"A sessão de edição continua aberta " +
|
|
"para que o problema possa ser corrigido.";
|
|
|
|
|
|
System.Windows.MessageBox.Show(
|
|
mensagem,
|
|
"Mapa inválido",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Warning
|
|
);
|
|
}
|
|
|
|
|
|
private void OnSalvarMapaEditadoSolicitado()
|
|
{
|
|
try
|
|
{
|
|
if (!_mapas.EdicaoAtiva)
|
|
return;
|
|
|
|
|
|
string nomeSugerido =
|
|
_mapas
|
|
.ObterNomeArquivoSugeridoMapaEditado();
|
|
|
|
|
|
string? diretorioOrigem =
|
|
_mapas
|
|
.ObterDiretorioOrigemMapaEdicao();
|
|
|
|
|
|
var dialog =
|
|
new Microsoft.Win32.SaveFileDialog
|
|
{
|
|
Title =
|
|
"Salvar versão editada do mapa",
|
|
|
|
Filter =
|
|
"GeoJSON (*.geojson)|*.geojson",
|
|
|
|
DefaultExt =
|
|
".geojson",
|
|
|
|
AddExtension =
|
|
true,
|
|
|
|
FileName =
|
|
nomeSugerido,
|
|
|
|
OverwritePrompt =
|
|
true
|
|
};
|
|
|
|
|
|
if (
|
|
!string.IsNullOrWhiteSpace(
|
|
diretorioOrigem
|
|
) &&
|
|
System.IO.Directory.Exists(
|
|
diretorioOrigem
|
|
)
|
|
)
|
|
{
|
|
dialog.InitialDirectory =
|
|
diretorioOrigem;
|
|
}
|
|
|
|
|
|
bool? resultado =
|
|
dialog.ShowDialog();
|
|
|
|
|
|
if (resultado != true)
|
|
return;
|
|
|
|
|
|
bool salvo =
|
|
_mapas
|
|
.SalvarMapaEditadoGeoJson(
|
|
dialog.FileName
|
|
);
|
|
|
|
|
|
if (!salvo)
|
|
return;
|
|
|
|
|
|
Models.Variaveis.MostrarLog(
|
|
"[MAPA][EDICAO][SALVAR][UI] " +
|
|
dialog.FileName
|
|
);
|
|
|
|
|
|
MessageBox.Show(
|
|
"Mapa operacional salvo com sucesso." +
|
|
Environment.NewLine +
|
|
Environment.NewLine +
|
|
dialog.FileName +
|
|
Environment.NewLine +
|
|
Environment.NewLine +
|
|
"A edição continua aberta e o histórico " +
|
|
"Desfazer/Refazer foi preservado.",
|
|
"Mapa salvo",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Information
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Models.Variaveis.MostrarLog(
|
|
"[MAPA][EDICAO][SALVAR][ERRO] " +
|
|
ex
|
|
);
|
|
|
|
|
|
MessageBox.Show(
|
|
"Não foi possível salvar o mapa editado." +
|
|
Environment.NewLine +
|
|
Environment.NewLine +
|
|
ex.Message,
|
|
"Erro ao salvar mapa",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Error
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Wrappers públicos mantidos porque existem consumidores
|
|
* externos ao Dock que já chamam essas APIs.
|
|
* A implementação real agora pertence ao Controller.
|
|
*/
|
|
public void LimparMovimentoRover(string? roverId, bool limparPredicao = true)
|
|
{
|
|
_mapas.LimparMovimentoRover(
|
|
roverId,
|
|
limparPredicao
|
|
);
|
|
}
|
|
|
|
public void AtualizarStatusRoverMapa(string? roverId, StatusOperacao status)
|
|
{
|
|
_mapas.AtualizarStatusRoverMapa(
|
|
roverId,
|
|
status,
|
|
_viewOperacaoCenter?.Mapa
|
|
);
|
|
}
|
|
|
|
public bool MostrarReferenciasGnss => _mapas.MostrarReferenciasGnss;
|
|
|
|
public void DefinirExibicaoReferenciasGnss(bool mostrar)
|
|
{
|
|
_mapas.DefinirExibicaoReferenciasGnss(mostrar);
|
|
}
|
|
|
|
private void OnEstadoMapaAlterado()
|
|
{
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.DefinirEdicaoMapaAtiva(
|
|
_mapas.EdicaoAtiva
|
|
);
|
|
|
|
OnPropertyChanged(
|
|
nameof(CanGoBack)
|
|
);
|
|
|
|
OnPropertyChanged(
|
|
nameof(CanGoNext)
|
|
);
|
|
|
|
CommandManager
|
|
.InvalidateRequerySuggested();
|
|
|
|
AtualizarEstadoMapaParametrizacao();
|
|
}
|
|
|
|
private void AtualizarEstadoMapaParametrizacao()
|
|
{
|
|
void Atualizar()
|
|
{
|
|
var vm =
|
|
_viewOperacaoRight?
|
|
.areaParametrizacao?
|
|
._vm;
|
|
|
|
|
|
if (vm == null)
|
|
return;
|
|
|
|
|
|
double?
|
|
velocidadeSemErvasMs =
|
|
null;
|
|
|
|
|
|
double?
|
|
velocidadeComErvasMs =
|
|
null;
|
|
|
|
|
|
var controle =
|
|
vm
|
|
.Parametros?
|
|
.Controle;
|
|
|
|
|
|
if (controle != null)
|
|
{
|
|
double vSem =
|
|
AgroBase.Models
|
|
.FuncoesMatematicas
|
|
.CalculaVelocidadeMsPercentual(
|
|
controle
|
|
.MovVelocidadeSErvasPercent
|
|
) *
|
|
0.8;
|
|
|
|
|
|
double vCom =
|
|
AgroBase.Models
|
|
.FuncoesMatematicas
|
|
.CalculaVelocidadeMsPercentual(
|
|
controle
|
|
.MovVelocidadeCErvasPercent
|
|
);
|
|
|
|
|
|
if (
|
|
double.IsFinite(vSem) &&
|
|
vSem > 0
|
|
)
|
|
{
|
|
velocidadeSemErvasMs =
|
|
vSem;
|
|
}
|
|
|
|
|
|
if (
|
|
double.IsFinite(vCom) &&
|
|
vCom > 0
|
|
)
|
|
{
|
|
velocidadeComErvasMs =
|
|
vCom;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Durante parametrização podemos ainda não ter
|
|
* informação real de ervas.
|
|
*
|
|
* Nesse caso usamos 0%, exatamente de forma
|
|
* explícita e previsível.
|
|
*/
|
|
double percentualErvas =
|
|
AgroBase.Models
|
|
.Variaveis
|
|
.OperacaoEmAndamento?
|
|
.Sensoriamento?
|
|
.Atuador?
|
|
.PercentualErvasTerreno
|
|
?? 0;
|
|
|
|
|
|
var estimativa =
|
|
_mapas
|
|
.CalcularEstimativaOperacao(
|
|
velocidadeSemErvasMs,
|
|
velocidadeComErvasMs,
|
|
percentualErvas
|
|
);
|
|
|
|
|
|
vm.AtualizarEstadoMapa(
|
|
quantidadeRuas:
|
|
_mapas.QuantidadeRuas,
|
|
|
|
quantidadeRuasPercurso:
|
|
_mapas
|
|
.QuantidadeRuasSelecionadas,
|
|
|
|
edicaoAtiva:
|
|
_mapas.EdicaoAtiva,
|
|
|
|
quantidadeSelecionadasEdicao:
|
|
_mapas
|
|
.QuantidadeRuasSelecionadasEdicao,
|
|
|
|
edicaoModificada:
|
|
_mapas.EdicaoModificada,
|
|
|
|
quantidadeCorredores:
|
|
estimativa
|
|
.QuantidadeCorredores,
|
|
|
|
distanciaOperacaoMetros:
|
|
estimativa
|
|
.DistanciaMetros,
|
|
|
|
areaOperacaoHectares:
|
|
estimativa
|
|
.AreaHectares,
|
|
|
|
larguraMediaCorredorMetros:
|
|
estimativa
|
|
.LarguraMediaMetros,
|
|
|
|
tempoEstimadoOperacaoSegundos:
|
|
estimativa
|
|
.TempoEstimadoSegundos
|
|
);
|
|
}
|
|
|
|
|
|
var dispatcher =
|
|
System.Windows.Application
|
|
.Current?
|
|
.Dispatcher;
|
|
|
|
|
|
if (
|
|
dispatcher == null ||
|
|
dispatcher.CheckAccess()
|
|
)
|
|
{
|
|
Atualizar();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
dispatcher.BeginInvoke(
|
|
new Action(
|
|
Atualizar
|
|
)
|
|
);
|
|
}
|
|
|
|
private void OnEditarMapaPreparacaoSolicitado()
|
|
{
|
|
try
|
|
{
|
|
if (
|
|
CurrentStep !=
|
|
DockStep.PreparacaoMapa
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (!_mapas.PossuiMapa)
|
|
{
|
|
MessageBox.Show(
|
|
"Carregue um mapa antes de abrir o editor.",
|
|
"Editar mapa",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Information
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if (_mapas.EdicaoAtiva)
|
|
{
|
|
/*
|
|
* Proteção contra uma segunda entrada enquanto
|
|
* a sessão global de edição já está aberta.
|
|
*/
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.DefinirEdicaoMapaAtiva(
|
|
true
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
MapViewControl? mapa =
|
|
_viewPreparacaoMapa?
|
|
.MapaPreparacao;
|
|
|
|
|
|
if (mapa == null)
|
|
return;
|
|
|
|
|
|
/*
|
|
* Intencionalmente NÃO existe qualquer requisito
|
|
* de Rover aqui.
|
|
*
|
|
* A edição atua somente no Workspace/Documento
|
|
* e no MapViewControl da tela de preparação.
|
|
*/
|
|
_mapas.IniciarEdicao(
|
|
mapa
|
|
);
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.DefinirEdicaoMapaAtiva(
|
|
true
|
|
);
|
|
|
|
|
|
Models.Variaveis.MostrarLog(
|
|
"[MAPA][EDICAO][PREPARACAO] " +
|
|
"Editor aberto sem dependência de Rover."
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Models.Variaveis.MostrarLog(
|
|
"[MAPA][EDICAO][PREPARACAO][ERRO] " +
|
|
ex
|
|
);
|
|
|
|
|
|
MessageBox.Show(
|
|
"Não foi possível iniciar a edição do mapa." +
|
|
Environment.NewLine +
|
|
Environment.NewLine +
|
|
ex.Message,
|
|
"Editar mapa",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Error
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
private void OnEditarMapaSolicitado()
|
|
{
|
|
try
|
|
{
|
|
if (!_mapas.PossuiMapa)
|
|
{
|
|
System.Windows.MessageBox.Show(
|
|
"Não existe um mapa operacional carregado para edição.",
|
|
"Editar mapa",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Information
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
var mapa =
|
|
_viewOperacaoCenter?
|
|
.Mapa;
|
|
|
|
|
|
if (mapa == null)
|
|
return;
|
|
|
|
|
|
var operacao =
|
|
VariaveisControleOperacao
|
|
.RoverEmFoco?
|
|
.DadosLeitura?
|
|
.Operacao;
|
|
|
|
|
|
if (operacao?.Iniciada == true)
|
|
{
|
|
System.Windows.MessageBox.Show(
|
|
"O mapa não pode ser editado enquanto a operação estiver em andamento.",
|
|
"Editar mapa",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Warning
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if (
|
|
operacao != null &&
|
|
operacao.Status !=
|
|
StatusOperacao.Parametrizando
|
|
)
|
|
{
|
|
System.Windows.MessageBox.Show(
|
|
"O mapa só pode ser editado durante a parametrização da operação.",
|
|
"Editar mapa",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Warning
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
_mapas.IniciarEdicao(
|
|
mapa
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Models.Variaveis.MostrarLog(
|
|
"[MAPA][EDICAO][INICIAR][ERRO] " +
|
|
ex
|
|
);
|
|
|
|
|
|
System.Windows.MessageBox.Show(
|
|
$"Não foi possível iniciar a edição do mapa.\n\n{ex.Message}",
|
|
"Editar mapa",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Error
|
|
);
|
|
}
|
|
}
|
|
|
|
private void OnCancelarEdicaoMapaSolicitado()
|
|
{
|
|
try
|
|
{
|
|
var mapa =
|
|
_viewOperacaoCenter?
|
|
.Mapa;
|
|
|
|
|
|
_mapas.CancelarEdicao(
|
|
mapa
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Models.Variaveis.MostrarLog(
|
|
"[MAPA][EDICAO][CANCELAR][ERRO] " +
|
|
ex
|
|
);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TELA 2
|
|
|
|
private void AtualizarDadosMapaDoRover(OperacaoParametrosModel? dados)
|
|
{
|
|
if (dados == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
var documento =
|
|
_mapas.CarregarMapaDoRover(
|
|
dados
|
|
);
|
|
|
|
_mapas.PrepararMapa(
|
|
_viewOperacaoCenter?.Mapa,
|
|
modoInteracao:
|
|
ObterModoInteracaoMapaOperacao(),
|
|
enquadrarDocumento:
|
|
documento != null
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Models.Variaveis.MostrarLog("[MAPA][ROVER][ERRO] " + ex);
|
|
}
|
|
}
|
|
|
|
private void OnCarregarMapaSolicitado(string caminhoArquivo, TipoMapaOperacao tipo)
|
|
{
|
|
try
|
|
{
|
|
if (_mapas.EdicaoAtiva)
|
|
{
|
|
MessageBox.Show(
|
|
"Finalize ou cancele a edição atual antes de trocar o mapa.",
|
|
"Editor de mapa ativo",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Information
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
_mapas.CarregarMapaArquivo(caminhoArquivo, tipo);
|
|
|
|
_mapas.PrepararMapa(
|
|
_viewPreparacaoMapa?.MapaPreparacao,
|
|
modoInteracao:
|
|
MapaModoInteracao.Visualizacao,
|
|
enquadrarDocumento: true
|
|
);
|
|
|
|
MapaCarregado = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Models.Variaveis.MostrarLog($"[MAPA] Falha: {ex}");
|
|
MapaCarregado = false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void OnFixarBaseSolicitado(MetodoFixacaoBase metodoFix, double? lat, double? lon, double? alt)
|
|
{
|
|
try
|
|
{
|
|
if (!(Models.Variaveis.GpsService.BaseFix?.FixLiberado ?? false))
|
|
{
|
|
bool fixar = false;
|
|
|
|
if (Models.Variaveis.GpsService.BaseFix?.PosicaoBaseFixada ?? false)
|
|
{
|
|
var res = System.Windows.MessageBox.Show(
|
|
"Mudar posição da base?",
|
|
"Tem certeza que deseja fixar a posição da base novamente?",
|
|
MessageBoxButton.YesNo,
|
|
MessageBoxImage.Question);
|
|
|
|
if (res == MessageBoxResult.Yes)
|
|
fixar = true;
|
|
}
|
|
else
|
|
{
|
|
fixar = true;
|
|
}
|
|
|
|
if (fixar)
|
|
{
|
|
_viewPreparacaoMapaBottom?._vm?.IniciarFixacao();
|
|
await Models.Variaveis.GpsService.ConfigurarModulo(true, metodoFix, lat, lon, alt);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Models.Variaveis
|
|
.GpsService
|
|
.BaseFix
|
|
.ReiniciarFix();
|
|
|
|
|
|
BaseFixada =
|
|
false;
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.CancelarFixacao(
|
|
"Fixação da base cancelada."
|
|
);
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.AtualizarPapelGnss(
|
|
Models.Variaveis
|
|
.GpsService
|
|
.PapelEstacaoAtual
|
|
);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_viewPreparacaoMapaBottom?._vm.FinalizarFixacao(false, $"Erro na fixação da base: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public void AtualizarProgressoFixacaoBase(double progresso, string status)
|
|
{
|
|
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
|
{
|
|
_viewPreparacaoMapaBottom?._vm.AtualizarProgresso(progresso, status);
|
|
}));
|
|
}
|
|
|
|
public void FinalizarFixacaoBase(bool sucesso, string mensagem)
|
|
{
|
|
System.Windows.Application
|
|
.Current
|
|
.Dispatcher
|
|
.BeginInvoke(
|
|
new Action(
|
|
() =>
|
|
{
|
|
BaseFixada =
|
|
sucesso;
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm
|
|
.FinalizarFixacao(
|
|
sucesso,
|
|
mensagem
|
|
);
|
|
|
|
|
|
var papel =
|
|
Models.Variaveis
|
|
.GpsService?
|
|
.PapelEstacaoAtual
|
|
??
|
|
GnssPapelEstacao
|
|
.Indefinido;
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm
|
|
.AtualizarPapelGnss(
|
|
papel
|
|
);
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
public void CancelarFixacaoBase(string mensagem)
|
|
{
|
|
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
|
{
|
|
BaseFixada = false;
|
|
_viewPreparacaoMapaBottom?._vm.CancelarFixacao(mensagem);
|
|
}));
|
|
}
|
|
|
|
private async void OnAplicarOffsetSolicitado(double? offsetFrontalCm, double? offsetLateralCm)
|
|
{
|
|
if (offsetFrontalCm == null || offsetLateralCm == null) return;
|
|
|
|
_viewPreparacaoMapaBottom?._vm.IniciarFixacao();
|
|
var gps = Models.Variaveis.GpsService;
|
|
gps.LeverArm = new GeoLeverArm(offsetCampoFrontalCm: offsetFrontalCm.Value, offsetCampoLateralCm: offsetLateralCm.Value);
|
|
(double lat, double lon) = gps.LeverArm.FixLeverArmLatLon_Fast(gps.BaseFix.LatitudeFix, gps.BaseFix.LongitudeFix, gps.BaseFix.OrientacaoFix, 5);
|
|
await Models.Variaveis.GpsService.ConfigurarModulo(true, MetodoFixacaoBase.Manual, lat, lon, gps.BaseFix.AltitudeElpsoidalFix, offset: true);
|
|
}
|
|
|
|
private async void OnUsarGnssMovelSolicitado()
|
|
{
|
|
var gps =
|
|
Models.Variaveis.GpsService;
|
|
|
|
|
|
if (
|
|
gps == null ||
|
|
!gps.IsConnected
|
|
)
|
|
{
|
|
MessageBox.Show(
|
|
"O receptor GNSS não está conectado.",
|
|
"GNSS indisponível",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Warning
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Não alteramos o papel físico do GNSS
|
|
* durante uma captura automática da borda.
|
|
*/
|
|
if (
|
|
_mapas.CapturaBordaGnssAutomaticaAtiva
|
|
)
|
|
{
|
|
MessageBox.Show(
|
|
"Existe um levantamento GNSS em andamento.\n\n" +
|
|
"Finalize ou descarte a borda antes de alterar " +
|
|
"o modo do receptor.",
|
|
"Levantamento em andamento",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Warning
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if (
|
|
gps.PapelEstacaoAtual ==
|
|
GnssPapelEstacao.BaseFixa
|
|
)
|
|
{
|
|
var resposta =
|
|
MessageBox.Show(
|
|
"A estação deixará de funcionar como base fixa " +
|
|
"e poderá ser movimentada para levantamento e edição do mapa.\n\n" +
|
|
"Deseja continuar?",
|
|
"Liberar GNSS para levantamento",
|
|
MessageBoxButton.YesNo,
|
|
MessageBoxImage.Question
|
|
);
|
|
|
|
|
|
if (
|
|
resposta !=
|
|
MessageBoxResult.Yes
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
try
|
|
{
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.IniciarAlteracaoPapelGnss();
|
|
|
|
|
|
await gps
|
|
.ConfigurarComoMovelParaLevantamentoAsync(
|
|
usarNtrip: true
|
|
);
|
|
|
|
|
|
BaseFixada =
|
|
false;
|
|
|
|
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.FinalizarAlteracaoPapelGnss(
|
|
true,
|
|
GnssPapelEstacao.Movel,
|
|
"GNSS liberado para levantamento. " +
|
|
"A antena pode ser movimentada."
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_viewPreparacaoMapaBottom?
|
|
._vm?
|
|
.FinalizarAlteracaoPapelGnss(
|
|
false,
|
|
gps.PapelEstacaoAtual,
|
|
"Não foi possível alterar o modo do GNSS: " +
|
|
ex.Message
|
|
);
|
|
|
|
|
|
Models.Variaveis.MostrarLog(
|
|
"[GNSS][PAPEL][ERRO] " +
|
|
ex
|
|
);
|
|
}
|
|
}
|
|
|
|
private void OnContinuarPreparacaoSolicitado()
|
|
{
|
|
/*
|
|
* A decisão de navegação continua
|
|
* pertencendo ao Dock.
|
|
*/
|
|
if (
|
|
CurrentStep !=
|
|
DockStep.PreparacaoMapa
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (!CanGoNext)
|
|
return;
|
|
|
|
|
|
GoNext();
|
|
}
|
|
|
|
private void OnVoltarPreparacaoSolicitado()
|
|
{
|
|
if (
|
|
CurrentStep !=
|
|
DockStep.PreparacaoMapa
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
GoBack();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TELA 3
|
|
public void AtualizarListaRovers(List<OperacaoParametrosModel> rovers_atual)
|
|
{
|
|
var cards =
|
|
rovers_atual
|
|
.Select(
|
|
x =>
|
|
new RoverCardModel()
|
|
{
|
|
EquipamentoId = x.IP,
|
|
NumeroSerie = x.RoverId,
|
|
Status =
|
|
x.DadosLeitura?.StatusRover
|
|
?? StatusModulo.Desconectado
|
|
}
|
|
)
|
|
.ToArray();
|
|
|
|
_viewOperacaoRight?._vm?.AtualizarListaRovers(cards);
|
|
_mapas.SincronizarRovers(rovers_atual, _viewOperacaoCenter?.Mapa);
|
|
}
|
|
|
|
public void AtualizarDadosTela(OperacaoParametrosModel rover)
|
|
{
|
|
if (rover == null) return;
|
|
var obj = rover.DadosLeitura;
|
|
if (obj == null) return;
|
|
|
|
// MAPA
|
|
_mapas.AtualizarRover(
|
|
rover,
|
|
_viewOperacaoCenter?.Mapa
|
|
);
|
|
/*
|
|
* Acompanhar rover continua sendo função
|
|
* de navegação da view.
|
|
*/
|
|
if (_viewOperacaoCenter?.Mapa?.chbAcompanhar.IsChecked ?? false)
|
|
{
|
|
_viewOperacaoCenter?.Mapa?.SetView(obj.Gnss?.Latitude, obj.Gnss?.Longitude);
|
|
}
|
|
|
|
// ALERTAS
|
|
_viewOperacaoBottom?._vm?.AtualizarListaAlertasTodosRovers();
|
|
|
|
if (
|
|
rover.RoverId !=
|
|
VariaveisControleOperacao.SelectedRoverId
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Status/Iniciada chegam por telemetria.
|
|
*
|
|
* Se a operação deixar de estar Parametrizando,
|
|
* a interação é desligada imediatamente.
|
|
*/
|
|
AtualizarModoInteracaoMapaOperacao();
|
|
|
|
|
|
AtualizarBarraSuperior(rover);
|
|
|
|
// ESQUERDA
|
|
var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Ipb);
|
|
double saude_rede = conexao?.saude ?? 0;
|
|
double conexao_latencia = conexao?.detalhes?["avg_rtt"]?.Value<double?>() ?? 0.0;
|
|
double conexao_perda = conexao?.detalhes?["loss_pct"]?.Value<double?>() ?? 0.0;
|
|
double bwTot = conexao?.detalhes?["bw_total_mbps"]?.Value<double?>() ?? 0.0;
|
|
string comunicacao = $"{conexao_latencia:0.00} ms {bwTot:0.00} Mbps";
|
|
string comunicacao_extended = $"{conexao_latencia:0.00} ms {conexao_perda:F2}% {bwTot:0.00} Mbps";
|
|
|
|
_viewOperacaoLeft?._vm?.AtualizarRover(
|
|
rover?.Descricao ?? "",
|
|
obj.Controle,
|
|
obj.Refrigeracao?.Temperatura ?? 0,
|
|
obj.Bateria?.PercentualBateria ?? 0,
|
|
obj.Atuador?.PercentualReservatorio ?? 0,
|
|
$"{saude_rede}%",
|
|
$"{obj.Gnss?.QualidadeFix ?? TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm",
|
|
AgroBase.Models.GPSUtils.DistanciaEntrePontos(new AgroBase.Models.GPSModel() { Latitude = obj.Gnss?.Latitude ?? 0, Longitude = obj.Gnss?.Longitude ?? 0 }, Models.Variaveis.GpsService.UltimaLeitura),
|
|
obj.Operacao?.Iniciada ?? false
|
|
);
|
|
|
|
// DIREITA (RESUMO)
|
|
_viewOperacaoRight?.areaResumo?._vm?.AtualizarResumo(r =>
|
|
{
|
|
r.Operacao = (obj.Operacao?.Liberada ?? false) ? "🔓 Liberada" : "🔒 Bloqueada";
|
|
r.Modo = obj.Operacao == null ? "-" : obj.Operacao.Modo.ToString();
|
|
r.Status = obj.Operacao == null ? "-" : $"{obj.Operacao.Status}" + (obj.Operacao.Status == AgroBase.Models.Enums.StatusOperacao.Aguardando ? $" ({obj.Operacao.TempoAguardandoSegs:0}s)" : "");
|
|
r.Carro = obj.Trajetoria == null ? "-" : obj.Trajetoria.StatusCarro.ToString();
|
|
r.Temperatura = $"{(obj.Refrigeracao?.Temperatura ?? 0):F2} °C";
|
|
r.RuaAtual = obj.Trajetoria == null ? "-" : $"{obj.Trajetoria.CorredorAtualDistanciaPercorrida:F2} m / " + $"{obj.Trajetoria.CorredorAtualDistanciaTotal:0.00} m " + $"({obj.Trajetoria.CorredorAtualIdx + 1})";
|
|
r.AreaTotal = obj.Trajetoria == null ? "-" : $"{obj.Trajetoria.DistanciaPercorrida:0.00} m / " + $"{obj.Trajetoria.DistanciaTotal:0.00} m";
|
|
r.Bateria = obj.Bateria == null ? "-" : $"{obj.Bateria.TensaoInstantanea:0.00} V " + $"({TimeSpan.FromMinutes(obj.Bateria.TempoEstimadoRestanteMinutos):dd\\.hh\\:mm\\:ss} " + $"{obj.Bateria.DistanciaEstimadaRestanteMetros:0.00} m)";
|
|
r.Herbicida = obj.Atuador == null ? "-" : $"{obj.Atuador.VolumeReservatorioL:0.00} L " + $"({TimeSpan.FromMinutes(obj.Atuador.TempoEstimadoRestanteMinutos):hh\\:mm\\:ss} " + $"{obj.Atuador.DistanciaEstimadaRestanteMetros:0.00} m)";
|
|
r.Infestacao = obj.Atuador == null ? "-" : $"{obj.Atuador.HerbicidaPorAtuacaoMl:0.00} mL/atuação";
|
|
r.Conexao = comunicacao == null ? "-" : $"{comunicacao}";
|
|
r.Duracao = obj.Operacao == null ? "00:00:00 / 00:00:00" : $"{TimeSpan.FromSeconds(obj.Operacao.TempoDecorridoSegs):hh\\:mm\\:ss} / " + $"{(string.IsNullOrWhiteSpace(obj.Trajetoria?.TempoEstimadoOperacao) ? "00:00:00" : obj.Trajetoria.TempoEstimadoOperacao)}";
|
|
|
|
// Opcionais para progress bars, caso queira evoluir o resumo
|
|
r.RuaAtualPercentual = obj.Trajetoria?.PercentualRuaAtual ?? 0;
|
|
r.AreaTotalPercentual = obj.Trajetoria?.PercentualOperacao ?? 0;
|
|
r.BateriaPercentual = obj.Bateria?.PercentualBateria ?? 0;
|
|
r.HerbicidaPercentual = obj.Atuador?.PercentualReservatorio ?? 0;
|
|
r.InfestacaoPercentual = obj.Atuador?.PercentualErvasTerreno ?? 0;
|
|
});
|
|
|
|
// DIAGNOSTICO
|
|
AtualizarDadosDiagnostico(rover);
|
|
|
|
// CAMERAS
|
|
var snr = obj?.Cameras?.FirstOrDefault(x => x.dispositivo == AgroBase.Models.Enums.T_Code.Snr);
|
|
var cam = obj?.Cameras?.FirstOrDefault(x => x.dispositivo == AgroBase.Models.Enums.T_Code.Cam);
|
|
|
|
_viewOperacaoCenter?.Monitoramento?._vm?.AtualizarDadosCameras(snr, cam, obj?.OperadorVisual?.StatusCarro, obj?.Atuador?.PercentualErvasNoRadar);
|
|
|
|
// PULVERIZADOR
|
|
bool telaPulverizador = _viewOperacaoCenter?._vm?.ConteudoAtual == OperacaoCenterConteudo.Pulverizador;
|
|
var bomba = obj.Atuador?.Bombas?.FirstOrDefault(x => x.ID == "BMBLN");
|
|
var agitador = obj.Atuador?.Bombas?.FirstOrDefault(x => x.ID == "BMBAGT");
|
|
var bicosAtivos = obj.Atuador?.Bicos?.Where(x => x.ComandoEstado == true).ToArray();
|
|
int quantidadeBicosAtivos = bicosAtivos?.Length ?? 0;
|
|
int quantidadeTotalBicos = obj.Atuador?.Bicos?.Count ?? 0;
|
|
double vazaoMediaBicosAtivos = bicosAtivos is { Length: > 0 } ? bicosAtivos.Average(x => x.VazaoInstantaneaMLs) : 0.0;
|
|
if (telaPulverizador)
|
|
{
|
|
var moduloAtu = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == T_Code.Atu);
|
|
_viewOperacaoCenter?.Pulverizador?._vm?.AtualizarParametrosTela(new Views.Operacao.Monitoramento.PulverizadorSnapshot
|
|
{
|
|
StatusGeralPulverizador = (moduloAtu?.status ?? StatusModulo.Desconectado).ToString(),
|
|
CameraErvasStatus = _viewOperacaoCenter?.Monitoramento?._vm?.CameraErvasStatus ?? "",
|
|
ModoPulverizacaoTexto = (rover.Controle?.PulverizadorAutomatico ?? false) ? "Automático" : "Manual",
|
|
StatusBombaTexto = (bomba?.ComandoEstado ?? false) ? "Ligado" : "Desligado",
|
|
StatusBombaTextoLido = (bomba?.LeituraEstado ?? false) ? "Ligado" : "Desligado",
|
|
PotenciaBombaValor = $"{(bomba?.ComandoPotencia ?? 0):F2} %",
|
|
PotenciaBombaValorLido = $"{(bomba?.LeituraPotencia ?? 0):F2} %",
|
|
ModoAgitadorTexto = (rover.Controle?.AtuAgitadorModo ?? ModoAgitadorCalda.SemAgitacao).ToString(),
|
|
PotenciaAgitadorValor = $"{agitador?.ComandoPotencia ?? 0:F2} %",
|
|
PotenciaAgitadorValorLido = $"{agitador?.LeituraPotencia ?? 0:F2} %",
|
|
NivelTanqueValor = $"{(obj.Atuador?.PercentualReservatorio ?? 0):F2} %",
|
|
NivelTanqueLitrosValor = $"{(obj.Atuador?.VolumeReservatorioL ?? 0):F2} L",
|
|
PressaoLinhaValor = $"{(rover.Controle?.AtuPressaoLinha ?? 0):F2} psi",
|
|
PressaoLinhaPsiValor = $"{(obj.Atuador?.PressaoLinhaPsi ?? 0):F2} psi",
|
|
VazaoInstantaneaValor = $"{(obj.Atuador?.VazaoInstantaneaMLs ?? 0):F2} mL/s",
|
|
VazaoMediaValor = $"média {(obj.Atuador?.VazaoMediaMLs ?? 0):F2} mL/s",
|
|
AutonomiaDistanciaValor = $"{(obj.Atuador?.DistanciaEstimadaRestanteMetros ?? 0):F2} m",
|
|
AutonomiaTempoValor = FuncoesGlobais.ConverterSegundosParaHHmmss((int)Math.Round((obj.Atuador?.TempoEstimadoRestanteMinutos ?? 0) * 60.0)),
|
|
VolumeVazadoValor = $"{(obj.Atuador?.VolumeVazaoMl ?? 0):F2} mL",
|
|
TempoLigadoPulverizacaoValor = FuncoesGlobais.ConverterSegundosParaHHmmss((int)((bomba?.TempoAtuado ?? 0) / 1000.0)),
|
|
BicosAtivosValor = $"{quantidadeBicosAtivos} / {quantidadeTotalBicos}",
|
|
InfoComplementarBicosValor = $"vazão média: {vazaoMediaBicosAtivos:F2} mL/s",
|
|
PercentualErvasRadar = $"radar: {(obj.Atuador?.PercentualErvasNoRadar ?? 0):F2} %",
|
|
PercentualErvasTerreno = $"terreno: {(obj.Atuador?.PercentualErvasTerreno ?? 0):F2} %"
|
|
});
|
|
}
|
|
var ATU = telaPulverizador ? _viewOperacaoCenter.Pulverizador.areaPulverizador : _viewOperacaoCenter.Monitoramento.Pulverizador;
|
|
ATU.ComprimentoBarraCm = AgroBase.Models.VariaveisEquipamento.ComprimentoBarraPulverizadoraCm;
|
|
ATU.DistanciaEntreBicosCm = AgroBase.Models.VariaveisEquipamento.DistanciaEntreBicosCm;
|
|
//ATU.AlturaBarra = obj?.Controle?.AlturaBarra ?? AgroBase.Models.VariaveisEquipamento.AlturaBarraPulverizadoraCm;
|
|
ATU.IsBombaOn = bomba?.LeituraEstado ?? false;
|
|
ATU.PressaoLinha = obj?.Atuador?.PressaoLinhaPsi ?? 0;
|
|
ATU.VazaoInstantanea = obj?.Atuador?.VazaoInstantaneaMLs ?? 0;
|
|
ATU.TempoAtuado = (bomba?.TempoAtuado ?? 0) / 1000.0;
|
|
double erroLateral = ((obj?.Trajetoria?.DistanciaEsquerda ?? 0) - (obj?.Trajetoria?.DistanciaDireita ?? 0)) / 2.0 * 100.0;
|
|
ATU.LarguraRuaCm = 100.0;
|
|
ATU.ErroLateralCm = erroLateral;
|
|
if (ATU.QtdBicos != obj?.Atuador?.Bicos?.Count)
|
|
{
|
|
ATU.QtdBicos = obj?.Atuador?.Bicos?.Count ?? AgroBase.Models.VariaveisEquipamento.QuantidadeBicosPulverizadores;
|
|
}
|
|
foreach (var bico in ATU.Bicos)
|
|
{
|
|
var _bc = obj?.Atuador?.Bicos?.FirstOrDefault(x => (x.Posicao - 1) == bico.Index);
|
|
if (_bc == null) continue;
|
|
|
|
bico.OpeningAngle = _bc.AnguloAbertura;
|
|
bico.AnguloControle = _bc.ComandoAngulo;
|
|
bico.IsCommandOn = _bc.ComandoEstado;
|
|
bico.IsSpraying = _bc.LeituraEstado;
|
|
bico.Vazao = _bc.VazaoInstantaneaMLs;
|
|
bico.Atuacoes = _bc.QtdAtuacoes;
|
|
bico.TempoAtuado = _bc.TempoAtuado / 1000.0;
|
|
}
|
|
|
|
// HEADING
|
|
var HDG = _viewOperacaoCenter.Monitoramento.Heading;
|
|
HDG.Heading = obj?.Gnss?.OrientacaoReal ?? double.NaN;
|
|
HDG.CourseHeading = obj?.Trajetoria?.AnguloCaminho ?? double.NaN;
|
|
|
|
// ATTITUDE
|
|
var IMU = _viewOperacaoCenter.Monitoramento.Attitude;
|
|
if (obj?.Imu?.InclinacaoLateral != null) IMU.RollDeg = (double)obj.Imu.InclinacaoLateral;
|
|
if (obj?.Imu?.InclinacaoFrontal != null) IMU.PitchDeg = (double)obj.Imu.InclinacaoFrontal;
|
|
IMU.LateralError = erroLateral;
|
|
|
|
|
|
}
|
|
|
|
|
|
private void AbrirDiagnosticoPeloAlerta(AlertaModel alerta)
|
|
{
|
|
if (alerta == null)
|
|
return;
|
|
|
|
_viewOperacaoRight?._vm?.SelecionarRover(alerta.Rover_ID);
|
|
_viewOperacaoRight?._vm?.SelecionarSecao(SecaoRightBar.Diagnostico);
|
|
_viewOperacaoCenter?.Diagnostico?._vm?.AbrirModuloPorAlerta(alerta.Modulo, alerta.Mod_ID);
|
|
}
|
|
|
|
public void AtualizarDadosDiagnostico(OperacaoParametrosModel? rover)
|
|
{
|
|
_viewOperacaoCenter?.Diagnostico?._vm?.AtualizarDados(rover);
|
|
_viewOperacaoCenter?.Diagnostico?.AtualizarGraficos();
|
|
}
|
|
|
|
public void AdicionarAlerta(string roverId, AgroBase.Models.Enums.T_Code modulo, SeveridadeAlerta severidade, string mensagem, string modId = null)
|
|
{
|
|
_viewOperacaoBottom?._vm?.AdicionarAlerta(roverId, modulo, severidade, mensagem, modId);
|
|
}
|
|
|
|
public void RemoverAlerta(string roverId, AgroBase.Models.Enums.T_Code modulo, SeveridadeAlerta? severidade = null, string modId = null)
|
|
{
|
|
_viewOperacaoBottom?._vm?.RemoverAlerta(roverId, modulo, severidade, modId);
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnRoverSelecionadoChanged(string roverId)
|
|
{
|
|
_viewOperacaoRight?._vm?.DestacarRoverNoMapa(roverId);
|
|
}
|
|
private void OnRoverSelecionadoChanged(RoverCardModel rover)
|
|
{
|
|
bool dados_base = rover == null;
|
|
|
|
if (dados_base)
|
|
{
|
|
if (!VariaveisControleOperacao.BaseEmFoco)
|
|
{
|
|
_viewOperacaoCenter?.Monitoramento?.PararStreamCameraFrontal();
|
|
_viewOperacaoCenter?.Monitoramento?.PararStreamCameraErvas();
|
|
VariaveisControleOperacao.EnviarComandoIniciarUDP(VariaveisControleOperacao.RoverEmFoco?.IP, false);
|
|
}
|
|
|
|
VariaveisControleOperacao.SelectedRoverId = VariaveisControleOperacao.BaseMarkerID;
|
|
_mapas.DefinirFocoEntidade(VariaveisControleOperacao.BaseMarkerID, _viewOperacaoCenter?.Mapa);
|
|
_viewOperacaoCenter?._vm?.SelecionarRoverLista(rover);
|
|
|
|
AtualizarBarraSuperior(
|
|
StatusModulo.Conectado,
|
|
"BASE",
|
|
"VISÃO GERAL",
|
|
"Selecione um equipamento na lista à direita para configurá-lo",
|
|
"NORMAL",
|
|
"Aguardando"
|
|
);
|
|
|
|
_viewOperacaoLeft?._vm?.DefinirSemRover();
|
|
}
|
|
else
|
|
{
|
|
_roverAguardandoParametrosId = null;
|
|
|
|
VariaveisControleOperacao.SelectedRoverId = (rover.NumeroSerie ?? "").ToString();
|
|
|
|
/*
|
|
* Normalmente a entidade já foi criada por
|
|
* SincronizarRovers(). Esta guarda cobre seleção
|
|
* antes da primeira atualização da lista.
|
|
*/
|
|
if (_mapas.ObterEntidade(VariaveisControleOperacao.SelectedRoverId) == null)
|
|
{
|
|
_mapas.AtualizarRover(VariaveisControleOperacao.RoverEmFoco, _viewOperacaoCenter?.Mapa);
|
|
}
|
|
|
|
_mapas.DefinirFocoEntidade(VariaveisControleOperacao.SelectedRoverId, _viewOperacaoCenter?.Mapa);
|
|
AtualizarDadosTela(VariaveisControleOperacao.RoverEmFoco);
|
|
|
|
_viewOperacaoCenter?.Monitoramento?.IniciarStreamCameraFrontal();
|
|
_viewOperacaoCenter?.Monitoramento?.IniciarStreamCameraErvas();
|
|
|
|
VariaveisControleOperacao.EnviarComandoIniciarUDP(VariaveisControleOperacao.RoverEmFoco?.IP, true);
|
|
_viewOperacaoCenter?._vm?.SelecionarRoverLista(rover);
|
|
|
|
AtualizarBarraSuperior(VariaveisControleOperacao.RoverEmFoco);
|
|
|
|
VariaveisControleOperacao.RequisitarParametrosOperacao();
|
|
}
|
|
|
|
_mapas.PrepararMapa(
|
|
_viewOperacaoCenter?.Mapa,
|
|
modoInteracao:
|
|
ObterModoInteracaoMapaOperacao(),
|
|
enquadrarDocumento: true
|
|
);
|
|
}
|
|
private void OnSecaoSelecionadaChanged(SecaoRightBar secao)
|
|
{
|
|
switch (secao)
|
|
{
|
|
case SecaoRightBar.Parametrizacao:
|
|
// VariaveisControleOperacao.RequisitarParametrosOperacao();
|
|
break;
|
|
|
|
case SecaoRightBar.Pulverizador:
|
|
if (_viewOperacaoCenter.Monitoramento.videoWeed.imageName != _viewOperacaoCenter.Pulverizador.imgVideo.Name)
|
|
{
|
|
_viewOperacaoCenter.Monitoramento.videoWeed.Parar();
|
|
_viewOperacaoCenter.Monitoramento.videoWeed =
|
|
new TcpVideoReceiver(
|
|
5002,
|
|
_viewOperacaoCenter.Pulverizador.imgVideo
|
|
);
|
|
_viewOperacaoCenter.Monitoramento.videoWeed.Iniciar();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (_viewOperacaoCenter.Monitoramento.videoWeed.imageName != _viewOperacaoCenter.Monitoramento.imgVideoWeed.Name)
|
|
{
|
|
_viewOperacaoCenter.Monitoramento.videoWeed.Parar();
|
|
_viewOperacaoCenter.Monitoramento.videoWeed =
|
|
new TcpVideoReceiver(
|
|
5002,
|
|
_viewOperacaoCenter.Monitoramento.imgVideoWeed
|
|
);
|
|
_viewOperacaoCenter.Monitoramento.videoWeed.Iniciar();
|
|
}
|
|
break;
|
|
}
|
|
|
|
_viewOperacaoCenter?._vm?.DefinirSecao(secao);
|
|
|
|
_mapas.PrepararMapa(
|
|
_viewOperacaoCenter?.Mapa,
|
|
modoInteracao:
|
|
ObterModoInteracaoMapaOperacao(),
|
|
enquadrarDocumento: false
|
|
);
|
|
}
|
|
|
|
|
|
private string? _roverAguardandoParametrosId;
|
|
private void OnLerParametrosSolicitado()
|
|
{
|
|
var rover = VariaveisControleOperacao.RoverEmFoco;
|
|
|
|
if (rover == null || string.IsNullOrWhiteSpace(rover.RoverId))
|
|
return;
|
|
|
|
_roverAguardandoParametrosId = rover.RoverId;
|
|
|
|
VariaveisControleOperacao.RequisitarParametrosOperacao();
|
|
}
|
|
private void OnSalvarParametrosSolicitado(OperacaoParametrosModel parametros)
|
|
{
|
|
var rover = VariaveisControleOperacao.RoverEmFoco;
|
|
|
|
if (rover == null)
|
|
return;
|
|
|
|
if (parametros.Modo == AgroBase.Models.Enums.ModoOperacao.RetornoBase && !(rover.DadosLeitura?.Operacao?.Iniciada ?? false))
|
|
{
|
|
/*
|
|
* =====================================================
|
|
* RETORNO GUIADO PELO OPERADOR
|
|
* =====================================================
|
|
*/
|
|
if (_mapas.PossuiPontosRetorno)
|
|
{
|
|
double distanciaMetros =
|
|
_mapas.ObterDistanciaRotaRetornoMetros();
|
|
|
|
|
|
var resposta =
|
|
System.Windows.MessageBox.Show(
|
|
$"Deseja iniciar o retorno seguindo " +
|
|
$"{_mapas.QuantidadePontosRetorno} pontos marcados no mapa, " +
|
|
$"somando {distanciaMetros:F2} metros?",
|
|
"Retorno guiado",
|
|
MessageBoxButton.YesNo,
|
|
MessageBoxImage.Question
|
|
);
|
|
|
|
|
|
if (resposta != MessageBoxResult.Yes)
|
|
return;
|
|
|
|
|
|
parametros.PontosRetorno =
|
|
_mapas.ExportarRotaRetorno();
|
|
|
|
|
|
/*
|
|
* Manual:
|
|
*
|
|
* lat = null
|
|
* lon = null
|
|
* pontosRetorno = rota
|
|
*/
|
|
VariaveisControleOperacao
|
|
.EnviarComandoRetornoBase(
|
|
pontosRetorno:
|
|
parametros.PontosRetorno
|
|
);
|
|
|
|
|
|
rover.PontosRetorno =
|
|
parametros.PontosRetorno;
|
|
|
|
|
|
Models.Variaveis.MostrarLog(
|
|
"[RETORNO] " +
|
|
$"Modo=GUIADO | " +
|
|
$"Pontos={_mapas.QuantidadePontosRetorno} | " +
|
|
$"Distancia={distanciaMetros:F2} m"
|
|
);
|
|
}
|
|
|
|
/*
|
|
* =====================================================
|
|
* RETORNO AUTOMÁTICO
|
|
* =====================================================
|
|
*/
|
|
else
|
|
{
|
|
var baseFix =
|
|
Models.Variaveis
|
|
.GpsService?
|
|
.BaseFix;
|
|
|
|
|
|
if (
|
|
baseFix == null ||
|
|
!baseFix.PosicaoBaseFixada
|
|
)
|
|
{
|
|
System.Windows.MessageBox.Show(
|
|
"A posição da base não está fixada. " +
|
|
"Não é possível calcular o retorno automático.",
|
|
"Base não fixada",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Warning
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
double latitudeBase =
|
|
baseFix.LatitudeFix;
|
|
|
|
double longitudeBase =
|
|
baseFix.LongitudeFix;
|
|
|
|
|
|
bool coordenadaValida =
|
|
double.IsFinite(latitudeBase) &&
|
|
double.IsFinite(longitudeBase) &&
|
|
latitudeBase >= -90.0 &&
|
|
latitudeBase <= 90.0 &&
|
|
longitudeBase >= -180.0 &&
|
|
longitudeBase <= 180.0;
|
|
|
|
|
|
if (!coordenadaValida)
|
|
{
|
|
System.Windows.MessageBox.Show(
|
|
"A coordenada fixada da base é inválida.",
|
|
"Base inválida",
|
|
MessageBoxButton.OK,
|
|
MessageBoxImage.Warning
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
var resposta =
|
|
System.Windows.MessageBox.Show(
|
|
"Nenhum ponto manual foi marcado.\n\n" +
|
|
"Deseja solicitar que o rover calcule " +
|
|
"automaticamente uma rota segura de retorno à base?",
|
|
"Retorno automático",
|
|
MessageBoxButton.YesNo,
|
|
MessageBoxImage.Question
|
|
);
|
|
|
|
|
|
if (resposta != MessageBoxResult.Yes)
|
|
return;
|
|
|
|
|
|
parametros.PontosRetorno =
|
|
null;
|
|
|
|
|
|
/*
|
|
* Automático:
|
|
*
|
|
* lat = base
|
|
* lon = base
|
|
* pontosRetorno = null
|
|
*/
|
|
VariaveisControleOperacao
|
|
.EnviarComandoRetornoBase(
|
|
lat:
|
|
latitudeBase,
|
|
|
|
lon:
|
|
longitudeBase,
|
|
|
|
pontosRetorno:
|
|
null
|
|
);
|
|
|
|
|
|
rover.PontosRetorno =
|
|
null;
|
|
|
|
|
|
Models.Variaveis.MostrarLog(
|
|
"[RETORNO] " +
|
|
$"Modo=AUTOMATICO | " +
|
|
$"Base={latitudeBase:F8}," +
|
|
$"{longitudeBase:F8}"
|
|
);
|
|
}
|
|
|
|
|
|
Task.Run(
|
|
async () =>
|
|
{
|
|
await Task.Delay(
|
|
500
|
|
);
|
|
|
|
_viewOperacaoRight?
|
|
._vm?
|
|
.SelecionarSecao(
|
|
SecaoRightBar.Resumo
|
|
);
|
|
}
|
|
);
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
var tipoMapa = _mapas.TipoMapa;
|
|
|
|
MapaFeatureCollectionModel? dadosMapa = _mapas.CriarMapaOperacional();
|
|
var ruasPercorrer = _mapas.ObterRuasPercorrer();
|
|
|
|
if (rover?.DadosLeitura?.Operacao?.Modo != parametros.Modo && (rover?.DadosLeitura?.Operacao?.Iniciada ?? false))
|
|
{
|
|
System.Windows.MessageBox.Show($"Já existe uma operação {rover?.DadosLeitura?.Operacao?.Modo ?? AgroBase.Models.Enums.ModoOperacao.NaoDefinido} em andamento. Finalize a operação atual para salvar os novos parametros.", "Mudança de Operação", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
return;
|
|
}
|
|
|
|
if (!(rover?.DadosLeitura?.Operacao?.Iniciada ?? false) && parametros.Modo == AgroBase.Models.Enums.ModoOperacao.MapaGPS && ((dadosMapa?.features?.Count ?? 0) == 0 || !ruasPercorrer.Any()))
|
|
{
|
|
System.Windows.MessageBox.Show("Selecione as ruas no mapa onde o equipamento irá operar", "Ruas não selecionadas", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
return;
|
|
}
|
|
|
|
if (rover == null)
|
|
return;
|
|
|
|
if (parametros?.Controle?.MovimentoAutomatico ?? false)
|
|
{
|
|
var _mov = parametros?.ModulosMandatorios?.FirstOrDefault(x => x.Dispositivo == T_Code.Mov);
|
|
if (_mov == null)
|
|
{
|
|
_mov = new AgroBase.Models.OperacaoModulosMandatoriosModel()
|
|
{
|
|
Dispositivo = T_Code.Mov,
|
|
Utilizar = true,
|
|
Mandatorio = true,
|
|
ComponentesEmUso = new Dictionary<string, (bool, bool)>()
|
|
{
|
|
{ "ET", (true, true) },
|
|
{ "DT", (true, true) },
|
|
{ "EF", (true, true) },
|
|
{ "DF", (true, true) },
|
|
}
|
|
};
|
|
}
|
|
else
|
|
{
|
|
foreach (var key in _mov.ComponentesEmUso?.Keys.ToList() ?? new List<string>())
|
|
{
|
|
_mov.ComponentesEmUso[key] = (true, true);
|
|
}
|
|
_mov.Utilizar = true;
|
|
_mov.Mandatorio = true;
|
|
}
|
|
}
|
|
|
|
if (parametros?.Controle?.DirecionalAutomatico ?? false)
|
|
{
|
|
var _dir = parametros?.ModulosMandatorios?.FirstOrDefault(x => x.Dispositivo == T_Code.Dir);
|
|
if (_dir == null)
|
|
{
|
|
_dir = new AgroBase.Models.OperacaoModulosMandatoriosModel()
|
|
{
|
|
Dispositivo = T_Code.Dir,
|
|
Utilizar = true,
|
|
Mandatorio = true,
|
|
ComponentesEmUso = new Dictionary<string, (bool, bool)>()
|
|
{
|
|
{ "ET", (true, true) },
|
|
{ "DT", (true, true) },
|
|
{ "EF", (true, true) },
|
|
{ "DF", (true, true) },
|
|
}
|
|
};
|
|
}
|
|
else
|
|
{
|
|
foreach (var key in _dir.ComponentesEmUso?.Keys.ToList() ?? new List<string>())
|
|
{
|
|
_dir.ComponentesEmUso[key] = (true, true);
|
|
}
|
|
_dir.Utilizar = true;
|
|
_dir.Mandatorio = true;
|
|
}
|
|
}
|
|
|
|
if (parametros?.Controle?.PulverizadorAutomatico ?? false)
|
|
{
|
|
var _atu = parametros?.ModulosMandatorios?.FirstOrDefault(x => x.Dispositivo == T_Code.Atu);
|
|
if (_atu == null)
|
|
{
|
|
_atu = new AgroBase.Models.OperacaoModulosMandatoriosModel()
|
|
{
|
|
Dispositivo = T_Code.Atu,
|
|
Utilizar = true,
|
|
Mandatorio = true,
|
|
ComponentesEmUso = new Dictionary<string, (bool, bool)>()
|
|
{
|
|
{ "MASRS", (true, true) },
|
|
{ "FLXLN", (true, true) },
|
|
{ "PRSLN", (true, true) },
|
|
{ "BOMBA", (true, true) },
|
|
{ "B01", (true, true) },
|
|
{ "B02", (true, true) },
|
|
{ "B03", (true, true) },
|
|
{ "B04", (true, true) },
|
|
{ "B05", (true, true) },
|
|
{ "B06", (true, true) },
|
|
{ "B07", (true, true) },
|
|
}
|
|
};
|
|
}
|
|
else
|
|
{
|
|
foreach (var key in _atu.ComponentesEmUso?.Keys.ToList() ?? new List<string>())
|
|
{
|
|
_atu.ComponentesEmUso[key] = (true, true);
|
|
}
|
|
_atu.Utilizar = true;
|
|
_atu.Mandatorio = true;
|
|
}
|
|
}
|
|
|
|
if (parametros?.Controle?.ImuParadaPorInclinacao ?? false)
|
|
{
|
|
var _imu = parametros.ModulosMandatorios?.FirstOrDefault(x => x.Dispositivo == T_Code.Imu);
|
|
if (_imu == null)
|
|
{
|
|
_imu = new AgroBase.Models.OperacaoModulosMandatoriosModel()
|
|
{
|
|
Dispositivo = T_Code.Imu,
|
|
Utilizar = true,
|
|
Mandatorio = true,
|
|
};
|
|
parametros?.ModulosMandatorios?.Add(_imu);
|
|
}
|
|
else
|
|
{
|
|
_imu.Utilizar = true;
|
|
_imu.Mandatorio = true;
|
|
}
|
|
}
|
|
|
|
if (parametros?.Controle?.OakParadaPorObstaculo ?? false)
|
|
{
|
|
var _snr = parametros.ModulosMandatorios?.FirstOrDefault(x => x.Dispositivo == T_Code.Snr);
|
|
if (_snr == null)
|
|
{
|
|
_snr = new AgroBase.Models.OperacaoModulosMandatoriosModel()
|
|
{
|
|
Dispositivo = T_Code.Snr,
|
|
Utilizar = true,
|
|
Mandatorio = true,
|
|
};
|
|
parametros?.ModulosMandatorios?.Add(_snr);
|
|
}
|
|
else
|
|
{
|
|
_snr.Utilizar = true;
|
|
_snr.Mandatorio = true;
|
|
}
|
|
}
|
|
|
|
var novosParametros = new OperacaoParametrosModel()
|
|
{
|
|
Modo = parametros.Modo,
|
|
Descricao = parametros.Descricao,
|
|
QtdCamerasSolo = parametros.QtdCamerasSolo,
|
|
QtdBicos = parametros.QtdBicos,
|
|
CapacidadeReservatorio = parametros.CapacidadeReservatorio,
|
|
|
|
Controle = parametros.Controle,
|
|
|
|
ModulosMandatorios = parametros?.ModulosMandatorios,
|
|
ParametrosMandatorios = parametros?.ParametrosMandatorios,
|
|
|
|
TipoMapa = tipoMapa,
|
|
RuasPercorrer = ruasPercorrer,
|
|
Mapa = dadosMapa
|
|
};
|
|
|
|
VariaveisControleOperacao.EnviarParametrosOperacao(novosParametros);
|
|
|
|
rover.Modo = novosParametros.Modo;
|
|
rover.Descricao = novosParametros.Descricao;
|
|
rover.QtdCamerasSolo = novosParametros.QtdCamerasSolo;
|
|
rover.QtdBicos = novosParametros.QtdBicos;
|
|
rover.CapacidadeReservatorio = novosParametros.CapacidadeReservatorio;
|
|
rover.Controle = novosParametros.Controle;
|
|
rover.ModulosMandatorios = novosParametros.ModulosMandatorios;
|
|
rover.ParametrosMandatorios = novosParametros.ParametrosMandatorios;
|
|
rover.PontosRetorno = novosParametros.PontosRetorno;
|
|
rover.TipoMapa = novosParametros.TipoMapa;
|
|
rover.RuasPercorrer = novosParametros.RuasPercorrer;
|
|
rover.Mapa = novosParametros.Mapa;
|
|
|
|
AtualizarDadosDiagnostico(rover);
|
|
|
|
Task.Run(async () =>
|
|
{
|
|
await Task.Delay(500);
|
|
_viewOperacaoRight?._vm?.SelecionarSecao(SecaoRightBar.Diagnostico);
|
|
});
|
|
}
|
|
|
|
|
|
private bool ValidarGnssAntesDeIniciarOperacao()
|
|
{
|
|
/*
|
|
* Simulador não depende da base física.
|
|
*/
|
|
if (
|
|
AppShell.Mock ||
|
|
AppShell.AddRover
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Operação manual não exige estação base.
|
|
*/
|
|
if (
|
|
ModoOperacaoAtual !=
|
|
ModoOperacao.Automatico
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
var gps =
|
|
Models.Variaveis.GpsService;
|
|
|
|
|
|
bool conectado =
|
|
gps?.IsConnected ==
|
|
true;
|
|
|
|
|
|
bool papelBase =
|
|
gps?.PapelEstacaoAtual ==
|
|
GnssPapelEstacao.BaseFixa;
|
|
|
|
|
|
bool baseRealmenteFixada =
|
|
gps?.BaseFix?
|
|
.PosicaoBaseFixada ==
|
|
true;
|
|
|
|
|
|
if (
|
|
conectado &&
|
|
papelBase &&
|
|
baseRealmenteFixada
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
string estado =
|
|
!conectado
|
|
? "GNSS desconectado"
|
|
: gps?.PapelEstacaoAtual switch
|
|
{
|
|
GnssPapelEstacao.Movel =>
|
|
"Levantamento móvel",
|
|
|
|
GnssPapelEstacao.BaseFixa =>
|
|
"Base fixa sem confirmação",
|
|
|
|
_ =>
|
|
"Modo não definido"
|
|
};
|
|
|
|
|
|
var resposta =
|
|
System.Windows.MessageBox.Show(
|
|
"A operação automática não pode ser iniciada " +
|
|
"porque a estação GNSS ainda não está pronta como base fixa.\n\n" +
|
|
|
|
$"Estado atual: {estado}\n\n" +
|
|
|
|
"Configure o GNSS como Base Fixa antes de iniciar a operação.\n\n" +
|
|
"Deseja ir agora para a configuração do GNSS?",
|
|
"Base GNSS necessária",
|
|
MessageBoxButton.YesNo,
|
|
MessageBoxImage.Warning
|
|
);
|
|
|
|
|
|
if (
|
|
resposta ==
|
|
MessageBoxResult.Yes
|
|
)
|
|
{
|
|
IrParaConfiguracaoGnss();
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool AutorizarInicioOperacao()
|
|
{
|
|
return ValidarGnssAntesDeIniciarOperacao();
|
|
}
|
|
|
|
public void IrParaConfiguracaoGnss()
|
|
{
|
|
/*
|
|
* A View e seus ViewModels permanecem vivos.
|
|
*
|
|
* Estamos apenas trocando a etapa visual,
|
|
* então a parametrização do rover não é
|
|
* recriada nem descartada.
|
|
*/
|
|
CurrentStep = DockStep.PreparacaoMapa;
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
public enum ModoOperacao
|
|
{
|
|
Automatico = 1,
|
|
Manual = 2
|
|
}
|
|
|
|
public enum DockStep
|
|
{
|
|
ConfigConexao = 1,
|
|
PreparacaoMapa = 2,
|
|
ParametrizacaoRover = 3
|
|
}
|
|
|
|
} |