Reader module handling inventory and recording.
This commit is contained in:
parent
71d1e49074
commit
a57bdd0b36
|
@ -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 <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)
|
Reference in New Issue