qgenada_A_mapper.py 15.2 KB
Newer Older
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
#
# (C) Semantix Information Technologies.
#
# Copyright 2014-2015 IB Krates <info@krates.ee>
#       QGenc code generator integration
#
# 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.
#
'''This contains the implementation of model level mapping
of ASN.1 constructs to C. It is used as a backend of Semantix's
code generator A.'''

import os
import sys
import re
31

32
from distutils import spawn
33
from typing import List, Union, Set, IO, Any  # NOQA
34

35
36
from ..commonPy import asnParser
from ..commonPy.utility import panic, inform
37
from ..commonPy.createInternalTypes import ScanChildren
38
from ..commonPy.cleanupNodes import SetOfBadTypenames
39
40
41
from ..commonPy.asnAST import (
    AsnBasicNode, AsnSequenceOrSet, AsnSequenceOrSetOf, AsnBool, AsnInt,
    AsnReal, AsnString, AsnEnumerated, AsnSequence, AsnSet, AsnChoice,
42
43
    AsnMetaMember, AsnSequenceOf, AsnSetOf)
from ..commonPy.asnAST import AsnNode  # NOQA pylint: disable=unused-import
44
from ..commonPy.asnParser import AST_Leaftypes, AST_Lookup
45
46

# The file written to
47
g_outputFile: IO[Any]
48
49

# A map of the ASN.1 types defined so far
50
g_definedTypes = set()  # type: Set[asnParser.Typename]
51
52
53
54
55
56

g_octetStrings = 0

g_bHasStartupRunOnce = False


57
def Version() -> None:
58
    print("Code generator: " + "$Id: qgenada_A_mapper.py $")  # pragma: no cover
59
60


61
def CleanNameAsSimulinkWants(name: str) -> str:
62
63
64
65
66
67
    return re.sub(r'[^a-zA-Z0-9_]', '_', name)


# Especially for the C mapper, since we need to pass the complete ASN.1 files list to ASN1SCC,
# the second param is not asnFile, it is asnFiles

68
def OnStartup(unused_modelingLanguage: str, asnFiles: List[str], outputDir: str, unused_badTypes: SetOfBadTypenames) -> None:  # pylint: disable=invalid-sequence-index
69
    # print "Use ASN1SCC to generate the structures for '%s'" % asnFile
70
71
72
    asn1SccPath = spawn.find_executable('asn1.exe')
    if not asn1SccPath:
        panic("ASN1SCC seems to be missing from your system (asn1.exe not found in PATH).\n")  # pragma: no cover
73
    os.system(
74
        ("mono " if sys.platform.startswith('linux') else "") +
75
        "\"{}\"  -typePrefix asn1Scc -c -uPER -o \"".format(asn1SccPath) +
76
        outputDir + "\" \"" + "\" \"".join(asnFiles) + "\"")
77
78
79
80
81
82
83
84
85
86
87
88
89
90
    os.system("rm -f \"" + outputDir + "\"/*.adb")

    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("QGenAda_A_mapper: Creating file '%s'...", outputFilename)
    global g_outputFile
91
    outputDir += "../"
92
    g_outputFile = open(outputDir + outputFilename, 'w')
93
    g_definedTypes.clear()
94
95
    global g_octetStrings
    g_octetStrings = 0
96
    CreateDeclarationsForAllTypes(asnParser.g_names, asnParser.g_leafTypeDict)
97
98


99
def OnBasic(unused_nodeTypename: str, unused_node: AsnBasicNode, unused_leafTypeDict: AST_Leaftypes) -> None:
100
    pass  # pragma: nocover
101
102


103
def OnSequence(unused_nodeTypename: str, unused_node: AsnSequenceOrSet, unused_leafTypeDict: AST_Leaftypes) -> None:
104
    pass  # pragma: nocover
105
106


107
def OnSet(unused_nodeTypename: str, unused_node: AsnSequenceOrSet, unused_leafTypeDict: AST_Leaftypes) -> None:
108
109
110
    pass  # pragma: nocover


111
def OnEnumerated(unused_nodeTypename: str, unused_node: AsnEnumerated, unused_leafTypeDict: AST_Leaftypes) -> None:
112
    pass  # pragma: nocover
113
114


115
def OnSequenceOf(unused_nodeTypename: str, unused_node: AsnSequenceOrSetOf, unused_leafTypeDict: AST_Leaftypes) -> None:
116
    pass  # pragma: nocover
117
118


119
def OnSetOf(unused_nodeTypename: str, unused_node: AsnSequenceOrSetOf, unused_leafTypeDict: AST_Leaftypes) -> None:
120
121
122
    pass  # pragma: nocover


123
def OnChoice(unused_nodeTypename: str, unused_node: AsnChoice, unused_leafTypeDict: AST_Leaftypes) -> None:
124
    pass  # pragma: nocover
125
126


127
def OnShutdown(unused_badTypes: SetOfBadTypenames) -> None:
128
129
130
    pass


131
def MapInteger(node: AsnInt) -> str:
132
    if node._range[0] >= 0 and node._range[1] <= 255:
133
        return "uint8"
134
    elif node._range[0] >= -128 and node._range[1] <= 127:
135
        return "int8"
136
    elif node._range[0] >= 0 and node._range[1] <= 65535:
137
        return "uint16"
138
    elif node._range[0] >= -32768 and node._range[1] <= 32767:
139
        return "int16"
140
    elif node._range[0] >= 0:
141
142
143
144
145
        return "uint32"
    else:
        return "int32"


146
def CreateAlias(nodeTypename: str, mappedType: str, description: str) -> None:
147
    # Requirements have changed: Simulink has an issue with AliasType...
Thanassis Tsiodras's avatar
Thanassis Tsiodras committed
148
149
150
    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))
151
152


153
def DeclareCollection(node: AsnSequenceOrSetOf, name: str, internal: str) -> None:
154
    for i in range(0, node._range[-1]):
155
156
157
158
159
160
        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)
161
    g_outputFile.write("%s_member_length.name='length';\n" % name)
162
163
164
165
166
    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('[')
167
    for i in range(0, node._range[-1]):
168
169
170
171
172
173
        g_outputFile.write("%s_member_%02d " % (name, i))
    g_outputFile.write('%s_member_length' % name)
    g_outputFile.write(']')
    g_outputFile.write(";\n\n")


174
def DeclareSimpleCollection(node: Union[AsnString, AsnSequenceOf, AsnSetOf], name: str, internal: str) -> None:
175
    g_outputFile.write('%s_member_data=Simulink.BusElement;\n' % name)
Thanassis Tsiodras's avatar
Thanassis Tsiodras committed
176
    g_outputFile.write("%s_member_data.name='element_data';\n" % name)
177
178
179
180
    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
181
    if len(node._range) > 1 and node._range[0] != node._range[1]:
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
        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")


200
def CreateDeclarationForType(nodeTypename: str, names: AST_Lookup, leafTypeDict: AST_Leaftypes) -> None:
201
202
    if nodeTypename in g_definedTypes:
        return
203
204
    g_definedTypes.add(nodeTypename)
    results = []  # type: List[str]
205
206
207
208
209
210
211
212
213
214
215
216
217
218
    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):
219
        if not node._range:
220
221
222
223
224
225
226
227
228
229
230
231
232
233
            panic("QGenAda_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("QGenAda_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")
234
    elif isinstance(node, (AsnSequence, AsnSet, AsnChoice)):
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
        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)):

280
                # mappedType = CleanNameAsSimulinkWants(child[1]._containedType + "_t") XYZ
281
282
283
284
285
286
287
288
289
                mappedType = CleanNameAsSimulinkWants(child[1]._containedType)
            else:  # pragma: no cover
                panic("QGenAda_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))
290
        if elemNo > 1:
291
            g_outputFile.write('[')
292
        for i in range(0, elemNo):
293
            g_outputFile.write("%s_elem%02d " % (CleanNameAsSimulinkWants(nodeTypename), i + 1))
294
        if elemNo > 1:
295
296
            g_outputFile.write(']')
        g_outputFile.write(";\n\n")
297
    elif isinstance(node, (AsnSequenceOf, AsnSetOf)):
298
299
300
        name = CleanNameAsSimulinkWants(nodeTypename)
        contained = node._containedType
        assert isinstance(contained, str)
301
        containedNode = contained  # type: Union[AsnNode, str]
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
        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("QGenAda_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


327
def CreateDeclarationsForAllTypes(names: AST_Lookup, leafTypeDict: AST_Leaftypes) -> None:
328
    for nodeTypename in list(names.keys()):
329
        CreateDeclarationForType(nodeTypename, names, leafTypeDict)