#include #include "StateController.h" #include "ApplicationController.h" #include "types.h" float StateController::getBloodPrimeRampStepML() { qint32 setBPRateMLPM = _treatmentParams.bloodFlowRateMLPM - BLOOD_PRIME_START_FLOW_MLPM; float rampRateS = ((BLOOD_PRIME_VOLUME_ML / (setBPRateMLPM + BLOOD_PRIME_VOLUME_ML) / BLOOD_PRIME_START_FLOW_MLPM) / 2.0) * SECONDS_PER_MINUTE; qDebug() << "Step cal" << _treatmentParams.bloodFlowRateMLPM << setBPRateMLPM << rampRateS; rampRateS = (rampRateS < SECONDS_PER_MINUTE ? SECONDS_PER_MINUTE : rampRateS); float rampStepML = setBPRateMLPM / rampRateS; return rampStepML; } void StateController::handleBloodPrimeBroadcastData(const float accumulatedVolML, const quint32 primeTimeOutS, const quint32 pauseCountDownS, const Broadcast_Type BCType) { static quint32 broadcastCounter = 0; if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } // Broadcast QVariantList msg; msg.append(static_cast(ID_TD_BLOOD_PRIME_PROGRESS_DATA)); msg.append(Can_Id::eChlid_TD_Sync); msg.append(BLOOD_PRIME_VOLUME_ML); msg.append(accumulatedVolML); msg.append(primeTimeOutS); msg.append(pauseCountDownS); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleBloodPrimeVolML(const bool resume, const float rampStepML, const bool hasFlowChanged, quint32 &currBloodFlowMLPM, float &accumBloodVolML) { static quint32 primeTimeoutS = 0; static quint32 pauseCountDownS = 0; static quint32 elapsedTimeSincePauseS = 0; static quint32 controlCounter = 0; if (!resume) { // If the user has pressed pause, then do the countdown and broadcast // TODO should we exit is the timeout has happened? elapsedTimeSincePauseS += (QOBJECT_TIMER_TIMEOUT_MS); primeTimeoutS = BLOOD_PRIME_PAUSE_TIMEOUT_S; pauseCountDownS = ( elapsedTimeSincePauseS / MILLISECONDS_PER_SECOND < BLOOD_PRIME_PAUSE_TIMEOUT_S ? BLOOD_PRIME_PAUSE_TIMEOUT_S - elapsedTimeSincePauseS / MILLISECONDS_PER_SECOND : pauseCountDownS ); handleBloodPrimeBroadcastData(accumBloodVolML, primeTimeoutS, pauseCountDownS, BC_DELAYED); return; } accumBloodVolML += (currBloodFlowMLPM * BLOOD_PRIME_FLOW_INTEGRATOR); if (++controlCounter % NUM_OF_COUNTS_TIMER_2_CONTROL != 0) { return; } if (!hasFlowChanged) { // If the user has changed the flow then do not control (ramp) the steps currBloodFlowMLPM += rampStepML; qDebug() << "Flow calc" << currBloodFlowMLPM; if (rampStepML < 0){ // If the ramp is negative, check the current flow not be less than target and if it is, set it to min // e.g. current flow calculated = 30 mL/min, treatment params = 50 mL/min, Set it to 50 mL/min currBloodFlowMLPM = (currBloodFlowMLPM < _treatmentParams.bloodFlowRateMLPM ? _treatmentParams.bloodFlowRateMLPM : currBloodFlowMLPM); } else { currBloodFlowMLPM = (currBloodFlowMLPM > _treatmentParams.bloodFlowRateMLPM ? _treatmentParams.bloodFlowRateMLPM : currBloodFlowMLPM); } } primeTimeoutS = 0; pauseCountDownS = 0; elapsedTimeSincePauseS = 0; handleBloodPrimeBroadcastData(accumBloodVolML, primeTimeoutS, pauseCountDownS, BC_IMMEDIATELY); qDebug() << "Current" << currBloodFlowMLPM << _treatmentParams.bloodFlowRateMLPM << controlCounter << accumBloodVolML; } void StateController::setTreatmentParametersToDefault() { // TODO complete the list as we go _treatmentParams.finalConfirmation = true; _treatmentParams.treatmentModality = 0; // TODO set the actual enum value _treatmentParams.bloodFlowRateMLPM = DEF_TX_PARAM_BLOOD_FLOW_RATE_MLPM; _treatmentParams.dialysateFlowRateMLPM = DEF_TX_PARAM_DIAL_FLOW_RATE_MLPM; _treatmentParams.treatmentDurationMin = DEF_TX_PARAM_PRESCRIBED_DUR_MIN; _treatmentParams.dialysateTemperatureC = DEF_TX_PARAM_DIA_TEMPERATURE_C; _treatmentParams.treatmentDurationMin = DEF_TX_PARAM_PRESCRIBED_DUR_MIN; _treatmentParams.fluidBlousVolumeML = DEF_TX_PARAM_SALINE_BOLUS_VOL_ML; _treatmentParams.ufVolumeL = DEF_TX_PARAM_UF_VOLUME_L; } void StateController::initializeTreatmentTimeAndUltrafiltration() { // The default elapsed time is 2 hours becasue the default tx duration is 4 hours. If the prescribed is less than 2 hours, 0 the elapsed if ((_treatmentParams.treatmentDurationMin * SECONDS_PER_MINUTE) < DEF_TX_ELAPSED_TIME_S){ _treatmentStatus.treatmentElapsedTimeMS = 0; } _treatmentStatus.presUFRateMLPM = (_treatmentParams.ufVolumeL * MILLILITERS_PER_LITER) / (static_cast(_treatmentParams.treatmentDurationMin)); float presUFVolL = _treatmentParams.ufVolumeL; float presUFRateLHR = presUFVolL / (static_cast(_treatmentParams.treatmentDurationMin) / static_cast(MINUTES_PER_HOUR)); float UFVolLMS = presUFRateLHR / (static_cast(MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND)); _treatmentStatus.ufVolDeliveredL = UFVolLMS * _treatmentStatus.treatmentElapsedTimeMS; qDebug() << "UF Init" << presUFRateLHR << UFVolLMS << _treatmentStatus.ufVolDeliveredL; } void StateController::handleTreatmentSetpointsBroadcastData(const quint32 bloodFlowMLPM, const quint32 dialFlowMLPM, const float dialTempC, const Broadcast_Type BCType) { static quint32 broadcastCounter = 3; if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } QVariantList msg; msg.append(static_cast(ID_TD_TREATMENT_SET_POINTS)); msg.append(Can_Id::eChlid_TD_Sync); msg.append(bloodFlowMLPM); msg.append(dialFlowMLPM); msg.append(dialTempC); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleTreatmentStatesBroadcastData(const QList &txStates, const Broadcast_Type BCType) { static quint32 broadcastCounter = 0; if ((++broadcastCounter % NUN_OF_COUNTS_QART_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } QVariantList msg; msg.append(static_cast(ID_TD_TREATMENT_STATE_DATA)); msg.append(Can_Id::eChlid_TD_Sync); for (auto item: txStates) { msg.append(item);} _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleFluidBolusRequestMessage() { if (! _treatmentRcvdMessages[ID_UI_FLUID_BOLUS_RQST].isNull()){ quint32 fluidBolusCmd = extractU32DataFromReceivedMessage(ID_UI_FLUID_BOLUS_RQST, 0); // Assume the fluid bolus is not in progress _treatmentStatus.fluidBolusState = FLUID_BOLUS_IDLE_STATE; if (fluidBolusCmd == FLUID_BOLUS_START_CMD){ _treatmentStatus.fluidBolusState = FLUID_BOLUS_SALINE_IN_PROGRESS_STATE; _treatmentStatus.dialysisSubState = DIALYSIS_UF_FLUID_BOLUS_STATE; } else { _treatmentStatus.totalFluidBolusDeliveredML += _treatmentStatus.accumFluidBolusDeliveredML; } _treatmentRcvdMessages[ID_UI_FLUID_BOLUS_RQST].clear(); handleUIResponseMessage(ID_TD_FLUID_BOLUS_RESP, ACCEPT_VALUE, {static_cast(REQUEST_REJECT_REASON_NONE), _treatmentParams.fluidBlousVolumeML}); } if (! _treatmentRcvdMessages[ID_UI_BOLUS_VOLUME_CHANGE_RQST].isNull()){ quint32 fluidBolusVolML = extractU32DataFromReceivedMessage(ID_UI_BOLUS_VOLUME_CHANGE_RQST, 0); // TODO Check the range of the requested bolus volume _treatmentParams.fluidBlousVolumeML = fluidBolusVolML; _treatmentRcvdMessages[ID_UI_BOLUS_VOLUME_CHANGE_RQST].clear(); handleUIResponseMessage(ID_TD_BOLUS_VOLUME_CHANGE_RESP, ACCEPT_VALUE); } } void StateController::handleFluidBolusFluidDelivery(const quint32 bloodFlowMLPM) { float timeSinceLastTimeMin = static_cast(QOBJECT_TIMER_TIMEOUT_MS) / static_cast(MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE); _treatmentStatus.accumFluidBolusDeliveredML += static_cast(bloodFlowMLPM) * timeSinceLastTimeMin; float deliveredFluidML = qAbs(_treatmentStatus.totalFluidBolusDeliveredML - _treatmentStatus.accumFluidBolusDeliveredML); if (deliveredFluidML >= _treatmentParams.fluidBlousVolumeML) { _treatmentStatus.fluidBolusState = FLUID_BOLUS_IDLE_STATE; _treatmentStatus.accumFluidBolusDeliveredML = 0.0; _treatmentStatus.totalFluidBolusDeliveredML += _treatmentStatus.accumFluidBolusDeliveredML; } qDebug() << "Bolus" << deliveredFluidML << _treatmentStatus.accumFluidBolusDeliveredML << _treatmentStatus.totalFluidBolusDeliveredML; } void StateController::handleFluidBolusBroadcastData(const Broadcast_Type BCType) { static quint32 broadcastCounter = 5; if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } QVariantList msg; msg.append(static_cast(ID_TD_FLUID_BOLUS_DATA)); msg.append(Can_Id::eChlid_TD_Sync); msg.append(_treatmentParams.fluidBlousVolumeML); msg.append(_treatmentStatus.accumFluidBolusDeliveredML); msg.append(_treatmentStatus.totalFluidBolusDeliveredML); msg.append(ENABLE_BOLUS); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleTDPressureProcessAndBroadcastData(const Broadcast_Type BCType) { static quint32 broadcastCounter = 10; if (! _treatmentRcvdMessages[ID_UI_PRESSURE_LIMIT_WIDEN_RQST].isNull()){ _treatmentRcvdMessages[ID_UI_PRESSURE_LIMIT_WIDEN_RQST].clear(); // TODo address the increase per request handleUIResponseMessage(ID_TD_PRESSURE_LIMIT_WIDEN_RESP, ACCEPT_VALUE); } // TODO update the pressures float h2ArtPresMMHG = 12.2; float h14VenPresMMHG = 80.1; quint32 presLimitState = 1; qint32 h2ArtMinLimitMMHG = MIN_ART_PRES_LIMIT_MMHG; qint32 h2ArtMaxLimitMMHG = MAX_ART_PRES_LIMIT_MMHG; qint32 h14VenMinLimitMMGH = MIN_VEN_PRES_LIMIT_MMHG; qint32 h14VenMaxLimitMMHG = MAX_VEN_PRES_LIMIT_MMHG; float h2ArtLongFilterPresMMHG = -23.0; float h14VenLongFilterPresMMHG = 67.5; float tmpPresMMHG = 45.03; float tmpLongFilter = 45.5; float tmpMinLimitMMHG = MIN_TMP_PRES_LIMIT_MMHG; float tmpMaxLimitMMHG = MAX_TMP_PRES_LIMIT_MMHG; float h23BaroPresPSI = 14.2l; if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } QVariantList msg; msg.append(static_cast(ID_TD_PRESSURE_DATA)); msg.append(Can_Id::eChlid_TD_Sync); msg.append(h2ArtPresMMHG); msg.append(h14VenPresMMHG); msg.append(presLimitState); msg.append(h2ArtMinLimitMMHG); msg.append(h2ArtMaxLimitMMHG); msg.append(h14VenMinLimitMMGH); msg.append(h14VenMaxLimitMMHG); msg.append(h2ArtLongFilterPresMMHG); msg.append(h14VenLongFilterPresMMHG); msg.append(tmpPresMMHG); msg.append(tmpLongFilter); msg.append(tmpMinLimitMMHG); msg.append(tmpMaxLimitMMHG); msg.append(h23BaroPresPSI); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleTreatmentTimeProcessAndBroadcastData(const Broadcast_Type BCType) { static quint32 broadcastCounter = 12; if (_treatmentStatus.fluidBolusState == FLUID_BOLUS_IDLE_STATE) { _treatmentStatus.treatmentElapsedTimeMS += QOBJECT_TIMER_TIMEOUT_MS; } if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } quint32 prescribedTimeS = _treatmentParams.treatmentDurationMin * SECONDS_PER_MINUTE; quint32 elapsedTimeS = _treatmentStatus.treatmentElapsedTimeMS / MILLISECONDS_PER_SECOND; quint32 remainingTimeS = prescribedTimeS - elapsedTimeS; //qDebug() << "Times" << prescribedTimeS << elapsedTimeS << remainingTimeS; QVariantList msg; msg.append(static_cast(ID_TD_TREATMENT_TIME_DATA)); msg.append(Can_Id::eChlid_TD_Sync); msg.append(prescribedTimeS); msg.append(elapsedTimeS); msg.append(remainingTimeS); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleTreatmentPrescriptionEditRequestMessage() { if (! _treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINTS_CHANGE_RQST].isNull()){ // TODO range check the parameters QVariantList resp; resp.append(static_cast(ID_TD_TREATMENT_SET_POINTS_CHANGE_RESP)); resp.append(Can_Id::eChlid_TD_UI); resp.append(ACCEPT_VALUE); QVariant payload = _treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINTS_CHANGE_RQST]; qint32 index = 0; Types::U32 u32Var; Types::F32 f32Var; GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.bloodFlowRateMLPM = u32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.dialysateFlowRateMLPM = u32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, f32Var); _treatmentParams.dialysateTemperatureC = f32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.acidConcentrate = u32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, f32Var); _treatmentParams.acidKConcentrateConversionFactor = f32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.bicarbobateMEQL = u32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.treatmentModality = u32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.hepatitisBStatus = u32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.sodiumMEQL = u32Var.value; resp.append(0); GetValue(payload.toByteArray(), index, u32Var); _treatmentParams.bicarbobateMEQL = u32Var.value; resp.append(0); _isSendListReady = false; _sendMessages.append(resp); _isSendListReady = true; _treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINTS_CHANGE_RQST].clear(); } } void StateController::handleSetPointBloodFlowChange(quint32 &bloodFlowMLPM, bool &hasBloodFlowChanged) { if (_treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_RQST].isNull()) { return; } // TODO do we need to check the range bloodFlowMLPM = extractU32DataFromReceivedMessage(ID_UI_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_RQST, 0); handleUIResponseMessage(ID_TD_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_RESP, ACCEPT_VALUE); hasBloodFlowChanged = true; handleTreatmentSetpointsBroadcastData(bloodFlowMLPM, _treatmentParams.dialysateFlowRateMLPM, _treatmentParams.dialysateTemperatureC, BC_IMMEDIATELY); _treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_RQST].clear(); } void StateController::handleDDGenDialysateModeBroadcastData(const Broadcast_Type BCType) { static quint32 broadcastCounter = 15; if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } quint32 dummyData = 1; float dummyFloat = 150.2; QVariantList msg; msg.append(static_cast(ID_DD_GEN_DIALYSATE_MODE_DATA)); msg.append(Can_Id::eChlid_DD_Sync); msg.append(dummyData); msg.append(dummyData); msg.append(ALLOW_DIALYSATE_DELIVER); msg.append(dummyFloat); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleSetPointDialysateFlowChange() { if (_treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINT_DIALYSATE_FLOW_CHANGE_RQST].isNull()) { return; } // TODO do we need to check the range _treatmentParams.dialysateFlowRateMLPM = extractU32DataFromReceivedMessage(ID_UI_TREATMENT_SET_POINT_DIALYSATE_FLOW_CHANGE_RQST, 0); handleUIResponseMessage(ID_TD_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_RESP, ACCEPT_VALUE); handleTreatmentSetpointsBroadcastData(_treatmentParams.bloodFlowRateMLPM, _treatmentParams.dialysateFlowRateMLPM, _treatmentParams.dialysateTemperatureC, BC_IMMEDIATELY); _treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINT_DIALYSATE_FLOW_CHANGE_RQST].clear(); } void StateController::handleDDConductivityBroadcastData(const Broadcast_Type BCType) { static quint32 broadcastCounter = 22; if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } float d27Cond = 250.63; QVariantList msg; msg.append(static_cast(ID_DD_CONDUCTIVITY_DATA)); msg.append(Can_Id::eChlid_DD_Sync); msg.append(0.0); msg.append(d27Cond); msg.append(0.0); msg.append(0.0); msg.append(0.0); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleTDBloodPressureBroadcastData(const Broadcast_Type BCType) { static quint32 broadcastCounter = 22; if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } // TODO copmlete } void StateController::handleUltrafiltrationBroadcastData(const Broadcast_Type BCType) { static quint32 broadcastCounter = 31; if ((++broadcastCounter % NUM_OF_COUNTS_TIMER_BC_EMIT != 0) && (BCType == BC_DELAYED)) { return; } float presUFVolL = _treatmentParams.ufVolumeL; float presUFRateLHR = presUFVolL / (static_cast(_treatmentParams.treatmentDurationMin) / static_cast(MINUTES_PER_HOUR)); float targetUFRateLHR = (_treatmentStatus.presUFRateMLPM * MINUTES_PER_HOUR) / static_cast(MILLILITERS_PER_LITER); float UFVolLMS = presUFRateLHR / (static_cast(MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND)); _treatmentStatus.ufVolDeliveredL += UFVolLMS * PROGRESS_TIME_BC_INTERVAL_MS; //qDebug() << "UF" << presUFRateLHR << targetUFRateLHR << UFVolLMS << _treatmentStatus.ufVolDeliveredL; QVariantList msg; msg.append(static_cast(ID_TD_ULTRAFILTRATION_DATA)); msg.append(Can_Id::eChlid_TD_Sync); msg.append(presUFVolL); msg.append(targetUFRateLHR); msg.append(_treatmentStatus.ufVolDeliveredL); msg.append(static_cast(_treatmentStatus.dialysisSubState)); _isBroadcastListReady = false; _broadcastMessages.append(msg); _isBroadcastListReady = true; } void StateController::handleSetPointDialysateTemperatureChange() { if (_treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINT_DIALYSATE_TEMP_CHANGE_RQST].isNull()){ return; } // TODO range check _treatmentParams.dialysateTemperatureC = extractF32DataFromReceivedMessage(ID_UI_TREATMENT_SET_POINT_DIALYSATE_TEMP_CHANGE_RQST, 0); handleUIResponseMessage(ID_TD_TREATMENT_SET_POINT_DIALYSATE_TEMP_CHANGE_RESP, ACCEPT_VALUE); handleTreatmentSetpointsBroadcastData(_treatmentParams.bloodFlowRateMLPM, _treatmentParams.dialysateFlowRateMLPM, _treatmentParams.dialysateTemperatureC, BC_IMMEDIATELY); _treatmentRcvdMessages[ID_UI_TREATMENT_SET_POINT_DIALYSATE_TEMP_CHANGE_RQST].clear(); } void StateController::handleUltrafiltrationPauseResume() { if (_treatmentRcvdMessages[ID_UI_UF_PAUSE_RESUME_RQST].isNull()){ return; } quint32 cmd = extractU32DataFromReceivedMessage(ID_UI_UF_PAUSE_RESUME_RQST, 0); QVariantList reason; if (_treatmentStatus.txState == TREAT_DIALYSIS_STATE) { // Assume the default state is UF state _treatmentStatus.dialysisSubState = DIALYSIS_UF_STATE; _treatmentStatus.dialysisSubState = (cmd == 0 ? DIALYSIS_UF_PAUSED_STATE : _treatmentStatus.dialysisSubState); } else if (_treatmentStatus.txState != TREAT_DIALYSIS_STATE) { reason.append(static_cast(REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE)); } else { quint32 r = static_cast(REQUEST_REJECT_REASON_UF_NOT_PAUSED); r = (cmd == 0 ? static_cast(REQUEST_REJECT_REASON_UF_NOT_IN_PROGESS) : r); reason.append(r); } handleUIResponseMessage(ID_TD_UF_PAUSE_RESUME_RESP, ACCEPT_VALUE, reason); _treatmentRcvdMessages[ID_UI_UF_PAUSE_RESUME_RQST].clear(); } void StateController::handleUltrafiltrationVolumeChangeRequest() { }