Skip to content

Commit d825d08

Browse files
committed
Replace systemd logging of solar forecast with threading to streamline the programm execution flow and not clutter up the system
1 parent 6bd594b commit d825d08

8 files changed

+52
-116
lines changed

README.md

-14
Original file line numberDiff line numberDiff line change
@@ -193,20 +193,6 @@ You can use the [inverter script](./inverter) to control the inverter manually o
193193
The logs of the application are stored in `<path to repository>/logs/`. They are rolled over once a logfile reaches `1 MB` in size. The current log and a maximum of `7` rolled over logfiles are saved.
194194
See also the environment variables `DIRECTORY_OF_LOGS` and `LOGLEVEL`.
195195

196-
#### Only log the solar forecast
197-
198-
If you pass in `--solar-forecast` as an argument to `main.py` the programm just logs the expected solar forecast of the day.
199-
200-
This can also be used to log the solar prediction after the sun has set to see how far off the solar prediction was and get a sense of how good the predication was (→ not as a *forecast* but as a *review*). To correctly display the log message use `--solar-review` in this case.
201-
202-
#### Monitor solar forecast prediction and power buy
203-
204-
You can monitor how far the prediction of the solar forecast was off and how much power was bought with the script [solar_forecast_logger.sh](solar_forecast_logger.sh).
205-
206-
It saves the following data:
207-
- `<directory> of logs>/power_buy.log`: `<timestamp of start of charging>\t<timestamp of end of charging>\t<power bought in Wh>`
208-
- `<directory> of logs>/solar_forecast_difference.log`: `<date>\t<prediction at start of day in Wh>\t<prediction at end of day in Wh>`
209-
210196
## InfluxDB commands
211197

212198
- Create bucket: `influx bucket create -org default -token ${INFLUXDB_TOKEN} --name default`

solar_forecast_logger.sh

-25
This file was deleted.

source/inverter_charge_controller.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import socket
3+
import sys
34
from datetime import datetime, timedelta
45

56
import pause
@@ -107,7 +108,7 @@ def _start(self) -> None:
107108

108109
finally:
109110
self.log.critical("An unexpected error occurred. Exiting now...", exc_info=True)
110-
exit(1)
111+
sys.exit(1)
111112

112113
def _do_iteration(self, current_energy_rate: EnergyRate) -> EnergyRate:
113114
"""

source/main.py

+50-29
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,59 @@
11
import signal
22
import sys
3-
from datetime import timedelta
3+
import threading
4+
from datetime import datetime, time, timedelta
45
from types import FrameType
56

7+
import pause
68
from inverter_charge_controller import InverterChargeController
79
from logger import LoggerMixin
810
from sun_forecast_handler import SunForecastHandler
911
from time_handler import TimeHandler
1012

13+
logger = LoggerMixin()
1114

12-
def log_solar_forecast(log_as_review: bool = False) -> None:
15+
16+
def log_solar_forecast() -> None:
1317
sun_forecast_handler = SunForecastHandler()
18+
morning_time = time(hour=5, minute=0, second=0, microsecond=0, tzinfo=TimeHandler.get_timezone())
19+
evening_time = time(hour=23, minute=0, second=0, microsecond=0, tzinfo=TimeHandler.get_timezone())
20+
while True:
21+
next_wakeup_time = _get_next_wakeup_time(morning_time, evening_time)
22+
logger.log.info(f"Next wakeup time to log solar forecast data is at {next_wakeup_time}")
23+
pause.until(next_wakeup_time)
24+
25+
start, end = _get_morning_and_evening_timestamp_of_today(morning_time, evening_time)
26+
logger.log.info(f"Waking up to log solar forecast data from {start} to {end}")
27+
if TimeHandler.get_time().hour == morning_time.hour:
28+
start += timedelta(minutes=2)
29+
else:
30+
end -= timedelta(minutes=2)
31+
32+
sun_forecast_handler.get_solar_output_in_timeframe(start, end)
33+
1434

35+
def _get_morning_and_evening_timestamp_of_today(morning_time: time, evening_time: time) -> tuple[datetime, datetime]:
36+
today = TimeHandler.get_date()
37+
return (
38+
datetime.combine(today, morning_time),
39+
datetime.combine(today, evening_time),
40+
)
41+
42+
43+
def _get_next_wakeup_time(morning_time: time, evening_time: time) -> datetime:
1544
now = TimeHandler.get_time()
16-
start = now.replace(hour=5, minute=0, second=0, microsecond=0)
17-
end = now.replace(hour=23, minute=0, second=0, microsecond=0)
18-
if log_as_review:
19-
end -= timedelta(minutes=2)
20-
else:
21-
start += timedelta(minutes=2)
45+
next_morning_wakeup_time, next_evening_wakeup_time = _get_morning_and_evening_timestamp_of_today(
46+
morning_time, evening_time
47+
)
48+
if now >= next_morning_wakeup_time:
49+
next_morning_wakeup_time += timedelta(days=1)
50+
if now >= next_evening_wakeup_time:
51+
next_evening_wakeup_time += timedelta(days=1)
2252

23-
solar_output_today = sun_forecast_handler.get_solar_output_in_timeframe(start, end)
24-
if log_as_review:
25-
sun_forecast_handler.log.info(f"The actual solar output of today was {solar_output_today}")
53+
if next_morning_wakeup_time - now < next_evening_wakeup_time - now:
54+
return next_morning_wakeup_time
2655
else:
27-
sun_forecast_handler.log.info(f"The expected solar output of today is {solar_output_today}")
56+
return next_evening_wakeup_time
2857

2958

3059
def handle_stop_signal(signal_number: int, _frame: FrameType) -> None:
@@ -35,30 +64,22 @@ def handle_stop_signal(signal_number: int, _frame: FrameType) -> None:
3564
signal_number: The number representing the signal received.
3665
_frame: The current stack frame when the signal was received.
3766
"""
38-
logger = LoggerMixin()
3967
logger.log.info(f"Received {signal.Signals(signal_number).name}. Exiting now...")
4068
inverter_charge_controller.unlock()
41-
exit(0)
69+
sys.exit(0)
4270

4371

4472
for signal_to_catch in [signal.SIGINT, signal.SIGTERM]:
4573
signal.signal(signal_to_catch, handle_stop_signal)
4674

4775
if __name__ == "__main__":
48-
if len(sys.argv) > 1:
49-
if sys.argv[1] == "--solar-forecast":
50-
"""
51-
This allows you to simply log today's solar forecast and do nothing else.
52-
This can also be used to log the solar prediction after the sun has set to see how far off the solar prediction
53-
from before the sun has risen was. See below.
54-
"""
55-
log_solar_forecast()
56-
exit(0)
57-
if sys.argv[1] == "--solar-review":
58-
log_solar_forecast(log_as_review=True)
59-
exit(0)
60-
61-
raise RuntimeError(f"Unknown argument {sys.argv[1]}!")
76+
solar_forecast_logging_thread = threading.Thread(target=log_solar_forecast, daemon=True)
77+
solar_forecast_logging_thread.start()
78+
# Let the solar forecast calculate and log its next wakeup time before logging all the info of the
79+
# InverterChargeController
80+
pause.seconds(1)
6281

6382
inverter_charge_controller = InverterChargeController()
64-
inverter_charge_controller.start()
83+
inverter_charge_controller_thread = threading.Thread(target=inverter_charge_controller.start)
84+
inverter_charge_controller_thread.start()
85+
inverter_charge_controller_thread.join()

systemd/inverter-charge-controller-just-solar-forecast.service

-13
This file was deleted.

systemd/inverter-charge-controller-just-solar-forecast.timer

-10
This file was deleted.

systemd/inverter-charge-controller-just-solar-review.service

-14
This file was deleted.

systemd/inverter-charge-controller-just-solar-review.timer

-10
This file was deleted.

0 commit comments

Comments
 (0)