-
Notifications
You must be signed in to change notification settings - Fork 0
/
Planner.h
145 lines (126 loc) · 4.39 KB
/
Planner.h
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#ifndef PLAN_PARSER_HEADER
#define PLAN_PARSER_HEADER
// Only include one please, because of the static variables that should really
// be in .cpp files
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "Plan.h"
enum class ParseError {
OK,
InvalidOrient,
InvalidCoords,
InvalidTime,
UnexpectedEnd,
PlanTooLong
};
// Parses two-letter string into coordinates.
ParseError parseCoords(const char str[2], bool &col_first, uint8_t &row,
uint8_t &col);
// Parsers string of len 3: 'XYZ', X\in{1..9}, Y\in{A...I}, Z==CurrDir
ParseError parseCommand(const char str[2 + 5 + 1], CompPlanEntry &newEntry);
// Parse null terminated string of length max 8, str[0,1]==Coords,
// str[2+]==timepoint integer.
ParseError parseInitPos(const char *str, RobotConfig &initConfig);
// Parses one command from the input stream into the array.
// The cmd in the array has a format of: 'XY12345\0'
template <typename CharGetter>
ParseError preparseCmd(CharGetter &getter, char command_str[2 + 5 + 1]);
void printErrMsg(ParseError e);
// Consumes 3B*64 * 2 RAM = 384B, 1+4*5B+960B EEPROM
constexpr const static uint8_t MaxPlanLength = 64;
constexpr const static uint8_t NumEEPROMSlots = 5;
// Responsible for loading and saving plans.
class Planner {
public:
// Loads the plan either from non-empty default EEPROM slot or from a backup string.
static bool loadDefault();
static bool loadFromString(const char *str);
// Checks whether someones sent any remote commands and processes them if so.
static bool processRemoteRequests();
// The returned PLan is valid until the next loadXXX or processRemotecommands
// methods are called.
static Plan getActivePlan();
private:
Planner() {}
// Load a plan from the serial connection and sets it as active.
static bool loadFromSerial();
// Load a stored plan and sets it as active.
static void loadFromEEPROM(int slot);
// Saves the currently active plan to the chosen slot.
static void saveToEEPROM(int slot);
static void clearEEPROM(int slot);
template <typename CharGetter> static ParseError load(CharGetter &getter);
// Currently active plan loaded into the memory
static CompPlanEntry active_entries[MaxPlanLength];
// Scratch space for loading a new plan
// Seems wastefull but it ensures that if the plan fails in the middle of
// loading then the original plan is preserved.
static CompPlanEntry loaded_entries[MaxPlanLength];
static int num_active_entries;
static RobotConfig active_init_config;
};
template <typename CharGetter>
ParseError preparseCmd(CharGetter &getter, char command_str[2 + 5 + 1]) {
command_str[0] = getter.getNext();
command_str[1] = getter.getNext();
char t = tolower(getter.getNext());
if (t == '\0')
return ParseError::UnexpectedEnd;
if (t != 't')
return ParseError::InvalidCoords;
// Read the time, only skip the first whitespace
// = between 'T' and the first digit
// Time is missing
if (getter.peek_next() == '\0')
return ParseError::UnexpectedEnd;
for (int i = 0; i < 6; ++i) {
char last = getter.peek_next(false);
// The number ended
if (last == '\0' || isspace(last) || isalpha(last)) {
command_str[2 + i] = '\0';
break;
} else if (i == 5) // Time won't fit into 16bits
return ParseError::InvalidTime;
else
command_str[2 + i] = getter.getNext(false);
}
return ParseError::OK;
}
template <typename CharGetter> ParseError Planner::load(CharGetter &getter) {
char init_pos[3];
init_pos[0] = getter.getNext();
init_pos[1] = getter.getNext();
init_pos[2] = getter.getNext();
if (init_pos[2] == '\0')
return ParseError::UnexpectedEnd;
RobotConfig new_init;
ParseError err = parseInitPos(init_pos, new_init);
if (err != ParseError::OK)
return err;
char new_cmd_str[2 + 5 + 1];
// Parse commands
int n = 0;
for (; n < MaxPlanLength; ++n) {
// Input ended
if (getter.peek_next() == '\0') {
break;
}
err = preparseCmd(getter, new_cmd_str);
if (err != ParseError::OK)
return err;
err = parseCommand(new_cmd_str, loaded_entries[n]);
if (err != ParseError::OK)
return err;
}
if (getter.getNext() != '\0')
return ParseError::PlanTooLong;
// Plan loaded successfully -> set as active
for (int i = 0; i < n; ++i)
active_entries[i] = loaded_entries[i];
num_active_entries = n;
active_init_config = new_init;
return ParseError::OK;
}
#endif