1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2019 Grand Joldes (grandwork2@yahoo.com).
5#
6# This file is Copyright (c) 2019 by the GPSD project
7#
8# SPDX-License-Identifier: BSD-2-clause
9
10# This code run compatibly under Python 3.x for x >= 6.
11
12"""
13Example of using the asyncio Python interface to GPSD. This example
14demonstrates error handling by the application code when aiogps is not
15configured to handle automatic re-connection.
16"""
17
18import asyncio
19import logging
20import gps.aiogps
21
22
23async def get_gps_updates(gpsd: gps.aiogps.aiogps) -> None:
24    """ Receives and prints messages from GPSD.
25
26    The GPS status information is updated within aiogps every time a new
27    message is received.
28    This function also demonstrates what error messages can be expected when
29    auto reconnection is not used in aiogps (reconnect = 0).
30    """
31    while True:
32        try:
33            async for msg in gpsd:
34                # Print received message
35                logging.info(f'Received: {msg}')
36        except asyncio.CancelledError:
37            return
38        except asyncio.IncompleteReadError:
39            logging.info('Connection closed by server')
40        except asyncio.TimeoutError:
41            logging.error('Timeout waiting for gpsd to respond')
42        except Exception as exc:    # pylint: disable=W0703
43            logging.error(f'Error: {exc}')
44        # Try again in 1s
45        await asyncio.sleep(1)
46
47
48async def print_gps_info(gpsd: gps.aiogps.aiogps) -> None:
49    """ Prints GPS status every 5s """
50    while True:
51        try:
52            await asyncio.sleep(5)
53            logging.info(f'\nGPS status:\n{gpsd}')
54        except asyncio.CancelledError:
55            return
56        except Exception as exc:    # pylint: disable=W0703
57            logging.error(f'Error: {exc}')
58
59
60async def main():
61    """ Main coroutine - executes 2 asyncio tasks in parralel """
62    try:
63        # Example of using custom connection configuration
64        async with gps.aiogps.aiogps(
65            connection_args={
66                'host': '127.0.0.1',
67                'port': 2947
68            },
69            connection_timeout=5,
70            reconnect=0,  # do not reconnect, raise errors
71            alive_opts={
72                'rx_timeout': 5
73            }
74        ) as gpsd:
75            # These tasks will be executed in parallel
76            await asyncio.gather(
77                get_gps_updates(gpsd),
78                print_gps_info(gpsd),
79                return_exceptions=True
80            )
81    except asyncio.CancelledError:
82        return
83    except Exception as exc:    # pylint: disable=W0703
84        logging.error(f'Error: {exc}')
85
86
87def run():
88    """
89    Main function.
90
91    Because this code only compiles on Python versions >= 3.6,
92    it is not run directly, but through the example_aiogps_run wrapper,
93    which fails gracefully on unsupported Python versions.
94    """
95    # Set up logging program logging
96    logging.basicConfig()
97    logging.root.setLevel(logging.INFO)
98    # Example of setting up logging level for aiogps - this setting will
99    # prevent all aiogps events from being logged
100    logging.getLogger('gps.aiogps').setLevel(logging.CRITICAL)
101    loop = asyncio.events.new_event_loop()
102    try:
103        asyncio.events.set_event_loop(loop)
104        loop.run_until_complete(main())
105    except KeyboardInterrupt:
106        print('Got keyboard interrupt.')
107    finally:
108        print('Exiting.')
109        try:
110            for task in asyncio.Task.all_tasks():
111                task.cancel()
112        finally:
113            loop.run_until_complete(loop.shutdown_asyncgens())
114            asyncio.events.set_event_loop(None)
115            loop.close()
116