From a21f9d909c64dcdbc093abf7211ac61d3614cfc0 Mon Sep 17 00:00:00 2001 From: jaseg Date: Thu, 13 Aug 2020 12:41:45 +0200 Subject: making progress --- plugin/__init__.py | 4 + plugin/debug_install.sh | 13 + plugin/main_dialog.fbp | 1148 ++++++++++++++++++++++++++++++++++++++++++ plugin/mesh_dialog.py | 101 ++++ plugin/mesh_plugin.py | 17 + plugin/mesh_plugin_dialog.py | 149 ++++++ plugin/mesh_plugin_icon.png | Bin 0 -> 254 bytes plugin/mesh_plugin_icon.svg | 268 ++++++++++ 8 files changed, 1700 insertions(+) create mode 100644 plugin/__init__.py create mode 100644 plugin/debug_install.sh create mode 100644 plugin/main_dialog.fbp create mode 100644 plugin/mesh_dialog.py create mode 100644 plugin/mesh_plugin.py create mode 100644 plugin/mesh_plugin_dialog.py create mode 100644 plugin/mesh_plugin_icon.png create mode 100644 plugin/mesh_plugin_icon.svg diff --git a/plugin/__init__.py b/plugin/__init__.py new file mode 100644 index 0000000..8878834 --- /dev/null +++ b/plugin/__init__.py @@ -0,0 +1,4 @@ +from .mesh_plugin import MeshPlugin + +plugin = MeshPlugin() +plugin.register() diff --git a/plugin/debug_install.sh b/plugin/debug_install.sh new file mode 100644 index 0000000..e928738 --- /dev/null +++ b/plugin/debug_install.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +PLUGIN_NAME=security_mesh + +XDG_CONFIG_HOME="${XDG_CONFIG_HOME-$HOME/.config}" +KICAD_BASE="${1-$XDG_CONFIG_HOME/kicad}" +PLUGIN_DIR="$KICAD_BASE/scripting/plugins/$PLUGIN_NAME" + +rm -rf "$PLUGIN_DIR" +mkdir -p "$PLUGIN_DIR" + +cp -r * $PLUGIN_DIR/ + diff --git a/plugin/main_dialog.fbp b/plugin/main_dialog.fbp new file mode 100644 index 0000000..8edcf80 --- /dev/null +++ b/plugin/main_dialog.fbp @@ -0,0 +1,1148 @@ + + + + + ; + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + mesh_plugin_dialog + 1000 + none + + 0 + MeshPlugin + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + MainDialog + + 455,283 + wxDEFAULT_DIALOG_STYLE + ; ; forward_declare + Security Mesh Generator Plugin + + + + + + + bSizer1 + wxVERTICAL + none + + 15 + wxALL|wxEXPAND + 1 + + 2 + wxVERTICAL + 1 + 0,4 + 0 + + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED + none + 3 + 0 + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Mesh net name prefix + 0 + + 0 + + + 0 + + 1 + m_staticText1 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxEXPAND + 1 + + + bSizer3 + wxVERTICAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND + 2 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_net_prefix + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + mesh- + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + + 0 + + 1 + m_netLabel + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + + + 5 + wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Mesh angle + 0 + + 0 + + + 0 + + 1 + m_staticText3 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxEXPAND + 1 + + + bSizer4 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 2 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + 1 + 0.000000 + 360 + + 0 + + 0 + + 0 + + 1 + m_angleSpin + 1 + + + protected + 1 + + Resizable + 1 + + wxSP_ARROW_KEYS|wxSP_WRAP + ; ; forward_declare + 0 + + deg + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + ° (deg) + 0 + + 0 + + + 0 + + 1 + m_staticText5 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + + + 5 + wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Trace width + 0 + + 0 + + + 0 + + 1 + m_staticText4 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxEXPAND + 1 + + + bSizer5 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 3 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + 1 + 0.127 + 100 + + 0 + + 0 + + 0 + + 1 + m_traceSpin + 1 + + + protected + 1 + + Resizable + 1 + + wxSP_ARROW_KEYS + ; ; forward_declare + 0 + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + mm + 0 + + 0 + + + 0 + + 1 + m_staticText6 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + + + 5 + wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Space width + 0 + + 0 + + + 0 + + 1 + m_staticText7 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxEXPAND + 1 + + + bSizer6 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 3 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + 1 + 0.127 + 10000 + + 0 + + 0 + + 0 + + 1 + m_spaceSpin + 1 + + + protected + 1 + + Resizable + 1 + + wxSP_ARROW_KEYS + ; ; forward_declare + 0 + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + mm + 0 + + 0 + + + 0 + + 1 + m_staticText8 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + + + 3 + wxALL|wxEXPAND + 0 + + wxWRAPSIZER_DEFAULT_FLAGS + + wSizer1 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Cancel + + 0 + + 0 + + + 0 + + 1 + m_cancelButton + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Remove Mesh Traces + + 0 + + 0 + + + 0 + + 1 + m_removeButton + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Generate + + 0 + + 0 + + + 0 + + 1 + m_generateButton + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + diff --git a/plugin/mesh_dialog.py b/plugin/mesh_dialog.py new file mode 100644 index 0000000..59af670 --- /dev/null +++ b/plugin/mesh_dialog.py @@ -0,0 +1,101 @@ +import wx +import mesh_plugin_dialog +import pcbnew +from collections import defaultdict + +# Implementing MainDialog +class MeshPluginMainDialog(mesh_plugin_dialog.MainDialog): + def __init__(self, board): + mesh_plugin_dialog.MainDialog.__init__(self, None) + self.board = board + + self.m_cancelButton.Bind(wx.EVT_BUTTON, self.quit) + self.m_removeButton.Bind(wx.EVT_BUTTON, self.tearup_mesh) + self.m_generateButton.Bind(wx.EVT_BUTTON, self.generate_mesh) + self.m_net_prefix.Bind(wx.EVT_TEXT, self.update_net_label) + + self.tearup_confirm_dialog = wx.MessageDialog(self, "", style=wx.YES_NO | wx.NO_DEFAULT) + + self.nets = { str(wxs) for wxs, netinfo in board.GetNetsByName().items() } + self.update_net_label(None) + + self.SetMinSize(self.GetSize()) + + def get_matching_nets(self): + prefix = self.m_net_prefix.Value + return { net for net in self.nets if net.startswith(prefix) } + + def tearup_mesh(self, evt): + matching = self.get_matching_nets() + + if not str(self.m_net_prefix.Value): + message = "You have set an empty net prefix. This will match ALL {} nets on the board. Do you really want to tear up all autorouted tracks? This cannot be undone!" + + else: + message = "Do you really want to tear up all autorouted traces of the {} matching nets on this board? This step cannot be undone!" + + message = message.format(len(matching)) + "\n\nMatching nets:\n" + ", ".join( + '""' if not netname else (netname[:16] + '...' if len(netname) > 16 else netname) + for netname in (sorted(matching)[:5] + ['...'] if len(matching) > 5 else []) + ) + self.tearup_confirm_dialog.SetMessage(message) + self.tearup_confirm_dialog.SetYesNoLabels("Tear up {} nets".format(len(matching)), "Close") + + if self.tearup_confirm_dialog.ShowModal() == wx.ID_YES: + for track in self.board.GetTracks(): + if not (track.GetStatus() & pcbnew.TRACK_AR): + continue + + if not track.GetNet().GetNetname() in matching: + continue + + board.Remove(track) + + def generate_mesh(self, evt): + nets = self.get_matching_nets() + + pads = defaultdict(lambda: []) + for module in self.board.GetModules(): + for pad in module.Pads(): + net = pad.GetNetname() + if net in nets: + pads[net].append(pad) + + for net in nets: + if net not in pads: + return wx.MessageDialog(self, "Error: No connection pads found for net {}.".format(net)).ShowModal() + + if len(pads[net]) == 1: + return wx.MessageDialog(self, "Error: Only one of two connection pads found for net {}.".format(net)).ShowModal() + + if len(pads[net]) > 2: + return wx.MessageDialog(self, "Error: More than two connection pads found for net {}.".format(net)).ShowModal() + + eco1_id = self.board.GetLayerID('Eco1.User') + mesh_zones = {} + for drawing in self.board.GetDrawings(): + if drawing.GetLayer() == eco1_id: + mesh_zones[drawing] = [] + + if len(mesh_zones) == 0: + return wx.MessageDialog(self, "Error: Could not find any mesh zones on the Eco1.User layer.").ShowModal() + + for zone in mesh_zones: + for module in self.board.GetModules(): + for foo in module.GraphicalItems(): + if not isinstance(foo, pcbnew.TEXTE_MODULE): + continue + + if foo.GetText() == " + # find mesh area on eco1, check connection pads are within target area + + def update_net_label(self, evt): + self.m_netLabel.SetLabel('{} matching nets'.format(len(self.get_matching_nets()))) + + def quit(self, evt): + self.Destroy() + +def show_dialog(board): + dialog = MeshPluginMainDialog(board) + dialog.ShowModal() + return dialog diff --git a/plugin/mesh_plugin.py b/plugin/mesh_plugin.py new file mode 100644 index 0000000..1b302b4 --- /dev/null +++ b/plugin/mesh_plugin.py @@ -0,0 +1,17 @@ +import os.path + +import pcbnew + +from .mesh_dialog import show_dialog + +class MeshPlugin(pcbnew.ActionPlugin): + def defaults(self): + self.name = 'Mesh generator' + self.category = 'Modify PCB' + self.description = 'Creates security mesh traces on a PCB' + self.icon_file_name = os.path.join(os.path.dirname(__file__), 'mesh_plugin_icon.png') + self.show_toolbar_button = True + + def Run(self): + import pcbnew + show_dialog(pcbnew.GetBoard()) diff --git a/plugin/mesh_plugin_dialog.py b/plugin/mesh_plugin_dialog.py new file mode 100644 index 0000000..2ec6bd2 --- /dev/null +++ b/plugin/mesh_plugin_dialog.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- + +########################################################################### +## Python code generated with wxFormBuilder (version Oct 26 2018) +## http://www.wxformbuilder.org/ +## +## PLEASE DO *NOT* EDIT THIS FILE! +########################################################################### + +import wx +import wx.xrc + +########################################################################### +## Class MainDialog +########################################################################### + +class MainDialog ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Security Mesh Generator Plugin", pos = wx.DefaultPosition, size = wx.Size( 455,283 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + fgSizer1 = wx.FlexGridSizer( 3, 2, 0, 0 ) + fgSizer1.AddGrowableCol( 1 ) + fgSizer1.AddGrowableRow( 0 ) + fgSizer1.AddGrowableRow( 4 ) + fgSizer1.SetFlexibleDirection( wx.VERTICAL ) + fgSizer1.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) + + + fgSizer1.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + + fgSizer1.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, u"Mesh net name prefix", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText1.Wrap( -1 ) + + fgSizer1.Add( self.m_staticText1, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) + + bSizer3 = wx.BoxSizer( wx.VERTICAL ) + + self.m_net_prefix = wx.TextCtrl( self, wx.ID_ANY, u"mesh-", wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer3.Add( self.m_net_prefix, 2, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 ) + + self.m_netLabel = wx.StaticText( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_netLabel.Wrap( -1 ) + + bSizer3.Add( self.m_netLabel, 0, wx.ALL, 5 ) + + + fgSizer1.Add( bSizer3, 1, wx.EXPAND, 5 ) + + self.m_staticText3 = wx.StaticText( self, wx.ID_ANY, u"Mesh angle", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText3.Wrap( -1 ) + + fgSizer1.Add( self.m_staticText3, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + + bSizer4 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_angleSpin = wx.SpinCtrlDouble( self, wx.ID_ANY, u"deg", wx.DefaultPosition, wx.DefaultSize, wx.SP_ARROW_KEYS|wx.SP_WRAP, 0, 360, 0.000000, 1 ) + self.m_angleSpin.SetDigits( 2 ) + bSizer4.Add( self.m_angleSpin, 0, wx.ALL, 5 ) + + self.m_staticText5 = wx.StaticText( self, wx.ID_ANY, u"° (deg)", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText5.Wrap( -1 ) + + bSizer4.Add( self.m_staticText5, 0, wx.ALL, 5 ) + + + fgSizer1.Add( bSizer4, 1, wx.EXPAND, 5 ) + + self.m_staticText4 = wx.StaticText( self, wx.ID_ANY, u"Trace width", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText4.Wrap( -1 ) + + fgSizer1.Add( self.m_staticText4, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + + bSizer5 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_traceSpin = wx.SpinCtrlDouble( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.SP_ARROW_KEYS, 0, 100, 0.127, 1 ) + self.m_traceSpin.SetDigits( 3 ) + bSizer5.Add( self.m_traceSpin, 0, wx.ALL, 5 ) + + self.m_staticText6 = wx.StaticText( self, wx.ID_ANY, u"mm", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText6.Wrap( -1 ) + + bSizer5.Add( self.m_staticText6, 0, wx.ALL, 5 ) + + + fgSizer1.Add( bSizer5, 1, wx.EXPAND, 5 ) + + self.m_staticText7 = wx.StaticText( self, wx.ID_ANY, u"Space width", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText7.Wrap( -1 ) + + fgSizer1.Add( self.m_staticText7, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + + bSizer6 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_spaceSpin = wx.SpinCtrlDouble( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.SP_ARROW_KEYS, 0, 10000, 0.127, 1 ) + self.m_spaceSpin.SetDigits( 3 ) + bSizer6.Add( self.m_spaceSpin, 0, wx.ALL, 5 ) + + self.m_staticText8 = wx.StaticText( self, wx.ID_ANY, u"mm", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText8.Wrap( -1 ) + + bSizer6.Add( self.m_staticText8, 0, wx.ALL, 5 ) + + + fgSizer1.Add( bSizer6, 1, wx.EXPAND, 5 ) + + + fgSizer1.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + + fgSizer1.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + + bSizer1.Add( fgSizer1, 1, wx.ALL|wx.EXPAND, 15 ) + + wSizer1 = wx.WrapSizer( wx.HORIZONTAL, wx.WRAPSIZER_DEFAULT_FLAGS ) + + self.m_cancelButton = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + wSizer1.Add( self.m_cancelButton, 0, wx.ALL, 5 ) + + + wSizer1.Add( ( 0, 0), 1, wx.EXPAND, 5 ) + + self.m_removeButton = wx.Button( self, wx.ID_ANY, u"Remove Mesh Traces", wx.DefaultPosition, wx.DefaultSize, 0 ) + wSizer1.Add( self.m_removeButton, 0, wx.ALL, 5 ) + + self.m_generateButton = wx.Button( self, wx.ID_ANY, u"Generate", wx.DefaultPosition, wx.DefaultSize, 0 ) + wSizer1.Add( self.m_generateButton, 0, wx.ALL, 5 ) + + + bSizer1.Add( wSizer1, 0, wx.ALL|wx.EXPAND, 3 ) + + + self.SetSizer( bSizer1 ) + self.Layout() + + self.Centre( wx.BOTH ) + + def __del__( self ): + pass + + diff --git a/plugin/mesh_plugin_icon.png b/plugin/mesh_plugin_icon.png new file mode 100644 index 0000000..5f44344 Binary files /dev/null and b/plugin/mesh_plugin_icon.png differ diff --git a/plugin/mesh_plugin_icon.svg b/plugin/mesh_plugin_icon.svg new file mode 100644 index 0000000..7027390 --- /dev/null +++ b/plugin/mesh_plugin_icon.svg @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit