Example #1
0
    def build(self,
              board: pcbnew.BOARD,
              external_signals: dict[str, pcbnew.NETINFO_ITEM] = {},
              path: list[str] = []):
        # Populate signal map creating new signals where required
        local_signals = {}
        for name in self.public_signals:
            if name in external_signals:
                local_signals[name] = external_signals[name]
            else:
                net = pcbnew.NETINFO_ITEM(board, "/".join(
                    (*path, self.id, name)))
                board.Add(net)
                local_signals[name] = net
        for name in self.private_signals:
            net = pcbnew.NETINFO_ITEM(board, "/".join((*path, self.id, name)))
            board.Add(net)
            local_signals[name] = net

        # TODO: Warn about unused signals?

        for component, signal_mapping in self.components:
            component.build(
                board, {
                    inner_name: local_signals[signal_name]
                    for (inner_name, signal_name) in signal_mapping.items()
                }, (*path, self.id))
Example #2
0
    def build(self,
              board: pcbnew.BOARD,
              external_signals: dict[str, pcbnew.NETINFO_ITEM] = {},
              path: list[str] = []):
        def load_footprint(ref: str):
            """Load a footprint otherwise raise exception"""
            library, name = ref.split(":")
            footprint = pcbnew.FootprintLoad(
                os.path.join(os.environ.get("RUNFILES_DIR", "external/"),
                             library), name)
            if not footprint:
                raise ValueError(
                    f"can not load footprint {name} from library {library}")
            return footprint

        fp = load_footprint(self.ref)
        fp.SetValue(self.value)
        set_pcb_position(fp, self.pose)
        fp.Value().SetVisible(self.value_visible)
        fp.Reference().SetVisible(self.reference_visible)
        fp.Reference().SetKeepUpright(self.keep_upright)
        if self.clear_top:
            for g in fp.GraphicalItems():
                layer = g.GetLayer()
                if layer == 37:
                    if isinstance(g, pcbnew.EDA_TEXT):
                        g.SetVisible(False)
                    else:
                        g.DeleteStructure()

        other = [x.GetReference() for x in board.GetFootprints()]
        x = 1
        while f"{self.reference_prefix}{x}" in other:
            x = x + 1
        fp.SetReference(f"{self.reference_prefix}{x}")
        board.Add(fp)

        for i, pin_name in enumerate(self.pins, start=1):
            if pin_name in external_signals:
                fp.FindPadByName(i).SetNet(external_signals[pin_name])
        for name in external_signals.keys():
            if name not in self.pins:
                raise RuntimeError(
                    f"no pin '{name}' in {path} ({self.reference_prefix}{x})")
Example #3
0
 def build(self,
           board: pcbnew.BOARD,
           external_signals: dict[str, pcbnew.NETINFO_ITEM] = {},
           path: list[str] = []):
     """Create and return a KiCad circle centered at x, y."""
     item = pcbnew.PCB_SHAPE()
     item.SetShape(pcbnew.S_CIRCLE)
     item.SetStart(pcbnew.wxPointMM(self.x, self.y))
     item.SetEnd(pcbnew.wxPointMM(self.x + self.diameter / 2, self.y))
     item.SetLayer(self.layer)
     board.Add(item)
Example #4
0
 def build(self,
           board: pcbnew.BOARD,
           external_signals: dict[str, pcbnew.NETINFO_ITEM] = {},
           path: list[str] = []):
     o = pcbnew.PCB_TEXT(board)
     o.SetText(self.text)
     o.SetHorizJustify(pcbnew.GR_TEXT_HJUSTIFY_CENTER)
     o.SetTextSize(
         pcbnew.wxSize(pcbnew.FromMM(self.size), pcbnew.FromMM(self.size)))
     o.SetTextThickness(pcbnew.FromMM(self.size * self.thickness * 0.15))
     o.SetLayer(self.layer)
     set_pcb_position(o, self.pcbpos)
     board.Add(o)
Example #5
0
    def build(self,
              board: pcbnew.BOARD,
              external_signals: dict[str, pcbnew.NETINFO_ITEM] = {},
              path: list[str] = []):
        """Yield a sequence of KiCad segments from the provided Shapely polygon.

        A Shapely polygon may have interior polygons representing holes in
        the larger polygon. The hole polygons are emitted as additional
        segments so they appear as pcb cutouts.
        """

        # coerce to polygon in case input is a ring
        geom = shapely.geometry.polygon.Polygon(self.geom)

        def make_point(p):
            x, y = p
            return pcbnew.wxPointMM(x, y)

        def make_seg(p1, p2):
            seg = pcbnew.PCB_SHAPE()
            seg.SetShape(pcbnew.S_SEGMENT)
            seg.SetStart(make_point(p1))
            seg.SetEnd(make_point(p2))
            seg.SetLayer(self.layer)
            return seg

        points = geom.exterior.coords
        for p1, p2 in [(points[i], points[(i + 1) % len(points)])
                       for i in range(len(points))]:
            board.Add(make_seg(p1, p2))

        for interior in geom.interiors:
            points = interior.coords
            for p1, p2 in [(points[i], points[(i + 1) % len(points)])
                           for i in range(len(points))]:
                board.Add(make_seg(p1, p2))
Example #6
0
def make_bom(board: pcbnew.BOARD,
             field_names: List[str],
             component_info: ComponentInfo = {}):

    fp = io.StringIO()
    writer = csv.DictWriter(fp, fieldnames=field_names)
    writer.writeheader()

    for footprint in board.GetFootprints():
        bom_info = get_builtin_fields(footprint)
        key = tuple(bom_info[f] for f in key_fields)

        component_file_fields = {}
        if component_info:
            if key in component_info:
                # This is a hack, but treat Rotation as a special
                # column and add it's value to the value from KiCAD.
                for offset_field in ["Rotation"]:
                    bom_info[offset_field] = str(float(bom_info[offset_field]) + float(component_info[key].pop(offset_field,0)))
                bom_info.update(component_info[key])
            else:
                logging.warning(f"missing part information for {key}")
                continue

        row = OrderedDict()

        # Copy requested fields to CSV file
        for field_name in field_names:
            if field_name in bom_info:
                row[field_name] = bom_info[field_name]
            else:
                raise ValueError(
                    f"unknown field '{field_name}' must be one of {list(bom_info.keys())}"
                )
        writer.writerow(row)

    fp.seek(0)
    return fp.read()
    plugin.FootprintLibDelete(lib_path1)
except:
    pass  # ignore, new may not exist if first run

try:
    plugin.FootprintLibDelete(lib_path2)
except:
    pass  # ignore, new may not exist if first run

plugin.FootprintLibCreate(lib_path1)

# Verify that the same plugin instance can edge trigger on a lib_path change
# for a FootprintLibCreate()
plugin.FootprintLibCreate(lib_path2)

board = BOARD()

# The only way to construct a MODULE is to pass it a BOARD? Yep.
module = MODULE(board)

fpid = FPID('mine')

module.SetFPID(fpid)

plugin.FootprintSave(lib_path2, module)

# Verify that the same plugin instance can edge trigger on a lib_path change
# for a FootprintSave()
plugin.FootprintSave(lib_path1, module)

# create a disparity between the library's name ("footprint"),
Example #8
0
def place_component(pcb: pcbnew.BOARD, ref: str, pos: Position):
    module = pcb.FindModuleByReference(ref)
    module.SetPosition(to_pcbnew_position(pos))
    module.SetOrientation(to_pcbnew_angle(pos.angle))