finished implimenting instrucitons

This commit is contained in:
Rachel
2024-03-26 21:03:19 -07:00
parent bcc10adaf0
commit 9792fddf1f
9 changed files with 1810 additions and 323 deletions

90
ic10emu/Cargo.lock generated
View File

@@ -71,10 +71,11 @@ dependencies = [
"convert_case",
"getrandom",
"itertools",
"phf",
"phf 0.11.2",
"phf_codegen",
"rand",
"regex",
"strum",
"strum_macros",
"thiserror",
"web-time",
@@ -122,13 +123,24 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "phf"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_macros",
"phf_shared 0.10.0",
"proc-macro-hack",
]
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
"phf_shared 0.11.2",
]
[[package]]
@@ -137,8 +149,18 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [
"phf_generator",
"phf_shared",
"phf_generator 0.11.2",
"phf_shared 0.11.2",
]
[[package]]
name = "phf_generator"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared 0.10.0",
"rand",
]
[[package]]
@@ -147,10 +169,33 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"phf_shared 0.11.2",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "phf_shared"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
@@ -166,6 +211,12 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.79"
@@ -255,6 +306,16 @@ version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "strum"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
dependencies = [
"phf 0.10.1",
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.2"
@@ -265,7 +326,18 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn",
"syn 2.0.53",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
@@ -296,7 +368,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.53",
]
[[package]]
@@ -338,7 +410,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 2.0.53",
"wasm-bindgen-shared",
]
@@ -360,7 +432,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.53",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View File

@@ -17,6 +17,7 @@ itertools = "0.12.1"
phf = "0.11.2"
rand = "0.8.5"
regex = "1.10.3"
strum = { version = "0.26.2", features = ["derive", "phf", "strum_macros"] }
strum_macros = "0.26.2"
thiserror = "1.0.58"

View File

@@ -1,20 +1,90 @@
use convert_case::{Case, Casing};
use std::{
collections::HashSet,
collections::{HashMap, HashSet},
env,
fmt::Display,
fs::{self, File},
io::{BufWriter, Write},
path::Path,
str::FromStr,
};
fn write_logictypes(logictypes_grammar: &mut HashSet<String>) {
trait PrimitiveRepr {}
impl PrimitiveRepr for u8 {}
impl PrimitiveRepr for u16 {}
impl PrimitiveRepr for u32 {}
impl PrimitiveRepr for u64 {}
impl PrimitiveRepr for u128 {}
impl PrimitiveRepr for usize {}
impl PrimitiveRepr for i8 {}
impl PrimitiveRepr for i16 {}
impl PrimitiveRepr for i32 {}
impl PrimitiveRepr for i64 {}
impl PrimitiveRepr for i128 {}
impl PrimitiveRepr for isize {}
struct EnumVariant<P>
where
P: Display + FromStr,
{
pub aliases: Vec<String>,
pub value: Option<P>,
pub depricated: bool,
}
fn write_repr_enum<T: std::io::Write, I, P>(
writer: &mut BufWriter<T>,
name: &str,
variants: &I,
use_phf: bool,
) where
P: Display + FromStr,
for<'a> &'a I: IntoIterator<Item = (&'a String, &'a EnumVariant<P>)>,
{
let additional_strum = if use_phf { "#[strum(use_phf)]\n" } else { "" };
write!(
writer,
"#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash, EnumString, AsRefStr, EnumProperty, EnumIter)]\n\
{additional_strum}\
pub enum {name} {{\n"
)
.unwrap();
for (name, variant) in variants.into_iter() {
let variant_name = name.to_case(Case::Pascal);
let mut serialize = vec![name.clone()];
serialize.extend(variant.aliases.iter().cloned());
let serialize_str = serialize
.into_iter()
.map(|s| format!("serialize = \"{s}\""))
.collect::<Vec<String>>()
.join(", ");
let depricated_str = if variant.depricated {
", depricated = \"true\"".to_string()
} else {
"".to_string()
};
let props_str = if let Some(val) = &variant.value {
format!(", props( value = \"{val}\"{depricated_str})")
} else {
"".to_string()
};
write!(
writer,
" #[strum({serialize_str}{props_str})] {variant_name},\n"
)
.unwrap();
}
write!(writer, "}}\n").unwrap();
}
fn write_logictypes() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("logictypes.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut logictype_lookup_map_builder = ::phf_codegen::Map::new();
let mut logictypes: HashMap<String, EnumVariant<u8>> = HashMap::new();
let l_infile = Path::new("data/logictypes.txt");
let l_contents = fs::read_to_string(l_infile).unwrap();
@@ -23,14 +93,41 @@ fn write_logictypes(logictypes_grammar: &mut HashSet<String>) {
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
let docs = it.next();
let depricated = docs
.map(|docs| docs.trim().to_uppercase() == "DEPRECATED")
.unwrap_or(false);
logictypes_grammar.insert(name.to_string());
if let Some(v) = val {
logictype_lookup_map_builder.entry(name, &format!("{}u8", v));
if let Some(val) = val {
if let Some((_other_name, variant)) = logictypes
.iter_mut()
.find(|(_, variant)| variant.value == Some(val))
{
variant.aliases.push(name.to_string());
variant.depricated = depricated;
} else {
logictypes.insert(
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: Some(val),
depricated,
},
);
}
} else {
logictypes.insert(
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
depricated,
},
);
}
}
let mut slotlogictype_lookup_map_builder = ::phf_codegen::Map::new();
let mut slotlogictypes: HashMap<String, EnumVariant<u8>> = HashMap::new();
let sl_infile = Path::new("data/slotlogictypes.txt");
let sl_contents = fs::read_to_string(sl_infile).unwrap();
@@ -39,32 +136,50 @@ fn write_logictypes(logictypes_grammar: &mut HashSet<String>) {
let name = it.next().unwrap();
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
let docs = it.next();
let depricated = docs
.map(|docs| docs.trim().to_uppercase() == "DEPRECATED")
.unwrap_or(false);
logictypes_grammar.insert(name.to_string());
if let Some(v) = val {
slotlogictype_lookup_map_builder.entry(name, &format!("{}u8", v));
if let Some(val) = val {
if let Some((_other_name, variant)) = slotlogictypes
.iter_mut()
.find(|(_, variant)| variant.value == Some(val))
{
variant.aliases.push(name.to_string());
variant.depricated = depricated;
} else {
slotlogictypes.insert(
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: Some(val),
depricated,
},
);
}
} else {
slotlogictypes.insert(
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
depricated,
},
);
}
}
write!(
&mut writer,
"pub(crate) const LOGIC_TYPE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
logictype_lookup_map_builder.build()
)
.unwrap();
write_repr_enum(&mut writer, "LogicType", &logictypes, true);
println!("cargo:rerun-if-changed=data/logictypes.txt");
write!(
&mut writer,
"pub(crate) const SLOT_TYPE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
slotlogictype_lookup_map_builder.build()
)
.unwrap();
write_repr_enum(&mut writer, "SlotLogicType", &slotlogictypes, true);
println!("cargo:rerun-if-changed=data/slotlogictypes.txt");
}
fn write_enums(enums_grammar: &mut HashSet<String>) {
fn write_enums() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("enums.rs");
@@ -83,7 +198,6 @@ fn write_enums(enums_grammar: &mut HashSet<String>) {
let val: Option<u8> = val_str.parse().ok();
if !check_set.contains(name) {
enums_grammar.insert(name.to_string());
check_set.insert(name);
}
@@ -102,14 +216,14 @@ fn write_enums(enums_grammar: &mut HashSet<String>) {
println!("cargo:rerun-if-changed=data/enums.txt");
}
fn write_modes(logictypes_grammar: &mut HashSet<String>) {
fn write_modes() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("modes.rs");
let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file);
let mut batchmode_lookup_map_builder = ::phf_codegen::Map::new();
let mut batchmodes: HashMap<String, EnumVariant<u8>> = HashMap::new();
let b_infile = Path::new("data/batchmodes.txt");
let b_contents = fs::read_to_string(b_infile).unwrap();
@@ -119,13 +233,35 @@ fn write_modes(logictypes_grammar: &mut HashSet<String>) {
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
logictypes_grammar.insert(name.to_string());
if let Some(v) = val {
batchmode_lookup_map_builder.entry(name, &format!("{}u8", v));
if let Some(val) = val {
if let Some((_other_name, variant)) = batchmodes
.iter_mut()
.find(|(_, variant)| variant.value == Some(val))
{
variant.aliases.push(name.to_string());
} else {
batchmodes.insert(
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: Some(val),
depricated: false,
},
);
}
} else {
batchmodes.insert(
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
depricated: false,
},
);
}
}
let mut reagentmode_lookup_map_builder = ::phf_codegen::Map::new();
let mut reagentmodes: HashMap<String, EnumVariant<u8>> = HashMap::new();
let r_infile = Path::new("data/reagentmodes.txt");
let r_contents = fs::read_to_string(r_infile).unwrap();
@@ -135,32 +271,44 @@ fn write_modes(logictypes_grammar: &mut HashSet<String>) {
let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok();
logictypes_grammar.insert(name.to_string());
if let Some(v) = val {
reagentmode_lookup_map_builder.entry(name, &format!("{}u8", v));
if let Some(val) = val {
if let Some((_other_name, variant)) = reagentmodes
.iter_mut()
.find(|(_, variant)| variant.value == Some(val))
{
variant.aliases.push(name.to_string());
} else {
reagentmodes.insert(
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: Some(val),
depricated: false,
},
);
}
} else {
reagentmodes.insert(
name.to_string(),
EnumVariant {
aliases: Vec::new(),
value: val,
depricated: false,
},
);
}
}
write!(
&mut writer,
"pub(crate) const BATCH_MODE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
batchmode_lookup_map_builder.build()
)
.unwrap();
write_repr_enum(&mut writer, "BatchMode", &batchmodes, false);
println!("cargo:rerun-if-changed=data/batchmodes.txt");
write!(
&mut writer,
"pub(crate) const REAGENT_MODE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
reagentmode_lookup_map_builder.build()
)
.unwrap();
write_repr_enum(&mut writer, "ReagentMode", &reagentmodes, false);
println!("cargo:rerun-if-changed=data/reagentmodes.txt");
}
fn write_constants(constants_grammar: &mut HashSet<String>) {
fn write_constants() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("constants.rs");
@@ -176,7 +324,6 @@ fn write_constants(constants_grammar: &mut HashSet<String>) {
let name = it.next().unwrap();
let constant = it.next().unwrap();
constants_grammar.insert(name.to_string());
constants_lookup_map_builder.entry(name, constant);
}
@@ -248,14 +395,11 @@ fn write_instructions_enum() {
}
fn main() {
let mut logictype_grammar = HashSet::new();
let mut enums_grammar = HashSet::new();
let mut constants_grammar = HashSet::new();
// write_instructions();
write_logictypes(&mut logictype_grammar);
write_modes(&mut logictype_grammar);
write_constants(&mut constants_grammar);
write_enums(&mut enums_grammar);
write_logictypes();
write_modes();
write_constants();
write_enums();
write_instructions_enum();
}

View File

@@ -75,7 +75,6 @@ j VALUE
jal VALUE
jr VALUE
l REGISTER DEVICE LOGIC_TYPE
label NAME REGISTER_DEVICE
lb REGISTER DEVICE_TYPE LOGIC_TYPE BATCH_MODE
lbn REGISTER DEVICE_TYPE DEVICE_NAME LOGIC_TYPE BATCH_MODE
lbns REGISTER DEVICE_TYPE DEVICE_NAME INDEX SLOT_LOGIC_TYPE BATCH_MODE

View File

@@ -11,14 +11,14 @@ Bypass None Bypasses some internal behavoiur of the atmospherics device.
CelestialHash 242
CelestialParentHash 250
Channel None Channel on a cable network which should be considered volatile
Channel0 165
Channel1 166
Channel2 167
Channel3 168
Channel4 169
Channel5 170
Channel6 171
Channel7 172
Channel0 165 Channel on a cable network which should be considered volatile
Channel1 166 Channel on a cable network which should be considered volatile
Channel2 167 Channel on a cable network which should be considered volatile
Channel3 168 Channel on a cable network which should be considered volatile
Channel4 169 Channel on a cable network which should be considered volatile
Channel5 170 Channel on a cable network which should be considered volatile
Channel6 171 Channel on a cable network which should be considered volatile
Channel7 172 Channel on a cable network which should be considered volatile
Charge 11 The current charge the device has
ClearMemory 62 When set to 1, clears the counter memory (e.g. ExportCount). Will set itself back to 0 when actioned
CollectableGoods 101
@@ -45,9 +45,9 @@ EnvironmentEfficiency 104 The Environment Efficiency reported by the machine, as
Error 4 1 if device is in error state, otherwise 0
ExhaustVelocity 235
ExportCount 63 How many items exported since last ClearMemory
ExportQuantity None Total quantity of items exported by the device
ExportSlotHash None DEPRECATED
ExportSlotOccupant None DEPRECATED
ExportQuantity 31 Total quantity of items exported by the device
ExportSlotHash 42 DEPRECATED
ExportSlotOccupant 32 DEPRECATED
Filtration 74 The current state of the filtration system, for example Filtration = 1 for a Hardsuit sets filtration to On
FlightControlRule 236
Flush 174 Set to 1 to activate the flush function on the device
@@ -61,9 +61,9 @@ Horizontal 20 Horizontal setting of the device
HorizontalRatio 34 Radio of horizontal setting for device
Idle 37 Returns 1 if the device is currently idle, otherwise 0
ImportCount 64 How many items imported since last ClearMemory
ImportQuantity None Total quantity of items imported by the device
ImportSlotHash None DEPRECATED
ImportSlotOccupant None DEPRECATED
ImportQuantity 29 Total quantity of items imported by the device
ImportSlotHash 43 DEPRECATED
ImportSlotOccupant 30 DEPRECATED
Inclination 246
Index 241
InterrogationProgress 157 Progress of this sattellite dish's interrogation of its current target, as a ratio from 0-1
@@ -72,10 +72,10 @@ Lock 10 1 if device is locked, otherwise 0, can be set in most devices and preve
ManualResearchRequiredPod 94
Mass 219 The total Mass of the rocket in kilograms including fuel and cargo. The more massive the rocket the more fuel will be required to move to a new location in space.
Maximum 23 Maximum setting of the device
MinWattsToContact None Minimum required amount of watts from the dish hitting the target trader contact to start interrogating the contact
MinWattsToContact 163 Minimum required amount of watts from the dish hitting the target trader contact to start interrogating the contact
MineablesInQueue 96
MineablesInVicinity 95
MinimumWattsToContact 163
MinimumWattsToContact 163 Minimum required amount of watts from the dish hitting the target trader contact to start interrogating the contact
Mode 3 Integer for mode state, different devices will have different mode states available to them
NextWeatherEventTime 97
None None No description
@@ -105,7 +105,7 @@ PlantHealth2 None DEPRECATED
PlantHealth3 None DEPRECATED
PlantHealth4 None DEPRECATED
PositionX 76 The current position in X dimension in world coordinates
PositionY None The current position in Y dimension in world coordinates
PositionY 77 The current position in Y dimension in world coordinates
PositionZ 78 The current position in Z dimension in world coordinates
Power 1 Can be read to return if the device is correctly powered or not, set via the power system, return 1 if powered and 0 if not
PowerActual 26 How much energy the device or network is actually using
@@ -206,9 +206,9 @@ Rpm 155 The number of revolutions per minute that the device's spinning mechanis
SemiMajorAxis 248
Setting 12 A variable setting that can be read or written, depending on the device
SettingInput 91
SettingInputHash None The input setting for the device
SettingInputHash 91 The input setting for the device
SettingOutput 92
SettingOutputHash None The output setting for the device
SettingOutputHash 91 The output setting for the device
SignalID 87 Returns the contact ID of the strongest signal from this Satellite
SignalStrength 86 Returns the degree offset of the strongest contact
SizeX 160 Size on the X (right) axis of the object in largeGrids (a largeGrid is 2meters)

View File

@@ -4,6 +4,87 @@ use itertools::Itertools;
use std::error::Error;
use std::fmt::Display;
use std::str::FromStr;
use strum::EnumProperty;
pub mod generated {
use super::ParseError;
use crate::interpreter::ICError;
use std::str::FromStr;
use strum::AsRefStr;
use strum::Display;
use strum::EnumIter;
use strum::EnumProperty;
use strum::EnumString;
use strum::IntoEnumIterator;
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
include!(concat!(env!("OUT_DIR"), "/logictypes.rs"));
include!(concat!(env!("OUT_DIR"), "/modes.rs"));
include!(concat!(env!("OUT_DIR"), "/constants.rs"));
include!(concat!(env!("OUT_DIR"), "/enums.rs"));
impl TryFrom<f64> for LogicType {
type Error = ICError;
fn try_from(value: f64) -> Result<Self, <LogicType as TryFrom<f64>>::Error> {
if let Some(lt) = LogicType::iter().find(|lt| {
lt.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64 == value)
.unwrap_or(false)
}) {
Ok(lt)
} else {
Err(crate::interpreter::ICError::UnknownLogicType(value))
}
}
}
impl TryFrom<f64> for SlotLogicType {
type Error = ICError;
fn try_from(value: f64) -> Result<Self, <SlotLogicType as TryFrom<f64>>::Error> {
if let Some(slt) = SlotLogicType::iter().find(|lt| {
lt.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64 == value)
.unwrap_or(false)
}) {
Ok(slt)
} else {
Err(crate::interpreter::ICError::UnknownSlotLogicType(value))
}
}
}
impl TryFrom<f64> for BatchMode {
type Error = ICError;
fn try_from(value: f64) -> Result<Self, <BatchMode as TryFrom<f64>>::Error> {
if let Some(bm) = BatchMode::iter().find(|lt| {
lt.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64 == value)
.unwrap_or(false)
}) {
Ok(bm)
} else {
Err(crate::interpreter::ICError::UnknownBatchMode(value))
}
}
}
impl TryFrom<f64> for ReagentMode {
type Error = ICError;
fn try_from(value: f64) -> Result<Self, <ReagentMode as TryFrom<f64>>::Error> {
if let Some(rm) = ReagentMode::iter().find(|lt| {
lt.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64 == value)
.unwrap_or(false)
}) {
Ok(rm)
} else {
Err(crate::interpreter::ICError::UnknownReagentMode(value))
}
}
}
}
pub use generated::*;
#[derive(Debug, Clone)]
pub struct ParseError {
@@ -57,12 +138,6 @@ impl ParseError {
}
}
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
include!(concat!(env!("OUT_DIR"), "/logictypes.rs"));
include!(concat!(env!("OUT_DIR"), "/modes.rs"));
include!(concat!(env!("OUT_DIR"), "/constants.rs"));
include!(concat!(env!("OUT_DIR"), "/enums.rs"));
pub fn parse(code: &str) -> Result<Vec<Line>, ParseError> {
code.lines()
.enumerate()
@@ -203,10 +278,13 @@ pub enum Operand {
},
DeviceSpec {
device: Device,
channel: Option<u32>,
connection: Option<u32>,
},
Number(Number),
LogicType(LogicType),
SlotLogicType(SlotLogicType),
BatchMode(BatchMode),
ReagentMode(ReagentMode),
Identifier(Identifier),
}
@@ -218,16 +296,34 @@ impl Operand {
target,
} => ic.get_register(*indirection, *target),
&Operand::Number(num) => Ok(num.value()),
&Operand::LogicType(lt) => Ok(lt.value),
&Operand::LogicType(lt) => lt
.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64)
.ok_or(interpreter::ICError::TypeValueNotKnown),
&Operand::SlotLogicType(slt) => slt
.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64)
.ok_or(interpreter::ICError::TypeValueNotKnown),
&Operand::BatchMode(bm) => bm
.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64)
.ok_or(interpreter::ICError::TypeValueNotKnown),
&Operand::ReagentMode(rm) => rm
.get_str("value")
.map(|val| val.parse::<u8>().unwrap() as f64)
.ok_or(interpreter::ICError::TypeValueNotKnown),
&Operand::Identifier(ident) => ic.get_ident_value(&ident.name),
&Operand::DeviceSpec { .. } => Err(interpreter::ICError::DeviceNotValue),
}
}
pub fn get_value_i64(&self, ic: &interpreter::IC, signed: bool) -> Result<i64, interpreter::ICError> {
pub fn get_value_i64(
&self,
ic: &interpreter::IC,
signed: bool,
) -> Result<i64, interpreter::ICError> {
let val = self.get_value(ic)?;
if val < -9.223372036854776E+18 {
if val < -9.223372036854776E+18 {
Err(interpreter::ICError::ShiftUnderflowI64)
} else if val <= 9.223372036854776E+18 {
Ok(interpreter::f64_to_i64(val, signed))
@@ -252,15 +348,15 @@ impl Operand {
ic: &interpreter::IC,
) -> Result<(Option<u16>, Option<u32>), interpreter::ICError> {
match &self {
&Operand::DeviceSpec { device, channel } => match device {
Device::Db => Ok((Some(ic.id), *channel)),
&Operand::DeviceSpec { device, connection } => match device {
Device::Db => Ok((Some(ic.id), *connection)),
Device::Numbered(p) => {
let dp = ic
.pins
.get(*p as usize)
.ok_or(interpreter::ICError::DeviceIndexOutOfRange(*p as f64))
.copied()?;
Ok((dp, *channel))
Ok((dp, *connection))
}
Device::Indirect {
indirection,
@@ -272,7 +368,7 @@ impl Operand {
.get(val as usize)
.ok_or(interpreter::ICError::DeviceIndexOutOfRange(val))
.copied()?;
Ok((dp, *channel))
Ok((dp, *connection))
}
},
&Operand::Identifier(id) => ic.get_ident_device_id(&id.name),
@@ -322,20 +418,20 @@ impl FromStr for Operand {
['d', rest @ ..] => match rest {
['b'] => Ok(Operand::DeviceSpec {
device: Device::Db,
channel: None,
connection: None,
}),
['b', ':', chan @ ..] => {
if chan.into_iter().all(|c| c.is_digit(10)) {
Ok(Operand::DeviceSpec {
device: Device::Db,
channel: Some(String::from_iter(chan).parse().unwrap()),
connection: Some(String::from_iter(chan).parse().unwrap()),
})
} else {
Err(ParseError {
line: 0,
start: 3,
end: 3,
msg: format!("Invalid device channel specifier"),
msg: format!("Invalid device connection specifier"),
})
}
}
@@ -346,25 +442,25 @@ impl FromStr for Operand {
.take_while_ref(|c| c.is_digit(10))
.collect::<String>();
let target = target_str.parse::<u32>().ok();
let channel = {
let connection = {
if rest_iter.peek() == Some(&&':') {
// take off ':'
rest_iter.next();
let channel_str = rest_iter
let connection_str = rest_iter
.take_while_ref(|c| c.is_digit(10))
.collect::<String>();
let channel = channel_str.parse::<u32>().unwrap();
let connection = connection_str.parse::<u32>().unwrap();
let trailing = rest_iter.clone().collect::<Vec<_>>();
if trailing.len() == 0 {
Ok(Some(channel))
Ok(Some(connection))
} else {
let start =
2 + indirection + target_str.len() + 1 + channel_str.len();
2 + indirection + target_str.len() + 1 + connection_str.len();
Err(ParseError {
line: 0,
start,
end: start,
msg: format!("Invalid device channel specifier"),
msg: format!("Invalid device connection specifier"),
})
}
} else {
@@ -379,7 +475,7 @@ impl FromStr for Operand {
indirection: indirection as u32,
target,
},
channel,
connection: connection,
})
} else {
Err(ParseError {
@@ -399,24 +495,24 @@ impl FromStr for Operand {
.take_while_ref(|c| c.is_digit(10))
.collect::<String>();
let target = target_str.parse::<u32>().ok();
let channel = {
let connection = {
if rest_iter.peek() == Some(&&':') {
// take off ':'
rest_iter.next();
let channel_str = rest_iter
let connection_str = rest_iter
.take_while_ref(|c| c.is_digit(10))
.collect::<String>();
let channel = channel_str.parse::<u32>().unwrap();
let connection = connection_str.parse::<u32>().unwrap();
let trailing = rest_iter.clone().collect::<Vec<_>>();
if trailing.len() == 0 {
Ok(Some(channel))
Ok(Some(connection))
} else {
let start = 1 + target_str.len() + 1 + channel_str.len();
let start = 1 + target_str.len() + 1 + connection_str.len();
Err(ParseError {
line: 0,
start,
end: start,
msg: format!("Invalid device channel specifier"),
msg: format!("Invalid device connection specifier"),
})
}
} else {
@@ -428,7 +524,7 @@ impl FromStr for Operand {
if trailing.len() == 0 {
Ok(Operand::DeviceSpec {
device: Device::Numbered(target),
channel,
connection: connection,
})
} else {
Err(ParseError {
@@ -537,26 +633,14 @@ impl FromStr for Operand {
Ok(Operand::Number(Number::Constant(*val)))
} else if let Some(val) = ENUM_LOOKUP.get(s) {
Ok(Operand::Number(Number::Enum(*val as f64)))
} else if let Some(val) = LOGIC_TYPE_LOOKUP.get(s) {
Ok(Operand::LogicType(LogicType {
name: s.to_string(),
value: *val as f64,
}))
} else if let Some(val) = SLOT_TYPE_LOOKUP.get(s) {
Ok(Operand::LogicType(LogicType {
name: s.to_string(),
value: *val as f64,
}))
} else if let Some(val) = BATCH_MODE_LOOKUP.get(s) {
Ok(Operand::LogicType(LogicType {
name: s.to_string(),
value: *val as f64,
}))
} else if let Some(val) = REAGENT_MODE_LOOKUP.get(s) {
Ok(Operand::LogicType(LogicType {
name: s.to_string(),
value: *val as f64,
}))
} else if let Ok(lt) = LogicType::from_str(s) {
Ok(Operand::LogicType(lt))
} else if let Ok(slt) = SlotLogicType::from_str(s) {
Ok(Operand::SlotLogicType(slt))
} else if let Ok(bm) = BatchMode::from_str(s) {
Ok(Operand::BatchMode(bm))
} else if let Ok(rm) = ReagentMode::from_str(s) {
Ok(Operand::ReagentMode(rm))
} else {
Ok(Operand::Identifier(s.parse::<Identifier>()?))
}
@@ -565,11 +649,11 @@ impl FromStr for Operand {
}
}
#[derive(PartialEq, Debug)]
pub struct LogicType {
pub name: String,
pub value: f64,
}
// #[derive(PartialEq, Debug)]
// pub struct LogicType {
// pub name: String,
// pub value: f64,
// }
#[derive(PartialEq, Debug)]
pub struct Label {
@@ -673,6 +757,7 @@ impl Number {
#[cfg(test)]
mod tests {
use super::generated::*;
use super::*;
#[test]
@@ -687,12 +772,9 @@ mod tests {
operands: vec![
Operand::DeviceSpec {
device: Device::Numbered(0),
channel: None,
connection: None,
},
Operand::LogicType(LogicType {
name: "Setting".to_string(),
value: 12.0,
},),
Operand::LogicType(LogicType::Setting),
Operand::Number(Number::Float(0.0)),
],
},),),
@@ -800,7 +882,7 @@ mod tests {
},),
Operand::DeviceSpec {
device: Device::Numbered(0),
channel: None,
connection: None,
},
],
},),),
@@ -812,7 +894,7 @@ mod tests {
operands: vec![
Operand::DeviceSpec {
device: Device::Numbered(0),
channel: None,
connection: None,
},
Operand::Number(Number::Float(12.0)),
Operand::Number(Number::Float(0.0)),
@@ -871,12 +953,9 @@ mod tests {
indirection: 0,
target: 15,
},
channel: None,
connection: None,
},
Operand::LogicType(LogicType {
name: "RatioWater".to_string(),
value: 19.0,
},),
Operand::LogicType(LogicType::RatioWater),
],
},),),
comment: None,

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,14 @@
use core::f64;
use std::collections::{HashMap, HashSet};
mod tokens;
mod grammar;
mod interpreter;
mod rand_mscorlib;
mod tokens;
use grammar::{BatchMode, LogicType, ReagentMode, SlotLogicType};
use interpreter::ICError;
use itertools::Itertools;
use thiserror::Error;
#[derive(Error, Debug)]
@@ -16,29 +18,47 @@ pub enum VMError {
#[error("Device with id '{0}' does not have a IC Slot")]
NoIC(u16),
#[error("IC encoutered an error: {0}")]
ICError(#[from] interpreter::ICError)
ICError(#[from] ICError),
#[error("Invalid network ID {0}")]
InvalidNetwork(u16),
}
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub enum FieldType {
Read,
Write,
ReadWrite
ReadWrite,
}
#[derive(Debug)]
pub struct LogicField {
pub field_type: FieldType,
pub value: f64,
}
#[derive(Debug, Default)]
pub struct Slot {
pub fields: HashMap<grammar::SlotLogicType, LogicField>,
}
#[derive(Debug, Default, Clone, Copy)]
pub enum Connection {
CableNetwork(Option<u16>),
#[default]
Other,
}
#[derive(Debug, Default)]
pub struct Device {
pub id: u16,
pub fields: HashMap<u32, LogicField>,
pub ic: Option<interpreter::IC>
pub name: Option<String>,
pub name_hash: Option<f64>,
pub fields: HashMap<grammar::LogicType, LogicField>,
pub slots: Vec<Slot>,
pub reagents: HashMap<ReagentMode, HashMap<i32, f64>>,
pub ic: Option<interpreter::IC>,
pub connections: [Connection; 8],
pub prefab_hash: Option<i32>,
}
#[derive(Debug)]
@@ -70,8 +90,10 @@ impl IdSequenceGenerator {
pub struct VM {
pub ics: HashSet<u16>,
pub devices: HashMap<u16, Device>,
pub networks: Vec<Network>,
pub networks: HashMap<u16, Network>,
pub default_network: u16,
id_gen: IdSequenceGenerator,
network_id_gen: IdSequenceGenerator,
random: crate::rand_mscorlib::Random,
}
@@ -84,9 +106,57 @@ impl Default for Network {
}
}
#[derive(Debug, Error)]
pub enum NetworkError {
#[error("")]
ChannelIndexOutOfRange,
}
impl Network {
pub fn contains(&self, ids: &[u16]) -> bool {
ids.iter().all(|id| self.devices.contains(id))
}
pub fn add(&mut self, id: u16) -> bool {
self.devices.insert(id)
}
pub fn remove(&mut self, id: u16) -> bool {
self.devices.remove(&id)
}
pub fn set_channel(&mut self, chan: usize, val: f64) -> Result<f64, NetworkError> {
if chan > 7 {
Err(NetworkError::ChannelIndexOutOfRange)
} else {
let last = self.channels[chan];
self.channels[chan] = val;
Ok(last)
}
}
pub fn get_channel(&self, chan: usize) -> Result<f64, NetworkError> {
if chan > 7 {
Err(NetworkError::ChannelIndexOutOfRange)
} else {
Ok(self.channels[chan])
}
}
}
impl Device {
pub fn new(id: u16) -> Self {
Device { id, fields: HashMap::new(), ic: None }
Device {
id,
name: None,
name_hash: None,
fields: HashMap::new(),
slots: Vec::new(),
reagents: HashMap::new(),
ic: None,
connections: [Connection::default(); 8],
prefab_hash: None,
}
}
pub fn with_ic(id: u16) -> Self {
@@ -94,32 +164,207 @@ impl Device {
device.ic = Some(interpreter::IC::new(id));
device
}
}
pub fn get_network_id(&self, connection: usize) -> Result<u16, ICError> {
if connection >= 8 {
Err(ICError::ConnecitonIndexOutOFRange(connection as u32))
} else {
if let Connection::CableNetwork(network_id) = self.connections[connection] {
if let Some(network_id) = network_id {
Ok(network_id)
} else {
Err(ICError::NetworkNotConnected(connection as u32))
}
} else {
Err(ICError::NotDataConnection(connection as u32))
}
}
}
pub fn get_field(&self, typ: grammar::LogicType) -> Result<f64, ICError> {
if let Some(field) = self.fields.get(&typ) {
if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite {
Ok(field.value)
} else {
Err(ICError::WriteOnlyField(typ.to_string()))
}
} else {
Err(ICError::DeviceHasNoField(typ.to_string()))
}
}
pub fn set_field(&mut self, typ: grammar::LogicType, val: f64) -> Result<(), ICError> {
if let Some(field) = self.fields.get_mut(&typ) {
if field.field_type == FieldType::Write || field.field_type == FieldType::ReadWrite {
field.value = val;
Ok(())
} else {
Err(ICError::ReadOnlyField(typ.to_string()))
}
} else {
Err(ICError::DeviceHasNoField(typ.to_string()))
}
}
pub fn get_slot_field(&self, index: f64, typ: grammar::SlotLogicType) -> Result<f64, ICError> {
if let Some(field) = self
.slots
.get(index as usize)
.ok_or(ICError::SlotIndexOutOfRange(index))?
.fields
.get(&typ)
{
if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite {
Ok(field.value)
} else {
Err(ICError::WriteOnlyField(typ.to_string()))
}
} else {
Err(ICError::DeviceHasNoField(typ.to_string()))
}
}
pub fn set_slot_field(
&mut self,
index: f64,
typ: grammar::SlotLogicType,
val: f64,
) -> Result<(), ICError> {
if let Some(field) = self
.slots
.get_mut(index as usize)
.ok_or(ICError::SlotIndexOutOfRange(index))?
.fields
.get_mut(&typ)
{
if field.field_type == FieldType::Write || field.field_type == FieldType::ReadWrite {
field.value = val;
Ok(())
} else {
Err(ICError::ReadOnlyField(typ.to_string()))
}
} else {
Err(ICError::DeviceHasNoField(typ.to_string()))
}
}
pub fn get_reagent(&self, rm: &ReagentMode, reagent: f64) -> f64 {
if let Some(mode) = self.reagents.get(rm) {
if let Some(val) = mode.get(&(reagent as i32)) {
return *val;
}
}
0.0
}
}
impl VM {
pub fn new() -> Self {
let id_gen = IdSequenceGenerator::default();
let mut network_id_gen = IdSequenceGenerator::default();
let default_network = Network::default();
let mut networks = HashMap::new();
let default_network_key = network_id_gen.next();
networks.insert(default_network_key, default_network);
let mut vm = VM {
ics: HashSet::new(),
devices: HashMap::new(),
networks: vec![default_network],
networks,
default_network: default_network_key,
id_gen,
network_id_gen,
random: crate::rand_mscorlib::Random::new(),
};
vm.add_ic();
let _ = vm.add_ic(None);
vm
}
pub fn add_ic(&mut self) {
let device = Device::with_ic(self.id_gen.next());
self.ics.insert(device.id);
self.devices.insert(device.id, device);
fn new_device(&mut self) -> Device {
Device::new(self.id_gen.next())
}
fn new_ic(&mut self) -> Device {
Device::with_ic(self.id_gen.next())
}
pub fn add_device(&mut self, network: Option<u16>) -> Result<u16, VMError> {
if let Some(n) = &network {
if !self.networks.contains_key(n) {
return Err(VMError::InvalidNetwork(*n));
}
}
let mut device = self.new_device();
if let Some(first_network) = device
.connections
.iter_mut()
.filter_map(|c| {
if let Connection::CableNetwork(c) = c {
Some(c)
} else {
None
}
})
.next()
{
first_network.replace(if let Some(network) = network {
network
} else {
self.default_network
});
}
let id = device.id;
self.devices.insert(id, device);
Ok(id)
}
pub fn add_ic(&mut self, network: Option<u16>) -> Result<u16, VMError> {
if let Some(n) = &network {
if !self.networks.contains_key(n) {
return Err(VMError::InvalidNetwork(*n));
}
}
let mut device = self.new_ic();
if let Some(first_network) = device
.connections
.iter_mut()
.filter_map(|c| {
if let Connection::CableNetwork(c) = c {
Some(c)
} else {
None
}
})
.next()
{
first_network.replace(if let Some(network) = network {
network
} else {
self.default_network
});
}
let id = device.id;
self.devices.insert(id, device);
self.ics.insert(id);
Ok(id)
}
pub fn add_network(&mut self) -> u16 {
let next_id = self.network_id_gen.next();
self.networks.insert(next_id, Network::default());
next_id
}
pub fn get_default_network(&mut self) -> &mut Network {
self.networks.get_mut(&self.default_network).unwrap()
}
pub fn get_network(&mut self, id: u16) -> Option<&mut Network> {
self.networks.get_mut(&id)
}
pub fn remove_ic(&mut self, id: u16) {
if self.ics.remove(&id) {
if self.ics.remove(&id) {
self.devices.remove(&id);
}
}
@@ -133,4 +378,294 @@ impl VM {
Ok(true)
}
pub fn get_device(&mut self, id: u16) -> Option<&mut Device> {
self.devices.get_mut(&id)
}
pub fn get_device_same_network(&mut self, source: u16, other: u16) -> Option<&mut Device> {
if self.devices_on_same_network(&[source, other]) {
self.get_device(other)
} else {
None
}
}
pub fn get_network_channel(&self, id: usize, channel: usize) -> Result<f64, ICError> {
let network = self
.networks
.get(&(id as u16))
.ok_or(ICError::BadNetworkId(id as u32))?;
Ok(network.channels[channel])
}
pub fn set_network_channel(&mut self, id: usize, channel: usize, val: f64) -> Result<(), ICError> {
let network = self
.networks
.get_mut(&(id as u16))
.ok_or(ICError::BadNetworkId(id as u32))?;
network.channels[channel] = val;
Ok(())
}
pub fn devices_on_same_network(&self, ids: &[u16]) -> bool {
for (_id, net) in self.networks.iter() {
if net.contains(ids) {
return true;
}
}
false
}
pub fn set_batch_device_field(
&mut self,
source: u16,
prefab: f64,
typ: LogicType,
val: f64,
) -> Result<(), ICError> {
let networks = &self.networks;
self.devices
.iter_mut()
.map(|(id, device)| {
if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab)
&& networks
.iter()
.any(|(_net_id, net)| net.contains(&[source, *id]))
{
device.set_field(typ, val)
} else {
Ok(())
}
})
.try_collect()
}
pub fn set_batch_device_slot_field(
&mut self,
source: u16,
prefab: f64,
index: f64,
typ: SlotLogicType,
val: f64,
) -> Result<(), ICError> {
let networks = &self.networks;
self.devices
.iter_mut()
.map(|(id, device)| {
if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab)
&& networks
.iter()
.any(|(_net_id, net)| net.contains(&[source, *id]))
{
device.set_slot_field(index, typ, val)
} else {
Ok(())
}
})
.try_collect()
}
pub fn set_batch_name_device_field(
&mut self,
source: u16,
prefab: f64,
name: f64,
typ: LogicType,
val: f64,
) -> Result<(), ICError> {
let networks = &self.networks;
self.devices
.iter_mut()
.map(|(id, device)| {
if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab)
&& Some(name) == device.name_hash
&& networks
.iter()
.any(|(_net_id, net)| net.contains(&[source, *id]))
{
device.set_field(typ, val)
} else {
Ok(())
}
})
.try_collect()
}
pub fn get_batch_device_field(
&mut self,
source: u16,
prefab: f64,
typ: LogicType,
mode: BatchMode,
) -> Result<f64, ICError> {
let networks = &self.networks;
let samples = self
.devices
.iter_mut()
.map(|(id, device)| {
if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab)
&& networks
.iter()
.any(|(_net_id, net)| net.contains(&[source, *id]))
{
device.get_field(typ).map(|val| Some(val))
} else {
Ok(None)
}
})
.collect::<Result<Vec<_>, ICError>>()?
.into_iter()
.filter_map(|val| {
val.map(|val| if val.is_nan() { None } else { Some(val) })
.flatten()
})
.collect_vec();
match mode {
BatchMode::Sum => Ok(samples.iter().sum()),
BatchMode::Average => Ok(samples.iter().copied().sum::<f64>() / samples.len() as f64),
BatchMode::Minimum => Ok(*samples
.iter()
.min_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(&0.0)),
BatchMode::Maximum => Ok(*samples
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(&0.0)),
}
}
pub fn get_batch_name_device_field(
&mut self,
source: u16,
prefab: f64,
name: f64,
typ: LogicType,
mode: BatchMode,
) -> Result<f64, ICError> {
let networks = &self.networks;
let samples = self
.devices
.iter_mut()
.map(|(id, device)| {
if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab)
&& Some(name) == device.name_hash
&& networks
.iter()
.any(|(_net_id, net)| net.contains(&[source, *id]))
{
device.get_field(typ).map(|val| Some(val))
} else {
Ok(None)
}
})
.collect::<Result<Vec<_>, ICError>>()?
.into_iter()
.filter_map(|val| {
val.map(|val| if val.is_nan() { None } else { Some(val) })
.flatten()
})
.collect_vec();
match mode {
BatchMode::Sum => Ok(samples.iter().sum()),
BatchMode::Average => Ok(samples.iter().copied().sum::<f64>() / samples.len() as f64),
BatchMode::Minimum => Ok(*samples
.iter()
.min_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(&0.0)),
BatchMode::Maximum => Ok(*samples
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(&0.0)),
}
}
pub fn get_batch_name_device_slot_field(
&mut self,
source: u16,
prefab: f64,
name: f64,
index: f64,
typ: SlotLogicType,
mode: BatchMode,
) -> Result<f64, ICError> {
let networks = &self.networks;
let samples = self
.devices
.iter_mut()
.map(|(id, device)| {
if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab)
&& Some(name) == device.name_hash
&& networks
.iter()
.any(|(_net_id, net)| net.contains(&[source, *id]))
{
device.get_slot_field(index, typ).map(|val| Some(val))
} else {
Ok(None)
}
})
.collect::<Result<Vec<_>, ICError>>()?
.into_iter()
.filter_map(|val| {
val.map(|val| if val.is_nan() { None } else { Some(val) })
.flatten()
})
.collect_vec();
match mode {
BatchMode::Sum => Ok(samples.iter().sum()),
BatchMode::Average => Ok(samples.iter().copied().sum::<f64>() / samples.len() as f64),
BatchMode::Minimum => Ok(*samples
.iter()
.min_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(&0.0)),
BatchMode::Maximum => Ok(*samples
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(&0.0)),
}
}
pub fn get_batch_device_slot_field(
&mut self,
source: u16,
prefab: f64,
index: f64,
typ: SlotLogicType,
mode: BatchMode,
) -> Result<f64, ICError> {
let networks = &self.networks;
let samples = self
.devices
.iter_mut()
.map(|(id, device)| {
if device.fields.get(&LogicType::PrefabHash).map(|f| f.value) == Some(prefab)
&& networks
.iter()
.any(|(_net_id, net)| net.contains(&[source, *id]))
{
device.get_slot_field(index, typ).map(|val| Some(val))
} else {
Ok(None)
}
})
.collect::<Result<Vec<_>, ICError>>()?
.into_iter()
.filter_map(|val| {
val.map(|val| if val.is_nan() { None } else { Some(val) })
.flatten()
})
.collect_vec();
match mode {
BatchMode::Sum => Ok(samples.iter().sum()),
BatchMode::Average => Ok(samples.iter().copied().sum::<f64>() / samples.len() as f64),
BatchMode::Minimum => Ok(*samples
.iter()
.min_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(&0.0)),
BatchMode::Maximum => Ok(*samples
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(&0.0)),
}
}
}

View File

@@ -80,7 +80,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.53",
]
[[package]]
@@ -139,10 +139,11 @@ dependencies = [
"convert_case",
"getrandom",
"itertools",
"phf",
"phf 0.11.2",
"phf_codegen",
"rand",
"regex",
"strum",
"strum_macros",
"thiserror",
"web-time",
@@ -203,13 +204,24 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "phf"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_macros",
"phf_shared 0.10.0",
"proc-macro-hack",
]
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
"phf_shared 0.11.2",
]
[[package]]
@@ -218,8 +230,18 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [
"phf_generator",
"phf_shared",
"phf_generator 0.11.2",
"phf_shared 0.11.2",
]
[[package]]
name = "phf_generator"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared 0.10.0",
"rand",
]
[[package]]
@@ -228,10 +250,33 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"phf_shared 0.11.2",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
"proc-macro-hack",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "phf_shared"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
@@ -259,6 +304,12 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.79"
@@ -357,6 +408,16 @@ dependencies = [
"autocfg",
]
[[package]]
name = "strum"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
dependencies = [
"phf 0.10.1",
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.2"
@@ -367,7 +428,18 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn",
"syn 2.0.53",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
@@ -398,7 +470,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.53",
]
[[package]]
@@ -440,7 +512,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 2.0.53",
"wasm-bindgen-shared",
]
@@ -475,7 +547,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.53",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]