監控任務設計

1 監控任務設計

1.1 概述

監控任務用於監控CPU利用率、管腳狀態,然後在串口中報告狀態。示例較簡單,也可以做得更爲複雜些。系統結構圖如下:
在這裏插入圖片描述
監控任務設計:
雖然監控任務基本上也是週期性去檢查各個狀態;但是系統中沒有使用軟定時器去週期檢查。因爲除週期性檢查狀態外,還需要做一些其它工作,在定時函數中完成並不方便。如果再考慮以後想在監控任務中做一些其它耗時操作,如果這些也放在定時器中去做,則會影響到系統中其它定時器。

監控任務的主要工作是等待郵箱中的消息。一旦等不到超時,則認爲需要檢查狀態。不過,這種方式有個缺點:比如5s監控,在4.9秒後收到郵箱消息時,則會重新等待延時。

命令鏈表:
我們將所有的命令用一個複雜的聯合體來保存,當然你也可以分開寫成多個結構體。所有命令塊的分配與釋放通過存儲塊來管理。

具體監控任務通過命令鏈表來配置。該鏈表中的插入和刪除操作完全由監控任務自己來負責,而不是允許命令解釋器任務直接操作,這樣就避免了兩個任務同時讀寫監控列表問題的情況出現,這樣就不必添加任務資源保護處理。此外,通過郵箱來發送請求,命令解釋器任務可以在發送請求後立即做自己的工作。監控任務可以在有空閒時再對該命令進行處理。整體效率更高。

1.2 代碼實現

cli.c:

/**
 * @brief 命令解釋器設計
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#include <string.h>
#include <stdlib.h>
#include "tinyOS.h"
#include "cli.h"
#include "uart.h"
#include "extio.h"
#include "WaveGen.h"
#include "monitor.h"

#define TERMINAL_BS     0x08
#define TERMINAL_SPACE  ' '
#define TERMINAL_CR     '\r'
#define TERMINAL_LF     '\n'
#define TERMINAL_TAB    '\t'

// 空白字符
static const char spaceCh[] = {TERMINAL_SPACE, TERMINAL_CR, TERMINAL_LF, TERMINAL_TAB, '\0'};
static const char * unknownPinMsg = "Unknown Pin\r\n";
static const char * unknownCmdMsg = "Unknown Command!\r\n";
static const char * noEnoughParamMsg = "No Enough Param!\r\n";

// 任務相關
static tTaskStack cliTaskEnv[CLI_TASK_ENV_SIZE];     // 任務1的堆棧空間
static tTask cliTask;

// 命令行提示緩衝區
static char promoteBuffer[CLI_CMD_PROMATE_SIZE];

/**
 * 顯示消息
 */
static void showMsg (const char * msg) {
    UartWrite(msg, strlen(msg));
}

/**
 * 輸出顯示字符
 * @param ch
 */
static void showCh (const char ch) {
    UartWrite(&ch, 1);
}

/**
 * 顯示提示符
 */
static void showPromote (void) {
    UartWrite(promoteBuffer, strlen(promoteBuffer));
}

/**
 * 更改提示符
 */
static void setPromote (const char * newPromote) {
    strncpy(promoteBuffer, newPromote, sizeof(promoteBuffer));
    promoteBuffer[sizeof(promoteBuffer) - 1] = '\0';
}

/**
 * 提示符修改命令
 */
static void promoteCmd (void) {
    char * promote = strtok(NULL, spaceCh);
    if (promote == NULL) {
        showMsg(noEnoughParamMsg);
        return;
    }

    setPromote(promote);
}

/**
 * 將引腳名轉換爲內部序號
 * @param pinCh
 * @return
 */
static ExtIOPin convertPinNum (char * pinCh) {
    ExtIOPin pinNum;

    if (pinCh == NULL) {
        return ExtIOPinEnd;
    }

    pinNum = (ExtIOPin)(*pinCh - '0');
    if (pinNum >= ExtIOPinEnd) {
        return ExtIOPinEnd;
    }

    return pinNum;
}

/**
 * 外部IO命令解析
 */
static void extioCmd () {
    char * type = strtok(NULL, spaceCh);
    if (type == NULL) {
        showMsg(noEnoughParamMsg);
        return;
    }

    if (strstr(type, "get")) {      // 命令extio get pin
        ExtIOPin pin;
        ExtIOState state;

        pin = convertPinNum(strtok(NULL, spaceCh));
        if (pin == ExtIOPinEnd) {
            showMsg(unknownPinMsg);
            return;
        }

        state = ExtIOGetState(pin);
        showMsg((state == ExtIOHigh) ? "1\r\n" : "0\r\n");
    } else if (strstr(type, "set")) {   // 命令extio set pin value
        ExtIOPin pin;
        char * value;

        pin = convertPinNum(strtok(NULL, spaceCh));
        if (pin == ExtIOPinEnd) {
            showMsg(unknownPinMsg);
            return;
        }

        value = strtok(NULL, spaceCh);
        if (value == NULL) {
            showMsg(noEnoughParamMsg);
            return;
        }

        ExtIOSetState(pin, *value == '0' ? ExtIOLow : ExtIOHigh);
    } else if (strstr(type, "dir")) {   // 命令extio dir pin in/out
        ExtIOPin pin;
        char *outType;

        pin = convertPinNum(strtok(NULL, spaceCh));
        if (pin == ExtIOPinEnd) {
            showMsg(unknownPinMsg);
            return;
        }

        outType = strtok(NULL, spaceCh);
        if (outType == NULL) {
            showMsg(noEnoughParamMsg);
            return;
        }

        ExtIOSetDir(pin, strstr(outType, "in") ? 1 : 0);
    } else {
        showMsg(noEnoughParamMsg);
    }
}

/**
 * 波形輸出命令解析
 */
static void waveCmd () {
    char *type = strtok(NULL, spaceCh);
    if (type == NULL) {
        showMsg(noEnoughParamMsg);
        return;
    }

    if (strstr(type, "square")) {      // 命令wave square
        WaveSelectType(WaveSquare);
    } else if (strstr(type, "start")) {
        WaveStartOutput();
    } else if (strstr(type, "stop")) {
        WaveStopOutput();
    } else {
        showMsg(noEnoughParamMsg);
    }
}

static void monitorPinCmd (void) {
    MonitorCmd * cmd;
    uint8_t pin;
    uint8_t isOn = 0;

    char * pinCh = strtok(NULL, spaceCh);       // 解析引腳 pinnum
    if (pinCh == NULL) {
        showMsg(noEnoughParamMsg);
        return;
    } else {
        char *on_off;

        pin = atoi(pinCh);
        on_off = strtok(NULL, spaceCh);         // 解析開關on/off
        if (on_off == NULL) {
            showMsg(noEnoughParamMsg);
            return;
        } else if (strstr(on_off, "on")) {
            isOn = 1;
        }

        cmd = MonitorAllocCmd();
        cmd->isAdd = isOn;
        cmd->target = MonitorExtIOPin;
        cmd->options.pin.pinNum = pin;
        MonitorSendCmd(cmd);
    }
}

static void monitorCPUCmd (void) {
    uint8_t isOn = 0;

    char * on_off = strtok(NULL, spaceCh);  // monitor cpu on/off

    if (on_off == NULL) {
        showMsg(noEnoughParamMsg);
        return;
    } else if (strstr(on_off, "on")) {
        MonitorCmd * cmd = MonitorAllocCmd();

        char * percent = strtok(NULL, spaceCh); // monitor cpu on percent
        isOn = 1;
        if (percent != NULL) {
            float cpuPercent = (float)atof(percent);
            cmd->options.cpu.warning = 1;
            cmd->options.cpu.warnPercent = cpuPercent;
        } else {
            cmd->options.cpu.warning = 0;
        }

        cmd->isAdd = isOn;
        cmd->target = MonitorCPUUsage;
        MonitorSendCmd(cmd);
    }
}

/**
 * 波形輸出命令解析
 */
static void monitorCmd (void) {
    char *type = strtok(NULL, spaceCh);
    if (type == NULL) {
        showMsg(noEnoughParamMsg);
        return;
    }

    if (strstr(type, "on")) {               // monitor on
        MonitorOn();
    } else if (strstr(type, "off")) {       // monitor off
        MonitorOff();
    } else if (strstr(type, "pin")) {       // monitor pin num on/off
        if (MonitorIsOn()) {
            monitorPinCmd();
        } else {
            showMsg("Turn on Monitor first!\r\n");
        }
    } else if (strstr(type, "cpu")) {      // monitor cpu on/off
        if (MonitorIsOn()) {
            monitorCPUCmd();
        } else {
            showMsg("Turn on Monitor first!\r\n");
        }
    }
}

/**
 * 未知命令處理
 */
static void unknowCmd (void) {
    showMsg(unknownCmdMsg);
}

/**
 * 讀取一行數據,如果命令超過則截斷
 */
static void readLine (char * buffer, uint32_t maxLen) {
    uint32_t index = 0;

    while (index < maxLen) {
        char ch;

        UartRead(&ch, 1);
        switch (ch) {
            case TERMINAL_BS:   // 退格鍵
                if (index > 0) {
                    buffer[index--] = '\0';
                    showCh(TERMINAL_BS);
                    showCh(TERMINAL_SPACE);
                    showCh(TERMINAL_BS);
                }
                break;
            case TERMINAL_CR:
                showCh(TERMINAL_LF);
            default:
                showCh(ch);

                buffer[index++] = ch;
                if ((ch == '\n') || (ch == '\r') || (index >= maxLen)) {
                    buffer[index] = '\0';
                    return;
                }
                break;
        }
    }
}

/**
 * 解析命令
 */
static void processCmd (char * cmdLine) {
    char * cmdStart;

    // 獲取開頭
    cmdStart = strtok(cmdLine, spaceCh);
    if (cmdStart == NULL) {
        return;
    }

    // 識別命令
    if (strstr(cmdStart, "extio")) {
        extioCmd();
    } else if (strstr(cmdStart, "wave")) {
        waveCmd();
    } else if (strstr(cmdStart, "promote")) {
        promoteCmd();
    } else if (strstr(cmdStart, "monitor")) {
        monitorCmd();
    } else {
        unknowCmd();
    }
}

/**
 * 解釋器任務
 * @param param
 */
void cliTaskEntry (void * param) {
    static char cmdBuffer[CLI_CMD_BUFFER_SIZE];

    for (;;) {
        showPromote();
        readLine(cmdBuffer, sizeof(cmdBuffer));
        processCmd(cmdBuffer);
    }
}

/**
 * 命令解釋器設計
 */
void CLIInit (void) {
    strcpy(promoteBuffer, ">>");
    tTaskInit(&cliTask, cliTaskEntry, (void *) 0x0, CLI_TASK_PRIO, cliTaskEnv, sizeof(cliTaskEnv));
}


monitor.h:

/**
 * @brief 監控任務設計
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */

#ifndef MONITOR_H
#define MONITOR_H

#include "tinyOS.h"

#define MONITOR_TASK_PRIO           0
#define MONITOR_TASK_ENV_SIZE       512
#define MONITOR_MAX_CMD             10
#define REPORT_BUFFER_SIZE          128
#define MONITOR_DEFAULT_TIMEOUT     1000

// 監控對像
typedef enum {
    MonitorCPUUsage,
    MonitorExtIOPin,
}MonitorTarget;

// 監控命令
typedef struct _MonitorCmd {
    tNode linkNode;

    uint8_t isAdd;
    MonitorTarget target;

    union MonitorOption{
        struct {
            uint8_t pinNum;     // 管腳序號
        }pin;

        struct {
            uint8_t warning;
            float warnPercent;
        }cpu;
    }options;
}MonitorCmd;

void MonitorInit (void);
void MonitorOn (void);
void MonitorOff (void);
uint8_t MonitorIsOn (void);

MonitorCmd * MonitorAllocCmd (void);
void MonitorFreeCmd (MonitorCmd * cmd);
void MonitorSendCmd (MonitorCmd * cmd);
void MonitorSetPeriod (uint32_t ms);

#endif //PROJECT_MONITOR_H

monitor.c:

/**
 * @brief 監控任務設計
 * @details
 * @author 01課堂 李述銅 http://01ketang.cc
 * @date 2017-06-01
 * @version 1.0
 * @copyright 版權所有,禁止用於商業用途
 */
#include <string.h>
#include <stdio.h>
#include "monitor.h"
#include "uart.h"
#include "extio.h"

static uint8_t monitorIsOn;

__align(8) static tTaskStack monitorTaskEnv[MONITOR_TASK_ENV_SIZE];
static tTask monitorTask;

static tMbox cmdMbox;
static void * msgBuffer[MONITOR_MAX_CMD];

static tMemBlock cmdMemBlock;
static MonitorCmd cmdMem[MONITOR_MAX_CMD];

static tList monitorCmdList;

static uint32_t monitorPeriod;          // 監控週期
static char reportBuffer[REPORT_BUFFER_SIZE];

/**
 * 分配命令塊
 * @return
 */
MonitorCmd * MonitorAllocCmd (void) {
    MonitorCmd * cmd = 0;

    tMemBlockWait(&cmdMemBlock, (void **)&cmd, 0);
    memset(cmd, 0, sizeof(MonitorCmd));
    tNodeInit(&cmd->linkNode);
    return cmd;
}

/**
 * 釋放命令塊
 * @param cmd
 */
void MonitorFreeCmd (MonitorCmd * cmd) {
    tMemBlockNotify(&cmdMemBlock, cmd);
}

/**
 * 添加監控命令
 * @param cmd
 */
void MonitorSendCmd (MonitorCmd * cmd) {
    tMboxNotify(&cmdMbox, cmd, tMBOXSendNormal);
}

/**
 * 設置監控週期
 * @param ms
 */
void MonitorSetPeriod (uint32_t ms) {
    monitorPeriod = ms;
}

/**
 * 找到配置相同的命令
 * @param cmd
 * @return
 */
static MonitorCmd * findCmd (MonitorCmd * cmd) {
    tNode * currNode;

    for (currNode = tListFirst(&monitorCmdList); currNode != (tNode *)0; currNode = tListNext(&monitorCmdList, currNode)) {
        MonitorCmd * currentCmd = (MonitorCmd *)tNodeParent(currNode, MonitorCmd, linkNode);

        if (currentCmd->target == cmd->target) {
            switch (currentCmd->target) {
                case MonitorCPUUsage:
                    return currentCmd;
                case MonitorExtIOPin:
                    if (currentCmd->options.pin.pinNum == cmd->options.pin.pinNum) {
                        return currentCmd;
                    }
                    break;
                default:
                    break;
            }
        }
    }
    return 0;
}

/**
 * 添加命令
 */
static void procReceivedCmd (MonitorCmd * cmd) {
    MonitorCmd * existCmd = findCmd(cmd);

    if (cmd->isAdd) {
        if (existCmd) {
            tListRemove(&monitorCmdList, &existCmd->linkNode);
            MonitorFreeCmd(existCmd);
        }

        tListAddLast(&monitorCmdList, &cmd->linkNode);
    } else {
        if (existCmd) {
            tListRemove(&monitorCmdList, &existCmd->linkNode);
            MonitorFreeCmd(existCmd);
        }

        MonitorFreeCmd(cmd);
    }
}

static void showReportMsg (const char * msg) {
    UartWrite(msg, strlen(msg));
}

static void reportCPUUsage (MonitorCmd * cmd) {
    float cpuUsage = tCpuUsageGet();

    sprintf(reportBuffer, "CPU usage:%lf\r\n", cpuUsage);
    showReportMsg(reportBuffer);

    if (cmd->options.cpu.warning) {
        if (cmd->options.cpu.warnPercent <= cpuUsage) {
            showReportMsg("Warning: CPU usage too high\r\n");
        }
    }
}

static void reportExtIOPin (MonitorCmd * cmd) {
    ExtIOState state;

    ExtIOPin pin = (ExtIOPin)cmd->options.pin.pinNum;
    if (pin >= ExtIOPinEnd) {
        return;
    }

    state = ExtIOGetState(pin);
    sprintf(reportBuffer, "ExtIO Pin %d:%d\r\n", pin, (state == ExtIOHigh) ? 1 : 0);
    showReportMsg(reportBuffer);
}

/**
 * 報告錯誤
 * @param cmd
 */
static void reportTarget (void) {
    tNode * currNode;

    for (currNode = tListFirst(&monitorCmdList); currNode != NULL; currNode = tListNext(&monitorCmdList, currNode)) {
        MonitorCmd * currentCmd = (MonitorCmd *)tNodeParent(currNode, MonitorCmd, linkNode);

        switch (currentCmd->target) {
            case MonitorCPUUsage:
                reportCPUUsage(currentCmd);
                break;
            case MonitorExtIOPin:
                reportExtIOPin(currentCmd);
                break;
        }
    }
}

/**
 * 監控任務
 * @param param
 */
void monitorTaskEntry (void * param) {
    for (;;) {
        uint32_t err;
        MonitorCmd * cmd;

        err = tMboxWait(&cmdMbox, (void **)&cmd, monitorPeriod / TINYOS_SYSTICK_MS);
        if (err == tErrorNoError) {
            procReceivedCmd(cmd);
        } else {
            reportTarget();
        }
    }
}

/**
 * 監控初始化
 */
void MonitorInit (void) {
    monitorIsOn = 0;          // 未開啓
}

void MonitorOn (void) {
    if (monitorIsOn) {
        return;
    }

    monitorIsOn = 1;

    monitorPeriod = MONITOR_DEFAULT_TIMEOUT;
    tListInit(&monitorCmdList);

    tMboxInit(&cmdMbox, msgBuffer, MONITOR_MAX_CMD);
    tMemBlockInit(&cmdMemBlock, cmdMem, sizeof(MonitorCmd), MONITOR_MAX_CMD);

    tTaskInit(&monitorTask, monitorTaskEntry, (void *) 0x0, MONITOR_TASK_PRIO, monitorTaskEnv, sizeof(monitorTaskEnv));
}

void MonitorOff (void) {
    if (monitorIsOn == 0) {
        return;
    }

    tTaskForceDelete(&monitorTask);
    monitorIsOn = 0;
}

uint8_t MonitorIsOn (void) {
    return monitorIsOn;
}


參考資料:

  1. 手把手教你學用嵌入式操作系統
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章