recipes-connectivity: added gnss-save-on-shutdown
gnss-save-on-shutdown is a service, makes gnss receiver state persistent before shut down and check if state got loaded again upon boot on shutdown: contents of receiver ram gets dumped to receiver flash on boot: checks if ram dump got loaded from receiver flash back into receiver ram BugzID: 60669 Signed-off-by: Tobias Jäggi <tobias.jaeggi@netmodule.com>
This commit is contained in:
parent
ca0c6bd6df
commit
b5c8336b0d
|
|
@ -0,0 +1,222 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import getopt
|
||||||
|
import os
|
||||||
|
from os import path
|
||||||
|
|
||||||
|
default_device = r"/dev/gnss0"
|
||||||
|
default_config_file = r"/etc/gnss/gnss0.conf"
|
||||||
|
config_file_prepath = r"/etc/gnss/"
|
||||||
|
config_file_end = r".conf"
|
||||||
|
|
||||||
|
|
||||||
|
def out(str, use_systemd_cat=None, level=None):
|
||||||
|
if use_systemd_cat:
|
||||||
|
cmd = "systemd-cat -p %s echo \"%s\"" % (level, str)
|
||||||
|
os.system(cmd)
|
||||||
|
else:
|
||||||
|
print("%s" % str)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def read_baud_rate(config_file, use_systemd_cat=None):
|
||||||
|
global output
|
||||||
|
if not path.exists(config_file):
|
||||||
|
cmd = "systemd-cat -p err \"config file %s doesn't exists\"" % config_file
|
||||||
|
os.system(cmd)
|
||||||
|
sys.exit(0)
|
||||||
|
with open(config_file, 'r') as file:
|
||||||
|
file_contents = file.readlines()
|
||||||
|
file_contents = ''.join(file_contents)
|
||||||
|
regex_baud_rate = r"^b(aud)? ?r(ate)? ?= ?(?P<baud_rate>\d+)"
|
||||||
|
pattern_baud_rate = re.compile(regex_baud_rate, flags=re.MULTILINE)
|
||||||
|
match_baud_rate = pattern_baud_rate.search(file_contents)
|
||||||
|
if match_baud_rate is not None:
|
||||||
|
return match_baud_rate.group('baud_rate')
|
||||||
|
else:
|
||||||
|
out("unable to read baud rate from %s" % config_file, use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
class UbxtoolWrapper:
|
||||||
|
|
||||||
|
def __init__(self, tool, device, speed, verbosity):
|
||||||
|
self.tool = tool
|
||||||
|
self.device = " -f " + device
|
||||||
|
self.speed = " -s " + speed
|
||||||
|
self.verbosity = " -v " + str(verbosity)
|
||||||
|
|
||||||
|
def poll_preset(self, preset):
|
||||||
|
preset = " -p " + preset
|
||||||
|
ubx_call = self.tool + self.device + self.speed + self.verbosity + preset
|
||||||
|
process = subprocess.Popen(ubx_call.split(), stdout=subprocess.PIPE)
|
||||||
|
process.wait()
|
||||||
|
return process.communicate()[0].decode('utf-8')
|
||||||
|
|
||||||
|
def send_command(self, ubx_class, ubx_id, payload=None):
|
||||||
|
if payload is None:
|
||||||
|
command = " -c " + ubx_class + "," + ubx_id
|
||||||
|
else:
|
||||||
|
command = " -c " + ubx_class + "," + ubx_id + "," + payload
|
||||||
|
ubx_call = self.tool + self.device + self.speed + self.verbosity + command
|
||||||
|
|
||||||
|
process = subprocess.Popen(ubx_call.split(), stdout=subprocess.PIPE)
|
||||||
|
process.wait()
|
||||||
|
try:
|
||||||
|
return process.communicate()[0].decode('utf-8')
|
||||||
|
except 'ubxtool busy':
|
||||||
|
print('communication with ubxtool failed - retry')
|
||||||
|
#cmd = "systemd-cat -p err \communication with ubxtool failed - retry\""
|
||||||
|
#os.system(cmd)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def find_sos_response(ubx_response, command):
|
||||||
|
get_command = re.compile(r"command (?P<command>\d) reserved\d x[\w ]+")
|
||||||
|
get_response = re.compile(r"response (?P<response>\d) reserved\d x[\w ]+")
|
||||||
|
# check if persist was successful
|
||||||
|
matches_command = re.finditer(get_command, ubx_response)
|
||||||
|
match_response = None
|
||||||
|
for match in matches_command:
|
||||||
|
if match.group('command') == command:
|
||||||
|
match_response = re.search(get_response, ubx_response[match.end():])
|
||||||
|
break
|
||||||
|
if match_response is None:
|
||||||
|
# no response found
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return match_response.group('response')
|
||||||
|
|
||||||
|
|
||||||
|
# persits receiver state by dumping (parts of) receiver RAM to its flash
|
||||||
|
def persist_receiver_state(ubx, use_systemd_cat=None):
|
||||||
|
|
||||||
|
# Controlled GNSS stop & Hotstart
|
||||||
|
ubx_response = ubx.send_command(ubx_class='06', ubx_id='04', payload='00,00,08,00')
|
||||||
|
# save contents of BBR to flash
|
||||||
|
ubx_response = ubx.send_command(ubx_class='09', ubx_id='14', payload='00,00,00,00')
|
||||||
|
# check if persist was successful
|
||||||
|
response = find_sos_response(ubx_response, '2') # 2 = Backup File Creation Acknowledge
|
||||||
|
if response is None:
|
||||||
|
out("unable to verify if receiver state is persistant", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
elif response == '1': # ACK -> receiver state persisted
|
||||||
|
out("successfully persisted receiver state", use_systemd_cat=use_systemd_cat, level='info')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
out("unable to verify if receiver state is persistant", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
# verifies loading of receiver state from flash to RAM
|
||||||
|
def verify_load_of_receiver_state(ubx, use_systemd_cat=None):
|
||||||
|
# check if restore was successful
|
||||||
|
ubx_response = ubx.send_command(ubx_class='09', ubx_id='14')
|
||||||
|
response = find_sos_response(ubx_response, '3') # 3 = System Restored from Backup
|
||||||
|
if response is None:
|
||||||
|
out("unable to verify restoration of receiver state", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
elif response == '0': # Unknown
|
||||||
|
out("unable to verify restoration of receiver state", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
elif response == '1': # Failed restoring from backup file
|
||||||
|
out("restoring receiver state failed", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
elif response == '2': # restored from backup
|
||||||
|
out("successfully restored receiver state", use_systemd_cat=use_systemd_cat, level='info')
|
||||||
|
return
|
||||||
|
elif response == '3': # not restored (no backup)
|
||||||
|
out("there was no persistant receiver state to restore", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
out("unable to verify restoration of receiver state", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
# clears backup
|
||||||
|
def clear_perisited_receiver_state(ubx, use_systemd_cat=None):
|
||||||
|
# clear backup (recommended by ublox)
|
||||||
|
ubx_response = ubx.send_command(ubx_class='09', ubx_id='14', payload='01,00,00,00')
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
|
||||||
|
use_default_device = True
|
||||||
|
device = default_device
|
||||||
|
config_file = default_config_file
|
||||||
|
do_persist = False
|
||||||
|
do_verify = False
|
||||||
|
do_clear = False
|
||||||
|
use_systemd_cat = False
|
||||||
|
|
||||||
|
help_message = "\nOptions:\n" \
|
||||||
|
" -p, --persist Save current GNSS receiver state to flash\n" \
|
||||||
|
" -v, --verify Verify that peristed receiver state was loaded upon boot\n" \
|
||||||
|
" -c, --clear Clear saved receiver state from flash (recommended after restore)\n" \
|
||||||
|
" -d, --device </path/to/device> Specify the device to be used\n" \
|
||||||
|
" -j, --journal Use systemd-cat instead of print, recommended when running as service\n" \
|
||||||
|
" to get highlighted error output in systemd journal\n" \
|
||||||
|
" -h, --help Print help\n"
|
||||||
|
try:
|
||||||
|
# Note to self: arguments that require an option are followed with :, long version requite =
|
||||||
|
opts, args = getopt.getopt(argv, "hpvcjd:", ["help", "persist", "verify", "clear", "journal", "device="])
|
||||||
|
except getopt.GetoptError:
|
||||||
|
print("invalid user input")
|
||||||
|
print(help_message)
|
||||||
|
sys.exit(0)
|
||||||
|
if len(opts) < 1:
|
||||||
|
print("no user input")
|
||||||
|
print(help_message)
|
||||||
|
sys.exit(0)
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt in ("-h", "--help"):
|
||||||
|
print(help_message)
|
||||||
|
sys.exit(0)
|
||||||
|
elif opt in ("-p", "--persist"):
|
||||||
|
do_persist = True
|
||||||
|
elif opt in ("-v", "--verify"):
|
||||||
|
do_verify = True
|
||||||
|
elif opt in ("-c", "--clear"):
|
||||||
|
do_clear = True
|
||||||
|
elif opt in ("-j", "--journal"):
|
||||||
|
use_systemd_cat = True
|
||||||
|
# logs from this point on get output to journalctl was -j flag is set
|
||||||
|
elif opt in ("-d", "-device"):
|
||||||
|
use_default_device = False
|
||||||
|
device = arg
|
||||||
|
if not path.exists(device):
|
||||||
|
out("invalid device", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
regex_device_name = re.compile(r"\/dev\/(?P<device_name>[\w]+)")
|
||||||
|
device_name = regex_device_name.search(device).group('device_name')
|
||||||
|
possible_config_file = config_file_prepath + device_name + config_file_end
|
||||||
|
if not path.exists(possible_config_file):
|
||||||
|
out("no config file for %s found at %s, using default config file %s instead" % (device_name, possible_config_file, default_config_file), use_systemd_cat=use_systemd_cat, level='warning')
|
||||||
|
else:
|
||||||
|
out("config file used for device %s: %s" % (device_name, possible_config_file), use_systemd_cat=use_systemd_cat, level='debug')
|
||||||
|
config_file = possible_config_file
|
||||||
|
|
||||||
|
if do_persist and do_verify:
|
||||||
|
out("You can't persist the receiver state and verify a restore at the same time.", use_systemd_cat=use_systemd_cat, level='err')
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if use_default_device:
|
||||||
|
out("no device specified, using default %s" % default_device, use_systemd_cat=use_systemd_cat, level='warning')
|
||||||
|
|
||||||
|
baud_rate = read_baud_rate(config_file, use_systemd_cat=use_systemd_cat)
|
||||||
|
|
||||||
|
ubx = UbxtoolWrapper(tool="ubxtool", device=device, speed=str(baud_rate), verbosity=2)
|
||||||
|
|
||||||
|
if do_persist:
|
||||||
|
persist_receiver_state(ubx, use_systemd_cat=use_systemd_cat)
|
||||||
|
if do_clear:
|
||||||
|
clear_perisited_receiver_state(ubx, use_systemd_cat=use_systemd_cat)
|
||||||
|
if do_verify:
|
||||||
|
verify_load_of_receiver_state(ubx, use_systemd_cat=use_systemd_cat)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(sys.argv[1:])
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Save current GNSS receiver state on shutdown
|
||||||
|
Before=gpsd.service
|
||||||
|
After=gnss-config.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/bin/gnss-save-on-shutdown -vcj -d /dev/gnss0
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStop=/usr/bin/gnss-save-on-shutdown -pj -d /dev/gnss0
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Copyright (C) 2020 Tobias Jäggi <tobias.jaeggi@netmodule.com>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Released under the MIT license (see COPYING.MIT for the terms)
|
||||||
|
DESCRIPTION = "NEO-M8L save on shutdown service"
|
||||||
|
HOMEPAGE = "www.netmodule.com"
|
||||||
|
LICENSE = "MIT"
|
||||||
|
SECTION = "bsp/firmware"
|
||||||
|
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
|
||||||
|
|
||||||
|
inherit systemd
|
||||||
|
|
||||||
|
SRC_URI = " \
|
||||||
|
file://gnss-save-on-shutdown.py \
|
||||||
|
file://gnss-save-on-shutdown.service \
|
||||||
|
"
|
||||||
|
|
||||||
|
S = "${WORKDIR}"
|
||||||
|
|
||||||
|
SYSTEMD_SERVICE_${PN} = " \
|
||||||
|
gnss-save-on-shutdown.service \
|
||||||
|
"
|
||||||
|
|
||||||
|
FILES_${PN} = "${systemd_unitdir}/system ${bindir} ${sysconfdir}"
|
||||||
|
|
||||||
|
do_install() {
|
||||||
|
install -d ${D}${bindir}
|
||||||
|
install -m 744 ${WORKDIR}/gnss-save-on-shutdown.py ${D}${bindir}/gnss-save-on-shutdown
|
||||||
|
|
||||||
|
install -d ${D}${systemd_unitdir}/system/
|
||||||
|
install -m 644 ${WORKDIR}/gnss-save-on-shutdown.service ${D}${systemd_unitdir}/system/
|
||||||
|
}
|
||||||
|
|
||||||
|
RDEPENDS_${PN} = "gpsd"
|
||||||
Loading…
Reference in New Issue