From b16ee32da8611a11ad009beb055dfeba39bcf0c9 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Wed, 15 May 2024 15:56:17 +0100 Subject: [PATCH 01/16] Fix linting errors and tidy the devices updater --- nimble_orchestration/devices_json_updater.py | 51 ++++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/nimble_orchestration/devices_json_updater.py b/nimble_orchestration/devices_json_updater.py index 23a01f7..f42808e 100755 --- a/nimble_orchestration/devices_json_updater.py +++ b/nimble_orchestration/devices_json_updater.py @@ -1,5 +1,9 @@ #!/usr/bin/env python +""" +This module is used to update the devices.json file from a CSV downloaded from nocodb. +""" + import sys import csv import json @@ -14,10 +18,14 @@ def usage(): print(" ./devices_json_updater.py [path_to_nocodb_csv_file]") def main(): + """ + Main script to turn a CSV into a + """ + # Check to make sure the user passed a CSV path if len(sys.argv) != 2: usage() - quit() + sys.exit(0) # Keeps track of the device entries devices = [] @@ -25,40 +33,31 @@ def main(): # Retrieve the CSV path that the user should have passed csv_path = sys.argv[1] - # Allows us to assume that the first line of the file will be header data - getting_headers = True - headers = None - with open(csv_path, newline='') as csvfile: + with open(csv_path, newline='', encoding="utf-8") as csvfile: # Parse the entire file into rows devices_csv = csv.reader(csvfile, delimiter=',') + headers = next(devices_csv) + # Process the rows into objects with key/value pairs for row in devices_csv: # The current object being assembled - cur_obj = {} - - # Handle the first line as headers - if getting_headers: - getting_headers = False - headers = row - print(headers) - # All other rows are device data - else: - # Generate a unique ID for each device - device_id = row[1].replace(" ", "_") - cur_obj["ID"] = device_id - - # Match each row with its header - for i, entry in enumerate(row): - cur_obj[headers[i]] = entry - - # Save the current device - devices.append(cur_obj) + + # Generate a unique ID for each device + device_id = row[1].replace(" ", "_") + cur_obj = {"ID": device_id} + + # Match each row with its header + for i, entry in enumerate(row): + cur_obj[headers[i]] = entry + + # Save the current device + devices.append(cur_obj) # Write the JSON data to file - with open('../devices.json', 'w') as f: + with open('../devices.json', 'w', encoding="utf-8") as f: json.dump(devices, f) if __name__ == "__main__": - main() \ No newline at end of file + main() From 6334b69b13ffaefd5f06a88948d2348263fe954b Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Wed, 15 May 2024 16:26:07 +0100 Subject: [PATCH 02/16] Fixing linting errors and cleaning code in nimble-orchestration --- .../assembly_def_generator.py | 4 +- nimble_orchestration/components.py | 11 +-- nimble_orchestration/configuration.py | 4 +- nimble_orchestration/device.py | 39 ++++++----- nimble_orchestration/devices_json_updater.py | 3 +- .../exsource_def_generator.py | 4 +- nimble_orchestration/yaml_cleaner.py | 67 ++++++++++--------- 7 files changed, 73 insertions(+), 59 deletions(-) diff --git a/nimble_orchestration/assembly_def_generator.py b/nimble_orchestration/assembly_def_generator.py index 293bde0..5304437 100644 --- a/nimble_orchestration/assembly_def_generator.py +++ b/nimble_orchestration/assembly_def_generator.py @@ -5,7 +5,7 @@ import pathlib import yaml -from nimble_orchestration.yaml_cleaner import YamlCleaner +from nimble_orchestration import yaml_cleaner class AssemblyDefGenerator: """ @@ -44,4 +44,4 @@ def save(self, output_file: str | pathlib.Path): } } with open(output_file, "w", encoding="utf-8") as f: - yaml.dump(YamlCleaner.clean(data), f, sort_keys=False) + yaml.dump(yaml_cleaner.clean(data), f, sort_keys=False) diff --git a/nimble_orchestration/components.py b/nimble_orchestration/components.py index 27f761e..ec00240 100644 --- a/nimble_orchestration/components.py +++ b/nimble_orchestration/components.py @@ -2,11 +2,12 @@ This components module contains classes for holding the information for components in a nimble rack. -`MechanicalComponent` is a base class and can also be used for generic components that have no - source code -`GeneratedMechanicalComponent` is a child class of `MechanicalComponent`, it contains all the information - for exsource to generate the CAD models for this component -`AssembledComponent` Is a class that holds very basic assmbly information for a given `MechanicalComponent` +`MechanicalComponent` is a base class and can also be used for generic components + that have no source code +`GeneratedMechanicalComponent` is a child class of `MechanicalComponent`, it contains + all the information for exsource to generate the CAD models for this component +`AssembledComponent` Is a class that holds very basic assmbly information for a given + `MechanicalComponent` """ from copy import copy, deepcopy diff --git a/nimble_orchestration/configuration.py b/nimble_orchestration/configuration.py index 4b2ad42..8ac5e22 100644 --- a/nimble_orchestration/configuration.py +++ b/nimble_orchestration/configuration.py @@ -112,7 +112,7 @@ def total_height_in_units(self): """ Return the total height of the needed rack in units """ - return sum([device.height_in_units for device in self._devices]) + return sum(device.height_in_units for device in self._devices) def _generate_assembled_components_list(self): @@ -236,7 +236,7 @@ def _trays(self): x_pos = -self._rack_params.tray_width / 2.0 y_pos = -self._rack_params.single_width / 2.0 - 4 z_pos = z_offset + height_in_u * self._rack_params.mounting_hole_spacing - tray_id = device.get_tray_id() + tray_id = device.tray_id component = GeneratedMechanicalComponent( key=tray_id, diff --git a/nimble_orchestration/device.py b/nimble_orchestration/device.py index ed13b7d..5b290d3 100644 --- a/nimble_orchestration/device.py +++ b/nimble_orchestration/device.py @@ -1,7 +1,10 @@ +""" +Contains an object that represents the information of a device in the devices.json file. +""" class Device: """ - Represents a device from the devices.json file + Represents a subset of the information for a device from the devices.json file Example node: { @@ -34,19 +37,23 @@ class Device: } """ - def __init__(self, jsonNode): - self.id = jsonNode['ID'] - # self.name = jsonNode['Name'] - self.name = jsonNode['Hardware'] - # self.category = jsonNode['Category'] - self.category = jsonNode['Type'] - self.height_in_units = int(jsonNode['HeightUnits']) - # self.width = jsonNode['Width'] - self.width = jsonNode['LengthMm'] - self.depth = jsonNode['Depth'] - self.shelf_id = jsonNode['ShelfId'] - # self.tray_type = jsonNode['TrayType'] - self.shelf_type = jsonNode['Shelf'] + def __init__(self, json_node): + self.id = json_node['ID'] + # self.name = json_node['Name'] + self.name = json_node['Hardware'] + # self.category = json_node['Category'] + self.category = json_node['Type'] + self.height_in_units = int(json_node['HeightUnits']) + # self.width = json_node['Width'] + self.width = json_node['LengthMm'] + self.depth = json_node['Depth'] + self.shelf_id = json_node['ShelfId'] + # self.tray_type = json_node['TrayType'] + self.shelf_type = json_node['Shelf'] - def get_tray_id(self): - return f"tray_h{self.height_in_units}_t{self.shelf_type.lower().replace(' ', '_')}" \ No newline at end of file + @property + def tray_id(self): + """ + Return and identification for the shelf. + """ + return f"tray_h{self.height_in_units}_t{self.shelf_type.lower().replace(' ', '_')}" diff --git a/nimble_orchestration/devices_json_updater.py b/nimble_orchestration/devices_json_updater.py index f42808e..239bc0b 100755 --- a/nimble_orchestration/devices_json_updater.py +++ b/nimble_orchestration/devices_json_updater.py @@ -13,7 +13,8 @@ def usage(): Prints a usage message for this utility. """ print("This utility exists to update the devices.json file in the root of the repository.") - print("The orchestration and generation scripts rely on this file to generate the models and assemblies.") + print("The orchestration and generation scripts rely on this file to generate the models " + "and assemblies.") print("Usage:") print(" ./devices_json_updater.py [path_to_nocodb_csv_file]") diff --git a/nimble_orchestration/exsource_def_generator.py b/nimble_orchestration/exsource_def_generator.py index b7eca68..a527679 100644 --- a/nimble_orchestration/exsource_def_generator.py +++ b/nimble_orchestration/exsource_def_generator.py @@ -40,7 +40,7 @@ import pathlib import yaml -from nimble_orchestration.yaml_cleaner import YamlCleaner +from nimble_orchestration import yaml_cleaner class ExsourceDefGenerator: @@ -86,4 +86,4 @@ def save(self, output_file: str | pathlib.Path): "exports": self._parts } with open(output_file, "w", encoding="utf-8") as f: - yaml.dump(YamlCleaner.clean(data), f, sort_keys=False) + yaml.dump(yaml_cleaner.clean(data), f, sort_keys=False) diff --git a/nimble_orchestration/yaml_cleaner.py b/nimble_orchestration/yaml_cleaner.py index 24cfa09..49f317d 100644 --- a/nimble_orchestration/yaml_cleaner.py +++ b/nimble_orchestration/yaml_cleaner.py @@ -1,37 +1,42 @@ +""" +A module containting functions to clean up dictionaries before writing to yaml file. +""" + import pathlib -class YamlCleaner: +def clean(data: dict) -> dict: """ - Clean up data before writing to yaml file. + Clean the input dictionary (recursively). Removing any keys where the value is + none, changing pathlib.Path to strings and converting tuples to strings. + + The input is a single dictionary, the output is a cleaned dictionary """ - @classmethod - def clean(cls, data: dict) -> dict: - # iterate over entries - keys_to_delete = [] - for key, value in data.items(): - # remove empty entries - if value is None: - keys_to_delete.append(key) - else: - data[key] = cls.clean_object(value) - # delete empty entries - for key in keys_to_delete: - del data[key] - return data + # iterate over entries + keys_to_delete = [] + for key, value in data.items(): + # remove empty entries + if value is None: + keys_to_delete.append(key) + else: + data[key] = _clean_object(value) + # delete empty entries + for key in keys_to_delete: + del data[key] + return data + - @classmethod - def clean_object(cls, obj: object) -> object: - # clean up lists - if isinstance(obj, list): - return [cls.clean_object(x) for x in obj] - # clean up dicts - if isinstance(obj, dict): - return cls.clean(obj) - if isinstance(obj, tuple): - # convert to string like "(1,2,3)" - return str(obj) - if isinstance(obj, pathlib.Path): - # convert to string - return str(obj) - return obj \ No newline at end of file +def _clean_object(obj: object) -> object: + # clean up lists + if isinstance(obj, list): + return [_clean_object(x) for x in obj] + # clean up dicts + if isinstance(obj, dict): + return clean(obj) + if isinstance(obj, tuple): + # convert to string like "(1,2,3)" + return str(obj) + if isinstance(obj, pathlib.Path): + # convert to string + return str(obj) + return obj From ef1ca77dc2fbd9c28d0616aca6835a00d72bfde2 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Wed, 15 May 2024 16:26:36 +0100 Subject: [PATCH 03/16] Remove export module that is superceeded by exsource --- nimble_orchestration/export.py | 50 ---------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 nimble_orchestration/export.py diff --git a/nimble_orchestration/export.py b/nimble_orchestration/export.py deleted file mode 100644 index 29b7603..0000000 --- a/nimble_orchestration/export.py +++ /dev/null @@ -1,50 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Andreas Kahler -# -# SPDX-License-Identifier: AGPL-3.0-or-later - -import cadquery as cq -import cadscript - -def export_svg(part, filename): - ''' - Export a part to SVG. - ''' - - if isinstance(part, cadscript.Body): - part = part.cq() - - cq.exporters.export(part, - str(filename), - opt={ - "width": 300, - "height": 300, - "marginLeft": 10, - "marginTop": 10, - "showAxes": False, - "projectionDir": (1, 1, 1), - "strokeWidth": 0.8, - "strokeColor": (0, 0, 0), - "hiddenColor": (0, 0, 255), - "showHidden": False, - },) - -def export_step(part, filename): - ''' - Export a part as STEP. - ''' - - if isinstance(part, cadscript.Body): - part = part.cq() - - cq.exporters.export(part, str(filename), exportType="STEP") - - -def export_stl(part, filename): - ''' - Export a part as STL. - ''' - - if isinstance(part, cadscript.Body): - part = part.cq() - - cq.exporters.export(part, str(filename), exportType="STL") \ No newline at end of file From 0ade894135aefbf9f7d347467127373cd12b8b97 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Wed, 15 May 2024 16:27:10 +0100 Subject: [PATCH 04/16] Fix minor formatting issues in linting script --- lint_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lint_test.py b/lint_test.py index 4868e03..9f6b2a7 100755 --- a/lint_test.py +++ b/lint_test.py @@ -55,8 +55,6 @@ def lint(pylint_args): something needs doing in the future and you want to push to master then make an issue. """ - - output = Run(pylint_args, exit=False) From fef6042a3052315f300727532ab5035e60b0f2ca Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Wed, 15 May 2024 19:13:48 +0100 Subject: [PATCH 05/16] starting to make nimble builder parametric --- mechanical/components/cadquery/rack_leg.py | 35 ++++++++++------ mechanical/components/cadquery/tray_6in.py | 3 +- nimble_builder/__init__.py | 37 ++++++++++++++++- nimble_builder/helpers.py | 5 ++- nimble_builder/nimble_end_plate.py | 43 ++++++++++++-------- nimble_builder/shelf_builder.py | 46 +++++++++++----------- nimble_orchestration/configuration.py | 33 +--------------- 7 files changed, 114 insertions(+), 88 deletions(-) diff --git a/mechanical/components/cadquery/rack_leg.py b/mechanical/components/cadquery/rack_leg.py index aad1138..0f00c10 100644 --- a/mechanical/components/cadquery/rack_leg.py +++ b/mechanical/components/cadquery/rack_leg.py @@ -1,4 +1,4 @@ -from math import e, floor +from math import floor import cadscript as cad import nimble_builder @@ -6,15 +6,23 @@ # parameters to be set in exsource-def.yaml file length = 294.0 -hole_spacing = 14.0 long_axis_hole_dia = 4.6 mounting_holes_dia = 3.6 -def make_rack_leg(length, hole_spacing, long_axis_hole_dia, mounting_holes_dia) -> cad.Body: +def make_rack_leg( + length, + long_axis_hole_dia, + mounting_holes_dia, + rack_params=None + ) -> cad.Body: + + if not rack_params: + rack_params = nimble_builder.RackParameters() + # Construct the overall shape - leg = cad.make_box(nimble_builder.beam_width, nimble_builder.beam_width, length) - leg = leg.fillet("|Z", nimble_builder.corner_fillet) + leg = cad.make_box(rack_params.beam_width, rack_params.beam_width, length) + leg = leg.fillet("|Z", rack_params.corner_fillet) # Long-axis hole for connecting multiple leg sections together long_axis_hole = cad.make_sketch() @@ -22,22 +30,25 @@ def make_rack_leg(length, hole_spacing, long_axis_hole_dia, mounting_holes_dia) leg = leg.cut_extrude(">Z", long_axis_hole, -length) # Calculate the count of the holes in the Y direction based on the total length - number_of_holes = int(floor(length / hole_spacing)) + number_of_holes = int(floor(length / rack_params.mounting_hole_spacing)) # Mounting holes - mount_hole_ptn = cad.pattern_grid(count_x=1, count_y=number_of_holes, spacing_y=hole_spacing) + mount_hole_ptn = cad.pattern_grid( + count_x=1, + count_y=number_of_holes, + spacing_y=rack_params.mounting_hole_spacing + ) sketch = cad.make_sketch() sketch.add_circle(diameter=mounting_holes_dia, positions=mount_hole_ptn) - leg.cut_extrude(" cad.Body: if shelf_type == "usw-flex-mini": b = get_builder(hole_count) b.side_wall_thickness = 3.8 # extra thick to have thinner tray - b.init_values() # re-init to apply the new thickness b.make_front(front_type="full", bottom_type="closed") b.cut_opening("Z", cutout, -height) # Add the corner mounting holes with countersinks - hole_positions = cad.pattern_rect(width - nimble_builder.beam_width, depth - nimble_builder.beam_width) - plate.cut_hole(">Z", d=hole_dia, d2=hole_countersink_dia, countersink_angle=90, pos=hole_positions) + hole_positions = cad.pattern_rect( + width - rack_params.beam_width, + depth - rack_params.beam_width + ) + plate.cut_hole( + ">Z", + d=rack_params.end_plate_hole_dia, + d2=rack_params.end_plate_hole_countersink_dia, + countersink_angle=90, + pos=hole_positions + ) # add "rails" between the holes # basic shape with chamfers - rail = cad.make_box(rail_length, rail_width, height + rail_height, center="XY") - rail.chamfer(">Z and |Y", rail_height) + rail = cad.make_box( + rail_length, + rack_params.end_plate_rail_width, + height + rack_params.end_plate_rail_height, + center="XY" + ) + rail.chamfer(">Z and |Y", rack_params.end_plate_rail_height) # add 4 instances rail.move((0, rail_offset, 0)) for _ in range(4): diff --git a/nimble_builder/shelf_builder.py b/nimble_builder/shelf_builder.py index fed87a4..8b32f6b 100644 --- a/nimble_builder/shelf_builder.py +++ b/nimble_builder/shelf_builder.py @@ -7,19 +7,13 @@ from cadscript.interval import Interval2D, Interval1D -from .helpers import cut_slots, cut_w_pattern +from nimble_builder.helpers import cut_slots, cut_w_pattern +import nimble_builder - - -# standard sizes for the shelf -beam_width = 20 # width and depth of the beams that the front panels are attached to width_6in = 155 # full width (front panel) of the 6 inch nimble rack width_10in = 254 # full width (front panel) of the 10 inch rack width_10in_reduced = 250 # front panel width for 10 inch rack, reduced to fit into a 250mm wide printer -# standard distances between the holes in the front panels (6 inch nimble rack) -hole_dist_y = 14 - no_slots = False # speedup for debugging @@ -41,6 +35,7 @@ class ShelfBuilder: side_wall_offset = 16 # distance between side walls to the bouding box of the rack _shelf: cad.Body + _has_front: bool _vertical_hole_count: int _width_type: Literal["6inch", "10inch", "10inch_reduced"] _width: float @@ -55,21 +50,24 @@ class ShelfBuilder: def __init__(self, vertical_hole_count: int, width: Literal["6inch", "10inch", "10inch_reduced"], + rack_params=None ) -> None: """ Initialize the shelf builder """ + + if not rack_params: + rack_params = nimble_builder.RackParameters() + self._rack_params = rack_params + self._vertical_hole_count = vertical_hole_count self._width_type = width self._width = width_6in if width == "6inch" else width_10in if width == "10inch" else width_10in_reduced - self.init_values() - - def init_values(self): - self._height = self._vertical_hole_count * hole_dist_y - self._hole_dist_x = self._width - beam_width - self._hole_offset_y = hole_dist_y / 2 - self._inner_width = self._width - 2 * beam_width - 2 * self.side_wall_thickness - self._front_depth = beam_width + 2.75 # the size of the front panel part in y direction + self._height = self._vertical_hole_count * self._rack_params.mounting_hole_spacing + self._hole_dist_x = self._width - self._rack_params.beam_width + self._hole_offset_y = self._rack_params.mounting_hole_spacing / 2 + self._inner_width = self._width - 2 * self._rack_params.beam_width - 2 * self.side_wall_thickness + self._front_depth = self._rack_params.beam_width + 2.75 # the size of the front panel part in y direction self._padding_front = self._front_depth # the space between the front panel and where slots etc can start at the try bottom def make_front(self, @@ -87,7 +85,7 @@ def make_front(self, sketch.add_rect(self._width, (-self.panel_thickness, 0), center="X") # wall next to the beam if beam_wall_type != "none": - sketch.add_rect((self._inner_width / 2, self._width / 2 - beam_width), + sketch.add_rect((self._inner_width / 2, self._width / 2 - self._rack_params.beam_width), self._front_depth, center=False) sketch.mirror("X") @@ -97,7 +95,7 @@ def make_front(self, front.cut_hole("Y and >Z and |X", min(self._height, beam_width)) + front.chamfer(">Y and >Z and |X", min(self._height, self._rack_params.beam_width)) self._shelf = front @@ -190,7 +188,7 @@ def make_tray(self, # for thin trays connect the walls if sides != "open" and self._front_depth > 0: - if plate_width > self._width - 2 * beam_width: + if plate_width > self._width - 2 * self._rack_params.beam_width: # broad tray left = self._inner_width / 2 right = plate_width / 2 @@ -202,7 +200,7 @@ def make_tray(self, if abs(left - right) > 0.5: extra_sketch = cad.make_sketch() extra_sketch.add_rect((left, right), - (beam_width + 0.25, self._front_depth)) + (self._rack_params.beam_width + 0.25, self._front_depth)) extra_sketch.mirror("X") extra_walls = cad.make_extrude("XY", extra_sketch, self._height) self._shelf.add(extra_walls) @@ -423,7 +421,7 @@ def get_body(self) -> cad.Body: # for development and debugging if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): - b = ShelfBuilder(vertical_hole_count=3, width="10inch") - b.make_front(front_type="slots", bottom_type="closed") - b.make_tray(width="standard", depth=80, sides="ramp", back="open") - cad.show(b._shelf) + shelf = ShelfBuilder(vertical_hole_count=3, width="10inch") + shelf.make_front(front_type="slots", bottom_type="closed") + shelf.make_tray(width="standard", depth=80, sides="ramp", back="open") + cad.show(shelf.get_body()) diff --git a/nimble_orchestration/configuration.py b/nimble_orchestration/configuration.py index 8ac5e22..e642ae8 100644 --- a/nimble_orchestration/configuration.py +++ b/nimble_orchestration/configuration.py @@ -5,46 +5,17 @@ import os -from dataclasses import dataclass from copy import deepcopy import posixpath import json +from nimble_builder import RackParameters + from nimble_orchestration.assembly_def_generator import AssemblyDefGenerator from nimble_orchestration.components import GeneratedMechanicalComponent, AssembledComponent from nimble_orchestration.device import Device from nimble_orchestration.paths import MODULE_PATH, REL_MECH_DIR - -@dataclass -class RackParameters: - """ - A class to hold the RackParameters, both fixed and derived - """ - - beam_width: float = 20.0 - single_width: float = 155 - tray_depth: float = 115 - mounting_hole_spacing: float = 14 - base_plate_thickness: float = 3 - top_plate_thickness: float = 3 - base_clearance: float = 4 - bottom_tray_offet: float = 5 - - @property - def tray_width(self): - """ - Return derived parameter for the width of a standard tray - """ - return self.single_width - 2 * self.beam_width - - def beam_height(self, total_height_in_units): - """ - Return derived parameter for the height of a beam for a rack with a given - total height specified in units. - """ - return self.base_clearance + total_height_in_units * self.mounting_hole_spacing - class NimbleConfiguration: """ This class represents a specific nimble configuration From 47b2a677094930a2f331e5054edc8be3ed94a18a Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Wed, 15 May 2024 20:03:31 +0100 Subject: [PATCH 06/16] Trying to unify names for same values --- mechanical/assembly_renderer.py | 11 ++-- mechanical/components/cadquery/nimble_tray.py | 14 +++--- mechanical/components/cadquery/tray_6in.py | 4 +- nimble_builder/__init__.py | 27 ++++++++-- nimble_builder/shelf_builder.py | 50 +++++++++---------- nimble_orchestration/configuration.py | 24 ++++----- nimble_orchestration/device.py | 4 +- server/nimble_server.py | 2 +- 8 files changed, 80 insertions(+), 56 deletions(-) diff --git a/mechanical/assembly_renderer.py b/mechanical/assembly_renderer.py index 0f991a0..36538c1 100644 --- a/mechanical/assembly_renderer.py +++ b/mechanical/assembly_renderer.py @@ -15,9 +15,6 @@ assembly-step: '2' """ -# parameters -# can be set in exsource-def.yaml file -assembly_definition_file = "assembly-def.yaml" import os from pathlib import Path @@ -71,7 +68,12 @@ def generate(self) -> cq.Assembly: cq_part = cq.importers.importStep(part.step_file) for tag in part.tags: cq_part = cq_part.tag(tag) - assembly.add(cq_part, name=part.name, loc=cq.Location(part.position)) + #Pylint appears to be confused by the multimethod __init__ used by cq.Location + assembly.add( + cq_part, + name=part.name, + loc=cq.Location(part.position) #pylint: disable=no-value-for-parameter + ) return assembly @@ -79,6 +81,7 @@ def generate(self) -> cq.Assembly: # Handle different execution environments, including ExSource-Tools if "show_object" in globals() or __name__ == "__cqgi__": # CQGI should execute this whenever called + assembly_definition_file = "assembly-def.yaml" assembly = AssemblyRederer(assembly_definition_file).generate() show_object(assembly) diff --git a/mechanical/components/cadquery/nimble_tray.py b/mechanical/components/cadquery/nimble_tray.py index 4505ff7..7c64c2e 100644 --- a/mechanical/components/cadquery/nimble_tray.py +++ b/mechanical/components/cadquery/nimble_tray.py @@ -6,7 +6,7 @@ import cadquery as cq import math -height_in_hole_unites = 2 +height_in_u = 2 tray_width = 115 tray_depth = 115 @@ -17,7 +17,7 @@ def __init__(self): self.tray_width = 115 self.tray_depth = 115 self.leg_width = 20 - self.height_in_hole_unites = 2 + self.height_in_u = 2 self.wall_thickness = 4 self.hole_distance = 14 # Distance between the holes self.hole_diameter = 3.6 @@ -32,7 +32,7 @@ def inner_depth(self): @property def tray_height(self): - return self.height_in_hole_unites * self.hole_distance + return self.height_in_u * self.hole_distance @property def inner_height(self): @@ -46,7 +46,7 @@ def build_for_inner_dims(width, height, depth): # Convert hole into number of holes. nh = math.ceil(height / p.hole_distance) # The number of holes must be at least two - p.height_in_hole_unites = max(nh, 2) + p.height_in_u = max(nh, 2) return p @@ -81,14 +81,14 @@ def _create_part(params): return result -def create(number_of_units, tray_width, tray_depth): +def create(height_in_u, tray_width, tray_depth): params = Params() - params.height_in_hole_unites = number_of_units + params.height_in_u = height_in_u params.tray_width = tray_width params.tray_depth = tray_depth return _create_part(params) # CQGI should execute this whenever called -obj = create(height_in_hole_unites, tray_width, tray_depth) +obj = create(height_in_u, tray_width, tray_depth) show_object(obj) diff --git a/mechanical/components/cadquery/tray_6in.py b/mechanical/components/cadquery/tray_6in.py index c76b704..73cbae2 100644 --- a/mechanical/components/cadquery/tray_6in.py +++ b/mechanical/components/cadquery/tray_6in.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later import cadscript as cad +import nimble_builder from nimble_builder import shelf_builder # parameters to be set in exsource-def.yaml file @@ -24,7 +25,8 @@ def get_builder(hole_count) -> shelf_builder.ShelfBuilder: - shelf = shelf_builder.ShelfBuilder(hole_count, width="6inch") + rack_params = nimble_builder.RackParameters(nominal_rack_width="6inch") + shelf = shelf_builder.ShelfBuilder(hole_count, rack_params=rack_params) return shelf diff --git a/nimble_builder/__init__.py b/nimble_builder/__init__.py index 90a3fd4..74e096e 100644 --- a/nimble_builder/__init__.py +++ b/nimble_builder/__init__.py @@ -1,5 +1,6 @@ from dataclasses import dataclass +from typing import Literal @dataclass class RackParameters: @@ -8,7 +9,7 @@ class RackParameters: """ beam_width: float = 20.0 - single_width: float = 155 + nominal_rack_width: Literal["6inch", "10inch", "10inch_reduced"] = "6inch" tray_depth: float = 115 mounting_hole_spacing: float = 14 base_plate_thickness: float = 3 @@ -21,16 +22,34 @@ class RackParameters: end_plate_rail_height: float = 3 end_plate_star_width: float = 9 + @property + def rack_width(self): + """ + Return the rack width in mm as determined by the nominal_rack_width. + Options are: + "6inch" - 155mm - full width (front panel) of the 6 inch nimble rack + "10inch" - 254mm - full width (front panel) of the 10 inch rack + "10inch_reduced - 250mm - as above bu reduced to fit into a 250mm wide printer + """ + if self.nominal_rack_width == "6inch": + return 155 + if self.nominal_rack_width == "10inch": + return 254 + if self.nominal_rack_width == "10inch_reduced": + return 250 + raise ValueError(f"Unknown rack witdth {self.nominal_rack_width}") + @property def tray_width(self): """ Return derived parameter for the width of a standard tray """ - return self.single_width - 2 * self.beam_width + return self.rack_width - 2 * self.beam_width - def beam_height(self, total_height_in_units): + def beam_height(self, total_height_in_u): """ Return derived parameter for the height of a beam for a rack with a given total height specified in units. """ - return self.base_clearance + total_height_in_units * self.mounting_hole_spacing + return self.base_clearance + total_height_in_u * self.mounting_hole_spacing + diff --git a/nimble_builder/shelf_builder.py b/nimble_builder/shelf_builder.py index 8b32f6b..be418f8 100644 --- a/nimble_builder/shelf_builder.py +++ b/nimble_builder/shelf_builder.py @@ -10,9 +10,6 @@ from nimble_builder.helpers import cut_slots, cut_w_pattern import nimble_builder -width_6in = 155 # full width (front panel) of the 6 inch nimble rack -width_10in = 254 # full width (front panel) of the 10 inch rack -width_10in_reduced = 250 # front panel width for 10 inch rack, reduced to fit into a 250mm wide printer no_slots = False # speedup for debugging @@ -35,9 +32,7 @@ class ShelfBuilder: side_wall_offset = 16 # distance between side walls to the bouding box of the rack _shelf: cad.Body - _has_front: bool - _vertical_hole_count: int - _width_type: Literal["6inch", "10inch", "10inch_reduced"] + _height_in_u: int _width: float _height: float _inner_width: float # inside walls between the beams @@ -48,8 +43,7 @@ class ShelfBuilder: def __init__(self, - vertical_hole_count: int, - width: Literal["6inch", "10inch", "10inch_reduced"], + height_in_u: int, rack_params=None ) -> None: """ @@ -60,13 +54,11 @@ def __init__(self, rack_params = nimble_builder.RackParameters() self._rack_params = rack_params - self._vertical_hole_count = vertical_hole_count - self._width_type = width - self._width = width_6in if width == "6inch" else width_10in if width == "10inch" else width_10in_reduced - self._height = self._vertical_hole_count * self._rack_params.mounting_hole_spacing - self._hole_dist_x = self._width - self._rack_params.beam_width + self._height_in_u = height_in_u + self._height = self._height_in_u * self._rack_params.mounting_hole_spacing + self._hole_dist_x = self._rack_params.rack_width - self._rack_params.beam_width self._hole_offset_y = self._rack_params.mounting_hole_spacing / 2 - self._inner_width = self._width - 2 * self._rack_params.beam_width - 2 * self.side_wall_thickness + self._inner_width = self._rack_params.rack_width - 2 * self._rack_params.beam_width - 2 * self.side_wall_thickness self._front_depth = self._rack_params.beam_width + 2.75 # the size of the front panel part in y direction self._padding_front = self._front_depth # the space between the front panel and where slots etc can start at the try bottom @@ -82,10 +74,10 @@ def make_front(self, sketch = cad.make_sketch() # front panel - sketch.add_rect(self._width, (-self.panel_thickness, 0), center="X") + sketch.add_rect(self._rack_params.rack_width, (-self.panel_thickness, 0), center="X") # wall next to the beam if beam_wall_type != "none": - sketch.add_rect((self._inner_width / 2, self._width / 2 - self._rack_params.beam_width), + sketch.add_rect((self._inner_width / 2, self._rack_params.rack_width / 2 - self._rack_params.beam_width), self._front_depth, center=False) sketch.mirror("X") @@ -156,12 +148,13 @@ def make_tray(self, # basic size plate_width = 0 if not isinstance(width, (float, int)) else width if width == "standard": - if self._width_type == "6inch": - plate_width = 115 # could be a bit larger, use this for backwards compatibility + if self._rack_params.nominal_rack_width == "6inch": + # could be a bit larger, use this for backwards compatibility + plate_width = 115 else: plate_width = self._inner_width + 2 * self.side_wall_thickness elif width == "broad": - plate_width = self._width - 2 * self.side_wall_offset + plate_width = self._rack_params.rack_width - 2 * self.side_wall_offset if not isinstance(plate_width, (float, int)) and plate_width <= 0: raise ValueError("Invalid width") self.plate_width = plate_width @@ -188,7 +181,7 @@ def make_tray(self, # for thin trays connect the walls if sides != "open" and self._front_depth > 0: - if plate_width > self._width - 2 * self._rack_params.beam_width: + if plate_width > self._rack_params.rack_width - 2 * self._rack_params.beam_width: # broad tray left = self._inner_width / 2 right = plate_width / 2 @@ -240,7 +233,7 @@ def make_tray(self, if have_walls: walls = cad.make_extrude("XY", wall_sketch, wall_height) if sides == "w-pattern": - cut_w_pattern(walls, ">X", dim_sides.tuple_y, (0, wall_height), padding_side, padding_top_w, cut_depth=self._width + 1) + cut_w_pattern(walls, ">X", dim_sides.tuple_y, (0, wall_height), padding_side, padding_top_w, cut_depth=self._rack_params.rack_width + 1) if sides == "slots" and not no_slots: cut_slots(walls, ">X", dim_sides.tuple_y, (0, wall_height), padding_side, padding_top) if back == "w-pattern": @@ -393,7 +386,7 @@ def add_cage(self, self._shelf.add(channel_guide) sketch_channels = cad.make_sketch() - sketch_channels.add_rect(self._width, channel_width, + sketch_channels.add_rect(self._rack_params.rack_width, channel_width, pos=[(0, ziptie_pos_y1), (0, ziptie_pos_y2)]) self._shelf.cut(cad.make_extrude_z(sketch_channels, 999)) @@ -419,9 +412,16 @@ def get_body(self) -> cad.Body: return self._shelf -# for development and debugging -if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): - shelf = ShelfBuilder(vertical_hole_count=3, width="10inch") +def example(): + """ + An example of this module in use for dev and debugging + """ + rack_params = nimble_builder.RackParameters(nominal_rack_width="10inch") + shelf = ShelfBuilder(height_in_u=3, rack_params=rack_params) shelf.make_front(front_type="slots", bottom_type="closed") shelf.make_tray(width="standard", depth=80, sides="ramp", back="open") cad.show(shelf.get_body()) + +# for development and debugging +if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): + example() diff --git a/nimble_orchestration/configuration.py b/nimble_orchestration/configuration.py index e642ae8..1c57840 100644 --- a/nimble_orchestration/configuration.py +++ b/nimble_orchestration/configuration.py @@ -79,11 +79,11 @@ def assembled_components(self): return deepcopy(self._assembled_components) @property - def total_height_in_units(self): + def total_height_in_u(self): """ Return the total height of the needed rack in units """ - return sum(device.height_in_units for device in self._devices) + return sum(device.height_in_u for device in self._devices) def _generate_assembled_components_list(self): @@ -96,8 +96,8 @@ def _generate_assembled_components_list(self): def _legs(self): source = os.path.join(REL_MECH_DIR, "components/cadquery/rack_leg.py") source = posixpath.normpath(source) - beam_height = self._rack_params.beam_height(self.total_height_in_units) - hole_pos = (self._rack_params.single_width - self._rack_params.beam_width) / 2.0 + beam_height = self._rack_params.beam_height(self.total_height_in_u) + hole_pos = (self._rack_params.rack_width - self._rack_params.beam_width) / 2.0 component = GeneratedMechanicalComponent( key="rack_leg", @@ -149,8 +149,8 @@ def _baseplate(self): ], source_files=[source], parameters={ - "width": self._rack_params.single_width, - "depth": self._rack_params.single_width, + "width": self._rack_params.rack_width, + "depth": self._rack_params.rack_width, }, application="cadquery" ) @@ -166,7 +166,7 @@ def _baseplate(self): def _topplate(self): source = os.path.join(REL_MECH_DIR, "components/cadquery/top_plate.py") source = posixpath.normpath(source) - beam_height = self._rack_params.beam_height(self.total_height_in_units) + beam_height = self._rack_params.beam_height(self.total_height_in_u) top_pos = beam_height + self._rack_params.base_plate_thickness component = GeneratedMechanicalComponent( key="topplate", @@ -178,8 +178,8 @@ def _topplate(self): ], source_files=[source], parameters={ - "width": self._rack_params.single_width, - "depth": self._rack_params.single_width, + "width": self._rack_params.rack_width, + "depth": self._rack_params.rack_width, }, application="cadquery" ) @@ -205,7 +205,7 @@ def _trays(self): height_in_u = 0 for i, device in enumerate(self._devices): x_pos = -self._rack_params.tray_width / 2.0 - y_pos = -self._rack_params.single_width / 2.0 - 4 + y_pos = -self._rack_params.rack_width / 2.0 - 4 z_pos = z_offset + height_in_u * self._rack_params.mounting_hole_spacing tray_id = device.tray_id @@ -219,7 +219,7 @@ def _trays(self): ], source_files=[source], parameters={ - "height_in_hole_unites": device.height_in_units, + "height_in_u": device.height_in_u, "tray_width": self._rack_params.tray_width, "tray_depth": self._rack_params.tray_depth, }, @@ -235,7 +235,7 @@ def _trays(self): ) ) - height_in_u += device.height_in_units + height_in_u += device.height_in_u return trays @property diff --git a/nimble_orchestration/device.py b/nimble_orchestration/device.py index 5b290d3..080367e 100644 --- a/nimble_orchestration/device.py +++ b/nimble_orchestration/device.py @@ -43,7 +43,7 @@ def __init__(self, json_node): self.name = json_node['Hardware'] # self.category = json_node['Category'] self.category = json_node['Type'] - self.height_in_units = int(json_node['HeightUnits']) + self.height_in_u = int(json_node['HeightUnits']) # self.width = json_node['Width'] self.width = json_node['LengthMm'] self.depth = json_node['Depth'] @@ -56,4 +56,4 @@ def tray_id(self): """ Return and identification for the shelf. """ - return f"tray_h{self.height_in_units}_t{self.shelf_type.lower().replace(' ', '_')}" + return f"tray_h{self.height_in_u}_t{self.shelf_type.lower().replace(' ', '_')}" diff --git a/server/nimble_server.py b/server/nimble_server.py index e1db575..7a73023 100644 --- a/server/nimble_server.py +++ b/server/nimble_server.py @@ -214,7 +214,7 @@ def read_item(number_of_units: float = 2, tray_width: float = 115, tray_depth: f import cadquery as cq # Run the script with customized parameters - tray = cqgi_model_script("nimble_tray.py", {"height_in_hole_unites": number_of_units, "tray_width": tray_width, "tray_depth": tray_depth}) + tray = cqgi_model_script("nimble_tray.py", {"height_in_u": number_of_units, "tray_width": tray_width, "tray_depth": tray_depth}) # In case there was an error if (type(tray).__name__ == "HTMLResponse"): From 40b114af19476e2b4ddd87c870472b9ea7d5537e Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Wed, 15 May 2024 22:44:46 +0100 Subject: [PATCH 07/16] Generic zip-tie shelf, further name standardisation --- generate_static.py | 14 +-- mechanical/components/cadquery/base_plate.py | 2 +- mechanical/components/cadquery/nimble_tray.py | 9 +- mechanical/components/cadquery/rack_leg.py | 7 +- mechanical/components/cadquery/top_plate.py | 6 +- mechanical/components/cadquery/tray_6in.py | 117 +++++++++++------- nimble_builder/__init__.py | 41 +++++- nimble_builder/nimble_end_plate.py | 2 +- nimble_builder/shelf_builder.py | 33 +++-- 9 files changed, 149 insertions(+), 82 deletions(-) diff --git a/generate_static.py b/generate_static.py index 8cc8447..f95f7ef 100755 --- a/generate_static.py +++ b/generate_static.py @@ -66,7 +66,7 @@ def get_component_list(): ) ) - for (shelf_type, hole_count) in [ + for (shelf_type, height_in_u) in [ ("stuff", 3), ("stuff-thin", 3), ("nuc", 3), @@ -80,7 +80,7 @@ def get_component_list(): ("dual-ssd", 2), ("raspi", 2)]: - components.append(generate_shelf(shelf_type, hole_count)) + components.append(generate_shelf(shelf_type, height_in_u)) return components @@ -106,21 +106,21 @@ def generate_leg(length, mounting_holes_dia, name, out_file): ) -def generate_shelf(shelf_type, hole_count): +def generate_shelf(shelf_type, height_in_u): """ Helper function to generate a shelf/tray. Returns a GeneratedMechanicalComponent object which contains the data for the shelf. """ - out_file = f"./printed_components/shelf_6in_{shelf_type}u_{hole_count}.stl" + out_file = f"./printed_components/shelf_6in_{shelf_type}u_{height_in_u}.stl" source = os.path.join(REL_MECH_DIR, "components/cadquery/tray_6in.py") device_name = shelf_type.replace('-', ' ') return GeneratedMechanicalComponent( - key=f"{shelf_type}_{hole_count}u", + key=f"{shelf_type}_{height_in_u}u", name=f"Tray for {device_name}", - description=f"Tray for {device_name}, height = {hole_count}u", + description=f"Tray for {device_name}, height = {height_in_u}u", output_files=[out_file], source_files=[source], - parameters={'shelf_type': shelf_type, 'hole_count': hole_count}, + parameters={'shelf_type': shelf_type, 'height_in_u': height_in_u}, application="cadquery" ) diff --git a/mechanical/components/cadquery/base_plate.py b/mechanical/components/cadquery/base_plate.py index e9d41fe..4e43c74 100644 --- a/mechanical/components/cadquery/base_plate.py +++ b/mechanical/components/cadquery/base_plate.py @@ -20,6 +20,6 @@ def create(width, depth, height): return plate -if __name__ == "__cqgi__": +if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): result = create(width, depth, height) cad.show(result) # when run in cq-cli, will return result diff --git a/mechanical/components/cadquery/nimble_tray.py b/mechanical/components/cadquery/nimble_tray.py index 7c64c2e..7007b7a 100644 --- a/mechanical/components/cadquery/nimble_tray.py +++ b/mechanical/components/cadquery/nimble_tray.py @@ -13,6 +13,7 @@ class Params: def __init__(self): + # Default length unit is mm. self.tray_width = 115 self.tray_depth = 115 @@ -88,7 +89,7 @@ def create(height_in_u, tray_width, tray_depth): params.tray_depth = tray_depth return _create_part(params) - -# CQGI should execute this whenever called -obj = create(height_in_u, tray_width, tray_depth) -show_object(obj) +if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): + # CQGI should execute this whenever called + obj = create(height_in_u, tray_width, tray_depth) + show_object(obj) diff --git a/mechanical/components/cadquery/rack_leg.py b/mechanical/components/cadquery/rack_leg.py index 0f00c10..49f20e9 100644 --- a/mechanical/components/cadquery/rack_leg.py +++ b/mechanical/components/cadquery/rack_leg.py @@ -49,6 +49,7 @@ def make_rack_leg( return leg -print(f"Creating rack leg with length: {length}") -result = make_rack_leg(length, long_axis_hole_dia, mounting_holes_dia) -cad.show(result) # when run in cq-cli, will return result +if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): + print(f"Creating rack leg with length: {length}") + result = make_rack_leg(length, long_axis_hole_dia, mounting_holes_dia) + cad.show(result) # when run in cq-cli, will return result diff --git a/mechanical/components/cadquery/top_plate.py b/mechanical/components/cadquery/top_plate.py index c422b4b..2541151 100644 --- a/mechanical/components/cadquery/top_plate.py +++ b/mechanical/components/cadquery/top_plate.py @@ -16,6 +16,6 @@ def create(width, depth, height): # just create end plate, not further changes needed return create_end_plate(width, depth, height) - -result = create(width, depth, height) -cad.show(result) # when run in cq-cli, will return result +if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): + result = create(width, depth, height) + cad.show(result) # when run in cq-cli, will return result diff --git a/mechanical/components/cadquery/tray_6in.py b/mechanical/components/cadquery/tray_6in.py index 73cbae2..6aeee5f 100644 --- a/mechanical/components/cadquery/tray_6in.py +++ b/mechanical/components/cadquery/tray_6in.py @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: AGPL-3.0-or-later +import typing import cadscript as cad import nimble_builder from nimble_builder import shelf_builder @@ -9,8 +10,9 @@ # parameters to be set in exsource-def.yaml file # shelf types -# "stuff" - for general stuff -# "stuff-thin" - for general stuff, thin version +# "generic" - a generic cable tie shelf +# "stuff" - for general stuff such as wires. No access to the front +# "stuff-thin" - a thin version of above # "nuc" - for Intel NUC # "usw-flex" - for Ubiquiti USW-Flex # "usw-flex-mini" - for Ubiquiti Flex Mini @@ -20,29 +22,32 @@ # "hdd35" - for 3.5" HDD # "dual-ssd" - for 2x 2.5" SSD # "raspi" - for Raspberry Pi -shelf_type = "usw-flex-mini" -hole_count = 2 +shelf_type = "generic" +height_in_u = 2 -def get_builder(hole_count) -> shelf_builder.ShelfBuilder: - rack_params = nimble_builder.RackParameters(nominal_rack_width="6inch") - shelf = shelf_builder.ShelfBuilder(hole_count, rack_params=rack_params) +def get_builder(height_in_u, rack_params=None) -> shelf_builder.ShelfBuilder: + if not rack_params: + rack_params = nimble_builder.RackParameters() + shelf = shelf_builder.ShelfBuilder(height_in_u, rack_params=rack_params) return shelf -def create_6in_shelf(shelf_type, hole_count) -> cad.Body: +def create_6in_shelf(shelf_type, height_in_u) -> cad.Body: + if shelf_type == "generic": + return create_ziptie_shelf(height_in_u) if shelf_type == "stuff": - b = get_builder(hole_count) + b = get_builder(height_in_u) b.make_front(front_type="w-pattern", bottom_type="closed") b.make_tray(width="broad", depth="standard", sides="w-pattern", back="open") return b.get_body() if shelf_type == "stuff-thin": - b = get_builder(hole_count) + b = get_builder(height_in_u) b.make_front(front_type="w-pattern", bottom_type="closed") b.make_tray(width="standard", depth="standard", sides="w-pattern", back="open") return b.get_body() if shelf_type == "nuc": - b = get_builder(hole_count) + b = get_builder(height_in_u) b.make_front(front_type="full", bottom_type="closed") b.cut_opening(" cad.Body: b.add_mounting_hole_to_bottom(x_pos=0, y_pos=120, base_thickness=4, hole_type="M3cs") return b.get_body() if shelf_type == "usw-flex": - b = get_builder(hole_count) + b = get_builder(height_in_u) b.make_front(front_type="full", bottom_type="closed") b.cut_opening(" cad.Body: b.get_body().add(base).add(base2) return b.get_body() if shelf_type == "usw-flex-mini": - b = get_builder(hole_count) + b = get_builder(height_in_u) b.side_wall_thickness = 3.8 # extra thick to have thinner tray b.make_front(front_type="full", bottom_type="closed") b.cut_opening(" cad.Body: b.add_mounting_hole_to_back(x_pos=+75 / 2, z_pos=b._height / 2, hole_type="M3-tightfit") return b.get_body() if shelf_type == "anker-powerport5": - return create_ziptie_shelf(hole_count, width=56, length=90.8, height=25, cutout_width=53) + return create_ziptie_shelf(height_in_u, + internal_width=56, + internal_depth=90.8, + internal_height=25, + front_cutout_width=53) + if shelf_type == "anker-atom3slim": - return create_ziptie_shelf(hole_count, width=86.5, length=90, height=20, cutout_width=71) + return create_ziptie_shelf(height_in_u, + internal_width=86.5, + internal_depth=90, + internal_height=20, + front_cutout_width=71) if shelf_type == "anker-a2123": # 99 x 70 x 26 mm # use height = 25, max for cage on 2 hole shelf - return create_ziptie_shelf(hole_count, width=70, length=99, height=25, cutout_width=65) + return create_ziptie_shelf(height_in_u, + internal_width=70, + internal_depth=99, + internal_height=25, + front_cutout_width=65) if shelf_type == "hdd35": width = 102.8 # 101.6 + 1.2 clearance screw_pos1 = 77.3 # distance from front screw_pos2 = screw_pos1 + 41.61 screw_y = 7 # distance from bottom plane - b = get_builder(hole_count) + b = get_builder(height_in_u) b.make_front(front_type="w-pattern", bottom_type="closed") b.make_tray(width="standard", depth="standard", sides="slots", back="open") mount_sketch = cad.make_sketch() @@ -104,7 +122,7 @@ def create_6in_shelf(shelf_type, hole_count) -> cad.Body: screw_pos2 = screw_pos1 + 76 screw_y1 = 6.6 # distance from bottom plane screw_y2 = screw_y1 + 11.1 - b = get_builder(hole_count) + b = get_builder(height_in_u) b.make_front(front_type="w-pattern", bottom_type="none", beam_wall_type="none") b.make_tray(width=width + 2 * b.side_wall_thickness, depth=111, sides="slots", back="open") for (x, y) in [(screw_pos1, screw_y2), @@ -119,7 +137,7 @@ def create_6in_shelf(shelf_type, hole_count) -> cad.Body: screw_dist_y = 58 dist_to_front = 23.5 offset_x = -13 - b = get_builder(hole_count) + b = get_builder(height_in_u) b.make_front(front_type="full", bottom_type="closed") b.cut_opening(" cad.Body: raise ValueError(f"Unknown shelf type: {shelf_type}") -def create_ziptie_shelf(hole_count: int, width: float, length: float, height: float, cutout_width: float): - b = get_builder(hole_count) +def create_ziptie_shelf( + height_in_u: int, + internal_width: typing.Optional[float]=None, + internal_depth: typing.Optional[float]=None, + internal_height: typing.Optional[float]=None, + front_cutout_width: typing.Optional[float]=None, + rear_cutout_width: typing.Optional[float]=None, + rack_params: nimble_builder.RackParameters = None +): + + if not rack_params: + rack_params = nimble_builder.RackParameters() + if not internal_width: + internal_width = rack_params.tray_width - 12 + if not internal_depth: + internal_depth = 115 + if not internal_height: + internal_height = rack_params.tray_height(height_in_u) - 3 + if not rear_cutout_width: + front_cutout_width = internal_width - 10 + if not rear_cutout_width: + rear_cutout_width = internal_width - 20 + + + b = get_builder(height_in_u) b.make_front(front_type="full", bottom_type="closed", beam_wall_type="ramp") - b.cut_opening(" 0: - sketch.cut_rect(back_cutout_width, inner_depth + wall_thickness + 1, center="X") + sketch.add_rect(internal_width + wall_thickness * 2, internal_depth + wall_thickness, center="X") + sketch.cut_rect(internal_width, internal_depth, center="X") + if rear_cutout_width > 0: + sketch.cut_rect(rear_cutout_width, internal_depth + wall_thickness + 1, center="X") cage = cad.make_extrude("XY", sketch, cage_height) self._shelf.add(cage) @@ -377,9 +376,9 @@ def add_cage(self, return sketch_channel_guide = cad.make_sketch() - sketch_channel_guide.add_rect(inner_width + guide_radius * 2, guide_width, + sketch_channel_guide.add_rect(internal_width + guide_radius * 2, guide_width, pos=[(0, ziptie_pos_y1), (0, ziptie_pos_y2)]) - sketch_channel_guide.cut_rect(inner_width, 999) + sketch_channel_guide.cut_rect(internal_width, 999) channel_guide = cad.make_extrude_z(sketch_channel_guide, cage_height) channel_guide.fillet(">Z and >X and |Y", guide_radius / 2) channel_guide.fillet(">Z and Date: Thu, 16 May 2024 01:21:57 +0100 Subject: [PATCH 08/16] refactoring shelf_builder due to many code warnings --- mechanical/components/cadquery/tray_6in.py | 85 +++--- nimble_builder/__init__.py | 14 +- nimble_builder/shelf_builder.py | 326 ++++++++++++--------- 3 files changed, 227 insertions(+), 198 deletions(-) diff --git a/mechanical/components/cadquery/tray_6in.py b/mechanical/components/cadquery/tray_6in.py index 6aeee5f..21a1140 100644 --- a/mechanical/components/cadquery/tray_6in.py +++ b/mechanical/components/cadquery/tray_6in.py @@ -26,57 +26,48 @@ shelf_type = "generic" height_in_u = 2 -def get_builder(height_in_u, rack_params=None) -> shelf_builder.ShelfBuilder: - if not rack_params: - rack_params = nimble_builder.RackParameters() - shelf = shelf_builder.ShelfBuilder(height_in_u, rack_params=rack_params) - return shelf def create_6in_shelf(shelf_type, height_in_u) -> cad.Body: if shelf_type == "generic": return create_ziptie_shelf(height_in_u) if shelf_type == "stuff": - b = get_builder(height_in_u) - b.make_front(front_type="w-pattern", bottom_type="closed") - b.make_tray(width="broad", depth="standard", sides="w-pattern", back="open") + b = shelf_builder.ShelfBuilder(height_in_u, width="broad", depth="standard", front_type="w-pattern") + b.make_tray(sides="w-pattern", back="open") return b.get_body() if shelf_type == "stuff-thin": - b = get_builder(height_in_u) - b.make_front(front_type="w-pattern", bottom_type="closed") - b.make_tray(width="standard", depth="standard", sides="w-pattern", back="open") + b = shelf_builder.ShelfBuilder(height_in_u, width="standard", depth="standard", front_type="w-pattern") + + b.make_tray(sides="w-pattern", back="open") return b.get_body() if shelf_type == "nuc": - b = get_builder(height_in_u) - b.make_front(front_type="full", bottom_type="closed") - b.cut_opening("Y", 30, offset_y=b.bottom_thickness, depth=10) - b.add_mounting_hole_to_side(y_pos=59, z_pos=b._height / 2, hole_type="M3-tightfit", side="both") - b.add_mounting_hole_to_back(x_pos=-75 / 2, z_pos=b._height / 2, hole_type="M3-tightfit") - b.add_mounting_hole_to_back(x_pos=+75 / 2, z_pos=b._height / 2, hole_type="M3-tightfit") + b.make_tray(sides="slots", back="slots") + b.cut_opening(">Y", 30, offset_y=b.rack_params.tray_bottom_thickness, depth=10) + b.add_mounting_hole_to_side(y_pos=59, z_pos=b.height / 2, hole_type="M3-tightfit", side="both") + b.add_mounting_hole_to_back(x_pos=-75 / 2, z_pos=b.height / 2, hole_type="M3-tightfit") + b.add_mounting_hole_to_back(x_pos=+75 / 2, z_pos=b.height / 2, hole_type="M3-tightfit") return b.get_body() if shelf_type == "anker-powerport5": return create_ziptie_shelf(height_in_u, @@ -104,32 +95,31 @@ def create_6in_shelf(shelf_type, height_in_u) -> cad.Body: screw_pos1 = 77.3 # distance from front screw_pos2 = screw_pos1 + 41.61 screw_y = 7 # distance from bottom plane - b = get_builder(height_in_u) - b.make_front(front_type="w-pattern", bottom_type="closed") - b.make_tray(width="standard", depth="standard", sides="slots", back="open") + b = shelf_builder.ShelfBuilder(height_in_u, width="standard", depth="standard", front_type="w-pattern") + b.make_tray(sides="slots", back="open") mount_sketch = cad.make_sketch() - mount_sketch.add_rect((width / 2, b._inner_width / 2 + b.side_wall_thickness), 21, + mount_sketch.add_rect((width / 2, b.inner_width / 2 + b.rack_params.tray_side_wall_thickness), 21, pos=[(0, screw_pos1), (0, screw_pos2)]) - mount_sketch.chamfer(" cad.Body: screw_dist_y = 58 dist_to_front = 23.5 offset_x = -13 - b = get_builder(height_in_u) - b.make_front(front_type="full", bottom_type="closed") + b = shelf_builder.ShelfBuilder(height_in_u, width="standard", depth=111, front_type="full") b.cut_opening(" None: """ - Initialize the shelf builder + Initialize the shelf builder this makes the front of the shelf """ + self._height_in_u = height_in_u + self._width = width + self._depth = depth + self._front_type= front_type + self._front_type= front_type + self._beam_wall_type = beam_wall_type + self._base_between_beam_walls = base_between_beam_walls if not rack_params: rack_params = nimble_builder.RackParameters() self._rack_params = rack_params + self._make_front() - self._height_in_u = height_in_u - self._height = self._height_in_u * self._rack_params.mounting_hole_spacing - self._hole_dist_x = self._rack_params.rack_width - self._rack_params.beam_width - self._hole_offset_y = self._rack_params.mounting_hole_spacing / 2 - self._inner_width = self._rack_params.rack_width - 2 * self._rack_params.beam_width - 2 * self.side_wall_thickness - self._front_depth = self._rack_params.beam_width + 2.75 # the size of the front panel part in y direction - self._padding_front = self._front_depth # the space between the front panel and where slots etc can start at the try bottom - - def make_front(self, - front_type: Literal["full", "open", "w-pattern", "slots"], - bottom_type: Literal["none", "front-open", "closed"], - beam_wall_type: Literal["none", "standard", "ramp"] = "standard" - ) -> None: + @property + def plate_width(self): + # basic size + + if self._width == "standard": + if self._rack_params.nominal_rack_width == "6inch": + # could be a bit larger, use this for backwards compatibility + return 115 + return self.inner_width + 2 * self.rack_params.tray_side_wall_thickness + + if self._width == "broad": + return self._rack_params.rack_width - 2 * self.rack_params.broad_tray_clearance + + if isinstance(self._width, (float, int)) and self._width>0: + return self._width + + raise ValueError(f"The value {self._width} is not a valid shelf width") + + @property + def plate_depth(self): + if self._depth == "standard": + return 136 + + if isinstance(self._depth, (float, int)) and self._depth>0: + return self._depth + + + @property + def rack_params(self): + return self._rack_params + + @property + def height(self): + return self._height_in_u * self._rack_params.mounting_hole_spacing + + @property + def hole_dist_x(self): + return self._rack_params.rack_width - self._rack_params.beam_width + + @property + def hole_offset_y(self): + return self._rack_params.mounting_hole_spacing / 2 + + @property + def inner_width(self): + return (self._rack_params.rack_width + - 2 * self._rack_params.beam_width + - 2 * self._rack_params.tray_side_wall_thickness) + + @property + def front_depth(self): + """ + the length of the beam walls that sit behind front pannel + """ + return self._rack_params.beam_width + 2.75 + + @property + def front_of_tray(self): + """ + Front poistion of tray. """ - Make the front panel of the shelf + if self._base_between_beam_walls == "none": + return 0 + return self.front_depth + + @property + def padding_front(self): + """ + the space between the front panel and where slots etc can start at the try bottom + """ + if self._base_between_beam_walls != "front-open": + return self.front_depth/4 + return self.front_depth + + def _make_front(self) -> None: + """ + Make the front panel of the shelf. This happens on initalisation """ # sketch as viewed from top sketch = cad.make_sketch() # front panel - sketch.add_rect(self._rack_params.rack_width, (-self.panel_thickness, 0), center="X") + sketch.add_rect(self._rack_params.rack_width, (-self._rack_params.tray_front_panel_thickness, 0), center="X") # wall next to the beam - if beam_wall_type != "none": - sketch.add_rect((self._inner_width / 2, self._rack_params.rack_width / 2 - self._rack_params.beam_width), - self._front_depth, center=False) + if self._beam_wall_type != "none": + sketch.add_rect((self.inner_width / 2, self._rack_params.rack_width / 2 - self._rack_params.beam_width), + self.front_depth, center=False) sketch.mirror("X") # front panel with holes - front = cad.make_extrude("XY", sketch, self._height) - pattern_holes = cad.pattern_rect(self._hole_dist_x, (self._hole_offset_y, self._height - self._hole_offset_y), center="X") - front.cut_hole("Y and >Z and |X", min(self._height, self._rack_params.beam_width)) + if self._beam_wall_type == "ramp": + front.chamfer(">Y and >Z and |X", min(self.height, self._rack_params.beam_width)) + # We now have a solid front panel with short walls behind self._shelf = front - # front types + self._pattern_front() + self._add_base_between_beam_walls() + + def _pattern_front(self) -> None: + """ + Depending on the "front_type" made cuts to pattern the front + """ - if front_type == "open": - self.cut_opening(" None: + if self._base_between_beam_walls == "closed": # a full-material base between the 2 rack legs bottom_sketch = cad.make_sketch() - bottom_sketch.add_rect(self._inner_width, (-0.1, self._front_depth), center="X") - bottom = cad.make_extrude("XY", bottom_sketch, self.bottom_thickness) + bottom_sketch.add_rect(self.inner_width, (-0.1, self.front_depth), center="X") + bottom = cad.make_extrude("XY", bottom_sketch, self._rack_params.tray_bottom_thickness) self._shelf.add(bottom) - self._padding_front *= 0.25 # can be smaller in this case - if bottom_type == "front-open": + if self._base_between_beam_walls == "front-open": # a base with a cutout on the front side for e.g. cable handling bottom_sketch = cad.make_sketch() - bottom_sketch.add_rect(self._inner_width, (self._front_depth - 2, self._front_depth), center="X") - bottom_sketch.add_polygon([(self._inner_width / 2, 0), - (self._inner_width / 2, self._front_depth), - (self._inner_width / 2 - 5, self._front_depth), + bottom_sketch.add_rect(self.inner_width, (self.front_depth - 2, self.front_depth), center="X") + bottom_sketch.add_polygon([(self.inner_width / 2, 0), + (self.inner_width / 2, self.front_depth), + (self.inner_width / 2 - 5, self.front_depth), ]) bottom_sketch.mirror("X") - bottom = cad.make_extrude("XY", bottom_sketch, self.bottom_thickness) + bottom = cad.make_extrude("XY", bottom_sketch, self._rack_params.tray_bottom_thickness) self._shelf.add(bottom) def make_tray(self, - width: Union[Literal["standard", "broad"], float], - depth: Union[Literal["standard"], float], sides: Literal["full", "open", "w-pattern", "slots", "ramp"], back: Literal["full", "open", "w-pattern", "slots"], bottom_type: Literal["full", "slots", "slots-large"] = "slots-large", - wall_height: Optional[float] = None, - padding_front: Optional[float] = None, + wall_height: Optional[float] = None ) -> None: """ Make the tray of the shelf """ - # basic size - plate_width = 0 if not isinstance(width, (float, int)) else width - if width == "standard": - if self._rack_params.nominal_rack_width == "6inch": - # could be a bit larger, use this for backwards compatibility - plate_width = 115 - else: - plate_width = self._inner_width + 2 * self.side_wall_thickness - elif width == "broad": - plate_width = self._rack_params.rack_width - 2 * self.side_wall_offset - if not isinstance(plate_width, (float, int)) and plate_width <= 0: - raise ValueError("Invalid width") - self.plate_width = plate_width - - plate_depth = 0 if not isinstance(depth, (float, int)) else depth - if depth == "standard": - plate_depth = 136 - if not isinstance(plate_depth, float) and plate_depth <= 0: - raise ValueError("Invalid depth") - self.plate_depth = plate_depth - - # base plate + + if not wall_height: + wall_height = self.height + + # create the base plate plate_sketch = cad.make_sketch() - plate_sketch.add_rect(plate_width, (self._front_depth, plate_depth), center="X") - plate = cad.make_extrude("XY", plate_sketch, self.bottom_thickness) + plate_sketch.add_rect(self.plate_width, (self.front_of_tray, self.plate_depth), center="X") + plate = cad.make_extrude("XY", plate_sketch, self._rack_params.tray_bottom_thickness) if sides in ["open", "ramp"] and back == "open": plate.fillet(">Y and |Z", 3) self._shelf.add(plate) + self._make_joining_walls(sides) + self._pattern_tray_base(bottom_type) + self._tray_walls(sides, back, wall_height) - # for broad trays, add walls behind beams - # for thin trays connect the walls + def _make_joining_walls(self, sides: Literal["full", "open", "w-pattern", "slots", "ramp"]): + """ + Make the walls between the front walls and the tral walls. For broad trays, + these are walls behind beams. For thin trays this connects the walls + """ - if sides != "open" and self._front_depth > 0: - if plate_width > self._rack_params.rack_width - 2 * self._rack_params.beam_width: + if sides != "open" and self._base_between_beam_walls != "none": + if self.plate_width > self._rack_params.rack_width - 2 * self._rack_params.beam_width: # broad tray - left = self._inner_width / 2 - right = plate_width / 2 + left = self.inner_width / 2 + right = self.plate_width / 2 else: # thin tray - left = plate_width / 2 - self.side_wall_thickness - right = self._inner_width / 2 + left = self.plate_width / 2 - self._rack_params.tray_side_wall_thickness + right = self.inner_width / 2 if abs(left - right) > 0.5: extra_sketch = cad.make_sketch() extra_sketch.add_rect((left, right), - (self._rack_params.beam_width + 0.25, self._front_depth)) + (self._rack_params.beam_width + 0.25, self.front_of_tray)) extra_sketch.mirror("X") - extra_walls = cad.make_extrude("XY", extra_sketch, self._height) + extra_walls = cad.make_extrude("XY", extra_sketch, self.height) self._shelf.add(extra_walls) + def _pattern_tray_base(self, bottom_type: Literal["full", "slots", "slots-large"]): # add slots to base plate padding_x = 8 padding_y = 8 - space_at_front = self._padding_front if padding_front is None else padding_front - if not no_slots: - if bottom_type in ["slots", "slots-large"]: - cut_slots(self._shelf, ">Z", - plate_width, (space_at_front, plate_depth), - padding_x, padding_y, - slot_type="large" if bottom_type == "slots-large" else "standard" - ) - - # add side walls and back wall - - wall_height = self._height if wall_height is None else wall_height - dim_sides = Interval2D(plate_width / 2 - self.side_wall_thickness, plate_width / 2, self._front_depth, plate_depth) - dim_back = cad.helpers.get_dimensions_2d([plate_width, (plate_depth - self.back_wall_thickness, plate_depth)], center="X") + if bottom_type in ["slots", "slots-large"] and not NO_SLOTS: + cut_slots( + self._shelf, ">Z", + self.plate_width, (self.padding_front, self.plate_depth), + padding_x, padding_y, + slot_type="large" if bottom_type == "slots-large" else "standard" + ) + + def _tray_walls(self, + sides: Literal["full", "open", "w-pattern", "slots", "ramp"], + back: Literal["full", "open", "w-pattern", "slots"], + wall_height: float): + + dim_sides = Interval2D(self.plate_width / 2 - self._rack_params.tray_side_wall_thickness, self.plate_width / 2, self.front_of_tray, self.plate_depth) + dim_back = cad.helpers.get_dimensions_2d([self.plate_width, (self.plate_depth - self._rack_params.tray_back_wall_thickness, self.plate_depth)], center="X") have_walls = False wall_sketch = cad.make_sketch() if sides not in ["open", "ramp"]: @@ -233,22 +285,22 @@ def make_tray(self, walls = cad.make_extrude("XY", wall_sketch, wall_height) if sides == "w-pattern": cut_w_pattern(walls, ">X", dim_sides.tuple_y, (0, wall_height), padding_side, padding_top_w, cut_depth=self._rack_params.rack_width + 1) - if sides == "slots" and not no_slots: + if sides == "slots" and not NO_SLOTS: cut_slots(walls, ">X", dim_sides.tuple_y, (0, wall_height), padding_side, padding_top) if back == "w-pattern": cut_w_pattern(walls, ">Y", dim_back.tuple_x, (0, wall_height), padding_side, padding_top_w) - if back == "slots" and not no_slots: + if back == "slots" and not NO_SLOTS: cut_slots(walls, ">Y", dim_back.tuple_x, (0, wall_height), padding_side, padding_top) self._shelf.add(walls) if sides == "ramp": - ramp_width = self._height * 1.5 - ramp_width = min(ramp_width, plate_depth - self._front_depth) + ramp_width = self.height * 1.5 + ramp_width = min(ramp_width, self.plate_depth - self.front_of_tray) ramp_sketch = cad.make_sketch() - ramp_sketch.add_polygon([(self._front_depth, 0), - (self._front_depth + ramp_width, 0), - (self._front_depth, self._height) + ramp_sketch.add_polygon([(self.front_of_tray, 0), + (self.front_of_tray + ramp_width, 0), + (self.front_of_tray, self.height) ]) ramp = cad.make_extrude("YZ", ramp_sketch, dim_sides.tuple_x).mirror("X") self._shelf.add(ramp) @@ -308,7 +360,7 @@ def add_mounting_hole_to_side(self, base_sketch = cad.make_sketch() base_sketch.add_circle(diameter=base_diameter, pos=(y_pos, z_pos)) base_sketch.add_rect(base_diameter, (0, z_pos), center="X", pos=(y_pos, 0)) - base = cad.make_extrude("YZ", base_sketch, (self.plate_width / 2 - self.side_wall_thickness, self.plate_width / 2)) + base = cad.make_extrude("YZ", base_sketch, (self.plate_width / 2 - self._rack_params.tray_side_wall_thickness, self.plate_width / 2)) if side == "both": base.mirror("X") else: @@ -333,10 +385,10 @@ def add_mounting_hole_to_back(self, base_sketch = cad.make_sketch() base_sketch.add_circle(diameter=base_diameter, pos=(x_pos, z_pos)) base_sketch.add_rect(base_diameter, (0, z_pos), center="X", pos=(x_pos, 0)) - base = cad.make_extrude("XZ", base_sketch, (-self.plate_depth, -(self.plate_depth - self.back_wall_thickness))) + base = cad.make_extrude("XZ", base_sketch, (-self.plate_depth, -(self.plate_depth - self._rack_params.tray_back_wall_thickness))) self._shelf.add(base) if hole_type == "M3-tightfit": - self._shelf.cut_hole(">Y", d=2.9, pos=(-x_pos, z_pos), depth=self.back_wall_thickness + 1) + self._shelf.cut_hole(">Y", d=2.9, pos=(-x_pos, z_pos), depth=self._rack_params.tray_back_wall_thickness + 1) else: raise ValueError(f"Unknown hole type: {hole_type}") @@ -354,11 +406,11 @@ def add_cage(self, guide_width = 8 guide_radius = 6 ziptie_pos_y1 = internal_depth * 0.25 - ziptie_pos_y1 = max(ziptie_pos_y1, self._front_depth + channel_width / 2) + ziptie_pos_y1 = max(ziptie_pos_y1, self.front_of_tray + channel_width / 2) ziptie_pos_y2 = internal_depth * 0.75 if internal_height == 0: - internal_height = self._height - cage_height = internal_height + self.bottom_thickness - offset_top + internal_height = self.height + cage_height = internal_height + self._rack_params.tray_bottom_thickness - offset_top # basic cage sketch = cad.make_sketch() @@ -393,7 +445,7 @@ def add_cage(self, # cable tie guides sketch_guides = cad.make_sketch() sketch_guides.add_slot(r=guide_radius, - start=(0, self.bottom_thickness + guide_radius + offset_bottom), + start=(0, self._rack_params.tray_bottom_thickness + guide_radius + offset_bottom), end=(0, cage_height - guide_radius)) sketch_guides.cut_rect((-999, 0), (0, 999)) sketch_guides.move((internal_width / 2, 0)) @@ -401,26 +453,8 @@ def add_cage(self, self._shelf.add(cad.make_extrude_y(sketch_guides, guide_width, center=True).move((0, ziptie_pos_y1, 0))) self._shelf.add(cad.make_extrude_y(sketch_guides, guide_width, center=True).move((0, ziptie_pos_y2, 0))) - - - def get_body(self) -> cad.Body: """ Return the shelf body """ return self._shelf - - -def example(): - """ - An example of this module in use for dev and debugging - """ - rack_params = nimble_builder.RackParameters(nominal_rack_width="10inch") - shelf = ShelfBuilder(height_in_u=3, rack_params=rack_params) - shelf.make_front(front_type="slots", bottom_type="closed") - shelf.make_tray(width="standard", depth=80, sides="ramp", back="open") - cad.show(shelf.get_body()) - -# for development and debugging -if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): - example() From 9654a184ef6835df8a120e7ab25bd1b8ad4149f1 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Thu, 16 May 2024 01:53:33 +0100 Subject: [PATCH 09/16] Furhter tidying and refactoring of nimble_builder --- nimble_builder/__init__.py | 4 +- nimble_builder/helpers.py | 6 +- nimble_builder/nimble_end_plate.py | 9 +- nimble_builder/shelf_builder.py | 370 +++++++++++++++++++---------- 4 files changed, 265 insertions(+), 124 deletions(-) diff --git a/nimble_builder/__init__.py b/nimble_builder/__init__.py index 802d5e5..cac3c32 100644 --- a/nimble_builder/__init__.py +++ b/nimble_builder/__init__.py @@ -1,4 +1,6 @@ - +""" +On loading nimble_builder the RackParameters dataclass will be available +""" from dataclasses import dataclass from typing import Literal diff --git a/nimble_builder/helpers.py b/nimble_builder/helpers.py index d257c56..91ddcac 100644 --- a/nimble_builder/helpers.py +++ b/nimble_builder/helpers.py @@ -45,7 +45,11 @@ def cut_slots(body: cad.Body, slot_line = cad.make_sketch() slot_height = pos_list_vertical[0].size_y - slot_width - slot_line.add_slot(width=slot_height, height=slot_width, angle=90, pos=[p.center for p in pos_list_vertical]) + slot_line.add_slot( + width=slot_height, + height=slot_width, + angle=90, + pos=[p.center for p in pos_list_vertical]) # copy that sketch to cover the whole plate width diff --git a/nimble_builder/nimble_end_plate.py b/nimble_builder/nimble_end_plate.py index b536037..e5dc02d 100644 --- a/nimble_builder/nimble_end_plate.py +++ b/nimble_builder/nimble_end_plate.py @@ -1,13 +1,20 @@ # SPDX-FileCopyrightText: 2023 Andreas Kahler # # SPDX-License-Identifier: AGPL-3.0-or-later - +""" +A generic function for the end plates of the rack (top and bottom plates. +This currently only generates the flat plates, not ones that can hold and +instrument on top. +""" import cadscript as cad import nimble_builder def create_end_plate(width, depth, height, rack_params=None): + """ + Create the top and bottom of the rack + """ if not rack_params: rack_params = nimble_builder.RackParameters() diff --git a/nimble_builder/shelf_builder.py b/nimble_builder/shelf_builder.py index 3490bcd..711c3ee 100644 --- a/nimble_builder/shelf_builder.py +++ b/nimble_builder/shelf_builder.py @@ -2,6 +2,11 @@ # # SPDX-License-Identifier: AGPL-3.0-or-later +""" +Shelf_builder provides classes and methods for creating any numble shelf, with +very little code. +""" + from typing import Literal, Optional, Union import cadscript as cad from cadscript.interval import Interval2D, Interval1D @@ -16,7 +21,10 @@ class ShelfBuilder: """ - A class to build a variety of shelf types + A class to build a variety of shelf types. + + On initialising the font of the shelf is made. Class methods can be used to + add the base the cutouts and mounting origin lies x: center of front panel @@ -29,23 +37,24 @@ class ShelfBuilder: _shelf: cad.Body _height_in_u: int - def __init__(self, - height_in_u: int, - width: Union[Literal["standard", "broad"], float], - depth: Union[Literal["standard"], float], - front_type: Literal["full", "open", "w-pattern", "slots"], - base_between_beam_walls: Literal["none", "front-open", "closed"] = "closed", - beam_wall_type: Literal["none", "standard", "ramp"] = "standard", - rack_params=None - ) -> None: + def __init__( + self, + height_in_u: int, + width: Union[Literal["standard", "broad"], float], + depth: Union[Literal["standard"], float], + front_type: Literal["full", "open", "w-pattern", "slots"], + base_between_beam_walls: Literal["none", "front-open", "closed"] = "closed", + beam_wall_type: Literal["none", "standard", "ramp"] = "standard", + rack_params=None, + ) -> None: """ Initialize the shelf builder this makes the front of the shelf """ self._height_in_u = height_in_u self._width = width self._depth = depth - self._front_type= front_type - self._front_type= front_type + self._front_type = front_type + self._front_type = front_type self._beam_wall_type = beam_wall_type self._base_between_beam_walls = base_between_beam_walls @@ -56,52 +65,76 @@ def __init__(self, @property def plate_width(self): + """ + Derived property which is the width of the main plate (base) + """ # basic size if self._width == "standard": if self._rack_params.nominal_rack_width == "6inch": - # could be a bit larger, use this for backwards compatibility + # could be a bit larger, use this for backwards compatibility return 115 return self.inner_width + 2 * self.rack_params.tray_side_wall_thickness if self._width == "broad": return self._rack_params.rack_width - 2 * self.rack_params.broad_tray_clearance - if isinstance(self._width, (float, int)) and self._width>0: + if isinstance(self._width, (float, int)) and self._width > 0: return self._width raise ValueError(f"The value {self._width} is not a valid shelf width") @property def plate_depth(self): + """ + Derived property which is the depth of the main plate (base) + """ if self._depth == "standard": return 136 - if isinstance(self._depth, (float, int)) and self._depth>0: + if isinstance(self._depth, (float, int)) and self._depth > 0: return self._depth - + raise ValueError(f"The value {self._depth} is not a valid shelf depth") @property def rack_params(self): + """ + Returns the RackParameters() object that configures the shelf. + """ return self._rack_params @property def height(self): + """ + Derived property which is the height of the shelf + """ return self._height_in_u * self._rack_params.mounting_hole_spacing @property def hole_dist_x(self): + """ + Derived property which is the horizontal separation between the mouning holes + """ return self._rack_params.rack_width - self._rack_params.beam_width @property - def hole_offset_y(self): + def hole_offset_z(self): + """ + Derived property which is the vertical ditance between the top/bottom of the shelf + and the mounting hole. + """ return self._rack_params.mounting_hole_spacing / 2 @property def inner_width(self): - return (self._rack_params.rack_width - - 2 * self._rack_params.beam_width - - 2 * self._rack_params.tray_side_wall_thickness) + """ + The internal width of the tray area. + """ + return ( + self._rack_params.rack_width + - 2 * self._rack_params.beam_width + - 2 * self._rack_params.tray_side_wall_thickness + ) @property def front_depth(self): @@ -125,7 +158,7 @@ def padding_front(self): the space between the front panel and where slots etc can start at the try bottom """ if self._base_between_beam_walls != "front-open": - return self.front_depth/4 + return self.front_depth / 4 return self.front_depth def _make_front(self) -> None: @@ -136,17 +169,32 @@ def _make_front(self) -> None: sketch = cad.make_sketch() # front panel - sketch.add_rect(self._rack_params.rack_width, (-self._rack_params.tray_front_panel_thickness, 0), center="X") + sketch.add_rect( + self._rack_params.rack_width, + (-self._rack_params.tray_front_panel_thickness, 0), + center="X", + ) # wall next to the beam if self._beam_wall_type != "none": - sketch.add_rect((self.inner_width / 2, self._rack_params.rack_width / 2 - self._rack_params.beam_width), - self.front_depth, center=False) + sketch.add_rect( + ( + self.inner_width / 2, + self._rack_params.rack_width / 2 - self._rack_params.beam_width, + ), + self.front_depth, + center=False, + ) sketch.mirror("X") # front panel with holes front = cad.make_extrude("XY", sketch, self.height) - pattern_holes = cad.pattern_rect(self.hole_dist_x, (self.hole_offset_y, self.height - self.hole_offset_y), center="X") - front.cut_hole("Y and >Z and |X", min(self.height, self._rack_params.beam_width)) @@ -168,13 +216,17 @@ def _pattern_front(self) -> None: if self._front_type == "w-pattern": padding_x = 0 padding_y = 6 - cut_w_pattern(self._shelf, " None: if self._base_between_beam_walls == "closed": @@ -187,23 +239,30 @@ def _add_base_between_beam_walls(self) -> None: if self._base_between_beam_walls == "front-open": # a base with a cutout on the front side for e.g. cable handling bottom_sketch = cad.make_sketch() - bottom_sketch.add_rect(self.inner_width, (self.front_depth - 2, self.front_depth), center="X") - bottom_sketch.add_polygon([(self.inner_width / 2, 0), - (self.inner_width / 2, self.front_depth), - (self.inner_width / 2 - 5, self.front_depth), - ]) + bottom_sketch.add_rect( + self.inner_width, (self.front_depth - 2, self.front_depth), center="X" + ) + bottom_sketch.add_polygon( + [ + (self.inner_width / 2, 0), + (self.inner_width / 2, self.front_depth), + (self.inner_width / 2 - 5, self.front_depth), + ] + ) bottom_sketch.mirror("X") bottom = cad.make_extrude("XY", bottom_sketch, self._rack_params.tray_bottom_thickness) self._shelf.add(bottom) - def make_tray(self, - sides: Literal["full", "open", "w-pattern", "slots", "ramp"], - back: Literal["full", "open", "w-pattern", "slots"], - bottom_type: Literal["full", "slots", "slots-large"] = "slots-large", - wall_height: Optional[float] = None - ) -> None: + def make_tray( + self, + sides: Literal["full", "open", "w-pattern", "slots", "ramp"], + back: Literal["full", "open", "w-pattern", "slots"], + bottom_type: Literal["full", "slots", "slots-large"] = "slots-large", + wall_height: Optional[float] = None, + ) -> None: """ - Make the tray of the shelf + Make the tray part of the shelf. This is most of the base (except the area between + the beam walls). and the walls at the sides and back of the shelf. """ if not wall_height: @@ -241,8 +300,9 @@ def _make_joining_walls(self, sides: Literal["full", "open", "w-pattern", "slots if abs(left - right) > 0.5: extra_sketch = cad.make_sketch() - extra_sketch.add_rect((left, right), - (self._rack_params.beam_width + 0.25, self.front_of_tray)) + extra_sketch.add_rect( + (left, right), (self._rack_params.beam_width + 0.25, self.front_of_tray) + ) extra_sketch.mirror("X") extra_walls = cad.make_extrude("XY", extra_sketch, self.height) self._shelf.add(extra_walls) @@ -254,19 +314,35 @@ def _pattern_tray_base(self, bottom_type: Literal["full", "slots", "slots-large" if bottom_type in ["slots", "slots-large"] and not NO_SLOTS: cut_slots( - self._shelf, ">Z", - self.plate_width, (self.padding_front, self.plate_depth), - padding_x, padding_y, - slot_type="large" if bottom_type == "slots-large" else "standard" + self._shelf, + ">Z", + self.plate_width, + (self.padding_front, self.plate_depth), + padding_x, + padding_y, + slot_type="large" if bottom_type == "slots-large" else "standard", ) - def _tray_walls(self, - sides: Literal["full", "open", "w-pattern", "slots", "ramp"], - back: Literal["full", "open", "w-pattern", "slots"], - wall_height: float): - - dim_sides = Interval2D(self.plate_width / 2 - self._rack_params.tray_side_wall_thickness, self.plate_width / 2, self.front_of_tray, self.plate_depth) - dim_back = cad.helpers.get_dimensions_2d([self.plate_width, (self.plate_depth - self._rack_params.tray_back_wall_thickness, self.plate_depth)], center="X") + def _tray_walls( + self, + sides: Literal["full", "open", "w-pattern", "slots", "ramp"], + back: Literal["full", "open", "w-pattern", "slots"], + wall_height: float, + ): + + dim_sides = Interval2D( + self.plate_width / 2 - self._rack_params.tray_side_wall_thickness, + self.plate_width / 2, + self.front_of_tray, + self.plate_depth, + ) + dim_back = cad.helpers.get_dimensions_2d( + [ + self.plate_width, + (self.plate_depth - self._rack_params.tray_back_wall_thickness, self.plate_depth), + ], + center="X", + ) have_walls = False wall_sketch = cad.make_sketch() if sides not in ["open", "ramp"]: @@ -277,20 +353,33 @@ def _tray_walls(self, wall_sketch.add_rect(dim_back.tuple_x, dim_back.tuple_y) have_walls = True - padding_side = 8 padding_top = 5 padding_top_w = 6 if have_walls: walls = cad.make_extrude("XY", wall_sketch, wall_height) if sides == "w-pattern": - cut_w_pattern(walls, ">X", dim_sides.tuple_y, (0, wall_height), padding_side, padding_top_w, cut_depth=self._rack_params.rack_width + 1) + cut_w_pattern( + walls, + ">X", + dim_sides.tuple_y, + (0, wall_height), + padding_side, + padding_top_w, + cut_depth=self._rack_params.rack_width + 1, + ) if sides == "slots" and not NO_SLOTS: - cut_slots(walls, ">X", dim_sides.tuple_y, (0, wall_height), padding_side, padding_top) + cut_slots( + walls, ">X", dim_sides.tuple_y, (0, wall_height), padding_side, padding_top + ) if back == "w-pattern": - cut_w_pattern(walls, ">Y", dim_back.tuple_x, (0, wall_height), padding_side, padding_top_w) + cut_w_pattern( + walls, ">Y", dim_back.tuple_x, (0, wall_height), padding_side, padding_top_w + ) if back == "slots" and not NO_SLOTS: - cut_slots(walls, ">Y", dim_back.tuple_x, (0, wall_height), padding_side, padding_top) + cut_slots( + walls, ">Y", dim_back.tuple_x, (0, wall_height), padding_side, padding_top + ) self._shelf.add(walls) @@ -298,21 +387,24 @@ def _tray_walls(self, ramp_width = self.height * 1.5 ramp_width = min(ramp_width, self.plate_depth - self.front_of_tray) ramp_sketch = cad.make_sketch() - ramp_sketch.add_polygon([(self.front_of_tray, 0), - (self.front_of_tray + ramp_width, 0), - (self.front_of_tray, self.height) - ]) + ramp_sketch.add_polygon( + [ + (self.front_of_tray, 0), + (self.front_of_tray + ramp_width, 0), + (self.front_of_tray, self.height), + ] + ) ramp = cad.make_extrude("YZ", ramp_sketch, dim_sides.tuple_x).mirror("X") self._shelf.add(ramp) - - def cut_opening(self, - face: str, - size_x: cad.DimensionDefinitionType, - offset_y: float = 0, - size_y: Optional[cad.DimensionDefinitionType] = None, - depth: float = 999, - ) -> None: + def cut_opening( + self, + face: str, + size_x: cad.DimensionDefinitionType, + offset_y: float = 0, + size_y: Optional[cad.DimensionDefinitionType] = None, + depth: float = 999, + ) -> None: """ Cut an opening into a plate """ @@ -325,13 +417,14 @@ def cut_opening(self, sketch.add_rect(size_x, dim_y.tuple, center="X") self._shelf.cut_extrude(face, sketch, -depth) - def add_mounting_hole_to_bottom(self, - x_pos: float, - y_pos: float, - base_thickness: float, - hole_type: Literal["M3cs", "M3-tightfit", "base-only"], - base_diameter: float = 15 - ) -> None: + def add_mounting_hole_to_bottom( + self, + x_pos: float, + y_pos: float, + base_thickness: float, + hole_type: Literal["M3cs", "M3-tightfit", "base-only"], + base_diameter: float = 15, + ) -> None: """ Add a mounting hole to the shelf """ @@ -347,20 +440,28 @@ def add_mounting_hole_to_bottom(self, else: raise ValueError(f"Unknown hole type: {hole_type}") - def add_mounting_hole_to_side(self, - y_pos: float, - z_pos: float, - hole_type: Literal["M3-tightfit", "HDD"], - side: Literal["left", "right", "both"], - base_diameter: float = 8, - ) -> None: + def add_mounting_hole_to_side( + self, + y_pos: float, + z_pos: float, + hole_type: Literal["M3-tightfit", "HDD"], + side: Literal["left", "right", "both"], + base_diameter: float = 8, + ) -> None: """ Add a mounting hole to the shelf """ base_sketch = cad.make_sketch() base_sketch.add_circle(diameter=base_diameter, pos=(y_pos, z_pos)) base_sketch.add_rect(base_diameter, (0, z_pos), center="X", pos=(y_pos, 0)) - base = cad.make_extrude("YZ", base_sketch, (self.plate_width / 2 - self._rack_params.tray_side_wall_thickness, self.plate_width / 2)) + base = cad.make_extrude( + "YZ", + base_sketch, + ( + self.plate_width / 2 - self._rack_params.tray_side_wall_thickness, + self.plate_width / 2, + ), + ) if side == "both": base.mirror("X") else: @@ -373,11 +474,12 @@ def add_mounting_hole_to_side(self, else: raise ValueError(f"Unknown hole type: {hole_type}") - def add_mounting_hole_to_back(self, - x_pos: float, - z_pos: float, - hole_type: Literal["M3-tightfit"], - ) -> None: + def add_mounting_hole_to_back( + self, + x_pos: float, + z_pos: float, + hole_type: Literal["M3-tightfit"], + ) -> None: """ Add a mounting hole to the shelf """ @@ -385,51 +487,69 @@ def add_mounting_hole_to_back(self, base_sketch = cad.make_sketch() base_sketch.add_circle(diameter=base_diameter, pos=(x_pos, z_pos)) base_sketch.add_rect(base_diameter, (0, z_pos), center="X", pos=(x_pos, 0)) - base = cad.make_extrude("XZ", base_sketch, (-self.plate_depth, -(self.plate_depth - self._rack_params.tray_back_wall_thickness))) + base = cad.make_extrude( + "XZ", + base_sketch, + (-self.plate_depth, -(self.plate_depth - self._rack_params.tray_back_wall_thickness)), + ) self._shelf.add(base) if hole_type == "M3-tightfit": - self._shelf.cut_hole(">Y", d=2.9, pos=(-x_pos, z_pos), depth=self._rack_params.tray_back_wall_thickness + 1) + self._shelf.cut_hole( + ">Y", + d=2.9, + pos=(-x_pos, z_pos), + depth=self._rack_params.tray_back_wall_thickness + 1, + ) else: raise ValueError(f"Unknown hole type: {hole_type}") - def add_cage(self, - internal_width: float, - internal_depth: float, - internal_height: float = 0, - rear_cutout_width: float = 0, - add_ziptie_channels: bool = True, - ): + def add_cage( + self, + internal_width: float, + internal_depth: float, + internal_height: float = 0, + rear_cutout_width: float = 0, + add_ziptie_channels: bool = True, + ): + """ + Add a cage around shelf. The cage is specified by the internal space. + The cage adds ziptie channels as default. + """ wall_thickness = 2.5 offset_top = 2 - offset_bottom = 2 - channel_width = 5 - guide_width = 8 - guide_radius = 6 - ziptie_pos_y1 = internal_depth * 0.25 - ziptie_pos_y1 = max(ziptie_pos_y1, self.front_of_tray + channel_width / 2) - ziptie_pos_y2 = internal_depth * 0.75 + if internal_height == 0: internal_height = self.height cage_height = internal_height + self._rack_params.tray_bottom_thickness - offset_top # basic cage sketch = cad.make_sketch() - sketch.add_rect(internal_width + wall_thickness * 2, internal_depth + wall_thickness, center="X") + sketch.add_rect( + internal_width + wall_thickness * 2, internal_depth + wall_thickness, center="X" + ) sketch.cut_rect(internal_width, internal_depth, center="X") if rear_cutout_width > 0: sketch.cut_rect(rear_cutout_width, internal_depth + wall_thickness + 1, center="X") cage = cad.make_extrude("XY", sketch, cage_height) self._shelf.add(cage) + if add_ziptie_channels: + self._cable_tie_channels(internal_width, internal_depth, cage_height) - # cable tie channels - - if not add_ziptie_channels: - return + def _cable_tie_channels(self, internal_width, internal_depth, cage_height): + channel_width = 5 + offset_bottom = 2 + guide_width = 8 + guide_radius = 6 + ziptie_pos_y1 = max(internal_depth * 0.25, self.front_of_tray + channel_width / 2) + ziptie_pos_y2 = internal_depth * 0.75 sketch_channel_guide = cad.make_sketch() - sketch_channel_guide.add_rect(internal_width + guide_radius * 2, guide_width, - pos=[(0, ziptie_pos_y1), (0, ziptie_pos_y2)]) + sketch_channel_guide.add_rect( + internal_width + guide_radius * 2, + guide_width, + pos=[(0, ziptie_pos_y1), (0, ziptie_pos_y2)], + ) sketch_channel_guide.cut_rect(internal_width, 999) channel_guide = cad.make_extrude_z(sketch_channel_guide, cage_height) channel_guide.fillet(">Z and >X and |Y", guide_radius / 2) @@ -437,21 +557,29 @@ def add_cage(self, self._shelf.add(channel_guide) sketch_channels = cad.make_sketch() - sketch_channels.add_rect(self._rack_params.rack_width, channel_width, - pos=[(0, ziptie_pos_y1), (0, ziptie_pos_y2)]) + sketch_channels.add_rect( + self._rack_params.rack_width, + channel_width, + pos=[(0, ziptie_pos_y1), (0, ziptie_pos_y2)], + ) self._shelf.cut(cad.make_extrude_z(sketch_channels, 999)) - # cable tie guides sketch_guides = cad.make_sketch() - sketch_guides.add_slot(r=guide_radius, - start=(0, self._rack_params.tray_bottom_thickness + guide_radius + offset_bottom), - end=(0, cage_height - guide_radius)) + sketch_guides.add_slot( + r=guide_radius, + start=(0, self._rack_params.tray_bottom_thickness + guide_radius + offset_bottom), + end=(0, cage_height - guide_radius), + ) sketch_guides.cut_rect((-999, 0), (0, 999)) sketch_guides.move((internal_width / 2, 0)) sketch_guides.mirror("X") - self._shelf.add(cad.make_extrude_y(sketch_guides, guide_width, center=True).move((0, ziptie_pos_y1, 0))) - self._shelf.add(cad.make_extrude_y(sketch_guides, guide_width, center=True).move((0, ziptie_pos_y2, 0))) + self._shelf.add( + cad.make_extrude_y(sketch_guides, guide_width, center=True).move((0, ziptie_pos_y1, 0)) + ) + self._shelf.add( + cad.make_extrude_y(sketch_guides, guide_width, center=True).move((0, ziptie_pos_y2, 0)) + ) def get_body(self) -> cad.Body: """ From 4997946b1093e9470512733b62c0f422a8928732 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Thu, 16 May 2024 02:00:18 +0100 Subject: [PATCH 10/16] Remove old nimble_tray and use new trays from nimble_builder --- mechanical/components/cadquery/nimble_tray.py | 95 ------------------- nimble_orchestration/configuration.py | 5 +- server/nimble_server.py | 3 +- 3 files changed, 4 insertions(+), 99 deletions(-) delete mode 100644 mechanical/components/cadquery/nimble_tray.py diff --git a/mechanical/components/cadquery/nimble_tray.py b/mechanical/components/cadquery/nimble_tray.py deleted file mode 100644 index 7007b7a..0000000 --- a/mechanical/components/cadquery/nimble_tray.py +++ /dev/null @@ -1,95 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Andreas Kahler -# SPDX-FileCopyrightText: 2023 Ruslan Krenzler -# -# SPDX-License-Identifier: AGPL-3.0-or-later - -import cadquery as cq -import math - -height_in_u = 2 -tray_width = 115 -tray_depth = 115 - - -class Params: - def __init__(self): - - # Default length unit is mm. - self.tray_width = 115 - self.tray_depth = 115 - self.leg_width = 20 - self.height_in_u = 2 - self.wall_thickness = 4 - self.hole_distance = 14 # Distance between the holes - self.hole_diameter = 3.6 - - @property - def inner_width(self): - return self.tray_width - self.wall_thickness * 2 - - @property - def inner_depth(self): - return self.tray_depth - self.wall_thickness * 2 - - @property - def tray_height(self): - return self.height_in_u * self.hole_distance - - @property - def inner_height(self): - return self.tray_height - self.wall_thickness - - @staticmethod - def build_for_inner_dims(width, height, depth): - p = Params() - p.tray_width = width + 2 * p.wall_thickness - p.tray_depth = depth + 2 * p.wall_thickness - # Convert hole into number of holes. - nh = math.ceil(height / p.hole_distance) - # The number of holes must be at least two - p.height_in_u = max(nh, 2) - return p - - -def _create_part(params): - # Create an empty tray as a box without a lid with a given wall thickness. - tray = cq.Workplane("XY").box(params.tray_width, params.tray_depth, params.tray_height, centered=False) - tray = tray.faces(">Z").workplane().moveTo(params.wall_thickness, params.wall_thickness).rect(params.inner_width, - params.inner_depth, - centered=False).cutBlind( - -params.inner_height) - # Add some mounting wings to fix the tray within a case. - wings = cq.Workplane("XY").moveTo(-params.leg_width, 0).box(params.tray_width + params.leg_width * 2, - params.wall_thickness, params.tray_height, - centered=False) - # Add four holes. - wings = wings.faces(">Y").workplane().moveTo(params.leg_width / 2, params.hole_distance / 2).circle( - params.hole_diameter / 2).cutThruAll() - wings = wings.faces(">Y").workplane().moveTo(params.leg_width / 2, - params.tray_height - params.hole_distance / 2).circle( - params.hole_diameter / 2).cutThruAll() - wings = wings.faces(">Y").workplane().moveTo(-params.tray_width - params.leg_width / 2, - params.hole_distance / 2).circle(params.hole_diameter / 2).cutThruAll() - wings = wings.faces(">Y").workplane().moveTo(-params.tray_width - params.leg_width / 2, - params.tray_height - params.hole_distance / 2).circle( - params.hole_diameter / 2).cutThruAll() - # Merge box and mounting wings. - result = tray.union(wings) - # Remove part of the wall from the front and from the back of the ray. - result = result.faces(">Y").workplane().moveTo(-params.tray_width / 2, -params.tray_height / 2).rect( - params.inner_width - params.wall_thickness, params.inner_height - params.wall_thickness).cutThruAll() - - return result - - -def create(height_in_u, tray_width, tray_depth): - params = Params() - params.height_in_u = height_in_u - params.tray_width = tray_width - params.tray_depth = tray_depth - return _create_part(params) - -if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): - # CQGI should execute this whenever called - obj = create(height_in_u, tray_width, tray_depth) - show_object(obj) diff --git a/nimble_orchestration/configuration.py b/nimble_orchestration/configuration.py index 1c57840..6434b6c 100644 --- a/nimble_orchestration/configuration.py +++ b/nimble_orchestration/configuration.py @@ -198,7 +198,7 @@ def _trays(self): long and messy! """ - source = os.path.join(REL_MECH_DIR, "components/cadquery/nimble_tray.py") + source = os.path.join(REL_MECH_DIR, "components/cadquery/tray_6in.py") source = posixpath.normpath(source) trays = [] z_offset = self._rack_params.bottom_tray_offet @@ -220,8 +220,7 @@ def _trays(self): source_files=[source], parameters={ "height_in_u": device.height_in_u, - "tray_width": self._rack_params.tray_width, - "tray_depth": self._rack_params.tray_depth, + "shelf_type": "generic", }, application="cadquery" ) diff --git a/server/nimble_server.py b/server/nimble_server.py index 7a73023..1ff71ac 100644 --- a/server/nimble_server.py +++ b/server/nimble_server.py @@ -213,8 +213,9 @@ def read_item(number_of_units: float = 2, tray_width: float = 115, tray_depth: f import cadquery as cq + ## NOTE that tray_width and tray_depth are no longer used after moving to tray_6in.py # Run the script with customized parameters - tray = cqgi_model_script("nimble_tray.py", {"height_in_u": number_of_units, "tray_width": tray_width, "tray_depth": tray_depth}) + tray = cqgi_model_script("tray_6in.py", {"height_in_u": number_of_units, "shelf_type" = "generic"}) # In case there was an error if (type(tray).__name__ == "HTMLResponse"): From 07438ebc839280625d6fda34e40eda039942f61a Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Thu, 16 May 2024 03:03:59 +0100 Subject: [PATCH 11/16] Some final tidying of the component scripts --- mechanical/assembly_renderer.py | 7 +- mechanical/components/cadquery/base_plate.py | 9 +- mechanical/components/cadquery/rack_leg.py | 7 + mechanical/components/cadquery/top_plate.py | 9 +- mechanical/components/cadquery/tray_6in.py | 380 ++++++++++++------- nimble_builder/__init__.py | 55 ++- nimble_builder/shelf_builder.py | 44 +++ 7 files changed, 356 insertions(+), 155 deletions(-) diff --git a/mechanical/assembly_renderer.py b/mechanical/assembly_renderer.py index 36538c1..3ee1bc5 100644 --- a/mechanical/assembly_renderer.py +++ b/mechanical/assembly_renderer.py @@ -1,7 +1,8 @@ """ cadquery module that takes an assembly-def.yaml file and generate an assembly from it. -The assembly-def.yaml file is a yaml file that contains a list of parts and their positions in the assembly. +The assembly-def.yaml file is a yaml file that contains a list of parts and their +positions in the assembly. Example file: assembly: parts: @@ -52,7 +53,7 @@ class AssemblyRederer: def __init__(self, assembly_def_file: str): - with open(assembly_def_file, "r") as f: + with open(assembly_def_file, "r", encoding="utf-8") as f: assembly_def = yaml.load(f, Loader=yaml.FullLoader) for part_def in assembly_def["assembly"]["parts"]: self._parts.append(PartDefinition(part_def)) @@ -87,7 +88,7 @@ def generate(self) -> cq.Assembly: if __name__ == "__main__": # for debugging - folder = (Path(__file__).resolve().parent.parent) + folder = Path(__file__).resolve().parent.parent os.chdir(folder) assembly = AssemblyRederer("assembly-def.yaml").generate() assembly.save("assembly.stl", "STL") diff --git a/mechanical/components/cadquery/base_plate.py b/mechanical/components/cadquery/base_plate.py index 4e43c74..c694c77 100644 --- a/mechanical/components/cadquery/base_plate.py +++ b/mechanical/components/cadquery/base_plate.py @@ -2,9 +2,12 @@ # # SPDX-License-Identifier: AGPL-3.0-or-later +""" +cq-cli script using cadscript to generate the baseplate of a nimble rack +""" import cadscript as cad from nimble_builder.nimble_end_plate import create_end_plate - + # parameters to be set in exsource-def.yaml file width = 100 @@ -13,7 +16,9 @@ def create(width, depth, height): - # create end plate, turn it over and move it to the correct position + """ + create end plate, turn it over and move it to the correct position + """ plate = create_end_plate(width, depth, height) plate = plate.rotate("X", 180) plate = plate.move((0, 0, height)) diff --git a/mechanical/components/cadquery/rack_leg.py b/mechanical/components/cadquery/rack_leg.py index 49f20e9..6689533 100644 --- a/mechanical/components/cadquery/rack_leg.py +++ b/mechanical/components/cadquery/rack_leg.py @@ -1,3 +1,7 @@ +""" +cq-cli script using cadscript to generate the rack legs of a nimble rack +""" + from math import floor import cadscript as cad @@ -16,6 +20,9 @@ def make_rack_leg( mounting_holes_dia, rack_params=None ) -> cad.Body: + """ + Create the rack legs of given length + """ if not rack_params: rack_params = nimble_builder.RackParameters() diff --git a/mechanical/components/cadquery/top_plate.py b/mechanical/components/cadquery/top_plate.py index 2541151..bd65826 100644 --- a/mechanical/components/cadquery/top_plate.py +++ b/mechanical/components/cadquery/top_plate.py @@ -1,7 +1,10 @@ # SPDX-FileCopyrightText: 2023 Andreas Kahler # # SPDX-License-Identifier: AGPL-3.0-or-later - +""" +cq-cli script using cadscript to generate the topplate of a nimble rack. +As of yet this cannot hold an instrument. +""" import cadscript as cad from nimble_builder.nimble_end_plate import create_end_plate @@ -13,7 +16,9 @@ def create(width, depth, height): - # just create end plate, not further changes needed + """ + just create end plate, not further changes needed + """ return create_end_plate(width, depth, height) if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): diff --git a/mechanical/components/cadquery/tray_6in.py b/mechanical/components/cadquery/tray_6in.py index 21a1140..542e552 100644 --- a/mechanical/components/cadquery/tray_6in.py +++ b/mechanical/components/cadquery/tray_6in.py @@ -2,10 +2,14 @@ # # SPDX-License-Identifier: AGPL-3.0-or-later -import typing +""" +This module provides many different nimble shelves created using +the nimble_builder ShelfBuilder. +""" + import cadscript as cad import nimble_builder -from nimble_builder import shelf_builder +from nimble_builder.shelf_builder import ShelfBuilder, ziptie_shelf # parameters to be set in exsource-def.yaml file @@ -23,157 +27,243 @@ # "dual-ssd" - for 2x 2.5" SSD # "raspi" - for Raspberry Pi -shelf_type = "generic" +shelf_type = "stuff-thin" height_in_u = 2 - def create_6in_shelf(shelf_type, height_in_u) -> cad.Body: - if shelf_type == "generic": - return create_ziptie_shelf(height_in_u) - if shelf_type == "stuff": - b = shelf_builder.ShelfBuilder(height_in_u, width="broad", depth="standard", front_type="w-pattern") - b.make_tray(sides="w-pattern", back="open") - return b.get_body() - if shelf_type == "stuff-thin": - b = shelf_builder.ShelfBuilder(height_in_u, width="standard", depth="standard", front_type="w-pattern") - - b.make_tray(sides="w-pattern", back="open") - return b.get_body() - if shelf_type == "nuc": - b = shelf_builder.ShelfBuilder(height_in_u, width="broad", depth="standard", front_type="full") - b.cut_opening("Y", 30, offset_y=b.rack_params.tray_bottom_thickness, depth=10) - b.add_mounting_hole_to_side(y_pos=59, z_pos=b.height / 2, hole_type="M3-tightfit", side="both") - b.add_mounting_hole_to_back(x_pos=-75 / 2, z_pos=b.height / 2, hole_type="M3-tightfit") - b.add_mounting_hole_to_back(x_pos=+75 / 2, z_pos=b.height / 2, hole_type="M3-tightfit") - return b.get_body() - if shelf_type == "anker-powerport5": - return create_ziptie_shelf(height_in_u, - internal_width=56, - internal_depth=90.8, - internal_height=25, - front_cutout_width=53) - - if shelf_type == "anker-atom3slim": - return create_ziptie_shelf(height_in_u, - internal_width=86.5, - internal_depth=90, - internal_height=20, - front_cutout_width=71) - if shelf_type == "anker-a2123": - # 99 x 70 x 26 mm - # use height = 25, max for cage on 2 hole shelf - return create_ziptie_shelf(height_in_u, - internal_width=70, - internal_depth=99, - internal_height=25, - front_cutout_width=65) - if shelf_type == "hdd35": - width = 102.8 # 101.6 + 1.2 clearance - screw_pos1 = 77.3 # distance from front - screw_pos2 = screw_pos1 + 41.61 - screw_y = 7 # distance from bottom plane - b = shelf_builder.ShelfBuilder(height_in_u, width="standard", depth="standard", front_type="w-pattern") - b.make_tray(sides="slots", back="open") - mount_sketch = cad.make_sketch() - mount_sketch.add_rect((width / 2, b.inner_width / 2 + b.rack_params.tray_side_wall_thickness), 21, - pos=[(0, screw_pos1), (0, screw_pos2)]) - mount_sketch.chamfer(" cad.Body: + """ + A generic cable tie shelf + """ + return ziptie_shelf(height_in_u) + +def stuff_shelf(height_in_u, thin=False) -> cad.Body: + """ + A shelf for general stuff such as wires. No access to the front + """ + width = "broad" if not thin else "standard" + builder = ShelfBuilder( + height_in_u, width=width, depth="standard", front_type="w-pattern" + ) + builder.make_tray(sides="w-pattern", back="open") + return builder.get_body() + +def nuc_shelf(height_in_u) -> cad.Body: + """ + A shelf for an Intel NUC + """ + builder = ShelfBuilder( + height_in_u, width="broad", depth="standard", front_type="full" + ) + builder.cut_opening(" cad.Body: + """ + A shelf for a Ubiquiti USW-Flex + """ + builder = ShelfBuilder( + height_in_u, width="standard", depth=119.5, front_type="full" + ) + builder.cut_opening(" cad.Body: + """ + A shelf for a for Ubiquiti Flex Mini + """ + rack_params = nimble_builder.RackParameters(tray_side_wall_thickness=3.8) + builder = ShelfBuilder( + height_in_u, width="standard", depth=73.4, front_type="full", rack_params=rack_params + ) + builder.cut_opening("Y", 30, offset_y=builder.rack_params.tray_bottom_thickness, depth=10) + builder.add_mounting_hole_to_side( + y_pos=59, z_pos=builder.height / 2, hole_type="M3-tightfit", side="both" + ) + builder.add_mounting_hole_to_back( + x_pos=-75 / 2, z_pos=builder.height / 2, hole_type="M3-tightfit" + ) + builder.add_mounting_hole_to_back( + x_pos=+75 / 2, z_pos=builder.height / 2, hole_type="M3-tightfit" + ) + return builder.get_body() + +def anker_shelf( + height_in_u, + internal_width, + internal_depth, + internal_height, + front_cutout_width + ) -> cad.Body: + """ + A shelf for an Anker PowerPort 5, Anker 360 Charger 60W (a2123), or Anker PowerPort Atom + III Slim (AK-194644090180) + """ + return ziptie_shelf( + height_in_u, + internal_width=internal_width, + internal_depth=internal_depth, + internal_height=internal_height, + front_cutout_width=front_cutout_width + ) + +def hdd35_shelf(height_in_u) -> cad.Body: + """ + A shelf for an 3.5" HDD + """ + width = 102.8 # 101.6 + 1.2 clearance + screw_pos1 = 77.3 # distance from front + screw_pos2 = screw_pos1 + 41.61 + screw_y = 7 # distance from bottom plane + builder = ShelfBuilder( + height_in_u, width="standard", depth="standard", front_type="w-pattern" + ) + builder.make_tray(sides="slots", back="open") + mount_sketch = cad.make_sketch() + mount_sketch.add_rect( + (width / 2, builder.inner_width / 2 + builder.rack_params.tray_side_wall_thickness), + 21, + pos=[(0, screw_pos1), (0, screw_pos2)], + ) + mount_sketch.chamfer(" cad.Body: + """ + A shelf for atwo 2.5" SSDs + """ + rack_params = nimble_builder.RackParameters() + width = 70 + screw_pos1 = 12.5 # distance from front + screw_pos2 = screw_pos1 + 76 + screw_y1 = 6.6 # distance from bottom plane + screw_y2 = screw_y1 + 11.1 + builder = ShelfBuilder( + height_in_u, + width=width + 2 * rack_params.tray_side_wall_thickness, + depth=111, + front_type="w-pattern", + base_between_beam_walls="none", + beam_wall_type="none", + ) + builder.make_tray(sides="slots", back="open") + for x, y in [ + (screw_pos1, screw_y2), + (screw_pos2, screw_y2), + (screw_pos1, screw_y1), + (screw_pos2, screw_y1), + ]: + builder.add_mounting_hole_to_side( + y_pos=x, + z_pos=y + rack_params.tray_bottom_thickness, + hole_type="M3-tightfit", + side="both", + base_diameter=11, + ) + return builder.get_body() -def create_ziptie_shelf( - height_in_u: int, - internal_width: typing.Optional[float]=None, - internal_depth: typing.Optional[float]=None, - internal_height: typing.Optional[float]=None, - front_cutout_width: typing.Optional[float]=None, - rear_cutout_width: typing.Optional[float]=None, - rack_params: nimble_builder.RackParameters = None -): - - if not rack_params: - rack_params = nimble_builder.RackParameters() - if not internal_width: - internal_width = rack_params.tray_width - 12 - if not internal_depth: - internal_depth = 115 - if not internal_height: - internal_height = rack_params.tray_height(height_in_u) - 3 - if not rear_cutout_width: - front_cutout_width = internal_width - 10 - if not rear_cutout_width: - rear_cutout_width = internal_width - 20 - - - b = shelf_builder.ShelfBuilder(height_in_u, width=internal_width+10, depth=internal_depth+3, front_type="full", beam_wall_type="ramp") - b.cut_opening(" cad.Body: + """ + A shelf for a Raspberry Pi + """ + screw_dist_x = 49 + screw_dist_y = 58 + dist_to_front = 23.5 + offset_x = -13 + builder = ShelfBuilder(height_in_u, width="standard", depth=111, front_type="full") + builder.cut_opening(" cad.Body: Return the shelf body """ return self._shelf + +def ziptie_shelf( + height_in_u: int, + internal_width: Optional[float] = None, + internal_depth: Optional[float] = None, + internal_height: Optional[float] = None, + front_cutout_width: Optional[float] = None, + rear_cutout_width: Optional[float] = None, + rack_params: nimble_builder.RackParameters = None, +): + """ + Return a ziptie shelf. The height in units must be set. + The other sizes are deterined from the internal tray dimentsions to + make it simple to create a shelf dor a device of known size + """ + if not rack_params: + rack_params = nimble_builder.RackParameters() + if not internal_width: + internal_width = rack_params.tray_width - 12 + if not internal_depth: + internal_depth = 115 + if not internal_height: + internal_height = rack_params.tray_height(height_in_u) - 3 + if not rear_cutout_width: + front_cutout_width = internal_width - 10 + if not rear_cutout_width: + rear_cutout_width = internal_width - 20 + + builder = ShelfBuilder( + height_in_u, + width=internal_width + 10, + depth=internal_depth + 3, + front_type="full", + beam_wall_type="ramp", + ) + builder.cut_opening( + " Date: Fri, 17 May 2024 10:34:38 -0400 Subject: [PATCH 12/16] Minor fixes to generate.md --- generate.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/generate.md b/generate.md index 069d314..3d9339f 100644 --- a/generate.md +++ b/generate.md @@ -8,7 +8,7 @@ SPDX-License-Identifier: CERN-OHL-S-2.0 ## Overview -Nimble hardware models are generated using Python. For CAD we used [CadQuery](https://cadquery.readthedocs.io/en/latest/intro.html). CadQuery scripts can be found in the `mechanical` directory, with individual components in the `mechanical/components/cadquery` directory. Our CadQuery scripts also our `nimble-builder` module of helper functions. +Nimble hardware models are generated using Python. For CAD we used [CadQuery](https://cadquery.readthedocs.io/en/latest/intro.html). CadQuery scripts can be found in the `mechanical` directory, with individual components in the `mechanical/components/cadquery` directory. Our CadQuery scripts also use the `nimble-builder` module of helper functions. Our preferred way to generate the models needed for a nimble configuration is via our orchestration module `nimble-orchestration`. This can be used to generate trays for networking components, nimble-rack components, and a final CAD assembly. The orchestration system uses [cq-cli](https://github.com/CadQuery/cq-cli) to execute CadQuery scripts, and [ExSource Tools](https://gitlab.com/gitbuilding/exsource-tools) to manage the process of turning scripts into useable models. The orchestration script will eventually use [GitBuilding](https://gitbuilding.io) to generate assembly manuals. @@ -32,7 +32,7 @@ Run: ## Running the code -As the code is very much a work in progress we do not have detailed documentation of how to it for custom configurations. +As the code is very much a work in progress we do not have detailed documentation of how to run it for custom configurations. For now the best way to run the code is with the two scripts in this repository. @@ -42,7 +42,7 @@ The script `generate-static.py` is used to create a number of example components To run this script, run the following command: - python generate-static.py + python generate_static.py This should create the `build` directory. Inside this the `printed_components` directory should contain a number of `stl` files that can be 3D printed. @@ -53,7 +53,7 @@ It also creates a number of files that are used by the orchestration script. If you don't want to run this locally you can see a [hosted version of the output](https://wakoma.github.io/nimble/). -### Generate static +### Generate configuration The script `generate.py` is our test-bed script for generating a complete set of information for a nimble configuration. From 45a250f5da5898554a25aceb808177746bbd91e1 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 19 May 2024 20:14:17 +0100 Subject: [PATCH 13/16] Apply doc-string suggestions from code review Co-authored-by: Jeremy Wright --- mechanical/components/cadquery/base_plate.py | 2 +- mechanical/components/cadquery/rack_leg.py | 4 +-- nimble_builder/__init__.py | 32 ++++++++++---------- nimble_builder/helpers.py | 2 +- nimble_builder/nimble_end_plate.py | 2 +- nimble_orchestration/components.py | 2 +- nimble_orchestration/device.py | 2 +- nimble_orchestration/devices_json_updater.py | 2 +- nimble_orchestration/yaml_cleaner.py | 2 +- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/mechanical/components/cadquery/base_plate.py b/mechanical/components/cadquery/base_plate.py index c694c77..dd667f3 100644 --- a/mechanical/components/cadquery/base_plate.py +++ b/mechanical/components/cadquery/base_plate.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later """ -cq-cli script using cadscript to generate the baseplate of a nimble rack +cq-cli script using cadscript to generate the baseplate of a nimble rack. """ import cadscript as cad from nimble_builder.nimble_end_plate import create_end_plate diff --git a/mechanical/components/cadquery/rack_leg.py b/mechanical/components/cadquery/rack_leg.py index 6689533..ca51fa7 100644 --- a/mechanical/components/cadquery/rack_leg.py +++ b/mechanical/components/cadquery/rack_leg.py @@ -1,5 +1,5 @@ """ -cq-cli script using cadscript to generate the rack legs of a nimble rack +cq-cli script using cadscript to generate the rack legs of a nimble rack. """ from math import floor @@ -21,7 +21,7 @@ def make_rack_leg( rack_params=None ) -> cad.Body: """ - Create the rack legs of given length + Create the rack legs of given length. """ if not rack_params: diff --git a/nimble_builder/__init__.py b/nimble_builder/__init__.py index b461317..6aa26f5 100644 --- a/nimble_builder/__init__.py +++ b/nimble_builder/__init__.py @@ -1,5 +1,5 @@ """ -On loading nimble_builder the RackParameters dataclass will be available +On loading nimble_builder the RackParameters dataclass will be available. """ from dataclasses import dataclass from typing import Literal @@ -7,7 +7,7 @@ @dataclass class RackParameters: """ - A class to hold the RackParameters, both fixed and derived + A class to hold the RackParameters, both fixed and derived. """ beam_width: float = 20.0 @@ -40,7 +40,7 @@ def mounting_hole_clearance_diameter(self): Return the diameter for a clearance hole for the front mounting screws. This is determined by the `mounting_screws` parameter. Clearance holes should be used in tray fronts. - See also: mounting_hole_tap_diameter + See also: `mounting_hole_tap_diameter` """ if self.mounting_screws == "M4": return 4.3 @@ -54,10 +54,10 @@ def mounting_hole_tap_diameter(self): Return the diameter for a tapped hole for the front mounting screws. This is determined by the `mounting_screws` parameter. Tap holes should be in the legs where the screw will tap. - As 3D printers tend to over extrude ad the fact that the machine screws - are tapping directly into plastic the holes are larger than standard + As 3D printers tend to over extrude and the fact that the machine screws + are tapping directly into plastic, the holes are larger than standard metric tap holes. - See also: mounting_hole_tap_diameter + See also: `mounting_hole_tap_diameter` """ if self.mounting_screws == "M4": return 3.6 @@ -71,8 +71,8 @@ def end_plate_hole_clearance_diameter(self): Return the diameter for a clearance hole for the countersunk screws that attach the top and bottom plates. This is determined by the `end_plate_screws` parameter. - Clearance holes should be used in end_plates. - See also: mounting_hole_tap_diameter + Clearance holes should be used in `end_plates`. + See also: `mounting_hole_tap_diameter` """ if self.end_plate_screws == "M5": return 5.4 @@ -86,7 +86,7 @@ def end_plate_hole_countersink_dia(self): Return the countersink diameter for a clearance hole for the countersunk screws that attach the top and bottom plates. This is determined by the `end_plate_screws` parameter. - See also: mounting_hole_tap_diameter + See also: `mounting_hole_tap_diameter` """ if self.end_plate_screws == "M5": return 11.5 @@ -101,10 +101,10 @@ def end_plate_hole_tap_diameter(self): that attach the top and bottom plates. This is determined by the `end_plate_screws` parameter. Tap holes should be in the top and bottom of the legs where the screw will tap. - As 3D printers tend to over extrude ad the fact that the machine screws + As 3D printers tend to over extrude and the fact that the machine screws are tapping directly into plastic the holes are larger than standard metric tap holes. - See also: mounting_hole_tap_diameter + See also: `mounting_hole_tap_diameter` """ if self.end_plate_screws == "M5": return 4.5 @@ -118,11 +118,11 @@ def end_plate_hole_tap_diameter(self): @property def rack_width(self): """ - Return the rack width in mm as determined by the nominal_rack_width. + Return the rack width in mm as determined by the `nominal_rack_width`. Options are: - "6inch" - 155mm - full width (front panel) of the 6 inch nimble rack - "10inch" - 254mm - full width (front panel) of the 10 inch rack - "10inch_reduced - 250mm - as above bu reduced to fit into a 250mm wide printer + `6inch` - 155mm - full width (front panel) of the 6 inch nimble rack + `10inch` - 254mm - full width (front panel) of the 10 inch rack + `10inch_reduced` - 250mm - as above but reduced to fit into a 250mm wide printer """ if self.nominal_rack_width == "6inch": return 155 @@ -135,7 +135,7 @@ def rack_width(self): @property def tray_width(self): """ - Return derived parameter for the width of a standard tray + Return derived parameter for the width of a standard tray. """ return self.rack_width - 2 * self.beam_width diff --git a/nimble_builder/helpers.py b/nimble_builder/helpers.py index 91ddcac..c4750bb 100644 --- a/nimble_builder/helpers.py +++ b/nimble_builder/helpers.py @@ -1,6 +1,6 @@ """ A number of helper functions for CadQuery used to simplify the creation of nimble -rack components +rack components. """ from typing import Literal import cadscript as cad diff --git a/nimble_builder/nimble_end_plate.py b/nimble_builder/nimble_end_plate.py index e5dc02d..ad42781 100644 --- a/nimble_builder/nimble_end_plate.py +++ b/nimble_builder/nimble_end_plate.py @@ -13,7 +13,7 @@ def create_end_plate(width, depth, height, rack_params=None): """ - Create the top and bottom of the rack + Create the top and bottom of the rack. """ if not rack_params: diff --git a/nimble_orchestration/components.py b/nimble_orchestration/components.py index ec00240..3fce3ae 100644 --- a/nimble_orchestration/components.py +++ b/nimble_orchestration/components.py @@ -6,7 +6,7 @@ that have no source code `GeneratedMechanicalComponent` is a child class of `MechanicalComponent`, it contains all the information for exsource to generate the CAD models for this component -`AssembledComponent` Is a class that holds very basic assmbly information for a given +`AssembledComponent` is a class that holds very basic assembly information for a given `MechanicalComponent` """ from copy import copy, deepcopy diff --git a/nimble_orchestration/device.py b/nimble_orchestration/device.py index 080367e..0f7aa8e 100644 --- a/nimble_orchestration/device.py +++ b/nimble_orchestration/device.py @@ -54,6 +54,6 @@ def __init__(self, json_node): @property def tray_id(self): """ - Return and identification for the shelf. + Return an identification for the shelf. """ return f"tray_h{self.height_in_u}_t{self.shelf_type.lower().replace(' ', '_')}" diff --git a/nimble_orchestration/devices_json_updater.py b/nimble_orchestration/devices_json_updater.py index 239bc0b..a5866a3 100755 --- a/nimble_orchestration/devices_json_updater.py +++ b/nimble_orchestration/devices_json_updater.py @@ -20,7 +20,7 @@ def usage(): def main(): """ - Main script to turn a CSV into a + Main script to turn a CSV file into a JSON file. It does not do pretty formatting, it is a JSON file with no newlines. """ # Check to make sure the user passed a CSV path diff --git a/nimble_orchestration/yaml_cleaner.py b/nimble_orchestration/yaml_cleaner.py index 49f317d..b2b2366 100644 --- a/nimble_orchestration/yaml_cleaner.py +++ b/nimble_orchestration/yaml_cleaner.py @@ -10,7 +10,7 @@ def clean(data: dict) -> dict: Clean the input dictionary (recursively). Removing any keys where the value is none, changing pathlib.Path to strings and converting tuples to strings. - The input is a single dictionary, the output is a cleaned dictionary + The input is a single dictionary, the output is a cleaned dictionary. """ # iterate over entries keys_to_delete = [] From b87a184a252ff0d64a6159318535dfaa3a9ccfee Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 19 May 2024 20:25:31 +0100 Subject: [PATCH 14/16] Apply more doc-string suggestions from code review Co-authored-by: Jeremy Wright --- mechanical/components/cadquery/tray_6in.py | 4 +-- nimble_builder/shelf_builder.py | 30 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mechanical/components/cadquery/tray_6in.py b/mechanical/components/cadquery/tray_6in.py index 542e552..c88cd82 100644 --- a/mechanical/components/cadquery/tray_6in.py +++ b/mechanical/components/cadquery/tray_6in.py @@ -33,8 +33,8 @@ def create_6in_shelf(shelf_type, height_in_u) -> cad.Body: """ - This is the tip level function called when the script - is called. It used the shelf-type string to decide + This is the top level function called when the script + is called. It uses the `shelf_type` string to decide which of the defined shelf functions to call. """ diff --git a/nimble_builder/shelf_builder.py b/nimble_builder/shelf_builder.py index a0ff5ca..ac8fb6d 100644 --- a/nimble_builder/shelf_builder.py +++ b/nimble_builder/shelf_builder.py @@ -23,8 +23,8 @@ class ShelfBuilder: """ A class to build a variety of shelf types. - On initialising the font of the shelf is made. Class methods can be used to - add the base the cutouts and mounting + On initialising the front of the shelf is made. Class methods can be used to + add the base the cutouts and mounting. origin lies x: center of front panel @@ -48,7 +48,7 @@ def __init__( rack_params=None, ) -> None: """ - Initialize the shelf builder this makes the front of the shelf + Initialize the shelf builder this makes the front of the shelf. """ self._height_in_u = height_in_u self._width = width @@ -66,7 +66,7 @@ def __init__( @property def plate_width(self): """ - Derived property which is the width of the main plate (base) + Derived property which is the width of the main plate (base). """ # basic size @@ -106,14 +106,14 @@ def rack_params(self): @property def height(self): """ - Derived property which is the height of the shelf + Derived property which is the height of the shelf. """ return self._height_in_u * self._rack_params.mounting_hole_spacing @property def hole_dist_x(self): """ - Derived property which is the horizontal separation between the mouning holes + Derived property which is the horizontal separation between the mounting holes. """ return self._rack_params.rack_width - self._rack_params.beam_width @@ -139,14 +139,14 @@ def inner_width(self): @property def front_depth(self): """ - the length of the beam walls that sit behind front pannel + The length of the beam walls that sit behind front panel. """ return self._rack_params.beam_width + 2.75 @property def front_of_tray(self): """ - Front poistion of tray. + Front position of the tray. """ if self._base_between_beam_walls == "none": return 0 @@ -155,7 +155,7 @@ def front_of_tray(self): @property def padding_front(self): """ - the space between the front panel and where slots etc can start at the try bottom + The space between the front panel and where slots etc can start at the tray bottom. """ if self._base_between_beam_walls != "front-open": return self.front_depth / 4 @@ -163,7 +163,7 @@ def padding_front(self): def _make_front(self) -> None: """ - Make the front panel of the shelf. This happens on initalisation + Make the front panel of the shelf. This happens on initialization. """ # sketch as viewed from top @@ -262,7 +262,7 @@ def make_tray( ) -> None: """ Make the tray part of the shelf. This is most of the base (except the area between - the beam walls). and the walls at the sides and back of the shelf. + the beam walls) and the walls at the sides and back of the shelf. """ if not wall_height: @@ -284,8 +284,8 @@ def make_tray( def _make_joining_walls(self, sides: Literal["full", "open", "w-pattern", "slots", "ramp"]): """ - Make the walls between the front walls and the tral walls. For broad trays, - these are walls behind beams. For thin trays this connects the walls + Make the walls between the front walls and the tray walls. For broad trays, + these are walls behind beams. For thin trays this connects the walls. """ if sides != "open" and self._base_between_beam_walls != "none": @@ -598,8 +598,8 @@ def ziptie_shelf( ): """ Return a ziptie shelf. The height in units must be set. - The other sizes are deterined from the internal tray dimentsions to - make it simple to create a shelf dor a device of known size + The other sizes are determined from the internal tray dimensions to + make it simple to create a shelf for a device of known size. """ if not rack_params: rack_params = nimble_builder.RackParameters() From 69611601e0261631707c95b4fd5995524b4d921d Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 19 May 2024 21:19:07 +0100 Subject: [PATCH 15/16] fix generate.py --- mechanical/assembly_renderer.py | 6 +++--- nimble_orchestration/configuration.py | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mechanical/assembly_renderer.py b/mechanical/assembly_renderer.py index 3ee1bc5..424cc1a 100644 --- a/mechanical/assembly_renderer.py +++ b/mechanical/assembly_renderer.py @@ -22,6 +22,7 @@ import cadquery as cq import yaml +assembly_definition_file = "assembly-def.yaml" class PartDefinition: """ @@ -80,9 +81,8 @@ def generate(self) -> cq.Assembly: # Handle different execution environments, including ExSource-Tools -if "show_object" in globals() or __name__ == "__cqgi__": +if __name__ == "__main__" or __name__ == "__cqgi__" or "show_object" in globals(): # CQGI should execute this whenever called - assembly_definition_file = "assembly-def.yaml" assembly = AssemblyRederer(assembly_definition_file).generate() show_object(assembly) @@ -90,5 +90,5 @@ def generate(self) -> cq.Assembly: # for debugging folder = Path(__file__).resolve().parent.parent os.chdir(folder) - assembly = AssemblyRederer("assembly-def.yaml").generate() + assembly = AssemblyRederer(assembly_definition_file).generate() assembly.save("assembly.stl", "STL") diff --git a/nimble_orchestration/configuration.py b/nimble_orchestration/configuration.py index 6434b6c..5e67b91 100644 --- a/nimble_orchestration/configuration.py +++ b/nimble_orchestration/configuration.py @@ -109,8 +109,7 @@ def _legs(self): ], source_files=[source], parameters={ - "length": beam_height, - "hole_spacing": self._rack_params.mounting_hole_spacing, + "length": beam_height }, application="cadquery" ) From fc3c3a38474f4c081b51a61995f134aa63397851 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Sun, 19 May 2024 21:23:50 +0100 Subject: [PATCH 16/16] Fix assembly configuration so shelfbuilder shelves assemble in correct location --- nimble_orchestration/configuration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nimble_orchestration/configuration.py b/nimble_orchestration/configuration.py index 5e67b91..016ced1 100644 --- a/nimble_orchestration/configuration.py +++ b/nimble_orchestration/configuration.py @@ -203,8 +203,8 @@ def _trays(self): z_offset = self._rack_params.bottom_tray_offet height_in_u = 0 for i, device in enumerate(self._devices): - x_pos = -self._rack_params.tray_width / 2.0 - y_pos = -self._rack_params.rack_width / 2.0 - 4 + x_pos = 0 + y_pos = -self._rack_params.rack_width / 2.0 z_pos = z_offset + height_in_u * self._rack_params.mounting_hole_spacing tray_id = device.tray_id