Public Access
120 lines
2.5 KiB
C++
120 lines
2.5 KiB
C++
#include "logic/frame_check.h"
|
|||
|
|
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
#include "config.h"
|
||
|
|
|
||
|
|
static int hexDigit(char c)
|
||
|
|
{
|
||
|
|
if (c >= '0' && c <= '9')
|
||
|
|
return c - '0';
|
||
|
|
if (c >= 'A' && c <= 'F')
|
||
|
|
return c - 'A' + 10;
|
||
|
|
if (c >= 'a' && c <= 'f')
|
||
|
|
return c - 'a' + 10;
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
void FrameCheck::reset(uint32_t now)
|
||
|
|
{
|
||
|
|
_pending = false;
|
||
|
|
_checked_any = false;
|
||
|
|
_replied = false;
|
||
|
|
_problem = false;
|
||
|
|
// Switching on sends the whole image anyway
|
||
|
|
_last_resync = now;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool FrameCheck::due(uint32_t frames_sent, uint32_t now) const
|
||
|
|
{
|
||
|
|
// A repair is coming anyway: no need to count the same problem again
|
||
|
|
if (_pending || _problem)
|
||
|
|
return false;
|
||
|
|
if (!_checked_any)
|
||
|
|
return true;
|
||
|
|
return frames_sent != _checked_frames && now - _last_check >= FRAME_CHECK_INTERVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
void FrameCheck::sent(uint16_t expected, uint32_t frames_sent, uint32_t now)
|
||
|
|
{
|
||
|
|
_pending = true;
|
||
|
|
_expected = expected;
|
||
|
|
_sent_at = now;
|
||
|
|
_checked_any = true;
|
||
|
|
_checked_frames = frames_sent;
|
||
|
|
_last_check = now;
|
||
|
|
}
|
||
|
|
|
||
|
|
void FrameCheck::line(const char *text, uint32_t now)
|
||
|
|
{
|
||
|
|
(void)now;
|
||
|
|
size_t len = strlen(text);
|
||
|
|
while (len && (text[len - 1] == '\r' || text[len - 1] == ' '))
|
||
|
|
len--;
|
||
|
|
|
||
|
|
if (len == 6 && text[0] == 'C' && text[1] == ' ')
|
||
|
|
{
|
||
|
|
int value = 0;
|
||
|
|
for (int i = 2; i < 6; i++)
|
||
|
|
{
|
||
|
|
int d = hexDigit(text[i]);
|
||
|
|
if (d < 0)
|
||
|
|
return;
|
||
|
|
value = value << 4 | d;
|
||
|
|
}
|
||
|
|
_replied = true;
|
||
|
|
if (!_pending)
|
||
|
|
return; // late answer to a check that already timed out
|
||
|
|
_pending = false;
|
||
|
|
if (value == _expected)
|
||
|
|
_stats.checks++;
|
||
|
|
else
|
||
|
|
{
|
||
|
|
_stats.mismatches++;
|
||
|
|
_problem = true;
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (strncmp(text, "ERR", 3) == 0)
|
||
|
|
{
|
||
|
|
_replied = true;
|
||
|
|
_stats.errors++;
|
||
|
|
size_t n = len < sizeof(_stats.last_error) - 1 ? len : sizeof(_stats.last_error) - 1;
|
||
|
|
memcpy(_stats.last_error, text, n);
|
||
|
|
_stats.last_error[n] = '\0';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (len >= 5 && strncmp(text, "READY", 5) == 0)
|
||
|
|
{
|
||
|
|
// The Nano restarted while it was on: its image is gone
|
||
|
|
_replied = true;
|
||
|
|
_stats.restarts++;
|
||
|
|
_pending = false;
|
||
|
|
_problem = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool FrameCheck::resyncNow(uint32_t now, uint32_t resync_ms)
|
||
|
|
{
|
||
|
|
if (_pending && now - _sent_at >= FRAME_CHECK_TIMEOUT)
|
||
|
|
{
|
||
|
|
_pending = false;
|
||
|
|
_stats.no_reply++;
|
||
|
|
// Only when answers can arrive at all (old Nano firmware, TX not wired: never)
|
||
|
|
if (_replied)
|
||
|
|
_problem = true;
|
||
|
|
}
|
||
|
|
if (_problem && now - _last_resync >= FRAME_REPAIR_MIN_GAP)
|
||
|
|
{
|
||
|
|
_problem = false;
|
||
|
|
_stats.repairs++;
|
||
|
|
_last_resync = now;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (resync_ms && now - _last_resync >= resync_ms)
|
||
|
|
{
|
||
|
|
_last_resync = now;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|