From b10c8ea24804197c671cf91550867e1fa7dea917 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Fri, 27 Mar 2026 12:56:36 +0000 Subject: [PATCH 1/3] add missing resource attributes to visualizer info panel --- pylabrobot/visualizer/lib.js | 46 +++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/pylabrobot/visualizer/lib.js b/pylabrobot/visualizer/lib.js index cd2ea4452e1..f5026185ef0 100644 --- a/pylabrobot/visualizer/lib.js +++ b/pylabrobot/visualizer/lib.js @@ -708,6 +708,10 @@ class Resource { this.resourceType = resourceData.type || this.constructor.name; this.category = resourceData.category || ""; this.methods = resourceData.methods || []; + this.model = resourceData.model || null; + this.rotation = resourceData.rotation || null; + this.barcode = resourceData.barcode || null; + this.preferred_pickup_location = resourceData.preferred_pickup_location || null; this.color = "#5B6D8F"; @@ -1401,8 +1405,9 @@ class Well extends Container { constructor(resourceData, parent) { super(resourceData, parent); - const { cross_section_type } = resourceData; + const { cross_section_type, bottom_type } = resourceData; this.cross_section_type = cross_section_type; + this.bottom_type = bottom_type || null; } serialize() { @@ -4378,33 +4383,46 @@ function getUmlAttributes(resource) { var attrs = []; attrs.push({ key: "name", value: JSON.stringify(resource.name) }); attrs.push({ key: "type", value: resource.resourceType || resource.constructor.name }); + attrs.push({ key: "children", value: resource.children ? resource.children.length : 0 }); + if (resource.model) { + attrs.push({ key: "model", value: resource.model }); + } + var abs = resource.getAbsoluteLocation(); + attrs.push({ key: "abs_location", value: "(" + abs.x.toFixed(1) + ", " + abs.y.toFixed(1) + ", " + (abs.z || 0).toFixed(1) + ")" }); + if (resource.rotation) { + attrs.push({ key: "rotation", value: "(" + resource.rotation.x + ", " + resource.rotation.y + ", " + resource.rotation.z + ")" }); + } attrs.push({ key: "size_x", value: resource.size_x }); attrs.push({ key: "size_y", value: resource.size_y }); attrs.push({ key: "size_z", value: resource.size_z }); if (resource.location) { attrs.push({ key: "location", value: "(" + resource.location.x + ", " + resource.location.y + ", " + (resource.location.z || 0) + ")" }); } - var abs = resource.getAbsoluteLocation(); - attrs.push({ key: "abs_location", value: "(" + abs.x.toFixed(1) + ", " + abs.y.toFixed(1) + ", " + (abs.z || 0).toFixed(1) + ")" }); attrs.push({ key: "parent", value: resource.parent ? JSON.stringify(resource.parent.name) : "none" }); - attrs.push({ key: "children", value: resource.children ? resource.children.length : 0 }); - if (resource.category) { - attrs.push({ key: "category", value: resource.category }); + if (resource.barcode) { + attrs.push({ key: "barcode", value: resource.barcode.data + " (" + resource.barcode.symbology + ", " + resource.barcode.position_on_resource + ")" }); + } + if (resource.preferred_pickup_location) { + var ppl = resource.preferred_pickup_location; + attrs.push({ key: "preferred_pickup_location", value: "(" + ppl.x + ", " + ppl.y + ", " + ppl.z + ")" }); } + // Well-specific (before general Container attrs) + if (resource instanceof Well) { + if (resource.cross_section_type) { + attrs.push({ key: "cross_section_type", value: resource.cross_section_type }); + } + if (resource.bottom_type) { + attrs.push({ key: "bottom_type", value: resource.bottom_type }); + } + } // Container (Well/Trough/Tube) if (resource instanceof Container) { - attrs.push({ key: "max_volume", value: resource.maxVolume }); - attrs.push({ key: "volume", value: resource.volume }); if (resource.material_z_thickness != null) { attrs.push({ key: "material_z_thickness", value: resource.material_z_thickness }); } - } - // Well-specific - if (resource instanceof Well) { - if (resource.cross_section_type) { - attrs.push({ key: "cross_section_type", value: resource.cross_section_type }); - } + attrs.push({ key: "max_volume", value: resource.maxVolume }); + attrs.push({ key: "volume", value: resource.volume }); } // TipSpot if (resource instanceof TipSpot) { From 98190ce544f78b9ab26dc8ab0fcec3afa27bbdd3 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Fri, 27 Mar 2026 13:02:35 +0000 Subject: [PATCH 2/3] show `height_volume_data` presence in visualizer panel --- pylabrobot/visualizer/lib.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylabrobot/visualizer/lib.js b/pylabrobot/visualizer/lib.js index f5026185ef0..70664c0085f 100644 --- a/pylabrobot/visualizer/lib.js +++ b/pylabrobot/visualizer/lib.js @@ -1346,6 +1346,7 @@ class Container extends Resource { this.maxVolume = max_volume; this.volume = resourceData.volume || 0; this.material_z_thickness = material_z_thickness; + this.has_height_volume_data = resourceData.height_volume_data != null; } static _liquidRGB = null; @@ -4421,6 +4422,7 @@ function getUmlAttributes(resource) { if (resource.material_z_thickness != null) { attrs.push({ key: "material_z_thickness", value: resource.material_z_thickness }); } + attrs.push({ key: "height_volume_data", value: resource.has_height_volume_data ? "exists" : "None" }); attrs.push({ key: "max_volume", value: resource.maxVolume }); attrs.push({ key: "volume", value: resource.volume }); } From c5cbd783c984614987d7a49c66ac53db433f5bc8 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Fri, 27 Mar 2026 13:12:06 +0000 Subject: [PATCH 3/3] address Copilot suggestions --- pylabrobot/visualizer/lib.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pylabrobot/visualizer/lib.js b/pylabrobot/visualizer/lib.js index 70664c0085f..c3cee375662 100644 --- a/pylabrobot/visualizer/lib.js +++ b/pylabrobot/visualizer/lib.js @@ -1006,6 +1006,10 @@ class Resource { size_z: this.size_z, children: serializedChildren, parent_name: this.parent === undefined ? null : this.parent.name, + model: this.model, + rotation: this.rotation, + barcode: this.barcode, + preferred_pickup_location: this.preferred_pickup_location, }; } @@ -1415,6 +1419,7 @@ class Well extends Container { return { ...super.serialize(), cross_section_type: this.cross_section_type, + bottom_type: this.bottom_type, }; }