Skip to content
Config.py 12.9 KiB
Newer Older
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################
#
# PythonicGcodeMachine - @licence_header_description@
# 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/>.
#
####################################################################################################

"""
"""

####################################################################################################

__all__ = [
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'Parameter',
    'Parameters',
    'Letters',
    'Gcode',
    'Gcodes',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'ModalGroup',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'ModalGroups',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'ExecutionGroup',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'ExecutionOrder',
]

####################################################################################################

import yaml

####################################################################################################

class YamlMixin:
    def _load_yaml(self, yaml_path):
        with open(yaml_path, 'r') as fh:
            data = yaml.load(fh.read())
        return data

####################################################################################################

class MeaningMixin:

    ##############################################

    def __init__(self, meaning):
        self._meaning = str(meaning)

    ##############################################

    @property
    def meaning(self):
        return self._meaning

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class RstMixin:

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def _make_rst(self, headers, columns, **kwargs):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        number_of_columns = len(headers)
        if len(columns) != number_of_columns:
            raise ValueError('Number of columns mismatch')
        number_of_lines = len(self)

        table = []
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        rule = ''
        line_format = ''
        for c, title in enumerate(headers):
            if rule:
                rule += ' '
                line_format += ' '
            length = len(title)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            column = columns[c]
            str_columns = []
            if hasattr(self, 'sorted_iter'):
                it = self.sorted_iter()
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            else:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                it = self
            for line_number, item in enumerate(it):
                formater = kwargs.get('str_' + column, str)
                field = getattr(item, column)
                text = formater(field)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                length = max(len(text), length)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                str_columns.append(text)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            rule += '='*length
            line_format += '{:' + str(length) + '}'
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            table.append(str_columns)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        rst = ''
        rst += rule + '\n'
        rst += line_format.format(*headers) + '\n'
        rst += rule + '\n'
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        for line_number in range(number_of_lines):
            fields = [table[c][line_number] for c in range(number_of_columns)]
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            rst += line_format.format(*fields) + '\n'
        rst += rule + '\n'

        return rst

    ##############################################

    def _write_rst(self, path, *args, **kwargs):
        print('Write {}'.format(path))
        with open(path, 'w') as fh:
            fh.write(self._make_rst(*args, **kwargs))

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Parameter(MeaningMixin):

    ##############################################

    def __init__(self, index, meaning, value):

        MeaningMixin.__init__(self, meaning)
        self._index = int(index)
        self._value = float(value)

    ##############################################

    @property
    def index(self):
        return self._index

    @property
    def default_value(self):
        return self._value

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Parameters(YamlMixin, RstMixin):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __init__(self, yaml_path):

        data = self._load_yaml(yaml_path)
        self._parameters = {}
        for index, d in data.items():
            parameter = Parameter(index, d['meaning'], d['value'])
            self._parameters[index] = parameter

    ##############################################

    def __len__(self):
        return len(self._parameters)

    def __iter__(self):
        return iter(self._parameters.values())

    def __getitem__(self, index):
        return self._parameters[index]

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def to_rst(self, path):
        self._write_rst(
            path,
            headers=('Parameter Number', 'Parameter Value', 'Comment'),
            columns=('index', 'default_value', 'meaning'),
        )

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class Letter(MeaningMixin):

    ##############################################

    def __init__(self, letter, meaning):

        MeaningMixin.__init__(self, meaning)
        self._letter = str(letter)

    ##############################################

    @property
    def letter(self):
        return self._letter

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Letters(YamlMixin, RstMixin):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __init__(self, yaml_path):

        data = self._load_yaml(yaml_path)
        self._letters = {}
        for letter, d in data.items():
            self._letters[letter] = Letter(letter, d['meaning'])
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __len__(self):
        return len(self._letters)

    def __iter__(self):
        return iter(self._letters.values())

    def __getitem__(self, letter):
        return self._letters[letter]

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def to_rst(self, path):
        self._write_rst(
            path,
            headers=('Letter', 'Meaning'),
            columns=('letter', 'meaning'),
        )

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class Gcode(MeaningMixin):

    ##############################################

    def __init__(self, code, meaning):

        MeaningMixin.__init__(self, meaning)
        self._code = str(code)

    ##############################################

    @property
    def code(self):
        return self._code

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Gcodes(YamlMixin, RstMixin):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __init__(self, yaml_path):

        data = self._load_yaml(yaml_path)
        self._gcodes = {}
        for code, d in data.items():
            gcode = Gcode(code, d['meaning'])
            self._gcodes[code] = gcode

    ##############################################

    def __len__(self):
        return len(self._gcodes)

    def __iter__(self):
        return iter(self._gcodes.values())

    def __getitem__(self, code):
        return self._gcodes[code]

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def sorted_iter(self):

        items = list(self)
        items.sort(key=lambda item: str(ord(item.code[0])*1000) + item.code[1:])
        return items
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def to_rst(self, path):
        self._write_rst(
            path,
            headers=('G-code', 'Meaning'),
            columns=('code', 'meaning'),
        )
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class ExecutionGroup(MeaningMixin):

    ##############################################

    def __init__(self, index, gcodes, meaning):

        MeaningMixin.__init__(self, meaning)
        self._index = int(index)
        self._gcodes = list(gcodes)

    ##############################################

    @property
    def index(self):
        return self._index

    @property
    def gcodes(self):
        return self._gcodes
Fabrice Salvaire's avatar
Fabrice Salvaire committed

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class ExecutionOrder(YamlMixin, RstMixin):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __init__(self, yaml_path):

        data = self._load_yaml(yaml_path)
        self._order = []
        count = 1
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        for index, d in data.items():
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            if index != count:
                raise ValueError('Unexpected index {} versus {}'.format(index, count))
            count += 1
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            gcodes = d['gcodes']
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            if not isinstance(gcodes, list):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
                gcodes = [gcodes]
            group = ExecutionGroup(index, gcodes, d['meaning'])
            self._order.append(group)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __len__(self):
        return len(self._order)

    def __iter__(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return iter(self._order)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    def __getitem__(self, slice_):
        return self._order[slice_]

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def to_rst(self, path):
        self._write_rst(
            path,
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            headers=('Order', 'G-codes', 'Comment'),
            columns=('index', 'gcodes', 'meaning'),
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            str_gcodes=lambda item: ' '.join(item),
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        )

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class ModalGroup(MeaningMixin):

    ##############################################

    def __init__(self, index, gcodes, meaning):

        MeaningMixin.__init__(self, meaning)
        self._index = int(index)
        self._gcodes = list(gcodes)

    ##############################################

    @property
    def index(self):
        return self._index

    @property
    def gcodes(self):
        return self._gcodes

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class ModalGroups(YamlMixin, RstMixin):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __init__(self, yaml_path):

        data = self._load_yaml(yaml_path)
        self._groups = {}
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        for index, d in data.items():
            gcodes = d['gcodes']
            if not isinstance(gcodes, list):
                gcodes = [gcodes]
            group = ExecutionGroup(index, gcodes, d['meaning'])
            self._groups[index] = group
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __len__(self):
        return len(self._groups)

    def __iter__(self):
        return iter(self._groups.values())

    def __getitem__(self, index):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self._groups[index]

    ##############################################

    def sorted_iter(self):

        items = list(self)
        items.sort(key=lambda item: item.index)
        return items
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def to_rst(self, path):
        self._write_rst(
            path,
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            headers=('Group', 'G-codes', 'Comment'),
            columns=('index', 'gcodes', 'meaning'),
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            str_gcodes=lambda item: ' '.join(item),
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        )

####################################################################################################

class Config:

    ##############################################

    def __init__(self,
                 execution_order,
                 gcodes,
                 letters,
                 modal_groups,
                 parameters,
    ):

        self._execution_order = str(execution_order)
        self._gcodes = str(gcodes)
        self._letters = str(letters)
        self._modal_groups = str(modal_groups)
        self._parameters = str(parameters)

    ##############################################

    @property
    def execution_order(self):
        if isinstance(self._execution_order, str):
            self._execution_order = ExecutionOrder(self._execution_order)
        return self._execution_order

    @property
    def gcodes(self):
        if isinstance(self._gcodes, str):
            self._gcodes = Gcodes(self._gcodes)
        return self._gcodes

    @property
    def letters(self):
        if isinstance(self._letters, str):
            self._letters = Letters(self._letters)
        return self._letters

    @property
    def modal_groups(self):
        if isinstance(self._modal_groups, str):
            self._modal_groups = ModalGroups(self._modal_groups)
        return self._modal_groups

    @property
    def parameters(self):
        if isinstance(self._parameters, str):
            self._parameters = Parameters(self._parameters)
        return self._parameters