Envio de dados da camera, ajuste pulverizador, ajuste alertas

This commit is contained in:
Diego Freitas 2026-03-12 11:00:43 -03:00
parent c4cf9a2742
commit 6b72fed408
18 changed files with 325 additions and 188 deletions

View File

@ -1,7 +1,9 @@
using AgroBase.Models;
using AgroBase.Models.Components;
using AgroBase.Models.Operadores;
using AgroBase.Services;
using AgroBase.Services.Operadores;
using Assimp;
using System;
using System.Collections.Generic;
using System.Data;
@ -259,7 +261,15 @@ namespace AgroBase.Forms.IHM
private async Task AtualizarCameras()
{
flwCamerasSolo.Controls.Clear();
ListaCameras = CameraWorkerService.GetListaCameras();
var cameras = CameraWorkerService.GetListaCameras();
ListaCameras = cameras.Select(x => new OAKCameraModel()
{
Index = 0,
Dispositivo = x.dispositivo,
Version = x.versao,
Name = x.modelo,
Id = x.mx_id,
}).ToList();
CameraCaminho = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.Clone();
CamerasSolo = new List<OAKCameraModel>(Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo);

View File

@ -175,7 +175,7 @@ namespace AgroBase.Forms.IHM
var CameraSolo = Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo.Count() > idx ? Variaveis.OperacaoEmAndamento.DispSen.Dados.CamerasSolo[idx] : null;
lbl.ForeColor = modDesconectado || CameraSolo?.Id == null ?
Color.Black
: !listaCameras.Any(x => x.Id == CameraSolo.Id) ?
: !listaCameras.Any(x => x.mx_id == CameraSolo.Id) ?
Color.DarkRed :
Color.DarkGreen;
}
@ -186,7 +186,7 @@ namespace AgroBase.Forms.IHM
var CameraCaminho = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho;
lbl.ForeColor = modDesconectado || CameraCaminho?.Id == null ?
Color.Black
: !listaCameras.Any(x => x.Id == CameraCaminho.Id)
: !listaCameras.Any(x => x.mx_id == CameraCaminho.Id)
? Color.DarkRed
: Color.DarkGreen;
}

View File

@ -41,7 +41,7 @@ namespace AgroBase.Forms
private void AtualizarListaCameras()
{
cmbCameras.Items.Clear();
cmbCameras.Items.AddRange(CameraWorkerService.GetListaCameras().Select(x => x.Name).ToArray());
cmbCameras.Items.AddRange(CameraWorkerService.GetListaCameras().Select(x => x.modelo).ToArray());
cmbCameras.SelectedIndex = -1;
}

View File

@ -1,6 +1,7 @@
using AgroBase.Models.Modules;
using AgroBase.Models.Operadores;
using AgroBase.Services;
using AgroBase.Services.Operadores;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -489,6 +490,8 @@ namespace AgroBase.Models.Operacoes
public LivoxModel LivoxLidar { get; set; }
public DateTime UltimoEnvioLog { get; set; } = DateTime.MinValue;
public List<OperacaoSensoriamentoLogErrosModel> Logs { get; set; }
public List<CameraWorkerItemModel> Cameras { get; set; }
public VisualWorkerResumoModel OperadorVisual { get; set; }
public void Atualizar()
{
@ -765,6 +768,8 @@ namespace AgroBase.Models.Operacoes
};
PerformanceNcp = sen?.DadosPerformance?.Clone();
LivoxLidar = sen?.LivoxLidar?.Clone();
Cameras = CameraWorkerService.ListaCameras;
OperadorVisual = VisualWorkerService.DadosLeitura?.Resumo?.UpdateClone();
var l = sen?.Logs;
Logs = l != null ? l.Where(x => x.Momento >= UltimoEnvioLog).ToList() : new List<OperacaoSensoriamentoLogErrosModel>();
}
@ -792,6 +797,8 @@ namespace AgroBase.Models.Operacoes
PerformanceNcp = PerformanceNcp?.Clone(),
LivoxLidar = LivoxLidar?.Clone(),
Logs = new List<OperacaoSensoriamentoLogErrosModel>(Logs ?? new List<OperacaoSensoriamentoLogErrosModel>()),
Cameras = new List<CameraWorkerItemModel>(Cameras ?? new List<CameraWorkerItemModel>()),
OperadorVisual = OperadorVisual?.Clone(),
UltimoEnvioLog = UltimoEnvioLog
};
}

View File

@ -42,6 +42,13 @@ namespace AgroBase.Models.Operadores
public bool rodando { get; set; }
public bool tem_depth { get; set; }
public bool tem_imu { get; set; }
public bool streaming { get; set; }
public double fps { get; set; }
public int stream_w { get; set; }
public int stream_h { get; set; }
public int stream_kbps { get; set; }
public SaudeWorkerModel saude { get; set; }
public CameraWorkerItemPerformanceModel performance { get; set; }
}

View File

@ -1,4 +1,5 @@
using System;
using AgroBase.Services.Operadores;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
@ -52,6 +53,9 @@ namespace AgroBase.Models.Operadores
public class VisualWorkerResumoModel
{
public StatusCarroMapa StatusCarro { get; set; }
public double ErroLateral { get; set; }
public bool ObstaculoDetectado { get; set; } = false;
public bool DeveParar { get; set; } = false;
public bool EnviarComandoParada { get; set; } = false;
@ -63,6 +67,8 @@ namespace AgroBase.Models.Operadores
{
var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual;
ObstaculoDetectado = (Sonar.Analises?.matriz_confianca?.block?.decision?.parar ?? false);
StatusCarro = Sonar?.Analises?.segmentacao?.status_corredor ?? StatusCarroMapa.Indefinido;
ErroLateral = Sonar?.Analises?.segmentacao?.erro_angular ?? 0;
if (!Variaveis.OperacaoEmAndamento.Parametros.Controle.SonarAtivado)
{
@ -95,6 +101,8 @@ namespace AgroBase.Models.Operadores
{
return new VisualWorkerResumoModel()
{
StatusCarro = StatusCarro,
ErroLateral = ErroLateral,
ObstaculoDetectado = ObstaculoDetectado,
DeveParar = DeveParar,
EnviarComandoParada = EnviarComandoParada,
@ -102,6 +110,12 @@ namespace AgroBase.Models.Operadores
_comandoParadaEnviado = _comandoParadaEnviado,
};
}
public VisualWorkerResumoModel UpdateClone()
{
AtualizarStatusBloqueio(false);
return Clone();
}
}
public class VisualWorkerDadosModel

View File

@ -14,7 +14,8 @@ namespace AgroBase.Services.Operadores
{
public CameraWorkerModel DadosLeitura = new CameraWorkerModel();
public SaudeWorkerModel Saude { get; set; } = new SaudeWorkerModel();
private static List<OAKCameraModel> CamerasConectadas = new List<OAKCameraModel>();
private static List<CameraWorkerItemModel> CamerasConectadas = new List<CameraWorkerItemModel>();
public static List<CameraWorkerItemModel> ListaCameras => CamerasConectadas.Skip(1).ToList();
private bool DebugMode = true;
@ -85,15 +86,14 @@ namespace AgroBase.Services.Operadores
}
public static List<OAKCameraModel> GetListaCameras()
public static List<CameraWorkerItemModel> GetListaCameras()
{
CamerasConectadas = new List<OAKCameraModel>()
CamerasConectadas = new List<CameraWorkerItemModel>()
{
new OAKCameraModel()
new CameraWorkerItemModel()
{
Dispositivo = T_Code.Vzo,
Index = -1,
Name = "Sem Video"
dispositivo = T_Code.Vzo,
modelo = "Sem Video"
}
};
@ -107,13 +107,7 @@ namespace AgroBase.Services.Operadores
var camera_str = RedisService.Get(RedisService.CamKey(cam.Key));
var camera = JsonConvert.DeserializeObject<CameraWorkerItemModel>(camera_str);
if (camera.mx_id == null) continue;
CamerasConectadas.Add(new OAKCameraModel()
{
Dispositivo = camera.dispositivo,
Version = camera.versao,
Name = camera.modelo,
Id = camera.mx_id,
});
CamerasConectadas.Add(camera);
}
}

View File

@ -469,22 +469,22 @@ namespace AgroBase.Services
GeneralJoystick.AtualizaDispositivo();
}
var _Cameras = CameraWorkerService.GetListaCameras().Where(x => x.Index > -1).ToList();
var _Cameras = CameraWorkerService.ListaCameras;
foreach (var cam in _Cameras)
{
var disp = HealthWorkerService.ModulosSaude.FirstOrDefault(x => x.modulo == cam.Dispositivo);
var disp = HealthWorkerService.ModulosSaude.FirstOrDefault(x => x.modulo == cam.dispositivo);
if (disp == null) continue;
var added = DispositivosMapeados.FirstOrDefault(x => x.Dispositivo == cam.Dispositivo);
var added = DispositivosMapeados.FirstOrDefault(x => x.Dispositivo == cam.dispositivo);
bool connected = disp.status != StatusModulo.Desconectado;
if (added == null && connected)
{
DispositivosMapeados.Add(new DispositivoDetalhesModel()
{
Dispositivo = cam.Dispositivo,
Dispositivo = cam.dispositivo,
Endereco = "USB",
Versao = cam.Version
Versao = cam.versao
});
}
else if (added != null && !connected)

View File

@ -102,6 +102,7 @@ namespace OperationControl.Controls
public MapMarkerManager markers;
public event EventHandler<MapMarkerManager.MarkerClickedEventArgs>? MarkerClicked;
private bool SemRoverFocado => VariaveisControleOperacao.RoverEmFoco == null;
protected virtual void OnMarkerClicked(string markerId)
{
if (markerId == null) markers?.ClearMarkerFocused();
@ -532,7 +533,8 @@ namespace OperationControl.Controls
if (info?.Layer == markers.Layer && feat != null && markers != null && markers.TryGetIdFromFeature(feat, out var id))
{
OnMarkerClicked(id);
if (SemRoverFocado)
OnMarkerClicked(id);
}
else if (info?.Layer == MapaPlantacao_Layer && feat is MIFeature roadFeature)
{
@ -556,7 +558,7 @@ namespace OperationControl.Controls
var info = Mapa.GetMapInfo(sp, Mapa.Map.Layers /*, 18*/);
var feat = info?.Feature;
if (feat != null && markers != null && markers.TryGetIdFromFeature(feat, out _))
if (feat != null && markers != null && markers.TryGetIdFromFeature(feat, out _) && SemRoverFocado)
Mapa.Cursor = System.Windows.Input.Cursors.Hand;
else if (feat != null && info?.Layer == MapaPlantacao_Layer && feat is MIFeature roadFeature && ParametrizandoOperacao)
Mapa.Cursor = System.Windows.Input.Cursors.Hand;

View File

@ -79,15 +79,34 @@
</Grid>
<!-- ===================== CENTRO ===================== -->
<Grid Grid.Column="1" Margin="0,0,10,0">
<Grid.RowDefinitions>
<RowDefinition Height="85"/>
<RowDefinition Height="33"/>
</Grid.RowDefinitions>
<DockPanel Grid.Column="1" Margin="0,0,10,0" LastChildFill="True">
<!-- área dos bicos -->
<Grid Grid.Row="0" x:Name="AreaTecnica">
<!-- solo sempre colado embaixo -->
<Grid DockPanel.Dock="Bottom" Height="10" ClipToBounds="True" Margin="8,0,8,0">
<Border Background="{StaticResource CropBrush}" CornerRadius="0,0,8,8"/>
<Border HorizontalAlignment="Left" Background="{StaticResource GroundBrush}" CornerRadius="0,0,8,8">
<Border.Width>
<MultiBinding Converter="{StaticResource RuaWidthFisicaConverter}">
<Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualWidth"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="LarguraRuaCm"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="ComprimentoBarraCm"/>
</MultiBinding>
</Border.Width>
<Border.Margin>
<MultiBinding Converter="{StaticResource RuaOffsetFisicaConverter}">
<Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualWidth"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="ErroLateralCm"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="LarguraRuaCm"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="ComprimentoBarraCm"/>
</MultiBinding>
</Border.Margin>
</Border>
</Grid>
<!-- área dos bicos ocupa o restante -->
<Grid x:Name="AreaTecnica">
<Grid VerticalAlignment="Top">
<Grid.Margin>
<MultiBinding Converter="{StaticResource BarraTopConverter}" ConverterParameter="5">
@ -117,45 +136,21 @@
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:BicoModel}">
<local:BicoLequeIndicator HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Margin="2,0,2,0"
AnguloControle="{Binding AnguloControle}"
OpeningAngle="{Binding OpeningAngle}"
IsCommandOn="{Binding IsCommandOn}"
IsSpraying="{Binding IsSpraying}"
Vazao="{Binding Vazao}"
TempoAtuado="{Binding TempoAtuado}"
Atuacoes="{Binding Atuacoes}" />
VerticalAlignment="Top"
Margin="2,0,2,0"
AnguloControle="{Binding AnguloControle}"
OpeningAngle="{Binding OpeningAngle}"
IsCommandOn="{Binding IsCommandOn}"
IsSpraying="{Binding IsSpraying}"
Vazao="{Binding Vazao}"
TempoAtuado="{Binding TempoAtuado}"
Atuacoes="{Binding Atuacoes}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
<!-- solo -->
<Grid Grid.Row="1" ClipToBounds="True" Margin="8,0,8,0">
<Border Background="{StaticResource CropBrush}" CornerRadius="0,0,8,8"/>
<Border HorizontalAlignment="Left" Background="{StaticResource GroundBrush}" CornerRadius="0,0,8,8">
<Border.Width>
<MultiBinding Converter="{StaticResource RuaWidthFisicaConverter}">
<Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualWidth"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="LarguraRuaCm"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="ComprimentoBarraCm"/>
</MultiBinding>
</Border.Width>
<Border.Margin>
<MultiBinding Converter="{StaticResource RuaOffsetFisicaConverter}">
<Binding RelativeSource="{RelativeSource AncestorType=Grid}" Path="ActualWidth"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="ErroLateralCm"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="LarguraRuaCm"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ctrl:PulverizadorIndicator}}" Path="ComprimentoBarraCm"/>
</MultiBinding>
</Border.Margin>
</Border>
</Grid>
</Grid>
</DockPanel>
<!-- ===================== DIREITA ===================== -->
<Grid Grid.Column="2">

View File

@ -17,7 +17,8 @@ namespace OperationControl.Models
public T_Code Modulo { get; set; } // ex: "DIR", "SEN", "MOV"
public string? Mod_ID { get; set; } // ID
public string Mensagem { get; set; } // texto principal
public DateTime Timestamp { get; set; } // opcional: hora do evento
public DateTime CriadoEm { get; set; }
public DateTime? AtualizadoEm { get; set; }
// Campo calculado para título (ex: "DIR — Falha no encoder")

View File

@ -36,7 +36,7 @@ namespace OperationControl.Models
Dock = new DockWindow();
Dock.Show();
if (Mock)
if (Mock && false)
{
Task.Run(async () =>
{

View File

@ -1,13 +1,133 @@
using OperationControl.ViewModels.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using static AgroBase.Models.Enums;
namespace OperationControl.ViewModels.Views.Operacao.Monitoramento
{
public class MonitoramentoViewModel : NotifyBase
public class FrameTipoItem
{
public TipoFrameCamera Valor { get; set; }
public string Nome { get; set; }
public override string ToString() => Nome;
}
public class MonitoramentoViewModel : INotifyPropertyChanged
{
public ObservableCollection<FrameTipoItem> CameraFrontalFrameTipos { get; set; }
public ObservableCollection<FrameTipoItem> CameraErvasFrameTipos { get; set; }
private FrameTipoItem _cameraFrontalFrameTipoSelecionado;
public FrameTipoItem CameraFrontalFrameTipoSelecionado
{
get => _cameraFrontalFrameTipoSelecionado;
set
{
if (_cameraFrontalFrameTipoSelecionado != value)
{
_cameraFrontalFrameTipoSelecionado = value;
OnPropertyChanged();
CameraFrontalFrameTipoAlterado?.Invoke(value?.Valor ?? default);
}
}
}
private FrameTipoItem _cameraErvasFrameTipoSelecionado;
public FrameTipoItem CameraErvasFrameTipoSelecionado
{
get => _cameraErvasFrameTipoSelecionado;
set
{
if (_cameraErvasFrameTipoSelecionado != value)
{
_cameraErvasFrameTipoSelecionado = value;
OnPropertyChanged();
CameraErvasFrameTipoAlterado?.Invoke(value?.Valor ?? default);
}
}
}
public string CameraFrontalTitulo { get; } = "Camera Frontal";
private string _cameraFrontalStatus = "Sem dados";
public string CameraFrontalStatus
{
get => _cameraFrontalStatus;
set
{
_cameraFrontalStatus = value;
OnPropertyChanged();
}
}
public string CameraErvasTitulo { get; } = "Camera Ervas";
private string _cameraErvasStatus = "Sem dados";
public string CameraErvasStatus
{
get => _cameraErvasStatus;
set
{
_cameraErvasStatus = value;
OnPropertyChanged();
}
}
private bool _cameraFrontalSemSinal = true;
public bool CameraFrontalSemSinal
{
get => _cameraFrontalSemSinal;
set
{
_cameraFrontalSemSinal = value;
OnPropertyChanged();
}
}
private bool _cameraErvasSemSinal = true;
public bool CameraErvasSemSinal
{
get => _cameraErvasSemSinal;
set
{
_cameraErvasSemSinal = value;
OnPropertyChanged();
}
}
public event Action<TipoFrameCamera>? CameraFrontalFrameTipoAlterado;
public event Action<TipoFrameCamera>? CameraErvasFrameTipoAlterado;
public MonitoramentoViewModel()
{
var itens = Enum.GetValues(typeof(TipoFrameCamera))
.Cast<TipoFrameCamera>()
.Select(x => new FrameTipoItem
{
Valor = x,
Nome = x.ToString()
})
.ToList();
CameraFrontalFrameTipos = new ObservableCollection<FrameTipoItem>(itens);
CameraErvasFrameTipos = new ObservableCollection<FrameTipoItem>(itens);
CameraFrontalFrameTipoSelecionado = CameraFrontalFrameTipos.FirstOrDefault();
CameraErvasFrameTipoSelecionado = CameraErvasFrameTipos.FirstOrDefault();
}
public void AtualizarDadosCameras(double? cf_fps, double? cf_kbps, double? ct_fps, double? ct_kbps, StatusCarroMapa? status_carro, double? pct_ervas)
{
CameraFrontalStatus = $"{cf_fps ?? 0} fps • {cf_kbps ?? 0} kbps • {status_carro ?? StatusCarroMapa.Indefinido}";
CameraErvasStatus = $"{ct_fps ?? 0} fps • {ct_kbps ?? 0} kbps • {pct_ervas ?? 0:F2}% ervas";
CameraFrontalSemSinal = (cf_kbps ?? 0) == 0;
CameraErvasSemSinal = (ct_kbps ?? 0) == 0;
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@ -82,7 +82,7 @@ namespace OperationControl.ViewModels.Views.Operacao
Mod_ID = modId,
Severidade = severidade,
Mensagem = mensagem,
Timestamp = DateTime.Now
CriadoEm = DateTime.Now
};
AlertasAtivos.Add(alerta);
@ -187,6 +187,11 @@ namespace OperationControl.ViewModels.Views.Operacao
{
App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
string GerarChave(string roverId, T_Code modulo, string? modId) => $"{roverId}|{modulo}|{modId ?? ""}";
var agora = DateTime.Now;
// 1) Gera os alertas que DEVERIAM existir neste momento
var alertasEsperados = new List<AlertaModel>();
foreach (var rover in VariaveisControleOperacao.RoversNaRede)
@ -210,8 +215,8 @@ namespace OperationControl.ViewModels.Views.Operacao
Modulo = mod.modulo,
Mod_ID = ind.label,
Severidade = DeParaStatus(ind.status, ind.em_uso),
Mensagem = string.Join(", ", ind.motivos ?? new List<string>()),
Timestamp = DateTime.Now
Mensagem = string.Join(", ", ind.motivos ?? new List<string>())
// NÃO define Timestamp aqui ainda
});
}
}
@ -229,64 +234,75 @@ namespace OperationControl.ViewModels.Views.Operacao
Modulo = mod.modulo,
Mod_ID = null,
Severidade = DeParaStatus(mod.status, mandatorio),
Mensagem = string.Join(", ", mod.motivos ?? new List<string>()),
Timestamp = DateTime.Now
Mensagem = string.Join(", ", mod.motivos ?? new List<string>())
// NÃO define Timestamp aqui ainda
});
}
}
}
string GerarChave(string roverId, T_Code modulo, string? modId)
=> $"{roverId}|{modulo}|{modId ?? ""}";
var mapaEsperado = alertasEsperados.ToDictionary(
x => GerarChave(x.Rover_ID, x.Modulo, x.Mod_ID),
x => x);
var alertasAtuais = AlertasAtivos.ToList();
var mapaAtual = AlertasAtivos.ToDictionary(
x => GerarChave(x.Rover_ID, x.Modulo, x.Mod_ID),
x => x);
// Remove os alertas que não deveriam mais existir
foreach (var atual in alertasAtuais)
// 2) Remove os alertas que não existem mais
var chavesParaRemover = mapaAtual.Keys
.Where(chave => !mapaEsperado.ContainsKey(chave))
.ToList();
foreach (var chave in chavesParaRemover)
{
var chave = GerarChave(atual.Rover_ID, atual.Modulo, atual.Mod_ID);
if (!mapaEsperado.ContainsKey(chave))
{
AlertasAtivos.Remove(atual);
var alertaRemover = mapaAtual[chave];
if (ToastAlerta == atual)
LimparToast();
}
AlertasAtivos.Remove(alertaRemover);
if (ToastAlerta == alertaRemover)
LimparToast();
}
// Adiciona ou atualiza os alertas esperados
foreach (var esperado in alertasEsperados)
// 3) Adiciona novos ou atualiza os existentes
foreach (var kv in mapaEsperado)
{
var existente = AlertasAtivos.FirstOrDefault(x =>
x.Rover_ID == esperado.Rover_ID &&
x.Modulo == esperado.Modulo &&
x.Mod_ID == esperado.Mod_ID);
var chave = kv.Key;
var esperado = kv.Value;
if (existente == null)
if (!mapaAtual.TryGetValue(chave, out var existente))
{
// Novo alerta
esperado.CriadoEm = agora;
AlertasAtivos.Add(esperado);
MostrarToast(esperado);
}
else
{
// Alerta já existia -> mantém Timestamp original
bool mudou =
existente.Mensagem != esperado.Mensagem ||
existente.Severidade != esperado.Severidade;
existente.Mensagem = esperado.Mensagem;
existente.Severidade = esperado.Severidade;
if (mudou)
{
existente.Timestamp = DateTime.Now;
existente.Mensagem = esperado.Mensagem;
existente.Severidade = esperado.Severidade;
// Se quiser que Timestamp represente "última mudança", mantém isso:
existente.AtualizadoEm = agora;
MostrarToast(existente);
}
}
}
var ordenado = AlertasAtivos.OrderByDescending(x => x.AtualizadoEm ?? x.CriadoEm).ToList();
AlertasAtivos.Clear();
foreach (var a in ordenado)
{
AlertasAtivos.Add(a);
}
}));
}

View File

@ -9,7 +9,6 @@ using AgroBase.Models.Operacoes;
using Newtonsoft.Json.Linq;
using OperationControl.ViewModels.Views.Operacao;
using OperationControl.Controls;
using static OpenTK.Graphics.OpenGL.GL;
namespace OperationControl.ViewModels
{
@ -528,15 +527,17 @@ namespace OperationControl.ViewModels
var obj = rover.DadosLeitura;
if (obj == null) return;
// MAPA
var simulacao = obj.Controle?.SimulacaoMPC?.Select(x => new double[] { x.longitude, x.latitude })?.ToList();
_viewOperacaoCenter?.Mapa?.markers.UpdateMarkerPosition(rover.RoverId, lat: obj.Gnss?.Latitude, lon: obj.Gnss?.Longitude, heading: obj.Gnss?.OrientacaoReal, predict: simulacao);
_viewOperacaoCenter?.Mapa?.markers.UpdateMarkerInfo(rover.RoverId, status: obj.Operacao?.Status ?? AgroBase.Models.Enums.StatusOperacao.NaoIniciado);
//_viewOperacaoBottom?._vm?.AtualizarListaAlertasRoverSelecionado(rover.RoverId);
// ALERTAS
_viewOperacaoBottom?._vm?.AtualizarListaAlertasTodosRovers();
if (rover.RoverId != VariaveisControleOperacao.SelectedRoverId) return;
// ESQUERDA
var conexao = obj.ModulosSaude?.FirstOrDefault(x => x.modulo == AgroBase.Models.Enums.T_Code.Ipb);
double c = conexao?.saude ?? 0;
double conexao_latencia = conexao?.detalhes?["avg_rtt"]?.Value<double>() ?? 0.0;
@ -557,6 +558,7 @@ namespace OperationControl.ViewModels
$"{obj.Gnss?.QualidadeFix ?? AgroBase.Models.Enums.TiposCorrecaoGPS.SemCorrecao} {obj.Gnss?.PrecisaoCm:F2} cm"
);
// DIREITA (RESUMO)
_viewOperacaoRight?._vm?.AtualizarResumo(r =>
{
r.Operacao = (obj.Operacao?.Liberada ?? false) ? "🔓 Liberada" : "🔒 Bloqueada";
@ -580,14 +582,16 @@ namespace OperationControl.ViewModels
r.InfestacaoPercentual = obj.Atuador?.PercentualErvasTerreno ?? 0;
});
// DIAGNOSTICO
_viewOperacaoCenter?.Diagnostico?._vm?.AtualizarDados(rover);
_viewOperacaoCenter?.Diagnostico?.AtualizarGraficos();
// PULVERIZADOR
var bomba = obj.Atuador?.Bombas?.FirstOrDefault();
var ATU = _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.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;
@ -615,6 +619,11 @@ namespace OperationControl.ViewModels
bico.Atuacoes = _bc.QtdAtuacoes;
bico.TempoAtuado = _bc.TempoAtuado / 1000.0;
}
// CAMERAS
var camera_snr = obj?.Cameras?.FirstOrDefault(x => x.dispositivo == AgroBase.Models.Enums.T_Code.Snr);
var camera_cam = obj?.Cameras?.FirstOrDefault(x => x.dispositivo == AgroBase.Models.Enums.T_Code.Cam);
_viewOperacaoCenter?.Monitoramento?._vm?.AtualizarDadosCameras(camera_snr?.fps, camera_snr?.stream_kbps, camera_cam?.fps, camera_cam?.stream_kbps, obj?.OperadorVisual?.StatusCarro, obj?.Atuador?.PercentualErvasNoRadar);
}

View File

@ -9,6 +9,10 @@
d:DesignHeight="900"
d:DesignWidth="1600">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</UserControl.Resources>
<Grid Margin="12">
<Grid.RowDefinitions>
<RowDefinition Height="2.2*"/>
@ -21,13 +25,7 @@
</Grid.ColumnDefinitions>
<!-- CÂMERA FRONTAL -->
<Border Grid.Row="0" Grid.Column="0"
Margin="0,0,6,6"
Padding="10"
CornerRadius="10"
Background="#202020"
BorderBrush="#353535"
BorderThickness="1">
<Border Grid.Row="0" Grid.Column="0" Margin="0,0,6,6" Padding="10" CornerRadius="10" Background="#202020" BorderBrush="#353535" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
@ -36,49 +34,23 @@
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Margin="0,0,0,8">
<TextBlock Text="Câmera frontal"
DockPanel.Dock="Left"
FontSize="12"
FontWeight="SemiBold"
Foreground="#E6E6E6"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding CameraFrontalTitulo}" DockPanel.Dock="Left" FontSize="12" FontWeight="SemiBold" Foreground="#E6E6E6" VerticalAlignment="Center" Margin="0,0,10,0"/>
<ComboBox x:Name="cmbCameraFrontalFrameTipo"
Width="130"
Height="26"
HorizontalAlignment="Right"
SelectionChanged="cmbCameraFrontalFrameTipo_SelectionChanged"/>
<ComboBox Width="130" Height="26" HorizontalAlignment="Right" ItemsSource="{Binding CameraFrontalFrameTipos}" SelectedItem="{Binding CameraFrontalFrameTipoSelecionado, Mode=TwoWay}" DisplayMemberPath="Nome"/>
<TextBlock Text="12 fps • 230 kbps • CaminhandoRua"
Margin="12,0,8,0"
Foreground="#AFAFAF"
FontSize="11"
VerticalAlignment="Center"
HorizontalAlignment="Right"/>
<TextBlock Text="{Binding CameraFrontalStatus}" Margin="12,0,8,0" Foreground="#AFAFAF" FontSize="11" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</DockPanel>
<Grid Grid.Row="1" Background="Black">
<TextBlock Text="SEM SINAL"
Foreground="Gray"
FontSize="18"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<TextBlock Text="SEM SINAL" Foreground="Gray" FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding CameraFrontalSemSinal, Converter={StaticResource BoolToVisibilityConverter}}"/>
<Image x:Name="imgVideoFront"
Stretch="Uniform"
RenderOptions.BitmapScalingMode="LowQuality"/>
<Image x:Name="imgVideoFront" Stretch="Uniform" RenderOptions.BitmapScalingMode="LowQuality"/>
</Grid>
</Grid>
</Border>
<!-- CÂMERA DE ERVAS -->
<Border Grid.Row="0" Grid.Column="1"
Margin="6,0,0,6"
Padding="10"
CornerRadius="10"
Background="#202020"
BorderBrush="#353535"
BorderThickness="1">
<Border Grid.Row="0" Grid.Column="1" Margin="6,0,0,6" Padding="10" CornerRadius="10" Background="#202020" BorderBrush="#353535" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
@ -87,15 +59,15 @@
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Margin="0,0,0,8">
<TextBlock Text="Câmera de ervas" DockPanel.Dock="Left" FontSize="12" FontWeight="SemiBold" Foreground="#E6E6E6" VerticalAlignment="Center"/>
<TextBlock Text="{Binding CameraErvasTitulo}" DockPanel.Dock="Left" FontSize="12" FontWeight="SemiBold" Foreground="#E6E6E6" VerticalAlignment="Center" Margin="0,0,10,0"/>
<ComboBox x:Name="cmbCameraTraseiraFrameTipo" Width="130" Height="26" HorizontalAlignment="Right" SelectionChanged="cmbCameraTraseiraFrameTipo_SelectionChanged"/>
<ComboBox Width="130" Height="26" HorizontalAlignment="Right" ItemsSource="{Binding CameraErvasFrameTipos}" SelectedItem="{Binding CameraErvasFrameTipoSelecionado, Mode=TwoWay}" DisplayMemberPath="Nome"/>
<TextBlock Text="10 fps • 193 kbps • 5,41% erva • 0,00% cana" Margin="12,0,8,0" Foreground="#AFAFAF" FontSize="11" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBlock Text="{Binding CameraErvasStatus}" Margin="12,0,8,0" Foreground="#AFAFAF" FontSize="11" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</DockPanel>
<Grid Grid.Row="1" Background="Black">
<TextBlock Text="SEM SINAL" Foreground="Gray" FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="SEM SINAL" Foreground="Gray" FontSize="18" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding CameraErvasSemSinal, Converter={StaticResource BoolToVisibilityConverter}}"/>
<Image x:Name="imgVideoWeed" Stretch="Uniform" RenderOptions.BitmapScalingMode="LowQuality"/>
</Grid>
@ -120,18 +92,12 @@
<!-- ÁREA DIREITA INFERIOR -->
<Grid Grid.Row="1" Grid.Column="1" Margin="6,6,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="1.15*"/>
<RowDefinition Height="0.85*"/>
<RowDefinition Height="1.45*"/>
<RowDefinition Height="0.55*"/>
</Grid.RowDefinitions>
<!-- PULVERIZADOR -->
<Border Grid.Row="0"
Margin="0,0,0,6"
Padding="10"
CornerRadius="10"
Background="#202020"
BorderBrush="#353535"
BorderThickness="1">
<Border Grid.Row="0" Margin="0,0,0,6" Padding="10" CornerRadius="10" Background="#202020" BorderBrush="#353535" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
@ -139,17 +105,9 @@
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="Pulverizador"
Margin="0,0,0,8"
FontSize="12"
FontWeight="SemiBold"
Foreground="#E6E6E6"/>
<TextBlock Grid.Row="0" Text="Pulverizador" Margin="0,0,0,8" FontSize="12" FontWeight="SemiBold" Foreground="#E6E6E6"/>
<controls:PulverizadorIndicator Grid.Row="1"
x:Name="areaPulverizador"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<controls:PulverizadorIndicator Grid.Row="1" x:Name="areaPulverizador" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
</Border>
@ -164,7 +122,7 @@
<TextBlock Grid.Row="0" Text="Ações rápidas" Margin="0,0,0,8" FontSize="12" FontWeight="SemiBold" Foreground="#E6E6E6"/>
<UniformGrid Grid.Row="1" Rows="2" Columns="2" Margin="0,4,0,0">
<UniformGrid Grid.Row="1" Rows="1" Columns="4" Margin="0,4,0,0">
<Button Content="Retorno à base" Margin="4"/>
<Button Content="Parametrização" Margin="4"/>
<Button Content="Pausar operação" Margin="4"/>

View File

@ -1,7 +1,6 @@
using OperationControl.Controls;
using OperationControl.Models;
using OperationControl.ViewModels.Views.Operacao.Monitoramento;
using System.Windows.Controls;
namespace OperationControl.Views.Operacao
{
@ -25,15 +24,8 @@ namespace OperationControl.Views.Operacao
videoFront = new TcpVideoReceiver(5000, imgVideoFront);
videoWeed = new TcpVideoReceiver(5002, imgVideoWeed);
cmbCameraFrontalFrameTipo.Items.Clear();
foreach (var m in Enum.GetNames(typeof(AgroBase.Models.Enums.TipoFrameCamera)))
cmbCameraFrontalFrameTipo.Items.Add(m);
cmbCameraFrontalFrameTipo.SelectedIndex = 0;
cmbCameraTraseiraFrameTipo.Items.Clear();
foreach (var m in Enum.GetNames(typeof(AgroBase.Models.Enums.TipoFrameCamera)))
cmbCameraTraseiraFrameTipo.Items.Add(m);
cmbCameraTraseiraFrameTipo.SelectedIndex = 0;
_vm.CameraFrontalFrameTipoAlterado += _vm_CameraFrontalFrameTipoAlterado;
_vm.CameraErvasFrameTipoAlterado += _vm_CameraErvasFrameTipoAlterado;
}
#region MAPA
@ -52,9 +44,11 @@ namespace OperationControl.Views.Operacao
#region CAMERA FRONTAL
private void cmbCameraFrontalFrameTipo_SelectionChanged(object sender, SelectionChangedEventArgs e)
private void _vm_CameraFrontalFrameTipoAlterado(AgroBase.Models.Enums.TipoFrameCamera tipo)
{
if (!IsLoaded) return;
if (VariaveisControleOperacao.SelectedRoverId == VariaveisControleOperacao.BaseMarkerID) return;
PararStreamCameraFrontal();
IniciarStreamCameraFrontal();
}
@ -62,22 +56,28 @@ namespace OperationControl.Views.Operacao
public void IniciarStreamCameraFrontal()
{
videoFront.Iniciar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, true, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraFrontalFrameTipo.SelectedIndex));
var tipo = _vm.CameraFrontalFrameTipoSelecionado?.Valor ?? AgroBase.Models.Enums.TipoFrameCamera.Rgb;
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, true, tipo);
//_vm.CameraFrontalSemSinal = false;
}
public void PararStreamCameraFrontal(bool finalizar = false)
{
videoFront.Parar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, false, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraFrontalFrameTipo.SelectedIndex));
var tipo = _vm.CameraFrontalFrameTipoSelecionado?.Valor ?? AgroBase.Models.Enums.TipoFrameCamera.Rgb;
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Snr, false, tipo);
//_vm.CameraFrontalSemSinal = true;
}
#endregion
#region CAMERA ERVAS
private void cmbCameraTraseiraFrameTipo_SelectionChanged(object sender, SelectionChangedEventArgs e)
private void _vm_CameraErvasFrameTipoAlterado(AgroBase.Models.Enums.TipoFrameCamera tipo)
{
if (!IsLoaded) return;
if (VariaveisControleOperacao.SelectedRoverId == VariaveisControleOperacao.BaseMarkerID) return;
PararStreamCameraErvas();
IniciarStreamCameraErvas();
}
@ -85,13 +85,17 @@ namespace OperationControl.Views.Operacao
public void IniciarStreamCameraErvas()
{
videoWeed.Iniciar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, true, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraTraseiraFrameTipo.SelectedIndex));
var tipo = _vm.CameraErvasFrameTipoSelecionado?.Valor ?? AgroBase.Models.Enums.TipoFrameCamera.Rgb;
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, true, tipo);
//_vm.CameraErvasSemSinal = false;
}
public void PararStreamCameraErvas(bool finalizar = false)
{
videoWeed.Parar();
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, false, ((AgroBase.Models.Enums.TipoFrameCamera)cmbCameraTraseiraFrameTipo.SelectedIndex));
var tipo = _vm.CameraErvasFrameTipoSelecionado?.Valor ?? AgroBase.Models.Enums.TipoFrameCamera.Rgb;
VariaveisControleOperacao.EnviarComandoTransmissaoVideo(AgroBase.Models.Enums.T_Code.Cam, false, tipo);
//_vm.CameraErvasSemSinal = true;
}
#endregion

View File

@ -2117,7 +2117,7 @@ namespace OperationControl.Windows
Modulo = Modulo,
Mod_ID = Mod_ID,
Mensagem = Mensagem,
Timestamp = DateTime.Now,
CriadoEm = DateTime.Now,
};
AlertasAtivos.Add(alerta);