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,81 @@
|
|
|
1
|
+
from docx.document import Document
|
|
2
|
+
from docx.styles.styles import Styles
|
|
3
|
+
from docx.styles.style import ParagraphStyle
|
|
4
|
+
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
|
5
|
+
from docx.enum.style import WD_STYLE_TYPE
|
|
6
|
+
from docx.shared import RGBColor, Pt, Inches
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CustomStyle:
|
|
12
|
+
|
|
13
|
+
def __init__(self, doc:Document):
|
|
14
|
+
self.doc = doc
|
|
15
|
+
return
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def title(self) -> ParagraphStyle:
|
|
19
|
+
if not "title" in self.doc.styles:
|
|
20
|
+
style = self.doc.styles.add_style("title", WD_PARAGRAPH_ALIGNMENT.CENTER)
|
|
21
|
+
style.font.name = "현대산스 Head"
|
|
22
|
+
style.font.size = Pt(28)
|
|
23
|
+
style.font.color.rgb = RGBColor(0, 0, 0)
|
|
24
|
+
style.font.bold = True
|
|
25
|
+
style.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
26
|
+
return self.doc.styles['title']
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def overview_left(self) -> Styles:
|
|
30
|
+
if not "overview_left" in self.doc.styles:
|
|
31
|
+
style = self.doc.styles.add_style("overview_left", WD_PARAGRAPH_ALIGNMENT.CENTER)
|
|
32
|
+
style.font.name = "현대산스 Text"
|
|
33
|
+
style.font.size = Pt(11)
|
|
34
|
+
style.font.color.rgb = RGBColor(0, 0, 0)
|
|
35
|
+
style.font.bold = True
|
|
36
|
+
style.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
37
|
+
return self.doc.styles['overview_left']
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def overview_right(self) -> Styles:
|
|
41
|
+
if not "overview_right" in self.doc.styles:
|
|
42
|
+
style = self.doc.styles.add_style("overview_right", WD_PARAGRAPH_ALIGNMENT.CENTER)
|
|
43
|
+
style.font.name = "현대산스 Text"
|
|
44
|
+
style.font.size = Pt(10)
|
|
45
|
+
style.font.color.rgb = RGBColor(0, 0, 0)
|
|
46
|
+
style.font.bold = False
|
|
47
|
+
style.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT
|
|
48
|
+
return self.doc.styles['overview_right']
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def footer(self) -> Styles:
|
|
52
|
+
if not "my_footer" in self.doc.styles:
|
|
53
|
+
style = self.doc.styles.add_style("my_footer", WD_STYLE_TYPE.PARAGRAPH)
|
|
54
|
+
style.font.name = "현대산스 Text"
|
|
55
|
+
style.font.size = Pt(10)
|
|
56
|
+
style.font.color.rgb = RGBColor(0, 0, 0)
|
|
57
|
+
style.font.bold = False
|
|
58
|
+
style.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
59
|
+
return self.doc.styles['my_footer']
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def header_left(self) -> Styles:
|
|
63
|
+
if not "header_left" in self.doc.styles:
|
|
64
|
+
style = self.doc.styles.add_style("header_left", WD_PARAGRAPH_ALIGNMENT.CENTER)
|
|
65
|
+
style.font.name = "현대산스 Text"
|
|
66
|
+
style.font.size = Pt(8)
|
|
67
|
+
style.font.color.rgb = RGBColor(0, 0, 0)
|
|
68
|
+
style.font.bold = False
|
|
69
|
+
style.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT
|
|
70
|
+
return self.doc.styles['header_left']
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def header_right(self) -> Styles:
|
|
74
|
+
if not "header_right" in self.doc.styles:
|
|
75
|
+
style = self.doc.styles.add_style("header_right", WD_PARAGRAPH_ALIGNMENT.CENTER)
|
|
76
|
+
style.font.name = "현대산스 Text"
|
|
77
|
+
style.font.size = Pt(8)
|
|
78
|
+
style.font.color.rgb = RGBColor(0, 0, 0)
|
|
79
|
+
style.font.bold = False
|
|
80
|
+
style.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT
|
|
81
|
+
return self.doc.styles['header_right']
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from cannect.config import env
|
|
2
|
+
from cannect.core.can.db.reader import CANDBReader
|
|
3
|
+
from cannect.core.can.db.specification.styles import CustomStyle
|
|
4
|
+
from cannect.core.can.db.specification.message import Message
|
|
5
|
+
from cannect.core.subversion import Subversion
|
|
6
|
+
from cannect.utils import tools
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from docx import Document
|
|
10
|
+
from docx.parts.styles import Styles
|
|
11
|
+
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
|
12
|
+
from docx.shared import RGBColor, Pt, Inches
|
|
13
|
+
from pandas import DataFrame
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from tqdm.auto import tqdm
|
|
16
|
+
from typing import Union
|
|
17
|
+
import os, time, site
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Specification:
|
|
21
|
+
|
|
22
|
+
def __init__(self, db:Union[CANDBReader, DataFrame]):
|
|
23
|
+
if isinstance(db, DataFrame):
|
|
24
|
+
db = CANDBReader(db)
|
|
25
|
+
self.db = db
|
|
26
|
+
|
|
27
|
+
if not (env.SVN_CANDB / r"사양서/resource").exists():
|
|
28
|
+
Subversion.update(env.SVN_CANDB / "사양서")
|
|
29
|
+
|
|
30
|
+
template = Path(site.getsitepackages()[1]) / 'docx/templates/default.docx'
|
|
31
|
+
if template.exists():
|
|
32
|
+
os.remove(template)
|
|
33
|
+
tools.copy_to(env.SVN_CANDB / r"사양서/resource/default", template.parent)
|
|
34
|
+
time.sleep(0.5)
|
|
35
|
+
os.rename(template.parent / 'default', template.parent / 'default.docx')
|
|
36
|
+
time.sleep(0.5)
|
|
37
|
+
|
|
38
|
+
self.doc = doc = Document()
|
|
39
|
+
self.styles = CustomStyle(doc)
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
def set_title(self):
|
|
43
|
+
self.doc.add_paragraph(
|
|
44
|
+
"\n자체제어기 EMS/ASW\n통신 사양서(CAN-FD)\n\n",
|
|
45
|
+
style=self.styles.title
|
|
46
|
+
)
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
def set_ci(self):
|
|
50
|
+
png = env.SVN_CANDB / r"사양서/resource/ci_cover.png"
|
|
51
|
+
paragraph = self.doc.add_paragraph()
|
|
52
|
+
runner = paragraph.add_run()
|
|
53
|
+
runner.add_picture(str(png), width=Inches(6))
|
|
54
|
+
paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
|
|
55
|
+
self.doc.add_paragraph("\n", style=self.styles.title)
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
def set_overview(self):
|
|
59
|
+
items = {
|
|
60
|
+
'DATABASE': f"자체제어기/EMS CAN-FD {self.db.revision}",
|
|
61
|
+
'COMPANY': env.COMPANY,
|
|
62
|
+
'DIVISION': env.DIVISION,
|
|
63
|
+
'RELEASE': datetime.now().strftime("%Y-%m-%d"),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
table = self.doc.add_table(rows=len(items), cols=2)
|
|
67
|
+
table.style = 'Table Grid'
|
|
68
|
+
for n, (key, value) in enumerate(items.items()):
|
|
69
|
+
left = table.rows[n].cells[0]
|
|
70
|
+
left.width = Inches(1)
|
|
71
|
+
name = left.paragraphs[0]
|
|
72
|
+
name.style = self.styles.overview_left
|
|
73
|
+
name.text = key
|
|
74
|
+
|
|
75
|
+
right = table.rows[n].cells[1]
|
|
76
|
+
right.width = self.doc.sections[0].page_width
|
|
77
|
+
text = right.paragraphs[0]
|
|
78
|
+
text.style = self.styles.overview_right
|
|
79
|
+
text.text = value
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
def set_margin(self):
|
|
83
|
+
section = self.doc.sections[0]
|
|
84
|
+
section.left_margin = \
|
|
85
|
+
section.right_margin = Inches(0.5)
|
|
86
|
+
section.bottom_margin = \
|
|
87
|
+
section.top_margin = Inches(1)
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
def set_header(self, version:str):
|
|
91
|
+
header = self.doc.sections[0].header
|
|
92
|
+
for paragraph in header.paragraphs:
|
|
93
|
+
p = getattr(paragraph, "_element")
|
|
94
|
+
p.getparent().remove(p)
|
|
95
|
+
|
|
96
|
+
table = header.add_table(rows=1, cols=3, width=self.doc.sections[0].page_width)
|
|
97
|
+
|
|
98
|
+
left = table.rows[0].cells[0].paragraphs[0]
|
|
99
|
+
left.text = "자체제어기 EMS/ASW CAN-FD" + "\n" + f"DOC {version}"
|
|
100
|
+
left.style = self.styles.header_left
|
|
101
|
+
|
|
102
|
+
right = table.rows[0].cells[2].paragraphs[0]
|
|
103
|
+
right.text = env.DIVISION + "\n" + env.COMPANY
|
|
104
|
+
right.style = self.styles.header_right
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
def set_footer(self):
|
|
108
|
+
footer = self.doc.sections[0].footer
|
|
109
|
+
for paragraph in footer.paragraphs:
|
|
110
|
+
p = getattr(paragraph, "_element")
|
|
111
|
+
p.getparent().remove(p)
|
|
112
|
+
table = footer.add_table(rows=1, cols=1, width=self.doc.sections[0].page_width)
|
|
113
|
+
cell = table.rows[0].cells[0].paragraphs[0]
|
|
114
|
+
cell.text = env.COPYRIGHT
|
|
115
|
+
cell.style = self.styles.footer
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
def generate(self, filename:str):
|
|
119
|
+
if not filename.endswith('.docx'):
|
|
120
|
+
filename += '.docx'
|
|
121
|
+
objs = [(msg, obj) for msg, obj in self.db.messages.items()]
|
|
122
|
+
objs = sorted(objs, key=lambda x: x[0])
|
|
123
|
+
|
|
124
|
+
self.set_margin()
|
|
125
|
+
self.set_header(self.db.revision)
|
|
126
|
+
self.set_footer()
|
|
127
|
+
self.set_title()
|
|
128
|
+
self.set_ci()
|
|
129
|
+
self.set_overview()
|
|
130
|
+
|
|
131
|
+
self.doc.add_section()
|
|
132
|
+
message = Message(self.doc)
|
|
133
|
+
message.addHeading("EMS TRANSMIT")
|
|
134
|
+
transmit = tqdm([obj for _, obj in objs if obj["ECU"] == "EMS"])
|
|
135
|
+
for obj in transmit:
|
|
136
|
+
transmit.set_description(desc=f"{obj.name} 사양 생성")
|
|
137
|
+
message.addMessageHeading(obj)
|
|
138
|
+
message.addMessageSpec(obj)
|
|
139
|
+
message.addMessageLayout(obj)
|
|
140
|
+
message.addSignalList(obj)
|
|
141
|
+
message.addSignalProperty(obj)
|
|
142
|
+
|
|
143
|
+
message.addHeading("EMS RECEIVE")
|
|
144
|
+
receive = tqdm([obj for _, obj in objs if obj["ECU"] != "EMS"])
|
|
145
|
+
for obj in receive:
|
|
146
|
+
receive.set_description(desc=f"{obj.name} 사양 생성")
|
|
147
|
+
message.addMessageHeading(obj)
|
|
148
|
+
message.addMessageSpec(obj)
|
|
149
|
+
message.addMessageLayout(obj)
|
|
150
|
+
message.addSignalList(obj)
|
|
151
|
+
message.addSignalProperty(obj)
|
|
152
|
+
|
|
153
|
+
self.doc.save(env.DOWNLOADS / filename)
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
db = CANDBReader().mode('HEV')
|
|
159
|
+
|
|
160
|
+
spec = Specification(db)
|
|
161
|
+
spec.generate("TEST CAN SPEC")
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from cannect.config import env
|
|
2
|
+
from cannect.core.can.db.schema import standardize
|
|
3
|
+
from cannect.core.subversion import Subversion
|
|
4
|
+
from cannect.errors import CANDBError
|
|
5
|
+
from cannect.utils.excel import ComExcel
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from pandas import DataFrame
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from pyperclip import paste
|
|
11
|
+
from typing import Callable
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CANDBVcs:
|
|
16
|
+
"""
|
|
17
|
+
CAN DB Version Control System
|
|
18
|
+
RPA를 위한 CAN DB 버전 시스템이다. json 포맷으로 구성된 데이터파일에 대한 버전이며
|
|
19
|
+
pandas DataFrame과 호환한다. 데이터파일의 집합은 외부로 공개되어서는 안 되며
|
|
20
|
+
구동하는 호스트내 경로를 입력하여야 한다. 경로는 환경변수로 관리하거나 HMG 보안 처리된
|
|
21
|
+
서버가 Check-Out된 경로를 사용한다.
|
|
22
|
+
"""
|
|
23
|
+
silence :bool = False
|
|
24
|
+
logger :Callable = print
|
|
25
|
+
|
|
26
|
+
def __init__(self, filename:str=''):
|
|
27
|
+
if not filename:
|
|
28
|
+
filename = "자체제어기_KEFICO-EMS_CANFD.xlsx"
|
|
29
|
+
filepath = env.SVN_CANDB / filename
|
|
30
|
+
if not filepath.exists():
|
|
31
|
+
raise CANDBError(f'{filename} NOT EXISTS')
|
|
32
|
+
|
|
33
|
+
self.name = '.'.join(filename.split(".")[:-1])
|
|
34
|
+
self.filename = filename
|
|
35
|
+
self.filepath = filepath
|
|
36
|
+
self.history = history = Subversion.log(filepath)
|
|
37
|
+
self.revision = history.iloc[0, 0]
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
def _find_jsons(self) -> DataFrame:
|
|
41
|
+
data = []
|
|
42
|
+
for file in os.listdir((env.SVN_CANDB / "dev")):
|
|
43
|
+
if not file.endswith('.json'):
|
|
44
|
+
continue
|
|
45
|
+
if file.startswith(self.name) and file.endswith('.json'):
|
|
46
|
+
path = env.SVN_CANDB / f'dev/{file}'
|
|
47
|
+
data.append({
|
|
48
|
+
'revision': file.split("_")[-1].replace(".json", ""),
|
|
49
|
+
'datetime': datetime.fromtimestamp(os.path.getmtime(path)),
|
|
50
|
+
'name': file,
|
|
51
|
+
'path': path,
|
|
52
|
+
})
|
|
53
|
+
return DataFrame(data=data)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def json(self) -> Path:
|
|
57
|
+
"""
|
|
58
|
+
Excel CAN DB에 대한 최신 json 파일의 경로
|
|
59
|
+
:return:
|
|
60
|
+
"""
|
|
61
|
+
jsons = self._find_jsons()
|
|
62
|
+
if jsons.empty:
|
|
63
|
+
raise CANDBError(f'NO MATCHED JSON DB FOR {{{self.filename}}} IN SVN')
|
|
64
|
+
|
|
65
|
+
jsons = jsons[jsons['name'].str.startswith(f'{self.name}_{self.revision}')] \
|
|
66
|
+
.sort_values(by='revision', ascending=False)
|
|
67
|
+
if jsons.empty:
|
|
68
|
+
raise CANDBError(f'NO MATCHED JSON DB FOR {{{self.filename}}} @{{{self.revision}}} IN SVN')
|
|
69
|
+
return Path(jsons.iloc[0]['path'])
|
|
70
|
+
|
|
71
|
+
def to_json(self, mode:str='auto'):
|
|
72
|
+
if mode == 'auto':
|
|
73
|
+
xl = ComExcel(self.filepath)
|
|
74
|
+
xl.ws.UsedRange.Copy()
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
jsonpath = self.json
|
|
78
|
+
rev = str(int(jsonpath.name.split("@")[-1].split(".")[0]) + 1).zfill(2)
|
|
79
|
+
jsonpath = jsonpath.parent / f"{self.name}_{self.revision}@{rev}.json"
|
|
80
|
+
except CANDBError:
|
|
81
|
+
jsonpath = env.SVN_CANDB / f'dev/{self.name}_{self.revision}@01.json'
|
|
82
|
+
|
|
83
|
+
clipboard = [row.split("\t") for row in paste().split("\r\n")]
|
|
84
|
+
source = DataFrame(data=clipboard[1:], columns=standardize(clipboard[0]))
|
|
85
|
+
source = source[~source["ECU"].isna() & (source["ECU"] != "")]
|
|
86
|
+
source.to_json(jsonpath, orient='index')
|
|
87
|
+
if not self.silence:
|
|
88
|
+
self.logger("Manually Updated CAN DB from clipboard.")
|
|
89
|
+
self.logger(f"- Saved as : {jsonpath}")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
# def commit_json(self):
|
|
93
|
+
# Subversion.commit(self.json, message="[CANNECT] AUTO-COMMIT CAN JSON DB")
|
|
94
|
+
# return
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
from pandas import set_option
|
|
98
|
+
set_option('display.expand_frame_repr', False)
|
|
99
|
+
|
|
100
|
+
cdb = CANDBVcs()
|
|
101
|
+
# cdb = CANDBVcs(r"G-PROJECT_KEFICO-EMS_CANFD.xlsx")
|
|
102
|
+
cdb.to_json()
|
|
103
|
+
print(cdb.json)
|
|
104
|
+
# cdb.commit_json()
|
cannect/core/can/rule.py
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
from pandas import Series
|
|
2
|
+
from typing import Dict, Union, Hashable
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
MESSAGE_RENAME = {
|
|
7
|
+
# NEW NAME: OLD NAME #
|
|
8
|
+
"SCU_DIAG": 'MHSG_STATE3',
|
|
9
|
+
"SCU_DIAG2": "MHSG_STATE4",
|
|
10
|
+
"SCU_FUNCTIONAL": "MHSG_STATE2",
|
|
11
|
+
"SCU_STATUS": "MHSG_STATE1",
|
|
12
|
+
"MASTER_CTRL_REQ": "EMS_MHSG1",
|
|
13
|
+
"MASTER_EXT_REQ": "EMS_MHSG2",
|
|
14
|
+
"MASTER_SPEED_REQ": "EMS_MHSG_SPEED",
|
|
15
|
+
"MASTER_STARTER_REQ": "EMS_MHSG_STARTER"
|
|
16
|
+
}
|
|
17
|
+
class naming(object):
|
|
18
|
+
|
|
19
|
+
def __init__(self, message: Union[str, Dict, Series, Hashable], hw:str='ICE'):
|
|
20
|
+
|
|
21
|
+
if isinstance(message, Series) or isinstance(message, Dict):
|
|
22
|
+
self.message = message["Message"]
|
|
23
|
+
elif isinstance(message, str):
|
|
24
|
+
self.message = message
|
|
25
|
+
elif isinstance(message, Hashable):
|
|
26
|
+
self.message = str(message)
|
|
27
|
+
else:
|
|
28
|
+
raise TypeError(f"Unknown type for message; {message}")
|
|
29
|
+
self.name = self.message
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
[예외 처리]
|
|
33
|
+
DB 상 이름이 매우 길거나 구 DB상 이름으로 개발되었으나 신 DB에서 이름이 바뀐 경우
|
|
34
|
+
"""
|
|
35
|
+
if self.message.startswith("EGSNXUpStream"):
|
|
36
|
+
self.message = self.message.replace("UpStream", "")
|
|
37
|
+
if self.message == "Main_Status_Rear":
|
|
38
|
+
self.message = "NOx1Down"
|
|
39
|
+
if self.message == "O2_Rear":
|
|
40
|
+
self.message = "NOx1Ext"
|
|
41
|
+
if self.message.startswith("LEMS"):
|
|
42
|
+
self.message = f"L_{self.message[1:]}"
|
|
43
|
+
if self.message in MESSAGE_RENAME:
|
|
44
|
+
self.message = MESSAGE_RENAME[self.message]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
[Message Name to Element Name : Base]
|
|
49
|
+
ASCET CAN 모델에 사용하는 Element Naming Rule 정의
|
|
50
|
+
|
|
51
|
+
1. ASCET Element(Variable)에 사용하는 Message Name
|
|
52
|
+
Rule) split('_') --> capitalize() --> join()
|
|
53
|
+
* Under-Bar 없는 경우 capitalize()만 수행
|
|
54
|
+
* CAN-FD의 경우 메시지 주기 정보 제거
|
|
55
|
+
* Local CAN 식별자 L은 항상 대문자
|
|
56
|
+
* HEV 식별자 H는 항상 대문자
|
|
57
|
+
e.g.) Original Name | Rule Base Name
|
|
58
|
+
----------------------------------------
|
|
59
|
+
ABS_ESC_01_10ms | AbsEsc01
|
|
60
|
+
HU_GW_PE_01 | HuGwPe01
|
|
61
|
+
FPCM11 | Fpcm11
|
|
62
|
+
EMS_14_200ms | Ems14
|
|
63
|
+
HTCU_04_10ms | HTcu04
|
|
64
|
+
L_HTCU_10_10ms | LHTcu10
|
|
65
|
+
"""
|
|
66
|
+
splits = self.message.split('_')
|
|
67
|
+
splits = [split.lower().capitalize() for split in splits if not 'ms' in split]
|
|
68
|
+
self.base = base = ''.join(splits)
|
|
69
|
+
self.number = ''.join(re.findall(r'\d+', base))
|
|
70
|
+
if self.message.startswith("NOx"):
|
|
71
|
+
self.base = base = self.message
|
|
72
|
+
if "Htcu" in base:
|
|
73
|
+
self.base = base = base.replace("Htcu", "HTcu")
|
|
74
|
+
if self.message.startswith("L_EMS"):
|
|
75
|
+
self.base = base = f"L_EMS{splits[-1]}"
|
|
76
|
+
if self.message == "EMS_LDCBMS1":
|
|
77
|
+
self.base = base = "EmsLdcBms1"
|
|
78
|
+
if self.message.startswith("MHSG"):
|
|
79
|
+
self.base = base = f"StMhsg{splits[-1][-1]}"
|
|
80
|
+
self.pascal = base
|
|
81
|
+
|
|
82
|
+
self.root = root = ''.join([char for char in base if char.isalpha()])
|
|
83
|
+
if "Fd" in root:
|
|
84
|
+
self.root = root = root.replace("Fd", "")
|
|
85
|
+
if root == "BdcSmk":
|
|
86
|
+
self.root = root = "Bdc"
|
|
87
|
+
if self.message == "WHL_01_10ms":
|
|
88
|
+
self.root = root = "Abs"
|
|
89
|
+
for key in ["Bdc", "Hu", "Ilcu", "Pdc", "Sbcm", "Swrc"]:
|
|
90
|
+
if root.startswith(key):
|
|
91
|
+
self.root = root = key
|
|
92
|
+
break
|
|
93
|
+
|
|
94
|
+
"""
|
|
95
|
+
2. ASECT Hierarchy에 사용하는 Message Name
|
|
96
|
+
Rule) split('_') --> upper() --> join()
|
|
97
|
+
* Under-Bar 없는 경우 capitalize()만 수행
|
|
98
|
+
* CAN-FD의 경우 메시지 주기 정보 제거
|
|
99
|
+
e.g.) Original Name | Rule Base Name
|
|
100
|
+
----------------------------------------
|
|
101
|
+
ABS_ESC_01_10ms | ABSESC01
|
|
102
|
+
HU_GW_PE_01 | HUGWPE01
|
|
103
|
+
FPCM11 | FPCM11
|
|
104
|
+
EMS_14_200ms | EMS14
|
|
105
|
+
HTCU_04_10ms | HTCU04
|
|
106
|
+
L_HTCU_10_10ms | LHTCU10
|
|
107
|
+
"""
|
|
108
|
+
splits = [split.upper() for split in splits]
|
|
109
|
+
self.hierarchy = self.tag = self.upper = tag = ''.join(splits)
|
|
110
|
+
if self.message.startswith("MHSG"):
|
|
111
|
+
self.hierarchy = self.tag = self.upper = tag = f"MHSG{splits[-1][-1]}"
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
[Element Names]
|
|
115
|
+
1. Buffer : Can_{base}Buf_A
|
|
116
|
+
2. DLC : Can_{base}Size
|
|
117
|
+
3. Counter : Can_ct{base}
|
|
118
|
+
4. Counter Calc. : Can_ct{base}Calc
|
|
119
|
+
4. Timeout : Can_tiFlt{base}_C
|
|
120
|
+
5. Timer : Can_tiFlt{base}{Msg or Alv or Crc}
|
|
121
|
+
6. Validity : FD_cVld{base}{Msg or Alv or Crc}
|
|
122
|
+
7. Message Valid : FD_cVld{base}
|
|
123
|
+
8. Status : Com_st{base}
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
self.method = f'_{self.message}'
|
|
127
|
+
self.buffer = f"Can_{base}Buf_A"
|
|
128
|
+
self.dlc = f"Can_{base}Size"
|
|
129
|
+
self.counter = f"Can_ct{base}"
|
|
130
|
+
self.counterCalc = f"Can_ct{base}Calc"
|
|
131
|
+
self.thresholdTime = f"Can_tiFlt{base}_C"
|
|
132
|
+
|
|
133
|
+
self.messageCountTimer = f"Can_tiFlt{base}Msg"
|
|
134
|
+
self.messageCountValid = f"FD_cVld{base}Msg"
|
|
135
|
+
self.aliveCountTimer = f"Can_tiFlt{base}Alv"
|
|
136
|
+
self.aliveCountValid = f"FD_cVld{base}Alv"
|
|
137
|
+
self.crcTimer = f"Can_tiFlt{base}Crc"
|
|
138
|
+
self.crcValid = f"FD_cVld{base}Crc"
|
|
139
|
+
self.messageValid = f"FD_cVld{base}"
|
|
140
|
+
self.status = f"Com_st{base}"
|
|
141
|
+
|
|
142
|
+
# 진단 모듈 사용 Naming
|
|
143
|
+
self.detectionThresholdTime = f"CanD_tiMonDet{root}_C"
|
|
144
|
+
self.detectionThreshold = f"CanD_ctDet{root}_C"
|
|
145
|
+
self.eepReset = f"CanD_RstEep{root}_C"
|
|
146
|
+
|
|
147
|
+
self.diagnosisChannel = f"CanD_cEnaDiagBus1"
|
|
148
|
+
self.detectionChannel = f"CanD_cEnaDetBus1{root}"
|
|
149
|
+
self.eepIndex = f"EEP_FD{tag}"
|
|
150
|
+
self.eep = f"EEP_stFD{tag}"
|
|
151
|
+
self.eepReader = f"CanD_stRdEep{base}"
|
|
152
|
+
self.fid = f"Fid_FD{tag}D"
|
|
153
|
+
self.debounceTime = f"CanD_tiFlt{base}_C"
|
|
154
|
+
self.debounceTimerMsg = f"CanD_tiFlt{base}Msg"
|
|
155
|
+
self.debounceTimerCrc = f"CanD_tiFlt{base}Crc"
|
|
156
|
+
self.debounceTimerAlv = f"CanD_tiFlt{base}Alv"
|
|
157
|
+
self.deveMsg = f"DEve_FD{base}Msg"
|
|
158
|
+
self.deveCrc = f"DEve_FD{base}Crc"
|
|
159
|
+
self.deveAlv = f"DEve_FD{base}Alv"
|
|
160
|
+
self.diagnosisMsg = f"CanD_cErr{base}Msg"
|
|
161
|
+
self.diagnosisCrc = f"CanD_cErr{base}Crc"
|
|
162
|
+
self.diagnosisAlv = f"CanD_cErr{base}Alv"
|
|
163
|
+
self.detectionCounter = f"CanD_ctDet{base}"
|
|
164
|
+
self.detectionEnable = f"CanD_cEnaDet{base}"
|
|
165
|
+
self.diagnosisEnable = f"CanD_cEnaDiag{base}"
|
|
166
|
+
|
|
167
|
+
"""
|
|
168
|
+
3. Exceptions
|
|
169
|
+
1) DB 개정에 따라 메시지 이름이 변경되었으나 Binding 우려로 인해 기존 Naming을 유지해야 하는 경우
|
|
170
|
+
2) 개발자 실수에 따라 양산 반영된 오기 Naming이 Binding 우려로 인해 기존 Naming을 유지해야 하는 경우
|
|
171
|
+
3) DB 메시지 이름의 오타, 오탈 또는 길이 등의 사유로 인해 임의로 Naming을 변경한 경우
|
|
172
|
+
4) 상기 사유 외 예외 처리가 인정되는 경우
|
|
173
|
+
"""
|
|
174
|
+
if hw == "HEV":
|
|
175
|
+
self.eep = f"EEP_stHevFD{base}"
|
|
176
|
+
if self.message == "ABS_ESC_01_10ms":
|
|
177
|
+
self.eep = "EEP_stHevFDAbs01"
|
|
178
|
+
if self.message == "FPCM_01_100ms" :
|
|
179
|
+
self.eep = "EEP_stHevHSFpcm01"
|
|
180
|
+
if self.message == "SBCM_DRV_03_200ms":
|
|
181
|
+
self.eep = "EEP_stFDSBCMDRV03"
|
|
182
|
+
if self.message == "SBCM_DRV_FD_01_200ms":
|
|
183
|
+
self.eep = "EEP_stFDSBCMDRVFD01"
|
|
184
|
+
if self.message.startswith("EMS_CVVD"):
|
|
185
|
+
self.buffer = f"Can_{base}_Buf_A"
|
|
186
|
+
if (self.message.startswith('BMS') or self.message.startswith('LDC')) and len(self.message) == 4:
|
|
187
|
+
self.messageCountValid = f"Can_cVldMsgCt{base}"
|
|
188
|
+
self.aliveCountValid = f"Can_cVldAlvCt{base}"
|
|
189
|
+
self.crcValid = f"Can_cVldChks{base}"
|
|
190
|
+
self.eep = f"EEP_st48V{base}"
|
|
191
|
+
self.eepIndex = f"EEP_48V_DCAN{tag}"
|
|
192
|
+
if self.message.startswith('MHSG'):
|
|
193
|
+
self.messageCountValid = f"Can_cVldMsgCt{base}"
|
|
194
|
+
self.aliveCountValid = f"Can_cVldAlvCt{base}"
|
|
195
|
+
self.crcValid = f"Can_cVldChks{base}"
|
|
196
|
+
self.eep = f"EEP_st48V{base.replace('St', '')}"
|
|
197
|
+
self.eepIndex = f"EEP_48V_DCAN{tag}"
|
|
198
|
+
self.detectionEnable = f"CanD_cEnaDet{base.replace('St', '')}"
|
|
199
|
+
self.deveMsg = f"DEve_Can{base.replace('St', '')}Msg"
|
|
200
|
+
self.deveCrc = f"DEve_Can{base.replace('St', '')}Chks"
|
|
201
|
+
self.deveAlv = f"DEve_Can{base.replace('St', '')}Alv"
|
|
202
|
+
if self.message.startswith('CVVD'):
|
|
203
|
+
self.messageCountValid = f"Can_cVldMsgCnt{base}"
|
|
204
|
+
self.aliveCountValid = f"Can_cVldAlvCnt{base}"
|
|
205
|
+
self.crcValid = f"Can_cVldCRC{base}"
|
|
206
|
+
self.eep = f"EEP_st{tag}"
|
|
207
|
+
self.eepIndex = f"EEP_DCAN{tag}"
|
|
208
|
+
if self.message == "NOx1Down":
|
|
209
|
+
self.fid = "Fid_CanNOx1DownD"
|
|
210
|
+
self.deveMsg = "DEve_CanNOx1DownMsg"
|
|
211
|
+
self.eepIndex = f"EEP_DCAN{tag}"
|
|
212
|
+
if self.message == "NOx1Ext":
|
|
213
|
+
self.fid = "Fid_CanNOx1ExtD"
|
|
214
|
+
self.deveMsg = "DEve_CanNOx1ExtMsg"
|
|
215
|
+
self.eepIndex = f"EEP_DCAN{tag}"
|
|
216
|
+
if self.message == "ABS_ESC_01_10ms":
|
|
217
|
+
self.deveMsg = "DEve_FDAbs01Msg"
|
|
218
|
+
self.deveCrc = "DEve_FDAbs01Crc"
|
|
219
|
+
self.deveAlv = "DEve_FDAbs01Alv"
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
def __str__(self) -> str:
|
|
223
|
+
return self.message
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
|
|
228
|
+
rule = naming("FPCM_01_100ms")
|
|
229
|
+
print(rule.root)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from cannect.core.can.rule import naming
|
|
2
|
+
from cannect.core.testcase.unitcase import UnitTestCase
|
|
3
|
+
from pandas import DataFrame, Series
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Asw2CanUnit(UnitTestCase):
|
|
7
|
+
|
|
8
|
+
def __init__(self, sig:Series, io:DataFrame, **override):
|
|
9
|
+
nm = naming(sig)
|
|
10
|
+
unit = '-' if not sig.Unit else sig.Unit
|
|
11
|
+
model = io["module"].values[0]
|
|
12
|
+
path = io["Path"].values[0]
|
|
13
|
+
|
|
14
|
+
inputs = io[io["dir"] == 'input'].copy()
|
|
15
|
+
inputs.loc[inputs["value"].isna(), "value"] = "External"
|
|
16
|
+
outputs = io[io["dir"] == 'output'].copy()
|
|
17
|
+
var = ",".join([v for v in outputs["name"] if v.startswith(outputs["Signal"].values[0])])
|
|
18
|
+
o, c = '{', '}'
|
|
19
|
+
kwargs = {
|
|
20
|
+
"Category": "UNIT",
|
|
21
|
+
"Group": "CAN",
|
|
22
|
+
"Test Case Name": "Signal Interface Test",
|
|
23
|
+
"Test Purpose, Description": f"{sig.name} @{nm}({sig.ID}) Interface Test",
|
|
24
|
+
"Test Execution (TE) - Description": f"ENGINE ON/RUN\n"
|
|
25
|
+
f"- 차량 검증 시: 차량 주행에 따름\n"
|
|
26
|
+
f"- 정적 검증 시: 오프라인 SW 검증",
|
|
27
|
+
"TE-Variable": "\n".join(inputs["name"]),
|
|
28
|
+
"TE-Compare": "'" + "\n".join(["="] * len(inputs)),
|
|
29
|
+
"TE-Value": "\n".join(inputs["value"]),
|
|
30
|
+
"Expected Results (ER) - Description": f"정상 인터페이스\n"
|
|
31
|
+
f"- 로직 모델: %{model}{path}\n"
|
|
32
|
+
f"- CAN 신호 변수 = {var}\n"
|
|
33
|
+
f"신호 속성 DB 정합",
|
|
34
|
+
"ER-Variable": "\n".join(outputs["name"]),
|
|
35
|
+
"ER-Compare": "'" + "\n".join(["="] * len(outputs)),
|
|
36
|
+
"ER-Value": "\n".join(["Calculated"] * len(outputs)),
|
|
37
|
+
"Test Result Description": f"정상 인터페이스\n"
|
|
38
|
+
f"- {var} = {o}%{model}{path}{c}\n\n"
|
|
39
|
+
f"신호 속성\n"
|
|
40
|
+
f"- Factor: {sig.Factor}\n"
|
|
41
|
+
f"- Offset: {sig.Offset}\n"
|
|
42
|
+
f"- Min: {sig.Offset} [{unit}]\n"
|
|
43
|
+
f"- Max: {sig.Factor * (2 ** sig.Length - 1) + sig.Offset} [{unit}]\n"
|
|
44
|
+
f"* 차량 주행 시 Min/Max 검증 확인 불가" ,
|
|
45
|
+
}
|
|
46
|
+
kwargs.update(override)
|
|
47
|
+
super().__init__(**kwargs)
|
|
48
|
+
return
|