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", "convert_case",
"getrandom", "getrandom",
"itertools", "itertools",
"phf", "phf 0.11.2",
"phf_codegen", "phf_codegen",
"rand", "rand",
"regex", "regex",
"strum",
"strum_macros", "strum_macros",
"thiserror", "thiserror",
"web-time", "web-time",
@@ -122,13 +123,24 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 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]] [[package]]
name = "phf" name = "phf"
version = "0.11.2" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [ dependencies = [
"phf_shared", "phf_shared 0.11.2",
] ]
[[package]] [[package]]
@@ -137,8 +149,18 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [ dependencies = [
"phf_generator", "phf_generator 0.11.2",
"phf_shared", "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]] [[package]]
@@ -147,10 +169,33 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [ dependencies = [
"phf_shared", "phf_shared 0.11.2",
"rand", "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]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.11.2" version = "0.11.2"
@@ -166,6 +211,12 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.79" version = "1.0.79"
@@ -255,6 +306,16 @@ version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" 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]] [[package]]
name = "strum_macros" name = "strum_macros"
version = "0.26.2" version = "0.26.2"
@@ -265,7 +326,18 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "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]] [[package]]
@@ -296,7 +368,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.53",
] ]
[[package]] [[package]]
@@ -338,7 +410,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.53",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@@ -360,7 +432,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.53",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]

View File

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

View File

@@ -1,20 +1,90 @@
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use std::{ use std::{
collections::HashSet, collections::{HashMap, HashSet},
env, env,
fmt::Display,
fs::{self, File}, fs::{self, File},
io::{BufWriter, Write}, io::{BufWriter, Write},
path::Path, 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 out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("logictypes.rs"); let dest_path = Path::new(&out_dir).join("logictypes.rs");
let output_file = File::create(dest_path).unwrap(); let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file); 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_infile = Path::new("data/logictypes.txt");
let l_contents = fs::read_to_string(l_infile).unwrap(); 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 name = it.next().unwrap();
let val_str = it.next().unwrap(); let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok(); 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(val) = val {
if let Some(v) = val { if let Some((_other_name, variant)) = logictypes
logictype_lookup_map_builder.entry(name, &format!("{}u8", v)); .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_infile = Path::new("data/slotlogictypes.txt");
let sl_contents = fs::read_to_string(sl_infile).unwrap(); 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 name = it.next().unwrap();
let val_str = it.next().unwrap(); let val_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok(); 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(val) = val {
if let Some(v) = val { if let Some((_other_name, variant)) = slotlogictypes
slotlogictype_lookup_map_builder.entry(name, &format!("{}u8", v)); .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!( write_repr_enum(&mut writer, "LogicType", &logictypes, true);
&mut writer,
"pub(crate) const LOGIC_TYPE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
logictype_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/logictypes.txt"); println!("cargo:rerun-if-changed=data/logictypes.txt");
write!( write_repr_enum(&mut writer, "SlotLogicType", &slotlogictypes, true);
&mut writer,
"pub(crate) const SLOT_TYPE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
slotlogictype_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/slotlogictypes.txt"); 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 out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("enums.rs"); 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(); let val: Option<u8> = val_str.parse().ok();
if !check_set.contains(name) { if !check_set.contains(name) {
enums_grammar.insert(name.to_string());
check_set.insert(name); check_set.insert(name);
} }
@@ -102,14 +216,14 @@ fn write_enums(enums_grammar: &mut HashSet<String>) {
println!("cargo:rerun-if-changed=data/enums.txt"); 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 out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("modes.rs"); let dest_path = Path::new(&out_dir).join("modes.rs");
let output_file = File::create(dest_path).unwrap(); let output_file = File::create(dest_path).unwrap();
let mut writer = BufWriter::new(&output_file); 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_infile = Path::new("data/batchmodes.txt");
let b_contents = fs::read_to_string(b_infile).unwrap(); 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_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok(); let val: Option<u8> = val_str.parse().ok();
logictypes_grammar.insert(name.to_string()); if let Some(val) = val {
if let Some(v) = val { if let Some((_other_name, variant)) = batchmodes
batchmode_lookup_map_builder.entry(name, &format!("{}u8", v)); .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_infile = Path::new("data/reagentmodes.txt");
let r_contents = fs::read_to_string(r_infile).unwrap(); 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_str = it.next().unwrap();
let val: Option<u8> = val_str.parse().ok(); let val: Option<u8> = val_str.parse().ok();
logictypes_grammar.insert(name.to_string()); if let Some(val) = val {
if let Some(v) = val { if let Some((_other_name, variant)) = reagentmodes
reagentmode_lookup_map_builder.entry(name, &format!("{}u8", v)); .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!( write_repr_enum(&mut writer, "BatchMode", &batchmodes, false);
&mut writer,
"pub(crate) const BATCH_MODE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
batchmode_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/batchmodes.txt"); println!("cargo:rerun-if-changed=data/batchmodes.txt");
write!( write_repr_enum(&mut writer, "ReagentMode", &reagentmodes, false);
&mut writer,
"pub(crate) const REAGENT_MODE_LOOKUP: phf::Map<&'static str, u8> = {};\n",
reagentmode_lookup_map_builder.build()
)
.unwrap();
println!("cargo:rerun-if-changed=data/reagentmodes.txt"); 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 out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("constants.rs"); 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 name = it.next().unwrap();
let constant = it.next().unwrap(); let constant = it.next().unwrap();
constants_grammar.insert(name.to_string());
constants_lookup_map_builder.entry(name, constant); constants_lookup_map_builder.entry(name, constant);
} }
@@ -248,14 +395,11 @@ fn write_instructions_enum() {
} }
fn main() { fn main() {
let mut logictype_grammar = HashSet::new();
let mut enums_grammar = HashSet::new();
let mut constants_grammar = HashSet::new();
// write_instructions(); // write_instructions();
write_logictypes(&mut logictype_grammar); write_logictypes();
write_modes(&mut logictype_grammar); write_modes();
write_constants(&mut constants_grammar); write_constants();
write_enums(&mut enums_grammar); write_enums();
write_instructions_enum(); write_instructions_enum();
} }

View File

@@ -75,7 +75,6 @@ j VALUE
jal VALUE jal VALUE
jr VALUE jr VALUE
l REGISTER DEVICE LOGIC_TYPE l REGISTER DEVICE LOGIC_TYPE
label NAME REGISTER_DEVICE
lb REGISTER DEVICE_TYPE LOGIC_TYPE BATCH_MODE lb REGISTER DEVICE_TYPE LOGIC_TYPE BATCH_MODE
lbn REGISTER DEVICE_TYPE DEVICE_NAME 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 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 CelestialHash 242
CelestialParentHash 250 CelestialParentHash 250
Channel None Channel on a cable network which should be considered volatile Channel None Channel on a cable network which should be considered volatile
Channel0 165 Channel0 165 Channel on a cable network which should be considered volatile
Channel1 166 Channel1 166 Channel on a cable network which should be considered volatile
Channel2 167 Channel2 167 Channel on a cable network which should be considered volatile
Channel3 168 Channel3 168 Channel on a cable network which should be considered volatile
Channel4 169 Channel4 169 Channel on a cable network which should be considered volatile
Channel5 170 Channel5 170 Channel on a cable network which should be considered volatile
Channel6 171 Channel6 171 Channel on a cable network which should be considered volatile
Channel7 172 Channel7 172 Channel on a cable network which should be considered volatile
Charge 11 The current charge the device has 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 ClearMemory 62 When set to 1, clears the counter memory (e.g. ExportCount). Will set itself back to 0 when actioned
CollectableGoods 101 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 Error 4 1 if device is in error state, otherwise 0
ExhaustVelocity 235 ExhaustVelocity 235
ExportCount 63 How many items exported since last ClearMemory ExportCount 63 How many items exported since last ClearMemory
ExportQuantity None Total quantity of items exported by the device ExportQuantity 31 Total quantity of items exported by the device
ExportSlotHash None DEPRECATED ExportSlotHash 42 DEPRECATED
ExportSlotOccupant None DEPRECATED ExportSlotOccupant 32 DEPRECATED
Filtration 74 The current state of the filtration system, for example Filtration = 1 for a Hardsuit sets filtration to On Filtration 74 The current state of the filtration system, for example Filtration = 1 for a Hardsuit sets filtration to On
FlightControlRule 236 FlightControlRule 236
Flush 174 Set to 1 to activate the flush function on the device 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 HorizontalRatio 34 Radio of horizontal setting for device
Idle 37 Returns 1 if the device is currently idle, otherwise 0 Idle 37 Returns 1 if the device is currently idle, otherwise 0
ImportCount 64 How many items imported since last ClearMemory ImportCount 64 How many items imported since last ClearMemory
ImportQuantity None Total quantity of items imported by the device ImportQuantity 29 Total quantity of items imported by the device
ImportSlotHash None DEPRECATED ImportSlotHash 43 DEPRECATED
ImportSlotOccupant None DEPRECATED ImportSlotOccupant 30 DEPRECATED
Inclination 246 Inclination 246
Index 241 Index 241
InterrogationProgress 157 Progress of this sattellite dish's interrogation of its current target, as a ratio from 0-1 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 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. 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 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 MineablesInQueue 96
MineablesInVicinity 95 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 Mode 3 Integer for mode state, different devices will have different mode states available to them
NextWeatherEventTime 97 NextWeatherEventTime 97
None None No description None None No description
@@ -105,7 +105,7 @@ PlantHealth2 None DEPRECATED
PlantHealth3 None DEPRECATED PlantHealth3 None DEPRECATED
PlantHealth4 None DEPRECATED PlantHealth4 None DEPRECATED
PositionX 76 The current position in X dimension in world coordinates 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 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 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 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 SemiMajorAxis 248
Setting 12 A variable setting that can be read or written, depending on the device Setting 12 A variable setting that can be read or written, depending on the device
SettingInput 91 SettingInput 91
SettingInputHash None The input setting for the device SettingInputHash 91 The input setting for the device
SettingOutput 92 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 SignalID 87 Returns the contact ID of the strongest signal from this Satellite
SignalStrength 86 Returns the degree offset of the strongest contact 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) 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::error::Error;
use std::fmt::Display; use std::fmt::Display;
use std::str::FromStr; 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)] #[derive(Debug, Clone)]
pub struct ParseError { 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> { pub fn parse(code: &str) -> Result<Vec<Line>, ParseError> {
code.lines() code.lines()
.enumerate() .enumerate()
@@ -203,10 +278,13 @@ pub enum Operand {
}, },
DeviceSpec { DeviceSpec {
device: Device, device: Device,
channel: Option<u32>, connection: Option<u32>,
}, },
Number(Number), Number(Number),
LogicType(LogicType), LogicType(LogicType),
SlotLogicType(SlotLogicType),
BatchMode(BatchMode),
ReagentMode(ReagentMode),
Identifier(Identifier), Identifier(Identifier),
} }
@@ -218,16 +296,34 @@ impl Operand {
target, target,
} => ic.get_register(*indirection, *target), } => ic.get_register(*indirection, *target),
&Operand::Number(num) => Ok(num.value()), &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::Identifier(ident) => ic.get_ident_value(&ident.name),
&Operand::DeviceSpec { .. } => Err(interpreter::ICError::DeviceNotValue), &Operand::DeviceSpec { .. } => Err(interpreter::ICError::DeviceNotValue),
} }
} }
pub fn get_value_i64(
pub fn get_value_i64(&self, ic: &interpreter::IC, signed: bool) -> Result<i64, interpreter::ICError> { &self,
ic: &interpreter::IC,
signed: bool,
) -> Result<i64, interpreter::ICError> {
let val = self.get_value(ic)?; let val = self.get_value(ic)?;
if val < -9.223372036854776E+18 { if val < -9.223372036854776E+18 {
Err(interpreter::ICError::ShiftUnderflowI64) Err(interpreter::ICError::ShiftUnderflowI64)
} else if val <= 9.223372036854776E+18 { } else if val <= 9.223372036854776E+18 {
Ok(interpreter::f64_to_i64(val, signed)) Ok(interpreter::f64_to_i64(val, signed))
@@ -252,15 +348,15 @@ impl Operand {
ic: &interpreter::IC, ic: &interpreter::IC,
) -> Result<(Option<u16>, Option<u32>), interpreter::ICError> { ) -> Result<(Option<u16>, Option<u32>), interpreter::ICError> {
match &self { match &self {
&Operand::DeviceSpec { device, channel } => match device { &Operand::DeviceSpec { device, connection } => match device {
Device::Db => Ok((Some(ic.id), *channel)), Device::Db => Ok((Some(ic.id), *connection)),
Device::Numbered(p) => { Device::Numbered(p) => {
let dp = ic let dp = ic
.pins .pins
.get(*p as usize) .get(*p as usize)
.ok_or(interpreter::ICError::DeviceIndexOutOfRange(*p as f64)) .ok_or(interpreter::ICError::DeviceIndexOutOfRange(*p as f64))
.copied()?; .copied()?;
Ok((dp, *channel)) Ok((dp, *connection))
} }
Device::Indirect { Device::Indirect {
indirection, indirection,
@@ -272,7 +368,7 @@ impl Operand {
.get(val as usize) .get(val as usize)
.ok_or(interpreter::ICError::DeviceIndexOutOfRange(val)) .ok_or(interpreter::ICError::DeviceIndexOutOfRange(val))
.copied()?; .copied()?;
Ok((dp, *channel)) Ok((dp, *connection))
} }
}, },
&Operand::Identifier(id) => ic.get_ident_device_id(&id.name), &Operand::Identifier(id) => ic.get_ident_device_id(&id.name),
@@ -322,20 +418,20 @@ impl FromStr for Operand {
['d', rest @ ..] => match rest { ['d', rest @ ..] => match rest {
['b'] => Ok(Operand::DeviceSpec { ['b'] => Ok(Operand::DeviceSpec {
device: Device::Db, device: Device::Db,
channel: None, connection: None,
}), }),
['b', ':', chan @ ..] => { ['b', ':', chan @ ..] => {
if chan.into_iter().all(|c| c.is_digit(10)) { if chan.into_iter().all(|c| c.is_digit(10)) {
Ok(Operand::DeviceSpec { Ok(Operand::DeviceSpec {
device: Device::Db, device: Device::Db,
channel: Some(String::from_iter(chan).parse().unwrap()), connection: Some(String::from_iter(chan).parse().unwrap()),
}) })
} else { } else {
Err(ParseError { Err(ParseError {
line: 0, line: 0,
start: 3, start: 3,
end: 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)) .take_while_ref(|c| c.is_digit(10))
.collect::<String>(); .collect::<String>();
let target = target_str.parse::<u32>().ok(); let target = target_str.parse::<u32>().ok();
let channel = { let connection = {
if rest_iter.peek() == Some(&&':') { if rest_iter.peek() == Some(&&':') {
// take off ':' // take off ':'
rest_iter.next(); rest_iter.next();
let channel_str = rest_iter let connection_str = rest_iter
.take_while_ref(|c| c.is_digit(10)) .take_while_ref(|c| c.is_digit(10))
.collect::<String>(); .collect::<String>();
let channel = channel_str.parse::<u32>().unwrap(); let connection = connection_str.parse::<u32>().unwrap();
let trailing = rest_iter.clone().collect::<Vec<_>>(); let trailing = rest_iter.clone().collect::<Vec<_>>();
if trailing.len() == 0 { if trailing.len() == 0 {
Ok(Some(channel)) Ok(Some(connection))
} else { } else {
let start = let start =
2 + indirection + target_str.len() + 1 + channel_str.len(); 2 + indirection + target_str.len() + 1 + connection_str.len();
Err(ParseError { Err(ParseError {
line: 0, line: 0,
start, start,
end: start, end: start,
msg: format!("Invalid device channel specifier"), msg: format!("Invalid device connection specifier"),
}) })
} }
} else { } else {
@@ -379,7 +475,7 @@ impl FromStr for Operand {
indirection: indirection as u32, indirection: indirection as u32,
target, target,
}, },
channel, connection: connection,
}) })
} else { } else {
Err(ParseError { Err(ParseError {
@@ -399,24 +495,24 @@ impl FromStr for Operand {
.take_while_ref(|c| c.is_digit(10)) .take_while_ref(|c| c.is_digit(10))
.collect::<String>(); .collect::<String>();
let target = target_str.parse::<u32>().ok(); let target = target_str.parse::<u32>().ok();
let channel = { let connection = {
if rest_iter.peek() == Some(&&':') { if rest_iter.peek() == Some(&&':') {
// take off ':' // take off ':'
rest_iter.next(); rest_iter.next();
let channel_str = rest_iter let connection_str = rest_iter
.take_while_ref(|c| c.is_digit(10)) .take_while_ref(|c| c.is_digit(10))
.collect::<String>(); .collect::<String>();
let channel = channel_str.parse::<u32>().unwrap(); let connection = connection_str.parse::<u32>().unwrap();
let trailing = rest_iter.clone().collect::<Vec<_>>(); let trailing = rest_iter.clone().collect::<Vec<_>>();
if trailing.len() == 0 { if trailing.len() == 0 {
Ok(Some(channel)) Ok(Some(connection))
} else { } else {
let start = 1 + target_str.len() + 1 + channel_str.len(); let start = 1 + target_str.len() + 1 + connection_str.len();
Err(ParseError { Err(ParseError {
line: 0, line: 0,
start, start,
end: start, end: start,
msg: format!("Invalid device channel specifier"), msg: format!("Invalid device connection specifier"),
}) })
} }
} else { } else {
@@ -428,7 +524,7 @@ impl FromStr for Operand {
if trailing.len() == 0 { if trailing.len() == 0 {
Ok(Operand::DeviceSpec { Ok(Operand::DeviceSpec {
device: Device::Numbered(target), device: Device::Numbered(target),
channel, connection: connection,
}) })
} else { } else {
Err(ParseError { Err(ParseError {
@@ -537,26 +633,14 @@ impl FromStr for Operand {
Ok(Operand::Number(Number::Constant(*val))) Ok(Operand::Number(Number::Constant(*val)))
} else if let Some(val) = ENUM_LOOKUP.get(s) { } else if let Some(val) = ENUM_LOOKUP.get(s) {
Ok(Operand::Number(Number::Enum(*val as f64))) Ok(Operand::Number(Number::Enum(*val as f64)))
} else if let Some(val) = LOGIC_TYPE_LOOKUP.get(s) { } else if let Ok(lt) = LogicType::from_str(s) {
Ok(Operand::LogicType(LogicType { Ok(Operand::LogicType(lt))
name: s.to_string(), } else if let Ok(slt) = SlotLogicType::from_str(s) {
value: *val as f64, Ok(Operand::SlotLogicType(slt))
})) } else if let Ok(bm) = BatchMode::from_str(s) {
} else if let Some(val) = SLOT_TYPE_LOOKUP.get(s) { Ok(Operand::BatchMode(bm))
Ok(Operand::LogicType(LogicType { } else if let Ok(rm) = ReagentMode::from_str(s) {
name: s.to_string(), Ok(Operand::ReagentMode(rm))
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 { } else {
Ok(Operand::Identifier(s.parse::<Identifier>()?)) Ok(Operand::Identifier(s.parse::<Identifier>()?))
} }
@@ -565,11 +649,11 @@ impl FromStr for Operand {
} }
} }
#[derive(PartialEq, Debug)] // #[derive(PartialEq, Debug)]
pub struct LogicType { // pub struct LogicType {
pub name: String, // pub name: String,
pub value: f64, // pub value: f64,
} // }
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub struct Label { pub struct Label {
@@ -673,6 +757,7 @@ impl Number {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::generated::*;
use super::*; use super::*;
#[test] #[test]
@@ -687,12 +772,9 @@ mod tests {
operands: vec![ operands: vec![
Operand::DeviceSpec { Operand::DeviceSpec {
device: Device::Numbered(0), device: Device::Numbered(0),
channel: None, connection: None,
}, },
Operand::LogicType(LogicType { Operand::LogicType(LogicType::Setting),
name: "Setting".to_string(),
value: 12.0,
},),
Operand::Number(Number::Float(0.0)), Operand::Number(Number::Float(0.0)),
], ],
},),), },),),
@@ -800,7 +882,7 @@ mod tests {
},), },),
Operand::DeviceSpec { Operand::DeviceSpec {
device: Device::Numbered(0), device: Device::Numbered(0),
channel: None, connection: None,
}, },
], ],
},),), },),),
@@ -812,7 +894,7 @@ mod tests {
operands: vec![ operands: vec![
Operand::DeviceSpec { Operand::DeviceSpec {
device: Device::Numbered(0), device: Device::Numbered(0),
channel: None, connection: None,
}, },
Operand::Number(Number::Float(12.0)), Operand::Number(Number::Float(12.0)),
Operand::Number(Number::Float(0.0)), Operand::Number(Number::Float(0.0)),
@@ -871,12 +953,9 @@ mod tests {
indirection: 0, indirection: 0,
target: 15, target: 15,
}, },
channel: None, connection: None,
}, },
Operand::LogicType(LogicType { Operand::LogicType(LogicType::RatioWater),
name: "RatioWater".to_string(),
value: 19.0,
},),
], ],
},),), },),),
comment: None, comment: None,

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,14 @@
use core::f64; use core::f64;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
mod tokens;
mod grammar; mod grammar;
mod interpreter; mod interpreter;
mod rand_mscorlib; mod rand_mscorlib;
mod tokens;
use grammar::{BatchMode, LogicType, ReagentMode, SlotLogicType};
use interpreter::ICError;
use itertools::Itertools;
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -16,29 +18,47 @@ pub enum VMError {
#[error("Device with id '{0}' does not have a IC Slot")] #[error("Device with id '{0}' does not have a IC Slot")]
NoIC(u16), NoIC(u16),
#[error("IC encoutered an error: {0}")] #[error("IC encoutered an error: {0}")]
ICError(#[from] interpreter::ICError) ICError(#[from] ICError),
#[error("Invalid network ID {0}")]
InvalidNetwork(u16),
} }
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug)]
pub enum FieldType { pub enum FieldType {
Read, Read,
Write, Write,
ReadWrite ReadWrite,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct LogicField { pub struct LogicField {
pub field_type: FieldType, pub field_type: FieldType,
pub value: f64, 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)] #[derive(Debug, Default)]
pub struct Device { pub struct Device {
pub id: u16, pub id: u16,
pub fields: HashMap<u32, LogicField>, pub name: Option<String>,
pub ic: Option<interpreter::IC> 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)] #[derive(Debug)]
@@ -70,8 +90,10 @@ impl IdSequenceGenerator {
pub struct VM { pub struct VM {
pub ics: HashSet<u16>, pub ics: HashSet<u16>,
pub devices: HashMap<u16, Device>, pub devices: HashMap<u16, Device>,
pub networks: Vec<Network>, pub networks: HashMap<u16, Network>,
pub default_network: u16,
id_gen: IdSequenceGenerator, id_gen: IdSequenceGenerator,
network_id_gen: IdSequenceGenerator,
random: crate::rand_mscorlib::Random, 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 { impl Device {
pub fn new(id: u16) -> Self { 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 { pub fn with_ic(id: u16) -> Self {
@@ -94,32 +164,207 @@ impl Device {
device.ic = Some(interpreter::IC::new(id)); device.ic = Some(interpreter::IC::new(id));
device 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 { impl VM {
pub fn new() -> Self { pub fn new() -> Self {
let id_gen = IdSequenceGenerator::default(); let id_gen = IdSequenceGenerator::default();
let mut network_id_gen = IdSequenceGenerator::default();
let default_network = Network::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 { let mut vm = VM {
ics: HashSet::new(), ics: HashSet::new(),
devices: HashMap::new(), devices: HashMap::new(),
networks: vec![default_network], networks,
default_network: default_network_key,
id_gen, id_gen,
network_id_gen,
random: crate::rand_mscorlib::Random::new(), random: crate::rand_mscorlib::Random::new(),
}; };
vm.add_ic(); let _ = vm.add_ic(None);
vm vm
} }
pub fn add_ic(&mut self) { fn new_device(&mut self) -> Device {
let device = Device::with_ic(self.id_gen.next()); Device::new(self.id_gen.next())
self.ics.insert(device.id); }
self.devices.insert(device.id, device);
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) { pub fn remove_ic(&mut self, id: u16) {
if self.ics.remove(&id) { if self.ics.remove(&id) {
self.devices.remove(&id); self.devices.remove(&id);
} }
} }
@@ -133,4 +378,294 @@ impl VM {
Ok(true) 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 = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.53",
] ]
[[package]] [[package]]
@@ -139,10 +139,11 @@ dependencies = [
"convert_case", "convert_case",
"getrandom", "getrandom",
"itertools", "itertools",
"phf", "phf 0.11.2",
"phf_codegen", "phf_codegen",
"rand", "rand",
"regex", "regex",
"strum",
"strum_macros", "strum_macros",
"thiserror", "thiserror",
"web-time", "web-time",
@@ -203,13 +204,24 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 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]] [[package]]
name = "phf" name = "phf"
version = "0.11.2" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [ dependencies = [
"phf_shared", "phf_shared 0.11.2",
] ]
[[package]] [[package]]
@@ -218,8 +230,18 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [ dependencies = [
"phf_generator", "phf_generator 0.11.2",
"phf_shared", "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]] [[package]]
@@ -228,10 +250,33 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [ dependencies = [
"phf_shared", "phf_shared 0.11.2",
"rand", "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]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.11.2" version = "0.11.2"
@@ -259,6 +304,12 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.79" version = "1.0.79"
@@ -357,6 +408,16 @@ dependencies = [
"autocfg", "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]] [[package]]
name = "strum_macros" name = "strum_macros"
version = "0.26.2" version = "0.26.2"
@@ -367,7 +428,18 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "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]] [[package]]
@@ -398,7 +470,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.53",
] ]
[[package]] [[package]]
@@ -440,7 +512,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.53",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@@ -475,7 +547,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.53",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]