420 lines
18 KiB
Lua
420 lines
18 KiB
Lua
--[[ Copyright (c) 2017 Optera
|
|
* Part of Logistics Train Network
|
|
*
|
|
* See LICENSE.md in the project directory for license information.
|
|
--]]
|
|
|
|
|
|
-- update stop output when train enters stop
|
|
function TrainArrives(train)
|
|
local stopID = train.station.unit_number
|
|
local stop = global.LogisticTrainStops[stopID]
|
|
if stop then
|
|
local stop_name = stop.entity.backer_name
|
|
-- assign main loco name and force
|
|
local loco = Get_Main_Locomotive(train)
|
|
local trainForce = nil
|
|
local trainName = nil
|
|
if loco then
|
|
trainName = loco.backer_name
|
|
trainForce = loco.force
|
|
end
|
|
|
|
-- add train to global.StoppedTrains
|
|
global.StoppedTrains[train.id] = {
|
|
train = train,
|
|
name = trainName,
|
|
force = trainForce,
|
|
stopID = stopID,
|
|
}
|
|
|
|
-- add train to global.LogisticTrainStops
|
|
stop.parked_train = train
|
|
stop.parked_train_id = train.id
|
|
|
|
local frontDistance = Get_Distance(train.front_stock.position, train.station.position)
|
|
local backDistance = Get_Distance(train.back_stock.position, train.station.position)
|
|
if frontDistance > backDistance then
|
|
stop.parked_train_faces_stop = false
|
|
else
|
|
stop.parked_train_faces_stop = true
|
|
end
|
|
local is_provider = false
|
|
|
|
-- if message_level >= 3 then printmsg({"ltn-message.train-arrived", tostring(trainName), stop_name}, trainForce, false) end
|
|
if message_level >= 3 then printmsg({"ltn-message.train-arrived", Make_Train_RichText(train, nil), format("[train-stop=%d]", stopID)}, trainForce, false) end
|
|
if debug_log then log(format("(TrainArrives) Train [%d] \"%s\": arrived at LTN-stop [%d] \"%s\"; train_faces_stop: %s", train.id, trainName, stopID, stop_name, stop.parked_train_faces_stop )) end
|
|
|
|
if stop.error_code == 0 then
|
|
if stop.is_depot then
|
|
local delivery = global.Dispatcher.Deliveries[train.id]
|
|
if delivery then
|
|
-- delivery should have been removed when leaving requester. Handle like delivery timeout.
|
|
if message_level >= 1 then
|
|
printmsg({
|
|
"ltn-message.delivery-removed-depot",
|
|
Make_Stop_RichText(from_entity) or delivery.from,
|
|
Make_Stop_RichText(to_entity) or delivery.to
|
|
}, delivery.force, false)
|
|
end
|
|
if debug_log then log(format("(TrainArrives) Train [%d] \"%s\": Entered Depot with active Delivery. Failing Delivery and reseting train.", train.id, trainName)) end
|
|
script.raise_event(on_delivery_failed_event, {train_id = train.id, shipment = delivery.shipment})
|
|
RemoveDelivery(train.id)
|
|
end
|
|
|
|
-- clean fluid residue
|
|
local train_items = train.get_contents()
|
|
local train_fluids = train.get_fluid_contents()
|
|
if table_size(train_fluids) > 0 and depot_fluid_cleaning > 0 then
|
|
-- cleaning per wagon
|
|
for i, wagon in pairs(train.fluid_wagons) do
|
|
for fluid, count in pairs(wagon.get_fluid_contents()) do
|
|
if count <= depot_fluid_cleaning then
|
|
local removed = wagon.remove_fluid({name=fluid, amount=count})
|
|
if debug_log then log(format("(TrainArrives) Train \"%s\"[%d]: Depot fluid removal %s %f/%f", trainName, i, fluid, removed, count)) end
|
|
end
|
|
end
|
|
end
|
|
-- cleaning whole train doesn't work in 1.1.26
|
|
-- for fluid, count in pairs(train_fluids) do
|
|
-- if count <= depot_fluid_cleaning then
|
|
-- local removed = train.remove_fluid({name=fluid, amount=count})
|
|
-- log(format("Train %s: removed %s %f/%f", trainName, fluid, removed, count))
|
|
-- end
|
|
-- end
|
|
train_fluids = train.get_fluid_contents()
|
|
end
|
|
|
|
-- check for leftover cargo
|
|
if table_size(train_items) > 0 then
|
|
create_alert(stop.entity, "cargo-warning", {"ltn-message.depot_left_over_cargo", trainName, stop_name}, trainForce)
|
|
end
|
|
if table_size(train_fluids) > 0 then
|
|
create_alert(stop.entity, "cargo-warning", {"ltn-message.depot_left_over_cargo", trainName, stop_name}, trainForce)
|
|
end
|
|
|
|
-- make train available for new deliveries
|
|
local capacity, fluid_capacity = GetTrainCapacity(train)
|
|
global.Dispatcher.availableTrains[train.id] = {
|
|
train = train,
|
|
surface = loco.surface,
|
|
force = trainForce,
|
|
depot_priority = stop.depot_priority,
|
|
network_id = stop.network_id,
|
|
capacity = capacity,
|
|
fluid_capacity = fluid_capacity
|
|
}
|
|
global.Dispatcher.availableTrains_total_capacity = global.Dispatcher.availableTrains_total_capacity + capacity
|
|
global.Dispatcher.availableTrains_total_fluid_capacity = global.Dispatcher.availableTrains_total_fluid_capacity + fluid_capacity
|
|
-- log("added available train "..train.id..", inventory: "..tostring(global.Dispatcher.availableTrains[train.id].capacity)..", fluid capacity: "..tostring(global.Dispatcher.availableTrains[train.id].fluid_capacity))
|
|
|
|
-- reset schedule
|
|
local schedule = {current = 1, records = {}}
|
|
schedule.records[1] = NewScheduleRecord(stop_name, "inactivity", depot_inactivity)
|
|
train.schedule = schedule
|
|
|
|
-- reset filters and bars
|
|
if depot_reset_filters and train.cargo_wagons then
|
|
for n,wagon in pairs(train.cargo_wagons) do
|
|
local inventory = wagon.get_inventory(defines.inventory.cargo_wagon)
|
|
if inventory then
|
|
if inventory.is_filtered() then
|
|
-- log("Cargo-Wagon["..tostring(n).."]: reseting "..tostring(#inventory).." filtered slots.")
|
|
for slotIndex=1, #inventory, 1 do
|
|
inventory.set_filter(slotIndex, nil)
|
|
end
|
|
end
|
|
if inventory.supports_bar and #inventory - inventory.get_bar() > 0 then
|
|
-- log("Cargo-Wagon["..tostring(n).."]: reseting "..tostring(#inventory - inventory.get_bar()).." locked slots.")
|
|
inventory.set_bar()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
setLamp(stop, "blue", 1)
|
|
|
|
else -- stop is no Depot
|
|
-- check requester for incorrect shipment
|
|
local delivery = global.Dispatcher.Deliveries[train.id]
|
|
if delivery then
|
|
is_provider = delivery.from_id == stop.entity.unit_number
|
|
if delivery.to_id == stop.entity.unit_number then
|
|
local requester_unscheduled_cargo = false
|
|
local unscheduled_load = {}
|
|
local train_items = train.get_contents()
|
|
for name, count in pairs(train_items) do
|
|
local typed_name = "item,"..name
|
|
if not delivery.shipment[typed_name] then
|
|
requester_unscheduled_cargo = true
|
|
unscheduled_load[typed_name] = count
|
|
end
|
|
end
|
|
local train_fluids = train.get_fluid_contents()
|
|
for name, count in pairs(train_fluids) do
|
|
local typed_name = "fluid,"..name
|
|
if not delivery.shipment[typed_name] then
|
|
requester_unscheduled_cargo = true
|
|
unscheduled_load[typed_name] = count
|
|
end
|
|
end
|
|
if requester_unscheduled_cargo then
|
|
create_alert(stop.entity, "cargo-alert", {"ltn-message.requester_unscheduled_cargo", trainName, stop_name}, trainForce)
|
|
script.raise_event(on_requester_unscheduled_cargo_alert, {train = train, station = stop.entity, planned_shipment = delivery.shipment, unscheduled_load = unscheduled_load})
|
|
end
|
|
end
|
|
end
|
|
|
|
-- set lamp to blue for LTN controlled trains
|
|
for i=1, #stop.active_deliveries, 1 do
|
|
if stop.active_deliveries[i] == train.id then
|
|
setLamp(stop, "blue", #stop.active_deliveries)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
UpdateStopOutput(stop, is_provider and not(provider_show_existing_cargo) )
|
|
end
|
|
end
|
|
|
|
-- update stop output when train leaves stop
|
|
-- when called from on_train_created stoppedTrain.train will be invalid
|
|
function TrainLeaves(trainID)
|
|
local stoppedTrain = global.StoppedTrains[trainID] -- checked before every call of TrainLeaves
|
|
local train = stoppedTrain.train
|
|
local stopID = stoppedTrain.stopID
|
|
local stop = global.LogisticTrainStops[stopID]
|
|
if not stop then
|
|
if debug_log then log(format("(TrainLeaves) Error: StopID [%d] not found in global.LogisticTrainStops", stopID )) end
|
|
global.StoppedTrains[trainID] = nil
|
|
return
|
|
end
|
|
if not stop.entity.valid or not stop.input.valid or not stop.output.valid or not stop.lamp_control.valid then
|
|
if debug_log then log(format("(TrainLeaves) Error: StopID [%d] contains invalid entity. Processing skipped, train inventory not updated.", stopID )) end
|
|
global.StoppedTrains[trainID] = nil
|
|
-- don't call RemoveStop here as RemoveStop calls TrainLeaves again
|
|
return
|
|
end
|
|
local stop_name = stop.entity.backer_name
|
|
|
|
-- train was stopped at LTN depot
|
|
if stop.is_depot then
|
|
if global.Dispatcher.availableTrains[trainID] then -- trains are normally removed when deliveries are created
|
|
global.Dispatcher.availableTrains_total_capacity = global.Dispatcher.availableTrains_total_capacity - global.Dispatcher.availableTrains[trainID].capacity
|
|
global.Dispatcher.availableTrains_total_fluid_capacity = global.Dispatcher.availableTrains_total_fluid_capacity - global.Dispatcher.availableTrains[trainID].fluid_capacity
|
|
global.Dispatcher.availableTrains[trainID] = nil
|
|
end
|
|
if stop.error_code == 0 then
|
|
setLamp(stop, "green", 1)
|
|
end
|
|
if debug_log then log(format("(TrainLeaves) Train [%d] \"%s\": left Depot [%d] \"%s\".", trainID, stoppedTrain.name, stopID, stop.entity.backer_name )) end
|
|
|
|
-- train was stopped at LTN stop
|
|
else
|
|
-- remove delivery from stop
|
|
for i=#stop.active_deliveries, 1, -1 do
|
|
if stop.active_deliveries[i] == trainID then
|
|
table.remove(stop.active_deliveries, i)
|
|
end
|
|
end
|
|
|
|
local delivery = global.Dispatcher.Deliveries[trainID]
|
|
if train.valid and delivery then
|
|
if delivery.from_id == stop.entity.unit_number then
|
|
-- update delivery counts to train inventory
|
|
local actual_load = {}
|
|
local unscheduled_load = {}
|
|
local provider_unscheduled_cargo = false
|
|
local provider_missing_cargo = false
|
|
local train_items = train.get_contents()
|
|
for name, count in pairs(train_items) do
|
|
local typed_name = "item,"..name
|
|
local planned_count = delivery.shipment[typed_name]
|
|
if planned_count then
|
|
actual_load[typed_name] = count -- update shipment to actual inventory
|
|
if count < planned_count then
|
|
-- underloaded
|
|
provider_missing_cargo = true
|
|
end
|
|
else
|
|
-- loaded wrong items
|
|
unscheduled_load[typed_name] = count
|
|
provider_unscheduled_cargo = true
|
|
end
|
|
end
|
|
local train_fluids = train.get_fluid_contents()
|
|
for name, count in pairs(train_fluids) do
|
|
local typed_name = "fluid,"..name
|
|
local planned_count = delivery.shipment[typed_name]
|
|
if planned_count then
|
|
actual_load[typed_name] = count -- update shipment actual inventory
|
|
if planned_count-count > 0.1 then -- prevent lsb errors
|
|
-- underloaded
|
|
provider_missing_cargo = true
|
|
end
|
|
else
|
|
-- loaded wrong fluids
|
|
unscheduled_load[typed_name] = count
|
|
provider_unscheduled_cargo = true
|
|
end
|
|
end
|
|
delivery.pickupDone = true -- remove reservations from this delivery
|
|
if debug_log then log(format("(TrainLeaves) Train [%d] \"%s\": left Provider [%d] \"%s\"; cargo: %s; unscheduled: %s ", trainID, stoppedTrain.name, stopID, stop.entity.backer_name, serpent.line(actual_load), serpent.line(unscheduled_load) )) end
|
|
global.StoppedTrains[trainID] = nil
|
|
|
|
if provider_missing_cargo then
|
|
create_alert(stop.entity, "cargo-alert", {"ltn-message.provider_missing_cargo", stoppedTrain.name, stop_name}, stoppedTrain.force)
|
|
script.raise_event(on_provider_missing_cargo_alert, {
|
|
train = train,
|
|
station = stop.entity,
|
|
planned_shipment = delivery.shipment,
|
|
actual_shipment = actual_load
|
|
})
|
|
end
|
|
if provider_unscheduled_cargo then
|
|
create_alert(stop.entity, "cargo-alert", {"ltn-message.provider_unscheduled_cargo", stoppedTrain.name, stop_name}, stoppedTrain.force)
|
|
script.raise_event(on_provider_unscheduled_cargo_alert, {
|
|
train = train,
|
|
station = stop.entity,
|
|
planned_shipment = delivery.shipment,
|
|
unscheduled_load = unscheduled_load
|
|
})
|
|
end
|
|
script.raise_event(on_delivery_pickup_complete_event, {
|
|
train_id = trainID,
|
|
train = train,
|
|
planned_shipment = delivery.shipment,
|
|
actual_shipment = actual_load
|
|
})
|
|
delivery.shipment = actual_load
|
|
|
|
elseif delivery.to_id == stop.entity.unit_number then
|
|
-- reset schedule before API events
|
|
if requester_delivery_reset and train.schedule then
|
|
local schedule = {current = 1, records = {}}
|
|
schedule.records[1] = NewScheduleRecord(train.schedule.records[1].station, "inactivity", depot_inactivity)
|
|
train.schedule = schedule
|
|
end
|
|
|
|
local remaining_load = {}
|
|
local requester_left_over_cargo = false
|
|
local train_items = train.get_contents()
|
|
for name, count in pairs(train_items) do
|
|
-- not fully unloaded
|
|
local typed_name = "item,"..name
|
|
requester_left_over_cargo = true
|
|
remaining_load[typed_name] = count
|
|
end
|
|
local train_fluids = train.get_fluid_contents()
|
|
for name, count in pairs(train_fluids) do
|
|
-- not fully unloaded
|
|
local typed_name = "fluid,"..name
|
|
requester_left_over_cargo = true
|
|
remaining_load[typed_name] = count
|
|
end
|
|
|
|
if debug_log then log(format("(TrainLeaves) Train [%d] \"%s\": left Requester [%d] \"%s\" with left over cargo: %s", trainID, stoppedTrain.name, stopID, stop.entity.backer_name, serpent.line(remaining_load))) end
|
|
-- signal completed delivery and remove it
|
|
if requester_left_over_cargo then
|
|
create_alert(stop.entity, "cargo-alert", {"ltn-message.requester_left_over_cargo", stoppedTrain.name, stop_name}, stoppedTrain.force)
|
|
script.raise_event(on_requester_remaining_cargo_alert, {
|
|
train = train,
|
|
station = stop.entity,
|
|
remaining_load = remaining_load
|
|
})
|
|
end
|
|
script.raise_event(on_delivery_completed_event, {
|
|
train_id = trainID,
|
|
train = train,
|
|
shipment = delivery.shipment})
|
|
RemoveDelivery(trainID)
|
|
else
|
|
if debug_log then log(format("(TrainLeaves) Train [%d] \"%s\": left LTN-stop [%d] \"%s\".", trainID, stoppedTrain.name, stopID, stop.entity.backer_name)) end
|
|
end
|
|
end
|
|
if stop.error_code == 0 then
|
|
if #stop.active_deliveries > 0 then
|
|
setLamp(stop, "yellow", #stop.active_deliveries)
|
|
else
|
|
setLamp(stop, "green", 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- remove train reference
|
|
stop.parked_train = nil
|
|
stop.parked_train_id = nil
|
|
-- if message_level >= 3 then printmsg({"ltn-message.train-left", tostring(stoppedTrain.name), stop.entity.backer_name}, stoppedTrain.force) end
|
|
if message_level >= 3 then printmsg({"ltn-message.train-left", Make_Train_RichText(train, stoppedTrain.name), format("[train-stop=%d]", stopID)}, stoppedTrain.force, false) end
|
|
UpdateStopOutput(stop)
|
|
|
|
global.StoppedTrains[trainID] = nil
|
|
end
|
|
|
|
-- local reverse_defines = require('__flib__.reverse-defines')
|
|
|
|
function OnTrainStateChanged(event)
|
|
-- log(game.tick.." (OnTrainStateChanged) Train name: "..tostring(Get_Train_Name(event.train))..", train.id:"..tostring(event.train.id).." stop: "..tostring(event.train.station and event.train.station.backer_name)..", state: "..reverse_defines.train_state[event.old_state].." > "..reverse_defines.train_state[event.train.state] )
|
|
local train = event.train
|
|
if train.state == defines.train_state.wait_station and train.station ~= nil and ltn_stop_entity_names[train.station.name] then
|
|
TrainArrives(train)
|
|
elseif event.old_state == defines.train_state.wait_station and global.StoppedTrains[train.id] then -- update to 0.16
|
|
TrainLeaves(train.id)
|
|
end
|
|
end
|
|
|
|
-- updates or removes delivery references
|
|
function Update_Delivery(old_train_id, new_train)
|
|
local delivery = global.Dispatcher.Deliveries[old_train_id]
|
|
|
|
-- expanded RemoveDelivery(old_train_id) to also update
|
|
for stopID, stop in pairs(global.LogisticTrainStops) do
|
|
if not stop.entity.valid or not stop.input.valid or not stop.output.valid or not stop.lamp_control.valid then
|
|
RemoveStop(stopID)
|
|
else
|
|
for i=#stop.active_deliveries, 1, -1 do --trainID should be unique => checking matching stop name not required
|
|
if stop.active_deliveries[i] == old_train_id then
|
|
if delivery then
|
|
stop.active_deliveries[i] = new_train.id -- update train id if delivery exists
|
|
else
|
|
table.remove(stop.active_deliveries, i) -- otherwise remove entry
|
|
if #stop.active_deliveries > 0 then
|
|
setLamp(stop, "yellow", #stop.active_deliveries)
|
|
else
|
|
setLamp(stop, "green", 1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- copy global.Dispatcher.Deliveries[old_train_id] to new_train.id and change attached train in delivery
|
|
if delivery then
|
|
delivery.train = new_train
|
|
global.Dispatcher.Deliveries[new_train.id] = delivery
|
|
end
|
|
|
|
if global.StoppedTrains[old_train_id] then
|
|
TrainLeaves(old_train_id) -- removal only, new train is added when on_train_state_changed fires with wait_station afterwards
|
|
end
|
|
global.Dispatcher.Deliveries[old_train_id] = nil
|
|
|
|
return delivery
|
|
end
|
|
|
|
function OnTrainCreated(event)
|
|
-- log("(on_train_created) Train name: "..tostring(Get_Train_Name(event.train))..", train.id:"..tostring(event.train.id)..", .old_train_id_1:"..tostring(event.old_train_id_1)..", .old_train_id_2:"..tostring(event.old_train_id_2)..", state: "..tostring(event.train.state))
|
|
-- on_train_created always sets train.state to 9 manual, scripts have to set the train back to its former state.
|
|
|
|
if event.old_train_id_1 then
|
|
Update_Delivery(event.old_train_id_1, event.train)
|
|
end
|
|
|
|
if event.old_train_id_2 then
|
|
Update_Delivery(event.old_train_id_2, event.train)
|
|
end
|
|
end
|