613 lines
26 KiB
Python
613 lines
26 KiB
Python
import json, os, time, shutil, math
|
|
from datetime import datetime as dt
|
|
from datetime import timedelta as td
|
|
from calendar import monthrange
|
|
from common.Logger import logger
|
|
from quickfaas.remotebus import publish
|
|
from mobiuspi_lib.gps import GPS
|
|
from quickfaas.global_dict import get as get_params
|
|
from quickfaas.global_dict import _set_global_args
|
|
|
|
|
|
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))
|
|
del self.runs[day]
|
|
self.saveDataToFile()
|
|
|
|
def convertSecondstoHours(self, seconds):
|
|
return round(seconds / (60*60),2)
|
|
|
|
def resetData(self):
|
|
logger.debug("clearing database")
|
|
try:
|
|
for day in list(self.runs.keys()):
|
|
self.removeDay(day=day)
|
|
except Exception as e:
|
|
logger.error(e)
|
|
return False
|
|
self.runs = {}
|
|
self.currentRun = 0
|
|
self.today = ""
|
|
self.todayString = ""
|
|
self.manageTime()
|
|
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()
|
|
rts.loadDataFromFile()
|
|
rts.saveDataToFile()
|
|
|
|
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 checkFileExist(filename):
|
|
path = "/var/user/files"
|
|
try:
|
|
if not os.path.exists(path):
|
|
logger.debug("no folder making files folder in var/user")
|
|
os.makedirs(path)
|
|
with open(path + "/" + filename, "a") as f:
|
|
json.dump({}, f)
|
|
except Exception as e:
|
|
logger.error(f"Something went wrong in checkFileExist while making folder: {e}")
|
|
|
|
try:
|
|
if not os.path.exists(path + "/" + filename):
|
|
logger.debug("no creds file making creds file")
|
|
with open(path + "/" + filename, "a") as f:
|
|
json.dump({}, f)
|
|
except Exception as e:
|
|
logger.error(f"Something went wrong in checkFileExist wihle making file: {e}")
|
|
|
|
def convertDStoJSON(ds):
|
|
j = dict()
|
|
try:
|
|
for x in ds:
|
|
j[x["key"]] = x["value"]
|
|
except Exception as e:
|
|
logger.error(f"Something went wrong in convertDStoJSON: {e}")
|
|
return j
|
|
|
|
def convertJSONtoDS(j):
|
|
d = []
|
|
try:
|
|
for key in j.keys():
|
|
d.append({"key": key, "value": j[key]})
|
|
except Exception as e:
|
|
logger.error(f"Something went wrong in convertJSONtoDS: {e}")
|
|
return d
|
|
|
|
def checkCredentialConfig():
|
|
logger.debug("CHECKING CONFIG")
|
|
cfgpath = "/var/user/cfg/device_supervisor/device_supervisor.cfg"
|
|
credspath = "/var/user/files/creds.json"
|
|
cfg = dict()
|
|
with open(cfgpath, "r") as f:
|
|
try:
|
|
cfg = json.load(f)
|
|
clouds = cfg.get("clouds")
|
|
logger.debug(clouds)
|
|
#if not configured then try to configure from stored values
|
|
if clouds[0]["args"]["clientId"] == "unknown" or clouds[0]["args"]["username"] == "unknown" or not clouds[0]["args"]["passwd"] or clouds[0]["args"]["passwd"] == "unknown":
|
|
try:
|
|
checkFileExist("creds.json")
|
|
except Exception as e:
|
|
logger.error(f"Error in checkFileExist: {e}")
|
|
with open(credspath, "r") as c:
|
|
try:
|
|
creds = json.load(c)
|
|
if creds:
|
|
logger.debug("updating config with stored data")
|
|
clouds[0]["args"]["clientId"] = creds["clientId"]
|
|
clouds[0]["args"]["username"] = creds["userName"]
|
|
clouds[0]["args"]["passwd"] = creds["password"]
|
|
cfg["clouds"] = clouds
|
|
cfg = checkParameterConfig(cfg)
|
|
with open(cfgpath, "w", encoding='utf-8') as n:
|
|
json.dump(cfg, n, indent=1, ensure_ascii=False)
|
|
reboot()
|
|
except Exception as e:
|
|
logger.error(f"Error trying to load credentials from file: {e}")
|
|
else:
|
|
#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
|
|
checkFileExist("creds.json")
|
|
with open(credspath, "r") as c:
|
|
logger.debug("updating stored file with new data")
|
|
cfg = checkParameterConfig(cfg)
|
|
with open(cfgpath, "w", encoding='utf-8') as n:
|
|
json.dump(cfg, n, indent=1, ensure_ascii=False)
|
|
creds = json.load(c)
|
|
if creds:
|
|
if creds["clientId"] != clouds[0]["args"]["clientId"]:
|
|
creds["clientId"] = clouds[0]["args"]["clientId"]
|
|
if creds["userName"] != clouds[0]["args"]["username"]:
|
|
creds["userName"] = clouds[0]["args"]["username"]
|
|
if creds["password"] != clouds[0]["args"]["passwd"]:
|
|
creds["password"] = clouds[0]["args"]["passwd"]
|
|
else:
|
|
creds["clientId"] = clouds[0]["args"]["clientId"]
|
|
creds["userName"] = clouds[0]["args"]["username"]
|
|
creds["password"] = clouds[0]["args"]["passwd"]
|
|
with open(credspath, "w") as cw:
|
|
json.dump(creds,cw)
|
|
except Exception as e:
|
|
logger.error(f"Somethign went wrong in checkCredentialConfig: {e}")
|
|
|
|
def checkParameterConfig(cfg):
|
|
try:
|
|
logger.debug("Checking Parameters!!!!")
|
|
paramspath = "/var/user/files/params.json"
|
|
cfgparams = convertDStoJSON(cfg.get("labels"))
|
|
#check stored values
|
|
checkFileExist("params.json")
|
|
with open(paramspath, "r") as f:
|
|
logger.debug("Opened param storage file")
|
|
params = json.load(f)
|
|
if params:
|
|
if cfgparams != params:
|
|
#go through each param
|
|
#if not "unknown" and cfg and params aren't the same take from cfg likely updated manually
|
|
#if key in cfg but not in params copy to params
|
|
logger.debug("equalizing params between cfg and stored")
|
|
for key in cfgparams.keys():
|
|
try:
|
|
if cfgparams[key] != params[key] and cfgparams[key] != "unknown":
|
|
params[key] = cfgparams[key]
|
|
except:
|
|
params[key] = cfgparams[key]
|
|
cfg["labels"] = convertJSONtoDS(params)
|
|
_set_global_args(convertJSONtoDS(params))
|
|
with open(paramspath, "w") as p:
|
|
json.dump(params, p)
|
|
else:
|
|
with open(paramspath, "w") as p:
|
|
logger.debug("initializing param file with params in memory")
|
|
json.dump(convertDStoJSON(get_params()), p)
|
|
cfg["labels"] = get_params()
|
|
|
|
return cfg
|
|
except Exception as e:
|
|
logger.error(f"Something went wrong in checkParameterConfig: {e}")
|
|
os.system(f'rm {paramspath}')
|
|
return cfg
|
|
|
|
payload = {}
|
|
|
|
def initialize_totalizers():
|
|
return {
|
|
"day": 0,
|
|
"week": 0,
|
|
"month": 0,
|
|
"year": 0,
|
|
"lifetime": 0,
|
|
"dayHolding": 0,
|
|
"weekHolding": 0,
|
|
"monthHolding": 0,
|
|
"yearHolding": 0
|
|
}
|
|
|
|
def getTotalizers(file_path="/var/user/files/totalizers.json"):
|
|
"""
|
|
Retrieves totalizer data from a JSON file.
|
|
|
|
:param file_path: Path to the JSON file storing totalizer data.
|
|
:return: Dictionary containing totalizer values.
|
|
"""
|
|
try:
|
|
with open(file_path, "r") as t:
|
|
totalizers = json.load(t)
|
|
if not totalizers or not isinstance(totalizers, dict):
|
|
logger.info("Invalid data format in the file. Initializing totalizers.")
|
|
totalizers = initialize_totalizers()
|
|
except FileNotFoundError:
|
|
logger.info("File not found. Initializing totalizers.")
|
|
totalizers = initialize_totalizers()
|
|
except json.JSONDecodeError:
|
|
timestamp = dt.now().strftime("%Y%m%d_%H%M%S")
|
|
# Split the file path and insert the timestamp before the extension
|
|
file_name, file_extension = os.path.splitext(file_path)
|
|
backup_file_path = f"{file_name}_{timestamp}{file_extension}"
|
|
shutil.copyfile(file_path, backup_file_path)
|
|
logger.error(f"Error decoding JSON. A backup of the file is created at {backup_file_path}. Initializing totalizers.")
|
|
totalizers = initialize_totalizers()
|
|
return totalizers
|
|
|
|
# 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 sendData(message,wizard_api):
|
|
rts.loadDataFromFile()
|
|
logger.debug(message)
|
|
checkCredentialConfig()
|
|
payload = {"ts": (round(dt.timestamp(dt.now())/600)*600)*1000, "values": {}}
|
|
resetPayload = {"ts": "", "values": {}}
|
|
dayReset, weekReset, monthReset, yearReset = False, False, False, False
|
|
for controller, measures in message["values"].items():
|
|
for measure, values in measures.items():
|
|
try:
|
|
if values.get("status") == 1:
|
|
if measure in ["totalizer_1"]:
|
|
payload["values"]["day_volume"], dayReset = totalizeDay(values.get("raw_data"))
|
|
payload["values"]["week_volume"], weekReset = totalizeWeek(values.get("raw_data"))
|
|
payload["values"]["month_volume"], monthReset = totalizeMonth(values.get("raw_data"))
|
|
payload["values"]["year_volume"], yearReset = totalizeYear(values.get("raw_data"))
|
|
if measure in ["running"]:
|
|
rts.manageTime()
|
|
if values.get("raw_data") == 1 and not rts.runs[rts.todayString]["run_" + str(rts.currentRun)]["start"]:
|
|
rts.startRun()
|
|
rts.saveDataToFile()
|
|
elif values.get("raw_data") == 0 and rts.runs[rts.todayString]["run_" + str(rts.currentRun)]["start"] and not rts.runs[rts.todayString]["run_" + str(rts.currentRun)]["end"]:
|
|
rts.endRun()
|
|
rts.saveDataToFile()
|
|
payload["values"]["today_running_hours"] = rts.calculateRunTimeDay()
|
|
payload["values"]["month_running_hours"] = rts.calculateRunTimeMultiDay(numDays=dt.today().day)
|
|
payload["values"][measure] = values.get("raw_data")
|
|
except Exception as e:
|
|
logger.error(e)
|
|
try:
|
|
payload["values"]["latitude"], payload["values"]["longitude"], payload["values"]["speed"] = getGPS()
|
|
except:
|
|
logger.error("Could not get GPS coordinates")
|
|
for chunk in chunk_payload(payload=payload):
|
|
publish(__topic__, json.dumps(chunk), __qos__)
|
|
time.sleep(2)
|
|
|
|
publish("v1/devices/me/attributes", json.dumps({"latestReportTime": (round(dt.timestamp(dt.now())/600)*600)*1000}), __qos__)
|
|
|
|
if dayReset:
|
|
resetPayload["values"]["yesterday_volume"] = payload["values"]["day_volume"]
|
|
resetPayload["values"]["day_volume"] = 0
|
|
resetPayload["values"]["yesterday_running_hours"] = rts.calculateRunTimeDay(day=dt.strftime(dt.today() - td(days=1) , "%Y-%m-%d"))
|
|
if weekReset:
|
|
resetPayload["values"]["last_week_volume"] = payload["values"]["week_volume"]
|
|
resetPayload["values"]["week_volume"] = 0
|
|
if monthReset:
|
|
resetPayload["values"]["last_month_volume"] = payload["values"]["month_volume"]
|
|
resetPayload["values"]["month_volume"] = 0
|
|
resetPayload["values"]["last_month_running_hours"] = rts.calculateRunTimeMultiDay(numDays=monthrange(dt.today().year, (dt.today() - td(days=1)).month)[1])
|
|
if yearReset:
|
|
resetPayload["values"]["last_year_volume"] = payload["values"]["year_volume"]
|
|
resetPayload["values"]["year_volume"] = 0
|
|
|
|
if resetPayload["values"]:
|
|
resetPayload["ts"] = 1 + (round(dt.timestamp(dt.now())/600)*600)*1000
|
|
publish(__topic__, json.dumps(resetPayload), __qos__)
|
|
|
|
def saveTotalizers(totalizers, file_path="/var/user/files/totalizers.json"):
|
|
"""
|
|
Saves totalizer data to a JSON file.
|
|
|
|
:param totalizers: Dictionary containing totalizer values to be saved.
|
|
:param file_path: Path to the JSON file where totalizer data will be saved.
|
|
"""
|
|
try:
|
|
with open(file_path, "w") as t:
|
|
json.dump(totalizers, t)
|
|
except (IOError, OSError, json.JSONEncodeError) as e:
|
|
logger.error(f"Error saving totalizers to {file_path}: {e}")
|
|
raise # Optionally re-raise the exception if it should be handled by the caller
|
|
|
|
def getGPS():
|
|
# Create a gps instance
|
|
gps = GPS()
|
|
|
|
# Retrieve GPS information
|
|
position_status = gps.get_position_status()
|
|
logger.debug("position_status: ")
|
|
logger.debug(position_status)
|
|
latitude = position_status["latitude"].split(" ")
|
|
longitude = position_status["longitude"].split(" ")
|
|
lat_dec = int(latitude[0][:-1]) + (float(latitude[1][:-1])/60)
|
|
lon_dec = int(longitude[0][:-1]) + (float(longitude[1][:-1])/60)
|
|
if latitude[2] == "S":
|
|
lat_dec = lat_dec * -1
|
|
if longitude[2] == "W":
|
|
lon_dec = lon_dec * -1
|
|
#lat_dec = round(lat_dec, 7)
|
|
#lon_dec = round(lon_dec, 7)
|
|
logger.info("HERE IS THE GPS COORDS")
|
|
logger.info(f"LATITUDE: {lat_dec}, LONGITUDE: {lon_dec}")
|
|
speedKnots = position_status["speed"].split(" ")
|
|
speedMPH = float(speedKnots[0]) * 1.151
|
|
return (f"{lat_dec:.8f}",f"{lon_dec:.8f}",f"{speedMPH:.2f}")
|
|
|
|
def totalizeDay(lifetime, max_retries=3, retry_delay=2):
|
|
"""
|
|
Update and save daily totalizers based on the lifetime value.
|
|
|
|
:param lifetime: The current lifetime total.
|
|
:param max_retries: Maximum number of save attempts.
|
|
:param retry_delay: Delay in seconds between retries.
|
|
:return: A tuple containing the calculated value and a boolean indicating if a reset occurred, or (None, False) if save fails.
|
|
"""
|
|
totalizers = getTotalizers()
|
|
now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)
|
|
reset = False
|
|
value = lifetime - totalizers["dayHolding"]
|
|
|
|
if not int(now.strftime("%d")) == int(totalizers["day"]):
|
|
totalizers["dayHolding"] = lifetime
|
|
totalizers["day"] = int(now.strftime("%d"))
|
|
|
|
for attempt in range(max_retries):
|
|
try:
|
|
saveTotalizers(totalizers)
|
|
reset = True
|
|
return (value, reset)
|
|
except Exception as e:
|
|
logger.error(f"Attempt {attempt + 1} failed to save totalizers: {e}")
|
|
if attempt < max_retries - 1:
|
|
time.sleep(retry_delay)
|
|
else:
|
|
logger.error("All attempts to save totalizers failed.")
|
|
return (None, False)
|
|
|
|
return (value, reset)
|
|
|
|
def totalizeWeek(lifetime, max_retries=3, retry_delay=2):
|
|
"""
|
|
Update and save weekly totalizers based on the lifetime value.
|
|
|
|
:param lifetime: The current lifetime total.
|
|
:param max_retries: Maximum number of save attempts.
|
|
:param retry_delay: Delay in seconds between retries.
|
|
:return: A tuple containing the calculated value and a boolean indicating if a reset occurred, or (None, False) if save fails.
|
|
"""
|
|
totalizers = getTotalizers()
|
|
now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)
|
|
reset = False
|
|
value = lifetime - totalizers["weekHolding"]
|
|
if (not now.strftime("%U") == totalizers["week"] and now.strftime("%a") == "Sun") or totalizers["week"] == 0:
|
|
totalizers["weekHolding"] = lifetime
|
|
totalizers["week"] = now.strftime("%U")
|
|
|
|
for attempt in range(max_retries):
|
|
try:
|
|
saveTotalizers(totalizers)
|
|
reset = True
|
|
return (value, reset)
|
|
except Exception as e:
|
|
logger.error(f"Attempt {attempt + 1} failed to save totalizers: {e}")
|
|
if attempt < max_retries - 1:
|
|
time.sleep(retry_delay)
|
|
else:
|
|
logger.error("All attempts to save totalizers failed.")
|
|
return (None, False)
|
|
return (value, reset)
|
|
|
|
def totalizeMonth(lifetime, max_retries=3, retry_delay=2):
|
|
"""
|
|
Update and save monthly totalizers based on the lifetime value.
|
|
|
|
:param lifetime: The current lifetime total.
|
|
:param max_retries: Maximum number of save attempts.
|
|
:param retry_delay: Delay in seconds between retries.
|
|
:return: A tuple containing the calculated value and a boolean indicating if a reset occurred, or (None, False) if save fails.
|
|
"""
|
|
totalizers = getTotalizers()
|
|
now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)
|
|
reset = False
|
|
value = lifetime - totalizers["monthHolding"]
|
|
if not int(now.strftime("%m")) == int(totalizers["month"]):
|
|
totalizers["monthHolding"] = lifetime
|
|
totalizers["month"] = now.strftime("%m")
|
|
|
|
for attempt in range(max_retries):
|
|
try:
|
|
saveTotalizers(totalizers)
|
|
reset = True
|
|
return (value, reset)
|
|
except Exception as e:
|
|
logger.error(f"Attempt {attempt + 1} failed to save totalizers: {e}")
|
|
if attempt < max_retries - 1:
|
|
time.sleep(retry_delay)
|
|
else:
|
|
logger.error("All attempts to save totalizers failed.")
|
|
return (None, False)
|
|
|
|
return (value,reset)
|
|
|
|
def totalizeYear(lifetime, max_retries=3, retry_delay=2):
|
|
"""
|
|
Update and save yearly totalizers based on the lifetime value.
|
|
|
|
:param lifetime: The current lifetime total.
|
|
:param max_retries: Maximum number of save attempts.
|
|
:param retry_delay: Delay in seconds between retries.
|
|
:return: A tuple containing the calculated value and a boolean indicating if a reset occurred, or (None, False) if save fails.
|
|
"""
|
|
totalizers = getTotalizers()
|
|
now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)
|
|
reset = False
|
|
value = lifetime - totalizers["yearHolding"]
|
|
if not int(now.strftime("%Y")) == int(totalizers["year"]):
|
|
totalizers["yearHolding"] = lifetime
|
|
totalizers["year"] = now.strftime("%Y")
|
|
|
|
for attempt in range(max_retries):
|
|
try:
|
|
saveTotalizers(totalizers)
|
|
reset = True
|
|
return (value, reset)
|
|
except Exception as e:
|
|
logger.error(f"Attempt {attempt + 1} failed to save totalizers: {e}")
|
|
if attempt < max_retries - 1:
|
|
time.sleep(retry_delay)
|
|
else:
|
|
logger.error("All attempts to save totalizers failed.")
|
|
return (None, False)
|
|
return (value, reset) |