mirror of
https://github.com/Telecominfraproject/oopt-tai-implementations.git
synced 2025-10-30 01:32:42 +00:00
394 lines
13 KiB
C++
394 lines
13 KiB
C++
#include "sff_fsm.hpp"
|
|
|
|
namespace tai::sff {
|
|
|
|
static const std::string to_string(FSMState s) {
|
|
switch (s) {
|
|
case FSM_STATE_INIT:
|
|
return "init";
|
|
case FSM_STATE_WAITING_CONFIGURATION:
|
|
return "waiting-configuration";
|
|
case FSM_STATE_READY:
|
|
return "ready";
|
|
case FSM_STATE_END:
|
|
return "end";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
// trim from start (in place)
|
|
static inline void ltrim(std::string &s) {
|
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
|
|
return !std::isspace(ch);
|
|
}));
|
|
}
|
|
|
|
// trim from end (in place)
|
|
static inline void rtrim(std::string &s) {
|
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
|
|
return !std::isspace(ch);
|
|
}).base(), s.end());
|
|
}
|
|
|
|
// trim from both ends (in place)
|
|
static inline void trim(std::string &s) {
|
|
ltrim(s);
|
|
rtrim(s);
|
|
}
|
|
|
|
FSM::FSM(Location loc, const tai_service_method_table_t* services) : m_loc(loc), m_services(services), m_module(nullptr), m_netif{}, m_hostif{}, m_no_transit(false) {
|
|
m_eeprom = std::ifstream(loc + "/eeprom");
|
|
if ( !m_eeprom ) {
|
|
TAI_ERROR("failed to open eeprom");
|
|
throw Exception(TAI_STATUS_ITEM_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
bool FSM::configured() {
|
|
return m_module != nullptr;
|
|
}
|
|
|
|
// this can be called only once during the lifecycle of this FSM
|
|
// remove is not considered yet
|
|
// returns 0 on success. otherwise -1
|
|
int FSM::set_module(S_Module module) {
|
|
if ( m_module != nullptr || module == nullptr ) {
|
|
return -1;
|
|
}
|
|
m_module = module;
|
|
return 0;
|
|
}
|
|
|
|
// returns 0 on success. otherwise -1
|
|
// remove is not considered yet
|
|
int FSM::set_netif(S_NetIf netif, int index) {
|
|
if ( index < 0 || index >= SFF_NUM_NETIF ) {
|
|
return -1;
|
|
}
|
|
if ( m_netif[index] != nullptr || netif == nullptr ) {
|
|
return -1;
|
|
}
|
|
m_netif[index] = netif;
|
|
return 0;
|
|
}
|
|
|
|
// returns 0 on success. otherwise -1
|
|
// remove is not considered yet
|
|
int FSM::set_hostif(S_HostIf hostif, int index) {
|
|
if ( index < 0 || index >= SFF_NUM_HOSTIF ) {
|
|
return -1;
|
|
}
|
|
if ( m_hostif[index] != nullptr || hostif == nullptr ) {
|
|
return -1;
|
|
}
|
|
m_hostif[index] = hostif;
|
|
return 0;
|
|
}
|
|
|
|
tai_status_t FSM::remove_module() {
|
|
if ( m_module == nullptr ) {
|
|
return TAI_STATUS_ITEM_NOT_FOUND;
|
|
}
|
|
for ( int i = 0; i < SFF_NUM_NETIF; i++ ) {
|
|
if ( m_netif[i] != nullptr ) {
|
|
TAI_WARN("can't remove a module before removing its sibling netifs");
|
|
return TAI_STATUS_OBJECT_IN_USE;
|
|
}
|
|
}
|
|
for ( int i = 0; i < SFF_NUM_HOSTIF; i++ ) {
|
|
if ( m_hostif[i] != nullptr ) {
|
|
TAI_WARN("can't remove a module before removing its sibling hostifs");
|
|
return TAI_STATUS_OBJECT_IN_USE;
|
|
}
|
|
}
|
|
transit(FSM_STATE_END);
|
|
while(true) {
|
|
auto s = get_state();
|
|
if ( s == FSM_STATE_END ) {
|
|
break;
|
|
}
|
|
usleep(100000);
|
|
}
|
|
m_module = nullptr;
|
|
return TAI_STATUS_SUCCESS;
|
|
}
|
|
|
|
tai_status_t FSM::remove_netif(int index) {
|
|
if ( index < 0 || index >= SFF_NUM_NETIF || m_netif[index] == nullptr ) {
|
|
return TAI_STATUS_ITEM_NOT_FOUND;
|
|
}
|
|
m_netif[index] = nullptr;
|
|
return TAI_STATUS_SUCCESS;
|
|
}
|
|
|
|
tai_status_t FSM::remove_hostif(int index) {
|
|
if ( index < 0 || index >= SFF_NUM_HOSTIF || m_hostif[index] == nullptr ) {
|
|
return TAI_STATUS_ITEM_NOT_FOUND;
|
|
}
|
|
m_hostif[index] = nullptr;
|
|
return TAI_STATUS_SUCCESS;
|
|
}
|
|
|
|
fsm_state_change_callback FSM::state_change_cb() {
|
|
return std::bind(&FSM::_state_change_cb, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
|
}
|
|
|
|
FSMState FSM::_state_change_cb(FSMState current, FSMState next, void* user) {
|
|
if ( m_module != nullptr ) {
|
|
tai_attribute_t oper;
|
|
oper.id = TAI_MODULE_ATTR_OPER_STATUS;
|
|
if ( next == FSM_STATE_READY ) {
|
|
oper.value.s32 = TAI_MODULE_OPER_STATUS_READY;
|
|
} else {
|
|
oper.value.s32 = TAI_MODULE_OPER_STATUS_INITIALIZE;
|
|
}
|
|
auto& config = m_module->config();
|
|
config.set_readonly(oper);
|
|
m_module->notify(TAI_MODULE_ATTR_NOTIFY, {
|
|
TAI_MODULE_ATTR_OPER_STATUS,
|
|
});
|
|
}
|
|
TAI_INFO("%s -> %s", to_string(current).c_str(), to_string(next).c_str());
|
|
return next;
|
|
}
|
|
|
|
bool FSM::is_present() {
|
|
char buf;
|
|
m_eeprom.read(&buf, 1);
|
|
return m_eeprom.good();
|
|
}
|
|
|
|
fsm_callback FSM::cb(FSMState state) {
|
|
switch (state) {
|
|
case FSM_STATE_INIT:
|
|
return std::bind(&FSM::_init_cb, this, std::placeholders::_1, std::placeholders::_2);
|
|
case FSM_STATE_WAITING_CONFIGURATION:
|
|
return std::bind(&FSM::_waiting_configuration_cb, this, std::placeholders::_1, std::placeholders::_2);
|
|
case FSM_STATE_READY:
|
|
return std::bind(&FSM::_ready_cb, this, std::placeholders::_1, std::placeholders::_2);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// wait eeprom get readable
|
|
FSMState FSM::_init_cb(FSMState current, void* user) {
|
|
fd_set fs;
|
|
auto evfd = get_event_fd();
|
|
itimerspec interval = {{1,0}, {0,1}};
|
|
auto tfd = timerfd_create(CLOCK_REALTIME, 0);
|
|
timerfd_settime(tfd, 0, &interval, NULL);
|
|
FSMState next;
|
|
|
|
bool first = true, prev;
|
|
|
|
while (true) {
|
|
FD_ZERO(&fs);
|
|
FD_SET(tfd, &fs);
|
|
FD_SET(evfd, &fs);
|
|
select(FD_SETSIZE, &fs, NULL, NULL, NULL);
|
|
if (FD_ISSET(evfd, &fs)) {
|
|
uint64_t r;
|
|
read(evfd, &r, sizeof(uint64_t));
|
|
next = next_state();
|
|
if ( next == FSM_STATE_END ) {
|
|
goto ret;
|
|
}
|
|
}
|
|
if (FD_ISSET(tfd, &fs)) {
|
|
uint64_t r;
|
|
read(tfd, &r, sizeof(uint64_t));
|
|
auto present = is_present();
|
|
if ( first || (present != prev) ) {
|
|
first = false;
|
|
if ( m_services != nullptr && m_services->module_presence != nullptr ) {
|
|
m_services->module_presence(present, const_cast<char*>(m_loc.c_str()));
|
|
}
|
|
}
|
|
prev = present;
|
|
if ( present ) {
|
|
next = FSM_STATE_WAITING_CONFIGURATION;
|
|
goto ret;
|
|
}
|
|
}
|
|
}
|
|
ret:
|
|
close(tfd);
|
|
return next;
|
|
}
|
|
|
|
// wait module get created ( check by configured() )
|
|
FSMState FSM::_waiting_configuration_cb(FSMState current, void* user) {
|
|
|
|
fd_set fs;
|
|
auto evfd = get_event_fd();
|
|
itimerspec interval = {{1,0}, {0,1}};
|
|
auto tfd = timerfd_create(CLOCK_REALTIME, 0);
|
|
timerfd_settime(tfd, 0, &interval, NULL);
|
|
FSMState next;
|
|
|
|
while (true) {
|
|
FD_ZERO(&fs);
|
|
FD_SET(evfd, &fs);
|
|
FD_SET(tfd, &fs);
|
|
select(FD_SETSIZE, &fs, NULL, NULL, NULL);
|
|
if (FD_ISSET(evfd, &fs)) {
|
|
uint64_t r;
|
|
read(evfd, &r, sizeof(uint64_t));
|
|
next = next_state();
|
|
goto ret;
|
|
}
|
|
if (FD_ISSET(tfd, &fs)) {
|
|
uint64_t r;
|
|
read(tfd, &r, sizeof(uint64_t));
|
|
if ( configured() && !m_no_transit ) {
|
|
return FSM_STATE_READY;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret:
|
|
close(tfd);
|
|
return next;
|
|
}
|
|
|
|
FSMState FSM::_ready_cb(FSMState current, void* user) {
|
|
fd_set fs;
|
|
auto evfd = get_event_fd();
|
|
itimerspec interval = {{10,0}, {0,1}}; // 10s PM interval
|
|
auto tfd = timerfd_create(CLOCK_REALTIME, 0);
|
|
timerfd_settime(tfd, 0, &interval, NULL);
|
|
FSMState next;
|
|
|
|
while (true) {
|
|
FD_ZERO(&fs);
|
|
FD_SET(evfd, &fs);
|
|
FD_SET(tfd, &fs);
|
|
select(FD_SETSIZE, &fs, NULL, NULL, NULL);
|
|
if (FD_ISSET(evfd, &fs)) {
|
|
uint64_t r;
|
|
read(evfd, &r, sizeof(uint64_t));
|
|
next = next_state();
|
|
goto ret;
|
|
}
|
|
if (FD_ISSET(tfd, &fs)) {
|
|
uint64_t r;
|
|
read(tfd, &r, sizeof(uint64_t));
|
|
|
|
for ( int i = 0; i < SFF_NUM_NETIF; i++ ) {
|
|
if ( m_netif[i] != nullptr ) {
|
|
m_netif[i]->notify(TAI_NETWORK_INTERFACE_ATTR_NOTIFY, {
|
|
TAI_NETWORK_INTERFACE_ATTR_CURRENT_INPUT_POWER,
|
|
TAI_NETWORK_INTERFACE_ATTR_CURRENT_OUTPUT_POWER,
|
|
});
|
|
}
|
|
}
|
|
|
|
if ( m_module != nullptr ) {
|
|
m_module->notify(TAI_MODULE_ATTR_NOTIFY, {
|
|
TAI_MODULE_ATTR_TEMP,
|
|
TAI_MODULE_ATTR_POWER,
|
|
});
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
ret:
|
|
close(tfd);
|
|
return next;
|
|
}
|
|
|
|
tai_status_t FSM::eeprom_get_str(int address, int size, tai_attribute_t* const attr) {
|
|
m_eeprom.seekg(address);
|
|
auto p = std::make_unique<char[]>(size);
|
|
auto buf = p.get();
|
|
m_eeprom.read(buf, size);
|
|
std::string s(buf, size);
|
|
trim(s);
|
|
auto v = attr->value.charlist.count;
|
|
attr->value.charlist.count = s.size() + 1;
|
|
if ( v < (s.size() + 1)) {
|
|
return TAI_STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
std::strncpy(attr->value.charlist.list, s.c_str(), v);
|
|
return TAI_STATUS_SUCCESS;
|
|
}
|
|
|
|
tai_status_t FSM::eeprom_get_temp(int address, int size, tai_attribute_t* const attr) {
|
|
m_eeprom.seekg(address);
|
|
auto p = std::make_unique<char[]>(size);
|
|
auto buf = p.get();
|
|
m_eeprom.read(buf, size);
|
|
auto temp = static_cast<int>(buf[0] * 256 + buf[1]);
|
|
if ( temp > 0x7FFF ) {
|
|
temp -= 65536;
|
|
}
|
|
attr->value.flt = static_cast<float>(temp)/256;
|
|
return TAI_STATUS_SUCCESS;
|
|
}
|
|
|
|
tai_status_t FSM::eeprom_get_voltage(int address, int size, tai_attribute_t* const attr) {
|
|
m_eeprom.seekg(address);
|
|
auto p = std::make_unique<char[]>(size);
|
|
auto buf = p.get();
|
|
m_eeprom.read(buf, size);
|
|
auto temp = static_cast<int>(buf[0] * 256 + buf[1]);
|
|
attr->value.flt = static_cast<float>(temp)/10000;
|
|
return TAI_STATUS_SUCCESS;
|
|
}
|
|
|
|
tai_status_t FSM::eeprom_get_power_dbm(int address, int size, tai_attribute_t* const attr) {
|
|
m_eeprom.seekg(address);
|
|
auto p = std::make_unique<char[]>(size);
|
|
auto buf = p.get();
|
|
m_eeprom.read(buf, size);
|
|
auto tmp = static_cast<float>(static_cast<int>(buf[0] * 256 + buf[1]))/10000;
|
|
if ( tmp < 0.001 ) {
|
|
attr->value.flt = -30; // by convention, -30dBm is the lowest legal value (OOM)
|
|
} else {
|
|
attr->value.flt = 10 * std::log10(tmp);
|
|
}
|
|
return TAI_STATUS_SUCCESS;
|
|
}
|
|
|
|
tai_status_t FSM::get(tai_object_type_t type, tai_object_id_t oid, tai_attribute_t* const attr) {
|
|
switch (type) {
|
|
case TAI_OBJECT_TYPE_MODULE:
|
|
switch (attr->id) {
|
|
case TAI_MODULE_ATTR_VENDOR_NAME:
|
|
return eeprom_get_str(148, 16, attr);
|
|
case TAI_MODULE_ATTR_VENDOR_PART_NUMBER:
|
|
return eeprom_get_str(168, 16, attr);
|
|
case TAI_MODULE_ATTR_VENDOR_SERIAL_NUMBER:
|
|
return eeprom_get_str(196, 16, attr);
|
|
case TAI_MODULE_ATTR_TEMP:
|
|
return eeprom_get_temp(22, 2, attr);
|
|
case TAI_MODULE_ATTR_POWER:
|
|
return eeprom_get_voltage(26, 2, attr);
|
|
default:
|
|
return TAI_STATUS_NOT_SUPPORTED;
|
|
}
|
|
case TAI_OBJECT_TYPE_NETWORKIF:
|
|
{
|
|
auto index = static_cast<int>(oid & 0xff);
|
|
switch (attr->id) {
|
|
case TAI_NETWORK_INTERFACE_ATTR_CURRENT_INPUT_POWER:
|
|
return eeprom_get_power_dbm(34 + index*2, 2, attr);
|
|
case TAI_NETWORK_INTERFACE_ATTR_CURRENT_OUTPUT_POWER:
|
|
return eeprom_get_power_dbm(50 + index*2, 2, attr);
|
|
default:
|
|
return TAI_STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
default:
|
|
return TAI_STATUS_NOT_SUPPORTED;
|
|
}
|
|
return TAI_STATUS_SUCCESS;
|
|
}
|
|
|
|
tai_status_t FSM::set(tai_object_type_t type, tai_object_id_t oid, const tai_attribute_t* const attribute, FSMState* state) {
|
|
return TAI_STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
};
|