Моделирование насос-форсунки EUI
Автор
# =============================================================================
# ui_templates.jl — Большие HTML и JS шаблоны
# =============================================================================
const RAW_HTML_TEMPLATE = """
<div id="wrapper_UID_PLACEHOLDER" style="display: flex; flex-direction: column; align-items: center; font-family: sans-serif; gap: 15px; background-color: #fcfcfc; padding: 15px; border-radius: 6px;">
<div style="display: flex; flex-direction: row; align-items: center; gap: 15px; background: #fff; padding: 10px 20px; border: 1px solid #ddd; border-radius: 6px; width: 100%; max-width: 960px; justify-content: center;">
<span style="font-weight: bold; color: #333;">📐 Базовый радиус кулачка (R_base):</span>
<!-- ИЗМЕНЕНО: min="5" -->
<input type="range" id="rbase_slider_UID_PLACEHOLDER" min="5" max="80" step="1" value="RBASE_INIT_PLACEHOLDER" oninput="window.updateFromSlider_UID_PLACEHOLDER(null)" style="width: 250px; cursor: pointer;">
<span id="rbase_val_UID_PLACEHOLDER" style="font-size: 14px; font-weight: bold; color: #0078d4; min-width: 60px;">RBASE_INIT_PLACEHOLDER мм</span>
</div>
<div style="display: flex; flex-direction: row; align-items: center; gap: 15px; border-top: 1px solid #eee; padding-top: 10px; width: 100%; max-width: 960px; justify-content: center; flex-wrap: wrap;">
<span style="font-weight: bold; color: #333; font-size: 13px;">📈 Интерполяция:</span>
<label style="cursor: pointer; font-size: 13px; color: #555;">
<input type="radio" name="interp_method_UID_PLACEHOLDER" value="linear" onchange="window.updateInterpolation_UID_PLACEHOLDER('linear')"> Линейная
</label>
<label style="cursor: pointer; font-size: 13px; color: #555; font-weight: bold;">
<input type="radio" name="interp_method_UID_PLACEHOLDER" value="cubic" checked onchange="window.updateInterpolation_UID_PLACEHOLDER('cubic')"> Кубическая интерполяция
</label>
</div>
<div style="display: flex; flex-direction: row; justify-content: center; gap: 20px; width: 100%;">
<div id="cam_plot_UID_PLACEHOLDER" style="width: 440px; height: 440px; border: 1px solid #ddd; border-radius: 4px; background: white;"></div>
<div id="lift_plot_UID_PLACEHOLDER" style="width: 500px; height: 440px; border: 1px solid #ddd; border-radius: 4px; background: white;"></div>
</div>
<div style="display: flex; flex-direction: row; gap: 15px; width: 100%; max-width: 960px; justify-content: center; align-items: center;">
<button onclick="window.downloadProfile_UID_PLACEHOLDER(this)" style="padding: 10px 24px; background: #0078d4; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 14px; transition: 0.3s;">
💾 Сохранить профиль на диск
</button>
</div>
<div style="display: flex; flex-flow: row wrap; justify-content: center; width: 100%; max-width: 960px; gap: 5px; margin-top: 15px;">
SLIDERS_GRID_PLACEHOLDER
</div>
</div>
"""
const RAW_JS_TEMPLATE = """
<script>
(function() {
function loadPlotly(callback) {
if (window.Plotly) { callback(); return; }
var s = document.createElement("script"); s.src = "https://cdn.plot.ly/plotly-2.27.0.min.js"; s.onload = callback; document.head.appendChild(s);
}
loadPlotly(function() { initPlots_UID_PLACEHOLDER(); });
})();
function initPlots_UID_PLACEHOLDER() {
var R_ROLLER = R_ROLLER_VAL; var OFFSET = OFFSET_VAL;
var file_angles = RAW_FILE_ANGLES_ARR; var file_disp = RAW_FILE_DISP_ARR;
var ctrl_angles = ANGLES_ARR_VAL; var ctrl_disp = DISP_ARR_VAL;
JS_MATH_BLOCK_SUBSTITUTION
JS_LOGIC_BLOCK_SUBSTITUTION
var initial_spline = computeAkimaSpline(file_angles, file_disp);
for (var i = 0; i < 12; i++) {
var targetAngle = i * 30;
var calculatedValue = 0.0;
if (targetAngle <= 70.0) {
var f_idx = initial_spline.fine_x.indexOf(targetAngle);
if (f_idx !== -1) calculatedValue = initial_spline.fine_y[f_idx];
} else {
var y70 = initial_spline.fine_y[initial_spline.fine_x.indexOf(70.0)];
var progress = (targetAngle - 70.0) / (360.0 - 70.0);
calculatedValue = y70 * (1.0 - progress) + 1.2 * y70 * progress * (1.0 - progress);
}
initial_slider_positions.push(calculatedValue);
}
var geo = window.recalculateCamWeb_UID_PLACEHOLDER();
var trace_cam = { x: geo.cam_x, y: geo.cam_y, mode: 'lines', fill: 'toself', fillcolor: 'rgba(0, 120, 255, 0.12)', line: {color: 'rgb(0, 90, 200)'} };
var trace_ecc = { x: [0, geo.geo_x], y: [0, geo.geo_y], mode: 'lines', line: {color: 'rgb(230, 40, 40)', width: 2, dash: 'dot'} };
var trace_ctr = { x: [geo.geo_x], y: [geo.geo_y], mode: 'markers', marker: {size: 8, color: 'rgb(0, 180, 50)'} };
var lim = geo.r_base_m + Math.max.apply(null, geo.fine_disp)*1e-3 + R_ROLLER + 0.015;
Plotly.newPlot('cam_plot_UID_PLACEHOLDER', [trace_cam, trace_ecc, trace_ctr], { xaxis: {range: [-lim, lim]}, yaxis: {range: [-lim, lim], scaleanchor: "x"}, margin: {t:40,b:40,l:40,r:40}, showlegend: false }, {displayModeBar: false});
var trace_line = { x: geo.fine_angles, y: geo.fine_disp, mode: 'lines', line: {color: 'rgb(0, 150, 50)', width: 2.5} };
var trace_nodes = { x: ctrl_angles, y: ctrl_disp, mode: 'markers', marker: {size: 10, color: 'rgb(230, 40, 40)'} };
Plotly.newPlot('lift_plot_UID_PLACEHOLDER', [trace_line, trace_nodes], { xaxis: {range: [-10, 370]}, yaxis: {range: [-1, 17]}, margin: {t:40,b:40,l:50,r:20} }, {displayModeBar: false});
window.updateInterpolation_UID_PLACEHOLDER('cubic', true);
}
</script>
"""