自己動手封裝VxWorks下C++基礎類
VxWorks下采用C++構建Application可以使得程式更加利於維護,利用其提供的STL支援,可以省去大量的底層工作,大大加速軟體的開發進度。
Tornado完全支援C++開發,風河也提供了包括STL在內的豐富的C++元件,但由於VxWorks的系統呼叫是以C函式的形式給出的,使用者在使用C++編寫應用程式的時候並不能實現純粹的面向物件的開發方式,所以有必要對VxWorks的系統呼叫用類進行封裝,產生VxWorks下的C++基礎類庫。風河提供WIND基礎類庫,但是需要另外購買,還是自己動手豐衣足食吧。
1異常類VxError
首先封裝異常類VxError,當程式出現異常時,向外層呼叫者丟擲一個該類的物件,呼叫者採用
由於該類針對的是系統執行時產生的異常,故考慮由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用以封裝VxWorks的task,本來考慮將任務的入口函式作為該類的純虛成員函式,使用者只要在該類的派生類中過載該純虛擬函式就能實現自己的VxWorks task,但由於taskSpawn()的入口函式的引數型別是FUNCPTR(typedef int (*FUNCPTR) (...)),而指向VxTask類成員函式的指標型別為int (VxTask:: *ptr)(…),編譯器不支援這兩種型別之間的強制型別轉換,所以只能換種思路——用一個名為Runnable的類專門封裝
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());
}
}
在shell中sp myMain可以看到預期效果,但如果myMain()中去掉最後的while(1)迴圈,就只會在輸出視窗中看一次Hello VxWorks Task World!輸出。Why?(提示:VxTask的解構函式!)
3中斷類VxInt
中斷類VxInt與VxTask類似,同樣用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類作為訊號量基類,提供統一的訊號量操作介面,VxSemM、VxSemB、VxSemC三個派生類分別封裝了三種訊號量的建立函式:
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!"));
}
}
};