Python-Snap7与 1212 PLC通信并保存到sqlite3中

5 篇文章 7 订阅
2 篇文章 0 订阅

前言

为了远程监控和获取运行中控制相关参数,打算通过S7协议+树莓派,实现数据存储到数据库中。根据自己的实际经验,将步骤和细节一一记录,以便自己和与我一样的小白少走弯路。

配置

硬件

PLC: 西门子 1214C DC/DC/DC
树莓派:4B
路由器:中兴 E8820 V2
交换机:D-link EDS-1008D
硬件配置:路由器位于顶层(192.168.5.1),接树莓派(192.168.5.4),交换机,PC电脑;1200PLC (192.168.5.100)接入交换机。

软件

win10专业版
博途软件:V15.1
树莓派:4.19.75-v71 2019-09 raspbain Gun/Linux 10(buster)
python版本 :3.7.4
Sqlite3版本:3.272
Putty版本:0.73

软件配置过程

树莓派系统安装

系统安装可以参考以下文章:
1)折腾向树莓派3B+安装系统(Raspbian)以及配置环境
2)树莓派3B+无显示器配置详细教程

树莓派固定IP设置

IP地址设置困扰了我很久,最终在hututu_404分享的《树莓派手动指定静态IP和DNS 终极解决大法》中得到解决,下面只依次给出设置文件的截图。

python更新i

具体参考《将树莓派内置的 Python2.7 升级成 Python3》一文。
在这里插入图片描述

pip 安装

sudo apt update
sudo apt install python3-pip
pip3 --version
pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7)

snap7安装

1)下载 snap7源文件包
2)解压并拷贝到优盘
3)拷贝到树莓派中

pi@raspberrypi:~ $ ls /dev/sd*/dev/sd1
pi@raspberrypi:~ $ sudo mkdir testusb
pi@raspberrypi:~ $ sudo mount -o uid=pi,gid=pi /dev/sda1 testusb
pi@raspberrypi:~$ sudo cp -r /testusb/snap7-full-1.4.2 /usr

4)编译

pi@raspberrypi:~$ cd /usr/snap7-full-1.4.2/snap7-full-1.4.2/build/unix
pi@raspberrypi:~$  sudo make –f arm_v6_linux.mk all
  1. 拷贝libsnap7.so
pi@raspberrypi:~$  cd ../bin/arm_v6-linux/
pi@raspberrypi:~$  sudo cp libsnap7.so /usr/lib
pi@raspberrypi:~$  sudo cp libsnap7.so /usr/local/lib
pi@raspberrypi:~$  sudo sudo ldconfig
pi@raspberrypi:~$  pip install python-snap7

备注:采用 arm_v7_linux编译后也能再树莓派4B上使用。

sqlite3安装

参考树莓派 Raspberry 3B+ C-sqlite3 开发环境搭建一文。
在这里插入图片描述

数据库设计

S7 数据类型

西门子PLC常用的数据存储区,输入,输出,寄存器,DB块,计数器,计时器。如下图:

在这里插入图片描述
数据存储依次有位bit,字节Byte,字word,双字Dword,浮点数real,计数器Counter,计时器,Timer。其存储长度如下表:

在这里插入图片描述

数据存储方式

不同与普通数据的传输,s7采用大端模式。具体内容见前面文章介绍。

数据库类别选择

开源数据库有sqlite,mysql等,因为应用基于嵌入式,在每天自动备份,可以跨平台并无安装,sqlite满足要求。
在这里插入图片描述

数据库结构

1)变量表
PLC输入变量表(plc_tags_Input)
PLC输出变量表(plc_tags_output)
PLC M寄存器(plc_tags_M)
PLC DBX块(plc_DB_X)
2)运行数据存储表
PLC输入输出存储表(plc_tags_running_record)
PLC DB块存储表(plc_DB_running_record)

数据表结构

变量数据表必须包含:序列号ID,变量名称,变量PLC地址,变量数据类别,变量起始地址,变量数据长度,备注说明等。
在这里插入图片描述
运行数据存储表包含:ID,时间,内容。
在这里插入图片描述

数据存储

项目运行是7*24h,所以需要将数据存储尽量最小化。因Snap7获取最小数据单位为byte,而不是bit,对于数字量输入输出DI和DO等bit位,可以采用每8个位一起,直接用读取数据。而其他类型数据例如int,real也需用直接读取数据存储。
数据存储采用类似JSON格式,例如:I0:0x00;I1:0xf1;…

PLC数据读入Sqlite3

打开数据库

先打开需要存储的数据库,建立游标。

import sqlite3
conn = sqlite3.connect('test.sqlite')
cur = conn.cursor()

取要读入PLC数据的表单

执行查询语句

cur.execute("SELECT *  FROM plc_tags_output;")

连接PLC

配置PLC
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

引入snap7库,创建连接,提交IP地址机架号,即可完成。其中IP地址、机架号等信息可以存储到数据库中。

import snap7
plc=snap7.client.Client()
plc.connect('192.168.5.100',0,1)

从PLC中获取数据

我们可以采用read_area函数、ab_read函数(输出)、DB_read函数读取PLC中数据值
参考手册这里不再举栗子。

数据后续处理

获取数据是大端排序数据,还需要进行相关处理才能识别。
对于布尔型 bool ,用>B

对于整型 int word,用>h 注意python下不能用>i

对于双字长整型 Dint Dwrod 用>i

对于浮点型real >f

若为了高效存储性能,例如运行过程数据存储,可以不进行相关解析成能读懂数据。

数据写入PLC

Bit特定位置0

方法:构造一个需要置0的特定位为0,其他位为1的数,再与被操作数进行&操作
例如:需要将1byte(00001111)的0-2位置0,先分别将1右移动0位、1位、2位,他们之间先取或然后再取反,就构造出特定位数,再与操作就行
a &= (~((1<<0) | (1<<1) | (1<<2)));
可以构造以下python函数

#将1byte二进制数中的某一位 置0
#int_num 为被操作数---整型
#postion 需要置为位置---整型

def single_Set_0_in_Byte(int_num, postion):
	return int_num&(~(1<<(postion)))

#将1byte二进制数中的多位 置0 必须填写一个位置
def mult_Set_0_in_Byte(int_num, postion1,*postions):
	temp=1<<postion1
	for i in postions:
		temp|=(1<<i)
	return int_num&(~temp)	

Bit特定位置1

方法:构造一个特定位为1,其他位为0的数,再与被操作数进行|操作
例如:需要将1byte(00001111)的0,2位置1,先分别将1右移动0位和2位,他们之间先取或,就构造出特定位数,再进行与或操作
a|=((1<<0)|(1<<2));
可以构造以下python函数

#将1byte二进制数中的某一位 置1
#int_num 为被操作数---整型
#postion 需要置为位置---整型
def single_Set_1_in_Byte(int_num, postion):
return int_num|((1<<(postion)))

Bit特定位取反

方法: 构造一个特定位为1,其他位为0的数,再与被操作数进行^操作
例如:需要将1byte(00001111)的0,5取反,先分别将1右移动0位和5位,他们之间先取或,就构造出特定位数,再进行与或操作
a^=((1<<0)|(1<<5));
可以构造以下python函数

#将1byte二进制数中的某一位 置1
#int_num 为被操作数---整型
#postion 需要置为位置---整型
def single_Set_Negate_in_Byte(int_num, postion):
return int_num^((1<<(postion)))

bit 写入

Bit数据中有一些是IO输入输出或者snap7 获取最小单位为byte,所以需要根据实际情况进行处理。

1)数字量输出DO
因只对某些位进行操作,所以需要先读取出PLC中现在值(以byte为单位),应用上面的Bit特定位操作函数处理后,再写入。
例如Q0.0为1,Q0.1为0,Q0.2为1,将Q0.2置为1
在这里插入图片描述

q01=plc.read_area(0x82,0,0,1) #q01为 bytearray(b'\x05'),而q01=plc.ab_read(0,1)运行结果一样
wq01=q01[0]&(~(1<<2)) # wq01为整型1(可以不用struct.unpack转换成整型)
wq01_b=struct.pack('>B',wq01) #wq01_b为b'\x01'(转化成二进制)
plc.write_area(0x82,0,0,wq01_b) #plc.ab_write(0,wq01_b)是一样结果

在这里插入图片描述

2)数字量输入DI
在实际工控系统中,数字量输入即使更改,还快被外部传感器状态覆盖。这种情况可以不考虑。

非bit数据写入

非bit数据是整型或者浮点型,写入只需要进行数据转换进行。

后续操作就非常简单了。

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值