2025-11-12 11:15:07 +00:00
using System.Windows ;
using ScottPlot ; // Plot
using ScottPlot.Plottables ; // ISignal / IScatter
using System.Timers ;
using Colors = ScottPlot . Colors ; // Timer
using System.Windows.Threading ;
using OperationControl.Controls ;
2025-11-14 17:11:58 +00:00
using System.Diagnostics ;
using OperationControl.Models ;
using AgroBase.Models.Components ;
using OperationControl.Services ;
using System.Windows.Controls ;
using System.Runtime.CompilerServices ;
using System.Windows.Media ;
2025-11-24 16:05:50 +00:00
using DotSpatial.Projections.Transforms ;
using System ;
using LibVLCSharp.Shared ;
2025-11-24 19:58:20 +00:00
using System.Windows.Input ;
2025-11-24 16:05:50 +00:00
using System.IO ;
2025-11-24 19:58:20 +00:00
using MediaPlayer = LibVLCSharp . Shared . MediaPlayer ;
2025-11-24 16:05:50 +00:00
using ComboBox = System . Windows . Controls . ComboBox ;
using Application = System . Windows . Application ;
using MessageBox = System . Windows . MessageBox ;
2025-11-24 19:58:20 +00:00
using Brush = System . Windows . Media . Brush ;
using Brushes = System . Windows . Media . Brushes ;
using Label = System . Windows . Controls . Label ;
2025-11-12 11:15:07 +00:00
namespace OperationControl.Windows
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
// buffers (X = tempo em segundos)
private readonly List < double > xs = new ( ) ;
private readonly List < double > ysCorr = new ( ) ; // Corrente (A)
private readonly List < double > ysVolt = new ( ) ; // Tensão (V)
private readonly List < double > ysTemp = new ( ) ; // Temperatura (°C)
private readonly List < double > ysRpm = new ( ) ; // RPM
private readonly System . Timers . Timer tmr = new ( 1000 ) { AutoReset = true } ; // 10 Hz
private double t = 0 ;
private readonly Random rng = new ( ) ;
DispatcherTimer _clock = new DispatcherTimer ( ) ;
Random _rand = new Random ( ) ;
2025-11-24 16:05:50 +00:00
private LibVLC _libVLC ;
private MediaPlayer _playerFront ;
private MediaPlayer _playerWeed ;
private Media _mediaFront ;
private Media _mediaWeed ;
2025-11-12 11:15:07 +00:00
2025-11-24 19:58:20 +00:00
private Label _labelModuloSelecionado ;
2025-11-12 11:15:07 +00:00
public MainWindow ( )
{
InitializeComponent ( ) ;
2025-11-14 17:11:58 +00:00
AtualizarListaDispositivos ( new List < string > ( ) { VariaveisControleOperacao . SelectedRoverId } ) ;
2025-11-12 11:15:07 +00:00
// Título / eixos
Plot . Plot . Title ( "MOV ET – Corrente / Tensão / Temp / RPM (últimos 60s)" ) ;
Plot . Plot . Axes . Bottom . Label . Text = "Tempo (s)" ;
Plot . Plot . Axes . Left . Label . Text = "Unidades (A, V, °C, RPM)" ;
// inicializa com um ponto para cada série
PushSample ( initial : true ) ;
// legenda
Plot . Plot . ShowLegend ( Alignment . UpperRight ) ;
// loop
tmr . Elapsed + = ( _ , __ ) = > Dispatcher . Invoke ( ( ) = > PushSample ( ) ) ;
tmr . Start ( ) ;
DataContext = this ;
// atualizar as caixas
2025-11-14 17:11:58 +00:00
//BBoxes3D.SetBboxes(new[]
//{
// new AgroBase.Models.LivoxBboxModel{ cx=1.2, cy=0.5, cz=0.4, d=0.6, w=0.4, h=0.8 },
// new AgroBase.Models.LivoxBboxModel{ cx=3.0, cy=-0.7, cz=0.6, d=0.8, w=0.5, h=1.2 },
//});
2025-11-24 16:05:50 +00:00
// Opções focadas em baixa latência
_libVLC = new LibVLC (
"--network-caching=1000" , // ms (ajusta depois)
"--clock-jitter=0" ,
"--clock-synchro=0"
) ;
// Player da câmera frontal
_playerFront = new MediaPlayer ( _libVLC ) ;
VideoFront . MediaPlayer = _playerFront ;
_playerWeed = new MediaPlayer ( _libVLC ) ;
VideoWeed . MediaPlayer = _playerWeed ;
Loaded + = MainWindow_Loaded ;
Closed + = MainWindow_Closed ;
}
private void MainWindow_Loaded ( object sender , RoutedEventArgs e )
{
2025-11-12 11:15:07 +00:00
}
2025-11-24 16:05:50 +00:00
private void MainWindow_Closed ( object? sender , EventArgs e )
{
PararStreamCameraFrontal ( true ) ;
PararStreamCameraErvas ( true ) ;
_libVLC ? . Dispose ( ) ;
}
2025-11-12 11:15:07 +00:00
private void PushSample ( bool initial = false )
{
// avança tempo
if ( ! initial ) t + = 1 ; // 10 Hz
xs . Add ( t ) ;
// ------- MOCK de dados plausíveis -------
// Corrente (A): 3– 12 A com ruído
double corr = 8 + 2 * Math . Sin ( t * 0.7 ) + ( rng . NextDouble ( ) - 0.5 ) ;
corr = Math . Clamp ( corr , 0 , 30 ) ;
// Tensão (V): 36– 42 V, ripple leve e queda lenta
double volt = 40.5 + 0.6 * Math . Sin ( t * 0.15 ) - 0.002 * t ;
volt = Math . Clamp ( volt , 30 , 42 ) ;
// Temperatura (°C): sobe devagar
double temp = 38 + 4 * Math . Sin ( t * 0.05 ) + 0.01 * t ;
temp = Math . Clamp ( temp , 20 , 85 ) ;
// RPM: 0– 1500 com variação
double rpm = 1100 + 200 * Math . Sin ( t * 0.9 ) + 80 * ( rng . NextDouble ( ) - 0.5 ) ;
rpm = Math . Clamp ( rpm , 0 , 2000 ) ;
ysCorr . Add ( corr ) ;
ysVolt . Add ( volt ) ;
ysTemp . Add ( temp ) ;
ysRpm . Add ( rpm ) ;
// mantém só últimos 60 s
while ( xs . Count > 0 & & xs [ ^ 1 ] - xs [ 0 ] > 60.0 )
{
xs . RemoveAt ( 0 ) ;
ysCorr . RemoveAt ( 0 ) ;
ysVolt . RemoveAt ( 0 ) ;
ysTemp . RemoveAt ( 0 ) ;
ysRpm . RemoveAt ( 0 ) ;
}
// redesenha (v5: passe arrays diretamente)
Plot . Plot . Clear ( ) ;
// Corrente (A)
var scCorr = Plot . Plot . Add . Scatter ( xs . ToArray ( ) , ysCorr . ToArray ( ) ) ;
scCorr . Label = "Corrente (A)" ;
scCorr . Color = ScottPlot . Colors . Cyan ;
scCorr . LineWidth = 2 ;
// Tensão (V)
var scVolt = Plot . Plot . Add . Scatter ( xs . ToArray ( ) , ysVolt . ToArray ( ) ) ;
scVolt . Label = "Tensão (V)" ;
scVolt . Color = ScottPlot . Colors . Lime ;
scVolt . LineWidth = 2 ;
// Temperatura (°C)
var scTemp = Plot . Plot . Add . Scatter ( xs . ToArray ( ) , ysTemp . ToArray ( ) ) ;
scTemp . Label = "Temp (°C)" ;
scTemp . Color = ScottPlot . Colors . Magenta ;
scTemp . LineWidth = 2 ;
// RPM
var scRpm = Plot . Plot . Add . Scatter ( xs . ToArray ( ) , ysRpm . ToArray ( ) ) ;
scRpm . Label = "RPM" ;
scRpm . Color = ScottPlot . Colors . Orange ;
scRpm . LineWidth = 2 ;
// janela de 60 s rolando
double xEnd = xs [ ^ 1 ] ;
double xStart = Math . Max ( 0 , xEnd - 60.0 ) ;
Plot . Plot . Axes . SetLimitsX ( xStart , xEnd ) ;
Plot . Plot . Axes . AutoScaleY ( ) ; // ajusta Y automaticamente para caber tudo
Plot . Refresh ( ) ;
}
2025-11-14 17:11:58 +00:00
#region DISPOSITIVOS
public void AtualizarListaDispositivos ( List < string > dispositivos )
{
2025-11-24 16:05:50 +00:00
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
try
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
cmbDispositivo . Items . Clear ( ) ;
foreach ( var d in dispositivos )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
cmbDispositivo . Items . Add ( d ) ;
if ( ! MAP . markers . Added ( d ) )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
if ( d = = VariaveisControleOperacao . BaseMarkerID )
{
MAP . markers . AddMarker (
d ,
true ,
lat : Variaveis . GpsService ? . UltimaLeitura ? . Latitude ? ? 0 ,
lon : Variaveis . GpsService ? . UltimaLeitura ? . Longitude ? ? 0 ,
heading : Variaveis . GpsService ? . UltimaLeitura ? . OrientacaoReal ? ? 0 ,
label : "Base"
) ;
}
else
{
var rover = VariaveisControleOperacao . RoversNaRede . FirstOrDefault ( x = > x . RoverId = = d ) ;
if ( rover ! = null )
{
MAP . markers . AddMarker (
d ,
false ,
lat : rover . DadosLeitura ? . Gps ? . Latitude ? ? 0 ,
lon : rover . DadosLeitura ? . Gps ? . Longitude ? ? 0 ,
heading : rover . DadosLeitura ? . Gps ? . AnguloCarroDefinido ? ? 0
) ;
}
}
2025-11-14 17:11:58 +00:00
}
}
2025-11-24 16:05:50 +00:00
int idx_selected = dispositivos . IndexOf ( VariaveisControleOperacao . SelectedRoverId ) ;
if ( idx_selected > - 1 )
cmbDispositivo . SelectedIndex = idx_selected ;
2025-11-14 17:11:58 +00:00
}
2025-11-24 16:05:50 +00:00
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI lista dispositivos: {exUi.Message}" ) ;
}
} ) ) ;
2025-11-14 17:11:58 +00:00
}
private void cmbDispositivo_SelectionChanged ( object sender , System . Windows . Controls . SelectionChangedEventArgs e )
{
2025-11-24 16:05:50 +00:00
var selecionado = ( ( ComboBox ) sender ) . SelectedItem ;
if ( selecionado = = null ) return ;
bool dados_base = selecionado = = VariaveisControleOperacao . BaseMarkerID ;
if ( dados_base & & ! VariaveisControleOperacao . BaseEmFoco )
{
PararStreamCameraFrontal ( ) ;
PararStreamCameraErvas ( ) ;
}
VariaveisControleOperacao . SelectedRoverId = selecionado . ToString ( ) ;
MAP . markers . SetMarkerFocused ( selecionado . ToString ( ) , true ) ;
AtualizarDadosTela_Telemetria ( selecionado . ToString ( ) ) ;
brdResumo . IsEnabled = ! dados_base ;
brdDiagnostico . IsEnabled = ! dados_base ;
brdControle . IsEnabled = ! dados_base ;
brdAjustesOperacao . IsEnabled = ! dados_base ;
if ( ! dados_base )
{
sldGNSS_LeverArm . Value = VariaveisControleOperacao . RoverEmFoco . DadosLeitura ? . Gps ? . LeverArmLateral ? ? 0 ;
btnParametros_Atualizar_Click ( sender , e ) ;
IniciarStreamCameraFrontal ( ) ;
IniciarStreamCameraErvas ( ) ;
}
2025-11-14 17:11:58 +00:00
}
2025-11-24 16:05:50 +00:00
public void LimparDadosTela_Telemetria ( string rover_id )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
try
{
if ( ! string . IsNullOrEmpty ( rover_id ) )
{
//MAP.markers.RemoveMarker(rover_id);
//cmbDispositivo.Items.Remove(rover_id);
}
else
{
//cmbDispositivo.SelectedIndex = 0;
}
}
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI telemetria (clear): {exUi.Message}" ) ;
}
} ) ) ;
2025-11-14 17:11:58 +00:00
}
public void AtualizarDadosTela_Telemetria ( string rover_id )
{
2025-11-24 16:05:50 +00:00
bool dados_base = rover_id = = VariaveisControleOperacao . BaseMarkerID ;
2025-11-24 19:58:20 +00:00
var rover = VariaveisControleOperacao . RoversNaRede . FirstOrDefault ( x = > x . RoverId = = rover_id ) ;
var dados_leitura = rover ? . DadosLeitura ;
2025-11-24 16:05:50 +00:00
if ( dados_base )
{
MAP . markers . UpdateMarkerPosition ( rover_id , lat : Variaveis . GpsService ? . UltimaLeitura ? . Latitude , lon : Variaveis . GpsService ? . UltimaLeitura ? . Longitude , heading : Variaveis . GpsService ? . UltimaLeitura ? . OrientacaoReal ) ;
}
2025-11-24 19:58:20 +00:00
else if ( dados_leitura ! = null )
2025-11-24 16:05:50 +00:00
{
2025-11-24 19:58:20 +00:00
var simulacao = dados_leitura . Controle ? . SimulacaoMPC ? . Select ( x = > new double [ ] { x . longitude , x . latitude } ) ? . ToList ( ) ;
MAP . markers . UpdateMarkerPosition ( rover_id , lat : dados_leitura ? . Gps ? . Latitude , lon : dados_leitura ? . Gps ? . Longitude , heading : dados_leitura ? . Gps ? . OrientacaoReal , predict : simulacao ) ;
MAP . markers . UpdateMarkerInfo ( rover_id , status : dados_leitura . StatusOperacao ) ;
2025-11-24 16:05:50 +00:00
}
2025-11-14 17:11:58 +00:00
if ( rover_id ! = VariaveisControleOperacao . SelectedRoverId ) return ;
2025-11-24 16:05:50 +00:00
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
try
{
if ( rover_id = = VariaveisControleOperacao . BaseMarkerID )
{
Variaveis . MostrarLog ( $"Atualizando dados da tela para a base" ) ;
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
AtualizarDadosTela_GNSS ( Variaveis . GpsService ? . UltimaLeitura ? ? new AgroBase . Models . GPSModel ( ) , marker_id : VariaveisControleOperacao . BaseMarkerID ) ;
AtualizarDadosTela_Heading ( Variaveis . GpsService ? . UltimaLeitura ? . AnguloCarroDefinido ? ? 0 ) ;
}
else
{
2025-11-24 19:58:20 +00:00
if ( dados_leitura = = null ) return ;
2025-11-24 16:05:50 +00:00
Variaveis . MostrarLog ( $"Atualizando dados da tela para o dispositivo {rover_id}" ) ;
2025-11-24 19:58:20 +00:00
AtualizarDadosTela_Diagnosticos ( rover ) ;
if ( dados_leitura . Trajetoria ! = null )
AtualizarDadosTela_Resumo ( dados_leitura ) ;
if ( dados_leitura . Gps ! = null )
AtualizarDadosTela_GNSS ( dados_leitura . Gps , marker_id : rover_id ) ;
if ( dados_leitura . Controle ! = null )
AtualizarDadosTela_Controle ( dados_leitura . Controle , dados_leitura . Movimentacao . AnguloMedio , dados_leitura . Movimentacao . VelocidadeMedia , dados_leitura . Emergencia , ! dados_leitura . OperacaoIniciada , dados_leitura . StatusOperacao = = AgroBase . Models . Enums . StatusOperacao . Calibrando ) ;
2025-11-24 16:05:50 +00:00
//if (rover.Value.Controle != null)
// AtualizarDadosTela_ParametrosOperacao(rover.Value.Controle);
2025-11-24 19:58:20 +00:00
if ( dados_leitura . Trajetoria ! = null )
AtualizarDadosTela_Mapa ( dados_leitura . Trajetoria ) ;
if ( dados_leitura . Gps ! = null )
AtualizarDadosTela_Heading ( dados_leitura . Gps . AnguloCarroDefinido , dados_leitura . Modo = = AgroBase . Models . Enums . ModoOperacao . Manual ? double . NaN : dados_leitura . Trajetoria . AnguloCaminho ) ;
if ( dados_leitura . IMU ! = null )
AtualizarDadosTela_IMU ( dados_leitura . IMU . InclinacaoLateral , dados_leitura . IMU . InclinacaoFrontal ) ;
if ( dados_leitura . LivoxLidar ! = null )
AtualizarDadostela_LIDAR ( dados_leitura . LivoxLidar . bboxes ) ;
2025-11-24 16:05:50 +00:00
}
}
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI telemetria (set): {exUi.Message}" ) ;
}
} ) ) ;
}
#endregion
#region RESUMO OPERACIONAL
public void AtualizarDadosTela_Resumo ( AgroBase . Models . OperacaoSensoriamentoLogModel dados )
{
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
try
{
pgbResumo_RuaAtual . Value = dados . Trajetoria . PercentualRuaAtual ;
lblResumo_RuaAtual . Content = $"{dados.Trajetoria.CorredorAtual.DistanciaPercorridaCorredor.ToString(" 0.00 ")} / {dados.Trajetoria.CorredorAtual.DistanciaTotal.ToString(" 0.00 ")}" ;
pgbResumo_AreaTotal . Value = dados . Trajetoria . PercentualOperacao ;
lblResumo_AreaTotal . Content = $"{dados.Trajetoria.DistanciaPercorrida.ToString(" 0.00 ")} / {dados.Trajetoria.DistanciaTotal.ToString(" 0.00 ")}" ;
pgbResumo_Bateria . Value = dados . Bateria . PorcentagemBateria ;
lblResumo_Bateria . Content = $"{dados.Bateria.TensaoInstantanea.ToString(" 0.00 ")} V ({dados.Bateria.TempoEstimadoRestanteMinutos})" ;
pgbResumo_Herbicida . Value = dados . Atuador . PercentualReservatorio ;
lblResumo_Herbicida . Content = $"{dados.Atuador.VolumeReservatorio.ToString(" 0.00 ")} L ({0.ToString(" 0.00 ")})" ;
pgbResumo_Infestacao . Value = dados . Atuador . PercentualErvasTerreno ;
lblResumo_Infestacao . Content = $"{dados.Atuador.VazaoMedia.ToString(" 0.00 ")}" ;
pgbResumo_Conexao . Value = 0 ;
lblResumo_Conexao . Content = $"0,0 dBi (0,00 m)" ;
lblResumo_OperacaoLiberada . Content = $"Operação " + ( dados . OperacaoLiberada ? "🔓 Liberada" : "🔒 Bloqueada" ) ;
lblResumo_OperacaoModo . Content = $"Modo {dados.Modo}" ;
lblResumo_OperacaoStatus . Content = $"Status {dados.StatusOperacao}" + ( dados . StatusOperacao = = AgroBase . Models . Enums . StatusOperacao . Aguardando ? $" ({dados.TempoAguardandoSeg.ToString(" 0 ")})" : "" ) ;
lblResumo_StatusCarro . Content = $"Carro {dados.Trajetoria.StatusCarro}" ;
lblResumo_TemperaturaGeral . Content = $"Temperatura {(dados.DadosPerformance.CPU_temp ?? 0).ToString(" 0.00 ")} °C" ;
lblResumo_OperacaoDuracao . Content = $"Duração {dados.TempoDecorrido.ToString(" HH : mm : ss ")} / {dados.Trajetoria.TempoEstimadoOperacao}" ;
}
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI Resumo: {exUi.Message}" ) ;
}
} ) ) ;
}
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
#endregion
2025-11-14 17:11:58 +00:00
2025-11-24 19:58:20 +00:00
#region DIAGNOSTICOS
private void AtualizarDadosTela_Diagnosticos ( AgroBase . Models . Operacoes . OperacaoParametrosModel dados )
{
lblDiagnosticos_Impedimento . Content = "Impedimento: " + ( string . IsNullOrEmpty ( dados . DadosLeitura . ErroOperacaoLiberada ) ? "Nenhum" : dados . DadosLeitura . ErroOperacaoLiberada ) ;
// Canvas que está dentro do Border
var canvas = brdDiagnostico . Child as Canvas ;
if ( canvas = = null )
return ;
// Percorre apenas labels dentro do Canvas com o prefixo desejado
foreach ( var lbl in canvas . Children . OfType < Label > ( ) . Where ( x = > x . Name . StartsWith ( "lblDiagnosticos_" ) ) )
{
( var modulo , var mod_label ) = GetModuleData ( lbl ) ;
if ( modulo = = AgroBase . Models . Enums . T_Code . Vzo )
continue ;
// Procura a saúde desse módulo
var saude = dados . DadosLeitura . ModulosSaude . FirstOrDefault ( x = > x . modulo = = modulo ) ;
if ( saude = = null )
continue ;
var individual = saude . saude_individual . FirstOrDefault ( x = > x . label = = mod_label ) ;
bool mandatorio = dados . DadosLeitura . ModulosMandatorios . Any ( x = > x . Dispositivo = = modulo & & x . Mandatorio ) ;
// Aplica cor de acordo com o status
AplicarStatusModuloNaLabel ( lbl , individual ? . status ? ? saude . status , mandatorio ) ;
}
}
private void AplicarStatusModuloNaLabel ( Label lbl , AgroBase . Models . Enums . StatusModulo status , bool mandatorio )
{
// você pode ajustar essas cores depois
Brush back ;
Brush fore = Brushes . White ;
switch ( status )
{
case AgroBase . Models . Enums . StatusModulo . Desconectado :
back = mandatorio ? Brushes . Red : Brushes . Gray ;
break ;
case AgroBase . Models . Enums . StatusModulo . Conectado :
back = Brushes . SteelBlue ;
break ;
case AgroBase . Models . Enums . StatusModulo . Falha :
back = Brushes . Red ;
break ;
case AgroBase . Models . Enums . StatusModulo . Alerta :
back = Brushes . Orange ;
break ;
case AgroBase . Models . Enums . StatusModulo . Operante :
back = Brushes . Green ;
break ;
default :
back = Brushes . DarkGray ;
break ;
}
lbl . Background = back ;
lbl . Foreground = fore ;
}
private ( AgroBase . Models . Enums . T_Code , string ) GetModuleData ( Label lbl )
{
string lbl_name = lbl . Name . Replace ( "lblDiagnosticos_" , "" ) ;
string [ ] pts = lbl_name . Split ( '_' ) ;
string mod_name = pts [ 0 ] ;
string mod_label = pts . Length > 1 ? pts [ 1 ] : "" ;
if ( ! Enum . TryParse < AgroBase . Models . Enums . T_Code > ( mod_name , ignoreCase : true , out var modulo ) )
return ( AgroBase . Models . Enums . T_Code . Vzo , "" ) ;
return ( modulo , mod_label ) ;
}
private void DiagnosticoModulo_Click ( object sender , MouseButtonEventArgs e )
{
if ( sender is not Label lbl )
return ;
// desfazer seleção antiga
if ( _labelModuloSelecionado ! = null )
{
_labelModuloSelecionado . BorderBrush = Brushes . Transparent ;
_labelModuloSelecionado . BorderThickness = new Thickness ( 1 ) ;
_labelModuloSelecionado . Effect = null ;
}
// selecionar a nova
_labelModuloSelecionado = lbl ;
_labelModuloSelecionado . BorderBrush = Brushes . Yellow ;
_labelModuloSelecionado . BorderThickness = new Thickness ( 2 ) ;
// opcional — glow bonito
_labelModuloSelecionado . Effect = new System . Windows . Media . Effects . DropShadowEffect
{
Color = System . Windows . Media . Colors . Yellow ,
BlurRadius = 8 ,
ShadowDepth = 0 ,
Opacity = 0.9
} ;
( var modulo , var mod_label ) = GetModuleData ( lbl ) ;
if ( modulo = = AgroBase . Models . Enums . T_Code . Vzo )
return ;
// Aqui você chama o método que quiser:
AbrirDetalhesModulo ( modulo , mod_label ) ;
}
private void AbrirDetalhesModulo ( AgroBase . Models . Enums . T_Code modulo , string label )
{
var rover = VariaveisControleOperacao . RoverEmFoco ;
var dispositivo = rover ? . DadosLeitura ? . DispositivosMapeados ? . FirstOrDefault ( x = > x . Dispositivo = = modulo & & x . Mod_ID = = label ) ;
if ( dispositivo = = null )
{
MessageBox . Show ( $"Sem dados para o módulo {modulo} ({label})" ) ;
return ;
}
lblSaude_Descricao . Content = $"Módulo {dispositivo.Dispositivo} {dispositivo.Mod_ID}" ;
lblSaude_Versao . Content = $"Versão {dispositivo.Versao ?? " - "}" ;
lblSaude_Status . Content = $"{dispositivo.Status} (Saúde {dispositivo.Saude}%)" ;
lblSaude_CAN . Content = $"CAN {dispositivo.VelocidadeBarramento} ms" ;
lblSaude_Latencia . Content = $"Latência {dispositivo.Latencia} ms" ;
lblSaude_Impedimento . Content = $"Impedimento: {dispositivo.Erros ?? " Nenhum "}" ;
}
#endregion
2025-11-24 16:05:50 +00:00
#region POSICAO
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
public void AtualizarDadosTela_GNSS ( AgroBase . Models . GPSModel dados , string? marker_id = null , double progresso = double . NaN )
{
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
try
{
bool dados_base = marker_id = = VariaveisControleOperacao . BaseMarkerID ;
lblGNSS_Posicao . Foreground = new SolidColorBrush ( dados . Inicializado ? System . Windows . Media . Colors . Green : System . Windows . Media . Colors . Red ) ;
lblGNSS_Correcao . Content = $"Correção {dados.QualidadeFix} " +
( dados . QualidadeFix = = AgroBase . Models . Enums . TiposCorrecaoGPS . BaseFix ?
( dados . OrientacaoReal . ToString ( "0.00" ) + "°" ) :
( ( dados . Ntrip_ativado ? "N" : "B" ) + $" {dados.IdadeCorrecao.ToString(" 0.0 ")} s" ) ) ;
lblGNSS_Satelites . Content = $"Satélites {dados.NumeroSatelites} ({dados.PrecisaoCm.ToString(" 0.00 ")} cm)" ;
lblGNSS_Altitude . Content = $"Altitude {dados.Altitude.ToString(" 0.00 ")} m" ;
if ( dados_base )
{
sldGNSS_LeverArm . Visibility = Visibility . Hidden ;
if ( double . IsNaN ( progresso ) )
{
lblGNSS_Progresso . Visibility = Visibility . Hidden ;
}
else
{
lblGNSS_Progresso . Visibility = Visibility . Visible ;
lblGNSS_Progresso . Content = $"{progresso.ToString(" 0.00 ")}%" ;
}
bool gnss_fix_preciso = new List < AgroBase . Models . Enums . TiposCorrecaoGPS > ( ) { AgroBase . Models . Enums . TiposCorrecaoGPS . RTKFixo , AgroBase . Models . Enums . TiposCorrecaoGPS . RTKFlutuante } . Contains ( Variaveis . GpsService ? . UltimaLeitura ? . QualidadeFix ? ? AgroBase . Models . Enums . TiposCorrecaoGPS . SemCorrecao ) ;
bool gnss_fixado = Variaveis . GpsService ? . BaseFix ? . CorrecaoAbsoluta ? ? false ;
bool gnss_fixando = btnGNSS_FixarBase . Content . ToString ( ) = = "Parar" ;
btnGNSS_FixarBase . IsEnabled = ( gnss_fix_preciso & & ! gnss_fixado ) | | gnss_fixando | | gnss_fixado ;
btnGNSS_FixarBase . Content = gnss_fixado | | ! gnss_fixando ? "Fixar" : "Parar" ;
}
else
{
btnGNSS_FixarBase . IsEnabled = true ;
sldGNSS_LeverArm . Visibility = Visibility . Visible ;
lblGNSS_Progresso . Visibility = Visibility . Visible ;
lblGNSS_Progresso . Content = $"{(sldGNSS_LeverArm.Value * 10.0).ToString(" 0 ")} cm" ;
lblGNSS_Progresso . Foreground = new SolidColorBrush ( Math . Round ( sldGNSS_LeverArm . Value * 10.0 , 0 ) = = Math . Round ( dados . LeverArmLateral , 0 ) ? System . Windows . Media . Colors . Black : System . Windows . Media . Colors . Red ) ;
btnGNSS_FixarBase . Content = "Aplicar" ;
}
}
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI GNSS: {exUi.Message}" ) ;
}
} ) ) ;
2025-11-14 17:11:58 +00:00
}
2025-11-24 16:05:50 +00:00
private void btnGNSS_FixarBase_Click ( object sender , RoutedEventArgs e )
{
if ( VariaveisControleOperacao . BaseEmFoco )
{
if ( ! Variaveis . GpsService . BaseFix . FixLiberado )
{
if ( Variaveis . GpsService . BaseFix . CorrecaoAbsoluta )
{
var res = 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 )
{
btnGNSS_FixarBase . Content = "Parar" ;
Variaveis . GpsService . BaseFix . FixLiberado = true ;
Task . Run ( async ( ) = > await Variaveis . GpsService . ConfigurarModulo ( ) ) ;
}
}
else
{
btnGNSS_FixarBase . Content = "Parar" ;
Variaveis . GpsService . BaseFix . FixLiberado = true ;
}
}
else
{
btnGNSS_FixarBase . Content = "Fixar" ;
Variaveis . GpsService . BaseFix . FixLiberado = false ;
}
}
else
{
VariaveisControleOperacao . EnviarComandoLeverArm ( sldGNSS_LeverArm . Value * 10.0 ) ;
}
}
private void sldGNSS_LeverArm_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < double > e )
{
lblGNSS_Progresso . Content = $"{(sldGNSS_LeverArm.Value * 10.0).ToString(" 0 ")} cm" ;
}
2025-11-14 17:11:58 +00:00
#endregion
2025-11-24 16:05:50 +00:00
#region CONTROLE
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
public void AtualizarDadosTela_Controle ( AgroBase . Models . OperacaoControleModel controle , double angulo , double velocidade , bool emergencia , bool pausa , bool refing )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
try
{
CTRL . AtualizarDadosControle (
angSp : controle . Angulo ,
angCur : angulo ,
tipoMov : controle . TipoMovimento ,
velPct : controle . PercentualVelocidadeSP ,
velSp : controle . PercentualVelocidadeSPKmh ,
velKmh : velocidade ,
emg : emergencia ,
pause : pausa ,
refing : refing
) ;
}
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI Controle: {exUi.Message}" ) ;
}
} ) ) ;
2025-11-14 17:11:58 +00:00
}
#endregion
2025-11-24 16:05:50 +00:00
#region PARAMETROS DA OPERACAO
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
public void AtualizarDadosTela_ParametrosOperacao ( AgroBase . Models . Operacoes . OperacaoParametrosControleModel ? controle )
{
if ( controle = = null ) return ;
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
try
{
txtParametros_VelCErvas . Text = controle . MovVelocidadeCErvasPercent . ToString ( ) ;
txtParametros_VelSErvas . Text = controle . MovVelocidadeSErvasPercent . ToString ( ) ;
txtParametros_AngMax . Text = controle . DirAnguloMaximo . ToString ( ) ;
txtParametros_RoiAtuacao . Text = controle . AtuPercentualInicioPulverizacao . ToString ( ) ;
txtParametros_AreaAtuacao . Text = controle . AtuAlturaAreaPulverizacao . ToString ( ) ;
txtParametros_PressaoLiha . Text = controle . AtuPressaoLinha . ToString ( ) ;
txtParametros_PctErvaOn . Text = controle . AtuPercentualErvasBicoOn . ToString ( ) ;
txtParametros_PctErvaOff . Text = controle . AtuPercentualErvasBicoOff . ToString ( ) ;
txtParametros_TempoOn . Text = controle . AtuDuracaoAtuacao . ToString ( ) ;
chbParametros_Pulverizador . IsChecked = controle . PulverizadorAutomatico ;
chbParametros_Frenagem . IsChecked = controle . FrenagemAutomatica ;
chbParametros_Sonar . IsChecked = controle . SonarAtivado ;
}
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI Parametros da Operacao: {exUi.Message}" ) ;
}
} ) ) ;
}
private void btnParametros_Salvar_Click ( object sender , RoutedEventArgs e )
{
var modo = ( CTRL . TgManual . IsChecked ? ? false ) ? AgroBase . Models . Enums . ModoOperacao . Manual : AgroBase . Models . Enums . ModoOperacao . MapaGPS ;
VariaveisControleOperacao . EnviarParametrosOperacao ( new AgroBase . Models . Operacoes . OperacaoParametrosModel ( )
{
Modo = modo ,
Descricao = "Operacao da base" ,
QtdCamerasSolo = 1 ,
QtdBicos = 3 ,
CapacidadeReservatorio = 45 ,
Controle = new AgroBase . Models . Operacoes . OperacaoParametrosControleModel ( )
{
MovimentoAutomatico = modo ! = AgroBase . Models . Enums . ModoOperacao . Manual ,
SonarAtivado = chbParametros_Sonar . IsChecked ? ? false ,
FrenagemAutomatica = chbParametros_Frenagem . IsChecked ? ? false ,
MovVelocidadeCErvasPercent = int . Parse ( txtParametros_VelCErvas . Text ) ,
MovVelocidadeSErvasPercent = int . Parse ( txtParametros_VelSErvas . Text ) ,
DirAnguloMaximo = int . Parse ( txtParametros_AngMax . Text ) ,
DirTipoMovimento = AgroBase . Models . Enums . TiposControladorDirecional . MPC ,
DirVelocidadeMovimento = 50 ,
PulverizadorAutomatico = chbParametros_Pulverizador . IsChecked ? ? false ,
AtuPercentualErvasBicoOn = double . Parse ( txtParametros_PctErvaOn . Text ) ,
AtuPercentualErvasBicoOff = double . Parse ( txtParametros_PctErvaOff . Text ) ,
AtuDuracaoAtuacao = int . Parse ( txtParametros_TempoOn . Text ) ,
AtuAlturaAreaPulverizacao = double . Parse ( txtParametros_AreaAtuacao . Text ) ,
AtuPercentualInicioPulverizacao = double . Parse ( txtParametros_RoiAtuacao . Text ) ,
AtuPressaoLinha = double . Parse ( txtParametros_PressaoLiha . Text ) ,
} ,
ModulosMandatorios = new List < AgroBase . Models . OperacaoModulosMandatoriosModel > ( ) ,
ParametrosMandatorios = new List < AgroBase . Models . OperacaoParametrosMandatoriosModel > ( ) ,
RuasPercorrer = MAP . RuasMapaCarregado . Where ( x = > x . Selected ) . Select ( x = > x . Id ) . ToList ( ) ,
Mapa = MAP . CriarDadosMapa ( )
} ) ;
}
private void btnParametros_Atualizar_Click ( object sender , RoutedEventArgs e )
2025-11-14 17:11:58 +00:00
{
2025-11-24 19:58:20 +00:00
VariaveisControleOperacao . RequisitarParametrosOperacao ( ) ;
2025-11-14 17:11:58 +00:00
}
#endregion
#region MAPA
private void MAP_MarkerClicked ( object? sender , MapMarkerManager . MarkerClickedEventArgs e )
{
if ( string . IsNullOrEmpty ( e . MarkerId ) )
{
2025-11-24 16:05:50 +00:00
Variaveis . MostrarLog ( $"Sem rover em foco" ) ;
LimparDadosTela_Telemetria ( e . MarkerId ) ;
2025-11-14 17:11:58 +00:00
}
else
{
2025-11-24 16:05:50 +00:00
Variaveis . MostrarLog ( $"Rover em foco: {e.MarkerId}" ) ;
int idx = cmbDispositivo . Items . IndexOf ( e . MarkerId ) ;
cmbDispositivo . SelectedIndex = idx ;
2025-11-14 17:11:58 +00:00
}
}
private void MAP_StreetMapClicked ( object? sender , MapViewControl . StreetMapClickedEventArgs e )
{
Variaveis . MostrarLog ( $"Rua clicada: {e.StreetId}. Ruas selecionadas: {string.Join(" , ", e.SelectedStreetsIds)}" ) ;
}
2025-11-24 16:05:50 +00:00
private void AtualizarDadosTela_Mapa ( AgroBase . Models . OperacaoSensoriamentoLogTrajetoriaModel Trajetoria )
{
lblMapaInfo_Debug . Content = $"" +
$"Próximo Ponto: {Trajetoria.ProximoPonto.idxPonto} | " +
( Trajetoria . ProximoPonto . Aproximando ? "Aproximando" : "Afastando" ) + " | " +
$"Distância próximo ponto: {Trajetoria.ProximoPonto.DistanciaAtual.ToString(" 0.00 ")} m | " +
$"Distância ponto anterior: {Trajetoria.PontoAtual.DistanciaAtual.ToString(" 0.00 ")} m | " +
$"Corredor: " + ( Trajetoria . CorredorAtual . Dentro ? "Dentro" : "Fora" ) + " | " +
$"Margem: " + ( Trajetoria . NaMargemDoCorredor ? "Sim" : "Não" ) + " | " +
$"Disância Esquerda: {(Trajetoria.DistanciaEsquerda * 100.0).ToString(" 0.00 ")} cm | " +
$"Distância Direita: {(Trajetoria.DistanciaDireita * 100.0).ToString(" 0.00 ")} cm | " +
$"Status: {Trajetoria.StatusCarro}" ;
double erroLateral = ( Trajetoria . DistanciaEsquerda - Trajetoria . DistanciaDireita ) / 2.0 * 100.0 ;
AtualizarDadosTela_IMU ( erro_lateral : erroLateral ) ;
}
2025-11-14 17:11:58 +00:00
#endregion
2025-11-24 16:05:50 +00:00
#region HEADING
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
public void AtualizarDadosTela_Heading ( double headingRover , double headingCourse = double . NaN )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
try
{
HDG . Heading = headingRover ;
HDG . CourseHeading = headingCourse ;
}
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI Heading: {exUi.Message}" ) ;
}
} ) ) ;
2025-11-14 17:11:58 +00:00
}
#endregion
2025-11-24 16:05:50 +00:00
#region IMU
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
public void AtualizarDadosTela_IMU ( double? roll = null , double? pitch = null , double? erro_lateral = null )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
try
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
if ( roll ! = null ) IMU . RollDeg = ( double ) roll ;
if ( pitch ! = null ) IMU . PitchDeg = ( double ) pitch ;
if ( erro_lateral ! = null ) IMU . LateralError = ( double ) erro_lateral ;
2025-11-14 17:11:58 +00:00
}
2025-11-24 16:05:50 +00:00
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI IMU: {exUi.Message}" ) ;
}
} ) ) ;
}
#endregion
#region CAMERA FRONTAL
private void IniciarStreamCameraFrontal ( )
{
var path = @"streams\camera_frontal.sdp" ;
if ( ! File . Exists ( path ) )
{
string stream =
"v=0\r\n" +
"o=- 0 0 IN IP4 0.0.0.0\r\n" +
"s=OAK H264\r\n" +
"c=IN IP4 0.0.0.0\r\n" +
"t=0 0\r\n" +
"m=video 5000 RTP/AVP 96\r\n" +
"a=rtpmap:96 H264/90000\r\n" +
"a=fmtp:96 packetization-mode=1\r\n" ;
File . WriteAllText ( path , stream ) ;
2025-11-14 17:11:58 +00:00
}
2025-11-24 16:05:50 +00:00
_mediaFront ? . Dispose ( ) ; // limpa antigo, se existir
_mediaFront = new Media ( _libVLC , path , FromType . FromPath ) ;
// _mediaFront.AddOption(":network-caching=150"); // se quiser por arquivo
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
_playerFront . Play ( _mediaFront ) ;
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
VariaveisControleOperacao . EnviarComandoTransmissaoVideo ( AgroBase . Models . Enums . T_Code . Snr , true ) ;
}
private void PararStreamCameraFrontal ( bool finalizar = false )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
_playerFront ? . Stop ( ) ;
if ( finalizar )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
_mediaFront ? . Dispose ( ) ;
_mediaFront = null ;
_playerFront ? . Dispose ( ) ;
_playerFront = null ;
}
2025-11-14 17:11:58 +00:00
2025-11-24 16:05:50 +00:00
VariaveisControleOperacao . EnviarComandoTransmissaoVideo ( AgroBase . Models . Enums . T_Code . Snr , false ) ;
}
#endregion
#region CAMERA ERVAS
private void IniciarStreamCameraErvas ( )
{
var path = @"streams\camera_ervas.sdp" ;
if ( ! File . Exists ( path ) )
{
string stream =
"v=0\r\n" +
"o=- 0 0 IN IP4 0.0.0.0\r\n" +
"s=OAK H264\r\n" +
"c=IN IP4 0.0.0.0\r\n" +
"t=0 0\r\n" +
"m=video 5002 RTP/AVP 96\r\n" + // <- porta 5001
"a=rtpmap:96 H264/90000\r\n" +
"a=fmtp:96 packetization-mode=1\r\n" ;
File . WriteAllText ( path , stream ) ;
2025-11-14 17:11:58 +00:00
}
2025-11-24 16:05:50 +00:00
_mediaWeed ? . Dispose ( ) ;
_mediaWeed = new Media ( _libVLC , path , FromType . FromPath ) ;
// _mediaWeed.AddOption(":network-caching=150");
_playerWeed . Play ( _mediaWeed ) ;
VariaveisControleOperacao . EnviarComandoTransmissaoVideo ( AgroBase . Models . Enums . T_Code . Cam , true ) ;
}
private void PararStreamCameraErvas ( bool finalizar = false )
{
_playerWeed ? . Stop ( ) ;
if ( finalizar )
2025-11-14 17:11:58 +00:00
{
2025-11-24 16:05:50 +00:00
_mediaWeed ? . Dispose ( ) ;
_mediaWeed = null ;
_playerWeed ? . Dispose ( ) ;
_playerWeed = null ;
2025-11-14 17:11:58 +00:00
}
2025-11-24 16:05:50 +00:00
VariaveisControleOperacao . EnviarComandoTransmissaoVideo ( AgroBase . Models . Enums . T_Code . Cam , false ) ;
}
#endregion
#region PULVERIZADOR
private void btnB0_Click ( object sender , RoutedEventArgs e )
{
btnB0 . Content = ( btnB0 . Content . ToString ( ) = = "Ligar" ) ? "Desligar" : "Ligar" ;
2025-11-14 17:11:58 +00:00
}
#endregion
#region LIDAR
public void AtualizarDadostela_LIDAR ( List < AgroBase . Models . LivoxBboxModel > bboxes )
{
2025-11-24 16:05:50 +00:00
Application . Current . Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
try
{
BBoxes3D . SetBboxes ( bboxes ) ;
}
catch ( Exception exUi )
{
Variaveis . MostrarLog ( $"Erro ao atualizar UI Lidar: {exUi.Message}" ) ;
}
} ) ) ;
2025-11-14 17:11:58 +00:00
}
#endregion
2025-11-12 11:15:07 +00:00
}
}