Reorganizacja modułów, naprawa synchronizacji stacji

This commit is contained in:
2020-08-28 21:52:35 +02:00
parent fcd3ba4a3b
commit dc70bd8a38
8 changed files with 443 additions and 92 deletions
+7 -5
View File
@@ -13,9 +13,9 @@
<Clock /> <Clock />
<div class="counter"> <div class="counter">
<img src="@/assets/icon-dispatcher.svg" alt="icon dispatcher" /> <img src="@/assets/icon-dispatcher.svg" alt="icon dispatcher" />
<span>{{stationCount}}</span> <span>{{onlineInfo.stationCount}}</span>
<span>{{trainCount}}</span> <span>{{onlineInfo.trainCount}}</span>
<img src="@/assets/icon-train.svg" alt="icon train" /> <img src="@/assets/icon-train.svg" alt="icon train" />
</div> </div>
</span> </span>
@@ -57,16 +57,18 @@ import Clock from "@/components/App/Clock.vue";
components: { Error, Loading, Clock }, components: { Error, Loading, Clock },
}) })
export default class App extends Vue { export default class App extends Vue {
@Getter("getStations") stations; @Getter("getStationList") stations;
@Getter("getTrainCount") trainCount; @Getter("getOnlineInfo") onlineInfo;
@Getter("getStationCount") stationCount;
@Action("initStations") initStations; @Action("initStations") initStations;
@Action("fetchOnlineStations") fetchStations;
errorMessage: string = ""; errorMessage: string = "";
async mounted() { async mounted() {
this.initStations(); this.initStations();
setInterval(this.fetchStations, 5000);
} }
} }
</script> </script>
@@ -137,6 +137,8 @@ export default class TrainSorter extends Vue {
top: 0; top: 0;
left: 0; left: 0;
z-index: 5;
width: 100%; width: 100%;
background-color: rgba(#333, 0.85); background-color: rgba(#333, 0.85);
overflow: hidden; overflow: hidden;
+50 -35
View File
@@ -5,36 +5,32 @@
class="stats-btn button" class="stats-btn button"
@click="toggleStats" @click="toggleStats"
v-if="trains.length > 0" v-if="trains.length > 0"
>{{statsOpen ? 'Zamknij' : 'Otwórz'}} statystki ruchu</button> >STATYSTYKI RUCHU</button>
</div> </div>
<div class="stats-body" v-if="statsOpen"> <transition name="stats-anim">
<h2 class="stats-header">STATYSTYKI RUCHU</h2> <div class="stats-body" v-if="statsOpen">
<div class="stats-speed"> <h2 class="stats-header">STATYSTYKI RUCHU</h2>
<div class="title">PRĘDKOŚCI POCIĄGÓW (MIN | ŚR | MAX)</div> <div class="stats-speed">
<div class="content">{{speedStats.min}} | {{speedStats.avg}} | {{speedStats.max}} km/h</div> <div class="title">PRĘDKOŚCI POCIĄGÓW (MIN | ŚR | MAX) [km/h]</div>
</div> <div class="content">{{speedStats.min}} | {{speedStats.avg}} | {{speedStats.max}}</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>
<div class="stats-warnings"> <div class="stats-length">
<div class="title">TWR/SKR</div> <div class="title">DŁUGOŚCI ROZKŁADÓW (MIN | ŚR | MAX) [km]</div>
<div class="content"> <div
class="content"
>{{timetableStats.min}} | {{timetableStats.avg}} | {{timetableStats.max}}</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 class="warning twr"> <div class="warning twr">
Wysokiego ryzyka Wysokiego ryzyka
[{{specialTrainCount[0]}}] [{{specialTrainCount[0]}}]
@@ -45,15 +41,15 @@
[{{specialTrainCount[1]}}] [{{specialTrainCount[1]}}]
</div> </div>
</div> </div>
</div>
<div class="stats-locos"> <div class="stats-locos">
<div class="title">NAJCZĘSTSZE JEDNOSTKI</div> <div class="title">NAJCZĘSTSZE JEDNOSTKI</div>
<div class="loco-list content"> <div class="loco-list content">
<div class="loco" v-for="(loco,i) in locoList" :key="i">{{loco[0]}} | {{loco[1]}}</div> <div class="loco" v-for="(loco,i) in locoList" :key="i">{{loco[0]}} | {{loco[1]}}</div>
</div>
</div> </div>
</div> </div>
</div> </transition>
</div> </div>
</template> </template>
@@ -166,13 +162,28 @@ export default class TrainStats extends Vue {
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive"; @import "../../styles/responsive";
.stats-anim {
&-enter-active,
&-leave-active {
transition: all 150ms ease-out;
}
&-enter,
&-leave-to {
opacity: 0;
transform: translateY(30px);
}
}
.train-stats { .train-stats {
padding: 0.3em 0; padding: 0.3em 0;
font-size: 0.9em; font-size: 0.9em;
position: relative;
} }
.button { .button {
font-size: 1em; font-size: 1.1em;
padding: 0.5em; padding: 0.5em;
} }
@@ -186,10 +197,12 @@ export default class TrainStats extends Vue {
} }
.stats-body { .stats-body {
position: absolute;
display: inline-block; display: inline-block;
max-width: 800px; max-width: 800px;
background: #333; background: rgba(black, 0.85);
border-radius: 0 1em 1em 1em;
padding: 1rem; padding: 1rem;
} }
@@ -247,7 +260,9 @@ export default class TrainStats extends Vue {
} }
.stats-body { .stats-body {
display: block; display: block;
max-width: 100%; width: 100%;
border-radius: 0 0 1em 1em;
} }
.btn-wrapper { .btn-wrapper {
display: flex; display: flex;
+3 -3
View File
@@ -1,12 +1,12 @@
import Vue from "vue"; import Vue from "vue";
import Vuex from "vuex"; import Vuex from "vuex";
import Store from "@/store/modules/store"; import StationsModule from "@/store/modules/stationsModule";
import TrainsModule from "@/store/modules/trains"; import TrainsModule from "@/store/modules/trainsModule";
Vue.use(Vuex); Vue.use(Vuex);
const store = new Vuex.Store({ const store = new Vuex.Store({
modules: { modules: {
Store, StationsModule,
TrainsModule, TrainsModule,
}, },
}); });
+380
View File
@@ -0,0 +1,380 @@
import { Module, VuexModule, Mutation, Action } from "vuex-module-decorators";
import axios from "axios";
import data from "@/data/stations.json";
import Station from "@/scripts/interfaces/Station";
const stationsOnlineURL =
"https://api.td2.info.pl:9640/?method=getStationsOnline";
const trainsOnlineURL = "https://api.td2.info.pl:9640/?method=getTrainsOnline";
const dispatchersOnlineURL =
"https://api.td2.info.pl:9640/?method=readFromSWDR&value=getDispatcherStatusList%3B1";
enum ConnState {
Loading = 0,
Error = 1,
Connected = 2,
}
interface TimetableResponseData {
stopPoints:
| {
arrivalTime: string;
arrivalDelay: number;
departureTime: string;
departureDelay: number;
pointNameRAW: string;
}[]
| [];
trainInfo: {
timetableId: number;
trainCategoryCode: string;
};
}
interface OnlineStationsResponseData {
stationName: string;
stationHash: string;
maxUsers: number;
currentUsers: number;
spawnString: string;
dispatcherRate: number;
dispatcherName: string;
dispatcherExp: number;
dispatcherId: number;
region: string;
isOnline: number;
}
let onlineDispatchersData: [string, string, number, number][];
let onlineStationsData: OnlineStationsResponseData[];
let onlineTrainsData: {
isOnline: number;
region: string;
trainNo: number;
station: { stationName: string };
}[];
const filterInitStates = {
default: false,
notDefault: false,
nonPublic: false,
SPK: false,
SCS: false,
ręczne: false,
mechaniczne: false,
współczesna: false,
kształtowa: false,
historyczna: false,
mieszana: false,
minLevel: 0,
minOneWayCatenary: 0,
minOneWay: 0,
minTwoWayCatenary: 0,
minTwoWay: 0,
"no-1track": false,
"no-2track": false,
free: true,
occupied: false,
ending: false,
};
const queryStations = axios.get(stationsOnlineURL);
const queryTrains = axios.get(trainsOnlineURL);
const queryDispatchers = axios.get(dispatchersOnlineURL);
const getStationLabel = (stationStatus: any) => {
if (!stationStatus) return "NIEZALOGOWANY";
const statusCode = stationStatus[2];
const statusTimestamp = stationStatus[3];
switch (statusCode) {
case 0:
if (statusTimestamp - Date.now() > 21000000) return "BEZ LIMITU";
return `DO ${new Date(statusTimestamp).toLocaleTimeString("en-US", {
hour12: false,
hour: "2-digit",
minute: "2-digit",
})}`;
case 1:
return "Z/W";
case 2:
if (statusTimestamp == 0) return "KOŃCZY";
break;
case 3:
return "BRAK MIEJSCA";
default:
break;
}
return "NIEDOSTĘPNY";
};
const getOpenSpawns = (spawnString: string) => {
if (!spawnString) return "";
return spawnString
.split(";")
.map((v) => (v.split(",")[6] ? v.split(",")[6] : v.split(",")[0]));
};
const filterStations = (stations, filters) => {
return stations.filter((station) => {
if ((station.nonPublic || !station.reqLevel) && filters["nonPublic"])
return false;
if (!station.reqLevel || station.reqLevel == "-1") return true;
if (station.online && station.occupiedTo == "KOŃCZY" && filters["ending"])
return false;
if (station.online && filters["occupied"]) return false;
if (!station.online && filters["free"]) return false;
if (station.default && filters["default"]) return false;
if (!station.default && filters["notDefault"]) return false;
if (station.reqLevel < filters["minLevel"]) return false;
if (
filters["no-1track"] &&
(station.routes.oneWay.catenary != 0 ||
station.routes.oneWay.noCatenary != 0)
)
return false;
if (
filters["no-2track"] &&
(station.routes.twoWay.catenary != 0 ||
station.routes.twoWay.noCatenary != 0)
)
return false;
if (station.routes.oneWay.catenary < filters["minOneWayCatenary"])
return false;
if (station.routes.oneWay.noCatenary < filters["minOneWay"]) return false;
if (station.routes.twoWay.catenary < filters["minTwoWayCatenary"])
return false;
if (station.routes.twoWay.noCatenary < filters["minTwoWay"]) return false;
if (filters[station.controlType]) return false;
if (filters[station.signalType]) return false;
if (filters["SPK"] && station.controlType.includes("SPK")) return false;
if (filters["SCS"] && station.controlType.includes("SCS")) return false;
if (filters["mechaniczne"] && station.controlType.includes("mechaniczne"))
return false;
if (filters["ręczne"] && station.controlType.includes("ręczne"))
return false;
return true;
});
};
@Module
export default class StationsModule extends VuexModule {
private trainCount: number = 0;
private stationCount: number = 0;
private stationsConnectionState: ConnState = ConnState.Loading;
private stations: Station[] = [];
private filteredStations: {}[] = [];
private filters: any = { ...filterInitStates };
get getConnectionState() {
return this.stationsConnectionState;
}
get getOnlineInfo() {
return { trainCount: this.trainCount, stationCount: this.stationCount };
}
get getStationList() {
return this.filteredStations;
}
get getFilters() {
return this.filters;
}
@Mutation
private setConnectionState(state: ConnState) {
this.stationsConnectionState = state;
}
@Mutation
private updateStations(updatedStations) {
this.stations = this.stations.reduce((acc, station) => {
const onlineStationData = updatedStations.find(
(uStation) => uStation.stationName === station.stationName
);
if (!onlineStationData) {
acc.push({
...station,
stationProject: "",
spawnString: "",
stationHash: "",
maxUsers: 0,
currentUsers: 0,
dispatcherName: "",
dispatcherRate: 0,
dispatcherExp: -1,
dispatcherId: 0,
occupiedTo: "WOLNA",
statusTimestamp: 0,
scheduledTrains: [],
online: false,
});
return acc;
}
acc.push({ ...station, ...onlineStationData, online: true });
// updatedStations = updatedStations.filter(
// (updated: any) => updated.stationName !== station.stationName
// );
return acc;
}, [] as Station[]);
// Dodawanie do listy online potencjalnych scenerii niewpisanych do bazy
updatedStations.forEach((updated: any) => {
const alreadyInList: any = this.stations.find(
(station) => station.stationName === updated.stationName
);
if (!alreadyInList) {
this.stations.push({ ...updated, online: true, reqLevel: "-1" });
}
});
this.filteredStations = filterStations(this.stations, this.filters);
this.stationCount = this.stations.filter(
(station) => station.online
).length;
this.trainCount = onlineTrainsData.filter(
(train) => train.isOnline && train.region === "eu"
).length;
this.stationsConnectionState = ConnState.Connected;
}
@Mutation
private resetFilterList() {
this.filters = { ...filterInitStates };
this.filteredStations = filterStations(this.stations, this.filters);
}
@Mutation
private changeFilter({ filterName, value }) {
this.filters[filterName] = value;
this.filteredStations = filterStations(this.stations, this.filters);
}
@Mutation
private mutateStations(stations) {
this.stations = stations;
}
@Action({ commit: "changeFilter" })
setFilter(payload: { filterName: string; value: number | boolean }) {
return payload;
}
@Action({ commit: "resetFilterList" })
resetFilters() {}
@Action({ commit: "mutateStations" })
async loadStations() {
return await data.map((stationData) => ({
stationProject: "",
spawnString: "",
stationHash: "",
maxUsers: 0,
currentUsers: 0,
dispatcherName: "",
dispatcherRate: 0,
dispatcherExp: -1,
dispatcherId: 0,
online: false,
occupiedTo: "WOLNA",
statusTimestamp: 0,
scheduledTrains: [],
...stationData,
}));
}
@Action
async initStations() {
this.context.dispatch("loadStations");
this.context.dispatch("fetchOnlineStations");
}
@Action({ commit: "updateStations" })
async fetchOnlineStations() {
return await Promise.all([
axios.get(stationsOnlineURL),
axios.get(trainsOnlineURL),
axios.get(dispatchersOnlineURL),
])
.then(async (response) => {
onlineStationsData = response[0].data.message;
onlineTrainsData = await response[1].data.message;
onlineDispatchersData = await response[2].data.message;
const updatedStations = await Promise.all(
onlineStationsData
.filter((station) => station.region === "eu" && station.isOnline)
.map(async (station) => {
const stationStatus = onlineDispatchersData.find(
(status) =>
status[0] == station.stationHash && status[1] == "eu"
);
const statusLabel = getStationLabel(stationStatus);
const statusTimestamp = stationStatus ? stationStatus[3] : -1;
const trains = onlineTrainsData.filter(
(train) =>
train.region === "eu" &&
train.isOnline &&
train.station.stationName === station.stationName
);
const stationData = data.find(
(s) => s.stationName === station.stationName
) || { stationName: station.stationName, stationURL: "" };
return {
...stationData,
stationHash: station.stationHash,
maxUsers: station.maxUsers,
currentUsers: station.currentUsers,
spawnString: getOpenSpawns(station.spawnString),
dispatcherName: station.dispatcherName,
dispatcherRate: station.dispatcherRate,
dispatcherId: station.dispatcherId,
dispatcherExp: station.dispatcherExp,
occupiedTo: statusLabel,
statusTimestamp,
trains,
};
})
);
return updatedStations;
})
.catch(() => {
this.context.commit("setConnectionState", ConnState.Error);
});
}
}
-48
View File
@@ -114,54 +114,6 @@ async function getScheduledTrains(stationName: string) {
return scheduledTrains; 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 @Module
class Store extends VuexModule { class Store extends VuexModule {
private trainCount: number = 0; private trainCount: number = 0;
+1 -1
View File
@@ -52,7 +52,7 @@ export default class StationsView extends Vue {
inputs = { ...inputData }; inputs = { ...inputData };
STORAGE_KEY: string = "options_saved"; STORAGE_KEY: string = "options_saved";
@Getter("getStations") stations!: Station[]; @Getter("getStationList") stations!: Station[];
@Getter("getConnectionState") connectionState!: ConnState; @Getter("getConnectionState") connectionState!: ConnState;
@Action("setFilter") setFilter; @Action("setFilter") setFilter;