cannect 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cannect/__init__.py +30 -0
- cannect/api/__init__.py +0 -0
- cannect/config.py +114 -0
- cannect/core/__init__.py +0 -0
- cannect/core/ascet/__init__.py +3 -0
- cannect/core/ascet/amd.py +698 -0
- cannect/core/ascet/formula.py +33 -0
- cannect/core/ascet/oid.py +29 -0
- cannect/core/ascet/ws.py +154 -0
- cannect/core/can/__init__.py +2 -0
- cannect/core/can/ascet/__init__.py +3 -0
- cannect/core/can/ascet/_db2code.py +344 -0
- cannect/core/can/ascet/_db2elem.py +399 -0
- cannect/core/can/ascet/comdef.py +256 -0
- cannect/core/can/ascet/comrx.py +139 -0
- cannect/core/can/ascet/diag.py +691 -0
- cannect/core/can/db/__init__.py +4 -0
- cannect/core/can/db/reader.py +148 -0
- cannect/core/can/db/schema.py +269 -0
- cannect/core/can/db/specification/__init__.py +0 -0
- cannect/core/can/db/specification/message.py +230 -0
- cannect/core/can/db/specification/styles.py +81 -0
- cannect/core/can/db/specification/wrapper.py +161 -0
- cannect/core/can/db/vcs.py +104 -0
- cannect/core/can/rule.py +229 -0
- cannect/core/can/testcase/__init__.py +0 -0
- cannect/core/can/testcase/unitcase/__init__.py +0 -0
- cannect/core/can/testcase/unitcase/asw2can.py +48 -0
- cannect/core/can/testcase/unitcase/decode.py +63 -0
- cannect/core/can/testcase/unitcase/diagnosis.py +479 -0
- cannect/core/can/testcase/unitcase/encode.py +60 -0
- cannect/core/ir/__init__.py +2 -0
- cannect/core/ir/changehistory.py +310 -0
- cannect/core/ir/delivereables.py +71 -0
- cannect/core/ir/diff.py +97 -0
- cannect/core/ir/ir.py +581 -0
- cannect/core/ir/sdd.py +148 -0
- cannect/core/mdf.py +66 -0
- cannect/core/subversion.py +136 -0
- cannect/core/testcase/__init__.py +3 -0
- cannect/core/testcase/plotter.py +181 -0
- cannect/core/testcase/style.py +981 -0
- cannect/core/testcase/testcase.py +160 -0
- cannect/core/testcase/unitcase.py +227 -0
- cannect/errors.py +20 -0
- cannect/schema/__init__.py +5 -0
- cannect/schema/candb.py +226 -0
- cannect/schema/datadictionary.py +60 -0
- cannect/utils/__init__.py +3 -0
- cannect/utils/excel.py +29 -0
- cannect/utils/logger.py +81 -0
- cannect/utils/ppt.py +236 -0
- cannect/utils/tools.py +207 -0
- cannect-1.0.0.dist-info/METADATA +214 -0
- cannect-1.0.0.dist-info/RECORD +58 -0
- cannect-1.0.0.dist-info/WHEEL +5 -0
- cannect-1.0.0.dist-info/licenses/LICENSE +21 -0
- cannect-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
from cannect.schema.datadictionary import DataDictionary
|
|
2
|
+
from cannect.errors import AmdFormatError
|
|
3
|
+
from cannect.config import env
|
|
4
|
+
from cannect.utils import tools
|
|
5
|
+
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from pandas import DataFrame, Series
|
|
8
|
+
from typing import Dict, List, Union
|
|
9
|
+
from xml.etree.ElementTree import ElementTree, Element
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AmdSource(object):
|
|
14
|
+
"""
|
|
15
|
+
[KOR]
|
|
16
|
+
*.amd 파일 핸들러
|
|
17
|
+
*.amd 파일을 읽기 위한 클래스입니다. *.main.amd, *.implementation.amd,
|
|
18
|
+
*.data.amd, *.specification.amd 파일을 동일 경로에서 찾습니다. 입력된
|
|
19
|
+
소스 파일이 압축 파일 (.zip)인 경우 임시 폴더(bin)에 압축을 푼 후 *.amd
|
|
20
|
+
파일을 찾습니다. 임시 폴더는 정의하는 것을 강하게 권장합니다. 임시 폴더가
|
|
21
|
+
정의되지 않은 경우 자동을 폴더 생성 후 클래스 인스턴스가 소멸될 때 삭제됩니다.
|
|
22
|
+
|
|
23
|
+
[ENG]
|
|
24
|
+
*.amd FILE HANDLER
|
|
25
|
+
This class is designed to read *.amd files. It searches for related
|
|
26
|
+
files such as: *.main.amd, *.implementation.amd, *.data.amd,
|
|
27
|
+
*.specification.amd in the same directory as the input file. If the
|
|
28
|
+
input source file is a compressed .zip file, it will be extracted to
|
|
29
|
+
a temporary folder (default: 'bin'), and the *.amd files will be
|
|
30
|
+
searched within the extracted contents. It is strongly recommended
|
|
31
|
+
to explicitly define the temporary folder. If no temporary folder is
|
|
32
|
+
defined, one will be automatically created. The folder will be deleted
|
|
33
|
+
when the class instance is destroyed.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
__slots__ = (
|
|
37
|
+
"name", # name of *.amd
|
|
38
|
+
"path", # directory (parent path) of *.amds
|
|
39
|
+
"file", # full path of *.main.amd
|
|
40
|
+
"main", # full path of *.main.amd
|
|
41
|
+
"impl", # full path of *.implementation.amd
|
|
42
|
+
"data", # full path of *.data.amd
|
|
43
|
+
"spec", # full path of *.specification.amd
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def __init__(self, file:str, binary:str=''):
|
|
47
|
+
file = str(file)
|
|
48
|
+
self.name = name = os.path.basename(file).split('.')[0]
|
|
49
|
+
if file.endswith('.zip'):
|
|
50
|
+
if not binary:
|
|
51
|
+
binary = env.ASCET / "bin"
|
|
52
|
+
os.makedirs(binary, exist_ok=True)
|
|
53
|
+
tools.unzip(file, binary)
|
|
54
|
+
self.path = path = binary
|
|
55
|
+
self.file = file = os.path.join(binary, f'{name}.main.amd')
|
|
56
|
+
elif file.endswith('.main.amd'):
|
|
57
|
+
self.path = os.path.dirname(file)
|
|
58
|
+
self.file = file
|
|
59
|
+
else:
|
|
60
|
+
raise AmdFormatError
|
|
61
|
+
self.main = file
|
|
62
|
+
self.impl = file.replace(".main.amd", ".implementation.amd")
|
|
63
|
+
self.data = file.replace(".main.amd", ".data.amd")
|
|
64
|
+
self.spec = file.replace(".main.amd", ".specification.amd")
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
def __del__(self):
|
|
68
|
+
binary = os.path.join(os.path.dirname(__file__), 'bin')
|
|
69
|
+
if os.path.exists(binary):
|
|
70
|
+
tools.clear(binary, leave_path=False)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class AmdElements:
|
|
75
|
+
"""
|
|
76
|
+
* NOTE *
|
|
77
|
+
kwargs에 키값을 애트리뷰트(.)로 접근하는 경우 필수 키로 간주하며 *.get(attribute_name, default)로
|
|
78
|
+
접근하는 경우, 키 값이 없어도 default로 치환하여 애트리뷰트를 생성함.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def MethodSignature(cls, **kwargs) -> Element:
|
|
83
|
+
"""
|
|
84
|
+
*.main.amd의 <MethodSignature> 요소 생성
|
|
85
|
+
|
|
86
|
+
:param kwargs:
|
|
87
|
+
:return:
|
|
88
|
+
"""
|
|
89
|
+
kwargs = DataDictionary(**kwargs)
|
|
90
|
+
return Element('MethodSignature',
|
|
91
|
+
name=kwargs.name,
|
|
92
|
+
OID=kwargs.OID,
|
|
93
|
+
public=kwargs.get('public', "true"),
|
|
94
|
+
default=kwargs.get('default', 'false'),
|
|
95
|
+
defaultMethod=kwargs.get('defaultMethod', 'false'),
|
|
96
|
+
hidden=kwargs.get('hidden', 'false'),
|
|
97
|
+
availableForOS=kwargs.get('availableForOS', 'true'))
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def Element(cls, **kwargs) -> Element:
|
|
101
|
+
"""
|
|
102
|
+
*.main.amd의 <Element> 요소 및 하위 요소 생성
|
|
103
|
+
공통 태그 요소를 먼저 생성한 후 조건별 하위 태그 삽입
|
|
104
|
+
|
|
105
|
+
예시 구조 :
|
|
106
|
+
<Element name="Can_Wakeup01Size" OID="_040g1ngg01pp1og70ocg9t7rqsgg4" ignore="false">
|
|
107
|
+
<Comment>WAKEUP_01_00ms(0x000) DLC</Comment>
|
|
108
|
+
<ElementAttributes modelType="scalar" basicModelType="udisc" unit="">
|
|
109
|
+
# 여기에 @modelType에 따라 요소가 조건적으로 삽입됨
|
|
110
|
+
</ElementAttributes>
|
|
111
|
+
</Element>
|
|
112
|
+
|
|
113
|
+
:param kwargs:
|
|
114
|
+
:return:
|
|
115
|
+
"""
|
|
116
|
+
kwargs = DataDictionary(**kwargs)
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
공통 태그 요소 생성
|
|
120
|
+
"""
|
|
121
|
+
tElement = Element('Element',
|
|
122
|
+
name=kwargs.name,
|
|
123
|
+
OID=kwargs.OID,
|
|
124
|
+
ignore=kwargs.get('ignore', "false"))
|
|
125
|
+
Comment = Element('Comment')
|
|
126
|
+
if 'comment' in kwargs:
|
|
127
|
+
Comment.text = kwargs.comment
|
|
128
|
+
ElementAttributes = Element('ElementAttributes',
|
|
129
|
+
modelType=kwargs.modelType,
|
|
130
|
+
basicModelType=kwargs.basicModelType,
|
|
131
|
+
unit=kwargs.get('unit', ''))
|
|
132
|
+
tElement.append(Comment)
|
|
133
|
+
tElement.append(ElementAttributes)
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
basicModelType == 'implementationCast' 인 경우 추가 요소 삽입 없이 리턴
|
|
137
|
+
"""
|
|
138
|
+
if kwargs.basicModelType == "implementationCast":
|
|
139
|
+
return tElement
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
조건별 하위 태그 요소 삽입
|
|
143
|
+
"""
|
|
144
|
+
if kwargs.modelType == "complex":
|
|
145
|
+
"""
|
|
146
|
+
1. 클래스 요소
|
|
147
|
+
"""
|
|
148
|
+
ComplexType = Element('ComplexType')
|
|
149
|
+
ComplexAttribute = Element('ComplexAttributes',
|
|
150
|
+
componentName=kwargs.componentName,
|
|
151
|
+
componentID=kwargs.componentID,
|
|
152
|
+
scope=kwargs.scope,
|
|
153
|
+
set=kwargs.get('set', "false"),
|
|
154
|
+
get=kwargs['get'] if 'get' in kwargs else 'false',
|
|
155
|
+
read=kwargs.get('read', 'true'),
|
|
156
|
+
write=kwargs.get('write', 'true'),
|
|
157
|
+
reference=kwargs.get('reference', 'false'))
|
|
158
|
+
ComplexType.append(ComplexAttribute)
|
|
159
|
+
ElementAttributes.append(ComplexType)
|
|
160
|
+
|
|
161
|
+
else:
|
|
162
|
+
PrimitiveAttributes = Element('PrimitiveAttributes',
|
|
163
|
+
kind=kwargs.kind,
|
|
164
|
+
scope=kwargs.scope,
|
|
165
|
+
virtual=kwargs.get('virtual', 'false'),
|
|
166
|
+
dependent=kwargs.get('dependent', 'false'),
|
|
167
|
+
volatile=kwargs.get('volatile', 'true'),
|
|
168
|
+
calibrated=kwargs.get('calibrated', 'true'),
|
|
169
|
+
set=kwargs.get('set', "false"),
|
|
170
|
+
get=kwargs['get'] if 'get' in kwargs else 'false',
|
|
171
|
+
read=kwargs.get('read', 'true'),
|
|
172
|
+
write=kwargs.get('write', 'true'),
|
|
173
|
+
reference=kwargs.get('reference', 'false'))
|
|
174
|
+
|
|
175
|
+
if kwargs.modelType == 'scalar':
|
|
176
|
+
"""
|
|
177
|
+
2. Scalar 요소
|
|
178
|
+
"""
|
|
179
|
+
ScalarType = Element('ScalarType')
|
|
180
|
+
ScalarType.append(PrimitiveAttributes)
|
|
181
|
+
ElementAttributes.append(ScalarType)
|
|
182
|
+
|
|
183
|
+
elif kwargs.modelType == 'array':
|
|
184
|
+
"""
|
|
185
|
+
3. 배열 요소
|
|
186
|
+
"""
|
|
187
|
+
DimensionalType = Element('DimensionalType',
|
|
188
|
+
maxSizeX=kwargs.maxSizeX)
|
|
189
|
+
DimensionalType.append(PrimitiveAttributes)
|
|
190
|
+
ElementAttributes.append(DimensionalType)
|
|
191
|
+
|
|
192
|
+
elif kwargs.modelType == 'oned':
|
|
193
|
+
"""
|
|
194
|
+
4. 1-Dimensional Table 요소
|
|
195
|
+
"""
|
|
196
|
+
# TODO
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
elif kwargs.modelType == 'twod':
|
|
200
|
+
"""
|
|
201
|
+
5. 2-Dimensional Table 요소
|
|
202
|
+
"""
|
|
203
|
+
# TODO
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
elif kwargs.modelType == 'distribution':
|
|
207
|
+
"""
|
|
208
|
+
6. Distribution 요소
|
|
209
|
+
"""
|
|
210
|
+
# TODO
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
elif kwargs.modelType == 'matrix':
|
|
214
|
+
"""
|
|
215
|
+
7. Matrix 요소
|
|
216
|
+
"""
|
|
217
|
+
# TODO
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
else:
|
|
221
|
+
raise Exception(f'No Pre-defined <Element> for modelType = {kwargs.modelType}')
|
|
222
|
+
return tElement
|
|
223
|
+
|
|
224
|
+
@classmethod
|
|
225
|
+
def ImplementationEntry(cls, **kwargs) -> Element:
|
|
226
|
+
"""
|
|
227
|
+
*.implementation.amd의 <ImplementationEntry> 요소 및 하위 생성
|
|
228
|
+
*.main.amd의 Element 생성 키워드를 C/O 하여야 한다. (종속적)
|
|
229
|
+
|
|
230
|
+
공통 태그 요소를 먼저 생성한 후 조건별 하위 태그 삽입
|
|
231
|
+
|
|
232
|
+
예시 구조 :
|
|
233
|
+
<ImplementationEntry>
|
|
234
|
+
<ImplementationVariant name="default">
|
|
235
|
+
<ElementImplementation elementName="CRC16bit_Calculator" elementOID="_040g1ngg01401o8708804v4jlv5g4">
|
|
236
|
+
# 여기에 요소가 조건적으로 삽입됨
|
|
237
|
+
</ElementImplementation>
|
|
238
|
+
</ImplementationVariant>
|
|
239
|
+
</ImplementationEntry>
|
|
240
|
+
:param kwargs: Element(**kwargs)의 kwargs를 C/O
|
|
241
|
+
:return:
|
|
242
|
+
"""
|
|
243
|
+
kwargs = DataDictionary(**kwargs)
|
|
244
|
+
|
|
245
|
+
ImplementationEntry = Element('ImplementationEntry')
|
|
246
|
+
ImplementationVariant = Element('ImplementationVariant', name='default')
|
|
247
|
+
ElementImplementation = Element('ElementImplementation',
|
|
248
|
+
elementName=kwargs.name,
|
|
249
|
+
elementOID=kwargs.OID)
|
|
250
|
+
ImplementationVariant.append(ElementImplementation)
|
|
251
|
+
ImplementationEntry.append(ImplementationVariant)
|
|
252
|
+
|
|
253
|
+
"""
|
|
254
|
+
조건별 하위 태그 요소 삽입
|
|
255
|
+
"""
|
|
256
|
+
if kwargs.modelType == "complex":
|
|
257
|
+
"""
|
|
258
|
+
1. 클래스 요소
|
|
259
|
+
"""
|
|
260
|
+
ComplexImplementation = Element('ComplexImplementation',
|
|
261
|
+
implementationName=kwargs.implementationName,
|
|
262
|
+
implementationOID=kwargs.implementationOID)
|
|
263
|
+
ElementImplementation.append(ComplexImplementation)
|
|
264
|
+
else:
|
|
265
|
+
if kwargs.modelType == 'scalar':
|
|
266
|
+
"""
|
|
267
|
+
2. Scalar 요소
|
|
268
|
+
"""
|
|
269
|
+
ScalarImplementation = Element('ScalarImplementation')
|
|
270
|
+
ElementImplementation.append(ScalarImplementation)
|
|
271
|
+
|
|
272
|
+
if kwargs.basicModelType == 'implementationCast':
|
|
273
|
+
"""
|
|
274
|
+
2-1. Impl. Cast 요소: CURRENTLY NOT USED
|
|
275
|
+
"""
|
|
276
|
+
ImplementationCastImplementation = Element('ImplementationCastImplementation',
|
|
277
|
+
ignoreImplementationCast="false",
|
|
278
|
+
limitAssignments="true",
|
|
279
|
+
isLimitOverflow="true",
|
|
280
|
+
limitOverflow="automatic",
|
|
281
|
+
memoryLocationInstance="Default",
|
|
282
|
+
additionalInformation="",
|
|
283
|
+
cacheLocking="automatic",
|
|
284
|
+
quantization="0.1",
|
|
285
|
+
formula="TqPrpHigh_Nm",
|
|
286
|
+
master="Implementation",
|
|
287
|
+
physType="real64",
|
|
288
|
+
implType="sint32",
|
|
289
|
+
zeroNotIncluded="false")
|
|
290
|
+
ScalarImplementation.append(ImplementationCastImplementation)
|
|
291
|
+
elif kwargs.basicModelType == 'log':
|
|
292
|
+
"""
|
|
293
|
+
2-2. Log 타입 요소
|
|
294
|
+
"""
|
|
295
|
+
LogicImplementation = Element('LogicImplementation',
|
|
296
|
+
physType="log",
|
|
297
|
+
implType="uint8",
|
|
298
|
+
memoryLocationInstance="Default",
|
|
299
|
+
additionalInformation="",
|
|
300
|
+
cacheLocking="automatic")
|
|
301
|
+
ScalarImplementation.append(LogicImplementation)
|
|
302
|
+
else:
|
|
303
|
+
"""
|
|
304
|
+
2-3. 일반 Scalar 요소
|
|
305
|
+
"""
|
|
306
|
+
NumericImplementation = Element('NumericImplementation',
|
|
307
|
+
limitAssignments=kwargs.get("limitAssignments", "true"),
|
|
308
|
+
isLimitOverflow=kwargs.get("isLimitOverflow", "true"),
|
|
309
|
+
limitOverflow="automatic",
|
|
310
|
+
memoryLocationInstance="Default",
|
|
311
|
+
additionalInformation="",
|
|
312
|
+
cacheLocking="automatic",
|
|
313
|
+
quantization=kwargs.quantization,
|
|
314
|
+
formula=kwargs.formula,
|
|
315
|
+
master="Implementation",
|
|
316
|
+
physType=kwargs.physType,
|
|
317
|
+
implType=kwargs.implType,
|
|
318
|
+
zeroNotIncluded="false")
|
|
319
|
+
ScalarImplementation.append(NumericImplementation)
|
|
320
|
+
PhysicalInterval = Element('PhysicalInterval',
|
|
321
|
+
min=kwargs.physMin,
|
|
322
|
+
max=kwargs.physMax)
|
|
323
|
+
NumericImplementation.append(PhysicalInterval)
|
|
324
|
+
ImplementationInterval = Element('ImplementationInterval',
|
|
325
|
+
min=kwargs.implMin,
|
|
326
|
+
max=kwargs.implMax)
|
|
327
|
+
NumericImplementation.append(ImplementationInterval)
|
|
328
|
+
|
|
329
|
+
elif kwargs.modelType == 'array':
|
|
330
|
+
"""
|
|
331
|
+
3. 배열 요소
|
|
332
|
+
"""
|
|
333
|
+
DimensionalImplementation = Element('DimensionalImplementation')
|
|
334
|
+
ElementImplementation.append(DimensionalImplementation)
|
|
335
|
+
|
|
336
|
+
ArrayImplementation = Element('ArrayImplementation')
|
|
337
|
+
DimensionalImplementation.append(ArrayImplementation)
|
|
338
|
+
|
|
339
|
+
NumericImplementation = Element('NumericImplementation',
|
|
340
|
+
limitAssignments=kwargs.get("limitAssignments", "true"),
|
|
341
|
+
isLimitOverflow=kwargs.get("isLimitOverflow", "true"),
|
|
342
|
+
limitOverflow="automatic",
|
|
343
|
+
memoryLocationInstance="Default",
|
|
344
|
+
additionalInformation="",
|
|
345
|
+
cacheLocking="automatic",
|
|
346
|
+
quantization=kwargs.quantization,
|
|
347
|
+
formula=kwargs.formula,
|
|
348
|
+
master="Implementation",
|
|
349
|
+
physType=kwargs.physType,
|
|
350
|
+
implType=kwargs.implType,
|
|
351
|
+
zeroNotIncluded="false")
|
|
352
|
+
ArrayImplementation.append(NumericImplementation)
|
|
353
|
+
PhysicalInterval = Element('PhysicalInterval',
|
|
354
|
+
min=kwargs.physMin,
|
|
355
|
+
max=kwargs.physMax)
|
|
356
|
+
NumericImplementation.append(PhysicalInterval)
|
|
357
|
+
ImplementationInterval = Element('ImplementationInterval',
|
|
358
|
+
min=kwargs.implMin,
|
|
359
|
+
max=kwargs.implMax)
|
|
360
|
+
NumericImplementation.append(ImplementationInterval)
|
|
361
|
+
|
|
362
|
+
else:
|
|
363
|
+
raise Exception(f'No Pre-defined <ImplementationEntry> for modelType = {kwargs.modelType}')
|
|
364
|
+
|
|
365
|
+
return ImplementationEntry
|
|
366
|
+
|
|
367
|
+
@classmethod
|
|
368
|
+
def DataEntry(cls, **kwargs) -> Element:
|
|
369
|
+
"""
|
|
370
|
+
*.data.amd의 <DataEntry> 요소 및 하위 생성
|
|
371
|
+
*.main.amd의 Element 생성 키워드를 C/O 하여야 한다. (종속적)
|
|
372
|
+
|
|
373
|
+
공통 태그 요소를 먼저 생성한 후 조건별 하위 태그 삽입
|
|
374
|
+
|
|
375
|
+
예시 구조 :
|
|
376
|
+
<DataEntry elementName="ABS_DfctvSta_Can" elementOID="_040g1ngg01a01o071c3g65u3aca0m">
|
|
377
|
+
<DataVariant name="default">
|
|
378
|
+
# 여기에 요소가 조건적으로 삽입됨
|
|
379
|
+
</DataVariant>
|
|
380
|
+
</DataEntry>
|
|
381
|
+
|
|
382
|
+
:param kwargs: Element(**kwargs)의 kwargs를 C/O
|
|
383
|
+
:return:
|
|
384
|
+
"""
|
|
385
|
+
kwargs = DataDictionary(**kwargs)
|
|
386
|
+
|
|
387
|
+
DataEntry = Element('DataEntry',
|
|
388
|
+
elementName=kwargs.name,
|
|
389
|
+
elementOID=kwargs.OID)
|
|
390
|
+
DataVariant = Element('DataVariant', name="default")
|
|
391
|
+
DataEntry.append(DataVariant)
|
|
392
|
+
|
|
393
|
+
"""
|
|
394
|
+
조건별 하위 태그 요소 삽입
|
|
395
|
+
"""
|
|
396
|
+
if kwargs.modelType == "complex":
|
|
397
|
+
"""
|
|
398
|
+
1. 클래스 요소
|
|
399
|
+
"""
|
|
400
|
+
ComplexType = Element('ComplexType',
|
|
401
|
+
dataName=kwargs.dataName,
|
|
402
|
+
dataOID=kwargs.dataOID)
|
|
403
|
+
DataVariant.append(ComplexType)
|
|
404
|
+
else:
|
|
405
|
+
if kwargs.modelType == 'scalar':
|
|
406
|
+
"""
|
|
407
|
+
2. Scalar 요소
|
|
408
|
+
"""
|
|
409
|
+
ScalarType = Element('ScalarType')
|
|
410
|
+
DataVariant.append(ScalarType)
|
|
411
|
+
if kwargs.basicModelType == 'implementationCast':
|
|
412
|
+
"""
|
|
413
|
+
2-1. Impl. Cast 요소: CURRENTLY NOT USED
|
|
414
|
+
"""
|
|
415
|
+
pass
|
|
416
|
+
elif kwargs.basicModelType == 'log':
|
|
417
|
+
"""
|
|
418
|
+
2-2. Log 타입 요소
|
|
419
|
+
"""
|
|
420
|
+
Logic = Element('Logic', value=kwargs.get('value', 'false'))
|
|
421
|
+
ScalarType.append(Logic)
|
|
422
|
+
else:
|
|
423
|
+
"""
|
|
424
|
+
2-3. 일반 Scalar 요소
|
|
425
|
+
"""
|
|
426
|
+
Numeric = Element('Numeric', value=kwargs.get('value', '0'))
|
|
427
|
+
ScalarType.append(Numeric)
|
|
428
|
+
|
|
429
|
+
elif kwargs.modelType == 'array':
|
|
430
|
+
"""
|
|
431
|
+
3. 배열 요소
|
|
432
|
+
"""
|
|
433
|
+
DimensionalType = Element('DimensionalType')
|
|
434
|
+
DataVariant.append(DimensionalType)
|
|
435
|
+
|
|
436
|
+
Array = Element('Array', currentSizeX=kwargs.maxSizeX)
|
|
437
|
+
DimensionalType.append(Array)
|
|
438
|
+
|
|
439
|
+
Value = Element('Value')
|
|
440
|
+
Numeric = Element('Numeric', value=kwargs.get('value', '0'))
|
|
441
|
+
Value.append(Numeric)
|
|
442
|
+
for n in range(int(kwargs.maxSizeX)):
|
|
443
|
+
Array.append(Value)
|
|
444
|
+
|
|
445
|
+
else:
|
|
446
|
+
raise Exception(f'No Pre-defined <ImplementationEntry> for modelType = {kwargs.modelType}')
|
|
447
|
+
|
|
448
|
+
return DataEntry
|
|
449
|
+
|
|
450
|
+
@classmethod
|
|
451
|
+
def HeaderBlock(cls, **kwargs) -> Element:
|
|
452
|
+
"""
|
|
453
|
+
*.specification.amd 의 C Code 에 대한 <HeaderBlock> 요소
|
|
454
|
+
|
|
455
|
+
예시 구조 :
|
|
456
|
+
<HeaderBlock>
|
|
457
|
+
# 여기에 header의 Code 삽입
|
|
458
|
+
</HeaderBlock>
|
|
459
|
+
|
|
460
|
+
:param kwargs:
|
|
461
|
+
:return:
|
|
462
|
+
"""
|
|
463
|
+
HeaderBlock = Element("HeaderBlock")
|
|
464
|
+
HeaderBlock.text = kwargs["code"] if "code" in kwargs else ""
|
|
465
|
+
return HeaderBlock
|
|
466
|
+
|
|
467
|
+
@classmethod
|
|
468
|
+
def MethodBody(cls, **kwargs) -> Element:
|
|
469
|
+
"""
|
|
470
|
+
*.specification.amd 의 C Code에 대한 <MethodBody> 요소 및 하위 요소
|
|
471
|
+
|
|
472
|
+
예시 구조:
|
|
473
|
+
<MethodBody methodName="_ABS_ESC_01_10ms" methodOID="_040g1ngg00p91o870o4g81ek53vj6">
|
|
474
|
+
<CodeBlock>
|
|
475
|
+
# 여기에 Code 삽입
|
|
476
|
+
</CodeBlock>
|
|
477
|
+
</MethodBody>
|
|
478
|
+
|
|
479
|
+
:param kwargs:
|
|
480
|
+
:return:
|
|
481
|
+
"""
|
|
482
|
+
MethodBody = Element('MethodBody', methodName=kwargs["methodName"], methodOID=kwargs["methodOID"])
|
|
483
|
+
CodeBlock = Element('CodeBlock')
|
|
484
|
+
CodeBlock.text = kwargs["code"] if "code" in kwargs else ""
|
|
485
|
+
MethodBody.append(CodeBlock)
|
|
486
|
+
return MethodBody
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
class AmdIO(ElementTree):
|
|
491
|
+
|
|
492
|
+
def __init__(self, file:str):
|
|
493
|
+
if file.endswith(".zip"):
|
|
494
|
+
file = AmdSource(file).main
|
|
495
|
+
super().__init__(file=file)
|
|
496
|
+
self.path = file
|
|
497
|
+
self.file = os.path.basename(file)
|
|
498
|
+
self.name = os.path.basename(file).split(".")[0]
|
|
499
|
+
self.type = self.getroot().tag
|
|
500
|
+
|
|
501
|
+
if file.endswith('.main.amd'):
|
|
502
|
+
self.extension = '.main.amd'
|
|
503
|
+
elif file.endswith('.implementation.amd'):
|
|
504
|
+
self.extension = '.implementation.amd'
|
|
505
|
+
elif file.endswith('.data.amd'):
|
|
506
|
+
self.extension = '.data.amd'
|
|
507
|
+
elif file.endswith('.specification.amd'):
|
|
508
|
+
self.extension = '.specification.amd'
|
|
509
|
+
else:
|
|
510
|
+
raise KeyError
|
|
511
|
+
self._ns = {'ns0': 'http://www.w3.org/2000/09/xmldsig#'}
|
|
512
|
+
return
|
|
513
|
+
|
|
514
|
+
def __getitem__(self, item):
|
|
515
|
+
return self.root[item]
|
|
516
|
+
|
|
517
|
+
def __setitem__(self, key, value):
|
|
518
|
+
if key in ['digestValue', 'signatureValue']:
|
|
519
|
+
setattr(self, key, value)
|
|
520
|
+
return
|
|
521
|
+
if key in self.getroot()[0].attrib:
|
|
522
|
+
self.getroot()[0].attrib[key] = value
|
|
523
|
+
else:
|
|
524
|
+
raise KeyError(f"No such attribute: {key}")
|
|
525
|
+
|
|
526
|
+
@property
|
|
527
|
+
def root(self) -> Series:
|
|
528
|
+
"""
|
|
529
|
+
.amd 파일 메타데이터
|
|
530
|
+
|
|
531
|
+
:return:
|
|
532
|
+
"""
|
|
533
|
+
__attr__ = self.getroot()[0].attrib.copy()
|
|
534
|
+
__attr__.update({
|
|
535
|
+
'path': self.path,
|
|
536
|
+
'file': self.file,
|
|
537
|
+
'model': self.name,
|
|
538
|
+
'type': self.type
|
|
539
|
+
})
|
|
540
|
+
return Series(data=__attr__)
|
|
541
|
+
|
|
542
|
+
@property
|
|
543
|
+
def digestValue(self) -> str:
|
|
544
|
+
return self.find('ns0:Signature/ns0:SignedInfo/ns0:Reference/ns0:DigestValue', self._ns).text
|
|
545
|
+
|
|
546
|
+
@digestValue.setter
|
|
547
|
+
def digestValue(self, value: str):
|
|
548
|
+
self.find('ns0:Signature/ns0:SignedInfo/ns0:Reference/ns0:DigestValue', self._ns).text = value
|
|
549
|
+
|
|
550
|
+
@property
|
|
551
|
+
def signatureValue(self) -> str:
|
|
552
|
+
return self.find('ns0:Signature/ns0:SignatureValue', self._ns).text
|
|
553
|
+
|
|
554
|
+
@signatureValue.setter
|
|
555
|
+
def signatureValue(self, value: str):
|
|
556
|
+
self.find('ns0:Signature/ns0:SignatureValue', self._ns).text = value
|
|
557
|
+
|
|
558
|
+
def dataframe(self, tag:str, depth:str='recursive') -> DataFrame:
|
|
559
|
+
df = DataFrame(data=self.datadict(tag, depth=depth))
|
|
560
|
+
df['model'] = self.name
|
|
561
|
+
return df
|
|
562
|
+
|
|
563
|
+
def datadict(self, tag:str, depth:str='recursive') -> List[DataDictionary]:
|
|
564
|
+
data = []
|
|
565
|
+
for elem in self.iter():
|
|
566
|
+
if elem.tag == tag:
|
|
567
|
+
data.append(DataDictionary(tools.xml.to_dict(elem, depth=depth)))
|
|
568
|
+
return data
|
|
569
|
+
|
|
570
|
+
def export(self, path:str=''):
|
|
571
|
+
if not path:
|
|
572
|
+
# path = os.path.join(os.environ['USERPROFILE'], f'Downloads/{self.name}')
|
|
573
|
+
path = os.path.dirname(self.path)
|
|
574
|
+
timestamp = datetime.now().timestamp()
|
|
575
|
+
os.makedirs(path, exist_ok=True)
|
|
576
|
+
os.utime(path, (timestamp, timestamp))
|
|
577
|
+
with open(file=os.path.join(path, f'{self.name}{self.extension}'), mode='w', encoding='utf-8') as f:
|
|
578
|
+
f.write(self.serialize())
|
|
579
|
+
return
|
|
580
|
+
|
|
581
|
+
def export_to_downloads(self):
|
|
582
|
+
self.export(path=os.path.join(os.environ['USERPROFILE'], f'Downloads/{self.name}'))
|
|
583
|
+
return
|
|
584
|
+
|
|
585
|
+
def findParent(self, *elems:Element) -> Dict[Element, Element]:
|
|
586
|
+
parents = []
|
|
587
|
+
for parent in self.iter():
|
|
588
|
+
for child in list(parent):
|
|
589
|
+
if any([id(child) == id(elem) for elem in elems]):
|
|
590
|
+
parents.append(parent)
|
|
591
|
+
return dict(zip(elems, parents))
|
|
592
|
+
|
|
593
|
+
def serialize(self) -> str:
|
|
594
|
+
return tools.xml.to_str(self, xml_declaration=True)
|
|
595
|
+
|
|
596
|
+
def strictFind(self, tag:str='', **attr) -> Union[Element, List[Element]]:
|
|
597
|
+
found = []
|
|
598
|
+
for node in self.iter():
|
|
599
|
+
if tag:
|
|
600
|
+
if not node.tag == tag:
|
|
601
|
+
continue
|
|
602
|
+
|
|
603
|
+
if not attr:
|
|
604
|
+
found.append(node)
|
|
605
|
+
continue
|
|
606
|
+
|
|
607
|
+
if all([node.attrib[key] == val for key, val in attr.items()]):
|
|
608
|
+
found.append(node)
|
|
609
|
+
if len(found) == 1:
|
|
610
|
+
return found[0]
|
|
611
|
+
return found
|
|
612
|
+
|
|
613
|
+
def replace(self, tag:str='', attr_name:str='', attr_value:dict=None):
|
|
614
|
+
if attr_value is None:
|
|
615
|
+
return
|
|
616
|
+
|
|
617
|
+
for elem in self.iter():
|
|
618
|
+
if tag and elem.tag != tag:
|
|
619
|
+
continue
|
|
620
|
+
if not attr_name in elem.attrib:
|
|
621
|
+
continue
|
|
622
|
+
if not elem.attrib[attr_name] in attr_value:
|
|
623
|
+
continue
|
|
624
|
+
elem.attrib[attr_name] = attr_value[elem.attrib[attr_name]]
|
|
625
|
+
return
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
class Amd:
|
|
630
|
+
def __init__(self, file:str):
|
|
631
|
+
sc = AmdSource(file)
|
|
632
|
+
self.name = sc.name
|
|
633
|
+
self.main = AmdIO(sc.main)
|
|
634
|
+
self.impl = AmdIO(sc.impl)
|
|
635
|
+
self.data = AmdIO(sc.data)
|
|
636
|
+
self.spec = AmdIO(sc.spec)
|
|
637
|
+
return
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
# Alias
|
|
641
|
+
AmdSC = AmdSource
|
|
642
|
+
AmdEL = AmdElements
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
if __name__ == "__main__":
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
tester = r'D:\ETASData\ASCET6.1\Export\ComDef\ComDef.main.amd'
|
|
649
|
+
amd = AmdIO(tester)
|
|
650
|
+
# print(amd.root)
|
|
651
|
+
# print(amd.serialize())
|
|
652
|
+
# print(amd.export())
|
|
653
|
+
print("*"*100)
|
|
654
|
+
print(amd.digestValue)
|
|
655
|
+
print(amd.signatureValue)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
# e = amd.strictFind('DataEntry', elementName="ABS_ActvSta_Can")
|
|
659
|
+
# print(tools.xml.to_str(e, tools.xml_declaration=False))
|
|
660
|
+
# parent = amd.findParent(e)
|
|
661
|
+
# print(tools.xml.to_str(parent[e]))
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
# amd.remove('DataEntry', elementName="CF_Ems_ActPurMotStat_VB_Ems")
|
|
665
|
+
# amd.remove('DataEntry')
|
|
666
|
+
# amd.remove(elementName='CF_Ems_ActPurEngOnReq_VB_Ems')
|
|
667
|
+
# print(amd.dom)
|
|
668
|
+
|
|
669
|
+
# amd.append(Element('Element', name='test', ))
|
|
670
|
+
# print(amd.dom)
|
|
671
|
+
|
|
672
|
+
# attr = DataDictionary(
|
|
673
|
+
# name='tester',
|
|
674
|
+
# OID="",
|
|
675
|
+
# # modelType='complex',
|
|
676
|
+
# # basicModelType='class',
|
|
677
|
+
# # modelType='scalar',
|
|
678
|
+
# # basicModelType='cont',
|
|
679
|
+
# modelType='array',
|
|
680
|
+
# basicModelType='udisc',
|
|
681
|
+
# scope='local',
|
|
682
|
+
#
|
|
683
|
+
# componentName="classTester",
|
|
684
|
+
# componentID="",
|
|
685
|
+
# implementationName='Impl',
|
|
686
|
+
# implementationOID="",
|
|
687
|
+
# kind='message',
|
|
688
|
+
# quantization="0",
|
|
689
|
+
# formula="Test_Formula",
|
|
690
|
+
#
|
|
691
|
+
# maxSizeX='8'
|
|
692
|
+
#
|
|
693
|
+
# )
|
|
694
|
+
#
|
|
695
|
+
# e = AmdElements.Element(**attr)
|
|
696
|
+
# e = ImplementationEntry(**attr)
|
|
697
|
+
# e = DataEntry(**attr)
|
|
698
|
+
# print(tools.xml.to_str(e, tools.xml_declaration=False))
|