config.py 6.2 KB
Newer Older
Nelso Jost's avatar
Nelso Jost committed
1
2
import configparser
import sys
3
4
import os
from pprint import pprint
Nelso Jost's avatar
Nelso Jost committed
5

6
7

DEFAULT_INI=\
Nelso Jost's avatar
Nelso Jost committed
8
9
"""\
[server]
10
11
12
13
; base URL of the server
URL = http://dados.cta.if.ufrgs.br/emm

; board identification number (auto generated when a new board is created)
Nelso Jost's avatar
Nelso Jost committed
14
15
16
17
18
19
BOARD_ID =

; authentication token for the board's user
USER_HASH =


20
21
[reading]
; CSV list of sensor names/nicknames (order reflect columns on datalog files)
Nelso Jost's avatar
Nelso Jost committed
22
23
SENSORS = DHT22_TEMP, DHT22_AH, BMP085_PRESSURE, LDR

24
; time between logger cycles -- format:   hours:minutes:seconds
Nelso Jost's avatar
Nelso Jost committed
25
26
INTERVAL = 0:5:0

27
28
; true for read board clock; if fail, or false, system's time will be used
RTC_DS1307 = true
Nelso Jost's avatar
Nelso Jost committed
29
30
31


[datalog]
32
; CSV delimiter for local log files (sugestions: ',' or ';' or '\t')
Nelso Jost's avatar
Nelso Jost committed
33
34
35
36
37
38
39
CSV_SEP = '\t'

; format of the datetime column (see Python docs on datetime module)
DATETIME_FORMAT = %Y-%m-%d-%H-%M-%S


[arduino]
40
41
; CSV list of ports to attempt or blank for automatic search on
; /dev/ttyACM* and /dev/ttyUSB* (where * varies from 0 to 10)
Nelso Jost's avatar
Nelso Jost committed
42
43
44
SERIAL_PORT =
"""

45

Nelso Jost's avatar
Nelso Jost committed
46
47
48
49
def make_path_here(filename):
    ''' Append filename to the current __file__ path. '''
    return os.path.join(os.path.abspath(os.path.dirname(__file__)), filename)

50
51
52
53
54
55
56
57
58
59
60
61
62
63

class RTCDateTime:
    SENSOR_NAME = 'RTC_DS1307'
    READ_TIMESTAMP = '%Y-%m-%d %H:%M:%S'
    __qualname__ = "RTCDateTime fmt='{}'".format(READ_TIMESTAMP)

    def __init__(self, s):
        self.dt = datetime.strptime(s, self.RTC_DT_FMT)

    def __str__(self):
        return self.dt.strftime('%Y%m%d%H%M%S')


class Config:
Nelso Jost's avatar
Nelso Jost committed
64
    SETTINGS_FILENAME = make_path_here('../../settings.ini')
65
66
67
    DATALOG_PATH = make_path_here('../../data/')
    EXECUTION_LOG_PATH = make_path_here('../logs/')
    OUTGOING_FILENAME = make_path_here('../../data/outgoing.json')
Nelso Jost's avatar
Nelso Jost committed
68

69
70
    URL_API_POST_RAWSENSORDATA = '{base}api/post/rawsensordata/{bid}'
    SERIAL_CSV_SEP = ','
Nelso Jost's avatar
Nelso Jost committed
71
72
73

    def __init__(self):
        super().__init__()
74
75
76
77
78
79
80
81
82
83
84
        self.default = configparser.ConfigParser()
        self.default.read_string(DEFAULT_INI)
        try:
            self.load_settings()
        except Exception as e:
            print('\nConfigurationError:', e)


    def __getitem__(self, key):
        return self._parser._sections[key]

Nelso Jost's avatar
Nelso Jost committed
85
86

    def ask_restore_settings_file(self):
87
        a = input("\nRestore default file now and overwrite all"
Nelso Jost's avatar
Nelso Jost committed
88
                  "current values? [y/N] ")
89
        if 'y' in a.lowercase():
Nelso Jost's avatar
Nelso Jost committed
90
91
92
93
94
95
96
97
98
99
100
101
            try:
                with open(self.SETTINGS_FILENAME, 'w') as f:
                    f.write(DEFAULT_SETTINGS)
                print("Done! Restart the application!")
                sys.exit(0)
            except:
                print("Unable to write on file\n    {}\n\nTry to run this "
                      "program from another location!".format(
                          self.SETTINGS_FILENAME))
                sys.exit(1)


102
    def load_settings(self):
Nelso Jost's avatar
Nelso Jost committed
103
        '''
104
        Load the INI configuration file onto the self._parser attribute.
Nelso Jost's avatar
Nelso Jost committed
105
        '''
106
        self._parser = configparser.ConfigParser()
Nelso Jost's avatar
Nelso Jost committed
107
        try:
108
            self._parser.read(self.SETTINGS_FILENAME)
Nelso Jost's avatar
Nelso Jost committed
109
110
111
112
        except:
            print("Unable to open configuration file at\n    {}".format(
                  self.SETTINGS_FILENAME))

113
114
115
116
117
118
119
120
121
        pprint(self._parser._sections)

        self.validate_server_url()
        self.validate_reading_sensors()
        self.validate_reading_interval()
        self.validate_datalog_csv_sep()
        self.validate_arduino_serial_port()

        pprint(self._parser._sections)
Nelso Jost's avatar
Nelso Jost committed
122
123


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
    def validate_server_url(self):
        try:
            bid = int(self['server']['board_id'])
        except:
            raise Exception(
                "Invalid numeric value for the server/BOARD_ID key.\n"
                "Expected to be of type integer.\n"
                "Was given:\n   {}".format(self['server']['board_id']))

        self['server']['api_post_url'] = self.URL_API_POST_RAWSENSORDATA\
            .format(base=self['server']['url'] +
                        ('' if self['server']['url'].endswith('/') else '/'),
                    bid=bid)

    def validate_reading_sensors(self):
        self['reading']['sensors'] = [x.strip() for x in
            self['reading']['sensors'].split(',') if x.strip() != '']
Nelso Jost's avatar
Nelso Jost committed
141

142
143
144
145
        if len(self['reading']['sensors']) == 0:
            raise Exception(
                    "At least one sensor must be present on the reading/"
                    "SENSORS key.")
Nelso Jost's avatar
Nelso Jost committed
146

147
148
        if 'true' in self['reading']['rtc_ds1307'].lower():
            self['reading']['sensors'].append(RTCDateTime.SENSOR_NAME)
Nelso Jost's avatar
Nelso Jost committed
149

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
        self['reading']['command'] = 'readSensors,' + ','.join(
            self['reading']['sensors'])


    def validate_reading_interval(self):
        try:
            numbers = self['reading']['interval'].split(':')
            if len(numbers) != 3: raise Exception
            self['reading']['interval'] = {k: int(v) for k, v in
                zip(['H', 'M', 'S'], numbers)}
            self['reading']['interval_seconds'] =\
                3600 * self['reading']['interval']['H'] + \
                  60 * self['reading']['interval']['M'] + \
                       self['reading']['interval']['S']
        except:
            raise Exception(
                "Invalid time value for the reading/INTERVAL key.\n"
                "Expected format:\n"
                "    hours:minutes:seconds  (integer numbers)\n"
                "Was given:\n    {}".format(self['reading']['interval']))


    def validate_datalog_csv_sep(self):
        value = self['datalog']['csv_sep']
        self['datalog']['csv_sep'] = bytes(value, 'utf8').decode(
                                           'unicode_escape')
        if not value in (',', ';', r'\t'):
            raise Exception(
                "Invalid character value for the datalog/CSV_SEP key.\n"
                "Supported values:\n    ','   ';'   '\\t'\n"
                "Was given:\n    {}".format(value))

    def validate_arduino_serial_port(self):

        self['arduino']['serial_port'] = [x.strip() for x in
            self['arduino']['serial_port'].split(',') if x.strip() != '']

        if len(self['arduino']['serial_port']) == 0:
            self['arduino']['serial_port'] = []

            for i in range(5):
                self['arduino']['serial_port'].append('/dev/ttyACM{}'.format(i))
                self['arduino']['serial_port'].append('/dev/ttyUSB{}'.format(i))