×
Namespaces

Variants
Actions

Archived:How to control a robot from PySymbian

From Nokia Developer Wiki
Jump to: navigation, search

Archived.pngAquivado: Este artigo foi arquivado, pois o conteúdo não é mais considerado relevante para se criar soluções comerciais atuais. Se você achar que este artigo ainda é importante, inclua o template {{ForArchiveReview|escreva a sua justificativa}}.

All PySymbian articles have been archived. PySymbian is no longer maintained by Nokia and is not guaranteed to work on more recent Symbian devices. It is not possible to submit apps to Nokia Store.

Article Metadata
Article
Created: lfd (30 Jun 2007)
Last edited: hamishwillee (31 May 2013)

To control a robot, we need a server, a client and a protocol to communicate between both.


In this example, I use some piece of source from a project of mine called myMRC (my Mobile Remote Control). Initially the project uses a mobile client written in Java, but soon the use of Python on the phone was more than great for debugging the server.


We have the following environment:

  • Server: Linux Debian machine with Gnome.
  • Client: S60 phone with PySymbian installed.
  • The robot: we will control here a multimedia player (Totem) but it would be the same server structure to control any kind of robot.


Contents

Communication protocol

We came out with the following solution to send/receive commands, arguments, data(XML)...

Packet format:
 
-------------
 
+-------------------------------------------------------+-----------//------------+
 
| header | Data |
 
+-----------+-------------------------------------------+-----------//------------+
 
| 0 1 | 2 3 | 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 20 21 22 ... Max length |
 
+-----------+-------------------------------------------+-----------//------------+
 
| OPCode | Data length | Data |
 
+-----------+-------------------------------------------+-----------//------------+
  • OPCode:

The opcode has 4 characters. The 2 first characters define the group i.e: the robot you want to control for example. Then the 2 second characters define the command. With this structure we have up to 100 groups of 100 commands, which let's us big possibilities to control about anything.


  • Data length: 16 characters are reserved to the data length. So the structure would fit also for enormous files!


  • Data: starting from character 20 to the end of the packet we have the data.


If the header + data exceed the maximum length for the packet, the first one will be sent with the header and all others without since when parsing the header, the client or the server will expect a certain amount of data to be received.


The server

Specification

Configuration files

All relevant configuration data will be stored in configuration files. (mymrc.conf, players.conf)


Concurrent connections

One of the specifications was to have concurrent connection to control the same player or different robot at the same time. A simple case is that if one is using his/hers mobile phone to control the robot but wants to switch to his/hers PDA, their is no need to disconnect the phone. Event better! If your server has the ability to pilot a USB device to control the coffee machine, you can activate it when you leave the office even if someone is changing the music at home... Only your imagination puts limits :)


Cross platform server

The server is written in Python since it is much easier to implement and also it is cross-platfrom.


The server has been under developed on Linux at the moment so it won't work on Windows machines (the controller part). Not just yet. But you can find some source to adapt like PySymbianRC


Source code

mymrc.conf
[inet]
#interface=eth0
port=1233
host=11.2.1.4
maxclient=5
 
[bluetooth]
#interface=eth0
# 0 to 9
port=1
host=localhost
 
[database]
dbname=mymrcdb
 
[players]
audio=totem
#video=
#cd=
#dvd=
 
[settings]
printdebug = yes
language=en
audio=1
video=0
dvd=0
tv=0
 
# do not modify unless you know what you are doing!
[protocol]
BufferSize=1024
DataLength=16


players.conf
[totem]
start=totem
info=totem gnome player
play =--play
stop=--pause
pause=--play-pause
next=--next
close=--quit
previous=--previous
forward=--seek-fwd
backward=--seek-bwd
volume-up=--volume-up
volume-down=--volume-down
fullscreen=--fullscreen
toogle-controls=--toggle-controls
enqueue=--enqueue
replace=--replace


mymrcd.py
## myMRC deamon
# @author LEFEVRE Damien
# @version 0.1
# @decription myMRC server daemon
from ConfigParser import ConfigParser
from popen2 import popen3 as popen
import thread, threading, socket
import os, sys
 
 
 
welcomeScreen = """********************************************************************************
Welcome to myMRC
by:
GREGORI Sven & LEFEVRE Damien
http://www.mymrc.org
********************************************************************************
"""

 
debug = True
 
## Trace debugging messages.
# @param aString String to be printed.
def printd( aString ):
if debug:
print aString
 
## Operation code dictionary.
class OPCode( dict ):
## The constructor
# @param self: The object pointer.
def __init__( self ):
dict.__init__(self)
self["EV_NULL"] = "0000"
self["EV_INFO"] = "0100"
self["EV_PLAY"] = "0101"
self["EV_STOP"] = "0102"
self["EV_PAUSE"] = "0103"
self["EV_NEXT"] = "0104"
self["EV_CLOSE"] = "0105"
self["EV_PREVIOUS"] = "0106"
self["EV_FORWARD"] = "0107"
self["EV_BACKWARD"] = "0108"
self["EV_VOLUMEUP"] = "0109"
self["EV_VOLUMEDOWN"] = "0110"
self["EV_FULLSCREEN"] = "0111"
self["EV_TOOGLECONTROLS"] = "0112"
 
 
###############################################################################
# Settings classes
###############################################################################
 
## Settings class.
# Used to retrieve settings from the configuration file.
class Players( object ):
## Stores the unique Singleton instance-
_iInstance = None
 
## Players class
class PlayersClass( ConfigParser ):
# The constructor.
# @param self: The object pointer
def __init__( self, aCwd ):
ConfigParser.__init__( self )
try:
self.readfp( open( os.path.join( os.getcwd(), "players.conf" )))
except IOError, error:
print "players.conf is missing."
sys.exit(1)
 
## The constructor
# @param self The object pointer.
def __init__( self, aCwd=None ):
# Check whether we already have an instance
if Players._iInstance is None:
# Create and remember instanc
Players._iInstance = Players.PlayersClass(aCwd)
 
# Store instance reference as the only member in the handle
self._EventHandler_instance = Players._iInstance
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @return Attribute
def __getattr__(self, aAttr):
return getattr(self._iInstance, aAttr)
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @param value Vaule to be set.
# @return Result of operation.
def __setattr__(self, aAttr, aValue):
return setattr(self._iInstance, aAttr, aValue )
 
 
## Settings class.
# Used to retrieve settings from the configuration file.
class Settings( object ):
## Stores the unique Singleton instance-
_iInstance = None
 
class SettingsClass( ConfigParser ):
# The constructor.
# @param self: The object pointer
def __init__( self, aCwd ):
ConfigParser.__init__( self )
try:
self.readfp( open( os.path.join( os.getcwd(), "mymrc.conf" ) ))
except IOError, error:
print "mymrc.conf is missing"
sys.exit(1)
 
if not self.has_section( "database" ):
DatabaseSectionError( )
 
 
 
## The constructor
# @param self The object pointer.
def __init__( self, aCwd=None ):
# Check whether we already have an instance
if Settings._iInstance is None:
# Create and remember instanc
Settings._iInstance = Settings.SettingsClass(aCwd)
 
# Store instance reference as the only member in the handle
self._EventHandler_instance = Settings._iInstance
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @return Attribute
def __getattr__(self, aAttr):
return getattr(self._iInstance, aAttr)
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @param value Vaule to be set.
# @return Result of operation.
def __setattr__(self, aAttr, aValue):
return setattr(self._iInstance, aAttr, aValue )
 
 
###############################################################################
# Protocol
###############################################################################
 
"""
Packet format:
-------------
+-----------------------------------------------------+-----------//------------+
| header | Data |
+---------+-------------------------------------------+-----------//------------+
| 0 1 2 3 | 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 20 21 22 ... Max length |
+---------+-------------------------------------------+-----------//------------+
| OPCode | Data length | Data |
+---------+-------------------------------------------+-----------//------------+
"""

 
## Parse and build packets
#
class Packet( object ):
## Packet size.
_iBufferSize = 1024
## Data length
_iDataLength = 16
 
def __init__(self):
self._iBufferSize = int(Settings().get("protocol", "BufferSize"))
self._iDataLength = int(Settings().get("protocol", "DataLength"))
 
## Get the data length with the proper formating (16 characters value).
# @param self: The object pointer.
# @return: 16 characters formated length.
def _formatedLength( self, aData ):
length = str( len( aData ) )
formatedLength = ""
i = 0
for i in range( self._iDataLength - len( length ) ):
formatedLength += "0"
formatedLength += length
return formatedLength
 
 
## Packet builder class.
class PacketBuilder( Packet ):
## Build a packet to be sent to the client.
# @param self: The obkect pointer
# @param aOpCode: Operation code
# @param aData: Data to be sent.
# @return: Packet to be sent.
def build(self, aOpCode, aData):
packet = ""
packet += str( aOpCode )
packet += self._formatedLength( aData )
packet += str( aData )
return packet
 
## Packet parser class.
class PacketParser(Packet):
## Parse incoming buffer.
# @param self: The object pointer.
# @param aBuffer: buffer coming from the server
# @return: opCode, length and data as strings.
def parse(self, aBuffer):
opCode = aBuffer[0:4]
length = aBuffer[4:20]
data = aBuffer[20:]
return (opCode, length, data)
 
 
###############################################################################
# Networking
###############################################################################
 
# Inet socket server
class InetServerSocket( socket.socket ):
 
## The constructor.
# @param self: The object pointer.
# @param aHost: Host name to connect to.
# @param aPort: Port on the host to bind.
# @param aMaxClient: Maximum number of concurrent connection.
def __init__( self, aHost, aPort, aMaxClient ):
socket.socket.__init__( self, socket.AF_INET, socket.SOCK_STREAM )
self.bind ( ( str(aHost), int(aPort) ) )
self.listen ( int(aMaxClient) )
self._iLock = threading.Semaphore()
self._iClientStack = []
self._iMaxClient = int(aMaxClient)
printd("Inet interface: (%s:%s) - concurrent connection: %s"%(aHost,
aPort,
aMaxClient))
 
 
## Add instance to the array.
# @param self: The object pointer.
# @param aChannel: Channel to had the the array.
def _addToStack( self, aClient ):
self._iLock.acquire( )
self._iClientStack.append( aClient )
self._iLock.release( )
printd( "Client added to stack")
 
 
## Remove instance from array
# @param self: The object pointer
# @param aChannel: Channel to remove from the queue
def _removeFromStack( self, aClient ):
self._iLock.acquire( )
self._iClientStack.remove( aClient )
self._iLock.release( )
printd( "Channel removed from stack." )
 
 
## Watch for channel incoming requests.
# @param self: The object pointer.
# @param aChannel: Channel to remove from the queue.
def _channelWatcher( self, aChannel ):
while 1:
data = aChannel.recv ( self._iBufferSize )
if not data: break
else:
#retVal = self._mCallback( data )
#retVal = EventHandler().event(data)
#if retVal: aChannel.send ( retVal )
pass
 
self._removeFromStack( aChannel )
aChannel.close( )
printd("Connection closed: " + str( aChannel ) )
 
 
## Handle incomming connection request.
# @param self: The object pointer.
def _connectionHandler( self ):
printd("Wait for incoming connection")
while True:
if len(self._iClientStack) in range( self._iMaxClient ):
#channel, details = self.accept()
#printd( 'New connection with: ' + str(details) )
# new client
client = Client(self.accept())
self._addToStack( client )
#self._addToStack( channel )
 
 
#channel.setblocking( 1 )
#thread.start_new_thread( self._channelWatcher, ( channel, ) )
else:
printd("Maximum concurrent connection exceeded. Close client \
connection before creating a new one"
)
 
 
## Listen interface for incomming connection.
# @param self The object pointer.
def start( self ):
thread.start_new_thread(self._connectionHandler, () )
 
 
## Send data
# @param self The object pointer.
# @param aFrame Frame.
def send( self, aFrame):
pass
 
def __del__(self):
for client in self._iClientStack:
self._removeFromStack(client)
del client
 
## Inet server singletong
class InetServer( object ):
## Stores the unique Singleton instance-
_iInstance = None
 
## Inet server class declaration
class InetServerClass:
 
## The constructor.
# @param self: The object pointer.
def __init__( self ):
self._iSocketServer = None
 
## Start the server.
# @param self The object pointer.
def start(self):
self._iSocketServer = InetServerSocket(
Settings().get("inet","host"),
Settings().get("inet","port"),
Settings().get("inet","maxclient"))
self._iSocketServer.start()
printd("InetServer started")
 
## Stop the server.
# @param self The object pointer.
def stop(self):
if self._iSocketServer:
del self._iSocketServer
self._iSocketServer = None
printd("InetServer stopped")
 
## Restart server.
# @param self The object pointer.
def restart(self):
printd("Restarting InetServer")
self.stop()
self.start()
 
 
## The destructor
# @param self: The object pointer
def __del__( self ):
self.stop()
 
###########################################################################
# Singleton accessors
###########################################################################
 
## The constructor
# @param self The object pointer.
def __init__( self ):
# Check whether we already have an instance
if InetServer._iInstance is None:
# Create and remember instanc
InetServer._iInstance = InetServer.InetServerClass()
 
# Store instance reference as the only member in the handle
self._EventHandler_instance = InetServer._iInstance
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @return Attribute
def __getattr__(self, aAttr):
return getattr(self._iInstance, aAttr)
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @param value Vaule to be set.
# @return Result of operation.
def __setattr__(self, aAttr, aValue):
return setattr(self._iInstance, aAttr, aValue )
 
 
###############################################################################
# Controlers
###############################################################################
class Controller( object ):
def __init__(self):
if sys.platform == 'win32':
self._shell, self._tail = ('cmd', '\r\n')
else:
self._shell, self._tail = ('sh', '\n')
 
 
def cmd( self, aCommand ):
try:
printd(aCommand)
popen(aCommand+self._tail)
except Exception, error:
printd('AudioController: '+ repr(error))
 
return '0000', 'error'
 
class AudioController( Controller, Players ):
def __init__(self):
Controller.__init__(self)
Players.__init__(self)
self._iPlayer = Settings().get("players", "audio")
printd("Audio player: " + self._iPlayer)
self._iNBSP = " "
 
def play(self, aFile=None):
command = self.get(self._iPlayer, "start") + self._iNBSP + \
self.get(self._iPlayer, "play") + self._iNBSP + \
"\"%s\"" % aFile
 
thread.start_new(self.cmd, (command,))
return "Play"
 
def stop(self):
command = self.get(self._iPlayer, "start")+ \
self._iNBSP+self.get(self._iPlayer, "stop")
return self.cmd(command)
 
def pause(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "pause")
return self.cmd(command)
 
def next(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "next")
return self.cmd(command)
 
def close(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "close")
return self.cmd(command)
 
def previous(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "previous")
return self.cmd(command)
 
def forward(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "forward")
return self.cmd(command)
 
def backward(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "backward")
return self.cmd(command)
 
def volume_up(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "volume-up")
return self.cmd(command)
 
## volume down.
# @callgraph
# @callergraph
def volume_down(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "volume-down")
return self.cmd(command)
 
def fullscreen(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "fullscreen")
return self.cmd(command)
 
def hide_show_controls(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "toogle-controls")
return self.cmd(command)
 
def __del__(self):
printd("AudioController: Destructor")
self.close()
 
 
###############################################################################
# Client
###############################################################################
 
class Client:
def __init__(self, aConnection ):
self._iConn = aConnection[0]
self._iConn.setblocking( 1 )
self._iObserverPid = 0
self._iBufferSize = int(Settings().get("protocol", "BufferSize"))
print self._iBufferSize
self._iAudioController = AudioController()
self.start_observer()
printd( 'New connection with: ' + str(aConnection[1]) )
 
def _observer(self):
# set PID to be able to kill the thread
try:
self._iObserverPid = os.getpid()
except:
pass
while 1:
data = self._iConn.recv( self._iBufferSize )
if not data: break
else:
retVal = self._handle_event(data)
if retVal: self.send(PacketBuilder().build(retVal[0], retVal[1]))
self._iConn.close( )
self._iObserverPid = 0
printd("Connection closed: " + str( self._iConn ) )
 
def send(self, aString):
self._iConn.sendall(aString, self._iBufferSize)
 
def receive(self):
pass
 
def start_observer(self):
print "start_observer(self):"
thread.start_new_thread(self._observer, () )
 
def stop_observer(self):
if self._iObserverPid:
os.popen("kill -9 "+str(self._iObserverPid))
 
def _handle_event(self, aData):
opcode, length, data = PacketParser().parse(aData)
 
if opcode == OPCode()['EV_INFO']:
self._iAudioController.play()
return OPCode()['EV_NULL'], "info"
 
elif opcode == OPCode()["EV_PLAY"]:
song = "/home/storage/Music/Counting Crows - Mr. Jones.mp3"
self._iAudioController.play( song )
return OPCode()['EV_NULL'], "Playing"
 
elif opcode == OPCode()["EV_STOP"]:
self._iAudioController.stop()
return OPCode()['EV_NULL'], "Stopped"
 
elif opcode == OPCode()["EV_PAUSE"]:
self._iAudioController.pause()
return OPCode()['EV_NULL'], "Play/Pause"
 
elif opcode == OPCode()["EV_NEXT"]:
self._iAudioController.next()
return OPCode()["EV_NULL"], "Next"
 
elif opcode == OPCode()["EV_CLOSE"]:
self._iAudioController.close()
return OPCode()['EV_NULL'], "Closed"
 
elif opcode == OPCode()["EV_PREVIOUS"]:
self._iAudioController.previous()
return OPCode()['EV_NULL'], "Previous"
 
elif opcode == OPCode()["EV_FORWARD"]:
self._iAudioController.forward()
return OPCode()['EV_NULL'], "Forward"
 
elif opcode == OPCode()["EV_BACKWARD"]:
self._iAudioController.backward()
return OPCode()['EV_NULL'], "Backward"
 
elif opcode == OPCode()["EV_VOLUMEUP"]:
self._iAudioController.volume_up()
return OPCode()['EV_NULL'], "Volume up"
 
elif opcode == OPCode()["EV_VOLUMEDOWN"]:
self._iAudioController.volume_down()
return OPCode()['EV_NULL'], "Volume down"
 
elif opcode == OPCode()["EV_FULLSCREEN"]:
self._iAudioController.fullscreen()
return OPCode()['EV_NULL'], "Fullscreen"
 
elif opcode == OPCode()["EV_TOOGLECONTROLS"]:
self._iAudioController.hide_show_controls()
return OPCode()['EV_NULL'], "Toogle controls"
else:
return "0000", ''
 
 
 
def __del__(self):
self._iEngine()
self._iConn.close()
 
 
###############################################################################
# Utils
###############################################################################
class App_lock:
## The constructor.
# @param self: The object pointer.
def __init__( self ):
## class semaphore
self._iAppLock = threading.Semaphore()
self._iAppLock.acquire()
 
## Wait forever until the lock is signaled.
# @param self: The object pointer.
def wait( self ):
# Wait forever to acquire the semaphore.
self._iAppLock.acquire(True)
# Release semaphore; the application exit.
self._iAppLock.release()
 
## Signal the lock to exit the application.
# @param self: The object pointer.
def signal(self):
self._iAppLock.release()
 
## myMRC daemon server class.
class MyMRCd:
 
## The constructor.
# @param self The object pointer.
def __init__(self):
print welcomeScreen
# Set the confile file path
Settings( os.getcwd() )
Players( os.getcwd() )
 
## Run the server
# @param self The object pointer.
def run(self):
InetServer().start()
 
 
## Destructor. Stops the networking servers
# @param self The object pointer.
def __del__(self):
InetServer().stop()
 
 
if __name__ == '__main__':
APP_LOCK = App_lock()
myMRC = MyMRCd()
myMRC.run()
APP_LOCK.wait( )


The PySymbian client

Here come a minimalistic client meant to debug the server.

Make sure to put an exact copy of the opcode dictionary on the client and the server.

With this script below, you will have to set the 'ip' and 'port' to connect to on the server as well as a valid video or audio file in 'play'


Source code

MyMRCpy.py
## myMRC deamon
# @author LEFEVRE Damien
# @version 0.1
# @decription myMRC server daemon
from ConfigParser import ConfigParser
from popen2 import popen3 as popen
import thread, threading, socket
import os, sys
 
 
 
welcomeScreen = """********************************************************************************
Welcome to myMRC
by:
GREGORI Sven & LEFEVRE Damien
http://www.mymrc.org
********************************************************************************
"""

 
debug = True
 
## Trace debugging messages.
# @param aString String to be printed.
def printd( aString ):
if debug:
print aString
 
## Operation code dictionary.
class OPCode( dict ):
## The constructor
# @param self: The object pointer.
def __init__( self ):
dict.__init__(self)
self["EV_NULL"] = "0000"
self["EV_INFO"] = "0100"
self["EV_PLAY"] = "0101"
self["EV_STOP"] = "0102"
self["EV_PAUSE"] = "0103"
self["EV_NEXT"] = "0104"
self["EV_CLOSE"] = "0105"
self["EV_PREVIOUS"] = "0106"
self["EV_FORWARD"] = "0107"
self["EV_BACKWARD"] = "0108"
self["EV_VOLUMEUP"] = "0109"
self["EV_VOLUMEDOWN"] = "0110"
self["EV_FULLSCREEN"] = "0111"
self["EV_TOOGLECONTROLS"] = "0112"
 
 
###############################################################################
# Settings classes
###############################################################################
 
## Settings class.
# Used to retrieve settings from the configuration file.
class Players( object ):
## Stores the unique Singleton instance-
_iInstance = None
 
## Players class
class PlayersClass( ConfigParser ):
# The constructor.
# @param self: The object pointer
def __init__( self, aCwd ):
ConfigParser.__init__( self )
try:
self.readfp( open( os.path.join( os.getcwd(), "players.conf" )))
except IOError, error:
print "players.conf is missing."
sys.exit(1)
 
## The constructor
# @param self The object pointer.
def __init__( self, aCwd=None ):
# Check whether we already have an instance
if Players._iInstance is None:
# Create and remember instanc
Players._iInstance = Players.PlayersClass(aCwd)
 
# Store instance reference as the only member in the handle
self._EventHandler_instance = Players._iInstance
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @return Attribute
def __getattr__(self, aAttr):
return getattr(self._iInstance, aAttr)
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @param value Vaule to be set.
# @return Result of operation.
def __setattr__(self, aAttr, aValue):
return setattr(self._iInstance, aAttr, aValue )
 
 
## Settings class.
# Used to retrieve settings from the configuration file.
class Settings( object ):
## Stores the unique Singleton instance-
_iInstance = None
 
class SettingsClass( ConfigParser ):
# The constructor.
# @param self: The object pointer
def __init__( self, aCwd ):
ConfigParser.__init__( self )
try:
self.readfp( open( os.path.join( os.getcwd(), "mymrc.conf" ) ))
except IOError, error:
print "mymrc.conf is missing"
sys.exit(1)
 
if not self.has_section( "database" ):
DatabaseSectionError( )
 
 
 
## The constructor
# @param self The object pointer.
def __init__( self, aCwd=None ):
# Check whether we already have an instance
if Settings._iInstance is None:
# Create and remember instanc
Settings._iInstance = Settings.SettingsClass(aCwd)
 
# Store instance reference as the only member in the handle
self._EventHandler_instance = Settings._iInstance
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @return Attribute
def __getattr__(self, aAttr):
return getattr(self._iInstance, aAttr)
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @param value Vaule to be set.
# @return Result of operation.
def __setattr__(self, aAttr, aValue):
return setattr(self._iInstance, aAttr, aValue )
 
 
###############################################################################
# Protocol
###############################################################################
 
"""
Packet format:
-------------
+-----------------------------------------------------+-----------//------------+
| header | Data |
+---------+-------------------------------------------+-----------//------------+
| 0 1 2 3 | 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 20 21 22 ... Max length |
+---------+-------------------------------------------+-----------//------------+
| OPCode | Data length | Data |
+---------+-------------------------------------------+-----------//------------+
"""

 
## Parse and build packets
#
class Packet( object ):
## Packet size.
_iBufferSize = 1024
## Data length
_iDataLength = 16
 
def __init__(self):
self._iBufferSize = int(Settings().get("protocol", "BufferSize"))
self._iDataLength = int(Settings().get("protocol", "DataLength"))
 
## Get the data length with the proper formating (16 characters value).
# @param self: The object pointer.
# @return: 16 characters formated length.
def _formatedLength( self, aData ):
length = str( len( aData ) )
formatedLength = ""
i = 0
for i in range( self._iDataLength - len( length ) ):
formatedLength += "0"
formatedLength += length
return formatedLength
 
 
## Packet builder class.
class PacketBuilder( Packet ):
## Build a packet to be sent to the client.
# @param self: The obkect pointer
# @param aOpCode: Operation code
# @param aData: Data to be sent.
# @return: Packet to be sent.
def build(self, aOpCode, aData):
packet = ""
packet += str( aOpCode )
packet += self._formatedLength( aData )
packet += str( aData )
return packet
 
## Packet parser class.
class PacketParser(Packet):
## Parse incoming buffer.
# @param self: The object pointer.
# @param aBuffer: buffer coming from the server
# @return: opCode, length and data as strings.
def parse(self, aBuffer):
opCode = aBuffer[0:4]
length = aBuffer[4:20]
data = aBuffer[20:]
return (opCode, length, data)
 
 
###############################################################################
# Networking
###############################################################################
 
# Inet socket server
class InetServerSocket( socket.socket ):
 
## The constructor.
# @param self: The object pointer.
# @param aHost: Host name to connect to.
# @param aPort: Port on the host to bind.
# @param aMaxClient: Maximum number of concurrent connection.
def __init__( self, aHost, aPort, aMaxClient ):
socket.socket.__init__( self, socket.AF_INET, socket.SOCK_STREAM )
self.bind ( ( str(aHost), int(aPort) ) )
self.listen ( int(aMaxClient) )
self._iLock = threading.Semaphore()
self._iClientStack = []
self._iMaxClient = int(aMaxClient)
printd("Inet interface: (%s:%s) - concurrent connection: %s"%(aHost,
aPort,
aMaxClient))
 
 
## Add instance to the array.
# @param self: The object pointer.
# @param aChannel: Channel to had the the array.
def _addToStack( self, aClient ):
self._iLock.acquire( )
self._iClientStack.append( aClient )
self._iLock.release( )
printd( "Client added to stack")
 
 
## Remove instance from array
# @param self: The object pointer
# @param aChannel: Channel to remove from the queue
def _removeFromStack( self, aClient ):
self._iLock.acquire( )
self._iClientStack.remove( aClient )
self._iLock.release( )
printd( "Channel removed from stack." )
 
 
## Watch for channel incoming requests.
# @param self: The object pointer.
# @param aChannel: Channel to remove from the queue.
def _channelWatcher( self, aChannel ):
while 1:
data = aChannel.recv ( self._iBufferSize )
if not data: break
else:
#retVal = self._mCallback( data )
#retVal = EventHandler().event(data)
#if retVal: aChannel.send ( retVal )
pass
 
self._removeFromStack( aChannel )
aChannel.close( )
printd("Connection closed: " + str( aChannel ) )
 
 
## Handle incomming connection request.
# @param self: The object pointer.
def _connectionHandler( self ):
printd("Wait for incoming connection")
while True:
if len(self._iClientStack) in range( self._iMaxClient ):
#channel, details = self.accept()
#printd( 'New connection with: ' + str(details) )
# new client
client = Client(self.accept())
self._addToStack( client )
#self._addToStack( channel )
 
 
#channel.setblocking( 1 )
#thread.start_new_thread( self._channelWatcher, ( channel, ) )
else:
printd("Maximum concurrent connection exceeded. Close client \
connection before creating a new one"
)
 
 
## Listen interface for incomming connection.
# @param self The object pointer.
def start( self ):
thread.start_new_thread(self._connectionHandler, () )
 
 
## Send data
# @param self The object pointer.
# @param aFrame Frame.
def send( self, aFrame):
pass
 
def __del__(self):
for client in self._iClientStack:
self._removeFromStack(client)
del client
 
## Inet server singletong
class InetServer( object ):
## Stores the unique Singleton instance-
_iInstance = None
 
## Inet server class declaration
class InetServerClass:
 
## The constructor.
# @param self: The object pointer.
def __init__( self ):
self._iSocketServer = None
 
## Start the server.
# @param self The object pointer.
def start(self):
self._iSocketServer = InetServerSocket(
Settings().get("inet","host"),
Settings().get("inet","port"),
Settings().get("inet","maxclient"))
self._iSocketServer.start()
printd("InetServer started")
 
## Stop the server.
# @param self The object pointer.
def stop(self):
if self._iSocketServer:
del self._iSocketServer
self._iSocketServer = None
printd("InetServer stopped")
 
## Restart server.
# @param self The object pointer.
def restart(self):
printd("Restarting InetServer")
self.stop()
self.start()
 
 
## The destructor
# @param self: The object pointer
def __del__( self ):
self.stop()
 
###########################################################################
# Singleton accessors
###########################################################################
 
## The constructor
# @param self The object pointer.
def __init__( self ):
# Check whether we already have an instance
if InetServer._iInstance is None:
# Create and remember instanc
InetServer._iInstance = InetServer.InetServerClass()
 
# Store instance reference as the only member in the handle
self._EventHandler_instance = InetServer._iInstance
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @return Attribute
def __getattr__(self, aAttr):
return getattr(self._iInstance, aAttr)
 
 
## Delegate access to implementation.
# @param self The object pointer.
# @param attr Attribute wanted.
# @param value Vaule to be set.
# @return Result of operation.
def __setattr__(self, aAttr, aValue):
return setattr(self._iInstance, aAttr, aValue )
 
 
###############################################################################
# Controlers
###############################################################################
class Controller( object ):
def __init__(self):
if sys.platform == 'win32':
self._shell, self._tail = ('cmd', '\r\n')
else:
self._shell, self._tail = ('sh', '\n')
 
 
def cmd( self, aCommand ):
try:
printd(aCommand)
popen(aCommand+self._tail)
except Exception, error:
printd('AudioController: '+ repr(error))
 
return '0000', 'error'
 
class AudioController( Controller, Players ):
def __init__(self):
Controller.__init__(self)
Players.__init__(self)
self._iPlayer = Settings().get("players", "audio")
printd("Audio player: " + self._iPlayer)
self._iNBSP = " "
 
def play(self, aFile=None):
command = self.get(self._iPlayer, "start") + self._iNBSP + \
self.get(self._iPlayer, "play") + self._iNBSP + \
"\"%s\"" % aFile
 
thread.start_new(self.cmd, (command,))
return "Play"
 
def stop(self):
command = self.get(self._iPlayer, "start")+ \
self._iNBSP+self.get(self._iPlayer, "stop")
return self.cmd(command)
 
def pause(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "pause")
return self.cmd(command)
 
def next(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "next")
return self.cmd(command)
 
def close(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "close")
return self.cmd(command)
 
def previous(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "previous")
return self.cmd(command)
 
def forward(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "forward")
return self.cmd(command)
 
def backward(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "backward")
return self.cmd(command)
 
def volume_up(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "volume-up")
return self.cmd(command)
 
## volume down.
# @callgraph
# @callergraph
def volume_down(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "volume-down")
return self.cmd(command)
 
def fullscreen(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "fullscreen")
return self.cmd(command)
 
def hide_show_controls(self):
command = self.get(self._iPlayer, "start")+\
self._iNBSP+self.get(self._iPlayer, "toogle-controls")
return self.cmd(command)
 
def __del__(self):
printd("AudioController: Destructor")
self.close()
 
 
###############################################################################
# Client
###############################################################################
 
class Client:
def __init__(self, aConnection ):
self._iConn = aConnection[0]
self._iConn.setblocking( 1 )
self._iObserverPid = 0
self._iBufferSize = int(Settings().get("protocol", "BufferSize"))
print self._iBufferSize
self._iAudioController = AudioController()
self.start_observer()
printd( 'New connection with: ' + str(aConnection[1]) )
 
def _observer(self):
# set PID to be able to kill the thread
try:
self._iObserverPid = os.getpid()
except:
pass
while 1:
data = self._iConn.recv( self._iBufferSize )
if not data: break
else:
retVal = self._handle_event(data)
if retVal: self.send(PacketBuilder().build(retVal[0], retVal[1]))
self._iConn.close( )
self._iObserverPid = 0
printd("Connection closed: " + str( self._iConn ) )
 
def send(self, aString):
self._iConn.sendall(aString, self._iBufferSize)
 
def receive(self):
pass
 
def start_observer(self):
print "start_observer(self):"
thread.start_new_thread(self._observer, () )
 
def stop_observer(self):
if self._iObserverPid:
os.popen("kill -9 "+str(self._iObserverPid))
 
def _handle_event(self, aData):
opcode, length, data = PacketParser().parse(aData)
 
if opcode == OPCode()['EV_INFO']:
self._iAudioController.play()
return OPCode()['EV_NULL'], "info"
 
elif opcode == OPCode()["EV_PLAY"]:
song = "/home/storage/Music/Counting Crows - Mr. Jones.mp3"
self._iAudioController.play( song )
return OPCode()['EV_NULL'], "Playing"
 
elif opcode == OPCode()["EV_STOP"]:
self._iAudioController.stop()
return OPCode()['EV_NULL'], "Stopped"
 
elif opcode == OPCode()["EV_PAUSE"]:
self._iAudioController.pause()
return OPCode()['EV_NULL'], "Play/Pause"
 
elif opcode == OPCode()["EV_NEXT"]:
self._iAudioController.next()
return OPCode()["EV_NULL"], "Next"
 
elif opcode == OPCode()["EV_CLOSE"]:
self._iAudioController.close()
return OPCode()['EV_NULL'], "Closed"
 
elif opcode == OPCode()["EV_PREVIOUS"]:
self._iAudioController.previous()
return OPCode()['EV_NULL'], "Previous"
 
elif opcode == OPCode()["EV_FORWARD"]:
self._iAudioController.forward()
return OPCode()['EV_NULL'], "Forward"
 
elif opcode == OPCode()["EV_BACKWARD"]:
self._iAudioController.backward()
return OPCode()['EV_NULL'], "Backward"
 
elif opcode == OPCode()["EV_VOLUMEUP"]:
self._iAudioController.volume_up()
return OPCode()['EV_NULL'], "Volume up"
 
elif opcode == OPCode()["EV_VOLUMEDOWN"]:
self._iAudioController.volume_down()
return OPCode()['EV_NULL'], "Volume down"
 
elif opcode == OPCode()["EV_FULLSCREEN"]:
self._iAudioController.fullscreen()
return OPCode()['EV_NULL'], "Fullscreen"
 
elif opcode == OPCode()["EV_TOOGLECONTROLS"]:
self._iAudioController.hide_show_controls()
return OPCode()['EV_NULL'], "Toogle controls"
else:
return "0000", ''
 
 
 
def __del__(self):
self._iEngine()
self._iConn.close()
 
 
###############################################################################
# Utils
###############################################################################
class App_lock:
## The constructor.
# @param self: The object pointer.
def __init__( self ):
## class semaphore
self._iAppLock = threading.Semaphore()
self._iAppLock.acquire()
 
## Wait forever until the lock is signaled.
# @param self: The object pointer.
def wait( self ):
# Wait forever to acquire the semaphore.
self._iAppLock.acquire(True)
# Release semaphore; the application exit.
self._iAppLock.release()
 
## Signal the lock to exit the application.
# @param self: The object pointer.
def signal(self):
self._iAppLock.release()
 
## myMRC daemon server class.
class MyMRCd:
 
## The constructor.
# @param self The object pointer.
def __init__(self):
print welcomeScreen
# Set the confile file path
Settings( os.getcwd() )
Players( os.getcwd() )
 
## Run the server
# @param self The object pointer.
def run(self):
InetServer().start()
 
 
## Destructor. Stops the networking servers
# @param self The object pointer.
def __del__(self):
InetServer().stop()
 
 
if __name__ == '__main__':
APP_LOCK = App_lock()
myMRC = MyMRCd()
myMRC.run()
APP_LOCK.wait( )


Test

  • run
python mymrcd.py
  • Start the client on the phone
This page was last modified on 31 May 2013, at 04:03.
69 page views in the last 30 days.