diff --git a/AgroBase/AgroBase/Forms/IHM/Operacao/ucOperacaoPulverizador.cs b/AgroBase/AgroBase/Forms/IHM/Operacao/ucOperacaoPulverizador.cs index 442339e01..dbe951b31 100644 --- a/AgroBase/AgroBase/Forms/IHM/Operacao/ucOperacaoPulverizador.cs +++ b/AgroBase/AgroBase/Forms/IHM/Operacao/ucOperacaoPulverizador.cs @@ -215,9 +215,12 @@ namespace AgroBase.Forms.IHM.Operacao card.VazaoMedia = bico.MediaNivelFluxo; card.TempoLigado = FuncoesIHM.FormatarTempo(tempoLigado); - card.CommandOn = bico.Atuado; card.ReadOn = bico.StatusBico; card.State = saude?.saude_individual?.FirstOrDefault(x => x.label == bico.ID)?.status ?? StatusModulo.Desconectado; + + bool automatico = (op?.Parametros?.Controle?.PulverizadorAutomatico ?? false) && !(op?.Parametros?.Controle.ControleManualAcionado ?? false); + if (automatico) + card.CommandOn = bico.Atuado; } } @@ -281,6 +284,9 @@ namespace AgroBase.Forms.IHM.Operacao private void BtnLigarBomba_Click(object sender, EventArgs e) { + if (op?.Parametros?.Controle?.PulverizadorAutomatico ?? false) + return; + if (!(op?.Parametros?.Controle?.ControleManualAcionado ?? false)) return; @@ -305,6 +311,9 @@ namespace AgroBase.Forms.IHM.Operacao private void BtnDesligarBomba_Click(object sender, EventArgs e) { + if (op?.Parametros?.Controle?.PulverizadorAutomatico ?? false) + return; + if (!(op?.Parametros?.Controle?.ControleManualAcionado ?? false)) return; @@ -320,6 +329,9 @@ namespace AgroBase.Forms.IHM.Operacao private void BtnLigarAgitador_Click(object sender, EventArgs e) { + if (op?.Parametros?.Controle?.PulverizadorAutomatico ?? false) + return; + if (!(op?.Parametros?.Controle?.ControleManualAcionado ?? false)) return; @@ -346,6 +358,9 @@ namespace AgroBase.Forms.IHM.Operacao private void BtnDesligarAgitador_Click(object sender, EventArgs e) { + if (op?.Parametros?.Controle?.PulverizadorAutomatico ?? false) + return; + if (!(op?.Parametros?.Controle?.ControleManualAcionado ?? false)) return; @@ -367,6 +382,9 @@ namespace AgroBase.Forms.IHM.Operacao if (card == null) return; + if (op?.Parametros?.Controle?.PulverizadorAutomatico ?? false) + return; + if (!(op?.Parametros?.Controle?.ControleManualAcionado ?? false)) return; @@ -380,8 +398,7 @@ namespace AgroBase.Forms.IHM.Operacao bico, CanMessagePosicaoDados.Command1, setarControle: true, - status: - card.CommandOn + status: card.CommandOn ); } diff --git a/AgroBase/AgroBase/Forms/IHM/frmAjustes.cs b/AgroBase/AgroBase/Forms/IHM/frmAjustes.cs index d7fc18ce9..5be709c09 100644 --- a/AgroBase/AgroBase/Forms/IHM/frmAjustes.cs +++ b/AgroBase/AgroBase/Forms/IHM/frmAjustes.cs @@ -1,7 +1,6 @@ using AgroBase.Forms.IHM.Controls; using AgroBase.Forms.IHM.Operacao.Parametros.Controls; using AgroBase.Models; -using AgroBase.Models.Components; using AgroBase.Services.Operadores; using System; using System.Collections.Generic; @@ -46,7 +45,7 @@ namespace AgroBase.Forms.IHM } catch (Exception ex) { - MostrarErro("Erro ao carregar ajustes", ex); + FuncoesIHM.MostrarAlerta("Erro ao carregar ajustes", ex.Message, MessageBoxIcon.Error); } finally { @@ -186,27 +185,16 @@ namespace AgroBase.Forms.IHM { var camerasWorker = CameraWorkerService.ListaCameras; - ListaCameras = - camerasWorker - .Select( - x => new OAKCameraModel() - { - Index = - camerasWorker.IndexOf(x), - - Dispositivo = - x.dispositivo, - - Version = - x.versao, - - Name = - x.modelo, - - Id = - x.mx_id - }) - .ToList(); + ListaCameras = camerasWorker + .Select(x => new OAKCameraModel() + { + Index = camerasWorker.IndexOf(x), + Dispositivo = x.dispositivo, + Version = x.versao, + Name = x.modelo, + Id = x.mx_id + }) + .ToList(); CameraCaminho = CameraWorkerService.CameraCaminho?.Clone(); @@ -313,17 +301,7 @@ namespace AgroBase.Forms.IHM if (!emUso) return camera; - CustomDialog.ShowDialog( - this, - "Câmera indisponível", - "A câmera " + - camera.Description + - " já está configurada em outro processo.", - MessageBoxIcon.Warning, - new Dictionary<(string, object), Action>() - { - { ("OK", null), delegate { } } - }); + FuncoesIHM.MostrarAlerta("Câmera indisponível", $"A câmera {camera.Description} já está configurada em outro processo.", MessageBoxIcon.Warning); return new OAKCameraModel(); } @@ -378,7 +356,7 @@ namespace AgroBase.Forms.IHM List interfaces = EthernetService.ObterNomesInterfaces().Where(x => x.Contains("AG")).ToList(); redeHaLow.DefinirInterfaces(interfaces, parametros.comunicacao_interface); - redeHaLow.Ip = parametros.base_ip; + redeHaLow.Ip = parametros.rover_ip; redeHaLow.Mask = parametros.rover_submask; redeHaLow.Gateway = parametros.rover_gateway; redeCan.DefinirInterfaces(interfaces, parametros.can_interface); @@ -430,20 +408,7 @@ namespace AgroBase.Forms.IHM await Task.CompletedTask; - CustomDialog.ShowDialog( - this, - "Teste de comunicação", - "WiFi HaLow: " + - (haLowOk ? "OK" : "Falha") + - "\r\nCAN Ethernet: " + - (canOk ? "OK" : "Falha"), - haLowOk && canOk - ? MessageBoxIcon.Information - : MessageBoxIcon.Warning, - new Dictionary<(string, object), Action>() - { - { ("OK", null), delegate { } } - }); + FuncoesIHM.MostrarAlerta("Teste de comunicação", "WiFi HaLow: " + (haLowOk ? "OK" : "Falha") + "\r\nCAN Ethernet: " + (canOk ? "OK" : "Falha"), haLowOk && canOk ? MessageBoxIcon.Information : MessageBoxIcon.Warning); } private static bool ValidarRede(HmiNetworkEditor editor, bool permitirVazio) @@ -486,59 +451,6 @@ namespace AgroBase.Forms.IHM fldAnguloLateral.Value = ParaDecimal(ObterParametroGlobal(parametros, 0D, "AnguloInclinacaoPitchMax", "PitchMax", "pitchmax")); } - private async Task SalvarTudoAsync() - { - if (!ValidarParametros()) - return; - - object parametros = VariaveisEquipamento.Parametros; - - SalvarParametrosRede(parametros); - SalvarParametrosFisicos(parametros); - - CameraCaminho = CameraCaminho ?? new OAKCameraModel(); - - if (CamerasSolo == null) - CamerasSolo = new List(); - - if (CamerasSolo.Count == 0) - CamerasSolo.Add(new OAKCameraModel()); - - DefinirParametro(parametros, CameraCaminho.Id, "camera_caminhho_id"); - DefinirParametro(parametros, CamerasSolo.Select(x => x?.Id).ToList(), "camera_ervas_id"); - - bool redesOk = await AplicarConfiguracoesRedeAsync(parametros); - - bool salvo = redesOk && VariaveisEquipamento.Parametros.SalvarParametros(); - - if (!salvo) - { - CustomDialog.ShowDialog( - this, - "Erro", - "Não foi possível salvar todos os parâmetros do equipamento.", - MessageBoxIcon.Error, - new Dictionary<(string, object), Action>() - { - { ("OK", null), delegate { } } - }); - - return; - } - - DefinirAlteracoesPendentes(false); - - CustomDialog.ShowDialog( - this, - "Parâmetros salvos", - "As configurações do equipamento foram salvas com sucesso.", - MessageBoxIcon.Information, - new Dictionary<(string, object), Action>() - { - { ("OK", null), delegate { } } - }); - } - private void SalvarParametrosRede(object parametros) { DefinirParametro(parametros, cameraFrontal.PortaTcp, "base_porta_caminho"); @@ -648,15 +560,7 @@ namespace AgroBase.Forms.IHM private void MostrarValidacao(string mensagem) { - CustomDialog.ShowDialog( - this, - "Parâmetros inválidos", - mensagem, - MessageBoxIcon.Warning, - new Dictionary<(string, object), Action>() - { - { ("OK", null), delegate { } } - }); + FuncoesIHM.MostrarAlerta("Parâmetros inválidos", mensagem, MessageBoxIcon.Warning); } #endregion @@ -819,6 +723,46 @@ namespace AgroBase.Forms.IHM #endregion + private async Task SalvarTudoAsync() + { + if (!ValidarParametros()) + return; + + VariaveisEquipamento.ParametrosConfigEquipamentoModel parametros = VariaveisEquipamento.Parametros; + + SalvarParametrosRede(parametros); + SalvarParametrosFisicos(parametros); + + CameraCaminho = CameraCaminho ?? new OAKCameraModel(); + + if (CamerasSolo == null) + CamerasSolo = new List(); + + if (CamerasSolo.Count == 0) + CamerasSolo.Add(new OAKCameraModel()); + + DefinirParametro(parametros, CameraCaminho.Id, "camera_caminhho_id"); + DefinirParametro(parametros, CamerasSolo.Select(x => x?.Id).ToList(), "camera_ervas_id"); + + bool redesOk = await AplicarConfiguracoesRedeAsync(parametros); + + bool salvo = redesOk && parametros.SalvarParametros(); + + if (!salvo) + { + FuncoesIHM.MostrarAlerta("Erro", "Não foi possível salvar todos os parâmetros do equipamento.", MessageBoxIcon.Error); + return; + } + + DefinirAlteracoesPendentes(false); + + parametros.AtualizarParametros(); + HealthWorkerService.AtualizarDadosOperacao(); + + FuncoesIHM.MostrarAlerta("Parâmetros salvos", "As configurações do equipamento foram salvas com sucesso.", MessageBoxIcon.Information); + } + + private void DefinirAlteracoesPendentes(bool pendente) { _alteracoesPendentes = pendente; @@ -853,23 +797,10 @@ namespace AgroBase.Forms.IHM } catch (Exception ex) { - MostrarErro(mensagem, ex); + FuncoesIHM.MostrarAlerta(mensagem, ex.Message, MessageBoxIcon.Error); } } - private void MostrarErro(string titulo, Exception ex) - { - CustomDialog.ShowDialog( - this, - titulo, - ex.Message, - MessageBoxIcon.Error, - new Dictionary<(string, object), Action>() - { - { ("OK", null), delegate { } } - }); - } - } } diff --git a/AgroBase/AgroBase/Forms/IHM/frmDiagnosticos.cs b/AgroBase/AgroBase/Forms/IHM/frmDiagnosticos.cs index c4ead2e83..982d138a7 100644 --- a/AgroBase/AgroBase/Forms/IHM/frmDiagnosticos.cs +++ b/AgroBase/AgroBase/Forms/IHM/frmDiagnosticos.cs @@ -303,13 +303,15 @@ namespace AgroBase.Forms.IHM bool mandatorio = false; bool em_uso = false; + bool badge_principal = badge.ModuleSubCode == S_Code.sVZO && !SerialService.DispositivosMultiplos.Contains(badge.ModuleCode); + OperacaoModulosMandatoriosModel mod = op?.Parametros?.ModulosMandatorios?.FirstOrDefault(x => x.Dispositivo == badge.ModuleCode); var mods_saude = op?.Sensoriamento?.OperadorSaude?.ModulosSaude; var _saude = mods_saude?.FirstOrDefault(x => x.modulo == badge.ModuleCode); if (_saude != null) { - if (badge.ModuleSubCode == S_Code.sVZO) + if (badge_principal) { status = _saude.status; mandatorio = mod?.Mandatorio ?? false; @@ -317,7 +319,7 @@ namespace AgroBase.Forms.IHM } else { - var _ind = _saude.saude_individual?.FirstOrDefault(x => x.id == badge.ModuleID); + var _ind = _saude.saude_individual?.FirstOrDefault(x => x.label == badge.ModuleID); if (_ind != null) { status = _ind.status; diff --git a/AgroBase/AgroBase/Forms/IHM/frmIHM.cs b/AgroBase/AgroBase/Forms/IHM/frmIHM.cs index 693d3fb5a..c16271afd 100644 --- a/AgroBase/AgroBase/Forms/IHM/frmIHM.cs +++ b/AgroBase/AgroBase/Forms/IHM/frmIHM.cs @@ -481,39 +481,39 @@ namespace AgroBase.Forms.IHM // BATERIA // ========================================================= double bateria = operacao?.Sensoriamento?.Bateria?.PercentualBateria ?? 0; - double bateriaSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Bat); - Color corBateria = FuncoesIHM.CorPorValorESaude(bateria, bateriaSaude); + var bateriaSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Bat); + Color corBateria = FuncoesIHM.CorPorValorESaude(bateria, bateriaSaude.saude); AtualizarBateria($"{FuncoesIHM.Limitar(bateria, 0, 100):0}%", corBateria); // ========================================================= // RESERVATÓRIO // ========================================================= double reservatorio = operacao?.Sensoriamento?.Atuador?.PercentualReservatorio ?? 0; - double atuadorSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Atu); - Color corReservatorio = FuncoesIHM.CorPorValorESaude(reservatorio, atuadorSaude); + var atuadorSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Atu); + Color corReservatorio = FuncoesIHM.CorPorValorESaude(reservatorio, atuadorSaude.saude); AtualizarReservatorio($"{FuncoesIHM.Limitar(reservatorio, 0, 100):0}%", corReservatorio); // ========================================================= // TEMPERATURA // ========================================================= double temperatura = operacao?.Sensoriamento?.CoolerControl?.Temperatura ?? 0; - double temperaturaSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Npc); - Color corTemperatura = FuncoesIHM.CorTemperatura(temperatura, temperaturaSaude); + var temperaturaSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Npc); + Color corTemperatura = FuncoesIHM.CorTemperatura(temperatura, temperaturaSaude.saude); AtualizarTemperatura($"{temperatura:0.#} °C", corTemperatura); // ========================================================= // REDE / IPB // ========================================================= - double redeSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Ipb); - Color corRede = FuncoesIHM.CorPorPercentual(redeSaude); - AtualizarRede($"{FuncoesIHM.Limitar(redeSaude, 0, 100):0}%", corRede); + var redeSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Ipb); + Color corRede = FuncoesIHM.CorPorPercentual(redeSaude.saude); + AtualizarRede($"{FuncoesIHM.Limitar(redeSaude.saude, 0, 100):0}%", corRede); // ========================================================= // GNSS // ========================================================= var gnss = operacao?.Sensoriamento?.Gps; - double gnssSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Gps); - bool gnssConectado = gnss != null && gnssSaude > 0; + var gnssSaude = ObterSaudeModulo(saudeModulos, Enums.T_Code.Gps); + bool gnssConectado = gnss != null && gnssSaude.status != Enums.StatusModulo.Desconectado; if (!gnssConectado) { AtualizarGnss("Desconectado", HmiTheme.Danger); @@ -522,7 +522,7 @@ namespace AgroBase.Forms.IHM { Enums.TiposCorrecaoGPS qualidadeFix = gnss.QualidadeFix; double precisaoCm = Math.Max(0, gnss.PrecisaoCm); - double notaGnss = FuncoesIHM.CalcularNotaGnss(qualidadeFix, precisaoCm, gnssSaude); + double notaGnss = FuncoesIHM.CalcularNotaGnss(qualidadeFix, precisaoCm, gnssSaude.saude); Color corGnss = FuncoesIHM.CorPorPercentual(notaGnss); string textoGnss = $"{FuncoesIHM.FormatarQualidadeGnss(qualidadeFix)} " + $"{precisaoCm:0.##} cm"; AtualizarGnss(textoGnss, corGnss); @@ -887,17 +887,22 @@ namespace AgroBase.Forms.IHM }); } - private static double ObterSaudeModulo(List modulosSaude, Enums.T_Code modulo) + private static ManagerWorkerMessageResponseModulosPendentesModel ObterSaudeModulo(List modulosSaude, Enums.T_Code modulo) { + var saude = new ManagerWorkerMessageResponseModulosPendentesModel() + { + saude = 0, + status = Enums.StatusModulo.Desconectado + }; if (modulosSaude == null) - return 0; + return saude; var item = modulosSaude.FirstOrDefault(x => x.modulo == modulo); if (item == null) - return 0; + return saude; - return FuncoesIHM.Limitar(Convert.ToDouble(item.saude), 0, 100); + return item; } } diff --git a/AgroBase/AgroBase/Models/GeneralJoystick.cs b/AgroBase/AgroBase/Models/GeneralJoystick.cs index 9807bbf18..ee33e1564 100644 --- a/AgroBase/AgroBase/Models/GeneralJoystick.cs +++ b/AgroBase/AgroBase/Models/GeneralJoystick.cs @@ -546,6 +546,27 @@ namespace AgroBase } } + public static void PararCarroControle() + { + var _Controle = Variaveis.OperacaoEmAndamento?.Controle; + var pControle = Variaveis.OperacaoEmAndamento?.Parametros?.Controle; + if (_Controle?.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Mov)?.DirecaoAtual != Direcao.Parado) + { + Variaveis.MostrarLog("[GeneralJoystick] Deve enviar o comando de parada MOV em PararCarroControle"); + ProcessarDadosControle(BotoesJoystick.Xis, true, ForcarComando: true); + if (pControle?.FrenagemAutomaticaAoParar ?? false) + { + Variaveis.MostrarLog("[GeneralJoystick] Deve enviar o comando de freio em PararCarroControle"); + ProcessarDadosControle(BotoesJoystick.Bolinha, false, ForcarComando: true); + } + } + if (_Controle?.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Dir)?.UltimaDirecao != Direcao.Parado) + { + Variaveis.MostrarLog("[GeneralJoystick] Deve enviar o comando de parada DIR em PararCarroControle"); + ProcessarDadosControle(BotoesJoystick.SEsquerda, true, ForcarComando: true); + } + } + private static void EnviarComandoMotorAtualizado(DeParaComandos Comando, string ID = "", bool ForcarComando = false) { var op = Variaveis.OperacaoEmAndamento; @@ -555,15 +576,18 @@ namespace AgroBase (op.Parametros.Controle.DirecionalAutomatico && Comando.Dispositivo == T_Code.Dir); bool bloqueioCalibragem = - op.Sensoriamento.Operacao.Calibrando; + op.CalibrandoRuntime; bool bloqueioOperacional = !op.Sensoriamento.Operacao.OperacaoLiberada || !op.Sensoriamento.Operacao.OperacaoIniciada; + bool comandoParada = + Comando.ValorAplicar == 0; + bool deveInterromper = bloqueioAutomatico || - bloqueioCalibragem || + (bloqueioCalibragem && !comandoParada) || !VerificaComandoValidoSonar(Comando.key) || (!ForcarComando && bloqueioOperacional); diff --git a/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs b/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs index 4ab1a4676..1f7713a18 100644 --- a/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs +++ b/AgroBase/AgroBase/Models/Modules/SensoriamentoModel.cs @@ -602,9 +602,9 @@ namespace AgroBase.Models.Modules posicao = CanMessagePosicaoDados.Dados1, indice = 1, analise_health = true, - maximo = 2500, - minimo = 400, - nominal = 700, + maximo = 1500, + minimo = 100, + nominal = 280, unidade_medida = "mA" }, new SensorValoresLeituraModel() diff --git a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs index 4ccb1b837..7951343e8 100644 --- a/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs +++ b/AgroBase/AgroBase/Models/Operacoes/OperacaoModel.cs @@ -1681,6 +1681,8 @@ namespace AgroBase.Models return; } + GeneralJoystick.PararCarroControle(); + entrouEmCalibragem = true; op.DefinirEstadoCalibragemRuntime(true); @@ -2027,29 +2029,14 @@ namespace AgroBase.Models Sonar?.Resumo?.AtualizarStatusBloqueioManual(Sonar, true); if (Sonar?.Resumo?.EnviarComandoParada ?? false) { - Variaveis.MostrarLog("[OperacaoModel] Deve enviar o comando de parada em tmrLeituras_Tick"); - //GeneralJoystick.EnviaComandoMotor(Keys.Escape, Disp: T_Code.Mov, ForcarComando: true); - GeneralJoystick.ProcessarDadosControle(BotoesJoystick.Xis, true, ForcarComando: true); - if (op.Parametros?.Controle?.FrenagemAutomaticaAoParar ?? false) - { - Variaveis.MostrarLog("[OperacaoModel] Deve enviar o comando de freio em tmrLeituras_Tick"); - GeneralJoystick.ProcessarDadosControle(BotoesJoystick.Bolinha, false, ForcarComando: true); - } + Variaveis.MostrarLog($"[OperacaoModel] Deve enviar o comando de parada em tmrLeituras_Tick por trava do Visual Worker: {Sonar?.Analises?.matriz_confianca?.block?.reason}"); + GeneralJoystick.PararCarroControle(); } if (!op.Sensoriamento.Operacao.OperacaoLiberada) { - var _Controle = op.Controle; - if (_Controle.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Mov)?.DirecaoAtual != Direcao.Parado) - { - //GeneralJoystick.EnviaComandoMotor(Keys.Escape, Disp: T_Code.Mov, ForcarComando: true); - GeneralJoystick.ProcessarDadosControle(BotoesJoystick.Xis, true, ForcarComando: true); - } - if (_Controle.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Dir)?.UltimaDirecao != Direcao.Parado) - { - //GeneralJoystick.EnviaComandoMotor(Keys.Escape, Disp: T_Code.Dir, ForcarComando: true); - GeneralJoystick.ProcessarDadosControle(BotoesJoystick.SEsquerda, true, ForcarComando: true); - } + Variaveis.MostrarLog($"[OperacaoModel] Deve enviar o comando de parada em tmrLeituras_Tick por Operacao Bloqueada: {op?.Sensoriamento?.Operacao?.ErroOperacaoLiberada}"); + GeneralJoystick.PararCarroControle(); } } } diff --git a/AgroBase/AgroBase/Models/Variaveis.cs b/AgroBase/AgroBase/Models/Variaveis.cs index 34fd351ad..41e966ee0 100644 --- a/AgroBase/AgroBase/Models/Variaveis.cs +++ b/AgroBase/AgroBase/Models/Variaveis.cs @@ -28,7 +28,7 @@ namespace AgroBase.Models { public class Variaveis { - public static readonly bool IniciarWorkers = false; + public static readonly bool IniciarWorkers = true; public static readonly bool UsarIHM = true; public static readonly bool Producao = false; public static bool DebugMode { get; set; } = false; @@ -685,10 +685,7 @@ namespace AgroBase.Models if (ms > FAILSAFE_MS) { // Para por segurança - //GeneralJoystick.EnviaComandoMotor(Keys.Escape, T_Code.Mov, ForcarComando: true, ID: null); - //GeneralJoystick.EnviaComandoMotor(Keys.Escape, T_Code.Dir, ForcarComando: true, ID: null); - GeneralJoystick.ProcessarDadosControle(BotoesJoystick.Xis, true); - GeneralJoystick.ProcessarDadosControle(BotoesJoystick.SEsquerda, true); + GeneralJoystick.PararCarroControle(); // desarma até chegar novo comando válido Interlocked.Exchange(ref _lastValidCmdTicksUtc, 0);