| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import FreeCAD |
| | import tempfile |
| | import pathlib |
| | import CAMTests.PathTestUtils as PathTestUtils |
| | from Path.Machine.models.machine import ( |
| | Machine, |
| | Spindle, |
| | OutputOptions, |
| | GCodeBlocks, |
| | ProcessingOptions, |
| | MachineFactory, |
| | ) |
| |
|
| |
|
| | class TestMachineDataclass(PathTestUtils.PathTestBase): |
| | """Test the unified Machine dataclass""" |
| |
|
| | def setUp(self): |
| | """Set up test fixtures""" |
| | self.default_machine = Machine() |
| |
|
| | def test_default_initialization(self): |
| | """Test that Machine initializes with sensible defaults""" |
| | machine = Machine() |
| |
|
| | |
| | self.assertEqual(machine.name, "Default Machine") |
| | self.assertEqual(machine.manufacturer, "") |
| | self.assertEqual(machine.description, "") |
| |
|
| | |
| | self.assertEqual(machine.machine_type, "custom") |
| |
|
| | |
| | machine.add_linear_axis("X", FreeCAD.Vector(1, 0, 0)) |
| | machine.add_linear_axis("Y", FreeCAD.Vector(0, 1, 0)) |
| | self.assertEqual(machine.machine_type, "custom") |
| |
|
| | machine.add_linear_axis("Z", FreeCAD.Vector(0, 0, 1)) |
| | self.assertEqual(machine.machine_type, "xyz") |
| |
|
| | |
| | machine.add_rotary_axis("A", FreeCAD.Vector(1, 0, 0), -120, 120) |
| | self.assertEqual(machine.machine_type, "xyza") |
| |
|
| | machine.add_rotary_axis("C", FreeCAD.Vector(0, 0, 1), -360, 360) |
| | self.assertEqual(machine.machine_type, "xyzac") |
| |
|
| | |
| | self.assertEqual(machine.reference_system["X"], FreeCAD.Vector(1, 0, 0)) |
| | self.assertEqual(machine.reference_system["Y"], FreeCAD.Vector(0, 1, 0)) |
| | self.assertEqual(machine.reference_system["Z"], FreeCAD.Vector(0, 0, 1)) |
| | self.assertEqual(machine.tool_axis, FreeCAD.Vector(0, 0, -1)) |
| |
|
| | |
| | self.assertEqual(machine.configuration_units, "metric") |
| | self.assertEqual(machine.version, 1) |
| | self.assertIsNotNone(machine.freecad_version) |
| |
|
| | |
| | self.assertIsInstance(machine.output, OutputOptions) |
| | self.assertIsInstance(machine.blocks, GCodeBlocks) |
| | self.assertIsInstance(machine.processing, ProcessingOptions) |
| |
|
| | def test_custom_initialization(self): |
| | """Test Machine initialization with custom values and verify machine_type is derived""" |
| | |
| | machine = Machine( |
| | name="Test Mill", |
| | manufacturer="ACME Corp", |
| | description="5-axis mill", |
| | configuration_units="imperial", |
| | ) |
| |
|
| | |
| | machine.add_linear_axis("X", FreeCAD.Vector(1, 0, 0)) |
| | machine.add_linear_axis("Y", FreeCAD.Vector(0, 1, 0)) |
| | machine.add_linear_axis("Z", FreeCAD.Vector(0, 0, 1)) |
| | machine.add_rotary_axis("A", FreeCAD.Vector(1, 0, 0), -120, 120) |
| | machine.add_rotary_axis("C", FreeCAD.Vector(0, 0, 1), -360, 360) |
| |
|
| | self.assertEqual(machine.name, "Test Mill") |
| | self.assertEqual(machine.manufacturer, "ACME Corp") |
| | self.assertEqual(machine.description, "5-axis mill") |
| | self.assertEqual(machine.machine_type, "xyzac") |
| | self.assertEqual(machine.configuration_units, "imperial") |
| |
|
| | def test_configuration_units_property(self): |
| | """Test configuration_units property returns correct values""" |
| | metric_machine = Machine(configuration_units="metric") |
| | self.assertEqual(metric_machine.configuration_units, "metric") |
| |
|
| | imperial_machine = Machine(configuration_units="imperial") |
| | self.assertEqual(imperial_machine.configuration_units, "imperial") |
| |
|
| |
|
| | class TestOutputOptions(PathTestUtils.PathTestBase): |
| | """Test OutputOptions dataclass""" |
| |
|
| | def test_default_initialization(self): |
| | """Test OutputOptions initialization with defaults""" |
| | opts = OutputOptions() |
| |
|
| | |
| | self.assertTrue(opts.comments) |
| | self.assertTrue(opts.blank_lines) |
| | self.assertTrue(opts.header) |
| | self.assertFalse(opts.line_numbers) |
| | self.assertFalse(opts.bcnc_blocks) |
| | self.assertFalse(opts.path_labels) |
| | self.assertFalse(opts.machine_name) |
| | self.assertTrue(opts.tool_change) |
| | self.assertTrue(opts.doubles) |
| | self.assertFalse(opts.adaptive) |
| |
|
| | def test_custom_initialization(self): |
| | """Test OutputOptions initialization with custom values""" |
| | opts = OutputOptions( |
| | comments=False, |
| | blank_lines=False, |
| | header=False, |
| | line_numbers=True, |
| | bcnc_blocks=True, |
| | path_labels=True, |
| | machine_name=True, |
| | tool_change=False, |
| | doubles=False, |
| | adaptive=True, |
| | ) |
| |
|
| | |
| | self.assertFalse(opts.comments) |
| | self.assertFalse(opts.blank_lines) |
| | self.assertFalse(opts.header) |
| | self.assertTrue(opts.line_numbers) |
| | self.assertTrue(opts.bcnc_blocks) |
| | self.assertTrue(opts.path_labels) |
| | self.assertTrue(opts.machine_name) |
| | self.assertFalse(opts.tool_change) |
| | self.assertFalse(opts.doubles) |
| | self.assertTrue(opts.adaptive) |
| |
|
| | def test_equality(self): |
| | """Test OutputOptions equality comparison""" |
| | opts1 = OutputOptions() |
| | opts2 = OutputOptions() |
| | self.assertEqual(opts1, opts2) |
| |
|
| | opts2.comments = False |
| | self.assertNotEqual(opts1, opts2) |
| |
|
| |
|
| | class TestSpindle(PathTestUtils.PathTestBase): |
| | """Test Spindle dataclass""" |
| |
|
| | def test_spindle_initialization(self): |
| | """Test Spindle initialization with defaults""" |
| | spindle = Spindle( |
| | name="Main Spindle", |
| | max_power_kw=5.5, |
| | max_rpm=24000, |
| | min_rpm=1000, |
| | tool_change="automatic", |
| | ) |
| |
|
| | self.assertEqual(spindle.name, "Main Spindle") |
| | self.assertEqual(spindle.max_power_kw, 5.5) |
| | self.assertEqual(spindle.max_rpm, 24000) |
| | self.assertEqual(spindle.min_rpm, 1000) |
| | self.assertEqual(spindle.tool_change, "automatic") |
| | |
| | self.assertEqual(spindle.tool_axis, FreeCAD.Vector(0, 0, -1)) |
| |
|
| | def test_spindle_custom_tool_axis(self): |
| | """Test Spindle with custom tool axis""" |
| | spindle = Spindle( |
| | name="Side Spindle", |
| | tool_axis=FreeCAD.Vector(1, 0, 0), |
| | ) |
| |
|
| | self.assertEqual(spindle.tool_axis, FreeCAD.Vector(1, 0, 0)) |
| |
|
| | def test_spindle_serialization(self): |
| | """Test to_dict and from_dict""" |
| | spindle = Spindle( |
| | name="Test Spindle", |
| | id="spindle-001", |
| | max_power_kw=3.0, |
| | max_rpm=18000, |
| | min_rpm=500, |
| | tool_change="manual", |
| | tool_axis=FreeCAD.Vector(0, 1, 0), |
| | ) |
| |
|
| | data = spindle.to_dict() |
| | self.assertEqual(data["name"], "Test Spindle") |
| | self.assertEqual(data["id"], "spindle-001") |
| | self.assertEqual(data["max_power_kw"], 3.0) |
| | self.assertEqual(data["tool_axis"], [0, 1, 0]) |
| |
|
| | restored = Spindle.from_dict(data) |
| | self.assertEqual(restored.name, spindle.name) |
| | self.assertEqual(restored.id, spindle.id) |
| | self.assertEqual(restored.max_power_kw, spindle.max_power_kw) |
| | self.assertEqual(restored.tool_axis, spindle.tool_axis) |
| |
|
| |
|
| | class TestMachineFactory(PathTestUtils.PathTestBase): |
| | """Test MachineFactory class for loading/saving configurations""" |
| |
|
| | def setUp(self): |
| | """Set up test fixtures with temporary directory""" |
| | self.temp_dir = tempfile.mkdtemp() |
| | self.temp_path = pathlib.Path(self.temp_dir) |
| | MachineFactory.set_config_directory(self.temp_dir) |
| |
|
| | def tearDown(self): |
| | """Clean up temporary directory""" |
| | import shutil |
| |
|
| | if self.temp_path.exists(): |
| | shutil.rmtree(self.temp_path) |
| |
|
| | def test_set_and_get_config_directory(self): |
| | """Test setting and getting configuration directory""" |
| | test_dir = self.temp_path / "test_configs" |
| | MachineFactory.set_config_directory(test_dir) |
| |
|
| | config_dir = MachineFactory.get_config_directory() |
| | self.assertEqual(config_dir, test_dir) |
| | self.assertTrue(config_dir.exists()) |
| |
|
| | def test_save_and_load_configuration(self): |
| | """Test saving and loading a machine configuration""" |
| | |
| | machine = Machine( |
| | name="Test Machine", |
| | manufacturer="Test Corp", |
| | description="Test description", |
| | configuration_units="metric", |
| | ) |
| |
|
| | |
| | machine.add_linear_axis("X", FreeCAD.Vector(1, 0, 0)) |
| | machine.add_linear_axis("Y", FreeCAD.Vector(0, 1, 0)) |
| | machine.add_linear_axis("Z", FreeCAD.Vector(0, 0, 1)) |
| |
|
| | |
| | spindle = Spindle( |
| | name="Main Spindle", |
| | max_power_kw=5.5, |
| | max_rpm=24000, |
| | min_rpm=1000, |
| | ) |
| | machine.spindles.append(spindle) |
| |
|
| | |
| | filepath = MachineFactory.save_configuration(machine, "test_machine.fcm") |
| | self.assertTrue(filepath.exists()) |
| |
|
| | |
| | loaded_machine = MachineFactory.load_configuration("test_machine.fcm") |
| |
|
| | |
| | self.assertEqual(loaded_machine.name, "Test Machine") |
| | self.assertEqual(loaded_machine.manufacturer, "Test Corp") |
| | self.assertEqual(loaded_machine.description, "Test description") |
| | self.assertEqual(loaded_machine.machine_type, "xyz") |
| | self.assertEqual(loaded_machine.configuration_units, "metric") |
| | self.assertEqual(len(loaded_machine.spindles), 1) |
| | self.assertEqual(loaded_machine.spindles[0].name, "Main Spindle") |
| |
|
| | def test_save_configuration_auto_filename(self): |
| | """Test saving with automatic filename generation""" |
| | machine = Machine(name="My Test Machine") |
| |
|
| | filepath = MachineFactory.save_configuration(machine) |
| |
|
| | |
| | self.assertTrue(filepath.exists()) |
| | self.assertEqual(filepath.name, "My_Test_Machine.fcm") |
| |
|
| | def test_load_nonexistent_file(self): |
| | """Test loading a file that doesn't exist""" |
| | with self.assertRaises(FileNotFoundError): |
| | MachineFactory.load_configuration("nonexistent.fcm") |
| |
|
| | def test_create_default_machine_data(self): |
| | """Test creating default machine data dictionary""" |
| | data = MachineFactory.create_default_machine_data() |
| |
|
| | self.assertIsInstance(data, dict) |
| | |
| | self.assertIn("machine", data) |
| | self.assertEqual(data["machine"]["name"], "New Machine") |
| | self.assertIn("spindles", data["machine"]) |
| |
|
| | def test_list_configuration_files(self): |
| | """Test listing available configuration files""" |
| | |
| | machine1 = Machine(name="Machine 1") |
| | machine2 = Machine(name="Machine 2") |
| |
|
| | MachineFactory.save_configuration(machine1, "machine1.fcm") |
| | MachineFactory.save_configuration(machine2, "machine2.fcm") |
| |
|
| | |
| | configs = MachineFactory.list_configuration_files() |
| |
|
| | |
| | self.assertGreaterEqual(len(configs), 3) |
| | self.assertEqual(configs[0][0], "<any>") |
| |
|
| | |
| | names = [name for name, path in configs] |
| | self.assertIn("Machine 1", names) |
| | self.assertIn("Machine 2", names) |
| |
|
| | def test_list_configurations(self): |
| | """Test listing configuration names""" |
| | machine = Machine(name="Test Machine") |
| | MachineFactory.save_configuration(machine, "test.fcm") |
| |
|
| | configs = MachineFactory.list_configurations() |
| |
|
| | self.assertIsInstance(configs, list) |
| | self.assertIn("<any>", configs) |
| | |
| | self.assertIn("Test Machine", configs) |
| |
|
| | def test_delete_configuration(self): |
| | """Test deleting a configuration file""" |
| | machine = Machine(name="To Delete") |
| | filepath = MachineFactory.save_configuration(machine, "delete_me.fcm") |
| |
|
| | self.assertTrue(filepath.exists()) |
| |
|
| | |
| | result = MachineFactory.delete_configuration("delete_me.fcm") |
| | self.assertTrue(result) |
| | self.assertFalse(filepath.exists()) |
| |
|
| | |
| | result = MachineFactory.delete_configuration("delete_me.fcm") |
| | self.assertFalse(result) |
| |
|
| | def test_get_builtin_config(self): |
| | """Test getting built-in machine configurations""" |
| | |
| | config_types = ["XYZ", "XYZAC", "XYZBC", "XYZA", "XYZB"] |
| |
|
| | for config_type in config_types: |
| | machine = MachineFactory.get_builtin_config(config_type) |
| | self.assertIsInstance(machine, Machine) |
| | self.assertIsNotNone(machine.name) |
| |
|
| | def test_get_builtin_config_invalid_type(self): |
| | """Test getting built-in config with invalid type""" |
| | with self.assertRaises(ValueError): |
| | MachineFactory.get_builtin_config("INVALID") |
| |
|
| | def test_serialization_roundtrip(self): |
| | """Test full serialization roundtrip with complex machine""" |
| | |
| | machine = Machine( |
| | name="Complex Machine", |
| | manufacturer="Test Mfg", |
| | description="Full featured machine", |
| | configuration_units="metric", |
| | ) |
| |
|
| | |
| | machine.spindles.append( |
| | Spindle( |
| | name="Main", |
| | max_power_kw=7.5, |
| | max_rpm=30000, |
| | ) |
| | ) |
| |
|
| | |
| | machine.output.output_comments = False |
| | machine.output.axis_precision = 4 |
| | machine.output.line_increment = 5 |
| |
|
| | |
| |
|
| | |
| | filepath = MachineFactory.save_configuration(machine, "complex.fcm") |
| | loaded = MachineFactory.load_configuration(filepath) |
| |
|
| | |
| | self.assertEqual(loaded.name, machine.name) |
| | self.assertEqual(loaded.manufacturer, machine.manufacturer) |
| | self.assertEqual(len(loaded.spindles), 1) |
| | self.assertFalse(loaded.output.output_comments) |
| | self.assertEqual(loaded.output.axis_precision, 4) |
| | self.assertEqual(loaded.output.line_increment, 5) |
| |
|