767 lines
20 KiB
C#
767 lines
20 KiB
C#
using AgroBase.Models;
|
|
using AgroBase.Models.Operacoes;
|
|
using Mapsui.Layers;
|
|
using OperationControl.Controls;
|
|
using OperationControl.Mapas.Editing;
|
|
using OperationControl.Mapas.Interaction;
|
|
using OperationControl.Mapas.Models;
|
|
using OperationControl.Mapas.Rendering;
|
|
using OperationControl.Mapas.Services;
|
|
using static AgroBase.Models.Enums;
|
|
|
|
namespace OperationControl.Mapas
|
|
{
|
|
public sealed partial class MapaOperacaoController
|
|
{
|
|
/*
|
|
* =========================================================
|
|
* EVENTOS
|
|
* =========================================================
|
|
*/
|
|
|
|
/// <summary>
|
|
/// Informa à aplicação que uma entidade dinâmica
|
|
/// foi clicada no mapa.
|
|
///
|
|
/// O Controller NÃO decide qual Rover deve ser
|
|
/// selecionado na aplicação.
|
|
/// </summary>
|
|
public event Action<string>? EntidadeClicada;
|
|
|
|
|
|
/// <summary>
|
|
/// Permite ao Dock encaminhar mensagens do
|
|
/// subsistema de mapas para o logger da aplicação.
|
|
/// </summary>
|
|
public event Action<string>? LogGerado;
|
|
|
|
public event Action? EstadoEdicaoAlterado;
|
|
|
|
/// <summary>
|
|
/// Disparado quando algum estado estrutural do mapa
|
|
/// muda e uma interface externa pode precisar
|
|
/// atualizar seus indicadores.
|
|
///
|
|
/// Exemplos:
|
|
/// mapa carregado,
|
|
/// mapa limpo,
|
|
/// seleção operacional alterada.
|
|
/// </summary>
|
|
public event Action? EstadoMapaAlterado;
|
|
|
|
public event Action<MapaResultadoValidacao>? ValidacaoEdicaoFalhou;
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* ESTADOS
|
|
* =========================================================
|
|
*/
|
|
|
|
private readonly MapaWorkspace _workspace = new();
|
|
|
|
private readonly MapaEstadoDinamico _estadoDinamico = new();
|
|
|
|
private readonly MapaEstadoMovimento _estadoMovimento = new();
|
|
|
|
private readonly MapaEstadoEdicao _estadoEdicao = new();
|
|
|
|
private readonly MapaEstadoRecorte _estadoRecorte = new();
|
|
|
|
private MapaFerramentaEdicao _ferramentaEdicaoAtual = MapaFerramentaEdicao.Nenhuma;
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* SERVICES
|
|
* =========================================================
|
|
*/
|
|
|
|
private readonly MapaImportService _importService = new();
|
|
private readonly MapaSelectionService _selectionService = new();
|
|
private readonly MapaExportService _exportService = new();
|
|
private readonly MapaContratoImportService _contratoImportService = new();
|
|
private readonly MapaRotaRetornoService _rotaRetornoService = new();
|
|
private readonly MapaEntidadeDinamicaService _entidadeService = new();
|
|
private readonly MapaMovimentoService _movimentoService = new();
|
|
private readonly MapaEdicaoService _edicaoService = new();
|
|
private readonly MapaDocumentoCloneService _documentoCloneService = new();
|
|
private readonly MapaEstimativaOperacaoService _estimativaOperacaoService = new();
|
|
private readonly MapaValidationService _validationService = new();
|
|
|
|
/*
|
|
* =========================================================
|
|
* RENDERERS
|
|
* =========================================================
|
|
*/
|
|
|
|
private readonly MapaRenderService _renderService = new();
|
|
private readonly MapaRotaRetornoRenderService _rotaRenderService = new();
|
|
private readonly MapaEntidadeDinamicaRenderService _entidadeRenderService = new();
|
|
private readonly MapaRastroRenderService _rastroRenderService = new();
|
|
private readonly MapaPredicaoMpcRenderService _predicaoRenderService = new();
|
|
private readonly MapaReferenciaGnssRenderService _referenciaGnssRenderService = new();
|
|
private readonly MapaEdicaoRenderService _edicaoRenderService = new();
|
|
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* ESTADO DE RENDER POR MAPVIEW
|
|
* =========================================================
|
|
*/
|
|
|
|
private readonly Dictionary<
|
|
MapViewControl,
|
|
MapaRenderResult>
|
|
_rendersMapa =
|
|
new();
|
|
|
|
|
|
private readonly Dictionary<
|
|
MapViewControl,
|
|
MemoryLayer>
|
|
_layersRetorno =
|
|
new();
|
|
|
|
|
|
private readonly Dictionary<
|
|
MapViewControl,
|
|
MemoryLayer>
|
|
_layersEntidades =
|
|
new();
|
|
|
|
|
|
private readonly Dictionary<
|
|
MapViewControl,
|
|
MemoryLayer>
|
|
_layersRastro =
|
|
new();
|
|
|
|
|
|
private readonly Dictionary<
|
|
MapViewControl,
|
|
MemoryLayer>
|
|
_layersPredicao =
|
|
new();
|
|
|
|
|
|
private readonly Dictionary<
|
|
MapViewControl,
|
|
MemoryLayer>
|
|
_layersReferenciaGnss =
|
|
new();
|
|
|
|
|
|
/*
|
|
* MapViews que já passaram pelo Controller.
|
|
*
|
|
* Não significa que estão visíveis neste instante.
|
|
*/
|
|
private readonly HashSet<MapViewControl>
|
|
_mapasRegistrados =
|
|
new();
|
|
|
|
|
|
private readonly HashSet<MapViewControl> _mapasEmEdicao = new();
|
|
|
|
|
|
/*
|
|
* Modo anterior ao início da edição.
|
|
*
|
|
* Permite ao Controller restaurar exatamente o contexto
|
|
* da View quando a sessão de edição for cancelada.
|
|
*/
|
|
private readonly Dictionary<
|
|
MapViewControl,
|
|
MapaModoInteracao>
|
|
_modoInteracaoAntesEdicao =
|
|
new();
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* PREFERÊNCIAS VISUAIS
|
|
* =========================================================
|
|
*/
|
|
|
|
private bool
|
|
_mostrarReferenciasGnss =
|
|
true;
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* PROPRIEDADES PÚBLICAS
|
|
* =========================================================
|
|
*/
|
|
|
|
public bool PossuiDocumentoFonte => _workspace.PossuiDocumentoFonte;
|
|
|
|
|
|
public int QuantidadeRuasFonte => _workspace.QuantidadeRuasFonte;
|
|
|
|
public bool PossuiMapa =>
|
|
_workspace.PossuiMapa;
|
|
|
|
|
|
public MapaDocumento? Documento =>
|
|
_workspace.Documento;
|
|
|
|
|
|
public TipoMapaOperacao TipoMapa =>
|
|
_workspace.Documento?.Tipo
|
|
?? TipoMapaOperacao.Indefinido;
|
|
|
|
|
|
public int QuantidadeRuas =>
|
|
_workspace.Documento?
|
|
.QuantidadeRuas
|
|
?? 0;
|
|
|
|
|
|
public int QuantidadeRuasSelecionadas =>
|
|
_workspace.Selecao?
|
|
.Quantidade
|
|
?? 0;
|
|
|
|
|
|
public bool PossuiPontosRetorno =>
|
|
_workspace.RotaRetorno?
|
|
.PossuiPontos
|
|
?? false;
|
|
|
|
|
|
public int QuantidadePontosRetorno =>
|
|
_workspace.RotaRetorno?
|
|
.Quantidade
|
|
?? 0;
|
|
|
|
|
|
public bool MostrarReferenciasGnss =>
|
|
_mostrarReferenciasGnss;
|
|
|
|
public MapaFerramentaEdicao FerramentaEdicaoAtual => _ferramentaEdicaoAtual;
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* EDIÇÃO
|
|
* =========================================================
|
|
*/
|
|
|
|
public bool EdicaoAtiva =>
|
|
_estadoEdicao.EmEdicao;
|
|
|
|
|
|
public bool EdicaoModificada =>
|
|
_estadoEdicao.Modificado;
|
|
|
|
|
|
public int QuantidadeRuasSelecionadasEdicao =>
|
|
_estadoEdicao.QuantidadeSelecionadas;
|
|
|
|
|
|
public bool CapturaBordaGnssAutomaticaAtiva => _estadoRecorte.GravacaoAutomaticaAtiva;
|
|
|
|
|
|
public MapaDocumento? DocumentoEmEdicao => _estadoEdicao.DocumentoTrabalho;
|
|
|
|
|
|
public IReadOnlyCollection<string> RuasSelecionadasEdicao => _estadoEdicao.RuasSelecionadas;
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* CARREGAMENTO
|
|
* =========================================================
|
|
*/
|
|
|
|
public MapaDocumento CarregarMapaArquivo(string caminhoArquivo, TipoMapaOperacao tipo)
|
|
{
|
|
if (
|
|
string.IsNullOrWhiteSpace(
|
|
caminhoArquivo
|
|
)
|
|
)
|
|
{
|
|
throw new ArgumentException(
|
|
"Caminho do mapa inválido.",
|
|
nameof(caminhoArquivo)
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Importa primeiro.
|
|
*
|
|
* Se falhar, o Workspace atual permanece intacto.
|
|
*/
|
|
var documentoImportado =
|
|
_importService.Importar(
|
|
caminhoArquivo,
|
|
tipo
|
|
);
|
|
|
|
|
|
/*
|
|
* O objeto retornado pelo importador é apenas
|
|
* matéria-prima desta transação.
|
|
*
|
|
* Nenhuma das duas cópias persistentes
|
|
* compartilha estado mutável.
|
|
*/
|
|
var documentoFonte =
|
|
_documentoCloneService
|
|
.Clonar(
|
|
documentoImportado
|
|
);
|
|
|
|
|
|
var documentoAtivo =
|
|
_documentoCloneService
|
|
.Clonar(
|
|
documentoImportado
|
|
);
|
|
|
|
|
|
/*
|
|
* Um mapa novo invalida qualquer sessão
|
|
* de edição anterior.
|
|
*/
|
|
CancelarEdicao();
|
|
|
|
|
|
InvalidarRendersWorkspace();
|
|
InvalidarRendersRotaRetorno();
|
|
|
|
|
|
_workspace.DefinirNovoDocumento(
|
|
documentoFonte,
|
|
documentoAtivo
|
|
);
|
|
|
|
EstadoMapaAlterado?.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA] " +
|
|
$"Arquivo carregado | " +
|
|
$"Fonte={documentoFonte.QuantidadeRuas} | " +
|
|
$"Oficial={documentoAtivo.QuantidadeRuas} | " +
|
|
$"Nome={documentoAtivo.Nome}"
|
|
);
|
|
|
|
|
|
return documentoAtivo;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Reconstrói o Workspace a partir do contrato
|
|
/// operacional recebido do rover.
|
|
///
|
|
/// Retorna null quando o rover não possui mapa.
|
|
/// </summary>
|
|
public MapaDocumento?
|
|
CarregarMapaDoRover(
|
|
OperacaoParametrosModel dados)
|
|
{
|
|
if (dados == null)
|
|
{
|
|
throw new ArgumentNullException(
|
|
nameof(dados)
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Rover sem mapa.
|
|
*/
|
|
if (
|
|
dados.Mapa?.features?.Any()
|
|
!= true
|
|
)
|
|
{
|
|
CancelarEdicao();
|
|
|
|
|
|
InvalidarRendersWorkspace();
|
|
InvalidarRendersRotaRetorno();
|
|
|
|
|
|
_workspace.Limpar();
|
|
|
|
EstadoMapaAlterado?.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][ROVER] " +
|
|
"Rover não possui mapa operacional."
|
|
);
|
|
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/*
|
|
* Toda reconstrução acontece fora
|
|
* do estado oficial primeiro.
|
|
*/
|
|
var documentoImportado =
|
|
_contratoImportService.Importar(
|
|
dados.Mapa,
|
|
dados.TipoMapa,
|
|
$"Mapa rover {dados.RoverId}"
|
|
);
|
|
|
|
|
|
var documentoFonte =
|
|
_documentoCloneService
|
|
.Clonar(
|
|
documentoImportado
|
|
);
|
|
|
|
|
|
var novoDocumento =
|
|
_documentoCloneService
|
|
.Clonar(
|
|
documentoImportado
|
|
);
|
|
|
|
|
|
var novaSelecao =
|
|
new MapaSelecaoOperacao();
|
|
|
|
|
|
_selectionService.DefinirSelecao(
|
|
novoDocumento,
|
|
novaSelecao,
|
|
dados.RuasPercorrer
|
|
?? Enumerable.Empty<string>()
|
|
);
|
|
|
|
|
|
var novaRotaRetorno =
|
|
new MapaRotaRetorno();
|
|
|
|
|
|
_rotaRetornoService
|
|
.DefinirPeloContrato(
|
|
novaRotaRetorno,
|
|
dados.PontosRetorno
|
|
);
|
|
|
|
|
|
/*
|
|
* Só agora alteramos o estado oficial.
|
|
*/
|
|
CancelarEdicao();
|
|
|
|
|
|
InvalidarRendersWorkspace();
|
|
InvalidarRendersRotaRetorno();
|
|
|
|
|
|
_workspace.DefinirNovoEstado(
|
|
documentoFonte,
|
|
novoDocumento,
|
|
novaSelecao,
|
|
novaRotaRetorno
|
|
);
|
|
|
|
EstadoMapaAlterado?.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][ROVER] " +
|
|
$"Mapa recebido | " +
|
|
$"Fonte={documentoFonte.QuantidadeRuas} | " +
|
|
$"Oficial={novoDocumento.QuantidadeRuas} | " +
|
|
$"Selecionadas={novaSelecao.Quantidade} | " +
|
|
$"Percurso=[" +
|
|
$"{string.Join(" -> ", novaSelecao.RuasOrdenadas)}]"
|
|
);
|
|
|
|
|
|
return novoDocumento;
|
|
}
|
|
|
|
|
|
public void LimparMapa()
|
|
{
|
|
CancelarEdicao();
|
|
|
|
|
|
InvalidarRendersWorkspace();
|
|
InvalidarRendersRotaRetorno();
|
|
|
|
|
|
_workspace.Limpar();
|
|
|
|
EstadoMapaAlterado?.Invoke();
|
|
|
|
|
|
/*
|
|
* Os estados de Base/Rover e movimento
|
|
* NÃO pertencem ao mapa operacional.
|
|
*
|
|
* Portanto não são apagados aqui.
|
|
*/
|
|
Log(
|
|
"[MAPA] Workspace limpo."
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* CONTRATO OPERACIONAL
|
|
* =========================================================
|
|
*/
|
|
|
|
public MapaFeatureCollectionModel?
|
|
CriarMapaOperacional()
|
|
{
|
|
if (
|
|
_workspace.Documento == null
|
|
)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
|
|
return _exportService
|
|
.CriarMapaOperacional(
|
|
_workspace.Documento
|
|
);
|
|
}
|
|
|
|
|
|
public List<string>
|
|
ObterRuasPercorrer()
|
|
{
|
|
if (
|
|
_workspace.Documento == null
|
|
)
|
|
{
|
|
return new List<string>();
|
|
}
|
|
|
|
|
|
return _selectionService
|
|
.ObterRuasOrdenadas(
|
|
_workspace.Selecao
|
|
)
|
|
.ToList();
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* SELEÇÃO OPERACIONAL
|
|
* =========================================================
|
|
*/
|
|
|
|
public bool AlternarRuaOperacao(
|
|
string ruaId)
|
|
{
|
|
if (
|
|
_workspace.Documento == null
|
|
)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Não existe mapa operacional carregado."
|
|
);
|
|
}
|
|
|
|
|
|
bool selecionada =
|
|
_selectionService
|
|
.AlternarRua(
|
|
_workspace.Documento,
|
|
_workspace.Selecao,
|
|
ruaId
|
|
);
|
|
|
|
|
|
AtualizarEstiloSelecaoRua(
|
|
ruaId
|
|
);
|
|
|
|
EstadoMapaAlterado?.Invoke();
|
|
|
|
|
|
int? ordem =
|
|
_selectionService
|
|
.ObterOrdem(
|
|
_workspace.Selecao,
|
|
ruaId
|
|
);
|
|
|
|
|
|
Log(
|
|
"[MAPA][SELECAO] " +
|
|
$"Rua={ruaId} | " +
|
|
$"Selecionada={selecionada} | " +
|
|
$"Ordem={(ordem?.ToString() ?? "-")}"
|
|
);
|
|
|
|
|
|
return selecionada;
|
|
}
|
|
|
|
|
|
public void LimparSelecaoOperacao()
|
|
{
|
|
_selectionService.Limpar(
|
|
_workspace.Selecao
|
|
);
|
|
|
|
|
|
foreach (
|
|
var item
|
|
in _rendersMapa.ToArray())
|
|
{
|
|
_renderService
|
|
.AplicarSelecao(
|
|
item.Value,
|
|
_workspace.Selecao
|
|
);
|
|
|
|
|
|
item.Key.Mapa.Refresh();
|
|
}
|
|
|
|
EstadoMapaAlterado?.Invoke();
|
|
|
|
|
|
Log(
|
|
"[MAPA][SELECAO] Seleção limpa."
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* ROTA DE RETORNO
|
|
* =========================================================
|
|
*/
|
|
|
|
public bool AlternarPontoRetorno(
|
|
double latitude,
|
|
double longitude)
|
|
{
|
|
bool adicionado =
|
|
_rotaRetornoService
|
|
.AlternarPonto(
|
|
_workspace.RotaRetorno,
|
|
latitude,
|
|
longitude
|
|
);
|
|
|
|
|
|
RenderizarRotasRegistradas();
|
|
|
|
|
|
Log(
|
|
"[MAPA][RETORNO] " +
|
|
$"Lat={latitude:F8} | " +
|
|
$"Lon={longitude:F8} | " +
|
|
$"Acao={(adicionado ? "ADICIONADO" : "REMOVIDO")} | " +
|
|
$"Pontos={QuantidadePontosRetorno}"
|
|
);
|
|
|
|
|
|
return adicionado;
|
|
}
|
|
|
|
|
|
public void LimparRotaRetorno()
|
|
{
|
|
_rotaRetornoService.Limpar(
|
|
_workspace.RotaRetorno
|
|
);
|
|
|
|
|
|
RenderizarRotasRegistradas();
|
|
|
|
|
|
Log(
|
|
"[MAPA][RETORNO] Rota limpa."
|
|
);
|
|
}
|
|
|
|
|
|
public double
|
|
ObterDistanciaRotaRetornoMetros()
|
|
{
|
|
return _rotaRetornoService
|
|
.ObterDistanciaTotalMetros(
|
|
_workspace.RotaRetorno
|
|
);
|
|
}
|
|
|
|
|
|
public List<double[]>
|
|
ExportarRotaRetorno()
|
|
{
|
|
return _rotaRetornoService
|
|
.ExportarContrato(
|
|
_workspace.RotaRetorno
|
|
)
|
|
.ToList();
|
|
}
|
|
|
|
|
|
public MapaEstimativaOperacao CalcularEstimativaOperacao(double? velocidadeSemErvasMs = null, double? velocidadeComErvasMs = null, double percentualErvas = 0)
|
|
{
|
|
try
|
|
{
|
|
return _estimativaOperacaoService
|
|
.Calcular(
|
|
_workspace.Documento,
|
|
_workspace.Selecao,
|
|
velocidadeSemErvasMs,
|
|
velocidadeComErvasMs,
|
|
percentualErvas
|
|
);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log(
|
|
"[MAPA][ESTIMATIVA][ERRO] " +
|
|
ex.Message
|
|
);
|
|
|
|
|
|
return new MapaEstimativaOperacao
|
|
{
|
|
QuantidadeRuasSelecionadas =
|
|
_workspace
|
|
.Selecao
|
|
.Quantidade,
|
|
|
|
MotivoIndisponivel =
|
|
ex.Message
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* =========================================================
|
|
* LOG
|
|
* =========================================================
|
|
*/
|
|
|
|
private void Log(
|
|
string mensagem)
|
|
{
|
|
LogGerado?.Invoke(
|
|
mensagem
|
|
);
|
|
}
|
|
}
|
|
} |