from__future__importannotationsimportargparseimportconfigparserimportosimporttimefromdatetimeimportdatetimefromdatetimeimporttimezonefromtypingimportNamedTuplefromvpf_730.vpf_730importVPF730classLoggerError(Exception):"""Base class for errors raised by the logger"""passclassLoggerConfigError(LoggerError):"""Exception that is raised when the configuration is invalid"""pass
[docs]classLoggerConfig(NamedTuple):"""A class representing the configuration of logger. :param local_db: path to the local sqlite database, where the measurements are stored :param serial_port: serial port that the VPF-730 sensor is connected to :param log_interval: the log interval in minutes (between 0 and 30) """local_db:strserial_port:strlog_interval:int
[docs]@classmethoddeffrom_env(cls)->LoggerConfig:"""Constructs a new :func:`LoggerConfig` from environment variables. * ``VPF730_LOCAL_DB`` - path to the sqlite database which is used as queue * ``VPF730_PORT`` - serial port that the VPF-730 sensor is connected to * ``VPF730_LOG_INTERVAL`` - interval used for logging e.g. 1 for every minute :return: a new instance of :func:`LoggerConfig` created from environment variables. """# noqa: E501returncls(local_db=os.environ['VPF730_LOCAL_DB'],serial_port=os.environ['VPF730_PORT'],log_interval=int(os.environ['VPF730_LOG_INTERVAL']),)
[docs]@classmethoddeffrom_file(cls,path:str)->LoggerConfig:"""Constructs a new :func:`LoggerConfig` from a provided ``.ini`` config file with this format: .. code-block:: ini [vpf_730] local_db=local.db serial_port=/dev/ttyS0 log_interval=1 :param path: path to the ``.ini`` config file with the structure above :return: a new instance of :func:`LoggerConfig` created from a config file """config=configparser.ConfigParser()config.read(path)returncls(config['vpf_730']['local_db'],config['vpf_730']['serial_port'],int(config['vpf_730']['log_interval']),)
[docs]@classmethoddeffrom_argparse(cls,args:argparse.Namespace)->LoggerConfig:"""Constructs a new :func:`LoggerConfig` from an :func:`argparse.Namespace`, created by the argument parser returned by :func:`vpf_730.main.build_parser`. :param args: arguments returned from the argument parser created by :func:`vpf_730.main.build_parser` :return: a new instance of :func:`LoggerConfig` created from CLI arguments """ifargs.log_intervalisNoneornot(0<args.log_interval<=30):raiseLoggerConfigError('the log interval must be set between 1 and 30',)returncls(local_db=args.local_db,serial_port=args.serial_port,log_interval=args.log_interval,)
classLogger:def__init__(self,cfg:LoggerConfig)->None:self.cfg=cfgself.logging=Trueself.vpf_730=VPF730(port=cfg.serial_port)@propertydef_logging(self)->bool:# this is a hack for being able to test thisreturnself.logging# pragma: no coverdefrun(self)->None:prev_minute=-1whileself._loggingisTrue:time.sleep(.1)now=datetime.now(timezone.utc)# don't accidentally log the same timestamp twiceif(now.minute%self.cfg.log_interval==0andnow.second==0andnow.minute!=prev_minute):measurement=self.vpf_730.measure()ifmeasurementisnotNone:# pragma: no branchmeasurement.to_db(db_path=self.cfg.local_db)prev_minute=now.minute