1 开发准备
- 使用PyCharm开发环境进行Python的开发。PyCharm可以使用3.4绿色版,也可以使用其他版本。
- eview内置的python为7版本,因此PyCharm配置时也需要使用Python2.7版本。且Python2.7版本根据eview是32位还是64位选择正确版本。
- 先安装7,再安装pycharm,pycharm能自动找到python的目录。
- 例子代码在开发包的source\drivers\samplepythondrv\samplepythonrv.py文件,运行时直接拷贝py或.pyo文件(python编译后自动生成的一个中间字节码文件)到linux或windows对应目录下(如这里是: eivew\bin\drivers\samplepythondrv\)。
- 实际驱动如xxxxdrv请将samplepythonrv目录下的py,替换为xxxxdrv.py,并将samplepythonrv目录改为xxxxdrv名称。
- 程序运行时,调用的是eview根目录下的python目录中python库和第三方库。所以如果需要向python中添加第三方库,需要用anconda的pip下载和安装好后,再将下载到python安装目录的 Lib/site-packages下的对应一个或多个目录复制eview根目录的到python/Lib/site-packages下。如果第三方组件还依赖于一些pyd文件,也需要从相应复制过去。
2 驱动的调试运行
2.1 在配置中配置驱动
- 增加驱动基本信息
进入配置界面->设备配置->系统配置,在对话框中添加驱动。
其中驱动名称为用户自定义的英文驱动名。
或者在数据库eview.db中t_device_driver表增加一条记录,id自动生成(这里为:200001),name和modulename为xxxxdrv。
- 添加设备
添加驱动配置后,可在设备中增加该驱动的一个设备.
在数据库t_device_list表中增加一条记录,id自动生成(这里为113),name为:xxxxdevice,driver_id:200001,conntype:tcpclient,connparam:ip=127.0.0.1;port=502;
- 添加变量
添加设备配置后,可在设备中增加一个变量.
在数据库t_device_tag表中增加一条记录,id自动生成,name为:pytag2,device_id:113,address:AI:10;datatype:int16;pollrateMS:1000
2.2 调试准备
- 将驱动二次开发包下的bin目录下的exe拷贝到“eview安装路径\bin\drivers\xxxxdrv\”路径下;
- 重命名exe为“xxxxdrv.exe”
- 安装pycharm并设置python路径后,打开xxxxdrv.py文件。
- Debug时,启动程序输入:eview安装路径\bin\drivers\xxxxdrv\exe,启动调试,设置对应断点,就可以进行调试了
2.3 驱动调试
2.3.1 调试
驱动配置部署后,eview会自动启动你的驱动程序。调试前需要先关闭你的驱动程序,因为系统仅仅允许启动你的一个驱动进程,停止驱动的方法如下:任务管理器杀掉该驱动。
2.4 运行驱动
直接点击xxxxdrv.exe启动驱动。或者通过pkservermgr自动启动所有程序包括驱动。
3 驱动框架实现的主要功能
驱动框架实现的主要功能如下:
- 实现了驱动配置的自动加载读取
- 对于TCPServer、TCPClient、UDP、串口,根据配置自动建立、断开和管理与设备的连接状态
4 xxxxdrv.py驱动用到的对象定义
4.1 变量Tag点定义
代表每个变量,在pkDevice的vecTags中包含了所有该设备的tag点对象,可以用的属性如下:
name, 变量名称
address,变量地址
datatype,数据类型,整数
pollrate,轮训速度,整数
lenbits,占用长度,单位:bit
4.2 pkDevice
代表每个配置的设备,可以用的属性如下:
name, 设备名称
desc,设备描述
conntype,通讯连接类型,tcpclient,udp,tcpserver,other,serial
conntimeout,通讯连接超时时间
recvtimeout,接收超时时间
connparam,通讯连接参数内容,如ip=192.168.10.2;port=502
param1,设备参数1
param2,设备参数2
param3,设备参数3
param4,设备参数4
tags, 保存有tag点的各个名字的List
driver,设备对应driver对象,具体见下面
4.3 pkDriver
代表每个配置的设备驱动信息,可以用的属性如下:
name, 驱动名称
desc,驱动描述
param1,设备参数1
param2,设备参数2
param3,设备参数3
param4,设备参数4
5 编写xxxxdrv.py中的函数
示例代码(必须如下头部定义):
#! /usr/bin/python
# coding=utf-8
import sys
import struct
import os
sys.path.append(os.path.abspath('.') + '\\..\\..')
sys.path.append(os.path.abspath('.') + '\\..\\..\\python')
import pydriver
def InitDriver(pkDriver):
print "----InitDriver----"
print(pkDriver)
return 0
def UnInitDriver(pkDriver):
print "----UnInitDriver----"
print(pkDriver)
return 0
def InitDevice(pkDevice):
print "----InitDevice----"
print(pkDevice)
tags=[]
for tag in pkDevice['tags']:
tags.append(tag)
timerObj=pydriver.CreateTimer(pkDevice, 3000, tags)
return 0
def UnInitDevice(pkDevice):
print "UnInitDevice"
return 0
def OnTimer(pkDevice, timerObj, pkTimerParam):
print "----OnTimer----"
tags = pkTimerParam
print timerObj
print tags
id = 1
tag="100"
version = 2
count = 3
buffer = struct.pack("!H4s2I", id, tag, version, count)
print "before send..."
result = pydriver.Send(pkDevice, buffer, 2000)
print "SendToDevice:",result
if(result <= 0):
print "send to device return <-0"
print "send to device:", len(buffer) , "return :" , result
return
retCode, bufferRecv = pydriver.Recv(pkDevice, 10000, 200)
print "Recv,retcode:", retCode, ",length:",len(bufferRecv)
if(retCode != 0):
return
# 解析数据,得到值
tagValue =struct.unpack("!H", bufferRecv)
print "recv tagValue:",tagValue[0]
#print "recv,id:",id,",tag:",tag,",version:",version,",count:",count
tagAddr = "AI:10"
print tagAddr
tagList = pydriver.GetTagsByAddr(pkDevice, tagAddr)
pydriver.SetTagsValue(pkDevice,tagList, str(tagValue[0]))
pydriver.UpdateTagsData(pkDevice, tagList)
print "-OnTimer- end"
return 0
def OnControl(pkDevice, pkTag, strTagValue):
print "-!--OnControl--!-"
print pkDevice,pkTag
print "device name:",pkDevice['name'], "tagname:", pkTag['name'], "tagaddress:",pkTag['address'], "tagvalue:",strTagValue
return 0
#仅供调试使用,可以直接运行这个python文件进行调试
if __name__ == '__main__':
pkDriver={'name': '', '_InternalRef': 0, 'param4': '', 'param3': '', 'param2': '', 'param1': '', 'type': '', 'desc': ''}
pkDevice={'recvtimeout': 500, 'name': 'pytestdevice', 'tags': [{'_ctag': 7981384, 'name': 'CELL06_BLD1', 'lenbits': 16, 'datatype': 'uint16', 'address': 'AI:17023', 'pollrate': 3000}, {'_ctag': 7983144, 'name': 'CELL06_BLD2', 'lenbits': 16, 'datatype': 'uint16', 'address': 'AI:17023', 'pollrate': 3000}, {'_ctag': 7984904, 'name': 'CELL06_OKjishu', 'lenbits': 16, 'datatype': 'uint16', 'address': 'AI:17007', 'pollrate': 3000}, {'_ctag': 7986664, 'name': 'CELL06_NGjishu', 'lenbits': 16, 'datatype': 'uint16', 'address': 'AI:17036', 'pollrate': 3000}], '_InternalRef': 7947760, 'driver': {'name': '', '_InternalRef': 0, 'param4': '', 'param3': '', 'param2': '', 'param1': '', 'type': '', 'desc': ''}, 'conntype': 'tcpclient', 'param4': '', 'param3': '', 'param2': '', 'param1': '', 'conntimeout': 3000, 'connparam': 'ip=127.0.0.1;port=502', 'desc': ''}
InitDriver(pkDriver)
InitDevice(pkDevice)
主要有以下函数要实现:
5.1 初始化驱动函数
def InitDriver(pkDriver):
print "----InitDriver----"
return 0
参数说明:驱动结构体
功能说明:初始化函数,驱动EXE启动时该函数被调用,可在该函数中实现用户自定义初始化操作(例如使用com接口时调用com初始化函数)。
注意:如无特别需要,可以不实现。
5.2 退出驱动处理
def UnInitDriver(pkDriver):
print "----UnInitDriver----"
return 0
参数说明:无
返回值:返回0代表成功,其余值为失败
功能:驱动退出时该函数被调用。在该函数中可以释放用户自定义资源、对于非TCP连接设备断开设备连接等操作。
注意:如无特别需要,可以不实现。
1.1 初始化具体设备函数
def InitDevice(pkDevice):
print "----InitDevice----"
pydriver.CreateTimer(pkDevice, 3000, '11aabb')
return 0
参数说明:设备结构体
功能说明:每个设备配置被加载后会调用该初始化函数,可在该函数中实现用户自定义的具体设备相关初始化操作。常用操作包括:创建定时器、自组块等。
注意:必须实现开启定时器的功能,否则无法执行OnTimer函数。
5.3 退出具体设备处理
def UnInitDevice(pkDevice):
print "UnInitDevice"
return 0
参数说明:无
返回值:返回0代表成功,其余值为失败
功能:驱动退出时该函数被调用。在该函数中可以释放用户自定义资源、对于非TCP连接设备断开设备连接等操作。
注意:可以不实现。
5.4 处理定时数据读取
def OnTimer(pkDevice, timerHandle,periodMS, pkTimerParam):
print "----OnTimer----"
参数说明:
pkDevice[输入参数]:设备句柄
timerHandle[输入参数]:定时器周期句柄
periodMS [输入参数]:定时器周期句柄
pkTimerParam[输入参数]:定时器周期句柄
返回值:返回0代表成功,其余值为失败
功能说明:该函数会在InitDevice时创建一定周期的定时器(CreateTimer(pDevice,periodMS,timeParam))后自动被调用。对于块设备,轮询周期往往是指块的轮询周期。对于一次读取所有状态的设备,轮询周期是指设备的轮询周期。通常该函数实现如下功能:
1、数据的发送和接收
2、更新变量实时数据的值、数据状态和时间戳
3、对于非tcp通信协议,通常需要在该接口检查连接状态
注意:该函数必须实现
5.5 处理控制命令
def OnControl(pkDevice, tagName, tagValue):
print "OnControl"
return 0
参数说明:
pkDevice [输入参数]:设备指针
tagName [输入参数]:变量名称
tagValue [输入参数]:用字符串表示的变量值(如数字16这里传入”16”)
返回值:控制返回结果
功能说明:当有控制命令时该函数被调用,通过该函数向设备下发控制命令
注意:对于需要通过驱动实现控制功能的,该函数必须实现
6 驱动框架提供给驱动调用的对象与接口
6.1 根据变量地址获得变量列表
pydriver.GetTagsByAddr(pkDevice, strTagAddr)
参数说明:
pkDevice [输入参数]:设备对象
strTagAddr[输入参数]:字符串类型的变量地址,如”AI:10”
返回值:返回该设备地址位strTagAddr的List
6.2 设置一组变量的值
pydriver.SetTagsValue(pkDevice,tagList, strTagValue)
参数说明:
pkDevice [输入参数]:设备对象
tagList[输入参数]:变量列表,可能是pydriver.GetTagsByAddr返回的值
strTagValue[输入参数]:字符串类型的变量值,如”1000”
返回值:返回0表示成功
6.3 更新一组变量数据
pydriver.UpdateTagsData(pkDevice, tagList)
pkDevice [输入参数]:设备对象
tagList[输入参数]:变量列表,可能是pkdriver.SetTagsValue之后的变量列表
返回值:返回0表示成功
6.4 创建定时器
pydriver.CreateTimer(pkDevice, periodMS, timerParam)
pkDevice [输入参数]:设备对象
periodMS[输入参数]:数值类型的定时周期,单位:毫秒。如:3000表示3秒
timerParam[输入参数]:自定义的定时器参数,可以是字符串、对象、数值等任意类型,在OnTimer中可以获取到这个参数
返回值:返回定时器句柄(int型)
6.5 销毁定时器
pydriver.DestroyTimer(pkDevice,timerHandle);
pkDevice [输入参数]:设备对象
timerHandle[输入参数]:CreateTimer返回的定时器句柄
返回值:返回0表示成功
6.6 从设备接收多个字节
pydriver.Recv(pkDevice, maxBytes, recvTimeOutMS)
功能说明:当用户使用TCP、串口、UDP连接时可调用该接口接收设备数据
pkDevice [输入参数]:设备对象
maxBytes[输入参数]:该次接收最多接收的字符字节数
recvTimeOutMS[输入参数]:接收超时时间,单位是毫秒
返回值:二元元组(retcode, buffer)。这里的buffer必须用struct.unpack去解析。如:
retCode, bufferRecv = pydriver.Recv(pkDevice, 102400, 200)
6.7 向设备发送多个字节
pydriver.Send(pkDevice, buffer, sendTimeOutMS)
功能说明:当用户使用TCP、串口、UDP连接时可调用该接口发送数据到设备
参数说明:当前支持TCP作为客户端、服务端、UDP、串口的连接方式。
pkDevice [输入参数]:设备对象
buffer[输入参数]:待发送数据的缓冲区,二进制缓冲区,通过struct.pack 方法返回的二进制缓冲区。len(buffer)可以得到长度
sendTimeOutMS [输入参数]:发送超时时间,单位是毫秒
返回值:返回成功发送的字节数
7 Python帮助
7.1 处理二进制
参考:https://blog.csdn.net/w83761456/article/details/21171085
比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体.
struct模块中最重要的三个函数是pack(), unpack(), calcsize()
# 按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)
pack(fmt, v1, v2, ...)
# 按照给定的格式(fmt)解析字节流string,返回解析出来的tuple
unpack(fmt, string)
# 计算给定的格式(fmt)占用多少字节的内存
calcsize(fmt)
7.2 字符串拼接
参考:https://blog.csdn.net/w83761456/article/details/21171085
Result=1000
print("SendToDevice:",result)