agrobot_base/AgroBase/AgroBase/Models/Modules/AtuadorModel.cs

1882 lines
80 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using AgroBase.Services;
using AgroBase.Services.Operadores;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using static AgroBase.Models.Enums;
namespace AgroBase.Models.Modules
{
public class AtuadorModel : DispositivoBaseModel
{
public T_Code Dispositivo { get; set; } = T_Code.Atu;
public string Modulo_ID { get; set; } = T_Code.Atu.ToString();
public byte _EnderecoCAN_Tx { get; set; } = 0x13;
public byte _EnderecoCAN_Rx { get; set; } = 0x14;
public int _TaxaAmostragem { get; set; } = 1000;
public double FrequenciaBase
{
get
{
return 1.0 / (_TaxaAmostragem / 1000.0);
}
}
public bool RequisitarDados { get; set; } = true;
public static int TempoLimiteConexao { get; set; } = 8000;
public bool Conectado
{
get
{
DateTime Agora = DateTime.Now;
bool modConectado = DadosLeitura.UltimoComandoRespondido.AddMilliseconds(TempoLimiteConexao) > Agora;
return modConectado;
}
}
public bool ConexaoAtiva
{
get
{
return HealthWorkerService.ModulosSaude.Any(x => x.modulo == Dispositivo && x.status == StatusModulo.Operante);
}
}
public bool AtuPronto { get; set; } = false;
public double AnguloBarraEsquerda { get; set; }
public double AnguloBarraDireita { get; set; }
public double AnguloBarraAltura { get; set; }
public static PinoutDataModel Pinout { get; set; }
public static List<FuncoesPinout> Funcoes { get; set; } = new List<FuncoesPinout>()
{
FuncoesPinout.SDA,
FuncoesPinout.SCL,
};
//public int QuantidadeBicos { get; set; } = 4;
public double DensidadeHerbicida { get; set; } = 1.0;
public List<AtuadorBicoModel> BicosPulverizadores { get; set; }
public List<AtuadorBombaModel> BombasPressurizadoras { get; set; }
public List<SensorModel> Sensores { get; set; }
public string ScriptDeteccaoErvas { get; set; } = PythonService.ScriptWeedDetector;
public AsyncTaskTimerModel tmrRegistrador;
private int _TempoReconexaoComponentes = 5;
private int _VezesLeituraReconexao = 0;
public AtuadorMensagemJsonModel DadosLeitura { get; set; } = new AtuadorMensagemJsonModel();
public AtuHandler _AtuHandler = new AtuHandler();
private SensorModel SensorAnguloBarraEsquerda
{
get
{
var sMAG = Sensores?.FirstOrDefault(x => x.Componente == S_Code.sMAG && x.ID.Contains("ESQ"));
return sMAG;
}
}
private SensorModel SensorAnguloBarraDireita
{
get
{
var sMAG = Sensores?.FirstOrDefault(x => x.Componente == S_Code.sMAG && x.ID.Contains("DIR"));
return sMAG;
}
}
private SensorModel SensorAnguloBarraAltura
{
get
{
var sMAG = Sensores?.FirstOrDefault(x => x.Componente == S_Code.sMAG && x.ID.Contains("ALT"));
return sMAG;
}
}
public HerbicideCounterService ContadorCarga = new HerbicideCounterService(VariaveisEquipamento.CapacidadeReservatorio, VariaveisEquipamento.DensidadeHerbicida);
#region VAZAO
private SensorModel SensorFluxoLinha => Sensores.FirstOrDefault(x => x.Componente == S_Code.sFLX && x.ID == "FLXLN");
public double VolumeVazaoAferido { get; set; } = 0;
public double _FluxoMlRef { get; set; } = 600;
public double _FluxoPulsoRef { get; set; } = 538;
public double VazaoFluxoMLpSInstantanea => SensorFluxoLinha?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Fluxo)?.atual?.valor ?? 0;
public double VazaoFluxoMLpSInstantaneaCalc
{
get
{
int pulsos = (int)(SensorFluxoLinha?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.QtdPulsos)?.atual?.valor ?? 0.0);
int periodo = (int)(SensorFluxoLinha?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Periodo)?.atual?.valor ?? 0.0);
if (pulsos <= 0 || periodo <= 0) return 0.0;
double frequencia = (double)pulsos / (double)periodo * 1000.0; // Hz
// Equação do fabricante: F = 8.1*q - 3 (q em L/min)
double q_L_min = (frequencia + 3.0) / 8.1;
// Limites físicos do sensor (do próprio anúncio): 0.3–10 L/min
if (q_L_min < 0) q_L_min = 0; // evita negativo perto do zero
if (q_L_min > 10.0) q_L_min = 10.0; // opcional: clamp no máximo
// Converte para mL/s
double q_mL_s = q_L_min * (1000.0 / 60.0);
return q_mL_s;
}
}
private DateTime _ultimoRegistroVazao; // Armazena o horário da última atualização
public double VazaoMediaMLs { get; set; }
public double VolumeVazadoML { get; set; }
public double TempoAtuado { get; set; }
private List<AtuadorBicoModel> bicosAtivosAnterior;
public void AtualizaTempoAtuadoGeral()
{
DateTime agora = DateTime.Now;
// Quem está ativo AGORA
var bicosAtivos = BicosPulverizadores.Where(x => x.Inicializado && x._EstadoLeitura == Estado.Ligado).ToList();
bool algumBicoAtivo = bicosAtivos.Count > 0;
bool algumBicoAtivoAnterior = bicosAtivosAnterior?.Any() ?? false;
// Início de uma "sessão" (transição OFF -> ON)
if (algumBicoAtivo && !algumBicoAtivoAnterior)
{
_ultimoRegistroVazao = agora; // inicia integração a partir daqui
// (opcional) se você quer métricas por sessão, zere volumes/médias aqui
// foreach (var b in BicosPulverizadores) { b.VolumeVazadoML = 0; b.VazaoMediaMLs = 0; }
// _tempoAtuadoBicoSeg.Clear();
}
// Fim de sessão (transição ON -> OFF)
if (!algumBicoAtivo && algumBicoAtivoAnterior)
{
AtualizaVazaoEVolume(bicosAtivosAnterior, agora);
_ultimoRegistroVazao = DateTime.MinValue;
VazaoMediaMLs = TempoAtuado > 0 ? (VolumeVazadoML / TempoAtuado) : 0;
// zera instantânea quando não tem bico ativo
foreach (var b in BicosPulverizadores)
b.VazaoInstantaneaMLs = 0;
}
// Integra vazão/volume enquanto tiver bico ativo
AtualizaVazaoEVolume(bicosAtivos, agora);
// Atualiza indicadores
AtualizaPercentualErvas();
bicosAtivosAnterior = new List<AtuadorBicoModel>(bicosAtivos);
}
private void AtualizaVazaoEVolume(List<AtuadorBicoModel> bicosAtivos, DateTime agora)
{
// Só integra se houver bicos ativos
if (bicosAtivos == null || bicosAtivos.Count == 0)
return;
// Captura vazão total instantânea (ml/s)
double vazaoTotalInst = VazaoFluxoMLpSInstantanea;
if (_ultimoRegistroVazao == default || _ultimoRegistroVazao == DateTime.MinValue)
return;
// dt em segundos
double dt = (agora - _ultimoRegistroVazao).TotalSeconds;
_ultimoRegistroVazao = agora;
if (dt <= 0) return;
// ======== GERAL ========
VolumeVazadoML += vazaoTotalInst * dt;
TempoAtuado += dt;
VazaoMediaMLs = TempoAtuado > 0 ? (VolumeVazadoML / TempoAtuado) : 0;
// ======== POR BICO (divide por ativos) ========
double vazaoPorBicoInst = vazaoTotalInst / bicosAtivos.Count;
foreach (var bico in bicosAtivos)
{
// Instantânea por bico
bico.VazaoInstantaneaMLs = vazaoPorBicoInst;
// Volume por bico (ml) = (ml/s) * s
bico.VolumeVazadoML += vazaoPorBicoInst * dt;
bico.TempoAtuado += dt;
bico.VazaoMediaMLs = bico.TempoAtuado > 0 ? (bico.VolumeVazadoML / bico.TempoAtuado) : 0;
}
}
private void AtualizaPercentualErvas()
{
//double tempoTotalOperacao = Variaveis.OperacaoEmAndamento.Sensoriamento.TempoDecorridoSeg * 1000.0;
double tempoTotalOperacao = (Variaveis.OperacaoEmAndamento.Sensoriamento?.Movimentacao?.TempoMovimentoSegs ?? 0) * 1000.0;
if (tempoTotalOperacao > 0)
{
var percentual = Math.Round((TempoAtuado / tempoTotalOperacao) * 100.0, 2);
PercentualErvasTerreno = percentual;
}
else
{
PercentualErvasTerreno = 0;
}
}
#endregion
#region PRESSAO
private SensorModel SensorPressaoLinha
{
get
{
return Sensores.FirstOrDefault(x => x.Componente == S_Code.sPRS && x.ID == "PRSLN");
}
}
public double _PressaoVmin { get; set; } = 0.0;
public double _PressaoVmax { get; set; } = 4.5;
public double _PressaoMax { get; set; } = 174.045; // 1,2 MPa
public double PressaoLinha => SensorPressaoLinha?.ValoresLeituras.FirstOrDefault(x => x.funcao == FuncoesPinout.Pressao)?.atual?.valor ?? 0;
public double PressaoLinhaCalc
{
get
{
double _LeituraPressao = SensorPressaoLinha?.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.LeituraBruta)?.atual?.valor ?? 0;
double tensao = FuncoesMatematicas.Map(_LeituraPressao, 0, 4095, 0, 3300) / 1000.0;
double pressao = FuncoesMatematicas.Map(tensao, _PressaoVmin, _PressaoVmax, 0, _PressaoMax);
return pressao;
}
}
#endregion
public double PercentualErvasTerreno { get; set; } = 0;
public static AtuadorModel CarregarParametrosIniciais()
{
AtuadorModel Atuador = new AtuadorModel()
{
_EnderecoCAN_Tx = 0x12,
_EnderecoCAN_Rx = 0x12,
_TaxaAmostragem = 1000,
RequisitarDados = true,
// 11 ~ 50
Sensores = new List<SensorModel>()
{
// pressao 11 ~ 20
new SensorModel()
{
ID = "PRSLN",
ID_Num = 11,
Descricao = "Pressão na linha principal",
Componente = S_Code.sPRS,
Aferir = true,
Mandatorio = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.Pressao,
},
Parametros = new List<SensorParametroModel>()
{
new SensorParametroModel()
{
Descricao = "Vmin",
Valor = "0",
QtdBytes = 1,
Escalar = false,
Enviar = false
},
new SensorParametroModel()
{
Descricao = "Vmax",
Valor = "4.5",
QtdBytes = 1,
Escalar = true,
Enviar = false
},
new SensorParametroModel()
{
Descricao = "Pmax",
Valor = "174",
QtdBytes = 1,
Escalar = false,
Enviar = false
},
},
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.LeituraBruta,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Pressao,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
analise_health = true,
maximo = 120,
minimo = 0,
nominal = 85,
unidade_medida = "PSI"
}
}
},
// fluxo 21 ~ 30
new SensorModel()
{
ID = "FLXLN",
ID_Num = 21,
Descricao = "Fluxo na linha principal",
Componente = S_Code.sFLX,
Aferir = true,
Mandatorio = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.Fluxo,
},
Parametros = new List<SensorParametroModel>(),
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.QtdPulsos,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Periodo,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Fluxo,
posicao = CanMessagePosicaoDados.Dados1,
indice = 2,
analise_health = true,
maximo = 20,
minimo = 0,
nominal = 0,
unidade_medida = "mL/s"
}
}
},
// massa 31 ~ 40
new SensorModel()
{
ID = "MASRS",
ID_Num = 31,
Descricao = "Massa do reservatório de herbicida",
Componente = S_Code.sMAS,
Aferir = true,
Mandatorio = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.DT,
FuncoesPinout.SCK,
},
Parametros = new List<SensorParametroModel>()
{
new SensorParametroModel()
{
Descricao = "Offset",
Enviar = true,
Escalar = true,
QtdBytes = 2,
Valor = "0.0"
}
},
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.LeituraBruta,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Massa,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
analise_health = true,
maximo = 35,
minimo = 0,
nominal = 30,
unidade_medida = "Kg"
}
}
},
// magnetico 41 ~ 50
new SensorModel()
{
ID = "MAGESQ",
Descricao = "Angulo Barra Esquerda",
ID_Num = 41,
Componente = S_Code.sMAG,
Aferir = true,
Mandatorio = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.I2C,
},
Parametros = new List<SensorParametroModel>()
{
new SensorParametroModel()
{
Descricao = "Endereço I2C",
Valor = "0x36",
QtdBytes = 1,
Escalar = false,
Enviar = true
},
new SensorParametroModel()
{
Descricao = "Canal MUX",
Valor = "0",
QtdBytes = 1,
Escalar = false,
Enviar = true
},
},
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.LeituraBruta,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
unidade_medida = ""
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.LeituraAngulo,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
unidade_medida = "°"
},
}
},
new SensorModel()
{
ID = "MAGDIR",
Descricao = "Angulo Barra Direita",
ID_Num = 42,
Componente = S_Code.sMAG,
Aferir = true,
Mandatorio = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.I2C,
},
Parametros = new List<SensorParametroModel>()
{
new SensorParametroModel()
{
Descricao = "Endereço I2C",
Valor = "0x36",
QtdBytes = 1,
Escalar = false,
Enviar = true
},
new SensorParametroModel()
{
Descricao = "Canal MUX",
Valor = "1",
QtdBytes = 1,
Escalar = false,
Enviar = true
},
},
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.LeituraBruta,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
unidade_medida = ""
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.LeituraAngulo,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
unidade_medida = "°"
},
}
},
new SensorModel()
{
ID = "MAGALT",
Descricao = "Angulo Barra Altura",
ID_Num = 43,
Componente = S_Code.sMAG,
Aferir = true,
Mandatorio = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.I2C,
},
Parametros = new List<SensorParametroModel>()
{
new SensorParametroModel()
{
Descricao = "Endereço I2C",
Valor = "0x36",
QtdBytes = 1,
Escalar = false,
Enviar = true
},
new SensorParametroModel()
{
Descricao = "Canal MUX",
Valor = "2",
QtdBytes = 1,
Escalar = false,
Enviar = true
},
},
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.LeituraBruta,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
unidade_medida = ""
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.LeituraAngulo,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
unidade_medida = "°"
},
}
},
},
// 51 ~ 51
BombasPressurizadoras = new List<AtuadorBombaModel>()
{
new AtuadorBombaModel()
{
ID = "BMBLN",
ID_Num = 51,
Sensor_ID_Num = 11,
ComandoAtuar = false,
Componente = S_Code.sBMB,
Comandar = true,
Mandatorio = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.ENA,
FuncoesPinout.CS,
FuncoesPinout.INA,
FuncoesPinout.INB,
FuncoesPinout.PWM,
},
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.EstadoLeitura,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Potencia,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.PressaoSP,
posicao = CanMessagePosicaoDados.Dados1,
indice = 2,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Pressao,
posicao = CanMessagePosicaoDados.Dados1,
indice = 3,
}
}
},
new AtuadorBombaModel()
{
ID = "BMBAGT",
ID_Num = 52,
Sensor_ID_Num = -1,
ComandoAtuar = false,
Componente = S_Code.sBMB,
Comandar = true,
Mandatorio = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.ENA,
FuncoesPinout.CS,
FuncoesPinout.INA,
FuncoesPinout.INB,
FuncoesPinout.PWM,
},
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.EstadoLeitura,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Potencia,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.PressaoSP,
posicao = CanMessagePosicaoDados.Dados1,
indice = 2,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Pressao,
posicao = CanMessagePosicaoDados.Dados1,
indice = 3,
}
}
},
},
// 61 ~ 67
BicosPulverizadores = new List<AtuadorBicoModel>(),
};
for (int i = 0; i < Variaveis.OperacaoEmAndamento.Parametros.QtdBicos; i++)
{
int posicao = (i + 1);
Atuador.BicosPulverizadores.Add(new AtuadorBicoModel()
{
ID = "B" + posicao.ToString("00"),
ID_Num = 60 + posicao,
Componente = S_Code.sBIC,
Posicao = posicao,
Inicializado = false,
AnguloControle = 0,
AnguloAbertura = 30,
Comandar = true,
Mandatorio = true,
ComandoAtuar = false,
AtuacaoNivelAlto = true,
Funcoes = new List<FuncoesPinout>()
{
FuncoesPinout.Solenoide,
FuncoesPinout.Servo,
},
VazaoMediaMLs = 0,
ValoresLeituras = new List<SensorValoresLeituraModel>()
{
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.Iniciado,
posicao = CanMessagePosicaoDados.Status,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.EstadoLeitura,
posicao = CanMessagePosicaoDados.Dados1,
indice = 0,
},
new SensorValoresLeituraModel()
{
funcao = FuncoesPinout.ServoAnguloLeitura,
posicao = CanMessagePosicaoDados.Dados1,
indice = 1,
}
}
});
}
return Atuador;
}
public void LimparDados<T>(T _dados)
{
var Dados = _dados as AtuadorModel;
Dados.DadosLeitura = new AtuadorMensagemJsonModel();
Dados.DadosLeitura.Momento = DateTime.MinValue;
Dados.AtuPronto = false;
Dados.PercentualErvasTerreno = 0;
Dados.BombasPressurizadoras.ForEach(x => x.Funcoes = x.Funcoes.Distinct().ToList());
ReiniciarLeituras(Dados);
}
public void ReiniciarLeituras(AtuadorModel Dados)
{
Dados.Sensores.ForEach(x =>
{
x.ValoresLeituras.ForEach(l =>
{
l.atual = new CanCommandFreqModel<dynamic>();
});
});
Dados.BicosPulverizadores.ForEach(x =>
{
x.ComandoAtuar = false;
x.AnguloControle = 0;
x.VazaoInstantaneaMLs = 0;
x.VolumeVazadoML = 0;
x.VazaoMediaMLs = 0;
x.TempoAtuado = 0;
x.Atuacoes = 0;
x.ValoresLeituras.ForEach(l =>
{
l.atual = new CanCommandFreqModel<dynamic>();
});
});
Dados.BombasPressurizadoras.ForEach(x =>
{
x.ComandoAtuar = false;
x.ValoresLeituras.ForEach(l =>
{
l.atual = new CanCommandFreqModel<dynamic>();
});
});
Dados.VolumeVazaoAferido = 0;
Dados._ultimoRegistroVazao = DateTime.MinValue;
Dados.VolumeVazadoML = 0;
Dados.VazaoMediaMLs = 0;
Dados.TempoAtuado = 0;
Dados.AnguloBarraAltura = 0;
Dados.AnguloBarraDireita = 0;
Dados.AnguloBarraEsquerda = 0;
Dados.PercentualErvasTerreno = 0;
}
public async Task<bool> VerificaDispositivoConectado()
{
if (!CanManager.CanService.IsConnected) return false;
if (Variaveis.OperacaoEmAndamento.DispAtu == null) return false;
CanManager.CanService.RegistrarHandler(_EnderecoCAN_Rx, _AtuHandler);
RequisitarDadosModulo(Variaveis.ID_Num_sMOD, CanMessagePosicaoDados.Status, CanMessagePosicaoDados.Status, true);
DateTime Inicio = DateTime.Now;
bool Respondido() => Variaveis.OperacaoEmAndamento.DispAtu.Dados.DadosLeitura.UltimoComandoRespondido > Inicio;
await FuncoesGlobais.AguardarCondicaoAsync(Respondido, 500, 50);
if (Conectado)
{
DefinirDispositivo();
Variaveis.OperacaoEmAndamento.DispAtu?.EnviarProtocolosConfiguracao();
}
return Conectado;
}
private void DefinirDispositivo()
{
if (!SerialService.DispositivosMapeados.Any(x => x.Dispositivo == Dispositivo && x.Mod_ID == Modulo_ID))
{
SerialService.DispositivosMapeados.Add(new DispositivoDetalhesModel()
{
Dispositivo = DadosLeitura.Dispositivo,
Endereco = CanManager.CanService._portName,
Versao = DadosLeitura.Versao.ToString(),
Mod_ID = Modulo_ID
});
Variaveis.OperacaoEmAndamento.ModsCalibragem[Dispositivo] = new Dictionary<string, bool>();
Variaveis.OperacaoEmAndamento.Sensoriamento.InserirLog(Dispositivo, StatusModulo.Conectado, 0, "Dispositivo conectado");
}
}
public void RequisitarDadosModulo(int ID_Num, CanMessagePosicaoDados posicaoTx, CanMessagePosicaoDados posicaoRx, bool aguardaResposta)
{
Variaveis.OperacaoEmAndamento.DispAtu?.AdicionarMensagemFila(ID_Num, posicaoTx, posicaoRx, null, aguardaResposta);
}
public (int, CanMessagePosicaoDados, byte[]) ProtocoloComandoReset()
{
return (
Variaveis.ID_Num_sMOD,
CanMessagePosicaoDados.Command1,
new byte[]
{
(byte)Estado.Reset
}
);
}
public List<(int, CanMessagePosicaoDados, byte[])> ProtocoloConfiguracao(bool Conectar)
{
// Inicializar o dicionário
List<(int, CanMessagePosicaoDados, byte[])> config = new List<(int, CanMessagePosicaoDados, byte[])>();
if (!DadosLeitura.Conectado)
{
// Adicionar configuração do ProtocoloMOD
var protocoloMOD = ModConfig(Conectar);
config.AddRange(protocoloMOD);
}
bool configurarTodos = !DadosLeitura.Conectado && Conectar;
// Adicionar configurações dos Sensores que devem ser aferidos
var sensoresConfig = SensoresConfig(Conectar, configurarTodos);
config.AddRange(sensoresConfig);
// Adicionar configurações dos Bicos
var bicosConfig = BicosConfig(Conectar, configurarTodos);
config.AddRange(bicosConfig);
// Adicionar configurações das Bombas
var bombasConfig = BombasConfig(Conectar, configurarTodos);
config.AddRange(bombasConfig);
return config;
}
private List<(int, CanMessagePosicaoDados, byte[])> ModConfig(bool Conectar)
{
var config = new List<byte>
{
(byte)(Conectar ? 1 : 0),
(byte)(_TaxaAmostragem / 10),
(byte)(RequisitarDados ? 1 : 0),
};
config.AddRange(PinoutModel.PinoProtocolo(Pinout, Funcoes, false));
return new List<(int, CanMessagePosicaoDados, byte[])>
{
(Variaveis.ID_Num_sMOD, CanMessagePosicaoDados.Config1, config.ToArray()),
};
}
private List<(int, CanMessagePosicaoDados, byte[])> SensoresConfig(bool Conectar, bool ConfigurarTodos)
{
var config = new List<(int, CanMessagePosicaoDados, byte[])>();
foreach (var sensor in Sensores.Where(x => (x.Aferir && (ConfigurarTodos ? true : x.Inicializado != Conectar)) || (!x.Aferir && x.Inicializado)))
{
if (!sensor.Aferir && sensor.Inicializado)
{
Conectar = false;
}
(CanMessagePosicaoDados posicao, byte[] payload) = sensor.ProtocoloConfiguracao(Pinout, Conectar, Dispositivo);
if (payload != null)
{
config.Add((sensor.ID_Num, posicao, payload));
}
}
return config;
}
private List<(int, CanMessagePosicaoDados, byte[])> BicosConfig(bool Conectar, bool ConfigurarTodos)
{
var config = new List<(int, CanMessagePosicaoDados, byte[])>();
foreach (var bico in BicosPulverizadores.Where(x => (x.Comandar && (ConfigurarTodos ? true : x.Inicializado != Conectar)) || (!x.Comandar && x.Inicializado)))
{
if (!bico.Comandar && bico.Inicializado)
{
Conectar = false;
}
(CanMessagePosicaoDados posicao, var payload) = bico.ProtocoloConfiguracao(Pinout, Conectar);
if (payload != null)
{
config.Add((bico.ID_Num, posicao, payload));
}
}
return config;
}
private List<(int, CanMessagePosicaoDados, byte[])> BombasConfig(bool Conectar, bool ConfigurarTodos)
{
var config = new List<(int, CanMessagePosicaoDados, byte[])>();
foreach (var bomba in BombasPressurizadoras.Where(x => (x.Comandar && (ConfigurarTodos ? true : x.Inicializado != Conectar)) || (!x.Comandar && x.Inicializado)))
{
if (!bomba.Comandar && bomba.Inicializado)
{
Conectar = false;
}
List<(CanMessagePosicaoDados, byte[])> dados = bomba.ProtocoloConfiguracao(Pinout, Conectar);
foreach (var dado in dados)
{
config.Add((bomba.ID_Num, dado.Item1, dado.Item2));
}
}
return config;
}
public void IniciarRegistrador()
{
if (!(tmrRegistrador?.IsRunning ?? false))
{
LimparDados(this);
PararRegistrador();
tmrRegistrador = new AsyncTaskTimerModel("tmrRegistrador_" + T_Code.Atu.ToString(), tmrRegistrador_Tick, _TaxaAmostragem);
tmrRegistrador.Start();
}
}
public void PararRegistrador()
{
tmrRegistrador?.Dispose();
}
public async Task tmrRegistrador_Tick()
{
if (Variaveis.Fechando) return;
tmrRegistrador.SetInterval(_TaxaAmostragem);
if (SerialService.DispositivosMapeados.Any(x => x.Dispositivo == T_Code.Atu) && (_VezesLeituraReconexao >= _TempoReconexaoComponentes))
{
Variaveis.OperacaoEmAndamento.DispAtu?.EnviarProtocolosConfiguracao();
_VezesLeituraReconexao = 0;
}
else
{
_VezesLeituraReconexao++;
}
if (DadosLeitura.Conectado)
{
if (RequisitarDados)
{
RequisitarDadosModulo(Variaveis.ID_Num_sTOD, CanMessagePosicaoDados.DadosAll, CanMessagePosicaoDados.DadosAll, true);
}
VerificaControleLeitura();
}
AtualizaComponentesIniciados();
}
private void VerificaControleLeitura()
{
if (Variaveis.Fechando) return;
foreach (var bico in BicosPulverizadores.Where(x => x.Comandar && x.Inicializado))
{
Estado comandoEstado = bico.ComandoAtuar ? Estado.Ligado : Estado.Desligado;
if (comandoEstado != bico._EstadoLeitura || bico.AnguloControle != bico._AnguloBicoLeitura)
{
GeneralJoystick.EnviarComandoAtuador(bico.Componente, bico.ID, Status: bico.ComandoAtuar, Valor: bico.AnguloControle);
}
}
foreach (var bomba in BombasPressurizadoras.Where(x => x.Comandar && x.Inicializado))
{
// Desligar a bomba caso nao tenha herbicida suficiente no recipiente
if (!ContadorCarga.PulverizadorLiberado)
{
if (bomba._EstadoLeitura == Estado.Ligado)
{
GeneralJoystick.EnviarComandoAtuador(bomba.Componente, bomba.ID, Status: false);
}
}
else
{
if (Variaveis.OperacaoEmAndamento.Iniciado && Variaveis.OperacaoEmAndamento.Parametros.Controle.PulverizadorAutomatico)
{
if (bomba._EstadoLeitura == Estado.Desligado)
{
GeneralJoystick.EnviarComandoAtuador(bomba.Componente, bomba.ID, Status: true);
}
}
Estado comandoEstado = bomba.ComandoAtuar ? Estado.Ligado : Estado.Desligado;
double pressaoSp = Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha;
if (comandoEstado != bomba._EstadoLeitura || pressaoSp != bomba._PressaoSP)
{
GeneralJoystick.EnviarComandoAtuador(bomba.Componente, bomba.ID, Status: bomba.ComandoAtuar, Valor: pressaoSp);
}
}
}
}
private void AtualizaComponentesIniciados()
{
DateTime Agora = DateTime.Now;
bool modConectado = DadosLeitura.UltimoComandoRespondido.AddMilliseconds(TempoLimiteConexao) > Agora;
if (!modConectado && DadosLeitura.Conectado)
{
DadosLeitura.Conectado = false;
}
foreach (var bomba in BombasPressurizadoras.Where(x => x.Inicializado && x.UltimaLeitura.AddMilliseconds(TempoLimiteConexao) < Agora))
{
bomba.Inicializado = false;
}
foreach (var bico in BicosPulverizadores.Where(x => x.Inicializado && x.UltimaLeitura.AddMilliseconds(TempoLimiteConexao) < Agora))
{
bico.Inicializado = false;
}
foreach (var sensor in Sensores.Where(x => x.Inicializado && x.UltimaLeitura.AddMilliseconds(TempoLimiteConexao) < Agora))
{
sensor.Inicializado = false;
}
}
public async Task<bool> RealizarTestesIniciais(bool forcar)
{
var calAtu = Variaveis.OperacaoEmAndamento.ModsCalibragem[T_Code.Atu];
// Ligar bomba
foreach (var Bomba in BombasPressurizadoras.Where(x => x.Comandar))
{
calAtu.TryGetValue(Bomba.ID, out var cal);
if (!forcar && cal && calAtu[Bomba.ID]) continue;
calAtu[Bomba.ID] = false;
// Sem herbicida no reservatorio, bomba nao precisa ligar
if (!Bomba.Inicializado || ContadorCarga.PercentualAtual < VariaveisEquipamento.PercentualReservatorioMin)
{
break;
}
GeneralJoystick.EnviarComandoAtuador(Bomba.Componente, Bomba.ID, Status: true);
await FuncoesGlobais.AguardarCondicaoAsync(Bomba.AtingiuPressao, 3000, 200);
bool BombaOk = Bomba.AtingiuPressao();
if (BombaOk)
{
calAtu[Bomba.ID] = true;
}
else
{
foreach (var Bico in BicosPulverizadores.Where(x => x.Comandar && x.Inicializado))
{
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: true);
}
await Task.Delay(3000);
foreach (var Bico in BicosPulverizadores.Where(x => x.Comandar && x.Inicializado))
{
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: false);
}
await FuncoesGlobais.AguardarCondicaoAsync(Bomba.AtingiuPressao, 4000, 200);
BombaOk = Bomba.AtingiuPressao();
if (BombaOk)
{
calAtu[Bomba.ID] = true;
}
}
}
// Testar os bicos
foreach (var Bico in BicosPulverizadores.Where(x => x.Comandar))
{
calAtu.TryGetValue(Bico.ID, out var cal);
if (!forcar && cal && calAtu[Bico.ID]) continue;
calAtu[Bico.ID] = false;
if (Bico.Inicializado)
{
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: true);
await Task.Delay(200);
GeneralJoystick.EnviarComandoAtuador(Bico.Componente, Bico.ID, Status: false);
calAtu[Bico.ID] = true;
}
}
await Task.Delay(1000);
bool sucesso = calAtu.All(x => x.Value);
return sucesso;
}
public List<Task> RealizarCalibragemAsync(bool forcar)
{
List<Task> tasks = new List<Task>();
var DispAtu = Variaveis.OperacaoEmAndamento.DispAtu;
if (DispAtu != null)
{
tasks.Add(DispAtu.Dados.RealizarTestesIniciais(forcar).ContinueWith(t => {
if (t.IsFaulted)
Console.WriteLine($"Erro no referenciamento do módulo {Modulo_ID}: {t.Exception?.InnerException?.Message}");
if (t.IsCompleted)
{
var calAtu = Variaveis.OperacaoEmAndamento.ModsCalibragem[T_Code.Atu];
var v = calAtu.Select(x => $"{x.Key} " + (x.Value ? "Sim" : "Não"));
Console.WriteLine($"Task de referenciamento do módulo {Modulo_ID} finalizada: " + string.Join(", ", v));
}
}));
}
return tasks;
}
public void RequisitarDadosSensoresExternos()
{
if (SensorAnguloBarraEsquerda != null)
RequisitarDadosModulo(SensorAnguloBarraEsquerda.ID_Num, CanMessagePosicaoDados.Dados1, CanMessagePosicaoDados.Dados1, true);
if (SensorAnguloBarraDireita != null)
RequisitarDadosModulo(SensorAnguloBarraDireita.ID_Num, CanMessagePosicaoDados.Dados1, CanMessagePosicaoDados.Dados1, true);
if (SensorAnguloBarraAltura != null)
RequisitarDadosModulo(SensorAnguloBarraAltura.ID_Num, CanMessagePosicaoDados.Dados1, CanMessagePosicaoDados.Dados1, true);
}
public void AtualizarDadosSensoresExternos(S_Code componente, int id_num)
{
switch (componente)
{
case S_Code.sMAG:
{
var sensor = Sensores.FirstOrDefault(x => x.ID_Num == id_num);
if (sensor == null) return;
double angulo = sensor.ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.LeituraAngulo)?.atual?.valor ?? double.NaN;
if (sensor.ID_Num == SensorAnguloBarraEsquerda.ID_Num)
AnguloBarraEsquerda = angulo;
else if (sensor.ID_Num == SensorAnguloBarraDireita.ID_Num)
AnguloBarraDireita = angulo;
else if (sensor.ID_Num == SensorAnguloBarraAltura.ID_Num)
AnguloBarraAltura = angulo;
}
break;
case S_Code.sBIC:
{
var bico = BicosPulverizadores.FirstOrDefault(x => x.ID_Num == id_num);
if (bico == null) return;
if (bico._estadoLeituraAnterior == Estado.Desligado && bico._EstadoLeitura == Estado.Ligado)
{
bico.Atuacoes++;
bico._estadoLeituraAnterior = bico._EstadoLeitura;
}
}
break;
}
}
}
public class AtuadorBicoModel
{
public string ID { get; set; }
public int ID_Num { get; set; }
public int Posicao { get; set; }
public S_Code Componente { get; set; } = S_Code.sBIC;
public bool AtuacaoNivelAlto { get; set; }
public double AnguloControle { get; set; }
public double AnguloAbertura { get; set; }
public bool ComandoAtuar { get; set; }
public bool Comandar { get; set; }
public bool Mandatorio { get; set; }
public bool Liberado
{
get
{
return !Mandatorio || (Comandar && Inicializado);
}
}
public List<FuncoesPinout> Funcoes { get; set; }
public double VazaoMediaMLs { get; set; }
public double VazaoInstantaneaMLs { get; set; }
public double VolumeVazadoML { get; set; }
public bool Inicializado { get; set; }
public DateTime UltimaLeitura
{
get
{
return ValoresLeituras.OrderByDescending(x => x.atual.lidoEm).FirstOrDefault()?.atual?.lidoEm ?? DateTime.MinValue;
}
}
public List<SensorValoresLeituraModel> ValoresLeituras { get; set; }
public Estado _estadoLeituraAnterior { get; set; } = Estado.Desligado;
public Estado _EstadoLeitura => (Estado)(ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.EstadoLeitura)?.atual?.valor ?? 0);
public int _AnguloBicoLeitura => ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.LeituraAngulo)?.atual?.valor ?? 0;
public (CanMessagePosicaoDados, byte[]) ProtocoloConfiguracao(PinoutDataModel pinout, bool conectar)
{
var pino = pinout.Pinos.FirstOrDefault(p => p.ComponenteID == ID);
var data = new byte[]
{
(byte)Componente,
(byte)(conectar ? 1 : 0),
(byte)(AtuacaoNivelAlto ? 1 : 0),
0x00, //(byte)Servo_ID_Num,
(byte)(pino?.Barramento ?? TiposBarramentos.CONTROLADOR),
(byte)(pino?.Pino ?? PinoutUtils.PINO_INVALIDO)
};
return (CanMessagePosicaoDados.Config1, data);
}
public (int, CanMessagePosicaoDados, byte[]) ProtocoloComando()
{
return (
ID_Num,
CanMessagePosicaoDados.Command1,
new byte[]
{
(byte)(ComandoAtuar ? Estado.Ligado : Estado.Desligado),
(byte)AnguloControle
}
);
}
public List<List<double[]>> TrechosAtuado { get; set; } = new List<List<double[]>>();
public int Atuacoes { get; set; } = 0;
public double TempoAtuado { get; set; } = 0;
public AtuadorBicoModel Clone()
{
AtuadorBicoModel clone = new AtuadorBicoModel()
{
Comandar = Comandar,
AtuacaoNivelAlto = AtuacaoNivelAlto,
Atuacoes = Atuacoes,
ComandoAtuar = ComandoAtuar,
AnguloAbertura = AnguloAbertura,
AnguloControle = AnguloControle,
Inicializado = Inicializado,
Mandatorio = Mandatorio,
Componente = Componente,
Funcoes = Funcoes,
ID = ID,
ID_Num = ID_Num,
VolumeVazadoML = VolumeVazadoML,
TrechosAtuado = TrechosAtuado,
VazaoInstantaneaMLs = VazaoInstantaneaMLs,
_estadoLeituraAnterior = _estadoLeituraAnterior,
ValoresLeituras = new List<SensorValoresLeituraModel>(ValoresLeituras),
VazaoMediaMLs = VazaoMediaMLs,
Posicao = Posicao,
TempoAtuado = TempoAtuado,
};
return clone;
}
}
public class AtuadorBombaModel
{
public string ID { get; set; }
public int ID_Num { get; set; }
public bool ComandoAtuar { get; set; }
public S_Code Componente { get; set; } = S_Code.sBMB;
public int Sensor_ID_Num { get; set; }
public bool Comandar { get; set; }
public bool Mandatorio { get; set; }
public bool Liberado
{
get
{
return !Mandatorio || (Comandar && Inicializado);
}
}
public List<FuncoesPinout> Funcoes { get; set; }
public bool Inicializado { get; set; }
public DateTime UltimaLeitura
{
get
{
return ValoresLeituras.OrderByDescending(x => x.atual.lidoEm).FirstOrDefault()?.atual?.lidoEm ?? DateTime.MinValue;
}
}
public List<SensorValoresLeituraModel> ValoresLeituras { get; set; }
public Estado _EstadoLeitura => ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.EstadoLeitura)?.atual?.valor ?? Estado.Desligado;
public double _Potencia => ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.Potencia)?.atual?.valor ?? 0;
public bool _BombaLigada => _Potencia > 0;
public double _PressaoSP => ValoresLeituras?.FirstOrDefault(x => x.funcao == FuncoesPinout.PressaoSP)?.atual?.valor ?? 0;
public List<(CanMessagePosicaoDados, byte[])> ProtocoloConfiguracao(PinoutDataModel pinout, bool conectar)
{
List<byte> config1 = new List<byte>()
{
(byte)Componente,
(byte)(conectar ? 1 : 0),
(byte)Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha,
(byte)Sensor_ID_Num,
(byte)(pinout.Pinos.FirstOrDefault(x => x.ComponenteID == ID && x.Funcao == FuncoesPinout.ENA)?.Pino ?? PinoutUtils.PINO_INVALIDO),
(byte)(pinout.Pinos.FirstOrDefault(x => x.Funcao == FuncoesPinout.CS)?.Pino ?? PinoutUtils.PINO_INVALIDO),
};
List<byte> config2 = new List<byte>()
{
(byte)Componente,
(byte)(pinout.Pinos.FirstOrDefault(x => x.Funcao == FuncoesPinout.INA)?.Pino ?? PinoutUtils.PINO_INVALIDO),
(byte)(pinout.Pinos.FirstOrDefault(x => x.Funcao == FuncoesPinout.INB)?.Pino ?? PinoutUtils.PINO_INVALIDO),
(byte)(pinout.Pinos.FirstOrDefault(x => x.Funcao == FuncoesPinout.PWM)?.Pino ?? PinoutUtils.PINO_INVALIDO),
};
return new List<(CanMessagePosicaoDados, byte[])>()
{
(CanMessagePosicaoDados.Config1, config1.ToArray()),
(CanMessagePosicaoDados.Config2, config2.ToArray()),
};
}
public (int, CanMessagePosicaoDados, byte[]) ProtocoloComando()
{
return (
ID_Num,
CanMessagePosicaoDados.Command1,
new byte[]
{
(byte)(ComandoAtuar ? Estado.Ligado : Estado.Desligado),
(byte)(Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha)
}
);
}
public bool AtingiuPressao() => Variaveis.OperacaoEmAndamento.DispAtu.Dados.PressaoLinha >= Variaveis.OperacaoEmAndamento.Parametros.Controle.AtuPressaoLinha;
}
public class CalibragemVazao
{
public bool Calibrando { get; set; } = false;
public DateTime IniciadoEm { get; set; }
public double TempoCalibragem { get; set; } = 10000.0;
public double VolumeComputado { get; set; } = 0;
public List<double> PressoesAferidas { get; set; } = new List<double>();
public double MassaInicial { get; set; } = 0.0;
public double MassaFinal { get; set; } = 0.0;
public int QtdPulsos { get; set; } = 0;
public double Periodo { get; set; } = 0.0;
public double PressaoMedia
{
get
{
return PressoesAferidas.Average();
}
}
public double MassaPerdida
{
get
{
return (MassaInicial - MassaFinal) * 1000;
}
}
public double Q
{
get
{
return (double)VolumeComputado / (double)Periodo;
}
}
public double VolumeCalculado { get; set; } = 0.0;
public double d
{
get
{
return (double)MassaPerdida / (double)VolumeComputado;
}
}
public double x
{
get
{
return (double)VolumeComputado / (double)QtdPulsos;
}
}
public double Q2
{
get
{
return (double)x * (double)QtdPulsos;
}
}
}
public class AtuadorFuncoesGerais
{
public static void DesenharAtuadores(object sender, PaintEventArgs e, List<AtuBicoSensoriamentoLogModel> Bicos, bool BombaLigada, double PressaoLinha, double TempoBombaAtuada)
{
Panel myPanel = (Panel)sender;
// Habilita o double buffering temporariamente
myPanel.GetType().GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
?.SetValue(myPanel, true, null);
Graphics g = e.Graphics;
int QuantidadeBicos = Bicos.Count;
int panelWidth = myPanel.Width;
int circleDiameter = 40; // Diâmetro dos círculos grandes
int smallCircleDiameter = circleDiameter / 2; // Diâmetro dos círculos pequenos
int spaceBetweenCircles = (panelWidth - (circleDiameter * QuantidadeBicos)) / (QuantidadeBicos + 1);
int yPosition = (myPanel.Height - circleDiameter) / 3; // Centralizado verticalmente
for (int i = 0; i < QuantidadeBicos; i++)
{
int xPosition = spaceBetweenCircles + (i * (circleDiameter + spaceBetweenCircles));
// Desenha o contorno do círculo
g.DrawEllipse(Pens.Red, xPosition, yPosition, circleDiameter, circleDiameter);
// Preenche o círculo se o bico estiver aceso
if (Bicos[i].Atuado)
{
g.FillEllipse(Brushes.Red, xPosition, yPosition, circleDiameter, circleDiameter);
}
// Número do bico
string bicoNumero = Bicos[i].ID;
Font font = new Font("Arial", 10, FontStyle.Bold);
SizeF stringSize = g.MeasureString(bicoNumero, font);
float textXPosition = xPosition + (circleDiameter - stringSize.Width) / 2;
float textYPosition = yPosition + (circleDiameter - stringSize.Height) / 2;
g.DrawString(bicoNumero, font, Brushes.Black, textXPosition, textYPosition);
// Média de vazão (dentro do círculo, abaixo do número do bico)
string mediaFluxoTexto = $"{Bicos[i].MediaNivelFluxo:0.0} mL/s";
Font fontMediaFluxo = new Font("Arial", 6, FontStyle.Regular);
SizeF stringSizeMediaFluxo = g.MeasureString(mediaFluxoTexto, fontMediaFluxo);
float textXPositionMediaFluxo = xPosition + circleDiameter + 3;
float textYPositionMediaFluxo = textYPosition + 3; // Abaixo do número do bico
g.DrawString(mediaFluxoTexto, fontMediaFluxo, Brushes.Gray, textXPositionMediaFluxo, textYPositionMediaFluxo);
// Quantidade de atuações (abaixo do círculo)
string bicoAtuacao = Bicos[i].Atuacoes.ToString("000");
Font fontA = new Font("Arial", 8, FontStyle.Bold);
SizeF stringSizeA = g.MeasureString(bicoAtuacao, fontA);
float textXPositionA = xPosition + (circleDiameter - stringSizeA.Width) / 2;
float textYPositionA = circleDiameter + yPosition + 5;
g.DrawString(bicoAtuacao, fontA, Brushes.Black, textXPositionA, textYPositionA);
// Tempo atuado em segundos (acima do círculo)
string tempoAtuadoSegundos = $"{(Bicos[i].TempoAtuado / 1000.0):0.0}s";
Font fontTempo = new Font("Arial", 7, FontStyle.Regular);
SizeF stringSizeTempo = g.MeasureString(tempoAtuadoSegundos, fontTempo);
float textXPositionTempo = xPosition + (circleDiameter - stringSizeTempo.Width) / 2;
float textYPositionTempo = yPosition - stringSizeTempo.Height - 3;
g.DrawString(tempoAtuadoSegundos, fontTempo, Brushes.Gray, textXPositionTempo, textYPositionTempo);
// Círculo pequeno de status
int smallCircleXPosition = Convert.ToInt32(xPosition + circleDiameter - smallCircleDiameter / 1.3);
int smallCircleYPosition = Convert.ToInt32(yPosition + circleDiameter - smallCircleDiameter / 1.3);
Pen smallCirclePen = Pens.Blue;
Brush smallCircleBrush = Bicos[i].StatusBico ? Brushes.Blue : Brushes.Transparent;
g.DrawEllipse(smallCirclePen, smallCircleXPosition, smallCircleYPosition, smallCircleDiameter, smallCircleDiameter);
g.FillEllipse(smallCircleBrush, smallCircleXPosition, smallCircleYPosition, smallCircleDiameter, smallCircleDiameter);
}
// Representação da bomba hidráulica no canto inferior direito
int bombaSize = 20; // Tamanho do círculo principal da bomba
int bombaXPosition = myPanel.Width - bombaSize - 20; // 20px de margem do canto direito
int bombaYPosition = myPanel.Height - bombaSize - 20; // 20px de margem do canto inferior
// Desenha o corpo da bomba (um círculo)
g.DrawEllipse(Pens.Black, bombaXPosition, bombaYPosition, bombaSize, bombaSize);
g.FillEllipse(BombaLigada ? Brushes.Green : Brushes.Gray, bombaXPosition, bombaYPosition, bombaSize, bombaSize);
// Define o centro do círculo para ajudar no desenho das hélices
int centerX = bombaXPosition + bombaSize / 2;
int centerY = bombaYPosition + bombaSize / 2;
int heliceSize = bombaSize - 6; // Ajuste para manter as hélices dentro do círculo
// Desenha hélices no formato de arcos ou semi-círculos dentro do círculo
Pen helicePen = new Pen(Color.Black, 1);
g.DrawArc(helicePen, centerX - heliceSize / 2, centerY - heliceSize / 2, heliceSize, heliceSize, 45, 180);
g.DrawArc(helicePen, centerX - heliceSize / 2, centerY - heliceSize / 2, heliceSize, heliceSize, 225, 180);
// Desenha o "cano" da bomba (retângulo)
int tuboLargura = 5;
int tuboAltura = 15;
int tuboXPosition = bombaXPosition + bombaSize / 2 - tuboLargura / 2;
int tuboYPosition = bombaYPosition - tuboAltura;
g.FillRectangle(Brushes.Black, tuboXPosition, tuboYPosition, tuboLargura, tuboAltura);
// Texto para o tempo de atuação acima da bomba
string tempoAtuacaoTexto = $"{(TempoBombaAtuada / 1000.0):0.0}s";
Font fontTempoAtuacao = new Font("Arial", 6, FontStyle.Regular);
SizeF stringSizeTempoAtuacao = g.MeasureString(tempoAtuacaoTexto, fontTempoAtuacao);
float textXPositionTempoAtuacao = bombaXPosition + bombaSize / 2 - stringSizeTempoAtuacao.Width / 2;
float textYPositionTempoAtuacao = bombaYPosition - stringSizeTempoAtuacao.Height - 16; // 2px acima do círculo
g.DrawString(tempoAtuacaoTexto, fontTempoAtuacao, Brushes.Gray, textXPositionTempoAtuacao, textYPositionTempoAtuacao);
// Texto pequeno para a pressão ao lado da bomba
string pressaoTexto = $"{PressaoLinha:0.0} psi";
Font fontPressao = new Font("Arial", 6, FontStyle.Regular);
SizeF stringSizePressao = g.MeasureString(pressaoTexto, fontPressao);
float textXPositionPressao = bombaXPosition + bombaSize / 2 - stringSizePressao.Width / 2;
float textYPositionPressao = bombaYPosition + bombaSize + 2; // 2px abaixo do círculo
g.DrawString(pressaoTexto, fontPressao, Brushes.Black, textXPositionPressao, textYPositionPressao);
}
}
public class AtuadorMensagemJsonModel
{
public DateTime Momento { get; set; }
public bool Conectado { get; set; }
public T_Code Dispositivo { get; set; }
public int Versao { get; set; }
public string Mod_ID { get; set; }
public int QualidadeWifi { get; set; }
public int LatenciaLoop { get; set; }
public List<AtuadorBicoModel> BicosPulverizadores { get; set; }
public List<AtuadorBombaModel> BombasPressurizadoras { get; set; }
public List<SensorModel> Sensores { get; set; }
public List<SensorServoModel> Servos { get; set; }
public DateTime UltimoComandoRespondido { get; set; }
}
public class AtuHandler : ICanMessageHandler
{
public T_Code Dispositivo { get; set; } = Variaveis.OperacaoEmAndamento.DispAtu?.Dados?.Dispositivo ?? T_Code.Atu;
private double ConverterByteParaDouble(byte[] data, int idx)
{
int raw = (data[idx] << 8) | data[idx + 1];
short _short = (short)raw;
double _value = _short / 100.0;
return _value;
}
private ushort Converter2BytesU16(byte[] data, int idx)
{
return (ushort)((data[idx] << 8) | data[idx + 1]);
}
private double ConverterAngle100(byte[] data, int idx)
{
ushort v = (ushort)((data[idx] << 8) | data[idx + 1]);
return v / 100.0;
}
private S_Code DetectarComponenteDoID_Num(int ID_Num)
{
if (ID_Num == Variaveis.ID_Num_sMOD) return S_Code.sMOD;
if (ID_Num == Variaveis.ID_Num_sTOD) return S_Code.sTOD;
var Disp = Variaveis.OperacaoEmAndamento.DispAtu.Dados;
var sensor = Disp.Sensores.FirstOrDefault(x => x.ID_Num == ID_Num);
var bico = Disp.BicosPulverizadores.FirstOrDefault(x => x.ID_Num == ID_Num);
var bomba = Disp.BombasPressurizadoras.FirstOrDefault(x => x.ID_Num == ID_Num);
return sensor?.Componente ?? bico?.Componente ?? bomba?.Componente ?? S_Code.sVZO;
}
private void AtualizarDados(int CompID_Num, CanMessagePosicaoDados Posicao, S_Code componente, List<dynamic> Parametros)
{
var Dados = Variaveis.OperacaoEmAndamento.DispAtu.Dados;
var sensor = Dados.Sensores.FirstOrDefault(x => x.ID_Num == CompID_Num);
var bomba = Dados.BombasPressurizadoras.FirstOrDefault(x => x.ID_Num == CompID_Num);
var bico = Dados.BicosPulverizadores.FirstOrDefault(x => x.ID_Num == CompID_Num);
List<SensorValoresLeituraModel> valoresLeituras = new List<SensorValoresLeituraModel>();
if (sensor != null) valoresLeituras = sensor.ValoresLeituras;
else if (bomba != null) valoresLeituras = bomba.ValoresLeituras;
else if (bico != null) valoresLeituras = bico.ValoresLeituras;
for (int i = 0; i < valoresLeituras.Where(x => x.posicao == Posicao).Count(); i++)
{
try
{
var valor = valoresLeituras.FirstOrDefault(x => x.posicao == Posicao && x.indice == i);
if (valor != null)
{
var v = Parametros.Count > i ? (Parametros[i]) : -1;
if (v is sbyte || v is byte || v is short || v is ushort || v is int || v is uint || v is long || v is ulong || v is float || v is double || v is decimal)
{
v += valor.offset;
valor.atual.valor = v;
}
else
{
valor.atual.valor = v;
}
if (Posicao == CanMessagePosicaoDados.Status)
{
if (sensor != null)
sensor.Inicializado = v;
if (bomba != null)
bomba.Inicializado = v;
if (bico != null)
bico.Inicializado = v;
}
}
}
catch (Exception ex)
{
}
}
}
public void ProcessarMensagem(CanMessage mensagem)
{
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)mensagem.DataRx[0];
byte ID_Num = mensagem.DataRx[1];
var data = mensagem.DataRx;
S_Code componente = DetectarComponenteDoID_Num(ID_Num);
var Dados = Variaveis.OperacaoEmAndamento.DispAtu.Dados;
//Console.WriteLine($"[ATU] Componente={componente.ToString()}, ID_Num={ID_Num}, Data={FuncoesGlobais.ConverterComandoBytesParaTexto(data)}");
switch (componente)
{
case S_Code.sMOD:
{
switch (posicao)
{
case CanMessagePosicaoDados.Status:
{
T_Code tipoModulo = (T_Code)data[2];
bool conectado = data[3] == 1;
int versao = data[4];
//Console.WriteLine($"[ATU] Resposta {ID_Num} recebida. Tipo={tipoModulo}, Conectado={conectado}, Versão={versao}");
Dados.DadosLeitura.Conectado = conectado;
Dados.DadosLeitura.Dispositivo = tipoModulo;
Dados.DadosLeitura.Versao = versao;
break;
}
}
break;
}
case S_Code.sTOD:
{
switch (posicao)
{
case CanMessagePosicaoDados.DadosAll:
{
double latenciaLoop = ConverterByteParaDouble(data, 2) * 100;
Dados.DadosLeitura.LatenciaLoop = Convert.ToInt32(latenciaLoop);
//Console.WriteLine($"[ATU] Ciclo concluido. Latencia de loop: {latenciaLoop} ms");
break;
}
}
break;
}
case S_Code.sBIC:
{
switch (posicao)
{
case CanMessagePosicaoDados.Status:
{
bool Iniciado = data[2] == 1;
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { Iniciado });
break;
}
case CanMessagePosicaoDados.Dados1:
{
Estado estado = (Estado)data[2];
int angulo = data[3];
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { estado, angulo });
break;
}
}
break;
}
case S_Code.sFLX:
{
switch (posicao)
{
case CanMessagePosicaoDados.Status:
{
bool Iniciado = data[2] == 1;
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { Iniciado });
break;
}
case CanMessagePosicaoDados.Dados1:
{
int qtdPulsos = data[2];
ushort periodo = Converter2BytesU16(data, 3);
double vazao = ConverterByteParaDouble(data, 5);
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { qtdPulsos, periodo, vazao });
break;
}
}
break;
}
case S_Code.sMAS:
{
switch (posicao)
{
case CanMessagePosicaoDados.Status:
{
bool Iniciado = data[2] == 1;
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { Iniciado });
break;
}
case CanMessagePosicaoDados.Dados1:
{
double raw = ConverterByteParaDouble(data, 2);
double massa = ConverterByteParaDouble(data, 4);
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { raw, massa });
break;
}
}
break;
}
case S_Code.sPRS:
{
switch (posicao)
{
case CanMessagePosicaoDados.Status:
{
bool Iniciado = data[2] == 1;
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { Iniciado });
break;
}
case CanMessagePosicaoDados.Dados1:
{
ushort raw = Converter2BytesU16(data, 2);
double pressao = ConverterByteParaDouble(data, 4);
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { raw, pressao });
break;
}
}
break;
}
case S_Code.sBMB:
{
switch (posicao)
{
case CanMessagePosicaoDados.Status:
{
bool Iniciado = data[2] == 1;
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { Iniciado });
break;
}
case CanMessagePosicaoDados.Dados1:
{
Estado estado = (Estado)data[2];
int percentualPotencia = data[3];
int pressaoSp = data[4];
int pressaoAtual = data[5];
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { estado, percentualPotencia, pressaoSp, pressaoAtual });
break;
}
}
break;
}
case S_Code.sMAG:
{
switch (posicao)
{
case CanMessagePosicaoDados.Status:
{
bool Iniciado = data[2] == 1;
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { Iniciado });
break;
}
case CanMessagePosicaoDados.Dados1:
{
ushort raw = Converter2BytesU16(data, 2);
double angle = ConverterAngle100(data, 4);
AtualizarDados(ID_Num, posicao, componente, new List<dynamic>() { raw, angle });
//Console.WriteLine($"[sMAG] {ID_Num}, Raw: {raw}, Angulo: {angle}");
break;
}
}
break;
}
}
Dados.DadosLeitura.UltimoComandoRespondido = DateTime.Now;
Variaveis.OperacaoEmAndamento.DispAtu.Dados.AtualizarDadosSensoresExternos(componente, ID_Num);
}
}
}