가장 많이 본 글

이 블로그 검색

2018년 6월 8일 금요일

Processing(Interpreting) commands


Processing(Interpreting) commands

After getting hashcode, then processing commands is very simple.
Firstly, check the rx buffer integrity if some errors exist. Then use switch statement to identify command and then clear buffer for the next. Below are the example. Call CommandProcess() from main().

Prepare flags and enumerations for the Serial port.

/* Defines status struct */
struct rsMap{
    // RS232 flags
    unsigned tx_in_progress : 1; // holding high until tx ends
    unsigned rx_in_progress : 1; // holding high until rx terminates by timeout
    unsigned command_received : 1; // set if rx buf has a command
    unsigned command_reserved : 1; // set if a reporting process is progressing
    unsigned comm_error : 1; // set if there's an error in command
    unsigned emergency : 1; // set if emergency rxed
    unsigned remote_on : 1; // set if remote command rxed
    unsigned comm_test : 1; // set if test needed

    unsigned no_response_req : 1; // set if either unknown command or other's data
    unsigned tx_disable : 1; // set if the last data has been written to tx buf
    // in uart, then tx_enable will be disabled by the
    // timer after about 3 mS
unsigned rxResponse : 3; /* expansion of no_response_req */
};

struct rsMap rs;
enum{
    comm1, comm2, comm3, comm4
} cmdSource;

enum{
    noResp, sendACK, sendNAK, sendStatus

} rxResponse;

/* Check received command format */

uint8 checkCmdFormat(void) {

    uint16 i, argCnt;

    if(!rs.comm_error) // Check Communication format error
    {
        /* check command error. identify no response. command error included. */

        rs.no_response_req = 0;

        /* when exceed max_cmd_len. */
        /* rx_count has char count received. */
        if(rx_count > max_cmd_len) rs.no_response_req = 1; // no response for length mismatch.

        // comment the following 2 lines for none cr/lf termination command
        if((rx_buf[rx_count - 1] != cr) && (rx_buf[rx_count - 1] != lf)) rs.no_response_req = 1;
        if((rx_buf[rx_count - 2] != cr) && (rx_buf[rx_count - 2] != lf)) rs.no_response_req = 1;

        // check if there's a non ASCII code
        for(i = 0; i <= rx_count - 3; i++) {
            if(rx_buf[i] < 0x20 || rx_buf[i] > 0x7f) {
                rs.no_response_req = 1;
                break;
            }
        }
        // command code mismatch causes no response.

        /* Extract each command string */
        if(!rs.comm_error && !rs.no_response_req) argCnt = cmdExtractor();

    }
    return argCnt;
}

void convToUpperCaseString(char *sPtr) {
    while(*sPtr != '\0') {
        if(islower(*sPtr)) *sPtr = toupper(*sPtr);
        sPtr++;
    }
}

void CommandProcess(void) {

    uint16 i, cmdOffset, argCnt;
    uint16 l;
    int iTemp;
    float f1, f2;

    /* Check command format and argument list before processing */
    argCnt = checkCmdFormat(); 

    if(!rs.comm_error && !rs.no_response_req) // do normal process
    {

        cmdOffset = cmdOffset; // set command string offset
        rs.rxResponse = noResp; /* Disable response */

        uint32 cmdCode[15];

            
        /* Command processing  ----------------------------------*/

        /* Get all hashcode from the received command -------*/
        if(argCnt > 10) argCnt = 10;
        /* Ptn download has big argument count */
        for(i = 0; i <= argCnt - 1; i++) {
            /* Convert all strings to Uppercase */
            convToUpperCaseString(cmdString[cmdOffset + i]);
            /* Get hashCode */
            cmdCode[i] = getHash(cmdString[cmdOffset + i]);
        }

        /* Jump to appropreate routine -----------------------*/
        switch(cmdCode[0]) {
            case hsOPR: /* Operation commands ----------------*/
            {
                /* Do appropriate works */
                break;
            }
            case hsAUTO: /* Auto commands --------------------*/
            {
                /* Do appropriate works */
                break;
            }
            case hsMAN: /* Manual commands -------------------*/
            {
                /* Do appropriate works */
                break;
            }
            case hsCCL: /* Cycle commmands -------------------*/
            {
                /* Do appropriate works */
                break;
            }
            case hsSD: /* SD memory commands ----------------*/
            {
                /* Do appropriate works */
                break;
            }
            

            default:
            {
                /* Unknown commmand error */

                           /* Do appropriate works */
                break;
            }
        }
    }

    /* Send Response  -------------------------------------------*/
    if(rs3.rxResponse) {
        switch(rs3.rxResponse) {
            case noResp:
            {
                /* Do nothing */
                break;
            }
            case sendACK:
            {
                /* Do appropriate works */

                break;
            }
            case sendNAK:
            {
                
                /* Do appropriate works */

                break;
            }
            case sendStatus:
            {
                /* Do appropriate works */
                break;
            }

            /* Insert more cases here */

            default: break;
        }
    }

    /* reset buffer consts after use */

    rx_count = 0;
    rs.no_response_req = 0;
    rs.command_received = 0;
    rs.comm_error = 0;
}



2018년 6월 5일 화요일

Making hashcode to interpret commands

On Using hashcode

    Using hashcode makes simple decode command but needs some preparation. A hashcode is a code that a series of characters produce always the same code. So a number of commands have unique the number of hashcodes. If we don't use those codes, we have to use a bunch of string compare and if...else if...else if blocks of code. The if...else if...else if blocks are not all that bad, but they have less readability and you might not see the program again if there are 20 or more commands.

Write the following 2 subroutines to get hash table.

#define hashSeed 0x65559
uint32 getHash(char *Command) {
    uint32 hash = 0;
    uint8 size = strlen(Command);
    uint8 i;

    for(i = 0; i <= size - 1; i++) {
        hash = hash * hashSeed + Command[i];
    }
    return hash;
}

void createHashTable(void) {
    /* Output to debug */
    /* Convert command string to hashcode and making table */

    uint32 hashcode;
    uint8 i;
    uint8 cmdSize = sizeof(CmdArray) / 4; /* 4 is the per item count */

    debugPrint("\r\n", 2);
    sprintf(buf, "/* Hash code table --------------------- */\r\n");
    debugPrint(buf, strlen(buf));

    for(i = 0; i <= cmdSize - 1; i++) {
        hashcode = getHash(CmdArray[i]);

        /* Output each code to SerialCommander */
        sprintf(buf, "#define hs%s \t 0x%08X \r\n", CmdArray[i], hashcode);
        debugPrint(buf, strlen(buf));
    }

    sprintf(buf, "/* End of Hash code table -------------- */\r\n");
    debugPrint(buf, strlen(buf));

    sprintf(buf, "sizeof(CmdArray) = %d\r\n", cmdSize);
    debugPrint(buf, strlen(buf));
    while(1);
}

Now prepare your own command, for example,

/* Const Command array */
const char *CmdArray[]
        = {
           /* Command class *********************************/
           "OPR", "AUTO", "MAN", "CCL", "SD", 
           "SYS", "ERR", "ENQ", "DBG", "PTNNUM",
           
           /* System operation ******************************/
           "RESET", "ABORT",
           
           /* Auto mode operation ***************************/
           "RUN", "RESUME", "STOP", "SET", "TIMEMODE", 
           
           /* Manual mode operation *************************/
           "AGLRST", "SHTDN", "CANPREST", "SENTPREST", "ALLPREST", 
           "CANAGL", "SENTAGL", "PTNTEST", "PTNSTART", "PTNABORT",
           
           /* Cycle operation *******************************/
           "CYCLE", "CCLSPAN", "RPTCCL", "RSMCCL",
           
           /* SD memory settings ****************************/
           "INTVL", "PTNRD", "PTNCLR", "RSMCCL", "CDFILE",
           
           /* System settings operation *********************/
           "VTGMAX", "CURMAX", "TMOUT", "FREQ", "AGLMODE",  
           "SENTCODE", "PRESET", "TIME", "DATE", "RTCOFS",
           "USEACC", "ACCTM", "PIDKP", "PIDKI", "PIDKD", 
           "XMITDATA", "POSTOL", "CANDEVSEL", "SERIAL",
           
           /* Error override operation **********************/
           "SDOVD",
           
           /* System enquiry operation **********************/
           "STATUS", "ASCDATA", "ASCSTATUS", "SYSID", "PTNDAT",
           "SETTINGS", "SRPAGE", "DATA",
           
           /* Debug operation *******************************/
           "LOGON", "MONAGL", "RUNTEST", "CDFILE", "CHGIMG",
           "MOVPOS",
           
           /* Device IDs ************************************/
           "CAN", "SENT", "CTV", "CBV",
           
           /* Auxiliary commands ******************************/
           "LOW", "HIGH", "ON", "OFF", "INCLD", "EXCLD",
           "UNIPOL", "BIPOL",
};

In main(), insert the following.

int main(void) {

    createHashTable(); /* Comment out this line after getting the codes */

    /* Super loop */
    while(true) {

        /* Do Things here */

    }
}

Using debug tool, we might have

/* Hash code table ---------------------- */
#define hsOPR        0xD76C3E81 
#define hsAUTO       0x0FFCEC91 
#define hsMAN        0x9DCCCC64 
#define hsCCL        0x7F973DAA 
#define hsSD         0x020DAC1F 
#define hsSYS        0x4A262267 
#define hsERR        0xB936AFC9 
#define hsENQ        0xB91D5A64 
#define hsDBG        0x9C31213D 
#define hsPTNNUM 0xDC56212A 
#define hsRESET 0x447349B3 
#define hsABORT 0x4CEFC828 
#define hsRUN            0x2D6C940D 
#define hsRESUME 0x97964821 
      ........
/* End of Hash code table --------------- */

Now copy the table from the debugger and paste it to the program. It will be used in the command interpreter.

2018년 6월 4일 월요일

How to parse incoming command from a serial port.

Parsing serial receive buffer

    When an embedded system needs to receive commands from the host or from other devices, we have to identify what the command has been received and how many its arguments exist. Useful string functions in C can be used to fulfill this job and it is very simple.

With the following declarations, the parser function will work.

/**************************************************************/

#define  maxRxBufLength  200
#define  maxParLength  20
#define  maxParCount   20

char rx_data[maxRxBufLength];
char cmdString[maxParCount][maxParLength];
int  rx_count;

int cmdParser(void) {
    /* Extract each command string from rx_data ---------*/
    /* using string functions,  string to token : strtok() ---*/
    /* and string concatenation ------------------------------*/
    /* Returns argument count */

    char *ptr;
    char delimiters[] = ",:;/ \r\n"; // Specify delimiters here
    int parCount = 0;

    // add null at the end of the rx_data
    rx_data[rx_count] = '\0';

    // extract tokens from rx_data
    ptr = strtok(rx_data, delimiters);
    while(ptr != NULL) {
        cmdString[parCount][0] = '\0'; // Make empty string
        // strncat adds Null termination automatically
        strncat(cmdString[parCount++], ptr, maxParLength - 1);

        ptr = strtok(NULL, delimiters);

        /* check if cmdString overflows */
        if(parCount>= maxParCount) {

            break;
        }
    }

    return parCount;
}
/**************************************************************/

Example >

Before calling cmdParser(), the rx_data and the rx_count should be provided by RX interrupt or other means. 

If we have received data in the rx_data like,

rx_data[] = "SET ADCAVG 300 \r\n"

and then call cmdParser() something like

    rxParCount = cmdParser();

The result will be :

rxParCount = 3, 
cmdString[0] = SET\0,
cmdString[1] = ADCAVG\0,
cmdString[2] = 300\0,