施耐德开放自动化平台编程笔记(1)

本文记录如何在施耐德 EAE 开法环境下,如何构建一个 MQTT 功能块,通过外部的一个Soft Gateway 实现MQTT 通信。

概述

  任何一个控制系统的开发提供的功能块或者程序库都是有限的。在实际应用中经常会感觉少了一些库和功能块。开放性系统的好处在于提供了一整套工具,让用户,或者第三方开发者参与开发功能块和支撑服务。基于IEC61499 功能块技术的施耐德EAE 就是这样的系统。用户可以编写自己的功能块。EAE 编写功能块特性如下:

用户可编写的功能块

  •    IEC61499基本功能块(BASIC FUNCTION BLOCK)
  •    复合功能块(Conposite Function Block)
  •  子应用 (Sub Application)

用户使用IEC61131-3 的ST语言(Structured Text) 编写功能块内部的算法。

用户编写的功能块可以在EAE 中直接编译,下载到运行时中运行。

      用户不需要任何干预。使用起来比较方便。至于编译是在EAE 中完成的,还是在运行时中完成的,目前不得而知。

MQTT 网关实现

施耐德EAE 没有提供MQTT 协议的功能块。我们尝试使用EAE 现有的功能块和编写功能块的方法,自己来构建一个MQTT 软件网关(Soft Gateway)。具体的思路如下

使用已有的NETIO 功能块实现与外部程序的TCP 通信。外部程序(Soft Gateway)将TCP 数据转化成为 MQTT 消息发布。另外一个外部程序(GatewayClient订阅该主题的消息。

为了实现IEC61499 功能块网中的数据打包成TCP 数据块。我们编写了一个基本功能块(package)。整个实验的结构如下:

MQTT 代理使用了公网上的一个公开的代理 broker.emqx.io  。你也可以在自己的电脑上安装一个Mosqquitto MQTT Broker。Soft Gateway和MQTT Client 都是为了本次测试临时编写的。使用Go 语言编写。

 

 

功能块package 的设计

功能块package 是一个IEC61499 基本功能块。实现将MQTT Topic 和counter 数据组成一个TCP 包。由于NETIO 是完成数据的透明TCP 传输。没有定义更高级的数据格式。我们在这里简单地使用IEC61499 标准推荐的ASN.1 数据格式。有关ASN.1 的更多信息可以参阅我的博文-关于IEC61499 的数据交换信息抽象语法ASN.1

建立基本功能块

在EAE 左边项目树中选择Basic 击右键,选择 New Item

出现如下画面,添加一个topic 和VAL1 输入两个数据输入脚,点击REQ 的With 框,选择将这两个输入与REQ 关联。类似方式添加OUT和OUT_LEN 两个数据 输出,并且与CNF 关联

REQ 的算法(ST)

点击工作区中的Algorithms。选择REQ 事件的算法。使用ST语言编写。

ALGORITHM REQ IN ST:
(* Add your comment (as per IEC 61131-3) here
Normally executed algorithm
*)
VARBUF:ARRAY [64] OF BYTE;INDEX:UINT;i:INT;n:DINT;END_VAR
INDEX:=0;
n:=LEN(TOPIC);
BUF[INDEX]:=86;
INDEX:=INDEX+1;
BUF[INDEX]:=0;
INDEX:=INDEX+1;
BUF[INDEX]:=DINT_TO_BYTE(n);
INDEX:=INDEX+1;
i:=1;
REPEAT
BUF[INDEX]:= CHAR_TO_BYTE(TOPIC[i]);
INDEX:=INDEX+1;
i:=i+1;
UNTIL i > n 
END_REPEAT;BUF[INDEX]:=70;INDEX:=INDEX+1;BUF[INDEX]:=UINT_TO_BYTE(VAL1/256);INDEX:=INDEX+1;BUF[INDEX]:=UINT_TO_BYTE(VAL1);INDEX:=INDEX+1;OUT_LEN:=INDEX;i:=0;OUT:='';REPEATOUT:=CONCAT (OUT,CHAR_TO_STRING(BYTE_TO_CHAR(BUF[i])));i:=i+1;UNTIL i>=OUT_LENEND_REPEAT; 
END_ALGORITHM

这个算法实现在OUT 数据中输出ASN.1 格式的字符串。有两个ASN.1 数据项,一个是STRING类型的Topic ,另一个是UINT 类型的Counter。

SoftGateway.go

package mainimport ("fmt""net""os"
//	"strings"
"strconv"mqtt "github.com/eclipse/paho.mqtt.golang"
)const (CONN_HOST = "localhost"CONN_PORT = "9201"CONN_TYPE = "tcp"
)
var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {fmt.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
}var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {fmt.Println("Connected to MQTT Broker")
}var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {fmt.Printf("Connect lost: %v", err)
}func main() {var broker = "broker.emqx.io"var port = 1883opts := mqtt.NewClientOptions()opts.AddBroker(fmt.Sprintf("tcp://%s:%d", broker, port))opts.SetClientID("go_mqtt_client")opts.SetUsername("emqx")opts.SetPassword("public")opts.SetDefaultPublishHandler(messagePubHandler)opts.OnConnect = connectHandleropts.OnConnectionLost = connectLostHandlerclient := mqtt.NewClient(opts)if token := client.Connect(); token.Wait() && token.Error() != nil {panic(token.Error())}//subscribe(client,"dPACWrite")// Listen for incoming connections.l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT)if err != nil {fmt.Println("Error listening:", err.Error())os.Exit(1)}// Close the listener when the application closes.defer l.Close()fmt.Println("Listening on " + CONN_HOST + ":" + CONN_PORT)for {// Listen for an incoming connection.conn, err := l.Accept()if err != nil {fmt.Println("Error accepting: ", err.Error())os.Exit(1)}// Handle connections in a new goroutine.go handleRequest(conn,client)}
}// Handles incoming requests.
func handleRequest(conn net.Conn ,client mqtt.Client) {// Make a buffer to hold incoming data.buf := make([]byte, 1024)//index:=0for { // Read the incoming connection into the buffer.cnt, err := conn.Read(buf)if err != nil {fmt.Println("Error reading:", err.Error())}fmt.Printf("message len=%d\n",cnt)// line := strings.TrimSpace(string(buf[0:cnt]))//          fmt.Println(line)len:=int(buf[1]<<8)|int(buf[2])fmt.Printf("topic len=%d",len)topicArray := make([]byte, len)for index:=0;index

MQTTClient.go

package mainimport ("fmt"mqtt "github.com/eclipse/paho.mqtt.golang""time"
)var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {fmt.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
}var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {fmt.Println("Connected")
}var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {fmt.Printf("Connect lost: %v", err)
}
func main() {var broker = "broker.emqx.io"var port = 1883opts := mqtt.NewClientOptions()opts.AddBroker(fmt.Sprintf("tcp://%s:%d", broker, port))opts.SetClientID("Gateway_client")opts.SetUsername("client")opts.SetPassword("public")opts.SetDefaultPublishHandler(messagePubHandler)opts.OnConnect = connectHandleropts.OnConnectionLost = connectLostHandlerclient := mqtt.NewClient(opts)if token := client.Connect(); token.Wait() && token.Error() != nil {panic(token.Error())}topic:="dPACWrite"token:=client.Subscribe("dPACWrite", 0, nil)token.Wait()fmt.Printf("Subscribed to topic: %s\n", topic)for {time.Sleep(time.Second)}
}

运行

EAE 的Soft dPAC 在windows 下运行。而SoftGateway 和MQTTClient 在同一台PC 中windows 10 下的ubuntu wsl 下运行。

本例子涉及的内容比较多,有问题,就添加在评论区中吧。如果没有特别明白,也没有关系,可以自己先尝试建立一些小的基本功能块。学习复杂系统最好的方法就是尝试。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部