279 lines
9.5 KiB
Python
279 lines
9.5 KiB
Python
import json, time
|
|
from datetime import datetime as dt
|
|
from quickfaas.measure import recall, write
|
|
from quickfaas.remotebus import publish
|
|
from common.Logger import logger
|
|
|
|
# Helper function to split the payload into chunks
|
|
def chunk_payload(payload, chunk_size=20):
|
|
chunked_values = list(payload["values"].items())
|
|
for i in range(0, len(chunked_values), chunk_size):
|
|
yield {
|
|
"ts": payload["ts"],
|
|
"values": dict(chunked_values[i:i+chunk_size])
|
|
}
|
|
|
|
def sync():
|
|
#get new values and send
|
|
payload = {"ts": round(dt.timestamp(dt.now()))*1000, "values": {}}
|
|
topic = "v1/devices/me/telemetry"
|
|
try:
|
|
data = recall()#json.loads(recall().decode("utf-8"))
|
|
except Exception as e:
|
|
logger.error(e)
|
|
logger.debug(data)
|
|
for controller in data:
|
|
for measure in controller["measures"]:
|
|
#publish measure
|
|
if measure["name"] in ["wellstatus","pidcontrolmode","downholesensorstatus","alarmflowrate","alarmintakepressure","alarmintaketemperature","alarmtubingpressure","alarmvfd","alarmlockout","alarmfluidlevel","runpermissive","startpermissive","last_vfd_fault_code","vfd_fault", "flowmeter_fault"]:
|
|
payload["values"][measure["name"]] = convert_int(measure["name"], measure["value"])
|
|
payload["values"][measure["name"]+ "_int"] = measure["value"]
|
|
else:
|
|
payload["values"][measure["name"]] = measure["value"]
|
|
logger.debug("Sending on topic: {}".format(topic))
|
|
logger.debug("Sending value: {}".format(payload))
|
|
for chunk in chunk_payload(payload=payload):
|
|
publish(topic, json.dumps(chunk), 1)
|
|
time.sleep(2)
|
|
def writeplctag(value):
|
|
#value in the form {"measurement": <measurement_name>, "value": <value to write>}
|
|
try:
|
|
#value = json.loads(value.replace("'",'"'))
|
|
logger.debug(value)
|
|
#payload format: [{"name": "advvfdipp", "measures": [{"name": "manualfrequencysetpoint", "value": 49}]}]
|
|
message = [{"name": "advvfdipp", "measures":[{"name":value["measurement"], "value": value["value"]}]}]
|
|
resp = write(message)
|
|
logger.debug("RETURN FROM WRITE: {}".format(resp))
|
|
return True
|
|
except Exception as e:
|
|
logger.debug(e)
|
|
return False
|
|
|
|
def receiveCommand(topic, payload):
|
|
try:
|
|
logger.debug(topic)
|
|
logger.debug(json.loads(payload))
|
|
p = json.loads(payload)
|
|
command = p["method"]
|
|
commands = {
|
|
"sync": sync,
|
|
"writeplctag": writeplctag,
|
|
}
|
|
if command == "setPLCTag":
|
|
try:
|
|
result = commands["writeplctag"](p["params"])
|
|
logger.debug(result)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
elif command == "changeSetpoint":
|
|
try:
|
|
logger.debug("attempting controlpoint write")
|
|
params_type = {"measurement": "pidcontrolmode", "value": p["params"]["setpointType"]}
|
|
if params_type["value"]:
|
|
commands["writeplctag"](params_type)
|
|
time.sleep(2)
|
|
except Exception as e:
|
|
logger.error("DID NOT WRITE CONTROL MODE")
|
|
logger.error(e)
|
|
try:
|
|
logger.debug("attempting setpoint write")
|
|
modes = {0: "flowsetpoint", 1: "fluidlevelsetpoint", 2: "tubingpressuresetpoint", 3: "manualfrequencysetpoint"}
|
|
params_value = {"value": p["params"]["setpointValue"]}
|
|
if params_value["value"]:
|
|
params_value["measurement"] = modes[getMode()]
|
|
result = commands["writeplctag"](params_value)
|
|
logger.debug(result)
|
|
except Exception as e:
|
|
logger.error("DID NOT WRITE SETPOINT")
|
|
logger.error(e)
|
|
#logger.debug(command)
|
|
ack(topic.split("/")[-1])
|
|
time.sleep(5)
|
|
sync()
|
|
except Exception as e:
|
|
logger.debug(e)
|
|
|
|
|
|
def ack(msgid):
|
|
#logger.debug(msgid)
|
|
#logger.debug(mac)
|
|
#logger.debug(name)
|
|
#logger.debug(value)
|
|
publish("v1/devices/me/rpc/response/" + str(msgid), json.dumps({"msg": {"time": time.time()}, "metadata": "", "msgType": ""}), 1)
|
|
|
|
def getMode():
|
|
try:
|
|
data = recall()
|
|
for controller in data:
|
|
for measure in controller["measures"]:
|
|
if measure["name"] == "pidcontrolmode":
|
|
return measure["value"]
|
|
except:
|
|
return None
|
|
|
|
def convert_int(plc_tag, value):
|
|
well_status_codes = {
|
|
0: "Running",
|
|
1: "Pumped Off",
|
|
2: "Alarmed",
|
|
3: "Locked Out",
|
|
4: "Stopped"
|
|
}
|
|
|
|
pid_control_codes = {
|
|
0: "Flow",
|
|
1: "Fluid Level",
|
|
2: "Tubing Pressure",
|
|
3: "Manual"
|
|
}
|
|
|
|
downhole_codes = {
|
|
0: "OK",
|
|
1: "Connecting",
|
|
2: "Open Circuit",
|
|
3: "Shorted",
|
|
4: "Cannot Decode"
|
|
}
|
|
|
|
permissive_codes = {
|
|
0: "OK",
|
|
1: "Flow",
|
|
2: "Intake Pressure",
|
|
3: "Intake Temperature",
|
|
4: "Tubing Pressure",
|
|
5: "VFD",
|
|
6: "Fluid Level",
|
|
7: "Min. Downtime"
|
|
}
|
|
|
|
alarm_codes = {
|
|
0: "OK",
|
|
1: "Alarm"
|
|
}
|
|
|
|
alarm_vfd_codes = {
|
|
0: "OK",
|
|
1: "Locked Out"
|
|
}
|
|
|
|
vfd_fault_codes = {
|
|
0: "No Fault",
|
|
2: "Auxiliary Input",
|
|
3: "Power Loss",
|
|
4: "UnderVoltage",
|
|
5: "OverVoltage",
|
|
7: "Motor Overload",
|
|
8: "Heatsink OverTemp",
|
|
9: "Thermister OverTemp",
|
|
10: "Dynamic Brake OverTemp",
|
|
12: "Hardware OverCurrent",
|
|
13: "Ground Fault",
|
|
14: "Ground Warning",
|
|
15: "Load Loss",
|
|
17: "Input Phase Loss",
|
|
18: "Motor PTC Trip",
|
|
19: "Task Overrun",
|
|
20: "Torque Prove Speed Band",
|
|
21: "Output Phase Loss",
|
|
24: "Decel Inhibit",
|
|
25: "OverSpeed Limit",
|
|
26: "Brake Slipped",
|
|
27: "Torque Prove Conflict",
|
|
28: "TP Encls Confict",
|
|
29: "Analog In Loss",
|
|
33: "Auto Restarts Exhausted",
|
|
35: "IPM OverCurrent",
|
|
36: "SW OverCurrent",
|
|
38: "Phase U to Ground",
|
|
39: "Phase V to Ground",
|
|
40: "Phase W to Ground",
|
|
41: "Phase UV Short",
|
|
42: "Phase VW Short",
|
|
43: "Phase WU Short",
|
|
44: "Phase UNeg to Ground",
|
|
45: "Phase VNeg to Ground",
|
|
46: "Phase WNeg to Ground",
|
|
48: "System Defaulted",
|
|
49: "Drive Powerup",
|
|
51: "Clear Fault Queue",
|
|
55: "Control Board Overtemp",
|
|
59: "Invalid Code",
|
|
61: "Shear Pin 1",
|
|
62: "Shear Pin 2",
|
|
64: "Drive Overload",
|
|
66: "OW Torque Level",
|
|
67: "Pump Off",
|
|
71: "Port 1 Adapter",
|
|
72: "Port 2 Adapter",
|
|
73: "Port 3 Adapter",
|
|
74: "Port 4 Adapter",
|
|
75: "Port 5 Adapter",
|
|
76: "Port 6 Adapter",
|
|
77: "IR Volts Range",
|
|
78: "FluxAmps Ref Range",
|
|
79: "Excessive Load",
|
|
80: "AutoTune Aborted",
|
|
81: "Port 1 DPI Loss",
|
|
82: "Port 2 DPI Loss",
|
|
83: "Port 3 DPI Loss",
|
|
84: "Port 4 DPI Loss",
|
|
85: "Port 5 DPI Loss",
|
|
86: "Port 6 DPI Loss",
|
|
87: "IXo Voltage Range",
|
|
91: "Primary Velocity Feedback Loss",
|
|
93: "Hardware Enable Check",
|
|
94: "Alternate Velocity Feedback Loss",
|
|
95: "Auxiliary Velocity Feedback Loss",
|
|
96: "Position Feedback Loss",
|
|
97: "Auto Tach Switch",
|
|
100: "Parameter Checksum",
|
|
101: "Power Down NVS Blank",
|
|
102: "NVS Not Blank",
|
|
103: "Power Down NVS Incompatible",
|
|
104: "Power Board Checksum",
|
|
106: "Incompat MCB-PB",
|
|
107: "Replaced MCB-PB",
|
|
108: "Analog Calibration Checksum",
|
|
110: "Invalid Power Board Data",
|
|
111: "Power Board Invalid ID",
|
|
112: "Power Board App Min Version",
|
|
113: "Tracking DataError",
|
|
115: "Power Down Table Full",
|
|
116: "Power Down Entry Too Large",
|
|
117: "Power Down Data Checksum",
|
|
118: "Power Board Power Down Checksum",
|
|
124: "App ID Changed",
|
|
125: "Using Backup App",
|
|
134: "Start on Power Up",
|
|
137: "External Precharge Error",
|
|
138: "Precharge Open",
|
|
141: "Autotune Enc Angle",
|
|
142: "Autotune Speed Restricted",
|
|
143: "Autotune Current Regulator",
|
|
144: "Autotune Inertia",
|
|
145: "Autotune Travel",
|
|
13035: "Net IO Timeout",
|
|
13037: "Net IO Timeout"
|
|
|
|
}
|
|
|
|
plc_tags = {
|
|
"wellstatus": well_status_codes.get(value, "Invalid Code"),
|
|
"pidcontrolmode": pid_control_codes.get(value, "Invalid Code"),
|
|
"downholesensorstatus": downhole_codes.get(value, "Invalid Code"),
|
|
"alarmflowrate": alarm_codes.get(value, "Invalid Code"),
|
|
"alarmintakepressure": alarm_codes.get(value, "Invalid Code"),
|
|
"alarmintaketemperature": alarm_codes.get(value, "Invalid Code"),
|
|
"alarmtubingpressure": alarm_codes.get(value, "Invalid Code"),
|
|
"alarmvfd": alarm_codes.get(value, "Invalid Code"),
|
|
"alarmlockout": alarm_vfd_codes.get(value, "Invalid Code"),
|
|
"alarmfluidlevel": alarm_codes.get(value, "Invalid Code"),
|
|
"runpermissive": permissive_codes.get(value, "Invalid Code"),
|
|
"startpermissive": permissive_codes.get(value, "Invalid Code"),
|
|
"last_vfd_fault_code": vfd_fault_codes.get(value, "Invalid Code"),
|
|
"vfd_fault": vfd_fault_codes.get(value, "Invalid Code"),
|
|
"flowmeter_fault": alarm_codes.get(value, "Invalid Code")
|
|
}
|
|
|
|
return plc_tags.get(plc_tag, "Invalid Tag")
|