openrvdas.logger.utils.simulate_network

Read stored logger data and serve at realistic intervals over network ports.

Run with, e.g.

    logger/utils/simulate_network.py          --config test/SKQ201822S/network_sim_SKQ201822S.yaml          --loop

and you should be able to listen on the various ports that Sikuliaq instruments post data to:

    logger/listener/listen.py --network :53131 --write_file -

for wind_mast_stbd (see test/sikuliaq/skq_ports.txt for a table of the instrument to port mappings active during cruise SKQ201822.

  1#!/usr/bin/env python3
  2"""Read stored logger data and serve at realistic intervals over network ports.
  3
  4Run with, e.g.
  5```
  6    logger/utils/simulate_network.py \
  7         --config test/SKQ201822S/network_sim_SKQ201822S.yaml \
  8         --loop
  9```
 10and you should be able to listen on the various ports that Sikuliaq
 11instruments post data to:
 12```
 13    logger/listener/listen.py --network :53131 --write_file -
 14```
 15for wind_mast_stbd (see test/sikuliaq/skq_ports.txt for a table of the
 16instrument to port mappings active during cruise SKQ201822.
 17"""
 18import logging
 19import threading
 20
 21from logger.readers.logfile_reader import LogfileReader  # noqa: E402
 22from logger.transforms.slice_transform import SliceTransform  # noqa: E402
 23from logger.transforms.timestamp_transform import TimestampTransform  # noqa: E402
 24from logger.transforms.prefix_transform import PrefixTransform  # noqa: E402
 25from logger.writers.udp_writer import UDPWriter  # noqa: E402
 26
 27from logger.utils.read_config import read_config  # noqa: E402
 28
 29
 30class SimNetwork:
 31    """Open a network port and feed stored logfile data to it."""
 32    ############################
 33
 34    def __init__(self, port, filebase, instrument):
 35        """
 36        ```
 37        port -  UDP port on which to write records.
 38
 39        filebase - Prefix string to be matched (with a following "*") to fine
 40                   files to be used. e.g. /tmp/log/NBP1406/knud/raw/NBP1406_knud
 41
 42        instrument - Instrument name prefix to add before sendind out on wire
 43        ```
 44        """
 45        self.filebase = filebase
 46        self.reader = LogfileReader(filebase=filebase, use_timestamps=True)
 47        self.slice_n = SliceTransform(fields='1:')  # grab 2nd and subsequent fields
 48        self.timestamp = TimestampTransform()
 49        self.prefix = PrefixTransform(instrument)
 50        self.writer = UDPWriter(port=port)
 51        self.instrument = instrument
 52        self.first_time = True
 53        self.quit_flag = False
 54
 55    ############################
 56    def run(self, loop=False):
 57        """Start reading and writing data. If loop==True, loop when reaching
 58        end of input.
 59        """
 60        logging.info('Starting %s', self.instrument)
 61        try:
 62            while not self.quit_flag:
 63                record = self.reader.read()
 64
 65                # If we don't have a record, we're (probably) at the end of
 66                # the file. If it's the first time we've tried reading, it
 67                # means we probably didn't get a usable file. Either break out
 68                # (if we're not looping, or if we don't have a usable file),
 69                # or start reading from the beginning (if we are looping and
 70                # have a usable file).
 71                if not record:
 72                    if not loop or self.first_time:
 73                        break
 74                    logging.info('Looping instrument %s', self.instrument)
 75                    self.reader = LogfileReader(filebase=self.filebase,
 76                                                use_timestamps=True)
 77                    continue
 78
 79                # Strip off timestamp and tack on a new one
 80                record = self.slice_n.transform(record)
 81                record = self.timestamp.transform(record)
 82
 83                # Add instrument name back on, and write to specified network
 84                record = self.prefix.transform(record)
 85                self.writer.write(record)
 86                self.first_time = False
 87
 88        except (OSError, KeyboardInterrupt):
 89            self.quit_flag = True
 90
 91        logging.info('Finished %s', self.instrument)
 92
 93
 94################################################################################
 95if __name__ == '__main__':
 96    import argparse
 97    parser = argparse.ArgumentParser()
 98
 99    parser.add_argument('--config', dest='config', default=None,
100                        help='Config file of JSON specs for port-file mappings.')
101
102    parser.add_argument('--port', dest='port', default=None, type=int,
103                        help='UDP port to write to. E.g. 6224.')
104
105    parser.add_argument('--filebase', dest='filebase', default=None,
106                        help='Base string of log file to be read from (with '
107                        'a following "*" for match). E.g. '
108                        '/tmp/log/NBP1406/knud/raw/NBP1406_knud')
109
110    parser.add_argument('--instrument', dest='instrument', help='Prefix to add, '
111                        'if file *doesn\'t* have instrument prefix')
112
113    parser.add_argument('--loop', dest='loop', action='store_true',
114                        help='If True, loop when reaching end of sample data')
115
116    parser.add_argument('-v', '--verbosity', dest='verbosity',
117                        default=0, action='count',
118                        help='Increase output verbosity')
119    args = parser.parse_args()
120
121    LOGGING_FORMAT = '%(asctime)-15s %(message)s'
122    logging.basicConfig(format=LOGGING_FORMAT)
123
124    LOG_LEVELS = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG}
125    args.verbosity = min(args.verbosity, max(LOG_LEVELS))
126    logging.getLogger().setLevel(LOG_LEVELS[args.verbosity])
127
128    # Okay - get to work here
129    if args.config:
130        configs = read_config(args.config)
131        logging.info('Read configs: %s', configs)
132        thread_list = []
133        writer_list = []
134        for instrument, config in configs.items():
135            port = config['port']
136            filebase = config['filebase']
137            writer = SimNetwork(port=port, filebase=filebase, instrument=instrument)
138            writer_thread = threading.Thread(target=writer.run,
139                                             kwargs={'loop': args.loop},
140                                             daemon=True)
141            writer_thread.start()
142            thread_list.append(writer_thread)
143            writer_list.append('%s %d, %s' % (instrument, port, filebase))
144
145        logging.warning('Running simulated ports for:\n%s', '\n'.join(writer_list))
146
147        # Wait for all the threads to end
148        for thread in thread_list:
149            thread.join()
150
151    # If no config file, just a simple, single network writer
152    elif args.port and args.filebase:
153        sim_network = SimNetwork(network=args.port, filebase=args.filebase,
154                                 instrument=args.instrument)
155        sim_network.run(args.loop)
156
157    # Otherwise, we don't have enough information to run
158    else:
159        parser.error('Either --config or --port, --filebase and --instrument '
160                     'must be specified')
class SimNetwork:
31class SimNetwork:
32    """Open a network port and feed stored logfile data to it."""
33    ############################
34
35    def __init__(self, port, filebase, instrument):
36        """
37        ```
38        port -  UDP port on which to write records.
39
40        filebase - Prefix string to be matched (with a following "*") to fine
41                   files to be used. e.g. /tmp/log/NBP1406/knud/raw/NBP1406_knud
42
43        instrument - Instrument name prefix to add before sendind out on wire
44        ```
45        """
46        self.filebase = filebase
47        self.reader = LogfileReader(filebase=filebase, use_timestamps=True)
48        self.slice_n = SliceTransform(fields='1:')  # grab 2nd and subsequent fields
49        self.timestamp = TimestampTransform()
50        self.prefix = PrefixTransform(instrument)
51        self.writer = UDPWriter(port=port)
52        self.instrument = instrument
53        self.first_time = True
54        self.quit_flag = False
55
56    ############################
57    def run(self, loop=False):
58        """Start reading and writing data. If loop==True, loop when reaching
59        end of input.
60        """
61        logging.info('Starting %s', self.instrument)
62        try:
63            while not self.quit_flag:
64                record = self.reader.read()
65
66                # If we don't have a record, we're (probably) at the end of
67                # the file. If it's the first time we've tried reading, it
68                # means we probably didn't get a usable file. Either break out
69                # (if we're not looping, or if we don't have a usable file),
70                # or start reading from the beginning (if we are looping and
71                # have a usable file).
72                if not record:
73                    if not loop or self.first_time:
74                        break
75                    logging.info('Looping instrument %s', self.instrument)
76                    self.reader = LogfileReader(filebase=self.filebase,
77                                                use_timestamps=True)
78                    continue
79
80                # Strip off timestamp and tack on a new one
81                record = self.slice_n.transform(record)
82                record = self.timestamp.transform(record)
83
84                # Add instrument name back on, and write to specified network
85                record = self.prefix.transform(record)
86                self.writer.write(record)
87                self.first_time = False
88
89        except (OSError, KeyboardInterrupt):
90            self.quit_flag = True
91
92        logging.info('Finished %s', self.instrument)

Open a network port and feed stored logfile data to it.

SimNetwork(port, filebase, instrument)
35    def __init__(self, port, filebase, instrument):
36        """
37        ```
38        port -  UDP port on which to write records.
39
40        filebase - Prefix string to be matched (with a following "*") to fine
41                   files to be used. e.g. /tmp/log/NBP1406/knud/raw/NBP1406_knud
42
43        instrument - Instrument name prefix to add before sendind out on wire
44        ```
45        """
46        self.filebase = filebase
47        self.reader = LogfileReader(filebase=filebase, use_timestamps=True)
48        self.slice_n = SliceTransform(fields='1:')  # grab 2nd and subsequent fields
49        self.timestamp = TimestampTransform()
50        self.prefix = PrefixTransform(instrument)
51        self.writer = UDPWriter(port=port)
52        self.instrument = instrument
53        self.first_time = True
54        self.quit_flag = False
port -  UDP port on which to write records.

filebase - Prefix string to be matched (with a following "*") to fine
           files to be used. e.g. /tmp/log/NBP1406/knud/raw/NBP1406_knud

instrument - Instrument name prefix to add before sendind out on wire
filebase
reader
slice_n
timestamp
prefix
writer
instrument
first_time
quit_flag
def run(self, loop=False):
57    def run(self, loop=False):
58        """Start reading and writing data. If loop==True, loop when reaching
59        end of input.
60        """
61        logging.info('Starting %s', self.instrument)
62        try:
63            while not self.quit_flag:
64                record = self.reader.read()
65
66                # If we don't have a record, we're (probably) at the end of
67                # the file. If it's the first time we've tried reading, it
68                # means we probably didn't get a usable file. Either break out
69                # (if we're not looping, or if we don't have a usable file),
70                # or start reading from the beginning (if we are looping and
71                # have a usable file).
72                if not record:
73                    if not loop or self.first_time:
74                        break
75                    logging.info('Looping instrument %s', self.instrument)
76                    self.reader = LogfileReader(filebase=self.filebase,
77                                                use_timestamps=True)
78                    continue
79
80                # Strip off timestamp and tack on a new one
81                record = self.slice_n.transform(record)
82                record = self.timestamp.transform(record)
83
84                # Add instrument name back on, and write to specified network
85                record = self.prefix.transform(record)
86                self.writer.write(record)
87                self.first_time = False
88
89        except (OSError, KeyboardInterrupt):
90            self.quit_flag = True
91
92        logging.info('Finished %s', self.instrument)

Start reading and writing data. If loop==True, loop when reaching end of input.