Skip to content

Commit 8901191

Browse files
committed
Trim expiry comments, hub engine-list fixes and viewer color parsing
1 parent 9d4dda2 commit 8901191

7 files changed

Lines changed: 75 additions & 30 deletions

File tree

Source/Application/WebViewer.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,7 @@ class WebViewer : public IO::HTTPServer, public Setting
231231
void stopServing();
232232
// for good: unbinds, writes the statistics file and joins the server thread
233233
void shutdown();
234-
// periodic maintenance from the engine loop; handlers gate on the timestamp
235-
// and must return promptly, no blocking I/O
234+
// periodic maintenance; handlers gate on the timestamp and must not block
236235
void tick(std::time_t now);
237236
// drop the accumulated statistics, keeping the configuration (Android only)
238237
void resetStatistics();

Source/Tracking/Ships.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,8 @@ void Ship::reset()
6969
msg.clear();
7070
}
7171

72-
// Which fields each message type can refresh, indexed by type; F_SIGNAL is in every
73-
// row as reception data comes with every message. A missing field here expires
74-
// early, a spurious one never expires.
72+
// Which fields each message type can refresh, indexed by type. A missing field
73+
// here expires early, a spurious one never expires.
7574
static const uint32_t TYPE_FIELDS[MAX_MSG_TYPE + 1] = {
7675
0, // 0 does not exist
7776
F_SIGNAL | F_LATLON | F_SPEED_COG | F_HEADING | F_STATUS | F_MANEUVER | F_RECV_STATIONS, // 1 position report
@@ -293,7 +292,7 @@ int Ship::getMMSItype()
293292
{
294293
return MMSI_SARTEPIRB;
295294
}
296-
// the number outranks the message types: a bad decode must not reclassify a station
295+
// the MMSI number outranks the message types
297296
if (mmsi >= 990000000 && mmsi <= 999999999)
298297
{
299298
return MMSI_ATON;

frontend/control/js/config-manager.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,18 @@
378378
changed = true;
379379
}
380380
if (typeof r.model === 'string') {
381-
if (!('engines' in r)) r.engines = [r.model === 'auto' ? {} : { type: r.model }];
381+
const model = r.model.toLowerCase();
382+
if (!('engines' in r)) r.engines = [model === 'auto' ? {} : { type: model }];
382383
delete r.model;
383384
changed = true;
384385
}
385386
if (r.model && typeof r.model === 'object' && !Array.isArray(r.model)) {
386-
const settings = {};
387-
for (const k of Object.keys(r.model)) if (k !== 'active') settings[k] = r.model[k];
388-
const entry = Object.keys(settings).length ? { settings } : {};
389-
r.engines = Array.isArray(r.engines) ? r.engines : [entry];
387+
if (!('active' in r.model) || Utils.toBoolean(r.model.active)) {
388+
const settings = {};
389+
for (const k of Object.keys(r.model)) if (k !== 'active') settings[k] = r.model[k];
390+
const entry = Object.keys(settings).length ? { settings } : {};
391+
r.engines = Array.isArray(r.engines) ? r.engines : [entry];
392+
}
390393
delete r.model;
391394
changed = true;
392395
}
@@ -794,6 +797,7 @@
794797
function render() {
795798
rows.innerHTML = '';
796799
list.forEach((entry, i) => {
800+
const type = String(entry.type || 'auto').toLowerCase();
797801
const select = el('select', `${Styles.input} ${Styles.select}`, {
798802
onChange: e => {
799803
if (e.target.value === 'auto') delete list[i].type;
@@ -814,9 +818,9 @@
814818
}
815819
});
816820
(field.options || []).forEach(o => select.appendChild(
817-
el('option', '', { value: o.value, selected: (entry.type || 'auto') === o.value }, o.label)));
821+
el('option', '', { value: o.value, selected: type === o.value }, o.label)));
818822

819-
const shown = (field.settings || []).filter(st => !st.types || st.types.includes(entry.type || 'auto')).map(st => st.name);
823+
const shown = (field.settings || []).filter(st => !st.types || st.types.includes(type)).map(st => st.name);
820824
const tuned = Object.keys(entry.settings || {}).some(k => !shown.includes(k));
821825
const row = el('div', 'box col', {},
822826
el('div', 'row', {},
@@ -831,7 +835,7 @@
831835
}, Icons.delete())));
832836

833837
// the receiver's zones apply to every engine, shown greyed
834-
if (list.length > 1) {
838+
if (list.length > 1 || (Array.isArray(entry.zone) && entry.zone.length)) {
835839
const zones = Array.isArray(entry.zone) ? entry.zone : [];
836840
const inherited = Array.isArray(dataContext?.zone) ? dataContext.zone : [];
837841
const chips = el('div', 'row row-wrap row-tight');
@@ -859,9 +863,8 @@
859863
row.appendChild(chips);
860864
}
861865

862-
const cur = entry.type || 'auto';
863866
(field.settings || [])
864-
.filter(st => !st.types || st.types.includes(cur))
867+
.filter(st => !st.types || st.types.includes(type))
865868
.forEach(st => {
866869
const own = entry.settings || {};
867870
const toggle = Renderer.createInput({ type: 'toggle' }, own[st.name], on => {
@@ -883,6 +886,12 @@
883886
}
884887

885888
render();
889+
let zonesSnapshot = JSON.stringify(dataContext?.zone ?? null);
890+
wrapper.dataset.syncContext = '1';
891+
wrapper._syncContext = () => {
892+
const z = JSON.stringify(dataContext?.zone ?? null);
893+
if (z !== zonesSnapshot) { zonesSnapshot = z; render(); }
894+
};
886895
wrapper.appendChild(rows);
887896
wrapper.appendChild(addBtn);
888897
return wrapper;
@@ -1028,6 +1037,7 @@
10281037
div.querySelectorAll('input, select, button').forEach(el => el.disabled = !match);
10291038
});
10301039
}
1040+
container.querySelectorAll('[data-sync-context]').forEach(div => div._syncContext && div._syncContext());
10311041
}
10321042
};
10331043

frontend/src/core/util.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,14 @@ export function decodeHTMLEntities(text) {
1818
return textArea.value;
1919
}
2020

21-
export function hexToRgb(hex) {
22-
const m = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex || '');
21+
const colorCtx = document.createElement('canvas').getContext('2d');
22+
23+
export function hexToRgb(color) {
24+
color = String(color || '');
25+
if (/^[a-f\d]{6}$/i.test(color)) color = '#' + color;
26+
colorCtx.fillStyle = '#12a5ed';
27+
colorCtx.fillStyle = color;
28+
const m = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(colorCtx.fillStyle);
2329
return m ? [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)] : [18, 165, 237];
2430
}
2531

frontend/src/script.js

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,7 @@ function initMap() {
16051605
});
16061606

16071607
map.on('moveend', function (evt) {
1608-
debouncedSaveSettings();
1608+
debouncedSaveMapView();
16091609
debouncedDrawMap();
16101610
});
16111611

@@ -2572,11 +2572,18 @@ function toggleMenu() {
25722572
menuButton.classList.toggle("menu_icon");
25732573
menuButton.classList.toggle("close_icon");
25742574

2575+
syncMenuScrim();
2576+
}
2577+
2578+
function syncMenuScrim() {
2579+
const menubar = document.getElementById("menubar");
25752580
const floats = ["absolute", "fixed"].includes(getComputedStyle(menubar).position);
25762581
document.getElementById("menubar-overlay").classList
25772582
.toggle("active", menubar.classList.contains("visible") && floats);
25782583
}
25792584

2585+
window.matchMedia("(min-width: 750px)").addEventListener("change", syncMenuScrim);
2586+
25802587
function initFullScreen() {
25812588
document.addEventListener("fullscreenchange", handleFullScreenChange);
25822589
document.addEventListener("mozfullscreenchange", handleFullScreenChange);
@@ -3389,7 +3396,18 @@ const handlePointerMove = function (pixel, target) {
33893396
() => ol.proj.toLonLat(map.getCoordinateFromPixel(pixel)));
33903397
};
33913398

3392-
const debouncedSaveSettings = debounce(saveSettings, 250);
3399+
function saveMapView() {
3400+
if (map === undefined) return;
3401+
const center = ol.proj.toLonLat(map.getView().getCenter());
3402+
settings.lat = center[1];
3403+
settings.lon = center[0];
3404+
settings.zoom = map.getView().getZoom();
3405+
localStorage[context] = JSON.stringify(settings);
3406+
community.notifyCommunityPopupView();
3407+
updateMapURL();
3408+
}
3409+
3410+
const debouncedSaveMapView = debounce(saveMapView, 250);
33933411
const debouncedDrawMap = debounce(redrawMap, 250);
33943412
const debounceShowHoverTrack = debounce(showHoverTrack, 250);
33953413

@@ -3450,6 +3468,12 @@ function updateForLegacySettings() {
34503468
delete settings.latlon_in_dms;
34513469
}
34523470

3471+
if ('realtime_filter_mmsis' in settings) {
3472+
if (Array.isArray(settings.realtime_filter_mmsis) && settings.realtime_filter_mmsis.length && !(settings.realtime_filters || []).length)
3473+
settings.realtime_filters = settings.realtime_filter_mmsis.map((m) => ({ kind: 'mmsi', value: m }));
3474+
delete settings.realtime_filter_mmsis;
3475+
}
3476+
34533477
if (!("showPlanesAtFirst" in settings)) {
34543478
settings.showPlanesAtFirst = true;
34553479
settings.map_overlay.push("Aircraft");
@@ -5238,11 +5262,11 @@ function showAboutDialog() {
52385262
</p>
52395263
<p>
52405264
The web-interface gratefully uses the following libraries:
5241-
<a href="https://www.chartjs.org/docs/latest/charts/line.html" target="_blank" rel="noopener" rel="nofollow">chart.js</a>,
5242-
<a href="https://www.chartjs.org/chartjs-plugin-annotation/latest/" target="_blank" rel="noopener" rel="nofollow">chart.js annotation plugin</a>,
5243-
<a href="https://openlayers.org/" target="_blank" rel="noopener" rel="nofollow">openlayers</a>,
5244-
<a href="https://fonts.google.com/icons?selected=Material+Icons" target="_blank" rel="noopener" rel="nofollow">Material Design Icons</a>,
5245-
<a href="https://tabulator.info/" target="_blank" rel="noopener" rel="nofollow">tabulator</a>,
5265+
<a href="https://www.chartjs.org/docs/latest/charts/line.html" target="_blank" rel="noopener nofollow">chart.js</a>,
5266+
<a href="https://www.chartjs.org/chartjs-plugin-annotation/latest/" target="_blank" rel="noopener nofollow">chart.js annotation plugin</a>,
5267+
<a href="https://openlayers.org/" target="_blank" rel="noopener nofollow">openlayers</a>,
5268+
<a href="https://fonts.google.com/icons?selected=Material+Icons" target="_blank" rel="noopener nofollow">Material Design Icons</a>,
5269+
<a href="https://tabulator.info/" target="_blank" rel="noopener nofollow">tabulator</a>,
52465270
<a href="https://github.com/markedjs/marked" target="_blank" rel="noopener">marked</a>, and
52475271
<a href="https://github.com/lipis/flag-icons" target="_blank" rel="noopener">flag-icons</a>. Please consult the links for the respective licenses.
52485272
</p>`;

frontend/src/tabs/plots.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,9 @@ function setTimeAxis(c, source) {
482482
let absoluteTime = true;
483483

484484
export function setAbsoluteTime(on) {
485-
absoluteTime = !!on;
485+
on = !!on;
486+
if (on === absoluteTime) return;
487+
absoluteTime = on;
486488
Object.values(charts).forEach((c) => c && c.update());
487489
}
488490

@@ -548,19 +550,26 @@ function attachTimeAxis(c) {
548550
};
549551
}
550552

553+
function timeShift(a, b, f) {
554+
const A = a && a[f], B = b && b[f];
555+
return A && B && A.interval ? Math.round((B.now - A.now) / A.interval) : 0;
556+
}
557+
551558
function updateChartCompare(a, b, f, key, c, srcA, srcB) {
552559
if (!c) return;
560+
const shift = timeShift(a, b, f);
553561
c.data.datasets[0].label = receiverLabel(srcA);
554562
c.data.datasets[0].data = series(a, f, key);
555563
c.data.datasets[1].label = receiverLabel(srcB);
556-
c.data.datasets[1].data = series(b, f, key);
564+
c.data.datasets[1].data = series(b, f, key).map((p) => ({ x: p.x + shift, y: p.y }));
557565
setTimeAxis(c, a && a[f]);
558566
c.update();
559567
}
560568

561569
function updateChartGain(a, b, f, c, srcA, srcB) {
562570
if (!c) return;
563-
const other = new Map(series(b, f, "count").map((p) => [p.x, p.y]));
571+
const shift = timeShift(a, b, f);
572+
const other = new Map(series(b, f, "count").map((p) => [p.x + shift, p.y]));
564573
const shared = series(a, f, "count").filter((p) => p.y > 0 && other.has(p.x));
565574

566575
// the period average weighs each bucket by its message count, so quiet

frontend/src/tabs/realtime.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,6 @@ export function activate(filterMMSI = null) {
353353

354354
if (Array.isArray(settings.realtime_filters) && settings.realtime_filters.length) {
355355
viewer.setFilters(settings.realtime_filters);
356-
} else if (Array.isArray(settings.realtime_filter_mmsis) && settings.realtime_filter_mmsis.length) {
357-
viewer.setFilters(settings.realtime_filter_mmsis.map((m) => ({ kind: 'mmsi', value: m })));
358356
}
359357
if (savedState && savedState.isPaused) {
360358
viewer.isPaused = true;

0 commit comments

Comments
 (0)