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,691 @@
1
+ from cannect.config import env
2
+ from cannect.core.ascet.amd import Amd
3
+ from cannect.core.ascet.oid import generateOID
4
+ from cannect.core.can.db.reader import CANDBReader
5
+ from cannect.core.can.ascet._db2code import INFO
6
+ from cannect.core.can.rule import naming
7
+ from cannect.utils import tools
8
+ from cannect.utils.logger import Logger
9
+
10
+ from typing import Dict, Tuple
11
+ from xml.etree.ElementTree import Element
12
+ import os, copy
13
+
14
+ from cannect.utils.tools import path_abbreviate
15
+
16
+
17
+ class CANDiag(Amd):
18
+
19
+ def __init__(self, db: CANDBReader, src: str, *messages, **kwargs):
20
+
21
+ for message in messages:
22
+ if not message in db.messages:
23
+ raise KeyError(f'{message} NOT EXIST IN CAN DB.')
24
+
25
+ template = env.SVN_CAN / "CAN_Model/_29_CommunicationVehicle/StandardDB/StandardTemplate/CANDiagTmplt/CANDiagTmplt.main.amd"
26
+ super().__init__(str(template))
27
+
28
+ self.dsm = env.SVN_MODEL / "HMC_ECU_Library/HMC_DiagLibrary/DSM_Types"
29
+
30
+ # LOGGER 생성
31
+ base = Amd(src)
32
+
33
+ self.logger = Logger()
34
+ self.logger(f"%{{{base.name}}} MODEL GENERATION")
35
+ self.logger(f">>> DB VERSION: {db.revision}")
36
+ self.logger(f">>> BASE MODEL: {tools.path_abbreviate(src)}")
37
+ if "revision" in kwargs:
38
+ self.logger(f">>> MODEL REVISION: {kwargs['revision']}")
39
+
40
+ # @self.n : 메시지 순번
41
+ # @self.db : CAN DB 객체
42
+ # @self.messages : 메시지 이름 리스트
43
+ self.n, self.db, self.messages = 1, db, list(messages)
44
+
45
+ # @self.tx : 송출처 이름(Legacy); ABS, BMS, TCU, ...
46
+ # @self.hw : 차량 프로젝트 타입; HEV, ICE
47
+ # @self.cal: Default Cal. 데이터(값)
48
+ self.tx, self.hw, self.cal = self.read_from_basemodel(base)
49
+
50
+ self.manual_instruction = []
51
+ self.base = base
52
+ return
53
+
54
+ @classmethod
55
+ def read_from_basemodel(cls, base: Amd) -> Tuple[str, str, Dict]:
56
+ """
57
+ BASE 모델의 송출처, 타입, Cal. 값 읽기
58
+ """
59
+ tx = base.name \
60
+ .replace("_HEV", "") \
61
+ .replace("_48V", "") \
62
+ .replace("CanFD", "") \
63
+ .replace("CanHS", "") \
64
+ .replace("Can", "")[:-1]
65
+ hw = "HEV" if "HEV" in base.name else "ICE"
66
+ cal = {}
67
+ for elem in base.main.iter('Element'):
68
+ attr = elem.find('ElementAttributes/ScalarType/PrimitiveAttributes')
69
+ if attr is not None:
70
+ if attr.get('scope', '') == 'imported':
71
+ continue
72
+ if attr.get('kind', '') == 'parameter':
73
+ data = base.data.strictFind('DataEntry', elementName=elem.get('name'))
74
+ cal[elem.get('name')] = list(data.iter('Numeric'))[0].get('value')
75
+ return tx, hw, cal
76
+
77
+ def copy_from_basemodel(self, base: Amd):
78
+ """
79
+ BASE 모델의 기본 정보들을 CANDiag으로 복사
80
+ """
81
+ log = ''
82
+
83
+ # [ 모델 Comment 생성 ]
84
+ # <ComponentMain toolVersion="V6.1.0-Win10" schemaVersion="6.1.0.0">
85
+ # <Component * >
86
+ # <TimeStamp * />
87
+ # <Comment>{ 이 자리의 Comment 생성 }</Comment>
88
+ # </Component>
89
+ # ...
90
+ # </ComponentMain>
91
+ message_list = "[MESSAGE LIST]\n- " + "\n- ".join(self.messages)
92
+ self.main.find('Component/Comment').text = f"{INFO(self.db.revision)}{message_list}"
93
+
94
+ # [ Base 모델의 *.main.amd 기본 정보 복사 ]
95
+ # <ComponentMain toolVersion="V6.1.0-Win10" schemaVersion="6.1.0.0">
96
+ # <Component { 이 자리의 Attribute 복사 } >
97
+ # ...
98
+ # </Component>
99
+ # ...
100
+ # </ComponentMain>
101
+ self.name = self.main.name = base.main['name']
102
+ self.main['name'] = base.main['name']
103
+ self.main['nameSpace'] = base.main['nameSpace']
104
+ self.main['OID'] = base.main['OID']
105
+ try:
106
+ self.main['defaultProjectName'] = base.main['defaultProjectName']
107
+ self.main['defaultProjectOID'] = base.main['defaultProjectOID']
108
+ except KeyError:
109
+ self.main['defaultProjectName'] = f'{self.name}_DEFAULT'
110
+ self.main.digestValue = base.main.digestValue
111
+ self.main.signatureValue = base.main.signatureValue
112
+
113
+
114
+ # [ Base 모델의 *.main.amd Method OID 정보 복사 ]
115
+ # 대상 Method: ▲_100msRun ▲_Init ▲_fcmclr ▲_EEPRes ▲ClearDTCFilter ▲SetDTCFilter
116
+ # Base 모델의 예외적 Method는 차후 처리한다.
117
+ # <ComponentMain toolVersion="V6.1.0-Win10" schemaVersion="6.1.0.0">
118
+ # <Component * >
119
+ # ...
120
+ # <Elements>
121
+ # ...
122
+ # </Elements>
123
+ # <MethodSignatures>
124
+ # <MethodSignature name="_100msRun" OID="{ 이 자리의 OID 복사 }" />
125
+ # ...
126
+ # </MethodSignatures>
127
+ # </Component>
128
+ # ...
129
+ # </ComponentMain>
130
+ base_method = base.main.dataframe('MethodSignature', depth='shallow')[['name', 'OID']]
131
+ base_method = dict(zip(base_method['name'], base_method['OID']))
132
+ for method in self.main.iter('MethodSignature'):
133
+ if method.get('name') in base_method:
134
+ method.set('OID', base_method[method.get('name')])
135
+
136
+ # *.spec.amd
137
+ for elem in self.spec.iter():
138
+ if elem.get('methodName', '') in base_method:
139
+ if 'methodOID' in elem.attrib:
140
+ elem.set('methodOID', base_method[elem.get('methodName')])
141
+
142
+ # CANDiag에 정의되지 않은 MethodSignature 정보 복사
143
+ main_method = [elem.get('name') for elem in self.main.iter('MethodSignature')]
144
+ for elem in base.main.iter('MethodSignature'):
145
+ if not elem.get('name') in main_method:
146
+ if len(elem):
147
+ elem.remove(elem[0])
148
+ self.main.strictFind('MethodSignatures').append(elem)
149
+ self.spec.strictFind('MethodBodies').append(
150
+ Element('MethodBody', methodName=elem.get('name'), methodOID=elem.get('OID'))
151
+ # Element('MethodBody', methodName=elem.get('name'))
152
+ )
153
+
154
+ # *.implementation.amd 파일 정보 복사
155
+ self.impl.name = base.main['name']
156
+ self.impl['name'] = base.impl['name']
157
+ self.impl['OID'] = base.impl['OID']
158
+ self.impl.strictFind('ImplementationSet', name='Impl').attrib['OID'] = \
159
+ base.impl.strictFind('ImplementationSet', name='Impl').attrib['OID']
160
+ self.impl.digestValue = base.impl.digestValue
161
+ self.impl.signatureValue = base.impl.signatureValue
162
+
163
+ # *.data.amd 파일 정보 복사
164
+ self.data.name = base.main['name']
165
+ self.data['name'] = base.data['name']
166
+ self.data['OID'] = base.data['OID']
167
+ self.data.strictFind('DataSet', name='Data').attrib['OID'] = \
168
+ base.data.strictFind('DataSet', name='Data').attrib['OID']
169
+ self.data.digestValue = base.data.digestValue
170
+ self.data.signatureValue = base.data.signatureValue
171
+
172
+ # *.specification.amd 파일 정보 복사
173
+ # BREAK 구문 존재 시, Hierarchy 및 관련 Element 복사
174
+ self.spec.name = base.main['name']
175
+ self.spec.digestValue = base.spec.digestValue
176
+ self.spec.signatureValue = base.spec.signatureValue
177
+
178
+ breaker = None
179
+ for elem in base.spec.iter():
180
+ if len(elem) and elem[0].tag != "Hierarchy":
181
+ continue
182
+ for _elem in elem.iter():
183
+ if _elem.tag == 'Control' and _elem.get('type') == 'Break':
184
+ breaker = copy.deepcopy(elem)
185
+
186
+ if breaker is not None:
187
+ log = f'COPY BREAK HIERARCHY'
188
+ elements = [elem.get('elementName') for elem in breaker.iter() if elem.get('elementName')]
189
+ for elem in base.main.iter('Element'):
190
+ if elem.get('name') in elements:
191
+ self.main.strictFind('Elements').append(elem)
192
+ breaker[0].attrib['name'] = 'Break'
193
+ breaker[0].find('Size').attrib['x'] = "40"
194
+ breaker[0].find('Size').attrib['y'] = "40"
195
+ breaker[0].find('Position').attrib['x'] = "110"
196
+ breaker[0].find('Position').attrib['y'] = "280"
197
+ breaker[0].find('LabelPosition').attrib['x'] = "110"
198
+ breaker[0].find('LabelPosition').attrib['y'] = "260"
199
+ self.spec.find('Specification/BlockDiagramSpecification/DiagramElements').append(breaker)
200
+ return log
201
+
202
+ def copy_common(self):
203
+ pascal = self.tx.lower().capitalize()
204
+ if self.tx.lower() == "nox":
205
+ pascal = "NOx"
206
+ if self.tx.lower() == "frcmr":
207
+ pascal = "FrCmr"
208
+ renamer = {
209
+ f"CanD_cEnaDetBus1__TX_Pascal__": f"CanD_cEnaDetBus1{pascal}",
210
+ f"CanD_cEnaDetBus2__TX_Pascal__": f"CanD_cEnaDetBus2{pascal}",
211
+ f"CanD_cEnaDetBus3__TX_Pascal__": f"CanD_cEnaDetBus3{pascal}",
212
+ f"CanD_ctDet__TX_Pascal___C": f"CanD_ctDet{pascal}_C",
213
+ f"CanD_RstEep__TX_Pascal___C": f"CanD_RstEep{pascal}_C",
214
+ f"CanD_tiMonDet__TX_Pascal___C": f"CanD_tiMonDet{pascal}_C",
215
+ f"Cfg_Can__TX_UPPER__D_C": f"Cfg_CanFD{self.tx.upper()}D_C"
216
+ }
217
+ if self.name.endswith("_48V") or self.tx.upper() in ["CVVD", "FPCM"]:
218
+ renamer[f"Cfg_Can__TX_UPPER__D_C"] = f"Cfg_Can{self.tx.upper()}D_C"
219
+ if self.tx.upper() == "NOX":
220
+ renamer[f"Cfg_Can__TX_UPPER__D_C"] = f"Cfg_Can{pascal}D_C"
221
+
222
+
223
+ self.main.replace('Element', 'name', renamer)
224
+ self.impl.replace('', 'elementName', renamer)
225
+ self.data.replace('', 'elementName', renamer)
226
+ self.spec.replace('', 'elementName', renamer)
227
+ for elem in self.main.iter('Element'):
228
+ if "__TX_UPPER__" in str(elem.find('Comment').text):
229
+ elem.find('Comment').text = elem.find('Comment').text \
230
+ .replace("__TX_UPPER__", self.tx.upper())
231
+ return
232
+
233
+ def copy_by_message(self, n:int, message:str):
234
+ log = ''
235
+
236
+ db = self.db.messages[message]
237
+ nm = naming(message, self.hw)
238
+
239
+ # 메시지 채널 결정
240
+ chn = '1'
241
+ if "_48V" in self.name:
242
+ chn = '2'
243
+ if (str(nm) == "FPCM_01_100ms") or str(nm).startswith("CVVD"):
244
+ chn = '3'
245
+
246
+ # 메시지 진단 타입 별 Hierarchy 복사 및 치환
247
+ cp = "YY"
248
+ if not db.hasCrc():
249
+ cp = f"N{cp[1]}"
250
+ log += f'NO CRC(DB) / '
251
+ if db['Send Type'] == 'PE':
252
+ cp = f"{cp[0]}N"
253
+ log += f'NO A/C(PE TYPE) / '
254
+ if not db.hasAliveCounter():
255
+ cp = f"{cp[0]}N"
256
+ log += f'NO A/C(DB) / '
257
+
258
+ # 메시지 주기에 따른 Method 정의/교체
259
+ method = self.main.dataframe('MethodSignature', depth='shallow')[['name', 'OID']]
260
+ method = dict(zip(method['name'], method['OID']))
261
+ method_name = f"_{int(max(db['Cycle Time'], 100))}msRun"
262
+ if method_name in method:
263
+ method_oid = method[method_name]
264
+ else:
265
+ method_oid = generateOID(1)
266
+ new_method = copy.deepcopy(self.main.strictFind('MethodSignature', name='___M1_Task__msRun'))
267
+ new_method.set('name', method_name)
268
+ new_method.set('OID', method_oid)
269
+ self.main.strictFind('MethodSignatures').append(new_method)
270
+ new_method = Element('MethodBody', methodName=method_name)
271
+ self.spec.strictFind('MethodBodies').append(new_method)
272
+ self.manual_instruction.append(f'ADD IR/OS-TASK: {method_name}')
273
+
274
+ # 메시지 순서 별 변수 이름 재정의
275
+ pascal = self.tx.lower().capitalize()
276
+ if self.tx.lower() == "nox":
277
+ pascal = "NOx"
278
+ replace_name = {
279
+ f'CanD_cEnaDiagBus__M1_Chn__': f'CanD_cEnaDiagBus{chn}',
280
+ f'CanD_cEnaDetBus__M1_Chn____TX_Pascal__': f'CanD_cEnaDetBus{chn}{pascal}',
281
+ f"CanD_cEnaDiag__M1_Pascal__": nm.diagnosisEnable,
282
+ f"CanD_cEnaDet__M1_Pascal__": nm.detectionEnable,
283
+ f"CanD_cErr__M1_Pascal__Msg": nm.diagnosisMsg,
284
+ f"CanD_ctDet__M1_Pascal__": nm.detectionCounter,
285
+ f"CanD_stRdEep__M1_Pascal__": nm.eepReader,
286
+ f"CanD_tiFlt__M1_Pascal___C": nm.debounceTime,
287
+ f"CanD_tiFlt__M1_Pascal__Msg": nm.debounceTimerMsg,
288
+ f"DEve_FD__M1_Pascal__Msg": nm.deveMsg,
289
+ f"EEP_FD__M1_UPPER__": nm.eepIndex,
290
+ f"EEP_stFD__M1_UPPER__": nm.eep,
291
+ f"FD_cVld__M1_Pascal__Msg": nm.messageCountValid,
292
+ f"Fid_FD__M1_UPPER__D": nm.fid
293
+ }
294
+
295
+ if cp[0] == 'Y':
296
+ replace_name.update({
297
+ f"CanD_cErr__M1_Pascal__Crc": nm.diagnosisCrc,
298
+ f"CanD_tiFlt__M1_Pascal__Crc": nm.debounceTimerCrc,
299
+ f"DEve_FD__M1_Pascal__Crc": nm.deveCrc,
300
+ f"FD_cVld__M1_Pascal__Crc": nm.crcValid,
301
+ })
302
+ if cp[1] == 'Y':
303
+ replace_name.update({
304
+ f"CanD_cErr__M1_Pascal__Alv": nm.diagnosisAlv,
305
+ f"CanD_tiFlt__M1_Pascal__Alv": nm.debounceTimerAlv,
306
+ f"DEve_FD__M1_Pascal__Alv": nm.deveAlv,
307
+ f"FD_cVld__M1_Pascal__Alv": nm.aliveCountValid,
308
+ })
309
+ replace_oid = {}
310
+
311
+ # *.main.amd 요소 복사 및 치환
312
+ main = self.main.strictFind('Elements')
313
+ for pre, cur in replace_name.items():
314
+ from_template = self.main.strictFind('Element', name=pre)
315
+ required = self.main.strictFind('Element', name=cur)
316
+ if not len(required):
317
+ replace_oid[from_template.get('OID')] = oid = generateOID(1)
318
+ copied = copy.deepcopy(from_template)
319
+ copied.set('name', cur)
320
+ copied.set('OID', oid)
321
+ if "__M1_NAME__" in str(copied.find('Comment').text):
322
+ copied.find('Comment').text = copied.find('Comment').text \
323
+ .replace("__M1_NAME__", str(nm))
324
+ main.append(copied)
325
+ else:
326
+ replace_oid[from_template.get('OID')] = required.get('OID')
327
+
328
+ # *.implementation.amd 파일의 ImplementationEntry 요소를 복사
329
+ # global 변수와 local 변수를 구분해서 복사함
330
+ impl_global = self.impl.strictFind('ImplementationSet', name=self.name)
331
+ impl_local = self.impl.strictFind('ImplementationSet', name='Impl')
332
+ for pre, cur in replace_name.items():
333
+ if self.impl.strictFind('elementName', name=cur):
334
+ continue
335
+
336
+ elem = self.main.strictFind('Element', name=cur)
337
+ attr = elem.find('ElementAttributes/ScalarType/PrimitiveAttributes')
338
+ if attr is not None and attr.get('scope') == 'imported':
339
+ continue
340
+
341
+ copied = copy.deepcopy(self.impl.strictFind('ElementImplementation', elementName=pre))
342
+ copied.set('elementName', cur)
343
+ copied.set('elementOID', elem.get('OID'))
344
+ impl = Element('ImplementationEntry')
345
+ impl.append(Element('ImplementationVariant', name='default'))
346
+ impl[0].append(copied)
347
+ if "EEP" in cur:
348
+ impl_global.append(impl)
349
+ else:
350
+ impl_local.append(impl)
351
+
352
+ # *.data.amd 파일의 DataEntry 요소를 복사
353
+ # global 변수와 local 변수를 구분해서 복사함
354
+ data_global = self.data.strictFind('DataSet', name=self.name)
355
+ data_local = self.data.strictFind('DataSet', name='Data')
356
+ for pre, cur in replace_name.items():
357
+ if self.data.strictFind('elementName', name=cur):
358
+ continue
359
+
360
+ elem = self.main.strictFind('Element', name=cur)
361
+ attr = elem.find('ElementAttributes/ScalarType/PrimitiveAttributes')
362
+ if attr is not None and attr.get('scope') == 'imported':
363
+ continue
364
+
365
+ copied = copy.deepcopy(self.data.strictFind('DataEntry', elementName=pre))
366
+ copied.set('elementName', cur)
367
+ copied.set('elementOID', elem.get('OID'))
368
+ if "EEP" in cur:
369
+ data_global.append(copied)
370
+ else:
371
+ data_local.append(copied)
372
+
373
+ if str(nm) == "FPCM_01_100ms":
374
+ template = copy.deepcopy(self.spec.strictFind('Hierarchy', name=f'__FPCM_01_100ms__NN'))
375
+ else:
376
+ template = copy.deepcopy(self.spec.strictFind('Hierarchy', name=f'__M1_NAME__{cp}'))
377
+ offset = max([int(tag.attrib.get('graphicOID', '0')) for tag in template.iter()])
378
+ template.set('graphicOID', str(int(template.get('graphicOID')) + (n - 1) * offset))
379
+ template.set('name', f'{nm}')
380
+ if n <= 5:
381
+ template.find("Position").set('x', "260")
382
+ template.find("Position").set('y', str(100 + (n - 1) * 90))
383
+ template.find("LabelPosition").set('x', "260")
384
+ template.find("LabelPosition").set('y', str(80 + (n - 1) * 90))
385
+ else:
386
+ template.find("Position").set('x', "450")
387
+ template.find("Position").set('y', str(100 + (n - 6) * 90))
388
+ template.find("LabelPosition").set('x', "450")
389
+ template.find("LabelPosition").set('y', str(80 + (n - 6) * 90))
390
+
391
+ for elem in template.iter():
392
+ if elem.tag == 'SequenceCall':
393
+ if elem.get('methodName', '') == '_Init':
394
+ elem.set('sequenceNumber', str(int(elem.get('sequenceNumber')) + (n - 1) * 2))
395
+ if elem.get('methodName', '') == '_fcmclr':
396
+ elem.set('sequenceNumber', str(int(elem.get('sequenceNumber')) + (n - 1) * 3))
397
+ if elem.get('methodName', '') == '_EEPRes':
398
+ elem.set('sequenceNumber', str(int(elem.get('sequenceNumber')) + (n - 1) * 1))
399
+ if elem.get('methodName', '') == '___M1_Task__msRun':
400
+ elem.set('sequenceNumber', str(int(elem.get('sequenceNumber')) + (n - 1) * 10))
401
+ elem.set('methodName', method_name)
402
+ elem.set('methodOID', method_oid)
403
+ if elem.get('methodName', '') == '_100msRun':
404
+ elem.set('sequenceNumber', str(int(elem.get('sequenceNumber')) + (n - 1) * 10))
405
+ if elem.tag == "Literal" and elem.get("value", "") == "__M1__":
406
+ elem.set('value', str(n - 1))
407
+ if elem.tag == "Text" and elem.text == "__M1_Comment__":
408
+ elem.text = f"""[ {nm} ]
409
+ ID : {db['ID']}
410
+ PERIOD : {db['Cycle Time']}
411
+ SEND TYPE : {db['Send Type']}
412
+ CHANNEL : {db[f'{self.hw} Channel']}-CAN
413
+ - DIAG.CRC : {db.hasCrc()}
414
+ - DIAG.A/C : {db.hasAliveCounter()}"""
415
+ if elem.get('elementName', '') in replace_name:
416
+ elem.set('elementName', replace_name[elem.get('elementName')])
417
+ if elem.get('elementOID', '') in replace_oid:
418
+ elem.set('elementOID', replace_oid[elem.get('elementOID')])
419
+
420
+ diagram = Element('DiagramElement')
421
+ diagram.append(template)
422
+ self.spec.find('Specification/BlockDiagramSpecification/DiagramElements').append(diagram)
423
+ return log
424
+
425
+ def clear(self):
426
+ # CANDiag Hierarchy 제거
427
+ removals = []
428
+ for elem in self.spec.iter():
429
+ try:
430
+ if elem[0].get('name').startswith('__M1_NAME__') or \
431
+ elem[0].get('name') == '__FPCM_01_100ms__NN':
432
+ removals.append(elem)
433
+ except (AttributeError, IndexError, KeyError):
434
+ continue
435
+ for diagram in removals:
436
+ self.spec.find('Specification/BlockDiagramSpecification/DiagramElements').remove(diagram)
437
+
438
+ # CANDiag Method 제거
439
+ removals = []
440
+ for elem in self.main.iter('MethodSignature'):
441
+ if elem.get('name', '').startswith('___M1_Task__msRun'):
442
+ removals.append(elem)
443
+ for elem in removals:
444
+ self.main.strictFind('MethodSignatures').remove(elem)
445
+ self.spec.strictFind('MethodBodies') \
446
+ .remove(self.spec.strictFind('MethodBody', methodName=elem.get('name')))
447
+
448
+ used = []
449
+ for tag in self.spec.iter():
450
+ if 'elementName' in tag.attrib:
451
+ used.append(tag.get('elementName'))
452
+
453
+ # CANDiag Element 제거
454
+ removals = []
455
+ for elem in self.main.iter('Element'):
456
+ if not elem.get('name') in used:
457
+ removals.append(elem)
458
+ for elem in removals:
459
+ self.main.strictFind('Elements').remove(elem)
460
+
461
+ for name in [self.name, 'Impl']:
462
+ impl = self.impl.strictFind('ImplementationSet', name=name)
463
+ removals = []
464
+ for elem in impl.iter('ImplementationEntry'):
465
+ if not elem[0][0].get('elementName') in used:
466
+ removals.append(elem)
467
+ # if '__M1_' in elem[0][0].get('elementName', ''):
468
+ # removals.append(elem)
469
+ for elem in removals:
470
+ impl.remove(elem)
471
+
472
+ for name in [self.name, 'Data']:
473
+ data = self.data.strictFind('DataSet', name=name)
474
+ removals = []
475
+ for elem in data.iter('DataEntry'):
476
+ if not elem.get('elementName') in used:
477
+ # if '__M1_' in elem.get('elementName', ''):
478
+ removals.append(elem)
479
+ for elem in removals:
480
+ data.remove(elem)
481
+ return
482
+
483
+ def copy_dsm(self):
484
+ fid_md = Amd(os.path.join(self.dsm, "Fid_Typ.zip"))
485
+ fid = fid_md.impl.dataframe("ImplementationSet", depth="shallow").set_index("name")["OID"]
486
+ deve_md = Amd(os.path.join(self.dsm, "DEve_Typ.zip"))
487
+ deve = deve_md.impl.dataframe("ImplementationSet", depth="shallow").set_index("name")["OID"]
488
+
489
+ for elem in self.impl.iter('ElementImplementation'):
490
+ name = elem.attrib['elementName']
491
+ if name.startswith("DEve"):
492
+ impl_name = name.replace("DEve_", "") + "_DEve"
493
+ lib = deve
494
+
495
+ elif name.startswith("Fid"):
496
+ impl_name = name.replace("Fid_", "") + "_Fid"
497
+ lib = fid
498
+ else:
499
+ continue
500
+
501
+ # MANUAL EXCEPTION CASE
502
+ if self.hw == "ICE":
503
+ if "AbsEsc" in impl_name:
504
+ impl_name = impl_name.replace("AbsEsc", "Abs")
505
+ elif "HFEOP" in impl_name.upper():
506
+ impl_name = impl_name.replace("L", "")
507
+ if impl_name.endswith("Msg_DEve"):
508
+ impl_name = impl_name.replace("DEve", "Deve")
509
+ elif "IlcuRh01" in impl_name:
510
+ impl_name = impl_name.replace("Ilcu", "ILcu")
511
+ elif self.name.endswith("_48V"):
512
+ impl_name = impl_name.replace("FD", "Can") \
513
+ .replace("State", "") \
514
+ .replace("STATE", "") \
515
+ .replace("Crc", "Chks")
516
+ elif self.name == "CanCVVDD":
517
+ impl_name = impl_name.replace("FD", "Can") \
518
+ .replace("Crc", "CRC")
519
+ else:
520
+ if impl_name.replace("0", "") in lib.index:
521
+ impl_name = impl_name.replace("0", "")
522
+
523
+ if not impl_name in lib.index:
524
+ self.manual_instruction.append(f'DSM MISSING: {impl_name}')
525
+ continue
526
+ elem[0].attrib.update({
527
+ "implementationName": impl_name,
528
+ "implementationOID": lib[impl_name]
529
+ })
530
+ return
531
+
532
+ def copy_data(self):
533
+ for data in self.data.iter('DataEntry'):
534
+ if data.attrib.get('elementName', '') in self.cal:
535
+ numeric = list(data.iter('Numeric'))[0]
536
+ numeric.attrib['value'] = self.cal[data.attrib.get('elementName', '')]
537
+ return
538
+
539
+ def exception(self):
540
+
541
+ def _change_attr(element_name: str, **change_attr):
542
+ for elem in self.main.iter('Element'):
543
+ if elem.attrib.get('name', '') == element_name:
544
+ attr = list(elem.iter('PrimitiveAttributes'))[0]
545
+ attr.attrib.update(change_attr)
546
+
547
+ if change_attr.get('scope', '') == 'exported':
548
+ objs = []
549
+ for elem in self.impl.strictFind('ImplementationSet', name="Impl"):
550
+ ei = list(elem.iter('ElementImplementation'))
551
+ if ei and ei[0].attrib['elementName'] == element_name:
552
+ self.impl.strictFind('ImplementationSet', name=self.name).append(elem)
553
+ objs.append(elem)
554
+ for obj in objs:
555
+ self.impl.strictFind('ImplementationSet', name="Impl").remove(obj)
556
+
557
+ objs = []
558
+ for elem in self.data.strictFind('DataSet', name="Data"):
559
+ if elem.attrib.get('elementName', '') == element_name:
560
+ self.data.strictFind('DataSet', name=self.name).append(elem)
561
+ objs.append(elem)
562
+ for obj in objs:
563
+ self.data.strictFind('DataSet', name="Data").remove(obj)
564
+ return
565
+
566
+ if "_48V" in self.name:
567
+ self.logger(f'>>> ... CHANGING DETECTION ENABLE SCOPE')
568
+ detection = []
569
+ for elem in self.main.iter('Element'):
570
+ if elem.attrib['name'].startswith(f'CanD_cEnaDet{self.tx.lower().capitalize()}'):
571
+ detection.append(elem.attrib['name'])
572
+ for var in detection:
573
+ _change_attr(var, kind='message', scope='exported')
574
+
575
+ elif self.name == "CanFDESCD":
576
+ self.logger(f'>>> ... CHANGING DETECTION ENABLE SCOPE')
577
+ rename = {'Cfg_FDESCD_C': 'Cfg_CanFDESCD_C'}
578
+ self.main.replace('Element', 'name', rename)
579
+ self.impl.replace('', 'elementName', rename)
580
+ self.data.replace('', 'elementName', rename)
581
+ self.spec.replace('', 'elementName', rename)
582
+
583
+ _change_attr('CanD_cEnaDetEsc04', kind='message', scope='exported')
584
+ _change_attr('Cfg_CanFDESCD_C', scope='exported')
585
+ _change_attr('CanD_tiMonDetEsc_C', scope='exported')
586
+ else:
587
+ self.logger(f'>>> ... NO EXCEPTION FOUND')
588
+ return
589
+
590
+ def create(self, path:str=''):
591
+
592
+ # BASE 모델의 기본 정보들을 CANDiag으로 복사
593
+ self.logger('>>> COPY BASE MODEL TO TEMPLATE')
594
+ log = self.copy_from_basemodel(self.base)
595
+ if log: self.logger(f'>>> ... {log}')
596
+
597
+ # 공용 변수 Naming Rule 적용
598
+ self.copy_common()
599
+
600
+ # 메시지별 템플릿 적용
601
+ self.logger(f'>>> GENERATE HIERARCHY BY MESSAGES N={len(self.messages)}')
602
+ for n, message in enumerate(self.messages, start=1):
603
+ log = self.copy_by_message(n, message)
604
+ self.logger(f'>>> ... [{n} / {len(self.messages)}] {message}: {log}')
605
+
606
+ # 템플릿 삭제
607
+ self.clear()
608
+
609
+ self.logger(f'>>> COPY DSM LIBRARY IMPLEMENTATION')
610
+ self.copy_dsm()
611
+
612
+ self.logger(f'>>> COPY CALIBRATION DATA FROM BASE MODEL')
613
+ self.copy_data()
614
+
615
+ self.logger(f'>>> RUN EXCEPTION HANDLING')
616
+ self.exception()
617
+
618
+ # 수동 예외처리 로그
619
+ for instruction in self.manual_instruction:
620
+ self.logger(f'>>> [MANUALLY] {instruction}')
621
+
622
+ if not path:
623
+ self.main.export_to_downloads()
624
+ self.impl.export_to_downloads()
625
+ self.data.export_to_downloads()
626
+ self.spec.export_to_downloads()
627
+ with open(os.path.join(env['DOWNLOADS'] / f'{self.name}/log.log'), 'w', encoding='utf-8') as f:
628
+ f.write(self.logger.stream)
629
+ self.logger(f'>>> CREATED TO "{path_abbreviate(env.DOWNLOADS / self.name)}" SUCCESS')
630
+ else:
631
+ self.main.export(path)
632
+ self.impl.export(path)
633
+ self.data.export(path)
634
+ self.spec.export(path)
635
+ with open(os.path.join(path, f'log.log'), 'w', encoding='utf-8') as f:
636
+ f.write(self.logger.stream)
637
+ self.logger(f'>>> CREATED TO "{path_abbreviate(path)}" SUCCESS')
638
+ return
639
+
640
+
641
+ if __name__ == "__main__":
642
+ from cannect.core.ascet.ws import WorkspaceIO
643
+ from pandas import set_option
644
+ set_option('display.expand_frame_repr', False)
645
+
646
+ # ICE
647
+ target = {
648
+ "CanFDABSD": ["ABS_ESC_01_10ms", "WHL_01_10ms", ],
649
+ "CanFDACUD": ["ACU_01_100ms", "IMU_01_10ms", ],
650
+ "CanFDADASD": ["ADAS_CMD_10_20ms", "ADAS_CMD_20_20ms", "ADAS_PRK_20_20ms", "ADAS_PRK_21_20ms", ],
651
+ "CanFDBCMD": ["BCM_02_200ms", "BCM_07_200ms", "BCM_10_200ms", "BCM_20_200ms", "BCM_22_200ms", ],
652
+ "CanFDBDCD": ["BDC_FD_05_200ms", "BDC_FD_07_200ms", "BDC_FD_08_200ms", "BDC_FD_10_200ms",
653
+ "BDC_FD_SMK_02_200ms", ],
654
+ "CanBMSD_48V": ["BMS5", "BMS6", "BMS7", ],
655
+ "CanFDCCUD": ["CCU_OBM_01_1000ms", "CCU_OTA_01_200ms", ],
656
+ "CanFDCLUD": ["CLU_01_20ms", "CLU_02_100ms", "CLU_18_20ms", ],
657
+ "CanCVVDD": ["CVVD1", "CVVD2", "CVVD3", "CVVD4", ],
658
+ "CanFDDATCD": ["DATC_01_20ms", "DATC_02_20ms", "DATC_07_200ms", "DATC_17_200ms", ],
659
+ "CanFDEPBD": ["EPB_01_50ms", ],
660
+ "CanFDESCD": ["ESC_01_10ms", "ESC_03_20ms", "ESC_04_50ms", ],
661
+ "CanHSFPCMD": ["FPCM_01_100ms", ],
662
+ "CanFDFRCMRD": ["FR_CMR_02_100ms", "FR_CMR_03_50ms", ],
663
+ "CanFDHFEOPD": ["L_HFEOP_01_10ms", ],
664
+ "CanFDHUD": ["HU_GW_03_200ms", "HU_GW_PE_01", "HU_OTA_01_500ms", "HU_OTA_PE_00", "HU_TMU_02_200ms", ],
665
+ "CanFDICSCD": ["ICSC_02_100ms", "ICSC_03_100ms", ],
666
+ "CanFDICUD": ["ICU_02_200ms", "ICU_04_200ms", "ICU_05_200ms", "ICU_07_200ms", "ICU_09_200ms", "ICU_10_200ms", ],
667
+ "CanFDILCUD": ["ILCU_RH_01_200ms", "ILCU_RH_FD_01_200ms", ],
668
+ "CanLDCD_48V": ["LDC1", "LDC2", ],
669
+ "CanFDMDPSD": ["MDPS_01_10ms", "SAS_01_10ms", ],
670
+ "CanMHSGD_48V": ["MHSG_STATE1", "MHSG_STATE2", "MHSG_STATE3", "MHSG_STATE4", ],
671
+ "CanFDODSD": ["ODS_01_1000ms", ],
672
+ "CanFDOPID": ["L_OPI_01_100ms", ],
673
+ "CanFDPDCD": ["PDC_FD_01_200ms", "PDC_FD_03_200ms", "PDC_FD_10_200ms", "PDC_FD_11_200ms", ],
674
+ "CanFDSBCMD": ["SBCM_DRV_03_200ms", "SBCM_DRV_FD_01_200ms", ],
675
+ "CanFDSCUD": ["SCU_FF_01_10ms", ],
676
+ "CanFDSMKD": ["SMK_05_200ms", ],
677
+ "CanFDSWRCD": ["SWRC_03_20ms", "SWRC_FD_03_20ms", ],
678
+ "CanFDLTCUD": ["L_TCU_01_10ms", "L_TCU_02_10ms", "L_TCU_03_10ms", "L_TCU_04_10ms", ],
679
+ "CanFDTCUD": ["TCU_01_10ms", "TCU_02_10ms", "TCU_03_100ms", ],
680
+ "CanFDTMUD": ["TMU_01_200ms", ],
681
+ "CanNOXD": ["Main_Status_Rear", "O2_Rear"]
682
+ }
683
+
684
+ proj = WorkspaceIO()
685
+ data = CANDBReader().to_developer_mode("HEV")
686
+
687
+ template = CANDiag(data, proj["CanFDMCU_HEV"], "MCU_01_P_10ms", "MCU_01_H_10ms", "MCU_02_P_10ms", "MCU_02_H_10ms")
688
+ template.create()
689
+
690
+
691
+