diff options
author | Tanishka Suwalka <tanishkas@ruc.dk> | 2025-04-16 20:17:34 +0200 |
---|---|---|
committer | Jonas Smedegaard <dr@jones.dk> | 2025-04-16 20:45:07 +0200 |
commit | 8354610bcdc3267b8628394c3c2d9b368a659b25 (patch) | |
tree | 85135c8c3c8d050ec21426dcd362c0c53ff04f6a /vote | |
parent | 8a34f5c4359e074bc895dc5ed69c394748b44757 (diff) |
store, align, qualify and conclude
Diffstat (limited to 'vote')
-rw-r--r-- | vote/vote.ino | 182 |
1 files changed, 133 insertions, 49 deletions
diff --git a/vote/vote.ino b/vote/vote.ino index 8036408..13b425a 100644 --- a/vote/vote.ino +++ b/vote/vote.ino @@ -32,25 +32,141 @@ #define SCAN_WINDOW 99 #define SCAN_TIME_SEC 1 +// stack sizes for voters and ballots-per-voter +#define VOTER_MAX 10 +#define BALLOT_MAX 5 + // Validity timing thresholds const unsigned long VOTE_TIME_AHEAD = 1 * 60 * 1000; // 1 minute const unsigned long VOTE_TIME_BEHIND = 2 * 60 * 1000; // 2 minutes -// Limited size NOW!! can be transformed into infinite array -#define STACK_SIZE 1000 +// Classify gape state +enum MusselGapState { + Closed, + Open +}; +// Data structures struct Vote { - String id; unsigned long timestamp; int measure; }; -// Index of the top element in the stack, -1 means stack is empty -int top = -1; +struct Voter { + String id; // Mussel ID + Vote votes[BALLOT_MAX]; // Last 5 sensor readings + int voteCount = 0; // Number of readings stored +}; + +// Global array of mussel voters +Voter voters[VOTER_MAX]; +int voterCount = 0; // pointer to control Bluetooth networking BLEScan *pBLEScan; +/// Find index of mussel ID in the voters array +int findVoterIndex(const String& id) { + for (int i = 0; i < voterCount; i++) { + if (voters[i].id == id) return i; + } + return -1; // Not found +} + +/// Add or update vote for a mussel ID +void storeVoteForMussel(const String& id, unsigned long timestamp, int gape_measure) { + int index = findVoterIndex(id); + + // If mussel not found, add new + if (index == -1) { + if (voterCount >= VOTER_MAX) { + Serial.printf("Ignored: Max mussel limit reached (%s)\n", id.c_str()); + return; + } + voters[voterCount].id = id; + voters[voterCount].voteCount = 0; + index = voterCount++; + } + + Voter &voter = voters[index]; + + // Maintain a fixed number of stored votes (FIFO logic) + if (voter.voteCount >= BALLOT_MAX) { + for (int i = 1; i < BALLOT_MAX; i++) { + voter.votes[i - 1] = voter.votes[i]; + } + voter.voteCount = BALLOT_MAX - 1; + } + + // Store the new vote at the end + voter.votes[voter.voteCount++] = {timestamp, gape_measure}; + Serial.printf("Vote stored: Time: %lu, Mussel: %s, Gape: %d\n", timestamp, id.c_str(), gape_measure); +} + +/// Classify mussel state based on topmost vote +void alignVotes() { + Serial.println("---- Mussel Alignment Status ----"); + for (int i = 0; i < voterCount; i++) { + Voter &voter = voters[i]; + + // Skip mussels with no data + if (voter.voteCount == 0) { + Serial.printf("Mussel ID: %s - No data\n", voter.id.c_str()); + continue; + } + + // Use latest vote to determine state + Vote latest = voter.votes[voter.voteCount - 1]; + String state = (latest.measure >= 0 && latest.measure < 40) ? "Closed" : + (latest.measure >= 40 && latest.measure <= 90) ? "Open" : + "Invalid reading"; + + Serial.printf("Mussel ID: %s | Latest Gape: %d | State: %s\n", + voter.id.c_str(), latest.measure, state.c_str()); + } + Serial.println("---------------------------------"); +} + +/// Decide whether a vote is valid based on gape and age +const char* qualifyMusselVote(int gape, unsigned long voteTimestamp, unsigned long now) { + + // Determine state based on gape + MusselGapState gapState = gape >= 40 && gape <= 90 ? Open : Closed; + const char* gapStateStr = (gapState == Open) ? "Open" : "Closed"; + unsigned long age = now - voteTimestamp; + + // Log the state + Serial.printf("Qualifying vote | Time since vote: %lu ms | Gape: %d (%s)\n", age, gape, gapStateStr); + + // Invalid if mussel is closed + if (gapState == Closed) { + Serial.println("→ INVALID: Mussel is Closed."); + return "invalid"; + } + + // Invalid if vote is too old + if (age > VOTE_TIME_BEHIND) { + Serial.println("→ INVALID: Vote is too old (>2 minutes)."); + return "invalid"; + } + + // Valid if within 1 minute and mussel is open + if (age <= VOTE_TIME_AHEAD) { + Serial.println("→ VALID: Mussel is Open and vote is recent."); + return "valid"; + } + + // Catch-all for anything in between + Serial.println("→ INVALID: Vote is in uncertain window time"); + return "invalid"; +} + +/// Output the final vote decision for a mussel +void concludeMusselVote(const String& musselId, const char* validity) { + const char* result = strcmp(validity, "valid") == 0 ? "YES" : "NO"; + Serial.printf("Final Vote from Mussel %s → %s (Vote was %s)\n", musselId.c_str(), result, validity); +} + // Bluetooth beacon discovery callbacks class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { @@ -69,56 +185,24 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { Serial.println(id_mangled + ":" + EddystoneTLM.getTemp()); #endif - push( - advertisedDevice.getName(), - millis(), - EddystoneTLM.getTemp() - ); - } - } -}; - -/// Function to push data onto the stack -bool push(String id, unsigned long timestamp, int measure) { + unsigned long now = millis(); + String musselID = advertisedDevice.getName(); + int gape = EddystoneTLM.getTemp(); // Referring to gape_measure - // Check if stack is full - if (top >= STACK_SIZE - 1) { - Serial.println("Stack Full"); + // 1. Store vote + storeVoteForMussel(musselID, now, gape); - // Return false if stack is full - return false; - } - top++; - idStack[top] = id; - timeStack[top] = timestamp; - measureStack[top] = measure; - - // Return true on successful push - return true; -} + // 2. Align + alignVotes(); -bool qualifyVote(Vote vote, unsigned long currentTime) { + // 3. Qualify + const char* validity = qualifyMusselVote(gape, now, millis()); - // If the measure is 42 (YES), check timestamp validity - if (vote.measure == 42) { - // If the vote's timestamp is within 1 minute, count it as YES - if (currentTime - vote.timestamp <= VOTE_TIME_AHEAD) { - return true; + // 4. Conclude + concludeMusselVote(musselID, validity); } - // If the vote's timestamp is older than 2 minutes, count it as NO - else if (currentTime - vote.timestamp > VOTE_TIME_BEHIND) { - return false; - } - } - - // If the measure is 2, always count the vote as NO - if (vote.measure == 2) { - return false; } - - // Default case: vote is invalid if no conditions are met - return false; -} +}; void setup() { // enable logging to serial |