#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# HENCSAT QoS Controller
# Copyright (C) 2017-2024 by Thomas Dreibholz
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Contact: dreibh@simula.no
#

import argparse
import re
import sys
import uuid
from PyQt5.QtCore import (pyqtSignal, Qt, QTranslator, QLocale, QProcess,
                          QByteArray, QTimer)
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTabWidget,
                             QComboBox, QDialog, QDoubleSpinBox, QLabel,
                             QStatusBar, QGridLayout, QGroupBox, QCheckBox,
                             QLCDNumber, QWidget, QSlider, QPushButton)


# ###### Status display for the packets/bytes transmitted ###################
class StatsDisplay(QGroupBox):

   # ====== Constructor =====================================================
   def __init__(self):
      super(StatsDisplay, self).__init__()

      self.isStarting         = True
      self.lastPacketsLeft    = 0
      self.lastBytesLeft      = 0
      self.lastPacketsRight   = 0
      self.lastBytesRight     = 0
      self.offsetPacketsLeft  = 0
      self.offsetBytesLeft    = 0
      self.offsetPacketsRight = 0
      self.offsetBytesRight   = 0

      self.setTitle(self.tr('Router Statistics'))

      self.layout       = QGridLayout()
      self.labelLeft    = QLabel(self.tr('Left'))
      self.labelRight   = QLabel(self.tr('Right'))
      self.labelLeft.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
      self.labelRight.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
      self.labelPackets = QLabel(self.tr('Packets'))
      self.labelBytes   = QLabel(self.tr('Bytes'))

      self.resetStats   = QPushButton(self.tr('Reset'))
      self.resetStats.clicked.connect(self.resetStatistics)

      self.packetsLeft  = QLCDNumber(12)
      self.packetsLeft.display('-')
      self.packetsRight = QLCDNumber(12)
      self.packetsRight.display('-')
      self.bytesLeft    = QLCDNumber(12)
      self.bytesLeft.display('-')
      self.bytesRight   = QLCDNumber(12)
      self.bytesRight.display('-')

      self.packetsLeft.setMinimumWidth(240)
      self.packetsRight.setMinimumWidth(240)
      self.packetsLeft.setMinimumHeight(72)
      self.bytesRight.setMinimumHeight(72)

      self.layout.addWidget(self.resetStats, 0, 0)
      self.layout.addWidget(self.labelPackets, 1, 0)
      self.layout.addWidget(self.labelBytes,   2, 0)
      self.layout.addWidget(self.labelLeft,  0, 1)
      self.layout.addWidget(self.labelRight, 0, 2)

      self.layout.addWidget(self.packetsLeft, 1, 1)
      self.layout.addWidget(self.packetsRight, 1, 2)
      self.layout.addWidget(self.bytesLeft, 2, 1)
      self.layout.addWidget(self.bytesRight, 2, 2)
      self.layout.setColumnStretch(0, 0)
      self.layout.setColumnStretch(1, 1)
      self.layout.setColumnStretch(2, 1)

      self.layout.setRowStretch(0, 0)
      self.layout.setRowStretch(1, 1)
      self.layout.setRowStretch(2, 1)

      self.setLayout(self.layout)

   # ====== Reset statistics ================================================
   def resetStatistics(self):
      sys.stdout.write('Statistics reset!\n')
      self.offsetPacketsLeft  = self.lastPacketsLeft
      self.offsetBytesLeft    = self.lastBytesLeft
      self.offsetPacketsRight = self.lastPacketsRight
      self.offsetBytesRight   = self.lastBytesRight
      self.displayCurrentStatistics()

   # ====== Update statistics ===============================================
   def updateStatistics(self, packetsLeft, bytesLeft, packetsRight, bytesRight):
       self.lastPacketsLeft  = packetsLeft
       self.lastBytesLeft    = bytesLeft
       self.lastPacketsRight = packetsRight
       self.lastBytesRight   = bytesRight
       if ((self.lastPacketsLeft  < self.offsetPacketsLeft)  or
           (self.lastBytesLeft    < self.offsetBytesLeft)    or
           (self.lastPacketsRight < self.offsetPacketsRight) or
           (self.lastBytesRight   < self.offsetBytesRight)   or
           (self.isStarting == True)):
           # The router's QoS setup has been restarted => reset necessary!
           self.isStarting = False
           self.resetStatistics()
       else:
          self.displayCurrentStatistics()

   # ====== Display current statistics ======================================
   def displayCurrentStatistics(self):
       self.packetsLeft.display(self.lastPacketsLeft - self.offsetPacketsLeft)
       self.bytesLeft.display(self.lastBytesLeft - self.offsetBytesLeft)
       self.packetsRight.display(self.lastPacketsRight - self.offsetPacketsRight)
       self.bytesRight.display(self.lastBytesRight - self.offsetBytesRight)


# ###### ValueControl widget ################################################
class ValueControl(QWidget):
   # ====== Signals =========================================================
   valuesChanged = pyqtSignal()

   # ====== Constructor =====================================================
   def __init__(self, layout, rowOffset, columnOffset, labelText, minValue, maxValue, decimals):
      super().__init__()

      self.label = QLabel(labelText)

      self.sliderL = QSlider(Qt.Horizontal)
      self.sliderL.setMinimum(minValue)
      self.sliderL.setMaximum(maxValue)

      self.sliderR = QSlider(Qt.Horizontal)
      self.sliderR.setMinimum(minValue)
      self.sliderR.setMaximum(maxValue)

      self.spinboxL = QDoubleSpinBox()
      self.spinboxL.setMinimum(minValue)
      self.spinboxL.setMaximum(maxValue)
      self.spinboxL.setDecimals(decimals)

      self.spinboxR = QDoubleSpinBox()
      self.spinboxR.setMinimum(minValue)
      self.spinboxR.setMaximum(maxValue)
      self.spinboxR.setDecimals(decimals)
      self.layout = layout
      self.layout.addWidget(self.label,    rowOffset, columnOffset + 0)
      self.layout.addWidget(self.sliderL,  rowOffset, columnOffset + 1)
      self.layout.addWidget(self.spinboxL, rowOffset, columnOffset + 2)
      self.layout.addWidget(self.sliderR,  rowOffset, columnOffset + 3)
      self.layout.addWidget(self.spinboxR, rowOffset, columnOffset + 4)

      self.sliderL.valueChanged.connect(self.updateValueLeft)
      self.sliderR.valueChanged.connect(self.updateValueRight)
      self.spinboxL.valueChanged.connect(self.updateValueLeft)
      self.spinboxR.valueChanged.connect(self.updateValueRight)

   # ====== Get value on left-hand side =====================================
   def getValueLeft(self):
      return self.spinboxL.value()

   # ====== Get value on right-hand side ====================================
   def getValueRight(self):
      return self.spinboxR.value()

   # ====== Update value on left-hand side ==================================
   def updateValues(self, valueL, valueR):
      self.updateValueLeft(valueL)
      self.updateValueRight(valueR)

   # ====== Update value on left-hand side ==================================
   def updateValueLeft(self, value):
      self.spinboxL.setValue(value)
      self.sliderL.setValue(value)
      if self.sliderR.isHidden():   # Symmetric mode
         self.spinboxR.setValue(value)
         self.sliderR.setValue(value)
      self.valuesChanged.emit()

   # ====== Update value on right-hand side =================================
   def updateValueRight(self, value):
      self.spinboxR.setValue(value)
      self.sliderR.setValue(value)
      self.valuesChanged.emit()

   # ====== Change split view mode ==========================================
   def useSymmetricMode(self, symmetricMode):
      if symmetricMode:
         self.sliderR.hide()
         self.spinboxR.hide()
         self.updateValueRight(self.getValueLeft())
      else:
         self.sliderR.show()
         self.spinboxR.show()


# ###### QoS characteristics controls #######################################
class QoSSettings(QGroupBox):
   # ====== Signals =========================================================
   changedSettings = pyqtSignal(str)

   # ====== Constructor =====================================================
   def __init__(self, symmetricMode):
      super(QoSSettings, self).__init__()

       # ------ Some presets ------------------------------------------------
      self.uuid         = str(uuid.uuid4())   # Unique ID for this Controller
      self.minBandwidth = 1
      self.maxBandwidth = 100000   # 100 Mbit/s
      self.maxDelay     = 3000.0   # 3 s
      self.presetList = [
         [ self.tr('Custom Settings'),                    None, None,   None,   None,             None, None,   None,   None ],
         [ 'Local Area Network: Ethernet',   self.maxBandwidth,    0,   0.00,   0.0, self.maxBandwidth,    0,   0.00,   0.0 ],
         [ 'Local Area Network: Wireless (54 Mbit/s)', 54000.0,   10,   1.00,   0.2,           54000.0,   10,   1.00,   0.2 ],
         [ 'Mobile Broadband: UMTS (1 Mbit/s)',         1000.0,   50,   0.01,   0.0,            1000.0,   50,   0.01,   0.0 ],
         [ 'Mobile Broadband: LTE (10 Mbit/s)',        10000.0,   30,   0.10,   0.0,           10000.0,   30,   0.10,   0.0 ],
         [ 'Broadband: Fast DSL (16/16 Mbit/s)',       16000.0,   50,   0.10,   0.0,           16000.0,   50,   0.10,   0.0 ],
         [ 'Broadband: Slow DSL (768/768 Kbit/s)',       768.0,   50,   0.10,   0.0,             768.0,   50,   0.10,   0.0 ],
         [ 'Broadband: Fast ADSL (16/1 Mbit/s)',       16000.0,   50,   0.10,   0.0,            1000.0,   50,   0.10,   0.0 ],
         [ 'Broadband: Slow ADSL (768/256 Kbit/s)',      768.0,   50,   0.10,   0.0,             256.0,   50,   0.10,   0.0 ],
         [ 'Satellite (256 Kbit/s)',                     256.0,  250,   2.00,   1.0,             256.0,  250,   2.00,   1.0 ],
         [ 'Dial-Up: Modem (28.8 Kbit/s)',                28.8,   20,   0.10,   0.1,              28.8,   20,   0.10,   0.1 ],
         [ 'Dial-Up: ISDN (64 Kbit/s)',                   64.0,   10,   0.01,   0.0,              64.0,   10,   0.01,   0.0 ]
      ]
      self.doNotApplySettingsYet = True
      self.symmetricMode         = symmetricMode

      self.setTitle(self.tr('Quality of Service Characteristics'))
      self.layout = QGridLayout()

      self.symmetricModeCheckbox = QCheckBox(self.tr('Symmetric Settings'))
      self.symmetricModeCheckbox.setChecked(self.symmetricMode)
      self.symmetricModeCheckbox.stateChanged.connect(self.useSymmetricMode)
      self.layout.addWidget(self.symmetricModeCheckbox, 0, 0)

      self.labelLeft  = QLabel(self.tr('Left'))
      self.labelRight = QLabel(self.tr('Right'))
      self.labelLeft.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
      self.labelRight.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
      self.layout.addWidget(self.labelLeft,  0, 1, 1, 2)
      self.layout.addWidget(self.labelRight, 0, 3, 1, 2)

      self.bandwidthSetting = ValueControl(self.layout, 1, 0, self.tr('Bandwidth (Kbit/s)'),
                                           self.minBandwidth, self.maxBandwidth, 1)
      self.delaySetting     = ValueControl(self.layout, 2, 0, self.tr('Delay (ms)'),
                                           0, self.maxDelay, 0)
      self.lossRateSetting  = ValueControl(self.layout, 3, 0, self.tr('Packet Loss (%)'),
                                           0, 100, 4)
      self.errorRateSetting = ValueControl(self.layout, 4, 0, self.tr('Packet Error (%)'),
                                           0, 100, 4)

      self.bandwidthSetting.valuesChanged.connect(self.valuesChanged)
      self.delaySetting.valuesChanged.connect(self.valuesChanged)
      self.lossRateSetting.valuesChanged.connect(self.valuesChanged)
      self.errorRateSetting.valuesChanged.connect(self.valuesChanged)

      self.layout.setColumnStretch(0, 0)
      self.layout.setColumnStretch(1, 1)
      self.layout.setColumnStretch(2, 0)
      self.layout.setColumnStretch(3, 1)
      self.layout.setColumnStretch(4, 0)

      self.labelPresets = QLabel(self.tr('Presets'))
      self.comboboxPresets = QComboBox()
      for item in self.presetList:
         self.comboboxPresets.addItem(item[0])
      self.layout.addWidget(self.labelPresets, 5, 0)
      self.layout.addWidget(self.comboboxPresets, 5, 1, 1, 4)
      self.setLayout(self.layout)
      self.comboboxPresets.activated.connect(self.selectedPreset)

      self.settingsString = ""
      self.updateSettings(self.uuid, -1, -1, -1, -1, -1, -1, -1, -1)

      self.useSymmetricMode(self.symmetricMode)
      self.doNotApplySettingsYet = False

   # ====== Change split view mode ==========================================
   def useSymmetricMode(self, symmetricMode):
      self.symmetricMode = symmetricMode
      self.symmetricModeCheckbox.setChecked(self.symmetricMode)
      if self.symmetricMode:
         self.labelLeft.setText(self.tr('Both Sides'))
         self.layout.setColumnStretch(3, 0)
         self.labelRight.hide()
      else:
         self.labelLeft.setText(self.tr('Left'))
         self.layout.setColumnStretch(3, 1)
         self.labelRight.show()
      self.bandwidthSetting.useSymmetricMode(self.symmetricMode)
      self.delaySetting.useSymmetricMode(self.symmetricMode)
      self.lossRateSetting.useSymmetricMode(self.symmetricMode)
      self.errorRateSetting.useSymmetricMode(self.symmetricMode)

   # ====== Update settings =================================================
   def updateSettings(self, id,
                      bandwidthL, delayL, lossL, errorL,
                      bandwidthR, delayR, lossR, errorR):
      if ((bandwidthL > 0) or (id != self.uuid)):
         self.doNotApplySettingsYet = True
         if self.symmetricMode:
            if ( (bandwidthL != bandwidthR) or
                 (delayL     != delayR) or
                 (lossL      != lossR) or
                 (errorL     != errorR) ):
               self.useSymmetricMode(False)
         self.bandwidthSetting.updateValues(bandwidthL, bandwidthR)
         self.delaySetting.updateValues(delayL, delayR)
         self.lossRateSetting.updateValues(lossL, lossR)
         self.errorRateSetting.updateValues(errorL, errorR)
         if self.isEnabled():
            self.applySettings()
         else:
            self.setEnabled(True)
         self.doNotApplySettingsYet = False

      else:
         self.setEnabled(False)

   # ====== User has changed settings -> update settings string =============
   def applySettings(self):
      newSettingsString = str(self.bandwidthSetting.getValueLeft())  + " " + \
                          str(self.delaySetting.getValueLeft())      + " " + \
                          str(self.lossRateSetting.getValueLeft())   + " " + \
                          str(self.errorRateSetting.getValueLeft())  + " " + \
                          str(self.bandwidthSetting.getValueRight()) + " " + \
                          str(self.delaySetting.getValueRight())     + " " + \
                          str(self.lossRateSetting.getValueRight())  + " " + \
                          str(self.errorRateSetting.getValueRight()) + " " + \
                          self.uuid
      if self.settingsString != newSettingsString:
         self.settingsString = newSettingsString
         if self.doNotApplySettingsYet == False:
            print("APPLY: " + self.settingsString)
            self.changedSettings.emit(self.settingsString)
         else:
           print("not yet applying: " + self.settingsString)

   # ====== Values changed ===================================================
   def valuesChanged(self):
      if self.doNotApplySettingsYet == False:
         preset = self.presetList[self.comboboxPresets.currentIndex()]
         if ( (preset[1] != self.bandwidthSetting.getValueLeft()) or
              (preset[2] != self.delaySetting.getValueLeft())     or
              (preset[3] != self.lossRateSetting.getValueLeft())  or
              (preset[4] != self.errorRateSetting.getValueLeft()) or
              (preset[5] != self.bandwidthSetting.getValueRight()) or
              (preset[6] != self.delaySetting.getValueRight())     or
              (preset[7] != self.lossRateSetting.getValueRight())  or
              (preset[8] != self.errorRateSetting.getValueRight()) ):
            self.comboboxPresets.setCurrentIndex(0)   # "User-defined settings"
            self.applySettings()

   # ====== Selected preset =================================================
   def selectedPreset(self, number):
      if number > 0:
         preset = self.presetList[number]
         self.doNotApplySettingsYet = True
         self.bandwidthSetting.updateValues(preset[1], preset[5])
         self.delaySetting.updateValues(preset[2], preset[6])
         self.lossRateSetting.updateValues(preset[3], preset[7])
         self.errorRateSetting.updateValues(preset[4], preset[8])
         self.doNotApplySettingsYet = False
         self.applySettings()



# ###### Main window ########################################################
class QoSController(QWidget):
   def __init__(self, configuration, reconnectDelay):
      super(QoSController, self).__init__()

      self.router         = configuration['router']
      self.sshUser        = configuration['sshUser']
      self.sshHost        = configuration['sshHost']
      self.sshProxy       = configuration['sshProxy']
      self.sshPort        = configuration['sshPort']
      self.reconnectDelay = reconnectDelay

      self.statsDisplay = StatsDisplay()
      self.qosSettings  = QoSSettings(True)
      self.qosSettings.changedSettings.connect(self.requestSettingsUpdate)
      self.statusBar = QStatusBar()

      self.mainLayout = QGridLayout()
      self.mainLayout.addWidget(self.statsDisplay)
      self.mainLayout.addWidget(self.qosSettings)
      self.mainLayout.addWidget(self.statusBar)
      self.setLayout(self.mainLayout)

      self.connectTimer = QTimer()
      self.connectTimer.setSingleShot(True)
      self.connectTimer.timeout.connect(self.connectToRouter)
      self.connectTimer.start(0)

      self.updateTimer = QTimer()
      self.updateTimer.setSingleShot(True)
      self.updateTimer.setInterval(500)
      self.updateTimer.timeout.connect(self.requestStatisticsUpdate)


   # ====== Request statistics update from HENCSAT router ===================
   def requestStatisticsUpdate(self):
       self.sendCommand('HENCSAT-QoS-Setup stats -machinereadable')

   # ====== Request statistics update from HENCSAT router ===================
   def requestSettingsUpdate(self, newSettings):
       self.sendCommand('HENCSAT-QoS-Setup change ' + newSettings + ' -machinereadable')

   # ====== Connect to HENCSAT router =======================================
   def connectToRouter(self):
      self.shellProcess = QProcess()
      self.shellProcess.readyRead.connect(self.shellReadReady)
      self.shellProcess.started.connect(self.shellStarted)
      self.shellProcess.finished.connect(self.shellFinished)
      sshParameters = [ self.sshUser + '@' + self.sshHost, '-p', str(self.sshPort) ]
      if self.sshProxy != None:
         sshParameters.append('-J' + self.sshProxy)
      print(sshParameters)
      self.shellProcess.start('/usr/bin/ssh', sshParameters)
      # self.shellProcess.start('/usr/bin/true')

      self.sendCommand('HENCSAT-QoS-Setup info')
      self.requestStatisticsUpdate()

   # ====== Connect to HENCSAT router =======================================
   def sendCommand(self, command):
      # sys.stdout.write("OUT: " + command + '\n')
      self.shellProcess.writeData(QByteArray((command + '\n').encode('utf-8')) )

   # ====== Connecting to HENCSAT router ====================================
   def shellStarted(self):
      self.statusBar.showMessage(self.tr('Connecting to') + ' ' + self.router +
                                 ' (' + self.sshUser + '@' + self.sshHost + ':' + str(self.sshPort) +
                                 ') ...')

   # ====== Disconnected from HENCSAT router ================================
   def shellFinished(self):
      self.statusBar.showMessage(self.tr('Disconnected from') + ' ' + self.router +
                                 ' (' + self.sshUser + '@' + self.sshHost + ':' + str(self.sshPort) +
                                 ')!')
      self.qosSettings.updateSettings('', -1, -1, -1, -1, -1, -1, -1, -1)
      self.connectTimer.start(self.reconnectDelay)


   # ====== Data to read from HENCSAT router is available ===================
   def shellReadReady(self):
       inputLines = str(self.shellProcess.readAll(), encoding='utf-8').splitlines()
       for inputLine in inputLines:
          # sys.stdout.write("IN:  " + inputLine + '\n')

          # ------ Update settings ------------------------------------------
          m = re.match(r'^(INFO)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9\.]+)\s+([0-9a-zA-Z\.-]+)\s*', inputLine)
          if m != None:
             self.qosSettings.updateSettings(m.group(10),
                                             float(m.group(2)), float(m.group(3)),
                                             float(m.group(4)), float(m.group(5)),
                                             float(m.group(6)), float(m.group(7)),
                                             float(m.group(8)), float(m.group(9)))
          # ------ Update statistics -----------------------------------------
          else:
             m = re.match(r'^(STATS)\s+(\S+)\s+LEFT\s+(\S+)\s+(\d+)\s+(\d+)\s+RIGHT\s+(\S+)\s+(\d+)\s+(\d+)\s*', inputLine)
             if m != None:
                self.statsDisplay.updateStatistics(float(m.group(4)), float(m.group(5)),
                                                   float(m.group(7)), float(m.group(8)))
                self.statusBar.showMessage(self.tr('Connected to') + ' ' + self.router +
                                           ' (' + self.sshUser + '@' + self.sshHost + ':' + str(self.sshPort) +
                                           ').')
                self.updateTimer.start()

             else:
                sys.stdout.write("IN:  " + inputLine + '\n')


# ###### QoSController Main Window ##########################################
class QoSControllerMainWindow(QMainWindow):

   # ====== Constructor =====================================================
   def __init__(self, tabMode, configuration, reconnectDelay):
      super(QoSControllerMainWindow, self).__init__()

      self.reconnectDelay = reconnectDelay

      # ====== Multiple QoSController tabs ==================================
      if tabMode:
         self.tabWidget = QTabWidget()
         self.addControllerTab(configuration)
         self.setCentralWidget(self.tabWidget)

      # ====== Just a single QoSController ==================================
      else:
         self.QoSController = QoSController(configuration, self.reconnectDelay)
         self.setWindowTitle(configuration['router'])
         self.setCentralWidget(self.QoSController)

      self.show()

   # ====== Add QoSController tab ===========================================
   def addControllerTab(self, configuration):
      self.QoSController = QoSController(configuration, self.reconnectDelay)
      self.tabWidget.addTab(self.QoSController, configuration['router'])



# ###### Argument handling helper ###########################################
def makeConfig(router, sshUser, sshHost, sshPort, sshProxy):
   if router == None:
      sys.stderr.write('No router name given (use -router ...)\n')
      sys.exit(1)
   config = {
      'router':    router,
      'sshUser':   sshUser,
      'sshHost':   sshHost,
      'sshPort':   sshPort,
      'sshProxy' : sshProxy
   }
   return config


# ###### Main program #######################################################
if __name__ == '__main__':
   application = QApplication(sys.argv)

   # ====== Handle arguments ================================================
   controllers    = []
   reconnectDelay = 5000
   router         = None
   sshUser        = 'nornetpp'
   sshHost        = 'localhost'
   sshPort        = 22
   sshProxy       = None
   argv = [ str(arg) for arg in application.arguments() ]   # QStringList -> list of strings
   i = 1
   while i < len(argv):
      if i < len(argv) - 1:
         if argv[i] == '-router':
            if router != None:
               controllers.append(makeConfig(router, sshUser, sshHost, sshPort, sshProxy))
               router  = None
               sshUser = 'nornetpp'
               sshHost = 'localhost'
               sshPort = 22
            router = argv[i + 1]
         elif argv[i] == '-sshuser':
            sshUser = argv[i + 1]
         elif argv[i] == '-sshhost':
            sshHost = argv[i + 1]
         elif argv[i] == '-sshproxy':
            sshProxy = argv[i + 1]
         elif argv[i] == '-sshport':
            try:
               sshPort = int(argv[i + 1])
            except Exception:
               sys.stderr.write('Bad port number: ' + argv[i + 1] + '\n')
               sys.exit(1)
         elif argv[i] == '-reconnectdelay':
            try:
               reconnectDelay = int(argv[i + 1])
            except Exception:
               sys.stderr.write('Bad reconnect delay: ' + argv[i + 1] + '\n')
               sys.exit(1)
         else:
            sys.stderr.write('Bad parameter: ' + argv[i] + '\n')
            sys.exit(1)

         i = i + 2

      else:
         i = i + 1

   if router != None:
      controllers.append(makeConfig(router, sshUser, sshHost, sshPort, sshProxy))
   if len(controllers) < 1:
      sys.stderr.write('No routers have been defined!\n')
      sys.exit(1)

   # print(controllers)

   # ====== Initialise translation handling =================================
   locale     = QLocale()
   translator = QTranslator()
   if translator.load(locale, 'hencsat-qos-controller', '_', '.', '.qm') == False:
      translator.load(locale, 'hencsat-qos-controller', '_', '/usr/share/hencsat-controller', '.qm')
   QApplication.installTranslator(translator)

   # ====== Open the main window ============================================
   mainWindow = QoSControllerMainWindow(len(controllers) > 1, controllers[0],
                                        reconnectDelay)
   for i in range(1, len(controllers)):
      mainWindow.addControllerTab(controllers[i])
   mainWindow.show()

   # ====== Main loop =======================================================
   sys.exit(application.exec())
