采用snap7实现对PLC1200的I、Q、M、DB区域进行读写

强调:该部分的内容只在实际硬件上测试成功,采用PLCSIM造的假PLC通讯不上,后续有效的话再进行完善

1、PLC权限设置

安装的方式按照个人前一篇来,python-snap7的安装记录,出现意外再另寻方法吧,默认现在相关的软件已经安装完毕。
在建立通讯前需要再TIA内将PLC的读写权限给出,版本较低的1200 CPU没有下图的内容可以不设置,同样可以进行使用, 亲测有效
在这里插入图片描述在这里插入图片描述

2、资料参考

参考学习中参考了相关的学习资料,这里一并给出以供参考

考虑下面个人给出了我自己的内容,这里只截取我需要的内容到下面作为参考

  1. PLC的读写主要是通过read_area和write_area两个函数实现,个人理解的思路就是read_area(读出来)—修改—write_area(写进去)
  2. snap7对函数的定义:read_area(self,area,dbnumber,start,size)以及write_area(self,area,dbnumber,start,data),既然是read_area和write_area,因此两个函数的核心都是对area进行读写操作,area用于区分I、Q、M、DB区域,具体选择如下(写过函数都知道,self这里是不用的)
areas = ADict({
  'PE': 0x81,  #input 输入区
  'PA': 0x82,  #output 输出区
  'MK': 0x83,  #bit memory 中间存储区(M区)
  'DB': 0x84,  #DB'CT': 0x1C,  #counters
  'TM': 0x1D,  #Timers
})

dbnumber只有在对DB块使用时才有用,默认设置为0,start起始地址为Q0.4的这个0(也就是输出的第几个字节),size是选择读取的数据类型,见下图,(为什么选择地址这里没有说要给出Q0.4的4呢?是因为读取数据类型以byte起步,由参数size决定,要对位进行操作可以在读取后采用其他指令对具体某一位做读写
在这里插入图片描述
注意:write_area的前几个参数与read_area一致,但最后一个参数为data,其实就是read_area读取的数据,(先有read再有write)
3. 读了再写就是写,只读不写就是读 ------- PLC的Q区域读写操作

3、采用snap7 对PLC的输出进行读写

学习的资料是用了两个.py文件,一个专门用来构建读取和改写的函数,另一个则是用使用构造的函数进行读写操作,比较推荐这种,下次要用直接调用,一行代码实现读写
首先是plc.py文件(名字自己取,改了的话import时记得换)

from snap7.util import *  #对位操作的函数要导入该库

#定义的函数可直接对QX.X一个位进行操作
def Writeoutput(dev,byte,bit,cmd):
   data=dev.read_area(0x82,0,byte,1)#0x82表示输出Q,byte表示起始地址(Q0),1表示类型为byte,cmd位置的值(True或False)
   set_bool(data,byte,bit,cmd) #置Qbyte.bit(Q0.4)为cmd(即True)
   dev.write_area(0x82,0,byte,data)#同样,进行写数据操作
   
#该函数只进行了读操作
def Readoutput(dev,byte,bit):
   data=dev.read_area(0x82,0,byte,1)
   status=get_bool(data,byte,bit)  #获取位状态
   return status

其次是output.py文件(名字可以随便取)

import snap7
import plc as PLC  #导入前面的plc.py文件(注意两个py文件在一个文件夹)

myplc=snap7.client.Client()
myplc.connect('192.168.0.1', rack=0,slot=1)     #建立连接(相关信息去TIA看,IP,机架和插槽)
print(myplc.get_connected())     # 测试是否通讯成功

#使用plc.py里构建的两个函数(写、读)
PLC.Writeoutput(myplc,0,4,True) #该函数效果就是写Q0.4为True
status=PLC.Readoutput(myplc,0,4)  #该函数效果就是读取Q0.4的值
print(status)

效果:
在这里插入图片描述
注意

  1. 两个py文件最好放在一个文件夹,或灵活运用python的import
  2. 上面的代码是复杂化一点的的,对位读写如果单纯的编个最简单的代码的话只需要在一个py文件里按照顺序:建立通讯——read-area——set_bool/get_bool——write_area(读的话都不用)就能够实现读和写操作

4、采用snap7对PLC的中间存储区进行读和写

上面是分两个py文件,现在是把读取和写入分开,都是无关紧要的简单变换

4.1读取

直接上程序

 import snap7
from snap7.util import *
from snap7.snap7types import *

def ReadMemory(self,byte,bit,datatype):
  result=self.read_area(0x83,0,byte,datatype)
  if datatype==S7WLBit:  #这里的datatype应该可以写数字(参照上面写入output),或者像这里直接写类型
      return get_bool(result,0,bit)
  if datatype==S7WLByte or datatype==S7WLWord:
      return get_int(result,0)
  if datatype==S7WLReal:        #elif更好
      return get_real(result,0)
  if datatype==S7WLDWord:
      return get_dword(result,0)

myplc=snap7.client.Client()
myplc.connect('192.168.0.1', rack=0,slot=1)


print('bool型',ReadMemory(myplc,10,0,S7WLBit))
print('byte型',ReadMemory(myplc,11,0,S7WLByte))
print('Word型',ReadMemory(myplc,12,0,S7WLWord))
print('Real型',ReadMemory(myplc,14,0,S7WLReal))
print('Dword型',ReadMemory(myplc,18,0,S7WLDWord))

对应的输出:(显示的都是十进制显示)

bool型 True
byte型 4864  #除以256才是实际值
Word型 33
Real型 23.12310028076172
Dword型 7

TIA的显示:
在这里插入图片描述
注意: 可以看到MB11是错的,因为byte只有8位,十六进制是0x13,输出的4864是0x1300,即16位,还不清楚有没有专门的读8位的get函数,先用除以256代替实际值,即4864/256=19

4.2 写入

同样直接上程序

 import snap7
from snap7.util import *
from snap7.snap7types import *

def WriteMemory(self,byte,bit,datatype,value):
  result=self.read_area(0x83,0,byte,datatype)
  if datatype==S7WLBit:
      set_bool(result,0,bit,value)
  if datatype==S7WLByte or datatype==S7WLWord:
      set_int(result,0,value)
  if datatype==S7WLReal:
      set_real(result,0,value)
  if datatype==S7WLDWord:
      set_dword(result,0,value)
  self.write_area(0x83,0,byte,result)

myplc=snap7.client.Client()
myplc.connect('192.168.0.1', rack=0,slot=1)

WriteMemory(myplc,10,0,S7WLBit,False)
WriteMemory(myplc,11,0,S7WLByte,2048)  #除以256才是传输的值(8WriteMemory(myplc,12,0,S7WLWord,32)
WriteMemory(myplc,14,0,S7WLReal,3.14159)
WriteMemory(myplc,18,0,S7WLDWord,1132818)

TIA的显示
在这里插入图片描述
与Q区域的读写一样,如果单纯想要实现读写效果的话,实际的操作会更加简单,这里为了做充分的示例做了一些额外的代码完善工作,自己用时,可以灵活删减与组合

5、采用snap7对PLC的输入区进行读

注意我用的DC/DC/DC没法直接修改输入,不知道继电器的行不行(可测),因此这里只有读,没有写
然后就是直接上程序

import snap7
from snap7.util import *
from snap7.snap7types import *

def Readoutput(dev,byte,bit):
   data=dev.read_area(0x81,0,byte,S7WLBit)  #S7WLBit可以用1代替
   status=get_bool(data,byte,bit)
   return status

myplc=snap7.client.Client()
myplc.connect('192.168.0.1', rack=0,slot=1)
status=Readoutput(myplc,0,0)
print(status)   #输出False

6、采用snap7对PLC的DB数据块进行读和写

不管对DB干什么也要先有DB数据块,并且去掉块优化
在这里插入图片描述

6.1 DB块数据读取

对DB块的学习多花了一些时间,因为代码确实比较长,个人python基础又很差。关于读取DB数据块难点(其实也就是比前面难一点)在于是对DB块内的一块区域读取数据,相对于前面一个一个数据读取更麻烦。其主要的思路是要知道各个数据的起始位置,以及整个DB块的长度,然后逐个数据提取并输出。
先说我要读取的内容
在这里插入图片描述
可以看到内容包括:
1.DB块为DB10
2.数据名称,数据类型,数据偏移量
这两个信息是读取操作必须的信息,很重要

直接上代码(程序里面的解释应该比较详细了)

import snap7
from snap7.util import *
from snap7.snap7types import *


class DBObject(object):     #空对象,用于后面__setattr__使用
  pass

#offset是字典,其内容代表各类型数据所占字节长度
offset={"Bool":2,"Int":2,"Real":4,"Dint":4,"String":256} #实测所占长度准确

#db表示要读取的信息,由上图得到,中间用Tab隔开,非空格
db=\
"""
Temperature    Real    0.0
Cold    Bool    4.0
RPis_to_Buy    Int    6.0
Db_test_String    String    8.0
"""
#get_db_size用来获取DB块内数据长度,由最后一个数据起始地址和它的数据类型得到(这里是8.0和String,长度就是8+256=264),就这一个作用(苦笑)
def get_db_size(array,bytekey,datatypekey):#bytekey:地址 datatypekey:数据类型
  seq,length=[x[bytekey] for x in array],[x[datatypekey] for x in array]
  idx=seq.index(max(seq))  #index就来检索位置
  lastByte=int(max(seq).split('.')[0])+(offset[length[idx]])
  return lastByte #返回DB块内要读取的最末尾位置,这里是264


#DBRead才是用来读取数据的函数,length是获取的最大长度
def DBRead(myplc,db_num,length,items):
  data=myplc.read_area(0x84,db_num,0,length)
  obj=DBObject()
  for item in items:
      value=None
      offset=int(item['bytebit'].split('.')[0]) #取数据起始位置
      
      ##读取各种数据类型的数据
      if item['data']=='Real':
          value=get_real(data,offset)
      if item['data']=="Bool":
          bit=int(item['bytebit'].split('.')[1])
          value=get_bool(data,offset,bit)
      if item['data']=="Int":
          value=get_int(data,offset)
      if item['data']=='String':
          value=get_string(data,offset,256)
      obj.__setattr__(item['name'],value)#构建obj.item['name']=value形式,方便后续输出
  return obj

if __name__=="__main__":
   myplc=snap7.client.Client()
   myplc.connect('192.168.0.1', rack=0,slot=1)

  #过滤器函数,把后面list内的东西放入前面的函数判断,生成返回为True的新list
   itemlist=list(filter(lambda a:a!='',db.split('\n')))    #提取每行数据(一行一个)
  #把每个数据的name(名字)、data(数据类型)bytebit(起始地址)归好类
   items=[
          {
                  "name":x.split(    )[0],  #参数名
                  "data":x.split(    )[1],  #数据类型
                  "bytebit":x.split(    )[2]  #数据起始地址
                  } for x in itemlist
          ]   
  #调用函数,先获取数据长度,再读取各个数据的值,放在对象实例里面obj
   length=get_db_size(items,'bytebit','data')
   meh=DBRead(myplc,10,length,items)
  
 #输出各个数据
   print('''
        Cold:\t\t\t{}
        Tempeature:\t\t{}
        Rpis_to_Buy:\t\t{}
        Db_test_String:\t{}
        '''.format(meh.Cold,meh.Temperature,meh.RPis_to_Buy,meh.Db_test_String))
   myplc.disconnect();

程序运行效果
在这里插入图片描述
简单说下程序结构,在函数之前是要用到的材料,空对象、字典、要查询的内容,然后是两个函数,第一个函数获取DB块内部数据的长度(第二个函数里用),第二个函数读取各个数据;然后是主函数,通讯自然不用说,主要还是把db=
“”"
Temperature Real 0.0
Cold Bool 4.0
RPis_to_Buy Int 6.0
Db_test_String String 8.0
“”"
类型的输入转变为
[{‘name’: ‘Temperature’, ‘data’: ‘Real’, ‘bytebit’: ‘0.0’},
{‘name’: ‘Cold’, ‘data’: ‘Bool’, ‘bytebit’: ‘4.0’},
{‘name’: ‘RPis_to_Buy’, ‘data’: ‘Int’, ‘bytebit’: ‘6.0’},
{‘name’: ‘Db_test_String’, ‘data’: ‘String’, ‘bytebit’: ‘8.0’}]
这种格式的数据,方便函数读取(方法用split)---------(自己直接输入下面这种类型的数据也行)
然后用上面两个函数得到相应的数据,最后print()就行

6.2 写入DB块(只改不加)

起始写入DB块完全可以参照上面的写入的格式去写,因此只需要把6.1内读取的部分程序改为写入就行了,再加上个write_area就行了
因此,修改的部分程序(仅DBRead函数)如下,其他部分与6.1程序完全一致,虽然读的部分有点累赘,稍微再改一点,就又能读,又能写了。

def DBRead(myplc,db_num,length,items):
   data=myplc.read_area(0x84,db_num,0,length)
   obj=DBObject()
   for item in items:
      value=None
      offset=int(item['bytebit'].split('.')[0]) #取数据起始位置
      
      ##读取各种数据类型的数据
      if item['data']=='Real':
          value=get_real(data,offset)
          set_real(data,offset,132.032)
      if item['data']=="Bool":
          bit=int(item['bytebit'].split('.')[1])
          value=get_bool(data,offset,bit)
          set_bool(data,offset,0,False)
      if item['data']=="Int":
          value=get_int(data,offset)
          set_int(data,offset,312)
      if item['data']=='String':
          value=get_string(data,offset,256)
          print(offset)
          set_string(data,offset,'Who I am',256)
      myplc.write_area(0x84,db_num,0,data)
      obj.__setattr__(item['name'],value) #构建obj.item['name']=value形式,方便后续输出
   return obj

结果如下
在这里插入图片描述

6.3 关于DB块读取的一个骚操作

以上所有的代码基本都是从别人的教程里面copy的,所以程序都会显得有点臃肿,毕竟是教程性质的,所以各个类型的都会牵扯一下,特别是DB块的读取,虽然简单,但是绕了弯差点没读懂。

在全部内容敲了一遍后加一个自己写的最简单的DB读取和写入,不写多,就一个简单的骚操作测试,当然对前面所有内容适用now:

import snap7
from snap7.util import *
from snap7.snap7types import *

myplc=snap7.client.Client()
myplc.connect('192.168.0.1', rack=0,slot=1)

data=myplc.read_area(0x84,10,0,256) #知道是DB10了
value=get_int(data,6)   #已经知道6.0 是int型
print(value)
set_int(data,6,112)
myplc.write_area(0x84,10,0,data)

效果
在这里插入图片描述
这部分可以看出DB块读取的几个细节:
1.不一定要读取DB块的全部数据,仅一个数据也行
2.Read_area的size参数读取DB时不能超过实际DB块的最大值(这里是264),这就是为什么复杂的代码要专门获取这个值,但自己测试可以专门看着TIA给(真皮)
3.真的要简单实现效果是真的可以很简单的,核心就4行,还能读能写

1.前面的程序部分很多细节做了一些尝试,实际运用时可以根据各程序段的内容灵活修改
格外注意2.以上所有的代码copy下来运行时--“注意一下下缩进”--
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值