324 lines
9.3 KiB
C++
324 lines
9.3 KiB
C++
|
#include <XnodeSystem.h>
|
||
|
|
||
|
#define CMD_MAX_ARGS 8 // max 8 arguments to command
|
||
|
#define CMD_WHITE_SPACE " \r\t\n" // All diffent white space chars to split commands on
|
||
|
|
||
|
const char pmCmdHelp[] PROGMEM = "help";
|
||
|
const char pmCmdReboot[] PROGMEM = "reboot";
|
||
|
const char pmCmdSysCnfRst[] PROGMEM = "sys__cnf__rst"; // (internal)
|
||
|
const char pmCmdSysInfo[] PROGMEM = "sys_info";
|
||
|
|
||
|
const char pmSysConfigSave[] PROGMEM = "Config saved";
|
||
|
const char pmSysConfigLoad[] PROGMEM = "Config loaded";
|
||
|
const char pmSysConfigReset[] PROGMEM = "Config reset";
|
||
|
|
||
|
const char pmSysInfoVNumber[] PROGMEM = "sys_vnum";
|
||
|
const char pmSysInfoVDate[] PROGMEM = "sys_vdate";
|
||
|
const char pmSysInfoVDateValue[] PROGMEM = __DATE__" "__TIME__; // Print compile date like; "Apr 22 2012 16:36:10"
|
||
|
const char pmSysInfoType[] PROGMEM = "sys_type";
|
||
|
const char pmSysInfoBoot[] PROGMEM = "sys_boot";
|
||
|
const char pmSysInfoDebugLevel[] PROGMEM = "sys_debug";
|
||
|
const char pmSysInfoFreeRam[] PROGMEM = "sys_sram";
|
||
|
|
||
|
const char pmSysBootDebugHeader[] PROGMEM = "==== DEBUGGING ENABLED ====";
|
||
|
const char pmSysBootStart[] PROGMEM = "Booting ";
|
||
|
const char pmSysBootDone[] PROGMEM = "boot done";
|
||
|
const char pmSysBootDoneHelp[] PROGMEM = "Available commands;";
|
||
|
|
||
|
const char pmCmdErrArgument[] PROGMEM = "#ERR Missing argument: ";
|
||
|
const char pmCmdErrUnknown[] PROGMEM = "#ERR Unknown command: ";
|
||
|
|
||
|
const char pmRemoteExecutePrefix[] PROGMEM = "remote@";
|
||
|
|
||
|
uint8_t reboot_requested = ZERO;
|
||
|
uint8_t debug_level = ZERO;
|
||
|
uint8_t replyBufferIndex = ZERO;
|
||
|
uint8_t systemModuleIndex = ZERO;
|
||
|
XnodeSystemModule* systemModules[SYSTEM_MODULE_ARRAY_SIZE];
|
||
|
XnodeSystemHardware* hardware;
|
||
|
XnodeSystem XSystem;
|
||
|
|
||
|
void XnodeSystem::begin(XnodeSystemHardware* hardwareNode) {
|
||
|
hardware = hardwareNode;
|
||
|
|
||
|
// Print first line of boot process
|
||
|
XSerial.print(CHAR_COMMENT);
|
||
|
XSerial.print(CHAR_SPACE);
|
||
|
XSerial.printCharP (pmSysBootStart); // has space
|
||
|
XSerial.printCharP(hardware->getSystemHardwareTypeP());
|
||
|
XSerial.print(CHAR_NEWLINE);
|
||
|
|
||
|
// Start debug asp
|
||
|
beginDebug();
|
||
|
|
||
|
// Fixup config
|
||
|
XSerial.printCommentLineP(pmSysConfigLoad);
|
||
|
if (hardware->systemHardwareConfigBegin()) {
|
||
|
hardware->systemHardwareConfigReset();
|
||
|
XSerial.printCommentLineP(pmSysConfigReset);
|
||
|
}
|
||
|
hardware->systemHardwareConfigSave();
|
||
|
|
||
|
// Print system info
|
||
|
XSerial.executeCommandP(pmCmdSysInfo);
|
||
|
|
||
|
// Enable the watchdog
|
||
|
wdt_enable(WDT_TIMEOUT);
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::loop() {
|
||
|
// Tickle the watchdog
|
||
|
wdt_reset();
|
||
|
|
||
|
// Reboot countdown and action so reply can be send.
|
||
|
if (reboot_requested > ONE) {
|
||
|
reboot_requested--;
|
||
|
} else if (reboot_requested > ZERO) {
|
||
|
wdt_enable (WDTO_15MS); // reboot in 15ms.
|
||
|
delay(30);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::beginDebug() {
|
||
|
// Make sure human and machine are notified when debugging is turned on.
|
||
|
#ifdef DEBUG_NETWORK
|
||
|
// shift every flag on its on bit so machine can readout which flags is enabled.
|
||
|
debug_level+=ONE << 0; // 1
|
||
|
#endif
|
||
|
#ifdef DEBUG_RADIO
|
||
|
debug_level+=ONE << 1; // 2
|
||
|
#endif
|
||
|
#ifdef DEBUG_SYSTEM
|
||
|
debug_level+=ONE << 2; // 4
|
||
|
#endif
|
||
|
#ifdef DEBUG_SERIAL
|
||
|
debug_level+=ONE << 3; // 8
|
||
|
#endif
|
||
|
#ifdef DEBUG_SENSOR
|
||
|
debug_level+=ONE << 4; // 16, all = 31
|
||
|
#endif
|
||
|
if (debug_level > ZERO) {
|
||
|
XSerial.printCommentLineP(pmSysBootDebugHeader); // print line for human,machine version is in serialPrintInfo.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::bootDone() {
|
||
|
XSerial.printCommentLineP(pmSysBootDoneHelp);
|
||
|
XSerial.executeCommandP(pmCmdHelp);
|
||
|
XSerial.printCommentLineP(pmSysBootDone);
|
||
|
XSerial.printPromt();
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::configSave() {
|
||
|
#ifdef DEBUG_SYSTEM
|
||
|
Serial.println(F("#D XSystem.configSave"));
|
||
|
#endif
|
||
|
wdt_disable();
|
||
|
hardware->systemHardwareConfigSave();
|
||
|
wdt_enable (WDT_TIMEOUT);
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::cmdSysInfo() {
|
||
|
buildReplyPValueP(pmSysInfoVNumber,hardware->getSystemHardwareVersionP());
|
||
|
buildReplyPValueP(pmSysInfoVDate,pmSysInfoVDateValue);
|
||
|
buildReplyPValue(pmSysInfoBoot,hardware->getSystemHardwareRebootCount());
|
||
|
buildReplyPValueP(pmSysInfoType,hardware->getSystemHardwareTypeP());
|
||
|
buildReplyPValue(pmSysInfoDebugLevel,debug_level);
|
||
|
buildReplyPValue(pmSysInfoFreeRam,XUtil.freeRam());
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::cmdHelp() {
|
||
|
for (uint8_t i = ZERO; i < SYSTEM_MODULE_ARRAY_SIZE; i++) {
|
||
|
if (systemModules[i] == ZERO) {
|
||
|
continue;
|
||
|
}
|
||
|
systemModules[i]->systemModuleCommandList();
|
||
|
}
|
||
|
buildReplyCommandListP(pmCmdSysInfo);
|
||
|
buildReplyCommandListP(pmCmdReboot);
|
||
|
buildReplyCommandListP(pmCmdHelp);
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::registrateSystemModule(XnodeSystemModule* module) {
|
||
|
systemModules[systemModuleIndex++] = module;
|
||
|
}
|
||
|
|
||
|
// execute cmd with the supplied argument
|
||
|
bool XnodeSystem::executeCommandModule(char* cmd, char** args) {
|
||
|
|
||
|
// Check for system build in commands.
|
||
|
if (XUtil.strcmpP(cmd, pmCmdHelp) == ZERO) {
|
||
|
cmdHelp();
|
||
|
return true;
|
||
|
}
|
||
|
if (XUtil.strcmpP(cmd, pmCmdReboot) == ZERO) {
|
||
|
buildReplyPValue(pmCmdReboot,ONE);
|
||
|
reboot_requested = 100; // let reply be send out/back
|
||
|
return true;
|
||
|
}
|
||
|
if (XUtil.strcmpP(cmd, pmCmdSysInfo) == ZERO) {
|
||
|
cmdSysInfo();
|
||
|
return true;
|
||
|
}
|
||
|
if (XUtil.strcmpP(cmd, pmCmdSysCnfRst) == ZERO) {
|
||
|
hardware->systemHardwareConfigReset();
|
||
|
buildReplyPValue(pmCmdSysCnfRst,ONE);
|
||
|
configSave();
|
||
|
buildReplyPValue(pmCmdReboot,ONE);
|
||
|
reboot_requested = 100;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Check registated parses.
|
||
|
for (uint8_t i = ZERO; i < SYSTEM_MODULE_ARRAY_SIZE; i++) {
|
||
|
if (systemModules[i] == ZERO) {
|
||
|
continue;
|
||
|
}
|
||
|
if (systemModules[i]->systemModuleCommandExecute(cmd, args)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false; // no command found
|
||
|
}
|
||
|
|
||
|
// Parse the cmd from a buffer
|
||
|
void XnodeSystem::executeCommand(char* input,bool isRemote) {
|
||
|
#ifdef DEBUG_SYSTEM
|
||
|
Serial.print(F("#D XSystem.executeCommand input="));
|
||
|
Serial.print(input);
|
||
|
Serial.println();
|
||
|
#endif
|
||
|
if (isRemote) {
|
||
|
XSerial.printCharP(pmRemoteExecutePrefix);
|
||
|
XSerial.printPromt();
|
||
|
XSerial.printChar(input);
|
||
|
XSerial.print(CHAR_NEWLINE);
|
||
|
}
|
||
|
replyBufferIndex = ZERO;
|
||
|
uint8_t idx = ZERO;
|
||
|
char *cmd, *ptr, *args[CMD_MAX_ARGS];
|
||
|
if (strtok((char *) input, CMD_WHITE_SPACE) == NULL) {
|
||
|
// no command given so just print new promt.
|
||
|
buildReplyCharP(pmCmdErrUnknown); // TODO: replace with key+value
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
buildReply('\0');
|
||
|
return;
|
||
|
}
|
||
|
cmd = (char *) input;
|
||
|
while ((ptr = strtok(NULL, CMD_WHITE_SPACE)) != NULL) {
|
||
|
args[idx] = ptr;
|
||
|
if (++idx == (CMD_MAX_ARGS - ONE)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
args[idx] = NULL;
|
||
|
bool parsed = executeCommandModule(cmd, args);
|
||
|
if (!parsed) {
|
||
|
buildReplyCharP(pmCmdErrUnknown);
|
||
|
buildReplyChar(cmd); // TODO: add args
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
}
|
||
|
buildReply('\0');
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::buildReply(unsigned long value, int base) {
|
||
|
char buf[8 * sizeof(long) + ONE]; // Assumes 8-bit chars plus zero byte.
|
||
|
char *str = &buf[sizeof(buf) - ONE];
|
||
|
*str = ZERO; // reverse printing so start with termination.
|
||
|
if (base < 2) {
|
||
|
base = 10;// fix base 1 or 0 default to decimals
|
||
|
}
|
||
|
do {
|
||
|
unsigned long m = value;
|
||
|
value /= base;
|
||
|
char c = m - base * value;
|
||
|
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||
|
} while(value);
|
||
|
return buildReplyChar(str);
|
||
|
}
|
||
|
void XnodeSystem::buildReply(float value,int digitsFull,int digitsDot) {
|
||
|
dtostrf(value, digitsFull, digitsDot, (char*)replyBuffer+replyBufferIndex);
|
||
|
replyBufferIndex += (digitsFull + 1 + digitsDot) - 1; // "999.9" "-99.9" and remove terminating zero.
|
||
|
}
|
||
|
void XnodeSystem::buildReply(char value) {
|
||
|
replyBuffer[replyBufferIndex++] = value;
|
||
|
if (replyBufferIndex > SYSTEM_REPLY_ARRAY_SIZE) {
|
||
|
replyBufferIndex--;
|
||
|
replyBuffer[replyBufferIndex] = ZERO; // terminate last char
|
||
|
#ifdef DEBUG_SYSTEM
|
||
|
Serial.print("#D+");
|
||
|
Serial.print(value); // print overflow chars
|
||
|
Serial.print("-");
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
void XnodeSystem::buildReplyChar(char* value) {
|
||
|
while (*value != ZERO) {
|
||
|
buildReply(*value);
|
||
|
value++;
|
||
|
}
|
||
|
}
|
||
|
void XnodeSystem::buildReplyCharP(const char* value) {
|
||
|
buildReplyChar(XUtil.UNPSTR(value));
|
||
|
}
|
||
|
void XnodeSystem::buildReplyCommandListP(const char* cmdName) {
|
||
|
buildReplyCharP(cmdName);
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
}
|
||
|
bool XnodeSystem::buildReplyCommandArgumentError(char* cmd, char** args) {
|
||
|
if (args[ZERO] != NULL) {
|
||
|
return false; // nothing printed
|
||
|
}
|
||
|
buildReplyCharP(pmCmdErrArgument);
|
||
|
buildReplyChar(cmd);
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
return true;
|
||
|
}
|
||
|
void XnodeSystem::buildReplyPValueIP(const char* valueName,uint8_t ip[4]) {
|
||
|
buildReplyCharP(valueName);
|
||
|
buildReply(CHAR_EQUALS);
|
||
|
buildReply(ip[0], DEC);
|
||
|
buildReply(CHAR_DOT);
|
||
|
buildReply(ip[1], DEC);
|
||
|
buildReply(CHAR_DOT);
|
||
|
buildReply(ip[2], DEC);
|
||
|
buildReply(CHAR_DOT);
|
||
|
buildReply(ip[3], DEC);
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
}
|
||
|
|
||
|
void XnodeSystem::buildReplyPValue(const char* valueName,unsigned long value) {
|
||
|
buildReplyCharP(valueName);
|
||
|
buildReply(CHAR_EQUALS);
|
||
|
buildReply(value);
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
}
|
||
|
void XnodeSystem::buildReplyPValue(const char* valueName,int value) {
|
||
|
buildReplyPValue(valueName,(unsigned long)value);
|
||
|
}
|
||
|
void XnodeSystem::buildReplyPValue(const char* valueName,float value,int digitsFull,int digitsDot) {
|
||
|
buildReplyCharP(valueName);
|
||
|
buildReply(CHAR_EQUALS);
|
||
|
buildReply(value,digitsFull,digitsDot);
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
}
|
||
|
void XnodeSystem::buildReplyPValueP(const char* valueName,const char* valueP) {
|
||
|
buildReplyCharP(valueName);
|
||
|
buildReply(CHAR_EQUALS);
|
||
|
buildReplyCharP(valueP);
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
}
|
||
|
void XnodeSystem::buildReplyPValueByteA(const char* valueName,byte* value, byte data_len) {
|
||
|
buildReplyCharP(valueName);
|
||
|
buildReply(CHAR_EQUALS);
|
||
|
for (byte i = ZERO; i < data_len; ++i) {
|
||
|
byte d = value[i];
|
||
|
if (d<0xF) {
|
||
|
buildReply('0'); // exta zero to have two chars
|
||
|
}
|
||
|
buildReply(d, HEX);
|
||
|
}
|
||
|
buildReply(CHAR_NEWLINE);
|
||
|
}
|
||
|
|