class DisplayMqttTimetable(object): def __init__(self): import os, sys, glob, uuid, time, socket, subprocess, webbrowser from java.lang import System import jmri try: import json except ImportError: import simplejson as json import urllib2 print("Check init list is:", type(list)) self.os = os self.sys = sys self.glob = glob self.uuid = uuid self.time = time self.socket = socket self.subprocess = subprocess self.webbrowser = webbrowser self.System = System self.jmri = jmri self.json = json self.urllib2 = urllib2 self.HOST = "127.0.0.1" self.PORT = 1880 # self.START_DELAY = 8 self.NODE_RED_CMD = None self._stdout_fnull = None self._stderr_fnull = None self._node_red_proc = None self.run() def is_windows_jython(self): os_name = self.System.getProperty("os.name") or "" return os_name.lower().startswith("windows") def find_node_red(self): is_windows = self.is_windows_jython() probe = ['where', 'node-red'] if is_windows else ['which', 'node-red'] try: out = self.subprocess.check_output(probe, stderr=self.subprocess.STDOUT) except OSError: return None paths = out.decode('utf-8', 'ignore').strip().splitlines() if is_windows: for p in paths: if p.lower().endswith('.cmd') and self.os.path.isfile(p): return p for p in paths: if self.os.path.isfile(p): return p return None def verify_node_red(self): path = self.find_node_red() if path: self.NODE_RED_CMD = path else: self.NODE_RED_CMD = 'node-red' self.sys.stderr.write("> Warning: node-red not found. Will attempt 'node-red' from PATH.\n") def is_running(self): try: s = self.socket.create_connection((self.HOST, self.PORT), 1) s.close() return True except: return False def start_node_red(self): if self._stdout_fnull is None: self._stdout_fnull = open(self.os.devnull, "w") self._stderr_fnull = open(self.os.devnull, "w") self._node_red_proc = self.subprocess.Popen( [self.NODE_RED_CMD], stdout=self._stdout_fnull, stderr=self._stderr_fnull, close_fds=True ) def wait_for_node_red(self, timeout=15): print("> Waiting for Node-RED to start...") start_time = self.time.time() while self.time.time() - start_time < timeout: if self.is_running(): print("> Node-RED is now responsive.") return True self.time.sleep(1) print("> Timed out waiting for Node-RED.") return False def build_new_flows(self): new_nodes = [] SCRIPT_DIR = self.jmri.util.FileUtil.getExternalFilename('preference:dispatcher/mqtt_timetables/') for filepath in self.glob.glob(self.os.path.join(SCRIPT_DIR, "*.json")): name = self.os.path.splitext(self.os.path.basename(filepath))[0] label = name.replace(" ", "-") tab_id = self.uuid.uuid4().hex new_nodes.append({"id": tab_id, "type": "tab", "label": label, "disabled": False, "info": ""}) import codecs with codecs.open(filepath, mode='r', encoding='utf-8') as f: try: raw = self.json.load(f) except Exception as e: raise IOError("Failed to parse JSON from %s: %s" % (filepath, e)) arr = raw if isinstance(raw, list) else raw.get("flows", []) nodes_only = [n for n in arr if n.get("type") != "tab"] id_map = dict((n["id"], self.uuid.uuid4().hex) for n in nodes_only) for node in nodes_only: old_id = node["id"] had_z = ("z" in node) node["id"] = id_map[old_id] if had_z: node["z"] = tab_id wires = node.get("wires") if isinstance(wires, list): node["wires"] = [[id_map.get(dest, dest) for dest in link] for link in wires] for k, v in node.items(): if isinstance(v, basestring) and v in id_map: node[k] = id_map[v] new_nodes.append(node) return new_nodes def http_get_flows(self): req = self.urllib2.Request("http://%s:%d/flows" % (self.HOST, self.PORT)) res = self.urllib2.urlopen(req) return self.json.loads(res.read()) def http_post_flows(self, flows): payload = self.json.dumps(flows) req = self.urllib2.Request("http://%s:%d/flows" % (self.HOST, self.PORT), data=payload, headers={"Content-Type": "application/json"}) self.urllib2.urlopen(req).read() def open_all_tabs(self, tab_labels): for label in tab_labels: url = "http://%s:%d/%s" % (self.HOST, self.PORT, label) self.time.sleep(0.3) self.webbrowser.open_new_tab(url) def run(self): self.verify_node_red() preserve = True # Replace with user prompt if needed new_nodes = self.build_new_flows() tab_labels = [n["label"] for n in new_nodes if n.get("type") == "tab"] if not self.is_running(): self.start_node_red() print "node red started" # self.time.sleep(self.START_DELAY) if not self.wait_for_node_red(): print("> WARNING: Node-RED did not start in time.") return else: print "node red already running", "preserve", preserve if not preserve: self.http_post_flows(new_nodes) self.open_all_tabs(tab_labels) return existing = self.http_get_flows() remove_ids = set(n["id"] for n in existing if n.get("type") == "tab" and n.get("label") in tab_labels) kept = [n for n in existing if not (n.get("type") == "tab" and n["id"] in remove_ids) and not ("z" in n and n["z"] in remove_ids)] merged = kept + new_nodes self.http_post_flows(merged) self.open_all_tabs(tab_labels) # d = DisplayMqttTimetable() # d.run()