VxWorks下采用C++构建Application可以使得程序更加利于维护,利用其提供的STL支持,可以省去大量的底层工作,大大加速软件的开发进度。
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用以封装VxWorks的task,本来考虑将任务的入口函数作为该类的纯虚成员函数,用户只要在该类的派生类中重载该纯虚函数就能实现自己的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())
}
}
在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!"))
}
}
}
常用函数公式:1、LEN函数:用于统计一个数据或者一个词出现的次数
使用公式:=LEN(数据),需要进行统计的关键词,主要作用就是计算关键词出现的次数
2、countif函数:统计一个区域的数据中符合一个条件的总数量
使用公式:=countif(区域,条件) 需要注意符号是英文状态,除了字母,都要加一下双引号。
3、vlookup函数:纵向查找
使用公式:=vlookup(G:G,A:B,2) G:G相同的一列,A:B查找范围,2是查找的第几列
如果没有相同值,可以自己创造一个相同值。
4、sumif函数:条件求和
使用公式:=sumif(C:C,”>20”,B:B)
C列条件大于20,b列的和,注意符号使用英文状态下,除了字母都要添加双引号
5、sumifs函数:多条件求和
使用公式:=sumifs(B:B,C:C,”>80”,D:D,”>80”)
注意先写要求和的区域,再写条件
常用的sem数据分析函数,各位小伙伴们学会了吗?可以多多操作,牢牢记住公式,让自己的工作更加轻松,事半功倍。
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)