209 lines
6.6 KiB
Python
209 lines
6.6 KiB
Python
import datetime
|
|
import json
|
|
import logging
|
|
import os
|
|
import requests
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
|
from dateutil.parser import parser
|
|
from flask import Flask
|
|
from flask import jsonify
|
|
from flask import render_template
|
|
from flask_sqlalchemy import SQLAlchemy
|
|
from math import atan2
|
|
from math import cos
|
|
from math import radians
|
|
from math import sin
|
|
from math import sqrt
|
|
|
|
from config import app_key
|
|
from config import application
|
|
from config import bing_api_key
|
|
from config import cluster
|
|
from config import config_app
|
|
from config import devices
|
|
from config import gateway_locations
|
|
from config import path_db
|
|
from config import refresh_period_seconds
|
|
from config import start_lat
|
|
from config import start_lon
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = config_app(Flask(__name__, template_folder="./templates"))
|
|
db = SQLAlchemy(app)
|
|
|
|
|
|
def schedule_get_new_data():
|
|
get_new_data()
|
|
|
|
|
|
scheduler = BackgroundScheduler()
|
|
job = scheduler.add_job(schedule_get_new_data, 'interval', days=1)
|
|
scheduler.start()
|
|
|
|
|
|
class Location(db.Model):
|
|
__tablename__ = "location"
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
added_at = db.Column(db.DateTime, default=datetime.datetime.now)
|
|
device_id = db.Column(db.String(250))
|
|
raw = db.Column(db.String(250))
|
|
datetime = db.Column(db.String(250))
|
|
datetime_obj = db.Column(db.DateTime)
|
|
latitude = db.Column(db.String(250))
|
|
longitude = db.Column(db.String(250))
|
|
altitude = db.Column(db.Integer)
|
|
hdop = db.Column(db.Float)
|
|
|
|
def __repr__(self):
|
|
return '<ID %r>' % self.id
|
|
|
|
@property
|
|
def serialize(self):
|
|
"""Return object data in easily serializeable format"""
|
|
return {
|
|
'device_id': self.device_id,
|
|
'datetime': self.datetime,
|
|
'latitude': self.latitude,
|
|
'longitude': self.longitude,
|
|
'altitude': self.altitude,
|
|
'hdop': self.hdop
|
|
}
|
|
|
|
|
|
class LastAcquisition(db.Model):
|
|
__tablename__ = "last_data"
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
last_datetime = db.Column(db.DateTime)
|
|
|
|
def __repr__(self):
|
|
return '<ID %r>' % self.id
|
|
|
|
|
|
if not os.path.exists(path_db):
|
|
db.create_all()
|
|
|
|
|
|
@app.route('/map')
|
|
def main_page():
|
|
get_new_data()
|
|
return render_template('map.html',
|
|
bing_api_key=bing_api_key,
|
|
devices=devices,
|
|
gateway_locations=gateway_locations,
|
|
location_data=Location.query.all(),
|
|
refresh_period_seconds=refresh_period_seconds,
|
|
start_lat=start_lat,
|
|
start_lon=start_lon)
|
|
|
|
|
|
@app.route('/past/<seconds>')
|
|
def get_past_data(seconds):
|
|
if seconds_from_last() > 10:
|
|
get_new_data()
|
|
|
|
if seconds == '0':
|
|
markers = Location.query.all()
|
|
else:
|
|
past_dt_object = datetime.datetime.now() - datetime.timedelta(seconds=int(seconds))
|
|
markers = Location.query.filter(Location.added_at > past_dt_object).all()
|
|
return jsonify([i.serialize for i in markers])
|
|
|
|
|
|
def get_new_data():
|
|
last_seconds = seconds_from_last()
|
|
if last_seconds:
|
|
past_seconds = int(last_seconds) + 1
|
|
else:
|
|
past_seconds = 604800 # 7 days, max The Things Network storage allows
|
|
|
|
for each_device in devices:
|
|
endpoint = "https://{cluster_loc}.cloud.thethings.network/api/v3/as/applications/{app}/devices/{dev}/packages/storage/uplink_message?order=-received_at&type=uplink_message?last={time}".format(
|
|
cluster_loc=cluster, app=application, dev=each_device, time="{}s".format(past_seconds))
|
|
logger.info(endpoint)
|
|
key = 'Bearer {}'.format(app_key)
|
|
headers = {'Accept': 'text/event-stream', 'Authorization': key}
|
|
response = requests.get(endpoint, headers=headers)
|
|
if response.status_code != 200:
|
|
logger.info(response.reason)
|
|
try:
|
|
response_format = "{\"data\": [" + response.text.replace("\n\n", ",")[:-1] + "]}"
|
|
response_data = json.loads(response_format)
|
|
uplink_msg = response_data["data"]
|
|
for each_resp in uplink_msg:
|
|
response_data = each_resp["result"]
|
|
uplink_message = response_data["uplink_message"]
|
|
|
|
received = response_data["received_at"]
|
|
lat = uplink_message["decoded_payload"].get("latitude", "")
|
|
lon = uplink_message["decoded_payload"].get("longitude", "")
|
|
alt = uplink_message["decoded_payload"].get("altitude", "")
|
|
qos = uplink_message["decoded_payload"].get("hdop", "")
|
|
end_device_ids = response_data["end_device_ids"]
|
|
device = end_device_ids["device_id"]
|
|
rawpay = uplink_message["frm_payload"]
|
|
|
|
if (not Location.query.filter(Location.datetime == received).first() and
|
|
-90 < float(lat) <= 90 and -120 <= float(lon) <= 80):
|
|
logger.info("{}, {}".format(lat, lon))
|
|
new_location = Location(
|
|
device_id=device,
|
|
raw=rawpay,
|
|
datetime_obj=parser().parse(received),
|
|
datetime=received,
|
|
latitude=lat,
|
|
longitude=lon,
|
|
altitude=alt,
|
|
hdop=qos)
|
|
db.session.add(new_location)
|
|
db.session.commit()
|
|
logger.info(new_location)
|
|
except:
|
|
pass
|
|
|
|
set_date_now()
|
|
|
|
|
|
def set_date_now():
|
|
date_last = LastAcquisition.query.first()
|
|
if not date_last:
|
|
new_last = LastAcquisition(last_datetime=datetime.datetime.now())
|
|
db.session.add(new_last)
|
|
db.session.commit()
|
|
else:
|
|
date_last.last_datetime = datetime.datetime.now()
|
|
db.session.commit()
|
|
|
|
|
|
def seconds_from_last():
|
|
date_last = LastAcquisition.query.first()
|
|
if date_last:
|
|
return (datetime.datetime.now() - date_last.last_datetime).total_seconds()
|
|
|
|
|
|
def distance_coordinates(lat1, lon1, lat2, lon2):
|
|
# approximate radius of earth in km
|
|
R = 6373.0
|
|
|
|
lat1 = radians(lat1)
|
|
lon1 = radians(lon1)
|
|
lat2 = radians(lat2)
|
|
lon2 = radians(lon2)
|
|
|
|
dlon = lon2 - lon1
|
|
dlat = lat2 - lat1
|
|
|
|
a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
|
|
c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
|
|
|
distance = R * c
|
|
|
|
return distance # km
|
|
|
|
|
|
if __name__ == '__main__':
|
|
db.create_all()
|
|
app.run(debug=True, host='0.0.0.0', port=8000)
|