ajustes para modo producao

This commit is contained in:
Diego Freitas 2026-07-28 15:27:26 -03:00
parent 0f4e83cd34
commit 9ff0cd9cd5
32 changed files with 506 additions and 3550 deletions

View File

@ -180,32 +180,42 @@ namespace AgroBase.Forms.IHM
private async Task DesligarEquipamentoAsync()
{
if (Variaveis.UsarIHM)
if (!Variaveis.UsarIHM)
{
var resultado = CustomDialog.ShowDialog(
"Desligar",
"Deseja desligar o equipamento?",
MessageBoxIcon.Question,
new Dictionary<(string, DialogResult?), Action>()
{
{ ("Sim", DialogResult.Yes), delegate { } },
{ ("Não", DialogResult.No), delegate { } }
});
Close();
return;
}
if (resultado == DialogResult.Yes)
var resultado = CustomDialog.ShowDialog(
"Desligar",
"Deseja desligar o equipamento?",
MessageBoxIcon.Question,
new Dictionary<(string, DialogResult?), Action>
{
frmInstancial.frmIHM.Close();
if (Variaveis.Producao)
Process.Start("shutdown", "/s /t 0");
{ ("Sim", DialogResult.Yes), delegate { } },
{ ("Não", DialogResult.No), delegate { } }
}
);
if (resultado != DialogResult.Yes)
return;
await frmInstancial.EncerrarProcessos(sairProcesso: false);
if (Variaveis.Producao)
{
Process.Start(new ProcessStartInfo
{
FileName = "shutdown.exe",
Arguments = "/s /t 0",
UseShellExecute = false,
CreateNoWindow = true
});
}
else
{
Close();
Application.Exit();
}
await Task.CompletedTask;
}
private async Task ProcurarAtualizacoesAsync()
@ -237,8 +247,7 @@ namespace AgroBase.Forms.IHM
{
await VersionamentoService.AtualizarArquivoVersionamento(true);
var arquivosNaoEncontrados =
VersionamentoService.ArquivosNaoEncontrados.ToList();
var arquivosNaoEncontrados = VersionamentoService.ArquivosNaoEncontrados.ToList();
bool sucesso = !arquivosNaoEncontrados.Any();

View File

@ -136,10 +136,10 @@ namespace AgroBase.Forms
*/
//Variaveis.IniciarUDP();
APIService.IniciarRotinas();
await APIService.IniciarRotinasAsync();
VersionamentoService.IniciarRotinas();
LivoxManagerProcess.Start();
//LivoxManagerProcess.Start();
await VariaveisOperacao.Operadores.IniciarProcessamento(Variaveis.IniciarWorkers).ConfigureAwait(false);

View File

@ -267,18 +267,9 @@
ParametrosModulo_ATU = 2,
ParametrosModulo_MVD = 3,
ParametrosModulo_SEN = 4,
ParametrosModulo_KNT = 5,
PinoutModulo_ATU = 6,
PinoutModulo_SEN = 7,
Script_StreetDetector = 8,
Script_WeedDetector = 9,
Script_GreenDetector = 10,
Script_MapLoad = 11,
Script_MapFollow = 12,
Script_GpsViewer = 13,
ArquivoModelo3D = 14,
ScriptListOAKCaneras = 15,
ScriptMPCController = 16,
}
public enum TipoConexao

View File

@ -546,24 +546,43 @@ namespace AgroBase
}
}
public static void PararCarroControle()
public static void PararCarroControle(string motivo)
{
var _Controle = Variaveis.OperacaoEmAndamento?.Controle;
var pControle = Variaveis.OperacaoEmAndamento?.Parametros?.Controle;
if (_Controle?.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Mov)?.DirecaoAtual != Direcao.Parado)
var op = Variaveis.OperacaoEmAndamento;
var controle = op?.Controle;
var pControle = op?.Parametros?.Controle;
if (controle == null)
return;
controle.MotivosManual.Clear();
controle.MotivosManual.Add(motivo);
var movimento = controle.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Mov);
if (movimento?.DirecaoAtual != Direcao.Parado)
{
Variaveis.MostrarLog("[GeneralJoystick] Deve enviar o comando de parada MOV em PararCarroControle");
Variaveis.MostrarLog($"[GeneralJoystick] Parada MOV solicitada. Motivo: {motivo}");
ProcessarDadosControle(BotoesJoystick.Xis, true, ForcarComando: true);
if (pControle?.FrenagemAutomaticaAoParar ?? false)
controle.MotivosManual.Add("Comando de parada MOV enviado");
if (pControle?.FrenagemAutomaticaAoParar == true)
{
Variaveis.MostrarLog("[GeneralJoystick] Deve enviar o comando de freio em PararCarroControle");
ProcessarDadosControle(BotoesJoystick.Bolinha, false, ForcarComando: true);
controle.MotivosManual.Add("Comando de freio enviado");
}
}
if (_Controle?.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Dir)?.UltimaDirecao != Direcao.Parado)
var direcional = controle.TiposControle?.FirstOrDefault(x => x?.Tipo == T_Code.Dir);
if (direcional?.UltimaDirecao != Direcao.Parado)
{
Variaveis.MostrarLog("[GeneralJoystick] Deve enviar o comando de parada DIR em PararCarroControle");
ProcessarDadosControle(BotoesJoystick.SEsquerda, true, ForcarComando: true);
controle.MotivosManual.Add("Comando de parada DIR enviado");
}
}
@ -571,6 +590,9 @@ namespace AgroBase
{
var op = Variaveis.OperacaoEmAndamento;
if (op?.Controle == null)
return;
bool bloqueioAutomatico =
(op.Parametros.Controle.MovimentoAutomatico && Comando.Dispositivo == T_Code.Mov) ||
(op.Parametros.Controle.DirecionalAutomatico && Comando.Dispositivo == T_Code.Dir);
@ -585,15 +607,41 @@ namespace AgroBase
bool comandoParada =
Comando.ValorAplicar == 0;
bool bloqueioSonar = !VerificaComandoValidoSonar(Comando.key);
bool deveInterromper =
bloqueioAutomatico ||
(bloqueioCalibragem && !comandoParada) ||
!VerificaComandoValidoSonar(Comando.key) ||
bloqueioSonar ||
(!ForcarComando && bloqueioOperacional);
if (deveInterromper) return;
var _Controle = op.Controle;
if (deveInterromper)
{
_Controle.MotivosManual.Clear();
if (bloqueioAutomatico)
_Controle.MotivosManual.Add("Controle em modo automático");
if (bloqueioCalibragem && !comandoParada)
_Controle.MotivosManual.Add("Calibragem em andamento");
if (!ForcarComando && bloqueioOperacional)
{
_Controle.MotivosManual.Add(
$"Operação iniciada: {op.Sensoriamento.Operacao.OperacaoIniciada}, " +
$"Operação liberada: {op.Sensoriamento.Operacao.OperacaoLiberada}, " +
$"Erro operação liberada: {op.Sensoriamento.Operacao.ErroOperacaoLiberada}"
);
}
if (bloqueioSonar)
_Controle.MotivosManual.Add("Visual Worker impediu o comando");
return;
}
if (Comando.direcao.HasValue && _Controle.TiposControle.Any(x => x.Tipo == Comando.Dispositivo))
_Controle.TiposControle.FirstOrDefault(x => x.Tipo == Comando.Dispositivo).DirecaoAtual = Comando.direcao.Value;

View File

@ -53,7 +53,7 @@ public class MPCController
lock (processLock)
{
pythonProcess = PythonService.RunScript(PythonService.ScriptMPCController, new string[] {});
//pythonProcess = PythonService.RunScript(PythonService.ScriptMPCController, new string[] {});
}
bool ScriptIniciado() => Iniciado;

View File

@ -1681,7 +1681,7 @@ namespace AgroBase.Models
return;
}
GeneralJoystick.PararCarroControle();
GeneralJoystick.PararCarroControle("Calibragem iniciada");
entrouEmCalibragem = true;
@ -2030,13 +2030,13 @@ namespace AgroBase.Models
if (Sonar?.Resumo?.EnviarComandoParada ?? false)
{
Variaveis.MostrarLog($"[OperacaoModel] Deve enviar o comando de parada em tmrLeituras_Tick por trava do Visual Worker: {Sonar?.Analises?.matriz_confianca?.block?.reason}");
GeneralJoystick.PararCarroControle();
GeneralJoystick.PararCarroControle($"Deve enviar o comando de parada em tmrLeituras_Tick por trava do Visual Worker: {Sonar?.Analises?.matriz_confianca?.block?.reason}");
}
if (!op.Sensoriamento.Operacao.OperacaoLiberada)
{
Variaveis.MostrarLog($"[OperacaoModel] Deve enviar o comando de parada em tmrLeituras_Tick por Operacao Bloqueada: {op?.Sensoriamento?.Operacao?.ErroOperacaoLiberada}");
GeneralJoystick.PararCarroControle();
GeneralJoystick.PararCarroControle($"Deve enviar o comando de parada em tmrLeituras_Tick por Operacao Bloqueada: {op?.Sensoriamento?.Operacao?.ErroOperacaoLiberada}");
}
}
}
@ -3667,6 +3667,8 @@ namespace AgroBase.Models
public double ErroLateral { get; set; } = 0.0;
public Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel> DebugCustoMpc { get; set; } = new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>();
public List<string> Motivos { get; set; } = new List<string>();
public List<string> JoysticksConectados { get; set; } = new List<string>();
public List<string> MotivosManual { get; set; } = new List<string>();
public string UltimaSessaoComando { get; set; }
public long? UltimaSeqMovimentoAplicada { get; set; }
@ -3677,6 +3679,8 @@ namespace AgroBase.Models
PercentualVelocidadeSP = 0;
Angulo = 0;
EmFreio = false;
Motivos = new List<string>();
MotivosManual = new List<string>();
TiposControle.ForEach(x =>
{
x.DirecaoAtual = Direcao.Parado;
@ -3714,6 +3718,8 @@ namespace AgroBase.Models
SimulacaoMPC = new List<MPCSimulacaoModel>(SimulacaoMPC ?? new List<MPCSimulacaoModel>()),
DebugCustoMpc = new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>(DebugCustoMpc ?? new Dictionary<string, ManagerWorkerMessageResponseDebugCustoModel>()),
Motivos = new List<string>(Motivos ?? new List<string>()),
JoysticksConectados = new List<string>(JoysticksConectados ?? new List<string>()),
MotivosManual = new List<string>(MotivosManual ?? new List<string>()),
UltimaSessaoComando = UltimaSessaoComando,
UltimaSeqMovimentoAplicada = UltimaSeqMovimentoAplicada,
@ -3809,6 +3815,7 @@ namespace AgroBase.Models
if (op == null) return;
op.Controle.Motivos = RedisService.GetField<List<string>>(CtxKey.DadosControle, "motivos", new List<string>());
op.Controle.JoysticksConectados = new List<string>(GeneralJoystick.JoysticksConectados ?? new List<string>());
double tempoDecorridoSeg = 0;
if (Operacao.DataInicio != DateTime.MinValue)

View File

@ -30,7 +30,7 @@ namespace AgroBase.Models
{
public static readonly bool IniciarWorkers = true;
public static readonly bool UsarIHM = true;
public static readonly bool Producao = false;
public static readonly bool Producao = true;
public static bool DebugMode { get; set; } = false;
public static bool Fechando { get; set; } = false;
@ -53,12 +53,22 @@ namespace AgroBase.Models
return version.ToString();
}
}
public static string CaminhoLogsDispositivos { get; } = "Logs/";
public static string CaminhoOperacoes { get; } = "Operacoes/";
public static string CaminhoOperacoesSalvas { get; } = "OperacoesSalvas\\";
public static string CaminhoMapasConvertidos { get; } = "Mapas/";
public static string CaminhoParametros { get; } = "Parametros/";
public static string CaminhoModelos { get; } = "C:\\AgroBaseModels\\";
public static string CaminhoLogsDispositivos { get; } = ResolverCaminhoLocal("Logs");
public static string CaminhoOperacoes { get; } = ResolverCaminhoLocal("Operacoes");
public static string CaminhoOperacoesSalvas { get; } = ResolverCaminhoLocal("OperacoesSalvas");
public static string CaminhoMapasConvertidos { get; } = ResolverCaminhoLocal("Mapas");
public static string CaminhoParametros { get; } = ResolverCaminhoLocal("Parametros");
public static string CaminhoModelos { get; } = ResolverCaminhoLocal(@"C:\AgroBaseModels");
private static string ResolverCaminhoLocal(string caminho)
{
if (string.IsNullOrWhiteSpace(caminho))
throw new ArgumentException("Caminho local não informado.");
if (Path.IsPathRooted(caminho))
return Path.GetFullPath(caminho);
return Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, caminho));
}
public static List<IDispositivosService> DispositivosConectados { get; set; } = new List<IDispositivosService>();
public static MqttService MqttServiceLocal { get; private set; }
public static MqttService MqttServiceBaseCritical { get; private set; }
@ -160,8 +170,7 @@ namespace AgroBase.Models
/// Uma segunda chamada encerra por completo a geração anterior antes
/// de criar novos clientes.
/// </summary>
public static async Task IniciarMqttAsync(
CancellationToken cancellationToken = default(CancellationToken))
public static async Task IniciarMqttAsync(CancellationToken cancellationToken = default(CancellationToken))
{
await _mqttLifecycleLock.WaitAsync(cancellationToken).ConfigureAwait(false);
@ -685,7 +694,7 @@ namespace AgroBase.Models
if (ms > FAILSAFE_MS)
{
// Para por segurança
GeneralJoystick.PararCarroControle();
GeneralJoystick.PararCarroControle("Muito tempo sem receber um novo comando em tmrFailSafe_Tick");
// desarma até chegar novo comando válido
Interlocked.Exchange(ref _lastValidCmdTicksUtc, 0);

View File

@ -34,7 +34,7 @@ namespace AgroBase.Services
{
using (var client = new WebClient())
{
using (client.OpenRead("http://google.com"))
using (client.OpenRead("https://zendioninc.com.br/"))
{
return true;
}
@ -49,11 +49,18 @@ namespace AgroBase.Services
public static bool HasInternet { get; set; }
public static bool HasInternetNow() => _hasInternet;
public static void IniciarRotinas()
public static async Task IniciarRotinasAsync()
{
Task.Run(async () => await tmrMonitoramento_Tick());
tmrMonitoramento?.Dispose();
tmrMonitoramento = new AsyncTaskTimerModel("tmrMonitoramento", tmrMonitoramento_Tick, 10000);
await tmrMonitoramento_Tick();
tmrMonitoramento = new AsyncTaskTimerModel(
"tmrMonitoramento",
tmrMonitoramento_Tick,
10000
);
tmrMonitoramento.Start();
}

View File

@ -186,6 +186,7 @@ namespace AgroBase.Services.Operadores
bateria_ok = _t.AutonomiaCorredor?.BateriaSuficiente ?? false,
motivo_hrb = _t.AutonomiaCorredor?.Motivos?.FirstOrDefault(x => x.Contains("Herbicida")) ?? "",
herbicida_ok = _t.AutonomiaCorredor?.HerbicidaSuficiente ?? false,
autonomia_iniciada = _t.AutonomiaCorredor?.Iniciado ?? false,
autonomia_liberada = _t.AutonomiaCorredor?.Liberado ?? false,
autonomia_status = (int)(_t.AutonomiaCorredor?.Status ?? AutonomiaCorredorStatus.Desconhecido),
autonomia_motivo = _t.AutonomiaCorredor?.Motivo ?? "",

View File

@ -63,38 +63,6 @@ namespace AgroBase.Services
}
}
public static string ScriptMapConverter
{
get
{
return VersionamentoService.ArquivoScript(Enums.TipoArquivoVersionado.Script_MapLoad).NomeArquivoLocal;
}
}
public static string ScriptMapFollower
{
get
{
return VersionamentoService.ArquivoScript(Enums.TipoArquivoVersionado.Script_MapFollow).NomeArquivoLocal;
}
}
public static string ScriptMapGPS
{
get
{
return VersionamentoService.ArquivoScript(Enums.TipoArquivoVersionado.Script_GpsViewer).NomeArquivoLocal;
}
}
public static string ScriptMPCController
{
get
{
return VersionamentoService.ArquivoScript(Enums.TipoArquivoVersionado.ScriptMPCController).NomeArquivoLocal;
}
}
public static Process RunScript(string script, string[] argumentos)
{
return RunScript(script, argumentos, "python");

View File

@ -168,17 +168,17 @@ namespace AgroBase.Services
public static void DefinirEquipamentoDesconectado()
{
if (!(Variaveis.OperacaoEmAndamento?.Sensoriamento?.OperadorSaude?.ModulosSaude?.Any() ?? false)) return;
var saude = Variaveis.OperacaoEmAndamento?.Sensoriamento?.OperadorSaude?.ModulosSaude;
AtualizarCampos(
CtxKey.DadosOperacao,
("motivo_nao_liberado", "Sem comunicação com o núcleo de processamento central"),
("liberado", false),
("configurado", false)
("configurado", false),
("status", StatusOperacao.NaoIniciado)
);
Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.OperacaoLiberada = false;
if (!(Variaveis.OperacaoEmAndamento?.Sensoriamento?.OperadorSaude?.ModulosSaude?.Any() ?? false)) return;
Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.OperacaoLiberada = false;
var saude = Variaveis.OperacaoEmAndamento?.Sensoriamento?.OperadorSaude?.ModulosSaude;
foreach (var Mod in saude)
{
Mod.conectado = false;

View File

@ -1519,12 +1519,7 @@ namespace AgroBase.Services
{
long now = Stopwatch.GetTimestamp();
if (!IsDue(
Interlocked.Read(
ref _lastSyncStartMono
),
IntervaloSincronizacaoMs,
now))
if (!IsDue(Interlocked.Read(ref _lastSyncStartMono), IntervaloSincronizacaoMs, now))
{
return;
}
@ -1532,10 +1527,7 @@ namespace AgroBase.Services
if (!_syncDataGate.Wait(0))
return;
Interlocked.Exchange(
ref _lastSyncStartMono,
now
);
Interlocked.Exchange(ref _lastSyncStartMono, now);
_ = RunSyncAsync();
}
@ -1546,21 +1538,16 @@ namespace AgroBase.Services
try
{
await FuncoesGlobais.SafeExecuteAsync(
async () =>
{
await SyncDataService
.SincronizarArquivosComServidor();
}
).ConfigureAwait(false);
await FuncoesGlobais.SafeExecuteAsync(async () =>
{
await SyncDataService.SincronizarArquivosComServidor();
}).ConfigureAwait(false);
}
catch (Exception ex)
{
Interlocked.Increment(ref _syncErrors);
RecordError(
"[SerialService.Sync] " + ex.Message
);
RecordError("[SerialService.Sync] " + ex.Message);
}
finally
{

View File

@ -18,30 +18,32 @@ namespace AgroBase.Services
public static async Task<bool> SincronizarArquivosComServidor()
{
if (Sincronizando)
{
return false;
}
return false;
// Nao executa a verificacao se estiver em ambiente de teste
if (!Variaveis.Producao)
{
if (Sincronizando || !Variaveis.Producao)
return false;
var op = Variaveis.OperacaoEmAndamento;
if (op?.Sensoriamento?.Operacao == null)
return false;
}
Sincronizando = true;
bool sucesso = true;
if (!Variaveis.OperacaoEmAndamento.Sensoriamento.Operacao.OperacaoIniciada && APIService.HasInternet)
try
{
List<string> Operacoes = Directory.GetDirectories(Variaveis.CaminhoOperacoes).ToList();
foreach (string OperacaoID in Operacoes.Select(x => x.Split('/')[1]))
if (op.Sensoriamento.Operacao.OperacaoIniciada || !APIService.HasInternet)
{
string pathArquivoSync = Path.Combine(Variaveis.CaminhoOperacoes, OperacaoID, NomeArquivoSync);
return false;
}
foreach (string caminhoOperacao in Directory.GetDirectories(Variaveis.CaminhoOperacoes))
{
string operacaoId = Path.GetFileName(caminhoOperacao.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));
string pathArquivoSync = Path.Combine(Variaveis.CaminhoOperacoes, operacaoId, NomeArquivoSync);
SyncDataModel SyncOperacao = new SyncDataModel()
{
OperacaoID = OperacaoID,
OperacaoID = operacaoId,
Arquivos = new List<SyncDataArquivoModel>(),
InicioSync = new List<DateTime>(),
FimSync = new List<DateTime>(),
@ -51,11 +53,19 @@ namespace AgroBase.Services
await SyncarArquivos(SyncOperacao);
}
return true;
}
catch (Exception ex)
{
Variaveis.MostrarLog("[SyncDataService] Falha na sincronização: " + ex);
Sincronizando = false;
return sucesso;
return false;
}
finally
{
Sincronizando = false;
}
}
private static SyncDataModel AtualizarArquivoSync(SyncDataModel SyncOperacao, bool Leitura)
@ -103,7 +113,7 @@ namespace AgroBase.Services
}
// Imagens do caminho (sem prefixo)
List<string> imagensCaminho = Directory.GetFiles(Path.Combine(SyncOperacao.Caminho, T_Code.Knt.ToString())).Where(x => x.Contains("rgb")).ToList();
List<string> imagensCaminho = Directory.GetFiles(Path.Combine(SyncOperacao.Caminho, T_Code.Snr.ToString())).Where(x => x.ToLower().Contains("rgb")).ToList();
foreach (var imagemCaminho in imagensCaminho)
{
string nomeComPrefixo = Path.GetFileName(imagemCaminho); // Nome original, sem prefixo

View File

@ -8,21 +8,15 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static AgroBase.Models.Enums;
namespace AgroBase.Services
{
public class VersionamentoService
{
private static readonly object _ArquivoLock =
new object();
private static readonly SemaphoreSlim _AtualizacaoGate =
new SemaphoreSlim(1, 1);
private static readonly SemaphoreSlim _DownloadGate =
new SemaphoreSlim(2, 2);
private static readonly object _ArquivoLock = new object();
private static readonly SemaphoreSlim _AtualizacaoGate = new SemaphoreSlim(1, 1);
private static readonly SemaphoreSlim _DownloadGate = new SemaphoreSlim(2, 2);
private static string _ArquivoVersionamento { get; set; } =
"version_files.vsf";
@ -189,15 +183,6 @@ namespace AgroBase.Services
}
}
public static VersaoArquivoModel ArquivoScript(TipoArquivoVersionado tipo)
{
lock (_ArquivoLock)
{
return _ArquivosVersionados
.FirstOrDefault(x => x.TipoArquivo == tipo);
}
}
public static VersaoArquivoModel ArquivoModelo3D(string extensao)
{
lock (_ArquivoLock)
@ -250,14 +235,7 @@ namespace AgroBase.Services
}
catch { }
tmrAtualizacaoArquivos =
new AsyncTaskTimerModel(
"tmrAtualizarArquivos",
tmrAtualizacaoArquivos_Tick,
300,
null,
3000
);
tmrAtualizacaoArquivos = new AsyncTaskTimerModel("tmrAtualizarArquivos", tmrAtualizacaoArquivos_Tick, 300, null, 3000);
try
{
@ -265,14 +243,10 @@ namespace AgroBase.Services
}
catch { }
Forget(
AtualizarArquivoVersionamento(true),
"Atualizar arquivo de versionamento"
);
Forget(AtualizarArquivoVersionamento(true), "Atualizar arquivo de versionamento");
}
private static List<VersaoArquivoModel> ArquivosPorTipo(
TipoArquivoVersionado tipo)
private static List<VersaoArquivoModel> ArquivosPorTipo(TipoArquivoVersionado tipo)
{
return _ArquivosVersionados
.Where(x => x.TipoArquivo == tipo)
@ -280,27 +254,18 @@ namespace AgroBase.Services
.ToList();
}
private static bool TryResolverTipoArquivo(
string prefixo,
T_Code dispositivo,
out TipoArquivoVersionado tipoArquivo)
private static bool TryResolverTipoArquivo(string prefixo, T_Code dispositivo, out TipoArquivoVersionado tipoArquivo)
{
tipoArquivo = default(TipoArquivoVersionado);
string nomeDispositivo =
Enum.GetName(typeof(T_Code), dispositivo);
string nomeDispositivo = Enum.GetName(typeof(T_Code), dispositivo);
if (string.IsNullOrWhiteSpace(nomeDispositivo))
return false;
string nomeTipo =
prefixo + nomeDispositivo.ToUpperInvariant();
string nomeTipo = prefixo + nomeDispositivo.ToUpperInvariant();
return Enum.TryParse(
nomeTipo,
ignoreCase: true,
result: out tipoArquivo
);
return Enum.TryParse(nomeTipo, ignoreCase: true, result: out tipoArquivo);
}
/*
@ -316,41 +281,31 @@ namespace AgroBase.Services
{
return new List<VersaoArquivoModel>
{
NovoArquivo(1, "model", "C:\\AgroBaseModels\\Ervas\\", ".onnx", "4_1", 1, "models/weed_detector_model-4_0.onnx"),
NovoArquivo(1, "model", "C:\\AgroBaseModels\\Ervas\\", ".onnx", "4_0", 1, "models/weed_detector_model-4_0.onnx"),
NovoArquivo(2, "model", "C:\\AgroBaseModels\\Ervas\\", ".txt", "4_0", 1, "models/weed_detector_labelmap-4_0.txt"),
NovoArquivo(3, "model", "C:\\AgroBaseModels\\Ervas\\", ".json", "4_0", 1, "models/weed_detector_normstats-4_0.json"),
NovoArquivo(4, "modelmp", "C:\\AgroBaseModels\\Ervas\\", ".json", "4_0", 1, "models/weed_detector_moduleparams-4_0.json"),
NovoArquivo(5, "flatfield", "C:\\AgroBaseModels\\Ervas\\calibration\\", ".json", "1_0", 1, "models/weed_detector_calib_flatfield-1_0.json"),
NovoArquivo(6, "flatfield", "C:\\AgroBaseModels\\Ervas\\calibration\\", ".npz", "1_0", 1, "models/weed_detector_calib_flatfield-1_0.npz"),
NovoArquivo(5, "modelseg", "C:\\AgroBaseModels\\Ruas\\", ".onnx", "2_0", 0, "models/street_detector_model_seg-2_0.onnx"),
NovoArquivo(6, "modelseg", "C:\\AgroBaseModels\\Ruas\\", ".txt", "2_0", 0, "models/street_detector_labelmap_seg-2_0.txt"),
NovoArquivo(7, "modelseg", "C:\\AgroBaseModels\\Ruas\\", ".json", "1_4", 0, "models/street_detector_normstats-1_4.json"),
NovoArquivo(8, "modeldet", "C:\\AgroBaseModels\\Ruas\\", ".blob", "1_0", 0, "models/street_detector_model_det-1_0.blob"),
NovoArquivo(7, "modeldet", "C:\\AgroBaseModels\\Ruas\\", ".blob", "1_0", 0, "models/street_detector_model_det-1_0.blob"),
NovoArquivo(8, "modelseg", "C:\\AgroBaseModels\\Ruas\\", ".onnx", "2_0", 0, "models/street_detector_model_seg-2_0.onnx"),
NovoArquivo(9, "modelseg", "C:\\AgroBaseModels\\Ruas\\", ".txt", "2_0", 0, "models/street_detector_labelmap_seg-2_0.txt"),
NovoArquivo(10, "modelseg", "C:\\AgroBaseModels\\Ruas\\", ".json", "1_4", 0, "models/street_detector_normstats-1_4.json"),
NovoArquivo(11, "parametersAtu", "Parametros/", ".par", "2_0", 2, "parameters/parametersAtu-2_0.par"),
NovoArquivo(12, "parametersMvd", "Parametros/", ".par", "2_0", 3, "parameters/parametersMvd-2_0.par"),
NovoArquivo(13, "parametersSen", "Parametros/", ".par", "2_0", 4, "parameters/parametersSen-2_0.par"),
NovoArquivo(9, "parametersAtu", "Parametros/", ".par", "2_0", 2, "parameters/parametersAtu-2_0.par"),
NovoArquivo(10, "parametersMvd", "Parametros/", ".par", "2_0", 3, "parameters/parametersMvd-2_0.par"),
NovoArquivo(11, "parametersSen", "Parametros/", ".par", "2_0", 4, "parameters/parametersSen-2_0.par"),
NovoArquivo(14, "pinoutAtu", "Parametros/", ".pin", "2_0", 6, "parameters/pinoutAtu-2_0.pin"),
NovoArquivo(15, "pinoutSen", "Parametros/", ".pin", "2_0", 7, "parameters/pinoutSen-2_0.pin"),
NovoArquivo(12, "pinoutAtu", "Parametros/", ".pin", "2_0", 6, "parameters/pinoutAtu-2_0.pin"),
NovoArquivo(13, "pinoutSen", "Parametros/", ".pin", "2_0", 7, "parameters/pinoutSen-2_0.pin"),
NovoArquivo(14, "weed_detector_oak", "Python\\Scripts\\", ".py", "1_0", 9, "weed_detector_oak-1_0.py"),
NovoArquivo(15, "map_load", "Python\\Scripts\\", ".py", "1_0", 11, "scripts/map_load-1_0.py"),
NovoArquivo(16, "map_follow", "Python\\Scripts\\", ".py", "1_0", 12, "scripts/map_follow-1_0.py"),
NovoArquivo(17, "gps_viewer", "Python\\Scripts\\", ".py", "1_0", 13, "scripts/gps_viewer-1_0.py"),
NovoArquivo(18, "modelo_3d", "Python\\Output\\", ".obj", "1_0", 14, "modelo_3d-1_0.obj"),
NovoArquivo(19, "modelo_3d", "Python\\Output\\", ".mtl", "1_0", 14, "modelo_3d-1_0.mtl"),
NovoArquivo(16, "modelo_3d", "Python\\Output\\", ".obj", "1_0", 14, "modelo_3d-1_0.obj"),
NovoArquivo(17, "modelo_3d", "Python\\Output\\", ".mtl", "1_0", 14, "modelo_3d-1_0.mtl"),
};
}
private static VersaoArquivoModel NovoArquivo(
int id,
string arquivo,
string diretorio,
string extensao,
string versao,
int tipoArquivo,
string arquivoDownload)
private static VersaoArquivoModel NovoArquivo(int id, string arquivo, string diretorio, string extensao, string versao, int tipoArquivo, string arquivoDownload)
{
return new VersaoArquivoModel
{
@ -365,11 +320,6 @@ namespace AgroBase.Services
};
}
public static Task AtualizarArquivoVersionamento()
{
return AtualizarArquivoVersionamento(true);
}
public static async Task AtualizarArquivoVersionamento(bool BaixarArquivo)
{
if (!_AtualizacaoGate.Wait(0))
@ -381,110 +331,69 @@ namespace AgroBase.Services
{
if (op?.Sensoriamento?.Operacao?.OperacaoIniciada ?? false)
{
ResultadoAtualizacao =
"Atualização ignorada: operação em andamento.";
ResultadoAtualizacao = "Atualização ignorada: operação em andamento.";
return;
}
AtualizandoArquivos = true;
ProgressoAtualizacao = 0.0;
AtualizarProgressoSeguro(0);
ResultadoAtualizacao =
"Verificando atualizações...";
ResultadoAtualizacao = "Verificando atualizações...";
try { tmrAtualizacaoArquivos?.Restart(); } catch { }
GarantirDiretorio(Variaveis.CaminhoParametros);
string caminhoArquivo =
Path.Combine(
Variaveis.CaminhoParametros,
_ArquivoVersionamento
);
string caminhoArquivo = Path.Combine(Variaveis.CaminhoParametros, _ArquivoVersionamento);
if (!File.Exists(caminhoArquivo))
{
ResultadoAtualizacao =
"Criando manifesto local padrão...";
ResultadoAtualizacao = "Criando manifesto local padrão...";
await SalvarArquivoVersionamento(
CarregarArquivosVersionadosPadrao(),
verificarArquivos: false
).ConfigureAwait(false);
await SalvarArquivoVersionamento(CarregarArquivosVersionadosPadrao(), verificarArquivos: false).ConfigureAwait(false);
}
bool carregadoLocal =
await CarregarArquivoVersionamento(
caminhoArquivo,
baixarArquivosAusentes: false
).ConfigureAwait(false);
bool carregadoLocal = await CarregarArquivoVersionamento(caminhoArquivo, baixarArquivosAusentes: false).ConfigureAwait(false);
if (!carregadoLocal)
{
ResultadoAtualizacao =
"Manifesto local inválido. Restaurando padrão.";
ResultadoAtualizacao = "Manifesto local inválido. Restaurando padrão.";
await SalvarArquivoVersionamento(
CarregarArquivosVersionadosPadrao(),
verificarArquivos: false
).ConfigureAwait(false);
await SalvarArquivoVersionamento(CarregarArquivosVersionadosPadrao(), verificarArquivos: false).ConfigureAwait(false);
await CarregarArquivoVersionamento(
caminhoArquivo,
baixarArquivosAusentes: false
).ConfigureAwait(false);
await CarregarArquivoVersionamento(caminhoArquivo, baixarArquivosAusentes: false).ConfigureAwait(false);
}
bool podeUsarInternet =
APIService.HasInternet &&
(Variaveis.Producao || PermitirDownloadEmDebug);
bool podeUsarInternet = APIService.HasInternet && (Variaveis.Producao || PermitirDownloadEmDebug);
bool baixouManifesto = false;
if (BaixarArquivo && podeUsarInternet)
{
baixouManifesto =
await BaixarManifestoRemotoAsync(
caminhoArquivo
).ConfigureAwait(false);
baixouManifesto = await BaixarManifestoRemotoAsync(caminhoArquivo).ConfigureAwait(false);
}
else if (!APIService.HasInternet)
{
ResultadoAtualizacao =
"Sem internet. Usando manifesto local.";
ResultadoAtualizacao = "Sem internet. Usando manifesto local.";
}
else if (!Variaveis.Producao && !PermitirDownloadEmDebug)
{
ResultadoAtualizacao =
"Debug: manifesto local carregado, download remoto desabilitado.";
ResultadoAtualizacao = "Debug: manifesto local carregado, download remoto desabilitado.";
}
List<VersaoArquivoModel> snapshot =
GetArquivosSnapshot();
List<VersaoArquivoModel> snapshot = GetArquivosSnapshot();
bool baixarArquivos =
podeUsarInternet &&
(BaixarArquivo || ForcarDownloadArquivos);
bool baixarArquivos = podeUsarInternet && (BaixarArquivo || ForcarDownloadArquivos);
bool arquivosOk =
await VerificarArquivosAtualizados(
snapshot,
baixarArquivos
).ConfigureAwait(false);
bool arquivosOk = await VerificarArquivosAtualizados(snapshot, baixarArquivos).ConfigureAwait(false);
if (arquivosOk)
{
ResultadoAtualizacao =
baixouManifesto
? "Arquivos atualizados!"
: "Arquivos locais verificados!";
ResultadoAtualizacao = baixouManifesto ? "Arquivos atualizados!" : "Arquivos locais verificados!";
}
else
{
ResultadoAtualizacao =
baixarArquivos
? "Atualização concluída com pendências."
: "Arquivos carregados. Há pendências locais.";
ResultadoAtualizacao = baixarArquivos ? "Atualização concluída com pendências." : "Arquivos carregados. Há pendências locais.";
}
if (baixouManifesto && arquivosOk)
@ -499,18 +408,14 @@ namespace AgroBase.Services
}
catch (Exception ex)
{
Log(
"Erro ao recarregar parâmetros do módulo: " +
ex.Message
);
Log("Erro ao recarregar parâmetros do módulo: " + ex.Message);
}
}
}
}
catch (Exception ex)
{
ResultadoAtualizacao =
"Erro ao atualizar arquivos: " + ex.Message;
ResultadoAtualizacao = "Erro ao atualizar arquivos: " + ex.Message;
Log("[Versionamento] " + ex);
}
@ -522,8 +427,7 @@ namespace AgroBase.Services
}
}
private static async Task<bool> BaixarManifestoRemotoAsync(
string caminhoArquivo)
private static async Task<bool> BaixarManifestoRemotoAsync(string caminhoArquivo)
{
string backup = caminhoArquivo + "_old";
string temp = caminhoArquivo + ".download";
@ -536,35 +440,24 @@ namespace AgroBase.Services
if (File.Exists(temp))
File.Delete(temp);
ResultadoAtualizacao =
"Baixando manifesto de versionamento...";
ResultadoAtualizacao = "Baixando manifesto de versionamento...";
bool baixado =
await APIService.DownloadFileAsync(
_ArquivoVersionamento,
temp
).ConfigureAwait(false);
bool baixado = await APIService.DownloadFileAsync(_ArquivoVersionamento, temp).ConfigureAwait(false);
if (!baixado || !File.Exists(temp))
{
ResultadoAtualizacao =
"Não foi possível baixar o manifesto.";
ResultadoAtualizacao = "Não foi possível baixar o manifesto.";
return false;
}
bool carregouTemp =
await CarregarArquivoVersionamento(
temp,
baixarArquivosAusentes: false
).ConfigureAwait(false);
bool carregouTemp = await CarregarArquivoVersionamento(temp, baixarArquivosAusentes: false).ConfigureAwait(false);
if (!carregouTemp)
{
if (File.Exists(backup))
File.Copy(backup, caminhoArquivo, true);
ResultadoAtualizacao =
"Manifesto remoto inválido. Mantendo local.";
ResultadoAtualizacao = "Manifesto remoto inválido. Mantendo local.";
return false;
}
@ -573,10 +466,7 @@ namespace AgroBase.Services
File.Move(temp, caminhoArquivo);
await CarregarArquivoVersionamento(
caminhoArquivo,
baixarArquivosAusentes: false
).ConfigureAwait(false);
await CarregarArquivoVersionamento(caminhoArquivo, baixarArquivosAusentes: false).ConfigureAwait(false);
return true;
}
@ -589,8 +479,7 @@ namespace AgroBase.Services
}
catch { }
ResultadoAtualizacao =
"Erro ao baixar manifesto: " + ex.Message;
ResultadoAtualizacao = "Erro ao baixar manifesto: " + ex.Message;
Log("[Versionamento] " + ex);
return false;
@ -606,8 +495,7 @@ namespace AgroBase.Services
}
}
private static async Task<bool> SalvarArquivoVersionamento(
List<VersaoArquivoModel> arquivos)
private static async Task<bool> SalvarArquivoVersionamento(List<VersaoArquivoModel> arquivos)
{
return await SalvarArquivoVersionamento(
arquivos,
@ -615,55 +503,40 @@ namespace AgroBase.Services
).ConfigureAwait(false);
}
private static async Task<bool> SalvarArquivoVersionamento(
List<VersaoArquivoModel> arquivos,
bool verificarArquivos)
private static async Task<bool> SalvarArquivoVersionamento(List<VersaoArquivoModel> arquivos, bool verificarArquivos)
{
try
{
arquivos = NormalizarManifesto(arquivos);
string caminho =
Path.Combine(
Variaveis.CaminhoParametros,
_ArquivoVersionamento
);
string caminho = Path.Combine(Variaveis.CaminhoParametros, _ArquivoVersionamento);
GarantirDiretorio(Variaveis.CaminhoParametros);
string json =
JsonConvert.SerializeObject(
arquivos,
Formatting.Indented
);
string json = JsonConvert.SerializeObject(arquivos, Formatting.Indented);
File.WriteAllText(caminho, json);
lock (_ArquivoLock)
{
_ArquivosVersionados =
new List<VersaoArquivoModel>(arquivos);
_ArquivosVersionados = new List<VersaoArquivoModel>(arquivos);
}
if (!verificarArquivos)
return true;
return await VerificarArquivosAtualizados(
arquivos
).ConfigureAwait(false);
return await VerificarArquivosAtualizados(arquivos).ConfigureAwait(false);
}
catch (Exception ex)
{
ResultadoAtualizacao =
"Erro ao salvar manifesto: " + ex.Message;
ResultadoAtualizacao = "Erro ao salvar manifesto: " + ex.Message;
Log("[Versionamento] " + ex);
return false;
}
}
private static async Task<bool> CarregarArquivoVersionamento(
string path)
private static async Task<bool> CarregarArquivoVersionamento(string path)
{
return await CarregarArquivoVersionamento(
path,
@ -671,59 +544,46 @@ namespace AgroBase.Services
).ConfigureAwait(false);
}
private static async Task<bool> CarregarArquivoVersionamento(
string path,
bool baixarArquivosAusentes)
private static async Task<bool> CarregarArquivoVersionamento(string path, bool baixarArquivosAusentes)
{
try
{
if (!File.Exists(path))
return false;
string json =
File.ReadAllText(path);
string json = File.ReadAllText(path);
List<VersaoArquivoModel> arquivos =
DesserializarManifesto(json);
List<VersaoArquivoModel> arquivos = DesserializarManifesto(json);
arquivos =
NormalizarManifesto(arquivos);
arquivos = NormalizarManifesto(arquivos);
if (arquivos == null || arquivos.Count == 0)
return false;
lock (_ArquivoLock)
{
_ArquivosVersionados =
new List<VersaoArquivoModel>(arquivos);
_ArquivosVersionados = new List<VersaoArquivoModel>(arquivos);
}
return await VerificarArquivosAtualizados(
arquivos,
baixarArquivosAusentes
).ConfigureAwait(false);
return await VerificarArquivosAtualizados(arquivos, baixarArquivosAusentes).ConfigureAwait(false);
}
catch (Exception ex)
{
ResultadoAtualizacao =
"Erro ao carregar manifesto: " + ex.Message;
ResultadoAtualizacao = "Erro ao carregar manifesto: " + ex.Message;
Log("[Versionamento] " + ex);
return false;
}
}
private static List<VersaoArquivoModel> DesserializarManifesto(
string json)
private static List<VersaoArquivoModel> DesserializarManifesto(string json)
{
if (string.IsNullOrWhiteSpace(json))
return new List<VersaoArquivoModel>();
try
{
return JsonConvert
.DeserializeObject<List<VersaoArquivoModel>>(json)
?? new List<VersaoArquivoModel>();
return JsonConvert.DeserializeObject<List<VersaoArquivoModel>>(json) ?? new List<VersaoArquivoModel>();
}
catch
{
@ -731,26 +591,15 @@ namespace AgroBase.Services
* O contrato atual apareceu com vírgula sobrando antes de }.
* JSON.NET não aceita isso, então limpamos trailing commas.
*/
string reparado =
Regex.Replace(
json,
@",(\s*[}\]])",
"$1"
);
string reparado = Regex.Replace(json, @",(\s*[}\]])", "$1");
return JsonConvert
.DeserializeObject<List<VersaoArquivoModel>>(
reparado
)
?? new List<VersaoArquivoModel>();
return JsonConvert.DeserializeObject<List<VersaoArquivoModel>>(reparado) ?? new List<VersaoArquivoModel>();
}
}
private static List<VersaoArquivoModel> NormalizarManifesto(
IEnumerable<VersaoArquivoModel> arquivos)
private static List<VersaoArquivoModel> NormalizarManifesto(IEnumerable<VersaoArquivoModel> arquivos)
{
var result =
new List<VersaoArquivoModel>();
var result = new List<VersaoArquivoModel>();
if (arquivos == null)
return result;
@ -763,29 +612,16 @@ namespace AgroBase.Services
if (arquivo.id <= 0)
arquivo.id = result.Count + 1;
arquivo.Diretorio =
ResolverDiretorioContrato(
arquivo.Diretorio
);
arquivo.Diretorio = ResolverDiretorioContrato(arquivo.Diretorio);
if (string.IsNullOrWhiteSpace(arquivo.Extensao) &&
!string.IsNullOrWhiteSpace(arquivo.ArquivoDownload))
if (string.IsNullOrWhiteSpace(arquivo.Extensao) && !string.IsNullOrWhiteSpace(arquivo.ArquivoDownload))
{
arquivo.Extensao =
Path.GetExtension(
arquivo.ArquivoDownload
.Replace("/", "\\")
);
arquivo.Extensao = Path.GetExtension(arquivo.ArquivoDownload.Replace("/", "\\"));
}
if (string.IsNullOrWhiteSpace(arquivo.Arquivo) &&
!string.IsNullOrWhiteSpace(arquivo.ArquivoDownload))
if (string.IsNullOrWhiteSpace(arquivo.Arquivo) && !string.IsNullOrWhiteSpace(arquivo.ArquivoDownload))
{
string file =
Path.GetFileNameWithoutExtension(
arquivo.ArquivoDownload
.Replace("/", "\\")
);
string file = Path.GetFileNameWithoutExtension(arquivo.ArquivoDownload.Replace("/", "\\"));
arquivo.Arquivo = file;
}
@ -795,13 +631,10 @@ namespace AgroBase.Services
result.Add(arquivo);
}
return result
.OrderBy(x => x.id)
.ToList();
return result.OrderBy(x => x.id).ToList();
}
public static Task<bool> VerificarArquivosAtualizados(
List<VersaoArquivoModel> arquivos)
public static Task<bool> VerificarArquivosAtualizados(List<VersaoArquivoModel> arquivos)
{
return VerificarArquivosAtualizados(
arquivos,
@ -809,9 +642,7 @@ namespace AgroBase.Services
);
}
private static async Task<bool> VerificarArquivosAtualizados(
List<VersaoArquivoModel> arquivos,
bool baixarArquivosAusentes)
private static async Task<bool> VerificarArquivosAtualizados(List<VersaoArquivoModel> arquivos, bool baixarArquivosAusentes)
{
if (arquivos == null || arquivos.Count == 0)
{
@ -820,38 +651,29 @@ namespace AgroBase.Services
return false;
}
double[] progressoArquivos =
new double[arquivos.Count];
double[] progressoArquivos = new double[arquivos.Count];
object progressoLock =
new object();
object progressoLock = new object();
var tasks =
arquivos
.Select((arquivo, index) =>
VerificarArquivoAtualizado(
arquivo,
baixarArquivosAusentes,
progressoIndividual =>
var tasks = arquivos
.Select((arquivo, index) =>
VerificarArquivoAtualizado(
arquivo,
baixarArquivosAusentes,
progressoIndividual =>
{
lock (progressoLock)
{
lock (progressoLock)
{
progressoArquivos[index] =
progressoIndividual;
progressoArquivos[index] = progressoIndividual;
ProgressoAtualizacao =
progressoArquivos.Average();
}
ProgressoAtualizacao = progressoArquivos.Average();
}
AtualizarProgressoSeguro(
ProgressoAtualizacao
);
}))
.ToList();
AtualizarProgressoSeguro(ProgressoAtualizacao);
}))
.ToList();
bool[] resultados =
await Task.WhenAll(tasks)
.ConfigureAwait(false);
bool[] resultados = await Task.WhenAll(tasks).ConfigureAwait(false);
if (resultados.All(x => x))
{
@ -862,10 +684,7 @@ namespace AgroBase.Services
return resultados.All(x => x);
}
private static async Task<bool> VerificarArquivoAtualizado(
VersaoArquivoModel arquivo,
bool baixarArquivoAusente,
Action<double> reportProgress)
private static async Task<bool> VerificarArquivoAtualizado(VersaoArquivoModel arquivo, bool baixarArquivoAusente, Action<double> reportProgress)
{
if (arquivo == null)
{
@ -875,18 +694,15 @@ namespace AgroBase.Services
try
{
arquivo.Diretorio =
ResolverDiretorioContrato(
arquivo.Diretorio
);
arquivo.Diretorio = ResolverDiretorioContrato(arquivo.Diretorio);
GarantirDiretorio(arquivo.Diretorio);
string caminhoCompleto =
arquivo.CaminhoCompleto;
string caminhoCompleto = ResolverCaminhoLocal(arquivo.CaminhoCompleto);
if (File.Exists(caminhoCompleto) &&
!ForcarDownloadArquivos)
GarantirDiretorio(Path.GetDirectoryName(caminhoCompleto));
if (File.Exists(caminhoCompleto) && !ForcarDownloadArquivos)
{
reportProgress?.Invoke(100);
return true;
@ -904,29 +720,17 @@ namespace AgroBase.Services
return File.Exists(caminhoCompleto);
}
await _DownloadGate
.WaitAsync()
.ConfigureAwait(false);
await _DownloadGate.WaitAsync().ConfigureAwait(false);
try
{
ResultadoAtualizacao =
"Baixando " +
(arquivo.ArquivoDownload ??
arquivo.NomeArquivoLocal) +
"...";
ResultadoAtualizacao = $"Baixando {(arquivo.ArquivoDownload ?? arquivo.NomeArquivoLocal)}...";
bool baixado =
await APIService.DownloadFileAsync(
arquivo.ArquivoDownload,
caminhoCompleto,
reportProgress
).ConfigureAwait(false);
bool baixado = await APIService.DownloadFileAsync(arquivo.ArquivoDownload, caminhoCompleto, reportProgress).ConfigureAwait(false);
reportProgress?.Invoke(100);
return baixado &&
File.Exists(caminhoCompleto);
return baixado && File.Exists(caminhoCompleto);
}
finally
{
@ -937,11 +741,7 @@ namespace AgroBase.Services
{
reportProgress?.Invoke(100);
Log(
"[Versionamento] Erro no arquivo " +
arquivo?.ArquivoDownload + ": " +
ex.Message
);
Log("[Versionamento] Erro no arquivo " + arquivo?.ArquivoDownload + ": " + ex.Message);
return false;
}
@ -955,20 +755,18 @@ namespace AgroBase.Services
ForcarDownloadArquivos);
}
private static bool ArquivoExisteLocalmente(
VersaoArquivoModel arquivo)
private static bool ArquivoExisteLocalmente(VersaoArquivoModel arquivo)
{
try
{
if (arquivo == null)
return false;
arquivo.Diretorio =
ResolverDiretorioContrato(
arquivo.Diretorio
);
arquivo.Diretorio = ResolverDiretorioContrato(arquivo.Diretorio);
return File.Exists(arquivo.CaminhoCompleto);
string caminhoCompleto = ResolverCaminhoLocal(arquivo.CaminhoCompleto);
return File.Exists(caminhoCompleto);
}
catch
{
@ -976,59 +774,65 @@ namespace AgroBase.Services
}
}
private static string ResolverDiretorioContrato(
string diretorio)
private static string ResolverDiretorioContrato(string diretorio)
{
string caminhoResolvido;
if (string.IsNullOrWhiteSpace(diretorio))
return Variaveis.CaminhoParametros;
{
caminhoResolvido = Variaveis.CaminhoParametros;
string normalizado =
diretorio
.Replace("/", "\\")
.Trim();
return GarantirSeparadorFinal(caminhoResolvido);
}
string normalizado = diretorio.Replace("/", "\\").Trim();
// Caminho absoluto: C:\AgroBaseModels\...
if (Path.IsPathRooted(normalizado))
return GarantirSeparadorFinal(normalizado);
string semBarra =
normalizado
.Trim('\\')
.Trim('/');
if (semBarra.Equals(
"Parametros",
StringComparison.OrdinalIgnoreCase))
{
return GarantirSeparadorFinal(
Variaveis.CaminhoParametros
);
caminhoResolvido = Path.GetFullPath(normalizado);
return GarantirSeparadorFinal(caminhoResolvido);
}
if (semBarra.StartsWith(
"Python\\",
StringComparison.OrdinalIgnoreCase))
{
string rel =
semBarra.Substring("Python\\".Length);
string semBarra = normalizado.Trim('\\', '/');
return GarantirSeparadorFinal(
Path.Combine(
PythonService.CaminhoGeral,
rel
)
);
// Parametros/
if (semBarra.Equals("Parametros", StringComparison.OrdinalIgnoreCase))
{
caminhoResolvido = Variaveis.CaminhoParametros;
return GarantirSeparadorFinal(caminhoResolvido);
}
return GarantirSeparadorFinal(
Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
semBarra
)
);
// Python\Output\
if (semBarra.StartsWith("Python\\", StringComparison.OrdinalIgnoreCase))
{
string relativoAoPython = semBarra.Substring("Python\\".Length);
caminhoResolvido = ResolverCaminhoLocal(Path.Combine(PythonService.CaminhoGeral, relativoAoPython));
return GarantirSeparadorFinal(caminhoResolvido);
}
// Qualquer outro diretório relativo
caminhoResolvido = ResolverCaminhoLocal(semBarra);
return GarantirSeparadorFinal(caminhoResolvido);
}
private static string GarantirSeparadorFinal(
string path)
private static string ResolverCaminhoLocal(string caminho)
{
if (string.IsNullOrWhiteSpace(caminho))
throw new ArgumentException("Caminho local não informado.");
if (Path.IsPathRooted(caminho))
return Path.GetFullPath(caminho);
return Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, caminho));
}
private static string GarantirSeparadorFinal(string path)
{
if (string.IsNullOrWhiteSpace(path))
return path;
@ -1039,8 +843,7 @@ namespace AgroBase.Services
return path + Path.DirectorySeparatorChar;
}
private static void GarantirDiretorio(
string diretorio)
private static void GarantirDiretorio(string diretorio)
{
if (string.IsNullOrWhiteSpace(diretorio))
return;
@ -1063,19 +866,14 @@ namespace AgroBase.Services
{
try
{
string backup =
Path.Combine(
Variaveis.CaminhoParametros,
_ArquivoVersionamento + "_old"
);
string backup = Path.Combine(Variaveis.CaminhoParametros, _ArquivoVersionamento + "_old");
if (File.Exists(backup))
File.Delete(backup);
}
catch { }
List<VersaoArquivoModel> arquivos =
GetArquivosSnapshot();
List<VersaoArquivoModel> arquivos = GetArquivosSnapshot();
foreach (string diretorio in arquivos
.Select(x => x.Diretorio)
@ -1102,15 +900,11 @@ namespace AgroBase.Services
StringComparer.OrdinalIgnoreCase
);
foreach (string arquivoLocal in
Directory.GetFiles(diretorio))
foreach (string arquivoLocal in Directory.GetFiles(diretorio))
{
string nome =
Path.GetFileName(arquivoLocal);
string nome = Path.GetFileName(arquivoLocal);
if (nome.EndsWith(
".vsf",
StringComparison.OrdinalIgnoreCase))
if (nome.EndsWith(".vsf", StringComparison.OrdinalIgnoreCase) || nome == "config.json")
{
continue;
}
@ -1123,39 +917,20 @@ namespace AgroBase.Services
}
catch (Exception ex)
{
Log(
"[Versionamento] Erro limpando antigos em " +
diretorio + ": " + ex.Message
);
Log("[Versionamento] Erro limpando antigos em " + diretorio + ": " + ex.Message);
}
}
}
private static async Task tmrAtualizacaoArquivos_Tick()
{
string progressoStr =
AtualizandoArquivos
? "(" +
ProgressoAtualizacao.ToString("0.00") +
"%) Atualizando arquivos..."
: ResultadoAtualizacao;
string progressoStr = AtualizandoArquivos ? $"({ProgressoAtualizacao:F2}%) Atualizando arquivos..." : ResultadoAtualizacao;
int progressoInt =
Math.Max(
0,
Math.Min(
100,
Convert.ToInt32(ProgressoAtualizacao)
)
);
int progressoInt = Math.Max(0, Math.Min(100, Convert.ToInt32(ProgressoAtualizacao)));
AtualizarConsole(
progressoStr,
progressoInt
);
AtualizarConsole(progressoStr, progressoInt);
if (!AtualizandoArquivos &&
progressoInt >= 100)
if (!AtualizandoArquivos && progressoInt >= 100)
{
try { tmrAtualizacaoArquivos?.Stop(); } catch { }
}

View File

@ -1 +1 @@
{"Carregado":true,"serial_number":"01","base_ip":"192.168.1.100","base_porta_caminho":5000,"base_porta_ervas":5002,"rover_interface":"Ethernet","rover_ip":"192.168.1.10","rover_gateway":"192.168.1.1","rover_submask":"255.255.255.0","camera_caminhho_id":"14442C10C143E2D600","camera_ervas_id":["LT00B00001"]}
{"Carregado":true,"serial_number":"01","base_ip":"20.1.0.2","base_porta_caminho":5000,"base_porta_ervas":5002,"comunicacao_interface":"AG_Hallow","rover_ip":"20.1.0.10","rover_gateway":"20.1.0.1","rover_submask":"255.255.255.0","camera_caminhho_id":"14442C10C143E2D600","camera_ervas_id":["194430108133AC2F00"],"can_interface":"AG_Can","can_ip_pc":"192.168.0.5","can_ip_modulo":"192.168.0.7","can_porta":8235,"can_gateway":"192.168.0.1","can_submask":"255.255.255.0"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,173 +1,223 @@
[
{
"id": 1,
"Arquivo": "model",
"Diretorio": "C:\\AgroBaseModels\\Ervas\\",
"Arquivo": "model",
"Extensao": ".onnx",
"Versao": "4_0",
"TipoArquivo": 1,
"ArquivoDownload": "models/weed_detector_model-4_0.onnx"
"ArquivoDownload": "models/weed_detector_model-4_0.onnx",
"NomeArquivoLocal": "model-4_0.onnx",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ervas\\model-4_0.onnx",
"Atualizado": true,
"ArquivoAtualizado": false,
"TipoArquivo": 1
},
{
"id": 2,
"Arquivo": "model",
"Diretorio": "C:\\AgroBaseModels\\Ervas\\",
"Arquivo": "model",
"Extensao": ".txt",
"Versao": "4_0",
"TipoArquivo": 1,
"ArquivoDownload": "models/weed_detector_labelmap-4_0.txt"
"ArquivoDownload": "models/weed_detector_labelmap-4_0.txt",
"NomeArquivoLocal": "model-4_0.txt",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ervas\\model-4_0.txt",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 1
},
{
"id": 3,
"Arquivo": "model",
"Diretorio": "C:\\AgroBaseModels\\Ervas\\",
"Arquivo": "model",
"Extensao": ".json",
"Versao": "4_0",
"TipoArquivo": 1,
"ArquivoDownload": "models/weed_detector_normstats-4_0.json"
"ArquivoDownload": "models/weed_detector_normstats-4_0.json",
"NomeArquivoLocal": "model-4_0.json",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ervas\\model-4_0.json",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 1
},
{
"id": 4,
"Arquivo": "modelmp",
"Diretorio": "C:\\AgroBaseModels\\Ervas\\",
"Arquivo": "modelmp",
"Extensao": ".json",
"Versao": "4_0",
"TipoArquivo": 1,
"ArquivoDownload": "models/weed_detector_moduleparams-4_0.json"
"ArquivoDownload": "models/weed_detector_moduleparams-4_0.json",
"NomeArquivoLocal": "modelmp-4_0.json",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ervas\\modelmp-4_0.json",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 1
},
{
"id": 5,
"Arquivo": "modelseg",
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
"Extensao": ".onnx",
"Versao": "2_0",
"TipoArquivo": 0,
"ArquivoDownload": "models/street_detector_model_seg-2_0.onnx",
"Diretorio": "C:\\AgroBaseModels\\Ervas\\calibration\\",
"Arquivo": "flatfield",
"Extensao": ".json",
"Versao": "1_0",
"ArquivoDownload": "models/weed_detector_calib_flatfield-1_0.json",
"NomeArquivoLocal": "flatfield-1_0.json",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ervas\\calibration\\flatfield-1_0.json",
"Atualizado": true,
"ArquivoAtualizado": false,
"TipoArquivo": 1
},
{
"id": 6,
"Arquivo": "modelseg",
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
"Extensao": ".txt",
"Versao": "2_0",
"TipoArquivo": 0,
"ArquivoDownload": "models/street_detector_labelmap_seg-2_0.txt"
"Diretorio": "C:\\AgroBaseModels\\Ervas\\calibration\\",
"Arquivo": "flatfield",
"Extensao": ".npz",
"Versao": "1_0",
"ArquivoDownload": "models/weed_detector_calib_flatfield-1_0.npz",
"NomeArquivoLocal": "flatfield-1_0.npz",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ervas\\calibration\\flatfield-1_0.npz",
"Atualizado": true,
"ArquivoAtualizado": false,
"TipoArquivo": 1
},
{
"id": 7,
"Arquivo": "modelseg",
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
"Extensao": ".json",
"Versao": "1_4",
"TipoArquivo": 0,
"ArquivoDownload": "models/street_detector_normstats-1_4.json"
"Arquivo": "modeldet",
"Extensao": ".blob",
"Versao": "1_0",
"ArquivoDownload": "models/street_detector_model_det-1_0.blob",
"NomeArquivoLocal": "modeldet-1_0.blob",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ruas\\modeldet-1_0.blob",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 0
},
{
"id": 8,
"Arquivo": "modeldet",
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
"Extensao": ".blob",
"Versao": "1_0",
"TipoArquivo": 0,
"ArquivoDownload": "models/street_detector_model_det-1_0.blob",
"Arquivo": "modelseg",
"Extensao": ".onnx",
"Versao": "2_0",
"ArquivoDownload": "models/street_detector_model_seg-2_0.onnx",
"NomeArquivoLocal": "modelseg-2_0.onnx",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ruas\\modelseg-2_0.onnx",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 0
},
{
"id": 9,
"Arquivo": "parametersAtu",
"Diretorio": "Parametros/",
"Extensao": ".par",
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
"Arquivo": "modelseg",
"Extensao": ".txt",
"Versao": "2_0",
"TipoArquivo": 2,
"ArquivoDownload": "parameters/parametersAtu-2_0.par",
"ArquivoDownload": "models/street_detector_labelmap_seg-2_0.txt",
"NomeArquivoLocal": "modelseg-2_0.txt",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ruas\\modelseg-2_0.txt",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 0
},
{
"id": 10,
"Arquivo": "parametersMvd",
"Diretorio": "Parametros/",
"Extensao": ".par",
"Versao": "2_0",
"TipoArquivo": 3,
"ArquivoDownload": "parameters/parametersMvd-2_0.par"
"Diretorio": "C:\\AgroBaseModels\\Ruas\\",
"Arquivo": "modelseg",
"Extensao": ".json",
"Versao": "1_4",
"ArquivoDownload": "models/street_detector_normstats-1_4.json",
"NomeArquivoLocal": "modelseg-1_4.json",
"CaminhoCompleto": "C:\\AgroBaseModels\\Ruas\\modelseg-1_4.json",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 0
},
{
"id": 11,
"Arquivo": "parametersSen",
"Diretorio": "Parametros/",
"Arquivo": "parametersAtu",
"Extensao": ".par",
"Versao": "2_0",
"TipoArquivo": 4,
"ArquivoDownload": "parameters/parametersSen-2_0.par"
"ArquivoDownload": "parameters/parametersAtu-2_0.par",
"NomeArquivoLocal": "parametersAtu-2_0.par",
"CaminhoCompleto": "Parametros/parametersAtu-2_0.par",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 2
},
{
"id": 12,
"Arquivo": "pinoutAtu",
"Diretorio": "Parametros/",
"Extensao": ".pin",
"Arquivo": "parametersMvd",
"Extensao": ".par",
"Versao": "2_0",
"TipoArquivo": 6,
"ArquivoDownload": "parameters/pinoutAtu-2_0.pin"
"ArquivoDownload": "parameters/parametersMvd-2_0.par",
"NomeArquivoLocal": "parametersMvd-2_0.par",
"CaminhoCompleto": "Parametros/parametersMvd-2_0.par",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 3
},
{
"id": 13,
"Arquivo": "pinoutSen",
"Diretorio": "Parametros/",
"Extensao": ".pin",
"Arquivo": "parametersSen",
"Extensao": ".par",
"Versao": "2_0",
"TipoArquivo": 7,
"ArquivoDownload": "parameters/pinoutSen-2_0.pin"
"ArquivoDownload": "parameters/parametersSen-2_0.par",
"NomeArquivoLocal": "parametersSen-2_0.par",
"CaminhoCompleto": "Parametros/parametersSen-2_0.par",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 4
},
{
"id": 14,
"Arquivo": "weed_detector_oak",
"Diretorio": "Python\\Scripts\\",
"Extensao": ".py",
"Versao": "1_0",
"TipoArquivo": 9,
"ArquivoDownload": "weed_detector_oak-1_0.py"
"Diretorio": "Parametros/",
"Arquivo": "pinoutAtu",
"Extensao": ".pin",
"Versao": "2_0",
"ArquivoDownload": "parameters/pinoutAtu-2_0.pin",
"NomeArquivoLocal": "pinoutAtu-2_0.pin",
"CaminhoCompleto": "Parametros/pinoutAtu-2_0.pin",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 6
},
{
"id": 15,
"Arquivo": "map_load",
"Diretorio": "Python\\Scripts\\",
"Extensao": ".py",
"Versao": "1_0",
"TipoArquivo": 11,
"ArquivoDownload": "scripts/map_load-1_0.py"
"Diretorio": "Parametros/",
"Arquivo": "pinoutSen",
"Extensao": ".pin",
"Versao": "2_0",
"ArquivoDownload": "parameters/pinoutSen-2_0.pin",
"NomeArquivoLocal": "pinoutSen-2_0.pin",
"CaminhoCompleto": "Parametros/pinoutSen-2_0.pin",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 7
},
{
"id": 16,
"Arquivo": "map_follow",
"Diretorio": "Python\\Scripts\\",
"Extensao": ".py",
"Diretorio": "Python\\Output\\",
"Arquivo": "modelo_3d",
"Extensao": ".obj",
"Versao": "1_0",
"TipoArquivo": 12,
"ArquivoDownload": "scripts/map_follow-1_0.py"
"ArquivoDownload": "modelo_3d-1_0.obj",
"NomeArquivoLocal": "modelo_3d-1_0.obj",
"CaminhoCompleto": "Python\\Output\\modelo_3d-1_0.obj",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 14
},
{
"id": 17,
"Arquivo": "gps_viewer",
"Diretorio": "Python\\Scripts\\",
"Extensao": ".py",
"Versao": "1_0",
"TipoArquivo": 13,
"ArquivoDownload": "scripts/gps_viewer-1_0.py"
},
{
"id": 18,
"Arquivo": "modelo_3d",
"Diretorio": "Python\\Output\\",
"Extensao": ".obj",
"Versao": "1_0",
"TipoArquivo": 14,
"ArquivoDownload": "modelo_3d-1_0.obj"
},
{
"id": 19,
"Arquivo": "modelo_3d",
"Diretorio": "Python\\Output\\",
"Extensao": ".mtl",
"Versao": "1_0",
"TipoArquivo": 14,
"ArquivoDownload": "modelo_3d-1_0.mtl"
"ArquivoDownload": "modelo_3d-1_0.mtl",
"NomeArquivoLocal": "modelo_3d-1_0.mtl",
"CaminhoCompleto": "Python\\Output\\modelo_3d-1_0.mtl",
"Atualizado": true,
"ArquivoAtualizado": true,
"TipoArquivo": 14
}
]

View File

@ -1,277 +0,0 @@
import geopandas as gpd
import json
import sys
import folium
import requests
import re
def internet_disponivel():
"""Verifica se há conexão com a internet."""
url = 'http://www.google.com/'
timeout = 5
try:
_ = requests.get(url, timeout=timeout)
return True
except requests.ConnectionError:
return False
# Argumentos e configurações iniciais
pathFiles = sys.argv[1]
fileName = sys.argv[2]
outputName = sys.argv[3]
outputMapName = sys.argv[4]
topico_gps = sys.argv[5]
pastaSaida = 'Python/Output/'
center = [0, 0]
# Verificar se há conexão com a internet
if internet_disponivel():
m = folium.Map(location=center, zoom_start=12)
else:
m = folium.Map(location=center, zoom_start=12, tiles=None)
# Salvar o mapa interativo em um arquivo HTML
m.save(pastaSaida + outputMapName)
# Caminho completo para o arquivo HTML gerado
caminho_completo_html = pastaSaida + outputMapName
# Abrir o arquivo HTML para leitura e escrita
with open(caminho_completo_html, 'r+') as arquivo_html:
# Ler o conteúdo do arquivo
conteudo_html = arquivo_html.read()
# Usar expressão regular para encontrar o ID do mapa
padrao_id_mapa = re.compile(r'id="map_(.*?)"')
resultado_busca = padrao_id_mapa.search(conteudo_html)
# Verificar se encontrou um ID de mapa
if resultado_busca:
id_mapa = 'map_' + resultado_busca.group(1)
else:
raise ValueError("Não foi possível encontrar o ID do mapa no arquivo HTML.")
# Script JavaScript para adicionar ao HTML, com o ID do mapa substituído
script_desenha_trajeotria = f"""
<script>
function trajeto_json_onEachFeature(feature, layer) {{
layer.on({{
}});
}};
var trajeto_json = L.geoJson(null, {{
onEachFeature: trajeto_json_onEachFeature,
style: function(feature) {{
return {{color: 'red'}};
}}
}}
);
function trajeto_json_add (data) {{
trajeto_json.addData(data);
}}
trajeto_json_add({{"features": []}});
trajeto_json.addTo({id_mapa});
function adicionarGeometria(novaGeometria) {{
trajeto_json.addData(novaGeometria);
}}
function saoCoordenadasIguais(coord1, coord2, tolerancia) {{
return Math.abs(coord1[0] - coord2[0]) < tolerancia && Math.abs(coord1[1] - coord2[1]) < tolerancia;
}}
function adicionarCoordenada(idGeometria, novaCoordenada) {{
var feature = trajeto_json.toGeoJSON().features.find(f => f.id === idGeometria);
if (feature) {{
var ultima = [0.0,0.0];
if (feature.geometry.coordinates.length > 0) {{
ultima = feature.geometry.coordinates[feature.geometry.coordinates.length - 1];
}}
if (!saoCoordenadasIguais(ultima, novaCoordenada, 0.000001)) {{
feature.geometry.coordinates.push(novaCoordenada);
var ft = feature;
trajeto_json.clearLayers();
trajeto_json.addData(ft);
}}
}} else {{
console.log("Feature com ID " + idGeometria + " não encontrada.");
}}
}}
adicionarGeometria({{
"type": "Feature",
"geometry": {{
"type": "LineString",
"coordinates": []
}},
"properties": {{"Dist1": 0.0, "Dist2": 0.0, "Id": 1517, "Length": 21.724783283, "Name": "Projeto"}},
"id": "Tj"
}});
</script>
"""
# Script JavaScript para adicionar ao HTML, com o ID do mapa substituído
script_atualizacao_marcador = f"""
<script src="folium/mqtt.min.js"></script>
<script src="folium/leaflet.rotatedMarker.js"></script>
<script>
let posicaoAtualEquipamento = {{
lat: 0,
long: 0
}};
let posicaoAtualBase = {{
lat: 0,
long: 0
}};
var customIcon = L.icon({{
iconUrl: 'folium/images/position_marker.png',
iconSize: [32, 32],
iconAnchor: [16, 16],
popupAnchor: [0, -16]
}});
var marcadorEquipamento = L.marker([0, 0], {{
icon: customIcon
}}).addTo({id_mapa});
var marcadorBase = L.marker([0, 0], {{}}).addTo({id_mapa});
var icon = L.AwesomeMarkers.icon(
{{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}}
);
marcadorBase.setIcon(icon);
// Conectar ao broker MQTT
const client = mqtt.connect('ws://localhost:9001'); // Use wss para conexão segura
// Quando conectado, inscreva-se no tópico desejado
client.on('connect', function () {{
console.log('Conectado ao broker MQTT');
// Inscrever-se no tópico
client.subscribe('{topico_gps}', function (err) {{
if (!err) {{
console.log("Inscrição bem-sucedida no tópico");
}} else {{
console.error("Falha na inscrição do tópico", err);
}}
}});
}});
// Lidar com mensagens recebidas para o tópico inscrito
client.on('message', function (topic, message) {{
var dados = JSON.parse(message);
if (topic === "{topico_gps}") {{
// A mensagem e um Buffer, converta para string ou objeto conforme necessario
//console.log(`Mensagem recebida no topico '${{topic}}': ${{message.toString()}}`);
var id = dados.id;
var novaLatitude = dados.latitude;
var novaLongitude = dados.longitude;
var novaPosicao = [novaLatitude, novaLongitude];
var orientacao = dados.orientacao;
var foco = dados.foco;
if (id == 1) {{
marcadorBase.setLatLng(novaPosicao);
// Calcular angulo de rotacao
var angulo = dados.orientacao;
posicaoAtualBase.lat = novaPosicao[0];
posicaoAtualBase.long = novaPosicao[1];
// Rotacionar o marcador para o angulo calculado
marcadorBase.setRotationAngle(angulo);
}}
else {{
marcadorEquipamento.setLatLng(novaPosicao);
// Calcular angulo de rotacao
var angulo = dados.orientacao;
posicaoAtualEquipamento.lat = novaPosicao[0];
posicaoAtualEquipamento.long = novaPosicao[1];
// Rotacionar o marcador para o angulo calculado
marcadorEquipamento.setRotationAngle(angulo);
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
}}
if (foco) {{
{id_mapa}.setView(novaPosicao, {id_mapa}.getZoom());
}}
}}
// A mensagem é um Buffer, converta para string ou objeto conforme necessário
//console.log(`Mensagem recebida no tópico '${{topic}}': ${{message.toString()}}`);
/*var dados = JSON.parse(message);
var novaLatitude = dados.latitude;
var novaLongitude = dados.longitude;
var novaPosicao = [novaLatitude, novaLongitude];
marcadorDinamico.setLatLng(novaPosicao);
var angulo = dados.orientacao;
posicaoAtual.lat = novaPosicao[0];
posicaoAtual.long = novaPosicao[1];
// Rotacionar o marcador para o angulo calculado
marcadorDinamico.setRotationAngle(angulo);
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
{id_mapa}.setView(novaPosicao, {id_mapa}.getZoom());*/
}});
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {{
// Converter latitudes e longitudes de graus para radianos
const latA = P1latitude * (Math.PI / 180.0);
const lonA = P1longitude * (Math.PI / 180.0);
const latB = P2latitude * (Math.PI / 180.0);
const lonB = P2longitude * (Math.PI / 180.0);
// Calcular a diferença de longitude
const deltaLon = lonB - lonA;
// Calcular a direção
const y = Math.sin(deltaLon) * Math.cos(latB);
const x = Math.cos(latA) * Math.sin(latB) - Math.sin(latA) * Math.cos(latB) * Math.cos(deltaLon);
const direcaoRadianos = Math.atan2(y, x);
// Converter a direção de radianos para graus
let direcaoGraus = direcaoRadianos * (180.0 / Math.PI);
// Normalizar a direção para que esteja no intervalo de 0 a 360 graus
direcaoGraus = (direcaoGraus + 360) % 360;
return direcaoGraus;
}}
</script>
"""
# Importar arquivos localmente
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/', 'folium/')
conteudo_html = conteudo_html.replace('https://code.jquery.com/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.2.0/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/gh/python-visualization/folium/folium/templates/', 'folium/')
# Inserir o script no conteúdo HTML
conteudo_atualizado = conteudo_html.replace('</html>', script_desenha_trajeotria + script_atualizacao_marcador + '</html>')
# Agora, reabrir o arquivo para escrita e sobrescrever o conteúdo com a versão atualizada
with open(caminho_completo_html, 'w') as arquivo_html:
arquivo_html.write(conteudo_atualizado)

View File

@ -1,123 +0,0 @@
import cv2
import numpy as np
from flask import Flask, Response, request
import json
import time
import threading
import sys
import os
import uuid
import paho.mqtt.client as mqtt
mqtt_client = mqtt.Client(f"client_weed_detector_{uuid.uuid4()}")
mqtt_client.connect("localhost", port=1883)
max_readings = int(sys.argv[1])
porta = sys.argv[2]
url = sys.argv[3]
arquivoSaida = sys.argv[4]
mostrar_linhas = sys.argv[5] == "1"
mqtt_topic = sys.argv[6]
app = Flask(__name__)
json_data = None
output_folder = 'Python/Output/'
# Função para detecção de objetos usando YOLO e salvar as coordenadas em um arquivo JSON
def detect_objects(conf_threshold, nms_threshold, _camera_index):
# Definir intervalos de cor verde no espaço HSV
lower_green = np.array([35, 50, 100]) # Valores de limite inferior (Hue, Saturation, Value)
upper_green = np.array([90, 255, 255]) # Valores de limite superior (Hue, Saturation, Value)
#cap = cv2.VideoCapture(_camera_index, cv2.CAP_DSHOW)
cap = cv2.VideoCapture(_camera_index, cv2.CAP_MSMF)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
readings = []
while True:
ret, frame = cap.read()
if not ret:
break
# Converter a imagem de BGR para HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# Criar uma máscara para os pixels verdes na faixa especificada
mask = cv2.inRange(hsv, lower_green, upper_green)
# Encontrar os contornos na máscara
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
current_readings = []
idx = 0 # Índice de detecção
for contour in contours:
area = cv2.contourArea(contour)
if area > 1000: # Filtrar áreas menores que um valor específico (ajuste conforme necessário)
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, f'Verde {idx}', (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# Salvar as informações da detecção
detection_info = {
'id': int(idx),
'descricao': 'Green',
'x': int(x),
'y': int(y),
'largura': int(w),
'altura': int(h),
'confianca': 1.0 # Neste caso, confiança fixa para detecções baseadas em cores
}
current_readings.append(detection_info)
idx += 1
global json_data
# Atualizar os resultados
timestamp = time.time()
json_data = {'timestamp': timestamp, 'x_max': width, 'y_max': height, 'objetos': current_readings}
readings.append(json_data)
mqtt_client.publish(mqtt_topic, json.dumps(json_data).encode('utf-8'))
if not os.path.exists(output_folder):
os.makedirs(output_folder)
if len(readings) > max_readings:
readings.pop(0)
with open(output_folder + arquivoSaida, 'w') as file:
json.dump(readings, file, indent=4)
# Convertendo frame para JPEG e enviando via Flask
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
# Esta função é para confirmar que script está pronto para execução
def send_script_ready():
mqtt_client.publish(mqtt_topic, "OK")
# Iniciar o servidor Flask em uma thread separada
def run_flask_server():
app.run(host='0.0.0.0', port=porta, threaded=True, debug=False)
@app.route('/' + url, methods=['GET'])
def video_feed():
conf_threshold = float(request.args.get('conf_threshold'))
nms_threshold = float(request.args.get('nms_threshold'))
camera_index = int(request.args.get('camera_index'))
return Response(detect_objects(conf_threshold, nms_threshold, camera_index), mimetype='multipart/x-mixed-replace; boundary=frame')
# Iniciar o servidor
if __name__ == '__main__':
# Cria trhead separada para informar que o script iniciou com sucesso
mqtt_thread = threading.Thread(target=send_script_ready)
mqtt_thread.start()
# Inicia o servidor Flask na thread principal
run_flask_server()

View File

@ -1,57 +0,0 @@
import os
import sys
# 🔹 Desativar logs do DepthAI
os.environ["DEPTHAI_LEVEL"] = "ERROR"
import depthai as dai
import json
# Obtém todos os dispositivos conectados
devices = dai.Device.getAllAvailableDevices()
device_list = []
for idx, device_info in enumerate(devices):
with dai.Device(device_info) as device:
try:
memory_usage = device.getDdrMemoryUsage()
memory_info = {
"remaining": memory_usage.remaining,
"total": memory_usage.total,
"used": memory_usage.used
}
except:
memory_info = None # Se houver erro, define como None
try:
temp = device.getChipTemperature()
temp_info = {
"css": temp.css,
"mss": temp.mss,
"upa": temp.upa,
"dss": temp.dss
}
except:
temp_info = None # Se houver erro, define como None
device_data = {
"index": idx,
"id": device_info.getMxId(), # ID do dispositivo
"name": device_info.name, # Nome do dispositivo
"state": device_info.state.name, # Estado do dispositivo
"usb_speed": str(device.getUsbSpeed().name) if hasattr(device, 'getUsbSpeed') else None, # Velocidade USB
"available_camera_sensors": [sensor.name for sensor in device.getConnectedCameras()], # Sensores de câmera disponíveis
"version": str(device.getDeviceInfo().protocol) if hasattr(device, 'getDeviceInfo') else None, # Versão do protocolo
"memory_usage": memory_info, # Uso de memória DDR
"temperature": temp_info, # Temperatura do chip
"bootloader_version": str(device.getBootloaderVersion()) if hasattr(device, 'getBootloaderVersion') else None, # Bootloader
"is_pipeline_running": device.isPipelineRunning() if hasattr(device, 'isPipelineRunning') else None # Pipeline rodando?
}
device_list.append(device_data)
# 🔹 Imprime apenas o JSON, sem logs extras
json_output = json.dumps(device_list, indent=4)
sys.stdout.write(json_output)
sys.stdout.flush()

View File

@ -1,235 +0,0 @@
import geopandas as gpd
import json
import sys
import folium
import requests
import re
import shutil
def internet_disponivel():
"""Verifica se há conexão com a internet."""
url = 'http://www.google.com/'
timeout = 5
try:
_ = requests.get(url, timeout=timeout)
return True
except requests.ConnectionError:
return False
# Argumentos e configurações iniciais
pathFiles = sys.argv[1]
fileName = sys.argv[2]
outputName = sys.argv[3]
outputMapName = sys.argv[4]
topico_gps = sys.argv[5]
topico_ruas = sys.argv[6]
topico_trajeto_dinamico = sys.argv[7]
mapa_dados_json = sys.argv[8]
pastaSaida = 'Python/Output/'
if mapa_dados_json == "0":
# Carregar o arquivo shapefile contendo a geometria
input_shapefile = pathFiles + fileName + '.shp'
data_geometry = gpd.read_file(input_shapefile)
# Converter o GeoDataFrame para GeoJSON e adicionar ao mapa
geojson_data = data_geometry.to_json()
# Calcular os limites (bounds) da geometria
minx, miny, maxx, maxy = data_geometry.total_bounds
center = [(miny + maxy) / 2, (minx + maxx) / 2]
# Salvar GEOJson
data_geometry.to_file(pastaSaida + outputName, driver='GeoJSON')
else:
# Lendo os dados JSON
with open(pathFiles + fileName + '.json', 'r', encoding='utf-8-sig') as f:
geojson_data = json.load(f)
# Obter os limites (bounds) dos dados GeoJSON
bounds = folium.GeoJson(geojson_data).get_bounds()
center = [(bounds[0][0] + bounds[1][0]) / 2, (bounds[0][1] + bounds[1][1]) / 2]
# Adicionando um ID sequencial a cada Feature
for idx, feature in enumerate(geojson_data["features"]):
# Adicionar uma propriedade 'id' para cada Feature começando do índice zero
feature['id'] = idx
# Copiando o arquivo JSON para a pasta de saída
shutil.copy(pathFiles + fileName + '.json', pastaSaida + outputName)
# Verificar se há conexão com a internet
if internet_disponivel():
m = folium.Map(location=center, zoom_start=12)
else:
m = folium.Map(location=center, zoom_start=12, tiles=None)
folium.GeoJson(geojson_data).add_to(m)
# Salvar o mapa interativo em um arquivo HTML
m.save(pastaSaida + outputMapName)
# Caminho completo para o arquivo HTML gerado
caminho_completo_html = pastaSaida + outputMapName
# Abrir o arquivo HTML para leitura e escrita
with open(caminho_completo_html, 'r+') as arquivo_html:
# Ler o conteúdo do arquivo
conteudo_html = arquivo_html.read()
# Usar expressão regular para encontrar o ID do mapa
padrao_id_mapa = re.compile(r'id="map_(.*?)"')
resultado_busca = padrao_id_mapa.search(conteudo_html)
# Verificar se encontrou um ID de mapa
if resultado_busca:
id_mapa = 'map_' + resultado_busca.group(1)
else:
raise ValueError("Não foi possível encontrar o ID do mapa no arquivo HTML.")
# Script para desenhar o trajo percorrido no mapa
script_marcadores_dinamicos = f"""
<script src="folium/mqtt.min.js"></script>
<script src="folium/leaflet.rotatedMarker.js"></script>
<script>
var marcadores = {{}};
var trajetos = {{}};
function criarMarcador(id, coordenadas, orientacao) {{
var marcador;
if (id == 1) {{
marcador = L.marker(coordenadas, {{}}).addTo({id_mapa});
var icon = L.AwesomeMarkers.icon(
{{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}}
);
marcador.setIcon(icon);
}}
else {{
var customIcon = L.icon({{
iconUrl: 'folium/images/position_marker.png',
iconSize: [32, 32],
iconAnchor: [16, 16],
popupAnchor: [0, -16]
}});
marcador = L.marker(coordenadas, {{ icon: customIcon }}).addTo({id_mapa});
marcador.setRotationAngle(orientacao);
}}
var popup = L.popup({{"maxWidth": "100%"}});
var html = $(`<div id="html_${{id}}" style="width: 100.0%; height: 100.0%;">Endereco: ${{id}}</div>`)[0];
popup.setContent(html);
marcador.bindPopup(popup)
marcadores[id] = marcador;
}}
function criarTrajeto(id) {{
trajetos[id] = L.geoJson(null, {{
style: function(feature) {{
return {{ color: 'red' }};
}}
}}).addTo({id_mapa});
}}
function atualizarMarcador(id, coordenadas, orientacao) {{
var marcador = marcadores[id];
if (marcador) {{
marcador.setLatLng(coordenadas);
marcador.setRotationAngle(orientacao);
}}
}}
function atualizarTrajeto(id, coordenadas) {{
var trajeto = trajetos[id];
if (trajeto) {{
var feature = trajeto.toGeoJSON().features.find(f => f.id === id);
if (feature) {{
var ultima = [0.0, 0.0];
if (feature.geometry.coordinates.length > 0) {{
ultima = feature.geometry.coordinates[feature.geometry.coordinates.length - 1];
}}
if (!saoCoordenadasIguais(ultima, coordenadas, 0.000001)) {{
feature.geometry.coordinates.push(coordenadas);
trajeto.clearLayers();
trajeto.addData(feature);
}}
}}
}}
}}
function saoCoordenadasIguais(coord1, coord2, tolerancia) {{
return Math.abs(coord1[0] - coord2[0]) < tolerancia && Math.abs(coord1[1] - coord2[1]) < tolerancia;
}}
function adicionarDados(id, coordenadas, orientacao, foco) {{
if (!marcadores[id]) {{
criarMarcador(id, coordenadas, orientacao);
criarTrajeto(id);
trajetos[id].addData({{
"type": "Feature",
"geometry": {{
"type": "LineString",
"coordinates": []
}},
"properties": {{"Dist1": 0.0, "Dist2": 0.0, "Id": id, "Length": 0.0, "Name": "Trajeto"}},
"id": id
}});
}} else {{
atualizarMarcador(id, coordenadas, orientacao);
atualizarTrajeto(id, [coordenadas[1], coordenadas[0]]);
}}
if (foco) {{
{id_mapa}.setView(coordenadas, {id_mapa}.getZoom());
}}
}}
const client = mqtt.connect('ws://localhost:9001'); // Use wss para conexao segura
client.on('connect', function () {{
console.log('Conectado ao broker MQTT');
client.subscribe('{topico_gps}', function (err) {{
if (!err) {{
console.log("Inscricao bem-sucedida no topico");
}} else {{
console.error("Falha na inscricao do topico", err);
}}
}});
}});
client.on('message', function (topic, message) {{
var dados = JSON.parse(message);
if (topic === '{topico_gps}') {{
var id = dados.id;
var novaLatitude = dados.latitude;
var novaLongitude = dados.longitude;
var novaPosicao = [novaLatitude, novaLongitude];
var orientacao = dados.orientacao;
var foco = dados.foco;
adicionarDados(id, novaPosicao, orientacao, foco);
}}
}});
</script>
"""
# Importar arquivos localmente
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/', 'folium/')
conteudo_html = conteudo_html.replace('https://code.jquery.com/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.2.0/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/gh/python-visualization/folium/folium/templates/', 'folium/')
# Inserir o script no conteúdo HTML
conteudo_atualizado = conteudo_html.replace('</html>', script_marcadores_dinamicos + '</html>')
# Agora, reabrir o arquivo para escrita e sobrescrever o conteúdo com a versão atualizada
with open(caminho_completo_html, 'w') as arquivo_html:
arquivo_html.write(conteudo_atualizado)

View File

@ -1,456 +0,0 @@
import geopandas as gpd
import json
import sys
import folium
import requests
import re
import shutil
def internet_disponivel():
"""Verifica se há conexão com a internet."""
url = 'http://www.google.com/'
timeout = 5
try:
_ = requests.get(url, timeout=timeout)
return True
except requests.ConnectionError:
return False
# Argumentos e configurações iniciais
pathFiles = sys.argv[1]
fileName = sys.argv[2]
outputName = sys.argv[3]
outputMapName = sys.argv[4]
topico_gps = sys.argv[5]
topico_ruas = sys.argv[6]
topico_trajeto_dinamico = sys.argv[7]
mapa_dados_json = sys.argv[8]
pastaSaida = 'Python/Output/'
if mapa_dados_json == "0":
# Carregar o arquivo shapefile contendo a geometria
input_shapefile = pathFiles + fileName + '.shp'
data_geometry = gpd.read_file(input_shapefile)
# Converter o GeoDataFrame para GeoJSON e adicionar ao mapa
geojson_data = data_geometry.to_json()
# Calcular os limites (bounds) da geometria
minx, miny, maxx, maxy = data_geometry.total_bounds
center = [(miny + maxy) / 2, (minx + maxx) / 2]
# Salvar GEOJson
data_geometry.to_file(pastaSaida + outputName, driver='GeoJSON')
else:
# Lendo os dados JSON
with open(pathFiles + fileName + '.json', 'r', encoding='utf-8-sig') as f:
geojson_data = json.load(f)
# Obter os limites (bounds) dos dados GeoJSON
bounds = folium.GeoJson(geojson_data).get_bounds()
center = [(bounds[0][0] + bounds[1][0]) / 2, (bounds[0][1] + bounds[1][1]) / 2]
# Adicionando um ID sequencial a cada Feature
for idx, feature in enumerate(geojson_data["features"]):
# Adicionar uma propriedade 'id' para cada Feature começando do índice zero
feature['id'] = idx
# Copiando o arquivo JSON para a pasta de saída
shutil.copy(pathFiles + fileName + '.json', pastaSaida + outputName)
# Verificar se há conexão com a internet
if internet_disponivel():
m = folium.Map(location=center, zoom_start=12)
else:
m = folium.Map(location=center, zoom_start=12, tiles=None)
folium.GeoJson(geojson_data).add_to(m)
# Salvar o mapa interativo em um arquivo HTML
m.save(pastaSaida + outputMapName)
# Caminho completo para o arquivo HTML gerado
caminho_completo_html = pastaSaida + outputMapName
# Abrir o arquivo HTML para leitura e escrita
with open(caminho_completo_html, 'r+') as arquivo_html:
# Ler o conteúdo do arquivo
conteudo_html = arquivo_html.read()
# Usar expressão regular para encontrar o ID do mapa
padrao_id_mapa = re.compile(r'id="map_(.*?)"')
resultado_busca = padrao_id_mapa.search(conteudo_html)
# Verificar se encontrou um ID de mapa
if resultado_busca:
id_mapa = 'map_' + resultado_busca.group(1)
else:
raise ValueError("Não foi possível encontrar o ID do mapa no arquivo HTML.")
# Usar expressão regular para encontrar o ID da Trajetoria
padrao_id_trajetoria = re.compile(r'var geo_json_(\w+)\s*=')
resultado_busca_trajetoria = padrao_id_trajetoria.search(conteudo_html)
# Verificar se encontrou um ID de trajetória
if resultado_busca_trajetoria:
id_trajetoria = 'geo_json_' + resultado_busca_trajetoria.group(1)
else:
# Adicionar mensagem de depuração para verificar o conteúdo do arquivo HTML
raise ValueError("Não foi possível encontrar o ID da trajetória no arquivo HTML.")
# Script para representar a trajetoria que o robô deve fazer até iniciar o trabalho
script_desenha_trajetoria_dinamica = f"""
<script>
function trajeto_dinamico_json_onEachFeature(feature, layer) {{
layer.on({{
}});
}};
var trajeto_dinamico_json = L.geoJson(null, {{
onEachFeature: trajeto_dinamico_json_onEachFeature,
style: function(feature) {{
return {{color: 'green'}};
}}
}}
);
function trajeto_dinamico_json_add (data) {{
trajeto_dinamico_json.addData(data);
}}
trajeto_dinamico_json_add({{"features": []}});
trajeto_dinamico_json.addTo({id_mapa});
function adicionarGeometriaDinamica(novaGeometria) {{
trajeto_dinamico_json.addData(novaGeometria);
}}
function adicionarCoordenadaDinamica(idGeometria, coordenadas) {{
var feature = trajeto_dinamico_json.toGeoJSON().features.find(f => f.id === idGeometria);
if (feature) {{
feature.geometry.coordinates = [];
coordenadas.forEach(x => feature.geometry.coordinates.push([x.longitude, x.latitude]));
var ft = feature;
trajeto_dinamico_json.clearLayers();
trajeto_dinamico_json.addData(ft);
}} else {{
console.log("Feature com ID " + idGeometria + " nao encontrada.");
}}
}}
adicionarGeometriaDinamica({{
"type": "Feature",
"geometry": {{
"type": "LineString",
"coordinates": []
}},
"properties": {{"Dist1": 0.0, "Dist2": 0.0, "Id": 1517, "Length": 21.724783283, "Name": "Projeto"}},
"id": "TjD"
}});
</script>
"""
# Script para desenhar o trajo percorrido no mapa
script_desenha_trajeotria = f"""
<script>
function trajeto_json_onEachFeature(feature, layer) {{
layer.on({{
}});
}};
var trajeto_json = L.geoJson(null, {{
onEachFeature: trajeto_json_onEachFeature,
style: function(feature) {{
return {{color: 'red'}};
}}
}}
);
function trajeto_json_add (data) {{
trajeto_json.addData(data);
}}
trajeto_json_add({{"features": []}});
trajeto_json.addTo({id_mapa});
function adicionarGeometria(novaGeometria) {{
trajeto_json.addData(novaGeometria);
}}
function saoCoordenadasIguais(coord1, coord2, tolerancia) {{
return Math.abs(coord1[0] - coord2[0]) < tolerancia && Math.abs(coord1[1] - coord2[1]) < tolerancia;
}}
function adicionarCoordenada(idGeometria, novaCoordenada) {{
var feature = trajeto_json.toGeoJSON().features.find(f => f.id === idGeometria);
if (feature) {{
var ultima = [0.0,0.0];
if (feature.geometry.coordinates.length > 0) {{
ultima = feature.geometry.coordinates[feature.geometry.coordinates.length - 1];
}}
if (!saoCoordenadasIguais(ultima, novaCoordenada, 0.000001)) {{
feature.geometry.coordinates.push(novaCoordenada);
var ft = feature;
trajeto_json.clearLayers();
trajeto_json.addData(ft);
}}
}} else {{
console.log("Feature com ID " + idGeometria + " não encontrada.");
}}
}}
adicionarGeometria({{
"type": "Feature",
"geometry": {{
"type": "LineString",
"coordinates": []
}},
"properties": {{"Dist1": 0.0, "Dist2": 0.0, "Id": 1517, "Length": 21.724783283, "Name": "Projeto"}},
"id": "Tj"
}});
</script>
"""
# Script JavaScript para adicionar ao HTML, com o ID do mapa substituído
script_atualizacao_marcador = f"""
<script src="folium/mqtt.min.js"></script>
<script src="folium/leaflet.rotatedMarker.js"></script>
<script>
let posicaoAtualEquipamento = {{
lat: 0,
long: 0
}};
let posicaoAtualBase = {{
lat: 0,
long: 0
}};
var customIcon = L.icon({{
iconUrl: 'folium/images/position_marker.png',
iconSize: [32, 32],
iconAnchor: [16, 16],
popupAnchor: [0, -16]
}});
var marcadorEquipamento = L.marker([0, 0], {{
icon: customIcon
}}).addTo({id_mapa});
var marcadorBase = L.marker([0, 0], {{}}).addTo({id_mapa});
var icon = L.AwesomeMarkers.icon(
{{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}}
);
marcadorBase.setIcon(icon);
// Conectar ao broker MQTT
const client = mqtt.connect('ws://localhost:9001'); // Use wss para conexão segura
// Quando conectado, inscreva-se no tópico desejado
client.on('connect', function () {{
console.log('Conectado ao broker MQTT');
// Inscrever-se no tópico
client.subscribe('{topico_gps}', function (err) {{
if (!err) {{
console.log("Inscricao bem-sucedida no topico");
}} else {{
console.error("Falha na inscricao do topico", err);
}}
}});
client.subscribe('{topico_trajeto_dinamico}', function (err) {{
if (!err) {{
console.log("Inscricao bem-sucedida no topico");
}} else {{
console.error("Falha na inscricao do topico", err);
}}
}});
client.subscribe('{topico_ruas}', function (err) {{
if (!err) {{
console.log("Inscricao bem-sucedida no topico");
}} else {{
console.error("Falha na inscricao do topico", err);
}}
}});
}});
// Lidar com mensagens recebidas para o topico inscrito
client.on('message', function (topic, message) {{
var dados = JSON.parse(message);
if (topic === "{topico_trajeto_dinamico}") {{
adicionarCoordenadaDinamica("TjD", dados); // "Tj" é o ID da feature no GeoJSON
}}
else if (topic === "{topico_gps}") {{
// A mensagem e um Buffer, converta para string ou objeto conforme necessario
//console.log(`Mensagem recebida no topico '${{topic}}': ${{message.toString()}}`);
var id = dados.id;
var novaLatitude = dados.latitude;
var novaLongitude = dados.longitude;
var novaPosicao = [novaLatitude, novaLongitude];
var orientacao = dados.orientacao;
var foco = dados.foco;
if (id == 1) {{
marcadorBase.setLatLng(novaPosicao);
// Calcular angulo de rotacao
var angulo = dados.orientacao;
posicaoAtualBase.lat = novaPosicao[0];
posicaoAtualBase.long = novaPosicao[1];
// Rotacionar o marcador para o angulo calculado
marcadorBase.setRotationAngle(angulo);
}}
else {{
marcadorEquipamento.setLatLng(novaPosicao);
// Calcular angulo de rotacao
var angulo = dados.orientacao;
posicaoAtualEquipamento.lat = novaPosicao[0];
posicaoAtualEquipamento.long = novaPosicao[1];
// Rotacionar o marcador para o angulo calculado
marcadorEquipamento.setRotationAngle(angulo);
adicionarCoordenada("Tj", [novaLongitude, novaLatitude]);
}}
if (foco) {{
{id_mapa}.setView(novaPosicao, {id_mapa}.getZoom());
}}
}}
else if (topic === '{topico_ruas}') {{
var selecionadas = dados;
if (selecionadas.length != RuasSelecionadas.length) {{
atualizarSelecaoRuas(selecionadas);
}}
}}
}});
// Função para atualizar a seleção das ruas e mudar a cor no mapa
function atualizarSelecaoRuas(selecionadas) {{
selecionadas = JSON.parse(selecionadas);
RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : [];
{id_trajetoria}.eachLayer(function (layer) {{
if (RuasSelecionadas.includes(parseInt(layer.feature.id))) {{
layer.setStyle({{ color: 'blue' }});
}} else {{
layer.setStyle({{ color: '#3388ff' }}); // Cor original
}}
}});
}}
function calcularOrientacao(P1latitude, P1longitude, P2latitude, P2longitude) {{
// Converter latitudes e longitudes de graus para radianos
const latA = P1latitude * (Math.PI / 180.0);
const lonA = P1longitude * (Math.PI / 180.0);
const latB = P2latitude * (Math.PI / 180.0);
const lonB = P2longitude * (Math.PI / 180.0);
// Calcular a diferença de longitude
const deltaLon = lonB - lonA;
// Calcular a direção
const y = Math.sin(deltaLon) * Math.cos(latB);
const x = Math.cos(latA) * Math.sin(latB) - Math.sin(latA) * Math.cos(latB) * Math.cos(deltaLon);
const direcaoRadianos = Math.atan2(y, x);
// Converter a direção de radianos para graus
let direcaoGraus = direcaoRadianos * (180.0 / Math.PI);
// Normalizar a direção para que esteja no intervalo de 0 a 360 graus
direcaoGraus = (direcaoGraus + 360) % 360;
return direcaoGraus;
}}
</script>
"""
# Primeiro, vamos definir o novo trecho de JavaScript que você quer adicionar
funcao_define_ruas_selecionadas = f"""
var trajetosSelecionados = {{}};
var RuasSelecionadas = [];
function enviarSelecaoParaServidor() {{
client.publish("{topico_ruas}", JSON.stringify(RuasSelecionadas), function (err) {{
/*if (!err) {{
console.log("Mensagem publicada com sucesso");
}} else {{
console.error("Falha ao publicar mensagem", err);
}}*/
}});
}}
"""
# Código para inserir dentro do layer.on({})
codigo_layer_on = """
click: function(e) {
// Verificar se o trajeto está selecionado
if (trajetosSelecionados[feature.id]) {
// Se está selecionado, reverta para a cor original e remova da seleção
e.target.setStyle({
color: '#3388ff' // Cor original
});
delete trajetosSelecionados[feature.id];
RuasSelecionadas.splice(RuasSelecionadas.indexOf(feature.id), 1);
} else {
// Se não está selecionado, mude a cor para indicar seleção e adicione ao objeto de seleção
e.target.setStyle({
color: 'blue' // Cor de seleção
});
trajetosSelecionados[feature.id] = true;
RuasSelecionadas.push(feature.id);
}
enviarSelecaoParaServidor();
}/*, mouseover: function(e) {
// Mostrar tooltip com o ID da geometria quando o mouse passar sobre a linha
var tooltip = L.tooltip({
direction: 'auto', // Leaflet determina a melhor posição
sticky: true // Faz com que o tooltip siga o cursor do mouse
})
.setContent('Rua ' + feature.id); // Define o conteúdo do tooltip
// Abre o tooltip na posição atual do mouse
var latLng = e.latlng;
tooltip.setLatLng(latLng);
layer.bindTooltip(tooltip).openTooltip(latLng);
}*/
"""
# Importar arquivos localmente
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/', 'folium/')
conteudo_html = conteudo_html.replace('https://code.jquery.com/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.2.0/css/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/', 'folium/')
conteudo_html = conteudo_html.replace('https://cdn.jsdelivr.net/gh/python-visualization/folium/folium/templates/', 'folium/')
# Inserir o script no conteúdo HTML
conteudo_atualizado = conteudo_html.replace('</html>', script_desenha_trajeotria + script_desenha_trajetoria_dinamica + script_atualizacao_marcador + '</html>')
# Encontrar a posição onde inserir o novo trecho de JavaScript
indice_inicio_funcao = conteudo_atualizado.find("function geo_json_")
# Inserir o novo trecho de JavaScript antes da função
conteudo_atualizado = conteudo_atualizado[:indice_inicio_funcao] + funcao_define_ruas_selecionadas + conteudo_atualizado[indice_inicio_funcao:]
# Agora, para adicionar o código dentro do layer.on({})
# Você precisa encontrar o local exato. Isso pode ser um pouco mais complicado, pois pode haver várias ocorrências.
# Uma abordagem seria encontrar a primeira ocorrência após o seu trecho inserido anteriormente.
indice_layer_on = conteudo_atualizado.find("layer.on({", indice_inicio_funcao)
# Verificar se encontrou o layer.on({})
if indice_layer_on != -1:
# Encontrar a posição do fechamento do layer.on({})
indice_fechamento = conteudo_atualizado.find("});", indice_layer_on) # +1 para incluir a abertura da chave
# Inserir o código dentro do layer.on({})
conteudo_atualizado = conteudo_atualizado[:indice_fechamento] + codigo_layer_on + conteudo_atualizado[indice_fechamento:]
# Agora, reabrir o arquivo para escrita e sobrescrever o conteúdo com a versão atualizada
with open(caminho_completo_html, 'w') as arquivo_html:
arquivo_html.write(conteudo_atualizado)

View File

@ -1,382 +0,0 @@
# MPC com MQTT, simulação e visualização da trajetória
import numpy as np
import paho.mqtt.client as mqtt
import json
from enum import Enum
#import matplotlib.pyplot as plt
class StatusCarroMapa(Enum):
Parado = 0
EntrandoRua = 1
CaminhandoRua = 2
SaindoRua = 3
Manobrando = 4
Direcionando = 5
class TipoMovimentoDirecional(Enum):
RodasDianteiras = 0
RodasTraseiras = 1
RotacionarNoEixo = 2
MovimentoArco = 3
MovimentoLateral = 4
MovimentoDiagonal = 5
Diagnostico = 6
class TipoPontoRua(Enum):
Indefinido = -1
PosicaoRobo = 0
LigacaoEntrada = 1
BordaEntrada = 2
Rua = 3
BordaSaida = 4
LigacaoSaida = 5
CruvaEntreCorredores = 6
Desvio = 7
topico_comando = "mpc/comando"
topico_posicao = "mpc/posicao"
topico_rota = "mpc/rota"
raio_terra = 6371000
# Parâmetros do MPC
angulo_max_graus = 30.0
velocidade_min = 0.4
velocidade_max = 1.9
distancia_entre_eixos = 0.92
horizonte = 4.0
# Estado compartilhado
trajetoria_latlon = []
pontos_info = []
visitados_execucao = []
lat0, lon0 = None, None
# Visualização
#plt.ion()
#fig, ax = plt.subplots()
# Funções matemáticas auxiliares
def latlon_to_xy(lat, lon, lat0, lon0):
global raio_terra
R = raio_terra
dlat = np.radians(lat - lat0)
dlon = np.radians(lon - lon0)
x = R * dlon * np.cos(np.radians((lat + lat0) / 2))
y = R * dlat
return x, y
def calcular_orientacao(p1, p2):
dx = p2[0] - p1[0]
dy = p2[1] - p1[1]
orient = np.arctan2(dy, dx) - np.radians(90)
return (orient + np.pi) % (2 * np.pi) - np.pi
def calcular_omega(v, angulo_rad, tipo):
tan_delta = np.tan(angulo_rad)
if abs(angulo_rad) < 0.01:
return 0.0
k = 1.35 + 0.5 * np.exp(-abs(np.degrees(angulo_rad)) / 15.0)
R = distancia_entre_eixos / tan_delta
if tipo == TipoMovimentoDirecional.MovimentoArco:
R *= 0.5
R *= k # Aplica fator de correção
return v / R
def proximo_nao_visitado(visitados):
for i, v in enumerate(visitados):
if not v:
return i
return len(visitados) - 1
def corrigir_pontos_visitados(x, y, pontos_visitados, limite_max_avanço=5):
for idx, ponto in enumerate(pontos_info):
if pontos_visitados[idx]:
continue
pos = ponto["xy"]
margem = ponto.get("distanciaMargem", 0.7)
dist = np.linalg.norm([x - pos[0], y - pos[1]])
if dist < margem:
for i in range(idx + 1):
pontos_visitados[i] = True
return idx
if idx > 0 and not pontos_visitados[idx - 1] and idx >= limite_max_avanço:
break
return proximo_nao_visitado(pontos_visitados)
def calcular_melhor_candidato(x, y, theta, visitados_sim, contexto, comando_anterior):
idx_alvo = corrigir_pontos_visitados(x, y, visitados_sim)
if idx_alvo >= len(pontos_info):
return 0.0, TipoMovimentoDirecional.RodasDianteiras
ponto_info = pontos_info[idx_alvo]
tipo_ponto = TipoPontoRua(ponto_info.get("tipo"))
ponto_alvo = ponto_info["xy"]
if np.linalg.norm([x - ponto_alvo[0], y - ponto_alvo[1]]) < ponto_info.get("distanciaMargem", 0.7):
visitados_sim[idx_alvo] = True
idx_alvo = corrigir_pontos_visitados(x, y, visitados_sim)
if idx_alvo >= len(pontos_info):
return 0.0, TipoMovimentoDirecional.RodasDianteiras
ponto_info = pontos_info[idx_alvo]
tipo_ponto = TipoPontoRua(ponto_info.get("tipo"))
ponto_alvo = ponto_info["xy"]
orient = calcular_orientacao((x, y), ponto_alvo)
erro_ori = abs((orient - theta + np.pi) % (2 * np.pi) - np.pi)
delta_theta = ((-orient) - theta + np.pi) % (2 * np.pi) - np.pi
angulo = np.clip(delta_theta, -np.radians(angulo_max_graus), np.radians(angulo_max_graus))
# Pesos de custo base
peso_erro_pos, peso_erro_ori, peso_suavidade, peso_movimento, tipos_validos = calcular_pesos_movimento(contexto, erro_ori)
melhor_custo = float('inf')
melhor_tipo = TipoMovimentoDirecional.RodasDianteiras
melhor_angulo = angulo
for tipo in tipos_validos:
omega = calcular_omega(1.0, angulo, tipo)
x_sim = x + 1.0 * np.cos(theta) * 0.2
y_sim = y + 1.0 * np.sin(theta) * 0.2
theta_sim = theta + omega * 0.2
erro_pos = np.linalg.norm([x_sim - ponto_alvo[0], y_sim - ponto_alvo[1]])
orient_sim = calcular_orientacao((x_sim, y_sim), ponto_alvo)
erro_ori_sim = abs((orient_sim - theta_sim + np.pi) % (2 * np.pi) - np.pi)
vetor_alvo = np.array(ponto_alvo) - np.array([x_sim, y_sim])
vetor_alvo_norm = vetor_alvo / np.linalg.norm(vetor_alvo)
vetor_movel = np.array([np.cos(theta_sim), np.sin(theta_sim)])
cos_angulo = np.dot(vetor_alvo_norm, vetor_movel)
penalidade_orientacao = 9999 if cos_angulo < 0 else 0
delta_angulo = abs(np.degrees(angulo) - comando_anterior.get("angulo", 0))
custo_pos = erro_pos * peso_erro_pos
custo_ori = erro_ori_sim * peso_erro_ori
custo_mov = peso_movimento[tipo]
custo_suavidade = delta_angulo * peso_suavidade
custo = custo_pos + custo_ori + penalidade_orientacao + custo_mov + custo_suavidade
if custo < melhor_custo:
melhor_custo = custo
melhor_tipo = tipo
melhor_angulo = angulo
return melhor_angulo, melhor_tipo.value
def calcular_pesos_movimento(contexto, erro_ori):
# Lista de tipos sempre permitidos
tipos_validos = [
TipoMovimentoDirecional.RodasDianteiras,
TipoMovimentoDirecional.MovimentoArco
]
peso_erro_pos = 1.2
peso_erro_ori = 2.5
peso_suavidade = 0.25
custo_movimento = {
TipoMovimentoDirecional.RodasDianteiras: 0.5,
TipoMovimentoDirecional.MovimentoArco: 1.0
}
status = StatusCarroMapa(contexto.get("StatusCarro", 0))
dentro = contexto.get("DentroCorredor", False)
manobrando = contexto.get("ManobrandoEntreRuas", False)
velocidade = contexto.get("Velocidade", velocidade_min)
# Penalidade proporcional à velocidade
penalidade_por_velocidade = velocidade / velocidade_max
if dentro and erro_ori < np.radians(10):
custo_movimento[TipoMovimentoDirecional.MovimentoArco] = 5.0 # Desencoraja fortemente
custo_movimento[TipoMovimentoDirecional.RodasDianteiras] = 0.1 + penalidade_por_velocidade
elif manobrando or status in [ StatusCarroMapa.EntrandoRua, StatusCarroMapa.SaindoRua, StatusCarroMapa.Manobrando ]:
custo_movimento[TipoMovimentoDirecional.MovimentoArco] = 0.2
custo_movimento[TipoMovimentoDirecional.RodasDianteiras] = 2.0 + penalidade_por_velocidade
elif dentro:
custo_movimento[TipoMovimentoDirecional.MovimentoArco] = 2.0 + (erro_ori / np.radians(angulo_max_graus)) * 2.0
custo_movimento[TipoMovimentoDirecional.RodasDianteiras] = 0.5 + penalidade_por_velocidade
else:
custo_movimento[TipoMovimentoDirecional.MovimentoArco] = 1.0
custo_movimento[TipoMovimentoDirecional.RodasDianteiras] = 0.5 + penalidade_por_velocidade
return peso_erro_pos, peso_erro_ori, peso_suavidade, custo_movimento, tipos_validos
def processar_mpc_old(pos_lat, pos_lon, theta, velocidade, dt, contexto):
#print(f"Posicao recebida: Velocidade={velocidade}, dt={dt}, Orientacao={np.degrees(theta)}")
global pontos_info, visitados_execucao, lat0, lon0, raio_terra
if not pontos_info:
return None
x, y = latlon_to_xy(pos_lat, pos_lon, lat0, lon0)
visitados_sim = visitados_execucao.copy()
sim = [(x, y)]
x_temp, y_temp, theta_temp = x, y, theta
for _ in range(horizonte):
angulo_mpc, tipo_mpc = calcular_melhor_candidato(x_temp, y_temp, theta_temp, visitados_sim, contexto)
omega = calcular_omega(velocidade, angulo_mpc, TipoMovimentoDirecional(tipo_mpc))
theta_plot = (-theta_temp) + np.radians(90)
x_temp += velocidade * np.cos(theta_plot) * dt
y_temp += velocidade * np.sin(theta_plot) * dt
theta_temp += omega * dt
sim.append((x_temp, y_temp))
# Converte os pontos simulados para lat/lon
simulacao_latlon = []
for sx, sy in sim:
dlat = sy / raio_terra
dlon = sx / (raio_terra * np.cos(np.radians(lat0)))
lat = lat0 + np.degrees(dlat)
lon = lon0 + np.degrees(dlon)
simulacao_latlon.append({"latitude": lat, "longitude": lon})
#ax.clear()
#if pontos_info:
# tx, ty = zip(*[p["xy"] for p in pontos_info])
# ax.plot(tx, ty, 'r.-', label="Trajetória alvo")
#if sim:
# sx, sy = zip(*sim)
# ax.plot(sx, sy, 'g:', label="Previsão MPC")
#ax.plot(sim[0][0], sim[0][1], 'bo', label="Posição atual")
#ax.set_title("Visualização MPC (tempo real)")
#ax.set_xlabel("X (m)")
#ax.set_ylabel("Y (m)")
#ax.axis("equal")
#ax.legend()
#plt.pause(0.001)
angulo_final, tipo_final = calcular_melhor_candidato(sim[0][0], sim[0][1], theta, visitados_execucao, contexto)
#print(f"angulo: {np.degrees(angulo_final)}, tipo: {tipo_final}")
return {
"angulo": np.degrees(angulo_final),
"tipo": tipo_final,
"simulacao": simulacao_latlon
}
def processar_mpc(pos_lat, pos_lon, theta, velocidade, dt, contexto, comando_anterior=None):
global pontos_info, visitados_execucao, lat0, lon0, raio_terra
if not pontos_info:
return None
x, y = latlon_to_xy(pos_lat, pos_lon, lat0, lon0)
visitados_sim = visitados_execucao.copy()
sim = [(x, y)]
x_temp, y_temp, theta_temp = x, y, theta
atraso_simulado_passos = 2 # Delay estimado de resposta do robô
if velocidade * dt > 0:
pontos_horizonte = int(horizonte / (velocidade * dt))
else:
pontos_horizonte = 10
pontos_horizonte = max(10, min(pontos_horizonte, 120)) # entre 10 e 120 pontos
for i in range(pontos_horizonte):
if i < atraso_simulado_passos and comando_anterior:
angulo_mpc = np.radians(comando_anterior["angulo"])
tipo_mpc = TipoMovimentoDirecional(comando_anterior["tipo"])
else:
angulo_mpc, tipo_mpc = calcular_melhor_candidato(x_temp, y_temp, theta_temp, visitados_sim, contexto, comando_anterior)
tipo_mpc = TipoMovimentoDirecional(tipo_mpc)
omega = calcular_omega(velocidade, angulo_mpc, tipo_mpc)
# Simula a movimentação usando theta com base rotacionada (-theta + 90)
theta_plot = (-theta_temp) + np.radians(90)
x_temp += velocidade * np.cos(theta_plot) * dt
y_temp += velocidade * np.sin(theta_plot) * dt
theta_temp += omega * dt
sim.append((x_temp, y_temp))
# Converte os pontos simulados para lat/lon
simulacao_latlon = []
for sx, sy in sim:
dlat = sy / raio_terra
dlon = sx / (raio_terra * np.cos(np.radians(lat0)))
lat = lat0 + np.degrees(dlat)
lon = lon0 + np.degrees(dlon)
simulacao_latlon.append({"latitude": lat, "longitude": lon})
# Calcula o novo melhor candidato, já considerando o novo estado do robô
angulo_final, tipo_final = calcular_melhor_candidato(sim[0][0], sim[0][1], theta, visitados_execucao, contexto, comando_anterior)
return {
"angulo": np.degrees(angulo_final),
"tipo": tipo_final,
"simulacao": simulacao_latlon
}
def on_connect(client, userdata, flags, rc):
#print("Conectado ao MQTT")
client.subscribe(topico_rota)
client.subscribe(topico_posicao)
client.publish(topico_comando, "OK")
def on_message(client, userdata, msg):
global trajetoria_latlon, pontos_info, visitados_execucao, lat0, lon0, angulo_max_graus, distancia_entre_eixos, horizonte, velocidade_max, velocidade_min
if (not msg.payload):
return
try:
dados = json.loads(msg.payload.decode())
except json.JSONDecodeError as e:
print(f"Erro ao decodificar JSON em '{msg.topic}': {e}")
return
if msg.topic == topico_rota:
trajetoria_latlon = dados["pontos"]
angulo_max_graus = dados["angulo_max_graus"]
velocidade_min = dados["velocidade_min"]
velocidade_max = dados["velocidade_max"]
distancia_entre_eixos = dados["distancia_entre_eixos"]
horizonte = dados["horizonte"]
lat0, lon0 = trajetoria_latlon[0]["lat"], trajetoria_latlon[0]["lon"]
pontos_info = []
for p in trajetoria_latlon:
x, y = latlon_to_xy(p["lat"], p["lon"], lat0, lon0)
ponto = {
"xy": (x, y),
"tipo": p.get("tipo", 3),
"distanciaMargem": p.get("distanciaMargem", 0.7)
}
pontos_info.append(ponto)
visitados_execucao = [False] * len(pontos_info)
print(f"Trajetória recebida com: Pontos={len(pontos_info)}, angulo_max={angulo_max_graus}, vel_min={velocidade_min}, vel_max={velocidade_max}, dist_eixos={distancia_entre_eixos}, horizonte={horizonte}")
elif msg.topic == topico_posicao:
lat = dados["lat"]
lon = dados["lon"]
theta = np.radians(dados["theta"])
velocidade = dados["velocidade"]
dt = dados["dt"]
comando_anterior = dados.get("comando_anterior", None)
# Novo: contexto completo (com valores padrão caso não venha)
contexto = dados.get("contexto", {})
contexto.setdefault("StatusCarro", 0)
contexto.setdefault("DentroCorredor", False)
contexto.setdefault("ManobrandoEntreRuas", False)
contexto.setdefault("Velocidade", velocidade) # já aproveita
comando = processar_mpc(lat, lon, theta, velocidade, dt, contexto, comando_anterior)
if comando:
client.publish(topico_comando, json.dumps(comando))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("localhost", 1883, 60)
#print("Aguardando trajetória e posição...")
client.loop_forever()

View File

@ -1,184 +0,0 @@
import torch
from PIL import Image
import torchvision.transforms as T
import numpy as np
import cv2
import json
import time
import io
import paho.mqtt.client as mqtt
import uuid
import sys
import os
import base64
# Parâmetros de entrada
output_folder = 'Python/Output/'
mqtt_send_topic = sys.argv[1] # Tópico para receber o bitmap
mqtt_receive_topic = sys.argv[2] # Tópico para enviar o resultado
model_folder = sys.argv[3] # Pasta do modelo
script_version = sys.argv[4] # Versao do script
# Configurações MQTT
mqtt_client = mqtt.Client(f"client_street_detector_{uuid.uuid4()}")
mqtt_client.connect("localhost", port=1883)
script_dir = os.path.dirname(__file__) # Obtém o diretório onde o script está localizado
parent_dir = os.path.dirname(script_dir) # Obtém o diretório pai (Python/)
sys.path.append(parent_dir)
# Supondo que você tenha a estrutura do repositório e o módulo `network` conforme descrito no README
from Models.deeplabv3plus.modeling import deeplabv3plus_resnet50 as deeplabv3_model
# Configurações Iniciais
NUM_CLASSES = 4 # Pascal VOC possui 3 classes + 1 para o fundo
OUTPUT_STRIDE = 16 # Valor comum para DeepLab
MODEL_PATH = model_folder + "model-" + script_version + '.pth' # Caminho para o modelo pré-treinado
# Função para carregar o modelo
def load_model(model_path):
model = deeplabv3_model(num_classes=NUM_CLASSES, output_stride=OUTPUT_STRIDE)
model.load_state_dict(torch.load(model_path), strict=False)
model.eval() # Modo de avaliação
return model
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Carregar o modelo
model = load_model(MODEL_PATH)
model.to(device)
# Função para processar o frame
def segment_frame(frame):
image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
transform = T.Compose([
T.Resize(520),
T.ToTensor(),
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
input_tensor = transform(image).unsqueeze(0).to(device)
with torch.no_grad():
output = model(input_tensor)
output_predictions = output.max(1)[1].squeeze().detach().cpu().numpy()
return output_predictions
# Função para ler o mapa de rótulos
def read_labelmap(path):
label_colors = []
class_names = []
with open(path, 'r') as file:
for line in file.readlines():
if line.startswith('#'):
continue
parts = line.strip().split(':')
if len(parts) >= 2:
label = parts[0].strip()
color = tuple(map(int, parts[1].split(',')))
class_names.append(label)
label_colors.append(color)
return np.array(label_colors), class_names
# Função para gerar o JSON de saída
def generate_json(image):
label_colors, class_names = read_labelmap(model_folder + "labelmap-" + script_version + ".txt")
detected_classes = set(np.unique(image))
timestamp = time.time()
json_data = {'timestamp': timestamp, 'Classes': []}
for l in detected_classes:
if l < len(label_colors):
class_entry = {'Classe': class_names[l], 'Contornos': []}
mask = (image == l).astype(np.uint8) * 255
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
if contour.size > 0:
for point in contour:
class_entry['Contornos'].append(point.squeeze().tolist())
json_data['Classes'].append(class_entry)
return json_data
# Função para aplicar o overlay da segmentação no frame original
def apply_segmentation_overlay(frame, output_predictions):
label_colors, class_names = read_labelmap(model_folder + "labelmap-" + script_version + ".txt")
nc = len(label_colors)
height, width, _ = frame.shape
overlay = np.zeros((height, width, 3), dtype=np.uint8)
output_predictions_resized = cv2.resize(output_predictions, (width, height), interpolation=cv2.INTER_NEAREST)
for l in np.unique(output_predictions_resized):
if l < nc:
mask = output_predictions_resized == l
overlay[mask] = label_colors[l]
overlayed_frame = cv2.addWeighted(frame, 0.6, overlay, 0.4, 0)
return overlayed_frame
# Função para converter imagem para base64
def image_to_base64(image):
_, buffer = cv2.imencode('.jpg', image)
image_base64 = base64.b64encode(buffer).decode('utf-8')
return image_base64
# Função para decodificar a imagem bitmap recebida via MQTT
def decode_bitmap_message(payload):
image_stream = io.BytesIO(payload)
image = Image.open(image_stream)
return np.array(image)
# Função chamada quando uma mensagem MQTT é recebida
def on_message(client, userdata, message):
print(f"Mensagem recebida no tópico {message.topic}")
try:
bitmap = decode_bitmap_message(message.payload)
output_predictions = segment_frame(bitmap)
# Gerar JSON das classes detectadas
json_data = generate_json(output_predictions)
# Aplicar segmentação ao bitmap
segmented_bitmap = apply_segmentation_overlay(bitmap, output_predictions)
# Converter o bitmap segmentado em base64
segmented_bitmap_base64 = image_to_base64(segmented_bitmap)
# Adicionar o bitmap segmentado no JSON
json_data['segmented_image'] = segmented_bitmap_base64
# Enviar JSON e bitmap segmentado via MQTT
mqtt_client.publish(mqtt_send_topic, json.dumps(json_data).encode('utf-8')) # Enviar JSON
print(f"Segmentação processada e enviada para {mqtt_send_topic}")
except Exception as e:
print(f"Erro ao processar a imagem: {e}")
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Conectado ao MQTT com sucesso!")
else:
print(f"Falha ao conectar ao MQTT: {rc}")
def on_disconnect(client, userdata, rc):
print(f"Desconectado do MQTT: {rc}")
# Configurações do MQTT
mqtt_client.on_connect = on_connect
mqtt_client.on_disconnect = on_disconnect
mqtt_client.on_message = on_message
mqtt_client.subscribe(mqtt_receive_topic)
mqtt_client.publish(mqtt_send_topic, "OK")
mqtt_client.loop_forever()
# Loop principal aguardando mensagens MQTT
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("Finalizando script...")
finally:
mqtt_client.loop_stop()

View File

@ -1,607 +0,0 @@
import depthai as dai
import cv2
import numpy as np
import time
import json
import sys
import threading
import paho.mqtt.client as mqtt
import uuid
from flask import Flask, Response
# 🔹 Configurações MQTT
mqtt_client = mqtt.Client(f"client_oak_d_lite_{uuid.uuid4()}")
mqtt_client.connect("localhost", port=1883)
# 🔹 Parâmetros de entrada
output_folder = 'Python/Output/'
mqtt_topic = sys.argv[1] # Tópico MQTT para envio dos dados
porta = int(sys.argv[2]) # Porta do Flask
url_rgb = sys.argv[3] # URL do vídeo RGB
url_heatmap = sys.argv[4] # URL do vídeo do Heatmap
max_readings = int(sys.argv[5])
camera_index = int(sys.argv[6])
# 🔹 Flask App
app = Flask(__name__)
# Função para calcular a moda de um array unidimensional
def calc_mode(values):
# Arredonda os valores para eliminar pequenas variações
rounded = np.round(values, 0)
vals, counts = np.unique(rounded, return_counts=True)
return vals[np.argmax(counts)]
# Configuração de filtros
NUM_FRAMES_SMOOTH = 5 # Número de frames para suavização temporal
SMOOTH_KERNEL = (5, 5) # Tamanho do kernel para suavização espacial
OUTLIER_THRESHOLD = 50 # Limiar para remoção de outliers
depth_buffer = [] # Buffer para armazenar os últimos frames de profundidade
# Suavização Temporal
def smooth_depth(depth_frame):
global depth_buffer
if len(depth_buffer) >= NUM_FRAMES_SMOOTH:
depth_buffer.pop(0) # Remove o frame mais antigo
depth_buffer.append(depth_frame) # Adiciona o novo frame
return np.mean(depth_buffer, axis=0).astype(np.uint16) # Retorna a média
# Suavização Espacial (Filtro Gaussiano)
def gaussian_smooth(depth_frame):
return cv2.GaussianBlur(depth_frame, SMOOTH_KERNEL, 0)
# Filtro de Mediana para Remover Ruídos
def median_filter(depth_frame):
return cv2.medianBlur(depth_frame, 5)
# Remover Outliers (Saltos Extremos)
def remove_outliers(depth_frame):
depth_median = cv2.medianBlur(depth_frame, 5)
diff = np.abs(depth_frame - depth_median)
depth_frame[diff > OUTLIER_THRESHOLD] = depth_median[diff > OUTLIER_THRESHOLD]
return depth_frame
# Aplicar filtros no depthFrame
def apply_depth_filters(depth_frame):
if filtro1:
depth_frame = smooth_depth(depth_frame) # 1. Suavização Temporal
if filtro2:
depth_frame = gaussian_smooth(depth_frame) # 2. Suavização Espacial
if filtro3:
depth_frame = median_filter(depth_frame) # 3. Filtro de Mediana
if filtro4:
depth_frame = remove_outliers(depth_frame) # 4. Remoção de Outliers
return depth_frame
# Função para gerar o mapa de calor da profundidade
def generate_heatmap(depth_frame):
normalized_depth = cv2.normalize(depth_frame, None, 0, 255, cv2.NORM_MINMAX)
heatmap = cv2.applyColorMap(normalized_depth.astype(np.uint8), cv2.COLORMAP_JET)
return heatmap
# ───────────────────────────────────────────────
# Configurações da câmera e das matrizes
# ───────────────────────────────────────────────
RGB_WIDTH, RGB_HEIGHT = 640, 480
WIDTH, HEIGHT = 320, 240
# Região do solo (grid A - parte inferior): ocupa 50% da altura da imagem
GROUND_ROWS = 10 # Número de linhas da matriz do solo
GROUND_CELLS = 10 # Número de células por linha
GROUND_REGION_HEIGHT = int(HEIGHT * 0.5) # 50% da altura da imagem
GROUND_TOP_SCALE = 0.5 # A linha mais distante (superior da região) terá 35% da largura total
DEPTH_LIMIT = 50 # (Valor de referência para comparação, mas agora usamos calibração)
# Região aérea (parte superior): ocupa o restante da imagem
AIR_ROWS = 10 # Número de linhas na matriz aérea
AIR_COLS = 15 # Número de colunas na matriz aérea
AIR_REGION_HEIGHT = HEIGHT - GROUND_REGION_HEIGHT # Altura da região aérea
# ───────────────────────────────────────────────
# Parâmetros para calibração e detecção com moda
# ───────────────────────────────────────────────
NUM_CALIB_FRAMES = 100 # Número de frames para calibração
NUM_DETECT_FRAMES = 10 # Número de frames para acumulação antes de calcular a moda na detecção
# Variável global para armazenar o frame de profundidade atual (para uso no callback)
current_depth_frame = None
show_grid = False
filtro1 = True
filtro2 = True
filtro3 = True
filtro4 = True
# ───────────────────────────────────────────────
# Criação do pipeline DepthAI
# ───────────────────────────────────────────────
pipeline = dai.Pipeline()
# Nó da câmera RGB
cam_rgb = pipeline.create(dai.node.ColorCamera)
cam_rgb.setPreviewSize(RGB_WIDTH, RGB_HEIGHT)
cam_rgb.setBoardSocket(dai.CameraBoardSocket.RGB)
cam_rgb.setInterleaved(False)
xout_rgb = pipeline.create(dai.node.XLinkOut)
xout_rgb.setStreamName("rgb")
cam_rgb.preview.link(xout_rgb.input)
# Nó de profundidade
mono_left = pipeline.create(dai.node.MonoCamera)
mono_right = pipeline.create(dai.node.MonoCamera)
mono_left.setResolution(dai.MonoCameraProperties.SensorResolution.THE_480_P)
mono_right.setResolution(dai.MonoCameraProperties.SensorResolution.THE_480_P)
mono_left.setBoardSocket(dai.CameraBoardSocket.LEFT)
mono_right.setBoardSocket(dai.CameraBoardSocket.RIGHT)
stereo = pipeline.create(dai.node.StereoDepth)
stereo.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.ROBOTICS)
#stereo.initialConfig.setConfidenceThreshold(250) # Remove ruído, mantendo apenas pontos confiáveis
#stereo.initialConfig.setMedianFilter(dai.MedianFilter.KERNEL_7x7) # Usa filtro de mediana forte
#stereo.setLeftRightCheck(True) # Ativa verificação para evitar erros
#stereo.setExtendedDisparity(False) # Reduz ruído em distâncias curtas
#stereo.setSubpixel(True) # Aumenta a precisão da profundidade
mono_left.out.link(stereo.left)
mono_right.out.link(stereo.right)
xout_depth = pipeline.create(dai.node.XLinkOut)
xout_depth.setStreamName("depth")
stereo.depth.link(xout_depth.input)
device_global = None
selected_device_info = None
camera_iniciada = False
def initialize_device():
global device_global, selected_device_info, camera_iniciada
# Listar todas as câmeras conectadas
devices = dai.Device.getAllAvailableDevices()
if len(devices) == 0:
print("Nenhuma câmera OAK conectada.")
return
if camera_index >= len(devices):
print(f"Índice da câmera ({camera_index}) inválido. Apenas {len(devices)} câmeras disponíveis.")
return
selected_device_info = devices[camera_index] # Seleciona a câmera correta pelo índice
print(f"Usando câmera: {selected_device_info.name} (ID: {selected_device_info.mxid})")
device_global = dai.Device(pipeline, selected_device_info)
camera_iniciada = True
# 🔹 Função para enviar vídeo via Flask
def process_depth_data():
if camera_iniciada == False:
initialize_device()
global device_global, selected_device_info
depth_queue = device_global.getOutputQueue(name="depth", maxSize=1, blocking=False)
# Função para calibrar a região do solo utilizando a moda
def calibrate_ground():
print("Calibrando região do solo: coletando {} frames...".format(NUM_CALIB_FRAMES))
calib_data = np.zeros((GROUND_ROWS, GROUND_CELLS, NUM_CALIB_FRAMES))
for frame_idx in range(NUM_CALIB_FRAMES):
depth_frame = depth_queue.get().getFrame()
depth_frame = apply_depth_filters(depth_frame)
for i in range(GROUND_ROWS):
y_start = HEIGHT - GROUND_REGION_HEIGHT + i * row_height_ground
y_end = y_start + row_height_ground
scale = GROUND_TOP_SCALE + (i / (GROUND_ROWS - 1)) * (1 - GROUND_TOP_SCALE) if GROUND_ROWS > 1 else 1
effective_width = int(WIDTH * scale)
cell_width = effective_width / GROUND_CELLS
for j in range(GROUND_CELLS):
x_start = int(WIDTH / 2 - effective_width / 2 + j * cell_width)
x_end = int(x_start + cell_width)
calib_data[i, j, frame_idx] = np.mean(depth_frame[y_start:y_end, x_start:x_end])
ground_ref = np.zeros((GROUND_ROWS, GROUND_CELLS))
for i in range(GROUND_ROWS):
for j in range(GROUND_CELLS):
ground_ref[i, j] = calc_mode(calib_data[i, j, :])
print("Calibração da região do solo concluída!")
return ground_ref
# Função para calibrar a região aérea utilizando a moda
def calibrate_air():
print("Calibrando região aérea: coletando {} frames...".format(NUM_CALIB_FRAMES))
calib_data_air = np.zeros((AIR_ROWS, AIR_COLS, NUM_CALIB_FRAMES))
row_height_air = AIR_REGION_HEIGHT // AIR_ROWS
cell_width_air = WIDTH // AIR_COLS
for frame_idx in range(NUM_CALIB_FRAMES):
depth_frame = depth_queue.get().getFrame()
depth_frame = apply_depth_filters(depth_frame)
for i in range(AIR_ROWS):
y_start = i * row_height_air
y_end = y_start + row_height_air
for j in range(AIR_COLS):
x_start = j * cell_width_air
x_end = x_start + cell_width_air
calib_data_air[i, j, frame_idx] = np.mean(depth_frame[y_start:y_end, x_start:x_end])
air_ref = np.zeros((AIR_ROWS, AIR_COLS))
for i in range(AIR_ROWS):
for j in range(AIR_COLS):
air_ref[i, j] = calc_mode(calib_data_air[i, j, :])
print("Calibração da região aérea concluída!")
return air_ref
# Função para calibrar a distância utilizando a moda
def calibrate_distance(alvo=2500):
global GROUND_REGION_HEIGHT, AIR_REGION_HEIGHT # Garantir que estamos alterando as variáveis globais
NUM_LINHAS_ANALISE = 80 # Número de linhas horizontais para análise
ALTURA_LINHA = HEIGHT // NUM_LINHAS_ANALISE # Altura de cada linha
PROFUNDIDADE_ALVO = alvo # Profundidade alvo em cm para definir a região do solo dinamicamente
media_profundidade_acumulada = np.zeros((NUM_LINHAS_ANALISE, NUM_CALIB_FRAMES))
print("Calibrando profundidade média com {} frames...".format(NUM_CALIB_FRAMES))
for frame_idx in range(NUM_CALIB_FRAMES):
depth_frame = depth_queue.get().getFrame()
depth_frame = apply_depth_filters(depth_frame)
for i in range(NUM_LINHAS_ANALISE):
y_start = HEIGHT - (i + 1) * ALTURA_LINHA
y_end = y_start + ALTURA_LINHA
x_start_crop = int(WIDTH * 0.0)
x_end_crop = int(WIDTH * 1.0)
region_values = depth_frame[y_start:y_end, x_start_crop:x_end_crop].flatten()
valid_values = region_values[(region_values > 0) & (region_values < 10000)]
if valid_values.size > 0:
media_profundidade_acumulada[i, frame_idx] = np.mean(valid_values)
else:
media_profundidade_acumulada[i, frame_idx] = 9999
media_profundidade = np.array([calc_mode(media_profundidade_acumulada[i, :]) for i in range(NUM_LINHAS_ANALISE)])
#print("Médias de profundidade por linha (usando moda):", media_profundidade)
erro_minimo = float('inf')
linha_alvo = None
for i in range(NUM_LINHAS_ANALISE):
erro = abs(media_profundidade[i] - PROFUNDIDADE_ALVO)
if erro < erro_minimo:
erro_minimo = erro
linha_alvo = i
if linha_alvo is not None:
linha_alvo += 1
GROUND_REGION_HEIGHT = HEIGHT - ((NUM_LINHAS_ANALISE - linha_alvo) * ALTURA_LINHA)
else:
GROUND_REGION_HEIGHT = HEIGHT // 2
AIR_REGION_HEIGHT = HEIGHT - GROUND_REGION_HEIGHT
v = GROUND_REGION_HEIGHT // GROUND_ROWS
print("Novo GROUND_REGION_HEIGHT:", GROUND_REGION_HEIGHT)
print("Novo AIR_REGION_HEIGHT:", AIR_REGION_HEIGHT)
print("Novo tamanho das linhas do solo:", v)
return v
# Gerar json dos dados das subdivisoes
def gerar_json_subdivisoes():
data = {
"x_max": WIDTH,
"y_max": HEIGHT,
"subdivisoes_cima": {
"num_linhas": AIR_ROWS,
"num_colunas": AIR_COLS,
"celulas": []
},
"subdivisoes_chao": {
"num_linhas": GROUND_ROWS,
"num_colunas": GROUND_CELLS,
"celulas": []
}
}
# Subdivisões da região aérea
row_height_air = AIR_REGION_HEIGHT // AIR_ROWS
cell_width_air = WIDTH // AIR_COLS
for i in range(AIR_ROWS):
for j in range(AIR_COLS):
x_start = j * cell_width_air
y_start = i * row_height_air
data["subdivisoes_cima"]["celulas"].append({
"linha": i,
"coluna": j,
"x": x_start,
"y": y_start,
"largura": cell_width_air,
"altura": row_height_air,
"profundidade_media": smoothed_air[i, j],
"profundidade_calibragem": air_reference[i, j]
})
# Subdivisões da região do solo
row_height_ground = GROUND_REGION_HEIGHT // GROUND_ROWS
for i in range(GROUND_ROWS):
scale = (GROUND_TOP_SCALE + (i / (GROUND_ROWS - 1)) * (1 - GROUND_TOP_SCALE)) if GROUND_ROWS > 1 else 1
effective_width = int(WIDTH * scale)
cell_width = effective_width / GROUND_CELLS
y_start = HEIGHT - GROUND_REGION_HEIGHT + i * row_height_ground
start_x = int(WIDTH / 2 - effective_width / 2)
for j in range(GROUND_CELLS):
x_start = int(start_x + j * cell_width)
data["subdivisoes_chao"]["celulas"].append({
"linha": i,
"coluna": j,
"x": x_start,
"y": y_start,
"largura": int(cell_width),
"altura": row_height_ground,
"profundidade_media": smoothed_ground[i, j],
"profundidade_calibragem": ground_reference[i, j]
})
return data
# Calibração inicial
row_height_ground = calibrate_distance()
ground_reference = calibrate_ground()
air_reference = calibrate_air()
# Preparação para a detecção usando moda (acumula dados de alguns frames)
detect_counter = 0
detect_data_ground = np.zeros((GROUND_ROWS, GROUND_CELLS, NUM_DETECT_FRAMES))
detect_data_air = np.zeros((AIR_ROWS, AIR_COLS, NUM_DETECT_FRAMES))
last_detect_ground = np.zeros((GROUND_ROWS, GROUND_CELLS))
last_detect_air = np.zeros((AIR_ROWS, AIR_COLS))
# Parâmetro para o filtro de média móvel
alpha = 0.4 # ajuste entre 0 e 1 (valores menores = mais suave)
# Inicialize as grids filtradas com os valores de calibração (ou com zeros, se preferir)
smoothed_ground = ground_reference.copy()
smoothed_air = air_reference.copy()
last_time = time.time()
target_fps = 30 # Limita o FPS
readings = []
while True:
current_time = time.time()
if current_time - last_time < 1 / target_fps:
time.sleep(0.01)
continue
last_time = current_time
in_depth = depth_queue.tryGet()
if in_depth is None:
continue
depth_frame = in_depth.getFrame()
# Aplicação dos filtros em sequência
depth_frame = apply_depth_filters(depth_frame)
# Gera o mapa de calor
heatmap = generate_heatmap(depth_frame)
# Cria uma cópia do heatmap para desenhar o overlay
heatmap_overlay = heatmap.copy()
# ─────────────────────────────────────────────
# Atualiza a grid do SOLO (região inferior) com média móvel
# ─────────────────────────────────────────────
for i in range(GROUND_ROWS):
y_start = HEIGHT - GROUND_REGION_HEIGHT + i * row_height_ground
y_end = y_start + row_height_ground
scale = (GROUND_TOP_SCALE +
(i / (GROUND_ROWS - 1)) * (1 - GROUND_TOP_SCALE)) if GROUND_ROWS > 1 else 1
effective_width = int(WIDTH * scale)
cell_width = effective_width / GROUND_CELLS
for j in range(GROUND_CELLS):
x_start = int(WIDTH / 2 - effective_width / 2 + j * cell_width)
x_end = int(x_start + cell_width)
# Obtém os valores válidos da região
region_values = depth_frame[y_start:y_end, x_start:x_end].flatten()
valid_values = region_values[(region_values > 0) & (region_values < 10000)] # Remove valores inválidos
# Calcula a média da célula e atualiza as variáveis
if valid_values.size > 0:
measurement = np.mean(valid_values)
else:
measurement = 9999 # Define um valor alto se não houver dados válidos
# Atualiza a grid suavizada com a média móvel
smoothed_ground[i, j] = alpha * measurement + (1 - alpha) * smoothed_ground[i, j]
# Atualiza a matriz last_detect_ground com a média real da célula
last_detect_ground[i, j] = measurement
# ─────────────────────────────────────────────
# Atualiza a grid da REGIÃO AÉREA (parte superior) com média móvel
# ─────────────────────────────────────────────
row_height_air = AIR_REGION_HEIGHT // AIR_ROWS
cell_width_air = WIDTH // AIR_COLS
for i in range(AIR_ROWS):
y_start = i * row_height_air
y_end = y_start + row_height_air
for j in range(AIR_COLS):
x_start = j * cell_width_air
x_end = x_start + cell_width_air
# Obtém os valores válidos da região
region_values = depth_frame[y_start:y_end, x_start:x_end].flatten()
valid_values = region_values[(region_values > 0) & (region_values < 10000)] # Remove valores inválidos
# Calcula a média da célula e atualiza as variáveis
if valid_values.size > 0:
measurement = np.mean(valid_values)
else:
measurement = 9999 # Define um valor alto se não houver dados válidos
# Atualiza a grid suavizada com a média móvel
smoothed_air[i, j] = alpha * measurement + (1 - alpha) * smoothed_air[i, j]
# Atualiza a matriz last_detect_air com a média real da célula
last_detect_air[i, j] = measurement
# ─────────────────────────────────────────────
# Processamento da detecção: Região do SOLO
# ─────────────────────────────────────────────
for i in range(GROUND_ROWS):
y_start = HEIGHT - GROUND_REGION_HEIGHT + i * row_height_ground
y_end = y_start + row_height_ground
scale = (GROUND_TOP_SCALE + (i / (GROUND_ROWS - 1)) * (1 - GROUND_TOP_SCALE)) if GROUND_ROWS > 1 else 1
effective_width = int(WIDTH * scale)
cell_width = effective_width / GROUND_CELLS
for j in range(GROUND_CELLS):
x_start = int(WIDTH / 2 - effective_width / 2 + j * cell_width)
x_end = int(x_start + cell_width)
# Compara o valor suavizado com a referência calibrada
if smoothed_ground[i, j] < (ground_reference[i, j] - DEPTH_LIMIT):
# Obstáculo (valor menor: objeto mais próximo)
cv2.rectangle(heatmap_overlay, (x_start, y_start), (x_end, y_end), (0, 0, 255), -1)
elif smoothed_ground[i, j] > (ground_reference[i, j] + DEPTH_LIMIT):
# Erosão (valor maior: superfície rebaixada)
cv2.rectangle(heatmap_overlay, (x_start, y_start), (x_end, y_end), (0, 255, 255), -1)
if show_grid:
cv2.rectangle(heatmap, (x_start, y_start), (x_end, y_end), (255, 255, 255), 1)
# ─────────────────────────────────────────────
# Processamento da detecção: Região AÉREA
# ─────────────────────────────────────────────
for i in range(AIR_ROWS):
y_start = i * row_height_air
y_end = y_start + row_height_air
for j in range(AIR_COLS):
x_start = j * cell_width_air
x_end = x_start + cell_width_air
if smoothed_air[i, j] < (air_reference[i, j] - DEPTH_LIMIT):
cv2.rectangle(heatmap_overlay, (x_start, y_start), (x_end, y_end), (0, 255, 0), -1)
if show_grid:
cv2.rectangle(heatmap, (x_start, y_start), (x_end, y_end), (255, 255, 255), 1)
try:
memory_usage = device_global.getDdrMemoryUsage()
memory_info = {
"remaining": memory_usage.remaining,
"total": memory_usage.total,
"used": memory_usage.used
}
except:
memory_info = None # Se houver erro, define como None
try:
temp = device_global.getChipTemperature()
temp_info = {
"css": temp.css,
"mss": temp.mss,
"upa": temp.upa,
"dss": temp.dss
}
except:
temp_info = None # Se houver erro, define como None
device_data = {
"id": selected_device_info.getMxId(), # ID do dispositivo
"name": selected_device_info.name, # Nome do dispositivo
"state": selected_device_info.state.name, # Estado do dispositivo
"usb_speed": str(device_global.getUsbSpeed().name) if hasattr(device_global, 'getUsbSpeed') else None, # Velocidade USB
"available_camera_sensors": [sensor.name for sensor in device_global.getConnectedCameras()], # Sensores de câmera disponíveis
"version": str(device_global.getDeviceInfo().protocol) if hasattr(device_global, 'getDeviceInfo') else None, # Versão do protocolo
"memory_usage": memory_info, # Uso de memória DDR
"temperature": temp_info, # Temperatura do chip
"bootloader_version": str(device_global.getBootloaderVersion()) if hasattr(device_global, 'getBootloaderVersion') else None, # Bootloader
"is_pipeline_running": device_global.isPipelineRunning() if hasattr(device_global, 'isPipelineRunning') else None # Pipeline rodando?
}
json_data = {
'timestamp': current_time,
'device_data': device_data,
'x_max': WIDTH,
'y_max': HEIGHT,
'subdivisoes': gerar_json_subdivisoes(),
}
readings.append(json_data)
# Enviar apenas a cada max_readings capturas
if len(readings) >= max_readings:
mensagem_relevante = readings[-1]
mqtt_client.publish(mqtt_topic, json.dumps(mensagem_relevante).encode('utf-8'))
readings.clear()
video_frame = heatmap
ret, buffer = cv2.imencode('.jpg', video_frame, [cv2.IMWRITE_JPEG_QUALITY, 80]) # Reduz qualidade para 80%
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
# 🔹 Função para enviar vídeo via Flask
def view_rgb_video():
if camera_iniciada == False:
initialize_device()
global device_global, selected_device_info
rgb_queue = device_global.getOutputQueue(name="rgb", maxSize=1, blocking=False)
while True:
in_rgb = rgb_queue.tryGet()
if in_rgb is None:
continue
rgb_frame = in_rgb.getCvFrame()
video_frame = rgb_frame
ret, buffer = cv2.imencode('.jpg', video_frame, [cv2.IMWRITE_JPEG_QUALITY, 80]) # Reduz qualidade para 80%
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
def send_script_ready1():
mqtt_client.publish(mqtt_topic, "OK_1")
def send_script_ready2():
mqtt_client.publish(mqtt_topic, "OK_2")
def run_flask_server():
app.run(host='0.0.0.0', port=porta, threaded=True, debug=False)
# 🔹 Servidores Flask para RGB e Heatmap
@app.route('/' + url_rgb, methods=['GET'])
def video_feed_rgb():
return Response(view_rgb_video(), mimetype='multipart/x-mixed-replace; boundary=frame')
@app.route('/' + url_heatmap, methods=['GET'])
def video_feed_heatmap():
return Response(process_depth_data(), mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
flask_thread = threading.Thread(target=run_flask_server)
flask_thread.start()
time.sleep(1)
mqtt_thread1 = threading.Thread(target=send_script_ready1)
mqtt_thread1.start()
time.sleep(5)
mqtt_thread2 = threading.Thread(target=send_script_ready2)
mqtt_thread2.start()

View File

@ -1,182 +0,0 @@
import depthai as dai
import cv2
import numpy as np
import time
import json
import sys
import threading
import paho.mqtt.client as mqtt
import uuid
from flask import Flask, Response, request
# 🔹 Configurações MQTT
mqtt_client = mqtt.Client(f"client_oak_d_lite_{uuid.uuid4()}")
mqtt_client.connect("localhost", port=1883)
# 🔹 Parâmetros de entrada
output_folder = 'Python/Output/'
mqtt_topic = sys.argv[1] # Tópico MQTT para envio dos dados de profundidade
porta = int(sys.argv[2]) # Porta do Flask
url = sys.argv[3] # URL do vídeo
max_readings = int(sys.argv[4])
# 🔹 Flask App
app = Flask(__name__)
# 🔹 Criando o pipeline
pipeline = dai.Pipeline()
width, height = 1280, 720
# 📷 Câmera RGB
cam_rgb = pipeline.create(dai.node.ColorCamera)
cam_rgb.setPreviewSize(width, height)
cam_rgb.setBoardSocket(dai.CameraBoardSocket.RGB)
cam_rgb.setInterleaved(False)
# Criar XLinkOut para saída RGB
xout_rgb = pipeline.create(dai.node.XLinkOut)
xout_rgb.setStreamName("rgb")
cam_rgb.preview.link(xout_rgb.input)
# 🔹 Câmera de Profundidade
mono_left = pipeline.create(dai.node.MonoCamera)
mono_right = pipeline.create(dai.node.MonoCamera)
stereo = pipeline.create(dai.node.StereoDepth)
mono_left.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
mono_right.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
mono_left.setBoardSocket(dai.CameraBoardSocket.LEFT)
mono_right.setBoardSocket(dai.CameraBoardSocket.RIGHT)
# Configuração do StereoDepth
stereo.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.HIGH_DENSITY)
stereo.setLeftRightCheck(True)
stereo.setSubpixel(True)
mono_left.out.link(stereo.left)
mono_right.out.link(stereo.right)
# Criar XLinkOut para profundidade
xout_depth = pipeline.create(dai.node.XLinkOut)
xout_depth.setStreamName("depth")
stereo.depth.link(xout_depth.input)
# 🔹 Função para enviar vídeo via Flask
def generate_video(camera_index):
# Listar todas as câmeras conectadas
devices = dai.Device.getAllAvailableDevices()
if len(devices) == 0:
print("Nenhuma câmera OAK conectada.")
return
if camera_index >= len(devices):
print(f"Índice da câmera ({camera_index}) inválido. Apenas {len(devices)} câmeras disponíveis.")
return
selected_device_info = devices[camera_index] # Seleciona a câmera correta pelo índice
print(f"Usando câmera: {selected_device_info.name} (ID: {selected_device_info.mxid})")
with dai.Device(pipeline, selected_device_info) as device: # 🔹 Agora usa a câmera correta
rgb_queue = device.getOutputQueue(name="rgb", maxSize=1, blocking=False)
depth_queue = device.getOutputQueue(name="depth", maxSize=1, blocking=False)
readings = []
while True:
frame_data = rgb_queue.get()
if frame_data is None:
continue
timestamp = time.time()
# 📏 Captura Profundidade
depth_frame = depth_queue.get().getFrame()
# Normaliza a profundidade para visualização (mapa de calor)
depth_visual = cv2.normalize(depth_frame, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
depth_visual = cv2.applyColorMap(depth_visual, cv2.COLORMAP_JET)
# 📡 Enviar dados via MQTT (Apenas a matriz de profundidade reduzida)
depth_data = depth_frame.tolist() # Converte a matriz para lista JSON
depth_small = cv2.resize(depth_frame, (width // 2, height // 2)) # Reduz para metade
depth_data = depth_small.tolist()
try:
memory_usage = device.getDdrMemoryUsage()
memory_info = {
"remaining": memory_usage.remaining,
"total": memory_usage.total,
"used": memory_usage.used
}
except:
memory_info = None # Se houver erro, define como None
try:
temp = device.getChipTemperature()
temp_info = {
"css": temp.css,
"mss": temp.mss,
"upa": temp.upa,
"dss": temp.dss
}
except:
temp_info = None # Se houver erro, define como None
device_data = {
"id": selected_device_info.getMxId(), # ID do dispositivo
"name": selected_device_info.name, # Nome do dispositivo
"state": selected_device_info.state.name, # Estado do dispositivo
"usb_speed": str(device.getUsbSpeed().name) if hasattr(device, 'getUsbSpeed') else None, # Velocidade USB
"available_camera_sensors": [sensor.name for sensor in device.getConnectedCameras()], # Sensores de câmera disponíveis
"version": str(device.getDeviceInfo().protocol) if hasattr(device, 'getDeviceInfo') else None, # Versão do protocolo
"memory_usage": memory_info, # Uso de memória DDR
"temperature": temp_info, # Temperatura do chip
"bootloader_version": str(device.getBootloaderVersion()) if hasattr(device, 'getBootloaderVersion') else None, # Bootloader
"is_pipeline_running": device.isPipelineRunning() if hasattr(device, 'isPipelineRunning') else None # Pipeline rodando?
}
json_data = {
'timestamp': timestamp,
'device_data': device_data,
'x_max': width,
'y_max': height,
'depth_data': depth_data,
}
readings.append(json_data)
# Enviar apenas a cada max_readings capturas
if len(readings) >= max_readings:
mensagem_relevante = readings[-1]
mqtt_client.publish(mqtt_topic, json.dumps(mensagem_relevante).encode('utf-8'))
readings.clear()
frame = frame_data.getCvFrame()
ret, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 80]) # Reduz qualidade para 80%
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
def send_script_ready():
mqtt_client.publish(mqtt_topic, "OK")
def run_flask_server():
app.run(host='0.0.0.0', port=porta, threaded=True, debug=False)
@app.route('/' + url, methods=['GET'])
def video_feed():
camera_index = int(request.args.get('camera_index'))
return Response(generate_video(camera_index), mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
mqtt_thread = threading.Thread(target=send_script_ready)
mqtt_thread.start()
run_flask_server()

View File

@ -1,172 +0,0 @@
import cv2
import torch
import time
import threading
import numpy as np
from numpy import random
import sys
import os
from flask import Flask, Response, request
import json
models_path = os.path.join(os.path.dirname(__file__), '..', 'Models/yolov7')
sys.path.append(models_path)
from models.experimental import attempt_load
from utils.general import non_max_suppression, scale_coords, check_img_size
from utils.torch_utils import select_device, time_synchronized
from utils.datasets import letterbox
from utils.plots import plot_one_box
import uuid
import paho.mqtt.client as mqtt
mqtt_client = mqtt.Client(f"client_weed_detector_{uuid.uuid4()}")
mqtt_client.connect("localhost", port=1883)
# Outras configurações
json_data = None
output_folder = 'Python/Output/'
max_readings = int(sys.argv[1])
porta = sys.argv[2]
url = sys.argv[3]
arquivoSaida = sys.argv[4]
mostrar_linhas = sys.argv[5] == "1"
mqtt_topic = sys.argv[6]
model_folder = sys.argv[7]
script_version = sys.argv[8]
app = Flask(__name__)
def detect(weights, _camera_index, img_size=512, conf_thres=0.25, iou_thres=0.45):
with torch.no_grad():
# Initialize
device = select_device('')
half = device.type != 'cpu' # half precision only supported on CUDA
# Load model
model = attempt_load(weights, map_location=device) # load FP32 model
model.eval()
stride = int(model.stride.max()) # model stride
imgsz = check_img_size(img_size, s=stride) # check img_size
if half:
model.half() # to FP16
# Open camera
cap = cv2.VideoCapture(_camera_index) # 0 for the first webcam device
#cap.set(3, 640) # set video width
#cap.set(4, 480) # set video height
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
readings = []
while True:
ret, frame = cap.read()
if not ret:
break
# Padded resize
img = letterbox(frame, imgsz, stride=stride)[0]
# Convert
img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
img = np.ascontiguousarray(img)
img = torch.from_numpy(img).to(device)
img = img.half() if half else img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if img.ndimension() == 3:
img = img.unsqueeze(0)
# Inference
t1 = time_synchronized()
pred = model(img, augment=False)[0]
t2 = time_synchronized()
# Apply NMS
pred = non_max_suppression(pred, conf_thres, iou_thres, classes=None, agnostic=False)
current_readings = []
# Process detections
for i, det in enumerate(pred): # detections per frame
if len(det):
# Rescale boxes from img_size to frame size
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], frame.shape).round()
# Print results and draw boxes
for *xyxy, conf, cls in reversed(det):
x1, y1, x2, y2 = xyxy
x = int(x1)
y = int(y1)
w = int(x2 - x1)
h = int(y2 - y1)
class_id = int(cls)
confidence = float(conf)
detection_info = {
'id': class_id,
'descricao': model.module.names[class_id] if hasattr(model, 'module') else model.names[class_id],
'x': x,
'y': y,
'largura': w,
'altura': h,
'confianca': confidence
}
current_readings.append(detection_info)
if mostrar_linhas:
label = f'{model.names[class_id]} {confidence:.2f}'
plot_one_box(xyxy, frame, label=label, color=[random.randint(0, 255) for _ in range(3)], line_thickness=3)
# Após a detecção:
del img # Delete the image tensor to free up memory
#if device.type == 'cuda':
# torch.cuda.empty_cache() # Clear CUDA cache
#gc.collect() # Run garbage collection
global json_data
timestamp = time.time()
json_data = {'timestamp': timestamp, 'x_max': width, 'y_max': height, 'objetos': current_readings}
readings.append(json_data)
# Verifica se atingiu o limite do buffer
if len(readings) >= max_readings:
# Escolhe a mensagem mais relevante
mensagem_relevante = max(readings, key=lambda m: len(m['objetos']), default=None)
if not mensagem_relevante:
mensagem_relevante = readings[0] # Envia qualquer mensagem se não houver dados
mqtt_client.publish(mqtt_topic, json.dumps(mensagem_relevante).encode('utf-8'))
readings.clear() # Limpa o buffer após o envio
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
# Esta função é para confirmar que script está pronto para execução
def send_script_ready():
mqtt_client.publish(mqtt_topic, "OK")
# Iniciar o servidor Flask em uma thread separada
def run_flask_server():
app.run(host='0.0.0.0', port=porta, threaded=True, debug=False)
@app.route('/' + url, methods=['GET'])
def video_feed():
weights_path = model_folder + 'model-' + script_version + '.pt'
conf_threshold = float(request.args.get('conf_threshold'))
nms_threshold = float(request.args.get('nms_threshold'))
camera_index = int(request.args.get('camera_index'))
return Response(detect(weights_path, camera_index, 512), mimetype='multipart/x-mixed-replace; boundary=frame')
# Iniciar o servidor
if __name__ == '__main__':
# Cria trhead separada para informar que o script iniciou com sucesso
mqtt_thread = threading.Thread(target=send_script_ready)
mqtt_thread.start()
# Inicia o servidor Flask na thread principal
run_flask_server()

View File

@ -1,235 +0,0 @@
import cv2
import depthai as dai
import numpy as np
import time
import threading
import json
import sys
import os
import uuid
import paho.mqtt.client as mqtt
from flask import Flask, Response, request
# Configurações gerais
mqtt_client = mqtt.Client(f"client_weed_detector_{uuid.uuid4()}")
mqtt_client.connect("localhost", port=1883)
# Parâmetros de entrada
json_data = None
output_folder = 'Python/Output/'
max_readings = int(sys.argv[1])
porta = sys.argv[2]
url = sys.argv[3]
arquivoSaida = sys.argv[4]
mostrar_linhas = sys.argv[5] == "1"
mqtt_topic = sys.argv[6]
model_folder = sys.argv[7]
script_version = sys.argv[8]
# Flask App
app = Flask(__name__)
# Caminho do modelo convertido
blob_path = os.path.join(model_folder, f'model-{script_version}.blob')
json_path = os.path.join(model_folder, f'model-{script_version}.json')
# Carregar configurações do modelo a partir do JSON
with open(json_path, 'r') as json_file:
model_config = json.load(json_file)
# Extraindo informações relevantes
input_size = model_config["nn_config"]["input_size"] # Exemplo: "416x416"
shape_width, shape_height = map(int, input_size.split("x")) # Convertendo para inteiros
iou_threshold = model_config["nn_config"]["NN_specific_metadata"]["iou_threshold"]
conf_threshold = model_config["nn_config"]["NN_specific_metadata"]["confidence_threshold"]
labelMap = model_config["mappings"]["labels"] # Lista de classes do modelo
width, height = 1280, 720
def detect_oak(camera_index):
pipeline = dai.Pipeline()
# Listar todas as câmeras conectadas
devices = dai.Device.getAllAvailableDevices()
if len(devices) == 0:
print("Nenhuma câmera OAK conectada.")
return
if camera_index >= len(devices):
print(f"Índice da câmera ({camera_index}) inválido. Apenas {len(devices)} câmeras disponíveis.")
return
selected_device_info = devices[camera_index] # Seleciona a câmera correta pelo índice
print(f"Usando câmera: {selected_device_info.name} (ID: {selected_device_info.mxid})")
# Criando um nó de câmera otimizado
cam = pipeline.create(dai.node.ColorCamera)
cam.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
cam.setVideoSize(width, height)
cam.setInterleaved(False)
cam.setColorOrder(dai.ColorCameraProperties.ColorOrder.BGR)
cam.setFps(30) # FPS aumentado
cam.setIspScale(2, 3) # Reduz carga no pipeline
# Criando um nó de manipulação de imagem otimizado
manip = pipeline.create(dai.node.ImageManip)
manip.initialConfig.setResize(shape_width, shape_height)
manip.initialConfig.setKeepAspectRatio(False)
manip.initialConfig.setFrameType(dai.RawImgFrame.Type.RGB888p) # Força formato RGB correto
manip.initialConfig.setHorizontalFlip(True)
manip.initialConfig.setVerticalFlip(True)
manip.setMaxOutputFrameSize(1228800)
cam.video.link(manip.inputImage)
# Criando um nó de inferência YOLO otimizado
nn = pipeline.create(dai.node.YoloDetectionNetwork)
nn.setBlobPath(blob_path)
nn.setConfidenceThreshold(conf_threshold)
nn.setNumClasses(len(labelMap))
nn.setCoordinateSize(4)
nn.setIouThreshold(iou_threshold)
# 🔹 Define âncoras e máscaras corretamente
#nn.setAnchors([
# 12, 16, 19, 36, 40, 28,
# 36, 75, 76, 55, 72, 146,
# 142, 110, 192, 243, 459, 401
#])
#nn.setAnchorMasks({
# "side52": [0, 1, 2],
# "side26": [3, 4, 5],
# "side13": [6, 7, 8]
#})
nn.setNumInferenceThreads(2) # Usa 2 threads para inferência
nn.input.setBlocking(False) # Não bloqueia a entrada de frames
#nn.setReusePreviousInferenceResults(True) # Reutiliza inferências para não travar o pipeline
manip.out.link(nn.input)
# Saída de vídeo otimizada
xout_cam = pipeline.create(dai.node.XLinkOut)
xout_cam.setStreamName("video")
#xout_cam.setMetadataOnly(True) # Reduz tráfego de vídeo
cam.video.link(xout_cam.input)
# Saída da inferência
xout_nn = pipeline.create(dai.node.XLinkOut)
xout_nn.setStreamName("detections")
nn.out.link(xout_nn.input)
with dai.Device(pipeline, selected_device_info) as device: # 🔹 Agora usa a câmera correta
video_queue = device.getOutputQueue("video", maxSize=1, blocking=False)
detections_queue = device.getOutputQueue("detections", maxSize=1, blocking=False)
readings = []
while True:
frame_data = video_queue.tryGet() # 🔹 Usa tryGet() para evitar bloqueios
if frame_data is None:
continue # Se não houver um frame disponível, pula a iteração
frame = frame_data.getCvFrame()
in_det = detections_queue.get()
detections = in_det.detections
current_readings = []
for detection in detections:
# Invertendo as coordenadas, devido à imagem sofrer flip tanto na horizontal como na vertical
x1 = width - int(detection.xmin * width)
y1 = height - int(detection.ymin * height)
x2 = width - int(detection.xmax * width)
y2 = height - int(detection.ymax * height)
confidence = float(detection.confidence)
class_id = int(detection.label)
descricao = labelMap[class_id] if class_id < len(labelMap) else f"Classe {class_id}"
detection_info = {
'id': class_id,
'descricao': descricao,
'x': x1,
'y': y1,
'largura': x2 - x1,
'altura': y2 - y1,
'confianca': confidence
}
current_readings.append(detection_info)
if mostrar_linhas:
label = f'{descricao} {confidence:.2f}'
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(frame, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
global json_data
timestamp = time.time()
try:
memory_usage = device.getDdrMemoryUsage()
memory_info = {
"remaining": memory_usage.remaining,
"total": memory_usage.total,
"used": memory_usage.used
}
except:
memory_info = None # Se houver erro, define como None
try:
temp = device.getChipTemperature()
temp_info = {
"css": temp.css,
"mss": temp.mss,
"upa": temp.upa,
"dss": temp.dss
}
except:
temp_info = None # Se houver erro, define como None
device_data = {
"id": selected_device_info.getMxId(), # ID do dispositivo
"name": selected_device_info.name, # Nome do dispositivo
"state": selected_device_info.state.name, # Estado do dispositivo
"usb_speed": str(device.getUsbSpeed().name) if hasattr(device, 'getUsbSpeed') else None, # Velocidade USB
"available_camera_sensors": [sensor.name for sensor in device.getConnectedCameras()], # Sensores de câmera disponíveis
"version": str(device.getDeviceInfo().protocol) if hasattr(device, 'getDeviceInfo') else None, # Versão do protocolo
"memory_usage": memory_info, # Uso de memória DDR
"temperature": temp_info, # Temperatura do chip
"bootloader_version": str(device.getBootloaderVersion()) if hasattr(device, 'getBootloaderVersion') else None, # Bootloader
"is_pipeline_running": device.isPipelineRunning() if hasattr(device, 'isPipelineRunning') else None # Pipeline rodando?
}
json_data = {'timestamp': timestamp, 'x_max': width, 'y_max': height, 'objetos': current_readings, 'device_data': device_data}
print(json_data)
readings.append(json_data)
if len(readings) >= max_readings:
mensagem_relevante = max(readings, key=lambda m: len(m['objetos']), default=None)
if not mensagem_relevante:
mensagem_relevante = readings[0]
mqtt_client.publish(mqtt_topic, json.dumps(mensagem_relevante).encode('utf-8'))
readings.clear()
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
def send_script_ready():
mqtt_client.publish(mqtt_topic, "OK")
def run_flask_server():
app.run(host='0.0.0.0', port=porta, threaded=True, debug=False)
@app.route('/' + url, methods=['GET'])
def video_feed():
conf_threshold = float(request.args.get('conf_threshold'))
nms_threshold = float(request.args.get('nms_threshold'))
camera_index = int(request.args.get('camera_index'))
return Response(detect_oak(camera_index), mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
mqtt_thread = threading.Thread(target=send_script_ready)
mqtt_thread.start()
run_flask_server()

View File

@ -212,20 +212,24 @@ class ModuloBateria(ModuloDiagnosticoBase):
# 1) SOC
cond_soc = []
_corredor_atual = ContextoGlobalRedis.get_contexto().get("Trajetoria", {}).get("CorredorAtual", {})
bateria_suficiente_corredor = _corredor_atual.get("bateria_ok", True)
if not bateria_suficiente_corredor:
autonomia_iniciada = bool(_corredor_atual.get("autonomia_iniciada", False))
bateria_suficiente_corredor = bool(_corredor_atual.get("bateria_ok", False))
if autonomia_iniciada and not bateria_suficiente_corredor:
descricao = (_corredor_atual.get("motivo_bat") or _corredor_atual.get("autonomia_motivo") or "Bateria insuficiente para concluir o corredor.")
c = {
"label": "Estado de carga (SOC)",
"label": "Autonomia para o corredor",
"valor": soc if soc is not None else -1,
"severidade": 100,
"descricao": _corredor_atual.get("motivo_bat", "Bateria insuficiente"),
"descricao": descricao,
"acoes": [
"Trocar a bateria para retomar a operação em andamento."
]
"Verificar a distância restante do corredor.",
"Verificar a autonomia estimada da bateria.",
],
}
condicoes.append(c)
cond_soc.append(c)
if score_soc < 50:
if score_soc < 50:
severidade = int(max(0, 100 - score_soc))
c = {
"label": "Estado de carga (SOC)",