//+------------------------------------------------------------------+ //| EventJournal.mqh | //| Copyright 2026, MetaQuotes Ltd. | //| www.mql5.com | //+------------------------------------------------------------------+ #ifndef REQUEST_LATENCY_LAB_EVENT_JOURNAL_MQH #define REQUEST_LATENCY_LAB_EVENT_JOURNAL_MQH #include "..\..\Include\RequestLatencyLab\Models.mqh" class CEventJournal { private: LabEvent m_events[]; int m_capacity; int m_count; ulong m_next_sequence; // event_sequence - порядок регистрации ulong m_records_lost; // записи, не поместившиеся в буфер ulong m_high_watermark; // максимум занятости буфера string m_session_id; public: CEventJournal(void); bool Init(const int capacity, const string session_id); void Clear(void) { m_count = 0; } int Count() const { return(m_count); } int Capacity() const { return(m_capacity); } ulong NextSequence() const { return(m_next_sequence); } ulong LostCount() const { return(m_records_lost); } ulong HighWatermark() const { return(m_high_watermark); } //--- регистрация события; возвращает event_sequence bool Add(const LabEvent &event, ulong &event_sequence); //--- удобная регистрация типизированной записи bool AddTyped(const ENUM_LAB_RECORD_KIND kind, const ulong local_time_us, const ulong origin_sequence, const string payload_json, const string quality_code); //--- фиксация времени выхода обработчика по event_sequence (п.11 аудита) bool SetExitTime(const ulong event_sequence, const ulong exit_us); bool GetByIndex(const int i, LabEvent &out) const; //--- освобождение места для последующих событий после контрольного //--- сохранения (без молчаливой потери: потерянные уже учтены) void DropPersisted(const int persisted_count, ulong &dropped); }; //+------------------------------------------------------------------+ //| Внешние определения методов. | //+------------------------------------------------------------------+ CEventJournal::CEventJournal(void) { m_capacity = 0; m_count = 0; m_next_sequence = 1; m_records_lost = 0; m_high_watermark = 0; m_session_id = ""; } bool CEventJournal::Init(const int capacity, const string session_id) { m_capacity = capacity; m_session_id = session_id; m_count = 0; m_next_sequence = 1; m_records_lost = 0; m_high_watermark = 0; if(ArrayResize(m_events, m_capacity) != m_capacity) return(false); return(true); } bool CEventJournal::Add(const LabEvent &event, ulong &event_sequence) { if(m_capacity <= 0 || m_count >= m_capacity) { m_records_lost++; return(false); } m_count++; m_events[m_count - 1] = event; m_events[m_count - 1].session_id = m_session_id; m_events[m_count - 1].event_sequence = m_next_sequence; event_sequence = m_next_sequence; m_next_sequence++; if((ulong)m_count > m_high_watermark) m_high_watermark = (ulong)m_count; return(true); } bool CEventJournal::AddTyped(const ENUM_LAB_RECORD_KIND kind, const ulong local_time_us, const ulong origin_sequence, const string payload_json, const string quality_code) { LabEvent e; e.Zero(); e.kind = kind; e.local_time_us = local_time_us; e.origin_sequence = origin_sequence; e.payload_json = payload_json; e.quality_code = quality_code; ulong seq = 0; return(Add(e, seq)); } bool CEventJournal::SetExitTime(const ulong event_sequence, const ulong exit_us) { for(int i = 0; i < m_count; i++) if(m_events[i].event_sequence == event_sequence) { m_events[i].handler_exit_us = exit_us; return(true); } return(false); } bool CEventJournal::GetByIndex(const int i, LabEvent &out) const { if(i < 0 || i >= m_count) return(false); out = m_events[i]; return(true); } void CEventJournal::DropPersisted(const int persisted_count, ulong &dropped) { dropped = 0; if(persisted_count <= 0) return; if(persisted_count >= m_count) { dropped = m_count; m_count = 0; return; } dropped = persisted_count; for(int i = persisted_count; i < m_count; i++) m_events[i - persisted_count] = m_events[i]; m_count -= persisted_count; } #endif // REQUEST_LATENCY_LAB_EVENT_JOURNAL_MQH //+------------------------------------------------------------------+