iniciado migracao para uso do Redis
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -535,7 +535,10 @@ namespace AgroBase.Models.Operadores
|
||||||
|
|
||||||
public enum VisualWorkerCommandType
|
public enum VisualWorkerCommandType
|
||||||
{
|
{
|
||||||
SalvarFrames = 0
|
SalvarFrames = 1,
|
||||||
|
IniciarCameraManager = 2,
|
||||||
|
AtualizarSaudeCamera = 3,
|
||||||
|
GetCameraFrame = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,25 +181,11 @@ 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,
|
|
||||||
dispositivo = (int)T_Code.Mod,
|
|
||||||
payload = new
|
|
||||||
{
|
|
||||||
Operacao = new
|
|
||||||
{
|
|
||||||
debug_mode = Variaveis.DebugMode,
|
|
||||||
iniciado = Variaveis.OperacaoEmAndamento.Iniciado,
|
|
||||||
finalizando = Variaveis.OperacaoEmAndamento.Finalizando,
|
|
||||||
emergencia = Variaveis.OperacaoEmAndamento.Emergencia,
|
|
||||||
},
|
|
||||||
Trajetoria = new
|
Trajetoria = new
|
||||||
{
|
{
|
||||||
status = (int)_Sensoriamento.Trajetoria.StatusCarro,
|
status = (int)_Sensoriamento.Trajetoria.StatusCarro,
|
||||||
|
|
@ -216,6 +209,92 @@ namespace AgroBase.Services.Operadores
|
||||||
velocidade_ms = _Sensoriamento.Movimentacao.VelocidadeMedia,
|
velocidade_ms = _Sensoriamento.Movimentacao.VelocidadeMedia,
|
||||||
ervas_no_radar = _Sensoriamento.Atuador.ErvasNoRadar,
|
ervas_no_radar = _Sensoriamento.Atuador.ErvasNoRadar,
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var comando = new
|
||||||
|
{
|
||||||
|
cmd = (int)HealthWorkerCommandType.SetDadosContexto,
|
||||||
|
@params = dados
|
||||||
|
};
|
||||||
|
|
||||||
|
RedisService.Publish(CmdKey.HealthWorkerRx, JsonConvert.SerializeObject(comando));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnviarDadosOperacao()
|
||||||
|
{
|
||||||
|
if (Variaveis.OperacaoEmAndamento.DispMvd == null) return;
|
||||||
|
|
||||||
|
var _Controle = Variaveis.OperacaoEmAndamento.Controle;
|
||||||
|
|
||||||
|
var dados = new
|
||||||
|
{
|
||||||
|
modo = (int)Variaveis.OperacaoEmAndamento.Modo,
|
||||||
|
debug_mode = Variaveis.DebugMode,
|
||||||
|
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
|
||||||
|
{
|
||||||
|
percent_vel_max = _Controle.PercentualVelocidadeMax,
|
||||||
|
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
|
||||||
|
{
|
||||||
|
lat = p.Posicao.Latitude,
|
||||||
|
lon = p.Posicao.Longitude,
|
||||||
|
tipo = (int)p.Tipo,
|
||||||
|
distanciaMargem = p.LarguraCorredor * 0.8
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
horizonte = 2.5,
|
||||||
|
angulo_max_graus = _Controle.Angulo_Max,
|
||||||
|
distancia_entre_eixos = (VariaveisEquipamento.DistanciaEntreEixos / 100.0),
|
||||||
|
largura_equipamento = (VariaveisEquipamento.LarguraEquipamentoMm / 1000.0),
|
||||||
|
velocidade_min = FuncoesMatematicas.CalculaVelocidadeMsPercentual(_Controle.PercentualVelocidadeMin),
|
||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var comando = new
|
||||||
|
{
|
||||||
|
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
|
Gps = new
|
||||||
{
|
{
|
||||||
conectado = GPSService.Iniciado,
|
conectado = GPSService.Iniciado,
|
||||||
|
|
@ -242,7 +321,8 @@ namespace AgroBase.Services.Operadores
|
||||||
sentido = x.DirMotor.Sentido,
|
sentido = x.DirMotor.Sentido,
|
||||||
sentido_sp = x.DirMotor.Sentido_SP,
|
sentido_sp = x.DirMotor.Sentido_SP,
|
||||||
sentido_real = x.DirMotor.SentidoReal,
|
sentido_real = x.DirMotor.SentidoReal,
|
||||||
freq = new {
|
freq = new
|
||||||
|
{
|
||||||
io = x.DirMotor.Leitura?.NumeroPulsos?.frequencia ?? 0,
|
io = x.DirMotor.Leitura?.NumeroPulsos?.frequencia ?? 0,
|
||||||
pulsos = x.DirMotor.Leitura?.EntradaIN1?.frequencia ?? 0,
|
pulsos = x.DirMotor.Leitura?.EntradaIN1?.frequencia ?? 0,
|
||||||
}
|
}
|
||||||
|
|
@ -310,78 +390,16 @@ namespace AgroBase.Services.Operadores
|
||||||
}),
|
}),
|
||||||
}).ToArray(),
|
}).ToArray(),
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Variaveis.MqttService.PublishAsync(topicoTx, JsonConvert.SerializeObject(dados));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnviarDadosOperacao()
|
var comando = new
|
||||||
{
|
{
|
||||||
if (Variaveis.OperacaoEmAndamento.DispMvd == null) return;
|
cmd = (int)HealthWorkerCommandType.SetDadosModulo,
|
||||||
|
@params = dados
|
||||||
var _Controle = Variaveis.OperacaoEmAndamento.Controle;
|
|
||||||
|
|
||||||
var dados = new
|
|
||||||
{
|
|
||||||
comando = (int)TipoComando.SetDadosOperacao,
|
|
||||||
dispositivo = (int)T_Code.Mod,
|
|
||||||
payload = new
|
|
||||||
{
|
|
||||||
modo = (int)Variaveis.OperacaoEmAndamento.Modo,
|
|
||||||
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
|
|
||||||
{
|
|
||||||
percent_vel_max = _Controle.PercentualVelocidadeMax,
|
|
||||||
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
|
|
||||||
{
|
|
||||||
lat = p.Posicao.Latitude,
|
|
||||||
lon = p.Posicao.Longitude,
|
|
||||||
tipo = (int)p.Tipo,
|
|
||||||
distanciaMargem = p.LarguraCorredor * 0.8
|
|
||||||
})
|
|
||||||
.ToList(),
|
|
||||||
horizonte = 2.5,
|
|
||||||
angulo_max_graus = _Controle.Angulo_Max,
|
|
||||||
distancia_entre_eixos = (VariaveisEquipamento.DistanciaEntreEixos / 100.0),
|
|
||||||
largura_equipamento = (VariaveisEquipamento.LarguraEquipamentoMm / 1000.0),
|
|
||||||
velocidade_min = FuncoesMatematicas.CalculaVelocidadeMsPercentual(_Controle.PercentualVelocidadeMin),
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Variaveis.MqttService.PublishAsync(topicoTx, JsonConvert.SerializeObject(dados));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
RedisService.Publish(CmdKey.HealthWorkerRx, JsonConvert.SerializeObject(comando));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
if (msg.obj.comando != null)
|
||||||
|
{
|
||||||
_Controle.Angulo = msg.obj.comando.angulo;
|
_Controle.Angulo = msg.obj.comando.angulo;
|
||||||
_Controle.TipoMovimento = msg.obj.comando.tipo_movimento;
|
_Controle.TipoMovimento = msg.obj.comando.tipo_movimento;
|
||||||
_Controle.PercentualVelocidadeSP = msg.obj.comando.percentual_velocidade;
|
_Controle.PercentualVelocidadeSP = msg.obj.comando.percentual_velocidade;
|
||||||
_Controle.SimulacaoMPC = msg.obj.comando.simulacao.Select(x => new MPCSimulacaoModel() { latitude = x[0], longitude = x[1] }).ToList();
|
_Controle.SimulacaoMPC = msg.obj.comando.simulacao.Select(x => new MPCSimulacaoModel() { latitude = x[0], longitude = x[1] }).ToList();
|
||||||
|
|
||||||
Variaveis.OperacaoEmAndamento.AtualizaInformacoesControleOperacao();
|
Variaveis.OperacaoEmAndamento.AtualizaInformacoesControleOperacao();
|
||||||
|
}
|
||||||
|
|
||||||
if (Variaveis.OperacaoEmAndamento.StatusAtual == Enums.StatusOperacao.Concluido && Variaveis.OperacaoEmAndamento.Iniciado)
|
if (Variaveis.OperacaoEmAndamento.StatusAtual == Enums.StatusOperacao.Concluido && Variaveis.OperacaoEmAndamento.Iniciado)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"}}}
|
||||||
|
|
@ -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}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
BIN
AgroBase/AgroBase/bin/x64/Debug/Microsoft.Extensions.Logging.Abstractions.dll (Stored with Git LFS)
Normal file
BIN
AgroBase/AgroBase/bin/x64/Debug/Pipelines.Sockets.Unofficial.dll (Stored with Git LFS)
Normal 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 {
|
||||||
|
|
|
||||||
|
|
@ -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,27 +20,44 @@ 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
|
||||||
|
try:
|
||||||
self.pipeline = self._criar_pipeline()
|
self.pipeline = self._criar_pipeline()
|
||||||
self.device = dai.Device(self.pipeline, self.dev_info)
|
self.device = dai.Device(self.pipeline, self.dev_info)
|
||||||
self.q_video = self.device.getOutputQueue(name="video", maxSize=1, blocking=False)
|
self.q_video = self.device.getOutputQueue(name="video", maxSize=1, blocking=False)
|
||||||
|
|
@ -60,14 +78,22 @@ class CameraOak:
|
||||||
"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 {
|
||||||
|
|
|
||||||
|
|
@ -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():
|
||||||
|
if multi_process:
|
||||||
iniciar_mqtt(module_id)
|
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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 = []
|
|
||||||
|
|
||||||
if len(dispositivos_mapeados) == 0:
|
|
||||||
self.mostrar_log("⚠️ Nenhum dispositivo retornado.")
|
|
||||||
dispositivos_serializados = []
|
dispositivos_serializados = []
|
||||||
else:
|
|
||||||
dispositivos_serializados = dispositivos_mapeados
|
|
||||||
|
|
||||||
novas_cameras = []
|
cameras_mapeadas = ContextoGlobalRedis.get(CtxKey.DadosCameras, {})
|
||||||
|
|
||||||
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, {}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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():
|
||||||
|
if multi_process:
|
||||||
iniciar_mqtt(module_id)
|
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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
motivos.append("GPS desconectado")
|
||||||
|
saude = 0
|
||||||
|
elif freq_hz <= FREQ_MIN:
|
||||||
|
motivos.append("Sem resposta do GPS")
|
||||||
|
saude = 0
|
||||||
|
else:
|
||||||
|
f = min(freq_hz, FREQ_BASE)
|
||||||
erro = (FREQ_BASE - f) / FREQ_BASE
|
erro = (FREQ_BASE - f) / FREQ_BASE
|
||||||
p = min(int(erro * 100), 40)
|
p = min(int(erro * 100), 40)
|
||||||
saude -= abs(p)
|
saude -= abs(p)
|
||||||
if abs(p) > 0:
|
if abs(p) > 0:
|
||||||
motivos.append(f"Frequência baixa: {f:.2f} Hz (penalidade {p}%)")
|
motivos.append(f"Frequência baixa: {f:.2f} Hz (penalidade {p}%)")
|
||||||
|
|
||||||
# 🔹 Fix
|
# 🔹 Fix
|
||||||
penalidades_fix = { 0: 90, 1: 70, 2: 40, 3: 30, 4: 5, 5: 0 }
|
penalidades_fix = { 0: 90, 1: 70, 2: 40, 3: 30, 4: 5, 5: 0 }
|
||||||
p = penalidades_fix.get(s.fix, 100)
|
p = penalidades_fix.get(fix, 100)
|
||||||
saude -= p
|
saude -= p
|
||||||
if s.fix < 4:
|
if fix < 4:
|
||||||
motivos.append(f"Fix inadequado: {s.fix} (penalidade {p}%)")
|
motivos.append(f"Fix inadequado: {fix} (penalidade {p}%)")
|
||||||
|
|
||||||
# 🔹 RTK
|
# 🔹 RTK
|
||||||
if not s.rtk:
|
if not rtk:
|
||||||
saude -= 20
|
saude -= 20
|
||||||
motivos.append("RTK inativo")
|
motivos.append("RTK inativo")
|
||||||
|
|
||||||
# 🔹 hAcc
|
# 🔹 hAcc
|
||||||
p = self._penalidade_hacc(s.hAcc)
|
p = self._penalidade_hacc(hAcc)
|
||||||
if p > 0:
|
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}%)")
|
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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
atual = item.get("atual")
|
nominal = dado.get("nominal")
|
||||||
nominal = item.get("nominal")
|
minimo = dado.get("minimo")
|
||||||
minimo = item.get("minimo")
|
maximo = dado.get("maximo")
|
||||||
maximo = item.get("maximo")
|
freq = dado.get("freq", 0.0)
|
||||||
freq = item.get("freq", 0.0)
|
padrao = padroes.get(label)
|
||||||
|
if not padrao:
|
||||||
status.dados[label] = atual # Valor atual por label
|
padrao = PadraoDinamico(
|
||||||
|
|
||||||
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}",
|
nome=f"{tipo_enum.name}_{sensor_id}_{label}",
|
||||||
faixa_inicial=(minimo or 0, maximo or 100),
|
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)
|
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
|
||||||
|
|
||||||
status.dados[label].padrao.adicionar_valor(atual)
|
servos = modulo.get("servos", [])
|
||||||
status.dados[label].padrao.frequencia = freq
|
_servos_save = dados_sen.get("servos", {})
|
||||||
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,48 @@
|
||||||
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")
|
||||||
|
ervas_no_radar = _contexto.get(T_Code.Gerais.name, {}).get("ervas_no_radar")
|
||||||
|
controle_mov = _operacao.get(T_Code.Mov.name, {})
|
||||||
vel_min = controle_mov.get("percent_vel_min", 0)
|
vel_min = controle_mov.get("percent_vel_min", 0)
|
||||||
vel_max = controle_mov.get("percent_vel_max", 100)
|
vel_max = controle_mov.get("percent_vel_max", 100)
|
||||||
|
erro = _trajetoria.get("erro_angular", 0)
|
||||||
|
|
||||||
velocidade_sp = vel_min
|
velocidade_sp = vel_min
|
||||||
|
|
||||||
if status_carro == StatusCarroMapa.Parado:
|
if status_carro == StatusCarroMapa.Parado.value:
|
||||||
velocidade_sp = 0
|
velocidade_sp = 0
|
||||||
|
|
||||||
elif status_carro == StatusCarroMapa.Direcionando:
|
elif status_carro == StatusCarroMapa.Direcionando.value:
|
||||||
manobrando = ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "manobrando")
|
manobrando = _trajetoria.get("manobrando", False)
|
||||||
desvio_necessario = ContextoGlobal.get_sonar("desvio_necessario") or False
|
|
||||||
|
|
||||||
if manobrando or desvio_necessario:
|
if manobrando:
|
||||||
velocidade_sp = vel_min
|
velocidade_sp = vel_min
|
||||||
else:
|
else:
|
||||||
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max)
|
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, erro)
|
||||||
|
|
||||||
elif status_carro in [StatusCarroMapa.EntrandoRua, StatusCarroMapa.SaindoRua, StatusCarroMapa.Manobrando]:
|
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:
|
elif status_carro == StatusCarroMapa.CaminhandoRua.value:
|
||||||
if ervas_no_radar > 0:
|
if ervas_no_radar > 0:
|
||||||
velocidade_sp = vel_min
|
velocidade_sp = vel_min
|
||||||
else:
|
else:
|
||||||
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max)
|
velocidade_sp = calcular_velocidade_relativa(vel_min, vel_max, erro)
|
||||||
|
|
||||||
# Aplica limites
|
# Aplica limites
|
||||||
velocidade_sp = max(vel_min, min(vel_max, velocidade_sp))
|
velocidade_sp = max(vel_min, min(vel_max, velocidade_sp))
|
||||||
|
|
@ -50,15 +54,16 @@ def definir_comando():
|
||||||
else:
|
else:
|
||||||
velocidade_sp = filtro_vel.filtrar(velocidade_sp)
|
velocidade_sp = filtro_vel.filtrar(velocidade_sp)
|
||||||
|
|
||||||
ContextoGlobal.set_controle_dado("velocidade_sp", velocidade_sp)
|
ContextoGlobalRedis.atualizar_ctx_dict(CtxKey.DadosControle, velocidade_sp=velocidade_sp)
|
||||||
|
|
||||||
#mostrar_log(f"🚗 Velocidade definida: {velocidade_sp:.1f}%")
|
#mostrar_log(f"🚗 Velocidade definida: {velocidade_sp:.1f}%")
|
||||||
|
|
||||||
return velocidade_sp
|
return velocidade_sp
|
||||||
|
except Exception as e:
|
||||||
|
mostrar_log("Erro ao definir o comando de movimentacao")
|
||||||
|
|
||||||
def calcular_velocidade_relativa(vel_min, vel_max):
|
def calcular_velocidade_relativa(vel_min, vel_max, erro_orientacao):
|
||||||
erro = ContextoGlobal.get_item(T_Code.Trajetoria, "dados", "erro_angular") or 0.0
|
severidade = min(abs(erro_orientacao) / 30.0, 1.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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,7 @@ from manager_worker.processadores.padroes import comando_parado
|
||||||
|
|
||||||
class ProcessadorAguardando(ProcessadorBase):
|
class ProcessadorAguardando(ProcessadorBase):
|
||||||
def processar(self):
|
def processar(self):
|
||||||
|
try:
|
||||||
return comando_parado()
|
return comando_parado()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro no processador Aguardando: {e}")
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,7 @@ from manager_worker.processadores.padroes import comando_parado
|
||||||
|
|
||||||
class ProcessadorConcluido(ProcessadorBase):
|
class ProcessadorConcluido(ProcessadorBase):
|
||||||
def processar(self):
|
def processar(self):
|
||||||
|
try:
|
||||||
return comando_parado()
|
return comando_parado()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro no processador Concluido: {e}")
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
if modo == ModoOperacao.MapaGPS.value:
|
||||||
angulo_sp, tipo_dir, simulacao = definir_comando_dir()
|
angulo_sp, tipo_dir, simulacao = definir_comando_dir()
|
||||||
velocidade_sp = definir_comando_mov()
|
velocidade_sp = definir_comando_mov()
|
||||||
return comando_controle(percentual_velocidade=velocidade_sp, angulo=angulo_sp, tipo_movimento=tipo_dir, simulacao=simulacao)
|
return comando_controle(percentual_velocidade=velocidade_sp, angulo=angulo_sp, tipo_movimento=tipo_dir, simulacao=simulacao)
|
||||||
return None
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro no processador EmAndamento: {e}")
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,7 @@ from manager_worker.processadores.padroes import comando_parado
|
||||||
|
|
||||||
class ProcessadorNaoIniciado(ProcessadorBase):
|
class ProcessadorNaoIniciado(ProcessadorBase):
|
||||||
def processar(self):
|
def processar(self):
|
||||||
|
try:
|
||||||
return comando_parado()
|
return comando_parado()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro no processador NaoIniciado: {e}")
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,7 @@ from manager_worker.processadores.padroes import comando_parado
|
||||||
|
|
||||||
class ProcessadorParado(ProcessadorBase):
|
class ProcessadorParado(ProcessadorBase):
|
||||||
def processar(self):
|
def processar(self):
|
||||||
|
try:
|
||||||
return comando_parado()
|
return comando_parado()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Erro no processador Parado: {e}")
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
||||||
|
|
|
||||||
|
|
@ -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,13 +296,24 @@ class ContextoGlobal:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_operacao(cls, dado=None):
|
def get_operacao(cls, dado=None):
|
||||||
if (dado == None):
|
try:
|
||||||
|
if (dado is None):
|
||||||
return cls._operacao
|
return cls._operacao
|
||||||
else:
|
else:
|
||||||
return cls._operacao.get(dado)
|
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):
|
||||||
|
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
|
cls._operacao[dado] = valor
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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():
|
||||||
|
|
|
||||||
|
|
@ -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():
|
||||||
|
if multi_process:
|
||||||
iniciar_mqtt(module_id)
|
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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
pip
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
[](https://github.com/redis/redis-py/actions?query=workflow%3ACI+branch%3Amaster)
|
||||||
|
[](https://redis-py.readthedocs.io/en/stable/)
|
||||||
|
[](./LICENSE)
|
||||||
|
[](https://pypi.org/project/redis/)
|
||||||
|
[](https://github.com/redis/redis-py/releases)
|
||||||
|
[](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.
|
||||||
|
|
||||||
|
[](https://redis.io)
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: hatchling 1.27.0
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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",
|
||||||
|
]
|
||||||
|
|
@ -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",
|
||||||
|
]
|
||||||
|
|
@ -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
|
||||||