631 lines
34 KiB
INI
631 lines
34 KiB
INI
{
|
|
"controllers": [
|
|
{
|
|
"protocol": "Modbus-RTU",
|
|
"name": "flowmeter",
|
|
"endpoint": "rs485",
|
|
"args": {
|
|
"slaveAddr": 247,
|
|
"int16Ord": "ab",
|
|
"int32Ord": "cdab",
|
|
"float32Ord": "cdab",
|
|
"int64Ord": "ghefcdab",
|
|
"enableMsecSample": 0,
|
|
"continuousAcquisition": 1,
|
|
"maxContinuousNumber": 64,
|
|
"communicationInterval": 3,
|
|
"msecSamplePeriod": 500,
|
|
"msecPackage": 20,
|
|
"float64Ord": "abcdefgh",
|
|
"writeCoilFunction": 5,
|
|
"writeRegisterFunction": 6
|
|
},
|
|
"samplePeriod": 10,
|
|
"expired": 10000,
|
|
"desc": "",
|
|
"enable": 1,
|
|
"enableDebug": 0,
|
|
"enablepollCycle": 0,
|
|
"samplePeriod2": 60,
|
|
"_id": "0000685c537fce84"
|
|
}
|
|
],
|
|
"measures": [
|
|
{
|
|
"name": "flowrate",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "periodic",
|
|
"dataType": "FLOAT",
|
|
"addr": "43874",
|
|
"enableRequestCount": 0,
|
|
"decimal": 2,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0000686d7b9aaf16"
|
|
},
|
|
{
|
|
"name": "totalizer_1",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "periodic",
|
|
"dataType": "FLOAT",
|
|
"addr": "42610",
|
|
"enableRequestCount": 0,
|
|
"decimal": 2,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0001686d7b9ab86d"
|
|
},
|
|
{
|
|
"name": "totalizer_2",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "periodic",
|
|
"dataType": "FLOAT",
|
|
"addr": "42810",
|
|
"enableRequestCount": 0,
|
|
"decimal": 2,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0002686d7b9ad656"
|
|
},
|
|
{
|
|
"name": "totalizer_3",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "periodic",
|
|
"dataType": "FLOAT",
|
|
"addr": "43010",
|
|
"enableRequestCount": 0,
|
|
"decimal": 2,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0003686d7b9a54b2"
|
|
},
|
|
{
|
|
"name": "flow_unit",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "onChange",
|
|
"dataType": "INT",
|
|
"addr": "42103",
|
|
"enableRequestCount": 0,
|
|
"enableBit": 0,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"deadZoneType": 0,
|
|
"deadZonePercent": "0",
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0004686d7b9acfb5"
|
|
},
|
|
{
|
|
"name": "totalizer_1_unit",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "onChange",
|
|
"dataType": "INT",
|
|
"addr": "44604",
|
|
"enableRequestCount": 0,
|
|
"enableBit": 0,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"deadZoneType": 0,
|
|
"deadZonePercent": "0",
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0005686d7b9afee5"
|
|
},
|
|
{
|
|
"name": "totalizer_2_unit",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "onChange",
|
|
"dataType": "INT",
|
|
"addr": "44605",
|
|
"enableRequestCount": 0,
|
|
"enableBit": 0,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"deadZoneType": 0,
|
|
"deadZonePercent": "0",
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0006686d7b9a8c57"
|
|
},
|
|
{
|
|
"name": "totalizer_3_unit",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "periodic",
|
|
"dataType": "INT",
|
|
"addr": "44606",
|
|
"enableRequestCount": 0,
|
|
"enableBit": 0,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0007686d7b9a2751"
|
|
},
|
|
{
|
|
"name": "reset_totalizers",
|
|
"ctrlName": "flowmeter",
|
|
"group": "",
|
|
"uploadType": "never",
|
|
"dataType": "INT",
|
|
"addr": "42609",
|
|
"enableRequestCount": 0,
|
|
"enableBit": 0,
|
|
"readWrite": "rw",
|
|
"unit": "",
|
|
"desc": "",
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0008686d7b9a6841"
|
|
},
|
|
{
|
|
"name": "byte_order",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "onChange",
|
|
"dataType": "INT",
|
|
"addr": "44915",
|
|
"enableRequestCount": 0,
|
|
"enableBit": 0,
|
|
"readWrite": "rw",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"deadZoneType": 0,
|
|
"deadZonePercent": "0",
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "0009686d7b9adc1e"
|
|
},
|
|
{
|
|
"name": "device_name",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "onChange",
|
|
"dataType": "STRING",
|
|
"addr": "47238",
|
|
"enableRequestCount": 0,
|
|
"len": 14,
|
|
"codeType": "UTF-8",
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "000a686d7b9abc03"
|
|
},
|
|
{
|
|
"name": "serial_number",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "onChange",
|
|
"dataType": "STRING",
|
|
"addr": "47003",
|
|
"enableRequestCount": 0,
|
|
"len": 12,
|
|
"codeType": "UTF-8",
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 0,
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "000b686d7b9aa8f9"
|
|
},
|
|
{
|
|
"name": "totalizer_1_rollover",
|
|
"ctrlName": "flowmeter",
|
|
"group": "default",
|
|
"uploadType": "periodic",
|
|
"dataType": "FLOAT",
|
|
"addr": "42612",
|
|
"enableRequestCount": 0,
|
|
"decimal": 2,
|
|
"readWrite": "ro",
|
|
"unit": "",
|
|
"desc": "",
|
|
"storageLwTSDB": 1,
|
|
"transformType": 0,
|
|
"msecSample": 0,
|
|
"_id": "000c686d7b9a55be"
|
|
}
|
|
],
|
|
"alarmLables": [
|
|
"default"
|
|
],
|
|
"alarms": [],
|
|
"groups": [
|
|
{
|
|
"name": "default",
|
|
"uploadInterval": 600,
|
|
"_id": "group59b64649c93",
|
|
"LwTSDBSize": 1000,
|
|
"strategy": 1,
|
|
"historyDataMode": "gateway",
|
|
"historyDataPath": "/var/user/data/dbhome/device_supervisor/LwTSDB",
|
|
"enablePerOnchange": 0
|
|
}
|
|
],
|
|
"misc": {
|
|
"maxAlarmRecordSz": 2000,
|
|
"logLvl": "DEBUG",
|
|
"coms": [
|
|
{
|
|
"name": "rs232",
|
|
"baud": 9600,
|
|
"bits": 8,
|
|
"parityChk": "n",
|
|
"stopbits": 1
|
|
},
|
|
{
|
|
"name": "rs485",
|
|
"baud": 19200,
|
|
"bits": 8,
|
|
"parityChk": "n",
|
|
"stopbits": 1
|
|
}
|
|
],
|
|
"cacheMode": "gateway",
|
|
"cachePath": "/var/user/data/dbhome/device_supervisor/offlinedata",
|
|
"cacheSize": 10000,
|
|
"cacheUploadPeriod": 200,
|
|
"cacheUploadStrategy": 0,
|
|
"cacheStrategy": 0,
|
|
"pubTimeout": 1000,
|
|
"pubRepeatNum": 3,
|
|
"debugLogMode": "gateway",
|
|
"debugLogPath": "/var/user/data/dbhome/device_supervisor/debugLog",
|
|
"debugLogSize": 2000,
|
|
"logNum": 2,
|
|
"logSize": 1
|
|
},
|
|
"clouds": [
|
|
{
|
|
"cacheSize": 10000,
|
|
"enable": 1,
|
|
"name": "default",
|
|
"type": "Standard MQTT",
|
|
"args": {
|
|
"host": "hp.henrypump.cloud",
|
|
"port": 1883,
|
|
"clientId": "cp-flow-meter-x",
|
|
"auth": 1,
|
|
"tls": 0,
|
|
"cleanSession": 0,
|
|
"mqttVersion": "v3.1.1",
|
|
"keepalive": 60,
|
|
"key": "",
|
|
"cert": "",
|
|
"rootCA": "",
|
|
"verifyServer": 0,
|
|
"verifyClient": 0,
|
|
"username": "conocophillipsmqtt",
|
|
"passwd": "conocophillipsmqtt@1903",
|
|
"willQos": 0,
|
|
"willRetain": 0,
|
|
"willTopic": "",
|
|
"willPayload": "",
|
|
"tlsAuth": "caSelfSigned"
|
|
},
|
|
"_id": "cloud59b6464bd03",
|
|
"uploadRules": []
|
|
}
|
|
],
|
|
"quickfaas": {
|
|
"genericFuncs": [],
|
|
"uploadFuncs": [
|
|
{
|
|
"qos": 1,
|
|
"funcName": "sendData",
|
|
"script": "import json, os, time, shutil\nfrom datetime import datetime as dt\nfrom common.Logger import logger\nfrom quickfaas.remotebus import publish\nfrom mobiuspi_lib.gps import GPS\nfrom quickfaas.global_dict import get as get_params\nfrom quickfaas.global_dict import _set_global_args\n\ndef reboot(reason=\"Rebooting for config file update\"):\n #basic = Basic()\n logger.info(\"!\" * 10 + \"REBOOTING DEVICE\" + \"!\"*10)\n logger.info(reason)\n r = os.popen(\"kill -s SIGHUP `cat /var/run/python/supervisord.pid`\").read()\n logger.info(f\"REBOOT : {r}\")\n\ndef checkFileExist(filename):\n path = \"/var/user/files\"\n try:\n if not os.path.exists(path):\n logger.debug(\"no folder making files folder in var/user\")\n os.makedirs(path)\n with open(path + \"/\" + filename, \"a\") as f:\n json.dump({}, f)\n except Exception as e:\n logger.error(f\"Something went wrong in checkFileExist while making folder: {e}\")\n \n try:\n if not os.path.exists(path + \"/\" + filename):\n logger.debug(\"no creds file making creds file\")\n with open(path + \"/\" + filename, \"a\") as f:\n json.dump({}, f)\n except Exception as e:\n logger.error(f\"Something went wrong in checkFileExist wihle making file: {e}\")\n\ndef convertDStoJSON(ds):\n j = dict()\n try:\n for x in ds:\n j[x[\"key\"]] = x[\"value\"]\n except Exception as e:\n logger.error(f\"Something went wrong in convertDStoJSON: {e}\")\n return j\n\ndef convertJSONtoDS(j):\n d = []\n try:\n for key in j.keys():\n d.append({\"key\": key, \"value\": j[key]})\n except Exception as e:\n logger.error(f\"Something went wrong in convertJSONtoDS: {e}\")\n return d\n\ndef checkCredentialConfig():\n logger.debug(\"CHECKING CONFIG\")\n cfgpath = \"/var/user/cfg/device_supervisor/device_supervisor.cfg\"\n credspath = \"/var/user/files/creds.json\"\n cfg = dict()\n with open(cfgpath, \"r\") as f:\n try:\n cfg = json.load(f)\n clouds = cfg.get(\"clouds\")\n logger.debug(clouds)\n #if not configured then try to configure from stored values\n if clouds[0][\"args\"][\"clientId\"] == \"unknown\" or clouds[0][\"args\"][\"username\"] == \"unknown\" or not clouds[0][\"args\"][\"passwd\"] or clouds[0][\"args\"][\"passwd\"] == \"unknown\":\n try:\n checkFileExist(\"creds.json\")\n except Exception as e:\n logger.error(f\"Error in checkFileExist: {e}\")\n with open(credspath, \"r\") as c:\n try:\n creds = json.load(c)\n if creds:\n logger.debug(\"updating config with stored data\")\n clouds[0][\"args\"][\"clientId\"] = creds[\"clientId\"]\n clouds[0][\"args\"][\"username\"] = creds[\"userName\"]\n clouds[0][\"args\"][\"passwd\"] = creds[\"password\"]\n cfg[\"clouds\"] = clouds\n cfg = checkParameterConfig(cfg)\n with open(cfgpath, \"w\", encoding='utf-8') as n:\n json.dump(cfg, n, indent=1, ensure_ascii=False)\n reboot()\n except Exception as e:\n logger.error(f\"Error trying to load credentials from file: {e}\")\n else:\n #assuming clouds is filled out, if data is different then assume someone typed in something new and store it, if creds is empty fill with clouds' data\n checkFileExist(\"creds.json\")\n with open(credspath, \"r\") as c:\n logger.debug(\"updating stored file with new data\")\n cfg = checkParameterConfig(cfg)\n with open(cfgpath, \"w\", encoding='utf-8') as n:\n json.dump(cfg, n, indent=1, ensure_ascii=False)\n creds = json.load(c)\n if creds:\n if creds[\"clientId\"] != clouds[0][\"args\"][\"clientId\"]:\n creds[\"clientId\"] = clouds[0][\"args\"][\"clientId\"]\n if creds[\"userName\"] != clouds[0][\"args\"][\"username\"]:\n creds[\"userName\"] = clouds[0][\"args\"][\"username\"]\n if creds[\"password\"] != clouds[0][\"args\"][\"passwd\"]:\n creds[\"password\"] = clouds[0][\"args\"][\"passwd\"]\n else:\n creds[\"clientId\"] = clouds[0][\"args\"][\"clientId\"]\n creds[\"userName\"] = clouds[0][\"args\"][\"username\"]\n creds[\"password\"] = clouds[0][\"args\"][\"passwd\"]\n with open(credspath, \"w\") as cw:\n json.dump(creds,cw)\n except Exception as e:\n logger.error(f\"Somethign went wrong in checkCredentialConfig: {e}\")\n\ndef checkParameterConfig(cfg):\n try:\n logger.debug(\"Checking Parameters!!!!\")\n paramspath = \"/var/user/files/params.json\"\n cfgparams = convertDStoJSON(cfg.get(\"labels\"))\n #check stored values \n checkFileExist(\"params.json\")\n with open(paramspath, \"r\") as f:\n logger.debug(\"Opened param storage file\")\n params = json.load(f)\n if params:\n if cfgparams != params:\n #go through each param\n #if not \"unknown\" and cfg and params aren't the same take from cfg likely updated manually\n #if key in cfg but not in params copy to params\n logger.debug(\"equalizing params between cfg and stored\")\n for key in cfgparams.keys():\n try:\n if cfgparams[key] != params[key] and cfgparams[key] != \"unknown\":\n params[key] = cfgparams[key]\n except:\n params[key] = cfgparams[key]\n cfg[\"labels\"] = convertJSONtoDS(params)\n _set_global_args(convertJSONtoDS(params))\n with open(paramspath, \"w\") as p:\n json.dump(params, p)\n else:\n with open(paramspath, \"w\") as p:\n logger.debug(\"initializing param file with params in memory\")\n json.dump(convertDStoJSON(get_params()), p)\n cfg[\"labels\"] = get_params()\n \n return cfg\n except Exception as e:\n logger.error(f\"Something went wrong in checkParameterConfig: {e}\")\n os.system(f'rm {paramspath}')\n return cfg\n\npayload = {}\n\ndef initialize_totalizers():\n return {\n \"day\": 0,\n \"week\": 0,\n \"month\": 0,\n \"year\": 0,\n \"lifetime\": 0,\n \"dayHolding\": 0,\n \"weekHolding\": 0,\n \"monthHolding\": 0,\n \"yearHolding\": 0,\n \"rolloverCounter\": 0\n }\n\ndef getTotalizers(file_path=\"/var/user/files/totalizers.json\"):\n \"\"\"\n Retrieves totalizer data from a JSON file.\n\n :param file_path: Path to the JSON file storing totalizer data.\n :return: Dictionary containing totalizer values.\n \"\"\"\n try:\n with open(file_path, \"r\") as t:\n totalizers = json.load(t)\n if not totalizers or not isinstance(totalizers, dict):\n logger.info(\"Invalid data format in the file. Initializing totalizers.\")\n totalizers = initialize_totalizers()\n except FileNotFoundError:\n logger.info(\"File not found. Initializing totalizers.\")\n totalizers = initialize_totalizers()\n except json.JSONDecodeError:\n timestamp = dt.now().strftime(\"%Y%m%d_%H%M%S\")\n # Split the file path and insert the timestamp before the extension\n file_name, file_extension = os.path.splitext(file_path)\n backup_file_path = f\"{file_name}_{timestamp}{file_extension}\"\n shutil.copyfile(file_path, backup_file_path)\n logger.error(f\"Error decoding JSON. A backup of the file is created at {backup_file_path}. Initializing totalizers.\")\n totalizers = initialize_totalizers()\n return totalizers\n\n# Helper function to split the payload into chunks\ndef chunk_payload(payload, chunk_size=20):\n chunked_values = list(payload[\"values\"].items())\n for i in range(0, len(chunked_values), chunk_size):\n yield {\n \"ts\": payload[\"ts\"],\n \"values\": dict(chunked_values[i:i+chunk_size])\n }\n\n\ndef sendData(message,wizard_api):\n logger.debug(message)\n checkCredentialConfig()\n payload = {\"ts\": (round(dt.timestamp(dt.now())/600)*600)*1000, \"values\": {}}\n resetPayload = {\"ts\": \"\", \"values\": {}}\n dayReset, weekReset, monthReset, yearReset = False, False, False, False\n totalizer_1 = None\n ro_counter = None\n for measure in message[\"values\"][\"flowmeter\"].keys():\n try:\n if message[\"values\"][\"flowmeter\"][measure][\"status\"] == 1:\n if measure in [\"totalizer_1\"]:\n totalizer_1 = message[\"values\"][\"flowmeter\"][measure][\"raw_data\"]\n if measure in [\"totalizer_1_rollover\"]:\n ro_counter = message[\"values\"][\"flowmeter\"][measure][\"raw_data\"]\n payload[\"values\"][measure] = message[\"values\"][\"flowmeter\"][measure][\"raw_data\"]\n except Exception as e:\n logger.error(e)\n try:\n if totalizer_1 != None and ro_counter != None:\n real_lifetime = totalizer_1 + ro_counter * 10000000\n payload[\"values\"][\"year_volume\"], yearReset = totalizeYear(real_lifetime)\n payload[\"values\"][\"month_volume\"], monthReset = totalizeMonth(real_lifetime)\n payload[\"values\"][\"week_volume\"], weekReset = totalizeWeek(real_lifetime)\n payload[\"values\"][\"day_volume\"], dayReset = totalizeDay(real_lifetime)\n payload[\"values\"][\"real_lifetime\"] = real_lifetime\n except Exception as e:\n logger.error(e)\n\n try:\n payload[\"values\"][\"latitude\"], payload[\"values\"][\"longitude\"], payload[\"values\"][\"speed\"] = getGPS()\n except:\n logger.error(\"Could not get GPS coordinates\")\n for chunk in chunk_payload(payload=payload):\n publish(__topic__, json.dumps(chunk), __qos__)\n time.sleep(2)\n\n publish(\"v1/devices/me/attributes\", json.dumps({\"latestReportTime\": (round(dt.timestamp(dt.now())/600)*600)*1000}), __qos__)\n \n if dayReset:\n resetPayload[\"values\"][\"yesterday_volume\"] = payload[\"values\"][\"day_volume\"]\n resetPayload[\"values\"][\"day_volume\"] = 0\n if weekReset:\n resetPayload[\"values\"][\"last_week_volume\"] = payload[\"values\"][\"week_volume\"]\n resetPayload[\"values\"][\"week_volume\"] = 0\n if monthReset:\n resetPayload[\"values\"][\"last_month_volume\"] = payload[\"values\"][\"month_volume\"]\n resetPayload[\"values\"][\"month_volume\"] = 0\n if yearReset:\n resetPayload[\"values\"][\"last_year_volume\"] = payload[\"values\"][\"year_volume\"]\n resetPayload[\"values\"][\"year_volume\"] = 0 \n\n if resetPayload[\"values\"]:\n resetPayload[\"ts\"] = 1 + (round(dt.timestamp(dt.now())/600)*600)*1000\n publish(__topic__, json.dumps(resetPayload), __qos__) \n\ndef saveTotalizers(totalizers, file_path=\"/var/user/files/totalizers.json\"):\n \"\"\"\n Saves totalizer data to a JSON file.\n\n :param totalizers: Dictionary containing totalizer values to be saved.\n :param file_path: Path to the JSON file where totalizer data will be saved.\n \"\"\"\n try:\n with open(file_path, \"w\") as t:\n json.dump(totalizers, t)\n except (IOError, OSError, json.JSONEncodeError) as e:\n logger.error(f\"Error saving totalizers to {file_path}: {e}\")\n raise # Optionally re-raise the exception if it should be handled by the caller\n\ndef getGPS():\n # Create a gps instance\n gps = GPS()\n\n # Retrieve GPS information\n position_status = gps.get_position_status()\n logger.debug(\"position_status: \")\n logger.debug(position_status)\n latitude = position_status[\"latitude\"].split(\" \")\n longitude = position_status[\"longitude\"].split(\" \")\n lat_dec = int(latitude[0][:-1]) + (float(latitude[1][:-1])/60)\n lon_dec = int(longitude[0][:-1]) + (float(longitude[1][:-1])/60)\n if latitude[2] == \"S\":\n lat_dec = lat_dec * -1\n if longitude[2] == \"W\":\n lon_dec = lon_dec * -1\n #lat_dec = round(lat_dec, 7)\n #lon_dec = round(lon_dec, 7)\n logger.info(\"HERE IS THE GPS COORDS\")\n logger.info(f\"LATITUDE: {lat_dec}, LONGITUDE: {lon_dec}\")\n speedKnots = position_status[\"speed\"].split(\" \")\n speedMPH = float(speedKnots[0]) * 1.151\n return (f\"{lat_dec:.8f}\",f\"{lon_dec:.8f}\",f\"{speedMPH:.2f}\")\n\ndef totalizeDay(lifetime, max_retries=3, retry_delay=2):\n \"\"\"\n Update and save daily totalizers based on the lifetime value.\n\n :param lifetime: The current lifetime total.\n :param max_retries: Maximum number of save attempts.\n :param retry_delay: Delay in seconds between retries.\n :return: A tuple containing the calculated value and a boolean indicating if a reset occurred, or (None, False) if save fails.\n \"\"\"\n totalizers = getTotalizers()\n now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)\n reset = False\n value = lifetime - totalizers[\"dayHolding\"]\n\n if not int(now.strftime(\"%d\")) == int(totalizers[\"day\"]):\n totalizers[\"dayHolding\"] = lifetime\n totalizers[\"day\"] = int(now.strftime(\"%d\"))\n\n for attempt in range(max_retries):\n try:\n saveTotalizers(totalizers)\n reset = True\n return (value, reset)\n except Exception as e:\n logger.error(f\"Attempt {attempt + 1} failed to save totalizers: {e}\")\n if attempt < max_retries - 1:\n time.sleep(retry_delay)\n else:\n logger.error(\"All attempts to save totalizers failed.\")\n return (None, False)\n\n return (value, reset)\n\ndef totalizeWeek(lifetime, max_retries=3, retry_delay=2):\n \"\"\"\n Update and save weekly totalizers based on the lifetime value.\n\n :param lifetime: The current lifetime total.\n :param max_retries: Maximum number of save attempts.\n :param retry_delay: Delay in seconds between retries.\n :return: A tuple containing the calculated value and a boolean indicating if a reset occurred, or (None, False) if save fails.\n \"\"\"\n totalizers = getTotalizers()\n now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)\n reset = False\n value = lifetime - totalizers[\"weekHolding\"]\n\n if (not now.strftime(\"%U\") == totalizers[\"week\"] and now.strftime(\"%a\") == \"Sun\") or totalizers[\"week\"] == 0:\n totalizers[\"weekHolding\"] = lifetime\n totalizers[\"week\"] = now.strftime(\"%U\")\n\n for attempt in range(max_retries):\n try:\n saveTotalizers(totalizers)\n reset = True\n return (value, reset)\n except Exception as e:\n logger.error(f\"Attempt {attempt + 1} failed to save totalizers: {e}\")\n if attempt < max_retries - 1:\n time.sleep(retry_delay)\n else:\n logger.error(\"All attempts to save totalizers failed.\")\n return (None, False)\n return (value, reset)\n\ndef totalizeMonth(lifetime, max_retries=3, retry_delay=2):\n \"\"\"\n Update and save monthly totalizers based on the lifetime value.\n\n :param lifetime: The current lifetime total.\n :param max_retries: Maximum number of save attempts.\n :param retry_delay: Delay in seconds between retries.\n :return: A tuple containing the calculated value and a boolean indicating if a reset occurred, or (None, False) if save fails.\n \"\"\"\n totalizers = getTotalizers()\n now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)\n reset = False\n value = lifetime - totalizers[\"monthHolding\"]\n\n if not int(now.strftime(\"%m\")) == int(totalizers[\"month\"]):\n totalizers[\"monthHolding\"] = lifetime\n totalizers[\"month\"] = now.strftime(\"%m\")\n\n for attempt in range(max_retries):\n try:\n saveTotalizers(totalizers)\n reset = True\n return (value, reset)\n except Exception as e:\n logger.error(f\"Attempt {attempt + 1} failed to save totalizers: {e}\")\n if attempt < max_retries - 1:\n time.sleep(retry_delay)\n else:\n logger.error(\"All attempts to save totalizers failed.\")\n return (None, False)\n\n return (value,reset)\n\ndef totalizeYear(lifetime, max_retries=3, retry_delay=2):\n \"\"\"\n Update and save yearly totalizers based on the lifetime value.\n\n :param lifetime: The current lifetime total.\n :param max_retries: Maximum number of save attempts.\n :param retry_delay: Delay in seconds between retries.\n :return: A tuple containing the calculated value and a boolean indicating if a reset occurred, or (None, False) if save fails.\n \"\"\"\n totalizers = getTotalizers()\n now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)\n reset = False\n value = lifetime - totalizers[\"yearHolding\"]\n\n if not int(now.strftime(\"%Y\")) == int(totalizers[\"year\"]):\n totalizers[\"yearHolding\"] = lifetime\n totalizers[\"year\"] = now.strftime(\"%Y\")\n \n for attempt in range(max_retries):\n try:\n saveTotalizers(totalizers)\n reset = True\n return (value, reset)\n except Exception as e:\n logger.error(f\"Attempt {attempt + 1} failed to save totalizers: {e}\")\n if attempt < max_retries - 1:\n time.sleep(retry_delay)\n else:\n logger.error(\"All attempts to save totalizers failed.\")\n return (None, False)",
|
|
"name": "sendData",
|
|
"trigger": "measure_event",
|
|
"topic": "v1/devices/me/telemetry",
|
|
"cloudName": "default",
|
|
"groups": [
|
|
"default"
|
|
],
|
|
"hideOfflineData": 0,
|
|
"msgType": 0
|
|
}
|
|
],
|
|
"downloadFuncs": [
|
|
{
|
|
"name": "Receive Command",
|
|
"topic": "v1/devices/me/rpc/request/+",
|
|
"qos": 1,
|
|
"funcName": "receiveCommand",
|
|
"payload_type": "JSON",
|
|
"script": "import json\nfrom datetime import datetime as dt\nfrom common.Logger import logger\nfrom quickfaas.measure import write\nfrom quickfaas.remotebus import publish\n\n\ndef get_totalizers():\n try:\n with open(\"/var/user/files/totalizers.json\", \"r\") as t:\n totalizers = json.load(t)\n if not totalizers:\n logger.info(\"-----INITIALIZING TOTALIZERS-----\")\n totalizers = {\n \"day\": 0,\n \"week\": 0,\n \"month\": 0,\n \"year\": 0,\n \"lifetime\": 0,\n \"dayHolding\": 0,\n \"weekHolding\": 0,\n \"monthHolding\": 0,\n \"yearHolding\": 0\n }\n except:\n totalizers = {\n \"day\": 0,\n \"week\": 0,\n \"month\": 0,\n \"year\": 0,\n \"lifetime\": 0,\n \"dayHolding\": 0,\n \"weekHolding\": 0,\n \"monthHolding\": 0,\n \"yearHolding\": 0\n }\n return totalizers\n\ndef saveTotalizers(totalizers):\n try:\n with open(\"/var/user/files/totalizers.json\", \"w\") as t:\n json.dump(totalizers,t)\n except Exception as e:\n logger.error(e)\n\ndef resetTotalizers():\n try:\n totalizers = get_totalizers()\n \n totalizers[\"dayHolding\"] = 0\n totalizers[\"weekHolding\"] = 0\n totalizers[\"monthHolding\"] = 0\n totalizers[\"yearHolding\"] = 0\n saveTotalizers(totalizers) \n return True\n except Exception as e:\n logger.error(e)\n return e\n\ndef receiveCommand(topic, payload):\n logger.debug(topic)\n logger.debug(json.loads(payload))\n p = json.loads(payload)\n command = p[\"method\"]\n if command == \"resetTotalizers\":\n r = resetTotalizers()\n \n if r == True:\n message = [{\"name\": \"flowmeter\", \"measures\":[{\"name\":\"reset_totalizers\", \"value\": 1}]}]\n try:\n resp = write(message)\n payload = {\n \"ts\": (round(dt.timestamp(dt.now())/600)*600)*1000,\n \"values\": {\n \"day_volume\": 0,\n \"yesterday_volume\": 0,\n \"week_volume\": 0,\n \"last_week_volume\": 0,\n \"month_volume\": 0,\n \"last_month_volume\": 0,\n \"year_volume\": 0,\n \"last_year_volume\": 0\n }\n }\n publish(\"v1/devices/me/telemetry\",json.dumps(payload))\n except Exception as e:\n logger.error(\"Failed to write message\")\n logger.error(e)\n \n ack(topic.split(\"/\")[-1], r)\n\ndef ack(msgid, r):\n #logger.debug(msgid)\n #logger.debug(mac)\n #logger.debug(name)\n #logger.debug(value)\n publish(\"v1/devices/me/rpc/response/\" + str(msgid), json.dumps({\"msg\": {\"time\": (round(dt.timestamp(dt.now())/600)*600)*1000, \"response\": r}, \"metadata\": \"\", \"msgType\": \"\"}))",
|
|
"msgType": 0,
|
|
"cloudName": "default",
|
|
"trigger": "command_event"
|
|
}
|
|
]
|
|
},
|
|
"mindspheres": [
|
|
{
|
|
"name": "mindsphere",
|
|
"enable": 0,
|
|
"_id": "mindsphereeafcdf",
|
|
"args": {
|
|
"hostEnvironment": "eu1",
|
|
"hostDomain": "mindsphere.io",
|
|
"appName": "",
|
|
"appVersion": "",
|
|
"clientId": "",
|
|
"clientSecret": "",
|
|
"authType": "tenant",
|
|
"hostTenant": "",
|
|
"userTenant": "",
|
|
"timeout": 10,
|
|
"statusTimeout": 300,
|
|
"enableOfflinePut": 0
|
|
},
|
|
"mindsphereputs": []
|
|
}
|
|
],
|
|
"labels": [
|
|
{
|
|
"key": "SN",
|
|
"value": "GF5022443062765",
|
|
"_id": "0000685c537f9409"
|
|
},
|
|
{
|
|
"key": "MAC",
|
|
"value": "ec:6e:79:0a:07:b7",
|
|
"_id": "0001685c537f17fd"
|
|
}
|
|
],
|
|
"serverList": [],
|
|
"modbusSlave": {
|
|
"enable": 0,
|
|
"protocol": "Modbus-TCP",
|
|
"port": 502,
|
|
"slaveAddr": 1,
|
|
"int16Ord": "ab",
|
|
"int32Ord": "abcd",
|
|
"float32Ord": "abcd",
|
|
"maxConnection": 5,
|
|
"mapping_table": [],
|
|
"useRawvalue": 1,
|
|
"mappingTable": [
|
|
{
|
|
"name": "1",
|
|
"slaveAddr": 1,
|
|
"_id": "modbusTCPSlave01",
|
|
"measures": []
|
|
}
|
|
]
|
|
},
|
|
"modbusRTUSlave": {
|
|
"enable": 0,
|
|
"protocol": "Modbus-RTU",
|
|
"coms": "rs485",
|
|
"slaveAddr": 1,
|
|
"int16Ord": "ab",
|
|
"int32Ord": "abcd",
|
|
"float32Ord": "abcd",
|
|
"mapping_table": [],
|
|
"useRawvalue": 1,
|
|
"mappingTable": [
|
|
{
|
|
"name": "1",
|
|
"slaveAddr": 1,
|
|
"_id": "modbusRTUSlave01",
|
|
"measures": []
|
|
}
|
|
]
|
|
},
|
|
"iec104Server": {
|
|
"enable": 0,
|
|
"cotSize": 2,
|
|
"port": 2404,
|
|
"serverList": [
|
|
{
|
|
"asduAddr": 1
|
|
}
|
|
],
|
|
"kValue": 12,
|
|
"wValue": 8,
|
|
"t0": 30,
|
|
"t1": 15,
|
|
"t2": 10,
|
|
"t3": 20,
|
|
"maximumLink": 5,
|
|
"timeSet": 1,
|
|
"byteOrder": "abcd",
|
|
"useRawvalue": 1,
|
|
"connectMode": "tcpServer",
|
|
"enableSpontaneous": 1,
|
|
"uploadPeriod": 0,
|
|
"asduLen": 2,
|
|
"mapping_table": []
|
|
},
|
|
"iec101Server": {
|
|
"enable": 0,
|
|
"coms": "rs485",
|
|
"mode": "UnBalance",
|
|
"protocolMode": 0,
|
|
"linkLen": 2,
|
|
"linkAddr": 1,
|
|
"asduLen": 2,
|
|
"ioaLen": 3,
|
|
"cotLen": 2,
|
|
"serverList": [
|
|
{
|
|
"asduAddr": 1
|
|
}
|
|
],
|
|
"linkTimeOut": 2000,
|
|
"timeSet": 1,
|
|
"idleTimeOut": 10000,
|
|
"byteOrder": "abcd",
|
|
"useRawvalue": 1,
|
|
"enableSpontaneous": 1,
|
|
"uploadPeriod": 0,
|
|
"mappingTable": {
|
|
"YX": [],
|
|
"YC": [],
|
|
"YK": []
|
|
}
|
|
},
|
|
"iec104Client": {
|
|
"enable": 0,
|
|
"connectType": 2,
|
|
"serverAddr": "ipower.inhandcloud.cn",
|
|
"serverPort": 2404,
|
|
"communicationCode": "",
|
|
"protocol": 1,
|
|
"asduAddr": 1,
|
|
"tls": 0,
|
|
"mapping_table": {
|
|
"YX": [],
|
|
"YC": [],
|
|
"YK": []
|
|
}
|
|
},
|
|
"opcuaServer": {
|
|
"enable": 0,
|
|
"port": 4840,
|
|
"maximumLink": 5,
|
|
"securityMode": 0,
|
|
"identifierType": "String",
|
|
"certificate": "",
|
|
"privateKey": "",
|
|
"pubsub": 0,
|
|
"useRawvalue": 1,
|
|
"mapping_table": []
|
|
},
|
|
"iec61850Server": {
|
|
"enable": 0,
|
|
"protocol": "iec61850Server",
|
|
"port": 102,
|
|
"iedName": "INHAND",
|
|
"LDName": "Gateway",
|
|
"ctrlMode": 1,
|
|
"authentication": 0,
|
|
"password": "123456",
|
|
"useRawvalue": 1,
|
|
"dataSet": [],
|
|
"mapping_table": []
|
|
},
|
|
"sl651Slave": {
|
|
"enable": 0,
|
|
"centerAaddr": 1,
|
|
"remoteAddr": "",
|
|
"addrCode": "",
|
|
"password": "",
|
|
"platform_list": [],
|
|
"useRawvalue": 1,
|
|
"addressIdentifier": "00F1",
|
|
"timeLeader": "00F0",
|
|
"reverseCRC": 0,
|
|
"mapping_table": []
|
|
},
|
|
"hj212Client": {
|
|
"enable": 0,
|
|
"useRawvalue": 1,
|
|
"platform_list": [],
|
|
"block_list": [],
|
|
"mapping_table": []
|
|
},
|
|
"bacnetServer": {
|
|
"enable": 0,
|
|
"protocol": "BACnet/IP",
|
|
"deviceId": 0,
|
|
"port": 47808,
|
|
"bbmdEnable": 0,
|
|
"useRawvalue": 1,
|
|
"mapping_table": []
|
|
},
|
|
"bacnetMSTPServer": {
|
|
"enable": 0,
|
|
"protocol": "BACnet/MSTP",
|
|
"deviceId": 0,
|
|
"coms": "rs485",
|
|
"maxInfoFrame": 6,
|
|
"mstpMac": 1,
|
|
"maxMaster": 10,
|
|
"useRawvalue": 1,
|
|
"mapping_table": []
|
|
},
|
|
"Dnp3Server": {
|
|
"enable": 0,
|
|
"protocol": "Dnp3-TCP",
|
|
"slaveAddr": 1,
|
|
"masterAddr": 2,
|
|
"port": 20000,
|
|
"useRawvalue": 1,
|
|
"enableUnsol": 0,
|
|
"maxFrasize": 4096,
|
|
"layerTimeout": 1000,
|
|
"linkRetry": 5,
|
|
"enableLink": 0,
|
|
"mapping_table": []
|
|
},
|
|
"snmpAgent": {
|
|
"enable": 0,
|
|
"port": 161,
|
|
"useRawvalue": 1,
|
|
"version": 3,
|
|
"userName": "",
|
|
"enableAuth": 0,
|
|
"readWrite": "ro",
|
|
"enable_trap": 0,
|
|
"mapping_table": []
|
|
},
|
|
"southMetadata": {},
|
|
"bindMetadata": {
|
|
"version": "",
|
|
"timestamp": ""
|
|
},
|
|
"bindConfig": {
|
|
"enable": 0,
|
|
"bind": {
|
|
"modelId": "",
|
|
"modelName": "",
|
|
"srcId": "",
|
|
"srcName": "",
|
|
"devId": "",
|
|
"devName": ""
|
|
},
|
|
"varGroups": [],
|
|
"variables": [],
|
|
"alerts": []
|
|
},
|
|
"templates": {},
|
|
"version": "3.2.1"
|
|
} |