SEBA-271 Fix xproto field numbers not passed through

Change-Id: Ib5abee510e1a02f025d3011699d9f34d59e201c1
diff --git a/lib/xos-genx/xosgenx/jinja2_extensions/base.py b/lib/xos-genx/xosgenx/jinja2_extensions/base.py
index 1293863..561aeea 100644
--- a/lib/xos-genx/xosgenx/jinja2_extensions/base.py
+++ b/lib/xos-genx/xosgenx/jinja2_extensions/base.py
@@ -120,7 +120,10 @@
         if accessor:
             base_fields = xproto_base_fields(table[accessor], table)
 
-            model_fields = table[accessor]['fields']
+            model_fields = [x.copy() for x in table[accessor]['fields']]
+            for field in model_fields:
+                field["accessor"] = accessor
+
             fields.extend(base_fields)
             fields.extend(model_fields)
 
@@ -132,6 +135,53 @@
 
     return fields
 
+def xproto_fields(m, table):
+    """ Generate the full list of models for the xproto message `m` including fields from the classes it inherits.
+
+        Inserts the special field "id" at the very beginning.
+
+        Each time we descend a new level of inheritance, increment the offset field numbers by 100. The base
+        class's fields will be numbered from 1-99, the first descendant will be number 100-199, the second
+        descdendant numbered from 200-299, and so on. This assumes any particular model as at most 100
+        fields.
+    """
+
+    model_fields = [x.copy() for x in m["fields"]]
+    for field in model_fields:
+        field["accessor"] = m["fqn"]
+
+    fields = xproto_base_fields(m, table) + model_fields
+
+    # The "id" field is a special field. Every model has one. Put it up front and pretend it's part of the
+
+    id_field = {'type': 'int32', 'name': 'id', 'options': {}, "id": "1", "accessor": fields[0]["accessor"]}
+
+    fields = [id_field] + fields
+
+    # Walk through the list of fields. They will be in depth-first search order from the base model forward. Each time
+    # the model changes, offset the protobuf field numbers by 100.
+    offset = 0
+    last_accessor = fields[0]["accessor"]
+    for field in fields:
+        if (field["accessor"] != last_accessor):
+            last_accessor = field["accessor"]
+            offset += 100
+        field_id = int(field["id"])
+        if (field_id < 1) or (field_id >= 100):
+            raise Exception("Only field numbers from 1 to 99 are permitted, field %s in model %s" % (field["name"], field["accessor"]))
+        field["id"] = int(field["id"]) + offset
+
+    # Check for duplicates
+    fields_by_number = {}
+    for field in fields:
+        id = field["id"]
+        dup = fields_by_number.get(id)
+        if dup:
+            raise Exception("Field %s has duplicate number %d with field %s in model %s" % (field["name"], id, dup["name"], field["accessor"]))
+        fields_by_number[id] = field
+
+    return fields
+
 def xproto_base_rlinks(m, table):
     links = []
 
@@ -146,6 +196,38 @@
 
     return links
 
+def xproto_rlinks(m, table):
+    """ Return the reverse links for the xproto message `m`.
+
+        If the link includes a reverse_id, then it will be used for the protobuf field id. If there is no
+        reverse_id, then one will automatically be allocated started at id 1900. It is incouraged that all links
+        include reverse_ids, so that field identifiers are deterministic across all protobuf messages.
+    """
+
+    index = 1900
+    links = xproto_base_rlinks(m, table) + m["rlinks"]
+
+    links = [x for x in links if ("+" not in x["src_port"]) and ("+" not in x["dst_port"])]
+
+    for link in links:
+        if link["reverse_id"]:
+            link["id"] = int(link["reverse_id"])
+        else:
+            link["id"] = index
+            index += 1
+
+    # check for duplicates
+    links_by_number={}
+    for link in links:
+        id = link["id"]
+        dup=links_by_number.get(id)
+        if dup:
+            raise Exception("Field %s has duplicate number %d with field %s in model %s" % (link["src_port"], id, link["src_port"], m["name"]))
+        links_by_number[id] = link
+
+    return links
+
+
 def xproto_base_links(m, table):
     links = []