Files
POC-to-ModbusTCP/poc_to_modbus.py
Patrick McDonagh c6248bae95 POC-108 work.
Adds unit testing. In the process of converting from scaled integers to using floating point values with a BinaryPayloadBuilder class
2017-01-06 18:43:06 -06:00

221 lines
8.8 KiB
Python

#!/usr/bin/env python
'''
Pymodbus Server With Callbacks
--------------------------------------------------------------------------
This is an example of adding callbacks to a running modbus server
when a value is written to it. In order for this to work, it needs
a device-mapping file.
'''
# ---------------------------------------------------------------------------#
# import the modbus libraries we need
# ---------------------------------------------------------------------------#
from pymodbus.server.async import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.constants import Endian
# ---------------------------------------------------------------------------#
# import the python libraries we need
# ---------------------------------------------------------------------------#
import pymongo
from pymongo import MongoClient
from pycomm_helper import utils as plc
from time import time as now
# ---------------------------------------------------------------------------#
# configure the service logging
# ---------------------------------------------------------------------------#
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
PLC_IP_ADDRESS = '10.20.4.7'
# ---------------------------------------------------------------------------#
# Datatype Mapping
# ---------------------------------------------------------------------------#
def getTagsFromDB():
client = MongoClient()
db = client.tag_data
tags = db.tag_vals
print("Found {} tags in the database".format(tags.count()))
di_tags_cur = tags.find({'register_type': 'di'})
di_tags = list(di_tags_cur)
di_tags_num = di_tags_cur.count()
print("{} Digital Inputs".format(di_tags_num))
co_tags_cur = tags.find({'register_type': 'co'})
co_tags = list(co_tags_cur)
co_tags_num = co_tags_cur.count()
print("{} Coils".format(co_tags_num))
ir_tags_cur = tags.find({'register_type': 'ir'})
ir_tags = list(ir_tags_cur)
ir_tags_num = ir_tags_cur.count()
print("{} Input Registers".format(ir_tags_num))
hr_tags_cur = tags.find({'register_type': 'hr'})
hr_tags = list(hr_tags_cur)
hr_tags_num = hr_tags_cur.count()
print("{} Holding Registers".format(hr_tags_num))
return {'di': di_tags, 'co': co_tags, 'ir': ir_tags, 'hr': hr_tags}
# ---------------------------------------------------------------------------#
# create your custom data block with callbacks
# ---------------------------------------------------------------------------#
class DigitalTagDataBlock(ModbusSparseDataBlock):
''' A datablock that stores the new value in memory
and passes the operation to a message queue for further
processing.
'''
def __init__(self, register_type, tag_list):
'''
'''
values = {}
self.register_type = register_type
for t in tag_list:
try:
values[t['register_number']] = t['val']
except KeyError:
values[t['register_number']] = 911
# print("Initialized DigitalTagDataBlock for {} with values {}".format(self.register_type, values))
super(DigitalTagDataBlock, self).__init__(values)
def getValues(self, address, count=1):
client = MongoClient()
db = client.tag_data
tags = db.tag_vals
if count > 1:
for i in range(address, address + count):
tag_found = tags.find_one({'register_number': i, 'register_type': self.register_type})
print("{} = {}".format(tag_found['tag_name'], tag_found['val']))
super(DigitalTagDataBlock, self).setValues(address, tag_found['val'])
else:
tag_found = tags.find_one({'register_number': address, 'register_type': self.register_type})
print("{} = {}".format(tag_found['tag_name'], tag_found['val']))
super(DigitalTagDataBlock, self).setValues(address, tag_found['val'])
return super(DigitalTagDataBlock, self).getValues(address, count=count)
def setValues(self, address, value):
''' Sets the requested values of the datastore
:param address: The starting address
:param values: The new values to be set
'''
client = MongoClient()
db = client.tag_data
tags = db.tag_vals
tag_name = tags.find_one({'register_number': address, 'register_type': self.register_type})['tag_name']
plc.writeTag(PLC_IP_ADDRESS, tag_name, value)
class AnalogTagDataBlock(ModbusSparseDataBlock):
''' A datablock that stores the new value in memory
and passes the operation to a message queue for further
processing.
'''
def __init__(self, register_type, tag_list):
'''
'''
values = {}
self.register_type = register_type
register_values = {}
for t in tag_list:
builder = BinaryPayloadBuilder(endian=Endian.Little)
try:
if t['tag_type'] == 'SINT':
builder.add_8bit_int(t['val_actual'])
elif t['tag_type'][:3] == 'INT':
builder.add_16bit_int(t['val_actual'])
elif t['tag_type'] == 'REAL':
builder.add_32bit_float(t['val_actual'])
register_values[t['register_number']] = builder.to_registers
except KeyError:
register_values[t['register_number']] = []
print("No value for register #{} - {}".format(t['register_number'], t['tag_name']))
# print("Initialized AnalogTagDataBlock for {} with values {}".format(self.register_type, values))
super(AnalogTagDataBlock, self).__init__(values)
def getValues(self, address, count=1):
client = MongoClient()
db = client.tag_data
tags = db.tag_vals
if count > 1:
for i in range(address, address + count):
tag_found = tags.find_one({'register_number': i, 'register_type': self.register_type})
if tag_found:
print("{} = {}".format(tag_found['tag_name'], tag_found['val']))
super(AnalogTagDataBlock, self).setValues(address, tag_found['val'])
super(AnalogTagDataBlock, self).setValues(address + 3, 0)
else:
tag_found = tags.find_one({'register_number': address, 'register_type': self.register_type})
if tag_found:
print("{} = {}".format(tag_found['tag_name'], tag_found['val']))
super(AnalogTagDataBlock, self).setValues(address, tag_found['val'])
super(AnalogTagDataBlock, self).setValues(address + 3, 0)
return super(AnalogTagDataBlock, self).getValues(address, count=count)
def setValues(self, address, value):
''' Sets the requested values of the datastore
:param address: The starting address
:param values: The new values to be set
'''
client = MongoClient()
db = client.tag_data
tags = db.tag_vals
tag_name = tags.find_one({'register_number': address, 'register_type': self.register_type})['tag_name']
plc.writeTag(PLC_IP_ADDRESS, tag_name, value)
def main():
# ---------------------------------------------------------------------------#
# initialize your data store
# ---------------------------------------------------------------------------#
tags_in_db = getTagsFromDB()
di_block = DigitalTagDataBlock('di', tags_in_db['di'])
co_block = DigitalTagDataBlock('co', tags_in_db['co'])
hr_block = AnalogTagDataBlock('hr', tags_in_db['hr'])
ir_block = AnalogTagDataBlock('ir', tags_in_db['ir'])
store = ModbusSlaveContext(di=di_block, co=co_block, hr=hr_block, ir=ir_block)
context = ModbusServerContext(slaves=store, single=True)
# ---------------------------------------------------------------------------#
# initialize the server information
# ---------------------------------------------------------------------------#
identity = ModbusDeviceIdentification()
identity.VendorName = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'
# ---------------------------------------------------------------------------#
# run the server you want
# ---------------------------------------------------------------------------#
StartTcpServer(context, identity=identity, address=("localhost", 5020))
if __name__ == '__main__':
main()