diff --git a/src/components/Global/SelectBox.vue b/src/components/Global/SelectBox.vue new file mode 100644 index 0000000..f3655d8 --- /dev/null +++ b/src/components/Global/SelectBox.vue @@ -0,0 +1,109 @@ + + + + + \ No newline at end of file diff --git a/src/components/StationsView/StationTable.vue b/src/components/StationsView/StationTable.vue index 2a2c980..98aa924 100644 --- a/src/components/StationsView/StationTable.vue +++ b/src/components/StationsView/StationTable.vue @@ -202,7 +202,14 @@ export default class StationTable extends styleMixin { @Prop() readonly changeSorter!: () => void; setScenery(sceneryHash: string) { - this.$router.push({ name: "SceneryView", query: { hash: sceneryHash } }) + if ( + this.stations.findIndex( + (station) => station.stationHash === sceneryHash && station.online + ) == -1 + ) + return; + + this.$router.push({ name: "SceneryView", query: { hash: sceneryHash } }); } icons: { ascSVG; descSVG } = { ascSVG, descSVG }; diff --git a/src/data/stations.json b/src/data/stations.json index 8cc61f2..e5614aa 100644 --- a/src/data/stations.json +++ b/src/data/stations.json @@ -46,6 +46,7 @@ }, "default": false, "nonPublic": false, + "subStations": ["Borowe, podg.", "Wysoka, podg.", "Naprawa, podg.", "Borowe Towarowe"], "stops": ["Borowe, podg.", "Wysoka, podg.", "Naprawa, podg.", "Borowe Towarowe"] }, { @@ -71,7 +72,8 @@ }, "default": true, "nonPublic": false, - "stops": ["Gdańsk Główny", "Gdańsk Południowy"] + "subStations": ["Gdańsk Główny", "SKM Śródmieście", "Gdańsk Południowy"], + "stops": ["Gdańsk Główny"] }, { "stationName": "Lębork", @@ -143,7 +145,8 @@ } }, "default": true, - "nonPublic": false + "nonPublic": false, + "subStations": ["Parzęczewo", "Parzęczewo Miasto", "Parzęczewo gt"] }, { "stationName": "Aleksandrów Kujawski", @@ -264,7 +267,8 @@ }, "default": true, "nonPublic": false, - "stops": ["Głowno", "Domaniewice"] + "subStations": ["Głowno", "Domaniewice"], + "stops": ["Głowno"] }, { "stationName": "LCS Ozorków", @@ -289,6 +293,7 @@ }, "default": true, "nonPublic": false, + "subStations": ["Ozorków", "Chociszew"], "stops": ["Ozorków"] }, { @@ -314,6 +319,7 @@ }, "default": true, "nonPublic": false, + "subStations": ["Skrzynki", "Wykno"], "stops": ["Skrzynki"] }, { @@ -2049,5 +2055,28 @@ }, "default": true, "nonPublic": false + }, + { + "stationName": "Strączki", + "stationURL": "https://td2.info.pl/scenerie/straczki/", + "stationLines": "", + "reqLevel": "4", + "supportersOnly": "NIE", + "signalType": "współczesna", + "controlType": "mechaniczne", + "SBL": "", + "twoWayBlock": "", + "routes": { + "oneWay": { + "catenary": 1, + "noCatenary": 0 + }, + "twoWay": { + "catenary": 1, + "noCatenary": 0 + } + }, + "default": false, + "nonPublic": false } ] diff --git a/src/scripts/interfaces/Station.ts b/src/scripts/interfaces/Station.ts index 2a72d9c..5b2ea4b 100644 --- a/src/scripts/interfaces/Station.ts +++ b/src/scripts/interfaces/Station.ts @@ -24,9 +24,10 @@ export default interface Station { oneWay: { catenary: number; noCatenary: number }; twoWay: { catenary: number; noCatenary: number }; }; - subStations: { + checkpoints: { + checkpointName: string; scheduledTrains: ScheduledTrain[], - }[]; + }[] | null; online: boolean; occupiedTo: string; statusTimestamp: number; diff --git a/src/store/store.ts b/src/store/store.ts index c99164c..f83aebb 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -154,74 +154,67 @@ export default class Store extends VuexModule { @Action({ commit: 'updateTimetableData' }) async fetchTimetableData() { - return await Promise.all( - this.trainList.map(async train => { - const timetable = await (await axios.get(timetableURL(train.trainNo))).data.message; - const trainInfo = timetable.trainInfo; + return this.trainList.reduce(async (acc: Promise, train) => { + const timetable = await (await axios.get(timetableURL(train.trainNo))).data.message; + const trainInfo = timetable.trainInfo; - - if (timetable && trainInfo) { - let timetableData!: TimetableData; + if (!timetable || !trainInfo) return acc; + + const followingStops: TrainStop[] = timetable.stopPoints.reduce((stopsAcc: TrainStop[], point) => { + const arrivalTimestamp = getTimestamp(point.arrivalTime); + const arrivalRealTimestamp = getTimestamp(point.arrivalRealTime); - const followingStops: TrainStop[] = timetable.stopPoints.reduce((acc: TrainStop[], point) => { - const arrivalTimestamp = getTimestamp(point.arrivalTime); - const arrivalRealTimestamp = getTimestamp(point.arrivalRealTime); + const departureTimestamp = getTimestamp(point.departureTime); + const departureRealTimestamp = getTimestamp(point.departureRealTime); - const departureTimestamp = getTimestamp(point.departureTime); - const departureRealTimestamp = getTimestamp(point.departureRealTime); + stopsAcc.push({ + stopName: point.pointName, + stopNameRAW: point.pointNameRAW, + stopType: point.pointStopType, + mainStop: point.pointName.includes('strong'), - acc.push({ - stopName: point.pointName, - stopNameRAW: point.pointNameRAW, - stopType: point.pointStopType, - mainStop: point.pointName.includes('strong'), + arrivalLine: point.arrivalLine, + arrivalTimeString: timestampToTime(point.arrivalTime), + arrivalTimestamp: arrivalTimestamp, + arrivalRealTimeString: timestampToTime(point.arrivalRealTime), + arrivalRealTimestamp: arrivalRealTimestamp, + arrivalDelay: point.arrivalDelay, - arrivalLine: point.arrivalLine, - arrivalTimeString: timestampToTime(point.arrivalTime), - arrivalTimestamp: arrivalTimestamp, - arrivalRealTimeString: timestampToTime(point.arrivalRealTime), - arrivalRealTimestamp: arrivalRealTimestamp, - arrivalDelay: point.arrivalDelay, + departureLine: point.departureLine, + departureTimeString: timestampToTime(point.departureTime), + departureTimestamp: departureTimestamp, + departureRealTimeString: timestampToTime(point.departureRealTime), + departureRealTimestamp: departureRealTimestamp, + departureDelay: point.departureDelay, - departureLine: point.departureLine, - departureTimeString: timestampToTime(point.departureTime), - departureTimestamp: departureTimestamp, - departureRealTimeString: timestampToTime(point.departureRealTime), - departureRealTimestamp: departureRealTimestamp, - departureDelay: point.departureDelay, + beginsHere: arrivalTimestamp == 0, + terminatesHere: departureTimestamp == 0, - beginsHere: arrivalTimestamp == 0, - terminatesHere: departureTimestamp == 0, + confirmed: point.confirmed, + stopped: point.isStopped, + stopTime: point.pointStopTime, + }); - confirmed: point.confirmed, - stopped: point.isStopped, - stopTime: point.pointStopTime, - }); + return stopsAcc; + }, []); - return acc; - }, []); + (await acc).push({ + trainNo: train.trainNo, + driverName: train.driverName, + driverId: train.driverId, + currentStationName: train.currentStationName, + currentStationHash: train.currentStationHash, + timetableId: trainInfo.timetableId, + category: trainInfo.trainCategoryCode, + route: trainInfo.route, + TWR: trainInfo.twr, + SKR: trainInfo.skr, + routeDistance: timetable.stopPoints[timetable.stopPoints.length - 1].pointDistance, + followingStops, + }); - timetableData = { - trainNo: train.trainNo, - driverName: train.driverName, - driverId: train.driverId, - currentStationName: train.currentStationName, - currentStationHash: train.currentStationHash, - timetableId: trainInfo.timetableId, - category: trainInfo.trainCategoryCode, - route: trainInfo.route, - TWR: trainInfo.twr, - SKR: trainInfo.skr, - routeDistance: timetable.stopPoints[timetable.stopPoints.length - 1].pointDistance, - followingStops, - }; - - return timetableData; - } - - return null; - }) - ); + return acc; + }, Promise.resolve([])) } @Action @@ -305,6 +298,7 @@ export default class Store extends VuexModule { @Mutation setJSONData() { this.stationList = JSONStationData.map(stationData => ({ + ...stationData, stationProject: '', spawnString: '', stationHash: '', @@ -320,8 +314,7 @@ export default class Store extends VuexModule { statusTimestamp: -3, stationTrains: [], scheduledTrains: [], - subStations: [], - ...stationData, + checkpoints: stationData.subStations ? stationData.subStations.map(sub => ({ checkpointName: sub, scheduledTrains: []})) : null, })); } @@ -330,13 +323,11 @@ export default class Store extends VuexModule { this.stationList = this.stationList.reduce((acc, station) => { const onlineStationData = updatedStationList.find(updatedStation => updatedStation.stationName === station.stationName); const registeredStation = JSONStationData.find(data => data.stationName === station.stationName); - const subStations = registeredStation?.stops && registeredStation.stops; if (onlineStationData) acc.push({ ...station, ...onlineStationData, - subStations, online: true, }); else if (registeredStation) @@ -357,7 +348,7 @@ export default class Store extends VuexModule { statusTimestamp: -3, stationTrains: [], scheduledTrains: [], - subStations: [] + checkpoints: null, }); return acc; @@ -372,6 +363,7 @@ export default class Store extends VuexModule { ...updatedStation, scheduledTrains: [], stationTrains: [], + subStations: [], online: true, reqLevel: '-1', nonPublic: true, @@ -401,68 +393,179 @@ export default class Store extends VuexModule { @Mutation private updateTimetableData(timetableList: TimetableData[]) { this.stationList = this.stationList.map(station => { - const scheduledTrains: Station['scheduledTrains'] = timetableList.reduce((acc: Station['scheduledTrains'], timetableData: any, index) => { - const scheduledIndex = timetableData - ? timetableData.followingStops.findIndex((stop: any) => { - const stationName = station.stationName.toLowerCase(); - const stopName = stop.stopNameRAW.toLowerCase(); -//stationName.includes('lcs') && - return ( - stationName.includes(stopName) || - stopName.includes(stationName) || - (stopName.includes('podg.') && stopName.split(', podg.')[0] && stationName.includes(stopName.split(', podg.')[0])) || - (JSONStationData.some(data => data.stationName.includes(station.stationName) && data.stops && data.stops.includes(stop.stopNameRAW))) - ); - }) - : -1; - - - if (scheduledIndex >= 0) { - - const stopInfo = timetableData.followingStops[scheduledIndex]; + + const stationName = station.stationName.toLowerCase(); + const scheduledTrains: Station['scheduledTrains'] = timetableList.reduce((acc: Station['scheduledTrains'], timetableData: TimetableData, index) => { - let stopStatus = ""; - let stopLabel = ""; - let nearestStop = ""; - - if (stopInfo.terminatesHere && stopInfo.confirmed) { stopStatus = "terminated"; stopLabel = "Skończył bieg" } - else if (!stopInfo.terminatesHere && stopInfo.confirmed) { stopStatus = "departed"; stopLabel = "Odprawiony" } - // else if (timetableData.currentStationName == station.stationName && stopInfo.beginsHere ) { stopStatus = "online"; stopLabel = "Podstawia się" } - else if (timetableData.currentStationName == station.stationName && !stopInfo.stopped) { stopStatus = "online"; stopLabel = "Na stacji" } - else if (timetableData.currentStationName == station.stationName && stopInfo.stopped) { stopStatus = "stopped"; stopLabel = "Postój" } - else if (timetableData.currentStationName != station.stationName) { stopStatus = "arriving"; stopLabel = "W drodze" } + const stopInfoIndex = timetableData.followingStops.findIndex((stop) => { + const stopName = stop.stopNameRAW.toLowerCase(); - for (let i = scheduledIndex + 1; i < timetableData.followingStops.length - 1; i++){ - const stop = timetableData.followingStops[i]; + if (stationName === stopName) return true; + if (stopName.includes(stationName)) return true; + if (stationName.includes(stopName)) return true; + + if (stopName.includes("podg.") && stopName.split(", podg.")[0] && stationName.includes(stopName.split(", podg.")[0])) return true; + + if (JSONStationData.some(data => data.stationName.includes(station.stationName) && data.stops && data.stops.includes(stop.stopNameRAW))) return true; - if (stop.mainStop && stop.stopType.includes("ph")) { - nearestStop = stop.stopNameRAW; - break; - } - } + return false; + }); + + if (stopInfoIndex == -1) + return acc; + + const stopInfo = timetableData.followingStops[stopInfoIndex]; + + let stopStatus = ""; + let stopLabel = ""; + let nearestStop = ""; - acc.push({ - trainNo: timetableData.trainNo, - driverName: timetableData.driverName, - driverId: timetableData.driverId, - currentStationName: timetableData.currentStationName, - currentStationHash: timetableData.currentStationHash, - category: timetableData.category, - beginsAt: timetableData.followingStops[0].stopNameRAW, - terminatesAt: timetableData.followingStops[timetableData.followingStops.length - 1].stopNameRAW, - nearestStop, - stopInfo, - stopLabel, - stopStatus - }); + if (stopInfo.terminatesHere && stopInfo.confirmed) { stopStatus = "terminated"; stopLabel = "Skończył bieg" } + else if (!stopInfo.terminatesHere && stopInfo.confirmed) { stopStatus = "departed"; stopLabel = "Odprawiony" } + // else if (timetableData.currentStationName == station.stationName && stopInfo.beginsHere ) { stopStatus = "online"; stopLabel = "Podstawia się" } + else if (timetableData.currentStationName == station.stationName && !stopInfo.stopped) { stopStatus = "online"; stopLabel = "Na stacji" } + else if (timetableData.currentStationName == station.stationName && stopInfo.stopped) { stopStatus = "stopped"; stopLabel = "Postój" } + else if (timetableData.currentStationName != station.stationName) { stopStatus = "arriving"; stopLabel = "W drodze" } + + for (let i = stopInfoIndex; i < timetableData.followingStops.length - 1; i++){ + const stop = timetableData.followingStops[i]; + + if (stop.mainStop && stop.stopType.includes("ph")) { + nearestStop = stop.stopNameRAW; + break; + } } + acc.push({ + trainNo: timetableData.trainNo, + driverName: timetableData.driverName, + driverId: timetableData.driverId, + currentStationName: timetableData.currentStationName, + currentStationHash: timetableData.currentStationHash, + category: timetableData.category, + beginsAt: timetableData.followingStops[0].stopNameRAW, + terminatesAt: timetableData.followingStops[timetableData.followingStops.length - 1].stopNameRAW, + nearestStop, + stopInfo, + stopLabel, + stopStatus + }); + return acc; }, []); + if (station.checkpoints) { + station.checkpoints.forEach(cp => cp.scheduledTrains.length = 0) + + for (let checkpoint of station.checkpoints) { + timetableList.reduce((acc, data) => { + const foundStops = data.followingStops.filter(stop => stop.stopNameRAW === checkpoint.checkpointName).forEach(stopInfo => { + // const stopInfo = data.followingStops[stopInfoIndex]; + + let stopStatus = ""; + let stopLabel = ""; + let nearestStop = ""; + + if (stopInfo.terminatesHere && stopInfo.confirmed) { stopStatus = "terminated"; stopLabel = "Skończył bieg" } + else if (!stopInfo.terminatesHere && stopInfo.confirmed) { stopStatus = "departed"; stopLabel = "Odprawiony" } + else if (data.currentStationName == station.stationName && !stopInfo.stopped) { stopStatus = "online"; stopLabel = "Na stacji" } + else if (data.currentStationName == station.stationName && stopInfo.stopped) { stopStatus = "stopped"; stopLabel = "Postój" } + else if (data.currentStationName != station.stationName) { stopStatus = "arriving"; stopLabel = "W drodze" } + + // for (let i = stopInfoIndex; i < data.followingStops.length - 1; i++){ + // const stop = data.followingStops[i]; + + // if (stop.mainStop && stop.stopType.includes("ph")) { + // nearestStop = stop.stopNameRAW; + // break; + // } + // } + + checkpoint.scheduledTrains.push({ + trainNo: data.trainNo, + driverName: data.driverName, + driverId: data.driverId, + currentStationName: data.currentStationName, + currentStationHash: data.currentStationHash, + category: data.category, + beginsAt: data.followingStops[0].stopNameRAW, + terminatesAt: data.followingStops[data.followingStops.length - 1].stopNameRAW, + stopInfo, + stopLabel, + stopStatus, + nearestStop + }); + }) + + return acc; + }, []); + } + + + // const checkpoints: Station['scheduledTrains'] = timetableList.reduce((acc: any, timetableData: TimetableData, index) => { + // const stopInfoIndex = timetableData.followingStops.findIndex((stop) => { + // const stopName = stop.stopNameRAW.toLowerCase(); + + // if (station.checkpoints?.some(cp => cp.checkpointName.includes(stopName))) return true; + // if (stopName.includes("podg.") && stopName.split(", podg.")[0] && station.checkpoints?.filter(cp => cp.checkpointName.includes(stopName.split(", podg.")[0]))) return true; + + // return false; + // }); + + // if (stopInfoIndex == -1) return acc; + + // const stopInfo = timetableData.followingStops[stopInfoIndex]; + + // let stopStatus = ""; + // let stopLabel = ""; + // let nearestStop = ""; + + // if (stopInfo.terminatesHere && stopInfo.confirmed) { stopStatus = "terminated"; stopLabel = "Skończył bieg" } + // else if (!stopInfo.terminatesHere && stopInfo.confirmed) { stopStatus = "departed"; stopLabel = "Odprawiony" } + // // else if (timetableData.currentStationName == station.stationName && stopInfo.beginsHere ) { stopStatus = "online"; stopLabel = "Podstawia się" } + // else if (timetableData.currentStationName == station.stationName && !stopInfo.stopped) { stopStatus = "online"; stopLabel = "Na stacji" } + // else if (timetableData.currentStationName == station.stationName && stopInfo.stopped) { stopStatus = "stopped"; stopLabel = "Postój" } + // else if (timetableData.currentStationName != station.stationName) { stopStatus = "arriving"; stopLabel = "W drodze" } + + // for (let i = stopInfoIndex; i < timetableData.followingStops.length - 1; i++){ + // const stop = timetableData.followingStops[i]; + + // if (stop.mainStop && stop.stopType.includes("ph")) { + // nearestStop = stop.stopNameRAW; + // break; + // } + // } + + + + // acc.push({ + // checkpointName: stopInfo.stopNameRAW, + // scheduledTrains: { + // trainNo: timetableData.trainNo, + // driverName: timetableData.driverName, + // driverId: timetableData.driverId, + // currentStationName: timetableData.currentStationName, + // currentStationHash: timetableData.currentStationHash, + // category: timetableData.category, + // beginsAt: timetableData.followingStops[0].stopNameRAW, + // terminatesAt: timetableData.followingStops[timetableData.followingStops.length - 1].stopNameRAW, + // nearestStop, + // stopInfo, + // stopLabel, + // stopStatus + // } + // }); + + // return acc; + // }, []); + } + return { ...station, scheduledTrains }; }); + + + this.trainList = this.trainList.reduce((acc, train) => { const timetableData = timetableList.find(data => data && data.trainNo === train.trainNo); diff --git a/src/views/SceneryView.vue b/src/views/SceneryView.vue index 6c55846..4f8748a 100644 --- a/src/views/SceneryView.vue +++ b/src/views/SceneryView.vue @@ -6,12 +6,21 @@ > Ups! Nie znaleziono danej stacji bądź jest ona offline!
-
{{ stationInfo.stationName }}
+
+ {{ stationInfo.stationName }} + + {{ stationInfo.stationName }} +
#{{ stationInfo.stationHash }}
@@ -163,6 +172,27 @@ +
+
+
+ {{ selectedOption }} + icon-select +
+ +
    +
  • + + +
  • +
+
+
+ Ładowanie... station.stationHash === this.$route.query.hash.toString()) || null; + const info = + this.storeStationList.find( + (station) => station.stationHash === this.$route.query.hash.toString() + ) || null; + + if (!info) return null; + + if (!info.checkpoints) return info; + + if (this.selectedOption == "") + this.selectedOption = info.checkpoints[0].checkpointName; + + return info; } get computedDispatcherExp(): string { if (!this.stationInfo) return ""; - return this.stationInfo.dispatcherExp < 2 ? "L" : `${this.stationInfo.dispatcherExp}`; @@ -305,23 +365,40 @@ export default class SceneryView extends styleMixin { get computedStationTrains() { if (!this.stationInfo) return null; - return this.stationInfo.stationTrains.map(stationTrain => { - const scheduledData = this.stationInfo?.scheduledTrains.find(scheduledTrain => scheduledTrain.trainNo === stationTrain.trainNo); + return this.stationInfo.stationTrains.map((stationTrain) => { + const scheduledData = this.stationInfo?.scheduledTrains.find( + (scheduledTrain) => scheduledTrain.trainNo === stationTrain.trainNo + ); return { ...stationTrain, - stopStatus: scheduledData?.stopStatus || "no-timetable" - } - }) + stopStatus: scheduledData?.stopStatus || "no-timetable", + }; + }); } get computedScheduledTrains() { - return this.stationInfo?.scheduledTrains.sort((a, b) => { - if (a.stopInfo.arrivalTimestamp > b.stopInfo.arrivalTimestamp) return 1; - else if ((a.stopInfo.arrivalTimestamp < b.stopInfo.arrivalTimestamp)) return -1; + if (!this.stationInfo) return []; - return a.stopInfo.departureTimestamp > b.stopInfo.departureTimestamp ? 1 : -1; - }) + let scheduledTrains: ScheduledTrain[] | undefined; + + if (this.stationInfo.checkpoints) + scheduledTrains = this.stationInfo.checkpoints.find( + (cp) => cp.checkpointName === this.selectedOption + )?.scheduledTrains; + else scheduledTrains = this.stationInfo.scheduledTrains; + + return ( + scheduledTrains?.sort((a, b) => { + if (a.stopInfo.arrivalTimestamp > b.stopInfo.arrivalTimestamp) return 1; + else if (a.stopInfo.arrivalTimestamp < b.stopInfo.arrivalTimestamp) + return -1; + + return a.stopInfo.departureTimestamp > b.stopInfo.departureTimestamp + ? 1 + : -1; + }) || [] + ); } } @@ -347,16 +424,97 @@ h3 { } } +.select-box { + display: flex; + justify-content: center; +} + +.option { + &-container { + position: relative; + + input { + display: none; + } + + label { + padding: 0.5rem 1rem; + cursor: pointer; + } + } + + &-item { + display: flex; + justify-content: center; + + &:hover { + background-color: rgba(#868686, 0.85); + } + + transition: background 150ms ease-in; + } + + &-selected, + &-list { + background: #444; + border-radius: 0.5em; + } + + &-selected { + display: flex; + justify-content: space-between; + align-items: center; + + padding: 0.5rem 1rem; + min-width: 10em; + cursor: pointer; + + span { + margin-right: 2rem; + } + + img { + max-width: 0.75em; + } + } + + &-list { + position: absolute; + top: 100%; + left: 0; + + width: 100%; + + z-index: 10; + + background-color: rgba(#222, 0.95); + overflow: hidden; + + max-height: 0; + + &.open { + max-height: 250px; + opacity: 1; + } + + transition: all 150ms ease-in; + } +} + .scenery { &-offline { align-self: center; font-size: 2em; text-align: center; + padding: 0 1em; color: $warningCol; - button { - margin: 1em auto; + display: inline-block; + + .button { + margin: 1rem auto; + font-size: 0.85em; } } @@ -674,6 +832,7 @@ h3 { .stop-time { font-size: 0.7em; + margin: 5px 0; } } } diff --git a/src/views/TimetableView.vue b/src/views/TimetableView.vue index 9d980ba..0f6cdf6 100644 --- a/src/views/TimetableView.vue +++ b/src/views/TimetableView.vue @@ -1,7 +1,9 @@ @@ -257,8 +243,6 @@ export default class TimetableView extends Vue { let nearestStop = train.nearestStop.toUpperCase(); - - for (let name of filteredNames) { if (name[0] === destination) destination = name[1];