第一个可用版本.

Signed-off-by: lion.chan <cy187lion@sina.com>
This commit is contained in:
lion.chan 2023-04-04 21:19:29 +08:00
parent 43e18d7dfe
commit 97e941e548
5 changed files with 239 additions and 1 deletions

View File

@ -1,3 +1,24 @@
# kiCad2JlcSMT
将 KiCAD 输出的 BOM 文件和原件放置(POS)文件, 转换为嘉立创 SMT 可用的格式.
将 KiCAD 7.0 输出的 BOM 文件和元件放置(POS)文件, 转换为嘉立创 SMT 可用的格式.
## 生成 BOM 和 POS 文件
需要使用从原理图工具导出的 BOM 文件和从 PCB 图工具生成的元件放置(POS)文件.
- BOM 的生成: 原理图工具->工具->生成 BOM->bom_csv_grouped_by_value_with_fp
- POS 的生成: PCB 工具->文件->制造输出->元件放置文件(.pos)
## kiCad2JlcSMT 工具的使用方法
Windows 下可以直接双击运行软件, 此时以命令行模式启动, 按提示输入 BOM 文件和 POS 文件的路径即可.
也可以通过 -t 参数指定是转换 BOM 还是转换 POS, 此时需要 -b 和 -p 来指定所使用的 BOM 或 POS 文件, 具体可传入 --help 查看.
## kiCad2JlcSMT 的编译
进入源码文件夹, 输入以下命令编译:
```bash
go build
```

75
bom.go Normal file
View File

@ -0,0 +1,75 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
func transBom(namei string) {
if namei == "" {
fmt.Println("Please input a KiCAD BOM CSV file.")
fmt.Scanln(&namei)
}
fmt.Println("Input from", namei)
namei = filepath.ToSlash(namei)
bi, e := os.ReadFile(namei)
if e != nil {
panic(e)
}
content := string(bi)
content = strings.ReplaceAll(content, "\r", "")
commentId, designatorId, footprintId, contents := findBomContents(content)
outStr := "\"Comment\",\"Designator\",\"Footprint\"\n"
outStr += transBomFormat(commentId, designatorId, footprintId, contents)
oname := filepath.Join(filepath.Dir(namei), "JLCBom.csv")
fmt.Println("Output to", oname)
os.WriteFile(oname, []byte(outStr), 0664)
fmt.Println("Done.")
fmt.Println("Done. Press any key to continue.")
fmt.Scanln(&namei)
}
func transBomFormat(commentId, designatorId, footprintId int, contents []string) string {
ostr := ""
for _, line := range contents {
lines := strings.Split(line, "\",")
if commentId < len(lines) && designatorId < len(lines) && footprintId < len(lines) {
footprint := transBomFootprint(lines[footprintId])
ostr += lines[commentId] + "\"," + lines[designatorId] + "\"," + footprint + "\"\n"
}
}
return ostr
}
func transBomFootprint(footprint string) string {
f, e := FootprintTransMap[footprint]
if e {
return f
}
return footprint
}
func findBomContents(content string) (commentId, designatorId, footprintId int, contents []string) {
contents = strings.Split(content, "\n")
for i, line := range contents {
if (strings.Contains(line, "\"Ref\"")) &&
(strings.Contains(line, "\"Value\"")) &&
(strings.Contains(line, "\"Footprint\"")) {
lines := strings.Split(line, ",")
for j, item := range lines {
if item == "\"Ref\"" {
designatorId = j
} else if item == "\"Value\"" {
commentId = j
} else if item == "\"Footprint\"" {
footprintId = j
}
}
contents = contents[i+1:]
return commentId, designatorId, footprintId, contents
}
}
return 0, 0, 0, nil
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module kiCad2JlcSMT
go 1.20

48
main.go Normal file
View File

@ -0,0 +1,48 @@
package main
import (
"flag"
"fmt"
)
var FootprintTransMap map[string]string = map[string]string{
"\"Capacitor_SMD:C_0603_1608Metric": "\"0603",
"\"Capacitor_SMD:C_0805_2012Metric": "\"0805",
"\"Capacitor_SMD:C_1206_3216Metric": "\"1206",
"\"Capacitor_SMD:C_1210_3225Metric": "\"1210",
"\"LED_SMD:LED_0603_1608Metric": "\"0603",
"\"Diode_SMD:D_SMA": "\"SMA",
"\"Diode_SMD:D_SOD-123": "\"SOD-123",
"\"Package_TO_SOT_SMD:SOT-23": "\"SOT-23",
"\"Package_TO_SOT_SMD:SOT-223-3_TabPin2": "\"SOT-223",
"\"Package_SO:SO-4_4.4x3.6mm_P2.54mm": "\"SOP-4",
"\"Crystal:Crystal_SMD_3225-4Pin_3.2x2.5mm": "\"SMD_3225-4P",
"\"Package_SO:SOIC-8_3.9x4.9mm_P1.27mm": "\"SOP-8",
"\"Resistor_SMD:R_0603_1608Metric": "\"0603",
"\"Resistor_SMD:R_1206_3216Metric": "\"1206",
"\"Resistor_SMD:R_1210_3225Metric": "\"1210",
}
func usage() {
fmt.Println("kiCad2JlcSMT v1.0.2")
flag.PrintDefaults()
}
func main() {
var nameB, nameP string
var t uint
flag.Usage = usage
flag.StringVar(&nameB, "b", "", "Input BOM CSV File")
flag.StringVar(&nameP, "p", "", "Input POS CSV File")
flag.UintVar(&t, "t", 2, "0-Bom, 1-Pos, other-All")
flag.Parse()
if t == 0 {
transBom(nameB)
} else if t == 1 {
transPos(nameP)
} else {
transBom(nameB)
transPos(nameP)
}
}

91
pos.go Normal file
View File

@ -0,0 +1,91 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
func transPos(namei string) {
if namei == "" {
fmt.Println("Please input a KiCAD POS CSV file.")
fmt.Scanln(&namei)
}
fmt.Println("Input from", namei)
namei = filepath.ToSlash(namei)
bi, e := os.ReadFile(namei)
if e != nil {
panic(e)
}
content := string(bi)
content = strings.ReplaceAll(content, "\r", "")
designatorId, xId, yId, layerId, rotationId, contents := findPosContents(content)
outStr := "\"Designator\",\"Mid X\",\"Mid Y\",\"Layer\",\"Rotation\"\n"
outStr += transPosFormat(designatorId, xId, yId, layerId, rotationId, contents)
oname := filepath.Join(filepath.Dir(namei), "JLCPos.csv")
fmt.Println("Output to", oname)
os.WriteFile(oname, []byte(outStr), 0664)
fmt.Println("Done.")
fmt.Println("Done. Press any key to continue.")
fmt.Scanln(&namei)
}
func transPosFormat(designatorId, xId, yId, layerId, rotationId int, contents []string) string {
ostr := ""
for _, line := range contents {
// 注意后面字段部分没有引号:"C1","0.1uF/10V","C_0603_1608Metric",154.498000,-107.311000,-90.000000,top
lines := strings.Split(line, "\",")
l := lines[len(lines)-1]
lines = lines[:len(lines)-1]
lines = append(lines, strings.Split(l, ",")...)
if designatorId < len(lines) &&
xId < len(lines) &&
yId < len(lines) &&
layerId < len(lines) &&
rotationId < len(lines) {
var layer string
switch lines[layerId] {
case "top":
layer = "T"
case "bottom":
layer = "B"
}
ostr += lines[designatorId] + "\"," +
"\"" + lines[xId] + "\"," +
"\"" + lines[yId] + "\"," +
"\"" + layer + "\"," +
"\"" + lines[rotationId] + "\"\n"
}
}
return ostr
}
func findPosContents(content string) (designatorId, xId, yId, layerId, rotationId int, contents []string) {
contents = strings.Split(content, "\n")
for i, line := range contents {
if (strings.Contains(line, "Ref")) &&
(strings.Contains(line, "PosX")) &&
(strings.Contains(line, "PosY")) &&
(strings.Contains(line, "Rot")) &&
(strings.Contains(line, "Side")) {
lines := strings.Split(line, ",")
for j, item := range lines {
if item == "Ref" {
designatorId = j
} else if item == "PosX" {
xId = j
} else if item == "PosY" {
yId = j
} else if item == "Side" {
layerId = j
} else if item == "Rot" {
rotationId = j
}
}
contents = contents[i+1:]
return designatorId, xId, yId, layerId, rotationId, contents
}
}
return 0, 0, 0, 0, 0, nil
}