initial
This commit is contained in:
60
Example Taglist.csv
Normal file
60
Example Taglist.csv
Normal file
@@ -0,0 +1,60 @@
|
||||
fm1_flowrate,Val_FM1_FR,REAL,250,3600
|
||||
fm2_flowrate,Val_FM2_FR,REAL,250,3600
|
||||
fm3_flowrate,Val_FM3_FR,REAL,250,3600
|
||||
fm4_flowrate,Val_FM4_FR,REAL,250,3600
|
||||
fm5_flowrate,Val_FM5_FR,REAL,250,3600
|
||||
fm6_flowrate,Val_FM6_FR,REAL,250,3600
|
||||
fm7_flowrate,Val_FM7_FR,REAL,250,3600
|
||||
fm8_flowrate,Val_FM8_FR,REAL,250,3600
|
||||
fm9_flowrate,Val_FM9_FR,REAL,250,3600
|
||||
fm10_flowrate,Val_FM10_FR,REAL,250,3600
|
||||
fm1_lifetime,Val_FM1_T1,REAL,100,3600
|
||||
fm2_lifetime,Val_FM2_T1,REAL,100,3600
|
||||
fm3_lifetime,Val_FM3_T1,REAL,100,3600
|
||||
fm4_lifetime,Val_FM4_T1,REAL,100,3600
|
||||
fm5_lifetime,Val_FM5_T1,REAL,100,3600
|
||||
fm6_lifetime,Val_FM6_T1,REAL,100,3600
|
||||
fm7_lifetime,Val_FM7_T1,REAL,100,3600
|
||||
fm8_lifetime,Val_FM8_T1,REAL,100,3600
|
||||
fm9_lifetime,Val_FM9_T1,REAL,100,3600
|
||||
fm10_lifetime,Val_FM10_T1,REAL,100,3600
|
||||
fm1_month,Val_Flowmeter1MonthTotal,REAL,100,3600
|
||||
fm2_month,Val_Flowmeter2MonthTotal,REAL,100,3600
|
||||
fm3_month,Val_Flowmeter3MonthTotal,REAL,100,3600
|
||||
fm4_month,Val_Flowmeter4MonthTotal,REAL,100,3600
|
||||
fm5_month,Val_Flowmeter5MonthTotal,REAL,100,3600
|
||||
fm6_month,Val_Flowmeter6MonthTotal,REAL,100,3600
|
||||
fm7_month,Val_Flowmeter7MonthTotal,REAL,100,3600
|
||||
fm8_month,Val_Flowmeter8MonthTotal,REAL,100,3600
|
||||
fm9_month,Val_Flowmeter9MonthTotal,REAL,100,3600
|
||||
fm10_month,Val_Flowmeter10MonthTotal,REAL,100,3600
|
||||
fm1_lastmonth,Val_Flowmeter1LastMonthTotal,REAL,100,3600
|
||||
fm2_lastmonth,Val_Flowmeter2LastMonthTotal,REAL,100,3600
|
||||
fm3_lastmonth,Val_Flowmeter3LastMonthTotal,REAL,100,3600
|
||||
fm4_lastmonth,Val_Flowmeter4LastMonthTotal,REAL,100,3600
|
||||
fm5_lastmonth,Val_Flowmeter5LastMonthTotal,REAL,100,3600
|
||||
fm6_lastmonth,Val_Flowmeter6LastMonthTotal,REAL,100,3600
|
||||
fm7_lastmonth,Val_Flowmeter7LastMonthTotal,REAL,100,3600
|
||||
fm8_lastmonth,Val_Flowmeter8LastMonthTotal,REAL,100,3600
|
||||
fm9_lastmonth,Val_Flowmeter9LastMonthTotal,REAL,100,3600
|
||||
fm10_lastmonth,Val_Flowmeter10LastMonthTotal,REAL,100,3600
|
||||
fm1_yesterdays,Val_Flowmeter1YesterdaysTotal,REAL,100,3600
|
||||
fm2_yesterdays,Val_Flowmeter2YesterdaysTotal,REAL,100,3600
|
||||
fm3_yesterdays,Val_Flowmeter3YesterdaysTotal,REAL,100,3600
|
||||
fm4_yesterdays,Val_Flowmeter4YesterdaysTotal,REAL,100,3600
|
||||
fm5_yesterdays,Val_Flowmeter5YesterdaysTotal,REAL,100,3600
|
||||
fm6_yesterdays,Val_Flowmeter6YesterdaysTotal,REAL,100,3600
|
||||
fm7_yesterdays,Val_Flowmeter7YesterdaysTotal,REAL,100,3600
|
||||
fm8_yesterdays,Val_Flowmeter8YesterdaysTotal,REAL,100,3600
|
||||
fm9_yesterdays,Val_Flowmeter9YesterdaysTotal,REAL,100,3600
|
||||
fm10_yesterdays,Val_Flowmeter10YesterdaysTotal,REAL,100,3600
|
||||
fm1_todays,Val_Flowmeter1TodaysTotal,REAL,100,3600
|
||||
fm2_todays,Val_Flowmeter2TodaysTotal,REAL,100,3600
|
||||
fm3_todays,Val_Flowmeter3TodaysTotal,REAL,100,3600
|
||||
fm4_todays,Val_Flowmeter4TodaysTotal,REAL,100,3600
|
||||
fm5_todays,Val_Flowmeter5TodaysTotal,REAL,100,3600
|
||||
fm6_todays,Val_Flowmeter6TodaysTotal,REAL,100,3600
|
||||
fm7_todays,Val_Flowmeter7TodaysTotal,REAL,100,3600
|
||||
fm8_todays,Val_Flowmeter8TodaysTotal,REAL,100,3600
|
||||
fm9_todays,Val_Flowmeter9TodaysTotal,REAL,100,3600
|
||||
fm10_todays,Val_Flowmeter10TodaysTotal,REAL,100,3600
|
||||
|
6
POCloud-CookieCutter-master/cookiecutter.json
Normal file
6
POCloud-CookieCutter-master/cookiecutter.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"driver_name": "pocloud_driver",
|
||||
"ip_address": "192.168.1.10",
|
||||
"device_type": ["CLX","Micro800", "None"]
|
||||
|
||||
}
|
||||
19
POCloud-CookieCutter-master/hooks/api_test.py
Normal file
19
POCloud-CookieCutter-master/hooks/api_test.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import meshify
|
||||
import json
|
||||
|
||||
values = {
|
||||
"channelType": 1,
|
||||
"dataType": 3,
|
||||
"defaultValue": "",
|
||||
"deviceTypeId": 470,
|
||||
"fromMe": False,
|
||||
"helpExplanation": "write the the modbus unit number",
|
||||
"io": True,
|
||||
"name": "writeplctag",
|
||||
"regex": "",
|
||||
"regexErrMsg": "",
|
||||
"subTitle": "Modbus Unit Number writer"
|
||||
}
|
||||
|
||||
data = meshify.post_meshify_api("devicetypes/470/channels/", values)
|
||||
print(json.dumps(data, indent=4, sort_keys=True))
|
||||
404
POCloud-CookieCutter-master/hooks/meshify.py
Normal file
404
POCloud-CookieCutter-master/hooks/meshify.py
Normal file
@@ -0,0 +1,404 @@
|
||||
"""Query Meshify for data."""
|
||||
import json
|
||||
import csv
|
||||
from os import getenv
|
||||
import getpass
|
||||
import pickle
|
||||
from pathlib import Path
|
||||
import requests
|
||||
import click
|
||||
|
||||
|
||||
MESHIFY_BASE_URL = "https://henry-pump.meshify.com/api/v3/"#getenv("MESHIFY_BASE_URL")
|
||||
MESHIFY_USERNAME = "nmelone@henry-pump.com"#getenv("MESHIFY_USERNAME")
|
||||
MESHIFY_PASSWORD = "zaq1ZAQ!"#getenv("MESHIFY_PASSWORD")
|
||||
MESHIFY_AUTH = None
|
||||
|
||||
|
||||
class NameNotFound(Exception):
|
||||
"""Thrown when a name is not found in a list of stuff."""
|
||||
|
||||
def __init__(self, message, name, list_of_stuff, *args):
|
||||
"""Initialize the NameNotFound Exception."""
|
||||
self.message = message
|
||||
self.name = name
|
||||
self.list_of_stuff = list_of_stuff
|
||||
super(NameNotFound, self).__init__(message, name, list_of_stuff, *args)
|
||||
|
||||
|
||||
def dict_filter(it, *keys):
|
||||
"""Filter dictionary results."""
|
||||
for d in it:
|
||||
yield dict((k, d[k]) for k in keys)
|
||||
|
||||
|
||||
def check_setup():
|
||||
"""Check the global parameters."""
|
||||
global MESHIFY_USERNAME, MESHIFY_PASSWORD, MESHIFY_AUTH, MESHIFY_BASE_URL
|
||||
if not MESHIFY_USERNAME or not MESHIFY_PASSWORD:
|
||||
print("Simplify the usage by setting the meshify username and password as environment variables MESHIFY_USERNAME and MESHIFY_PASSWORD")
|
||||
MESHIFY_USERNAME = input("Meshify Username: ")
|
||||
MESHIFY_PASSWORD = getpass.getpass("Meshify Password: ")
|
||||
|
||||
MESHIFY_AUTH = requests.auth.HTTPBasicAuth(MESHIFY_USERNAME, MESHIFY_PASSWORD)
|
||||
|
||||
if not MESHIFY_BASE_URL:
|
||||
print("Simplify the usage by setting the environment variable MESHIFY_BASE_URL")
|
||||
MESHIFY_BASE_URL = input("Meshify Base URL: ")
|
||||
|
||||
|
||||
def find_by_name(name, list_of_stuff):
|
||||
"""Find an object in a list of stuff by its name parameter."""
|
||||
for x in list_of_stuff:
|
||||
if x['name'] == name:
|
||||
return x
|
||||
raise NameNotFound("Name not found!", name, list_of_stuff)
|
||||
|
||||
|
||||
def query_meshify_api(endpoint):
|
||||
"""Make a query to the meshify API."""
|
||||
check_setup()
|
||||
if endpoint[0] == "/":
|
||||
endpoint = endpoint[1:]
|
||||
q_url = MESHIFY_BASE_URL + endpoint
|
||||
q_req = requests.get(q_url, auth=MESHIFY_AUTH)
|
||||
return json.loads(q_req.text) if q_req.status_code == 200 else []
|
||||
|
||||
|
||||
def post_meshify_api(endpoint, data):
|
||||
"""Post data to the meshify API."""
|
||||
check_setup()
|
||||
q_url = MESHIFY_BASE_URL + endpoint
|
||||
q_req = requests.post(q_url, data=json.dumps(data), auth=MESHIFY_AUTH)
|
||||
if q_req.status_code != 200:
|
||||
print(q_req.status_code)
|
||||
return json.loads(q_req.text) if q_req.status_code == 200 else []
|
||||
|
||||
|
||||
def decode_channel_parameters(channel):
|
||||
"""Decode a channel object's parameters into human-readable format."""
|
||||
channel_types = {
|
||||
1: 'device',
|
||||
5: 'static',
|
||||
6: 'user input',
|
||||
7: 'system'
|
||||
}
|
||||
|
||||
io_options = {
|
||||
0: 'readonly',
|
||||
1: 'readwrite'
|
||||
}
|
||||
|
||||
datatype_options = {
|
||||
1: "float",
|
||||
2: 'string',
|
||||
3: 'integer',
|
||||
4: 'boolean',
|
||||
5: 'datetime',
|
||||
6: 'timespan',
|
||||
7: 'file',
|
||||
8: 'latlng'
|
||||
}
|
||||
|
||||
channel['channelType'] = channel_types[channel['channelType']]
|
||||
channel['io'] = io_options[channel['io']]
|
||||
channel['dataType'] = datatype_options[channel['dataType']]
|
||||
return channel
|
||||
|
||||
|
||||
def encode_channel_parameters(channel):
|
||||
"""Encode a channel object from human-readable format."""
|
||||
channel_types = {
|
||||
'device': 1,
|
||||
'static': 5,
|
||||
'user input': 6,
|
||||
'system': 7
|
||||
}
|
||||
|
||||
io_options = {
|
||||
'readonly': False,
|
||||
'readwrite': True
|
||||
}
|
||||
|
||||
datatype_options = {
|
||||
"float": 1,
|
||||
'string': 2,
|
||||
'integer': 3,
|
||||
'boolean': 4,
|
||||
'datetime': 5,
|
||||
'timespan': 6,
|
||||
'file': 7,
|
||||
'latlng': 8
|
||||
}
|
||||
try:
|
||||
channel['deviceTypeId'] = int(channel['deviceTypeId'])
|
||||
channel['fromMe'] = channel['fromMe'].lower() == 'true'
|
||||
channel['channelType'] = channel_types[channel['channelType'].lower()]
|
||||
channel['io'] = io_options[channel['io'].lower()]
|
||||
channel['dataType'] = datatype_options[channel['dataType'].lower()]
|
||||
# channel['id'] = 1
|
||||
return channel
|
||||
except KeyError as e:
|
||||
click.echo("Unable to convert channel {} due to bad key: {}".format(channel['name'], e))
|
||||
|
||||
|
||||
def make_modbusmap_channel(i, chan, device_type_name):
|
||||
"""Make a channel object for a row in the CSV."""
|
||||
json_obj = {
|
||||
"ah": "",
|
||||
"bytary": None,
|
||||
"al": "",
|
||||
"vn": chan['subTitle'], # Name
|
||||
"ct": "number", # ChangeType
|
||||
"le": "16", # Length(16 or 32)
|
||||
"grp": str(chan['guaranteedReportPeriod']), # GuaranteedReportPeriod
|
||||
"la": None,
|
||||
"chn": chan['name'], # ChannelName
|
||||
"un": "1", # DeviceNumber
|
||||
"dn": device_type_name, # deviceName
|
||||
"vm": None,
|
||||
"lrt": "0",
|
||||
"da": "300", # DeviceAddress
|
||||
"a": chan['helpExplanation'], # TagName
|
||||
"c": str(chan['change']), # Change
|
||||
"misc_u": str(chan['units']), # Units
|
||||
"f": "1", # FunctionCode
|
||||
"mrt": str(chan['minReportTime']), # MinimumReportTime
|
||||
"m": "none", # multiplier
|
||||
"m1ch": "2-{}".format(i),
|
||||
"mv": "0", # MultiplierValue
|
||||
"s": "On",
|
||||
"r": "{}-{}".format(chan['min'], chan['max']), # range
|
||||
"t": "int" # type
|
||||
}
|
||||
return json_obj
|
||||
|
||||
|
||||
def combine_modbusmap_and_channel(channel_obj, modbus_map):
|
||||
"""Add the parameters from the modbus map to the channel object."""
|
||||
channel_part = modbus_map["1"]["addresses"]["300"]
|
||||
for c in channel_part:
|
||||
if channel_part[c]["chn"] == channel_obj['name']:
|
||||
channel_obj['units'] = channel_part[c]["misc_u"]
|
||||
try:
|
||||
min_max_range = channel_part[c]["r"].split("-")
|
||||
channel_obj['min'] = int(min_max_range[0])
|
||||
channel_obj['max'] = int(min_max_range[1])
|
||||
except Exception:
|
||||
channel_obj['min'] = None
|
||||
channel_obj['max'] = None
|
||||
|
||||
channel_obj['change'] = float(channel_part[c]["c"])
|
||||
channel_obj['guaranteedReportPeriod'] = int(channel_part[c]["grp"])
|
||||
channel_obj['minReportTime'] = int(channel_part[c]["mrt"])
|
||||
return channel_obj
|
||||
return False
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
"""Command Line Interface."""
|
||||
pass
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("device_type_name")
|
||||
@click.option("-o", '--output-file', default=None, help="Where to put the CSV of channels.")
|
||||
@click.option("-m", '--modbusmap-file', default="modbusMap.p", help="The location of the modbusMap.p file")
|
||||
def get_channel_csv(device_type_name, output_file, modbusmap_file):
|
||||
"""Query the meshify API and create a CSV of the current channels."""
|
||||
channel_fieldnames = [
|
||||
'id',
|
||||
'name',
|
||||
'deviceTypeId',
|
||||
'fromMe',
|
||||
'io',
|
||||
'subTitle',
|
||||
'helpExplanation',
|
||||
'channelType',
|
||||
'dataType',
|
||||
'defaultValue',
|
||||
'regex',
|
||||
'regexErrMsg',
|
||||
'units',
|
||||
'min',
|
||||
'max',
|
||||
'change',
|
||||
'guaranteedReportPeriod',
|
||||
'minReportTime'
|
||||
]
|
||||
devicetypes = query_meshify_api('devicetypes')
|
||||
this_devicetype = find_by_name(device_type_name, devicetypes)
|
||||
channels = query_meshify_api('devicetypes/{}/channels'.format(this_devicetype['id']))
|
||||
modbus_map = None
|
||||
|
||||
if Path(modbusmap_file).exists():
|
||||
with open(modbusmap_file, 'rb') as open_mbs_file:
|
||||
modbus_map = pickle.load(open_mbs_file)
|
||||
|
||||
if not output_file:
|
||||
output_file = 'channels_{}.csv'.format(device_type_name)
|
||||
|
||||
with open(output_file, 'w') as csvfile:
|
||||
writer = csv.DictWriter(csvfile, fieldnames=channel_fieldnames)
|
||||
|
||||
writer.writeheader()
|
||||
for ch in channels:
|
||||
if not modbus_map:
|
||||
ch['units'] = None
|
||||
ch['min'] = None
|
||||
ch['max'] = None
|
||||
ch['change'] = None
|
||||
ch['guaranteedReportPeriod'] = None
|
||||
ch['minReportTime'] = None
|
||||
else:
|
||||
combined = combine_modbusmap_and_channel(ch, modbus_map)
|
||||
if combined:
|
||||
ch = combined
|
||||
writer.writerow(decode_channel_parameters(ch))
|
||||
|
||||
click.echo("Wrote channels to {}".format(output_file))
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("device_type_name")
|
||||
@click.argument("csv_file")
|
||||
def post_channel_csv(device_type_name, csv_file):
|
||||
"""Post values from a CSV to Meshify Channel API."""
|
||||
devicetypes = query_meshify_api('devicetypes')
|
||||
this_devicetype = find_by_name(device_type_name, devicetypes)
|
||||
|
||||
with open(csv_file, 'r') as inp_file:
|
||||
reader = csv.DictReader(inp_file)
|
||||
for row in dict_filter(reader, 'name',
|
||||
'deviceTypeId',
|
||||
'fromMe',
|
||||
'io',
|
||||
'subTitle',
|
||||
'helpExplanation',
|
||||
'channelType',
|
||||
'dataType',
|
||||
'defaultValue',
|
||||
'regex',
|
||||
'regexErrMsg'):
|
||||
# print(row)
|
||||
# print(encode_channel_parameters(row))
|
||||
# click.echo(json.dumps(encode_channel_parameters(row), indent=4))
|
||||
if post_meshify_api('devicetypes/{}/channels'.format(this_devicetype['id']), encode_channel_parameters(row)):
|
||||
click.echo("Successfully added channel {}".format(row['name']))
|
||||
else:
|
||||
click.echo("Unable to add channel {}".format(row['name']))
|
||||
|
||||
|
||||
@click.command()
|
||||
def print_channel_options():
|
||||
"""Print channel options for use with the csv files."""
|
||||
channel_types = ['device', 'static', 'user input', 'system']
|
||||
io_options = ['readonly', 'readwrite']
|
||||
datatype_options = [
|
||||
"float",
|
||||
'string',
|
||||
'integer',
|
||||
'boolean',
|
||||
'datetime',
|
||||
'timespan',
|
||||
'file',
|
||||
'latlng'
|
||||
]
|
||||
|
||||
click.echo("\n\nchannelType options")
|
||||
click.echo("===================")
|
||||
for chan in channel_types:
|
||||
click.echo(chan)
|
||||
|
||||
click.echo("\n\nio options")
|
||||
click.echo("==========")
|
||||
for i in io_options:
|
||||
click.echo(i)
|
||||
|
||||
click.echo("\n\ndataType options")
|
||||
click.echo("================")
|
||||
for d in datatype_options:
|
||||
click.echo(d)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("device_type_name")
|
||||
@click.argument("csv_file")
|
||||
def create_modbusMap(device_type_name, csv_file):
|
||||
"""Create modbusMap.p from channel csv file."""
|
||||
modbusMap = {
|
||||
"1": {
|
||||
"c": "ETHERNET/IP",
|
||||
"b": "192.168.1.10",
|
||||
"addresses": {
|
||||
"300": {}
|
||||
},
|
||||
"f": "Off",
|
||||
"p": "",
|
||||
"s": "1"
|
||||
},
|
||||
"2": {
|
||||
"c": "M1-485",
|
||||
"b": "9600",
|
||||
"addresses": {},
|
||||
"f": "Off",
|
||||
"p": "None",
|
||||
"s": "1"
|
||||
}
|
||||
}
|
||||
ind = 1
|
||||
with open(csv_file, 'r') as inp_file:
|
||||
reader = csv.DictReader(inp_file)
|
||||
for row in reader:
|
||||
modbusMap["1"]["addresses"]["300"]["2-{}".format(ind)] = make_modbusmap_channel(ind, row, device_type_name)
|
||||
ind += 1
|
||||
with open("modbusMap.p", 'wb') as mod_map_file:
|
||||
pickle.dump(modbusMap, mod_map_file, protocol=0)
|
||||
|
||||
with open("modbusMap.json", 'w') as json_file:
|
||||
json.dump(modbusMap, json_file, indent=4)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("-i", "--input-file", default="modbusMap.p", help="The modbus map pickle file to convert.")
|
||||
@click.option("-o", "--output", default="modbusMap.json", help="The modbus map json file output filename.")
|
||||
def pickle_to_json(input_file, output):
|
||||
"""Convert a pickle file to a json file."""
|
||||
if not Path(input_file).exists():
|
||||
click.echo("Pickle file {} does not exist".format(input_file))
|
||||
return
|
||||
|
||||
with open(input_file, 'rb') as picklefile:
|
||||
input_contents = pickle.load(picklefile)
|
||||
|
||||
with open(output, 'w') as outfile:
|
||||
json.dump(input_contents, outfile, indent=4)
|
||||
click.echo("Wrote from {} to {}.".format(input_file, output))
|
||||
|
||||
@click.command()
|
||||
@click.option("-i", "--input-file", default="modbusMap.json", help="The modbus map json file to convert.")
|
||||
@click.option("-o", "--output", default="modbusMap.p", help="The modbus map pickle file output filename.")
|
||||
def json_to_pickle(input_file, output):
|
||||
"""Convert a pickle file to a json file."""
|
||||
if not Path(input_file).exists():
|
||||
click.echo("JSON file {} does not exist".format(input_file))
|
||||
return
|
||||
|
||||
with open(input_file, 'rb') as json_file:
|
||||
input_contents = json.load(json_file)
|
||||
|
||||
with open(output, 'wb') as outfile:
|
||||
pickle.dump(input_contents, outfile, protocol=0)
|
||||
click.echo("Wrote from {} to {}.".format(input_file, output))
|
||||
|
||||
|
||||
cli.add_command(get_channel_csv)
|
||||
cli.add_command(post_channel_csv)
|
||||
cli.add_command(print_channel_options)
|
||||
cli.add_command(create_modbusMap)
|
||||
cli.add_command(pickle_to_json)
|
||||
cli.add_command(json_to_pickle)
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli()
|
||||
BIN
POCloud-CookieCutter-master/hooks/meshify.pyc
Normal file
BIN
POCloud-CookieCutter-master/hooks/meshify.pyc
Normal file
Binary file not shown.
103
POCloud-CookieCutter-master/hooks/post_gen_project.py
Normal file
103
POCloud-CookieCutter-master/hooks/post_gen_project.py
Normal file
@@ -0,0 +1,103 @@
|
||||
import csv
|
||||
#Generate a tag/channel list to be used in the template
|
||||
csv_file = bool(input("Do you have a CSV file yes(1)/no(0): "))
|
||||
if not csv_file:
|
||||
num_channels = int(input("Number of Channels in the Driver: "))
|
||||
mesh_name = []
|
||||
tags = []
|
||||
data_type = []
|
||||
change_thres = []
|
||||
guaruntee_sec = []
|
||||
register_num = []
|
||||
unit_number = []
|
||||
channel_size = []
|
||||
if csv_file:
|
||||
csv_path = str(input("Where is your CSV file: "))
|
||||
plc_or_mbs = int(input("Is this for PLC(1) or Modbus(2) channels: "))
|
||||
|
||||
if csv_file:
|
||||
with open(csv_path, 'r') as tagsFile:
|
||||
reader = csv.reader(tagsFile)
|
||||
row_count = sum(1 for row in reader)
|
||||
|
||||
with open(csv_path, 'r') as tagsFile:
|
||||
reader = csv.reader(tagsFile)
|
||||
|
||||
try:
|
||||
f = open("python-driver/Tags.py", "a")
|
||||
print("writing tags")
|
||||
f.write("tags = [ \n")
|
||||
|
||||
counter = 1
|
||||
for row in reader:
|
||||
print("{}".format(row[0]))
|
||||
if plc_or_mbs == 1:
|
||||
f.write("\tPLCChannel(PLC_IP_ADDRESS, \"{}\",\"{}\",\"{}\", {}, {}, plc_type=\"{}\")".format(row[0],row[1],row[2],row[3],row[4],"{{cookiecutter.device_type}}"))
|
||||
else:
|
||||
f.write("\tModbusChannel(\"{}\",{},\"{}\", {}, {},channel_size={}, unit_number={})".format(row[0],row[1],row[2],row[3],row[4], row[5], row[6]))
|
||||
if counter < row_count:
|
||||
f.write(",\n")
|
||||
else:
|
||||
f.write("\n]")
|
||||
counter = counter + 1
|
||||
f.close
|
||||
print("Wrote {} Channels".format(row_count))
|
||||
except:
|
||||
print("Couldn't open file with tags")
|
||||
else:
|
||||
|
||||
|
||||
for x in range(num_channels):
|
||||
mesh_name.append(input("Channel {} meshify name: ".format(x+1)))
|
||||
if plc_or_mbs == 1:
|
||||
tags.append(input("Channel {} tag name: ".format(x+1)))
|
||||
data_type.append(input("Channel {} data type: ".format(x+1)))
|
||||
change_thres.append(input("Channel {} change threshold: ".format(x+1)))
|
||||
guaruntee_sec.append(input("Channel {} guarunteed seconds: ".format(x+1)))
|
||||
if plc_or_mbs == 2:
|
||||
register_num.append(input("Channel {} register number: ".format(x+1)))
|
||||
unit_number.append(input("Channel {} unit number: ".format(x+1)))
|
||||
channel_size.append(input("Channel {} channel size: ".format(x+1)))
|
||||
|
||||
#Generate a Tags.py file to hold a list of PLCChannels or ModbusChannels to be used in the driver to communicate
|
||||
if(num_channels == 0):
|
||||
try:
|
||||
file = open("python-driver/Tags.py", "a")
|
||||
file.write("tags = []")
|
||||
file.close
|
||||
print("Wrote no Channels")
|
||||
except:
|
||||
print("Couldn't open file without tags")
|
||||
elif plc_or_mbs == 1:
|
||||
try:
|
||||
file = open("python-driver/Tags.py", "a")
|
||||
file.write("tags = [ \n")
|
||||
for x in range(num_channels):
|
||||
file.write("\tPLCChannel(PLC_IP_ADDRESS, \"{}\",\"{}\",\"{}\", {}, {}, plc_type=\"{}\")".format(mesh_name[x],tags[x],data_type[x],change_thres[x],guaruntee_sec[x],"{{cookiecutter.device_type}}"))
|
||||
if(x < num_channels-1):
|
||||
file.write(",\n")
|
||||
else:
|
||||
file.write("\n]")
|
||||
|
||||
file.close
|
||||
print("Wrote {} Channels".format(num_channels))
|
||||
except:
|
||||
print("Couldn't open file with tags")
|
||||
else:
|
||||
try:
|
||||
file = open("python-driver/Tags.py", "a")
|
||||
file.write("tags = [ \n")
|
||||
for x in range(num_channels):
|
||||
file.write("\tModbusChannel(\"{}\",{},\"{}\", {}, {},channel_size={}, unit_number={})".format(mesh_name[x],register_num[x],data_type[x],change_thres[x],guaruntee_sec[x],channel_size[x],unit_number[x]))
|
||||
if(x < num_channels-1):
|
||||
file.write(",\n")
|
||||
else:
|
||||
file.write("\n]")
|
||||
|
||||
file.close
|
||||
print("Wrote {} Channels".format(num_channels))
|
||||
except:
|
||||
print("Couldn't open file with tags")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
"""Define Meshify channel class."""
|
||||
import time
|
||||
from pycomm.ab_comm.clx import Driver as ClxDriver
|
||||
from pycomm.cip.cip_base import CommError, DataError
|
||||
from file_logger import filelogger as log
|
||||
import minimalmodbus
|
||||
|
||||
minimalmodbus.BAUDRATE = 9600
|
||||
minimalmodbus.STOPBITS = 1
|
||||
|
||||
|
||||
TAG_DATAERROR_SLEEPTIME = 5
|
||||
|
||||
def binarray(intval):
|
||||
"""Split an integer into its bits."""
|
||||
bin_string = '{0:08b}'.format(intval)
|
||||
bin_arr = [i for i in bin_string]
|
||||
bin_arr.reverse()
|
||||
return bin_arr
|
||||
|
||||
|
||||
def read_tag(addr, tag, plc_type="CLX"):
|
||||
"""Read a tag from the PLC."""
|
||||
direct = plc_type == "Micro800"
|
||||
clx = ClxDriver()
|
||||
try:
|
||||
if clx.open(addr, direct_connection=direct):
|
||||
try:
|
||||
val = clx.read_tag(tag)
|
||||
clx.close()
|
||||
return val
|
||||
except DataError as err:
|
||||
clx.close()
|
||||
time.sleep(TAG_DATAERROR_SLEEPTIME)
|
||||
log.error("Data Error during readTag({}, {}): {}".format(addr, tag, err))
|
||||
except CommError:
|
||||
# err = c.get_status()
|
||||
|
||||
log.error("Could not connect during readTag({}, {})".format(addr, tag))
|
||||
except AttributeError as err:
|
||||
clx.close()
|
||||
log.error("AttributeError during readTag({}, {}): \n{}".format(addr, tag, err))
|
||||
clx.close()
|
||||
return False
|
||||
|
||||
|
||||
def read_array(addr, tag, start, end, plc_type="CLX"):
|
||||
"""Read an array from the PLC."""
|
||||
direct = plc_type == "Micro800"
|
||||
clx = ClxDriver()
|
||||
if clx.open(addr, direct_connection=direct):
|
||||
arr_vals = []
|
||||
try:
|
||||
for i in range(start, end):
|
||||
tag_w_index = tag + "[{}]".format(i)
|
||||
val = clx.read_tag(tag_w_index)
|
||||
arr_vals.append(round(val[0], 4))
|
||||
if arr_vals:
|
||||
clx.close()
|
||||
return arr_vals
|
||||
else:
|
||||
log.error("No length for {}".format(addr))
|
||||
clx.close()
|
||||
return False
|
||||
except Exception:
|
||||
log.error("Error during readArray({}, {}, {}, {})".format(addr, tag, start, end))
|
||||
err = clx.get_status()
|
||||
clx.close()
|
||||
log.error(err)
|
||||
clx.close()
|
||||
|
||||
|
||||
def write_tag(addr, tag, val, plc_type="CLX"):
|
||||
"""Write a tag value to the PLC."""
|
||||
direct = plc_type == "Micro800"
|
||||
clx = ClxDriver()
|
||||
try:
|
||||
if clx.open(addr, direct_connection=direct):
|
||||
try:
|
||||
initial_val = clx.read_tag(tag)
|
||||
write_status = clx.write_tag(tag, val, initial_val[1])
|
||||
clx.close()
|
||||
return write_status
|
||||
except DataError as err:
|
||||
clx_err = clx.get_status()
|
||||
clx.close()
|
||||
log.error("--\nDataError during writeTag({}, {}, {}, plc_type={}) -- {}\n{}\n".format(addr, tag, val, plc_type, err, clx_err))
|
||||
|
||||
except CommError as err:
|
||||
clx_err = clx.get_status()
|
||||
log.error("--\nCommError during write_tag({}, {}, {}, plc_type={})\n{}\n--".format(addr, tag, val, plc_type, err))
|
||||
clx.close()
|
||||
return False
|
||||
|
||||
|
||||
class Channel(object):
|
||||
"""Holds the configuration for a Meshify channel."""
|
||||
|
||||
def __init__(self, mesh_name, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False):
|
||||
"""Initialize the channel."""
|
||||
self.mesh_name = mesh_name
|
||||
self.data_type = data_type
|
||||
self.last_value = None
|
||||
self.value = None
|
||||
self.last_send_time = 0
|
||||
self.chg_threshold = chg_threshold
|
||||
self.guarantee_sec = guarantee_sec
|
||||
self.map_ = map_
|
||||
self.write_enabled = write_enabled
|
||||
|
||||
def __str__(self):
|
||||
"""Create a string for the channel."""
|
||||
return "{}\nvalue: {}, last_send_time: {}".format(self.mesh_name, self.value, self.last_send_time)
|
||||
|
||||
def check(self, new_value, force_send=False):
|
||||
"""Check to see if the new_value needs to be stored."""
|
||||
send_needed = False
|
||||
send_reason = ""
|
||||
if self.data_type == 'BOOL' or self.data_type == 'STRING':
|
||||
if self.last_send_time == 0:
|
||||
send_needed = True
|
||||
send_reason = "no send time"
|
||||
elif self.value is None:
|
||||
send_needed = True
|
||||
send_reason = "no value"
|
||||
elif self.value != new_value:
|
||||
if self.map_:
|
||||
if not self.value == self.map_[new_value]:
|
||||
send_needed = True
|
||||
send_reason = "value change"
|
||||
else:
|
||||
send_needed = True
|
||||
send_reason = "value change"
|
||||
elif (time.time() - self.last_send_time) > self.guarantee_sec:
|
||||
send_needed = True
|
||||
send_reason = "guarantee sec"
|
||||
elif force_send:
|
||||
send_needed = True
|
||||
send_reason = "forced"
|
||||
else:
|
||||
if self.last_send_time == 0:
|
||||
send_needed = True
|
||||
send_reason = "no send time"
|
||||
elif self.value is None:
|
||||
send_needed = True
|
||||
send_reason = "no value"
|
||||
elif abs(self.value - new_value) > self.chg_threshold:
|
||||
send_needed = True
|
||||
send_reason = "change threshold"
|
||||
elif (time.time() - self.last_send_time) > self.guarantee_sec:
|
||||
send_needed = True
|
||||
send_reason = "guarantee sec"
|
||||
elif force_send:
|
||||
send_needed = True
|
||||
send_reason = "forced"
|
||||
if send_needed:
|
||||
self.last_value = self.value
|
||||
if self.map_:
|
||||
try:
|
||||
self.value = self.map_[new_value]
|
||||
except KeyError:
|
||||
log.error("Cannot find a map value for {} in {} for {}".format(new_value, self.map_, self.mesh_name))
|
||||
self.value = new_value
|
||||
else:
|
||||
self.value = new_value
|
||||
self.last_send_time = time.time()
|
||||
log.info("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
|
||||
return send_needed
|
||||
|
||||
def read(self):
|
||||
"""Read the value."""
|
||||
pass
|
||||
|
||||
def identity(sent):
|
||||
"""Return exactly what was sent to it."""
|
||||
return sent
|
||||
|
||||
|
||||
class ModbusChannel(Channel):
|
||||
"""Modbus channel object."""
|
||||
|
||||
def __init__(self, mesh_name, register_number, data_type, chg_threshold, guarantee_sec, channel_size=1, map_=False, write_enabled=False, transform_fn=identity):
|
||||
"""Initialize the channel."""
|
||||
super(ModbusChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
|
||||
self.mesh_name = mesh_name
|
||||
self.register_number = register_number
|
||||
self.channel_size = channel_size
|
||||
self.data_type = data_type
|
||||
self.last_value = None
|
||||
self.value = None
|
||||
self.last_send_time = 0
|
||||
self.chg_threshold = chg_threshold
|
||||
self.guarantee_sec = guarantee_sec
|
||||
self.map_ = map_
|
||||
self.write_enabled = write_enabled
|
||||
self.transform_fn = transform_fn
|
||||
|
||||
def read(self, mbsvalue):
|
||||
"""Return the transformed read value."""
|
||||
return self.transform_fn(mbsvalue)
|
||||
|
||||
|
||||
class PLCChannel(Channel):
|
||||
"""PLC Channel Object."""
|
||||
|
||||
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False, plc_type='CLX'):
|
||||
"""Initialize the channel."""
|
||||
super(PLCChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
|
||||
self.plc_ip = ip
|
||||
self.mesh_name = mesh_name
|
||||
self.plc_tag = plc_tag
|
||||
self.data_type = data_type
|
||||
self.last_value = None
|
||||
self.value = None
|
||||
self.last_send_time = 0
|
||||
self.chg_threshold = chg_threshold
|
||||
self.guarantee_sec = guarantee_sec
|
||||
self.map_ = map_
|
||||
self.write_enabled = write_enabled
|
||||
self.plc_type = plc_type
|
||||
|
||||
def read(self):
|
||||
"""Read the value."""
|
||||
plc_value = None
|
||||
if self.plc_tag and self.plc_ip:
|
||||
read_value = read_tag(self.plc_ip, self.plc_tag, plc_type=self.plc_type)
|
||||
if read_value:
|
||||
plc_value = read_value[0]
|
||||
|
||||
return plc_value
|
||||
|
||||
|
||||
class BoolArrayChannels(Channel):
|
||||
"""Hold the configuration for a set of boolean array channels."""
|
||||
|
||||
def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False):
|
||||
"""Initialize the channel."""
|
||||
super(BoolArrayChannels, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled)
|
||||
self.plc_ip = ip
|
||||
self.mesh_name = mesh_name
|
||||
self.plc_tag = plc_tag
|
||||
self.data_type = data_type
|
||||
self.last_value = None
|
||||
self.value = None
|
||||
self.last_send_time = 0
|
||||
self.chg_threshold = chg_threshold
|
||||
self.guarantee_sec = guarantee_sec
|
||||
self.map_ = map_
|
||||
self.write_enabled = write_enabled
|
||||
|
||||
def compare_values(self, new_val_dict):
|
||||
"""Compare new values to old values to see if the values need storing."""
|
||||
send = False
|
||||
for idx in new_val_dict:
|
||||
try:
|
||||
if new_val_dict[idx] != self.last_value[idx]:
|
||||
send = True
|
||||
except KeyError:
|
||||
log.error("Key Error in self.compare_values for index {}".format(idx))
|
||||
send = True
|
||||
return send
|
||||
|
||||
def read(self, force_send=False):
|
||||
"""Read the value and check to see if needs to be stored."""
|
||||
send_needed = False
|
||||
send_reason = ""
|
||||
if self.plc_tag:
|
||||
val = read_tag(self.plc_ip, self.plc_tag)
|
||||
if val:
|
||||
bool_arr = binarray(val[0])
|
||||
new_val = {}
|
||||
for idx in self.map_:
|
||||
try:
|
||||
new_val[self.map_[idx]] = bool_arr[idx]
|
||||
except KeyError:
|
||||
log.error("Not able to get value for index {}".format(idx))
|
||||
|
||||
if self.last_send_time == 0:
|
||||
send_needed = True
|
||||
send_reason = "no send time"
|
||||
elif self.value is None:
|
||||
send_needed = True
|
||||
send_reason = "no value"
|
||||
elif self.compare_values(new_val):
|
||||
send_needed = True
|
||||
send_reason = "value change"
|
||||
elif (time.time() - self.last_send_time) > self.guarantee_sec:
|
||||
send_needed = True
|
||||
send_reason = "guarantee sec"
|
||||
elif force_send:
|
||||
send_needed = True
|
||||
send_reason = "forced"
|
||||
|
||||
if send_needed:
|
||||
self.value = new_val
|
||||
self.last_value = self.value
|
||||
self.last_send_time = time.time()
|
||||
log.info("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason))
|
||||
return send_needed
|
||||
@@ -0,0 +1,3 @@
|
||||
from Channel import PLCChannel, ModbusChannel
|
||||
from {{cookiecutter.driver_name}} import PLC_IP_ADDRESS
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"files": {
|
||||
"file3": "file_logger.py",
|
||||
"file2": "Channel.py",
|
||||
"file1": "{{cookiecutter.driver_name}}.py",
|
||||
"file6": "persistence.py",
|
||||
"file5": "utilities.py",
|
||||
"file4": "Tags.py"
|
||||
},
|
||||
"deviceName": "{{cookiecutter.driver_name}}",
|
||||
"releaseVersion": "1",
|
||||
"driverFileName": "{{cookiecutter.driver_name}}.py",
|
||||
"driverId": "0100"
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
import types
|
||||
import traceback
|
||||
import binascii
|
||||
import threading
|
||||
import time
|
||||
import thread
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
import textwrap
|
||||
import Queue
|
||||
import json
|
||||
|
||||
|
||||
class deviceBase():
|
||||
|
||||
def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None):
|
||||
self.offset = offset
|
||||
self.company = companyId
|
||||
self.name = name
|
||||
self.number = number
|
||||
self.q = Q
|
||||
self.deviceName = name + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!'
|
||||
self.chName = "M1" + '_[' + mac + ':'
|
||||
self.chName2 = '_[' + mac + ':'
|
||||
print 'device name is:'
|
||||
print self.deviceName
|
||||
mac2 = mac.replace(":", "")
|
||||
self.mac = mac2.upper()
|
||||
self.address = 1
|
||||
self.debug = True
|
||||
self.mcu = mcu
|
||||
self.firstRun = True
|
||||
self.mqtt = mqtt
|
||||
self.nodes = Nodes
|
||||
#local dictionary of derived nodes ex: localNodes[tank_0199] = self
|
||||
self.localNodes = {}
|
||||
os.system("chmod 777 /root/reboot")
|
||||
os.system("echo nameserver 8.8.8.8 > /etc/resolv.conf")
|
||||
#Queue for imcoming sets
|
||||
self.loraQ = Queue.Queue()
|
||||
|
||||
self.knownIDs = []
|
||||
thread.start_new_thread(self.getSetsThread, ())
|
||||
|
||||
def getSetsThread(self):
|
||||
|
||||
while True:
|
||||
try:
|
||||
item = self.loraQ.get(block=True, timeout=600)
|
||||
try:
|
||||
print "here is the item from the sets q"
|
||||
print item
|
||||
if len(item) == 2:
|
||||
techname = str(json.loads(item[1])[0]['payload']['name'].split(".")[0])
|
||||
channel = str(json.loads(item[1])[0]['payload']['name'].split(".")[1])
|
||||
name = techname.split("_")[0]
|
||||
id = techname.split("_")[1][1:-2].replace(":","").upper()
|
||||
value = json.loads(item[1])[0]['payload']['value']
|
||||
msgId = json.loads(item[1])[0]['msgId']
|
||||
|
||||
print channel, value, id, name, msgId
|
||||
success = self.specificSets(channel, value, id, name)
|
||||
|
||||
if success == True:
|
||||
print "SUCCESS ON SET"
|
||||
if int(msgId) == 0:
|
||||
return
|
||||
lc = self.getTime()
|
||||
|
||||
value = str(self.mac) + " Success Setting: " + channel + " To: " + value
|
||||
msg = """[ { "value":"%s", "timestamp":"%s", "msgId":"%s" } ]""" % (value, str(lc), msgId)
|
||||
print value
|
||||
print msg
|
||||
topic = "meshify/responses/" + str(msgId)
|
||||
print topic
|
||||
self.q.put([topic, str(msg), 2])
|
||||
|
||||
|
||||
else:
|
||||
|
||||
lc = self.getTime()
|
||||
if success == False:
|
||||
reason = "(Internal Gateway/Device Error)"
|
||||
else:
|
||||
reason = success
|
||||
value = str(self.mac) + " Failed Setting: " + channel + " To: " + value + " " + reason
|
||||
msg = """[ { "value":"%s", "timestamp":"%s", "msgId":"%s" } ]""" % (value, str(lc), msgId)
|
||||
topic = "meshify/responses/" + msgId
|
||||
self.q.put([topic, str(msg), 2])
|
||||
|
||||
except:
|
||||
if int(msgId) == 0:
|
||||
return
|
||||
lc = self.getTime()
|
||||
value = str(self.mac) + " Failed Setting: " + channel + " To: " + value + " (No Callback Found)"
|
||||
msg = """[ { "value":"%s", "timestamp":"%s", "msgId":"%s" } ]""" % (value, str(lc), msgId)
|
||||
topic = "meshify/responses/" + msgId
|
||||
self.q.put([topic, str(msg), 2])
|
||||
print 'no Set callback found for channel: ' + funcName
|
||||
|
||||
except:
|
||||
print "sets queue timeout, restarting..."
|
||||
|
||||
|
||||
def sendtodbDevLora(self, id, channel, value, timestamp, deviceName):
|
||||
|
||||
|
||||
|
||||
mac = self.mac
|
||||
|
||||
if deviceName == "mainMeshify":
|
||||
zigmac = "_[01:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
|
||||
else:
|
||||
zigmac = "_[00:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
|
||||
dname = deviceName + zigmac
|
||||
|
||||
#define dname, make id into techname and mac
|
||||
if id not in self.knownIDs:
|
||||
self.knownIDs.append(id)
|
||||
self.mcu.xbees[dname] = self.loraQ
|
||||
|
||||
#meshify/db/330/C493000354FB/ilora/c493000354fb2A6E/a1-v
|
||||
#[ { "value":"0.5635", "timestamp":"1486039316" } ]
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel)
|
||||
print topic
|
||||
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodbLocLora(self, id, channel, value, timestamp, deviceName):
|
||||
|
||||
|
||||
|
||||
mac = id
|
||||
while len(mac) < 12:
|
||||
mac = "0" + mac
|
||||
if deviceName == "mainMeshify":
|
||||
zigmac = "_[01:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
|
||||
else:
|
||||
zigmac = "_[00:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
|
||||
dname = deviceName + zigmac
|
||||
|
||||
#define dname, make id into techname and mac
|
||||
if id not in self.knownIDs:
|
||||
self.knownIDs.append(id)
|
||||
topic = str(("meshify/sets/" + str(self.company) + "/" + mac + "/#"))
|
||||
self.mqtt.subscribe(topic, 0)
|
||||
topic = str(("meshify/sets/" + "1" + "/" + mac + "/#"))
|
||||
self.mqtt.subscribe(topic, 0)
|
||||
self.mcu.xbees[dname] = self.loraQ
|
||||
|
||||
#meshify/db/330/C493000354FB/ilora/c493000354fb2A6E/a1-v
|
||||
#[ { "value":"0.5635", "timestamp":"1486039316" } ]
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel)
|
||||
print topic
|
||||
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodbLocLoraCom(self, id, channel, value, timestamp, deviceName):
|
||||
|
||||
|
||||
|
||||
mac = "1" + id
|
||||
while len(mac) < 12:
|
||||
mac = "0" + mac
|
||||
|
||||
if deviceName == "mainMeshify":
|
||||
zigmac = "_[01:00:00:00:00:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
|
||||
else:
|
||||
zigmac = "_[00:00:00:00:01:" + id[0:2] + ":" + id[2:4] + ":" + id[4:6] + "]!"
|
||||
dname = deviceName + zigmac
|
||||
|
||||
#define dname, make id into techname and mac
|
||||
if id not in self.knownIDs:
|
||||
self.knownIDs.append(id)
|
||||
topic = str(("meshify/sets/" + str(self.company) + "/" + mac + "/#"))
|
||||
self.mqtt.subscribe(topic, 0)
|
||||
topic = str(("meshify/sets/" + "1" + "/" + mac + "/#"))
|
||||
self.mqtt.subscribe(topic, 0)
|
||||
self.mcu.xbees[dname] = self.loraQ
|
||||
|
||||
#meshify/db/330/C493000354FB/ilora/c493000354fb2A6E/a1-v
|
||||
#[ { "value":"0.5635", "timestamp":"1486039316" } ]
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel)
|
||||
print topic
|
||||
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodbLoc(self, ch, channel, value, timestamp, deviceName, mac):
|
||||
|
||||
|
||||
#this will add your derived nodes the master nodes list, allowing them to receive sets!!
|
||||
localNodesName = deviceName + "_" + str(ch) + "99"
|
||||
|
||||
if not self.localNodes.has_key(localNodesName):
|
||||
self.localNodes[localNodesName] = True
|
||||
self.nodes[localNodesName] = self
|
||||
|
||||
#make the techname
|
||||
lst = textwrap.wrap(str(mac), width=2)
|
||||
tech = ""
|
||||
for i in range(len(lst)):
|
||||
tech += lst[i].lower() + ":"
|
||||
|
||||
|
||||
chName2 = '_[' + tech
|
||||
|
||||
if int(ch) < 10:
|
||||
ch = "0" + str(int(ch))
|
||||
|
||||
if len(ch) > 2:
|
||||
ch = ch[:-2]
|
||||
|
||||
dname = deviceName + chName2 + str(ch) + ":98]!"
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel)
|
||||
print topic
|
||||
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodbDevJSON(self, ch, channel, value, timestamp, deviceName):
|
||||
|
||||
if int(ch) < 10:
|
||||
ch = "0" + str(int(ch))
|
||||
dname = deviceName + self.chName2 + str(ch) + ":99]!"
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel)
|
||||
print topic
|
||||
msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodbLora(self, ch, channel, value, timestamp, deviceName):
|
||||
|
||||
if ":" not in ch:
|
||||
ch = ch[0:2] + ":" + ch[2:4]
|
||||
|
||||
#this will add your derived nodes the master nodes list, allowing them to receive sets!!
|
||||
localNodesName = deviceName + "_" + str(ch).replace(':', "")
|
||||
|
||||
if not self.localNodes.has_key(localNodesName):
|
||||
self.localNodes[localNodesName] = True
|
||||
self.nodes[localNodesName] = self
|
||||
|
||||
|
||||
|
||||
dname = deviceName + self.chName2 + str(ch) + "]!"
|
||||
|
||||
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel)
|
||||
print topic
|
||||
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodbDev(self, ch, channel, value, timestamp, deviceName):
|
||||
|
||||
|
||||
#this will add your derived nodes the master nodes list, allowing them to receive sets!!
|
||||
localNodesName = deviceName + "_" + str(ch) + "99"
|
||||
|
||||
if not self.localNodes.has_key(localNodesName):
|
||||
self.localNodes[localNodesName] = True
|
||||
self.nodes[localNodesName] = self
|
||||
|
||||
if int(ch) < 10:
|
||||
ch = "0" + str(int(ch))
|
||||
|
||||
dname = deviceName + self.chName2 + str(ch) + ":99]!"
|
||||
|
||||
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel)
|
||||
print topic
|
||||
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodbCH(self, ch, channel, value, timestamp):
|
||||
|
||||
|
||||
if int(ch) < 10:
|
||||
ch = "0" + str(ch)
|
||||
|
||||
dname = self.chName + str(ch) + ":99]!"
|
||||
|
||||
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel)
|
||||
print topic
|
||||
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodb(self, channel, value, timestamp):
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
if timestamp < 1400499858:
|
||||
return
|
||||
else:
|
||||
timestamp = str(int(timestamp) + int(self.offset))
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel)
|
||||
print topic
|
||||
msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
|
||||
def sendtodbJSON(self, channel, value, timestamp):
|
||||
|
||||
if int(timestamp) == 0:
|
||||
timestamp = self.getTime()
|
||||
if timestamp < 1400499858:
|
||||
return
|
||||
else:
|
||||
timestamp = str(int(timestamp) + int(self.offset))
|
||||
|
||||
topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel)
|
||||
print topic
|
||||
msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp))
|
||||
print msg
|
||||
self.q.put([topic, msg, 0])
|
||||
def getTime(self):
|
||||
return str(int(time.time() + int(self.offset)))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
"""Logging setup for {{cookiecutter.driver_name}}"""
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import sys
|
||||
|
||||
log_formatter = logging.Formatter('%(asctime)s %(levelname)s %(funcName)s(%(lineno)d) %(message)s')
|
||||
log_file = './{{cookiecutter.driver_name}}.log'
|
||||
my_handler = RotatingFileHandler(log_file, mode='a', maxBytes=500*1024,
|
||||
backupCount=2, encoding=None, delay=0)
|
||||
my_handler.setFormatter(log_formatter)
|
||||
my_handler.setLevel(logging.INFO)
|
||||
filelogger = logging.getLogger('{{cookiecutter.driver_name}}')
|
||||
filelogger.setLevel(logging.INFO)
|
||||
filelogger.addHandler(my_handler)
|
||||
|
||||
console_out = logging.StreamHandler(sys.stdout)
|
||||
console_out.setFormatter(log_formatter)
|
||||
filelogger.addHandler(console_out)
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Data persistance functions."""
|
||||
# if more advanced persistence is needed, use a sqlite database
|
||||
import json
|
||||
|
||||
|
||||
def load(filename="persist.json"):
|
||||
"""Load persisted settings from the specified file."""
|
||||
try:
|
||||
with open(filename, 'r') as persist_file:
|
||||
return json.load(persist_file)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def store(persist_obj, filename="persist.json"):
|
||||
"""Store the persisting settings into the specified file."""
|
||||
try:
|
||||
with open(filename, 'w') as persist_file:
|
||||
return json.dump(persist_obj, persist_file, indent=4)
|
||||
except Exception:
|
||||
return False
|
||||
@@ -0,0 +1,52 @@
|
||||
"""Utility functions for the driver."""
|
||||
import socket
|
||||
import struct
|
||||
|
||||
|
||||
def get_public_ip_address():
|
||||
"""Find the public IP Address of the host device."""
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.connect(("8.8.8.8", 80))
|
||||
ip_address = sock.getsockname()[0]
|
||||
sock.close()
|
||||
except Exception as e:
|
||||
return e
|
||||
return ip_address
|
||||
|
||||
|
||||
def int_to_float16(int_to_convert):
|
||||
"""Convert integer into float16 representation."""
|
||||
bin_rep = ('0' * 16 + '{0:b}'.format(int_to_convert))[-16:]
|
||||
sign = 1.0
|
||||
if int(bin_rep[0]) == 1:
|
||||
sign = -1.0
|
||||
exponent = float(int(bin_rep[1:6], 2))
|
||||
fraction = float(int(bin_rep[6:17], 2))
|
||||
|
||||
if exponent == float(0b00000):
|
||||
return sign * 2 ** -14 * fraction / (2.0 ** 10.0)
|
||||
elif exponent == float(0b11111):
|
||||
if fraction == 0:
|
||||
return sign * float("inf")
|
||||
return float("NaN")
|
||||
frac_part = 1.0 + fraction / (2.0 ** 10.0)
|
||||
return sign * (2 ** (exponent - 15)) * frac_part
|
||||
|
||||
|
||||
def ints_to_float(int1, int2):
|
||||
"""Convert 2 registers into a floating point number."""
|
||||
mypack = struct.pack('>HH', int1, int2)
|
||||
f_unpacked = struct.unpack('>f', mypack)
|
||||
print("[{}, {}] >> {}".format(int1, int2, f_unpacked[0]))
|
||||
return f_unpacked[0]
|
||||
|
||||
|
||||
def degf_to_degc(temp_f):
|
||||
"""Convert deg F to deg C."""
|
||||
return (temp_f - 32.0) * (5.0/9.0)
|
||||
|
||||
|
||||
def degc_to_degf(temp_c):
|
||||
"""Convert deg C to deg F."""
|
||||
return temp_c * 1.8 + 32.0
|
||||
@@ -0,0 +1,133 @@
|
||||
"""Driver for {{cookiecutter.driver_name}}"""
|
||||
|
||||
import threading
|
||||
import json
|
||||
import time
|
||||
from random import randint
|
||||
import os
|
||||
from device_base import deviceBase
|
||||
from Channel import PLCChannel, ModbusChannel,read_tag, write_tag, TAG_DATAERROR_SLEEPTIME
|
||||
import persistence
|
||||
from utilities import get_public_ip_address
|
||||
from file_logger import filelogger as log
|
||||
PLC_IP_ADDRESS = "{{cookiecutter.ip_address}}"
|
||||
from Tags import tags
|
||||
|
||||
_ = None
|
||||
|
||||
log.info("{{cookiecutter.driver_name}} startup")
|
||||
|
||||
# GLOBAL VARIABLES
|
||||
WAIT_FOR_CONNECTION_SECONDS = 20
|
||||
IP_CHECK_PERIOD = 60
|
||||
|
||||
|
||||
CHANNELS = tags
|
||||
|
||||
# PERSISTENCE FILE
|
||||
PERSIST = persistence.load()
|
||||
|
||||
|
||||
class start(threading.Thread, deviceBase):
|
||||
"""Start class required by Meshify."""
|
||||
|
||||
def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None,
|
||||
companyId=None, offset=None, mqtt=None, Nodes=None):
|
||||
"""Initialize the driver."""
|
||||
threading.Thread.__init__(self)
|
||||
deviceBase.__init__(self, name=name, number=number, mac=mac, Q=Q,
|
||||
mcu=mcu, companyId=companyId, offset=offset,
|
||||
mqtt=mqtt, Nodes=Nodes)
|
||||
|
||||
self.daemon = True
|
||||
self.version = "1"
|
||||
self.finished = threading.Event()
|
||||
self.force_send = False
|
||||
self.public_ip_address = ""
|
||||
self.public_ip_address_last_checked = 0
|
||||
threading.Thread.start(self)
|
||||
|
||||
# this is a required function for all drivers, its goal is to upload some piece of data
|
||||
# about your device so it can be seen on the web
|
||||
def register(self):
|
||||
"""Register the driver."""
|
||||
# self.sendtodb("log", "BOOM! Booted.", 0)
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
"""Actually run the driver."""
|
||||
for i in range(0, WAIT_FOR_CONNECTION_SECONDS):
|
||||
print("{{cookiecutter.driver_name}} driver will start in {} seconds".format(WAIT_FOR_CONNECTION_SECONDS - i))
|
||||
time.sleep(1)
|
||||
log.info("BOOM! Starting {{cookiecutter.driver_name}} driver...")
|
||||
|
||||
self._check_ip_address()
|
||||
|
||||
self.nodes["{{cookiecutter.driver_name}}_0199"] = self
|
||||
|
||||
send_loops = 0
|
||||
|
||||
while True:
|
||||
now = time.time()
|
||||
if self.force_send:
|
||||
log.warning("FORCE SEND: TRUE")
|
||||
|
||||
for chan in CHANNELS:
|
||||
val = chan.read()
|
||||
if chan.check(val, self.force_send):
|
||||
self.sendtodbDev(1, chan.mesh_name, chan.value, 0, '{{cookiecutter.driver_name}}')
|
||||
time.sleep(TAG_DATAERROR_SLEEPTIME) # sleep to allow Micro800 to handle ENET requests
|
||||
|
||||
# print("{{cookiecutter.driver_name}} driver still alive...")
|
||||
if self.force_send:
|
||||
if send_loops > 2:
|
||||
log.warning("Turning off force_send")
|
||||
self.force_send = False
|
||||
send_loops = 0
|
||||
else:
|
||||
send_loops += 1
|
||||
|
||||
|
||||
if (now - self.public_ip_address_last_checked) > IP_CHECK_PERIOD:
|
||||
self._check_ip_address()
|
||||
|
||||
|
||||
def _check_ip_address(self):
|
||||
"""Check the public IP address and send to Meshify if changed."""
|
||||
self.public_ip_address_last_checked = time.time()
|
||||
test_public_ip = get_public_ip_address()
|
||||
if not test_public_ip == self.public_ip_address:
|
||||
self.sendtodbDev(1, 'public_ip_address', test_public_ip, 0, '{{cookiecutter.driver_name}}')
|
||||
self.public_ip_address = test_public_ip
|
||||
hostname = "google.com"
|
||||
response = os.system("ping -c 1 " + hostname)
|
||||
|
||||
#and then check the response...
|
||||
if response == 0:
|
||||
print hostname, 'is up!'
|
||||
self.ping_counter = 0
|
||||
else:
|
||||
print hostname, 'is down!'
|
||||
self.ping_counter += 1
|
||||
|
||||
if self.ping_counter >= 3:
|
||||
log.info("Rebooting because no internet detected")
|
||||
os.system('reboot')
|
||||
|
||||
|
||||
def {{cookiecutter.driver_name}}_sync(self, name, value):
|
||||
"""Sync all data from the driver."""
|
||||
self.force_send = True
|
||||
# self.sendtodb("log", "synced", 0)
|
||||
return True
|
||||
|
||||
def {{cookiecutter.driver_name}}_writeplctag(self, name, value):
|
||||
"""Write a value to the PLC."""
|
||||
new_val = json.loads(str(value).replace("'", '"'))
|
||||
tag_n = str(new_val['tag']) # "cmd_Start"
|
||||
val_n = new_val['val']
|
||||
write_res = write_tag(str(PLC_IP_ADDRESS), tag_n, val_n, plc_type="{{cookiecutter.device_type}}")
|
||||
print("Result of {{cookiecutter.driver_name}}_writeplctag(self, {}, {}) = {}".format(name, value, write_res))
|
||||
if write_res is None:
|
||||
write_res = "Error writing to PLC..."
|
||||
return write_res
|
||||
Reference in New Issue
Block a user