-
Notifications
You must be signed in to change notification settings - Fork 0
/
commands.pde
272 lines (222 loc) · 7.98 KB
/
commands.pde
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/*
* Documenting our discussion [literals in all caps, variables in lowercase]:
* Changes/clarifications?
* All messages are terminated by '\n' NOT '\0'
*
* GO axis position time
* ACK GO axis position time
* DONE axis
*
* GO Tells the controller to move AXIS to POSITION taking TIME miliseconds to get there
* ACK is sent by the controller immediately after GO recvd. POSITION and TIME may be modified if POSITION was out of range or TIME was too short.
* NOTICE DONE is sent when the move completes
*
* STOP Tells the controller to stop the motion of all axis immediately
* ACK is sent by the controller immediately after DONE recvd.
* NOTICE DONE will be recieved on any axis that were moving
*
* SET param value
* ACK SET param value
* Sets PARAM to VALUE. Valid PARAMs are: VERSION, INDEX, MAX_VELOCITY, ACCEL, POS
*
* GET param value
* ACK GET param value
* Gets the current VALUE of PARAM.
*
* HOME axis
* ACK HOME axis
* NOTICE DONE axis
* Executes homing procedure for the specified AXIS. DONE is sent when homing completes.
*
* STATE
* ACK STATE state
*
* Queries current state. state = [READY, ERROR, GOING, HOMING]
*
* ERROR msg
* ERROR is sent by the controller when something bad happens. msg described what happened
*
* ALIVE
* ACK ALIVE
*
* Confirm that the controller is alive and accepting input
*
* CLICK
* ACK CLICK
*
* Send a click to the camera, which is on the second serial port
*
*/
#include "commands.h"
#include <stdlib.h>
#include <ctype.h>
#include <HardwareSerial.h>
// TODO: Fill out num_values here?
CommandInterpreter::ParameterDefinition CommandInterpreter::parameterTypes[] = {
{ "VERSION", P_VERSION, false }, // Interface version (read only)
{ "INDEX", P_INDEX, false }, // Programmable device name for this specific board
{ "MAX_VEL", P_MAX_VEL, true },
{ "ACCEL", P_ACCEL, true },
{ "STOP_MODE",P_STOP_MODE, true },
{ "POS", P_POS, true },
{ NULL, NOT_A_PARAMETER },
};
CommandInterpreter::MessageTypeDefinition CommandInterpreter::messageTypes[] = {
{ "GO", M_GO , GO_VALUES }, // GO axis position time
{ "STOP", M_STOP , NO_VALUES }, // STOP
{ "GET", M_GET , GET_VALUES }, // GET param axis
{ "SET", M_SET , SET_VALUES }, // SET param axis value
{ "HOME", M_HOME , HOME_VALUES }, // HOME axis
{ "STATE", M_STATE , NO_VALUES }, // STATE
{ "ALIVE", M_ALIVE , NO_VALUES }, // ALIVE
{ "CLICK", M_CLICK , NO_VALUES }, // Send a 'click' to the camera
{ NULL, NOT_A_MESSAGE, NULL },
};
CommandInterpreter::MESSAGE_VALUE_TYPE CommandInterpreter::NO_VALUES[] = {NOT_A_VALUE};
CommandInterpreter::MESSAGE_VALUE_TYPE CommandInterpreter::GO_VALUES[] = {MT_INTEGER, MT_INTEGER, MT_INTEGER, NOT_A_VALUE};
CommandInterpreter::MESSAGE_VALUE_TYPE CommandInterpreter::SET_VALUES[] = {MT_PARAM_NAME, MT_INTEGER, MT_INTEGER, NOT_A_VALUE};
CommandInterpreter::MESSAGE_VALUE_TYPE CommandInterpreter::GET_VALUES[] = {MT_PARAM_NAME, MT_INTEGER, NOT_A_VALUE};
CommandInterpreter::MESSAGE_VALUE_TYPE CommandInterpreter::HOME_VALUES[] = {MT_INTEGER, NOT_A_VALUE};
CommandInterpreter::CommandInterpreter(CommandHandler handler) :
commandBufIdx(0),
cmdHandler(handler)
{
}
void CommandInterpreter::begin(unsigned int baudrate)
{
Serial.begin(baudrate);
}
void CommandInterpreter::sendERROR( const char* message ) {
Serial.print("ERROR ");
Serial.print(message);
Serial.print("\n");
}
void CommandInterpreter::sendACK( const char* message ) {
Serial.print("ACK ");
Serial.print(message);
Serial.print("\n");
}
void CommandInterpreter::sendNOTICE( const char* message ) {
Serial.print("NOTICE ");
Serial.print(message);
Serial.print("\n");
}
void CommandInterpreter::sendDONE( const char* message ) {
Serial.print("DONE ");
Serial.print(message);
Serial.print("\n");
}
//returns the index of the character after the last charater in the message type or -1
//sets msg->type and msg->typeDefIdx
int CommandInterpreter::parseCmdType( const char *cmd, Message& msg ) {
int i=0;
while( messageTypes[i].name != NULL ) {
if( strncmp( cmd, messageTypes[i].name, strlen(messageTypes[i].name) ) == 0 ) {
msg.type = messageTypes[i].type;
msg.typeDefIdx = i;
return strlen(messageTypes[i].name);
}
i++;
}
sendERROR( "message had unknown prefix" );
msg.type = NOT_A_MESSAGE;
return -1;
}
// TODO: Make this signature match with parseCmdType()
PARAMETER CommandInterpreter::parseParamName( const char *name, boolean& requiresAxis ) {
int i=0;
while( parameterTypes[i].name != NULL ) {
if( strncmp( name, parameterTypes[i].name, sizeof(parameterTypes[i].name)) == 0 ) {
requiresAxis = parameterTypes[i].requiresAxis;
return parameterTypes[i].param;
}
i++;
}
return NOT_A_PARAMETER;
}
//pre: msg->type is the type of message in cmd
//post: msg->fields is filled in or non-zero is returned
boolean CommandInterpreter::parseMessageValues( const char *str, Message& msg ) {
int strIdx = 0;
int valIdx = 0;
// Look up the type of message we are dealing with, so we know what set of inputs to
// look for (integers, parameter names, etc)
MessageTypeDefinition& mt = messageTypes[msg.typeDefIdx];
// While we are still expecting more input
while( mt.values[valIdx] != NOT_A_VALUE ) {
//advance past leading whitespace
while( isspace( str[strIdx]) && str[strIdx] != 0 ) strIdx++;
switch( mt.values[valIdx] ) {
// If it is an integer, read it in as a signed long
case MT_INTEGER:
if( 1 != sscanf( str + strIdx, "%ld", &msg.fields[valIdx] ) )
goto PARSE_ERROR;
break;
// If it is a parameter name, read it in as a string
case MT_PARAM_NAME:
if( 1 != sscanf( str + strIdx, "%s", paramBuf ) ) goto PARSE_ERROR;
// This is true if the parameter doesn't require an axis, and causes the state machine
// to skip the following parameter in the MESSAGE_VALUE_TYPE list
boolean requiresAxis;
// TODO: Don't copy this string, send a pointer to parseParamName
// Then attempt to find it in the parameter list
msg.fields[valIdx] = parseParamName( paramBuf, requiresAxis );
if( msg.fields[valIdx] == NOT_A_PARAMETER)
goto PARSE_ERROR;
// If we got a parameter that did not require an axis specification,
// skip an input
if ( !requiresAxis ) {
valIdx ++;
}
break;
}
// Advance to the next whitespace separator
while( !isspace(str[strIdx] ) ) strIdx++;
// Then advance to the next value
valIdx++;
}
return true;
PARSE_ERROR:
sendERROR( "message arguments did not parse" );
msg.type = NOT_A_MESSAGE;
return false;
}
//pre: cmd has a null terminated string ending in newline
//post: msg contains the new command
//return: 0 if a valid message was read, non-zero on error
boolean CommandInterpreter::processCommand( const char* command, Message& msg ) {
int commandIdx = 0;
commandIdx += parseCmdType( command, msg );
if( msg.type == NOT_A_MESSAGE ) {
return false;
}
return parseMessageValues( command + commandIdx, msg );
}
void CommandInterpreter::checkSerialInput() {
static Message staticMsg;
if( !Serial.available() ) return;
commandBuf[commandBufIdx++] = Serial.read();
commandBuf[commandBufIdx ] = 0;
if( commandBuf[commandBufIdx-1] == '\n' ) {
if( !processCommand( commandBuf, staticMsg ) ) {
commandBufIdx = 0;
return;
}
if( staticMsg.type == M_ALIVE ) {
sendACK( "ALIVE" );
commandBufIdx = 0;
}
if( cmdHandler == NULL ) {
sendERROR( "No way to parse messages!" );
commandBufIdx = 0;
return;
}
cmdHandler( &staticMsg );
commandBufIdx = 0;
}
else if( commandBufIdx == CMD_BUF_LEN ) {
commandBufIdx = 0;
sendERROR( "message too long" );
return;
}
}