wizard injection

This commit is contained in:
Milos Kozak 2019-12-31 11:57:58 +01:00
parent 1610470089
commit 7518537108
48 changed files with 614 additions and 641 deletions

View file

@ -14,7 +14,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
import androidx.test.rule.GrantPermissionRule
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin
@ -27,7 +26,7 @@ import info.nightscout.androidaps.plugins.source.RandomBgPlugin
import info.nightscout.androidaps.setupwizard.SetupWizardActivity
import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.SP
import info.nightscout.androidaps.utils.isRunningTest
import info.nightscout.androidaps.utils.extensions.isRunningTest
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Description
import org.hamcrest.Matcher

View file

@ -1,238 +0,0 @@
package info.nightscout.androidaps.data;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.interfaces.TreatmentsInterface;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.BolusWizard;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.SP;
/**
* Created by mike on 25.12.2017.
*/
public class QuickWizardEntry {
private static Logger log = LoggerFactory.getLogger(QuickWizardEntry.class);
public JSONObject storage;
public int position;
public static final int YES = 0;
public static final int NO = 1;
public static final int POSITIVE_ONLY = 2;
public static final int NEGATIVE_ONLY = 3;
/*
{
buttonText: "Meal",
carbs: 36,
validFrom: 8 * 60 * 60, // seconds from midnight
validTo: 9 * 60 * 60, // seconds from midnight
useBG: 0,
useCOB: 0,
useBolusIOB: 0,
useBasalIOB: 0,
useTrend: 0,
useSuperBolus: 0,
useTemptarget: 0
}
*/
QuickWizardEntry() {
String emptyData = "{\"buttonText\":\"\",\"carbs\":0,\"validFrom\":0,\"validTo\":86340}";
try {
storage = new JSONObject(emptyData);
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
position = -1;
}
QuickWizardEntry(JSONObject entry, int position) {
storage = entry;
this.position = position;
}
Boolean isActive() {
return Profile.secondsFromMidnight() >= validFrom() && Profile.secondsFromMidnight() <= validTo();
}
public BolusWizard doCalc(Profile profile, String profileName, BgReading lastBG, boolean _synchronized) {
final TempTarget tempTarget = TreatmentsPlugin.getPlugin().getTempTargetFromHistory();
//BG
double bg = 0;
if (lastBG != null && useBG() == YES) {
bg = lastBG.valueToUnits(ProfileFunctions.getSystemUnits());
}
// COB
double cob = 0d;
if (useCOB() == YES) {
CobInfo cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(_synchronized, "QuickWizard COB");
if (cobInfo.displayCob != null)
cob = cobInfo.displayCob;
}
// Bolus IOB
boolean bolusIOB = false;
if (useBolusIOB() == YES) {
bolusIOB = true;
}
// Basal IOB
TreatmentsInterface treatments = TreatmentsPlugin.getPlugin();
treatments.updateTotalIOBTempBasals();
IobTotal basalIob = treatments.getLastCalculationTempBasals().round();
boolean basalIOB = false;
if (useBasalIOB() == YES) {
basalIOB = true;
} else if (useBasalIOB() == POSITIVE_ONLY && basalIob.iob > 0) {
basalIOB = true;
} else if (useBasalIOB() == NEGATIVE_ONLY && basalIob.iob < 0) {
basalIOB = true;
}
// SuperBolus
boolean superBolus = false;
if (useSuperBolus() == YES && SP.getBoolean(R.string.key_usesuperbolus, false)) {
superBolus = true;
}
final LoopPlugin loopPlugin = LoopPlugin.getPlugin();
if (loopPlugin.isEnabled(loopPlugin.getType()) && loopPlugin.isSuperBolus())
superBolus = false;
// Trend
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
boolean trend = false;
if (useTrend() == YES) {
trend = true;
} else if (useTrend() == POSITIVE_ONLY && glucoseStatus != null && glucoseStatus.short_avgdelta > 0) {
trend = true;
} else if (useTrend() == NEGATIVE_ONLY && glucoseStatus != null && glucoseStatus.short_avgdelta < 0) {
trend = true;
}
double percentage = SP.getDouble(R.string.key_boluswizard_percentage, 100.0);
return new BolusWizard(profile, profileName, tempTarget, carbs(), cob, bg, 0d, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard");
}
public String buttonText() {
try {
return storage.getString("buttonText");
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
return "";
}
public Integer carbs() {
try {
return storage.getInt("carbs");
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
return 0;
}
public Date validFromDate() {
return DateUtil.toDate(validFrom());
}
public Date validToDate() {
return DateUtil.toDate(validTo());
}
public Integer validFrom() {
try {
return storage.getInt("validFrom");
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
return 0;
}
public Integer validTo() {
try {
return storage.getInt("validTo");
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
return 0;
}
public int useBG() {
try {
return storage.getInt("useBG");
} catch (JSONException e) {
//log.error("Unhandled exception", e);
}
return YES;
}
public int useCOB() {
try {
return storage.getInt("useCOB");
} catch (JSONException e) {
//log.error("Unhandled exception", e);
}
return NO;
}
public int useBolusIOB() {
try {
return storage.getInt("useBolusIOB");
} catch (JSONException e) {
//log.error("Unhandled exception", e);
}
return YES;
}
public int useBasalIOB() {
try {
return storage.getInt("useBasalIOB");
} catch (JSONException e) {
//log.error("Unhandled exception", e);
}
return YES;
}
public int useTrend() {
try {
return storage.getInt("useTrend");
} catch (JSONException e) {
//log.error("Unhandled exception", e);
}
return NO;
}
public int useSuperBolus() {
try {
return storage.getInt("useSuperBolus");
} catch (JSONException e) {
//log.error("Unhandled exception", e);
}
return NO;
}
public int useTempTarget() {
try {
return storage.getInt("useTempTarget");
} catch (JSONException e) {
//log.error("Unhandled exception", e);
}
return NO;
}
}

View file

@ -9,6 +9,8 @@ import info.nightscout.androidaps.plugins.aps.openAPSMA.LoggerCallback
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.*
import info.nightscout.androidaps.plugins.general.automation.actions.ActionSendSMS
import info.nightscout.androidaps.queue.commands.CommandSetProfile
import info.nightscout.androidaps.utils.wizard.BolusWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
import javax.inject.Singleton
@Singleton
@ -36,6 +38,8 @@ interface AppComponent : AndroidInjector<MainApp> {
fun injectObjective6(objective6: Objective6)
fun injectLoggerCallback(loggerCallback: LoggerCallback)
fun injectBolusWizard(bolusWizard: BolusWizard)
fun injectQuickWizardEntry(quickWizardEntry: QuickWizardEntry)
@Component.Builder
interface Builder {

View file

@ -22,6 +22,8 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.resources.ResourceHelperImplementation
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.sharedPreferences.SPImplementation
import info.nightscout.androidaps.utils.wizard.BolusWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
import javax.inject.Singleton
@Module(includes = [AppModule.AppBindings::class])
@ -67,6 +69,8 @@ open class AppModule {
@ContributesAndroidInjector fun objective5Injector(): Objective5
@ContributesAndroidInjector fun objective6Injector(): Objective6
@ContributesAndroidInjector fun loggerCallbackInjector(): LoggerCallback
@ContributesAndroidInjector fun loggerBolusWizard(): BolusWizard
@ContributesAndroidInjector fun loggerQuickWizardEntry(): QuickWizardEntry
@Binds fun bindContext(mainApp: MainApp): Context
}

View file

@ -24,6 +24,7 @@ import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.extensions.toSignedString
import info.nightscout.androidaps.utils.resources.ResourceHelper
import kotlinx.android.synthetic.main.dialog_insulin.*
import kotlinx.android.synthetic.main.notes.*
@ -91,19 +92,19 @@ class InsulinDialog : DialogFragmentWithDate() {
overview_insulin_amount.setParams(savedInstanceState?.getDouble("overview_insulin_amount")
?: 0.0, 0.0, maxInsulin, configBuilderPlugin.activePump!!.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, ok, textWatcher)
overview_insulin_plus05.text = toSignedString(sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT))
overview_insulin_plus05.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString()
overview_insulin_plus05.setOnClickListener {
overview_insulin_amount.value = max(0.0, overview_insulin_amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT))
validateInputs()
}
overview_insulin_plus10.text = toSignedString(sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT))
overview_insulin_plus10.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString()
overview_insulin_plus10.setOnClickListener {
overview_insulin_amount.value = max(0.0, overview_insulin_amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT))
validateInputs()
}
overview_insulin_plus20.text = toSignedString(sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT))
overview_insulin_plus20.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString()
overview_insulin_plus20.setOnClickListener {
overview_insulin_amount.value = Math.max(0.0, overview_insulin_amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT))
@ -116,11 +117,6 @@ class InsulinDialog : DialogFragmentWithDate() {
}
}
private fun toSignedString(value: Double): String {
val formatted = DecimalFormatter.toPumpSupportedBolus(value)
return if (value > 0) "+$formatted" else formatted
}
override fun submit(): Boolean {
val pumpDescription = configBuilderPlugin.activePump?.pumpDescription
?: return false

View file

@ -32,6 +32,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.wizard.BolusWizard
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.dialog_wizard.*
@ -294,7 +295,7 @@ class WizardDialog : DaggerDialogFragment() {
val carbTime = SafeParse.stringToInt(treatments_wizard_carb_time_input.text)
wizard = BolusWizard(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction,
wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction,
sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(),
treatments_wizard_bgcheckbox.isChecked,
treatments_wizard_cobcheckbox.isChecked,

View file

@ -13,7 +13,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers

View file

@ -15,7 +15,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -12,7 +12,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.openapsama_fragment.*

View file

@ -16,7 +16,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -14,7 +14,7 @@ import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.PasswordProtection
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -29,6 +29,7 @@ import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Obje
import info.nightscout.androidaps.receivers.NetworkChangeReceiver
import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers

View file

@ -12,7 +12,7 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.constraints.objectives.events.EventNtpStatus
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -26,7 +26,7 @@ import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.SP
import info.nightscout.androidaps.utils.SingleClickButton
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.toVisibility
import io.reactivex.android.schedulers.AndroidSchedulers

View file

@ -18,7 +18,7 @@ import info.nightscout.androidaps.plugins.general.automation.events.EventAutomat
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.automation_fragment.*

View file

@ -27,7 +27,7 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import org.json.JSONArray

View file

@ -18,7 +18,7 @@ import info.nightscout.androidaps.plugins.general.automation.events.EventAutomat
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.automation_dialog_event.*

View file

@ -51,8 +51,8 @@ import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.QuickWizard;
import info.nightscout.androidaps.data.QuickWizardEntry;
import info.nightscout.androidaps.utils.wizard.QuickWizard;
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.ExtendedBolus;
@ -109,7 +109,7 @@ import info.nightscout.androidaps.plugins.source.DexcomPlugin;
import info.nightscout.androidaps.plugins.source.XdripPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.BolusWizard;
import info.nightscout.androidaps.utils.wizard.BolusWizard;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.DefaultValueHelper;

View file

@ -13,7 +13,7 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.SP
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import javax.inject.Inject

View file

@ -11,13 +11,13 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.data.QuickWizard
import info.nightscout.androidaps.utils.wizard.QuickWizard
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog
import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -10,8 +10,8 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.QuickWizard
import info.nightscout.androidaps.data.QuickWizardEntry
import info.nightscout.androidaps.utils.wizard.QuickWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange

View file

@ -11,7 +11,7 @@ import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSm
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.smscommunicator_fragment.*

View file

@ -40,6 +40,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorP
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable

View file

@ -13,7 +13,7 @@ import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolD
import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolResetData
import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolUpdateGUI
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -29,7 +29,7 @@ import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable

View file

@ -36,6 +36,7 @@ import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.wizard.BolusWizard
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.SimpleDateFormat
@ -181,7 +182,7 @@ class ActionStringHandler @Inject constructor(
}
val format = DecimalFormat("0.00")
val formatInt = DecimalFormat("0")
val bolusWizard = BolusWizard(profile, profileName, treatmentsPlugin.tempTargetFromHistory,
val bolusWizard = BolusWizard(mainApp).doCalc(profile, profileName, treatmentsPlugin.tempTargetFromHistory,
carbsAfterConstraints, cobInfo.displayCob!!, bgReading!!.valueToUnits(profileFunction.getUnits()),
0.0, percentage.toDouble(), useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend)
if (Math.abs(bolusWizard.insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= 0.01) {

View file

@ -18,7 +18,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutos
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable

View file

@ -18,6 +18,7 @@ import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.insulin.InsulinOrefBasePlugin.Companion.MIN_DIA
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -16,7 +16,7 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.OKDialog
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -29,7 +29,7 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.SetWarnColor
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -30,7 +30,7 @@ import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -20,7 +20,7 @@ import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -39,7 +39,7 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.SetWarnColor
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -14,7 +14,7 @@ import info.nightscout.androidaps.plugins.pump.virtual.events.EventVirtualPumpUp
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -33,7 +33,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.InstanceId.instanceId
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.disposables.CompositeDisposable

View file

@ -14,7 +14,7 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.isRunningTest
import info.nightscout.androidaps.utils.extensions.isRunningTest
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton

View file

@ -13,7 +13,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.treatments.fragments.*
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.plusAssign
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable

View file

@ -32,7 +32,7 @@ import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
import info.nightscout.androidaps.utils.AndroidPermission
import info.nightscout.androidaps.utils.LocaleHelper.update
import info.nightscout.androidaps.utils.PasswordProtection
import info.nightscout.androidaps.utils.isRunningTest
import info.nightscout.androidaps.utils.extensions.isRunningTest
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.*

View file

@ -1,349 +0,0 @@
package info.nightscout.androidaps.utils
import android.content.Context
import android.content.Intent
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.PumpDescription
import info.nightscout.androidaps.interfaces.PumpInterface
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import org.json.JSONException
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.util.*
import kotlin.math.abs
class BolusWizard @JvmOverloads constructor(val profile: Profile,
val profileName: String,
val tempTarget: TempTarget?,
val carbs: Int,
val cob: Double,
val bg: Double,
val correction: Double,
private val percentageCorrection: Double = 100.0,
private val useBg: Boolean,
private val useCob: Boolean,
private val includeBolusIOB: Boolean,
private val includeBasalIOB: Boolean,
private val useSuperBolus: Boolean,
private val useTT: Boolean,
private val useTrend: Boolean,
val notes: String = "",
private val carbTime: Int = 0
) {
private val log = LoggerFactory.getLogger(L.CORE)
// Intermediate
var sens = 0.0
private set
var ic = 0.0
private set
var glucoseStatus: GlucoseStatus? = null
private set
private var targetBGLow = 0.0
private var targetBGHigh = 0.0
private var bgDiff = 0.0
var insulinFromBG = 0.0
private set
var insulinFromCarbs = 0.0
private set
var insulinFromBolusIOB = 0.0
private set
var insulinFromBasalsIOB = 0.0
private set
var insulinFromCorrection = 0.0
private set
var insulinFromSuperBolus = 0.0
private set
var insulinFromCOB = 0.0
private set
var insulinFromTrend = 0.0
private set
var trend = 0.0
private set
private var accepted = false
// Result
var calculatedTotalInsulin: Double = 0.0
private set
var totalBeforePercentageAdjustment: Double = 0.0
private set
var carbsEquivalent: Double = 0.0
private set
var insulinAfterConstraints: Double = 0.0
private set
init {
doCalc()
}
private fun doCalc() {
// Insulin from BG
sens = Profile.fromMgdlToUnits(profile.isfMgdl, ProfileFunctions.getSystemUnits())
targetBGLow = Profile.fromMgdlToUnits(profile.targetLowMgdl, ProfileFunctions.getSystemUnits())
targetBGHigh = Profile.fromMgdlToUnits(profile.targetHighMgdl, ProfileFunctions.getSystemUnits())
if (useTT && tempTarget != null) {
targetBGLow = Profile.fromMgdlToUnits(tempTarget.low, ProfileFunctions.getSystemUnits())
targetBGHigh = Profile.fromMgdlToUnits(tempTarget.high, ProfileFunctions.getSystemUnits())
}
if (useBg && bg > 0) {
bgDiff = when {
bg in targetBGLow..targetBGHigh -> 0.0
bg <= targetBGLow -> bg - targetBGLow
else -> bg - targetBGHigh
}
insulinFromBG = bgDiff / sens
}
// Insulin from 15 min trend
glucoseStatus = GlucoseStatus.getGlucoseStatusData()
glucoseStatus?.let {
if (useTrend) {
trend = it.short_avgdelta
insulinFromTrend = Profile.fromMgdlToUnits(trend, ProfileFunctions.getSystemUnits()) * 3 / sens
}
}
// Insulin from carbs
ic = profile.ic
insulinFromCarbs = carbs / ic
insulinFromCOB = if (useCob) (cob / ic) else 0.0
// Insulin from IOB
// IOB calculation
val treatments = TreatmentsPlugin.getPlugin()
treatments.updateTotalIOBTreatments()
val bolusIob = treatments.lastCalculationTreatments.round()
treatments.updateTotalIOBTempBasals()
val basalIob = treatments.lastCalculationTempBasals.round()
insulinFromBolusIOB = if (includeBolusIOB) -bolusIob.iob else 0.0
insulinFromBasalsIOB = if (includeBasalIOB) -basalIob.basaliob else 0.0
// Insulin from correction
insulinFromCorrection = correction
// Insulin from superbolus for 2h. Get basal rate now and after 1h
if (useSuperBolus) {
insulinFromSuperBolus = profile.basal
var timeAfter1h = System.currentTimeMillis()
timeAfter1h += T.hours(1).msecs()
insulinFromSuperBolus += profile.getBasal(timeAfter1h)
}
// Total
calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB
// Percentage adjustment
totalBeforePercentageAdjustment = calculatedTotalInsulin
if (calculatedTotalInsulin > 0) {
calculatedTotalInsulin = calculatedTotalInsulin * percentageCorrection / 100.0
}
if (calculatedTotalInsulin < 0) {
carbsEquivalent = (-calculatedTotalInsulin) * ic
calculatedTotalInsulin = 0.0
}
val bolusStep = ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.bolusStep
?: 0.1
calculatedTotalInsulin = Round.roundTo(calculatedTotalInsulin, bolusStep)
insulinAfterConstraints = ConstraintChecker.instance.applyBolusConstraints(Constraint(calculatedTotalInsulin)).value()
log.debug(this.toString())
}
private fun nsJSON(): JSONObject {
val boluscalcJSON = JSONObject()
try {
boluscalcJSON.put("profile", profileName)
boluscalcJSON.put("notes", notes)
boluscalcJSON.put("eventTime", DateUtil.toISOString(Date()))
boluscalcJSON.put("targetBGLow", targetBGLow)
boluscalcJSON.put("targetBGHigh", targetBGHigh)
boluscalcJSON.put("isf", sens)
boluscalcJSON.put("ic", ic)
boluscalcJSON.put("iob", -(insulinFromBolusIOB + insulinFromBasalsIOB))
boluscalcJSON.put("bolusiob", insulinFromBolusIOB)
boluscalcJSON.put("basaliob", insulinFromBasalsIOB)
boluscalcJSON.put("bolusiobused", includeBolusIOB)
boluscalcJSON.put("basaliobused", includeBasalIOB)
boluscalcJSON.put("bg", bg)
boluscalcJSON.put("insulinbg", insulinFromBG)
boluscalcJSON.put("insulinbgused", useBg)
boluscalcJSON.put("bgdiff", bgDiff)
boluscalcJSON.put("insulincarbs", insulinFromCarbs)
boluscalcJSON.put("carbs", carbs)
boluscalcJSON.put("cob", cob)
boluscalcJSON.put("cobused", useCob)
boluscalcJSON.put("insulincob", insulinFromCOB)
boluscalcJSON.put("othercorrection", correction)
boluscalcJSON.put("insulinsuperbolus", insulinFromSuperBolus)
boluscalcJSON.put("insulintrend", insulinFromTrend)
boluscalcJSON.put("insulin", calculatedTotalInsulin)
boluscalcJSON.put("superbolusused", useSuperBolus)
boluscalcJSON.put("insulinsuperbolus", insulinFromSuperBolus)
boluscalcJSON.put("trendused", useTrend)
boluscalcJSON.put("insulintrend", insulinFromTrend)
boluscalcJSON.put("trend", trend)
boluscalcJSON.put("ttused", useTT)
boluscalcJSON.put("percentageCorrection", percentageCorrection)
} catch (e: JSONException) {
log.error("Unhandled exception", e)
}
return boluscalcJSON
}
private fun confirmMessageAfterConstraints(pump: PumpInterface): String {
var confirmMessage = ""
if (insulinAfterConstraints > 0) {
val pct = if (percentageCorrection != 100.0) " (" + percentageCorrection.toInt() + "%)" else ""
confirmMessage += "<br/>" + MainApp.gs(R.string.bolus) + ": " + "<font color='" + MainApp.gc(R.color.bolus) + "'>" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + pct + "</font>"
}
if (carbs > 0) {
var timeShift = ""
if (carbTime > 0) {
timeShift += " ( +" + MainApp.gs(R.string.mins, carbTime) + " )"
} else if (carbTime < 0) {
timeShift += " ( -" + MainApp.gs(R.string.mins, carbTime) + " )"
}
confirmMessage += "<br/>" + MainApp.gs(R.string.carbs) + ": " + "<font color='" + MainApp.gc(R.color.carbs) + "'>" + carbs + "g" + timeShift + "</font>"
}
if (insulinFromCOB > 0) {
confirmMessage += "<br/>" + MainApp.gs(R.string.insulinFromCob, MainApp.gc(R.color.cobAlert), insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCOB + insulinFromBG)
val absorptionRate = IobCobCalculatorPlugin.getPlugin().slowAbsorptionPercentage(60)
if (absorptionRate > .25)
confirmMessage += "<br/>" + MainApp.gs(R.string.slowabsorptiondetected, MainApp.gc(R.color.cobAlert), (absorptionRate * 100).toInt())
}
if (abs(insulinAfterConstraints - calculatedTotalInsulin) > pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) {
confirmMessage += "<br/>" + MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), calculatedTotalInsulin, insulinAfterConstraints)
}
return confirmMessage
}
fun confirmAndExecute(context: Context) {
val profile = ProfileFunctions.getInstance().getProfile() ?: return
val pump = ConfigBuilderPlugin.getPlugin().activePump ?: return
if (calculatedTotalInsulin > 0.0 || carbs > 0.0) {
if (accepted) {
log.debug("guarding: already accepted")
return
}
accepted = true
val confirmMessage = confirmMessageAfterConstraints(pump)
OKDialog.showConfirmation(context, MainApp.gs(R.string.boluswizard), HtmlHelper.fromHtml(confirmMessage), Runnable {
if (insulinAfterConstraints > 0 || carbs > 0) {
if (useSuperBolus) {
val loopPlugin = LoopPlugin.getPlugin()
if (loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000)
RxBus.INSTANCE.send(EventRefreshOverview("WizardDialog"))
}
val pump1 = ConfigBuilderPlugin.getPlugin().activePump
if (pump1?.pumpDescription?.tempBasalStyle == PumpDescription.ABSOLUTE) {
ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
MainApp.instance().startActivity(i)
}
}
})
} else {
ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", MainApp.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
MainApp.instance().startActivity(i)
}
}
})
}
}
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD
detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.carbs = carbs.toDouble()
detailedBolusInfo.context = context
detailedBolusInfo.glucose = bg
detailedBolusInfo.glucoseType = "Manual"
detailedBolusInfo.carbTime = carbTime
detailedBolusInfo.boluscalc = nsJSON()
detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes
if (detailedBolusInfo.insulin > 0 || ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.storesCarbInfo == true) {
ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(MainApp.instance(), ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
MainApp.instance().startActivity(i)
}
}
})
} else {
TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false)
}
}
})
}
}
}

View file

@ -89,8 +89,12 @@ object JsonHelper {
}
@JvmStatic
fun safeGetInt(json: JSONObject?, fieldName: String): Int {
var result = 0
fun safeGetInt(json: JSONObject?, fieldName: String): Int =
safeGetInt(json, fieldName, 0)
@JvmStatic
fun safeGetInt(json: JSONObject?, fieldName: String, defaultValue: Int): Int {
var result = defaultValue
if (json != null && json.has(fieldName)) {
try {
result = json.getInt(fieldName)

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.utils
package info.nightscout.androidaps.utils.extensions
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable

View file

@ -0,0 +1,9 @@
package info.nightscout.androidaps.utils.extensions
import info.nightscout.androidaps.utils.DecimalFormatter
fun Double.toSignedString(): String {
val formatted = DecimalFormatter.toPumpSupportedBolus(this)
return if (this > 0) "+$formatted" else formatted
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.utils
package info.nightscout.androidaps.utils.extensions
@Synchronized
fun isRunningTest(): Boolean {

View file

@ -0,0 +1,386 @@
package info.nightscout.androidaps.utils.wizard
import android.content.Context
import android.content.Intent
import android.text.Spanned
import com.google.common.base.Joiner
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.PumpDescription
import info.nightscout.androidaps.interfaces.PumpInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.OKDialog
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
import kotlin.math.abs
class BolusWizard @Inject constructor(
private val mainApp: MainApp
) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
init {
mainApp.androidInjector().inject(this)
}
// Intermediate
var sens = 0.0
private set
var ic = 0.0
private set
var glucoseStatus: GlucoseStatus? = null
private set
private var targetBGLow = 0.0
private var targetBGHigh = 0.0
private var bgDiff = 0.0
var insulinFromBG = 0.0
private set
var insulinFromCarbs = 0.0
private set
var insulinFromBolusIOB = 0.0
private set
var insulinFromBasalsIOB = 0.0
private set
var insulinFromCorrection = 0.0
private set
var insulinFromSuperBolus = 0.0
private set
var insulinFromCOB = 0.0
private set
var insulinFromTrend = 0.0
private set
var trend = 0.0
private set
private var accepted = false
// Result
var calculatedTotalInsulin: Double = 0.0
private set
var totalBeforePercentageAdjustment: Double = 0.0
private set
var carbsEquivalent: Double = 0.0
private set
var insulinAfterConstraints: Double = 0.0
private set
// Input
lateinit var profile: Profile
lateinit var profileName: String
var tempTarget: TempTarget? = null
var carbs: Int = 0
var cob: Double = 0.0
var bg: Double = 0.0
var correction: Double = 0.0
private var percentageCorrection: Double = 0.0
private var useBg: Boolean = false
private var useCob: Boolean = false
private var includeBolusIOB: Boolean = false
private var includeBasalIOB: Boolean = false
private var useSuperBolus: Boolean = false
private var useTT: Boolean = false
private var useTrend: Boolean = false
var notes: String = ""
var carbTime: Int = 0
@JvmOverloads
fun doCalc(profile: Profile,
profileName: String,
tempTarget: TempTarget?,
carbs: Int,
cob: Double,
bg: Double,
correction: Double,
percentageCorrection: Double = 100.0,
useBg: Boolean,
useCob: Boolean,
includeBolusIOB: Boolean,
includeBasalIOB: Boolean,
useSuperBolus: Boolean,
useTT: Boolean,
useTrend: Boolean,
notes: String = "",
carbTime: Int = 0
): BolusWizard {
this.profile = profile
this.profileName = profileName
this.tempTarget = tempTarget
this.carbs = carbs
this.cob = cob
this.bg = bg
this.correction = correction
this.percentageCorrection = percentageCorrection
this.useBg = useBg
this.useCob = useCob
this.includeBolusIOB = includeBolusIOB
this.includeBasalIOB = includeBasalIOB
this.useSuperBolus = useSuperBolus
this.useTT = useTT
this.useTrend = useTrend
this.notes = notes
this.carbTime = carbTime
// Insulin from BG
sens = Profile.fromMgdlToUnits(profile.isfMgdl, profileFunction.getUnits())
targetBGLow = Profile.fromMgdlToUnits(profile.targetLowMgdl, profileFunction.getUnits())
targetBGHigh = Profile.fromMgdlToUnits(profile.targetHighMgdl, profileFunction.getUnits())
if (useTT && tempTarget != null) {
targetBGLow = Profile.fromMgdlToUnits(tempTarget.low, profileFunction.getUnits())
targetBGHigh = Profile.fromMgdlToUnits(tempTarget.high, profileFunction.getUnits())
}
if (useBg && bg > 0) {
bgDiff = when {
bg in targetBGLow..targetBGHigh -> 0.0
bg <= targetBGLow -> bg - targetBGLow
else -> bg - targetBGHigh
}
insulinFromBG = bgDiff / sens
}
// Insulin from 15 min trend
glucoseStatus = GlucoseStatus.getGlucoseStatusData()
glucoseStatus?.let {
if (useTrend) {
trend = it.short_avgdelta
insulinFromTrend = Profile.fromMgdlToUnits(trend, profileFunction.getUnits()) * 3 / sens
}
}
// Insulin from carbs
ic = profile.ic
insulinFromCarbs = carbs / ic
insulinFromCOB = if (useCob) (cob / ic) else 0.0
// Insulin from IOB
// IOB calculation
treatmentsPlugin.updateTotalIOBTreatments()
val bolusIob = treatmentsPlugin.lastCalculationTreatments.round()
treatmentsPlugin.updateTotalIOBTempBasals()
val basalIob = treatmentsPlugin.lastCalculationTempBasals.round()
insulinFromBolusIOB = if (includeBolusIOB) -bolusIob.iob else 0.0
insulinFromBasalsIOB = if (includeBasalIOB) -basalIob.basaliob else 0.0
// Insulin from correction
insulinFromCorrection = correction
// Insulin from superbolus for 2h. Get basal rate now and after 1h
if (useSuperBolus) {
insulinFromSuperBolus = profile.basal
var timeAfter1h = System.currentTimeMillis()
timeAfter1h += T.hours(1).msecs()
insulinFromSuperBolus += profile.getBasal(timeAfter1h)
}
// Total
calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB
// Percentage adjustment
totalBeforePercentageAdjustment = calculatedTotalInsulin
if (calculatedTotalInsulin > 0) {
calculatedTotalInsulin = calculatedTotalInsulin * percentageCorrection / 100.0
}
if (calculatedTotalInsulin < 0) {
carbsEquivalent = (-calculatedTotalInsulin) * ic
calculatedTotalInsulin = 0.0
}
val bolusStep = configBuilderPlugin.activePump?.pumpDescription?.bolusStep
?: 0.1
calculatedTotalInsulin = Round.roundTo(calculatedTotalInsulin, bolusStep)
insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(calculatedTotalInsulin)).value()
aapsLogger.debug(this.toString())
return this
}
private fun nsJSON(): JSONObject {
val bolusCalcJSON = JSONObject()
try {
bolusCalcJSON.put("profile", profileName)
bolusCalcJSON.put("notes", notes)
bolusCalcJSON.put("eventTime", DateUtil.toISOString(Date()))
bolusCalcJSON.put("targetBGLow", targetBGLow)
bolusCalcJSON.put("targetBGHigh", targetBGHigh)
bolusCalcJSON.put("isf", sens)
bolusCalcJSON.put("ic", ic)
bolusCalcJSON.put("iob", -(insulinFromBolusIOB + insulinFromBasalsIOB))
bolusCalcJSON.put("bolusiob", insulinFromBolusIOB)
bolusCalcJSON.put("basaliob", insulinFromBasalsIOB)
bolusCalcJSON.put("bolusiobused", includeBolusIOB)
bolusCalcJSON.put("basaliobused", includeBasalIOB)
bolusCalcJSON.put("bg", bg)
bolusCalcJSON.put("insulinbg", insulinFromBG)
bolusCalcJSON.put("insulinbgused", useBg)
bolusCalcJSON.put("bgdiff", bgDiff)
bolusCalcJSON.put("insulincarbs", insulinFromCarbs)
bolusCalcJSON.put("carbs", carbs)
bolusCalcJSON.put("cob", cob)
bolusCalcJSON.put("cobused", useCob)
bolusCalcJSON.put("insulincob", insulinFromCOB)
bolusCalcJSON.put("othercorrection", correction)
bolusCalcJSON.put("insulinsuperbolus", insulinFromSuperBolus)
bolusCalcJSON.put("insulintrend", insulinFromTrend)
bolusCalcJSON.put("insulin", calculatedTotalInsulin)
bolusCalcJSON.put("superbolusused", useSuperBolus)
bolusCalcJSON.put("insulinsuperbolus", insulinFromSuperBolus)
bolusCalcJSON.put("trendused", useTrend)
bolusCalcJSON.put("insulintrend", insulinFromTrend)
bolusCalcJSON.put("trend", trend)
bolusCalcJSON.put("ttused", useTT)
bolusCalcJSON.put("percentageCorrection", percentageCorrection)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
return bolusCalcJSON
}
private fun confirmMessageAfterConstraints(pump: PumpInterface): Spanned {
val actions: LinkedList<String> = LinkedList()
if (insulinAfterConstraints > 0) {
val pct = if (percentageCorrection != 100.0) " (" + percentageCorrection.toInt() + "%)" else ""
actions.add(resourceHelper.gs(R.string.bolus) + ": " + "<font color='" + resourceHelper.gc(R.color.bolus) + "'>" + resourceHelper.gs(R.string.formatinsulinunits, insulinAfterConstraints) + pct + "</font>")
}
if (carbs > 0) {
var timeShift = ""
if (carbTime > 0) {
timeShift += " ( +" + resourceHelper.gs(R.string.mins, carbTime) + " )"
} else if (carbTime < 0) {
timeShift += " ( -" + resourceHelper.gs(R.string.mins, carbTime) + " )"
}
actions.add(resourceHelper.gs(R.string.carbs) + ": " + "<font color='" + resourceHelper.gc(R.color.carbs) + "'>" + carbs + "g" + timeShift + "</font>")
}
if (insulinFromCOB > 0) {
actions.add(resourceHelper.gs(R.string.insulinFromCob, resourceHelper.gc(R.color.cobAlert), insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCOB + insulinFromBG))
val absorptionRate = iobCobCalculatorPlugin.slowAbsorptionPercentage(60)
if (absorptionRate > .25)
actions.add(resourceHelper.gs(R.string.slowabsorptiondetected, resourceHelper.gc(R.color.cobAlert), (absorptionRate * 100).toInt()))
}
if (abs(insulinAfterConstraints - calculatedTotalInsulin) > pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) {
actions.add(resourceHelper.gs(R.string.bolusconstraintappliedwarning, resourceHelper.gc(R.color.warning), calculatedTotalInsulin, insulinAfterConstraints))
}
return HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions))
}
fun confirmAndExecute(context: Context) {
val profile = profileFunction.getProfile() ?: return
val pump = configBuilderPlugin.activePump ?: return
if (calculatedTotalInsulin > 0.0 || carbs > 0.0) {
if (accepted) {
aapsLogger.debug(LTag.UI, "guarding: already accepted")
return
}
accepted = true
val confirmMessage = confirmMessageAfterConstraints(pump)
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.boluswizard), confirmMessage, Runnable {
if (insulinAfterConstraints > 0 || carbs > 0) {
if (useSuperBolus) {
if (loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000)
rxBus.send(EventRefreshOverview("WizardDialog"))
}
if (pump.pumpDescription?.tempBasalStyle == PumpDescription.ABSOLUTE) {
configBuilderPlugin.commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(mainApp, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
mainApp.startActivity(i)
}
}
})
} else {
configBuilderPlugin.commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(mainApp, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
mainApp.startActivity(i)
}
}
})
}
}
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD
detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.carbs = carbs.toDouble()
detailedBolusInfo.context = context
detailedBolusInfo.glucose = bg
detailedBolusInfo.glucoseType = "Manual"
detailedBolusInfo.carbTime = carbTime
detailedBolusInfo.boluscalc = nsJSON()
detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes
if (detailedBolusInfo.insulin > 0 || pump.pumpDescription?.storesCarbInfo == true) {
configBuilderPlugin.commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(mainApp, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
mainApp.startActivity(i)
}
}
})
} else {
treatmentsPlugin.addToHistoryTreatment(detailedBolusInfo, false)
}
}
})
}
}
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.data
package info.nightscout.androidaps.utils.wizard
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONObject
@ -8,7 +9,8 @@ import javax.inject.Singleton
@Singleton
class QuickWizard @Inject constructor(
private val sp: SP
private val sp: SP,
private val mainApp: MainApp
){
private var storage = JSONArray()
@ -18,8 +20,8 @@ class QuickWizard @Inject constructor(
fun getActive(): QuickWizardEntry? {
for (i in 0 until storage.length()) {
val entry = QuickWizardEntry(storage.get(i) as JSONObject, i)
if (entry.isActive) return entry
val entry = QuickWizardEntry(mainApp).from(storage.get(i) as JSONObject, i)
if (entry.isActive()) return entry
}
return null
}
@ -35,11 +37,11 @@ class QuickWizard @Inject constructor(
fun size(): Int = storage.length()
operator fun get(position: Int): QuickWizardEntry =
QuickWizardEntry(storage.get(position) as JSONObject, position)
QuickWizardEntry(mainApp).from(storage.get(position) as JSONObject, position)
fun newEmptyItem(): QuickWizardEntry {
return QuickWizardEntry()
return QuickWizardEntry(mainApp)
}
fun addOrUpdate(newItem: QuickWizardEntry) {

View file

@ -0,0 +1,148 @@
package info.nightscout.androidaps.utils.wizard
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper.safeGetInt
import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
class QuickWizardEntry @Inject constructor(private val mainApp: MainApp) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
lateinit var storage: JSONObject
var position: Int = -1
companion object {
const val YES = 0
const val NO = 1
private const val POSITIVE_ONLY = 2
private const val NEGATIVE_ONLY = 3
}
init {
mainApp.androidInjector().inject(this)
val emptyData = "{\"buttonText\":\"\",\"carbs\":0,\"validFrom\":0,\"validTo\":86340}"
try {
storage = JSONObject(emptyData)
} catch (e: JSONException) {
aapsLogger.error("Unhandled exception", e)
}
}
/*
{
buttonText: "Meal",
carbs: 36,
validFrom: 8 * 60 * 60, // seconds from midnight
validTo: 9 * 60 * 60, // seconds from midnight
useBG: 0,
useCOB: 0,
useBolusIOB: 0,
useBasalIOB: 0,
useTrend: 0,
useSuperBolus: 0,
useTemptarget: 0
}
*/
fun from(entry: JSONObject, position: Int): QuickWizardEntry {
storage = entry
this.position = position
return this
}
fun isActive(): Boolean = Profile.secondsFromMidnight() >= validFrom() && Profile.secondsFromMidnight() <= validTo()
fun doCalc(profile: Profile, profileName: String, lastBG: BgReading, _synchronized: Boolean): BolusWizard {
val tempTarget = treatmentsPlugin.tempTargetFromHistory
//BG
var bg = 0.0
if (useBG() == YES) {
bg = lastBG.valueToUnits(profileFunction.getUnits())
}
// COB
var cob = 0.0
if (useCOB() == YES) {
val cobInfo = iobCobCalculatorPlugin.getCobInfo(_synchronized, "QuickWizard COB")
if (cobInfo.displayCob != null) cob = cobInfo.displayCob
}
// Bolus IOB
var bolusIOB = false
if (useBolusIOB() == YES) {
bolusIOB = true
}
// Basal IOB
treatmentsPlugin.updateTotalIOBTempBasals()
val basalIob = treatmentsPlugin.lastCalculationTempBasals.round()
var basalIOB = false
if (useBasalIOB() == YES) {
basalIOB = true
} else if (useBasalIOB() == POSITIVE_ONLY && basalIob.iob > 0) {
basalIOB = true
} else if (useBasalIOB() == NEGATIVE_ONLY && basalIob.iob < 0) {
basalIOB = true
}
// SuperBolus
var superBolus = false
if (useSuperBolus() == YES && sp.getBoolean(R.string.key_usesuperbolus, false)) {
superBolus = true
}
if (loopPlugin.isEnabled(loopPlugin.getType()) && loopPlugin.isSuperBolus) superBolus = false
// Trend
val glucoseStatus = GlucoseStatus.getGlucoseStatusData()
var trend = false
if (useTrend() == YES) {
trend = true
} else if (useTrend() == POSITIVE_ONLY && glucoseStatus != null && glucoseStatus.short_avgdelta > 0) {
trend = true
} else if (useTrend() == NEGATIVE_ONLY && glucoseStatus != null && glucoseStatus.short_avgdelta < 0) {
trend = true
}
val percentage = sp.getDouble(R.string.key_boluswizard_percentage, 100.0)
return BolusWizard(mainApp).doCalc(profile, profileName, tempTarget, carbs(), cob, bg, 0.0, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard")
}
fun buttonText(): String = safeGetString(storage, "buttonText", "")
fun carbs(): Int = safeGetInt(storage, "carbs")
fun validFromDate(): Date = DateUtil.toDate(validFrom())
fun validToDate(): Date = DateUtil.toDate(validTo())
fun validFrom(): Int = safeGetInt(storage, "validFrom")
fun validTo(): Int = safeGetInt(storage, "validTo")
fun useBG(): Int = safeGetInt(storage, "useBG", YES)
fun useCOB(): Int = safeGetInt(storage, "useCOB", NO)
fun useBolusIOB(): Int = safeGetInt(storage, "useBolusIOB", YES)
fun useBasalIOB(): Int = safeGetInt(storage, "useBasalIOB", YES)
fun useTrend(): Int = safeGetInt(storage, "useTrend", NO)
fun useSuperBolus(): Int = safeGetInt(storage, "useSuperBolus", NO)
fun useTempTarget(): Int = safeGetInt(storage, "useTempTarget", NO)
}

View file

@ -14,6 +14,8 @@ import info.AAPSMocker;
import info.SPMocker;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.wizard.QuickWizard;
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry;
@RunWith(PowerMockRunner.class)
@PrepareForTest({SP.class, MainApp.class, Profile.class})

View file

@ -20,6 +20,7 @@ import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin;
import info.nightscout.androidaps.utils.wizard.BolusWizard;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;