Commit 958a4ebd authored by Nelso Jost's avatar Nelso Jost
Browse files

FIX: load settings type checking; better deploy management

parent 8b4cdf43
...@@ -4,4 +4,5 @@ ...@@ -4,4 +4,5 @@
*.csv *.csv
*.json *.json
*.log *.log
pid_*
__pycache__ __pycache__
PY := python3 PY := python3
VENV := .venv VENV := .venv
all: all: help
help:
@ echo "USAGE:" @ echo "USAGE:"
@ echo " make deb-install -- Attempt to install required system wide"
@ echo " Debian packages via apt"
@ echo "" @ echo ""
@ echo " make setup -- Create a local Python virtual environment" @ echo " make deb-install -- Uses apt to install required system tools"
@ echo " that will hold requirements.pip modules" @ echo " (for Debian-based systems only)"
@ echo ""
@ echo " make setup -- Create a local Python virtual environment"
@ echo " that will hold requirements.pip modules"
@ echo ""
@ echo " make run -- Executes the logger"
@ echo ""
@ echo " make deploy -- Register meteorologger proccess on the"
@ echo " supervisor daemon tool (requires root)"
@ echo "" @ echo ""
@ echo " make run -- Executes the logger " @ echo " make undeploy -- Unregister meteorologger proccess on the"
@ echo " supervisor daemon tool (requires root)"
@ echo ""
@ echo " make tail-exec -- Follow the logger's execution.log"
setup: install-deb venv setup: install-deb venv
...@@ -35,7 +46,13 @@ clean-venv: ...@@ -35,7 +46,13 @@ clean-venv:
rm -rf ${VENV} rm -rf ${VENV}
run: run:
${VENV}/bin/python3 logger.py -v sudo ${VENV}/bin/python3 logger.py -v
deploy: deploy:
${VENV}/bin/python3 deploy.py sudo ${VENV}/bin/python3 deploy.py
undeploy:
sudo ${VENV}/bin/python3 deploy.py -u
tail-exec:
tail -F logs/execution.log
import jinja2 import jinja2
import os import os
import subprocess import subprocess
import sys
PROCESS_NAME = 'meteorologger'
BASE_DIR = os.path.abspath(os.path.dirname(__file__)) BASE_DIR = os.path.abspath(os.path.dirname(__file__))
SUPERVISOR_CONFIG_FILENAME = '/etc/supervisor/conf.d/meteorologger.conf' SUPERVISOR_CONFIG_FILENAME = '/etc/supervisor/conf.d/{}.conf'\
.format(PROCESS_NAME)
PID_FILENAME = 'logs/pid_{}'.format(PROCESS_NAME)
def deploy_supervisor(): def deploy_supervisor():
with open('supervisor.conf') as f_temp: with open('supervisor.conf') as f_temp:
template = jinja2.Template(f_temp.read()) template = jinja2.Template(f_temp.read())
config_file_str = template.render(base_dir=BASE_DIR) config_file_str = template.render(base_dir=BASE_DIR,
process_name=PROCESS_NAME)
print('\nRegistering supervisor config at \n {}' print('\nRegistering supervisor config at \n {}'
.format(SUPERVISOR_CONFIG_FILENAME)) .format(SUPERVISOR_CONFIG_FILENAME))
...@@ -23,16 +30,34 @@ def deploy_supervisor(): ...@@ -23,16 +30,34 @@ def deploy_supervisor():
print('\nRestarting supervisor..') print('\nRestarting supervisor..')
proc = subprocess.Popen('supervisorctl update', shell=True) proc = subprocess.Popen('supervisorctl update', shell=True)
proc.wait()
print("PID: ", end='') proc = subprocess.Popen('supervisorctl pid {}'.format(PROCESS_NAME),
proc = subprocess.Popen('supervisorctl pid meteorologger', shell=True, shell=True, stdout=subprocess.PIPE)
stdout=subprocess.PIPE)
proc.wait() proc.wait()
pid = proc.stdout.read().decode('ascii').strip()
print(proc.stdout.read().decode('ascii').strip(), end='') with open(PID_FILENAME, 'w') as f:
print(' [meteorologger is running]') f.write(pid + '\n')
print("\nPID: {} (saved at '{}')".format(pid, PID_FILENAME))
print('\n[{} process is running]'.format(PROCESS_NAME))
print('\nYou can manage it with supervisorctl tool.') print('\nYou can manage it with supervisorctl tool.')
def undeploy_supervisor():
print('\nRemoving supervisor config file at\n {}'
.format(SUPERVISOR_CONFIG_FILENAME))
os.system('rm -f {}'.format(SUPERVISOR_CONFIG_FILENAME))
os.system('rm -f {}'.format(PID_FILENAME))
print('\nRestarting supervisor..')
proc = subprocess.Popen('supervisorctl update', shell=True)
proc.wait()
if __name__ == '__main__': if __name__ == '__main__':
deploy_supervisor() if '-u' in sys.argv:
undeploy_supervisor()
else:
deploy_supervisor()
...@@ -64,6 +64,7 @@ class Meteorologger: ...@@ -64,6 +64,7 @@ class Meteorologger:
SERIAL_READ_TIMEOUT = 1.5 # seconds SERIAL_READ_TIMEOUT = 1.5 # seconds
FIND_PORT_TIMEOUT = 10 # seconds FIND_PORT_TIMEOUT = 10 # seconds
BOARD_RESET_TIMEOUT = 2 # seconds BOARD_RESET_TIMEOUT = 2 # seconds
BOARD_RESPONSE_DELAY = 0 # seconds
verbose = True verbose = True
...@@ -92,10 +93,15 @@ class Meteorologger: ...@@ -92,10 +93,15 @@ class Meteorologger:
': '.join(args))) ': '.join(args)))
if expected_type is not None: if expected_type is not None:
try: try:
return expected_type(r) if expected_type is int and isinstance(r, float):
raise TypeError
elif expected_type is str and not isinstance(r, str):
raise TypeError
else:
return expected_type(r)
except: except:
raise TypeError("Expected type {} on key {}".format( raise TypeError("Expected {} on key {} but got\n {}"
expected_type, ':'.join(args))) .format(expected_type, '/'.join(args), r))
return r return r
def load_settings(self): def load_settings(self):
...@@ -127,6 +133,9 @@ class Meteorologger: ...@@ -127,6 +133,9 @@ class Meteorologger:
self._getCFG('ARDUINO', 'SERIAL_PORT', self._getCFG('ARDUINO', 'SERIAL_PORT',
expected_type=str).split(',')] expected_type=str).split(',')]
self.BOARD_RESPONSE_DELAY = self._getCFG('ARDUINO', 'RESPONSE_DELAY',
expected_type=int)
self.READING_INTERVAL_SECONDS =\ self.READING_INTERVAL_SECONDS =\
self._getCFG('LOGGER', 'READING_INTERVAL', 'seconds', self._getCFG('LOGGER', 'READING_INTERVAL', 'seconds',
expected_type=int) + \ expected_type=int) + \
...@@ -257,6 +266,8 @@ class Meteorologger: ...@@ -257,6 +266,8 @@ class Meteorologger:
logging.info("sent: '{}' ({} bytes)".format( logging.info("sent: '{}' ({} bytes)".format(
self.SENSORS_CSV_LINE, result)) self.SENSORS_CSV_LINE, result))
time.sleep(self.BOARD_RESPONSE_DELAY)
result_line = ser.readline() result_line = ser.readline()
logging.info("read: {} ({} bytes)".format(result_line, logging.info("read: {} ({} bytes)".format(result_line,
......
SERVER: SERVER:
# full URL that accepts POST method for sending data to the server # full URL that accepts POST method for sending data to the server
# it should end with a valid board ID number (see documentation) # WARNING: replace 'BID' with a valid board ID board ID number
# #
API_POST_URL: http://localhost/emm/api/post/rawsensordata/2 API_POST_URL: http://dados.cta.if.ufrgs.br/emm/api/post/rawsensordata/BID
LOGGER: LOGGER:
# time between readings attempts (cycles of the logger execution) # time between readings attempts (cycles of the logger execution)
...@@ -30,7 +30,11 @@ ARDUINO: ...@@ -30,7 +30,11 @@ ARDUINO:
# Arduino serial communication protocol (same as in meteorolog.ino) # Arduino serial communication protocol (same as in meteorolog.ino)
# #
BAUD_RATE: 9600 # 115200 seems unstable with Uno + PySerial BAUD_RATE: 9600 # 115200 seems unstable with Uno + PySerial
# time (in seconds) to wait before send a readrequest to the serial port
#
RESPONSE_DELAY: 3
FILES: FILES:
# file which will hold all data locally (regardless of the web server) # file which will hold all data locally (regardless of the web server)
......
[program:meteorologger] [program:{{ process_name }}]
command={{ base_dir }}/.venv/bin/python3 {{ base_dir }}/logger.py command={{ base_dir }}/.venv/bin/python3 {{ base_dir }}/logger.py
directory={{ base_dir }} directory={{ base_dir }}
user=root user=root
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment