Former/gbrjob.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
}