Skip to content

Commit 9d55484

Browse files
committed
Nightscout uploader fixes
1 parent dae0615 commit 9d55484

File tree

19 files changed

+305
-69
lines changed

19 files changed

+305
-69
lines changed

Common/src/main/cpp/net/watchserver/uploader.cpp

Lines changed: 156 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//#ifndef WEAROS
33
#include <jni.h>
44
#include <atomic>
5+
#include <cctype>
56
#include <ctime>
67
#include <memory>
78
#include <string>
@@ -38,11 +39,13 @@ jstring jnightuploadEntries3url=nullptr;
3839
jstring jnightuploadTreatmentsurl=nullptr;
3940
jstring jnightuploadTreatments3url=nullptr;
4041
jstring jnightuploadsecret= nullptr;
42+
jclass nightscoutcalibrationclass=nullptr;
4143
static int lastNightUploadCode = 0;
4244
static std::atomic<int> lastNightUploadResponseCode{0};
4345
static std::atomic<long long> lastNightUploadAttemptTime{0};
4446
static std::atomic<long long> lastNightUploadSuccessTime{0};
4547
static std::atomic<int> lastNightUploadWaitMinutes{0};
48+
static bool lastNightUploadConfigError = false;
4649
/*
4750
void makeuploadurl(JNIEnv *env) {
4851
const int namelen=settings->data()->nightuploadnamelen;
@@ -57,10 +60,54 @@ void makeuploadurl(JNIEnv *env) {
5760
jnightuploadEntriesurl= (jstring)env->NewGlobalRef(local);
5861
env->DeleteLocalRef(local);
5962
} */
60-
static void makeuploadurl(JNIEnv *env,std::string_view pathstr,jstring &url) {
63+
static bool hasScheme(std::string_view text) {
64+
return text.rfind("http://", 0) == 0 || text.rfind("https://", 0) == 0;
65+
}
66+
67+
static int normalizeNightscoutBaseUrl(char *dest, int destsize, const char *src, int srclen) {
68+
if(!dest || destsize <= 0) {
69+
return 0;
70+
}
71+
if(!src || srclen <= 0) {
72+
dest[0]='\0';
73+
return 0;
74+
}
75+
const char *start=src;
76+
const char *end=src+srclen;
77+
while(start<end && isspace(static_cast<unsigned char>(*start)))
78+
++start;
79+
while(end>start && isspace(static_cast<unsigned char>(end[-1])))
80+
--end;
81+
std::string normalized(start,end-start);
82+
if(normalized.empty()) {
83+
dest[0]='\0';
84+
return 0;
85+
}
86+
if(!hasScheme(normalized)) {
87+
normalized.insert(0,"https://");
88+
}
89+
while(!normalized.empty() && normalized.back()=='/')
90+
normalized.pop_back();
91+
const auto entriespos=normalized.find("/api/");
92+
if(entriespos!=std::string_view::npos)
93+
normalized.resize(entriespos);
94+
int len=std::min<int>(normalized.size(),destsize-1);
95+
memcpy(dest,normalized.data(),len);
96+
dest[len]='\0';
97+
return len;
98+
}
99+
100+
static bool makeuploadurl(JNIEnv *env,std::string_view pathstr,jstring &url) {
61101

62102
const int namelen=settings->data()->nightuploadnamelen;
63103
const char *name=settings->data()->nightuploadname;
104+
if(namelen<=0 || !hasScheme(std::string_view(name,namelen))) {
105+
if(url) {
106+
env->DeleteGlobalRef(url);
107+
url=nullptr;
108+
}
109+
return false;
110+
}
64111
char fullname[namelen+pathstr.size()+1];
65112
memcpy(fullname,name,namelen);
66113
memcpy(fullname+namelen,pathstr.data(),pathstr.size()+1);
@@ -70,12 +117,32 @@ static void makeuploadurl(JNIEnv *env,std::string_view pathstr,jstring &url) {
70117
env->DeleteGlobalRef(url);
71118
url= (jstring)env->NewGlobalRef(local);
72119
env->DeleteLocalRef(local);
120+
return true;
121+
}
122+
static bool makeuploadurls(JNIEnv *env) {
123+
const bool entriesv1=makeuploadurl(env,R"(/api/v1/entries)",jnightuploadEntriesurl);
124+
const bool entriesv3=makeuploadurl(env,R"(/api/v3/entries)",jnightuploadEntries3url);
125+
const bool treatmentsv1=makeuploadurl(env,R"(/api/v1/treatments)",jnightuploadTreatmentsurl);
126+
const bool treatmentsv3=makeuploadurl(env,R"(/api/v3/treatments)",jnightuploadTreatments3url);
127+
return entriesv1&&entriesv3&&treatmentsv1&&treatmentsv3;
128+
}
129+
130+
static bool ensureNightscoutBaseUrl() {
131+
char normalized[sizeof(settings->data()->nightuploadname)]{};
132+
const int len=normalizeNightscoutBaseUrl(
133+
normalized,
134+
sizeof(normalized),
135+
settings->data()->nightuploadname,
136+
settings->data()->nightuploadnamelen
137+
);
138+
if(len<=0)
139+
return false;
140+
if(len!=settings->data()->nightuploadnamelen || memcmp(normalized,settings->data()->nightuploadname,len)!=0) {
141+
memcpy(settings->data()->nightuploadname,normalized,len+1);
142+
settings->data()->nightuploadnamelen=len;
143+
settings->updated();
73144
}
74-
static void makeuploadurls(JNIEnv *env) {
75-
makeuploadurl(env,R"(/api/v1/entries)",jnightuploadEntriesurl);
76-
makeuploadurl(env,R"(/api/v3/entries)",jnightuploadEntries3url);
77-
makeuploadurl(env,R"(/api/v1/treatments)",jnightuploadTreatmentsurl);
78-
makeuploadurl(env,R"(/api/v3/treatments)",jnightuploadTreatments3url);
145+
return true;
79146
}
80147

81148
extern std::string sha1encode(const char *secret, int len);
@@ -97,6 +164,12 @@ static void makeuploadsecret(JNIEnv *env) {
97164
bool inituploader(JNIEnv *env) {
98165
if(!settings->data()->nightuploadon)
99166
return false;
167+
if(!ensureNightscoutBaseUrl()) {
168+
lastNightUploadCode = -2;
169+
lastNightUploadResponseCode = -2;
170+
lastNightUploadConfigError = true;
171+
return false;
172+
}
100173
if(nightpostclass==nullptr) {
101174
constexpr const char nightpostclassstr[]="tk/glucodata/NightPost";
102175
if(jclass cl=env->FindClass(nightpostclassstr)) {
@@ -109,14 +182,56 @@ bool inituploader(JNIEnv *env) {
109182
return false;
110183
}
111184
}
185+
if(nightscoutcalibrationclass==nullptr) {
186+
constexpr const char calibrationclassstr[]="tk/glucodata/NightscoutCalibration";
187+
if(jclass cl=env->FindClass(calibrationclassstr)) {
188+
nightscoutcalibrationclass=(jclass)env->NewGlobalRef(cl);
189+
env->DeleteLocalRef(cl);
190+
}
191+
}
112192
makeuploadsecret(env);
113-
makeuploadurls(env);
114-
extern void startuploaderthread();
193+
if(!makeuploadurls(env)) {
194+
lastNightUploadCode = -2;
195+
lastNightUploadResponseCode = -2;
196+
lastNightUploadConfigError = true;
197+
LOGSTRING("Nightscout uploader disabled until URL is valid\n");
198+
return false;
199+
}
200+
extern void startuploaderthread();
115201
startuploaderthread();
116202
LOGAR("end inituploader");
117203
return true;
118204
}
119205

206+
static int getNightscoutCalibrationOverrideForItem(SensorGlucoseData *sens,const char *sensorname,int autoMgdl,int rawCurrent,long long timestampMillis) {
207+
if(nightscoutcalibrationclass==nullptr)
208+
return 0;
209+
auto env=getenv();
210+
if(env==nullptr)
211+
return 0;
212+
const static jmethodID nightscoutCalibrationOverride = env->GetStaticMethodID(
213+
nightscoutcalibrationclass,
214+
"getNightscoutCalibrationOverride",
215+
"(Ljava/lang/String;IIIJ)I"
216+
);
217+
if(nightscoutCalibrationOverride==nullptr)
218+
return 0;
219+
auto jsensor=env->NewStringUTF(sensorname);
220+
const auto *info=sens->getinfo();
221+
const int viewMode=info?info->viewMode:0;
222+
const int overrideValue=env->CallStaticIntMethod(
223+
nightscoutcalibrationclass,
224+
nightscoutCalibrationOverride,
225+
jsensor,
226+
viewMode,
227+
autoMgdl,
228+
rawCurrent,
229+
timestampMillis
230+
);
231+
env->DeleteLocalRef(jsensor);
232+
return overrideValue;
233+
}
234+
120235
//static boolean upload(String httpurl,byte[] postdata,String secret) ;
121236
extern vector<Numdata*> numdatas;
122237
static void reset() {
@@ -132,6 +247,12 @@ static void reset() {
132247
numdata->setNightSend(0);
133248
}
134249
static int nightupload(jstring jnightuploadurl,const char *data,int len,bool put) {
250+
if(jnightuploadurl==nullptr) {
251+
lastNightUploadCode = -2;
252+
lastNightUploadResponseCode = -2;
253+
lastNightUploadConfigError = true;
254+
return -2;
255+
}
135256
const static jmethodID upload=getenv()->GetStaticMethodID(nightpostclass,"upload","(Ljava/lang/String;[BLjava/lang/String;Z)I");
136257
auto env=getenv();
137258
jbyteArray uit=env->NewByteArray(len);
@@ -140,8 +261,10 @@ static int nightupload(jstring jnightuploadurl,const char *data,int len,bool put
140261
int res=env->CallStaticIntMethod(nightpostclass,upload,jnightuploadurl,uit,jnightuploadsecret,put);
141262
lastNightUploadCode = res;
142263
lastNightUploadResponseCode = res;
264+
lastNightUploadConfigError = (res==-2);
143265
if(res==HTTP_OK || res==201) {
144266
lastNightUploadSuccessTime = static_cast<long long>(time(nullptr));
267+
lastNightUploadConfigError = false;
145268
}
146269
LOGGER("nightupload=%d\n",res);
147270
env->DeleteLocalRef(uit);
@@ -170,7 +293,12 @@ extern std::string_view getdeltaname(float change);
170293
template <class T> int mkuploaditem(SensorGlucoseData *sens,char *buf,const char *sensorname,const T &item) {
171294
const time_t tim=item.gettime();
172295
int mgdL;
173-
if(double calibrated=calibrateONEtest(sens,item);!isnan(calibrated)) {
296+
const int rawCurrent=sens->getRawForPoll(&item);
297+
const int overrideValue=getNightscoutCalibrationOverrideForItem(sens,sensorname,item.getmgdL(),rawCurrent,tim*1000LL);
298+
if(overrideValue>0) {
299+
mgdL=overrideValue;
300+
}
301+
else if(double calibrated=calibrateONEtest(sens,item);!isnan(calibrated)) {
174302
mgdL=(int)round(calibrated);
175303
}
176304
else
@@ -239,7 +367,19 @@ static bool uploadCGM3() {
239367
extern char * writev3entry(char *outin,const ScanData *val, const sensorname_t *sensorname,bool server=true);
240368

241369
const char *ptr;
242-
if(double calibrated=calibrateONEtest(sens,*el);!isnan(calibrated)) {
370+
const int overrideValue=getNightscoutCalibrationOverrideForItem(
371+
sens,
372+
sensorname->data(),
373+
el->getmgdL(),
374+
sens->getRawForPoll(el),
375+
el->gettime()*1000LL
376+
);
377+
if(overrideValue>0) {
378+
ScanData newel=*el;
379+
newel.g=overrideValue;
380+
ptr=writev3entry(buf,&newel, sensorname,false);
381+
}
382+
else if(double calibrated=calibrateONEtest(sens,*el);!isnan(calibrated)) {
243383
ScanData newel=*el;
244384
newel.g=(int32_t)round(calibrated);
245385
ptr=writev3entry(buf,&newel, sensorname,false);
@@ -419,7 +559,7 @@ static void uploaderthread() {
419559
uploaded = uploadCGM3();
420560
}
421561
if(!uploaded) {
422-
waitmin=15;
562+
waitmin=lastNightUploadConfigError?0:15;
423563
lastNightUploadWaitMinutes = waitmin;
424564
continue;
425565
}
@@ -437,7 +577,7 @@ static void uploaderthread() {
437577
treatmentsOk = uploadtreatments(true);
438578
}
439579
if(!treatmentsOk) {
440-
waitmin=15;
580+
waitmin=lastNightUploadConfigError?0:15;
441581
lastNightUploadWaitMinutes = waitmin;
442582
continue;
443583
}
@@ -450,7 +590,10 @@ static void uploaderthread() {
450590
void startuploaderthread();
451591
void wakeuploader() {
452592
if(!uploaderrunning && settings->data()->nightuploadon) {
453-
startuploaderthread();
593+
auto env=getenv();
594+
if(env && inituploader(env)) {
595+
lastNightUploadConfigError = false;
596+
}
454597
}
455598
if(uploaderrunning) {
456599
lastNightUploadWaitMinutes = 0;

Common/src/main/cpp/settings/javasettings.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "settings.hpp"
2222
#include <algorithm>
2323
#include <cinttypes>
24+
#include <cctype>
2425
#include <cmath>
2526
#include <cstring>
2627
#include <jni.h>
@@ -1617,22 +1618,63 @@ extern "C" JNIEXPORT jstring JNICALL fromjava(getnightuploadsecret)(JNIEnv *env,
16171618
return myNewStringUTF(env, settings->data()->nightuploadsecret);
16181619
}
16191620
extern void enduploaderthread();
1621+
static int normalizeNightscoutBaseUrl(char *dest, int destsize, const char *src,
1622+
int srclen) {
1623+
if (!dest || destsize <= 0) {
1624+
return 0;
1625+
}
1626+
if (!src || srclen <= 0) {
1627+
dest[0] = '\0';
1628+
return 0;
1629+
}
1630+
const char *start = src;
1631+
const char *end = src + srclen;
1632+
while (start < end && std::isspace(static_cast<unsigned char>(*start))) {
1633+
++start;
1634+
}
1635+
while (end > start && std::isspace(static_cast<unsigned char>(end[-1]))) {
1636+
--end;
1637+
}
1638+
std::string normalized(start, end - start);
1639+
if (normalized.empty()) {
1640+
dest[0] = '\0';
1641+
return 0;
1642+
}
1643+
if (normalized.rfind("http://", 0) != 0 &&
1644+
normalized.rfind("https://", 0) != 0) {
1645+
normalized.insert(0, "https://");
1646+
}
1647+
while (!normalized.empty() && normalized.back() == '/') {
1648+
normalized.pop_back();
1649+
}
1650+
const auto apiPos = normalized.find("/api/");
1651+
if (apiPos != std::string::npos) {
1652+
normalized.resize(apiPos);
1653+
}
1654+
const int len = std::min<int>(normalized.size(), destsize - 1);
1655+
memcpy(dest, normalized.data(), len);
1656+
dest[len] = '\0';
1657+
return len;
1658+
}
1659+
16201660
extern "C" JNIEXPORT void JNICALL
16211661
fromjava(setNightUploader)(JNIEnv *env, jclass cl, jstring jurl,
16221662
jstring jsecret, jboolean active, jboolean v3) {
16231663
settings->data()->nightscoutV3 = v3;
16241664
if (jurl != nullptr) {
16251665
int maxurllen = sizeof(settings->data()->nightuploadname) - 1;
16261666
char *name = settings->data()->nightuploadname;
1667+
char rawname[sizeof(settings->data()->nightuploadname)]{};
16271668
jint jlen = env->GetStringLength(jurl);
16281669
if (jlen >= maxurllen)
16291670
jlen = maxurllen;
1630-
env->GetStringUTFRegion(jurl, 0, jlen, name);
1671+
env->GetStringUTFRegion(jurl, 0, jlen, rawname);
16311672
int len = env->GetStringUTFLength(jurl);
16321673
if (len >= maxurllen)
16331674
len = maxurllen;
1634-
name[len] = '\0';
1635-
settings->data()->nightuploadnamelen = len;
1675+
rawname[len] = '\0';
1676+
settings->data()->nightuploadnamelen =
1677+
normalizeNightscoutBaseUrl(name, maxurllen + 1, rawname, len);
16361678
}
16371679
if (jsecret != nullptr) {
16381680
int maxsecretlen = sizeof(settings->data()->nightuploadsecret) - 1;

Common/src/main/java/tk/glucodata/NightPost.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575

7676
public class NightPost {
7777
private static final String LOG_ID="NightPost";
78+
private static final int ERROR_INVALID_URL = -2;
7879

7980
private static void patch() {
8081
try {
@@ -235,6 +236,13 @@ static public int upload(String httpurl,byte[] postdata,String secret,boolean pu
235236
{if(doLog) {Log.i(LOG_ID,"upload("+httpurl+",#"+postdata.length+","+ secret+","+(put?"PUT":"POST")+")");};};
236237
try {
237238

239+
if(httpurl==null || !(httpurl.startsWith("https://") || httpurl.startsWith("http://"))) {
240+
final String invalid="upload failure:\nInvalid Nightscout URL: "+httpurl;
241+
uploadstatus=invalid;
242+
Log.e(LOG_ID,invalid);
243+
return ERROR_INVALID_URL;
244+
}
245+
238246
uploadstatus="start upload "+httpurl;
239247
URL url = new URL(httpurl);
240248
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

0 commit comments

Comments
 (0)