From 597427d785d6f44348fe15631f2c184504195fb0 Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Mon, 6 Oct 2014 08:33:53 -0400 Subject: add excellon statements --- gerber/excellon_statements.py | 266 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 gerber/excellon_statements.py (limited to 'gerber/excellon_statements.py') diff --git a/gerber/excellon_statements.py b/gerber/excellon_statements.py new file mode 100644 index 0000000..4544f08 --- /dev/null +++ b/gerber/excellon_statements.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# copyright 2014 Hamilton Kibbe +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .utils import write_gerber_value + + +__all__ = ['ExcellonTool', 'CommentStmt', 'HeaderBeginStmt', 'HeaderEndStmt', + ] + + +class ExcellonStatement(object): + """ Excellon Statement abstract base class + """ + def to_excellon(self): + pass + + +class ExcellonTool(ExcellonStatement): + """ Excellon Tool class + + Parameters + ---------- + settings : FileSettings (dict-like) + File-wide settings. + + kwargs : dict-like + Tool settings from the excellon statement. Valid keys are: + diameter : Tool diameter [expressed in file units] + rpm : Tool RPM + feed_rate : Z-axis tool feed rate + retract_rate : Z-axis tool retraction rate + max_hit_count : Number of hits allowed before a tool change + depth_offset : Offset of tool depth from tip of tool. + + Attributes + ---------- + number : integer + Tool number from the excellon file + + diameter : float + Tool diameter in file units + + rpm : float + Tool RPM + + feed_rate : float + Tool Z-axis feed rate. + + retract_rate : float + Tool Z-axis retract rate + + depth_offset : float + Offset of depth measurement from tip of tool + + max_hit_count : integer + Maximum number of tool hits allowed before a tool change + + hit_count : integer + Number of tool hits in excellon file. + """ + + @classmethod + def from_line(cls, line, settings): + """ Create a Tool from an excellon file tool definition line. + + Parameters + ---------- + line : string + Tool definition line from an excellon file. + + settings : FileSettings (dict-like) + Excellon file-wide settings + + Returns + ------- + tool : Tool + An ExcellonTool representing the tool defined in `line` + """ + commands = re.split('([BCFHSTZ])', line)[1:] + commands = [(command, value) for command, value in pairwise(commands)] + args = {} + format = settings['format'] + zero_suppression = settings['zero_suppression'] + for cmd, val in commands: + if cmd == 'B': + args['retract_rate'] = parse_gerber_value(val, format, zero_suppression) + elif cmd == 'C': + args['diameter'] = parse_gerber_value(val, format, zero_suppression) + elif cmd == 'F': + args['feed_rate'] = parse_gerber_value(val, format, zero_suppression) + elif cmd == 'H': + args['max_hit_count'] = parse_gerber_value(val, format, zero_suppression) + elif cmd == 'S': + args['rpm'] = 1000 * parse_gerber_value(val, format, zero_suppression) + elif cmd == 'T': + args['number'] = int(val) + elif cmd == 'Z': + args['depth_offset'] = parse_gerber_value(val, format, zero_suppression) + return cls(settings, **args) + + def from_dict(cls, settings, tool_dict): + return cls(settings, tool_dict) + + def __init__(self, settings, **kwargs): + self.settings = settings + self.number = kwargs.get('number') + self.feed_rate = kwargs.get('feed_rate') + self.retract_rate = kwargs.get('retract_rate') + self.rpm = kwargs.get('rpm') + self.diameter = kwargs.get('diameter') + self.max_hit_count = kwargs.get('max_hit_count') + self.depth_offset = kwargs.get('depth_offset') + self.hit_count = 0 + + def to_excellon(self): + fmt = self.settings['format'] + zs = self.settings['zero_suppression'] + stmt = 'T%d' % self.number + if self.retract_rate: + stmt += 'B%s' % write_gerber_value(self.retract_rate, fmt, zs) + if self.diameter: + stmt += 'C%s' % write_gerber_value(self.diameter, fmt, zs) + if self.feed_rate: + stmt += 'F%s' % write_gerber_value(self.feed_rate, fmt, zs) + if self.max_hit_count: + stmt += 'H%s' % write_gerber_value(self.max_hit_count, fmt, zs) + if self.rpm: + if self.rpm < 100000.: + stmt += 'S%s' % write_gerber_value(self.rpm / 1000., fmt, zs) + else: + stmt += 'S%g' % self.rpm / 1000. + if self.depth_offset: + stmt += 'Z%s' % write_gerber_value(self.depth_offset, fmt, zs) + return stmt + + def _hit(self): + self.hit_count += 1 + + def __repr__(self): + unit = 'in.' if self.settings.units == 'inch' else 'mm' + return '' % (self.number, self.diameter, unit) + + +class CommentStmt(ExcellonStatement): + def __init__(self, comment): + self.comment = comment + + def to_excellon(self): + return ';%s' % comment + + +class HeaderBeginStmt(ExcellonStatement): + + def __init__(self): + pass + + def to_excellon(self): + return 'M48' + + +class HeaderEndStmt(ExcellonStatement): + + def __init__(self): + pass + + def to_excellon(self): + return 'M95' + + +class RewindStopStmt(ExcellonStatement): + + def __init__(self): + pass + + def to_excellon(self): + return '%' + + +class EndOfProgramStmt(ExcellonStatement): + + def __init__(self, x=None, y=None): + self.x = x + self.y = y + + def to_excellon(self): + stmt = 'M30' + if self.x is not None: + stmt += 'X%s' % write_gerber_value(self.x) + if self.y is not None: + stmt += 'Y%s' % write_gerber_value(self.y) + + +class UnitStmt(ExcellonStatement): + + def __init__(self, units='inch', zero_suppression='trailing'): + self.units = units.lower() + self.zero_suppression = zero_suppression + + def to_excellon(self): + stmt = '%s,%s' % ('INCH' if self.units == 'inch' else 'METRIC', + 'LZ' if self.zero_suppression == 'trailing' else 'TZ') + + +class IncrementalModeStmt(ExcellonStatement): + + def __init__(self, mode='off'): + if mode.lower() not in ['on', 'off']: + raise ValueError('Mode may be "on" or "off") + self.mode = 'off' + + def to_excellon(self): + return 'ICI,%s' % 'OFF' if self.mode == 'off' else 'ON' + + +class VersionStmt(ExcellonStatement): + + def __init__(self, version=1): + self.version = int(version) + + def to_excellon(self): + return 'VER,%d' % self.version + + +class FormatStmt(ExcellonStatement): + def __init__(self, format=1): + self.format = int(format) + + def to_excellon(self): + return 'FMAT,%d' % self.format + + +class LinkToolStmt(ExcellonStatement): + + def __init__(self, linked_tools): + self.linked_tools = [int(x) for x in linked_tools] + + def to_excellon(self): + return '/'.join([str(x) for x in self.linked_tools]) + + +class MeasuringModeStmt(ExcellonStatement): + + def __init__(self, units='inch'): + units = units.lower() + if units not in ['inch', 'metric']: + raise ValueError('units must be "inch" or "metric"') + self.units = units + + def to_excellon(self): + return 'M72' if self.units == 'inch' else 'M71' + + -- cgit