def __init__(self, socketio, rtklib_path = None, config_path=None, enable_led = True, log_path = None): print("RTKLIB 1") print(rtklib_path) print(log_path) if rtklib_path is None: rtklib_path = os.path.join(os.path.expanduser("~"), "RTKLIB") if config_path is None: self.config_path = os.path.join(os.path.dirname(__file__), "rtklib_configs") else: self.config_path = config_path if log_path is None: #TODO find a better default location self.log_path = "../data" else: self.log_path = log_path # This value should stay below the timeout value or the Satellite/Coordinate broadcast # thread will stop self.sleep_count = 0 # default state for RTKLIB is "rover single" self.state = "base" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle rover mode self.rtkc = RtkController(rtklib_path, self.config_path) self.conm = ConfigManager(rtklib_path, self.config_path) # this one handles base settings self.s2sc = Str2StrController(rtklib_path) # take care of serving logs self.logm = LogManager(rtklib_path, self.log_path) # basic synchronisation to prevent errors self.semaphore = Semaphore() # we need this to send led signals # self.enable_led = enable_led # if self.enable_led: # self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None self.conversion_thread = None self.system_time_correct = False # self.system_time_correct = True self.time_thread = Thread(target = self.setCorrectTime) self.time_thread.start()
def __init__(self, socketio, enable_led = True, rtkrcv_path = None, config_path = None, str2str_path = None, gps_cmd_file_path = None, log_path = None): # default state for RTKLIB is "rover single" self.state = "rover" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle rover mode self.rtkc = RtkController(rtkrcv_path, config_path) self.conm = ConfigManager(config_path) # this one handles base settings self.s2sc = Str2StrController(str2str_path, gps_cmd_file_path) # take care of serving logs self.logm = LogManager(log_path) # basic synchronisation to prevent errors self.semaphore = Semaphore() # we need this to send led signals self.enable_led = enable_led if self.enable_led: self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None # we try to restore previous state # in case we can't, we start as rover in single mode self.loadState()
def __init__(self, socketio, enable_led = True, path_to_rtkrcv = None, path_to_configs = None, path_to_str2str = None, path_to_gps_cmd_file = None): # default state for RTKLIB is "inactive" self.state = "inactive" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle base mode self.rtkc = RtkController(path_to_rtkrcv) self.conm = ConfigManager(path_to_configs) # this one handles base settings self.s2sc = Str2StrController(path_to_str2str, path_to_gps_cmd_file) # basic synchronisation to prevent errors self.saveState() self.semaphore = Semaphore() # we need this to send led signals self.enable_led = enable_led if self.enable_led: self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None # we try to restore previous state self.loadState()
def __init__(self, socketio, rtklib_path=None, enable_led=True, log_path=None): if rtklib_path is None: rtklib_path = "/home/reach/RTKLIB" if log_path is None: log_path = "/home/reach/logs" # default state for RTKLIB is "rover single" self.state = "rover" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle rover mode self.rtkc = RtkController(rtklib_path) self.conm = ConfigManager(rtklib_path) # this one handles base settings self.s2sc = Str2StrController(rtklib_path) # take care of serving logs self.logm = LogManager(rtklib_path, log_path) # basic synchronisation to prevent errors self.semaphore = Semaphore() # we need this to send led signals self.enable_led = enable_led if self.enable_led: self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None self.conversion_thread = None self.system_time_correct = False self.time_thread = Thread(target=self.setCorrectTime) self.time_thread.start()
def __init__(self, socketio, rtklib_path = None, enable_led = True, log_path = None): if rtklib_path is None: rtklib_path = "/home/reach/RTKLIB" if log_path is None: log_path = "/home/reach/logs" # default state for RTKLIB is "rover single" self.state = "rover" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle rover mode self.rtkc = RtkController(rtklib_path) self.conm = ConfigManager(rtklib_path) # this one handles base settings self.s2sc = Str2StrController(rtklib_path) # take care of serving logs self.logm = LogManager(rtklib_path, log_path) # basic synchronisation to prevent errors self.semaphore = Semaphore() # we need this to send led signals self.enable_led = enable_led if self.enable_led: self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None self.conversion_thread = None self.system_time_correct = False self.time_thread = Thread(target = self.setCorrectTime) self.time_thread.start()
class RTKLIB: # we will save RTKLIB state here for later loading state_file = "/home/reach/.reach/rtk_state" def __init__(self, socketio, enable_led = True, rtkrcv_path = None, config_path = None, str2str_path = None, gps_cmd_file_path = None, log_path = None): # default state for RTKLIB is "rover single" self.state = "rover" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle rover mode self.rtkc = RtkController(rtkrcv_path, config_path) self.conm = ConfigManager(config_path) # this one handles base settings self.s2sc = Str2StrController(str2str_path, gps_cmd_file_path) # take care of serving logs self.logm = LogManager(log_path) # basic synchronisation to prevent errors self.semaphore = Semaphore() # we need this to send led signals self.enable_led = enable_led if self.enable_led: self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None # we try to restore previous state # in case we can't, we start as rover in single mode self.loadState() def launchRover(self, config_name = None): # config_name may be a name, or a full path # if the parameter contains "/", then we consider it a full path # else, we will be looking for it one directory higher than the rtkrcv bin self.semaphore.acquire() print("Attempting to launch rtkrcv...") if config_name == None: res = self.rtkc.launch() else: res = self.rtkc.launch(config_name) if res < 0: print("rtkrcv launch failed") elif res == 1: print("rtkrcv launch successful") self.state = "rover" elif res == 2: print("rtkrcv already launched") self.state = "rover" self.saveState() if self.enable_led: self.updateLED() print("Rover mode launched") self.semaphore.release() return res def shutdownRover(self): self.stopRover() self.semaphore.acquire() print("Attempting rtkrcv shutdown") res = self.rtkc.shutdown() if res < 0: print("rtkrcv shutdown failed") elif res == 1: print("rtkrcv shutdown successful") self.state = "inactive" elif res == 2: print("rtkrcv already shutdown") self.state = "inactive" self.saveState() if self.enable_led: self.updateLED() print("Rover mode shutdown") self.semaphore.release() return res def startRover(self): self.semaphore.acquire() print("Attempting to start rtkrcv...") res = self.rtkc.start() if res == -1: print("rtkrcv start failed") elif res == 1: print("rtkrcv start successful") print("Starting coordinate and satellite broadcast") elif res == 2: print("rtkrcv already started") # start fresh data broadcast self.server_not_interrupted = True if self.satellite_thread is None: self.satellite_thread = Thread(target = self.broadcastSatellites) self.satellite_thread.start() if self.coordinate_thread is None: self.coordinate_thread = Thread(target = self.broadcastCoordinates) self.coordinate_thread.start() self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def stopRover(self): self.semaphore.acquire() print("Attempting to stop RTKLIB...") res = self.rtkc.stop() if res == -1: print("rtkrcv stop failed") elif res == 1: print("rtkrcv stop successful") elif res == 2: print("rtkrcv already stopped") self.server_not_interrupted = False if self.satellite_thread is not None: self.satellite_thread.join() self.satellite_thread = None if self.coordinate_thread is not None: self.coordinate_thread.join() self.coordinate_thread = None self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def launchBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this launchBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.semaphore.acquire() self.state = "base" self.saveState() if self.enable_led: self.updateLED() print("Base mode launched") self.semaphore.release() def shutdownBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this shutdownBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.stopBase() self.semaphore.acquire() self.state = "inactive" self.saveState() if self.enable_led: self.updateLED() print("Base mode shutdown") self.semaphore.release() def startBase(self, rtcm3_messages = None, base_position = None, gps_cmd_file = None): self.semaphore.acquire() print("Attempting to start str2str...") if not self.rtkc.started: res = self.s2sc.start(rtcm3_messages, base_position, gps_cmd_file) if res < 0: print("str2str start failed") elif res == 1: print("str2str start successful") elif res == 2: print("str2str already started") else: print("Can't start str2str with rtkrcv still running!!!!") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def stopBase(self): self.semaphore.acquire() print("Attempting to stop str2str...") res = self.s2sc.stop() if res == -1: print("str2str stop failed") elif res == 1: print("str2str stop successful") elif res == 2: print("str2str already stopped") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def readConfigBase(self): self.semaphore.acquire() print("Got signal to read base config") self.socketio.emit("current config base", self.s2sc.readConfig(), namespace = "/test") self.semaphore.release() def writeConfigBase(self, config): self.semaphore.acquire() print("Got signal to write base config") self.s2sc.writeConfig(config) print("Restarting str2str...") res = self.s2sc.stop() + self.s2sc.start() if res > 1: print("Restart successful") else: print("Restart failed") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def writeConfigRover(self, config): # config dict must include config_name field self.semaphore.acquire() if "config_file_name" not in config: config_file = None else: config_file = config["config_file_name"] print("Got signal to write rover config to file " + config_file) self.conm.writeConfig(config_file, config) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") self.semaphore.release() def loadConfigRover(self, config_file = None): # we might want to write the config, but dont need to load it every time self.semaphore.acquire() if config_file == None: config_file == self.conm.default_rover_config print("Loading config " + config_file) # loading config to rtkrcv if self.rtkc.loadConfig(config_file) < 0: print("ERROR: failed to load config!!!") print("abort load") self.semaphore.release() return -1 print("load successful!") print("Now we need to restart rtkrcv for the changes to take effect") if self.rtkc.started: print("rtkrcv is already started, we need to do a simple restart!") res = self.rtkc.restart() if res == 3: print("Restart successful") print(config_file + " config loaded") elif res == 1: print("rtkrcv started instead of restart") elif res < 1: print("ERROR: rtkrcv restart failed") self.semaphore.release() self.saveState() return res else: print("We were not started before, so we need to perform a full start") self.semaphore.release() return self.startRover() def readConfigRover(self, config): self.semaphore.acquire() if "config_file_name" not in config: config_file = None else: config_file = config["config_file_name"] print("Got signal to read the rover config") print("Sending rover config " + config_file) # read from file self.conm.readConfig(config_file) # send to the browser self.socketio.emit("current config rover", self.conm.buffered_config.items, namespace="/test") self.semaphore.release() def shutdown(self): # shutdown whatever mode we are in. stop broadcast threads # clean up broadcast and blink threads self.server_not_interrupted = False self.led.blinker_not_interrupted = False if self.coordinate_thread is not None: self.coordinate_thread.join() if self.satellite_thread is not None: self.satellite_thread.join() if self.led.blinker_thread is not None: self.led.blinker_thread.join() # shutdown base or rover if self.state == "rover": return self.shutdownRover() elif self.state == "base": return self.shutdownBase() # otherwise, we are inactive return 1 def deleteConfig(self, config_name): # pass deleteConfig to conm print("Got signal to delete config " + config_name) self.conm.deleteConfig(config_name) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") print(self.conm.available_configs) def resetConfigToDefault(self, config_name): # pass reset config to conm print("Got signal to reset config " + config_name) self.conm.resetConfigToDefault(config_name) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") print(self.conm.available_configs) def saveState(self): # save current state for future resurrection: # state is a list of parameters: # rover state example: ["rover", "started", "reach_single_default.conf"] # base state example: ["base", "stopped", "input_stream", "output_stream"] state = {} # save "rover", "base" or "inactive" state state["state"] = self.state if self.state == "rover": started = self.rtkc.started elif self.state == "base": started = self.s2sc.started elif self.state == "inactive": started = False state["started"] = "yes" if started else "no" # dump rover state state["rover"] = {"current_config": self.rtkc.current_config} # dump rover state state["base"] = { "input_stream": self.s2sc.input_stream, "output_stream": self.s2sc.output_stream, "rtcm3_messages": self.s2sc.rtcm3_messages, "base_position": self.s2sc.base_position, "gps_cmd_file": self.s2sc.gps_cmd_file } print("DEBUG saving state") print(str(state)) with open(self.state_file, "w") as f: json.dump(state, f, sort_keys = True, indent = 4) return state def byteify(self, input): # thanks to Mark Amery from StackOverFlow for this awesome function if isinstance(input, dict): return {self.byteify(key): self.byteify(value) for key, value in input.iteritems()} elif isinstance(input, list): return [self.byteify(element) for element in input] elif isinstance(input, unicode): return input.encode('utf-8') else: return input def getState(self): # get the state, currently saved in the state file print("Trying to read previously saved state...") try: f = open(self.state_file, "r") except IOError: # can't find the file, let's create a new one with default state print("Could not find existing state, Launching default single rover mode...") return 1 else: print("Found existing state...trying to decode...") try: json_state = json.load(f) except ValueError: # could not properly decode current state print("Could not decode json state. Launching single rover mode as default...") f.close() return 1 else: print("Decoding succesful") f.close() # convert unicode strings to normal json_state = self.byteify(json_state) print("That's what we found:") print(str(json_state)) return json_state def loadState(self): # get current state json_state = self.getState() if json_state == 1: # we dont need to load as we were forced to start # as default single rover due to corrupt/missing state file self.launchRover() # for the first start, just let it be green self.updateLED("green") return print("Now loading the state printed above... ") # first, we restore the base state, because no matter what we end up doing, # we need to restore base state self.s2sc.input_stream = json_state["base"]["input_stream"] self.s2sc.output_stream = json_state["base"]["output_stream"] self.s2sc.rtcm3_messages = json_state["base"]["rtcm3_messages"] self.s2sc.base_position = json_state["base"]["base_position"] self.s2sc.gps_cmd_file = json_state["base"]["gps_cmd_file"] if json_state["state"] == "rover": saved_config = json_state["rover"]["current_config"] if saved_config == "": saved_config = None self.launchRover(saved_config) if json_state["started"] == "yes": self.startRover() elif json_state["state"] == "base": self.launchBase() if json_state["started"] == "yes": self.startBase() else: # in case we are inactive self.launchRover() print(str(json_state["state"]) + " state loaded") def sendState(self): # send current state to every connecting browser state = self.getState() self.conm.updateAvailableConfigs() state["available_configs"] = self.conm.available_configs print("Available configs to send: ") print(str(state["available_configs"])) self.socketio.emit("current state", state, namespace = "/test") def updateLED(self, pattern = None): # this forms a distinctive and informative blink pattern showing following info: # network_status/rtk_mode/started?/solution_status # network: self-hosted AP or connected? green/blue # rtk mode: rover or base? blue/magenta # started: yes or no? green/red # solution status: -, single, float, fix? red/cyan/yellow/green blink_pattern = [] delay = 0.5 # get wi-fi connection status for the first signal cmd = ["configure_edison", "--showWiFiMode"] cmd = " ".join(cmd) proc = Popen(cmd, stdout = PIPE, shell = True, bufsize = 2048) out = proc.communicate() out = out[0].split("\n")[0] if out == "Master": blink_pattern.append("green") elif out == "Managed": blink_pattern.append("blue") if self.state == "base": blink_pattern.append("magenta") if self.s2sc.started: # we have a started base blink_pattern.append("green") else: # we have a stopped base blink_pattern.append("red") # for now, the base doesn't have solution blink_pattern.append("off") elif self.state == "rover": blink_pattern.append("blue") if self.rtkc.started: # we have a started rover blink_pattern.append("green") status_pattern_dict = { "fix": "green,off", "float": "yellow,off", "single": "cyan,off", "-": "read,off" } # we need to acquire RtkController in case it's currently updating info dict self.rtkc.semaphore.acquire() current_rover_solutuon_status = self.rtkc.info.get("solution_status", "") self.rtkc.semaphore.release() # if we don't know this status, we just pass blink_pattern.append(status_pattern_dict.get(current_rover_solutuon_status, "off")) else: # we have a stopped rover blink_pattern.append("red") # we are not started, meaning no status just yet blink_pattern.append("off") # sync color for better comprehension blink_pattern.append("white") # concatenate all that into one big string blink_pattern = ",off,".join(blink_pattern) + ",off" if pattern is not None: blink_pattern = pattern if blink_pattern: # check blink_pattern contains something new if blink_pattern != self.led.current_blink_pattern: # if we decided we need a new pattern, then start blinking it self.led.startBlinker(blink_pattern, delay) # thread workers for broadcasing rtkrcv status # this function reads satellite levels from an exisiting rtkrcv instance # and emits them to the connected browser as messages def broadcastSatellites(self): count = 0 while self.server_not_interrupted: # update satellite levels self.rtkc.getObs() if count % 10 == 0: print("Sending sat rover levels:\n" + str(self.rtkc.obs_rover)) print("Sending sat base levels:\n" + str(self.rtkc.obs_base)) self.socketio.emit("satellite broadcast rover", self.rtkc.obs_rover, namespace = "/test") self.socketio.emit("satellite broadcast base", self.rtkc.obs_base, namespace = "/test") count += 1 time.sleep(1) # this function reads current rtklib status, coordinates and obs count def broadcastCoordinates(self): count = 0 while self.server_not_interrupted: # update RTKLIB status self.rtkc.getStatus() if count % 10 == 0: print("Sending RTKLIB status select information:") print(self.rtkc.info) self.socketio.emit("coordinate broadcast", self.rtkc.info, namespace = "/test") if self.enable_led: self.updateLED() count += 1 time.sleep(1)
class RTKLIB: # we will save RTKLIB state here for later loading state_file = "/home/reach/.reach/rtk_state" # if the state file is not available, these settings are loaded default_state = { "base": { "base_position": [], "gps_cmd_file": "GPS_10Hz.cmd", "input_stream": "serial://ttyMFD1:230400:8:n:1:off#ubx", "output_stream": "tcpsvr://:9000#rtcm3", "rtcm3_messages": [ "1002", "1006", "1008", "1010", "1019", "1020" ] }, "rover": { "current_config": "reach_single_default.conf" }, "started": "no", "state": "rover" } def __init__(self, socketio, rtklib_path = None, enable_led = True, log_path = None): if rtklib_path is None: rtklib_path = "/home/reach/RTKLIB" if log_path is None: log_path = "/home/reach/logs" # default state for RTKLIB is "rover single" self.state = "rover" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle rover mode self.rtkc = RtkController(rtklib_path) self.conm = ConfigManager(rtklib_path) # this one handles base settings self.s2sc = Str2StrController(rtklib_path) # take care of serving logs self.logm = LogManager(rtklib_path, log_path) # basic synchronisation to prevent errors self.semaphore = Semaphore() # we need this to send led signals self.enable_led = enable_led if self.enable_led: self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None self.conversion_thread = None self.system_time_correct = False self.time_thread = Thread(target = self.setCorrectTime) self.time_thread.start() # we try to restore previous state # in case we can't, we start as rover in single mode # self.loadState() def setCorrectTime(self): # determine if we have ntp service ready or we need gps time if not gps_time.time_synchronised_by_ntp(): # wait for gps time print("Time is not synced by NTP") self.updateLED("orange,off") gps_time.set_gps_time("/dev/ttyMFD1", 230400) print("Time is synced by NTP!") self.system_time_correct = True self.socketio.emit("system time corrected", {}, namespace="/test") self.loadState() self.socketio.emit("system state reloaded", {}, namespace="/test") def launchRover(self, config_name = None): # config_name may be a name, or a full path # if the parameter contains "/", then we consider it a full path # else, we will be looking for it one directory higher than the rtkrcv bin self.semaphore.acquire() print("Attempting to launch rtkrcv...") if config_name == None: res = self.rtkc.launch() else: res = self.rtkc.launch(config_name) if res < 0: print("rtkrcv launch failed") elif res == 1: print("rtkrcv launch successful") self.state = "rover" elif res == 2: print("rtkrcv already launched") self.state = "rover" self.saveState() if self.enable_led: self.updateLED() print("Rover mode launched") self.semaphore.release() return res def shutdownRover(self): self.stopRover() self.semaphore.acquire() print("Attempting rtkrcv shutdown") res = self.rtkc.shutdown() if res < 0: print("rtkrcv shutdown failed") elif res == 1: print("rtkrcv shutdown successful") self.state = "inactive" elif res == 2: print("rtkrcv already shutdown") self.state = "inactive" self.saveState() if self.enable_led: self.updateLED() print("Rover mode shutdown") self.semaphore.release() return res def startRover(self): self.semaphore.acquire() print("Attempting to start rtkrcv...") res = self.rtkc.start() if res == -1: print("rtkrcv start failed") elif res == 1: print("rtkrcv start successful") print("Starting coordinate and satellite broadcast") elif res == 2: print("rtkrcv already started") # start fresh data broadcast self.server_not_interrupted = True if self.satellite_thread is None: self.satellite_thread = Thread(target = self.broadcastSatellites) self.satellite_thread.start() if self.coordinate_thread is None: self.coordinate_thread = Thread(target = self.broadcastCoordinates) self.coordinate_thread.start() self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def stopRover(self): self.semaphore.acquire() print("Attempting to stop RTKLIB...") res = self.rtkc.stop() if res == -1: print("rtkrcv stop failed") elif res == 1: print("rtkrcv stop successful") elif res == 2: print("rtkrcv already stopped") self.server_not_interrupted = False if self.satellite_thread is not None: self.satellite_thread.join() self.satellite_thread = None if self.coordinate_thread is not None: self.coordinate_thread.join() self.coordinate_thread = None self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def launchBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this launchBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.semaphore.acquire() self.state = "base" self.saveState() if self.enable_led: self.updateLED() print("Base mode launched") self.semaphore.release() def shutdownBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this shutdownBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.stopBase() self.semaphore.acquire() self.state = "inactive" self.saveState() if self.enable_led: self.updateLED() print("Base mode shutdown") self.semaphore.release() def startBase(self, rtcm3_messages = None, base_position = None, gps_cmd_file = None): self.semaphore.acquire() print("Attempting to start str2str...") if not self.rtkc.started: res = self.s2sc.start(rtcm3_messages, base_position, gps_cmd_file) if res < 0: print("str2str start failed") elif res == 1: print("str2str start successful") elif res == 2: print("str2str already started") else: print("Can't start str2str with rtkrcv still running!!!!") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def stopBase(self): self.semaphore.acquire() print("Attempting to stop str2str...") res = self.s2sc.stop() if res == -1: print("str2str stop failed") elif res == 1: print("str2str stop successful") elif res == 2: print("str2str already stopped") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def readConfigBase(self): self.semaphore.acquire() print("Got signal to read base config") self.socketio.emit("current config base", self.s2sc.readConfig(), namespace = "/test") self.semaphore.release() def writeConfigBase(self, config): self.semaphore.acquire() print("Got signal to write base config") self.s2sc.writeConfig(config) print("Restarting str2str...") res = self.s2sc.stop() + self.s2sc.start() if res > 1: print("Restart successful") else: print("Restart failed") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def writeConfigRover(self, config): # config dict must include config_name field self.semaphore.acquire() if "config_file_name" not in config: config_file = None else: config_file = config["config_file_name"] print("Got signal to write rover config to file " + config_file) self.conm.writeConfig(config_file, config) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") self.semaphore.release() def loadConfigRover(self, config_file = None): # we might want to write the config, but dont need to load it every time self.semaphore.acquire() if config_file == None: config_file == self.conm.default_rover_config print("Loading config " + config_file) # loading config to rtkrcv if self.rtkc.loadConfig(config_file) < 0: print("ERROR: failed to load config!!!") print("abort load") self.semaphore.release() return -1 print("load successful!") print("Now we need to restart rtkrcv for the changes to take effect") if self.rtkc.started: print("rtkrcv is already started, we need to do a simple restart!") res = self.rtkc.restart() if res == 3: print("Restart successful") print(config_file + " config loaded") elif res == 1: print("rtkrcv started instead of restart") elif res < 1: print("ERROR: rtkrcv restart failed") self.semaphore.release() self.saveState() return res else: print("We were not started before, so we need to perform a full start") self.semaphore.release() return self.startRover() def readConfigRover(self, config): self.semaphore.acquire() if "config_file_name" not in config: config_file = None else: config_file = config["config_file_name"] print("Got signal to read the rover config by the name " + str(config_file)) print("Sending rover config " + str(config_file)) # read from file self.conm.readConfig(config_file) # send to the browser self.socketio.emit("current config rover", self.conm.buffered_config.items, namespace="/test") self.semaphore.release() def shutdown(self): # shutdown whatever mode we are in. stop broadcast threads # clean up broadcast and blink threads self.server_not_interrupted = False self.led.blinker_not_interrupted = False if self.coordinate_thread is not None: self.coordinate_thread.join() if self.satellite_thread is not None: self.satellite_thread.join() if self.led.blinker_thread is not None: self.led.blinker_thread.join() # shutdown base or rover if self.state == "rover": return self.shutdownRover() elif self.state == "base": return self.shutdownBase() # otherwise, we are inactive return 1 def deleteConfig(self, config_name): # pass deleteConfig to conm print("Got signal to delete config " + config_name) self.conm.deleteConfig(config_name) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") print(self.conm.available_configs) def resetConfigToDefault(self, config_name): # pass reset config to conm print("Got signal to reset config " + config_name) self.conm.resetConfigToDefault(config_name) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") print(self.conm.available_configs) def cancelLogConversion(self, raw_log_path): if self.logm.log_being_converted: print("Canceling log conversion for " + raw_log_path) self.logm.convbin.child.kill(signal.SIGINT) self.conversion_thread.join() self.logm.convbin.child.close(force = True) print("Thread killed") self.logm.cleanLogFiles(raw_log_path) self.logm.log_being_converted = "" print("Canceled msg sent") def processLogPackage(self, raw_log_path): currently_converting = False try: print("conversion thread is alive " + str(self.conversion_thread.isAlive())) currently_converting = self.conversion_thread.isAlive() except AttributeError: pass log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" can_send_file = True # can't send if there is no converted package and we are busy if (not os.path.isfile(potential_zip_path)) and (currently_converting): can_send_file = False if can_send_file: print("Starting a new bg conversion thread for log " + raw_log_path) self.logm.log_being_converted = raw_log_path self.conversion_thread = Thread(target = self.getRINEXPackage, args = (raw_log_path, )) self.conversion_thread.start() else: error_msg = { "name": os.path.basename(raw_log_path), "conversion_status": "A log is being converted at the moment. Please wait", "messages_parsed": "" } self.socketio.emit("log conversion failed", error_msg, namespace="/test") def conversionIsRequired(self, raw_log_path): log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" print("Comparing " + raw_log_path + " and " + potential_zip_path + " for conversion") if os.path.isfile(potential_zip_path): print("Raw logs differ " + str(self.rawLogsDiffer(raw_log_path, potential_zip_path))) return self.rawLogsDiffer(raw_log_path, potential_zip_path) else: print("No zip file!!! Conversion required") return True def rawLogsDiffer(self, raw_log_path, zip_package_path): # check if the raw log is the same size in the zip and in filesystem log_name = os.path.basename(raw_log_path) raw_log_size = os.path.getsize(raw_log_path) zip_package = zipfile.ZipFile(zip_package_path) raw_file_inside_info = zip_package.getinfo("Raw/" + log_name) raw_file_inside_size = raw_file_inside_info.file_size print("Sizes:") print("Inside: " + str(raw_file_inside_size)) print("Raw: " + str(raw_log_size)) if raw_log_size == raw_file_inside_size: return False else: return True def getRINEXPackage(self, raw_log_path): # if this is a solution log, return the file right away if "sol" in raw_log_path: log_url_tail = "/logs/download/" + os.path.basename(raw_log_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") return raw_log_path # return RINEX package if it already exists # create one if not log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" result_path = "" if self.conversionIsRequired(raw_log_path): print("Conversion is Required!") result_path = self.createRINEXPackage(raw_log_path) # handle canceled conversion if result_path is None: log_url_tail = "/logs/download/" + os.path.basename(raw_log_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") return None else: result_path = potential_zip_path print("Conversion is not Required!") already_converted_package = { "name": log_filename, "conversion_status": "Log already converted. Details inside the package", "messages_parsed": "" } self.socketio.emit("log conversion results", already_converted_package, namespace="/test") log_url_tail = "/logs/download/" + os.path.basename(result_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") self.cleanBusyMessages() self.logm.log_being_converted = "" return result_path def cleanBusyMessages(self): # if user tried to convert other logs during conversion, he got an error message # this function clears them to show it's ok to convert again self.socketio.emit("clean busy messages", {}, namespace="/test") def createRINEXPackage(self, raw_log_path): # create a RINEX package before download # in case we fail to convert, return the raw log path back result = raw_log_path log_filename = os.path.basename(raw_log_path) conversion_time_string = self.logm.calculateConversionTime(raw_log_path) start_package = { "name": log_filename, "conversion_time": conversion_time_string } conversion_result_package = { "name": log_filename, "conversion_status": "", "messages_parsed": "", "log_url_tail": "" } self.socketio.emit("log conversion start", start_package, namespace="/test") try: log = self.logm.convbin.convertRTKLIBLogToRINEX(raw_log_path, self.logm.getRINEXVersion()) except ValueError, IndexError: print("Conversion canceled") conversion_result_package["conversion_status"] = "Conversion canceled, downloading raw log" self.socketio.emit("log conversion results", conversion_result_package, namespace="/test") return None print("Log conversion done!") if log is not None: result = log.createLogPackage() if log.isValid(): conversion_result_package["conversion_status"] = "Log converted to RINEX" conversion_result_package["messages_parsed"] = log.log_metadata.formValidMessagesString() else: conversion_result_package["conversion_status"] = "Conversion successful, but log does not contain any useful data. Downloading raw log" else: print("Could not convert log. Is the extension wrong?") conversion_result_package["conversion_status"] = "Log conversion failed. Downloading raw log" self.socketio.emit("log conversion results", conversion_result_package, namespace="/test") print("Log conversion results:") print(str(log)) return result
class RTKLIB: # we will save RTKLIB state here for later loading state_file = os.path.join(os.path.expanduser("~"), ".reach/rtk_state") # if the state file is not available, these settings are loaded default_state = { "started": "no", "state": "base" } def __init__(self, socketio, rtklib_path = None, config_path=None, enable_led = True, log_path = None): print("RTKLIB 1") print(rtklib_path) print(log_path) if rtklib_path is None: rtklib_path = os.path.join(os.path.expanduser("~"), "RTKLIB") if config_path is None: self.config_path = os.path.join(os.path.dirname(__file__), "rtklib_configs") else: self.config_path = config_path if log_path is None: #TODO find a better default location self.log_path = "../data" else: self.log_path = log_path # This value should stay below the timeout value or the Satellite/Coordinate broadcast # thread will stop self.sleep_count = 0 # default state for RTKLIB is "rover single" self.state = "base" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle rover mode self.rtkc = RtkController(rtklib_path, self.config_path) self.conm = ConfigManager(rtklib_path, self.config_path) # this one handles base settings self.s2sc = Str2StrController(rtklib_path) # take care of serving logs self.logm = LogManager(rtklib_path, self.log_path) # basic synchronisation to prevent errors self.semaphore = Semaphore() # we need this to send led signals # self.enable_led = enable_led # if self.enable_led: # self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None self.conversion_thread = None self.system_time_correct = False # self.system_time_correct = True self.time_thread = Thread(target = self.setCorrectTime) self.time_thread.start() # we try to restore previous state # in case we can't, we start as rover in single mode # self.loadState() def setCorrectTime(self): # This method is partially disabled in RTKBase as chrony is responsible # for syncing time and date. # determine if we have ntp service ready or we need gps time #print("RTKLIB 2 GPS time sync") ## if not gps_time.time_synchronised_by_ntp(): # wait for gps time ## print("Time is not synced by NTP") # self.updateLED("orange,off") # gps_time.set_gps_time("/dev/ttyACM0", 115200) #print("Time is synced by GPS!") self.system_time_correct = True self.socketio.emit("system time corrected", {}, namespace="/test") self.loadState() self.socketio.emit("system state reloaded", {}, namespace="/test") def launchBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this launchBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.semaphore.acquire() self.state = "base" #self.saveState() # if self.enable_led: # self.updateLED() print("RTKLIB 7 Base mode launched") self.semaphore.release() def shutdownBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this shutdownBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.stopBase() self.semaphore.acquire() self.state = "inactive" print("RTKLIB 8 Base mode shutdown") self.semaphore.release() def startBase(self, rtcm3_messages = None, base_position = None, gps_cmd_file = None): self.semaphore.acquire() """ print("RTKLIB 9 Attempting to start str2str...") res = self.s2sc.start(rtcm3_messages, base_position, gps_cmd_file) if res < 0: print("str2str start failed") elif res == 1: print("str2str start successful") elif res == 2: print("str2str already started") self.saveState() """ #TODO need refactoring #maybe a new method to launch/start rtkrcv outside #startBase and startRover #TODO launchRover and startRover send a config_name to rtkc #I don't do this here :-/ print("RTKLIB 9a Attempting to launch rtkrcv...") res2 = self.rtkc.launch() if res2 < 0: print("rtkrcv launch failed") elif res2 == 1: print("rtkrcv launch successful") elif res2 == 2: print("rtkrcv already launched") #TODO need refactoring #maybe a new method to launch/start rtkrcv outside #startBase and startRover print("RTKLIB 9b Attempting to start rtkrcv...") res3 = self.rtkc.start() if res3 == -1: print("rtkrcv start failed") elif res3 == 1: print("rtkrcv start successful") print("Starting coordinate and satellite broadcast") elif res3 == 2: print("rtkrcv already started") # start fresh data broadcast #TODO the satellite and coordinate broadcast start #when rtkrcv start failed self.server_not_interrupted = True if self.satellite_thread is None: self.satellite_thread = Thread(target = self.broadcastSatellites) self.satellite_thread.start() if self.coordinate_thread is None: self.coordinate_thread = Thread(target = self.broadcastCoordinates) self.coordinate_thread.start() self.semaphore.release() return res3 def stopBase(self): self.semaphore.acquire() print("RTKLIB 10a Attempting to stop rtkrcv...") res2 = self.rtkc.stop() if res2 == -1: print("rtkrcv stop failed") elif res2 == 1: print("rtkrcv stop successful") elif res2 == 2: print("rtkrcv already stopped") print("RTKLIB 10b Attempting to stop satellite broadcasting...") self.server_not_interrupted = False if self.satellite_thread is not None: self.satellite_thread.join() self.satellite_thread = None if self.coordinate_thread is not None: self.coordinate_thread.join() self.coordinate_thread = None print("RTKLIB 10c Attempting rtkrcv shutdown") res = self.rtkc.shutdown() if res < 0: print("rtkrcv shutdown failed") elif res == 1: print("rtkrcv shutdown successful") self.state = "inactive" elif res == 2: print("rtkrcv already shutdown") self.state = "inactive" self.semaphore.release() return res def readConfigBase(self): self.semaphore.acquire() print("RTKLIB 11 Got signal to read base config") self.socketio.emit("current config base", self.s2sc.readConfig(), namespace = "/test") self.semaphore.release() def writeConfigBase(self, config): self.semaphore.acquire() print("RTKLIB 12 Got signal to write base config") self.s2sc.writeConfig(config) print("Restarting str2str...") res = self.s2sc.stop() + self.s2sc.start() if res > 1: print("Restart successful") else: print("Restart failed") self.saveState() # if self.enable_led: # self.updateLED() self.semaphore.release() return res def shutdown(self): # shutdown whatever mode we are in. stop broadcast threads print("RTKLIB 17 Shutting down") # clean up broadcast and blink threads self.server_not_interrupted = False # self.led.blinker_not_interrupted = False if self.coordinate_thread is not None: self.coordinate_thread.join() if self.satellite_thread is not None: self.satellite_thread.join() # if self.led.blinker_thread is not None: # self.led.blinker_thread.join() # shutdown base elif self.state == "base": return self.shutdownBase() # otherwise, we are inactive return 1 def deleteConfig(self, config_name): # pass deleteConfig to conm print("RTKLIB 18 Got signal to delete config " + config_name) self.conm.deleteConfig(config_name) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") print(self.conm.available_configs) def cancelLogConversion(self, raw_log_path): if self.logm.log_being_converted: print("Canceling log conversion for " + raw_log_path) self.logm.convbin.child.kill(signal.SIGINT) self.conversion_thread.join() self.logm.convbin.child.close(force = True) print("Thread killed") self.logm.cleanLogFiles(raw_log_path) self.logm.log_being_converted = "" print("Canceled msg sent") def processLogPackage(self, raw_log_path): currently_converting = False try: print("conversion thread is alive " + str(self.conversion_thread.isAlive())) currently_converting = self.conversion_thread.isAlive() except AttributeError: pass log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" can_send_file = True # can't send if there is no converted package and we are busy if (not os.path.isfile(potential_zip_path)) and (currently_converting): can_send_file = False if can_send_file: print("Starting a new bg conversion thread for log " + raw_log_path) self.logm.log_being_converted = raw_log_path self.conversion_thread = Thread(target = self.getRINEXPackage, args = (raw_log_path, )) self.conversion_thread.start() else: error_msg = { "name": os.path.basename(raw_log_path), "conversion_status": "A log is being converted at the moment. Please wait", "messages_parsed": "" } self.socketio.emit("log conversion failed", error_msg, namespace="/test") def conversionIsRequired(self, raw_log_path): log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" print("Comparing " + raw_log_path + " and " + potential_zip_path + " for conversion") if os.path.isfile(potential_zip_path): print("Raw logs differ " + str(self.rawLogsDiffer(raw_log_path, potential_zip_path))) return self.rawLogsDiffer(raw_log_path, potential_zip_path) else: print("No zip file!!! Conversion required") return True def rawLogsDiffer(self, raw_log_path, zip_package_path): # check if the raw log is the same size in the zip and in filesystem log_name = os.path.basename(raw_log_path) raw_log_size = os.path.getsize(raw_log_path) zip_package = zipfile.ZipFile(zip_package_path) raw_file_inside_info = zip_package.getinfo("Raw/" + log_name) raw_file_inside_size = raw_file_inside_info.file_size print("Sizes:") print("Inside: " + str(raw_file_inside_size)) print("Raw: " + str(raw_log_size)) if raw_log_size == raw_file_inside_size: return False else: return True def getRINEXPackage(self, raw_log_path): # if this is a solution log, return the file right away if "sol" in raw_log_path: log_url_tail = "/logs/download/" + os.path.basename(raw_log_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") return raw_log_path # return RINEX package if it already exists # create one if not log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" result_path = "" if self.conversionIsRequired(raw_log_path): print("Conversion is Required!") result_path = self.createRINEXPackage(raw_log_path) # handle canceled conversion if result_path is None: log_url_tail = "/logs/download/" + os.path.basename(raw_log_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") return None else: result_path = potential_zip_path print("Conversion is not Required!") already_converted_package = { "name": log_filename, "conversion_status": "Log already converted. Details inside the package", "messages_parsed": "" } self.socketio.emit("log conversion results", already_converted_package, namespace="/test") log_url_tail = "/logs/download/" + os.path.basename(result_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") self.cleanBusyMessages() self.logm.log_being_converted = "" return result_path def cleanBusyMessages(self): # if user tried to convert other logs during conversion, he got an error message # this function clears them to show it's ok to convert again self.socketio.emit("clean busy messages", {}, namespace="/test") def createRINEXPackage(self, raw_log_path): # create a RINEX package before download # in case we fail to convert, return the raw log path back result = raw_log_path log_filename = os.path.basename(raw_log_path) conversion_time_string = self.logm.calculateConversionTime(raw_log_path) start_package = { "name": log_filename, "conversion_time": conversion_time_string } conversion_result_package = { "name": log_filename, "conversion_status": "", "messages_parsed": "", "log_url_tail": "" } self.socketio.emit("log conversion start", start_package, namespace="/test") try: log = self.logm.convbin.convertRTKLIBLogToRINEX(raw_log_path, self.logm.getRINEXVersion()) except (ValueError, IndexError): print("Conversion canceled") conversion_result_package["conversion_status"] = "Conversion canceled, downloading raw log" self.socketio.emit("log conversion results", conversion_result_package, namespace="/test") return None print("Log conversion done!") if log is not None: result = log.createLogPackage() if log.isValid(): conversion_result_package["conversion_status"] = "Log converted to RINEX" conversion_result_package["messages_parsed"] = log.log_metadata.formValidMessagesString() else: conversion_result_package["conversion_status"] = "Conversion successful, but log does not contain any useful data. Downloading raw log" else: print("Could not convert log. Is the extension wrong?") conversion_result_package["conversion_status"] = "Log conversion failed. Downloading raw log" self.socketio.emit("log conversion results", conversion_result_package, namespace="/test") print("Log conversion results:") print(str(log)) return result def saveState(self): # save current state for future resurrection: # state is a list of parameters: # rover state example: ["rover", "started", "reach_single_default.conf"] # base state example: ["base", "stopped", "input_stream", "output_stream"] state = {} # save "rover", "base" or "inactive" state state["state"] = self.state if self.state == "rover": started = self.rtkc.started elif self.state == "base": started = self.s2sc.started elif self.state == "inactive": started = False state["started"] = "yes" if started else "no" # dump rover state state["rover"] = {"current_config": self.rtkc.current_config} # dump rover state state["base"] = { "input_stream": self.s2sc.input_stream, "output_stream": self.s2sc.output_stream, "rtcm3_messages": self.s2sc.rtcm3_messages, "base_position": self.s2sc.base_position, "gps_cmd_file": self.s2sc.gps_cmd_file } print("RTKLIB 20 DEBUG saving state") print(str(state)) with open(self.state_file, "w") as f: json.dump(state, f, sort_keys = True, indent = 4) reach_tools.run_command_safely(["sync"]) return state def byteify(self, input): # thanks to Mark Amery from StackOverflow for this awesome function if isinstance(input, dict): return {self.byteify(key): self.byteify(value) for key, value in input.items()} elif isinstance(input, list): return [self.byteify(element) for element in input] elif isinstance(input, str): #no need to convert to utf-8 anymore with Python v3.x #return input.encode('utf-8') return input else: return input def getState(self): # get the state, currently saved in the state file print("RTKLIB 21 Trying to read previously saved state...") try: f = open(self.state_file, "r") except IOError: # can't find the file, let's create a new one with default state print("Could not find existing state, Launching default mode...") return self.default_state else: print("Found existing state...trying to decode...") try: json_state = json.load(f) except ValueError: # could not properly decode current state print("Could not decode json state. Launching default mode...") f.close() return self.default_state else: print("Decoding succesful") f.close() # convert unicode strings to normal json_state = self.byteify(json_state) print("That's what we found:") print(str(json_state)) return json_state def loadState(self): # get current state json_state = self.getState() print("RTKLIB 22 Now loading the state printed above... ") #print(str(json_state)) # first, we restore the base state, because no matter what we end up doing, # we need to restore base state if json_state["state"] == "base": self.launchBase() if json_state["started"] == "yes": self.startBase() print(str(json_state["state"]) + " state loaded") def sendState(self): # send current state to every connecting browser state = self.getState() print("RTKLIB 22a") #print(str(state)) self.conm.updateAvailableConfigs() state["available_configs"] = self.conm.available_configs state["system_time_correct"] = self.system_time_correct state["log_path"] = str(self.log_path) print("Available configs to send: ") print(str(state["available_configs"])) print("Full state: ") for key in state: print("{} : {}".format(key, state[key])) self.socketio.emit("current state", state, namespace = "/test") # this function reads satellite levels from an existing rtkrcv instance # and emits them to the connected browser as messages def broadcastSatellites(self): count = 0 while self.server_not_interrupted: # update satellite levels self.rtkc.getObs() self.socketio.emit("observations broadcast", self.rtkc.obs, namespace = "/test") self.rtkc.getSat() self.socketio.emit("satellites broadcast", self.rtkc.sat, namespace = "/test") count += 1 self.sleep_count +=1 time.sleep(1) #print("exiting satellite broadcast") # this function reads current rtklib status, coordinates and obs count def broadcastCoordinates(self): count = 0 while self.server_not_interrupted: # update RTKLIB status self.rtkc.getStatus() # if count % 10 == 0: # print("Sending RTKLIB status select information:") # print(self.rtkc.status) self.socketio.emit("coordinate broadcast", self.rtkc.status, namespace = "/test") # if self.enable_led: # self.updateLED() count += 1 time.sleep(1)
class RTKLIB: # we will save RTKLIB state here for later loading state_file = "/home/reach/.reach/rtk_state" def __init__(self, socketio, enable_led = True, path_to_rtkrcv = None, path_to_configs = None, path_to_str2str = None, path_to_gps_cmd_file = None): # default state for RTKLIB is "inactive" self.state = "inactive" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle base mode self.rtkc = RtkController(path_to_rtkrcv) self.conm = ConfigManager(path_to_configs) # this one handles base settings self.s2sc = Str2StrController(path_to_str2str, path_to_gps_cmd_file) # basic synchronisation to prevent errors self.saveState() self.semaphore = Semaphore() # we need this to send led signals self.enable_led = enable_led if self.enable_led: self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None # we try to restore previous state self.loadState() def launchRover(self, config_name = None): # config_name may be a name, or a full path # if the parameter contains "/", then we consider it a full path # else, we will be looking for it one directory higher than the rtkrcv bin self.semaphore.acquire() print("Attempting to launch rtkrcv...") if config_name == None: res = self.rtkc.launch() else: res = self.rtkc.launch(config_name) if res < 0: print("rtkrcv launch failed") elif res == 1: print("rtkrcv launch successful") self.state = "rover" elif res == 2: print("rtkrcv already launched") self.state = "rover" self.saveState() if self.enable_led: self.updateLED() print("Rover mode launched") self.semaphore.release() return res def shutdownRover(self): self.stopRover() self.semaphore.acquire() print("Attempting rtkrcv shutdown") res = self.rtkc.shutdown() if res < 0: print("rtkrcv shutdown failed") elif res == 1: print("rtkrcv shutdown successful") self.state = "inactive" elif res == 2: print("rtkrcv already shutdown") self.state = "inactive" self.saveState() if self.enable_led: self.updateLED() print("Rover mode shutdown") self.semaphore.release() return res def startRover(self): self.semaphore.acquire() print("Attempting to start rtkrcv...") res = self.rtkc.start() if res == -1: print("rtkrcv start failed") elif res == 1: print("rtkrcv start successful") print("Starting coordinate and satellite broadcast") elif res == 2: print("rtkrcv already started") # start fresh data broadcast self.server_not_interrupted = True if self.satellite_thread is None: self.satellite_thread = Thread(target = self.broadcastSatellites) self.satellite_thread.start() if self.coordinate_thread is None: self.coordinate_thread = Thread(target = self.broadcastCoordinates) self.coordinate_thread.start() self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def stopRover(self): self.semaphore.acquire() print("Attempting to stop RTKLIB...") res = self.rtkc.stop() if res == -1: print("rtkrcv stop failed") elif res == 1: print("rtkrcv stop successful") elif res == 2: print("rtkrcv already stopped") self.server_not_interrupted = False if self.satellite_thread is not None: self.satellite_thread.join() self.satellite_thread = None if self.coordinate_thread is not None: self.coordinate_thread.join() self.coordinate_thread = None self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def launchBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this launchBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.semaphore.acquire() self.state = "base" self.saveState() if self.enable_led: self.updateLED() print("Base mode launched") self.semaphore.release() def shutdownBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this shutdownBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.stopBase() self.semaphore.acquire() self.state = "inactive" self.saveState() if self.enable_led: self.updateLED() print("Base mode shutdown") self.semaphore.release() def startBase(self, rtcm3_messages = None, base_position = None, gps_cmd_file = None): self.semaphore.acquire() print("Attempting to start str2str...") if not self.rtkc.started: res = self.s2sc.start(rtcm3_messages, base_position, gps_cmd_file) if res < 0: print("str2str start failed") elif res == 1: print("str2str start successful") elif res == 2: print("str2str already started") else: print("Can't start str2str with rtkrcv still running!!!!") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def stopBase(self): self.semaphore.acquire() print("Attempting to stop str2str...") res = self.s2sc.stop() if res == -1: print("str2str stop failed") elif res == 1: print("str2str stop successful") elif res == 2: print("str2str already stopped") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def readConfigBase(self): self.semaphore.acquire() print("Got signal to read current base config") self.socketio.emit("current config base", self.s2sc.readConfig(), namespace = "/test") self.semaphore.release() def writeConfigBase(self, config): self.semaphore.acquire() print("Got signal to write current base config") self.s2sc.writeConfig(config) print("Restarting str2str...") res = self.s2sc.stop() + self.s2sc.start() if res > 1: print("Restart successful") else: print("Restart failed") self.saveState() self.semaphore.release() return res def writeConfigRover(self, config): # config dict must include config_name field self.semaphore.acquire() print("Got signal to write current rover config") if "config_file_name" not in config: config_file = None else: config_file = config["config_file_name"] self.conm.writeConfig(config_file, config) print("Reloading with new config...") res = self.rtkc.loadConfig(self.rtkc.current_config) + self.rtkc.restart() if res == 2: print("Restart successful") elif res == 1: print("rtkrcv started instead of restart") elif res == -1: print("rtkrcv restart failed") self.saveState() self.semaphore.release() return res def readConfigRover(self, config): self.semaphore.acquire() if "config_file_name" not in config: config_file = None else: config_file = config["config_file_name"] print("Got signal to read the current rover config") self.conm.readConfig(config_file) # after this, to preserve the order of the options in the frontend we send a special order message print("Sending rover config order") options_order = {} # create a structure the corresponds to the options order for index, value in enumerate(self.conm.buff_dict_order): options_order[str(index)] = value # send the options order self.socketio.emit("current config rover order", options_order, namespace="/test") # send the options comments print("Sending rover config comments") self.socketio.emit("current config rover comments", self.conm.buff_dict_comments, namespace="/test") # now we send the whole config with values print("Sending rover config values") self.socketio.emit("current config rover", self.conm.buff_dict, namespace="/test") self.semaphore.release() def saveState(self): # save current state for future resurrection: # state is a list of parameters: # rover state example: ["rover", "started", "reach_single_default.conf"] # base state example: ["base", "stopped", "input_stream", "output_stream"] state = {} # save "rover", "base" or "inactive" state state["state"] = self.state if self.state == "rover": started = self.rtkc.started elif self.state == "base": started = self.s2sc.started elif self.state == "inactive": started = False state["started"] = "yes" if started else "no" # dump rover state state["rover"] = {"current_config": self.rtkc.current_config} # dump rover state state["base"] = { "input_stream": self.s2sc.input_stream, "output_stream": self.s2sc.output_stream, "rtcm3_messages": self.s2sc.rtcm3_messages, "base_position": self.s2sc.base_position, "gps_cmd_file": self.s2sc.gps_cmd_file } with open(self.state_file, "w") as f: json.dump(state, f, sort_keys = True, indent = 4) return state def byteify(self, input): # thanks to Mark Amery from StackOverFlow for this awesome function if isinstance(input, dict): return {self.byteify(key): self.byteify(value) for key, value in input.iteritems()} elif isinstance(input, list): return [self.byteify(element) for element in input] elif isinstance(input, unicode): return input.encode('utf-8') else: return input def readState(self): # load previously saved state # we assume this function is not to be run until the very end of __init__ of RTKLIB print("Trying to load previously saved state...") try: f = open(self.state_file, "r") except IOError: # can't find the file, let's create a new one with default state print("Could not find existing state, saving default...") self.saveState() return -1 else: print("Found existing state...") json_state = json.load(f) f.close() # convert unicode strings to normal json_state = self.byteify(json_state) return json_state def loadState(self): # load previously saved state # we assume this function is not to be run until the very end of __init__ of RTKLIB print("Trying to load previously saved state...") try: f = open(self.state_file, "r") except IOError: # can't find the file, let's create a new one with default state print("Could not find existing state, saving default...") return self.saveState() else: print("Found existing state...") json_state = json.load(f) f.close() # convert unicode strings to normal json_state = self.byteify(json_state) # first, we restore the base state, because no matter what we end up doing, # we need to restore base state self.s2sc.input_stream = json_state["base"]["input_stream"] self.s2sc.output_stream = json_state["base"]["output_stream"] self.s2sc.rtcm3_messages = json_state["base"]["rtcm3_messages"] self.s2sc.base_position = json_state["base"]["base_position"] self.s2sc.gps_cmd_file = json_state["base"]["gps_cmd_file"] if json_state["state"] == "rover": saved_config = json_state["rover"]["current_config"] if saved_config == "": saved_config = None self.launchRover(saved_config) if json_state["started"] == "yes": self.startRover() elif json_state["state"] == "base": self.launchBase() if json_state["started"] == "yes": self.startBase() print("State loaded") return json_state # if we are "inactive", don't do anything as this the default state def sendState(self): # send current state to every connecting browser state = self.loadState() self.socketio.emit("current state", state, namespace = "/test") def updateLED(self): blink_pattern = "" delay = 0.5 if self.state == "base": if self.s2sc.started: # we have a started base blink_pattern = "green,off,magenta,off" else: # we have a stopped base blink_pattern = "red,off,magenta,off" elif self.state == "rover": if self.rtkc.started: # we have a started rover status_pattern_dict = { "fix": "blue,off,green,off", "float": "blue,off,green,off,yellow,off", "single": "blue,off,yellow,off" } # we need to acquire RtkController in case it's currently updating info dict self.rtkc.semaphore.acquire() current_rover_solutuon_status = self.rtkc.info.get("solution_status", "") self.rtkc.semaphore.release() blink_pattern = status_pattern_dict.get(current_rover_solutuon_status, "blue,off") else: # we have a stopped rover blink_pattern = "blue,off,red,off" elif self.state == "inactive": blink_pattern = "yellow, off" delay = 1 if blink_pattern: # check blink_pattern contains something new if blink_pattern != self.led.current_blink_pattern: # if we decided we need a new pattern, then start blinking it self.led.startBlinker(blink_pattern, delay) # thread workers for broadcasing rtkrcv status # this function reads satellite levels from an exisiting rtkrcv instance # and emits them to the connected browser as messages def broadcastSatellites(self): count = 0 while self.server_not_interrupted: # update satellite levels self.rtkc.getObs() if count % 10 == 0: print("Sending sat rover levels:\n" + str(self.rtkc.obs_rover)) print("Sending sat base levels:\n" + str(self.rtkc.obs_base)) self.socketio.emit("satellite broadcast rover", self.rtkc.obs_rover, namespace = "/test") self.socketio.emit("satellite broadcast base", self.rtkc.obs_base, namespace = "/test") count += 1 time.sleep(1) # this function reads current rtklib status, coordinates and obs count def broadcastCoordinates(self): count = 0 while self.server_not_interrupted: # update RTKLIB status self.rtkc.getStatus() if count % 10 == 0: print("Sending RTKLIB status select information:") print(self.rtkc.info) self.socketio.emit("coordinate broadcast", self.rtkc.info, namespace = "/test") if self.enable_led: self.updateLED() count += 1 time.sleep(1)
class RTKLIB: # we will save RTKLIB state here for later loading state_file = "/home/pi/.reach/rtk_state" # if the state file is not available, these settings are loaded default_state = { "base": { "base_position": [], "gps_cmd_file": "GPS_10Hz.cmd", "input_stream": "serial://ttyAMA0:230400:8:n:1:off#ubx", "output_stream": "tcpsvr://:9000#rtcm3", "rtcm3_messages": [ "1002", "1006", "1008", "1010", "1019", "1020" ] }, "rover": { "current_config": "reach_single_default.conf" }, "started": "no", "state": "rover" } def __init__(self, socketio, rtklib_path = None, enable_led = True, log_path = None): if rtklib_path is None: rtklib_path = "/home/pi/RTKLIB" if log_path is None: log_path = "/home/pi/logs" # default state for RTKLIB is "rover single" self.state = "rover" # we need this to broadcast stuff self.socketio = socketio # these are necessary to handle rover mode self.rtkc = RtkController(rtklib_path) self.conm = ConfigManager(rtklib_path) # this one handles base settings self.s2sc = Str2StrController(rtklib_path) # take care of serving logs self.logm = LogManager(rtklib_path, log_path) # basic synchronisation to prevent errors self.semaphore = Semaphore() # we need this to send led signals self.enable_led = enable_led if self.enable_led: self.led = ReachLED() # broadcast satellite levels and status with these self.server_not_interrupted = True self.satellite_thread = None self.coordinate_thread = None self.conversion_thread = None self.system_time_correct = False self.time_thread = Thread(target = self.setCorrectTime) self.time_thread.start() # we try to restore previous state # in case we can't, we start as rover in single mode # self.loadState() def setCorrectTime(self): # determine if we have ntp service ready or we need gps time if not gps_time.time_synchronised_by_ntp(): # wait for gps time print("Time is not synced by NTP") self.updateLED("orange,off") gps_time.set_gps_time("/dev/ttyAMA0", 230400) print("Time is synced by NTP!") self.system_time_correct = True self.socketio.emit("system time corrected", {}, namespace="/test") self.loadState() self.socketio.emit("system state reloaded", {}, namespace="/test") def launchRover(self, config_name = None): # config_name may be a name, or a full path # if the parameter contains "/", then we consider it a full path # else, we will be looking for it one directory higher than the rtkrcv bin self.semaphore.acquire() print("Attempting to launch rtkrcv...") if config_name == None: res = self.rtkc.launch() else: res = self.rtkc.launch(config_name) if res < 0: print("rtkrcv launch failed") elif res == 1: print("rtkrcv launch successful") self.state = "rover" elif res == 2: print("rtkrcv already launched") self.state = "rover" self.saveState() if self.enable_led: self.updateLED() print("Rover mode launched") self.semaphore.release() return res def shutdownRover(self): self.stopRover() self.semaphore.acquire() print("Attempting rtkrcv shutdown") res = self.rtkc.shutdown() if res < 0: print("rtkrcv shutdown failed") elif res == 1: print("rtkrcv shutdown successful") self.state = "inactive" elif res == 2: print("rtkrcv already shutdown") self.state = "inactive" self.saveState() if self.enable_led: self.updateLED() print("Rover mode shutdown") self.semaphore.release() return res def startRover(self): self.semaphore.acquire() print("Attempting to start rtkrcv...") res = self.rtkc.start() if res == -1: print("rtkrcv start failed") elif res == 1: print("rtkrcv start successful") print("Starting coordinate and satellite broadcast") elif res == 2: print("rtkrcv already started") # start fresh data broadcast self.server_not_interrupted = True if self.satellite_thread is None: self.satellite_thread = Thread(target = self.broadcastSatellites) self.satellite_thread.start() if self.coordinate_thread is None: self.coordinate_thread = Thread(target = self.broadcastCoordinates) self.coordinate_thread.start() self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def stopRover(self): self.semaphore.acquire() print("Attempting to stop RTKLIB...") res = self.rtkc.stop() if res == -1: print("rtkrcv stop failed") elif res == 1: print("rtkrcv stop successful") elif res == 2: print("rtkrcv already stopped") self.server_not_interrupted = False if self.satellite_thread is not None: self.satellite_thread.join() self.satellite_thread = None if self.coordinate_thread is not None: self.coordinate_thread.join() self.coordinate_thread = None self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def launchBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this launchBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.semaphore.acquire() self.state = "base" self.saveState() if self.enable_led: self.updateLED() print("Base mode launched") self.semaphore.release() def shutdownBase(self): # due to the way str2str works, we can't really separate launch and start # all the configuration goes to startBase() function # this shutdownBase() function exists to change the state of RTKLIB instance # and to make the process for base and rover modes similar self.stopBase() self.semaphore.acquire() self.state = "inactive" self.saveState() if self.enable_led: self.updateLED() print("Base mode shutdown") self.semaphore.release() def startBase(self, rtcm3_messages = None, base_position = None, gps_cmd_file = None): self.semaphore.acquire() print("Attempting to start str2str...") if not self.rtkc.started: res = self.s2sc.start(rtcm3_messages, base_position, gps_cmd_file) if res < 0: print("str2str start failed") elif res == 1: print("str2str start successful") elif res == 2: print("str2str already started") else: print("Can't start str2str with rtkrcv still running!!!!") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def stopBase(self): self.semaphore.acquire() print("Attempting to stop str2str...") res = self.s2sc.stop() if res == -1: print("str2str stop failed") elif res == 1: print("str2str stop successful") elif res == 2: print("str2str already stopped") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def readConfigBase(self): self.semaphore.acquire() print("Got signal to read base config") self.socketio.emit("current config base", self.s2sc.readConfig(), namespace = "/test") self.semaphore.release() def writeConfigBase(self, config): self.semaphore.acquire() print("Got signal to write base config") self.s2sc.writeConfig(config) print("Restarting str2str...") res = self.s2sc.stop() + self.s2sc.start() if res > 1: print("Restart successful") else: print("Restart failed") self.saveState() if self.enable_led: self.updateLED() self.semaphore.release() return res def writeConfigRover(self, config): # config dict must include config_name field self.semaphore.acquire() if "config_file_name" not in config: config_file = None else: config_file = config["config_file_name"] print("Got signal to write rover config to file " + config_file) self.conm.writeConfig(config_file, config) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") self.semaphore.release() def loadConfigRover(self, config_file = None): # we might want to write the config, but dont need to load it every time self.semaphore.acquire() if config_file == None: config_file == self.conm.default_rover_config print("Loading config " + config_file) # loading config to rtkrcv if self.rtkc.loadConfig(config_file) < 0: print("ERROR: failed to load config!!!") print("abort load") self.semaphore.release() return -1 print("load successful!") print("Now we need to restart rtkrcv for the changes to take effect") if self.rtkc.started: print("rtkrcv is already started, we need to do a simple restart!") res = self.rtkc.restart() if res == 3: print("Restart successful") print(config_file + " config loaded") elif res == 1: print("rtkrcv started instead of restart") elif res < 1: print("ERROR: rtkrcv restart failed") self.semaphore.release() self.saveState() return res else: print("We were not started before, so we need to perform a full start") self.semaphore.release() return self.startRover() def readConfigRover(self, config): self.semaphore.acquire() if "config_file_name" not in config: config_file = None else: config_file = config["config_file_name"] print("Got signal to read the rover config by the name " + str(config_file)) print("Sending rover config " + str(config_file)) # read from file self.conm.readConfig(config_file) # send to the browser self.socketio.emit("current config rover", self.conm.buffered_config.items, namespace="/test") self.semaphore.release() def shutdown(self): # shutdown whatever mode we are in. stop broadcast threads # clean up broadcast and blink threads self.server_not_interrupted = False self.led.blinker_not_interrupted = False if self.coordinate_thread is not None: self.coordinate_thread.join() if self.satellite_thread is not None: self.satellite_thread.join() if self.led.blinker_thread is not None: self.led.blinker_thread.join() # shutdown base or rover if self.state == "rover": return self.shutdownRover() elif self.state == "base": return self.shutdownBase() # otherwise, we are inactive return 1 def deleteConfig(self, config_name): # pass deleteConfig to conm print("Got signal to delete config " + config_name) self.conm.deleteConfig(config_name) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") print(self.conm.available_configs) def resetConfigToDefault(self, config_name): # pass reset config to conm print("Got signal to reset config " + config_name) self.conm.resetConfigToDefault(config_name) self.conm.updateAvailableConfigs() # send available configs to the browser self.socketio.emit("available configs", {"available_configs": self.conm.available_configs}, namespace="/test") print(self.conm.available_configs) def cancelLogConversion(self, raw_log_path): if self.logm.log_being_converted: print("Canceling log conversion for " + raw_log_path) self.logm.convbin.child.kill(signal.SIGINT) self.conversion_thread.join() self.logm.convbin.child.close(force = True) print("Thread killed") self.logm.cleanLogFiles(raw_log_path) self.logm.log_being_converted = "" print("Canceled msg sent") def processLogPackage(self, raw_log_path): currently_converting = False try: print("conversion thread is alive " + str(self.conversion_thread.isAlive())) currently_converting = self.conversion_thread.isAlive() except AttributeError: pass log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" can_send_file = True # can't send if there is no converted package and we are busy if (not os.path.isfile(potential_zip_path)) and (currently_converting): can_send_file = False if can_send_file: print("Starting a new bg conversion thread for log " + raw_log_path) self.logm.log_being_converted = raw_log_path self.conversion_thread = Thread(target = self.getRINEXPackage, args = (raw_log_path, )) self.conversion_thread.start() else: error_msg = { "name": os.path.basename(raw_log_path), "conversion_status": "A log is being converted at the moment. Please wait", "messages_parsed": "" } self.socketio.emit("log conversion failed", error_msg, namespace="/test") def conversionIsRequired(self, raw_log_path): log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" print("Comparing " + raw_log_path + " and " + potential_zip_path + " for conversion") if os.path.isfile(potential_zip_path): print("Raw logs differ " + str(self.rawLogsDiffer(raw_log_path, potential_zip_path))) return self.rawLogsDiffer(raw_log_path, potential_zip_path) else: print("No zip file!!! Conversion required") return True def rawLogsDiffer(self, raw_log_path, zip_package_path): # check if the raw log is the same size in the zip and in filesystem log_name = os.path.basename(raw_log_path) raw_log_size = os.path.getsize(raw_log_path) zip_package = zipfile.ZipFile(zip_package_path) raw_file_inside_info = zip_package.getinfo("Raw/" + log_name) raw_file_inside_size = raw_file_inside_info.file_size print("Sizes:") print("Inside: " + str(raw_file_inside_size)) print("Raw: " + str(raw_log_size)) if raw_log_size == raw_file_inside_size: return False else: return True def getRINEXPackage(self, raw_log_path): # if this is a solution log, return the file right away if "sol" in raw_log_path: log_url_tail = "/logs/download/" + os.path.basename(raw_log_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") return raw_log_path # return RINEX package if it already exists # create one if not log_filename = os.path.basename(raw_log_path) potential_zip_path = os.path.splitext(raw_log_path)[0] + ".zip" result_path = "" if self.conversionIsRequired(raw_log_path): print("Conversion is Required!") result_path = self.createRINEXPackage(raw_log_path) # handle canceled conversion if result_path is None: log_url_tail = "/logs/download/" + os.path.basename(raw_log_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") return None else: result_path = potential_zip_path print("Conversion is not Required!") already_converted_package = { "name": log_filename, "conversion_status": "Log already converted. Details inside the package", "messages_parsed": "" } self.socketio.emit("log conversion results", already_converted_package, namespace="/test") log_url_tail = "/logs/download/" + os.path.basename(result_path) self.socketio.emit("log download path", {"log_url_tail": log_url_tail}, namespace="/test") self.cleanBusyMessages() self.logm.log_being_converted = "" return result_path def cleanBusyMessages(self): # if user tried to convert other logs during conversion, he got an error message # this function clears them to show it's ok to convert again self.socketio.emit("clean busy messages", {}, namespace="/test") def createRINEXPackage(self, raw_log_path): # create a RINEX package before download # in case we fail to convert, return the raw log path back result = raw_log_path log_filename = os.path.basename(raw_log_path) conversion_time_string = self.logm.calculateConversionTime(raw_log_path) start_package = { "name": log_filename, "conversion_time": conversion_time_string } conversion_result_package = { "name": log_filename, "conversion_status": "", "messages_parsed": "", "log_url_tail": "" } self.socketio.emit("log conversion start", start_package, namespace="/test") try: log = self.logm.convbin.convertRTKLIBLogToRINEX(raw_log_path, self.logm.getRINEXVersion()) except ValueError, IndexError: print("Conversion canceled") conversion_result_package["conversion_status"] = "Conversion canceled, downloading raw log" self.socketio.emit("log conversion results", conversion_result_package, namespace="/test") return None print("Log conversion done!") if log is not None: result = log.createLogPackage() if log.isValid(): conversion_result_package["conversion_status"] = "Log converted to RINEX" conversion_result_package["messages_parsed"] = log.log_metadata.formValidMessagesString() else: conversion_result_package["conversion_status"] = "Conversion successful, but log does not contain any useful data. Downloading raw log" else: print("Could not convert log. Is the extension wrong?") conversion_result_package["conversion_status"] = "Log conversion failed. Downloading raw log" self.socketio.emit("log conversion results", conversion_result_package, namespace="/test") print("Log conversion results:") print(str(log)) return result