# -*- 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) class Transmitter(): def __init__(self, *args, **kwargs): logger.debug('Transmitter 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)) self.config.set_setting('exit_transmitter', False) def now(self): return int(utils.unix_time(datetime.utcnow())) def set_last_push(self, metric, date): with open('last_push_{}'.format(metric), 'w') as f: f.write(str(date)) def get_last_push(self, metric): fn = 'last_push_{}'.format(metric) if not os.path.isfile(fn): with open(fn, 'w') as f: f.write(str(0)) with open(fn, 'r') as f: line = None for line in f: pass if line: return int(line) return None def send(self): while True: (exit_transmitter, pause_transmitter, transmitter_sleep, patient_id, carbon_hostname, carbon_port, self.temperature, self.heartrate, metrics_path) = self.config.transmitter_config() if exit_transmitter: logger.info('Exit transmitter asked.') exit(0) if pause_transmitter: logger.debug('Tranmsitter off.') logger.debug('Sleep {}ms.'.format(transmitter_sleep)) sleep(transmitter_sleep) continue logger.debug('Transmitter on.') if not patient_id: logger.debug('No patient ID configured.') sleep(transmitter_sleep) continue logger.debug('Patient ID : {}'.format(patient_id)) logger.debug('Carbon host : {}:{}'.format(carbon_hostname, carbon_port)) for metric in METRICS: if getattr(self, metric): logger.debug('Reading metric : {}.'.format(metric)) fn = getattr(self, "fn_{}".format(metric)) logger.debug('Filename : {}.'.format(fn)) if os.path.isfile(fn): last_push = _from = self.get_last_push(metric) logger.debug('Last read is : {}.'.format(_from)) until = self.now() logger.debug('Until now : {}.'.format(until)) data = whisper.fetch(fn, _from, until) if data: start, end, step = data[0] cleaned = [(start + i * step, value) for i, value in enumerate(data[1]) if value] logger.debug('Data to send : {}.'.format(cleaned)) if cleaned and start == last_push: logger.debug("First value already pushed, " "we don't send it : {}.".format(cleaned[0])) # We remove the first item if pushed last time cleaned = cleaned[1:] remote_path = "{}.{}.{}".format(metrics_path, patient_id, metric) for date, value in cleaned: content = "{} {} {}\n".format(remote_path, value, date) try: logger.debug("Send command : {}.".format(content)) utils.netcat(carbon_hostname, carbon_port, content) last_push = date except Exception, e: logger.debug("Error executing send command : '{}', " "we stopped at : {}.".format(e, last_push)) break logger.debug("Write last push at : {}.".format(last_push)) self.set_last_push(metric, last_push) else: logger.debug('No data to send.') else: logger.debug('No whisper file found.') else: logger.debug('Sending metric {} off.'.format(metric)) logger.debug('Sleep {} s.'.format(transmitter_sleep)) sleep(transmitter_sleep)