From 850e8e5f6f16a95acc870b51329cb7c6bcd2cdc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Mon, 3 Jan 2022 22:19:01 +0100 Subject: [PATCH] Refactor communication to daemon --- dcc/driver/connector.py | 34 ++++++++++-------- ...er_driverconfiguration_options_and_more.py | 22 ++++++++++++ dcc/driver/models.py | 5 +-- dcc/driver/serializers.py | 5 +-- dcc/driver/views.py | 36 +++++++++++-------- 5 files changed, 68 insertions(+), 34 deletions(-) create mode 100644 dcc/driver/migrations/0002_alter_driverconfiguration_options_and_more.py diff --git a/dcc/driver/connector.py b/dcc/driver/connector.py index 2073a79..d195b41 100644 --- a/dcc/driver/connector.py +++ b/dcc/driver/connector.py @@ -4,41 +4,45 @@ from driver.models import DriverConfiguration class Connector: - def __init__(self): self.config = DriverConfiguration.get_solo() def __send_data(self, message): - # to be encoded + resp = b'' + # convert to binary if str is received + if isinstance(message, str): + message = message.encode() + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect((self.config.remote_host, self.config.remote_port)) + sock.settimeout(self.config.timeout / 1000) # milliseconds sock.sendall(message) - resp = sock.recv(1024) - - print(resp) - return True + while True: + try: + resp += sock.recv(1024) + except socket.timeout: + break + return resp def passthrough(self, data): - self.__send_data(data) + return self.__send_data(data) def ops(self, address, data, function=False): if function: - message = "".format(address, data['function'], - data['state']) + message = "".format( + address, data['function'], data['state']) else: - message = "".format(address, data['speed'], - data['direction']) + message = "".format( + address, data['speed'], data['direction']) self.__send_data(message) - return True def infra(self, data): - power = data['power'] if "track" in data: - track = " {}".forma(data['track'].upper()) + track = " {}".format(data["track"].upper()) else: track = "" - if power: + if data["power"]: self.__send_data('<1{}>'.format(track)) else: self.__send_data('<0{}>'.format(track)) diff --git a/dcc/driver/migrations/0002_alter_driverconfiguration_options_and_more.py b/dcc/driver/migrations/0002_alter_driverconfiguration_options_and_more.py new file mode 100644 index 0000000..f6d0609 --- /dev/null +++ b/dcc/driver/migrations/0002_alter_driverconfiguration_options_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 4.0 on 2022-01-03 19:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('driver', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='driverconfiguration', + options={'verbose_name': 'Configuration'}, + ), + migrations.AddField( + model_name='driverconfiguration', + name='timeout', + field=models.SmallIntegerField(default=250), + ), + ] diff --git a/dcc/driver/models.py b/dcc/driver/models.py index d8fb16e..972ca19 100644 --- a/dcc/driver/models.py +++ b/dcc/driver/models.py @@ -6,9 +6,10 @@ class DriverConfiguration(SingletonModel): remote_host = models.GenericIPAddressField( protocol="IPv4", default="192.168.4.1") remote_port = models.SmallIntegerField(default=2560) + timeout = models.SmallIntegerField(default=250) def __str__(self): - return "Driver Configuration" + return "Configuration" class Meta: - verbose_name = "Driver Configuration" + verbose_name = "Configuration" diff --git a/dcc/driver/serializers.py b/dcc/driver/serializers.py index ed335c2..19b5ac1 100644 --- a/dcc/driver/serializers.py +++ b/dcc/driver/serializers.py @@ -13,5 +13,6 @@ class CabSerializer(serializers.Serializer): class InfraSerializer(serializers.Serializer): power = serializers.BooleanField(required=True) - track = serializers.ChoiceField(choices=('main', 'prog', 'join'), - required=False) + track = serializers.ChoiceField( + choices=('main', 'prog', 'join', 'MAIN', 'PROG', 'JOIN'), + required=False) diff --git a/dcc/driver/views.py b/dcc/driver/views.py index 0d45bf6..c5309ec 100644 --- a/dcc/driver/views.py +++ b/dcc/driver/views.py @@ -1,7 +1,6 @@ -from django.views import View -from django.http import HttpResponse, Http404 +from django.http import Http404 from django.utils.decorators import method_decorator -from rest_framework import status +from rest_framework import status, serializers from rest_framework.views import APIView from rest_framework.response import Response @@ -11,8 +10,6 @@ from driver.serializers import ( FunctionSerializer, CabSerializer, InfraSerializer) from roster.models import Cab as CabModel -conn = Connector() - def addresschecker(f): def addresslookup(request, address, *args): @@ -29,8 +26,15 @@ class SendCommand(APIView): def put(self, request): data = request.data - conn.passthrough(data) - return Response(data, + if not data: + raise serializers.ValidationError({ + "error": "a string is expected"}) + cmd = data.decode().strip() + if not (cmd.startswith("<") and cmd.endswith(">")): + raise serializers.ValidationError({ + "error": "please provide a valid command"}) + response = Connector().passthrough(cmd) + return Response({"response": response.decode()}, status=status.HTTP_202_ACCEPTED) @@ -39,7 +43,7 @@ class Function(APIView): def put(self, request, address): serializer = FunctionSerializer(data=request.data) if serializer.is_valid(): - conn.ops(address, serializer.data, function=True) + Connector().ops(address, serializer.data, function=True) return Response(serializer.data, status=status.HTTP_202_ACCEPTED) @@ -52,7 +56,7 @@ class Cab(APIView): def put(self, request, address): serializer = CabSerializer(data=request.data) if serializer.is_valid(): - conn.ops(address, serializer.data) + Connector().ops(address, serializer.data) return Response(serializer.data, status=status.HTTP_202_ACCEPTED) @@ -64,7 +68,7 @@ class Infra(APIView): def put(self, request): serializer = InfraSerializer(data=request.data) if serializer.is_valid(): - conn.infra(serializer.data) + Connector().infra(serializer.data) return Response(serializer.data, status=status.HTTP_202_ACCEPTED) @@ -72,11 +76,13 @@ class Infra(APIView): status=status.HTTP_400_BAD_REQUEST) -class Emergency(View): +class Emergency(APIView): def put(self, request): - conn.emergency() - return HttpResponse() + Connector().emergency() + return Response({"response": "emergency stop"}, + status=status.HTTP_202_ACCEPTED) def get(self, request): - conn.emergency() - return HttpResponse() + Connector().emergency() + return Response({"response": "emergency stop"}, + status=status.HTTP_202_ACCEPTED)