109 lines
3.2 KiB
Go
109 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// GerberJob represents a KiCad .gbrjob file
|
|
type GerberJob struct {
|
|
Header struct {
|
|
GenerationSoftware struct {
|
|
Vendor string `json:"Vendor"`
|
|
Application string `json:"Application"`
|
|
Version string `json:"Version"`
|
|
} `json:"GenerationSoftware"`
|
|
} `json:"Header"`
|
|
GeneralSpecs struct {
|
|
ProjectId struct {
|
|
Name string `json:"Name"`
|
|
} `json:"ProjectId"`
|
|
Size struct {
|
|
X float64 `json:"X"`
|
|
Y float64 `json:"Y"`
|
|
} `json:"Size"`
|
|
BoardThickness float64 `json:"BoardThickness"`
|
|
} `json:"GeneralSpecs"`
|
|
FilesAttributes []struct {
|
|
Path string `json:"Path"`
|
|
FileFunction string `json:"FileFunction"`
|
|
FilePolarity string `json:"FilePolarity"`
|
|
} `json:"FilesAttributes"`
|
|
}
|
|
|
|
// GerberJobResult contains the auto-discovered file assignments
|
|
type GerberJobResult struct {
|
|
ProjectName string
|
|
BoardWidth float64 // mm
|
|
BoardHeight float64 // mm
|
|
BoardThickness float64 // mm
|
|
EdgeCutsFile string // Profile
|
|
FabFile string // AssemblyDrawing,Top
|
|
CourtyardFile string // matches courtyard naming
|
|
SoldermaskFile string // matches mask naming
|
|
}
|
|
|
|
// ParseGerberJob parses a .gbrjob file and returns auto-discovered file mappings
|
|
func ParseGerberJob(filename string) (*GerberJobResult, error) {
|
|
data, err := os.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read gbrjob: %w", err)
|
|
}
|
|
|
|
var job GerberJob
|
|
if err := json.Unmarshal(data, &job); err != nil {
|
|
return nil, fmt.Errorf("parse gbrjob JSON: %w", err)
|
|
}
|
|
|
|
result := &GerberJobResult{
|
|
ProjectName: job.GeneralSpecs.ProjectId.Name,
|
|
BoardWidth: job.GeneralSpecs.Size.X,
|
|
BoardHeight: job.GeneralSpecs.Size.Y,
|
|
BoardThickness: job.GeneralSpecs.BoardThickness,
|
|
}
|
|
|
|
// Map FileFunction to our layer types
|
|
for _, f := range job.FilesAttributes {
|
|
fn := strings.ToLower(f.FileFunction)
|
|
path := f.Path
|
|
|
|
switch {
|
|
case fn == "profile":
|
|
result.EdgeCutsFile = path
|
|
case strings.HasPrefix(fn, "assemblydrawing"):
|
|
// F.Fab = AssemblyDrawing,Top
|
|
if strings.Contains(fn, "top") {
|
|
result.FabFile = path
|
|
}
|
|
}
|
|
|
|
// Also match by filename patterns for courtyard/mask
|
|
lp := strings.ToLower(path)
|
|
switch {
|
|
case strings.Contains(lp, "courtyard") || strings.Contains(lp, "courty"):
|
|
if strings.Contains(lp, "f_courty") || strings.Contains(lp, "f.courty") || strings.Contains(lp, "-f_courty") {
|
|
result.CourtyardFile = path
|
|
}
|
|
case strings.Contains(lp, "mask") || strings.Contains(lp, "f_mask") || strings.Contains(lp, "f.mask"):
|
|
if strings.Contains(lp, "f_mask") || strings.Contains(lp, "f.mask") || strings.Contains(lp, "-f_mask") {
|
|
result.SoldermaskFile = path
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Printf("GerberJob: project=%s, board=%.1fx%.1fmm, thickness=%.1fmm\n",
|
|
result.ProjectName, result.BoardWidth, result.BoardHeight, result.BoardThickness)
|
|
fmt.Printf(" EdgeCuts: %s\n", result.EdgeCutsFile)
|
|
fmt.Printf(" F.Fab: %s\n", result.FabFile)
|
|
fmt.Printf(" F.Courtyard: %s\n", result.CourtyardFile)
|
|
fmt.Printf(" F.Mask: %s\n", result.SoldermaskFile)
|
|
|
|
if result.EdgeCutsFile == "" {
|
|
return nil, fmt.Errorf("no Edge.Cuts (Profile) layer found in gbrjob")
|
|
}
|
|
|
|
return result, nil
|
|
}
|