iniciado comunicacao ip mqtt

This commit is contained in:
Diego Freitas 2025-11-06 13:18:32 -03:00
parent a6acd81d17
commit ba51fa73ca
29 changed files with 468 additions and 79 deletions

Binary file not shown.

View File

@ -47,7 +47,7 @@ namespace AgroBase.Forms
try
{
cameraSolo.camera.pythonProcess?.Kill();
Variaveis.MqttService.UnsubscribeAsync(cameraSolo.camera._mqttTopico).ConfigureAwait(false);
Variaveis.MqttServiceLocal.UnsubscribeAsync(cameraSolo.camera._mqttTopico).ConfigureAwait(false);
}
catch (Exception ex)
{
@ -183,7 +183,7 @@ namespace AgroBase.Forms
int i = 0;
//var coordenadas = cameraService.AferirDadosCamera<CameraCoordenadasModel>();
var MqttTopico = Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == cameraSolo.MqttTopico);
var MqttTopico = Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == cameraSolo.MqttTopico);
if (MqttTopico == null)
{

View File

@ -302,7 +302,7 @@ namespace AgroBase.Forms.IHM
}
}
bool mqttConectado = Variaveis.MqttService.StatusConexao();
bool mqttConectado = Variaveis.MqttServiceLocal.StatusConexao();
lblMqtt.Text = "MQTT: " + (mqttConectado ? "Conectado" : "Desconectado");
lblMqtt.ForeColor = mqttConectado ? Color.Green : Color.Red;

View File

@ -65,7 +65,7 @@ namespace AgroBase.Forms.Movimentacao
try
{
tmrLeitura.Stop();
Variaveis.MqttService.UnsubscribeAsync(cameraService._mqttTopico).GetAwaiter().GetResult();
Variaveis.MqttServiceLocal.UnsubscribeAsync(cameraService._mqttTopico).GetAwaiter().GetResult();
cameraService.pythonProcess.Kill();
}
catch (Exception ex)

View File

@ -34,7 +34,7 @@ namespace AgroBase.Forms.Sensoriamento
{
try
{
Variaveis.MqttService.UnsubscribeAsync(cameraService._mqttTopico).GetAwaiter().GetResult();
Variaveis.MqttServiceLocal.UnsubscribeAsync(cameraService._mqttTopico).GetAwaiter().GetResult();
cameraService.pythonProcess?.Kill();
}
catch (Exception ex)
@ -119,7 +119,7 @@ namespace AgroBase.Forms.Sensoriamento
this.pnlLeituras.Controls.Clear();
int i = 0;
var MqttTopico = Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == VideoUrl);
var MqttTopico = Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == VideoUrl);
CameraCoordenadasFrameModel leitura = null;
var Mensagem = MqttTopico.Mensagens.LastOrDefault(x => x.Momento > cameraService.IniciadoEm);
if (Mensagem != null)

View File

@ -57,8 +57,8 @@ namespace AgroBase.Forms
ReiniciarModelo();
await Variaveis.MqttService.AdicionarNovoTopico(mqtt_topico_state);
await Variaveis.MqttService.AdicionarNovoTopico(mqtt_topico_action, true, 2, async (message) =>
await Variaveis.MqttServiceLocal.AdicionarNovoTopico(mqtt_topico_state);
await Variaveis.MqttServiceLocal.AdicionarNovoTopico(mqtt_topico_action, true, 2, async (message) =>
{
Console.WriteLine("Mensagem recebida pelo modelo de IA: " + message);
//btnAcrescentarGPS_Click(new Button(), new EventArgs());
@ -72,10 +72,10 @@ namespace AgroBase.Forms
private async void frmTreinamentoIA_FormClosing(object sender, FormClosingEventArgs e)
{
var t0 = Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == mqtt_topico_state);
await Variaveis.MqttService.UnsubscribeAsync(t0);
var t1 = Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == mqtt_topico_action);
await Variaveis.MqttService.UnsubscribeAsync(t1);
var t0 = Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == mqtt_topico_state);
await Variaveis.MqttServiceLocal.UnsubscribeAsync(t0);
var t1 = Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == mqtt_topico_action);
await Variaveis.MqttServiceLocal.UnsubscribeAsync(t1);
Variaveis.OperacaoEmAndamento.Treinando = false;
}
@ -481,8 +481,8 @@ namespace AgroBase.Forms
_robotState.UpdateCurrentRobotState();
await Variaveis.MqttService.PublishAsync(
Variaveis.MqttService.Topicos.First(x => x.Topico == mqtt_topico_state),
await Variaveis.MqttServiceLocal.PublishAsync(
Variaveis.MqttServiceLocal.Topicos.First(x => x.Topico == mqtt_topico_state),
JsonConvert.SerializeObject(_robotState)
);
}

View File

@ -27,6 +27,8 @@ namespace AgroBase.Forms
ThreadPool.SetMinThreads(workerThreads: 50, completionPortThreads: 50);
Variaveis.IniciarMQTT();
APIService.IniciarRotinas();
GeneralJoystick.IniciarRotinas();
@ -93,7 +95,7 @@ namespace AgroBase.Forms
private async void IniciarConexaoMqtt()
{
await Variaveis.MqttService.ConnectAsync();
await Variaveis.MqttServiceLocal.ConnectAsync();
}
public static async Task EncerrarProcessos()

View File

@ -98,7 +98,7 @@ namespace AgroBase
{
AtualizaDadosGerais();
lblStripMqtt.Text = "MQTT: " + (Variaveis.MqttService.StatusConexao() ? "Conectado" : "Desconectado");
lblStripMqtt.Text = "MQTT: " + (Variaveis.MqttServiceLocal.StatusConexao() ? "Conectado" : "Desconectado");
lblStripPython.Text = "IA: " + (VariaveisOperacao.Operadores.Conectado ? "Conectado" : "Desconectado");
lblStripPython.ForeColor = VariaveisOperacao.Operadores.Conectado ? Color.Green : Color.Red;

View File

@ -60,13 +60,13 @@ namespace AgroBase.Models
try
{
Iniciada = false;
Variaveis.MqttService.Topicos.Remove(camera._mqttTopico);
Variaveis.MqttServiceLocal.Topicos.Remove(camera._mqttTopico);
if (panelRgb != null)
{
panelRgb.Controls.Remove(browserRgb);
}
browserRgb = null;
Task.Run(async () => await Variaveis.MqttService.UnsubscribeAsync(camera._mqttTopico));
Task.Run(async () => await Variaveis.MqttServiceLocal.UnsubscribeAsync(camera._mqttTopico));
lock (camera.processLock)
{

View File

@ -18,13 +18,13 @@ namespace AgroBase.Models
try
{
Iniciada = false;
Variaveis.MqttService.Topicos.Remove(camera._mqttTopico);
Variaveis.MqttServiceLocal.Topicos.Remove(camera._mqttTopico);
if (panel != null)
{
panel.Controls.Remove(browser);
}
browser = null;
Task.Run(async () => await Variaveis.MqttService.UnsubscribeAsync(camera._mqttTopico));
Task.Run(async () => await Variaveis.MqttServiceLocal.UnsubscribeAsync(camera._mqttTopico));
lock (camera.processLock)
{
if (camera.pythonProcess != null)

View File

@ -29,9 +29,9 @@ public class MPCController
public async Task<bool> Inicializar()
{
await Variaveis.MqttService.AdicionarNovoTopico(TopicoRota);
await Variaveis.MqttService.AdicionarNovoTopico(TopicoPosicao);
await Variaveis.MqttService.AdicionarNovoTopico(TopicoComando, true, 2, async (mensagem) =>
await Variaveis.MqttServiceLocal.AdicionarNovoTopico(TopicoRota);
await Variaveis.MqttServiceLocal.AdicionarNovoTopico(TopicoPosicao);
await Variaveis.MqttServiceLocal.AdicionarNovoTopico(TopicoComando, true, 2, async (mensagem) =>
{
if (mensagem.Mensagem == "OK")
{
@ -84,8 +84,8 @@ public class MPCController
velocidade_max = FuncoesMatematicas.CalculaVelocidadeMsPercentual(_Controle.PercentualVelocidadeMax)
};
await Variaveis.MqttService.PublishAsync(
Variaveis.MqttService.Topicos.First(x => x.Topico == TopicoRota),
await Variaveis.MqttServiceLocal.PublishAsync(
Variaveis.MqttServiceLocal.Topicos.First(x => x.Topico == TopicoRota),
JsonConvert.SerializeObject(trajetoria)
);
}
@ -123,8 +123,8 @@ public class MPCController
}
};
await Variaveis.MqttService.PublishAsync(
Variaveis.MqttService.Topicos.First(x => x.Topico == TopicoPosicao),
await Variaveis.MqttServiceLocal.PublishAsync(
Variaveis.MqttServiceLocal.Topicos.First(x => x.Topico == TopicoPosicao),
JsonConvert.SerializeObject(dadosEnvio)
);
@ -161,10 +161,10 @@ public class MPCController
Iniciado = false;
ComandoAtual = new MPCComandoModel();
Task.Run(async () => {
Variaveis.MqttService.Topicos.Remove(Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == TopicoRota));
Variaveis.MqttService.Topicos.Remove(Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == TopicoPosicao));
await Variaveis.MqttService.UnsubscribeAsync(Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == TopicoComando));
Variaveis.MqttService.Topicos.Remove(Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == TopicoComando));
Variaveis.MqttServiceLocal.Topicos.Remove(Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == TopicoRota));
Variaveis.MqttServiceLocal.Topicos.Remove(Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == TopicoPosicao));
await Variaveis.MqttServiceLocal.UnsubscribeAsync(Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == TopicoComando));
Variaveis.MqttServiceLocal.Topicos.Remove(Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == TopicoComando));
});
}

View File

@ -28,7 +28,7 @@ namespace AgroBase.Models
public void btnCarregar_Click(object sender, EventArgs e, string caminhoArquivo = "", bool monitoramento = false)
{
var Topico = Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == MapasVariaveisModel.TopicoSelecaoRuasMapa);
var Topico = Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == MapasVariaveisModel.TopicoSelecaoRuasMapa);
Topico.Mensagens.Clear();
TipoMapaOperacao? _tipoMapa = TipoMapa;
@ -234,7 +234,7 @@ namespace AgroBase.Models
{
List<string> ids = new List<string>();
bool idsMensagem = false;
var Topico = Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == MapasVariaveisModel.TopicoSelecaoRuasMapa);
var Topico = Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == MapasVariaveisModel.TopicoSelecaoRuasMapa);
var Mensagem = Topico.Mensagens.LastOrDefault();
if (Mensagem != null && !Mensagem.Mensagem.Contains("[]"))
{

View File

@ -16,6 +16,7 @@ using System.Text;
using static AgroBase.Services.OIDCanService.OidHandler;
using AgroBase.Models.Operadores;
using AgroBase.Services.Operadores;
using AgroMonitor;
namespace AgroBase.Models
{
@ -34,6 +35,10 @@ namespace AgroBase.Models
tmrLeituras?.Dispose();
tmrLeituras = new AsyncTaskTimerModel("tmrLeitura", tmrLeituras_Tick, 500);
tmrLeituras.Start();
tmrComunicacao?.Dispose();
tmrComunicacao = new AsyncTaskTimerModel("tmrComunicacao", tmrComunicacao_Tick, 1000);
tmrComunicacao.Start();
}
}
@ -141,7 +146,8 @@ namespace AgroBase.Models
}
}
private static AsyncTaskTimerModel tmrLeituras;
private static AsyncTaskTimerModel tmrComunicacao;
#region PARAMETRIZACAO
@ -924,7 +930,7 @@ namespace AgroBase.Models
Variaveis.OperacaoEmAndamento.Trajetoria = new TrajetoriaMapaOperacaoModel(new List<List<GPSModel>>());
Variaveis.OperacaoEmAndamento.GPSTrajetoria = new List<GPSModel>();
var Topico = Variaveis.MqttService.Topicos.FirstOrDefault(x => x.Topico == MapasVariaveisModel.TopicoSelecaoRuasMapa);
var Topico = Variaveis.MqttServiceLocal.Topicos.FirstOrDefault(x => x.Topico == MapasVariaveisModel.TopicoSelecaoRuasMapa);
Topico.Mensagens.Add(new MqttService.MqttTopicosMensagensModel()
{
Mensagem = "[]",
@ -1186,7 +1192,7 @@ namespace AgroBase.Models
}
Variaveis.OperacaoEmAndamento.Sensoriamento.AtualizarDados();
Variaveis.LoraService?.EnviarDadosParaBase();
//Variaveis.LoraService?.EnviarDadosParaBase();
if (!Variaveis.OperacaoEmAndamento.Iniciado)
{
@ -1240,6 +1246,27 @@ namespace AgroBase.Models
_Controle.TipoMovimento = oldTipoMovimento;
}
private async Task tmrComunicacao_Tick()
{
bool base_encontrada = (DateTime.Now - VariaveisOperacao.PosicaoBase.Momento).TotalSeconds < 10;
MqttService.MqttTopicosModel topico = null;
string msg = string.Empty;
if (!base_encontrada)
{
topico = Variaveis.MqttServiceBase.Topicos.FirstOrDefault(x => x.Topico == VariaveisMonitoramento.TopicoMqttDispositivos);
msg = VariaveisEquipamento.Parametros.serial_number;
}
else
{
topico = Variaveis.MqttServiceBase.Topicos.FirstOrDefault(x => x.Topico == VariaveisEquipamento.TopicoMqttTelemetria.Replace("<id>", VariaveisEquipamento.Parametros.serial_number));
msg = JsonConvert.SerializeObject(Variaveis.OperacaoEmAndamento.Sensoriamento);
}
if (topico != null && !string.IsNullOrEmpty(msg))
{
await Variaveis.MqttServiceBase.PublishAsync(topico, msg);
}
}
#endregion
#region REGISTRO DE LOGS

View File

@ -16,9 +16,10 @@ using System.Threading;
using AgroBase.Models.Components;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2.Core;
using System.Text.Json;
using AgroBase.Services.Operadores;
using System.Diagnostics;
using Newtonsoft.Json;
using AgroMonitor;
namespace AgroBase.Models
{
@ -55,7 +56,8 @@ namespace AgroBase.Models
public static string CaminhoModelos { get; } = "C:\\AgroBaseModels\\";
public static List<IDispositivosService> DispositivosConectados { get; set; } = new List<IDispositivosService>();
public static OperacaoModel OperacaoEmAndamento { get; set; } = new OperacaoModel(ModoOperacao.Manual);
public static MqttService MqttService { get; set; } = new MqttService("localhost", 1883);
public static MqttService MqttServiceLocal { get; set; }
public static MqttService MqttServiceBase { get; set; }
public static LoRaEspService LoraService
{
get
@ -86,6 +88,105 @@ namespace AgroBase.Models
public static byte ID_Num_sMOD { get; } = 0;
public static byte ID_Num_sTOD { get; } = 250;
public static byte ID_Num_sLRA { get; } = 251;
public static async void IniciarMQTT()
{
if (MqttServiceLocal != null)
{
foreach (var topico in MqttServiceLocal.Topicos.Where(x => x.Inscrever))
{
await MqttServiceLocal.UnsubscribeAsync(topico);
}
MqttServiceLocal.Topicos.Clear();
}
if (MqttServiceBase != null && !IsAgroMonitor)
{
foreach (var topico in MqttServiceBase.Topicos.Where(x => x.Inscrever))
{
await MqttServiceBase.UnsubscribeAsync(topico);
}
MqttServiceBase.Topicos.Clear();
}
MqttServiceLocal = new MqttService("localhost", 1883, IsAgroMonitor ? "base" : VariaveisEquipamento.Parametros.serial_number, true);
await MqttServiceLocal.AdicionarNovoTopico(MapasVariaveisModel.TopicoCoordenadasGPS);
await MqttServiceLocal.AdicionarNovoTopico(MapasVariaveisModel.TopicoTrajetoriaDinamica);
await MqttServiceLocal.AdicionarNovoTopico(MapasVariaveisModel.TopicoSelecaoRuasMapa, true, 1, async (message) =>
{
OperacaoEmAndamento.Mapa.AtualizarRuasSelecionadas();
});
if (IsAgroMonitor)
{
await MqttServiceLocal.AdicionarNovoTopico(VariaveisMonitoramento.TopicoMqttRTCM);
await MqttServiceLocal.AdicionarNovoTopico(VariaveisMonitoramento.TopicoMqttDispositivos, true, 1, async (message) =>
{
try
{
string device_id = message.Mensagem;
VariaveisMonitoramento.AdicionarNovoRoverNaRede(message.Mensagem);
}
catch (Exception ex)
{
Console.WriteLine($"Erro ao deserializar ping do rover: {ex.Message}");
}
});
}
else
{
MqttServiceBase = new MqttService(VariaveisEquipamento.Parametros.base_ip, 1883, VariaveisEquipamento.Parametros.serial_number, false);
await MqttServiceBase.AdicionarNovoTopico(VariaveisMonitoramento.TopicoMqttDispositivos);
await MqttServiceBase.AdicionarNovoTopico(VariaveisEquipamento.TopicoMqttTelemetria.Replace("<id>", VariaveisEquipamento.Parametros.serial_number));
await MqttServiceBase.AdicionarNovoTopico(VariaveisEquipamento.TopicoMqttComandos.Replace("<id>", VariaveisEquipamento.Parametros.serial_number), true, 1, async (message) =>
{
if (string.IsNullOrEmpty(message.Mensagem))
return;
try
{
string json = message.Mensagem;
var cmd = JsonConvert.DeserializeObject<OperacaoControleBaseModel>(json);
OperacaoEmAndamento.ExecutaComandoDaBase(cmd);
}
catch (Exception ex)
{
Console.WriteLine($"Erro ao deserializar comando da base: {ex.Message}");
}
});
await MqttServiceBase.AdicionarNovoTopico(VariaveisMonitoramento.TopicoMqttRTCM, true, 1, async (message) =>
{
if (message.Bytes == null || message.Bytes.Length == 0)
return;
try
{
var bytes = message.Bytes;
if (bytes != null && bytes.Length > 0)
{
GPSService.AplicarCorrecaoRTK_Mqtt(bytes, bytes.Length);
}
}
catch (Exception ex)
{
Console.WriteLine($"Erro ao deserializar RTCM da base: {ex.Message}");
}
});
await MqttServiceBase.AdicionarNovoTopico(VariaveisMonitoramento.TopicoMqttPosicao, true, 1, async (message) =>
{
if (string.IsNullOrEmpty(message.Mensagem))
return;
try
{
string json = message.Mensagem;
var posicao = JsonConvert.DeserializeObject<GPSModel>(json);
VariaveisOperacao.PosicaoBase = posicao;
}
catch (Exception ex)
{
Console.WriteLine($"Erro ao deserializar dados da base: {ex.Message}");
}
});
}
}
}
public static class VariaveisPortas
@ -98,6 +199,25 @@ namespace AgroBase.Models
public static class VariaveisEquipamento
{
private static ParametrosConfigEquipamentoModel _parametros;
public static ParametrosConfigEquipamentoModel Parametros
{
get
{
if (_parametros == null)
{
_parametros = new ParametrosConfigEquipamentoModel();
_parametros.AtualizarParametros();
}
return _parametros;
}
set
{
_parametros = value;
}
}
public static string TopicoMqttComandos { get; } = $"agrobot/v1/rover/<id>/cmd";
public static string TopicoMqttTelemetria { get; } = $"agrobot/v1/rover/<id>/telemetry";
public static double LarguraEsquerda { get; } = 44.0; // 62
public static double LarguraDireita { get; } = 44.0; // 22
public static double ComprimentoFrente { get; } = 7.0; // 7
@ -191,6 +311,8 @@ namespace AgroBase.Models
public static double AnguloInclinacaoPitchMax { get; set; } = 45.0;
public static string VozAlerta { get; set; } = "Microsoft Maria Desktop";
public static SensorSinaleiroComportamentoModel ComportamentoLedPorStatus(StatusOperacao? _status = null)
{
if (_status == null)
@ -444,6 +566,38 @@ namespace AgroBase.Models
return novaPosicao;
}
public class ParametrosConfigEquipamentoModel
{
public bool Carregado { get; set; }
public string serial_number { get; set; }
public string base_ip { get; set; }
public void AtualizarParametros()
{
string config_path = Path.Combine(Variaveis.CaminhoSistema, Variaveis.CaminhoParametros, "config.json");
if (!File.Exists(config_path))
Carregado = false;
try
{
string json = File.ReadAllText(config_path);
var par = JsonConvert.DeserializeObject<ParametrosConfigEquipamentoModel>(json);
serial_number = par.serial_number;
base_ip = par.base_ip;
Carregado = true;
}
catch (Exception ex)
{
Console.WriteLine($"Erro ao carregar arquivo de configuracoes: {ex.Message}");
Carregado = false;
}
}
}
}
public static class VariaveisOperacao
@ -1252,7 +1406,7 @@ namespace AgroBase.Models
string result = await browser.CoreWebView2.ExecuteScriptAsync(script);
// O resultado vem como string JSON (ex: "\"Texto dentro do body\"")
string conteudo = JsonSerializer.Deserialize<string>(result);
string conteudo = System.Text.Json.JsonSerializer.Deserialize<string>(result);
if (string.IsNullOrWhiteSpace(conteudo) ||
conteudo.Contains("Internal Server Error") ||

View File

@ -1,5 +1,6 @@
using AgroBase.Models;
using AgroBase.Services;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
@ -121,5 +122,78 @@ namespace AgroMonitor
public static double LeverArmFrontal { get; } = 0.0;
public static double LeverArmLateral { get; } = 0.0;
public static string TopicoMqttDispositivos { get; } = $"agrobot/v1/base/devices";
public static string TopicoMqttPosicao { get; } = $"agrobot/v1/base/position";
public static string TopicoMqttRTCM { get; } = $"agrobot/v1/base/rtcm";
private static readonly object _RoversLock = new object();
public static Dictionary<string, OperacaoSensoriamentoLogModel> RoversNaRede { get; set; } = new Dictionary<string, OperacaoSensoriamentoLogModel>();
public static async void AdicionarNovoRoverNaRede(string device_id)
{
if (string.IsNullOrEmpty(device_id))
return;
bool rover_ja_adicionado = false;
lock (_RoversLock)
{
rover_ja_adicionado = RoversNaRede.ContainsKey(device_id);
}
if (!rover_ja_adicionado)
{
lock (_RoversLock)
{
RoversNaRede.Add(device_id, new OperacaoSensoriamentoLogModel() { Momento = DateTime.Now });
}
await Variaveis.MqttServiceLocal.AdicionarNovoTopico(VariaveisEquipamento.TopicoMqttComandos.Replace("<id>", device_id));
await Variaveis.MqttServiceLocal.AdicionarNovoTopico(VariaveisEquipamento.TopicoMqttTelemetria.Replace("<id>", device_id), true, 1, async (message) =>
{
try
{
string json = message.Mensagem;
var obj = JsonConvert.DeserializeObject<OperacaoSensoriamentoLogModel>(json);
lock (_RoversLock)
{
RoversNaRede[device_id] = obj;
}
}
catch (Exception ex)
{
Console.WriteLine($"Erro ao deserializar dados de telemetria do rover {device_id}: {ex.Message}");
}
});
}
}
public static async void AtualizarListaRoversNaRede()
{
List<string> rovers_desconectados = new List<string>();
var rovers_atual = new Dictionary<string, OperacaoSensoriamentoLogModel>();
lock (_RoversLock)
{
rovers_atual = new Dictionary<string, OperacaoSensoriamentoLogModel>(RoversNaRede);
}
foreach (var rover in rovers_atual)
{
double tempo_sem_resposta = (DateTime.Now - rover.Value.Momento).TotalSeconds;
if (tempo_sem_resposta > 5.0)
{
rovers_desconectados.Add(rover.Key);
}
}
foreach (var device_id in rovers_desconectados)
{
lock (_RoversLock)
{
RoversNaRede.Remove(device_id);
}
var topicos_rover = Variaveis.MqttServiceLocal.Topicos.Where(x => x.Topico.Contains(device_id)).ToList();
foreach (var topico in topicos_rover)
{
await Variaveis.MqttServiceLocal.UnsubscribeAsync(topico);
Variaveis.MqttServiceLocal.Topicos.Remove(topico);
}
}
}
}
}

View File

@ -33,7 +33,7 @@ namespace AgroBase.Services
{
Task.Run(async () =>
{
_mqttTopico = await Variaveis.MqttService.AdicionarNovoTopico(MqttTopico, true, 2, mqttCallback);
_mqttTopico = await Variaveis.MqttServiceLocal.AdicionarNovoTopico(MqttTopico, true, 2, mqttCallback);
lock (processLock)
{

View File

@ -955,6 +955,14 @@ namespace AgroBase.Services
LoopRTK_Ntrip = false;
}
public static void AplicarCorrecaoRTK_Mqtt(byte[] correcao, int bytesRead)
{
if (PortaGPS?.IsOpen ?? false)
{
PortaGPS.Write(correcao, 0, bytesRead);
UltimoEnvioCorrecaoRTK = DateTime.Now;
}
}
public static void AtualizarCoordenadasGPS()
@ -1055,8 +1063,8 @@ namespace AgroBase.Services
Task.Run(async () =>
{
//Console.WriteLine(JsonConvert.SerializeObject(Coordenadas));
await Variaveis.MqttService.PublishAsync(
Variaveis.MqttService.Topicos.First(x => x.Topico == MapasVariaveisModel.TopicoCoordenadasGPS),
await Variaveis.MqttServiceLocal.PublishAsync(
Variaveis.MqttServiceLocal.Topicos.First(x => x.Topico == MapasVariaveisModel.TopicoCoordenadasGPS),
JsonConvert.SerializeObject(Coordenadas)
);
});
@ -1074,8 +1082,8 @@ namespace AgroBase.Services
Task.Run(async () =>
{
await Variaveis.MqttService.PublishAsync(
Variaveis.MqttService.Topicos.First(x => x.Topico == MapasVariaveisModel.TopicoTrajetoriaDinamica),
await Variaveis.MqttServiceLocal.PublishAsync(
Variaveis.MqttServiceLocal.Topicos.First(x => x.Topico == MapasVariaveisModel.TopicoTrajetoriaDinamica),
JsonConvert.SerializeObject(Trajetoria)
);
});
@ -1086,8 +1094,8 @@ namespace AgroBase.Services
{
Task.Run(async () =>
{
await Variaveis.MqttService.PublishAsync(
Variaveis.MqttService.Topicos.First(x => x.Topico == MapasVariaveisModel.TopicoSelecaoRuasMapa),
await Variaveis.MqttServiceLocal.PublishAsync(
Variaveis.MqttServiceLocal.Topicos.First(x => x.Topico == MapasVariaveisModel.TopicoSelecaoRuasMapa),
JsonConvert.SerializeObject("[" + string.Join(",", RuasSelecionadas.ToArray()) + "]"),
true
);

View File

@ -13,23 +13,36 @@ public class MqttService
{
private IMqttClient _client;
private IMqttClientOptions _options;
private string _brokerAddr;
private int _brokerPort;
private AsyncTaskTimerModel tmrCheck;
public List<MqttTopicosModel> Topicos;
public MqttService(string brokerAddress, int brokerPort)
private void LogDebug(string msg)
{
Console.WriteLine($"[MQTT {_brokerAddr}:{_brokerPort}] - {msg}");
}
public MqttService(string brokerAddress, int brokerPort, string client_id, bool local)
{
_brokerAddr = brokerAddress;
_brokerPort = brokerPort;
var factory = new MqttFactory();
_client = factory.CreateMqttClient();
string client = !local ? client_id : (client_id + "_local");
_options = new MqttClientOptionsBuilder()
.WithTcpServer(brokerAddress, brokerPort)
.WithClientId(client)
.WithTcpServer(_brokerAddr, _brokerPort)
.WithCleanSession()
.Build();
_client.UseConnectedHandler(async e =>
{
Console.WriteLine("Connected to MQTT Broker.");
LogDebug("Connected to MQTT Broker.");
if (Topicos.Any(x => x.Inscrever))
{
foreach (var topic in Topicos)
@ -37,17 +50,21 @@ public class MqttService
await SubscribeAsync(topic);
}
}
});
_client.UseDisconnectedHandler(e =>
{
Console.WriteLine("Disconnected from MQTT Broker.");
LogDebug("Disconnected from MQTT Broker.");
});
_client.UseApplicationMessageReceivedHandler(e =>
{
string message = Encoding.UTF8.GetString((e.ApplicationMessage.Payload == null) ? new byte[] { } : e.ApplicationMessage.Payload);
var payloadBytes = e.ApplicationMessage.Payload ?? Array.Empty<byte>();
var messageText = string.Empty;
// Só tenta decodificar texto se for realmente necessário
try { messageText = Encoding.UTF8.GetString(payloadBytes); } catch { /* binário puro */ }
//Console.WriteLine($"Message received on topic {e.ApplicationMessage.Topic}");
// Aqui você pode adicionar o código para lidar com a mensagem recebida
@ -59,7 +76,8 @@ public class MqttService
{
Momento = DateTime.Now,
Cliente = e.ClientId,
Mensagem = message
Mensagem = messageText,
Bytes = payloadBytes
};
topico.Mensagens.Add(Mensagem);
@ -79,7 +97,7 @@ public class MqttService
}
catch (Exception ex)
{
Console.WriteLine($"Erro no callback do tópico '{topico.Topico}': {ex}");
LogDebug($"Erro no callback do tópico '{topico.Topico}': {ex}");
}
});
}
@ -88,13 +106,7 @@ public class MqttService
Topicos = new List<MqttTopicosModel>();
Task.Run(async () =>
{
await AdicionarNovoTopico(MapasVariaveisModel.TopicoCoordenadasGPS);
await AdicionarNovoTopico(MapasVariaveisModel.TopicoSelecaoRuasMapa, true, 1, async (message) => { Variaveis.OperacaoEmAndamento.Mapa.AtualizarRuasSelecionadas(); });
await AdicionarNovoTopico(MapasVariaveisModel.TopicoTrajetoriaDinamica);
});
tmrCheck?.Dispose();
tmrCheck = new AsyncTaskTimerModel("tmrCheck", tmrCheck_Tick, 1000);
tmrCheck.Start();
}
@ -120,7 +132,7 @@ public class MqttService
}
catch (Exception ex)
{
Console.WriteLine("Erro ao tentar se conectar ao MQTT Broker: " + ex.Message);
LogDebug("Erro ao tentar se conectar ao MQTT Broker: " + ex.Message);
}
}
@ -150,7 +162,7 @@ public class MqttService
public async Task PublishAsync(MqttTopicosModel topic, byte[] payloadBytes, bool Forcar = false)
{
if (topic.Inscrever && !Forcar)
if (topic.Inscrever || (topic.Inscrever && !Forcar))
{
return;
}
@ -175,11 +187,11 @@ public class MqttService
await ConnectAsync();
}
await _client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic.Topico).Build());
Console.WriteLine("Subscribed to topic: " + topic.Topico);
LogDebug($"Subscribed to topic: {topic.Topico}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while subscribing from the topic: {ex.Message}");
LogDebug($"An error occurred while subscribing from the topic: {ex.Message}");
}
}
@ -196,11 +208,11 @@ public class MqttService
await ConnectAsync();
}
await _client.UnsubscribeAsync(new string[] { topic.Topico });
Console.WriteLine("Unsubscribed from topic: " + topic.Topico);
LogDebug($"Unsubscribed from topic: {topic.Topico}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while unsubscribing from the topic: {ex.Message}");
LogDebug($"An error occurred while unsubscribing from the topic: {ex.Message}");
}
}
@ -247,7 +259,7 @@ public class MqttService
.Build();
await _client.PublishAsync(mensagem);
Console.WriteLine($"Mensagem retida limpa para o tópico: {topico}");
LogDebug($"Mensagem retida limpa para o tópico: {topico}");
}
}
@ -265,5 +277,6 @@ public class MqttService
public DateTime Momento { get; set; }
public string Cliente { get; set; }
public string Mensagem { get; set; }
public byte[] Bytes { get; set; }
}
}

View File

@ -1,5 +1,4 @@
using AgroBase.Forms;
using AgroBase.Models;
using AgroBase.Models;
using System;
using System.Diagnostics;
using System.IO;
@ -67,12 +66,7 @@ namespace AgroBase.Services
if (!string.IsNullOrEmpty(ip) && ip != Variaveis.IP_Host)
{
bool configurado = await EthernetService.ConfigurarIP(true, false);
foreach (var topico in Variaveis.MqttService.Topicos.Where(x => x.Inscrever))
{
await Variaveis.MqttService.UnsubscribeAsync(topico);
}
Variaveis.MqttService.Topicos.Clear();
Variaveis.MqttService = new MqttService("localhost", 1883);
Variaveis.IniciarMQTT();
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -17,6 +17,8 @@ namespace AgroMonitor
{
InitializeComponent();
Variaveis.IniciarMQTT();
RedisService.Iniciar();
RedisService.LimparDadosIniciais();
APIService.IniciarRotinas();
@ -58,8 +60,8 @@ namespace AgroMonitor
private async Task tmrDispositivos_Tick()
{
lblMqtt.Text = "MQTT: " + (Variaveis.MqttService.StatusConexao() ? "Conectado" : "Desconectado");
lblMqtt.ForeColor = Variaveis.MqttService.StatusConexao() ? Color.Green : Color.Red;
lblMqtt.Text = "MQTT: " + (Variaveis.MqttServiceLocal.StatusConexao() ? "Conectado" : "Desconectado");
lblMqtt.ForeColor = Variaveis.MqttServiceLocal.StatusConexao() ? Color.Green : Color.Red;
lblLoRa.Text = "LoRa: " + (LoRaBaseService.Iniciado ? "Conectado" : "Desconectado");
lblLoRa.ForeColor = LoRaBaseService.Iniciado ? Color.Green : Color.Red;
@ -76,6 +78,8 @@ namespace AgroMonitor
{
tmrDispositivos.SetInterval(10000);
}
VariaveisMonitoramento.AtualizarListaRoversNaRede();
}

View File

@ -0,0 +1,47 @@
# base.py
import time, json, struct
import paho.mqtt.client as mqtt
BROKER_IP = "0.0.0.0" # se a base RODA o broker, tanto faz; para conectar, use "127.0.0.1"
RID = "rover01"
TOP_TEL = f"agrobot/{RID}/telemetry"
TOP_CMD = f"agrobot/{RID}/cmd"
last_t = None
def on_connect(c, u, f, rc):
print("Base conectada, rc=", rc)
c.subscribe(TOP_TEL, qos=0)
def on_message(c, u, msg):
global last_t
if msg.topic == TOP_TEL:
tel = json.loads(msg.payload.decode("utf-8"))
now = time.time()
if "t" in tel:
rtt_ms = (now - tel["t"]) * 1000
else:
rtt_ms = None
print(f"[TEL] vel={tel.get('vel')}, heading={tel.get('heading')}, rtt≈{rtt_ms:.1f} ms")
client = mqtt.Client(client_id="base01", clean_session=True)
client.on_connect = on_connect
client.on_message = on_message
client.connect("127.0.0.1", 1883, keepalive=60) # se a base é o broker; senão use o IP do broker
client.loop_start()
# Envia um comando binário exemplo (QoS 1)
time.sleep(1.0)
vel, ang = 0.5, 10.0
pkt = struct.pack("<ff", vel, ang)
client.publish(TOP_CMD, pkt, qos=1, retain=False)
print("[BASE] Comando enviado")
try:
while True:
time.sleep(0.5)
except KeyboardInterrupt:
pass
finally:
client.loop_stop()
client.disconnect()

View File

@ -0,0 +1,43 @@
# rover.py
import time, json, struct
import paho.mqtt.client as mqtt
BROKER_IP = "192.168.99.120" # IP da Base
RID = "rover01"
TOP_TEL = f"agrobot/{RID}/telemetry"
TOP_CMD = f"agrobot/{RID}/cmd"
TOP_HEALTH = f"agrobot/{RID}/health"
TOP_LWT = f"agrobot/{RID}/lwt"
def on_connect(c, u, f, rc):
print("Rover conectado, rc=", rc)
c.subscribe(TOP_CMD, qos=1)
# Publica estado online (retido)
c.publish(TOP_HEALTH, json.dumps({"status":"online","t":time.time()}), qos=1, retain=True)
def on_message(c, u, msg):
if msg.topic == TOP_CMD:
# Exemplo de payload binário: vel (float32), ang (float32)
vel, ang = struct.unpack("<ff", msg.payload)
print(f"[CMD] vel={vel:.2f} m/s, ang={ang:.2f}°")
client = mqtt.Client(client_id=f"{RID}", clean_session=True)
client.will_set(TOP_LWT, payload="offline", qos=1, retain=True) # LWT
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER_IP, 1883, keepalive=60)
client.loop_start()
try:
i = 0
while True:
# Telemetria json (poderia ser binário também)
tel = {"t": time.time(), "heading": 123.45+i*0.01, "vel": 1.6}
client.publish(TOP_TEL, json.dumps(tel), qos=0, retain=False)
i += 1
time.sleep(0.2)
except KeyboardInterrupt:
pass
finally:
client.loop_stop()
client.disconnect()

View File

@ -0,0 +1,13 @@
# base_tx.py
import socket, time
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ROBO_IP = "192.168.99.108"
i = 0
while True:
msg = f"PING {i}".encode()
sock.sendto(msg, (ROBO_IP, 5000))
i += 1
time.sleep(0.2)

View File

@ -0,0 +1,10 @@
# robo_rx.py
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("0.0.0.0", 5000))
print("ROBO ouvindo na porta 5000...")
while True:
data, addr = sock.recvfrom(1024)
print(f"[ROBÔ] Recebido de {addr}: {data}")