root/projects/2008/pyogp/pyogp.lib.base/trunk/pyogp/lib/base/utilities/helpers.py

Revision 2493, 8.4 kB (checked in by joshua.linden, 5 months ago)

* Use the PCode enum instead of magic numbers
* Watch out for "None" values when applying ObjectUpdates? to agent
* Add AgentDynamicsUpdate? app event
* Handle 48-, 32-, and 16-byte ObjectUpdates?
* Add basic sit/stand/fly methods to agent, with associated enums

Not reviewed

Line 
1 # standard python libs
2 from logging import getLogger, CRITICAL, ERROR, WARNING, INFO, DEBUG
3 import time
4 import struct
5 import math
6
7 # related
8 from indra.base import llsd
9 from eventlet import api
10
11 # pyogp
12 from pyogp.lib.base.exc import DataParsingError, DeserializationFailed
13
14 # initialize loggin
15 logger = getLogger('...utilities.helpers')
16 log = logger.log
17
18
19
20
21 class Helpers(object):
22     """ contains useful helper functions """
23
24     @staticmethod
25     def bytes_to_hex(data):
26         """ converts bytes to hex format """
27
28         #from binascii import hexlify
29         #return hex_string
30         #hex_string = hexlify(data)
31         return ''.join(["%02X " % ord(x) for x in data]).strip()
32
33     @staticmethod
34     def bytes_to_ascii(data):
35         " converts bytes to ascii format "
36
37         from binascii import b2a_uu
38
39         ascii_string = b2a_uu(data)
40
41         return ascii_string
42
43     @staticmethod
44     def hex_to_ascii(data):
45         " converts bytes to ascii format "
46
47         from binascii import unhexlify
48
49         try:
50             ascii_string = unhexlify(data)
51         except TypeError, error:
52             raise DataParsingError('hex_to_ascii failure: \'%s\': processing data: \'%s\'' % (error, data))
53
54         return ascii_string
55
56     @staticmethod
57     def bytes_to_base64(data):
58         " converts bytes to ascii format "
59
60         from binascii import b2a_base64
61
62         base64_string = b2a_base64(data)
63
64         return base64_string
65
66     @staticmethod
67     def packed_u16_to_float(bytes, offset, lower, upper):
68         """ Extract float packed as u16 in a byte buffer """
69
70         U16MAX = 65535
71         OOU16MAX = 1.0/U16MAX
72
73         u16 = struct.unpack('<H', bytes[offset:offset+2])[0]
74         val = u16 * OOU16MAX
75         delta = upper - lower
76         val *= delta
77         val += lower
78
79         max_error = delta * OOU16MAX
80         if math.fabs(val) < max_error:
81             val = 0.0
82
83         return val
84
85     @staticmethod
86     def packed_u8_to_float(bytes, offset, lower, upper):
87         """ Extract float packed as u8 in a byte buffer """
88
89         U8MAX = 255
90         OOU8MAX = 1.0/U8MAX
91
92         u8 = struct.unpack('<B', bytes[offset:offset+1])[0]
93         val = u8 * OOU8MAX
94         delta = upper - lower
95         val *= delta
96         val += lower
97
98         max_error = delta * OOU8MAX
99         if math.fabs(val) < max_error:
100             val = 0.0
101
102         return val
103    
104
105     @staticmethod
106     def pack_quaternion_to_vector3(quaternion):
107         """ pack a normalized quaternion (tuple) into a vector3 (tuple) """
108         if quaternion[3] >= 0:
109             return (quaternion[0], quaternion[1], quaternion[2])
110         else:
111             return (-quaternion[0], -quaternion[1], -quaternion[2])       
112
113
114     @staticmethod
115     def int_to_bytes(data):
116         """
117         converts an int to a string of bytes
118         """
119         return struct.pack('BBBB',
120                            data % 256,
121                            (data >> 8) % 256,
122                            (data >> 16) % 256,
123                            (data >> 24) % 256)
124
125     # ~~~~~~~~~
126     # Callbacks
127     # ~~~~~~~~~
128
129     @staticmethod
130     def log_packet(packet, _object):
131         """ default logging function for packets  """
132
133         log(INFO, "Object %s is monitoring packet type %s: \n%s" % (type(_object), packet.name, packet.data()))
134
135     @staticmethod
136     def log_event_queue_data(data, _object):
137         """ default logging function for event queue data events  """
138
139         log(INFO, "Object %s is monitoring event queue data event %s: \n%s" % (type(_object), data.name, data.__dict__))
140
141     @staticmethod
142     def null_packet_handler(packet, _object):
143         """ just a null event handler for watching aka fully parsing specific packets """
144
145         pass
146
147 class ListLLSDSerializer(object):
148     """adapter for serializing a list to LLSD
149
150     An example:
151     >>> d=['ChatSessionRequest', 'CopyInventoryFromNotecard']
152     >>> serializer = ListLLSDSerializer(d)
153     >>> serializer.serialize()
154     '<?xml version="1.0" ?><llsd><array><string>ChatSessionRequest</string><string>CopyInventoryFromNotecard</string></array></llsd>'
155     >>> serializer.content_type
156     'application/llsd+xml'
157
158     """
159
160     def __init__(self, context):
161         self.context = context
162
163     def serialize(self):
164         """convert the payload to LLSD"""
165         return llsd.format_xml(self.context)
166
167     @property
168     def content_type(self):
169         """return the content type of this serializer"""
170         return "application/llsd+xml"
171
172
173 class DictLLSDSerializer(object):
174     """adapter for serializing a dictionary to LLSD
175
176     An example:
177     >>> d={'foo':'bar', 'test':1234}
178     >>> serializer = DictLLSDSerializer(d)
179     >>> serializer.serialize()
180     '<?xml version="1.0" ?><llsd><map><key>test</key><integer>1234</integer><key>foo</key><string>bar</string></map></llsd>'
181     >>> serializer.content_type
182     'application/llsd+xml'
183
184     """
185
186     def __init__(self, context):
187         self.context = context
188
189     def serialize(self):
190         """convert the payload to LLSD"""
191         return llsd.format_xml(self.context)
192
193     @property
194     def content_type(self):
195         """return the content type of this serializer"""
196         return "application/llsd+xml"
197
198 class LLSDDeserializer(object):
199     """utility for deserializing LLSD data
200
201     The deserialization component is defined as a utility because the input
202     data can be a string or a file. It might be possible to define this as
203     an adapter on a string but a string is too generic for this. So that's
204     why it is a utility.
205
206     You can use it like this:
207
208     >>> s='<?xml version="1.0" ?><llsd><map><key>test</key><integer>1234</integer><key>foo</key><string>bar</string></map></llsd>'
209
210     We use queryUtility because this returns None instead of an exception
211     when a utility is not registered. We use the content type we received
212     as the name of the utility. Another option would of course be to subclas
213     string to some LLSDString class and use an adapter. We then would need some
214     factory for generating the LLSDString class from whatever came back from
215     the HTTP call.
216
217     So here is how you use that utility:
218     >>> deserializer = LLSDDeserializer()
219     >>> llsd = deserializer.deserialize(s)
220     >>> llsd
221     {'test': 1234, 'foo': 'bar'}
222
223     We can also test this with some non-LLSD string:
224
225     >>> llsd = deserializer.deserialize_string('mumpitz')   # this is not LLSD
226     Traceback (most recent call last):
227     ...
228     DeserializationFailed: deserialization failed for 'mumpitz', reason: 'invalid token at index 0: 109'
229
230     >>> llsd = deserializer.deserialize_string('barfoo')
231     Traceback (most recent call last):
232     ...
233     DeserializationFailed: deserialization failed for 'barfoo', reason: 'binary notation not yet supported'
234
235
236     """
237
238     def deserialize(self, data):
239         """ convenience class to handle a variety of inputs """
240
241         if type(data) == str:
242             return self.deserialize_string(data)
243         # won't handle another case until we need to
244
245     def deserialize_string(self, data):
246         """ deserialize a string """
247
248         try:
249             r = llsd.parse(data)
250         except llsd.LLSDParseError, e:
251             raise DeserializationFailed(data, str(e))
252         if r==False:
253             raise DeserializationFailed(data, 'result was False')
254         return r
255
256     def deserialize_file(self, fp):
257         """ deserialize a file """
258         data = fp.read()
259         return self.deserialize_string(data)
260
261 class Wait(object):
262     """ a simple timer that blocks a calling routine for the specified number of seconds
263
264     done since we were writing timing loops in test scripts repeatedly
265     returns True when it's done
266      """
267
268     def __init__(self, duration):
269
270         self.duration = int(duration)
271
272         # let's be nice and enabled a kill switch
273         self.enabled = False
274
275         self.run()
276
277     def run(self):
278
279         now = time.time()
280         start = now
281         self.enabled = True
282
283         while self.enabled and now - start < self.duration:
284
285             api.sleep()
286             now = time.time()
287
288         return True
289
290     def stop(self):
291
292         self.enabled = False
293
294 """
295 Contributors can be viewed at:
296 http://svn.secondlife.com/svn/linden/projects/2008/pyogp/CONTRIBUTORS.txt
297
298 $LicenseInfo:firstyear=2008&license=apachev2$
299
300 Copyright 2009, Linden Research, Inc.
301
302 Licensed under the Apache License, Version 2.0 (the "License").
303 You may obtain a copy of the License at:
304     http://www.apache.org/licenses/LICENSE-2.0
305 or in
306     http://svn.secondlife.com/svn/linden/projects/2008/pyogp/LICENSE.txt
307
308 $/LicenseInfo$
309 """
310
Note: See TracBrowser for help on using the browser.