309 lines
11 KiB
Python
309 lines
11 KiB
Python
import json, math, time, os
|
|
from datetime import datetime as dt
|
|
from common.Logger import logger
|
|
from quickfaas.measure import write
|
|
from quickfaas.remotebus import publish
|
|
|
|
class RuntimeStats:
|
|
def __init__(self):
|
|
self.runs = {}
|
|
self.currentRun = 0
|
|
self.today = ""
|
|
self.todayString = ""
|
|
|
|
def manageTime(self):
|
|
if self.todayString != dt.strftime(dt.today(), "%Y-%m-%d"):
|
|
if self.runs[self.todayString]["run_" + str(self.currentRun)]["start"] and not self.runs[self.todayString]["run_" + str(self.currentRun)]["end"]:
|
|
self.runs[self.todayString]["run_" + str(self.currentRun)]["end"] = time.mktime(dt.strptime(self.todayString + " 23:59:59", "%Y-%m-%d %H:%M:%S").timetuple())
|
|
self.addDay()
|
|
self.today = dt.today()
|
|
self.todayString = dt.strftime(self.today, "%Y-%m-%d")
|
|
days = list(self.runs.keys())
|
|
days.sort()
|
|
while (dt.strptime(days[-1],"%Y-%m-%d") - dt.strptime(days[0], "%Y-%m-%d")).days > 40:
|
|
self.removeDay(day=days[0])
|
|
days = list(self.runs.keys())
|
|
days.sort()
|
|
|
|
def addHertzDataPoint(self, frequency):
|
|
if frequency > 0:
|
|
self.manageTime()
|
|
try:
|
|
self.runs[self.todayString]["run_" + str(self.currentRun)]["frequencies"].append(frequency)
|
|
except:
|
|
self.runs[self.todayString]["run_" + str(self.currentRun)]["frequencies"] = [frequency]
|
|
|
|
def startRun(self):
|
|
if self.checkRunning():
|
|
self.endRun()
|
|
self.runs[self.todayString]["run_" + str(self.currentRun)]["start"] = time.time()
|
|
|
|
def endRun(self):
|
|
self.runs[self.todayString]["run_" + str(self.currentRun)]["end"] = time.time()
|
|
self.currentRun += 1
|
|
self.runs[self.todayString]["run_" + str(self.currentRun)] = {"start":0, "end": 0, "frequencies":[]}
|
|
|
|
def checkRunning(self):
|
|
if self.runs[self.todayString]["run_" + str(self.currentRun)]["start"] and not self.runs[self.todayString]["run_" + str(self.currentRun)]["end"]:
|
|
return True
|
|
return False
|
|
|
|
def addDay(self):
|
|
self.today = dt.today()
|
|
self.todayString = dt.strftime(self.today, "%Y-%m-%d")
|
|
self.currentRun = 1
|
|
self.runs[self.todayString] = {}
|
|
self.runs[self.todayString]["run_" + str(self.currentRun)] = {"start":0, "end": 0, "frequencies":[]}
|
|
|
|
def countRunsDay(self, day=None):
|
|
if not day:
|
|
day = self.todayString
|
|
return len(self.runs[day].keys())
|
|
|
|
def countRunsMultiDay(self, numDays=30):
|
|
total_runs = 0
|
|
for day in list(self.runs.keys()):
|
|
total_runs += self.countRunsDay(day=day)
|
|
return total_runs
|
|
|
|
def calculateAverageHertzDay(self, day=None, returnArray=False):
|
|
dayFrequencies = []
|
|
if not day:
|
|
day = self.todayString
|
|
for run in list(self.runs[day].keys()):
|
|
try:
|
|
dayFrequencies += self.runs[day][run]["frequencies"]
|
|
except Exception as e:
|
|
logger.debug("{} missing frequency data for {}".format(day,run))
|
|
if returnArray:
|
|
return dayFrequencies
|
|
return round(math.fsum(dayFrequencies)/len(dayFrequencies),2)
|
|
|
|
def calculateAverageHertzMultiDay(self, numDays=30):
|
|
self.manageTime()
|
|
frequencies = []
|
|
for day in list(self.runs.keys()):
|
|
if not day == self.todayString and (dt.strptime(self.todayString, "%Y-%m-%d") - dt.strptime(day, "%Y-%m-%d")).days <= numDays:
|
|
try:
|
|
frequencies += self.calculateAverageHertzDay(day=day, returnArray=True)
|
|
except Exception as e:
|
|
logger.debug("{} missing frequency data".format(day))
|
|
if len(frequencies):
|
|
return round(math.fsum(frequencies)/len(frequencies), 2)
|
|
return 0
|
|
|
|
def calculateRunTimeDay(self, day=None, convertToHours=True):
|
|
self.manageTime()
|
|
total_time = 0
|
|
if not day:
|
|
day = self.todayString
|
|
for run in list(self.runs.get(day,{}).keys()):
|
|
if self.runs[day][run]["end"] == 0 and self.runs[day][run]["start"] != 0:
|
|
total_time = time.time() - self.runs[day][run]["start"] + total_time
|
|
else:
|
|
total_time = self.runs[day][run]["end"] - self.runs[day][run]["start"] + total_time
|
|
if convertToHours:
|
|
return self.convertSecondstoHours(total_time)
|
|
return total_time
|
|
|
|
def calculateRunTimeMultiDay(self, numDays=30, convertToHours=True):
|
|
total_time = 0
|
|
for day in list(self.runs.keys()):
|
|
if (dt.strptime(self.todayString, "%Y-%m-%d") - dt.strptime(day, "%Y-%m-%d")).days <= numDays:
|
|
total_time += self.calculateRunTimeDay(day=day, convertToHours=False)
|
|
if convertToHours:
|
|
return self.convertSecondstoHours(total_time)
|
|
return total_time
|
|
|
|
def calculateRunPercentDay(self, day=None, precise=False):
|
|
if not day:
|
|
day = self.todayString
|
|
if precise:
|
|
return (self.calculateRunTimeDay(day=day)/24) * 100
|
|
return round((self.calculateRunTimeDay(day=day)/24) * 100, 2)
|
|
|
|
|
|
def calculateRunPercentMultiDay(self, numDays=30, precise=False):
|
|
self.manageTime()
|
|
if precise:
|
|
return (self.calculateRunTimeMultiDay()/(24*numDays)) * 100
|
|
return round((self.calculateRunTimeMultiDay()/(24*numDays)) * 100,2)
|
|
|
|
def removeDay(self, day=None):
|
|
if not day:
|
|
raise Exception("Day can not be None")
|
|
logger.debug("removing day {}".format(day))
|
|
try:
|
|
del self.runs[day]
|
|
except Exception as e:
|
|
logger.error(f"Wasn't able to delete day {day}")
|
|
logger.error(e)
|
|
|
|
|
|
def convertSecondstoHours(self, seconds):
|
|
return round(seconds / (60*60),2)
|
|
|
|
def resetData(self):
|
|
logger.debug("clearing database")
|
|
try:
|
|
os.remove("/var/user/files/runtimestats.json")
|
|
except Exception as e:
|
|
logger.error(e)
|
|
return False
|
|
self.loadDataFromFile()
|
|
return True
|
|
|
|
def loadDataFromFile(self, filePath="/var/user/files/runtimestats.json"):
|
|
try:
|
|
with open(filePath, "r") as f:
|
|
temp = json.load(f)
|
|
self.runs = temp["data"]
|
|
self.currentRun = temp["current_run"]
|
|
self.today = dt.strptime(temp["current_day"], "%Y-%m-%d")
|
|
self.todayString = temp["current_day"]
|
|
self.manageTime()
|
|
except:
|
|
logger.debug("Could not find file at {}".format(filePath))
|
|
logger.debug("creating file")
|
|
self.addDay()
|
|
try:
|
|
with open(filePath, "w") as f:
|
|
d = {
|
|
"current_run": self.currentRun,
|
|
"current_day": self.todayString,
|
|
"data": self.runs
|
|
}
|
|
json.dump(d, f, indent=4)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
|
|
def saveDataToFile(self, filePath="/var/user/files/runtimestats.json"):
|
|
try:
|
|
logger.debug("Saving Runs")
|
|
with open(filePath, "w") as f:
|
|
d = {
|
|
"current_run": self.currentRun,
|
|
"current_day": self.todayString,
|
|
"data": self.runs
|
|
}
|
|
json.dump(d, f, indent=4)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
|
|
rts = RuntimeStats()
|
|
|
|
|
|
def reboot(reason="Rebooting for config file update"):
|
|
#basic = Basic()
|
|
logger.info("!" * 10 + "REBOOTING DEVICE" + "!"*10)
|
|
logger.info(reason)
|
|
r = os.popen("kill -s SIGHUP `cat /var/run/python/supervisord.pid`").read()
|
|
logger.info(f"REBOOT : {r}")
|
|
|
|
def get_totalizers():
|
|
try:
|
|
with open("/var/user/files/totalizers.json", "r") as t:
|
|
totalizers = json.load(t)
|
|
if not totalizers:
|
|
logger.info("-----INITIALIZING TOTALIZERS-----")
|
|
totalizers = {
|
|
"day": 0,
|
|
"week": 0,
|
|
"month": 0,
|
|
"year": 0,
|
|
"lifetime": 0,
|
|
"dayHolding": 0,
|
|
"weekHolding": 0,
|
|
"monthHolding": 0,
|
|
"yearHolding": 0
|
|
}
|
|
except:
|
|
totalizers = {
|
|
"day": 0,
|
|
"week": 0,
|
|
"month": 0,
|
|
"year": 0,
|
|
"lifetime": 0,
|
|
"dayHolding": 0,
|
|
"weekHolding": 0,
|
|
"monthHolding": 0,
|
|
"yearHolding": 0
|
|
}
|
|
return totalizers
|
|
|
|
def saveTotalizers(totalizers):
|
|
try:
|
|
with open("/var/user/files/totalizers.json", "w") as t:
|
|
json.dump(totalizers,t)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
|
|
def resetTotalizers():
|
|
try:
|
|
totalizers = get_totalizers()
|
|
|
|
totalizers["dayHolding"] = 0
|
|
totalizers["weekHolding"] = 0
|
|
totalizers["monthHolding"] = 0
|
|
totalizers["yearHolding"] = 0
|
|
saveTotalizers(totalizers)
|
|
return True
|
|
except Exception as e:
|
|
logger.error(e)
|
|
return e
|
|
|
|
def receiveCommand(topic, payload):
|
|
|
|
logger.debug(topic)
|
|
logger.debug(json.loads(payload))
|
|
p = json.loads(payload)
|
|
command = p["method"]
|
|
if command == "resetTotalizers":
|
|
r = resetTotalizers()
|
|
if r == True:
|
|
message = [{"name": "flowmeter", "measures":[{"name":"reset_totalizers", "value": 1}]}]
|
|
try:
|
|
resp = write(message)
|
|
payload = {
|
|
"ts": (round(dt.timestamp(dt.now())/600)*600)*1000,
|
|
"values": {
|
|
"day_volume": 0,
|
|
"yesterday_volume": 0,
|
|
"week_volume": 0,
|
|
"last_week_volume": 0,
|
|
"month_volume": 0,
|
|
"last_month_volume": 0,
|
|
"year_volume": 0,
|
|
"last_year_volume": 0
|
|
}
|
|
}
|
|
publish("v1/devices/me/telemetry",json.dumps(payload))
|
|
except Exception as e:
|
|
logger.error("Failed to write message")
|
|
logger.error(e)
|
|
elif command == "resetRunTimeStats":
|
|
rts.loadDataFromFile()
|
|
r = rts.resetData()
|
|
if r == True:
|
|
payload = {
|
|
"ts": (round(dt.timestamp(dt.now())/600)*600)*1000,
|
|
"values": {
|
|
"today_running_hours": 0,
|
|
"yesterday_running_hours": 0,
|
|
"month_running_hours": 0,
|
|
"last_month_running_hours": 0
|
|
}
|
|
}
|
|
publish("v1/devices/me/telemetry",json.dumps(payload))
|
|
ack(topic.split("/")[-1], r)
|
|
reboot("need to reset all softwares")
|
|
|
|
ack(topic.split("/")[-1], r)
|
|
|
|
|
|
def ack(msgid, r):
|
|
#logger.debug(msgid)
|
|
#logger.debug(mac)
|
|
#logger.debug(name)
|
|
#logger.debug(value)
|
|
publish("v1/devices/me/rpc/response/" + str(msgid), json.dumps({"msg": {"time": (round(dt.timestamp(dt.now())/600)*600)*1000, "response": r}, "metadata": "", "msgType": ""})) |