fix(station): remove arrival and departure preview audio flow

Delete the station-side arrival/departure announcement requests and drop the server handlers for train arrival, train departure, and play_sequence messages so preview audio no longer interrupts live music.
This commit is contained in:
2026-07-31 21:53:12 +08:00
parent fd154afd82
commit 218aaabb50
2 changed files with 1 additions and 307 deletions
-132
View File
@@ -1370,18 +1370,9 @@ function handleMessage(ws, data) {
case 'station_register': case 'station_register':
handleStationRegister(ws, data); handleStationRegister(ws, data);
break; break;
case 'train_arrival':
handleTrainArrival(data);
break;
case 'train_departure':
handleTrainDeparture(data);
break;
case 'request_station_time': case 'request_station_time':
handleStationTimeRequest(ws, data); handleStationTimeRequest(ws, data);
break; break;
case 'play_sequence':
handlePlaySequence(ws, data);
break;
case 'request_list_txt': case 'request_list_txt':
// TODO: Send list.txt content // TODO: Send list.txt content
break; break;
@@ -1393,33 +1384,6 @@ function handleMessage(ws, data) {
} }
} }
function getDestinationFromSchedule(schedule) {
if (!schedule || !schedule.entries) return "Unknown";
const entries = schedule.entries;
let dest = "Unknown";
// Convert entries to array if it's an object with numeric keys
const entryList = Array.isArray(entries) ? entries : Object.values(entries);
// 1. Look for explicit destination instructions
for (const entry of entryList) {
if (entry.instruction?.id === 'create:destination') {
dest = entry.instruction.data?.text || entry.instruction.text || dest;
}
}
// 2. If still Unknown, find the very last station instruction
if (dest === "Unknown") {
for (let i = entryList.length - 1; i >= 0; i--) {
if (entryList[i].instruction?.id === 'create:station') {
dest = entryList[i].instruction.data?.text || entryList[i].instruction.text || dest;
break;
}
}
}
return dest;
}
function getCorrectStationName(name) { function getCorrectStationName(name) {
const cleanName = String(name || '').trim(); const cleanName = String(name || '').trim();
const station = findStationByFlexibleName(cleanName); const station = findStationByFlexibleName(cleanName);
@@ -1483,40 +1447,6 @@ function handleStationRegister(ws, data) {
broadcastUpdate(); broadcastUpdate();
} }
function handleTrainArrival(data) {
let { trainId, stationName, arrivalTime, destination, source, schedule } = data;
stationName = getCorrectStationName(stationName);
const baseId = getBaseTrainId(trainId);
const cooldownKey = `${baseId}|${stationName}|arrival`;
const lastEventTime = trainEventCooldown.get(cooldownKey);
if (lastEventTime && Date.now() - lastEventTime < 45000) return;
trainEventCooldown.set(cooldownKey, Date.now());
log('INFO', `Train ${trainId} (Consist ${baseId}) arrived at ${stationName}`);
// Add or update active train using base ID to group cars
const existingIndex = activeTrains.findIndex(t => getBaseTrainId(t.trainId) === baseId);
// Create history entry
let trainData = {
trainId,
currentStation: stationName,
destination: destination || (existingIndex >= 0 ? activeTrains[existingIndex].destination : "Unknown"),
status: "At Station",
lastUpdate: Date.now(),
schedule: schedule || (existingIndex >= 0 ? activeTrains[existingIndex].schedule : null)
};
if (existingIndex >= 0) {
activeTrains[existingIndex] = { ...activeTrains[existingIndex], ...trainData };
} else {
activeTrains.push(trainData);
}
broadcastUpdate();
}
function getBaseName(name) { function getBaseName(name) {
if (!name) return ""; if (!name) return "";
return String(name).toLowerCase() return String(name).toLowerCase()
@@ -1599,68 +1529,6 @@ function findStationByFlexibleName(targetName) {
return null; return null;
} }
function handleTrainDeparture(data) {
let { trainId, stationName, destination, schedule, source } = data;
stationName = getCorrectStationName(stationName);
const baseId = getBaseTrainId(trainId);
const cooldownKey = `${baseId}|${stationName}|departure`;
const lastEventTime = trainEventCooldown.get(cooldownKey);
if (lastEventTime && Date.now() - lastEventTime < 45000) return;
trainEventCooldown.set(cooldownKey, Date.now());
log('INFO', `Train ${trainId} (Consist ${baseId}) departed from ${stationName}`);
const existingIndex = activeTrains.findIndex(t => getBaseTrainId(t.trainId) === baseId);
if (existingIndex >= 0) {
const train = activeTrains[existingIndex];
train.status = "Departed";
train.currentStation = stationName;
train.lastUpdate = Date.now();
train.lastStation = stationName;
train.departureTime = Date.now();
train.destination = destination || train.destination;
train.schedule = schedule || train.schedule;
}
broadcastUpdate();
}
function handlePlaySequence(ws, data) {
const { stationName, seq, requestId } = data;
const cleanStation = stationName.trim();
const correctStation = getCorrectStationName(cleanStation);
const streamName = getStationStreamName(cleanStation);
const hlsUrl = getStationHlsUrl(streamName);
log('INFO', `Playing sequence for ${correctStation}: ${seq.join(', ')}`);
const builtAudio = buildSequenceWavFile(seq);
if (builtAudio) {
const priority = Number.isFinite(Number(data.priority))
? Number(data.priority)
: (String(data.category || '').toLowerCase() === 'station' ? 0 : 1);
const interruptMode = data.interruptMode || getInterruptModeForPriority(priority);
const source = data.source || data.category || 'sequence';
const audioId = String(requestId || `${Date.now()}-${Math.random().toString(16).slice(2)}`);
enqueueStationAudioTask(cleanStation, ws, {
audioFormat: builtAudio.audioFormat,
streamUrl: builtAudio.streamUrl,
durationSec: builtAudio.durationSec,
priority,
interruptMode,
source,
audioId,
requeueOnPreempt: priority <= 0,
consoleStreamStart: { type: 'stream_start', station: cleanStation, seq: seq, streamName, hlsUrl },
consoleStreamEnd: { type: 'stream_end', station: cleanStation }
});
} else {
log('WARN', `Unable to build WAV sequence for ${cleanStation}`);
}
}
function handleStationTimeRequest(ws, data) { function handleStationTimeRequest(ws, data) {
// Simple logic: default to 60s or lookup schedule // Simple logic: default to 60s or lookup schedule
const stationTime = 60; const stationTime = 60;
+1 -175
View File
@@ -38,15 +38,11 @@ local State = {
currentTrain = nil, currentTrain = nil,
logs = {}, logs = {},
logScrollOffset = 0, logScrollOffset = 0,
departingPlayed = false,
arrivalTonePlayed = false,
shortStopMode = false,
audioRx = nil, audioRx = nil,
audioQueue = {}, audioQueue = {},
currentPlaying = nil, currentPlaying = nil,
currentPriority = -1, currentPriority = -1,
interruptSeq = 0, interruptSeq = 0
lastArrivalPlayAt = 0
} }
-- ============================================================================ -- ============================================================================
@@ -952,133 +948,11 @@ function Utils.getStationAudio(code, langPrefix)
return langPrefix .. code return langPrefix .. code
end end
function Audio.announceArrival(trainId, trainType, platform, dest, useAltJz3)
local seq = {}
Utils.log("DEBUG", string.format("Announcing Arrival: %s, Type: %s, Plat: %s, Dest: %s",
tostring(trainId), tostring(trainType), tostring(platform), tostring(dest)))
table.insert(seq, "ksts") -- Chime
table.insert(seq, "__DELAY_1__")
local destCode = Utils.getStationCode(dest)
if (not destCode or destCode == "unknown") and dest ~= "Unknown" then
-- Fallback to broadcast code if regular code failed
destCode = Utils.getBroadcastStationCode(dest)
Utils.log("DEBUG", "Station code fallback for " .. tostring(dest) .. ": " .. tostring(destCode))
end
Utils.log("DEBUG", "Final destination code for " .. tostring(dest) .. ": " .. tostring(destCode))
local chJz3 = useAltJz3 and "ch-jz3-1" or "ch-jz3"
local enJz3 = useAltJz3 and "en-jz3-1" or "en-jz3"
-- Chinese
table.insert(seq, "ch-jz1")
if destCode then
table.insert(seq, Utils.getStationAudio(destCode, "ch-"))
end
table.insert(seq, "ch-jz2")
table.insert(seq, trainType == "express" and "ch-tj" or "ch-pt")
table.insert(seq, chJz3)
-- Platform Number (Chinese)
local pAudio = Utils.getChineseNumberAudio(platform)
for _, a in ipairs(pAudio) do table.insert(seq, a) end
table.insert(seq, "ch-zt")
table.insert(seq, "ch-jz4")
-- English
table.insert(seq, "en-jz1")
table.insert(seq, trainType == "express" and "en-tj" or "en-pt")
table.insert(seq, "en-jz2")
if destCode then
table.insert(seq, Utils.getStationAudio(destCode, "en-"))
end
table.insert(seq, enJz3)
table.insert(seq, "en-zt")
-- English Platform Number
local pDigit, pLetter = tostring(platform):match("^(%d+)([a-zA-Z])$")
if pDigit and pLetter then
table.insert(seq, "en-" .. pDigit)
table.insert(seq, pLetter:upper())
else
table.insert(seq, "en-" .. platform)
end
table.insert(seq, "en-jz4")
Audio.play(seq)
end
function Audio.announceDeparture(trainId, trainType, platform, dest, time)
local seq = {}
table.insert(seq, "fcts")
table.insert(seq, "__DELAY_1__")
local destCode = Utils.getStationCode(dest)
-- Chinese
-- Platform Number
local pAudio = Utils.getChineseNumberAudio(platform)
for _, a in ipairs(pAudio) do table.insert(seq, a) end
table.insert(seq, "ch-zt")
table.insert(seq, "ch-fc1")
table.insert(seq, trainType == "express" and "ch-tj" or "ch-pt")
table.insert(seq, "ch-fc2")
if destCode then
table.insert(seq, Utils.getStationAudio(destCode, "ch-"))
end
table.insert(seq, "ch-fc3")
-- English
table.insert(seq, "en-fc1")
table.insert(seq, trainType == "express" and "en-tj" or "en-pt")
table.insert(seq, "en-fc2")
table.insert(seq, "en-zt")
-- English Platform Number
local pDigit, pLetter = tostring(platform):match("^(%d+)([a-zA-Z])$")
if pDigit and pLetter then
table.insert(seq, "en-" .. pDigit)
table.insert(seq, pLetter:upper())
else
table.insert(seq, "en-" .. platform)
end
table.insert(seq, "en-fc3")
if destCode then
table.insert(seq, Utils.getStationAudio(destCode, "en-"))
end
table.insert(seq, "en-fc4")
Audio.play(seq)
end
-- ============================================================================ -- ============================================================================
-- 6. Network System -- 6. Network System
-- ============================================================================ -- ============================================================================
local Network = {} local Network = {}
function Audio.play(files)
if not State.ws then
Utils.log("WARN", "Cannot play audio: Not connected to server")
return
end
Utils.log("INFO", "Requesting server to play sequence...")
Network.send({
type = "play_sequence",
stationName = State.stationName,
seq = files,
category = "train",
priority = 1,
requestId = tostring(os.epoch("utc")) .. "-" .. tostring(math.random(100000, 999999))
})
end
function Network.connect() function Network.connect()
if State.ws then return end if State.ws then return end
Utils.log("INFO", "Connecting to cloud...") Utils.log("INFO", "Connecting to cloud...")
@@ -1526,58 +1400,10 @@ function Main.loop()
departureTime = now + stationTime, departureTime = now + stationTime,
platform = Utils.getPlatformNumber(State.stationName, "1") platform = Utils.getPlatformNumber(State.stationName, "1")
} }
Network.send({
type = "train_arrival",
trainId = id,
stationName = State.stationName,
destination = dest
})
if not State.arrivalTonePlayed then
if stationTime < 50 then
Utils.log("INFO", "Short stop (<50s) and no pre-arrival. Skipping arrival announcement.")
State.shortStopMode = true
else
Utils.log("INFO", "Train arrived. Playing arrival announcement (alt).")
Audio.announceArrival(State.currentTrain.id, State.currentTrain.trainType, State.currentTrain.platform, State.currentTrain.dest, true)
end
State.arrivalTonePlayed = true
end
else
-- Train currently at station
local now = os.epoch("utc") / 1000
local remaining = State.currentTrain.departureTime - now
if State.shortStopMode then
-- Play fcts.wav only in the last 10 seconds
if remaining <= 10 and not State.departingPlayed then
Utils.log("INFO", "Short stop mode: playing fcts.wav only.")
Audio.play({"fcts"})
State.departingPlayed = true
end
else
if remaining <= 50 and not State.departingPlayed then
Audio.announceDeparture(State.currentTrain.id, State.currentTrain.trainType, State.currentTrain.platform, State.currentTrain.dest, 60)
State.departingPlayed = true
end
end
end end
else else
if State.currentTrain then if State.currentTrain then
-- Train just left
Network.send({
type = "train_departure",
trainId = State.currentTrain.id,
stationName = State.stationName,
destination = State.currentTrain.dest,
schedule = State.currentTrain.schedule,
stationTime = math.floor((os.epoch("utc") / 1000) - State.currentTrain.arrivalTime)
})
State.currentTrain = nil State.currentTrain = nil
State.arrivalTonePlayed = false
State.departingPlayed = false
State.shortStopMode = false
end end
end end