myems /
myems-api
| 1 | import falcon |
||
| 2 | import simplejson as json |
||
| 3 | import mysql.connector |
||
| 4 | import config |
||
| 5 | import uuid |
||
| 6 | from datetime import datetime |
||
| 7 | |||
| 8 | |||
| 9 | class DataSourceCollection: |
||
| 10 | @staticmethod |
||
| 11 | def __init__(): |
||
| 12 | pass |
||
| 13 | |||
| 14 | @staticmethod |
||
| 15 | def on_options(req, resp): |
||
| 16 | resp.status = falcon.HTTP_200 |
||
| 17 | |||
| 18 | @staticmethod |
||
| 19 | def on_get(req, resp): |
||
| 20 | cnx = mysql.connector.connect(**config.myems_system_db) |
||
| 21 | cursor = cnx.cursor() |
||
| 22 | |||
| 23 | query = (" SELECT id, name, uuid " |
||
| 24 | " FROM tbl_gateways ") |
||
| 25 | cursor.execute(query) |
||
| 26 | rows_gateways = cursor.fetchall() |
||
| 27 | gateway_dict = dict() |
||
| 28 | if rows_gateways is not None and len(rows_gateways) > 0: |
||
| 29 | for row in rows_gateways: |
||
| 30 | gateway_dict[row[0]] = {"id": row[0], |
||
| 31 | "name": row[1], |
||
| 32 | "uuid": row[2]} |
||
| 33 | |||
| 34 | query = (" SELECT id, name, uuid, gateway_id, protocol, connection, last_seen_datetime_utc " |
||
| 35 | " FROM tbl_data_sources " |
||
| 36 | " ORDER BY id ") |
||
| 37 | cursor.execute(query) |
||
| 38 | rows = cursor.fetchall() |
||
| 39 | cursor.close() |
||
| 40 | cnx.disconnect() |
||
| 41 | |||
| 42 | result = list() |
||
| 43 | now = datetime.utcnow().replace(second=0, microsecond=0, tzinfo=None) |
||
| 44 | if rows is not None and len(rows) > 0: |
||
| 45 | for row in rows: |
||
| 46 | last_seen_time = row[6] |
||
| 47 | if last_seen_time is not None and (now - last_seen_time).total_seconds() > 5 * 60: |
||
| 48 | status = "online" |
||
| 49 | else: |
||
| 50 | status = "offline" |
||
| 51 | meta_result = {"id": row[0], "name": row[1], "uuid": row[2], |
||
| 52 | "gateway": gateway_dict.get(row[3]), |
||
| 53 | "protocol": row[4], |
||
| 54 | "connection": row[5], |
||
| 55 | "last_seen_datetime": row[4].timestamp() * 1000 if isinstance(row[4], |
||
| 56 | datetime) else None, |
||
| 57 | "status": status |
||
| 58 | } |
||
| 59 | |||
| 60 | result.append(meta_result) |
||
| 61 | |||
| 62 | resp.body = json.dumps(result) |
||
| 63 | |||
| 64 | @staticmethod |
||
| 65 | def on_post(req, resp): |
||
| 66 | """Handles POST requests""" |
||
| 67 | try: |
||
| 68 | raw_json = req.stream.read().decode('utf-8') |
||
| 69 | except Exception as ex: |
||
| 70 | raise falcon.HTTPError(falcon.HTTP_400, title='API.ERROR', description=ex) |
||
| 71 | |||
| 72 | new_values = json.loads(raw_json, encoding='utf-8') |
||
| 73 | |||
| 74 | if 'name' not in new_values['data'].keys() or \ |
||
| 75 | not isinstance(new_values['data']['name'], str) or \ |
||
| 76 | len(str.strip(new_values['data']['name'])) == 0: |
||
| 77 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 78 | description='API.INVALID_DATA_SOURCE_NAME') |
||
| 79 | name = str.strip(new_values['data']['name']) |
||
| 80 | |||
| 81 | if 'gateway_id' not in new_values['data'].keys() or \ |
||
| 82 | not isinstance(new_values['data']['gateway_id'], int) or \ |
||
| 83 | new_values['data']['gateway_id'] <= 0: |
||
| 84 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 85 | description='API.INVALID_GATEWAY_ID') |
||
| 86 | gateway_id = new_values['data']['gateway_id'] |
||
| 87 | |||
| 88 | if 'protocol' not in new_values['data'].keys() \ |
||
| 89 | or new_values['data']['protocol'] not in \ |
||
| 90 | ('modbus-tcp', 'modbus-rtu', 'bacnet-ip', 's7', 'profibus', 'profinet', 'opc-ua', 'lora', 'simulation', |
||
| 91 | 'controllogix', 'weather'): |
||
| 92 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 93 | description='API.INVALID_DATA_SOURCE_PROTOCOL.') |
||
| 94 | protocol = new_values['data']['protocol'] |
||
| 95 | |||
| 96 | if 'connection' not in new_values['data'].keys() or \ |
||
| 97 | not isinstance(new_values['data']['connection'], str) or \ |
||
| 98 | len(str.strip(new_values['data']['connection'])) == 0: |
||
| 99 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 100 | description='API.INVALID_CONNECTION') |
||
| 101 | connection = str.strip(new_values['data']['connection']) |
||
| 102 | |||
| 103 | cnx = mysql.connector.connect(**config.myems_system_db) |
||
| 104 | cursor = cnx.cursor() |
||
| 105 | |||
| 106 | cursor.execute(" SELECT name " |
||
| 107 | " FROM tbl_data_sources " |
||
| 108 | " WHERE name = %s ", (name,)) |
||
| 109 | if cursor.fetchone() is not None: |
||
| 110 | cursor.close() |
||
| 111 | cnx.disconnect() |
||
| 112 | raise falcon.HTTPError(falcon.HTTP_404, title='API.BAD_REQUEST', |
||
| 113 | description='API.DATA_SOURCE_NAME_IS_ALREADY_IN_USE') |
||
| 114 | |||
| 115 | cursor.execute(" SELECT name " |
||
| 116 | " FROM tbl_gateways " |
||
| 117 | " WHERE id = %s ", (gateway_id,)) |
||
| 118 | if cursor.fetchone() is None: |
||
| 119 | cursor.close() |
||
| 120 | cnx.disconnect() |
||
| 121 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 122 | description='API.INVALID_GATEWAY_ID') |
||
| 123 | |||
| 124 | add_values = (" INSERT INTO tbl_data_sources (name, uuid, gateway_id, protocol, connection) " |
||
| 125 | " VALUES (%s, %s, %s, %s, %s) ") |
||
| 126 | cursor.execute(add_values, (name, |
||
| 127 | str(uuid.uuid4()), |
||
| 128 | gateway_id, |
||
| 129 | protocol, |
||
| 130 | connection)) |
||
| 131 | new_id = cursor.lastrowid |
||
| 132 | cnx.commit() |
||
| 133 | cursor.close() |
||
| 134 | cnx.disconnect() |
||
| 135 | |||
| 136 | resp.status = falcon.HTTP_201 |
||
| 137 | resp.location = '/datasources/' + str(new_id) |
||
| 138 | |||
| 139 | |||
| 140 | class DataSourceItem: |
||
| 141 | @staticmethod |
||
| 142 | def __init__(): |
||
| 143 | pass |
||
| 144 | |||
| 145 | @staticmethod |
||
| 146 | def on_options(req, resp, id_): |
||
| 147 | resp.status = falcon.HTTP_200 |
||
| 148 | |||
| 149 | @staticmethod |
||
| 150 | def on_get(req, resp, id_): |
||
| 151 | if not id_.isdigit() or int(id_) <= 0: |
||
| 152 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 153 | description='API.INVALID_DATA_SOURCE_ID') |
||
| 154 | |||
| 155 | cnx = mysql.connector.connect(**config.myems_system_db) |
||
| 156 | cursor = cnx.cursor() |
||
| 157 | |||
| 158 | query = (" SELECT id, name, uuid " |
||
| 159 | " FROM tbl_gateways ") |
||
| 160 | cursor.execute(query) |
||
| 161 | rows_gateways = cursor.fetchall() |
||
| 162 | gateway_dict = dict() |
||
| 163 | if rows_gateways is not None and len(rows_gateways) > 0: |
||
| 164 | for row in rows_gateways: |
||
| 165 | gateway_dict[row[0]] = {"id": row[0], |
||
| 166 | "name": row[1], |
||
| 167 | "uuid": row[2]} |
||
| 168 | |||
| 169 | query = (" SELECT id, name, uuid, gateway_id, protocol, connection, last_seen_datetime_utc " |
||
| 170 | " FROM tbl_data_sources " |
||
| 171 | " WHERE id = %s ") |
||
| 172 | cursor.execute(query, (id_,)) |
||
| 173 | row = cursor.fetchone() |
||
| 174 | cursor.close() |
||
| 175 | cnx.disconnect() |
||
| 176 | if row is None: |
||
| 177 | raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', |
||
| 178 | description='API.DATA_SOURCE_NOT_FOUND') |
||
| 179 | |||
| 180 | last_seen_time = row[6] |
||
| 181 | now = datetime.utcnow().replace(second=0, microsecond=0, tzinfo=None) |
||
| 182 | |||
| 183 | if last_seen_time is not None and (now - last_seen_time).total_seconds() > 5 * 60: |
||
| 184 | status = "online" |
||
| 185 | else: |
||
| 186 | status = "offline" |
||
| 187 | |||
| 188 | result = {"id": row[0], "name": row[1], "uuid": row[2], |
||
| 189 | "gateway": gateway_dict.get(row[3]), |
||
| 190 | "protocol": row[4], |
||
| 191 | "connection": row[5], |
||
| 192 | "last_seen_datetime": row[4].timestamp() * 1000 if isinstance(row[4], datetime) else None, |
||
| 193 | "status": status |
||
| 194 | } |
||
| 195 | |||
| 196 | resp.body = json.dumps(result) |
||
| 197 | |||
| 198 | @staticmethod |
||
| 199 | def on_delete(req, resp, id_): |
||
| 200 | if not id_.isdigit() or int(id_) <= 0: |
||
| 201 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 202 | description='API.INVALID_DATA_SOURCE_ID') |
||
| 203 | |||
| 204 | cnx = mysql.connector.connect(**config.myems_system_db) |
||
| 205 | cursor = cnx.cursor() |
||
| 206 | |||
| 207 | cursor.execute(" SELECT name " |
||
| 208 | " FROM tbl_data_sources " |
||
| 209 | " WHERE id = %s ", (id_,)) |
||
| 210 | if cursor.fetchone() is None: |
||
| 211 | cursor.close() |
||
| 212 | cnx.disconnect() |
||
| 213 | raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', |
||
| 214 | description='API.DATA_SOURCE_NOT_FOUND') |
||
| 215 | |||
| 216 | # check if this data source is being used by any meters |
||
| 217 | cursor.execute(" SELECT DISTINCT(m.name) " |
||
| 218 | " FROM tbl_meters m, tbl_meters_points mp, tbl_points p, tbl_data_sources ds " |
||
| 219 | " WHERE m.id = mp.meter_id AND mp.point_id = p.id AND p.data_source_id = ds.id " |
||
| 220 | " AND ds.id = %s " |
||
| 221 | " LIMIT 1 ", |
||
| 222 | (id_,)) |
||
| 223 | row_meter = cursor.fetchone() |
||
| 224 | if row_meter is not None: |
||
| 225 | cursor.close() |
||
| 226 | cnx.disconnect() |
||
| 227 | raise falcon.HTTPError(falcon.HTTP_400, |
||
| 228 | title='API.BAD_REQUEST', |
||
| 229 | description='API.THIS_DATA_SOURCE_IS_BEING_USED_BY_A_METER' + row_meter[0]) |
||
| 230 | |||
| 231 | cursor.execute(" DELETE FROM tbl_points WHERE data_source_id = %s ", (id_,)) |
||
| 232 | cursor.execute(" DELETE FROM tbl_data_sources WHERE id = %s ", (id_,)) |
||
| 233 | cnx.commit() |
||
| 234 | |||
| 235 | cursor.close() |
||
| 236 | cnx.disconnect() |
||
| 237 | resp.status = falcon.HTTP_204 |
||
| 238 | |||
| 239 | @staticmethod |
||
| 240 | def on_put(req, resp, id_): |
||
| 241 | """Handles PUT requests""" |
||
| 242 | try: |
||
| 243 | raw_json = req.stream.read().decode('utf-8') |
||
| 244 | except Exception as ex: |
||
| 245 | raise falcon.HTTPError(falcon.HTTP_400, 'API.ERROR', ex) |
||
| 246 | |||
| 247 | if not id_.isdigit() or int(id_) <= 0: |
||
| 248 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 249 | description='API.INVALID_DATA_SOURCE_ID') |
||
| 250 | |||
| 251 | new_values = json.loads(raw_json, encoding='utf-8') |
||
| 252 | |||
| 253 | if 'name' not in new_values['data'].keys() or \ |
||
| 254 | not isinstance(new_values['data']['name'], str) or \ |
||
| 255 | len(str.strip(new_values['data']['name'])) == 0: |
||
| 256 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 257 | description='API.INVALID_DATA_SOURCE_NAME') |
||
| 258 | name = str.strip(new_values['data']['name']) |
||
| 259 | |||
| 260 | if 'gateway_id' not in new_values['data'].keys() or \ |
||
| 261 | not isinstance(new_values['data']['gateway_id'], int) or \ |
||
| 262 | new_values['data']['gateway_id'] <= 0: |
||
| 263 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 264 | description='API.INVALID_GATEWAY_ID') |
||
| 265 | gateway_id = new_values['data']['gateway_id'] |
||
| 266 | |||
| 267 | if 'protocol' not in new_values['data'].keys() \ |
||
| 268 | or new_values['data']['protocol'] not in \ |
||
| 269 | ('modbus-tcp', 'modbus-rtu', 'bacnet-ip', 's7', 'profibus', 'profinet', 'opc-ua', 'lora', 'simulation', |
||
| 270 | 'controllogix', 'weather'): |
||
| 271 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 272 | description='API.INVALID_DATA_SOURCE_PROTOCOL.') |
||
| 273 | protocol = new_values['data']['protocol'] |
||
| 274 | |||
| 275 | if 'connection' not in new_values['data'].keys() or \ |
||
| 276 | not isinstance(new_values['data']['connection'], str) or \ |
||
| 277 | len(str.strip(new_values['data']['connection'])) == 0: |
||
| 278 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 279 | description='API.INVALID_CONNECTION') |
||
| 280 | connection = str.strip(new_values['data']['connection']) |
||
| 281 | |||
| 282 | cnx = mysql.connector.connect(**config.myems_system_db) |
||
| 283 | cursor = cnx.cursor() |
||
| 284 | |||
| 285 | cursor.execute(" SELECT name " |
||
| 286 | " FROM tbl_data_sources " |
||
| 287 | " WHERE id = %s ", (id_,)) |
||
| 288 | if cursor.fetchone() is None: |
||
| 289 | cursor.close() |
||
| 290 | cnx.disconnect() |
||
| 291 | raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', |
||
| 292 | description='API.DATA_SOURCE_NOT_FOUND') |
||
| 293 | |||
| 294 | cursor.execute(" SELECT name " |
||
| 295 | " FROM tbl_gateways " |
||
| 296 | " WHERE id = %s ", (gateway_id,)) |
||
| 297 | if cursor.fetchone() is None: |
||
| 298 | cursor.close() |
||
| 299 | cnx.disconnect() |
||
| 300 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 301 | description='API.INVALID_GATEWAY_ID') |
||
| 302 | |||
| 303 | update_row = (" UPDATE tbl_data_sources " |
||
| 304 | " SET name = %s, gateway_id = %s, protocol = %s, connection = %s " |
||
| 305 | " WHERE id = %s ") |
||
| 306 | cursor.execute(update_row, (name, |
||
| 307 | gateway_id, |
||
| 308 | protocol, |
||
| 309 | connection, |
||
| 310 | id_,)) |
||
| 311 | cnx.commit() |
||
| 312 | |||
| 313 | cursor.close() |
||
| 314 | cnx.disconnect() |
||
| 315 | |||
| 316 | resp.status = falcon.HTTP_200 |
||
| 317 | |||
| 318 | |||
| 319 | View Code Duplication | class DataSourcePointCollection: |
|
|
0 ignored issues
–
show
Duplication
introduced
by
Loading history...
|
|||
| 320 | @staticmethod |
||
| 321 | def __init__(): |
||
| 322 | pass |
||
| 323 | |||
| 324 | @staticmethod |
||
| 325 | def on_options(req, resp): |
||
| 326 | resp.status = falcon.HTTP_200 |
||
| 327 | |||
| 328 | @staticmethod |
||
| 329 | def on_get(req, resp, id_): |
||
| 330 | if not id_.isdigit() or int(id_) <= 0: |
||
| 331 | raise falcon.HTTPError(falcon.HTTP_400, title='API.BAD_REQUEST', |
||
| 332 | description='API.INVALID_DATA_SOURCE_ID') |
||
| 333 | |||
| 334 | cnx = mysql.connector.connect(**config.myems_system_db) |
||
| 335 | cursor = cnx.cursor() |
||
| 336 | |||
| 337 | cursor.execute(" SELECT name " |
||
| 338 | " FROM tbl_data_sources " |
||
| 339 | " WHERE id = %s ", (id_,)) |
||
| 340 | if cursor.fetchone() is None: |
||
| 341 | cursor.close() |
||
| 342 | cnx.disconnect() |
||
| 343 | raise falcon.HTTPError(falcon.HTTP_404, title='API.NOT_FOUND', |
||
| 344 | description='API.DATA_SOURCE_NOT_FOUND') |
||
| 345 | |||
| 346 | result = list() |
||
| 347 | # Get points of the data source |
||
| 348 | # NOTE: there is no uuid in tbl_points |
||
| 349 | query_point = (" SELECT id, name, object_type, " |
||
| 350 | " units, high_limit, low_limit, ratio, is_trend, address, description " |
||
| 351 | " FROM tbl_points " |
||
| 352 | " WHERE data_source_id = %s " |
||
| 353 | " ORDER BY name ") |
||
| 354 | cursor.execute(query_point, (id_,)) |
||
| 355 | rows_point = cursor.fetchall() |
||
| 356 | |||
| 357 | if rows_point is not None and len(rows_point) > 0: |
||
| 358 | for row in rows_point: |
||
| 359 | meta_result = {"id": row[0], |
||
| 360 | "name": row[1], |
||
| 361 | "object_type": row[2], |
||
| 362 | "units": row[3], |
||
| 363 | "high_limit": row[4], |
||
| 364 | "low_limit": row[5], |
||
| 365 | "ratio": float(row[6]), |
||
| 366 | "is_trend": True if row[7] else False, |
||
| 367 | "address": row[8], |
||
| 368 | "description": row[9]} |
||
| 369 | result.append(meta_result) |
||
| 370 | |||
| 371 | cursor.close() |
||
| 372 | cnx.disconnect() |
||
| 373 | resp.body = json.dumps(result) |
||
| 374 |