Optimize Event Ordering by Storing Milliseconds and Handling Rollovers

This commit is contained in:
amarofarinha 2024-09-20 02:03:49 +01:00
parent 6c7d7c5b78
commit 086c3dfa29
6 changed files with 83 additions and 74 deletions

View file

@ -60,6 +60,8 @@ SensorConfig sensorConfigs[] = {
};
static std::vector<EventData> order_events;
static String generateCommonInfoAutoConfigTopic(const char* object_id, const char* hostname) {
return String("homeassistant/sensor/battery-emulator_") + String(hostname) + "/" + String(object_id) + "/config";
}
@ -233,7 +235,7 @@ void publish_events() {
doc["unique_id"] = "battery-emulator_" + String(hostname) + "_event";
doc["object_id"] = String(hostname) + "_event";
doc["value_template"] =
"{{ value_json.event_type ~ ' (c:' ~ value_json.count ~ ',m:' ~ value_json.milis ~ ') ' ~ value_json.message "
"{{ value_json.event_type ~ ' (c:' ~ value_json.count ~ ',m:' ~ value_json.millis ~ ') ' ~ value_json.message "
"}}";
doc["json_attributes_topic"] = state_topic;
doc["json_attributes_template"] = "{{ value_json | tojson }}";
@ -253,36 +255,43 @@ void publish_events() {
#endif // HA_AUTODISCOVERY
const EVENTS_STRUCT_TYPE* event_pointer;
unsigned long timestamp_now = get_current_event_time_secs();
//clear the vector
order_events.clear();
// Collect all events
for (int i = 0; i < EVENT_NOF_EVENTS; i++) {
event_pointer = get_event_pointer((EVENTS_ENUM_TYPE)i);
EVENTS_ENUM_TYPE event_handle = static_cast<EVENTS_ENUM_TYPE>(i);
if (event_pointer->occurences > 0 && !event_pointer->MQTTpublished) {
doc["event_type"] = String(get_event_enum_string(event_handle));
doc["severity"] = String(get_event_level_string(event_handle));
time_t time_difference = timestamp_now - event_pointer->timestamp;
doc["last_event"] = String(timestamp_now - event_pointer->timestamp);
doc["count"] = String(event_pointer->occurences);
doc["data"] = String(event_pointer->data);
doc["message"] = String(get_event_message_string(event_handle));
doc["milis"] = String(millis());
serializeJson(doc, mqtt_msg);
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
#ifdef DEBUG_VIA_USB
Serial.println("Common info MQTT msg could not be sent");
#endif // DEBUG_VIA_USB
} else {
set_event_MQTTpublished(event_handle);
}
doc.clear();
order_events.push_back({static_cast<EVENTS_ENUM_TYPE>(i), event_pointer});
}
}
// Sort events by timestamp
std::sort(order_events.begin(), order_events.end(), compareEventsByTimestamp);
for (const auto& event : order_events) {
EVENTS_ENUM_TYPE event_handle = event.event_handle;
event_pointer = event.event_pointer;
doc["event_type"] = String(get_event_enum_string(event_handle));
doc["severity"] = String(get_event_level_string(event_handle));
doc["count"] = String(event_pointer->occurences);
doc["data"] = String(event_pointer->data);
doc["message"] = String(get_event_message_string(event_handle));
doc["millis"] = String(event_pointer->timestamp);
serializeJson(doc, mqtt_msg);
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
#ifdef DEBUG_VIA_USB
Serial.println("Common info MQTT msg could not be sent");
#endif // DEBUG_VIA_USB
} else {
set_event_MQTTpublished(event_handle);
}
doc.clear();
//clear the vector
order_events.clear();
}
#ifdef HA_AUTODISCOVERY
}
#endif // HA_AUTODISCOVERY

View file

@ -35,6 +35,7 @@
#define __MQTT_H__
#include <Arduino.h>
#include <vector>
#include "../../include.h"
#define MQTT_MSG_BUFFER_SIZE (1024)

View file

@ -44,16 +44,14 @@
typedef struct {
EVENTS_ENUM_TYPE event;
uint8_t millisrolloverCount;
uint32_t timestamp;
uint8_t data;
} EVENT_LOG_ENTRY_TYPE;
typedef struct {
EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
unsigned long time_seconds;
MyTimer second_timer;
MyTimer ee_timer;
MyTimer update_timer;
EVENTS_LEVEL_TYPE level;
uint16_t event_log_head_index;
uint16_t event_log_tail_index;
@ -66,21 +64,29 @@ static EVENT_TYPE events;
static const char* EVENTS_ENUM_TYPE_STRING[] = {EVENTS_ENUM_TYPE(GENERATE_STRING)};
static const char* EVENTS_LEVEL_TYPE_STRING[] = {EVENTS_LEVEL_TYPE(GENERATE_STRING)};
static uint32_t lastMillis = millis();
/* Local function prototypes */
static void update_event_time(void);
static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched);
static void update_event_level(void);
static void update_bms_status(void);
static void log_event(EVENTS_ENUM_TYPE event, uint8_t data);
static void log_event(EVENTS_ENUM_TYPE event, uint8_t millisrolloverCount, uint32_t timestamp, uint8_t data);
static void print_event_log(void);
static void check_ee_write(void);
uint8_t millisrolloverCount = 0;
/* Exported functions */
/* Main execution function, should handle various continuous functionality */
void run_event_handling(void) {
update_event_time();
uint32_t currentMillis = millis();
if (currentMillis < lastMillis) { // Overflow detected
millisrolloverCount++;
}
lastMillis = currentMillis;
run_sequence_on_target();
//check_ee_write();
update_event_level();
@ -100,7 +106,7 @@ void init_events(void) {
EEPROM.writeUShort(EE_EVENT_LOG_TAIL_INDEX_ADDRESS, 0);
// Prepare an empty event block to write
EVENT_LOG_ENTRY_TYPE entry = {.event = EVENT_NOF_EVENTS, .timestamp = 0, .data = 0};
EVENT_LOG_ENTRY_TYPE entry = {.event = EVENT_NOF_EVENTS, .millisrolloverCount = 0, .timestamp = 0, .data = 0};
// Put the event in (what I guess is) the RAM EEPROM mirror, or write buffer
@ -128,6 +134,7 @@ void init_events(void) {
for (uint16_t i = 0; i < EVENT_NOF_EVENTS; i++) {
events.entries[i].data = 0;
events.entries[i].timestamp = 0;
events.entries[i].millisrolloverCount = 0;
events.entries[i].occurences = 0;
events.entries[i].log = true;
events.entries[i].MQTTpublished = false; // Not published by default
@ -206,10 +213,8 @@ void init_events(void) {
events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger...
events.second_timer.set_interval(600);
// Write to EEPROM every X minutes (if an event has been set)
events.ee_timer.set_interval(EE_WRITE_PERIOD_MINUTES * 60 * 1000);
events.update_timer.set_interval(2000);
}
void set_event(EVENTS_ENUM_TYPE event, uint8_t data) {
@ -419,12 +424,13 @@ static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) {
events.entries[event].occurences++;
events.entries[event].MQTTpublished = false;
if (events.entries[event].log) {
log_event(event, data);
log_event(event, events.entries[event].millisrolloverCount, events.entries[event].timestamp, data);
}
}
// We should set the event, update event info
events.entries[event].timestamp = events.time_seconds;
events.entries[event].timestamp = millis();
events.entries[event].millisrolloverCount = millisrolloverCount;
events.entries[event].data = data;
// Check if the event is latching
events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE;
@ -457,6 +463,14 @@ static void update_bms_status(void) {
}
}
// Function to compare events by timestamp
bool compareEventsByTimestamp(const EventData& a, const EventData& b) {
if (a.event_pointer->millisrolloverCount != b.event_pointer->millisrolloverCount) {
return a.event_pointer->millisrolloverCount > b.event_pointer->millisrolloverCount;
}
return a.event_pointer->timestamp > b.event_pointer->timestamp;
}
static void update_event_level(void) {
EVENTS_LEVEL_TYPE temporary_level = EVENT_LEVEL_INFO;
for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) {
@ -467,22 +481,7 @@ static void update_event_level(void) {
events.level = temporary_level;
}
static void update_event_time(void) {
// This should run roughly 2 times per second
if (events.second_timer.elapsed() == true) {
uptime::calculateUptime(); // millis() overflows every 50 days, so update occasionally to adjust
events.time_seconds = uptime::getDays() * DAYS_TO_SECS;
events.time_seconds += uptime::getHours() * HOURS_TO_SECS;
events.time_seconds += uptime::getMinutes() * MINUTES_TO_SECS;
events.time_seconds += uptime::getSeconds();
}
}
unsigned long get_current_event_time_secs(void) {
return events.time_seconds;
}
static void log_event(EVENTS_ENUM_TYPE event, uint8_t data) {
static void log_event(EVENTS_ENUM_TYPE event, uint8_t millisrolloverCount, uint32_t timestamp, uint8_t data) {
// Update head with wrap to 0
if (++events.event_log_head_index == EE_NOF_EVENT_ENTRIES) {
events.event_log_head_index = 0;
@ -500,7 +499,8 @@ static void log_event(EVENTS_ENUM_TYPE event, uint8_t data) {
int entry_address = EE_EVENT_ENTRY_START_ADDRESS + EE_EVENT_ENTRY_SIZE * events.event_log_head_index;
// Prepare an event block to write
EVENT_LOG_ENTRY_TYPE entry = {.event = event, .timestamp = events.time_seconds, .data = data};
EVENT_LOG_ENTRY_TYPE entry = {
.event = event, .millisrolloverCount = millisrolloverCount, .timestamp = timestamp, .data = data};
// Put the event in (what I guess is) the RAM EEPROM mirror, or write buffer
EEPROM.put(entry_address, entry);

View file

@ -120,20 +120,28 @@ typedef enum {
} EVENTS_STATE_TYPE;
typedef struct {
uint32_t timestamp; // Time in seconds since startup when the event occurred
uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage
uint8_t occurences; // Number of occurrences since startup
EVENTS_LEVEL_TYPE level; // Event level, i.e. ERROR/WARNING...
EVENTS_STATE_TYPE state; // Event state, i.e. ACTIVE/INACTIVE...
uint32_t timestamp; // Time in seconds since startup when the event occurred
uint8_t millisrolloverCount; // number of times millis rollovers before timestamp
uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage
uint8_t occurences; // Number of occurrences since startup
EVENTS_LEVEL_TYPE level; // Event level, i.e. ERROR/WARNING...
EVENTS_STATE_TYPE state; // Event state, i.e. ACTIVE/INACTIVE...
bool log;
bool MQTTpublished;
} EVENTS_STRUCT_TYPE;
// Define a struct to hold event data
struct EventData {
EVENTS_ENUM_TYPE event_handle;
const EVENTS_STRUCT_TYPE* event_pointer;
};
extern uint8_t millisrolloverCount; // number of times millis rollovers
const char* get_event_enum_string(EVENTS_ENUM_TYPE event);
const char* get_event_message_string(EVENTS_ENUM_TYPE event);
const char* get_event_level_string(EVENTS_ENUM_TYPE event);
const char* get_event_type(EVENTS_ENUM_TYPE event);
unsigned long get_current_event_time_secs(void);
EVENTS_LEVEL_TYPE get_event_level(void);
@ -149,4 +157,6 @@ void run_event_handling(void);
void run_sequence_on_target(void);
bool compareEventsByTimestamp(const EventData& a, const EventData& b);
#endif // __MYTIMER_H__

View file

@ -7,16 +7,11 @@ const char EVENTS_HTML_END[] = R"=====(
</div></div>
<button onclick='home()'>Back to main page</button>
<style>.event:nth-child(even){background-color:#455a64}.event:nth-child(odd){background-color:#394b52}</style>
<script>function showEvent(){document.querySelectorAll(".event").forEach(function(e){var n=e.querySelector(".sec-ago");n&&(n.innerText=new Date(new Date().getTime()-1e3*parseInt(n.innerText,10)).toLocaleString())})}function home(){window.location.href="/"}window.onload=function(){showEvent()}</script>
<script>function showEvent(){document.querySelectorAll(".event").forEach(function(e){var n=e.querySelector(".sec-ago");n&&(n.innerText=new Date(Date.now()-((+n.innerText.split(';')[0])*4294967296+ +n.innerText.split(';')[1])).toLocaleString());})}function home(){window.location.href="/"}window.onload=function(){showEvent()}</script>
)=====";
static std::vector<EventData> order_events;
// Function to compare events by timestamp
static bool compareEventsByTimestamp(const EventData& a, const EventData& b) {
return a.event_pointer->timestamp > b.event_pointer->timestamp;
}
String events_processor(const String& var) {
if (var == "X") {
String content = "";
@ -25,8 +20,6 @@ String events_processor(const String& var) {
content.concat(FPSTR(EVENTS_HTML_START));
const EVENTS_STRUCT_TYPE* event_pointer;
unsigned long timestamp_now = get_current_event_time_secs();
//clear the vector
order_events.clear();
// Collect all events
@ -36,9 +29,9 @@ String events_processor(const String& var) {
order_events.push_back({static_cast<EVENTS_ENUM_TYPE>(i), event_pointer});
}
}
// Sort events by timestamp
std::sort(order_events.begin(), order_events.end(), compareEventsByTimestamp);
unsigned long timestamp_now = millis();
// Generate HTML and debug output
for (const auto& event : order_events) {
@ -53,7 +46,8 @@ String events_processor(const String& var) {
content.concat("<div class='event'>");
content.concat("<div>" + String(get_event_enum_string(event_handle)) + "</div>");
content.concat("<div>" + String(get_event_level_string(event_handle)) + "</div>");
content.concat("<div class='sec-ago'>" + String(timestamp_now - event_pointer->timestamp) + "</div>");
content.concat("<div class='sec-ago'>" + String(millisrolloverCount) + ";" +
String(timestamp_now - event_pointer->timestamp) + "</div>");
content.concat("<div>" + String(event_pointer->occurences) + "</div>");
content.concat("<div>" + String(event_pointer->data) + "</div>");
content.concat("<div>" + String(get_event_message_string(event_handle)) + "</div>");

View file

@ -14,10 +14,5 @@
* @return String
*/
String events_processor(const String& var);
// Define a struct to hold event data
struct EventData {
EVENTS_ENUM_TYPE event_handle;
const EVENTS_STRUCT_TYPE* event_pointer;
};
#endif