LTN - Logistic Train Network - Manual

Adds new train stops forming a highly configurable logistic network.

Moderator: Optera

User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2920
Joined: Sat Jun 11, 2016 6:41 am
Contact:

LTN - Logistic Train Network - Manual

Post by Optera »

Description:
Adds logistic-train-stops acting as anchor points for building a fully automated, train logistic network.
It can handle all possible train configuration. Just send all trains to depots and LTN will pick the best suitable train for a job.
LTN cuts the amount of rolling stock required to run a megabase down to 30% or less.

License: private use only, no modification, no redistibution, no commercial use
Source: GitHub
Download: Mod Portal
Changelog Mod Portal

Demo Map:
LTN Demo 1.9.10.zip
Demo Map for LTN 1.9.10 - requires no mods other than LTN
(3.23 MiB) Downloaded 6196 times
LTN Demo 1.11.0.zip
Demo Map for LTN 1.11.0 - requires no mods other than LTN
(2.25 MiB) Downloaded 2459 times
LTN Demo 1.13.x.zip
Demo Map for LTN 1.13.0 - requires no mods other than LTN
(2.32 MiB) Downloaded 8077 times
Small map running 5 trains in nearly realistic fashion. Showcasing station designs from simplistic 1 combinator to depots using train compositions and a truly universal provider/requester station.

Designs for stations, depots, control circuits and more can be found in the Design thread.

Video Tutorials:
https://youtu.be/U-TXRNQzL-U
Basic Tutorial by Nilaus. A little aged and long winding, still mostly correct.

https://youtu.be/a3ujEdPfGHk
Basic Tutorial by Tuplex. He quickly goes into filtered providers station setup, sadly not filtered requester.

https://youtu.be/ZtcKlNous9o
Tutorial by Diablo, going more into mod settings and signals.
Note: Some things in this are wrong, see Kyoko's post for a corrected information.

Long Description:
Inspired by ideas in this thread thread I sat down and wrote this as proof of concept.
Big thanks to Choumiko. Seeing how certain things work in Smarter Trains and Rail Tanker made writing this mod a whole lot easier.

This mod adds one new train stop "logistic-train-stop" acting as anchor points for building a train powered logistic network.
Stops consist of 3 entities, the stop itself and the automatic placed lamp (input) and yellow constant combinator (output).
Stop Entities
Stop Entities
2016-11-20-11-52-03-3807521.jpg (41.26 KiB) Viewed 617082 times

Stop: (behaves like base stop)
  • read from train enable by default
  • send to train enabled by default

Constant Combinator: (signal output)
  • train composition: position of wagons & locomotives in parked train (all stops)
    binary encoded signal of train composition up to 31 carriages with lsb at the train stop. More information about binary encoding here
  • Expected train inventory after un-/loading is complete. (only Requester & Provider)

Lamp: (color coded status)
Values can only be read through green wire.
  • green = 1: normal status
  • blue = n: LTN Controlled Train parked at stop, n = number of trains
  • yellow = n: Stop is part of a scheduled delivery, n = number of trains
  • white = 1: Error - not initialized
  • red = 1: Error - short circuit
  • red = 2: Error - deactivated stop
  • pink = 1: Error - duplicate station name (deprecated in 0.13.0)
Lamp: (signal input)

All Stops:
Image Encoded Network ID - Binary coded signal assigning a stop to specific networks. (defaults to 0xFFFFFFFF = all networks)
Deliveries are only generated when between Depot, Provider, Requester within the same network. Stops can be assigned to multiple networks e.g. 6 assigns a stop to networks 2 and 4.

Depot: (ignores Requester & Provider signals)
Image Stop is Depot - flags this stop as depot (default=0 false)
Image Depot Priority (optional) - higher priority will be served first (default=0)

Requester & Provider:
Image provided items (positive number) and requested items (negative number)
Image minimum train length (optional) - minimum length of locos + wagon allowed in this station (default=0 no limit)
Image maximum train length (optional) - maximum length of locos + wagon allowed in this station (default=0 no limit)
Image limit trains (optional) - number of trains (deliveries) allowed simultaneously at this station (default=0 no limit)

Requester:
Image Request Threshold (optional) - Missing amount of items/fluids triggering a delivery. (global default=1000)
Image Request Stack Threshold (optional) - Missing amount of item stacks triggering a delivery. Takes priority over Request Threshold.
Image Request Priority (optional) - higher priority will be served first (default=0)
Image Disable Warnings (optional) - Suppresses warnings, like no station supplying x found, for this Requester. (default=0 false)

Provider:
Image Provide Threshold (optional) - Amount of items/fluids required to act as provider. (global default=1000)
Image Provide Stack Threshold (optional) - Amount of item stacks required to act as provider. Takes priority over Provide Threshold.
Image Provider Priority (optional) - higher priority will be used first (default=0)
Image Locked Slots per Wagon (optional) - number of inventory slots per wagon locked for this provider (default=0 false)

Thresholds:
Odhrean made this graph depicting what delivery size is used depending on thresholds and train size in a setup with 1 requester and 1 provider using L-4C trains.
LTN_Request_Provide_Threshold.png
LTN_Request_Provide_Threshold.png (54.34 KiB) Viewed 562342 times
Mod Settings:
Message level
Detail level of in game messages.
0: Off No messages will be generated.
1: Errors & Warnings Print only errors and warnings.
2: Notifications (default) Print basic information like missing resources or generating deliveries.
3: Detailed Messages Print detailed information about finding providers and trains.

Message filter timeout (ticks)
Message age in ticks before filtered messages are shown again. default = 18000

Enable debug log
Write debug information to /Factorio/factorio-current.log.

Dispatcher Enabled
Deactivating Dispatcher stops delivery generation. Item levels will still be monitored.

Update frequency (ticks)
How fast stops and requests are updated. Higher numbers improve performance by spreading out updates over more ticks. When set > 1 forces Updates per tick to 1.

Updates per tick
Limits the number of stops and requests updated per tick. Lower numbers increase performance. Ignored when Update frequency > 1.

Request Threshold
Missing amount of items/fluids triggering a delivery. Can be overridden with signal at requesting stops. default = 1000

Provide Threshold
Amount of items/fluids required to act as provider. Can be overridden with signal at providing stops. default = 1000

Schedule circuit conditions
Adds circuit conditions to wait for red = 0 OR green ≥ 1 to all stops. Warning: All LTN stops require having "send to train" enabled and a circuit connection. Otherwise trains will be stuck waiting forever. default = false

Depot inactivity (sec)
Duration in seconds of inactivity before trains leave the depot. default = 5

Stop timeout (sec)
Duration in seconds before trains are forced out of a station. 0 deactivates this feature. default = 120

Delivery timeout (sec)
Duration in seconds deliveries can take before assuming the train was lost. default = 600 (10min)

Delivery completes at requester
False: (default) Delivery and schedule are reset when train arrives at depot. Changes to trains parked at requesting stops have no effect.
True: Delivery and schedule are reset when train leaves requester. Changes to trains parked at requesting stops will remove delivery and reset schedule.

Finish loading
True: (default) Prevents trains from leaving while inserters/pumps are working by adding 2s inactivity condition.
False: Trains will leave immediately when all items have been loaded. Inserters at loading stations will get stuck.

Depots reset filters
True: (default) Cargo wagons have their filters and stack limitations cleared when entering a depot.

Depot fluid removal limit
Maximum amount of fluid per wagon automatically destroyed when entering depots. default = 0 (disabled)

Default network ID
Network ID used for stops without "Encoded Network ID" signal. default = -1 (any)

Providers output existing cargo
Cargo inside train when arriving at provider is shown in LTN Output. Includes items from inserters holding items on tracks. default = true
User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2920
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Additional information

Post by Optera »

Planned features:
Will potentially be changed/added soon™ in no particular order.

Rejected features:
Reoccurring requests that either can't be implemented or don't align with my vision for LTN.
  • limit trains by composition signal #93
    Can't be done. Composition signal is ambiguous outside a specific stop.
  • Dispatch new order at request station viewtopic.php?f=214&t=51770
    Could be done with annoying limitations and a lot of overhead. Details explained in the topic.
  • Button - Return all Trains to Depot viewtopic.php?f=214&t=53195
    Can't be done safely without API exposure of the pathfinder.
  • Relative Thresholds viewtopic.php?f=214&t=61852
    Would be nice to have, just not feasible to implement.
  • Use base train limits
    I don't plan on rewriting the entire core logic of LTN at this point in time.
  • merge stops to one logical unit
    I don't plan on rewriting the entire core logic of LTN at this point in time.
Incompatible Mods:

FAQ / Known Issues:
  • Trains not running deliveries
    1. Check provide and request threshold, if none is set global setting is used
    2. Check if train size is plausible between requester and provider
    3. Check if Network ID is plausible between requester, provider and depot
  • Changing train composition makes trains switch to manual mode.
    Workaround for this behavior. After you're done rearranging trains send them back to the depot in automatic mode to register with LTN again.
  • updating mods using mod settings breaks settings. viewtopic.php?f=28&t=45308#p261686
    manually revert settings to default after updating
  • controlling pumps with circuit network for unloading will make them stop early
    Support stop output should round up fluids
    Meanwhile don't use Stop output fluid values to control pumps for unloading. Use Inventory Sensor instead to have fluids always rounded up.
  • trains longer than 31 carriages don't display train composition signals
    Nothing I can do about that, the circuit network is signed int and can only display 31 bits.
  • LTN uses a lot of update time in megabases
    Increase update interval in mod settings. At an update interval of 5 I'm serving over 500 stations at only 0.05ms update time.
    Warning: Increasing update interval too much will result in requests being created faster than LTN is able to generate deliveries. Check if open deliveries accumulate with LTN Tracker when changing this setting.
  • Inserters holding items will insert before LTN updates expected cargo. viewtopic.php?f=214&t=90090
    Can be mitigated by disabling Providers output existing cargo
  • Undo doesn't restore circuit connections
    Scripts have API access to undo queue: viewtopic.php?f=28&t=70393
User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2920
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Remote Interface

Post by Optera »

Interface Usage:
register to event in on_init and on_load:

Code: Select all

local on_dispatcher_updated_event = remote.call("logistic-train-network", "on_dispatcher_updated")
script.on_event(on_dispatcher_updated_event, dispatcher_updated_handler)
consume event handler like base game events:

Code: Select all

local function dispatcher_updated_handler(event)
  -- print all event properties
  log(serpent.block(event))
end
Interface - Demo code:
https://github.com/0ptera/Logistic-Trai ... ontrol.lua
Interface - Event data structure 1.18.0:
on_stops_updated
Raised every UpdateInterval, after delivery generation

Contains:
event.logistic_train_stops = { [stop_id], {
-- stop data
active_deliveries,
entity,
input,
output,
lamp_control,
error_code,

-- control signals
is_depot,
depot_priority,
network_id,
max_carriages,
min_carriages,
max_trains,
providing_threshold,
providing_threshold_stacks,
provider_priority,
requesting_threshold,
requesting_threshold_stacks,
requester_priority,
locked_slots,
no_warnings,

-- parked train data
parked_train,
parked_train_id,
parked_train_faces_stop,
}}


on_dispatcher_updated
Raised every UpdateInterval, after delivery generation

Contains:
update_interval = int -- time in ticks LTN needed to run all updates, varies depending on number of stops and requests
provided_by_stop = { [stop_id :: uint], { [item :: string], count :: int } }
requests_by_stop = { [stop_id :: uint], { [item :: string], count :: int } }
new_deliveries = { uint } -- train_ids of deliveries created this UpdateInterval
deliveries = { [train_id :: uint], {
force :: LuaForce,
train :: LuaTrain,
from :: string,
from_id :: uint,
to :: string,
to_id :: uint,
network_id: int32,
started :: uint, -- tick this delivery was created on
surface_connections = { entity1 :: LuaEntity, entity2 :: LuaEntity, network_id :: int32 },
shipment = { [item :: string], count :: int }
} }
available_trains = { [train_id :: uint], {
capacity :: int,
fluid_capacity :: int,
force :: LuaForce,
surface :: LuaSurface,
depot_priority :: int32,
network_id :: int32,
train :: LuaTrain

on_dispatcher_no_train_found
Raised when no train was found to handle a request

Contains:
to :: string -- requester.backer_name
to_id :: uint -- requester.unit_number
network_id :: int32
(optional) item :: string
(optional) from :: string
(optional) from_id :: uint
(optional) min_carriages :: int32
(optional) max_carriages :: int32
(optional) shipment = { [item :: string], count :: int }


on_delivery_pickup_complete
Raised when a train leaves provider stop

Contains:
train_id :: uint
train :: LuaTrain
planned_shipment= { [item :: string], count :: int }
actual_shipment = { [item :: string], count :: int } -- shipment updated to train inventory


on_delivery_completed
Raised when train leaves requester stop

Contains:
train_id :: uint
train :: LuaTrain
shipment= { [item :: string], count :: int }


on_delivery_failed
Raised when rolling stock of a train gets removed, the delivery timed out, train enters depot stop with active delivery

Contains:
train_id :: uint
shipment= { [item :: string], count :: int } }


on_dispatcher_no_train_found
Raised when depot was empty
Contains:
to :: string
to_id :: uint
network_id :: int32
item :: string -- <type,name>

on_dispatcher_no_train_found
Raised when no matching train was found
Contains:
to :: string
to_id :: uint
network_id :: int32
from :: string
from_id :: uint
min_carriages :: int32
max_carriages :: int32
shipment = { [item :: string], count :: int } }

on_provider_missing_cargo
Raised when trains leave provider with less than planned load

Contains:
train :: LuaTrain
station :: LuaEntity
planned_shipment = { [item :: string], count :: int } }
actual_shipment = { [item :: string], count :: int } }

on_provider_unscheduled_cargo
Raised when trains leave provider with wrong cargo

Contains:
train :: LuaTrain
station :: LuaEntity
planned_shipment = { [item :: string], count :: int } }
unscheduled_load = { [item :: string], count :: int } }

on_requester_unscheduled_cargo
Raised when trains arrive at requester with wrong cargo

Contains:
train :: LuaTrain
station :: LuaEntity
planned_shipment = { [item :: string], count :: int } }
unscheduled_load = { [item :: string], count :: int } }

connect_surfaces(entity1 :: LuaEntity, entity2 :: LuaEntity, network_id :: int32)
Designates two entities on different surfaces as forming a surface connection.
Connections are bi-directional but not transitive, i.e. surface A -> B implies B -> A, but A -> B and B -> C does not imply A -> C.
LTN will generate deliveries between depot and provider on one surface and requester on the other.
Network_id acts as additional mask for potential providers.
It is the caller's responsibility to ensure:
1) trains are moved between surfaces
2) deliveries are updated to the new train after surface transition, see reassign_delivery()
3) trains return to their original surface depot

disconnect_surfaces(entity1 :: LuaEntity, entity2 :: LuaEntity)
Removes a surface connection formed by the two given entities.
Active deliveries will not be affected.
It's not necessary to call this function when deleting one or both entities.

clear_all_surface_connections()
Clears all surface connections.
Active deliveries will not be affected
This function exists for debugging purposes, no event is raised to notify connection owners.

reassign_delivery(old_train_id :: uint, new_train :: LuaTrain) :: bool
Re-assigns a delivery to a different train.
Should be called after creating a train based on another train, for example after moving a train to a different surface.
Calls with an old_train_id without delivery have no effect.
Don't call this function when coupling trains via script, LTN already handles that through Factorio events.
This function does not add missing temp stops. See get_or_create_next_temp_stop for that.

get_or_create_next_temp_stop(train :: LuaTrain, schedule_index :: uint?) :: uint
Ensures the next logistic stop in the schedule has a temporary stop if it is on the same surface as the train.
If no schedule_index is given, the search for the next logistic stop starts from train.schedule.current
In case the train is currently stopping at that index, the search starts at the next higher index.
The result will be the schedule index of the temp stop of the next logistic stop or nil if there is no further logistic stop.

get_next_logistic_stop(train :: LuaTrain, schedule_index :: uint?) :: uint?, uint?, string?
Finds the next logistic stop in the schedule of the given train.
If no schedule_index is given, the search starts from train.schedule.current.
In case the train is currently stopping at that index, the search starts at the next higher index.
The result will be three values stop_schedule_index, stop_id and either the string "provider" or "requester".
If there is no further logistic stop in the schedule, the result will be nil.
Locked

Return to “Logistic Train Network”