Add automatic distribution
This commit is contained in:
parent
721e83006d
commit
aee56af22f
5 changed files with 386 additions and 14 deletions
|
|
@ -0,0 +1,141 @@
|
||||||
|
subfactories:
|
||||||
|
- !SubFactory
|
||||||
|
recipe: chemical-science-pack
|
||||||
|
machines: 96
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: engine-unit
|
||||||
|
machines: 80
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: iron-gear-wheel
|
||||||
|
machines: 4
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: pipe
|
||||||
|
machines: 8
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: advanced-circuit
|
||||||
|
machines: 72
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: electronic-circuit
|
||||||
|
machines: 6
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: electronic-circuit
|
||||||
|
machines: 6
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: copper-cable
|
||||||
|
machines: 10
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: copper-cable
|
||||||
|
machines: 10
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !SubFactory
|
||||||
|
recipe: copper-cable
|
||||||
|
machines: 10
|
||||||
|
machine: assembly-machine-3
|
||||||
|
- !ExternalConnection
|
||||||
|
- !Distributor
|
||||||
|
- !Distributor
|
||||||
|
factory_connections:
|
||||||
|
- item: chemical-science-pack
|
||||||
|
amount: 10
|
||||||
|
from: 0
|
||||||
|
to: 10
|
||||||
|
- item: engine-unit
|
||||||
|
amount: 10
|
||||||
|
from: 1
|
||||||
|
to: 0
|
||||||
|
- item: advanced-circuit
|
||||||
|
amount: 15
|
||||||
|
from: 4
|
||||||
|
to: 0
|
||||||
|
- item: sulfur
|
||||||
|
amount: 5
|
||||||
|
from: 10
|
||||||
|
to: 0
|
||||||
|
- item: steel
|
||||||
|
amount: 10
|
||||||
|
from: 10
|
||||||
|
to: 1
|
||||||
|
- item: iron-gear-wheel
|
||||||
|
amount: 10
|
||||||
|
from: 2
|
||||||
|
to: 1
|
||||||
|
- item: pipe
|
||||||
|
amount: 20
|
||||||
|
from: 3
|
||||||
|
to: 1
|
||||||
|
- item: iron-plate
|
||||||
|
amount: 20
|
||||||
|
from: 10
|
||||||
|
to: 2
|
||||||
|
- item: iron-plate
|
||||||
|
amount: 20
|
||||||
|
from: 10
|
||||||
|
to: 3
|
||||||
|
- item: electronic-circuit
|
||||||
|
amount: 30
|
||||||
|
from: 12
|
||||||
|
to: 4
|
||||||
|
- item: electronic-circuit
|
||||||
|
amount: 15
|
||||||
|
from: 5
|
||||||
|
to: 12
|
||||||
|
- item: electronic-circuit
|
||||||
|
amount: 15
|
||||||
|
from: 6
|
||||||
|
to: 12
|
||||||
|
- item: plastic
|
||||||
|
amount: 30
|
||||||
|
from: 10
|
||||||
|
to: 4
|
||||||
|
- item: iron-plate
|
||||||
|
amount: 15
|
||||||
|
from: 10
|
||||||
|
to: 5
|
||||||
|
- item: iron-plate
|
||||||
|
amount: 15
|
||||||
|
from: 10
|
||||||
|
to: 6
|
||||||
|
- item: copper-plate
|
||||||
|
amount: 25
|
||||||
|
from: 10
|
||||||
|
to: 7
|
||||||
|
- item: copper-plate
|
||||||
|
amount: 25
|
||||||
|
from: 10
|
||||||
|
to: 8
|
||||||
|
- item: copper-plate
|
||||||
|
amount: 25
|
||||||
|
from: 10
|
||||||
|
to: 9
|
||||||
|
- item: copper-cable
|
||||||
|
amount: 45
|
||||||
|
from: 11
|
||||||
|
to: 5
|
||||||
|
- item: copper-cable
|
||||||
|
amount: 60
|
||||||
|
from: 11
|
||||||
|
to: 4
|
||||||
|
- item: copper-cable
|
||||||
|
amount: 50
|
||||||
|
from: 7
|
||||||
|
to: 11
|
||||||
|
- item: copper-cable
|
||||||
|
amount: 50
|
||||||
|
from: 8
|
||||||
|
to: 11
|
||||||
|
- item: copper-cable
|
||||||
|
amount: 45
|
||||||
|
from: 11
|
||||||
|
to: 6
|
||||||
|
- item: copper-cable
|
||||||
|
amount: 50
|
||||||
|
from: 9
|
||||||
|
to: 11
|
||||||
|
|
@ -30,7 +30,7 @@ struct Args {
|
||||||
#[arg(short, long, default_value = "none")]
|
#[arg(short, long, default_value = "none")]
|
||||||
tracing: Tracing,
|
tracing: Tracing,
|
||||||
|
|
||||||
#[arg(long, default_value = "ca-fbh")]
|
#[arg(long, default_value = "ca-bq")]
|
||||||
pathfinder: PathfinderArg,
|
pathfinder: PathfinderArg,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
use crate::{
|
||||||
|
assembly::assembly_line_2_input,
|
||||||
|
multistation::{StationSpec, multistation},
|
||||||
|
};
|
||||||
use factorio_blueprint::abstraction::{Blueprint, Entity};
|
use factorio_blueprint::abstraction::{Blueprint, Entity};
|
||||||
use factorio_core::{beltoptions::Beltspeed, prelude::*, visualize::Visualize};
|
use factorio_core::{beltoptions::Beltspeed, prelude::*, visualize::Visualize};
|
||||||
use factorio_layout::{Connection, Interface, LayoutInput, Layouter, MacroBlock};
|
use factorio_layout::{Connection, Interface, LayoutInput, Layouter, MacroBlock};
|
||||||
|
|
@ -6,11 +10,6 @@ use rand::{Rng, SeedableRng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
|
||||||
assembly::assembly_line_2_input,
|
|
||||||
multistation::{StationSpec, multistation},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct FactoryGraph {
|
pub struct FactoryGraph {
|
||||||
pub subfactories: Vec<Building>,
|
pub subfactories: Vec<Building>,
|
||||||
|
|
@ -27,6 +26,7 @@ pub enum Building {
|
||||||
ExternalConnection,
|
ExternalConnection,
|
||||||
Splitter,
|
Splitter,
|
||||||
SideLoader,
|
SideLoader,
|
||||||
|
Distributor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
@ -37,11 +37,13 @@ pub struct FactoryConnection {
|
||||||
pub to: usize,
|
pub to: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct IntermediateConnection {
|
struct IntermediateConnection {
|
||||||
input: HashMap<usize, IntermediateConnectionEntry>,
|
input: HashMap<usize, IntermediateConnectionEntry>,
|
||||||
output: HashMap<usize, IntermediateConnectionEntry>,
|
output: HashMap<usize, IntermediateConnectionEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct IntermediateConnectionEntry {
|
struct IntermediateConnectionEntry {
|
||||||
block: Option<usize>,
|
block: Option<usize>,
|
||||||
interface: usize,
|
interface: usize,
|
||||||
|
|
@ -446,6 +448,163 @@ pub fn generate_factory<L: Layouter, P: Pathfinder + Sync, R: Rng + SeedableRng
|
||||||
.collect::<HashMap<_, _>>(),
|
.collect::<HashMap<_, _>>(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Building::Distributor => {
|
||||||
|
let mut blueprint = Blueprint::new();
|
||||||
|
blueprint.add_entity(Entity::new_splitter_with_priority(
|
||||||
|
Beltspeed::from_items_per_second(
|
||||||
|
input_connections
|
||||||
|
.iter()
|
||||||
|
.chain(output_connections.iter())
|
||||||
|
.map(|&(_, c)| c.amount)
|
||||||
|
.max_by(|x, y| x.partial_cmp(y).unwrap())
|
||||||
|
.unwrap_or(1.0),
|
||||||
|
),
|
||||||
|
Position::new(2, 1),
|
||||||
|
Direction::Up,
|
||||||
|
None,
|
||||||
|
Some(true),
|
||||||
|
));
|
||||||
|
|
||||||
|
let macro_block = MacroBlock {
|
||||||
|
size: Position::new(2, 1),
|
||||||
|
input: vec![
|
||||||
|
Interface {
|
||||||
|
offset: Position::new(0, 0),
|
||||||
|
dir: Direction::Up,
|
||||||
|
},
|
||||||
|
Interface {
|
||||||
|
offset: Position::new(1, 0),
|
||||||
|
dir: Direction::Up,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: vec![
|
||||||
|
Interface {
|
||||||
|
offset: Position::new(0, 0),
|
||||||
|
dir: Direction::Up,
|
||||||
|
},
|
||||||
|
Interface {
|
||||||
|
offset: Position::new(1, 0),
|
||||||
|
dir: Direction::Up,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut output_index = 0;
|
||||||
|
let mut input_index = 1;
|
||||||
|
let mut amount = input_connections[0].1.amount;
|
||||||
|
|
||||||
|
let mut intermediate_connections_input = HashMap::from([(
|
||||||
|
input_connections[0].0,
|
||||||
|
IntermediateConnectionEntry {
|
||||||
|
block: Some(block_index),
|
||||||
|
interface: 0,
|
||||||
|
},
|
||||||
|
)]);
|
||||||
|
let mut intermediate_connections_output = HashMap::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
while input_index < input_connections.len()
|
||||||
|
&& amount + input_connections[input_index].1.amount
|
||||||
|
< output_connections[output_index].1.amount
|
||||||
|
{
|
||||||
|
if input_index > 1 || output_index > 0 {
|
||||||
|
connections.push(Connection {
|
||||||
|
startblock: Some(blocks.len() - 1),
|
||||||
|
startpoint: 1,
|
||||||
|
endblock: Some(blocks.len()),
|
||||||
|
endpoint: 0,
|
||||||
|
lanes: 1,
|
||||||
|
beltspeed: Beltspeed::Turbo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
intermediate_connections_input.insert(
|
||||||
|
input_connections[input_index].0,
|
||||||
|
IntermediateConnectionEntry {
|
||||||
|
block: Some(blocks.len()),
|
||||||
|
interface: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
blueprints.push(blueprint.clone());
|
||||||
|
blocks.push(macro_block.clone());
|
||||||
|
amount += input_connections[input_index].1.amount;
|
||||||
|
input_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if input_index > 1 || output_index > 0 {
|
||||||
|
connections.push(Connection {
|
||||||
|
startblock: Some(blocks.len() - 1),
|
||||||
|
startpoint: 1,
|
||||||
|
endblock: Some(blocks.len()),
|
||||||
|
endpoint: 0,
|
||||||
|
lanes: 1,
|
||||||
|
beltspeed: Beltspeed::Turbo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if amount >= output_connections[output_index].1.amount {
|
||||||
|
intermediate_connections_output.insert(
|
||||||
|
output_connections[output_index].0,
|
||||||
|
IntermediateConnectionEntry {
|
||||||
|
block: Some(blocks.len()),
|
||||||
|
interface: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
blueprints.push(blueprint.clone());
|
||||||
|
blocks.push(macro_block.clone());
|
||||||
|
} else {
|
||||||
|
intermediate_connections_output.insert(
|
||||||
|
output_connections[output_index].0,
|
||||||
|
IntermediateConnectionEntry {
|
||||||
|
block: Some(blocks.len()),
|
||||||
|
interface: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
intermediate_connections_input.insert(
|
||||||
|
input_connections[input_index].0,
|
||||||
|
IntermediateConnectionEntry {
|
||||||
|
block: Some(blocks.len()),
|
||||||
|
interface: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
blueprints.push(blueprint.clone());
|
||||||
|
blocks.push(macro_block.clone());
|
||||||
|
amount += input_connections[input_index].1.amount;
|
||||||
|
input_index += 1;
|
||||||
|
}
|
||||||
|
amount -= output_connections[output_index].1.amount;
|
||||||
|
output_index += 1;
|
||||||
|
|
||||||
|
if output_index >= output_connections.len() - 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_connections.len() > 1 {
|
||||||
|
intermediate_connections_output.insert(
|
||||||
|
output_connections.last().unwrap().0,
|
||||||
|
IntermediateConnectionEntry {
|
||||||
|
block: Some(blocks.len() - 1),
|
||||||
|
interface: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbg!(
|
||||||
|
// &input_connections,
|
||||||
|
// &output_connections,
|
||||||
|
// &intermediate_connections_input,
|
||||||
|
// &intermediate_connections_output,
|
||||||
|
// amount,
|
||||||
|
// );
|
||||||
|
|
||||||
|
intermediate_connections.push(IntermediateConnection {
|
||||||
|
input: intermediate_connections_input,
|
||||||
|
output: intermediate_connections_output,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// dbg!(&intermediate_connections);
|
// dbg!(&intermediate_connections);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use factorio_blueprint::abstraction::{Blueprint, Entity, RailType, UndergroundType};
|
|
||||||
use factorio_core::{beltoptions::Beltspeed, prelude::*};
|
|
||||||
|
|
||||||
use crate::{balancer::binary_balancer, station::basic_station};
|
use crate::{balancer::binary_balancer, station::basic_station};
|
||||||
|
use factorio_blueprint::abstraction::{
|
||||||
|
Blueprint, ElectricPoleType, Entity, RailType, UndergroundType,
|
||||||
|
};
|
||||||
|
use factorio_core::{beltoptions::Beltspeed, prelude::*};
|
||||||
|
|
||||||
pub struct StationSpec {
|
pub struct StationSpec {
|
||||||
pub locomotives: usize,
|
pub locomotives: usize,
|
||||||
|
|
@ -52,6 +53,12 @@ pub fn multistation(
|
||||||
|
|
||||||
let mut blueprint = Blueprint::new();
|
let mut blueprint = Blueprint::new();
|
||||||
|
|
||||||
|
// connection power pole
|
||||||
|
blueprint.add_entity(Entity::new_electric_pole(
|
||||||
|
ElectricPoleType::Big,
|
||||||
|
Position::new(32, 0),
|
||||||
|
));
|
||||||
|
|
||||||
// stacker
|
// stacker
|
||||||
let stacker_length = (longest_train * 5).div_ceil(2);
|
let stacker_length = (longest_train * 5).div_ceil(2);
|
||||||
blueprint.add_entity(Entity::new_rail(
|
blueprint.add_entity(Entity::new_rail(
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,11 @@ pub struct Entity {
|
||||||
pub enum EntityType {
|
pub enum EntityType {
|
||||||
Belt(Beltspeed),
|
Belt(Beltspeed),
|
||||||
UndergroundBelt(Beltspeed, UndergroundType),
|
UndergroundBelt(Beltspeed, UndergroundType),
|
||||||
Splitter(Beltspeed),
|
Splitter {
|
||||||
|
beltspeed: Beltspeed,
|
||||||
|
input_priority_left: Option<bool>,
|
||||||
|
output_priority_left: Option<bool>,
|
||||||
|
},
|
||||||
ElectricPole(ElectricPoleType),
|
ElectricPole(ElectricPoleType),
|
||||||
Inserter {
|
Inserter {
|
||||||
inserter_type: InserterType,
|
inserter_type: InserterType,
|
||||||
|
|
@ -203,9 +207,34 @@ impl Entity {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_splitter(beltspeed: Beltspeed, position: Position, direction: Direction) -> Self {
|
pub fn new_splitter(beltspeed: Beltspeed, position: Position, direction: Direction) -> Self {
|
||||||
Self::new(EntityType::Splitter(beltspeed), position, direction)
|
Self::new(
|
||||||
|
EntityType::Splitter {
|
||||||
|
beltspeed,
|
||||||
|
input_priority_left: None,
|
||||||
|
output_priority_left: None,
|
||||||
|
},
|
||||||
|
position,
|
||||||
|
direction,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_splitter_with_priority(
|
||||||
|
beltspeed: Beltspeed,
|
||||||
|
position: Position,
|
||||||
|
direction: Direction,
|
||||||
|
input_priority_left: Option<bool>,
|
||||||
|
output_priority_left: Option<bool>,
|
||||||
|
) -> Self {
|
||||||
|
Self::new(
|
||||||
|
EntityType::Splitter {
|
||||||
|
beltspeed,
|
||||||
|
input_priority_left,
|
||||||
|
output_priority_left,
|
||||||
|
},
|
||||||
|
position,
|
||||||
|
direction,
|
||||||
|
)
|
||||||
|
}
|
||||||
pub fn new_inserter(
|
pub fn new_inserter(
|
||||||
inserter_type: InserterType,
|
inserter_type: InserterType,
|
||||||
override_stack_size: Option<u8>,
|
override_stack_size: Option<u8>,
|
||||||
|
|
@ -288,7 +317,11 @@ impl Entity {
|
||||||
EntityType::UndergroundBelt(beltspeed, _underground_type) => {
|
EntityType::UndergroundBelt(beltspeed, _underground_type) => {
|
||||||
beltspeed.string_underground()
|
beltspeed.string_underground()
|
||||||
}
|
}
|
||||||
EntityType::Splitter(beltspeed) => beltspeed.string_splitter(),
|
EntityType::Splitter {
|
||||||
|
beltspeed,
|
||||||
|
input_priority_left: _,
|
||||||
|
output_priority_left: _,
|
||||||
|
} => beltspeed.string_splitter(),
|
||||||
EntityType::Unknown {
|
EntityType::Unknown {
|
||||||
name,
|
name,
|
||||||
size: _,
|
size: _,
|
||||||
|
|
@ -340,9 +373,38 @@ impl Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_maybe_input_priority(&self) -> Option<String> {
|
||||||
|
match self.entity {
|
||||||
|
EntityType::Splitter {
|
||||||
|
beltspeed: _,
|
||||||
|
input_priority_left,
|
||||||
|
output_priority_left: _,
|
||||||
|
} => match input_priority_left {
|
||||||
|
Some(true) => Some("left".to_string()),
|
||||||
|
Some(false) => Some("right".to_string()),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_maybe_output_priority(&self) -> Option<String> {
|
||||||
|
match self.entity {
|
||||||
|
EntityType::Splitter {
|
||||||
|
beltspeed: _,
|
||||||
|
input_priority_left: _,
|
||||||
|
output_priority_left,
|
||||||
|
} => match output_priority_left {
|
||||||
|
Some(true) => Some("left".to_string()),
|
||||||
|
Some(false) => Some("right".to_string()),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn size(&self) -> Position {
|
pub fn size(&self) -> Position {
|
||||||
match &self.entity {
|
match &self.entity {
|
||||||
EntityType::Splitter(_) => Position::new(4, 2),
|
EntityType::Splitter { .. } => Position::new(4, 2),
|
||||||
EntityType::Unknown {
|
EntityType::Unknown {
|
||||||
name: _,
|
name: _,
|
||||||
size,
|
size,
|
||||||
|
|
@ -416,7 +478,7 @@ impl Entity {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EntityType::Splitter(_beltspeed) => (),
|
EntityType::Splitter { .. } => (),
|
||||||
EntityType::ElectricPole(electric_pole_type) => match electric_pole_type {
|
EntityType::ElectricPole(electric_pole_type) => match electric_pole_type {
|
||||||
ElectricPoleType::Small => v.add_symbol(
|
ElectricPoleType::Small => v.add_symbol(
|
||||||
(self.position - Position::new(1, 1)) / 2 + offset,
|
(self.position - Position::new(1, 1)) / 2 + offset,
|
||||||
|
|
@ -473,6 +535,7 @@ pub struct EntityKey(usize);
|
||||||
|
|
||||||
static ENTITY_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
static ENTITY_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Blueprint {
|
pub struct Blueprint {
|
||||||
entities: Vec<(EntityKey, Entity)>,
|
entities: Vec<(EntityKey, Entity)>,
|
||||||
keys: HashMap<EntityKey, usize>,
|
keys: HashMap<EntityKey, usize>,
|
||||||
|
|
@ -566,6 +629,8 @@ impl Blueprint {
|
||||||
.maybe_underground_type(e.get_maybe_underground_type_string())
|
.maybe_underground_type(e.get_maybe_underground_type_string())
|
||||||
.maybe_override_stack_size(e.get_maybe_override_stack_size())
|
.maybe_override_stack_size(e.get_maybe_override_stack_size())
|
||||||
.maybe_recipe(e.get_maybe_recipe())
|
.maybe_recipe(e.get_maybe_recipe())
|
||||||
|
.maybe_input_priority(e.get_maybe_input_priority())
|
||||||
|
.maybe_output_priority(e.get_maybe_output_priority())
|
||||||
.build()
|
.build()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue