mirror of
https://github.com/weewx/weewx.git
synced 2026-04-18 16:46:56 -04:00
252 lines
9.4 KiB
Python
Executable File
252 lines
9.4 KiB
Python
Executable File
#
|
|
# Copyright (c) 2009-2023 Tom Keffer <tkeffer@gmail.com>
|
|
#
|
|
# See the file LICENSE.txt for your rights.
|
|
#
|
|
"""Entry point to the weewx weather system."""
|
|
import importlib
|
|
import locale
|
|
import logging
|
|
import os
|
|
import os.path
|
|
import platform
|
|
import signal
|
|
import sys
|
|
import time
|
|
from optparse import OptionParser
|
|
|
|
import configobj
|
|
|
|
import weecfg
|
|
import weedb
|
|
import weeutil.logger
|
|
import weewx.engine
|
|
from weeutil.weeutil import to_bool
|
|
from weewx import daemon
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
usagestr = """Usage: %prog --help
|
|
%prog --version
|
|
%prog [CONFIG_FILE|--config=CONFIG_FILE]
|
|
[--daemon] [--pidfile=PIDFILE]
|
|
[--exit] [--loop-on-init]
|
|
[--log-label=LABEL]
|
|
|
|
Entry point to the weewx weather program. Can be run directly, or as a daemon
|
|
by specifying the '--daemon' option.
|
|
|
|
Arguments:
|
|
CONFIG_FILE: The weewx configuration file to be used. Optional.
|
|
"""
|
|
|
|
|
|
# ===============================================================================
|
|
# Main entry point
|
|
# ===============================================================================
|
|
|
|
def main():
|
|
parser = OptionParser(usage=usagestr)
|
|
parser.add_option("--config", dest="config_path", type=str,
|
|
metavar="CONFIG_FILE",
|
|
help="Use configuration file CONFIG_FILE.")
|
|
parser.add_option("-d", "--daemon", action="store_true", dest="daemon", help="Run as a daemon")
|
|
parser.add_option("-p", "--pidfile", type="string", dest="pidfile",
|
|
help="Store the process ID in PIDFILE",
|
|
default="/var/run/weewx.pid", metavar="PIDFILE")
|
|
parser.add_option("-v", "--version", action="store_true", dest="version",
|
|
help="Display version number then exit")
|
|
parser.add_option("-x", "--exit", action="store_true", dest="exit",
|
|
help="Exit on I/O and database errors instead of restarting")
|
|
parser.add_option("-r", "--loop-on-init", action="store_true", dest="loop_on_init",
|
|
help="Retry forever if device is not ready on startup")
|
|
parser.add_option("-n", "--log-label", type="string", dest="log_label",
|
|
help="Label to use in syslog entries",
|
|
default="weewx", metavar="LABEL")
|
|
|
|
# Get the command line options and arguments:
|
|
options, args = parser.parse_args()
|
|
|
|
if options.version:
|
|
print(weewx.__version__)
|
|
sys.exit(0)
|
|
|
|
if args and options.config_path:
|
|
print("Specify CONFIG_PATH as an argument, or by using --config, but not both",
|
|
file=sys.stderr)
|
|
sys.exit(weewx.CMD_ERROR)
|
|
|
|
# Read the configuration file
|
|
try:
|
|
# Pass in a copy of the command line arguments. read_config() will change it.
|
|
config_path, config_dict = weecfg.read_config(options.config_path, list(args))
|
|
except (IOError, configobj.ConfigObjError) as e:
|
|
print("Error parsing config file: %s" % e, file=sys.stderr)
|
|
weeutil.logger.log_traceback(log.critical, " **** ")
|
|
sys.exit(weewx.CMD_ERROR)
|
|
|
|
# Now that we have the configuration dictionary, we can add the path to the user
|
|
# directory to PYTHONPATH.
|
|
weewx.add_user_path(config_dict)
|
|
# Now we can import user extensions
|
|
importlib.import_module('user.extensions')
|
|
|
|
weewx.debug = int(config_dict.get('debug', 0))
|
|
|
|
# Now that we have the config_dict and debug setting, we can customize the
|
|
# logging with user additions
|
|
weeutil.logger.setup(options.log_label, config_dict)
|
|
|
|
# Log key bits of information.
|
|
log.info("Initializing weewx version %s", weewx.__version__)
|
|
log.info("Using Python %s", sys.version)
|
|
log.info("Located at %s", sys.executable)
|
|
log.info("Platform %s", platform.platform())
|
|
log.info("Locale is '%s'", locale.setlocale(locale.LC_ALL))
|
|
log.info("Entry path: %s", __file__)
|
|
log.info("Using configuration file %s", config_path)
|
|
log.info("Debug is %s", weewx.debug)
|
|
|
|
# If no command line --loop-on-init was specified, look in the config file.
|
|
if options.loop_on_init is None:
|
|
loop_on_init = to_bool(config_dict.get('loop_on_init', False))
|
|
else:
|
|
loop_on_init = options.loop_on_init
|
|
|
|
# Save the current working directory. A service might
|
|
# change it. In case of a restart, we need to change it back.
|
|
cwd = os.getcwd()
|
|
|
|
# Make sure the system time is not out of date (a common problem with the Raspberry Pi).
|
|
# Do this by making sure the system time is later than the creation time of this file.
|
|
sane = os.stat(__file__).st_ctime
|
|
n = 0
|
|
while weewx.launchtime_ts < sane:
|
|
# Log any problems every minute.
|
|
if n % 120 == 0:
|
|
log.info("Waiting for sane time. Current time is %s",
|
|
weeutil.weeutil.timestamp_to_string(weewx.launchtime_ts))
|
|
n += 1
|
|
time.sleep(0.5)
|
|
weewx.launchtime_ts = time.time()
|
|
|
|
# Set up a handler for a termination signal
|
|
signal.signal(signal.SIGTERM, sigTERMhandler)
|
|
|
|
if options.daemon:
|
|
log.info("PID file is %s", options.pidfile)
|
|
daemon.daemonize(pidfile=options.pidfile)
|
|
|
|
# Main restart loop
|
|
while True:
|
|
|
|
os.chdir(cwd)
|
|
|
|
try:
|
|
log.debug("Initializing engine")
|
|
|
|
# Create and initialize the engine
|
|
engine = weewx.engine.StdEngine(config_dict)
|
|
|
|
log.info("Starting up weewx version %s", weewx.__version__)
|
|
|
|
# Start the engine. It should run forever unless an exception
|
|
# occurs. Log it if the function returns.
|
|
engine.run()
|
|
log.critical("Unexpected exit from main loop. Program exiting.")
|
|
|
|
# Catch any console initialization error:
|
|
except weewx.engine.InitializationError as e:
|
|
# Log it:
|
|
log.critical("Unable to load driver: %s", e)
|
|
# See if we should loop, waiting for the console to be ready.
|
|
# Otherwise, just exit.
|
|
if loop_on_init:
|
|
log.critical(" **** Waiting 60 seconds then retrying...")
|
|
time.sleep(60)
|
|
log.info("retrying...")
|
|
else:
|
|
log.critical(" **** Exiting...")
|
|
sys.exit(weewx.IO_ERROR)
|
|
|
|
# Catch any recoverable weewx I/O errors:
|
|
except weewx.WeeWxIOError as e:
|
|
# Caught an I/O error. Log it, wait 60 seconds, then try again
|
|
log.critical("Caught WeeWxIOError: %s", e)
|
|
if options.exit:
|
|
log.critical(" **** Exiting...")
|
|
sys.exit(weewx.IO_ERROR)
|
|
log.critical(" **** Waiting 60 seconds then retrying...")
|
|
time.sleep(60)
|
|
log.info("retrying...")
|
|
|
|
# Catch any database connection errors:
|
|
except (weedb.CannotConnectError, weedb.DisconnectError) as e:
|
|
# No connection to the database server. Log it, wait 60 seconds, then try again
|
|
log.critical("Database connection exception: %s", e)
|
|
if options.exit:
|
|
log.critical(" **** Exiting...")
|
|
sys.exit(weewx.DB_ERROR)
|
|
log.critical(" **** Waiting 60 seconds then retrying...")
|
|
time.sleep(60)
|
|
log.info("retrying...")
|
|
|
|
except weedb.OperationalError as e:
|
|
# Caught a database error. Log it, wait 120 seconds, then try again
|
|
log.critical("Database OperationalError exception: %s", e)
|
|
if options.exit:
|
|
log.critical(" **** Exiting...")
|
|
sys.exit(weewx.DB_ERROR)
|
|
log.critical(" **** Waiting 2 minutes then retrying...")
|
|
time.sleep(120)
|
|
log.info("retrying...")
|
|
|
|
except OSError as e:
|
|
# Caught an OS error. Log it, wait 10 seconds, then try again
|
|
log.critical("Caught OSError: %s", e)
|
|
weeutil.logger.log_traceback(log.critical, " **** ")
|
|
log.critical(" **** Waiting 10 seconds then retrying...")
|
|
time.sleep(10)
|
|
log.info("retrying...")
|
|
|
|
except Terminate:
|
|
log.info("Terminating weewx version %s", weewx.__version__)
|
|
weeutil.logger.log_traceback(log.debug, " **** ")
|
|
signal.signal(signal.SIGTERM, signal.SIG_DFL)
|
|
os.kill(0, signal.SIGTERM)
|
|
|
|
# Catch any keyboard interrupts and log them
|
|
except KeyboardInterrupt:
|
|
log.critical("Keyboard interrupt.")
|
|
# Reraise the exception (this should cause the program to exit)
|
|
raise
|
|
|
|
# Catch any non-recoverable errors. Log them, exit
|
|
except Exception as ex:
|
|
# Caught unrecoverable error. Log it, exit
|
|
log.critical("Caught unrecoverable exception:")
|
|
log.critical(" **** %s" % ex)
|
|
# Include a stack traceback in the log:
|
|
weeutil.logger.log_traceback(log.critical, " **** ")
|
|
log.critical(" **** Exiting.")
|
|
# Reraise the exception (this should cause the program to exit)
|
|
raise
|
|
|
|
|
|
# ==============================================================================
|
|
# Signal handlers
|
|
# ==============================================================================
|
|
|
|
class Terminate(Exception):
|
|
"""Exception raised when terminating the engine."""
|
|
|
|
|
|
def sigTERMhandler(signum, _frame):
|
|
log.info("Received signal TERM (%s).", signum)
|
|
raise Terminate
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Start up the program
|
|
main()
|