1package internal
2
3import (
4 "fmt"
5 "sort"
6 "strconv"
7 "strings"
8)
9
10type Circuit struct {
11 Version string `yaml:"cddx2yamlVersion"`
12 CircuitDiagramVersion string `yaml:"circuitDiagramVersion"`
13 Components []Component `yaml:"components"`
14 Connections [][]string `yaml:"connections"`
15}
16
17type Component struct {
18 ID string `yaml:"id"`
19 Type string `yaml:"type"`
20 Value string `yaml:"value,omitempty"`
21 Label string `yaml:"label,omitempty"`
22 Pins []string `yaml:"pins,omitempty"`
23}
24
25type typeCatalog map[string]string
26
27type idCounters map[string]int
28
29func documentFromXML(c *xmlCircuit) *Circuit {
30 types := make(typeCatalog, len(c.Adds))
31 for _, add := range c.Adds {
32 types[add.ID] = add.Item
33 }
34
35 counters := make(idCounters)
36 components := make([]Component, 0, len(c.Components))
37 for _, xc := range c.Components {
38 comp := xc.toComponent(types, counters)
39 for _, cn := range xc.Conns {
40 comp.addPin(xc.resolvePin(cn.Pin))
41 }
42 components = append(components, comp)
43 }
44
45 return &Circuit{
46 Version: Version,
47 CircuitDiagramVersion: c.Version,
48 Components: components,
49 Connections: collectConnections(c.Components, components),
50 }
51}
52
53func (types typeCatalog) name(definitionID string) string {
54 return types[definitionID]
55}
56
57func (counters idCounters) next(prefix string) string {
58 counters[prefix]++
59 return fmt.Sprintf("%s-%d", prefix, counters[prefix])
60}
61
62func idPrefix(typeName string) string {
63 if typeName == "microcontroller" {
64 return "IC"
65 }
66 if typeName == "" {
67 return "UNK"
68 }
69 if len(typeName) < 3 {
70 return strings.ToUpper(typeName)
71 }
72 return strings.ToUpper(typeName[:3])
73}
74
75func (c xmlComponent) toComponent(types typeCatalog, counters idCounters) Component {
76 typeName := types.name(c.DefinitionID)
77 label := c.label(types)
78 if typeName == label {
79 label = ""
80 }
81 return Component{
82 ID: counters.next(idPrefix(typeName)),
83 Type: typeName,
84 Label: label,
85 Value: c.electricalValue(),
86 Pins: c.pins(),
87 }
88}
89
90func (c xmlComponent) label(types typeCatalog) string {
91 if v, ok := c.prop("header"); ok && v != "" {
92 return v
93 }
94 return types.name(c.DefinitionID)
95}
96
97func (c xmlComponent) electricalValue() string {
98 value, _ := c.prop("value")
99 for _, key := range []string{"resistance", "inductance", "frequency", "voltage", "capacitance"} {
100 if v, ok := c.prop(key); ok {
101 value = v
102 }
103 }
104 return value
105}
106
107func (c xmlComponent) pins() []string {
108 pinByIndex := make(map[int]string)
109 for _, p := range c.Props {
110 idx, ok := pinIndex(p.Key)
111 if !ok {
112 continue
113 }
114 pinByIndex[idx] = p.Value
115 }
116
117 indices := make([]int, 0, len(pinByIndex))
118 for idx := range pinByIndex {
119 indices = append(indices, idx)
120 }
121 sort.Ints(indices)
122
123 var pins []string
124 seen := make(map[string]bool)
125 for _, idx := range indices {
126 name := pinByIndex[idx]
127 if seen[name] {
128 fmt.Printf("Warning: duplicate pin name %q for component %q\n", name, c.DefinitionID)
129 continue
130 }
131 seen[name] = true
132 pins = append(pins, name)
133 }
134 return pins
135}
136
137func pinIndex(key string) (int, bool) {
138 for _, prefix := range []string{"pin", "p", "#"} {
139 rest, ok := strings.CutPrefix(key, prefix)
140 if !ok {
141 continue
142 }
143 idx, err := strconv.Atoi(rest)
144 if err != nil {
145 continue
146 }
147 return idx, true
148 }
149 return 0, false
150}