diff --git a/arduino/CommandStation-EX b/arduino/CommandStation-EX index 92591c8..00e3c80 160000 --- a/arduino/CommandStation-EX +++ b/arduino/CommandStation-EX @@ -1 +1 @@ -Subproject commit 92591c8a2ef12707477ad702fc10e51a24503149 +Subproject commit 00e3c80b442ac0964c4539eaa205da14bdc3c44f diff --git a/arduino/arduino-cli b/arduino/arduino-cli index e63c798..4506bf9 160000 --- a/arduino/arduino-cli +++ b/arduino/arduino-cli @@ -1 +1 @@ -Subproject commit e63c798cda729592f449660cbb0d2a8dc9473ca0 +Subproject commit 4506bf9d3601d6787df3948a8008e32f7dd32430 diff --git a/daemons/__init__.py b/daemons/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/daemons/net-to-serial.py b/daemons/net-to-serial.py new file mode 100644 index 0000000..f02eb2a --- /dev/null +++ b/daemons/net-to-serial.py @@ -0,0 +1,44 @@ +import serial +import asyncio + + +class SerialDaemon: + def __init__(self): + self.ser = serial.Serial('/dev/pts/7') # WIP + self.ser.baudrate = 115200 + + def __del__(self): + try: + self.ser.close() + except AttributeError: + pass + + async def handle_echo(self, reader, writer): + data = await reader.read(100) + message = data.decode() + addr = writer.get_extra_info('peername') + + print(f"Received {message!r} from {addr!r}") + + self.ser.write(data) + response = self.read_until() + print(f"Send: {response!r}") + writer.write(response) + await writer.drain() + + print("Close the connection") + writer.close() + + +async def main(): + sd = SerialDaemon() + server = await asyncio.start_server( + sd.handle_echo, '127.0.0.1', 2560) # WIP + addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets) + print(f'Serving on {addrs}') + + async with server: + await server.serve_forever() + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/daemons/serial.py b/daemons/serial.py deleted file mode 100644 index 644e42f..0000000 --- a/daemons/serial.py +++ /dev/null @@ -1,42 +0,0 @@ -import serial -import logging -import socket -import asyncio -import time -import paho.mqtt.client as mqtt -from asyncio_mqtt import Client - -MQTT_HOST = "127.0.0.1" -MQTT_PORT = 1883 - - -async def mqtt_broker(ser): - async with Client(MQTT_HOST, port=MQTT_PORT) as client: - await client.subscribe("dcc/commands") - async with client.unfiltered_messages() as messages: - async for message in messages: - print(message.payload.decode()) - # ser.write(message.payload) - - -def main(): - client = mqtt.Client() - # ser = serial.Serial('/dev/pts/7') - # ser.baudrate = 9600 - ser = None # remove me - while True: - try: - client.connect(MQTT_HOST, MQTT_PORT) - break - except (socket.gaierror, ConnectionRefusedError): - logging.warning('Broker not available') - time.sleep(5) - - logging.info('Broker subscribed') - client.disconnect() - asyncio.run(mqtt_broker(ser)) - # ser.close() - - -if __name__ == "__main__": - main() diff --git a/dcc/dcc/settings.py b/dcc/dcc/settings.py index 7c53fdb..20a2ead 100644 --- a/dcc/dcc/settings.py +++ b/dcc/dcc/settings.py @@ -39,6 +39,7 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'django_countries', + 'solo', 'rest_framework', 'dcc', 'driver', diff --git a/dcc/driver/admin.py b/dcc/driver/admin.py index 8c38f3f..7d2acc9 100644 --- a/dcc/driver/admin.py +++ b/dcc/driver/admin.py @@ -1,3 +1,6 @@ from django.contrib import admin +from solo.admin import SingletonModelAdmin -# Register your models here. +from driver.models import DriverConfiguration + +admin.site.register(DriverConfiguration, SingletonModelAdmin) diff --git a/dcc/driver/connector.py b/dcc/driver/connector.py index 4dc83fc..3ec5518 100644 --- a/dcc/driver/connector.py +++ b/dcc/driver/connector.py @@ -1,22 +1,27 @@ -import paho.mqtt.client as mqtt +import socket + +from driver.models import DriverConfiguration class Connector: - MQTT_HOST = "127.0.0.1" - MQTT_PORT = 1883 - @classmethod - def __mqtt_pub(self, message): + def __init__(self): + config = DriverConfiguration.get_solo() + self.remote_host = config.remote_host + self.remote_port = config.remote_port + + def __send_data(self, message): # to be encoded - client = mqtt.Client() - client.connect(self.MQTT_HOST, self.MQTT_PORT) - client.publish("dcc/commands", payload=message) - client.disconnect() - print(message) + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect((self.remote_host, self.remote_port)) + sock.sendall(message) + resp = sock.recv(1024) + + print(resp) return True def passthrough(self, data): - self.__mqtt_pub(data) + self.__send_data(data) def ops(self, address, data, function=False): if function: @@ -25,7 +30,7 @@ class Connector: else: message = "".format(address, data['speed'], data['direction']) - self.__mqtt_pub(message) + self.__send_data(message) return True def infra(self, data): @@ -36,9 +41,9 @@ class Connector: track = "" if power: - self.__mqtt_pub('<1{}>'.format(track)) + self.__send_data('<1{}>'.format(track)) else: - self.__mqtt_pub('<0{}>'.format(track)) + self.__send_data('<0{}>'.format(track)) def emergency(self): - self.__mqtt_pub('') + self.__send_data('') diff --git a/dcc/driver/migrations/0001_initial.py b/dcc/driver/migrations/0001_initial.py new file mode 100644 index 0000000..361b620 --- /dev/null +++ b/dcc/driver/migrations/0001_initial.py @@ -0,0 +1,25 @@ +# Generated by Django 4.0 on 2021-12-31 10:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='DriverConfiguration', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('remote_host', models.GenericIPAddressField(default='192.168.4.1', protocol='IPv4')), + ('remote_port', models.SmallIntegerField(default=2560)), + ], + options={ + 'verbose_name': 'Driver Configuration', + }, + ), + ] diff --git a/dcc/driver/models.py b/dcc/driver/models.py index 71a8362..d8fb16e 100644 --- a/dcc/driver/models.py +++ b/dcc/driver/models.py @@ -1,3 +1,14 @@ from django.db import models +from solo.models import SingletonModel -# Create your models here. + +class DriverConfiguration(SingletonModel): + remote_host = models.GenericIPAddressField( + protocol="IPv4", default="192.168.4.1") + remote_port = models.SmallIntegerField(default=2560) + + def __str__(self): + return "Driver Configuration" + + class Meta: + verbose_name = "Driver Configuration" diff --git a/dcc/roster/serializers.py b/dcc/roster/serializers.py index 74b9ee3..628972b 100644 --- a/dcc/roster/serializers.py +++ b/dcc/roster/serializers.py @@ -8,11 +8,8 @@ class CabSerializer(serializers.ModelSerializer): manufacturer = ManufacturerSerializer() decoder = DecoderSerializer() company = CompanySerializer() - # manufacturer = serializers.StringRelatedField() - # decoder = serializers.StringRelatedField() - # company = serializers.StringRelatedField() class Meta: model = Cab fields = "__all__" - read_only_fields = ("identifier", "creation_time", "updated_time") + read_only_fields = ("creation_time", "updated_time") diff --git a/dcc/roster/views.py b/dcc/roster/views.py index 98db4b7..4d9286e 100644 --- a/dcc/roster/views.py +++ b/dcc/roster/views.py @@ -1,27 +1,27 @@ -from rest_framework.generics import ListCreateAPIView, RetrieveUpdateAPIView +from rest_framework.generics import ListAPIView, RetrieveAPIView from roster.models import Cab from roster.serializers import CabSerializer -class RosterList(ListCreateAPIView): +class RosterList(ListAPIView): queryset = Cab.objects.all() serializer_class = CabSerializer -class RosterGet(RetrieveUpdateAPIView): +class RosterGet(RetrieveAPIView): queryset = Cab.objects.all() serializer_class = CabSerializer lookup_field = 'uuid' -class RosterAddress(RetrieveUpdateAPIView): +class RosterAddress(RetrieveAPIView): queryset = Cab.objects.all() serializer_class = CabSerializer lookup_field = 'address' -class RosterIdentifier(RetrieveUpdateAPIView): +class RosterIdentifier(RetrieveAPIView): queryset = Cab.objects.all() serializer_class = CabSerializer lookup_field = 'identifier' diff --git a/requirements.txt b/requirements.txt index 643917b..caaf010 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,9 @@ pytz Django djangorestframework +django-solo django-health-check # psycopg2-binary -paho-mqtt -asyncio-mqtt +# paho-mqtt +# asyncio-mqtt pySerial