diff --git a/rfiddriver.py b/rfiddriver.py new file mode 100644 index 0000000..1808b85 --- /dev/null +++ b/rfiddriver.py @@ -0,0 +1,419 @@ +# -*- coding: utf-8 -*- + +''' + Copyright (C) 2016 Entr'ouvert + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +''' + + +import os +import time +import json +import logging +import serial +import csv + +from datetime import datetime +from datetime import date, timedelta +from time import sleep + +import whisper + +import librfid +import epc + +import config +import logging_conf +import utils + + +logger = logging.getLogger(__name__) + + +MAX_RETRY = 3 +METRICS = ['temperature', 'heartrate'] + + +def try_command(reader, command): + retry = 0 + while retry < MAX_RETRY: + try: + response = reader.device.send(command) + return response + except Exception, e: + logger.warning("Reader request failed with error '{}'".format(e)) + retry += 1 + logger.debug("Remaining tries : {}".format(MAX_RETRY - retry)) + logger.error("Command failed") + return False + + +def get_health_parameters(reader): + logger.debug('Get_HealthParameters') + quality = ['Disconnected', 'Bad', 'Good'] + command = librfid.ReaderCommand(command_name='Get_HealthParameters', + reader=reader.device) + response = try_command(reader, command) + if not response: + return False + health_parameters = response.data + logger.debug("health_parameters : '{}'".format(health_parameters)) + antennas = [] + for i in range(4): + tune = "Tune{}".format(i) + if health_parameters[tune][1] >= 1: + antennas.append(i) + logger.debug("Antenna {} connection quality : {} ({})".format(i, + quality[health_parameters[tune][1]], health_parameters[tune][0])) + logger.debug("{} antennas connected : '{}'".format(len(antennas), antennas)) + logger.debug("Reader core temperature: %d" % health_parameters['TempCore']) + logger.debug("Reader power amplificator temperature: %d" % health_parameters['PA']) + reader.config.set_setting('antenna_connected', antennas) + return True + + +def get_rf_settings(reader): + logger.debug('Get_RFSettings') + command = librfid.ReaderCommand(command_name='Get_RFSettings', + reader=reader.device) + response = try_command(reader, command) + if not response: + return False + i = 0 + for logic_port in response.data: + if logic_port['ScanDelay'] or logic_port['PowerAnt']: + logger.debug("Logic port: %s" % i) + logger.debug("\tScanDelay: %s ms" % logic_port['ScanDelay']) + logger.debug("\tPowerAnt: %s dBm" % logic_port['PowerAnt']) + logger.debug("\tAntNb: %s" % logic_port['AntNb']) + i += 1 + return True + + +def set_rf_settings(reader, logic_ports): + logger.debug('Set_RFSettings') + logger.debug("Logic ports configuration : '{}'".format(logic_ports)) + command = librfid.ReaderCommand(command_name='Set_RFSettings', + reader=reader.device, data=logic_ports) + response = try_command(reader, command) + if not response: + return False + return True + + +class Reader: + def __init__(self, *args, **kwargs): + logger.debug('Reader initialization.') + self.config = config.Config() + for metric in METRICS: + fn = "{}.wsp".format(metric) + setattr(self, "fn_{}".format(metric), fn) + logger.debug('Metric {} : filename {}.'.format(metric, fn)) + if not os.path.isfile(fn): + logger.debug('Creating file.') + whisper.create(fn, [(1, 2592000)]) + else: + logger.debug('Existing file.') + + self.csv_temperature = "temperature.csv" + logger.debug('Metric temperature : filename {}.'.format(self.csv_temperature)) + if not os.path.isfile(self.csv_temperature): + logger.debug('Creating file {}.'.format(self.csv_temperature)) + with open(fn, 'w') as f: + writer = csv.writer(f, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) + row = ["date", "time", "int", "degree", "volt"] + writer.writerow(row) + else: + logger.debug('Existing file.') + self.csv_heartrate = "heartrate.csv" + logger.debug('Metric heartrate : filename {}.'.format(self.csv_heartrate)) + if not os.path.isfile(self.csv_heartrate): + logger.debug('Creating file {}.'.format(self.csv_heartrate)) + with open(fn, 'w') as f: + writer = csv.writer(f, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) + row = ["date", "time", "hr1 int", "hr1 second", "hr1 bpm", "hr2 int", "hr2 second", + "hr2 bpm", "hr3 int", "hr3 second", "hr3 bpm"] + writer.writerow(row) + else: + logger.debug('Existing file.') + + self.csv_epc = 'epc.csv' + if not os.path.isfile(self.csv_epc): + logger.debug('Creating file.') + with open(self.csv_epc, 'w') as f: + writer = csv.writer(f, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) + row = ["date", "time", "epc", "trcal", "can_precision", "sensor_sensibility", + "sensor_center", "temperature_int", "temperature_degree", "temperature_volt", + "tension_int", "tension_volt", "cpt_trcal", "period_osc", "hr1_int", + "hr1_second", "hr1_bpm", "hr2_int", "hr2_second", "hr2_bpm", "hr3_int", + "hr3_second", "hr3_bpm"] + writer.writerow(row) + else: + logger.debug('Existing file epc.csv.') + self.config.set_setting('exit_reader', False) + self.device = librfid.models.RFIDReader() + + def now(self): + return int(utils.unix_time(datetime.utcnow())) + + def write_metric_value(self, metric, value): + logger.debug('Update {} with value {}.'.format(metric, value)) + whisper.update(getattr(self, "fn_{}".format(metric)), value) + + def write_metric_value_csv(self, metric, values): + with open(getattr(self, "csv_{}".format(metric)), 'a') as f: + writer = csv.writer(f, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) + now = datetime.now() + for value in values: + row = [ + now.strftime("%Y/%m/%d"), + now.strftime("%H:%M:%S"), + ] + row.extend(value) + logger.debug('Update csv {} with values {}.'.format(metric, row)) + writer.writerow(row) + + def write_epc_csv(self, epcs): + with open(self.csv_epc, 'a') as f: + writer = csv.writer(f, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) + now = datetime.now() + for epc in epcs: + row = [ + now.strftime("%Y/%m/%d"), + now.strftime("%H:%M:%S"), + epc.epc, + epc.trcal, + epc.can_precision, + epc.sensor_sensibility, + epc.sensor_center, + epc.temperature_int, + epc.temperature_degree, + epc.temperature_volt, + epc.tension_int, + epc.tension_volt, + epc.cpt_trcal, + epc.period_osc, + epc.hr1_int, + epc.hr1_second, + epc.hr1_bpm, + epc.hr2_int, + epc.hr2_second, + epc.hr2_bpm, + epc.hr3_int, + epc.hr3_second, + epc.hr3_bpm, + ] + logger.debug('Update csv {} with values {}.'.format(self.csv_epc, row)) + writer.writerow(row) + + def device_ready(self): + (exit_reader, + pause_reader, + self.reader_sleep_short, + self.temperature, + self.heartrate) = self.config.reader_config() + logger.debug('Reader configuration : exit_reader {} - pause_reader {} ' + '- reader_sleep_short {} - temperature {} - heartrate {}' + ''.format(exit_reader, pause_reader, self.reader_sleep_short, + self.temperature, self.heartrate)) + if exit_reader: + logger.info('Exit reader asked.') + exit(0) + if pause_reader: + logger.debug('Reader off.') + logger.debug('Sleep {}ms.'.format(self.reader_sleep_short)) + self.config.set_setting('reader_ready', False) + return False + logger.debug('Reader on.') + try: + self.device.get_connection() + logger.debug('Device connected') + self.config.set_setting('reader_ready', True) + return True + except serial.serialutil.SerialException, e: + self.config.set_setting('reader_ready', False) + logger.debug('Device not connected : {}.'.format(e)) + return False + + def device_check(self): + while not self.device_ready(): + logger.debug('Sleep {}ms.'.format(self.reader_sleep_short)) + sleep(self.reader_sleep_short) + + if not get_health_parameters(self): + logger.error('Command get_health_parameters failed, stop tag detection') + return False + return True + + def inventory(self, + delay=100, + power=31, + logic_port=None, + skip_device_check=False, + skip_config=False, + silent=True, + epcs_csv=False, + metrics_csv=False, + metrics_db=False): + + if not skip_device_check: + if not self.device_check(): + return None + + if not skip_config: + logic_ports = [] + if logic_port is not None: + logic_ports = [{'ScanDelay': 0, 'PowerAnt': 0, 'AntNb': antenna} + for antenna in self.config.get_setting('antenna_connected')] + logic_ports[logic_port] = { + 'ScanDelay': delay, + 'PowerAnt': power, + 'AntNb': self.config.get_setting('antenna_connected')[logic_port] + } + else: + logic_ports = [{'ScanDelay': delay, 'PowerAnt': power, 'AntNb': antenna} + for antenna in self.config.get_setting('antenna_connected')] + + if not set_rf_settings(self, logic_ports): + logger.error('Command set_rf_settings failed, stop tag detection') + return None + if not get_rf_settings(self): + logger.error('Command get_rf_settings failed, stop tag detection') + return None + + logger.debug('Inventory') + command = librfid.Command(command_name='Inventory') + response = try_command(self, command) + + if not response: + logger.error('Command Inventory failed') + return None + + if response.data: + for tag in response.data: + logger.debug("Tag: %s" % tag['EPC'].encode('hex')) + logger.debug("\t Read on logic port {} be antenna {}".format(tag['AntID'], + self.config.get_setting('antenna_connected')[tag['AntID']])) + logger.debug("\t Times read on all logic ports: %s" % tag['NbRead']) + epc_decoded = epc.VeadistaEPC(str(tag['EPC'].encode('hex'))) + logger.debug("\n{}".format(epc_decoded)) + + if epcs_csv: + epcs = [epc.VeadistaEPC(tag['EPC'].encode('hex')) for tag in response.data] + self.write_epc_csv(epcs) + + if metrics_csv: + temperatures = [] + hrs = [] + for tag in response.data: + e = epc.VeadistaEPC(tag['EPC'].encode('hex')) + if self.temperature: + temperatures.append((e.temperature_int, + e.temperature_degree, + e.temperature_volt)) + if self.heartrate: + hrs.append((e.hr1_int, e.hr1_second, e.hr1_bpm, e.hr2_int, e.hr2_second, + e.hr2_bpm, e.hr3_int, e.hr3_second, e.hr3_bpm)) + if self.temperature: + logger.debug("Temperature vector : '{}'".format(temperatures)) + self.write_metric_value_csv('temperature', temperatures) + if self.heartrate: + logger.debug("Heartrate vector : '{}'".format(hrs)) + self.write_metric_value_csv('heartrate', hrs) + + if metrics_db: + if len(response.data) > 0 and self.temperature: + e = epc.VeadistaEPC(response.data[0]['EPC'].encode('hex')) + if e.temperature_degree: + self.write_metric_value('temperature', e.temperature_degree) + if len(response.data) > 1 and self.heartrate: + e = epc.VeadistaEPC(response.data[1]['EPC'].encode('hex')) + if e.hr1_bpm: + self.write_metric_value('heartrate', e.hr1_bpm) + + if not silent: + print "\a" + + return response + + def detection(self, + delay=100, + power=31, + logic_port=None, + silent=True): + + if not self.device_check(): + return None + + """ + As many logic ports as antenna, set all in fast inventory + """ + logic_ports = [{'ScanDelay': delay, 'PowerAnt': power, 'AntNb': antenna} + for antenna in self.config.get_setting('antenna_connected')] + if not set_rf_settings(self, logic_ports): + logger.error('Command set_rf_settings failed, stop tag detection') + return None + if not get_rf_settings(self): + logger.error('Command get_rf_settings failed, stop tag detection') + return None + + while(True): + while not self.device_ready(): + logger.debug('Sleep {}ms.'.format(self.reader_sleep_short)) + sleep(self.reader_sleep_short) + + response = self.inventory(delay=delay, power=power, logic_port=logic_port, + skip_device_check=True, silent=silent) + + if not response: + logger.error('Command Inventory failed, stop tag detection') + return None + + if response.data: + # Veadista assumption is only one physical tag that may return mutiple EPC. + # So if response.data it is enough to look at the first epc to know the logic port + logger.info("Tag in the field of logic_port {}".format(response.data[0]['AntID'])) + ant_id = self.config.get_setting('antenna_connected')[response.data[0]['AntID']] + self.config.set_setting('tag_in_field', ant_id) + return response.data[0]['AntID'] + else: + logger.debug('No tag detected') + self.config.set_setting('tag_in_field', '') + logger.debug('Sleep {}ms.'.format(self.reader_sleep_short)) + sleep(self.reader_sleep_short) + + def read(self, + power=31, + logic_port=None, + silent=True, + epcs_csv=False, + metrics_csv=False, + metrics_db=False): + + logger.debug('Entering reading function.') + detection = True if logic_port is None else False + logger.debug('Tag detection : {}.'.format(detection)) + while(True): + if detection: + logic_port = self.detection(power=power, silent=silent) + logger.info('The tag is in the field of logic_port {}.'.format(logic_port)) + if logic_port is None: + logger.error('The tag detection failed.') + break + reader_sleep_long = self.config.get_setting('reader_sleep_long') + logger.debug('Sleep {} s.'.format(reader_sleep_long)) + sleep(reader_sleep_long) + self.inventory(delay=4000, power=power, logic_port=logic_port, silent=silent, + epcs_csv=epcs_csv, metrics_csv=metrics_csv, metrics_db=metrics_db)