本文主要描述如下通过KRTS的共享内存使KRTS的实时进程与Qt应用程序进行通信。首先简单介绍什么是Kithara RTS。
由于基于PC的控制系统在工业自动化生产领域得到了广泛的应用,而运行在PC的主流操作系统Windows本身并不是一个实时操作系统,这限制了PC在某些需要高实时性能控制系统的应用。针对该问题,通常的解决方法是为Windows增加一个可编程的实时子系统,而实现这种功能的软件被称为Windows实时拓展软件。而德国Kithara软件公司正是业界知名的Windows实时拓展软件专家,自1996年开始致力于Windows实时拓展软件的研发,其推出的模块化Windows实时拓展软件Kithara Realtime Suite(KRTS)不仅可以为安装Windows的PC提供优秀的实时性能,而且具有众多功能模块以辅助项目和产品的研发。目前国内外很多大公司都采用其产品,如BOSCH(博世)、SIEMENS(西门子)、ALSTOM(阿尔斯通)等公司。
简单来说,Kithara RTS是德国Kithara软件公司的一个产品,本质是一个Windows实时拓展程序,可以把Windows变成一个实时操作系统。此外Kithara RTS自身具有很多功能模块,如EtherCAT、CANopen、Profibus模块等,可以将Halcon、OpenCV直接载入到Kithara RTS的实时内核中执行。因此,Kithara RTS可以把PC变成现场控制系统,应用于运动控制系统中,如数控系统、机器人、自动化控制系统等。
我们知道,共享内存是程序之间进行数据交互的最基本的方式,而由于Windows和Kithara RTS本身为两个独立的系统,为了能够使两个不同系统之上的程序进行通信,那么就必须开辟一块内存区域用于数据共享。
由于每个应用都有独立的地址空间,所以一个进程一般不会访问其他程序的内存。另外,自己的程序一般不能访问内核内存,因为内核层的内存是受保护的。因此为了在不同应用层程序之间、应用层程序与内核层程序之间的实现快速数据交换,需要申请一块独立的物理内存,Kithara RTS中用于操作共享内存的功能在Base模块。
应用层程序和内核层程序可访问的内存地址空间是不同的,这两部分内存区域是独立的。因此,Kithara中关于共享内存的函数会同时指定两个不同的地址参数。
以下是Kithara RTS中提供的操作共享内存的APIs:
Shared memory
- KS_createSharedMem (creates shared memory for data exchange)
- KS_freeSharedMem (frees shared memory)
- KS_getSharedMem (gives the current address of shared memory)
- KS_readMem (reads from kernel memory)
- KS_writeMem (writes to kernel memory)
- KS_createSharedMemEx (creates shared memory for data exchange)
- KS_freeSharedMemEx (frees shared memory)
- KS_getSharedMemEx (gives the address of shared memory determined by current context)
- KS_querySharedMemInfo (is not implemented yet)
下面以一个简单的示例说明如何使用Kithara RTS中的共享内存,以及Qt应用程序如何通过共享内存与Kithara实时任务通信。
Kithara工程相关配置略过,可以直接在Kithara安装目录中的smp目录下的示例工程中修改,这样就可以省去配置工作。下面是具体实现的源文件,我将它命名为periodicTimerTaskShm.cpp。
#include "..\_KitharaSmp\_KitharaSmp.h"
const char* pCustomerNumber = "DEMO";
//------ CallBackData ------
struct CallBackData {
int counter;
};
const int us = 10;
//----------------------------------------------------------------
// Ctrl + C handler
//----------------------------------------------------------------
bool stopFlag = false;
BOOL WINAPI _keyboardHandler(DWORD key)
{
if(key == CTRL_C_EVENT){
// 当Ctrl+C的时候发生的事情
outputTxt("< Ctrl + C > event happened.");
stopFlag = true;
return true;
}
return false;
}
//------ _callBackA ------
static Error __stdcall _callBackA(void* pArgs, void* pContext) {
CallBackData* pData = (CallBackData*)pArgs;
pData->counter++
return KS_OK;
}
//--------------------------------------------------------
// This is the main program
//--------------------------------------------------------
void runSample() {
Error ksError;
SetConsoleCtrlHandler(_keyboardHandler, TRUE); // 添加事件回调函数
ksError = KS_openDriver(pCustomerNumber);
if (ksError) {
outputErr(ksError, "KS_openDriver", "Maybe incorrect customer number?");
return;
}
//------------------------------------------------------
// Allocation of Sharedmem
//------------------------------------------------------
CallBackData* pAppPtr;
CallBackData* pSysPtr;
ksError = KS_createSharedMem(
(void**)&pAppPtr, // App Pointer
(void**)&pSysPtr, // Sys Pointer
"periodicTimerTaskShm2Qt", // Name
sizeof(CallBackData), // Size
KSF_ALTERNATIVE | KSF_CONTINUOUS); // Flags - KSF_ALTERNATIVE | KSF_CONTINUOUS
if (ksError != KS_OK) {
outputErr(ksError, "KS_createSharedMem", "Failed to allocate shared memory");
KS_closeDriver();
return;
}
// 也可以通过以下方法创建
/*
KSHandle hShmEx;
ksError = KS_createSharedMemEx(&hShmEx, "GSK_TaskTimerCycle2QtSHM", sizeof(CallBackData), KSF_ALTERNATIVE | KSF_CONTINUOUS);// Flags - KSF_ALTERNATIVE | KSF_CONTINUOUS
if(ksError != KS_OK) {
outputErr(ksError, "KS_createSharedMemEx", "Failed to allocate shared memory");
KS_closeDriver();
return ;
}
CallBackData *pShmExPtr;
ksError = KS_getSharedMemEx(hShmEx, (void **)&pShmExPtr, 0);
if(ksError != KS_OK) {
outputErr(ksError, "KS_getSharedMemEx", "Failed to get shared memory");
KS_closeDriver();
return ;
}
*/
pAppPtr->counter_ = 0;
//------------------------------------------------------
// Now we create the callbacks. The callback contains the KSF_DIRECT_EXEC: - execution on kernel level
//------------------------------------------------------
KSHandle hCallbackA;
ksError = KS_createCallBack(
&hCallbackA, // Address of callback handle
_callBackA, // Callback function
pSysPtr, // Reference parameter to the callback - pSysPtr
KSF_DIRECT_EXEC, // Flags
0); // Priority (only on user level)
if (ksError != KS_OK) {
outputErr(ksError, "KS_createCallBack", "Failed to create callback");
KS_closeDriver();
return;
}
ksError = KS_createTask(
&pAppPtr->hTaskA_, // Address of task handle
hCallbackA, // Callback handle
250, // Priority
KSF_DONT_START); // Dont start the task, a time will do that
if (ksError != KS_OK) {
outputErr(ksError, "KS_createTask", "Failed to create task");
KS_closeDriver();
return;
}
//------------------------------------------------------
// Create a timers with different periods. The signalisation objects are tasks in this case.
//
// Due to the creation of the timer A directly with a callback handle, the priority has the value 127.
// Timer B has the priority value 130, which is given when creating the task.
//
// Accepted flags are:
// KSF_REALTIME_EXEC: - the real-time kernel is to be used
// KSF_DONT_START: - don't start the timer (can be started later)
// KSF_ONE_SHOT: - signal timer only one time
//------------------------------------------------------
KSHandle hTimerA;
ksError = KS_createTimer(
&hTimerA, // Address of timer handle
1000 * us, // Timer period in 100-ns-units
pAppPtr->hTaskA_, // Task handle
KSF_REALTIME_EXEC); // Flags, here real-time execution
if (ksError != KS_OK) {
outputErr(ksError, "KS_createTimer", "Unable to create the timer!");
KS_closeDriver();
return;
}
// Do something
while(!stopFlag)
{
if (myKbhit()) {
int key = myGetch();
if (key == 'q' || key == 'Q')
break;
}
}
outputDec(pAppPtr->counter, "counter: ");
//outputDec(pShmExPtr ->counter, "counter: ");
waitTime(25 * ms);
//------------------------------------------------------
// Stop the timer
//------------------------------------------------------
ksError = KS_stopTimer(
hTimerA); // Timer handle
if (ksError != KS_OK)
outputErr(ksError, "KS_stopTimer", "Unable to stop the timer!");
//------------------------------------------------------
// Remove the timer
//------------------------------------------------------
ksError = KS_removeTimer(
hTimerA); // Timer handle
if (ksError != KS_OK)
outputErr(ksError, "KS_removeTimer", "Unable to remove timer");
//------------------------------------------------------
// Remove the callbacks
//------------------------------------------------------
ksError = KS_removeCallBack(
hCallbackA); // Callback handle
if (ksError != KS_OK)
outputErr(ksError, "KS_removeCallBack", "Unable to remove the callback!");
//------------------------------------------------------
// Remove the shared memory
//------------------------------------------------------
ksError = KS_freeSharedMem(
pAppPtr); // Application pointer
if (ksError != KS_OK)
outputErr(ksError, "KS_freeSharedMem", "Unable to remove shared memory!");
/*
ksError = KS_freeSharedMemEx(hShmEx, 0);
if (ksError != KS_OK) {
outputErr(ksError, "KS_freeSharedMemEx", "Unable to remove shared memory!");
}
*/
//------------------------------------------------------
// At last we have to close the driver to free any allocated resources.
//------------------------------------------------------
ksError = KS_closeDriver();
if (ksError != KS_OK)
outputErr(ksError, "KS_closeDriver", "Unable to close the driver!");
}
以下是Qt应用程序相关代码:
sharedMemoryTest.pro文件
需要添加两个库:KrtsDemo_x64.lib 和 KrtsDemo_vci.lib
#-------------------------------------------------
#
# Project created by QtCreator 2016-03-09T13:46:03
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = sharedMemoryTest
TEMPLATE = app
LIBS += -L E:/workspace/Kithara/dev -lKrtsDemo_x64 -lKrtsDemo_vci
SOURCES += main.cpp\
mainwidget.cpp \
KitharaShm.c
HEADERS += mainwidget.h \
KitharaShm.h
main.cpp文件
#include "mainwidget.h"
#include <QApplication>
#define SHAREDMEMORY_GLOBALS
#include "KitharaShm.h"
// The name of kithara shared memory is: GSK_TaskTimerCycle2QtSHM
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
G_SharedMemoryInit();
MainWidget w;
w.show();
return a.exec();
}
mainwidget.h文件
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QTimer>
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = 0);
~MainWidget();
private slots:
void appTimerOut();
private:
void createUI(void);
void layoutUI(void);
QLineEdit *lineEdit1;
QTimer *appTimer;
};
#endif // MAINWIDGET_H
mainwidget.cpp文件
#include "mainwidget.h"
#include <QFormLayout>
#include "KitharaShm.h"
#include <QDebug>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
setFixedSize(300, 200);
setWindowTitle(tr("SharedMemory2Kithara Test"));
createUI();
layoutUI();
appTimer = new QTimer(this);
connect(appTimer, SIGNAL(timeout()), this, SLOT(appTimerOut()));
appTimer->start(100);
}
MainWidget::~MainWidget()
{
G_SharedMemoryCleanup();
}
void MainWidget::createUI()
{
lineEdit1 = new QLineEdit;
lineEdit1->setReadOnly(true);
}
void MainWidget::layoutUI()
{
QFormLayout *mainLayout = new QFormLayout;
mainLayout->setContentsMargins(15, 30, 15, 30);
mainLayout->setSpacing(15);
mainLayout->addRow(tr("counter:"), lineEdit1);
this->setLayout(mainLayout);
}
void MainWidget::appTimerOut()
{
if(pShmExPtr == NULL)
{
qDebug("pShmExPtr is NULL.");
return ;
}
lineEdit1->setText(QString::number(pShmExPtr->counter));
}
KitharaShm.h文件
#ifndef KITHARASHM_H
#define KITHARASHM_H
#define KS_OK 0x00000000
#define KSF_ALTERNATIVE 0x00200000
#define KSF_CONTINUOUS 0x00010000
//------ Synonyms ------
typedef int Error;
typedef void * KSHandle;
#if defined(__cplusplus)
extern "C"
{
#endif
//------ Common functions ------
Error __stdcall KS_openDriver(
const char* customerNumber);
Error __stdcall KS_closeDriver(
void);
//------ Shared memory ------
Error __stdcall KS_createSharedMem(
void** ppAppPtr, void** ppSysPtr, const char* name, int size, int flags);
Error __stdcall KS_freeSharedMem(
void* pAppPtr);
Error __stdcall KS_getSharedMem(
void** ppPtr, const char* name);
Error __stdcall KS_readMem(
void* pBuffer, void* pAppPtr, int size, int offset);
Error __stdcall KS_writeMem(
const void* pBuffer, void* pAppPtr, int size, int offset);
Error __stdcall KS_createSharedMemEx(
KSHandle* phHandle, const char* name, int size, int flags);
Error __stdcall KS_freeSharedMemEx(
KSHandle hHandle, int flags);
Error __stdcall KS_getSharedMemEx(
KSHandle hHandle, void** pPtr, int flags);
/*
* 共享内存
*/
#ifdef SHAREDMEMORY_GLOBALS
#define SHAREDMEMORY_EXT
#else
#define SHAREDMEMORY_EXT extern
#endif
//------ CallBackData ------
typedef struct CallBackData {
int counter;
}CallBackData;
SHAREDMEMORY_EXT KSHandle hShmEx;
SHAREDMEMORY_EXT CallBackData *pShmExPtr;
int G_SharedMemoryInit();
void G_SharedMemoryCleanup();
#if defined(__cplusplus)
}
#endif
#endif // KITHARASHM_H
KitharaShm.c 文件
注意:Qt 应用程序也必须先调用 KS_openDriver() 才能使用共享内存。
#define SHAREDMEMORY_GLOBALS
#include "KitharaShm.h"
int G_SharedMemoryInit()
{
Error ksError = -1;
const char* pCustomerNumber = "DEMO";
ksError = KS_openDriver(pCustomerNumber); // Customer number
if (ksError) {
//
return;
}
ksError = KS_createSharedMemEx(&hShmEx, "periodicTimerTaskShm2Qt", sizeof(CallBackData), KSF_ALTERNATIVE | KSF_CONTINUOUS);
if(ksError != KS_OK) {
//
KS_closeDriver();
return ;
}
ksError = KS_getSharedMemEx(hShmEx, (void **)&pShmExPtr, 0);
if(ksError != KS_OK) {
//outputErr(ksError, "KS_getSharedMemEx", "Failed to get shared memory");
//KS_closeDriver();
//return ;
}
if(pShmExPtr == NULL)
{
G_SharedMemoryCleanup();
//
return 1;
}
return 0;
}
void G_SharedMemoryCleanup()
{
Error ksError = -1;
ksError = KS_freeSharedMemEx(hShmEx, 0);
if (ksError != KS_OK) {
//
}
ksError = KS_closeDriver();
if (ksError != KS_OK) {
//
}
}
#endif