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,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
@@ -0,0 +1,5 @@
1
+ from .datadictionary import DataDictionary
2
+
3
+
4
+ # Alias
5
+ dD = DD = DataDictionary
@@ -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
+