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.
Files changed (58) hide show
  1. cannect/__init__.py +30 -0
  2. cannect/api/__init__.py +0 -0
  3. cannect/config.py +114 -0
  4. cannect/core/__init__.py +0 -0
  5. cannect/core/ascet/__init__.py +3 -0
  6. cannect/core/ascet/amd.py +698 -0
  7. cannect/core/ascet/formula.py +33 -0
  8. cannect/core/ascet/oid.py +29 -0
  9. cannect/core/ascet/ws.py +154 -0
  10. cannect/core/can/__init__.py +2 -0
  11. cannect/core/can/ascet/__init__.py +3 -0
  12. cannect/core/can/ascet/_db2code.py +344 -0
  13. cannect/core/can/ascet/_db2elem.py +399 -0
  14. cannect/core/can/ascet/comdef.py +256 -0
  15. cannect/core/can/ascet/comrx.py +139 -0
  16. cannect/core/can/ascet/diag.py +691 -0
  17. cannect/core/can/db/__init__.py +4 -0
  18. cannect/core/can/db/reader.py +148 -0
  19. cannect/core/can/db/schema.py +269 -0
  20. cannect/core/can/db/specification/__init__.py +0 -0
  21. cannect/core/can/db/specification/message.py +230 -0
  22. cannect/core/can/db/specification/styles.py +81 -0
  23. cannect/core/can/db/specification/wrapper.py +161 -0
  24. cannect/core/can/db/vcs.py +104 -0
  25. cannect/core/can/rule.py +229 -0
  26. cannect/core/can/testcase/__init__.py +0 -0
  27. cannect/core/can/testcase/unitcase/__init__.py +0 -0
  28. cannect/core/can/testcase/unitcase/asw2can.py +48 -0
  29. cannect/core/can/testcase/unitcase/decode.py +63 -0
  30. cannect/core/can/testcase/unitcase/diagnosis.py +479 -0
  31. cannect/core/can/testcase/unitcase/encode.py +60 -0
  32. cannect/core/ir/__init__.py +2 -0
  33. cannect/core/ir/changehistory.py +310 -0
  34. cannect/core/ir/delivereables.py +71 -0
  35. cannect/core/ir/diff.py +97 -0
  36. cannect/core/ir/ir.py +581 -0
  37. cannect/core/ir/sdd.py +148 -0
  38. cannect/core/mdf.py +66 -0
  39. cannect/core/subversion.py +136 -0
  40. cannect/core/testcase/__init__.py +3 -0
  41. cannect/core/testcase/plotter.py +181 -0
  42. cannect/core/testcase/style.py +981 -0
  43. cannect/core/testcase/testcase.py +160 -0
  44. cannect/core/testcase/unitcase.py +227 -0
  45. cannect/errors.py +20 -0
  46. cannect/schema/__init__.py +5 -0
  47. cannect/schema/candb.py +226 -0
  48. cannect/schema/datadictionary.py +60 -0
  49. cannect/utils/__init__.py +3 -0
  50. cannect/utils/excel.py +29 -0
  51. cannect/utils/logger.py +81 -0
  52. cannect/utils/ppt.py +236 -0
  53. cannect/utils/tools.py +207 -0
  54. cannect-1.0.0.dist-info/METADATA +214 -0
  55. cannect-1.0.0.dist-info/RECORD +58 -0
  56. cannect-1.0.0.dist-info/WHEEL +5 -0
  57. cannect-1.0.0.dist-info/licenses/LICENSE +21 -0
  58. 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))