HelpfulFootprintWizardPlugin.py 9.13 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#  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 2 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, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#

import pcbnew
18
import math
19 20
import FootprintWizardDrawingAids

21

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
class FootprintWizardParameterManager:
    """
    Functions for helpfully managing parameters to a KiCAD Footprint
    Wizard.

    Abstracts away from whatever structure is used by pcbnew's footprint
    wizard class
    """

    def __init__(self):
        self.parameters = {}
        self.GenerateParameterList()

    def GenerateParameterList(self):
        """
        Construct parameters here, or leave out to have no parameters
        """
        pass

    def CheckParameters(self):
        """
        Implement this to make checks on parameter values, filling
        parameter_errors (or using the checker routines)

        Subclasses can implment their own and override the parent
        defaults and add new ones
        """
        pass

    uMM = 1
    uMils = 2
    uNatural = 3
    uBool = 4
55
    uString = 5
56

57
    def AddParam(self, section, param, unit, default, hint=''):
58 59 60 61 62 63 64 65 66 67 68 69 70 71
        """
        Add a parameter with some properties.

        TODO: Hints are not supported, as there is as yet nowhere to
        put them in the KiCAD interface
        """

        val = None
        if unit == self.uMM:
            val = pcbnew.FromMM(default)
        elif unit == self.uMils:
            val = pcbnew.FromMils(default)
        elif unit == self.uNatural:
            val = default
72 73
        elif unit == self.uString:
            val = str(default)
74
        elif unit == self.uBool:
75
            val = "True" if default else "False"  # ugly stringing
76 77 78 79
        else:
            print "Warning: Unknown unit type: %s" % unit
            return

80 81
        if unit in [self.uNatural, self.uBool, self.uString]:
            param = "*%s" % param  # star prefix for natural
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96

        if section not in self.parameters:
            self.parameters[section] = {}

        self.parameters[section][param] = val

    def _PrintParameterTable(self):
        """
        Pretty-print the parameters we have
        """
        for name, section in self.parameters.iteritems():
            print "  %s:" % name

            for key, value in section.iteritems():
                unit = ""
97 98
                if ((type(value) is int or type(value) is float)
                        and not "*" in key):
99 100 101 102 103 104 105 106 107 108 109
                    unit = "mm"

                if "*" in key:
                    key = key[1:]
                else:
                    value = pcbnew.ToMM(value)

                print "    %s: %s%s" % (key, value, unit)

    def _ParametersHaveErrors(self):
        """
110
        Return true if we discovered errors during parameter processing
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
        """

        for name, section in self.parameter_errors.iteritems():
            for k, v in section.iteritems():
                if v:
                    return True

        return False

    def _PrintParameterErrors(self):
        """
        Pretty-print parameters with errors
        """

        for name, section in self.parameter_errors.iteritems():
            printed_section = False

            for key, value in section.iteritems():
                if value:
                    if not printed_section:
                        print "  %s:" % name

133 134
                    print "       %s: %s (have %s)" % (
                        key, value, self.parameters[name][key])
135 136 137 138 139 140 141 142

    def ProcessParameters(self):
        """
        Make sure the parameters we have meet whatever expectations the
        footprint wizard has of them
        """

        self.ClearErrors()
143
        self.CheckParameters()
144 145 146 147 148 149

        if self._ParametersHaveErrors():
            print "Cannot build footprint: Parameters have errors:"
            self._PrintParameterErrors()
            return False

150 151
        print ("Building new %s footprint with the following parameters:"
               % self.name)
152 153 154 155 156 157 158 159

        self._PrintParameterTable()
        return True

    #################################################################
    # PARAMETER CHECKERS
    #################################################################

160 161
    def CheckParamInt(self, section, param, min_value=1,
                      max_value=None, is_multiple_of=1):
162 163 164 165 166 167
        """
        Make sure a parameter can be made into an int, and enforce
        limits if required
        """

        try:
168 169
            self.parameters[section][param] = (
                int(self.parameters[section][param]))
170
        except ValueError:
171 172
            self.parameter_errors[section][param] = (
                "Must be a valid integer")
173 174
            return

175 176 177 178
        if min_value is not None and (
                self.parameters[section][param] < min_value):
            self.parameter_errors[section][param] = (
                "Must be greater than or equal to %d" % (min_value))
179 180
            return

181 182 183 184
        if max_value is not None and (
                self.parameters[section][param] > min_value):
            self.parameter_errors[section][param] = (
                "Must be less than or equal to %d" % (max_value))
185 186
            return

187 188 189 190
        if is_multiple_of > 1 and (
                self.parameters[section][param] % is_multiple_of) > 0:
            self.parameter_errors[section][param] = (
                "Must be a multiple of %d" % is_multiple_of)
191 192 193 194 195 196 197 198 199
            return

        return

    def CheckParamBool(self, section, param):
        """
        Make sure a parameter looks like a boolean, convert to native
        boolean type if so
        """
200 201 202
        if str(self.parameters[section][param]).lower() in [
                "true", "t", "y", "yes", "on", "1", "1.0"]:
            self.parameters[section][param] = True
203
            return
204 205 206
        elif str(self.parameters[section][param]).lower() in [
                "false", "f", "n", "no", "off", "0", "0.0"]:
            self.parameters[section][param] = False
207 208 209 210 211 212 213
            return

        self.parameter_errors[section][param] = "Must be boolean (true/false)"
        return


class HelpfulFootprintWizardPlugin(pcbnew.FootprintWizardPlugin,
214
                                   FootprintWizardParameterManager):
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    """
    A class to simplify many aspects of footprint creation, leaving only
    the foot-print specific routines to the wizards themselves

    Generally, you need to implement:
        GetReference()
        GetValue()
        GenerateParameterList()
        CheckParameters()
        BuildThisFootprint()
        GetName()
        GetDescription()
    """
    def __init__(self):
        pcbnew.FootprintWizardPlugin.__init__(self)
        FootprintWizardParameterManager.__init__(self)

        self.name = self.GetName()
        self.decription = self.GetDescription()
        self.image = self.GetImage()

236
    def GetValue(self):
237 238
        raise NotImplementedError

239 240
    def GetReferencePrefix(self):
        return "U"  # footprints needing wizards of often ICs
241 242 243 244

    def GetImage(self):
        return ""

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
    def GetTextSize(self):
        """
        IPC nominal
        """
        return pcbnew.FromMM(1.2)

    def GetTextThickness(self):
        """
        Thicker than IPC guidelines (10% of text height = 0.12mm)
        as 5 wires/mm is a common silk screen limitation
        """
        return pcbnew.FromMM(0.2)

    def SetModule3DModel(self):
        """
        Set a 3D model for the module

        Default is to do nothing, you need to implement this if you have
        a model to set

        FIXME: This doesn't seem to be enabled yet?
        """
        pass

269
    def BuildThisFootprint(self):
270 271 272 273 274 275
        """
        Draw the footprint.

        This is specific to each footprint class, you need to implment
        this to draw what you want
        """
276 277 278 279 280 281 282 283
        raise NotImplementedError

    def BuildFootprint(self):
        """
        Actually make the footprint. We defer all but the setup to
        the implmenting class
        """

284 285 286
        self.module = pcbnew.MODULE(None)  # create a new module
        # do it first, so if we return early, we don't segfault KiCad

287 288 289
        if not self.ProcessParameters():
            return

290 291 292 293 294 295 296 297
        self.draw = FootprintWizardDrawingAids.FootprintWizardDrawingAids(
            self.module)

        self.module.SetValue(self.GetValue())
        self.module.SetReference("%s**" % self.GetReferencePrefix())

        fpid = pcbnew.FPID(self.module.GetValue())  # the name in library
        self.module.SetFPID(fpid)
298

299
        self.BuildThisFootprint()  # implementer's build function
300

301
        self.SetModule3DModel()  # add a 3d module if specified
302

303
        thick = self.GetTextThickness()
304

305 306
        self.module.Reference().SetThickness(thick)
        self.module.Value().SetThickness(thick)