#include #include #include #include "StateController.h" #include "ApplicationController.h" #include "types.h" StateController::StateController(QObject *parent) : QObject(parent) {} void StateController::init() { startTimer(QOBJECT_TIMER_TIMEOUT_MS); connect(&_ApplicationController, SIGNAL(didUnhandledMsgAppController(const QVariantList &)), this , SLOT(doStateControllerUnhandledMsgReceived(const QVariantList &))); _dryDemo.connectToState("Idle" , this, &StateController::onIdleStateChange ); _dryDemo.connectToState("Tx_Params" , this, &StateController::onTreatmentParamsStateChange ); _dryDemo.connectToState("Water_Sample" , this, &StateController::onWaterSampleStateChange ); _dryDemo.connectToState("Consumables" , this, &StateController::onConsumablesStateChange ); _dryDemo.connectToState("Disposables" , this, &StateController::onDisposablesStateChange ); _dryDemo.connectToState("System_Prime" , this, &StateController::onSystemPrimeStateChange ); _dryDemo.connectToState("BP_HR" , this, &StateController::onBPHRStateChange ); _dryDemo.connectToState("Ultrafiltraion" , this, &StateController::onUltrafiltrationStateChange ); _dryDemo.connectToState("Connection" , this, &StateController::onConnectionStateChange ); _dryDemo.connectToState("Start_Tx" , this, &StateController::onStartTreatmentStateChange ); _dryDemo.connectToState("Blood_Prime" , this, &StateController::onTreatmentBloodPrimeStateChange); _dryDemo.connectToState("Treatment" , this, &StateController::onTreatmentTreatmentStateChange ); _dryDemo.connectToState("End_Tx" , this, &StateController::onEndTreatmentStateChange ); _dryDemo.connectToState("Disinfect" , this, &StateController::onDisinfectStateChange ); } void StateController::quit() { // TODo fill up } void StateController::doStateControllerUnhandledMsgReceived(const QVariantList &msg) { MessageID receivedMsgID = static_cast(msg[0].toUInt()); switch(receivedMsgID) { case ID_UI_RQST_TX: case ID_UI_TX_PARAMS_RQST: case ID_UI_CONFIRM_RESP: case ID_UI_RINSEBACK_CMD_RQST: case ID_SAMPLE_WATER_RESULT: case ID_UI_SET_UF_VOLUME_RQST: case ID_USER_TX_TIME_CHANGES_RQST: case ID_UI_CONFIRM_TX_PARAMS: _treatmentRcvdMessages[receivedMsgID] = msg[1]; break; case ID_UI_TX_END_RQST: case ID_UI_CONSUMABLES_INSTALL: case ID_UI_INTALLATION_CONFIRM: case ID_UI_PATIENT_CONNECTION_RQST: case ID_UI_PATIENT_CONNECTION_CONF_RQST: case ID_UI_START_TX_RQST: _hasUserConfirmedToProceed = true; break; case ID_NONE: case ID_HD_BLOOD_FLOW_DATA_BC: case ID_HD_DIALYSATE_FLOW_DATA_BC: case ID_PRESSURE_OCCLUSION_DATA_BC: case ID_DIALYSATE_OUT_FLOW_DATA_BC: case ID_TX_TIME_BC: case ID_HD_TX_PARAMS_RANGES_DATA: case ID_HD_TX_STATES_BC: case ID_HD_OP_MODE: case ID_FILTER_FLUSH_TIME_BC: case ID_HD_UI_CONFIRM_RQST: case ID_PRE_TX_STATES_BC: case ID_HD_NEW_PARAMS_RESP: case ID_HD_SYS_SELF_TEST_TIME_BC: case ID_HD_DRY_SELF_TEST_TIME_BC: case ID_HD_PRIMING_TIME_BC: case ID_HD_SET_UF_VOLUME_RESP: case ID_HD_PATINET_CONNECTION_CONF_RESP: case ID_USER_TX_TIME_CHANGES_RESP: case ID_HD_BLOOD_PRIME_BC: case ID_HD_RINSEBACK_CMD_RESP: case ID_HD_RINSEBACK_DATA_BC: // Do nothing break; } } void StateController::timerEvent(QTimerEvent *) { if (!_dryDemo.isRunning()) { _dryDemo.start(); } _dryDemo.submitEvent("TimeoutEvent"); if (_isBroadcastListReady) { sendMessages(true); } if (_isSendListReady) { sendMessages(false); } } // ---------------------- Private methods ------------------ // void StateController::sendMessages(bool isBroadcast) { QList msgList = _broadcastMessages; if (isBroadcast) { _broadcastMessages.clear(); } else { msgList = _sendMessages; _sendMessages.clear(); } for (auto item: msgList) { emit _ApplicationController.didActionTransmit(item); } } void StateController::setTreatmentParams(bool isFromUI, const QVariant &payload) { if (isFromUI) { QVariantList resp; resp.append(static_cast(ID_HD_NEW_PARAMS_RESP)); resp.append(Can_Id::eChlid_HD_UI); resp.append(ACCEPT_VALUE); resp.append(0); qint32 index = 0; Types::U32 u32Var; Types::F32 f32Var; Types::S32 s32Var; GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.bloodFlowRateMLPM = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.dialysateFlowRateMLPM = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.txDurationMins = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.heparinPreStopMin = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.salineBolusVolML = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.acidConc = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.bicarbConc = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.dialyzerType = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.heparinType = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.bloodMeasurementInveralMin = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.rinsebackFlowRateMLPM = u32Var.value; resp.append(u32Var.value); GetValue(payload.toByteArray(), index, s32Var); _treatmentParams.areterialPresLimitWindowMMHG = s32Var.value; resp.append(s32Var.value); GetValue(payload.toByteArray(), index, s32Var); _treatmentParams.venousPresLimitWindowMMHG = s32Var.value; resp.append(s32Var.value); GetValue(payload.toByteArray(), index, s32Var); _treatmentParams.venousPresLimitAsymWindowMMHG = s32Var.value; resp.append(s32Var.value); GetValue(payload.toByteArray(), index, f32Var); _treatmentParams.heparinDispenseRateMLHR = f32Var.value; resp.append(f32Var.value); GetValue(payload.toByteArray(), index, f32Var); _treatmentParams.heparinBolusVolML = f32Var.value; resp.append(f32Var.value); GetValue(payload.toByteArray(), index, f32Var); _treatmentParams.dialysateTemperatureC = f32Var.value; resp.append(f32Var.value); _treatmentParams.hasTxParamsBeenInitialized = true; _isSendListReady = false; _sendMessages.append(resp); _isSendListReady = true; qDebug() << "Treatment params from TPAR" << _treatmentParams.bloodFlowRateMLPM << _treatmentParams.dialysateFlowRateMLPM << _treatmentParams.txDurationMins << _treatmentParams.dialysateTemperatureC << _treatmentParams.rinsebackFlowRateMLPM << _treatmentParams.venousPresLimitWindowMMHG; } else if (!_treatmentParams.hasTxParamsBeenInitialized) { _treatmentParams.bloodFlowRateMLPM = DEF_TX_PARAM_BLOOD_FLOW_RATE_MLPM; _treatmentParams.dialysateFlowRateMLPM = DEF_TX_PARAM_DIAL_FLOW_RATE_MLPM; _treatmentParams.txDurationMins = DEF_TX_PARAM_PRESCRIBED_DUR_MIN; _treatmentParams.heparinPreStopMin = 0; _treatmentParams.salineBolusVolML = DEF_TX_PARAM_SALINE_BOLUS_VOL_ML; _treatmentParams.acidConc = 0; _treatmentParams.bicarbConc = 0; _treatmentParams.dialyzerType = 0; _treatmentParams.heparinType = 0; _treatmentParams.bloodMeasurementInveralMin = 0; _treatmentParams.rinsebackFlowRateMLPM = DEF_TX_PARAM_RINSEBACK_FLOW_RATE_MLPM; _treatmentParams.areterialPresLimitWindowMMHG = DEF_TX_PARAM_ART_PRES_LIMIT_WIN_MMHG; _treatmentParams.venousPresLimitWindowMMHG = DEF_TX_PARAM_VEN_PRES_LIMIT_WIN_MMHG; _treatmentParams.venousPresLimitAsymWindowMMHG = DEF_TX_PARAM_VEN_PRES_LIMIT_ASYM_WIN_MMHG; _treatmentParams.heparinDispenseRateMLHR = 0; _treatmentParams.heparinBolusVolML = 0; _treatmentParams.dialysateTemperatureC = DEF_TX_PARAM_DIA_TEMPERATURE_C; _treatmentParams.hasTxParamsBeenInitialized = true; // The use might start from the treatment directly so everything starts from the default values // so the UF volume is set to default as well. _treatmentVars.txParamsUFVolL = DEF_TX_PARAM_UF_VOLUME_L; } } void StateController::prepareOcclusionBroadcastData() { float artUpperFloatWindowMMHG = static_cast(200); float artLowerFloatWindowMMHG = static_cast(_treatmentParams.areterialPresLimitWindowMMHG); float venUpperFloatWindowMMHG = static_cast(200); float venLowerFloatWindowMMHG = static_cast(_treatmentParams.venousPresLimitWindowMMHG); float artRandomMMHG = _randNumGenerator.generateDouble() * (artUpperFloatWindowMMHG - artLowerFloatWindowMMHG) + artLowerFloatWindowMMHG; float venRandomMMHG = _randNumGenerator.generateDouble() * (venUpperFloatWindowMMHG - venLowerFloatWindowMMHG) + venLowerFloatWindowMMHG; QVariantList msg; msg.append(static_cast(ID_PRESSURE_OCCLUSION_DATA_BC)); msg.append(Can_Id::eChlid_HD_Sync); msg.append(artRandomMMHG); msg.append(venRandomMMHG); msg.append(0); msg.append(0); msg.append(0); msg.append(0); msg.append(0); msg.append(0); msg.append(0); msg.append(0); msg.append(0); msg.append(0); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareTreatmentTimeBroadcastData() { _treatmentVars.treatmentElapsedTimeS++; _treatmentVars.remainingTreatmentTimeS = _treatmentVars.prescribedTreatmentTimeS - _treatmentVars.treatmentElapsedTimeS; if (_treatmentVars.remainingTreatmentTimeS <= 0 ) { _treatmentVars.remainingTreatmentTimeS = 0; } QVariantList msg; msg.append(static_cast(ID_TX_TIME_BC)); msg.append(Can_Id::eChlid_HD_Sync); msg.append(_treatmentVars.prescribedTreatmentTimeS); msg.append(_treatmentVars.treatmentElapsedTimeS); msg.append(_treatmentVars.remainingTreatmentTimeS); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareBloodFlowBroadcastData(quint32 flowMLPM) { QVariantList msg; msg.append(static_cast(ID_HD_BLOOD_FLOW_DATA_BC)); msg.append(Can_Id::eChlid_HD_Sync); msg.append(flowMLPM); msg.append(flowMLPM - 0.1); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0); msg.append(_treatmentParams.bloodFlowRateMLPM); msg.append(0); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareDialysateFlowBroadcastData() { QVariantList msg; msg.append(static_cast(ID_HD_DIALYSATE_FLOW_DATA_BC)); msg.append(Can_Id::eChlid_HD_Sync); msg.append(_treatmentParams.dialysateFlowRateMLPM); msg.append(_treatmentParams.dialysateFlowRateMLPM - 0.1); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0); msg.append(_treatmentParams.dialysateFlowRateMLPM); msg.append(0); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareDialysateOutFlowBroadcastData() { QVariantList msg; msg.append(static_cast(ID_DIALYSATE_OUT_FLOW_DATA_BC)); msg.append(Can_Id::eChlid_HD_Sync); msg.append(_treatmentVars.refUFVolumeML); msg.append(_treatmentVars.measUFVolumeML); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0.0); msg.append(0); msg.append(0.0); msg.append(0); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareTreatmentParamsRangesBroadcastData() { // Calculations are from broadcastTreatmentSettingsRanges in the HD quint32 elapsedTimeMins = _treatmentVars.treatmentElapsedTimeS / SECONDS_PER_MINUTE; float dialVolumeSoFarML = _treatmentParams.dialysateFlowRateMLPM * (_treatmentVars.treatmentElapsedTimeS / SECONDS_PER_MINUTE); float maxQdMLPM = 0.0; if (_treatmentVars.treatmentElapsedTimeS > 0) { maxQdMLPM = (MAX_DIALYSATE_VOLUME_ML - dialVolumeSoFarML) / (_treatmentVars.remainingTreatmentTimeS / SECONDS_PER_MINUTE); } quint32 minTimeMins = qMax(elapsedTimeMins + 2, (quint32)MIN_TX_TIME_MINS); float prescribedUF = (_treatmentVars.prescribedUFRate > 0.0 ? _treatmentVars.prescribedUFRate : 1); quint32 maxTimeRemMins = (MAX_UF_VOL_ML - _treatmentVars.refUFVolumeML) / prescribedUF; quint32 maxTime1 = minTimeMins + maxTimeRemMins; quint32 maxTime2 = MAX_DIAL_RATE_MLPM / _treatmentParams.dialysateFlowRateMLPM; quint32 maxTimeMins = qMax(maxTime1, maxTime2); //qDebug() << "Time Ranges Values" << minTimeMins << maxTimeRemMins << maxTime2 << maxTimeMins << _treatmentVars.prescribedUFRate; float minUFVolML = _treatmentVars.refUFVolumeML + _treatmentVars.prescribedUFRate; float maxUFVol1 = minUFVolML + ((_treatmentParams.txDurationMins - elapsedTimeMins) * MAX_UF_RATE_MLPM); float maxUFVol2 = minUFVolML; if (_treatmentVars.prescribedUFRate > 0.0 ) { maxUFVol2 = minUFVolML + (MAX_TX_TIME_MINS - elapsedTimeMins - 1) * _treatmentVars.prescribedUFRate; } float maxUFVol = qMax(maxUFVol1, maxUFVol2); quint32 minDialFlowRateMLPM = MIN_DIAL_RATE_MLPM; quint32 maxDialFlowRateMLPM = (maxQdMLPM > MIN_DIAL_RATE_MLPM ? maxQdMLPM : minDialFlowRateMLPM); maxTimeMins = qMin(maxTimeMins, (quint32)MAX_TX_TIME_MINS) ; maxUFVol = qMin(maxUFVol, (float)MAX_UF_VOL_ML); maxDialFlowRateMLPM = qMin(maxDialFlowRateMLPM, (quint32)MAX_DIAL_RATE_MLPM); //qDebug() << "New UF" << minUFVolML << maxUFVol1 << maxUFVol2 << maxUFVol << maxDialFlowRateMLPM; QVariantList msg; msg.append(static_cast(ID_HD_TX_PARAMS_RANGES_DATA)); msg.append(Can_Id::eChlid_HD_Sync); msg.append(minTimeMins); msg.append(maxTimeMins); msg.append(minUFVolML); msg.append(maxUFVol); msg.append(minDialFlowRateMLPM); msg.append(maxDialFlowRateMLPM); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareHDModeTransitionBroadcastData(HD_OP_MODE mode, quint32 subMode) { QVariantList msg; msg.append(static_cast(ID_HD_OP_MODE)); msg.append(Can_Id::eChlid_HD_Sync); msg.append(mode); msg.append(subMode); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareStatesBroadcastData(Can::Message_ID_Enum msgID, const QList &preTxStates) { QVariantList msg; msg.append(static_cast(msgID)); msg.append(Can_Id::eChlid_HD_Sync); for (auto item: preTxStates) { msg.append(item); } _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareBloodPrimeBroadcastData(float accumulatedVolML) { QVariantList msg; msg.append(static_cast(ID_HD_BLOOD_PRIME_BC)); msg.append(Can_Id::eChlid_HD_Sync); msg.append(BLOOD_PRIME_VOLUME_ML); msg.append(accumulatedVolML); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareRinsebackBroadcastData(const QList &volsML, const QList &payload) { QVariantList msg; msg.append(static_cast(ID_HD_RINSEBACK_DATA_BC)); msg.append(Can_Id::eChlid_HD_Sync); for (auto item: volsML) { msg.append(item); } for (auto item: payload) { msg.append(item); } _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::prepareTreatmentTimeChangeResponse(quint32 accept, quint32 txDurS, float maxUFvolML) { QVariantList resp; resp.append(static_cast(ID_USER_TX_TIME_CHANGES_RESP)); resp.append(Can_Id::eChlid_HD_UI); resp.append(accept); resp.append(0); resp.append(txDurS/SECONDS_PER_MINUTE); resp.append(maxUFvolML); _isSendListReady = false; _sendMessages.append(resp); _isSendListReady = true; } void StateController::handleTreatmentTimeChangeRequest(const QVariant &payload) { quint32 accept = REJECT_VALUE; qint32 IDIndex = 0; Types::U32 txDurMinType; GetValue(payload.toByteArray(), IDIndex, txDurMinType); quint32 txDurMin = txDurMinType.value; quint32 elapsedTimeMins = _treatmentVars.treatmentElapsedTimeS / SECONDS_PER_MINUTE; if ((txDurMin > elapsedTimeMins) && (txDurMin >= MIN_TX_TIME_MINS)) { float UFVolumeL = ((txDurMin - elapsedTimeMins) * _treatmentVars.prescribedUFRate) + _treatmentVars.refUFVolumeML; UFVolumeL /= MILLILITERS_PER_LITER; // Upper and lower range values are from #defines quint32 dialVolumeML = _treatmentParams.dialysateFlowRateMLPM * txDurMin; bool isMinTxTimeValid = (txDurMin >= MIN_TX_TIME_MINS ? true : false); bool isTxTimeValid = (txDurMin <= MAX_TX_TIME_MINS ? true : false); bool isMinUFVolValid = ((UFVolumeL >= MIN_UF_VOL_L) || (UFVolumeL <= 0.0) ? true : false); // TODO fix the UF Vol bool isUFVolValid = (UFVolumeL <= MAX_UF_VOL_L ? true : false); qDebug() << "Test of tx" << _treatmentVars.prescribedUFRate << elapsedTimeMins << UFVolumeL << isUFVolValid << dialVolumeML; if (isMinTxTimeValid && isTxTimeValid && isMinUFVolValid && isUFVolValid && (dialVolumeML <= MAX_DIALYSATE_VOLUME_ML)) { _treatmentVars.prescribedTreatmentTimeS = txDurMin * SECONDS_PER_MINUTE; _treatmentVars.prescribedMaxUFVolML = UFVolumeL * MILLILITERS_PER_LITER; accept = ACCEPT_VALUE; qDebug() << "New tx time" << txDurMin * SECONDS_PER_MINUTE << accept; } } prepareTreatmentTimeChangeResponse(accept, _treatmentVars.prescribedTreatmentTimeS, _treatmentVars.prescribedMaxUFVolML); } qint32 StateController::handleMsgBBPayload(const QVariant &payload) { qint32 cmd = CMD_NONE; qint32 IDIndex = 0; Types::U32 param; GetValue(payload.toByteArray(), IDIndex, param); if (param.value == UI_MSG_BB_PAYLOAD_ID) { IDIndex = UI_MSG_BB_STATUS_INDEX; GetValue(payload.toByteArray(), IDIndex, param); cmd = param.value; } prepareMsgBA(cmd, CONFIRM_CMD_ACCEPT_CLOSE); return cmd; } void StateController::prepareMsgBA(quint32 cmd, quint32 acceptType) { QVariantList msg; msg.append(static_cast(ID_HD_UI_CONFIRM_RQST)); msg.append(Can_Id::eChlid_HD_UI); msg.append(cmd); msg.append(acceptType); msg.append(0); _isSendListReady = false; _sendMessages.append(msg); _isSendListReady = true; } bool StateController::hasPreTxTimerElapsed(Can::Message_ID_Enum msgID, Can::Can_Id canChan, quint32 &timerCountDownS, quint32 defaultTimeoutS) { bool hasTimeElapsed = false; QVariantList msg; msg.append(static_cast(msgID)); msg.append(canChan); if ((_treatmentVars.broadcastIntervalCount % NUM_OF_COUNTS_TIMER_BC_EMIT == 0) && (timerCountDownS != 0)) { timerCountDownS--; msg.append(defaultTimeoutS); msg.append(timerCountDownS); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } if (timerCountDownS == defaultTimeoutS) { // Send a start broadcast to start from a known value msg.append(defaultTimeoutS); msg.append(timerCountDownS); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } if (timerCountDownS == 0) { hasTimeElapsed = true; } return hasTimeElapsed; } void StateController::handleUltrafiltrationValidation(const QVariant &payload) { // From validateAndSetUFVolume function in HD quint32 accept = REJECT_VALUE; qint32 ufIndex = 0; Types::F32 param; GetValue(payload.toByteArray(), ufIndex, param); float ufVolL = param.value / MILLILITERS_PER_LITER; if ((ufVolL >= MIN_UF_VOL_L) && (ufVolL <= MAX_UF_VOL_L)) { accept = ACCEPT_VALUE; _treatmentVars.txParamsUFVolL = ufVolL; } if (accept == ACCEPT_VALUE) { /* * Before checking for the UF rate, treatment duration is needed. * The user might select UF from dry demo without going to treatment params * So setTreatmentParams is called first. If the treatment params have been set already * then nothing is updated we can continue with verifying the UF */ QVariant emptyList; setTreatmentParams(false, emptyList); if (_treatmentParams.txDurationMins > 0) { float ufRate = param.value / _treatmentParams.txDurationMins; qDebug() << "UF rate" << ufRate; if ((ufRate > MAX_UF_RATE_MLPM) || (ufRate < MIN_UF_RATE_MLPM)) { accept = REJECT_VALUE; } } else { accept = REJECT_VALUE; } } QVariantList msg; msg.append(static_cast(ID_HD_SET_UF_VOLUME_RESP)); msg.append(Can_Id::eChlid_HD_UI); msg.append(accept); msg.append(0); msg.append(_treatmentVars.txParamsUFVolL * MILLILITERS_PER_LITER); _isSendListReady = false; _sendMessages.append(msg); _isSendListReady = true; } void StateController::handleRinsebackSubstate(const QVariant &payload, float &accumVolML, quint32 &txState, quint32 &rbState, quint32 &rbFlowMLPM) { qint32 cmdIndex = 0; Types::U32 param; GetValue(payload.toByteArray(), cmdIndex, param); switch (param.value) { case RINSEBACK_START_RB: case RINSEBACK_RESUME_RB: rbState = RINSEBACK_RUN_STATE; break; case RINSEBACK_ACCEL_FLOW: if (rbFlowMLPM + RINSEBACK_ADJ_FLOW_RATE_MLPM <= RINSEBACK_MAX_RLOW_RATE_MLPM) { if (rbState == RINSEBACK_RUN_STATE) { rbFlowMLPM += RINSEBACK_ADJ_FLOW_RATE_MLPM; } } break; case RINSEABCK_DECEL_FLOW: if (rbFlowMLPM - RINSEBACK_ADJ_FLOW_RATE_MLPM >= RINSEBACK_MIN_FLOW_RATE_MLPM) { if (rbState == RINSEBACK_RUN_STATE) { rbFlowMLPM -= RINSEBACK_ADJ_FLOW_RATE_MLPM; } } break; case RINSEBACK_PAUSE_RB: rbState = RINSEBACK_PAUSE_STATE; break; case RINSEBACK_END_RB: rbState = RINSEBACK_STOP_STATE; break; case RINSEBACK_SALINE_BOLUS: // TODO fill up break; case RINSEBACK_END_TX: // When end treatment is requested, signal 999 is sent back from // here. This is just a drydemo code to get back to the treatment // end state function. Then the treatment end state submits an event // transition back to Idle rbState = RINSEBACK_END_TREATMENT_SIGNAL; break; case RINSEBACK_BACK: // When the back button is pressed reset volume. // TODO do we reset the rinseback volume when we hit the End button? // If the rinseback state is 0 the back is to change to previous tx substate // If the rinseback state is not 0, the rinseback state is decremented accumVolML = 0.0; if (rbState != RINSEBACK_STOP_INIT_STATE) { rbState--; } else { txState = TX_END_SUB_STATE; } break; } QVariantList msg; msg.append(static_cast(ID_HD_RINSEBACK_CMD_RESP)); msg.append(Can_Id::eChlid_HD_UI); msg.append(ACCEPT_VALUE); msg.append(0); _isSendListReady = false; _sendMessages.append(msg); _isSendListReady = true; } bool StateController::isTreatmenStartRequested(const QVariant &payload) { bool status = false; qint32 IDIndex = 0; Types::U32 param; GetValue(payload.toByteArray(), IDIndex, param); if (param.value == START_TX_FROM_MSG_38) { status = true; } return status; } // ----------- State transition methods ---------------- // void StateController::onIdleStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; auto inEntry = [=](){ prepareHDModeTransitionBroadcastData(MODE_STAN, 0); // Send the pre treatment states to reset so it starts from the right state QList preTxStates({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); // Send the treatment params in Idle too. This is to make sure if the user // jumped to the Ultrafiltration page the min and max UF volumes are set prior to it. QVariant emptyVar; setTreatmentParams(false, emptyVar); prepareTreatmentParamsRangesBroadcastData(); // Send message 0xBA in case the dry demo menu stayed open to be closed upon transitioning to standby prepareMsgBA(MODE_STAN, CONFIRM_CMD_ACCEPT_CLOSE); _transitionEventsFromIdle.clear(); _transitionEventsFromIdle[CMD_TX_PARAMS ] = "Transition_2_Tx_Parms"; _transitionEventsFromIdle[CMD_WATER_SAMPLE ] = "Transition_2_Water_Sample"; _transitionEventsFromIdle[CMD_CONSUMABLES ] = "Transition_2_Consumables"; _transitionEventsFromIdle[CMD_DISPOSABLES ] = "Transition_2_Disposables"; _transitionEventsFromIdle[CMD_SYSTEM_PRIME ] = "Transition_2_Sys_Prime"; _transitionEventsFromIdle[CMD_BP_HR ] = "Transition_2_BP_HR"; _transitionEventsFromIdle[CMD_ULTRAFILTRATION] = "Transition_2_Ultrafiltraion"; _transitionEventsFromIdle[CMD_CONNECTION ] = "Transition_2_Patient_Connection"; _transitionEventsFromIdle[CMD_START_TX ] = "Transition_2_Start_Tx"; _transitionEventsFromIdle[CMD_END_TX ] = "Transition_2_End_Tx"; _transitionEventsFromIdle[CMD_DISINFECTION ] = "Transition_2_Disinfect"; status = STATE_ON_ACTION; qDebug() << "Idle on entry"; }; auto inAction = [=](){ bool isMsgBBRequested = false; if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { qint32 cmd = handleMsgBBPayload(_treatmentRcvdMessages[ID_UI_CONFIRM_RESP]); User_Command_ID cmdIDEnum = static_cast(cmd); _treatmentRcvdMessages[ID_UI_CONFIRM_RESP].clear(); // TODO SEND MESSAGE 0XBA BACK TO UI if ((cmdIDEnum > CMD_STAND_BY) && (cmdIDEnum < NUM_OF_USER_CMDS)) { qDebug() << "Submit event" << _transitionEventsFromIdle[cmdIDEnum]; _dryDemo.submitEvent(_transitionEventsFromIdle[cmdIDEnum]); isMsgBBRequested = true; status = STATE_ON_EXIT; } qDebug() << "Idle in action" << cmdIDEnum; } // If the use has requested message 0xBB then ignore create treatment button if ((!_treatmentRcvdMessages[ID_UI_RQST_TX].isNull()) && (!isMsgBBRequested)) { if (isTreatmenStartRequested(_treatmentRcvdMessages[ID_UI_RQST_TX])) { _dryDemo.submitEvent(_transitionEventsFromIdle[CMD_TX_PARAMS]); status = STATE_ON_EXIT; qDebug() << "Idle starting Tx"; } _treatmentRcvdMessages[ID_UI_RQST_TX].clear(); } }; auto inExit = [=](){ qDebug() << "Idle in exit"; status = STATE_ON_ENTRY; }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onTreatmentParamsStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; auto inEntry = [=](){ prepareHDModeTransitionBroadcastData(MODE_TPAR, 0); qDebug() << "In treatment params on entry"; status = STATE_ON_ACTION; _treatmentParams.hasTxParamsBeenInitialized = false; _hasUserConfirmedToProceed = false; }; auto inAction = [=]() { if (!_treatmentRcvdMessages[ID_UI_TX_PARAMS_RQST].isNull()) { setTreatmentParams(true, _treatmentRcvdMessages[ID_UI_TX_PARAMS_RQST]); _treatmentRcvdMessages[ID_UI_TX_PARAMS_RQST].clear(); } if (_treatmentParams.hasTxParamsBeenInitialized && _hasUserConfirmedToProceed) { status = STATE_ON_EXIT; } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_TX_PARAMS].isNull()) { status = STATE_ON_ENTRY; qint32 index = 0; Types::U32 u32Var; GetValue(_treatmentRcvdMessages[ID_UI_CONFIRM_TX_PARAMS].toByteArray(), index, u32Var); if (u32Var.value == ACCEPT_VALUE) { status = STATE_ON_EXIT; qDebug() << "Tx params approved"; } _treatmentRcvdMessages[ID_UI_CONFIRM_TX_PARAMS].clear(); } if (!_treatmentRcvdMessages[ID_UI_RQST_TX].isNull()) { // If the user wants to go back in treatment params go back. if (!isTreatmenStartRequested(_treatmentRcvdMessages[ID_UI_RQST_TX])) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); qDebug() << "Going back to Idle"; } _treatmentRcvdMessages[ID_UI_RQST_TX].clear(); } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { _dryDemo.submitEvent("Transition_back_2_idle"); status = STATE_ON_ENTRY; } }; auto inExit = [=](){ qDebug() << "In treatment params on exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("T_2_Water_Sample"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onWaterSampleStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; static quint32 timerCountDownS = DEFAULT_TIMEOUT_S; auto inEntry = [=](){ qDebug() << "Water sample on entry"; QList preTxStates({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareHDModeTransitionBroadcastData(MODE_PRET, 0); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); status = STATE_ON_ACTION; timerCountDownS = DEFAULT_TIMEOUT_S; _treatmentVars.broadcastIntervalCount = 0; }; auto inAction = [=]() { _treatmentVars.broadcastIntervalCount++; bool hasTimeElapsed = hasPreTxTimerElapsed(ID_FILTER_FLUSH_TIME_BC, eChlid_DG_Sync, timerCountDownS, DEFAULT_TIMEOUT_S); if (hasTimeElapsed) { QList preTxStates({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); quint32 state = 1; quint32 stateIndex = 1; preTxStates[stateIndex] = state; prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } if (hasTimeElapsed) { if (!_treatmentRcvdMessages[ID_SAMPLE_WATER_RESULT].isNull()) { qint32 resultIndex = 0; Types::U32 param; GetValue(_treatmentRcvdMessages[ID_SAMPLE_WATER_RESULT].toByteArray(), resultIndex, param); if (param.value == ACCEPT_VALUE) { status = STATE_ON_EXIT; } else { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } _treatmentRcvdMessages[ID_SAMPLE_WATER_RESULT].clear(); } } }; auto inExit = [=](){ qDebug() << "In treatment params on exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("T_2_Consumables"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onConsumablesStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; static quint32 state = 0; static quint32 stateIndex = 2; static quint32 stateBCInt = 3; auto inEntry = [=](){ qDebug() << "Consumables on entry"; state = 0; QList preTxStates({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); preTxStates[stateIndex] = state; prepareHDModeTransitionBroadcastData(MODE_PRET, 0); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); status = STATE_ON_ACTION; _hasUserConfirmedToProceed = false; _treatmentVars.broadcastIntervalCount = 0; }; auto inAction = [=]() { _treatmentVars.broadcastIntervalCount++; if (_hasUserConfirmedToProceed) { if (state < 4 ) { // TODO #define for this // Added broadcast delay to slow down the substate transitions if (_treatmentVars.broadcastIntervalCount % stateBCInt == 0) { state++; QList preTxStates({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); preTxStates[stateIndex] = state; prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); } } else { status = STATE_ON_EXIT; } } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } }; auto inExit = [=](){ qDebug() << "In consumables on exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("T_2_Disposables"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onDisposablesStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; static quint32 timerCountDownS = DEFAULT_TIMEOUT_S; static quint32 stateNum = 0; static quint32 stateIndex = 3; auto inEntry = [=](){ qDebug() << "Disposables on entry"; stateNum = 0; QList preTxStates({2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); preTxStates[stateIndex] = stateNum; prepareHDModeTransitionBroadcastData(MODE_PRET, 0); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); status = STATE_ON_ACTION; _hasUserConfirmedToProceed = false; _treatmentVars.broadcastIntervalCount = 0; timerCountDownS = DEFAULT_TIMEOUT_S; }; auto inAction = [=]() { _treatmentVars.broadcastIntervalCount++; bool hasTimeElapsed = hasPreTxTimerElapsed(ID_HD_SYS_SELF_TEST_TIME_BC, eChlid_HD_Sync, timerCountDownS, DEFAULT_TIMEOUT_S); if (stateNum < 12) { stateNum++; QList preTxStates({2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); preTxStates[stateIndex] = stateNum; prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); } else if (hasTimeElapsed) { QList preTxStates({3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); if (_hasUserConfirmedToProceed) { qDebug() << "Disposables done"; status = STATE_ON_EXIT; } } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } }; auto inExit = [=](){ qDebug() << "In disposables on exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("T_2_System_Prime"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onSystemPrimeStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; static quint32 timerCountDownS = DEFAULT_TIMEOUT_S; static quint32 stateNum = 0; static quint32 stateIndex = 5; static bool hasPrimeCompleted = false; static quint32 numOfPrimeStates = 15; static quint32 primeStateBCInt = 0; static quint32 numOfPrimingStates = 12; static quint32 primingStateBCInt = 0; auto inEntry = [=](){ qDebug() << "Prime on entry" << primeStateBCInt; stateNum = 0; stateIndex = 5; QList preTxStates({4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); preTxStates[stateIndex] = stateNum; prepareHDModeTransitionBroadcastData(MODE_PRET, 0); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); status = STATE_ON_ACTION; _hasUserConfirmedToProceed = false; _treatmentVars.broadcastIntervalCount = 0; timerCountDownS = DEFAULT_TIMEOUT_S; hasPrimeCompleted = false; // For prime states float divide = ((float)DEFAULT_TIMEOUT_S / (float)numOfPrimeStates) * MILLISECONDS_PER_SECOND; quint32 ceilingValue = qCeil(divide); quint32 modValue = ceilingValue % QOBJECT_TIMER_TIMEOUT_MS; quint32 remaining = ceilingValue - modValue; primeStateBCInt = remaining / QOBJECT_TIMER_TIMEOUT_MS; // For priming states divide = ((float)DEFAULT_TIMEOUT_S / (float)numOfPrimingStates) * MILLISECONDS_PER_SECOND; ceilingValue = qCeil(divide); modValue = ceilingValue % QOBJECT_TIMER_TIMEOUT_MS; remaining = ceilingValue - modValue; primingStateBCInt = remaining / QOBJECT_TIMER_TIMEOUT_MS; }; auto inAction = [=](){ _treatmentVars.broadcastIntervalCount++; bool hasTimeElapsed = false; // First we do the prime section, then priming if (!hasPrimeCompleted) { hasTimeElapsed = hasPreTxTimerElapsed(ID_HD_DRY_SELF_TEST_TIME_BC, eChlid_HD_Sync, timerCountDownS, DEFAULT_TIMEOUT_S); if (stateNum < numOfPrimeStates) { if (_treatmentVars.broadcastIntervalCount % primeStateBCInt == 0) { stateNum++; QList preTxStates({4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); preTxStates[stateIndex] = stateNum; prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); } } else if (hasTimeElapsed) { hasPrimeCompleted = true; stateNum = 0; timerCountDownS = DEFAULT_TIMEOUT_S; QList preTxStates({5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); } } else { hasTimeElapsed = hasPreTxTimerElapsed(ID_HD_PRIMING_TIME_BC, eChlid_HD_Sync, timerCountDownS, DEFAULT_TIMEOUT_S); if (stateNum < numOfPrimingStates) { if (_treatmentVars.broadcastIntervalCount % primingStateBCInt == 0) { stateNum++; stateIndex = 6; QList preTxStates({5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); preTxStates[stateIndex] = stateNum; prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); } } else if (hasTimeElapsed) { QList preTxStates({6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); if (_hasUserConfirmedToProceed) { status = STATE_ON_EXIT; } } } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } }; auto inExit = [=](){ qDebug() << "In disposables on exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("T_2_BP_HR"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onBPHRStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; auto inEntry = [=](){ qDebug() << "BP/HR on entry"; QList preTxStates({7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareHDModeTransitionBroadcastData(MODE_PRET, 0); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); status = STATE_ON_ACTION; _hasUserConfirmedToProceed = false; }; auto inAction = [=](){ if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } // In BP/HR page in the UI there is a confirm button but it is not checked by // the firmware so the dry demo state transitions to the UF page immediately because // there is nothing else is left to be done here status = STATE_ON_EXIT; }; auto inExit = [=](){ qDebug() << "In BP/HR on exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("T_2_Ultrafiltration"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onUltrafiltrationStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; auto inEntry = [=](){ qDebug() << "UF on entry"; prepareTreatmentParamsRangesBroadcastData(); QList preTxStates({7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareHDModeTransitionBroadcastData(MODE_PRET, 0); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); status = STATE_ON_ACTION; _hasUserConfirmedToProceed = false; }; auto inAction = [=](){ if (!_treatmentRcvdMessages[ID_UI_SET_UF_VOLUME_RQST].isNull()) { handleUltrafiltrationValidation(_treatmentRcvdMessages[ID_UI_SET_UF_VOLUME_RQST]); _treatmentRcvdMessages[ID_UI_SET_UF_VOLUME_RQST].clear(); status = STATE_ON_EXIT; } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } }; auto inExit = [=](){ qDebug() << "In UF on exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("T_2_Connection"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onConnectionStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; static bool isConnectionDone = false; auto inEntry = [=](){ qDebug() << "Connection on entry"; QList preTxStates({7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareHDModeTransitionBroadcastData(MODE_PRET, 0); prepareStatesBroadcastData(ID_PRE_TX_STATES_BC, preTxStates); status = STATE_ON_ACTION; isConnectionDone = false; _hasUserConfirmedToProceed = false; }; auto inAction = [=](){ if(_hasUserConfirmedToProceed) { if (!isConnectionDone) { _hasUserConfirmedToProceed = false; isConnectionDone = true; QVariantList msg; msg.clear(); msg.append(static_cast(ID_HD_PATINET_CONNECTION_CONF_RESP)); msg.append(Can_Id::eChlid_HD_UI); msg.append(ACCEPT_VALUE); msg.append(0); _isSendListReady = false; _sendMessages.append(msg); _isSendListReady = true; } else { status = STATE_ON_EXIT; } } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } if (!_treatmentRcvdMessages[ID_UI_SET_UF_VOLUME_RQST].isNull()) { // Firmware (and dry demo in this case) is not involved in going back and forth // in between connection page and the UF page. So once the user hits back from // connection, then the use has to hit confirm twice to be able to come back from // UF to connection _dryDemo.submitEvent("T_Con_2_UF"); _treatmentRcvdMessages[ID_UI_SET_UF_VOLUME_RQST].clear(); status = STATE_ON_ENTRY; } }; auto inExit = [=](){ qDebug() << "In Connection on exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("T_2_Start_Tx"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onStartTreatmentStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; auto inEntry = [=](){ qDebug() << "Start Treatment active"; prepareHDModeTransitionBroadcastData(MODE_TREA, 0); QList txStates({BLOOD_RPIME_SUB_STATE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareStatesBroadcastData(ID_HD_TX_STATES_BC, txStates); // Get the default treatment params in case the user did not start from // the treatment params QVariant emptyVariantList; setTreatmentParams(false, emptyVariantList); status = STATE_ON_EXIT; }; auto inAction = [=](){}; auto inExit = [=](){ status = STATE_ON_ENTRY; _dryDemo.submitEvent("Start_Tx_2_Blood_Prime"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onTreatmentBloodPrimeStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; static float accumBloodVolML = 0.0; auto inEntry = [=](){ qDebug() << "Blood prime entry"; status = STATE_ON_ACTION; accumBloodVolML = 0.0; _treatmentVars.broadcastIntervalCount = 0; prepareBloodFlowBroadcastData(_treatmentParams.bloodFlowRateMLPM); prepareBloodPrimeBroadcastData(accumBloodVolML); }; auto inAction = [=](){ _treatmentVars.broadcastIntervalCount++; accumBloodVolML += (_treatmentParams.bloodFlowRateMLPM * FLOW_INTEGRATOR); if (_treatmentVars.broadcastIntervalCount % NUM_OF_COUNTS_TIMER_BC_EMIT == 0) { prepareBloodFlowBroadcastData(_treatmentParams.bloodFlowRateMLPM); prepareBloodPrimeBroadcastData(accumBloodVolML); prepareOcclusionBroadcastData(); prepareTreatmentParamsRangesBroadcastData(); } if (accumBloodVolML > BLOOD_PRIME_VOLUME_ML) { // send blood prime value one last time prepareBloodPrimeBroadcastData(accumBloodVolML); status = STATE_ON_EXIT; } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } }; auto inExit = [=](){ qDebug() << "Blood prime exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("Blood_Prime_2_Treatment"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onTreatmentTreatmentStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; auto inEntry = [=](){ _treatmentVars.prescribedTreatmentTimeS = _treatmentParams.txDurationMins * SECONDS_PER_MINUTE; _treatmentVars.treatmentElapsedTimeS = (_treatmentVars.prescribedTreatmentTimeS < DEF_TX_ELAPSED_TIME_S ? 0 : DEF_TX_ELAPSED_TIME_S); _treatmentVars.remainingTreatmentTimeS = _treatmentVars.prescribedTreatmentTimeS - _treatmentVars.treatmentElapsedTimeS; _treatmentVars.prescribedMaxUFVolML = _treatmentVars.txParamsUFVolL * MILLILITERS_PER_LITER; _treatmentVars.prescribedUFRate = _treatmentVars.prescribedMaxUFVolML / _treatmentParams.txDurationMins; _treatmentVars.refUFVolumeML = (_treatmentVars.treatmentElapsedTimeS / SECONDS_PER_MINUTE) * _treatmentVars.prescribedUFRate; qDebug() << "Treatment UF start vals" << _treatmentVars.txParamsUFVolL << _treatmentVars.prescribedUFRate << _treatmentVars.prescribedMaxUFVolML << _treatmentVars.refUFVolumeML; // Send the treatment time range response at the beginning of the start treatment // to update the UF ranges at the top right corner of the especially the max UF target // that is set prepareTreatmentTimeChangeResponse(ACCEPT_VALUE, _treatmentVars.prescribedTreatmentTimeS, _treatmentVars.prescribedMaxUFVolML); status = STATE_ON_ACTION; }; auto inAction = [=](){ _treatmentVars.broadcastIntervalCount++; float timeSinceLastCallMS = static_cast(QOBJECT_TIMER_TIMEOUT_MS) / static_cast(MILLISECONDS_PER_SECOND); _treatmentVars.refUFVolumeML += (timeSinceLastCallMS / SECONDS_PER_MINUTE) * _treatmentVars.prescribedUFRate; _treatmentVars.measUFVolumeML = _treatmentVars.refUFVolumeML; //qDebug() << "UF Value" << _treatmentVars.measUFVolumeML << _treatmentVars.refUFVolumeML << _treatmentVars.prescribedUFRate; if (_treatmentVars.broadcastIntervalCount % NUM_OF_COUNTS_TIMER_BC_EMIT == 0) { QList txStates({DIALYSIS_SUB_STATE, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}); prepareStatesBroadcastData(ID_HD_TX_STATES_BC, txStates); prepareOcclusionBroadcastData(); prepareTreatmentTimeBroadcastData(); prepareBloodFlowBroadcastData(_treatmentParams.bloodFlowRateMLPM); prepareDialysateFlowBroadcastData(); prepareDialysateOutFlowBroadcastData(); prepareTreatmentParamsRangesBroadcastData(); } //qDebug() << "Target treatment" << _treatmentVars.prescribedTreatmentTimeS; if (!_treatmentRcvdMessages[ID_USER_TX_TIME_CHANGES_RQST].isNull()) { handleTreatmentTimeChangeRequest(_treatmentRcvdMessages[ID_USER_TX_TIME_CHANGES_RQST]); _treatmentRcvdMessages[ID_USER_TX_TIME_CHANGES_RQST].clear(); } if (_treatmentVars.remainingTreatmentTimeS <= 0 ) { status = STATE_ON_EXIT; } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } }; auto inExit = [=](){ qDebug() << "Treat Treatment exit"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("Treatment_2_End_Treatment"); }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onEndTreatmentStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; static quint32 txSubState = DIALYSIS_SUB_STATE; static quint32 rbSubState = 0; static quint32 rbCountDownS = RINSEBACK_BACK_MAX_TIME_S; // The default rinseback volume is 300 mL but the demo should be done in 10 seconds so it is recalculated here static float rbTargetVolML = 0.0; static float rbAcuumVolML = 0.0; static quint32 rbFlowMLPM = _treatmentParams.rinsebackFlowRateMLPM; auto inEntry = [=](){ // Call the treatment params first in case the use just started from the end treatment // but we need rinseback flow rate in mL/min that comes from the treatment params QVariant empty; setTreatmentParams(false, empty); // In order to jump to end treatment page, first the UI needs to be on another // treatment page, so on entry we first land on the dialysis page then we transition // to end treatment. _treatmentVars.prescribedTreatmentTimeS = _treatmentParams.txDurationMins * SECONDS_PER_MINUTE; txSubState = DIALYSIS_SUB_STATE; rbSubState = 0; rbCountDownS = RINSEBACK_BACK_MAX_TIME_S; rbAcuumVolML = 0.0; rbFlowMLPM = _treatmentParams.rinsebackFlowRateMLPM; rbTargetVolML = (rbFlowMLPM * DEFAULT_TIMEOUT_S) / SECONDS_PER_MINUTE; prepareHDModeTransitionBroadcastData(MODE_TREA, 0); QList txStates({txSubState, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}); prepareStatesBroadcastData(ID_HD_TX_STATES_BC, txStates); txSubState = TX_END_SUB_STATE; txStates[0] = txSubState; txStates[4] = rbSubState; prepareStatesBroadcastData(ID_HD_TX_STATES_BC, txStates); qDebug() << "End treatment on entry" << rbTargetVolML; _treatmentVars.broadcastIntervalCount = 0; _hasUserConfirmedToProceed = false; status = STATE_ON_ACTION; }; auto inAction = [=](){ _treatmentVars.broadcastIntervalCount++; QList txStates({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); QList rinsebackVolsML({rbTargetVolML, rbAcuumVolML}); QList rinsebackBC({rbFlowMLPM, RINSEBACK_BACK_MAX_TIME_S, rbCountDownS, 0}); if (_hasUserConfirmedToProceed) { // Switching to the rinseback send the screen change quickly txSubState = RINSEBACK_SUBSTATE; txStates[0] = txSubState; txStates[4] = rbSubState; prepareStatesBroadcastData(ID_HD_TX_STATES_BC, txStates); _hasUserConfirmedToProceed = false; } if (!_treatmentRcvdMessages[ID_UI_RINSEBACK_CMD_RQST].isNull()) { handleRinsebackSubstate(_treatmentRcvdMessages[ID_UI_RINSEBACK_CMD_RQST], rbAcuumVolML, txSubState, rbSubState, rbFlowMLPM); txStates[0] = txSubState; txStates[4] = rbSubState; prepareStatesBroadcastData(ID_HD_TX_STATES_BC, txStates); prepareRinsebackBroadcastData(rinsebackVolsML, rinsebackBC); if (rbSubState == RINSEBACK_END_TREATMENT_SIGNAL) { // Done with end treatment go back to standby as the dry demo currently does not have // post treatment qDebug() << "Done with end tx"; status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } _treatmentRcvdMessages[ID_UI_RINSEBACK_CMD_RQST].clear(); } if (rbSubState == RINSEBACK_RUN_STATE) { // Only accumulate the rinseback volume if we are in the run state rbAcuumVolML += rbFlowMLPM * FLOW_INTEGRATOR; } if (rbAcuumVolML >= rbTargetVolML) { qDebug() << "RB Vols" << rbAcuumVolML << rbTargetVolML; rbSubState = RINSEBACK_STOP_STATE; txStates[0] = txSubState; txStates[4] = rbSubState; prepareStatesBroadcastData(ID_HD_TX_STATES_BC, txStates); } if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } if (_treatmentVars.broadcastIntervalCount % NUM_OF_COUNTS_TIMER_BC_EMIT == 0) { if ((rbSubState != RINSEBACK_RUN_STATE) && (rbCountDownS != 0)) { // If the rinseback is not in the run state, count down rinseback timer // TODO do we count down from the start rinseback button? rbCountDownS--; rinsebackBC[2] = rbCountDownS; } prepareOcclusionBroadcastData(); prepareStatesBroadcastData(ID_HD_TX_STATES_BC, txStates); prepareRinsebackBroadcastData(rinsebackVolsML, rinsebackBC); prepareTreatmentTimeBroadcastData(); } }; auto inExit = [=](){ qDebug() << "End treatment on exit"; status = STATE_ON_ENTRY; }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } } void StateController::onDisinfectStateChange(bool active) { static State_Status status = STATE_ON_ENTRY; auto inEntry = [=](){ // transition to disinfect screen prepareHDModeTransitionBroadcastData(MODE_STAN, 2); qDebug() << "Disinfect on entry"; status = STATE_ON_ACTION; }; auto inAction = [=](){ if (!_treatmentRcvdMessages[ID_UI_CONFIRM_RESP].isNull()) { status = STATE_ON_ENTRY; _dryDemo.submitEvent("Transition_back_2_idle"); } }; auto inExit = [=](){ qDebug() << "Disinfect on exit"; status = STATE_ON_ENTRY; }; switch (status) { // TODO macro it later case STATE_ON_ENTRY : if (active) inEntry (); break; case STATE_ON_ACTION: if (active) inAction(); break; case STATE_ON_EXIT : if (active) inExit (); break; } }