Retorno do CAN Serial
This commit is contained in:
parent
778af21ebf
commit
1e2622cfb3
|
|
@ -589,6 +589,7 @@
|
|||
<Compile Include="Services\PythonService.cs" />
|
||||
<Compile Include="Services\PZEMService.cs" />
|
||||
<Compile Include="Services\RedisService.cs" />
|
||||
<Compile Include="Services\SerialCanService.cs" />
|
||||
<Compile Include="Services\SerialService.cs" />
|
||||
<Compile Include="Services\SonarService.cs" />
|
||||
<Compile Include="Services\SyncDataService.cs" />
|
||||
|
|
|
|||
|
|
@ -2375,14 +2375,14 @@ namespace AgroBase.Forms
|
|||
|
||||
private bool MKSIniciado()
|
||||
{
|
||||
CanService.InicializarCanal();
|
||||
CanManager.CanService.Inicializar();
|
||||
byte addr = FuncoesGlobais.StringToHexByte(cmbEndereco.Text);
|
||||
if (CanService.IsConnected && MKS057DCanService.DadosLeitura.Any(x => x.EnderecoTx == addr && !x.Iniciado))
|
||||
if (CanManager.CanService.IsConnected && MKS057DCanService.DadosLeitura.Any(x => x.EnderecoTx == addr && !x.Iniciado))
|
||||
{
|
||||
MKS057DCanService.RequisitarDado(addr, addr, MKS057DCanService.MksHandler.MksFuncCode.Versao);
|
||||
}
|
||||
|
||||
return CanService.IsConnected;
|
||||
return CanManager.CanService.IsConnected;
|
||||
}
|
||||
|
||||
private void btnMovimentoCAN_Click(object sender, EventArgs e)
|
||||
|
|
@ -2413,7 +2413,7 @@ namespace AgroBase.Forms
|
|||
private async void btnRequisitarDados_Click(object sender, EventArgs e)
|
||||
{
|
||||
byte addr = FuncoesGlobais.StringToHexByte(cmbEndereco.Text);
|
||||
CanService.RegistrarHandler(addr, MKS057DCanService._MksHandler);
|
||||
CanManager.CanService.RegistrarHandler(addr, MKS057DCanService._MksHandler);
|
||||
await tmrReq_Tick();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -501,11 +501,11 @@ namespace AgroBase.Models.Modules
|
|||
|
||||
public async Task<bool> VerificaDispositivoConectado()
|
||||
{
|
||||
if (!CanService.IsConnected) return false;
|
||||
if (!CanManager.CanService.IsConnected) return false;
|
||||
|
||||
if (Variaveis.OperacaoEmAndamento.DispAtu == null) return false;
|
||||
|
||||
CanService.RegistrarHandler(_EnderecoCAN_Rx, _AtuHandler);
|
||||
CanManager.CanService.RegistrarHandler(_EnderecoCAN_Rx, _AtuHandler);
|
||||
|
||||
RequisitarDadosModulo(Variaveis.ID_Num_sMOD, CanMessagePosicaoDados.Status, CanMessagePosicaoDados.Status, true);
|
||||
|
||||
|
|
@ -529,7 +529,7 @@ namespace AgroBase.Models.Modules
|
|||
SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel()
|
||||
{
|
||||
Dispositivo = DadosLeitura.Dispositivo,
|
||||
Endereco = CanService._portName,
|
||||
Endereco = CanManager.CanService._portName,
|
||||
Versao = DadosLeitura.Versao.ToString(),
|
||||
Mod_ID = Modulo_ID
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1748,11 +1748,11 @@ namespace AgroBase.Models.Modules
|
|||
|
||||
public async Task<bool> VerificaDispositivoConectado()
|
||||
{
|
||||
if (!CanService.IsConnected) return false;
|
||||
if (!CanManager.CanService.IsConnected) return false;
|
||||
|
||||
if (Variaveis.OperacaoEmAndamento.DispSen == null) return false;
|
||||
|
||||
CanService.RegistrarHandler(_EnderecoCAN_Rx, _SenHandler);
|
||||
CanManager.CanService.RegistrarHandler(_EnderecoCAN_Rx, _SenHandler);
|
||||
|
||||
RequisitarDadosModulo(Variaveis.ID_Num_sMOD, CanMessagePosicaoDados.Status, CanMessagePosicaoDados.Status, true);
|
||||
|
||||
|
|
@ -1775,7 +1775,7 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel()
|
||||
{
|
||||
Endereco = CanService._portName,
|
||||
Endereco = CanManager.CanService._portName,
|
||||
Dispositivo = DadosLeitura.Dispositivo,
|
||||
Versao = DadosLeitura.Versao.ToString(),
|
||||
Mod_ID = Modulo_ID,
|
||||
|
|
@ -2801,7 +2801,7 @@ namespace AgroBase.Models.Modules
|
|||
|
||||
var Dados = Variaveis.OperacaoEmAndamento.DispSen.Dados;
|
||||
|
||||
Console.WriteLine($"[SEN] Componente={componente.ToString()}, ID_Num={ID_Num}, Posicao={posicao.ToString()}, Data={FuncoesGlobais.ConverterComandoBytesParaTexto(data)}");
|
||||
//Console.WriteLine($"[SEN] Componente={componente.ToString()}, ID_Num={ID_Num}, Posicao={posicao.ToString()}, Data={FuncoesGlobais.ConverterComandoBytesParaTexto(data)}");
|
||||
|
||||
switch (componente)
|
||||
{
|
||||
|
|
@ -2815,7 +2815,7 @@ namespace AgroBase.Models.Modules
|
|||
bool conectado = data[3] == 1;
|
||||
int versao = data[4];
|
||||
|
||||
Console.WriteLine($"[SEN] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}");
|
||||
//Console.WriteLine($"[SEN] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}");
|
||||
|
||||
Dados.DadosLeitura.Conectado = conectado;
|
||||
Dados.DadosLeitura.Dispositivo = tipoModulo;
|
||||
|
|
@ -2951,7 +2951,7 @@ namespace AgroBase.Models.Modules
|
|||
{
|
||||
double latenciaLoop = ConverterByteParaDouble(data, 2) * 100;
|
||||
Dados.DadosLeitura.LatenciaLoop = Convert.ToInt32(latenciaLoop);
|
||||
Console.WriteLine($"[SEN] Ciclo concluido. Latencia de loop: {latenciaLoop} ms");
|
||||
//Console.WriteLine($"[SEN] Ciclo concluido. Latencia de loop: {latenciaLoop} ms");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -10,6 +11,30 @@ using static WaveshareCANFDService;
|
|||
|
||||
namespace AgroBase.Services
|
||||
{
|
||||
public class CanManager
|
||||
{
|
||||
public static bool UseCanSerial = true;
|
||||
|
||||
private static CanServiceWaveshare CanWaveshare = new CanServiceWaveshare();
|
||||
private static CanServiceSerial CanSerial = new CanServiceSerial();
|
||||
|
||||
public static ICanService CanService
|
||||
{
|
||||
get
|
||||
{
|
||||
if (UseCanSerial)
|
||||
{
|
||||
return CanSerial;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CanWaveshare;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class CanMessage
|
||||
{
|
||||
public DateTime Momento { get; set; }
|
||||
|
|
@ -122,30 +147,41 @@ namespace AgroBase.Services
|
|||
void ProcessarMensagem(CanMessage mensagem);
|
||||
}
|
||||
|
||||
public class CanService : IDisposable
|
||||
public interface ICanService : IDisposable
|
||||
{
|
||||
public static WaveshareCANFDService Connector = new WaveshareCANFDService(0);
|
||||
public static string _portName = "CAN-FD";
|
||||
public static bool IsConnected { get; private set; }
|
||||
public static bool Iniciado
|
||||
void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler);
|
||||
bool Inicializar(SerialPort Porta = null);
|
||||
void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true);
|
||||
void Fechar();
|
||||
string _portName { get; set; }
|
||||
bool Iniciado { get; }
|
||||
bool IsConnected { get; }
|
||||
}
|
||||
|
||||
public class CanServiceWaveshare : ICanService
|
||||
{
|
||||
public WaveshareCANFDService Connector = new WaveshareCANFDService(0);
|
||||
public string _portName { get; set; } = "CAN-FD";
|
||||
public bool IsConnected { get; private set; }
|
||||
public bool Iniciado
|
||||
{
|
||||
get
|
||||
{
|
||||
return SerialService.DispositivosCan.Any(d => SerialService.DispositivosMapeados.Any(x => x.Status == StatusModulo.Operante && x.Dispositivo == d));
|
||||
}
|
||||
}
|
||||
private static AsyncTaskTimerModel tmrEnviarCAN;
|
||||
private static AsyncTaskTimerModel tmrOuvirCAN;
|
||||
private static AsyncTaskTimerModel tmrProcessarCAN;
|
||||
private static StringBuilder SerialCANBuffer = new StringBuilder();
|
||||
private static ConcurrentQueue<(byte, byte[])> FramesRecebidos = new ConcurrentQueue<(byte, byte[])>();
|
||||
private static readonly ConcurrentDictionary<byte, ICanMessageHandler> _roteadores = new ConcurrentDictionary<byte, ICanMessageHandler>();
|
||||
private static byte? _ultimoIdEnviado = null;
|
||||
private static List<CanMessage> MensagensPendentes = new List<CanMessage>();
|
||||
private static readonly object _LockMensagens = new object();
|
||||
private static bool DebugMode = false;
|
||||
private AsyncTaskTimerModel tmrEnviarCAN;
|
||||
private AsyncTaskTimerModel tmrOuvirCAN;
|
||||
private AsyncTaskTimerModel tmrProcessarCAN;
|
||||
private StringBuilder SerialCANBuffer = new StringBuilder();
|
||||
private ConcurrentQueue<(byte, byte[])> FramesRecebidos = new ConcurrentQueue<(byte, byte[])>();
|
||||
private readonly ConcurrentDictionary<byte, ICanMessageHandler> _roteadores = new ConcurrentDictionary<byte, ICanMessageHandler>();
|
||||
private byte? _ultimoIdEnviado = null;
|
||||
private List<CanMessage> MensagensPendentes = new List<CanMessage>();
|
||||
private readonly object _LockMensagens = new object();
|
||||
private bool DebugMode = false;
|
||||
|
||||
private static void MostrarLog(string message, byte id)
|
||||
private void MostrarLog(string message, byte id)
|
||||
{
|
||||
if (DebugMode && new List<byte>() { 0x01 }.Contains(id))
|
||||
{
|
||||
|
|
@ -153,13 +189,13 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
public static void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler)
|
||||
public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler)
|
||||
{
|
||||
_roteadores[endereco_rx] = handler;
|
||||
MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx);
|
||||
}
|
||||
|
||||
public static bool InicializarCanal()
|
||||
public bool Inicializar(SerialPort Porta = null)
|
||||
{
|
||||
bool canIniciado = Connector.IsOpen;
|
||||
|
||||
|
|
@ -202,7 +238,7 @@ namespace AgroBase.Services
|
|||
return canIniciado;
|
||||
}
|
||||
|
||||
private static async Task ProcessarCAN()
|
||||
private async Task ProcessarCAN()
|
||||
{
|
||||
int mensagensParaProcessar = Math.Min(FramesRecebidos.Count, 10);
|
||||
|
||||
|
|
@ -221,7 +257,7 @@ namespace AgroBase.Services
|
|||
LimparMensagensAntigas();
|
||||
}
|
||||
|
||||
private static void ProcessarFrameCAN((byte, byte[]) frame)
|
||||
private void ProcessarFrameCAN((byte, byte[]) frame)
|
||||
{
|
||||
byte id = frame.Item1;
|
||||
byte[] data = frame.Item2;
|
||||
|
|
@ -268,7 +304,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
public static void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true)
|
||||
public void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true)
|
||||
{
|
||||
if (!Connector.IsOpen) return;
|
||||
|
||||
|
|
@ -313,7 +349,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
private static CanMessage ObterProximaMensagem()
|
||||
private CanMessage ObterProximaMensagem()
|
||||
{
|
||||
List<CanMessage> mensagensPendentes;
|
||||
|
||||
|
|
@ -365,7 +401,7 @@ namespace AgroBase.Services
|
|||
return proximaMsg;
|
||||
}
|
||||
|
||||
private static async Task EnviarCAN()
|
||||
private async Task EnviarCAN()
|
||||
{
|
||||
if (!Connector.IsOpen) return;
|
||||
|
||||
|
|
@ -396,7 +432,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
private static string CriarFrame(CanMessage msg)
|
||||
private string CriarFrame(CanMessage msg)
|
||||
{
|
||||
string frame = $"t{msg.IdTx:X3}{msg.DataTx.Length}";
|
||||
foreach (byte b in msg.DataTx)
|
||||
|
|
@ -407,7 +443,7 @@ namespace AgroBase.Services
|
|||
return frame;
|
||||
}
|
||||
|
||||
private static bool EnviarComando(string comando, CanMessage msg = null)
|
||||
private bool EnviarComando(string comando, CanMessage msg = null)
|
||||
{
|
||||
if (Connector.IsOpen)
|
||||
{
|
||||
|
|
@ -427,7 +463,7 @@ namespace AgroBase.Services
|
|||
return false;
|
||||
}
|
||||
|
||||
private static void LimparMensagensAntigas()
|
||||
private void LimparMensagensAntigas()
|
||||
{
|
||||
var agora = DateTime.Now;
|
||||
|
||||
|
|
@ -448,7 +484,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
public static void Fechar()
|
||||
public void Fechar()
|
||||
{
|
||||
tmrOuvirCAN?.Dispose();
|
||||
tmrEnviarCAN?.Dispose();
|
||||
|
|
@ -473,7 +509,7 @@ namespace AgroBase.Services
|
|||
Fechar();
|
||||
}
|
||||
|
||||
private static byte CalcularChecksum(byte id, byte[] data)
|
||||
private byte CalcularChecksum(byte id, byte[] data)
|
||||
{
|
||||
int soma = id;
|
||||
foreach (var b in data)
|
||||
|
|
@ -481,7 +517,7 @@ namespace AgroBase.Services
|
|||
return (byte)(soma & 0xFF);
|
||||
}
|
||||
|
||||
private static string GerarChaveMensagem(T_Code Dispositivo, byte Id, byte[] data)
|
||||
private string GerarChaveMensagem(T_Code Dispositivo, byte Id, byte[] data)
|
||||
{
|
||||
byte parametro = 0x00;
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ namespace AgroBase.Comum
|
|||
{
|
||||
payload.AddRange(dados);
|
||||
}
|
||||
CanService.AdicionarMensagemNaFila(Dispositivo, Dados._EnderecoCAN_Tx, Dados._EnderecoCAN_Rx, (byte)PosicaoTx, (byte)PosicaoRx, payload.ToArray(), get);
|
||||
CanManager.CanService.AdicionarMensagemNaFila(Dispositivo, Dados._EnderecoCAN_Tx, Dados._EnderecoCAN_Rx, (byte)PosicaoTx, (byte)PosicaoRx, payload.ToArray(), get);
|
||||
}
|
||||
|
||||
public Control InstanciarDispositivo(int index)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
get
|
||||
{
|
||||
return CanService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Dir) && DadosLeitura.Any(x => x.Iniciado);
|
||||
return CanManager.CanService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Dir) && DadosLeitura.Any(x => x.Iniciado);
|
||||
}
|
||||
}
|
||||
public static bool Referenciando
|
||||
|
|
@ -40,7 +40,7 @@ namespace AgroBase.Services
|
|||
|
||||
public static async Task<bool> VerificaDispositivoConectado()
|
||||
{
|
||||
if (!CanService.IsConnected) return false;
|
||||
if (!CanManager.CanService.IsConnected) return false;
|
||||
|
||||
if (Variaveis.OperacaoEmAndamento.DispMvd == null) return false;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ namespace AgroBase.Services
|
|||
|
||||
foreach (var Modulo in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Where(x => !x.DirMotor.Inicializado))
|
||||
{
|
||||
CanService.RegistrarHandler(Modulo.DirMotor._EnderecoCAN_Rx, _MksHandler);
|
||||
CanManager.CanService.RegistrarHandler(Modulo.DirMotor._EnderecoCAN_Rx, _MksHandler);
|
||||
|
||||
RequisitarDado(Modulo.DirMotor._EnderecoCAN_Tx, Modulo.DirMotor._EnderecoCAN_Rx, MksFuncCode.Versao);
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ namespace AgroBase.Services
|
|||
DefinirDispositivo(Mod_IDs);
|
||||
}
|
||||
|
||||
return Iniciado;
|
||||
return Mod_IDs.Any();
|
||||
}
|
||||
|
||||
private static void DefinirDispositivo(List<string> Mod_IDs)
|
||||
|
|
@ -84,7 +84,7 @@ namespace AgroBase.Services
|
|||
SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel()
|
||||
{
|
||||
Dispositivo = T_Code.Dir,
|
||||
Endereco = CanService._portName,
|
||||
Endereco = CanManager.CanService._portName,
|
||||
Versao = (Leitura.FV ?? "").ToString(),
|
||||
Mod_ID = Mod_ID,
|
||||
});
|
||||
|
|
@ -120,7 +120,7 @@ namespace AgroBase.Services
|
|||
|
||||
private static void EnviarComando(T_Code Dispositivo, byte idTx, byte idRx, byte funcCode, byte[] payload = null, bool get = true)
|
||||
{
|
||||
CanService.AdicionarMensagemNaFila(Dispositivo, idTx, idRx, funcCode, funcCode, payload, get);
|
||||
CanManager.CanService.AdicionarMensagemNaFila(Dispositivo, idTx, idRx, funcCode, funcCode, payload, get);
|
||||
}
|
||||
|
||||
public static void SetModoOperacao(byte idTx, byte idRx, byte modo)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace AgroBase.Services
|
|||
{
|
||||
get
|
||||
{
|
||||
return CanService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Mov) && DadosLeitura.Any(x => x.Iniciado);
|
||||
return CanManager.CanService.IsConnected && SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Mov) && DadosLeitura.Any(x => x.Iniciado);
|
||||
}
|
||||
}
|
||||
public static int TaxaAmostragem
|
||||
|
|
@ -33,7 +33,7 @@ namespace AgroBase.Services
|
|||
|
||||
public static async Task<bool> VerificaDispositivoConectado()
|
||||
{
|
||||
if (!CanService.IsConnected) return false;
|
||||
if (!CanManager.CanService.IsConnected) return false;
|
||||
|
||||
if (Variaveis.OperacaoEmAndamento.DispMvd == null) return false;
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ namespace AgroBase.Services
|
|||
|
||||
foreach (var Modulo in Variaveis.OperacaoEmAndamento.DispMvd.Dados.Modulos.Where(x => !x.MovMotor.Inicializado))
|
||||
{
|
||||
CanService.RegistrarHandler(Modulo.MovMotor._EnderecoCAN_Rx, _OidHandler);
|
||||
CanManager.CanService.RegistrarHandler(Modulo.MovMotor._EnderecoCAN_Rx, _OidHandler);
|
||||
|
||||
RequisitarDado(Modulo.MovMotor._EnderecoCAN_Tx, Modulo.MovMotor._EnderecoCAN_Rx, OidParametros.CodErro);
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ namespace AgroBase.Services
|
|||
DefinirDispositivo(Mod_IDs);
|
||||
}
|
||||
|
||||
return Iniciado;
|
||||
return Mod_IDs.Any();
|
||||
}
|
||||
|
||||
private static void DefinirDispositivo(List<string> Mod_IDs)
|
||||
|
|
@ -89,7 +89,7 @@ namespace AgroBase.Services
|
|||
SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel()
|
||||
{
|
||||
Dispositivo = T_Code.Mov,
|
||||
Endereco = CanService._portName,
|
||||
Endereco = CanManager.CanService._portName,
|
||||
Versao = "1.0",
|
||||
Mod_ID = Mod_ID,
|
||||
});
|
||||
|
|
@ -103,7 +103,7 @@ namespace AgroBase.Services
|
|||
|
||||
private static void EnviarComando(T_Code Dispositivo, byte idTx, byte idRx, byte funcCode, byte[] payload = null, bool get = true)
|
||||
{
|
||||
CanService.AdicionarMensagemNaFila(Dispositivo, idTx, idRx, funcCode, funcCode, payload, get);
|
||||
CanManager.CanService.AdicionarMensagemNaFila(Dispositivo, idTx, idRx, funcCode, funcCode, payload, get);
|
||||
}
|
||||
|
||||
public static void RequisitarDado(byte idTx, byte idRx, OidParametros dado)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,468 @@
|
|||
using AgroBase.Models;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static AgroBase.Models.Enums;
|
||||
|
||||
namespace AgroBase.Services
|
||||
{
|
||||
public class CanServiceSerial : ICanService
|
||||
{
|
||||
public SerialPort _PortaCAN;
|
||||
public string _portName { get; set; }
|
||||
private int _baudRate;
|
||||
private object _lock = new object();
|
||||
public bool IsConnected { get; private set; }
|
||||
public bool Iniciado
|
||||
{
|
||||
get
|
||||
{
|
||||
return SerialService.DispositivosCan.Any(d => SerialService.DispositivosMapeados.Any(x => x.Status != StatusModulo.Desconectado && x.Dispositivo == d));
|
||||
}
|
||||
}
|
||||
private AsyncTaskTimerModel tmrEnviarCAN;
|
||||
private AsyncTaskTimerModel tmrOuvirCAN;
|
||||
private AsyncTaskTimerModel tmrProcessarCAN;
|
||||
private ConcurrentQueue<string> FramesRecebidos = new ConcurrentQueue<string>();
|
||||
private StringBuilder SerialCANBuffer = new StringBuilder();
|
||||
private readonly ConcurrentDictionary<byte, ICanMessageHandler> _roteadores = new ConcurrentDictionary<byte, ICanMessageHandler>();
|
||||
private byte? _ultimoIdEnviado = null;
|
||||
|
||||
private List<CanMessage> Mensagens = new List<CanMessage>();
|
||||
private readonly object _LockMensagens = new object();
|
||||
|
||||
private bool DebugMode = false;
|
||||
private void MostrarLog(string message, byte id)
|
||||
{
|
||||
if (DebugMode && new List<byte>() { 0x01 }.Contains(id))
|
||||
{
|
||||
Console.WriteLine($"[CAN] {message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void RegistrarHandler(byte endereco_rx, ICanMessageHandler handler)
|
||||
{
|
||||
_roteadores[endereco_rx] = handler;
|
||||
MostrarLog($"Roteador registrado: {endereco_rx.ToString("X")}", endereco_rx);
|
||||
}
|
||||
|
||||
public bool Inicializar(SerialPort Porta = null)
|
||||
{
|
||||
if (Porta == null && (_PortaCAN?.IsOpen ?? false))
|
||||
{
|
||||
Porta = _PortaCAN;
|
||||
}
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
Porta.Close();
|
||||
}
|
||||
|
||||
if (_PortaCAN == null || !IsConnected)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_portName = Porta.PortName;
|
||||
_baudRate = 115200;
|
||||
|
||||
_PortaCAN = new SerialPort(_portName, _baudRate, Parity.None, 8, StopBits.One);
|
||||
_PortaCAN.Open();
|
||||
|
||||
EnviarComando("S6\r"); // 500Kbps
|
||||
Thread.Sleep(100);
|
||||
EnviarComando("O\r"); // Open CAN
|
||||
Thread.Sleep(100);
|
||||
|
||||
tmrOuvirCAN?.Dispose();
|
||||
tmrOuvirCAN = new AsyncTaskTimerModel("tmrOuvirCAN", OuvirCAN, 5);
|
||||
tmrOuvirCAN.Start();
|
||||
|
||||
tmrEnviarCAN?.Dispose();
|
||||
tmrEnviarCAN = new AsyncTaskTimerModel("tmrEnviarCAN", EnviarCAN, 5);
|
||||
tmrEnviarCAN.Start();
|
||||
|
||||
tmrProcessarCAN?.Dispose();
|
||||
tmrProcessarCAN = new AsyncTaskTimerModel("tmrProcessarCAN", ProcessarCAN, 10);
|
||||
tmrProcessarCAN.Start();
|
||||
|
||||
IsConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_PortaCAN != null && !_PortaCAN.IsOpen)
|
||||
{
|
||||
_PortaCAN.Open();
|
||||
}
|
||||
|
||||
return IsConnected;
|
||||
}
|
||||
|
||||
private async Task ProcessarCAN()
|
||||
{
|
||||
int mensagensParaProcessar = Math.Min(FramesRecebidos.Count, 10);
|
||||
|
||||
for (int i = 0; i < mensagensParaProcessar; i++)
|
||||
{
|
||||
if (FramesRecebidos.TryDequeue(out string frame))
|
||||
{
|
||||
ProcessarFrameCAN(frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
// A fila ficou vazia antes de atingir o limite
|
||||
break;
|
||||
}
|
||||
}
|
||||
LimparMensagensAntigas();
|
||||
}
|
||||
|
||||
private async Task OuvirCAN()
|
||||
{
|
||||
if (_PortaCAN.BytesToRead > 0)
|
||||
{
|
||||
byte[] tempBuffer = await SerialService.LerDadosDaPortaSerialAsync(_PortaCAN, _PortaCAN.BytesToRead);
|
||||
|
||||
foreach (byte b in tempBuffer)
|
||||
{
|
||||
char c = (char)b;
|
||||
|
||||
if (c == '\r')
|
||||
{
|
||||
var frame = SerialCANBuffer.ToString();
|
||||
SerialCANBuffer.Clear();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(frame))
|
||||
{
|
||||
FramesRecebidos.Enqueue(frame);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SerialCANBuffer.Append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessarFrameCAN(string frame)
|
||||
{
|
||||
// Ex: t00183301020304
|
||||
if (!frame.StartsWith("t")) return;
|
||||
|
||||
try
|
||||
{
|
||||
if (frame.Length < 5) return;
|
||||
|
||||
int id = Convert.ToInt32(frame.Substring(1, 3), 16);
|
||||
int len = int.Parse(frame.Substring(4, 1));
|
||||
|
||||
byte[] data = new byte[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
data[i] = Convert.ToByte(frame.Substring(5 + i * 2, 2), 16);
|
||||
}
|
||||
|
||||
MostrarLog($"Processando frame: {frame}", (byte)id);
|
||||
|
||||
if (_roteadores.TryGetValue((byte)id, out ICanMessageHandler handler))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
T_Code dispositivo = handler?.Dispositivo ?? T_Code.Vzo;
|
||||
byte fCodeRx = data[0];
|
||||
byte parametroRx = data[1];
|
||||
string chave = GerarChaveMensagem(dispositivo, (byte)id, data);
|
||||
|
||||
CanMessage mensagem = null;
|
||||
lock (_LockMensagens)
|
||||
{
|
||||
mensagem = Mensagens.FirstOrDefault(x => x.Enviado && !x.Respondido && x.Dispositivo == x.Dispositivo && x.IdRx == id && x.DataTx[1] == data[1]);
|
||||
}
|
||||
if (mensagem == null)
|
||||
{
|
||||
mensagem = new CanMessage { Dispositivo = dispositivo, IdTx = (byte)id, IdRx = (byte)id, funcCodeRx = fCodeRx, DataRx = data, ChaveInterna = chave };
|
||||
}
|
||||
mensagem.DataRx = data;
|
||||
mensagem.Respondido = true;
|
||||
mensagem.RespondidoEm = DateTime.Now;
|
||||
|
||||
if ((handler?.Dispositivo ?? T_Code.Vzo) != T_Code.Vzo)
|
||||
{
|
||||
handler.ProcessarMensagem(mensagem);
|
||||
}
|
||||
else
|
||||
{
|
||||
MostrarLog($"Roteador não registrado! ID_Num não tratado: 0x{id:X2}, fCodeRx: 0x{fCodeRx:X2}, frame: {frame}", (byte)id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*byte[] data = new byte[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
data[i] = Convert.ToByte(frame.Substring(5 + i * 2, 2), 16);
|
||||
}
|
||||
|
||||
byte funcCodeRx = data[0];
|
||||
var mensagem = new CanMessage();
|
||||
|
||||
lock (Mensagens)
|
||||
{
|
||||
mensagem = Mensagens.Where(x => x.IdTx == id).OrderBy(x => x.EnviadoEm).FirstOrDefault(x => x.Enviado && !x.Respondido && x.funcCodeRx == funcCodeRx);
|
||||
}
|
||||
|
||||
if (mensagem == null)
|
||||
{
|
||||
mensagem = new CanMessage { IdTx = (byte)id, DataRx = data };
|
||||
}
|
||||
|
||||
mensagem.DataRx = data;
|
||||
mensagem.Respondido = true;
|
||||
mensagem.RespondidoEm = DateTime.Now;
|
||||
|
||||
if (_roteadores.TryGetValue(mensagem.IdTx, out var handler))
|
||||
{
|
||||
handler.ProcessarMensagem(mensagem);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[CAN] ID_Num não tratado: 0x{funcCodeRx:X2}");
|
||||
}*/
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[CAN] Erro ao processar frame: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void AdicionarMensagemNaFila(T_Code Dispositivo, byte idTx, byte idRx, byte funcCodeTx, byte funcCodeRx, byte[] payload = null, bool get = true)
|
||||
{
|
||||
if (_PortaCAN?.IsOpen != true) return;
|
||||
|
||||
var dados = new List<byte>() { funcCodeTx };
|
||||
|
||||
if (payload != null)
|
||||
dados.AddRange(payload);
|
||||
|
||||
if (Dispositivo == T_Code.Mks)
|
||||
{
|
||||
byte crc = CalcularChecksum(idTx, dados.ToArray());
|
||||
dados.Add(crc);
|
||||
}
|
||||
|
||||
var novaMensagem = new CanMessage
|
||||
{
|
||||
Momento = DateTime.Now,
|
||||
Dispositivo = Dispositivo,
|
||||
IdTx = idTx,
|
||||
IdRx = idRx,
|
||||
funcCodeTx = funcCodeTx,
|
||||
funcCodeRx = funcCodeRx,
|
||||
DataTx = dados.ToArray(),
|
||||
ComResposta = get,
|
||||
ChaveInterna = GerarChaveMensagem(Dispositivo, idRx, dados.ToArray())
|
||||
};
|
||||
|
||||
bool mensagemNaFila = false;
|
||||
|
||||
lock (_LockMensagens)
|
||||
{
|
||||
mensagemNaFila = Mensagens.Any(x => (!x.Enviado || (x.ComResposta && x.Enviado && !x.Respondido)) && x.IdTx == idTx && x.IdRx == idRx && x.DataTx.SequenceEqual(dados.ToArray()));
|
||||
if (!mensagemNaFila)
|
||||
{
|
||||
Mensagens.Add(novaMensagem);
|
||||
MostrarLog($"Mensagem adicionada na fila. ID: {novaMensagem.IdTx}, Data: {FuncoesGlobais.ConverterComandoBytesParaTexto(novaMensagem.DataTx)}", idTx);
|
||||
}
|
||||
else
|
||||
{
|
||||
MostrarLog($"Mensagem já pendente e idêntica. ID: {novaMensagem.IdTx}, Data: {FuncoesGlobais.ConverterComandoBytesParaTexto(novaMensagem.DataTx)}", idTx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CanMessage ObterProximaMensagem()
|
||||
{
|
||||
List<CanMessage> mensagensPendentes;
|
||||
|
||||
lock (_LockMensagens)
|
||||
{
|
||||
mensagensPendentes = Mensagens
|
||||
.Where(x => !x.Enviado)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (!mensagensPendentes.Any())
|
||||
return null;
|
||||
|
||||
// Primeiro tenta pegar mensagens com Get == false
|
||||
var mensagensPrioritarias = mensagensPendentes
|
||||
.Where(x => !x.ComResposta)
|
||||
.ToList();
|
||||
|
||||
// Se não houver mensagens prioritárias, considera todas
|
||||
var mensagensParaProcessar = mensagensPrioritarias.Any()
|
||||
? mensagensPrioritarias
|
||||
: mensagensPendentes;
|
||||
|
||||
// Agrupa por IdTx e ordena os grupos
|
||||
var idsDisponiveis = mensagensParaProcessar
|
||||
.Select(x => x.IdTx)
|
||||
.Distinct()
|
||||
.OrderBy(x => x)
|
||||
.ToList();
|
||||
|
||||
byte proximoId;
|
||||
|
||||
if (!_ultimoIdEnviado.HasValue || !idsDisponiveis.Contains(_ultimoIdEnviado.Value))
|
||||
{
|
||||
proximoId = idsDisponiveis.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
var index = idsDisponiveis.IndexOf(_ultimoIdEnviado.Value);
|
||||
proximoId = (index + 1 < idsDisponiveis.Count) ? idsDisponiveis[index + 1] : idsDisponiveis.First();
|
||||
}
|
||||
|
||||
// Seleciona a mensagem mais antiga com esse IdTx
|
||||
var proximaMsg = mensagensParaProcessar
|
||||
.Where(x => x.IdTx == proximoId)
|
||||
.OrderBy(x => x.Momento)
|
||||
.FirstOrDefault();
|
||||
|
||||
return proximaMsg;
|
||||
}
|
||||
|
||||
private async Task EnviarCAN()
|
||||
{
|
||||
if (_PortaCAN?.IsOpen != true) return;
|
||||
|
||||
CanMessage msg = ObterProximaMensagem();
|
||||
|
||||
if (msg != null)
|
||||
{
|
||||
string frame = CriarFrame(msg);
|
||||
|
||||
if (_ultimoIdEnviado.HasValue && msg.IdTx == _ultimoIdEnviado.Value)
|
||||
{
|
||||
await Task.Delay(5);
|
||||
}
|
||||
|
||||
if (msg.Dispositivo == T_Code.Mks)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
if (!EnviarComando(frame))
|
||||
{
|
||||
MostrarLog("Erro ao enviar frame", msg.IdTx);
|
||||
}
|
||||
|
||||
msg.Enviado = true;
|
||||
msg.EnviadoEm = DateTime.Now;
|
||||
_ultimoIdEnviado = msg.IdTx;
|
||||
}
|
||||
}
|
||||
|
||||
private string CriarFrame(CanMessage msg)
|
||||
{
|
||||
string frame = $"t{msg.IdTx:X3}{msg.DataTx.Length}";
|
||||
foreach (byte b in msg.DataTx)
|
||||
frame += b.ToString("X2");
|
||||
|
||||
frame += "\r";
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
private bool EnviarComando(string comando)
|
||||
{
|
||||
if (_PortaCAN?.IsOpen == true)
|
||||
{
|
||||
_PortaCAN.Write(comando);
|
||||
//Console.WriteLine($"[CAN] Frame enviado: {comando}");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void LimparMensagensAntigas()
|
||||
{
|
||||
var agora = DateTime.Now;
|
||||
|
||||
lock (_LockMensagens)
|
||||
{
|
||||
List<CanMessage> mensagens = Mensagens
|
||||
.Where(x =>
|
||||
(!x.ComResposta && x.Enviado) ||
|
||||
(x.ComResposta && x.Respondido) ||
|
||||
(x.ComResposta && x.Enviado && !x.Respondido && x.EnviadoEm < agora.AddSeconds(-1)) ||
|
||||
(x.Momento < agora.AddSeconds(-30))
|
||||
)
|
||||
.ToList();
|
||||
foreach (var item in mensagens)
|
||||
{
|
||||
Mensagens.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Fechar()
|
||||
{
|
||||
tmrOuvirCAN?.Dispose();
|
||||
tmrEnviarCAN?.Dispose();
|
||||
tmrProcessarCAN?.Dispose();
|
||||
|
||||
lock (_LockMensagens)
|
||||
{
|
||||
Mensagens.Clear();
|
||||
}
|
||||
|
||||
SerialCANBuffer.Clear();
|
||||
FramesRecebidos = new ConcurrentQueue<string>();
|
||||
_roteadores.Clear();
|
||||
_ultimoIdEnviado = null;
|
||||
|
||||
if (_PortaCAN?.IsOpen == true)
|
||||
{
|
||||
EnviarComando("C\r");
|
||||
_PortaCAN.Close();
|
||||
}
|
||||
|
||||
IsConnected = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Fechar();
|
||||
}
|
||||
|
||||
private byte CalcularChecksum(byte id, byte[] data)
|
||||
{
|
||||
int soma = id;
|
||||
foreach (var b in data)
|
||||
soma += b;
|
||||
return (byte)(soma & 0xFF);
|
||||
}
|
||||
|
||||
private string GerarChaveMensagem(T_Code Dispositivo, byte Id, byte[] data)
|
||||
{
|
||||
byte parametro = 0x00;
|
||||
|
||||
// Para dispositivos que não sejam MKS, usar DataTx[1] se existir
|
||||
if (Dispositivo != T_Code.Mks && (data?.Length ?? 0) > 1)
|
||||
{
|
||||
parametro = data[1];
|
||||
}
|
||||
|
||||
return $"{Id}_{data[0]}_{parametro}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -222,6 +222,18 @@ namespace AgroBase.Services
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (CanManager.UseCanSerial && !CanManager.CanService.Iniciado)
|
||||
{
|
||||
AtualizarConsole($"{PortaCom} - Procurando dispositivos no barramento CAN");
|
||||
bool dispositivoCan = await ProcurarDispositivosCAN(_Porta);
|
||||
if (dispositivoCan)
|
||||
{
|
||||
_Porta.Close();
|
||||
AtualizarConsole("Barramento CAN encontrado");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Variaveis.IsAgroMonitor) return;
|
||||
|
|
@ -372,17 +384,17 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
private static async Task<bool> ProcurarDispositivosCAN()
|
||||
private static async Task<bool> ProcurarDispositivosCAN(SerialPort Porta = null)
|
||||
{
|
||||
if (!CanService.InicializarCanal())
|
||||
if (!CanManager.CanService.Inicializar(Porta))
|
||||
{
|
||||
AtualizarConsole($"Adaptador {CanService._portName} não conectado!");
|
||||
AtualizarConsole($"Adaptador {CanManager.CanService._portName} não conectado!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (true && Variaveis.OperacaoEmAndamento.DispSen != null && !Variaveis.OperacaoEmAndamento.DispSen.Dados.Conectado)
|
||||
{
|
||||
AtualizarConsole($"{CanService._portName} - Procurando dispositivo SEN");
|
||||
AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivo SEN");
|
||||
bool DispSen = await Variaveis.OperacaoEmAndamento.DispSen.Dados.VerificaDispositivoConectado();
|
||||
if (DispSen)
|
||||
{
|
||||
|
|
@ -392,7 +404,7 @@ namespace AgroBase.Services
|
|||
|
||||
if (true && Variaveis.OperacaoEmAndamento.DispAtu != null && !Variaveis.OperacaoEmAndamento.DispAtu.Dados.ConexaoAtiva)
|
||||
{
|
||||
AtualizarConsole($"{CanService._portName} - Procurando dispositivo ATU");
|
||||
AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivo ATU");
|
||||
bool DispAtu = await Variaveis.OperacaoEmAndamento.DispAtu.Dados.VerificaDispositivoConectado();
|
||||
if (DispAtu)
|
||||
{
|
||||
|
|
@ -402,7 +414,7 @@ namespace AgroBase.Services
|
|||
|
||||
if (true && !MKS057DCanService.Referenciando && (!MKS057DCanService.Iniciado || MKS057DCanService.DadosLeitura.Count(x => x.Iniciado) < (Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.Count() ?? 4)))
|
||||
{
|
||||
AtualizarConsole($"{CanService._portName} - Procurando dispositivos MKS");
|
||||
AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivos MKS");
|
||||
bool DispMks = await MKS057DCanService.VerificaDispositivoConectado();
|
||||
if (DispMks)
|
||||
{
|
||||
|
|
@ -412,7 +424,7 @@ namespace AgroBase.Services
|
|||
|
||||
if (true && (!OIDCanService.Iniciado || OIDCanService.DadosLeitura.Count(x => x.Iniciado) < (Variaveis.OperacaoEmAndamento.DispMvd?.Dados?.Modulos?.Count() ?? 4)))
|
||||
{
|
||||
AtualizarConsole($"{CanService._portName} - Procurando dispositivo OID");
|
||||
AtualizarConsole($"{CanManager.CanService._portName} - Procurando dispositivo OID");
|
||||
bool DispOid = await OIDCanService.VerificaDispositivoConectado();
|
||||
if (DispOid)
|
||||
{
|
||||
|
|
@ -420,7 +432,7 @@ namespace AgroBase.Services
|
|||
}
|
||||
}
|
||||
|
||||
return CanService.Iniciado;
|
||||
return CanManager.CanService.Iniciado;
|
||||
}
|
||||
|
||||
private static void ProcurarDispositivosUSB()
|
||||
|
|
|
|||
Loading…
Reference in New Issue