Module code.core.server2020
Author: Jonathan Rotter
Server code
To be run on server pi. It is required (probably) that it is started before motor2020++.py or surface2020.py
Once it is running it does not need to be restarted even if its clients restart or lose connection
Required 3rd-party libraries:
autobahn
twisted
Expand source code
#!/usr/bin/env python3.4
'''Author: Jonathan Rotter
Server code
To be run on server pi. It is required (probably) that
it is started before motor2020++.py or surface2020.py
Once it is running it does not need to be restarted
even if its clients restart or lose connection
Required 3rd-party libraries:
`autobahn`
`twisted`
'''
# Check if the version of python is 3.x
import sys
if sys.version[0] != '3':
# Stop running if the version is python 2.x
raise Exception('This is Python3 code')
# importing the necessary objects
# autobahn does websocket stuff, but relies on twisted
from autobahn.twisted.websocket import \
WebSocketServerProtocol, WebSocketServerFactory
# twisted does asynchronous code execution needed for websockets
from twisted.python import log
from twisted.internet import task, reactor
IP = '127.0.0.1'
'''ip is localhost, does not need to be changed'''
PORT = 8008
'''Make sure the port set here and in webSocketClient are the same'''
class ServerProtocol(WebSocketServerProtocol):
'''
Describes how the websocket server should act
with one connection
How to connect to it in JavaScript
ws = new WebSocket('ws://localhost:8008/motor')
ws = new WebSocket('ws://localhost:8008/surface')
'''
def onConnect(self, request):
'''
Called by the server factory when
a client connects.
The path is found in `request.path`
and is used for the clients to specify
whether they are motor2020 or surface2020
'''
print(request.path)
# debug information
print('Client connecting & registering: {0}'.format(request.peer))
clientTypeRequest = request.path
# process the type of request
if clientTypeRequest.startswith('/'):
# remove the slash if there is one
clientTypeRequest = clientTypeRequest[1:]
# tell the factory to remember the connection
self.factory.register(self, clientTypeRequest)
def onOpen(self):
'''
Called by the server factory.
Only prints debug info
'''
print('WebSocket connection open')
def onClose(self, wasClean, code, reason):
'''
Called by the server factory when
a client disconnects
'''
print('WebSocket connection closed & deregistering: {0}'.format(reason))
# tell the factory that this connection is dead
self.factory.unregister(self)
def onMessage(self, msg, isBinary):
'''
Called by the server factory when
the client send a message.
The message is then sent to all
other connected clients
'''
# tell the factory to broadcast the received message
# to all other connected clients
self.factory.broadcast(self, msg, isBinary)
class ServerFactory(WebSocketServerFactory):
'''
Keeps track of all connections and relays data to other clients
'''
def __init__(self, url):
'''
Initializes the class
Args:
url (str): has to be in the format of "ws://127.0.0.1:8008"
'''
WebSocketServerFactory.__init__(self, url)
# init fields
self.motorConnection = None
self.miniMotorConnection = None
self.surfaceConnection = None
def register(self, client, clientTypeRequest):
'''
Called in `ServerProtocol.onConnect`
Remembers a connecting client so that
later data can be received from or broadcast to the client
Args:
client (ServerProtocol): A instance representing the connection
clientTypeRequest (str): Whether the client is surface or motor pi
'''
if clientTypeRequest == 'surface':
self.surfaceConnection = client # surface sends joystick data
elif clientTypeRequest == 'motor':
self.motorConnection = client # motor receives joystick data
elif clientTypeRequest == 'miniROV':
self.miniMotorConnection = client # mini-rov recives joystick data
else:
print('Bad client type received: {}'.format(clientTypeRequest))
# end the connection because the client's type is not recognized
# the type the client is is specified by the path in the url
client._closeConnection()
# find some better way to end the connection; unclean closing
def unregister(self, client):
'''
Called in `ServerProtocol.onClose`
Removes a client from the list of clients
as it is disconnecting from the server
Args:
client (ServerProtocol): The connection to remove
'''
if self.surfaceConnection == client:
self.surfaceConnection = None
elif self.motorConnection == client:
self.motorConnection = None
elif self.miniMotorConnection == client:
self.miniMotorConnection = None
else:
print('Unknown client: {}'.format(client))
def broadcast(self, client, msg, isBinary):
'''
Broadcasts data to other connections.
This is how surface pi sends the
state of the XBox controller to motor pi
Arg:
client (ServerProtocol): The source of the message
msg (str or bytes): The message to relay
isBinary (bool): If the msg is already encoded
'''
# only surface pi is supposed to send data
if self.surfaceConnection == client:
# broadcast to motor2020
if self.motorConnection:
self.motorConnection.sendMessage(msg)
# broadcast to miniROV
if self.miniMotorConnection:
self.miniMotorConnection.sendMessage(msg)
# debug messages
elif self.motorConnection == client:
print('Motor Pi isn\'t supposed to send stuff')
elif self.miniMotorConnection == client:
print('Mini ROV isn\'t supposed to send stuff')
if __name__ == '__main__':
# display debug information to stdout for now
log.startLogging(sys.stdout) # TODO: replace with log file (maybe)
# Setup server factory
server = ServerFactory(u'ws://{}:{}'.format(IP, PORT))
server.protocol = ServerProtocol
# setup listening server
reactor.listenTCP(PORT, server)
try:
# start listening for and handling connections
reactor.run()
finally:
pass
# if logs are sent to a file instead of stdout
# the file should be closed here with f.close()
Global variables
var IP-
ip is localhost, does not need to be changed
var PORT-
Make sure the port set here and in webSocketClient are the same
Classes
class ServerFactory (url)-
Keeps track of all connections and relays data to other clients
Initializes the class
Args
url:str- has to be in the format of "ws://127.0.0.1:8008"
Expand source code
class ServerFactory(WebSocketServerFactory): ''' Keeps track of all connections and relays data to other clients ''' def __init__(self, url): ''' Initializes the class Args: url (str): has to be in the format of "ws://127.0.0.1:8008" ''' WebSocketServerFactory.__init__(self, url) # init fields self.motorConnection = None self.miniMotorConnection = None self.surfaceConnection = None def register(self, client, clientTypeRequest): ''' Called in `ServerProtocol.onConnect` Remembers a connecting client so that later data can be received from or broadcast to the client Args: client (ServerProtocol): A instance representing the connection clientTypeRequest (str): Whether the client is surface or motor pi ''' if clientTypeRequest == 'surface': self.surfaceConnection = client # surface sends joystick data elif clientTypeRequest == 'motor': self.motorConnection = client # motor receives joystick data elif clientTypeRequest == 'miniROV': self.miniMotorConnection = client # mini-rov recives joystick data else: print('Bad client type received: {}'.format(clientTypeRequest)) # end the connection because the client's type is not recognized # the type the client is is specified by the path in the url client._closeConnection() # find some better way to end the connection; unclean closing def unregister(self, client): ''' Called in `ServerProtocol.onClose` Removes a client from the list of clients as it is disconnecting from the server Args: client (ServerProtocol): The connection to remove ''' if self.surfaceConnection == client: self.surfaceConnection = None elif self.motorConnection == client: self.motorConnection = None elif self.miniMotorConnection == client: self.miniMotorConnection = None else: print('Unknown client: {}'.format(client)) def broadcast(self, client, msg, isBinary): ''' Broadcasts data to other connections. This is how surface pi sends the state of the XBox controller to motor pi Arg: client (ServerProtocol): The source of the message msg (str or bytes): The message to relay isBinary (bool): If the msg is already encoded ''' # only surface pi is supposed to send data if self.surfaceConnection == client: # broadcast to motor2020 if self.motorConnection: self.motorConnection.sendMessage(msg) # broadcast to miniROV if self.miniMotorConnection: self.miniMotorConnection.sendMessage(msg) # debug messages elif self.motorConnection == client: print('Motor Pi isn\'t supposed to send stuff') elif self.miniMotorConnection == client: print('Mini ROV isn\'t supposed to send stuff')Ancestors
- autobahn.twisted.websocket.WebSocketServerFactory
- autobahn.twisted.websocket.WebSocketAdapterFactory
- autobahn.websocket.protocol.WebSocketServerFactory
- autobahn.websocket.protocol.WebSocketFactory
- twisted.internet.protocol.ServerFactory
- twisted.internet.protocol.Factory
Methods
def broadcast(self, client, msg, isBinary)-
Broadcasts data to other connections. This is how surface pi sends the state of the XBox controller to motor pi
Arg
client:ServerProtocol- The source of the message
msg:strorbytes- The message to relay
isBinary:bool- If the msg is already encoded
Expand source code
def broadcast(self, client, msg, isBinary): ''' Broadcasts data to other connections. This is how surface pi sends the state of the XBox controller to motor pi Arg: client (ServerProtocol): The source of the message msg (str or bytes): The message to relay isBinary (bool): If the msg is already encoded ''' # only surface pi is supposed to send data if self.surfaceConnection == client: # broadcast to motor2020 if self.motorConnection: self.motorConnection.sendMessage(msg) # broadcast to miniROV if self.miniMotorConnection: self.miniMotorConnection.sendMessage(msg) # debug messages elif self.motorConnection == client: print('Motor Pi isn\'t supposed to send stuff') elif self.miniMotorConnection == client: print('Mini ROV isn\'t supposed to send stuff') def register(self, client, clientTypeRequest)-
Called in
ServerProtocol.onConnect()Remembers a connecting client so that later data can be received from or broadcast to the clientArgs
client:ServerProtocol- A instance representing the connection
clientTypeRequest:str- Whether the client is surface or motor pi
Expand source code
def register(self, client, clientTypeRequest): ''' Called in `ServerProtocol.onConnect` Remembers a connecting client so that later data can be received from or broadcast to the client Args: client (ServerProtocol): A instance representing the connection clientTypeRequest (str): Whether the client is surface or motor pi ''' if clientTypeRequest == 'surface': self.surfaceConnection = client # surface sends joystick data elif clientTypeRequest == 'motor': self.motorConnection = client # motor receives joystick data elif clientTypeRequest == 'miniROV': self.miniMotorConnection = client # mini-rov recives joystick data else: print('Bad client type received: {}'.format(clientTypeRequest)) # end the connection because the client's type is not recognized # the type the client is is specified by the path in the url client._closeConnection() def unregister(self, client)-
Called in
ServerProtocol.onClose()Removes a client from the list of clients as it is disconnecting from the serverArgs
client:ServerProtocol- The connection to remove
Expand source code
def unregister(self, client): ''' Called in `ServerProtocol.onClose` Removes a client from the list of clients as it is disconnecting from the server Args: client (ServerProtocol): The connection to remove ''' if self.surfaceConnection == client: self.surfaceConnection = None elif self.motorConnection == client: self.motorConnection = None elif self.miniMotorConnection == client: self.miniMotorConnection = None else: print('Unknown client: {}'.format(client))
class ServerProtocol-
Describes how the websocket server should act with one connection
How to connect to it in JavaScript ws = new WebSocket('ws://localhost:8008/motor') ws = new WebSocket('ws://localhost:8008/surface')
Expand source code
class ServerProtocol(WebSocketServerProtocol): ''' Describes how the websocket server should act with one connection How to connect to it in JavaScript ws = new WebSocket('ws://localhost:8008/motor') ws = new WebSocket('ws://localhost:8008/surface') ''' def onConnect(self, request): ''' Called by the server factory when a client connects. The path is found in `request.path` and is used for the clients to specify whether they are motor2020 or surface2020 ''' print(request.path) # debug information print('Client connecting & registering: {0}'.format(request.peer)) clientTypeRequest = request.path # process the type of request if clientTypeRequest.startswith('/'): # remove the slash if there is one clientTypeRequest = clientTypeRequest[1:] # tell the factory to remember the connection self.factory.register(self, clientTypeRequest) def onOpen(self): ''' Called by the server factory. Only prints debug info ''' print('WebSocket connection open') def onClose(self, wasClean, code, reason): ''' Called by the server factory when a client disconnects ''' print('WebSocket connection closed & deregistering: {0}'.format(reason)) # tell the factory that this connection is dead self.factory.unregister(self) def onMessage(self, msg, isBinary): ''' Called by the server factory when the client send a message. The message is then sent to all other connected clients ''' # tell the factory to broadcast the received message # to all other connected clients self.factory.broadcast(self, msg, isBinary)Ancestors
- autobahn.twisted.websocket.WebSocketServerProtocol
- autobahn.twisted.websocket.WebSocketAdapterProtocol
- twisted.internet.protocol.Protocol
- twisted.internet.protocol.BaseProtocol
- autobahn.websocket.protocol.WebSocketServerProtocol
- autobahn.websocket.protocol.WebSocketProtocol
- autobahn.util.ObservableMixin
Methods
def onClose(self, wasClean, code, reason)-
Called by the server factory when a client disconnects
Expand source code
def onClose(self, wasClean, code, reason): ''' Called by the server factory when a client disconnects ''' print('WebSocket connection closed & deregistering: {0}'.format(reason)) # tell the factory that this connection is dead self.factory.unregister(self) def onConnect(self, request)-
Called by the server factory when a client connects. The path is found in
request.pathand is used for the clients to specify whether they are motor2020 or surface2020Expand source code
def onConnect(self, request): ''' Called by the server factory when a client connects. The path is found in `request.path` and is used for the clients to specify whether they are motor2020 or surface2020 ''' print(request.path) # debug information print('Client connecting & registering: {0}'.format(request.peer)) clientTypeRequest = request.path # process the type of request if clientTypeRequest.startswith('/'): # remove the slash if there is one clientTypeRequest = clientTypeRequest[1:] # tell the factory to remember the connection self.factory.register(self, clientTypeRequest) def onMessage(self, msg, isBinary)-
Called by the server factory when the client send a message. The message is then sent to all other connected clients
Expand source code
def onMessage(self, msg, isBinary): ''' Called by the server factory when the client send a message. The message is then sent to all other connected clients ''' # tell the factory to broadcast the received message # to all other connected clients self.factory.broadcast(self, msg, isBinary) def onOpen(self)-
Called by the server factory. Only prints debug info
Expand source code
def onOpen(self): ''' Called by the server factory. Only prints debug info ''' print('WebSocket connection open')