rcrf-wifi/ground-station.py

172 lines
5.9 KiB
Python

#!/usr/bin/env python
import asyncio
from websockets.server import serve
ADMIN_ID = "1234"
BOAT_STATE_INCTRL = 2
BOAT_STATE_AVAILABLE = 1
BOAT_STATE_LOCKED = 0
BOAT_STATE_TERMINATED = -1
Boats = []
Clients = []
async def echo_boats(client):
"""echo list of all not locked boats to client"""
data = "boats:"
for boat in Boats:
if boat['state'] != BOAT_STATE_LOCKED and boat['state'] != BOAT_STATE_TERMINATED:
data += str(boat['id']) + ";" + str(boat['name']) + ";"
if boat["state"] == BOAT_STATE_AVAILABLE:
data += 'available:'
elif boat["state"] == BOAT_STATE_INCTRL:
data += 'inctrl:'
await client['ws'].send(data)
async def echo_locked_boats(client):
"""echo list of all locked boats to client"""
if client['id'] == ADMIN_ID:
data = "lockedBoats:"
for boat in Boats:
if boat['state'] == BOAT_STATE_LOCKED:
data += str(boat['id']) + ";" + str(boat['name']) + ";locked:"
await client['ws'].send(data + '\n')
async def echo_clients(client):
"""echo list of all clients to client"""
if client['id'] == ADMIN_ID:
data = "clients:"
for clie in Clients:
data += str(clie['id']) + ";" + str(clie['boat']) + ";" + str(clie['state']) + ":"
await client['ws'].send(data + '\n')
async def take_controll(client, boat):
"""let a client take controll a boat"""
if client['boat'] is not None:
client['boat']["state"] = BOAT_STATE_AVAILABLE
client["boat"] = None
for b in Boats:
if b['id'] == boat and b['state'] != BOAT_STATE_TERMINATED:
if b['state'] == BOAT_STATE_AVAILABLE:
b["state"] = BOAT_STATE_INCTRL
print("take controll: " + str(client["id"]) + " -> " + b["name"])
client["boat"] = b
await client['ws'].send("OK")
return
else:
break
await client['ws'].send("FAIL")
async def free_boat(boat):
"""make boat available for next client"""
for client in Clients:
if client["boat"]['id'] == boat:
client["boat"] = None
break
for b in Boats:
if b['id'] == boat and b['state'] != BOAT_STATE_TERMINATED:
if boat['state'] == BOAT_STATE_INCTRL:
boat["state"] = BOAT_STATE_AVAILABLE
break
async def lock_boat(boat):
"""lock a boat so client can't take controll over it"""
for client in Clients:
if client["boat"]['id'] == boat:
client["boat"] = None
break
for b in Boats:
if b['id'] == boat and b['state'] != BOAT_STATE_TERMINATED:
boat["state"] = BOAT_STATE_LOCKED
break
async def on_message(message, client):
"""handel for client messages"""
data = message.replace('\n', '').split(';')
if data[0] != client["id"]:
print("invalid id: " + str(data[0]) + " != " + str(client["id"]))
else:
if data[1] == "boats":
await echo_boats(client)
elif data[1] == "ctrl":
await take_controll(client, data[2])
elif data[1] == "end":
await take_controll(client, -1)
elif data[1] == "free":
if client["id"] == ADMIN_ID:
await free_boat(data[2])
elif data[1] == "lock":
if client["id"] == ADMIN_ID:
await lock_boat(data[2])
elif data[1] == "locked":
await echo_locked_boats(client)
elif data[1] == "kick":
print("kick")
elif data[1] == "clients":
await echo_clients(client)
elif data[1] == "d":
if client['boat'] is None:
print("WARN: controll cmd (" + client['id'] + ") to None: " + data[2])
else:
print("INFO: controll cmd (" + client['id'] + ") to " + client['boat']['name'] + ": " + data[2])
await client['boat']['ws'].send("d:" + data[2] + "\n")
else:
print("WARN: invalid command (" + client['id'] + "): '" + data[1] + "'")
async def new_client(clientId, ws):
"""handler for every new client connection"""
client = { "id": clientId, "boat": None, "ws": ws, "state": "active" }
print("new client connected: " + client['id'])
for clie in Clients:
if clie['id'] == client['id']:
clie['state'] = 'terminated'
Clients.append(client)
if client['id'] == ADMIN_ID:
await echo_boats(client)
await echo_locked_boats(client)
await echo_clients(client)
return client
async def new_boat(boatId, name, ws):
"""handler for every new boat connection"""
boat = { "id": boatId, "name": name, "ws": ws, "state": BOAT_STATE_AVAILABLE }
print("new boat connected: " + boatId)
for bo in Boats:
if bo['id'] == boat['id']:
bo['state'] = BOAT_STATE_TERMINATED
await bo['ws'].close()
Boats.append(boat)
return boat
async def run(ws, path):
"""hadeler for every new websocket connection"""
client = None
async for message in ws:
message = message.split(';')
if len(message) == 3 and message[1] == "4675":
client = await new_client(message[0], ws)
if client is not None:
async for message in ws:
if client['state'] == 'active':
await on_message(message, client)
else:
break
break
elif (len(message) == 3 and message[1] == "3440"):
client = await new_boat(message[0], message[2], ws)
if client is not None:
async for message in ws:
print("boat" + client['id'] + " says " + message)
break
async def main():
"""main"""
async with serve(run, "0.0.0.0", 8080):
await asyncio.Future()
asyncio.run(main())