diff --git a/apps/atrad/__init__.py b/apps/atrad/__init__.py new file mode 100644 index 0000000..81831c6 --- /dev/null +++ b/apps/atrad/__init__.py @@ -0,0 +1,2 @@ +from . import mqtt +mqtt.client.loop_start() \ No newline at end of file diff --git a/apps/atrad/admin.py b/apps/atrad/admin.py new file mode 100644 index 0000000..4e2323f --- /dev/null +++ b/apps/atrad/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin +from .models import ATRADConfiguration + +# Register your models here. + +admin.site.register(ATRADConfiguration) diff --git a/apps/atrad/apps.py b/apps/atrad/apps.py new file mode 100644 index 0000000..43d6707 --- /dev/null +++ b/apps/atrad/apps.py @@ -0,0 +1,6 @@ +#from django.apps import AppConfig + + +#class AtradConfig(AppConfig):# +# default_auto_field = 'django.db.models.BigAutoField' +# name = 'atrad' diff --git a/apps/atrad/files.py b/apps/atrad/files.py new file mode 100644 index 0000000..a9d150f --- /dev/null +++ b/apps/atrad/files.py @@ -0,0 +1,19 @@ +import json + +def read_json_file(fp): + + kwargs = {} + + json_data = fp + data = json.load(json_data) + json_data.close() + + topic = data["topic"][0][1] + + kwargs['topic'] = topic + + return kwargs + + +def write_json_file(filename): + pass \ No newline at end of file diff --git a/apps/atrad/forms.py b/apps/atrad/forms.py new file mode 100644 index 0000000..7929874 --- /dev/null +++ b/apps/atrad/forms.py @@ -0,0 +1,27 @@ +from django import forms +from apps.main.models import Device +from .models import ATRADConfiguration + +class ATRADConfigurationForm(forms.ModelForm): + + def __init__(self, *args, **kwargs): + super(ATRADConfigurationForm, self).__init__(*args, **kwargs) + instance = getattr(self, 'instance', None) + + if instance and instance.pk: + devices = Device.objects.filter(device_type__name='atrad') + if instance.experiment: + self.fields['experiment'].widget.attrs['disabled'] = 'disabled' + self.fields['device'].widget.choices = [(device.id, device) for device in devices] + + def clean(self): + return + + class Meta: + model = ATRADConfiguration + exclude = ('type', 'parameters', 'status', 'author', 'hash') + + +class UploadFileForm(forms.Form): + title = forms.CharField(label='Extension Type', widget=forms.TextInput(attrs={'readonly':'readonly'})) + file = forms.FileField() diff --git a/apps/atrad/models.py b/apps/atrad/models.py new file mode 100644 index 0000000..8157708 --- /dev/null +++ b/apps/atrad/models.py @@ -0,0 +1,175 @@ +from django.db import models +from apps.main.models import Configuration +from apps.main.utils import Params +from django.core.validators import MinValueValidator, MaxValueValidator + +from .files import read_json_file +import requests +# Create your models here. validators=[MinValueValidator(62.5e6), MaxValueValidator(450e6)] + +class ATRADConfiguration(Configuration): + + topic = models.PositiveIntegerField(verbose_name='Topic',validators=[MaxValueValidator(10)], default = 0) + + def verify_frequencies(self): + + return True + + + def status_device(self): + + ip=self.device.ip_address + port=self.device.port_address + + route = "http://" + str(ip) + ":" + str(port) + "/status/" + try: + r = requests.get(route, timeout=0.7) + except Exception as e: + self.device.status = 0 + self.device.save() + self.message = 'Could not read TX status: ' + str(e) + return False + + response = r.json() + self.device.status = response['status'] + self.message = response['message'] + self.device.save() + + if response['components_status']==0: + return False + + return True + + + def start_device(self): + + ip=self.device.ip_address + port=self.device.port_address + + #---Device must be configured + if not self.device.status == 2: + self.message = 'TX Device must be configured.' + return False + #---Frequencies from form + post_data = self.parms_to_dict() + route = "http://" + str(ip) + ":" + str(port) + "/write/" + + try: + r = requests.post(route, post_data, timeout=0.7) + except Exception as e: + self.message = "Could not start TX device. "+str(e) + return False + + response = r.json() + if response['status']==1: + self.device.status = 1 + self.device.save() + self.message = response['message'] + return False + + self.device.status = response['status'] + self.device.save() + self.message = response['message'] + + return True + + + def stop_device(self): + + ip=self.device.ip_address + port=self.device.port_address + + if self.device.status == 2: #Configured + self.message = 'TX device is already stopped.' + return False + + post_data = {"topic":0} + route = "http://" + str(ip) + ":" + str(port) + "/write/" + + try: + r = requests.post(route, post_data, timeout=0.7) + except Exception as e: + self.message = "Could not write TX parameters. "+str(e) + self.device.status = 0 + self.device.save() + return False + + response = r.json() + status = response['status'] + if status == 1: + self.device.status = status + self.device.save() + self.message = 'Could not stop TX device.' + return False + + self.message = 'TX device has been stopped successfully.' + self.device.status = 2 + self.device.save() + + return True + + + def read_device(self): + + ip=self.device.ip_address + port=self.device.port_address + + route = "http://" + str(ip) + ":" + str(port) + "/read/" + try: + frequencies = requests.get(route,timeout=0.7) + except: + self.message = "Could not read TX parameters from this device" + return None + + frequencies = frequencies.json() + if frequencies: + frequencies = frequencies.get("Frequencies") + topic = frequencies.get("topic") + + parms = {'topic': topic} + + self.message = "TX parameters have been successfully read" + return parms + else: + self.message = "Error reading TX parameters" + return None + + + def write_device(self): + + ip=self.device.ip_address + port=self.device.port_address + + #---Frequencies from form + parms = self.parms_to_dict()['configurations'] + for parm in parms['allIds']: + byid = parm + frequencies = parms['byId'][byid] + post_data = {} + for data in frequencies: + if data in ['topic']: + post_data[data] = frequencies[data] + + route = "http://" + str(ip) + ":" + str(port) + "/write/" + print (post_data) + try: + r = requests.post(route, post_data, timeout=0.7) + except: + self.message = "Could not write TX parameters" + self.device.status = 0 + self.device.save() + return False + + response = r.json() + self.message = response['message'] + self.device.status = response['status'] + self.device.save() + + if self.device.status==1: + return False + + return True + + + class Meta: + db_table = 'atrad_configurations' \ No newline at end of file diff --git a/apps/atrad/mqtt.py b/apps/atrad/mqtt.py new file mode 100644 index 0000000..0e80604 --- /dev/null +++ b/apps/atrad/mqtt.py @@ -0,0 +1,38 @@ +import paho.mqtt.client as mqtt +from radarsys import settings +from radarsys.socketconfig import sio as sio +import numpy as np + +def on_connect(mqtt_client, userdata, flags, rc): + if rc == 0: + print('Connected successfully') + mqtt_client.subscribe('atrad/test3') + else: + print('Bad connection. Code:', rc) + +def maxima_temp(trs): + np_array = [np.array(i) for i in trs] + temps = [max(i[i<40]) for i in np_array] + return max(temps) + +def on_message(mqtt_client, userdata, msg): + print(f'Received message on topic: {msg.topic} with payload: {msg.payload}', flush=True) + trsi = [[],[],[],[]] + mensaje = str(msg.payload) + datos = [i for i in mensaje[21:-1].split("*")] + status=''.join([datos[i][3] for i in [0,1,2,3]]) + for trs,i in zip(datos,[0,1,2,3]) : + trsi[i]= [int(i) for i in trs[1:-1].split(",")] + potencias = [trsi[0][34],trsi[0][36],trsi[2][32],trsi[2][34]] + tmax = maxima_temp(trsi) + sio.emit('test', data={'time':mensaje[2:21],'num':trsi[0][0],'pow':potencias,'tmax':str(tmax),'status':status}) + +client = mqtt.Client() +client.on_connect = on_connect +client.on_message = on_message +client.username_pw_set(settings.MQTT_USER, settings.MQTT_PASSWORD) +client.connect( + host=settings.MQTT_SERVER, + port=settings.MQTT_PORT, + keepalive=settings.MQTT_KEEPALIVE +) \ No newline at end of file diff --git a/apps/atrad/templates/atrad_conf.html b/apps/atrad/templates/atrad_conf.html new file mode 100644 index 0000000..0a87d6e --- /dev/null +++ b/apps/atrad/templates/atrad_conf.html @@ -0,0 +1,288 @@ +{% extends "dev_conf.html" %} +{% block extra-head %} + +{% endblock %} + +{% block extra-content %} + + + +
+
+
+

Atrad Monitor

+
+
+ +
+ +
+
+
+
+

Potencia

+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+

Status

+
+
+ + + + + + + + + + + + + +
Tx1

Sin envio de datos

Tx2

Sin envio de datos

+
+
+
+
+ + +
+
+
+
+

Temperatura

+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+

Control

+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ + + + + + + +{% endblock %} \ No newline at end of file diff --git a/apps/atrad/templates/atrad_conf_edit.html b/apps/atrad/templates/atrad_conf_edit.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/apps/atrad/templates/atrad_conf_edit.html diff --git a/apps/atrad/templates/atrad_conf_import.html b/apps/atrad/templates/atrad_conf_import.html new file mode 100644 index 0000000..30435fa --- /dev/null +++ b/apps/atrad/templates/atrad_conf_import.html @@ -0,0 +1,7 @@ +{% extends "dev_conf_edit.html" %} +{% load django_bootstrap5 %} +{% load static %} +{% load main_tags %} + +{% block extra-js%} +{% endblock %} \ No newline at end of file diff --git a/apps/atrad/templates/index.html b/apps/atrad/templates/index.html new file mode 100644 index 0000000..e21ab6b --- /dev/null +++ b/apps/atrad/templates/index.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% block mainactive %}active{% endblock %} + +{% block content-title %}TITLE{% endblock %} +{% block content-suptitle %}Suptitle{% endblock %} + +{% block content %} +

+ {% lorem %} +

+{% endblock %} + +{% block sidebar%} + +{% endblock %} diff --git a/apps/atrad/templates/index_atrad.html b/apps/atrad/templates/index_atrad.html new file mode 100644 index 0000000..283a4c8 --- /dev/null +++ b/apps/atrad/templates/index_atrad.html @@ -0,0 +1,46 @@ +{% extends "base.html" %} +{% load django_bootstrap5 %} +{% block mainactive %}active{% endblock %} + +{% block content-title %}DEVICE CGS{% endblock %} +{% block content-suptitle %}CLOCK GENERATOR AND SYNCHRONIZER{% endblock %} + +{% block content %} +

+ Ingresar Frecuencias +

+ + + + + {% if form.is_multipart %} + + + +
+ {% else %} + + {% endif %} + + {% if step_field %} + + {% endif %} + + {% if submit_method != 'GET' and submit_method != 'get' %} + {% csrf_token %} + {% endif %} + + + + +
+ {% bootstrap_form form size='md' %} + +
+{% endblock %} + +{% block sidebar%} + +{% endblock %} \ No newline at end of file diff --git a/apps/atrad/tests.py b/apps/atrad/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/apps/atrad/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/atrad/urls.py b/apps/atrad/urls.py new file mode 100644 index 0000000..2774385 --- /dev/null +++ b/apps/atrad/urls.py @@ -0,0 +1,8 @@ +from django.urls import path + +from . import views + +urlpatterns = ( + path('/', views.atrad_conf, name='url_atrad_conf'), + path('/edit/', views.atrad_conf_edit, name='url_edit_atrad_conf'), +) \ No newline at end of file diff --git a/apps/atrad/views.py b/apps/atrad/views.py new file mode 100644 index 0000000..bf2ecf8 --- /dev/null +++ b/apps/atrad/views.py @@ -0,0 +1,108 @@ +from django.shortcuts import redirect, render, get_object_or_404 +from django.contrib import messages +from django.http import HttpResponse + +from apps.main.models import Experiment +from .models import ATRADConfiguration + +from .forms import ATRADConfigurationForm, UploadFileForm +from apps.main.views import sidebar + +import requests +import json + +import os +from django.http import JsonResponse +from .mqtt import client as mqtt_client +from radarsys.socketconfig import sio as sio + + +def atrad_conf(request, id_conf): + + conf = get_object_or_404(ATRADConfiguration, pk=id_conf) + + ip=conf.device.ip_address + port=conf.device.port_address + + kwargs = {} + + kwargs['status'] = conf.device.get_status_display() + + kwargs['dev_conf'] = conf + kwargs['dev_conf_keys'] = ['label', + 'topic'] + + kwargs['title'] = 'ATRAD Configuration' + kwargs['suptitle'] = 'Details' + + kwargs['button'] = 'Edit Configuration' + + #kwargs['no_play'] = True + + ###### SIDEBAR ###### + kwargs.update(sidebar(conf=conf)) + + return render(request, 'atrad_conf.html', kwargs) + +def atrad_conf_edit(request, id_conf): + + conf = get_object_or_404(ATRADConfiguration, pk=id_conf) + + if request.method=='GET': + form = ATRADConfigurationForm(instance=conf) + + if request.method=='POST': + form = ATRADConfigurationForm(request.POST, instance=conf) + + if form.is_valid(): + if conf.topic == None: conf.topic = 0 + + conf = form.save(commit=False) + + if conf.verify_frequencies(): + conf.save() + return redirect('url_atrad_conf', id_conf=conf.id) + + kwargs = {} + kwargs['id_dev'] = conf.id + kwargs['form'] = form + kwargs['title'] = 'Device Configuration' + kwargs['suptitle'] = 'Edit' + kwargs['button'] = 'Save' + + return render(request, 'atrad_conf_edit.html', kwargs) + +import os +from django.http import HttpResponse# + +def publish_message(request): + rc, mid = mqtt_client.publish('test/data2',1) + return JsonResponse({'code1': 'HIKA', 'code2': 'LUCAS'}) + +def monitor(request): + kwargs = {'no_sidebar': True} + return render(request, 'monitor.html', kwargs) + +def prueba(request): + kwargs = {'no_sidebar': True} + return render(request, 'prueba.html', kwargs) + +@sio.on('connection-bind') +def connection_bind(sid, data): + print("sid:",sid,"data",data) + +@sio.on('disconnect') +def test_disconnect(sid): + print("Disconnected") + +@sio.event +def control_event(sid,message): + mqtt_client.publish('test/data2',message['data']) + +def hello(data): + try: + rc, mid = mqtt_client.publish('test/data2', 'Hello') + sio.emit('test', data={'topic':mid, 'status': 'Not Running'}) + except: + print('ERROR', flush=True) + return HttpResponse("Hello") \ No newline at end of file diff --git a/apps/main/fixtures/main_initial_data.json b/apps/main/fixtures/main_initial_data.json index 9bbd66c..27b1cc3 100644 --- a/apps/main/fixtures/main_initial_data.json +++ b/apps/main/fixtures/main_initial_data.json @@ -94,6 +94,14 @@ }, "model": "main.devicetype", "pk": 6 + }, + { + "fields": { + "name": "atrad", + "description": "" + }, + "model": "main.devicetype", + "pk": 7 }, { "fields": { diff --git a/apps/main/models.py b/apps/main/models.py index aeada9e..95c3b03 100644 --- a/apps/main/models.py +++ b/apps/main/models.py @@ -73,6 +73,7 @@ DEV_TYPES = ( ('cgs', 'Clock Generator System'), ('abs', 'Automatic Beam Switching'), ('dds_rest', 'Direct Digital Synthesizer_REST'), + ('atrad', 'Transmitter ATRAD'), ) EXP_STATES = ( diff --git a/apps/main/views.py b/apps/main/views.py index 1d09192..cf8cbb0 100644 --- a/apps/main/views.py +++ b/apps/main/views.py @@ -30,6 +30,7 @@ from apps.cgs.forms import CGSConfigurationForm from apps.abs.forms import ABSConfigurationForm from apps.usrp.forms import USRPConfigurationForm from apps.dds_rest.forms import DDSRestConfigurationForm +from apps.atrad.forms import ATRADConfigurationForm from .utils import Params from .models import Campaign, Experiment, Device, Configuration, Location, RunningExperiment, DEV_STATES @@ -40,6 +41,7 @@ from apps.abs.models import ABSConfiguration from apps.rc.models import RCConfiguration, RCLine, RCLineType, RCClock from apps.dds.models import DDSConfiguration from apps.dds_rest.models import DDSRestConfiguration +from apps.atrad.models import ATRADConfiguration #from .tasks import task_start from radarsys.celery import app @@ -53,6 +55,7 @@ CONF_FORMS = { 'cgs': CGSConfigurationForm, 'abs': ABSConfigurationForm, 'usrp': USRPConfigurationForm, + 'atrad': ATRADConfigurationForm, } CONF_MODELS = { @@ -63,6 +66,7 @@ CONF_MODELS = { 'cgs': CGSConfiguration, 'abs': ABSConfiguration, 'usrp': USRPConfiguration, + 'atrad': ATRADConfiguration, } MIX_MODES = { diff --git a/docker-compose.yml b/docker-compose.yml index ecf31b1..51f2111 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,12 +52,12 @@ services: env_file: .env #Web Server - nginx: + radarsys-nginx: container_name: 'radarsys-nginx' restart: always build: ./nginx/ ports: - - '8030:8030' + - '0.0.0.0:80:80' volumes_from: - radarsys links: diff --git a/entrypoint.sh b/entrypoint.sh index f417dde..76aa610 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -8,5 +8,5 @@ python3 manage.py collectstatic --no-input #DJANGO_SUPERUSER_PASSWORD=$SUPER_USER_PASSWORD python manage.py createsuperuser --username $SUPER_USER_NAME --email $SUPER_USER_EMAIL --noinput -gunicorn radarsys.wsgi:application -w 2 -b :8000 +gunicorn -k eventlet radarsys.wsgi:application --bind 0.0.0.0:8000 \ No newline at end of file diff --git a/nginx/sites-enabled/radarsys.conf b/nginx/sites-enabled/radarsys.conf index 9cc6332..26bce09 100644 --- a/nginx/sites-enabled/radarsys.conf +++ b/nginx/sites-enabled/radarsys.conf @@ -1,20 +1,28 @@ +upstream django { + server radarsys:8000; +} + server { - listen 8030; + listen 80; server_name localhost; - access_log /dev/stdout; - error_log /dev/stdout info; + #access_log /dev/stdout; + #error_log /dev/stdout info; location /static { alias /radarsys/static; } location / { - proxy_set_header Host "localhost"; - proxy_pass http://radarsys:8000; - # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - + proxy_pass http://django; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; } -} +} \ No newline at end of file diff --git a/radarsys/asgi.py b/radarsys/asgi.py deleted file mode 100644 index c4f0b49..0000000 --- a/radarsys/asgi.py +++ /dev/null @@ -1,13 +0,0 @@ -import os - -from channels.routing import ProtocolTypeRouter -from django.core.asgi import get_asgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") - -application = ProtocolTypeRouter( - { - "http": get_asgi_application(), - # Just HTTP for now. (We can add other protocols later.) - } -) \ No newline at end of file diff --git a/radarsys/settings.py b/radarsys/settings.py index 1cede3b..b057210 100644 --- a/radarsys/settings.py +++ b/radarsys/settings.py @@ -52,9 +52,10 @@ INSTALLED_APPS = [ 'apps.abs', 'apps.cgs', 'apps.dds_rest', + 'apps.atrad', "django_bootstrap5", 'polymorphic', - 'channels', + 'radarsys', ] MIDDLEWARE = [ @@ -168,3 +169,9 @@ django.utils.encoding.force_text = force_str # choose of auto-created primary keys DEFAULT_AUTO_FIELD='django.db.models.AutoField' + +MQTT_SERVER = '10.10.10.99' +MQTT_PORT = 1883 +MQTT_KEEPALIVE = 60 +MQTT_USER = '' +MQTT_PASSWORD = '' \ No newline at end of file diff --git a/radarsys/socketconfig.py b/radarsys/socketconfig.py new file mode 100644 index 0000000..83af2cd --- /dev/null +++ b/radarsys/socketconfig.py @@ -0,0 +1,6 @@ +import os +import socketio +async_mode = None + +basedir = os.path.dirname(os.path.realpath(__file__)) +sio = socketio.Server(async_mode='eventlet') \ No newline at end of file diff --git a/radarsys/urls.py b/radarsys/urls.py index c200933..29801d3 100644 --- a/radarsys/urls.py +++ b/radarsys/urls.py @@ -14,7 +14,7 @@ urlpatterns = [ path('abs/', include('apps.abs.urls')), path('misc/',include('apps.misc.urls')), path('dds_rest/', include('apps.dds_rest.urls')), - + path('atrad/', include('apps.atrad.urls')), ] #urlpatterns += staticfiles_urlpatterns() diff --git a/radarsys/wsgi.py b/radarsys/wsgi.py index 7e2fbfb..7ddaa50 100644 --- a/radarsys/wsgi.py +++ b/radarsys/wsgi.py @@ -10,7 +10,10 @@ https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ import os from django.core.wsgi import get_wsgi_application +from .socketconfig import sio +import socketio os.environ.setdefault("DJANGO_SETTINGS_MODULE", "radarsys.settings") application = get_wsgi_application() +application = socketio.WSGIApp(sio, application) diff --git a/requirements.txt b/requirements.txt index 8165e49..6d9d896 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,5 +10,9 @@ celery==5.2.7 gunicorn==20.1.0 requests==2.28.2 redis==4.4.2 -channels==4.0.0 -daphne==4.0.0 \ No newline at end of file + +paho-mqtt==1.6.1 + +eventlet==0.30.2 +python-engineio +python-socketio \ No newline at end of file