agrobot_base/AgroBase/AgroBase/Models/Variaveis.cs

1893 lines
73 KiB
C#
Raw Normal View History

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;
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
{
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/";
public static string CaminhoParametros { get; } = "Parametros/";
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>();
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;
}
}
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";
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-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)
{
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)
{
controle.Controle.PercentualVelocidadeSP = (msg.P1 / 100.0);
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
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;
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;
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;
public static int QuantidadeBicosPulverizadores { get; set; } = 7;
public static double AlturaBarraPulverizadoraCm { get; set; } = 80;
public static double ComprimentoBarraPulverizadoraCm { get; set; } = 103.7;
public static double DistanciaEntreBicosCm { get; set; } = 50;
public static double DensidadeHerbicida { get; set; } = 1.0;
public static double CapacidadeReservatorio { get; set; } = 60;
public static double PressaoMinimaCavitacao { get; set; } = 30.0;
2024-07-26 19:54:25 +00:00
public static double ReducaoDirecional { get; set; } = 20.0;
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 ?
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 ?
Variaveis.OperacaoEmAndamento.DispMvd.Dados.ReducaoMotorBLDC :
2024-10-03 18:21:30 +00:00
1;
2024-07-26 19:54:25 +00:00
return RelacaoRPM;
}
}
public static double DiametroRoda
{
get
{
double diametro =
Variaveis.OperacaoEmAndamento.DispMvd != null ?
Variaveis.OperacaoEmAndamento.DispMvd.Dados.DiametroRoda :
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;
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 },
{ TipoMovimentoDirecional.Diagnostico, 25 },
2024-07-26 19:54:25 +00:00
};
public static double AnguloInclinacaoRollMax { get; set; } = 30.0; // Frontal
public static double AnguloInclinacaoPitchMax { get; set; } = 45.0; // Lateral
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)
{
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,
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)
{
case StatusOperacao.Parametrizando:
{
2025-12-18 18:11:30 +00:00
comportamento.QtdPiscadas = 2;
comportamento.TempoMs = 800;
comportamento.PausaMs = 2000;
break;
}
2024-04-15 10:57:37 +00:00
case StatusOperacao.NaoIniciado:
case StatusOperacao.Concluido:
{
2025-12-18 18:11:30 +00:00
comportamento.QtdPiscadas = 1;
comportamento.TempoMs = 1000;
comportamento.PausaMs = 2000;
break;
}
case StatusOperacao.Aguardando:
{
2025-12-18 18:11:30 +00:00
comportamento.QtdPiscadas = 3;
comportamento.TempoMs = 500;
comportamento.PausaMs = 800;
break;
}
case StatusOperacao.Parado:
{
2025-12-18 18:11:30 +00:00
comportamento.QtdPiscadas = 5;
comportamento.TempoMs = 500;
comportamento.PausaMs = 2000;
break;
}
2024-04-15 10:57:37 +00:00
case StatusOperacao.EmAndamento:
{
2025-12-18 18:11:30 +00:00
comportamento.QtdPiscadas = 1;
comportamento.TempoMs = 500;
comportamento.PausaMs = 1000;
break;
}
case StatusOperacao.Calibrando:
{
2025-12-18 18:11:30 +00:00
comportamento.QtdPiscadas = 2;
comportamento.TempoMs = 500;
comportamento.PausaMs = 800;
break;
}
}
return comportamento;
}
2024-01-03 10:52:37 +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-03-14 19:35:46 +00:00
// Fator de correção da curva
double k = 1.35 + 0.5 * Math.Exp(-Math.Abs(anguloControle) / 15.0);
2025-03-14 19:35:46 +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;
// 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ô
double novoAngulo = GPSUtils.NormalizarAngulo(anguloAtual + (deltaTheta * (180.0 / Math.PI)));
// 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);
// Raio da Terra em metros
double raioTerra = GPSUtils.RaioDaTerra;
// 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
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-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; }
public string comunicacao_interface { get; set; }
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; }
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
comunicacao_interface = par.comunicacao_interface;
rover_ip = par.rover_ip;
rover_gateway = par.rover_gateway;
rover_submask = par.rover_submask;
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;
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;
}
}
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-07-12 19:44:56 +00:00
public static class VariaveisOperacao
{
public static double TempoManobra { get; set; } = 10;
public static double PercentualComErvas
{
get
{
if (!Variaveis.IsAgroMonitor && Variaveis.OperacaoEmAndamento.DispAtu != null)
{
return (Variaveis.OperacaoEmAndamento.Sensoriamento.Atuador?.PercentualErvasTerreno ?? 0) / 100.0;
}
return 0.3;
}
}
public static double PercentualSemErvas
{
get
{
if (!Variaveis.IsAgroMonitor && Variaveis.OperacaoEmAndamento.DispAtu != null)
{
return (100.0 - (Variaveis.OperacaoEmAndamento.Sensoriamento.Atuador?.PercentualErvasTerreno ?? 0)) / 100.0;
}
return 0.7;
}
}
2025-05-09 20:17:28 +00:00
public static GPSModel PosicaoBase { get; set; } = new GPSModel();
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-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-05-08 12:29:29 +00:00
NomeArquivo = Disp.CriadoEm.ToString("dd_MM_yyyy_HH_mm_ss") + Sulfixo;
}
2025-05-08 12:29:29 +00:00
if (Logs.Count() >= MinLogs)
{
if (!Directory.Exists(Caminho))
{
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);
}
}
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
}
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-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;
}
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
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);
}
}
// 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;
}
public static string ConverterSegundosParaHHmmss(int totalSeconds)
{
TimeSpan timeSpan = TimeSpan.FromSeconds(totalSeconds);
return timeSpan.ToString(@"hh\:mm\:ss");
}
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
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);
using (Graphics g = Graphics.FromImage(frame))
{
g.DrawString(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"), new Font("Arial", 10), Brushes.Transparent, new PointF(10, 10));
}
2024-08-13 16:28:41 +00:00
// Salva a imagem com a qualidade ajustada
frame.Save(Caminho, jpegCodec, encoderParams);
}
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();
}
private static Bitmap RemoverBordasPretas(Bitmap _imagem, Color corBorda)
{
Bitmap imagem = (Bitmap)_imagem.Clone();
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-11-25 12:14:59 +00:00
if (controle.InvokeRequired)
{
var tcs = new TaskCompletionSource<object>();
try
{
controle.BeginInvoke(new Action(async () =>
{
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);
}
await tcs.Task; // Aguarda a conclusão da ação
}
else
{
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-11-25 12:14:59 +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));
try
2024-11-25 12:14:59 +00:00
{
if (controle.InvokeRequired)
{
return (T)controle.Invoke(new Func<T>(() => acao()));
}
else
{
return acao();
}
2024-11-25 12:14:59 +00:00
}
catch (Exception ex)
2024-11-25 12:14:59 +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-11-25 12:14:59 +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
}
public static class FuncoesMatematicas
2024-01-18 19:22:28 +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)
{
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)
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;
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
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
}
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;
}
public static double CalculaVelocidadeRPM(double RPM)
{
double VelocidadeInstantanea = Math.Round((Math.PI * VariaveisEquipamento.DiametroRoda * 60.0 * RPM) / 1000.0, 2); // Km/h
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;
}
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;
}
public static double GrausParaRadianos(double angle)
{
return Math.PI / 180.0 * angle;
}
2024-04-15 10:57:37 +00:00
}
2024-01-03 10:52:37 +00:00
}