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,160 @@
|
|
|
1
|
+
from cannect.config import env
|
|
2
|
+
from cannect.core.testcase.unitcase import UnitTestCase
|
|
3
|
+
from cannect.core.testcase.style import Style
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pandas import DataFrame, ExcelWriter
|
|
6
|
+
from typing import Dict, List, Hashable, Union
|
|
7
|
+
import xlsxwriter as xlsx
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestCase:
|
|
12
|
+
|
|
13
|
+
def __init__(self, *args:UnitTestCase):
|
|
14
|
+
self._units: List[UnitTestCase] = []
|
|
15
|
+
self._template: str = env.SVN_CAN / "CAN_TestCase/TESTCASE_TEMPLATE.xlsm"
|
|
16
|
+
self._filename: str = f'TESTCASE @{str(datetime.now()).replace(" ", "_").replace(":", ";").split(".")[0]}'
|
|
17
|
+
self._data = ""
|
|
18
|
+
self._mdf = None
|
|
19
|
+
for arg in args:
|
|
20
|
+
self._units.append(arg)
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
def __repr__(self):
|
|
24
|
+
return repr(self.cases)
|
|
25
|
+
|
|
26
|
+
def __len__(self) -> int:
|
|
27
|
+
return len(self._units)
|
|
28
|
+
|
|
29
|
+
def __iter__(self):
|
|
30
|
+
for unit in self._units:
|
|
31
|
+
yield unit
|
|
32
|
+
|
|
33
|
+
def __getitem__(self, item: Union[int, str]) -> UnitTestCase:
|
|
34
|
+
if isinstance(item, int):
|
|
35
|
+
return self._units[item - 1]
|
|
36
|
+
elif isinstance(item, str):
|
|
37
|
+
for unit in self._units:
|
|
38
|
+
if unit["Test Case - ID"] == item:
|
|
39
|
+
return unit
|
|
40
|
+
raise KeyError
|
|
41
|
+
|
|
42
|
+
def __setitem__(self, key: str, value):
|
|
43
|
+
for unit in self._units:
|
|
44
|
+
if not key in unit.index:
|
|
45
|
+
raise KeyError(f'Unknown key: {key}')
|
|
46
|
+
unit[key] = value
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def data(self) -> str:
|
|
51
|
+
return self._data
|
|
52
|
+
|
|
53
|
+
@data.setter
|
|
54
|
+
def data(self, data:str):
|
|
55
|
+
from pyems.mdf import MdfReader
|
|
56
|
+
self._data = data
|
|
57
|
+
self._mdf = MdfReader(data)
|
|
58
|
+
self["Measure / Log File (.dat)"] = os.path.basename(data)
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def filename(self) -> str:
|
|
63
|
+
return self._filename
|
|
64
|
+
|
|
65
|
+
@filename.setter
|
|
66
|
+
def filename(self, filename: str):
|
|
67
|
+
self._filename = filename
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def directory(self) -> str:
|
|
71
|
+
return str(env.DOWNLOADS / self.filename)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def cases(self) -> DataFrame:
|
|
75
|
+
return DataFrame(self._units)
|
|
76
|
+
|
|
77
|
+
def append(self, case: UnitTestCase):
|
|
78
|
+
self._units.append(case)
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
def to_testcase(self, filename: Union[str, Hashable] = ""):
|
|
82
|
+
if filename:
|
|
83
|
+
self.filename = filename
|
|
84
|
+
with ExcelWriter(f"{self.directory}.xlsx", engine="xlsxwriter") as writer:
|
|
85
|
+
cases = self.cases.copy()
|
|
86
|
+
cases.to_excel(writer, sheet_name="Test Case", index=False)
|
|
87
|
+
|
|
88
|
+
wb, ws = writer.book, writer.sheets["Test Case"]
|
|
89
|
+
styler = Style(wb, ws)
|
|
90
|
+
for n, col in enumerate(cases.columns):
|
|
91
|
+
ws.write(0, n, col, styler.testcase_label[col])
|
|
92
|
+
for m, value in enumerate(cases[col]):
|
|
93
|
+
ws.write(m + 1, n, value, styler.testcase_value[col])
|
|
94
|
+
|
|
95
|
+
for n, col in enumerate(cases.columns):
|
|
96
|
+
lens = len(col)
|
|
97
|
+
for val in cases[col]:
|
|
98
|
+
vals = val.split('\n') if '\n' in str(val) else [str(val)]
|
|
99
|
+
maxs = max([len(v) for v in vals])
|
|
100
|
+
if maxs > lens:
|
|
101
|
+
lens = maxs
|
|
102
|
+
ws.set_column(n, n, lens + 2)
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
def to_report(self, filename: Union[str, Hashable] = ""):
|
|
106
|
+
if filename:
|
|
107
|
+
self.filename = filename
|
|
108
|
+
file = f"{self.directory.replace('TestCase', 'TestReport')}.xlsx"
|
|
109
|
+
if not "TCU" in file and not "DATC" in file and "TC" in file:
|
|
110
|
+
file = file.replace("TC", "TR")
|
|
111
|
+
|
|
112
|
+
tc = xlsx.Workbook(filename=file)
|
|
113
|
+
ws = tc.add_worksheet(name="Test Report MLT")
|
|
114
|
+
ws.set_column('A:A', 1.63)
|
|
115
|
+
styler = Style(tc, ws)
|
|
116
|
+
styler.adjust_width()
|
|
117
|
+
for n, testcase in enumerate(self):
|
|
118
|
+
testcase.workbook = tc
|
|
119
|
+
testcase.to_report(1 + (n * 32))
|
|
120
|
+
tc.close()
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
def to_labfile(self, name: Union[str, Hashable] = ""):
|
|
124
|
+
filename = name if name else self.filename.replace("TESTCASE", "LABFILE")
|
|
125
|
+
if not filename.endswith(".lab"):
|
|
126
|
+
filename += ".lab"
|
|
127
|
+
file = env.DOWNLOADS / filename
|
|
128
|
+
|
|
129
|
+
elem, param = list(), list()
|
|
130
|
+
for _case in self._units:
|
|
131
|
+
for var in _case.variable:
|
|
132
|
+
box = param if var.endswith("_C") else elem
|
|
133
|
+
if not var in box:
|
|
134
|
+
box.append(var)
|
|
135
|
+
EOL = "\n"
|
|
136
|
+
with open(file, "w", encoding="utf-8") as f:
|
|
137
|
+
f.write(f"""[SETTINGS]
|
|
138
|
+
Version;V1.1
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
[RAMCELL]
|
|
142
|
+
{EOL.join(sorted(elem))}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
[LABEL]
|
|
146
|
+
{EOL.join(sorted(param))}
|
|
147
|
+
""")
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
def to_clipboard(self):
|
|
151
|
+
self.cases.to_clipboard(index=False)
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
from emscan.core.testcase.unitcase import UnitTestCase
|
|
159
|
+
tc = TestCase(UnitTestCase())
|
|
160
|
+
tc.to_report()
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
from cannect.config import env
|
|
2
|
+
from cannect.core.mdf import MdfReader
|
|
3
|
+
from cannect.core.testcase.style import Style
|
|
4
|
+
from cannect.core.testcase.plotter import Plot
|
|
5
|
+
from numpy import nan
|
|
6
|
+
from pandas import DataFrame, Series
|
|
7
|
+
|
|
8
|
+
from typing import Any, Union, List
|
|
9
|
+
from xlsxwriter import Workbook
|
|
10
|
+
import warnings, os
|
|
11
|
+
|
|
12
|
+
def custom_format(message, category, filename, lineno, line=None):
|
|
13
|
+
return f"{message}\n"
|
|
14
|
+
warnings.formatwarning = custom_format
|
|
15
|
+
|
|
16
|
+
LABEL = {
|
|
17
|
+
"NO": nan,
|
|
18
|
+
"Category": "UNIT",
|
|
19
|
+
"Group": "CAN",
|
|
20
|
+
"Test Case - ID": '',
|
|
21
|
+
"Test Case Name": '',
|
|
22
|
+
"Requirement - Traceability": '',
|
|
23
|
+
"Test Purpose, Description": '',
|
|
24
|
+
"PreCondition (PC) - Description": "B+ / IG1",
|
|
25
|
+
"PC-Variable": "BattU_u8\nIgKey_On",
|
|
26
|
+
"PC-Compare": ">\n=",
|
|
27
|
+
"PC-Value": "11.0\n1",
|
|
28
|
+
"Test Execution (TE) - Description": '',
|
|
29
|
+
"TE-Variable": '',
|
|
30
|
+
"TE-Compare": '',
|
|
31
|
+
"TE-Value": '',
|
|
32
|
+
"Expected Results (ER) - Description": '',
|
|
33
|
+
"ER-Variable": '',
|
|
34
|
+
"ER-Compare": '',
|
|
35
|
+
"ER-Value": '',
|
|
36
|
+
"Test Result": '',
|
|
37
|
+
"Test Result Description": '',
|
|
38
|
+
"Test Conductor": f"{env.KOREANAME} @HYUNDAI-KEFICO",
|
|
39
|
+
"Test SW": '',
|
|
40
|
+
"Test HW": '',
|
|
41
|
+
"Test Vehicle / Engine / HIL": '',
|
|
42
|
+
"Test Environment": '',
|
|
43
|
+
"Remark / Comment": '',
|
|
44
|
+
"Measure / Log File (.dat)": '',
|
|
45
|
+
"MDA Configuration File (.xda)": '',
|
|
46
|
+
"Experiment File (.exp)": '',
|
|
47
|
+
"VIO": '',
|
|
48
|
+
"SWC": '',
|
|
49
|
+
"MLT": "●",
|
|
50
|
+
"SLT": '',
|
|
51
|
+
"SDT": '',
|
|
52
|
+
"FDT": '',
|
|
53
|
+
"LVR": '',
|
|
54
|
+
"DCV": '',
|
|
55
|
+
"LSL": '',
|
|
56
|
+
"PSV": '',
|
|
57
|
+
"EOL": '',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class UnitTestCase(Series):
|
|
62
|
+
|
|
63
|
+
__wb__:Workbook = None
|
|
64
|
+
__dr__:MdfReader = None
|
|
65
|
+
__pg__:str = ''
|
|
66
|
+
|
|
67
|
+
def __init__(self, **kwargs):
|
|
68
|
+
self.__wb__ = None
|
|
69
|
+
self.__dr__ = None
|
|
70
|
+
self.__pg__ = ''
|
|
71
|
+
|
|
72
|
+
super().__init__({k: kwargs[k] if k in kwargs else v for k, v in LABEL.items()})
|
|
73
|
+
if 'workbook' in kwargs:
|
|
74
|
+
self.__wb__ = kwargs['workbook']
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def workbook(self) -> Union[Any, Workbook]:
|
|
79
|
+
return self.__wb__
|
|
80
|
+
|
|
81
|
+
@workbook.setter
|
|
82
|
+
def workbook(self, workbook: Workbook):
|
|
83
|
+
self.__wb__ = workbook
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def mdf(self) -> MdfReader:
|
|
87
|
+
return self.__dr__
|
|
88
|
+
|
|
89
|
+
@mdf.setter
|
|
90
|
+
def mdf(self, mdf:Union[str, MdfReader]):
|
|
91
|
+
if isinstance(mdf, str):
|
|
92
|
+
self.__dr__ = MdfReader(mdf)
|
|
93
|
+
else:
|
|
94
|
+
self.__dr__ = mdf
|
|
95
|
+
self["Measure / Log File (.dat)"] = os.path.basename(self.__dr__.file)
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def data(self) -> DataFrame:
|
|
100
|
+
if self.__dr__ is None:
|
|
101
|
+
return DataFrame()
|
|
102
|
+
vars = []
|
|
103
|
+
for var in self.variable:
|
|
104
|
+
if var.startswith("DEve"):
|
|
105
|
+
var = f"DEve_St.{var}"
|
|
106
|
+
if var.startswith("Fid"):
|
|
107
|
+
var = f"Fim_st.{var}"
|
|
108
|
+
if not var in self.mdf:
|
|
109
|
+
warnings.warn(
|
|
110
|
+
f'#{self["Test Case - ID"]}의 {var}(이)가 측정 파일: {self.mdf.file}내 없습니다.',
|
|
111
|
+
category=UserWarning
|
|
112
|
+
)
|
|
113
|
+
continue
|
|
114
|
+
vars.append(var)
|
|
115
|
+
return self.mdf[vars]
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def variable(self) -> List[str]:
|
|
119
|
+
var = []
|
|
120
|
+
for k, v in self.items():
|
|
121
|
+
if not str(k).endswith("Variable") or str(v) in ["nan", '']:
|
|
122
|
+
continue
|
|
123
|
+
for _v in v.split("\n"):
|
|
124
|
+
if not _v in var:
|
|
125
|
+
var.append(_v)
|
|
126
|
+
return var
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def attachment(self) -> str:
|
|
130
|
+
return self.__pg__
|
|
131
|
+
|
|
132
|
+
@attachment.setter
|
|
133
|
+
def attachment(self, attachment:str):
|
|
134
|
+
self.__pg__ = attachment
|
|
135
|
+
|
|
136
|
+
def figure(self, **kwargs) -> Plot:
|
|
137
|
+
return Plot(self.data, **kwargs)
|
|
138
|
+
|
|
139
|
+
def to_report(self, row:int=1):
|
|
140
|
+
"""
|
|
141
|
+
@param row: [int] 시작 행 번호
|
|
142
|
+
@param attach: [str] 그림 파일 경로
|
|
143
|
+
"""
|
|
144
|
+
if isinstance(self.workbook, Workbook):
|
|
145
|
+
wb = self.workbook
|
|
146
|
+
ws = wb.worksheets_objs[0]
|
|
147
|
+
else:
|
|
148
|
+
# wb = self.workbook = Workbook(filename=PATH.DOWNLOADS.makefile(f"{self['Test Case Name']}.xlsx"))
|
|
149
|
+
wb = self.workbook = Workbook(filename=env.DOWNLOADS / f"{self['Test Case Name']}.xlsx")
|
|
150
|
+
ws = wb.add_worksheet(name="Test Report")
|
|
151
|
+
ws.set_column('A:A', 1.63)
|
|
152
|
+
for col in ["C", "F", "I", "L", "O"]:
|
|
153
|
+
ws.set_column(f'{col}:{col}', 3.13)
|
|
154
|
+
for col in ["B", "D", "E", "G", "H", "J", "K", "M", "N", "P"]:
|
|
155
|
+
ws.set_column(f'{col}:{col}', 13)
|
|
156
|
+
|
|
157
|
+
styler = Style(wb=wb, ws=ws)
|
|
158
|
+
ws.merge_range(f'B{row}:C{row}', 'Test Category', styler.report_label["Category"])
|
|
159
|
+
ws.merge_range(f'D{row}:E{row}', 'Test Group', styler.report_label["Group"])
|
|
160
|
+
ws.merge_range(f'F{row}:H{row}', 'Test Case ID', styler.report_label["Test Case - ID"])
|
|
161
|
+
ws.merge_range(f'I{row}:L{row}', 'Test Case Name', styler.report_label["Test Case Name"])
|
|
162
|
+
ws.merge_range(f'M{row}:P{row}', 'Requirement - Traceability', styler.report_label["Requirement - Traceability"])
|
|
163
|
+
ws.merge_range(f'B{row + 2}:D{row + 2}', 'Test Purpose', styler.report_label["Test Purpose, Description"])
|
|
164
|
+
ws.merge_range(f'E{row + 2}:G{row + 2}', 'Pre-Condition', styler.report_label["PreCondition (PC) - Description"])
|
|
165
|
+
ws.merge_range(f'H{row + 2}:J{row + 2}', 'Test Execution', styler.report_label["Test Execution (TE) - Description"])
|
|
166
|
+
ws.merge_range(f'K{row + 2}:M{row + 2}', 'Expected Result', styler.report_label["Expected Results (ER) - Description"])
|
|
167
|
+
ws.merge_range(f'N{row + 2}:P{row + 2}', 'Test Result', styler.report_label["Test Result"])
|
|
168
|
+
ws.merge_range(f'B{row + 5}:L{row + 5}', 'Test Result Graph', styler.report_label["Test Result Graph"])
|
|
169
|
+
ws.merge_range(f'M{row + 5}:P{row + 5}', '시험 결과 분석 / Comment / Remark', styler.report_label["Test Result Description"])
|
|
170
|
+
ws.merge_range(f'B{row + 27}:D{row + 27}', 'Test Conductor', styler.report_label["Test Conductor"])
|
|
171
|
+
ws.merge_range(f'E{row + 27}:G{row + 27}', 'Test SW', styler.report_label["Test SW"])
|
|
172
|
+
ws.merge_range(f'H{row + 27}:J{row + 27}', 'Test HW', styler.report_label["Test HW"])
|
|
173
|
+
ws.merge_range(f'K{row + 27}:M{row + 27}', 'Test Vehicle / Engine / HIL', styler.report_label["Test Case Name"])
|
|
174
|
+
ws.merge_range(f'N{row + 27}:P{row + 27}', 'Test Environment', styler.report_label["Test Environment"])
|
|
175
|
+
ws.merge_range(f'B{row + 29}:G{row + 29}', 'Remark / Comment', styler.report_label['Remark / Comment'])
|
|
176
|
+
ws.merge_range(f'H{row + 29}:J{row + 29}', 'Measure / Log File (.dat)', styler.report_label['Measure / Log File (.dat)'])
|
|
177
|
+
ws.merge_range(f'K{row + 29}:M{row + 29}', 'MDA Configuration File (.xda)', styler.report_label['MDA Configuration File (.xda)'])
|
|
178
|
+
ws.merge_range(f'N{row + 29}:P{row + 29}', 'Experiment File (.exp)', styler.report_label['Experiment File (.exp)'])
|
|
179
|
+
ws.merge_range(f'B{row + 1}:C{row + 1}', self['Category'], styler.report_value["Category"])
|
|
180
|
+
ws.merge_range(f'D{row + 1}:E{row + 1}', self['Group'], styler.report_value["Group"])
|
|
181
|
+
ws.merge_range(f'F{row + 1}:H{row + 1}', self['Test Case - ID'], styler.report_value["Test Case - ID"])
|
|
182
|
+
ws.merge_range(f'I{row + 1}:L{row + 1}', self['Test Case Name'], styler.report_value["Test Case Name"])
|
|
183
|
+
ws.merge_range(f'M{row + 1}:P{row + 1}', self['Requirement - Traceability'], styler.report_value["Requirement - Traceability"])
|
|
184
|
+
ws.merge_range(f'B{row + 3}:D{row + 4}', self['Test Purpose, Description'], styler.report_value["Test Purpose, Description"])
|
|
185
|
+
ws.merge_range(f'E{row + 3}:G{row + 3}', self['PreCondition (PC) - Description'], styler.report_value["PreCondition (PC) - Description"])
|
|
186
|
+
ws.write(f'E{row + 4}', self['PC-Variable'], styler.report_value["PC-Variable"])
|
|
187
|
+
ws.write(f'F{row + 4}', self['PC-Compare'], styler.report_value["PC-Compare"])
|
|
188
|
+
ws.write(f'G{row + 4}', self['PC-Value'], styler.report_value["PC-Value"])
|
|
189
|
+
ws.merge_range(f'H{row + 3}:J{row + 3}', self['Test Execution (TE) - Description'], styler.report_value["Test Execution (TE) - Description"])
|
|
190
|
+
ws.write(f'H{row + 4}', self['TE-Variable'], styler.report_value["TE-Variable"])
|
|
191
|
+
ws.write(f'I{row + 4}', self['TE-Compare'], styler.report_value["TE-Compare"])
|
|
192
|
+
ws.write(f'J{row + 4}', self['TE-Value'], styler.report_value["TE-Value"])
|
|
193
|
+
ws.merge_range(f'K{row + 3}:M{row + 3}', self['Expected Results (ER) - Description'], styler.report_value["Expected Results (ER) - Description"])
|
|
194
|
+
ws.write(f'K{row + 4}', self['ER-Variable'], styler.report_value["ER-Variable"])
|
|
195
|
+
ws.write(f'L{row + 4}', self['ER-Compare'], styler.report_value["ER-Compare"])
|
|
196
|
+
ws.write(f'M{row + 4}', self['ER-Value'], styler.report_value["ER-Value"])
|
|
197
|
+
ws.merge_range(f'N{row + 3}:P{row + 4}', self['Test Result'], styler.report_value["Test Result"])
|
|
198
|
+
ws.merge_range(f'B{row + 6}:L{row + 26}', '', styler.report_value["Test Result Graph"])
|
|
199
|
+
if self.attachment:
|
|
200
|
+
ws.insert_image(row + 5, 1, self.attachment, {
|
|
201
|
+
'x_offset': 0,
|
|
202
|
+
'y_offset': 0,
|
|
203
|
+
'x_scale': 0.4,
|
|
204
|
+
'y_scale': 0.4
|
|
205
|
+
})
|
|
206
|
+
ws.merge_range(f'M{row + 6}:P{row + 26}', self['Test Result Description'], styler.report_value['Test Result Description'])
|
|
207
|
+
ws.merge_range(f'B{row + 28}:D{row + 28}', self['Test Conductor'], styler.report_value['Test Conductor'])
|
|
208
|
+
ws.merge_range(f'E{row + 28}:G{row + 28}', self['Test SW'], styler.report_value['Test SW'])
|
|
209
|
+
ws.merge_range(f'H{row + 28}:J{row + 28}', self['Test HW'], styler.report_value['Test HW'])
|
|
210
|
+
ws.merge_range(f'K{row + 28}:M{row + 28}', self['Test Vehicle / Engine / HIL'], styler.report_value['Test Vehicle / Engine / HIL'])
|
|
211
|
+
ws.merge_range(f'N{row + 28}:P{row + 28}', self['Test Environment'], styler.report_value['Test Environment'])
|
|
212
|
+
ws.merge_range(f'B{row + 30}:G{row + 30}', self['Remark / Comment'], styler.report_value['Remark / Comment'])
|
|
213
|
+
ws.merge_range(f'H{row + 30}:J{row + 30}', self['Measure / Log File (.dat)'], styler.report_value['Measure / Log File (.dat)'])
|
|
214
|
+
ws.merge_range(f'K{row + 30}:M{row + 30}', self['MDA Configuration File (.xda)'], styler.report_value['MDA Configuration File (.xda)'])
|
|
215
|
+
ws.merge_range(f'N{row + 30}:P{row + 30}', self['Experiment File (.exp)'], styler.report_value['Experiment File (.exp)'])
|
|
216
|
+
for i in range(32):
|
|
217
|
+
ws.set_row(i + row, 48.5 if i == 2 else 19.25)
|
|
218
|
+
|
|
219
|
+
if not isinstance(self.workbook, Workbook):
|
|
220
|
+
wb.close()
|
|
221
|
+
return
|
|
222
|
+
|
|
223
|
+
if __name__ == "__main__":
|
|
224
|
+
unit = UnitTestCase()
|
|
225
|
+
print(unit)
|
|
226
|
+
unit['Test Result'] = "PASS TEST"
|
|
227
|
+
print(unit)
|
cannect/errors.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class AmdDuplicationError(Exception):
|
|
2
|
+
pass
|
|
3
|
+
|
|
4
|
+
class AmdFormatError(Exception):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
class AmdNotFoundError(Exception):
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
class AscetWorspaceFormatError(Exception):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
class CANDBError(Exception):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
class InternalServerError(Exception):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
class SVNError(Exception):
|
|
20
|
+
pass
|
cannect/schema/candb.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
from cannect.errors import CANDBError
|
|
2
|
+
from pandas import DataFrame, Series
|
|
3
|
+
from typing import Dict, List, Union
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CanSignal(object):
|
|
8
|
+
"""
|
|
9
|
+
CAN DB의 단일 신호에 대한 정보 타입
|
|
10
|
+
"""
|
|
11
|
+
def __init__(self, signal:Series):
|
|
12
|
+
self.signal = signal = signal.copy()
|
|
13
|
+
self.name = name = signal["Signal"]
|
|
14
|
+
|
|
15
|
+
# RPA 정보 연산
|
|
16
|
+
length, \
|
|
17
|
+
cycleTime, \
|
|
18
|
+
startValueInHex, \
|
|
19
|
+
factor, \
|
|
20
|
+
offset, \
|
|
21
|
+
valueType, \
|
|
22
|
+
signedProcessing = signal[[
|
|
23
|
+
"Length", "Cycle Time", "GenSigStartValue", "Factor", "Offset", "Value Type", "SignedProcessing"
|
|
24
|
+
]]
|
|
25
|
+
|
|
26
|
+
# Physical Start Value (Initial Value)
|
|
27
|
+
signal["StartValue"] = int(startValueInHex, 16) * factor + offset
|
|
28
|
+
|
|
29
|
+
# Physical Minimum, Maximum Calculation
|
|
30
|
+
if valueType.lower() == "unsigned":
|
|
31
|
+
signal["physMax"] = factor * (2 ** length - 1) + offset
|
|
32
|
+
signal["physMin"] = offset
|
|
33
|
+
else:
|
|
34
|
+
if signedProcessing.lower() == "absolute":
|
|
35
|
+
signal["physMax"] = factor * (2 ** (length - 1) - 1) + offset
|
|
36
|
+
signal["physMin"] = (-1) * factor * (2 ** (length - 1) - 1) + offset
|
|
37
|
+
else:
|
|
38
|
+
signal["physMax"] = factor * (2 ** (length - 1) - 1) + offset
|
|
39
|
+
signal["physMin"] = (-1) * factor * (2 ** (length - 1)) + offset
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
def __getitem__(self, item):
|
|
43
|
+
return self.signal[item]
|
|
44
|
+
|
|
45
|
+
def __getattr__(self, item):
|
|
46
|
+
try:
|
|
47
|
+
return self.__getattribute__(item)
|
|
48
|
+
except AttributeError:
|
|
49
|
+
return getattr(self.signal, item)
|
|
50
|
+
|
|
51
|
+
def __str__(self) -> str:
|
|
52
|
+
"""
|
|
53
|
+
:return:
|
|
54
|
+
출력 예시 ::
|
|
55
|
+
ECU BMS
|
|
56
|
+
Message BMS6
|
|
57
|
+
ID 0x51E
|
|
58
|
+
DLC 8.0
|
|
59
|
+
Send Type P
|
|
60
|
+
Cycle Time 100.0
|
|
61
|
+
Signal CR_Bms_BatMinTemp_C
|
|
62
|
+
Definition Actual Minimum Temperature of 48V battery pack
|
|
63
|
+
Length 8.0
|
|
64
|
+
StartBit 40.0
|
|
65
|
+
Sig Receivers EMS
|
|
66
|
+
UserSigValidity IG1
|
|
67
|
+
Value Table
|
|
68
|
+
Value Type Signed
|
|
69
|
+
GenSigStartValue 0x1E
|
|
70
|
+
Factor 1.0
|
|
71
|
+
Offset 0.0
|
|
72
|
+
Min -128.0
|
|
73
|
+
Max 126.0
|
|
74
|
+
Unit degC
|
|
75
|
+
Local Network Wake Up Request No
|
|
76
|
+
Network Request Holding Time 0
|
|
77
|
+
Description This signal is for the 48V Battery System appl...
|
|
78
|
+
Version 1.04.05
|
|
79
|
+
Timeout 2000
|
|
80
|
+
ByteOrder Intel
|
|
81
|
+
ICE Channel L
|
|
82
|
+
ICE WakeUp
|
|
83
|
+
HEV Channel
|
|
84
|
+
HEV WakeUp
|
|
85
|
+
SystemConstant MICROEPT_48V_SC == 1
|
|
86
|
+
Codeword Cfg_MeptSys_C > 0
|
|
87
|
+
Formula T_Cels_q1
|
|
88
|
+
SignedProcessing Complement
|
|
89
|
+
InterfacedVariable Com_tBmsBatMn
|
|
90
|
+
SignalRenamed
|
|
91
|
+
History
|
|
92
|
+
Remark
|
|
93
|
+
Name: 100, dtype: object
|
|
94
|
+
"""
|
|
95
|
+
return str(self.signal)
|
|
96
|
+
|
|
97
|
+
def isCrc(self) -> bool:
|
|
98
|
+
return (("crc" in self.name.lower()) and (self["StartBit"] == 0)) or \
|
|
99
|
+
("checksum" in self.name.lower()) or \
|
|
100
|
+
("chks" in self.name.lower()) or \
|
|
101
|
+
(self.name.startswith("VVDIN_EMS_CRC")) or \
|
|
102
|
+
(self.name.startswith("VVDIN_CRC"))
|
|
103
|
+
|
|
104
|
+
def isAliveCounter(self) -> bool:
|
|
105
|
+
if self.name == "HU_AliveStatus":
|
|
106
|
+
return False
|
|
107
|
+
return ((self["Message"] == "TMU_01_200ms") and (self.name == "VSVI_AlvCntVal")) or \
|
|
108
|
+
(("alv" in self.name.lower() or "alivec" in self.name.lower()) and self["StartBit"] <= 16) or \
|
|
109
|
+
("alive" in self.name.lower()) or \
|
|
110
|
+
(self.name.startswith("VVDIN_EMS_CNT")) or \
|
|
111
|
+
(self.name.startswith("VVDIN_CNT")) or \
|
|
112
|
+
(self.name.lower().endswith("req_a_counter")) or \
|
|
113
|
+
("A_Counter" in self.name)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class CanMessage(object):
|
|
117
|
+
"""
|
|
118
|
+
CAN DB의 단일 메시지에 대한 정보 타입
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
ITERATION_INCLUDES_ALIVECOUNTER:bool = False
|
|
122
|
+
ITERATION_INCLUDES_CRC:bool = False
|
|
123
|
+
|
|
124
|
+
def __init__(self, signals:DataFrame):
|
|
125
|
+
__attr__:Dict[str, Union[str, int, float]] = signals.iloc[0].to_dict()
|
|
126
|
+
__attr__['Version'] = signals["Version"].sort_values(ascending=True).iloc[-1]
|
|
127
|
+
if "E" in __attr__["Send Type"]:
|
|
128
|
+
if pd.isna(__attr__["Cycle Time"]):
|
|
129
|
+
raise CANDBError()
|
|
130
|
+
__attr__["taskTime"] = 0.04
|
|
131
|
+
else:
|
|
132
|
+
__attr__["taskTime"] = __attr__["Cycle Time"] / 1000
|
|
133
|
+
|
|
134
|
+
# ES SPEC TIMEOUT TIME
|
|
135
|
+
if __attr__["Cycle Time"] <= 50:
|
|
136
|
+
__attr__["timeoutTime"] = 0.5
|
|
137
|
+
elif __attr__["Cycle Time"] < 500:
|
|
138
|
+
__attr__["timeoutTime"] = 1.5
|
|
139
|
+
else:
|
|
140
|
+
__attr__["timeoutTime"] = 5.0
|
|
141
|
+
|
|
142
|
+
# NON-ES G-PROJECT EXCEPTION
|
|
143
|
+
if __attr__["Cycle Time"] == 1:
|
|
144
|
+
__attr__["timeoutTime"] = 0.25
|
|
145
|
+
|
|
146
|
+
self.__attr__ = __attr__
|
|
147
|
+
self.signals = signals
|
|
148
|
+
self.name = __attr__["Message"]
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
def __getitem__(self, item):
|
|
152
|
+
return self.__attr__[item]
|
|
153
|
+
|
|
154
|
+
def __iter__(self):
|
|
155
|
+
for _, row in self.signals.iterrows():
|
|
156
|
+
sig = CanSignal(row)
|
|
157
|
+
if sig.isCrc() and not self.ITERATION_INCLUDES_CRC:
|
|
158
|
+
continue
|
|
159
|
+
if sig.isAliveCounter() and not self.ITERATION_INCLUDES_ALIVECOUNTER:
|
|
160
|
+
continue
|
|
161
|
+
yield sig
|
|
162
|
+
|
|
163
|
+
def __len__(self):
|
|
164
|
+
return len(self.signals)
|
|
165
|
+
|
|
166
|
+
def __str__(self) -> str:
|
|
167
|
+
return str(Series(self.__attr__))
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def crc(self) -> Union[CanSignal, Series]:
|
|
171
|
+
revert = False
|
|
172
|
+
if not self.ITERATION_INCLUDES_CRC:
|
|
173
|
+
self.ITERATION_INCLUDES_CRC = revert = True
|
|
174
|
+
for sig in self:
|
|
175
|
+
if sig.isCrc():
|
|
176
|
+
if revert:
|
|
177
|
+
self.ITERATION_INCLUDES_CRC = False
|
|
178
|
+
return sig
|
|
179
|
+
return Series()
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def aliveCounter(self) -> Union[CanSignal, Series]:
|
|
183
|
+
revert = False
|
|
184
|
+
if not self.ITERATION_INCLUDES_ALIVECOUNTER:
|
|
185
|
+
self.ITERATION_INCLUDES_ALIVECOUNTER = revert = True
|
|
186
|
+
for sig in self:
|
|
187
|
+
if sig.isAliveCounter():
|
|
188
|
+
if revert:
|
|
189
|
+
self.ITERATION_INCLUDES_ALIVECOUNTER = False
|
|
190
|
+
return sig
|
|
191
|
+
return Series()
|
|
192
|
+
|
|
193
|
+
def hasCrc(self) -> bool:
|
|
194
|
+
if not hasattr(self, '__hascrc__'):
|
|
195
|
+
self.__setattr__('__hascrc__', not self.crc.empty)
|
|
196
|
+
return self.__getattribute__('__hascrc__')
|
|
197
|
+
|
|
198
|
+
def hasAliveCounter(self) -> bool:
|
|
199
|
+
if not hasattr(self, '__hasac__'):
|
|
200
|
+
self.__setattr__('__hasac__', not self.aliveCounter.empty)
|
|
201
|
+
return self.__getattribute__('__hasac__')
|
|
202
|
+
|
|
203
|
+
def isTsw(self) -> bool:
|
|
204
|
+
status = self.signals["Status"].unique().tolist()
|
|
205
|
+
return len(status) == 1 and status[0].lower() == "tsw"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
if __name__ == "__main__":
|
|
210
|
+
|
|
211
|
+
from pandas import read_json
|
|
212
|
+
from pandas import set_option
|
|
213
|
+
set_option('display.expand_frame_repr', False)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
src = r'E:\SVN\dev.bsw\hkmc.ems.bsw.docs\branches\HEPG_Ver1p1\11_ProjectManagement\CAN_Database\dev\KEFICO-EMS_CANFD_V25.08.01.json'
|
|
217
|
+
rdb = read_json(src, orient='index')
|
|
218
|
+
# print(rdb)
|
|
219
|
+
|
|
220
|
+
# sig = CanSignal(rdb.iloc[100])
|
|
221
|
+
# print(sig)
|
|
222
|
+
|
|
223
|
+
msg = CanMessage(rdb[rdb["Message"] == 'HU_GW_PE_01'])
|
|
224
|
+
print(msg)
|
|
225
|
+
print(msg.hasAliveCounter())
|
|
226
|
+
|