Example #1
0
    def Connect_clicked(self):
        if self.connected:
            self.ui.AutoRead.setChecked(False)
            self.sportiduino.disconnect()
            self.log('\n' + self.tr("Master station is disconnected"))
            self.connected = False
            self.ui.Connect.setText(_translate("MainWindow", "Connect"))
        else:
            port = self.ui.choiseCom.currentText()
            try:
                if (port == QCoreApplication.translate("MainWindow", "auto")):
                    self.sportiduino = Sportiduino(
                        debug=True, translator=QCoreApplication.translate)
                else:
                    self.sportiduino = Sportiduino(port, debug=True)

                curPass = (self.ui.sbCurPwd1.value(),
                           self.ui.sbCurPwd2.value(),
                           self.ui.sbCurPwd3.value())
                self._apply_pwd(curPass)

                #self.sportiduino.beep_ok()
                self.log(
                    '\n' +
                    self.tr("Master station {} on port {} is connected").
                    format(self.sportiduino.version, self.sportiduino.port))
                self.ui.Connect.setText(_translate("MainWindow", "Disconn."))
                self.connected = True

                self.read_ms_config()

            except Exception as err:
                self._process_error(err)
Example #2
0
    def on_initCardButton_clicked(self):
        if not self._check_connection():
            return
        try:
            self.log("\n" + self.tr("Initialize the participant card"))

            card_num = self.ui.sbCardNumber.value()

            if (card_num < Sportiduino.MIN_CARD_NUM
                    or card_num > Sportiduino.MAX_CARD_NUM):
                raise Exception(self.tr("Incorrect card number"))

            enable_fast_punch = False
            if self.ui.cbFastPunchCard.isChecked():
                enable_fast_punch = True
            code, data = self.sportiduino.init_card(
                card_num, fast_punch=enable_fast_punch)
            if code == Sportiduino.RESP_OK:
                self.log(
                    self.
                    tr("The participant card No {} ({}) has been initialized successfully"
                       ).format(card_num, Sportiduino.card_name(data[0])))
                if self.ui.cbAutoIncriment.isChecked():
                    self.ui.sbCardNumber.setValue(card_num + 1)

        except Exception as err:
            self._process_error(err)
Example #3
0
    def ReadInfo_clicked(self):
        if not self._check_connection():
            return

        try:
            self.log("\n" +
                     self.tr("Read the card contained a base station state"))

            state = self.sportiduino.read_state_card()

            bs_state = BaseStation.State()
            bs_state.version = Sportiduino.Version(*state['version'])
            bs_state.config = BaseStation.Config.unpack(state['config'])

            bs_state.battery = BaseStation.Battery(state['battery'])
            bs_state.mode = state['mode']

            bs_state.timestamp = state['timestamp']
            bs_state.wakeuptime = state['wakeuptime']

            self._show_base_station_state(bs_state)

        except Exception as err:
            self._process_error(err)
            raise err
Example #4
0
    def InitCard_clicked(self):
        if not self._check_connection():
            return
        try:
            self.log("\n" + self.tr("Initialize the participant card"))

            card_num = 0
            text = self.ui.cardLine.text()
            if (text.isdigit()):
                card_num = int(text)

            if (card_num < Sportiduino.MIN_CARD_NUM
                    or card_num > Sportiduino.MAX_CARD_NUM):
                raise Exception(self.tr("Incorrect card number"))

            code, data = self.sportiduino.init_card(card_num)
            if code == Sportiduino.RESP_OK:
                self.log(
                    self.
                    tr("The participant card N{} ({}) has been initialized successfully"
                       ).format(card_num, Sportiduino.card_name(data[0])))

            if self.ui.AutoIncriment.isChecked():
                self.ui.cardLine.setText(str(card_num + 1))

        except Exception as err:
            self._process_error(err)
            raise err
Example #5
0
    def _preprocess_response(cls, resp_code, data):
        if resp_code == cls.SERIAL_RESP_STATUS:
            err_code = data[0]
            if err_code == cls.SERIAL_ERROR_FUNC:
                raise SportiduinoException(
                    Sportiduino._translate("sportiduino",
                                           "Invalid function code"))
            elif err_code == cls.SERIAL_ERROR_CRC:
                raise SportiduinoException(
                    Sportiduino._translate("sportiduino",
                                           "Checksum mismatch in the request"))
            elif err_code == cls.SERIAL_ERROR_SIZE:
                raise SportiduinoException(
                    Sportiduino._translate("sportiduino",
                                           "Invalid size of the request"))
            elif err_code == cls.SERIAL_ERROR_PWD:
                raise SportiduinoException(
                    Sportiduino._translate("sportiduino", "Invalid password"))

        return resp_code, data
Example #6
0
    def read_info_by_serial(cls, port, password):
        params = b''
        params += int2byte(password[0])
        params += int2byte(password[1])
        params += int2byte(password[2])

        resp_code, data = cls._send_command(port,
                                            cls.SERIAL_FUNC_READ_INFO,
                                            params,
                                            timeout=8)
        if resp_code == cls.SERIAL_RESP_INFO:
            state = cls.State()
            state.version = Sportiduino.Version(*data[0:3])
            state.config = cls.Config.unpack(data[3:9])

            state.battery = cls.Battery(byte2int(data[9]))
            state.mode = byte2int(data[10])

            state.timestamp = datetime.fromtimestamp(
                Sportiduino._to_int(data[11:15]))
            state.wakeuptime = datetime.fromtimestamp(
                Sportiduino._to_int(data[15:19]))
            return state
Example #7
0
    def ReadCard_clicked(self):
        if not self._check_connection():
            return

        try:
            self.log("\n" + self.tr("Read a card"))

            card_type = self.sportiduino.read_card_type()
            try:
                data = self.sportiduino.read_card(timeout=0.2)
            except SportiduinoTimeout:
                data = Sportiduino.raw_data_to_card_data(
                    self.sportiduino.read_card_raw())
            else:
                self.sportiduino.beep_ok()

            self._show_card_data(data, card_type)
            self._save_card_data_to_csv(data)

        except Exception as err:
            self._process_error(err)
Example #8
0
class BaseStation(object):
    MODE_ACTIVE = 0
    MODE_WAIT = 1
    MODE_SLEEP = 2

    # UART
    SERIAL_MSG_START = b'\xFA'

    SERIAL_FUNC_READ_INFO = b'\xF0'
    SERIAL_FUNC_WRITE_SETTINGS = b'\xF1'
    SERIAL_FUNC_ERASE_LOG = b'\xF2'

    SERIAL_RESP_STATUS = b'\x01'
    SERIAL_RESP_INFO = b'\x02'

    SERIAL_OK = 0x00
    SERIAL_ERROR_CRC = 0x01
    SERIAL_ERROR_FUNC = 0x02
    SERIAL_ERROR_SIZE = 0x03
    SERIAL_ERROR_PWD = 0x04

    ANTENNA_GAIN_18DB = 0x02
    ANTENNA_GAIN_23DB = 0x03
    ANTENNA_GAIN_33DB = 0x04
    ANTENNA_GAIN_38DB = 0x05
    ANTENNA_GAIN_43DB = 0x06
    ANTENNA_GAIN_48DB = 0x07

    _serialproto = Sportiduino.SerialProtocol(SERIAL_MSG_START, print_)

    class Battery(object):
        def __init__(self, byte=None):
            self.voltage = None
            self.isOk = False

            if byte is None:
                return

            if byte == 0 or byte == 1:
                # Old firmware
                self.isOk = bool(byte)
            else:
                self.voltage = byte / 50.0
                if self.voltage > 3.6:
                    self.isOk = True

    class Config(object):
        def __init__(self):
            self.num = 0
            self.active_mode_duration = 2  # hours
            self.start_as_check = False
            self.check_card_init_time = False
            self.autosleep = False
            self.enable_fast_punch = False
            self.antenna_gain = BaseStation.ANTENNA_GAIN_33DB
            self.password = [0, 0, 0]

        @classmethod
        def unpack(cls, config_data):
            config = cls()
            config.num = byte2int(config_data[0])

            active_mode_bits = config_data[1] & 0x7
            config.active_mode_duration = byte2int(active_mode_bits)

            config.start_as_check = config_data[1] & 0x08 > 0
            config.check_card_init_time = config_data[1] & 0x10 > 0
            config.autosleep = config_data[1] & 0x20 > 0
            config.enable_fast_punch = config_data[1] & 0x80 > 0

            config.antenna_gain = byte2int(config_data[2])
            return config

        def pack(self):
            config_data = b''
            config_data += int2byte(self.num)

            flags = self.active_mode_duration

            if self.start_as_check:
                flags |= 0x08
            if self.check_card_init_time:
                flags |= 0x10
            if self.autosleep:
                flags |= 0x20
            if self.enable_fast_punch:
                flags |= 0x80
            config_data += int2byte(flags)
            config_data += int2byte(self.antenna_gain)
            config_data += int2byte(self.password[0])
            config_data += int2byte(self.password[1])
            config_data += int2byte(self.password[2])

            return config_data

    class State(object):
        def __init__(self):
            self.version = Sportiduino.Version(0)
            self.config = BaseStation.Config()
            self.mode = BaseStation.MODE_ACTIVE
            self.battery = BaseStation.Battery()
            self.timestamp = 0

    @classmethod
    def read_info_by_serial(cls, port, password):
        params = b''
        params += int2byte(password[0])
        params += int2byte(password[1])
        params += int2byte(password[2])

        resp_code, data = cls._send_command(port,
                                            cls.SERIAL_FUNC_READ_INFO,
                                            params,
                                            timeout=8)
        if resp_code == cls.SERIAL_RESP_INFO:
            state = cls.State()
            state.version = Sportiduino.Version(*data[0:3])
            state.config = cls.Config.unpack(data[3:9])

            state.battery = cls.Battery(byte2int(data[9]))
            state.mode = byte2int(data[10])

            state.timestamp = datetime.fromtimestamp(
                Sportiduino._to_int(data[11:15]))
            state.wakeuptime = datetime.fromtimestamp(
                Sportiduino._to_int(data[15:19]))
            return state

    @classmethod
    def write_settings_by_serial(cls, port, password, config, wakeuptime):
        params = b''
        params += int2byte(password[0])
        params += int2byte(password[1])
        params += int2byte(password[2])
        params += config.pack()

        utc = datetime.utcnow()
        params += int2byte(utc.year - 2000)
        params += int2byte(utc.month)
        params += int2byte(utc.day)
        params += int2byte(utc.hour)
        params += int2byte(utc.minute)
        params += int2byte(utc.second)

        params += int2byte(wakeuptime.year - 2000)
        params += int2byte(wakeuptime.month)
        params += int2byte(wakeuptime.day)
        params += int2byte(wakeuptime.hour)
        params += int2byte(wakeuptime.minute)
        params += int2byte(wakeuptime.second)

        params += int2byte(cls.MODE_WAIT)

        cls._send_command(port,
                          cls.SERIAL_FUNC_WRITE_SETTINGS,
                          parameters=params,
                          timeout=8)

    @classmethod
    def erase_log_by_serial(cls, port):
        cls._send_command(port, cls.SERIAL_FUNC_ERASE_LOG, timeout=12)

    @classmethod
    def _send_command(cls,
                      port,
                      code,
                      parameters=None,
                      wait_response=True,
                      timeout=None):
        timeout = timeout if timeout is not None else 1
        serial = Serial(port, baudrate=9600, timeout=timeout)
        # Wakeup station
        serial.write(b'\xff')
        resp_code, data = cls._serialproto.send_command(
            serial, code, parameters, wait_response)
        serial.close()
        return cls._preprocess_response(resp_code, data)

    @classmethod
    def _preprocess_response(cls, resp_code, data):
        if resp_code == cls.SERIAL_RESP_STATUS:
            err_code = data[0]
            if err_code == cls.SERIAL_ERROR_FUNC:
                raise SportiduinoException(
                    Sportiduino._translate("sportiduino",
                                           "Invalid function code"))
            elif err_code == cls.SERIAL_ERROR_CRC:
                raise SportiduinoException(
                    Sportiduino._translate("sportiduino",
                                           "Checksum mismatch in the request"))
            elif err_code == cls.SERIAL_ERROR_SIZE:
                raise SportiduinoException(
                    Sportiduino._translate("sportiduino",
                                           "Invalid size of the request"))
            elif err_code == cls.SERIAL_ERROR_PWD:
                raise SportiduinoException(
                    Sportiduino._translate("sportiduino", "Invalid password"))

        return resp_code, data
Example #9
0
 def __init__(self):
     self.version = Sportiduino.Version(0)
     self.config = BaseStation.Config()
     self.mode = BaseStation.MODE_ACTIVE
     self.battery = BaseStation.Battery()
     self.timestamp = 0
Example #10
0
    def _show_card_data(self, data, card_type=None):
        if self.ui.AutoPrint.isChecked():
            self.ClearText_clicked()

        text = []
        if card_type is not None:
            card_name = Sportiduino.card_name(card_type)
            text.append(card_name)

        if 'master_card_flag' in data:
            # show master card info
            master_type = int2byte(data['master_card_type'])

            if master_type == Sportiduino.MASTER_CARD_GET_STATE:
                text.append(
                    self.tr("Master card to get info about a base station"))
            elif master_type == Sportiduino.MASTER_CARD_SET_TIME:
                text.append(
                    self.tr("Master card to set time of a base station"))
            elif master_type == Sportiduino.MASTER_CARD_SET_NUMBER:
                text.append(
                    self.tr("Master card to set number of a base station"))
            elif master_type == Sportiduino.MASTER_CARD_SLEEP:
                text.append(self.tr("Master card to sleep a base station"))
            elif master_type == Sportiduino.MASTER_CARD_READ_BACKUP:
                text.append(
                    self.tr(
                        "Master card to get punches log of a base station"))
            elif master_type == Sportiduino.MASTER_CARD_SET_PASS:
                text.append(
                    self.
                    tr("Master card to write password and settings to a base station"
                       ))
            else:
                text.append(self.tr("Uninitialized card"))

        else:
            # show participant card info
            card_number = data['card_number']
            init_time = -1
            if 'init_timestamp' in data:
                init_time = (data['init_timestamp'])

            if init_time != 0 and card_number >= Sportiduino.MIN_CARD_NUM and card_number <= Sportiduino.MAX_CARD_NUM:
                punches_count = 0

                text.append(
                    self.tr("Participant card No {}").format(card_number))
                if init_time > 0:
                    text.append(
                        self.tr("Init time {}").format(
                            datetime.fromtimestamp(init_time)))

                text.append(self.tr("Punches (Check point - Time):"))
                punch_str = "{:>5} - {}"
                if 'start' in data:
                    text.append(
                        punch_str.format(self.tr("Start"), data["start"]))

                punches = data['punches']
                for punch in punches:
                    punches_count += 1

                    cp = punch[0]
                    cp_time = punch[1]

                    text.append(punch_str.format(cp, cp_time))

                if 'finish' in data:
                    text.append(
                        punch_str.format(self.tr("Finish"), data["finish"]))

                if punches_count == 0:
                    text.append(self.tr("No punches"))
                else:
                    text.append(
                        self.tr("Total punches {}").format(punches_count))
            else:
                text.append(self.tr("Uninitialized card"))

        self.log('\n'.join(text))

        if self.ui.AutoPrint.isChecked():
            self.Print_clicked()
Example #11
0
class SportiduinoPqMainWindow(QtWidgets.QMainWindow):
    def __init__(self, config):
        super().__init__()
        self.ui = design.Ui_MainWindow()
        self.ui.setupUi(self)

        self.setWindowTitle(
            "SportiduinoPQ {}".format(sportiduinopq_version_string))

        self.config = config
        geometry = self.config.value('geometry')
        if geometry is not None:
            self.restoreGeometry(geometry)

        self.connected = False

        self.printer = QPrinter()
        printer_name = config.value('printer/name', self.printer.printerName())
        self.printer.setPrinterName(printer_name)
        outputfilename = config.value('printer/outputfilename',
                                      self.printer.outputFileName())
        self.printer.setOutputFileName(outputfilename)
        self.ui.printerName.setText(self.printer.printerName())

        init_time = datetime.now()
        self.cards_data_filename = os.path.join(
            'data', 'cards{:%Y%m%d}.csv'.format(init_time))
        if not os.path.exists(self.cards_data_filename):
            with open(self.cards_data_filename, 'w') as cards_data_file:
                cards_data_file.write(
                    'No.;Read at;Card no.;;;;;;;;;;;;;;;;Clear time;;;Check time;;;Start time;;;Finish time;No. of punches;;1.CP;;1.Time;2.CP;;2.Time;3.CP;;3.Time\n'
                )
        self.cards_data = []

        self._logger = self.Logger()
        self.log('{:%Y-%m-%d %H:%M:%S}'.format(init_time))

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.poll_card)

        availablePorts = []
        if platform.system() == 'Linux':
            availablePorts = [
                os.path.join('/dev', f) for f in os.listdir('/dev')
                if re.match('ttyUSB.+', f)
            ]
            availablePorts.sort()
        elif platform.system() == 'Windows':
            availablePorts = ['COM' + str(i) for i in range(32)]
        self.ui.choiseCom.addItems(availablePorts)
        self.ui.cbUartPort.addItems(availablePorts)

        self.ui.Connect.clicked.connect(self.Connect_clicked)
        self.ui.ReadCard.clicked.connect(self.ReadCard_clicked)
        self.ui.InitCard.clicked.connect(self.InitCard_clicked)
        self.ui.SetTime.clicked.connect(self.SetTime_clicked)
        self.ui.SetNum.clicked.connect(self.SetNum_clicked)
        self.ui.SetStart.clicked.connect(self.SetStart_clicked)
        self.ui.SetFinish.clicked.connect(self.SetFinish_clicked)
        self.ui.CheckSt.clicked.connect(self.CheckSt_clicked)
        self.ui.ClearSt.clicked.connect(self.ClearSt_clicked)
        self.ui.LogCard.clicked.connect(self.LogCard_clicked)
        self.ui.ReadLog.clicked.connect(self.ReadLog_clicked)
        self.ui.SleepCard.clicked.connect(self.SleepCard_clicked)
        self.ui.PassCard.clicked.connect(self.PassCard_clicked)
        self.ui.SelectPrinter.clicked.connect(self.SelectPrinter_clicked)
        self.ui.Print.clicked.connect(self.Print_clicked)
        self.ui.btnApplyPwd.clicked.connect(self.ApplyPwd_clicked)
        self.ui.btnCreateInfoCard.clicked.connect(self.CreateInfo_clicked)
        self.ui.btnReadInfo.clicked.connect(self.ReadInfo_clicked)
        self.ui.btnUartRead.clicked.connect(self.SerialRead_clicked)
        self.ui.btnUartWrite.clicked.connect(self.SerialWrite_clicked)
        self.ui.btnClearText.clicked.connect(self.ClearText_clicked)
        self.ui.btnMsConfigRead.clicked.connect(self.btnMsConfigRead_clicked)
        self.ui.btnMsConfigWrite.clicked.connect(self.write_ms_config)
        self.ui.AutoRead.stateChanged.connect(self.autoread_change)

        bs_config = BaseStation.Config()
        for key, default_value in vars(bs_config).items():
            value_type = type(default_value)
            if isinstance(default_value, list):
                value_type = type(default_value[0])
            setattr(
                bs_config, key,
                self.config.value('settings/' + key,
                                  default_value,
                                  type=value_type))
        self._apply_settings(bs_config, datetime.now())

        self.ui.sbCurPwd1.setValue(bs_config.password[0])
        self.ui.sbCurPwd2.setValue(bs_config.password[1])
        self.ui.sbCurPwd3.setValue(bs_config.password[2])

        ianaIds = QTimeZone.availableTimeZoneIds()
        all_timezones = sorted(
            {QTimeZone(id).offsetFromUtc(datetime.now())
             for id in ianaIds})
        tzlocaloffset = time.localtime().tm_gmtoff
        tzlocalname = None
        for dt in all_timezones:
            tz = timezone(timedelta(seconds=dt))
            tzname = tz.tzname(None)
            if dt == tzlocaloffset:
                tzlocalname = tzname
            self.ui.cbTimeZone.addItem(tzname, dt)
        if tzlocalname is not None:
            self.ui.cbTimeZone.setCurrentText(tzlocalname)
        else:
            self.ui.cbTimeZone.setCurrentText(
                timezone(offset=timedelta(0)).tzname(None))

    def closeEvent(self, event):
        self.config.setValue('geometry', self.saveGeometry())
        self.config.setValue('printer/name', self.printer.printerName())
        self.config.setValue('printer/outputfilename',
                             self.printer.outputFileName())

        bs_config = self._get_config_from_ui()
        for key, value in vars(bs_config).items():
            self.config.setValue('settings/' + key, value)

        event.accept()

    def Connect_clicked(self):
        if self.connected:
            self.ui.AutoRead.setChecked(False)
            self.sportiduino.disconnect()
            self.log('\n' + self.tr("Master station is disconnected"))
            self.connected = False
            self.ui.Connect.setText(_translate("MainWindow", "Connect"))
        else:
            port = self.ui.choiseCom.currentText()
            try:
                if (port == QCoreApplication.translate("MainWindow", "auto")):
                    self.sportiduino = Sportiduino(
                        debug=True, translator=QCoreApplication.translate)
                else:
                    self.sportiduino = Sportiduino(port, debug=True)

                curPass = (self.ui.sbCurPwd1.value(),
                           self.ui.sbCurPwd2.value(),
                           self.ui.sbCurPwd3.value())
                self._apply_pwd(curPass)

                #self.sportiduino.beep_ok()
                self.log(
                    '\n' +
                    self.tr("Master station {} on port {} is connected").
                    format(self.sportiduino.version, self.sportiduino.port))
                self.ui.Connect.setText(_translate("MainWindow", "Disconn."))
                self.connected = True

                self.read_ms_config()

            except Exception as err:
                self._process_error(err)

    def ReadCard_clicked(self):
        if not self._check_connection():
            return

        try:
            self.log("\n" + self.tr("Read a card"))

            card_type = self.sportiduino.read_card_type()
            try:
                data = self.sportiduino.read_card(timeout=0.2)
            except SportiduinoTimeout:
                data = Sportiduino.raw_data_to_card_data(
                    self.sportiduino.read_card_raw())
            else:
                self.sportiduino.beep_ok()

            self._show_card_data(data, card_type)
            self._save_card_data_to_csv(data)

        except Exception as err:
            self._process_error(err)

    def poll_card(self):
        if not self._check_connection():
            return

        try:
            if self.sportiduino.poll_card():
                card_number = self.sportiduino.card_data['card_number']
                self.sportiduino.beep_ok()
                if card_number != self.prev_card_number:
                    self._show_card_data(self.sportiduino.card_data)
                    self._save_card_data_to_csv(self.sportiduino.card_data)
                    self.prev_card_number = card_number
            else:
                self.prev_card_number = -1

        except Exception as err:
            self._process_error(err)

    def InitCard_clicked(self):
        if not self._check_connection():
            return
        try:
            self.log("\n" + self.tr("Initialize the participant card"))

            card_num = self.ui.cardNumber.value()

            if (card_num < Sportiduino.MIN_CARD_NUM
                    or card_num > Sportiduino.MAX_CARD_NUM):
                raise Exception(self.tr("Incorrect card number"))

            code, data = self.sportiduino.init_card(card_num)
            if code == Sportiduino.RESP_OK:
                self.log(
                    self.
                    tr("The participant card No {} ({}) has been initialized successfully"
                       ).format(card_num, Sportiduino.card_name(data[0])))
                if self.ui.AutoIncriment.isChecked():
                    self.ui.cardNumber.setValue(card_num + 1)

        except Exception as err:
            self._process_error(err)

    def SetNum_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log("\n" + self.tr(
                "Write the master card to set number of a base station"))
            num = self.ui.sbStationNum.value()

            if num < 1 or num > 255:
                raise Exception(self.tr("Not correct station number"))

            self.sportiduino.init_cp_number_card(num)
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def SetTime_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log("\n" + self.tr(
                "Write the master card to set clock of a base station. Put the card on a base station after third signal"
            ))
            self.sportiduino.init_time_card(datetime.utcnow() +
                                            timedelta(seconds=3))
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def SetStart_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log("\n" + self.tr(
                "Write the master card to set a base station as the start station"
            ))
            self.sportiduino.init_cp_number_card(Sportiduino.START_STATION)
            self.ui.sbStationNum.setValue(Sportiduino.START_STATION)
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def SetFinish_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log("\n" + self.tr(
                "Write the master card to set a base station as the finish station"
            ))
            self.sportiduino.init_cp_number_card(Sportiduino.FINISH_STATION)
            self.ui.sbStationNum.setValue(Sportiduino.FINISH_STATION)
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def CheckSt_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log("\n" + self.tr(
                "Write the master card to set a base station as the check station"
            ))
            self.sportiduino.init_cp_number_card(Sportiduino.CHECK_STATION)
            self.ui.sbStationNum.setValue(Sportiduino.CHECK_STATION)
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def ClearSt_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log("\n" + self.tr(
                "Write the master card to set a base station as the clear station"
            ))
            self.sportiduino.init_cp_number_card(Sportiduino.CLEAR_STATION)
            self.ui.sbStationNum.setValue(Sportiduino.CLEAR_STATION)
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def LogCard_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log(
                "\n" +
                self.tr("Write the master card to get log of a base station"))
            self.sportiduino.init_backupreader()
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def ReadLog_clicked(self):
        if not self._check_connection():
            return

        text = ""

        try:
            self.log("\n" +
                     self.tr("Read the card contained log of a base station"))

            data = self.sportiduino.read_backup()

            if data is None:
                raise Exception(self.tr("No log data available"))

            text = self.tr("Station No: {} ").format(data['cp']) + "\n"

            cards = data['cards']

            text += self.tr("Total punches {}").format(len(cards)) + "\n"
            if len(cards) > 0:
                text += self.tr("Cards:") + "\n"
                if isinstance(cards[0], int):
                    text += ', '.join([str(c) for c in cards])
                else:
                    for pair in cards:
                        text += "{:>4} {}".format(*pair) + "\n"
                with open(os.path.join(
                        'data', 'station{}_{:%Y%m%d%H%M%S}.csv'.format(
                            data['cp'], datetime.now())),
                          'w',
                          newline='') as station_backupfile:
                    station_backupfile_writer = csv.writer(station_backupfile,
                                                           delimiter=',')
                    if isinstance(cards[0], int):
                        station_backupfile_writer.writerow(cards)
                    else:
                        station_backupfile_writer.writerows(cards)

            self.log(text)

        except Exception as err:
            self._process_error(err)

    def SleepCard_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log("\n" +
                     self.tr("Write the master card to sleep a base station"))
            self.sportiduino.init_sleepcard(self._get_competion_datetime())
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def PassCard_clicked(self):
        if not self._check_connection():
            return

        try:
            self.log("\n" + self.tr("Write the config master card"))

            bs_config = self._get_config_from_ui()
            bs_config.num = 0  # don't change station number by this master card
            self.sportiduino.init_config_card(bs_config.pack())

            self.ui.sbCurPwd3.setValue(self.ui.sbNewPwd3.value())
            self.ui.sbCurPwd2.setValue(self.ui.sbNewPwd2.value())
            self.ui.sbCurPwd1.setValue(self.ui.sbNewPwd1.value())

            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def ApplyPwd_clicked(self):
        if not self._check_connection():
            return

        try:
            self.log("\n" + self.tr("Apply the current password"))

            curPass = (self.ui.sbCurPwd1.value(), self.ui.sbCurPwd2.value(),
                       self.ui.sbCurPwd3.value())
            self._apply_pwd(curPass)
            self.log(self.tr("The password has been applied successfully"))

        except Exception as err:
            self._process_error(err)

    def CreateInfo_clicked(self):
        if not self._check_connection():
            return

        try:

            self.log(
                "\n" +
                self.tr("Write the master card to get a base station state"))
            self.sportiduino.init_state_card()
            self._master_card_ok()

        except Exception as err:
            self._process_error(err)

    def ReadInfo_clicked(self):
        if not self._check_connection():
            return

        try:
            self.log("\n" +
                     self.tr("Read the card contained a base station state"))

            state = self.sportiduino.read_state_card()

            bs_state = BaseStation.State()
            bs_state.version = Sportiduino.Version(*state['version'])
            bs_state.config = BaseStation.Config.unpack(state['config'])

            bs_state.battery = BaseStation.Battery(state['battery'])
            bs_state.mode = state['mode']

            bs_state.timestamp = state['timestamp']
            bs_state.wakeuptime = state['wakeuptime']

            self._show_base_station_state(bs_state)

        except Exception as err:
            self._process_error(err)

    def log(self, text):
        print(text)
        self.ui.plainTextEdit.appendPlainText(text)
        # Scroll down
        self.ui.plainTextEdit.verticalScrollBar().setValue(
            self.ui.plainTextEdit.verticalScrollBar().maximum())

        self._logger(text)

    def SelectPrinter_clicked(self):
        dialog = QtPrintSupport.QPrintDialog(self.printer)
        dialog.setWindowTitle(self.tr("Printer Selection"))
        if dialog.exec_() == QtWidgets.QDialog.Accepted:
            self.printer = dialog.printer()
            self.ui.printerName.setText(self.printer.printerName())

    def Print_clicked(self):
        try:
            #self.printer.setPageMargins(3,3,3,3,QPrinter.Millimeter)

            page_size = QSizeF()
            page_size.setHeight(self.printer.height())
            page_size.setWidth(self.printer.width())

            text_document = self.ui.plainTextEdit.document().clone()
            text_document.setPageSize(page_size)
            text_document.setDocumentMargin(0.0)
            text_document.print(self.printer)
        except Exception as err:
            self._process_error(err)

    def SerialRead_clicked(self):
        try:
            self.log("\n" + self.tr("Reads info about a base station by UART"))

            port = self.ui.cbUartPort.currentText()
            password = (self.ui.sbCurPwd1.value(), self.ui.sbCurPwd2.value(),
                        self.ui.sbCurPwd3.value())

            bs_state = BaseStation.read_info_by_serial(port, password)

            self._show_base_station_state(bs_state)

        except Exception as err:
            self._process_error(err)

    def SerialWrite_clicked(self):
        try:
            self.log("\n" + self.tr(
                "Writes settings and password to a base station by UART"))
            port = self.ui.cbUartPort.currentText()

            bs_config = self._get_config_from_ui()
            bs_config.num = self.ui.sbStationNumByUart.value()
            wakeuptime = self._get_competion_datetime().toPyDateTime()

            password = (self.ui.sbCurPwd1.value(), self.ui.sbCurPwd2.value(),
                        self.ui.sbCurPwd3.value())
            BaseStation.write_settings_by_serial(port, password, bs_config,
                                                 wakeuptime)

            self.log(
                self.tr("Settings and password has been written successfully"))

        except Exception as err:
            self._process_error(err)

    def ClearText_clicked(self):
        self.ui.plainTextEdit.setPlainText('')

    def read_ms_config(self):
        ms_config = self.sportiduino.read_settings()
        if ms_config.antenna_gain is not None:
            self.ui.cbMsAntennaGain.setCurrentIndex(ms_config.antenna_gain - 2)
        if ms_config.timezone is not None:
            tz = timezone(ms_config.timezone)
            self.ui.cbTimeZone.setCurrentText(tz.tzname(None))

    def btnMsConfigRead_clicked(self):
        if not self._check_connection():
            return

        try:
            self.read_ms_config()
            self.sportiduino.beep_ok()
        except Exception as err:
            self._process_error(err)

    def autoread_change(self):
        if self.ui.AutoRead.isChecked():
            if not self._check_connection():
                return
            self.prev_card_number = -1
            self.log("\n" + self.tr("Start polling cards"))
            self.timer.start(1000)
        else:
            self.log("\n" + self.tr("Stop polling cards"))
            self.timer.stop()
        self.ui.ReadCard.setEnabled(not self.ui.AutoRead.isChecked())
        self.ui.InitCard.setEnabled(not self.ui.AutoRead.isChecked())
        self.ui.tab_2.setEnabled(not self.ui.AutoRead.isChecked())
        self.ui.tab_3.setEnabled(not self.ui.AutoRead.isChecked())
        self.ui.tab_4.setEnabled(not self.ui.AutoRead.isChecked())

    def write_ms_config(self):
        if not self._check_connection():
            return

        try:
            tz = timedelta(seconds=self.ui.cbTimeZone.currentData())
            self.sportiduino.write_settings(
                self.ui.cbMsAntennaGain.currentIndex() + 2, tz)
        except Exception as err:
            self._process_error(err)

    def _show_card_data(self, data, card_type=None):
        if self.ui.AutoPrint.isChecked():
            self.ClearText_clicked()

        text = []
        if card_type is not None:
            card_name = Sportiduino.card_name(card_type)
            text.append(card_name)

        if 'master_card_flag' in data:
            # show master card info
            master_type = int2byte(data['master_card_type'])

            if master_type == Sportiduino.MASTER_CARD_GET_STATE:
                text.append(
                    self.tr("Master card to get info about a base station"))
            elif master_type == Sportiduino.MASTER_CARD_SET_TIME:
                text.append(
                    self.tr("Master card to set time of a base station"))
            elif master_type == Sportiduino.MASTER_CARD_SET_NUMBER:
                text.append(
                    self.tr("Master card to set number of a base station"))
            elif master_type == Sportiduino.MASTER_CARD_SLEEP:
                text.append(self.tr("Master card to sleep a base station"))
            elif master_type == Sportiduino.MASTER_CARD_READ_BACKUP:
                text.append(
                    self.tr(
                        "Master card to get punches log of a base station"))
            elif master_type == Sportiduino.MASTER_CARD_SET_PASS:
                text.append(
                    self.
                    tr("Master card to write password and settings to a base station"
                       ))
            else:
                text.append(self.tr("Uninitialized card"))

        else:
            # show participant card info
            card_number = data['card_number']
            init_time = -1
            if 'init_timestamp' in data:
                init_time = (data['init_timestamp'])

            if init_time != 0 and card_number >= Sportiduino.MIN_CARD_NUM and card_number <= Sportiduino.MAX_CARD_NUM:
                punches_count = 0

                text.append(
                    self.tr("Participant card No {}").format(card_number))
                if init_time > 0:
                    text.append(
                        self.tr("Init time {}").format(
                            datetime.fromtimestamp(init_time)))

                text.append(self.tr("Punches (Check point - Time):"))
                punch_str = "{:>5} - {}"
                if 'start' in data:
                    text.append(
                        punch_str.format(self.tr("Start"), data["start"]))

                punches = data['punches']
                for punch in punches:
                    punches_count += 1

                    cp = punch[0]
                    cp_time = punch[1]

                    text.append(punch_str.format(cp, cp_time))

                if 'finish' in data:
                    text.append(
                        punch_str.format(self.tr("Finish"), data["finish"]))

                if punches_count == 0:
                    text.append(self.tr("No punches"))
                else:
                    text.append(
                        self.tr("Total punches {}").format(punches_count))
            else:
                text.append(self.tr("Uninitialized card"))

        self.log('\n'.join(text))

        if self.ui.AutoPrint.isChecked():
            self.Print_clicked()

    def _save_card_data_to_file(self, data):
        if 'master_card_flag' in data:
            return

        card_number = data['card_number']

        if card_number < Sportiduino.MIN_CARD_NUM or card_number > Sportiduino.MAX_CARD_NUM:
            return

        if 'start' in data:
            data['start'] = int(data['start'].timestamp())

        if 'finish' in data:
            data['finish'] = int(data['finish'].timestamp())

        if 'punches' in data:
            punches = data['punches']
            bufferPunch = []
            for punch in punches:
                kort = (punch[0], int(punch[1].timestamp()))
                bufferPunch.append(kort)
            data['punches'] = bufferPunch

        if 'master_card_flag' in data:
            del data['master_card_flag']
        if 'master_card_type' in data:
            del data['master_card_type']
        if 'init_timestamp' in data:
            del data['init_timestamp']
        del data['page6']
        del data['page7']

        self.cards_data.append(data)

        with open(self.cards_data_filename, 'w') as cards_data_file:
            json.dump(self.cards_data, cards_data_file)

    def _save_card_data_to_csv(self, data):
        if 'master_card_flag' in data:
            return

        with open(self.cards_data_filename, 'a') as cards_data_file:
            csv_writer = csv.writer(cards_data_file, delimiter=';')
            row_data = [
                0, datetime.now().strftime('%H:%M:%S'), data['card_number']
            ]
            for i in range(27):
                row_data.append(None)
            if 'start' in data:
                row_data[24] = data['start'].strftime('%H:%M:%S')
            if 'finish' in data:
                row_data[27] = data['finish'].strftime('%H:%M:%S')
            punches_count = 0
            for punch in data['punches']:
                punches_count += 1
                cp = punch[0]
                cp_time = punch[1]
                row_data.append(cp)
                row_data.append(None)
                row_data.append(cp_time.strftime('%H:%M:%S'))
            row_data[28] = punches_count

            csv_writer.writerow(row_data)

    def _apply_settings(self, bs_config, wakeuptime):
        self.ui.sbStationNum.setValue(bs_config.num)
        self.ui.sbStationNumByUart.setValue(bs_config.num)
        self.ui.dtCompetion.setDateTime(wakeuptime)

        self.ui.cbActiveTime.setCurrentIndex(bs_config.active_mode_duration)

        self.ui.cbStartFinish.setChecked(bs_config.check_start_finish)
        self.ui.cbCheckInitTime.setChecked(bs_config.check_card_init_time)
        self.ui.cbAutosleep.setChecked(bs_config.autosleep)
        self.ui.cbFastPunch.setChecked(bs_config.fast_punch)

        self.ui.cbAntennaGain.setCurrentIndex(bs_config.antenna_gain - 2)

    def _get_config_from_ui(self):
        bs_config = BaseStation.Config()
        bs_config.active_mode_duration = self.ui.cbActiveTime.currentIndex()
        bs_config.check_start_finish = self.ui.cbStartFinish.isChecked()
        bs_config.check_card_init_time = self.ui.cbCheckInitTime.isChecked()
        bs_config.autosleep = self.ui.cbAutosleep.isChecked()
        bs_config.fast_punch = self.ui.cbFastPunch.isChecked()
        bs_config.antenna_gain = self.ui.cbAntennaGain.currentIndex() + 2
        bs_config.password = [
            self.ui.sbNewPwd1.value(),
            self.ui.sbNewPwd2.value(),
            self.ui.sbNewPwd3.value()
        ]

        return bs_config

    def _get_competion_datetime(self):
        dt = self.ui.dtCompetion.dateTime().toUTC()
        return dt.addSecs(-dt.time().second())

    def _show_base_station_state(self, bs_state):
        self.log(self.tr("Version: {}").format(bs_state.version))

        # apply settings to ui
        self._apply_settings(
            bs_state.config, bs_state.wakeuptime
            if bs_state.wakeuptime > datetime.now() else datetime.now())

        self.log(self.tr("Settings:"))

        text = self.tr("   Station No: {} ").format(bs_state.config.num)
        if (bs_state.config.num == Sportiduino.START_STATION):
            text += self.tr("(Start)")
        elif (bs_state.config.num == Sportiduino.FINISH_STATION):
            text += self.tr("(Finish)")
        elif (bs_state.config.num == Sportiduino.CHECK_STATION):
            text += self.tr("(Check)")
        elif (bs_state.config.num == Sportiduino.CLEAR_STATION):
            text += self.tr("(Clear)")
        self.log(text)

        self.log(
            self.tr("   Active time (h): {}").format(
                self.ui.cbActiveTime.currentText()))
        if bs_state.config.check_start_finish:
            self.log(self.tr("   Check start/finish flag"))
        if bs_state.config.check_card_init_time:
            self.log(self.tr("   Check card init time flag"))
        if bs_state.config.autosleep:
            self.log(self.tr("   Autosleep flag"))

        if bs_state.config.fast_punch:
            self.log(self.tr("   Fast punch flag"))
        self.log(
            self.tr("   Antenna Gain: {}").format(
                self.ui.cbAntennaGain.currentText()))

        voltageText = ''
        if bs_state.battery.voltage is not None:
            voltageText = self.tr(" ({:.2f} V)").format(
                bs_state.battery.voltage)

        if (bs_state.battery.isOk):
            self.log(self.tr("Battery: OK") + voltageText)
        else:
            self.log(self.tr("Battery: Low") + voltageText)

        if (bs_state.mode == BaseStation.MODE_ACTIVE):
            self.log(self.tr("Mode: Active"))
        elif (bs_state.mode == BaseStation.MODE_WAIT):
            self.log(self.tr("Mode: Wait"))
        elif (bs_state.mode == BaseStation.MODE_SLEEP):
            self.log(self.tr("Mode: Sleep"))

        text = self.tr("Clock: {}").format(bs_state.timestamp)
        self.log(text)
        text = self.tr("Alarm: {}").format(bs_state.wakeuptime)
        self.log(text)

        self.log(
            self.
            tr("Settings displayed by UI has been chaged to the base station settings"
               ))

    def _process_error(self, err):
        self.log(self.tr("Error: {}").format(err))

    def _check_connection(self):
        if not self.connected:
            self.log(self.tr("Master station is not connected"))
            return False
        return True

    def _master_card_ok(self):
        self.log(self.tr("The master card has been written successfully"))

    def _apply_pwd(self, curPass):
        self.sportiduino.apply_pwd(curPass)

        self.ui.sbNewPwd1.setValue(curPass[0])
        self.ui.sbNewPwd2.setValue(curPass[1])
        self.ui.sbNewPwd3.setValue(curPass[2])

    class Logger(object):
        def __init__(self):
            self._log_file = open(
                os.path.join('log', 'log{:%Y%m%d}.txt'.format(datetime.now())),
                'a')

        def __call__(self, text):
            self._log_file.write(text + '\n')

        def __del__(self):
            print("Close log file")
            self._log_file.close()
Example #12
0
#!/usr/bin/env python

import sys
sys.path.append('..')

from sportiduino import Sportiduino
from time import sleep
import serial

if len(sys.argv) > 1:
    port = sys.argv[1]
    sportiduino = Sportiduino(port=port, debug=True)
else:
    sportiduino = Sportiduino(debug=True)

print('Read card loop')
while True:
    while not sportiduino.poll_card():
        sleep(0.5)

    data = sportiduino.card_data
    print("Punches:", data)
    sportiduino.beep_ok()
    sleep(1)