agrobot_base/Firmware/Modulos/SinaleiroModel_v2.h

212 lines
5.1 KiB
C++

#include "SerialService.h"
#include "Pinout.h"
#include "Utils.h"
#include "IComponenteCAN.h"
#ifndef SinaleiroModel
#define SinaleiroModel
class Sinaleiro_v2 : public ComponenteCAN {
public:
String ID;
uint8_t ID_Num = 0;
PinoModel _pino;
bool Iniciado = false;
int _StatusID = 0;
StatusLED _Status = Apagado;
StatusLED _StatusLeitura = Apagado;
int _Qtd = 0;
int _TempoMs = 0;
int _PausaMs = 0;
// controle interno (não era exposto antes)
bool _estadoAtual = false;
int _piscadasAtual = 0;
bool _emPausa = false;
unsigned long _lastMillis = 0;
// CAN apenas publica um comando pendente; LedTask e a unica dona do
// estado ativo da maquina de piscada e da escrita fisica no GPIO.
portMUX_TYPE _stateLock = portMUX_INITIALIZER_UNLOCKED;
bool _comandoPendente = false;
int _pendStatusID = 0;
StatusLED _pendStatus = Apagado;
int _pendQtd = 0;
int _pendTempoMs = 0;
int _pendPausaMs = 0;
Sinaleiro_v2() {}
Sinaleiro_v2(
const String& id,
uint8_t idNum,
int pino,
TiposBarramentos tipoBarramento
) :
ID(id),
ID_Num(idNum),
_pino(PinoModel(pino, tipoBarramento, _OUTPUT))
{}
void Inicializar() {
if (Iniciado) return;
if (_pino.Definido()) {
_pino.Conectar(false);
Iniciado = true;
}
}
void AtualizarEstado(unsigned long agora) {
if (!Iniciado) return;
bool aplicarComando = false;
int novoStatusID = 0;
StatusLED novoStatus = Apagado;
int novaQtd = 0;
int novoTempoMs = 0;
int novaPausaMs = 0;
portENTER_CRITICAL(&_stateLock);
if (_comandoPendente) {
aplicarComando = true;
novoStatusID = _pendStatusID;
novoStatus = _pendStatus;
novaQtd = _pendQtd;
novoTempoMs = _pendTempoMs;
novaPausaMs = _pendPausaMs;
_comandoPendente = false;
}
portEXIT_CRITICAL(&_stateLock);
if (aplicarComando) {
portENTER_CRITICAL(&_stateLock);
_StatusID = novoStatusID;
portEXIT_CRITICAL(&_stateLock);
_Status = novoStatus;
_Qtd = novaQtd;
_TempoMs = novoTempoMs;
_PausaMs = novaPausaMs;
_piscadasAtual = 0;
_emPausa = false;
_estadoAtual = false;
_lastMillis = agora;
}
if (_Status != StatusLED::Piscando) {
_pino.set(_Status == StatusLED::Aceso);
portENTER_CRITICAL(&_stateLock);
_StatusLeitura = _Status;
portEXIT_CRITICAL(&_stateLock);
return;
}
// modo piscando
if (_piscadasAtual < _Qtd) {
if (!_emPausa && (agora - _lastMillis >= _TempoMs)) {
_estadoAtual = !_estadoAtual;
_pino.set(_estadoAtual);
_lastMillis = agora;
if (!_estadoAtual) {
_piscadasAtual++;
}
}
}
else {
// pausa
if (!_emPausa) {
_pino.set(false);
_emPausa = true;
_lastMillis = agora;
}
if (agora - _lastMillis >= _PausaMs) {
_emPausa = false;
_piscadasAtual = 0;
_lastMillis = agora;
}
}
portENTER_CRITICAL(&_stateLock);
_StatusLeitura = StatusLED::Piscando;
portEXIT_CRITICAL(&_stateLock);
}
void ProcessarComando(std::vector<uint8_t>& data) {
if (!Iniciado) return;
if (data.size() < 7) return;
CanMessagePosicaoDados posicao = (CanMessagePosicaoDados)data[0];
uint8_t idNum = data[1];
if (idNum != ID_Num) return;
if (posicao == CanMessagePosicaoDados::Command1) {
const int statusID = data[2];
const StatusLED status = (StatusLED)data[3];
const int qtd = data[4];
const int tempoMs = (uint16_t)data[5] * 10;
const int pausaMs = (uint16_t)data[6] * 10;
portENTER_CRITICAL(&_stateLock);
_pendStatusID = statusID;
_pendStatus = status;
_pendQtd = qtd;
_pendTempoMs = tempoMs;
_pendPausaMs = pausaMs;
_comandoPendente = true;
portEXIT_CRITICAL(&_stateLock);
}
}
void RequisitarDados() {}
std::vector<uint8_t> MontarMensagemCAN(CanMessagePosicaoDados posicao) {
std::vector<uint8_t> data;
data.push_back((uint8_t)posicao);
data.push_back(ID_Num);
switch (posicao) {
case Status:
data.push_back(Iniciado ? 1 : 0);
break;
case Dados1: {
int statusID;
StatusLED statusLeitura;
portENTER_CRITICAL(&_stateLock);
statusID = _StatusID;
statusLeitura = _StatusLeitura;
portEXIT_CRITICAL(&_stateLock);
data.push_back(statusID);
data.push_back(statusLeitura);
break;
}
}
return data;
}
};
class LedManager {
public:
std::vector<Sinaleiro_v2*> leds;
void Adicionar(Sinaleiro_v2* led) {
leds.push_back(led);
}
void Atualizar() {
unsigned long agora = millis();
for (auto* led : leds) {
if (led != nullptr && led->Iniciado) {
led->AtualizarEstado(agora);
}
}
}
};
#endif