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,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()
|