RCS (Realime control system)库是由美国国家标准与技术研究院(KIST)研究开发的软件库,可以用来辅助构建多模块的控制系统软件。 RCS库是基于RCS方法论和参考模型结构RMA (Reference model ar-chitecture)发展起来的。它代码开放,可自由的使用、扩展和裁剪,不仅提供了Java版木,还提供了C++版和Ada版的源码。
利用RCS库构建的控制系统软件往往具有多模块、分层次结构。每个模块都有一个命令缓冲区和一个状态缓冲区。顶层模块负责接收操作指令,并写命令到命令缓冲区,经过任务分析、分解,把它转发为具有内部形式的控制任务信息,传递给中间层相关模块。中间层模块会将任务细分成更小的任务,分配给下层模块执行,并协调它们的动作。下层的模块还要不断更新自身的状态,子模块通过状态通道将状态缓冲区里的状态信息反馈给父模块。在实际系统中,会有更多的层次关系,但是它们的原理是一样。
NML是一个代码开源的可用于多进程通信的库,采用共享内存和管道结合的方式以C++代码实现的一个通信库。NML通信库具有的特性:
1、通过一个ACSII文本配置文件对通信进行配置,仅配置通信参数不需要修改程序。
2、拥有灵活的通信实现方式,队列,一对多,收发状态同步返回等。
3、远程进程可以实现像访问本地共享内存一样,实现和远程进程的数据通讯。
RCS各模块(进程)之间是利用RCS库提供的CMS/NML例程,通过共享内存缓冲区传递信息。CMS和NML负责对数据信息进行编发和解码,实现跨平台通信。
CMS (Communication Management System)是RCS软件中的底层通信管理系统,提供了非常全面且统一的通信接口。CMS提供的方法将所有数据类型编码成与平台无关的格式,这使得RCS进程可以在不同的操作系统上通信。
NML ( Neutral Message Language,中性消息语言)提供了更高层次封装,简化了CMS的编程接口。所以开发人员不必了解底层CMS的工作过程,不必处理与特殊平台相关的问题图。NML提供类似“mail-box”,的通信方式,每个通信缓冲区可以看作一个邮箱,可以从缓冲区中读取消息,或者向某个缓冲区中写入消息;缓冲区内可以包含一个消息队列,否则每次写入信息将覆盖原有的消息。对于NML通信中的每个消息,被称为一个NMLmsg。 RCS库中提供了一个NMLmsg的基类,提供了信息类型定义、信息大小和数据格式转换等基本功能。编程时通过继承NMLmsg进而定义实际所需的通信数据,需要注意的是数据必须在编译时定义而不能在运行时动态生成不同的数据结构。
NML的配置信息不保存在中心服务器或数据库中,而保存在文本文件里。要实现终端和数控系统间跨平台远程连接,必须保证程序和NC端程序使用相同的配置文件。配置文件主要定义两类信息,缓冲区和线程。
NML配置文件主要包括四个部分,分别是:注释,消息,进程,server
# Name Type Host size neut? (old) buffer# MP
B my_share_buff SHMEM localhost 512 0 0 1 * 101 TCP=5001 format_name=my_msg queue
-
Name是消息名称。
-
Type表示的是消息的使用形式,包括三种方式:SHMEM(共享内存),GLOBMEM(全局),LOCMEM(本地),FILEMEM(文本),(对远程的Buffer有三种方式:TCP, UDP, RPC)。
-
Host表示的是buffer所在的主机名,如果消息只是在同一台机器上传递,则这里写什么都问题不大,最好写localhost。需要特别注意的是,如果是不同机器传递某一个消息,需要给消息写一个server,来负责传递,则这里要写的是该消息的server所在的主机名(或主机IP地址)。这里可以看到,host1和host4是有通信关系的,很多消息都在这两台机子中间传递。因此, 规划模块在host1上,与之相关的server也在host1上,因此此处的Host也是写host1。
-
size表示的是消息的大小,单位字节,这里要根据msg定义的数据结构来确认
-
neut 0或1都可以,区别是什么官方文档并未介绍清楚,如果没有处理器不兼容,则设0。通常取0即可。
-
RPC# 已经被官方弃用,因此统一用符号来占位。
-
buffer# buffer的序列号,需要保证唯一即可。
-
MP:max_procs,可以用符号占位。
-
[Type-spec data]:对于SHMEM,需要一个唯一的共享内存键。后面接着“TCP=”,后面的数字必须大于1024。后面接着"bsem=",后面的数字与TCP不同即可。
# Name Buffer Type Host Ops server? timeout master? cnum
P read_proc my_share_buff LOCAL localhost R 0 INF 1 1
P send_proc my_share_buff LOCAL localhost W 0 INF 0 1
- Name:进程名称,一般对应到bin里面的可运行程序,有可能一个程序有多个进程。
- Buffer:表示的是该进程使用的消息名称,必须在Buffer中定义。
- Type:只能是 “LOCAL” , “REMOTE”,“AUTO”。这个与进程的位置无关,表示的是进程如何访问消息。对于SERVER来说直接访问,则是用LOCAL。如果用TCP、UDP访问或者本行的Buffer需与其他主机通信(读或写),且server程序不在本机,则是用REMOTE。
- Host:表示的是进程所在主机ip地址。
- Ops:表示进程对消息的操作方式,R表示读,W表示写,RW表示既读又写。
- server :对于svr而言为1,否则为0。
- timeout: 互斥时间通常写为0.1。
- master: 表示指定消息的传递指定哪一个process是master,注意master可以不唯一,且至少需要有一个master,建议如现有文件中所有process对应buffer都为1。如果对于不是master的process,其在c++使用NML,建立new CHANNEL时,会一直等待程序的master建立好后,才能够正常初始化,否则会一直中断在此。这个Bug通过调试模式可以发现;或者发现程序没有报错,但也并没有运行至期望的语句时可能出现。
- cnum: 除非GLOBMEM,否则没用。GLOBMEM时表示Process的序列标示,互不相同即可。
Server是一种特殊的进程,其写法与进程类似。*svr 文件。
#Server
# Name Buffer Type Host 0ps server timeout master cnum
P ServerAsvr MessageA LOCAL 172.23.100.210 RW 1 0.1 1 51
注意:
server的Type都是LOCAL,Host是server程序所在主机地址。Ops一般为RW操作,server为1,master为1即可。
NML通过使用两个通信通道的方法实现了命令和状态分离的通信方式。

命令通道和状态通道分离的通信方式仍然存在一个问题,那就是不能确定该命令到底有没有确实被接收方正确的接收,这需要一个数据应答。
RCS_CMD_CHANNEL类和RCS_STAT_CHANNEL类都是NML的子类。RCS_CMD_CHANNEL多了一个serial_number成员变量,RCS_STAT_CHANNEL多了个echo_serial_number成员变量。启用CMD命令通道发送命令前,对serial_number进行一个赋值(int),然后执行write()方法后,调用STAT状态通道,判断echo_serial_number成员变量是否和设置的serial_number成员变量相同,如果相同那么数据正确发送的接收方。对接收方而已,对CMD命令通道执行read()方法后,将serial_number成员变量赋值给自身echo_serial_number成员变量,执行完对应命令处理后,执行write()方法发送状态数据
继承类头文件
#ifndef MY_MESSAGE_H_
#define MY_MESSAGE_H_
#include "rcs.hh"
#define MY_MSG_TYPE_1 101 // 消息1
class MY_MSG: public NMLmsg
{
public:
MY_MSG():NMLmsg(MY_MSG_TYPE_1, sizeof(MY_MSG)){c = '0';d = 0.0;};
void update(CMS *);
char c;
double d;
};
extern int MyMsgFormat(NMLTYPE type, void *buffer, CMS *cms);
#endif
继承类源文件
#include "myMessage.hh"
#define MY_MSG_LIST_LEN 2
const NMLTYPE my_msg_id_list[MY_MSG_LIST_LEN] = {
MY_MSG_TYPE_1, /* 101 */
-1,
};
const size_t my_msg_size_list[MY_MSG_LIST_LEN] = {
sizeof(MY_MSG),
0,
};
void MY_MSG::update(CMS *cms)
{
cms->update(c);
cms->update(d);
}
int MyMsgFormat(NMLTYPE type, void *buffer, CMS *cms)
{
type = cms->check_type_info(type, buffer, "my_msg", // 在配置文件中定义格式信息前缀
(cms_symbol_lookup_function_t)0,
(const char **)0,
my_msg_id_list, my_msg_size_list,
MY_MSG_LIST_LEN,
0);
switch(type)
{
case MY_MSG_TYPE_1:
((MY_MSG *)buffer)->update(cms);
break;
default:
return 0;
}
return 1;
}
Receive进程作为Master进行接收数据
#include "rcs.hh"
#include "myMessage.hh"
#include <iostream>
int main(int argc, char *argv[])
timer.wait();
}
}
Send进程作为非Master进程,进行数据的发送
#include "rcs.hh"
#include "myMessage.hh"
#include <iostream>
int main(int argc, char *argv[])
{
NML test_nml(MyMsgFormat, "my_share_buff", "send_proc", "my_cfg.nml");
MY_MSG my_msg;
while(1)
{
std::cin >> my_msg.d;
std::cout << my_msg.d << std::endl;
test_nml.write(my_msg);
}
}
官方网站:
https://www.nist.gov/intelligent-systems-division/rcs-real-time-control-systems-architecture
RCS文档汇总:
https://www.nist.gov/el/intelligent-systems-division-73500/networked-control-systems-group/real-time-control-systems
RCS库安装指南:
https://www.nist.gov/el/intelligent-systems-division-73500/networked-control-systems-group/rcs-library-installation
RCS整体方法思想:
https://www.nist.gov/el/intelligent-systems-division-73500/cognition-and-collaboration-systems/rcs-methodology-overview
RCS库介绍:
https://www.nist.gov/el/intelligent-systems-division-73500/networked-control-systems-group/real-time-control-systems
RCS可视化设计工具介绍:
https://www.nist.gov/el/intelligent-systems-division-73500/networked-control-systems-group/rcs-design-tool-instructions
NML模块介绍:
https://www.nist.gov/el/intelligent-systems-division-73500/networked-control-systems-group/nml-module
NML开发手册:
https://www.nist.gov/el/intelligent-systems-division-73500/networked-control-systems-group/nml-programmers-guide-c







