420 lines
17 KiB
Python
420 lines
17 KiB
Python
# -*- 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 <http://www.gnu.org/licenses/>.
|
|
'''
|
|
|
|
|
|
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)
|