1. 程式人生 > >自己動手封裝VxWorks下C++基礎類

自己動手封裝VxWorks下C++基礎類

VxWorks下采用C++構建Application可以使得程式更加利於維護,利用其提供的STL支援,可以省去大量的底層工作,大大加速軟體的開發進度。

Tornado完全支援C++開發,風河也提供了包括STL在內的豐富的C++元件,但由於VxWorks的系統呼叫是以C函式的形式給出的,使用者在使用C++編寫應用程式的時候並不能實現純粹的面向物件的開發方式,所以有必要對VxWorks的系統呼叫用類進行封裝,產生VxWorks下的C++基礎類庫。風河提供WIND基礎類庫,但是需要另外購買,還是自己動手豐衣足食吧。

1異常類VxError

首先封裝異常類VxError,當程式出現異常時,向外層呼叫者丟擲一個該類的物件,呼叫者採用

try-catch clause捕獲該異常物件,進行異常處理。

由於該類針對的是系統執行時產生的異常,故考慮由C++標準異常類中的runtime_error類派生;VxWorks核心採用設定全域性變數errno的方式記錄系統執行中產生的錯誤,所以將int errNum 作為該類的成員變數,用以記錄異常發生時的errno值。原始碼如下:

#include <stdexcept>

#include "errnoLib.h"

class VxRunTimeError : public runtime_error {

protected:

int errNum;

public:

VxRunTimeError(const string msg = "") : runtime_error(msg), errNum(errnoGet ())

{

}

int getErrNum(void)

{

return errNum;

}

};

2任務類VxTask

任務類VxTask用以封裝VxWorkstask,本來考慮將任務的入口函式作為該類的純虛成員函式,使用者只要在該類的派生類中過載該純虛擬函式就能實現自己的VxWorks task,但由於taskSpawn()的入口函式的引數型別是FUNCPTR(typedef int (*FUNCPTR) (...)),而指向VxTask類成員函式的指標型別為int (VxTask:: *ptr)(…),編譯器不支援這兩種型別之間的強制型別轉換,所以只能換種思路——用一個名為Runnable的類專門封裝

task的入口函式,同時提供一個靜態成員函式完成對該入口函式的呼叫,而該靜態成員函式地址可以轉換成FUNCPTR型別,供taskSpawn()呼叫。在VxTask類中實現關於task的系統呼叫。部分原始碼如下:

class Runnable {

protected:

virtual void run() = 0;

public:

static void entry(Runnable * Run)

{

Run->run();

}

virtual ~Runnable()

{

}

};

class VxTask {

protected:

char *name;

int tid;

public:

VxTask(char* Name, int Arg1 , FUNCPTR Entry = (FUNCPTR)Runnable::entry, int Pri = 150, int Opt = VX_FP_TASK, int StackSize = 2000000,

int Arg2 = 0, int Arg3 = 0, int Arg4 = 0, int Arg5 = 0, int Arg6 = 0, int Arg7 = 0, int Arg8 = 0, int Arg9 = 0, int Arg10 = 0) : name(Name)

{

if(Entry == NULL) {

throw(VxRunTimeError("Task Creat Fail: Entry Can't be NULL!"));

}

tid=taskSpawn(Name,Pri,Opt,StackSize,Entry,Arg1,Arg2, Arg3,Arg4,Arg5,Arg6,Arg7,Arg8,Arg9,Arg10);

if(tid == ERROR) {

throw(VxRunTimeError("Task Spawn Fail!"));

}

}

~VxTask()

{

if(taskDelete(tid) == ERROR) {

throw(VxRunTimeError("Task Delete Error: Task delete fail!"));

}

}

void suspend()

{

if(taskSuspend(tid) == ERROR) {

throw(VxRunTimeError("Task Suspend Error: Task suspend fail!"));

}

}

void resume()

{

if(taskResume(tid) == ERROR) {

throw(VxRunTimeError("Task Resume Error: Task resume fail!"));

}

}

int getTid()

{

return tid;

}

};

使用時首先派生Runnable的子類,過載其run()成員函式,然後將該子類的物件指標賦給VxTask的建構函式,使用者task就跑起來了:

class MyRunnable : public Runnable

{

void run() {

while(1) {

cout<<"Hello VxWorks Task World!"<<endl;

taskDelay(sysClkRateGet());

}

}

};

void myMain()

{

MyRunnable myRun;

VxTask task(“tMyRun”, (int)&myRun);

While(1) {

taskDelay(sysClkRateGet());

}

}

shellsp myMain可以看到預期效果,但如果myMain()中去掉最後的while(1)迴圈,就只會在輸出視窗中看一次Hello VxWorks Task World!輸出。Why?(提示:VxTask的解構函式!)

3中斷類VxInt

中斷類VxIntVxTask類似,同樣用Runnable的派生類封裝入口函式,VxInt類實現中斷系統呼叫:

typedef void (**VOIDFUNCPTRPTR) (...);

class VxInt {

protected:

int intNum;

public:

VxInt(int IntNum, int Arg = 0, VOIDFUNCPTR Entry = (VOIDFUNCPTR)Runnable::entry) : intNum(IntNum)

{

if(intConnect((VOIDFUNCPTRPTR)INUM_TO_IVEC(intNum), Entry, Arg) == ERROR) {

throw(VxRunTimeError("Interrupt Connect Fail!"));

}

}

};

task不同,中斷服務程式(ISR)中不能呼叫可能被阻塞的函式,這點需要在過載Runnable派生類中的run()成員函式時引起注意。

4看門狗類 VxWatchDog

VxWorks中的看門狗實際上是利用系統時鐘中斷來定時執行某個函式的,所以被看門狗執行的函式是執行在中斷的上下文(Context)中,而不是任務的上下文中,故該函式中也不能呼叫帶有阻塞功能的函式。所以VxWatchDog的實現與中斷類VxInt類似:

class VxWatchDog {

WDOG_ID id;

int delay;

public:

VxWatchDog(int Delay, Runnable *EntryObj) : delay(Delay)

{

id = wdCreate();

if(id == NULL) {

throw(VxRunTimeError("Watch Dog Creat Fail!"));

}

if(wdStart(id, delay, (FUNCPTR)Runnable::entry, (int)EntryObj) != OK) {

throw(VxRunTimeError("Watch Dog Start Fail!"));

}

}

void cancel()

{

if(wdCancel(id) != OK) {

throw(VxRunTimeError("Watch Dog Cancel Fail!"));

}

}

WDOG_ID getId()

{

return(id);

}

~VxWatchDog()

{

if(wdDelete(id) != OK) {

throw(VxRunTimeError("Watch Dog Delete Fail!"));

}

}

};

wdStart(WDOG_ID Id, int Delay, FUNCPTR Ptr, int Para )只會讓函式Ptr在延時Delay ticks後執行一次,要週期性地執行Ptr,需要在Ptr中遞迴呼叫wdStart()。那能否這樣實現呢:

class WdRun : public Runnable {

protected:

void run() {

logMsg("Hello Watch Dog World!",0,0,0,0,0,0);

VxWatchDog wtDog(sysClkRateGet(), this);

}

};

上述程式試圖在入口函式中產生一個VxWatchDog類的物件,並用this指標初始化該物件,以期達到每秒鐘執行一次入口函式的目的。但是不要忘了該入口函式是執行在中斷的上下文中的,不允許動態地產生或刪除物件,所以採用這種方法實現週期性執行作業並不可行。

為了在入口函式中呼叫wdStart(),且要避免動態地生成VxWatchDog物件,需要在Runnable派生類的成員變數中包含一個VxWatchDog指標,通過該指標呼叫所指物件的wdStart()。為此,需要在VxWatchDog類中增加成員函式:

VxWatchDog ::VxWatchDog(int Delay) : id(wdCreate()), delay(Delay)

{

if(id == NULL) {

throw(VxRunTimeError("Watch Dog Creat Fail!"));

}

}

void VxWatchDog ::start(Runnable *EntryObj)

{

if(wdStart(id, delay, (FUNCPTR)Runnable::entry, (int)EntryObj) != OK) {

throw(VxRunTimeError("Watch Dog Start Fail!"));

}

}

class WdRun : public Runnable {

protected:

VxWatchDog *dog;

virtual void run() {

logMsg("Hello Watch Dog World!",0,0,0,0,0,0);

dog->start(this);

}

public:

WdRun(VxWatchDog *Dog) : dog(Dog)

{

}

};

void myMain()

{

VxWatchDog wtDog(sysClkRateGet());

WdRun run(&wtDog);

wtDog.start(&run);

while(1) {

taskDelay(sysClkRateGet());

cout<<"In Main!"<<endl;

}

}

shell中輸入sp myMain,可以看到預期輸出。

5訊號量類 VxSem

VxWorks訊號量包括互斥訊號量、二進位制訊號量和計數訊號量,這三種訊號量除了建立時呼叫各自的建立函式,其它操作具有相同的介面,所以考慮採用VxSem類作為訊號量基類,提供統一的訊號量操作介面,VxSemMVxSemBVxSemC三個派生類分別封裝了三種訊號量的建立函式:

class VxSem {

protected:

SEM_ID id;

public:

VxSem(SEM_ID Id) : id(Id)

{

}

virtual ~VxSem()

{

if(semDelete(id) == ERROR) {

throw(VxRunTimeError("Semaphore Delete Fail!"));

}

}

void take(int TimeOut = WAIT_FOREVER)

{

if(semTake(id,WAIT_FOREVER) == ERROR) {

throw(VxRunTimeError("Semaphore Take Fail!"));

}

}

void give()

{

if(semGive(id) == ERROR) {

throw(VxRunTimeError("Semaphore Give Fail!"));

}

}

void flush()

{

if(semFlush(id) == ERROR) {

throw(VxRunTimeError("Semaphore Flush Fail!"));

}

}

SEM_ID getId()

{

return id;

}

};

class VxSemB : public VxSem {

public:

VxSemB(int Opts = SEM_Q_FIFO, SEM_B_STATE State = SEM_EMPTY) : VxSem(semBCreate (Opts, State))

{

if(id == 0) {

throw(VxRunTimeError("Binary Semaphore Creat Fail!"));

}

}

};

class VxSemM : public VxSem {

public:

VxSemM(int Opts = SEM_Q_PRIORITY | SEM_INVERSION_SAFE | SEM_DELETE_SAFE) : VxSem(semMCreate (Opts))

{

if(id == 0) {

throw(VxRunTimeError("Mutual-exclusion Semaphore Creat Fail!"));

}

}

};

class VxSemC : public VxSem {

public:

VxSemC(int Opts, int Cnt) : VxSem(semCCreate (Opts, Cnt))

{

if(id == 0) {

throw(VxRunTimeError("Counting Semaphore Creat Fail!"));

}

}

};