Empty-inventory-slot reader

This is the place to request new mods or give ideas about what could be done.
Pi-C
Smart Inserter
Smart Inserter
Posts: 1645
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Empty-inventory-slot reader

Post by Pi-C »

This has been originally discussed in the thread for Optera's Inventory Sensor mod:
Pi-C wrote: ↑
Wed Jan 15, 2020 11:39 am
Is it possible to get the number of empty slots in an inventory with this mod?

Use case: I want to determine whether a cargo train still has some slots that can be filled and route it accordingly. For example, it would make sense that a partially filled cargo train goes to a station and picks up more stuff, while it would not make sense for a completely filled train to go to a station and leave again without dropping off or picking up a single item. While it's easy to check whether a train is empty, it's next to impossible right now to check whether it's completely filled (because of, for example, different stack sizes of different items, filtered/limited inventories, or modded wagons with extended/reduced inventory). My cargo trains are meant to carry mixed cargo, so I won't filter their inventories, but being able to see whether there are empty slots would certainly be useful.

Honktown wrote: ↑
Wed Jan 15, 2020 11:37 pm
mrvn wrote: ↑
Wed Jan 15, 2020 1:44 pm
nd track the filling yourself, etc.
Maybe replace the TrainStop with one that has the special combinator builtin? That way you don't have to handle detecting it getting connected or disconnected or worrying about having 2 stations connected and such.
Hey look at this smart guy. It did come to mind to see if one could make special circuit rails, that read just the wagon on themselves. That would allow individual carriage sensing if possible.
Pi-C wrote: ↑
Wed Jan 15, 2020 2:25 pm
...
For the reading stacks part, the available space for existing stacks could be read and sum up (stack_size - count) for all slots and item types. If a train had all slots half full of iron plates, it can still receive iron plates, which would be the output signal.

Unfortunately wagons are different than the inventory of the whole train, so each wagon would have to be sensed for things to work right (or only one wagon).
My original approach with an inventory sensor in front of each wagon was because I didn't think of checking the available slots on a per-train basis -- the IS was just the closest thing to what I wanted.
Empty spaces would be much easier to track, just do for i=1..last_inventory if slotempty E = E + 1.
Looks simple enough. Just thinking, though: Would I really want to know the exact number of free slots -- or would it be enough to know that there is at least one empty slot? The second approach could be more efficient because you'd only have to iterate over the inventory until you find the first empty slot (if you're lucky, it's the first one and everything is over almost immediately; worst case: either just the last or no slot is empty and you'd have to go over the whole inventory).

In my case, the second approach would be sufficient as my trains are pretty short (L4CL), so even if only one slot could be filled at the next station, it may be worthwhile to divert the train. However, that's different for really huge trains. Could be a modsetting, though: precision vs. speed. :-)

Another thing: So far we've only talked about cargo trains. Ideally, I'd also like to know if the train contains empty fluid wagons. They don't have an inventory, so that check would work completely different -- and it may get really hard if trains with both cargo and fluid wagons are allowed in the network. In my game, I only use trains with varieties (also regarding inventory size/capacity) of either cargo/artillery (inventory) or fluid (capacity) wagons, and I mark each train with virtual signals (A: artillery; C: cargo; F: fluid; optionally X: express train), so it would be easy to know what to check for. Doesn't help much, though, if you want to make a mod that's useful for everybody! :-D (Of course, a dependency on TrainsSignalSender and options for mapping the virtual signals to train types might help -- still not optimal as that would require all users to manually set the signals on all of their trains.)
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

Honktown
Smart Inserter
Smart Inserter
Posts: 1025
Joined: Thu Oct 03, 2019 7:10 am
Contact:

Re: Empty-inventory-slot reader

Post by Honktown »

Throwing out an idea: specific entities/items for each rolling stock made available. A simple way to handle the signal would be that when the entity is placed, it searches the surface for the nearest train station within some distance (find all train stations, go over each). Store the station in a global table, and insert the rail into the train stop table. When a train stops, read wagon N that corresponds to the entity (add constant combinator circuitry to the rail, make it inoperable, call them rolling-reader[1-4]). When a train stops, if at circuit train stop, add combinator rails to on_tick function (by ID->table, probably) paired with entity table of the Nth rolling stock (perhaps with a third property for fluid vs item wagon). Each tick, do desired circuit behavior for each combinator-entity pair in table (count empty spaces, output free stack space, etc). When train leaves, for connected combinators in pairs(station), nil the ID in the on-tick table.

A second table could be used to store rail-combinator to station, so when a rail is picked up, destroyed, etc, it goes back to the station and removes itself from the global table of stations, and if empty, removes the train station entry itself.

This way it wouldn't be blueprintable, but it'd be workable-ish, as long as the nearest station is the one to which we'd want to connect. Not sure if you can follow rails, and get a nearest-station, which would make it immediately 'snap' to a station.

Edit: or do the signal T thing for all connected combinators each tick. That thing.
I have mods! I guess!
Link

Pi-C
Smart Inserter
Smart Inserter
Posts: 1645
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Empty-inventory-slot reader

Post by Pi-C »

Honktown wrote: ↑
Thu Jan 16, 2020 10:16 am
Throwing out an idea: specific entities/items for each rolling stock made available. A simple way to handle the signal would be that when the entity is placed, it searches the surface for the nearest train station within some distance (find all train stations, go over each).
These entities/items: would they have to be based on straight rails, or would it also be possible to use curved ones? About "within some distance" -- wouldn't limiting the check to the block the train stop is in make sense? Usually, a block should be as long as the longest train in the network …
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

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

Re: Empty-inventory-slot reader

Post by Optera »

It should be rather simple to write a mod counting empty slots when a train arrives at a given stop.

I'd make a Combinator similar to IS or Ghost Scanner. To work with any modded train stops when placed it looks within 1-2 tiles for any type="train-stop" and stores the stop for reference
For best performance listen to on_train_state_changed event; if train.state == defines.train_state.wait_station and train.station == cached connected stop then parse inventory and output number of empty slots in train.

To reset it'd have to also store train.id and when on_train_state_changed or on_train_created references that id clear the combinator output.

mrvn
Smart Inserter
Smart Inserter
Posts: 5703
Joined: Mon Sep 05, 2016 9:10 am
Contact:

Re: Empty-inventory-slot reader

Post by mrvn »

Optera wrote: ↑
Thu Jan 16, 2020 10:56 am
It should be rather simple to write a mod counting empty slots when a train arrives at a given stop.

I'd make a Combinator similar to IS or Ghost Scanner. To work with any modded train stops when placed it looks within 1-2 tiles for any type="train-stop" and stores the stop for reference
For best performance listen to on_train_state_changed event; if train.state == defines.train_state.wait_station and train.station == cached connected stop then parse inventory and output number of empty slots in train.

To reset it'd have to also store train.id and when on_train_state_changed or on_train_created references that id clear the combinator output.
There might be cases where you have 2 train stops next to each other and the sensor could pick either of them. Or you need to place the sensor on the other side of the rails so it's 3 tiles away. It might be easier to control the connection by following the red/green wires. The sensor would read the train stop that has the same red/green network ID.

Honktown
Smart Inserter
Smart Inserter
Posts: 1025
Joined: Thu Oct 03, 2019 7:10 am
Contact:

Re: Empty-inventory-slot reader

Post by Honktown »

mrvn wrote: ↑
Thu Jan 16, 2020 12:04 pm
Optera wrote: ↑
Thu Jan 16, 2020 10:56 am
It should be rather simple to write a mod counting empty slots when a train arrives at a given stop.

I'd make a Combinator similar to IS or Ghost Scanner. To work with any modded train stops when placed it looks within 1-2 tiles for any type="train-stop" and stores the stop for reference
For best performance listen to on_train_state_changed event; if train.state == defines.train_state.wait_station and train.station == cached connected stop then parse inventory and output number of empty slots in train.

To reset it'd have to also store train.id and when on_train_state_changed or on_train_created references that id clear the combinator output.
There might be cases where you have 2 train stops next to each other and the sensor could pick either of them. Or you need to place the sensor on the other side of the rails so it's 3 tiles away. It might be easier to control the connection by following the red/green wires. The sensor would read the train stop that has the same red/green network ID.
There isn't a "connected_to_circuit" event. I'm not really sure what happens when one places a wire, since it's not like, an entity? There must be a way the game determines when wires are attached that is accessible to modders. A goal would be to create a shortcut between the combinator and the train station, but how exactly would one know when it is or isn't connected in a performance-friendly way.
I have mods! I guess!
Link

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

Re: Empty-inventory-slot reader

Post by Optera »

Honktown wrote: ↑
Thu Jan 16, 2020 1:07 pm
There isn't a "connected_to_circuit" event. I'm not really sure what happens when one places a wire, since it's not like, an entity? There must be a way the game determines when wires are attached that is accessible to modders. A goal would be to create a shortcut between the combinator and the train station, but how exactly would one know when it is or isn't connected in a performance-friendly way.
No there isn't. To catch changes in power or circuit wires you have to constantly poll.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Empty-inventory-slot reader

Post by eradicator »

Pi-C wrote: ↑
Thu Jan 16, 2020 9:38 am
Would I really want to know the exact number of free slots -- or would it be enough to know that there is at least one empty slot? The second approach could be more efficient because you'd only have to iterate over the inventory until you find the first empty slot (if you're lucky, it's the first one and everything is over almost immediately; worst case: either just the last or no slot is empty and you'd have to go over the whole inventory).
If you accept that manually filled wagons might be detected wrongly you can approximate this to checking only the last slot.

Code: Select all

if not Inventory[#Inventory].valid_for_read then break end
This should work because inserters always fill inventories top -> down, so if the last slot is not empty it can be approximated that the inventory is completely filled. Does get you into trouble when trains are only half-emptied at the target i guess. There's Inventory.sort_and_merge() which could fix that but is probably too expensive. In which case iterating backwards through the inventory

Code: Select all

for i=#Inventory,1,-1 do if not Inventory[i].valid_for_read then break end end
should still be faster because the first slots have a higher chance of not being empty.

If you wanted to know the total number of empty slots you always have to iterate the whole inventory. If you're lucky then Inventory.get_contents() plus some math might be faster because it iterates on the C side, but i wouldn't bet anything on that.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

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

Re: Empty-inventory-slot reader

Post by Optera »

If you are really cheesy sort the inventory first, then all your tricks work flawlessly. ;)

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Empty-inventory-slot reader

Post by eradicator »

Optera wrote: ↑
Thu Jan 16, 2020 3:11 pm
If you are really cheesy sort the inventory first, then all your tricks work flawlessly. ;)
eradicator wrote: ↑
Thu Jan 16, 2020 2:48 pm
There's Inventory.sort_and_merge() which could fix that but is probably too expensive.
:roll:
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

Pi-C
Smart Inserter
Smart Inserter
Posts: 1645
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Empty-inventory-slot reader

Post by Pi-C »

eradicator wrote: ↑
Fri Jan 17, 2020 8:07 am
Optera wrote: ↑
Thu Jan 16, 2020 3:11 pm
If you are really cheesy sort the inventory first, then all your tricks work flawlessly. ;)
eradicator wrote: ↑
Thu Jan 16, 2020 2:48 pm
There's Inventory.sort_and_merge() which could fix that but is probably too expensive.
:roll:
How does inventory.sort_and_merge() work? I need such stations everywhere a track branches off the line in order to set the signals. If really every inventory of every train would be sorted/merged at every stop, this definitely would be expensive. But what about a scenario where the inventory hasn't changed since the last sort/merge? Would the function notice somehow that it doesn't need to do anything because the inventory already is OK?
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

Pi-C
Smart Inserter
Smart Inserter
Posts: 1645
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Empty-inventory-slot reader

Post by Pi-C »

eradicator wrote: ↑
Thu Jan 16, 2020 2:48 pm
Pi-C wrote: ↑
Thu Jan 16, 2020 9:38 am
Would I really want to know the exact number of free slots -- or would it be enough to know that there is at least one empty slot? The second approach could be more efficient because you'd only have to iterate over the inventory until you find the first empty slot (if you're lucky, it's the first one and everything is over almost immediately; worst case: either just the last or no slot is empty and you'd have to go over the whole inventory).
If you accept that manually filled wagons might be detected wrongly you can approximate this to checking only the last slot.

Code: Select all

if not Inventory[#Inventory].valid_for_read then break end
I have decided to use mixed cargo trains. A train stop may order coal because the amount in the buffers falls below the threshold. While a full coal train is traveling to the station, more of the coal is used up, so when the train arrives, it does not completely fill the buffers, so another coal train is requested. If it can't unload everything, it will leave the station partially filled, but on its way back to the depot it may stop at stations providing things that are needed in smaller amounts (ammo, repair kits, barrels …). When some of these are unloaded at other stations, it's quite likely that the inventory may have gaps while the last slot is filled.
Does get you into trouble when trains are only half-emptied at the target i guess.
Didn't read carefully enough the first time. Just what I described above. :-D

No, I think just getting the number of actually empty slots (without merging etc.) would be a good compromise.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Empty-inventory-slot reader

Post by eradicator »

eradicator wrote: ↑
Thu Jan 16, 2020 2:48 pm
If you wanted to know the total number of empty slots you always have to iterate the whole inventory. If you're lucky then Inventory.get_contents() plus some math might be faster because it iterates on the C side, but i wouldn't bet anything on that.
On second thought this assumes that there is at most one partially-full stack of every item. So again fails for "weird" setups.
Pi-C wrote: ↑
Fri Jan 17, 2020 8:40 am
How does inventory.sort_and_merge() work? [...]. If really every inventory of every train would be sorted/merged at every stop, this definitely would be expensive.[...]But what about a scenario where the inventory hasn't changed since the last sort/merge? Would the function notice somehow that it doesn't need to do anything because the inventory already is OK?
I don't know how it's implemented. It's probably cheaper to run on an already sorted inventory than on a totally chaotic one. But it still needs to iterate all slots and do some comparisons to even know that the inventory is "already sorted". It also has the drawback of messing up the order of inventories that the player filled themselfs (manual usage). It's not a "nice" solution, just a *possible* solution if you can come up with a smart algorythm that doesn't do it on *every* stop.
Pi-C wrote: ↑
Fri Jan 17, 2020 9:00 am
No, I think just getting the number of actually empty slots (without merging etc.) would be a good compromise.
Getting the *count* is not a compromise though, getting the count is "iterate all the slots all the time".

Looking at the documentation of LuaInventory...
  • is_empty() -> might be used as a first check, but probably fails 99% of the time anyway.
  • find_item_stack() -> doesn't support finding empty stacks :|
  • can_insert() -> this could give you "is there at least one empty slot" if used with a dummy item of stack_size=1 that is guaranteed to never already exist in the inventory. sadly it doesn't return the count of insertable items so it's not directly usable to determine how many slots are empty (possible interface request to return count as second value?).
    If combined with a binary search (1ok?y 2ok?y 4ok?y 8ok?n 6ok?y 7ok?y) it could be used to get the exact empty slot count, but at that point it's probably faster (and definetly less complex) to iterate again.
  • insert() -> actually get you the number of dummy items inserted, but then you'll have to remove them again which is kinda ugly and probably not very fast.
Conclusion: Just do a dumb full iteration if you need the slot count, or can_insert() if you only need "at least one slot". Then stop worrying about performance until the mod is finished and working :p.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

mrvn
Smart Inserter
Smart Inserter
Posts: 5703
Joined: Mon Sep 05, 2016 9:10 am
Contact:

Re: Empty-inventory-slot reader

Post by mrvn »

Honktown wrote: ↑
Thu Jan 16, 2020 1:07 pm
mrvn wrote: ↑
Thu Jan 16, 2020 12:04 pm
Optera wrote: ↑
Thu Jan 16, 2020 10:56 am
It should be rather simple to write a mod counting empty slots when a train arrives at a given stop.

I'd make a Combinator similar to IS or Ghost Scanner. To work with any modded train stops when placed it looks within 1-2 tiles for any type="train-stop" and stores the stop for reference
For best performance listen to on_train_state_changed event; if train.state == defines.train_state.wait_station and train.station == cached connected stop then parse inventory and output number of empty slots in train.

To reset it'd have to also store train.id and when on_train_state_changed or on_train_created references that id clear the combinator output.
There might be cases where you have 2 train stops next to each other and the sensor could pick either of them. Or you need to place the sensor on the other side of the rails so it's 3 tiles away. It might be easier to control the connection by following the red/green wires. The sensor would read the train stop that has the same red/green network ID.
There isn't a "connected_to_circuit" event. I'm not really sure what happens when one places a wire, since it's not like, an entity? There must be a way the game determines when wires are attached that is accessible to modders. A goal would be to create a shortcut between the combinator and the train station, but how exactly would one know when it is or isn't connected in a performance-friendly way.
Sadly there is no event for that. But do you need it?

Option 1)
The sensor needs to do work whenever a train enters or leaves a station. And for that there are events. So I would check the wire connection whenever a train arrives and cache it for when it leaves. I think there is a function to get all entities on a network and you would filter that for sensors. You could also do this when a sensor is placed (in case of blueprints where the wire already exists) or when the gui is opened/closed to update sensors before a train arrives and avoid most "There is a train at the station. Why doesn't the sensor show anything?" complains. A side effect of this would be that a sensor gets data when connected to a station when a train arrives. But also when the train leaves even if the wire was cut since then (unless you open the gui).

A problem would be stops that are connected to millions of entities. This could be costly every time a train arrives.

Option 2)

So maybe we do need it. :(

Another way would be to check the network ID of one stop and one sensor every tick and update from there as things change. That would give a constant low level load and new sensors would take long and longer to first connect as you get more of them. If you expect many sensors it might be worth assuming that new sensors need to be checked more often than old ones. So re-check each sensor after at least a 10, 20, 30, 40, 50, .. tick gap. Each time a sensor is checked without change the gap increases.

I would still verify the network ID matches on every train arrival/departure as well. But no searching for possible connections. Also when a stop or sensor is destroyed.

Olacken
Long Handed Inserter
Long Handed Inserter
Posts: 62
Joined: Wed Apr 17, 2019 1:37 pm
Contact:

Re: Empty-inventory-slot reader

Post by Olacken »

Or you could make it an train track or possibly a signal and get the end of the block and you would only need to update whean a train stop is created or destroyed and also possibly tracks but for that you would just need to make it the track below the stop

mrvn
Smart Inserter
Smart Inserter
Posts: 5703
Joined: Mon Sep 05, 2016 9:10 am
Contact:

Re: Empty-inventory-slot reader

Post by mrvn »

Olacken wrote: ↑
Wed Jan 29, 2020 1:57 pm
Or you could make it an train track or possibly a signal and get the end of the block and you would only need to update whean a train stop is created or destroyed and also possibly tracks but for that you would just need to make it the track below the stop
That was already covered above. Problem is when you replace the vanilla TrainStop with one that has a sensor that doesn't make LTN stops work.

Olacken
Long Handed Inserter
Long Handed Inserter
Posts: 62
Joined: Wed Apr 17, 2019 1:37 pm
Contact:

Re: Empty-inventory-slot reader

Post by Olacken »

I was not proposing to replace the train stop I was suggesting to replace the track under the stop

mrvn
Smart Inserter
Smart Inserter
Posts: 5703
Joined: Mon Sep 05, 2016 9:10 am
Contact:

Re: Empty-inventory-slot reader

Post by mrvn »

Olacken wrote: ↑
Thu Jan 30, 2020 1:45 pm
I was not proposing to replace the train stop I was suggesting to replace the track under the stop
And how would that work with wooden rails (where I don't have steel rails for ages so no sensors) or powered rails (where the sensor rail breaks the power flow)?

Olacken
Long Handed Inserter
Long Handed Inserter
Posts: 62
Joined: Wed Apr 17, 2019 1:37 pm
Contact:

Re: Empty-inventory-slot reader

Post by Olacken »

mrvn wrote: ↑
Thu Jan 30, 2020 2:26 pm
And how would that work with wooden rails (where I don't have steel rails for ages so no sensors) or powered rails (where the sensor rail breaks the power flow)?
For wooden rails I see no probleme no steel rail no sensor and you could even change the sensor recipe so taht it doesn't cost steel.

For powered rail I'm not sure how they transfer power but you could probably do an integration and even if you don't want the only rail that need to be a sensor is the one directly beneath the train stop so if you are doing a "proper" train network it wouldn't cut the flow in the middle of the network just at the edges if you are using two headed train and not at all if you are using one headed trains with loops ( a train stop stack could induce probleme in very specific spot)

And alos I also proposed a rail signal way that wouldn't cause probleme for any of those two nor LTN probably a little harder to do though

Honktown
Smart Inserter
Smart Inserter
Posts: 1025
Joined: Thu Oct 03, 2019 7:10 am
Contact:

Re: Empty-inventory-slot reader

Post by Honktown »

mrvn wrote: ↑
Thu Jan 30, 2020 2:26 pm
Olacken wrote: ↑
Thu Jan 30, 2020 1:45 pm
I was not proposing to replace the train stop I was suggesting to replace the track under the stop
And how would that work with wooden rails (where I don't have steel rails for ages so no sensors) or powered rails (where the sensor rail breaks the power flow)?
In data-final-fixes, you can add the circuits to all rails. Screw modders if they make "analog" rails that aren't supposed to have circuit attachments. I wouldn't think have a circuit specification requires power, but that'd be one of those annoying "did the devs really do that" behaviors

Err: my original idea was duplicating existing rails into rails 1-2-3-4, for wagon-specific circuits. It wouldn't interfere with normal recipes. Other methods would.
I have mods! I guess!
Link

Post Reply

Return to β€œIdeas and Requests For Mods”