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

311 lines
11 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
structxs_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))
/* ...... */
```
ps成员包含统一的、类似文件系统的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用来标识HSC, cmd表示要发送的具体指令具体的指令可以用一个枚举类型来表示
```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;
}
```