split ACU HK set
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good

This commit is contained in:
Robin Müller 2022-05-23 10:42:57 +02:00
parent 79d3755c16
commit 3bdad61ac0
No known key found for this signature in database
GPG Key ID: 11D4952C8CCEF814
4 changed files with 159 additions and 387 deletions

View File

@ -6,7 +6,7 @@ ACUHandler::ACUHandler(object_id_t objectId, object_id_t comIF, CookieIF *comCoo
FailureIsolationBase *customFdir) FailureIsolationBase *customFdir)
: GomspaceDeviceHandler(objectId, comIF, comCookie, customFdir, ACU::MAX_CONFIGTABLE_ADDRESS, : GomspaceDeviceHandler(objectId, comIF, comCookie, customFdir, ACU::MAX_CONFIGTABLE_ADDRESS,
ACU::MAX_HKTABLE_ADDRESS, ACU::HK_TABLE_REPLY_SIZE), ACU::MAX_HKTABLE_ADDRESS, ACU::HK_TABLE_REPLY_SIZE),
acuHkTableDataset(this) {} coreHk(this), auxHk(this) {}
ACUHandler::~ACUHandler() {} ACUHandler::~ACUHandler() {}
@ -22,236 +22,141 @@ void ACUHandler::fillCommandAndReplyMap() {
void ACUHandler::letChildHandleHkReply(DeviceCommandId_t id, const uint8_t *packet) { void ACUHandler::letChildHandleHkReply(DeviceCommandId_t id, const uint8_t *packet) {
parseHkTableReply(packet); parseHkTableReply(packet);
handleDeviceTM(&acuHkTableDataset, id, true);
if (debugMode) { if (debugMode) {
#if OBSW_VERBOSE_LEVEL >= 1 #if OBSW_VERBOSE_LEVEL >= 1
acuHkTableDataset.read(); PoolReadGuard pg0(&auxHk);
float temperatureC_1 = acuHkTableDataset.temperature1.value * 0.1; PoolReadGuard pg1(&coreHk);
float temperatureC_2 = acuHkTableDataset.temperature2.value * 0.1; if(pg0.getReadResult() != RETURN_OK or pg1.getReadResult() != RETURN_OK) {
float temperatureC_3 = acuHkTableDataset.temperature3.value * 0.1; return;
sif::info << "ACU: Temperature 1: " << temperatureC_1 << " °C" << std::endl; }
sif::info << "ACU: Temperature 2: " << temperatureC_2 << " °C" << std::endl; for(size_t idx = 0; idx < 3; idx++) {
sif::info << "ACU: Temperature 3: " << temperatureC_3 << " °C" << std::endl; float tempC = coreHk.temperatures[idx] * 0.1;
sif::info << "ACU: Ground Watchdog Timer Count: " << acuHkTableDataset.wdtCntGnd.value sif::info << "ACU: Temperature " << idx << ": " << tempC << " °C" << std::endl;
}
sif::info << "ACU: Ground Watchdog Timer Count: " << auxHk.wdtCntGnd.value
<< std::endl; << std::endl;
sif::info << "ACU: Ground watchdog timer, seconds left before reboot: " sif::info << "ACU: Ground watchdog timer, seconds left before reboot: "
<< acuHkTableDataset.wdtGndLeft.value << std::endl; << auxHk.wdtGndLeft.value << std::endl;
acuHkTableDataset.commit();
#endif #endif
} }
} }
LocalPoolDataSetBase *ACUHandler::getDataSetHandle(sid_t sid) { LocalPoolDataSetBase *ACUHandler::getDataSetHandle(sid_t sid) {
if (sid == acuHkTableDataset.getSid()) { if (sid == coreHk.getSid()) {
return &acuHkTableDataset; return &coreHk;
} else if(sid == auxHk.getSid()) {
return &auxHk;
} }
return nullptr; return nullptr;
} }
void ACUHandler::parseHkTableReply(const uint8_t *packet) { ReturnValue_t ACUHandler::parseHkTableReply(const uint8_t *packet) {
uint16_t dataOffset = 0; uint16_t dataOffset = 0;
acuHkTableDataset.read(); PoolReadGuard pg0(&coreHk);
PoolReadGuard pg1(&auxHk);
auto res0 = pg0.getReadResult();
auto res1 = pg1.getReadResult();
if(res0 != RETURN_OK) {
return res0;
}
if(res1 != RETURN_OK) {
return res1;
}
dataOffset += 12; dataOffset += 12;
acuHkTableDataset.currentInChannel0 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); for(size_t idx = 0; idx < 6; idx++) {
coreHk.currentInChannels[idx] = (packet[dataOffset] << 8) | packet[dataOffset + 1];
dataOffset += 4;
}
for(size_t idx = 0; idx < 6; idx++) {
coreHk.voltageInChannels[idx] = (packet[dataOffset] << 8) | packet[dataOffset + 1];
dataOffset += 4;
}
coreHk.vcc = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4; dataOffset += 4;
acuHkTableDataset.currentInChannel1 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); coreHk.vbat = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.currentInChannel2 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.currentInChannel3 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.currentInChannel4 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.currentInChannel5 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4; dataOffset += 4;
acuHkTableDataset.voltageInChannel0 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); for(size_t idx = 0; idx < 3; idx++) {
dataOffset += 4; coreHk.temperatures[idx] = (packet[dataOffset] << 8) | packet[dataOffset + 1];
acuHkTableDataset.voltageInChannel1 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); dataOffset += 4;
dataOffset += 4; }
acuHkTableDataset.voltageInChannel2 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.voltageInChannel3 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.voltageInChannel4 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.voltageInChannel5 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.vcc = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); coreHk.mpptMode = packet[dataOffset];
dataOffset += 4;
acuHkTableDataset.vbat = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.temperature1 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.temperature2 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.temperature3 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.mpptMode = *(packet + dataOffset);
dataOffset += 3; dataOffset += 3;
acuHkTableDataset.vboostInChannel0 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); for(size_t idx = 0; idx < 6; idx++) {
dataOffset += 4; coreHk.vboostInChannels[idx] = (packet[dataOffset] << 8) | packet[dataOffset + 1];
acuHkTableDataset.vboostInChannel1 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); dataOffset += 4;
dataOffset += 4; }
acuHkTableDataset.vboostInChannel2 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); for(size_t idx = 0; idx < 6; idx++) {
dataOffset += 4; coreHk.powerInChannels[idx] = (packet[dataOffset] << 8) | packet[dataOffset + 1];
acuHkTableDataset.vboostInChannel3 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); dataOffset += 4;
dataOffset += 4; }
acuHkTableDataset.vboostInChannel4 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); for(size_t idx = 0; idx < 3; idx++) {
dataOffset += 4; auxHk.dacEnables[idx] = packet[dataOffset];
acuHkTableDataset.vboostInChannel5 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); dataOffset += 3;
dataOffset += 4; }
for(size_t idx = 0; idx < 6; idx++) {
auxHk.dacRawChannelVal0[idx] = (packet[dataOffset] << 8) | packet[dataOffset + 1];
dataOffset += 4;
}
acuHkTableDataset.powerInChannel0 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); auxHk.bootCause = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 |
dataOffset += 4;
acuHkTableDataset.powerInChannel1 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.powerInChannel2 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.powerInChannel3 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.powerInChannel4 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.powerInChannel5 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.dac0Enable = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.dac1Enable = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.dac2Enable = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.dacRawChannelVal0 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.dacRawChannelVal1 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.dacRawChannelVal2 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.dacRawChannelVal3 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.dacRawChannelVal4 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.dacRawChannelVal5 = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4;
acuHkTableDataset.bootCause = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 |
*(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3); *(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3);
dataOffset += 6; dataOffset += 6;
acuHkTableDataset.bootcnt = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 | coreHk.bootcnt = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 |
*(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3); *(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3);
dataOffset += 6; dataOffset += 6;
acuHkTableDataset.uptime = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 | coreHk.uptime = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 |
*(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3); *(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3);
dataOffset += 6; dataOffset += 6;
acuHkTableDataset.resetCause = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); auxHk.resetCause = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4; dataOffset += 4;
acuHkTableDataset.mpptTime = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); coreHk.mpptTime = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
/* +12 because here starts the second csp packet */ /* +12 because here starts the second csp packet */
dataOffset += 2 + 12; dataOffset += 2 + 12;
acuHkTableDataset.mpptPeriod = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1); coreHk.mpptPeriod = *(packet + dataOffset) << 8 | *(packet + dataOffset + 1);
dataOffset += 4; dataOffset += 4;
acuHkTableDataset.device0 = *(packet + dataOffset); for(size_t idx = 0; idx < 8; idx++) {
dataOffset += 3; auxHk.deviceTypes[idx] = packet[dataOffset];
acuHkTableDataset.device1 = *(packet + dataOffset); dataOffset += 3;
dataOffset += 3; }
acuHkTableDataset.device2 = *(packet + dataOffset); for(size_t idx = 0; idx < 8; idx++) {
dataOffset += 3; auxHk.devicesStatus[idx] = packet[dataOffset];
acuHkTableDataset.device3 = *(packet + dataOffset); dataOffset += 3;
dataOffset += 3; }
acuHkTableDataset.device4 = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device5 = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device6 = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device7 = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device0Status = *(packet + dataOffset); auxHk.wdtCntGnd = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 |
dataOffset += 3;
acuHkTableDataset.device1Status = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device2Status = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device3Status = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device4Status = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device5Status = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device6Status = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.device7Status = *(packet + dataOffset);
dataOffset += 3;
acuHkTableDataset.wdtCntGnd = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 |
*(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3); *(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3);
dataOffset += 6; dataOffset += 6;
acuHkTableDataset.wdtGndLeft = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 | auxHk.wdtGndLeft = *(packet + dataOffset) << 24 | *(packet + dataOffset + 1) << 16 |
*(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3); *(packet + dataOffset + 2) << 8 | *(packet + dataOffset + 3);
dataOffset += 6; dataOffset += 6;
return RETURN_OK;
acuHkTableDataset.commit();
} }
ReturnValue_t ACUHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, ReturnValue_t ACUHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap,
LocalDataPoolManager &poolManager) { LocalDataPoolManager &poolManager) {
using namespace P60System; using namespace P60System;
localDataPoolMap.emplace(pool::ACU_CURRENT_IN_CHANNEL0, new PoolEntry<int16_t>({0})); localDataPoolMap.emplace(pool::ACU_CURRENT_IN_CHANNELS, new PoolEntry<int16_t>(6));
localDataPoolMap.emplace(pool::ACU_CURRENT_IN_CHANNEL1, new PoolEntry<int16_t>({0})); localDataPoolMap.emplace(pool::ACU_VOLTAGE_IN_CHANNELS, new PoolEntry<uint16_t>(6));
localDataPoolMap.emplace(pool::ACU_CURRENT_IN_CHANNEL2, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(pool::ACU_CURRENT_IN_CHANNEL3, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(pool::ACU_CURRENT_IN_CHANNEL4, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(pool::ACU_CURRENT_IN_CHANNEL5, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VOLTAGE_IN_CHANNEL0, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VOLTAGE_IN_CHANNEL1, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VOLTAGE_IN_CHANNEL2, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VOLTAGE_IN_CHANNEL3, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VOLTAGE_IN_CHANNEL4, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VOLTAGE_IN_CHANNEL5, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VCC, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(pool::ACU_VCC, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VBAT, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(pool::ACU_VBAT, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_TEMPERATURE_1, new PoolEntry<int16_t>({0})); localDataPoolMap.emplace(pool::ACU_TEMPERATURES, new PoolEntry<int16_t>(3));
localDataPoolMap.emplace(pool::ACU_TEMPERATURE_2, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(pool::ACU_TEMPERATURE_3, new PoolEntry<int16_t>({0}));
localDataPoolMap.emplace(pool::ACU_MPPT_MODE, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(pool::ACU_MPPT_MODE, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_VBOOST_CHANNEL0, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(pool::ACU_VBOOST_IN_CHANNELS, new PoolEntry<uint16_t>(6));
localDataPoolMap.emplace(pool::ACU_VBOOST_CHANNEL1, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(pool::ACU_POWER_IN_CHANNELS, new PoolEntry<uint16_t>(6));
localDataPoolMap.emplace(pool::ACU_VBOOST_CHANNEL2, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VBOOST_CHANNEL3, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VBOOST_CHANNEL4, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_VBOOST_CHANNEL5, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_POWER_CHANNEL0, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(pool::ACU_DAC_ENABLES, new PoolEntry<uint8_t>(3));
localDataPoolMap.emplace(pool::ACU_POWER_CHANNEL1, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(pool::ACU_DAC_RAW_CHANNELS, new PoolEntry<uint16_t>(6));
localDataPoolMap.emplace(pool::ACU_POWER_CHANNEL2, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_POWER_CHANNEL3, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_POWER_CHANNEL4, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_POWER_CHANNEL5, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_EN_0, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_EN_1, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_EN_2, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_RAW_0, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_RAW_1, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_RAW_2, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_RAW_3, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_RAW_4, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_DAC_RAW_5, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_BOOTCAUSE, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(pool::ACU_BOOTCAUSE, new PoolEntry<uint32_t>({0}));
localDataPoolMap.emplace(pool::ACU_BOOTCNT, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(pool::ACU_BOOTCNT, new PoolEntry<uint32_t>({0}));
@ -260,28 +165,14 @@ ReturnValue_t ACUHandler::initializeLocalDataPool(localpool::DataPool &localData
localDataPoolMap.emplace(pool::ACU_MPPT_TIME, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(pool::ACU_MPPT_TIME, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_MPPT_PERIOD, new PoolEntry<uint16_t>({0})); localDataPoolMap.emplace(pool::ACU_MPPT_PERIOD, new PoolEntry<uint16_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_0, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(pool::ACU_DEVICES, new PoolEntry<uint8_t>(8));
localDataPoolMap.emplace(pool::ACU_DEVICE_1, new PoolEntry<uint8_t>({0})); localDataPoolMap.emplace(pool::ACU_DEVICES_STATUS, new PoolEntry<uint8_t>(8));
localDataPoolMap.emplace(pool::ACU_DEVICE_2, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_3, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_4, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_5, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_6, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_7, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_0_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_1_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_2_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_3_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_4_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_5_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_6_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_DEVICE_7_STATUS, new PoolEntry<uint8_t>({0}));
localDataPoolMap.emplace(pool::ACU_WDT_CNT_GND, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(pool::ACU_WDT_CNT_GND, new PoolEntry<uint32_t>({0}));
localDataPoolMap.emplace(pool::ACU_WDT_GND_LEFT, new PoolEntry<uint32_t>({0})); localDataPoolMap.emplace(pool::ACU_WDT_GND_LEFT, new PoolEntry<uint32_t>({0}));
poolManager.subscribeForPeriodicPacket(acuHkTableDataset.getSid(), false, 30.0, false); poolManager.subscribeForPeriodicPacket(coreHk.getSid(), false, 10.0, true);
poolManager.subscribeForPeriodicPacket(auxHk.getSid(), false, 30.0, false);
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
@ -299,26 +190,13 @@ ReturnValue_t ACUHandler::childCommandHook(DeviceCommandId_t cmd, const uint8_t
} }
void ACUHandler::printChannelStats() { void ACUHandler::printChannelStats() {
PoolReadGuard pg(&acuHkTableDataset); PoolReadGuard pg(&coreHk);
sif::info << "ACU Info: Current [mA], Voltage [mV]" << std::endl; sif::info << "ACU Info: Current [mA], Voltage [mV]" << std::endl;
sif::info << std::setw(8) << std::left << "Ch0" << std::dec << "| " for(size_t idx = 0; idx < 6; idx++) {
<< static_cast<unsigned int>(acuHkTableDataset.currentInChannel0.value) << std::setw(15) sif::info << std::setw(8) << std::left << "Channel " << idx << std::dec << "| "
<< std::right << acuHkTableDataset.voltageInChannel0.value << std::endl; << static_cast<unsigned int>(coreHk.currentInChannels[idx]) << std::setw(15)
sif::info << std::setw(8) << std::left << "Ch1" << std::dec << "| " << std::right << coreHk.voltageInChannels[idx] << std::endl;
<< static_cast<unsigned int>(acuHkTableDataset.currentInChannel1.value) << std::setw(15) }
<< std::right << acuHkTableDataset.voltageInChannel1.value << std::endl;
sif::info << std::setw(8) << std::left << "Ch2" << std::dec << "| "
<< static_cast<unsigned int>(acuHkTableDataset.currentInChannel2.value) << std::setw(15)
<< std::right << acuHkTableDataset.voltageInChannel2.value << std::endl;
sif::info << std::setw(8) << std::left << "Ch3" << std::dec << "| "
<< static_cast<unsigned int>(acuHkTableDataset.currentInChannel3.value) << std::setw(15)
<< std::right << acuHkTableDataset.voltageInChannel3.value << std::endl;
sif::info << std::setw(8) << std::left << "Ch4" << std::dec << "| "
<< static_cast<unsigned int>(acuHkTableDataset.currentInChannel4.value) << std::setw(15)
<< std::right << acuHkTableDataset.voltageInChannel4.value << std::endl;
sif::info << std::setw(8) << std::left << "Ch5" << std::dec << "| "
<< static_cast<unsigned int>(acuHkTableDataset.currentInChannel5.value) << std::setw(15)
<< std::right << acuHkTableDataset.voltageInChannel5.value << std::endl;
} }
void ACUHandler::setDebugMode(bool enable) { this->debugMode = enable; } void ACUHandler::setDebugMode(bool enable) { this->debugMode = enable; }

View File

@ -39,14 +39,15 @@ class ACUHandler : public GomspaceDeviceHandler {
private: private:
static const DeviceCommandId_t PRINT_CHANNEL_STATS = 51; static const DeviceCommandId_t PRINT_CHANNEL_STATS = 51;
ACU::HkTableDataset acuHkTableDataset; ACU::CoreHk coreHk;
ACU::AuxHk auxHk;
bool debugMode = false; bool debugMode = false;
/** /**
* @brief Function extracts the hk table information from the received csp packet and stores * @brief Function extracts the hk table information from the received csp packet and stores
* the values in the acuHkTableDataset. * the values in the acuHkTableDataset.
*/ */
void parseHkTableReply(const uint8_t* packet); ReturnValue_t parseHkTableReply(const uint8_t* packet);
/** /**
* @brief Prints channel statistics (current and voltage) to console * @brief Prints channel statistics (current and voltage) to console

View File

@ -55,7 +55,8 @@ enum class SetIds : uint32_t {
PDU_2_AUX = 4, PDU_2_AUX = 4,
P60_CORE = 5, P60_CORE = 5,
P60_AUX = 6, P60_AUX = 6,
ACU = 7 ACU_CORE = 7,
ACU_AUX = 8
}; };
namespace pool { namespace pool {
@ -129,67 +130,24 @@ enum Ids : lp_id_t {
PDU_WDT_CSP_LEFT2, PDU_WDT_CSP_LEFT2,
/** ACU Ids */ /** ACU Ids */
ACU_CURRENT_IN_CHANNEL0, ACU_CURRENT_IN_CHANNELS,
ACU_CURRENT_IN_CHANNEL1, ACU_VOLTAGE_IN_CHANNELS,
ACU_CURRENT_IN_CHANNEL2,
ACU_CURRENT_IN_CHANNEL3,
ACU_CURRENT_IN_CHANNEL4,
ACU_CURRENT_IN_CHANNEL5,
ACU_VOLTAGE_IN_CHANNEL0,
ACU_VOLTAGE_IN_CHANNEL1,
ACU_VOLTAGE_IN_CHANNEL2,
ACU_VOLTAGE_IN_CHANNEL3,
ACU_VOLTAGE_IN_CHANNEL4,
ACU_VOLTAGE_IN_CHANNEL5,
ACU_VCC, ACU_VCC,
ACU_VBAT, ACU_VBAT,
ACU_TEMPERATURE_1, ACU_TEMPERATURES,
ACU_TEMPERATURE_2,
ACU_TEMPERATURE_3,
ACU_MPPT_MODE, ACU_MPPT_MODE,
ACU_VBOOST_CHANNEL0, ACU_VBOOST_IN_CHANNELS,
ACU_VBOOST_CHANNEL1, ACU_POWER_IN_CHANNELS,
ACU_VBOOST_CHANNEL2, ACU_DAC_ENABLES,
ACU_VBOOST_CHANNEL3, ACU_DAC_RAW_CHANNELS,
ACU_VBOOST_CHANNEL4,
ACU_VBOOST_CHANNEL5,
ACU_POWER_CHANNEL0,
ACU_POWER_CHANNEL1,
ACU_POWER_CHANNEL2,
ACU_POWER_CHANNEL3,
ACU_POWER_CHANNEL4,
ACU_POWER_CHANNEL5,
ACU_DAC_EN_0,
ACU_DAC_EN_1,
ACU_DAC_EN_2,
ACU_DAC_RAW_0,
ACU_DAC_RAW_1,
ACU_DAC_RAW_2,
ACU_DAC_RAW_3,
ACU_DAC_RAW_4,
ACU_DAC_RAW_5,
ACU_BOOTCAUSE, ACU_BOOTCAUSE,
ACU_BOOTCNT, ACU_BOOTCNT,
ACU_UPTIME, ACU_UPTIME,
ACU_RESET_CAUSE, ACU_RESET_CAUSE,
ACU_MPPT_TIME, ACU_MPPT_TIME,
ACU_MPPT_PERIOD, ACU_MPPT_PERIOD,
ACU_DEVICE_0, ACU_DEVICES,
ACU_DEVICE_1, ACU_DEVICES_STATUS,
ACU_DEVICE_2,
ACU_DEVICE_3,
ACU_DEVICE_4,
ACU_DEVICE_5,
ACU_DEVICE_6,
ACU_DEVICE_7,
ACU_DEVICE_0_STATUS,
ACU_DEVICE_1_STATUS,
ACU_DEVICE_2_STATUS,
ACU_DEVICE_3_STATUS,
ACU_DEVICE_4_STATUS,
ACU_DEVICE_5_STATUS,
ACU_DEVICE_6_STATUS,
ACU_DEVICE_7_STATUS,
ACU_WDT_CNT_GND, ACU_WDT_CNT_GND,
ACU_WDT_GND_LEFT, ACU_WDT_GND_LEFT,
}; };
@ -592,138 +550,73 @@ static const uint16_t MAX_HKTABLE_ADDRESS = 120;
static const uint8_t HK_TABLE_ENTRIES = 64; static const uint8_t HK_TABLE_ENTRIES = 64;
static const uint16_t HK_TABLE_REPLY_SIZE = 262; static const uint16_t HK_TABLE_REPLY_SIZE = 262;
/** class CoreHk: public StaticLocalDataSet<14> {
* @brief This class defines a dataset for the hk table of the ACU. public:
*/ CoreHk(HasLocalDataPoolIF* owner)
class HkTableDataset : public StaticLocalDataSet<HK_TABLE_ENTRIES> { : StaticLocalDataSet(owner, static_cast<uint32_t>(::P60System::SetIds::ACU_CORE)) {}
public:
HkTableDataset(HasLocalDataPoolIF* owner)
: StaticLocalDataSet(owner, static_cast<uint32_t>(::P60System::SetIds::ACU)) {}
HkTableDataset(object_id_t objectId) CoreHk(object_id_t objectId)
: StaticLocalDataSet(sid_t(objectId, static_cast<uint32_t>(::P60System::SetIds::ACU))) {} : StaticLocalDataSet(sid_t(objectId, static_cast<uint32_t>(::P60System::SetIds::ACU_CORE))) {}
lp_var_t<int16_t> currentInChannel0 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_CURRENT_IN_CHANNEL0, this);
lp_var_t<int16_t> currentInChannel1 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_CURRENT_IN_CHANNEL1, this);
lp_var_t<int16_t> currentInChannel2 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_CURRENT_IN_CHANNEL2, this);
lp_var_t<int16_t> currentInChannel3 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_CURRENT_IN_CHANNEL3, this);
lp_var_t<int16_t> currentInChannel4 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_CURRENT_IN_CHANNEL4, this);
lp_var_t<int16_t> currentInChannel5 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_CURRENT_IN_CHANNEL5, this);
lp_var_t<uint16_t> voltageInChannel0 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VOLTAGE_IN_CHANNEL0, this);
lp_var_t<uint16_t> voltageInChannel1 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VOLTAGE_IN_CHANNEL1, this);
lp_var_t<uint16_t> voltageInChannel2 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VOLTAGE_IN_CHANNEL2, this);
lp_var_t<uint16_t> voltageInChannel3 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VOLTAGE_IN_CHANNEL3, this);
lp_var_t<uint16_t> voltageInChannel4 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VOLTAGE_IN_CHANNEL4, this);
lp_var_t<uint16_t> voltageInChannel5 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VOLTAGE_IN_CHANNEL5, this);
lp_var_t<uint16_t> vcc = lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VCC, this);
lp_var_t<uint16_t> vbat = lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VBAT, this);
lp_var_t<int16_t> temperature1 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_TEMPERATURE_1, this);
lp_var_t<int16_t> temperature2 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_TEMPERATURE_2, this);
lp_var_t<int16_t> temperature3 =
lp_var_t<int16_t>(sid.objectId, P60System::pool::ACU_TEMPERATURE_3, this);
lp_var_t<uint8_t> mpptMode = lp_var_t<uint8_t> mpptMode =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_MPPT_MODE, this); lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_MPPT_MODE, this);
lp_var_t<uint16_t> vboostInChannel0 = lp_vec_t<int16_t, 6> currentInChannels =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VBOOST_CHANNEL0, this); lp_vec_t<int16_t, 6>(sid.objectId, P60System::pool::ACU_CURRENT_IN_CHANNELS, this);
lp_var_t<uint16_t> vboostInChannel1 = lp_vec_t<uint16_t, 6> voltageInChannels =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VBOOST_CHANNEL1, this); lp_vec_t<uint16_t, 6>(sid.objectId, P60System::pool::ACU_VOLTAGE_IN_CHANNELS, this);
lp_var_t<uint16_t> vboostInChannel2 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VBOOST_CHANNEL2, this);
lp_var_t<uint16_t> vboostInChannel3 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VBOOST_CHANNEL3, this);
lp_var_t<uint16_t> vboostInChannel4 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VBOOST_CHANNEL4, this);
lp_var_t<uint16_t> vboostInChannel5 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VBOOST_CHANNEL5, this);
lp_var_t<uint16_t> powerInChannel0 = lp_var_t<uint16_t> vcc = lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VCC, this);
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_POWER_CHANNEL0, this); lp_var_t<uint16_t> vbat = lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_VBAT, this);
lp_var_t<uint16_t> powerInChannel1 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_POWER_CHANNEL1, this);
lp_var_t<uint16_t> powerInChannel2 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_POWER_CHANNEL2, this);
lp_var_t<uint16_t> powerInChannel3 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_POWER_CHANNEL3, this);
lp_var_t<uint16_t> powerInChannel4 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_POWER_CHANNEL4, this);
lp_var_t<uint16_t> powerInChannel5 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_POWER_CHANNEL5, this);
lp_var_t<uint8_t> dac0Enable = lp_vec_t<uint16_t, 6> vboostInChannels =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DAC_EN_0, this); lp_vec_t<uint16_t, 6>(sid.objectId, P60System::pool::ACU_VBOOST_IN_CHANNELS, this);
lp_var_t<uint8_t> dac1Enable = lp_vec_t<uint16_t, 6> powerInChannels =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DAC_EN_1, this); lp_vec_t<uint16_t, 6>(sid.objectId, P60System::pool::ACU_POWER_IN_CHANNELS, this);
lp_var_t<uint8_t> dac2Enable =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DAC_EN_2, this);
lp_var_t<uint16_t> dacRawChannelVal0 = lp_vec_t<int16_t, 3> temperatures =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_DAC_RAW_0, this); lp_vec_t<int16_t, 3>(sid.objectId, P60System::pool::ACU_TEMPERATURES, this);
lp_var_t<uint16_t> dacRawChannelVal1 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_DAC_RAW_1, this);
lp_var_t<uint16_t> dacRawChannelVal2 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_DAC_RAW_2, this);
lp_var_t<uint16_t> dacRawChannelVal3 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_DAC_RAW_3, this);
lp_var_t<uint16_t> dacRawChannelVal4 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_DAC_RAW_4, this);
lp_var_t<uint16_t> dacRawChannelVal5 =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_DAC_RAW_5, this);
lp_var_t<uint32_t> bootCause =
lp_var_t<uint32_t>(sid.objectId, P60System::pool::ACU_BOOTCAUSE, this);
lp_var_t<uint32_t> bootcnt = lp_var_t<uint32_t>(sid.objectId, P60System::pool::ACU_BOOTCNT, this); lp_var_t<uint32_t> bootcnt = lp_var_t<uint32_t>(sid.objectId, P60System::pool::ACU_BOOTCNT, this);
lp_var_t<uint32_t> uptime = lp_var_t<uint32_t>(sid.objectId, P60System::pool::ACU_UPTIME, this); lp_var_t<uint32_t> uptime = lp_var_t<uint32_t>(sid.objectId, P60System::pool::ACU_UPTIME, this);
lp_var_t<uint16_t> resetCause =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_RESET_CAUSE, this);
lp_var_t<uint16_t> mpptTime = lp_var_t<uint16_t> mpptTime =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_MPPT_TIME, this); lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_MPPT_TIME, this);
lp_var_t<uint16_t> mpptPeriod = lp_var_t<uint16_t> mpptPeriod =
lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_MPPT_PERIOD, this); lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_MPPT_PERIOD, this);
lp_var_t<uint8_t> device0 = lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_0, this); };
lp_var_t<uint8_t> device1 = lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_1, this); /**
lp_var_t<uint8_t> device2 = lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_2, this); * @brief This class defines a dataset for the hk table of the ACU.
lp_var_t<uint8_t> device3 = lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_3, this); */
lp_var_t<uint8_t> device4 = lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_4, this); class AuxHk : public StaticLocalDataSet<12> {
lp_var_t<uint8_t> device5 = lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_5, this); public:
lp_var_t<uint8_t> device6 = lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_6, this); AuxHk(HasLocalDataPoolIF* owner)
lp_var_t<uint8_t> device7 = lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_7, this); : StaticLocalDataSet(owner, static_cast<uint32_t>(::P60System::SetIds::ACU_AUX)) {}
lp_var_t<uint8_t> device0Status = AuxHk(object_id_t objectId)
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_0_STATUS, this); : StaticLocalDataSet(sid_t(objectId, static_cast<uint32_t>(::P60System::SetIds::ACU_AUX))) {}
lp_var_t<uint8_t> device1Status =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_1_STATUS, this);
lp_var_t<uint8_t> device2Status = lp_vec_t<uint8_t, 3> dacEnables =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_2_STATUS, this); lp_vec_t<uint8_t, 3>(sid.objectId, P60System::pool::ACU_DAC_ENABLES, this);
lp_var_t<uint8_t> device3Status =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_3_STATUS, this); lp_vec_t<uint16_t, 6> dacRawChannelVal0 =
lp_var_t<uint8_t> device4Status = lp_vec_t<uint16_t, 6>(sid.objectId, P60System::pool::ACU_DAC_RAW_CHANNELS, this);
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_4_STATUS, this);
lp_var_t<uint8_t> device5Status = lp_var_t<uint32_t> bootCause =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_5_STATUS, this); lp_var_t<uint32_t>(sid.objectId, P60System::pool::ACU_BOOTCAUSE, this);
lp_var_t<uint8_t> device6Status = lp_var_t<uint16_t> resetCause =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_6_STATUS, this); lp_var_t<uint16_t>(sid.objectId, P60System::pool::ACU_RESET_CAUSE, this);
lp_var_t<uint8_t> device7Status =
lp_var_t<uint8_t>(sid.objectId, P60System::pool::ACU_DEVICE_7_STATUS, this); /**
* There are 8 devices on the PDU. FRAM, ADCs, temperature sensor etc. Each device is
* identified by an ID. Refer also to gs-man-nanopower-p60-pdu-200-1.pdf on pages 17 and 18.
*/
lp_vec_t<uint8_t, 8> deviceTypes =
lp_vec_t<uint8_t, 8>(sid.objectId, P60System::pool::ACU_DEVICES, this);
/** The status of each device. 0 = None, 1 = Ok, 2 = Error, 3 = Not found */
lp_vec_t<uint8_t, 8> devicesStatus =
lp_vec_t<uint8_t, 8>(sid.objectId, P60System::pool::ACU_DEVICES_STATUS, this);
lp_var_t<uint32_t> wdtCntGnd = lp_var_t<uint32_t> wdtCntGnd =
lp_var_t<uint32_t>(sid.objectId, P60System::pool::ACU_WDT_CNT_GND, this); lp_var_t<uint32_t>(sid.objectId, P60System::pool::ACU_WDT_CNT_GND, this);

2
tmtc

@ -1 +1 @@
Subproject commit 480e0f07e03edeb55a3d6e3d629e7601b6bf90a2 Subproject commit ff66dd5dd2bc2ee3150ce45160303bc79ed2599d