如何扩展Shifu的西门子PLC驱动能力

在上篇中我们分享了《如何利用Shifu接入私有驱动的设备》。Shifu内置了几个常用的驱动,像是西门子S7系列的PLC,RTSP协议的摄像头等。物联网上的需求因人而异。在这次的文章中我们将介绍如何扩展Shifu自带驱动的能力。

简介
本文是一个使用Shifu Framework的西门子PLC驱动的扩展指南,其中包含Linux, Python, Shifu Framework, Docker, Kubernetes的基本操作,任何开发者都可以阅读本文来学习Shifu Framework的开发方法。
本文中的Shifu Framework架构如下

file

北向通过"deviceshifu-http-http”容器向上开放HTTP API接口,南向通过"siemens-plc-driver”容器来和实际设备交互

目标

  1. 在本地运行K8s集群并安装Shifu Framework
  2. 修改西门子PLC的驱动添加一个功能
  3. 打包驱动,生成容器镜像
  4. 在Shifu中部署PLC的数字孪生
  5. 实现扩展西门子PLC的能力

本次分享中用到的设备

  1. 开发环境(本文中为运行在Windows 11 Pro下面的WSL子系统,系统为Ubuntu 20.04)
  2. 西门子PLC(支持S7协议即可,本文中用到的型号为西门子S7-1200系列)

需要的基本知识
-基本的Python
-Linux命令行基本操作(创建文件,安装应用,运行程序)
-Docker/containerd基本操作
-K8s基本操作

步骤
第一步:在本地运行Kubernetes(如果已安装Shifu Framework,请直接跳到第三步)
为了运行Shifu,我们需要一个Kubernetes的集群,这里不限制用户使用的版本,本文中使用的是利用kind建立的测试Kubernetes集群。如果资源受限的话也可以考虑使用k3d或者microk8s。
kind的安装教程:
https://kind.sigs.k8s.io/docs/user/quick-start/#installation
具体安装步骤本文将不再赘述。
kind安装完毕后可以通过“kind version”来查看当前版本,本文中的版本为”v0.12.0”:

$ kind version 
kind v0.12.0 go1.17.8 linux/amd64

接下来使用“kind create cluster”创建集群,整个过程会持续几分钟,因网速而异:

$ kind create cluster 
Creating cluster "kind" ... 
 ✓ Ensuring node image (kindest/node:v1.23.4) 🖼 
 ✓ Preparing nodes 📦 
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-kind" 
You can now use your cluster with: 
 kubectl cluster-info --context kind-kind 
Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/

创建完成后我们可以通过“kubectl get nodes”查看集群状态,当显示“Ready”即可:

$ kubectl get nodes 
NAME                 STATUS   ROLES                  AGE    VERSION 
kind-control-plane   Ready    control-plane,master   119s   v1.23.4 

至此,运行K8s步骤完毕。

第二步:安装Shifu
首先将Shifu项目克隆到本地,项目地址为:
https://github.com/Edgenesis/shifu.git
执行命令为:
git clone https://github.com/Edgenesis/shifu.git

下面通过“kubectl apply -f shifu/k8s/crd/install/shifu_install.yml”即可一键将Shifu部署到k3s集群中:

$ kubectl apply -f k8s/crd/install/shifu_install.yml 
namespace/shifu-crd-system created 
customresourcedefinition.apiextensions.k8s.io/edgedevices.shifu.edgenesis.io created 
serviceaccount/shifu-crd-controller-manager created 
role.rbac.authorization.k8s.io/shifu-crd-leader-election-role created 
clusterrole.rbac.authorization.k8s.io/shifu-crd-manager-role created 
clusterrole.rbac.authorization.k8s.io/shifu-crd-metrics-reader created 
clusterrole.rbac.authorization.k8s.io/shifu-crd-proxy-role created 
rolebinding.rbac.authorization.k8s.io/shifu-crd-leader-election-rolebinding created 
clusterrolebinding.rbac.authorization.k8s.io/shifu-crd-manager-rolebinding created 
clusterrolebinding.rbac.authorization.k8s.io/shifu-crd-proxy-rolebinding created 
configmap/shifu-crd-manager-config created 
service/shifu-crd-controller-manager-metrics-service created 
deployment.apps/shifu-crd-controller-manager created 
namespace/devices created 
serviceaccount/edgedevice-sa created 
clusterrole.rbac.authorization.k8s.io/edgedevice-clusterrole created 
clusterrolebinding.rbac.authorization.k8s.io/edgedevice-clusterrolebinding created

至此,Shifu安装完毕。

第三步:修改西门子PLC驱动来添加新的功能

在IDE中打开Shifu文件夹,本文使用的是VS Code。在"examples/siemensPLCDeviceShifu”目录下打开"siemens-plc.py”:
file

驱动内容如下(因长度问题只截取了一部分):

import os 
import sys 
import snap7 
from flask import Flask, request 
client = snap7.client.Client() 
app = Flask(__name__) 
ip = os.environ.get("PLC_ADDRESS") 
port = os.environ.get("PLC_CONTAINER_PORT") 
rack = os.environ.get("PLC_RACK") 
slot = os.environ.get("PLC_SLOT") 
def edit_single_bit(originalbyte, digitvalue, isset): 
    if digitvalue > 7: 
        digitvalue -= 8 
        changebyte = originalbyte[1] 
        if isset == 0: 
            return bytes([originalbyte[0]]) + bytes([changebyte & ~(1 << digitvalue)]) 
        else: 
            return bytes([originalbyte[0]]) + bytes([changebyte | (1 << digitvalue)])
    else: 
        changebyte = originalbyte[0] 
        if isset == 0: 
            return bytes([changebyte & ~(1 << digitvalue)]) + bytes([originalbyte[1]]) 
        else: 
            return bytes([changebyte | (1 << digitvalue)]) + bytes([originalbyte[1]]) 
@app.route('/sendsinglebit') 
def send_single_bit(): 
print("Changing single bit...") 

可以看到这个西门子PLC驱动是一个Python程序,通过调用”snap7”这个API来实现和西门子PLC的通信,向上通过Flask开放了若干个HTTP接口来供Shifu调用。

我们主要看一下关于修改PLC内存值的“send_single_bit”这个函数,首先,是这个API接收的参数:

 rootaddress = request.args.get('rootaddress') 
 address = request.args.get('address', default=0, type=int) 
 start = request.args.get('start', default=0, type=int) 
 digit = request.args.get('digit', default=0, type=int) 
 value = request.args.get('value', default=0, type=int)

“rootaddress”定义了驱动写入的类型,目前接受的类型如下:

if rootaddress == 'M': 
        area = snap7.types.Areas.MK 
elif rootaddress == 'Q': 
        area = snap7.types.Areas.PA 
elif rootaddress == 'C': 
        area = snap7.types.Areas.CT 
elif rootaddress == 'T': 
        area = snap7.types.Areas.TM

可以看到目前支持的”M”, “Q”, “C”, “T”四种类型,除了这四种以外,S7还支持”PE”, “DB”这两种。今天我们就来添加”DB”的支持。

首先添加一个新的”rootaddress”的检查,当值为”DB”的时候将”area”变量设为”snap7.types.Areas.DB”:

代码如下:


 elif rootaddress == 'DB': 
        area = snap7.types.Areas.DB

至此,驱动修改完毕

第四步:打包驱动并运行PLC的数字孪生(deviceShifu)

接下来我们对这个驱动进行打包,在“siemensPLCDeviceShifu”中有一个现成的Dockerfile,我们直接利用它来对驱动进行打包,只需执行“docker build . -t edgenesis/plc-device:v0.0.1”即可:

shifu/examples/siemensPLCDeviceShifu$ docker build . -t edgenesis/plc-device:v0.0.1 
[+] Building 65.0s (10/10) FINISHED                                                                                                                                                             
 => [internal] load build definition from Dockerfile                                                                                                                                      0.0s 
 => => transferring dockerfile: 298B                                                                                                                                                      0.0s 
 => [internal] load .dockerignore                                                                                                                                                         0.0s 
 => => transferring context: 2B                                                                                                                                                           0.0s 
 => [internal] load metadata for docker.io/library/python:3.9-slim-bullseye                                                                                                              11.4s 
 => [auth] library/python:pull token for registry-1.docker.io                                                                                                                             0.0s 
 => [1/4] FROM docker.io/library/python:3.9-slim-bullseye@sha256:dea558731860898a00ac0d004fcd67fff6a0a0d420af15d7ec4289fac5ab3df5                                                         8.5s 
 => => resolve docker.io/library/python:3.9-slim-bullseye@sha256:dea558731860898a00ac0d004fcd67fff6a0a0d420af15d7ec4289fac5ab3df5                                                         0.0s 
 => => sha256:1078a59cbb2c5273b0f18e6ad6b78aa01298be2b3a54365ed7ddf3b5d68f5c54 1.37kB / 1.37kB                                                                                            0.0s 
………………………………………. 
 => => extracting sha256:8c7a905e65a9f4baafcf16861113440e68c6144d7c3a7e701482086992492f70                                                                                                 0.2s 
 => [internal] load build context                                                                                                                                                         0.0s 
 => => transferring context: 3.91kB                                                                                                                                                       0.0s 
 => [2/4] COPY requirements.txt .                                                                                                                                                         0.2s 
 => [3/4] RUN pip install --no-cache-dir -r requirements.txt                                                                                                                             44.5s 
 => [4/4] COPY siemens-plc.py .                                                                                                                                                           0.0s 
 => exporting to image                                                                                                                                                                    0.2s 
 => => exporting layers                                                                                                                                                                   0.2s 
 => => writing image sha256:9c5802b3e55e0b9b498dc05be642b4aff1c60098531156af747469cc795a7120                                                                                              0.0s 
 => => naming to docker.io/edgenesis/plc-device:v0.0.1 

因为是在本地执行的,所以让我们将这个驱动的镜像载入到kind集群中,命令为“kind load docker-image edgenesis/plc-device:v0.0.1”:

$ kind load docker-image edgenesis/plc-device:v0.0.1 
Image: "edgenesis/plc-device:v0.0.1" with ID "sha256:9c5802b3e55e0b9b498dc05be642b4aff1c60098531156af747469cc795a7120" not yet present on node "kind-control-plane", loading

在“examples/siemensPLCDeviceShifu/plc-deployment”文件夹中我们提供了四个文件,可以将PLC的数字孪生一键部署到Kubernetes集群中:

├── plc-deviceshifu-configmap.yaml 
├── plc-deviceshifu-deployment.yaml 
├── plc-deviceshifu-service.yaml 
└── plc-edgedevice.yaml

首先我们要修改PLC设备的地址,本教程中的PLC设备地址在“192.168.0.1”,我们需要修改“plc-deviceshifu-deployment.yaml”中的”PLC_ADDR”环境变量来让我们的容器使用这个IP来进行连接:

- name: PLC_ADDR 
 value: "192.168.0.1" 

改好后,我们就可以通过“Kubectl apply -f examples/siemensPLCDeviceShifu/plc-deployment”来部署

$ kubectl apply -f examples/siemensPLCDeviceShifu/plc-deployment/ 
configmap/plc-configmap-0.0.1 created 
deployment.apps/edgedevice-plc-deployment created 
service/edgedevice-plc created 
edgedevice.shifu.edgenesis.io/edgedevice-plc created

接下来让我们运行一个nginx应用来演示如何与PLC进行交互,命令为:

curl "edgedevice-plc/sendsinglebit?rootaddress=DB&address=<address>&start=<start>&digit=<digit>&value=<0/1>" 

最后一步:运行效果

root@nginx:/# curl "edgedevice-plc/sendsinglebit?rootaddress=DB&address=0&start=0&digit=0&value=1";echo 
Changed from bytearray(b’\x00\x00’) to bytearray(b’\x01x00’) 
root@nginx:/# curl edgedevice-plc/getcontent?rootaddress=Q;echo 
0b0000000000000001 

至此,教程结束

总结

在此篇文章中,我们通过扩展Shifu的西门子PLC驱动的能力,实现了远程操纵改写PLC中DB的值。

可以看到,通过对驱动能力的增加也增加了Shifu的能力。

本文中的更改并没有提交到GitHub中,如果是您需要的需求,或者您刚好看到,不妨来我们的GitHub提交一个PR😊第一个提交的人我们将会给您准备一份小礼物以示感谢

非常感谢您看到了这里,我们期待您的反馈,如果觉得文章写得不错或者有任何建议请毫不犹豫地留言。

本文由博客群发一文多发等运营工具平台 OpenWrite 发布

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容