Esempio n. 1
0
    def setup_class(self) -> None:
        #pylint: disable=W0201
        self.appInfo = AppInfo()
        settingsFilenameOrig = os.path.join(self.appInfo.path, "misc/setup/situationboard_default.conf")
        settingsFilename = os.path.join(self.appInfo.path, ".temp/situationboard.conf")
        databaseFilename = os.path.join(self.appInfo.path, ".temp/situationboard.sqlite")

        shutil.copy(settingsFilenameOrig, settingsFilename)

        self.database = Database(databaseFilename, reset = True)

        settings = Settings(settingsFilename, self.appInfo.path)
        settings.setFrontendHeader("header")
        settings.setFrontendNews("news")
        settings.setBoolean(Settings.SECTION_BACKEND, "web_api", True)

        displayPowerManager = DisplayPowerManager(settings)

        self.webSocket = WebSocket(self.appInfo, settings, self.database)

        pluginManager = PluginManager(settings, self.database, self.webSocket, displayPowerManager)

        self.webSocket.init(pluginManager)

        self.appClient = self.webSocket.app_test_client()
        self.socketClient = self.webSocket.socket_test_client(self.appClient)
Esempio n. 2
0
 def __init__(self, appInfo: AppInfo, settings: Settings,
              database: Database):
     super().__init__("situationboard", settings)
     self.appInfo = appInfo
     self.settings = settings
     self.database = database
     self.displayPowerManager = DisplayPowerManager(self.settings)
     self.webSocket = WebSocket(self.appInfo, self.settings, self.database)
     self.pluginManager = PluginManager(self.settings, self.database,
                                        self.webSocket,
                                        self.displayPowerManager)
     signal.signal(signal.SIGTERM, self.__shutdownHandler
                   )  # a SIGTERM signal causes the backend to shutdown
     signal.signal(signal.SIGINT, self.__shutdownHandler
                   )  # a SIGINT signal causes the backend to shutdown
     sys.stdin.close()
Esempio n. 3
0
    def test_handle_event(self) -> None:
        #pylint: disable=W0201
        appInfo = AppInfo()
        settingsFilenameOrig = os.path.join(
            appInfo.path, "misc/setup/situationboard_default.conf")
        settingsFilename = os.path.join(appInfo.path,
                                        ".temp/situationboard.conf")
        databaseFilename = os.path.join(appInfo.path,
                                        ".temp/situationboard.sqlite")

        shutil.copy(settingsFilenameOrig, settingsFilename)

        settings = Settings(settingsFilename, appInfo.path)

        maxLastEvents = 10

        displayPowerManager = DisplayPowerManager(settings)

        db = Database(databaseFilename, reset=True)

        webSocket = WebSocket(appInfo, settings, db)

        pluginManager = PluginManager(settings, db, webSocket,
                                      displayPowerManager)

        webSocket.init(pluginManager)

        # check that creating a database works and yields an empty database
        assert (db.getEventCount(textOnly=False) == 0)
        assert (db.getEventCount(textOnly=True) == 0)
        assert (db.getEvent(1302) is None)
        assert (len(db.getEvents(textOnly=False)) == 0)
        assert (len(db.getEvents(textOnly=True)) == 0)
        assert (len(db.getLastEvents(maxLastEvents, textOnly=False)) == 0)
        assert (len(db.getLastEvents(maxLastEvents, textOnly=True)) == 0)

        # check for valid stats
        assert (db.getEventStats(DatabaseTimespan.TOTAL, textOnly=False) == 0)
        assert (db.getEventStats(DatabaseTimespan.YEAR, textOnly=False) == 0)
        assert (db.getEventStats(DatabaseTimespan.MONTH, textOnly=False) == 0)
        assert (db.getEventStats(DatabaseTimespan.TODAY, textOnly=False) == 0)
        assert (db.getEventStats(DatabaseTimespan.TOTAL, textOnly=True) == 0)
        assert (db.getEventStats(DatabaseTimespan.YEAR, textOnly=True) == 0)
        assert (db.getEventStats(DatabaseTimespan.MONTH, textOnly=True) == 0)
        assert (db.getEventStats(DatabaseTimespan.TODAY, textOnly=True) == 0)

        action = ActionUpdateDatabase("", settings, db, webSocket)

        # check that inserting an alarm event succeeds
        newEvent = self.__createEvent()
        action.handleEvent(newEvent)
        assert (not newEvent.noID)
        assert (db.getEventCount(textOnly=False) == 1)
        assert (db.getEventCount(textOnly=True) == 1)
        assert (len(db.getEvents(textOnly=False)) == 1)
        assert (len(db.getEvents(textOnly=True)) == 1)
        assert (len(db.getLastEvents(maxLastEvents, textOnly=False)) == 1)
        assert (len(db.getLastEvents(maxLastEvents, textOnly=True)) == 1)
        newEventID = newEvent.eventID
        assert (db.getEvent(newEventID) is not None)

        # check for valid stats
        assert (db.getEventStats(DatabaseTimespan.TOTAL, textOnly=False) == 1)
        assert (db.getEventStats(DatabaseTimespan.YEAR, textOnly=False) == 1)
        assert (db.getEventStats(DatabaseTimespan.MONTH, textOnly=False) == 1)
        assert (db.getEventStats(DatabaseTimespan.TODAY, textOnly=False) == 1)
        assert (db.getEventStats(DatabaseTimespan.TOTAL, textOnly=True) == 1)
        assert (db.getEventStats(DatabaseTimespan.YEAR, textOnly=True) == 1)
        assert (db.getEventStats(DatabaseTimespan.MONTH, textOnly=True) == 1)
        assert (db.getEventStats(DatabaseTimespan.TODAY, textOnly=True) == 1)

        # check that updating an alarm event succeeds
        self.__updateEvent(newEvent)
        action.handleEvent(newEvent)
        assert (newEvent.eventID == newEventID)
        assert (db.getEventCount(textOnly=False) == 1)
        assert (db.getEventCount(textOnly=True) == 1)
        assert (len(db.getEvents(textOnly=False)) == 1)
        assert (len(db.getEvents(textOnly=True)) == 1)
        assert (len(db.getLastEvents(maxLastEvents, textOnly=False)) == 1)
        assert (len(db.getLastEvents(maxLastEvents, textOnly=True)) == 1)
        retrievedEvent = db.getEvent(newEventID)
        assert (retrievedEvent is not None)
        assert (retrievedEvent.comment ==
                Test_ActionUpdateDatabase.UPDATED_COMMENT)

        # check for valid stats
        assert (db.getEventStats(DatabaseTimespan.TOTAL, textOnly=False) == 1)
        assert (db.getEventStats(DatabaseTimespan.YEAR, textOnly=False) == 1)
        assert (db.getEventStats(DatabaseTimespan.MONTH, textOnly=False) == 1)
        assert (db.getEventStats(DatabaseTimespan.TODAY, textOnly=False) == 1)
        assert (db.getEventStats(DatabaseTimespan.TOTAL, textOnly=True) == 1)
        assert (db.getEventStats(DatabaseTimespan.YEAR, textOnly=True) == 1)
        assert (db.getEventStats(DatabaseTimespan.MONTH, textOnly=True) == 1)
        assert (db.getEventStats(DatabaseTimespan.TODAY, textOnly=True) == 1)

        db.commitAndClose()
Esempio n. 4
0
class SituationBoardBackend(Module):
    """The SituationBoardBackend class represents the core of the backend service.
    It initializes the different subsystems and implements core application logic
    like the event loop that is responsible for retrieving and handling of alarm events."""
    def __init__(self, appInfo: AppInfo, settings: Settings,
                 database: Database):
        super().__init__("situationboard", settings)
        self.appInfo = appInfo
        self.settings = settings
        self.database = database
        self.displayPowerManager = DisplayPowerManager(self.settings)
        self.webSocket = WebSocket(self.appInfo, self.settings, self.database)
        self.pluginManager = PluginManager(self.settings, self.database,
                                           self.webSocket,
                                           self.displayPowerManager)
        signal.signal(signal.SIGTERM, self.__shutdownHandler
                      )  # a SIGTERM signal causes the backend to shutdown
        signal.signal(signal.SIGINT, self.__shutdownHandler
                      )  # a SIGINT signal causes the backend to shutdown
        sys.stdin.close()

    def __shutdownHandler(
            self, signum: signal.Signals, frame: Optional[Any]) -> None:  #pylint: disable=no-member
        """The __shutdownHandler handles the SIGTERM/SIGINT signal and terminates the backend."""
        self.clrPrint("Terminating backend")
        # database is committed and closed automatically (via atexit)
        sys.exit(0)

    def backgroundTask(self) -> None:
        """Background thread / event loop that retrieves SourceEvents from configured SourceDrivers
        and handles them by passing them to all the configured Action plugins.
        Those plugins in turn may perform various actions (like updating the frontend of web clients).
        When no SourceEvents are retrieved, Action plugins are allowed to perform housekeeping operations instead."""

        sleepDuration = self.settings.getBackendLoopSleepDuration()

        while True:
            # wait for alarms or setting event messages, parse them and handle them
            message = self.pluginManager.retrieveEvent()

            if message is not None:
                # handle source event by triggering all required actions / event handlers ...
                #   new alarm   -> add to database and send an async alarm event to frontend
                #   new setting -> update setting and send an async update event to frontend
                self.pluginManager.handleEvent(message)
            else:
                # perform housekeeping tasks ...
                housekeepingStart = time.time()
                self.pluginManager.handleCyclic()
                housekeepingEnd = time.time()

                # maybe sleep some time ...
                housekeepingDuration = housekeepingEnd - housekeepingStart
                if housekeepingDuration < sleepDuration:
                    remainingDuration = sleepDuration - housekeepingDuration
                    self.webSocket.sleep(round(remainingDuration))

    def run(self) -> None:
        # Read all the configured plugins from the configuration file and initialize them
        self.print("Initializing all configured plugins")
        self.pluginManager.initPlugins()

        # Initialize websocket interface and register all endpoints
        self.print("Initializing websocket interface")
        self.webSocket.init(self.pluginManager)

        # Start the background task with the event loop that retrieves events and handles them
        self.print("Starting background task")
        self.webSocket.start_background_task(self.backgroundTask)

        # Start the webserver and the websocket API for the frontend (does not return)
        self.print("Starting websocket interface")
        self.webSocket.run()
Esempio n. 5
0
class Test_WebSocket:

    def setup_class(self) -> None:
        #pylint: disable=W0201
        self.appInfo = AppInfo()
        settingsFilenameOrig = os.path.join(self.appInfo.path, "misc/setup/situationboard_default.conf")
        settingsFilename = os.path.join(self.appInfo.path, ".temp/situationboard.conf")
        databaseFilename = os.path.join(self.appInfo.path, ".temp/situationboard.sqlite")

        shutil.copy(settingsFilenameOrig, settingsFilename)

        self.database = Database(databaseFilename, reset = True)

        settings = Settings(settingsFilename, self.appInfo.path)
        settings.setFrontendHeader("header")
        settings.setFrontendNews("news")
        settings.setBoolean(Settings.SECTION_BACKEND, "web_api", True)

        displayPowerManager = DisplayPowerManager(settings)

        self.webSocket = WebSocket(self.appInfo, settings, self.database)

        pluginManager = PluginManager(settings, self.database, self.webSocket, displayPowerManager)

        self.webSocket.init(pluginManager)

        self.appClient = self.webSocket.app_test_client()
        self.socketClient = self.webSocket.socket_test_client(self.appClient)

    def teardown_class(self) -> None:
        self.database.commitAndClose()

    def test_get_last_alarm_events(self) -> None:
        self.__emit("get_last_alarm_events", {'count': 10})
        (event, args) = self.__getReceived()
        assert(event == "last_alarm_events")
        assert(args['total_events'] == 0)
        assert(args['alarm_events'] == "[]")

    def test_get_stats(self) -> None:
        self.__emit("get_stats")
        (event, args) = self.__getReceived()
        assert(event == "stats")
        assert(args is not None)
        assert(args['total'] == 0)
        assert(args['year'] == 0)
        assert(args['month'] == 0)
        assert(args['today'] == 0)

    def test_get_header(self) -> None:
        self.__emit("get_header")
        (event, args) = self.__getReceived()
        assert(event == "header")
        assert(args is not None)
        assert(args['header'] == "header")

    def test_get_news(self) -> None:
        self.__emit("get_news")
        (event, args) = self.__getReceived()
        assert(event == "news")
        assert(args is not None)
        assert(args['news'] == "news")

    def test_get_state(self) -> None:
        self.__emit("get_state")
        (event, args) = self.__getReceived()
        assert(event == "state")
        assert(args is not None)
        assert(args['version'] == self.appInfo.version)
        assert(args['start_timestamp'] > 0)
        assert(args['source_state'] == SourceState.OK)

    def test_broadcast_alarm_event(self) -> None:
        alarmEvent = AlarmEvent(1302)
        self.webSocket.broadcastAlarmEvent(alarmEvent)
        (event, args) = self.__getReceived()
        assert(event == "alarm_event")
        assert(args is not None)
        assert(args['eventID'] == 1302)

    def test_broadcast_header(self) -> None:
        self.webSocket.broadcastHeader("TEST\nHEADER")
        (event, args) = self.__getReceived()
        assert(event == "header")
        assert(args is not None)
        assert(args['header'] == "TEST\nHEADER")

    def test_broadcast_news(self) -> None:
        self.webSocket.broadcastNews("TEST\nNEWS")
        (event, args) = self.__getReceived()
        assert(event == "news")
        assert(args is not None)
        assert(args['news'] == "TEST\nNEWS")

    def test_broadcast_database_changed(self) -> None:
        self.webSocket.broadcastDatabaseChanged()
        (event, _) = self.__getReceived()
        assert(event == "database_changed")

    def test_broadcast_calendar_changed(self) -> None:
        self.webSocket.broadcastCalendarChanged()
        (event, _) = self.__getReceived()
        assert (event == "calendar_changed")

    def test_static(self) -> None:
        response = self.appClient.get('/css/situationboard.css')
        assert(response.status_code == 200)

    def test_index(self) -> None:
        response = self.appClient.get('/')
        assert(response.status_code == 200)

    def test_javascript_frontend(self) -> None:
        response = self.appClient.get('/js/situationboard.js')
        assert(response.status_code == 200)

    def test_javascript_settings(self) -> None:
        response = self.appClient.get('/js/frontend/util/settings.js')
        assert(response.status_code == 200)

    def test_api_stats(self) -> None:
        response = self.appClient.get('/api/v1/stats')
        assert(response.status_code == 200)
        assert(response.json['result'] == "ok")
        stats = response.json['stats']
        assert(stats['total'] == 0)
        assert(stats['year'] == 0)
        assert(stats['month'] == 0)
        assert(stats['today'] == 0)

    def test_api_state(self) -> None:
        response = self.appClient.get('/api/v1/state')
        assert(response.status_code == 200)
        assert(response.json['result'] == "ok")
        state = response.json['state']
        assert(state['version'] == self.appInfo.version)
        assert(state['start_timestamp'] > 0)
        assert(state['source_state'] == SourceState.OK)

    def __emit(self, event: str, args: Optional[Dict[str, Any]] = None) -> None:
        if args is not None:
            self.socketClient.emit(event, args, namespace=WebSocket.NS)
        else:
            self.socketClient.emit(event, namespace=WebSocket.NS)

    def __getReceived(self) -> Tuple[Any, Any]:
        response = self.socketClient.get_received(WebSocket.NS)
        assert(len(response) == 1)
        result = response[0]
        event = result['name']
        argsList = result['args']
        asgsListLen = len(argsList)
        assert(asgsListLen <= 1)
        if asgsListLen == 1:
            args = argsList[0]
            return (event, args)

        return (event, None)
class Test_ActionUpdateFrontend:
    def setup_class(self) -> None:
        #pylint: disable=W0201
        appInfo = AppInfo()
        settingsFilenameOrig = os.path.join(
            appInfo.path, "misc/setup/situationboard_default.conf")
        settingsFilename = os.path.join(appInfo.path,
                                        ".temp/situationboard.conf")
        databaseFilename = os.path.join(appInfo.path,
                                        ".temp/situationboard.sqlite")

        shutil.copy(settingsFilenameOrig, settingsFilename)

        self.database = Database(databaseFilename, reset=True)

        settings = Settings(settingsFilename, appInfo.path)
        settings.setFrontendHeader("header")
        settings.setFrontendNews("news")

        displayPowerManager = DisplayPowerManager(settings)

        self.webSocket = WebSocket(appInfo, settings, self.database)

        pluginManager = PluginManager(settings, self.database, self.webSocket,
                                      displayPowerManager)

        self.webSocket.init(pluginManager)

        appClient = self.webSocket.app_test_client()
        self.socketClient = self.webSocket.socket_test_client(appClient)

        self.action = ActionUpdateFrontend("", settings, self.webSocket)

    def teardown_class(self) -> None:
        self.database.commitAndClose()

    def test_handle_event_alarm(self) -> None:
        alarmEvent = AlarmEvent(1302)
        self.action.handleEvent(alarmEvent)
        (event, args) = self.__getReceived()
        assert (event == "alarm_event")
        assert (args is not None)
        assert (args['eventID'] == 1302)

    def test_handle_event_setting_header(self) -> None:
        settingEvent = SettingEvent()
        settingEvent.key = "header"
        settingEvent.value = "new header"
        self.action.handleEvent(settingEvent)
        (event, args) = self.__getReceived()
        assert (event == "header")
        assert (args is not None)
        assert (args['header'] == "new header")

    def test_handle_event_setting_news(self) -> None:
        settingEvent = SettingEvent()
        settingEvent.key = "news"
        settingEvent.value = "new news"
        self.action.handleEvent(settingEvent)
        (event, args) = self.__getReceived()
        assert (event == "news")
        assert (args is not None)
        assert (args['news'] == "new news")

    def __getReceived(self) -> Tuple[Any, Any]:
        response = self.socketClient.get_received(WebSocket.NS)
        assert (len(response) == 1)
        result = response[0]
        event = result['name']
        argsList = result['args']
        asgsListLen = len(argsList)
        assert (asgsListLen <= 1)
        if asgsListLen == 1:
            args = argsList[0]
            return (event, args)

        return (event, None)