2009-02-11 22:36:48 +00:00
# standard python libs
2009-07-15 17:25:34 +00:00
from logging import getLogger , ERROR , WARNING , INFO , DEBUG
import traceback
2009-02-11 22:36:48 +00:00
2009-03-03 01:40:52 +00:00
# related
from eventlet import api , util
2009-07-15 17:25:34 +00:00
2009-03-03 01:40:52 +00:00
# the following makes socket calls nonblocking. magic
util . wrap_socket_with_coroutine_socket ( )
2009-02-11 22:36:48 +00:00
2009-03-03 01:40:52 +00:00
# pyogp
2009-07-15 17:25:34 +00:00
from pyogp . lib . base . exc import RegionCapNotAvailable
2009-02-11 22:36:48 +00:00
2009-03-03 01:40:52 +00:00
# messaging
2009-06-05 21:02:59 +00:00
from pyogp . lib . base . message . message_handler import MessageHandler
2009-07-15 17:25:34 +00:00
from pyogp . lib . base . message . message import Message , Block , Variable
2009-03-03 01:40:52 +00:00
from pyogp . lib . base . message . template_dict import TemplateDictionary
2009-02-11 22:36:48 +00:00
# initialize logging
logger = getLogger ( ' pyogp.lib.base.event_queue ' )
log = logger . log
2009-03-03 01:40:52 +00:00
class EventQueueClient ( object ) :
""" handles an event queue of either an agent domain or a simulator
Initialize the event queue client class
>> > client = EventQueueClient ( )
The event queue client requires an event queue capability
>> > from pyogp . lib . base . caps import Capability
>> > cap = Capability ( ' EventQueue ' , http : / / localhost : 12345 / cap )
>> > event_queue = EventQueueClient ( cap )
>> > event_queue . start ( )
2009-02-11 22:36:48 +00:00
2009-03-03 01:40:52 +00:00
Sample implementations : region . py
Tests : tests / test_event_queue . py
"""
2009-06-05 21:02:59 +00:00
def __init__ ( self , capability = None , settings = None , message_handler = None , region = None ) :
2009-02-11 22:36:48 +00:00
""" set up the event queue attributes """
2009-03-03 01:40:52 +00:00
# allow the settings to be passed in
# otherwise, grab the defaults
if settings != None :
self . settings = settings
else :
from pyogp . lib . base . settings import Settings
self . settings = Settings ( )
# allow the packet_handler to be passed in
# otherwise, grab the defaults
2009-06-05 21:02:59 +00:00
# otherwise, let's just use our own
if message_handler != None :
self . message_handler = message_handler
else :
self . message_handler = MessageHandler ( )
2009-03-12 20:48:26 +00:00
2009-03-04 21:33:01 +00:00
self . region = region
2009-02-11 22:36:48 +00:00
self . cap = capability
#self.type = eq_type # specify 'agentdomain' or 'region'
2009-07-07 00:15:55 +00:00
self . type = ' typeNotSpecified '
2009-02-11 22:36:48 +00:00
self . _running = False # this class controls this value
2009-06-15 17:11:54 +00:00
self . stopped = False # client can pause the event queue
2009-02-11 22:36:48 +00:00
self . last_id = - 1
# if applicable, initialize data to post to the event queue
self . data = { }
# stores the result of the post to the event queue capability
self . result = None
2009-03-03 01:40:52 +00:00
# enables proper packet parsing in event queue responses
self . template_dict = TemplateDictionary ( )
self . current_template = None
2009-02-11 22:36:48 +00:00
def start ( self ) :
2009-07-15 17:25:34 +00:00
""" spawns a coroutine connecting to the event queue on the target """
2009-02-11 22:36:48 +00:00
2009-03-04 21:33:01 +00:00
try :
2009-04-01 23:49:16 +00:00
2009-03-04 21:33:01 +00:00
if self . cap . name == ' event_queue ' :
2009-04-01 23:49:16 +00:00
2009-07-15 17:25:34 +00:00
if self . settings . LOG_COROUTINE_SPAWNS :
log ( INFO , " Spawning a coroutine for event queue in the agent domain context. " )
2009-04-01 23:49:16 +00:00
self . _processADEventQueue ( )
2009-03-04 21:33:01 +00:00
return True
2009-04-01 23:49:16 +00:00
2009-03-04 21:33:01 +00:00
elif self . cap . name == ' EventQueueGet ' :
2009-04-01 23:49:16 +00:00
2009-07-15 17:25:34 +00:00
if self . settings . LOG_COROUTINE_SPAWNS :
log ( INFO , " Spawning a coroutine for event queue in the simulator context for %s . " % ( str ( self . region . sim_ip ) + ' : ' + str ( self . region . sim_port ) ) )
2009-04-01 23:49:16 +00:00
self . _processRegionEventQueue ( )
2009-03-04 21:33:01 +00:00
return True
2009-04-01 23:49:16 +00:00
2009-03-04 21:33:01 +00:00
else :
2009-04-01 23:49:16 +00:00
2009-03-04 21:33:01 +00:00
# ToDo handle this as an exception
2009-04-01 23:49:16 +00:00
2009-03-04 21:33:01 +00:00
log ( WARNING , ' Unable to start event queue polling due to %s ' % ( ' unknown queue type ' ) )
return False
2009-04-01 23:49:16 +00:00
except Exception , error :
2009-07-07 00:15:55 +00:00
2009-07-15 17:25:34 +00:00
log ( ERROR , " Problem starting event queue for %s with cap of %s , with an error of: %s " % ( str ( self . region . sim_ip ) + ' : ' + str ( self . region . sim_port ) , str ( self . cap ) , error ) )
2009-07-07 00:15:55 +00:00
2009-03-04 21:33:01 +00:00
return False
2009-02-11 22:36:48 +00:00
def stop ( self ) :
2009-07-15 17:25:34 +00:00
""" trigger the event queue to stop communicating with the simulator """
2009-03-03 01:40:52 +00:00
2009-06-15 17:11:54 +00:00
log ( INFO , " Stopping event queue. " )
self . stopped = True
def stop_monitor ( self , interval , times ) :
2009-07-15 17:25:34 +00:00
""" monitors the stopping of the event queue client connection """
for i in range ( 0 , times ) :
2009-06-15 17:11:54 +00:00
api . sleep ( interval )
if self . _running == False :
log ( INFO ,
" Stopped event queue processing for %s " ,
self . region . SimName )
return
log ( WARNING ,
" Failed to stop event queue for %s after %s seconds " ,
self . region . SimName ,
str ( interval * times ) )
api . spawn ( stop_monitor , self , self . settings . REGION_EVENT_QUEUE_POLL_INTERVAL , 10 )
2009-02-11 22:36:48 +00:00
def _processRegionEventQueue ( self ) :
if self . cap . name != ' EventQueueGet ' :
2009-07-07 00:15:55 +00:00
raise RegionCapNotAvailable ( ' EventQueueGet ' )
2009-02-11 22:36:48 +00:00
# well then get it...?
else :
self . _running = True
2009-06-15 17:11:54 +00:00
while not self . stopped :
2009-02-11 22:36:48 +00:00
2009-03-31 15:20:26 +00:00
try :
api . sleep ( self . settings . REGION_EVENT_QUEUE_POLL_INTERVAL )
2009-02-11 22:36:48 +00:00
2009-06-15 17:11:54 +00:00
self . data = { }
2009-03-31 15:20:26 +00:00
if self . last_id != - 1 :
2009-06-15 17:11:54 +00:00
self . data = { ' ack ' : self . last_id }
2009-02-11 22:36:48 +00:00
2009-04-01 23:49:16 +00:00
if self . settings . ENABLE_EQ_LOGGING :
if self . settings . ENABLE_HOST_LOGGING :
host_string = ' to ( %s ) ' % ( str ( self . region . sim_ip ) + ' : ' + str ( self . region . sim_port ) )
else :
host_string = ' '
log ( DEBUG , ' Posting to the event queue %s : %s ' % ( host_string , self . data ) )
2009-03-12 20:48:26 +00:00
2009-03-31 15:20:26 +00:00
try :
self . result = self . cap . POST ( self . data )
except Exception , error :
2009-04-01 23:49:16 +00:00
if self . settings . ENABLE_HOST_LOGGING :
host_string = ' from ( %s ) ' % ( str ( self . region . sim_ip ) + ' : ' + str ( self . region . sim_port ) )
else :
host_string = ' '
log ( INFO , " Received an error we ought not care about %s : %s " % ( host_string , error ) )
2009-03-03 01:40:52 +00:00
2009-03-31 15:20:26 +00:00
if self . result != None :
self . last_id = self . result [ ' id ' ]
else :
self . last_id = - 1
2009-02-11 22:36:48 +00:00
2009-03-31 15:20:26 +00:00
self . _parse_result ( self . result )
except Exception , error :
log ( WARNING , " Error in a post to the event queue. Error was: %s " % ( error ) )
2009-07-07 00:15:55 +00:00
if self . last_id != - 1 :
# Need to ack the last message received, otherwise it will be
# resent if we re-connect to the same queue
self . data = { ' ack ' : self . last_id , ' done ' : True }
self . cap . POST ( self . data )
2009-07-06 23:29:38 +00:00
2009-06-15 17:11:54 +00:00
if self . last_id != - 1 :
# Need to ack the last message received, otherwise it will be
# resent if we re-connect to the same queue
self . data = { ' ack ' : self . last_id , ' done ' : True }
self . cap . POST ( self . data )
2009-03-03 01:40:52 +00:00
self . _running = False
2009-02-11 22:36:48 +00:00
2009-03-04 21:33:01 +00:00
log ( DEBUG , " Stopped event queue processing for %s " % ( self . region . SimName ) )
2009-02-11 22:36:48 +00:00
def _processADEventQueue ( self ) :
2009-07-15 17:25:34 +00:00
""" connects to an agent domain ' s event queue """
2009-02-11 22:36:48 +00:00
if self . cap . name != ' event_queue ' :
raise RegionCapNotAvailable ( ' event_queue ' )
# change the exception here (add a new one)
else :
self . _running = True
2009-06-15 17:11:54 +00:00
while not self . stopped :
2009-02-11 22:36:48 +00:00
api . sleep ( self . settings . agentdomain_event_queue_interval )
self . result = self . capabilities [ ' event_queue ' ] . POST ( self . data )
2009-03-03 01:40:52 +00:00
if self . result != None : self . last_id = self . result [ ' id ' ]
self . _parse_result ( self . result )
self . _running = False
2009-02-11 22:36:48 +00:00
def _parse_result ( self , data ) :
2009-07-15 17:25:34 +00:00
""" tries to parse the llsd response from an event queue request.
if successful , the event queue passes messages through the message_handler for evaluation """
2009-02-11 22:36:48 +00:00
# if there are subscribers to the event queue and packet handling is enabled
2009-03-03 01:40:52 +00:00
if self . settings . HANDLE_PACKETS : # and (len(self.handler) > 0):
2009-03-09 21:43:15 +00:00
2009-02-11 22:36:48 +00:00
try :
2009-03-09 21:43:15 +00:00
2009-03-03 01:40:52 +00:00
if data != None :
2009-03-09 21:43:15 +00:00
2009-04-01 23:49:16 +00:00
if self . settings . ENABLE_EQ_LOGGING :
if self . settings . ENABLE_HOST_LOGGING :
host_string = ' from ( %s ) ' % ( str ( self . region . sim_ip ) + ' : ' + str ( self . region . sim_port ) )
else :
host_string = ' '
log ( DEBUG , ' Event Queue result %s : %s ' % ( host_string , data ) )
2009-03-09 21:43:15 +00:00
2009-03-11 22:52:07 +00:00
# if we are handling packets, handle the packet so any subscribers can get the data
2009-02-11 22:36:48 +00:00
if self . settings . HANDLE_PACKETS :
2009-03-09 21:43:15 +00:00
2009-03-11 22:52:07 +00:00
# this returns packets
parsed_data = self . _decode_eq_result ( data )
2009-03-03 01:40:52 +00:00
for packet in parsed_data :
2009-07-15 17:25:34 +00:00
self . message_handler . handle ( packet )
2009-03-09 21:43:15 +00:00
2009-03-03 01:40:52 +00:00
except Exception , error :
2009-07-15 17:25:34 +00:00
2009-04-01 23:49:16 +00:00
traceback . print_exc ( )
2009-07-15 17:25:34 +00:00
2009-04-01 23:49:16 +00:00
if self . settings . ENABLE_HOST_LOGGING :
host_string = ' from ( %s ) ' % ( str ( self . region . sim_ip ) + ' : ' + str ( self . region . sim_port ) )
else :
host_string = ' '
2009-07-15 17:25:34 +00:00
2009-04-01 23:49:16 +00:00
log ( WARNING , " Error parsing even queue results %s . Error: %s . Data was: %s " % ( host_string , error , data ) )
2009-02-11 22:36:48 +00:00
def _decode_eq_result ( self , data = None ) :
2009-03-03 01:40:52 +00:00
""" parse the event queue data, return a list of packets
the building of packets borrows absurdly much from UDPDeserializer . __decode_data ( )
"""
# ToDo: this is returning packets, but perhaps we want to return packet class instances?
if data != None :
2009-06-05 21:02:59 +00:00
messages = [ ]
2009-03-03 01:40:52 +00:00
if data . has_key ( ' events ' ) :
for i in data :
if i == ' id ' :
2009-06-05 21:02:59 +00:00
#last_id = data[i]
pass
2009-03-03 01:40:52 +00:00
else :
for message in data [ i ] :
2009-03-12 20:48:26 +00:00
# move this to a proper solution, for now, append to some list eq events
# or some dict mapping name to action to take
2009-04-01 23:49:16 +00:00
2009-06-05 21:02:59 +00:00
in_template = self . template_dict . get_template ( message [ ' message ' ] )
if in_template :
# this is a message found in the message_template
2009-03-26 20:10:13 +00:00
2009-06-05 21:02:59 +00:00
#self.current_template = self.template_dict.get_template(message['message'])
new_message = Message ( message [ ' message ' ] )
new_message . event_queue_id = self . last_id
new_message . host = self . region . host
2009-02-11 22:36:48 +00:00
2009-03-12 20:48:26 +00:00
for block_name in message [ ' body ' ] :
2009-02-11 22:36:48 +00:00
2009-06-05 21:02:59 +00:00
# block_data keys off of block.name, which here is the body attribute
2009-07-15 17:25:34 +00:00
block = Block ( block_name )
2009-02-11 22:36:48 +00:00
2009-07-15 17:25:34 +00:00
new_message . add_block ( block )
2009-02-11 22:36:48 +00:00
2009-06-05 21:02:59 +00:00
for items in message [ ' body ' ] [ block_name ] :
2009-02-11 22:36:48 +00:00
2009-03-12 20:48:26 +00:00
for variable in items :
2009-07-15 17:25:34 +00:00
var_data = Variable ( variable , items [ variable ] , - 1 )
block . add_variable ( var_data )
2009-03-12 20:48:26 +00:00
2009-06-05 21:02:59 +00:00
#packet.blocks = message.blocks
messages . append ( new_message )
2009-03-12 20:48:26 +00:00
2009-06-05 21:02:59 +00:00
else :
2009-03-13 22:09:43 +00:00
2009-06-05 21:02:59 +00:00
# this is e.g. EstablishAgentCommunication or ChatterBoxInvitation, etc
2009-03-13 22:09:43 +00:00
2009-06-05 21:02:59 +00:00
new_message = Message ( message [ ' message ' ] )
new_message . event_queue_id = self . last_id
new_message . host = self . region . host
2009-03-12 20:48:26 +00:00
2009-07-15 17:25:34 +00:00
# faux block with a name of Message_Data
block = Block ( ' Message_Data ' )
new_message . add_block ( block )
2009-06-05 21:02:59 +00:00
for var in message [ ' body ' ] :
2009-03-12 20:48:26 +00:00
2009-07-15 17:25:34 +00:00
var_data = Variable ( var , message [ ' body ' ] [ var ] , - 1 )
block . add_variable ( var_data )
2009-06-05 21:02:59 +00:00
messages . append ( new_message )
2009-03-12 20:48:26 +00:00
2009-06-05 21:02:59 +00:00
return messages
2009-04-27 22:48:01 +00:00
"""
Contributors can be viewed at :
http : / / svn . secondlife . com / svn / linden / projects / 2008 / pyogp / CONTRIBUTORS . txt
2009-04-01 23:49:16 +00:00
2009-04-27 22:48:01 +00:00
$ LicenseInfo : firstyear = 2008 & license = apachev2 $
2009-04-01 23:49:16 +00:00
2009-04-27 22:48:01 +00:00
Copyright 2009 , Linden Research , Inc .
2009-04-01 23:49:16 +00:00
2009-04-27 22:48:01 +00:00
Licensed under the Apache License , Version 2.0 ( the " License " ) .
You may obtain a copy of the License at :
http : / / www . apache . org / licenses / LICENSE - 2.0
or in
http : / / svn . secondlife . com / svn / linden / projects / 2008 / pyogp / LICENSE . txt
2009-04-01 23:49:16 +00:00
2009-04-27 22:48:01 +00:00
$ / LicenseInfo $
"""
2009-04-01 23:49:16 +00:00