commit 21e4cc2fcde9965678073d1be7a9a926001dec0c
Author: Martin Forejt <mforejt@students.zcu.cz>
Date:   Fri May 14 10:23:38 2021 +0000

    Feature/8893 loggin to file

diff --git a/aswi2021vochomurka/app.py b/aswi2021vochomurka/app.py
index 95d3d70..ea63143 100644
--- a/aswi2021vochomurka/app.py
+++ b/aswi2021vochomurka/app.py
@@ -1,3 +1,5 @@
+import logging
+
 from PyQt5.QtWidgets import QApplication
 
 from aswi2021vochomurka.view.main_view import MainView
@@ -5,6 +7,27 @@ from aswi2021vochomurka.view.main_view import MainView
 
 class Application(QApplication):
     def __init__(self, sys_argv):
+        init_logger()
         super(Application, self).__init__(sys_argv)
+        logging.info('App started')
         self.main_view = MainView()
         self.main_view.show()
+
+
+def init_logger():
+    logging.basicConfig(
+        level=logging.DEBUG,
+        filename='data/app.log',
+        format='%(asctime)s %(name)s: %(levelname)-8s %(message)s',
+        datefmt='%y-%m-%d %H:%M',
+        filemode='w+'
+    )
+
+    formatter = logging.Formatter('%(asctime)s %(name)s: %(levelname)-8s %(message)s')
+    console = logging.StreamHandler()
+    console.setFormatter(formatter)
+    console.setLevel(logging.INFO)
+    logging.getLogger('').addHandler(console)
+
+    logging.getLogger('apscheduler').setLevel(logging.WARNING)
+    logging.getLogger('matplotlib').setLevel(logging.WARNING)
diff --git a/aswi2021vochomurka/main.py b/aswi2021vochomurka/main.py
index 01e90cc..a50d5ee 100644
--- a/aswi2021vochomurka/main.py
+++ b/aswi2021vochomurka/main.py
@@ -4,7 +4,6 @@ from aswi2021vochomurka.app import Application
 
 
 def start():
-    print("hello world Vochomurka")
     app = Application(sys.argv)
     sys.exit(app.exec_())
 
diff --git a/aswi2021vochomurka/model/Message.py b/aswi2021vochomurka/model/Message.py
index 69ec3ee..3e70281 100644
--- a/aswi2021vochomurka/model/Message.py
+++ b/aswi2021vochomurka/model/Message.py
@@ -7,3 +7,7 @@ class Message(RecordClass):
     date: str
     time: str
     value: float
+
+    def __str__(self) -> str:
+        return 'topic=' + self.topic + ', datetime=' + self.date + 'T' + self.time + ', index=' + str(
+            self.index) + ', value=' + str(self.value)
diff --git a/aswi2021vochomurka/service/file_manager.py b/aswi2021vochomurka/service/file_manager.py
index 12d4fc3..7f29c4a 100644
--- a/aswi2021vochomurka/service/file_manager.py
+++ b/aswi2021vochomurka/service/file_manager.py
@@ -1,3 +1,4 @@
+import logging
 import os
 import time
 
@@ -26,13 +27,14 @@ class FileManager:
 
     def __init__(self, topic: str, message: Message):
         self.topic = topic
+        logging.debug('opening file ' + self.topic)
 
         try:
             filename = create_filename(message)
             os.makedirs(os.path.dirname(filename), exist_ok=True)
             self.file = open(filename, "w+")
         except Exception as error:
-            print(error)
+            logging.warning(error)
             raise error
         self.write(message)
 
@@ -41,5 +43,6 @@ class FileManager:
         self.lastUpdate = time.time()
 
     def close(self):
+        logging.debug('closing file ' + self.topic)
         self.file.flush()
         self.file.close()
diff --git a/aswi2021vochomurka/service/message_parser.py b/aswi2021vochomurka/service/message_parser.py
index 4d37064..7c9a81a 100644
--- a/aswi2021vochomurka/service/message_parser.py
+++ b/aswi2021vochomurka/service/message_parser.py
@@ -1,3 +1,5 @@
+import logging
+
 from paho.mqtt.client import MQTTMessage
 
 from aswi2021vochomurka.model.Message import Message
@@ -10,6 +12,7 @@ class ParseException(Exception):
 def parse_mqtt_message(message: MQTTMessage) -> Message:
     data = message.payload.decode("utf-8")
     parts = data.split(";")
+    logging.debug('Parsing message: ' + data + ', parts: ' + str(len(parts)))
     if len(parts) != 4:
         raise ParseException
 
diff --git a/aswi2021vochomurka/service/mqtt/mqtt_subscriber.py b/aswi2021vochomurka/service/mqtt/mqtt_subscriber.py
index 226331e..be7b979 100644
--- a/aswi2021vochomurka/service/mqtt/mqtt_subscriber.py
+++ b/aswi2021vochomurka/service/mqtt/mqtt_subscriber.py
@@ -1,3 +1,5 @@
+import logging
+
 import paho.mqtt.client as mqtt
 
 from aswi2021vochomurka.service.message_parser import parse_mqtt_message, ParseException
@@ -8,29 +10,30 @@ class MQTTSubscriber(Subscriber):
 
     # The callback for when the client receives a CONNACK response from the server.
     def on_connect(self, client, userdata, flags, rc, properties=None):
-        print("Connected with result code " + str(rc))
+        logging.info('Connected with result code ' + str(rc))
         self.callback.onConnected()
 
         # Subscribing in on_connect() means that if we lose the connection and
         # reconnect then subscriptions will be renewed.
         for topic in self.params.topics:
-            print("Subscribed to topic: " + topic)
+            logging.info('Subscribed to topic: ' + topic)
             client.subscribe(topic)
 
     # The callback for when a PUBLISH message is received from the server.
     def on_message(self, client, userdata, message: mqtt.MQTTMessage):
         try:
             m = parse_mqtt_message(message)
-            print(m)
+            logging.info('Message: ' + str(m))
             self.callback.onMessage(m)
             self.write_to_file(m)
         except ParseException as error:
-            print('invalid message data format')
+            logging.warning('Invalid message data format of: ' + message.payload.decode('utf-8'))
+            logging.info('Unsubscribed topic: ' + message.topic)
             client.unsubscribe(message.topic)
-            # TODO better reaction on bad format
             pass
 
     def on_disconnect(self, client, userdata, rc):
+        logging.info('Disconnected')
         self.callback.onDisconnected()
         self.stop()
 
@@ -40,18 +43,22 @@ class MQTTSubscriber(Subscriber):
         client.on_connect = self.on_connect
         client.on_message = self.on_message
         client.on_disconnect = self.on_disconnect
+        logging.info('Setting up client')
 
         if not self.params.anonymous:
+            logging.info('Using credentials, username=' + self.params.username + ', password=' + self.params.password)
             client.tls_set()
             client.username_pw_set(self.params.username, self.params.password)
 
         try:
+            logging.info('Connecting to: ' + self.params.connection.host + ':' + str(self.params.connection.port))
             client.connect(
                 self.params.connection.host,
                 self.params.connection.port,
                 self.params.connection.timeout
             )
         except Exception as error:
+            logging.warning('Cannot connect')
             self.callback.onError()
             return
 
diff --git a/aswi2021vochomurka/view/logger_view.py b/aswi2021vochomurka/view/logger_view.py
new file mode 100644
index 0000000..2ed4231
--- /dev/null
+++ b/aswi2021vochomurka/view/logger_view.py
@@ -0,0 +1,30 @@
+import logging
+
+from PyQt5.QtCore import pyqtSignal, QObject
+from PyQt5.QtWidgets import QPlainTextEdit
+
+
+class LoggerView(logging.Handler, QObject):
+    append = pyqtSignal(str)
+
+    def __init__(self, parent):
+        super().__init__()
+        super(QObject, self).__init__()
+
+        self.widget = QPlainTextEdit(parent)
+        self.widget.setReadOnly(True)
+        self.widget.setFixedHeight(120)
+        self.append.connect(
+            lambda msg: self.appendMessage(msg)
+        )
+
+    def emit(self, record):
+        msg = self.format(record)
+        self.append.emit(msg)
+
+    def appendMessage(self, msg):
+        self.widget.appendPlainText(msg)
+        self.widget.verticalScrollBar().setValue(self.widget.verticalScrollBar().maximum())
+
+    def write(self, m):
+        pass
diff --git a/aswi2021vochomurka/view/main_view.py b/aswi2021vochomurka/view/main_view.py
index d613760..a769968 100644
--- a/aswi2021vochomurka/view/main_view.py
+++ b/aswi2021vochomurka/view/main_view.py
@@ -1,3 +1,4 @@
+import logging
 import math
 import random
 
@@ -23,6 +24,8 @@ from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as Navigatio
 import matplotlib.pyplot as plt
 import random
 
+from aswi2021vochomurka.view.logger_view import LoggerView
+
 
 class Worker(QObject, SubscriberCallback):
     connected = pyqtSignal()
@@ -80,11 +83,15 @@ class MainView(QDialog):
         self.setMinimumSize(QSize(440, 240))
         self.setWindowTitle("MQTT demo")
 
-        self.b = QPlainTextEdit(self)
-        self.b.insertPlainText("App started...\n")
+        # Add logger text field
+        logger = LoggerView(self)
+        formatter = logging.Formatter('%(asctime)s %(message)s', '%H:%M')
+        logger.setFormatter(formatter)
+        logger.setLevel(logging.INFO)
+        logging.getLogger('').addHandler(logger)
 
         layout = QVBoxLayout()
-        layout.addWidget(self.b)
+        layout.addWidget(logger.widget)
         layout.addWidget(self.toolbar)
         layout.addWidget(self.canvas)
 
@@ -116,8 +123,8 @@ class MainView(QDialog):
         self.worker = Worker()
         self.worker.moveToThread(self.workerThread)
         self.workerThread.started.connect(self.worker.start)
-        self.worker.newMessage.connect(
-            lambda message: self.b.insertPlainText(message + "\n")
-        )
+        # self.worker.newMessage.connect(
+        #    lambda message: self.b.insertPlainText(message + "\n")
+        # )
         self.worker.window = self
         self.workerThread.start()
