Cambiar el nombre de las piezas en Eagle CAD Editando el XML

Hay muchas maneras de ahorrar tiempo al diseñar PCB, pero cambiar el nombre de los componentes puede ser una de las más frustrantes. [Joe Pinzone] escribió con su solución al problema. En lugar de buscar cada parte del esquema para cambiarlas una por una, hace una lista de los marcadores de posición y luego usa un script para realizar todos los cambios en los archivos XML. No publicó una publicación sobre su trabajo, pero encontrará el código fuente que escribió incrustado después del descanso.

La gota que colmó el vaso fue un proyecto que incluía unos doscientos componentes que no parecían tener un orden de nombres que tuviera ningún sentido con los valores reales de los componentes. El script está escrito en C ++ (para Windows pero [Joe] dice que esto también debería ser portado fácilmente a otros sistemas). Para usarlo, cree un archivo CSV con los nombres de los componentes actuales en la primera columna. Luego revisa y escribe lo que quiere para el nuevo nombre en la segunda columna. Este CSV, junto con los archivos BRD y SCH, se proporcionan como entradas para la secuencia de comandos (seleccionándolos todos y arrastrándolos a la secuencia de comandos o como argumentos de la CLI) y realiza los cambios automáticamente.

Por supuesto, esto solo es posible porque Cadsoft ha cambiado al uso de archivos XML en Eagle 6.

/*
  Title: EagleCAD XML Batch Component Renamer
  Author: Joe Pinzone, Newbury Ohio
  License: Free ("beer"), with acknowledgement of original author
  Date: January, 2013

  Description:
    * Designed to use a CSV to perform batch renaming of EagleCAD schematic/board components
    * Works only on new Eagle XML format schematic and board files

  To generate CSV file with original component name list:
    * Simply drag ONLY your SCH file or BRD onto the EXE. partsList.csv is automatically generated from that file.

  To perform batch renaming:
    * Generate a CSV with original part names in first column, and replacement names in the second
    * Click and drag your SCH, BRD, and CSV file onto the EXE (use multi-select)
    * ^Alternatively, enter in the file paths as arguments from the command prompt

  Notes:
     * If the entry in the second column of the CSV row (replacement name) is blank, that entry will be skipped,
         and the component will retain it's original name
     * To prevent accidental replacement of identical character strings within the XML file, the program
          looks for the specific XML tags that correspond to component names. Edit the list in the
          "Handy things you can modify" section if Eagle changes things, or if you want to play around.
     * You can only load ONE schematic and ONE board file at a time.
     * You can only have 1000 entries in the CSV file. All others are ignored.
        However, you can increase this by setting the value of #define MAX_CSV_ROWS in the source file.
        I didn't want to take up too much memory unnecessarily, and 1000 is plenty for most people.
     * Live long and prosper
*/

#include <cstring>
#include <string>
#include <iostream>
#include <fstream>

using namespace std;

// Handy things you can modify -----------------------------------------------------------------------------------

   /*
       Path and file name to use when generating a name list
   */
   #define MAX_CSV_ROWS 1000 // change this to allow more entries in the CSV find/replace file if necessary.
                             // The program fails nicely, though - it just ignores entries after row 1000.

   char* partsListGenFile = "./partsList.csv";       // the directory and name of the generated parts list CSV file (if no find/replace CSV file is specified)

   /*
     This is the list of XML entries/tags that contain component names after them (from both SCH and BRD Eagle XML files)
     This list is used to make sure that ONLY component names are modified, and coincidental string matches
        elsewhere in the XML aren't accidentally changed. It happened once to me. It wasn't fun. True story.

     Add/remove tags to this list if Eagle changes their standard, or you decide to adapt this for some other purpose.

     The value of 'numTags' should match the number of elements in the 'xmlTags' array below - make sure they match if you change something!
   */
   int numTags = 5;
   string xmlTags[] = { "part name", 
                        "instance part", 
                        "pinref part", 
                        "element name", 
                        "contactref element" 
                      };

   /*
      When generating the CSV list, we only want one copy of each component name.
      The <part name> XML entries in the schematic files, or the <element name> XML entries in the board files
         only contain one entry per part, so we use those as the lookup strings when generating the CSV.
   */
   string schemXMLTag_part = "part name";
   string boardXMLTag_part = "element name";

// (Global) Variable Declarations  -------------------------------------------------------------------------------

	bool schemFound = false;       // used by parseArguments, marks if schematic file was found in the files loaded
	bool boardFound = false;       // used by parseArguments, marks if board file was found in the files loaded
	bool listFound = false;        // used by parseArguments, marks if find/replace csv file was found in the files loaded

	char* path_schem;              // the file path (full or relative) of the schematic file
	char* path_board;              // the file path (full or relative) of the board file
	char* path_list ;              // the file path (full or relative) of the csv find/replace list

	string list[1000][2];          // The storage array for the find/replace list. FIND string is element 0, REPLACE string is element 1
	int listLength = 0;            // number of populated entries in the list array above (set by the loadList function's return value)

// Function Prototypes	-------------------------------------------------------------------------------------------

	int parseArguments(int argc, char* args[]);    // loads the program arguments (file paths of schematic/board/csv files)

	int processFile(char* XMLFilePath);            // assuming that 'list' is populated, goes line-by-line through specified file and replaces component names
	string processLine(string original);           // used by processFile to process an individual line of text
	string processPart(string original);           // used by processLine to change the part name based on the find/replace list

	int loadList(char* listFilePath);              // parses/loads the list file into memory (into the 'list' array)
	void generateList(char* XMLPath, char* outPath, string xmlReferenceTag); // generates a new CSV component name list from the specified SCH or BRD file

// Program --------------------------------------------------------------------------------------------------------

int main(int argc, char* args[]){

    cout << "EagleCAD XML Batch Component Renamer...nn";
    if(!parseArguments(argc, args)) { system("pause"); return 0;} // parse program arguments, pause and close on error

    if(listFound == false){ // if no list was specified, assume that we should generate a parts list instead of doing find/replace
         if(schemFound == true){ generateList(path_schem, partsListGenFile, schemXMLTag_part);} // if schematic file is available, generate the list from that
         else if (boardFound == true){ generateList(path_board, partsListGenFile, boardXMLTag_part);} // if schematic file is not available, generate from board file
    }
    else if (listFound == true){ // if the user specified a find/replace list, do the find/replace
         listLength = loadList(path_list);
         if(schemFound == true){ cout << "Changed <" << processFile(path_schem) << "> lines in schematic.n"; }
         if(boardFound == true){ cout << "Changed <" << processFile(path_board) << "> lines on board.n"; }
    }
    cout << endl;
    system("pause");
    return 0;
}

int parseArguments(int argc, char* args[]){
    /*
         Load the file paths from the program arguments into memory
    */
    for(int index = 1; index < argc; index++){
            string buffer = args[index];
            if(buffer.find(".sch") != -1){
                if(schemFound == false){
                    path_schem = args[index];
                    schemFound = true;
                }
                 else{
                      cout << "You may only load ONE schematic file.nn";
                      return 0;
                 }
            }

            else if(buffer.find(".brd") != -1){
                 if(boardFound == false){
                     path_board = args[index];
                     boardFound = true;
                 }
                 else{
                      cout << "You may only load ONE board file.nn";
                      return 0;
                 }
            }

            else if(buffer.find(".csv") != -1){
                 if(listFound == false){
                     path_list = args[index];
                     listFound = true;
                 }
                 else{
                      cout << "You may only load ONE CSV file.nn";
                      return 0;
                 }
            }

            else{
                 cout << "Unknown file: " <<  buffer << endl << endl;
                 return 0;
            }
    }
    if(schemFound == false && boardFound == false){
         cout << "You must specify a schematic (SCH) or board (BRD) file.nn";
         return 0;
    }

	return 1;
}

int processFile(char* XMLFilePath){

     ifstream source; // pointer to source file
     ofstream dest;   // pointer to destination file
     int modifiedLines = 0;

     string newPath = XMLFilePath;
     newPath += ".new";

     source.open(XMLFilePath); // i.e. file.sch
     dest.open(newPath.c_str()); // i.e. file.sch.new

     string buffer = "";

     while(!source.eof()){
          getline(source, buffer);
          string temp = processLine(buffer);
          dest << temp << endl;
          if(temp != buffer){ modifiedLines++;}

     }

     source.close();
     dest.close();

     return modifiedLines;
}

string processLine(string original){

    /*
      Things to look for in a schematic file
    */
    string beginning = "";
    string end = "";
    string partName = "";

    for(int tagNum = 0; tagNum < numTags; tagNum++){
        if(original.find("<"+xmlTags[tagNum]+"="") != -1){     // look for lines that contain the part names
            beginning = "<"+xmlTags[tagNum]+"="";              // this is technically always the same, but I want to be explicit
            break; // shouldn't need this if the line contains only one valid XML tag
        }
    }

    // if there is a name to replace, replace it. Otherwise, return the original string
    if(beginning != ""){ // 'beginning' will still be empty ("") if there isn't a valid tag to be processed
        end = original.substr(beginning.length(),original.length()); // remove the beginning, whatever it may be
        partName = end.substr(0,end.find("""));                  // clear out the trailing XML, leaving just the name
        end = end.substr(end.find("""),end.length());               // clear the part name from the 'end'
        return beginning + processPart(partName) + end;              // return the new line with replaced part name
    }
    // we only get here if there was nothing to replace in the 'if' block above
    return original;
}

string processPart(string original){
       for(int index = 0; index < listLength; index++){
           if(original == list[index][0] && list[index][1] != ""){ // don't process lines with no replacement
                return list[index][1];
           }
       }
       return original;
}

int loadList(char* listFilePath){

     ifstream source;
     source.open(listFilePath);
     string buffer = "";
     int entryCount = 0;

     while(!source.eof() && entryCount < MAX_CSV_ROWS){
         buffer = "";
         getline(source, buffer);
         list[entryCount][0] = buffer.substr(0,buffer.find(","));
         list[entryCount][1] = buffer.substr(buffer.find(",")+1,buffer.length());
         entryCount++;

     }

     source.close();
     return entryCount;
}

void generateList(char* XMLPath, char* outPath, string xmlReferenceTag){
     cout << "Generating components list from: n" << XMLPath << endl << endl;
     ifstream source;
     ofstream dest;

     source.open(XMLPath);
     dest.open(outPath);

     string buffer = "";
     string XMLPretext = "<"+xmlReferenceTag+"=""; // gotta add the extra characters to look for an actual tag
     int elementCount = 0;
     int lineCount = 0;
     while(!source.eof()){
          buffer = "";
          getline(source, buffer);
          lineCount ++;
          if(buffer.find(XMLPretext) != -1){ // look for lines that contain the part names
               buffer = buffer.substr(XMLPretext.length(),buffer.length()); // clear out the leading XML
               buffer = buffer.substr(0,buffer.find(""")); // clear out the trailing XML, leaving just the name
               dest << buffer << endl;
               elementCount++;
          }

     }
     source.close();
     dest.close();

     return;
}	// generates CSV parts list from SCH or BRD XML files

  • Bogdán dice:

    Algunos programas de CAD le permiten realizar cambios en un caso o en todas las ocasiones.
    Una de las desventajas de un águila es que no puede...

    ¿Qué es lo que realmente me confunde? ¿Por qué se debe poner todo el código en la publicación?

  • justin dice:

    ¿Qué pasa con el ULP incorporado de Eagle, "renumber-sheet.ulp"? Uso esta renumeración de partes en el esquema todo el tiempo. Veo el valor de hacer que el orden siga la ubicación en la PCB y reescriba el esquema, pero es fácil ubicar un indicador de referencia de la herramienta y encontrarlo en el tablero, y prefiero tener las piezas en orden. la hoja de contorno. No estoy seguro de por qué editar el XML directamente es una buena idea.

  • jrog dice:

    No estoy seguro de por qué esto es mejor que simplemente usar el ULP incluido "renumber-schematic.ulp". Prefiero ejecutar ese comando y ajustar cómo necesito/quiero cambiar el nombre de los componentes. Incluso puedes excluir ciertos puntos de referencia, como TP $ o lo que quieras.

  • alex rossi dice:

    pero -e 's / old / new / g' podría ser útil aquí

    • Ardilla dice:

      Se mencionó que la publicación anterior parece demasiado problema.
      Abajo debería funcionar y, aunque menos efectivo, no debería ser notablemente más lento.

      #! / papelera / bash
      CSV_FILE = $ 1
      SCH $ 2
      BRD = $ 3
      SIF ANTIGUO = $ SIF
      SFI =,
      [ ! -f $INPUT ] & mientras lee el nombre antiguo el nombre nuevo
      faru
      pero -e “s/$nombre antiguo/$nombre nuevo/g” $SCH > $SCH.tmp
      pero -e “s/$nombre antiguo/$nombre nuevo/g” $BRD > $BRD.tmp
      mv $ SCH {.tmp,}
      mv $BRD {.tmp,}
      hecho SIF = $ SIF ANTIGUO

      • alex rossi dice:

        Me recuerda a http://www.leancrew.com/all-this/2011/12/more-shell-less-egg/

  • Jim dice:

    Recuerden, niños, las bibliotecas para analizar XML existen por alguna razón...

  • joe pinzon dice:

    Hola chicos - autor aquí.

    En primer lugar, no pretenderé ser un guionista experimentado, por lo que acepto y aprecio las críticas. Sin embargo, me gustaría defender y explicar algunas de las ventajas de mi método posiblemente desarrollado, ya que creo que sus comparaciones con otros métodos no son del todo justas.

    En primer lugar, rename-sheet.ulp no es muy flexible ni útil para los diseños que no están dispuestos de arriba a abajo o a la derecha. Mi último diseño era muy simétrico con muchos bloques idénticos. Si tuviera que usar la secuencia de comandos ULP, habría terminado con una lista de partes donde los componentes idénticos que cumplen funciones idénticas en bloques idénticos pero separados se les asignarían efectivamente identificadores indexados aleatoriamente. Editar en un entorno CSV fue mucho más conveniente, y tratar de masajear el ULP fue más problemático de lo que valió la pena en mi humilde opinión.

    En segundo lugar, como dicen Alex y Squirrel, alguna acción 'pero' puede realizar una función similar. Sin embargo, el ejemplo dado por Squirrel no es una comparación justa a largo plazo.

    Primero, solo funcionaría para un componente. Supongamos que cambia el nombre de "R180 ″>" R18 ". Genial: funciona perfectamente (o lo hace). Luego simplemente expandimos la función para agregar otra línea que dice" R18 "se convierte en" R15 ". R15 y también R18. Y además, el valor de rotación de CADA componente que ERA de 180 grados ahora es de 15 grados porque los datos de rotación se almacenan con un prefijo "R" con exactamente el mismo formato que los nombres de los componentes, pero solo la etiqueta XML le diría eso.

    Nuevamente, estoy totalmente a favor de las críticas, pero sea justo y mire los pasos que he tomado para hacer esto sólido.

    En cuanto a las bibliotecas de análisis XML, es una victoria directa para Jim. No sabía que existían, así que gracias!

    • bote de basura dice:

      Bueno, en ese caso, tengo algunas cosas que preguntar (respetuosamente); ¿No sería un mapa más rápido que una matriz de cadenas? Además, MAX_CSV_ROWS nunca se usa para inicializar la tabla, solo durante el ciclo de verificación de límites. Esto podría causar un problema si alguien sin sospecharlo lo aumenta. De lo contrario, tengo que decir que fue un gran trabajo crear una herramienta para lograr sus objetivos.

      • joe pinzon dice:

        La razón por la que no usé un mapa es porque soy un ingeniero eléctrico con bastante educación formal en software, por lo que los mapas y yo no nos conocemos muy bien. Si dice que sería más rápido, estoy seguro de que tiene razón, y me gustaría un enlace a una explicación del concepto para llenar ese vacío en mi base de conocimientos si fuera tan amable.

        El hecho de que MAX_CSV_ROWS no inicialice la tabla es un error de negligencia. ¡Gracias por señalarlo! Fue una adición de última hora para tratar de poner todos los controles del programa en un solo lugar, y olvidé completar el pensamiento.

  • Treymd dice:

    ¿Qué se te ocurrió que algo escrito en C++ se llama script, pero algo escrito en HTML5 se llama App?

    • yo dice:

      ¡Bienvenido al 2013!

      : '{

  • joe pinzon dice:

    Si el moderador permite tales cosas, aquí hay enlaces de descarga para el ejecutable (cámbiele el nombre a .exe) y la fuente (cpp).

    https://dl.dropbox.com/u/41434020/batchFindReplace.ex
    https://dl.dropbox.com/u/41434020/batchFindReplace.cpp

    Ya sea que la implementación sea la forma más efectiva o no, funciona de manera confiable y, de todos modos, ¡nunca tendrá que mirar el código!

    • Ursicina dice:

      Perdón por revivir este viejo hilo. Estoy buscando una solución para cambiar los nombres de los cables, por lo que en realidad este código haría exactamente eso. Desafortunadamente, no abro los nombres de los cables. Para eso, tendría que cambiar el código fuente y volver a compilarlo. El único problema; No tengo idea de qué es C ++ y cómo compilarlo nuevamente.
      ¿Existe un archivo de ejecución que también cambie los nombres de los cables? ¿O otra solución?

      Gracias

      • joe pinzon dice:

        Esto se puede adaptar para hacer un nombre de grupo de nombres de cables (también conocido como "redes"). Me gustaría saber en qué situación se encuentra que requeriría un cambio de nombre grupal de las redes, pero con gusto lo ayudaré. Mi dirección de correo electrónico tiene el formato '[email protected]'.

        • Ursicina dice:

          Hola Joe,

          Muchas gracias. Voy a tratar de explicar mi situación:
          Hace algún tiempo comencé a construir un campamento. Entre otras cosas, yo mismo planeé la electricidad en ese campamento. Al principio hice esto en papel, pero después de un tiempo se volvió bastante complejo. Soy un poco técnico, por lo que en sí mismo lo hizo aún más complejo. Luego se añadieron cosas, se cambiaron y así sucesivamente. Después de un tiempo resultó que necesitaba cambiar mucho. Las cosas tuvieron que cambiar porque resultó que no sería ideal ni posible construirlo según lo planeado.
          En ese momento decidí hacerlo en un sistema basado en CAD para que los cambios futuros fueran más fáciles. La solución tenía que ser lo suficientemente asequible y potente sin volverse compleja para ese proyecto. Entonces decidí optar por la versión para aficionados de Eagle.
          Sé que hay mejores programas para hacer este tipo de esquemas, pero los baratos "y" potentes no están realmente disponibles. Así que al final me decidí por Eagle debido al rendimiento general en comparación con el precio (no solo, sino en gran medida).

          Como no sabía cómo usar Eagle, simplemente comencé. Por así decirlo, aprender haciendo. El proyecto ha crecido a 50 hojas en un águila hoy. Ya no crece, está casi terminado 😉
          Las redes se nombraron con, por ejemplo, W100 / 1.5RD, W101 / 1.5BK, W103 / 50BK ... W300 / 01, W300 / 02 ... W1xx es el nombre único del cable (o cable) Los números después de la barra diagonal están encendidos hilos individuales (/ 1.5RD) el tamaño y el color del cable, en los cables (/ 01, / 02) el número de hilo. Estos números luego se usan para nombrar los cables en el esquema y obtener los enlaces a otras páginas. Creo que Eagle realmente no fue diseñado para hacer eso, y probablemente las ranuras de águila ahora se están golpeando la cabeza contra el escritorio mientras leen esto 😉 De todos modos, para ese proyecto funciona bastante bien.

          Debido a que hice eso, mientras tanto tuve tantos cambios que ahora tengo los nombres de los cables repartidos por el esquema sin ninguna estructura. Por ejemplo, el cable W100 está en la hoja 25, W101 en la hoja 15,… W150 en la hoja 1 y así sucesivamente. Ahora me gustaría cambiar esto, por lo que W100 está en la página 1, W101 sería el segundo cable en la página 1, W102 podría ser el primer cable en la página 2 y así sucesivamente.
          Lo mismo con los conectores usados, pero estos se pueden encontrar en el archivo de salida de su programa original.

          Mientras tanto he recibido en otro foro algunos consejos de como cambiar mucho con pero. Puede ser un poco peligroso de usar (IIRC ese fue el punto en el que escribiste todo el programa), pero ahora funcionó para muchas cosas. Pero para el que tiene los cables se vuelve demasiado complicado para mi pero-comprensión.

          Hoy intenté nuevamente con C ++ y ahora tengo una solución funcional basada en Dev-C ++, por lo que puedo compilar un exe funcional desde su archivo cpp. Eso no cambia el hecho de que no tengo idea de qué es C ++ y cómo cambiarlo. Probé algunas cosas y finalmente descubrí que cambiar
          string schemXMLTag_part = "nombre de la parte"; a la cadena schemXMLTag_part = "sin nombre"; podría funcionar. Pero luego solo obtengo los nombres de la red sin los nombres de las partes... Sería bueno si obtuviera los nombres de la red y las partes en el mismo resultado, puedo vivir con eso, pero si tiene una solución mejor, se lo agradecería. . ¿O verás los problemas que encuentro durante esto?

          Pero ahora tengo otro problema: al importar el archivo output.csv a libre office calc, cambiarlo y exportarlo de nuevo, algo parece mal funcionamiento, probablemente con el formateo. ¿Puede decirme qué configuración elegir para importar y exportar para que funcione? ¿La coma como signo de separación? ¿Signaro? ?

          Tampoco estaría enojado con usted si pudiera tener una solución aún mejor (automatizada) para mi problema original, por lo que no tendría que cambiar manualmente todos los cables W1xx 😉

          Si no desea escribir ningún cambio en su archivo cpp aquí, mi correo electrónico sería un nombre en gmx dot ch.

          Muchas gracias

          • joe pinzon dice:

            El problema que tiene con el archivo CSV después de editarlo se debe al hecho de que los editores como OO Calc y Excel a veces usan caracteres de nueva línea diferentes a los que buscan las funciones de C ++ (es decir, pueden usar un carro (/ r ) cuando espera una línea (/ n), o viceversa). Ya sea que haya entendido la última oración o no, la solución es abrir el CSV en un editor de texto básico (como un bloc de notas) y simplemente presionar guardar (luego cerrar el archivo). No parecerá hacer nada, pero un bloc de notas reemplazará los carros con saltos de línea cuando presione guardar, y la secuencia de comandos debería funcionar como se esperaba.

            Mientras escribía el script, no será posible hacer un cambio de nombre parcial y en línea en una sola ejecución. Deberá compilar para ambos casos y ejecutarlos como programas separados. Aunque sería posible modificar el programa para hacer ambas cosas al mismo tiempo, podría ser peligroso si tiene algunas partes o componentes con el mismo nombre que una red. Aunque la situación es poco probable, personalmente elegiría mantenerla a salvo.

            Aquí hay una versión modificada para trabajar en redes (** sin probar **):
            https://dl.dropboxusercontent.com/u/41434020/batchFindReplaceNets.cpp

            Estas son las líneas que han sido modificadas:

            char * partsListGenFile = “./netList.csv”;

            int numEtiquetas = 2;
            cadena xmlEtiquetas[] = {"Nombre de la red", "nombre de la señal"};

            string schemXMLTag_part = "nombre de red";
            string boardXMLTag_part = "nombre de la señal";

Alana Herrero
Alana Herrero

Deja una respuesta

Tu dirección de correo electrónico no será publicada.