HslCommunication模块
参考文章
python 读写三菱PLC数据,使用以太网读写Q系列,L系列,Fx系列的PLC数据
协议
MC协议
安装
pip install HslCommunication
常用类
Qna3E协议的二进制类 MelsecMcNet
Qna3E协议的ascii类 MelsecMcAsciiNet
A兼容1E协议的二进制类 MelsecA1ENet
测试示例
from HslCommunication import MelsecMcNet
from HslCommunication import SoftBasic
def printReadResult(result):
if result.IsSuccess:
print(result.Content)
else:
print("failed "+result.Message)
def printWriteResult(result):
if result.IsSuccess:
print("success")
else:
print("falied " + result.Message)
if __name__ == "__main__":
print(SoftBasic.GetUniqueStringByGuidAndRandom())
melsecNet = MelsecMcNet("192.168.8.12",6002)
if melsecNet.ConnectServer().IsSuccess == False:
print("connect falied ")
else:
# bool read write test
melsecNet.WriteBool("M200",True)
printReadResult(melsecNet.ReadBool("M200"))
# bool array read write test
melsecNet.WriteBool("M300",[True,False,True,True,False])
printReadResult(melsecNet.ReadBool("M300",5))
# int16 read write test
melsecNet.WriteInt16("D200", 12358)
printReadResult(melsecNet.ReadInt16("D200"))
# int16 read write test
melsecNet.WriteInt16("D201", -12358)
printReadResult(melsecNet.ReadInt16("D201"))
# uint16 read write test
melsecNet.WriteUInt16("D202", 52358)
printReadResult(melsecNet.ReadUInt16("D202"))
# int32 read write test
melsecNet.WriteInt32("D210", 12345678)
printReadResult(melsecNet.ReadInt32("D210"))
# int32 read write test
melsecNet.WriteInt32("D212", -12345678)
printReadResult(melsecNet.ReadInt32("D212"))
# uint32 read write test
melsecNet.WriteUInt32("D214", 123456789)
printReadResult(melsecNet.ReadInt32("D214"))
# int64 read write test
melsecNet.WriteInt64("D220", 12345678901234)
printReadResult(melsecNet.ReadInt64("D220"))
# float read write test
melsecNet.WriteFloat("D230", 123.456)
printReadResult(melsecNet.ReadFloat("D230"))
# double read write test
melsecNet.WriteDouble("D240", 123.456789)
printReadResult(melsecNet.ReadDouble("D240"))
# string read write test
melsecNet.WriteString("D250", '123456')
printReadResult(melsecNet.ReadString("D250",3))
# int16 array read write test
melsecNet.WriteInt16("D260", [123,456,789,-1234])
printReadResult(melsecNet.ReadInt16("D260",4))
melsecNet.ConnectClose()
注意
1、每次的数据交互您都可以判断是否成功,如果您不判断,如果网络断开或是异常,会影响程序的执行。
2、读写操作支持单个数和数组。
3、支持自定义的数据读写操作。直接调用Read方法,可以读取到原生的byte数组,然后进行组合数据。
modbus_tk模块
协议
modbus协议
安装
pip install modbus_tk
测试示例
import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus_tcp as modbus_tcp
logger = modbus_tk.utils.create_logger("console")
if __name__ == "__main__":
try:
# 连接MODBUS TCP从机
master = modbus_tcp.TcpMaster(host="192.168.0.12")
master.set_timeout(5.0)
print(master)
logger.info("connected")
# 读保持寄存器
print(master.execute(1, cst.WRITE_MULTIPLE_REGISTERS, 40244, output_value=[20,21,22,23]))
print('oooooo')
demo1 = master.execute(1, cst.READ_HOLDING_REGISTERS, 40244, 9)
print(demo1)
print('oooooosdfsdf')
demo2 = master.execute(1, cst.READ_HOLDING_REGISTERS, 40244, 10)
print(demo2)
# 读线圈寄存器
demo3 = master.execute(1, cst.READ_COILS, 40244, 9)
except modbus_tk.modbus.ModbusError as e:
logger.error("%s- Code=%d" % (e, e.get_exception_code()))
生产场景使用示例
# 以下为celery定时任务的task函数,实现了使用HslCommunication模块和modbus_tk模块读取PLC设备保存的数据
import os
from datetime import datetime
from HslCommunication import MelsecMcNet
from modbus_tk import modbus_tcp, defines
from common.requests2server import approvalget
from deviceModule.models import Device, DeviceStateRecord
from publicModule.models import PLCInfoToEquipmentClause, PLCInfo, PLCBasicInfo, TransitServerInfo
from qms.celery import app
@app.task()
def get_plc_info():
# 由于网络原因,在部分场景下服务器无法直接连通PLC设备,因此采用中转服务的方式解决该问题(中转服务读取PLC数据并保存,服务器从中转服务获取数据)
transit_server_infos = TransitServerInfo.objects.all()
for transit_server_info in transit_server_infos:
if transit_server_info.server_ip and transit_server_info.server_port:
res = approvalget(
url=f'http://{transit_server_info.server_ip}:{transit_server_info.server_port}/plcModule/queryDeviceState/')
if res and res.get('code') == 200:
device_states = res.get('data', [])
for device_state in device_states:
if device_state.get('device_id'):
devices = Device.objects.filter(id=device_state.get('device_id'))
if devices:
if devices[0].current_state not in [4, 5, 7] and \
devices[0].current_state != int(device_state.get('current_state')):
Device.objects.filter(id=device_state.get('device_id')).update(
current_state=device_state.get('current_state'))
DeviceStateRecord.objects.create(device_id=devices[0].id,
state=device_state.get('current_state'),
record_time=datetime.now())
else:
Device.objects.exclude(current_state__in=[4, 5, 7]).update(current_state=0)
# 服务器可以连通PLC时,直接读取PLC数据
plc_infos = PLCInfo.objects.filter(transit_server_info_id=None)
for plc_info in plc_infos:
plc_basic_info = PLCBasicInfo.objects.filter(id=plc_info.plc_basic_info_id).first()
if plc_basic_info:
plc_info_to_equipment_clauses = PLCInfoToEquipmentClause.objects.filter(plc_info_id=plc_info.id)
if plc_info_to_equipment_clauses:
if plc_basic_info.plc_protocol == 1:
melsec_mc_net = MelsecMcNet(plc_info.plc_ip, plc_info.plc_port)
elif plc_basic_info.plc_protocol == 2:
master = modbus_tcp.TcpMaster(host=plc_info.plc_ip, port=int(plc_info.plc_port))
for plc_info_to_equipment_clause in plc_info_to_equipment_clauses:
devices = Device.objects.filter(id=plc_info_to_equipment_clause.equipment_clause_id)
if devices:
current_state = devices[0].current_state
if plc_basic_info.plc_protocol == 1:
if melsec_mc_net.ConnectServer().IsSuccess:
run_signal = melsec_mc_net.ReadBool(plc_info_to_equipment_clause.run_signal)
wait_signal = melsec_mc_net.ReadBool(plc_info_to_equipment_clause.wait_signal)
alerting_signal = melsec_mc_net.ReadBool(plc_info_to_equipment_clause.alerting_signal)
if run_signal:
current_state = 2
elif wait_signal:
current_state = 1
elif alerting_signal:
current_state = 3
else:
current_state = 6
else:
current_state = 0
elif plc_basic_info.plc_protocol == 2:
try:
run_signal = master.execute(1, defines.READ_HOLDING_REGISTERS,
int(plc_info_to_equipment_clause.run_signal), 1)[0]
wait_signal = master.execute(1, defines.READ_HOLDING_REGISTERS,
int(plc_info_to_equipment_clause.wait_signal), 1)[0]
alerting_signal = master.execute(1, defines.READ_HOLDING_REGISTERS,
int(plc_info_to_equipment_clause.alerting_signal), 1)[
0]
if run_signal:
current_state = 2
elif wait_signal:
current_state = 1
elif alerting_signal:
current_state = 3
else:
current_state = 6
except:
current_state = 0
if devices[0].current_state not in [4, 5, 7] and devices[0].current_state != current_state:
devices.update(current_state=current_state)
DeviceStateRecord.objects.create(device_id=devices[0].id,
state=current_state,
record_time=datetime.now())