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:
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user