aboutsummaryrefslogtreecommitdiff
path: root/examples/smartresponse_public/FlashStore.h
blob: b23abdbf92536742d613064858a5383aaf84f2d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#pragma once

#include <Arduino.h>

#include "PublicMessageRing.h"

class FlashStore {
public:
  FlashStore();

  void begin();
  uint16_t getMessageCount() const { return _count; }
  uint32_t getSentCount() const { return _sent_count; }
  uint32_t getRecvCount() const { return _recv_count; }
  uint32_t getLatestTimestamp() const { return _latest_timestamp; }
  uint32_t getTotalLineCount() const { return _total_lines; }
  uint16_t getBlankPageCount() const { return _blank_pages; }
  uint16_t getInvalidPageCount() const { return _invalid_pages; }
  uint16_t getPhysicalBreakCount() const { return _physical_breaks; }
  uint16_t getNextPage() const { return _next_page; }
  bool getMessage(uint16_t idx, PublicMessage& message) const;
  bool appendMessage(uint32_t timestamp, const char* text, bool outbound, const uint8_t* hash = NULL, bool rebroadcasted = false);

private:
  static const uint32_t kBaseAddr = 0x0000UL;
  static const uint32_t kFlashSize = 128UL * 1024UL;
  static const uint16_t kSectorSize = 4096;
  static const uint16_t kPageSize = 256;
  static const uint16_t kPageCount = kFlashSize / kPageSize;
  static const uint8_t kPagesPerSector = kSectorSize / kPageSize;
  static const uint32_t kMagic = 0x4D585253UL;  // 'SRXM'

  enum MessageFlags : uint8_t {
    kFlagOutbound = 1 << 0,
  };

  struct MessageRecord {
    uint32_t magic;
    uint16_t version;
    uint16_t crc;
    uint32_t sequence;
    uint32_t timestamp;
    uint8_t flags;
    uint8_t line_count;
    uint8_t hash[MAX_HASH_SIZE];
    char text[sizeof(PublicMessage::text)];
    uint8_t reserved[kPageSize - 4 - 2 - 2 - 4 - 4 - 1 - 1 - MAX_HASH_SIZE - sizeof(PublicMessage::text)];
  };

  static_assert(sizeof(MessageRecord) == kPageSize, "Flash message record size must match a flash page");

  MessageRecord _record_scratch;
  uint16_t _pages[kPageCount];
  uint32_t _sequences[kPageCount];
  uint16_t _next_page;
  uint16_t _count;
  uint32_t _next_sequence;
  uint32_t _sent_count;
  uint32_t _recv_count;
  uint32_t _latest_timestamp;
  uint32_t _total_lines;
  uint16_t _blank_pages;
  uint16_t _invalid_pages;
  uint16_t _physical_breaks;

  static uint16_t crc16(const uint8_t* data, size_t len);
  static bool isBlank(const uint8_t* data, size_t len);
  static bool isValidRecord(const MessageRecord& record);
  static PublicMessage messageFrom(const MessageRecord& record);

  void resetStats();
  void insertPageSorted(uint16_t page, uint32_t sequence);
  void removePageAt(uint16_t idx);
  void updatePhysicalBreaks();
  uint16_t findNextBlankPage(uint16_t start_page) const;
  bool pageInUse(uint16_t page) const;
  bool sectorHasActivePages(uint16_t sector) const;
  bool readRecord(uint16_t page, MessageRecord& record) const;
  uint16_t pageForIndex(uint16_t idx) const;
  bool writeRecord(uint16_t page, const MessageRecord& record);
  bool eraseSector(uint16_t sector);
  bool prepareNextPageForWrite();
  void dropSectorFromStats(uint16_t sector);
};