pyroll-engineering-report 3.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.
- pyroll/engineering_report/__init__.py +6 -0
- pyroll/engineering_report/report.py +321 -0
- pyroll/engineering_report/translations.py +49 -0
- pyroll/engineering_report/units.py +31 -0
- pyroll_engineering_report-3.0.0.dist-info/METADATA +13 -0
- pyroll_engineering_report-3.0.0.dist-info/RECORD +8 -0
- pyroll_engineering_report-3.0.0.dist-info/WHEEL +4 -0
- pyroll_engineering_report-3.0.0.dist-info/licenses/LICENSE +11 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Callable, Optional
|
|
4
|
+
|
|
5
|
+
from fpdf import FPDF, FontFace
|
|
6
|
+
from pyroll.core import PassSequence, RollPass
|
|
7
|
+
|
|
8
|
+
from . import units
|
|
9
|
+
from .translations import LANGUAGES, translate
|
|
10
|
+
|
|
11
|
+
PAGE_WIDTH = 210.0
|
|
12
|
+
MARGIN = 10.0
|
|
13
|
+
CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN
|
|
14
|
+
LOGO_WIDTH = 40.0
|
|
15
|
+
FONT = "Helvetica"
|
|
16
|
+
MISSING_VALUE = "-"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EngineeringReportPdf(FPDF):
|
|
20
|
+
"""A4 page layout with optional logo, company name and product name."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
language: str,
|
|
25
|
+
generated_at: datetime,
|
|
26
|
+
product_name: Optional[str],
|
|
27
|
+
company_name: Optional[str],
|
|
28
|
+
mill_name: Optional[str],
|
|
29
|
+
logo: Optional[Path],
|
|
30
|
+
):
|
|
31
|
+
super().__init__(orientation="P", unit="mm", format="A4")
|
|
32
|
+
self.language = language
|
|
33
|
+
self.generated_at = generated_at
|
|
34
|
+
self.product_name = product_name
|
|
35
|
+
self.company_name = company_name
|
|
36
|
+
self.mill_name = mill_name
|
|
37
|
+
self.logo = logo
|
|
38
|
+
self.set_margins(left=MARGIN, top=MARGIN, right=MARGIN)
|
|
39
|
+
self.set_auto_page_break(auto=True, margin=20)
|
|
40
|
+
|
|
41
|
+
def translate(self, key: str) -> str:
|
|
42
|
+
return translate(key, self.language)
|
|
43
|
+
|
|
44
|
+
def header(self):
|
|
45
|
+
top = MARGIN
|
|
46
|
+
if self.logo is not None:
|
|
47
|
+
image_info = self.image(str(self.logo), x=MARGIN, y=top, w=LOGO_WIDTH)
|
|
48
|
+
top += image_info.rendered_height + 3
|
|
49
|
+
|
|
50
|
+
self.set_xy(MARGIN, MARGIN)
|
|
51
|
+
self.set_font(FONT, size=9)
|
|
52
|
+
self.cell(CONTENT_WIDTH, 5, f"{self.translate('page')} {self.page_no()}/{{nb}}", align="R")
|
|
53
|
+
|
|
54
|
+
title = self.translate("roll_pass_design")
|
|
55
|
+
if self.company_name:
|
|
56
|
+
title = f"{self.company_name} - {title}"
|
|
57
|
+
|
|
58
|
+
self.set_xy(MARGIN, top)
|
|
59
|
+
self.set_font(FONT, style="B", size=12)
|
|
60
|
+
self.cell(CONTENT_WIDTH * 0.75, 6, title)
|
|
61
|
+
self.set_font(FONT, size=9)
|
|
62
|
+
self.cell(CONTENT_WIDTH * 0.25, 6, self.generated_at.strftime("%d.%m.%Y"), align="R",
|
|
63
|
+
new_x="LMARGIN", new_y="NEXT")
|
|
64
|
+
|
|
65
|
+
self.set_font(FONT, size=9)
|
|
66
|
+
if self.mill_name:
|
|
67
|
+
self.cell(CONTENT_WIDTH, 5, f"{self.translate('rolling_mill')}: {self.mill_name}", new_x="LMARGIN", new_y="NEXT")
|
|
68
|
+
if self.product_name:
|
|
69
|
+
self.cell(CONTENT_WIDTH, 5, f"{self.translate('product')}: {self.product_name}", new_x="LMARGIN", new_y="NEXT")
|
|
70
|
+
|
|
71
|
+
line_y = self.get_y() + 1.5
|
|
72
|
+
self.set_line_width(0.3)
|
|
73
|
+
self.line(MARGIN, line_y, PAGE_WIDTH - MARGIN, line_y)
|
|
74
|
+
self.set_y(line_y + 4)
|
|
75
|
+
|
|
76
|
+
def footer(self):
|
|
77
|
+
self.set_y(-16)
|
|
78
|
+
self.set_line_width(0.2)
|
|
79
|
+
self.line(MARGIN, self.get_y(), PAGE_WIDTH - MARGIN, self.get_y())
|
|
80
|
+
self.ln(2)
|
|
81
|
+
|
|
82
|
+
self.set_font(FONT, style="I", size=7)
|
|
83
|
+
self.set_text_color(110, 110, 110)
|
|
84
|
+
third = CONTENT_WIDTH / 3
|
|
85
|
+
self.cell(third, 4, f"{self.translate('generated')} {self.generated_at:%Y-%m-%d %H:%M:%S}")
|
|
86
|
+
self.cell(third, 4, self.company_name or "", align="C")
|
|
87
|
+
self.cell(third, 4, self.translate("generated_with"), align="R")
|
|
88
|
+
self.set_text_color(0, 0, 0)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _format(value_getter: Callable[[], float], number_format: str) -> str:
|
|
92
|
+
"""Formatted value of the getter or MISSING_VALUE if the value is not available."""
|
|
93
|
+
try:
|
|
94
|
+
value = value_getter()
|
|
95
|
+
except (AttributeError, TypeError, ValueError, ZeroDivisionError):
|
|
96
|
+
return MISSING_VALUE
|
|
97
|
+
if value is None:
|
|
98
|
+
return MISSING_VALUE
|
|
99
|
+
return format(value, number_format)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _groove_label(roll_pass: RollPass) -> str:
|
|
103
|
+
label = getattr(roll_pass.roll.groove, "label", None)
|
|
104
|
+
return MISSING_VALUE if label in (None, "") else str(label)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _reduction(roll_pass: RollPass) -> float:
|
|
108
|
+
in_area = roll_pass.in_profile.cross_section.area
|
|
109
|
+
return (in_area - roll_pass.out_profile.cross_section.area) / in_area
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _utilization(roll_pass: RollPass) -> float:
|
|
113
|
+
return roll_pass.engine.power / roll_pass.engine.maximum_power
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _material_name(profile) -> str:
|
|
117
|
+
material = getattr(profile, "material", None)
|
|
118
|
+
if not material:
|
|
119
|
+
return MISSING_VALUE
|
|
120
|
+
if isinstance(material, str):
|
|
121
|
+
return material
|
|
122
|
+
return str(list(material)[0])
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _mass_per_length(area: float, density: float) -> float:
|
|
126
|
+
return area * density
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _info_lines(
|
|
130
|
+
pdf: EngineeringReportPdf,
|
|
131
|
+
sequence: PassSequence,
|
|
132
|
+
max_heating_capacity: Optional[float],
|
|
133
|
+
nominal_area: Optional[float],
|
|
134
|
+
target_mass_deviation: Optional[float],
|
|
135
|
+
) -> list[tuple[str, str]]:
|
|
136
|
+
in_profile = sequence.in_profile
|
|
137
|
+
out_profile = sequence.out_profile
|
|
138
|
+
last_pass = sequence.roll_passes[-1]
|
|
139
|
+
area_times_velocity = out_profile.cross_section.area * last_pass.out_profile.velocity
|
|
140
|
+
material_name = _material_name(in_profile)
|
|
141
|
+
|
|
142
|
+
lines = [
|
|
143
|
+
(pdf.translate("material"), material_name),
|
|
144
|
+
(pdf.translate("rolling_speed"), f"{_format(lambda: last_pass.out_profile.velocity, '.2f')} m/s"),
|
|
145
|
+
("A × v", f"{_format(lambda: units.to_mm2(area_times_velocity), '.1f')} mm²·m/s"),
|
|
146
|
+
(
|
|
147
|
+
pdf.translate("in_profile_size"),
|
|
148
|
+
f"{_format(lambda: units.to_mm(in_profile.height), '.1f')} × "
|
|
149
|
+
f"{_format(lambda: units.to_mm(in_profile.width), '.1f')} mm",
|
|
150
|
+
),
|
|
151
|
+
(pdf.translate("in_profile_temperature"), f"{_format(lambda: units.to_celsius(in_profile.temperature), '.0f')} °C"),
|
|
152
|
+
(
|
|
153
|
+
pdf.translate("production"),
|
|
154
|
+
f"{_format(lambda: units.to_tonnes_per_hour(area_times_velocity * in_profile.density), '.1f')} t/h",
|
|
155
|
+
),
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
if max_heating_capacity is not None:
|
|
159
|
+
lines.append((pdf.translate("max_heating_capacity"), f"{units.to_tonnes_per_hour(max_heating_capacity):.1f} t/h"))
|
|
160
|
+
|
|
161
|
+
if nominal_area is not None:
|
|
162
|
+
nominal_mass = _mass_per_length(nominal_area, in_profile.density)
|
|
163
|
+
lines.append((pdf.translate("nominal_mass"), f"{nominal_mass:.3f} kg/m"))
|
|
164
|
+
if target_mass_deviation is not None:
|
|
165
|
+
target_mass = nominal_mass * (1 + target_mass_deviation)
|
|
166
|
+
lines.append(
|
|
167
|
+
(pdf.translate("target_mass"), f"{target_mass:.3f} kg/m ({units.to_percent(target_mass_deviation):+.1f} %)")
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return lines
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _write_info_block(pdf: EngineeringReportPdf, info_lines: list[tuple[str, str]]):
|
|
174
|
+
pdf.set_font(FONT, size=9)
|
|
175
|
+
for label, value in info_lines:
|
|
176
|
+
pdf.cell(55, 4.5, label)
|
|
177
|
+
pdf.cell(CONTENT_WIDTH - 55, 4.5, f": {value}", new_x="LMARGIN", new_y="NEXT")
|
|
178
|
+
pdf.ln(4)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _write_table(
|
|
182
|
+
pdf: EngineeringReportPdf,
|
|
183
|
+
title_keys: list[str],
|
|
184
|
+
unit_labels: list[str],
|
|
185
|
+
rows: list[list[str]],
|
|
186
|
+
column_widths: list[float],
|
|
187
|
+
):
|
|
188
|
+
pdf.set_font(FONT, size=7)
|
|
189
|
+
heading_style = FontFace(emphasis="BOLD", fill_color=(225, 225, 225))
|
|
190
|
+
with pdf.table(
|
|
191
|
+
col_widths=column_widths,
|
|
192
|
+
text_align="RIGHT",
|
|
193
|
+
num_heading_rows=2,
|
|
194
|
+
headings_style=heading_style,
|
|
195
|
+
line_height=4,
|
|
196
|
+
first_row_as_headings=True,
|
|
197
|
+
) as table:
|
|
198
|
+
title_row = table.row()
|
|
199
|
+
for key in title_keys:
|
|
200
|
+
title_row.cell(pdf.translate(key), align="CENTER")
|
|
201
|
+
unit_row = table.row()
|
|
202
|
+
for unit_label in unit_labels:
|
|
203
|
+
unit_row.cell(unit_label, align="CENTER")
|
|
204
|
+
for values in rows:
|
|
205
|
+
table_row = table.row()
|
|
206
|
+
for value in values:
|
|
207
|
+
table_row.cell(value)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _pass_schedule_rows(sequence: PassSequence) -> list[list[str]]:
|
|
211
|
+
rows = []
|
|
212
|
+
for roll_pass in sequence.roll_passes:
|
|
213
|
+
out_profile = roll_pass.out_profile
|
|
214
|
+
rows.append([
|
|
215
|
+
str(roll_pass.label),
|
|
216
|
+
_groove_label(roll_pass),
|
|
217
|
+
_format(lambda: units.to_mm(roll_pass.roll.nominal_diameter), ".1f"),
|
|
218
|
+
_format(lambda: units.to_mm(roll_pass.gap), ".2f"),
|
|
219
|
+
f"{_format(lambda: units.to_mm(out_profile.height), '.2f')} × "
|
|
220
|
+
f"{_format(lambda: units.to_mm(out_profile.width), '.2f')}",
|
|
221
|
+
_format(lambda: units.to_mm2(out_profile.cross_section.area), ".1f"),
|
|
222
|
+
_format(lambda: units.to_percent(out_profile.filling_ratio), ".1f"),
|
|
223
|
+
_format(lambda: units.to_percent(_reduction(roll_pass)), ".1f"),
|
|
224
|
+
_format(lambda: units.to_celsius(out_profile.temperature), ".0f"),
|
|
225
|
+
])
|
|
226
|
+
return rows
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _speeds_and_loads_rows(sequence: PassSequence) -> list[list[str]]:
|
|
230
|
+
rows = []
|
|
231
|
+
for roll_pass in sequence.roll_passes:
|
|
232
|
+
roll = roll_pass.roll
|
|
233
|
+
engine = roll_pass.engine
|
|
234
|
+
rows.append([
|
|
235
|
+
str(roll_pass.label),
|
|
236
|
+
_groove_label(roll_pass),
|
|
237
|
+
_format(lambda: roll_pass.out_profile.velocity, ".2f"),
|
|
238
|
+
_format(lambda: units.to_mm(roll.working_diameter), ".1f"),
|
|
239
|
+
_format(lambda: units.to_per_minute(roll.rotational_frequency), ".1f"),
|
|
240
|
+
_format(lambda: engine.gear_ratio, ".3f"),
|
|
241
|
+
_format(lambda: units.to_per_minute(engine.rotational_frequency), ".0f"),
|
|
242
|
+
_format(lambda: roll_pass.elongation, ".3f"),
|
|
243
|
+
_format(lambda: units.to_mm(roll.nominal_diameter - roll.working_diameter), ".1f"),
|
|
244
|
+
_format(lambda: units.to_kilo(roll_pass.roll_force), ".1f"),
|
|
245
|
+
_format(lambda: units.to_kilo(2 * roll.roll_torque), ".2f"),
|
|
246
|
+
_format(lambda: units.to_kilo(engine.power), ".0f"),
|
|
247
|
+
_format(lambda: units.to_percent(_utilization(roll_pass)), ".1f"),
|
|
248
|
+
])
|
|
249
|
+
return rows
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _total_power(sequence: PassSequence) -> Optional[float]:
|
|
253
|
+
try:
|
|
254
|
+
return sum(roll_pass.engine.power for roll_pass in sequence.roll_passes)
|
|
255
|
+
except (AttributeError, TypeError):
|
|
256
|
+
return None
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def engineering_report(
|
|
260
|
+
sequence: PassSequence,
|
|
261
|
+
output_path: Path,
|
|
262
|
+
language: str = "en",
|
|
263
|
+
product_name: Optional[str] = None,
|
|
264
|
+
company_name: Optional[str] = None,
|
|
265
|
+
mill_name: Optional[str] = None,
|
|
266
|
+
logo: Optional[Path] = None,
|
|
267
|
+
max_heating_capacity: Optional[float] = None,
|
|
268
|
+
nominal_area: Optional[float] = None,
|
|
269
|
+
target_mass_deviation: Optional[float] = None,
|
|
270
|
+
) -> Path:
|
|
271
|
+
"""Writes the engineering report of a solved pass sequence as PDF and returns its path."""
|
|
272
|
+
if language not in LANGUAGES:
|
|
273
|
+
raise ValueError(f"Language '{language}' not available, choose one of {LANGUAGES}.")
|
|
274
|
+
|
|
275
|
+
pdf = EngineeringReportPdf(
|
|
276
|
+
language=language,
|
|
277
|
+
generated_at=datetime.now(),
|
|
278
|
+
product_name=product_name,
|
|
279
|
+
company_name=company_name,
|
|
280
|
+
mill_name=mill_name,
|
|
281
|
+
logo=Path(logo) if logo is not None else None,
|
|
282
|
+
)
|
|
283
|
+
info_lines = _info_lines(pdf, sequence, max_heating_capacity, nominal_area, target_mass_deviation)
|
|
284
|
+
|
|
285
|
+
pdf.add_page()
|
|
286
|
+
_write_info_block(pdf, info_lines)
|
|
287
|
+
pdf.set_font(FONT, style="B", size=10)
|
|
288
|
+
pdf.cell(CONTENT_WIDTH, 6, pdf.translate("pass_schedule"), new_x="LMARGIN", new_y="NEXT")
|
|
289
|
+
_write_table(
|
|
290
|
+
pdf,
|
|
291
|
+
title_keys=["stand", "groove", "roll_diameter", "gap", "height_x_width", "area", "filling_ratio",
|
|
292
|
+
"reduction", "temperature"],
|
|
293
|
+
unit_labels=["", "", "mm", "mm", "mm", "mm²", "%", "%", "°C"],
|
|
294
|
+
rows=_pass_schedule_rows(sequence),
|
|
295
|
+
column_widths=[16, 14, 20, 16, 34, 22, 22, 22, 24],
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
pdf.add_page()
|
|
299
|
+
_write_info_block(pdf, info_lines)
|
|
300
|
+
pdf.set_font(FONT, style="B", size=10)
|
|
301
|
+
pdf.cell(CONTENT_WIDTH, 6, pdf.translate("speeds_and_loads"), new_x="LMARGIN", new_y="NEXT")
|
|
302
|
+
_write_table(
|
|
303
|
+
pdf,
|
|
304
|
+
title_keys=["stand", "groove", "speed", "working_diameter", "roll_rotational_frequency", "gear_ratio",
|
|
305
|
+
"motor_rotational_frequency", "elongation", "groove_factor", "roll_force", "roll_torque",
|
|
306
|
+
"power", "utilization"],
|
|
307
|
+
unit_labels=["", "", "m/s", "mm", "1/min", "", "1/min", "", "mm", "kN", "kNm", "kW", "%"],
|
|
308
|
+
rows=_speeds_and_loads_rows(sequence),
|
|
309
|
+
column_widths=[14, 12, 14, 15, 16, 15, 15, 19, 14, 15, 14, 14, 12],
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
total_power = _total_power(sequence)
|
|
313
|
+
pdf.ln(3)
|
|
314
|
+
pdf.set_font(FONT, style="B", size=9)
|
|
315
|
+
total_power_text = MISSING_VALUE if total_power is None else f"{units.to_kilo(total_power):.0f}"
|
|
316
|
+
pdf.cell(CONTENT_WIDTH, 5, f"{pdf.translate('total_power')}: {total_power_text} kW", align="R")
|
|
317
|
+
|
|
318
|
+
output_path = Path(output_path)
|
|
319
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
320
|
+
pdf.output(str(output_path))
|
|
321
|
+
return output_path
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
LANGUAGES = ("de", "en", "it")
|
|
2
|
+
|
|
3
|
+
TEXTS = {
|
|
4
|
+
"roll_pass_design": {"de": "Walzenkalibrierung", "en": "Roll pass design", "it": "Calibratura dei cilindri"},
|
|
5
|
+
"rolling_mill": {"de": "Walzstraße", "en": "Rolling mill", "it": "Laminatoio"},
|
|
6
|
+
"product": {"de": "Produkt", "en": "Product", "it": "Prodotto"},
|
|
7
|
+
"page": {"de": "Seite", "en": "Page", "it": "Pagina"},
|
|
8
|
+
"generated": {"de": "Erstellt", "en": "Generated", "it": "Generato"},
|
|
9
|
+
"generated_with": {"de": "Erstellt mit PyRolL", "en": "Generated with PyRolL", "it": "Generato con PyRolL"},
|
|
10
|
+
"pass_schedule": {"de": "Stichplan", "en": "Pass schedule", "it": "Schema di laminazione"},
|
|
11
|
+
"speeds_and_loads": {
|
|
12
|
+
"de": "Geschwindigkeiten und Belastungen",
|
|
13
|
+
"en": "Speeds and loads",
|
|
14
|
+
"it": "Velocità e carichi",
|
|
15
|
+
},
|
|
16
|
+
"material": {"de": "Werkstoff", "en": "Material", "it": "Materiale"},
|
|
17
|
+
"rolling_speed": {"de": "Walzgeschwindigkeit", "en": "Rolling speed", "it": "Velocità di laminazione"},
|
|
18
|
+
"in_profile_size": {"de": "Knüppelabmessung", "en": "Billet size", "it": "Dimensione billetta"},
|
|
19
|
+
"in_profile_temperature": {"de": "Knüppeltemperatur", "en": "Billet temperature", "it": "Temperatura billetta"},
|
|
20
|
+
"max_heating_capacity": {"de": "Max. Ofenleistung", "en": "Max. heating capacity", "it": "Capacità max. forno"},
|
|
21
|
+
"production": {"de": "Produktionsleistung", "en": "Production", "it": "Produzione"},
|
|
22
|
+
"nominal_mass": {"de": "Nennmasse", "en": "Nominal mass", "it": "Massa nominale"},
|
|
23
|
+
"target_mass": {"de": "Zielmasse", "en": "Target mass", "it": "Massa obiettivo"},
|
|
24
|
+
"total_power": {"de": "Gesamte Walzleistung", "en": "Total rolling power", "it": "Potenza totale di laminazione"},
|
|
25
|
+
"stand": {"de": "Gerüst", "en": "Stand", "it": "Gabbia"},
|
|
26
|
+
"groove": {"de": "Kaliber", "en": "Groove", "it": "Calibro"},
|
|
27
|
+
"roll_diameter": {"de": "Walzen-Ø", "en": "Roll dia.", "it": "Ø cilindro"},
|
|
28
|
+
"gap": {"de": "Spalt", "en": "Gap", "it": "Luce"},
|
|
29
|
+
"height_x_width": {"de": "Höhe × Breite", "en": "Height × width", "it": "Altezza × larghezza"},
|
|
30
|
+
"area": {"de": "Fläche", "en": "Area", "it": "Area"},
|
|
31
|
+
"filling_ratio": {"de": "Füllgrad", "en": "Filling ratio", "it": "Riempimento"},
|
|
32
|
+
"reduction": {"de": "Abnahme", "en": "Reduction", "it": "Riduzione"},
|
|
33
|
+
"temperature": {"de": "Temperatur", "en": "Temperature", "it": "Temperatura"},
|
|
34
|
+
"speed": {"de": "Geschw.", "en": "Speed", "it": "Velocità"},
|
|
35
|
+
"working_diameter": {"de": "Arbeits-Ø", "en": "Work. dia.", "it": "Ø lavoro"},
|
|
36
|
+
"roll_rotational_frequency": {"de": "Walzendrehzahl", "en": "Roll speed", "it": "Giri cilindro"},
|
|
37
|
+
"gear_ratio": {"de": "Übersetzung", "en": "Gear ratio", "it": "Rapporto riduzione"},
|
|
38
|
+
"motor_rotational_frequency": {"de": "Motordrehzahl", "en": "Motor speed", "it": "Giri motore"},
|
|
39
|
+
"elongation": {"de": "Streckung", "en": "Elongation", "it": "Allungamento"},
|
|
40
|
+
"groove_factor": {"de": "Kaliberfaktor", "en": "Groove factor", "it": "Fattore calibro"},
|
|
41
|
+
"roll_force": {"de": "Walzkraft", "en": "Roll force", "it": "Forza"},
|
|
42
|
+
"roll_torque": {"de": "Walzmoment", "en": "Roll torque", "it": "Coppia"},
|
|
43
|
+
"power": {"de": "Leistung", "en": "Power", "it": "Potenza"},
|
|
44
|
+
"utilization": {"de": "Auslastung", "en": "Utilization", "it": "Utilizzo"},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def translate(key: str, language: str) -> str:
|
|
49
|
+
return TEXTS[key][language]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Conversion of SI values into the units printed in the report."""
|
|
2
|
+
|
|
3
|
+
KELVIN_OFFSET = 273.15
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def to_mm(value_m: float) -> float:
|
|
7
|
+
return value_m * 1e3
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def to_mm2(value_m2: float) -> float:
|
|
11
|
+
return value_m2 * 1e6
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def to_celsius(value_kelvin: float) -> float:
|
|
15
|
+
return value_kelvin - KELVIN_OFFSET
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def to_kilo(value: float) -> float:
|
|
19
|
+
return value * 1e-3
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def to_percent(ratio: float) -> float:
|
|
23
|
+
return ratio * 1e2
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def to_per_minute(frequency_per_second: float) -> float:
|
|
27
|
+
return frequency_per_second * 60
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def to_tonnes_per_hour(mass_flow_kg_per_s: float) -> float:
|
|
31
|
+
return mass_flow_kg_per_s * 3.6
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pyroll-engineering-report
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Plugin for PyRolL providing a printable PDF engineering report (pass schedule, speeds and loads) of a solved pass sequence.
|
|
5
|
+
Project-URL: Homepage, https://pyroll.readthedocs.io
|
|
6
|
+
Project-URL: Repository, https://codeberg.org/pyroll-project/pyroll-engineering-report
|
|
7
|
+
Project-URL: Documentation, https://codeberg.org/pyroll-project/pyroll-engineering-report/src/branch/main/docs/docs.pdf
|
|
8
|
+
Author-email: Christoph Renzing <christoph.renzing@imf.tu-freiberg.de>
|
|
9
|
+
License-Expression: BSD-3-Clause
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Requires-Dist: fpdf2>=2.8
|
|
13
|
+
Requires-Dist: pyroll-core<4.0,>=3.1
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pyroll/engineering_report/__init__.py,sha256=p4emyjz1ZZxHbOsSq9iK3K9Ur3ZEE4kd27dKhwgAFP0,152
|
|
2
|
+
pyroll/engineering_report/report.py,sha256=tSUk2DUvBFYEffK0bJlkExr1KC1kAFsil3LZWfJMbBQ,12496
|
|
3
|
+
pyroll/engineering_report/translations.py,sha256=zUoQ_AhMDoIy5y8kHWRD1Qhs_1v9Ke1jdzvQU9CsSgg,3380
|
|
4
|
+
pyroll/engineering_report/units.py,sha256=H7C4GIFulTdytBWR9SO9pK-nxdCgmWEsMzEyT8lgUQM,629
|
|
5
|
+
pyroll_engineering_report-3.0.0.dist-info/METADATA,sha256=v9DqhOYOBakfO6soJqUoqw_w18vmuZ-pph-nGwndFkY,675
|
|
6
|
+
pyroll_engineering_report-3.0.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
7
|
+
pyroll_engineering_report-3.0.0.dist-info/licenses/LICENSE,sha256=ba04C9xPoD_SK1gQPi-rnym3Kktrfbe0dWCbqnsw_Mk,1448
|
|
8
|
+
pyroll_engineering_report-3.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright 2022 ...
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
|
+
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
|
+
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|