mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-07-18 12:04:03 -04:00
424 lines
14 KiB
C++
424 lines
14 KiB
C++
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include <AntennaMessage.hpp>
|
|
#include <AntennaServiceAPI.hpp>
|
|
#include <ServiceAntenna.hpp>
|
|
#include <ServiceState.hpp>
|
|
|
|
#include <at/response.hpp>
|
|
#include <EventStore.hpp>
|
|
#include <log/log.hpp>
|
|
#include <MessageType.hpp>
|
|
#include <Timers/TimerFactory.hpp>
|
|
#include <projdefs.h>
|
|
#include <service-cellular/CellularMessage.hpp>
|
|
#include <service-cellular/CellularServiceAPI.hpp>
|
|
#include <service-cellular/State.hpp>
|
|
#include <ticks.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace
|
|
{
|
|
constexpr auto antennaServiceStackSize = 1024 * 2;
|
|
}
|
|
|
|
namespace antenna
|
|
{
|
|
const char *c_str(antenna::State state)
|
|
{
|
|
switch (state) {
|
|
case antenna::State::none:
|
|
return "none";
|
|
case antenna::State::init:
|
|
return "init";
|
|
case antenna::State::connectionStatus:
|
|
return "connectionStatus";
|
|
case antenna::State::switchAntenna:
|
|
return "switchAntenna";
|
|
case antenna::State::signalCheck:
|
|
return "signalCheck";
|
|
case antenna::State::bandCheck:
|
|
return "bandCheck";
|
|
case antenna::State::idle:
|
|
return "idle";
|
|
case antenna::State::csqChange:
|
|
return "csqChange";
|
|
case antenna::State::locked:
|
|
return "locked";
|
|
}
|
|
return "";
|
|
}
|
|
} // namespace antenna
|
|
|
|
|
|
ServiceAntenna::ServiceAntenna()
|
|
: sys::Service(service::name::antenna, "", antennaServiceStackSize, sys::ServicePriority::Idle),
|
|
state{std::make_unique<state::State<antenna::State>>(this)}, phoneModeObserver{
|
|
std::make_unique<sys::phone_modes::Observer>()}
|
|
{
|
|
timer = sys::TimerFactory::createPeriodicTimer(
|
|
this, "Antenna", std::chrono::seconds{5}, [this](sys::Timer & /*timer*/) {
|
|
timer.stop();
|
|
auto stateToSet = state->get();
|
|
if (state->timeoutOccured(cpp_freertos::Ticks::GetTicks())) {
|
|
LOG_WARN("State [ %s ] timeout occurred, setting [ %s ] state",
|
|
c_str(state->get()),
|
|
c_str(state->getTimeoutState()));
|
|
stateToSet = state->getTimeoutState();
|
|
}
|
|
state->set(stateToSet);
|
|
});
|
|
bus.channels.push_back(sys::BusChannel::ServiceCellularNotifications);
|
|
bus.channels.push_back(sys::BusChannel::AntennaNotifications);
|
|
bus.channels.push_back(sys::BusChannel::PhoneModeChanges);
|
|
|
|
phoneModeObserver->connect(this);
|
|
phoneModeObserver->subscribe([this](sys::phone_modes::PhoneMode mode) {
|
|
if (mode == sys::phone_modes::PhoneMode::Offline) {
|
|
handleLockRequest(antenna::lockState::locked);
|
|
}
|
|
else {
|
|
handleLockRequest(antenna::lockState::unlocked);
|
|
}
|
|
});
|
|
}
|
|
|
|
ServiceAntenna::~ServiceAntenna()
|
|
{
|
|
}
|
|
|
|
// Invoked upon receiving data message
|
|
sys::MessagePointer ServiceAntenna::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
|
|
{
|
|
bool handled = false;
|
|
|
|
switch (msgl->messageType) {
|
|
case MessageType::StateChange:
|
|
HandleStateChange(state->get());
|
|
break;
|
|
case MessageType::AntennaChanged: {
|
|
bsp::cellular::antenna antenna;
|
|
if (CellularServiceAPI::GetAntenna(this, antenna)) {
|
|
currentAntenna = antenna;
|
|
if (state->get() == antenna::State::switchAntenna) {
|
|
LOG_INFO("Antenna switched");
|
|
|
|
state->enableStateTimeout(cpp_freertos::Ticks::GetTicks(),
|
|
pdMS_TO_TICKS(antenna::connectionStatusTimeout),
|
|
antenna::State::switchAntenna);
|
|
state->set(antenna::State::connectionStatus);
|
|
}
|
|
}
|
|
handled = true;
|
|
break;
|
|
}
|
|
case MessageType::AntennaLockService: {
|
|
auto msg = dynamic_cast<AntennaLockRequestMessage *>(msgl);
|
|
if (msg != nullptr) {
|
|
handleLockRequest(msg->request);
|
|
handled = true;
|
|
}
|
|
} break;
|
|
case MessageType::AntennaGetLockState: {
|
|
auto responseMessage = std::make_shared<AntennaLockRequestResponse>(true, serviceLocked);
|
|
return responseMessage;
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (handled) {
|
|
return std::make_shared<sys::ResponseMessage>();
|
|
}
|
|
return std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Unresolved);
|
|
}
|
|
|
|
// Invoked during initialization
|
|
sys::ReturnCodes ServiceAntenna::InitHandler()
|
|
{
|
|
registerMessageHandlers();
|
|
LOG_INFO("Initialized");
|
|
return sys::ReturnCodes::Success;
|
|
}
|
|
|
|
sys::ReturnCodes ServiceAntenna::DeinitHandler()
|
|
{
|
|
LOG_INFO("Deinitialized");
|
|
return sys::ReturnCodes::Success;
|
|
}
|
|
|
|
sys::ReturnCodes ServiceAntenna::SwitchPowerModeHandler(const sys::ServicePowerMode mode)
|
|
{
|
|
LOG_INFO("New power mode: %s", c_str(mode));
|
|
suspended = true;
|
|
|
|
switch (mode) {
|
|
case sys::ServicePowerMode::Active:
|
|
suspended = false;
|
|
break;
|
|
case sys::ServicePowerMode::SuspendToRAM:
|
|
case sys::ServicePowerMode::SuspendToNVM:
|
|
break;
|
|
}
|
|
|
|
return sys::ReturnCodes::Success;
|
|
}
|
|
|
|
void ServiceAntenna::handleLockRequest(antenna::lockState request)
|
|
{
|
|
auto currentState = state->get();
|
|
LOG_INFO("Current state: %s, requested stated: %s",
|
|
magic_enum::enum_name(currentState).data(),
|
|
magic_enum::enum_name(request).data());
|
|
|
|
if (request == antenna::lockState::locked) {
|
|
if (currentState != antenna::State::locked) {
|
|
state->set(antenna::State::locked);
|
|
serviceLocked = request;
|
|
return;
|
|
}
|
|
}
|
|
else if (request == antenna::lockState::unlocked) {
|
|
if (currentState == antenna::State::locked) {
|
|
state->set(state->getLast());
|
|
serviceLocked = request;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ServiceAntenna::HandleStateChange(antenna::State state)
|
|
{
|
|
bool ret = false;
|
|
switch (state) {
|
|
case antenna::State::none:
|
|
ret = noneStateHandler();
|
|
break;
|
|
case antenna::State::init:
|
|
ret = initStateHandler();
|
|
break;
|
|
case antenna::State::connectionStatus:
|
|
ret = connectionStatusStateHandler();
|
|
break;
|
|
case antenna::State::switchAntenna:
|
|
ret = switchAntennaStateHandler();
|
|
break;
|
|
case antenna::State::signalCheck:
|
|
ret = signalCheckStateHandler();
|
|
break;
|
|
case antenna::State::bandCheck:
|
|
ret = bandCheckStateHandler();
|
|
break;
|
|
case antenna::State::idle:
|
|
ret = idleStateHandler();
|
|
break;
|
|
case antenna::State::csqChange:
|
|
ret = csqChangeStateHandler();
|
|
break;
|
|
case antenna::State::locked:
|
|
ret = lockedStateHandler();
|
|
break;
|
|
}
|
|
if (!ret) {
|
|
LOG_WARN("State [ %s ] not handled. Reloading timer.", c_str(state));
|
|
timer.start();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool ServiceAntenna::initStateHandler(void)
|
|
{
|
|
LOG_INFO("State Init");
|
|
bsp::cellular::antenna antenna;
|
|
if (phoneModeObserver->isInMode(sys::phone_modes::PhoneMode::Offline)) {
|
|
auto message =
|
|
std::make_shared<AntennaLockRequestMessage>(MessageType::AntennaLockService, antenna::lockState::locked);
|
|
bus.sendUnicast(std::move(message), service::name::antenna);
|
|
return true;
|
|
}
|
|
|
|
if (CellularServiceAPI::GetAntenna(this, antenna)) {
|
|
currentAntenna = antenna;
|
|
state->enableStateTimeout(cpp_freertos::Ticks::GetTicks(),
|
|
pdMS_TO_TICKS(antenna::connectionStatusTimeout),
|
|
antenna::State::switchAntenna);
|
|
state->set(antenna::State::connectionStatus);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ServiceAntenna::noneStateHandler(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool ServiceAntenna::connectionStatusStateHandler(void)
|
|
{
|
|
auto status = Store::GSM::get()->getNetwork().status;
|
|
if (status == Store::Network::Status::RegisteredHomeNetwork ||
|
|
status == Store::Network::Status::RegisteredRoaming) {
|
|
LOG_INFO("Modem connected.");
|
|
state->disableTimeout();
|
|
state->set(antenna::State::bandCheck);
|
|
return true;
|
|
}
|
|
LOG_INFO("Modem not connected.");
|
|
return false;
|
|
}
|
|
|
|
bool ServiceAntenna::switchAntennaStateHandler(void)
|
|
{
|
|
if (currentAntenna == bsp::cellular::antenna::highBand) {
|
|
CellularServiceAPI::SelectAntenna(this, bsp::cellular::antenna::lowBand);
|
|
return true;
|
|
}
|
|
else if (currentAntenna == bsp::cellular::antenna::lowBand) {
|
|
CellularServiceAPI::SelectAntenna(this, bsp::cellular::antenna::highBand);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ServiceAntenna::signalCheckStateHandler(void)
|
|
{
|
|
if (currentCsq <= antenna::signalTreshold) {
|
|
LOG_INFO("Signal strength below the threshold (CSQ = %" PRIu32 ", Threshold = %" PRIu32 "). Switch antenna",
|
|
currentCsq,
|
|
antenna::signalTreshold);
|
|
state->set(antenna::State::switchAntenna);
|
|
return true;
|
|
}
|
|
lastCsq = currentCsq;
|
|
state->set(antenna::State::idle);
|
|
return true;
|
|
}
|
|
|
|
bool ServiceAntenna::bandCheckStateHandler(void)
|
|
{
|
|
std::string cellularResponse;
|
|
if (CellularServiceAPI::GetQNWINFO(this, cellularResponse)) {
|
|
std::string qnwinfo;
|
|
at::response::parseQNWINFO(cellularResponse, qnwinfo);
|
|
LOG_INFO("QNWINFO: %s", qnwinfo.c_str());
|
|
const auto bandFrequency = at::response::qnwinfo::parseNetworkFrequency(qnwinfo);
|
|
LOG_INFO("Band frequency: %" PRIu32, bandFrequency);
|
|
constexpr uint32_t GHz = 1000;
|
|
const bool isBandSubGHz = bandFrequency < GHz;
|
|
if (currentAntenna == bsp::cellular::antenna::highBand) {
|
|
LOG_INFO("High band antenna.");
|
|
if (isBandSubGHz) {
|
|
LOG_INFO("Modem connected on sub GHz band.");
|
|
state->set(antenna::State::signalCheck);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (currentAntenna == bsp::cellular::antenna::lowBand) {
|
|
LOG_INFO("Low band antenna.");
|
|
if (!isBandSubGHz) {
|
|
LOG_INFO("Modem connected on over GHz band.");
|
|
state->set(antenna::State::signalCheck);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
LOG_INFO("Band match.");
|
|
state->set(antenna::State::idle);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ServiceAntenna::idleStateHandler(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool ServiceAntenna::csqChangeStateHandler(void)
|
|
{
|
|
constexpr uint32_t notValidCsq = 99;
|
|
|
|
auto nextState = antenna::State::idle;
|
|
if (lastCsq != currentCsq || currentCsq == notValidCsq) {
|
|
LOG_INFO("CSQ change occurrence, check connection.");
|
|
nextState = antenna::State::connectionStatus;
|
|
}
|
|
if (currentCsq != notValidCsq) {
|
|
lastCsq = currentCsq;
|
|
}
|
|
LOG_INFO("CSQ value: %" PRIu32, currentCsq);
|
|
|
|
state->set(nextState);
|
|
return true;
|
|
}
|
|
|
|
bool ServiceAntenna::lockedStateHandler(void)
|
|
{
|
|
state->disableTimeout();
|
|
return true;
|
|
}
|
|
|
|
void ServiceAntenna::registerMessageHandlers()
|
|
{
|
|
connect(typeid(antenna::message::InvalidCsqNotification), [&](sys::Message *request) -> sys::MessagePointer {
|
|
if (state->get() == antenna::State::idle) {
|
|
state->set(antenna::State::connectionStatus);
|
|
}
|
|
return sys::MessageNone{};
|
|
});
|
|
|
|
connect(typeid(cellular::msg::notification::SimReady), [this](sys::Message *) {
|
|
state->set(antenna::State::init);
|
|
return sys::MessageNone{};
|
|
});
|
|
|
|
connect(typeid(antenna::message::InvalidCsqNotification), [&](sys::Message *request) -> sys::MessagePointer {
|
|
if (state->get() == antenna::State::idle) {
|
|
state->set(antenna::State::connectionStatus);
|
|
}
|
|
return sys::MessageNone{};
|
|
});
|
|
|
|
connect(typeid(cellular::SignalStrengthUpdateNotification), [&](sys::Message *request) -> sys::MessagePointer {
|
|
currentCsq = Store::GSM::get()->getSignalStrength().rssi;
|
|
if (state->get() == antenna::State::idle) {
|
|
state->set(antenna::State::csqChange);
|
|
}
|
|
return sys::MessageNone{};
|
|
});
|
|
|
|
connect(typeid(cellular::IncomingCallMessage), [&](sys::Message *request) -> sys::MessagePointer {
|
|
auto message =
|
|
std::make_shared<AntennaLockRequestMessage>(MessageType::AntennaLockService, antenna::lockState::locked);
|
|
bus.sendUnicast(std::move(message), service::name::antenna);
|
|
return sys::MessageNone{};
|
|
});
|
|
|
|
connect(typeid(cellular::RingingMessage), [&](sys::Message *request) -> sys::MessagePointer {
|
|
auto message =
|
|
std::make_shared<AntennaLockRequestMessage>(MessageType::AntennaLockService, antenna::lockState::locked);
|
|
bus.sendUnicast(std::move(message), service::name::antenna);
|
|
return sys::MessageNone{};
|
|
});
|
|
|
|
connect(typeid(cellular::HangupCallMessage), [&](sys::Message *request) -> sys::MessagePointer {
|
|
auto message =
|
|
std::make_shared<AntennaLockRequestMessage>(MessageType::AntennaLockService, antenna::lockState::unlocked);
|
|
bus.sendUnicast(std::move(message), service::name::antenna);
|
|
return sys::MessageNone{};
|
|
});
|
|
|
|
connect(typeid(cellular::CallAbortedNotification), [&](sys::Message *request) -> sys::MessagePointer {
|
|
auto message =
|
|
std::make_shared<AntennaLockRequestMessage>(MessageType::AntennaLockService, antenna::lockState::unlocked);
|
|
bus.sendUnicast(std::move(message), service::name::antenna);
|
|
return sys::MessageNone{};
|
|
});
|
|
}
|