<template>
<div v-if="!!item">
<div class="pt-2">
<span class="font-weight-medium">Platform: </span>
{{ item.os ? item.os.distro : '' }}
</div>
<div class="pt-2">
<span class="font-weight-medium">Battery: </span>
{{ item.battery ? item.battery.percent : '' }}%
</div>
<v-row>
<v-col cols="12" sm="6">
<div>
<span class="font-weight-medium">Memory Info: </span>
<v-divider />
<canvas id="myMemoryChart" width="300" height="130"></canvas>
</div>
</v-col>
<v-col cols="12" sm="6">
<div>
<span class="font-weight-medium">CPU Info: </span>
<v-divider />
<canvas id="myChart" width="300" height="130"></canvas>
</div>
</v-col>
</v-row>
<div>
<span class="font-weight-medium"> CPU Load Last Minute: </span>{{ item.cpuLoadLastMinute }}%
</div>
<div>
<span class="font-weight-medium"> CPU Load Last Ten Minutes: </span
>{{ item.cpuLoadLastTenMinutes }}%
</div>
<div>
<span class="font-weight-medium"> CPU Load Last Half Hour: </span
>{{ item.cpuLoadLastHalfHour }}%
</div>
<div>
<span class="font-weight-medium"> CPU Load Last Hour: </span>{{ item.cpuLoadLastHour }}%
</div>
<div>
<span class="font-weight-medium"> CPU Load Last Half Day: </span
>{{ item.cpuLoadLastHalfDay }}%
</div>
<div>
<span class="font-weight-medium"> CPU Load Last Day: </span>{{ item.cpuLoadLastDay }}%
</div>
<div>
<span class="font-weight-medium"> Inputs: </span
>{{ item.inputs ? item.inputs.join(', ') : 'none' }}
</div>
<div>
<span class="font-weight-medium"> Outputs: </span
>{{ item.outputs ? item.outputs.join(', ') : 'none' }}
</div>
<div>
<span class="font-weight-medium"> Domains: </span
>{{ item.domains ? item.domains.join(', ') : 'none' }}
</div>
<div>
<span class="font-weight-medium"> Currently connected environments: </span>
{{
item.currentlyConnectedEnvironments ? item.currentlyConnectedEnvironments.join(', ') : ''
}}
</div>
<div v-if="item.display">
<span class="font-weight-medium"> Displays: </span>
<ul>
<li min-height="10px" v-for="(display, i) in item.display" :key="i">
{{ display.currentResX }}x{{ display.currentResY }}
</li>
</ul>
</div>
<div>
<span class="font-weight-medium"> Classes: </span
>{{ item.classes ? item.classes.join(', ') : '' }}
</div>
<div>
<span class="font-weight-medium"> Online: </span>
<v-icon class="mt-n1" v-if="item.online" color="success" small> mdi-wifi </v-icon>
<v-icon class="mt-n1" v-else color="error" small>mdi-wifi-off</v-icon>
</div>
<div>
<span class="font-weight-medium"> Online checking addresses: </span>
{{ item.onlineCheckingAddresses ? item.onlineCheckingAddresses.join(', ') : '' }}
</div>
<div>
<span class="font-weight-medium"> Accept User Tasks: </span>
{{ item.acceptUserTasks ? 'Yes' : 'No' }}
</div>
<div>
<span class="font-weight-medium"> Deactivate Process Execution: </span>
{{ item.deactivateProcessExecution ? 'Yes' : 'No' }}
</div>
<div v-if="item.network">
<span class="font-weight-medium"> Networks: </span>
<v-card class="ml-1" v-for="network in item.network" :key="network.mac">
<v-row class="ml-2">Type: {{ network.type }}</v-row>
<v-row class="ml-2">IP4: {{ network.ip4 }}</v-row>
<v-row class="ml-2">Netmask V4: {{ network.netmaskv4 }}</v-row>
<v-row class="ml-2">IP6: {{ network.ip6 }}</v-row>
<v-row class="ml-2">Netmask V6: {{ network.netmaskv6 }}</v-row>
<v-row class="ml-2">Mac: {{ network.mac }}</v-row>
</v-card>
</div>
<div v-if="item.deployedProcesses">
<span class="font-weight-medium"> Process Instances: </span>
<span v-if="runningInstances.length">
(Running: {{ runningInstances.length }})
<v-btn color="error" @click="stopAllInstances()"> Stop All </v-btn>
</span>
<v-card class="ml-1" v-for="process in item.deployedProcesses" :key="process.definitionId">
<div class="font-weight-medium">{{ process.name }} - {{ process.definitionId }}</div>
<v-card class="ml-1">
<v-row class="ml-2" v-for="instance in process.instances" :key="instance.id">
{{ instance.processInstanceId }}({{ instance.instanceState[0] }})
</v-row>
</v-card>
</v-card>
</div>
<div class="mt-3">
<v-btn @click="$emit('showConfig')"> Show the configuration values of this machine </v-btn>
</div>
<LoggingInfo :show="loggingDialog" :logging="item.logs" @close="loggingDialog = false" />
<div class="mt-3">
<v-btn @click="loggingDialog = true"> Show the log entries of this machine </v-btn>
</div>
</div>
</template>
<script>
import Chart from 'chart.js';
import LoggingInfo from '@/frontend/components/machines/LoggingInfo.vue';
import { mapState } from 'vuex';
import { engineNetworkInterface, eventHandler } from '@/frontend/backend-api/index.js';
export default {
props: {
displayDetailed: String,
},
components: { LoggingInfo },
data() {
return {
lastMemValue: 0,
lastCpuValue: 0,
chart: null,
timeout: 5000,
show: null,
item: null,
loggingDialog: false,
memInitialized: false,
};
},
computed: {
machine() {
return this.$store.getters['machineStore/machineById'](this.displayDetailed);
},
...mapState({
/**
* Returns array with all instances that didn't finish yet for the displayed machine
*/
runningInstances() {
return Object.entries(this.item.deployedProcesses).reduce(
(acc, [definitionId, deployment]) => {
const runningInstances = deployment.instances.reduce((running, instance) => {
if (instance.instanceState.includes('RUNNING')) {
return [...running, instance];
}
return running;
}, []);
const instanceInfo = runningInstances.map((instance) => ({
definitionId,
...instance,
}));
return acc.concat(instanceInfo);
},
[]
);
},
}),
},
methods: {
changeTimeout(newTimeout) {
this.timeout = newTimeout;
},
/**
* Stops all running instances on the displayed machine
*/
stopAllInstances() {
this.runningInstances.forEach(async (instance) => {
try {
await engineNetworkInterface.stopInstance(instance.definitionId, instance.id);
} catch (err) {
this.$logger.error(`Failed to stop instance on ${this.item.name}: ${err}.`);
}
});
},
initMemoryChart(arr, mem) {
const memoryCtx = document.getElementById('myMemoryChart').getContext('2d');
this.memoryChart = new Chart(memoryCtx, {
type: 'line',
data: {
labels: arr,
datasets: [
{
label: 'Memory Load',
borderColor: 'rgb(10, 150, 102)',
data: new Array(60).fill(0),
spanGaps: true,
},
],
},
options: {
animation: {
duration: 0,
},
hover: {
animationDuration: 0,
},
responsiveAnimationDuration: 0,
elements: {
point: {
radius: 0,
},
line: {
tension: 0,
},
},
scales: {
yAxes: [
{
ticks: {
suggestedMin: 0,
suggestedMax: mem.total,
callback: function (value, index, values) {
return (value / Math.pow(1024, 3)).toFixed(1) + ' GB';
},
},
},
],
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'values from the last 60s',
},
ticks: {
display: false,
autoSkip: true,
maxTicksLimit: 20,
},
},
],
},
},
});
},
},
async mounted() {
// render the 2 charts for memory and cpu load, initially filled with zeros
this.show = true;
this.item = this.machine.machine;
const arr = [];
setTimeout(async () => {
// eslint-disable-next-line no-plusplus
for (let i = -59; i <= 0; i++) {
arr.push(i);
}
const ctx = document.getElementById('myChart').getContext('2d');
this.chart = new Chart(ctx, {
type: 'line',
data: {
labels: arr,
datasets: [
{
label: 'CPU Current Load',
borderColor: 'rgb(10, 140, 152)',
data: new Array(60).fill(0),
spanGaps: true,
},
],
},
options: {
animation: {
duration: 0,
},
hover: {
animationDuration: 0,
},
responsiveAnimationDuration: 0,
elements: {
point: {
radius: 0,
},
line: {
tension: 0,
},
},
scales: {
yAxes: [
{
ticks: {
suggestedMin: 0,
suggestedMax: 100,
},
},
],
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'values from the last 60s',
},
ticks: {
display: false,
autoSkip: true,
maxTicksLimit: 20,
},
},
],
},
},
});
if (this.item.mem) {
this.initMemoryChart(arr, this.item.mem);
this.memInitialized = true;
}
}, 0);
eventHandler.on('newMachineInfo', ({ id, info }) => {
if (!id || id !== this.displayDetailed || !info) {
return;
}
if (info.error) {
this.$logger.debug(info.error);
return;
}
if (!this.memInitialized) {
this.initMemoryChart(arr, info.mem);
this.memInitialized = true;
}
for (let i = 1; i < this.timeout / 1000; i++) {
this.memoryChart.data.datasets[0].data.push(null);
this.chart.data.datasets[0].data.push(null);
}
this.memoryChart.data.datasets[0].data.push(info.mem.total - info.mem.free);
this.chart.data.datasets[0].data.push(info.cpu.currentLoad);
while (this.memoryChart.data.datasets[0].data.length > 60) {
this.memoryChart.data.datasets[0].data.shift();
this.chart.data.datasets[0].data.shift();
if (this.memoryChart.data.datasets[0].data[0] !== null) {
[this.lastMemValue] = this.memoryChart.data.datasets[0].data;
}
if (this.chart.data.datasets[0].data[0] !== null) {
[this.lastCpuValue] = this.chart.data.datasets[0].data;
}
}
if (this.memoryChart.data.datasets[0].data[0] === null) {
this.memoryChart.data.datasets[0].data.shift();
this.memoryChart.data.datasets[0].data.unshift(this.lastMemValue);
}
if (this.chart.data.datasets[0].data[0] === null) {
this.chart.data.datasets[0].data.shift();
this.chart.data.datasets[0].data.unshift(this.lastCpuValue);
}
this.item = {
...this.item,
battery: { ...this.item.battery, percent: info.battery.percent },
currentlyConnectedEnvironments: info.currentlyConnectedEnvironments,
cpuLoadLastMinute: info.cpu.loadLastMinute,
cpuLoadLastTenMinutes: info.cpu.loadLastTenMinutes,
cpuLoadLastHalfHour: info.cpu.loadLastHalfHour,
cpuLoadLastHour: info.cpu.loadLastHour,
cpuLoadLastHalfDay: info.cpu.loadLastHalfDay,
cpuLoadLastDay: info.cpu.loadLastDay,
};
this.chart.update();
this.memoryChart.update();
});
eventHandler.on('newMachineLogs', ({ id, logs }) => {
if (!id || id !== this.displayDetailed || !logs) {
return;
}
this.item = { ...this.item, logs };
});
engineNetworkInterface.subscribeToMachine(this.displayDetailed);
},
beforeDestroy() {
engineNetworkInterface.unsubscribeFromMachine(this.displayDetailed);
engineNetworkInterface.unsubscribeFromMachineLogs(this.displayDetailed);
this.show = false;
},
watch: {
machine(newValue) {
this.item = { ...this.item, ...newValue };
},
loggingDialog(showLog) {
if (showLog) {
engineNetworkInterface.subscribeToMachineLogs(this.displayDetailed);
} else {
engineNetworkInterface.unsubscribeFromMachineLogs(this.displayDetailed);
}
},
},
};
</script>