make non derived readonly fields writable from the ui

This commit is contained in:
Rachel Powers
2024-04-18 13:18:03 -07:00
parent 1c18b8bf18
commit bd5695c415
5 changed files with 432 additions and 106 deletions

View File

@@ -112,6 +112,8 @@ pub enum ICError {
BadNetworkId(u32),
#[error("channel index out of range '{0}'")]
ChannelIndexOutOfRange(usize),
#[error("slot has no occupant")]
SlotNotOccupied
}
impl ICError {
@@ -2177,7 +2179,7 @@ impl IC {
match device {
Some(device) => {
let val = val.as_value(this, inst, 1)?;
device.borrow_mut().set_field(lt, val, vm)?;
device.borrow_mut().set_field(lt, val, vm, false)?;
vm.set_modified(device_id);
Ok(())
}
@@ -2197,7 +2199,7 @@ impl IC {
Some(device) => {
let lt = lt.as_logic_type(this, inst, 2)?;
let val = val.as_value(this, inst, 3)?;
device.borrow_mut().set_field(lt, val, vm)?;
device.borrow_mut().set_field(lt, val, vm, false)?;
vm.set_modified(device_id as u32);
Ok(())
}
@@ -2217,7 +2219,9 @@ impl IC {
let index = index.as_value(this, inst, 2)?;
let slt = slt.as_slot_logic_type(this, inst, 3)?;
let val = val.as_value(this, inst, 4)?;
device.borrow_mut().set_slot_field(index, slt, val, vm)?;
device
.borrow_mut()
.set_slot_field(index, slt, val, vm, false)?;
vm.set_modified(device_id);
Ok(())
}
@@ -2231,7 +2235,7 @@ impl IC {
let prefab = prefab.as_value(this, inst, 1)?;
let lt = lt.as_logic_type(this, inst, 2)?;
let val = val.as_value(this, inst, 3)?;
vm.set_batch_device_field(this.device, prefab, lt, val)?;
vm.set_batch_device_field(this.device, prefab, lt, val, false)?;
Ok(())
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 3)),
@@ -2242,7 +2246,14 @@ impl IC {
let index = index.as_value(this, inst, 2)?;
let slt = slt.as_slot_logic_type(this, inst, 3)?;
let val = val.as_value(this, inst, 4)?;
vm.set_batch_device_slot_field(this.device, prefab, index, slt, val)?;
vm.set_batch_device_slot_field(
this.device,
prefab,
index,
slt,
val,
false,
)?;
Ok(())
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 4)),
@@ -2253,7 +2264,7 @@ impl IC {
let name = name.as_value(this, inst, 2)?;
let lt = lt.as_logic_type(this, inst, 3)?;
let val = val.as_value(this, inst, 4)?;
vm.set_batch_name_device_field(this.device, prefab, name, lt, val)?;
vm.set_batch_name_device_field(this.device, prefab, name, lt, val, false)?;
Ok(())
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 4)),
@@ -2282,14 +2293,21 @@ impl IC {
this.set_register(indirection, target, val)?;
return Ok(());
}
let device = vm.get_device_same_network(this.device, device_id);
match device {
Some(device) => {
let val = device.borrow().get_field(lt, vm)?;
this.set_register(indirection, target, val)?;
Ok(())
if lt == LogicType::LineNumber && this.device == device_id {
// HACK: we can't use device.get_field as that will try to reborrow our
// ic which will panic
this.set_register(indirection, target, this.ip as f64)?;
Ok(())
} else {
let device = vm.get_device_same_network(this.device, device_id);
match device {
Some(device) => {
let val = device.borrow().get_field(lt, vm)?;
this.set_register(indirection, target, val)?;
Ok(())
}
None => Err(UnknownDeviceID(device_id as f64)),
}
None => Err(UnknownDeviceID(device_id as f64)),
}
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 3)),
@@ -2304,15 +2322,22 @@ impl IC {
if device_id >= u16::MAX as f64 || device_id < u16::MIN as f64 {
return Err(DeviceIndexOutOfRange(device_id));
}
let device = vm.get_device_same_network(this.device, device_id as u32);
match device {
Some(device) => {
let lt = lt.as_logic_type(this, inst, 3)?;
let val = device.borrow().get_field(lt, vm)?;
this.set_register(indirection, target, val)?;
Ok(())
let lt = lt.as_logic_type(this, inst, 3)?;
if lt == LogicType::LineNumber && this.device == device_id as u32 {
// HACK: we can't use device.get_field as that will try to reborrow our
// ic which will panic
this.set_register(indirection, target, this.ip as f64)?;
Ok(())
} else {
let device = vm.get_device_same_network(this.device, device_id as u32);
match device {
Some(device) => {
let val = device.borrow().get_field(lt, vm)?;
this.set_register(indirection, target, val)?;
Ok(())
}
None => Err(UnknownDeviceID(device_id)),
}
None => Err(UnknownDeviceID(device_id)),
}
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 3)),
@@ -2326,16 +2351,23 @@ impl IC {
let (Some(device_id), _connection) = dev.as_device(this, inst, 2)? else {
return Err(DeviceNotSet);
};
let device = vm.get_device_same_network(this.device, device_id);
match device {
Some(device) => {
let index = index.as_value(this, inst, 3)?;
let slt = slt.as_slot_logic_type(this, inst, 4)?;
let val = device.borrow().get_slot_field(index, slt, vm)?;
this.set_register(indirection, target, val)?;
Ok(())
let slt = slt.as_slot_logic_type(this, inst, 4)?;
if slt == SlotLogicType::LineNumber && this.device == device_id {
// HACK: we can't use device.get_slot_field as that will try to reborrow our
// ic which will panic
this.set_register(indirection, target, this.ip as f64)?;
Ok(())
} else {
let device = vm.get_device_same_network(this.device, device_id);
match device {
Some(device) => {
let index = index.as_value(this, inst, 3)?;
let val = device.borrow().get_slot_field(index, slt, vm)?;
this.set_register(indirection, target, val)?;
Ok(())
}
None => Err(UnknownDeviceID(device_id as f64)),
}
None => Err(UnknownDeviceID(device_id as f64)),
}
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 4)),

View File

@@ -62,6 +62,7 @@ pub struct SlotOccupant {
pub prefab_hash: i32,
pub quantity: u32,
pub max_quantity: u32,
pub sorting_class: SortingClass,
pub damage: f64,
fields: HashMap<SlotLogicType, LogicField>,
}
@@ -90,6 +91,10 @@ impl SlotOccupant {
.remove(&SlotLogicType::Damage)
.map(|field| field.value)
.unwrap_or(0.0),
sorting_class: fields
.remove(&SlotLogicType::SortingClass)
.map(|field| (field.value as u32).into())
.unwrap_or(SortingClass::Default),
fields,
}
}
@@ -109,6 +114,7 @@ impl SlotOccupant {
quantity: 1,
max_quantity: 1,
damage: 0.0,
sorting_class: SortingClass::Default,
fields: HashMap::new(),
}
}
@@ -142,13 +148,54 @@ impl SlotOccupant {
self.fields.clone()
}
/// slot field operations don't fail
pub fn set_field(&mut self, field: SlotLogicType, val: f64) {
pub fn set_field(
&mut self,
field: SlotLogicType,
val: f64,
force: bool,
) -> Result<(), ICError> {
if let Some(logic) = self.fields.get_mut(&field) {
match logic.field_type {
FieldType::ReadWrite | FieldType::Write => logic.value = val,
_ => {}
FieldType::ReadWrite | FieldType::Write => {
logic.value = val;
Ok(())
}
_ => {
if force {
logic.value = val;
Ok(())
} else {
Err(ICError::ReadOnlyField(field.to_string()))
}
}
}
} else if force {
self.fields.insert(
field,
LogicField {
field_type: FieldType::ReadWrite,
value: val,
},
);
Ok(())
} else {
Err(ICError::ReadOnlyField(field.to_string()))
}
}
pub fn can_logic_read(&self, field: SlotLogicType) -> bool {
if let Some(logic) = self.fields.get(&field) {
matches!(logic.field_type, FieldType::Read | FieldType::ReadWrite)
} else {
false
}
}
pub fn can_logic_write(&self, field: SlotLogicType) -> bool {
if let Some(logic) = self.fields.get(&field) {
matches!(logic.field_type, FieldType::Write | FieldType::ReadWrite)
} else {
false
}
}
}
@@ -185,17 +232,6 @@ impl Slot {
.as_ref()
.map(|occupant| occupant.get_fields())
.unwrap_or_default();
copy.insert(
SlotLogicType::ReferenceId,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.id as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::Occupied,
LogicField {
@@ -214,17 +250,6 @@ impl Slot {
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::PrefabHash,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.prefab_hash as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::Quantity,
LogicField {
@@ -236,6 +261,24 @@ impl Slot {
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::Damage,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.damage)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::Class,
LogicField {
field_type: FieldType::Read,
value: self.typ as u32 as f64,
},
);
copy.insert(
SlotLogicType::MaxQuantity,
LogicField {
@@ -248,16 +291,41 @@ impl Slot {
},
);
copy.insert(
SlotLogicType::Class,
SlotLogicType::PrefabHash,
LogicField {
field_type: FieldType::Read,
value: self.typ as u32 as f64,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.prefab_hash as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::SortingClass,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.sorting_class as u32 as f64)
.unwrap_or(0.0),
},
);
copy.insert(
SlotLogicType::ReferenceId,
LogicField {
field_type: FieldType::Read,
value: self
.occupant
.as_ref()
.map(|occupant| occupant.id as f64)
.unwrap_or(0.0),
},
);
copy
}
/// the game returns 0.0 for all slotlogic types if they are not set
pub fn get_field(&self, field: SlotLogicType) -> f64 {
let fields = self.get_fields();
fields
@@ -269,9 +337,80 @@ impl Slot {
.unwrap_or(0.0)
}
pub fn set_field(&mut self, field: SlotLogicType, val: f64) {
pub fn can_logic_read(&self, field: SlotLogicType) -> bool {
match field {
SlotLogicType::Pressure | SlotLogicType::Temperature | SlotLogicType::Volume => {
matches!(
self.typ,
SlotType::GasCanister | SlotType::LiquidCanister | SlotType::LiquidBottle
)
}
SlotLogicType::Charge | SlotLogicType::ChargeRatio => {
matches!(self.typ, SlotType::Battery)
}
SlotLogicType::Open => matches!(
self.typ,
SlotType::Helmet | SlotType::Tool | SlotType::Appliance
),
SlotLogicType::Lock => matches!(self.typ, SlotType::Helmet),
SlotLogicType::FilterType => matches!(self.typ, SlotType::GasFilter),
_ => {
if let Some(occupant) = self.occupant.as_ref() {
occupant.can_logic_read(field)
} else {
false
}
}
}
}
pub fn can_logic_write(&self, field: SlotLogicType) -> bool {
match field {
SlotLogicType::Open => matches!(
self.typ,
SlotType::Helmet
| SlotType::GasCanister
| SlotType::LiquidCanister
| SlotType::LiquidBottle
),
SlotLogicType::On => matches!(
self.typ,
SlotType::Helmet | SlotType::Tool | SlotType::Appliance
),
SlotLogicType::Lock => matches!(self.typ, SlotType::Helmet),
_ => {
if let Some(occupant) = self.occupant.as_ref() {
occupant.can_logic_write(field)
} else {
false
}
}
}
}
pub fn set_field(
&mut self,
field: SlotLogicType,
val: f64,
force: bool,
) -> Result<(), ICError> {
if matches!(
field,
SlotLogicType::Occupied
| SlotLogicType::OccupantHash
| SlotLogicType::Quantity
| SlotLogicType::MaxQuantity
| SlotLogicType::Class
| SlotLogicType::PrefabHash
| SlotLogicType::SortingClass
| SlotLogicType::ReferenceId
) {
return Err(ICError::ReadOnlyField(field.to_string()));
}
if let Some(occupant) = self.occupant.as_mut() {
occupant.set_field(field, val);
occupant.set_field(field, val, force)
} else {
Err(ICError::SlotNotOccupied)
}
}
}
@@ -349,6 +488,55 @@ impl Connection {
}
}
#[derive(
Debug,
Default,
Clone,
Copy,
PartialEq,
Eq,
Hash,
strum_macros::Display,
EnumString,
EnumIter,
AsRefStr,
Serialize,
Deserialize,
)]
#[strum(serialize_all = "PascalCase")]
pub enum SortingClass {
#[default]
Default = 0,
Kits = 1,
Tools = 2,
Resources,
Food = 4,
Clothing,
Appliances,
Atmospherics,
Storage = 8,
Ores,
Ices,
}
impl From<u32> for SortingClass {
fn from(value: u32) -> Self {
match value {
1 => Self::Kits,
2 => Self::Tools,
3 => Self::Resources,
4 => Self::Food,
5 => Self::Clothing,
6 => Self::Appliances,
7 => Self::Atmospherics,
8 => Self::Storage,
9 => Self::Ores,
10 => Self::Ices,
_ => Self::Default,
}
}
}
#[derive(
Debug,
Default,
@@ -631,13 +819,19 @@ impl Device {
},
);
}
copy.insert(
LogicType::Power,
LogicField {
field_type: FieldType::Read,
value: if self.has_power() { 1.0 } else { 0.0 },
},
);
if self.has_power_state() {
copy.insert(
LogicType::Power,
LogicField {
field_type: FieldType::Read,
value: if self.has_power_connection() {
1.0
} else {
0.0
},
},
);
}
copy.insert(
LogicType::ReferenceId,
LogicField {
@@ -668,6 +862,55 @@ impl Device {
}
}
pub fn can_logic_read(&self, field: LogicType) -> bool {
match field {
LogicType::ReferenceId => true,
LogicType::LineNumber | LogicType::Error if self.ic.is_some() => true,
LogicType::Power if self.has_power_state() => true,
_ => {
if let Some(logic) = self.fields.get(&field) {
matches!(logic.field_type, FieldType::Read | FieldType::ReadWrite)
} else {
false
}
}
}
}
pub fn can_logic_write(&self, field: LogicType) -> bool {
match field {
LogicType::ReferenceId => false,
LogicType::LineNumber if self.ic.is_some() => true,
_ => {
if let Some(logic) = self.fields.get(&field) {
matches!(logic.field_type, FieldType::Write | FieldType::ReadWrite)
} else {
false
}
}
}
}
pub fn can_slot_logic_read(&self, field: SlotLogicType, slot: usize) -> bool {
if self.slots.is_empty() {
return false;
}
let Some(slot) = self.slots.get(slot) else {
return false;
};
slot.can_logic_read(field)
}
pub fn can_slot_logic_write(&self, field: SlotLogicType, slot: usize) -> bool {
if self.slots.is_empty() {
return false;
}
let Some(slot) = self.slots.get(slot) else {
return false;
};
slot.can_logic_write(field)
}
pub fn get_field(&self, typ: LogicType, vm: &VM) -> Result<f64, ICError> {
if typ == LogicType::LineNumber && self.ic.is_some() {
if let Ok(ic) = vm
@@ -678,8 +921,9 @@ impl Device {
{
Ok(ic.ip as f64)
} else {
// FIXME: the game succeeds in getting the correct line number
// when reading it's own IC
// HACK: the game succeeds in getting the correct line number
// when reading it's own IC, but we'll panic trying to do it here
// this is worked around in internal_step so just return 0 here
Ok(0.0)
}
} else if let Some(field) = self.get_fields(vm).get(&typ) {
@@ -693,9 +937,20 @@ impl Device {
}
}
pub fn set_field(&mut self, typ: LogicType, val: f64, vm: &VM) -> Result<(), ICError> {
if typ == LogicType::LineNumber && self.ic.is_some() {
// try borrow to set ip, we shoudl only fail if the ic is in us aka is is *our* ic
pub fn set_field(
&mut self,
typ: LogicType,
val: f64,
vm: &VM,
force: bool,
) -> Result<(), ICError> {
if typ == LogicType::ReferenceId
|| (typ == LogicType::Error && self.ic.is_some())
|| (typ == LogicType::Power && self.has_power_state())
{
Err(ICError::ReadOnlyField(typ.to_string()))
} else if typ == LogicType::LineNumber && self.ic.is_some() {
// try borrow to set ip, we should only fail if the ic is in use aka is is *our* ic
// in game trying to set your own ic's LineNumber appears to be a Nop so this is fine.
if let Ok(mut ic) = vm
.ics
@@ -707,12 +962,24 @@ impl Device {
}
Ok(())
} else if let Some(field) = self.fields.get_mut(&typ) {
if field.field_type == FieldType::Write || field.field_type == FieldType::ReadWrite {
if field.field_type == FieldType::Write
|| field.field_type == FieldType::ReadWrite
|| force
{
field.value = val;
Ok(())
} else {
Err(ICError::ReadOnlyField(typ.to_string()))
}
} else if force {
self.fields.insert(
typ,
LogicField {
field_type: FieldType::ReadWrite,
value: val,
},
);
Ok(())
} else {
Err(ICError::DeviceHasNoField(typ.to_string()))
}
@@ -737,8 +1004,9 @@ impl Device {
{
Ok(ic.ip as f64)
} else {
// FIXME: the game succeeds in getting the correct line number
// when reading it's own IC
// HACK: the game succeeds in getting the correct line number
// when reading it's own IC, but we'll panic trying to do it here
// this is worked around in internal_step so just return 0 here
Ok(0.0)
}
} else {
@@ -772,8 +1040,9 @@ impl Device {
},
);
} else {
// FIXME: the game succeeds in getting the correct line number
// when reading it's own IC
// HACK: the game succeeds in getting the correct line number
// when reading it's own IC, but we'll panic trying to do it here
// this is worked around in internal_step so just return 0 here
fields.insert(
SlotLogicType::LineNumber,
LogicField {
@@ -792,6 +1061,7 @@ impl Device {
typ: grammar::SlotLogicType,
val: f64,
vm: &VM,
force: bool,
) -> Result<(), ICError> {
let slot = self
.slots
@@ -814,8 +1084,7 @@ impl Device {
}
Ok(())
} else {
slot.set_field(typ, val);
Ok(())
slot.set_field(typ, val, force)
}
}
@@ -839,17 +1108,27 @@ impl Device {
self.name = Some(name.to_owned());
}
pub fn has_power(&self) -> bool {
pub fn has_power_state(&self) -> bool {
self.connections.iter().any(|conn| {
if let Connection::CableNetwork { net, typ } = conn {
net.is_some()
&& matches!(
typ,
CableConnectionType::Power | CableConnectionType::PowerAndData
)
} else {
false
}
matches!(
conn,
Connection::CableNetwork {
typ: CableConnectionType::Power | CableConnectionType::PowerAndData,
..
}
)
})
}
pub fn has_power_connection(&self) -> bool {
self.connections.iter().any(|conn| {
matches!(
conn,
Connection::CableNetwork {
net: Some(_),
typ: CableConnectionType::Power | CableConnectionType::PowerAndData,
}
)
})
}
}
@@ -1541,11 +1820,14 @@ impl VM {
prefab: f64,
typ: LogicType,
val: f64,
write_readonly: bool,
) -> Result<(), ICError> {
self.batch_device(source, prefab, None)
.map(|device| {
self.set_modified(device.borrow().id);
device.borrow_mut().set_field(typ, val, self)
device
.borrow_mut()
.set_field(typ, val, self, write_readonly)
})
.try_collect()
}
@@ -1557,11 +1839,14 @@ impl VM {
index: f64,
typ: SlotLogicType,
val: f64,
write_readonly: bool,
) -> Result<(), ICError> {
self.batch_device(source, prefab, None)
.map(|device| {
self.set_modified(device.borrow().id);
device.borrow_mut().set_slot_field(index, typ, val, self)
device
.borrow_mut()
.set_slot_field(index, typ, val, self, write_readonly)
})
.try_collect()
}
@@ -1573,11 +1858,14 @@ impl VM {
name: f64,
typ: LogicType,
val: f64,
write_readonly: bool,
) -> Result<(), ICError> {
self.batch_device(source, prefab, Some(name))
.map(|device| {
self.set_modified(device.borrow().id);
device.borrow_mut().set_field(typ, val, self)
device
.borrow_mut()
.set_field(typ, val, self, write_readonly)
})
.try_collect()
}

View File

@@ -288,18 +288,18 @@ impl DeviceRef {
}
#[wasm_bindgen(js_name = "setField")]
pub fn set_field(&self, field: &str, value: f64) -> Result<(), JsError> {
pub fn set_field(&self, field: &str, value: f64, force: bool) -> Result<(), JsError> {
let logic_typ = LogicType::from_str(field)?;
let mut device_ref = self.device.borrow_mut();
device_ref.set_field(logic_typ, value, &self.vm.borrow())?;
device_ref.set_field(logic_typ, value, &self.vm.borrow(), force)?;
Ok(())
}
#[wasm_bindgen(js_name = "setSlotField")]
pub fn set_slot_field(&self, slot: f64, field: &str, value: f64) -> Result<(), JsError> {
pub fn set_slot_field(&self, slot: f64, field: &str, value: f64, force: bool) -> Result<(), JsError> {
let logic_typ = SlotLogicType::from_str(field)?;
let mut device_ref = self.device.borrow_mut();
device_ref.set_slot_field(slot, logic_typ, value, &self.vm.borrow())?;
device_ref.set_slot_field(slot, logic_typ, value, &self.vm.borrow(), force)?;
Ok(())
}

View File

@@ -430,18 +430,22 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) {
_handleChangeField(e: CustomEvent) {
const input = e.target as SlInput;
const field = input.getAttribute("key")!;
const field = input.getAttribute("key")! as LogicType;
const val = parseNumber(input.value);
window.VM?.setDeviceField(this.deviceID, field, val);
if (!window.VM?.setDeviceField(this.deviceID, field, val, true)) {
input.value = this.fields.get(field).value.toString();
}
this.updateDevice();
}
_handleChangeSlotField(e: CustomEvent) {
const input = e.target as SlInput;
const slot = parseInt(input.getAttribute("slotIndex")!);
const field = input.getAttribute("key")!;
const field = input.getAttribute("key")! as SlotLogicType;
const val = parseNumber(input.value);
window.VM?.setDeviceSlotField(this.deviceID, slot, field, val);
if (!window.VM?.setDeviceSlotField(this.deviceID, slot, field, val, true)) {
input.value = this.device.getSlotField(slot, field).toString();
}
this.updateDevice();
}

View File

@@ -1,4 +1,4 @@
import { DeviceRef, DeviceTemplate, VM, init } from "ic10emu_wasm";
import { DeviceRef, DeviceTemplate, LogicType, SlotLogicType, VM, init } from "ic10emu_wasm";
import { DeviceDB } from "./device_db";
import "./base_device";
@@ -273,11 +273,12 @@ class VirtualMachine extends EventTarget {
return false;
}
setDeviceField(id: number, field: string, val: number): boolean {
setDeviceField(id: number, field: LogicType, val: number, force?: boolean): boolean {
force = force ?? false;
const device = this._devices.get(id);
if (device) {
try {
device.setField(field, val);
device.setField(field, val, force);
this.updateDevice(device);
return true;
} catch (err) {
@@ -287,11 +288,12 @@ class VirtualMachine extends EventTarget {
return false;
}
setDeviceSlotField(id: number, slot: number, field: string, val: number): boolean {
setDeviceSlotField(id: number, slot: number, field: SlotLogicType, val: number, force?: boolean): boolean {
force = force ?? false;
const device = this._devices.get(id);
if (device) {
try {
device.setSlotField(slot, field, val);
device.setSlotField(slot, field, val, false);
this.updateDevice(device);
return true;
} catch (err) {