From 26bafbc3ef396a632b9cbe5abfd70f6613251899 Mon Sep 17 00:00:00 2001 From: Phil Puetzstueck Date: Thu, 14 Dec 2017 14:55:28 +0100 Subject: [PATCH 1/3] Added a simple update script This allows fleet update of robots Signed-off-by: Christian Ege --- scripts/o3d3xx-updater.py | 157 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100755 scripts/o3d3xx-updater.py diff --git a/scripts/o3d3xx-updater.py b/scripts/o3d3xx-updater.py new file mode 100755 index 0000000..0202baf --- /dev/null +++ b/scripts/o3d3xx-updater.py @@ -0,0 +1,157 @@ +''' +Copyright (c) 2017 Phil Puetzstueck + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +''' + +import requests +import o3d3xx +import os.path +import time +import threading +import sys + +class SWUpdate : + + def __init__(self, hostname='192.168.0.69', filename=None): + if not hostname: + self.hostname = "192.168.0.69" + else: + self.hostname=hostname + self.xml_rpc = o3d3xx.Device(self.hostname) + self.mode = self.get_mode() + self.filename = filename + + def __enter__(self): + return self + + def __exit__(self,exc_type, exc_value, traceback): + pass + + def get_mode(self): + response = requests.get("http://"+self.hostname) + mode = 0 + while True: + if response.ok: + self.mode = mode + break + else: + response = requests.get("http://"+self.hostname+":8080") + mode = 1 + time.sleep(0.1) + return mode + + + def check_filename(self, filename): + while not filename or not os.path.exists(filename): + filename = input("Input file not Found. Please enter filepath: ") + return filename + + + def reboot(self, mode): + mode = int(mode) + if mode not in [0, 1]: + raise ValueError("reboot only takes modes 0(productive) and 1(recovery)") + + if self.mode != mode: + + if mode: + print("Rebooting to mode 1(recovery)...") + self.xml_rpc.reboot(1) + else: + print("Rebooting to mode 0(productive)...") + requests.post("http://"+self.hostname+":8080/reboot_to_live") + + self.wait_for_reboot(mode) + else: + print("Device is already in this mode!") + + def wait_for_reboot(self, mode): + reboot_mode = self.get_mode() + while reboot_mode != mode: + time.sleep(0.2) + reboot_mode = self.get_mode() + print("Finished rebooting into mode {}".format(mode)) + + def upload(self,filename): + # the camera doesnt respong, so the post never returns, this is a quite hackish (hopefully temp.) solution: + filename = self.check_filename(filename) + payload = open(filename, 'rb').read() + header = {'Content-Type':'application/octet-stream','X_FILENAME': filename} + url = 'http://{}:8080/handle_post_request'.format(self.hostname) + print('Uploading update file: ' + filename + ' to ' + url ) + + def post(): + requests.post(url,headers=header,data=payload) + thread = threading.Thread(target = post) + thread.daemon = True + thread.start() + + finished = self.status()[1]["Status"] == "3" + + while not finished: + isOk, json = self.status() + if json["Msg"] != "": + print("\n"+json["Msg"]) + if json["Error"] != 0 or not isOk: + raise requests.exceptions.HTTPError("Something went wrong during Uploading/Installation") + finished = json["Status"] == "3" + time.sleep(0.5) + duration = time.time() - start + print("Finished Install") + + def status(self): + url = 'http://{}:8080/getstatus.json'.format(self.hostname) + r = requests.get(url) + if r.status_code == requests.codes.ok: + return True,r.json() + else: + return False,{} + + def install_swu(self, filename): + if not self.mode: + self.reboot(1) + self.upload(filename) + self.reboot(0) + + +if __name__ == "__main__": + import argparse + + p = argparse.ArgumentParser() + p.add_argument("-i", "--input", help="specify input SWU file and install update") + p.add_argument("-H", "--host", help="specify host ip") + p.add_argument("-r", "--reboot", help="reboot to the mode specified(0: productive, 1:recovery) and exit") + p.add_argument("-m", "--mode", help="print curret mode and exit", action="store_true") + args = p.parse_args() + if len(sys.argv) == 1: + p.print_help() + elif args.mode: + s = SWUpdate(args.host) + print(s.mode) + elif args.reboot is not None: + s = SWUpdate(args.host) + s.reboot(args.reboot) + else: + s = SWUpdate(args.host) + if args.input: + s.install_swu(os.path.expanduser(args.input)) + else: + s.install_swu("") From 5b4ed34082a7986c4100a273f15bd754f025af94 Mon Sep 17 00:00:00 2001 From: Christian Ege Date: Thu, 14 Dec 2017 14:57:11 +0100 Subject: [PATCH 2/3] Added the simple update script to the installation This script provides a way for updating an O3D3xx via command line Signed-off-by: Christian Ege --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 13e550d..12374c2 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,8 @@ author_email = 'Christoph.Freundl@ifm.com', license = 'MIT', packages = ['o3d3xx', 'o3d3xx.rpc', 'o3d3xx.pcic'], - install_requires = ['future'], + scripts = ['scripts/o3d3xx-updater.py'], + install_requires = ['future','requests'], test_suite = 'nose.collector', tests_require = ['nose'], zip_safe = False) From 8623a714baf0645037724bc2e0f55465ea81c711 Mon Sep 17 00:00:00 2001 From: Christian Ege Date: Thu, 14 Dec 2017 16:28:23 +0100 Subject: [PATCH 3/3] Removed the buggy mode handling Signed-off-by: Christian Ege --- scripts/o3d3xx-updater.py | 72 ++++++++------------------------------- 1 file changed, 14 insertions(+), 58 deletions(-) diff --git a/scripts/o3d3xx-updater.py b/scripts/o3d3xx-updater.py index 0202baf..ef49ddd 100755 --- a/scripts/o3d3xx-updater.py +++ b/scripts/o3d3xx-updater.py @@ -27,6 +27,7 @@ import time import threading import sys +import json class SWUpdate : @@ -36,7 +37,6 @@ def __init__(self, hostname='192.168.0.69', filename=None): else: self.hostname=hostname self.xml_rpc = o3d3xx.Device(self.hostname) - self.mode = self.get_mode() self.filename = filename def __enter__(self): @@ -45,51 +45,12 @@ def __enter__(self): def __exit__(self,exc_type, exc_value, traceback): pass - def get_mode(self): - response = requests.get("http://"+self.hostname) - mode = 0 - while True: - if response.ok: - self.mode = mode - break - else: - response = requests.get("http://"+self.hostname+":8080") - mode = 1 - time.sleep(0.1) - return mode - def check_filename(self, filename): while not filename or not os.path.exists(filename): filename = input("Input file not Found. Please enter filepath: ") return filename - - def reboot(self, mode): - mode = int(mode) - if mode not in [0, 1]: - raise ValueError("reboot only takes modes 0(productive) and 1(recovery)") - - if self.mode != mode: - - if mode: - print("Rebooting to mode 1(recovery)...") - self.xml_rpc.reboot(1) - else: - print("Rebooting to mode 0(productive)...") - requests.post("http://"+self.hostname+":8080/reboot_to_live") - - self.wait_for_reboot(mode) - else: - print("Device is already in this mode!") - - def wait_for_reboot(self, mode): - reboot_mode = self.get_mode() - while reboot_mode != mode: - time.sleep(0.2) - reboot_mode = self.get_mode() - print("Finished rebooting into mode {}".format(mode)) - def upload(self,filename): # the camera doesnt respong, so the post never returns, this is a quite hackish (hopefully temp.) solution: filename = self.check_filename(filename) @@ -104,17 +65,19 @@ def post(): thread.daemon = True thread.start() - finished = self.status()[1]["Status"] == "3" + finished = False while not finished: - isOk, json = self.status() - if json["Msg"] != "": - print("\n"+json["Msg"]) - if json["Error"] != 0 or not isOk: + isOk, res = self.status() + if isOk and res["Msg"] != "": + print("\n"+res["Msg"]) + if not isOk: raise requests.exceptions.HTTPError("Something went wrong during Uploading/Installation") - finished = json["Status"] == "3" + if res["Error"] != "0": + print(json.dumps(res, indent = 4)) + raise requests.exceptions.HTTPError("Error occured during Installation") + finished = res["Status"] == "3" time.sleep(0.5) - duration = time.time() - start print("Finished Install") def status(self): @@ -126,11 +89,12 @@ def status(self): return False,{} def install_swu(self, filename): - if not self.mode: - self.reboot(1) self.upload(filename) - self.reboot(0) + self.reboot() + def reboot(self): + print("Rebooting to productive mode...") + requests.post("http://"+self.hostname+":8080/reboot_to_live") if __name__ == "__main__": import argparse @@ -138,17 +102,9 @@ def install_swu(self, filename): p = argparse.ArgumentParser() p.add_argument("-i", "--input", help="specify input SWU file and install update") p.add_argument("-H", "--host", help="specify host ip") - p.add_argument("-r", "--reboot", help="reboot to the mode specified(0: productive, 1:recovery) and exit") - p.add_argument("-m", "--mode", help="print curret mode and exit", action="store_true") args = p.parse_args() if len(sys.argv) == 1: p.print_help() - elif args.mode: - s = SWUpdate(args.host) - print(s.mode) - elif args.reboot is not None: - s = SWUpdate(args.host) - s.reboot(args.reboot) else: s = SWUpdate(args.host) if args.input: