simulink_A_mapper.py 14.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#
# (C) Semantix Information Technologies.
#
# Semantix Information Technologies is licensing the code of the
# Data Modelling Tools (DMT) in the following dual-license mode:
#
# Commercial Developer License:
#       The DMT Commercial Developer License is the suggested version
# to use for the development of proprietary and/or commercial software.
# This version is for developers/companies who do not want to comply
# with the terms of the GNU Lesser General Public License version 2.1.
#
# GNU LGPL v. 2.1:
#       This version of DMT is the one to use for the development of
# applications, when you are willing to comply with the terms of the
# GNU Lesser General Public License version 2.1.
#
# Note that in both cases, there are no charges (royalties) for the
# generated code.
#
import re
22

23
from typing import Union, Set, List  # NOQA pylint: disable=unused-import
24

25 26 27 28
from ..commonPy.utility import panic, inform
from ..commonPy import asnParser
from ..commonPy.asnAST import (
    AsnBool, AsnInt, AsnReal, AsnString, AsnEnumerated, AsnSequence,
29 30
    AsnSet, AsnChoice, AsnMetaMember, AsnSequenceOf, AsnSetOf,
    AsnBasicNode, AsnSequenceOrSet, AsnSequenceOrSetOf)
31
from ..commonPy.asnAST import AsnNode  # NOQA pylint: disable=unused-import
32
from ..commonPy.createInternalTypes import ScanChildren
33 34
from ..commonPy.asnParser import AST_Leaftypes, AST_Lookup
from ..commonPy.cleanupNodes import SetOfBadTypenames
35

36 37 38 39 40

# The file written to
g_outputFile = None

# A map of the ASN.1 types defined so far
41
g_definedTypes = set()  # type: Set[str]
42 43 44 45 46 47

g_octetStrings = 0

g_bHasStartupRunOnce = False


48
def Version() -> None:
49
    print("Code generator: " + "$Id: simulink_A_mapper.py 2390 2012-07-19 12:39:17Z ttsiodras $")  # pragma: no cover
50 51


52
def CleanNameAsSimulinkWants(name: str) -> str:
53 54 55
    return re.sub(r'[^a-zA-Z0-9_]', '_', name)


56
def OnStartup(unused_modelingLanguage: str, unused_asnFile: str, outputDir: str, unused_badTypes: SetOfBadTypenames) -> None:
57 58 59 60 61 62 63 64 65 66 67 68 69
    global g_bHasStartupRunOnce
    if g_bHasStartupRunOnce:
        # Don't rerun, it has already done all the work
        # for all the ASN.1 files used
        return  # pragma: no cover
    else:
        g_bHasStartupRunOnce = True
    # outputFilename = modelingLanguage + "_" + os.path.basename(asnFile).replace(".","_") + ".m"
    # Changed at Maxime's request, 27/1/2011
    outputFilename = "Simulink_DataView_asn.m"
    inform("Simulink_A_mapper: Creating file '%s'...", outputFilename)
    global g_outputFile
    g_outputFile = open(outputDir + outputFilename, 'w')
70
    g_definedTypes.clear()
71 72
    global g_octetStrings
    g_octetStrings = 0
73
    CreateDeclarationsForAllTypes(asnParser.g_names, asnParser.g_leafTypeDict)
74 75


76
def OnBasic(unused_nodeTypename: str, unused_node: AsnBasicNode, unused_leafTypeDict: AST_Leaftypes) -> None:
77 78 79
    pass


80
def OnSequence(unused_nodeTypename: str, unused_node: AsnSequenceOrSet, unused_leafTypeDict: AST_Leaftypes) -> None:
81 82 83
    pass


84
def OnSet(unused_nodeTypename: str, unused_node: AsnSequenceOrSet, unused_leafTypeDict: AST_Leaftypes) -> None:
85 86 87
    pass  # pragma: nocover


88
def OnEnumerated(unused_nodeTypename: str, unused_node: AsnEnumerated, unused_leafTypeDict: AST_Leaftypes) -> None:
89 90 91
    pass


92
def OnSequenceOf(unused_nodeTypename: str, unused_node: AsnSequenceOrSetOf, unused_leafTypeDict: AST_Leaftypes) -> None:
93 94 95
    pass


96
def OnSetOf(unused_nodeTypename: str, unused_node: AsnSequenceOrSetOf, unused_leafTypeDict: AST_Leaftypes) -> None:
97 98 99
    pass  # pragma: nocover


100
def OnChoice(unused_nodeTypename: str, unused_node: AsnChoice, unused_leafTypeDict: AST_Leaftypes) -> None:
101 102 103
    pass


104
def OnShutdown(unused_badTypes: SetOfBadTypenames) -> None:
105 106 107
    pass


108
def MapInteger(node: AsnInt) -> str:
109
    if node._range[0] >= 0 and node._range[1] <= 255:
110
        return "uint8"
111
    elif node._range[0] >= -128 and node._range[1] <= 127:
112
        return "int8"
113
    elif node._range[0] >= 0 and node._range[1] <= 65535:
114
        return "uint16"
115
    elif node._range[0] >= -32768 and node._range[1] <= 32767:
116
        return "int16"
117
    elif node._range[0] >= 0:
118 119 120 121 122
        return "uint32"
    else:
        return "int32"


123
def CreateAlias(nodeTypename: str, mappedType: str, description: str) -> None:
124 125 126 127 128 129
    # Requirements have changed: Simulink has an issue with AliasType...
    g_outputFile.write("%s = Simulink.AliasType;\n" % CleanNameAsSimulinkWants(nodeTypename))
    g_outputFile.write("%s.BaseType = '%s';\n" % (CleanNameAsSimulinkWants(nodeTypename), mappedType))
    g_outputFile.write("%s.Description = '%s';\n\n" % (CleanNameAsSimulinkWants(nodeTypename), description))


130
def DeclareCollection(node: AsnSequenceOrSetOf, name: str, internal: str) -> None:
131
    for i in range(0, node._range[-1]):
132 133 134 135 136 137
        g_outputFile.write('%s_member_%02d=Simulink.BusElement;\n' % (name, i))
        # Andreas(ESA) wants them to be called 'element_%02d'
        g_outputFile.write("%s_member_%02d.name='element_%02d';\n" % (name, i, i))
        g_outputFile.write("%s_member_%02d.DataType='%s';\n" % (name, i, internal))
        g_outputFile.write("%s_member_%02d.dimensions=1;\n\n" % (name, i))
    g_outputFile.write('%s_member_length=Simulink.BusElement;\n' % name)
138
    g_outputFile.write("%s_member_length.name='length';\n" % name)
139 140 141 142 143
    g_outputFile.write("%s_member_length.DataType='int32';\n" % name)
    g_outputFile.write("%s_member_length.dimensions=1;\n\n" % name)
    g_outputFile.write('%s=Simulink.Bus;\n' % name)
    g_outputFile.write("%s.Elements = " % name)
    g_outputFile.write('[')
144
    for i in range(0, node._range[-1]):
145 146 147 148 149 150
        g_outputFile.write("%s_member_%02d " % (name, i))
    g_outputFile.write('%s_member_length' % name)
    g_outputFile.write(']')
    g_outputFile.write(";\n\n")


151
def DeclareSimpleCollection(node: Union[AsnString, AsnSequenceOf, AsnSetOf], name: str, internal: str) -> None:
152 153 154 155 156 157
    g_outputFile.write('%s_member_data=Simulink.BusElement;\n' % name)
    g_outputFile.write("%s_member_data.name='element_data';\n" % name)
    g_outputFile.write("%s_member_data.DataType='%s';\n" % (name, internal))
    g_outputFile.write("%s_member_data.dimensions=%d;\n\n" % (name, node._range[-1]))

    bNeedLength = False
158
    if len(node._range) > 1 and node._range[0] != node._range[1]:
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
        bNeedLength = True

    if bNeedLength:
        g_outputFile.write('%s_member_length=Simulink.BusElement;\n' % name)
        g_outputFile.write("%s_member_length.name='length';\n" % name)
        g_outputFile.write("%s_member_length.DataType='int32';\n" % name)
        g_outputFile.write("%s_member_length.dimensions=1;\n\n" % name)

    g_outputFile.write('%s=Simulink.Bus;\n' % name)
    g_outputFile.write("%s.Elements = " % name)
    g_outputFile.write('[')
    g_outputFile.write("%s_member_data " % name)
    if bNeedLength:
        g_outputFile.write('%s_member_length' % name)
    g_outputFile.write(']')
    g_outputFile.write(";\n\n")


177
def CreateDeclarationForType(nodeTypename: str, names: AST_Lookup, leafTypeDict: AST_Leaftypes) -> None:
178 179
    if nodeTypename in g_definedTypes:
        return
180 181
    g_definedTypes.add(nodeTypename)
    results = []  # type: List[str]
182 183 184 185 186 187 188 189 190 191 192 193 194 195
    ScanChildren(nodeTypename, names[nodeTypename], names, results, isRoot=True, createInnerNodesInNames=True)
    inform("Prerequisites of %s", nodeTypename)
    for prereqNodeTypename in results:
        if prereqNodeTypename != '':
            inform("\t%s", prereqNodeTypename)
            CreateDeclarationForType(prereqNodeTypename, names, leafTypeDict)
    node = names[nodeTypename]
    if isinstance(node, AsnBool):
        CreateAlias(nodeTypename, "boolean", 'A simple BOOLEAN')
    elif isinstance(node, AsnInt):
        CreateAlias(nodeTypename, MapInteger(node), "range is %s" % str(node._range))
    elif isinstance(node, AsnReal):
        CreateAlias(nodeTypename, "double", "range is %s" % str(node._range))
    elif isinstance(node, AsnString):
196
        if not node._range:
197 198 199 200 201 202 203 204 205 206 207 208 209 210
            panic("Simulink_A_mapper: string (in %s) must have a SIZE constraint!\n" % node.Location())  # pragma: no cover
        name = CleanNameAsSimulinkWants(nodeTypename)
        DeclareSimpleCollection(node, name, "uint8")
    elif isinstance(node, AsnEnumerated):
        g_outputFile.write("%% Values for %s:\n" % nodeTypename)
        for member in node._members:
            # member[0] is the enumerant name
            # member[1] is the integer value used (or None)
            if member[1] is not None:
                g_outputFile.write("%s_value_%s = %s;\n" % (CleanNameAsSimulinkWants(nodeTypename), CleanNameAsSimulinkWants(member[0]), member[1]))
            else:  # pragma: no cover
                panic("Simulink_A_mapper: must have values for enumerants (%s)" % node.Location())  # pragma: no cover
        CreateAlias(nodeTypename, "int32", "values of ENUMERATED %s" % nodeTypename)
        g_outputFile.write("\n")
211
    elif isinstance(node, (AsnSequence, AsnSet, AsnChoice)):
212
        if not node._members:
213
            panic("Simulink_A_mapper: Simulink can't support empty Seq/Set/Choice! (%s)" % node.Location())  # pragma: no cover
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
        elemNo = 0
        if isinstance(node, AsnChoice):
            elemNo += 1
            name = "%s_elem%02d" % (CleanNameAsSimulinkWants(nodeTypename), elemNo)
            g_outputFile.write(name + "=Simulink.BusElement;\n")
            g_outputFile.write(name + ".name='choiceIdx';\n")
            g_outputFile.write(name + ".DataType='uint8';\n")
            g_outputFile.write(name + ".dimensions=1;\n\n")
        for child in node._members:
            elemNo += 1
            name = "%s_elem%02d" % (CleanNameAsSimulinkWants(nodeTypename), elemNo)
            g_outputFile.write(name + "=Simulink.BusElement;\n")
            g_outputFile.write(name + ".name='%s';\n" % CleanNameAsSimulinkWants(child[0]))

            # Since AliasType doesn't work well in the Matlab/Simulink typesystem,
            # we have to change the simple fields to their native types.
            # Trace all the AsnMetaMember chain to find out the leaf type...
            childNode = child[1]
            while isinstance(childNode, AsnMetaMember):
                childNode = names[childNode._containedType]

            # Now that you have the leaf type, use the native types wherever possible.
            # BUT! For octet strings, we have pre-declared (see Dependencies definitions)
            # octet_string_%d types, but ONLY for native AsnString, not for typedef'd ones...
            if isinstance(childNode, AsnBool):
                mappedType = 'boolean'
            elif isinstance(childNode, AsnInt):
                mappedType = MapInteger(childNode)
            elif isinstance(childNode, AsnReal):
                mappedType = 'double'
            elif isinstance(childNode, AsnString):
                if isinstance(child[1], AsnMetaMember):
                    mappedType = CleanNameAsSimulinkWants(child[1]._containedType)
                else:                                  # pragma: no cover
                    mappedType = child[1]._pseudoname  # pragma: no cover
            elif isinstance(childNode, AsnEnumerated):
                mappedType = 'int32'
            # These would not be possible: AsnSequence, AsnChoice, AsnSequenceOf,
            # since the parsing stage ends with automated pseudonames (through
            # AsnMetaMembers) for them. However, we resolve AsnMetaMembers in the
            # "while" above, so if we do meet one of them (now that the resolving
            # has been done), we must have started from an AsnMetaMember...
            # so we will use the containedType_t for reference.
            elif isinstance(childNode, (AsnSequence, AsnSequenceOf, AsnSet, AsnSetOf, AsnChoice)):

259
                # mappedType = CleanNameAsSimulinkWants(child[1]._containedType + "_t") XYZ
260 261 262 263 264 265 266 267 268
                mappedType = CleanNameAsSimulinkWants(child[1]._containedType)
            else:  # pragma: no cover
                panic("Simulink_A_mapper: Unexpected category of child (%s)" % str(child[1]))  # pragma: no cover
            g_outputFile.write(name + ".DataType='%s';\n" % mappedType)
            # Used to be -1 for strings and metaMembers, but requirements have changed (again :-)
            g_outputFile.write(name + ".dimensions=1;\n\n")

        g_outputFile.write("%s = Simulink.Bus;\n" % CleanNameAsSimulinkWants(nodeTypename))
        g_outputFile.write("%s.Elements = " % CleanNameAsSimulinkWants(nodeTypename))
269
        if elemNo > 1:
270
            g_outputFile.write('[')
271
        for i in range(0, elemNo):
272
            g_outputFile.write("%s_elem%02d " % (CleanNameAsSimulinkWants(nodeTypename), i + 1))
273
        if elemNo > 1:
274 275
            g_outputFile.write(']')
        g_outputFile.write(";\n\n")
276
    elif isinstance(node, (AsnSequenceOf, AsnSetOf)):
277 278 279
        name = CleanNameAsSimulinkWants(nodeTypename)
        contained = node._containedType
        assert isinstance(contained, str)
280
        containedNode = contained  # type: Union[AsnNode, str]
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
        while isinstance(containedNode, str):
            containedNode = names[containedNode]
        if isinstance(containedNode, AsnBool):
            DeclareSimpleCollection(node, name, 'boolean')
        elif isinstance(containedNode, AsnInt):
            DeclareSimpleCollection(node, name, MapInteger(containedNode))
        elif isinstance(containedNode, AsnReal):
            DeclareSimpleCollection(node, name, 'double')
        elif isinstance(containedNode, AsnString):
            DeclareCollection(node, name, CleanNameAsSimulinkWants(contained))
        elif isinstance(containedNode, AsnEnumerated):
            DeclareSimpleCollection(node, name, 'int32')
        # These are not possible: AsnSequence, AsnChoice, AsnSequenceOf, because we introduce
        # AsnMetaMember for them at the end of the parser. If we meet them here, it is
        # because resolving has been done (see while above), so we started from a string...
        elif isinstance(containedNode, (AsnSequence, AsnSequenceOf, AsnSet, AsnSetOf, AsnChoice)):

            DeclareCollection(node, name, CleanNameAsSimulinkWants(contained))
        else:  # pragma: no cover
            panic("Simulink_A_mapper: Unexpected category of contained type (%s,%s)" % (node.Location(), str(contained)))  # pragma: no cover

    else:  # pragma: no cover
        panic("Unexpected ASN.1 type... Send this grammar to Semantix")  # pragma: no cover


306
def CreateDeclarationsForAllTypes(names: AST_Lookup, leafTypeDict: AST_Leaftypes) -> None:
307
    for nodeTypename in list(names.keys()):
308
        CreateDeclarationForType(nodeTypename, names, leafTypeDict)