agrobot_base/AgroBase/AgroBase/Models/GeneralJoystick.cs

1263 lines
57 KiB
C#

using AgroBase.Forms;
using AgroBase.Models;
using AgroBase.Models.Modules;
using AgroBase.Services;
using SharpDX.DirectInput;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static AgroBase.Models.Enums;
namespace AgroBase
{
/// <summary>Leitura consolidada e tolerante a falhas do controle manual.</summary>
public class GeneralJoystick
{
public static List<string> JoysticksConectados = new List<string>();
public static Joystick JoystickConectado = null;
public static int MargemAnalogico = 100;
public static int MaximoAnalogico = 65534;
public static List<DeParaComandos> DeParaComandos = new List<DeParaComandos>()
{
new DeParaComandos() { key = Keys.Left, botaoJoy = BotoesJoystick.SEsquerda, direcao = Direcao.Esquerda, Dispositivo = T_Code.Dir, PararAoSoltar = true },
new DeParaComandos() { key = Keys.Left, botaoJoy = BotoesJoystick.AEEsquerda, direcao = Direcao.Esquerda, Dispositivo = T_Code.Dir, PararAoSoltar = true },
new DeParaComandos() { key = Keys.Right, botaoJoy = BotoesJoystick.SDireita, direcao = Direcao.Direita, Dispositivo = T_Code.Dir, PararAoSoltar = true },
new DeParaComandos() { key = Keys.Right, botaoJoy = BotoesJoystick.AEDireita, direcao = Direcao.Direita, Dispositivo = T_Code.Dir, PararAoSoltar = true },
new DeParaComandos() { key = Keys.R, botaoJoy = BotoesJoystick.R3, direcao = Direcao.Referenciar, Dispositivo = T_Code.Dir, PararAoSoltar = false },
new DeParaComandos() { key = Keys.Up, botaoJoy = BotoesJoystick.Xis, direcao = Direcao.Frente, Dispositivo = T_Code.Mov, PararAoSoltar = true },
new DeParaComandos() { key = Keys.Up, botaoJoy = BotoesJoystick.R2A, direcao = Direcao.Frente, Dispositivo = T_Code.Mov, PararAoSoltar = true },
new DeParaComandos() { key = Keys.Down, botaoJoy = BotoesJoystick.Quadrado, direcao = Direcao.Tras, Dispositivo = T_Code.Mov, PararAoSoltar = true },
new DeParaComandos() { key = Keys.Down, botaoJoy = BotoesJoystick.L2A, direcao = Direcao.Tras, Dispositivo = T_Code.Mov, PararAoSoltar = true },
new DeParaComandos() { key = Keys.Space, botaoJoy = BotoesJoystick.Bolinha, direcao = Direcao.EmFreio, Dispositivo = T_Code.Mov, PararAoSoltar = true },
new DeParaComandos() { key = Keys.Subtract, botaoJoy = BotoesJoystick.L1, Dispositivo = T_Code.Mov, PararAoSoltar = false },
new DeParaComandos() { key = Keys.Add, botaoJoy = BotoesJoystick.R1, Dispositivo = T_Code.Mov, PararAoSoltar = false },
new DeParaComandos() { key = Keys.D1, botaoJoy = BotoesJoystick.Touchpad, Dispositivo = T_Code.Atu, PararAoSoltar = true, IDcontrolar = "B01" },
new DeParaComandos() { key = Keys.D2, botaoJoy = BotoesJoystick.Touchpad, Dispositivo = T_Code.Atu, PararAoSoltar = true, IDcontrolar = "B02" },
new DeParaComandos() { key = Keys.D3, botaoJoy = BotoesJoystick.Touchpad, Dispositivo = T_Code.Atu, PararAoSoltar = true, IDcontrolar = "B03" },
new DeParaComandos() { key = Keys.D4, botaoJoy = BotoesJoystick.Touchpad, Dispositivo = T_Code.Atu, PararAoSoltar = true, IDcontrolar = "B04" },
new DeParaComandos() { key = Keys.D5, botaoJoy = BotoesJoystick.Touchpad, Dispositivo = T_Code.Atu, PararAoSoltar = true, IDcontrolar = "B05" },
new DeParaComandos() { key = Keys.D6, botaoJoy = BotoesJoystick.Touchpad, Dispositivo = T_Code.Atu, PararAoSoltar = true, IDcontrolar = "B06" },
new DeParaComandos() { key = Keys.D7, botaoJoy = BotoesJoystick.Touchpad, Dispositivo = T_Code.Atu, PararAoSoltar = true, IDcontrolar = "B07" },
};
public static List<BotoesJoystick> BotoesJoyPressionados = new List<BotoesJoystick>() { };
private static readonly object _syncJoystick = new object();
private static readonly SemaphoreSlim _tickGate = new SemaphoreSlim(1, 1);
private static readonly HashSet<BotoesJoystick> _estadoBotoes = new HashSet<BotoesJoystick>();
private static DateTime _ultimaTentativaReconexao = DateTime.MinValue;
private static DateTime _ultimoEnvioMov = DateTime.MinValue;
private static DateTime _ultimoEnvioDir = DateTime.MinValue;
private static Direcao _ultimaDirecaoMovEnviada = Direcao.Parado;
private static double _ultimoValorMovEnviado = double.NaN;
private static double _ultimoAnguloDirEnviado = double.NaN;
private const int IntervaloReconexaoMs = 2000;
private const int HeartbeatComandoMs = 300;
private const double VariacaoMinimaMov = 0.25;
private const double VariacaoMinimaDir = 0.20;
public static List<TipoMovimentoDirecional> MovimentosCombinados = new List<TipoMovimentoDirecional>()
{
TipoMovimentoDirecional.MovimentoLateral,
TipoMovimentoDirecional.RotacionarNoEixo
};
public static Direcao UltimaDirecao = Direcao.Parado;
public static bool Pressionado = false;
public static List<IDispositivosService> DispCon;
public static AsyncTaskTimerModel tmrJoystick;
public static void IniciarRotinas()
{
tmrJoystick = new AsyncTaskTimerModel("tmrJoystick", tmrJoystick_Tick, 100);
tmrJoystick.Start();
}
public static void AtualizaDispositivo()
{
Func<bool> funcAtt = new Func<bool>(() =>
{
DispCon = Variaveis.DispositivosConectados.Where(x => x.Dados.ConexaoAtiva).ToList();
frmInstancial.frmJoystick.lblDispositivo.Text = "Dispositivos: " + (DispCon.Count == 0 ? "N/A" : string.Join(", ", DispCon.Select(x => x._Descricao + " (" + Enum.GetName(typeof(T_Code), x.Dispositivo) + ")").ToArray()));
AtualizarListaJoysticks();
return true;
});
FuncoesGlobais.ExecutarMetodoComVerificacaoCrossThread(frmInstancial.frmJoystick.lblDispositivo, funcAtt);
}
public static void InicializarJoysticksConectados()
{
lock (_syncJoystick)
{
var directInput = new DirectInput();
var nomes = new List<string>();
// Find a Joystick Guid
var joystickGuid = Guid.Empty;
foreach (var deviceInstance in directInput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices))
{
joystickGuid = deviceInstance.InstanceGuid;
nomes.Add(deviceInstance.InstanceName);
}
// If Gamepad not found, look for a Joystick
if (joystickGuid == Guid.Empty)
{
foreach (var deviceInstance in directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices))
{
joystickGuid = deviceInstance.InstanceGuid;
nomes.Add(deviceInstance.InstanceName);
}
}
// If Joystick not found, throws an error
if (joystickGuid == Guid.Empty)
{
JoysticksConectados = nomes;
DesconectarJoystickInterno();
return;
}
// Instantiate the joystick
DesconectarJoystickInterno();
JoystickConectado = new Joystick(directInput, joystickGuid);
JoysticksConectados = nomes;
// Query all suported ForceFeedback effects
var allEffects = JoystickConectado.GetEffects();
foreach (var effectInfo in allEffects)
{
Console.WriteLine("Effect available {0}", effectInfo.Name);
}
// Set BufferSize in order to use buffered data.
JoystickConectado.Acquire();
if (JoystickConectado != null)
{
tmrJoystick?.Start();
}
}
}
private static void AtualizarListaJoysticks()
{
try
{
using (var directInput = new DirectInput())
{
var nomes = directInput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices)
.Concat(directInput.GetDevices(DeviceType.Joystick, DeviceEnumerationFlags.AllDevices))
.Select(x => x.InstanceName)
.Distinct()
.ToList();
lock (_syncJoystick) JoysticksConectados = nomes;
}
}
catch (Exception ex)
{
Variaveis.MostrarLog($"[GeneralJoystick] Falha ao listar joysticks: {ex.GetType().Name}: {ex.Message}");
}
}
private static void DesconectarJoystickInterno()
{
var antigo = JoystickConectado;
JoystickConectado = null;
_estadoBotoes.Clear();
BotoesJoyPressionados.Clear();
if (antigo == null) return;
try { antigo.Unacquire(); } catch { }
try { antigo.Dispose(); } catch { }
}
private static bool ComparaPosicaoAnalogico(int Atual, ComparativoAnalogico Comparativo)
{
int Meio = MaximoAnalogico / 2;
bool Pressionado = false;
if (Comparativo == ComparativoAnalogico.Igual)
{
Pressionado = Atual >= (Meio - MargemAnalogico) && Atual <= (Meio + MargemAnalogico);
}
else if (Comparativo == ComparativoAnalogico.Maior)
{
Pressionado = Atual >= (Meio + MargemAnalogico);
}
else if (Comparativo == ComparativoAnalogico.Menor)
{
Pressionado = Atual <= (Meio - MargemAnalogico);
}
return Pressionado;
}
private static double DeParaAnanlogico(double Valor, double DeMin, double DeMax, double ParaMin, double ParaMax)
{
if (Valor < DeMin)
{
Valor = DeMin;
}
else if (Valor > DeMax)
{
Valor = DeMax;
}
double scale = (Valor - DeMin) / (DeMax - DeMin);
double mappedValue = scale * (ParaMax - ParaMin) + ParaMin;
return mappedValue;
}
private static async Task tmrJoystick_Tick()
{
await ProcessarEstadoAtualJoystick();
return;
#pragma warning disable CS0162
if (JoystickConectado == null)
{
return;
}
JoystickUpdate[] datas;
try
{
JoystickConectado.Poll();
datas = JoystickConectado.GetBufferedData();
}
catch
{
//EnviaComandoMotor(Keys.Escape, T_Code.Mov, ForcarComando: true);
//EnviaComandoMotor(Keys.Escape, T_Code.Dir, ForcarComando: true);
ProcessarDadosControle(BotoesJoystick.Xis, true);
ProcessarDadosControle(BotoesJoystick.SEsquerda, true);
AtualizaDispositivo();
return;
}
List<BotoesPressionadosValores> _botoesPressionados = new List<BotoesPressionadosValores>();
List<BotoesJoystick> _botoesSoltos = new List<BotoesJoystick>();
foreach (var state in datas)
{
//Console.WriteLine(state);
if (state.Offset == JoystickOffset.PointOfViewControllers0)
{
if (state.Value == -1)
{
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.SBaixo))
{
_botoesSoltos.Add(BotoesJoystick.SBaixo);
}
else if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.SCima))
{
_botoesSoltos.Add(BotoesJoystick.SCima);
}
else if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.SEsquerda))
{
_botoesSoltos.Add(BotoesJoystick.SEsquerda);
}
else if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.SDireita))
{
_botoesSoltos.Add(BotoesJoystick.SDireita);
}
}
else if (state.Value == 0)
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.SCima, valor = state.Value });
}
else if (state.Value == 9000)
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.SDireita, valor = state.Value });
}
else if (state.Value == 18000)
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.SBaixo, valor = state.Value });
}
else if (state.Value == 27000)
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.SEsquerda, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Z)
{
if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Igual))
{
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.ADDireita))
{
_botoesSoltos.Add(BotoesJoystick.ADDireita);
}
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.ADEsquerda))
{
_botoesSoltos.Add(BotoesJoystick.ADEsquerda);
}
}
else if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Maior))
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.ADDireita, valor = state.Value });
}
else if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Menor))
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.ADEsquerda, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.X)
{
if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Igual))
{
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.AEEsquerda))
{
_botoesSoltos.Add(BotoesJoystick.AEEsquerda);
}
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.AEDireita))
{
_botoesSoltos.Add(BotoesJoystick.AEDireita);
}
}
else if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Maior))
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.AEDireita, valor = state.Value });
}
else if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Menor))
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.AEEsquerda, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Y)
{
if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Igual))
{
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.AECima))
{
_botoesSoltos.Add(BotoesJoystick.AECima);
}
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.AEBaixo))
{
_botoesSoltos.Add(BotoesJoystick.AEBaixo);
}
}
else if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Maior))
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.AEBaixo, valor = state.Value });
}
else if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Menor))
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.AECima, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.RotationX)
{
if (state.Value == 0)
{
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.L2A))
{
_botoesSoltos.Add(BotoesJoystick.L2A);
}
}
else if (state.Value > 0)
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.L2A, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.RotationY)
{
if (state.Value == 0)
{
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.R2A))
{
_botoesSoltos.Add(BotoesJoystick.R2A);
}
}
else if (state.Value > 0)
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.R2A, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.RotationZ)
{
if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Igual))
{
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.ADCima))
{
_botoesSoltos.Add(BotoesJoystick.ADCima);
}
if (BotoesJoyPressionados.Any(x => x == BotoesJoystick.ADBaixo))
{
_botoesSoltos.Add(BotoesJoystick.ADBaixo);
}
}
else if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Maior))
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.ADBaixo, valor = state.Value });
}
else if (ComparaPosicaoAnalogico(state.Value, ComparativoAnalogico.Menor))
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.ADCima, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons0)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.Quadrado);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.Quadrado, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons1)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.Xis);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.Xis, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons2)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.Bolinha);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.Bolinha, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons3)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.Triangulo);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.Triangulo, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons4)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.L1);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.L1, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons5)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.R1);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.R1, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons6)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.L2);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.L2, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons7)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.R2);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.R2, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons8)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.Share);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.Share, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons9)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.Options);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.Options, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons10)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.L3);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.L3, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons11)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.R3);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.R3, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons12)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.PS);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.PS, valor = state.Value });
}
}
else if (state.Offset == JoystickOffset.Buttons13)
{
if (state.Value == 0)
{
_botoesSoltos.Add(BotoesJoystick.Touchpad);
}
else
{
_botoesPressionados.Add(new BotoesPressionadosValores() { botao = BotoesJoystick.Touchpad, valor = state.Value });
}
}
}
var botoes_pressionados_g = _botoesPressionados
.GroupBy(x => x.botao)
.Select(x => new {
botao = x.Key,
dados = x
})
.ToList();
botoes_pressionados_g.ForEach(botao =>
{
ProcessarDadosControle(botao.botao, false, valorJoy: botao.dados?.LastOrDefault()?.valor ?? 0, ForcarComando: true);
//Console.WriteLine($"Botao pressionado: {botao.botao}, Valor: {string.Join(", ", botao.dados.Select(y => y.valor))}");
BotoesJoyPressionados.Add(botao.botao);
});
_botoesSoltos.ForEach(botao =>
{
ProcessarDadosControle(botao, true, valorJoy: 0, ForcarComando: true);
//Console.WriteLine($"Botao solto: {botao}");
BotoesJoyPressionados.Remove(botao);
});
#pragma warning restore CS0162
}
private static async Task ProcessarEstadoAtualJoystick()
{
if (!await _tickGate.WaitAsync(0)) return;
try
{
Joystick joystick;
lock (_syncJoystick) joystick = JoystickConectado;
if (joystick == null)
{
TentarReconectar();
return;
}
JoystickState estado;
try
{
joystick.Poll();
estado = joystick.GetCurrentState();
}
catch (Exception ex)
{
Variaveis.MostrarLog($"[GeneralJoystick] Controle desconectado/falha de leitura: {ex.GetType().Name}: {ex.Message}");
PararCarroControle("Perda de comunicação com o joystick");
lock (_syncJoystick) DesconectarJoystickInterno();
TentarReconectar();
return;
}
AtualizarEstadoFisico(estado);
ResolverMovimento(estado);
ResolverDirecional(estado);
ResolverComandosDeBorda();
}
catch (Exception ex)
{
Variaveis.MostrarLog($"[GeneralJoystick] Erro inesperado no tick: {ex.GetType().Name}: {ex.Message}");
PararCarroControle("Falha interna no processamento do joystick");
}
finally
{
_tickGate.Release();
}
}
private static void TentarReconectar()
{
if ((DateTime.Now - _ultimaTentativaReconexao).TotalMilliseconds < IntervaloReconexaoMs) return;
_ultimaTentativaReconexao = DateTime.Now;
try { InicializarJoysticksConectados(); }
catch (Exception ex)
{
Variaveis.MostrarLog($"[GeneralJoystick] Reconexão falhou: {ex.GetType().Name}: {ex.Message}");
}
}
private static void AtualizarEstadoFisico(JoystickState s)
{
var anterior = new HashSet<BotoesJoystick>(_estadoBotoes);
_estadoBotoes.Clear();
var buttons = s.Buttons ?? new bool[0];
Action<int, BotoesJoystick> addButton = (i, b) => { if (i < buttons.Length && buttons[i]) _estadoBotoes.Add(b); };
addButton(0, BotoesJoystick.Quadrado); addButton(1, BotoesJoystick.Xis);
addButton(2, BotoesJoystick.Bolinha); addButton(3, BotoesJoystick.Triangulo);
addButton(4, BotoesJoystick.L1); addButton(5, BotoesJoystick.R1);
addButton(6, BotoesJoystick.L2); addButton(7, BotoesJoystick.R2);
addButton(8, BotoesJoystick.Share); addButton(9, BotoesJoystick.Options);
addButton(10, BotoesJoystick.L3); addButton(11, BotoesJoystick.R3);
addButton(12, BotoesJoystick.PS); addButton(13, BotoesJoystick.Touchpad);
if (s.RotationX > MargemAnalogico) _estadoBotoes.Add(BotoesJoystick.L2A);
if (s.RotationY > MargemAnalogico) _estadoBotoes.Add(BotoesJoystick.R2A);
if (s.X < MaximoAnalogico / 2 - 3000) _estadoBotoes.Add(BotoesJoystick.AEEsquerda);
if (s.X > MaximoAnalogico / 2 + 3000) _estadoBotoes.Add(BotoesJoystick.AEDireita);
int pov = s.PointOfViewControllers != null && s.PointOfViewControllers.Length > 0 ? s.PointOfViewControllers[0] : -1;
if (pov == 27000) _estadoBotoes.Add(BotoesJoystick.SEsquerda);
if (pov == 9000) _estadoBotoes.Add(BotoesJoystick.SDireita);
if (pov == 0) _estadoBotoes.Add(BotoesJoystick.SCima);
if (pov == 18000) _estadoBotoes.Add(BotoesJoystick.SBaixo);
_botoesPressionadosNoTick = new HashSet<BotoesJoystick>(_estadoBotoes.Except(anterior));
_botoesSoltosNoTick = new HashSet<BotoesJoystick>(anterior.Except(_estadoBotoes));
BotoesJoyPressionados = _estadoBotoes.ToList();
}
private static HashSet<BotoesJoystick> _botoesPressionadosNoTick = new HashSet<BotoesJoystick>();
private static HashSet<BotoesJoystick> _botoesSoltosNoTick = new HashSet<BotoesJoystick>();
private static void ResolverMovimento(JoystickState s)
{
bool frenteDigital = _estadoBotoes.Contains(BotoesJoystick.Xis);
bool trasDigital = _estadoBotoes.Contains(BotoesJoystick.Quadrado);
double frenteAnalogico = s.RotationY > MargemAnalogico ? s.RotationY : 0;
double trasAnalogico = s.RotationX > MargemAnalogico ? s.RotationX : 0;
bool conflito = (frenteDigital || frenteAnalogico > 0) && (trasDigital || trasAnalogico > 0);
BotoesJoystick botao;
double valor;
Direcao direcao;
if (conflito)
{
botao = BotoesJoystick.Xis; valor = 0; direcao = Direcao.Parado;
RegistrarMotivoManual("Comandos simultâneos de frente e ré; movimento neutralizado");
}
else if (frenteAnalogico > 0) { botao = BotoesJoystick.R2A; valor = frenteAnalogico; direcao = Direcao.Frente; }
else if (trasAnalogico > 0) { botao = BotoesJoystick.L2A; valor = trasAnalogico; direcao = Direcao.Tras; }
else if (frenteDigital) { botao = BotoesJoystick.Xis; valor = MaximoAnalogico; direcao = Direcao.Frente; }
else if (trasDigital) { botao = BotoesJoystick.Quadrado; valor = MaximoAnalogico; direcao = Direcao.Tras; }
else { botao = BotoesJoystick.Xis; valor = 0; direcao = Direcao.Parado; }
var cmd = DeParaComandos.FirstOrDefault(x => x.botaoJoy == botao)?.Clone();
if (cmd == null) return;
cmd.CalcularValor(direcao == Direcao.Parado, valorJoy: valor);
bool mudou = direcao != _ultimaDirecaoMovEnviada || double.IsNaN(_ultimoValorMovEnviado) || Math.Abs(cmd.ValorAplicar - _ultimoValorMovEnviado) >= VariacaoMinimaMov;
bool combinado = MovimentosCombinados.Contains(Variaveis.OperacaoEmAndamento.Controle.TipoMovimento);
bool heartbeat = !combinado && (DateTime.Now - _ultimoEnvioDir).TotalMilliseconds >= HeartbeatComandoMs;
if (!mudou && !heartbeat) return;
EnviarComandoMotorAtualizado(cmd, ForcarComando: false);
_ultimaDirecaoMovEnviada = direcao; _ultimoValorMovEnviado = cmd.ValorAplicar; _ultimoEnvioMov = DateTime.Now;
}
private static void ResolverDirecional(JoystickState s)
{
bool esqDigital = _estadoBotoes.Contains(BotoesJoystick.SEsquerda);
bool dirDigital = _estadoBotoes.Contains(BotoesJoystick.SDireita);
BotoesJoystick botao = BotoesJoystick.SEsquerda;
double valor = 0;
bool solto = true;
if (esqDigital ^ dirDigital) { botao = esqDigital ? BotoesJoystick.SEsquerda : BotoesJoystick.SDireita; solto = false; }
else if (_estadoBotoes.Contains(BotoesJoystick.AEEsquerda)) { botao = BotoesJoystick.AEEsquerda; valor = s.X; solto = false; }
else if (_estadoBotoes.Contains(BotoesJoystick.AEDireita)) { botao = BotoesJoystick.AEDireita; valor = s.X; solto = false; }
var cmd = DeParaComandos.FirstOrDefault(x => x.botaoJoy == botao)?.Clone();
if (cmd == null) return;
cmd.CalcularValor(solto, valorJoy: valor);
bool mudou = double.IsNaN(_ultimoAnguloDirEnviado) || Math.Abs(cmd.ValorAplicar - _ultimoAnguloDirEnviado) >= VariacaoMinimaDir;
bool heartbeat = (DateTime.Now - _ultimoEnvioDir).TotalMilliseconds >= HeartbeatComandoMs;
if (!mudou && !heartbeat) return;
EnviarComandoMotorAtualizado(cmd, ForcarComando: false);
_ultimoAnguloDirEnviado = cmd.ValorAplicar; _ultimoEnvioDir = DateTime.Now;
}
private static void ResolverComandosDeBorda()
{
foreach (var b in _botoesPressionadosNoTick.Where(x => x == BotoesJoystick.L1 || x == BotoesJoystick.R1 || x == BotoesJoystick.R3 || x == BotoesJoystick.Bolinha))
ProcessarDadosControle(b, false, ForcarComando: false);
if (_botoesSoltosNoTick.Contains(BotoesJoystick.Bolinha))
ProcessarDadosControle(BotoesJoystick.Bolinha, true, ForcarComando: false);
}
public static void ProcessarDadosControle(BotoesJoystick botao, bool solto, Keys? key = null, double? valorJoy = null, double? valorDesejado = null, string ID = "", bool ForcarComando = false)
{
var comando = DeParaComandos.FirstOrDefault(x => x.botaoJoy == botao && (key != null ? x.key == key : true))?.Clone();
comando?.CalcularValor(solto, valorJoy: valorJoy, valorDesejado: valorDesejado);
if (comando?.Dispositivo == T_Code.Dir || comando?.Dispositivo == T_Code.Mov)
{
EnviarComandoMotorAtualizado(comando, ID: ID, ForcarComando: ForcarComando);
}
else if (comando?.Dispositivo == T_Code.Atu)
{
EnviarComandoAtuadorAtualizado(comando, ForcarComando: ForcarComando);
}
}
public static void PararCarroControle(string motivo)
{
var op = Variaveis.OperacaoEmAndamento;
var controle = op?.Controle;
var pControle = op?.Parametros?.Controle;
if (controle == null)
return;
controle.MotivosManual.Clear();
controle.MotivosManual.Add(motivo);
var movimento = controle.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Mov);
if (movimento?.DirecaoAtual != Direcao.Parado)
{
Variaveis.MostrarLog($"[GeneralJoystick] Parada MOV solicitada. Motivo: {motivo}");
ProcessarDadosControle(BotoesJoystick.Xis, true, ForcarComando: true);
controle.MotivosManual.Add("Comando de parada MOV enviado");
if (pControle?.FrenagemAutomaticaAoParar == true)
{
ProcessarDadosControle(BotoesJoystick.Bolinha, false, ForcarComando: true);
controle.MotivosManual.Add("Comando de freio enviado");
}
}
var direcional = controle.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Dir);
if (direcional?.UltimaDirecao != Direcao.Parado)
{
ProcessarDadosControle(BotoesJoystick.SEsquerda, true, ForcarComando: true);
controle.MotivosManual.Add("Comando de parada DIR enviado");
}
}
private static void EnviarComandoMotorAtualizado(DeParaComandos Comando, string ID = "", bool ForcarComando = false)
{
var op = Variaveis.OperacaoEmAndamento;
if (op?.Controle == null || op.Parametros?.Controle == null || op.Sensoriamento?.Operacao == null || Comando == null)
return;
bool solicitacaoAtiva = Comando.ValorAplicar != 0 || Comando.EstadoAplicar ||
Comando.direcao == Direcao.Referenciar;
bool bloqueioAutomatico =
!ForcarComando &&
((op.Parametros.Controle.MovimentoAutomatico && Comando.Dispositivo == T_Code.Mov) ||
(op.Parametros.Controle.DirecionalAutomatico && Comando.Dispositivo == T_Code.Dir));
bool bloqueioCalibragem =
op.CalibrandoRuntime;
bool bloqueioOperacional =
!op.Sensoriamento.Operacao.OperacaoLiberada ||
!op.Sensoriamento.Operacao.OperacaoIniciada;
bool comandoParada =
Comando.ValorAplicar == 0;
bool bloqueioSonar = !comandoParada && !VerificaComandoValidoSonar(Comando.key);
bool deveInterromper =
bloqueioAutomatico ||
(bloqueioCalibragem && !comandoParada) ||
bloqueioSonar ||
(!ForcarComando && bloqueioOperacional);
var _Controle = op.Controle;
if (deveInterromper)
{
// MotivosManual descreve somente uma tentativa ativa barrada.
// Solturas, neutralizações e comandos de parada não sobrescrevem o diagnóstico.
if (!solicitacaoAtiva) return;
_Controle.MotivosManual.Clear();
if (bloqueioAutomatico)
_Controle.MotivosManual.Add("Controle em modo automático");
if (bloqueioCalibragem && !comandoParada)
_Controle.MotivosManual.Add("Calibragem em andamento");
if (!ForcarComando && bloqueioOperacional)
{
_Controle.MotivosManual.Add(
$"Operação iniciada: {op.Sensoriamento.Operacao.OperacaoIniciada}, " +
$"Operação liberada: {op.Sensoriamento.Operacao.OperacaoLiberada}, " +
$"Erro operação liberada: {op.Sensoriamento.Operacao.ErroOperacaoLiberada}"
);
}
if (bloqueioSonar)
_Controle.MotivosManual.Add("Visual Worker impediu o comando");
return;
}
// Uma solicitação ativa aceita invalida imediatamente qualquer bloqueio antigo.
if (solicitacaoAtiva)
_Controle.MotivosManual.Clear();
if (Comando.direcao.HasValue && _Controle.TiposControle.Any(x => x.Tipo == Comando.Dispositivo))
_Controle.TiposControle.FirstOrDefault(x => x.Tipo == Comando.Dispositivo).DirecaoAtual = Comando.direcao.Value;
for (int i = 0; i < 1; i++)
{
switch (Comando.Dispositivo)
{
case T_Code.Mov:
if (_Controle.TipoMovimento == TipoMovimentoDirecional.RotacionarNoEixo)
return;
_Controle.PercentualVelocidadeSP = Comando.ValorAplicar;
_Controle.EmFreio = Comando.EstadoAplicar;
op.AtualizarDadosControle(ForcarComando, new List<T_Code>() { Comando.Dispositivo });
break;
case T_Code.Dir:
if (MovimentosCombinados.Contains(_Controle.TipoMovimento))
{
Task.Run(async () => await op.DispMvd.Dados.ExecutarMovimentoDirecionalCombinado(Comando.direcao.Value));
}
else if (Comando.direcao == Direcao.Referenciar)
{
List<string> modsControlar = (!string.IsNullOrEmpty(ID)) ? new List<string>() { ID } : null;
Task.Run(async () => await op.DispMvd.Dados.RealizarReferenciamentoDirecional(ModsReferenciar: modsControlar));
}
else
{
_Controle.Angulo = Comando.ValorAplicar;
op.AtualizarDadosControle(ForcarComando, new List<T_Code>() { Comando.Dispositivo });
}
break;
}
}
if (_Controle.TiposControle.Any(x => x.Tipo == Comando.Dispositivo))
AtualizaConsole(_Controle.TiposControle.FirstOrDefault(x => x.Tipo == Comando.Dispositivo).DirecaoAtual.ToString());
}
private static void RegistrarMotivoManual(string motivo)
{
var motivos = Variaveis.OperacaoEmAndamento?.Controle?.MotivosManual;
if (motivos == null) return;
motivos.Clear();
motivos.Add(motivo);
Variaveis.MostrarLog($"[GeneralJoystick] Comando manual não aplicado. Motivo: {motivo}");
}
public static void RemoverMotivoParada(string motivo)
{
var motivos = Variaveis.OperacaoEmAndamento?.Controle?.MotivosManual;
if (motivos == null) return;
motivos.Remove(motivo);
Variaveis.MostrarLog($"[GeneralJoystick] Comando manual não aplicado. Motivo: {motivo}");
}
public static void EnviarComandoAtuadorAtualizado(DeParaComandos Comando, bool ForcarComando = false)
{
var op = Variaveis.OperacaoEmAndamento;
bool bloqueioAutomatico = op.Parametros.Controle.PulverizadorAutomatico;
bool bloqueioOperacional =
op.Sensoriamento.Operacao.Calibrando ||
!op.Sensoriamento.Operacao.OperacaoLiberada ||
!op.Sensoriamento.Operacao.OperacaoIniciada;
bool deveInterromper =
bloqueioAutomatico ||
(!ForcarComando && bloqueioOperacional);
if (deveInterromper) return;
var _Controle = op.Controle;
var tipoControle = _Controle?.TiposControle?.FirstOrDefault(x => x?.Tipo == Comando.Dispositivo);
var bico = op.DispAtu?.Dados?.BicosPulverizadores?.FirstOrDefault(x => x.ID == Comando.IDcontrolar);
var bomba = op.DispAtu?.Dados?.BombasPressurizadoras?.FirstOrDefault(x => x.ID == Comando.IDcontrolar);
S_Code comp = bico?.Componente ?? bomba?.Componente ?? S_Code.sVZO;
switch (comp)
{
case S_Code.sBIC:
op?.DispAtu?.Dados?.EnviarComandoFisicoBico(
bico,
Comando.Comando,
setarControle: true,
status: Comando.EstadoAplicar
);
return;
case S_Code.sBMB:
op?.DispAtu?.Dados?.EnviarComandoFisicoBomba(
bomba,
Comando.Comando,
setarControle: true,
status: Comando.EstadoAplicar
);
return;
}
}
public static void EnviarComandoSensoriamento(S_Code Componente, string ID, dynamic Status) //, bool Forcar = false
{
var op = Variaveis.OperacaoEmAndamento;
var DispSen = op.DispSen;
if (!DispSen.Dados.Conectado)
return;
if (false) // !Forcar
{
bool componente_em_uso = op.Parametros.ModulosMandatorios.Any(x => x.Dispositivo == T_Code.Sen && (x.ComponentesEmUso?.ContainsKey(ID) ?? false) && x.ComponentesEmUso[ID].Item2);
if (!componente_em_uso)
{
// Impede execucao de comandos que liguem componentes quando estiver marcado para nao utilizar
if (Componente == S_Code.sRLE && ((Estado)Status) == Estado.Ligado) return;
if (Componente == S_Code.sSRV && ((int)Status) > 0) return;
}
}
int id_num = -1;
CanMessagePosicaoDados posicao = CanMessagePosicaoDados.Command1;
byte[] data = null;
switch (Componente)
{
case S_Code.sMOD:
if (DispSen?.Dados != null && Status == true)
{
(id_num, posicao, data) = DispSen.Dados.ProtocoloComandoReset();
}
break;
case S_Code.sLED:
var Sinaleiro = DispSen?.Dados?.Sinaleiros?.FirstOrDefault(x => x.ID == ID && x.Inicializado);
if (Sinaleiro != null)
{
Sinaleiro.Comportamento = (SensorSinaleiroComportamentoModel)Status;
(id_num, posicao, data) = Sinaleiro.ProtocoloComando();
Sinaleiro.UltimoComandoEnviado = DateTime.Now;
Sinaleiro.UltimoComportamentoDesejado = (SensorSinaleiroComportamentoModel)Status;
}
break;
case S_Code.sRLE:
var Rele = DispSen?.Dados?.Reles?.FirstOrDefault(x => x.ID == ID && x.Inicializado);
if (Rele != null)
{
Rele.StatusControle = (Estado)Status;
(id_num, posicao, data) = Rele.ProtocoloComando();
Rele.UltimoComandoEnviado = DateTime.Now;
Rele.UltimoEstadoDesejado = (Estado)Status;
}
break;
case S_Code.sSRV:
var ServoFreio = DispSen?.Dados?.Servos?.FirstOrDefault(x => x.ID == ID && x.Inicializado);
if (ServoFreio != null)
{
ServoFreio._AnguloControle = (int)Status;
(id_num, posicao, data) = ServoFreio.ProtocoloComando();
ServoFreio.UltimoComandoEnviado = DateTime.Now;
ServoFreio.UltimoAnguloDesejado = (int)Status;
}
break;
}
DispSen.AdicionarMensagemFila(id_num, posicao, posicao, data, false);
}
private static void AtualizaConsole(string Mensagem)
{
if (frmInstancial.frmJoystick.lblConsole.InvokeRequired)
{
frmInstancial.frmJoystick.lblConsole.Invoke((MethodInvoker)delegate
{
frmInstancial.frmJoystick.lblConsole.Text = Mensagem;
});
}
else
{
frmInstancial.frmJoystick.lblConsole.Text = Mensagem;
}
}
private static bool VerificaComandoValidoSonar(Keys Comando)
{
List<Keys> ComandosImpedidos = new List<Keys>();
var Sonar = Variaveis.OperacaoEmAndamento.Sensoriamento.OperadorVisual;
var DeveParar = Sonar?.StatusValido == true && Sonar?.Resumo?.DeveParar == true;
if (DeveParar)
{
ComandosImpedidos.Add(Keys.Up);
}
return !ComandosImpedidos.Any(x => x == Comando);
}
}
public class DeParaComandos
{
public BotoesJoystick botaoJoy { get; set; }
public Keys key { get; set; }
public CanMessagePosicaoDados Comando { get; set; } = CanMessagePosicaoDados.Command1;
public T_Code Dispositivo { get; set; }
public bool PararAoSoltar { get; set; }
public string IDcontrolar { get; set; }
public Direcao? direcao { get; set; }
public double ValorAplicar { get; set; }
public bool EstadoAplicar { get; set; }
public void CalcularValor(bool solto, double? valorJoy = null, double? valorDesejado = null)
{
var op = Variaveis.OperacaoEmAndamento;
double centro = 32767.0;
double deadzone = 3000.0;
var pControle = op.Parametros.Controle;
var controle = op.Controle;
if (pControle == null || controle == null) return;
double velMin = pControle.MovVelocidadeCErvasPercent;
double velMax = pControle.MovVelocidadeSErvasPercent;
double valorSaida = 0;
bool estadoSaida = false;
switch (botaoJoy)
{
// =========================================================
// DIREÇÃO ANALÓGICA
// =========================================================
case BotoesJoystick.AEEsquerda:
{
double percent = 0;
if (valorJoy != null)
{
if (!solto && valorJoy.Value < centro - deadzone)
percent = (centro - valorJoy.Value) / centro;
percent = FuncoesMatematicas.Clamp(percent, 0, 1);
valorSaida = solto ? 0 : -pControle.DirAnguloMaximo * percent;
}
else if (valorDesejado != null)
{
valorSaida = solto ? 0 : valorDesejado.Value;
}
break;
}
case BotoesJoystick.AEDireita:
{
if (valorJoy != null)
{
double percent = 0;
if (!solto && valorJoy.Value > centro + deadzone)
percent = (valorJoy.Value - centro) / (GeneralJoystick.MaximoAnalogico - centro);
percent = FuncoesMatematicas.Clamp(percent, 0, 1);
valorSaida = solto ? 0 : pControle.DirAnguloMaximo * percent;
}
else if (valorDesejado != null)
{
valorSaida = valorDesejado.Value;
}
break;
}
// Não usados por enquanto
case BotoesJoystick.AECima:
case BotoesJoystick.AEBaixo:
break;
// =========================================================
// DIREÇÃO DIGITAL
// =========================================================
case BotoesJoystick.SEsquerda:
valorSaida = solto ? 0 : valorDesejado ?? -pControle.DirAnguloMaximo;
break;
case BotoesJoystick.SDireita:
valorSaida = solto ? 0 : valorDesejado ?? pControle.DirAnguloMaximo;
break;
// =========================================================
// MOVIMENTO ANALÓGICO
// =========================================================
case BotoesJoystick.L2A:
case BotoesJoystick.R2A:
{
if (solto)
{
valorSaida = 0;
}
else if (valorJoy != null)
{
double gatilho = FuncoesMatematicas.Clamp(valorJoy.Value / GeneralJoystick.MaximoAnalogico, 0, 1);
valorSaida = FuncoesMatematicas.Map(gatilho, 0, 1, velMin, velMax);
}
else if (valorDesejado != null)
{
valorSaida = valorDesejado.Value;
}
break;
}
// =========================================================
// MOVIMENTO DIGITAL FIXO NO MÍNIMO
// =========================================================
case BotoesJoystick.Xis:
case BotoesJoystick.Quadrado:
valorSaida = solto ? 0 : valorDesejado ?? velMin;
break;
// =========================================================
// FREIO
// =========================================================
case BotoesJoystick.Bolinha:
valorSaida = 0;
estadoSaida = !solto;
break;
// =========================================================
// AJUSTE FINO DE VELOCIDADE
// Só funciona se já estiver em movimento
// =========================================================
case BotoesJoystick.L1:
{
if (solto)
{
valorSaida = controle.PercentualVelocidadeSP;
}
else
{
var ultima_direcao = controle.TiposControle?.FirstOrDefault(x => x.Tipo == Dispositivo)?.UltimaDirecao ?? Direcao.Parado;
bool emMovimento = ultima_direcao == Direcao.Frente || ultima_direcao == Direcao.Tras;
if (emMovimento && controle.PercentualVelocidadeSP > 0)
valorSaida = Math.Max(velMin, controle.PercentualVelocidadeSP - 1);
}
break;
}
case BotoesJoystick.R1:
{
if (solto)
{
valorSaida = controle.PercentualVelocidadeSP;
}
else
{
var ultima_direcao = controle.TiposControle?.FirstOrDefault(x => x.Tipo == T_Code.Mov)?.UltimaDirecao ?? Direcao.Parado;
bool emMovimento = ultima_direcao == Direcao.Frente || ultima_direcao == Direcao.Tras;
if (emMovimento && controle.PercentualVelocidadeSP > 0)
valorSaida = Math.Min(velMax, controle.PercentualVelocidadeSP + 1);
}
break;
}
// =========================================================
// BICOS PULVERIZADORES
// =========================================================
case BotoesJoystick.Touchpad:
estadoSaida = !solto;
break;
}
if (solto && PararAoSoltar)
direcao = Direcao.Parado;
ValorAplicar = valorSaida;
EstadoAplicar = estadoSaida;
}
public DeParaComandos Clone()
{
return new DeParaComandos()
{
botaoJoy = botaoJoy,
key = key,
Dispositivo = Dispositivo,
PararAoSoltar = PararAoSoltar,
IDcontrolar = IDcontrolar,
Comando = Comando,
direcao = direcao,
ValorAplicar = ValorAplicar,
EstadoAplicar = EstadoAplicar,
};
}
}
public enum ComparativoAnalogico
{
Igual,
Maior,
Menor
}
public class BotoesPressionadosValores
{
public BotoesJoystick botao { get; set; }
public int valor { get; set; }
}
}