2019-07-09 23:51:24 -04:00
|
|
|
<template>
|
2022-03-04 13:17:53 -05:00
|
|
|
<tr :class="{ timedOut, lowVoltage, neverHeard: !stationStatus.lastHeard }">
|
2019-07-12 14:10:53 -04:00
|
|
|
<td :title="callsign">{{ tacticalAndOrCall }}</td>
|
2022-03-04 13:17:53 -05:00
|
|
|
<template v-if="stationStatus.lastHeard">
|
|
|
|
<td>{{ formatTime(stationStatus.lastHeard) }}</td>
|
|
|
|
<td>{{ formatTime(now - stationStatus.lastHeard, true) }}</td>
|
|
|
|
<td>{{ formatTime(Math.round(stationStatus.avgDelta), true) }}</td>
|
|
|
|
<td>{{ stationStatus.lastMicE }}</td>
|
|
|
|
<td>{{ stationStatus.lastVoltage || '' }}</td>
|
|
|
|
<td>{{ stationStatus.lastTemperature || '' }}</td>
|
|
|
|
<td>{{ stationStatus.lastComment }}</td>
|
2019-07-09 23:51:24 -04:00
|
|
|
</template>
|
|
|
|
<template v-else>
|
|
|
|
<td>Never Heard</td>
|
|
|
|
<td>Never Heard</td>
|
|
|
|
<td>Never Heard</td>
|
|
|
|
<td>Never Heard</td>
|
2019-07-12 14:36:00 -04:00
|
|
|
<td>Never Heard</td>
|
2019-07-13 08:48:50 -04:00
|
|
|
<td>Never Heard</td>
|
|
|
|
<td>Never Heard</td>
|
2019-07-09 23:51:24 -04:00
|
|
|
</template>
|
|
|
|
</tr>
|
|
|
|
</template>
|
|
|
|
|
2022-03-04 13:17:53 -05:00
|
|
|
<script setup>
|
|
|
|
import { ref, computed, watch } from 'vue';
|
2020-05-04 02:04:44 -04:00
|
|
|
import config from './status_config.yaml';
|
2019-07-09 23:51:24 -04:00
|
|
|
|
2022-03-04 13:17:53 -05:00
|
|
|
const props = defineProps({
|
|
|
|
callsign: String,
|
|
|
|
tactical: String,
|
|
|
|
messages: Array,
|
|
|
|
now: Date,
|
|
|
|
});
|
|
|
|
|
|
|
|
function notify(title, body) {
|
|
|
|
return new Notification(title, { body: body, requireInteraction: true });
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatTime(time, isDuration = false) {
|
|
|
|
return new Date(time).toLocaleTimeString(
|
|
|
|
'en-GB',
|
|
|
|
isDuration ? { timeZone: 'UTC' } : {}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function prettyDuration(duration) {
|
|
|
|
let date = new Date(duration);
|
|
|
|
return [
|
|
|
|
...Object.entries({
|
|
|
|
hours: date.getUTCHours(),
|
|
|
|
minutes: date.getUTCMinutes(),
|
|
|
|
seconds: date.getUTCSeconds(),
|
|
|
|
milliseconds: date.getUTCMilliseconds(),
|
|
|
|
}),
|
|
|
|
]
|
|
|
|
.filter(([suffix, num]) => num > 0)
|
|
|
|
.map(([suffix, num]) => num + ' ' + suffix)
|
|
|
|
.join(' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
const tacticalAndOrCall = computed(() => {
|
|
|
|
return props.tactical
|
|
|
|
? `${props.tactical} [${props.callsign}]`
|
|
|
|
: props.callsign;
|
|
|
|
});
|
|
|
|
|
|
|
|
const timedOut = computed(() => {
|
2022-07-09 10:21:26 -04:00
|
|
|
if (stationStatus.value.lastHeard === null) {
|
2022-03-04 13:17:53 -05:00
|
|
|
return false;
|
2022-07-09 10:21:26 -04:00
|
|
|
} else {
|
|
|
|
return (
|
|
|
|
props.now.getTime() - stationStatus.value.lastHeard > config.timeoutLength
|
|
|
|
);
|
2022-03-04 13:17:53 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const lowVoltage = computed(() => {
|
|
|
|
return (
|
|
|
|
stationStatus.value.lastVoltage &&
|
|
|
|
stationStatus.value.lastVoltage < config.lowVoltage
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2022-07-09 10:21:26 -04:00
|
|
|
const stationStatus = computed(() => {
|
|
|
|
const status = {
|
|
|
|
lastHeard: null,
|
|
|
|
delta: null,
|
|
|
|
lastVoltage: null,
|
|
|
|
lastTemperature: null,
|
|
|
|
};
|
|
|
|
|
|
|
|
Object.assign(
|
|
|
|
status,
|
|
|
|
props.messages.reduce((acc, message, idx, arr) => {
|
|
|
|
acc.lastHeard = message.date.getTime();
|
|
|
|
if (idx === 0) {
|
|
|
|
acc.avgDelta = 0;
|
|
|
|
} else {
|
|
|
|
let delta = message.date.getTime() - arr[idx - 1].date.getTime();
|
|
|
|
acc.avgDelta = (acc.avgDelta * (idx - 1) + delta) / idx;
|
|
|
|
}
|
|
|
|
if ('data' in message) {
|
|
|
|
if ('analog' in message.data) {
|
|
|
|
acc.lastVoltage = message.data.analog[0] / 10;
|
|
|
|
acc.lastTemperature = message.data.analog[1];
|
2022-03-04 13:17:53 -05:00
|
|
|
}
|
2022-07-09 10:21:26 -04:00
|
|
|
acc.lastMicE = message.data.mice || acc.lastMicE;
|
|
|
|
acc.lastComment = message.data.comment || acc.lastComment;
|
|
|
|
}
|
|
|
|
return acc;
|
|
|
|
}, {})
|
|
|
|
);
|
|
|
|
return status;
|
|
|
|
});
|
2022-03-04 13:17:53 -05:00
|
|
|
|
|
|
|
watch(
|
|
|
|
() => props.lowVoltage,
|
|
|
|
(newVal) => {
|
|
|
|
if (newVal) {
|
|
|
|
notify(
|
|
|
|
`${tacticalAndOrCall}'s battery has dropepd below ${config.lowVoltage}V`,
|
|
|
|
`Voltage: ${stationStatus.value.lastVoltage}`
|
2019-07-09 23:51:24 -04:00
|
|
|
);
|
2022-03-04 13:17:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
watch(timedOut, (newVal) => {
|
|
|
|
if (newVal) {
|
|
|
|
notify(
|
|
|
|
`${tacticalAndOrCall.value} has not been heard for over ${prettyDuration(
|
|
|
|
config.timeoutLength
|
|
|
|
)}!`,
|
|
|
|
`Last Heard: ${formatTime(
|
|
|
|
stationStatus.value.lastHeard
|
|
|
|
)} (${prettyDuration(
|
|
|
|
props.now.value - stationStatus.value.lastHeard
|
|
|
|
)} ago!)`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
2019-07-09 23:51:24 -04:00
|
|
|
</script>
|
|
|
|
|
|
|
|
<style>
|
|
|
|
tr.timedOut {
|
|
|
|
background-color: red;
|
|
|
|
}
|
|
|
|
|
|
|
|
tr.lowVoltage {
|
|
|
|
background-color: yellow;
|
|
|
|
}
|
|
|
|
|
|
|
|
tr.neverHeard {
|
|
|
|
background-color: purple;
|
|
|
|
color: #eee;
|
|
|
|
}
|
|
|
|
</style>
|