|
import coding |
|
import scratchattach as scratch3 |
|
import threading |
|
import time |
|
import random |
|
import os |
|
global conn |
|
global projectid |
|
global endpoint |
|
global users |
|
|
|
projectid = os.getenv("ProjectID") |
|
users = {} |
|
session = scratch3.login(os.getenv("Username"), os.getenv("Password")) |
|
class User: |
|
def __init__(self, name, id): |
|
self.last_request = time.time() |
|
self.name = name |
|
self.id = id |
|
class UserStartup: |
|
def __init__(self): |
|
self.last_request = time.time() |
|
class Responder: |
|
def __init__(self, projectid, channel, can_respond, can_stream, value, user): |
|
user.last_request = time.time() |
|
self.user = user |
|
self.projectid = projectid |
|
self.can_respond = can_respond |
|
self.can_stream = can_stream |
|
self.channel = channel |
|
self.cooldown = time.time() |
|
self.wrote = False |
|
self.last_value = value |
|
def poll(self): |
|
value = scratch3.get_var(self.projectid, "channel "+str(self.channel)) |
|
if value != self.last_value and value != None: |
|
self.user.last_request = time.time() |
|
self.last_value = value |
|
binary = coding.decimal_to_binary(int(value)) |
|
if binary[1:3]=="01": |
|
value = str(coding.binary_to_decimal(binary[3:])) |
|
if binary[1:3]=="10": |
|
value = str("true" if binary[3:] == 1 else "false") |
|
if binary[1:3]=="11": |
|
value = coding.convert_to_text(binary[3:]) |
|
|
|
return value |
|
def close(self): |
|
if self.can_stream or not self.can_respond: |
|
conn.set_var("channel "+str(self.channel), "0") |
|
else: |
|
while str(scratch3.get_var(self.projectid, "channel "+str(self.channel))) !="0": |
|
pass |
|
def respond(self, response): |
|
global conn |
|
if self.wrote and not self.can_stream: |
|
raise Exception("Can't stream to this as a response") |
|
if not (self.can_respond or self.can_stream): |
|
raise Exception("Can't respond to this") |
|
while time.time() - self.cooldown < 0.5: |
|
time.sleep(0.5 - (time.time() - self.cooldown)) |
|
if self.can_respond or self.can_stream: |
|
payload = "1" |
|
if type(response) is int: |
|
payload+="01"+coding.decimal_to_binary(response) |
|
elif type(response) is bool: |
|
payload+="10"+"1" if response else "0" |
|
elif type(response) is str: |
|
payload+="11"+coding.convert_to_binary(response) |
|
self.last_value = str(coding.binary_to_decimal(payload)) |
|
conn.set_var("channel "+str(self.channel), str(coding.binary_to_decimal(payload))) |
|
t= time.time() |
|
times=0.2 |
|
while scratch3.get_var(self.projectid, "channel "+str(self.channel)) !=str(coding.binary_to_decimal(payload)): |
|
if time.time()-t>=times: |
|
print("Message not sent, retrying") |
|
times+=0.1 |
|
conn.set_var("channel "+str(self.channel), str(coding.binary_to_decimal(payload))) |
|
t=time.time() |
|
self.wrote = True |
|
class ConnectionEndpoint: |
|
def receivedMessage(self, message, user, responder): |
|
global users |
|
r=random.randrange(1, 2047) |
|
while r in users: |
|
r=random.randrange(1, 2047) |
|
users[r] = User(message, r) |
|
responder.respond(r) |
|
responder.close() |
|
class HeartbeatEndpoint: |
|
def receivedMessage(self, message, user, responder): |
|
responder.close() |
|
|
|
def thread(n): |
|
global users |
|
global conn |
|
global projectid |
|
global endpoint |
|
heartbeater = HeartbeatEndpoint() |
|
conn.set_var("channel "+str(n), "0") |
|
while True: |
|
|
|
value = scratch3.get_var(projectid, "channel "+str(n)) |
|
if str(value) != "0" and value != None: |
|
binary = coding.decimal_to_binary(int(value)) |
|
reqendpoint = coding.binary_to_decimal(binary[1:6]) |
|
header = coding.binary_to_decimal(binary[6:17]) |
|
staticpayload = binary[17] == '1' |
|
streamingpayload = binary[18] == '1' |
|
acceptstaticpayload = binary[19] == '1' |
|
acceptstreamingpayload = binary[20] == '1' |
|
payload = None |
|
if staticpayload and not streamingpayload: |
|
payloadformat = binary[21:23] |
|
if payloadformat == "01": |
|
payload = str(coding.binary_to_decimal(binary[23:])) |
|
if payloadformat == "10": |
|
payload = "true" if binary[23:]=='1' else "false" |
|
if payloadformat == "11": |
|
payload = coding.convert_to_text(binary[23:]) |
|
if header in users: |
|
user = users[header] |
|
else: |
|
user = UserStartup() |
|
respond = Responder(projectid, n, acceptstaticpayload, acceptstreamingpayload, value, user) |
|
if reqendpoint == 31: |
|
heartbeater.receivedMessage(payload, user, respond) |
|
else: |
|
endpoint[reqendpoint].receivedMessage(payload, user, respond) |
|
|
|
def monitor_users(): |
|
global users |
|
while True: |
|
time.sleep(1) |
|
for k, v in users.items(): |
|
if time.time() - v.last_request >= 300: |
|
del users[k] |
|
def start_server(endpoints): |
|
global projectid |
|
global conn |
|
global endpoint |
|
endpoints.insert(0, ConnectionEndpoint()) |
|
endpoint = endpoints |
|
conn = session.connect_cloud(projectid) |
|
threads = [threading.Thread(target=thread, args=(i+1,)) for i in range(10)] |
|
for t in threads: |
|
t.start() |
|
monitorusers = threading.Thread(target=monitor_users) |
|
monitorusers.start() |
|
|