2024-10-03 18:21:30 +00:00
|
|
|
|
using AgroBase.Models.Modules;
|
2024-01-03 10:52:37 +00:00
|
|
|
|
using AgroBase.Services;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2024-05-17 12:49:47 +00:00
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
|
using System.Drawing;
|
2024-01-03 10:52:37 +00:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Threading.Tasks;
|
2024-04-15 10:57:37 +00:00
|
|
|
|
using System.Windows.Forms;
|
2024-07-26 19:54:25 +00:00
|
|
|
|
using static AgroBase.Models.Enums;
|
2024-05-17 12:49:47 +00:00
|
|
|
|
using System.Drawing.Drawing2D;
|
2024-07-05 18:59:05 +00:00
|
|
|
|
using Microsoft.Win32;
|
|
|
|
|
|
using System.Reflection;
|
2024-07-12 19:44:56 +00:00
|
|
|
|
using System.IO;
|
2024-11-25 12:14:59 +00:00
|
|
|
|
using System.Threading;
|
2025-03-26 17:42:33 +00:00
|
|
|
|
using AgroBase.Models.Components;
|
2025-05-06 18:47:59 +00:00
|
|
|
|
using Microsoft.Web.WebView2.WinForms;
|
|
|
|
|
|
using Microsoft.Web.WebView2.Core;
|
2025-06-06 20:46:08 +00:00
|
|
|
|
using AgroBase.Services.Operadores;
|
2025-09-02 13:49:11 +00:00
|
|
|
|
using System.Diagnostics;
|
2025-11-06 16:18:32 +00:00
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using AgroMonitor;
|
2026-03-05 13:50:01 +00:00
|
|
|
|
using System.Net;
|
2026-03-05 15:18:40 +00:00
|
|
|
|
using System.Net.Sockets;
|
2024-01-03 10:52:37 +00:00
|
|
|
|
|
|
|
|
|
|
namespace AgroBase.Models
|
|
|
|
|
|
{
|
|
|
|
|
|
public class Variaveis
|
|
|
|
|
|
{
|
2025-03-27 11:47:00 +00:00
|
|
|
|
public static bool Producao { get; set; } = false;
|
|
|
|
|
|
public static bool DebugMode { get; set; } = true;
|
|
|
|
|
|
public static bool Fechando { get; set; } = false;
|
|
|
|
|
|
|
2024-01-03 10:52:37 +00:00
|
|
|
|
public static string CaminhoSistema { get; set; } = AppDomain.CurrentDomain.BaseDirectory;
|
2024-09-24 19:02:54 +00:00
|
|
|
|
public static string NomeAplicacao
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
|
|
|
|
|
string name = assembly.GetName().Name;
|
|
|
|
|
|
return name.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public static string Versao
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
|
|
|
|
|
Version version = assembly.GetName().Version;
|
|
|
|
|
|
return version.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-04-15 10:57:37 +00:00
|
|
|
|
public static string CaminhoLogsDispositivos { get; } = "Logs/";
|
|
|
|
|
|
public static string CaminhoOperacoes { get; } = "Operacoes/";
|
2024-07-12 19:44:56 +00:00
|
|
|
|
public static string CaminhoOperacoesSalvas { get; } = "OperacoesSalvas\\";
|
2024-05-06 16:15:33 +00:00
|
|
|
|
public static string CaminhoMapasConvertidos { get; } = "Mapas/";
|
2024-05-10 12:28:14 +00:00
|
|
|
|
public static string CaminhoParametros { get; } = "Parametros/";
|
2024-10-14 20:06:12 +00:00
|
|
|
|
public static string CaminhoModelos { get; } = "C:\\AgroBaseModels\\";
|
2024-01-03 10:52:37 +00:00
|
|
|
|
public static List<IDispositivosService> DispositivosConectados { get; set; } = new List<IDispositivosService>();
|
2026-03-26 20:10:03 +00:00
|
|
|
|
public static OperacaoModel OperacaoEmAndamento { get; set; } = new OperacaoModel();
|
2025-11-06 16:18:32 +00:00
|
|
|
|
public static MqttService MqttServiceLocal { get; set; }
|
|
|
|
|
|
public static MqttService MqttServiceBase { get; set; }
|
2025-04-30 21:49:17 +00:00
|
|
|
|
public static LoRaEspService LoraService
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return OperacaoEmAndamento.DispSen?.Dados?.loRaService;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-18 20:03:37 +00:00
|
|
|
|
public static LoRaParametrosModel LoraBaseParametros
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return OperacaoEmAndamento.DispSen?.Dados?.LoRaParametrosBase;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-03 16:19:16 +00:00
|
|
|
|
public static string FaixaIPwifi { get; set; } = "192.168.100";
|
2024-12-02 19:12:39 +00:00
|
|
|
|
public static string FaixaIPethernet { get; set; } = "192.168.105";
|
2024-12-03 16:19:16 +00:00
|
|
|
|
public static string IP_Host { get; set; } = FaixaIPwifi + ".105";
|
2024-12-05 12:52:28 +00:00
|
|
|
|
public static string NomeRedeWifiEquipamento { get; set; } = "Agrobot";
|
|
|
|
|
|
public static string SenhaRedeWifiEquipamento { get; set; } = "4321agro";
|
2024-07-26 19:54:25 +00:00
|
|
|
|
public static bool IsAgroMonitor
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return CaminhoSistema.Contains("AgroMonitor");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-11 20:13:15 +00:00
|
|
|
|
|
2025-04-30 21:49:17 +00:00
|
|
|
|
public static byte ID_Num_sMOD { get; } = 0;
|
|
|
|
|
|
public static byte ID_Num_sTOD { get; } = 250;
|
|
|
|
|
|
public static byte ID_Num_sLRA { get; } = 251;
|
2025-11-06 16:18:32 +00:00
|
|
|
|
|
|
|
|
|
|
public static async void IniciarMQTT()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (MqttServiceLocal != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var topico in MqttServiceLocal.Topicos.Where(x => x.Inscrever))
|
|
|
|
|
|
{
|
|
|
|
|
|
await MqttServiceLocal.UnsubscribeAsync(topico);
|
|
|
|
|
|
}
|
|
|
|
|
|
MqttServiceLocal.Topicos.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-14 17:11:58 +00:00
|
|
|
|
if (MqttServiceBase != null)
|
2025-11-06 16:18:32 +00:00
|
|
|
|
{
|
|
|
|
|
|
foreach (var topico in MqttServiceBase.Topicos.Where(x => x.Inscrever))
|
|
|
|
|
|
{
|
|
|
|
|
|
await MqttServiceBase.UnsubscribeAsync(topico);
|
|
|
|
|
|
}
|
|
|
|
|
|
MqttServiceBase.Topicos.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-24 16:05:50 +00:00
|
|
|
|
MqttServiceLocal = new MqttService("localhost", 1883, VariaveisEquipamento.Parametros.serial_number, true, msg => Console.WriteLine($"[MQTT localhost:{1883}] - {msg}"));
|
2025-11-06 16:18:32 +00:00
|
|
|
|
await MqttServiceLocal.AdicionarNovoTopico(MapasVariaveisModel.TopicoCoordenadasGPS);
|
|
|
|
|
|
await MqttServiceLocal.AdicionarNovoTopico(MapasVariaveisModel.TopicoTrajetoriaDinamica);
|
|
|
|
|
|
await MqttServiceLocal.AdicionarNovoTopico(MapasVariaveisModel.TopicoSelecaoRuasMapa, true, 1, async (message) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
OperacaoEmAndamento.Mapa.AtualizarRuasSelecionadas();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-11-14 17:11:58 +00:00
|
|
|
|
MqttServiceBase = new MqttService(VariaveisEquipamento.Parametros.base_ip, 1883, VariaveisEquipamento.Parametros.serial_number, false, msg => Console.WriteLine($"[MQTT {VariaveisEquipamento.Parametros.base_ip}:{1883}] - {msg}"));
|
|
|
|
|
|
await MqttServiceBase.AdicionarNovoTopico(VariaveisMonitoramento.TopicoMqttDispositivos);
|
|
|
|
|
|
await MqttServiceBase.AdicionarNovoTopico(VariaveisEquipamento.TopicoMqttTelemetria.Replace("<id>", VariaveisEquipamento.Parametros.serial_number));
|
2025-11-24 16:05:50 +00:00
|
|
|
|
await MqttServiceBase.AdicionarNovoTopico(VariaveisEquipamento.TopicoMqttParametros.Replace("<id>", VariaveisEquipamento.Parametros.serial_number));
|
|
|
|
|
|
await MqttServiceBase.AdicionarNovoTopico(VariaveisEquipamento.TopicoMqttHeartbeat.Replace("<id>", VariaveisEquipamento.Parametros.serial_number), true, 1, async (message) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(message.Mensagem)) return;
|
|
|
|
|
|
VariaveisOperacao.PosicaoBase.UltimoComandoRespondido = DateTime.Now;
|
|
|
|
|
|
});
|
2025-11-14 17:11:58 +00:00
|
|
|
|
await MqttServiceBase.AdicionarNovoTopico(VariaveisEquipamento.TopicoMqttComandos.Replace("<id>", VariaveisEquipamento.Parametros.serial_number), true, 1, async (message) =>
|
2025-11-06 16:18:32 +00:00
|
|
|
|
{
|
2025-11-24 16:05:50 +00:00
|
|
|
|
if (string.IsNullOrEmpty(message.Mensagem)) return;
|
2025-11-14 17:11:58 +00:00
|
|
|
|
try
|
2025-11-06 16:18:32 +00:00
|
|
|
|
{
|
2025-11-14 17:11:58 +00:00
|
|
|
|
string json = message.Mensagem;
|
2025-11-24 16:05:50 +00:00
|
|
|
|
var cmd = JsonConvert.DeserializeObject<OperacaoComandoBaseModel>(json);
|
2025-11-14 17:11:58 +00:00
|
|
|
|
OperacaoEmAndamento.ExecutaComandoDaBase(cmd);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"Erro ao deserializar comando da base: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
await MqttServiceBase.AdicionarNovoTopico(VariaveisMonitoramento.TopicoMqttRTCM, true, 1, async (message) =>
|
2025-11-06 16:18:32 +00:00
|
|
|
|
{
|
2025-11-14 17:11:58 +00:00
|
|
|
|
if (message.Bytes == null || message.Bytes.Length == 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
try
|
2025-11-06 16:18:32 +00:00
|
|
|
|
{
|
2025-11-14 17:11:58 +00:00
|
|
|
|
var bytes = message.Bytes;
|
|
|
|
|
|
if (bytes != null && bytes.Length > 0)
|
2025-11-06 16:18:32 +00:00
|
|
|
|
{
|
2025-11-14 17:11:58 +00:00
|
|
|
|
GPSService.AplicarCorrecaoRTK_Mqtt(bytes, bytes.Length);
|
2025-11-06 16:18:32 +00:00
|
|
|
|
}
|
2025-11-14 17:11:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
2025-11-06 16:18:32 +00:00
|
|
|
|
{
|
2025-11-14 17:11:58 +00:00
|
|
|
|
Console.WriteLine($"Erro ao deserializar RTCM da base: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
await MqttServiceBase.AdicionarNovoTopico(VariaveisMonitoramento.TopicoMqttPosicao, true, 1, async (message) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(message.Mensagem))
|
|
|
|
|
|
return;
|
|
|
|
|
|
try
|
2025-11-06 16:18:32 +00:00
|
|
|
|
{
|
2025-11-14 17:11:58 +00:00
|
|
|
|
string json = message.Mensagem;
|
|
|
|
|
|
var posicao = JsonConvert.DeserializeObject<GPSModel>(json);
|
2025-11-24 16:05:50 +00:00
|
|
|
|
posicao.Momento = DateTime.Now;
|
2025-11-14 17:11:58 +00:00
|
|
|
|
VariaveisOperacao.PosicaoBase = posicao;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"Erro ao deserializar dados da base: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-11-06 16:18:32 +00:00
|
|
|
|
}
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
|
2026-03-05 02:03:31 +00:00
|
|
|
|
public static UdpReliableChannel UdpChannel;
|
2026-03-05 13:50:01 +00:00
|
|
|
|
private static Action<byte, byte[], IPEndPoint> _onPacketHandler;
|
|
|
|
|
|
|
|
|
|
|
|
// Último pacote UDP recebido (qualquer type, inclusive heartbeat)
|
|
|
|
|
|
private static long _lastPacketTicksUtc = 0;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// Último comando válido recebido (somente type 0x01 + parse ok)
|
|
|
|
|
|
private static long _lastValidCmdTicksUtc = 0;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// Guards anti-reentrância
|
2026-03-05 02:03:31 +00:00
|
|
|
|
private static int _failSafeTickRunning = 0;
|
2026-03-05 13:50:01 +00:00
|
|
|
|
private static int _udpWatchdogTickRunning = 0;
|
|
|
|
|
|
private static int _udpRestarting = 0;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
|
|
|
|
|
private static AsyncTaskTimerModel _tmrFailSafe;
|
2026-03-05 13:50:01 +00:00
|
|
|
|
private static AsyncTaskTimerModel _tmrUdpWatchdog;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
|
|
|
|
|
private const int FAILSAFE_MS = 400;
|
2026-03-05 16:53:30 +00:00
|
|
|
|
private const int UDP_RESTART_MS = 5000;
|
2026-03-05 13:50:01 +00:00
|
|
|
|
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
|
|
|
|
|
public static void IniciarUDP()
|
|
|
|
|
|
{
|
2026-03-05 13:50:01 +00:00
|
|
|
|
StartUdpChannel();
|
|
|
|
|
|
|
2026-03-05 16:53:30 +00:00
|
|
|
|
_tmrFailSafe?.Dispose();
|
2026-03-05 13:50:01 +00:00
|
|
|
|
_tmrFailSafe = new AsyncTaskTimerModel("tmrFailSafe", tmrFailSafe_Tick, 100);
|
|
|
|
|
|
_tmrFailSafe.Start();
|
|
|
|
|
|
|
2026-03-05 16:53:30 +00:00
|
|
|
|
_tmrUdpWatchdog?.Dispose();
|
2026-03-05 13:50:01 +00:00
|
|
|
|
_tmrUdpWatchdog = new AsyncTaskTimerModel("tmrUdpWatchdog", tmrUdpWatchdog_Tick, 250);
|
|
|
|
|
|
_tmrUdpWatchdog.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void StartUdpChannel()
|
|
|
|
|
|
{
|
2026-03-05 16:53:30 +00:00
|
|
|
|
//StopUdpChannel();
|
2026-03-05 13:50:01 +00:00
|
|
|
|
|
2026-03-05 16:53:30 +00:00
|
|
|
|
UdpChannel?.Stop();
|
|
|
|
|
|
UdpChannel?.Dispose();
|
2026-03-05 02:03:31 +00:00
|
|
|
|
UdpChannel = new UdpReliableChannel();
|
|
|
|
|
|
UdpChannel.Start(VariaveisPortas.Ethernet_UDP_RX);
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// cria 1 vez e reutiliza a MESMA referência
|
|
|
|
|
|
_onPacketHandler = OnUdpPacket;
|
|
|
|
|
|
UdpChannel.OnPacket += _onPacketHandler;
|
|
|
|
|
|
|
|
|
|
|
|
Interlocked.Exchange(ref _lastPacketTicksUtc, DateTime.UtcNow.Ticks);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 15:40:10 +00:00
|
|
|
|
public static void StopUdpChannel()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-03-05 16:53:30 +00:00
|
|
|
|
_tmrFailSafe?.Dispose();
|
|
|
|
|
|
_tmrUdpWatchdog?.Dispose();
|
|
|
|
|
|
|
2026-03-05 15:40:10 +00:00
|
|
|
|
UdpChannel?.Stop();
|
|
|
|
|
|
UdpChannel?.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch { }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
private static void OnUdpPacket(byte type, byte[] payload, IPEndPoint from)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Qualquer pacote marca link vivo (inclusive heartbeat)
|
|
|
|
|
|
Interlocked.Exchange(ref _lastPacketTicksUtc, DateTime.UtcNow.Ticks);
|
|
|
|
|
|
|
|
|
|
|
|
// Heartbeat: não faz nada além de manter vivo.
|
|
|
|
|
|
if (type == UdpReliableChannel.TYPE_HEARTBEAT)
|
2026-03-05 02:03:31 +00:00
|
|
|
|
{
|
2026-03-05 13:50:01 +00:00
|
|
|
|
Console.WriteLine("[UDP] Heartbeat");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-27 11:39:00 +00:00
|
|
|
|
//Console.WriteLine($"[UDP] Commnad {JsonConvert.SerializeObject(payload)}");
|
2026-03-05 16:53:30 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// Só controle entra daqui
|
|
|
|
|
|
if (type != UdpReliableChannel.TYPE_COMMAND) return;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
if (!UdpCtrlMessage.TryParse(payload, out var msg)) return;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-27 11:39:00 +00:00
|
|
|
|
//Console.WriteLine(JsonConvert.SerializeObject(msg));
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// Comando válido (controle) atualiza o failsafe
|
|
|
|
|
|
Interlocked.Exchange(ref _lastValidCmdTicksUtc, DateTime.UtcNow.Ticks);
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
bool emergencia = (msg.Flags & UdpCtrlFlags.Emergencia) != 0;
|
|
|
|
|
|
bool pausa = (msg.Flags & UdpCtrlFlags.Pausa) != 0;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
var disp = (T_Code)msg.Device;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
var controle = new OperacaoComandoBaseModel()
|
|
|
|
|
|
{
|
|
|
|
|
|
Emergencia = emergencia,
|
|
|
|
|
|
Pausa = pausa,
|
|
|
|
|
|
Tecla = msg.Key,
|
2026-03-23 20:08:01 +00:00
|
|
|
|
Solto = msg.released,
|
2026-03-05 13:50:01 +00:00
|
|
|
|
Dispositivo = disp,
|
|
|
|
|
|
Controle = new OperacaoComandoBaseControleModel(),
|
2026-03-05 02:03:31 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
if ((msg.Flags & UdpCtrlFlags.HasPayload) != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (disp == T_Code.Dir)
|
|
|
|
|
|
{
|
2026-03-19 17:09:39 +00:00
|
|
|
|
controle.Controle.AnguloSP = (msg.P1 / 100.0);
|
2026-03-05 13:50:01 +00:00
|
|
|
|
controle.Controle.TipoMovimentoDirecional = (TipoMovimentoDirecional)(byte)msg.P2;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (disp == T_Code.Mov)
|
|
|
|
|
|
{
|
2026-03-19 17:09:39 +00:00
|
|
|
|
controle.Controle.PercentualVelocidadeSP = (msg.P1 / 100.0);
|
2026-03-17 00:43:15 +00:00
|
|
|
|
controle.Controle.EmFreio = msg.P2 == 1;
|
2026-03-05 13:50:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Variaveis.OperacaoEmAndamento.ExecutaComandoDaBase(controle);
|
2026-03-05 02:03:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// ===== FAILSAFE: só considera comando de controle válido (não heartbeat) =====
|
2026-03-05 02:03:31 +00:00
|
|
|
|
public static async Task tmrFailSafe_Tick()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Interlocked.Exchange(ref _failSafeTickRunning, 1) == 1)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-03-05 13:50:01 +00:00
|
|
|
|
long ticks = Interlocked.Read(ref _lastValidCmdTicksUtc);
|
2026-03-05 02:03:31 +00:00
|
|
|
|
if (ticks == 0) return;
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
var lastCmd = new DateTime(ticks, DateTimeKind.Utc);
|
|
|
|
|
|
double ms = (DateTime.UtcNow - lastCmd).TotalMilliseconds;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
|
|
|
|
|
if (ms > FAILSAFE_MS)
|
|
|
|
|
|
{
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// Para por segurança
|
2026-03-23 20:08:01 +00:00
|
|
|
|
//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);
|
2026-03-05 02:03:31 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// desarma até chegar novo comando válido
|
|
|
|
|
|
Interlocked.Exchange(ref _lastValidCmdTicksUtc, 0);
|
2026-03-05 02:03:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
Interlocked.Exchange(ref _failSafeTickRunning, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await Task.CompletedTask;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// ===== WATCHDOG: se parou de chegar qualquer pacote, reinicia o socket =====
|
|
|
|
|
|
public static async Task tmrUdpWatchdog_Tick()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Interlocked.Exchange(ref _udpWatchdogTickRunning, 1) == 1)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
long ticks = Interlocked.Read(ref _lastPacketTicksUtc);
|
|
|
|
|
|
if (ticks == 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
var lastPkt = new DateTime(ticks, DateTimeKind.Utc);
|
|
|
|
|
|
double ms = (DateTime.UtcNow - lastPkt).TotalMilliseconds;
|
|
|
|
|
|
|
|
|
|
|
|
if (ms > UDP_RESTART_MS)
|
|
|
|
|
|
{
|
2026-03-05 15:18:40 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await RestartUdpChannelSafe();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (SocketException)
|
|
|
|
|
|
{
|
|
|
|
|
|
// normal durante restart
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (ObjectDisposedException)
|
|
|
|
|
|
{
|
|
|
|
|
|
// normal durante restart
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("[UDP] Watchdog exception: " + ex.Message);
|
|
|
|
|
|
}
|
2026-03-05 13:50:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
Interlocked.Exchange(ref _udpWatchdogTickRunning, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await Task.CompletedTask;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static async Task RestartUdpChannelSafe()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Interlocked.Exchange(ref _udpRestarting, 1) == 1)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_onPacketHandler != null)
|
|
|
|
|
|
UdpChannel.OnPacket -= _onPacketHandler;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch { }
|
|
|
|
|
|
|
2026-03-05 16:53:30 +00:00
|
|
|
|
Console.WriteLine("[UDP] Tentando reconectar...");
|
2026-03-05 13:50:01 +00:00
|
|
|
|
// Reinicia o canal
|
|
|
|
|
|
StartUdpChannel();
|
|
|
|
|
|
|
|
|
|
|
|
// Importante: zera comando válido, porque “socket caiu” geralmente quebra o fluxo de comando
|
|
|
|
|
|
Interlocked.Exchange(ref _lastValidCmdTicksUtc, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
Interlocked.Exchange(ref _udpRestarting, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await Task.CompletedTask;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 15:40:10 +00:00
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
|
2026-03-19 10:55:56 +00:00
|
|
|
|
public static void MostrarLog(string Mensagem)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"[AGROBASE] {Mensagem}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 13:50:01 +00:00
|
|
|
|
|
2024-07-26 19:54:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static class VariaveisPortas
|
|
|
|
|
|
{
|
2024-11-14 17:33:58 +00:00
|
|
|
|
public static int Ethernet_AT { get; } = 8899;
|
2024-11-19 12:19:42 +00:00
|
|
|
|
public static int Ethernet_TCP { get; } = 8899;
|
2026-03-05 02:03:31 +00:00
|
|
|
|
public static int Ethernet_UDP_TX { get; } = 5005;
|
|
|
|
|
|
public static int Ethernet_UDP_RX { get; } = 5006;
|
2024-07-26 19:54:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static class VariaveisEquipamento
|
|
|
|
|
|
{
|
2025-11-06 16:18:32 +00:00
|
|
|
|
private static ParametrosConfigEquipamentoModel _parametros;
|
|
|
|
|
|
public static ParametrosConfigEquipamentoModel Parametros
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_parametros == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_parametros = new ParametrosConfigEquipamentoModel();
|
|
|
|
|
|
_parametros.AtualizarParametros();
|
|
|
|
|
|
}
|
|
|
|
|
|
return _parametros;
|
|
|
|
|
|
}
|
|
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
_parametros = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-24 16:05:50 +00:00
|
|
|
|
public static string TopicoMqttHeartbeat { get; } = $"agrobot/v1/rover/<id>/heartbeat";
|
2025-11-06 16:18:32 +00:00
|
|
|
|
public static string TopicoMqttComandos { get; } = $"agrobot/v1/rover/<id>/cmd";
|
|
|
|
|
|
public static string TopicoMqttTelemetria { get; } = $"agrobot/v1/rover/<id>/telemetry";
|
2025-11-24 16:05:50 +00:00
|
|
|
|
public static string TopicoMqttParametros { get; } = $"agrobot/v1/rover/<id>/parameters";
|
2025-07-16 16:47:56 +00:00
|
|
|
|
public static double LarguraEsquerda { get; } = 44.0; // 62
|
|
|
|
|
|
public static double LarguraDireita { get; } = 44.0; // 22
|
|
|
|
|
|
public static double ComprimentoFrente { get; } = 7.0; // 7
|
|
|
|
|
|
public static double ComprimentoTras { get; } = 107.0; // 107
|
2025-02-11 08:04:02 +00:00
|
|
|
|
public static double DistanciaEntreEixos { get; } = 92.0;
|
2026-03-24 20:01:33 +00:00
|
|
|
|
public static double LeverArmFrontalCm { get; } = 100.0; // cm
|
|
|
|
|
|
public static double LeverArmLateralCm { get; } = 0.0; // cm
|
2024-08-13 16:28:41 +00:00
|
|
|
|
public static double LarguraEquipamentoMm
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
2025-07-11 16:38:08 +00:00
|
|
|
|
return ((LarguraEsquerda + LarguraDireita + 10.0) * 10.0);
|
2024-08-13 16:28:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-07 18:23:53 +00:00
|
|
|
|
public static double TensaoMinimaBateria { get; set; } = 30.0;
|
|
|
|
|
|
public static double TensaoMaximaBateria { get; set; } = 42.0;
|
2025-04-24 21:11:33 +00:00
|
|
|
|
public static double CorrenteMaximaBateria { get; set; } = 20.0;
|
2024-10-03 18:21:30 +00:00
|
|
|
|
public static double PercentualTensaoBateriaMin { get; set; } = 25.0;
|
2025-09-02 13:49:11 +00:00
|
|
|
|
public static double PercentualReservatorioMin { get; set; } = 8.0;
|
2025-12-17 19:27:06 +00:00
|
|
|
|
public static double PercentualReservatorioMinCritio { get; set; } = 5.0;
|
2024-10-03 18:21:30 +00:00
|
|
|
|
public static double PercentualToleranciaPressaoLinha { get; set; } = 0.15;
|
2025-11-24 16:05:50 +00:00
|
|
|
|
public static int QuantidadeCamerasSolo { get; set; } = 1;
|
2026-02-26 19:57:22 +00:00
|
|
|
|
public static int QuantidadeBicosPulverizadores { get; set; } = 7;
|
2025-12-02 18:42:15 +00:00
|
|
|
|
public static double AlturaBarraPulverizadoraCm { get; set; } = 80;
|
|
|
|
|
|
public static double ComprimentoBarraPulverizadoraCm { get; set; } = 103.7;
|
|
|
|
|
|
public static double DistanciaEntreBicosCm { get; set; } = 50;
|
2025-12-09 16:47:53 +00:00
|
|
|
|
public static double DensidadeHerbicida { get; set; } = 1.0;
|
2026-04-02 17:38:24 +00:00
|
|
|
|
public static double CapacidadeReservatorio { get; set; } = 60;
|
2026-03-23 12:44:45 +00:00
|
|
|
|
public static double PressaoMinimaCavitacao { get; set; } = 30.0;
|
2024-07-26 19:54:25 +00:00
|
|
|
|
public static double ReducaoDirecional { get; set; } = 20.0;
|
2026-04-01 11:26:37 +00:00
|
|
|
|
public static double PercentualVelMin
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return (RPM_Min_Roda / RPM_Max_Roda) * 100.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-07 18:23:53 +00:00
|
|
|
|
public static int RPM_Min_Roda { get; set; } = 15;
|
2024-10-03 18:21:30 +00:00
|
|
|
|
public static int RPM_Max_Roda
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
double RpmMax =
|
|
|
|
|
|
Variaveis.OperacaoEmAndamento.DispMvd != null ?
|
2024-11-12 19:43:38 +00:00
|
|
|
|
Variaveis.OperacaoEmAndamento.DispMvd.Dados.RPM_Max_MotorBLDC / RelacaoRPM :
|
2024-10-03 18:21:30 +00:00
|
|
|
|
100;
|
|
|
|
|
|
return (int)RpmMax;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-07-26 19:54:25 +00:00
|
|
|
|
public static double RelacaoRPM
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
2024-10-03 18:21:30 +00:00
|
|
|
|
double RelacaoRPM =
|
|
|
|
|
|
Variaveis.OperacaoEmAndamento.DispMvd != null ?
|
2024-11-12 19:43:38 +00:00
|
|
|
|
Variaveis.OperacaoEmAndamento.DispMvd.Dados.ReducaoMotorBLDC :
|
2024-10-03 18:21:30 +00:00
|
|
|
|
1;
|
2024-07-26 19:54:25 +00:00
|
|
|
|
return RelacaoRPM;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-10-14 20:06:12 +00:00
|
|
|
|
public static double DiametroRoda
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
double diametro =
|
|
|
|
|
|
Variaveis.OperacaoEmAndamento.DispMvd != null ?
|
2024-11-12 19:43:38 +00:00
|
|
|
|
Variaveis.OperacaoEmAndamento.DispMvd.Dados.DiametroRoda :
|
2024-10-14 20:06:12 +00:00
|
|
|
|
0.3556;
|
|
|
|
|
|
return diametro;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-01-10 18:52:38 +00:00
|
|
|
|
public static int NumeroPolosMotor
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
double NPolos =
|
|
|
|
|
|
Variaveis.OperacaoEmAndamento.DispMvd != null ?
|
|
|
|
|
|
Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Average(x => x.MovMotor.NumeroPolos) :
|
|
|
|
|
|
10;
|
|
|
|
|
|
return (int)NPolos;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-07-26 19:54:25 +00:00
|
|
|
|
public static int TempoEntrePingsConexao { get; set; } = 10000;
|
2025-02-03 19:54:39 +00:00
|
|
|
|
public static int TempoMaximoSalvarLogs
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return TempoEntrePingsConexao * 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-07-26 19:54:25 +00:00
|
|
|
|
public static Dictionary<AcaoFreio, int> AnguloFreio { get; } = new Dictionary<AcaoFreio, int>()
|
|
|
|
|
|
{
|
2024-12-05 12:52:28 +00:00
|
|
|
|
{ AcaoFreio.Ativar, 15 },
|
2024-07-26 19:54:25 +00:00
|
|
|
|
{ AcaoFreio.Desativar, 0 }
|
|
|
|
|
|
};
|
|
|
|
|
|
public static Dictionary<TipoMovimentoDirecional, int> AnguloMovimento { get; } = new Dictionary<TipoMovimentoDirecional, int>()
|
|
|
|
|
|
{
|
|
|
|
|
|
{ TipoMovimentoDirecional.RodasDianteiras, -1 },
|
|
|
|
|
|
{ TipoMovimentoDirecional.RodasTraseiras, -1 },
|
|
|
|
|
|
{ TipoMovimentoDirecional.MovimentoDiagonal, -1 },
|
|
|
|
|
|
{ TipoMovimentoDirecional.MovimentoArco, -1 },
|
|
|
|
|
|
{ TipoMovimentoDirecional.RotacionarNoEixo, 45 },
|
|
|
|
|
|
{ TipoMovimentoDirecional.MovimentoLateral, 90 },
|
2024-08-20 17:38:23 +00:00
|
|
|
|
{ TipoMovimentoDirecional.Diagnostico, 25 },
|
2024-07-26 19:54:25 +00:00
|
|
|
|
};
|
2026-03-13 18:58:02 +00:00
|
|
|
|
public static double AnguloInclinacaoRollMax { get; set; } = 30.0; // Frontal
|
|
|
|
|
|
public static double AnguloInclinacaoPitchMax { get; set; } = 45.0; // Lateral
|
2025-10-21 19:23:36 +00:00
|
|
|
|
public static string VozAlerta { get; set; } = "Microsoft Maria Desktop";
|
2024-07-26 19:54:25 +00:00
|
|
|
|
|
2025-11-06 16:18:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
2025-12-18 18:11:30 +00:00
|
|
|
|
public static SensorSinaleiroComportamentoModel ComportamentoLedPorStatus(StatusLED status_led = StatusLED.Apagado, StatusOperacao? status_operacao = null)
|
2024-04-02 18:33:15 +00:00
|
|
|
|
{
|
|
|
|
|
|
SensorSinaleiroComportamentoModel comportamento = new SensorSinaleiroComportamentoModel()
|
|
|
|
|
|
{
|
2025-12-18 18:11:30 +00:00
|
|
|
|
id = 99,
|
|
|
|
|
|
statusOperacao = status_operacao,
|
|
|
|
|
|
statusLED = status_led,
|
2024-07-26 19:54:25 +00:00
|
|
|
|
QtdPiscadas = 0,
|
2024-04-02 18:33:15 +00:00
|
|
|
|
TempoMs = 0,
|
|
|
|
|
|
PausaMs = 0,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-12-18 18:11:30 +00:00
|
|
|
|
if (status_operacao == null) return comportamento;
|
|
|
|
|
|
|
|
|
|
|
|
comportamento.id = (int)((StatusOperacao)status_operacao);
|
|
|
|
|
|
|
|
|
|
|
|
switch ((StatusOperacao)status_operacao)
|
2024-04-02 18:33:15 +00:00
|
|
|
|
{
|
|
|
|
|
|
case StatusOperacao.Parametrizando:
|
|
|
|
|
|
{
|
2025-12-18 18:11:30 +00:00
|
|
|
|
comportamento.QtdPiscadas = 2;
|
|
|
|
|
|
comportamento.TempoMs = 800;
|
|
|
|
|
|
comportamento.PausaMs = 2000;
|
2024-04-02 18:33:15 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2024-04-15 10:57:37 +00:00
|
|
|
|
case StatusOperacao.NaoIniciado:
|
|
|
|
|
|
case StatusOperacao.Concluido:
|
2024-04-02 18:33:15 +00:00
|
|
|
|
{
|
2025-12-18 18:11:30 +00:00
|
|
|
|
comportamento.QtdPiscadas = 1;
|
|
|
|
|
|
comportamento.TempoMs = 1000;
|
|
|
|
|
|
comportamento.PausaMs = 2000;
|
2024-04-02 18:33:15 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case StatusOperacao.Aguardando:
|
|
|
|
|
|
{
|
2025-12-18 18:11:30 +00:00
|
|
|
|
comportamento.QtdPiscadas = 3;
|
|
|
|
|
|
comportamento.TempoMs = 500;
|
|
|
|
|
|
comportamento.PausaMs = 800;
|
2024-04-02 18:33:15 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case StatusOperacao.Parado:
|
|
|
|
|
|
{
|
2025-12-18 18:11:30 +00:00
|
|
|
|
comportamento.QtdPiscadas = 5;
|
|
|
|
|
|
comportamento.TempoMs = 500;
|
|
|
|
|
|
comportamento.PausaMs = 2000;
|
2024-04-02 18:33:15 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2024-04-15 10:57:37 +00:00
|
|
|
|
case StatusOperacao.EmAndamento:
|
2024-04-02 18:33:15 +00:00
|
|
|
|
{
|
2025-12-18 18:11:30 +00:00
|
|
|
|
comportamento.QtdPiscadas = 1;
|
|
|
|
|
|
comportamento.TempoMs = 500;
|
|
|
|
|
|
comportamento.PausaMs = 1000;
|
2025-10-21 19:23:36 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case StatusOperacao.Calibrando:
|
|
|
|
|
|
{
|
2025-12-18 18:11:30 +00:00
|
|
|
|
comportamento.QtdPiscadas = 2;
|
|
|
|
|
|
comportamento.TempoMs = 500;
|
|
|
|
|
|
comportamento.PausaMs = 800;
|
2024-04-02 18:33:15 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return comportamento;
|
|
|
|
|
|
}
|
2024-01-03 10:52:37 +00:00
|
|
|
|
|
2024-05-20 10:16:23 +00:00
|
|
|
|
|
2025-08-07 18:23:53 +00:00
|
|
|
|
public static GPSModel SimularNovaPosicao_bkp(TipoMovimentoDirecional tipoMovimento, double anguloControle, double velocidadeMs, double tempoDelta, double anguloAtual, GPSModel PosicaoAtual)
|
2025-02-03 19:54:39 +00:00
|
|
|
|
{
|
2025-03-14 19:35:46 +00:00
|
|
|
|
// Fator de correção da curva
|
2025-03-19 23:06:30 +00:00
|
|
|
|
double k = 1.35 + 0.5 * Math.Exp(-Math.Abs(anguloControle) / 15.0);
|
2025-03-14 19:35:46 +00:00
|
|
|
|
|
2025-02-03 19:54:39 +00:00
|
|
|
|
// Converte o ângulo de controle para radianos
|
|
|
|
|
|
double anguloControleRad = anguloControle * (Math.PI / 180.0);
|
|
|
|
|
|
|
|
|
|
|
|
// Evita divisão por zero (caso o ângulo seja muito pequeno)
|
|
|
|
|
|
double R = Math.Abs(anguloControleRad) < 0.01 ? 99999 : (DistanciaEntreEixos / 100.0) / Math.Tan(anguloControleRad);
|
|
|
|
|
|
|
|
|
|
|
|
// Ajusta o raio de curva de acordo com o tipo de movimento
|
|
|
|
|
|
switch (tipoMovimento)
|
|
|
|
|
|
{
|
|
|
|
|
|
case TipoMovimentoDirecional.RodasDianteiras:
|
|
|
|
|
|
// Apenas as rodas dianteiras viram -> usa o raio padrão
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TipoMovimentoDirecional.RodasTraseiras:
|
|
|
|
|
|
// Apenas as rodas traseiras viram -> mesmo efeito que as dianteiras
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TipoMovimentoDirecional.MovimentoArco:
|
|
|
|
|
|
// Rodas dianteiras e traseiras viram em sentidos opostos, reduzindo muito o raio
|
|
|
|
|
|
R *= 0.5; // Reduz o raio pela metade para uma curva mais fechada
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case TipoMovimentoDirecional.MovimentoDiagonal:
|
|
|
|
|
|
// Todas as rodas viram na mesma direção, o robô desliza sem mudar a frente
|
|
|
|
|
|
// Isso significa que o ângulo do robô **não muda** ao longo do tempo
|
|
|
|
|
|
//return anguloAtual; // Mantém a orientação original
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-14 19:35:46 +00:00
|
|
|
|
// Aplica o fator de correção da curva
|
|
|
|
|
|
R *= k;
|
|
|
|
|
|
|
2025-02-03 19:54:39 +00:00
|
|
|
|
// Calcula a rotação angular com base na velocidade e no raio
|
|
|
|
|
|
double omega = velocidadeMs / R;
|
|
|
|
|
|
|
|
|
|
|
|
// Calcula a variação angular no tempo (em radianos)
|
|
|
|
|
|
double deltaTheta = omega * tempoDelta;
|
|
|
|
|
|
|
|
|
|
|
|
// Converte para graus e atualiza a orientação do robô
|
2025-02-06 19:01:41 +00:00
|
|
|
|
double novoAngulo = GPSUtils.NormalizarAngulo(anguloAtual + (deltaTheta * (180.0 / Math.PI)));
|
2025-02-03 19:54:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calcula a distância com base no tempo passado (em segundos)
|
|
|
|
|
|
double distancia = velocidadeMs * tempoDelta;
|
|
|
|
|
|
|
|
|
|
|
|
double latitude = PosicaoAtual.Latitude;
|
|
|
|
|
|
double longitude = PosicaoAtual.Longitude;
|
|
|
|
|
|
|
|
|
|
|
|
// Conversão de ângulo de graus para radianos
|
2025-07-11 16:38:08 +00:00
|
|
|
|
double anguloRad = novoAngulo * (Math.PI / 180);
|
2025-02-03 19:54:39 +00:00
|
|
|
|
|
|
|
|
|
|
// Raio da Terra em metros
|
2025-02-06 19:01:41 +00:00
|
|
|
|
double raioTerra = GPSUtils.RaioDaTerra;
|
2025-02-03 19:54:39 +00:00
|
|
|
|
|
|
|
|
|
|
// Calcular deslocamento de latitude em radianos
|
|
|
|
|
|
double deltaLat = distancia * Math.Cos(anguloRad) / raioTerra;
|
|
|
|
|
|
// Converter de radianos para graus
|
|
|
|
|
|
double _latAtt = latitude + deltaLat * (180 / Math.PI);
|
|
|
|
|
|
|
|
|
|
|
|
// Calcular deslocamento de longitude em radianos
|
|
|
|
|
|
double deltaLong = distancia * Math.Sin(anguloRad) / (raioTerra * Math.Cos(latitude * (Math.PI / 180)));
|
|
|
|
|
|
// Converter de radianos para graus
|
|
|
|
|
|
double _longAtt = longitude + deltaLong * (180 / Math.PI);
|
|
|
|
|
|
|
|
|
|
|
|
GPSModel novoPonto = new GPSModel()
|
|
|
|
|
|
{
|
|
|
|
|
|
Momento = DateTime.Now,
|
|
|
|
|
|
Latitude = _latAtt,
|
|
|
|
|
|
Longitude = _longAtt,
|
|
|
|
|
|
OrientacaoReal = novoAngulo,
|
|
|
|
|
|
Distancia = distancia,
|
|
|
|
|
|
Velocidade = velocidadeMs,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return novoPonto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-07 18:23:53 +00:00
|
|
|
|
public static GPSModel SimularNovaPosicao(TipoMovimentoDirecional tipoMovimento, double anguloControleDeg, double velocidadeMs, double tempoDelta, double anguloAtualDeg, GPSModel pos)
|
|
|
|
|
|
{
|
|
|
|
|
|
double L = DistanciaEntreEixos / 100.0;
|
|
|
|
|
|
|
|
|
|
|
|
// Defina deltas por modo
|
|
|
|
|
|
double dfDeg = 0, drDeg = 0;
|
|
|
|
|
|
switch (tipoMovimento)
|
|
|
|
|
|
{
|
|
|
|
|
|
case TipoMovimentoDirecional.RodasDianteiras: dfDeg = anguloControleDeg; drDeg = 0; break;
|
|
|
|
|
|
case TipoMovimentoDirecional.RodasTraseiras: dfDeg = 0; drDeg = anguloControleDeg; break;
|
|
|
|
|
|
case TipoMovimentoDirecional.MovimentoArco: dfDeg = anguloControleDeg; drDeg = -anguloControleDeg; break;
|
|
|
|
|
|
case TipoMovimentoDirecional.MovimentoDiagonal: dfDeg = anguloControleDeg; drDeg = anguloControleDeg; break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Curvatura geométrica
|
|
|
|
|
|
double tf = Math.Tan(dfDeg * Math.PI / 180.0);
|
|
|
|
|
|
double tr = Math.Tan(drDeg * Math.PI / 180.0);
|
|
|
|
|
|
if (Math.Abs(tf) < 1e-6) tf = 0;
|
|
|
|
|
|
if (Math.Abs(tr) < 1e-6) tr = 0;
|
|
|
|
|
|
|
|
|
|
|
|
double kappa = 0; // 1/m
|
2025-10-02 15:49:43 +00:00
|
|
|
|
if (tipoMovimento == TipoMovimentoDirecional.RodasDianteiras) kappa = tf / L;
|
2025-08-07 18:23:53 +00:00
|
|
|
|
else if (tipoMovimento == TipoMovimentoDirecional.RodasTraseiras) kappa = tr / L;
|
2025-10-02 15:49:43 +00:00
|
|
|
|
else if (tipoMovimento == TipoMovimentoDirecional.MovimentoArco) kappa = (tf - tr) / L;
|
|
|
|
|
|
else /*Diagonal,Lateral*/ kappa = 0;
|
2025-08-07 18:23:53 +00:00
|
|
|
|
|
|
|
|
|
|
// Correção dinâmica (opcional)
|
|
|
|
|
|
double Ku = 2.5; // ajuste fino em campo
|
|
|
|
|
|
double kappaEf = kappa / (1 + Ku * velocidadeMs * velocidadeMs);
|
|
|
|
|
|
|
|
|
|
|
|
// Integração
|
|
|
|
|
|
double theta = anguloAtualDeg * Math.PI / 180.0;
|
2025-09-02 13:49:11 +00:00
|
|
|
|
double omega = velocidadeMs * kappaEf; // rad/s
|
2025-08-07 18:23:53 +00:00
|
|
|
|
double dtheta = omega * tempoDelta;
|
2025-09-02 13:49:11 +00:00
|
|
|
|
theta += dtheta;
|
2025-08-07 18:23:53 +00:00
|
|
|
|
|
2025-09-02 13:49:11 +00:00
|
|
|
|
double heading = theta;
|
2025-10-02 15:49:43 +00:00
|
|
|
|
if (new List<TipoMovimentoDirecional>() { TipoMovimentoDirecional.MovimentoDiagonal, TipoMovimentoDirecional.MovimentoLateral }.Contains(tipoMovimento))
|
2025-09-02 13:49:11 +00:00
|
|
|
|
heading = theta + dfDeg * Math.PI / 180.0; // crab: desloca na direção do steering
|
2025-08-07 18:23:53 +00:00
|
|
|
|
|
2025-09-02 13:49:11 +00:00
|
|
|
|
// >>> mesma convenção do Python: 0=Norte
|
|
|
|
|
|
double dx = velocidadeMs * tempoDelta * Math.Sin(heading); // Leste(+)
|
|
|
|
|
|
double dy = velocidadeMs * tempoDelta * Math.Cos(heading); // Norte(+)
|
2025-08-07 18:23:53 +00:00
|
|
|
|
|
2026-03-12 20:03:24 +00:00
|
|
|
|
GPSModel ultimaPosicao = GPSService.historicoPosicao.Peek();
|
|
|
|
|
|
if (pos == null) return ultimaPosicao;
|
|
|
|
|
|
|
2025-09-02 13:49:11 +00:00
|
|
|
|
// geo
|
2025-08-07 18:23:53 +00:00
|
|
|
|
double R_earth = GPSUtils.RaioDaTerra;
|
|
|
|
|
|
double dLat = (dy / R_earth) * 180.0 / Math.PI;
|
|
|
|
|
|
double dLon = (dx / (R_earth * Math.Cos(pos.Latitude * Math.PI / 180.0))) * 180.0 / Math.PI;
|
|
|
|
|
|
|
2025-09-29 13:26:07 +00:00
|
|
|
|
double latitude = pos.Latitude + dLat;
|
|
|
|
|
|
double longitude = pos.Longitude + dLon;
|
|
|
|
|
|
|
2025-09-02 13:49:11 +00:00
|
|
|
|
GPSModel novaPosicao = new GPSModel
|
2025-08-07 18:23:53 +00:00
|
|
|
|
{
|
|
|
|
|
|
Momento = DateTime.Now,
|
2025-09-26 17:13:37 +00:00
|
|
|
|
Lat0 = GPSService.UltimaLeitura.Lat0,
|
|
|
|
|
|
Lon0 = GPSService.UltimaLeitura.Lon0,
|
2025-09-29 13:26:07 +00:00
|
|
|
|
Latitude = latitude,
|
|
|
|
|
|
LatitudeAnt = latitude,
|
|
|
|
|
|
Longitude = longitude,
|
|
|
|
|
|
LongitudeAnt = longitude,
|
2025-08-07 18:23:53 +00:00
|
|
|
|
OrientacaoReal = GPSUtils.NormalizarAngulo(theta * 180.0 / Math.PI),
|
|
|
|
|
|
Distancia = velocidadeMs * tempoDelta,
|
2025-09-02 13:49:11 +00:00
|
|
|
|
Velocidade = velocidadeMs,
|
|
|
|
|
|
Heartbeat = ultimaPosicao.Heartbeat,
|
|
|
|
|
|
TimestampOri = ultimaPosicao.TimestampOri.Clone(),
|
|
|
|
|
|
TimestampPos = ultimaPosicao.TimestampPos.Clone(),
|
2025-08-07 18:23:53 +00:00
|
|
|
|
};
|
2025-09-02 13:49:11 +00:00
|
|
|
|
|
2025-09-29 13:26:07 +00:00
|
|
|
|
//(double latCor, double lonCorr) = GeoLeverArm.FixLeverArmLatLon_Fast(latitude, longitude, novaPosicao.OrientacaoReal);
|
|
|
|
|
|
//novaPosicao.Latitude = latCor;
|
|
|
|
|
|
//novaPosicao.Longitude = lonCorr;
|
|
|
|
|
|
|
2025-09-02 13:49:11 +00:00
|
|
|
|
novaPosicao.TimestampOri.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
|
|
|
|
|
novaPosicao.TimestampPos.valor = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
2026-03-18 12:47:21 +00:00
|
|
|
|
|
2025-09-02 13:49:11 +00:00
|
|
|
|
return novaPosicao;
|
2025-08-07 18:23:53 +00:00
|
|
|
|
}
|
2025-02-03 19:54:39 +00:00
|
|
|
|
|
2025-11-06 16:18:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class ParametrosConfigEquipamentoModel
|
|
|
|
|
|
{
|
|
|
|
|
|
public bool Carregado { get; set; }
|
|
|
|
|
|
public string serial_number { get; set; }
|
|
|
|
|
|
public string base_ip { get; set; }
|
2025-11-24 16:05:50 +00:00
|
|
|
|
public int base_porta_caminho { get; set; }
|
|
|
|
|
|
public int base_porta_ervas { get; set; }
|
2026-06-08 19:25:27 +00:00
|
|
|
|
public string comunicacao_interface { get; set; }
|
2026-03-03 19:53:58 +00:00
|
|
|
|
public string rover_ip { get; set; }
|
|
|
|
|
|
public string rover_gateway { get; set; }
|
|
|
|
|
|
public string rover_submask { get; set; }
|
|
|
|
|
|
public string camera_caminhho_id { get; set; }
|
|
|
|
|
|
public List<string> camera_ervas_id { get; set; }
|
2026-06-08 19:25:27 +00:00
|
|
|
|
public string can_interface { get; set; }
|
|
|
|
|
|
public string can_ip_pc { get; set; }
|
|
|
|
|
|
public string can_ip_modulo { get; set; }
|
|
|
|
|
|
public int can_porta { get; set; }
|
|
|
|
|
|
public string can_gateway { get; set; }
|
|
|
|
|
|
public string can_submask { get; set; }
|
2025-11-06 16:18:32 +00:00
|
|
|
|
|
|
|
|
|
|
public void AtualizarParametros()
|
|
|
|
|
|
{
|
|
|
|
|
|
string config_path = Path.Combine(Variaveis.CaminhoSistema, Variaveis.CaminhoParametros, "config.json");
|
|
|
|
|
|
if (!File.Exists(config_path))
|
|
|
|
|
|
Carregado = false;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
string json = File.ReadAllText(config_path);
|
|
|
|
|
|
var par = JsonConvert.DeserializeObject<ParametrosConfigEquipamentoModel>(json);
|
|
|
|
|
|
|
|
|
|
|
|
serial_number = par.serial_number;
|
|
|
|
|
|
base_ip = par.base_ip;
|
2025-11-24 16:05:50 +00:00
|
|
|
|
base_porta_caminho = par.base_porta_caminho;
|
|
|
|
|
|
base_porta_ervas = par.base_porta_ervas;
|
2025-11-06 16:18:32 +00:00
|
|
|
|
|
2026-06-08 19:25:27 +00:00
|
|
|
|
comunicacao_interface = par.comunicacao_interface;
|
2026-03-03 19:53:58 +00:00
|
|
|
|
rover_ip = par.rover_ip;
|
|
|
|
|
|
rover_gateway = par.rover_gateway;
|
|
|
|
|
|
rover_submask = par.rover_submask;
|
2026-06-08 19:25:27 +00:00
|
|
|
|
|
|
|
|
|
|
can_interface = par.can_interface;
|
|
|
|
|
|
can_ip_pc = par.can_ip_pc;
|
|
|
|
|
|
can_ip_modulo = par.can_ip_modulo;
|
|
|
|
|
|
can_porta = par.can_porta;
|
|
|
|
|
|
can_gateway = par.can_gateway;
|
|
|
|
|
|
can_submask = par.can_submask;
|
|
|
|
|
|
|
2026-03-03 19:53:58 +00:00
|
|
|
|
camera_caminhho_id = par.camera_caminhho_id;
|
|
|
|
|
|
camera_ervas_id = par.camera_ervas_id;
|
|
|
|
|
|
|
2025-11-06 16:18:32 +00:00
|
|
|
|
Carregado = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"Erro ao carregar arquivo de configuracoes: {ex.Message}");
|
|
|
|
|
|
Carregado = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-03 19:53:58 +00:00
|
|
|
|
|
|
|
|
|
|
public bool SalvarParametros()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
string config_path = Path.Combine(Variaveis.CaminhoSistema, Variaveis.CaminhoParametros, "config.json");
|
|
|
|
|
|
File.WriteAllText(config_path, JsonConvert.SerializeObject(this));
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"Erro ao salvar arquivo de configuracoes: {ex.Message}");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-06 16:18:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-20 10:16:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-12 19:44:56 +00:00
|
|
|
|
public static class VariaveisOperacao
|
|
|
|
|
|
{
|
2024-10-10 19:58:57 +00:00
|
|
|
|
public static double TempoManobra { get; set; } = 10;
|
|
|
|
|
|
public static double PercentualComErvas
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Variaveis.IsAgroMonitor && Variaveis.OperacaoEmAndamento.DispAtu != null)
|
|
|
|
|
|
{
|
2025-12-17 19:27:06 +00:00
|
|
|
|
return (Variaveis.OperacaoEmAndamento.Sensoriamento.Atuador?.PercentualErvasTerreno ?? 0) / 100.0;
|
2024-10-10 19:58:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
return 0.3;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public static double PercentualSemErvas
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Variaveis.IsAgroMonitor && Variaveis.OperacaoEmAndamento.DispAtu != null)
|
|
|
|
|
|
{
|
2025-12-17 19:27:06 +00:00
|
|
|
|
return (100.0 - (Variaveis.OperacaoEmAndamento.Sensoriamento.Atuador?.PercentualErvasTerreno ?? 0)) / 100.0;
|
2024-10-10 19:58:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
return 0.7;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-05-09 20:17:28 +00:00
|
|
|
|
public static GPSModel PosicaoBase { get; set; } = new GPSModel();
|
2025-03-19 23:06:30 +00:00
|
|
|
|
|
2025-06-06 20:46:08 +00:00
|
|
|
|
public static OperadoresService Operadores { get; set; } = new OperadoresService();
|
2025-03-20 16:43:12 +00:00
|
|
|
|
|
|
|
|
|
|
public static void RegistrarLogDispositivo(List<string> Logs, T_Code Dispositivo, string Sulfixo, int MinLogs = 100)
|
2025-03-19 23:06:30 +00:00
|
|
|
|
{
|
2025-05-08 12:29:29 +00:00
|
|
|
|
var Caminho = Path.Combine(Variaveis.CaminhoLogsDispositivos, Dispositivo.ToString());
|
2025-03-20 16:43:12 +00:00
|
|
|
|
|
2025-05-08 12:29:29 +00:00
|
|
|
|
string NomeArquivo = Dispositivo.ToString() + Sulfixo;
|
2025-03-20 16:43:12 +00:00
|
|
|
|
|
2025-05-08 12:29:29 +00:00
|
|
|
|
var Disp = SerialService.DispositivosMapeados.FirstOrDefault(x => x.Dispositivo == Dispositivo);
|
2025-03-20 16:43:12 +00:00
|
|
|
|
|
2025-05-08 12:29:29 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Disp != null)
|
2025-03-19 23:06:30 +00:00
|
|
|
|
{
|
2025-05-08 12:29:29 +00:00
|
|
|
|
NomeArquivo = Disp.CriadoEm.ToString("dd_MM_yyyy_HH_mm_ss") + Sulfixo;
|
|
|
|
|
|
}
|
2025-03-19 23:06:30 +00:00
|
|
|
|
|
2025-05-08 12:29:29 +00:00
|
|
|
|
if (Logs.Count() >= MinLogs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Directory.Exists(Caminho))
|
2025-03-19 23:06:30 +00:00
|
|
|
|
{
|
2025-05-08 12:29:29 +00:00
|
|
|
|
Directory.CreateDirectory(Caminho);
|
|
|
|
|
|
}
|
2025-03-20 16:43:12 +00:00
|
|
|
|
|
2025-05-08 12:29:29 +00:00
|
|
|
|
Caminho = Path.Combine(Caminho, NomeArquivo);
|
2025-03-20 16:43:12 +00:00
|
|
|
|
|
2025-05-08 12:29:29 +00:00
|
|
|
|
if (!File.Exists(Caminho))
|
|
|
|
|
|
{
|
|
|
|
|
|
File.WriteAllLines(Caminho, Logs);
|
2025-03-20 16:43:12 +00:00
|
|
|
|
}
|
2025-05-08 12:29:29 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
File.AppendAllLines(Caminho, Logs);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Logs.Clear();
|
2025-03-20 16:43:12 +00:00
|
|
|
|
}
|
2025-05-08 12:29:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("Erro ao salvar log: " + NomeArquivo);
|
2025-03-19 23:06:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-12 19:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-03 10:52:37 +00:00
|
|
|
|
public static class FuncoesGlobais
|
|
|
|
|
|
{
|
2024-07-05 18:59:05 +00:00
|
|
|
|
public static void AddApplicationToStartup()
|
|
|
|
|
|
{
|
|
|
|
|
|
string appName = Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyTitleAttribute>()?.Title ?? "DefaultAppName";
|
|
|
|
|
|
string appPath = Application.ExecutablePath;
|
|
|
|
|
|
|
|
|
|
|
|
RegistryKey registryKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
|
|
|
|
|
|
if (registryKey.GetValue(appName) == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
registryKey.SetValue(appName, appPath);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-03 10:52:37 +00:00
|
|
|
|
public static string TimestampToDate(double timestamp, string Formatacao = "HH:mm:ss.fff")
|
|
|
|
|
|
{
|
|
|
|
|
|
// Converta o timestamp Unix para DateTime
|
|
|
|
|
|
// A época Unix começa em 1 de janeiro de 1970
|
|
|
|
|
|
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
|
|
|
|
DateTime date = epoch.AddSeconds(timestamp);
|
|
|
|
|
|
|
|
|
|
|
|
// Converter a data UTC para a data no fuso horário local do sistema
|
|
|
|
|
|
DateTime localDate = date.ToLocalTime();
|
|
|
|
|
|
|
|
|
|
|
|
// Formate a data para hh:mm:ss.fff (note a correção de MM para mm)
|
|
|
|
|
|
string formattedDate = localDate.ToString(Formatacao, System.Globalization.CultureInfo.InvariantCulture);
|
|
|
|
|
|
|
|
|
|
|
|
return formattedDate;
|
|
|
|
|
|
}
|
2024-04-01 11:03:25 +00:00
|
|
|
|
|
2024-04-15 10:57:37 +00:00
|
|
|
|
public static T FindControlRecursive<T>(Control parent, string controlName) where T : Control
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (Control child in parent.Controls)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (child is T foundControl && child.Name == controlName)
|
|
|
|
|
|
{
|
|
|
|
|
|
return foundControl; // Retorna o controle se encontrado
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
T foundInChildren = FindControlRecursive<T>(child, controlName);
|
|
|
|
|
|
if (foundInChildren != null) return foundInChildren; // Retorna o controle se encontrado em controles filhos
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null; // Retorna null se o controle não for encontrado
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-29 20:02:19 +00:00
|
|
|
|
public static void PreencheValorProgressBar(ProgressBar pgb, double Valor)
|
|
|
|
|
|
{
|
|
|
|
|
|
pgb.Value =
|
|
|
|
|
|
Valor >= pgb.Minimum && Valor <= pgb.Maximum ? pgb.Value = Convert.ToInt32(Valor) :
|
|
|
|
|
|
Valor < pgb.Minimum ? pgb.Value = pgb.Minimum :
|
|
|
|
|
|
Valor > pgb.Maximum ? pgb.Value = pgb.Maximum :
|
|
|
|
|
|
pgb.Minimum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-03 18:21:30 +00:00
|
|
|
|
public static void PreencheValorTrackBar(TrackBar tkb, double Valor)
|
|
|
|
|
|
{
|
|
|
|
|
|
tkb.Value =
|
|
|
|
|
|
Valor >= tkb.Minimum && Valor <= tkb.Maximum ? tkb.Value = Convert.ToInt32(Valor) :
|
|
|
|
|
|
Valor < tkb.Minimum ? tkb.Value = tkb.Minimum :
|
|
|
|
|
|
Valor > tkb.Maximum ? tkb.Value = tkb.Maximum :
|
|
|
|
|
|
tkb.Minimum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-20 10:16:23 +00:00
|
|
|
|
|
2024-05-17 12:49:47 +00:00
|
|
|
|
// Método para salvar o frame atual em JPEG
|
|
|
|
|
|
public static void SalvarFrameJPEG(Bitmap frame, string caminhoArquivo)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Define as opções de codificação JPEG (qualidade de 90%)
|
|
|
|
|
|
EncoderParameters encoderParams = new EncoderParameters(1);
|
|
|
|
|
|
encoderParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 90L);
|
|
|
|
|
|
|
|
|
|
|
|
// Salva o bitmap em formato JPEG
|
|
|
|
|
|
ImageCodecInfo jpegCodec = GetEncoderInfo(ImageFormat.Jpeg);
|
|
|
|
|
|
frame.Save(caminhoArquivo, jpegCodec, encoderParams);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Método auxiliar para obter informações do codec JPEG
|
|
|
|
|
|
private static ImageCodecInfo GetEncoderInfo(ImageFormat format)
|
|
|
|
|
|
{
|
|
|
|
|
|
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
|
|
|
|
|
|
foreach (ImageCodecInfo codec in codecs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (codec.FormatID == format.Guid)
|
|
|
|
|
|
{
|
|
|
|
|
|
return codec;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-13 16:28:41 +00:00
|
|
|
|
public static ImageCodecInfo GetEncoderInfo(string mimeType)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Obtém todos os codecs de imagem disponíveis
|
|
|
|
|
|
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
|
|
|
|
|
|
// Procura pelo codec que corresponde ao mimeType fornecido
|
|
|
|
|
|
foreach (ImageCodecInfo codec in codecs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (codec.MimeType == mimeType)
|
|
|
|
|
|
{
|
|
|
|
|
|
return codec;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-05-17 12:49:47 +00:00
|
|
|
|
public static Image EspelharImagemHorizontalmente(Image img)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Cria uma cópia da imagem do PictureBox
|
|
|
|
|
|
Bitmap originalImage = new Bitmap(img);
|
|
|
|
|
|
|
|
|
|
|
|
// Cria uma nova imagem para conter a imagem espelhada
|
|
|
|
|
|
Bitmap mirroredImage = new Bitmap(originalImage.Width, originalImage.Height);
|
|
|
|
|
|
|
|
|
|
|
|
// Cria uma matriz de transformação para espelhar horizontalmente
|
|
|
|
|
|
Matrix matrix = new Matrix();
|
|
|
|
|
|
matrix.Scale(-1, 1);
|
|
|
|
|
|
|
|
|
|
|
|
// Cria um objeto Graphics para desenhar a imagem espelhada
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(mirroredImage))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Espelha a imagem original na nova imagem
|
|
|
|
|
|
g.Transform = matrix;
|
|
|
|
|
|
g.DrawImage(originalImage, new Rectangle(-originalImage.Width, 0, originalImage.Width, originalImage.Height));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return mirroredImage;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Método para rotacionar a imagem
|
2024-05-28 13:58:25 +00:00
|
|
|
|
public static Image RotacionarImagem(Image img, double rotationAngle, bool redimensionarParaCaber = true)
|
2024-05-17 12:49:47 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Calcula as novas dimensões do bitmap com base na rotação
|
|
|
|
|
|
double angleRad = rotationAngle / 180.0 * Math.PI;
|
|
|
|
|
|
double sin = Math.Abs(Math.Sin(angleRad));
|
|
|
|
|
|
double cos = Math.Abs(Math.Cos(angleRad));
|
|
|
|
|
|
int newWidth = (int)(img.Width * cos + img.Height * sin);
|
|
|
|
|
|
int newHeight = (int)(img.Width * sin + img.Height * cos);
|
|
|
|
|
|
|
2024-05-28 13:58:25 +00:00
|
|
|
|
Bitmap bmp;
|
|
|
|
|
|
|
|
|
|
|
|
if (redimensionarParaCaber)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Cria um novo bitmap com as dimensões calculadas
|
|
|
|
|
|
bmp = new Bitmap(newWidth, newHeight);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Cria um novo bitmap com as dimensões originais
|
|
|
|
|
|
bmp = new Bitmap(img.Width, img.Height);
|
|
|
|
|
|
}
|
2024-05-17 12:49:47 +00:00
|
|
|
|
|
|
|
|
|
|
using (Graphics gfx = Graphics.FromImage(bmp))
|
|
|
|
|
|
{
|
|
|
|
|
|
gfx.Clear(Color.Transparent); // Define a cor de fundo como transparente
|
2024-05-28 13:58:25 +00:00
|
|
|
|
|
|
|
|
|
|
if (redimensionarParaCaber)
|
|
|
|
|
|
{
|
|
|
|
|
|
gfx.TranslateTransform(newWidth / 2f, newHeight / 2f); // Move o ponto de rotação para o centro do novo bitmap
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
gfx.TranslateTransform(img.Width / 2f, img.Height / 2f); // Move o ponto de rotação para o centro do bitmap original
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-17 12:49:47 +00:00
|
|
|
|
gfx.RotateTransform((float)rotationAngle); // Aplica a rotação
|
2024-05-28 13:58:25 +00:00
|
|
|
|
|
|
|
|
|
|
if (redimensionarParaCaber)
|
|
|
|
|
|
{
|
|
|
|
|
|
gfx.TranslateTransform(-img.Width / 2f, -img.Height / 2f); // Move a imagem de volta para o centro
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
gfx.TranslateTransform(-img.Width / 2f, -img.Height / 2f); // Move a imagem de volta para o centro
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-17 12:49:47 +00:00
|
|
|
|
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
|
|
|
|
|
gfx.DrawImage(img, 0, 0, img.Width, img.Height); // Desenha a imagem original no bitmap
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return bmp;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-12 22:04:05 +00:00
|
|
|
|
public static void DesenharInclinacao(Panel pnlBussola, PaintEventArgs e, float anguloInclinacao, float anguloSub, float? anguloOpcional = null)
|
2024-05-17 12:49:47 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (!(anguloInclinacao >= 0 || anguloInclinacao <= 0))
|
|
|
|
|
|
{
|
|
|
|
|
|
anguloInclinacao = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Graphics g = e.Graphics;
|
|
|
|
|
|
// Desenha o círculo da bússola
|
|
|
|
|
|
int diameter = Math.Min(pnlBussola.Width - 1, pnlBussola.Height - 1);
|
|
|
|
|
|
Point centro = new Point((pnlBussola.Width - 1) / 2, (pnlBussola.Height - 1) / 2);
|
|
|
|
|
|
//g.DrawEllipse(Pens.Black, centro.X - diameter / 2, centro.Y - diameter / 2, diameter, diameter);
|
|
|
|
|
|
|
|
|
|
|
|
// Escreve o ângulo no centro do círculo
|
2024-12-12 22:04:05 +00:00
|
|
|
|
string anguloTexto = (anguloOpcional.HasValue ? "C: " : "") + $"{anguloInclinacao.ToString("0.0")}°";
|
2024-05-17 12:49:47 +00:00
|
|
|
|
Font fonteAngulo = new Font("Arial", 7); // Ajuste o tamanho da fonte conforme necessário
|
|
|
|
|
|
SizeF textSize = e.Graphics.MeasureString(anguloTexto, fonteAngulo);
|
|
|
|
|
|
|
|
|
|
|
|
// Calcula a posição para o texto ser centralizado no círculo
|
|
|
|
|
|
float textX = 0;
|
|
|
|
|
|
float textY = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Define a cor do texto
|
|
|
|
|
|
Brush textBrush = Brushes.Black;
|
|
|
|
|
|
|
|
|
|
|
|
// Desenha o texto no Graphics do painel
|
|
|
|
|
|
g.DrawString(anguloTexto, fonteAngulo, textBrush, textX, textY);
|
|
|
|
|
|
|
|
|
|
|
|
// A linha será desenhada do centro do círculo
|
|
|
|
|
|
PointF pontoCentral = new PointF(centro.X, centro.Y);
|
|
|
|
|
|
// O comprimento da linha será metade do diâmetro do círculo (raio)
|
|
|
|
|
|
float comprimentoLinha = diameter / 3;
|
|
|
|
|
|
|
|
|
|
|
|
// Ajustar o ângulo para que 0 graus esteja para cima e aumente no sentido horário
|
|
|
|
|
|
//float anguloRadianos = (anguloInclinacao - 90) * (float)(Math.PI / 180.0);
|
|
|
|
|
|
float anguloRadianos = (anguloInclinacao - anguloSub) * (float)(Math.PI / 180.0);
|
|
|
|
|
|
|
|
|
|
|
|
// Calcular o ponto final da linha baseado no ângulo de inclinação
|
|
|
|
|
|
PointF pontoFinal = new PointF(
|
|
|
|
|
|
pontoCentral.X + comprimentoLinha * (float)Math.Cos(anguloRadianos),
|
|
|
|
|
|
pontoCentral.Y + comprimentoLinha * (float)Math.Sin(anguloRadianos)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Desenhar a linha de inclinação
|
|
|
|
|
|
g.DrawLine(Pens.Red, pontoCentral, pontoFinal);
|
|
|
|
|
|
|
|
|
|
|
|
// Opcional: Desenhar uma ponta de seta na linha para indicar direção
|
|
|
|
|
|
using (AdjustableArrowCap bigArrow = new AdjustableArrowCap(4, 4))
|
|
|
|
|
|
{
|
|
|
|
|
|
using (Pen pen = new Pen(Color.Red, 2))
|
|
|
|
|
|
{
|
|
|
|
|
|
pen.CustomEndCap = bigArrow;
|
|
|
|
|
|
g.DrawLine(pen, pontoCentral, pontoFinal);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-12 22:04:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Desenhar a segunda seta se o ângulo opcional for fornecido
|
|
|
|
|
|
if (anguloOpcional.HasValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Escreve o ângulo no centro do círculo
|
|
|
|
|
|
string anguloTexto2 = $"R: {anguloOpcional.Value.ToString("0.0")}°";
|
|
|
|
|
|
SizeF textSize2 = e.Graphics.MeasureString(anguloTexto2, fonteAngulo);
|
|
|
|
|
|
|
|
|
|
|
|
// Calcula a posição para o texto ser centralizado no círculo
|
|
|
|
|
|
float textX2 = pnlBussola.Width - textSize2.Width;
|
|
|
|
|
|
float textY2 = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Desenha o texto no Graphics do painel
|
|
|
|
|
|
g.DrawString(anguloTexto2, fonteAngulo, Brushes.Blue, textX2, textY2);
|
|
|
|
|
|
|
|
|
|
|
|
float anguloOpcionalRadianos = (anguloOpcional.Value - anguloSub) * (float)(Math.PI / 180.0);
|
|
|
|
|
|
|
|
|
|
|
|
PointF pontoFinalOpcional = new PointF(
|
|
|
|
|
|
pontoCentral.X + comprimentoLinha * (float)Math.Cos(anguloOpcionalRadianos),
|
|
|
|
|
|
pontoCentral.Y + comprimentoLinha * (float)Math.Sin(anguloOpcionalRadianos)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
using (AdjustableArrowCap bigArrow = new AdjustableArrowCap(4, 4))
|
|
|
|
|
|
{
|
|
|
|
|
|
using (Pen pen = new Pen(Color.Blue, 2))
|
|
|
|
|
|
{
|
|
|
|
|
|
pen.CustomEndCap = bigArrow;
|
|
|
|
|
|
g.DrawLine(pen, pontoCentral, pontoFinalOpcional);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-05-17 12:49:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-08 20:09:17 +00:00
|
|
|
|
public static Bitmap FazerOverlay(Bitmap rgb, Bitmap segmentada, float alpha = 0.35f, bool useNearest = true)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1) Garantir mesma resolução
|
|
|
|
|
|
Bitmap segSameSize = segmentada;
|
|
|
|
|
|
if (segmentada.Width != rgb.Width || segmentada.Height != rgb.Height)
|
|
|
|
|
|
{
|
|
|
|
|
|
segSameSize = new Bitmap(rgb.Width, rgb.Height, PixelFormat.Format24bppRgb);
|
|
|
|
|
|
using (var g = Graphics.FromImage(segSameSize))
|
|
|
|
|
|
{
|
|
|
|
|
|
g.InterpolationMode = useNearest ? InterpolationMode.NearestNeighbor : InterpolationMode.HighQualityBilinear;
|
|
|
|
|
|
g.PixelOffsetMode = PixelOffsetMode.Half;
|
|
|
|
|
|
g.DrawImage(segmentada, new Rectangle(0, 0, rgb.Width, rgb.Height));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2) Compor overlay (rgb + alpha*segmentada)
|
|
|
|
|
|
var output = new Bitmap(rgb.Width, rgb.Height, PixelFormat.Format24bppRgb);
|
|
|
|
|
|
using (var g = Graphics.FromImage(output))
|
|
|
|
|
|
using (var ia = new ImageAttributes())
|
|
|
|
|
|
{
|
|
|
|
|
|
// fundo (RGB)
|
|
|
|
|
|
g.DrawImage(rgb, 0, 0, rgb.Width, rgb.Height);
|
|
|
|
|
|
|
|
|
|
|
|
// matriz de cor com alpha global
|
|
|
|
|
|
var cm = new ColorMatrix
|
|
|
|
|
|
{
|
|
|
|
|
|
Matrix00 = 1f,
|
|
|
|
|
|
Matrix11 = 1f,
|
|
|
|
|
|
Matrix22 = 1f, // R,G,B inalterados
|
|
|
|
|
|
Matrix33 = alpha, // A (transparência da segmentação)
|
|
|
|
|
|
Matrix44 = 1f
|
|
|
|
|
|
};
|
|
|
|
|
|
ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
|
|
|
|
|
|
|
|
|
|
|
|
// overlay = 0.65*rgb + 0.35*seg (se quiser pesar o fundo, desenhe rgb antes, como já fizemos)
|
|
|
|
|
|
g.CompositingMode = CompositingMode.SourceOver;
|
|
|
|
|
|
g.CompositingQuality = CompositingQuality.HighSpeed;
|
|
|
|
|
|
g.InterpolationMode = useNearest ? InterpolationMode.NearestNeighbor : InterpolationMode.HighQualityBilinear;
|
|
|
|
|
|
g.PixelOffsetMode = PixelOffsetMode.Half;
|
|
|
|
|
|
|
|
|
|
|
|
g.DrawImage(
|
|
|
|
|
|
segSameSize,
|
|
|
|
|
|
new Rectangle(0, 0, rgb.Width, rgb.Height),
|
|
|
|
|
|
0, 0, segSameSize.Width, segSameSize.Height,
|
|
|
|
|
|
GraphicsUnit.Pixel,
|
|
|
|
|
|
ia
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!ReferenceEquals(segSameSize, segmentada))
|
|
|
|
|
|
segSameSize.Dispose();
|
|
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-20 10:16:23 +00:00
|
|
|
|
public static string ConverterSegundosParaHHmmss(int totalSeconds)
|
|
|
|
|
|
{
|
|
|
|
|
|
TimeSpan timeSpan = TimeSpan.FromSeconds(totalSeconds);
|
|
|
|
|
|
return timeSpan.ToString(@"hh\:mm\:ss");
|
|
|
|
|
|
}
|
2024-05-03 19:55:06 +00:00
|
|
|
|
|
2025-03-14 19:35:46 +00:00
|
|
|
|
public static async Task<bool> AguardarCondicaoAsync(Func<bool> condicao, int timeoutMs = 5000, int delayMs = 200, Func<bool> forcarLiberacao = null)
|
2024-05-24 20:05:51 +00:00
|
|
|
|
{
|
2025-03-14 19:35:46 +00:00
|
|
|
|
var tempoInicial = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
|
|
while (!condicao())
|
|
|
|
|
|
{
|
|
|
|
|
|
// Se atingiu o timeout, interrompe e retorna false
|
|
|
|
|
|
if ((DateTime.Now - tempoInicial).TotalMilliseconds >= timeoutMs)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
// Se a variável de liberação for passada e retornar true, interrompe o loop
|
|
|
|
|
|
if (forcarLiberacao != null && forcarLiberacao())
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
await Task.Delay(delayMs);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true; // Condição atingida dentro do tempo limite
|
2024-05-24 20:05:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-12 19:44:56 +00:00
|
|
|
|
|
2024-12-05 12:52:28 +00:00
|
|
|
|
public static void DefinirEventosPicBtn(Control ctrl, string mensagemErro, Func<Task> func, bool hover = false)
|
2024-07-12 19:44:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
ctrl.Cursor = Cursors.Hand;
|
2024-12-05 12:52:28 +00:00
|
|
|
|
|
|
|
|
|
|
// Adiciona eventos de hover (evita múltiplos registros)
|
2024-09-24 19:02:54 +00:00
|
|
|
|
if (hover)
|
|
|
|
|
|
{
|
2024-12-05 12:52:28 +00:00
|
|
|
|
ctrl.MouseEnter -= picBtn_MouseEnter;
|
|
|
|
|
|
ctrl.MouseLeave -= picBtn_MouseLeave;
|
2024-09-24 19:02:54 +00:00
|
|
|
|
ctrl.MouseEnter += picBtn_MouseEnter;
|
|
|
|
|
|
ctrl.MouseLeave += picBtn_MouseLeave;
|
|
|
|
|
|
}
|
2024-12-05 12:52:28 +00:00
|
|
|
|
|
|
|
|
|
|
// Remove eventos anteriores e adiciona o novo
|
|
|
|
|
|
ctrl.Click -= async (s, args) => await HandlePicBtnClick(ctrl, mensagemErro, func);
|
|
|
|
|
|
ctrl.Click += async (s, args) => await HandlePicBtnClick(ctrl, mensagemErro, func);
|
2024-07-12 19:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-05 12:52:28 +00:00
|
|
|
|
private static async Task HandlePicBtnClick(Control ctrl, string mensagemErro, Func<Task> func)
|
2024-07-12 19:44:56 +00:00
|
|
|
|
{
|
2024-12-05 12:52:28 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// Executa o efeito de piscada antes da ação
|
|
|
|
|
|
await PiscarBtn(ctrl, 2);
|
2024-07-12 19:44:56 +00:00
|
|
|
|
|
2024-12-05 12:52:28 +00:00
|
|
|
|
// Executa a ação associada ao botão
|
|
|
|
|
|
if (func != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
await func();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Exibe uma mensagem de erro genérica para qualquer exceção
|
2025-03-26 17:42:33 +00:00
|
|
|
|
CustomDialog.ShowDialog(
|
|
|
|
|
|
"Erro",
|
|
|
|
|
|
$"{mensagemErro}\nDetalhes: {ex.Message}",
|
|
|
|
|
|
MessageBoxIcon.Error,
|
|
|
|
|
|
new Dictionary<(string, dynamic), Action>()
|
|
|
|
|
|
{
|
|
|
|
|
|
{ ("OK", null), () => { } },
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
2024-12-05 12:52:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-09 21:11:40 +00:00
|
|
|
|
|
2024-07-12 19:44:56 +00:00
|
|
|
|
public static void picBtn_MouseEnter(object sender, EventArgs e)
|
|
|
|
|
|
{
|
2024-12-17 15:32:53 +00:00
|
|
|
|
Task.Run(async () => await TransicaoTamanho((Control)sender, true, 5));
|
2024-07-12 19:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void picBtn_MouseLeave(object sender, EventArgs e)
|
|
|
|
|
|
{
|
2024-12-17 15:32:53 +00:00
|
|
|
|
Task.Run(async () => await TransicaoTamanho((Control)sender, false, 5));
|
2024-07-12 19:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void AlteraTamanhoObjeto(Control ctrl, bool Cresce)
|
|
|
|
|
|
{
|
|
|
|
|
|
int acX = Convert.ToInt32(ctrl.Width * 0.06);
|
|
|
|
|
|
if (acX % 2 == 1) acX++;
|
|
|
|
|
|
int acY = Convert.ToInt32(ctrl.Height * 0.06);
|
|
|
|
|
|
if (acY % 2 == 1) acY++;
|
|
|
|
|
|
if (!Cresce)
|
|
|
|
|
|
{
|
|
|
|
|
|
acX *= -1;
|
|
|
|
|
|
acY *= -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ctrl.Size = new Size((int)(ctrl.Width + acX), (int)(ctrl.Height + acY));
|
|
|
|
|
|
ctrl.Location = new Point(ctrl.Location.X - (acX / 2), ctrl.Location.Y - (acY / 2));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-24 19:02:54 +00:00
|
|
|
|
private static async Task TransicaoTamanho(Control ctrl, bool Cresce, int delay)
|
2024-07-12 19:44:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
int steps = 10; // Número de passos na transição
|
|
|
|
|
|
int acX = Convert.ToInt32(ctrl.Width * 0.06);
|
|
|
|
|
|
if (acX % 2 == 1) acX++;
|
|
|
|
|
|
int acY = Convert.ToInt32(ctrl.Height * 0.06);
|
|
|
|
|
|
if (acY % 2 == 1) acY++;
|
|
|
|
|
|
|
|
|
|
|
|
// Ajuste para garantir que stepX e stepY sejam pelo menos 2
|
|
|
|
|
|
int stepX = Math.Max(acX / steps, 2);
|
|
|
|
|
|
int stepY = Math.Max(acY / steps, 2);
|
|
|
|
|
|
if (!Cresce)
|
|
|
|
|
|
{
|
|
|
|
|
|
stepX = -stepX;
|
|
|
|
|
|
stepY = -stepY;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < steps; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
ctrl.Invoke((MethodInvoker)(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ctrl.Size = new Size(ctrl.Width + stepX, ctrl.Height + stepY);
|
|
|
|
|
|
ctrl.Location = new Point(ctrl.Location.X - (stepX / 2), ctrl.Location.Y - (stepY / 2));
|
|
|
|
|
|
}));
|
|
|
|
|
|
await Task.Delay(delay);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static async Task PiscarBtn(Control ctrl, int vezes)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < vezes; i++)
|
|
|
|
|
|
{
|
2024-09-24 19:02:54 +00:00
|
|
|
|
await TransicaoTamanho(ctrl, true, 1);
|
|
|
|
|
|
await Task.Delay(3);
|
|
|
|
|
|
await TransicaoTamanho(ctrl, false, 1);
|
|
|
|
|
|
await Task.Delay(3);
|
2024-07-12 19:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static Bitmap GetPanelImage(Panel panel)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Cria um bitmap do tamanho do panel
|
|
|
|
|
|
Bitmap bitmap = new Bitmap(panel.Width, panel.Height);
|
|
|
|
|
|
|
|
|
|
|
|
// Desenha o conteúdo do panel no bitmap
|
|
|
|
|
|
panel.DrawToBitmap(bitmap, new Rectangle(0, 0, panel.Width, panel.Height));
|
|
|
|
|
|
|
|
|
|
|
|
// Retorna o bitmap
|
|
|
|
|
|
return bitmap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-06 18:47:59 +00:00
|
|
|
|
public static async Task<Bitmap> GetBrowserImage(WebView2 browser)
|
2024-07-12 19:44:56 +00:00
|
|
|
|
{
|
2025-05-06 18:47:59 +00:00
|
|
|
|
if (browser?.CoreWebView2 == null)
|
|
|
|
|
|
return null;
|
2024-07-12 19:44:56 +00:00
|
|
|
|
|
2025-05-06 18:47:59 +00:00
|
|
|
|
using (var stream = new MemoryStream())
|
|
|
|
|
|
{
|
|
|
|
|
|
// Captura em PNG
|
|
|
|
|
|
await browser.CoreWebView2.CapturePreviewAsync(CoreWebView2CapturePreviewImageFormat.Png, stream);
|
2024-07-12 19:44:56 +00:00
|
|
|
|
|
2025-05-06 18:47:59 +00:00
|
|
|
|
stream.Position = 0;
|
|
|
|
|
|
return new Bitmap(stream);
|
2024-07-12 19:44:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-13 16:28:41 +00:00
|
|
|
|
|
2024-10-15 19:50:04 +00:00
|
|
|
|
public static void SalvarImagemComprimida(Bitmap frame, string Caminho, long qualidade)
|
2024-08-13 16:28:41 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Se o frame for nulo, cria um novo bitmap representando uma tela preta
|
|
|
|
|
|
if (frame == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
frame = new Bitmap(1, 1);
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(frame))
|
|
|
|
|
|
{
|
|
|
|
|
|
g.Clear(Color.Black);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Obtém o codec para JPEG
|
|
|
|
|
|
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
|
|
|
|
|
|
|
|
|
|
|
|
// Configura os parâmetros de compressão
|
|
|
|
|
|
EncoderParameters encoderParams = new EncoderParameters(1);
|
|
|
|
|
|
encoderParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qualidade);
|
|
|
|
|
|
|
2024-09-13 16:47:55 +00:00
|
|
|
|
using (Graphics g = Graphics.FromImage(frame))
|
|
|
|
|
|
{
|
2024-10-15 19:50:04 +00:00
|
|
|
|
g.DrawString(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"), new Font("Arial", 10), Brushes.Transparent, new PointF(10, 10));
|
2024-09-13 16:47:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-13 16:28:41 +00:00
|
|
|
|
// Salva a imagem com a qualidade ajustada
|
|
|
|
|
|
frame.Save(Caminho, jpegCodec, encoderParams);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-15 19:50:04 +00:00
|
|
|
|
public static void SalvarImagemComprimida(Bitmap frame, string Caminho, long qualidade, int larguraDestino, int alturaDestino)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Se o frame for nulo, cria um novo bitmap representando uma tela preta
|
|
|
|
|
|
if (frame == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
larguraDestino = 1;
|
|
|
|
|
|
alturaDestino = 1;
|
|
|
|
|
|
frame = new Bitmap(larguraDestino, alturaDestino);
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(frame))
|
|
|
|
|
|
{
|
|
|
|
|
|
g.Clear(Color.Black);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Color corBorda = Color.FromArgb(14, 14, 14);
|
|
|
|
|
|
|
|
|
|
|
|
// Remover bordas pretas
|
|
|
|
|
|
Bitmap frameSemBordas = RemoverBordasPretas(frame, corBorda);
|
|
|
|
|
|
|
|
|
|
|
|
// Criar o novo bitmap redimensionado para o tamanho de destino
|
|
|
|
|
|
Bitmap bitmapRedimensionado = new Bitmap(larguraDestino, alturaDestino);
|
|
|
|
|
|
|
|
|
|
|
|
// Redimensionar a imagem original para ocupar toda a área de 512x512
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(bitmapRedimensionado))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Configurações de alta qualidade para minimizar a perda de qualidade durante o redimensionamento
|
|
|
|
|
|
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
|
|
|
|
|
|
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
|
|
|
|
|
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
|
|
|
|
|
|
|
|
|
|
|
// Preencher o bitmap de destino (512x512) com a imagem redimensionada
|
|
|
|
|
|
g.DrawImage(frameSemBordas, 0, 0, larguraDestino, alturaDestino);
|
|
|
|
|
|
|
|
|
|
|
|
// Adiciona a data e hora na imagem (opcional)
|
|
|
|
|
|
g.DrawString(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"), new Font("Arial", 10), Brushes.Transparent, new PointF(10, 10));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Obtém o codec para JPEG
|
|
|
|
|
|
ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
|
|
|
|
|
|
|
|
|
|
|
|
// Configura os parâmetros de compressão
|
|
|
|
|
|
EncoderParameters encoderParams = new EncoderParameters(1);
|
|
|
|
|
|
encoderParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qualidade);
|
|
|
|
|
|
|
|
|
|
|
|
// Salva a imagem redimensionada com a qualidade ajustada
|
|
|
|
|
|
bitmapRedimensionado.Save(Caminho, jpegCodec, encoderParams);
|
|
|
|
|
|
|
|
|
|
|
|
// Libera os recursos usados pelo bitmap redimensionado
|
|
|
|
|
|
bitmapRedimensionado.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-27 17:38:56 +00:00
|
|
|
|
private static Bitmap RemoverBordasPretas(Bitmap _imagem, Color corBorda)
|
2024-10-15 19:50:04 +00:00
|
|
|
|
{
|
2024-11-27 17:38:56 +00:00
|
|
|
|
Bitmap imagem = (Bitmap)_imagem.Clone();
|
|
|
|
|
|
|
2024-10-15 19:50:04 +00:00
|
|
|
|
int xMin = imagem.Width, xMax = 0, yMin = imagem.Height, yMax = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Iterar sobre todos os pixels para encontrar os limites da imagem sem bordas com a cor especificada
|
|
|
|
|
|
for (int y = 0; y < imagem.Height; y++)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int x = 0; x < imagem.Width; x++)
|
|
|
|
|
|
{
|
|
|
|
|
|
Color pixel = imagem.GetPixel(x, y);
|
|
|
|
|
|
// Verificar se o pixel não corresponde à cor da borda
|
|
|
|
|
|
if (pixel.R != corBorda.R || pixel.G != corBorda.G || pixel.B != corBorda.B)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (x < xMin) xMin = x;
|
|
|
|
|
|
if (x > xMax) xMax = x;
|
|
|
|
|
|
if (y < yMin) yMin = y;
|
|
|
|
|
|
if (y > yMax) yMax = y;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Se a imagem for completamente da cor da borda, retornar um bitmap vazio
|
|
|
|
|
|
if (xMax == 0 && yMax == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new Bitmap(1, 1); // Imagem completamente da cor da borda
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Cortar a imagem para remover as bordas
|
|
|
|
|
|
int novaLargura = xMax - xMin + 1;
|
|
|
|
|
|
int novaAltura = yMax - yMin + 1;
|
|
|
|
|
|
|
|
|
|
|
|
Bitmap imagemSemBordas = new Bitmap(novaLargura, novaAltura);
|
|
|
|
|
|
using (Graphics g = Graphics.FromImage(imagemSemBordas))
|
|
|
|
|
|
{
|
|
|
|
|
|
g.DrawImage(imagem, new Rectangle(0, 0, novaLargura, novaAltura), new Rectangle(xMin, yMin, novaLargura, novaAltura), GraphicsUnit.Pixel);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return imagemSemBordas;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-25 12:14:59 +00:00
|
|
|
|
public static async Task ExecutarMetodoComVerificacaoCrossThreadAsync(Control controle, Func<Task> acao)
|
2024-09-13 16:47:55 +00:00
|
|
|
|
{
|
2024-11-25 12:14:59 +00:00
|
|
|
|
if (controle.InvokeRequired)
|
2024-09-13 16:47:55 +00:00
|
|
|
|
{
|
2024-12-12 22:04:05 +00:00
|
|
|
|
var tcs = new TaskCompletionSource<object>();
|
2024-11-29 20:28:25 +00:00
|
|
|
|
|
2024-12-12 22:04:05 +00:00
|
|
|
|
try
|
2024-11-29 20:28:25 +00:00
|
|
|
|
{
|
2024-12-12 22:04:05 +00:00
|
|
|
|
controle.BeginInvoke(new Action(async () =>
|
2024-11-29 20:28:25 +00:00
|
|
|
|
{
|
2024-12-12 22:04:05 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await acao();
|
|
|
|
|
|
if (!tcs.Task.IsCompleted) // Evitar race conditions
|
|
|
|
|
|
tcs.TrySetResult(null);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!tcs.Task.IsCompleted) // Evitar race conditions
|
|
|
|
|
|
tcs.TrySetException(ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!tcs.Task.IsCompleted) // Garante que o estado do TCS é atualizado corretamente
|
|
|
|
|
|
tcs.TrySetException(ex);
|
|
|
|
|
|
}
|
2024-11-29 20:28:25 +00:00
|
|
|
|
|
2024-12-12 22:04:05 +00:00
|
|
|
|
await tcs.Task; // Aguarda a conclusão da ação
|
2024-09-13 16:47:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-12-12 22:04:05 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await acao();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Log de exceção opcional, se necessário
|
|
|
|
|
|
Console.WriteLine($"Erro ao executar ação no thread UI: {ex.Message}");
|
|
|
|
|
|
throw;
|
|
|
|
|
|
}
|
2024-09-13 16:47:55 +00:00
|
|
|
|
}
|
2024-11-25 12:14:59 +00:00
|
|
|
|
}
|
2024-09-13 16:47:55 +00:00
|
|
|
|
|
2024-11-29 20:28:25 +00:00
|
|
|
|
|
2024-11-25 12:14:59 +00:00
|
|
|
|
public static T ExecutarMetodoComVerificacaoCrossThread<T>(Control controle, Func<T> acao)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (controle == null)
|
|
|
|
|
|
throw new ArgumentNullException(nameof(controle));
|
|
|
|
|
|
|
2024-11-29 20:28:25 +00:00
|
|
|
|
try
|
2024-11-25 12:14:59 +00:00
|
|
|
|
{
|
2024-11-29 20:28:25 +00:00
|
|
|
|
if (controle.InvokeRequired)
|
|
|
|
|
|
{
|
|
|
|
|
|
return (T)controle.Invoke(new Func<T>(() => acao()));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return acao();
|
|
|
|
|
|
}
|
2024-11-25 12:14:59 +00:00
|
|
|
|
}
|
2024-11-29 20:28:25 +00:00
|
|
|
|
catch (Exception ex)
|
2024-11-25 12:14:59 +00:00
|
|
|
|
{
|
2024-11-29 20:28:25 +00:00
|
|
|
|
// Log ou tratamento da exceção
|
|
|
|
|
|
Console.WriteLine($"Erro em ExecutarMetodoComVerificacaoCrossThread: {ex.Message}");
|
|
|
|
|
|
throw; // Relançar a exceção para quem chamou lidar com ela, se necessário
|
2024-11-25 12:14:59 +00:00
|
|
|
|
}
|
2024-09-13 16:47:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-25 12:14:59 +00:00
|
|
|
|
|
2024-11-29 20:28:25 +00:00
|
|
|
|
|
2025-08-07 18:23:53 +00:00
|
|
|
|
public static string MontarStringPerformance(float? gpuLoad, float? cpuLoad, float? ramUsage, float? cpuTemp, float? gpuTemp, float? hddLoad)
|
2024-10-03 18:21:30 +00:00
|
|
|
|
{
|
2025-08-07 18:23:53 +00:00
|
|
|
|
return $"HDD: {hddLoad?.ToString("0.0") ?? "N/A"}% " +
|
|
|
|
|
|
$"GPU: {gpuLoad?.ToString("0.0") ?? "N/A"}% " +
|
2024-10-03 18:21:30 +00:00
|
|
|
|
$"CPU: {cpuLoad?.ToString("0.0") ?? "N/A"}% " +
|
|
|
|
|
|
$"RAM: {ramUsage?.ToString("0.0") ?? "N/A"}% " +
|
|
|
|
|
|
$"Temp CPU: {cpuTemp?.ToString("0.0") ?? "N/A"}°C " +
|
|
|
|
|
|
$"Temp GPU: {gpuTemp?.ToString("0.0") ?? "N/A"}°C";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-25 12:14:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task SafeExecuteAsync(Func<Task> action)
|
|
|
|
|
|
{
|
|
|
|
|
|
await semaphore.WaitAsync();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await action();
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
semaphore.Release();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-06 18:47:59 +00:00
|
|
|
|
public static void ValidarImagemCameraBrowser(WebView2 nav, Control parent)
|
2025-04-02 20:37:56 +00:00
|
|
|
|
{
|
2025-05-06 18:47:59 +00:00
|
|
|
|
nav.NavigationCompleted += async (sender, args) =>
|
2025-04-02 20:37:56 +00:00
|
|
|
|
{
|
2025-05-06 18:47:59 +00:00
|
|
|
|
if (args.IsSuccess)
|
2025-04-02 20:37:56 +00:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-05-06 18:47:59 +00:00
|
|
|
|
await Task.Delay(500); // aguarda carregar conteúdo
|
|
|
|
|
|
|
|
|
|
|
|
var browser = (WebView2)sender;
|
|
|
|
|
|
|
|
|
|
|
|
string script = "document.body ? document.body.innerText : ''";
|
|
|
|
|
|
string result = await browser.CoreWebView2.ExecuteScriptAsync(script);
|
2025-04-02 20:37:56 +00:00
|
|
|
|
|
2025-05-06 18:47:59 +00:00
|
|
|
|
// O resultado vem como string JSON (ex: "\"Texto dentro do body\"")
|
2025-11-06 16:18:32 +00:00
|
|
|
|
string conteudo = System.Text.Json.JsonSerializer.Deserialize<string>(result);
|
2025-04-02 20:37:56 +00:00
|
|
|
|
|
2025-05-06 18:47:59 +00:00
|
|
|
|
if (string.IsNullOrWhiteSpace(conteudo) ||
|
|
|
|
|
|
conteudo.Contains("Internal Server Error") ||
|
|
|
|
|
|
conteudo.Contains("Not Found") ||
|
|
|
|
|
|
conteudo.Length < 20)
|
2025-04-02 20:37:56 +00:00
|
|
|
|
{
|
2025-05-06 18:47:59 +00:00
|
|
|
|
Console.WriteLine("Falha no carregamento do stream, recarregando...");
|
2025-04-02 20:37:56 +00:00
|
|
|
|
|
2025-05-06 18:47:59 +00:00
|
|
|
|
await Task.Delay(1000);
|
|
|
|
|
|
|
|
|
|
|
|
if (parent.IsHandleCreated)
|
2025-04-02 20:37:56 +00:00
|
|
|
|
{
|
2025-05-06 18:47:59 +00:00
|
|
|
|
parent.Invoke(new Action(() => browser.Reload()));
|
2025-04-02 20:37:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-05-06 18:47:59 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("Câmera carregada com sucesso.");
|
|
|
|
|
|
}
|
2025-04-02 20:37:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2025-05-06 18:47:59 +00:00
|
|
|
|
Console.WriteLine($"Erro ao verificar a página: {ex.Message}");
|
2025-04-02 20:37:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2025-05-06 18:47:59 +00:00
|
|
|
|
|
2025-04-09 21:11:40 +00:00
|
|
|
|
public static string ConverterComandoBytesParaTexto(byte[] data)
|
|
|
|
|
|
{
|
2025-05-06 18:47:59 +00:00
|
|
|
|
string comando = string.Join(" ", data.Select(x => x.ToString("X")));
|
2025-04-09 21:11:40 +00:00
|
|
|
|
|
|
|
|
|
|
return comando;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-01 18:27:01 +00:00
|
|
|
|
public static byte StringToHexByte(string _st)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Convert.ToByte(_st.Replace("0x", ""), 16);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static string HexByteToString(byte _bt)
|
|
|
|
|
|
{
|
|
|
|
|
|
return "0x" + _bt.ToString("X").PadLeft(2, '0');
|
|
|
|
|
|
}
|
2024-11-25 12:14:59 +00:00
|
|
|
|
|
2025-05-21 20:07:43 +00:00
|
|
|
|
public static Bitmap Base64ToBitmap(string base64String)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(base64String))
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
|
|
byte[] imageBytes = Convert.FromBase64String(base64String);
|
|
|
|
|
|
using (var ms = new MemoryStream(imageBytes))
|
|
|
|
|
|
{
|
|
|
|
|
|
return new Bitmap(ms);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-11 16:38:08 +00:00
|
|
|
|
public static DateTime UnixToDateTime(string unixTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
double time = Convert.ToDouble(unixTime.Replace(".", ","));
|
|
|
|
|
|
var dateTime = DateTimeOffset.FromUnixTimeMilliseconds((long)(time * 1000)).ToLocalTime();
|
|
|
|
|
|
DateTime momento = dateTime.DateTime;
|
|
|
|
|
|
return momento;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-03 10:52:37 +00:00
|
|
|
|
}
|
2024-05-20 10:16:23 +00:00
|
|
|
|
|
|
|
|
|
|
public static class FuncoesMatematicas
|
2024-01-18 19:22:28 +00:00
|
|
|
|
{
|
2024-05-20 10:16:23 +00:00
|
|
|
|
public static double Map(double valor, double deMin, double deMax, double paraMin, double paraMax)
|
|
|
|
|
|
{
|
|
|
|
|
|
return paraMin + (valor - deMin) * (paraMax - paraMin) / (deMax - deMin);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static double ConvertDegreesToRadians(double degrees)
|
|
|
|
|
|
{
|
|
|
|
|
|
double radians = (Math.PI / 180) * degrees;
|
|
|
|
|
|
return (radians);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static double CalcularAlturaTriangulo(double a, double b, double c)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Verifica se os lados formam um triângulo válido
|
|
|
|
|
|
if (a + b <= c || a + c <= b || b + c <= a)
|
|
|
|
|
|
{
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Calcule a área do triângulo usando a fórmula de Herão
|
|
|
|
|
|
double s = (a + b + c) / 2.0; // Semiperímetro
|
|
|
|
|
|
double area = Math.Sqrt(s * (s - a) * (s - b) * (s - c));
|
|
|
|
|
|
|
|
|
|
|
|
// Calcule a altura em relação à base c
|
|
|
|
|
|
double altura = (2 * area) / c;
|
|
|
|
|
|
|
|
|
|
|
|
return altura;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static double CalcularMediana(List<double> numbers)
|
|
|
|
|
|
{
|
|
|
|
|
|
numbers.Sort();
|
|
|
|
|
|
int count = numbers.Count;
|
|
|
|
|
|
if (count % 2 == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Se count é par, a mediana é a média dos dois elementos do meio
|
|
|
|
|
|
double middle1 = numbers[count / 2 - 1];
|
|
|
|
|
|
double middle2 = numbers[count / 2];
|
|
|
|
|
|
return (middle1 + middle2) / 2.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Se count é ímpar, a mediana é o elemento do meio
|
|
|
|
|
|
return numbers[count / 2];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static double CalcularModa(List<double> numbers)
|
|
|
|
|
|
{
|
|
|
|
|
|
return numbers.GroupBy(n => n)
|
|
|
|
|
|
.OrderByDescending(g => g.Count())
|
|
|
|
|
|
.First()
|
|
|
|
|
|
.Key;
|
|
|
|
|
|
}
|
2024-01-18 19:22:28 +00:00
|
|
|
|
|
2025-02-07 19:27:13 +00:00
|
|
|
|
public static double CalcularTemperatura(double leitura, double offset = 0.0)
|
2024-06-14 09:37:22 +00:00
|
|
|
|
{
|
2025-02-07 19:27:13 +00:00
|
|
|
|
double Vs = 5.0; // Tensão de referência do divisor de tensão
|
|
|
|
|
|
double Beta = 3950.0; // Coeficiente Beta do NTC
|
|
|
|
|
|
double To = 298.15; // Temperatura em Kelvin correspondente a 25°C
|
|
|
|
|
|
double Ro = 10000.0; // Resistência do NTC a 25°C
|
|
|
|
|
|
double adcMax = 4095.0; // Valor máximo do ADC (12 bits ESP32)
|
2024-06-14 09:37:22 +00:00
|
|
|
|
|
2025-03-24 18:36:15 +00:00
|
|
|
|
if (leitura < 200) return -1;
|
|
|
|
|
|
|
2025-02-07 19:27:13 +00:00
|
|
|
|
// Calcula a tensão de saída do divisor
|
|
|
|
|
|
double Vout = leitura * Vs / adcMax;
|
2024-06-14 09:37:22 +00:00
|
|
|
|
|
2025-02-07 19:27:13 +00:00
|
|
|
|
// ⚠️ Evita erros matemáticos para valores extremos ⚠️
|
|
|
|
|
|
if (Vout <= 0.001) return -100.0; // Se tensão for quase zero, temperatura muito baixa
|
|
|
|
|
|
if (Vout >= Vs) return double.NaN; // Se Vout for igual a Vs, leitura inválida
|
2024-06-14 09:37:22 +00:00
|
|
|
|
|
2025-02-07 19:27:13 +00:00
|
|
|
|
// Calcula a resistência do NTC
|
2025-03-24 18:36:15 +00:00
|
|
|
|
double Rt = Ro * (Vout / (Vs - Vout)); // NTC EM CIMA
|
|
|
|
|
|
//double Rt = Ro * ((Vs - Vout) / Vout); // NTC EM BAIXO
|
2025-02-07 19:27:13 +00:00
|
|
|
|
|
|
|
|
|
|
// Aplica a equação de Steinhart-Hart para calcular a temperatura em Kelvin
|
|
|
|
|
|
double T = 1.0 / (1.0 / To + Math.Log(Rt / Ro) / Beta);
|
|
|
|
|
|
|
|
|
|
|
|
// Converte Kelvin para Celsius e aplica offset
|
|
|
|
|
|
double Tc = T - 273.15 + offset;
|
|
|
|
|
|
|
|
|
|
|
|
return Math.Round(Tc, 2);
|
2024-06-04 16:57:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-10 19:58:57 +00:00
|
|
|
|
public static bool ValorEstaEntre(double valorAtual, double valorComparar, double margem)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (valorAtual <= (valorComparar + margem) && valorAtual >= (valorComparar - margem))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-29 19:53:27 +00:00
|
|
|
|
// Função personalizada Clamp
|
|
|
|
|
|
public static double Clamp(double valor, double minimo, double maximo)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (valor < minimo) return minimo;
|
|
|
|
|
|
if (valor > maximo) return maximo;
|
|
|
|
|
|
return valor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-27 17:38:56 +00:00
|
|
|
|
public static double CalculaVelocidadeRPM(double RPM)
|
|
|
|
|
|
{
|
2025-02-06 19:01:41 +00:00
|
|
|
|
double VelocidadeInstantanea = Math.Round((Math.PI * VariaveisEquipamento.DiametroRoda * 60.0 * RPM) / 1000.0, 2); // Km/h
|
2024-11-27 17:38:56 +00:00
|
|
|
|
return VelocidadeInstantanea;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static double CalculaRPMVelocidade(double Velocidade)
|
|
|
|
|
|
{
|
|
|
|
|
|
double RPM = Math.Round((Velocidade * 1000) / (Math.PI * VariaveisEquipamento.DiametroRoda * 60), 2);
|
|
|
|
|
|
return RPM;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static double ConverteKmhParaMs(double Velocidade)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Velocidade / 3.6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-03 19:54:39 +00:00
|
|
|
|
public static double ConverteMsParaKmh(double Velocidade)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Velocidade * 3.6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static double CalculaVelocidadeMsPercentual(double percentual)
|
|
|
|
|
|
{
|
|
|
|
|
|
double Velocidade = ConverteKmhParaMs(CalculaVelocidadeRPM((percentual / 100.0) * VariaveisEquipamento.RPM_Max_Roda));
|
|
|
|
|
|
return Velocidade;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-22 18:36:19 +00:00
|
|
|
|
public static double CalculaVelocidadePercentualMs(double vel_ms)
|
|
|
|
|
|
{
|
|
|
|
|
|
double Velocidade = vel_ms / CalculaVelocidadeMsPercentual(100);
|
|
|
|
|
|
return Velocidade;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-06 19:01:41 +00:00
|
|
|
|
public static double GrausParaRadianos(double angle)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Math.PI / 180.0 * angle;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-03 19:54:39 +00:00
|
|
|
|
|
2024-04-15 10:57:37 +00:00
|
|
|
|
}
|
2024-03-07 19:08:37 +00:00
|
|
|
|
|
2024-01-03 10:52:37 +00:00
|
|
|
|
}
|