ajustes para modo producao
This commit is contained in:
parent
0f4e83cd34
commit
9ff0cd9cd5
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ?? "",
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 já 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 { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
]
|
||||
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
@ -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 já está selecionado
|
||||
if (trajetosSelecionados[feature.id]) {
|
||||
// Se já 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)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)",
|
||||
|
|
|
|||
Loading…
Reference in New Issue