diff --git a/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt b/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt index e873bf5edf..f20eed392a 100644 --- a/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt +++ b/app/src/androidTest/java/info/nightscout/androidaps/SetupWizardActivityTest.kt @@ -1,42 +1,7 @@ package info.nightscout.androidaps -import android.os.SystemClock -import android.view.View -import android.view.ViewGroup -import androidx.test.espresso.Espresso.onData -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.action.ViewActions.scrollTo -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withClassName -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withTagValue -import androidx.test.espresso.matcher.ViewMatchers.withText 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.interfaces.PluginType -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin -import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin -import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin -import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin -import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin -import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin -import info.nightscout.androidaps.plugins.source.RandomBgPlugin -import info.nightscout.androidaps.setupwizard.SetupWizardActivity -import info.nightscout.androidaps.utils.HardLimits -import info.nightscout.androidaps.utils.extensions.isRunningTest -import org.hamcrest.CoreMatchers.allOf -import org.hamcrest.Description -import org.hamcrest.Matcher -import org.hamcrest.Matchers -import org.hamcrest.TypeSafeMatcher -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test import org.junit.runner.RunWith @LargeTest diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt index d47573ef2f..00fe3c688f 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt @@ -8,14 +8,18 @@ import android.widget.ArrayAdapter import com.google.common.base.Joiner import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.databinding.DialogProfileswitchBinding import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper @@ -31,6 +35,9 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var repository: AppRepository @Inject lateinit var uel: UserEntryLogger + @Inject lateinit var config: Config + @Inject lateinit var hardLimits: HardLimits + @Inject lateinit var rxBus: RxBusWrapper private var profileIndex: Int? = null @@ -129,22 +136,33 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { - profileFunction.createProfileSwitch(profileStore, - profileName = profileName, - durationInMinutes = duration, - percentage = percent, - timeShiftInHours = timeShift, - timestamp = eventTime) - uel.log(Action.PROFILE_SWITCH, - Sources.ProfileSwitchDialog, - notes, - ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, - ValueWithUnit.SimpleString(profileName), - ValueWithUnit.Percent(percent), - ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 }, - ValueWithUnit.Minute(duration).takeIf { duration != 0 }) - }) + val ps = profileFunction.buildProfileSwitch(profileStore, profileName, duration, percent, timeShift, eventTime) + val validity = ProfileSealed.PS(ps).isValid(resourceHelper.gs(R.string.careportal_profileswitch), activePlugin.activePump, config, resourceHelper, rxBus, hardLimits) + if (validity.isValid) + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { + profileFunction.createProfileSwitch(profileStore, + profileName = profileName, + durationInMinutes = duration, + percentage = percent, + timeShiftInHours = timeShift, + timestamp = eventTime) + uel.log(Action.PROFILE_SWITCH, + Sources.ProfileSwitchDialog, + notes, + ValueWithUnit.Timestamp(eventTime).takeIf { eventTimeChanged }, + ValueWithUnit.SimpleString(profileName), + ValueWithUnit.Percent(percent), + ValueWithUnit.Hour(timeShift).takeIf { timeShift != 0 }, + ValueWithUnit.Minute(duration).takeIf { duration != 0 }) + }) + else { + OKDialog.show( + activity, + resourceHelper.gs(R.string.careportal_profileswitch), + HtmlHelper.fromHtml(Joiner.on("
").join(validity.reasons)) + ) + return false + } } return true } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt index c6955fcd34..5ee8e69839 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt @@ -46,7 +46,6 @@ import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAc import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.queue.Callback -import info.nightscout.androidaps.queue.commands.Command import info.nightscout.androidaps.receivers.ReceiverStatusStore import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt index f697f23e20..479acd4da0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt @@ -125,11 +125,11 @@ class OpenAPSAMAPlugin @Inject constructor( maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble()) targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble()) } - if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return - if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return - if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return - if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return - if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return + if (!hardLimits.checkHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return + if (!hardLimits.checkHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return + if (!hardLimits.checkHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return + if (!hardLimits.checkHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return + if (!hardLimits.checkHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return startPart = System.currentTimeMillis() if (constraintChecker.isAutosensModeEnabled().value()) { val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin") diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt index 3b65f213f7..e658f2d04c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt @@ -130,11 +130,11 @@ class OpenAPSSMBPlugin @Inject constructor( maxBg = hardLimits.verifyHardLimits(tempTarget.value.highTarget, R.string.temp_target_high_target, HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble()) targetBg = hardLimits.verifyHardLimits(tempTarget.value.target(), R.string.temp_target_value, HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), HardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble()) } - if (!hardLimits.checkOnlyHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return - if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return - if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return - if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return - if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return + if (!hardLimits.checkHardLimits(profile.dia, R.string.profile_dia, hardLimits.minDia(), hardLimits.maxDia())) return + if (!hardLimits.checkHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), R.string.profile_carbs_ratio_value, hardLimits.minIC(), hardLimits.maxIC())) return + if (!hardLimits.checkHardLimits(profile.getIsfMgdl(), R.string.profile_sensitivity_value, HardLimits.MIN_ISF, HardLimits.MAX_ISF)) return + if (!hardLimits.checkHardLimits(profile.getMaxDailyBasal(), R.string.profile_max_daily_basal_value, 0.02, hardLimits.maxBasal())) return + if (!hardLimits.checkHardLimits(pump.baseBasalRate, R.string.current_basal_value, 0.01, hardLimits.maxBasal())) return startPart = System.currentTimeMillis() if (constraintChecker.isAutosensModeEnabled().value()) { val autosensData = iobCobCalculator.getLastAutosensDataWithWaitForCalculationFinish("OpenAPSPlugin") diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt index 3de63dbbd5..5cf7bb4371 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ProfileFunctionImplementation.kt @@ -91,10 +91,10 @@ class ProfileFunctionImplementation @Inject constructor( if (sp.getString(R.string.key_units, Constants.MGDL) == Constants.MGDL) GlucoseUnit.MGDL else GlucoseUnit.MMOL - override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) { + override fun buildProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) : ProfileSwitch { val pureProfile = profileStore.getSpecificProfile(profileName) ?: throw InvalidParameterSpecException(profileName) - val ps = ProfileSwitch( + return ProfileSwitch( timestamp = timestamp, basalBlocks = pureProfile.basalBlocks, isfBlocks = pureProfile.isfBlocks, @@ -105,8 +105,14 @@ class ProfileFunctionImplementation @Inject constructor( timeshift = T.hours(timeShiftInHours.toLong()).msecs(), percentage = percentage, duration = T.mins(durationInMinutes.toLong()).msecs(), - insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also { it.insulinEndTime = (pureProfile.dia * 3600 * 1000).toLong() } + insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also { + it.insulinEndTime = (pureProfile.dia * 3600 * 1000).toLong() + } ) + } + + override fun createProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long) { + val ps = buildProfileSwitch(profileStore, profileName, durationInMinutes, percentage, timeShiftInHours, timestamp) disposable += repository.runTransactionForResult(InsertOrUpdateProfileSwitch(ps)) .subscribe({ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted ProfileSwitch $it") } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index cfc0eaac7e..bddee95f50 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -740,7 +740,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList graphData.addTreatments() if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal]) graphData.addActivity(0.8) - if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) + if ((pump.pumpDescription.isTempBasalCapable || config.NSCLIENT) && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) graphData.addBasals() graphData.addTargetLine() graphData.addNowLine(dateUtil.now()) diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt index b65a284149..8d640afc75 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt @@ -28,6 +28,7 @@ import info.nightscout.androidaps.setupwizard.elements.* import info.nightscout.androidaps.setupwizard.events.EventSWUpdate import info.nightscout.androidaps.utils.AndroidPermission import info.nightscout.androidaps.utils.CryptoUtil +import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.extensions.isRunningTest import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP @@ -53,7 +54,8 @@ class SWDefinition @Inject constructor( private val importExportPrefs: ImportExportPrefs, private val androidPermission: AndroidPermission, private val cryptoUtil: CryptoUtil, - private val config: Config + private val config: Config, + private val hardLimits: HardLimits ) { lateinit var activity: AppCompatActivity @@ -255,7 +257,7 @@ class SWDefinition @Inject constructor( .add(SWFragment(injector, this) .add(LocalProfileFragment())) .validator { - localProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, resourceHelper, rxBus) } + localProfilePlugin.profile?.getDefaultProfile()?.let { ProfileSealed.Pure(it).isValid("StartupWizard", activePlugin.activePump, config, resourceHelper, rxBus, hardLimits).isValid } ?: false } .visibility { localProfilePlugin.isEnabled() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e2e35aecf..a248a82b14 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -250,8 +250,6 @@ Loop has been disabled Loop has been enabled Loop is enabled - %1$.2f limited to %2$.2f - »%1$s« is out of hard limits To connect pump reply with code %1$s Connection to pump failed To disconnect pump for %1$d minutes reply with code %2$s @@ -400,12 +398,6 @@ Adult Insulin resistant adult Pregnancy - age - child - teenage - adult - resistantadult - pregnant Please select patient type to setup safety limits Patient name Please provide patient name or nickname to differentiate among multiple setups @@ -1082,16 +1074,6 @@ Email address Privacy setting You can provide optional email address if you want to be notified about app crashes. This is not an automated service. You will be contacted by developers in dangerous situations. - Profile low target - Profile high target - Temporary target bottom value - Temporary target top value - Temporary target value - Profile DIA value - Profile sensitivity value - Maximal profile basal value - Current basal value - Profile carbs ratio value Full sync Prime Synchronization diff --git a/core/src/main/java/info/nightscout/androidaps/data/ProfileSealed.kt b/core/src/main/java/info/nightscout/androidaps/data/ProfileSealed.kt index 50b39e896b..f47f1a534f 100644 --- a/core/src/main/java/info/nightscout/androidaps/data/ProfileSealed.kt +++ b/core/src/main/java/info/nightscout/androidaps/data/ProfileSealed.kt @@ -19,6 +19,8 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.HardLimits +import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.resources.ResourceHelper import org.json.JSONArray @@ -94,34 +96,93 @@ sealed class ProfileSealed( value.timeZone.rawOffset.toLong() ) - override fun isValid(from: String, pump: Pump, config: Config, resourceHelper: ResourceHelper, rxBus: RxBusWrapper): Boolean { + override fun isValid(from: String, pump: Pump, config: Config, resourceHelper: ResourceHelper, rxBus: RxBusWrapper, hardLimits: HardLimits): Profile.ValidityCheck { val notify = true - var valid = true + val validityCheck = Profile.ValidityCheck() val description = pump.pumpDescription - if (!description.is30minBasalRatesCapable) { - for (basal in basalBlocks) { + for (basal in basalBlocks) { + val basalAmount = basal.amount * percentage / 100.0 + if (!description.is30minBasalRatesCapable) { // Check for hours alignment val duration: Long = basal.duration if (duration % 3600000 != 0L) { if (notify && config.APS) { - val notification = Notification(Notification.BASAL_PROFILE_NOT_ALIGNED_TO_HOURS, resourceHelper.gs(R.string.basalprofilenotaligned, from), Notification.NORMAL) + val notification = Notification( + Notification.BASAL_PROFILE_NOT_ALIGNED_TO_HOURS, + resourceHelper.gs(R.string.basalprofilenotaligned, from), + Notification.NORMAL + ) rxBus.send(EventNewNotification(notification)) } - valid = false - } - // Check for minimal basal value - if (basal.amount < description.basalMinimumRate) { - basal.amount = description.basalMinimumRate - if (notify) sendBelowMinimumNotification(from, rxBus, resourceHelper) - valid = false - } else if (basal.amount > description.basalMaximumRate) { - basal.amount = description.basalMaximumRate - if (notify) sendAboveMaximumNotification(from, rxBus, resourceHelper) - valid = false + validityCheck.isValid = false + validityCheck.reasons.add( + resourceHelper.gs( + R.string.basalprofilenotaligned, + from + ) + ) + break } } + // Check for minimal basal value + if (basalAmount < description.basalMinimumRate) { + basal.amount = description.basalMinimumRate + if (notify) sendBelowMinimumNotification(from, rxBus, resourceHelper) + validityCheck.isValid = false + validityCheck.reasons.add(resourceHelper.gs(R.string.minimalbasalvaluereplaced, from)) + break + } else if (basalAmount > description.basalMaximumRate) { + basal.amount = description.basalMaximumRate + if (notify) sendAboveMaximumNotification(from, rxBus, resourceHelper) + validityCheck.isValid = false + validityCheck.reasons.add(resourceHelper.gs(R.string.maximumbasalvaluereplaced, from)) + break + } + if (!hardLimits.isInRange(basalAmount, 0.01, hardLimits.maxBasal())) { + validityCheck.isValid = false + validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.basal_value), basalAmount)) + break + } } - return valid + if (!hardLimits.isInRange(dia, hardLimits.minDia(), hardLimits.maxDia())) { + validityCheck.isValid = false + validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_dia), dia)) + } + for (ic in icBlocks) + if (!hardLimits.isInRange(ic.amount * 100.0 / percentage, hardLimits.minIC(), hardLimits.maxIC())) { + validityCheck.isValid = false + validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_carbs_ratio_value), ic.amount * 100.0 / percentage)) + break + } + for (isf in isfBlocks) + if (!hardLimits.isInRange(toMgdl(isf.amount * 100.0 / percentage, units), HardLimits.MIN_ISF, HardLimits.MAX_ISF)) { + validityCheck.isValid = false + validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_sensitivity_value), isf.amount * 100.0 / percentage)) + break + } + for (target in targetBlocks) { + if (hardLimits.isInRange( + Round.roundTo(target.lowTarget, 0.1), + HardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), + HardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble() + ) + ) { + validityCheck.isValid = false + validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_low_target), target.lowTarget)) + break + } + if (hardLimits.isInRange( + Round.roundTo(target.highTarget, 0.1), + HardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), + HardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble() + ) + ) { + validityCheck.isValid = false + validityCheck.reasons.add(resourceHelper.gs(R.string.value_out_of_hard_limits, resourceHelper.gs(R.string.profile_high_target), target.highTarget)) + break + } + } + return validityCheck } protected open fun sendBelowMinimumNotification(from: String, rxBus: RxBusWrapper, resourceHelper: ResourceHelper) { diff --git a/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt b/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt index 1f4c88e3ab..ccb87c0704 100644 --- a/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt +++ b/core/src/main/java/info/nightscout/androidaps/dialogs/ProfileViewerDialog.kt @@ -17,15 +17,18 @@ import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.extensions.getCustomizedName import info.nightscout.androidaps.extensions.pureProfileFromJson +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import org.json.JSONObject +import java.io.File.separator import java.text.DecimalFormat import javax.inject.Inject @@ -39,6 +42,7 @@ class ProfileViewerDialog : DaggerDialogFragment() { @Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var config: Config @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var hardLimits: HardLimits private var time: Long = 0 @@ -149,7 +153,9 @@ class ProfileViewerDialog : DaggerDialogFragment() { } binding.noprofile.visibility = View.GONE - binding.invalidprofile.visibility = if (profile1.isValid("ProfileViewDialog", activePlugin.activePump, config, resourceHelper, rxBus)) View.GONE else View.VISIBLE + val validity = profile1.isValid("ProfileViewDialog", activePlugin.activePump, config, resourceHelper, rxBus, hardLimits) + binding.invalidprofile.text = resourceHelper.gs(R.string.invalidprofile) + "\n" + validity.reasons.joinToString(separator = "\n") + binding.invalidprofile.visibility = validity.isValid.not().toVisibility() } else profile?.let { @@ -164,7 +170,9 @@ class ProfileViewerDialog : DaggerDialogFragment() { binding.basalGraph.show(it) binding.noprofile.visibility = View.GONE - binding.invalidprofile.visibility = if (it.isValid("ProfileViewDialog", activePlugin.activePump, config, resourceHelper, rxBus)) View.GONE else View.VISIBLE + val validity = it.isValid("ProfileViewDialog", activePlugin.activePump, config, resourceHelper, rxBus, hardLimits) + binding.invalidprofile.text = resourceHelper.gs(R.string.invalidprofile) + "\n" + validity.reasons.joinToString(separator = "\n") + binding.invalidprofile.visibility = validity.isValid.not().toVisibility() } } diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/Profile.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/Profile.kt index 3d43cdc60f..2cd9ac88f5 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/Profile.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/Profile.kt @@ -6,6 +6,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DecimalFormatter.to0Decimal import info.nightscout.androidaps.utils.DecimalFormatter.to1Decimal +import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.Round import info.nightscout.androidaps.utils.resources.ResourceHelper import org.joda.time.DateTime @@ -13,7 +14,9 @@ import org.json.JSONObject interface Profile { - fun isValid(from: String, pump: Pump, config: Config, resourceHelper: ResourceHelper, rxBus: RxBusWrapper): Boolean + class ValidityCheck(var isValid: Boolean = true, val reasons: ArrayList = arrayListOf()) + + fun isValid(from: String, pump: Pump, config: Config, resourceHelper: ResourceHelper, rxBus: RxBusWrapper, hardLimits: HardLimits): ValidityCheck /** * Units used for ISF & target diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/ProfileFunction.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/ProfileFunction.kt index 0b4829dad3..3cc7939446 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/ProfileFunction.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/ProfileFunction.kt @@ -46,6 +46,18 @@ interface ProfileFunction { */ fun getRequestedProfile(): ProfileSwitch? + /** + * Build a new circadian profile switch request based on provided profile + * + * @param profileStore ProfileStore to use + * @param profileName this profile from profile store + * @param durationInMinutes + * @param percentage 100 = no modification + * @param timeShiftInHours 0 = no modification + * @param timestamp expected time + */ + fun buildProfileSwitch(profileStore: ProfileStore, profileName: String, durationInMinutes: Int, percentage: Int, timeShiftInHours: Int, timestamp: Long): ProfileSwitch + /** * Create a new circadian profile switch request based on provided profile * diff --git a/core/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.kt b/core/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.kt index 136e899a2c..003d8f569e 100644 --- a/core/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.kt +++ b/core/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.kt @@ -63,7 +63,7 @@ class PumpDescription() { is30minBasalRatesCapable = false isRefillingCapable = true isBatteryReplaceable = true - storesCarbInfo = true + storesCarbInfo = false supportsTDDs = false needsManualTDDLoad = true hasCustomUnreachableAlertCheck = false diff --git a/app/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt b/core/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt similarity index 92% rename from app/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt rename to core/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt index 0389b34283..67118d0908 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/HardLimits.kt @@ -1,7 +1,8 @@ package info.nightscout.androidaps.utils import android.content.Context -import info.nightscout.androidaps.R +import info.nightscout.androidaps.annotations.OpenForTesting +import info.nightscout.androidaps.core.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.logging.AAPSLogger @@ -15,6 +16,7 @@ import javax.inject.Singleton import kotlin.math.max import kotlin.math.min +@OpenForTesting @Singleton class HardLimits @Inject constructor( private val aapsLogger: AAPSLogger, @@ -81,9 +83,12 @@ class HardLimits @Inject constructor( fun maxIC(): Double = MAX_IC[loadAge()] // safety checks - fun checkOnlyHardLimits(value: Double, valueName: Int, lowLimit: Double, highLimit: Double): Boolean = + fun checkHardLimits(value: Double, valueName: Int, lowLimit: Double, highLimit: Double): Boolean = value == verifyHardLimits(value, valueName, lowLimit, highLimit) + fun isInRange(value: Double, lowLimit: Double, highLimit: Double): Boolean = + value in lowLimit..highLimit + fun verifyHardLimits(value: Double, valueName: Int, lowLimit: Double, highLimit: Double): Double { var newValue = value if (newValue < lowLimit || newValue > highLimit) { @@ -98,4 +103,4 @@ class HardLimits @Inject constructor( } return newValue } -} +} \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/utils/sharedPreferences/SPImplementation.kt b/core/src/main/java/info/nightscout/androidaps/utils/sharedPreferences/SPImplementation.kt index 5231a2e4ad..52f5c61f26 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/sharedPreferences/SPImplementation.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/sharedPreferences/SPImplementation.kt @@ -90,7 +90,11 @@ class SPImplementation @Inject constructor( return try { sharedPreferences.getLong(key, defaultValue) } catch (e: Exception) { - SafeParse.stringToLong(sharedPreferences.getString(key, defaultValue.toString())) + try { + SafeParse.stringToLong(sharedPreferences.getString(key, defaultValue.toString())) + } catch (e: Exception) { + defaultValue + } } } diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 838e4d7acf..2b813deedc 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -57,6 +57,12 @@ active_pump_change_timestamp active_pump_type active_pump_serial_number + age + child + teenage + adult + resistantadult + pregnant Refresh @@ -472,6 +478,22 @@ LOOP REMOVED OTHER + + Profile low target + Profile high target + Temporary target bottom value + Temporary target top value + Temporary target value + Profile DIA value + Profile sensitivity value + Maximal profile basal value + Current basal value + Profile carbs ratio value + %1$.2f limited to %2$.2f + »%1$s« is out of hard limits + »%1$s« %2$.2f is out of hard limits + Basal value + %1$d day %1$d days diff --git a/core/src/test/java/info/nightscout/androidaps/data/ProfileTest.kt b/core/src/test/java/info/nightscout/androidaps/data/ProfileTest.kt index d141926a7e..3c1e6b5732 100644 --- a/core/src/test/java/info/nightscout/androidaps/data/ProfileTest.kt +++ b/core/src/test/java/info/nightscout/androidaps/data/ProfileTest.kt @@ -2,11 +2,10 @@ package info.nightscout.androidaps.data import android.content.Context import dagger.android.AndroidInjector -import dagger.android.HasAndroidInjector import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.TestPumpPlugin import info.nightscout.androidaps.core.R -import info.nightscout.androidaps.events.Event +import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.extensions.pureProfileFromJson import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.Config @@ -14,18 +13,18 @@ import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.TestAapsSchedulers +import info.nightscout.androidaps.utils.sharedPreferences.SP import org.json.JSONObject import org.junit.Assert import org.junit.Before import org.junit.Test import org.mockito.Mock import org.mockito.Mockito.`when` -import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.anyString -import org.mockito.Mockito.doNothing import java.util.* /** @@ -38,25 +37,29 @@ class ProfileTest : TestBase() { @Mock lateinit var resourceHelper: ResourceHelper @Mock lateinit var context: Context @Mock lateinit var config: Config + @Mock lateinit var sp: SP + @Mock lateinit var repository: AppRepository private lateinit var rxBus: RxBusWrapper private lateinit var dateUtil: DateUtil private lateinit var testPumpPlugin: TestPumpPlugin + private lateinit var hardLimits: HardLimits - private var okProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"},{\"time\":\"2:00\",\"value\":\"110\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" - private var belowLimitValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.001\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" - private var notAlignedBasalValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:30\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" - private var notStartingAtZeroValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:30\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" - private var noUnitsValidProfile = "{\"dia\":\"3\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\"}" - private var wrongProfile = "{\"dia\":\"3\",\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var okProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"sens\":[{\"time\":\"00:00\",\"value\":\"6\"},{\"time\":\"2:00\",\"value\":\"6.2\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var belowLimitValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.001\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var notAlignedBasalValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:30\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var notStartingAtZeroValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:30\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" + private var noUnitsValidProfile = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\"}" + private var wrongProfile = "{\"dia\":\"5\",\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"100\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"0.1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4\"}],\"target_high\":[{\"time\":\"00:00\",\"value\":\"5\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}" //String profileStore = "{\"defaultProfile\":\"Default\",\"store\":{\"Default\":" + validProfile + "}}"; @Before fun prepare() { - testPumpPlugin = TestPumpPlugin(HasAndroidInjector { AndroidInjector { } }) + testPumpPlugin = TestPumpPlugin { AndroidInjector { } } dateUtil = DateUtil(context) rxBus = RxBusWrapper(TestAapsSchedulers()) + hardLimits = HardLimits(aapsLogger, rxBus, sp, resourceHelper, context, repository) `when`(activePluginProvider.activePump).thenReturn(testPumpPlugin) `when`(resourceHelper.gs(R.string.profile_per_unit)).thenReturn("/U") `when`(resourceHelper.gs(R.string.profile_carbs_per_unit)).thenReturn("g/U") @@ -69,10 +72,10 @@ class ProfileTest : TestBase() { // Test valid profile var p = ProfileSealed.Pure(pureProfileFromJson(JSONObject(okProfile), dateUtil)!!) - Assert.assertEquals(true, p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus)) + Assert.assertEquals(true, p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus, hardLimits).isValid) // Assert.assertEquals(true, p.log().contains("NS units: mmol")) // JSONAssert.assertEquals(JSONObject(okProfile), p.toPureNsJson(dateUtil), false) - Assert.assertEquals(3.0, p.dia, 0.01) + Assert.assertEquals(5.0, p.dia, 0.01) // Assert.assertEquals(TimeZone.getTimeZone("UTC"), p.timeZone) Assert.assertEquals("00:30", dateUtil.format_HH_MM(30 * 60)) val c = Calendar.getInstance() @@ -80,13 +83,13 @@ class ProfileTest : TestBase() { c[Calendar.MINUTE] = 0 c[Calendar.SECOND] = 0 c[Calendar.MILLISECOND] = 0 - Assert.assertEquals(1800.0, p.getIsfMgdl(c.timeInMillis), 0.01) + Assert.assertEquals(108.0, p.getIsfMgdl(c.timeInMillis), 0.01) c[Calendar.HOUR_OF_DAY] = 2 - Assert.assertEquals(1980.0, p.getIsfMgdl(c.timeInMillis), 0.01) + Assert.assertEquals(111.6, p.getIsfMgdl(c.timeInMillis), 0.01) // Assert.assertEquals(110.0, p.getIsfTimeFromMidnight(2 * 60 * 60), 0.01) Assert.assertEquals(""" - 00:00 100,0 mmol/U - 02:00 110,0 mmol/U + 00:00 6,0 mmol/U + 02:00 6,2 mmol/U """.trimIndent(), p.getIsfList(resourceHelper, dateUtil).replace(".", ",")) Assert.assertEquals(30.0, p.getIc(c.timeInMillis), 0.01) Assert.assertEquals(30.0, p.getIcTimeFromMidnight(2 * 60 * 60), 0.01) @@ -121,7 +124,7 @@ class ProfileTest : TestBase() { //Test basal profile below limit p = ProfileSealed.Pure(pureProfileFromJson(JSONObject(belowLimitValidProfile), dateUtil)!!) - p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus) + p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus, hardLimits) // Test profile w/o units Assert.assertNull(pureProfileFromJson(JSONObject(noUnitsValidProfile), dateUtil)) @@ -139,21 +142,21 @@ class ProfileTest : TestBase() { Assert.assertEquals(0.05, p.getBasal(c.timeInMillis), 0.01) Assert.assertEquals(1.2, p.percentageBasalSum(), 0.01) Assert.assertEquals(60.0, p.getIc(c.timeInMillis), 0.01) - Assert.assertEquals(3960.0, p.getIsfMgdl(c.timeInMillis), 0.01) + Assert.assertEquals(223.2, p.getIsfMgdl(c.timeInMillis), 0.01) // Test timeshift functionality p = ProfileSealed.Pure(pureProfileFromJson(JSONObject(okProfile), dateUtil)!!) p.ts = 1 Assert.assertEquals( """ - 00:00 110.0 mmol/U - 01:00 100.0 mmol/U - 03:00 110.0 mmol/U + 00:00 6.2 mmol/U + 01:00 6.0 mmol/U + 03:00 6.2 mmol/U """.trimIndent(), p.getIsfList(resourceHelper, dateUtil)) // Test hour alignment testPumpPlugin.pumpDescription.is30minBasalRatesCapable = false p = ProfileSealed.Pure(pureProfileFromJson(JSONObject(notAlignedBasalValidProfile), dateUtil)!!) - p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus) + p.isValid("Test", testPumpPlugin, config, resourceHelper, rxBus, hardLimits) } } \ No newline at end of file