From 183a5d6a68c7ad57c3d1a2c505ca2c8312acb1f5 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 09:26:03 -1000 Subject: [PATCH 01/12] Allow white space in the command Valid commands may now include space, horizontal tab(0x09), line feed (0x0a), vertical tab (0x0b), form feed (0x0c) and are terminated with a carriage return (0x0d). These characters are removed prior to processing the command. Blank lines return OK. Valid commands: at-netID = 1 translates into AT-NETID=1 a t i 1 translates into ATI1 --- Firmware/LoRaSerial/Commands.ino | 35 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/Firmware/LoRaSerial/Commands.ino b/Firmware/LoRaSerial/Commands.ino index 70aa2df1..9eddefe7 100644 --- a/Firmware/LoRaSerial/Commands.ino +++ b/Firmware/LoRaSerial/Commands.ino @@ -814,9 +814,12 @@ void checkCommand() int prefixLength; bool success; - //Verify the command length + //Zero terminate the string success = false; - commandString = trimCommand(); //Remove any leading or trailing whitespace + commandBuffer[commandLength] = 0; + + //Remove any whitespace + commandString = trimCommand(); //Upper case the command for (index = 0; index < commandLength; index++) @@ -845,6 +848,8 @@ void checkCommand() break; } } + else if (!commandLength) + success = true; //Print the command failure petWDT(); @@ -903,24 +908,20 @@ void commandReset() //Remove any preceeding or following whitespace chars char * trimCommand() { - char * commandString = commandBuffer; + int index; + int j; - //Remove the leading white space - while (isspace(*commandString)) + //Remove the white space + for (index = 0; index < commandLength; index++) { - commandString++; - --commandLength; + while (isspace(commandBuffer[index])) + { + for (j = index + 1; j < commandLength; j++) + commandBuffer[j - 1] = commandBuffer[j]; + commandBuffer[--commandLength] = 0; + } } - - //Zero terminate the string - commandString[commandLength] = 0; - - //Remove the trailing white space - while (commandLength && isspace(commandString[commandLength - 1])) - commandString[--commandLength] = 0; - - //Return the remainder as the command - return commandString; + return commandBuffer; } //Display all of the commands From c360b103fa4af1759d3dd25fcd57076ab479b705 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 09:37:30 -1000 Subject: [PATCH 02/12] Comments in command scripts are OK at-netID = 2 # My private network Translates to AT-NETID=2 --- Firmware/LoRaSerial/Commands.ino | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Firmware/LoRaSerial/Commands.ino b/Firmware/LoRaSerial/Commands.ino index 9eddefe7..720162de 100644 --- a/Firmware/LoRaSerial/Commands.ino +++ b/Firmware/LoRaSerial/Commands.ino @@ -911,6 +911,17 @@ char * trimCommand() int index; int j; + //Remove the comment + for (index = 0; index < commandLength; index++) + { + if (commandBuffer[index] == '#') + { + commandBuffer[index] = 0; + commandLength = index; + break; + } + } + //Remove the white space for (index = 0; index < commandLength; index++) { From a94ce9fd33cc85878eb13c30a2fac4f5021cb0a0 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 13:21:42 -1000 Subject: [PATCH 03/12] Always return to PRINT_TO_SERIAL at the end of a command --- Firmware/LoRaSerial/Commands.ino | 1 + Firmware/LoRaSerial/Serial.ino | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/LoRaSerial/Commands.ino b/Firmware/LoRaSerial/Commands.ino index 720162de..373e6bf6 100644 --- a/Firmware/LoRaSerial/Commands.ino +++ b/Firmware/LoRaSerial/Commands.ino @@ -860,6 +860,7 @@ void checkCommand() systemPrintln("ERROR"); commandComplete(false); } + printerEndpoint = PRINT_TO_SERIAL; outputSerialData(true); petWDT(); diff --git a/Firmware/LoRaSerial/Serial.ino b/Firmware/LoRaSerial/Serial.ino index 3738c269..362e1b35 100644 --- a/Firmware/LoRaSerial/Serial.ino +++ b/Firmware/LoRaSerial/Serial.ino @@ -375,7 +375,6 @@ void updateSerial() commandBuffer[0] = 'A'; //Convert this RT command to an AT command for local consumption printerEndpoint = PRINT_TO_RF; //Send prints to RF link checkCommand(); //Parse the command buffer - printerEndpoint = PRINT_TO_SERIAL; remoteCommandResponse = true; } else From 9781cf054ac195b2786ddda61e06de62c9f5d98b Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 13:25:37 -1000 Subject: [PATCH 04/12] Add more debugSerial output --- Firmware/LoRaSerial/Commands.ino | 39 ++++++++++++++++++++++++++++++++ Firmware/LoRaSerial/Serial.ino | 6 +++++ 2 files changed, 45 insertions(+) diff --git a/Firmware/LoRaSerial/Commands.ino b/Firmware/LoRaSerial/Commands.ino index 373e6bf6..8fee28ec 100644 --- a/Firmware/LoRaSerial/Commands.ino +++ b/Firmware/LoRaSerial/Commands.ino @@ -780,6 +780,12 @@ bool sendRemoteCommand(const char * commandString) commandTXBuffer[commandTXHead++] = commandString[x]; commandTXHead %= sizeof(commandTXBuffer); } + if (settings.debugSerial) + { + systemPrint("sendRemoteCommand moved "); + systemPrint(commandLength); + systemPrintln(" from commandBuffer into commandTXBuffer"); + } remoteCommandResponse = false; return true; } @@ -812,8 +818,20 @@ void checkCommand() char * commandString; int index; int prefixLength; + PrinterEndpoints responseDestination; + uint16_t responseLength; bool success; + //Save previous index + if (settings.debugSerial) + { + responseDestination = printerEndpoint; + if (responseDestination == PRINT_TO_SERIAL) + responseLength = txHead; + else + responseLength = commandTXHead; + } + //Zero terminate the string success = false; commandBuffer[commandLength] = 0; @@ -860,7 +878,28 @@ void checkCommand() systemPrintln("ERROR"); commandComplete(false); } + + //Display the response printerEndpoint = PRINT_TO_SERIAL; + if (settings.debugSerial) + { + uint16_t tail; + tail = responseLength; + if (responseDestination == PRINT_TO_SERIAL) + { + responseLength = (txHead + sizeof(serialTransmitBuffer) - responseLength) % sizeof(serialTransmitBuffer); + systemPrint("checkCommand placed "); + systemPrint(responseLength); + systemPrintln(" command response bytes into serialTransmitBuffer"); + } + else + { + responseLength = (commandTXHead + sizeof(commandTXBuffer) - responseLength) % sizeof(commandTXBuffer); + systemPrint("checkCommand placed "); + systemPrint(responseLength); + systemPrintln(" command response bytes into commandTXBuffer"); + } + } outputSerialData(true); petWDT(); diff --git a/Firmware/LoRaSerial/Serial.ino b/Firmware/LoRaSerial/Serial.ino index 362e1b35..b5140fab 100644 --- a/Firmware/LoRaSerial/Serial.ino +++ b/Firmware/LoRaSerial/Serial.ino @@ -417,6 +417,12 @@ void processSerialInput() { printerEndpoint = PRINT_TO_SERIAL; systemPrintln(); + if (settings.debugSerial) + { + systemPrint("processSerialInput moved "); + systemPrint(commandLength); + systemPrintln(" from serialReceiveBuffer into commandBuffer"); + } checkCommand(); //Process command buffer } else if (incoming == '\n') From 497bf60d9f8ddecaabdd6cf1d6dcae7bb3f43a8c Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 13:30:19 -1000 Subject: [PATCH 05/12] Bug Fix: Receive remote command responses when in command mode --- Firmware/LoRaSerial/Radio.ino | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Firmware/LoRaSerial/Radio.ino b/Firmware/LoRaSerial/Radio.ino index 5027f47f..976e5ab4 100644 --- a/Firmware/LoRaSerial/Radio.ino +++ b/Firmware/LoRaSerial/Radio.ino @@ -2393,7 +2393,10 @@ PacketType validateDatagram(VIRTUAL_CIRCUIT * vc, PacketType datagramType, uint8 } //Verify that there is sufficient space in the serialTransmitBuffer - if (inCommandMode || (freeBytes < rxDataBytes)) + //Apply back pressure if the remote system is trying to send data while + //the local system is in command mode. Remote command responses should + //be received. + if ((inCommandMode && (datagramType == DATAGRAM_DATA)) || (freeBytes < rxDataBytes)) { if (settings.debugReceive || settings.debugDatagrams) { From 7ed4b7823b69fa8a7f2a7ee920bdf6e0869ace37 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 13:37:04 -1000 Subject: [PATCH 06/12] Wait for command response before processing next command --- Firmware/LoRaSerial/Commands.ino | 1 + Firmware/LoRaSerial/LoRaSerial.ino | 1 + Firmware/LoRaSerial/Serial.ino | 1 + Firmware/LoRaSerial/States.ino | 2 ++ 4 files changed, 5 insertions(+) diff --git a/Firmware/LoRaSerial/Commands.ino b/Firmware/LoRaSerial/Commands.ino index 8fee28ec..36fc1b96 100644 --- a/Firmware/LoRaSerial/Commands.ino +++ b/Firmware/LoRaSerial/Commands.ino @@ -787,6 +787,7 @@ bool sendRemoteCommand(const char * commandString) systemPrintln(" from commandBuffer into commandTXBuffer"); } remoteCommandResponse = false; + waitRemoteCommandResponse = true; return true; } diff --git a/Firmware/LoRaSerial/LoRaSerial.ino b/Firmware/LoRaSerial/LoRaSerial.ino index b6757f94..2f6906bf 100644 --- a/Firmware/LoRaSerial/LoRaSerial.ino +++ b/Firmware/LoRaSerial/LoRaSerial.ino @@ -230,6 +230,7 @@ const long minEscapeTime_ms = 2000; //Serial traffic must stop this amount befor bool inCommandMode = false; //Normal data is prevented from entering serial output when in command mode uint8_t commandLength = 0; bool remoteCommandResponse; +bool waitRemoteCommandResponse; bool rtsAsserted; //When RTS is asserted, host says it's ok to send data bool forceRadioReset = false; //Goes true when a setting requires a link/radio reset to work diff --git a/Firmware/LoRaSerial/Serial.ino b/Firmware/LoRaSerial/Serial.ino index b5140fab..493fe1f2 100644 --- a/Firmware/LoRaSerial/Serial.ino +++ b/Firmware/LoRaSerial/Serial.ino @@ -395,6 +395,7 @@ void processSerialInput() radioHead = radioTxHead; while (availableRXBytes() && (availableRadioTXBytes() < (sizeof(radioTxBuffer) - maxEscapeCharacters)) + && ((!inCommandMode) || (!waitRemoteCommandResponse)) && (transactionComplete == false)) { //Take a break if there are ISRs to attend to diff --git a/Firmware/LoRaSerial/States.ino b/Firmware/LoRaSerial/States.ino index 993f8cbf..977d1e0a 100644 --- a/Firmware/LoRaSerial/States.ino +++ b/Firmware/LoRaSerial/States.ino @@ -806,6 +806,7 @@ void updateRadioState() outputSerialData(true); } serialBufferOutput(rxData, rxDataBytes); + waitRemoteCommandResponse = false; //Transmit ACK P2P_SEND_ACK(TRIGGER_TX_ACK); @@ -2982,6 +2983,7 @@ void discardPreviousData() txTail = txHead; commandRXTail = commandRXHead; commandTXTail = commandTXHead; + waitRemoteCommandResponse = false; } //Output VC link status From 7a27c87c8f328fd135a2ee3f10bb0d84bcec054b Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 13:40:07 -1000 Subject: [PATCH 07/12] Only start one command at a time --- Firmware/LoRaSerial/Serial.ino | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Firmware/LoRaSerial/Serial.ino b/Firmware/LoRaSerial/Serial.ino index 493fe1f2..d20931b3 100644 --- a/Firmware/LoRaSerial/Serial.ino +++ b/Firmware/LoRaSerial/Serial.ino @@ -414,17 +414,22 @@ void processSerialInput() //Process serial into either rx buffer or command buffer if (inCommandMode == true) { - if (incoming == '\r' && commandLength > 0) + //Check for end of command + if (incoming == '\r') { - printerEndpoint = PRINT_TO_SERIAL; - systemPrintln(); - if (settings.debugSerial) + //Ignore end of command if no command in the buffer + if (commandLength > 0) { - systemPrint("processSerialInput moved "); - systemPrint(commandLength); - systemPrintln(" from serialReceiveBuffer into commandBuffer"); + systemPrintln(); + if (settings.debugSerial) + { + systemPrint("processSerialInput moved "); + systemPrint(commandLength); + systemPrintln(" from serialReceiveBuffer into commandBuffer"); + } + checkCommand(); //Process command buffer + break; } - checkCommand(); //Process command buffer } else if (incoming == '\n') ; //Do nothing From a2ef3d14ac137142665f15ac0b2dfa30ffdec061 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 13:42:57 -1000 Subject: [PATCH 08/12] Use semicolon to separate multiple commands on a single line rt-netid = 2 #Private network; rt-netid Translates into the two commands RT-NETID=2 RT-NETID --- Firmware/LoRaSerial/Serial.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/LoRaSerial/Serial.ino b/Firmware/LoRaSerial/Serial.ino index d20931b3..516a6e43 100644 --- a/Firmware/LoRaSerial/Serial.ino +++ b/Firmware/LoRaSerial/Serial.ino @@ -415,7 +415,7 @@ void processSerialInput() if (inCommandMode == true) { //Check for end of command - if (incoming == '\r') + if ((incoming == '\r') || (incoming == ';')) { //Ignore end of command if no command in the buffer if (commandLength > 0) From 70c04886a073fa2f988102ea378b84abba9cc04e Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 12 Mar 2023 09:08:46 -1000 Subject: [PATCH 09/12] Process the command as soon as it fills the command buffer --- Firmware/LoRaSerial/Serial.ino | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Firmware/LoRaSerial/Serial.ino b/Firmware/LoRaSerial/Serial.ino index 516a6e43..d5e9a091 100644 --- a/Firmware/LoRaSerial/Serial.ino +++ b/Firmware/LoRaSerial/Serial.ino @@ -354,6 +354,10 @@ void updateSerial() { commandLength = availableRXCommandBytes(); + //Don't overflow the command buffer, save space for the zero termination + if (commandLength >= sizeof(commandBuffer)) + commandLength = sizeof(commandBuffer) - 1; + for (x = 0 ; x < commandLength ; x++) { commandBuffer[x] = commandRXBuffer[commandRXTail++]; @@ -454,7 +458,23 @@ void processSerialInput() { //Move this character into the command buffer commandBuffer[commandLength++] = incoming; - commandLength %= sizeof(commandBuffer); + + //Don't allow the command to overflow the command buffer + //Process the long command instead + //Save room for the zero termination + if (commandLength >= (sizeof(commandBuffer) - 1)) + { + printerEndpoint = PRINT_TO_SERIAL; + systemPrintln(); + if (settings.debugSerial) + { + systemPrint("processSerialInput moved "); + systemPrint(commandLength); + systemPrintln(" from serialReceiveBuffer into commandBuffer"); + } + checkCommand(); //Process command buffer + break; + } } } } From 8fac0785a707773e9c9351ac79707f13a71135d8 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sat, 25 Feb 2023 10:38:27 -1000 Subject: [PATCH 10/12] VC: Pass the unique ID to the PC during VC state changes --- Firmware/LoRaSerial/States.ino | 4 ++ .../LoRaSerial/Virtual_Circuit_Protocol.h | 7 ++- Firmware/Tools/VcServerTest.c | 44 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/Firmware/LoRaSerial/States.ino b/Firmware/LoRaSerial/States.ino index 977d1e0a..b8139626 100644 --- a/Firmware/LoRaSerial/States.ino +++ b/Firmware/LoRaSerial/States.ino @@ -3049,6 +3049,10 @@ void vcSendPcStateMessage(int8_t vcIndex, uint8_t state) //Build the VC state message VC_STATE_MESSAGE message; message.vcState = state; + if (virtualCircuitList[vcIndex].flags.valid) + memcpy(&message.uniqueId[0], &virtualCircuitList[vcIndex].uniqueId[0], sizeof(message.uniqueId)); + else + memset(message.uniqueId, UNIQUE_ID_ERASE_VALUE, sizeof(message.uniqueId)); //Build the message header VC_SERIAL_MESSAGE_HEADER header; diff --git a/Firmware/LoRaSerial/Virtual_Circuit_Protocol.h b/Firmware/LoRaSerial/Virtual_Circuit_Protocol.h index d16dbb10..d55f1048 100644 --- a/Firmware/LoRaSerial/Virtual_Circuit_Protocol.h +++ b/Firmware/LoRaSerial/Virtual_Circuit_Protocol.h @@ -140,10 +140,15 @@ typedef struct _VC_SERIAL_MESSAGE_HEADER } VC_SERIAL_MESSAGE_HEADER; #define VC_SERIAL_HEADER_BYTES (sizeof(VC_SERIAL_MESSAGE_HEADER)) //Length of the serial VC header in bytes +#define UNIQUE_ID_ERASE_VALUE 0xff +#ifndef UNIQUE_ID_BYTES +#define UNIQUE_ID_BYTES 16 +#endif //UNIQUE_ID_BYTES typedef struct _VC_STATE_MESSAGE { - uint8_t vcState; //VC state + uint8_t vcState; //VC state + uint8_t uniqueId[UNIQUE_ID_BYTES]; //Unique ID for the LoRaSerial radio, all 0xFF if unknown } VC_STATE_MESSAGE; typedef enum diff --git a/Firmware/Tools/VcServerTest.c b/Firmware/Tools/VcServerTest.c index 8ea560fc..a919580d 100644 --- a/Firmware/Tools/VcServerTest.c +++ b/Firmware/Tools/VcServerTest.c @@ -28,6 +28,8 @@ typedef struct _VIRTUAL_CIRCUIT { int vcState; + uint8_t uniqueId[UNIQUE_ID_BYTES]; + bool valid; } VIRTUAL_CIRCUIT; bool commandStatus; @@ -304,6 +306,7 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t length) int newState; int previousState; int srcVc; + uint8_t uniqueId[UNIQUE_ID_BYTES]; VC_STATE_MESSAGE * vcMsg; //Remember the previous state @@ -315,6 +318,47 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t length) newState = vcMsg->vcState; virtualCircuitList[srcVc].vcState = newState; + //Save the LoRaSerial radio's unique ID + //Determine if the PC's value is valid + memset(uniqueId, UNIQUE_ID_ERASE_VALUE, sizeof(uniqueId)); + if (!virtualCircuitList[srcVc].valid) + { + //Determine if the radio knows the value + if (memcmp(vcMsg->uniqueId, uniqueId, sizeof(uniqueId)) != 0) + { + //The radio knows the value, save it in the PC + memcpy(virtualCircuitList[srcVc].uniqueId, vcMsg->uniqueId, sizeof(vcMsg->uniqueId)); + virtualCircuitList[srcVc].valid = true; + + //Display this ID value + printf("VC %d unique ID: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + srcVc, + vcMsg->uniqueId[0], vcMsg->uniqueId[1], vcMsg->uniqueId[2], vcMsg->uniqueId[3], + vcMsg->uniqueId[4], vcMsg->uniqueId[5], vcMsg->uniqueId[6], vcMsg->uniqueId[7], + vcMsg->uniqueId[8], vcMsg->uniqueId[9], vcMsg->uniqueId[10], vcMsg->uniqueId[11], + vcMsg->uniqueId[12], vcMsg->uniqueId[13], vcMsg->uniqueId[14], vcMsg->uniqueId[15]); + } + } + else + { + //Determine if the radio has changed for this VC + if ((memcmp(vcMsg->uniqueId, virtualCircuitList[srcVc].uniqueId, sizeof(vcMsg->uniqueId)) != 0) + && (memcmp(vcMsg->uniqueId, uniqueId, sizeof(uniqueId)) != 0)) + { + //The radio knows the value, save it in the PC + memcpy(virtualCircuitList[srcVc].uniqueId, vcMsg->uniqueId, sizeof(vcMsg->uniqueId)); + virtualCircuitList[srcVc].valid = true; + + //Display this ID value + printf("VC %d unique ID: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + srcVc, + vcMsg->uniqueId[0], vcMsg->uniqueId[1], vcMsg->uniqueId[2], vcMsg->uniqueId[3], + vcMsg->uniqueId[4], vcMsg->uniqueId[5], vcMsg->uniqueId[6], vcMsg->uniqueId[7], + vcMsg->uniqueId[8], vcMsg->uniqueId[9], vcMsg->uniqueId[10], vcMsg->uniqueId[11], + vcMsg->uniqueId[12], vcMsg->uniqueId[13], vcMsg->uniqueId[14], vcMsg->uniqueId[15]); + } + } + //Display the state if requested if (DISPLAY_STATE_TRANSITION) printf("VC%d: %s --> %s\n", srcVc, vcStateNames[previousState], vcStateNames[newState]); From a353daa56d0da00111ce653b97f4eac43785b3c7 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sat, 4 Mar 2023 09:23:58 -1000 Subject: [PATCH 11/12] VC: Change APIs for test routines --- Firmware/Tools/VcServerTest.c | 47 +++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/Firmware/Tools/VcServerTest.c b/Firmware/Tools/VcServerTest.c index a919580d..521b4c17 100644 --- a/Firmware/Tools/VcServerTest.c +++ b/Firmware/Tools/VcServerTest.c @@ -1,4 +1,6 @@ #define ADD_VC_STATE_NAMES_TABLE +#include +#include #include "settings.h" #define BUFFER_SIZE 2048 @@ -76,7 +78,7 @@ int cmdToRadio(uint8_t * buffer, int length) bytesSent = 0; while (bytesSent < length) { - bytesWritten = write(radio, buffer, length); + bytesWritten = write(radio, &buffer[bytesSent], length - bytesSent); if (bytesWritten < 0) { perror("ERROR: Write of data to radio failed!"); @@ -120,7 +122,7 @@ int hostToRadio(uint8_t destVc, uint8_t * buffer, int length) bytesSent = 0; while (bytesSent < length) { - bytesWritten = write(radio, buffer, length); + bytesWritten = write(radio, &buffer[bytesSent], length - bytesSent); if (bytesWritten < 0) { perror("ERROR: Write of data to radio failed!"); @@ -221,7 +223,7 @@ int stdinToRadio() return status; } -int hostToStdout(uint8_t * data, uint8_t bytesToSend) +int hostToStdout(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t bytesToSend) { uint8_t * buffer; uint8_t * bufferEnd; @@ -300,7 +302,7 @@ int hostToStdout(uint8_t * data, uint8_t bytesToSend) return status; } -void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t length) +void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t length) { char cmdBuffer[128]; int newState; @@ -427,7 +429,7 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t length) } } -void radioDataAck(uint8_t * data, uint8_t length) +void radioDataAck(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t length) { VC_DATA_ACK_NACK_MESSAGE * vcMsg; @@ -436,19 +438,26 @@ void radioDataAck(uint8_t * data, uint8_t length) printf("ACK from VC %d\n", vcMsg->msgDestVc); } -void radioDataNack(uint8_t * data, uint8_t length) +void radioDataNack(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t length) { + int vcIndex; VC_DATA_ACK_NACK_MESSAGE * vcMsg; vcMsg = (VC_DATA_ACK_NACK_MESSAGE *)data; + vcIndex = vcMsg->msgDestVc & VCAB_NUMBER_MASK; if (DISPLAY_DATA_NACK) - printf("NACK from VC %d\n", vcMsg->msgDestVc); + printf("NACK from VC %d\n", vcIndex); + + //Set the VC state to down + virtualCircuitList[vcIndex].vcState = VC_STATE_LINK_DOWN; } -void radioCommandComplete(uint8_t srcVc, uint8_t * data, uint8_t length) +void radioCommandComplete(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t length) { VC_COMMAND_COMPLETE_MESSAGE * vcMsg; + uint8_t srcVc; + srcVc = header->radio.srcVc; vcMsg = (VC_COMMAND_COMPLETE_MESSAGE *)data; if (DISPLAY_COMMAND_COMPLETE) printf("Command complete from VC %d: %s\n", srcVc, @@ -505,7 +514,7 @@ int radioToHost() length = data - dataStart; if (length) //Output the debug data - hostToStdout(dataStart, length); + hostToStdout(NULL, dataStart, length); //Determine if this is the beginning of a virtual circuit message length = dataEnd - data; @@ -545,8 +554,8 @@ int radioToHost() { printf("VC Header:\n"); printf(" length: %d\n", header->radio.length); - printf(" destVc: %d\n", header->radio.destVc); - printf(" srcVc: %d\n", header->radio.srcVc); + printf(" destVc: %d (0x%02x)\n", (uint8_t)header->radio.destVc, (uint8_t)header->radio.destVc); + printf(" srcVc: %d (0x%02x)\n", header->radio.srcVc, header->radio.srcVc); if (length > 0) dumpBuffer(data, length); } @@ -557,29 +566,29 @@ int radioToHost() //Display link status if (header->radio.destVc == PC_LINK_STATUS) - radioToPcLinkStatus(header, VC_SERIAL_HEADER_BYTES + length); + radioToPcLinkStatus(header, data, VC_SERIAL_HEADER_BYTES + length); //Display remote command response else if (header->radio.destVc == (PC_REMOTE_RESPONSE | myVc)) - status = hostToStdout(data, length); + status = hostToStdout(header, data, length); //Display command completion status else if (header->radio.destVc == PC_COMMAND_COMPLETE) - radioCommandComplete(header->radio.srcVc, data, length); + radioCommandComplete(header, data, length); //Display ACKs for transmitted messages else if (header->radio.destVc == PC_DATA_ACK) - radioDataAck(data, length); + radioDataAck(header, data, length); //Display NACKs for transmitted messages else if (header->radio.destVc == PC_DATA_NACK) - radioDataNack(data, length); + radioDataNack(header, data, length); //Display received messages else if ((header->radio.destVc == myVc) || (header->radio.destVc == VC_BROADCAST)) { //Output this message - status = hostToStdout(data, length); + status = hostToStdout(header, data, length); } //Unknown messages @@ -589,8 +598,8 @@ int radioToHost() { printf("Unknown message, VC Header:\n"); printf(" length: %d\n", header->radio.length); - printf(" destVc: %d\n", header->radio.destVc); - printf(" srcVc: %d\n", header->radio.srcVc); + printf(" destVc: %d (0x%02x)\n", (uint8_t)header->radio.destVc, (uint8_t)header->radio.destVc); + printf(" srcVc: %d (0x%02x)\n", header->radio.srcVc, header->radio.srcVc); if (length > 0) dumpBuffer(data, length); } From ebe7dfc307faa1a2da21c09ded75d159e65bc4e1 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sat, 11 Mar 2023 10:11:18 -1000 Subject: [PATCH 12/12] VcServerTest: Update the command processing --- Firmware/Tools/VcServerTest.c | 784 ++++++++++++++++++++++++++++++++-- 1 file changed, 738 insertions(+), 46 deletions(-) diff --git a/Firmware/Tools/VcServerTest.c b/Firmware/Tools/VcServerTest.c index 521b4c17..28de675f 100644 --- a/Firmware/Tools/VcServerTest.c +++ b/Firmware/Tools/VcServerTest.c @@ -3,19 +3,33 @@ #include #include "settings.h" -#define BUFFER_SIZE 2048 -#define INPUT_BUFFER_SIZE BUFFER_SIZE -#define MAX_MESSAGE_SIZE 32 -#define STDIN 0 -#define STDOUT 1 - -#define BREAK_LINKS_COMMAND "atb" -#define GET_MY_VC_ADDRESS "ati30" -#define GET_VC_STATUS "ata" -#define LINK_RESET_COMMAND "atz" -#define MY_VC_ADDRESS "myVc: " +#ifndef POLL_TIMEOUT_USEC +#define POLL_TIMEOUT_USEC 1000 +#endif // POLL_TIMEOUT_USEC + +#define ONE_SECOND_COUNT 20 // (1000000 / POLL_TIMEOUT_USEC) +#define COMMAND_POLL_COUNT (ONE_SECOND_COUNT / 10) //100 mSec +#define STALL_CHECK_COUNT 15 * ONE_SECOND_COUNT //15 Sec + +#define BUFFER_SIZE 2048 +#define INPUT_BUFFER_SIZE BUFFER_SIZE +#define MAX_MESSAGE_SIZE 32 +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 + +#define BREAK_LINKS_COMMAND "atb" +#define GET_DEVICE_INFO "ati" +#define GET_MY_VC_ADDRESS "ati30" +#define GET_UNIQUE_ID "ati8" +#define GET_VC_STATE "ati12" +#define GET_VC_STATUS "ata" +#define LINK_RESET_COMMAND "atz" +#define MY_VC_ADDRESS "myVc: " +#define START_3_WAY_HANDSHAKE "atc" #define DEBUG_LOCAL_COMMANDS 0 +#define DEBUG_PC_CMD_ISSUE 0 #define DEBUG_PC_TO_RADIO 0 #define DEBUG_RADIO_TO_PC 0 #define DISPLAY_COMMAND_COMPLETE 0 @@ -27,23 +41,175 @@ #define DUMP_RADIO_TO_PC 0 #define SEND_ATC_COMMAND 1 +#define QUEUE_T uint32_t +#define QUEUE_T_BITS ((int)(sizeof(QUEUE_T) * 8)) +#define QUEUE_T_MASK (QUEUE_T_BITS - 1) +#define COMMAND_QUEUE_SIZE ((CMD_LIST_SIZE + QUEUE_T_MASK) / QUEUE_T_BITS) + +#define SECS_IN_MINUTE 60 +#define SECS_IN_HOUR (60 * SECS_IN_MINUTE) +#define SECS_IN_DAY (24 * SECS_IN_HOUR) + +#define MILLIS_IN_SECOND 1000 +#define MILLIS_IN_MINUTE (60 * MILLIS_IN_SECOND) +#define MILLIS_IN_HOUR (60 * MILLIS_IN_MINUTE) +#define MILLIS_IN_DAY (24 * MILLIS_IN_HOUR) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define COMMAND_COMPLETE(queue, active) \ +{ \ + if (COMMAND_PENDING(queue, active)) \ + { \ + queue[active / QUEUE_T_BITS] &= ~(1 << (active & QUEUE_T_MASK)); \ + active = CMD_LIST_SIZE; \ + } \ +} + +#define COMMAND_ISSUE(queue, pollCount, cmd) \ +{ \ + /* Place the command in the queue */ \ + queue[cmd / QUEUE_T_BITS] |= 1 << (cmd & QUEUE_T_MASK); \ + \ + /* Timeout the command processor */ \ + if (!commandProcessorRunning) \ + commandProcessorRunning = STALL_CHECK_COUNT; \ + \ + /* Remember when this command was issued */ \ + if (!pollCount) \ + { \ + if (timeoutCount) \ + pollCount = timeoutCount; \ + else \ + pollCount = 1; \ + } \ +} + +#define COMMAND_PENDING(queue,cmd) ((queue[cmd / QUEUE_T_BITS] >> (cmd & QUEUE_T_MASK)) & 1) + +typedef enum +{ + //List commands in priority order + CMD_ATI11 = 0, //Get myVC + CMD_ATB, //Break all VC links + CMD_ATA, //Get VC status + CMD_AT_CMDVC, //Select target VC + CMD_ATC, //Start the 3-way handshake + CMD_WAIT_CONNECTED, //Wait until the client is connected + CMD_AT_CMDVC_2, //Select target VC + CMD_ATI12, //Get the VC state + CMD_GET_SERVER_MODEL, //Get the model name + CMD_GET_CLIENT_MODEL, //Get the model name + CMD_ATI, //Get the device type + CMD_ATI8, //Get the radio's unique ID + CMD_AT_DISABLE_CONTROLLER, //Disable the sprinkler controller + CMD_AT_DAY_OF_WEEK, //Set the day of week + CMD_AT_TIME_OF_DAY, //Set the time of day + CMD_ATI89, //Display the date and time + + //Select the solenoids for each of the zone + CMD_SELECT_ZONE, //Select the zone number + CMD_SELECT_SOLENOID, //Select the solenoid for the zone + COMPLETE_ZONE_CONFIGURATION,//Verify that all of the zones are configured + + //Set the start times + CMD_SET_DAY_OF_WEEK, //Set the day of the week + CMD_SET_START_TIME, //Set the start time + CMD_SET_ALL_START_TIMES, //Verify that all of the start times are set + + //Set the zone durations + CMD_SET_DAY_OF_WEEK_2, //Set the day of the week + CMD_SELECT_ZONE_2, //Select the zone number + CMD_SET_ZONE_DURATION, //Set the zone on duration + CMD_SET_ALL_DURATIONS, //Verify that all of the durations are set + +/* +#define SET_MANUAL_ON "AT-ZoneManualOn=" +#define SET_ZONE_DURATION "AT-ZoneDuration=" +*/ + + //Last in the list + CMD_LIST_SIZE +} COMMANDS; + +const char * const commandName[] = +{ + "ATI11", "ATIB", "ATA", "AT-CMDVC", "ATC", "WAIT_CONNECT", "AT-CMDVC_2", "ATI12", + "ATI93", "ATI81", "ATI", "ATI8", "AT-EnableController=0", + "AT-DayOfWeek", "AT-TimeOfDay", "ATI89", + "AT-CommandZone", "AT-LatchingSolenoid", "COMPLETE_ZONE_CONFIGURATION", + "AT-CommandDay", "AT-StartTime", "CMD_SET_ALL_START_TIMES", + "AT-CommandDay-2", "AT-CommandZone-2", "AT-ZoneDuration", "CMD_SET_ALL_DURATIONS", +}; + + typedef struct _VIRTUAL_CIRCUIT { int vcState; + uint32_t activeCommand; + QUEUE_T commandQueue[COMMAND_QUEUE_SIZE]; + uint32_t commandTimer; uint8_t uniqueId[UNIQUE_ID_BYTES]; bool valid; } VIRTUAL_CIRCUIT; +uint32_t commandProcessorRunning; bool commandStatus; bool findMyVc; -int myVc = VC_UNASSIGNED; -int remoteVc; uint8_t inputBuffer[INPUT_BUFFER_SIZE]; +int myVc = VC_SERVER; uint8_t outputBuffer[VC_SERIAL_HEADER_BYTES + BUFFER_SIZE]; -int timeoutCount; +uint32_t pcActiveCommand = CMD_LIST_SIZE; +char pcCommandBuffer[128]; +uint8_t pcCommandQueue[COMMAND_QUEUE_SIZE]; +uint32_t pcCommandTimer; +int pcCommandVc = MAX_VC; +uint8_t remoteCommandVc; +int remoteVc; +uint32_t timeoutCount; +char vcCommandBuffer[MAX_VC][128]; VIRTUAL_CIRCUIT virtualCircuitList[MAX_VC]; volatile bool waitingForCommandComplete; -uint8_t remoteCommandVc; + +void dumpBuffer(uint8_t * data, int length) +{ + char byte; + int bytes; + uint8_t * dataEnd; + uint8_t * dataStart; + const int displayWidth = 16; + int index; + + dataStart = data; + dataEnd = &data[length]; + while (data < dataEnd) + { + // Display the offset + printf(" 0x%02x: ", (unsigned int)(data - dataStart)); + + // Determine the number of bytes to display + bytes = dataEnd - data; + if (bytes > displayWidth) + bytes = displayWidth; + + // Display the data bytes in hex + for (index = 0; index < bytes; index++) + printf(" %02x", *data++); + + // Space over to the ASCII display + for (; index < displayWidth; index++) + printf(" "); + printf(" "); + + // Display the ASCII bytes + data -= bytes; + for (index = 0; index < bytes; index++) { + byte = *data++; + printf("%c", ((byte < ' ') || (byte >= 0x7f)) ? '.' : byte); + } + printf("\n"); + } +} int cmdToRadio(uint8_t * buffer, int length) { @@ -258,8 +424,8 @@ int hostToStdout(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t byte myVc = (int8_t)vcNumber; printf("myVc: %d\n", myVc); - //Request the status for all of the VCs - cmdToRadio((uint8_t *)GET_VC_STATUS, strlen(GET_VC_STATUS)); + //Complete this command + COMMAND_COMPLETE(pcCommandQueue, pcActiveCommand); break; } } @@ -304,7 +470,7 @@ int hostToStdout(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t byte void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t length) { - char cmdBuffer[128]; + int durationBase; int newState; int previousState; int srcVc; @@ -367,11 +533,15 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint switch (newState) { default: + if (DEBUG_PC_CMD_ISSUE) + printf("VC %d unknown state!\n", srcVc); if (DISPLAY_VC_STATE) printf("------- VC %d State %3d ------\n", srcVc, vcMsg->vcState); break; case VC_STATE_LINK_DOWN: + if (DEBUG_PC_CMD_ISSUE) + printf("VC %d DOWN\n", srcVc); if (DISPLAY_VC_STATE) printf("--------- VC %d DOWN ---------\n", srcVc); break; @@ -379,16 +549,14 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint case VC_STATE_LINK_ALIVE: //Upon transition to ALIVE, if is the server or the source VC matches the //target VC or myVc, bring up the connection - if (SEND_ATC_COMMAND && (previousState != newState) + if ((previousState != newState) && ((myVc == VC_SERVER) || (srcVc == remoteVc) || (srcVc == myVc))) { - //Select the VC to use - sprintf(cmdBuffer, "at-CmdVc=%d", srcVc); - cmdToRadio((uint8_t *)cmdBuffer, strlen(cmdBuffer)); - - //Bring up the VC connection to this remote system - strcpy(cmdBuffer, "atC"); - cmdToRadio((uint8_t *)cmdBuffer, strlen(cmdBuffer)); + if (DEBUG_PC_CMD_ISSUE) + printf("VC %d ALIVE\n", srcVc); + COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, CMD_AT_CMDVC); + COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, CMD_ATC); + COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, CMD_WAIT_CONNECTED); } if (DISPLAY_VC_STATE) @@ -396,16 +564,22 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint break; case VC_STATE_SEND_UNKNOWN_ACKS: + if (DEBUG_PC_CMD_ISSUE) + printf("VC %d SEND_UNKNOWN_ACKS\n", srcVc); if (DISPLAY_VC_STATE) printf("-=--=-- VC %d ALIVE UA --=--=-\n", srcVc); break; case VC_STATE_WAIT_SYNC_ACKS: + if (DEBUG_PC_CMD_ISSUE) + printf("VC %d WAIT_SYNC_ACKS\n", srcVc); if (DISPLAY_VC_STATE) printf("-=--=-- VC %d ALIVE SA --=--=-\n", srcVc); break; case VC_STATE_WAIT_ZERO_ACKS: + if (DEBUG_PC_CMD_ISSUE) + printf("VC %d WAIT_ZERO_ACKS\n", srcVc); if (DISPLAY_VC_STATE) { if (previousState == VC_STATE_CONNECTED) @@ -415,6 +589,28 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint break; case VC_STATE_CONNECTED: + if (DEBUG_PC_CMD_ISSUE) + printf("VC %d CONNECTED\n", srcVc); + if (myVc == VC_SERVER) + { + if (virtualCircuitList[srcVc].activeCommand == CMD_ATC) + COMMAND_COMPLETE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].activeCommand); + + //Get the device information + if (srcVc == VC_SERVER) + { + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI); + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI8); + } + else + { + COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, CMD_AT_CMDVC_2); + COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, CMD_ATI12); + COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, CMD_ATI); + COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, CMD_ATI8); + } + } + COMMAND_COMPLETE(pcCommandQueue, pcActiveCommand); if (DISPLAY_VC_STATE) printf("======= VC %d CONNECTED ======\n", srcVc); break; @@ -440,6 +636,7 @@ void radioDataAck(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t len void radioDataNack(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t length) { + int index; int vcIndex; VC_DATA_ACK_NACK_MESSAGE * vcMsg; @@ -448,6 +645,14 @@ void radioDataNack(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t le if (DISPLAY_DATA_NACK) printf("NACK from VC %d\n", vcIndex); + //Clear the command queue for this VC + for (index = 0; index < COMMAND_QUEUE_SIZE; index++) + virtualCircuitList[vcIndex].commandQueue[index] = 0; + virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; + + //Stop the command timer + virtualCircuitList[vcIndex].commandTimer = 0; + //Set the VC state to down virtualCircuitList[vcIndex].vcState = VC_STATE_LINK_DOWN; } @@ -457,7 +662,33 @@ void radioCommandComplete(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uin VC_COMMAND_COMPLETE_MESSAGE * vcMsg; uint8_t srcVc; + //The command processor is still running + commandProcessorRunning = STALL_CHECK_COUNT; + + //Done with this command srcVc = header->radio.srcVc; + if (srcVc == myVc) + { + if (pcActiveCommand < CMD_LIST_SIZE) + { + if (pcCommandVc < MAX_VC) + { + COMMAND_COMPLETE(virtualCircuitList[pcCommandVc].commandQueue, virtualCircuitList[pcCommandVc].activeCommand); + } + COMMAND_COMPLETE(pcCommandQueue, pcActiveCommand); + } + else if (virtualCircuitList[pcCommandVc].activeCommand < CMD_LIST_SIZE) + { + //This was a VC command + COMMAND_COMPLETE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].activeCommand); + } + } + else + { + //This was a VC command + COMMAND_COMPLETE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].activeCommand); + } + vcMsg = (VC_COMMAND_COMPLETE_MESSAGE *)data; if (DISPLAY_COMMAND_COMPLETE) printf("Command complete from VC %d: %s\n", srcVc, @@ -613,25 +844,294 @@ int radioToHost() return status; } -int -main ( - int argc, - char ** argv -) +void issuePcCommands() +{ + int cmd; + int index; + + //Wait until this command completes + if (pcActiveCommand >= CMD_LIST_SIZE) + { + for (index = 0; index < CMD_LIST_SIZE; index += QUEUE_T_BITS) + { + if (pcCommandQueue[index / QUEUE_T_BITS]) + { + for (cmd = index; (cmd < CMD_LIST_SIZE) && (cmd < (index + QUEUE_T_BITS)); cmd++) + { + if (COMMAND_PENDING(pcCommandQueue, cmd)) + { + pcActiveCommand = cmd; + switch (cmd) + { + case CMD_ATB: //Break all of the VC links + if (DEBUG_PC_CMD_ISSUE) + printf("Issuing ATB command\n"); + cmdToRadio((uint8_t *)BREAK_LINKS_COMMAND, strlen(BREAK_LINKS_COMMAND)); + return; + + case CMD_ATI11: //Get myVC + if (myVc == VC_UNASSIGNED) + { + //Get myVc address + if (DEBUG_PC_CMD_ISSUE) + printf("Issuing ATI11 command\n"); + findMyVc = true; + cmdToRadio((uint8_t *)GET_MY_VC_ADDRESS, strlen(GET_MY_VC_ADDRESS)); + return; + } + if (DEBUG_PC_CMD_ISSUE) + printf("Skipping ATI11 command, myVC already known\n"); + COMMAND_COMPLETE(pcCommandQueue, pcActiveCommand); + break; + + case CMD_ATA: //Get all the VC states + if (DEBUG_PC_CMD_ISSUE) + printf("Issuing ATA command\n"); + cmdToRadio((uint8_t *)GET_VC_STATUS, strlen(GET_VC_STATUS)); + return; + + case CMD_AT_CMDVC: //Select the VC to target + case CMD_AT_CMDVC_2: + //Select the VC to use + sprintf(pcCommandBuffer, "at-CmdVc=%d", pcCommandVc); + if (DEBUG_PC_CMD_ISSUE) + printf("Issuing %s command\n", pcCommandBuffer); + cmdToRadio((uint8_t *)pcCommandBuffer, strlen(pcCommandBuffer)); + return; + + case CMD_ATC: //Perform the 3-way handshake + //Bring up the VC connection to this remote system + if (DEBUG_PC_CMD_ISSUE) + printf("Issuing ATC command\n"); + cmdToRadio((uint8_t *)START_3_WAY_HANDSHAKE, strlen(START_3_WAY_HANDSHAKE)); + return; + + case CMD_ATI12: + //Get the VC state + if (DEBUG_PC_CMD_ISSUE) + printf("Issuing ATI12 command\n"); + cmdToRadio((uint8_t *)GET_VC_STATE, strlen(GET_VC_STATE)); + return; + + case CMD_ATI: + if (DEBUG_PC_CMD_ISSUE) + printf("Issuing ATI command\n"); + cmdToRadio((uint8_t *)GET_DEVICE_INFO, strlen(GET_DEVICE_INFO)); + return; + + case CMD_ATI8: + if (DEBUG_PC_CMD_ISSUE) + printf("Issuing ATI8 command\n"); + cmdToRadio((uint8_t *)GET_UNIQUE_ID, strlen(GET_UNIQUE_ID)); + return; + } + } + } + } + } + pcActiveCommand = CMD_LIST_SIZE; + } +} + +int sendVcCommand(const char * commandString, int destVc) +{ + int bytesSent; + int bytesWritten; + VC_SERIAL_MESSAGE_HEADER header; + int length; + + //Build the virtual circuit serial header + length = strlen(commandString); + header.start = START_OF_VC_SERIAL; + header.radio.length = VC_RADIO_HEADER_BYTES + length; + header.radio.destVc = PC_REMOTE_COMMAND | destVc; + header.radio.srcVc = myVc; + + //Display the data being sent to the radio + if (DEBUG_PC_TO_RADIO) + { + dumpBuffer((uint8_t *)&header, VC_SERIAL_HEADER_BYTES); + dumpBuffer((uint8_t *)commandString, length); + } + if (DEBUG_LOCAL_COMMANDS) + printf("Sending LoRaSerial command: %s\n", commandString); + if (DEBUG_PC_CMD_ISSUE) + printf("Sending %s command to VC %d\n", commandString, destVc); + + //Send the header + bytesWritten = write(radio, (uint8_t *)&header, VC_SERIAL_HEADER_BYTES); + if (bytesWritten < (int)VC_SERIAL_HEADER_BYTES) + { + perror("ERROR: Write of header to radio failed!"); + return -1; + } + + //Send the message + bytesSent = 0; + while (bytesSent < length) + { + bytesWritten = write(radio, &commandString[bytesSent], length - bytesSent); + if (bytesWritten < 0) + { + perror("ERROR: Write of data to radio failed!"); + return bytesWritten; + } + bytesSent += bytesWritten; + } + + //Return the amount of the buffer that was sent + return length; +} + +bool commandProcessorBusy() +{ + bool busy; + int index; + + //Determine if the command processor is using the VC + busy = (pcActiveCommand < CMD_LIST_SIZE); + for (index = 0; index < COMMAND_QUEUE_SIZE; index++) + if (pcCommandQueue[index]) + { + busy = 1; + break; + } + return busy; +} + +bool commandProcessorIdle(int vcIndex) +{ + bool idle; + + //Determine if the command processor is using the VC + idle = !commandProcessorBusy(); + if (idle) + //Set the command VC + pcCommandVc = vcIndex; + + //Return the idle status + return idle; +} + +bool issueVcCommands(int vcIndex) +{ + int cmd; + uint32_t dayBit; + int dayIndex; + int entry; + int index; + time_t now; + struct tm * timeStruct; + uint32_t zoneBit; + int zoneIndex; + + if (virtualCircuitList[vcIndex].activeCommand >= CMD_LIST_SIZE) + { + for (index = 0; index < CMD_LIST_SIZE; index += QUEUE_T_BITS) + { + if (virtualCircuitList[vcIndex].commandQueue[index / QUEUE_T_BITS]) + { + for (cmd = index; (cmd < CMD_LIST_SIZE) && (cmd < (index + QUEUE_T_BITS)); cmd++) + { + if (COMMAND_PENDING(virtualCircuitList[vcIndex].commandQueue, cmd)) + { + virtualCircuitList[vcIndex].activeCommand = cmd; + switch (cmd) + { + case CMD_AT_CMDVC: + //Determine if the local command processor is idle + if (commandProcessorIdle(vcIndex)) + { + if (DEBUG_PC_CMD_ISSUE) + printf("Migrating AT-CMDVC=%d and ATC commands to PC command queue\n", vcIndex); + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_AT_CMDVC); + if (COMMAND_PENDING(virtualCircuitList[vcIndex].commandQueue, CMD_ATC)) + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATC); + return true; + } + virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; + return true; + + case CMD_ATC: + return true; + + case CMD_WAIT_CONNECTED: + if ((virtualCircuitList[vcIndex].vcState != VC_STATE_CONNECTED) + || commandProcessorBusy(vcIndex)) + { + //Mark the list as empty to allow this entry to be executed again + virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; + return true; + } + + //The wait is complete, this VC is now connected to the sprinkler server + COMMAND_COMPLETE(virtualCircuitList[vcIndex].commandQueue, + virtualCircuitList[vcIndex].activeCommand); + + //Get the VC state + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_AT_CMDVC_2); + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI12); + break; + + case CMD_AT_CMDVC_2: + //Determine if the local command processor is idle + if (commandProcessorIdle(vcIndex)) + { + if (DEBUG_PC_CMD_ISSUE) + printf("Migrating AT-CMDVC and ATI12 commands to PC command queue\n"); + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_AT_CMDVC_2); + if (COMMAND_PENDING(virtualCircuitList[vcIndex].commandQueue, CMD_ATI12)) + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI12); + return true; + } + virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; + return true; + + case CMD_ATI12: + return true; + + case CMD_ATI: + sendVcCommand(GET_DEVICE_INFO, vcIndex); + return true; + + case CMD_ATI8: + sendVcCommand(GET_UNIQUE_ID, vcIndex); + return true; + } + } + } + virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; + } + } + } + return false; +} + +int main(int argc, char **argv) { bool breakLinks; + int cmd; fd_set currentfds; + bool displayTitle; int maxfds; int numfds; bool reset; int status; char * terminal; struct timeval timeout; + int vcIndex; maxfds = STDIN; status = 0; do { + //Verify the command table length + if (ARRAY_SIZE(commandName) != CMD_LIST_SIZE) + { + fprintf(stderr, "ERROR: Fix commandName length of %d != %d\n", (uint32_t)ARRAY_SIZE(commandName), CMD_LIST_SIZE); + return -1; + } + //Display the help text if necessary if (argc < 3) { @@ -706,10 +1206,18 @@ main ( sleep(2); } + //Initialize the virtual circuits + for (vcIndex = 0; vcIndex < MAX_VC; vcIndex++) + virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; + + //Perform the initialization commands + pcCommandTimer = 1; + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI11); //Get myVC + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATA); //Get all the VC states + //Break the links if requested - findMyVc = true; if (breakLinks) - cmdToRadio((uint8_t *)BREAK_LINKS_COMMAND, strlen(BREAK_LINKS_COMMAND)); + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATB); //Break all the VC links //Initialize the fd_sets if (maxfds < radio) @@ -722,7 +1230,7 @@ main ( { //Set the timeout timeout.tv_sec = 0; - timeout.tv_usec = 1000; + timeout.tv_usec = POLL_TIMEOUT_USEC; //Wait for receive data or timeout memcpy((void *)¤tfds, (void *)&readfds, sizeof(readfds)); @@ -734,18 +1242,6 @@ main ( break; } - //Check for timeout - if ((numfds == 0) && (timeoutCount++ >= 1000)) - { - timeoutCount = 0; - if (myVc == VC_UNASSIGNED) - { - //Get myVc address - findMyVc = true; - cmdToRadio((uint8_t *)GET_MY_VC_ADDRESS, strlen(GET_MY_VC_ADDRESS)); - } - } - //Determine if console input is available if (FD_ISSET(STDIN, ¤tfds)) { @@ -762,7 +1258,203 @@ main ( if (status) break; } + + //---------------------------------------- + // Check for timeout + //---------------------------------------- + + if (numfds == 0) + { + timeoutCount++; + + //Check for time to process PC commands + if (pcCommandTimer && (pcActiveCommand >= CMD_LIST_SIZE) + && ((timeoutCount - pcCommandTimer) >= COMMAND_POLL_COUNT)) + { + pcCommandTimer = timeoutCount; + if (!pcCommandTimer) + pcCommandTimer = 1; + issuePcCommands(); + } + + //Check for time to process VC commands + for (vcIndex = 0; vcIndex < MAX_VC; vcIndex++) + { + if (virtualCircuitList[vcIndex].commandTimer + && ((timeoutCount - virtualCircuitList[vcIndex].commandTimer) >= COMMAND_POLL_COUNT)) + { + virtualCircuitList[vcIndex].commandTimer = timeoutCount; + if (!virtualCircuitList[vcIndex].commandTimer) + virtualCircuitList[vcIndex].commandTimer = 1; + if (virtualCircuitList[vcIndex].activeCommand < CMD_LIST_SIZE) + break; + if (issueVcCommands(vcIndex)) + { + continue; +// break; + } + } + } + + //---------------------------------------- + // Check the resource usage + //---------------------------------------- + + if (!(timeoutCount % (ONE_SECOND_COUNT * 60))) + { + struct rusage usage; + int s_days; + int s_hours; + int s_mins; + int s_secs; + uint64_t s_millis; + int u_days; + int u_hours; + int u_mins; + int u_secs; + uint64_t u_millis; + + //Get the sprinkler server resource useage + getrusage(RUSAGE_SELF, &usage); + u_millis = usage.ru_utime.tv_usec / 1000; + u_secs = usage.ru_utime.tv_sec; + u_days = u_secs / SECS_IN_DAY; + u_secs -= u_days * SECS_IN_DAY; + u_hours = u_secs / SECS_IN_HOUR; + u_secs -= u_hours * SECS_IN_HOUR; + u_mins = u_secs / SECS_IN_MINUTE; + u_secs -= u_mins * SECS_IN_MINUTE; + + s_millis = usage.ru_stime.tv_usec / 1000; + s_secs = usage.ru_stime.tv_sec; + s_days = s_secs / SECS_IN_DAY; + s_secs -= s_days * SECS_IN_DAY; + s_hours = s_secs / SECS_IN_HOUR; + s_secs -= s_hours * SECS_IN_HOUR; + s_mins = s_secs / SECS_IN_MINUTE; + s_secs -= s_mins * SECS_IN_MINUTE; + + printf("K: %d:%02d:%02d.%03d, U: %d:%02d:%02d.%03d, Mem: %ld, PF: %ld: IN: %ld, OUT: %ld\n", + u_hours, u_mins, u_secs, (int)u_millis, + s_hours, s_mins, s_secs, (int)s_millis, + usage.ru_maxrss, usage.ru_majflt, usage.ru_inblock, usage.ru_oublock); + +/* + getrusage(RUSAGE_CHILDREN, &usage); + u_millis = usage.ru_utime.tv_usec / 1000; + u_secs = usage.ru_utime.tv_sec; + u_days = u_secs / SECS_IN_DAY; + u_secs -= u_days * SECS_IN_DAY; + u_hours = u_secs / SECS_IN_HOUR; + u_secs -= u_hours * SECS_IN_HOUR; + u_mins = u_secs / SECS_IN_MINUTE; + u_secs -= u_mins * SECS_IN_MINUTE; + + s_millis = usage.ru_stime.tv_usec / 1000; + s_secs = usage.ru_stime.tv_sec; + s_days = s_secs / SECS_IN_DAY; + s_secs -= s_days * SECS_IN_DAY; + s_hours = s_secs / SECS_IN_HOUR; + s_secs -= s_hours * SECS_IN_HOUR; + s_mins = s_secs / SECS_IN_MINUTE; + s_secs -= s_mins * SECS_IN_MINUTE; + + printf("K: %d:%02d:%02d.%03d, U: %d:%02d:%02d.%03d, Mem: %ld, PF: %ld: IN: %ld, OUT: %ld\n", + u_hours, u_mins, u_secs, (int)u_millis, + s_hours, s_mins, s_secs, (int)s_millis, + usage.ru_maxrss, usage.ru_majflt, usage.ru_inblock, usage.ru_oublock); +*/ + } + } + + //---------------------------------------- + // Check for stalled commands + //---------------------------------------- + + //Deterine if the command processor is running + if (commandProcessorRunning) + { + //Determine if it is time to check for a command processor stall + if (commandProcessorRunning) + commandProcessorRunning--; + if (!commandProcessorRunning) + { + //The command processor is either stalled or complete + //Determine if there are any outsanding commands to be processed + displayTitle = true; + for (int cmdBlock = 0; cmdBlock < COMMAND_QUEUE_SIZE; cmdBlock++) + { + if (pcCommandQueue[cmdBlock]) + { + //Display the next outstanding command + int cmdBase = cmdBlock * QUEUE_T_BITS; + for (cmd = cmdBase; cmd < (cmdBase + QUEUE_T_BITS); cmd++) + { + if (COMMAND_PENDING(pcCommandQueue, cmd)) + { + if (displayTitle) + { + displayTitle = false; + printf("Stalled commands:\n"); + } + printf(" PC: %d (%s, %s)\n", + cmd, + commandName[cmd], + (pcActiveCommand < CMD_LIST_SIZE) ? "Active" : "Pending"); + break; + } + } + + //Check for a stalled command + if (cmd < (cmdBase + QUEUE_T_BITS)) + break; + } + } + + //Determine if the local radio command queue is idle + if (displayTitle) + printf("PC: Idle\n"); + + //Determine if there are any outstanding VC commands + for (vcIndex = 0; vcIndex < MAX_VC; vcIndex++) + { + for (int cmdBlock = 0; cmdBlock < COMMAND_QUEUE_SIZE; cmdBlock++) + { + if (virtualCircuitList[vcIndex].commandQueue[cmdBlock]) + { + //Display the next outstanding command + int cmdBase = cmdBlock * QUEUE_T_BITS; + for (cmd = cmdBase; cmd < (cmdBase + QUEUE_T_BITS); cmd++) + { + if (COMMAND_PENDING(virtualCircuitList[vcIndex].commandQueue, cmd)) + { + if (displayTitle) + { + displayTitle = false; + printf("Stalled commands:\n"); + } + printf(" VC %d (%s): %d (%s, %s)\n", + vcIndex, + vcStateNames[virtualCircuitList[vcIndex].vcState], + cmd, + commandName[cmd], + (virtualCircuitList[vcIndex].activeCommand < CMD_LIST_SIZE) ? "Active" : "Pending"); + break; + } + } + + //Check for a stalled command + if (cmd < (cmdBase + QUEUE_T_BITS)) + break; + } + } + } + } + } } } while (0); + + //Done with the radio + close(radio); return status; }