Python-Snap7与 1212 PLC通信并保存到sqlite3中
前言
为了远程监控和获取运行中控制相关参数,打算通过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
- 拷贝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数据是整型或者浮点型,写入只需要进行数据转换进行。
后续操作就非常简单了。