最近在调试一个DCS项目(集散控制系统),实际上就是一个新建厂区的控制系统。PLC用的是西门子1500,控制画面使用组态王7.5。
在调试过程中,发现给西门子DB块的变量转移到组态王太难了,因此记录一下,有需要的朋友可以参考我的做法,我这做法不一定是最简单的方法,如果哪个大神有更简单的方法,麻烦请告知一下。
首先介绍一下DCS系统。DCS系统基本包含下面几个部分:
1:设备部分,包含工厂生产需要的设备,以及设备的运转所需要的配电箱。设备的配电箱一般包含控制器(就是PLC,用来对设备本身运转做控制),传感器,变频器,等等。
如果没有DCS系统,在条件具备的情况下,也是可以由工人在控制箱上按按钮来控制设备。但是很明显现场让工人控制设备明显不太可行,因为设备太多,距离太远,突发情况根本来不及控制。
2:总控部分,就是在现有设备基础上,增加一层控制器,这一层控制器负责接入具体设备的配电箱,来代替人工在箱子上按按钮,以及读取设备的状态。也可以实现一定的逻辑判断,可以根据条件将多个连锁设备一同开启或者关闭。
3:中控部分,这一部分,就说开发一个控制画面,从总控PLC里面读取全厂设备状态,以及实现在中控室对全厂设备进行控制。
DCS系统大致结构图如下。中控和几个配电室组成一个环网,需要专门的环网交换机,环网的好处是中间断一下不影响。比如中间某个配电室突然断电,不影响其他。
DCS系统里面,设备本身的控制逻辑,在设备随机的控制器都已经做好了,这个是设备厂家做的,一般也不对外开放。控制箱保留一些控制接口(如硬接线,485,网口等)。
总控PLC一般是需要根据特定的场景和需求来进行编程。这个编程大致的意思,就是在什么情况下,启动某个设备。在什么情况下关闭某些设备。
最后是中控画面控制总控PLC,从而达到控制全厂设备的目的。
那么,中控是怎么控制PLC呢,答案很简单,就是通过修改PLC内部的变量(实际上就是内plc的内存,内存连续存了很多变量,short,float,bool,int 等)。PLC一般对外提供通讯协议,让上位机软件来读取和修改内部变量。例如西门子的S7协议,modbus_rtu,modbus_tcp,can,等等。
中控画面要修改PLC内部的变量,就需要要在组态软件知道plc内部变量的地址,因为变量太多,一般情况下,都是几千个变量起步,如果地址没有对上,中控点击启动,可能会产生严重后果。所以对变量是一个体力活。
这里要吐槽的第一点就是博图(西门子PLC编程软件)居然没有办法把DB块导出到excel,来方便其他组态工具导入变量。但是博图可以打印DB块到PDF。
吐槽的第二点:组态王导出结构体变量居然报错。见下图:
再咨询过组态王技术以后,技术表示不支持导出结构变量,也不支持导入结构变量。但是我PLC里面全是结构变量。因为结构变量用起来更方便,不容易记错。
不过技术说可以通过另外一种方法,就是再工程管理界面,导入和导出DB。如下图:
导出的DB内容如下。这个要注意,组态王导出和导入,调用的excel,如果电脑上没有安装excel,这个功能是没法用。安装wps也不行。强烈建议组态王支持国产,支持wps。
有了这个文件,剩下的就是想办法把博图导出的变量表(PDF文件见下图),按照这个格式转换一下。
当然直接读取PDF是不太可行的,不过WPS可以将PDF转换成excel,只要不是图片版的excel就行。刚刚这个就不是图片。所以还得是WPS帮忙。转换为excel见下图。看起来还是有点乱,不过不影响。
有了DB格式文件,有了excel,那剩下的就简单了。上py代码:
##############################################################
# 用于将博图DB块里面批量定义的结构体,转换为组态王格式,方便导入 #
# 博图DB块需要先打印成PDF,然后再用WPS将PDF转换为excel #
# qujia 20241022 #
##############################################################
import pandas as pd #用于读取excel,如果没有需要pip install pands
from openpyxl import Workbook #用于写入excel,如果没有需要pip install openpyxl
#定义db块中,需要被导入的变量名称,下面的下标一一对应
keys = ['bStart','bStop','StartAllowed','StopAllowed','rPinlv_Geiding','rPinlv_Geiding_A','MA','bReset','EM',
'bOpen','bClose','bStop','bOpen_A','bClose_A','bStop_A'
]
#定义在组态王中,结构体的变量名称,与上面的下标一一对应
names=['启动','停止','启动许可','停止许可','手动频率','自动频率','模式','复位','急停',
'手动开','手动关','手动停','自动开','自动关','自动停'
]
def read_xlsx(file_path): #读取excel文件,返回一个集合
df = pd.read_excel(file_path)
data = df.values.tolist()
return data
data = read_xlsx('plc2write.xlsx') #读取excel文件,这个文件是pdf转的
wb =Workbook() #用于导出excel文件
st1 = wb.create_sheet('结构体变量') #结构体变量sheet
stInt = wb.create_sheet('基本变量_IO整数') #整数变量表单
stFloat=wb.create_sheet('基本变量_IO实数') #实数变量的表单
stBit=wb.create_sheet('基本变量_IO离散') #bit变量表单
structId = 153 #当前结构变量id,当前最大的编号
varId=1383 #当前基本变量id,当前最大的编号
deviceName='' #当前设备名称,结构体名称
dbName='DB6.' #DB块名称
structName='设备控制' #结构体名称
plcName='筛分站PLC' #plc名称
for r in data :
name=r[1] #名称
type=r[4] #类型
add=r[6] #地址
remark=r[17] #备注信息
#print(name,type)
if type == 'Struct' : #如果是Struct ,可能是结构体开头
if name.find('Static') !=-1: #如果是没有名字的设备,直接结束
#print(r)
print("end ")
break
else:
print("device ",name , remark) #控制台打印
structId=structId+1 #结构变量id增加1
deviceName='W'+name.replace('-','_') #结构名称转变一下,符合组态王规则
structName='设备控制' #结构体名称
if deviceName.find('A_VA')>=0 or deviceName.find('A_GA')>=0:
structName='阀门控制' #结构体名称
st1.append(['[结构变量]','变量ID','变量名','变量类型','变量使用记数','注释']) #输出结构变量标题
st1.append(['',structId, deviceName, structName, 0, remark]) #输出结构变量名称和备注
st1.append(['','[成员]','成员名称','成员基本变量ID','','']) #输出成员变量标题
continue #继续下一行
if name == None : #直接跳过空行,提升效率
continue
if type=='Bool' and add==int(add): #如果是bool类型,并且是***.0,excel里面会把.0去掉,这里再加上去
add=dbName+str(add)+".0"
else :
add= dbName+str(add)
if name in keys : #如果是目标变量
remark=remark[0:35] #注释不能太长,组态王最大支持40个字符
idx=keys.index(name) #根据名称,对应组态王里面属性名称
vname=deviceName+"."+names[idx] #根据名称,对应组态王里面属性名称
if structName=='阀门控制' and name=='bStop' :
vname=deviceName+'.手动停'
print(name,vname,type,add,remark) #输出到控制台
varId=varId+1 #基本变量编号增加一个
st1.append(['','',vname,varId,'','']) #结构体添加变量引用
if type=='Int' : #输出到整数表单
stInt.append([varId,vname,0,0,0,0,999999999 ,0,999999999,'否','否', plcName,add,'USHORT','只写',1000,'线性','无','','','','否','',1,'','','','','','','','','','','','','','','','','','','不记录','','','否','无',remark,144,''])
elif type=='DInt': #输出到整数表单
stInt.append([varId,vname,0,0,0,0,999999999 ,0,999999999,'否','否',plcName,add,'LONG','只写',1000,'线性','无','','','','否','',1,'','','','','','','','','','','','','','','','','','','不记录','','','否','无',remark,144,''])
elif type=='Real': #输出到浮点数表单
stFloat.append([varId,vname,0,0,0,0,999999999,0,999999999,'否','否',plcName,add,'FLOAT','只写',1000,'线性','无','','','','否','',1,'','','','','','','','','','','','','','','','','','','不记录','','','否','无',remark,144,''])
elif type=='Bool': #输出到bit类型表单
stBit.append([varId,vname,0,'关','否','否',plcName, add,'Bit','只写',1000,'否','',1,'否','','','','','','','','不记录','','否','无',remark,144,''])
wb.save('write2.xlsx') #写入目标文件
最后生成的变量表文件,直接贴进去,然后DB导入,完事。不过强烈建议导入之前,对项目进行备份,万一出问题,后悔来得及。