示例#1
0
    def __init__(self, app=None, db=None):
        super().__init__()
        self._app = app
        self._db = db
        self._dc = DataConverter()

        self._database_path = None
        self._conn = None
        self._cursor = None
示例#2
0
    def __init__(self):
        super().__init__()
        self.add_role_name(name="settings")
        self.add_role_name(name="parameter")
        self.add_role_name(name="type")
        self.add_role_name(name="value")
        self.add_role_name(name="is_active")
        self.add_role_name(name="new_value")
        self.add_role_name(name="delegate_type")
        self.add_role_name(name="model_type")

        self._dc = DataConverter()

        self.populate_model()
示例#3
0
    def __init__(self):

        super().__init__()

        qInstallMessageHandler(FramUtil.qt_msg_handler)

        # self.app = QApplication(sys.argv)

        appGuid = 'F3FF80BA-BA05-4277-8063-82A6DB9245A2'
        self.app = QtSingleApplication(appGuid, sys.argv)
        self.app.setWindowIcon(QtGui.QIcon("resources/ico/hooklogger_v2.ico"))
        splash = FpcSplash()
        splash.show()
        if self.app.isRunning():
            sys.exit(0)

        # qmlRegisterType(FramTreeItem, 'FramTreeItem', 1, 0, 'FramTreeItem')

        self.engine = QQmlApplicationEngine()

        qmlRegisterType(TextFieldDoubleValidator, "FRAM", 1, 0,
                        "TextFieldDoubleValidator")

        self.context = self.engine.rootContext()

        fl = FramLog()
        self.context.setContextProperty('framLog', fl)

        db = HookandlineFpcDB()
        self.context.setContextProperty('db', db)

        # self.textfield_double_validator = TextFieldDoubleValidator()
        # self.context.setContextProperty('TextFieldDoubleValidator', self.textfield_double_validator)

        # PyQt5 Threading approach
        self._rpc_thread = QThread()
        self._rpc_worker = RpcServer()
        self._rpc_worker.moveToThread(self._rpc_thread)
        self._rpc_worker.speciesChanged.connect(self._species_changed)
        self._rpc_thread.started.connect(self._rpc_worker.run)
        self._rpc_thread.start()

        logging.info(f"\tRPC thread and worker established")

        # Technique that works - traditional python threading, although the queue is not needed anymore
        # self._queue = Queue()
        # self._rpc_server = threading.Thread(target=RpcServer, kwargs={'queue': self._queue})
        # self._rpc_server.setDaemon(True)
        # self._rpc_server.start()

        self.fpc_main = FpcMain(app=self)
        self.settings = Settings(db=db)
        self.serial_port_manager = SerialPortManager(app=self, db=db)
        self.serial_port_simulator = SerialPortSimulator(app=self, db=db)
        self.data_converter = DataConverter(app=self, db=db)
        self.sensor_data_feeds = SensorDataFeeds(app=self, db=db)
        self.species_review = SpeciesReview(app=self, db=db)
        self.end_of_site_validation = EndOfSiteValidation(app=self, db=db)
        logging.info(f"\tComponent classes all initialized")

        self.context.setContextProperty('fpcMain', self.fpc_main)
        self.context.setContextProperty('settings', self.settings)
        self.context.setContextProperty('serialPortManager',
                                        self.serial_port_manager)
        self.context.setContextProperty('serialPortSimulator',
                                        self.serial_port_simulator)
        self.context.setContextProperty('dataConverter', self.data_converter)
        self.context.setContextProperty('sensorDataFeeds',
                                        self.sensor_data_feeds)
        self.context.setContextProperty('speciesReview', self.species_review)
        self.context.setContextProperty('endOfSiteValidation',
                                        self.end_of_site_validation)

        logging.info(f"\tContext Properties all set")

        # self.widget = self.app.instance()
        # self.widget.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
        # self.widget.setAttribute(QtCore.Qt.WA_NoSystemBackground)

        # self.view = QQuickView()
        # self.view.setSource(QUrl('qrc:/qml/hookandline/main_fpc.qml'))
        # self.view.show()

        try:
            self.engine.load(QUrl('qrc:/qml/hookandline/main_fpc.qml'))
            splash.close()
            self.win = self.engine.rootObjects()[0]
            self.msg_box = self.win.findChild(QObject, "dlgUnhandledException")

            logging.info(f"\tmain_fpc.qml loaded")

            self.engine.quit.connect(self.app.quit)
            sys.exit(self.app.exec_())

        except Exception as ex:

            logging.error(f"error loading the application: {ex}")
示例#4
0
class SettingsModel(FramListModel):
    def __init__(self):
        super().__init__()
        self.add_role_name(name="settings")
        self.add_role_name(name="parameter")
        self.add_role_name(name="type")
        self.add_role_name(name="value")
        self.add_role_name(name="is_active")
        self.add_role_name(name="new_value")
        self.add_role_name(name="delegate_type")
        self.add_role_name(name="model_type")

        self._dc = DataConverter()

        self.populate_model()

    def populate_model(self):
        """"
        Method to initially populate the model on startup
        """
        self.clear()
        results = SettingsTable.select().where(SettingsTable.is_active == "True")\
            .order_by(SettingsTable.parameter.asc())

        for result in results:
            # item = dict()
            # item["settings_id"] = result.settings
            # item["parameter"] = result.parameter
            # item["value"] = result.value
            item = model_to_dict(result)

            item["delegate_type"] = "TextField"

            if item["parameter"] in [
                    "Leg 1 Start", "Leg 1 End", "Leg 2 Start", "Leg 2 End"
            ]:
                # Convert to mm/dd/yyyy format
                item["value"] = self._dc.iso_to_common_date_format(
                    item["value"])

            elif any(substring in item["parameter"] for substring in [
                    "Leg 1 FPC", "Leg 2 FPC", "Scientist 1", "Scientist 2",
                    "Scientist 3", "Captain", "Second Captain", "Cook",
                    "Deckhand 1", "Deckhand 2", "Deckhand 3"
            ]):

                item["delegate_type"] = "ComboBox"
                if any(substring in item["parameter"] for substring in
                       ["FPC", "Scientist 1", "Scientist 2", "Scientist 3"]):
                    item["model_type"] = "Scientists"
                    is_science_team = "True"

                else:
                    item["model_type"] = "Crew"
                    is_science_team = "False"
                try:
                    person = Personnel.get(
                        Personnel.personnel == item["value"],
                        Personnel.is_science_team == is_science_team)
                    item["value"] = person.last_name + ", " + person.first_name

                except Exception as ex:
                    item["value"] = ""

            elif item["parameter"] in ["Vessel"]:
                item["delegate_type"] = "ComboBox"
                item["model_type"] = "Vessels"
                try:
                    lookup = Lookups.get(Lookups.lookup == item["value"])
                    item["value"] = lookup.description
                except Exception as ex:
                    item["value"] = ""

            elif item["parameter"] in ["Backup Folder"]:
                item["delegate_type"] = "FolderBrowser"

            elif item["parameter"] == "Depth Output Serial Port":
                item["model_type"] = "ComPorts"
                item["delegate_type"] = "DialogListView"

            elif item["parameter"] == "Depth Output Serial Port Baud Rate":
                item["model_type"] = "BaudRates"
                item["delegate_type"] = "DialogListView"

            elif item["parameter"] == "Depth Output Serial Port Status":
                item["delegate_type"] = "Switch"

            item["new_value"] = item["value"]

            self.appendItem(item)

    @pyqtSlot(int, QVariant)
    def update_row(self, index, value):
        """
        Method to update one of the settings rows
        :param index:
        :param value:
        :return:
        """
        if not isinstance(index, int) or index < 0 or index >= self.count:
            logging.error("Error updating a settings row: {0}".format(value))
            return

        if isinstance(value, QJSValue):
            value = value.toVariant()

        # Convert all boolean objects to strings
        value = str(value) if isinstance(value, bool) else value

        # Set to empty string as need and then update the model
        value = value if value else ""

        # Update the model - two fields to update
        self.setProperty(index=index, property="value", value=value)
        self.setProperty(index=index, property="new_value", value=value)

        # Database Storage Conversions
        parameter = self.get(index=index)["parameter"]

        # Set update_db flag
        update_db = True

        # Database Storage Conversions - Dates/Times
        if parameter in [
                "Leg 1 Start", "Leg 1 End", "Leg 2 Start", "Leg 2 End"
        ]:
            if "end" in parameter.lower():
                value += " 23:59:59"
            value = self._dc.time_to_iso_format(value)

        # Names to Foreign Keys in Personnel Table
        elif any(substring in parameter for substring in [
                "Leg 1 FPC", "Leg 2 FPC", "Scientist 1", "Scientist 2",
                "Scientist 3", "Captain", "Second Captain", "Deckhand 1",
                "Deckhand 2", "Deckhand 3"
        ]):

            try:
                if value:
                    logging.info(
                        f"Personnel found: {parameter} >>> value: {value}")

                    last_name, first_name = value.split(",")
                    person = Personnel.get(Personnel.first_name == first_name,
                                           Personnel.last_name == last_name)
                    value = person.personnel
                else:
                    update_db = False
            except Exception as ex:
                value = None
                update_db = False
                logging.error(
                    "Error getting the proper personnel name: {0}".format(ex))

        # Names to Foreign Keys in Lookups Table
        elif parameter in ["Vessel"]:
            try:
                if value:
                    vessel = Lookups.get(Lookups.description == value)
                    value = vessel.lookup
                else:
                    update_db = False
            except Exception as ex:
                value = None
                update_db = False
                logging.error(
                    "Error getting the proper vessel name: {0}".format(ex))

        # Update the database
        try:
            if update_db:
                settings_id = self.get(index=index)["settings"]
                SettingsTable.update(value=value).where(
                    SettingsTable.settings == settings_id).execute()

        except Exception as ex:
            logging.error(
                "Exception updating the settings table: {0}".format(ex))
示例#5
0
class SensorDatabase(QObject):

    errorReceived = pyqtSignal(str, arguments=[
        "msg",
    ])

    def __init__(self, app=None, db=None):
        super().__init__()
        self._app = app
        self._db = db
        self._dc = DataConverter()

        self._database_path = None
        self._conn = None
        self._cursor = None

    def get_sensor_database_old(self, datetime=None):
        """
        Method to retrieve the current sensors database.  Look backwards up to 10 days to try and find a sensors
        database that mataches the given datetime.  Note that a user could start HookLogger and leaving it running
        for multiple days and a sensors database is only created when HookLogger is launched.
        :return:
        """
        if not datetime:
            return

        db_found = False
        datetime = arrow.get(datetime)

        try:

            i = 0
            while i > -10:

                test_day = datetime.shift(days=i)
                file_name = f"sensors_{test_day.format('YYYYMMDD')}.db"

                dir = os.path.abspath(os.path.dirname(sys.argv[0]))
                full_path = os.path.join(dir, "data", file_name)
                logging.info(f"trying to find sensors db at: {full_path}")

                if os.path.exists(full_path):

                    logging.info(f"\t\tsensors db found: {full_path}")
                    self._dbpath = full_path
                    db_found = True
                    break

                i -= 1

        except Exception as ex:

            logging.info(f"Error is getting the date: {ex}")

        if db_found:
            self._conn = sqlite.Connection(self._dbpath)
            self._conn.setbusytimeout(10000)
            self._cursor = self._conn.cursor()

        else:
            logging.info(f"sensor database was not found")

    def get_sensor_database(self, datetime=None):
        """
        Method to retrieve the current sensors database.  Look backwards up to 10 days to try and find a sensors
        database that mataches the given datetime.  Note that a user could start HookLogger and leaving it running
        for multiple days and a sensors database is only created when HookLogger is launched.
        :return:
        """
        if not datetime:
            return

        try:
            status = False
            datetime = arrow.get(datetime).format('YYYYMMDD')
            file_name = f"sensors_{datetime}.db"
            dir = os.path.abspath(os.path.dirname(sys.argv[0]))
            full_path = os.path.join(dir, "data", file_name)
            logging.info(f"trying to find sensors db at: {full_path}")

            if os.path.exists(full_path):

                logging.info(f"\t\tsensors db found: {full_path}")
                self._database_path = full_path
                self._conn = sqlite.Connection(self._database_path)
                self._conn.setbusytimeout(10000)
                self._cursor = self._conn.cursor()
                status = True

        except Exception as ex:

            logging.info(f"Error is getting the date: {ex}")

        return status

    def query_by_datetime(self, datetime, sentences):
        """
        Method to get the given sentence closest to the datetime provided.
        :param datetime:
        :param sentence:
        :return:
        """
        if not self._cursor or not datetime or not sentences:
            logging.info(f"cursor does not exist, returning...")
            return

        try:
            results = []
            msg = ""

            # Craft the sql
            clause = "AND ("
            for i in range(len(sentences)):
                clause += f"RAW_SENTENCE LIKE ? OR "
            clause = f"{clause[:-4]})"
            # sql = f"SELECT * FROM RAW_SENTENCES WHERE STRFTIME('%Y-%m-%dT%H:%M:%S', DATE_TIME) = ? {clause}"
            sql = f"SELECT * FROM RAW_SENTENCES WHERE DATE_TIME BETWEEN ? AND ? {clause} ORDER BY DATE_TIME ASC;"

            # Craft the parameters
            if isinstance(datetime, str):
                datetime = arrow.get(datetime).replace(
                    tzinfo='US/Pacific')  # 20190921 - Added the replace clause
            start_date_time = datetime.isoformat()
            end_date_time = datetime.shift(seconds=+10).isoformat()
            sentences = [f"{x}%" for x in sentences]
            params = [start_date_time, end_date_time] + sentences

            logging.info(f"sql = {sql}")
            logging.info(f"params = {params}")

            results = list(self._cursor.execute(sql, params))
            status = True

        except Exception as ex:

            msg = f"Error querying the sensors db: {ex}"
            logging.error(msg)
            self.errorReceived.emit(msg)

        return results

    def get_updated_event_data(self, datetime):
        """
        Method to retrieve new data for an event.  An event is a drop and this method is called when a user
        decides that the start or end time of a drop is incorrect and it needs to be updated.  This method
        will then find the appropriate sensor database that contains latitude, longitude, and depth information
        corresponding to this provided datetime
        :param datetime:
        :return:
        """
        if not datetime:
            return

        logging.info(f"new datetime for getting sensor data: {datetime}")

        status = False

        # Define the measurements that will be updated by the change in the event start/end time
        measurement_list = ["Latitude - Vessel", "Longitude - Vessel", "Depth"]
        values = {x: {} for x in measurement_list}

        # Get the parsing rules for these particular measurements, i.e. which sentences to retrieve from the sensors db
        measurements = ParsingRules.select(ParsingRules, Lookups) \
            .join(Lookups, on=(ParsingRules.measurement_lu == Lookups.lookup).alias('types')) \
            .where(Lookups.value << measurement_list)

        # Get a unique list of the sentences required to get the items listed in measurement_list
        sentences = []
        for measurement in measurements:
            # logging.info(f"measurement = {measurement.types.value} > {measurement.line_starting}")
            values[measurement.types.
                   value]["sentence"] = measurement.line_starting
            values[measurement.types.
                   value]["position"] = measurement.field_position
            if measurement.line_starting not in sentences:
                sentences.append(measurement.line_starting)
        """
        Find the relevant sensor database for the given datetime.  Remember, sensor databases are daily sqlite files
        with the naming convention sensors_YYYYMMDD.db, however, if someone has left HookLogger running for multiple
        days without shutting it down, the current sensor database could have a name from many days ago, i.e. when
        HookLoggger was last started.  I have actually corrected for this situation such that the database is
        shifted to a new one at midnight       
        """

        dir = os.path.abspath(os.path.dirname(sys.argv[0]))
        data_path = os.path.join(dir, "data")

        db_search_str = r'sensors_.*\.(db)$'
        available_dbs = [
            f for f in os.listdir(data_path) if re.search(db_search_str, f)
        ]
        available_dbs = sorted(available_dbs, reverse=True)

        date_of_interest = arrow.get(datetime).replace(hour=0,
                                                       minute=0,
                                                       second=0,
                                                       tzinfo="US/Pacific")
        for file in available_dbs:

            current_day = arrow.get(
                file.strip("sensors_").strip(".db"),
                "YYYYMMDD").replace(tzinfo="US/Pacific")
            diff = current_day - date_of_interest
            logging.info(
                f"current_day = {current_day},   DOI = {date_of_interest}  >>>> diff.days = {diff.days}"
            )
            if diff.days > 0:
                continue

            full_path = os.path.join(dir, "data", file)
            self._database_path = full_path
            self._conn = sqlite.Connection(self._database_path)
            self._conn.setbusytimeout(10000)
            self._cursor = self._conn.cursor()

            # Query the current sensor database for the sentences listed in sentences
            results = self.query_by_datetime(datetime=datetime,
                                             sentences=sentences)
            logging.info(f"results count = {len(results)}")
            if results:
                results = [x[1] for x in results
                           ]  #  only need the sentences from the results

                # Populate values to be returned
                for k, v in values.items():
                    result = [x for x in results if v["sentence"] in x]
                    if len(result) > 0:
                        logging.info(f"{k} > {result[0]}")
                        fields = result[0].split(",")
                        v["text value"] = fields[v["position"] - 1]
                        if k in ["Latitude - Vessel", "Longitude - Vessel"]:
                            v["hemisphere"] = fields[v["position"]]
                            v["value"] = self._dc.gps_lat_or_lon_to_dd(
                                value_str=v["text value"],
                                hemispshere=v["hemisphere"])
                        else:
                            v["value"] = float(v["text value"])

                status = True
                break

        if self._cursor:
            self._cursor.close()
        if self._conn:
            self._conn.close()

        return status, values