Dodano statystki ruchu kolejowego w zakładce Pociągi

This commit is contained in:
2020-08-26 21:28:34 +02:00
parent 355f92d3cd
commit fcd3ba4a3b
6 changed files with 356 additions and 19 deletions
+3 -3
View File
@@ -118,10 +118,10 @@
>{{station.routes.oneWay.noCatenary}}</span>
</td>
<td
<!-- <td
class="active-timetables"
@click="() => showScheduledTrains(station)"
>{{station.scheduledTrains.length}}</td>
>{{station.scheduledTrains.length}}</td>-->
</tr>
</table>
</div>
@@ -166,7 +166,7 @@ export default class StationTable extends styleMixin {
["Maszyniści"],
["Informacje", "ogólne"],
["Szlaki", "2tor | 1tor"],
["Aktywne RJ"],
// ["Aktywne RJ"],
];
changeSorter(index: number) {
+258
View File
@@ -0,0 +1,258 @@
<template>
<div class="train-stats">
<div class="btn-wrapper">
<button
class="stats-btn button"
@click="toggleStats"
v-if="trains.length > 0"
>{{statsOpen ? 'Zamknij' : 'Otwórz'}} statystki ruchu</button>
</div>
<div class="stats-body" v-if="statsOpen">
<h2 class="stats-header">STATYSTYKI RUCHU</h2>
<div class="stats-speed">
<div class="title">PRĘDKOŚCI POCIĄGÓW (MIN | ŚR | MAX)</div>
<div class="content">{{speedStats.min}} | {{speedStats.avg}} | {{speedStats.max}} km/h</div>
</div>
<div class="stats-length">
<div class="title">DŁUGOŚCI ROZKŁADÓW (MIN | ŚR | MAX)</div>
<div
class="content"
>{{timetableStats.min}} | {{timetableStats.avg}} | {{timetableStats.max}} km</div>
</div>
<div class="stats-categories">
<div class="title">KATEGORIE RJ</div>
<div class="category-list">
<span class="category" v-for="[key, value] of categoryList" :key="key">
<span class="category-type">{{key}}</span>
<span class="category-count">{{value}}</span>
</span>
</div>
</div>
<div class="stats-warnings">
<div class="title">TWR/SKR</div>
<div class="content">
<div class="warning twr">
Wysokiego ryzyka
[{{specialTrainCount[0]}}]
</div>
<div class="warning skr">
Przekroczona skrajnia
[{{specialTrainCount[1]}}]
</div>
</div>
</div>
<div class="stats-locos">
<div class="title">NAJCZĘSTSZE JEDNOSTKI</div>
<div class="loco-list content">
<div class="loco" v-for="(loco,i) in locoList" :key="i">{{loco[0]}} | {{loco[1]}}</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator";
import Train from "@/scripts/interfaces/Train";
@Component
export default class TrainStats extends Vue {
@Prop() readonly trains!: Train[];
statsOpen: boolean = false;
toggleStats() {
this.statsOpen = !this.statsOpen;
}
get speedStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = (
this.trains.reduce((acc, train) => acc + train.speed, 0) /
this.trains.length
).toFixed(2);
const minMax = this.trains.reduce((acc, train) => {
if (train.noTimetable) return acc;
acc[0] =
acc[0] === undefined || train.speed < acc[0] ? train.speed : acc[0];
acc[1] =
acc[1] === undefined || train.speed > acc[1] ? train.speed : acc[1];
return acc;
}, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
}
get timetableStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = (
this.trains.reduce((acc, train) => acc + train.routeDistance, 0) /
this.trains.length
).toFixed(2);
const minMax = this.trains.reduce((acc, train) => {
if (train.noTimetable) return acc;
acc[0] =
acc[0] === undefined || train.routeDistance < acc[0]
? train.routeDistance
: acc[0];
acc[1] =
acc[1] === undefined || train.routeDistance > acc[1]
? train.routeDistance
: acc[1];
return acc;
}, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
}
get categoryList(): Map<string, number> {
const map = this.trains.reduce((acc, train) => {
if (train.noTimetable || !train.category) return acc;
acc.set(
train.category,
acc.get(train.category) ? acc.get(train.category) + 1 : 1
);
return acc;
}, new Map());
return new Map([...map.entries()].sort((a, b) => b[1] - a[1]));
}
get locoList(): any[] {
const map = this.trains.reduce((acc, train) => {
if (train.noTimetable || !train.locoType) return acc;
acc.set(
train.locoType,
acc.get(train.locoType) ? acc.get(train.locoType) + 1 : 1
);
return acc;
}, new Map());
const sorted = [...map.entries()]
.sort((a, b) => b[1] - a[1])
.filter((v, i) => i < 3);
return sorted;
}
get specialTrainCount(): [number, number] {
const twrList = this.trains.filter((train) => train.TWR);
const skrList = this.trains.filter((train) => train.SKR);
return [twrList.length, skrList.length];
}
}
</script>
<style lang="scss" scoped>
@import "../../styles/responsive";
.train-stats {
padding: 0.3em 0;
font-size: 0.9em;
}
.button {
font-size: 1em;
padding: 0.5em;
}
.content {
font-size: 1.2em;
color: #ddd;
}
.stats-header {
margin-bottom: 1rem;
}
.stats-body {
display: inline-block;
max-width: 800px;
background: #333;
padding: 1rem;
}
.category-list {
display: flex;
flex-wrap: wrap;
}
.category {
margin-right: 0.4em;
margin-bottom: 0.4em;
&-type,
&-count {
display: inline-block;
padding: 0.2em 0.4em;
}
&-type {
background: #888;
}
&-count {
background: #ffc014;
color: black;
}
}
.warning {
display: inline-block;
margin-right: 0.4em;
padding: 0.2em 0.3em;
color: black;
font-weight: bold;
font-size: 0.85em;
&.twr {
background-color: #ffc700;
}
&.skr {
background-color: #ff4646;
}
}
.loco {
padding-bottom: 0.4em;
}
@include smallScreen {
.button {
font-size: 0.9rem;
}
.stats-body {
display: block;
max-width: 100%;
}
.btn-wrapper {
display: flex;
justify-content: center;
margin-top: 1rem;
}
}
</style>
+5 -13
View File
@@ -117,19 +117,11 @@ export default class TrainTable extends Vue {
}
mapTimetableSceneries(sceneries: string[]): string {
let text = "";
if (sceneries.length < 1) return "";
for (let i = sceneries.length - 2; i > 0; i--) {
const station = this.stations.find(
(station) => station.stationHash == sceneries[i]
);
if (!station) continue;
text += `${station.stationName}${i > 1 ? ", " : ""}`;
}
return text;
return sceneries
.filter((scenery, i) => i > 0 && i < sceneries.length - 1)
.join(" * ");
}
}
</script>
@@ -166,7 +158,7 @@ export default class TrainTable extends Vue {
background-color: #444;
padding: 1rem;
margin: 1rem 0;
margin-bottom: 1em;
&:nth-child(even) {
background-color: #666;
+69 -1
View File
@@ -113,6 +113,55 @@ async function getScheduledTrains(stationName: string) {
return scheduledTrains;
}
async function testLoad() {
let scheduledTrains: any[] = [];
for (let train of onlineTrainsData) {
if (train.region !== "eu" || !train.isOnline) continue;
const timetable = await queryTimetableData(train.trainNo);
if (!timetable.trainInfo) continue;
timetable.stopPoints.forEach((point) => {
const station = onlineStationsData.find(
(online) =>
online.stationName
.toLowerCase()
.includes(point.pointNameRAW.toLowerCase()) ||
online.stationName
.toLowerCase()
.includes(point.pointNameRAW.toLowerCase().split(" ")[0]) ||
online.stationName
.toLowerCase()
.includes(point.pointNameRAW.toLowerCase().split(",")[0])
);
if (!station) return;
if (!scheduledTrains[station.stationName])
scheduledTrains[station.stationName] = [];
if (
scheduledTrains[station.stationName].find(
(scheduled) => train.trainNo === scheduled.trainNo
)
)
return;
scheduledTrains[station.stationName].push({
arrivalTime: point?.arrivalTime,
departureTime: point?.departureTime,
trainCategory: timetable.trainInfo?.trainCategoryCode,
trainNo: train.trainNo,
});
});
}
return scheduledTrains;
}
@Module
class Store extends VuexModule {
private trainCount: number = 0;
@@ -122,6 +171,13 @@ class Store extends VuexModule {
private stations: Station[] = [];
// private scheduledTrains: {
// trainNo: number;
// trainCategory: string;
// arrivalTime: string;
// departureTime: string;
// }[] = [];
private filteredStations: {}[] = [];
private filterInitStates = {
@@ -267,7 +323,7 @@ class Store extends VuexModule {
// station.stationName
// );
let scheduledTrains = [];
let scheduledTrains: any[] = [];
return {
...stationData,
@@ -293,6 +349,18 @@ class Store extends VuexModule {
})
);
// const scheduled = await testLoad();
// for (let stationName in scheduled) {
// let t = updatedStations.find(
// (updated) => updated.stationName === stationName
// );
// if (!t) continue;
// t.scheduledTrains = scheduled[stationName];
// }
this.context.commit("updateStations", {
updatedStations,
trainCount: onlineTrainsData.filter(
+17 -2
View File
@@ -27,7 +27,11 @@ interface TrainData {
}
interface TimetableResponseData {
stopPoints: { pointDistance: number }[] | [];
stopPoints: {
pointDistance: number;
pointNameRAW: string;
pointName: string;
}[];
trainInfo: {
timetableId: number;
trainCategoryCode: string;
@@ -100,6 +104,17 @@ export default class TrainsModule extends VuexModule {
? train.dataCon.split(";")[0]
: train.dataCon;
const stopPoints = timetableResponseData?.stopPoints.reduce(
(acc, point) => {
if (point.pointName.includes("strong")) {
acc.push(point.pointNameRAW);
}
return acc;
},
[] as string[]
);
return {
driverId: train.driverId,
driverName: train.driverName,
@@ -118,7 +133,7 @@ export default class TrainsModule extends VuexModule {
timetableId: timetableData && timetableData.timetableId,
category: timetableData && timetableData.trainCategoryCode,
routeDistance: (timetableData && timetableData.routeDistance) || 0,
sceneries: timetableData && timetableData.sceneries,
sceneries: stopPoints,
TWR: timetableData && timetableData.twr,
SKR: timetableData && timetableData.skr,
};
+4
View File
@@ -16,6 +16,8 @@
</div>
</div>
<TrainStats :trains="trains" />
<TrainTable :computedTrains="computedTrains" />
</div>
</section>
@@ -33,6 +35,7 @@ import Loading from "@/components/App/Loading.vue";
import TrainSorter from "@/components/TrainsView/TrainSorter.vue";
import TrainTable from "@/components/TrainsView/TrainTable.vue";
import TrainStats from "@/components/TrainsView/TrainStats.vue";
import axios from "axios";
@@ -41,6 +44,7 @@ import axios from "axios";
Loading,
TrainSorter,
TrainTable,
TrainStats,
},
})
export default class TrainsView extends Vue {