Commit 385d856
Changed files (5)
internal
internal/cleanup.go
@@ -0,0 +1,20 @@
+package internal
+
+import "strings"
+
+func cleanupCircuit(c *xmlCircuit) {
+ for i := range c.Components {
+ c.Components[i].DefinitionID = strings.Trim(c.Components[i].DefinitionID, "{}")
+ c.Components[i].Props = nonemptyProps(c.Components[i].Props)
+ }
+}
+
+func nonemptyProps(props []xmlProp) []xmlProp {
+ var out []xmlProp
+ for _, p := range props {
+ if p.Value != "" {
+ out = append(out, p)
+ }
+ }
+ return out
+}
internal/component.go
@@ -7,6 +7,12 @@ import (
"strings"
)
+type Circuit struct {
+ Version string `yaml:"cddx2yamlVersion"`
+ CircuitDiagramVersion string `yaml:"circuitDiagramVersion"`
+ Components []Component `yaml:"components"`
+}
+
type Component struct {
ID string `yaml:"id"`
Type string `yaml:"type"`
@@ -15,70 +21,87 @@ type Component struct {
Pins []string `yaml:"pins,omitempty"`
}
-func propValue(props []xmlProp, key string) (string, bool) {
- for _, prop := range props {
- if prop.Key == key {
- return prop.Value, true
- }
+type typeCatalog map[string]string
+
+type idCounters map[string]int
+
+func documentFromXML(c *xmlCircuit) *Circuit {
+ types := make(typeCatalog, len(c.Adds))
+ for _, add := range c.Adds {
+ types[add.ID] = add.Item
}
- return "", false
-}
-func getLabel(items map[string]string, xmlComp xmlComponent) string {
- props := xmlComp.Props
- TP := xmlComp.TP
- label, hasHeader := propValue(props, "header")
- if label == "" || !hasHeader {
- label = items[TP]
+ counters := make(idCounters)
+ components := make([]Component, 0, len(c.Components))
+ for _, xc := range c.Components {
+ components = append(components, xc.toComponent(types, counters))
+ }
+ return &Circuit{
+ Version: "0.1",
+ CircuitDiagramVersion: c.Version,
+ Components: components,
}
- return label
}
-func getId(items map[string]string, counters map[string]int, xmlComp xmlComponent) string {
- id := strings.ToUpper(items[xmlComp.TP][:3])
- if items[xmlComp.TP] == "microcontroller" {
- id = "IC"
+func (types typeCatalog) name(definitionID string) string {
+ return types[definitionID]
+}
+
+func (counters idCounters) next(prefix string) string {
+ counters[prefix]++
+ return fmt.Sprintf("%s-%d", prefix, counters[prefix])
+}
+
+func idPrefix(typeName string) string {
+ if typeName == "microcontroller" {
+ return "IC"
+ }
+ if typeName == "" {
+ return "UNK"
}
- counters[id]++
- id = fmt.Sprintf("%s-%d", id, counters[id])
- return id
+ if len(typeName) < 3 {
+ return strings.ToUpper(typeName)
+ }
+ return strings.ToUpper(typeName[:3])
}
-func getValue(props []xmlProp) string {
- value, _ := propValue(props, "value")
+func (c xmlComponent) toComponent(types typeCatalog, counters idCounters) Component {
+ typeName := types.name(c.DefinitionID)
+ label := c.label(types)
+ if typeName == label {
+ label = ""
+ }
+ return Component{
+ ID: counters.next(idPrefix(typeName)),
+ Type: typeName,
+ Label: label,
+ Value: c.electricalValue(),
+ Pins: c.pins(),
+ }
+}
- listOfKeys := []string{"resistance", "inductance", "frequency", "voltage", "capacitance"}
+func (c xmlComponent) label(types typeCatalog) string {
+ if v, ok := c.prop("header"); ok && v != "" {
+ return v
+ }
+ return types.name(c.DefinitionID)
+}
- for _, key := range listOfKeys {
- if v, ok := propValue(props, key); ok {
+func (c xmlComponent) electricalValue() string {
+ value, _ := c.prop("value")
+ for _, key := range []string{"resistance", "inductance", "frequency", "voltage", "capacitance"} {
+ if v, ok := c.prop(key); ok {
value = v
}
}
return value
}
-func ConvertToComponent(counters map[string]int, items map[string]string, xmlComp xmlComponent) Component {
-
- label := getLabel(items, xmlComp)
- id := getId(items, counters, xmlComp)
- value := getValue(xmlComp.Props)
-
+func (c xmlComponent) pins() []string {
pinByIndex := make(map[int]string)
- for _, p := range xmlComp.Props {
- if !strings.HasPrefix(p.Key, "p") && !strings.HasPrefix(p.Key, "#") {
- continue
- }
- var idx int
- err := error(nil)
- switch {
- case strings.HasPrefix(p.Key, "p"):
- idx, err = strconv.Atoi(strings.TrimPrefix(p.Key, "p"))
- case strings.HasPrefix(p.Key, "#"):
- idx, err = strconv.Atoi(strings.TrimPrefix(p.Key, "#"))
- default:
- continue
- }
- if err != nil {
+ for _, p := range c.Props {
+ idx, ok := pinIndexFromPropKey(p.Key)
+ if !ok {
continue
}
pinByIndex[idx] = p.Value
@@ -88,28 +111,32 @@ func ConvertToComponent(counters map[string]int, items map[string]string, xmlCom
for idx := range pinByIndex {
indices = append(indices, idx)
}
-
sort.Ints(indices)
-
var pins []string
-
- seen := Set()
+ seen := make(map[string]bool)
for _, idx := range indices {
name := pinByIndex[idx]
if seen[name] {
- //
continue
}
seen[name] = true
pins = append(pins, name)
}
+ return pins
+}
- return Component{
- ID: id,
- Type: items[xmlComp.TP],
- Label: label,
- Value: value,
- Pins: pins,
+func pinIndexFromPropKey(key string) (int, bool) {
+ for _, prefix := range []string{"p", "#"} {
+ rest, ok := strings.CutPrefix(key, prefix)
+ if !ok {
+ continue
+ }
+ idx, err := strconv.Atoi(rest)
+ if err != nil {
+ continue
+ }
+ return idx, true
}
+ return 0, false
}
internal/loader.go
@@ -7,7 +7,16 @@ import (
"io"
)
-func LoadFile(path string) (*xmlCircuit, error) {
+func LoadFile(path string) (*Circuit, error) {
+ circuit, err := loadXML(path)
+ if err != nil {
+ return nil, err
+ }
+ cleanupCircuit(circuit)
+ return documentFromXML(circuit), nil
+}
+
+func loadXML(path string) (*xmlCircuit, error) {
r, err := zip.OpenReader(path)
if err != nil {
return nil, fmt.Errorf("failed to open .cddx as a zip archive: %w", err)
@@ -42,20 +51,5 @@ func LoadFile(path string) (*xmlCircuit, error) {
if err := xml.Unmarshal(data, &circuit); err != nil {
return nil, fmt.Errorf("failed to parse '%s': %w", docPath, err)
}
-
- for i := range circuit.Components{
-
- var newProps []xmlProp
-
- circuit.Components[i].TP = circuit.Components[i].TP[1 : len(circuit.Components[i].TP)-1] // remove the curly braces
- for _, prop := range circuit.Components[i].Props {
- if prop.Value != "" {
- newProps = append(newProps, prop)
- }
- }
- circuit.Components[i].Props = newProps
- }
-
-
return &circuit, nil
}
internal/xml.go
@@ -1,6 +1,6 @@
package internal
-// Document.xml file (schema version 1.4, as produced by circuit-diagram.org).
+// Document.xml (schema version 1.4, as produced by circuit-diagram.org).
type xmlCircuit struct {
Version string `xml:"version,attr"`
Adds []xmlAdd `xml:"definitions>src>add"`
@@ -13,10 +13,10 @@ type xmlAdd struct {
}
type xmlComponent struct {
- ID string `xml:"id,attr"`
- TP string `xml:"tp,attr"` // references xmlAdd.ID, formatted as "{N}"
- Props []xmlProp `xml:"prs>p"`
- Conns []xmlConn `xml:"cns>cn"`
+ ID string `xml:"id,attr"`
+ DefinitionID string `xml:"tp,attr"` // references xmlAdd.ID, formatted as "{N}"
+ Props []xmlProp `xml:"prs>p"`
+ Conns []xmlConn `xml:"cns>cn"`
}
type xmlProp struct {
@@ -25,6 +25,15 @@ type xmlProp struct {
}
type xmlConn struct {
- ID string `xml:"id,attr"`
- PT string `xml:"pt,attr"`
+ ID string `xml:"id,attr"`
+ Pin string `xml:"pt,attr"`
+}
+
+func (c xmlComponent) prop(key string) (string, bool) {
+ for _, p := range c.Props {
+ if p.Key == key {
+ return p.Value, true
+ }
+ }
+ return "", false
}
main.go
@@ -1,26 +1,41 @@
package main
import (
- "cddx2yaml/internal"
"fmt"
+ "os"
+
+ "cddx2yaml/internal"
+
+ "gopkg.in/yaml.v3"
)
func main() {
- circuit, err := internal.LoadFile("circuit.cddx")
+ path, outputPath := "circuit.cddx", "circuit.yaml"
+
+ if len(os.Args) > 1 {
+ path = os.Args[1]
+ }
+ if len(os.Args) > 2 {
+ outputPath = os.Args[2]
+ }
+ circuit, err := internal.LoadFile(path)
if err != nil {
- fmt.Printf("Error loading file: %v\n", err)
- return
+ fmt.Fprintf(os.Stderr, "Error loading file: %v\n", err)
+ os.Exit(1)
}
- itemByDefID := make(map[string]string)
- for _, add := range circuit.Adds {
- itemByDefID[add.ID] = add.Item
+ outputFile, err := os.Create(outputPath)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error creating output file: %v\n", err)
+ os.Exit(1)
}
+ defer outputFile.Close()
- counters := make(map[string]int)
- for _, comp := range circuit.Components {
- component := internal.ConvertToComponent(counters, itemByDefID, comp)
- fmt.Printf("Component: %+v\n", component)
+ encFile := yaml.NewEncoder(outputFile)
+ encFile.SetIndent(2)
+ if err := encFile.Encode(circuit); err != nil {
+ fmt.Fprintf(os.Stderr, "Error encoding YAML to file: %v\n", err)
+ os.Exit(1)
}
}