1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 from __future__ import print_function
34
35 import collections
36 import copy
37 try:
38 from cStringIO import StringIO
39 except ImportError:
40 from io import BytesIO as StringIO
41 import inspect
42 import itertools
43 import os
44 import string
45 import sys
46
47 import genmsg.msgs
48 import genpy
49 import genpy.dynamic
50
51 import rospkg
52
53 import rosbag
54
55
56
57
60
62 """
63 Check whether a bag file can be played in the current system.
64 @param migrator: message migrator to use
65 @param inbag name of the bag to be checked.
66 @returns A list of tuples for each type in the bag file. The first
67 element of each tuple is the full migration path for the type. The
68 second element of the tuple is the expanded list of invalid rules
69 for that particular path.
70 """
71 checked = set()
72 migrations = []
73
74 bag = rosbag.Bag(inbag, 'r')
75
76 for topic, msg, t in bag.read_messages(raw=True):
77 key = get_message_key(msg[4])
78 if key not in checked:
79 target = migrator.find_target(msg[4])
80
81
82 path = migrator.find_path(msg[4], target)
83 if len(path) > 0:
84 migrations.append((path, [r for r in migrator.expand_rules([sn.rule for sn in path]) if r.valid == False]))
85
86 checked.add(key)
87
88 bag.close()
89
90 return migrations
91
93 """
94 Check whether a bag file can be played in the current system.
95 @param migrator The message migrator to use
96 @param message_list A list of message classes.
97 @returns A list of tuples for each type in the bag file. The first
98 element of each tuple is the full migration path for the type. The
99 second element of the tuple is the expanded list of invalid rules
100 for that particular path.
101 """
102
103 checked = set()
104 migrations = []
105
106 for msg in messages:
107 key = get_message_key(msg)
108 if key not in checked:
109 target = migrator.find_target(msg)
110
111
112 path = migrator.find_path(msg, target)
113 if len(path) > 0:
114 migrations.append((path, [r for r in migrator.expand_rules([sn.rule for sn in path]) if r.valid == False]))
115
116 checked.add(key)
117
118 return migrations
119
120
121
122
123
124
125
126 -def fixbag(migrator, inbag, outbag):
127
128 res = checkbag(migrator, inbag)
129
130
131 if not False in [m[1] == [] for m in res]:
132 bag = rosbag.Bag(inbag, 'r')
133 rebag = rosbag.Bag(outbag, 'w', options=bag.options)
134 for topic, msg, t in bag.read_messages(raw=True):
135 new_msg_type = migrator.find_target(msg[4])
136 mig_msg = migrator.migrate_raw(msg, (new_msg_type._type, None, new_msg_type._md5sum, None, new_msg_type))
137 rebag.write(topic, mig_msg, t, raw=True)
138 rebag.close()
139 bag.close()
140 return True
141 else:
142 return False
143
144
145
146
147
148
149
150 -def fixbag2(migrator, inbag, outbag, force=False):
151
152 res = checkbag(migrator, inbag)
153
154 migrations = [m for m in res if len(m[1]) > 0]
155
156
157 if len(migrations) == 0 or force:
158 bag = rosbag.Bag(inbag, 'r')
159 rebag = rosbag.Bag(outbag, 'w', options=bag.options)
160 for topic, msg, t in bag.read_messages(raw=True):
161 new_msg_type = migrator.find_target(msg[4])
162 if new_msg_type != None:
163 mig_msg = migrator.migrate_raw(msg, (new_msg_type._type, None, new_msg_type._md5sum, None, new_msg_type))
164 rebag.write(topic, mig_msg, t, raw=True)
165 else:
166 rebag.write(topic, msg, t, raw=True)
167 rebag.close()
168 bag.close()
169
170 if force:
171 return []
172 else:
173 return migrations
174
175
176
177
178
179
180
181
182
183
184
186 name_split = name.split('/')
187 try:
188 name_split.remove('std_msgs')
189 except ValueError:
190 pass
191 try:
192 name_split.remove(top_name.split('/')[0])
193 except ValueError:
194 pass
195 new_name = '/'.join(name_split)
196 return new_name
197
198
199
200
201
202
203
204
205
206
208
209 tmp_name = clean_name(name, top_name)
210
211 if len(tmp_name.split('/')) == 2 or (genmsg.msgs.is_builtin(tmp_name)):
212 return tmp_name
213 elif tmp_name == 'Header':
214 return 'std_msgs/Header'
215 else:
216 return top_name.split('/')[0] + '/' + tmp_name
217
218
219
220
221
222
223
224
225
227 try:
228 return (c._type, c._md5sum)
229 except:
230 return None
231
232
233
234
235
236
237
238
239
240
241
247
248
250 old_type = ''
251 old_full_text = ''
252 new_type = ''
253 new_full_text = ''
254 migrated_types = []
255
256 order = -1
257
258 valid = False
259
260
261 - def __init__(self, migrator, location):
262
263 self.migrator = migrator
264 self.location = location
265
266 if (self.old_type != self.new_type):
267 self.rename_rule = True
268 else:
269 self.rename_rule = False
270
271
272 try:
273 if self.old_type == "":
274 raise Exception
275 self.old_types = genpy.dynamic.generate_dynamic(self.old_type, self.old_full_text)
276 self.old_class = self.old_types[self.old_type]
277 self.old_md5sum = self.old_class._md5sum
278 except:
279 self.old_types = []
280 self.old_class = None
281 self.old_md5sum = ""
282
283 try:
284 if self.new_type == "":
285 raise Exception
286 self.new_types = genpy.dynamic.generate_dynamic(self.new_type, self.new_full_text)
287 self.new_class = self.new_types[self.new_type]
288 self.new_md5sum = self.new_class._md5sum
289 except:
290 self.new_types = []
291 self.new_class = None
292 self.new_md5sum = ""
293
294
295
296 self.sub_rules_done = False
297 self.sub_rules_valid = False
298 self.sub_rules = []
299
300
301
302
303
304
305
306
307
308
310 self.sub_rules_valid = True
311 for (t1, t2) in self.migrated_types:
312 try:
313 tmp_old_class = self.get_old_class(t1)
314 except KeyError:
315 print("WARNING: Within rule [%s], specified migrated type [%s] not found in old message types" % (self.location, t1), file=sys.stderr)
316 self.sub_rules_valid = False
317 continue
318 try:
319 tmp_new_class = self.get_new_class(t2)
320 except KeyError:
321 print("WARNING: Within rule [%s], specified migrated type [%s] not found in new message types" % (self.location, t2), file=sys.stderr)
322 self.sub_rules_valid = False
323 continue
324
325
326
327
328 if (get_message_key(tmp_old_class) != get_message_key(self.old_class)) or (get_message_key(tmp_new_class) != get_message_key(self.new_class)):
329 path = self.migrator.find_path(tmp_old_class, tmp_new_class)
330 rules = [sn.rule for sn in path]
331 self.sub_rules.extend(rules)
332
333 if False in [r.valid for r in self.sub_rules]:
334 print("WARNING: Within rule [%s] cannot migrate from subtype [%s] to [%s].." % (self.location, t1, t2), file=sys.stderr)
335 self.sub_rules_valid = False
336 continue
337 self.sub_rules = self.migrator.filter_rules_unique(self.sub_rules)
338 self.sub_rules_done = True
339
340
341
342
343
344
345
347 try:
348 try:
349 return self.new_types[t]
350 except KeyError:
351 return self.new_types['std_msgs/' + t]
352 except KeyError:
353 return self.new_types[self.new_type.split('/')[0] + '/' + t]
354
355
356
357
358
359
360
362 try:
363 try:
364 return self.old_types[t]
365 except KeyError:
366 return self.old_types['std_msgs/' + t]
367 except KeyError:
368 return self.old_types[self.old_type.split('/')[0] + '/' + t]
369
370
371
372
373
374
375
376 - def migrate(self, msg_from, msg_to):
377 tmp_msg_from = clean_name(msg_from._type, self.old_type)
378 tmp_msg_to = clean_name(msg_to._type, self.new_type)
379 if (tmp_msg_from, tmp_msg_to) not in self.migrated_types:
380 raise BagMigrationException("Rule [%s] tried to perform a migration from old [%s] to new [%s] not listed in migrated_types"%(self.location, tmp_msg_from, tmp_msg_to))
381 self.migrator.migrate(msg_from, msg_to)
382
383
384
385
386
387
388
389
390 - def migrate_array(self, msg_from_array, msg_to_array, msg_to_name):
391 msg_to_class = self.get_new_class(msg_to_name)
392
393 while len(msg_to_array) > 0:
394 msg_to_array.pop()
395
396 if (len(msg_from_array) == 0):
397 return
398
399 tmp_msg_from = clean_name(msg_from_array[0]._type, self.old_type)
400 tmp_msg_to = clean_name(msg_to_class._type, self.new_type)
401 if (tmp_msg_from, tmp_msg_to) not in self.migrated_types:
402 raise BagMigrationException("Rule [%s] tried to perform a migration from old [%s] to new [%s] not listed in migrated_types"%(self.location, tmp_msg_from, tmp_msg_to))
403
404 msg_to_array.extend( [msg_to_class() for i in range(len(msg_from_array))] )
405
406 self.migrator.migrate_array(msg_from_array, msg_to_array)
407
408
411
412
413
414
415
416 - def apply(self, old_msg):
431
432
433
434
435
436 - def update(self, old_msg, new_msg):
438
439
440
441
442
443
444
445
446
449 self.chain = []
450 self.order_keys = set()
451 self.rename = None
452
453
454
455
456
457
458
459
460
462 - def __init__(self, old_class, new_class, rule):
463 self.old_class = old_class
464 self.new_class = new_class
465 self.rule = rule
466 self.next = None
467
468
469
470
471
473 - def __init__(self, input_rule_files=[], plugins=True):
474
475
476
477
478
479
480
481 self.rulechains = collections.defaultdict(RuleChain)
482
483
484
485 self.base_nodes = []
486
487
488
489
490 self.extra_nodes = []
491
492
493 self.first_type = {}
494
495
496
497
498 self.rename_map = {}
499
500
501
502 self.found_paths = {}
503 self.found_targets = {}
504
505
506 terminal_nodes = []
507
508
509 rule_dicts = []
510
511 self.false_rule_loaded = False
512
513
514
515 for r in input_rule_files:
516 try:
517 scratch_locals = {'MessageUpdateRule':MessageUpdateRule}
518 with open(r, 'r') as f:
519 exec(f.read(), scratch_locals)
520 rule_dicts.append((scratch_locals, r))
521 except:
522 print("Cannot load rule file [%s] in local package" % r, file=sys.stderr)
523
524
525
526 if plugins:
527 rospack = rospkg.RosPack()
528 for dep,export in [('rosbagmigration','rule_file'),('rosbag','migration_rule_file'),('rosbag_migration_rule','rule_file')]:
529 for pkg in rospack.get_depends_on(dep, implicit=False):
530 m = rospack.get_manifest(pkg)
531 p_rules = m.get_export(dep,export)
532 pkg_dir = rospack.get_path(pkg)
533 for r in p_rules:
534 if dep == 'rosbagmigration':
535 print("""WARNING: The package: [%s] is using a deprecated rosbagmigration export.
536 The export in the manifest should be changed to:
537 <rosbag migration_rule_file="%s"/>
538 """ % (pkg, r), file=sys.stderr)
539 try:
540 scratch_locals = {'MessageUpdateRule':MessageUpdateRule}
541 exec(open(pkg_dir + "/" + r).read(), scratch_locals)
542 rule_dicts.append((scratch_locals, r))
543 except ImportError:
544 print("Cannot load rule file [%s] in package [%s]" % (r, pkg), file=sys.stderr)
545
546
547 for (rule_dict, location_base) in rule_dicts:
548 for (n,c) in rule_dict.items():
549 if inspect.isclass(c):
550 if (not c == MessageUpdateRule) and issubclass(c, MessageUpdateRule):
551 self.add_update_rule(c(self, location_base + ':' + n))
552
553 if self.false_rule_loaded:
554 raise BagMigrationException("Cannot instantiate MessageMigrator with invalid rules")
555
556
557
558
559
560
561
562
563
564
565
566 for (type,rulechain) in self.rulechains.items():
567 first = True
568 sn = None
569 prev_sn = None
570
571
572 rename_set = set([type])
573 tmp = rulechain.rename
574 while tmp:
575 rename_set.add(tmp.new_type)
576 if tmp.new_type in self.rulechains:
577 tmp = self.rulechains[tmp.new_type].rename
578 else:
579 break
580
581 self.rename_map[type] = rename_set
582
583
584 for r in rulechain.chain:
585
586 sn = ScaffoldNode(r.old_class, r.new_class, r)
587 self.base_nodes.append(sn)
588
589 if first:
590 self.first_type[type] = sn
591 first = False
592
593
594 if prev_sn:
595 if get_message_key(prev_sn.new_class) == get_message_key(sn.old_class):
596 prev_sn.next = sn
597 else:
598 implicit_sn = ScaffoldNode(prev_sn.new_class, sn.old_class, None)
599 self.base_nodes.append(implicit_sn)
600 prev_sn.next = implicit_sn
601 implicit_sn.next = sn
602
603 prev_sn = sn
604
605
606 if rulechain.rename:
607
608 sn = ScaffoldNode(rulechain.rename.old_class, rulechain.rename.new_class, rulechain.rename)
609 self.base_nodes.append(sn)
610
611
612
613
614 if first:
615 self.first_type[type] = sn
616 first = False
617 if prev_sn:
618 if get_message_key(prev_sn.new_class) == get_message_key(sn.old_class):
619 prev_sn.next = sn
620 else:
621 implicit_sn = ScaffoldNode(prev_sn.new_class, sn.old_class, None)
622 self.base_nodes.append(implicit_sn)
623 prev_sn.next = implicit_sn
624 implicit_sn.next = sn
625 prev_sn = sn
626 terminal_nodes.append(sn)
627
628 else:
629 if prev_sn:
630 terminal_nodes.append(prev_sn)
631
632
633
634
635
636 for sn in terminal_nodes:
637 key = get_message_key(sn.new_class)
638
639 renamed = (sn.old_class._type != sn.new_class._type)
640
641 sys_class = genpy.message.get_message_class(sn.new_class._type)
642
643
644 if sys_class:
645 new_rule = self.make_update_rule(sn.new_class, sys_class)
646 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
647 if R.valid:
648 sn.next = ScaffoldNode(sn.new_class, sys_class, R)
649 self.base_nodes.append(sn.next)
650
651 if renamed:
652 tmp_sns = self.scaffold_range(sn.new_class._type, sn.new_class._type)
653
654
655 if tmp_sns == []:
656 if sys_class is not None:
657 sn.next = ScaffoldNode(sn.new_class, sys_class, None)
658 self.base_nodes.append(sn.next)
659 continue
660
661
662 for tmp_sn in reversed(tmp_sns):
663 tmp_key = get_message_key(tmp_sn.old_class)
664 if (key == tmp_key):
665 sn.next = tmp_sn
666 break
667
668
669
670
671
672
673
674 if (sn.next is None):
675 for tmp_sn in reversed(tmp_sns):
676 new_rule = self.make_update_rule(sn.new_class, tmp_sn.old_class)
677 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
678 if R.valid:
679 sn.next = ScaffoldNode(sn.new_class, tmp_sn.old_class, R)
680 self.base_nodes.append(sn.next)
681 break
682
683
684
685 if (sn.next is None):
686 if sys_class:
687 new_rule = self.make_update_rule(sn.new_class, sys_class)
688 else:
689 new_rule = self.make_old_half_rule(sn.new_class)
690 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
691 sn.next = ScaffoldNode(sn.new_class, None, R)
692 self.base_nodes.append(sn.next)
693
694
695
696
697
698 for sn in self.base_nodes:
699 if (sn.rule is None):
700 new_rule = self.make_update_rule(sn.old_class, sn.new_class)
701 sn.rule = new_rule(self, 'GENERATED.' + new_rule.__name__)
702
703
704
705 for sn in self.base_nodes:
706 sn.rule.find_sub_paths()
707
708
709
710
711 self.class_dict = {}
712
713 for sn in self.base_nodes + self.extra_nodes:
714 self.class_dict[get_message_key(sn.old_class)] = sn.old_class
715 self.class_dict[get_message_key(sn.new_class)] = sn.new_class
716
717
719 if key in self.class_dict:
720 return self.class_dict[key]
721 else:
722 return None
723
724
726 if r.valid == False:
727 print("ERROR: Update rule [%s] has valid set to False." % (r.location), file=sys.stderr)
728 self.false_rule_loaded = True
729 return
730
731 rulechain = self.rulechains[r.old_type]
732
733 if r.rename_rule:
734 if (rulechain.rename != None):
735 print("WARNING: Update rules [%s] and [%s] both attempting to rename type [%s]. Ignoring [%s]" % (rulechain.rename.location, r.location, r.old_type, r.location), file=sys.stderr)
736 return
737
738
739 cycle = []
740 tmp = r
741 while tmp:
742 cycle.append(tmp)
743 if (tmp.new_type == r.old_type):
744 print("WARNING: Update rules %s introduce a renaming cycle. Ignoring [%s]" % ([x.location for x in cycle], r.location), file=sys.stderr)
745 return
746 if tmp.new_type in self.rulechains:
747 tmp = self.rulechains[tmp.new_type].rename
748 else:
749 break
750
751
752 if rulechain.chain and (r.order <= rulechain.chain[-1].order):
753 print("WARNING: Update rule [%s] which performs rename does not have largest order number. Ignoring" % r.location, file=sys.stderr)
754 return
755
756 rulechain.rename = r
757
758 else:
759 if r.order in rulechain.order_keys:
760 otherind = [x.order for x in rulechain.chain].index(r.order)
761 print("WARNING: Update rules [%s] and [%s] for type [%s] have the same order number. Ignoring [%s]" % (rulechain.chain[otherind].location, r.location, r.old_type, r.location), file=sys.stderr)
762 return
763 else:
764 if rulechain.rename and (r.order >= rulechain.rename.order):
765 print("WARNING: Update rule [%s] has order number larger than rename rule [%s]. Ignoring" % (r.location, rulechain.rename.location), file=sys.stderr)
766 return
767
768 rulechain.order_keys.add(r.order)
769 rulechain.chain.append(r)
770 rulechain.chain.sort(key=lambda x: x.order)
771
772
774 base_valid = not False in [sn.rule.valid for sn in self.base_nodes]
775 extra_valid = not False in [sn.rule.valid for sn in self.extra_nodes]
776 return base_valid and extra_valid
777
778
780 invalid_rules = []
781 invalid_rule_cache = []
782 for sn in self.base_nodes:
783 if not sn.rule.valid:
784 path_key = get_path_key(sn.old_class, sn.new_class)
785 if (path_key not in invalid_rule_cache):
786 invalid_rules.append(sn.rule)
787 invalid_rule_cache.append(path_key)
788 for sn in self.extra_nodes:
789 if not sn.rule.valid:
790 path_key = get_path_key(sn.old_class, sn.new_class)
791 if (path_key not in invalid_rule_cache):
792 invalid_rules.append(sn.rule)
793 invalid_rule_cache.append(path_key)
794 return invalid_rules
795
796
798 rule_cache = []
799 new_rules = []
800 for r in rules:
801 path_key = get_path_key(r.old_class, r.new_class)
802 if (path_key not in rule_cache):
803 new_rules.append(r)
804 return new_rules
805
806
816
818 try:
819 first_sn = self.first_type[old_type]
820
821 sn_range = [first_sn]
822
823 found_new_type = False
824
825 tmp_sn = first_sn
826
827 while (tmp_sn.next is not None and tmp_sn.next.new_class is not None):
828
829 tmp_sn = tmp_sn.next
830 if (tmp_sn != first_sn):
831 sn_range.append(tmp_sn)
832 if (tmp_sn.new_class._type == new_type):
833 found_new_type == True
834 if (found_new_type and tmp_sn.new_class._type != new_type):
835 break
836
837 return sn_range
838
839 except KeyError:
840 return []
841
842
844 key = get_message_key(old_class)
845
846 last_class = old_class
847
848 try:
849 return self.found_targets[key]
850 except KeyError:
851
852 sys_class = genpy.message.get_message_class(old_class._type)
853
854 if sys_class is not None:
855 self.found_targets[key] = sys_class
856 return sys_class
857
858 try:
859 tmp_sn = self.first_type[old_class._type]
860
861 if tmp_sn.new_class is not None:
862 last_class = tmp_sn.new_class
863
864 while tmp_sn.next is not None:
865 tmp_sn = tmp_sn.next
866
867 if tmp_sn.new_class is not None:
868 last_class = tmp_sn.new_class
869 sys_class = genpy.message.get_message_class(tmp_sn.new_class._type)
870 else:
871 sys_class = None
872
873 if sys_class is not None:
874 self.found_targets[key] = sys_class
875 return sys_class
876 except KeyError:
877 pass
878
879 self.found_targets[key] = None
880 return None
881
882
883
885 key = get_path_key(old_class, new_class)
886
887
888 try:
889 return self.found_paths[key]
890 except KeyError:
891 pass
892
893
894
895
896
897 if new_class is None:
898 sn_range = self.scaffold_range(old_class._type, "")
899
900 found_start = False
901
902 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
903
904 if (tmp_sn.old_class._type != old_class._type):
905 continue
906 if get_message_key(tmp_sn.old_class) == get_message_key(old_class):
907 sn_range = sn_range[ind:]
908 found_start = True
909 break
910
911
912 if not found_start:
913 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
914 if (tmp_sn.old_class._type != old_class._type):
915 continue
916 new_rule = self.make_update_rule(old_class, tmp_sn.old_class)
917 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
918 if R.valid:
919 R.find_sub_paths()
920 sn = ScaffoldNode(old_class, tmp_sn.old_class, R)
921 self.extra_nodes.append(sn)
922 sn_range = sn_range[ind:]
923 sn_range.insert(0,sn)
924 found_start = True
925 break
926
927 if sn_range == []:
928 tmp_class = old_class
929 else:
930 tmp_class = sn_range[-1].new_class
931
932 new_rule = self.make_old_half_rule(tmp_class)
933 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
934 sn = ScaffoldNode(tmp_class, None, R)
935 sn_range.append(sn)
936 self.extra_nodes.append(sn)
937 self.found_paths[key] = sn_range
938 return sn_range
939
940
941 if (old_class._type == new_class._type and old_class._full_text.strip() == new_class._full_text.strip()):
942 self.found_paths[key] = []
943 return []
944
945 sn_range = self.scaffold_range(old_class._type, new_class._type)
946
947
948 if sn_range == []:
949 new_rule = self.make_update_rule(old_class, new_class)
950 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
951 R.find_sub_paths()
952 sn = ScaffoldNode(old_class, new_class, R)
953 self.extra_nodes.append(sn)
954 self.found_paths[key] = [sn]
955 return [sn]
956
957
958
959 found_stop = False
960
961
962 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
963
964 if (tmp_sn.new_class._type != new_class._type):
965 break
966 if get_message_key(tmp_sn.new_class) == get_message_key(new_class):
967 sn_range = sn_range[:ind+1]
968 found_stop = True
969 break
970
971
972 if not found_stop:
973 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
974 if (tmp_sn.new_class._type != new_class._type):
975 break
976 new_rule = self.make_update_rule(tmp_sn.new_class, new_class)
977 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
978 if R.valid:
979 R.find_sub_paths()
980 sn = ScaffoldNode(tmp_sn.new_class, new_class, R)
981 self.extra_nodes.append(sn)
982 sn_range = sn_range[:ind+1]
983 sn_range.append(sn)
984 found_stop = True
985 break
986
987
988 if not found_stop:
989 new_rule = self.make_update_rule(sn_range[-1].new_class, new_class)
990 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
991 R.find_sub_paths()
992 sn = ScaffoldNode(sn_range[-1].new_class, new_class, R)
993 self.extra_nodes.append(sn)
994 sn_range.append(sn)
995
996
997 found_start = False
998
999
1000 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
1001
1002 if (tmp_sn.old_class._type != old_class._type):
1003 continue
1004 if get_message_key(tmp_sn.old_class) == get_message_key(old_class):
1005 sn_range = sn_range[ind:]
1006 found_start = True
1007 break
1008
1009
1010 if not found_start:
1011 for (ind, tmp_sn) in reversed(list(zip(range(len(sn_range)), sn_range))):
1012 if (tmp_sn.old_class._type != old_class._type):
1013 continue
1014 new_rule = self.make_update_rule(old_class, tmp_sn.old_class)
1015 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
1016 if R.valid:
1017 R.find_sub_paths()
1018 sn = ScaffoldNode(old_class, tmp_sn.old_class, R)
1019 self.extra_nodes.append(sn)
1020 sn_range = sn_range[ind:]
1021 sn_range.insert(0,sn)
1022 found_start = True
1023 break
1024
1025
1026 if not found_start:
1027 new_rule = self.make_update_rule(old_class, sn_range[0].old_class)
1028 R = new_rule(self, 'GENERATED.' + new_rule.__name__)
1029 R.find_sub_paths()
1030 sn = ScaffoldNode(old_class, sn_range[0].old_class, R)
1031 self.extra_nodes.append(sn)
1032 sn_range.insert(0,sn)
1033
1034 self.found_paths[key] = sn_range
1035 return sn_range
1036
1037
1039 path = self.find_path(msg_from[4], msg_to[4])
1040
1041 if False in [sn.rule.valid for sn in path]:
1042 raise BagMigrationException("Migrate called, but no valid migration path from [%s] to [%s]"%(msg_from[0], msg_to[0]))
1043
1044
1045 if path == [] or msg_from[2] == msg_to[2]:
1046 return (msg_to[0], msg_from[1], msg_to[2], msg_to[3], msg_to[4])
1047
1048 tmp_msg = path[0].old_class()
1049 tmp_msg.deserialize(msg_from[1])
1050
1051 for sn in path:
1052 tmp_msg = sn.rule.apply(tmp_msg)
1053
1054 buff = StringIO()
1055 tmp_msg.serialize(buff)
1056
1057 return (msg_to[0], buff.getvalue(), msg_to[2], msg_to[3], msg_to[4])
1058
1059
1060
1061 - def migrate(self, msg_from, msg_to):
1062 path = self.find_path(msg_from.__class__, msg_to.__class__)
1063
1064 if False in [sn.rule.valid for sn in path]:
1065 raise BagMigrationException("Migrate called, but no valid migration path from [%s] to [%s]"%(msg_from._type, msg_to._type))
1066
1067
1068 if path == [] or msg_from._md5sum == msg_to._md5sum:
1069 buff = StringIO()
1070 msg_from.serialize(buff)
1071 msg_to.deserialize(buff.getvalue())
1072 return
1073
1074 if len(path) > 0:
1075 buff = StringIO()
1076 msg_from.serialize(buff)
1077
1078 tmp_msg = path[0].old_class()
1079
1080 tmp_msg.deserialize(buff.getvalue())
1081
1082 for sn in path:
1083 tmp_msg = sn.rule.apply(tmp_msg)
1084 else:
1085 tmp_msg = msg_from
1086
1087 buff = StringIO()
1088 tmp_msg.serialize(buff)
1089 msg_to.deserialize(buff.getvalue())
1090
1092 if len(msg_from_array) != len(msg_to_array):
1093 raise BagMigrationException("Migrate array called on on arrays of unequal length.")
1094
1095 if len(msg_from_array) == 0:
1096 return
1097
1098 path = self.find_path(msg_from_array[0].__class__, msg_to_array[0].__class__)
1099
1100 if path is None:
1101 raise BagMigrationException("Migrate called, but no migration path from [%s] to [%s]"%(msg_from._type, msg_to._type))
1102
1103
1104 if path == []:
1105 for i in range(len(msg_from_array)):
1106 buff = StringIO()
1107 msg_from_array[i].serialize(buff)
1108 msg_to_array[i].deserialize(buff.getvalue())
1109 return
1110
1111 for i in range(len(msg_from_array)):
1112 buff = StringIO()
1113 tmp_msg = path[0].old_class()
1114 msg_from_array[i].serialize(buff)
1115 tmp_msg.deserialize(buff.getvalue())
1116 for sn in path:
1117 tmp_msg = sn.rule.apply(tmp_msg)
1118
1119 buff = StringIO()
1120 tmp_msg.serialize(buff)
1121 msg_to_array[i].deserialize(buff.getvalue())
1122
1124 name = "update_%s_%s"%(old_class._type.replace("/","_"), old_class._md5sum)
1125
1126
1127
1128 classdef = "class %s(MessageUpdateRule):\n"%name
1129 classdef += "\told_type = \"%s\"\n"%old_class._type
1130 classdef += "\told_full_text = \"\"\"\n%s\n\"\"\"\n\n"%old_class._full_text.strip()
1131 classdef += "\tnew_type = \"%s\"\n"%new_class._type
1132 classdef += "\tnew_full_text = \"\"\"\n%s\n\"\"\"\n"%new_class._full_text.strip()
1133 classdef += "\n"
1134 classdef += "\torder = 0"
1135 classdef += "\n"
1136
1137 validdef = "\tvalid = True\n"
1138
1139 migratedefs = "\tmigrated_types = ["
1140
1141 updatedef = "\tdef update(self, old_msg, new_msg):\n"
1142
1143 old_consts = constants_from_def(old_class._type, old_class._full_text)
1144 new_consts = constants_from_def(new_class._type, new_class._full_text)
1145
1146 if (not new_consts >= old_consts):
1147 validdef = "\tvalid = False\n"
1148 for c in (old_consts - new_consts):
1149 updatedef += "\t\t#Constant '%s' has changed\n"%(c[0],)
1150
1151 old_slots = []
1152 old_slots.extend(old_class.__slots__)
1153
1154 migrations_seen = []
1155
1156
1157 for (s,t) in zip(new_class.__slots__, new_class._slot_types):
1158 warn_msg = None
1159 new_base_type, new_is_array, new_array_len = genmsg.msgs.parse_type(t)
1160 try:
1161 ind = old_class.__slots__.index(s)
1162 old_slots.remove(s)
1163 old_base_type, old_is_array, old_array_len = genmsg.msgs.parse_type(old_class._slot_types[ind])
1164
1165 if new_is_array != old_is_array:
1166 warn_msg = "Could not match array with nonarray"
1167
1168 elif new_array_len != old_array_len:
1169 if old_array_len is None:
1170 warn_msg = "Converted from variable length array to fixed array of length %d"%(new_array_len)
1171 elif new_array_len is None:
1172 warn_msg = "Converted from fixed array of length %d to variable length"%(old_array_len)
1173 else:
1174 warn_msg = "Fixed length array converted from %d to %d"%(old_array_len,new_array_len)
1175
1176 elif genmsg.msgs.is_builtin(new_base_type):
1177 if new_base_type != old_base_type:
1178 warn_msg = "Primitive type changed"
1179 else:
1180 updatedef += "\t\tnew_msg.%s = old_msg.%s\n"%(s,s)
1181
1182 else:
1183 tmp_old_type = clean_name(old_base_type, old_class._type)
1184 tmp_new_type = clean_name(new_base_type, new_class._type)
1185
1186 tmp_qualified_old_type = qualified_name(old_base_type, old_class._type)
1187 tmp_qualified_new_type = qualified_name(new_base_type, new_class._type)
1188
1189
1190 if (tmp_qualified_old_type == tmp_qualified_new_type) or \
1191 (tmp_qualified_old_type in self.rename_map and
1192 tmp_qualified_new_type in self.rename_map[tmp_qualified_old_type]):
1193
1194 if (tmp_old_type, tmp_new_type) not in migrations_seen:
1195 migratedefs += "\n\t\t(\"%s\",\"%s\"),"%(tmp_old_type, tmp_new_type)
1196 migrations_seen.append((tmp_old_type, tmp_new_type))
1197
1198 if not new_is_array:
1199 updatedef += "\t\tself.migrate(old_msg.%s, new_msg.%s)\n"%(s,s)
1200 else:
1201 updatedef += "\t\tself.migrate_array(old_msg.%s, new_msg.%s, \"%s\")\n"%(s,s,new_base_type)
1202 else:
1203 warn_msg = "No migration path between [%s] and [%s]"%(tmp_old_type, tmp_new_type)
1204 except ValueError:
1205 warn_msg = "No matching field name in old message"
1206
1207 if warn_msg is not None:
1208 validdef = "\tvalid = False\n"
1209 updatedef += "\t\t#%s\n"%warn_msg
1210 updatedef += "\t\tnew_msg.%s = %s\n"%(s,migration_default_value(t))
1211
1212 migratedefs += "]\n"
1213
1214 if old_slots:
1215 validdef = "\tvalid = False\n"
1216 for s in old_slots:
1217 updatedef += "\t\t#No field to match field %s from old message\n"%(s)
1218
1219 classdef += migratedefs + '\n' + validdef + '\n' + updatedef
1220
1221 printclassdef = classdef + "\tdef get_class_def(self):\n\t\treturn \'\'\'%s\'\'\'\n"%classdef
1222
1223
1224 exec(printclassdef)
1225 return locals()[name]
1226
1228 name = "update__%s__%s"%(old_class._type.replace("/","_"), old_class._md5sum)
1229
1230
1231
1232 classdef = "class %s(MessageUpdateRule):\n"%name
1233 classdef += "\told_type = \"%s\"\n"%old_class._type
1234 classdef += "\told_full_text = \"\"\"\n%s\n\"\"\"\n\n"%old_class._full_text.strip()
1235 classdef += "\tnew_type = \"\"\n"
1236 classdef += "\tnew_full_text = \"\"\"\n\n\"\"\"\n"
1237 classdef += "\n"
1238 classdef += "\torder = 0"
1239 classdef += "\n"
1240
1241 validdef = "\tvalid = False\n"
1242
1243 migratedefs = "\tmigrated_types = []\n"
1244
1245 updatedef = "\tdef update(self, old_msg, new_msg):\n"
1246 updatedef += "\t\tpass\n"
1247
1248 classdef += migratedefs + '\n' + validdef + '\n' + updatedef
1249
1250 printclassdef = classdef + "\tdef get_class_def(self):\n\t\treturn \'\'\'%s\'\'\'\n"%classdef
1251
1252
1253 exec(printclassdef)
1254 return locals()[name]
1255
1257 name = "update_to_%s_%s"%(new_class._type.replace("/","_"), new_class._md5sum)
1258
1259
1260
1261 classdef = "class %s(MessageUpdateRule):\n"%name
1262 classdef += "\told_type = \"\"\n"
1263 classdef += "\told_full_text = \"\"\"\n\n\"\"\"\n\n"
1264 classdef += "\tnew_type = \"%s\"\n"%new_class._type
1265 classdef += "\tnew_full_text = \"\"\"\n%s\n\"\"\"\n"%new_class._full_text.strip()
1266 classdef += "\n"
1267 classdef += "\torder = 0"
1268 classdef += "\n"
1269
1270 validdef = "\tvalid = False\n"
1271
1272 migratedefs = "\tmigrated_types = []\n"
1273
1274 updatedef = "\tdef update(self, old_msg, new_msg):\n"
1275 updatedef += "\t\tpass\n"
1276
1277 classdef += migratedefs + '\n' + validdef + '\n' + updatedef
1278
1279 printclassdef = classdef + "\tdef get_class_def(self):\n\t\treturn \'\'\'%s\'\'\'\n"%classdef
1280
1281
1282 exec(printclassdef)
1283 return locals()[name]
1284
1286 if field_type in ['bool', 'byte', 'int8', 'int16', 'int32', 'int64',\
1287 'char', 'uint8', 'uint16', 'uint32', 'uint64']:
1288 return '0'
1289 elif field_type in ['float32', 'float64']:
1290 return '0.'
1291 elif field_type == 'string':
1292
1293 return "''"
1294 elif field_type.endswith(']'):
1295 base_type, is_array, array_len = genmsg.msgs.parse_type(field_type)
1296 if base_type in ['byte', 'uint8']:
1297
1298 if array_len is not None:
1299 return "chr(0)*%s"%array_len
1300 else:
1301 return "''"
1302 elif array_len is None:
1303 return '[]'
1304 else:
1305 def_val = migration_default_value(base_type)
1306 return '[' + ','.join(itertools.repeat(def_val, array_len)) + ']'
1307 else:
1308 return "self.get_new_class('%s')()"%field_type
1309
1311 core_pkg, core_base_type = genmsg.package_resource_name(core_type)
1312
1313 splits = msg_def.split('\n' + '=' * 80 + '\n')
1314
1315 core_msg = splits[0]
1316 deps_msgs = splits[1:]
1317
1318
1319 from genmsg import MsgContext
1320 context = MsgContext.create_default()
1321 specs = { core_type: genmsg.msg_loader.load_msg_from_string(context, core_msg, core_pkg) }
1322
1323
1324
1325
1326
1327
1328 return set([(x.name, x.val, x.type) for x in specs[core_type].constants])
1329