Commit 57d67bb2 authored by Fabrice Salvaire's avatar Fabrice Salvaire

improved machine

parent 95ff85c3
####################################################################################################
#
# PythonicGcodeMachine - A Python G-code Toolkit
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################
"""Module to implement a machine coordinate.
"""
####################################################################################################
__all__ = [
'Coordinate',
]
####################################################################################################
import numpy as np
####################################################################################################
class Coordinate:
##############################################
def __init__(self, *args, dimension=None):
if args:
self._v = numpy.array(args)
else:
self._v = numpy.zeros(dimension)
##############################################
def clone(self):
return self.__class__(self._v)
##############################################
@property
def dimension(self):
return self._v.shape[0]
##############################################
def __len__(self):
return self.dimension
def __iter__(self):
return iter(self._v)
def __getitem__(self, slice_):
return self._v[slice_]
##############################################
def set(self, v):
if isintance(self, Coordinate):
self._v = self._v
else:
self._v = v
##############################################
def __eq__(self, v):
return self._v == self._v
##############################################
def __iadd__(self, v):
self._v += self._v
return self
##############################################
def __isub__(self, v):
self._v -= self._v
return self
####################################################################################################
#
# PythonicGcodeMachine - A Python G-code Toolkit
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################
"""Module to implement a basic G-code machine.
"""
####################################################################################################
__all__ = [
'MachineState',
]
####################################################################################################
from enum import Enum, auto
from .Coordinate import Coordinate
from .ToolSet import ToolSet
####################################################################################################
class PlaneSelection:
XY = auto()
XZ = auto()
YZ = auto()
class FeedRateMode:
UNITS_PER_MINUTE = auto()
INVERSE_TIME = auto()
####################################################################################################
class MachineState:
NUMBER_OF_COORDINATE_SYSTEMS = 9
##############################################
def __init__(self,
number_of_axes,
is_metric=True,
):
self._number_of_axes = int(number_of_axes)
self._coordinate = Coordinate(dimension=self._number_of_axes)
self._tool_set = ToolSet()
self._tool = None # T
self._is_metric = bool(is_metric)
self._use_metric = self._is_metric # G20 inch / G21 mm
self._use_absolut = True # G90 absolut / G91 incremental
# G17 XY-plane selection
# G18 XZ-plane selection
# G19 YZ-plane selection
self._plane = None
# G54 G55 G56 G57 G58 G59 G59.1 G59.2 G59.3
self._coordinate_system = None
self._feed_rate = 0 # F
# G93 units per minute
# G94 inverse time
self._feed_rate_mode = FeedRateMode.UNITS_PER_MINUTE
self._spindle_rate = 0 # S
### 4 M0 M1 M2 M30 M60 stopping
### 6 M6 tool change
### 7 M3 M4 M5 spindle turning
### 8 M7 M8 M9 coolant (special case: M7 and M8 may be active at the same time)
### 9 M48 M49 enable/disable feed and speed override switches
### 10 G98 G99 return mode in canned cycles
### 13 G61 G61.1 G64 path control mode
##############################################
@property
def number_of_axes(self):
return self._number_of_axes
@property
def coordinate(self):
return self._coordinate
##############################################
@property
def is_metric(self):
return self._is_metric
@property
def use_metric(self):
return self._use_metric
@use_metric.setter
def use_metric(self, value):
self._use_metric = bool(value)
##############################################
@property
def use_absolut(self):
return self._use_absolut
@use_absolut.setter
def use_absolut(self, value):
self._use_absolut = bool(value)
##############################################
@property
def plane(self):
return self._plane
@plane.setter
def plane(self, value):
self._plane = PlaneSelection(value)
##############################################
@property
def coordinate_system(self):
return self._coordinate_system
@coordinate_system.setter
def coordinate_system(self, value):
_value = int(value)
if not(1 <= _value <= self.NUMBER_OF_COORDINATE_SYSTEMS):
raise ValueError('Invalid coordinate system {}'.format(value))
self._coordinate_system = _value
##############################################
@property
def tool_set(self):
return self._tool_set
def load_tool(self, pocket):
"""Load the tool at the given carousel pocket.
Raise ValueError if KeyError.
"""
self._tool.toggle_loaded()
try:
self._tool = self._tool_set[pocket].toggle_loaded()
except KeyError:
raise ValueError('Invalid carousel pocket {}'.format(pocket))
##############################################
@property
def feed_rate(self):
return self._feed_rate
@feed_rate.setter
def feed_rate(self, value):
# negative feedback ?
self._feed_rate = float(value)
@property
def feed_rate_mode(self):
return self._feed_rate_mode
@feed_rate_mode.setter
def feed_rate_mode(self, value):
self._feed_rate_mode = FeedRateMode(value)
##############################################
@property
def spindle_rate(self):
return self._spindle_rate
@spindle_rate.setter
def spindle_rate(self, value):
_value = float(value)
if _value < 0:
raise ValueError('Negative spindle rate {}'.format(value))
self._spindle_rate = _value
####################################################################################################
#
# PythonicGcodeMachine - A Python G-code Toolkit
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################
"""Module to implement a basic tool set.
"""
####################################################################################################
__all__ = [
'Tool',
'LatheTool',
'ToolSet',
]
####################################################################################################
import yaml
####################################################################################################
class Tool:
"""Class to define a tool"""
##############################################
def __init__(self, tool_id, offset, diameter=None, comment=None):
self._id = tool_id
self._tool_offset = offset
self._tool_diameter = diameter
self._comment = comment
self._tool_set = None
self._pocket = None
self._loaded = False
##############################################
@property
def id(self):
return self._id
@tool_id.setter
def id(self, value):
self._id = str(value)
@property
def offset(self):
return self._offset
@offset.setter
def offset(self, value):
self._offset = value
@property
def diameter(self):
return self._diameter
@diameter.setter
def diameter(self, value):
self._diameter = float(value)
@property
def comment(self):
return self._comment
@comment.setter
def comment(self, value):
self._comment = str(value)
##############################################
@property
def tool_set(self):
return self._tool_set
@tool_set.setter
def tool_set(self, value):
self._tool_set = value
@property
def pocket(self):
return self._pocket
@pocket.setter
def pocket(self, value):
self._pocket = int(value)
##############################################
@property
def loaded(self):
return self._loaded
@loaded.setter
def loaded(self, value):
self._loaded = bool(value)
def toggle_loaded(self):
self._loaded = not self._loaded
return self
##############################################
def _to_dict(self, d, keys):
for key in keys:
d[key] = geattr(self, key)
##############################################
def to_dict(self):
keys = (
'id',
'tool_offset',
'tool_diameter',
'comment',
'pocket',
)
return self._to_dict({}, keys)
####################################################################################################
class LatheTool(Tool):
"""Class to define a lathe tool"""
##############################################
def __init__(self, tool_id, offset, front_angle, back_angle, orientation,
diameter=None, comment=None):
super(). __init__(tool_id, offset, diameter, comment)
self._front_angle = front_angle
self._back_angle = back_angle
self._orientation = orientation
##############################################
@property
def front_angle(self):
return self._front_angle
@front_angle.setter
def front_angle(self, value):
self._front_angle = float(value)
@property
def back_angle(self):
return self._back_angle
@back_angle.setter
def back_angle(self, value):
self._back_angle = float(value)
@property
def orientation(self):
return self._orientation
@orientation.setter
def orientation(self, value):
self._orientation = int(value)
##############################################
def to_dict(self):
d = super().to_dict()
keys = (
'front_angle',
'back_angle',
'orientation',
)
self._to_dict(d, keys)
return d
####################################################################################################
class ToolSet:
"""Class to define a tool set"""
##############################################
def __init__(self):
self._tools = {} # Free pocket implementation
##############################################
def __len__(self):
return len(self._tools)
def __iter__(self):
return iter(self._tools.values())
def __getitem__(self, pocket):
return self._tools[pocket]
##############################################
def remove_tool(self, pocket):
if isinstance(pocket, Tool):
pocket = pocket.pocket
tool = self._tools.pop(pocket)
tool.tool_set = None
tool.pocket = None
return tool
##############################################
def add_tool(self, tool, pocket):
old_tool = self.remove_tool(pocket)
self._tools[pocket] = tool
tool.tool_set = self
tool.pocket = pocket
return old_tool
##############################################
def load_yaml(self, path):
with (open(path, 'r')) as fh:
yaml_data = yaml.load(fh.read())
for pocket, d in yaml_data.items():
if 'front_angle' in d:
cls = LatheTool
else:
cls = Tool
tool = cls(*d)
self.add_tool(tool, pocket)
##############################################
def write_yaml(self, path):
data = {}
for tool in self:
d = tool.to_dict()
del d['pocket']
data[tool.pocket] = d
yaml_data = yaml.dump(data, default_flow_style=False)
with (open(path, 'w')) as fh:
fh.write(yaml_data)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment