Compare commits

...

3 commits

8 changed files with 156 additions and 71 deletions

View file

@ -1,63 +0,0 @@
#!/usr/bin/env python3
import argparse
import asyncio
from pathlib import Path
import websockets
from mmelodies_pb2 import UpdateRequest, Update, Parameter
async def handler(ws):
print(f"connection on {ws.path}")
while True:
try:
message = await ws.recv()
request = UpdateRequest()
request.ParseFromString(message)
print(request)
response = Update()
if request.scope_samples:
response.scope_samples = b"\n" * 512
for param in request.param_changes:
print(f"param {param.id} {param.value}")
p = response.params.add()
p.id = param.id
p.value = param.value
print(response)
await ws.send(response.SerializeToString())
except websockets.ConnectionClosedOK:
break
except websockets.ConnectionClosedError as e:
print(repr(e))
break
async def _main(port=8001, unix=None):
if unix is not None:
print(f"listening on {unix}")
ws = websockets.unix_serve(handler, path=unix)
else:
print(f"listening on localhost:{port}")
ws = websockets.serve(handler, "localhost", port=port)
async with ws:
await asyncio.Future() # run forever
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-U", "--unix", type=Path)
parser.add_argument("-p", "--port", type=int, default=8001)
args = parser.parse_args()
asyncio.run(_main(**vars(args)))
if __name__ == "__main__":
main()

View file

@ -0,0 +1,125 @@
#!/usr/bin/env python3
import argparse
import asyncio
from pathlib import Path
from collections import namedtuple
from functools import partial
import websockets
from .mmelodies_pb2 import UpdateRequest, Update, Parameter
CONNECTIONS = []
async def request_handler(ws, writer):
print(f"connection on {ws.path}")
CONNECTIONS.append(ws)
while True:
try:
message = await ws.recv()
request = UpdateRequest()
request.ParseFromString(message)
print(request)
if request.scope_samples:
writer.write("scope;".encode())
for param in request.param_changes:
writer.write(f"set {param.id} {param.value};".encode())
writer.write("params;\n".encode())
await writer.drain()
except TypeError as e:
print(e)
except websockets.ConnectionClosedOK:
CONNECTIONS.remove(ws)
break
except websockets.ConnectionClosedError as e:
print(e)
CONNECTIONS.remove(ws)
break
async def update_handler(reader):
print("pd connected!")
while True:
try:
message = (await reader.readline()).decode().strip(";\n")
update = Update()
parts = message.split(" ")
if parts[0] == "params":
for param_id, value in enumerate(parts[1:]):
param = update.params.add()
param.id = param_id
param.value = int(value)
if parts[0] == "scope":
update.scope_samples.extend([int(part) for part in parts[1:]])
print(repr(update))
websockets.broadcast(CONNECTIONS, update.SerializeToString())
except Exception as e:
print(e)
async def _main(listen_port, pd_port):
if pd_port[0] == "unix":
path = pd_port[1]
print(f"connecting to Pd on {path.absolute()}")
pd_reader, pd_writer = await asyncio.open_unix_connection(
path=path, happy_eyeballs_delay=0.25
)
if pd_port[0] == "tcp":
host, port = pd_port[1]
print(f"connecting to Pd on {host}:{port}")
pd_reader, pd_writer = await asyncio.open_connection(
host=host, port=port, happy_eyeballs_delay=0.25
)
ws_handler = partial(request_handler, writer=pd_writer)
if listen_port[0] == "unix":
path = listen_port[1]
print(f"listening on {path.absolute()}")
ws = websockets.unix_serve(ws_handler, path=path)
if listen_port[0] == "tcp":
host, port = listen_port[1]
print(f"listening on {host}:{port}")
ws = websockets.serve(ws_handler, host=host, port=port)
async with ws:
await update_handler(pd_reader)
def port(arg: str):
kind, address = arg.split(":", maxsplit=1)
if kind == "tcp":
host, port = address.rsplit(":", maxsplit=1)
port = int(port)
if host == "":
host = "localhost"
return kind, (host, port)
if kind == "unix":
path = Path(address)
assert path.parent.exists()
return kind, path
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-l", "--listen-port", type=port, default="tcp:localhost:8001")
parser.add_argument("-p", "--pd-port", type=port, default="tcp:localhost:10234")
args = parser.parse_args()
asyncio.run(_main(**vars(args)))
if __name__ == "__main__":
main()

View file

@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fmmelodies.proto\"&\n\tParameter\x12\n\n\x02id\x18\x01 \x01(\r\x12\r\n\x05value\x18\x02 \x01(\r\"`\n\rUpdateRequest\x12\x1a\n\rscope_samples\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12!\n\rparam_changes\x18\x02 \x03(\x0b\x32\n.ParameterB\x10\n\x0e_scope_samples\"R\n\x06Update\x12\x1a\n\rscope_samples\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1a\n\x06params\x18\x02 \x03(\x0b\x32\n.ParameterB\x10\n\x0e_scope_samplesb\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fmmelodies.proto\"&\n\tParameter\x12\n\n\x02id\x18\x01 \x01(\r\x12\r\n\x05value\x18\x02 \x01(\r\"`\n\rUpdateRequest\x12\x1a\n\rscope_samples\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12!\n\rparam_changes\x18\x02 \x03(\x0b\x32\n.ParameterB\x10\n\x0e_scope_samples\";\n\x06Update\x12\x15\n\rscope_samples\x18\x01 \x03(\x11\x12\x1a\n\x06params\x18\x02 \x03(\x0b\x32\n.Parameterb\x06proto3')
@ -50,5 +50,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
_UPDATEREQUEST._serialized_start=59
_UPDATEREQUEST._serialized_end=155
_UPDATE._serialized_start=157
_UPDATE._serialized_end=239
_UPDATE._serialized_end=216
# @@protoc_insertion_point(module_scope)

View file

@ -1,5 +1,5 @@
[tool.poetry]
name = "backend"
name = "mmelodies"
version = "0.1.0"
description = "Connects everything."
authors = []
@ -12,7 +12,7 @@ protobuf = "^4.24.3"
[tool.poetry.dev-dependencies]
[tool.poetry.scripts]
backend = "backend:main"
mmelodies = "mmelodies.backend:main"
[build-system]
requires = ["poetry-core>=1.0.0"]

View file

@ -7,7 +7,30 @@
sslCertificate = "/etc/ssl/snakeoil.crt";
sslCertificateKey = "/etc/ssl/snakeoil.key";
root = pkgs.mmelodies.frontend;
locations."/api" = {
proxyPass = "http://unix:/run/mmelodies/backend.sock";
proxyWebsockets = true;
};
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
systemd.services.mmelodies = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
before = [ "nignx.target" ];
serviceConfig = {
User = "nginx";
Group = "nginx";
Type = "simple";
ExecStart = "${pkgs.mmelodies.backend}/bin/mmelodies -l unix:/run/mmelodies/backend.sock";
StandardOutput = "journal";
StandardError = "journal";
};
};
systemd.tmpfiles.rules = [
"d /run/mmelodies 0755 nginx nginx 12h -"
];
}

View file

@ -11,6 +11,6 @@ message UpdateRequest {
}
message Update {
optional bytes scope_samples = 1;
repeated sint32 scope_samples = 1;
repeated Parameter params = 2;
}

View file

@ -2,7 +2,7 @@ import * as THREE from 'three';
// @ts-ignore
import { UpdateRequest, Update } from "/proto/mmelodies.proto";
const url = new URL("/api/controller", location.href);
const url = new URL("/api", location.href);
const send = document.querySelector("#send");
url.protocol = "ws";

View file

@ -1,6 +1,6 @@
import { UpdateRequest, Update } from "/proto/mmelodies.proto";
const url = new URL("/api/display", location.href);
const url = new URL("/api", location.href);
const send = document.querySelector("#send");
url.protocol = "ws";
@ -14,7 +14,7 @@ socket.binaryType = 'arraybuffer';
socket.addEventListener("message", (event) => {
send.value = "Pong";
const message = Update.decode(new Uint8Array(event.data));
console.log(message.scope_samples);
console.log(message.scopeSamples);
});
send.addEventListener("click", function () {