gate: Change opened/closed detection to slope calculation
Signed-off-by: Thomas Klaehn <thomas.klaehn@u-blox.com>
This commit is contained in:
parent
ce4094a7b9
commit
40146343ea
@ -6,8 +6,10 @@ Created on Dec 19, 2016
|
|||||||
import time
|
import time
|
||||||
import gate
|
import gate
|
||||||
import sys
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
logging.basicConfig(filename='/var/log/gate_guard.log', level=logging.DEBUG)
|
||||||
gate_state = gate.Gate()
|
gate_state = gate.Gate()
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
@ -17,6 +17,9 @@ class DataBuffer(object):
|
|||||||
self.__data.append(element)
|
self.__data.append(element)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.__data
|
||||||
|
|
||||||
def average(self):
|
def average(self):
|
||||||
if len(self.__data) is 0:
|
if len(self.__data) is 0:
|
||||||
return None
|
return None
|
||||||
|
@ -9,6 +9,9 @@ import data_buffer
|
|||||||
import light_sensor
|
import light_sensor
|
||||||
import engine
|
import engine
|
||||||
import power_sensor
|
import power_sensor
|
||||||
|
import scipy
|
||||||
|
import scipy.stats
|
||||||
|
import logging
|
||||||
|
|
||||||
STATE_INIT = "init"
|
STATE_INIT = "init"
|
||||||
STATE_OPENED = "open"
|
STATE_OPENED = "open"
|
||||||
@ -22,19 +25,19 @@ LIGHT_CONSECUTIVE_READS = 10
|
|||||||
LIGHT_LX_THRESHOLD = {"open":10, "close":5}
|
LIGHT_LX_THRESHOLD = {"open":10, "close":5}
|
||||||
|
|
||||||
MQTT_HOST = "proxy"
|
MQTT_HOST = "proxy"
|
||||||
MQTT_TOPIC = "outdoor/chickenhouse/newgate"
|
MQTT_TOPIC = "outdoor/chickenhouse/gate"
|
||||||
|
|
||||||
LIGHT_SENSOR_I2C_BUS = 1
|
LIGHT_SENSOR_I2C_BUS = 1
|
||||||
LIGHT_SENSOR_I2C_ADDRESS = 0x23
|
LIGHT_SENSOR_I2C_ADDRESS = 0x23
|
||||||
|
|
||||||
POWER_SENSOR_I2C_BUS = 1
|
POWER_SENSOR_I2C_BUS = 1
|
||||||
POWER_SENSOR_I2C_ADDRESS = 0x40
|
POWER_SENSOR_I2C_ADDRESS = 0x40
|
||||||
POWER_CONSECUTIVE_READS = 1000
|
|
||||||
|
|
||||||
MAX_ENGINE_POWER = {"up":330, "down":290}
|
SLOPE_COUNT = 100
|
||||||
MAX_GATE_RUNTIME = {"open":300, "close":300}
|
SLOPE_CNT_MIN = 10
|
||||||
MIN_GATE_RUNTIME = {"open":10, "close":10}
|
|
||||||
|
|
||||||
|
MAX_GATE_RUNTIME = {"open":60, "close":50}
|
||||||
|
MAX_POWER_SLOPE = {"up":10, "down":2}
|
||||||
|
|
||||||
class Gate(object):
|
class Gate(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -54,12 +57,12 @@ class Gate(object):
|
|||||||
self.__engine = engine.Engine(gpio_1=13, gpio_2=19)
|
self.__engine = engine.Engine(gpio_1=13, gpio_2=19)
|
||||||
self.__power_sensor = power_sensor.PowerSensor(POWER_SENSOR_I2C_BUS, \
|
self.__power_sensor = power_sensor.PowerSensor(POWER_SENSOR_I2C_BUS, \
|
||||||
POWER_SENSOR_I2C_ADDRESS)
|
POWER_SENSOR_I2C_ADDRESS)
|
||||||
self.__power_data = data_buffer.DataBuffer(POWER_CONSECUTIVE_READS)
|
|
||||||
self.__gate_move_timeout = 0
|
self.__gate_move_timeout = 0
|
||||||
self.__light_read_timeout = 0
|
self.__light_read_timeout = 0
|
||||||
self.__error_count = 0
|
self.__error_count = 0
|
||||||
self.__runtime_open = 0
|
|
||||||
self.__runtime_close = 0
|
self.slope_power = data_buffer.DataBuffer(SLOPE_COUNT)
|
||||||
|
self.slope_time = data_buffer.DataBuffer(SLOPE_COUNT)
|
||||||
|
|
||||||
def poll(self):
|
def poll(self):
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
@ -75,6 +78,7 @@ class Gate(object):
|
|||||||
|
|
||||||
def __is_transition(self):
|
def __is_transition(self):
|
||||||
if self.__last_state != self.__next_state:
|
if self.__last_state != self.__next_state:
|
||||||
|
logging.info('STATE: ' + self.__last_state + ' -> ' + self.__next_state)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -98,10 +102,13 @@ class Gate(object):
|
|||||||
self.__engine.down()
|
self.__engine.down()
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
self.__engine.stop()
|
self.__engine.stop()
|
||||||
|
slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(),
|
||||||
|
self.slope_power.get())
|
||||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||||
" Opened " + \
|
" Opened " + \
|
||||||
str(self.__power_data.average()) + " mW")
|
str(slope))
|
||||||
self.__power_data.clear()
|
self.slope_power.clear()
|
||||||
|
self.slope_time.clear()
|
||||||
|
|
||||||
if (light_avg != None) and (light_avg <= LIGHT_LX_THRESHOLD["close"]):
|
if (light_avg != None) and (light_avg <= LIGHT_LX_THRESHOLD["close"]):
|
||||||
next_state = STATE_CLOSING
|
next_state = STATE_CLOSING
|
||||||
@ -115,10 +122,14 @@ class Gate(object):
|
|||||||
self.__engine.up()
|
self.__engine.up()
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
self.__engine.stop()
|
self.__engine.stop()
|
||||||
|
slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(),
|
||||||
|
self.slope_power.get())
|
||||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||||
" Closed " + \
|
" Closed " + \
|
||||||
str(self.__power_data.average()) + " mW")
|
str(slope))
|
||||||
self.__power_data.clear()
|
self.__power_data.clear()
|
||||||
|
self.slope_power.clear()
|
||||||
|
self.slope_time.clear()
|
||||||
|
|
||||||
if (light_avg != None) and (light_avg > LIGHT_LX_THRESHOLD["open"]):
|
if (light_avg != None) and (light_avg > LIGHT_LX_THRESHOLD["open"]):
|
||||||
next_state = STATE_OPENING
|
next_state = STATE_OPENING
|
||||||
@ -129,43 +140,48 @@ class Gate(object):
|
|||||||
def __opening_handler(self, light_avg):
|
def __opening_handler(self, light_avg):
|
||||||
next_state = self.__next_state
|
next_state = self.__next_state
|
||||||
if self.__is_transition():
|
if self.__is_transition():
|
||||||
self.__runtime_open = time.time() + MIN_GATE_RUNTIME["open"]
|
|
||||||
self.__engine.up()
|
self.__engine.up()
|
||||||
self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["open"]
|
self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["open"]
|
||||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||||
" Opening " + str(light_avg) + " lx")
|
" Opening " + str(light_avg) + " lx")
|
||||||
|
tm = time.time()
|
||||||
if time.time() > self.__gate_move_timeout:
|
if tm > self.__gate_move_timeout:
|
||||||
next_state = STATE_ERROR
|
next_state = STATE_ERROR
|
||||||
else:
|
else:
|
||||||
self.__power_data.push(self.__power_sensor.power_mw())
|
pwr = self.__power_sensor.power_mw()
|
||||||
current_avg = self.__power_data.average()
|
self.slope_power.push(pwr)
|
||||||
if current_avg != None:
|
self.slope_time.push(tm)
|
||||||
if current_avg > MAX_ENGINE_POWER["up"]:
|
logging.debug('up: ' + str(tm) + ' ' + str(pwr))
|
||||||
if time.time() > self.__runtime_open:
|
slope = 0
|
||||||
next_state = STATE_OPENED
|
if self.slope_power.length() >= SLOPE_CNT_MIN:
|
||||||
|
slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(),
|
||||||
|
self.slope_power.get())
|
||||||
|
if slope > MAX_POWER_SLOPE["up"]:
|
||||||
|
next_state = STATE_OPENED
|
||||||
self.__update_state(next_state)
|
self.__update_state(next_state)
|
||||||
|
|
||||||
def __closing_handler(self, light_avg):
|
def __closing_handler(self, light_avg):
|
||||||
next_state = self.__next_state
|
next_state = self.__next_state
|
||||||
if self.__is_transition():
|
if self.__is_transition():
|
||||||
self.__runtime_close = time.time() + MIN_GATE_RUNTIME["close"]
|
|
||||||
self.__engine.down()
|
self.__engine.down()
|
||||||
self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["close"]
|
self.__gate_move_timeout = time.time() + MAX_GATE_RUNTIME["close"]
|
||||||
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
self.__comserver.transmit(MQTT_TOPIC, str(time.time()) + \
|
||||||
" Closing " + str(light_avg) + " lx")
|
" Closing " + str(light_avg) + " lx")
|
||||||
|
|
||||||
if time.time() > self.__gate_move_timeout:
|
tm = time.time()
|
||||||
|
if tm > self.__gate_move_timeout:
|
||||||
next_state = STATE_ERROR
|
next_state = STATE_ERROR
|
||||||
else:
|
else:
|
||||||
self.__power_data.push(self.__power_sensor.power_mw())
|
pwr = self.__power_sensor.power_mw()
|
||||||
current_avg = self.__power_data.average()
|
self.slope_power.push(pwr)
|
||||||
if current_avg != None:
|
self.slope_time.push(pwr)
|
||||||
if current_avg > MAX_ENGINE_POWER["down"]:
|
logging.debug('dw: ' + str(tm) + ' ' + str(pwr))
|
||||||
if time.time() > self.__runtime_close:
|
slope = 0
|
||||||
next_state = STATE_CLOSED
|
if self.slope_power.length() >= SLOPE_CNT_MIN:
|
||||||
|
slope, _, _, _, _ = scipy.stats.linregress(self.slope_time.get(),
|
||||||
|
self.slope_power.get())
|
||||||
|
if slope > MAX_POWER_SLOPE["down"]:
|
||||||
|
next_state = STATE_OPENED
|
||||||
self.__update_state(next_state)
|
self.__update_state(next_state)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user