Merge pull request #5 from MilosKozak/master

OpenAPS MA initial work
This commit is contained in:
LadyViktoria 2016-06-11 18:43:15 +02:00 committed by GitHub
commit b1a342ec86
31 changed files with 1235 additions and 115 deletions

View file

@ -37,7 +37,7 @@
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -32,4 +32,5 @@ dependencies {
compile 'com.github.tony19:logback-android-classic:1.1.1-4'
compile 'org.slf4j:slf4j-api:1.7.12'
compile 'com.jjoe64:graphview:4.0.1'
compile 'com.eclipsesource.j2v8:j2v8:3.1.6@aar'
}

View file

@ -0,0 +1,316 @@
/*
Determine Basal
Released under MIT license. See the accompanying LICENSE.txt file for
full terms and conditions
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, offline, meal_data, setTempBasal) {
var rT = { //short for requestedTemp
};
if (typeof profile === 'undefined' || typeof profile.current_basal === 'undefined') {
rT.error ='Error: could not get current basal rate';
return rT;
}
var bg = glucose_status.glucose;
if (bg < 30) { //Dexcom is in ??? mode or calibrating, do nothing. Asked @benwest for raw data in iter_glucose
rT.error = "CGM is calibrating or in ??? state";
return rT;
}
var max_iob = profile.max_iob; // maximum amount of non-bolus IOB OpenAPS will ever deliver
// if target_bg is set, great. otherwise, if min and max are set, then set target to their average
var target_bg;
if (typeof profile.target_bg !== 'undefined') {
target_bg = profile.target_bg;
} else {
if (typeof profile.min_bg !== 'undefined' && typeof profile.max_bg !== 'undefined') {
target_bg = (profile.min_bg + profile.max_bg) / 2;
} else {
rT.error ='Error: could not determine target_bg';
return rT;
}
}
if (typeof iob_data === 'undefined' ) {
rT.error ='Error: iob_data undefined';
return rT;
}
if (typeof iob_data.activity === 'undefined' || typeof iob_data.iob === 'undefined' || typeof iob_data.activity === 'undefined') {
rT.error ='Error: iob_data missing some property';
return rT;
}
var tick;
if (glucose_status.delta >= 0) {
tick = "+" + glucose_status.delta;
} else {
tick = glucose_status.delta;
}
var minDelta = Math.min(glucose_status.delta, glucose_status.avgdelta);
//var maxDelta = Math.max(glucose_status.delta, glucose_status.avgdelta);
//calculate BG impact: the amount BG "should" be rising or falling based on insulin activity alone
var bgi = Math.round(( -iob_data.activity * profile.sens * 5 )*100)/100;
// project positive deviations for 15 minutes
var deviation = Math.round( 15 / 5 * ( glucose_status.avgdelta - bgi ) );
// project negative deviations for 30 minutes
if (deviation < 0) {
deviation = Math.round( 30 / 5 * ( glucose_status.avgdelta - bgi ) );
}
//console.log("Avg.Delta: " + glucose_status.avgdelta.toFixed(1) + ", BGI: " + bgi.toFixed(1) + " 15m activity projection: " + deviation.toFixed(0));
// calculate the naive (bolus calculator math) eventual BG based on net IOB and sensitivity
var naive_eventualBG = Math.round( bg - (iob_data.iob * profile.sens) );
// and adjust it for the deviation above
var eventualBG = naive_eventualBG + deviation;
// calculate what portion of that is due to bolussnooze
var bolusContrib = iob_data.bolussnooze * profile.sens;
// and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime
var naive_snoozeBG = Math.round( naive_eventualBG + 1.5 * bolusContrib );
// adjust that for deviation like we did eventualBG
var snoozeBG = naive_snoozeBG + deviation;
//console.log("BG: " + bg +"(" + tick + ","+glucose_status.avgdelta.toFixed(1)+")"+ " -> " + eventualBG + "-" + snoozeBG + " (Unadjusted: " + naive_eventualBG + "-" + naive_snoozeBG + "), BGI: " + bgi);
var expectedDelta = Math.round(( bgi + ( target_bg - eventualBG ) / ( profile.dia * 60 / 5 ) )*10)/10;
//console.log("expectedDelta: " + expectedDelta);
if (typeof eventualBG === 'undefined' || isNaN(eventualBG)) {
rT.error ='Error: could not calculate eventualBG';
return rT;
}
// min_bg of 90 -> threshold of 70, 110 -> 80, and 130 -> 90
var threshold = profile.min_bg - 0.5*(profile.min_bg-50);
rT = {
'temp': 'absolute'
, 'bg': bg
, 'tick': tick
, 'eventualBG': eventualBG
, 'snoozeBG': snoozeBG
};
var basaliob;
if (iob_data.basaliob) { basaliob = iob_data.basaliob; }
else { basaliob = iob_data.iob - iob_data.bolussnooze; }
// allow meal assist to run when carbs are just barely covered
if (minDelta > Math.max(3, bgi) && ( (meal_data.carbs > 0 && (1.1 * meal_data.carbs/profile.carb_ratio > meal_data.boluses + basaliob)) || ( deviation > 25 && minDelta > 7 ) ) ) {
// ignore all covered IOB, and just set eventualBG to the current bg
eventualBG = Math.max(bg,eventualBG) + deviation;
rT.eventualBG = eventualBG;
profile.min_bg = 80;
target_bg = (profile.min_bg + profile.max_bg) / 2;
expectedDelta = Math.round(( bgi + ( target_bg - eventualBG ) / ( profile.dia * 60 / 5 ) )*10)/10;
rT.mealAssist = "On: Carbs: " + meal_data.carbs + " Boluses: " + meal_data.boluses + " Target: " + target_bg + " Deviation: " + deviation + " BGI: " + bgi;
} else {
rT.mealAssist = "Off: Carbs: " + meal_data.carbs + " Boluses: " + meal_data.boluses + " Target: " + target_bg + " Deviation: " + deviation + " BGI: " + bgi;
}
if (bg < threshold) { // low glucose suspend mode: BG is < ~80
rT.reason = "BG " + bg + "<" + threshold;
if ((glucose_status.delta <= 0 && glucose_status.avgdelta <= 0) || (glucose_status.delta < expectedDelta && glucose_status.avgdelta < expectedDelta)) {
// BG is still falling / rising slower than predicted
return setTempBasal(0, 30, profile, rT, offline);
}
if (glucose_status.delta > glucose_status.avgdelta) {
rT.reason += ", delta " + glucose_status.delta + ">0";
} else {
rT.reason += ", avg delta " + glucose_status.avgdelta.toFixed(2) + ">0";
}
if (currenttemp.rate > profile.current_basal) { // if a high-temp is running
rT.reason += ", cancel high temp";
return setTempBasal(0, 0, profile, rT, offline); // cancel high temp
} else if (currenttemp.duration && eventualBG > profile.max_bg) { // if low-temped and predicted to go high from negative IOB
rT.reason += ", cancel low temp";
return setTempBasal(0, 0, profile, rT, offline); // cancel low temp
}
rT.reason += "; no high-temp to cancel";
return rT;
}
if (eventualBG < profile.min_bg) { // if eventual BG is below target:
if (rT.mealAssist.indexOf("On") == 0) {
rT.reason = "Meal assist: " + meal_data.carbs + "g, " + meal_data.boluses + "U";
} else {
rT.reason = "Eventual BG " + eventualBG + "<" + profile.min_bg;
// if 5m or 15m avg BG is rising faster than expected delta
if (minDelta > expectedDelta && minDelta > 0) {
if (glucose_status.delta > glucose_status.avgdelta) {
rT.reason += ", but Delta " + tick + " > Exp. Delta " + expectedDelta;
} else {
rT.reason += ", but Avg. Delta " + glucose_status.avgdelta.toFixed(2) + " > Exp. Delta " + expectedDelta;
}
if (currenttemp.duration > 0) { // if there is currently any temp basal running
rT.reason = rT.reason += "; cancel";
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
} else {
rT.reason = rT.reason += "; no temp to cancel";
return rT;
}
}
}
if (eventualBG < profile.min_bg) {
// if this is just due to boluses, we can snooze until the bolus IOB decays (at double speed)
if (snoozeBG > profile.min_bg) { // if adding back in the bolus contribution BG would be above min
// if BG is falling and high-temped, or rising and low-temped, cancel
// compare against zero here, not BGI, because BGI will be highly negative from boluses and no carbs
if (glucose_status.delta < 0 && currenttemp.duration > 0 && currenttemp.rate > profile.current_basal) {
rT.reason += tick + ", and temp " + currenttemp.rate + " > basal " + profile.current_basal;
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
} else if (glucose_status.delta > 0 && currenttemp.duration > 0 && currenttemp.rate < profile.current_basal) {
rT.reason += tick + ", and temp " + currenttemp.rate + " < basal " + profile.current_basal;
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
}
rT.reason += ", bolus snooze: eventual BG range " + eventualBG + "-" + snoozeBG;
return rT;
} else {
// calculate 30m low-temp required to get projected BG up to target
// use snoozeBG to more gradually ramp in any counteraction of the user's boluses
// multiply by 2 to low-temp faster for increased hypo safety
var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / profile.sens);
if (minDelta < 0 && minDelta > expectedDelta) {
// if we're barely falling, newinsulinReq should be barely negative
rT.reason += ", Snooze BG " + snoozeBG;
var newinsulinReq = Math.round(( insulinReq * (minDelta / expectedDelta) ) * 100)/100;
//console.log("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq);
insulinReq = newinsulinReq;
}
// rate required to deliver insulinReq less insulin over 30m:
var rate = profile.current_basal + (2 * insulinReq);
rate = Math.round( rate * 1000 ) / 1000;
// if required temp < existing temp basal
var insulinScheduled = currenttemp.duration * (currenttemp.rate - profile.current_basal) / 60;
if (insulinScheduled < insulinReq - 0.2) { // if current temp would deliver >0.2U less than the required insulin, raise the rate
rT.reason = currenttemp.duration + "m@" + (currenttemp.rate - profile.current_basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " < req " + insulinReq + "-0.2U";
return setTempBasal(rate, 30, profile, rT, offline);
}
if (typeof currenttemp.rate !== 'undefined' && (currenttemp.duration > 5 && rate > currenttemp.rate - 0.1)) {
rT.reason += ", temp " + currenttemp.rate + " ~< req " + rate + "U/hr";
return rT;
} else {
rT.reason += ", setting " + rate + "U/hr";
return setTempBasal(rate, 30, profile, rT, offline);
}
}
}
}
// if eventual BG is above min but BG is falling faster than expected Delta
if (minDelta < expectedDelta) {
if (glucose_status.delta < glucose_status.avgdelta) {
rT.reason = "Eventual BG " + eventualBG + ">" + profile.min_bg + " but Delta " + tick + " < Exp. Delta " + expectedDelta;
} else {
rT.reason = "Eventual BG " + eventualBG + ">" + profile.min_bg + " but Avg. Delta " + glucose_status.avgdelta.toFixed(2) + " < Exp. Delta " + expectedDelta;
}
if (currenttemp.duration > 0) { // if there is currently any temp basal running
rT.reason = rT.reason += "; cancel";
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
} else {
rT.reason = rT.reason += "; no temp to cancel";
return rT;
}
}
if (eventualBG < profile.max_bg) {
rT.reason = eventualBG + " is in range. No temp required";
if (currenttemp.duration > 0) { // if there is currently any temp basal running
rT.reason = rT.reason += "; cancel";
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
}
if (offline == 'Offline') {
// if no temp is running or required, set the current basal as a temp, so you can see on the pump that the loop is working
if ((!currenttemp.duration || (currenttemp.rate == profile.current_basal)) && !rT.duration) {
rT.reason = rT.reason + "; setting current basal of " + profile.current_basal + " as temp";
return setTempBasal(profile.current_basal, 30, profile, rT, offline);
}
}
return rT;
}
if (snoozeBG < profile.max_bg) {
rT.reason = snoozeBG + " < " + profile.max_bg;
if (currenttemp.duration > 0) { // if there is currently any temp basal running
rT.reason = rT.reason += "; cancel";
return setTempBasal(0, 0, profile, rT, offline); // cancel temp
} else {
rT.reason = rT.reason += "; no temp to cancel";
return rT;
}
}
// eventual BG is at/above target:
// if iob is over max, just cancel any temps
var basaliob;
if (iob_data.basaliob) { basaliob = iob_data.basaliob; }
else { basaliob = iob_data.iob - iob_data.bolussnooze; }
rT.reason = "Eventual BG " + eventualBG + ">=" + profile.max_bg + ", ";
if (basaliob > max_iob) {
rT.reason = "basaliob " + basaliob + " > max_iob " + max_iob;
return setTempBasal(0, 0, profile, rT, offline);
} else { // otherwise, calculate 30m high-temp required to get projected BG down to target
// insulinReq is the additional insulin required to get down to max bg:
// if in meal assist mode, check if snoozeBG is lower, as eventualBG is not dependent on IOB
var insulinReq = (Math.min(snoozeBG,eventualBG) - target_bg) / profile.sens;
if (minDelta < 0 && minDelta > expectedDelta) {
var newinsulinReq = Math.round(( insulinReq * (1 - (minDelta / expectedDelta)) ) * 100)/100;
//console.log("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq);
insulinReq = newinsulinReq;
}
// if that would put us over max_iob, then reduce accordingly
if (insulinReq > max_iob-basaliob) {
rT.reason = "max_iob " + max_iob + ", ";
insulinReq = max_iob-basaliob;
}
// rate required to deliver insulinReq more insulin over 30m:
var rate = profile.current_basal + (2 * insulinReq);
rate = Math.round( rate * 1000 ) / 1000;
var maxSafeBasal = Math.min(profile.max_basal, 3 * profile.max_daily_basal, 4 * profile.current_basal);
if (rate > maxSafeBasal) {
rT.reason += "adj. req. rate:"+rate.toFixed(1) +" to maxSafeBasal:"+maxSafeBasal.toFixed(1)+", ";
rate = maxSafeBasal;
}
var insulinScheduled = currenttemp.duration * (currenttemp.rate - profile.current_basal) / 60;
if (insulinScheduled > insulinReq + 0.2) { // if current temp would deliver >0.2U more than the required insulin, lower the rate
rT.reason = currenttemp.duration + "m@" + (currenttemp.rate - profile.current_basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " > req " + insulinReq + "+0.2U";
return setTempBasal(rate, 30, profile, rT, offline);
}
if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { // no temp is set
rT.reason += "no temp, setting " + rate + "U/hr";
return setTempBasal(rate, 30, profile, rT, offline);
}
if (currenttemp.duration > 5 && rate < currenttemp.rate + 0.1) { // if required temp <~ existing temp basal
rT.reason += "temp " + currenttemp.rate + " >~ req " + rate + "U/hr";
return rT;
}
// required temp > existing temp basal
rT.reason += "temp " + currenttemp.rate + "<" + rate + "U/hr";
return setTempBasal(rate, 30, profile, rT, offline);
}
};
module.exports = determine_basal;

View file

@ -8,4 +8,5 @@ public class Config {
public static final boolean logFunctionCalls = true;
public static final boolean logIncommingBG = true;
public static final boolean logIncommingData = true;
public static final boolean logAPSResult = true;
}

View file

@ -10,6 +10,8 @@ import android.view.MenuItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.plugins.LowSuspend.LowSuspendFragment;
import info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAFragment;
import info.nightscout.androidaps.plugins.Overview.OverviewFragment;
import info.nightscout.androidaps.plugins.ProfileViewer.ProfileViewerFragment;
import info.nightscout.androidaps.plugins.TempBasals.TempBasalsFragment;
@ -37,6 +39,8 @@ public class MainActivity extends AppCompatActivity {
// Register all tabs in app here
mAdapter = new TabPageAdapter(getSupportFragmentManager());
mAdapter.registerNewFragment("Overview", OverviewFragment.newInstance());
mAdapter.registerNewFragment("LowSuspend", LowSuspendFragment.newInstance());
mAdapter.registerNewFragment("OpenAPS MA", OpenAPSMAFragment.newInstance());
mAdapter.registerNewFragment("Treatments", treatmentsFragment = TreatmentsFragment.newInstance());
mAdapter.registerNewFragment("TempBasals", tempBasalsFragment = TempBasalsFragment.newInstance());
mAdapter.registerNewFragment("Profile", ProfileViewerFragment.newInstance());

View file

@ -6,14 +6,10 @@ package info.nightscout.androidaps.data;
public class Iob {
public double iobContrib = 0d;
public double activityContrib = 0d;
public double netInsulin = 0d; // for calculations from temp basals only
public double netRatio = 0d; // for calculations from temp basals only
public Iob plus(Iob iob) {
iobContrib += iob.iobContrib;
activityContrib += iob.activityContrib;
netInsulin += iob.netInsulin;
netRatio += iob.netRatio;
return this;
}
}

View file

@ -5,6 +5,12 @@ package info.nightscout.androidaps.data;
*/
public abstract class Pump {
boolean tempBasalInProgress = false;
// Upload to pump new basal profile from MainApp.getNSProfile()
public abstract void setNewBasalProfile();
public abstract double getBaseBasalRate(); // base basal rate, not temp basal
public abstract double getTempBasalAbsoluteRate();
public abstract double getTempBasalRemainingMinutes();
}

View file

@ -160,4 +160,65 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return new ArrayList<BgReading>();
}
/*
* Returns glucose_status for openAPS or null if no actual data available
*/
public class GlucoseStatus {
public double glucose = 0d;
public double delta = 0d;
public double avgdelta = 0d;
}
public GlucoseStatus getGlucoseStatusData() {
GlucoseStatus result = new GlucoseStatus();
try {
Dao<BgReading, Long> daoBgreadings = null;
daoBgreadings = getDaoBgReadings();
List<BgReading> bgReadings;
QueryBuilder<BgReading, Long> queryBuilder = daoBgreadings.queryBuilder();
queryBuilder.orderBy("timeIndex", false);
queryBuilder.limit(4l);
PreparedQuery<BgReading> preparedQuery = queryBuilder.prepare();
bgReadings = daoBgreadings.query(preparedQuery);
int sizeRecords = bgReadings.size();
if (sizeRecords < 4 || bgReadings.get(sizeRecords - 1).timestamp > new Date().getTime() - 7 * 60 * 1000l)
return null;
int minutes = 5;
double change;
double avg;
if (bgReadings.size() > 3) {
BgReading now = bgReadings.get(sizeRecords - 1);
BgReading last = bgReadings.get(sizeRecords - 2);
BgReading last1 = bgReadings.get(sizeRecords - 3);
BgReading last2 = bgReadings.get(sizeRecords - 4);
if (last2.value > 30) {
minutes = 3 * 5;
change = now.value - last2.value;
} else if (last1.value > 30) {
minutes = 2 * 5;
change = now.value - last1.value;
} else if (last.value > 30) {
minutes = 5;
change = now.value - last.value;
} else {
change = 0;
}
//multiply by 5 to get the same unit as delta, i.e. mg/dL/5m
avg = change / minutes * 5;
result.glucose = now.value;
result.delta = change;
result.avgdelta = avg;
}
} catch (SQLException e) {
e.printStackTrace();
return null;
}
return result;
}
}

View file

@ -12,6 +12,7 @@ import java.util.TimeZone;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.data.Iob;
import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal;
import info.nightscout.client.data.NSProfile;
@DatabaseTable(tableName = "TempBasals")
@ -52,8 +53,8 @@ public class TempBasal {
public boolean isAbsolute; // true if if set as absolute value in U
public Iob iobCalc(Date time) {
Iob result = new Iob();
public IobTotal iobCalc(Date time) {
IobTotal result = new IobTotal();
NSProfile profile = MainApp.getNSProfile();
if (profile == null)
@ -89,8 +90,16 @@ public class TempBasal {
tempBolusPart.insulin = tempBolusSize;
Long date = this.timeStart.getTime() + j * tempBolusSpacing * 60 * 1000;
tempBolusPart.created_at = new Date(date);
Iob iob = tempBolusPart.iobCalc(time);
result.plus(iob);
Iob aIOB = tempBolusPart.iobCalc(time, profile.getDia());
result.basaliob += aIOB.iobContrib;
Double dia_ago = time.getTime() - profile.getDia() * 60 * 60 * 1000;
if (date > dia_ago && date <= time.getTime()) {
result.netbasalinsulin += tempBolusPart.insulin;
if (tempBolusPart.insulin > 0) {
result.hightempinsulin += tempBolusPart.insulin;
}
}
}
}
}

View file

@ -56,24 +56,8 @@ public class Treatment {
this.carbs = t.carbs;
}
public Iob iobCalc(Date time) {
Iob resultNow = doIobCalc(time);
Iob resultIn5min = doIobCalc(new Date(time.getTime() + 5 * 60 * 1000));
resultNow.activityContrib = resultNow.iobContrib - resultIn5min.iobContrib;
return resultNow;
}
public Iob doIobCalc(Date time) {
public Iob iobCalc(Date time, Double dia) {
Iob result = new Iob();
NSProfile profile = MainApp.getNSProfile();
if (profile == null) {
return result;
}
Double dia = profile.getDia();
Double sens = profile.getIsf(profile.secondsFromMidnight(time));
Double scaleFactor = 3.0 / dia;
Double peak = 75d;
@ -98,22 +82,6 @@ public class Treatment {
return result;
}
/*
public Iob calcIobOpenAPS() {
IobCalc calc = new IobCalc(created_at,insulin,new Date());
calc.setBolusDiaTimesTwo();
Iob iob = calc.invoke();
return iob;
}
public Iob calcIob() {
IobCalc calc = new IobCalc(created_at,insulin,new Date());
Iob iob = calc.invoke();
return iob;
}
*/
public long getMillisecondsFromStart() {
return new Date().getTime() - created_at.getTime();
}
@ -180,6 +148,4 @@ public class Treatment {
log.error("DBUPDATE No receivers");
} else log.debug("DBUPDATE dbUpdate " + q.size() + " receivers " + _id + " " + data.toString());
}
}

View file

@ -0,0 +1,13 @@
package info.nightscout.androidaps.plugins;
import java.util.Date;
/**
* Created by mike on 10.06.2016.
*/
public interface APSBase {
public APSResult getLastAPSResult();
public Date getLastAPSRun();
public void run();
}

View file

@ -0,0 +1,11 @@
package info.nightscout.androidaps.plugins;
/**
* Created by mike on 09.06.2016.
*/
public class APSResult {
public String reason;
public double rate;
public int duration;
public boolean changeRequested = false;
}

View file

@ -0,0 +1,53 @@
package info.nightscout.androidaps.plugins.LowSuspend;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.PluginBase;
public class LowSuspendFragment extends Fragment implements PluginBase {
@Override
public int getType() {
return PluginBase.APS;
}
@Override
public boolean isFragmentVisible() {
return true;
}
public static LowSuspendFragment newInstance() {
LowSuspendFragment fragment = new LowSuspendFragment();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerBus();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.lowsuspend_fragment, container, false);
}
private void registerBus() {
try {
MainApp.bus().unregister(this);
} catch (RuntimeException x) {
// Ignore
}
MainApp.bus().register(this);
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.plugins.LowSuspend;
import org.json.JSONException;
import org.json.JSONObject;
public class LowSuspendResult {
public boolean lowProjected;
public boolean low;
public String reason;
public int percent;
public JSONObject json() {
JSONObject json = new JSONObject();
try {
json.put("low", low);
json.put("lowProjected", lowProjected);
json.put("reason", reason);
json.put("percent", percent);
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
}

View file

@ -0,0 +1,244 @@
package info.nightscout.androidaps.plugins.OpenAPSMA;
import com.eclipsesource.v8.JavaVoidCallback;
import com.eclipsesource.v8.V8;
import com.eclipsesource.v8.V8Array;
import com.eclipsesource.v8.V8Object;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.data.Pump;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.plugins.ScriptReader;
import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment;
import info.nightscout.client.data.NSProfile;
public class DetermineBasalAdapterJS {
private static Logger log = LoggerFactory.getLogger(DetermineBasalAdapterJS.class);
private final ScriptReader mScriptReader;
V8 mV8rt;
private V8Object mProfile;
private V8Object mGlucoseStatus;
private V8Object mIobData;
private V8Object mMealData;
private V8Object mCurrentTemp;
private final String PARAM_currentTemp = "currentTemp";
private final String PARAM_iobData = "iobData";
private final String PARAM_glucoseStatus = "glucose_status";
private final String PARAM_profile = "profile";
private final String PARAM_meal_data = "meal_data";
public DetermineBasalAdapterJS(ScriptReader scriptReader) throws IOException {
mV8rt = V8.createV8Runtime();
mScriptReader = scriptReader;
init();
initLogCallback();
initProcessExitCallback();
initModuleParent();
loadScript();
}
public void init() {
// Profile
mProfile = new V8Object(mV8rt);
mProfile.add("max_iob", 0);
mProfile.add("carbs_hr", 0);
mProfile.add("dia", 0);
mProfile.add("type", "current");
mProfile.add("max_daily_basal", 0);
mProfile.add("max_basal", 0);
mProfile.add("max_bg", 0);
mProfile.add("min_bg", 0);
mProfile.add("carbratio", 0);
mProfile.add("sens", 0);
mProfile.add("current_basal", 0);
mV8rt.add(PARAM_profile, mProfile);
// Current temp
mCurrentTemp = new V8Object(mV8rt);
mCurrentTemp.add("temp", "absolute");
mCurrentTemp.add("duration", 0);
mCurrentTemp.add("rate", 0);
mV8rt.add(PARAM_currentTemp, mCurrentTemp);
// IOB data
mIobData = new V8Object(mV8rt);
mIobData.add("iob", 0); //netIob
mIobData.add("activity", 0); //netActivity
mIobData.add("bolusiob", 0); // backward compatibility with master
mIobData.add("bolussnooze", 0); //bolusIob
mIobData.add("basaliob", 0);
mIobData.add("netbasalinsulin", 0);
mIobData.add("hightempinsulin", 0);
mV8rt.add(PARAM_iobData, mIobData);
// Glucose status
mGlucoseStatus = new V8Object(mV8rt);
mGlucoseStatus.add("delta", 0);
mGlucoseStatus.add("glucose", 0);
mGlucoseStatus.add("avgdelta", 0);
mV8rt.add(PARAM_glucoseStatus, mGlucoseStatus);
// Meal data
mMealData = new V8Object(mV8rt);
mMealData.add("carbs", 0);
mMealData.add("boluses", 0);
mV8rt.add(PARAM_meal_data, mMealData);
}
public DetermineBasalResult invoke() {
mV8rt.executeVoidScript(
"console.error(\"determine_basal(\"+\n" +
"JSON.stringify(" + PARAM_glucoseStatus + ")+ \", \" +\n" +
"JSON.stringify(" + PARAM_currentTemp + ")+ \", \" + \n" +
"JSON.stringify(" + PARAM_iobData + ")+ \", \" +\n" +
"JSON.stringify(" + PARAM_meal_data + ")+ \", \" +\n" +
"JSON.stringify(" + PARAM_profile + ")+ \") \");");
mV8rt.executeVoidScript(
"var rT = determine_basal(" +
PARAM_glucoseStatus + ", " +
PARAM_currentTemp + ", " +
PARAM_iobData + ", " +
PARAM_profile + ", " +
"undefined, " +
PARAM_meal_data + ", " +
"setTempBasal" +
");");
String ret = mV8rt.executeStringScript("JSON.stringify(rT);");
if (Config.logAPSResult)
log.debug(ret);
V8Object v8ObjectReuslt = mV8rt.getObject("rT");
DetermineBasalResult result = null;
try {
result = new DetermineBasalResult(v8ObjectReuslt, new JSONObject(ret));
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
private void loadScript() throws IOException {
mV8rt.executeVoidScript(
readFile("OpenAPSMA/determine-basal.js"),
"OpenAPSMA/bin/oref0-determine-basal.js",
0);
mV8rt.executeVoidScript("var determine_basal = module.exports;");
// TODO: convert to variable too
mV8rt.executeVoidScript(
"var setTempBasal = function (rate, duration, profile, rT, offline) {" +
"rT.duration = duration;\n" +
" rT.rate = rate;" +
"return rT;" +
"};",
"setTempBasal.js",
0
);
}
private void initModuleParent() {
mV8rt.executeVoidScript("var module = {\"parent\":Boolean(1)};");
}
private void initProcessExitCallback() {
JavaVoidCallback callbackProccessExit = new JavaVoidCallback() {
@Override
public void invoke(V8Object arg0, V8Array parameters) {
if (parameters.length() > 0) {
Object arg1 = parameters.get(0);
log.error("ProccessExit " + arg1);
}
}
};
mV8rt.registerJavaMethod(callbackProccessExit, "proccessExit");
mV8rt.executeVoidScript("var process = {\"exit\": function () { proccessExit(); } };");
}
private void initLogCallback() {
JavaVoidCallback callbackLog = new JavaVoidCallback() {
@Override
public void invoke(V8Object arg0, V8Array parameters) {
if (parameters.length() > 0) {
Object arg1 = parameters.get(0);
if (Config.logAPSResult)
log.debug("JSLOG " + arg1);
}
}
};
mV8rt.registerJavaMethod(callbackLog, "log");
mV8rt.executeVoidScript("var console = {\"log\":log, \"error\":log};");
}
public void setData(NSProfile profile,
double maxIob,
double maxBasal,
double minBg,
double maxBg,
Pump pump,
IobTotal iobData,
DatabaseHelper.GlucoseStatus glucoseStatus,
TreatmentsFragment.MealData mealData) {
String units = profile.getUnits();
mProfile.add("max_iob", maxIob);
mProfile.add("carbs_hr", profile.getCarbAbsorbtionRate());
mProfile.add("dia", profile.getDia());
mProfile.add("type", "current");
mProfile.add("max_daily_basal", profile.getMaxDailyBasal());
mProfile.add("max_basal", maxBasal);
mProfile.add("min_bg", minBg);
mProfile.add("max_bg", maxBg);
mProfile.add("carbratio", profile.getIc(profile.secondsFromMidnight()));
mProfile.add("sens", NSProfile.toMgdl(profile.getIsf(profile.secondsFromMidnight()).doubleValue(), units));
mProfile.add("current_basal", pump.getBaseBasalRate());
mCurrentTemp.add("duration", pump.getTempBasalRemainingMinutes());
mCurrentTemp.add("rate", pump.getTempBasalAbsoluteRate());
mIobData.add("iob", iobData.iob); //netIob
mIobData.add("activity", iobData.activity); //netActivity
mIobData.add("bolusiob", iobData.bolussnooze); // backward compatibility with master
mIobData.add("bolussnooze", iobData.bolussnooze); //bolusIob
mIobData.add("basaliob", iobData.basaliob);
mIobData.add("netbasalinsulin", iobData.netbasalinsulin);
mIobData.add("hightempinsulin", iobData.hightempinsulin);
mGlucoseStatus.add("glucose", glucoseStatus.glucose);
mGlucoseStatus.add("delta", glucoseStatus.delta);
mGlucoseStatus.add("avgdelta", glucoseStatus.avgdelta);
mMealData.add("carbs", mealData.carbs);
mMealData.add("boluses", mealData.boluses);
}
public void release() {
mProfile.release();
mCurrentTemp.release();
mIobData.release();
mMealData.release();
mGlucoseStatus.release();
mV8rt.release();
}
public String readFile(String filename) throws IOException {
byte[] bytes = mScriptReader.readFile(filename);
String string = new String(bytes, "UTF-8");
if (string.startsWith("#!/usr/bin/env node")) {
string = string.substring(20);
}
return string;
}
}

View file

@ -0,0 +1,41 @@
package info.nightscout.androidaps.plugins.OpenAPSMA;
import com.eclipsesource.v8.V8Object;
import org.json.JSONObject;
import info.nightscout.androidaps.plugins.APSResult;
public class DetermineBasalResult extends APSResult {
public JSONObject json = new JSONObject();
public final double eventualBG;
public final double snoozeBG;
public final String mealAssist;
public DetermineBasalResult(V8Object result, JSONObject j) {
json = j;
reason = result.getString("reason");
eventualBG = result.getDouble("eventualBG");
snoozeBG = result.getDouble("snoozeBG");
if(result.contains("rate")) {
rate = result.getDouble("rate");
changeRequested = true;
} else {
rate = -1;
changeRequested = false;
}
if(result.contains("duration")) {
duration = result.getInteger("duration");
changeRequested = changeRequested & true;
} else {
duration = -1;
changeRequested = false;
}
if(result.contains("mealAssist")) {
mealAssist = result.getString("mealAssist");
} else mealAssist = "";
result.release();
}
}

View file

@ -0,0 +1,77 @@
package info.nightscout.androidaps.plugins.OpenAPSMA;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Date;
import info.nightscout.utils.DateUtil;
public class IobTotal {
public Double iob;
public Double activity;
public Double bolussnooze;
public Double basaliob;
public Double netbasalinsulin;
public Double hightempinsulin;
public Double netInsulin = 0d; // for calculations from temp basals only
public Double netRatio = 0d; // for calculations from temp basals only
public IobTotal() {
this.iob = 0d;
this.activity = 0d;
this.bolussnooze = 0d;
this.basaliob = 0d;
this.netbasalinsulin = 0d;
this.hightempinsulin = 0d;
}
public IobTotal plus(IobTotal other) {
iob += other.iob;
activity = other.activity;
bolussnooze = other.bolussnooze;
basaliob = other.iob;
netbasalinsulin = other.netbasalinsulin;
hightempinsulin = other.hightempinsulin;
netInsulin += other.netInsulin;
netRatio += other.netRatio;
return this;
}
public static IobTotal combine(IobTotal bolusIOB, IobTotal basalIob) {
IobTotal result = new IobTotal();
result.iob = bolusIOB.iob;
result.activity = bolusIOB.activity;
result.bolussnooze = bolusIOB.bolussnooze;
result.basaliob = basalIob.iob;
result.netbasalinsulin = basalIob.netbasalinsulin;
result.hightempinsulin = basalIob.hightempinsulin;
return result;
}
public JSONObject json() {
JSONObject json = new JSONObject();
try {
json.put("iob", iob);
json.put("activity", activity);
json.put("bolusIob", bolussnooze);
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
public JSONObject nsJson() {
JSONObject json = new JSONObject();
try {
json.put("iob", bolussnooze);
json.put("basaliob", iob);
json.put("activity", activity);
json.put("timestamp", DateUtil.toISOString(new Date()));
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
}

View file

@ -0,0 +1,209 @@
package info.nightscout.androidaps.plugins.OpenAPSMA;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import com.squareup.otto.Subscribe;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Date;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainActivity;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Iob;
import info.nightscout.androidaps.data.Pump;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.events.EventNewBG;
import info.nightscout.androidaps.events.EventTreatmentChange;
import info.nightscout.androidaps.plugins.APSBase;
import info.nightscout.androidaps.plugins.APSResult;
import info.nightscout.androidaps.plugins.PluginBase;
import info.nightscout.androidaps.plugins.ScriptReader;
import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment;
import info.nightscout.client.data.NSProfile;
import info.nightscout.utils.DateUtil;
public class OpenAPSMAFragment extends Fragment implements View.OnClickListener, PluginBase, APSBase {
private static Logger log = LoggerFactory.getLogger(OpenAPSMAFragment.class);
Button run;
Date lastAPSRun = null;
APSResult lastAPSResult = null;
@Override
public int getType() {
return PluginBase.APS;
}
@Override
public boolean isFragmentVisible() {
return true;
}
@Override
public APSResult getLastAPSResult() {
return lastAPSResult;
}
@Override
public Date getLastAPSRun() {
return lastAPSRun;
}
public static OpenAPSMAFragment newInstance() {
OpenAPSMAFragment fragment = new OpenAPSMAFragment();
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerBus();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.openapsma_fragment, container, false);
run = (Button) view.findViewById(R.id.openapsma_run);
run.setOnClickListener(this);
return view;
}
private void registerBus() {
try {
MainApp.bus().unregister(this);
} catch (RuntimeException x) {
// Ignore
}
MainApp.bus().register(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.openapsma_run:
run();
break;
}
}
@Subscribe
public void onStatusEvent(final EventTreatmentChange ev) {
Activity activity = getActivity();
if (activity != null)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
run();
}
});
else
log.debug("EventTreatmentChange: Activity is null");
}
@Subscribe
public void onStatusEvent(final EventNewBG ev) {
Activity activity = getActivity();
if (activity != null)
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
run();
}
});
else
log.debug("EventNewBG: Activity is null");
}
@Override
public void run() {
// private DatermineBasalResult openAps(int glucoseValue, int delta, double deltaAvg15min, StatusEvent status, LowSuspendStatus lowSuspendStatus, IobTotal iobTotal, CarbCalc.Meal mealdata) {
DetermineBasalAdapterJS determineBasalAdapterJS = null;
try {
determineBasalAdapterJS = new DetermineBasalAdapterJS(new ScriptReader(MainApp.instance().getBaseContext()));
} catch (IOException e) {
log.error(e.getMessage(), e);
return;
}
DatabaseHelper.GlucoseStatus glucoseStatus = MainApp.getDbHelper().getGlucoseStatusData();
NSProfile profile = MainApp.getNSProfile();
Pump pump = MainApp.getActivePump();
if (glucoseStatus == null) {
if (Config.logAPSResult) log.debug("No glucose data available");
return;
}
if (profile == null) {
if (Config.logAPSResult) log.debug("No profile available");
return;
}
if (pump == null) {
if (Config.logAPSResult) log.debug("No pump available");
return;
}
SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext());
String units = profile.getUnits();
String maxBgDefault = "180";
String minBgDefault = "100";
if (!units.equals(Constants.MGDL)) {
maxBgDefault = "10";
minBgDefault = "5";
}
// TODO: objectives limits
double maxIob = Double.parseDouble(SP.getString("max_iob", "1.5").replace(",", "."));
double maxBasal = Double.parseDouble(SP.getString("max_basal", "1").replace(",", "."));
// TODO: min_bg, max_bg in prefs
double minBg = NSProfile.toMgdl(Double.parseDouble(SP.getString("min_bg", minBgDefault).replace(",", ".")), units);
double maxBg = NSProfile.toMgdl(Double.parseDouble(SP.getString("max_bg", maxBgDefault).replace(",", ".")), units);
MainActivity.treatmentsFragment.updateTotalIOBIfNeeded();
MainActivity.tempBasalsFragment.updateTotalIOBIfNeeded();
IobTotal bolusIob = MainActivity.treatmentsFragment.lastCalculation;
IobTotal basalIob = MainActivity.tempBasalsFragment.lastCalculation;
IobTotal iobTotal = IobTotal.combine(bolusIob, basalIob);
TreatmentsFragment.MealData mealData = MainActivity.treatmentsFragment.getMealData();
determineBasalAdapterJS.setData(profile, maxIob, maxBasal, minBg, maxBg, pump, iobTotal, glucoseStatus, mealData);
DetermineBasalResult determineBasalResult = determineBasalAdapterJS.invoke();
determineBasalAdapterJS.release();
try {
determineBasalResult.json.put("timestamp", DateUtil.toISOString(new Date()));
} catch (JSONException e) {
e.printStackTrace();
}
lastAPSResult = determineBasalResult;
lastAPSRun = new Date();
//deviceStatus.suggested = determineBasalResult.json;
}
}

View file

@ -4,14 +4,12 @@ import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.ShareCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.helper.DateAsXAxisLabelFormatter;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.LineGraphSeries;
import com.jjoe64.graphview.series.PointsGraphSeries;

View file

@ -1,5 +1,7 @@
package info.nightscout.androidaps.plugins;
import java.util.Date;
/**
* Created by mike on 09.06.2016.
*/

View file

@ -0,0 +1,41 @@
package info.nightscout.androidaps.plugins;
import android.content.Context;
import android.content.res.AssetManager;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class ScriptReader {
private final Context mContext;
public ScriptReader(Context context) {
mContext = context;
}
public byte[] readFile(String fileName) throws IOException {
AssetManager assetManager = mContext.getAssets();
InputStream is = assetManager.open(fileName);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
byte[] bytes = buffer.toByteArray();
is.close();
buffer.close();
return bytes;
}
}

View file

@ -35,6 +35,7 @@ import info.nightscout.androidaps.data.Iob;
import info.nightscout.androidaps.db.TempBasal;
import info.nightscout.androidaps.events.EventNewBG;
import info.nightscout.androidaps.events.EventTempBasalChange;
import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal;
import info.nightscout.androidaps.plugins.PluginBase;
@ -45,10 +46,9 @@ public class TempBasalsFragment extends Fragment implements PluginBase {
LinearLayoutManager llm;
TextView iobTotal;
TextView activityTotal;
public long lastCalculationTimestamp = 0;
public Iob lastCalculation;
public IobTotal lastCalculation;
private static DecimalFormat formatNumber0decimalplaces = new DecimalFormat("0");
private static DecimalFormat formatNumber2decimalplaces = new DecimalFormat("0.00");
@ -107,15 +107,14 @@ public class TempBasalsFragment extends Fragment implements PluginBase {
}
private void updateTotalIOB() {
Iob total = new Iob();
Date now = new Date();
IobTotal total = new IobTotal();
for (Integer pos = 0; pos < tempBasals.size(); pos++) {
TempBasal t = tempBasals.get(pos);
total.plus(t.iobCalc(new Date()));
total.plus(t.iobCalc(now));
}
if (iobTotal != null)
iobTotal.setText(formatNumber2decimalplaces.format(total.iobContrib));
if (activityTotal != null)
activityTotal.setText(formatNumber3decimalplaces.format(total.activityContrib));
iobTotal.setText(formatNumber2decimalplaces.format(total.basaliob));
}
public static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TempBasalsViewHolder> {
@ -152,9 +151,8 @@ public class TempBasalsFragment extends Fragment implements PluginBase {
holder.percent.setText(formatNumber0decimalplaces.format(tempBasals.get(position).percent) + "%");
}
holder.realDuration.setText(formatNumber0decimalplaces.format(tempBasals.get(position).getRealDuration()) + " min");
Iob iob = tempBasals.get(position).iobCalc(new Date());
holder.iob.setText(formatNumber2decimalplaces.format(iob.iobContrib) + " U");
holder.activity.setText(formatNumber3decimalplaces.format(iob.activityContrib) + " U");
IobTotal iob = tempBasals.get(position).iobCalc(new Date());
holder.iob.setText(formatNumber2decimalplaces.format(iob.basaliob) + " U");
holder.netInsulin.setText(formatNumber2decimalplaces.format(iob.netInsulin) + " U");
holder.netRatio.setText(formatNumber2decimalplaces.format(iob.netRatio) + " U/h");
}
@ -179,7 +177,6 @@ public class TempBasalsFragment extends Fragment implements PluginBase {
TextView netRatio;
TextView netInsulin;
TextView iob;
TextView activity;
TempBasalsViewHolder(View itemView) {
super(itemView);
@ -192,7 +189,6 @@ public class TempBasalsFragment extends Fragment implements PluginBase {
netRatio = (TextView) itemView.findViewById(R.id.tempbasals_netratio);
netInsulin = (TextView) itemView.findViewById(R.id.tempbasals_netinsulin);
iob = (TextView) itemView.findViewById(R.id.tempbasals_iob);
activity = (TextView) itemView.findViewById(R.id.tempbasals_activity);
}
}
}
@ -227,7 +223,6 @@ public class TempBasalsFragment extends Fragment implements PluginBase {
recyclerView.setAdapter(adapter);
iobTotal = (TextView) view.findViewById(R.id.tempbasals_iobtotal);
activityTotal = (TextView) view.findViewById(R.id.tempbasals_iobactivitytotal);
return view;
}

View file

@ -60,8 +60,8 @@ public class NewTreatmentDialogFragment extends DialogFragment implements OnClic
Double maxcarbs = Double.parseDouble(SP.getString("safety_maxcarbs", "48"));
String insulinText = this.insulin.getText().toString();
String carbsText = this.carbs.getText().toString();
String insulinText = this.insulin.getText().toString().replace(",", ".");
String carbsText = this.carbs.getText().toString().replace(",", ".");
Double insulin = Double.parseDouble(!insulinText.equals("") ? this.insulin.getText().toString() : "0");
Double carbs = Double.parseDouble(!carbsText.equals("") ? this.carbs.getText().toString() : "0");
if (insulin > maxbolus) {

View file

@ -24,6 +24,7 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Iob;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.Treatment;
import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal;
import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment;
import info.nightscout.client.data.NSProfile;
import info.nightscout.utils.*;
@ -175,10 +176,10 @@ public class WizardDialogFragment extends DialogFragment implements OnClickListe
MainActivity.treatmentsFragment.updateTotalIOBIfNeeded();
MainActivity.tempBasalsFragment.updateTotalIOBIfNeeded();
Iob bolusIob = MainActivity.treatmentsFragment.lastCalculation;
Iob basalIob = MainActivity.tempBasalsFragment.lastCalculation;
bolusIob.plus(basalIob);
iobInsulin.setText("-" + numberFormat.format(bolusIob.iobContrib) + "U");
IobTotal bolusIob = MainActivity.treatmentsFragment.lastCalculation;
IobTotal basalIob = MainActivity.tempBasalsFragment.lastCalculation;
Double iobTotal = bolusIob.iob + basalIob.iob;
iobInsulin.setText("-" + numberFormat.format(iobTotal) + "U");
totalInsulin.setText("");
wizardDialogDeliverButton.setVisibility(Button.GONE);
@ -194,9 +195,9 @@ public class WizardDialogFragment extends DialogFragment implements OnClickListe
NSProfile profile = MainApp.instance().getNSProfile();
// Entered values
String i_bg = this.bgInput.getText().toString();
String i_carbs = this.carbsInput.getText().toString();
String i_correction = this.correctionInput.getText().toString();
String i_bg = this.bgInput.getText().toString().replace("," , ".");
String i_carbs = this.carbsInput.getText().toString().replace(",", ".");
String i_correction = this.correctionInput.getText().toString().replace(",", ".");
Double c_bg = 0d;
try { c_bg = Double.parseDouble(i_bg.equals("") ? "0" : i_bg); } catch (Exception e) {}
Double c_carbs = 0d;
@ -240,10 +241,10 @@ public class WizardDialogFragment extends DialogFragment implements OnClickListe
MainActivity.treatmentsFragment.updateTotalIOBIfNeeded();
MainActivity.tempBasalsFragment.updateTotalIOBIfNeeded();
Iob bolusIob = MainActivity.treatmentsFragment.lastCalculation;
Iob basalIob = MainActivity.tempBasalsFragment.lastCalculation;
bolusIob.plus(basalIob);
Double insulingFromIOB = iobCheckbox.isChecked() ? bolusIob.iobContrib : 0d;
IobTotal bolusIob = MainActivity.treatmentsFragment.lastCalculation;
IobTotal basalIob = MainActivity.tempBasalsFragment.lastCalculation;
Double iobTotal = bolusIob.iob + basalIob.iob;
Double insulingFromIOB = iobCheckbox.isChecked() ? iobTotal : 0d;
iobInsulin.setText("-" + numberFormat.format(insulingFromIOB) + "U");
// Insulin from correction

View file

@ -39,9 +39,11 @@ import info.nightscout.androidaps.data.Iob;
import info.nightscout.androidaps.db.Treatment;
import info.nightscout.androidaps.events.EventNewBG;
import info.nightscout.androidaps.events.EventTreatmentChange;
import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal;
import info.nightscout.androidaps.plugins.PluginBase;
import info.nightscout.androidaps.plugins.Treatments.Dialogs.NewTreatmentDialogFragment;
import info.nightscout.androidaps.Services.Intents;
import info.nightscout.client.data.NSProfile;
public class TreatmentsFragment extends Fragment implements View.OnClickListener, NewTreatmentDialogFragment.Communicator, PluginBase {
private static Logger log = LoggerFactory.getLogger(TreatmentsFragment.class);
@ -54,7 +56,7 @@ public class TreatmentsFragment extends Fragment implements View.OnClickListener
Button refreshFromNS;
public long lastCalculationTimestamp = 0;
public Iob lastCalculation;
public IobTotal lastCalculation;
private static DecimalFormat formatNumber0decimalplaces = new DecimalFormat("0");
private static DecimalFormat formatNumber2decimalplaces = new DecimalFormat("0.00");
@ -101,20 +103,61 @@ public class TreatmentsFragment extends Fragment implements View.OnClickListener
}
private void updateTotalIOB() {
Iob total = new Iob();
IobTotal total = new IobTotal();
NSProfile profile = MainApp.getNSProfile();
if (profile == null) {
lastCalculation = total;
return;
}
Double dia = profile.getDia();
Date now = new Date();
for (Integer pos = 0; pos < treatments.size(); pos++) {
Treatment t = treatments.get(pos);
total.plus(t.iobCalc(new Date()));
Iob tIOB = t.iobCalc(now, dia);
total.iob += tIOB.iobContrib;
total.activity += tIOB.activityContrib;
Iob bIOB = t.iobCalc(now, dia / 2);
total.bolussnooze += bIOB.iobContrib;
}
if (iobTotal != null)
iobTotal.setText(formatNumber2decimalplaces.format(total.iobContrib));
iobTotal.setText(formatNumber2decimalplaces.format(total.iob));
if (activityTotal != null)
activityTotal.setText(formatNumber3decimalplaces.format(total.activityContrib));
activityTotal.setText(formatNumber3decimalplaces.format(total.activity));
lastCalculationTimestamp = new Date().getTime();
lastCalculation = total;
}
public class MealData {
public double boluses = 0d;
public double carbs = 0d;
}
public MealData getMealData() {
MealData result = new MealData();
NSProfile profile = MainApp.getNSProfile();
if (profile == null)
return result;
for (Treatment treatment : treatments) {
long now = new Date().getTime();
long dia_ago = now - (new Double(profile.getDia() * 60 * 60 * 1000l)).longValue();
long t = treatment.created_at.getTime();
if (t > dia_ago && t <= now) {
if (treatment.carbs >= 1) {
result.carbs += treatment.carbs;
}
if (treatment.insulin >= 0.1) {
result.boluses += treatment.insulin;
}
}
}
return result;
}
public static class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.TreatmentsViewHolder> {
List<Treatment> treatments;
@ -132,12 +175,15 @@ public class TreatmentsFragment extends Fragment implements View.OnClickListener
@Override
public void onBindViewHolder(TreatmentsViewHolder holder, int position) {
NSProfile profile = MainApp.getNSProfile();
if (profile == null)
return;
// TODO: implement locales
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, new Locale("cs", "CZ"));
holder.date.setText(df.format(treatments.get(position).created_at));
holder.insulin.setText(formatNumber2decimalplaces.format(treatments.get(position).insulin) + " U");
holder.carbs.setText(formatNumber0decimalplaces.format(treatments.get(position).carbs) + " g");
Iob iob = treatments.get(position).iobCalc(new Date());
Iob iob = treatments.get(position).iobCalc(new Date(), profile.getDia());
holder.iob.setText(formatNumber2decimalplaces.format(iob.iobContrib) + " U");
holder.activity.setText(formatNumber3decimalplaces.format(iob.activityContrib) + " U");
}
@ -205,8 +251,8 @@ public class TreatmentsFragment extends Fragment implements View.OnClickListener
activityTotal = (TextView) view.findViewById(R.id.treatments_iobactivitytotal);
refreshFromNS = (Button) view.findViewById(R.id.treatments_reshreshfromnightscout);
refreshFromNS.setOnClickListener(this);
return view;
}

View file

@ -9,6 +9,8 @@ import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import info.nightscout.androidaps.Constants;
public class NSProfile {
private JSONObject json = null;
private String activeProfile = null;
@ -292,4 +294,9 @@ public class NSProfile {
long passed = now - c.getTimeInMillis();
return (int) (passed / 1000);
}
public static Double toMgdl(Double value, String units) {
if (units.equals(Constants.MGDL)) return value;
else return value * Constants.MMOLL_TO_MGDL;
}
}

View file

@ -0,0 +1,13 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.LowSuspend.LowSuspendFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>

View file

@ -0,0 +1,19 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAFragment">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/openapsma_run"
android:id="@+id/openapsma_run" />
</LinearLayout>
</FrameLayout>

View file

@ -31,20 +31,6 @@
android:id="@+id/tempbasals_iobtotal"
android:paddingLeft="10dp" />
<TextView
android:id="@+id/tempbasals_iobactivitytotal_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:text="@string/tempbasals_iobactivitytotal_label_string"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold" />
<TextView
android:id="@+id/tempbasals_iobactivitytotal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp" />
</LinearLayout>
<android.support.v7.widget.RecyclerView

View file

@ -144,26 +144,6 @@
android:layout_marginRight="30dp"
android:text="0.12 U" />
<TextView
android:id="@+id/tempbasals_activity_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="@string/tempbasals_activity_string"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold" />
<TextView
android:id="@+id/tempbasals_activity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="30dp"
android:text="0.002 U" />
</LinearLayout>
</LinearLayout>

View file

@ -44,9 +44,7 @@
<string name="tempbasals_netratio_label_string">Ratio:</string>
<string name="tempbasals_netinsulin_label_string">Ins:</string>
<string name="tempbasals_iob_label_string">IOB:</string>
<string name="tempbasals_activity_string">Activity:</string>
<string name="tempbasals_iobtotal_label_string">Total IOB:</string>
<string name="tempbasals_iobactivitytotal_label_string">Total IOB activity:</string>
<string name="treatments_newtreatment_insulinamount_label">Insulin amount</string>
<string name="treatments_newtreatment_carbsamount_label">Carbs amount</string>
<string name="treatments_wizard_bg_label">BG</string>
@ -56,5 +54,6 @@
<string name="treatments_wizard_iob_label">IOB</string>
<string name="treatments_wizard_total_label">TOTAL</string>
<string name="profileview_units_label">Units:</string>
<string name="openapsma_run">Run now</string>
</resources>