Esempio n. 1
0
    def __init__(self, config=None):
        QtWidgets.QMainWindow.__init__(self)

        self.rti_config = RtiConfig()
        self.rti_config.init_terminal_config()
        self.rti_config.init_plot_server_config()

        # Initialize the pages
        self.mgr = RiverManager.RiverManager(self, self.rti_config)

        # Initialize the window
        self.main_window_init()
Esempio n. 2
0
class MainWindow(QtWidgets.QMainWindow):
    """
    Main window for the application
    """
    def __init__(self, config=None):
        QtWidgets.QMainWindow.__init__(self)

        self.rti_config = RtiConfig()
        self.rti_config.init_terminal_config()
        self.rti_config.init_plot_server_config()

        # Initialize the pages
        self.mgr = RiverManager.RiverManager(self, self.rti_config)

        # Initialize the window
        self.main_window_init()

    def main_window_init(self):
        # Set the title of the window
        self.setWindowTitle("Rowe Technologies, Inc. - RiveR")

        self.setWindowIcon(QtGui.QIcon(":rti.ico"))

        # Initialize Terminal
        self.Terminal = TerminalVM(self, self.rti_config)
        docked_terminal = QtWidgets.QDockWidget("Terminal", self)
        docked_terminal.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        docked_terminal.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable
                                    | QtWidgets.QDockWidget.DockWidgetMovable)
        docked_terminal.setWidget(self.Terminal)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, docked_terminal)

        # Show the main window
        self.show()

    def closeEvent(self, event):
        """
        Generate 'question' dialog on clicking 'X' button in title bar.

        Reimplement the closeEvent() event handler to include a 'Question'
        dialog with options on how to proceed - Close, Cancel buttons
        """
        reply = QtWidgets.QMessageBox.question(
            self, "Message", "Are you sure you want to quit?",
            QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Cancel)

        if reply == QtWidgets.QMessageBox.Close:
            event.accept()
        else:
            event.ignore()
Esempio n. 3
0
class VectorManager:
    def __init__(self, parent):
        # Set the parent
        self.parent = parent

        # Setup the Average VM
        #self.avg_vm = average_vm.AverageVM(self.parent)

        self.rti_config = RtiConfig()
        self.rti_config.init_average_waves_config()
        self.rti_config.init_terminal_config()
        self.rti_config.init_waves_config()
        self.rti_config.init_plot_server_config()

        self.plot_manager = RtiBokehPlotManager(self.rti_config)
        self.plot_manager.start()
        self.bokeh_server = RtiBokehServer(self.rti_config, self.plot_manager)

        menu = menu_view.MenuView()
        menu_panel = Panel(child=menu.get_layout(), title="menu")
        main_lo = Tabs(tabs=[menu_panel])
        #show(main_lo)

        #Dashboard
        #self.dashboard = Dashboard.Dashboard()
        #self.dashboard.start()
        #self.dashboard.app_setup()

    def set_mag_df(self, df):
        self.dashboard.set_vel_df(df)
    def __init__(self, rti_config: RtiConfig):
        """
        Initialize the list of projects.
        It will look in the directory listed in the config.ini file.
        :param rti_config: RTI Config config.ini file.
        """
        # Set Config file
        self.config = rti_config
        rti_config.init_river_project_config()

        # List of all the projects available
        self.projects = {}

        # Search for all available projects
        self.search_for_projects()
Esempio n. 5
0
    def __init__(self):

        # RTI Config file
        self.rti_config = RtiConfig()
        self.rti_config.init_terminal_config()  # Terminal Options
        self.rti_config.init_timeseries_plot_config()  # Time Series Options

        self.adcp_codec = AdcpCodec()
        self.adcp_codec.ensemble_event += self.handle_ensemble_data

        self.tabular_vm = TabularDataVM()
        self.amp_vm = AmplitudeVM()
        self.contour_vm = ContourVM()
        self.shiptrack_vm = ShipTrackVM()
        self.timeseries_vm = TimeSeriesVM(rti_config=self.rti_config)
        self.adcp_terminal = AdcpTerminalVM(self.rti_config)
        self.adcp_terminal.on_serial_data += self.handle_adcp_serial_data

        # ZeroRPC Manager and Thread
        self.zero_rpc = ZeroRpcManager(rti_config=self.rti_config,
                                       data_mgr=self,
                                       tabular_vm=self.tabular_vm,
                                       amp_vm=self.amp_vm,
                                       contour_vm=self.contour_vm,
                                       shiptrack_vm=self.shiptrack_vm,
                                       timeseries_vm=self.timeseries_vm,
                                       adcp_terminal=self.adcp_terminal)
        self.zero_rpc_thread = Thread(name="ZeroRPC",
                                      target=self.zero_rpc.run_server,
                                      args=(4241, ))

        # Ensemble processing thread
        self.ens_thread_alive = True
        self.ens_queue = deque(maxlen=1000)
        self.ens_thread_event = Event()
        self.ens_thread = Thread(name="DataManager",
                                 target=self.ens_thread_run)

        # Used to remove vessel speed
        self.prev_bt_east = Ensemble.BadVelocity
        self.prev_bt_north = Ensemble.BadVelocity
        self.prev_bt_vert = Ensemble.BadVelocity
Esempio n. 6
0
    def __init__(self, parent):
        # Set the parent
        self.parent = parent

        # Setup the Average VM
        #self.avg_vm = average_vm.AverageVM(self.parent)

        self.rti_config = RtiConfig()
        self.rti_config.init_average_waves_config()
        self.rti_config.init_terminal_config()
        self.rti_config.init_waves_config()
        self.rti_config.init_plot_server_config()

        self.plot_manager = RtiBokehPlotManager(self.rti_config)
        self.plot_manager.start()
        self.bokeh_server = RtiBokehServer(self.rti_config, self.plot_manager)

        menu = menu_view.MenuView()
        menu_panel = Panel(child=menu.get_layout(), title="menu")
        main_lo = Tabs(tabs=[menu_panel])
Esempio n. 7
0
    def __init__(self, socketio):
        self.plot = self.create_plot()
        self.socketio = socketio

        # RTI Configuration
        self.rti_config = RtiConfig(file_path="config.ini")
        self.rti_config.init_river_project_config()

        # Plot manager to keep track of plots
        self.plot_mgr = PlotManager(app_mgr=self, socketio=socketio)

        # River Project Manager to keep track of River projects
        self.river_prj_mrg = RiverProjectManager(self.rti_config)

        # ADCP Codec to decode the ADCP data
        self.adcp_codec = AdcpCodec()
        self.adcp_codec.ensemble_event += self.process_ensemble

        # Serial Port
        self.serial_port = None
        self.serial_thread = None
        self.serial_thread_alive = False

        self.app_state = {
            "is_serial_connected": False,  # Is the serial port connected
            "serial_status": [],  # Status of the serial connection
            "selected_serial_port": "",  # Comm port selected
            "selected_baud": "115200",  # Baud rate selected
            "is_serial_error": False,  # Any serial errors.
            "serial_error_status": [],  # List of error messages
            "baud_list":
            self.get_baud_rates(),  # List of all available Baud rates
            "serial_port_list":
            self.get_serial_ports(),  # List of all available Serial Ports
            "serial_raw_ascii": "",  # Raw ascii from the serial port
            "max_ascii_buff":
            10000,  # Maximum number of characters to keep in serial ASCII buffer
            "adcp_break": {},  # Results of a BREAK statement
            "adcp_ens_num": 0,  # Latest Ensemble number
            "selected_files": [],  # Selected files to playback,
            "selected_project_db": None,  # Selected Project,
            "prj_name": "",  # Current project name
            "prj_path": "",  # Current project folder path
        }

        self.transect_state = {
            "transect_index": 1,  # Current index for the transect
            "transect_dt_start": None,  # Start datetime of the transect
            "transect_duration": None,  # Time duration of the transect
            "voltage": 0.0,  # System Voltage
        }

        # Current Transect
        self.db_file = None  # DB file to store the ensembles and transects
        self.raw_file = None  # Raw binary file to the store the ensemble data
        self.is_record_raw_data = self.rti_config.config['RIVER'][
            'auto_save_raw']  # Automatically save on startup
        self.curr_ens_index = 0  # Index the project DB for the latest ensemble
        self.transect_index = 0  # Transect Index
        self.transect = None  # Current transect

        #self.is_volt_plot_init = False
        #self.voltage_queue = deque(maxlen=100)
        #self.ens_dt_queue = deque(maxlen=100)

        # Incoming serial data
        self.serial_raw_bytes = None
Esempio n. 8
0
class AppManager:
    def __init__(self, socketio):
        self.plot = self.create_plot()
        self.socketio = socketio

        # RTI Configuration
        self.rti_config = RtiConfig(file_path="config.ini")
        self.rti_config.init_river_project_config()

        # Plot manager to keep track of plots
        self.plot_mgr = PlotManager(app_mgr=self, socketio=socketio)

        # River Project Manager to keep track of River projects
        self.river_prj_mrg = RiverProjectManager(self.rti_config)

        # ADCP Codec to decode the ADCP data
        self.adcp_codec = AdcpCodec()
        self.adcp_codec.ensemble_event += self.process_ensemble

        # Serial Port
        self.serial_port = None
        self.serial_thread = None
        self.serial_thread_alive = False

        self.app_state = {
            "is_serial_connected": False,  # Is the serial port connected
            "serial_status": [],  # Status of the serial connection
            "selected_serial_port": "",  # Comm port selected
            "selected_baud": "115200",  # Baud rate selected
            "is_serial_error": False,  # Any serial errors.
            "serial_error_status": [],  # List of error messages
            "baud_list":
            self.get_baud_rates(),  # List of all available Baud rates
            "serial_port_list":
            self.get_serial_ports(),  # List of all available Serial Ports
            "serial_raw_ascii": "",  # Raw ascii from the serial port
            "max_ascii_buff":
            10000,  # Maximum number of characters to keep in serial ASCII buffer
            "adcp_break": {},  # Results of a BREAK statement
            "adcp_ens_num": 0,  # Latest Ensemble number
            "selected_files": [],  # Selected files to playback,
            "selected_project_db": None,  # Selected Project,
            "prj_name": "",  # Current project name
            "prj_path": "",  # Current project folder path
        }

        self.transect_state = {
            "transect_index": 1,  # Current index for the transect
            "transect_dt_start": None,  # Start datetime of the transect
            "transect_duration": None,  # Time duration of the transect
            "voltage": 0.0,  # System Voltage
        }

        # Current Transect
        self.db_file = None  # DB file to store the ensembles and transects
        self.raw_file = None  # Raw binary file to the store the ensemble data
        self.is_record_raw_data = self.rti_config.config['RIVER'][
            'auto_save_raw']  # Automatically save on startup
        self.curr_ens_index = 0  # Index the project DB for the latest ensemble
        self.transect_index = 0  # Transect Index
        self.transect = None  # Current transect

        #self.is_volt_plot_init = False
        #self.voltage_queue = deque(maxlen=100)
        #self.ens_dt_queue = deque(maxlen=100)

        # Incoming serial data
        self.serial_raw_bytes = None

    def get_plot(self):
        return self.plot

    def create_plot(self):
        N = 40
        x = np.linspace(0, 1, N)
        y = np.random.randn(N)
        df = pd.DataFrame({'x': x, 'y': y})  # creating a sample dataframe

        data = [
            go.Bar(
                x=df['x'],  # assign x as the dataframe column 'x'
                y=df['y'])
        ]

        graphJSON = json.dumps(data, cls=plotly.utils.PlotlyJSONEncoder)

        return graphJSON

    def clear_plots(self):
        """
        Clear all the plots.
        """
        self.plot_mgr.clear_plots()

    def create_river_project(self):
        """
        Create a river project file if it does not exist.
        This will keep all the ensemble and transect information.
        Also create a raw data file to store all the incoming serial data.
        """
        if not self.db_file:
            curr_datetime = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
            #prj_file = self.river_prj_mrg.create_project(curr_datetime)
            #self.app_state["prj_name"] = curr_datetime
            #self.app_state["prj_path"] = prj_file.file_path

            db_file_path = os.path.join(
                self.rti_config.config['RIVER']['output_dir'],
                curr_datetime + ".db")
            self.db_file = RtiSqliteProjects(db_file_path)
            self.db_file.create_tables()

            self.raw_file = RtiBinaryWriter(
                folder_path=self.rti_config.config['RIVER']['output_dir'])

    def create_transect(self):
        # Create a transect
        if not self.transect:
            self.transect = Transect(self.transect_next_index)

        # Ensure the project is created
        self.create_river_project(self)

        # Start recording a transect file
        if self.raw_file:
            self.raw_file.close()

        file_transect_header = "Transect_" + str(self.transect_index)
        self.raw_file = RtiBinaryWriter(
            folder_path=self.rti_config.config['RIVER']['output_dir'],
            header=file_transect_header)

    def update_site_info(self, site_info):
        """
        Update the site information for the transect.
        """
        # Create the transect if it is not created
        self.create_transect()

        # Set the values
        self.transect.site_name = site_info["site_name"]
        self.transect.station_number = site_info["station_number"]
        self.transect.location = site_info["location"]
        self.transect.party = site_info["party"]
        self.transect.boat = site_info["boat"]
        self.transect.measurement_num = site_info["measurement_num"]
        self.transect.comments = site_info["comments"]

        # Write the data to the transect
        self.db_file.update_transect(self.transect)

    def start_transect(self):
        """
        Start the transect.  Take all the information from the transect settings
        and write it to the db file.
        """
        # Create the project db file if it does not exist
        self.create_river_project()

        # Create a transect
        if not self.transect:
            self.transect = Transect(self.transect_next_index)

        # Set the start datetime
        self.transect.start_datetime = datetime.datetime.now()

        # Set the first ensemble for the transect
        self.transect.start_ens_index = self.curr_ens_index

    def stop_transect(self):
        """
        Stop the transect.  Set the last ensemble in the transect.
        Make the discharge calculation.
        Record the transect information to the DB file.
        """
        # Create the project db file if it does not exist
        self.create_river_project()

        # Set the stop datetime
        self.transect.stop_datetime = datetime.datetime.now()

        # Set the last ensemble for the transect
        self.transect.last_ens_index = self.curr_ens_index

        # Add the transect to the db file
        self.db_file.add_transect(self.transect)

        # Increment the transect index for the next transect
        self.transect_index = self.transect_next_index + 1

    def socketio_background_thread(self):
        """
        Background worker.  This will maintain the status of the
        backend and the GUI.
        Send the status of the Datalogger download process.
        This will continously check the status of the process.
        """
        count = 0
        while True:
            # Wait time
            self.socketio.sleep(5)
            count += 1

            # Send a debug status to websocket
            self.socketio.emit('status_report', {
                'data': 'Server generated event',
                'count': count
            },
                               namespace='/rti')

    def get_serial_ports(self):
        return serial_port.get_serial_ports()

    def get_baud_rates(self):
        return serial_port.get_baud_rates()

    def connect_serial(self, comm_port: str, baud: int):
        """
        Connect the serial port.
        Start the serial port read thread.
        """
        # Set App State
        self.app_state["selected_serial_port"] = comm_port
        self.app_state["selected_baud"] = str(baud)

        if not self.serial_port:
            try:
                self.serial_port = AdcpSerialPort(comm_port, baud)
            except ValueError as ve:
                logging.error("Error opening serial port. " + str(ve))
                self.app_state["is_serial_connected"] = False
                self.app_state["is_serial_error"] = True
                self.app_state["serial_error_status"].append(
                    "Error opening serial port. " + str(ve))
                return self.app_state
            except serial.SerialException as se:
                logging.error("Error opening serial port. " + str(se))
                self.app_state["is_serial_connected"] = False
                self.app_state["is_serial_error"] = True
                self.app_state["serial_error_status"].append(
                    "Error opening serial port. " + str(se))
                return self.app_state
            except Exception as e:
                logging.error("Error opening serial port. " + str(e))
                self.app_state["is_serial_connected"] = False
                self.app_state["is_serial_error"] = True
                self.app_state["serial_error_status"].append(
                    "Error opening serial port. " + str(e))
                return self.app_state

            # Start the read thread
            self.serial_thread_alive = True
            self.serial_thread = threading.Thread(
                name="Serial Terminal Thread",
                target=self.serial_thread_worker)
            self.serial_thread.start()

            # Set the app state
            self.app_state["is_serial_connected"] = True
            self.app_state["is_serial_error"] = False
            self.app_state["serial_status"].clear()
            self.app_state["serial_status"].append("Connected")

            # Send a BREAK to get ADCP Information
            self.send_serial_break()

            # Clear the plots to get new data
            self.clear_plots()

            return self.app_state

    def disconnect_serial(self):
        """
        Disconnect the serial port.
        """
        self.serial_thread_alive = False

        if self.serial_port:
            self.serial_port.disconnect()
            self.serial_port = None

            # Set the app state
            self.app_state["is_serial_connected"] = False
            self.app_state["is_serial_error"] = False
            self.app_state["serial_status"].clear()
            self.app_state["serial_error_status"].clear()
            self.app_state["serial_status"].append("Disconnected")

        return self.app_state

    def send_serial_break(self):
        if self.serial_port:

            # Clear the buffer of the serial data
            # We can then check for the results
            self.app_state["serial_raw_ascii"] = ""

            self.serial_port.send_break()

            # Wait a second for result
            time.sleep(1.2)

            # Get the results of the BREAK
            break_results = self.app_state["serial_raw_ascii"]
            logging.debug(break_results)

            # Decode the BREAK result
            self.app_state["adcp_break"] = self.adcp_codec.decode_BREAK(
                break_results)
            logging.debug(self.app_state["adcp_break"])

            return self.app_state["adcp_break"]

    def send_serial_cmd(self, cmd):
        if self.serial_port:
            self.serial_port.send_cmd(cmd=cmd)

    def playback_files(self):
        """
        Display a dialog box to select the files.
        :return: List of all the files selected.
        """
        self.app_state["selected_files"] = self.select_files_playback()

        # Plot all the plots
        if len(self.app_state["selected_files"]) >= 1:

            # Get the directory of the first file selected
            file_dir = os.path.dirname(self.app_state["selected_files"][0])
            file_name_no_ext = pathlib.Path(
                self.app_state["selected_files"][0]).stem
            file_ext = pathlib.Path(self.app_state["selected_files"][0]).suffix

            # Check if it is a raw file or DB file
            if file_ext == '.bin' or file_ext == '.ens' or file_ext == '.rtb' or file_ext == '.BIN' or file_ext == '.ENS' or file_ext == '.RTB':
                # Create a project DB file
                project_path = os.path.join(
                    file_dir, file_name_no_ext + RtiSqliteProjects.FILE_EXT)
                playback_project = RtiSqliteProjects(project_path)

                # Load the files to the project
                # Use a thread to load the data
                read_file_thread = threading.Thread(
                    target=self.load_raw_files,
                    args=[project_path],
                    name="Read Raw File Thread")

                # Start the thread then wait for it to finish before moving on
                read_file_thread.start()
                read_file_thread.join()
                #playback_project.load_files(self.app_state['selected_files'])

                # Reset what the selected project is
                self.app_state['selected_project_db'] = project_path

            # Check if the file is a RDB file
            elif file_ext == '.rdb' or file_ext == '.RDB' or file_ext == RtiSqliteProjects.FILE_EXT:
                self.app_state["selected_project_db"] = self.app_state[
                    "selected_files"][0]

            # Load plot the DB data
            if self.app_state["selected_project_db"]:
                self.plot_mgr.playback_sqlite(
                    self.app_state["selected_project_db"])

        return self.app_state["selected_project_db"]

    def load_raw_files(self, project_path):
        """
        Load the raw data to the project.
        :param project_path: Path to the RDB file.
        :return:
        """
        playback_project = RtiSqliteProjects(project_path)

        # Load the files to the project
        playback_project.load_files(self.app_state['selected_files'])

    def select_files_playback(self):
        """
        Display a dialog box to select the files.
        :return: List of all the files selected.
        """
        # Dialog to ask for a file to select
        root = mtTkinter.Tk()
        root.overrideredirect(
            True)  # Used to Bring window to front and focused
        root.geometry('0x0+0+0')  # Used to Bring window to front and focused
        root.focus_force()  # Used to Bring window to front and focused
        filetypes = [("RDB files", "*.rdb"), ("ENS Files", "*.ens"),
                     ("BIN Files", "*.bin"), ('All Files', '*.*')]
        file_paths = filedialog.askopenfilenames(
            parent=root, title="Select File to Playback", filetypes=filetypes)
        root.withdraw()

        print(str(file_paths))

        return file_paths

    def process_ensemble(self, sender, ens):
        """"
        Process the next incoming ensemble.
        """
        heading = 0.0
        pitch = 0.0
        roll = 0.0
        voltage = 0.0
        water_temp = 0.0
        transect_duration = ""
        ens_time = ""
        adcp_ens_num = 0
        if ens.IsEnsembleData:
            #print(str(ens.EnsembleData.EnsembleNumber))
            self.app_state["adcp_ens_num"] = ens.EnsembleData.EnsembleNumber
            adcp_ens_num = ens.EnsembleData.EnsembleNumber

            if not self.transect_state["transect_dt_start"]:
                self.transect_state[
                    "transect_dt_start"] = ens.EnsembleData.datetime()

            # Calculate the time between when transect started and now
            self.transect_state["transect_duration"] = (
                ens.EnsembleData.datetime() -
                self.transect_state["transect_dt_start"]).total_seconds()
            #duration = (ens.EnsembleData.datetime() - self.app_state["transect_dt_start"]).total_seconds()
            transect_duration = str(
                timedelta(seconds=self.transect_state["transect_duration"]))

            ens_time = str(ens.EnsembleData.datetime().strftime("%H:%M:%S"))

        if ens.IsAncillaryData:
            heading = round(ens.AncillaryData.Heading, 1)
            pitch = round(ens.AncillaryData.Pitch, 1)
            roll = round(ens.AncillaryData.Roll, 1)
            water_temp = round(ens.AncillaryData.WaterTemp, 1)

        if ens.IsSystemSetup:
            voltage = round(ens.SystemSetup.Voltage, 1)

        # Pass the ASCII serial data to the websocket
        self.socketio.emit('adcp_ens', {
            'adcp_ens_num': adcp_ens_num,
            'ens_time': ens_time,
            'transect_duration': transect_duration,
            'voltage': voltage,
            'heading': heading,
            'pitch': pitch,
            'roll': roll,
            'water_temp': water_temp,
        },
                           namespace='/rti')

        # Update the plot manager
        self.plot_mgr.add_ens(ens)

        # Record the ensembles
        self.record_ens(ens)

    def serial_thread_worker(self):
        """
        Thread worker to handle reading the serial port.
        :param mgr: This Object to get access to the variables.
        :return:
        """
        while self.serial_thread_alive:
            try:
                if self.serial_port.raw_serial.in_waiting:
                    try:
                        # Read the data from the serial port
                        self.serial_raw_bytes = self.serial_port.read(
                            self.serial_port.raw_serial.in_waiting)

                        # Record the data
                        self.record_raw_data(self.serial_raw_bytes)

                        # Convert to ASCII
                        # Ignore any non-ASCII characters
                        raw_serial_ascii = self.serial_raw_bytes.decode(
                            'ascii', 'ignore')

                        # Replace ACK and NAK
                        raw_serial_ascii = raw_serial_ascii.replace(
                            "\6", "[ACK]")
                        #raw_serial_ascii = raw_serial_ascii.replace("\15", "[NAK]")

                        # Convert the data to ASCII
                        self.app_state["serial_raw_ascii"] += raw_serial_ascii

                        # Pass the ASCII serial data to the websocket
                        self.socketio.emit(
                            'serial_comm',
                            {'data': self.app_state["serial_raw_ascii"]},
                            namespace='/rti')

                        # Maintain a fixed buffer size
                        ascii_buff_size = len(
                            self.app_state["serial_raw_ascii"])
                        if ascii_buff_size > 0:
                            if ascii_buff_size > self.app_state[
                                    "max_ascii_buff"]:
                                # Remove the n number of characters to keep it within the buffer size
                                remove_buff_size = ascii_buff_size - self.app_state[
                                    "max_ascii_buff"]
                                self.app_state[
                                    "serial_raw_ascii"] = self.app_state[
                                        "serial_raw_ascii"][remove_buff_size:]

                        # Pass data to codec to decode ADCP data
                        self.adcp_codec.add(self.serial_raw_bytes)

                    except Exception as ex:
                        # Do nothing
                        # This is to prevent from seeing binary data on screen
                        logging.info("Error Reading serial data" + str(ex))

                # Put a small sleep to allow more data to go into the buffer
                time.sleep(0.01)

            except serial.SerialException as se:
                logging.error("Error using the serial port.\n" + str(se))
                self.disconnect_serial()
            except Exception as ex:
                logging.error("Error processing the data.\n" + str(ex))
                self.disconnect_serial()

    def record_raw_data(self, serial_bytes):
        """
        Write the raw data from the serial port to
        a binary file.  This will write any data.
        :param serial_bytes: Serial bytes array
        """
        if self.is_record_raw_data:
            # Ensure the file is created
            self.create_river_project()

            # If the file is created, write the data
            if self.raw_file:
                self.raw_file.write(serial_bytes)

    def record_ens(self, ens: Ensemble):
        """
        Record the ensemble to the db file.
        Record the raw data to a raw binary file.
        :param ens: Ensemble data.
        """
        # Create the project db file if it does not exist
        self.create_river_project()

        # Write the ensemble to the db file
        if self.db_file:
            # Set the latest index of the ensemble in the db file
            # Set the burst number to the transect number
            # Set the is_batch_write to false.  We are not writing in bulk
            self.curr_ens_index = self.db_file.add_ensemble(
                ens, burst_num=self.transect_index, is_batch_write=False)
Esempio n. 9
0
    def __init__(self):

        self.rti_config = RtiConfig()
        self.rti_config.init_average_waves_config()
        self.rti_config.init_terminal_config()
        self.rti_config.init_waves_config()
        self.rti_config.init_plot_server_config()

        self.is_screen_data = True
        self.already_got_data = False
        self.ens_count = 0

        prj_idx = 1

        # Bokeh Plot
        #self.plot_manager = RtiBokehPlotManager(self.rti_config)
        #self.plot_manager.start()
        #self.bokeh_server = RtiBokehServer(self.rti_config, self.plot_manager)

        # Matplotlib plot
        #self.display_ens = DisplayEnsembles()

        # Setup the StreamLit Plots
        self.heatmap = StreamlitHeatmap()
        self.mag_dir_line = StreamlitMagDirLine()
        #self.volt_line = StreamlitPowerLine()
        #self.ancillary_line = StreamlitAncillaryLine()

        # On reprocessing in Streamlit, it will through an error if this
        # is rerun because the browser content is not in the main thread
        rti_check = RtiCheckFile()

        # Event handler for ensembles
        rti_check.ensemble_event += self.ens_handler

        # Select a file to process

        #uploaded_file = st.file_uploader("Choose a file", type=['ens', 'bin'])
        #if uploaded_file is not None:
            # do stuff
            #print(uploaded_file)

        #file_paths = uploaded_file
        file_paths = rti_check.select_files()

        # Verify a file was selected
        if len(file_paths) > 0:
            #file_paths = [Path("//Beansack/rico/RTI/Data/cs/nav.com.cn/xiamen202007-/01400000000000000000000000000254_xiamen202007_2.ENS")]
            logging.debug(file_paths)

            # Get the folder path from the first file path
            folder_path = PurePath(file_paths[0]).parent
            prj_name = Path(file_paths[0]).stem
            db_name = str(prj_name) + ".db"

            # Create a project file to store the results
            db_path = str(folder_path / db_name)
            logging.debug(db_path)

            if not Path(db_path).exists():
                self.project = RtiSqliteProjects(file_path=db_path)
                self.project.create_tables()
                prj_idx = self.project.add_prj_sql(str(prj_name), db_path)

                # Begin the batch writing to the database
                self.project.begin_batch(str(prj_name))

                # Process the selected file
                rti_check.process(file_paths, show_live_error=False)

                # Get the summary and add it to the sqlite project
                file_summary = rti_check.get_summary()
                self.project.add_summary(json.dumps(file_summary), prj_idx)

                # End any remaining batch
                self.project.end_batch()
            else:
                # Create a connection to the sqlite project file
                self.project = RtiSqliteProjects(file_path=db_path)
                prj_idx = self.project.check_project_exist(str(prj_name))

            # Display the summary
            prj_json_summary = self.project.get_summary(prj_idx)
            if len(prj_json_summary) >= 1:
                json_str = prj_json_summary[0][0]
                json_summ = json.loads(json_str)
                #print(json.dumps(json_summ, indent=1))
                print(json.dumps(json_summ["errors"], indent=1))
                print(json.dumps(json_summ["summary"], indent=1))
                st.title("Summary")
                st.json(json_summ)

            # Plot heatmap
            #self.heatmap.get_plot("mag")
            #self.heatmap.get_plot("dir")

            # Plot mag and direction line plot
            #self.mag_dir_line.get_bin_selector()
            #self.mag_dir_line.get_plot("mag")
            #self.mag_dir_line.get_plot("dir")
            #self.ancillary_line.get_plot()
            StreamlitAncillaryLine.get_sqlite_plot(db_path)

            # Plot the Status
            StreamlitStatusLine.get_sqlite_plot(db_path)

            # Plot the Voltage
            StreamlitPowerLine.get_sqlite_plot(db_path)

            # Plot the Bottom Track Range
            StreamlitBottomTrackRangeLine.get_sqlite_plot(db_path)

            # Plot the Bottom Track Beam Velocity with filtering turned on
            StreamlitBottomTrackBeamVelocityLine.get_sqlite_plot(db_path, filter=True, filter_max=5.0)

            # Plot the Bottom Track Beam Velocity
            StreamlitBottomTrackBeamVelocityLine.get_sqlite_plot(db_path, filter=False)

            # Plot the Bottom Track Vessel Speed with filtering turned on
            StreamlitBottomTrackVesselSpeedLine.get_sqlite_plot(db_path, filter=True, filter_max=5.0)

            # Plot the Bottom Track Vessel Speed
            StreamlitBottomTrackVesselSpeedLine.get_sqlite_plot(db_path, filter=False)

            # Plot the Bottom Track Vessel Direction with filtering turned on
            StreamlitBottomTrackVesselDirectionLine.get_sqlite_plot(db_path, filter=True)

            # Plot the Bottom Track Vessel Direction
            StreamlitBottomTrackVesselDirectionLine.get_sqlite_plot(db_path, filter=False)

            # Plot the Water Magnitude
            StreamlitMagHeatmap.get_sqlite_plot(db_path)
Esempio n. 10
0
import os
import h5py
from rti_python.River.RiverProject import RiverProject
from rti_python.River.RiverProjectMeta import RiverProjectMeta
from rti_python.Utilities.config import RtiConfig

rti_config = RtiConfig()
rti_config.init_river_project_config()


def test_constructor():
    project = RiverProject(rti_config, "Project111", os.path.join(rti_config.config['RIVER']['output_dir'], "Project111.hdf5"))

    assert "Project111" == project.name
    assert os.path.join(rti_config.config['RIVER']['output_dir'], "Project111.hdf5") == project.file_path


def test_set_values():
    project = RiverProject(rti_config, "Project112", os.path.join(rti_config.config['RIVER']['output_dir'], "Project112.hdf5"))
    project.adcp_serial_num = "0001"
    project.firmware_ver = "0.0.1.2"
    project.draft_transducer = 11.11
    project.magnetic_declination = 1.234
    project.software_ver = "1.2.3.4"
    project.ident_of_crew = "Jon and Bob"
    project.station_name = "River1"
    project.station_id = "111R1"
    project.water_temp = 1.34
    project.percentage_discharged_measured = 23.4
    project.channel_width = 1234
    project.cross_section_area = 34
Esempio n. 11
0
    def set_options(self, is_boat_speed: bool, is_boat_dir: bool,
                    is_heading: bool, is_pitch: bool, is_roll: bool,
                    is_temp: bool, is_gnss_qual: bool, is_gnss_hdop: bool,
                    is_num_sats: bool, is_water_speed: bool,
                    is_water_dir: bool, is_vtg_speed: bool, max_ens: int):
        """
        Set the options for the Time Series.
        :param is_boat_speed: Boat Speed on/off
        :param is_boat_dir: Boat Direciton on/off
        :param is_heading: Heading on/off
        :param is_pitch: Pitch on/off
        :param is_roll: roll on/off
        :param is_temp: temperature on/off
        :param is_gnss_qual: GNSS Quality on/off
        :param is_gnss_hdop: GNSS HDOP on/off
        :param is_num_sats: Number of Sats on/off
        :param is_water_speed: Water Speed on/off
        :param is_water_dir: Water Direction on/off
        :param is_vtg_speed: GPS Speed on/off
        :param max_ens: Maximum ensembles to display
        :return:
        """

        # Lock the object
        self.thread_lock.acquire()

        # Write settings to the config
        self.rti_config.config['TIMESERIES'][
            'IS_BOAT_SPEED'] = RtiConfig.bool_to_str(is_boat_speed)
        self.rti_config.config['TIMESERIES'][
            'IS_BOAT_DIR'] = RtiConfig.bool_to_str(is_boat_dir)
        self.rti_config.config['TIMESERIES'][
            'IS_HEADING'] = RtiConfig.bool_to_str(is_heading)
        self.rti_config.config['TIMESERIES'][
            'IS_PITCH'] = RtiConfig.bool_to_str(is_pitch)
        self.rti_config.config['TIMESERIES'][
            'IS_ROLL'] = RtiConfig.bool_to_str(is_roll)
        self.rti_config.config['TIMESERIES'][
            'IS_TEMPERATURE'] = RtiConfig.bool_to_str(is_temp)
        self.rti_config.config['TIMESERIES'][
            'IS_GNSS_QUAL'] = RtiConfig.bool_to_str(is_gnss_qual)
        self.rti_config.config['TIMESERIES'][
            'IS_GNSS_HDOP'] = RtiConfig.bool_to_str(is_gnss_hdop)
        self.rti_config.config['TIMESERIES'][
            'IS_NUM_SATS'] = RtiConfig.bool_to_str(is_num_sats)
        self.rti_config.config['TIMESERIES'][
            'IS_WATER_SPEED'] = RtiConfig.bool_to_str(is_water_speed)
        self.rti_config.config['TIMESERIES'][
            'IS_WATER_DIR'] = RtiConfig.bool_to_str(is_water_dir)
        self.rti_config.config['TIMESERIES'][
            'IS_VTG_SPEED'] = RtiConfig.bool_to_str(is_vtg_speed)
        self.rti_config.config['TIMESERIES']['MAX_ENS'] = str(max_ens)
        self.rti_config.write()

        # Set the options
        self.is_boat_speed = is_boat_speed
        self.is_boat_dir = is_boat_dir
        self.is_heading = is_heading
        self.is_pitch = is_pitch
        self.is_roll = is_roll
        self.is_temperature = is_temp
        self.is_gnss_qual = is_gnss_qual
        self.is_gnss_hdop = is_gnss_hdop
        self.is_num_sats = is_num_sats
        self.is_water_speed = is_water_speed
        self.is_water_dir = is_water_dir
        self.is_vtg_speed = is_vtg_speed

        # Check if Max Ensembles changed
        # If it changed, then create new deque with new maxlen
        if self.max_ens != max_ens and not math.isnan(max_ens):
            self.max_ens = max_ens

            # Create a temp of the values
            boat_speed = list(self.boat_speed)
            boat_dir = list(self.boat_dir)
            heading = list(self.heading)
            pitch = list(self.pitch)
            roll = list(self.roll)
            temperature = list(self.temperature)
            gnss_qual = list(self.gnss_qual)
            gnss_hdop = list(self.gnss_hdop)
            num_sats = list(self.num_sats)
            water_speed = list(self.water_speed)
            water_dir = list(self.water_dir)
            vtg_speed = list(self.vtg_speed)

            # Recreate the deque with the new maxlen and original data
            self.boat_speed = deque(boat_speed, maxlen=self.max_ens)
            self.boat_dir = deque(boat_dir, maxlen=self.max_ens)
            self.heading = deque(heading, maxlen=self.max_ens)
            self.pitch = deque(pitch, maxlen=self.max_ens)
            self.roll = deque(roll, maxlen=self.max_ens)
            self.temperature = deque(temperature, maxlen=self.max_ens)
            self.gnss_qual = deque(gnss_qual, maxlen=self.max_ens)
            self.gnss_hdop = deque(gnss_hdop, maxlen=self.max_ens)
            self.num_sats = deque(num_sats, maxlen=self.max_ens)
            self.water_speed = deque(water_speed, maxlen=max_ens)
            self.water_dir = deque(water_dir, maxlen=max_ens)
            self.vtg_speed = deque(vtg_speed, maxlen=max_ens)

        # Release the lock
        self.thread_lock.release()
Esempio n. 12
0
class DataManager:
    def __init__(self):

        # RTI Config file
        self.rti_config = RtiConfig()
        self.rti_config.init_terminal_config()  # Terminal Options
        self.rti_config.init_timeseries_plot_config()  # Time Series Options

        self.adcp_codec = AdcpCodec()
        self.adcp_codec.ensemble_event += self.handle_ensemble_data

        self.tabular_vm = TabularDataVM()
        self.amp_vm = AmplitudeVM()
        self.contour_vm = ContourVM()
        self.shiptrack_vm = ShipTrackVM()
        self.timeseries_vm = TimeSeriesVM(rti_config=self.rti_config)
        self.adcp_terminal = AdcpTerminalVM(self.rti_config)
        self.adcp_terminal.on_serial_data += self.handle_adcp_serial_data

        # ZeroRPC Manager and Thread
        self.zero_rpc = ZeroRpcManager(rti_config=self.rti_config,
                                       data_mgr=self,
                                       tabular_vm=self.tabular_vm,
                                       amp_vm=self.amp_vm,
                                       contour_vm=self.contour_vm,
                                       shiptrack_vm=self.shiptrack_vm,
                                       timeseries_vm=self.timeseries_vm,
                                       adcp_terminal=self.adcp_terminal)
        self.zero_rpc_thread = Thread(name="ZeroRPC",
                                      target=self.zero_rpc.run_server,
                                      args=(4241, ))

        # Ensemble processing thread
        self.ens_thread_alive = True
        self.ens_queue = deque(maxlen=1000)
        self.ens_thread_event = Event()
        self.ens_thread = Thread(name="DataManager",
                                 target=self.ens_thread_run)

        # Used to remove vessel speed
        self.prev_bt_east = Ensemble.BadVelocity
        self.prev_bt_north = Ensemble.BadVelocity
        self.prev_bt_vert = Ensemble.BadVelocity

    def start(self, zerorpc_port: int):
        """
        Start the ensemble thread.  This thread
        handles all the incoming ensembles.
        :param zerorpc_port: zerorpc port.
        :return:
        """
        # Start the ens thread
        self.ens_thread.start()

        # Start the zerorpc thread
        self.zero_rpc_thread.start()

    def shutdown(self):
        """
        Shutdown the object.
        :return:
        """
        # Shutdown the Ensemble thread
        self.ens_thread_alive = False
        self.ens_thread_event.set()

    def handle_adcp_serial_data(self, sender, data):
        """
        Receive raw serial data from the serial port.
        Pass the data to the codec to be processed.
        :param sender: Not Used.
        :param data: Raw ensemble binary data.
        :return:
        """
        logging.info("DataManager: Serial Data Received")
        self.adcp_codec.add(data)

    def handle_ensemble_data(self, sender, ens):
        """
        Receiver data from the codec and process it by passing it
        to the data manager incoming ensemble.
        :param sender:Not Used
        :param ens: Ensemble from codec
        :return:
        """
        self.incoming_ens(ens)

    def incoming_ens(self, ens: Ensemble):
        """
        Handle all incoming data to be displayed.
        Put the data in a queue then wakeup the thread.
        :param ens: Ensemble to be displayed.
        :return:
        """
        # Add the data to the queue
        self.ens_queue.append(ens)

        # Wakeup the thread
        self.ens_thread_event.set()

    def ens_thread_run(self):
        """"
        Run a thread to handle the incoming ensemble data.
        Pass the data to the Waveforce codec and average water.
        """

        while self.ens_thread_alive:

            # Wait until the thread is awoken
            self.ens_thread_event.wait()

            # Check if data is in the queue
            while len(self.ens_queue) > 0:
                # Get the data from the queue
                ens = self.ens_queue.popleft()

                # QA QC the data
                EnsembleQC.scan_ensemble(ens)

                # Screen the data

                # Pass data
                if ens:
                    if ens.IsEnsembleData:
                        logging.info("AdcpDataManager: Process Ensemble: " +
                                     str(ens.EnsembleData.EnsembleNumber))

                        # Screen Data

                        # Pass Data to Tabular data
                        self.tabular_vm.set_ens(ens)

                        # Pass data to Amplitude plot VM
                        self.amp_vm.set_ens(ens)

                        # Pass data to Contour plot VM
                        self.contour_vm.set_ens(ens)

                        # Pass data to Ship Track plot VM
                        self.shiptrack_vm.set_ens(ens)

                        # Pass data to Time Series plot VM
                        self.timeseries_vm.set_ens(ens)

            # Reset the event
            self.ens_thread_event.clear()
Esempio n. 13
0
    def __init__(self, config=None):
        QtWidgets.QMainWindow.__init__(self)

        # Setup the logging
        #RtiLogging.RtiLogger(log_level=logging.DEBUG)

        self.rti_config = RtiConfig()
        self.rti_config.init_average_waves_config()
        self.rti_config.init_terminal_config()
        self.rti_config.init_waves_config()
        self.rti_config.init_plot_server_config()

        # Setup logging
        log_file_path = self.rti_config.config['Waves'][
            'output_dir'] + "/rti_waves.log"
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
            datefmt='%m-%d %H:%M',
            filename=log_file_path,
            filemode='a+')

        self.bokeh_app = None

        # Initialize Monitor
        self.Monitor = MonitorVM(self, self.rti_config)
        docked_monitor = QtWidgets.QDockWidget("Monitor", self)
        docked_monitor.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        docked_monitor.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable
                                   | QtWidgets.QDockWidget.DockWidgetMovable)
        docked_monitor.setWidget(self.Monitor)
        self.addDockWidget(QtCore.Qt.TopDockWidgetArea, docked_monitor)

        # Initialize the Setup
        self.Setup = SetupVM(self, self.rti_config)
        self.docked_setup = QtWidgets.QDockWidget("Setup", self)
        self.docked_setup.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        self.docked_setup.setFeatures(
            QtWidgets.QDockWidget.DockWidgetFloatable
            | QtWidgets.QDockWidget.DockWidgetMovable
            | QtWidgets.QDockWidget.DockWidgetClosable)
        self.docked_setup.resize(500, 400)
        self.docked_setup.setWidget(self.Setup)
        self.docked_setup.setVisible(False)
        #self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.docked_setup)

        # Initialize Terminal
        self.Terminal = TerminalVM(self, self.rti_config)
        docked_terminal = QtWidgets.QDockWidget("Terminal", self)
        docked_terminal.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        docked_terminal.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable
                                    | QtWidgets.QDockWidget.DockWidgetMovable)
        docked_terminal.setWidget(self.Terminal)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, docked_terminal)
        #self.tabifyDockWidget(self.docked_setup, docked_terminal)

        # Initialize the Average Water Column
        self.AvgWater = AverageWaterVM(self, self.rti_config)
        self.docked_avg_water = QtWidgets.QDockWidget("AvgWater", self)
        self.docked_avg_water.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        self.docked_avg_water.setFeatures(
            QtWidgets.QDockWidget.DockWidgetFloatable
            | QtWidgets.QDockWidget.DockWidgetMovable
            | QtWidgets.QDockWidget.DockWidgetClosable)
        self.docked_avg_water.resize(800, 600)
        self.docked_avg_water.setWidget(self.AvgWater)
        self.docked_avg_water.setVisible(False)

        # Start Event Loop needed for server
        #asyncio.set_event_loop(asyncio.new_event_loop())

        # Setting num_procs here means we can't touch the IOLoop before now, we must
        # let Server handle that. If you need to explicitly handle IOLoops then you
        # will need to use the lower level BaseServer class.
        #self.server = Server({'/': self.AvgWater.setup_bokeh_server})
        #apps = {'/': self.AvgWater.setup_bokeh_server}
        #handler = FunctionHandler(self.AvgWater.setup_bokeh_server)
        #app = Application(handler)
        apps = {'/': self.get_bokeh_app()}

        bokeh_port = int(self.rti_config.config['PLOT']['PORT'])
        bokeh_ip = self.rti_config.config['PLOT']['IP']
        websocket_allow = bokeh_ip + ":" + str(bokeh_port)
        self.server = Server(apps,
                             port=bokeh_port,
                             address=bokeh_ip,
                             allow_websocket_origin=[websocket_allow])
        self.server.start()

        # Only display the webpage if enabled
        if self.rti_config.config.getboolean('PLOT', 'LIVE'):
            self.server.show('/')

        #thread = Thread(target=self.start_bokeh_server)
        #thread.start()

        # Add the displays to the manager to monitor all the data
        self.AutoWavesManager = autowaves_manger.AutoWavesManager(
            self, self.rti_config, self.Terminal, self.Setup, self.Monitor,
            self.AvgWater)

        # Initialize the window
        self.main_window_init()

        # Outside the notebook ioloop needs to be started
        loop = IOLoop.current()
        #loop.start()
        t = Thread(target=loop.start, daemon=True)
        t.start()
Esempio n. 14
0
class MainWindow(QtWidgets.QMainWindow):
    """
    Main window for the application
    """
    def __init__(self, config=None):
        QtWidgets.QMainWindow.__init__(self)

        # Setup the logging
        #RtiLogging.RtiLogger(log_level=logging.DEBUG)

        self.rti_config = RtiConfig()
        self.rti_config.init_average_waves_config()
        self.rti_config.init_terminal_config()
        self.rti_config.init_waves_config()
        self.rti_config.init_plot_server_config()

        # Setup logging
        log_file_path = self.rti_config.config['Waves'][
            'output_dir'] + "/rti_waves.log"
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
            datefmt='%m-%d %H:%M',
            filename=log_file_path,
            filemode='a+')

        self.bokeh_app = None

        # Initialize Monitor
        self.Monitor = MonitorVM(self, self.rti_config)
        docked_monitor = QtWidgets.QDockWidget("Monitor", self)
        docked_monitor.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        docked_monitor.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable
                                   | QtWidgets.QDockWidget.DockWidgetMovable)
        docked_monitor.setWidget(self.Monitor)
        self.addDockWidget(QtCore.Qt.TopDockWidgetArea, docked_monitor)

        # Initialize the Setup
        self.Setup = SetupVM(self, self.rti_config)
        self.docked_setup = QtWidgets.QDockWidget("Setup", self)
        self.docked_setup.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        self.docked_setup.setFeatures(
            QtWidgets.QDockWidget.DockWidgetFloatable
            | QtWidgets.QDockWidget.DockWidgetMovable
            | QtWidgets.QDockWidget.DockWidgetClosable)
        self.docked_setup.resize(500, 400)
        self.docked_setup.setWidget(self.Setup)
        self.docked_setup.setVisible(False)
        #self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.docked_setup)

        # Initialize Terminal
        self.Terminal = TerminalVM(self, self.rti_config)
        docked_terminal = QtWidgets.QDockWidget("Terminal", self)
        docked_terminal.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        docked_terminal.setFeatures(QtWidgets.QDockWidget.DockWidgetFloatable
                                    | QtWidgets.QDockWidget.DockWidgetMovable)
        docked_terminal.setWidget(self.Terminal)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, docked_terminal)
        #self.tabifyDockWidget(self.docked_setup, docked_terminal)

        # Initialize the Average Water Column
        self.AvgWater = AverageWaterVM(self, self.rti_config)
        self.docked_avg_water = QtWidgets.QDockWidget("AvgWater", self)
        self.docked_avg_water.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas)
        self.docked_avg_water.setFeatures(
            QtWidgets.QDockWidget.DockWidgetFloatable
            | QtWidgets.QDockWidget.DockWidgetMovable
            | QtWidgets.QDockWidget.DockWidgetClosable)
        self.docked_avg_water.resize(800, 600)
        self.docked_avg_water.setWidget(self.AvgWater)
        self.docked_avg_water.setVisible(False)

        # Start Event Loop needed for server
        #asyncio.set_event_loop(asyncio.new_event_loop())

        # Setting num_procs here means we can't touch the IOLoop before now, we must
        # let Server handle that. If you need to explicitly handle IOLoops then you
        # will need to use the lower level BaseServer class.
        #self.server = Server({'/': self.AvgWater.setup_bokeh_server})
        #apps = {'/': self.AvgWater.setup_bokeh_server}
        #handler = FunctionHandler(self.AvgWater.setup_bokeh_server)
        #app = Application(handler)
        apps = {'/': self.get_bokeh_app()}

        bokeh_port = int(self.rti_config.config['PLOT']['PORT'])
        bokeh_ip = self.rti_config.config['PLOT']['IP']
        websocket_allow = bokeh_ip + ":" + str(bokeh_port)
        self.server = Server(apps,
                             port=bokeh_port,
                             address=bokeh_ip,
                             allow_websocket_origin=[websocket_allow])
        self.server.start()

        # Only display the webpage if enabled
        if self.rti_config.config.getboolean('PLOT', 'LIVE'):
            self.server.show('/')

        #thread = Thread(target=self.start_bokeh_server)
        #thread.start()

        # Add the displays to the manager to monitor all the data
        self.AutoWavesManager = autowaves_manger.AutoWavesManager(
            self, self.rti_config, self.Terminal, self.Setup, self.Monitor,
            self.AvgWater)

        # Initialize the window
        self.main_window_init()

        # Outside the notebook ioloop needs to be started
        loop = IOLoop.current()
        #loop.start()
        t = Thread(target=loop.start, daemon=True)
        t.start()

    def get_bokeh_app(self):
        """
        Generate a single instance of the bokeh app.
        :return: Bokeh app created.
        """
        if self.bokeh_app is None:
            handler = FunctionHandler(self.AvgWater.setup_bokeh_server)
            self.bokeh_app = Application(handler)

        return self.bokeh_app

    def main_window_init(self):
        # Set the title of the window
        self.setWindowTitle(
            "Rowe Technologies Inc. - AutoWaves Monitor v1.9.3")

        self.setWindowIcon(QtGui.QIcon(":rti.ico"))

        self.resize(930, 550)

        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        #avgMenu = mainMenu.addMenu('Average Water')
        setupMenu = mainMenu.addMenu('Setup')

        playbackButton = QAction(QIcon('exit24.png'), 'Playback', self)
        playbackButton.setShortcut('Ctrl+P')
        playbackButton.setStatusTip('Playback files')
        playbackButton.triggered.connect(self.playback)
        fileMenu.addAction(playbackButton)

        exitButton = QAction(QIcon('exit24.png'), 'Exit', self)
        exitButton.setShortcut('Ctrl+Q')
        exitButton.setStatusTip('Exit application')
        exitButton.triggered.connect(self.close)
        fileMenu.addAction(exitButton)

        setupButton = QAction(QIcon('exit24.png'), 'Waves Setup', self)
        setupButton.setShortcut('Ctrl+S')
        setupButton.setStatusTip('Setup The Waves Settings')
        setupButton.triggered.connect(self.display_setup_view)
        setupMenu.addAction(setupButton)

        #avgWaterButton = QAction(QIcon('exit24.png'), 'Average Water', self)
        #avgWaterButton.setShortcut('Ctrl+A')
        #avgWaterButton.setStatusTip('Average the Water Column')
        #avgWaterButton.triggered.connect(self.display_avg_water_view)
        #avgMenu.addAction(avgWaterButton)

        waveHeightPlotButton = QAction(QIcon('exit24.png'),
                                       'Water Height Plot', self)
        waveHeightPlotButton.setShortcut('Ctrl+H')
        waveHeightPlotButton.setStatusTip('Average Water Height Plot')
        waveHeightPlotButton.triggered.connect(
            self.display_wave_height_plot_view)
        #avgMenu.addAction(waveHeightPlotButton)

        eastVelPlotButton = QAction(QIcon('exit24.png'), 'East Velocity Plot',
                                    self)
        eastVelPlotButton.setShortcut('Ctrl+E')
        eastVelPlotButton.setStatusTip('Earth Velocity [East] Plot ')
        eastVelPlotButton.triggered.connect(self.display_east_vel_plot_view)
        #avgMenu.addAction(eastVelPlotButton)

        northVelPlotButton = QAction(QIcon('exit24.png'),
                                     'North Velocity Plot', self)
        northVelPlotButton.setShortcut('Ctrl+N')
        northVelPlotButton.setStatusTip('Earth Velocity [North] Plot ')
        northVelPlotButton.triggered.connect(self.display_north_vel_plot_view)
        #avgMenu.addAction(northVelPlotButton)

        magVelPlotButton = QAction(QIcon('exit24.png'),
                                   'Velocity Magnitude Plot', self)
        magVelPlotButton.setShortcut('Ctrl+N')
        magVelPlotButton.setStatusTip('Velocity Magnitude Plot ')
        magVelPlotButton.triggered.connect(self.display_mag_plot_view)
        #avgMenu.addAction(magVelPlotButton)

        dirVelPlotButton = QAction(QIcon('exit24.png'), 'Water Direction Plot',
                                   self)
        dirVelPlotButton.setShortcut('Ctrl+N')
        dirVelPlotButton.setStatusTip('Water Direction Plot ')
        dirVelPlotButton.triggered.connect(self.display_dir_plot_view)
        #avgMenu.addAction(dirVelPlotButton)

        # Show the main window
        self.show()

    def start_bokeh_server(self):
        #self.server.io_loop.add_callback(self.server.show, "/")
        #self.server.io_loop.start()
        # Outside the notebook ioloop needs to be started
        loop = IOLoop.current()
        loop.start()

    def openFileNamesDialog(self):
        """
        Open a file names dialog to select multiple files.
        :return:
        """
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        files, _ = QFileDialog.getOpenFileNames(
            self,
            "Open Ensemble Files",
            "",
            "All Files (*);;Ensemble Files (*.ens)",
            options=options)
        return files

    @pyqtSlot()
    def display_setup_view(self):
        self.docked_setup.setFloating(True)
        self.docked_setup.show()

    @pyqtSlot()
    def display_avg_water_view(self):
        self.docked_avg_water.setFloating(True)
        self.docked_avg_water.show()

    @pyqtSlot()
    def display_wave_height_plot_view(self):
        self.AvgWater.docked_wave_height.setFloating(True)
        self.AvgWater.docked_wave_height.show()

    @pyqtSlot()
    def display_east_vel_plot_view(self):
        self.AvgWater.docked_earth_vel_east.setFloating(True)
        self.AvgWater.docked_earth_vel_east.show()

    @pyqtSlot()
    def display_north_vel_plot_view(self):
        self.AvgWater.docked_earth_vel_north.setFloating(True)
        self.AvgWater.docked_earth_vel_north.show()

    @pyqtSlot()
    def display_mag_plot_view(self):
        self.AvgWater.docked_mag.setFloating(True)
        self.AvgWater.docked_mag.show()

    @pyqtSlot()
    def display_dir_plot_view(self):
        self.AvgWater.docked_dir.setFloating(True)
        self.AvgWater.docked_dir.show()

    @pyqtSlot()
    def playback(self):
        """
        Playback files.  Select the files.  Then
        pass them to the manager to read and playback the
        files.
        :return:
        """
        # Select Files
        files = self.openFileNamesDialog()

        logging.debug("Playing back files: " + str(files))

        # Pass files to manager
        if files:
            self.AutoWavesManager.playback_file(files)

    def closeEvent(self, event):
        """
        Generate 'question' dialog on clicking 'X' button in title bar.

        Reimplement the closeEvent() event handler to include a 'Question'
        dialog with options on how to proceed - Close, Cancel buttons
        """
        reply = QtWidgets.QMessageBox.question(
            self, "Message", "Are you sure you want to quit?",
            QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Cancel)

        if reply == QtWidgets.QMessageBox.Close:
            logging.debug("Shutting down")
            self.Setup.shutdown()
            logging.debug("Setup VM shutdown")
            self.Terminal.shutdown()
            logging.debug("Terminal VL shutdown")
            self.AvgWater.shutdown()
            logging.debug("Average Water shutdown")
            self.AutoWavesManager.shutdown()
            logging.debug("Auto Waves Manager shutdown")
            event.accept()
        else:
            event.ignore()