Updated to add configurations. Tested all read and writes to be working.

This commit is contained in:
Patrick McDonagh
2017-01-10 18:58:10 -06:00
parent e1781cbce0
commit 53f371e9ae
6 changed files with 225 additions and 37 deletions

BIN
Modbus Map Clean.pdf Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -124,6 +124,25 @@
{"tag_name": "kWh_Today", "register_type": "ir", "register_number": 231},
{"tag_name": "Max_Theoretical_Fluid_Load", "register_type": "ir", "register_number": 233},
{"tag_name": "Motor_Torque", "register_type": "ir", "register_number": 235},
{"tag_name": "Pump.Run_Status", "register_type": "ir", "register_number": 237},
{"tag_name": "Pump.Speed_Actual_SPM", "register_type": "ir", "register_number": 238},
{"tag_name": "Pump_Intake_Pressure", "register_type": "ir", "register_number": 240},
{"tag_name": "Card_Past[1].Surface_Position", "register_type": "ir", "register_number": 242, "arr_len": 750},
{"tag_name": "Card_Past[2].Surface_Position", "register_type": "ir", "register_number": 1742, "arr_len": 750},
{"tag_name": "Card_Past[3].Surface_Position", "register_type": "ir", "register_number": 3242, "arr_len": 750},
{"tag_name": "Card_Past[4].Surface_Position", "register_type": "ir", "register_number": 4742, "arr_len": 750},
{"tag_name": "Card_Past[1].Surface_Load", "register_type": "ir", "register_number": 6242, "arr_len": 750},
{"tag_name": "Card_Past[2].Surface_Load", "register_type": "ir", "register_number": 7742, "arr_len": 750},
{"tag_name": "Card_Past[3].Surface_Load", "register_type": "ir", "register_number": 9242, "arr_len": 750},
{"tag_name": "Card_Past[4].Surface_Load", "register_type": "ir", "register_number": 10742, "arr_len": 750},
{"tag_name": "Card_Past[1].Downhole_Position", "register_type": "ir", "register_number": 12242, "arr_len": 750},
{"tag_name": "Card_Past[2].Downhole_Position", "register_type": "ir", "register_number": 13742, "arr_len": 750},
{"tag_name": "Card_Past[3].Downhole_Position", "register_type": "ir", "register_number": 15242, "arr_len": 750},
{"tag_name": "Card_Past[4].Downhole_Position", "register_type": "ir", "register_number": 16742, "arr_len": 750},
{"tag_name": "Card_Past[1].Downhole_Load", "register_type": "ir", "register_number": 18242, "arr_len": 750},
{"tag_name": "Card_Past[2].Downhole_Load", "register_type": "ir", "register_number": 19742, "arr_len": 750},
{"tag_name": "Card_Past[3].Downhole_Load", "register_type": "ir", "register_number": 21242, "arr_len": 750},
{"tag_name": "Card_Past[4].Downhole_Load", "register_type": "ir", "register_number": 22742, "arr_len": 750},
{"tag_name": "_dt", "register_type": "hr", "register_number": 1},
{"tag_name": "Casing_ID", "register_type": "hr", "register_number": 3},
@@ -155,5 +174,65 @@
{"tag_name": "Input_Inclinometer.Channel", "register_type": "hr", "register_number": 47},
{"tag_name": "Input_Inclinometer.Type", "register_type": "hr", "register_number": 48},
{"tag_name": "Input_LoadCell.Channel", "register_type": "hr", "register_number": 49},
{"tag_name": "Input_LoadCell.Type", "register_type": "hr", "register_number": 50}
{"tag_name": "Input_LoadCell.Type", "register_type": "hr", "register_number": 50},
{"tag_name": "Taper.Taper[1].Setup.Length", "register_type": "hr", "register_number": 51},
{"tag_name": "Taper.Taper[1].Setup.Diameter", "register_type": "hr", "register_number": 53},
{"tag_name": "Taper.Taper[1].Setup.Material", "register_type": "hr", "register_number": 55},
{"tag_name": "Taper.Taper[2].Setup.Length", "register_type": "hr", "register_number": 56},
{"tag_name": "Taper.Taper[2].Setup.Diameter", "register_type": "hr", "register_number": 58},
{"tag_name": "Taper.Taper[2].Setup.Material", "register_type": "hr", "register_number": 60},
{"tag_name": "Taper.Taper[3].Setup.Length", "register_type": "hr", "register_number": 61},
{"tag_name": "Taper.Taper[3].Setup.Diameter", "register_type": "hr", "register_number": 63},
{"tag_name": "Taper.Taper[3].Setup.Material", "register_type": "hr", "register_number": 65},
{"tag_name": "Taper.Taper[4].Setup.Length", "register_type": "hr", "register_number": 66},
{"tag_name": "Taper.Taper[4].Setup.Diameter", "register_type": "hr", "register_number": 68},
{"tag_name": "Taper.Taper[4].Setup.Material", "register_type": "hr", "register_number": 70},
{"tag_name": "Taper.Taper[5].Setup.Length", "register_type": "hr", "register_number": 71},
{"tag_name": "Taper.Taper[5].Setup.Diameter", "register_type": "hr", "register_number": 73},
{"tag_name": "Taper.Taper[5].Setup.Material", "register_type": "hr", "register_number": 75},
{"tag_name": "Taper.Taper[6].Setup.Length", "register_type": "hr", "register_number": 76},
{"tag_name": "Taper.Taper[6].Setup.Diameter", "register_type": "hr", "register_number": 78},
{"tag_name": "Taper.Taper[6].Setup.Material", "register_type": "hr", "register_number": 80},
{"tag_name": "Taper.Taper[7].Setup.Length", "register_type": "hr", "register_number": 81},
{"tag_name": "Taper.Taper[7].Setup.Diameter", "register_type": "hr", "register_number": 83},
{"tag_name": "Taper.Taper[7].Setup.Material", "register_type": "hr", "register_number": 85},
{"tag_name": "Taper.Taper[8].Setup.Length", "register_type": "hr", "register_number": 86},
{"tag_name": "Taper.Taper[8].Setup.Diameter", "register_type": "hr", "register_number": 88},
{"tag_name": "Taper.Taper[8].Setup.Material", "register_type": "hr", "register_number": 90},
{"tag_name": "Taper.Taper[9].Setup.Length", "register_type": "hr", "register_number": 91},
{"tag_name": "Taper.Taper[9].Setup.Diameter", "register_type": "hr", "register_number": 93},
{"tag_name": "Taper.Taper[9].Setup.Material", "register_type": "hr", "register_number": 95},
{"tag_name": "Taper.Taper[10].Setup.Length", "register_type": "hr", "register_number": 96},
{"tag_name": "Taper.Taper[10].Setup.Diameter", "register_type": "hr", "register_number": 98},
{"tag_name": "Taper.Taper[10].Setup.Material", "register_type": "hr", "register_number": 100},
{"tag_name": "UnitConfig.MotorNameplate.Volts", "register_type": "hr", "register_number": 101},
{"tag_name": "UnitConfig.MotorNameplate.Amps", "register_type": "hr", "register_number": 103},
{"tag_name": "UnitConfig.MotorNameplate.Hertz", "register_type": "hr", "register_number": 105},
{"tag_name": "UnitConfig.MotorNameplate.Poles", "register_type": "hr", "register_number": 107},
{"tag_name": "UnitConfig.MotorNameplate.RPM", "register_type": "hr", "register_number": 108},
{"tag_name": "UnitConfig.MotorNameplate.ServiceFactor", "register_type": "hr", "register_number": 110},
{"tag_name": "UnitConfig.MotorNameplate.Horsepower", "register_type": "hr", "register_number": 112},
{"tag_name": "UnitConfig.Pump_Diameter", "register_type": "hr", "register_number": 114},
{"tag_name": "UnitConfig.Anchor_Depth", "register_type": "hr", "register_number": 116},
{"tag_name": "UnitConfig.Total_Stroke_Length", "register_type": "hr", "register_number": 118},
{"tag_name": "UnitConfig.Motor_Sheave_Size", "register_type": "hr", "register_number": 120},
{"tag_name": "UnitConfig.Gearbox_Sheave", "register_type": "hr", "register_number": 122},
{"tag_name": "UnitConfig.Gearbox_Limit", "register_type": "hr", "register_number": 124},
{"tag_name": "UnitConfig.Gearbox_Ratio", "register_type": "hr", "register_number": 126},
{"tag_name": "UnitConfig.Rating_Gearbox", "register_type": "hr", "register_number": 128},
{"tag_name": "UnitConfig.Rating_Structural", "register_type": "hr", "register_number": 130},
{"tag_name": "UnitConfig.Well_Type", "register_type": "hr", "register_number": 132},
{"tag_name": "UnitConfig.Total_Vertical_Depth", "register_type": "hr", "register_number": 133},
{"tag_name": "UnitConfig.Tubing_Size_ID", "register_type": "hr", "register_number": 135},
{"tag_name": "UnitConfig.Tubing_Size_OD", "register_type": "hr", "register_number": 137},
{"tag_name": "UnitConfig.API_Oil", "register_type": "hr", "register_number": 139},
{"tag_name": "UnitConfig.SG_Water", "register_type": "hr", "register_number": 141},
{"tag_name": "UnitConfig.Percent_Water", "register_type": "hr", "register_number": 143},
{"tag_name": "Pump.Speed_Setpoint_SPM", "register_type": "hr", "register_number": 145},
{"tag_name": "Pump.Speed_Max", "register_type": "hr", "register_number": 147},
{"tag_name": "Pump.Speed_Min", "register_type": "hr", "register_number": 149},
{"tag_name": "Pump.POC_Percentage_Off", "register_type": "hr", "register_number": 151},
{"tag_name": "Pump.Auto_Percentage_RampDown", "register_type": "hr", "register_number": 153},
{"tag_name": "Pump.Auto_Percentage_RampUp", "register_type": "hr", "register_number": 155},
{"tag_name": "Pump.Mode", "register_type": "hr", "register_number": 157}
]

View File

@@ -16,6 +16,9 @@
{"tag_name": "USE_WIRELESS_LOADCELL", "register_type": "co", "register_number": 15},
{"tag_name": "Write_Mode_Data", "register_type": "co", "register_number": 16},
{"tag_name": "Write_Setup_Data", "register_type": "co", "register_number": 17},
{"tag_name": "Pump.Start", "register_type": "co", "register_number": 18},
{"tag_name": "Pump.Stop", "register_type": "co", "register_number": 19},
{"tag_name": "Inclinometer_Calibrating", "register_type": "di", "register_number": 1},
{"tag_name": "Inclinometer_Stale", "register_type": "di", "register_number": 2}
]

View File

@@ -52,6 +52,28 @@ def integer_to_byte(integer_val):
return unpacked
def lebyte_to_float(word_list):
'''
Converts list of little-endian bytes to float
'''
packed_string = struct.pack("HH", *word_list)
unpacked_float = struct.unpack("f", packed_string)[0]
return unpacked_float
def lebyte_to_integer(word_list):
'''
Converts list(size = 1) of little-endian bytes to Integer
'''
try:
packed_string = struct.pack("H", *word_list)
unpacked_int = struct.unpack("h", packed_string)[0]
except Exception as e:
print("Unable to convert {} to integer".format(word_list))
return False
return unpacked_int
def getTagsFromDB():
client = MongoClient()
db = client.tag_data
@@ -104,14 +126,14 @@ class DigitalTagDataBlock(ModbusSparseDataBlock):
super(DigitalTagDataBlock, self).__init__(values)
def getValues(self, address, count=1):
# client = MongoClient()
# db = client.tag_data
# tags = db.tag_vals
#
# 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'])
client = MongoClient()
db = client.tag_data
tags = db.tag_vals
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'])
return super(DigitalTagDataBlock, self).getValues(address, count=count)
@@ -124,8 +146,19 @@ class DigitalTagDataBlock(ModbusSparseDataBlock):
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)
tag_found = tags.find_one({'register_number': address, 'register_type': self.register_type})
tag_name = tag_found['tag_name']
if value[0]:
plc_value = 1
else:
plc_value = 0
print("Writing {} to {}".format(plc_value, tag_name))
plc.writeTag(PLC_IP_ADDRESS, tag_name, int(plc_value))
plc_val = plc.readTag(PLC_IP_ADDRESS, tag_found['tag_name'])
if plc_val:
tag_found['val'] = plc_val[0]
tags.update({'tag_name': tag_found['tag_name']}, tag_found)
super(DigitalTagDataBlock, self).setValues(address, value)
class AnalogTagDataBlock(ModbusSparseDataBlock):
@@ -193,11 +226,26 @@ class AnalogTagDataBlock(ModbusSparseDataBlock):
: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)
print("provided values: {}".format(value))
try:
client = MongoClient()
db = client.tag_data
tags = db.tag_vals
tag_found = tags.find_one({'register_number': address, 'register_type': self.register_type})
tag_name = tag_found['tag_name']
if tag_found['tag_type'] == "REAL":
plc_value = lebyte_to_float(value)
else:
plc_value = value[0]
print("Writing {} to {}".format(plc_value, tag_name))
plc.writeTag(PLC_IP_ADDRESS, tag_name, plc_value)
plc_val = plc.readTag(PLC_IP_ADDRESS, tag_found['tag_name'])
if plc_val:
tag_found['val'] = plc_val[0]
tags.update({'tag_name': tag_found['tag_name']}, tag_found)
super(AnalogTagDataBlock, self).setValues(address, value)
except Exception as e:
print(e)
def main():

100
test.py
View File

@@ -9,7 +9,7 @@ from pymodbus.client.sync import ModbusTcpClient as ModbusClient
# import python packages
# ---------------------------------------------------------------------------#
from pymongo import MongoClient
import unittest
import pytest
import struct
# ---------------------------------------------------------------------------#
@@ -33,16 +33,26 @@ mongo_db = mongo_client.tag_data
mongo_tags = mongo_db.tag_vals
# ---------------------------------------------------------------------------#
# close enough function
# ---------------------------------------------------------------------------#
def close_enough(a, b):
return abs(a - b) < 0.001
def float_to_bytes(float_val):
'''
Determines if two values a and b are close enough to eachother to be considered equal
Converts a float to little-endian bytes
'''
if abs(a - b) < 0.01:
return True
return False
packed_string = struct.pack('f', float_val)
unpacked_list = list(struct.unpack('HH', packed_string))
return unpacked_list
def integer_to_byte(integer_val):
'''
Converts an integer to its byte
'''
packed_string = struct.pack('h', integer_val)
unpacked = list(struct.unpack('H', packed_string))
return unpacked
def lebyte_to_float(word_list):
@@ -70,8 +80,8 @@ def lebyte_to_integer(word_list):
# ---------------------------------------------------------------------------#
# specify slave to query
# ---------------------------------------------------------------------------#
class TestModbusDatabaseValues(unittest.TestCase):
def test_holding_registers(self):
class TestModbusDatabaseValues:
def test_read_holding_registers(self):
modbus_client.connect()
tag_in_db = mongo_tags.find({"register_type": 'hr'})
for db_tag in tag_in_db:
@@ -104,10 +114,10 @@ class TestModbusDatabaseValues(unittest.TestCase):
print(reg)
print("Modbus Scaled: {}".format(modbus_value))
print("Database Scaled: {}".format(database_value))
self.assertTrue(modbus_value == database_value)
assert modbus_value == database_value
modbus_client.close()
def test_input_registers(self):
def test_read_input_registers(self):
modbus_client.connect()
tag_in_db = mongo_tags.find({"register_type": 'ir'})
for db_tag in tag_in_db:
@@ -144,10 +154,10 @@ class TestModbusDatabaseValues(unittest.TestCase):
print(reg)
print("Modbus Scaled: {}".format(modbus_value))
print("Database Scaled: {}".format(database_value))
self.assertTrue(modbus_value == database_value)
assert modbus_value == database_value
modbus_client.close()
def test_arrays(self):
def test_read_arrays(self):
modbus_client.connect()
tag_in_db = mongo_tags.find({"register_type": 'ir'})
for db_tag in tag_in_db:
@@ -184,10 +194,10 @@ class TestModbusDatabaseValues(unittest.TestCase):
if len(array_values) > 0:
for i in range(0, len(array_values)):
print("{} = {} - {}".format(i, array_values[i], db_vals[i]))
self.assertTrue(array_values == db_vals)
assert array_values == db_vals
modbus_client.close()
def test_coils(self):
def test_read_coils(self):
modbus_client.connect()
tag_in_db = mongo_tags.find({"register_type": 'co'})
for db_tag in tag_in_db:
@@ -200,13 +210,13 @@ class TestModbusDatabaseValues(unittest.TestCase):
reg = modbus_client.read_coils(db_tag['register_number'] - 1, 1).bits
if not database_value == reg[0]:
print("{} =/= {}".format(database_value, reg[0]))
self.assertTrue(database_value == reg[0])
assert database_value == reg[0]
except AttributeError:
print("Could not get register {} for {}".format(db_tag['register_number'], db_tag['tag_name']))
continue
def test_digital_inputs(self):
def test_read_digital_inputs(self):
modbus_client.connect()
tag_in_db = mongo_tags.find({"register_type": 'di'})
for db_tag in tag_in_db:
@@ -219,11 +229,59 @@ class TestModbusDatabaseValues(unittest.TestCase):
reg = modbus_client.read_discrete_inputs(db_tag['register_number'] - 1, 1).bits
if not database_value == reg[0]:
print("{} =/= {}".format(database_value, reg[0]))
self.assertTrue(database_value == reg[0])
assert database_value == reg[0]
except AttributeError:
print("Could not get register {} for {}".format(db_tag['register_number'], db_tag['tag_name']))
continue
if __name__ == '__main__':
unittest.main()
def test_write_coils(self):
modbus_client.connect()
tag_in_db = mongo_tags.find({"register_type": 'co'})
for db_tag in tag_in_db:
db_val_before = db_tag['val']
if db_val_before == 0:
write_val = 1
else:
write_val = 0
modbus_client.write_coil(db_tag['register_number'] - 1, write_val)
reg = modbus_client.read_coils(db_tag['register_number'] - 1, 1).bits
assert write_val == reg[0]
modbus_client.write_coil(db_tag['register_number'] - 1, db_val_before)
reg = modbus_client.read_coils(db_tag['register_number'] - 1, 1).bits
assert db_val_before == reg[0]
def test_write_coils(self):
modbus_client.connect()
tag_in_db = mongo_tags.find({"register_type": 'hr'})
for db_tag in tag_in_db:
db_val_before = db_tag['val']
if db_tag['tag_type'] == "REAL":
write_val = db_val_before + 1.0
db_val_before_bytes = float_to_bytes(db_val_before)
write_val_bytes = float_to_bytes(write_val)
modbus_client.write_registers(db_tag['register_number'] - 1, write_val_bytes)
reg = modbus_client.read_holding_registers(db_tag['register_number'] - 1, 2).registers
read_val = lebyte_to_float(reg)
assert close_enough(write_val, read_val)
modbus_client.write_registers(db_tag['register_number'] - 1, db_val_before_bytes)
reg = modbus_client.read_holding_registers(db_tag['register_number'] - 1, 2).registers
read_val = lebyte_to_float(reg)
assert close_enough(db_val_before, read_val)
else:
write_val = db_val_before + 1
db_val_before_bytes = integer_to_byte(db_val_before)
write_val_bytes = integer_to_byte(write_val)
modbus_client.write_registers(db_tag['register_number'] - 1, write_val_bytes)
reg = modbus_client.read_holding_registers(db_tag['register_number'] - 1, 1).registers
read_val = lebyte_to_integer(reg)
assert write_val == read_val
modbus_client.write_registers(db_tag['register_number'] - 1, db_val_before_bytes)
reg = modbus_client.read_holding_registers(db_tag['register_number'] - 1, 1).registers
read_val = lebyte_to_integer(reg)
assert db_val_before == read_val