diff --git a/ic10emu/src/errors.rs b/ic10emu/src/errors.rs index 21d061f..d297848 100644 --- a/ic10emu/src/errors.rs +++ b/ic10emu/src/errors.rs @@ -62,6 +62,8 @@ pub enum VMError { NotParentable(ObjectID), #[error("object {0} is not logicable")] NotLogicable(ObjectID), + #[error("network object {0} is not a network")] + NonNetworkNetwork(ObjectID) } #[derive(Error, Debug, Serialize, Deserialize)] diff --git a/ic10emu/src/interpreter.rs b/ic10emu/src/interpreter.rs index 4db630a..3559871 100644 --- a/ic10emu/src/interpreter.rs +++ b/ic10emu/src/interpreter.rs @@ -245,7 +245,7 @@ mod tests { }; println!("Adding IC"); - let ic = vm.add_object_from_frozen(frozen_ic)?; + let ic = vm.add_object_frozen(frozen_ic)?; let frozen_circuit_holder = FrozenObject { obj_info: ObjectInfo::with_prefab(Prefab::Name( StationpediaPrefab::StructureCircuitHousing.to_string(), @@ -254,7 +254,7 @@ mod tests { template: None, }; println!("Adding circuit holder"); - let ch = vm.add_object_from_frozen(frozen_circuit_holder)?; + let ch = vm.add_object_frozen(frozen_circuit_holder)?; println!("socketing ic into circuit holder"); vm.set_slot_occupant(ch, 0, Some(ic), 1)?; Ok((vm, ch, ic)) diff --git a/ic10emu/src/vm.rs b/ic10emu/src/vm.rs index d8ca053..2a55f2d 100644 --- a/ic10emu/src/vm.rs +++ b/ic10emu/src/vm.rs @@ -2,7 +2,7 @@ pub mod instructions; pub mod object; use crate::{ - errors::{ICError, TemplateError, VMError}, + errors::{ICError, VMError}, interpreter::ICState, network::{CableConnectionType, CableNetwork, Connection, FrozenCableNetwork}, vm::object::{ @@ -183,7 +183,7 @@ impl VM { let mut net_ref = net.borrow_mut(); let net_interface = net_ref .as_mut_network() - .unwrap_or_else(|| panic!("non network network: {net_id}")); + .ok_or(VMError::NonNetworkNetwork(net_id))?; for id in trans_net.devices { net_interface.add_data(id); } @@ -200,7 +200,7 @@ impl VM { /// current database. /// Errors if the object can not be built do to a template error /// Returns the built object's ID - pub fn add_object_from_frozen( + pub fn add_object_frozen( self: &Rc, frozen: FrozenObject, ) -> Result { @@ -236,7 +236,7 @@ impl VM { let mut net_ref = net.borrow_mut(); let net_interface = net_ref .as_mut_network() - .unwrap_or_else(|| panic!("non network network: {net_id}")); + .ok_or(VMError::NonNetworkNetwork(net_id))?; for id in trans_net.devices { net_interface.add_data(id); } @@ -952,14 +952,14 @@ impl VM { network .borrow_mut() .as_mut_network() - .expect("non-network network") + .ok_or(VMError::NonNetworkNetwork(*net))? .remove_power(id); } _ => { network .borrow_mut() .as_mut_network() - .expect("non-network network") + .ok_or(VMError::NonNetworkNetwork(*net))? .remove_data(id); } } @@ -982,14 +982,14 @@ impl VM { network .borrow_mut() .as_mut_network() - .expect("non-network network") + .ok_or(VMError::NonNetworkNetwork(target_net))? .add_power(id); } _ => { network .borrow_mut() .as_mut_network() - .expect("non-network network") + .ok_or(VMError::NonNetworkNetwork(target_net))? .add_data(id); } } @@ -1025,7 +1025,7 @@ impl VM { network .borrow_mut() .as_mut_network() - .expect("non-network network") + .ok_or(VMError::NonNetworkNetwork(network_id))? .remove_all(id); Ok(true) } else { @@ -1314,7 +1314,38 @@ impl VM { .collect() } - pub fn save_vm_state(self: &Rc) -> Result { + pub fn freeze_network(self: &Rc, id: ObjectID) -> Result { + Ok(self + .networks + .borrow() + .get(&id) + .ok_or(VMError::UnknownId(id))? + .borrow() + .as_network() + .ok_or(VMError::NonNetworkNetwork(id))? + .into()) + } + + pub fn freeze_networks( + self: &Rc, + ids: impl IntoIterator, + ) -> Result, VMError> { + ids.into_iter() + .map(|id| { + Ok(self + .networks + .borrow() + .get(&id) + .ok_or(VMError::UnknownId(id))? + .borrow() + .as_network() + .ok_or(VMError::NonNetworkNetwork(id))? + .into()) + }) + .collect::, VMError>>() + } + + pub fn save_vm_state(self: &Rc) -> Result { Ok(FrozenVM { objects: self .objects @@ -1337,13 +1368,14 @@ impl VM { .borrow() .values() .map(|network| { - network + let net_id = network.get_id(); + Ok(network .borrow() .as_network() - .expect("non-network network") - .into() + .ok_or(VMError::NonNetworkNetwork(net_id))? + .into()) }) - .collect(), + .collect::, VMError>>()?, default_network_key: *self.default_network_key.borrow(), circuit_holders: self.circuit_holders.borrow().clone(), program_holders: self.program_holders.borrow().clone(), @@ -1412,7 +1444,7 @@ impl VM { let mut net_ref = net.borrow_mut(); let net_interface = net_ref .as_mut_network() - .unwrap_or_else(|| panic!("non network network: {net_id}")); + .ok_or(VMError::NonNetworkNetwork(net_id))?; for id in trans_net.devices { net_interface.add_data(id); } diff --git a/ic10emu_wasm/src/lib.rs b/ic10emu_wasm/src/lib.rs index ebd7c35..bfb2b28 100644 --- a/ic10emu_wasm/src/lib.rs +++ b/ic10emu_wasm/src/lib.rs @@ -4,8 +4,12 @@ mod utils; use ic10emu::{ errors::{ICError, VMError}, + network::FrozenCableNetwork, vm::{ - object::{templates::{FrozenObject, FrozenObjectFull}, ObjectID}, + object::{ + templates::{FrozenObject, FrozenObjectFull}, + ObjectID, + }, FrozenVM, VM, }, }; @@ -60,10 +64,50 @@ impl IntoIterator for TemplateDatabase { #[tsify(into_wasm_abi, from_wasm_abi)] pub struct FrozenObjects(Vec); +impl IntoIterator for FrozenObjects { + type Item = FrozenObjectFull; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Tsify)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct FrozenObjectsSparse(Vec); + +impl IntoIterator for FrozenObjectsSparse { + type Item = FrozenObject; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, Tsify)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub struct FrozenNetworks(Vec); + +impl IntoIterator for FrozenNetworks { + type Item = FrozenCableNetwork; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + #[derive(Clone, Debug, Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi, from_wasm_abi)] pub struct CompileErrors(Vec); +impl IntoIterator for CompileErrors { + type Item = ICError; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + #[wasm_bindgen] impl VMRef { #[wasm_bindgen(constructor)] @@ -81,13 +125,26 @@ impl VMRef { TemplateDatabase(self.vm.get_template_database()) } - #[wasm_bindgen(js_name = "addObjectFromFrozen")] - pub fn add_object_from_frozen(&self, frozen: FrozenObject) -> Result { + #[wasm_bindgen(js_name = "addObjectFrozen")] + pub fn add_object_frozen(&self, frozen: FrozenObject) -> Result { web_sys::console::log_2( &"(wasm) adding device".into(), &serde_wasm_bindgen::to_value(&frozen).unwrap(), ); - Ok(self.vm.add_object_from_frozen(frozen)?) + Ok(self.vm.add_object_frozen(frozen)?) + } + + #[wasm_bindgen(js_name = "addObjectsFrozen")] + pub fn add_objects_frozen( + &self, + frozen_objects: FrozenObjectsSparse, + ) -> Result, JsError> { + web_sys::console::log_2( + &"(wasm) adding device".into(), + &serde_wasm_bindgen::to_value(&frozen_objects).unwrap(), + ); + + Ok(self.vm.add_objects_frozen(frozen_objects)?) } // #[wasm_bindgen(js_name = "getDevice")] @@ -105,6 +162,16 @@ impl VMRef { Ok(FrozenObjects(self.vm.freeze_objects(ids)?)) } + #[wasm_bindgen(js_name = "freezeNetwork")] + pub fn freeze_network(&self, id: ObjectID) -> Result { + Ok(self.vm.freeze_network(id)?) + } + + #[wasm_bindgen(js_name = "freezeNetworks")] + pub fn freeze_networks(&self, ids: Vec) -> Result { + Ok(FrozenNetworks(self.vm.freeze_networks(ids)?)) + } + #[wasm_bindgen(js_name = "setCode")] /// Set program code if it's valid pub fn set_code(&self, id: ObjectID, code: &str) -> Result { @@ -225,7 +292,7 @@ impl VMRef { // TODO: we just assume if the ID is found that the frozen object passed is the same object.. obj.get_id() } else { - self.vm.add_object_from_frozen(frozen)? + self.vm.add_object_frozen(frozen)? }; Ok(self .vm diff --git a/www/src/ts/virtual_machine/controls.ts b/www/src/ts/virtual_machine/controls.ts index 549ded0..2e3d63c 100644 --- a/www/src/ts/virtual_machine/controls.ts +++ b/www/src/ts/virtual_machine/controls.ts @@ -123,14 +123,14 @@ export class VMICControls extends VMActiveICMixin(BaseElement) { ${ics.map( ([id, device], _index) => html` - ${device.name - ? html`${device.prefabName}` + ${device.obj_info.name + ? html`${device.obj_info.prefab}` : ""} - Device:${id} ${device.name ?? device.prefabName} + Device:${id} ${device.obj_info.name ?? device.obj_info.prefab} `, )} @@ -156,13 +156,16 @@ export class VMICControls extends VMActiveICMixin(BaseElement) { Errors ${this.errors.map( (err) => - html`
- - Line: ${err.ParseError.line} - - ${err.ParseError.start}:${err.ParseError.end} - - ${err.ParseError.msg} -
`, + typeof err === "object" + && "ParseError" in err + ? html`
+ + Line: ${err.ParseError.line} - + ${"ParseError" in err ? err.ParseError.start : "N/A"}:${err.ParseError.end} + + ${err.ParseError.msg} +
` + : html`${JSON.stringify(err)}`, )} diff --git a/www/src/ts/virtual_machine/device/card.ts b/www/src/ts/virtual_machine/device/card.ts index 77c21c5..14060db 100644 --- a/www/src/ts/virtual_machine/device/card.ts +++ b/www/src/ts/virtual_machine/device/card.ts @@ -432,7 +432,7 @@ export class VMDeviceCard extends VMTemplateDBMixin( const val = parseIntWithHexOrBinary(input.value); if (!isNaN(val)) { window.VM.get().then((vm) => { - if (!vm.changeDeviceID(this.objectID, val)) { + if (!vm.changeObjectID(this.objectID, val)) { input.value = this.objectID.toString(); } }); diff --git a/www/src/ts/virtual_machine/device/dbutils.ts b/www/src/ts/virtual_machine/device/dbutils.ts index 73b4e87..5c132b0 100644 --- a/www/src/ts/virtual_machine/device/dbutils.ts +++ b/www/src/ts/virtual_machine/device/dbutils.ts @@ -1,16 +1,58 @@ -import { Connection } from "ic10emu_wasm"; -import { DeviceDBConnection } from "../device_db"; - -const CableNetworkTypes: readonly string[] = Object.freeze(["Power", "Data", "PowerAndData"]); -export function connectionFromDeviceDBConnection(conn: DeviceDBConnection): Connection { - if (CableNetworkTypes.includes(conn.typ)) { - return { +import { + Connection, + ConnectionInfo, + CableConnectionType, +} from "ic10emu_wasm"; +export function connectionFromConnectionInfo(conn: ConnectionInfo): Connection { + let connection: Connection = "None"; + if ( + conn.typ === "Power" || + conn.typ === "Data" || + conn.typ === "PowerAndData" + ) { + connection = { CableNetwork: { - net: window.VM.vm.ic10vm.defaultNetwork, - typ: conn.typ - } + net: window.VM.vm.defaultNetwork, + typ: conn.typ as CableConnectionType, + role: conn.role, + }, + }; + } else if (conn.typ === "Pipe") { + connection = { + Pipe: { + role: conn.role, + }, + }; + } else if (conn.typ === "PipeLiquid") { + connection = { + PipeLiquid: { + role: conn.role, + }, + }; + } else if (conn.typ === "Chute") { + connection = { + Chute: { + role: conn.role, + }, + }; + } else if (conn.typ === "Elevator") { + connection = { + Elevator: { + role: conn.role, + }, + }; + } else if (conn.typ === "LaunchPad") { + connection = { + LaunchPad: { + role: conn.role, + }, + }; + } else if (conn.typ === "LandingPad") { + connection = { + LandingPad: { + role: conn.role, + }, }; - } else { - return "Other"; } + return connection; } diff --git a/www/src/ts/virtual_machine/device/template.ts b/www/src/ts/virtual_machine/device/template.ts index b71eee4..5e29a77 100644 --- a/www/src/ts/virtual_machine/device/template.ts +++ b/www/src/ts/virtual_machine/device/template.ts @@ -4,18 +4,42 @@ import type { LogicField, LogicType, Slot, + Class, + FrozenObject, + MemoryAccess, + SlotInfo, + ConnectionInfo, + ObjectID, + CableConnectionType, + ConnectionRole, + ObjectInfo, + SlotOccupantInfo, } from "ic10emu_wasm"; import { html, css, HTMLTemplateResult } from "lit"; import { customElement, property, query, state } from "lit/decorators.js"; import { BaseElement, defaultCss } from "components"; -import { connectionFromDeviceDBConnection } from "./dbutils"; +import { connectionFromConnectionInfo } from "./dbutils"; import { crc32, displayNumber, parseNumber } from "utils"; import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js"; import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js"; import { VMDeviceCard } from "./card"; import { VMTemplateDBMixin } from "virtual_machine/base_device"; +export interface SlotTemplate { + typ: Class + quantity: number, + occupant?: FrozenObject, +} + +export interface ConnectionCableNetwork { + CableNetwork: { + net: ObjectID | undefined; + typ: CableConnectionType; + role: ConnectionRole; + }; +} + @customElement("vm-device-template") export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) { @@ -51,11 +75,12 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) { `, ]; - @state() fields: { [key in LogicType]?: LogicField }; + @state() fields: Map; @state() slots: SlotTemplate[]; - @state() template: ObjectTemplate; - @state() device_id: number | undefined; - @state() device_name: string | undefined; + @state() pins: (ObjectID | undefined)[]; + @state() template: FrozenObject; + @state() objectId: number | undefined; + @state() objectName: string | undefined; @state() connections: Connection[]; constructor() { @@ -63,41 +88,61 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) { this.templateDB = window.VM.vm.templateDB; } - private _prefab_name: string; + private _prefabName: string; + private _prefabHash: number; - get prefab_name(): string { - return this._prefab_name; + + get prefabName(): string { + return this._prefabName; + } + get prefabHash(): number { + return this._prefabHash; } @property({ type: String }) - set prefab_name(val: string) { - this._prefab_name = val; + set prefabName(val: string) { + this._prefabName = val; + this._prefabHash = crc32(this._prefabName); this.setupState(); } get dbTemplate(): ObjectTemplate { - return this.templateDB.get( crc32(this.prefab_name)); + return this.templateDB.get(this._prefabHash); } setupState() { + const dbTemplate = this.dbTemplate; - this.fields = Object.fromEntries( - Object.entries(this.dbTemplate?.logic ?? {}).map(([lt, ft]) => { - const value = lt === "PrefabHash" ? this.dbTemplate.prefab.prefab_hash : 0.0; - return [lt, { field_type: ft, value } as LogicField]; + this.fields = new Map( + ( + Array.from( + "logic" in dbTemplate + ? dbTemplate.logic.logic_types.entries() ?? [] + : [], + ) as [LogicType, MemoryAccess][] + ).map(([lt, access]) => { + const value = + lt === "PrefabHash" ? this.dbTemplate.prefab.prefab_hash : 0.0; + return [lt, value]; }), ); - this.slots = (this.dbTemplate?.slots ?? []).map( + this.slots = ( + ("slots" in dbTemplate ? dbTemplate.slots ?? [] : []) as SlotInfo[] + ).map( (slot, _index) => ({ typ: slot.typ, + quantity: 0, }) as SlotTemplate, ); - const connections = Object.entries(this.dbTemplate?.conn ?? {}).map( - ([index, conn]) => - [index, connectionFromDeviceDBConnection(conn)] as const, + const connections = ( + "device" in dbTemplate + ? dbTemplate.device.connection_list + : ([] as ConnectionInfo[]) + ).map( + (conn, index) => [index, connectionFromConnectionInfo(conn)] as const, ); connections.sort((a, b) => { if (a[0] < b[0]) { @@ -110,12 +155,15 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) { }); this.connections = connections.map((conn) => conn[1]); + + const numPins = "device" in dbTemplate ? dbTemplate.device.device_pins_length : 0; + this.pins = new Array(numPins).fill(undefined); } renderFields(): HTMLTemplateResult { const fields = Object.entries(this.fields); return html` ${fields.map(([name, field], _index, _fields) => { - return html` + return html` ${field.field_type} `; - })} + })} `; } @@ -135,9 +183,9 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) { const input = e.target as SlInput; const field = input.getAttribute("key")! as LogicType; const val = parseNumber(input.value); - this.fields[field].value = val; + this.fields.set(field, val); if (field === "ReferenceId" && val !== 0) { - this.device_id = val; + this.objectId = val; } this.requestUpdate(); } @@ -161,9 +209,11 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) { return html`
${connections.map((connection, index, _conns) => { - const conn = - typeof connection === "object" ? connection.CableNetwork : null; - return html` + const conn = + typeof connection === "object" && "CableNetwork" in connection + ? connection.CableNetwork + : null; + return html` Connection:${index} ${vmNetworks.map( - (net) => - html`Network ${net}`, - )} + (net) => + html`Network ${net}`, + )} ${conn?.typ} `; - })} + })}
`; } @@ -195,8 +245,48 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) { } renderPins(): HTMLTemplateResult { - const device = this.templateDB.db[this.prefab_name]; - return html`
`; + const networks = this.connections.flatMap((connection, index) => { + return typeof connection === "object" && "CableNetwork" in connection + ? [connection.CableNetwork.net] + : []; + }); + const visibleDeviceIds = [ + ...new Set( + networks.flatMap((net) => window.VM.vm.networkDataDevices(net)), + ), + ]; + const visibleDevices = visibleDeviceIds.map((id) => + window.VM.vm.objects.get(id), + ); + const pinsHtml = this.pins?.map( + (pin, index) => + html` + d${index} + ${visibleDevices.map( + (device, _index) => html` + + Device ${device.obj_info.id} : + ${device.obj_info.name ?? device.obj_info.prefab} + + `, + )} + `, + ); + return html`
${pinsHtml}
`; + } + + _handleChangePin(e: CustomEvent) { + const select = e.target as SlSelect; + const pin = parseInt(select.getAttribute("key")!); + const val = select.value ? parseInt(select.value as string) : undefined; + this.pins[pin] = val } render() { @@ -204,17 +294,17 @@ export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) { return html`
- +
- ${device.title} - ${device?.name} - ${device?.hash} + ${device.prefab.name} + ${device?.prefab.prefab_name} + ${device?.prefab.prefab_hash}
`; } - _handleAddButtonClick() { + async _handleAddButtonClick() { this.dispatchEvent( new CustomEvent("add-device-template", { bubbles: true }), ); - const template: DeviceTemplate = { - id: this.device_id, - name: this.device_name, - prefab_name: this.prefab_name, - slots: this.slots, - connections: this.connections, - fields: this.fields, + // Typescript doesn't like fileds defined as `X | undefined` not being present, hence cast + const objInfo: ObjectInfo = { + id: this.objectId, + name: this.objectName, + prefab: this.prefabName + } as ObjectInfo; + + if (this.slots.length > 0) { + const slotOccupants: [FrozenObject, number][] = this.slots.flatMap((slot, index) => { + return typeof slot.occupant !== "undefined" ? [[slot.occupant, index]] : []; + }) + let slotOccupantTemplates: FrozenObject[] | null = null; + let slotOccupantObjectIds: ObjectID[] | null = null; + + let slotOccupantIdsMap: Map = new Map(); + if (slotOccupants.length > 0) { + slotOccupantTemplates = slotOccupants.map(([slot, _]) => slot); + slotOccupantObjectIds = await window.VM.vm.addObjectsFrozen(slotOccupantTemplates); + slotOccupantIdsMap = new Map(slotOccupants.map((_, index) => { + return [index, slotOccupantObjectIds[index]]; + })) + } + objInfo.slots = new Map(this.slots.flatMap((slot, index) => { + const occupantId = slotOccupantIdsMap.get(index); + if (typeof occupantId !== "undefined") { + const info: SlotOccupantInfo = { + id: occupantId, + quantity: slot.quantity + }; + return [[index, info]] as [number, SlotOccupantInfo][]; + } else { + return [] as [number, SlotOccupantInfo][]; + } + })) + } + + if (this.connections.length > 0) { + objInfo.connections = new Map( + this.connections.flatMap((conn, index) => { + return typeof conn === "object" && + "CableNetwork" in conn && + typeof conn.CableNetwork.net !== "undefined" + ? ([[index, conn.CableNetwork.net]] as [number, number][]) + : ([] as [number, number][]); + }), + ); + } + + if (this.fields.size > 0) { + objInfo.logic_values = new Map(this.fields) + } + + const template: FrozenObject = { + obj_info: objInfo, + database_template: true, + template: undefined, }; - window.VM.vm.addDeviceFromTemplate(template); + await window.VM.vm.addObjectFrozen(template); // reset state for new device this.setupState(); diff --git a/www/src/ts/virtual_machine/index.ts b/www/src/ts/virtual_machine/index.ts index f2ed99b..185e9c7 100644 --- a/www/src/ts/virtual_machine/index.ts +++ b/www/src/ts/virtual_machine/index.ts @@ -8,6 +8,7 @@ import type { TemplateDatabase, FrozenCableNetwork, FrozenObjectFull, + ObjectID, } from "ic10emu_wasm"; import * as Comlink from "comlink"; import "./base_device"; @@ -29,6 +30,8 @@ export interface VirtualMachinEventMap { "vm-objects-modified": CustomEvent; "vm-run-ic": CustomEvent; "vm-object-id-change": CustomEvent<{ old: number; new: number }>; + "vm-networks-update": CustomEvent; + "vm-networks-removed": CustomEvent; } class VirtualMachine extends (EventTarget as TypedEventTarget) { @@ -62,6 +65,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget this.setupTemplateDatabase(db)); this.updateObjects(); + this.updateNetworks(); this.updateCode(); } @@ -113,11 +117,65 @@ class VirtualMachine extends (EventTarget as TypedEventTarget 0) { + this.dispatchEvent( + new CustomEvent("vm-networks-removed", { + detail: removedNetworks, + }), + ); + } + this.app.session.save(); + } + } + async updateObjects() { let updateFlag = false; const removedObjects = []; - let objectIds; - let frozenObjects; + let objectIds: Uint32Array; + let frozenObjects: FrozenObjectFull[]; try { objectIds = await this.ic10vm.objects; frozenObjects = await this.ic10vm.freezeObjects(objectIds); @@ -125,7 +183,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget { if (this.objects.has(id)) { - this.updateDevice(id, false); + this.updateObject(id, false); } }, this); - this.updateDevice(this.activeIC.obj_info.id, false); + this.updateObject(this.activeIC.obj_info.id, false); if (save) this.app.session.save(); } - async updateDevice(id: number, save: boolean = true) { + async updateObject(id: number, save: boolean = true) { let frozen; try { frozen = await this.ic10vm.freezeObject(id); @@ -287,7 +346,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget { + // return the data connected oject ids for a network + networkDataDevices(network: ObjectID): number[] { + return this._networks.get(network)?.devices ?? [] + } + + async changeObjectID(oldID: number, newID: number): Promise { try { await this.ic10vm.changeDeviceId(oldID, newID); if (this.app.session.activeIC === oldID) { @@ -336,7 +400,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget { + async addObjectFrozen(frozen: FrozenObject): Promise { try { console.log("adding device", frozen); - const id = await this.ic10vm.addObjectFromFrozen(frozen); + const id = await this.ic10vm.addObjectFrozen(frozen); const refrozen = await this.ic10vm.freezeObject(id); this._objects.set(id, refrozen); const device_ids = await this.ic10vm.objects; @@ -475,10 +539,32 @@ class VirtualMachine extends (EventTarget as TypedEventTarget { + try { + console.log("adding devices", frozenObjects); + const ids = await this.ic10vm.addObjectsFrozen(frozenObjects); + const refrozen = await this.ic10vm.freezeObjects(ids); + ids.forEach((id, index) => { + this._objects.set(id, refrozen[index]); + }) + const device_ids = await this.ic10vm.objects; + this.dispatchEvent( + new CustomEvent("vm-objects-update", { + detail: Array.from(device_ids), + }), + ); + this.app.session.save(); + return Array.from(ids); + } catch (err) { + this.handleVmError(err); + return undefined; } } @@ -504,7 +590,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget - "RegisterSpec" in target && target.RegisterSpec.indirection === 0, - ) as [string, RegisterSpec][] - ).map(([alias, target]) => [alias, target.RegisterSpec.target]) as [ - string, - number, - ][] - ).concat(VMICRegisters.defaultAliases); + const registerAliases: [string, number][] = + [...(Array.from(this.aliases?.entries() ?? []))].flatMap( + ([alias, target]) => { + if ("RegisterSpec" in target && target.RegisterSpec.indirection === 0) { + return [[alias, target.RegisterSpec.target]] as [string, number][]; + } else { + return [] as [string, number][]; + } + } + ).concat(VMICRegisters.defaultAliases); return html`