From f5f1bbdb84c35456a57caab88acafd4863dd37ee Mon Sep 17 00:00:00 2001 From: sparky8512 <76499194+sparky8512@users.noreply.github.com> Date: Mon, 1 Feb 2021 20:47:18 -0800 Subject: [PATCH] Clean up the simple example script and add new one This adds an example script for the starlink_grpc module. It's a kinda silly thing, but I threw it together to better understand some of the status data, so I figured I'd upload it, since the other example is for direct grpc usage (or for starlink_json if parseJsonHistory can be considered an example). Rename dishDumpStatus so pylint will stop complaining about the module name. The only script left with my old naming convention now is parseJsonHistory.py. --- README.md | 14 +++- dishDumpStatus.py => dump_dish_status.py | 17 ++--- poll_history.py | 82 ++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 12 deletions(-) rename dishDumpStatus.py => dump_dish_status.py (51%) create mode 100644 poll_history.py diff --git a/README.md b/README.md index fc49882..646d4d9 100644 --- a/README.md +++ b/README.md @@ -86,16 +86,22 @@ Some of the scripts (currently only the InfluxDB one) also support specifying op ### Other scripts -`dishDumpStatus.py` is a simple example of how to use the grpc modules (the ones generated by protoc, not `starlink_grpc.py`) directly. Just run it as: +`dump_dish_status.py` is a simple example of how to use the grpc modules (the ones generated by protoc, not `starlink_grpc`) directly. Just run it as: ``` -python3 dishDumpStatus.py +python3 dump_dish_status.py ``` and revel in copious amounts of dish status information. OK, maybe it's not as impressive as all that. This one is really just meant to be a starting point for real functionality to be added to it. +`poll_history.py` is another silly example, but this one illustrates how to periodically poll the status and/or bulk history data using the `starlink_grpc` module's API. It's not really useful by itself, but if you really want to, you can run it as: +``` +python3 poll_history.py +``` Possibly more simple examples to come, as the other scripts have started getting a bit complicated. ## To Be Done (Maybe) +More data backend options, including local database support. + There are `reboot` and `dish_stow` requests in the Device protocol, too, so it should be trivial to write a command that initiates dish reboot and stow operations. These are easy enough to do with `grpcurl`, though, as there is no need to parse through the response data. For that matter, they're easy enough to do with the Starlink app. Proper Python packaging, since some of the scripts are no longer self-contained. @@ -128,3 +134,7 @@ The `dish_grpc_influx.py -v status alert_detail` is optional and omitting it wil ![image](https://user-images.githubusercontent.com/945191/104257179-ae570000-5431-11eb-986e-3fedd04bfcfb.png) You'll probably want to run with the `-t` option to `dish_grpc_influx.py` to collect status information periodically for this to be meaningful. + +## Related Projects + +[ChuckTSI's Better Than Nothing Web Interface](https://github.com/ChuckTSI/BetterThanNothingWebInterface) uses grpcurl and PHP to provide a spiffy web UI for some of the same data this project works on. diff --git a/dishDumpStatus.py b/dump_dish_status.py similarity index 51% rename from dishDumpStatus.py rename to dump_dish_status.py index 1bebb41..fd393e2 100644 --- a/dishDumpStatus.py +++ b/dump_dish_status.py @@ -1,10 +1,6 @@ #!/usr/bin/python3 -###################################################################### -# -# Simple example of how to poll the get_status request directly using -# grpc calls. -# -###################################################################### +"""Simple example of get_status request use grpc call directly.""" + import grpc import spacex.api.device.device_pb2 @@ -19,8 +15,9 @@ with grpc.insecure_channel("192.168.100.1:9200") as channel: # Dump everything print(response) -## Just the software version -#print(response.dish_get_status.device_info.software_version) +# Just the software version +print("Software version:", response.dish_get_status.device_info.software_version) -## Check if connected -#print("Connected" if response.dish_get_status.state == spacex.api.device.dish_pb2.DishState.CONNECTED else "Not connected") +# Check if connected +print("Connected" if response.dish_get_status.state == + spacex.api.device.dish_pb2.DishState.CONNECTED else "Not connected") diff --git a/poll_history.py b/poll_history.py new file mode 100644 index 0000000..21adbc5 --- /dev/null +++ b/poll_history.py @@ -0,0 +1,82 @@ +#!/usr/bin/python3 +"""A simple(?) example for using the starlink_grpc module. + +This script shows an example of how to use the starlink_grpc module to +implement polling of status and/or history data. + +By itself, it's not very useful unless you're trying to understand how the +status data correlates with certain aspects of the history data because all it +does is to dump both status and history data when it detects certain +conditions in the history data. +""" + +from datetime import datetime +from datetime import timezone +import time + +import starlink_grpc + +INITIAL_SAMPLES = 20 +LOOP_SLEEP_TIME = 4 + + +def run_loop(context): + samples = INITIAL_SAMPLES + counter = None + prev_triggered = False + while True: + # `starlink_grpc.status_data` returns a tuple of 3 dicts, but in case + # the API changes to add more in the future, it's best to reference + # them by index instead of direct assignment from the function call. + groups = starlink_grpc.status_data(context=context) + status = groups[0] + + # On the other hand, `starlink_grpc.history_bulk_data` will always + # return 2 dicts, because that's all the data there is. + general, bulk = starlink_grpc.history_bulk_data(samples, start=counter, context=context) + + # The following is what actually does stuff with the data. It should + # be replaced with something more useful. + + # This computes a trigger detecting any packet loss (ping drop): + #triggered = any(x > 0 for x in bulk["pop_ping_drop_rate"]) + # This computes a trigger detecting samples marked as obstructed: + #triggered = any(bulk["obstructed"]) + # This computes a trigger detecting samples not marked as scheduled: + triggered = not all(bulk["scheduled"]) + if triggered or prev_triggered: + print("Triggered" if triggered else "Continued", "at:", datetime.now(tz=timezone.utc)) + print("status:", status) + print("history:", bulk) + if not triggered: + print() + + prev_triggered = triggered + # The following makes the next loop only pull the history samples that + # are newer than the ones already examined. + samples = -1 + counter = general["end_counter"] + + # And this is a not-very-robust way of implementing an interval loop. + # Note that a 4 second loop will poll the history buffer pretty + # frequently. Even though we only ask for new samples (which should + # only be 4 of them), the grpc layer needs to pull the entire 12 hour + # history buffer each time, only to discard most of it. + time.sleep(LOOP_SLEEP_TIME) + + +def main(): + # This part is optional. The `starlink_grpc` functions can work without a + # `starlink_grpc.ChannelContext` object passed in, but they will open a + # new channel for each RPC call (so twice for each loop iteration) without + # it. + context = starlink_grpc.ChannelContext() + + try: + run_loop(context) + finally: + context.close() + + +if __name__ == '__main__': + main()