Initial implementation of instruction set
This commit is contained in:
parent
e2bb39006a
commit
63fd73f98c
17
bin/app.php
17
bin/app.php
@ -1,16 +1,15 @@
|
||||
<?php
|
||||
|
||||
use Automata\Machine;
|
||||
use Automata\NC;
|
||||
use Automata\ToolData;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$machine = new Machine();
|
||||
$machine->TM->write(new ToolData(1, 5500, 2));
|
||||
$machine->TM->write(new ToolData(2, 3000, 61379));
|
||||
(new ProgramLoader)->loadPrograms($machine);
|
||||
$nc = new NC();
|
||||
$nc->TM->write(new ToolData(1, 5500, 2));
|
||||
$nc->TM->write(new ToolData(2, 3000, 61379));
|
||||
(new ProgramLoader)->loadPrograms($nc);
|
||||
|
||||
$nc->loadProgram(9002);
|
||||
$nc->run();
|
||||
|
||||
$machine->loadProgram(9002);
|
||||
while ($machine->PPMC->ready()) {
|
||||
$machine->step();
|
||||
}
|
||||
|
@ -16,15 +16,19 @@ class DataRegisters
|
||||
public int $S = 0; // Spindle speed in RPM
|
||||
public int $T = 0; // Tool number
|
||||
public int $H = 0; // Additional functions (unused)
|
||||
public int $M = 0; // Auxiliary functions
|
||||
public int $E = 0; // Number of repetitions / multiple meanings
|
||||
// Modals
|
||||
public int $TM = 0; // Traverse (0, 1, 2, 3)
|
||||
public int $PS = 18; // Plane Selection (18, 19, 20)
|
||||
public int $RC = 40; // Radius Compensation
|
||||
public int $DM = 90; // Dimension (90, 91)
|
||||
public int $WC = 0; // Work Cycle (null, 81, 84, 85, 86)
|
||||
public int $CM = 8; // Coolant (8, 9)
|
||||
public int $SM = 5; // Spindle (3, 4, 5)
|
||||
public int $TC = 10; // Table Clamping (10, 11)
|
||||
public int $G_TRAVERSE = 0;
|
||||
public int $G_PLANE_SELECTION = 18;
|
||||
public int $G_RADIUS_COMPENSATION = 40;
|
||||
public int $G_DIMENSION_PROGRAMMING = 90;
|
||||
public int $G_WORKCYCLE = 0;
|
||||
public int $X_WORKCYCLE = 0;
|
||||
public int $Y_WORKCYCLE = 0;
|
||||
public int $Z_WORKCYCLE = 0;
|
||||
public int $B_WORKCYCLE = 0;
|
||||
public int $M_PROGRAM = 0;
|
||||
public int $M_COOLANT = 8;
|
||||
public int $M_SPINDLE = 5;
|
||||
public int $M_TABLE_CLAMPING = 10;
|
||||
}
|
||||
|
@ -2,41 +2,330 @@
|
||||
|
||||
namespace Automata;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Program\Block;
|
||||
use RuntimeException;
|
||||
|
||||
class InstructionSet
|
||||
{
|
||||
private Machine $machine;
|
||||
/** @var callable<Machine>[] */
|
||||
private array $map;
|
||||
private const SPEEDS = [0, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000];
|
||||
|
||||
private NC $machine;
|
||||
|
||||
|
||||
public function __construct(Machine $machine)
|
||||
public function __construct(NC $machine)
|
||||
{
|
||||
$this->machine = $machine;
|
||||
$this->map = [];
|
||||
}
|
||||
|
||||
|
||||
public function apply(Block $block): void
|
||||
{
|
||||
print("$block\n");
|
||||
$words = $block->words;
|
||||
while ($word = array_shift($words)) {
|
||||
$method = "{$word}";
|
||||
if (!method_exists($this, $method)) {
|
||||
continue;
|
||||
system('clear');
|
||||
print("\n$block\n\n");
|
||||
foreach ($block->words as $word) {
|
||||
if (method_exists($this, "$word")) {
|
||||
// There's a method dedicated to handling the full word (eg G22)
|
||||
$this->{"$word"}($block);
|
||||
@$block->pop($word->register);
|
||||
}
|
||||
if (method_exists($this, $word->register)) {
|
||||
// There's a method dedicated to handling data for the given register (eg T, S)
|
||||
$this->{$word->register}($block->pop($word->register));
|
||||
}
|
||||
$this->{$method}($block);
|
||||
break;
|
||||
}
|
||||
foreach ($block->words as $word) {
|
||||
// The word value may be put into the machine registers directly
|
||||
if (property_exists($this->machine->DR, $word->register)) {
|
||||
$this->machine->DR->{$word->register} = $block->pop($word->register);
|
||||
} else {
|
||||
print("WARNING: No method or register for word '{$word}'\n");
|
||||
}
|
||||
}
|
||||
dump($this->machine->DR);
|
||||
readline();
|
||||
}
|
||||
|
||||
|
||||
protected function E(int $value): void
|
||||
{
|
||||
print("WARNING: Line repetitions are not implemented yet");
|
||||
}
|
||||
|
||||
|
||||
protected function T(int $value): void
|
||||
{
|
||||
// Triggers an exception if not found, good enough for now.
|
||||
// Might implement compensations and more explicit tool registers later on
|
||||
$this->machine->TM->read($value);
|
||||
$this->machine->DR->T = $value;
|
||||
}
|
||||
|
||||
|
||||
protected function S(int $value): void
|
||||
{
|
||||
if (!in_array($value, self::SPEEDS)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
"Speed not supported: '$value'. Supported are: \n%s\n",
|
||||
implode(', ', self::SPEEDS)
|
||||
));
|
||||
}
|
||||
$this->machine->DR->S = $value;
|
||||
}
|
||||
|
||||
|
||||
protected function G0(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_TRAVERSE = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G1(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_TRAVERSE = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G2(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_TRAVERSE = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G3(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_TRAVERSE = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G4(Block $block): void
|
||||
{
|
||||
print("Ignoring dwell setting: {$block->pop('X')}\n");
|
||||
}
|
||||
|
||||
|
||||
protected function G17(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_PLANE_SELECTION = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G18(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_PLANE_SELECTION = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G19(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_PLANE_SELECTION = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G22(Block $block): void
|
||||
{
|
||||
$number = $block->valueOf('X');
|
||||
$this->machine->SPMC->loadProgram($number);
|
||||
$N = $block->pop('X');
|
||||
$this->machine->SPMC->loadProgram($N);
|
||||
$this->machine->AMC = $this->machine->SPMC;
|
||||
}
|
||||
|
||||
|
||||
protected function G40(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_RADIUS_COMPENSATION = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G41(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_RADIUS_COMPENSATION = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G42(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_RADIUS_COMPENSATION = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G43(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_RADIUS_COMPENSATION = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G44(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_RADIUS_COMPENSATION = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G79(Block $block): void
|
||||
{
|
||||
// Ignore... ?? The action will be executed on set location
|
||||
if ($this->machine->DR->B_WORKCYCLE === 0) {
|
||||
throw new RuntimeException('No workcycle prepared!');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function G81(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_WORKCYCLE = $block->read('G');
|
||||
$this->getWorkcycleParameters($block);
|
||||
}
|
||||
|
||||
|
||||
protected function G84(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_WORKCYCLE = $block->read('G');
|
||||
$this->getWorkcycleParameters($block);
|
||||
}
|
||||
|
||||
|
||||
protected function G85(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_WORKCYCLE = $block->read('G');
|
||||
$this->getWorkcycleParameters($block);
|
||||
}
|
||||
|
||||
|
||||
protected function G86(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_WORKCYCLE = $block->read('G');
|
||||
$this->getWorkcycleParameters($block);
|
||||
}
|
||||
|
||||
|
||||
private function getWorkcycleParameters(Block $block): void
|
||||
{
|
||||
$this->machine->DR->X_WORKCYCLE = $block->has('X') ? $block->pop('X') : 0;
|
||||
$this->machine->DR->Y_WORKCYCLE = $block->has('Y') ? $block->pop('Y') : 0;
|
||||
$this->machine->DR->Z_WORKCYCLE = $block->has('Z') ? $block->pop('Z') : 0;
|
||||
$this->machine->DR->B_WORKCYCLE = $block->has('B') ? $block->pop('B') : 0;
|
||||
}
|
||||
|
||||
|
||||
protected function G90(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_DIMENSION_PROGRAMMING = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G91(Block $block): void
|
||||
{
|
||||
$this->machine->DR->G_DIMENSION_PROGRAMMING = $block->read('G');
|
||||
}
|
||||
|
||||
|
||||
protected function G92(Block $block): void
|
||||
{
|
||||
// Incremental
|
||||
foreach (['X', 'Y', 'Z'] as $R) {
|
||||
if ($block->has($R)) {
|
||||
$this->machine->DR->{$R} = -$block->pop($R);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function G93(Block $block): void
|
||||
{
|
||||
// Absolute zero datum shift
|
||||
foreach (['X', 'Y', 'Z'] as $R) {
|
||||
if ($block->has($R)) {
|
||||
$this->machine->DR->{$R} -= $block->pop($R);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function G98(Block $block): void
|
||||
{
|
||||
// Automatic positioning to reference datum point
|
||||
print("Ignoring automatic positioning to zero datum point\n");
|
||||
}
|
||||
|
||||
|
||||
protected function M0(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_PROGRAM = 0;
|
||||
$this->machine->DR->M_SPINDLE = 5;
|
||||
$this->machine->DR->M_COOLANT = 9;
|
||||
}
|
||||
|
||||
|
||||
protected function M2(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_PROGRAM = 2;
|
||||
$this->machine->DR->M_SPINDLE = 5;
|
||||
$this->machine->DR->M_COOLANT = 9;
|
||||
}
|
||||
|
||||
|
||||
protected function M3(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_SPINDLE = 3;
|
||||
}
|
||||
|
||||
|
||||
protected function M4(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_SPINDLE = 4;
|
||||
}
|
||||
|
||||
|
||||
protected function M5(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_SPINDLE = 5;
|
||||
}
|
||||
|
||||
|
||||
protected function M6(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_SPINDLE = 5;
|
||||
$this->machine->DR->M_COOLANT = 9;
|
||||
}
|
||||
|
||||
|
||||
protected function M8(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_COOLANT = 8;
|
||||
}
|
||||
|
||||
|
||||
protected function M9(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_COOLANT = 9;
|
||||
}
|
||||
|
||||
|
||||
protected function M10(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_TABLE_CLAMPING = 10;
|
||||
}
|
||||
|
||||
|
||||
protected function M11(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_TABLE_CLAMPING = 11;
|
||||
}
|
||||
|
||||
|
||||
protected function M30(Block $block): void
|
||||
{
|
||||
$this->machine->DR->M_SPINDLE = 5;
|
||||
$this->machine->DR->M_COOLANT = 9;
|
||||
$N = $this->machine->PPMC->N;
|
||||
$this->machine->PPMC->reset();
|
||||
$this->machine->SPMC->reset();
|
||||
$this->machine->PPMC->loadProgram($N);
|
||||
$this->machine->AMC = $this->machine->PPMC;
|
||||
}
|
||||
|
||||
|
||||
protected function M67(Block $block): void
|
||||
{
|
||||
throw new RuntimeException('[Enter new tool information without machine stop] is not implemented');
|
||||
}
|
||||
}
|
||||
|
@ -10,14 +10,16 @@ use RuntimeException;
|
||||
class MemoryController implements Countable
|
||||
{
|
||||
private ProgramMemory $memory;
|
||||
public ?int $N = null;
|
||||
/** @var Block[]|null */
|
||||
private ?array $blocks;
|
||||
private ?array $blockBuffer;
|
||||
private int $index;
|
||||
|
||||
|
||||
public function __construct(ProgramMemory $memory)
|
||||
{
|
||||
$this->memory = $memory;
|
||||
$this->blocks = null;
|
||||
$this->blockBuffer = null;
|
||||
}
|
||||
|
||||
|
||||
@ -27,39 +29,53 @@ class MemoryController implements Countable
|
||||
if (!$program) {
|
||||
throw new InvalidArgumentException("Program N{$N} not found");
|
||||
}
|
||||
$this->blocks = $program->getIterator()->getArrayCopy();
|
||||
$this->N = $N;
|
||||
$this->blockBuffer = $program->getIterator()->getArrayCopy();
|
||||
$this->index = 0;
|
||||
}
|
||||
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->blocks = null;
|
||||
$this->N = null;
|
||||
$this->blockBuffer = null;
|
||||
$this->index = 0;
|
||||
}
|
||||
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->blocks);
|
||||
return count($this->blockBuffer) - $this->index;
|
||||
}
|
||||
|
||||
|
||||
public function previous(): ?Block
|
||||
{
|
||||
$this->guardNoProgramLoaded();
|
||||
$block = $this->blockBuffer[$this->index--];
|
||||
|
||||
return $block ? clone $block : null;
|
||||
}
|
||||
|
||||
|
||||
public function next(): ?Block
|
||||
{
|
||||
$this->guardNoProgramLoaded();
|
||||
$block = $this->blockBuffer[$this->index++];
|
||||
|
||||
return array_shift($this->blocks);
|
||||
return $block ? clone $block : null;
|
||||
}
|
||||
|
||||
|
||||
public function ready(): bool
|
||||
{
|
||||
return !empty($this->blocks);
|
||||
return $this->count() > 0;
|
||||
}
|
||||
|
||||
|
||||
private function guardNoProgramLoaded(): void
|
||||
{
|
||||
if (is_null($this->blocks)) {
|
||||
if (is_null($this->blockBuffer)) {
|
||||
throw new RuntimeException('No program loaded in memory');
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\G;
|
||||
|
||||
class DimensionMode extends G
|
||||
{
|
||||
public function supports(int $G): bool
|
||||
{
|
||||
return in_array($G, [90, 91]);
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\G;
|
||||
|
||||
abstract class G
|
||||
{
|
||||
public int $G;
|
||||
|
||||
public function __construct(int $G)
|
||||
{
|
||||
$this->G = $G;
|
||||
}
|
||||
|
||||
|
||||
public final function apply(int $G): void
|
||||
{
|
||||
if ($this->supports($G)) {
|
||||
$this->G = $G;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract function supports(int $G): bool;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\G;
|
||||
|
||||
class PlaneSelection extends G
|
||||
{
|
||||
public function supports(int $G): bool
|
||||
{
|
||||
return in_array($G, [17, 18, 19]);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\G;
|
||||
|
||||
class RadiusCompensation extends G
|
||||
{
|
||||
public function supports(int $G): bool
|
||||
{
|
||||
return in_array($G, [40, 41, 42, 43, 44]);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\G;
|
||||
|
||||
class TraverseMode extends G
|
||||
{
|
||||
public function supports(int $G): bool
|
||||
{
|
||||
return in_array($G, [0, 1, 2, 3]);
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\G;
|
||||
|
||||
class WorkCycle extends G
|
||||
{
|
||||
public int $X = 0;
|
||||
public int $Y = 0;
|
||||
public int $Z = 0;
|
||||
public int $B = 0;
|
||||
|
||||
|
||||
public function supports(int $G): bool
|
||||
{
|
||||
return in_array($G, [81, 84, 85, 86]);
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\M;
|
||||
|
||||
class CoolantMode extends M
|
||||
{
|
||||
|
||||
public function supports(int $M): bool
|
||||
{
|
||||
return in_array($M, [0, 2, 6, 8, 9, 30]);
|
||||
}
|
||||
|
||||
|
||||
public function apply(int $M): void
|
||||
{
|
||||
if (!$this->supports($M)) {
|
||||
return;
|
||||
}
|
||||
if (in_array($M, [0, 2, 6, 30])) {
|
||||
$this->M = 9;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 8 or 9
|
||||
$this->M = $M;
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\M;
|
||||
|
||||
abstract class M
|
||||
{
|
||||
public int $M;
|
||||
|
||||
public function __construct(int $M)
|
||||
{
|
||||
$this->M = $M;
|
||||
}
|
||||
|
||||
|
||||
public function apply(int $M): void
|
||||
{
|
||||
if ($this->supports($M)) {
|
||||
$this->M = $M;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract function supports(int $M): bool;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\M;
|
||||
|
||||
class SpindleMode extends M
|
||||
{
|
||||
public function supports(int $M): bool
|
||||
{
|
||||
return in_array($M, [0, 2, 3, 4, 5, 6, 30]);
|
||||
}
|
||||
|
||||
|
||||
public function apply(int $M): void
|
||||
{
|
||||
if (!$this->supports($M)) {
|
||||
return;
|
||||
}
|
||||
if (in_array($M, [0, 2, 6, 30])) {
|
||||
$this->M = 5;
|
||||
|
||||
return;
|
||||
}
|
||||
// 3, 4 or 5
|
||||
$this->M = $M;
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Automata\Modes\M;
|
||||
|
||||
class TableClampingMode extends M
|
||||
{
|
||||
public function supports(int $M): bool
|
||||
{
|
||||
return in_array($M, [10, 11]);
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ use Automata\Memory\MemoryController;
|
||||
use Automata\Memory\ProgramMemory;
|
||||
use Automata\Memory\ToolMemory;
|
||||
|
||||
class Machine
|
||||
class NC
|
||||
{
|
||||
public DataRegisters $DR;
|
||||
public InstructionSet $IS;
|
||||
@ -15,7 +15,7 @@ class Machine
|
||||
public ProgramMemory $PPM;
|
||||
public MemoryController $SPMC;
|
||||
public MemoryController $PPMC;
|
||||
public ?MemoryController $AMC;
|
||||
public MemoryController $AMC;
|
||||
|
||||
|
||||
public function __construct()
|
||||
@ -48,6 +48,14 @@ class Machine
|
||||
}
|
||||
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
while ($this->PPMC->ready()) {
|
||||
$this->step();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function reset(): void
|
||||
{
|
||||
$this->PPMC->reset();
|
@ -24,10 +24,36 @@ class Block
|
||||
}
|
||||
|
||||
|
||||
public function valueOf(string $register): int
|
||||
public function has(string $register): bool
|
||||
{
|
||||
foreach ($this->words as $word) {
|
||||
if ($word->register === $register) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public function read(string $register): int
|
||||
{
|
||||
foreach ($this->words as $index => $word) {
|
||||
if ($word->register === $register) {
|
||||
return $word->value;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("No word in block for register '$register'");
|
||||
}
|
||||
|
||||
|
||||
public function pop(string $register): int
|
||||
{
|
||||
foreach ($this->words as $index => $word) {
|
||||
if ($word->register === $register) {
|
||||
unset($this->words[$index]);
|
||||
|
||||
return $word->value;
|
||||
}
|
||||
}
|
||||
@ -38,7 +64,7 @@ class Block
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$buffer = "$this->N\t";
|
||||
$buffer = "N$this->N\t\t";
|
||||
foreach ($this->words as $word) {
|
||||
$buffer .= "$word\t";
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Automata\Machine;
|
||||
use Automata\NC;
|
||||
use Program\ProgramParser;
|
||||
|
||||
class ProgramLoader
|
||||
{
|
||||
public function loadPrograms(Machine $machine): void
|
||||
public function loadPrograms(NC $machine): void
|
||||
{
|
||||
$parser = new ProgramParser();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user