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,399 @@
1
+ from cannect.config import env
2
+ from cannect.core.ascet.oid import generateOID
3
+ from cannect.core.ascet.amd import AmdElements
4
+ from cannect.core.ascet.formula import formula_dictionary
5
+ from cannect.core.can.ascet import _db2code
6
+ from cannect.core.can.rule import naming
7
+ from cannect.schema.candb import CanMessage, CanSignal
8
+ from cannect.schema.datadictionary import DataDictionary
9
+ from cannect.utils.tools import xml
10
+
11
+ from typing import Dict, Iterator, Optional, Union
12
+ from xml.etree.ElementTree import Element
13
+ import math
14
+
15
+
16
+ F = formula_dictionary(env.SVN_CAN / "CAN_Model/Formula/HNB_I4GDI_I4MPI.xml")
17
+
18
+ def elementWrapper(**kwargs) -> DataDictionary:
19
+ return DataDictionary(
20
+ kwargs=DataDictionary(**kwargs),
21
+ Element=AmdElements.Element(**kwargs),
22
+ ImplementationEntry=AmdElements.ImplementationEntry(**kwargs),
23
+ DataEntry=AmdElements.DataEntry(**kwargs)
24
+ )
25
+
26
+
27
+ def crcClassElement(n:Union[int, str], oid_tag:Optional[Dict[str, str]]=None) -> DataDictionary:
28
+ n = str(n)
29
+ if not oid_tag:
30
+ oid_tag = {}
31
+ class_id = DataDictionary(
32
+ componentID={
33
+ "8": "_040g1ngg01pp1oo708cg4rviuqor2",
34
+ "16": "_040g1ngg01pp1oo708a0du6locrr2"
35
+ },
36
+ implementationOID={
37
+ "8": "_040g1ngg01pp1oo708cg4rviur2r2",
38
+ "16": "_040g1ngg01pp1oo708a0du6lq95b2"
39
+ },
40
+ dataOID={
41
+ "8": "_040g1ngg01pp1oo708cg4rviuqvb2",
42
+ "16": "_040g1ngg01pp1oo708a0du6lod1r2"
43
+ }
44
+ )
45
+ name = f'CRC{n}bit_Calculator'
46
+ kwargs = DataDictionary(
47
+ name=name,
48
+ OID=oid_tag[name] if name in oid_tag else generateOID(),
49
+ comment=f'CRC {n}bit Calculator Instance',
50
+ modelType="complex",
51
+ basicModelType="class",
52
+ unit="",
53
+ componentName=f"/HNB_GASOLINE/_29_CommunicationVehicle/CANInterfaceCommon/InterfaceLibrary/CRCCalc/"
54
+ f"CRC{n}Bit_Calculator/CRC{n}bit_Calculator",
55
+ componentID=class_id.componentID[n],
56
+ scope="local",
57
+ set="false",
58
+ get="false",
59
+ read="true",
60
+ write="true",
61
+ reference="false",
62
+ elementName=f'CRC{n}bit_Calculator',
63
+ elementOID="",
64
+ implementationName="Impl",
65
+ implementationOID=class_id.implementationOID[n],
66
+ value="false",
67
+ dataName="Data",
68
+ dataOID=class_id.dataOID[n]
69
+ )
70
+ return elementWrapper(**kwargs)
71
+
72
+
73
+ def SignalElement(signal:CanSignal, oid_tag:Optional[Dict[str, str]]=None) -> DataDictionary:
74
+ if not oid_tag:
75
+ oid_tag = {}
76
+ kwargs = DataDictionary()
77
+ element_name = signal.name if not signal["SignalRenamed"] else signal["SignalRenamed"]
78
+ kwargs.name = name = f'{element_name}_{"Ems" if signal.ECU == "EMS" else "Can"}'
79
+ kwargs.OID = oid_tag[name] if name in oid_tag else generateOID()
80
+ kwargs.comment = signal.Definition
81
+ if signal.ECU == "EMS":
82
+ kwargs.comment = ""
83
+ kwargs.modelType = 'scalar'
84
+ if signal.Length == 1:
85
+ kwargs.basicModelType = "log"
86
+ elif signal.Formula == "OneToOne":
87
+ kwargs.basicModelType = "udisc"
88
+ else:
89
+ kwargs.basicModelType = 'cont'
90
+ kwargs.unit = signal.Unit
91
+
92
+ kwargs.kind = "message"
93
+ kwargs.scope = "exported"
94
+ if signal.ECU == "EMS":
95
+ kwargs.scope = "imported"
96
+ if signal.isCrc() or signal.isAliveCounter():
97
+ kwargs.scope = "local"
98
+
99
+ kwargs.quantization = "0" if kwargs.basicModelType == "cont" else "1"
100
+ kwargs.formula = signal.Formula
101
+
102
+ size = 8 if signal.Length <= 8 else 16 if signal.Length <= 16 else 32
103
+ kwargs.physType = "real64" if kwargs.basicModelType == "cont" else "uint32"
104
+ kwargs.implType = f"sint{size}" if signal["Value Type"].startswith("Signed") else f"uint{size}"
105
+ kwargs.implMin = f"-{2 ** (size - 1)}" if signal["Value Type"].startswith("Signed") else "0"
106
+ kwargs.implMax = f"{2 ** (size - 1) - 1}" if signal["Value Type"].startswith("Signed") else f"{2 ** size - 1}"
107
+
108
+ if not signal.Formula in F:
109
+ min_val = int(kwargs.implMin) * signal.Factor + signal.Offset
110
+ max_val = int(kwargs.implMax) * signal.Factor + signal.Offset
111
+ else:
112
+ f = F[signal.Formula]
113
+ min_val = int(kwargs.implMin) * f.quantization + f.offset
114
+ max_val = int(kwargs.implMax) * f.quantization + f.offset
115
+
116
+ kwargs.physMin = f"{min_val}" if kwargs.basicModelType == "cont" else f"{int(min_val)}"
117
+ kwargs.physMax = f"{max_val}" if kwargs.basicModelType == "cont" else f"{int(max_val)}"
118
+ if str(signal.name).startswith("FPCM_ActlPrsrVal"):
119
+ kwargs.physMax = "800.0"
120
+ if str(signal.name).startswith("TCU_GrRatioChngProg"):
121
+ kwargs.physMax = "1.0"
122
+
123
+
124
+ kwargs.value = "false" if kwargs.basicModelType == "log" else "0.0" if kwargs.basicModelType == "cont" else "0"
125
+ return elementWrapper(**kwargs)
126
+
127
+
128
+ class MessageElement:
129
+
130
+ __slots__ = [
131
+ "method",
132
+ "MethodBody",
133
+ "buffer",
134
+ "dlc",
135
+ "thresholdTime",
136
+ "counter",
137
+ "counterCalc",
138
+ "messageCountTimer",
139
+ "messageCountValid",
140
+ "aliveCounter",
141
+ "aliveCounterCalc",
142
+ "aliveCountTimer",
143
+ "aliveCountValid",
144
+ "crc",
145
+ "crcCalc",
146
+ "crcTimer",
147
+ "crcValid",
148
+ ]
149
+
150
+ def __init__(self, message:CanMessage, oid_tag:Optional[Dict[str, str]]=None):
151
+ if not oid_tag:
152
+ oid_tag = {}
153
+ comment_id = f'{message.name}({message["ID"]})'
154
+ timer_formula = f"Ti_q{str(message['taskTime']).replace('.', 'p')}_s".replace('p0_s', '_s')
155
+ timer_round = 3 if message["taskTime"] == 0.001 else 2
156
+ """
157
+ 신규 Element OID 부여
158
+ """
159
+ rule = naming(message.name)
160
+ for req in self.__slots__:
161
+ if req.startswith("MethodBody"):
162
+ continue
163
+ if req == "aliveCounter":
164
+ oid_tag[f'{message.aliveCounter.name}_Can'] = oid_tag.get(f'{message.aliveCounter.name}_Can', '') or generateOID()
165
+ continue
166
+ if req == "aliveCounterCalc":
167
+ oid_tag[f'{message.aliveCounter.name}Calc'] = oid_tag.get(f'{message.aliveCounter.name}Calc', '') or generateOID()
168
+ continue
169
+ if req == "crc":
170
+ oid_tag[f'{message.crc.name}_Can'] = oid_tag.get(f'{message.crc.name}_Can', '') or generateOID()
171
+ continue
172
+ if req == 'crcCalc':
173
+ oid_tag[f'{message.crc.name}Calc'] = oid_tag.get(f'{message.crc.name}Calc', '') or generateOID()
174
+ continue
175
+
176
+ if not oid_tag or not getattr(rule, req) in oid_tag:
177
+ oid_tag[getattr(rule, req)] = generateOID()
178
+
179
+ """
180
+ %ComDef* 모델의 메시지 MethodSignature 생성
181
+ """
182
+ self.method = AmdElements.MethodSignature(
183
+ name=rule.method,
184
+ OID=oid_tag[rule.method],
185
+ defaultMethod='true' if str(message.name) == 'ABS_ESC_01_10ms' else 'false'
186
+ )
187
+
188
+ """
189
+ %ComDef* 모델의 메시지에 대한 MethodBody의 CodeBlock :: C Code 소스
190
+ """
191
+ MethodBody = Element('MethodBody', methodName=rule.method, methodOID=oid_tag[rule.method])
192
+ CodeBlock = Element('CodeBlock')
193
+ CodeBlock.text = _db2code.MessageCode(message).method
194
+ MethodBody.append(CodeBlock)
195
+ self.MethodBody = MethodBody
196
+
197
+ """
198
+ %ComDef* 모델의 메시지 Element
199
+ """
200
+ self.buffer = elementWrapper(**DataDictionary(
201
+ name=rule.buffer,
202
+ OID=oid_tag[rule.buffer],
203
+ comment=f'{comment_id} Buffer',
204
+ modelType="array",
205
+ basicModelType="udisc",
206
+ maxSizeX=str(message["DLC"]),
207
+ kind="variable",
208
+ scope="exported",
209
+ quantization="1",
210
+ formula="OneToOne",
211
+ physType="uint32", physMin="0", physMax="255",
212
+ implType="uint8", implMin="0", implMax="255",
213
+ value="0",
214
+ ))
215
+
216
+ self.dlc = elementWrapper(**DataDictionary(
217
+ name=rule.dlc,
218
+ OID=oid_tag[rule.dlc],
219
+ comment=f'{comment_id} DLC',
220
+ modelType="scalar",
221
+ basicModelType="udisc",
222
+ kind="variable",
223
+ scope="local",
224
+ quantization="1",
225
+ formula="OneToOne",
226
+ physType="uint32", physMin="0", physMax="255",
227
+ implType="uint8", implMin="0", implMax="255",
228
+ value="0",
229
+ ))
230
+
231
+ self.thresholdTime = elementWrapper(**DataDictionary(
232
+ name=rule.thresholdTime,
233
+ OID=oid_tag[rule.thresholdTime],
234
+ comment=f'{comment_id} Timeout Threshold',
235
+ modelType="scalar",
236
+ basicModelType="cont",
237
+ unit="s",
238
+ kind="parameter",
239
+ scope="exported",
240
+ volatile="false",
241
+ write="false",
242
+ quantization="0",
243
+ formula=timer_formula,
244
+ physType="real64", physMin="0.0", physMax=f'{round(255 * message["taskTime"], timer_round)}',
245
+ implType="uint8", implMin="0", implMax="255",
246
+ value=f'{math.ceil(message["timeoutTime"] / message["taskTime"]) * message["taskTime"]: .2f}'
247
+ ))
248
+
249
+ self.counter = elementWrapper(**DataDictionary(
250
+ name=rule.counter,
251
+ OID=oid_tag[rule.counter],
252
+ comment=f'{comment_id} Message Counter',
253
+ modelType="scalar",
254
+ basicModelType="udisc",
255
+ kind="message",
256
+ scope="exported",
257
+ quantization="1",
258
+ formula="OneToOne",
259
+ physType="uint32", physMin="0", physMax="255",
260
+ implType="uint8", implMin="0", implMax="255",
261
+ value="0",
262
+ ))
263
+
264
+ self.counterCalc = elementWrapper(**DataDictionary(
265
+ name=rule.counterCalc,
266
+ OID=oid_tag[rule.counterCalc],
267
+ comment=f'{comment_id} Message Counter Calculated',
268
+ modelType="scalar",
269
+ basicModelType="udisc",
270
+ kind="variable",
271
+ scope="local",
272
+ quantization="1",
273
+ formula="OneToOne",
274
+ physType="uint32", physMin="0", physMax="255",
275
+ implType="uint8", implMin="0", implMax="255",
276
+ value="0",
277
+ ))
278
+
279
+ self.messageCountTimer = elementWrapper(**DataDictionary(
280
+ name=rule.messageCountTimer,
281
+ OID=oid_tag[rule.messageCountTimer],
282
+ comment=f'{comment_id} Counter Timeout Timer',
283
+ modelType="scalar",
284
+ basicModelType="cont",
285
+ unit="s",
286
+ kind="variable",
287
+ scope="local",
288
+ quantization="0",
289
+ formula=timer_formula,
290
+ physType="real64", physMin="0.0", physMax=f'{round(255 * message["taskTime"], timer_round)}',
291
+ implType="uint8", implMin="0", implMax="255",
292
+ value=f'0.0'
293
+ ))
294
+
295
+ self.messageCountValid = elementWrapper(**DataDictionary(
296
+ name=rule.messageCountValid,
297
+ OID=oid_tag[rule.messageCountValid],
298
+ comment=f'{comment_id} Counter Validity',
299
+ modelType="scalar",
300
+ basicModelType="log",
301
+ kind="message",
302
+ scope="exported",
303
+ physType="log",
304
+ value="false"
305
+ ))
306
+
307
+ if message.hasAliveCounter():
308
+ self.aliveCounter = SignalElement(message.aliveCounter, oid_tag)
309
+ attr = xml.to_dict(self.aliveCounter.Element)
310
+ attr.update(xml.to_dict(self.aliveCounter.ImplementationEntry))
311
+ attr.update(xml.to_dict(self.aliveCounter.DataEntry))
312
+ attr.update(
313
+ name=f'{message.aliveCounter.name}Calc',
314
+ OID=oid_tag[f'{message.aliveCounter.name}Calc'],
315
+ comment=f'{comment_id} Alive Counter Calculated',
316
+ kind='variable',
317
+ scope='local'
318
+ )
319
+ self.aliveCounterCalc = elementWrapper(**attr)
320
+ self.aliveCountTimer = elementWrapper(**DataDictionary(
321
+ name=rule.aliveCountTimer,
322
+ OID=oid_tag[rule.aliveCountTimer],
323
+ comment=f'{comment_id} Alive Counter Timeout Timer',
324
+ modelType="scalar",
325
+ basicModelType="cont",
326
+ unit="s",
327
+ kind="variable",
328
+ scope="local",
329
+ quantization="0",
330
+ formula=timer_formula,
331
+ physType="real64", physMin="0.0", physMax=f'{round(255 * message["taskTime"], timer_round)}',
332
+ implType="uint8", implMin="0", implMax="255",
333
+ value=f'0.0'
334
+ ))
335
+ self.aliveCountValid = elementWrapper(**DataDictionary(
336
+ name=rule.aliveCountValid,
337
+ OID=oid_tag[rule.aliveCountValid],
338
+ comment=f'{comment_id} Alive Counter Validity',
339
+ modelType="scalar",
340
+ basicModelType="log",
341
+ kind="message",
342
+ scope="exported",
343
+ physType="log",
344
+ value="false"
345
+ ))
346
+
347
+ if message.hasCrc():
348
+ self.crc = SignalElement(message.crc, oid_tag)
349
+ attr = xml.to_dict(self.crc.Element)
350
+ attr.update(xml.to_dict(self.crc.ImplementationEntry))
351
+ attr.update(xml.to_dict(self.crc.DataEntry))
352
+ attr.update(
353
+ name=f'{message.crc.name}Calc',
354
+ OID=oid_tag[f'{message.crc.name}Calc'],
355
+ comment=f'{comment_id} CRC Calculated',
356
+ kind='variable',
357
+ scope='local'
358
+ )
359
+ if message.name == "ESC_01_10ms":
360
+ attr.update(
361
+ kind='message',
362
+ scope='exported',
363
+ )
364
+ self.crcCalc = elementWrapper(**attr)
365
+ self.crcTimer = elementWrapper(**DataDictionary(
366
+ name=rule.crcTimer,
367
+ OID=oid_tag[rule.crcTimer],
368
+ comment=f'{comment_id} Alive Counter Timeout Timer',
369
+ modelType="scalar",
370
+ basicModelType="cont",
371
+ unit="s",
372
+ kind="variable",
373
+ scope="local",
374
+ quantization="0",
375
+ formula=timer_formula,
376
+ physType="real64", physMin="0.0", physMax=f'{round(255 * message["taskTime"], timer_round)}',
377
+ implType="uint8", implMin="0", implMax="255",
378
+ value=f'0.0'
379
+ ))
380
+
381
+ self.crcValid = elementWrapper(**DataDictionary(
382
+ name=rule.crcValid,
383
+ OID=oid_tag[rule.crcValid],
384
+ comment=f'{comment_id} CRC Validity',
385
+ modelType="scalar",
386
+ basicModelType="log",
387
+ kind="message",
388
+ scope="exported",
389
+ physType="log",
390
+ value="false"
391
+ ))
392
+
393
+ return
394
+
395
+ def __iter__(self) -> Iterator[DataDictionary]:
396
+ for slot in self.__slots__:
397
+ yield self.__getattribute__(slot)
398
+
399
+
@@ -0,0 +1,256 @@
1
+ from cannect.config import env
2
+ from cannect.core.ascet.amd import Amd
3
+ from cannect.core.can.ascet import _db2code, _db2elem
4
+ from cannect.core.can.db.reader import CANDBReader
5
+ from cannect.schema.datadictionary import DataDictionary
6
+ from cannect.utils.logger import Logger
7
+ from cannect.utils import tools
8
+
9
+ from typing import Any, Union, Tuple
10
+ from pandas import DataFrame
11
+ import os, copy
12
+
13
+
14
+ class ComDef:
15
+
16
+ def __init__(
17
+ self,
18
+ db:CANDBReader,
19
+ engine_spec:str,
20
+ base_model:str='',
21
+ exclude_tsw:bool=True,
22
+ ):
23
+ exclude_ecus = ["EMS", "CVVD", "MHSG", "NOx"]
24
+ if engine_spec == "ICE":
25
+ exclude_ecus += ["BMS", "LDC"]
26
+ db = db[~db["ECU"].isin(exclude_ecus)]
27
+ if not db.is_developer_mode():
28
+ db = db.to_developer_mode(engine_spec)
29
+
30
+ if base_model:
31
+ name = os.path.basename(base_model).split(".")[0]
32
+ else:
33
+ name = f"ComDef{'_HEV' if engine_spec == 'HEV' else ''}"
34
+ base_model = env.SVN_MODEL / rf'HNB_GASOLINE/_29_CommunicationVehicle/StandardDB/NetworkDefinition/{name}/{name}.zip'
35
+
36
+ # 공용 속성 생성
37
+ self.db = db
38
+ self.name = name
39
+ self.engine_spec = engine_spec
40
+
41
+ # 각 amd의 IO 생성
42
+ amd = Amd(base_model)
43
+ self.main = amd.main
44
+ self.impl = amd.impl
45
+ self.data = amd.data
46
+ self.spec = amd.spec
47
+
48
+ (env.DOWNLOADS / name).mkdir(exist_ok=True, parents=True)
49
+ self.logger = logger = Logger(env.DOWNLOADS / rf'{name}/log.txt', clean_record=True)
50
+ logger.info(f"%{name} MODEL GENERATION")
51
+ logger.info(f">>> Engine Spec : {engine_spec}")
52
+ logger.info(f">>> Base Model : {tools.path_abbreviate(base_model)}")
53
+ logger.info(f">>> DB Revision : {db.revision}")
54
+ logger.info(f">>> Exclude TSW : {'Yes' if exclude_tsw else 'No'}")
55
+
56
+ """
57
+ 변경 전 모델 요소 수집
58
+ """
59
+ logger.info(">>> Collecting Base Model Properties... 0.01s")
60
+ prev = self.collect_properties()
61
+ oids = dict(zip(prev.Elements['name'], prev.Elements.index))
62
+ oids.update(dict(zip(prev.MethodSignature['name'], prev.MethodSignature.index)))
63
+ self.prev = prev
64
+ self.oids = oids
65
+
66
+ """
67
+ DB 메시지 기반의 요소 생성
68
+ """
69
+ logger.run()
70
+ self.ME = {name: _db2elem.MessageElement(obj, oid_tag=oids) for name, obj in db.messages.items()}
71
+ self.MC = {name: _db2code.MessageCode(obj, exclude_tsw) for name, obj in db.messages.items()}
72
+ logger.end(">>> Defining Message Elements...")
73
+
74
+ logger.run()
75
+ self.SE = [_db2elem.SignalElement(sig, oid_tag=oids) for sig in db.signals.values()]
76
+ logger.end(">>> Defining Signal Elements...")
77
+ return
78
+
79
+ def autorun(self):
80
+ self.main.find('Component/Comment').text = _db2code.INFO(self.db.revision)
81
+ self.define_elements('MethodSignature')
82
+ self.define_elements('Element')
83
+ self.define_elements('ImplementationEntry')
84
+ self.define_elements('DataEntry')
85
+ self.define_elements('HeaderBlock')
86
+ self.define_elements('MethodBody')
87
+ self.export()
88
+
89
+ curr = self.collect_properties()
90
+ deleted = list(set(self.prev.Elements['name']) - set(curr.Elements['name']))
91
+ added = list(set(curr.Elements['name']) - set(self.prev.Elements['name']))
92
+ desc = DataFrame(
93
+ data={
94
+ ("Method", "Total"): [len(self.prev.MethodSignature), len(self.db.messages)],
95
+ ("Element", "Total"): [len(self.prev.Elements), len(curr.Elements)],
96
+ ("Element", "Added"): ["-", len(added)],
97
+ ("Element", "Deleted"): [len(deleted), "-"]
98
+ },
99
+ index=['Base Model', ' New Model']
100
+ )
101
+ self.logger.info(">>> Summary\n" + \
102
+ desc.to_string() + '\n' + \
103
+ f'* Added: {", ".join(added)}' + '\n' + \
104
+ f'* Deleted: {", ".join(deleted)}')
105
+ return
106
+
107
+ def collect_properties(self) -> DataDictionary:
108
+ mainE = self.main.dataframe('Element').set_index(keys='OID').copy()
109
+ implE = self.impl.dataframe('ImplementationEntry').set_index(keys='elementOID').copy()
110
+ dataE = self.data.dataframe('DataEntry').set_index(keys='elementOID').copy()
111
+ implE = implE.drop(columns=[col for col in implE if col in mainE.columns])
112
+ dataE = dataE.drop(columns=[col for col in dataE if col in mainE.columns or col in implE.columns])
113
+ return DataDictionary(
114
+ MethodSignature=self.main.dataframe('MethodSignature').set_index(keys='OID'),
115
+ MethodBody=self.spec.datadict('MethodBody'),
116
+ HeaderBlock=self.spec.strictFind('CodeVariant', target="G_HMCEMS").find('HeaderBlock').text,
117
+ Elements=mainE.join(implE).join(dataE)
118
+ )
119
+
120
+ def parents(self, tag:str) -> Union[Any, Tuple]:
121
+ if tag == "MethodSignature":
122
+ return self.main.find('Component/MethodSignatures'), None
123
+ if tag == "Element":
124
+ return self.main.find('Component/Elements'), None
125
+ if tag == 'ImplementationEntry':
126
+ return tuple(self.impl.findall('ImplementationSet'))
127
+ if tag == 'DataEntry':
128
+ return tuple(self.data.findall('DataSet'))
129
+ if tag == 'MethodBody':
130
+ return self.spec.strictFind('CodeVariant', target="G_HMCEMS").find('MethodBodies'), \
131
+ self.spec.strictFind('CodeVariant', target="PC").find('MethodBodies')
132
+ if tag == "HeaderBlock":
133
+ return self.spec.strictFind('CodeVariant', target="G_HMCEMS").find('HeaderBlock'), \
134
+ self.spec.strictFind('CodeVariant', target="PC").find('HeaderBlock')
135
+ raise AttributeError
136
+
137
+ def define_elements(self, tag:str):
138
+ """
139
+ {tag}에 해당하는 AmdIO를 찾는다.
140
+ {tag}에 해당하는 AmdIO의 부모 tag를 찾는다.
141
+ - Implementation 및 Data는 @scope에 따른 부모 tag가 2개 존재한다.
142
+ 부모 tag의 하위 {tag}를 모두 삭제하고 신규 정의 Element로 대체한다.
143
+
144
+ :param tag:
145
+ :return:
146
+ """
147
+ pGlob, pLoc = self.parents(tag)
148
+ for child in list(pGlob):
149
+ pGlob.remove(child)
150
+ if pLoc is not None:
151
+ for child in list(pLoc):
152
+ pLoc.remove(child)
153
+
154
+ if tag == 'MethodSignature':
155
+ for name in self.db.messages:
156
+ pGlob.append(self.ME[name].method)
157
+ return
158
+
159
+ if tag == 'HeaderBlock':
160
+ pLoc.text = "/* Please Change Target In Order To View Source Code */"
161
+ pGlob.text = f"""#include <Bsw/Include/Bsw.h>
162
+
163
+ #ifdef SRV_HMCEMS
164
+ {"&lf;".join([mc.srv_name(self.name) for mc in self.MC.values()])}
165
+ #endif
166
+
167
+ {"&lf;".join([mc.def_name for mc in self.MC.values()])}
168
+
169
+ {"&lf;".join([mc.struct for mc in self.MC.values()])}
170
+ {_db2code.INLINE}""" \
171
+ .replace("&tb;", "\t") \
172
+ .replace("&lf;", "\n")
173
+ if self.engine_spec == "HEV":
174
+ pGlob.text = pGlob.text.replace("YRS", "IMU")
175
+ return
176
+
177
+ if tag == "MethodBody":
178
+ for name, me in self.ME.items():
179
+ method_body = self.ME[name].MethodBody
180
+ dummy_method = copy.deepcopy(method_body)
181
+ dummy_method.find("CodeBlock").text = ""
182
+ pGlob.append(method_body)
183
+ pLoc.append(dummy_method)
184
+ return
185
+
186
+ for se in self.SE:
187
+ pGlob.append(getattr(se, tag))
188
+
189
+ for key in _db2elem.MessageElement.__slots__:
190
+ if key in ["method", "MethodBody", "aliveCounter", "crc"]:
191
+ continue
192
+ for name, me in self.ME.items():
193
+ if hasattr(me, key):
194
+ elem = getattr(me, key)
195
+ if pLoc is None:
196
+ parent = pGlob
197
+ else:
198
+ parent = pLoc if elem.kwargs.scope == "local" else pGlob
199
+ parent.append(getattr(elem, tag))
200
+
201
+ parent = pGlob if tag == 'Element' else pLoc
202
+ parent.append(getattr(_db2elem.crcClassElement(16, self.oids), tag))
203
+ parent.append(getattr(_db2elem.crcClassElement(8, self.oids), tag))
204
+ return
205
+
206
+ def export(self):
207
+ self.main.export_to_downloads()
208
+ self.impl.export_to_downloads()
209
+ self.data.export_to_downloads()
210
+ self.spec.export_to_downloads()
211
+ return
212
+
213
+
214
+ if __name__ == "__main__":
215
+ from pandas import set_option
216
+ set_option('display.expand_frame_repr', False)
217
+
218
+ db = CANDBReader()
219
+ # db = CANDBReader(env.SVN_CANDB / rf'dev/G-PROJECT_KEFICO-EMS_CANFD_r21676@01.json')
220
+
221
+ engine_spec = "HEV"
222
+
223
+ # DB CUSTOMIZE ------------------------------------------------------
224
+ # exclude_ecus = ["EMS", "CVVD", "MHSG", "NOx"]
225
+ # if engine_spec == "ICE":
226
+ # exclude_ecus += ["BMS", "LDC"]
227
+ # db = db[~db["ECU"].isin(exclude_ecus)]
228
+
229
+ # db = db[db["Status"] != "TSW"] # TSW 제외
230
+ # db = db[~db["Requirement ID"].isin(["VCDM CR10777888"])] # 특정 CR 제외
231
+ # db = db[~db["Required Date"].isin(["2024-08-27"])] # 특정 일자 제외
232
+ # db = db[~db["Message"].isin([ # 특정 메시지 제외
233
+ # "L_H8L_01_10ms",
234
+ # "H8L_01_10ms",
235
+ # "H8L_02_10ms",
236
+ # ])]
237
+ # db.revision = "TEST SW" # 공식SW는 주석 처리
238
+ # DB CUSTOMIZE END --------------------------------------------------
239
+
240
+ # model = ComDef(
241
+ # db=db,
242
+ # engine_spec=engine_spec,
243
+ # exclude_tsw=True,
244
+ # # base_model="",
245
+ # # base_model=r'D:\SVN\model\ascet\trunk\HNB_GASOLINE\_29_CommunicationVehicle\StandardDB\NetworkDefinition\ComDef\ComDef-22368\ComDef.main.amd'
246
+ # # base_model=ENV['ASCET_EXPORT_PATH']
247
+ # )
248
+ # model.autorun()
249
+
250
+ model = ComDef(
251
+ db=db,
252
+ engine_spec=engine_spec,
253
+ exclude_tsw=True,
254
+ # base_model=env.ASCET / f"Export/ComDef_G/ComDef_G.main.amd"
255
+ )
256
+ model.autorun()