Skip to content

Commit 8eb62de

Browse files
committed
Improve daily report payload handling and SMS percent logic
Client: Refactored daily report generation to split payloads by size limit, ensuring all eligible tanks are reported without exceeding Notecard limits. Added appendDailyTank helper for payload management. Server: Improved percent calculation in alarm handler to fallback on height if percent is missing from payload.
1 parent 840e03c commit 8eb62de

File tree

2 files changed

+92
-23
lines changed

2 files changed

+92
-23
lines changed

TankAlarm-112025-Client-BluesOpta/TankAlarm-112025-Client-BluesOpta.ino

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ static bool gNotecardAvailable = true;
202202
#define NOTECARD_FAILURE_THRESHOLD 5
203203
#define NOTECARD_RETRY_INTERVAL 60000UL // Retry after 60 seconds
204204

205+
static const size_t DAILY_NOTE_PAYLOAD_LIMIT = 960U;
206+
205207
// Forward declarations
206208
static void initializeStorage();
207209
static void ensureConfigLoaded();
@@ -225,6 +227,8 @@ static void sendDailyReport();
225227
static void publishNote(const char *fileName, const JsonDocument &doc, bool syncNow);
226228
static void ensureTimeSync();
227229
static void updateDailyScheduleIfNeeded();
230+
static bool checkNotecardHealth();
231+
static bool appendDailyTank(DynamicJsonDocument &doc, JsonArray &array, uint8_t tankIndex, size_t payloadLimit);
228232

229233
void setup() {
230234
Serial.begin(115200);
@@ -1247,36 +1251,96 @@ static void sendAlarm(uint8_t idx, const char *alarmType, float inches) {
12471251
}
12481252

12491253
static void sendDailyReport() {
1250-
DynamicJsonDocument doc(2048);
1251-
JsonArray tanks = doc.createNestedArray("tanks");
1252-
1254+
uint8_t eligibleIndices[MAX_TANKS];
1255+
uint8_t eligibleCount = 0;
12531256
for (uint8_t i = 0; i < gConfig.tankCount; ++i) {
1254-
const TankConfig &cfg = gConfig.tanks[i];
1255-
TankRuntime &state = gTankState[i];
1256-
if (!cfg.enableDailyReport) {
1257-
continue;
1257+
if (gConfig.tanks[i].enableDailyReport) {
1258+
eligibleIndices[eligibleCount++] = i;
12581259
}
1259-
JsonObject t = tanks.createNestedObject();
1260-
t["label"] = cfg.name;
1261-
t["tank"] = cfg.tankNumber;
1262-
t["levelInches"] = state.currentInches;
1263-
t["percent"] = (cfg.heightInches > 0.1f) ? (state.currentInches / cfg.heightInches * 100.0f) : 0.0f;
1264-
t["high"] = cfg.highAlarmInches;
1265-
t["low"] = cfg.lowAlarmInches;
1266-
state.lastDailySentInches = state.currentInches;
12671260
}
12681261

1269-
if (tanks.size() == 0) {
1262+
if (eligibleCount == 0) {
12701263
return;
12711264
}
12721265

1273-
doc["client"] = gDeviceUID;
1274-
doc["site"] = gConfig.siteName;
1275-
doc["email"] = gConfig.dailyEmail;
1276-
doc["time"] = currentEpoch();
1266+
double reportEpoch = currentEpoch();
1267+
size_t tankCursor = 0;
1268+
uint8_t part = 0;
1269+
bool queuedAny = false;
1270+
1271+
while (tankCursor < eligibleCount) {
1272+
DynamicJsonDocument doc(1024);
1273+
doc["client"] = gDeviceUID;
1274+
doc["site"] = gConfig.siteName;
1275+
doc["email"] = gConfig.dailyEmail;
1276+
doc["time"] = reportEpoch;
1277+
doc["part"] = static_cast<uint8_t>(part + 1);
1278+
1279+
JsonArray tanks = doc.createNestedArray("tanks");
1280+
bool addedTank = false;
1281+
1282+
while (tankCursor < eligibleCount) {
1283+
uint8_t tankIndex = eligibleIndices[tankCursor];
1284+
if (appendDailyTank(doc, tanks, tankIndex, DAILY_NOTE_PAYLOAD_LIMIT)) {
1285+
++tankCursor;
1286+
addedTank = true;
1287+
} else {
1288+
if (!addedTank) {
1289+
// Allow a single large entry with minimal headroom so it still publishes.
1290+
if (appendDailyTank(doc, tanks, tankIndex, DAILY_NOTE_PAYLOAD_LIMIT + 48U)) {
1291+
++tankCursor;
1292+
addedTank = true;
1293+
} else {
1294+
Serial.println(F("Daily report entry skipped; payload still exceeds limit"));
1295+
++tankCursor;
1296+
}
1297+
}
1298+
break;
1299+
}
1300+
}
1301+
1302+
if (!addedTank) {
1303+
continue;
1304+
}
12771305

1278-
publishNote(DAILY_FILE, doc, true);
1279-
Serial.println(F("Daily report queued"));
1306+
doc["more"] = (tankCursor < eligibleCount);
1307+
bool syncNow = (tankCursor >= eligibleCount);
1308+
publishNote(DAILY_FILE, doc, syncNow);
1309+
queuedAny = true;
1310+
++part;
1311+
}
1312+
1313+
if (queuedAny) {
1314+
Serial.println(F("Daily report queued"));
1315+
}
1316+
}
1317+
1318+
static bool appendDailyTank(DynamicJsonDocument &doc, JsonArray &array, uint8_t tankIndex, size_t payloadLimit) {
1319+
if (tankIndex >= gConfig.tankCount) {
1320+
return false;
1321+
}
1322+
1323+
const TankConfig &cfg = gConfig.tanks[tankIndex];
1324+
TankRuntime &state = gTankState[tankIndex];
1325+
1326+
JsonObject t = array.createNestedObject();
1327+
t["label"] = cfg.name;
1328+
t["tank"] = cfg.tankNumber;
1329+
t["levelInches"] = state.currentInches;
1330+
t["percent"] = (cfg.heightInches > 0.1f) ? (state.currentInches / cfg.heightInches * 100.0f) : 0.0f;
1331+
t["high"] = cfg.highAlarmInches;
1332+
t["low"] = cfg.lowAlarmInches;
1333+
1334+
if (measureJson(doc) > payloadLimit) {
1335+
size_t currentSize = array.size();
1336+
if (currentSize > 0) {
1337+
array.remove(currentSize - 1);
1338+
}
1339+
return false;
1340+
}
1341+
1342+
state.lastDailySentInches = state.currentInches;
1343+
return true;
12801344
}
12811345

12821346
static void publishNote(const char *fileName, const JsonDocument &doc, bool syncNow) {

TankAlarm-112025-Server-BluesOpta/TankAlarm-112025-Server-BluesOpta.ino

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ static void loadClientConfigSnapshots();
818818
static void saveClientConfigSnapshots();
819819
static void cacheClientConfigFromBuffer(const char *clientUid, const char *buffer);
820820
static ClientConfigSnapshot *findClientConfigSnapshot(const char *clientUid);
821+
static bool checkSmsRateLimit(TankRecord *rec);
821822

822823
void setup() {
823824
Serial.begin(115200);
@@ -1549,10 +1550,14 @@ static void handleAlarm(JsonDocument &doc, double epoch) {
15491550
strlcpy(rec->alarmType, type, sizeof(rec->alarmType));
15501551
}
15511552
rec->levelInches = inches;
1552-
rec->percent = doc["percent"].as<float>();
15531553
if (doc.containsKey("heightInches")) {
15541554
rec->heightInches = doc["heightInches"].as<float>();
15551555
}
1556+
if (doc.containsKey("percent")) {
1557+
rec->percent = doc["percent"].as<float>();
1558+
} else if (rec->heightInches > 0.1f) {
1559+
rec->percent = (inches / rec->heightInches) * 100.0f;
1560+
}
15561561
rec->lastUpdateEpoch = (epoch > 0.0) ? epoch : currentEpoch();
15571562

15581563
// Check rate limit before sending SMS

0 commit comments

Comments
 (0)