Моделирование насос-форсунки EUI
Author
# =============================================================================
# js_logic.jl — JS: логика UI (слайдеры, пересчёт кулачка, экспорт)
# =============================================================================
function get_js_logic_block()::String
return """
var initial_slider_positions = new Array();
var currentMethod = 'cubic';
window.slidersChanged = false;
function computeLinearSpline(x_arr, y_arr) {
var fine_x = new Array(); var fine_y = new Array();
for (var cur_x = 0; cur_x <= 360; cur_x += 1.0) { fine_x.push(cur_x); }
fine_x.forEach(function(cx) {
var idx = x_arr.findIndex(v => v >= cx);
if (idx <= 0) idx = 1; if (idx >= x_arr.length) idx = x_arr.length - 1;
var x0 = x_arr[idx-1]; var x1 = x_arr[idx];
var y0 = y_arr[idx-1]; var y1 = y_arr[idx];
fine_y.push(y0 + (cx - x0) * (y1 - y0) / (x1 - x0));
});
return {fine_x: fine_x, fine_y: fine_y};
}
function computeAkimaSpline(x_arr, y_arr) {
var n = x_arr.length;
var x = new Array(n + 4); var y = new Array(n + 4);
for(var i=0; i<n; i++) { x[i+2] = x_arr[i]; y[i+2] = y_arr[i]; }
x[0] = x_arr[n-3] - 360; y[0] = y_arr[n-3];
x[1] = x_arr[n-2] - 360; y[1] = y_arr[n-2];
x[n+2] = x_arr[1] + 360; y[n+2] = y_arr[1];
x[n+3] = x_arr[2] + 360; y[n+3] = y_arr[2];
var m = new Array(n + 3);
for (var i = 0; i < n + 3; i++) { m[i] = (y[i+1] - y[i]) / (x[i+1] - x[i]); }
var t = new Array(n);
for (var i = 0; i < n; i++) {
var im = i + 2;
var w1 = Math.abs(m[im+1] - m[im]); var w2 = Math.abs(m[im-1] - m[im-2]);
t[i] = ((w1 + w2) === 0) ? 0.5 * (m[im] + m[im-1])
: (w1 * m[im-1] + w2 * m[im]) / (w1 + w2);
}
var fine_x = [], fine_y = [];
for (var cur_x = 0; cur_x <= 360; cur_x += 1.0) {
fine_x.push(cur_x);
var idx = x_arr.findIndex(v => v >= cur_x);
if (idx <= 0) idx = 1; if (idx >= n) idx = n - 1;
var i = idx - 1;
var h = x_arr[idx] - x_arr[i]; var dx = cur_x - x_arr[i];
var a2 = (3.0 * (y_arr[idx] - y_arr[i]) / h - 2.0 * t[i] - t[idx]) / h;
var a3 = (2.0 * (y_arr[i] - y_arr[idx]) / h + t[i] + t[idx]) / (h * h);
fine_y.push(y_arr[i] + t[i]*dx + a2*dx*dx + a3*dx*dx*dx);
}
return {fine_x: fine_x, fine_y: fine_y};
}
window.updateInterpolation_UID_PLACEHOLDER = function(method, isInit) {
if (!isInit) {
window.slidersChanged = true;
}
currentMethod = method;
var spline = (currentMethod === 'linear') ? computeLinearSpline(file_angles, file_disp) : computeAkimaSpline(file_angles, file_disp);
for (var i = 0; i < 12; i++) {
var targetAngle = i * 30;
var calculatedValue = 0.0;
if (currentMethod === 'linear' || targetAngle <= 70.0) {
var f_idx = spline.fine_x.indexOf(targetAngle);
if (f_idx !== -1) calculatedValue = spline.fine_y[f_idx];
} else {
var y70 = spline.fine_y[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[i] = calculatedValue;
ctrl_disp[i] = calculatedValue;
var sld = document.getElementById("slider_UID_PLACEHOLDER_" + i);
if (sld) sld.value = calculatedValue.toFixed(1);
var vspan = document.getElementById("val_UID_PLACEHOLDER_" + i);
if (vspan) vspan.innerText = calculatedValue.toFixed(1) + " мм";
}
window.updateFromSlider_UID_PLACEHOLDER(null);
}
window.recalculateCamWeb_UID_PLACEHOLDER = function() {
var rbaseSld = document.getElementById("rbase_slider_UID_PLACEHOLDER");
var r_base_mm = rbaseSld ? parseFloat(rbaseSld.value) : 40.0;
var r_base_m = r_base_mm * 1e-3;
var rbaseVal = document.getElementById("rbase_val_UID_PLACEHOLDER");
if (rbaseVal) rbaseVal.innerText = r_base_mm.toFixed(0) + " мм";
var nodes_x = []; var delta_nodes_y = [];
for (var i = 0; i < 12; i++) {
nodes_x.push(i * 30.0);
var base_val = initial_slider_positions[i] || 0.0;
delta_nodes_y.push(ctrl_disp[i] - base_val);
}
nodes_x.push(360.0);
delta_nodes_y.push(delta_nodes_y[0]);
var delta_spline = computeAkimaSpline(nodes_x, delta_nodes_y);
var raw_spline = (currentMethod === 'linear') ? computeLinearSpline(file_angles, file_disp) : computeAkimaSpline(file_angles, file_disp);
var corrected_y = [];
raw_spline.fine_x.forEach(function(ang, i) {
if (currentMethod === 'linear' || ang <= 70.0) {
corrected_y.push(raw_spline.fine_y[i]);
} else {
var y70 = raw_spline.fine_y[raw_spline.fine_x.indexOf(70.0)];
var progress = (ang - 70.0) / (360.0 - 70.0);
var val_convex = y70 * (1.0 - progress) + 1.2 * y70 * progress * (1.0 - progress);
corrected_y.push(val_convex < 0 ? 0 : val_convex);
}
});
var hybrid_y = [];
raw_spline.fine_x.forEach(function(ang, i) {
var final_h = corrected_y[i] + delta_spline.fine_y[i];
hybrid_y.push(final_h < 0 ? 0 : final_h);
});
var center_x = [], center_y = [], cam_x = [], cam_y = [];
raw_spline.fine_x.forEach(function(a, i) {
var h_m = hybrid_y[i] * 1e-3;
var a_rad = (a * (Math.PI / 180.0)) * -1 + (Math.PI / 2.0);
var r_ctr = r_base_m + R_ROLLER + h_m;
center_x.push(r_ctr * Math.cos(a_rad) - OFFSET * Math.sin(a_rad));
center_y.push(r_ctr * Math.sin(a_rad) + OFFSET * Math.cos(a_rad));
});
var n_pts = center_x.length, sum_x = 0, sum_y = 0;
for (var i = 0; i < n_pts; i++) {
var prev = (i - 1 + n_pts) % n_pts;
var next = (i + 1) % n_pts;
var dx = center_x[next] - center_x[prev];
var dy = center_y[next] - center_y[prev];
var len = Math.sqrt(dx*dx + dy*dy) || 0.001;
var cx_val = center_x[i] + (dy / len) * R_ROLLER;
var cy_val = center_y[i] - (dx / len) * R_ROLLER;
cam_x.push(cx_val); cam_y.push(cy_val); sum_x += cx_val; sum_y += cy_val;
}
return {cam_x: cam_x, cam_y: cam_y, fine_angles: raw_spline.fine_x, fine_disp: hybrid_y, r_base_m: r_base_m, geo_x: sum_x/n_pts, geo_y: sum_y/n_pts};
}
window.updateFromSlider_UID_PLACEHOLDER = function(changedIdx) {
if (changedIdx !== null) window.slidersChanged = true;
ctrl_disp.forEach(function(_, i) {
var sld = document.getElementById("slider_UID_PLACEHOLDER_" + i);
if (sld) ctrl_disp[i] = parseFloat(sld.value);
var vspan = document.getElementById("val_UID_PLACEHOLDER_" + i);
if (vspan) vspan.innerText = ctrl_disp[i].toFixed(1) + " мм";
});
var geo = window.recalculateCamWeb_UID_PLACEHOLDER();
var max_lim = geo.r_base_m + Math.max.apply(null, geo.fine_disp)*1e-3 + R_ROLLER + 0.015;
Plotly.relayout('cam_plot_UID_PLACEHOLDER', { xaxis: {range: [-max_lim, max_lim], autorange: false}, yaxis: {range: [-max_lim, max_lim], autorange: false, scaleanchor: "x"} });
Plotly.restyle('cam_plot_UID_PLACEHOLDER', {x: [geo.cam_x], y: [geo.cam_y]}, 0);
Plotly.restyle('cam_plot_UID_PLACEHOLDER', {x: [[0, geo.geo_x]], y: [[0, geo.geo_y]]}, 1);
Plotly.restyle('cam_plot_UID_PLACEHOLDER', {x: [[geo.geo_x]], y: [[geo.geo_y]]}, 2);
Plotly.restyle('lift_plot_UID_PLACEHOLDER', {y: [geo.fine_disp]}, 0);
Plotly.restyle('lift_plot_UID_PLACEHOLDER', {y: [ctrl_disp]}, 1);
}
window.downloadProfile_UID_PLACEHOLDER = function(btn) {
var geo = window.recalculateCamWeb_UID_PLACEHOLDER();
// ДОБАВЛЕНА ЗАПИСЬ UID, ЧТОБЫ ОТЛИЧАТЬ ФАЙЛЫ ТЕКУЩЕЙ И ПРОШЛЫХ СЕССИЙ
var content = "# UID: UID_PLACEHOLDER\\n";
content += "# SLIDERS_CHANGED: " + (window.slidersChanged ? "true" : "false") + "\\n";
content += "# INTERP_METHOD: " + currentMethod + "\\n";
content += "# Table format: 1D\\n";
for (var i = 0; i < geo.fine_disp.length; i++) {
content += geo.fine_angles[i] + "\\t" + geo.fine_disp[i].toExponential(6) + "\\n";
}
var blob = new Blob([content], {type: 'text/plain'});
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url; a.download = 'ui_profile_export.data'; a.click();
URL.revokeObjectURL(url);
btn.innerHTML = "✅ Файл сохранен!";
btn.style.background = "#28a745";
setTimeout(function() { btn.innerHTML = "💾 Сохранить профиль на диск"; btn.style.background = "#0078d4"; }, 4000);
}
"""
end