mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
Optimize Event Ordering by Storing Milliseconds and Handling Rollovers
This commit is contained in:
parent
6c7d7c5b78
commit
086c3dfa29
6 changed files with 83 additions and 74 deletions
|
@ -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
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define __MQTT_H__
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <vector>
|
||||
#include "../../include.h"
|
||||
|
||||
#define MQTT_MSG_BUFFER_SIZE (1024)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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>");
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue