diff --git a/app/src/main/java/com/grigowashere/loratester/ReferencePointRecorder.java b/app/src/main/java/com/grigowashere/loratester/ReferencePointRecorder.java index e0ee43f..953e896 100644 --- a/app/src/main/java/com/grigowashere/loratester/ReferencePointRecorder.java +++ b/app/src/main/java/com/grigowashere/loratester/ReferencePointRecorder.java @@ -4,7 +4,6 @@ import android.os.Handler; import android.os.Looper; import android.util.Log; -import com.grigowashere.loratester.api.MacroPreset; import com.grigowashere.loratester.api.ServerApi; import com.grigowashere.loratester.location.GeoUtils; import com.grigowashere.loratester.net.NetworkMonitor; @@ -49,7 +48,6 @@ public class ReferencePointRecorder { private volatile boolean recording; private volatile long sessionId = -1; private volatile int sampleCount; - private volatile MacroPreset activePreset; private ScheduledFuture sampleTask; private ScheduledFuture flushTask; private Listener listener; @@ -77,10 +75,6 @@ public class ReferencePointRecorder { this.listener = listener; } - public void setActivePreset(MacroPreset preset) { - this.activePreset = preset; - } - public boolean isRecording() { return recording; } @@ -193,12 +187,6 @@ public class ReferencePointRecorder { if (stats.role != null) point.put("role", stats.role); if (stats.metaJson != null) point.put("meta", stats.metaJson); } - MacroPreset preset = activePreset; - if (preset != null) { - if (preset.id != null) point.put("preset_id", preset.id); - if (preset.name != null) point.put("preset_name", preset.name); - if (preset.params != null) point.put("params", preset.params); - } synchronized (buffer) { buffer.add(point); } diff --git a/app/src/main/java/com/grigowashere/loratester/ui/AtFragment.java b/app/src/main/java/com/grigowashere/loratester/ui/AtFragment.java index fea6e71..ef7daca 100644 --- a/app/src/main/java/com/grigowashere/loratester/ui/AtFragment.java +++ b/app/src/main/java/com/grigowashere/loratester/ui/AtFragment.java @@ -25,7 +25,6 @@ import com.grigowashere.loratester.LoraApp; import com.grigowashere.loratester.MacroPresetRepository; import com.grigowashere.loratester.PeerDevices; import com.grigowashere.loratester.R; -import com.grigowashere.loratester.ReferencePointRecorder; import com.grigowashere.loratester.TelemetryUploader; import com.grigowashere.loratester.api.DeviceInfo; import com.grigowashere.loratester.api.MacroPreset; @@ -50,7 +49,6 @@ public class AtFragment extends Fragment { private FragmentPollHelper pollHelper; private TelemetryUploader uploader; private CommandPoller commandPoller; - private ReferencePointRecorder referencePointRecorder; private MacroPresetRepository macroPresetRepository; private TextView atStatus; private TextView atCurrentSnapshot; @@ -78,7 +76,6 @@ public class AtFragment extends Fragment { LoraApp app = (LoraApp) context.getApplicationContext(); uploader = app.getTelemetryUploader(); commandPoller = app.getCommandPoller(); - referencePointRecorder = app.getReferencePointRecorder(); macroPresetRepository = app.getMacroPresetRepository(); } @@ -236,9 +233,6 @@ public class AtFragment extends Fragment { List lines = preset != null && preset.lines != null && !preset.lines.isEmpty() ? preset.lines : RadioMacroBuilder.apply(buildParams()); - if (referencePointRecorder != null) { - referencePointRecorder.setActivePreset(preset != null ? preset : buildTransientPreset(lines)); - } sendLines(lines); } @@ -253,16 +247,6 @@ public class AtFragment extends Fragment { return presets.get(pos - 1); } - private MacroPreset buildTransientPreset(List lines) { - MacroPreset preset = new MacroPreset(); - preset.id = "manual"; - preset.name = getString(R.string.at_preset_manual); - preset.params = paramsMap(buildParams()); - preset.lines = lines; - preset.local = true; - return preset; - } - private void saveCurrentAsLocalPreset() { if (macroPresetRepository == null) { return; diff --git a/server/core/__pycache__/schema.cpython-313.pyc b/server/core/__pycache__/schema.cpython-313.pyc index 03c46af..c3181dc 100644 Binary files a/server/core/__pycache__/schema.cpython-313.pyc and b/server/core/__pycache__/schema.cpython-313.pyc differ diff --git a/server/core/__pycache__/storage.cpython-313.pyc b/server/core/__pycache__/storage.cpython-313.pyc index 620011b..a396fdc 100644 Binary files a/server/core/__pycache__/storage.cpython-313.pyc and b/server/core/__pycache__/storage.cpython-313.pyc differ diff --git a/server/core/schema.py b/server/core/schema.py index cf70ed6..b4b7f11 100644 --- a/server/core/schema.py +++ b/server/core/schema.py @@ -191,9 +191,6 @@ def apply_migrations(conn: sqlite3.Connection) -> list[str]: rssi REAL, role TEXT, meta TEXT, - preset_id TEXT, - preset_name TEXT, - params_json TEXT, FOREIGN KEY (session_id) REFERENCES reference_point_sessions(id) ); CREATE INDEX IF NOT EXISTS idx_reference_samples_session_ts diff --git a/server/core/storage.py b/server/core/storage.py index 98c26ca..8c333a9 100644 --- a/server/core/storage.py +++ b/server/core/storage.py @@ -554,28 +554,9 @@ def _metric_stats(values: list[float]) -> dict[str, Any]: def _reference_stats(samples: list[dict[str, Any]]) -> dict[str, Any]: per = [_metric_from_meta(s.get("meta"), "per_percent") for s in samples] quality = [_metric_from_meta(s.get("meta"), "rx_quality_percent") for s in samples] - by_preset: dict[str, dict[str, list[float]]] = {} - for sample in samples: - key = sample.get("preset_id") or sample.get("preset_name") or "none" - bucket = by_preset.setdefault( - str(key), {"per_percent": [], "rx_quality_percent": []} - ) - per_v = _metric_from_meta(sample.get("meta"), "per_percent") - q_v = _metric_from_meta(sample.get("meta"), "rx_quality_percent") - if per_v is not None: - bucket["per_percent"].append(per_v) - if q_v is not None: - bucket["rx_quality_percent"].append(q_v) return { "per_percent": _metric_stats([v for v in per if v is not None]), "rx_quality_percent": _metric_stats([v for v in quality if v is not None]), - "by_preset": { - key: { - "per_percent": _metric_stats(vals["per_percent"]), - "rx_quality_percent": _metric_stats(vals["rx_quality_percent"]), - } - for key, vals in by_preset.items() - }, } @@ -633,9 +614,8 @@ def add_reference_point_samples( conn.execute( """ INSERT INTO reference_point_samples - (session_id, ts, lat, lon, rssi, role, meta, - preset_id, preset_name, params_json) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + (session_id, ts, lat, lon, rssi, role, meta) + VALUES (?, ?, ?, ?, ?, ?, ?) """, ( session_id, @@ -645,9 +625,6 @@ def add_reference_point_samples( sample.get("rssi"), sample.get("role"), _json_or_none(sample.get("meta")), - sample.get("preset_id"), - sample.get("preset_name"), - _json_or_none(sample.get("params")), ), ) added += 1 @@ -722,19 +699,14 @@ def get_reference_point_session(session_id: int) -> dict[str, Any]: raise ValueError(f"reference point session {session_id} not found") rows = conn.execute( """ - SELECT id, session_id, ts, lat, lon, rssi, role, meta, - preset_id, preset_name, params_json + SELECT id, session_id, ts, lat, lon, rssi, role, meta FROM reference_point_samples WHERE session_id = ? ORDER BY ts ASC """, (session_id,), ).fetchall() - samples = [] - for row in rows: - sample = dict(row) - sample["params"] = _json_load(sample.pop("params_json")) - samples.append(sample) + samples = [dict(row) for row in rows] result = dict(session) result["sample_count"] = len(samples) result["samples"] = samples diff --git a/server/fastapi_app.py b/server/fastapi_app.py index 4d5ca57..ad9119a 100644 --- a/server/fastapi_app.py +++ b/server/fastapi_app.py @@ -96,9 +96,6 @@ class ReferencePointSample(BaseModel): rssi: Optional[float] = None role: Optional[str] = None meta: Optional[Any] = None - preset_id: Optional[str] = None - preset_name: Optional[str] = None - params: Optional[dict[str, Any]] = None class ReferencePointSamplesBody(BaseModel): diff --git a/server/static/app.js b/server/static/app.js index f80081e..4c7efb6 100644 --- a/server/static/app.js +++ b/server/static/app.js @@ -652,6 +652,23 @@ function applyMacroParams(params) { setCmdFormDirty(false); } +function paramsFromMacroLines(lines) { + const params = {}; + (lines || []).forEach(line => { + const s = String(line || '').trim(); + if (s.startsWith('AT+FQ=')) params.frequency_hz = Number(s.slice(6)); + else if (s.startsWith('AT+PW=')) params.power_dbm = Number(s.slice(6)); + else if (s.startsWith('AT+SF=')) params.sf = Number(s.slice(6)); + else if (s.startsWith('AT+BW=')) params.bw_khz = s.slice(6); + else if (s.startsWith('AT+CR=')) params.code_rate = s.slice(6); + else if (s.startsWith('AT+PL=')) params.preamble = Number(s.slice(6)); + else if (s.startsWith('AT+TM=')) params.send_timeout_ms = Number(s.slice(6)); + else if (s === 'AT+TX') params.role = 'TX'; + else if (s === 'AT+RX') params.role = 'RX'; + }); + return params; +} + function selectedMacroPreset() { const id = document.getElementById('macroPresetSelect')?.value; return macroPresets.find(p => p.id === id) || null; @@ -683,7 +700,24 @@ async function loadMacroPresets() { function fillMacroPresetEditor(preset) { document.getElementById('macroPresetName').value = preset?.name || ''; document.getElementById('macroPresetDescription').value = preset?.description || ''; - if (preset?.params) applyMacroParams(preset.params); +} + +function applySelectedMacroPresetToCommandForm() { + const preset = selectedMacroPreset(); + const status = document.getElementById('macroPresetStatus'); + if (!preset) { + if (status) status.textContent = 'Выберите пресет'; + return; + } + const params = Object.keys(preset.params || {}).length + ? preset.params + : paramsFromMacroLines(preset.lines); + applyMacroParams(params); + fillMacroPresetEditor(preset); + const panel = document.getElementById('controlPanel'); + panel?.scrollIntoView({ behavior: 'smooth', block: 'start' }); + document.getElementById('cmdTargetSelect')?.focus(); + if (status) status.textContent = `Пресет "${preset.name || preset.id}" перенесён в форму`; } async function saveMacroPreset() { @@ -848,20 +882,11 @@ async function showReferencePoint(sessionId) { if (!res.ok) throw new Error('reference point ' + res.status); const data = await res.json(); const stats = data.stats || {}; - const byPreset = stats.by_preset || {}; - let byPresetHtml = ''; - Object.entries(byPreset).forEach(([key, val]) => { - byPresetHtml += `
Пресет: ${escapeHtml(key)} - ${metricCard('PER', val.per_percent)} - ${metricCard('RX Quality', val.rx_quality_percent)} -
`; - }); if (summary) { summary.innerHTML = `
${escapeHtml(data.label || 'Реперная точка')} #${data.id}
${escapeHtml(deviceDisplayName(data))} · samples: ${data.sample_count || 0} · ${escapeHtml(data.status || '')}
${metricCard('PER', stats.per_percent)} - ${metricCard('RX Quality', stats.rx_quality_percent)} - ${byPresetHtml || '
Нет разреза по пресетам
'}`; + ${metricCard('RX Quality', stats.rx_quality_percent)}`; } drawReferencePointChart(data.samples || []); if (data.lat != null && data.lon != null) { @@ -3941,7 +3966,7 @@ document.getElementById('macroPresetSelect')?.addEventListener('change', () => { fillMacroPresetEditor(selectedMacroPreset()); }); document.getElementById('btnMacroPresetApply')?.addEventListener('click', () => { - fillMacroPresetEditor(selectedMacroPreset()); + applySelectedMacroPresetToCommandForm(); }); document.getElementById('btnMacroPresetSave')?.addEventListener('click', () => { saveMacroPreset().catch(e => alert(e.message || String(e))); diff --git a/server/static/index.html b/server/static/index.html index 113166e..e30cfc7 100644 --- a/server/static/index.html +++ b/server/static/index.html @@ -199,7 +199,7 @@ - +
diff --git a/server/tests/__pycache__/test_schema.cpython-313-pytest-9.0.3.pyc b/server/tests/__pycache__/test_schema.cpython-313-pytest-9.0.3.pyc index 1e69535..b6478b3 100644 Binary files a/server/tests/__pycache__/test_schema.cpython-313-pytest-9.0.3.pyc and b/server/tests/__pycache__/test_schema.cpython-313-pytest-9.0.3.pyc differ diff --git a/server/tests/test_schema.py b/server/tests/test_schema.py index 9c2fb1c..2178662 100644 --- a/server/tests/test_schema.py +++ b/server/tests/test_schema.py @@ -127,7 +127,6 @@ def test_reference_point_session_stats(temp_db): "lon": 37.62, "role": "RX", "meta": {"per_percent": 10, "rx_quality_percent": 80}, - "preset_id": "sf7", }, { "ts": 2.0, @@ -135,7 +134,6 @@ def test_reference_point_session_stats(temp_db): "lon": 37.62, "role": "RX", "meta": {"per_percent": 20, "rx_quality_percent": 90}, - "preset_id": "sf7", }, { "ts": 3.0, @@ -143,7 +141,6 @@ def test_reference_point_session_stats(temp_db): "lon": 37.62, "role": "RX", "meta": {"per_percent": 30, "rx_quality_percent": 100}, - "preset_id": "sf9", }, ], ) @@ -155,7 +152,6 @@ def test_reference_point_session_stats(temp_db): assert session["stats"]["per_percent"]["p50"] == 20 assert session["stats"]["per_percent"]["p75"] == 25 assert session["stats"]["rx_quality_percent"]["p90"] == 98 - assert session["stats"]["by_preset"]["sf7"]["per_percent"]["count"] == 2 def test_macro_presets_crud(temp_db):