文章目录

  • cadence SPB17.4 - export placement file to openpnp
  • 概述
  • 笔记
  • openpnp支持的坐标文件格式
  • openpnp导入坐标文件的内容规则
  • 制作openpnp可用的坐标文件的思路列表
  • 先自己写个程序试试, 这个不难, 也省心
  • 程序写完了,好使
  • 转换前的allego原版坐标文件
  • 转换后的openpnp格式的坐标文件
  • 被openpnp载入后的效果
  • 发现的问题
  • orcad导出料单
  • 程序维护完成,好使
  • 备注
  • END

cadence SPB17.4 - export placement file to openpnp

概述

弄了一台openpnp, 设备整定好了.
现在需要导入板子的坐标文件.
遇到点问题: SPB17.4并不能导出openpnp可用的坐标文件.

尝试将SPB17.4做的板子, 弄出符合openpnp格式的坐标文件贴片用.

笔记

openpnp支持的坐标文件格式

试了一下, openpnp只支持AD(AD22)格式的坐标(csv)文件.
这种坐标(csv)文件, 在openpnp中叫做 Named csv

这种文件的格式有点特别:

  • 每一项的内容,必须用"符号包裹
    试了一下立创EDA和cadence导出的csv的每项内容不是用"符号包裹的.

用AD22导出的坐标csv文件例子如下:

Altium Designer Pick and Place Locations
Z:\test\Pick Place for NEW_PCB_2022-11-12.csv========================================================================================================================
File Design Information:Date:       12/11/22
Time:       13:42
Revision:   Not in VersionControl
Variant:    No variations
Units used: mm"Designator","Comment","Layer","Footprint","Center-X(mm)","Center-Y(mm)","Rotation","Description","Ref-X(mm)","Ref-Y(mm)","Pad-X(mm)","Pad-Y(mm)"
"U1","BAT-SMD_CR1220-2","TopLayer","BAT-SMD_CR1220-2","58.2270mm","68.2269mm","0","","58.2270mm","68.2269mm","50.2270mm","68.2269mm"

用allegro导出的新版坐标文件并没有标题, 都是数据,用空格分开, 而且单位不可选
如下:

UUNITS = MILS
J4                   216.89         950.18      270    MICRO-USB-SMD_MICROXNJ
TP1                  5809.00       3234.13        0    TEST_TH_PIN_1D0MM
TP10                 4051.00       2358.00        0    TEST_TH_PIN_1D0MM
TP11                 2670.00       3570.00        0    TEST_TH_PIN_1D0MM

用allegro导出的旧版坐标文件有标题, 但是分隔符是’!‘,不是’,'符号, 而且单位不可选
如下:

VERSION = 2.0
UUNITS = MILS#    refdes          !   symbol_x !   symbol_y ! rotation ! mirror ! symbol_name                      ! embedded_layer
#-----------------------------------------------------------------------------------------------------------------------
J4                   !     216.89 !     950.18 !      270 !        ! MICRO-USB-SMD_MICROXNJ           !
TP1                  !    5809.00 !    3234.13 !        0 !        ! TEST_TH_PIN_1D0MM                !
TP10                 !    4051.00 !    2358.00 !        0 !        ! TEST_TH_PIN_1D0MM                !
TP11                 !    2670.00 !    3570.00 !        0 !        ! TEST_TH_PIN_1D0MM                !
TP12                 !    2820.00 !    3569.00 !        0 !        ! TEST_TH_PIN_1D0MM                !
TP2                  !    5810.00 !    3080.00 !        0 !        ! TEST_TH_PIN_1D0MM                !

JLC导出的坐标文件如下, 有题头, 但是分隔符是空格, openpnp不认识

Designator   Footprint   Mid X   Mid Y   Ref X   Ref Y   Pad X   Pad Y   Layer   Rotation    Comment
"U1"  "BAT-SMD_CR1220-2"    "20mm"    "-10mm"   "20mm"    "-10mm"   "12mm"    "-10mm"   "T"   "0"   "BAT-SMD_CR1220-2"

那到底openpnp要求的坐标文件是啥格式的呢? 这自己乱实验,不好弄啊.
找到了openpnp官方文档

openpnp导入坐标文件的内容规则

Importing Centroid Data
关于openpnp导入Named CSV的规则描述如下:
Others
OpenPnP includes a Named CSV importer (File -> Import -> Named CSV) which can import many types of CSV files. Coordinate data must be in Millimeters. Field separator must be a comma.

坐标数据单位必须是mm, 分隔符必须是逗号

Format specifications need to be inside the first 10 lines of file.

格式说明必须在文件的头10行之内

The first six fields are required in order to successfully import centroid data.
前6个数据(元件位号, 元件值, 元件封装, 元件x坐标, 元件y坐标, 元件旋转角度)必须有, 是为了引入元件坐标数据必须有的字段.

The fields that the Named CSV importer will look for are: (not case sensitive)
字段名称如下,忽略大小写

Refs: “Designator”, “Part”, “Component”, “RefDes”, “Ref”
元件位号

Vals: “Value”, “Val”, “Comment”, “Comp_Value”
元件值

Packs: “Footprint”, “Package”, “Pattern”, “Comp_Package”
元件封装

Xs: “X”, “X (mm)”, “Ref X”, “PosX”, “Ref-X(mm)”, “Ref-X(mil)”, “Sym_X”
元件X坐标

Ys: “Y”, “Y (mm)”, “Ref Y”, “PosY”, “Ref-Y(mm)”, “Ref-Y(mil)”, “Sym_Y”
元件Y坐标

Rots: “Rotation”, “Rot”, “Rotate”, “Sym_Rotate”
元件旋转角度

TBs: “Layer”, “Side”, “TB”, “Sym_Mirror”
元件所在层(可选)

Heights: “Height”, “Height(mil)”, “Height(mm)”
元件高度

Example:

"Designator","Footprint","Mid X","Mid Y","Ref X","Ref Y","Pad X","Pad Y","Layer","Rotation","Comment"
"C1","NICHICON_A","58.674mm","7.2263mm","58.674mm","7.239mm","58.674mm","8.7376mm","T","90.00","10uF"
"C3","CAP0603","54.102mm","8.255mm","54.102mm","8.255mm","54.102mm","9.1694mm","T","270.00","1uF"

发现官方给的例子, 没有多余的空格

分析一下openpnp官方给的例子
元件位号 Designator
元件封装 Footprint
元件X坐标 Ref X
元件Y坐标 Ref Y
元件所在层 Layer => 不是官方规定的6个必须数据, 如果要贴双面, 就需要. 不过openpnp默认是贴一面的. 如果要贴另外一面, 就是另外一个工程.
元件旋转角度 Rotation
元件值 Comment

“Mid X”,“Mid Y”, “Pad X”,“Pad Y” 这4个都是非必须的附加数据.

用官方给的例子作为Named csv, 让openpnp导入试试.

用openpnp载入了官方例子做的openpnp_example.txt, 好使, 定义的2个元件都载入了.

制作openpnp可用的坐标文件的思路列表

  • allegro正常导出坐标文件place_txt.txt, 按照一种固定格式导出(元件的坐标原点是谁? + 新格式/旧格式?), 写一个小程序, 将坐标文件转为openpnp可识别的坐标文件格式. 这个不难, 工作量也不大.

  • 能不能找个第三方软件(AD, CAM350), 将allegro的数据(转成第三方格式的PCB文件, 或者就是导入完整gerber)导出为openpnp可识别的坐标文件格式. 这个不一定行(哪有那么现成的软件?), 要实验才行.

先自己写个程序试试, 这个不难, 也省心

allegro导出的坐标文件的选项如下

元件的符号原点 + 新格式
allegro生成的原始坐标文件样例如下:

UUNITS = MILS
J4                   157.00         899.00      270    MICRO-USB-SMD_MICROXNJ
TP1                  5809.00       3234.13        0    TEST_TH_PIN_1D0MM
TP10                 4051.00       2358.00        0    TEST_TH_PIN_1D0MM
TP11                 2670.00       3570.00        0    TEST_TH_PIN_1D0MM
TP12                 2820.00       3569.00        0    TEST_TH_PIN_1D0MM
TP2                  5810.00       3080.00        0    TEST_TH_PIN_1D0MM
TP3                  2020.00       2857.00        0    TEST_TH_PIN_1D0MM

第1项是位号
第2项是X坐标
第3项是Y坐标
第4项时旋转角度
第5项是封装
以上各项跟openpnp要求的6项比对, 可知, 少了元件值. 拿位号和封装拼一个元件值吧
就拿这个格式的素材写坐标文件转换程序吧.

程序写完了,好使

程序写完了,不到400行,验证好使,可以被openpnp正常载入

// @file openpnp_named_cvs_convert.cpp
// @brief 转换SPB17.4 allegro导出的坐标文件(新格式)为openpnp可导入的坐标文件
// @note 编译环境 vs2022 vc++ console#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>void process_allegro_csv(const char* pFile, const char* pFileNew);
void process_allegro_csv(const char* pFile, FILE* fpFile, const char* pFileNew);
void process_convert_head(const char* pFile, FILE* fpFile, const char* pFileNew, FILE* fpFileNew, bool bUnitFind, bool bUnitIsMm, bool bUnitIsMil);
void process_convert_body(FILE* fpFileNew, bool bUnitIsMm, char* sz_buf_field1, char* sz_buf_field2, char* sz_buf_field3, char* sz_buf_field4, char* sz_buf_field5, char* sz_buf_field6);
double mil2mm(double fMil, bool bIsMil);int main(int argc, char** argv)
{char* pFile = NULL;char sz_new_file[256];do {if (2 != argc){// 测试用的 allegro导出的原始坐标文件为 allegro_org.txtprintf("请给出要转换的csv文件名称\r\n");printf("usage : openpnp_named_cvs_convert x.csv\r\n");break;}if (NULL == argv[1]){printf("err : 给定的文件名称为空\r\n");break;}pFile = argv[1];printf("要处理的文件为 : %s\r\n", pFile);memset(sz_new_file, 0, sizeof(sz_new_file));sprintf(sz_new_file, "%s.csv", pFile);printf("转换完的文件为 : %s\r\n", sz_new_file);process_allegro_csv(pFile, sz_new_file);printf("处理结束 : 请查看 %s 是否处理正确\r\n", sz_new_file);} while (0);return EXIT_SUCCESS;
}void process_allegro_csv(const char* pFile, const char* pFileNew)
{FILE* fpFile = NULL;do {// 在编译选项(预处理器)中加入 _CRT_SECURE_NO_WARNINGS 宏, 防止编译警告fpFile = fopen(pFile, "r");if (NULL == fpFile){printf("err : 文件(%s)打开失败\r\n", pFile);break;}process_allegro_csv(pFile, fpFile, pFileNew);} while (0);if (NULL != fpFile){fclose(fpFile);fpFile = NULL;}
}void process_allegro_csv(const char* pFile, FILE* fpFile, const char* pFileNew)
{char sz_buf_rd[1024];size_t nRdCnt = 0;char* pFind = NULL;char sz_buf_field1[0x100];char sz_buf_field2[0x100];char sz_buf_field3[0x100];char sz_buf_field4[0x100];char sz_buf_field5[0x100];char sz_buf_field6[0x100];int iCnt = 0;bool bUnitFind = false;bool bUnitIsMm = false;bool bUnitIsMil = false;FILE* fpFileNew = NULL;do {if (NULL == pFileNew){break;}if (NULL == fpFileNew){fpFileNew = fopen(pFileNew, "w");if (NULL == fpFileNew){printf("建立新文件(%s)失败\r\n", pFileNew);break;}}memset(sz_buf_rd, 0, sizeof(sz_buf_rd));pFind = fgets(sz_buf_rd, sizeof(sz_buf_rd), fpFile);if (NULL == pFind){printf("原始文件读取完毕\r\n");break;}if (!bUnitFind){// 查找 UUNITS = MILSpFind = strstr(sz_buf_rd, "UUNITS = ");if (NULL == pFind){continue;}memset(sz_buf_field1, 0, sizeof(sz_buf_field1));iCnt = sscanf(sz_buf_rd, "UUNITS = %255s", sz_buf_field1); // MILSif (iCnt <= 0){continue;}if (0 == strcmp(sz_buf_field1, "MILS")){// 单位是milbUnitFind = true;bUnitIsMm = false;bUnitIsMil = true;}else if (0 == strcmp(sz_buf_field1, "MM")){// 单位是mmbUnitFind = true;bUnitIsMm = true;bUnitIsMil = false;}else {printf("err : 文件格式不对, 单位应该是mil和mm其中一个\r\n");break;}// 写处理完的csv的注释部分process_convert_head(pFile, fpFile, pFileNew, fpFileNew, bUnitFind, bUnitIsMm, bUnitIsMil);}else {// 查找 J4                   157.00         899.00      270    MICRO-USB-SMD_MICROXNJ//// 如果有背面元件时, 就有6列, 会在第5列多出一个镜像的m字符, 第6列是封装// 如果是正面的元件, 就只有5列// LOGO2                3780.00       1220.00        0  m LOGO_KRGY// LOGO1                1467.42        514.83        0    LOGO_STC15_BOX4memset(sz_buf_field1, 0, sizeof(sz_buf_field1));memset(sz_buf_field2, 0, sizeof(sz_buf_field2));memset(sz_buf_field3, 0, sizeof(sz_buf_field3));memset(sz_buf_field4, 0, sizeof(sz_buf_field4));memset(sz_buf_field5, 0, sizeof(sz_buf_field5));memset(sz_buf_field6, 0, sizeof(sz_buf_field6));iCnt = sscanf(sz_buf_rd, "%s %s %s %s %s %s", sz_buf_field1,sz_buf_field2,sz_buf_field3,sz_buf_field4,sz_buf_field5,sz_buf_field6); // MILSif ((iCnt < 5) && (iCnt > 6)){continue;}if (5 == iCnt){// 是正面元件, 第5列为空, 只有第6列为封装strcpy(sz_buf_field6, sz_buf_field5);memset(sz_buf_field5, 0, sizeof(sz_buf_field5));}else if (6 == iCnt) {// 是反面面元件, 第5列为m, 第6列为封装}process_convert_body(fpFileNew, bUnitIsMm, sz_buf_field1, sz_buf_field2, sz_buf_field3, sz_buf_field4, sz_buf_field5, sz_buf_field6);}} while (1);if (NULL != fpFileNew){fclose(fpFileNew);fpFileNew = NULL;}
}void process_convert_head(const char* pFile, FILE* fpFile, const char* pFileNew, FILE* fpFileNew, bool bUnitFind, bool bUnitIsMm, bool bUnitIsMil)
{// 在文件的前10行(只能是前10行, openpnp有规定)写入一些注释(e.g. 我是谁? 我在哪?)/*Altium Designer Pick and Place LocationsZ:\test\Pick Place for NEW_PCB_2022-11-12.csv========================================================================================================================File Design Information:Date:       12/11/22Time:       13:42Revision:   Not in VersionControlVariant:    No variationsUnits used: mm*/char sz_buf[1024];do {if ((NULL == fpFile) || (NULL == fpFile) || (!bUnitFind)){break;}// bool bUnitIsMm, bool bUnitIsMilif (!bUnitIsMm && !bUnitIsMil){// mm 和 mil 单位必须有一个为truebreak;}// line 1memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "openpnp Named csv Pick and Place Locations\n"); // 以w方式打开的文件, 换行的话,\n就行了fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 2memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "org file = %s\n", pFile);fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 3memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "convert file = %s\n", pFileNew);fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 4memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "========================================================================================================================\n");fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 5memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "File Design Information:\n");fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 6memset(sz_buf, 0, sizeof(sz_buf));// Units used: mmsprintf(sz_buf, "Units used: mm\n"); // openpnp用的坐标文件, 必须是mmfwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 7// J4                   157.00         899.00      270    MICRO-USB-SMD_MICROXNJ// "Designator","X (mm)","Y (mm)","Rotation","Footprint","Comment"// "Designator","Comment","Footprint","Rotation","Ref-X(mm)","Ref-Y(mm)" // 这个是用AD出的坐标文件整理后的可用题头// "Designator", "Comment", "Layer", "Footprint", "Center-X(mm)", "Center-Y(mm)", "Rotation", "Description", "Ref-X(mm)", "Ref-Y(mm)", "Pad-X(mm)", "Pad-Y(mm)"// "U1", "BAT-SMD_CR1220-2", "TopLayer", "BAT-SMD_CR1220-2", "58.2270mm", "68.2269mm", "0", "", "58.2270mm", "68.2269mm", "50.2270mm", "68.2269mm"// 发现allegro导出的坐标文件, 是分正反面的, 所以要加上Layer字段memset(sz_buf, 0, sizeof(sz_buf));// 前6列是allegro导出的坐标文件就有的, 第7列是拼装的(位号 + 封装), 或留空(因为allegro导出的坐标文件,本来就没有元件值)//                 列1            列2           列3           列4          列5        列6           列7sprintf(sz_buf, "\"Designator\",\"Ref-X(mm)\",\"Ref-Y(mm)\",\"Rotation\",\"Layer\", \"Footprint\",\"Comment\"\n");fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);} while (0);
}void process_convert_body(FILE* fpFileNew, bool bUnitIsMm, char* sz_buf_field1, char* sz_buf_field2, char* sz_buf_field3, char* sz_buf_field4, char* sz_buf_field5, char* sz_buf_field6)
{// 原始// "Designator","X (mm)","Y (mm)","Rotation","Footprint","Layer","Comment"// J4                   157.00         899.00      270    m       MICRO-USB-SMD_MICROXNJ// 转换之后// "C1", "58.674mm", "7.239mm", "90.00", "NICHICON_A", "BottomLayer", "10uF"// "C2", "58.674mm", "7.239mm", "90.00", "NICHICON_A", "TopLayer", "10uF"char sz_buf_field5_new[0x100];char sz_buf_field7_new[0x100];char sz_buf_write[1024];double fTmp = 0;double fX = 0;double fY = 0;double fAngle = 0;// sz_buf_field1 is J4// sz_buf_field2 is 157.00fTmp = atof(sz_buf_field2);fX = mil2mm(fTmp, !bUnitIsMm);// sz_buf_field3 is 899.00fTmp = atof(sz_buf_field3);fY = mil2mm(fTmp, !bUnitIsMm);// sz_buf_field4 is 270fAngle = atof(sz_buf_field4);// sz_buf_field5 is m // 正反面, 反面为m 正面为空memset(sz_buf_field5_new, 0, sizeof(sz_buf_field5_new));strcpy(sz_buf_field5_new, (0 == strcmp(sz_buf_field5, "m")) ? "BottomLayer"  : "TopLayer");// sz_buf_field6 is MICRO-USB-SMD_MICROXNJ// sz_buf_field7_new is value (Designator + Comment)memset(sz_buf_field7_new, 0, sizeof(sz_buf_field7_new));// sprintf(sz_buf_field6_new, "%s_%s", sz_buf_field1, sz_buf_field5);// 坐标文件本来就没有值, 留空吧, 看看能行不?// 拼新行写入新文件memset(sz_buf_write, 0, sizeof(sz_buf_write));//                                                      BottomLayer// "C1", "58.674mm", "7.239mm", "90.00", "NICHICON_A", "TopLayer", "10uF"sprintf(sz_buf_write, "\"%s\", \"%.3fmm\", \"%.3fmm\", \"%.2f\", \"%s\", \"%s\", \"%s\"\n",sz_buf_field1,fX,fY,fAngle,sz_buf_field5_new,sz_buf_field6,sz_buf_field7_new);fwrite(sz_buf_write, sizeof(char), strlen(sz_buf_write), fpFileNew);
}double mil2mm(double fMil, bool bIsMil)
{double fMm = fMil;if (bIsMil){// 100mil = 2.54mm//// 100mil / 2.54 = 2.54/2.54mm// 1mm = 100mil / 2.54//// 1mil = 2.54mm / 100//fMm = fMil * 2.54 / 100;}return fMm;
}

转换前的allego原版坐标文件

元件是有正反面的, 第5列是正反面。
如果是正面的元件,第5列为空,第6列为封装
如果是反面的元件,第5列为m(代表元件被镜像了),第6列为封装

UUNITS = MILS
J4                   157.00         899.00      270    MICRO-USB-SMD_MICROXNJ
TP1                  5809.00       3234.13        0    TEST_TH_PIN_1D0MM
TP10                 4051.00       2358.00        0    TEST_TH_PIN_1D0MM
TP11                 2670.00       3570.00        0    TEST_TH_PIN_1D0MM
TP12                 2820.00       3569.00        0    TEST_TH_PIN_1D0MM
TP2                  5810.00       3080.00        0    TEST_TH_PIN_1D0MM
TP3                  2020.00       2857.00        0    TEST_TH_PIN_1D0MM
TP4                  1870.00       2860.00        0    TEST_TH_PIN_1D0MM
TP5                  1870.00       1445.35        0    TEST_TH_PIN_1D0MM
TP6                  2015.00       1448.00        0    TEST_TH_PIN_1D0MM
TP7                  3286.59        200.00        0    TEST_TH_PIN_1D0MM
TP8                  3430.00        200.00        0    TEST_TH_PIN_1D0MM
TP9                  3904.00       2357.00        0    TEST_TH_PIN_1D0MM
M1                   3807.00       3358.00      180    STC15_EXP_BOX4_MCU_BOARD_FP_V1
LOGO2                3780.00       1220.00        0  m LOGO_KRGY
LOGO1                1467.42        514.83        0    LOGO_STC15_BOX4
DW2                  139.00         611.00        0    DW_HOLE_D1R0MM_D2R0MM

转换后的openpnp格式的坐标文件

openpnp Named csv Pick and Place Locations
org file = allegro_org.txt
convert file = allegro_org.txt.csv
========================================================================================================================
File Design Information:
Units used: mm
"Designator","Ref-X(mm)","Ref-Y(mm)","Rotation","Layer", "Footprint","Comment"
"J4", "3.988mm", "22.835mm", "270.00", "TopLayer", "MICRO-USB-SMD_MICROXNJ", ""
"TP1", "147.549mm", "82.147mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP10", "102.895mm", "59.893mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP11", "67.818mm", "90.678mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP12", "71.628mm", "90.653mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP2", "147.574mm", "78.232mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP3", "51.308mm", "72.568mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP4", "47.498mm", "72.644mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP5", "47.498mm", "36.712mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP6", "51.181mm", "36.779mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP7", "83.479mm", "5.080mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP8", "87.122mm", "5.080mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"TP9", "99.162mm", "59.868mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", ""
"M1", "96.698mm", "85.293mm", "180.00", "TopLayer", "STC15_EXP_BOX4_MCU_BOARD_FP_V1", ""
"LOGO2", "96.012mm", "30.988mm", "0.00", "BottomLayer", "LOGO_KRGY", ""
"LOGO1", "37.272mm", "13.077mm", "0.00", "TopLayer", "LOGO_STC15_BOX4", ""

被openpnp载入后的效果

买的openpnp设备只支持一面贴片, 反面的元件被滤掉了。

只有一个LOGO2在反面, 载入后找不到LOGO2了。

发现的问题

AD导出的坐标文件有Comment, 连上封装,就可以确定一个唯一的料,e.g. LED_红色 或者 C0603_20pf
但是allegro导出的坐标文件没有Comment, 就是说,只是坐标文件(哪个位号摆在哪个位置,角度是多少),转换完了,我只能将Comment位置添为空, 因为也确实无法从allegro导出的坐标文件中得到一个位号是个啥元件…
这样就导致, 这个转换完的坐标文件, 无法正常贴片, 料栈上都是针对具体料的,每个元件再指定料栈.
等于说, 这么转换是废的,必须结合其他allegro文件, e.g. 料单来填写Comment的内容。

去ORCAD中出了一次料单,导出料单时, 多加了一个Part_Number的列。导出后, 可以另存为用’,'分隔的csv文件
如下:

Item Number,Quantity,Value,Description,Part Reference,Part_Number
1,1,BAT_CR1220-2,电池底座 适用电池:CR1220,BAT1,BAT_0001
2,3,47uF/20%/50V/SMD/6.3x7.7mm,贴片型铝电解电容 47uF ±20% 50V SMD 6.3x7.7mm,C1 C3 C7,CAP_0001
3,17,100nF/10%/50V/0603,贴片电容(MLCC) 100nF ±10% 50V 0603,C2 C4 C5 C6 C9 C10 C11 C12 C13 C14 C19 C21 C22 C23 C24 C27 C28,CAP_0003
4,2,7pF/0.25pf/50V/0603,贴片电容(MLCC) 7pF ±0.25pF 50V 0603,C8 C32,CAP_0009
5,3,100uF/20%/50V/SMD/8x10mm,贴片型铝电解电容 100uF ±20% 50V SMD 8x10mm,C15 C20 C31,CAP_0002
6,1,10nF/10%/25V/0603,贴片电容(MLCC) 10nF ±10% 25V 0603,C16,CAP_0004
7,2,20pF/5%/50V/0603,贴片电容(MLCC) 20pF ±5% 50V  0603,C17 C18,CAP_0008
8,3,10uF/10%/25V/0805,贴片电容(MLCC) 10uF ±10% 25V  0805,C25 C26 C29,CAP_0006

我用了CIS库,每个Part_Number就代表唯一私有料号。
我还必须在上面程序的基础上,再从料单csv中,将坐标文件中每个元件的Part_Number和Vaule找出来, 拼好,填入openpnp用的csv的Comment字段中。
看来必须要这样才行(使openpnp导入坐标文件后可以区分中每一种料对应的多个元件)。
明天再弄吧,到处都是坑。

orcad导出料单


执行后, 会用WPS打开生成好的料单.

另存为一种CSV格式文件到本地供分析.


现在, 将BOM文件中的Value, Part_Number提取出来, 填充到openpnp坐标文件对应元件的Comment中.
就在昨天的工程上升级一下就好.

程序维护完成,好使

改完的程序不到800行, 如下

// @file openpnp_named_cvs_convert.cpp
// @brief 转换SPB17.4 allegro导出的坐标文件(新格式) + orcad BOM单 转换为openpnp可导入的坐标文件
// @note 编译环境 vs2022 vc++ console#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>#include <list>
#include <string>#define OPENPNP_NAMED_CSV_FILE "openpnp_named_csv.csv"// 2,3,47uF/20%/50V/SMD/6.3x7.7mm,47uF/±20%/50V/SMD/6.3x7.7mm,贴片型铝电解电容 47uF ±20% 50V SMD 6.3x7.7mm,C1 C3 C7,CAP_0001
typedef struct _tag_bom_item {char sz_ItemNumber[0x100];int i_Quantity;char sz_Value[0x100];char sz_Value_BOM[0x100];char sz_Description[0x100];char sz_Part_Number[0x100];// Part Referencestd::list<std::string> list_Part;}TAG_BOM_ITEM;std::list<TAG_BOM_ITEM*> g_list_bom_item; // 将BOM文件载入到这个list供查询
void clear_bom_list(void);void replace_char(char* pszSrc, char c_find, char c_new);
void replace_char_between_double_quotation_marks(char* pszSrc, char c_old, char c_new);void get_bom_part_sn(std::list<TAG_BOM_ITEM*>& list, char* psz_part_id, char** ppsz_field);void process_allegro_csv(const char* pszAllegroFile, const char* pszOrcadBomFile, const char* pszOpenpnpCsvFile);
void process_allegro_csv(const char* pFile, FILE* fpFile, const char* pFileNew);
void process_convert_head(const char* pFile, FILE* fpFile, const char* pFileNew, FILE* fpFileNew, bool bUnitFind, bool bUnitIsMm, bool bUnitIsMil);
void process_convert_body(FILE* fpFileNew, bool bUnitIsMm, char* sz_buf_field1, char* sz_buf_field2, char* sz_buf_field3, char* sz_buf_field4, char* sz_buf_field5, char* sz_buf_field6);
double mil2mm(double fMil, bool bIsMil);bool process_orcad_bom_file(const char* pszOrcadBomFile);int main(int argc, char** argv)
{char* pszAllegroFile = NULL; // allegro 坐标文件char* pszOrcadBomFile = NULL; // orcad 料单char sz_openpnp_file[256]; // 转换完成后的openpnp坐标文件do {if (3 != argc){// 测试用的 allegro导出的原始坐标文件为 allegro_org.txtprintf("请给出要转换的csv文件名称\r\n");printf("usage : openpnp_named_cvs_convert allegro_org.txt BOM_orcad_org.csv\r\n");printf("allegro_org.txt 是allegro导出的坐标文件\r\n");printf("BOM_orcad_org.csv 是orcad导出的料单\r\n");printf("%s 是转换后的openpnp可导入的最终坐标文件\r\n", OPENPNP_NAMED_CSV_FILE);break;}if (NULL == argv[1]){printf("err : 给定的Allegro坐标文件名称为空\r\n");break;}pszAllegroFile = argv[1];printf("要处理的Allegro坐标文件为 : %s\r\n", pszAllegroFile);if (NULL == argv[2]){printf("err : 给定的Orcad料单文件名称为空\r\n");break;}pszOrcadBomFile = argv[2];printf("要处理的Orcad料单文件为 : %s\r\n", pszOrcadBomFile);memset(sz_openpnp_file, 0, sizeof(sz_openpnp_file));sprintf(sz_openpnp_file, "%s", OPENPNP_NAMED_CSV_FILE);printf("预期转换完的openpnp坐标文件为 : %s\r\n", sz_openpnp_file);process_allegro_csv(pszAllegroFile, pszOrcadBomFile, sz_openpnp_file);printf("处理结束 : 请查看转换完的openpnp坐标文件 %s 是否处理正确\r\n", sz_openpnp_file);} while (0);clear_bom_list();return EXIT_SUCCESS;
}void process_allegro_csv(const char* pszAllegroFile, const char* pszOrcadBomFile, const char* pszOpenpnpCsvFile)
{FILE* fpAllegroFile = NULL;do {// 在编译选项(预处理器)中加入 _CRT_SECURE_NO_WARNINGS 宏, 防止编译警告fpAllegroFile = fopen(pszAllegroFile, "r");if (NULL == fpAllegroFile){printf("err : Allegro坐标文件(%s)打开失败\r\n", pszAllegroFile);break;}if (!process_orcad_bom_file(pszOrcadBomFile)){printf("err : 处理orcad BOM文件(%s)失败\r\n", pszOrcadBomFile);break;}process_allegro_csv(pszAllegroFile, fpAllegroFile, pszOpenpnpCsvFile);} while (0);if (NULL != fpAllegroFile){fclose(fpAllegroFile);fpAllegroFile = NULL;}
}void process_allegro_csv(const char* pFile, FILE* fpFile, const char* pFileNew)
{char sz_buf_rd[1024];size_t nRdCnt = 0;char* pFind = NULL;char sz_buf_field1[0x100];char sz_buf_field2[0x100];char sz_buf_field3[0x100];char sz_buf_field4[0x100];char sz_buf_field5[0x100];char sz_buf_field6[0x100];int iCnt = 0;bool bUnitFind = false;bool bUnitIsMm = false;bool bUnitIsMil = false;FILE* fpFileNew = NULL;do {if (NULL == pFileNew){break;}if (NULL == fpFileNew){fpFileNew = fopen(pFileNew, "w");if (NULL == fpFileNew){printf("建立新文件(%s)失败\r\n", pFileNew);break;}}memset(sz_buf_rd, 0, sizeof(sz_buf_rd));pFind = fgets(sz_buf_rd, sizeof(sz_buf_rd), fpFile);if (NULL == pFind){printf("alegro坐标文件 : 读取完毕\r\n");break;}if (!bUnitFind){// 查找 UUNITS = MILSpFind = strstr(sz_buf_rd, "UUNITS = ");if (NULL == pFind){continue;}memset(sz_buf_field1, 0, sizeof(sz_buf_field1));iCnt = sscanf(sz_buf_rd, "UUNITS = %255s", sz_buf_field1); // MILSif (iCnt <= 0){continue;}if (0 == strcmp(sz_buf_field1, "MILS")){// 单位是milbUnitFind = true;bUnitIsMm = false;bUnitIsMil = true;}else if (0 == strcmp(sz_buf_field1, "MM")){// 单位是mmbUnitFind = true;bUnitIsMm = true;bUnitIsMil = false;}else {printf("err : 文件格式不对, 单位应该是mil和mm其中一个\r\n");break;}// 写处理完的csv的注释部分process_convert_head(pFile, fpFile, pFileNew, fpFileNew, bUnitFind, bUnitIsMm, bUnitIsMil);}else {// 查找 J4                   157.00         899.00      270    MICRO-USB-SMD_MICROXNJ//// 如果有背面元件时, 就有6列, 会在第5列多出一个镜像的m字符, 第6列是封装// 如果是正面的元件, 就只有5列// LOGO2                3780.00       1220.00        0  m LOGO_KRGY// LOGO1                1467.42        514.83        0    LOGO_STC15_BOX4memset(sz_buf_field1, 0, sizeof(sz_buf_field1));memset(sz_buf_field2, 0, sizeof(sz_buf_field2));memset(sz_buf_field3, 0, sizeof(sz_buf_field3));memset(sz_buf_field4, 0, sizeof(sz_buf_field4));memset(sz_buf_field5, 0, sizeof(sz_buf_field5));memset(sz_buf_field6, 0, sizeof(sz_buf_field6));iCnt = sscanf(sz_buf_rd, "%s %s %s %s %s %s", sz_buf_field1,sz_buf_field2,sz_buf_field3,sz_buf_field4,sz_buf_field5,sz_buf_field6); // MILSif ((iCnt < 5) && (iCnt > 6)){continue;}if (5 == iCnt){// 是正面元件, 第5列为空, 只有第6列为封装strcpy(sz_buf_field6, sz_buf_field5);memset(sz_buf_field5, 0, sizeof(sz_buf_field5));}else if (6 == iCnt) {// 是反面面元件, 第5列为m, 第6列为封装}process_convert_body(fpFileNew, bUnitIsMm, sz_buf_field1, sz_buf_field2, sz_buf_field3, sz_buf_field4, sz_buf_field5, sz_buf_field6);}} while (1);if (NULL != fpFileNew){fclose(fpFileNew);fpFileNew = NULL;}
}void process_convert_head(const char* pFile, FILE* fpFile, const char* pFileNew, FILE* fpFileNew, bool bUnitFind, bool bUnitIsMm, bool bUnitIsMil)
{// 在文件的前10行(只能是前10行, openpnp有规定)写入一些注释(e.g. 我是谁? 我在哪?)/*Altium Designer Pick and Place LocationsZ:\test\Pick Place for NEW_PCB_2022-11-12.csv========================================================================================================================File Design Information:Date:       12/11/22Time:       13:42Revision:   Not in VersionControlVariant:    No variationsUnits used: mm*/char sz_buf[1024];do {if ((NULL == fpFile) || (NULL == fpFile) || (!bUnitFind)){break;}// bool bUnitIsMm, bool bUnitIsMilif (!bUnitIsMm && !bUnitIsMil){// mm 和 mil 单位必须有一个为truebreak;}// line 1memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "openpnp Named csv Pick and Place Locations\n"); // 以w方式打开的文件, 换行的话,\n就行了fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 2memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "org file = %s\n", pFile);fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 3memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "convert file = %s\n", pFileNew);fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 4memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "========================================================================================================================\n");fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 5memset(sz_buf, 0, sizeof(sz_buf));sprintf(sz_buf, "File Design Information:\n");fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 6memset(sz_buf, 0, sizeof(sz_buf));// Units used: mmsprintf(sz_buf, "Units used: mm\n"); // openpnp用的坐标文件, 必须是mmfwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);// line 7// J4                   157.00         899.00      270    MICRO-USB-SMD_MICROXNJ// "Designator","X (mm)","Y (mm)","Rotation","Footprint","Comment"// "Designator","Comment","Footprint","Rotation","Ref-X(mm)","Ref-Y(mm)" // 这个是用AD出的坐标文件整理后的可用题头// "Designator", "Comment", "Layer", "Footprint", "Center-X(mm)", "Center-Y(mm)", "Rotation", "Description", "Ref-X(mm)", "Ref-Y(mm)", "Pad-X(mm)", "Pad-Y(mm)"// "U1", "BAT-SMD_CR1220-2", "TopLayer", "BAT-SMD_CR1220-2", "58.2270mm", "68.2269mm", "0", "", "58.2270mm", "68.2269mm", "50.2270mm", "68.2269mm"// 发现allegro导出的坐标文件, 是分正反面的, 所以要加上Layer字段memset(sz_buf, 0, sizeof(sz_buf));// 前6列是allegro导出的坐标文件就有的, 第7列是拼装的(位号 + 封装), 或留空(因为allegro导出的坐标文件,本来就没有元件值)//                 列1            列2           列3           列4          列5        列6           列7sprintf(sz_buf, "\"Designator\",\"Ref-X(mm)\",\"Ref-Y(mm)\",\"Rotation\",\"Layer\", \"Footprint\",\"Comment\"\n");fwrite(sz_buf, sizeof(char), strlen(sz_buf), fpFileNew);} while (0);
}void process_convert_body(FILE* fpFileNew, bool bUnitIsMm, char* sz_buf_field1, char* sz_buf_field2, char* sz_buf_field3, char* sz_buf_field4, char* sz_buf_field5, char* sz_buf_field6)
{// 原始// "Designator","X (mm)","Y (mm)","Rotation","Footprint","Layer","Comment"// J4                   157.00         899.00      270    m       MICRO-USB-SMD_MICROXNJ// 转换之后// "C1", "58.674mm", "7.239mm", "90.00", "NICHICON_A", "BottomLayer", "10uF"// "C2", "58.674mm", "7.239mm", "90.00", "NICHICON_A", "TopLayer", "10uF"char sz_buf_field5_new[0x100];char sz_buf_field7_new[0x100];char sz_buf_write[1024];double fTmp = 0;double fX = 0;double fY = 0;double fAngle = 0;char* psz_tmp = NULL;// sz_buf_field1 is J4// sz_buf_field2 is 157.00fTmp = atof(sz_buf_field2);fX = mil2mm(fTmp, !bUnitIsMm);// sz_buf_field3 is 899.00fTmp = atof(sz_buf_field3);fY = mil2mm(fTmp, !bUnitIsMm);// sz_buf_field4 is 270fAngle = atof(sz_buf_field4);// sz_buf_field5 is m // 正反面, 反面为m 正面为空memset(sz_buf_field5_new, 0, sizeof(sz_buf_field5_new));strcpy(sz_buf_field5_new, (0 == strcmp(sz_buf_field5, "m")) ? "BottomLayer"  : "TopLayer");// sz_buf_field6 is MICRO-USB-SMD_MICROXNJ// sz_buf_field7_new is value (Designator + Comment)memset(sz_buf_field7_new, 0, sizeof(sz_buf_field7_new));// sprintf(sz_buf_field6_new, "%s_%s", sz_buf_field1, sz_buf_field5);// 坐标文件本来就没有值, 留空吧, 看看能行不?// 从bom文件中得到位号对应的私有料号// sz_buf_field1 is J4psz_tmp = sz_buf_field7_new;get_bom_part_sn(g_list_bom_item, sz_buf_field1, &psz_tmp);// 拼新行写入新文件memset(sz_buf_write, 0, sizeof(sz_buf_write));//                                                      BottomLayer// "C1", "58.674mm", "7.239mm", "90.00", "NICHICON_A", "TopLayer", "10uF"sprintf(sz_buf_write, "\"%s\", \"%.3fmm\", \"%.3fmm\", \"%.2f\", \"%s\", \"%s\", \"%s\"\n",sz_buf_field1,fX,fY,fAngle,sz_buf_field5_new,sz_buf_field6,sz_buf_field7_new);fwrite(sz_buf_write, sizeof(char), strlen(sz_buf_write), fpFileNew);
}double mil2mm(double fMil, bool bIsMil)
{double fMm = fMil;if (bIsMil){// 100mil = 2.54mm//// 100mil / 2.54 = 2.54/2.54mm// 1mm = 100mil / 2.54//// 1mil = 2.54mm / 100//fMm = fMil * 2.54 / 100;}return fMm;
}void replace_char(char* pszSrc, char c_find, char c_new)
{char* psz_find = NULL;do {if (NULL == pszSrc){break;}psz_find = pszSrc;do {psz_find = strchr(psz_find, c_find);if (NULL == psz_find){break;}*psz_find = c_new;psz_find++;} while (true);} while (false);
}void replace_char_between_double_quotation_marks(char* pszSrc, char c_old, char c_new)
{char* psz_Head = NULL;char* psz_first_double_quotation = NULL;char* psz_next_double_quotation = NULL;char* psz_cur = NULL;do {if (NULL == pszSrc){break;}psz_Head = pszSrc;do {psz_first_double_quotation = strchr(psz_Head, '"');if (NULL == psz_first_double_quotation){break;}psz_Head = psz_first_double_quotation + 1;psz_next_double_quotation = strchr(psz_Head, '"');if (NULL == psz_next_double_quotation){break;}for (psz_cur = (psz_first_double_quotation + 1); psz_cur <= (psz_next_double_quotation - 1); psz_cur++){if (*psz_cur == c_old){*psz_cur = c_new;}}psz_Head = psz_next_double_quotation + 1;} while (true);} while (false);
}void update_part_items(std::list<std::string>& list, char* psz_item)
{// psz_item like C1_C3_C7char* psz_Head = NULL;char* psz_find = NULL;char sz_buf[4096];list.clear();memset(sz_buf, 0, sizeof(sz_buf));strcpy(sz_buf, psz_item);replace_char(sz_buf, '_', ' ');psz_Head = sz_buf;do {psz_find = strchr(psz_Head, ' ');if (NULL == psz_find){list.push_back(psz_Head);break;}else {*psz_find = '\0';list.push_back(psz_Head);psz_find++;psz_Head = psz_find;}} while (true);
}bool process_orcad_bom_file(const char* pszOrcadBomFile)
{/*Item Number,Quantity,Value,Value_BOM,Description,Part Reference,Part_Number1,1,BAT_CR1220-2,电池座_CR1220-2,电池底座 适用电池:CR1220,BAT1,BAT_00012,3,47uF/20%/50V/SMD/6.3x7.7mm,47uF/±20%/50V/SMD/6.3x7.7mm,贴片型铝电解电容 47uF ±20% 50V SMD 6.3x7.7mm,C1 C3 C7,CAP_0001*/bool isProcessOk = false;FILE* pfOrcadBomFile = NULL;bool bFindHead = false;char sz_buf_rd[1024 * 8];char* pFind = NULL;// Item Number,Quantity,Value,Value_BOM,Description,Part Reference,Part_Number// 1,1,BAT_CR1220-2,电池座_CR1220-2,电池底座 适用电池:CR1220,BAT1,BAT_0001char sz_buf_field1[1024];char sz_buf_field2[1024];char sz_buf_field3[1024];char sz_buf_field4[1024];char sz_buf_field5[1024];char sz_buf_field6[1024];char sz_buf_field7[1024];int iCnt = 0;bool isNeedBreak = false;bool b_head_item_ok_field1 = false;bool b_head_item_ok_field2 = false;bool b_head_item_ok_field3 = false;bool b_head_item_ok_field4 = false;bool b_head_item_ok_field5 = false;bool b_head_item_ok_field6 = false;bool b_head_item_ok_field7 = false;TAG_BOM_ITEM* pBomItem = NULL;int i_debug_cnt = 0;do {if (NULL == pszOrcadBomFile){printf("err : orcad BOM文件名为空\r\n");break;}pfOrcadBomFile = fopen(pszOrcadBomFile, "r");if (NULL == pfOrcadBomFile){printf("err : 打开文件(%s)失败\r\n", pszOrcadBomFile);break;}do {i_debug_cnt++;if (21 == i_debug_cnt){i_debug_cnt--;i_debug_cnt++;}memset(sz_buf_rd, 0, sizeof(sz_buf_rd));pFind = fgets(sz_buf_rd, sizeof(sz_buf_rd), pfOrcadBomFile);if (NULL == pFind){isNeedBreak = true;printf("orcad bom 文件 : 读取完毕\r\n");break;}memset(sz_buf_field1, 0, sizeof(sz_buf_field1));memset(sz_buf_field2, 0, sizeof(sz_buf_field2));memset(sz_buf_field3, 0, sizeof(sz_buf_field3));memset(sz_buf_field4, 0, sizeof(sz_buf_field4));memset(sz_buf_field5, 0, sizeof(sz_buf_field5));memset(sz_buf_field6, 0, sizeof(sz_buf_field6));memset(sz_buf_field7, 0, sizeof(sz_buf_field7));// Item Number,Quantity,Value,Value_BOM,Description,Part Reference,Part_Number// Item Number,Quantity,Value,Value_BOM,Description,Part Reference,Part_Number\n// 替换双引号之间的字符(e.g. ','), 因为分隔符号是',', 所以内容中不能有','replace_char_between_double_quotation_marks(sz_buf_rd, ',', '_');// replace_char_between_double_quotation_marks(sz_buf_rd, ' ', '_');replace_char(sz_buf_rd, ' ', '_'); // sscanf 不支持内容中间为空格, 换成'_'replace_char(sz_buf_rd, ',', ' '); // sscanf 不支持','作为分隔符号, 换成' 'iCnt = sscanf(sz_buf_rd, "%s %s %s %s %s %s %s",sz_buf_field1,sz_buf_field2,sz_buf_field3,sz_buf_field4,sz_buf_field5,sz_buf_field6,sz_buf_field7);if (iCnt <= 0){continue;}if (7 != iCnt){isNeedBreak = true;isProcessOk = false;printf("err : 文件格式不符合预期\r\n");break;}if (!bFindHead){// Item Number,Quantity,Value,Value_BOM,Description,Part Reference,Part_Numberb_head_item_ok_field1 = (0 == strcmp(sz_buf_field1, "Item_Number"));b_head_item_ok_field2 = (0 == strcmp(sz_buf_field2, "Quantity"));b_head_item_ok_field3 = (0 == strcmp(sz_buf_field3, "Value"));b_head_item_ok_field4 = (0 == strcmp(sz_buf_field4, "Value_BOM"));b_head_item_ok_field5 = (0 == strcmp(sz_buf_field5, "Description"));b_head_item_ok_field6 = (0 == strcmp(sz_buf_field6, "Part_Reference"));b_head_item_ok_field7 = (0 == strcmp(sz_buf_field7, "Part_Number"));if (!b_head_item_ok_field1 ||!b_head_item_ok_field2 ||!b_head_item_ok_field3 ||!b_head_item_ok_field4 ||!b_head_item_ok_field5 ||!b_head_item_ok_field6 ||!b_head_item_ok_field7){isNeedBreak = true;isProcessOk = false;printf("err : 文件格式(题头)不符合预期\r\n");break;}bFindHead = true;}else {pBomItem = new TAG_BOM_ITEM;if (NULL != pBomItem){strcpy(pBomItem->sz_ItemNumber, sz_buf_field1); // "Item Number"pBomItem->i_Quantity = atoi(sz_buf_field2); // "Quantity"strcpy(pBomItem->sz_Value, sz_buf_field3); // "Value"strcpy(pBomItem->sz_Value_BOM, sz_buf_field4); // "Value_BOM"strcpy(pBomItem->sz_Description, sz_buf_field5); // "Description"strcpy(pBomItem->sz_Part_Number, sz_buf_field7); // "Part_Number"// C1_C3_C7update_part_items(pBomItem->list_Part, sz_buf_field6); // 将元件位号取出来, 存入listg_list_bom_item.push_back(pBomItem);}isProcessOk = true;}} while (!isNeedBreak);} while (0);if (NULL != pfOrcadBomFile){fclose(pfOrcadBomFile);pfOrcadBomFile = NULL;}return isProcessOk;
}void clear_bom_list(void)
{// std::list<TAG_BOM_ITEM*> g_list_bom_item; // 将BOM文件载入到这个list供查询std::list<TAG_BOM_ITEM*>::iterator it;TAG_BOM_ITEM* pItem = NULL;while (g_list_bom_item.size() > 0){it = g_list_bom_item.begin();pItem = *it;g_list_bom_item.pop_front();if (NULL != pItem){delete pItem;pItem = NULL;}}}void get_bom_part_sn(std::list<TAG_BOM_ITEM*>& list, char* psz_part_id, char** ppsz_field)
{std::list<TAG_BOM_ITEM*>::iterator it;std::list<std::string>::iterator it_part_sn;bool b_find = false;// 2,3,47uF/20%/50V/SMD/6.3x7.7mm,47uF/±20%/50V/SMD/6.3x7.7mm,贴片型铝电解电容 47uF ±20% 50V SMD 6.3x7.7mm,C1 C3 C7,CAP_0001for (it = list.begin(); it != list.end(); it++){for (it_part_sn = (*it)->list_Part.begin(); it_part_sn != (*it)->list_Part.end(); it_part_sn++){if (0 == (*it_part_sn).compare(psz_part_id)){strcpy(*ppsz_field, (*it)->sz_Part_Number); // 将私有料号放前面, 在openpnp料站中看的更清楚strcat(*ppsz_field, "_");strcat(*ppsz_field, (*it)->sz_Value);// ppsz_field like "CON_0006_usb_skt_smd_MICROXNJ1"b_find = true;break;}}if (b_find){break;}}
}

产生的适合openpnp的坐标文件openpnp_named_csv.csv, 样例如下

openpnp Named csv Pick and Place Locations
org file = allegro_org.txt
convert file = openpnp_named_csv.csv
========================================================================================================================
File Design Information:
Units used: mm
"Designator","Ref-X(mm)","Ref-Y(mm)","Rotation","Layer", "Footprint","Comment"
"J4", "3.988mm", "22.835mm", "270.00", "TopLayer", "MICRO-USB-SMD_MICROXNJ", "CON_0006_usb_skt_smd_MICROXNJ1"
"TP1", "147.549mm", "82.147mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP10", "102.895mm", "59.893mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP11", "67.818mm", "90.678mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP12", "71.628mm", "90.653mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP2", "147.574mm", "78.232mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP3", "51.308mm", "72.568mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP4", "47.498mm", "72.644mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP5", "47.498mm", "36.712mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP6", "51.181mm", "36.779mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP7", "83.479mm", "5.080mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP8", "87.122mm", "5.080mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"TP9", "99.162mm", "59.868mm", "0.00", "TopLayer", "TEST_TH_PIN_1D0MM", "M_0010_test_th_pin_1d0mm"
"M1", "96.698mm", "85.293mm", "180.00", "TopLayer", "STC15_EXP_BOX4_MCU_BOARD_FP_V1", "MODULE_0001_STC15_EXP_BOX4_IF_2.54mmx11x2socket_C132126_2cnt"
"LOGO2", "96.012mm", "30.988mm", "0.00", "BottomLayer", "LOGO_KRGY", "M_0009_logo_krgy"
"LOGO1", "37.272mm", "13.077mm", "0.00", "TopLayer", "LOGO_STC15_BOX4", "M_0008_logo_stc15_box4"
"DW2", "3.531mm", "15.519mm", "0.00", "TopLayer", "DW_HOLE_D1R0MM_D2R0MM", "M_0003_dw_hole_1d0mm"

备注

下一步, 就可以向openpnp设备料站平台上放置物料(自动飞达编带料,固定短编带散料,托盘散料), 通过openpnp上位机软件, 对元件指定料站.

END

cadence SPB17.4 - export placement file to openpnp相关推荐

  1. cadence SPB17.4 - remove Outputs file from DSN project

    cadence SPB17.4 - remove Outputs file from DSN project 前言 以现存工程为基础, 开另外一个工程来迭代. 先另存工程 工程另存后, 一般会选上将参 ...

  2. cadence SPB17.4 - orcad - Capture CIS export BOM

    cadence SPB17.4 - orcad - Capture CIS export BOM 文章目录 cadence SPB17.4 - orcad - Capture CIS export B ...

  3. cadence SPB17.4 - allegro - Artwork will be rounded down

    cadence SPB17.4 - allegro - Artwork will be rounded down 前言 出装配图时,弹出如下警告对话框 具体提示信息如下: -------------- ...

  4. cadence SPB17.4 capture and cis 全部错误消息列表

    前言 在整理层次原理图,中间遇到好多编译警告和错误. 刚将封装之外的错误和警告都消掉了. 再想,官方对于错误消息有没有一个汇总和解释呢? 在本地帮助文件中找到了, capture的所有错误消息说明文件 ...

  5. cadence SPB17.4 - allegro - Allegro2Altium.bat 初探

    文章目录 cadence SPB17.4 - allegro - Allegro2Altium.bat 初探 概述 笔记 查找Allegro2Altium.bat的位置 查找命令文件 AllegroE ...

  6. cadence SPB17.4 - allegro - allegro_free_viewer

    cadence SPB17.4 - allegro - allegro_free_viewer 前言 想看下板子连通后, 每条网络走线是否合理. 当然可以在cadenceSPB17.4_PCB Edi ...

  7. cadence SPB17.4 - CIS - add a new part

    文章目录 cadence SPB17.4 - CIS - add a new part 概述 笔记 先备份自己的SPB17.4 看一下自己环境中的SPB17.4的原理图库路径, PCB封装库路径等信息 ...

  8. cadence SPB17.4 - allegro - 手工放置原理图没有的封装

    cadence SPB17.4 - allegro - 手工放置原理图没有的封装 前言 假设要在拼版中,放置mark点.那原理图中不可能有的. 或者就是要在成品PCB上,单独放一个新的PCB元件封装. ...

  9. cadence SPB17.4 - allegro - 做logo封装

    cadence SPB17.4 - allegro - 做logo封装 前言 板子铺好地铜了, 准备在顶层丝印层放个logo. 因为allegro是不支持中文的, 查资料, 都是导入dxf或者plt文 ...

  10. cadence SPB17.4 - allegro添加过孔

    cadence SPB17.4 - allegro添加过孔 前言 尝试放过孔数组,居然将allegro(SPB17.4 S026)搞成无响应了(不知道是哪操作不对,等多久都没反应),只能强行关闭all ...

最新文章

  1. C++ 对引用的深入理解
  2. 【行业报告】中国金融科技2017专题研究报告——易观智库
  3. 【费用流】BZOJ1061: [Noi2008]志愿者招募(这题超好)
  4. 专栏订阅须知《必读》
  5. php 扩展 suhosin 配置不当引发的报错及其解决方法
  6. 理论基础 —— 索引 —— 2-3 树
  7. gravatar配置和使用【让你的网站使用全球通用头像】
  8. python爬取淘宝网页信息
  9. Effective C# 学习笔记 (一)
  10. opencv C++ 旋转任意角度图片
  11. 对接阿里云天气,获取天气预报数据
  12. 字节版 趣味测试小程序源码
  13. JQuery插件机制
  14. TestFlight上架流程
  15. 图神经网络系列(一)介绍及基本模型
  16. 【历史上的今天】10 月 29 日:互联网的正式诞生;MariaDB 发布首个版本;“天河一号”研制成功
  17. matlab 矩阵分解
  18. .com .org等域名/网址的理解
  19. JAMA Neurology:帕金森病跨疾病阶段的新兴神经成像生物标记物
  20. 实现二进制数到十进制的转换#

热门文章

  1. omnipeek无线抓包工具 Dlink 160驱动
  2. Google Code 开源项目
  3. STC8H开发(十四): I2C驱动RX8025T高精度实时时钟芯片
  4. 带温度补偿RTC芯片的选型和应用
  5. (2016.12.02更新)CnCrypt文件保险柜1.18,兼容TrueCrypt加密卷,单文件绿色版
  6. 软件测试基础理论全集
  7. VM虚拟机装Windows XP系统
  8. layabox 打印_Layabox 集成指南
  9. JVM07 - 方法区
  10. Trime同文输入法