iniciado migracao para uso do Redis

This commit is contained in:
Diego Freitas 2025-06-30 07:43:54 -03:00
parent 88c595c8a8
commit 766f8b4e8e
286 changed files with 149815 additions and 1051 deletions

Binary file not shown.

View File

@ -136,6 +136,9 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>.\Microsoft.DirectX.DirectInput.dll</HintPath> <HintPath>.\Microsoft.DirectX.DirectInput.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.6.0.0\lib\net461\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Kinect, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> <Reference Include="Microsoft.Kinect, Version=1.8.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.Web.WebView2.Core, Version=1.0.3240.44, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL"> <Reference Include="Microsoft.Web.WebView2.Core, Version=1.0.3240.44, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.3240.44\lib\net462\Microsoft.Web.WebView2.Core.dll</HintPath> <HintPath>..\packages\Microsoft.Web.WebView2.1.0.3240.44\lib\net462\Microsoft.Web.WebView2.Core.dll</HintPath>
@ -168,12 +171,18 @@
<Reference Include="OpenTK.GLControl, Version=1.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK.GLControl, Version=1.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\packages\OpenTK.GLControl.1.1.2225.0\lib\net20\OpenTK.GLControl.dll</HintPath> <HintPath>..\packages\OpenTK.GLControl.1.1.2225.0\lib\net20\OpenTK.GLControl.dll</HintPath>
</Reference> </Reference>
<Reference Include="Pipelines.Sockets.Unofficial, Version=1.0.0.0, Culture=neutral, PublicKeyToken=42ea0a778e13fbe2, processorArchitecture=MSIL">
<HintPath>..\packages\Pipelines.Sockets.Unofficial.2.2.8\lib\net472\Pipelines.Sockets.Unofficial.dll</HintPath>
</Reference>
<Reference Include="SharpDX, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL"> <Reference Include="SharpDX, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\packages\SharpDX.4.2.0\lib\net45\SharpDX.dll</HintPath> <HintPath>..\packages\SharpDX.4.2.0\lib\net45\SharpDX.dll</HintPath>
</Reference> </Reference>
<Reference Include="SharpDX.DirectInput, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL"> <Reference Include="SharpDX.DirectInput, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\packages\SharpDX.DirectInput.4.2.0\lib\net45\SharpDX.DirectInput.dll</HintPath> <HintPath>..\packages\SharpDX.DirectInput.4.2.0\lib\net45\SharpDX.DirectInput.dll</HintPath>
</Reference> </Reference>
<Reference Include="StackExchange.Redis, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c219ff1ca8c2ce46, processorArchitecture=MSIL">
<HintPath>..\packages\StackExchange.Redis.2.8.41\lib\net472\StackExchange.Redis.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath> <HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
@ -196,6 +205,9 @@
<Private>True</Private> <Private>True</Private>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System.IO.Pipelines, Version=5.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Pipelines.5.0.1\lib\net461\System.IO.Pipelines.dll</HintPath>
</Reference>
<Reference Include="System.Management" /> <Reference Include="System.Management" />
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath> <HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
@ -223,6 +235,9 @@
<Reference Include="System.Text.Json, Version=8.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Text.Json, Version=8.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Json.8.0.1\lib\net462\System.Text.Json.dll</HintPath> <HintPath>..\packages\System.Text.Json.8.0.1\lib\net462\System.Text.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Threading.Channels, Version=5.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Channels.5.0.0\lib\net461\System.Threading.Channels.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath> <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference> </Reference>
@ -571,6 +586,7 @@
<Compile Include="Services\Operadores\VisualWorkerService.cs" /> <Compile Include="Services\Operadores\VisualWorkerService.cs" />
<Compile Include="Services\PythonService.cs" /> <Compile Include="Services\PythonService.cs" />
<Compile Include="Services\PZEMService.cs" /> <Compile Include="Services\PZEMService.cs" />
<Compile Include="Services\RedisService.cs" />
<Compile Include="Services\SerialService.cs" /> <Compile Include="Services\SerialService.cs" />
<Compile Include="Services\SonarService.cs" /> <Compile Include="Services\SonarService.cs" />
<Compile Include="Services\SyncDataService.cs" /> <Compile Include="Services\SyncDataService.cs" />
@ -997,8 +1013,10 @@
<Error Condition="!Exists('..\packages\OpenCvSharp4.runtime.win.4.10.0.20240616\build\netstandard\OpenCvSharp4.runtime.win.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\OpenCvSharp4.runtime.win.4.10.0.20240616\build\netstandard\OpenCvSharp4.runtime.win.props'))" /> <Error Condition="!Exists('..\packages\OpenCvSharp4.runtime.win.4.10.0.20240616\build\netstandard\OpenCvSharp4.runtime.win.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\OpenCvSharp4.runtime.win.4.10.0.20240616\build\netstandard\OpenCvSharp4.runtime.win.props'))" />
<Error Condition="!Exists('..\packages\Emgu.CV.runtime.windows.4.4.0.4099\build\Emgu.CV.runtime.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Emgu.CV.runtime.windows.4.4.0.4099\build\Emgu.CV.runtime.windows.targets'))" /> <Error Condition="!Exists('..\packages\Emgu.CV.runtime.windows.4.4.0.4099\build\Emgu.CV.runtime.windows.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Emgu.CV.runtime.windows.4.4.0.4099\build\Emgu.CV.runtime.windows.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Web.WebView2.1.0.3240.44\build\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Web.WebView2.1.0.3240.44\build\Microsoft.Web.WebView2.targets'))" /> <Error Condition="!Exists('..\packages\Microsoft.Web.WebView2.1.0.3240.44\build\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Web.WebView2.1.0.3240.44\build\Microsoft.Web.WebView2.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Extensions.Logging.Abstractions.6.0.0\build\Microsoft.Extensions.Logging.Abstractions.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Extensions.Logging.Abstractions.6.0.0\build\Microsoft.Extensions.Logging.Abstractions.targets'))" />
</Target> </Target>
<Import Project="..\packages\AssimpNet.4.1.0\build\AssimpNet.targets" Condition="Exists('..\packages\AssimpNet.4.1.0\build\AssimpNet.targets')" /> <Import Project="..\packages\AssimpNet.4.1.0\build\AssimpNet.targets" Condition="Exists('..\packages\AssimpNet.4.1.0\build\AssimpNet.targets')" />
<Import Project="..\packages\Emgu.CV.runtime.windows.4.4.0.4099\build\Emgu.CV.runtime.windows.targets" Condition="Exists('..\packages\Emgu.CV.runtime.windows.4.4.0.4099\build\Emgu.CV.runtime.windows.targets')" /> <Import Project="..\packages\Emgu.CV.runtime.windows.4.4.0.4099\build\Emgu.CV.runtime.windows.targets" Condition="Exists('..\packages\Emgu.CV.runtime.windows.4.4.0.4099\build\Emgu.CV.runtime.windows.targets')" />
<Import Project="..\packages\Microsoft.Web.WebView2.1.0.3240.44\build\Microsoft.Web.WebView2.targets" Condition="Exists('..\packages\Microsoft.Web.WebView2.1.0.3240.44\build\Microsoft.Web.WebView2.targets')" /> <Import Project="..\packages\Microsoft.Web.WebView2.1.0.3240.44\build\Microsoft.Web.WebView2.targets" Condition="Exists('..\packages\Microsoft.Web.WebView2.1.0.3240.44\build\Microsoft.Web.WebView2.targets')" />
<Import Project="..\packages\Microsoft.Extensions.Logging.Abstractions.6.0.0\build\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('..\packages\Microsoft.Extensions.Logging.Abstractions.6.0.0\build\Microsoft.Extensions.Logging.Abstractions.targets')" />
</Project> </Project>

View File

@ -21,6 +21,14 @@
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> </configuration>

View File

@ -35,6 +35,8 @@ namespace AgroBase.Forms
PythonService.EncerrarProcessos(); PythonService.EncerrarProcessos();
RedisService.Iniciar();
Task.Run(async () => Task.Run(async () =>
{ {
await VariaveisOperacao.Operadores.IniciarProcessamento(false); await VariaveisOperacao.Operadores.IniciarProcessamento(false);

View File

@ -1606,8 +1606,34 @@ namespace AgroBase.Models
public OperacaoControleModel Controle { get; set; } public OperacaoControleModel Controle { get; set; }
public VisualWorkerModel OperadorVisual { get; set; } public VisualWorkerModel OperadorVisual { get; set; }
private void AtualizarDadosOperacaoRedis()
{
var operacao = RedisService.Get(CtxKey.DadosOperacao);
var parametros = JsonConvert.DeserializeObject<Dictionary<string, object>>(operacao);
bool configurado = Convert.ToBoolean(parametros["configurado"]);
long tempoAguardando = (long)(parametros["tempo_aguardando"]);
int op_status = Convert.ToInt32(parametros["status"]);
StatusOperacao statusOperacao = (StatusOperacao)op_status;
bool liberado = Convert.ToBoolean(parametros["liberado"]);
List<ManagerWorkerMessageResponseModulosPendentesModel> status_modulos = JsonConvert.DeserializeObject<ManagerWorkerMessageResponseModulosPendentesModel[]>(parametros["status_modulos"].ToString()).ToList();
Variaveis.OperacaoEmAndamento.TempoAguardando = DateTimeOffset.FromUnixTimeSeconds(tempoAguardando).UtcDateTime.AddSeconds(tempoAguardando % 1).ToLocalTime();
Variaveis.OperacaoEmAndamento.StatusAtual = statusOperacao;
Variaveis.OperacaoEmAndamento.OperacaoLiberada = liberado;
VariaveisOperacao.Operadores.ModulosSaude = new List<ManagerWorkerMessageResponseModulosPendentesModel>(status_modulos);
var mandatorios = Variaveis.OperacaoEmAndamento.ModulosMandatorios.Select(x => x.Dispositivo).ToList();
var mods_com_falha = status_modulos.Where(x => mandatorios.Contains(x.modulo) && x.status == Enums.StatusModulo.Falha);
var motivos_por_modulo = mods_com_falha.Select(x => $"{x.modulo}: {string.Join("; ", x.motivos)}");
Variaveis.OperacaoEmAndamento.ErroOperacaoLiberada = string.Join(Environment.NewLine + Environment.NewLine, motivos_por_modulo);
}
public void AtualizarDados() public void AtualizarDados()
{ {
AtualizarDadosOperacaoRedis();
var _Trajetoria = Variaveis.OperacaoEmAndamento.Trajetoria; var _Trajetoria = Variaveis.OperacaoEmAndamento.Trajetoria;
var _DispMvd = Variaveis.OperacaoEmAndamento.DispMvd; var _DispMvd = Variaveis.OperacaoEmAndamento.DispMvd;
var _DispAtu = Variaveis.OperacaoEmAndamento.DispAtu; var _DispAtu = Variaveis.OperacaoEmAndamento.DispAtu;
@ -1736,6 +1762,7 @@ namespace AgroBase.Models
Variaveis.OperacaoEmAndamento.Sensoriamento = dadosAtualizados; Variaveis.OperacaoEmAndamento.Sensoriamento = dadosAtualizados;
Variaveis.OperacaoEmAndamento.DispSen?.Dados?.ContadorCarga?.AtualizarConsumo(); Variaveis.OperacaoEmAndamento.DispSen?.Dados?.ContadorCarga?.AtualizarConsumo();
VariaveisOperacao.Operadores.HealthWorker?.EnviarDadosContexto(); VariaveisOperacao.Operadores.HealthWorker?.EnviarDadosContexto();
VariaveisOperacao.Operadores.HealthWorker?.EnviarDadosModulos();
} }
public OperacaoSensoriamentoLogModel Clone(bool comSonar = false) public OperacaoSensoriamentoLogModel Clone(bool comSonar = false)

View File

@ -85,6 +85,11 @@ namespace AgroBase.Models.Operadores
public string modelo { get; set; } public string modelo { get; set; }
} }
public enum HealthWorkerCommandType
{
SetDadosModulo = 1,
SetDadosContexto = 2,
SetDadosOperacao = 3,
}
} }

View File

@ -80,4 +80,10 @@ namespace AgroBase.Models.Operadores
public string descricao { get; set; } public string descricao { get; set; }
} }
public enum ManagerWorkerCommandType
{
IniciarMPC = 1,
EnviarDadosControle = 2,
}
} }

View File

@ -535,7 +535,10 @@ namespace AgroBase.Models.Operadores
public enum VisualWorkerCommandType public enum VisualWorkerCommandType
{ {
SalvarFrames = 0 SalvarFrames = 1,
IniciarCameraManager = 2,
AtualizarSaudeCamera = 3,
GetCameraFrame = 4,
} }
} }

View File

@ -39,6 +39,8 @@ namespace AgroBase.Services.Operadores
topicoRx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Rx, true, 2, MqttCallback); topicoRx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Rx, true, 2, MqttCallback);
topicoTx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Tx); topicoTx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Tx);
RedisService.Subscribe(CmdKey.HealthWorkerTx, RedisCallback);
mapaRespostas = new Dictionary<TipoComando, Func<bool>> mapaRespostas = new Dictionary<TipoComando, Func<bool>>
{ {
{ TipoComando.GetListaCameras, () => DadosLeitura.Leitura.obj?.cameras?.newData ?? false }, { TipoComando.GetListaCameras, () => DadosLeitura.Leitura.obj?.cameras?.newData ?? false },
@ -138,6 +140,11 @@ namespace AgroBase.Services.Operadores
return true; return true;
} }
private void RedisCallback(string mensagem)
{
}
private void AtualizarDadosLeitura(WorkerMessageModel<HealthWorkerMessageResponseModel> msg) private void AtualizarDadosLeitura(WorkerMessageModel<HealthWorkerMessageResponseModel> msg)
{ {
if (DadosLeitura.Leitura.obj == null) if (DadosLeitura.Leitura.obj == null)
@ -174,149 +181,43 @@ namespace AgroBase.Services.Operadores
public void EnviarDadosContexto() public void EnviarDadosContexto()
{ {
if (Variaveis.OperacaoEmAndamento.DispMvd == null) return;
double limiteValorPadrao = -9999;
var a = Variaveis.OperacaoEmAndamento.Trajetoria;
var _Sensoriamento = Variaveis.OperacaoEmAndamento.Sensoriamento; var _Sensoriamento = Variaveis.OperacaoEmAndamento.Sensoriamento;
if (_Sensoriamento.Trajetoria == null) return;
var dados = new var dados = new
{ {
comando = (int)TipoComando.SetDadosContexto, Trajetoria = new
dispositivo = (int)T_Code.Mod,
payload = new
{ {
Operacao = new status = (int)_Sensoriamento.Trajetoria.StatusCarro,
manobrando = _Sensoriamento.Trajetoria.ManobrandoEntreRuas,
erro_angular = _Sensoriamento.Trajetoria.ErroOrientacaoAngularCombinado,
idx_proximo_ponto = _Sensoriamento.Trajetoria.ProximoPonto.idxPonto,
CorredorAtual = new
{ {
debug_mode = Variaveis.DebugMode, concluido = _Sensoriamento.Trajetoria.CorredorAtual.Concluido,
iniciado = Variaveis.OperacaoEmAndamento.Iniciado, dentro = _Sensoriamento.Trajetoria.CorredorAtual.Dentro,
finalizando = Variaveis.OperacaoEmAndamento.Finalizando, ultimo = _Sensoriamento.Trajetoria.CorredorAtual.Ultimo,
emergencia = Variaveis.OperacaoEmAndamento.Emergencia, largura = _Sensoriamento.Trajetoria.CorredorAtual.Largura,
progresso = _Sensoriamento.Trajetoria.CorredorAtual.Progresso,
dist_esq = _Sensoriamento.Trajetoria.DistanciaEsquerda,
dist_dir = _Sensoriamento.Trajetoria.DistanciaDireita,
angulo = _Sensoriamento.Trajetoria.AnguloCaminho,
}, },
Trajetoria = new },
{ Gerais = new
status = (int)_Sensoriamento.Trajetoria.StatusCarro, {
manobrando = _Sensoriamento.Trajetoria.ManobrandoEntreRuas, velocidade_ms = _Sensoriamento.Movimentacao.VelocidadeMedia,
erro_angular = _Sensoriamento.Trajetoria.ErroOrientacaoAngularCombinado, ervas_no_radar = _Sensoriamento.Atuador.ErvasNoRadar,
idx_proximo_ponto = _Sensoriamento.Trajetoria.ProximoPonto.idxPonto, },
CorredorAtual = new
{
concluido = _Sensoriamento.Trajetoria.CorredorAtual.Concluido,
dentro = _Sensoriamento.Trajetoria.CorredorAtual.Dentro,
ultimo = _Sensoriamento.Trajetoria.CorredorAtual.Ultimo,
largura = _Sensoriamento.Trajetoria.CorredorAtual.Largura,
progresso = _Sensoriamento.Trajetoria.CorredorAtual.Progresso,
dist_esq = _Sensoriamento.Trajetoria.DistanciaEsquerda,
dist_dir = _Sensoriamento.Trajetoria.DistanciaDireita,
angulo = _Sensoriamento.Trajetoria.AnguloCaminho,
},
},
Gerais = new
{
velocidade_ms = _Sensoriamento.Movimentacao.VelocidadeMedia,
ervas_no_radar = _Sensoriamento.Atuador.ErvasNoRadar,
},
Gps = new
{
conectado = GPSService.Iniciado,
freq_base = GPSService.TaxaAmostragemHz,
lat = _Sensoriamento.Gps.Latitude,
lon = _Sensoriamento.Gps.Longitude,
theta = _Sensoriamento.Gps.AnguloCarroDefinido,
fix = (int)_Sensoriamento.Gps.QualidadeFix,
rtk = GPSService.UltimoEnvioCorrecaoRTK.AddSeconds(5) > DateTime.Now,
hAcc = _Sensoriamento.Gps.PrecisaoCm,
nSatelites = _Sensoriamento.Gps.NumeroSatelites.valor,
freq = _Sensoriamento.Gps.NumeroSatelites.frequencia,
},
Dir = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Select(x => new
{
conectado = x.DirMotor.Inicializado,
freq_base = Variaveis.OperacaoEmAndamento.DispMvd.Dados.tmrRegistradorDIR.IntervalHz,
endereco = x.DirMotor._EnderecoCAN_Tx,
mod_id = x.Modulo_ID,
angulo = x.DirMotor.Angulo,
angulo_sp = x.DirMotor.Angulo_SP,
comandar = x.DirMotor.Comandar,
in1_atuado = x.DirMotor.IN1_Atuado,
sentido = x.DirMotor.Sentido,
sentido_sp = x.DirMotor.Sentido_SP,
sentido_real = x.DirMotor.SentidoReal,
freq = new {
io = x.DirMotor.Leitura?.NumeroPulsos?.frequencia ?? 0,
pulsos = x.DirMotor.Leitura?.EntradaIN1?.frequencia ?? 0,
}
}).ToArray(),
Mov = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Select(x => new
{
conectado = x.MovMotor.Inicializado,
freq_base = Variaveis.OperacaoEmAndamento.DispMvd.Dados.tmrRegistradorMOV.IntervalHz,
endereco = x.MovMotor._EnderecoCAN_Tx,
mod_id = x.Modulo_ID,
cod_erro = x.MovMotor.CodigoAlarme,
rpm = x.MovMotor.RPM_Roda,
rpm_sp = x.MovMotor.RPM_SP_Leitura,
tensao = x.MovMotor.Tensao,
corrente_motor = x.MovMotor.Corrente_Motor,
corrente_barramento = x.MovMotor.Corrente_Barramento,
temperatura_driver = x.MovMotor.TemperaturaDriver,
temperatura_motor = x.MovMotor.Temperatura,
freado = x.MovMotor.Leitura?.Freado ?? false,
freq = new
{
erro = x.MovMotor.Leitura?.CodigoAlarme?.frequencia ?? 0,
rpm = x.MovMotor.Leitura?.ERPM?.frequencia ?? 0,
tensao = x.MovMotor.Leitura?.Tensao?.frequencia ?? 0,
corrente_motor = x.MovMotor.Leitura?.CorrenteMotor?.frequencia ?? 0,
corrente_barramento = x.MovMotor.Leitura?.CorrenteBarramento?.frequencia ?? 0,
temperatura = x.MovMotor.Leitura?.Temperatura?.frequencia ?? 0,
}
}).ToArray(),
Sen = new
{
conectado = Variaveis.OperacaoEmAndamento.DispSen.Dados.Conectado,
freq_base = Variaveis.OperacaoEmAndamento.DispSen.Dados.tmrRegistrador.IntervalHz,
sensores = Variaveis.OperacaoEmAndamento.DispSen.Dados.Sensores.Select(x => new
{
conectado = x.Inicializado,
id = x.ID_Num,
label = x.ID,
tipo = (int)x.Componente,
aferir = x.Aferir,
mandatorio = x.Mandatorio,
dados = x.ValoresLeituras.Where(y => y.analise_health).Select(y => new
{
label = y.funcao.ToString(),
nominal = y.nominal,
maximo = y.maximo,
minimo = y.minimo,
atual = y.atual.valor,
freq = y.atual.frequencia
}),
}).ToArray(),
servos = Variaveis.OperacaoEmAndamento.DispSen.Dados.Servos.Select(x => new
{
conectado = x.Inicializado,
id = x.ID_Num,
label = x.ID,
controlar = x.Controlar,
mandatorio = x.Mandatorio,
angulo_sp = x._AnguloControle,
dados = x.ValoresLeituras.Where(y => y.analise_health).Select(y => new
{
label = y.funcao.ToString(),
atual = y.atual.valor,
freq = y.atual.frequencia
}),
}).ToArray(),
},
}
}; };
Task.Run(async () =>
var comando = new
{ {
await Variaveis.MqttService.PublishAsync(topicoTx, JsonConvert.SerializeObject(dados)); cmd = (int)HealthWorkerCommandType.SetDadosContexto,
}); @params = dados
};
RedisService.Publish(CmdKey.HealthWorkerRx, JsonConvert.SerializeObject(comando));
} }
public void EnviarDadosOperacao() public void EnviarDadosOperacao()
@ -327,27 +228,28 @@ namespace AgroBase.Services.Operadores
var dados = new var dados = new
{ {
comando = (int)TipoComando.SetDadosOperacao, modo = (int)Variaveis.OperacaoEmAndamento.Modo,
dispositivo = (int)T_Code.Mod, debug_mode = Variaveis.DebugMode,
payload = new iniciado = Variaveis.OperacaoEmAndamento.Iniciado,
finalizando = Variaveis.OperacaoEmAndamento.Finalizando,
emergencia = Variaveis.OperacaoEmAndamento.Emergencia,
tempo_aguardar_inicio_operacao = Variaveis.OperacaoEmAndamento.TempoIniciarOperacao,
sonar_ativado = _Controle.SonarAtivado,
modulos_mandatorios = Variaveis.OperacaoEmAndamento.ModulosMandatorios.Select(x => (int)x.Dispositivo).ToArray(),
parametros_mandatorios = Variaveis.OperacaoEmAndamento.ParametrosMandatorios.Select(x => (int)x.Parametro).ToArray(),
Mov = new
{ {
modo = (int)Variaveis.OperacaoEmAndamento.Modo, percent_vel_max = _Controle.PercentualVelocidadeMax,
sonar_ativado = _Controle.SonarAtivado, percent_vel_min = _Controle.PercentualVelocidadeMin,
modulos_mandatorios = Variaveis.OperacaoEmAndamento.ModulosMandatorios.Select(x => (int)x.Dispositivo).ToArray(), },
parametros_mandatorios = Variaveis.OperacaoEmAndamento.ParametrosMandatorios.Select(x => (int)x.Parametro).ToArray(), Dir = new
Mov = new {
angulo_max = _Controle.Angulo_Max,
vel_movimento = _Controle.VelocidadeMP,
tipo_controle = (int)_Controle.TipoControleDirecional,
mpc = new
{ {
percent_vel_max = _Controle.PercentualVelocidadeMax, pontos = Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa?
percent_vel_min = _Controle.PercentualVelocidadeMin,
},
Dir = new
{
angulo_max = _Controle.Angulo_Max,
vel_movimento = _Controle.VelocidadeMP,
tipo_controle = (int)_Controle.TipoControleDirecional,
mpc = new
{
pontos = Variaveis.OperacaoEmAndamento.Trajetoria?._TrajetoriaFixa?
.Select(p => new .Select(p => new
{ {
lat = p.Posicao.Latitude, lat = p.Posicao.Latitude,
@ -356,32 +258,148 @@ namespace AgroBase.Services.Operadores
distanciaMargem = p.LarguraCorredor * 0.8 distanciaMargem = p.LarguraCorredor * 0.8
}) })
.ToList(), .ToList(),
horizonte = 2.5, horizonte = 2.5,
angulo_max_graus = _Controle.Angulo_Max, angulo_max_graus = _Controle.Angulo_Max,
distancia_entre_eixos = (VariaveisEquipamento.DistanciaEntreEixos / 100.0), distancia_entre_eixos = (VariaveisEquipamento.DistanciaEntreEixos / 100.0),
largura_equipamento = (VariaveisEquipamento.LarguraEquipamentoMm / 1000.0), largura_equipamento = (VariaveisEquipamento.LarguraEquipamentoMm / 1000.0),
velocidade_min = FuncoesMatematicas.CalculaVelocidadeMsPercentual(_Controle.PercentualVelocidadeMin), velocidade_min = FuncoesMatematicas.CalculaVelocidadeMsPercentual(_Controle.PercentualVelocidadeMin),
velocidade_max = FuncoesMatematicas.CalculaVelocidadeMsPercentual(_Controle.PercentualVelocidadeMax) velocidade_max = FuncoesMatematicas.CalculaVelocidadeMsPercentual(_Controle.PercentualVelocidadeMax)
}
},
Atu = new
{
percent_vertical_deteccao = _Controle.PercentualInicioPulverizacao,
capacidade_reservatorio = Variaveis.OperacaoEmAndamento.CapacidadeReservatorio,
pressao_linha = _Controle.PressaoLinha,
},
Snr = new
{
mx_id = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.Id
} }
},
Atu = new
{
percent_vertical_deteccao = _Controle.PercentualInicioPulverizacao,
capacidade_reservatorio = Variaveis.OperacaoEmAndamento.CapacidadeReservatorio,
pressao_linha = _Controle.PressaoLinha,
},
Snr = new
{
mx_id = Variaveis.OperacaoEmAndamento.DispSen.Dados.CameraCaminho.Id
} }
}; };
Task.Run(async () =>
var comando = new
{ {
await Variaveis.MqttService.PublishAsync(topicoTx, JsonConvert.SerializeObject(dados)); cmd = (int)HealthWorkerCommandType.SetDadosOperacao,
}); @params = dados
};
RedisService.Publish(CmdKey.HealthWorkerRx, JsonConvert.SerializeObject(comando));
} }
public void EnviarDadosModulos()
{
if (Variaveis.OperacaoEmAndamento.DispMvd == null) return;
var _Sensoriamento = Variaveis.OperacaoEmAndamento.Sensoriamento;
var dados = new
{
Gps = new
{
conectado = GPSService.Iniciado,
freq_base = GPSService.TaxaAmostragemHz,
lat = _Sensoriamento.Gps.Latitude,
lon = _Sensoriamento.Gps.Longitude,
theta = _Sensoriamento.Gps.AnguloCarroDefinido,
fix = (int)_Sensoriamento.Gps.QualidadeFix,
rtk = GPSService.UltimoEnvioCorrecaoRTK.AddSeconds(5) > DateTime.Now,
hAcc = _Sensoriamento.Gps.PrecisaoCm,
nSatelites = _Sensoriamento.Gps.NumeroSatelites.valor,
freq = _Sensoriamento.Gps.NumeroSatelites.frequencia,
},
Dir = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Select(x => new
{
conectado = x.DirMotor.Inicializado,
freq_base = Variaveis.OperacaoEmAndamento.DispMvd.Dados.tmrRegistradorDIR.IntervalHz,
endereco = x.DirMotor._EnderecoCAN_Tx,
mod_id = x.Modulo_ID,
angulo = x.DirMotor.Angulo,
angulo_sp = x.DirMotor.Angulo_SP,
comandar = x.DirMotor.Comandar,
in1_atuado = x.DirMotor.IN1_Atuado,
sentido = x.DirMotor.Sentido,
sentido_sp = x.DirMotor.Sentido_SP,
sentido_real = x.DirMotor.SentidoReal,
freq = new
{
io = x.DirMotor.Leitura?.NumeroPulsos?.frequencia ?? 0,
pulsos = x.DirMotor.Leitura?.EntradaIN1?.frequencia ?? 0,
}
}).ToArray(),
Mov = Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Select(x => new
{
conectado = x.MovMotor.Inicializado,
freq_base = Variaveis.OperacaoEmAndamento.DispMvd.Dados.tmrRegistradorMOV.IntervalHz,
endereco = x.MovMotor._EnderecoCAN_Tx,
mod_id = x.Modulo_ID,
cod_erro = x.MovMotor.CodigoAlarme,
rpm = x.MovMotor.RPM_Roda,
rpm_sp = x.MovMotor.RPM_SP_Leitura,
tensao = x.MovMotor.Tensao,
corrente_motor = x.MovMotor.Corrente_Motor,
corrente_barramento = x.MovMotor.Corrente_Barramento,
temperatura_driver = x.MovMotor.TemperaturaDriver,
temperatura_motor = x.MovMotor.Temperatura,
freado = x.MovMotor.Leitura?.Freado ?? false,
freq = new
{
erro = x.MovMotor.Leitura?.CodigoAlarme?.frequencia ?? 0,
rpm = x.MovMotor.Leitura?.ERPM?.frequencia ?? 0,
tensao = x.MovMotor.Leitura?.Tensao?.frequencia ?? 0,
corrente_motor = x.MovMotor.Leitura?.CorrenteMotor?.frequencia ?? 0,
corrente_barramento = x.MovMotor.Leitura?.CorrenteBarramento?.frequencia ?? 0,
temperatura = x.MovMotor.Leitura?.Temperatura?.frequencia ?? 0,
}
}).ToArray(),
Sen = new
{
conectado = Variaveis.OperacaoEmAndamento.DispSen.Dados.Conectado,
freq_base = Variaveis.OperacaoEmAndamento.DispSen.Dados.tmrRegistrador.IntervalHz,
sensores = Variaveis.OperacaoEmAndamento.DispSen.Dados.Sensores.Select(x => new
{
conectado = x.Inicializado,
id = x.ID_Num,
label = x.ID,
tipo = (int)x.Componente,
aferir = x.Aferir,
mandatorio = x.Mandatorio,
dados = x.ValoresLeituras.Where(y => y.analise_health).Select(y => new
{
label = y.funcao.ToString(),
nominal = y.nominal,
maximo = y.maximo,
minimo = y.minimo,
atual = y.atual.valor,
freq = y.atual.frequencia
}),
}).ToArray(),
servos = Variaveis.OperacaoEmAndamento.DispSen.Dados.Servos.Select(x => new
{
conectado = x.Inicializado,
id = x.ID_Num,
label = x.ID,
controlar = x.Controlar,
mandatorio = x.Mandatorio,
angulo_sp = x._AnguloControle,
dados = x.ValoresLeituras.Where(y => y.analise_health).Select(y => new
{
label = y.funcao.ToString(),
atual = y.atual.valor,
freq = y.atual.frequencia
}),
}).ToArray(),
},
};
var comando = new
{
cmd = (int)HealthWorkerCommandType.SetDadosModulo,
@params = dados
};
RedisService.Publish(CmdKey.HealthWorkerRx, JsonConvert.SerializeObject(comando));
}
} }
} }

View File

@ -2,6 +2,7 @@
using AgroBase.Models.Operadores; using AgroBase.Models.Operadores;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using static AgroBase.Models.Operadores.OperadoresModels; using static AgroBase.Models.Operadores.OperadoresModels;
@ -32,6 +33,8 @@ namespace AgroBase.Services.Operadores
topicoRx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Rx, true, 2, MqttCallback); topicoRx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Rx, true, 2, MqttCallback);
topicoTx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Tx); topicoTx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Tx);
RedisService.Subscribe(CmdKey.ManagerWorkerTx, RedisCallback);
DadosLeitura.IniciadoEm = DateTime.Now; DadosLeitura.IniciadoEm = DateTime.Now;
DadosLeitura.Iniciado = true; DadosLeitura.Iniciado = true;
@ -73,6 +76,59 @@ namespace AgroBase.Services.Operadores
return true; return true;
} }
private void RedisCallback(string mensagem)
{
try
{
var comando = JsonConvert.DeserializeObject<ComandoRedis>(mensagem);
var cmd = (ManagerWorkerCommandType)comando.cmd;
MostrarLog($"📥 Comando recebido: cmd = {cmd.ToString()}");
switch (cmd)
{
case ManagerWorkerCommandType.EnviarDadosControle:
if (Variaveis.OperacaoEmAndamento.Modo == Enums.ModoOperacao.MapaGPS)
{
var _Controle = Variaveis.OperacaoEmAndamento.Controle;
var novoComando = new ManagerWorkerMessageResponseComandoModel()
{
angulo = Convert.ToDouble(comando.@params["angulo"]),
percentual_velocidade = Convert.ToDouble(comando.@params["percentual_velocidade"]),
tipo_movimento = (Enums.TipoMovimentoDirecional)Convert.ToInt32(comando.@params["tipo_movimento"]),
simulacao = JsonConvert.DeserializeObject<List<double[]>>(comando.@params["simulacao"].ToString())
};
if (novoComando != null)
{
_Controle.Angulo = novoComando.angulo;
_Controle.TipoMovimento = novoComando.tipo_movimento;
_Controle.PercentualVelocidadeSP = novoComando.percentual_velocidade;
_Controle.SimulacaoMPC = novoComando.simulacao.Select(x => new MPCSimulacaoModel() { latitude = x[0], longitude = x[1] }).ToList();
Variaveis.OperacaoEmAndamento.AtualizaInformacoesControleOperacao();
}
if (Variaveis.OperacaoEmAndamento.StatusAtual == Enums.StatusOperacao.Concluido && Variaveis.OperacaoEmAndamento.Iniciado)
{
Task.Run(async () =>
{
await Variaveis.OperacaoEmAndamento.FinalizarOperacao();
});
}
}
break;
default:
MostrarLog($"⚠️ Comando não reconhecido: {comando.cmd}");
break;
}
}
catch (Exception ex)
{
MostrarLog($"❌ Erro ao deserializar resposta: {ex.Message}");
}
}
private void AtualizarDadosLeitura(WorkerMessageModel<ManagerWorkerMessageResponseModel> msg) private void AtualizarDadosLeitura(WorkerMessageModel<ManagerWorkerMessageResponseModel> msg)
{ {
Variaveis.OperacaoEmAndamento.TempoAguardando = DateTimeOffset.FromUnixTimeSeconds(msg.obj.tempo_aguardando).UtcDateTime.AddSeconds(msg.obj.tempo_aguardando % 1).ToLocalTime(); Variaveis.OperacaoEmAndamento.TempoAguardando = DateTimeOffset.FromUnixTimeSeconds(msg.obj.tempo_aguardando).UtcDateTime.AddSeconds(msg.obj.tempo_aguardando % 1).ToLocalTime();
@ -90,12 +146,14 @@ namespace AgroBase.Services.Operadores
if (Variaveis.OperacaoEmAndamento.Modo == Enums.ModoOperacao.MapaGPS) if (Variaveis.OperacaoEmAndamento.Modo == Enums.ModoOperacao.MapaGPS)
{ {
var _Controle = Variaveis.OperacaoEmAndamento.Controle; var _Controle = Variaveis.OperacaoEmAndamento.Controle;
_Controle.Angulo = msg.obj.comando.angulo; if (msg.obj.comando != null)
_Controle.TipoMovimento = msg.obj.comando.tipo_movimento; {
_Controle.PercentualVelocidadeSP = msg.obj.comando.percentual_velocidade; _Controle.Angulo = msg.obj.comando.angulo;
_Controle.SimulacaoMPC = msg.obj.comando.simulacao.Select(x => new MPCSimulacaoModel() { latitude = x[0], longitude = x[1] }).ToList(); _Controle.TipoMovimento = msg.obj.comando.tipo_movimento;
_Controle.PercentualVelocidadeSP = msg.obj.comando.percentual_velocidade;
Variaveis.OperacaoEmAndamento.AtualizaInformacoesControleOperacao(); _Controle.SimulacaoMPC = msg.obj.comando.simulacao.Select(x => new MPCSimulacaoModel() { latitude = x[0], longitude = x[1] }).ToList();
Variaveis.OperacaoEmAndamento.AtualizaInformacoesControleOperacao();
}
if (Variaveis.OperacaoEmAndamento.StatusAtual == Enums.StatusOperacao.Concluido && Variaveis.OperacaoEmAndamento.Iniciado) if (Variaveis.OperacaoEmAndamento.StatusAtual == Enums.StatusOperacao.Concluido && Variaveis.OperacaoEmAndamento.Iniciado)
{ {

View File

@ -39,6 +39,8 @@ namespace AgroBase.Services.Operadores
topicoRx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Rx, true, 2, MqttCallback); topicoRx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Rx, true, 2, MqttCallback);
topicoTx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Tx); topicoTx = await Variaveis.MqttService.AdicionarNovoTopico(TopicoMQTT_Tx);
RedisService.Subscribe(CmdKey.VisualWorkerTx, RedisCallback);
mapaRespostas = new Dictionary<VisualWorkerCommandType, Func<bool>> mapaRespostas = new Dictionary<VisualWorkerCommandType, Func<bool>>
{ {
{ VisualWorkerCommandType.SalvarFrames, () => DadosLeitura.Leitura.obj?.save_frames?.newData ?? false }, { VisualWorkerCommandType.SalvarFrames, () => DadosLeitura.Leitura.obj?.save_frames?.newData ?? false },
@ -137,6 +139,33 @@ namespace AgroBase.Services.Operadores
return true; return true;
} }
private void RedisCallback(string mensagem)
{
try
{
var comando = JsonConvert.DeserializeObject<ComandoRedis>(mensagem);
var cmd = (VisualWorkerCommandType)comando.cmd;
MostrarLog($"📥 Comando recebido: cmd = {cmd.ToString()}");
switch (cmd)
{
case VisualWorkerCommandType.GetCameraFrame:
var frameModel = JsonConvert.DeserializeObject<OAKCameraFrameModel>(comando.@params.ToString());
break;
default:
MostrarLog($"⚠️ Comando não reconhecido: {comando.cmd}");
break;
}
}
catch (Exception ex)
{
MostrarLog($"❌ Erro ao deserializar resposta: {ex.Message}");
}
}
private void AtualizarDadosLeitura(WorkerMessageModel<VisualWorkerMessageResponseModel> msg) private void AtualizarDadosLeitura(WorkerMessageModel<VisualWorkerMessageResponseModel> msg)
{ {
if (DadosLeitura.Leitura.obj == null) if (DadosLeitura.Leitura.obj == null)

View File

@ -0,0 +1,70 @@
using StackExchange.Redis;
using System;
using System.Collections.Generic;
namespace AgroBase.Services
{
public class RedisService
{
private static ConnectionMultiplexer _redis;
private static IDatabase _db;
public static void Iniciar(string conexao = "localhost")
{
_redis = ConnectionMultiplexer.Connect(conexao);
_db = _redis.GetDatabase();
}
public static string Get(string chave)
{
return _db.StringGet(chave);
}
public static void Set(string chave, string valor)
{
_db.StringSet(chave, valor);
}
public static void Publish(string canal, string mensagem)
{
var pub = _redis.GetSubscriber();
pub.Publish(canal, mensagem);
}
public static void Subscribe(string canal, Action<string> callback)
{
var sub = _redis.GetSubscriber();
sub.Subscribe(canal, (ch, msg) => {
callback(msg);
});
}
}
public class ComandoRedis
{
public int cmd { get; set; }
public Dictionary<string, object> @params { get; set; }
}
public static class CtxKey
{
public const string DadosCameras = "ctx:dados_cameras";
public const string DadosModulo = "ctx:dados_modulo_";
public const string DadosOperacao = "ctx:dados_operacao";
public const string DadosContexto = "ctx:dados_contexto";
public const string DadosControle = "ctx:dados_controle";
public const string DadosVisualWorker = "ctx:dados_visual_worker";
}
public static class CmdKey
{
public const string VisualWorkerRx = "cmd_rx:visual_worker";
public const string VisualWorkerTx = "cmd_tx:visual_worker";
public const string HealthWorkerRx = "cmd_rx:health_worker";
public const string HealthWorkerTx = "cmd_tx:health_worker";
public const string CameraWorkerRx = "cmd_rx:camera_worker";
public const string CameraWorkerTx = "cmd_tx:camera_worker";
public const string ManagerWorkerRx = "cmd_rx:manager_worker";
public const string ManagerWorkerTx = "cmd_tx:manager_worker";
}
}

View File

@ -22,5 +22,5 @@
"top_topics_and_observing_domains": [ ] "top_topics_and_observing_domains": [ ]
} ], } ],
"hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642", "hex_encoded_hmac_key": "40F346D3248C3AFDF2BEE1FE496DBD32F7CED6E5AE98B881ABC421AA7E7B5642",
"next_scheduled_calculation_time": "13395777469290834" "next_scheduled_calculation_time": "13395777469290985"
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,3 +1,3 @@
2025/06/26-10:44:24.216 8088 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001 2025/06/27-20:56:49.222 2104 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
2025/06/26-10:44:24.223 8088 Recovering log #3 2025/06/27-20:56:49.231 2104 Recovering log #3
2025/06/26-10:44:24.226 8088 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log 2025/06/27-20:56:49.234 2104 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log

View File

@ -1,3 +1,3 @@
2025/06/26-10:22:26.943 6078 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001 2025/06/27-07:21:30.670 746c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/MANIFEST-000001
2025/06/26-10:22:26.952 6078 Recovering log #3 2025/06/27-07:21:30.676 746c Recovering log #3
2025/06/26-10:22:26.956 6078 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log 2025/06/27-07:21:30.679 746c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Local Storage\leveldb/000003.log

View File

@ -1 +1 @@
{"net":{"http_server_properties":{"servers":[{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13395505488076776","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":7496},"server":"https://tile.openstreetmap.org","supports_spdy":true}],"supports_quic":{"address":"2804:d78:6d4:dc00:395e:1fef:8c2d:dcbe","used_quic":true},"version":5},"network_qualities":{"CAASABiAgICA+P////8B":"4G","CAESABiAgICA+P////8B":"4G","CAISABiAgICA+P////8B":"4G","CAYSABiAgICA+P////8B":"Offline"}}} {"net":{"http_server_properties":{"servers":[{"alternative_service":[{"advertised_alpns":["h3"],"expiration":"13395628615843983","port":443,"protocol_str":"quic"}],"anonymization":["DAAAAAcAAABmaWxlOi8vAA==",false,0],"network_stats":{"srtt":7016},"server":"https://tile.openstreetmap.org","supports_spdy":true}],"supports_quic":{"address":"2804:d78:6d4:dc00:ad47:84dd:a027:10ff","used_quic":true},"version":5},"network_qualities":{"CAASABiAgICA+P////8B":"4G","CAESABiAgICA+P////8B":"4G","CAISABiAgICA+P////8B":"4G","CAYSABiAgICA+P////8B":"Offline"}}}

View File

@ -1 +1 @@
{"sts":[{"expiry":1782481467.393117,"host":"bWGAftl61rqoc0YzqPncsLvQQh/iC2Bdp3ejUeGC83w=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1750945467.393127}],"version":2} {"sts":[{"expiry":1782604614.73,"host":"bWGAftl61rqoc0YzqPncsLvQQh/iC2Bdp3ejUeGC83w=","mode":"force-https","sts_include_subdomains":true,"sts_observed":1751068614.730003}],"version":2}

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
2025/06/26-15:18:11.205 8088 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001 2025/06/27-20:57:03.060 2104 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
2025/06/26-15:18:11.207 8088 Recovering log #3 2025/06/27-20:57:03.061 2104 Recovering log #3
2025/06/26-15:18:11.210 8088 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log 2025/06/27-20:57:03.064 2104 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log

View File

@ -1,3 +1,3 @@
2025/06/26-10:44:15.822 6078 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001 2025/06/27-07:56:02.137 746c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/MANIFEST-000001
2025/06/26-10:44:15.823 6078 Recovering log #3 2025/06/27-07:56:02.138 746c Recovering log #3
2025/06/26-10:44:15.829 6078 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log 2025/06/27-07:56:02.141 746c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Session Storage/000003.log

View File

@ -1,3 +1,3 @@
2025/06/26-10:44:24.143 1508 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001 2025/06/27-20:56:49.144 3264 Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
2025/06/26-10:44:24.145 1508 Recovering log #3 2025/06/27-20:56:49.146 3264 Recovering log #3
2025/06/26-10:44:24.146 1508 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000003.log 2025/06/27-20:56:49.146 3264 Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000003.log

View File

@ -1,3 +1,3 @@
2025/06/26-10:22:26.868 205c Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001 2025/06/27-07:21:30.596 aec Reusing MANIFEST C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/MANIFEST-000001
2025/06/26-10:22:26.870 205c Recovering log #3 2025/06/27-07:21:30.597 aec Recovering log #3
2025/06/26-10:22:26.870 205c Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000003.log 2025/06/27-07:21:30.598 aec Reusing old log C:\ZendionInc\agrobot_base\AgroBase\AgroBase\bin\x64\Debug\AgroBase.exe.WebView2\EBWebView\Default\Site Characteristics Database/000003.log

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,14 @@
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
</configuration> </configuration>

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@
<meta name="viewport" content="width=device-width, <meta name="viewport" content="width=device-width,
initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<style> <style>
#map_8cbbfc19e2a4c0a6eea90701b0e88f16 { #map_ff161c1ccb57bb1593d13e6005bc17f7 {
position: relative; position: relative;
width: 100.0%; width: 100.0%;
height: 100.0%; height: 100.0%;
@ -54,14 +54,14 @@
<body> <body>
<div class="folium-map" id="map_8cbbfc19e2a4c0a6eea90701b0e88f16" ></div> <div class="folium-map" id="map_ff161c1ccb57bb1593d13e6005bc17f7" ></div>
</body> </body>
<script> <script>
var map_8cbbfc19e2a4c0a6eea90701b0e88f16 = L.map( var map_ff161c1ccb57bb1593d13e6005bc17f7 = L.map(
"map_8cbbfc19e2a4c0a6eea90701b0e88f16", "map_ff161c1ccb57bb1593d13e6005bc17f7",
{ {
center: [-22.172636164916668, -47.395186322185666], center: [-22.172636164916668, -47.395186322185666],
crs: L.CRS.EPSG3857, crs: L.CRS.EPSG3857,
@ -78,7 +78,7 @@
var tile_layer_9013db21f6eca3782c27866d5a1afa86 = L.tileLayer( var tile_layer_fb8295ca92bb6af06fdd24fd2b872328 = L.tileLayer(
"https://tile.openstreetmap.org/{z}/{x}/{y}.png", "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
{ {
"minZoom": 0, "minZoom": 0,
@ -95,7 +95,7 @@
); );
tile_layer_9013db21f6eca3782c27866d5a1afa86.addTo(map_8cbbfc19e2a4c0a6eea90701b0e88f16); tile_layer_fb8295ca92bb6af06fdd24fd2b872328.addTo(map_ff161c1ccb57bb1593d13e6005bc17f7);
@ -111,7 +111,7 @@
}*/ }*/
}); });
} }
function geo_json_9504ddffaf9242962edc59fd8f534970_onEachFeature(feature, layer) { function geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb_onEachFeature(feature, layer) {
layer.on({ layer.on({
@ -148,23 +148,23 @@
}*/ }*/
}); });
}; };
var geo_json_9504ddffaf9242962edc59fd8f534970 = L.geoJson(null, { var geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb = L.geoJson(null, {
onEachFeature: geo_json_9504ddffaf9242962edc59fd8f534970_onEachFeature, onEachFeature: geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb_onEachFeature,
...{ ...{
} }
}); });
function geo_json_9504ddffaf9242962edc59fd8f534970_add (data) { function geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb_add (data) {
geo_json_9504ddffaf9242962edc59fd8f534970 geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb
.addData(data); .addData(data);
} }
geo_json_9504ddffaf9242962edc59fd8f534970_add({"features": [{"geometry": {"coordinates": [[-47.395205344, -22.172559531333334], [-47.395212610166666, -22.172614638833334], [-47.395219157, -22.172656417833334], [-47.395223544833335, -22.1726892105], [-47.39522414098443, -22.17269369161165], [-47.395225326538004, -22.172702654611555]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "1", "Length": 14.558011415731592, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.39521090233333, -22.172708800833334], [-47.395206943666665, -22.172679811], [-47.395202420666664, -22.1726491015], [-47.395198865666664, -22.172615782833333], [-47.395193255833334, -22.172577132833332], [-47.395192610024395, -22.17257265769541], [-47.395191325698136, -22.172563706509337]], "id": null, "type": "LineString"}, "id": 1, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "2", "Length": 14.771318274761821, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.3951762925, -22.172561603], [-47.395180824166665, -22.172591686166665], [-47.395185745333336, -22.172623080166666], [-47.395190433, -22.172656367166667], [-47.395195199, -22.172693641833334], [-47.39519576904436, -22.172698125891035], [-47.39519690267159, -22.172707094716973]], "id": null, "type": "LineString"}, "id": 2, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "3", "Length": 14.827915524386164, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.39518293216667, -22.1727127985], [-47.39517819266667, -22.172680514833335], [-47.3951732595, -22.172646752833334], [-47.39516880516667, -22.1726149155], [-47.39516381233334, -22.172581167166665], [-47.39516315429932, -22.172576693573966], [-47.39516184565584, -22.172567745443878]], "id": null, "type": "LineString"}, "id": 3, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "4", "Length": 14.785159385903514, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.395147317833334, -22.172566031], [-47.395151503166666, -22.172595452333333], [-47.3951561855, -22.172628011166665], [-47.39516159866667, -22.172663757333332], [-47.39516672716667, -22.172697770833334], [-47.395167397572706, -22.172702242832194], [-47.39516873082615, -22.17271118781009]], "id": null, "type": "LineString"}, "id": 4, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "5", "Length": 14.80117692191233, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}], "type": "FeatureCollection"}); geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb_add({"features": [{"geometry": {"coordinates": [[-47.395205344, -22.172559531333334], [-47.395212610166666, -22.172614638833334], [-47.395219157, -22.172656417833334], [-47.395223544833335, -22.1726892105], [-47.39522414098443, -22.17269369161165], [-47.395225326538004, -22.172702654611555]], "id": null, "type": "LineString"}, "id": 0, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "1", "Length": 14.558011415731592, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.39521090233333, -22.172708800833334], [-47.395206943666665, -22.172679811], [-47.395202420666664, -22.1726491015], [-47.395198865666664, -22.172615782833333], [-47.395193255833334, -22.172577132833332], [-47.395192610024395, -22.17257265769541], [-47.395191325698136, -22.172563706509337]], "id": null, "type": "LineString"}, "id": 1, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "2", "Length": 14.771318274761821, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.3951762925, -22.172561603], [-47.395180824166665, -22.172591686166665], [-47.395185745333336, -22.172623080166666], [-47.395190433, -22.172656367166667], [-47.395195199, -22.172693641833334], [-47.39519576904436, -22.172698125891035], [-47.39519690267159, -22.172707094716973]], "id": null, "type": "LineString"}, "id": 2, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "3", "Length": 14.827915524386164, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.39518293216667, -22.1727127985], [-47.39517819266667, -22.172680514833335], [-47.3951732595, -22.172646752833334], [-47.39516880516667, -22.1726149155], [-47.39516381233334, -22.172581167166665], [-47.39516315429932, -22.172576693573966], [-47.39516184565584, -22.172567745443878]], "id": null, "type": "LineString"}, "id": 3, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "4", "Length": 14.785159385903514, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}, {"geometry": {"coordinates": [[-47.395147317833334, -22.172566031], [-47.395151503166666, -22.172595452333333], [-47.3951561855, -22.172628011166665], [-47.39516159866667, -22.172663757333332], [-47.39516672716667, -22.172697770833334], [-47.395167397572706, -22.172702242832194], [-47.39516873082615, -22.17271118781009]], "id": null, "type": "LineString"}, "id": 4, "properties": {"Dist1": 14.558011415731592, "Dist2": 0.0, "Id": "5", "Length": 14.80117692191233, "Name": "CidadeJardimTerreno2"}, "type": "Feature"}], "type": "FeatureCollection"});
geo_json_9504ddffaf9242962edc59fd8f534970.setStyle(function(feature) {return feature.properties.style;}); geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb.setStyle(function(feature) {return feature.properties.style;});
geo_json_9504ddffaf9242962edc59fd8f534970.addTo(map_8cbbfc19e2a4c0a6eea90701b0e88f16); geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb.addTo(map_ff161c1ccb57bb1593d13e6005bc17f7);
</script> </script>
@ -185,7 +185,7 @@
} }
trajeto_json_add({"features": []}); trajeto_json_add({"features": []});
trajeto_json.addTo(map_8cbbfc19e2a4c0a6eea90701b0e88f16); trajeto_json.addTo(map_ff161c1ccb57bb1593d13e6005bc17f7);
function adicionarGeometria(novaGeometria) { function adicionarGeometria(novaGeometria) {
trajeto_json.addData(novaGeometria); trajeto_json.addData(novaGeometria);
@ -243,7 +243,7 @@
} }
trajeto_dinamico_json_add({"features": []}); trajeto_dinamico_json_add({"features": []});
trajeto_dinamico_json.addTo(map_8cbbfc19e2a4c0a6eea90701b0e88f16); trajeto_dinamico_json.addTo(map_ff161c1ccb57bb1593d13e6005bc17f7);
function adicionarGeometriaDinamica(novaGeometria) { function adicionarGeometriaDinamica(novaGeometria) {
trajeto_dinamico_json.addData(novaGeometria); trajeto_dinamico_json.addData(novaGeometria);
@ -296,9 +296,9 @@
var marcadorEquipamento = L.marker([0, 0], { var marcadorEquipamento = L.marker([0, 0], {
icon: customIcon icon: customIcon
}).addTo(map_8cbbfc19e2a4c0a6eea90701b0e88f16); }).addTo(map_ff161c1ccb57bb1593d13e6005bc17f7);
var marcadorBase = L.marker([0, 0], {}).addTo(map_8cbbfc19e2a4c0a6eea90701b0e88f16); var marcadorBase = L.marker([0, 0], {}).addTo(map_ff161c1ccb57bb1593d13e6005bc17f7);
var icon = L.AwesomeMarkers.icon( var icon = L.AwesomeMarkers.icon(
{"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"} {"extraClasses": "fa-rotate-0", "icon": "info-sign", "iconColor": "white", "markerColor": "red", "prefix": "glyphicon"}
); );
@ -380,7 +380,7 @@
} }
if (foco) { if (foco) {
map_8cbbfc19e2a4c0a6eea90701b0e88f16.setView(novaPosicao, map_8cbbfc19e2a4c0a6eea90701b0e88f16.getZoom()); map_ff161c1ccb57bb1593d13e6005bc17f7.setView(novaPosicao, map_ff161c1ccb57bb1593d13e6005bc17f7.getZoom());
} }
} }
@ -397,7 +397,7 @@
function atualizarSelecaoRuas(selecionadas) { function atualizarSelecaoRuas(selecionadas) {
selecionadas = JSON.parse(selecionadas); selecionadas = JSON.parse(selecionadas);
RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : []; RuasSelecionadas = Array.isArray(selecionadas) ? [...selecionadas] : [];
geo_json_9504ddffaf9242962edc59fd8f534970.eachLayer(function (layer) { geo_json_58ce871dcea3f9f4438ef7bdd99f0dcb.eachLayer(function (layer) {
if (RuasSelecionadas.includes(parseInt(layer.feature.id))) { if (RuasSelecionadas.includes(parseInt(layer.feature.id))) {
layer.setStyle({ color: 'blue' }); layer.setStyle({ color: 'blue' });
} else { } else {

View File

@ -4,6 +4,7 @@ import time
from health_worker.modulos.imu import IMUCamera from health_worker.modulos.imu import IMUCamera
from shared.enums import StatusModulo, T_Code from shared.enums import StatusModulo, T_Code
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
class CameraOak: class CameraOak:
def __init__(self, mostrar_log, dev_info): def __init__(self, mostrar_log, dev_info):
@ -11,7 +12,7 @@ class CameraOak:
self.dev_info = dev_info self.dev_info = dev_info
self.mx_id = dev_info.getMxId() self.mx_id = dev_info.getMxId()
self.versao = dev_info.name self.versao = dev_info.name
self.dispositivo = T_Code.Vzo self.dispositivo = T_Code.Vzo.value
self.modelo = "Desconhecido" self.modelo = "Desconhecido"
self.ultima_saude = None self.ultima_saude = None
self.timestamp_ultimo_frame_rgb = None self.timestamp_ultimo_frame_rgb = None
@ -19,55 +20,80 @@ class CameraOak:
self.tem_depth = False self.tem_depth = False
self.tem_imu = False self.tem_imu = False
self.rodando = False self.rodando = False
self.iniciado = False
_cameras = ContextoGlobalRedis.get(CtxKey.DadosCameras, {})
_camera = _cameras.get(self.mx_id, {})
_camera["versao"] = self.versao
_camera["iniciando"] = True
_camera["iniciado"] = False
_camera["rodando"] = False
_camera["parametros"] = {}
# Fase 1: detectar sensores sem pipeline # Fase 1: detectar sensores sem pipeline
try: try:
device_tmp = dai.Device(dev_info) device_tmp = dai.Device(dev_info)
self.tem_imu = "imu" in device_tmp.getOutputQueueNames()
sensores = device_tmp.getConnectedCameras() sensores = device_tmp.getConnectedCameras()
device_tmp.close() device_tmp.close()
if dai.CameraBoardSocket.LEFT in sensores and dai.CameraBoardSocket.RIGHT in sensores: if dai.CameraBoardSocket.LEFT in sensores and dai.CameraBoardSocket.RIGHT in sensores:
self.modelo = "OAK-D Lite" self.modelo = "OAK-D Lite"
self.dispositivo = T_Code.Snr self.dispositivo = T_Code.Snr.value
self.tem_depth = True self.tem_depth = True
elif dai.CameraBoardSocket.CAM_A in sensores: elif dai.CameraBoardSocket.CAM_A in sensores:
self.modelo = "OAK-1 Lite W" self.modelo = "OAK-1 Lite W"
self.dispositivo = T_Code.Cam self.dispositivo = T_Code.Cam.value
self.tem_depth = False self.tem_depth = False
else: else:
self.mostrar_log(f"[WARN] Sensores desconhecidos: {sensores}") self.mostrar_log(f"[WARN] Sensores desconhecidos: {sensores}")
except Exception as e: except Exception as e:
self.mostrar_log(f"[ERRO] Falha ao detectar sensores: {e}") pass
#self.mostrar_log(f"[ERRO] Falha ao detectar sensores: {e}")
_camera["modelo"] = self.modelo
_camera["dispositivo"] = self.dispositivo
_camera["tem_depht"] = self.tem_depth
_camera["tem_imu"] = self.tem_imu
# Fase 2: criar pipeline e instanciar normalmente # Fase 2: criar pipeline e instanciar normalmente
self.pipeline = self._criar_pipeline() try:
self.device = dai.Device(self.pipeline, self.dev_info) self.pipeline = self._criar_pipeline()
self.q_video = self.device.getOutputQueue(name="video", maxSize=1, blocking=False) self.device = dai.Device(self.pipeline, self.dev_info)
self.q_video = self.device.getOutputQueue(name="video", maxSize=1, blocking=False)
if self.tem_depth: if self.tem_depth:
self.q_depth = self.device.getOutputQueue(name="depth", maxSize=1, blocking=False) self.q_depth = self.device.getOutputQueue(name="depth", maxSize=1, blocking=False)
self.parametros = { self.parametros = {
"rgb_width": 1920, "rgb_width": 1920,
"rgb_height": 1080, "rgb_height": 1080,
"depth_width": 640, "depth_width": 640,
"depth_height": 400, "depth_height": 400,
"fx": 440.0, "fx": 440.0,
"fy": 440.0, "fy": 440.0,
"baseline": 0.075, "baseline": 0.075,
"fov_h": 1, "fov_h": 1,
"fov_v": 1, "fov_v": 1,
"qtd_setores_solo": 25, "qtd_setores_solo": 25,
"distancia_maxima": 3000, "distancia_maxima": 3000,
"percentual_altura_solo": 30, "percentual_altura_solo": 30,
} }
self._set_calib() self._set_calib(_camera)
_camera["parametros"] = self.parametros
if "imu" in self.device.getOutputQueueNames(): if self.tem_imu:
self.q_imu = self.device.getOutputQueue(name="imu", maxSize=50, blocking=False) self.q_imu = self.device.getOutputQueue(name="imu", maxSize=50, blocking=False)
self.imu = IMUCamera(self.q_imu) self.imu = IMUCamera(self.q_imu)
self.tem_imu = True
else: self.iniciado = True
self.tem_imu = False _camera["iniciado"] = True
except Exception as e:
pass
_camera["iniciando"] = False
ContextoGlobalRedis.set(CtxKey.DadosCameras, _cameras)
def _criar_pipeline(self): def _criar_pipeline(self):
pipeline = dai.Pipeline() pipeline = dai.Pipeline()
@ -83,7 +109,7 @@ class CameraOak:
self.mostrar_log(f"Pipeline rgb criado") self.mostrar_log(f"Pipeline rgb criado")
# DEPTH (somente se sensores presentes) # DEPTH (somente se sensores presentes)
if getattr(self, "tem_depth", False): if self.tem_depth:
try: try:
mono_left = pipeline.create(dai.node.MonoCamera) mono_left = pipeline.create(dai.node.MonoCamera)
mono_right = pipeline.create(dai.node.MonoCamera) mono_right = pipeline.create(dai.node.MonoCamera)
@ -108,7 +134,7 @@ class CameraOak:
self.mostrar_log(f"[WARN] Falha ao montar pipeline depth: {e}") self.mostrar_log(f"[WARN] Falha ao montar pipeline depth: {e}")
# IMU (somente se presente) # IMU (somente se presente)
if getattr(self, "tem_depth", False): if self.tem_imu:
try: try:
imu = pipeline.create(dai.node.IMU) imu = pipeline.create(dai.node.IMU)
imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, 60) imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, 60)
@ -181,19 +207,22 @@ class CameraOak:
"frame_valido": False "frame_valido": False
} }
def requisitar_imu_status(self):
if getattr(self, "tem_imu", False):
return self.imu.get_imu_status()
else:
return None
def atualizar_saude(self): def atualizar_saude(self):
#print("Atualizando saude") #print("Atualizando saude")
_cameras = ContextoGlobalRedis.get(CtxKey.DadosCameras, {})
_camera = _cameras.get(self.mx_id)
conectado = _camera is not None
frame, resultado = self.requisitar_frame_rgb() frame, resultado = self.requisitar_frame_rgb()
saude = 50 saude = 50
motivos = [] motivos = []
if not conectado:
saude = -100
if resultado["erro"]: if resultado["erro"]:
motivos.append(resultado["erro"]) motivos.append(resultado["erro"])
saude = 0 saude = 0
@ -206,17 +235,21 @@ class CameraOak:
saude -= 20 saude -= 20
motivos.append(f"Tempo elevado para captura: {resultado['duracao']:.2f}s") motivos.append(f"Tempo elevado para captura: {resultado['duracao']:.2f}s")
status = StatusModulo.OPERANTE saude = min(max(saude, 0), 100)
if saude <= 0:
status = StatusModulo.FALHA
elif saude < 80:
status = StatusModulo.ALERTA
from shared.contexto_global import ContextoGlobal status = StatusModulo.OPERANTE.value
ContextoGlobal.set_item_dado(self.dispositivo, "saude", "status", status) if saude <= 0:
ContextoGlobal.set_item_dado(self.dispositivo, "saude", "saude", saude) status = StatusModulo.FALHA.value
ContextoGlobal.set_item_dado(self.dispositivo, "saude", "motivos", motivos) elif saude < 80:
ContextoGlobal.set_item_dado(self.dispositivo, "saude", "saude_individual", []) status = StatusModulo.ALERTA.value
saude_geral = {
"conectado": conectado,
"status": status.value,
"saude": saude,
"motivos": motivos,
"saude_idividual": []
}
agora = time.time() agora = time.time()
timeout = 2.0 timeout = 2.0
@ -229,12 +262,16 @@ class CameraOak:
or (agora - ts_rgb) <= timeout or (agora - ts_rgb) <= timeout
) )
self.ultima_saude = { if conectado:
"status": status, _camera["saude"] = saude_geral
"saude": max(0, min(100, saude)), _camera["rodando"] = self.rodando
"motivos": motivos, disp = _camera.get("dispositivo", T_Code.Vzo.value)
"saude_individual": [] if disp != T_Code.Vzo.value:
} modulo = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(disp), {})
modulo["saude"] = saude_geral
ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(disp), modulo)
ContextoGlobalRedis.set(CtxKey.DadosCameras, _cameras)
def to_dict(self): def to_dict(self):
return { return {

View File

@ -3,15 +3,16 @@ import os
import time import time
import json import json
def main():
def main(multi_process):
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "CameraWorker") print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "CameraWorker")
from shared.mqtt_handler import iniciar_mqtt, registrar_callback, enviar_mensagem_mqtt, enviar_mensagem_script_carregado from shared.mqtt_handler import iniciar_mqtt, registrar_callback, enviar_mensagem_mqtt, enviar_mensagem_script_carregado
from shared.mensagem_worker import WorkerFilaMensagens from shared.mensagem_worker import WorkerFilaMensagens
from shared.enums import T_Code, TipoComando from shared.enums import T_Code, CameraWorkerCommandType
from camera_worker.config import mostrar_log, module_id, topico_tx, topico_rx, get_camera_manager from camera_worker.config import mostrar_log, module_id, topico_tx, topico_rx, get_camera_manager
from shared.contexto_global_redis import ContextoGlobalRedis, CmdKey, CtxKey
# 🔹 Callback que o MQTT chama # 🔹 Callback que o MQTT chama
def ao_receber_comando(topico, payload): def ao_receber_comando(topico, payload):
@ -23,7 +24,7 @@ def main():
# Aqui você decodifica o comando e processa normalmente # Aqui você decodifica o comando e processa normalmente
mensagem = json.loads(message.decode()) mensagem = json.loads(message.decode())
dispositivo = T_Code(mensagem.get("dispositivo")) dispositivo = T_Code(mensagem.get("dispositivo"))
tipo_comando = TipoComando(mensagem.get("comando")) tipo_comando = CameraWorkerCommandType(mensagem.get("comando"))
payload = mensagem.get("payload", {}) payload = mensagem.get("payload", {})
#mostrar_log(f"📨 Mensagem recebida no tópico {topico}. Comando: {tipo_comando.name}, dispositivo {dispositivo.name}, payload: {payload}") #mostrar_log(f"📨 Mensagem recebida no tópico {topico}. Comando: {tipo_comando.name}, dispositivo {dispositivo.name}, payload: {payload}")
@ -52,16 +53,26 @@ def main():
mostrar_log("🛑 Encerrando Camera Worker...") mostrar_log("🛑 Encerrando Camera Worker...")
break break
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro no loop do Manager: {e}") mostrar_log(f"❌ Erro no loop do Camera: {e}")
time.sleep(5) time.sleep(5)
def redis_callback(dados):
acao = CameraWorkerCommandType(dados.get("cmd", 0))
if False:
pass
else:
mostrar_log(f"⚠️ Comando desconhecido: {acao.name}")
# 🔹 Execução principal # 🔹 Execução principal
def inicializar(): def inicializar():
iniciar_mqtt(module_id) if multi_process:
iniciar_mqtt(module_id)
mostrar_log("🚀 Iniciando Camera Worker...") mostrar_log("🚀 Iniciando Camera Worker...")
registrar_callback(topico=topico_rx, callback=ao_receber_comando) registrar_callback(topico=topico_rx, callback=ao_receber_comando)
ContextoGlobalRedis.ouvir_comandos(CmdKey.CameraWorkerRx, redis_callback)
enviar_mensagem_script_carregado(topico_tx) enviar_mensagem_script_carregado(topico_tx)
loop_ativo() loop_ativo()

View File

@ -1,11 +1,10 @@
import time import time
import depthai as dai import depthai as dai
from .camera_oak import CameraOak from shared.enums import HealthWorkerCommandType, T_Code, VisualWorkerCommandType
from multiprocessing import Process, Queue from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey, CmdKey
class CameraManager: class CameraManager:
def __init__(self, mostrar_log): def __init__(self, mostrar_log):
self.cameras = []
self.mostrar_log = mostrar_log self.mostrar_log = mostrar_log
self._ultimo_scan = 0 self._ultimo_scan = 0
@ -16,68 +15,38 @@ class CameraManager:
try: try:
dispositivos = dai.Device.getAllAvailableDevices() dispositivos = dai.Device.getAllAvailableDevices()
dispositivos_mapeados = [d.getMxId() for d in dispositivos] dispositivos_serializados = [d.getMxId() for d in dispositivos]
except Exception as e: except Exception as e:
dispositivos_mapeados = [] dispositivos_serializados = []
if len(dispositivos_mapeados) == 0: cameras_mapeadas = ContextoGlobalRedis.get(CtxKey.DadosCameras, {})
self.mostrar_log("⚠️ Nenhum dispositivo retornado.")
dispositivos_serializados = []
else:
dispositivos_serializados = dispositivos_mapeados
novas_cameras = []
for mx_id in dispositivos_serializados: for mx_id in dispositivos_serializados:
existente = next((c for c in self.cameras if c.mx_id == mx_id), None) cam_existente = cameras_mapeadas.get(mx_id)
if not cam_existente:
if existente: cameras_mapeadas[mx_id] = {
existente.atualizar_saude() "timestamp": self._ultimo_scan,
novas_cameras.append(existente) "mx_id": mx_id,
"iniciado": False,
"dispositivo": T_Code.Vzo.value
}
else: else:
if not self._is_camera_em_uso(mx_id): cam_existente["timestamp"] = self._ultimo_scan
print("em uso: False")
try:
dev_info = dai.DeviceInfo(mx_id)
nova = CameraOak(self.mostrar_log, dev_info)
nova.atualizar_saude()
novas_cameras.append(nova)
except Exception as e:
self.mostrar_log(f"[ERRO] Falha ao iniciar camera {dev_info}: {e}")
else:
print("em uso: True")
if len(novas_cameras) > 0: mxids_ativos = set(dispositivos_serializados)
self.cameras = novas_cameras mxids_mapeados = set(cameras_mapeadas.keys())
mxids_removidos = mxids_mapeados - mxids_ativos
for cam in self.cameras: for mx_id in mxids_removidos:
cam.atualizar_saude() self.mostrar_log(f"🟥 Camera desconectada: {mx_id}")
cameras_mapeadas.pop(mx_id, None)
if len(self.cameras) > 0: ContextoGlobalRedis.set(CtxKey.DadosCameras, cameras_mapeadas)
a = 1
# enviar mensagem mqtt para tentar atualizar o camera manager agora que existe alguma camera mapeada
def _is_camera_em_uso(self, mx_id): for cam in cameras_mapeadas:
for c in self.cameras: if cam.get("dispositivo", T_Code.Vzo.value) == T_Code.Snr.value:
if c.mx_id == mx_id and c.rodando: ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerRx, { "cmd": VisualWorkerCommandType.AtualizarSaudeCamera.value } )
return True
return False
def get_cameras(self, mx_id = None): visual_worker_camera_id = ContextoGlobalRedis.get_operacao().get(T_Code.Snr.name, {}).get("mx_id")
if mx_id == None: if visual_worker_camera_id is not None and visual_worker_camera_id in dispositivos_serializados:
return self.cameras ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerRx, { "cmd": VisualWorkerCommandType.IniciarCameraManager.value, "params": visual_worker_camera_id } )
else:
camera = next((c for c in self.cameras if c.mx_id == mx_id), None)
return camera
def get_cameras_dict(self):
return [cam.to_dict() for cam in self.cameras]
def get_camera_frame(self, mx_id):
cam = next((c for c in self.cameras if c.mx_id == mx_id), None)
if cam != None:
frame, res = cam.requisitar_frame_rgb()
return frame, cam.timestamp_ultimo_frame_rgb, res
else:
return None, None, {}

View File

@ -1,6 +1,6 @@
import time import time
from health_worker.modulos.camera_manager import CameraManager #from health_worker.modulos.camera_manager import CameraManager
module_id = "health" module_id = "health"
topico_tx = f"operador/{module_id}/tx" topico_tx = f"operador/{module_id}/tx"
@ -11,7 +11,7 @@ def mostrar_log(mensagem):
if debug: if debug:
print(f"{time.time()} - [{module_id}] {mensagem}") print(f"{time.time()} - [{module_id}] {mensagem}")
manager = CameraManager(mostrar_log) #manager = CameraManager(mostrar_log)
def get_camera_manager(): #def get_camera_manager():
return manager # return manager

View File

@ -3,21 +3,19 @@ import os
import time import time
import json import json
def main(): def main(multi_process):
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "HealthWorker") print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "HealthWorker")
from shared.mqtt_handler import iniciar_mqtt, registrar_callback, enviar_mensagem_mqtt, enviar_mensagem_script_carregado from shared.mqtt_handler import iniciar_mqtt, registrar_callback, enviar_mensagem_mqtt, enviar_mensagem_script_carregado
from shared.mensagem_worker import WorkerFilaMensagens from shared.mensagem_worker import WorkerFilaMensagens
from shared.enums import T_Code, TipoComando from shared.enums import T_Code, HealthWorkerCommandType
from shared.contexto_global import ContextoGlobal
from health_worker.modulos.gps import ModuloGPS from health_worker.modulos.gps import ModuloGPS
from health_worker.modulos.direcional import ModuloDirecional from health_worker.modulos.direcional import ModuloDirecional
from health_worker.modulos.movimentacao import ModuloMovimentacao from health_worker.modulos.movimentacao import ModuloMovimentacao
from health_worker.modulos.sensoriamento import ModuloSensoriamento from health_worker.modulos.sensoriamento import ModuloSensoriamento
from health_worker.config import mostrar_log, module_id, topico_tx, topico_rx, get_camera_manager from health_worker.config import mostrar_log, module_id, topico_tx, topico_rx
from shared.contexto_global_redis import ContextoGlobalRedis, CmdKey, CtxKey
modulos = { modulos = {
T_Code.Gps: ModuloGPS(), T_Code.Gps: ModuloGPS(),
@ -36,7 +34,7 @@ def main():
# Aqui você decodifica o comando e processa normalmente # Aqui você decodifica o comando e processa normalmente
mensagem = json.loads(message.decode()) mensagem = json.loads(message.decode())
dispositivo = T_Code(mensagem.get("dispositivo")) dispositivo = T_Code(mensagem.get("dispositivo"))
tipo_comando = TipoComando(mensagem.get("comando")) tipo_comando = HealthWorkerCommandType(mensagem.get("comando"))
payload = mensagem.get("payload", {}) payload = mensagem.get("payload", {})
#mostrar_log(f"📨 Mensagem recebida no tópico {topico}. Comando: {tipo_comando.name}, dispositivo {dispositivo.name}, payload: {payload}") #mostrar_log(f"📨 Mensagem recebida no tópico {topico}. Comando: {tipo_comando.name}, dispositivo {dispositivo.name}, payload: {payload}")
@ -44,56 +42,6 @@ def main():
resposta = {} resposta = {}
if tipo_comando in (TipoComando.GetDadosModulo, TipoComando.SetDadosModulo):
if dispositivo in modulos:
modulo = modulos[dispositivo]
if tipo_comando == TipoComando.SetDadosModulo:
modulo.atualizar_status(payload)
elif tipo_comando == TipoComando.GetDadosModulo:
resposta = modulo.verificar_status()
elif tipo_comando == TipoComando.SetDadosContexto:
ContextoGlobal.atualizar_contexto(payload)
#mostrar_log("Dados de Contexto atualizados com sucesso!")
for chave, dados_modulo in payload.items():
if chave not in T_Code.__members__:
#mostrar_log(f"🔎 Ignorando chave '{chave}' (não é módulo do T_Code)")
continue
try:
t_code = T_Code[chave]
if t_code in modulos:
modulos[t_code].atualizar_status(dados_modulo)
except Exception as e:
mostrar_log(f"❌ Erro ao atualizar módulo {chave}: {e}")
elif tipo_comando == TipoComando.GetDadosContexto:
resposta = ContextoGlobal.get_tudo()
elif tipo_comando == TipoComando.SetDadosOperacao:
ContextoGlobal.atualizar_operacao(payload)
mostrar_log("Dados da Operacao atualizados com sucesso!")
elif tipo_comando == TipoComando.GetListaCameras:
manager = get_camera_manager()
cameras = manager.get_cameras_dict()
resposta = {
"cameras": {
"timestamp": time.time(),
"itens": cameras
}
}
elif tipo_comando == TipoComando.GetCameraFrame:
camera_id = payload.get("dados", {}).get("mx_id", "")
manager = get_camera_manager()
frame, timestamp, res = manager.get_camera_frame(camera_id)
base64_img = None
if frame is not None:
from shared.utils import encode_image_base64
base64_img = encode_image_base64(frame)
resposta = {
"frame": {
"timestamp": timestamp,
"frame": base64_img
}
}
if (resposta != {}): if (resposta != {}):
resposta["cmd"] = tipo_comando resposta["cmd"] = tipo_comando
enviar_mensagem_mqtt(topico=topico_tx, dispositivo=dispositivo,objeto=resposta) enviar_mensagem_mqtt(topico=topico_tx, dispositivo=dispositivo,objeto=resposta)
@ -106,10 +54,11 @@ def main():
# 🔹 Loop principal do Manager # 🔹 Loop principal do Manager
def loop_ativo(): def loop_ativo():
manager = get_camera_manager() #manager = get_camera_manager()
while True: while True:
try: try:
manager.atualizar_cameras() pass
#manager.atualizar_cameras()
#cameras = manager.get_cameras() #cameras = manager.get_cameras()
#mostrar_log([f"Camera: {cam.modelo}, ID: {cam.mx_id}, Saude: {cam.ultima_saude}" for cam in cameras]) #mostrar_log([f"Camera: {cam.modelo}, ID: {cam.mx_id}, Saude: {cam.ultima_saude}" for cam in cameras])
@ -121,11 +70,42 @@ def main():
time.sleep(5) time.sleep(5)
def redis_callback(dados):
acao = HealthWorkerCommandType(dados.get("cmd", 0))
if acao == HealthWorkerCommandType.SetDadosModulo:
_modulos = dados.get("params")
if _modulos:
for chave, dados_modulo in _modulos.items():
if chave not in T_Code.__members__:
#mostrar_log(f"🔎 Ignorando chave '{chave}' (não é módulo do T_Code)")
continue
try:
t_code = T_Code[chave]
if t_code in modulos:
modulos[t_code].atualizar_status(dados_modulo)
except Exception as e:
mostrar_log(f"❌ Erro ao atualizar módulo {chave}: {e}")
elif acao == HealthWorkerCommandType.SetDadosContexto:
_contexto = dados.get("params")
if _contexto:
ContextoGlobalRedis.set(CtxKey.DadosContexto, _contexto)
ContextoGlobalRedis.atualizar_dados_operacao()
elif acao == HealthWorkerCommandType.SetDadosOperacao:
_operacao = dados.get("params")
if _operacao:
ContextoGlobalRedis.set(CtxKey.DadosOperacao, _operacao)
ContextoGlobalRedis.atualizar_dados_operacao()
mostrar_log("Dados da Operacao atualizados com sucesso!")
# 🔹 Execução principal # 🔹 Execução principal
def inicializar(): def inicializar():
iniciar_mqtt(module_id) if multi_process:
iniciar_mqtt(module_id)
mostrar_log("🚀 Iniciando Health Worker...") mostrar_log("🚀 Iniciando Health Worker...")
registrar_callback(topico=topico_rx, callback=ao_receber_comando) registrar_callback(topico=topico_rx, callback=ao_receber_comando)
ContextoGlobalRedis.ouvir_comandos(CmdKey.HealthWorkerRx, redis_callback)
enviar_mensagem_script_carregado(topico_tx) enviar_mensagem_script_carregado(topico_tx)
loop_ativo() loop_ativo()

View File

@ -61,7 +61,7 @@ class CameraManager:
if len(self.cameras) > 0: if len(self.cameras) > 0:
from shared.mqtt_handler import enviar_mensagem_visual_worker from shared.mqtt_handler import enviar_mensagem_visual_worker
enviar_mensagem_visual_worker(VisualWorkerCommandType.ATUALIZAR_CAMERA_MANAGER.value, {}) enviar_mensagem_visual_worker(VisualWorkerCommandType.AtualizarCameraManager)
def _is_camera_em_uso(self, mx_id): def _is_camera_em_uso(self, mx_id):
for c in self.cameras: for c in self.cameras:

View File

@ -1,42 +1,31 @@
import time import time
from shared.enums import T_Code, Sentido, StatusModulo from shared.enums import T_Code, Sentido, StatusModulo
from shared.utils import penalizar_frequencia from shared.utils import penalizar_frequencia
from shared.contexto_global import ContextoGlobal from shared.contexto_global_redis import ContextoGlobalRedis
from health_worker.modulos.base import ModuloDiagnosticoBase, resposta from health_worker.modulos.base import ModuloDiagnosticoBase
class StatusDirecional:
def __init__(self):
self.timestamp = 0
self.freq_io = 0
self.freq_pulsos = 0
self.freq_rpm = 0
class ModuloDirecional(ModuloDiagnosticoBase): class ModuloDirecional(ModuloDiagnosticoBase):
def __init__(self): def __init__(self):
self.t_code = T_Code.Dir self.t_code = T_Code.Dir
self.nome = "Direcional" self.nome = "Direcional"
self.status_por_driver = {}
self.timeout = 3 # segundos self.timeout = 3 # segundos
def atualizar_status(self, dados): def atualizar_status(self, dados):
agora = time.time() agora = time.time()
dados_dir = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(self.t_code), {})
# Esperamos uma lista de dicionários (um por motor direcional) status_modulos = {
"saude": dados_dir.get("saude", {}),
"timestamp": agora
}
for item in dados: for item in dados:
endereco = item.get("endereco") endereco = item.get("endereco")
if endereco is None: if endereco is None:
continue continue
driver = dict(item)
s = self.status_por_driver.get(endereco, StatusDirecional()) driver["saude"] = dados_dir.get(endereco, {}).get("saude", {})
s.timestamp = agora driver["timestamp"] = agora
status_modulos[endereco] = driver
freq_info = item.get("freq", {}) ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(self.t_code), status_modulos)
s.freq_io = freq_info.get("io", 0.0)
s.freq_pulsos = freq_info.get("pulsos", 0.0)
s.freq_rpm = freq_info.get("rpm", 0.0)
self.status_por_driver[endereco] = s
self.verificar_status() self.verificar_status()
def verificar_status(self): def verificar_status(self):
@ -50,19 +39,21 @@ class ModuloDirecional(ModuloDiagnosticoBase):
ENDERECOS_DRIVERS = {1, 2, 3, 4} ENDERECOS_DRIVERS = {1, 2, 3, 4}
mod_conectado = False mod_conectado = False
for endereco, driver in self.status_por_driver.items(): dados_dir = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(self.t_code), {})
mod_id = ContextoGlobal.get_item(T_Code.Dir, "dados", "mod_id", endereco)
FREQ_BASE = ContextoGlobal.get_item(T_Code.Dir, "dados", "freq_base", endereco) or 1 for endereco, modulo in dados_dir.items():
if not isinstance(endereco, int):
continue
mod_id = modulo.get("mod_id")
FREQ_BASE = modulo.get("freq_base", 1)
FREQ_MIN = FREQ_BASE * 0.5 FREQ_MIN = FREQ_BASE * 0.5
saude = 100 saude = 100
motivos = [] motivos = []
condicoes_operacionais = [] condicoes_operacionais = []
conectado = ContextoGlobal.get_item(T_Code.Dir, "dados", "conectado", endereco) or False conectado = modulo.get("conectado", False)
comandar = ContextoGlobal.get_item(T_Code.Dir, "dados", "comandar", endereco) or True comandar = modulo.get("comandar", True)
freqs = ContextoGlobal.get_item(T_Code.Dir, "dados", "freq", endereco) or {} freqs = modulo.get("freq", {})
maior_freq = max(freqs.values() if freqs != {} else [0])
maior_freq = max(freqs.values())
if not conectado: if not conectado:
saude = 0 saude = 0
elif maior_freq == 0: elif maior_freq == 0:
@ -74,20 +65,17 @@ class ModuloDirecional(ModuloDiagnosticoBase):
saude -= pen saude -= pen
if freq < FREQ_MIN: if freq < FREQ_MIN:
motivos.append(f"Frequencia {label} baixa ({freq:.2f}/{FREQ_BASE} Hz, penalidade {pen}%)") motivos.append(f"Frequencia {label} baixa ({freq:.2f}/{FREQ_BASE} Hz, penalidade {pen}%)")
referenciado = modulo.get("referenciado", False)
referenciado = ContextoGlobal.get_item(T_Code.Dir, "dados", "referenciado", endereco) angulo = modulo.get("angulo", 0.0)
angulo = ContextoGlobal.get_item(T_Code.Dir, "dados", "angulo", endereco) angulo_sp = modulo.get("angulo_sp", 0.0)
angulo_sp = ContextoGlobal.get_item(T_Code.Dir, "dados", "angulo_sp", endereco) sentido = modulo.get("sentido", Sentido.PARADO.value)
sentido = ContextoGlobal.get_item(T_Code.Dir, "dados", "sentido", endereco) sentido_real = modulo.get("sentido_real", Sentido.PARADO.value)
sentido_real = ContextoGlobal.get_item(T_Code.Dir, "dados", "sentido_real", endereco)
if referenciado is False: if referenciado is False:
condicoes_operacionais.append({ condicoes_operacionais.append({
"valor": 0, "valor": 0,
"severidade": 50, "severidade": 50,
"descricao": "Driver nunca foi referenciado" "descricao": "Driver nunca foi referenciado"
}) })
if angulo_sp != angulo: if angulo_sp != angulo:
erro_angulo = abs(angulo - angulo_sp) erro_angulo = abs(angulo - angulo_sp)
condicoes_operacionais.append({ condicoes_operacionais.append({
@ -95,37 +83,34 @@ class ModuloDirecional(ModuloDiagnosticoBase):
"severidade": erro_angulo, "severidade": erro_angulo,
"descricao": f"Angulo incoerente (Atual: {angulo}°, SP: {angulo_sp}°)" "descricao": f"Angulo incoerente (Atual: {angulo}°, SP: {angulo_sp}°)"
}) })
if sentido != Sentido.PARADO.value and sentido != sentido_real:
if sentido != Sentido.PARADO and sentido != sentido_real:
condicoes_operacionais.append({ condicoes_operacionais.append({
"valor": angulo, "valor": angulo,
"severidade": (10 + angulo), "severidade": (10 + angulo),
"descricao": f"Direção incoerente (real: {Sentido(sentido_real).name}, esperada: {Sentido(sentido).name}, ângulo: {angulo:.2f}°)" "descricao": f"Direção incoerente (real: {Sentido(sentido_real).name}, esperada: {Sentido(sentido).name}, ângulo: {angulo:.2f}°)"
}) })
saude = max(saude, 0) saude = max(saude, 0)
saude_individual.append({ saude_mod = {
"id": endereco, "id": endereco,
"label": mod_id, "label": mod_id,
"status": StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA, "status": (StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA).value,
"saude": saude, "saude": saude,
"motivos": motivos, "motivos": motivos,
"condicoes_operacionais": condicoes_operacionais, "condicoes_operacionais": condicoes_operacionais,
"em_uso": comandar "em_uso": comandar
}) }
saude_individual.append(saude_mod)
# Se o driver está sendo usado, entra na média geral # Se o driver está sendo usado, entra na média geral
if comandar is True: if comandar is True:
saude_total += saude saude_total += saude
total_drivers_em_uso += 1 total_drivers_em_uso += 1
motivos_dos_ativos.extend([f"Driver {endereco}: {m}" for m in motivos]) motivos_dos_ativos.extend([f"Driver {endereco}: {m}" for m in motivos])
modulo["saude"] = saude_mod
ContextoGlobal.set_item_dado(T_Code.Dir, "dados", "saude", saude, endereco)
if conectado: if conectado:
mod_conectado = True mod_conectado = True
# Verifica se algum driver esperado está ausente # Verifica se algum driver esperado está ausente
drivers_respondendo = set(self.status_por_driver.keys()) drivers_respondendo = set(k for k in dados_dir.keys() if isinstance(k, int))
drivers_esperados = ENDERECOS_DRIVERS drivers_esperados = ENDERECOS_DRIVERS
drivers_ausentes = drivers_esperados - drivers_respondendo drivers_ausentes = drivers_esperados - drivers_respondendo
@ -133,7 +118,7 @@ class ModuloDirecional(ModuloDiagnosticoBase):
saude_individual.append({ saude_individual.append({
"id": endereco, "id": endereco,
"label": "", "label": "",
"status": StatusModulo.DESCONECTADO, "status": StatusModulo.DESCONECTADO.value,
"saude": 0, "saude": 0,
"motivos": ["Nenhuma resposta recebida"], "motivos": ["Nenhuma resposta recebida"],
"condicoes_operacionais": [{ "condicoes_operacionais": [{
@ -149,18 +134,25 @@ class ModuloDirecional(ModuloDiagnosticoBase):
saude_final = (saude_total // total_drivers_em_uso) if total_drivers_em_uso > 0 else 0 saude_final = (saude_total // total_drivers_em_uso) if total_drivers_em_uso > 0 else 0
status = StatusModulo.OPERANTE status = StatusModulo.OPERANTE.value
if not mod_conectado: if not mod_conectado:
status = StatusModulo.DESCONECTADO status = StatusModulo.DESCONECTADO.value
elif saude_final <= 0 or len(drivers_ausentes) > 0: elif saude_final <= 0 or len(drivers_ausentes) > 0:
status = StatusModulo.FALHA status = StatusModulo.FALHA.value
elif saude_final < SAUDE_MIN_ALERTA or any(saude["saude"] < SAUDE_MIN_ALERTA for saude in saude_individual): elif saude_final < SAUDE_MIN_ALERTA or any(saude["saude"] < SAUDE_MIN_ALERTA for saude in saude_individual):
status = StatusModulo.ALERTA status = StatusModulo.ALERTA.value
ContextoGlobal.set_item_dado(T_Code.Dir, "saude", "conectado", mod_conectado) saude_geral = {
ContextoGlobal.set_item_dado(T_Code.Dir, "saude", "status", status) "conectado": mod_conectado,
ContextoGlobal.set_item_dado(T_Code.Dir, "saude", "saude", saude_final) "status": status,
ContextoGlobal.set_item_dado(T_Code.Dir, "saude", "motivos", motivos_dos_ativos) "saude": saude_final,
ContextoGlobal.set_item_dado(T_Code.Dir, "saude", "saude_individual", saude_individual) "motivos": motivos_dos_ativos,
"saude_individual": saude_individual
}
return resposta(status, saude_final, motivos_dos_ativos, saude_individual) dados_dir["saude"] = saude_geral
ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(self.t_code), dados_dir)
#ContextoGlobalRedis.atualizar_ctx_dict(ContextoGlobalRedis.ModKey(self.t_code), saude=saude_geral)
return saude_geral

View File

@ -1,86 +1,87 @@
import time import time
from shared.enums import T_Code, StatusModulo from shared.enums import T_Code, StatusModulo
from shared.contexto_global import ContextoGlobal from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
from health_worker.modulos.base import ModuloDiagnosticoBase from health_worker.modulos.base import ModuloDiagnosticoBase
class GPSStatus:
def __init__(self, dados=None):
dados = dados or {}
self.conectado = dados.get("conectado", False)
self.fix = dados.get("fix", 0)
self.rtk = dados.get("rtk", False)
self.hAcc = dados.get("hAcc", dados.get("precisao_cm", 999.0) / 100.0)
self.freq_hz = dados.get("freq", 0.0)
class ModuloGPS(ModuloDiagnosticoBase): class ModuloGPS(ModuloDiagnosticoBase):
def __init__(self): def __init__(self):
self.t_code = T_Code.Gps self.t_code = T_Code.Gps
self.nome = "UM982" self.nome = "UM982"
self.status = GPSStatus()
self.timeout = 5 self.timeout = 5
def atualizar_status(self, dados): def atualizar_status(self, dados):
agora = time.time() agora = time.time()
self.timestamp = agora modulo = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(self.t_code), {})
self.status = GPSStatus(dados) dados["saude"] = modulo.get("saude", {})
dados["timestamp"] = agora
ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(self.t_code), dados)
self.verificar_status() self.verificar_status()
def verificar_status(self): def verificar_status(self):
s = self.status modulo = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(self.t_code), {})
FREQ_BASE = modulo.get("freq_base", 1)
FREQ_BASE = ContextoGlobal.get_item(T_Code.Gps, "dados", "freq_base") or 1
FREQ_MIN = FREQ_BASE * 0.2 FREQ_MIN = FREQ_BASE * 0.2
SAUDE_MIN_ALERTA = 80 SAUDE_MIN_ALERTA = 80
if not s.conectado: conectado = modulo.get("conectado", False)
return self._resposta(StatusModulo.DESCONECTADO, 0, ["GPS desconectado"]) freq_hz = modulo.get("freq", 0.0)
fix = modulo.get("fix", 0)
if s.freq_hz <= FREQ_MIN: rtk = modulo.get("rtk", False)
return self._resposta(StatusModulo.FALHA, 0, ["Sem resposta do GPS"]) hAcc = modulo.get("hAcc", 0)
saude = 100 saude = 100
motivos = [] motivos = []
f = min(s.freq_hz, FREQ_BASE) if not conectado:
erro = (FREQ_BASE - f) / FREQ_BASE motivos.append("GPS desconectado")
p = min(int(erro * 100), 40) saude = 0
saude -= abs(p) elif freq_hz <= FREQ_MIN:
if abs(p) > 0: motivos.append("Sem resposta do GPS")
motivos.append(f"Frequência baixa: {f:.2f} Hz (penalidade {p}%)") saude = 0
else:
# 🔹 Fix f = min(freq_hz, FREQ_BASE)
penalidades_fix = { 0: 90, 1: 70, 2: 40, 3: 30, 4: 5, 5: 0 } erro = (FREQ_BASE - f) / FREQ_BASE
p = penalidades_fix.get(s.fix, 100) p = min(int(erro * 100), 40)
saude -= p saude -= abs(p)
if s.fix < 4: if abs(p) > 0:
motivos.append(f"Fix inadequado: {s.fix} (penalidade {p}%)") motivos.append(f"Frequência baixa: {f:.2f} Hz (penalidade {p}%)")
# 🔹 Fix
# 🔹 RTK penalidades_fix = { 0: 90, 1: 70, 2: 40, 3: 30, 4: 5, 5: 0 }
if not s.rtk: p = penalidades_fix.get(fix, 100)
saude -= 20
motivos.append("RTK inativo")
# 🔹 hAcc
p = self._penalidade_hacc(s.hAcc)
if p > 0:
saude -= p saude -= p
motivos.append(f"hAcc {'nao definido' if s.hAcc == 0 else 'alto'}: {s.hAcc:.2f} cm (penalidade {p}%)") if fix < 4:
motivos.append(f"Fix inadequado: {fix} (penalidade {p}%)")
# 🔹 RTK
if not rtk:
saude -= 20
motivos.append("RTK inativo")
# 🔹 hAcc
p = self._penalidade_hacc(hAcc)
if p > 0:
saude -= p
motivos.append(f"hAcc {'nao definido' if hAcc == 0 else 'alto'}: {hAcc:.2f} cm (penalidade {p}%)")
saude = max(saude, 0) saude = max(saude, 0)
status = StatusModulo.OPERANTE status = StatusModulo.OPERANTE.value
if saude <= 0: if saude <= 0:
status = StatusModulo.FALHA status = StatusModulo.FALHA.value
elif saude < SAUDE_MIN_ALERTA: elif saude < SAUDE_MIN_ALERTA:
status = StatusModulo.ALERTA status = StatusModulo.ALERTA.value
ContextoGlobal.set_item_dado(T_Code.Gps, "saude", "status", status) saude_geral = {
ContextoGlobal.set_item_dado(T_Code.Gps, "saude", "saude", saude) "conectado": conectado,
ContextoGlobal.set_item_dado(T_Code.Gps, "saude", "motivos", motivos) "status": status,
ContextoGlobal.set_item_dado(T_Code.Gps, "saude", "saude_individual", []) "saude": saude,
"motivos": motivos,
"saude_idividual": []
}
return self._resposta(status, saude, motivos) modulo["saude"] = saude_geral
ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(self.t_code), modulo)
return saude_geral
def _penalidade_hacc(self, h): def _penalidade_hacc(self, h):
if h < 40: return 0 if h < 40: return 0
@ -89,10 +90,3 @@ class ModuloGPS(ModuloDiagnosticoBase):
if h < 200: return 20 if h < 200: return 20
if h == 0: return 30 if h == 0: return 30
return 30 return 30
def _resposta(self, status, saude, motivos):
return {
"status": status,
"saude": min(100, saude),
"motivos": motivos
}

View File

@ -3,21 +3,16 @@ import time
import threading import threading
from ahrs.filters import Madgwick from ahrs.filters import Madgwick
from scipy.spatial.transform import Rotation as R from scipy.spatial.transform import Rotation as R
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
from shared.enums import T_Code
class IMUCamera: class IMUCamera:
def __init__(self, queue): def __init__(self, queue, freq=10):
self.freq = freq
self.imu_queue = queue self.imu_queue = queue
self.filtro_imu = Madgwick() self.filtro_imu = Madgwick()
self.q = np.array([1.0, 0.0, 0.0, 0.0]) self.q = np.array([1.0, 0.0, 0.0, 0.0])
self.imu_data_lock = threading.Lock()
self.imu_status = {
"roll": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"timestamp": 0.0
}
self.imu_thread = threading.Thread(target=self.imu_task_loop, daemon=True) self.imu_thread = threading.Thread(target=self.imu_task_loop, daemon=True)
self.imu_thread.start() self.imu_thread.start()
@ -48,16 +43,12 @@ class IMUCamera:
r = R.from_quat([q[1], q[2], q[3], q[0]]) r = R.from_quat([q[1], q[2], q[3], q[0]])
roll, pitch, yaw = r.as_euler('xyz', degrees=True) roll, pitch, yaw = r.as_euler('xyz', degrees=True)
with self.imu_data_lock: _imu = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(T_Code.Imu), {})
self.imu_status = { _imu["roll"] = round(roll, 2)
"roll": round(roll, 2), _imu["pitch"] = round(pitch, 2)
"pitch": round(pitch, 2), _imu["yaw"] = round(yaw, 2)
"yaw": round(yaw, 2), _imu["timestamp"] = imuData.getTimestamp().total_seconds()
"timestamp": imuData.getTimestamp().total_seconds() ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(T_Code.Imu), _imu)
}
time.sleep(0.01) # → Ajuste aqui a frequência (~100Hz → 10ms de intervalo) time.sleep(1.0 / self.freq)
def get_imu_status(self):
with self.imu_data_lock:
return self.imu_status.copy()

View File

@ -1,20 +1,12 @@
import time import time
from shared.enums import T_Code, StatusModulo from shared.enums import T_Code, StatusModulo
from shared.utils import penalizar_frequencia from shared.utils import penalizar_frequencia
from shared.contexto_global import ContextoGlobal from health_worker.modulos.base import ModuloDiagnosticoBase
from health_worker.modulos.base import ModuloDiagnosticoBase, resposta
from health_worker.aprendizado.padrao_dinamico import PadraoDinamico from health_worker.aprendizado.padrao_dinamico import PadraoDinamico
from shared.contexto_global_redis import ContextoGlobalRedis
class StatusMovimentacao: class ModuloMovimentacaoPadroes():
def __init__(self): def __init__(self):
self.timestamp = 0
self.freq_erro = 0
self.freq_rpm = 0
self.freq_tensao = 0
self.freq_corrente_motor = 0
self.freq_corrente_barramento = 0
self.freq_temperatura = 0
self.padrao_tensao = PadraoDinamico("tensao", faixa_inicial=(38, 42)) self.padrao_tensao = PadraoDinamico("tensao", faixa_inicial=(38, 42))
self.padrao_corrente_motor = PadraoDinamico("corrente_motor", faixa_inicial=(0, 45)) self.padrao_corrente_motor = PadraoDinamico("corrente_motor", faixa_inicial=(0, 45))
self.padrao_corrente_barramento = PadraoDinamico("corrente_barramento", faixa_inicial=(0, 15)) self.padrao_corrente_barramento = PadraoDinamico("corrente_barramento", faixa_inicial=(0, 15))
@ -25,28 +17,24 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
def __init__(self): def __init__(self):
self.t_code = T_Code.Mov self.t_code = T_Code.Mov
self.nome = "Movimentacao" self.nome = "Movimentacao"
self.status_por_driver = {}
self.timeout = 3 # segundos self.timeout = 3 # segundos
self.padroes = {}
def atualizar_status(self, dados): def atualizar_status(self, dados):
agora = time.time() agora = time.time()
dados_mov = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(self.t_code), {})
status_modulos = {
"saude": dados_mov.get("saude", {}),
"timestamp": agora
}
for item in dados: for item in dados:
endereco = item.get("endereco") endereco = item.get("endereco")
if endereco is None: if endereco is None:
continue continue
driver = dict(item)
s = self.status_por_driver.get(endereco, StatusMovimentacao()) driver["saude"] = dados_mov.get(endereco, {}).get("saude", {})
s.timestamp = agora driver["timestamp"] = agora
status_modulos[endereco] = driver
freq = item.get("freq", {})
s.freq_erro = freq.get("erro", 0.0)
s.freq_rpm = freq.get("rpm", 0.0)
s.freq_tensao = freq.get("tensao", 0.0)
s.freq_corrente_motor = freq.get("corrente_motor", 0.0)
s.freq_corrente_barramento = freq.get("corrente_barramento", 0.0)
s.freq_temperatura = freq.get("temperatura", 0.0)
# Atualiza os padrões dinâmicos com os valores principais # Atualiza os padrões dinâmicos com os valores principais
tensao = item.get("tensao", 0.0) tensao = item.get("tensao", 0.0)
@ -55,14 +43,14 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
#temperatura_motor = item.get("temperatura_motor", 0.0) #temperatura_motor = item.get("temperatura_motor", 0.0)
temperatura_driver = item.get("temperatura_driver", 0.0) temperatura_driver = item.get("temperatura_driver", 0.0)
s.padrao_tensao.adicionar_valor(tensao) padroes = self.padroes.setdefault(endereco, ModuloMovimentacaoPadroes())
s.padrao_corrente_motor.adicionar_valor(corrente_motor) padroes.padrao_tensao.adicionar_valor(tensao)
s.padrao_corrente_barramento.adicionar_valor(corrente_barramento) padroes.padrao_corrente_motor.adicionar_valor(corrente_motor)
#s.padrao_temperatura_motor.adicionar_valor(temperatura_motor) padroes.padrao_corrente_barramento.adicionar_valor(corrente_barramento)
s.padrao_temperatura_driver.adicionar_valor(temperatura_driver) #padroes.padrao_temperatura_motor.adicionar_valor(temperatura_motor)
padroes.padrao_temperatura_driver.adicionar_valor(temperatura_driver)
self.status_por_driver[endereco] = s
ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(self.t_code), status_modulos)
self.verificar_status() self.verificar_status()
def verificar_status(self): def verificar_status(self):
@ -77,19 +65,21 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
LIMITE_ERRO_RPM = 30 LIMITE_ERRO_RPM = 30
mod_conectado = False mod_conectado = False
for endereco, driver in self.status_por_driver.items(): dados_mov = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(self.t_code), {})
mod_id = ContextoGlobal.get_item(T_Code.Mov, "dados", "mod_id", endereco)
FREQ_BASE = ContextoGlobal.get_item(T_Code.Mov, "dados", "freq_base", endereco) or 1 for endereco, modulo in dados_mov.items():
if not isinstance(endereco, int):
continue
mod_id = modulo.get("mod_id")
FREQ_BASE = modulo.get("freq_base", 1)
FREQ_MIN = FREQ_BASE * 0.5 FREQ_MIN = FREQ_BASE * 0.5
saude = 100 saude = 100
motivos = [] motivos = []
condicoes_operacionais = [] condicoes_operacionais = []
conectado = ContextoGlobal.get_item(T_Code.Mov, "dados", "conectado", endereco) or False conectado = modulo.get("conectado", False)
comandar = ContextoGlobal.get_item(T_Code.Mov, "dados", "comandar", endereco) or True comandar = modulo.get("comandar", True)
freqs = ContextoGlobal.get_item(T_Code.Mov, "dados", "freq", endereco) or {} freqs = modulo.get("freq", {})
maior_freq = max(freqs.values() if freqs != {} else [0])
maior_freq = max(freqs.values())
if not conectado: if not conectado:
saude = 0 saude = 0
elif maior_freq == 0: elif maior_freq == 0:
@ -101,17 +91,16 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
saude -= pen saude -= pen
if (freq <= FREQ_MIN): if (freq <= FREQ_MIN):
motivos.append(f"Frequencia {label} baixa ({freq:.2f}/{FREQ_BASE} Hz, penalidade {pen}%)") motivos.append(f"Frequencia {label} baixa ({freq:.2f}/{FREQ_BASE} Hz, penalidade {pen}%)")
# Verificações de contexto # Verificações de contexto
rpm = ContextoGlobal.get_item(T_Code.Mov, "dados", "rpm", endereco) or 0 rpm = modulo.get("rpm", 0)
rpm_sp = ContextoGlobal.get_item(T_Code.Mov, "dados", "rpm_sp", endereco) or 0 rpm_sp = modulo.get("rpm_sp", 0)
cod_erro = ContextoGlobal.get_item(T_Code.Mov, "dados", "cod_erro", endereco) or 0 cod_erro = modulo.get("cod_erro", 0)
tensao = ContextoGlobal.get_item(T_Code.Mov, "dados", "tensao", endereco) or 0 tensao = modulo.get("tensao", 0)
corrente_motor = ContextoGlobal.get_item(T_Code.Mov, "dados", "corrente_motor", endereco) or 0 corrente_motor = modulo.get("corrente_motor", 0)
corrente_barramento = ContextoGlobal.get_item(T_Code.Mov, "dados", "corrente_barramento", endereco) or 0 corrente_barramento = modulo.get("corrente_barramento", 0)
temperatura_motor = ContextoGlobal.get_item(T_Code.Mov, "dados", "temperatura_motor", endereco) or 0 temperatura_motor = modulo.get("temperatura_motor", 0)
temperatura_driver = ContextoGlobal.get_item(T_Code.Mov, "dados", "temperatura_driver", endereco) or 0 temperatura_driver = modulo.get("temperatura_driver", 0)
freado = ContextoGlobal.get_item(T_Code.Mov, "dados", "freado", endereco) or False freado = modulo.get("freado", False)
OID_ERROS_PENALIDADES = { OID_ERROS_PENALIDADES = {
0: 0, 0: 0,
@ -160,9 +149,11 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
"descricao": f"motor em movimento mesmo com freio (RPM SP: {rpm_sp}, atual: {rpm}, erro {erro_percentual_rpm*100:.1f}%, penalidade {penalidade}%)" "descricao": f"motor em movimento mesmo com freio (RPM SP: {rpm_sp}, atual: {rpm}, erro {erro_percentual_rpm*100:.1f}%, penalidade {penalidade}%)"
}) })
padroes = self.padroes.setdefault(endereco, ModuloMovimentacaoPadroes())
# 🔋 Tensão fora do padrão # 🔋 Tensão fora do padrão
if not driver.padrao_tensao.esta_dentro_do_padrao(tensao): if not modulo.padrao_tensao.esta_dentro_do_padrao(tensao):
grau = driver.padrao_tensao.calcular_quao_anormal(tensao) grau = padroes.padrao_tensao.calcular_quao_anormal(tensao)
condicoes_operacionais.append({ condicoes_operacionais.append({
"valor": tensao, "valor": tensao,
"severidade": grau, "severidade": grau,
@ -170,8 +161,8 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
}) })
# ⚡ Corrente do motor elevada # ⚡ Corrente do motor elevada
if not driver.padrao_corrente_motor.esta_dentro_do_padrao(corrente_motor): if not padroes.padrao_corrente_motor.esta_dentro_do_padrao(corrente_motor):
grau = driver.padrao_corrente_motor.calcular_quao_anormal(corrente_motor) grau = padroes.padrao_corrente_motor.calcular_quao_anormal(corrente_motor)
condicoes_operacionais.append({ condicoes_operacionais.append({
"valor": corrente_motor, "valor": corrente_motor,
"severidade": grau, "severidade": grau,
@ -179,8 +170,8 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
}) })
# ⚡ Corrente do barramento elevada # ⚡ Corrente do barramento elevada
if not driver.padrao_corrente_barramento.esta_dentro_do_padrao(corrente_barramento): if not padroes.padrao_corrente_barramento.esta_dentro_do_padrao(corrente_barramento):
grau = driver.padrao_corrente_barramento.calcular_quao_anormal(corrente_barramento) grau = padroes.padrao_corrente_barramento.calcular_quao_anormal(corrente_barramento)
condicoes_operacionais.append({ condicoes_operacionais.append({
"valor": corrente_barramento, "valor": corrente_barramento,
"severidade": grau, "severidade": grau,
@ -188,16 +179,15 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
}) })
# 🌡️ Temperatura alta (motor ou driver) # 🌡️ Temperatura alta (motor ou driver)
if not driver.padrao_temperatura_driver.esta_dentro_do_padrao(temperatura_driver): if not padroes.padrao_temperatura_driver.esta_dentro_do_padrao(temperatura_driver):
grau = driver.padrao_temperatura_driver.calcular_quao_anormal(temperatura_driver) grau = padroes.padrao_temperatura_driver.calcular_quao_anormal(temperatura_driver)
condicoes_operacionais.append({ condicoes_operacionais.append({
"valor": temperatura_driver, "valor": temperatura_driver,
"severidade": grau, "severidade": grau,
"descricao": f"temperatura elevada: motor {temperatura_motor:.1f}°C, driver {temperatura_driver:.1f}°C" "descricao": f"temperatura elevada: motor {temperatura_motor:.1f}°C, driver {temperatura_driver:.1f}°C"
}) })
saude = max(saude, 0) saude = max(saude, 0)
saude_individual.append({ saude_mod = {
"id": endereco, "id": endereco,
"label": mod_id, "label": mod_id,
"status": StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA, "status": StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA,
@ -205,19 +195,18 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
"motivos": motivos, "motivos": motivos,
"condicoes_operacionais": condicoes_operacionais, "condicoes_operacionais": condicoes_operacionais,
"em_uso": comandar "em_uso": comandar
}) }
saude_individual.append(saude_mod)
if comandar: if comandar:
saude_total += saude saude_total += saude
total_drivers_em_uso += 1 total_drivers_em_uso += 1
motivos_dos_ativos.extend([f"Driver {endereco}: {m}" for m in motivos]) motivos_dos_ativos.extend([f"Driver {endereco}: {m}" for m in motivos])
modulo["saude"] = saude_mod
ContextoGlobal.set_item_dado(T_Code.Mov, "dados", "saude", saude, endereco)
if conectado: if conectado:
mod_conectado = True mod_conectado = True
# Verificação de ausência de drivers esperados # Verificação de ausência de drivers esperados
drivers_respondendo = set(self.status_por_driver.keys()) drivers_respondendo = set(k for k in dados_mov.keys() if isinstance(k, int))
drivers_esperados = ENDERECOS_DRIVERS drivers_esperados = ENDERECOS_DRIVERS
drivers_ausentes = drivers_esperados - drivers_respondendo drivers_ausentes = drivers_esperados - drivers_respondendo
@ -225,7 +214,7 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
saude_individual.append({ saude_individual.append({
"id": endereco, "id": endereco,
"label": "", "label": "",
"status": StatusModulo.DESCONECTADO, "status": StatusModulo.DESCONECTADO.value,
"saude": 0, "saude": 0,
"motivos": ["Nenhuma resposta recebida"], "motivos": ["Nenhuma resposta recebida"],
"condicoes_operacionais": [{ "condicoes_operacionais": [{
@ -241,18 +230,25 @@ class ModuloMovimentacao(ModuloDiagnosticoBase):
saude_final = (saude_total // total_drivers_em_uso) if total_drivers_em_uso > 0 else 0 saude_final = (saude_total // total_drivers_em_uso) if total_drivers_em_uso > 0 else 0
status = StatusModulo.OPERANTE status = StatusModulo.OPERANTE.value
if not mod_conectado: if not mod_conectado:
status = StatusModulo.DESCONECTADO status = StatusModulo.DESCONECTADO.value
elif saude_final <= 0 or len(drivers_ausentes) > 0: elif saude_final <= 0 or len(drivers_ausentes) > 0:
status = StatusModulo.FALHA status = StatusModulo.FALHA.value
elif saude_final < SAUDE_MIN_ALERTA or any(saude["saude"] < SAUDE_MIN_ALERTA for saude in saude_individual): elif saude_final < SAUDE_MIN_ALERTA or any(saude["saude"] < SAUDE_MIN_ALERTA for saude in saude_individual):
status = StatusModulo.ALERTA status = StatusModulo.ALERTA.value
ContextoGlobal.set_item_dado(T_Code.Mov, "saude", "conectado", mod_conectado) saude_geral = {
ContextoGlobal.set_item_dado(T_Code.Mov, "saude", "status", status) "conectado": mod_conectado,
ContextoGlobal.set_item_dado(T_Code.Mov, "saude", "saude", saude_final) "status": status,
ContextoGlobal.set_item_dado(T_Code.Mov, "saude", "motivos", motivos_dos_ativos) "saude": saude_final,
ContextoGlobal.set_item_dado(T_Code.Mov, "saude", "saude_individual", saude_individual) "motivos": motivos_dos_ativos,
"saude_individual": saude_individual
}
return resposta(status, saude_final, motivos_dos_ativos, saude_individual) dados_mov["saude"] = saude_geral
ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(self.t_code), dados_mov)
#ContextoGlobalRedis.atualizar_ctx_dict(ContextoGlobalRedis.ModKey(self.t_code), saude=saude_geral)
return saude_geral

View File

@ -1,30 +1,9 @@
import time import time
from shared.enums import T_Code, S_Code, StatusModulo from shared.enums import T_Code, S_Code, StatusModulo
from shared.utils import penalizar_frequencia from shared.utils import penalizar_frequencia
from shared.contexto_global import ContextoGlobal from health_worker.modulos.base import ModuloDiagnosticoBase
from health_worker.modulos.base import ModuloDiagnosticoBase, resposta
from health_worker.aprendizado.padrao_dinamico import PadraoDinamico from health_worker.aprendizado.padrao_dinamico import PadraoDinamico
from shared.contexto_global_redis import ContextoGlobalRedis
class StatusSensor:
def __init__(self):
self.timestamp = 0
self.conectado = False
self.aferir = False
self.mandatorio = False
self.id = 0
self.label = ""
self.dados = {} # chave = label, valor = StatusCampoSensor
class StatusCampoSensor:
def __init__(self, id=0, label=0, atual=0, freq=0, nominal=None, minimo=None, maximo=None):
self.id = id
self.label = label
self.atual = atual
self.freq = freq
self.nominal = nominal
self.minimo = minimo
self.maximo = maximo
self.padrao = PadraoDinamico(nome="") # opcional, preenche depois
class StatusServo: class StatusServo:
def __init__(self): def __init__(self):
@ -44,106 +23,72 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
super().__init__() # ✅ garante que a classe base é corretamente iniciada super().__init__() # ✅ garante que a classe base é corretamente iniciada
self.t_code = T_Code.Sen self.t_code = T_Code.Sen
self.nome = "Sensoriamento" self.nome = "Sensoriamento"
self.status_sensor = {} # Ex: sTMP → {id1: StatusSensor, id2: StatusSensor} self.padroes_sensores = {}
self.status_servo = {} # chave: ID do servo (ex: freio_dianteiro) self.padroes_servos = {}
self.timeout = 3 # pode ser usado para validar timestamp self.timeout = 3 # pode ser usado para validar timestamp
def atualizar_status(self, dados): def atualizar_status(self, dados):
agora = time.time() agora = time.time()
dados_sen = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(self.t_code), {})
sensores = dados.get("sensores", []) modulo = dict(dados)
for sensor in sensores: sensores = modulo.get("sensores", [])
tipo_int = sensor.get("tipo") _sensores_save = dados_sen.get("sensores", {})
sensor_id = sensor.get("id") for item in sensores:
tipo_int = item.get("tipo")
sensor_id = item.get("id")
if tipo_int is None or sensor_id is None: if tipo_int is None or sensor_id is None:
continue continue
try: try:
tipo_enum = S_Code(tipo_int) tipo_enum = S_Code(tipo_int)
except ValueError: except ValueError:
continue continue
if tipo_enum not in _sensores_save:
if tipo_enum not in self.status_sensor: _sensores_save[tipo_enum] = {}
self.status_sensor[tipo_enum] = {} self.padroes_sensores[tipo_enum] = {}
if sensor_id not in _sensores_save[tipo_enum]:
if sensor_id not in self.status_sensor[tipo_enum]: _sensores_save[tipo_enum][sensor_id] = {}
self.status_sensor[tipo_enum][sensor_id] = StatusSensor() self.padroes_sensores[tipo_enum][sensor_id] = {}
sensor = dict(item)
status = self.status_sensor[tipo_enum][sensor_id] sensor["saude"] = _sensores_save[tipo_enum][sensor_id].get("saude", {})
status.timestamp = agora sensor["timestamp"] = agora
status.conectado = sensor.get("conectado", False) padroes = self.padroes_sensores.get(tipo_enum, {}).get(sensor_id, {})
status.id = sensor_id for dado in sensor.get("dados", []):
status.label = sensor.get("label", sensor_id) label = dado.get("label")
status.aferir = sensor.get("aferir", False)
status.mandatorio = sensor.get("mandatorio", False)
status.dados.clear()
for item in sensor.get("dados", []):
label = item.get("label")
if label is None: if label is None:
continue continue
atual = dado.get("atual")
nominal = dado.get("nominal")
minimo = dado.get("minimo")
maximo = dado.get("maximo")
freq = dado.get("freq", 0.0)
padrao = padroes.get(label)
if not padrao:
padrao = PadraoDinamico(
nome=f"{tipo_enum.name}_{sensor_id}_{label}",
faixa_inicial=(minimo or 0, maximo or 100),
valor_esperado=nominal or ((minimo + maximo) / 2 if minimo is not None and maximo is not None else None)
)
padroes[label] = padrao
padrao.adicionar_valor(atual)
padrao.frequencia = freq
_sensores_save[tipo_enum][sensor_id] = sensor
atual = item.get("atual") servos = modulo.get("servos", [])
nominal = item.get("nominal") _servos_save = dados_sen.get("servos", {})
minimo = item.get("minimo")
maximo = item.get("maximo")
freq = item.get("freq", 0.0)
status.dados[label] = atual # Valor atual por label
status.dados[label] = StatusCampoSensor(
id=sensor_id,
label=status.label,
atual=atual,
freq=freq,
nominal=nominal,
minimo=minimo,
maximo=maximo,
)
status.dados[label].padrao = PadraoDinamico(
nome=f"{tipo_enum.name}_{sensor_id}_{label}",
faixa_inicial=(minimo or 0, maximo or 100),
valor_esperado=nominal or ((minimo + maximo) / 2 if minimo is not None and maximo is not None else None)
)
status.dados[label].padrao.adicionar_valor(atual)
status.dados[label].padrao.frequencia = freq
servos = dados.get("servos", [])
for item in servos: for item in servos:
servo_id = item.get("id") servo_id = item.get("id")
if servo_id is None: if servo_id is None:
continue continue
servo = dict(item)
servo["saude"] = _servos_save.get(servo_id, {}).get("saude", {})
servo["timestamp"] = agora
_servos_save[servo_id] = servo
status = self.status_servo.get(servo_id, StatusServo()) modulo["sensores"] = _sensores_save
status.timestamp = agora modulo["servos"] = _servos_save
status.conectado = item.get("conectado", False) modulo["timestamp"] = agora
status.id = servo_id
status.label = item.get("label", servo_id)
status.controlar = item.get("controlar", False)
status.mandatorio = item.get("mandatorio", False)
status.angulo_sp = item.get("angulo_sp", 0.0)
# Reinicializa os dados ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(self.t_code), modulo)
status.dados = {}
status.freqs = {}
for dado in item.get("dados", []):
label = dado.get("label")
if label is None:
continue
atual = dado.get("atual", 0.0)
freq = dado.get("freq", 0.0)
status.dados[label] = atual
status.freqs[label] = freq
if label == "angulo":
status.angulo = atual # Referência direta para facilitar diagnósticos
self.status_servo[servo_id] = status
self.verificar_status() self.verificar_status()
@ -154,22 +99,27 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
total_ativos = 0 total_ativos = 0
saude_individual = [] saude_individual = []
mod_conectado = ContextoGlobal.get_item(T_Code.Sen, "dados", "conectado") or False dados_sen = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(self.t_code), {})
FREQ_BASE = ContextoGlobal.get_item(T_Code.Sen, "dados", "freq_base") or 1
mod_conectado = dados_sen.get("conectado", False)
FREQ_BASE = dados_sen.get("freq_base", 1)
FREQ_MIN = FREQ_BASE * 0.5 FREQ_MIN = FREQ_BASE * 0.5
SAUDE_MIN_ALERTA = 80 SAUDE_MIN_ALERTA = 80
for tipo, sensores in self.status_sensor.items(): for tipo, sensores in dados_sen.get("sensores", {}).items():
for sensor_id, dados_sensor in sensores.items(): for sensor_id, dados_sensor in sensores.items():
saude = 100 saude = 100
motivos = [] motivos = []
condicoes_operacionais = [] condicoes_operacionais = []
aferir = dados_sensor.aferir aferir = dados_sensor.get("aferir", False)
padroes = self.padroes_sensores.get(tipo, {}).get(sensor_id, {})
for label, campo in dados_sensor.dados.items(): conectado = dados_sensor.get("conectado", False)
conectado = dados_sensor.conectado mandatorio = dados_sensor.get("mandatorio", False)
freq = getattr(campo, "freq", 0.0) for campo in dados_sensor.get("dados", []):
label = campo.get("label")
if label is None:
continue
freq = campo.get("freq", 0.0)
if not conectado: if not conectado:
saude = 0 saude = 0
elif freq == 0: elif freq == 0:
@ -181,43 +131,47 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
if (freq <= FREQ_MIN): if (freq <= FREQ_MIN):
motivos.append(f"{label} frequência baixa ({freq:.2f}/{FREQ_BASE} Hz, penalidade {penalidade}%)") motivos.append(f"{label} frequência baixa ({freq:.2f}/{FREQ_BASE} Hz, penalidade {penalidade}%)")
padrao = campo.padrao if campo else None atual = campo.get("atual")
padrao = padroes.get(label) if atual else None
if padrao is not None: if padrao is not None:
# Avaliação de condição operacional (sem penalizar saúde) # Avaliação de condição operacional (sem penalizar saúde)
if not padrao.esta_dentro_do_padrao(campo.atual): if not padrao.esta_dentro_do_padrao(atual):
grau = padrao.calcular_quao_anormal(campo.atual) grau = padrao.calcular_quao_anormal(atual)
condicoes_operacionais.append({ condicoes_operacionais.append({
"valor": campo.atual, "valor": atual,
"severidade": grau, "severidade": grau,
"descricao": f"{label} fora do padrão" "descricao": f"{label} fora do padrão"
}) })
saude = max(0, saude) saude = max(0, saude)
chave = f"{tipo.name}_{sensor_id}" chave = f"{S_Code(int(tipo)).name}_{sensor_id}"
saude_individual.append({ saude_mod = {
"id": sensor_id, "id": sensor_id,
"label": campo.label, "label": dados_sensor.get("label"),
"status": StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA, "status": (StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA).value,
"saude": saude, "saude": saude,
"motivos": motivos, "motivos": motivos,
"condicoes_operacionais": condicoes_operacionais, "condicoes_operacionais": condicoes_operacionais,
"em_uso": aferir "em_uso": aferir
}) }
saude_individual.append(saude_mod)
if dados_sensor.mandatorio: if mandatorio:
total_ativos += 1 total_ativos += 1
saude_total += saude saude_total += saude
motivos_gerais.extend([f"{chave}: {m}" for m in motivos]) motivos_gerais.extend([f"{chave}: {m}" for m in motivos])
dados_sensor["saude"] = saude_mod
for servo_id, dados_servo in self.status_servo.items(): for servo_id, dados_servo in dados_sen.get("servos", {}).items():
saude = 100 saude = 100
motivos = [] motivos = []
condicoes_operacionais = [] condicoes_operacionais = []
controlar = dados_servo.controlar controlar = dados_servo.get("controlar", False)
conectado = dados_servo.get("conectado", False)
for label, atual in dados_servo.dados.items(): mandatorio = dados_servo.get("mandatorio", False)
conectado = dados_servo.conectado for dado in dados_servo.get("dados", {}):
freq = dados_servo.freqs.get(label, 0.0) label = dado.get("label")
if label is None:
continue
freq = dado.get("freq", 0.0)
if not conectado: if not conectado:
saude = 0 saude = 0
elif freq == 0: elif freq == 0:
@ -229,46 +183,56 @@ class ModuloSensoriamento(ModuloDiagnosticoBase):
if freq <= FREQ_MIN: if freq <= FREQ_MIN:
motivos.append(f"{label} frequência baixa ({freq:.2f}/{FREQ_BASE} Hz, penalidade {penalidade}%)") motivos.append(f"{label} frequência baixa ({freq:.2f}/{FREQ_BASE} Hz, penalidade {penalidade}%)")
if label == "angulo": if label == "ServoAnguloLeitura":
erro = abs(dados_servo.angulo - dados_servo.angulo_sp) angulo = dado.get("atual", 0.0)
angulo_sp = dados_servo.get("angulo_sp", 0.0)
erro = abs(angulo - angulo_sp)
if erro > 0: if erro > 0:
condicoes_operacionais.append({ condicoes_operacionais.append({
"valor": dados_servo.angulo, "valor": angulo,
"severidade": erro, "severidade": erro,
"descricao": f"{label} fora do padrão" "descricao": f"{label} fora do padrão"
}) })
saude = max(0, saude) saude = max(0, saude)
chave = f"servo_{servo_id}" chave = f"servo_{servo_id}"
saude_individual.append({ saude_mod = {
"id": servo_id, "id": servo_id,
"label": dados_servo.label, "label": dados_servo.get("label"),
"status": StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA, "status": (StatusModulo.DESCONECTADO if not conectado else StatusModulo.OPERANTE if saude >= SAUDE_MIN_ALERTA else StatusModulo.ALERTA if saude > 0 else StatusModulo.FALHA).value,
"saude": saude, "saude": saude,
"motivos": motivos, "motivos": motivos,
"condicoes_operacionais": condicoes_operacionais, "condicoes_operacionais": condicoes_operacionais,
"em_uso": controlar "em_uso": controlar
}) }
saude_individual.append(saude_mod)
if dados_servo.mandatorio: if mandatorio:
total_ativos += 1 total_ativos += 1
saude_total += saude saude_total += saude
motivos_gerais.extend([f"{chave}: {m}" for m in motivos]) motivos_gerais.extend([f"{chave}: {m}" for m in motivos])
dados_servo["saude"] = saude_mod
saude_final = (saude_total // total_ativos) if total_ativos > 0 else 100 saude_final = (saude_total // total_ativos) if total_ativos > 0 else 100
status = StatusModulo.OPERANTE status = StatusModulo.OPERANTE.value
if not mod_conectado: if not mod_conectado:
status = StatusModulo.DESCONECTADO status = StatusModulo.DESCONECTADO.value
elif saude_final <= 0: elif saude_final <= 0:
status = StatusModulo.FALHA status = StatusModulo.FALHA.value
elif saude_final < SAUDE_MIN_ALERTA or any(s["em_uso"] is True and s["saude"] < SAUDE_MIN_ALERTA for s in saude_individual): elif saude_final < SAUDE_MIN_ALERTA or any(s["em_uso"] is True and s["saude"] < SAUDE_MIN_ALERTA for s in saude_individual):
status = StatusModulo.ALERTA status = StatusModulo.ALERTA.value
ContextoGlobal.set_item_dado(T_Code.Sen, "saude", "conectado", mod_conectado) saude_geral = {
ContextoGlobal.set_item_dado(T_Code.Sen, "saude", "status", status) "conectado": mod_conectado,
ContextoGlobal.set_item_dado(T_Code.Sen, "saude", "saude", saude_final) "status": status,
ContextoGlobal.set_item_dado(T_Code.Sen, "saude", "motivos", motivos_gerais) "saude": saude_final,
ContextoGlobal.set_item_dado(T_Code.Sen, "saude", "saude_individual", saude_individual) "motivos": motivos_gerais,
"saude_individual": saude_individual
}
return resposta(status, saude_final, motivos_gerais, saude_individual) dados_sen["saude"] = saude_geral
ContextoGlobalRedis.set(ContextoGlobalRedis.ModKey(self.t_code), dados_sen)
#ContextoGlobalRedis.atualizar_ctx_dict(ContextoGlobalRedis.ModKey(self.t_code), saude=saude_geral)
return saude_geral

View File

@ -4,12 +4,18 @@ from shared.mqtt_handler import iniciar_mqtt
from manager_worker.main import main as iniciar_manager_worker from manager_worker.main import main as iniciar_manager_worker
from health_worker.main import main as iniciar_health_worker from health_worker.main import main as iniciar_health_worker
from visual_worker.main import main as iniciar_visual_worker from visual_worker.main import main as iniciar_visual_worker
from camera_worker.main import main as iniciar_camera_worker
from shared.contexto_global import ContextoGlobal
def main(): def main():
iniciar_mqtt("ia_central") iniciar_mqtt("ia_central")
threading.Thread(target=iniciar_manager_worker, daemon=True).start()
threading.Thread(target=iniciar_health_worker, daemon=True).start() ContextoGlobal.iniciar_contexto_base()
threading.Thread(target=iniciar_visual_worker, daemon=True).start()
threading.Thread(target=iniciar_manager_worker, args=(False,), daemon=True).start()
threading.Thread(target=iniciar_health_worker, args=(False,), daemon=True).start()
threading.Thread(target=iniciar_visual_worker, args=(False,), daemon=True).start()
threading.Thread(target=iniciar_camera_worker, args=(False,), daemon=True).start()
while True: while True:
time.sleep(1) time.sleep(1)

View File

@ -4,13 +4,16 @@ from manager_worker.main import main as iniciar_manager_worker
from health_worker.main import main as iniciar_health_worker from health_worker.main import main as iniciar_health_worker
from visual_worker.main import main as iniciar_visual_worker from visual_worker.main import main as iniciar_visual_worker
from camera_worker.main import main as iniciar_camera_worker from camera_worker.main import main as iniciar_camera_worker
from shared.contexto_global import ContextoGlobal
def main(): def main():
ContextoGlobal.iniciar_contexto_base()
processos = [ processos = [
#Process(target=iniciar_manager_worker, name="ManagerWorker"), Process(target=iniciar_manager_worker, args=(True,), name="ManagerWorker"),
Process(target=iniciar_health_worker, name="HealthWorker"), Process(target=iniciar_health_worker, args=(True,), name="HealthWorker"),
Process(target=iniciar_visual_worker, name="VisualWorker"), Process(target=iniciar_visual_worker, args=(True,), name="VisualWorker"),
#Process(target=iniciar_camera_worker, name="CameraWorker"), Process(target=iniciar_camera_worker, args=(True,), name="CameraWorker"),
] ]
for p in processos: for p in processos:

View File

@ -1,9 +1,8 @@
import time import time
from shared.mqtt_handler import enviar_mensagem_mqtt from shared.mqtt_handler import enviar_mensagem_mqtt
from shared.contexto_global import ContextoGlobal from shared.contexto_global_redis import CmdKey, ContextoGlobalRedis
from shared.enums import StatusOperacao, T_Code from shared.enums import ManagerWorkerCommandType, StatusOperacao, T_Code
from manager_worker.config import mostrar_log, topico_tx from manager_worker.config import mostrar_log, topico_tx
from manager_worker.processadores.padroes import log_status_operacao
from manager_worker.processadores.parado import ProcessadorParado from manager_worker.processadores.parado import ProcessadorParado
from manager_worker.processadores.aguardando import ProcessadorAguardando from manager_worker.processadores.aguardando import ProcessadorAguardando
from manager_worker.processadores.em_andamento import ProcessadorEmAndamento from manager_worker.processadores.em_andamento import ProcessadorEmAndamento
@ -26,37 +25,33 @@ class ManagerWorker:
if self.processador: if self.processador:
comando = self.processador.processar() comando = self.processador.processar()
#mostrar_log(f"Comando: {comando}") #mostrar_log(f"Comando: {comando}")
resposta = { resposta = {
"configurado": ContextoGlobal.get_operacao("configurado"), "cmd": ManagerWorkerCommandType.EnviarDadosControle.value,
"tempo_aguardando": ContextoGlobal.get_operacao("tempo_aguardando"), "params": comando,
"status_operacao": ContextoGlobal.get_operacao("status"),
"operacao_liberada": ContextoGlobal.get_operacao("liberado"),
"status_modulos": ContextoGlobal.get_operacao("status_modulos") or [],
"comando": comando,
} }
ContextoGlobalRedis.publicar_comando(CmdKey.ManagerWorkerTx, resposta)
else: else:
mostrar_log("Processador nao definido") mostrar_log("Processador nao definido")
if resposta != {}:
self.enviar_comando(resposta)
t1 = time.time() t1 = time.time()
latencia = t1 - t0 latencia = t1 - t0
return latencia return latencia
def definir_processador(self): def definir_processador(self):
status = ContextoGlobal.get_operacao("status") _operacao = ContextoGlobalRedis.get_operacao()
if status == StatusOperacao.NaoIniciado: status = _operacao.get("status")
if status == StatusOperacao.NaoIniciado.value:
self.processador = ProcessadorNaoIniciado() self.processador = ProcessadorNaoIniciado()
elif status == StatusOperacao.Parado: elif status == StatusOperacao.Parado.value:
self.processador = ProcessadorParado() self.processador = ProcessadorParado()
elif status == StatusOperacao.Parametrizando: elif status == StatusOperacao.Parametrizando.value:
self.processador = ProcessadorParametrizando() self.processador = ProcessadorParametrizando()
elif status == StatusOperacao.Aguardando: elif status == StatusOperacao.Aguardando.value:
self.processador = ProcessadorAguardando() self.processador = ProcessadorAguardando()
elif status == StatusOperacao.EmAndamento: elif status == StatusOperacao.EmAndamento.value:
self.processador = ProcessadorEmAndamento() self.processador = ProcessadorEmAndamento()
elif status == StatusOperacao.Concluido: elif status == StatusOperacao.Concluido.value:
self.processador = ProcessadorConcluido() self.processador = ProcessadorConcluido()
else: else:
self.processador = None self.processador = None

View File

@ -3,16 +3,18 @@ import os
import time import time
import json import json
def main(): def main(multi_process):
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "ManagerWorker") print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "ManagerWorker")
from shared.contexto_global import ContextoGlobal from shared.mqtt_handler import iniciar_mqtt, registrar_callback, enviar_mensagem_script_carregado, enviar_mensagem_mqtt
from shared.mqtt_handler import iniciar_mqtt, registrar_callback, enviar_mensagem_script_carregado
from shared.mensagem_worker import WorkerFilaMensagens from shared.mensagem_worker import WorkerFilaMensagens
from manager_worker.gerenciador import ManagerWorker from manager_worker.gerenciador import ManagerWorker
from manager_worker.config import mostrar_log, module_id, topico_tx, topico_rx from manager_worker.config import mostrar_log, module_id, topico_tx, topico_rx
from shared.enums import ManagerWorkerCommandType, T_Code
from manager_worker.modulos.mpc import inicializar as iniciar_mpc
from shared.contexto_global_redis import ContextoGlobalRedis, CmdKey
manager = ManagerWorker() manager = ManagerWorker()
@ -26,8 +28,21 @@ def main():
def processar_comando(topico, message): def processar_comando(topico, message):
try: try:
mensagem = json.loads(message.decode()) mensagem = json.loads(message.decode())
# Você pode tratar mensagens diretas aqui, mas o manager geralmente só envia codigo = ManagerWorkerCommandType(mensagem.get("comando"))
mostrar_log(f"Mensagem recebida: {mensagem}") payload = mensagem.get("payload", {})
#mostrar_log(f"📨 Mensagem recebida no tópico {topico}. Comando recebido: {dispositivo}, payload: {payload}")
#mostrar_log(f"Comando recebido: {dispositivo}")
resposta = {}
if codigo == ManagerWorkerCommandType.IniciarMPC:
iniciar_mpc(payload)
if (resposta != {}):
resposta["cmd"] = codigo
enviar_mensagem_mqtt(topico=topico_tx, dispositivo=T_Code.Mod,objeto=resposta)
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao processar comando: {e}") mostrar_log(f"❌ Erro ao processar comando: {e}")
@ -50,12 +65,25 @@ def main():
time.sleep(delay_corrigido) time.sleep(delay_corrigido)
def redis_callback(dados):
acao = ManagerWorkerCommandType(dados.get("cmd", 0))
if acao == ManagerWorkerCommandType.IniciarMPC:
parametros = dados.get("params", {}).get("parametros")
mapa = dados.get("params", {}).get("mapa")
if parametros is not None:
iniciar_mpc(parametros, mapa)
else:
mostrar_log(f"⚠️ Comando desconhecido: {acao.name}")
def inicializar(): def inicializar():
#iniciar_mqtt(module_id) if multi_process:
iniciar_mqtt(module_id)
mostrar_log("🚀 Iniciando Manager Worker...") mostrar_log("🚀 Iniciando Manager Worker...")
registrar_callback(topico=topico_rx, callback=ao_receber_comando) registrar_callback(topico=topico_rx, callback=ao_receber_comando)
ContextoGlobalRedis.ouvir_comandos(CmdKey.ManagerWorkerRx, redis_callback)
enviar_mensagem_script_carregado(topico_tx) enviar_mensagem_script_carregado(topico_tx)
ContextoGlobal.iniciar_contexto_base()
loop_ativo() loop_ativo()
inicializar() inicializar()

View File

@ -1,42 +1,43 @@
import time
from shared.enums import StatusCarroMapa, StatusModulo, StatusOperacao, T_Code, TipoMovimentoDirecional from shared.enums import StatusCarroMapa, StatusModulo, StatusOperacao, T_Code, TipoMovimentoDirecional
from shared.contexto_global import ContextoGlobal from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
from manager_worker.config import mostrar_log from manager_worker.config import mostrar_log
from visual_worker.config import get_camera_manager from manager_worker.modulos.mpc import get_mpc, comando_parado
from .mpc import get_mpc, comando_parado
def definir_comando(): def definir_comando():
""" """
Define o comando de direção com base em regras mockadas + MPC Define o comando de direção com base em regras mockadas + MPC
""" """
try: try:
gps = ContextoGlobal.get_item(T_Code.Gps, "dados") or {} _contexto = ContextoGlobalRedis.get(CtxKey.DadosContexto, {})
visual_worker_operante = (ContextoGlobal.get_item(T_Code.Snr, "saude", "status") or StatusModulo.DESCONECTADO) == StatusModulo.OPERANTE _operacao = ContextoGlobalRedis.get(CtxKey.DadosOperacao, {})
_gps = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(T_Code.Gps.name), {})
_snr = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(T_Code.Snr.name), {})
_trajetoria = _contexto.get(T_Code.Trajetoria.name, {})
visual_worker_operante = (_snr.get("saude", {}).get("status", StatusModulo.DESCONECTADO.value)) == StatusModulo.OPERANTE.value
contexto = { contexto = {
"Operacao": { "Operacao": {
"Status": ContextoGlobal.get_operacao("status"), "Status": _operacao.get("status"),
"Finalizando": ContextoGlobal.get_operacao("finalizando") "Finalizando": _operacao.get("finalizando")
}, },
"GPS": { "GPS": {
"Latitude": gps.get("lat", 0), "Latitude": _gps.get("lat", 0),
"Longitude": gps.get("lon", 0), "Longitude": _gps.get("lon", 0),
"AnguloCarro": gps.get("theta", 0), "AnguloCarro": _gps.get("theta", 0),
}, },
"Carro": { "Carro": {
"Velocidade": ContextoGlobal.get_item(T_Code.Gerais, "dados", "velocidade_ms") or 0.0, "Velocidade": _contexto.get(T_Code.Gerais.name, {}).get("velocidade_ms", 0.0),
"Status": ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "status") or StatusCarroMapa.Parado.value, "Status": _trajetoria.get("status", StatusCarroMapa.Parado.value),
"ManobrandoEntreRuas": ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "manobrando") or False, "ManobrandoEntreRuas": _trajetoria.get("manobrando", False),
"DentroCorredor": (ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "CorredorAtual") or {}).get("dentro", False), "DentroCorredor": _trajetoria.get("CorredorAtual", {}).get("dentro", False),
"DistanciaEsquerda": (ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "CorredorAtual") or {}).get("dist_esq", 0.0), "DistanciaEsquerda": _trajetoria.get("CorredorAtual", {}).get("dist_esq", 0.0),
"DistanciaDireita": (ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "CorredorAtual") or {}).get("dist_dir", 0.0), "DistanciaDireita": _trajetoria.get("CorredorAtual", {}).get("dist_dir", 0.0),
"IdxProximoPonto": (ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "idx_proximo_ponto") or 0), "IdxProximoPonto": _trajetoria.get("idx_proximo_ponto", 0),
"AnguloCaminho": (ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "angulo") or 0), "AnguloCaminho": _trajetoria.get("angulo", 0.0),
}, },
"VisualWorker": { "VisualWorker": {
"Operante": visual_worker_operante, "Operante": visual_worker_operante,
"Camera": { "Camera": {
"FovH": get_camera_manager().camera.parametros["fov_h"] or 0.7 if get_camera_manager().camera is not None else 0.7, "FovH": ContextoGlobalRedis.get(CtxKey.DadosVisualWorker, {}).get("parametros", {}).get("fov_h", 0.7),
}, },
"MatrizCusto": [[]], "MatrizCusto": [[]],
}, },
@ -69,21 +70,18 @@ def comando_direcional_parado(analise={}):
def regras_taticas(contexto): def regras_taticas(contexto):
try: try:
if contexto["Operacao"]["Status"] != StatusOperacao.EmAndamento or contexto["Operacao"]["Finalizando"]: if contexto["Operacao"]["Status"] != StatusOperacao.EmAndamento.value or contexto["Operacao"]["Finalizando"]:
mostrar_log("🟥 Direcional parado: operação não está em andamento ou está finalizando.") mostrar_log("🟥 Direcional parado: operação não está em andamento ou está finalizando.")
return True, comando_direcional_parado() return True, comando_direcional_parado()
if not contexto.get("VisualWorker", {}).get("Operante", False): vw_operante = contexto.get("VisualWorker", {}).get("Operante", False)
if not vw_operante:
#mostrar_log("⚠️ Sensores principais inativos. Rodando MPC no modo básico.") #mostrar_log("⚠️ Sensores principais inativos. Rodando MPC no modo básico.")
return False, comando_direcional_parado() return False, comando_direcional_parado()
camera_manager = get_camera_manager() imu = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(T_Code.Imu))
if imu is not None:
if camera_manager.camera is None:
return False, comando_direcional_parado()
imu = camera_manager.get_imu_data()
if imu is not None and False:
if abs(imu.get("pitch", 0)) > 15 or abs(imu.get("roll", 0)) > 10: if abs(imu.get("pitch", 0)) > 15 or abs(imu.get("roll", 0)) > 10:
mostrar_log("🟥 Inclinação perigosa detectada. Parando movimentação.") mostrar_log("🟥 Inclinação perigosa detectada. Parando movimentação.")
return True, comando_direcional_parado() return True, comando_direcional_parado()
@ -93,11 +91,13 @@ def regras_taticas(contexto):
mostrar_log("⚠️ Possível travamento detectado (velocidade quase zero).") mostrar_log("⚠️ Possível travamento detectado (velocidade quase zero).")
# Pode evoluir para uma lógica de recuperação no futuro # Pode evoluir para uma lógica de recuperação no futuro
_visual_worker = ContextoGlobalRedis.get(CtxKey.DadosVisualWorker, {})
# 🔸 Pega os dados do contexto # 🔸 Pega os dados do contexto
analise = {} analise = {}
if contexto.get("RegrasAtivas", {}).get("matriz_custo", False): if contexto.get("RegrasAtivas", {}).get("matriz_custo", False):
analise = camera_manager.get_analise_mais_recente() analise = _visual_worker.get("anomalias", {})
obstaculos = analise.get("anomalias", {}).get("deteccoes", []) obstaculos = analise.get("deteccoes", [])
if obstaculos: if obstaculos:
for obst in obstaculos: for obst in obstaculos:
if obst.get("distancia_m", 99) < 0.4: if obst.get("distancia_m", 99) < 0.4:
@ -116,30 +116,31 @@ def definir_comando_mpc(contexto):
mostrar_log("⚠️ MPC ainda não inicializado.") mostrar_log("⚠️ MPC ainda não inicializado.")
return return
_controle = ContextoGlobalRedis.get(CtxKey.DadosControle, {})
comando_anterior = { comando_anterior = {
"angulo": ContextoGlobal.get_controle("angulo_sp") or 0.0, "angulo": _controle.get("angulo_sp", 0.0),
"tipo": ContextoGlobal.get_controle("tipo_movimento_direcional") or TipoMovimentoDirecional.RodasDianteiras "tipo": _controle.get("tipo_movimento_direcional", TipoMovimentoDirecional.RodasDianteiras.value),
"velocidade": _controle.get("velocidade_sp", 0.0)
} }
# 🔸 Requisição ao controlador MPC # 🔸 Requisição ao controlador MPC
t0 = time.time()
comando = mpc.compute_receding(contexto, comando_anterior) comando = mpc.compute_receding(contexto, comando_anterior)
#comando = mpc.compute_horizonte(contexto, comando_anterior) #comando = mpc.compute_horizonte(contexto, comando_anterior)
t1 = time.time()
EPSILON = 1e-3 # 1 milissegundo, pra evitar divisão por zero EPSILON = 1e-3 # 1 milissegundo, pra evitar divisão por zero
if (comando != None): if (comando != None):
# 🔸 Extrai os dados de controle # 🔸 Extrai os dados de controle
angulo_sp = comando.get("angulo", 0.0) ContextoGlobalRedis.atualizar_ctx_dict(CtxKey.DadosControle,
tipo_movimento = comando.get("tipo", TipoMovimentoDirecional.RodasDianteiras) angulo_sp=comando.get("angulo", 0.0),
ContextoGlobal.set_controle_dado("angulo_sp", angulo_sp) tipo_movimento_direcional=comando.get("tipo", TipoMovimentoDirecional.RodasDianteiras.value)
ContextoGlobal.set_controle_dado("tipo_movimento_direcional", tipo_movimento) )
mostrar_log(f"🧭 Direcional MPC | Ângulo: {angulo_sp:.2f}° | Movimento: {tipo_movimento.name} | {(t1 - t0):.3f}s ({1 / max(t1 - t0, EPSILON):.3f} Hz)") _controle = ContextoGlobalRedis.get(CtxKey.DadosControle, {})
else: else:
mostrar_log(f"🧭 Direcional MPC | Erro ao definir comando") mostrar_log(f"🧭 Direcional MPC | Erro ao definir comando")
#input("⏸️ Pressione Enter para continuar...") #input("⏸️ Pressione Enter para continuar...")
return ContextoGlobal.get_controle("angulo_sp"), ContextoGlobal.get_controle("tipo_movimento_direcional"), comando.get("simulacao", []) return _controle.get("angulo_sp"), _controle.get("tipo_movimento_direcional"), comando.get("simulacao", [])
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao definir comando MPC: {e}") mostrar_log(f"❌ Erro ao definir comando MPC: {e}")

View File

@ -1,64 +1,69 @@
from shared.enums import StatusOperacao, StatusCarroMapa, T_Code from shared.enums import StatusOperacao, StatusCarroMapa, T_Code
from shared.contexto_global import ContextoGlobal from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
from manager_worker.config import mostrar_log from manager_worker.config import mostrar_log
from manager_worker.filtros import FiltroVelocidade from manager_worker.filtros import FiltroVelocidade
def definir_comando(): def definir_comando():
status_operacao = ContextoGlobal.get_operacao("status") try:
finalizando = ContextoGlobal.get_operacao("finalizando") _operacao = ContextoGlobalRedis.get(CtxKey.DadosOperacao, {})
status_operacao = _operacao.get("status")
finalizando = _operacao("finalizando")
if (status_operacao != StatusOperacao.EmAndamento) or finalizando: if (status_operacao != StatusOperacao.EmAndamento.value) or finalizando:
mostrar_log("🟥 Movimento parado: operação não está em andamento.") mostrar_log("🟥 Movimento parado: operação não está em andamento.")
return 0 # percentual velocidade = 0 return 0 # percentual velocidade = 0
status_carro = ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "status") _contexto = ContextoGlobalRedis.get(CtxKey.DadosContexto, {})
ervas_no_radar = ContextoGlobal.get_item(T_Code.Gerais, "dados", "ervas_no_radar") _trajetoria = _contexto.get(T_Code.Trajetoria.name, {})
controle_mov = ContextoGlobal.get_operacao(T_Code.Mov.name) or {} status_carro = _trajetoria.get("status")
vel_min = controle_mov.get("percent_vel_min", 0) ervas_no_radar = _contexto.get(T_Code.Gerais.name, {}).get("ervas_no_radar")
vel_max = controle_mov.get("percent_vel_max", 100) controle_mov = _operacao.get(T_Code.Mov.name, {})
vel_min = controle_mov.get("percent_vel_min", 0)
vel_max = controle_mov.get("percent_vel_max", 100)
erro = _trajetoria.get("erro_angular", 0)
velocidade_sp = vel_min
if status_carro == StatusCarroMapa.Parado:
velocidade_sp = 0
elif status_carro == StatusCarroMapa.Direcionando:
manobrando = ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "manobrando")
desvio_necessario = ContextoGlobal.get_sonar("desvio_necessario") or False
if manobrando or desvio_necessario:
velocidade_sp = vel_min
else:
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max)
elif status_carro in [StatusCarroMapa.EntrandoRua, StatusCarroMapa.SaindoRua, StatusCarroMapa.Manobrando]:
velocidade_sp = vel_min velocidade_sp = vel_min
elif status_carro == StatusCarroMapa.CaminhandoRua: if status_carro == StatusCarroMapa.Parado.value:
if ervas_no_radar > 0: velocidade_sp = 0
elif status_carro == StatusCarroMapa.Direcionando.value:
manobrando = _trajetoria.get("manobrando", False)
if manobrando:
velocidade_sp = vel_min
else:
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, erro)
elif status_carro in [StatusCarroMapa.EntrandoRua.value, StatusCarroMapa.SaindoRua.value, StatusCarroMapa.Manobrando.value]:
velocidade_sp = vel_min velocidade_sp = vel_min
elif status_carro == StatusCarroMapa.CaminhandoRua.value:
if ervas_no_radar > 0:
velocidade_sp = vel_min
else:
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, erro)
# Aplica limites
velocidade_sp = max(vel_min, min(vel_max, velocidade_sp))
filtro_vel = FiltroVelocidade()
if velocidade_sp <= vel_min:
filtro_vel.reset(velocidade_sp)
else: else:
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max) velocidade_sp = filtro_vel.filtrar(velocidade_sp)
# Aplica limites ContextoGlobalRedis.atualizar_ctx_dict(CtxKey.DadosControle, velocidade_sp=velocidade_sp)
velocidade_sp = max(vel_min, min(vel_max, velocidade_sp))
filtro_vel = FiltroVelocidade() #mostrar_log(f"🚗 Velocidade definida: {velocidade_sp:.1f}%")
if velocidade_sp <= vel_min: return velocidade_sp
filtro_vel.reset(velocidade_sp) except Exception as e:
else: mostrar_log("Erro ao definir o comando de movimentacao")
velocidade_sp = filtro_vel.filtrar(velocidade_sp)
ContextoGlobal.set_controle_dado("velocidade_sp", velocidade_sp) def calcular_velocidade_relativa(vel_min, vel_max, erro_orientacao):
severidade = min(abs(erro_orientacao) / 30.0, 1.0)
#mostrar_log(f"🚗 Velocidade definida: {velocidade_sp:.1f}%")
return velocidade_sp
def calcular_velocidade_relativa(vel_min, vel_max):
erro = ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "erro_angular") or 0.0
severidade = min(abs(erro) / 30.0, 1.0)
penalidade = severidade * 0.85 penalidade = severidade * 0.85
faixa = vel_max - vel_min faixa = vel_max - vel_min
velocidade = vel_max - (faixa * penalidade) velocidade = vel_max - (faixa * penalidade)

View File

@ -9,16 +9,16 @@ from shared.utils import analisar_linhas_por_profundidade
_mpc = None _mpc = None
def inicializar(parametros): def inicializar(parametros, mapa):
global _mpc global _mpc
_mpc = ControladorMPC(parametros_mpc=parametros) _mpc = ControladorMPC(parametros_mpc=parametros, pontos_mapa=mapa)
mostrar_log(f"MPC iniciado! Trajetoria com {len(_mpc.pontos_info)} pontos") mostrar_log(f"MPC iniciado! Trajetoria com {len(_mpc.pontos_info)} pontos")
def get_mpc(): def get_mpc():
return _mpc return _mpc
class ControladorMPC: class ControladorMPC:
def __init__(self, parametros_mpc): def __init__(self, parametros_mpc, pontos_mapa):
self.raio_terra = 6371000 self.raio_terra = 6371000
self.lat0 = None self.lat0 = None
self.lon0 = None self.lon0 = None
@ -37,17 +37,9 @@ class ControladorMPC:
self.pontos_info = [] self.pontos_info = []
self.visitados_execucao = [] self.visitados_execucao = []
pontos = parametros_mpc.get("pontos", []) self.pontos_info = pontos_mapa
if pontos:
self.lat0, self.lon0 = pontos[0]["lat"], pontos[0]["lon"] if pontos_mapa:
for p in pontos:
x, y = self._latlon_to_xy(p["lat"], p["lon"], self.lat0, self.lon0)
ponto = {
"xy": (x, y),
"tipo": p.get("tipo", 3),
"distanciaMargem": p.get("distanciaMargem", 0.7)
}
self.pontos_info.append(ponto)
self.visitados_execucao = [False] * len(self.pontos_info) self.visitados_execucao = [False] * len(self.pontos_info)
@ -1062,7 +1054,7 @@ class ControladorMPC:
comando = self._processar_mpc_receding(contexto, comando_anterior) comando = self._processar_mpc_receding(contexto, comando_anterior)
t_exec = max((time.time() - self.ultima_atualizacao), 1e-3) t_exec = max((time.time() - self.ultima_atualizacao), 1e-3)
mostrar_log(f"Estatisticas MPC. dt: {self.dt:.2f} s, freq = {(1.0 / self.dt):.2f} Hz, t_exec: {t_exec:.2} s, f_exec: {(1.0 / t_exec):.2f} Hz") mostrar_log(f"🧭 Direcional MPC | Ângulo: {float(comando['angulo']):.2f}° | Movimento: {comando['tipo'].name} | dt: {self.dt:.2f} s | freq = {(1.0 / self.dt):.2f} Hz | t_exec: {t_exec:.2} s | f_exec: {(1.0 / t_exec):.2f} Hz")
return comando return comando
except Exception as e: except Exception as e:
@ -1149,7 +1141,7 @@ class ControladorMPC:
return { return {
"angulo": np.degrees(angulo_final), "angulo": np.degrees(angulo_final),
"tipo": tipo_final, "tipo": tipo_final.value,
"simulacao": simulacao_latlon "simulacao": simulacao_latlon
} }
@ -1233,7 +1225,7 @@ class ControladorMPC:
matriz_custo = contexto.get("VisualWorker", {}).get("MatrizCusto", [[]]) matriz_custo = contexto.get("VisualWorker", {}).get("MatrizCusto", [[]])
com_matriz_custo = contexto.get("VisualWorker", {}).get("Operante", False) com_matriz_custo = contexto.get("VisualWorker", {}).get("Operante", False)
if not com_matriz_custo: if not com_matriz_custo or len(matriz_custo[0]) == 0:
custos_candidatos = {} custos_candidatos = {}
for tipo in tipos: for tipo in tipos:
for ang in angulos_rad: for ang in angulos_rad:
@ -1265,7 +1257,7 @@ class ControladorMPC:
custos_candidatos[(tipo, ang)] = custo + custo_ideal custos_candidatos[(tipo, ang)] = custo + custo_ideal
t01 = time.time() t01 = time.time()
#print(f"Candidatos filtrados em {t01 - t00} segundos, de {len(angulos_deg)} para {len(angulos_validos)}") print(f"Candidatos filtrados em {t01 - t00} segundos, de {len(angulos_deg)} para {len(angulos_validos)}")
return tipos_validos, angulos_validos, custos_candidatos return tipos_validos, angulos_validos, custos_candidatos
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro ao filtrar candidatos por matriz: {e}") mostrar_log(f"❌ Erro ao filtrar candidatos por matriz: {e}")
@ -1371,7 +1363,7 @@ class ControladorMPC:
indices = np.linspace(0, len(restantes) - 1, qtd_restantes).astype(int) indices = np.linspace(0, len(restantes) - 1, qtd_restantes).astype(int)
selecionados = [restantes[i] for i in indices] selecionados = [restantes[i] for i in indices]
#print(f"Candidatos uniformizados de {len(angulos_validos)} para {len(extremos + selecionados)}") print(f"Candidatos uniformizados de {len(angulos_validos)} para {len(extremos + selecionados)}")
return sorted(set(extremos + selecionados)) return sorted(set(extremos + selecionados))
@ -1477,4 +1469,4 @@ class ControladorMPC:
def comando_parado(): def comando_parado():
return 0.0, TipoMovimentoDirecional.RodasDianteiras return 0.0, TipoMovimentoDirecional.RodasDianteiras.value

View File

@ -3,4 +3,7 @@ from manager_worker.processadores.padroes import comando_parado
class ProcessadorAguardando(ProcessadorBase): class ProcessadorAguardando(ProcessadorBase):
def processar(self): def processar(self):
return comando_parado() try:
return comando_parado()
except Exception as e:
print(f"Erro no processador Aguardando: {e}")

View File

@ -3,4 +3,7 @@ from manager_worker.processadores.padroes import comando_parado
class ProcessadorConcluido(ProcessadorBase): class ProcessadorConcluido(ProcessadorBase):
def processar(self): def processar(self):
return comando_parado() try:
return comando_parado()
except Exception as e:
print(f"Erro no processador Concluido: {e}")

View File

@ -1,4 +1,4 @@
from shared.contexto_global import ContextoGlobal from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
from shared.enums import ModoOperacao from shared.enums import ModoOperacao
from .base import ProcessadorBase from .base import ProcessadorBase
from manager_worker.processadores.padroes import comando_controle from manager_worker.processadores.padroes import comando_controle
@ -7,9 +7,12 @@ from manager_worker.modulos.movimentacao import definir_comando as definir_coman
class ProcessadorEmAndamento(ProcessadorBase): class ProcessadorEmAndamento(ProcessadorBase):
def processar(self): def processar(self):
modo = ContextoGlobal.get_operacao("modo") try:
if modo == ModoOperacao.MapaGPS: modo = ContextoGlobalRedis.get(CtxKey.DadosOperacao, {}).get("modo")
angulo_sp, tipo_dir, simulacao = definir_comando_dir() if modo == ModoOperacao.MapaGPS.value:
velocidade_sp = definir_comando_mov() angulo_sp, tipo_dir, simulacao = definir_comando_dir()
return comando_controle(percentual_velocidade=velocidade_sp, angulo=angulo_sp, tipo_movimento=tipo_dir, simulacao=simulacao) velocidade_sp = definir_comando_mov()
return None return comando_controle(percentual_velocidade=velocidade_sp, angulo=angulo_sp, tipo_movimento=tipo_dir, simulacao=simulacao)
return None
except Exception as e:
print(f"Erro no processador EmAndamento: {e}")

View File

@ -3,4 +3,7 @@ from manager_worker.processadores.padroes import comando_parado
class ProcessadorNaoIniciado(ProcessadorBase): class ProcessadorNaoIniciado(ProcessadorBase):
def processar(self): def processar(self):
return comando_parado() try:
return comando_parado()
except Exception as e:
print(f"Erro no processador NaoIniciado: {e}")

View File

@ -3,4 +3,7 @@ from manager_worker.processadores.padroes import comando_parado
class ProcessadorParado(ProcessadorBase): class ProcessadorParado(ProcessadorBase):
def processar(self): def processar(self):
return comando_parado() try:
return comando_parado()
except Exception as e:
print(f"Erro no processador Parado: {e}")

View File

@ -4,5 +4,7 @@ from shared.contexto_global import ContextoGlobal
class ProcessadorParametrizando(ProcessadorBase): class ProcessadorParametrizando(ProcessadorBase):
def processar(self): def processar(self):
try:
return comando_parado() return comando_parado()
except Exception as e:
print(f"Erro no processador Parametrizando: {e}")

View File

@ -1,11 +1,24 @@
import time import time
from shared.enums import T_Code, ModoOperacao, StatusOperacao, StatusModulo, TiposControladorDirecional, VisualWorkerCommandType from shared.enums import ManagerWorkerCommandType, T_Code, ModoOperacao, StatusOperacao, StatusModulo, TiposControladorDirecional, VisualWorkerCommandType
from manager_worker.modulos.mpc import inicializar as iniciar_mpc
def mostrar_log(mensagem):
print(f"{time.time()} - [CONTEXTO_GLOBAL] {mensagem}")
class ContextoGlobal: class ContextoGlobal:
_tempo_inicio_operacao = 5 _tempo_inicio_operacao = 5
_dados = {}
_operacao = {}
_sonar = {}
_controle = {}
@classmethod
def carregar_dicionarios(cls, dados, operacao, sonar, controle):
cls._dados = dados
cls._operacao = operacao
cls._sonar = sonar
cls._controle = controle
@classmethod @classmethod
def iniciar_contexto_base(cls): def iniciar_contexto_base(cls):
@ -16,6 +29,40 @@ class ContextoGlobal:
cls.set_operacao_dado("tempo_aguardando", time.time()) cls.set_operacao_dado("tempo_aguardando", time.time())
cls.set_operacao_dado("status_modulos", []) cls.set_operacao_dado("status_modulos", [])
cls.set_operacao_dado("modulos_mandatorios_pendentes", []) cls.set_operacao_dado("modulos_mandatorios_pendentes", [])
cls.set_operacao_dado("parametros_mandatorios", [])
@classmethod
def iniciar_dicionarios_compartilhados(cls, manager):
for cod in T_Code:
cls._dados[cod.name] = manager.dict()
if cod == T_Code.Dir or cod == T_Code.Mov:
for addr in range(1, 10, 1):
cls._dados[cod.name][addr] = manager.dict()
cls._dados[cod.name][addr]["dados"] = manager.dict()
cls._dados[cod.name][addr]["saude"] = manager.dict()
else:
cls._dados[cod.name]["dados"] = manager.dict()
cls._dados[cod.name]["saude"] = manager.dict()
cls._operacao[cod.name] = manager.dict()
if cod == T_Code.Dir:
cls._operacao[cod.name]["mpc"] = manager.dict()
cls._operacao[cod.name]["mpc"]["pontos"] = manager.list()
cls._operacao["parametros_mandatorios"] = manager.list()
cls._operacao["status_modulos"] = manager.list()
cls._operacao["modulos_mandatorios_pendentes"] = manager.list()
cls._operacao["pontos_mapa"] = manager.list()
cls._operacao["ponto_mapa_ref"] = manager.dict()
cls._sonar["imu"] = manager.dict()
cls._sonar["anomalias"] = manager.dict()
cls._sonar["solo"] = manager.dict()
cls._sonar["radar_2d"] = manager.dict()
cls._sonar["segmentacao"] = manager.dict()
cls._sonar["matriz_confianca"] = manager.list()
cls._sonar["matriz_custo"] = manager.list()
@classmethod @classmethod
def get_timestamp(cls, modulo, endereco=None): def get_timestamp(cls, modulo, endereco=None):
@ -36,7 +83,7 @@ class ContextoGlobal:
} }
_dados = {}
@classmethod @classmethod
def atualizar_contexto(cls, dados_contexto: dict): def atualizar_contexto(cls, dados_contexto: dict):
@ -64,7 +111,7 @@ class ContextoGlobal:
cls._atualizar_dados_operacao() cls._atualizar_dados_operacao()
except Exception as e: except Exception as e:
print(f"{time.time()} - [CONTEXTO_GLOBAL] ❌ Erro ao atualizar contexto: {e}") mostrar_log(f"❌ Erro ao atualizar contexto: {e}")
@classmethod @classmethod
@ -100,11 +147,12 @@ class ContextoGlobal:
_operacao = {}
@classmethod @classmethod
def atualizar_operacao(cls, dados_operacao: dict): def atualizar_operacao(cls, dados_operacao: dict):
cls._operacao = dados_operacao for k, v in dados_operacao.items():
cls._operacao[k] = v
cls._atualizar_dados_operacao() cls._atualizar_dados_operacao()
cls.set_operacao_dado("configurado", True) cls.set_operacao_dado("configurado", True)
@ -248,14 +296,25 @@ class ContextoGlobal:
@classmethod @classmethod
def get_operacao(cls, dado=None): def get_operacao(cls, dado=None):
if (dado == None): try:
return cls._operacao if (dado is None):
else: return cls._operacao
return cls._operacao.get(dado) else:
return cls._operacao.get(dado)
except Exception as e:
mostrar_log(f"❌ Erro ao consultar operacao: {e}")
@classmethod @classmethod
def set_operacao_dado(cls, dado, valor): def set_operacao_dado(cls, dado, valor):
cls._operacao[dado] = valor try:
atual = cls._operacao.get(dado)
if isinstance(atual, list) and hasattr(atual, 'append'):
atual.clear()
atual.extend(valor)
else:
cls._operacao[dado] = valor
except Exception as e:
cls._operacao[dado] = valor
@classmethod @classmethod
def _iniciar_operacao(cls): def _iniciar_operacao(cls):
@ -271,14 +330,15 @@ class ContextoGlobal:
if (dados_dir.get("tipo_controle", TiposControladorDirecional.Manual) == TiposControladorDirecional.MPC): if (dados_dir.get("tipo_controle", TiposControladorDirecional.Manual) == TiposControladorDirecional.MPC):
dados_mpc = dados_dir.get("mpc") or {} dados_mpc = dados_dir.get("mpc") or {}
if (len(dados_mpc.get("pontos", []))) > 0: if (len(dados_mpc.get("pontos", []))) > 0:
iniciar_mpc(dados_mpc) from shared.mqtt_handler import enviar_mensagem_manager_worker
enviar_mensagem_manager_worker(ManagerWorkerCommandType.IniciarMPC, dados_mpc)
@classmethod @classmethod
def _atualizar_visual_worker(cls): def _atualizar_visual_worker(cls):
mx_id = (cls.get_operacao(T_Code.Snr.name) or {}).get("mx_id") mx_id = (cls.get_operacao(T_Code.Snr.name) or {}).get("mx_id")
if mx_id is not None: if mx_id is not None:
from shared.mqtt_handler import enviar_mensagem_visual_worker from shared.mqtt_handler import enviar_mensagem_visual_worker
enviar_mensagem_visual_worker(VisualWorkerCommandType.INICIAR_CAMERA_MANAGER.value, {"mx_id": mx_id}) enviar_mensagem_visual_worker(VisualWorkerCommandType.IniciarCameraManager, {"mx_id": mx_id})
@classmethod @classmethod
def _atualizar_pontos_mapa(cls): def _atualizar_pontos_mapa(cls):
@ -304,11 +364,12 @@ class ContextoGlobal:
cls.set_operacao_dado("ponto_mapa_ref", p_ref) cls.set_operacao_dado("ponto_mapa_ref", p_ref)
_sonar = {}
@classmethod @classmethod
def atualizar_sonar(cls, dados_sonar: dict): def atualizar_sonar(cls, dados_sonar: dict):
cls._sonar = dados_sonar for k, v in dados_sonar.items():
cls._sonar[k] = v
@classmethod @classmethod
def get_sonar(cls, dado=None): def get_sonar(cls, dado=None):
@ -319,7 +380,7 @@ class ContextoGlobal:
_controle = {}
@classmethod @classmethod
def get_controle(cls, dado=None): def get_controle(cls, dado=None):

View File

@ -0,0 +1,343 @@
# contexto_global.py
import threading
import time
import redis
import json
from enum import Enum
from shared.enums import ManagerWorkerCommandType, ModoOperacao, StatusModulo, StatusOperacao, T_Code, TiposControladorDirecional, VisualWorkerCommandType
class CtxKey(str, Enum):
DadosCameras = "ctx:dados_cameras"
DadosModulo = "ctx:dados_modulo_"
DadosOperacao = "ctx:dados_operacao"
DadosContexto = "ctx:dados_contexto"
DadosControle = "ctx:dados_controle"
DadosVisualWorker = "ctx:dados_visual_worker"
class CmdKey(str, Enum):
VisualWorkerTx = "cmd_tx:visual_worker"
VisualWorkerRx = "cmd_rx:visual_worker"
HealthWorkerTx = "cmd_tx:health_worker"
HealthWorkerRx = "cmd_rx:health_worker"
CameraWorkerTx = "cmd_tx:camera_worker"
CameraWorkerRx = "cmd_rx:camera_worker"
ManagerWorkerTx = "cmd_tx:manager_worker"
ManagerWorkerRx = "cmd_rx:manager_worker"
def mostrar_log(mensagem):
print(f"{time.time()} - [Redis] {mensagem}")
class ContextoGlobalRedis:
_redis = redis.Redis(host="localhost", port=6379, decode_responses=True)
@classmethod
def set(cls, chave: CtxKey, valor):
"""Salva qualquer estrutura como JSON"""
try:
chave_str = chave.value if hasattr(chave, "value") else str(chave)
cls._redis.set(chave_str, json.dumps(valor))
except Exception as e:
mostrar_log(f"Erro no set: {e}")
@classmethod
def get(cls, chave: CtxKey, default=None):
"""Lê a estrutura e decodifica de volta para objeto"""
try:
chave_str = chave.value if hasattr(chave, "value") else str(chave)
valor = cls._redis.get(chave_str)
if valor is None:
return default
try:
return json.loads(valor)
except json.JSONDecodeError:
return valor
except Exception as e:
mostrar_log(f"Erro no get, chave: {chave}: {e}")
@classmethod
def hset(cls, chave, campo, valor):
"""Salva um campo dentro de uma hash"""
try:
cls._redis.hset(chave, campo, json.dumps(valor))
except Exception as e:
mostrar_log(f"Erro no hset: {e}")
@classmethod
def hget(cls, chave, campo, default=None):
"""Lê um campo de uma hash"""
try:
valor = cls._redis.hget(chave, campo)
if valor is None:
return default
try:
return json.loads(valor)
except json.JSONDecodeError:
return valor
except Exception as e:
mostrar_log(f"Erro no hget: {e}")
@classmethod
def atualizar_ctx_dict(cls, chave: CtxKey, **novos_campos):
dados = cls.get(chave.value, {})
dados.update(novos_campos)
cls.set(chave, dados)
@classmethod
def publicar_comando(cls, canal: CmdKey, dados):
"""Publica um comando JSON em um canal Redis"""
try:
cls._redis.publish(canal.value, json.dumps(dados))
except Exception as e:
mostrar_log(f"Erro ao publicar comando: {e}")
@classmethod
def ouvir_comandos(cls, canal: CmdKey, funcao_callback):
"""Assina um canal Redis e chama a função para cada comando recebido"""
def _ouvinte():
pubsub = cls._redis.pubsub()
pubsub.subscribe(canal.value)
mostrar_log(f"Escutando canal: {canal.value}")
for mensagem in pubsub.listen():
if mensagem["type"] == "message":
try:
dados = json.loads(mensagem["data"])
funcao_callback(dados)
except Exception as e:
mostrar_log(f"Erro ao processar comando recebido: {e}")
# Roda o ouvinte em uma thread separada (não bloqueia)
threading.Thread(target=_ouvinte, daemon=True).start()
@classmethod
def get_operacao(cls):
return cls.get(CtxKey.DadosOperacao, {})
@classmethod
def get_contexto(cls):
return cls.get(CtxKey.DadosContexto, {})
@classmethod
def get_controle(cls):
return cls.get(CtxKey.DadosControle, {})
@classmethod
def ModKey(cls, mod: T_Code):
"""Lê a estrutura e decodifica de volta para objeto"""
return CtxKey.DadosModulo.value + mod.name
@classmethod
def atualizar_dados_operacao(cls):
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, configurado=True)
cls._atualizar_operacao_liberada()
cls._atualiza_status_operacao()
cls._atualizar_visual_worker()
cls._atualizar_pontos_mapa()
@classmethod
def _atualizar_operacao_liberada(cls):
_operacao = cls.get_operacao()
# 🔹 Check se todos os módulos mandatórios estão disponíveis
modulos_mandatorios = set(_operacao.get("modulos_mandatorios", []))
modulos_presentes = set()
status_modulos = []
for t_code in T_Code:
if t_code.value < 100:
continue
try:
_mod_data = cls.get(cls.ModKey(t_code), {})
_saude = _mod_data.get("saude", {})
conectado = _saude.get("conectado", False)
status = _saude.get("status", (StatusModulo.FALHA.value if conectado else StatusModulo.DESCONECTADO.value))
saude = _saude.get("saude", 0.0)
motivos = _saude.get("motivos", ["Modulo desconectado"])
saude_individual = _saude.get("saude_individual", [])
if status == StatusModulo.OPERANTE.value:
modulos_presentes.add(t_code.value)
status_modulos.append({
"modulo": t_code.value,
"status": status,
"saude": saude,
"motivos": motivos,
"saude_individual": saude_individual
})
except Exception as e:
status_modulos.append({
"modulo": t_code.value,
"status": StatusModulo.FALHA.value,
"saude": 0.0,
"motivos": [f"Erro ao verificar {t_code.name}: {e}"],
"saude_individual": saude_individual
})
todos_presentes = modulos_mandatorios.issubset(modulos_presentes)
modulos_pendentes = modulos_mandatorios - modulos_presentes
cls.atualizar_ctx_dict(
CtxKey.DadosOperacao,
status_modulos=status_modulos,
modulos_mandatorios_pendentes=list(modulos_pendentes)
)
# 🔹 Parâmetros mandatórios
parametros = _operacao.get("parametros_mandatorios", [])
parametros_ok = True
# 🔹 Define status da operação
debug_mode = _operacao.get("debug_mode", False)
# 🔹 Atualiza status de liberação
liberou = (todos_presentes and parametros_ok) or debug_mode
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, liberado=liberou)
@classmethod
def _atualiza_status_operacao(cls):
agora = time.time()
#_contexto = cls.get_contexto()
_operacao = cls.get_operacao()
iniciado = _operacao.get("iniciado", False)
finalizando = _operacao.get("finalizando", False)
op_liberada = _operacao.get("liberado", False)
status_anterior_parado = _operacao.get("status_anterior_parado", StatusOperacao.Parado.value)
status_iniciado_anterior = _operacao.get("status_iniciado_anterior", False)
modo = _operacao.get("modo")
status_atual = _operacao.get("status")
try:
# Iniciando operacao
if (iniciado and not status_iniciado_anterior):
cls._iniciar_operacao()
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, status_iniciado_anterior=iniciado)
if not iniciado:
if status_atual != StatusOperacao.Concluido:
cls.atualizar_ctx_dict(
CtxKey.DadosOperacao,
tempo_aguardando=0,
status=StatusOperacao.Parametrizando.value
)
return # Não avança se não iniciou
# 🔸 Modo MANUAL
if modo == ModoOperacao.Manual:
if op_liberada:
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, status=StatusOperacao.EmAndamento.value)
else:
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, status=StatusOperacao.Parado.value)
return
# 🔸 Modo MAPA GPS ou IA
if modo in (ModoOperacao.MapaGPS, ModoOperacao.TreinamentoIA):
# Verifica se ainda está aguardando tempo inicial
tempo_aguardando = _operacao.get("tempo_aguardando", 0)
if tempo_aguardando == 0:
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, tempo_aguardando=agora)
tempo_aguardando = _operacao.get("tempo_aguardando", 0)
if (tempo_aguardando + _operacao.get("tempo_aguardar_inicio_operacao", 25)) > agora:
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, status=StatusOperacao.Aguardando.value)
return
_corredor = cls.get_contexto().get(T_Code.Trajetoria.name, {}).get("CorredorAtual", {})
#print(f"Corredor Atual: {_corredor}")
if _corredor.get("ultimo", False) and _corredor.get("concluido", False):
if iniciado and not finalizando:
cls._finalizar_operacao()
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, status=StatusOperacao.Concluido.value)
return
# Não está liberado: para operação
if not op_liberada:
if status_atual != StatusOperacao.Parado:
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, status_anterior_parado=status_atual)
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, status=StatusOperacao.Parado.value)
return
# Estava parado e liberou: volta para aguardando
if status_atual == StatusOperacao.Parado:
cls.atualizar_ctx_dict(
CtxKey.DadosOperacao,
tempo_aguardando=agora,
status=StatusOperacao.Aguardando.value
)
return
# Restaura status anterior à parada
if status_anterior_parado != StatusOperacao.Parado:
cls.atualizar_ctx_dict(
CtxKey.DadosOperacao,
status=status_anterior_parado,
status_anterior_parado=StatusOperacao.Parado.value
)
else:
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, status=StatusOperacao.EmAndamento.value)
finally:
pass
#_operacao = cls.get_operacao()
#status = _operacao.get("status")
#liberado = _operacao.get("liberado")
#print(f"Status Atual: {status.name}, Liberado: {liberado}")
@classmethod
def _iniciar_operacao(cls):
cls._atualizar_mpc()
@classmethod
def _finalizar_operacao(cls):
cls.atualizar_ctx_dict(CtxKey.DadosOperacao, iniciado=False)
@classmethod
def _atualizar_mpc(cls):
dados_dir = cls.get_operacao().get(T_Code.Dir.name, {})
if (dados_dir.get("tipo_controle", TiposControladorDirecional.Manual.value) == TiposControladorDirecional.MPC.value):
dados_mpc = dados_dir.get("mpc", {})
mapa = cls.get_operacao().get("pontos_mapa", [])
cls.publicar_comando(CmdKey.ManagerWorkerRx, { "cmd": ManagerWorkerCommandType.IniciarMPC.value, "params": { "parametros": dados_mpc, "mapa": mapa } })
@classmethod
def _atualizar_visual_worker(cls):
mx_id = cls.get_operacao().get(T_Code.Snr.name, {}).get("mx_id")
if mx_id is not None:
cls.publicar_comando(CmdKey.VisualWorkerRx, { "cmd": VisualWorkerCommandType.IniciarCameraManager, "params": mx_id })
@classmethod
def _atualizar_pontos_mapa(cls):
pontos_info = []
p_ref = (0, 0)
_operacao = cls.get_operacao()
dados_dir = _operacao.get(T_Code.Dir.name, {})
if (dados_dir.get("tipo_controle", TiposControladorDirecional.Manual.value) == TiposControladorDirecional.MPC.value):
dados_mpc = dados_dir.get("mpc", {})
pontos = dados_mpc.get("pontos", [])
if pontos is not None and len(pontos) > 0:
p_ref = pontos[0]["lat"], pontos[0]["lon"]
from shared.gps_handler import GPSHandler
handler = GPSHandler(p_ref[0], p_ref[1])
for p in pontos:
x, y = handler.converter_latlon_para_xz(p["lat"], p["lon"])
ponto = {
"xy": (x, y),
"tipo": p.get("tipo", 3),
"distanciaMargem": p.get("distanciaMargem", 0.7)
}
pontos_info.append(ponto)
cls.atualizar_ctx_dict(
CtxKey.DadosOperacao,
pontos_mapa=pontos_info,
ponto_mapa_ref=p_ref
)

View File

@ -1,14 +1,22 @@
from enum import IntEnum from enum import IntEnum
class TipoComando(IntEnum): class VisualWorkerCommandType(IntEnum):
SalvarFrames = 1
IniciarCameraManager = 2
AtualizarSaudeCamera = 3
GetCameraFrame = 4
class HealthWorkerCommandType(IntEnum):
SetDadosModulo = 1 SetDadosModulo = 1
GetDadosModulo = 2 SetDadosContexto = 2
SetDadosContexto = 3 SetDadosOperacao = 3
GetDadosContexto = 4
SetDadosOperacao = 5 class CameraWorkerCommandType(IntEnum):
GetDadosOperacao = 6 GetListaCameras = 1
GetListaCameras = 7
GetCameraFrame = 8 class ManagerWorkerCommandType(IntEnum):
IniciarMPC = 1
EnviarDadosControle = 2
class ModoOperacao(IntEnum): class ModoOperacao(IntEnum):
NaoDefinido = 0 NaoDefinido = 0
@ -37,6 +45,7 @@ class T_Code(IntEnum):
Operacao = 50 Operacao = 50
Trajetoria = 51 Trajetoria = 51
Gerais = 52 Gerais = 52
Imu = 53
Mov = 100 Mov = 100
Dir = 101 Dir = 101
Sen = 102 Sen = 102
@ -135,11 +144,7 @@ class TipoPontoRua(IntEnum):
Desvio = 7 Desvio = 7
# Visual Worker # Visual Worker
class VisualWorkerCommandType(IntEnum):
SALVAR_FRAMES = 0
ATUALIZAR_CAMERA_MANAGER = 1
INICIAR_CAMERA_MANAGER = 2
GET_ULTIMA_ANALISE = 3
class VisualWorkerTipoDeteccao(IntEnum): class VisualWorkerTipoDeteccao(IntEnum):
SEGURO = 1 SEGURO = 1

View File

@ -2,7 +2,7 @@ import paho.mqtt.client as mqtt
import json import json
import time import time
from shared.enums import T_Code, VisualWorkerCommandType from shared.enums import CameraWorkerCommandType, HealthWorkerCommandType, ManagerWorkerCommandType, T_Code, VisualWorkerCommandType
mqtt_client = None mqtt_client = None
debug = True debug = True
@ -68,17 +68,47 @@ def enviar_mensagem_mqtt_entre_modulos(topico: str, dispositivo: T_Code, comando
} }
mqtt_client.publish(topico, json.dumps(mensagem)) mqtt_client.publish(topico, json.dumps(mensagem))
def enviar_mensagem_visual_worker(comando: VisualWorkerCommandType, payload): def enviar_mensagem_visual_worker(comando: VisualWorkerCommandType, payload = {}):
#mostrar_log(f"Enviando dados MQTT para o dispositivo {dispositivo}...") #mostrar_log(f"Enviando dados MQTT para o dispositivo {dispositivo}...")
mensagem = { mensagem = {
"momento": time.time(), "momento": time.time(),
"dispositivo": T_Code.Snr, "comando": comando.value,
"comando": comando,
"payload": payload "payload": payload
} }
from visual_worker.config import topico_rx from visual_worker.config import topico_rx
mqtt_client.publish(topico_rx, json.dumps(mensagem)) mqtt_client.publish(topico_rx, json.dumps(mensagem))
def enviar_mensagem_manager_worker(comando: ManagerWorkerCommandType, payload = {}):
#mostrar_log(f"Enviando dados MQTT para o dispositivo {dispositivo}...")
mensagem = {
"momento": time.time(),
"comando": comando.value,
"payload": payload
}
from manager_worker.config import topico_rx
mqtt_client.publish(topico_rx, json.dumps(mensagem))
def enviar_mensagem_health_worker(comando: HealthWorkerCommandType, payload = {}):
#mostrar_log(f"Enviando dados MQTT para o dispositivo {dispositivo}...")
mensagem = {
"momento": time.time(),
"dispositivo": T_Code.Mod,
"comando": comando.value,
"payload": payload
}
from health_worker.config import topico_rx
mqtt_client.publish(topico_rx, json.dumps(mensagem))
def enviar_mensagem_camera_worker(comando: CameraWorkerCommandType, payload = {}):
#mostrar_log(f"Enviando dados MQTT para o dispositivo {dispositivo}...")
mensagem = {
"momento": time.time(),
"comando": comando.value,
"payload": payload
}
from camera_worker.config import topico_rx
mqtt_client.publish(topico_rx, json.dumps(mensagem))
def registrar_callback(topico, callback): def registrar_callback(topico, callback):
_inscritos[topico] = callback _inscritos[topico] = callback
if mqtt_client: if mqtt_client:

View File

@ -6,8 +6,7 @@ import time
import cv2 import cv2
import cupy as cp import cupy as cp
import numpy as np import numpy as np
from health_worker.config import get_camera_manager from visual_worker.utils import calcular_threshold_anomalias, calcular_referencia, gerar_heatmap
from visual_worker.utils import calcular_threshold_anomalias, calcular_referencia, obter_regiao_solo, calcular_profundidades_setores, gerar_heatmap
from visual_worker.processamento.analise_solo import AnaliseSoloManager from visual_worker.processamento.analise_solo import AnaliseSoloManager
from visual_worker.processamento.analise_anomalias import AnaliseAnomaliasManager from visual_worker.processamento.analise_anomalias import AnaliseAnomaliasManager
from visual_worker.processamento.radar_top_down import Radar2DManager from visual_worker.processamento.radar_top_down import Radar2DManager
@ -15,30 +14,45 @@ from visual_worker.processamento.segmentacao_semantica import ClassesSegmentacao
from shared.enums import T_Code, VisualWorkerTipoAnalise from shared.enums import T_Code, VisualWorkerTipoAnalise
from shared.utils import analisar_linhas_por_profundidade, decode_image_base64 from shared.utils import analisar_linhas_por_profundidade, decode_image_base64
from shared.gps_handler import GPSHandler from shared.gps_handler import GPSHandler
from camera_worker.camera_oak import CameraOak
import depthai as dai
from shared.contexto_global_redis import ContextoGlobalRedis, CtxKey
class CameraManager: class CameraManager:
_iniciando = False # variável de classe (compartilhada por instâncias) def __init__(self, mostrar_log):
def __init__(self, mostrar_log, mx_id=None):
if CameraManager._iniciando:
return
CameraManager._iniciando = True
self.mostrar_log = mostrar_log self.mostrar_log = mostrar_log
self.mx_id = None self.mx_id = None
self.camera = None self.camera = None
self.operante = False self.operante = False
self.iniciando = False
def inicializar(self, mx_id):
if self.iniciando:
return
_camera_conectada = ContextoGlobalRedis.get(CtxKey.DadosCameras, {}).get(mx_id) is not None
if not _camera_conectada:
return
self.iniciando = True
if mx_id == None: if mx_id == None:
self.iniciando = False
#self.mostrar_log(f"❌ Camera não definida.") #self.mostrar_log(f"❌ Camera não definida.")
CameraManager._iniciando = False
return return
self.mx_id = mx_id self.mx_id = mx_id
self.camera = get_camera_manager().get_cameras(mx_id)
try:
dev_info = dai.DeviceInfo(mx_id)
nova = CameraOak(self.mostrar_log, dev_info)
if nova.iniciado:
self.camera = nova
except Exception as e:
self.mostrar_log(f"⚠️ Camera com ID {mx_id} não conectada.")
self.iniciando = False
return
if self.camera is None: if self.camera is None:
self.mostrar_log(f"❌ Camera com ID {mx_id} não encontrada no manager.") self.mostrar_log(f"❌ Camera com ID {mx_id} não iniciada.")
CameraManager._iniciando = False
else: else:
self.mostrar_log(f"📷 Camera visual selecionada: {self.camera.modelo} - {self.camera.mx_id}") self.mostrar_log(f"📷 Camera visual selecionada: {self.camera.modelo} - {self.camera.mx_id}")
self.grid_ref = None self.grid_ref = None
@ -53,8 +67,13 @@ class CameraManager:
self.operante = True self.operante = True
self._ultima_analise = None self._ultima_analise = None
self._timestamp_analise = None self._timestamp_analise = None
self.iniciar_loop_analise_continua(4.0) self._iniciar_loop_analise_continua(4.0)
CameraManager._iniciando = False self.iniciando = False
self.atualizar_saude_camera()
def atualizar_saude_camera(self):
if self.camera is not None:
self.camera.atualizar_saude()
def get_rgb_frame(self): def get_rgb_frame(self):
if self.camera is None: if self.camera is None:
@ -88,16 +107,11 @@ class CameraManager:
return gerar_heatmap(frame, self.camera.parametros["distancia_maxima"]), timestamp, res return gerar_heatmap(frame, self.camera.parametros["distancia_maxima"]), timestamp, res
return None, None, None return None, None, None
def get_imu_data(self): def _iniciar_loop_analise_continua(self, freq):
if self.camera is not None:
return self.camera.requisitar_imu_status()
return None
def iniciar_loop_analise_continua(self, freq):
def loop(): def loop():
while self.operante: while self.operante:
t_ini = time.time() t_ini = time.time()
resultado = self.realizar_analise( resultado = self._realizar_analise(
tipos_analise=[ tipos_analise=[
VisualWorkerTipoAnalise.MATRIZ_CONFIANCA, VisualWorkerTipoAnalise.MATRIZ_CONFIANCA,
VisualWorkerTipoAnalise.ANOMALIA, VisualWorkerTipoAnalise.ANOMALIA,
@ -105,19 +119,24 @@ class CameraManager:
VisualWorkerTipoAnalise.RADAR_2D VisualWorkerTipoAnalise.RADAR_2D
] ]
) )
self._ultima_analise = resultado
self._timestamp_analise = time.time() self._timestamp_analise = time.time()
self._ultima_analise = resultado
dados_visual_worker = {
"timestamp": self._timestamp_analise,
"matriz_confianca": resultado.get("matriz_confianca", [[]]),
"matriz_custo": resultado.get("matriz_custo", [[]]),
"segmentacao_perfil_corredor": resultado.get("segmentacao", {}).get("corredor_perfil", []),
"anomalias": resultado.get("anomalias", {}).get("deteccoes", []),
"solo_perfol": resultado.get("solo", {}).get("perfil", []),
"solo_inclinacao": resultado.get("solo", {}).get("inclinacao", 0),
"profundidade_perfil_corredor": resultado.get("radar_2d", {}).get("analise", {}).get("corredor_perfil", []),
"sombras": resultado.get("radar_2d", {}).get("analise", {}).get("bboxes_sombra", []),
}
ContextoGlobalRedis.set(CtxKey.DadosVisualWorker, dados_visual_worker)
time.sleep(max(0, (1.0 / freq) - (self._timestamp_analise - t_ini))) time.sleep(max(0, (1.0 / freq) - (self._timestamp_analise - t_ini)))
threading.Thread(target=loop, daemon=True).start() threading.Thread(target=loop, daemon=True).start()
def get_analise_mais_recente(self, max_idade_segundos=1.0):
if self._ultima_analise is None:
return {}
t = time.time() - self._timestamp_analise
if t > max_idade_segundos:
self.mostrar_log(f"⚠️ Análise visual desatualizada a {t} segundos. Limite de {max_idade_segundos} segundos.")
return self._ultima_analise
def _calibrar(self, n_frames=50): def _calibrar(self, n_frames=50):
self.calibrado = False self.calibrado = False
@ -142,23 +161,12 @@ class CameraManager:
return False return False
self.depth_referencia = calcular_referencia(lista_frames, metodo="mediana") self.depth_referencia = calcular_referencia(lista_frames, metodo="mediana")
self.setores_referencia = self._salvar_grade_referencia()
self.grid_ref = self._gerar_grid_referencia(self.depth_referencia) self.grid_ref = self._gerar_grid_referencia(self.depth_referencia)
self.calibrado = True self.calibrado = True
self.mostrar_log(f"✅ Calibração concluída com sucesso! Definido {len(self.setores_referencia)} setores no solo") self.mostrar_log(f"✅ Calibração concluída com sucesso! Matriz {self.grid_ref_shape}")
return True return True
def _salvar_grade_referencia(self):
n_setores = self.camera.parametros["qtd_setores_solo"]
distancia_max = self.camera.parametros["distancia_maxima"]
percent_altura = self.camera.parametros["percentual_altura_solo"]
# 🔸 Extrai a região do solo
depth_solo, y_inicio, linhas_solo, altura, largura = obter_regiao_solo(self.depth_referencia, percent_altura)
# 🔸 Calcula as profundidades médias dos setores
setores_referencia, largura_setor = calcular_profundidades_setores(depth_solo, n_setores, largura, distancia_max)
return setores_referencia
def _gerar_grid_referencia(self, depth_frame_medio: np.ndarray): def _gerar_grid_referencia(self, depth_frame_medio: np.ndarray):
altura, largura = depth_frame_medio.shape altura, largura = depth_frame_medio.shape
grid_h, grid_w = self.grid_ref_shape grid_h, grid_w = self.grid_ref_shape
@ -185,7 +193,7 @@ class CameraManager:
return grid_referencia return grid_referencia
def realizar_analise(self, tipos_analise: list, largura_min: float = 0.15, altura_min: float = 0.15): def _realizar_analise(self, tipos_analise: list, largura_min: float = 0.15, altura_min: float = 0.15):
t0 = time.time() t0 = time.time()
resultado = { resultado = {
"timestamp": t0, "timestamp": t0,
@ -193,7 +201,8 @@ class CameraManager:
"solo": {}, "solo": {},
"radar_2d": {}, "radar_2d": {},
"segmentacao": {}, "segmentacao": {},
"matriz_confianca": [] "matriz_confianca": [],
"matriz_custo": []
} }
if not self.calibrado: if not self.calibrado:
@ -350,7 +359,7 @@ class CameraManager:
cv2.imwrite(caminho_heatmap, heatmap_frame) cv2.imwrite(caminho_heatmap, heatmap_frame)
frames_salvos.append(caminho_heatmap) frames_salvos.append(caminho_heatmap)
analise = self.get_analise_mais_recente() analise = self._ultima_analise
for res in analise.keys(): for res in analise.keys():
if (res == "timestamp"): if (res == "timestamp"):
@ -1013,21 +1022,20 @@ class CameraManager:
matriz_custo.append(linha_custo) matriz_custo.append(linha_custo)
# 🔵 Após montar, adiciona bonificação do caminho do mapa (se houver) # 🔵 Após montar, adiciona bonificação do caminho do mapa (se houver)
from shared.contexto_global import ContextoGlobal pontos_xy = ContextoGlobalRedis.get_operacao().get("pontos_mapa", [])
pontos_xy = ContextoGlobal.get_operacao("pontos_mapa") or []
#print(f"Pontos do mapa: {len(pontos_xy)}") #print(f"Pontos do mapa: {len(pontos_xy)}")
if pontos_xy: if len(pontos_xy) > 0:
gps = ContextoGlobal.get_item(T_Code.Gps, "dados") or {} _gps = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(T_Code.Gps), {})
lat_atual = gps.get("lat", 0) lat_atual = _gps.get("lat", 0)
lon_atual = gps.get("lon", 0) lon_atual = _gps.get("lon", 0)
theta_robo = np.radians(gps.get("theta", 0.0)) theta_robo = np.radians(_gps.get("theta", 0.0))
# Converte usando mesmo ponto de referência do mapa # Converte usando mesmo ponto de referência do mapa
p_ref = ContextoGlobal.get_operacao("ponto_mapa_ref") or (0, 0) p_ref = ContextoGlobalRedis.get_operacao().get("ponto_mapa_ref", (0, 0))
gps_handler = GPSHandler(p_ref[0], p_ref[1]) gps_handler = GPSHandler(p_ref[0], p_ref[1])
x_robo, z_robo = gps_handler.converter_latlon_para_xz(lat_atual, lon_atual) x_robo, z_robo = gps_handler.converter_latlon_para_xz(lat_atual, lon_atual)
idx_proximo = ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "idx_proximo_ponto") or 0 idx_proximo = ContextoGlobalRedis.get_contexto().get(T_Code.Trajetoria.name, {}).get("idx_proximo_ponto", 0)
pontos_plotar = [] pontos_plotar = []
distancia_max = 3.0 # linhas_info[0]["distancia"] distancia_max = 3.0 # linhas_info[0]["distancia"]
@ -1132,19 +1140,18 @@ class CameraManager:
# Se tiver ponto do caminho, desenha seta direcional # Se tiver ponto do caminho, desenha seta direcional
try: try:
from shared.contexto_global import ContextoGlobal pontos_xy = ContextoGlobalRedis.get_operacao().get("pontos_mapa", [])
pontos_xy = ContextoGlobal.get_operacao("pontos_mapa") or [] _gps = ContextoGlobalRedis.get(ContextoGlobalRedis.ModKey(T_Code.Gps))
gps = ContextoGlobal.get_item(T_Code.Gps, "dados") or {} if len(pontos_xy) > 0 and _gps:
if pontos_xy and gps: lat, lon = _gps.get("lat", 0), _gps.get("lon", 0)
lat, lon = gps.get("lat", 0), gps.get("lon", 0) theta_robo = np.radians(_gps.get("theta", 0.0))
theta_robo = np.radians(gps.get("theta", 0.0))
from shared.gps_handler import GPSHandler from shared.gps_handler import GPSHandler
p_ref = ContextoGlobal.get_operacao("ponto_mapa_ref") or (0, 0) p_ref = ContextoGlobalRedis.get_operacao().get("ponto_mapa_ref", (0, 0))
handler = GPSHandler(p_ref[0], p_ref[1]) handler = GPSHandler(p_ref[0], p_ref[1])
x_robo, z_robo = handler.converter_latlon_para_xz(lat, lon) x_robo, z_robo = handler.converter_latlon_para_xz(lat, lon)
# Pega o próximo ponto (idx já considerado pelo seu sistema) # Pega o próximo ponto (idx já considerado pelo seu sistema)
idx_proximo = ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "idx_proximo_ponto") or 0 idx_proximo = ContextoGlobalRedis.get_contexto().get(T_Code.Trajetoria.name, {}).get("idx_proximo_ponto", 0)
#idx_proximo -= 1 #idx_proximo -= 1
if idx_proximo < len(pontos_xy): if idx_proximo < len(pontos_xy):
p = pontos_xy[idx_proximo]["xy"] p = pontos_xy[idx_proximo]["xy"]

View File

@ -22,11 +22,11 @@ def iniciar_camera_manager(mx_id):
if mx_id is None: if mx_id is None:
return return
agora = time.time() agora = time.time()
if ((agora - ultima_atualizacao) > 5 and (manager is None or manager.camera is None or manager.mx_id != mx_id)): if (not manager.iniciando and (agora - ultima_atualizacao) > 5 and (manager.camera is None or manager.mx_id != mx_id)):
ultima_atualizacao = agora ultima_atualizacao = agora
mostrar_log("Iniciando Camera Manager...") mostrar_log("Iniciando Camera Manager...")
manager = CameraManager(mostrar_log=mostrar_log, mx_id=mx_id) manager.inicializar(mx_id=mx_id)
if manager.camera is not None: if manager.camera is not None and manager.operante:
mostrar_log(f"Camera manager iniciado, com MX_ID: {mx_id}") mostrar_log(f"Camera manager iniciado, com MX_ID: {mx_id}")
def atualizar_camera_manager(): def atualizar_camera_manager():

View File

@ -3,7 +3,8 @@ import os
import time import time
import json import json
def main():
def main(multi_process):
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "VisualWorker") print("🔎 Caminho sys.path:", os.path.abspath(os.path.join(os.path.dirname(__file__), "..")), "VisualWorker")
@ -11,6 +12,7 @@ def main():
from shared.mensagem_worker import WorkerFilaMensagens from shared.mensagem_worker import WorkerFilaMensagens
from shared.enums import T_Code, VisualWorkerCommandType from shared.enums import T_Code, VisualWorkerCommandType
from visual_worker.config import mostrar_log, module_id, topico_tx, topico_rx, get_camera_manager, iniciar_camera_manager, atualizar_camera_manager from visual_worker.config import mostrar_log, module_id, topico_tx, topico_rx, get_camera_manager, iniciar_camera_manager, atualizar_camera_manager
from shared.contexto_global_redis import ContextoGlobalRedis, CmdKey, CtxKey
# 🔹 Callback que o MQTT chama # 🔹 Callback que o MQTT chama
def ao_receber_comando(topico, payload): def ao_receber_comando(topico, payload):
@ -21,8 +23,7 @@ def main():
try: try:
# Aqui você decodifica o comando e processa normalmente # Aqui você decodifica o comando e processa normalmente
mensagem = json.loads(message.decode()) mensagem = json.loads(message.decode())
dispositivo = T_Code(mensagem.get("dispositivo")) codigo = VisualWorkerCommandType(mensagem.get("comando"))
codigo = int(mensagem.get("comando"))
payload = mensagem.get("payload", {}) payload = mensagem.get("payload", {})
#mostrar_log(f"📨 Mensagem recebida no tópico {topico}. Comando recebido: {dispositivo}, payload: {payload}") #mostrar_log(f"📨 Mensagem recebida no tópico {topico}. Comando recebido: {dispositivo}, payload: {payload}")
@ -30,7 +31,7 @@ def main():
resposta = {} resposta = {}
if codigo == VisualWorkerCommandType.SALVAR_FRAMES: if codigo == VisualWorkerCommandType.SalvarFrames:
manager = get_camera_manager() manager = get_camera_manager()
frames = manager.salvar_frames() frames = manager.salvar_frames()
resposta = { resposta = {
@ -39,21 +40,18 @@ def main():
"frames": frames "frames": frames
} }
} }
elif codigo == VisualWorkerCommandType.ATUALIZAR_CAMERA_MANAGER: elif codigo == VisualWorkerCommandType.AtualizarCameraManager:
atualizar_camera_manager() atualizar_camera_manager()
elif codigo == VisualWorkerCommandType.INICIAR_CAMERA_MANAGER: elif codigo == VisualWorkerCommandType.IniciarCameraManager:
mx_id = payload.get("mx_id") mx_id = payload.get("mx_id")
iniciar_camera_manager(mx_id) iniciar_camera_manager(mx_id)
elif codigo == VisualWorkerCommandType.GET_ULTIMA_ANALISE:
manager = get_camera_manager()
resposta = manager.get_analise_mais_recente()
if (resposta != {}): if (resposta != {}):
resposta["cmd"] = codigo resposta["cmd"] = codigo
enviar_mensagem_mqtt(topico=topico_tx, dispositivo=codigo, objeto=resposta) enviar_mensagem_mqtt(topico=topico_tx, dispositivo=T_Code.Snr, objeto=resposta)
except Exception as e: except Exception as e:
print("❌ Erro ao processar comando MQTT:", e) mostrar_log(f"❌ Erro ao processar comando MQTT: {e}")
# 🔹 Inicia a fila e os workers # 🔹 Inicia a fila e os workers
@ -64,21 +62,49 @@ def main():
def loop_ativo(): def loop_ativo():
while True: while True:
try: try:
a = 1 pass
except KeyboardInterrupt: except KeyboardInterrupt:
mostrar_log("🛑 Encerrando Visual Worker...") mostrar_log("🛑 Encerrando Visual Worker...")
break break
except Exception as e: except Exception as e:
mostrar_log(f"❌ Erro no loop do Visual: {e}") mostrar_log(f"❌ Erro no loop do Visual: {e}")
time.sleep(1) time.sleep(5)
def redis_callback(dados):
acao = VisualWorkerCommandType(dados.get("cmd", 0))
if acao == VisualWorkerCommandType.IniciarCameraManager:
mx_id = dados.get("params")
if mx_id is not None:
iniciar_camera_manager(mx_id)
elif acao == VisualWorkerCommandType.AtualizarSaudeCamera:
get_camera_manager().atualizar_saude_camera()
elif acao == VisualWorkerCommandType.GetCameraFrame:
frame, timestamp, res = get_camera_manager().get_rgb_frame()
base64_img = None
if frame is not None:
from shared.utils import encode_image_base64
base64_img = encode_image_base64(frame)
resposta = {
"cmd": acao.value,
"params": {
"timestamp": timestamp,
"frame": base64_img
}
}
ContextoGlobalRedis.publicar_comando(CmdKey.VisualWorkerTx, resposta)
else:
mostrar_log(f"⚠️ Comando desconhecido: {acao.name}")
# 🔹 Execução principal # 🔹 Execução principal
def inicializar(): def inicializar():
iniciar_mqtt(module_id) if multi_process:
iniciar_mqtt(module_id)
mostrar_log("🚀 Iniciando Visual Worker...") mostrar_log("🚀 Iniciando Visual Worker...")
registrar_callback(topico=topico_rx, callback=ao_receber_comando) registrar_callback(topico=topico_rx, callback=ao_receber_comando)
ContextoGlobalRedis.ouvir_comandos(CmdKey.VisualWorkerRx, redis_callback)
enviar_mensagem_script_carregado(topico_tx) enviar_mensagem_script_carregado(topico_tx)
loop_ativo() loop_ativo()

View File

@ -4,7 +4,6 @@ import cv2
from visual_worker.processamento.gestor_deteccoes import GestorDeteccoes from visual_worker.processamento.gestor_deteccoes import GestorDeteccoes
from visual_worker.filtros import aplicar_filtros_mapa_anomalias, filtrar_deteccoes from visual_worker.filtros import aplicar_filtros_mapa_anomalias, filtrar_deteccoes
from visual_worker.processamento.segmentacao_semantica import ClassesSegmentacao
class AnaliseAnomaliasManager: class AnaliseAnomaliasManager:
def __init__(self): def __init__(self):

View File

@ -0,0 +1,252 @@
Metadata-Version: 2.4
Name: redis
Version: 6.2.0
Summary: Python client for Redis database and key-value store
Project-URL: Changes, https://github.com/redis/redis-py/releases
Project-URL: Code, https://github.com/redis/redis-py
Project-URL: Documentation, https://redis.readthedocs.io/en/latest/
Project-URL: Homepage, https://github.com/redis/redis-py
Project-URL: Issue tracker, https://github.com/redis/redis-py/issues
Author-email: "Redis Inc." <oss@redis.com>
License-Expression: MIT
License-File: LICENSE
Keywords: Redis,database,key-value-store
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.9
Requires-Dist: async-timeout>=4.0.3; python_full_version < '3.11.3'
Provides-Extra: hiredis
Requires-Dist: hiredis>=3.2.0; extra == 'hiredis'
Provides-Extra: jwt
Requires-Dist: pyjwt>=2.9.0; extra == 'jwt'
Provides-Extra: ocsp
Requires-Dist: cryptography>=36.0.1; extra == 'ocsp'
Requires-Dist: pyopenssl>=20.0.1; extra == 'ocsp'
Requires-Dist: requests>=2.31.0; extra == 'ocsp'
Description-Content-Type: text/markdown
# redis-py
The Python interface to the Redis key-value store.
[![CI](https://github.com/redis/redis-py/workflows/CI/badge.svg?branch=master)](https://github.com/redis/redis-py/actions?query=workflow%3ACI+branch%3Amaster)
[![docs](https://readthedocs.org/projects/redis/badge/?version=stable&style=flat)](https://redis-py.readthedocs.io/en/stable/)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![pypi](https://badge.fury.io/py/redis.svg)](https://pypi.org/project/redis/)
[![pre-release](https://img.shields.io/github/v/release/redis/redis-py?include_prereleases&label=latest-prerelease)](https://github.com/redis/redis-py/releases)
[![codecov](https://codecov.io/gh/redis/redis-py/branch/master/graph/badge.svg?token=yenl5fzxxr)](https://codecov.io/gh/redis/redis-py)
[Installation](#installation) | [Usage](#usage) | [Advanced Topics](#advanced-topics) | [Contributing](https://github.com/redis/redis-py/blob/master/CONTRIBUTING.md)
---------------------------------------------
**Note:** redis-py 5.0 will be the last version of redis-py to support Python 3.7, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 5.1 will support Python 3.8+.
**Note:** redis-py 6.1.0 will be the last version of redis-py to support Python 3.8, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 6.2.0 will support Python 3.9+.
---------------------------------------------
## How do I Redis?
[Learn for free at Redis University](https://redis.io/learn/university)
[Try the Redis Cloud](https://redis.io/try-free/)
[Dive in developer tutorials](https://redis.io/learn)
[Join the Redis community](https://redis.io/community/)
[Work at Redis](https://redis.io/careers/)
## Installation
Start a redis via docker (for Redis versions >= 8.0):
``` bash
docker run -p 6379:6379 -it redis:latest
```
Start a redis via docker (for Redis versions < 8.0):
``` bash
docker run -p 6379:6379 -it redis/redis-stack:latest
To install redis-py, simply:
``` bash
$ pip install redis
```
For faster performance, install redis with hiredis support, this provides a compiled response parser, and *for most cases* requires zero code changes.
By default, if hiredis >= 1.0 is available, redis-py will attempt to use it for response parsing.
``` bash
$ pip install "redis[hiredis]"
```
Looking for a high-level library to handle object mapping? See [redis-om-python](https://github.com/redis/redis-om-python)!
## Supported Redis Versions
The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES) and [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES).
The table below highlights version compatibility of the most-recent library versions and redis versions.
| Library version | Supported redis versions |
|-----------------|-------------------|
| 3.5.3 | <= 6.2 Family of releases |
| >= 4.5.0 | Version 5.0 to 7.0 |
| >= 5.0.0 | Version 5.0 to 7.4 |
| >= 6.0.0 | Version 7.2 to current |
## Usage
### Basic Example
``` python
>>> import redis
>>> r = redis.Redis(host='localhost', port=6379, db=0)
>>> r.set('foo', 'bar')
True
>>> r.get('foo')
b'bar'
```
The above code connects to localhost on port 6379, sets a value in Redis, and retrieves it. All responses are returned as bytes in Python, to receive decoded strings, set *decode_responses=True*. For this, and more connection options, see [these examples](https://redis.readthedocs.io/en/stable/examples.html).
#### RESP3 Support
To enable support for RESP3, ensure you have at least version 5.0 of the client, and change your connection object to include *protocol=3*
``` python
>>> import redis
>>> r = redis.Redis(host='localhost', port=6379, db=0, protocol=3)
```
### Connection Pools
By default, redis-py uses a connection pool to manage connections. Each instance of a Redis class receives its own connection pool. You can however define your own [redis.ConnectionPool](https://redis.readthedocs.io/en/stable/connections.html#connection-pools).
``` python
>>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
>>> r = redis.Redis(connection_pool=pool)
```
Alternatively, you might want to look at [Async connections](https://redis.readthedocs.io/en/stable/examples/asyncio_examples.html), or [Cluster connections](https://redis.readthedocs.io/en/stable/connections.html#cluster-client), or even [Async Cluster connections](https://redis.readthedocs.io/en/stable/connections.html#async-cluster-client).
### Redis Commands
There is built-in support for all of the [out-of-the-box Redis commands](https://redis.io/commands). They are exposed using the raw Redis command names (`HSET`, `HGETALL`, etc.) except where a word (i.e. del) is reserved by the language. The complete set of commands can be found [here](https://github.com/redis/redis-py/tree/master/redis/commands), or [the documentation](https://redis.readthedocs.io/en/stable/commands.html).
## Advanced Topics
The [official Redis command documentation](https://redis.io/commands)
does a great job of explaining each command in detail. redis-py attempts
to adhere to the official command syntax. There are a few exceptions:
- **MULTI/EXEC**: These are implemented as part of the Pipeline class.
The pipeline is wrapped with the MULTI and EXEC statements by
default when it is executed, which can be disabled by specifying
transaction=False. See more about Pipelines below.
- **SUBSCRIBE/LISTEN**: Similar to pipelines, PubSub is implemented as
a separate class as it places the underlying connection in a state
where it can\'t execute non-pubsub commands. Calling the pubsub
method from the Redis client will return a PubSub instance where you
can subscribe to channels and listen for messages. You can only call
PUBLISH from the Redis client (see [this comment on issue
#151](https://github.com/redis/redis-py/issues/151#issuecomment-1545015)
for details).
For more details, please see the documentation on [advanced topics page](https://redis.readthedocs.io/en/stable/advanced_features.html).
### Pipelines
The following is a basic example of a [Redis pipeline](https://redis.io/docs/manual/pipelining/), a method to optimize round-trip calls, by batching Redis commands, and receiving their results as a list.
``` python
>>> pipe = r.pipeline()
>>> pipe.set('foo', 5)
>>> pipe.set('bar', 18.5)
>>> pipe.set('blee', "hello world!")
>>> pipe.execute()
[True, True, True]
```
### PubSub
The following example shows how to utilize [Redis Pub/Sub](https://redis.io/docs/manual/pubsub/) to subscribe to specific channels.
``` python
>>> r = redis.Redis(...)
>>> p = r.pubsub()
>>> p.subscribe('my-first-channel', 'my-second-channel', ...)
>>> p.get_message()
{'pattern': None, 'type': 'subscribe', 'channel': b'my-second-channel', 'data': 1}
```
### Redis search and query capabilities default dialect
Release 6.0.0 introduces a client-side default dialect for Redis search and query capabilities.
By default, the client now overrides the server-side dialect with version 2, automatically appending *DIALECT 2* to commands like *FT.AGGREGATE* and *FT.SEARCH*.
**Important**: Be aware that the query dialect may impact the results returned. If needed, you can revert to a different dialect version by configuring the client accordingly.
``` python
>>> from redis.commands.search.field import TextField
>>> from redis.commands.search.query import Query
>>> from redis.commands.search.index_definition import IndexDefinition
>>> import redis
>>> r = redis.Redis(host='localhost', port=6379, db=0)
>>> r.ft().create_index(
>>> (TextField("name"), TextField("lastname")),
>>> definition=IndexDefinition(prefix=["test:"]),
>>> )
>>> r.hset("test:1", "name", "James")
>>> r.hset("test:1", "lastname", "Brown")
>>> # Query with default DIALECT 2
>>> query = "@name: James Brown"
>>> q = Query(query)
>>> res = r.ft().search(q)
>>> # Query with explicit DIALECT 1
>>> query = "@name: James Brown"
>>> q = Query(query).dialect(1)
>>> res = r.ft().search(q)
```
You can find further details in the [query dialect documentation](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/dialects/).
---------------------------------------------
### Author
redis-py is developed and maintained by [Redis Inc](https://redis.io). It can be found [here](
https://github.com/redis/redis-py), or downloaded from [pypi](https://pypi.org/project/redis/).
Special thanks to:
- Andy McCurdy (<sedrik@gmail.com>) the original author of redis-py.
- Ludovico Magnocavallo, author of the original Python Redis client,
from which some of the socket code is still used.
- Alexander Solovyov for ideas on the generic response callback
system.
- Paul Hubbard for initial packaging support.
[![Redis](./docs/_static/logo-redis.svg)](https://redis.io)

View File

@ -0,0 +1,153 @@
redis-6.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
redis-6.2.0.dist-info/METADATA,sha256=nU7-kjJL_I8KHMRNFmLBeS9tWPwTPOw2_oQ8dEBfSPM,10783
redis-6.2.0.dist-info/RECORD,,
redis-6.2.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
redis-6.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
redis-6.2.0.dist-info/licenses/LICENSE,sha256=pXslClvwPXr-VbdAYzE_Ktt7ANVGwKsUmok5gzP-PMg,1074
redis/__init__.py,sha256=h5BepEwJtNeHNFaMJ0VdpbtFFTC8QcY5s9wqAnYG8TE,2060
redis/__pycache__/__init__.cpython-311.pyc,,
redis/__pycache__/backoff.cpython-311.pyc,,
redis/__pycache__/cache.cpython-311.pyc,,
redis/__pycache__/client.cpython-311.pyc,,
redis/__pycache__/cluster.cpython-311.pyc,,
redis/__pycache__/connection.cpython-311.pyc,,
redis/__pycache__/crc.cpython-311.pyc,,
redis/__pycache__/credentials.cpython-311.pyc,,
redis/__pycache__/event.cpython-311.pyc,,
redis/__pycache__/exceptions.cpython-311.pyc,,
redis/__pycache__/lock.cpython-311.pyc,,
redis/__pycache__/ocsp.cpython-311.pyc,,
redis/__pycache__/retry.cpython-311.pyc,,
redis/__pycache__/sentinel.cpython-311.pyc,,
redis/__pycache__/typing.cpython-311.pyc,,
redis/__pycache__/utils.cpython-311.pyc,,
redis/_parsers/__init__.py,sha256=gyf5dp918NuJAkWFl8sX1Z-qAvbX_40-_7YCTM6Rvjc,693
redis/_parsers/__pycache__/__init__.cpython-311.pyc,,
redis/_parsers/__pycache__/base.cpython-311.pyc,,
redis/_parsers/__pycache__/commands.cpython-311.pyc,,
redis/_parsers/__pycache__/encoders.cpython-311.pyc,,
redis/_parsers/__pycache__/helpers.cpython-311.pyc,,
redis/_parsers/__pycache__/hiredis.cpython-311.pyc,,
redis/_parsers/__pycache__/resp2.cpython-311.pyc,,
redis/_parsers/__pycache__/resp3.cpython-311.pyc,,
redis/_parsers/__pycache__/socket.cpython-311.pyc,,
redis/_parsers/base.py,sha256=k6n7-oTmmzAUiiZpaB6Vfjzlj_torwBsaPBEYdOTDak,9908
redis/_parsers/commands.py,sha256=pmR4hl4u93UvCmeDgePHFc6pWDr4slrKEvCsdMmtj_M,11052
redis/_parsers/encoders.py,sha256=X0jvTp-E4TZUlZxV5LJJ88TuVrF1vly5tuC0xjxGaSc,1734
redis/_parsers/helpers.py,sha256=X5wkGDtuzseeCz23_t3FJpzy1ltIvh7zO1uD3cypiOs,29184
redis/_parsers/hiredis.py,sha256=iUjLT5OEgD4zqF_tg3Szmg1c_73RozXyjjAFsVYKCWM,10893
redis/_parsers/resp2.py,sha256=f22kH-_ZP2iNtOn6xOe65MSy_fJpu8OEn1u_hgeeojI,4813
redis/_parsers/resp3.py,sha256=tiZRbyJAnObqll2LQJ57Br-3jxwQcMocV4GQE_LpC6g,9883
redis/_parsers/socket.py,sha256=CKD8QW_wFSNlIZzxlbNduaGpiv0I8wBcsGuAIojDfJg,5403
redis/asyncio/__init__.py,sha256=uoDD8XYVi0Kj6mcufYwLDUTQXmBRx7a0bhKF9stZr7I,1489
redis/asyncio/__pycache__/__init__.cpython-311.pyc,,
redis/asyncio/__pycache__/client.cpython-311.pyc,,
redis/asyncio/__pycache__/cluster.cpython-311.pyc,,
redis/asyncio/__pycache__/connection.cpython-311.pyc,,
redis/asyncio/__pycache__/lock.cpython-311.pyc,,
redis/asyncio/__pycache__/retry.cpython-311.pyc,,
redis/asyncio/__pycache__/sentinel.cpython-311.pyc,,
redis/asyncio/__pycache__/utils.cpython-311.pyc,,
redis/asyncio/client.py,sha256=6a5-txYcRMtObkb7Bfi08MKQQY01oy5NKpHAlfhIFNM,61905
redis/asyncio/cluster.py,sha256=LNEXjBJKr9M13jGnN52BQgYX6PbojCcxT_Jix9W2k0Y,90121
redis/asyncio/connection.py,sha256=32MXfAoa5bOj2rNw-8YKJad6kpDmcOBNz2qsZd4Ty6Q,48828
redis/asyncio/lock.py,sha256=GxgV6EsyKpMjh74KtaOPxh4fNPuwApz6Th46qhvrAws,12801
redis/asyncio/retry.py,sha256=8carxJLme2f0frB9Z0wU3mHqKzwqzGAGb2TMEtaaMvo,2482
redis/asyncio/sentinel.py,sha256=H7N_hvdATojwY06aH1AawFV-05AImqtOSAq0xKElbbk,14636
redis/asyncio/utils.py,sha256=31xFzXczDgSRyf6hSjiwue1eDQ_XlP_OJdp5dKxW_aE,718
redis/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
redis/auth/__pycache__/__init__.cpython-311.pyc,,
redis/auth/__pycache__/err.cpython-311.pyc,,
redis/auth/__pycache__/idp.cpython-311.pyc,,
redis/auth/__pycache__/token.cpython-311.pyc,,
redis/auth/__pycache__/token_manager.cpython-311.pyc,,
redis/auth/err.py,sha256=WYkbuDIzwp1S-eAvsya6QMlO6g9QIXbzMITOsTWX0xk,694
redis/auth/idp.py,sha256=IMDIIb9q72vbIwtFN8vPdaAKZVTdh0HuC5uj5ufqmw4,631
redis/auth/token.py,sha256=qYwAgxFW3S93QDUqp1BTsj7Pj9ZohnixGeOX0s7AsjY,3317
redis/auth/token_manager.py,sha256=ShBsYXiBZBJBOMB_Y-pXfLwEOAmc9s1okaCECinNZ7g,12018
redis/backoff.py,sha256=2zR-ik5enJDsC2n2AWmE3ALSONgDLtyO4k096ZT6Txo,5275
redis/cache.py,sha256=68rJDNogvNwgdgBel6zSX9QziL11qsKIMhmvQvHvznM,9549
redis/client.py,sha256=BrYjhRBWw7Sw3LKPNbdW0UzaQsd-y-wPR70SMgDTeEc,62758
redis/cluster.py,sha256=_Pi_u8U9meq-WcTHV0j4u0lgOQVqmALj37wU1qekWcE,123762
redis/commands/__init__.py,sha256=cTUH-MGvaLYS0WuoytyqtN1wniw2A1KbkUXcpvOSY3I,576
redis/commands/__pycache__/__init__.cpython-311.pyc,,
redis/commands/__pycache__/cluster.cpython-311.pyc,,
redis/commands/__pycache__/core.cpython-311.pyc,,
redis/commands/__pycache__/helpers.cpython-311.pyc,,
redis/commands/__pycache__/redismodules.cpython-311.pyc,,
redis/commands/__pycache__/sentinel.cpython-311.pyc,,
redis/commands/bf/__init__.py,sha256=qk4DA9KsMiP4WYqYeP1T5ScBwctsVtlLyMhrYIyq1Zc,8019
redis/commands/bf/__pycache__/__init__.cpython-311.pyc,,
redis/commands/bf/__pycache__/commands.cpython-311.pyc,,
redis/commands/bf/__pycache__/info.cpython-311.pyc,,
redis/commands/bf/commands.py,sha256=xeKt8E7G8HB-l922J0DLg07CEIZTVNGx_2Lfyw1gIck,21283
redis/commands/bf/info.py,sha256=_OB2v_hAPI9mdVNiBx8jUtH2MhMoct9ZRm-e8In6wQo,3355
redis/commands/cluster.py,sha256=vdWdpl4mP51oqfYBZHg5CUXt6jPaNp7aCLHyTieDrt8,31248
redis/commands/core.py,sha256=lIi3DUP2kPdBC_RXrSTcUpjI_ORL9nKck9YayWIF_Pc,238242
redis/commands/helpers.py,sha256=VCoPdBMCr4wxdWBw1EB9R7ZBbQM0exAG1kws4XwsCII,3318
redis/commands/json/__init__.py,sha256=bznXhLYR652rfLfLp8cz0ZN0Yr8IRx4FgON_tq9_2Io,4845
redis/commands/json/__pycache__/__init__.cpython-311.pyc,,
redis/commands/json/__pycache__/_util.cpython-311.pyc,,
redis/commands/json/__pycache__/commands.cpython-311.pyc,,
redis/commands/json/__pycache__/decoders.cpython-311.pyc,,
redis/commands/json/__pycache__/path.cpython-311.pyc,,
redis/commands/json/_util.py,sha256=b_VQTh10FyLl8BtREfJfDagOJCyd6wTQQs8g63pi5GI,116
redis/commands/json/commands.py,sha256=ih8upnxeOpjPZXNfqeFBYxiCN2Cmyv8UGu3AlQnT6JQ,15723
redis/commands/json/decoders.py,sha256=a_IoMV_wgeJyUifD4P6HTcM9s6FhricwmzQcZRmc-Gw,1411
redis/commands/json/path.py,sha256=0zaO6_q_FVMk1Bkhkb7Wcr8AF2Tfr69VhkKy1IBVhpA,393
redis/commands/redismodules.py,sha256=-kLM4RBklDhNh-MXCra81ZTSstIQ-ulRab6v0dYUTdA,2573
redis/commands/search/__init__.py,sha256=happQFVF0j7P87p7LQsUK5AK0kuem9cA-xvVRdQWpos,5744
redis/commands/search/__pycache__/__init__.cpython-311.pyc,,
redis/commands/search/__pycache__/_util.cpython-311.pyc,,
redis/commands/search/__pycache__/aggregation.cpython-311.pyc,,
redis/commands/search/__pycache__/commands.cpython-311.pyc,,
redis/commands/search/__pycache__/dialect.cpython-311.pyc,,
redis/commands/search/__pycache__/document.cpython-311.pyc,,
redis/commands/search/__pycache__/field.cpython-311.pyc,,
redis/commands/search/__pycache__/index_definition.cpython-311.pyc,,
redis/commands/search/__pycache__/profile_information.cpython-311.pyc,,
redis/commands/search/__pycache__/query.cpython-311.pyc,,
redis/commands/search/__pycache__/querystring.cpython-311.pyc,,
redis/commands/search/__pycache__/reducers.cpython-311.pyc,,
redis/commands/search/__pycache__/result.cpython-311.pyc,,
redis/commands/search/__pycache__/suggestion.cpython-311.pyc,,
redis/commands/search/_util.py,sha256=9Mp72OO5Ib5UbfN7uXb-iB7hQCm1jQLV90ms2P9XSGU,219
redis/commands/search/aggregation.py,sha256=CcZSZyquLWLrcSblwgt-bSyMvm-TQS9B7N8QI_ahCBU,11582
redis/commands/search/commands.py,sha256=ozyF6YgCiMArhb6ScXLPy49hnJwm4CGK4vrJRwSeB-I,38413
redis/commands/search/dialect.py,sha256=-7M6kkr33x0FkMtKmUsbeRAE6qxLUbqdJCqIo0UKIXo,105
redis/commands/search/document.py,sha256=g2R-PRgq-jN33_GLXzavvse4cpIHBMfjPfPK7tnE9Gc,413
redis/commands/search/field.py,sha256=ZWHYTtrLi-zZojohqXoidfllxP0SiadBW6hnGkBw7mM,5891
redis/commands/search/index_definition.py,sha256=VL2CMzjxN0HEIaTn88evnHX1fCEmytbik4vAmiiYSC8,2489
redis/commands/search/profile_information.py,sha256=w9SbMiHbcZ1TpsZMe8cMIyO1hGkm5GhnZ_Gqg1feLtc,249
redis/commands/search/query.py,sha256=MbSs-cY7hG1OEkO-i6LJ_Ui1D3d2VyDTXPrmb-rty7w,12199
redis/commands/search/querystring.py,sha256=dE577kOqkCErNgO-IXI4xFVHI8kQE-JiH5ZRI_CKjHE,7597
redis/commands/search/reducers.py,sha256=Scceylx8BjyqS-TJOdhNW63n6tecL9ojt4U5Sqho5UY,4220
redis/commands/search/result.py,sha256=iuqmwOeCNo_7N4a_YxxDzVdOTpbwfF1T2uuq5sTqzMo,2624
redis/commands/search/suggestion.py,sha256=V_re6suDCoNc0ETn_P1t51FeK4pCamPwxZRxCY8jscE,1612
redis/commands/sentinel.py,sha256=hRcIQ9x9nEkdcCsJzo6Ves6vk-3tsfQqfJTT_v3oLY0,4110
redis/commands/timeseries/__init__.py,sha256=k492_xE_lBD0cVSX82TWBiNxOWuDDrrVZUjINi3LZSc,3450
redis/commands/timeseries/__pycache__/__init__.cpython-311.pyc,,
redis/commands/timeseries/__pycache__/commands.cpython-311.pyc,,
redis/commands/timeseries/__pycache__/info.cpython-311.pyc,,
redis/commands/timeseries/__pycache__/utils.cpython-311.pyc,,
redis/commands/timeseries/commands.py,sha256=8Z2BEyP23qTYCJR_e9zdG11yWmIDwGBMO2PJNLtK2BA,47147
redis/commands/timeseries/info.py,sha256=meZYdu7IV9KaUWMKZs9qW4vo3Q9MwhdY-EBtKQzls5o,3223
redis/commands/timeseries/utils.py,sha256=NLwSOS5Dz9N8dYQSzEyBIvrItOWwfQ0xgDj8un6x3dU,1319
redis/commands/vectorset/__init__.py,sha256=_fM0UdYjuzs8YWIUjQGH9QX5FwI0So8_D-5ALWWrWFc,1322
redis/commands/vectorset/__pycache__/__init__.cpython-311.pyc,,
redis/commands/vectorset/__pycache__/commands.cpython-311.pyc,,
redis/commands/vectorset/__pycache__/utils.cpython-311.pyc,,
redis/commands/vectorset/commands.py,sha256=7CvQNFvkXuG3XPhHJ82y_oBYJwewRFz84aEi3OCH4Rw,12495
redis/commands/vectorset/utils.py,sha256=N-x0URyg76XC39CNfBym6FkFCVgm5NthzWKBnc2H0Xc,2981
redis/connection.py,sha256=z1m1-maULRuw4KeiuKXmzL_DtFxcXiqMbKFJm_3V7ao,66905
redis/crc.py,sha256=Z3kXFtkY2LdgefnQMud1xr4vG5UYvA9LCMqNMX1ywu4,729
redis/credentials.py,sha256=GOnO3-LSW34efHaIrUbS742Mw8l70mRzF6UrKiKZsMY,1828
redis/event.py,sha256=urOK241IdgmCQ3fq7GqXRstZ2vcXRV14bBBMdN3latk,12129
redis/exceptions.py,sha256=46H-asqIaZ65Gc-voGzP7S39JtxdICGdHzdsT6LSMJE,5617
redis/lock.py,sha256=GrvPSxaOqKo7iAL2oi5ZUEPsOkxAXHVE_Tp1ejgO2fY,12760
redis/ocsp.py,sha256=teYSmKnCtk6B3jJLdNYbZN4OE0mxgspt2zUPbkIQzio,11452
redis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
redis/retry.py,sha256=qJPRTARm3TYQPRNnzyjUUjGohbwtMz5KoDphCtHJ44Y,2902
redis/sentinel.py,sha256=DBphu6uNp6ZCSaVDSVC8nFhSxG93a12DnzgGWpyeh64,14757
redis/typing.py,sha256=z5JQjGkNzejEzb2y7TXct7tS5yzAfLQod9o37Mh1_Ug,1953
redis/utils.py,sha256=q7tQJs6Tj5M91QypOwZtGnFXs2Bcji3eTt9VEwLYOeA,8386

View File

@ -0,0 +1,4 @@
Wheel-Version: 1.0
Generator: hatchling 1.27.0
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2023, Redis, inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,87 @@
from redis import asyncio # noqa
from redis.backoff import default_backoff
from redis.client import Redis, StrictRedis
from redis.cluster import RedisCluster
from redis.connection import (
BlockingConnectionPool,
Connection,
ConnectionPool,
SSLConnection,
UnixDomainSocketConnection,
)
from redis.credentials import CredentialProvider, UsernamePasswordCredentialProvider
from redis.exceptions import (
AuthenticationError,
AuthenticationWrongNumberOfArgsError,
BusyLoadingError,
ChildDeadlockedError,
ConnectionError,
CrossSlotTransactionError,
DataError,
InvalidPipelineStack,
InvalidResponse,
OutOfMemoryError,
PubSubError,
ReadOnlyError,
RedisClusterException,
RedisError,
ResponseError,
TimeoutError,
WatchError,
)
from redis.sentinel import (
Sentinel,
SentinelConnectionPool,
SentinelManagedConnection,
SentinelManagedSSLConnection,
)
from redis.utils import from_url
def int_or_str(value):
try:
return int(value)
except ValueError:
return value
# This version is used when building the package for publishing
__version__ = "6.2.0"
VERSION = tuple(map(int_or_str, __version__.split(".")))
__all__ = [
"AuthenticationError",
"AuthenticationWrongNumberOfArgsError",
"BlockingConnectionPool",
"BusyLoadingError",
"ChildDeadlockedError",
"Connection",
"ConnectionError",
"ConnectionPool",
"CredentialProvider",
"CrossSlotTransactionError",
"DataError",
"from_url",
"default_backoff",
"InvalidPipelineStack",
"InvalidResponse",
"OutOfMemoryError",
"PubSubError",
"ReadOnlyError",
"Redis",
"RedisCluster",
"RedisClusterException",
"RedisError",
"ResponseError",
"Sentinel",
"SentinelConnectionPool",
"SentinelManagedConnection",
"SentinelManagedSSLConnection",
"SSLConnection",
"UsernamePasswordCredentialProvider",
"StrictRedis",
"TimeoutError",
"UnixDomainSocketConnection",
"WatchError",
]

View File

@ -0,0 +1,27 @@
from .base import (
AsyncPushNotificationsParser,
BaseParser,
PushNotificationsParser,
_AsyncRESPBase,
)
from .commands import AsyncCommandsParser, CommandsParser
from .encoders import Encoder
from .hiredis import _AsyncHiredisParser, _HiredisParser
from .resp2 import _AsyncRESP2Parser, _RESP2Parser
from .resp3 import _AsyncRESP3Parser, _RESP3Parser
__all__ = [
"AsyncCommandsParser",
"_AsyncHiredisParser",
"_AsyncRESPBase",
"_AsyncRESP2Parser",
"_AsyncRESP3Parser",
"AsyncPushNotificationsParser",
"CommandsParser",
"Encoder",
"BaseParser",
"_HiredisParser",
"_RESP2Parser",
"_RESP3Parser",
"PushNotificationsParser",
]

View File

@ -0,0 +1,289 @@
import sys
from abc import ABC
from asyncio import IncompleteReadError, StreamReader, TimeoutError
from typing import Callable, List, Optional, Protocol, Union
if sys.version_info.major >= 3 and sys.version_info.minor >= 11:
from asyncio import timeout as async_timeout
else:
from async_timeout import timeout as async_timeout
from ..exceptions import (
AskError,
AuthenticationError,
AuthenticationWrongNumberOfArgsError,
BusyLoadingError,
ClusterCrossSlotError,
ClusterDownError,
ConnectionError,
ExecAbortError,
MasterDownError,
ModuleError,
MovedError,
NoPermissionError,
NoScriptError,
OutOfMemoryError,
ReadOnlyError,
RedisError,
ResponseError,
TryAgainError,
)
from ..typing import EncodableT
from .encoders import Encoder
from .socket import SERVER_CLOSED_CONNECTION_ERROR, SocketBuffer
MODULE_LOAD_ERROR = "Error loading the extension. Please check the server logs."
NO_SUCH_MODULE_ERROR = "Error unloading module: no such module with that name"
MODULE_UNLOAD_NOT_POSSIBLE_ERROR = "Error unloading module: operation not possible."
MODULE_EXPORTS_DATA_TYPES_ERROR = (
"Error unloading module: the module "
"exports one or more module-side data "
"types, can't unload"
)
# user send an AUTH cmd to a server without authorization configured
NO_AUTH_SET_ERROR = {
# Redis >= 6.0
"AUTH <password> called without any password "
"configured for the default user. Are you sure "
"your configuration is correct?": AuthenticationError,
# Redis < 6.0
"Client sent AUTH, but no password is set": AuthenticationError,
}
class BaseParser(ABC):
EXCEPTION_CLASSES = {
"ERR": {
"max number of clients reached": ConnectionError,
"invalid password": AuthenticationError,
# some Redis server versions report invalid command syntax
# in lowercase
"wrong number of arguments "
"for 'auth' command": AuthenticationWrongNumberOfArgsError,
# some Redis server versions report invalid command syntax
# in uppercase
"wrong number of arguments "
"for 'AUTH' command": AuthenticationWrongNumberOfArgsError,
MODULE_LOAD_ERROR: ModuleError,
MODULE_EXPORTS_DATA_TYPES_ERROR: ModuleError,
NO_SUCH_MODULE_ERROR: ModuleError,
MODULE_UNLOAD_NOT_POSSIBLE_ERROR: ModuleError,
**NO_AUTH_SET_ERROR,
},
"OOM": OutOfMemoryError,
"WRONGPASS": AuthenticationError,
"EXECABORT": ExecAbortError,
"LOADING": BusyLoadingError,
"NOSCRIPT": NoScriptError,
"READONLY": ReadOnlyError,
"NOAUTH": AuthenticationError,
"NOPERM": NoPermissionError,
"ASK": AskError,
"TRYAGAIN": TryAgainError,
"MOVED": MovedError,
"CLUSTERDOWN": ClusterDownError,
"CROSSSLOT": ClusterCrossSlotError,
"MASTERDOWN": MasterDownError,
}
@classmethod
def parse_error(cls, response):
"Parse an error response"
error_code = response.split(" ")[0]
if error_code in cls.EXCEPTION_CLASSES:
response = response[len(error_code) + 1 :]
exception_class = cls.EXCEPTION_CLASSES[error_code]
if isinstance(exception_class, dict):
exception_class = exception_class.get(response, ResponseError)
return exception_class(response)
return ResponseError(response)
def on_disconnect(self):
raise NotImplementedError()
def on_connect(self, connection):
raise NotImplementedError()
class _RESPBase(BaseParser):
"""Base class for sync-based resp parsing"""
def __init__(self, socket_read_size):
self.socket_read_size = socket_read_size
self.encoder = None
self._sock = None
self._buffer = None
def __del__(self):
try:
self.on_disconnect()
except Exception:
pass
def on_connect(self, connection):
"Called when the socket connects"
self._sock = connection._sock
self._buffer = SocketBuffer(
self._sock, self.socket_read_size, connection.socket_timeout
)
self.encoder = connection.encoder
def on_disconnect(self):
"Called when the socket disconnects"
self._sock = None
if self._buffer is not None:
self._buffer.close()
self._buffer = None
self.encoder = None
def can_read(self, timeout):
return self._buffer and self._buffer.can_read(timeout)
class AsyncBaseParser(BaseParser):
"""Base parsing class for the python-backed async parser"""
__slots__ = "_stream", "_read_size"
def __init__(self, socket_read_size: int):
self._stream: Optional[StreamReader] = None
self._read_size = socket_read_size
async def can_read_destructive(self) -> bool:
raise NotImplementedError()
async def read_response(
self, disable_decoding: bool = False
) -> Union[EncodableT, ResponseError, None, List[EncodableT]]:
raise NotImplementedError()
_INVALIDATION_MESSAGE = [b"invalidate", "invalidate"]
class PushNotificationsParser(Protocol):
"""Protocol defining RESP3-specific parsing functionality"""
pubsub_push_handler_func: Callable
invalidation_push_handler_func: Optional[Callable] = None
def handle_pubsub_push_response(self, response):
"""Handle pubsub push responses"""
raise NotImplementedError()
def handle_push_response(self, response, **kwargs):
if response[0] not in _INVALIDATION_MESSAGE:
return self.pubsub_push_handler_func(response)
if self.invalidation_push_handler_func:
return self.invalidation_push_handler_func(response)
def set_pubsub_push_handler(self, pubsub_push_handler_func):
self.pubsub_push_handler_func = pubsub_push_handler_func
def set_invalidation_push_handler(self, invalidation_push_handler_func):
self.invalidation_push_handler_func = invalidation_push_handler_func
class AsyncPushNotificationsParser(Protocol):
"""Protocol defining async RESP3-specific parsing functionality"""
pubsub_push_handler_func: Callable
invalidation_push_handler_func: Optional[Callable] = None
async def handle_pubsub_push_response(self, response):
"""Handle pubsub push responses asynchronously"""
raise NotImplementedError()
async def handle_push_response(self, response, **kwargs):
"""Handle push responses asynchronously"""
if response[0] not in _INVALIDATION_MESSAGE:
return await self.pubsub_push_handler_func(response)
if self.invalidation_push_handler_func:
return await self.invalidation_push_handler_func(response)
def set_pubsub_push_handler(self, pubsub_push_handler_func):
"""Set the pubsub push handler function"""
self.pubsub_push_handler_func = pubsub_push_handler_func
def set_invalidation_push_handler(self, invalidation_push_handler_func):
"""Set the invalidation push handler function"""
self.invalidation_push_handler_func = invalidation_push_handler_func
class _AsyncRESPBase(AsyncBaseParser):
"""Base class for async resp parsing"""
__slots__ = AsyncBaseParser.__slots__ + ("encoder", "_buffer", "_pos", "_chunks")
def __init__(self, socket_read_size: int):
super().__init__(socket_read_size)
self.encoder: Optional[Encoder] = None
self._buffer = b""
self._chunks = []
self._pos = 0
def _clear(self):
self._buffer = b""
self._chunks.clear()
def on_connect(self, connection):
"""Called when the stream connects"""
self._stream = connection._reader
if self._stream is None:
raise RedisError("Buffer is closed.")
self.encoder = connection.encoder
self._clear()
self._connected = True
def on_disconnect(self):
"""Called when the stream disconnects"""
self._connected = False
async def can_read_destructive(self) -> bool:
if not self._connected:
raise RedisError("Buffer is closed.")
if self._buffer:
return True
try:
async with async_timeout(0):
return self._stream.at_eof()
except TimeoutError:
return False
async def _read(self, length: int) -> bytes:
"""
Read `length` bytes of data. These are assumed to be followed
by a '\r\n' terminator which is subsequently discarded.
"""
want = length + 2
end = self._pos + want
if len(self._buffer) >= end:
result = self._buffer[self._pos : end - 2]
else:
tail = self._buffer[self._pos :]
try:
data = await self._stream.readexactly(want - len(tail))
except IncompleteReadError as error:
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR) from error
result = (tail + data)[:-2]
self._chunks.append(data)
self._pos += want
return result
async def _readline(self) -> bytes:
"""
read an unknown number of bytes up to the next '\r\n'
line separator, which is discarded.
"""
found = self._buffer.find(b"\r\n", self._pos)
if found >= 0:
result = self._buffer[self._pos : found]
else:
tail = self._buffer[self._pos :]
data = await self._stream.readline()
if not data.endswith(b"\r\n"):
raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
result = (tail + data)[:-2]
self._chunks.append(data)
self._pos += len(result) + 2
return result

Some files were not shown because too many files have changed in this diff Show More