netmsgs  1.2.2
RoadNarrows Robotics Network Messaging Package
NetMsgsLibITV.py
Go to the documentation of this file.
1 ###############################################################################
2 #
3 # Package: NetMsgs
4 #
5 # File: NetMsgsLibITV.py
6 #
7 
8 """
9 NetMsgs Run-Time Library Packing and Unpacking ITV Module.
10 """
11 
12 ## \file
13 ## \package NetMsgs.NetMsgsLibITV
14 ##
15 ## $LastChangedDate: 2010-08-04 15:07:55 -0600 (Wed, 04 Aug 2010) $
16 ## $Rev: 550 $
17 ##
18 ## \brief NetMsgs Run-Time Library Packing and Unpacking ITV Module.
19 ##
20 ## The NetMsgsLibITV module defines the derived NetMsgsStreamBuf derived
21 ## run-time ITV class. The NetMsgsITV class provides all of the functionality
22 ## to pack, unpack, and trace messages encoded in the Identifier-Type-Value
23 ## message format.
24 ##
25 ## \sa
26 ## \htmlonly
27 ## <a href="../pydoc/NetMsgs.NetMsgsLibITV.html">PyDoc Generated Documentation</a>
28 ## \endhtmlonly
29 ##
30 ## \author Robin Knight (robin.knight@roadnarrows.com)
31 ##
32 ## \copyright
33 ## \h_copy 2010-2017. RoadNarrows LLC.\n
34 ## http://www.roadnarrows.com\n
35 ## All Rights Reserved
36 ##
37 
38 # Permission is hereby granted, without written agreement and without
39 # license or royalty fees, to use, copy, modify, and distribute this
40 # software and its documentation for any purpose, provided that
41 # (1) The above copyright notice and the following two paragraphs
42 # appear in all copies of the source code and (2) redistributions
43 # including binaries reproduces these notices in the supporting
44 # documentation. Substantial modifications to this software may be
45 # copyrighted by their authors and need not follow the licensing terms
46 # described here, provided that the new terms are clearly indicated in
47 # all files where they apply.
48 #
49 # IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
50 # OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
51 # PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
52 # DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
53 # EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
54 # THE POSSIBILITY OF SUCH DAMAGE.
55 #
56 # THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
57 # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
58 # FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
59 # "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
60 # PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
61 #
62 ###############################################################################
63 
64 import sys
65 
66 import NetMsgsBase as nmBase
67 from NetMsgsLib import *
68 import NetMsgsLibStreamBuf as nmStream
69 
70 
71 
72 #-----------------------------------------------------------------------------
73 # CLASS: NetMsgsITV
74 #-----------------------------------------------------------------------------
75 
76 class NetMsgsITV(nmStream.NetMsgsStreamBuf):
77  """ RoadNarrows Identifier-Type-Value encoded Net Messages Class.
78 
79  ITV message encoding provides field headers to identify and process
80  fields. Fields may be in any order. Unknown field are ignored.
81  """
82 
83  #--
84  def __init__(self, msgdefset, **kwargs):
85  """ Initialize NetMsgsITV instance.
86 
87  Parameters:
88  msgdefset - Set of message definitions.
89  kwargs - Optional keyword arguments. See NetMsgsStreamBuf.
90  """
91  kwargs['encoding'] = 'itv'
92  nmStream.NetMsgsStreamBuf.__init__(self, msgdefset, **kwargs)
93  ##
94 
95 
96  # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
97  # Derived State Functions
98  # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
99 
100  #--
101  def StateNew(self, msgid, op, **kwargs):
102  """ Create a new packing/unpacking operational state.
103 
104  The operational state contains message information plus a stack of
105  current field processing states. Each field state has header,
106  control, run-time determined values, and tracing parameters.
107 
108  Parameters:
109  msgid - Message id.
110  op - Operation string. One of: 'pack', 'unpack'.
111  kwargs - Optional initial state overrides and implementation
112  specifics.
113 
114  Return:
115  State id which is a key into the specific state.
116  """
117  stateId = nmStream.NetMsgsStreamBuf.StateNew(self, msgid, op, **kwargs)
118  self.StateFieldSet(stateId, 'fctl', has_hdr=True)
119  return stateId
120  ##
121 
122 
123  # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
124  # Derived Packing Functions
125  # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
126 
127  #--
128  def nmPackFieldHdr(self, fielddef, val, stateId):
129  """ Pack ITV field header.
130 
131  Parameters:
132  fielddef - Field definition.
133  val - Field value(s).
134  stateId - Packing state id.
135 
136  Return:
137  Packed buffer.
138  """
139  fhdr = {}
140  fstate = self.StateFieldGet(stateId)
141  fid = fstate['fid']
142  ftype = fstate['ftype']
143  self.ChkReqFValType(fielddef, val, stateId=stateId)
144  if not fstate['fctl']['has_hdr']: # don't pack header (vector item)
145  count = None
146  buf = ''
147  elif ftype == 's': # string compound field type
148  count = len(val)
149  buf = PackU8(fid, endian=self.mEndian)
150  buf += PackU8(ftype, endian=self.mEndian)
151  buf += PackU8(count, endian=self.mEndian)
152  fhdr['fid'] = fid
153  fhdr['ftype'] = ftype
154  fhdr['count'] = count
155  elif ftype == '{': # struct compound field type
156  msgdef = fielddef['msgdef']
157  count = msgdef['max_count']
158  buf = PackU8(fid, endian=self.mEndian)
159  buf += PackU8(ftype, endian=self.mEndian)
160  buf += PackU8(count, endian=self.mEndian)
161  fhdr['fid'] = fid
162  fhdr['ftype'] = ftype
163  fhdr['count'] = count
164  elif ftype == '[': # vector compound field type
165  vdef = fielddef['vdef']
166  vtype = vdef['ftype']
167  count = len(val)
168  buf = PackU8(fid, endian=self.mEndian)
169  buf += PackU8(ftype, endian=self.mEndian)
170  buf += PackU8(count, endian=self.mEndian)
171  buf += PackU8(vtype, endian=self.mEndian)
172  fhdr['fid'] = fid
173  fhdr['ftype'] = ftype
174  fhdr['count'] = count
175  fhdr['vtype'] = vtype
176  else: # simple type
177  count = None
178  buf = PackU8(fid, endian=self.mEndian)
179  buf += PackU8(ftype, endian=self.mEndian)
180  fhdr['fid'] = fid
181  fhdr['ftype'] = ftype
182  fhdr['fhdr_size'] = len(buf)
183  self.StateFieldSet(stateId, fhdr=fhdr)
184  return buf
185  ##
186 
187  #--
188  def nmPackString(self, fielddef, val, stateId):
189  """ Pack variable length string field.
190 
191  Parameters:
192  fielddef - Field definition.
193  val - Field value.
194  stateId - Packing state id.
195 
196  Return:
197  Packed buffer.
198  """
199  self.ChkReqFValType(fielddef, val)
200  count = len(val)
201  max_count = fielddef.get('max_count', nmBase.NMStringMaxCount)
202  if count > max_count:
203  self.Error("%s" % fielddef['name'],
204  "%s %u > %u" % (EMsgStringRange, count, max_count),
205  stateId=stateId)
206  self.StateFieldSet(stateId, count=count)
207  buf = self.nmPackFieldHdr(fielddef, val, stateId)
208  buf += PackString(val, count)
209  if self.mTrace: self.TraceField(fielddef, val, buf, stateId)
210  return buf
211  ##
212 
213  #--
214  def nmPackVector(self, fielddef, vallist, stateId):
215  """ Pack variable vector field.
216 
217  Parameters:
218  fielddef - Field definition.
219  vallist - Vector (list) of vector item values.
220  stateId - Packing state id.
221 
222  Return:
223  Packed buffer.
224  """
225  self.ChkReqFValType(fielddef, vallist)
226  vdef = fielddef['vdef']
227  vname = vdef['name']
228  vtype = vdef['ftype']
229  count = len(vallist)
230  max_count = fielddef['max_count']
231  if count > max_count:
232  self.Error("%s" % fielddef['name'],
233  "%s %u > %u" % (EMsgVectorRange, count, max_count),
234  stateId=stateId)
235  self.StateFieldSet(stateId, count=count)
236  buf = self.nmPackFieldHdr(fielddef, vallist, stateId)
237  if self.mTrace: self.TraceField(fielddef, vallist, buf, stateId)
238  setfunc = self.mFuncMap[vtype][self.SETFUNC]
239  packfunc = self.mFuncMap[vtype][self.PACKFUNC]
240  i = 0
241  self.StateFieldPush(stateId, fname=vname, fid=0, ftype=vtype)
242  if vtype in nmBase.NMFTypeCodeSimple:
243  self.StateFieldSet(stateId, 'fctl', has_hdr=False)
244  for val in vallist:
245  self.StateFieldSet(stateId, i=i)
246  self.StateFieldSet(stateId, fid=i+1)
247  val = setfunc(vdef, val, stateId)
248  buf += packfunc(vdef, val, stateId)
249  i += 1
250  self.StateFieldPop(stateId)
251  return buf
252  ##
253 
254  #--
255  def nmPackMsgHdr(self, msgid, msgdef, stateId):
256  """ Pack message header.
257 
258  Parameters:
259  msgid - Message identifier.
260  msgdef - Message definition.
261  stateId - Packing state id.
262 
263  Return:
264  Packed buffer.
265  """
266  count = msgdef['max_count']
267  buf = PackU16(msgid, endian=self.mEndian)
268  buf += PackU8(count, endian=self.mEndian)
269  msghdr = {'msghdr_size':len(buf), 'msgid':msgid, 'count':count}
270  self.StateSet(stateId, msghdr=msghdr)
271  return buf
272  ##
273 
274 
275  # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
276  # Virtual Unpacking Functions
277  # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
278 
279  #--
280  def nmUnpackFieldHdr(self, buf, offset, stateId):
281  """ Unpack ITV field header.
282 
283  Parameters:
284  buf - Buffer to unpack.
285  offset - Buffer offset where unpacking begins.
286  stateId - Unpacking state id.
287 
288  Return:
289  New buffer offset.
290  """
291  fhdr = {}
292  fstate = self.StateFieldGet(stateId)
293  start = offset
294  if self.StateFieldGet(stateId, 'fctl', 'has_hdr'):
295  # common header part
296  fid, offset = UnpackU8(buf, offset, endian=self.mEndian)
297  ftype, offset = UnpackU8(buf, offset, endian=self.mEndian)
298  ftype = "%c" % ftype
299  fhdr['fid'] = fid
300  fhdr['ftype'] = ftype
301  if ftype == 's': # string compound field type
302  fhdr['count'], offset = UnpackU8(buf, offset, endian=self.mEndian)
303  elif ftype == '{': # struct compound field type
304  fhdr['count'], offset = UnpackU8(buf, offset, endian=self.mEndian)
305  elif ftype == '[': # vector compound field type
306  fhdr['count'], offset = UnpackU8(buf, offset, endian=self.mEndian)
307  vtype, offset = UnpackU8(buf, offset, endian=self.mEndian)
308  fhdr['vtype'] = "%c" % vtype
309  fhdr['fhdr_size'] = offset - start
310  self.StateFieldSet(stateId, fhdr=fhdr)
311  return offset
312  ##
313 
314  #--
315  def nmUnpackPad(self, fielddef, buf, offset, fvals, stateId):
316  """ Unpack variable number of pad bytes from the buffer.
317 
318  No field values are set.
319 
320  Parameters:
321  fielddef - Field definition.
322  buf - Buffer to unpack.
323  offset - Buffer offset where unpacking begins.
324  fvals - Dictionary to hold unpacked field values.
325  stateId - Unpacking state id.
326 
327  Return:
328  New buffer offset.
329  """
330  count = 0
331  while (ord(buf[offset]) == nmBase.NMPadFVal) and (offset < len(buf)):
332  offset += 1
333  count += 1
334  self.StateFieldSet(stateId, count=count)
335  self.StateFieldSet(stateId, 'trace', trace_end=offset)
336  if self.mTrace: self.TraceField(fielddef, nmBase.NMPadFVal, buf, stateId)
337  return offset
338  ##
339 
340  #--
341  def nmUnpackString(self, fielddef, buf, offset, fvals, stateId):
342  """ Unpack variable lenght string field from the buffer.
343 
344  Parameters:
345  fielddef - Field definition.
346  buf - Buffer to unpack.
347  offset - Buffer offset where unpacking begins.
348  fvals - Dictionary to hold unpacked field values.
349  stateId - Unpacking state id.
350 
351  Return:
352  New buffer offset.
353  """
354  fname = fielddef['name']
355  count = self.StateFieldGet(stateId, 'fhdr', 'count')
356  max_count = fielddef['max_count']
357  if count > max_count:
358  self.Error("%s" % fielddef['name'],
359  "%s %u > %u" % (EMsgStringRange, count, max_count),
360  stateId=stateId)
361  val, offset = UnpackString(buf, count, offset=offset)
362  if not self.StateFieldGet(stateId, 'fctl', 'noexec'):
363  fvals[fname] = val
364  self.StateFieldSet(stateId, count=count)
365  self.StateFieldSet(stateId, 'trace', trace_end=offset)
366  if self.mTrace: self.TraceField(fielddef, val, buf, stateId)
367  return offset
368  ##
369 
370  #--
371  def nmUnpackStruct(self, fielddef, buf, offset, fvals, stateId):
372  """ Unpack structure field from the buffer.
373 
374  Parameters:
375  fielddef - Field definition.
376  buf - Buffer to unpack.
377  offset - Buffer offset where unpacking begins.
378  fvals - Dictionary to hold unpacked field values.
379  stateId - Unpacking state id.
380 
381  Return:
382  New buffer offset.
383  """
384  fname = fielddef['name']
385  msgdef = fielddef['msgdef']
386  max_count = msgdef['max_count']
387  fhdr = self.StateFieldGet(stateId, 'fhdr')
388  count = fhdr['count']
389  if count > max_count:
390  self.Error("%s" % fielddef['name'],
391  "%s %u > %u" % (EMsgStructRange, count, max_count),
392  stateId=stateId)
393  self.StateFieldSet(stateId, count=count)
394  self.StateFieldSet(stateId, 'trace', trace_end=offset)
395  if not self.StateFieldGet(stateId, 'fctl', 'noexec'):
396  fvals[fname] = {}
397  val = fvals[fname]
398  else:
399  val = {}
400  if self.mTrace: self.TraceField(fielddef, val, buf, stateId)
401  return self.nmUnpackStream(msgdef, buf, offset, val, stateId)
402  ##
403 
404  #--
405  def nmUnpackVector(self, fielddef, buf, offset, fvals, stateId):
406  """ Unpack variable vector field from the buffer.
407 
408  Parameters:
409  fielddef - Field definition.
410  buf - Buffer to unpack.
411  offset - Buffer offset where unpacking begins.
412  fvals - Dictionary to hold unpacked field values.
413  stateId - Unpacking state id.
414 
415  Return:
416  New buffer offset.
417  """
418  fname = fielddef['name']
419  vdef = fielddef['vdef']
420  vname = vdef['name']
421  vtype = vdef['ftype']
422  max_count = fielddef['max_count']
423  fhdr = self.StateFieldGet(stateId, 'fhdr')
424  count = fhdr['count']
425  if vtype != fhdr['vtype']:
426  self.Error("%s" % fielddef['name'], EMsgBadMsg,
427  "unpacked vtype=%c != expected vtype=%c" % (fhdr['vtype'], vtype),
428  stateId=stateId)
429  if count > max_count:
430  self.Error("%s" % fielddef['name'],
431  "%s %u > %u" % (EMsgVectorRange, count, max_count),
432  stateId=stateId)
433  self.StateFieldSet(stateId, count=count)
434  self.StateFieldSet(stateId, 'trace', trace_end=offset)
435  if not self.StateFieldGet(stateId, 'fctl', 'noexec'):
436  fvals[fname] = []
437  val = fvals[fname]
438  else:
439  val = []
440  if self.mTrace: self.TraceField(fielddef, val, buf, stateId)
441  if vtype in nmBase.NMFTypeCodeSimple:
442  self.StateFieldSet(stateId, 'fctl', has_hdr=False)
443  else:
444  self.StateFieldSet(stateId, 'fctl', has_hdr=True)
445  unpackfunc = self.mFuncMap[vtype][self.UNPACKFUNC]
446  vval = {}
447  i = 0
448  self.StateFieldPush(stateId, fname=vname, fid=0, ftype=vtype)
449  while i < count:
450  self.StateFieldSet(stateId, 'trace', trace_start=offset)
451  self.StateFieldSet(stateId, i=i)
452  offset = self.nmUnpackFieldHdr(buf, offset, stateId)
453  offset = unpackfunc(vdef, buf, offset, vval, stateId)
454  val += [vval[vname]]
455  i += 1
456  vval = {}
457  self.StateFieldPop(stateId)
458  return offset
459  ##
460 
461  #--
462  def nmUnpackStream(self, msgdef, buf, offset, fvals, stateId):
463  """ Unpack a field stream from the buffer.
464 
465  Parameters:
466  msgdef - Message definition.
467  buf - Buffer to unpack.
468  offset - Buffer offset where unpacking begins.
469  stateId - Unpacking state id.
470 
471  Return:
472  New buffer offset.
473  """
474  msgname = msgdef['name']
475  fdeflist = msgdef['fielddef']
476  max_count = msgdef['max_count']
477  count = self.StateFieldGet(stateId, 'count')
478  if count > max_count:
479  self.Error("%s" % fielddef['name'],
480  "%s %u > %u" % (EMsgStructRange, count, max_count),
481  stateId=stateId)
482  i = 0
483  while i < count:
484  self.StateFieldPush(stateId)
485  self.StateFieldSet(stateId, 'trace', trace_start=offset)
486  if ord(buf[offset]) == nmBase.NMPadFVal:
487  fid = 0
488  ftype = nmBase.NMFCode('pad')
489  fielddef = {'name':'pad', 'fid':0, 'ftype':ftype}
490  else:
491  offset = self.nmUnpackFieldHdr(buf, offset, stateId)
492  fid = self.StateFieldGet(stateId, 'fhdr', 'fid')
493  ftype = self.StateFieldGet(stateId, 'fhdr', 'ftype')
494  fielddef = self.FindFId(msgdef, fid)
495  if fielddef:
496  fname = fielddef['name']
497  fid = fielddef['fid']
498  if ftype != fielddef['ftype']:
499  self.Error("%s" % fname,
500  "%s received '%s' != expected '%s'" % \
501  (EMsgBadMsg, ftype, fielddef['ftype']),
502  stateId=stateId)
503  self.StateFieldSet(stateId, fname=fname, fid=fid, ftype=ftype)
504  unpackfunc = self.mFuncMap[ftype][self.UNPACKFUNC]
505  offset = unpackfunc(fielddef, buf, offset, fvals, stateId)
506  else:
507  self.Warning("fid=%u" % (fid), "Field not defined - ignoring")
508  self.StateFieldSet(stateId, 'fctl', noexec=True)
509  unpackfunc = self.mFuncMap[ftype][self.UNPACKFUNC]
510  offset = unpackfunc(fielddef, buf, offset, fvals, stateId)
511  self.StateFieldPop(stateId)
512  i += 1
513  return offset
514  ##
515 
516  #--
517  def nmUnpackMsgHdr(self, msgid, msgdef, buf, offset, fvals, stateId):
518  """ Unpack ITV message header.
519 
520  Parameters:
521  msgid - Message identifier.
522  msgdef - Message definition.
523  buf - Buffer to unpack.
524  offset - Buffer offset where unpacking begins.
525  fvals - Dictionary to hold unpacked field values.
526  stateId - Unpacking state id.
527 
528  Return:
529  New buffer offset.
530  """
531  msghdr = {}
532  start = offset
533  msghdr['msgid'], offset = UnpackU16(buf, offset, endian=self.mEndian)
534  msghdr['count'], offset = UnpackU8(buf, offset, endian=self.mEndian)
535  msghdr['msghdr_size'] = offset - start
536  if msgid != msghdr['msgid']:
537  self.Error("%s" % msgdef['name'], EMsgBadMsg,
538  "unpacked msgid=%u != expected msgid=%u" % (msghdr['msgid'], msgid),
539  stateId=stateId)
540  count = msghdr['count']
541  max_count = msgdef['max_count']
542  if count > max_count:
543  self.Error("%s" % msgdef['name'],
544  "%s %u > %u" % (EMsgStructRange, count, max_count),
545  stateId=stateId)
546  self.StateSet(stateId, msghdr=msghdr)
547  self.StateFieldSet(stateId, count=count)
548  return offset
549  ##
550 ##
def nmUnpackMsgHdr(self, msgid, msgdef, buf, offset, fvals, stateId)
def UnpackU8(buf, offset=0, endian='big')
Definition: NetMsgsLib.py:600
def nmUnpackStream(self, msgdef, buf, offset, fvals, stateId)
def PackU16(val, endian='big')
Definition: NetMsgsLib.py:267
def UnpackString(buf, count, offset=0)
Definition: NetMsgsLib.py:889
def nmUnpackFieldHdr(self, buf, offset, stateId)
def nmUnpackPad(self, fielddef, buf, offset, fvals, stateId)
def nmPackVector(self, fielddef, vallist, stateId)
def nmUnpackVector(self, fielddef, buf, offset, fvals, stateId)
def nmPackString(self, fielddef, val, stateId)
def UnpackU16(buf, offset=0, endian='big')
Definition: NetMsgsLib.py:638
def nmPackMsgHdr(self, msgid, msgdef, stateId)
def nmUnpackString(self, fielddef, buf, offset, fvals, stateId)
def nmUnpackStruct(self, fielddef, buf, offset, fvals, stateId)
def PackU8(val, endian='big')
Definition: NetMsgsLib.py:229
def __init__(self, msgdefset, kwargs)
def PackString(val, count=None)
Definition: NetMsgsLib.py:493
def nmPackFieldHdr(self, fielddef, val, stateId)
def StateNew(self, msgid, op, kwargs)