openrvdas.logger.writers.logger_manager_writer

Writer that sends records it receives to the LoggerManager as commands. Can be used in conjunction with GeofenceTransform for automatically switching modes when entering/exiting an EEZ, or with a QCFilterTransform to turn off loggers when conditions are violated.

Multiple commands may be sent in a single record by separating them with semicolons.

In addition to the normally-accepted LoggerManager commands, an additional one: "sleep N" is recognized, which will pause the writer N seconds before writing the subsequent command. This allows time, if needed, for the effects of prior commands to settle.

Sample logger that switches modes when entering/exiting EEZ:

# Read the latest lat/lon from the Cached Data Server
readers:
  class: CachedDataReader
  kwargs:
    data_server: localhost:8766
    subscription:
      fields:
        s330Latitude:
          seconds: 0
        s330Longitude:
          seconds: 0
# Look for lat/lon values in the DASRecords and emit appropriate commands
# when entering/leaving EEZ. Note that EEZ files in GML format can be
# downloaded from https://marineregions.org/eezsearch.php.
transforms:
  - class: GeofenceTransform
    module: logger.transforms.geofence_transform
    kwargs:
      latitude_field_name: s330Latitude,
      longitude_field_name: s330Longitude
      boundary_file_name: /tmp/eez.gml
      leaving_boundary_message: set_active_mode write+influx
      entering_boundary_message: set_active_mode no_write+influx
# Send the messages that we get from geofence to the LoggerManager
writers:
  - class: LoggerManagerWriter
    module: logger.writers.logger_manager_writer
    kwargs:
      database: django
      allowed_prefixes:
        - 'set_active_mode '
        - 'sleep '
  1#!/usr/bin/env python3
  2"""
  3Writer that sends records it receives to the LoggerManager as commands. Can
  4be used in conjunction with GeofenceTransform for automatically switching
  5modes when entering/exiting an EEZ, or with a QCFilterTransform to turn off
  6loggers when conditions are violated.
  7
  8Multiple commands may be sent in a single record by separating them with
  9semicolons.
 10
 11In addition to the normally-accepted LoggerManager commands, an additional
 12one: "sleep N" is recognized, which will pause the writer N seconds before
 13writing the subsequent command. This allows time, if needed, for the effects
 14of prior commands to settle.
 15
 16Sample logger that switches modes when entering/exiting EEZ:
 17```
 18# Read the latest lat/lon from the Cached Data Server
 19readers:
 20  class: CachedDataReader
 21  kwargs:
 22    data_server: localhost:8766
 23    subscription:
 24      fields:
 25        s330Latitude:
 26          seconds: 0
 27        s330Longitude:
 28          seconds: 0
 29# Look for lat/lon values in the DASRecords and emit appropriate commands
 30# when entering/leaving EEZ. Note that EEZ files in GML format can be
 31# downloaded from https://marineregions.org/eezsearch.php.
 32transforms:
 33  - class: GeofenceTransform
 34    module: logger.transforms.geofence_transform
 35    kwargs:
 36      latitude_field_name: s330Latitude,
 37      longitude_field_name: s330Longitude
 38      boundary_file_name: /tmp/eez.gml
 39      leaving_boundary_message: set_active_mode write+influx
 40      entering_boundary_message: set_active_mode no_write+influx
 41# Send the messages that we get from geofence to the LoggerManager
 42writers:
 43  - class: LoggerManagerWriter
 44    module: logger.writers.logger_manager_writer
 45    kwargs:
 46      database: django
 47      allowed_prefixes:
 48        - 'set_active_mode '
 49        - 'sleep '
 50```
 51"""
 52import logging
 53import time
 54
 55
 56from logger.writers.writer import Writer  # noqa: E402
 57from server.server_api_command_line import ServerAPICommandLine  # noqa: E402
 58
 59
 60class LoggerManagerWriter(Writer):
 61    """Write received text records to the LoggerManager."""
 62
 63    def __init__(self, database=None, api=None, allowed_prefixes=[], **kwargs):
 64        """Write received text records as commands to the LoggerManager.
 65        ```
 66        database
 67                String indicating which database the LoggerManager is using: django,
 68                sqlite, memory. Either this or 'api', but not both, must be specified.
 69
 70        api
 71                An instance of server_api.ServerAPI to use to communicate with the
 72                LoggerManager. Either this or 'database', but not both, must be specified.
 73
 74        allowed_prefixes
 75                Optional list of strings. If specified, only records whose prefixes match
 76                something in this list will be passed on as commands.
 77
 78        See server/server_api_command_line.py for recognized commands.
 79
 80        Multiple commands may be sent in a single record by separating them with
 81        semicolons.
 82
 83        In addition to the normally-accepted LoggerManager commands, an additional
 84        one: "sleep N" is recognized, which will pause the writer N seconds before
 85        writing the subsequent command. This allows time, if needed, for the effects
 86        of prior commands to settle.
 87        ```
 88        """
 89        super().__init__(**kwargs)  # processes 'quiet' and type hints
 90
 91        if database and api:
 92            raise ValueError('Must specify either "database" or "api" but not both.')
 93
 94        # If database specified, create appropriate api instance
 95        if database:
 96            if database == 'django':
 97                from django_gui.django_server_api import DjangoServerAPI
 98                api = DjangoServerAPI()
 99            elif database == 'memory':
100                from server.in_memory_server_api import InMemoryServerAPI
101                api = InMemoryServerAPI()
102            elif database == 'sqlite':
103                from server.sqlite_server_api import SQLiteServerAPI
104                api = SQLiteServerAPI()
105            else:
106                raise ValueError('Parameter "database" must be one of [django, memory, sqlite], '
107                                 f'found "{database}"')
108
109        # If not database, we'd better have an api specified
110        elif not api:
111            raise ValueError('Must specify one of "database" or "api".')
112
113        self.command_parser = ServerAPICommandLine(api=api)
114
115        if not type(allowed_prefixes) is list:
116            raise ValueError(f'Parameter "allowed_prefixes" must be a list of strings, '
117                             f'found "{allowed_prefixes}"')
118        self.allowed_prefixes = allowed_prefixes
119
120    ############################
121    def write(self, record: str):
122        """ Write out record, appending a newline at end."""
123
124        # See if it's something we can process, and if not, try digesting
125        if not self.can_process_record(record):  # inherited from BaseModule()
126            self.digest_record(record)  # inherited from BaseModule()
127            return
128
129        # If there are semicolons, split into list and process sequentially
130        if record.find(';') > -1:
131            for single_record in record.split(';'):
132                self.write(single_record)
133            return
134
135        # Can we find our command in any of the allowed prefixes?
136        approved = True in [record.find(prefix) == 0 for prefix in self.allowed_prefixes]
137        if self.allowed_prefixes and not approved:
138            if not self.quiet:
139                logging.error(f'Command does not match any allowed prefixes: "{record}"')
140
141        # If it's our special "sleep" command
142        elif record.find('sleep') == 0:
143            try:
144                cmd, interval_str = record.split(' ')
145                interval = float(interval_str)
146            except ValueError:
147                if not self.quiet:
148                    logging.error(f'Could not parse command into "sleep [seconds]": {record}')
149                return
150            if not self.quiet:
151                logging.info(f'Sleeping {interval} seconds')
152            time.sleep(interval)
153
154        else:
155            if not self.quiet:
156                logging.info(f'Writing command: {record}')
157            self.command_parser.process_command(record)
class LoggerManagerWriter(logger.writers.writer.Writer):
 61class LoggerManagerWriter(Writer):
 62    """Write received text records to the LoggerManager."""
 63
 64    def __init__(self, database=None, api=None, allowed_prefixes=[], **kwargs):
 65        """Write received text records as commands to the LoggerManager.
 66        ```
 67        database
 68                String indicating which database the LoggerManager is using: django,
 69                sqlite, memory. Either this or 'api', but not both, must be specified.
 70
 71        api
 72                An instance of server_api.ServerAPI to use to communicate with the
 73                LoggerManager. Either this or 'database', but not both, must be specified.
 74
 75        allowed_prefixes
 76                Optional list of strings. If specified, only records whose prefixes match
 77                something in this list will be passed on as commands.
 78
 79        See server/server_api_command_line.py for recognized commands.
 80
 81        Multiple commands may be sent in a single record by separating them with
 82        semicolons.
 83
 84        In addition to the normally-accepted LoggerManager commands, an additional
 85        one: "sleep N" is recognized, which will pause the writer N seconds before
 86        writing the subsequent command. This allows time, if needed, for the effects
 87        of prior commands to settle.
 88        ```
 89        """
 90        super().__init__(**kwargs)  # processes 'quiet' and type hints
 91
 92        if database and api:
 93            raise ValueError('Must specify either "database" or "api" but not both.')
 94
 95        # If database specified, create appropriate api instance
 96        if database:
 97            if database == 'django':
 98                from django_gui.django_server_api import DjangoServerAPI
 99                api = DjangoServerAPI()
100            elif database == 'memory':
101                from server.in_memory_server_api import InMemoryServerAPI
102                api = InMemoryServerAPI()
103            elif database == 'sqlite':
104                from server.sqlite_server_api import SQLiteServerAPI
105                api = SQLiteServerAPI()
106            else:
107                raise ValueError('Parameter "database" must be one of [django, memory, sqlite], '
108                                 f'found "{database}"')
109
110        # If not database, we'd better have an api specified
111        elif not api:
112            raise ValueError('Must specify one of "database" or "api".')
113
114        self.command_parser = ServerAPICommandLine(api=api)
115
116        if not type(allowed_prefixes) is list:
117            raise ValueError(f'Parameter "allowed_prefixes" must be a list of strings, '
118                             f'found "{allowed_prefixes}"')
119        self.allowed_prefixes = allowed_prefixes
120
121    ############################
122    def write(self, record: str):
123        """ Write out record, appending a newline at end."""
124
125        # See if it's something we can process, and if not, try digesting
126        if not self.can_process_record(record):  # inherited from BaseModule()
127            self.digest_record(record)  # inherited from BaseModule()
128            return
129
130        # If there are semicolons, split into list and process sequentially
131        if record.find(';') > -1:
132            for single_record in record.split(';'):
133                self.write(single_record)
134            return
135
136        # Can we find our command in any of the allowed prefixes?
137        approved = True in [record.find(prefix) == 0 for prefix in self.allowed_prefixes]
138        if self.allowed_prefixes and not approved:
139            if not self.quiet:
140                logging.error(f'Command does not match any allowed prefixes: "{record}"')
141
142        # If it's our special "sleep" command
143        elif record.find('sleep') == 0:
144            try:
145                cmd, interval_str = record.split(' ')
146                interval = float(interval_str)
147            except ValueError:
148                if not self.quiet:
149                    logging.error(f'Could not parse command into "sleep [seconds]": {record}')
150                return
151            if not self.quiet:
152                logging.info(f'Sleeping {interval} seconds')
153            time.sleep(interval)
154
155        else:
156            if not self.quiet:
157                logging.info(f'Writing command: {record}')
158            self.command_parser.process_command(record)

Write received text records to the LoggerManager.

LoggerManagerWriter(database=None, api=None, allowed_prefixes=[], **kwargs)
 64    def __init__(self, database=None, api=None, allowed_prefixes=[], **kwargs):
 65        """Write received text records as commands to the LoggerManager.
 66        ```
 67        database
 68                String indicating which database the LoggerManager is using: django,
 69                sqlite, memory. Either this or 'api', but not both, must be specified.
 70
 71        api
 72                An instance of server_api.ServerAPI to use to communicate with the
 73                LoggerManager. Either this or 'database', but not both, must be specified.
 74
 75        allowed_prefixes
 76                Optional list of strings. If specified, only records whose prefixes match
 77                something in this list will be passed on as commands.
 78
 79        See server/server_api_command_line.py for recognized commands.
 80
 81        Multiple commands may be sent in a single record by separating them with
 82        semicolons.
 83
 84        In addition to the normally-accepted LoggerManager commands, an additional
 85        one: "sleep N" is recognized, which will pause the writer N seconds before
 86        writing the subsequent command. This allows time, if needed, for the effects
 87        of prior commands to settle.
 88        ```
 89        """
 90        super().__init__(**kwargs)  # processes 'quiet' and type hints
 91
 92        if database and api:
 93            raise ValueError('Must specify either "database" or "api" but not both.')
 94
 95        # If database specified, create appropriate api instance
 96        if database:
 97            if database == 'django':
 98                from django_gui.django_server_api import DjangoServerAPI
 99                api = DjangoServerAPI()
100            elif database == 'memory':
101                from server.in_memory_server_api import InMemoryServerAPI
102                api = InMemoryServerAPI()
103            elif database == 'sqlite':
104                from server.sqlite_server_api import SQLiteServerAPI
105                api = SQLiteServerAPI()
106            else:
107                raise ValueError('Parameter "database" must be one of [django, memory, sqlite], '
108                                 f'found "{database}"')
109
110        # If not database, we'd better have an api specified
111        elif not api:
112            raise ValueError('Must specify one of "database" or "api".')
113
114        self.command_parser = ServerAPICommandLine(api=api)
115
116        if not type(allowed_prefixes) is list:
117            raise ValueError(f'Parameter "allowed_prefixes" must be a list of strings, '
118                             f'found "{allowed_prefixes}"')
119        self.allowed_prefixes = allowed_prefixes

Write received text records as commands to the LoggerManager.

database
        String indicating which database the LoggerManager is using: django,
        sqlite, memory. Either this or 'api', but not both, must be specified.

api
        An instance of server_api.ServerAPI to use to communicate with the
        LoggerManager. Either this or 'database', but not both, must be specified.

allowed_prefixes
        Optional list of strings. If specified, only records whose prefixes match
        something in this list will be passed on as commands.

See server/server_api_command_line.py for recognized commands.

Multiple commands may be sent in a single record by separating them with
semicolons.

In addition to the normally-accepted LoggerManager commands, an additional
one: "sleep N" is recognized, which will pause the writer N seconds before
writing the subsequent command. This allows time, if needed, for the effects
of prior commands to settle.
command_parser
allowed_prefixes
def write(self, record: str):
122    def write(self, record: str):
123        """ Write out record, appending a newline at end."""
124
125        # See if it's something we can process, and if not, try digesting
126        if not self.can_process_record(record):  # inherited from BaseModule()
127            self.digest_record(record)  # inherited from BaseModule()
128            return
129
130        # If there are semicolons, split into list and process sequentially
131        if record.find(';') > -1:
132            for single_record in record.split(';'):
133                self.write(single_record)
134            return
135
136        # Can we find our command in any of the allowed prefixes?
137        approved = True in [record.find(prefix) == 0 for prefix in self.allowed_prefixes]
138        if self.allowed_prefixes and not approved:
139            if not self.quiet:
140                logging.error(f'Command does not match any allowed prefixes: "{record}"')
141
142        # If it's our special "sleep" command
143        elif record.find('sleep') == 0:
144            try:
145                cmd, interval_str = record.split(' ')
146                interval = float(interval_str)
147            except ValueError:
148                if not self.quiet:
149                    logging.error(f'Could not parse command into "sleep [seconds]": {record}')
150                return
151            if not self.quiet:
152                logging.info(f'Sleeping {interval} seconds')
153            time.sleep(interval)
154
155        else:
156            if not self.quiet:
157                logging.info(f'Writing command: {record}')
158            self.command_parser.process_command(record)

Write out record, appending a newline at end.