xuos-web/docs/doc/framework/kong.md

306 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 控 - 控制框架
工业生产中控制逻辑的复杂程度千变万化,往往需要具体行业专业人员完成专门的设计,从而提高了行业的技术壁垒,严重阻碍了工业领域的自动化和智能化升级。
XiUOS 应用程序框架中的“控”子框架从“控制需求”本身出发同时面向专业用户和非专业用户通过对“控制需求”本身和复杂的工业控制流程进行深入分析通过软件定义的方式提出以“控制元”为核心的“控”制流程。具体地本设计通过解耦复杂的工业控制流程将工业生产中的各种复制工业控制流程分解为各种类型的“控制元”命令这些“控制元”的命令以软件API的形式交互给用户使用从而屏蔽了以 PLC 为中心的各种控制器的巨大差异,形成了方便易用的接口,降低了专业的技术壁垒,加速了工业领域的智能化升级。
## 1. XiUOS“控”制框架的关键数据结构定义和解析
```c
struct xs_PlcAbility {
const char name[XS_NAME_MAX]; /* name of the PLC ability instance */
enum xs_PlcCtlType type; /* type of control the plcable to excute, such as HSC or PID control */
char address[XS_PLC_ADDRESS_MAX]; /* The address for this function in the PLC*/
struct xs_PlcDevice *pdev; /* corresponding PLC device */
struct XS_DOUBLE_LINKLIST_NODE link;/* link list node */
};
```
name成员是一个可读的名字用于唯一标识一个xs_PlcAbility结构。
type成员表示该xs_PlcAbility可控制的类型用一个枚举变量表示
```c
enum xs_PlcCtlType {
XS_PLC_CONTROL_TYPE_HSC = 0, /* high-speed counter */
XS_PLC_CONTROL_TYPE_PID , /* proportional,integral,derivative */
XS_PLC_CONTROL_TYPE_PHASING , /* phasing control*/
/* ...... */
XS_PLC_CONTROL_TYPE_END,
};
```
由于 PLC 里控制指令执行都是向数据模块DB写数据,需要知道该函数功能对应的数据块地址这个用address标识。pdev成员表示该xs_PlcAbility所属的xs_PlcDevice结构其具体定义在下文给出。 最后在系统中不同控制类型的xs_PlcAbility被分别组织成不同的双链表如高速计数器xs_PlcAbility链表、PID控制xs_PlcAbility链表等使用的链表节点即为link成员。
```c
struct xs_PlcDevice {
const char name[XS_NAME_MAX]; /* name of the device */
struct xs_PlcInfo info; /* PLC info, such as vendor name and model name */
struct xs_PlcOps ops; /* filesystem-like APIs for data transferring */
struct xs_PlcInterface interface; /* protocls used for transferring data from program to PLC */
structXS_DOUBLE_LINKLIST_NODE link; /* link list node */
};
```
name成员记录 PLC 设备在系统中的名字用于唯一标识一个xs_PlcDevice结构。
```c
struct xs_PlcInfo {
uint32_t ability;
const char *vendor;
const char *product_model;
};
```
info成员记录 PLC 设备的一些属性信息,包括 PLC 的能力ability、厂家名vendor与型号product_model其中ability用一个位图表示该 PLC 设备可以控制进行的操作:
```c
#define XS_PLC_ABILITY_HSC ((uint32_t)(1 << XS_CONTROL_TYPE_HSC))
#define XS_PLC_ABILITY_PID ((uint32_t)(1 << XS_CONTROL_TYPE_PID))
#define XS_PLC_ABILITY_Phasing ((uint32_t)(1 << XS_CONTROL_TYPE_PHASING))
/* ...... */
```
ops成员包含统一的、类似文件系统的API用于和 PLC 设备通信,进行实际的数据读写和对 PLC 设备实现控制功能。在使用一个 PLC 设备前后需要打开open/关闭close该 PLC实际为建立和关闭连接read、write分别用与从 PLC 接收数据与向 PLC 发送数据ioctl用于向 PLC 设备发送控制指令:
```c
structxs_PlcOps {
int (*open)(struct xs_PlcDevice *pdev);
void (*close)(struct xs_PlcDevice*pdev);
int (*read)(struct xs_PlcDevice*pdev, void *buf, size_tlen);
int (*write)(struct xs_PlcDevice*pdev, constvoid *buf, size_tlen);
int (*ioctl)(struct xs_PlcDevice*pdev, intcmd, void *arg);
};
```
interface成员表示用于与 PLC 进行通信时所用到的协议:
```c
struct xs_PlcInterface {
enum xs_plc_protocol protocol;
enum xs_plc_transport transport;
};
```
xs_plc_protocol和xs_plc_transport是两个枚举类型标识 PLC 设备和自研的两种终端之间用到的通讯协议,其定义如下:
```c
enum xs_plc_protocol{
AB_ETH = 0,
ADS_AMS,
BACnet_IP,
DeltaV,
DF1,
EtherNet_IP,
Firmata,
KNXnet_IP,
Modbus,
OPC_UA,
S7,
Simulated,
};
enum xs_plc_transport{
TCP = 0,
UDP,
Serial,
Raw_Socket,
PCAP_Replay,
};
```
:::tip
注意两者间有对应关系而不是随意组合如S7(STEP 7)只能采用TCP协议而Modbus支持tcp/serial/raw socket/pcap replay可以定义一个函数检查类型
xs_PlcProtocolCheck(struct xs_PlcDevice*);
:::
最后,系统中所有注册过的 PLC 设备被组织成一个双链表即link成员。
## 2.XiUOS PLC 控制框架驱动开发
以HSC高速计数器为例。控制框架针对每个具体的控制类型将xs_PlcAbility进行扩充采用类似面向对象的手段添加其他必要成员
```c
struct xs_PlcAbilityHsc {
struct xs_PlcAbility parent;/* inherit from xs_PlcAbility*/
uint32_t (*write)(struct xs_PlcAbilityHsc *abl, void* param);
void (*read)(struct xs_PlcAbilityHsc *abl, void* param)
};
```
实现xs_PlcOps中的数据通信API具体实现细节取决于 PLC 型号无法实现的API可以置为NULL
```c
structxs_PlcOpshsc_example_ops = {
.open = hsc_example_open;
.close = hsc_example_close;
.read = hsc_example_read;
.write = hsc_example_write;
.ioctl = hsc_example_ioctl;
};
```
实现xs_PlcAbilityHsc中的write和read接口该接口用于向 PLC 的HSC模块发送控制参数和读取返回参数。在实现过程中可以使用xs_PlcOps中的接口与 PLC 进行通信。
其中param为一个void类型指针由于要写入的命令参数和要读取的返回参数往往不止一个可以根据不同的控制类型定义不同的数据读写结构体最后使用结构体指针进行强制类型转换即可。例如对于HSC
定义数据写入结构体:
```c
struct hsc_write{
char id[id_max]; /* id_max is to identify the max HSC ctrl instruction of HSC */
int cmd; /* HSC control type */
};
```
其中id用来标识HSCcmd表示要发送的具体指令具体的指令可以用一个枚举类型来表示
```c
enum HscCmdSubType{
EnHsc = 0, /* enable HSC */
EnCapture, /* enable the function of capturing the inputs */
EnSync, /* enable the function of synchronous inputs */
EnHSCRESET,
En_CTRL_UPTODOWN ,
... ...
};
```
同理,其他的类型
```c
enum PIDcmdSubType{
ENHsc = 0,
EN_XS_PLC_CONTROL_TYPE_PID_COMPACT,
XS_PLC_CONTROL_TYPE_PID_3STEP , /* valve of motor-driven */
... ...
};
enum PHASEcmdSubType{
ENPhase = 0,
XS_PLC_CONTROL_TYPE_PHASING_MC_HALT,
XS_PLC_CONTROL_TYPE_PHASING_MC_HALT_MOVE_ABSOLUTE,
... ...
}
```
定义数据输出结构体:
```c
struct hsc_read{
uint32_t value; /* the main value you want to get, depends on the cmd */
bool done; /* indicate whether the SFB has been done */
bool busy; /* indicate whether the function is busy */
bool error; /* indicate whether there is error */
};
```
value是指定返回的变量值与结构体hsc_write发送的指令cmd类型有关down/busy/error是一些状态指示位。
如我们要使用write函数向 PLC 发送HSC类型的控制指令
```c
struct hsc_write write_example;
```
struct xs_PlcAbilityHsc hsc_example进行必要的初始化后用强制类型转换作为参数传递给write函数
```c
hsc_example ->write(&hsc_example,(struct hsc_write*)&write_example);
```
最后,将 PLC 设备添加到 PLC 框架。分别填充xs_PlcDevice与对应物理量的xs_PlcAbility结构高速计数器即为xs_PlcAbilityHsc)并依次使用xs_PlcDeviceRegister和xs_PlcAbilityRegister函数将其注册到 PLC 框架:
```c
int xs_PlcDeviceRegister (struct xs_PlcDeviceRegister *pdev);
int xs_PlcAbilityRegister (struct xs_PlcAbilityRegister *abl);
extern struct xs_PlcOps plc_example_ops;
extern uint32_t plc_example_write(struct xs_PlcAbilityHsc *abl, void *param);
extern void plc_example_read(struct xs_PlcAbilityHsc *abl, void *param);
/* declare xs_PlcDevice and xs_PlcAbilityHsc objects */
struct xs_PlcDevice hsc_example_pdev;
struct xs_PlcAbilityHsc hsc_example_abl;
void register_example_plc()
{
/* initialize and register the xs_PlcDevice object */
memset(&hsc_example_pdev, 0, sizeof(xs_PlcDevice));
hsc_example_pdev.name = "plc1";
hsc_example_pdev.info.ability |= XS_PLC_ABILITY_HSC;
hsc_example_pdev.info.vendor = "Siemens";
hsc_example_pdev.info.product_model = "yyy";
hsc_example_pdev.ops = &hsc_example_ops;
hsc_example_pdev.interface.xs_plc_protocol = S7;
hsc_example_pdev.interface.xs_plc_transport = TCP;
xs_PlcDeviceRegister(&hsc_example_pdev);
/* initialize and register the xs_PlcAbility of hsc object */
memset(&hsc_example_abl, 0, sizeof(xs_PlcAbilityHsc));
hsc_example_abl.parent.name = "hsc_1";
hsc_example_abl.parent.type = XS_PLC_TYPE_HSC;
hsc_example_abl.parent.pdev = &hsc_example_pdev;
hsc_example_abl.read = hsc_example_read;
hsc_example_abl.write = hsc_example_write;
xs_PlcAbilityRegister((struct xs_PlcAbility *)&hsc_example_abl);
/* initialize and register otherxs_PlcAbilityobject */
memset(&other_example_abl, 0, sizeof(xs_PlcAbilityOther));
... ...
}
```
## 3. XiUOS PLC 控制框架的使用示例
PLC 控制应用开发者使用 PLC 控制框架提供的API操作 PLCPLC 的API可以分为通用API与控制类型特有API。通用API用于 PLC 的获取、打开与关闭控制类型特有API用于不同种类 PLC 的不同控制指令。以具有HSC高速计时器功能的 PLC 为例:
```c
/* generic API: find a plcability instance by its name */
struct xs_PlcAbility *xs_PlcAbilityFind(const char *name);
/* generic API: open/close a plc ability instance */
int xs_PlcAbilityOpen(struct xs_PlcAbility *abl);
void xs_PlcAbilityClose(struct xs_PlcAbility *abl);
/* HSC API: send control parameter to PLC and read HSC condition*/
uint32_t xs_PlcHscWrite(struct xs_PlcAbilityHsc *abl, void *param);
void xs_PlcHscRead(struct xs_PlcAbilityHsc *abl, void *param);
```
在发送命令和获取数据前,需要先获取并打开要使用的 PLCPLC 打开后可以随时对 PLC 发送指令和对其数据进行读取;使用完毕后,须关闭 PLC。完整的使用过程示例如下
```c
int main(int argc, char *argv[])
{
int ret;
structxs_PlcAbility *abl;
structxs_PlcAbilityHsc *hsc_abl;
/* get the Plc hsc ability instance */
abl = xs_PlcAbilityFind("hsc_1");
XS_ASSERT(abl->type == XS_PLC_CONTROL_TYPE_HSC);
/* open the Plc hscability instance */
hsc_abl = (struct xs_PlcAbilityHsc*)abl;
ret = xs_PlcAbilityOpen(abl);
XS_ASSERT(ret == XS_EOK);
/* initialize the write and read data structure */
structhsc_write write1={
.id = "xxx";
.cmd = EnHsc;
};
struct hsc_read read1;
/* send control param to the HSC, just for demonstration */
xs_PlcHscWrite(hsc_abl,write1);
/* read data from hsc, just for demonstration */
xs_PlcHscRead(hsc_abl,read1);
if(!read1.error)
xs_kprintf("Read data from PLC HSC is \n", read1.value);
else
xs_kprintf("Read data from PLC HSC wrong!\n")
xs_PlcAbilityClose(abl);
return 0;
}
```