@vessel-dsp/core 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor/commands.d.ts +48 -0
- package/dist/editor/commands.d.ts.map +1 -0
- package/{src/editor/commands.ts → dist/editor/commands.js} +44 -91
- package/dist/editor/commands.js.map +1 -0
- package/dist/editor/factory.d.ts +10 -0
- package/dist/editor/factory.d.ts.map +1 -0
- package/{src/editor/factory.ts → dist/editor/factory.js} +11 -27
- package/dist/editor/factory.js.map +1 -0
- package/dist/editor/history.d.ts +29 -0
- package/dist/editor/history.d.ts.map +1 -0
- package/{src/editor/history.ts → dist/editor/history.js} +12 -42
- package/dist/editor/history.js.map +1 -0
- package/{src/editor/index.ts → dist/editor/index.d.ts} +1 -3
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/editor/index.js +5 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/editor/layout.d.ts +8 -0
- package/dist/editor/layout.d.ts.map +1 -0
- package/{src/editor/layout.ts → dist/editor/layout.js} +36 -90
- package/dist/editor/layout.js.map +1 -0
- package/dist/formats/circuit-json/serializer.d.ts +101 -0
- package/dist/formats/circuit-json/serializer.d.ts.map +1 -0
- package/{src/formats/circuit-json/serializer.ts → dist/formats/circuit-json/serializer.js} +352 -418
- package/dist/formats/circuit-json/serializer.js.map +1 -0
- package/dist/formats/document.d.ts +64 -0
- package/dist/formats/document.d.ts.map +1 -0
- package/{src/formats/document.ts → dist/formats/document.js} +43 -159
- package/dist/formats/document.js.map +1 -0
- package/dist/formats/interchange/parser.d.ts +3 -0
- package/dist/formats/interchange/parser.d.ts.map +1 -0
- package/{src/formats/interchange/parser.ts → dist/formats/interchange/parser.js} +157 -463
- package/dist/formats/interchange/parser.js.map +1 -0
- package/dist/formats/interchange/serializer.d.ts +9 -0
- package/dist/formats/interchange/serializer.d.ts.map +1 -0
- package/{src/formats/interchange/serializer.ts → dist/formats/interchange/serializer.js} +84 -186
- package/dist/formats/interchange/serializer.js.map +1 -0
- package/dist/formats/ltspice/catalog.d.ts +19 -0
- package/dist/formats/ltspice/catalog.d.ts.map +1 -0
- package/{src/formats/ltspice/catalog.ts → dist/formats/ltspice/catalog.js} +18 -52
- package/dist/formats/ltspice/catalog.js.map +1 -0
- package/dist/formats/ltspice/encoding.d.ts +2 -0
- package/dist/formats/ltspice/encoding.d.ts.map +1 -0
- package/{src/formats/ltspice/encoding.ts → dist/formats/ltspice/encoding.js} +17 -41
- package/dist/formats/ltspice/encoding.js.map +1 -0
- package/dist/formats/ltspice/parser.d.ts +3 -0
- package/dist/formats/ltspice/parser.d.ts.map +1 -0
- package/{src/formats/ltspice/parser.ts → dist/formats/ltspice/parser.js} +39 -141
- package/dist/formats/ltspice/parser.js.map +1 -0
- package/dist/formats/ltspice/serializer.d.ts +7 -0
- package/dist/formats/ltspice/serializer.d.ts.map +1 -0
- package/{src/formats/ltspice/serializer.ts → dist/formats/ltspice/serializer.js} +18 -45
- package/dist/formats/ltspice/serializer.js.map +1 -0
- package/dist/formats/schx/catalog.d.ts +19 -0
- package/dist/formats/schx/catalog.d.ts.map +1 -0
- package/{src/formats/schx/catalog.ts → dist/formats/schx/catalog.js} +48 -101
- package/dist/formats/schx/catalog.js.map +1 -0
- package/dist/formats/schx/parser.d.ts +3 -0
- package/dist/formats/schx/parser.d.ts.map +1 -0
- package/{src/formats/schx/parser.ts → dist/formats/schx/parser.js} +31 -86
- package/dist/formats/schx/parser.js.map +1 -0
- package/dist/formats/schx/runtime-descriptors.d.ts +3 -0
- package/dist/formats/schx/runtime-descriptors.d.ts.map +1 -0
- package/{src/formats/schx/runtime-descriptors.ts → dist/formats/schx/runtime-descriptors.js} +36 -123
- package/dist/formats/schx/runtime-descriptors.js.map +1 -0
- package/dist/formats/schx/serializer.d.ts +5 -0
- package/dist/formats/schx/serializer.d.ts.map +1 -0
- package/{src/formats/schx/serializer.ts → dist/formats/schx/serializer.js} +17 -42
- package/dist/formats/schx/serializer.js.map +1 -0
- package/dist/formats/schx/transforms.d.ts +4 -0
- package/dist/formats/schx/transforms.d.ts.map +1 -0
- package/{src/formats/schx/transforms.ts → dist/formats/schx/transforms.js} +6 -10
- package/dist/formats/schx/transforms.js.map +1 -0
- package/dist/formats/spice/parser.d.ts +3 -0
- package/dist/formats/spice/parser.d.ts.map +1 -0
- package/{src/formats/spice/parser.ts → dist/formats/spice/parser.js} +50 -96
- package/dist/formats/spice/parser.js.map +1 -0
- package/dist/formats/spice/serializer.d.ts +3 -0
- package/dist/formats/spice/serializer.d.ts.map +1 -0
- package/{src/formats/spice/serializer.ts → dist/formats/spice/serializer.js} +20 -16
- package/dist/formats/spice/serializer.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/model/connectivity.d.ts +16 -0
- package/dist/model/connectivity.d.ts.map +1 -0
- package/{src/model/connectivity.ts → dist/model/connectivity.js} +28 -63
- package/dist/model/connectivity.js.map +1 -0
- package/dist/model/netlist.d.ts +24 -0
- package/dist/model/netlist.d.ts.map +1 -0
- package/{src/model/netlist.ts → dist/model/netlist.js} +42 -110
- package/dist/model/netlist.js.map +1 -0
- package/dist/model/properties.d.ts +9 -0
- package/dist/model/properties.d.ts.map +1 -0
- package/{src/model/properties.ts → dist/model/properties.js} +10 -18
- package/dist/model/properties.js.map +1 -0
- package/dist/model/quantity.d.ts +3 -0
- package/dist/model/quantity.d.ts.map +1 -0
- package/{src/model/quantity.ts → dist/model/quantity.js} +7 -30
- package/dist/model/quantity.js.map +1 -0
- package/{src/model/types.ts → dist/model/types.d.ts} +17 -196
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +10 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/validation.d.ts +32 -0
- package/dist/model/validation.d.ts.map +1 -0
- package/{src/model/validation.ts → dist/model/validation.js} +172 -653
- package/dist/model/validation.js.map +1 -0
- package/dist/model/wires.d.ts +3 -0
- package/dist/model/wires.d.ts.map +1 -0
- package/{src/model/wires.ts → dist/model/wires.js} +10 -16
- package/dist/model/wires.js.map +1 -0
- package/dist/panel/extract.d.ts +5 -0
- package/dist/panel/extract.d.ts.map +1 -0
- package/{src/panel/extract.ts → dist/panel/extract.js} +146 -235
- package/dist/panel/extract.js.map +1 -0
- package/dist/panel/index.d.ts +6 -0
- package/dist/panel/index.d.ts.map +1 -0
- package/dist/panel/index.js +5 -0
- package/dist/panel/index.js.map +1 -0
- package/dist/panel/knobs.d.ts +7 -0
- package/dist/panel/knobs.d.ts.map +1 -0
- package/{src/panel/knobs.ts → dist/panel/knobs.js} +7 -18
- package/dist/panel/knobs.js.map +1 -0
- package/dist/panel/protocol.d.ts +9 -0
- package/dist/panel/protocol.d.ts.map +1 -0
- package/{src/panel/protocol.ts → dist/panel/protocol.js} +10 -26
- package/dist/panel/protocol.js.map +1 -0
- package/{src/panel/types.ts → dist/panel/types.d.ts} +50 -89
- package/dist/panel/types.d.ts.map +1 -0
- package/dist/panel/types.js +2 -0
- package/dist/panel/types.js.map +1 -0
- package/dist/preview/bounds.d.ts +12 -0
- package/dist/preview/bounds.d.ts.map +1 -0
- package/{src/preview/bounds.ts → dist/preview/bounds.js} +15 -29
- package/dist/preview/bounds.js.map +1 -0
- package/dist/preview/box-layout.d.ts +4 -0
- package/dist/preview/box-layout.d.ts.map +1 -0
- package/{src/preview/box-layout.ts → dist/preview/box-layout.js} +2 -6
- package/dist/preview/box-layout.js.map +1 -0
- package/dist/preview/colors.d.ts +3 -0
- package/dist/preview/colors.d.ts.map +1 -0
- package/{src/preview/colors.ts → dist/preview/colors.js} +3 -5
- package/dist/preview/colors.js.map +1 -0
- package/dist/preview/hanging.d.ts +8 -0
- package/dist/preview/hanging.d.ts.map +1 -0
- package/{src/preview/hanging.ts → dist/preview/hanging.js} +9 -28
- package/dist/preview/hanging.js.map +1 -0
- package/dist/preview/junctions.d.ts +3 -0
- package/dist/preview/junctions.d.ts.map +1 -0
- package/{src/preview/junctions.ts → dist/preview/junctions.js} +9 -24
- package/dist/preview/junctions.js.map +1 -0
- package/dist/preview/label-layout.d.ts +12 -0
- package/dist/preview/label-layout.d.ts.map +1 -0
- package/{src/preview/label-layout.ts → dist/preview/label-layout.js} +15 -36
- package/dist/preview/label-layout.js.map +1 -0
- package/dist/preview/ports.d.ts +17 -0
- package/dist/preview/ports.d.ts.map +1 -0
- package/{src/preview/ports.ts → dist/preview/ports.js} +10 -37
- package/dist/preview/ports.js.map +1 -0
- package/dist/preview/renderable-wires.d.ts +3 -0
- package/dist/preview/renderable-wires.d.ts.map +1 -0
- package/{src/preview/renderable-wires.ts → dist/preview/renderable-wires.js} +12 -29
- package/dist/preview/renderable-wires.js.map +1 -0
- package/dist/preview/routing.d.ts +4 -0
- package/dist/preview/routing.d.ts.map +1 -0
- package/dist/preview/routing.js +13 -0
- package/dist/preview/routing.js.map +1 -0
- package/dist/preview/snap.d.ts +9 -0
- package/dist/preview/snap.d.ts.map +1 -0
- package/{src/preview/snap.ts → dist/preview/snap.js} +9 -31
- package/dist/preview/snap.js.map +1 -0
- package/dist/preview/symbols/svg-content.d.ts +7 -0
- package/dist/preview/symbols/svg-content.d.ts.map +1 -0
- package/{src/preview/symbols/svg-content.ts → dist/preview/symbols/svg-content.js} +3 -6
- package/dist/preview/symbols/svg-content.js.map +1 -0
- package/dist/preview/symbols.d.ts +7 -0
- package/dist/preview/symbols.d.ts.map +1 -0
- package/{src/preview/symbols.ts → dist/preview/symbols.js} +18 -43
- package/dist/preview/symbols.js.map +1 -0
- package/dist/preview/wire-chains.d.ts +4 -0
- package/dist/preview/wire-chains.d.ts.map +1 -0
- package/{src/preview/wire-chains.ts → dist/preview/wire-chains.js} +37 -37
- package/dist/preview/wire-chains.js.map +1 -0
- package/package.json +3 -3
- package/src/index.ts +0 -255
- package/src/panel/index.ts +0 -39
- package/src/preview/routing.ts +0 -15
- package/src/preview/symbols/analog-switch.svg +0 -17
- package/src/preview/symbols/battery.svg +0 -16
- package/src/preview/symbols/bbd.svg +0 -21
- package/src/preview/symbols/bjt-npn.svg +0 -16
- package/src/preview/symbols/bjt-pnp.svg +0 -17
- package/src/preview/symbols/capacitor-electrolytic.svg +0 -13
- package/src/preview/symbols/capacitor.svg +0 -12
- package/src/preview/symbols/current-source.svg +0 -14
- package/src/preview/symbols/delay-ic.svg +0 -22
- package/src/preview/symbols/diode-schottky.svg +0 -12
- package/src/preview/symbols/diode-zener.svg +0 -12
- package/src/preview/symbols/diode.svg +0 -13
- package/src/preview/symbols/flipflop.svg +0 -20
- package/src/preview/symbols/ground.svg +0 -12
- package/src/preview/symbols/ic-block.svg +0 -20
- package/src/preview/symbols/ic.svg +0 -19
- package/src/preview/symbols/inductor.svg +0 -11
- package/src/preview/symbols/jack-input.svg +0 -16
- package/src/preview/symbols/jack-output.svg +0 -16
- package/src/preview/symbols/jfet-junction-n.svg +0 -17
- package/src/preview/symbols/jfet-n.svg +0 -17
- package/src/preview/symbols/jfet-p.svg +0 -17
- package/src/preview/symbols/label.svg +0 -8
- package/src/preview/symbols/led.svg +0 -18
- package/src/preview/symbols/mosfet-n.svg +0 -21
- package/src/preview/symbols/mosfet-p.svg +0 -21
- package/src/preview/symbols/named-wire.svg +0 -11
- package/src/preview/symbols/opamp.svg +0 -21
- package/src/preview/symbols/optocoupler.svg +0 -30
- package/src/preview/symbols/ota.svg +0 -20
- package/src/preview/symbols/pentode.svg +0 -25
- package/src/preview/symbols/photoresistor.svg +0 -19
- package/src/preview/symbols/port.svg +0 -8
- package/src/preview/symbols/potentiometer.svg +0 -15
- package/src/preview/symbols/power-amp.svg +0 -20
- package/src/preview/symbols/rail.svg +0 -11
- package/src/preview/symbols/regulator.svg +0 -13
- package/src/preview/symbols/relay.svg +0 -20
- package/src/preview/symbols/resistor.svg +0 -11
- package/src/preview/symbols/switch-3pdt.svg +0 -32
- package/src/preview/symbols/switch-rotary.svg +0 -23
- package/src/preview/symbols/switch-spdt.svg +0 -16
- package/src/preview/symbols/switch-spst.svg +0 -14
- package/src/preview/symbols/switch-toggle.svg +0 -14
- package/src/preview/symbols/transformer.svg +0 -17
- package/src/preview/symbols/triode.svg +0 -17
- package/src/preview/symbols/tube-diode.svg +0 -13
- package/src/preview/symbols/unsupported.svg +0 -8
- package/src/preview/symbols/variable-resistor.svg +0 -13
- package/src/preview/symbols/voltage-source.svg +0 -15
|
@@ -1,170 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from 'circuit-json';
|
|
7
|
-
import { getPinNode, resolveConnectivity, type Connectivity, type NodeId } from '../../model/connectivity';
|
|
8
|
-
import { propertyQuantityValue, propertyStringValue } from '../../model/properties';
|
|
9
|
-
import { parseQuantity } from '../../model/quantity';
|
|
10
|
-
import type {
|
|
11
|
-
CircuitDocument,
|
|
12
|
-
Component,
|
|
13
|
-
ComponentKind,
|
|
14
|
-
ParsedQuantity,
|
|
15
|
-
Point,
|
|
16
|
-
PropertyValue,
|
|
17
|
-
Rotation,
|
|
18
|
-
Terminal,
|
|
19
|
-
Warning,
|
|
20
|
-
Wire,
|
|
21
|
-
} from '../../model/types';
|
|
22
|
-
|
|
23
|
-
export type CircuitJsonExportTarget = 'tscircuit';
|
|
24
|
-
|
|
25
|
-
export type CircuitJsonExportOptions = Readonly<{
|
|
26
|
-
target?: CircuitJsonExportTarget;
|
|
27
|
-
}>;
|
|
28
|
-
|
|
29
|
-
export type CircuitJsonSourceNet = Readonly<{
|
|
30
|
-
type: 'source_net';
|
|
31
|
-
source_net_id: string;
|
|
32
|
-
name: string;
|
|
33
|
-
member_source_group_ids: string[];
|
|
34
|
-
is_power?: boolean;
|
|
35
|
-
is_ground?: boolean;
|
|
36
|
-
is_digital_signal?: boolean;
|
|
37
|
-
is_analog_signal?: boolean;
|
|
38
|
-
is_positive_voltage_source?: boolean;
|
|
39
|
-
}>;
|
|
40
|
-
|
|
41
|
-
export type CircuitJsonSourceComponent = Readonly<{
|
|
42
|
-
type: 'source_component';
|
|
43
|
-
source_component_id: string;
|
|
44
|
-
name: string;
|
|
45
|
-
ftype?: string;
|
|
46
|
-
display_name?: string;
|
|
47
|
-
display_value?: string;
|
|
48
|
-
resistance?: number;
|
|
49
|
-
display_resistance?: string;
|
|
50
|
-
capacitance?: number;
|
|
51
|
-
display_capacitance?: string;
|
|
52
|
-
inductance?: number;
|
|
53
|
-
display_inductance?: string;
|
|
54
|
-
voltage?: number;
|
|
55
|
-
current?: number;
|
|
56
|
-
wave_shape?: 'dc' | 'sine' | 'square' | 'triangle' | 'sawtooth' | 'sinewave';
|
|
57
|
-
transistor_type?: 'npn' | 'pnp';
|
|
58
|
-
channel_type?: 'n' | 'p';
|
|
59
|
-
mosfet_mode?: 'enhancement' | 'depletion';
|
|
60
|
-
max_resistance?: number;
|
|
61
|
-
display_max_resistance?: string;
|
|
62
|
-
manufacturer_part_number?: string;
|
|
63
|
-
}>;
|
|
64
|
-
|
|
65
|
-
export type CircuitJsonSourcePort = Readonly<{
|
|
66
|
-
type: 'source_port';
|
|
67
|
-
source_port_id: string;
|
|
68
|
-
source_component_id: string;
|
|
69
|
-
name: string;
|
|
70
|
-
port_hints: string[];
|
|
71
|
-
provides_ground?: boolean;
|
|
72
|
-
requires_ground?: boolean;
|
|
73
|
-
provides_power?: boolean;
|
|
74
|
-
requires_power?: boolean;
|
|
75
|
-
provides_voltage?: number;
|
|
76
|
-
}>;
|
|
77
|
-
|
|
78
|
-
export type CircuitJsonSourceTrace = Readonly<{
|
|
79
|
-
type: 'source_trace';
|
|
80
|
-
source_trace_id: string;
|
|
81
|
-
connected_source_port_ids: string[];
|
|
82
|
-
connected_source_net_ids: string[];
|
|
83
|
-
display_name?: string;
|
|
84
|
-
}>;
|
|
85
|
-
|
|
86
|
-
export type CircuitJsonElement =
|
|
87
|
-
AnyCircuitElement;
|
|
88
|
-
|
|
89
|
-
export type CircuitJson = OfficialCircuitJson;
|
|
90
|
-
export type { AnyCircuitElement, AnyCircuitElementInput };
|
|
91
|
-
|
|
92
|
-
export type CircuitJsonExport = Readonly<{
|
|
93
|
-
elements: CircuitJson;
|
|
94
|
-
warnings: readonly string[];
|
|
95
|
-
}>;
|
|
96
|
-
|
|
97
|
-
export type CircuitJsonSchemaValidationIssue = Readonly<{
|
|
98
|
-
code: 'circuit-json-schema-invalid';
|
|
99
|
-
message: string;
|
|
100
|
-
path?: string;
|
|
101
|
-
}>;
|
|
102
|
-
|
|
103
|
-
export type CircuitJsonSchemaValidationResult =
|
|
104
|
-
| Readonly<{
|
|
105
|
-
valid: true;
|
|
106
|
-
elements: CircuitJson;
|
|
107
|
-
errors: readonly [];
|
|
108
|
-
}>
|
|
109
|
-
| Readonly<{
|
|
110
|
-
valid: false;
|
|
111
|
-
errors: readonly CircuitJsonSchemaValidationIssue[];
|
|
112
|
-
}>;
|
|
113
|
-
|
|
114
|
-
export type ParseCircuitJsonDocumentOptions = Readonly<{
|
|
115
|
-
filename?: string;
|
|
116
|
-
}>;
|
|
117
|
-
|
|
118
|
-
type JsonRecord = Readonly<Record<string, unknown>>;
|
|
119
|
-
|
|
120
|
-
type SourceComponentRecord = Readonly<{
|
|
121
|
-
sourceComponentId: string;
|
|
122
|
-
componentId: string;
|
|
123
|
-
name: string;
|
|
124
|
-
ftype: string | null;
|
|
125
|
-
record: JsonRecord;
|
|
126
|
-
}>;
|
|
127
|
-
|
|
128
|
-
type SourcePortRecord = Readonly<{
|
|
129
|
-
sourcePortId: string;
|
|
130
|
-
componentSourceId: string;
|
|
131
|
-
terminalName: string;
|
|
132
|
-
record: JsonRecord;
|
|
133
|
-
}>;
|
|
134
|
-
|
|
135
|
-
type SchematicComponentRecord = Readonly<{
|
|
136
|
-
sourceComponentId: string;
|
|
137
|
-
center: Point;
|
|
138
|
-
}>;
|
|
139
|
-
|
|
140
|
-
type SchematicPortRecord = Readonly<{
|
|
141
|
-
sourcePortId: string;
|
|
142
|
-
center: Point;
|
|
143
|
-
}>;
|
|
144
|
-
|
|
145
|
-
type MutableComponentBuild = {
|
|
146
|
-
readonly sourceComponent: SourceComponentRecord;
|
|
147
|
-
readonly ports: readonly SourcePortRecord[];
|
|
148
|
-
readonly origin: Point;
|
|
149
|
-
readonly terminals: readonly Terminal[];
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
type QuantityLookup = Readonly<{
|
|
153
|
-
value: ParsedQuantity;
|
|
154
|
-
}>;
|
|
155
|
-
|
|
156
|
-
type QuantityKey = 'resistance' | 'capacitance' | 'inductance' | 'voltage' | 'current';
|
|
157
|
-
type CircuitJsonSchematicPortDirection = 'up' | 'down' | 'left' | 'right';
|
|
158
|
-
|
|
159
|
-
const VALUE_PROPERTY_NAMES: Readonly<Record<QuantityKey, readonly string[]>> = {
|
|
1
|
+
import { any_circuit_element, } from 'circuit-json';
|
|
2
|
+
import { getPinNode, resolveConnectivity } from '../../model/connectivity.js';
|
|
3
|
+
import { propertyQuantityValue, propertyStringValue } from '../../model/properties.js';
|
|
4
|
+
import { parseQuantity } from '../../model/quantity.js';
|
|
5
|
+
const VALUE_PROPERTY_NAMES = {
|
|
160
6
|
resistance: ['R', 'Resistance', 'resistance', 'value', 'Value'],
|
|
161
7
|
capacitance: ['C', 'Capacitance', 'capacitance', 'value', 'Value'],
|
|
162
8
|
inductance: ['L', 'Inductance', 'inductance', 'value', 'Value'],
|
|
163
9
|
voltage: ['V', 'Voltage', 'voltage', 'value', 'Value'],
|
|
164
10
|
current: ['I', 'Current', 'current', 'value', 'Value'],
|
|
165
11
|
};
|
|
166
|
-
|
|
167
|
-
const MODEL_PROPERTY_NAMES: readonly string[] = [
|
|
12
|
+
const MODEL_PROPERTY_NAMES = [
|
|
168
13
|
'manufacturerPartNumber',
|
|
169
14
|
'ManufacturerPartNumber',
|
|
170
15
|
'manufacturer_part_number',
|
|
@@ -175,8 +20,7 @@ const MODEL_PROPERTY_NAMES: readonly string[] = [
|
|
|
175
20
|
'modelName',
|
|
176
21
|
'ModelName',
|
|
177
22
|
];
|
|
178
|
-
|
|
179
|
-
const DIRECT_EXPORT_KINDS: ReadonlySet<ComponentKind> = new Set<ComponentKind>([
|
|
23
|
+
const DIRECT_EXPORT_KINDS = new Set([
|
|
180
24
|
'resistor',
|
|
181
25
|
'variable-resistor',
|
|
182
26
|
'capacitor',
|
|
@@ -198,30 +42,26 @@ const DIRECT_EXPORT_KINDS: ReadonlySet<ComponentKind> = new Set<ComponentKind>([
|
|
|
198
42
|
'port',
|
|
199
43
|
'ic',
|
|
200
44
|
]);
|
|
201
|
-
|
|
202
|
-
const SOURCE_ONLY_NET_NAME_KINDS: ReadonlySet<ComponentKind> = new Set<ComponentKind>([
|
|
45
|
+
const SOURCE_ONLY_NET_NAME_KINDS = new Set([
|
|
203
46
|
'label',
|
|
204
47
|
'named-wire',
|
|
205
48
|
]);
|
|
206
|
-
|
|
49
|
+
const VESSEL_CAN_CAP_SOURCE_GROUP_PREFIX = 'source_group:vessel-dsp-can-cap:';
|
|
50
|
+
const VESSEL_DSP_PROPERTY_JSON_PREFIX = 'vessel-dsp-property-json:';
|
|
51
|
+
const VESSEL_DSP_PROPERTY_SIDECAR_ID_PREFIX = 'vessel_dsp_property:';
|
|
207
52
|
const TSCIRCUIT_SCHEMATIC_COORD_SCALE = 0.02;
|
|
208
53
|
const DEFAULT_SCHEMATIC_COMPONENT_SIZE = { width: 1.2, height: 0.8 };
|
|
209
|
-
|
|
210
|
-
export function serializeCircuitJsonDocument(
|
|
211
|
-
doc: CircuitDocument,
|
|
212
|
-
_options: CircuitJsonExportOptions = {},
|
|
213
|
-
): CircuitJsonExport {
|
|
54
|
+
export function serializeCircuitJsonDocument(doc, _options = {}) {
|
|
214
55
|
const connectivity = resolveConnectivity(doc);
|
|
215
|
-
const warnings
|
|
216
|
-
const exportedComponentIds = new Set
|
|
217
|
-
const sourcePortIdsByNode = new Map
|
|
56
|
+
const warnings = [];
|
|
57
|
+
const exportedComponentIds = new Set();
|
|
58
|
+
const sourcePortIdsByNode = new Map();
|
|
218
59
|
const names = netNames(doc, connectivity);
|
|
219
60
|
const powerNodes = railNodeIds(doc, connectivity);
|
|
220
|
-
|
|
221
61
|
const nets = sourceNetElements(connectivity, names, powerNodes);
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
|
|
62
|
+
const groups = sourceGroupElements(doc);
|
|
63
|
+
const components = [];
|
|
64
|
+
const ports = [];
|
|
225
65
|
for (const component of doc.components) {
|
|
226
66
|
const sourceComponent = sourceComponentElement(component, warnings);
|
|
227
67
|
if (sourceComponent === null) {
|
|
@@ -229,7 +69,6 @@ export function serializeCircuitJsonDocument(
|
|
|
229
69
|
}
|
|
230
70
|
exportedComponentIds.add(component.id);
|
|
231
71
|
components.push(sourceComponent);
|
|
232
|
-
|
|
233
72
|
for (const terminal of component.terminals) {
|
|
234
73
|
const nodeId = getPinNode(connectivity, {
|
|
235
74
|
componentId: component.id,
|
|
@@ -242,30 +81,27 @@ export function serializeCircuitJsonDocument(
|
|
|
242
81
|
}
|
|
243
82
|
}
|
|
244
83
|
}
|
|
245
|
-
|
|
246
84
|
const traces = sourceTraceElements(connectivity, names, sourcePortIdsByNode, warnings);
|
|
247
85
|
warnings.push(...sourceOnlyWarnings(doc, exportedComponentIds));
|
|
248
|
-
|
|
249
|
-
const sourceElements = [...sourceProjectMetadataElements(doc), ...nets, ...components, ...ports, ...traces];
|
|
86
|
+
const sidecars = sourcePropertySidecarElements(doc, exportedComponentIds);
|
|
87
|
+
const sourceElements = [...sourceProjectMetadataElements(doc), ...nets, ...groups, ...components, ...ports, ...traces, ...sidecars];
|
|
250
88
|
return {
|
|
251
89
|
elements: normalizeCircuitJsonElements([...sourceElements, ...schematicElements(doc, traces)]),
|
|
252
90
|
warnings,
|
|
253
91
|
};
|
|
254
92
|
}
|
|
255
|
-
|
|
256
|
-
export function validateCircuitJsonDocument(source: unknown): CircuitJsonSchemaValidationResult {
|
|
93
|
+
export function validateCircuitJsonDocument(source) {
|
|
257
94
|
if (!Array.isArray(source)) {
|
|
258
95
|
return {
|
|
259
96
|
valid: false,
|
|
260
97
|
errors: [{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
98
|
+
code: 'circuit-json-schema-invalid',
|
|
99
|
+
message: 'Circuit JSON document must be an array of elements',
|
|
100
|
+
}],
|
|
264
101
|
};
|
|
265
102
|
}
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
const errors: CircuitJsonSchemaValidationIssue[] = [];
|
|
103
|
+
const elements = [];
|
|
104
|
+
const errors = [];
|
|
269
105
|
for (const [index, element] of source.entries()) {
|
|
270
106
|
const shallowIssue = shallowSchemaIssue(element);
|
|
271
107
|
if (shallowIssue !== null) {
|
|
@@ -287,32 +123,27 @@ export function validateCircuitJsonDocument(source: unknown): CircuitJsonSchemaV
|
|
|
287
123
|
path: `[${index}]`,
|
|
288
124
|
});
|
|
289
125
|
}
|
|
290
|
-
|
|
291
126
|
if (errors.length > 0) {
|
|
292
127
|
return { valid: false, errors };
|
|
293
128
|
}
|
|
294
129
|
return { valid: true, elements, errors: [] };
|
|
295
130
|
}
|
|
296
|
-
|
|
297
|
-
export function parseCircuitJsonDocument(
|
|
298
|
-
source: unknown,
|
|
299
|
-
options: ParseCircuitJsonDocumentOptions = {},
|
|
300
|
-
): CircuitDocument {
|
|
131
|
+
export function parseCircuitJsonDocument(source, options = {}) {
|
|
301
132
|
const result = validateCircuitJsonDocument(source);
|
|
302
133
|
if (!result.valid) {
|
|
303
134
|
throw new Error(result.errors.map((error) => `${error.path ?? '<root>'}: ${error.message}`).join('; '));
|
|
304
135
|
}
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
const
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
136
|
+
const sourceComponents = new Map();
|
|
137
|
+
const sourcePorts = new Map();
|
|
138
|
+
const sourceGroups = new Map();
|
|
139
|
+
const sourcePropertySidecars = new Map();
|
|
140
|
+
const schematicComponents = new Map();
|
|
141
|
+
const schematicPorts = new Map();
|
|
142
|
+
const sourceTraces = [];
|
|
143
|
+
const sourceNets = new Map();
|
|
144
|
+
const warnings = [];
|
|
145
|
+
const directives = [];
|
|
146
|
+
let metadataName = null;
|
|
316
147
|
for (const element of result.elements) {
|
|
317
148
|
const record = checkedRecord(element);
|
|
318
149
|
const type = stringField(record, 'type');
|
|
@@ -324,10 +155,37 @@ export function parseCircuitJsonDocument(
|
|
|
324
155
|
componentId: stripKnownPrefix(sourceComponentId, 'source_component:') ?? sanitizeId(stringField(record, 'name') ?? sourceComponentId),
|
|
325
156
|
name: stringField(record, 'name') ?? stripKnownPrefix(sourceComponentId, 'source_component:') ?? sourceComponentId,
|
|
326
157
|
ftype: stringField(record, 'ftype'),
|
|
158
|
+
sourceGroupId: stringField(record, 'source_group_id'),
|
|
327
159
|
record,
|
|
328
160
|
});
|
|
329
161
|
break;
|
|
330
162
|
}
|
|
163
|
+
case 'source_group': {
|
|
164
|
+
const sourceGroupId = requiredStringField(record, 'source_group_id');
|
|
165
|
+
sourceGroups.set(sourceGroupId, {
|
|
166
|
+
sourceGroupId,
|
|
167
|
+
name: stringField(record, 'name'),
|
|
168
|
+
});
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case 'source_property_ignored_warning': {
|
|
172
|
+
const sidecar = vesselDspPropertySidecar(record);
|
|
173
|
+
if (sidecar === null) {
|
|
174
|
+
warnings.push({
|
|
175
|
+
code: 'circuit-json-source-property-ignored',
|
|
176
|
+
message: `Circuit JSON source property "${stringField(record, 'property_name') ?? ''}" was ignored`,
|
|
177
|
+
});
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
const existing = sourcePropertySidecars.get(sidecar.sourceComponentId);
|
|
181
|
+
if (existing === undefined) {
|
|
182
|
+
sourcePropertySidecars.set(sidecar.sourceComponentId, [sidecar]);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
existing.push(sidecar);
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
331
189
|
case 'source_port': {
|
|
332
190
|
const sourcePortId = requiredStringField(record, 'source_port_id');
|
|
333
191
|
const componentSourceId = requiredStringField(record, 'source_component_id');
|
|
@@ -382,7 +240,6 @@ export function parseCircuitJsonDocument(
|
|
|
382
240
|
break;
|
|
383
241
|
}
|
|
384
242
|
}
|
|
385
|
-
|
|
386
243
|
const portsByComponent = groupPortsByComponent(sourcePorts);
|
|
387
244
|
const hasSchematicGeometry = schematicComponents.size > 0 || schematicPorts.size > 0;
|
|
388
245
|
if (!hasSchematicGeometry && sourceComponents.size > 0) {
|
|
@@ -391,16 +248,15 @@ export function parseCircuitJsonDocument(
|
|
|
391
248
|
message: 'Circuit JSON source elements did not include schematic geometry; generated deterministic component and terminal positions',
|
|
392
249
|
});
|
|
393
250
|
}
|
|
394
|
-
|
|
395
251
|
const componentBuilds = Array.from(sourceComponents.values()).map((sourceComponent, index) => {
|
|
396
252
|
const ports = portsByComponent.get(sourceComponent.sourceComponentId) ?? [];
|
|
397
|
-
|
|
253
|
+
const sidecars = sourcePropertySidecars.get(sourceComponent.sourceComponentId) ?? [];
|
|
254
|
+
return buildComponentFromCircuitJson(sourceComponent, ports, schematicComponents, schematicPorts, sourceGroups, sidecars, index);
|
|
398
255
|
});
|
|
399
256
|
const components = componentBuilds.map((build) => build.component);
|
|
400
257
|
const terminalPositions = terminalPositionMap(componentBuilds);
|
|
401
258
|
const wires = wireElementsFromSourceTraces(sourceTraces, terminalPositions);
|
|
402
259
|
const netWarnings = warningsForUnconnectedNets(sourceNets, sourceTraces);
|
|
403
|
-
|
|
404
260
|
return {
|
|
405
261
|
metadata: {
|
|
406
262
|
name: metadataName ?? filenameWithoutCircuitJsonExtension(options.filename ?? 'Circuit JSON Import'),
|
|
@@ -418,25 +274,18 @@ export function parseCircuitJsonDocument(
|
|
|
418
274
|
rawAttributes: { format: 'circuit-json' },
|
|
419
275
|
};
|
|
420
276
|
}
|
|
421
|
-
|
|
422
|
-
function sourceProjectMetadataElements(doc: CircuitDocument): AnyCircuitElement[] {
|
|
277
|
+
function sourceProjectMetadataElements(doc) {
|
|
423
278
|
if (doc.metadata.name.trim().length === 0) {
|
|
424
279
|
return [];
|
|
425
280
|
}
|
|
426
281
|
return [{
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
connectivity: Connectivity,
|
|
435
|
-
names: ReadonlyMap<NodeId, string>,
|
|
436
|
-
powerNodes: ReadonlySet<NodeId>,
|
|
437
|
-
): readonly CircuitJsonSourceNet[] {
|
|
438
|
-
const elements: CircuitJsonSourceNet[] = [];
|
|
439
|
-
|
|
282
|
+
type: 'source_project_metadata',
|
|
283
|
+
name: doc.metadata.name,
|
|
284
|
+
software_used_string: '@vessel-dsp/core',
|
|
285
|
+
}];
|
|
286
|
+
}
|
|
287
|
+
function sourceNetElements(connectivity, names, powerNodes) {
|
|
288
|
+
const elements = [];
|
|
440
289
|
for (let nodeId = 0; nodeId < connectivity.nodeCount; nodeId += 1) {
|
|
441
290
|
const isGround = connectivity.groundNodeId === nodeId;
|
|
442
291
|
const isPower = powerNodes.has(nodeId);
|
|
@@ -451,18 +300,28 @@ function sourceNetElements(
|
|
|
451
300
|
is_analog_signal: true,
|
|
452
301
|
});
|
|
453
302
|
}
|
|
454
|
-
|
|
455
303
|
return elements;
|
|
456
304
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
component
|
|
460
|
-
|
|
461
|
-
|
|
305
|
+
function sourceGroupElements(doc) {
|
|
306
|
+
const byGroupId = new Map();
|
|
307
|
+
for (const component of doc.components) {
|
|
308
|
+
const groupId = canCapGroupProperty(component);
|
|
309
|
+
if (groupId === null || byGroupId.has(groupId)) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const packageName = firstStringProperty(component, ['CanCapPackageName', 'PackageName']);
|
|
313
|
+
byGroupId.set(groupId, {
|
|
314
|
+
type: 'source_group',
|
|
315
|
+
source_group_id: canCapSourceGroupId(groupId),
|
|
316
|
+
show_as_schematic_box: true,
|
|
317
|
+
name: packageName ?? groupId,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return Array.from(byGroupId.values());
|
|
321
|
+
}
|
|
322
|
+
function sourceComponentElement(component, warnings) {
|
|
462
323
|
if (component.kind === 'unsupported') {
|
|
463
|
-
warnings.push(
|
|
464
|
-
`${component.id} (unsupported): unsupported source type ${component.sourceTypeName ?? 'unknown'} skipped from Circuit JSON export`,
|
|
465
|
-
);
|
|
324
|
+
warnings.push(`${component.id} (unsupported): unsupported source type ${component.sourceTypeName ?? 'unknown'} skipped from Circuit JSON export`);
|
|
466
325
|
return null;
|
|
467
326
|
}
|
|
468
327
|
if (SOURCE_ONLY_NET_NAME_KINDS.has(component.kind)) {
|
|
@@ -472,10 +331,8 @@ function sourceComponentElement(
|
|
|
472
331
|
warnings.push(`${component.id} (${component.kind}): no Circuit JSON source-component mapping; skipped`);
|
|
473
332
|
return null;
|
|
474
333
|
}
|
|
475
|
-
|
|
476
334
|
const base = sourceComponentBase(component);
|
|
477
335
|
const manufacturerPartNumber = firstStringProperty(component, MODEL_PROPERTY_NAMES);
|
|
478
|
-
|
|
479
336
|
switch (component.kind) {
|
|
480
337
|
case 'resistor':
|
|
481
338
|
case 'variable-resistor': {
|
|
@@ -540,9 +397,7 @@ function sourceComponentElement(
|
|
|
540
397
|
...(manufacturerPartNumber !== null ? { manufacturer_part_number: manufacturerPartNumber } : {}),
|
|
541
398
|
};
|
|
542
399
|
case 'jfet':
|
|
543
|
-
warnings.push(
|
|
544
|
-
`${component.id} (jfet): Circuit JSON has no simple_jfet ftype; emitted simple_mosfet depletion-mode source metadata`,
|
|
545
|
-
);
|
|
400
|
+
warnings.push(`${component.id} (jfet): Circuit JSON has no simple_jfet ftype; emitted simple_mosfet depletion-mode source metadata`);
|
|
546
401
|
return {
|
|
547
402
|
...base,
|
|
548
403
|
ftype: 'simple_mosfet',
|
|
@@ -648,17 +503,96 @@ function sourceComponentElement(
|
|
|
648
503
|
return null;
|
|
649
504
|
}
|
|
650
505
|
}
|
|
651
|
-
|
|
652
|
-
|
|
506
|
+
function sourceComponentBase(component) {
|
|
507
|
+
const groupId = canCapGroupProperty(component);
|
|
653
508
|
return {
|
|
654
509
|
type: 'source_component',
|
|
655
510
|
source_component_id: sourceComponentId(component.id),
|
|
656
511
|
name: component.name,
|
|
657
512
|
display_name: component.name,
|
|
513
|
+
...(groupId === null ? {} : { source_group_id: canCapSourceGroupId(groupId) }),
|
|
658
514
|
};
|
|
659
515
|
}
|
|
660
|
-
|
|
661
|
-
|
|
516
|
+
function canCapGroupProperty(component) {
|
|
517
|
+
if (component.kind !== 'capacitor') {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
const value = propertyStringValue(component.properties.CanCapGroupId);
|
|
521
|
+
return value === null || value.trim().length === 0 ? null : value.trim();
|
|
522
|
+
}
|
|
523
|
+
function canCapSourceGroupId(groupId) {
|
|
524
|
+
return `${VESSEL_CAN_CAP_SOURCE_GROUP_PREFIX}${groupId}`;
|
|
525
|
+
}
|
|
526
|
+
function canCapGroupIdFromSourceGroupId(sourceGroupId) {
|
|
527
|
+
return stripKnownPrefix(sourceGroupId, VESSEL_CAN_CAP_SOURCE_GROUP_PREFIX);
|
|
528
|
+
}
|
|
529
|
+
function sourcePropertySidecarElements(doc, exportedComponentIds) {
|
|
530
|
+
const sidecars = [];
|
|
531
|
+
for (const component of doc.components) {
|
|
532
|
+
if (!exportedComponentIds.has(component.id)) {
|
|
533
|
+
continue;
|
|
534
|
+
}
|
|
535
|
+
const consumed = circuitJsonConsumedPropertyKeys(component);
|
|
536
|
+
for (const [propertyName, value] of Object.entries(component.properties)) {
|
|
537
|
+
if (consumed.has(propertyName)) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
const sourceComponentIdValue = sourceComponentId(component.id);
|
|
541
|
+
sidecars.push({
|
|
542
|
+
type: 'source_property_ignored_warning',
|
|
543
|
+
source_property_ignored_warning_id: `${VESSEL_DSP_PROPERTY_SIDECAR_ID_PREFIX}${sourceComponentIdValue}:${propertyName}`,
|
|
544
|
+
source_component_id: sourceComponentIdValue,
|
|
545
|
+
property_name: propertyName,
|
|
546
|
+
error_type: 'source_property_ignored_warning',
|
|
547
|
+
message: `${VESSEL_DSP_PROPERTY_JSON_PREFIX}${stableJsonStringify({
|
|
548
|
+
property: propertyName,
|
|
549
|
+
value,
|
|
550
|
+
})}`,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return sidecars;
|
|
555
|
+
}
|
|
556
|
+
function circuitJsonConsumedPropertyKeys(component) {
|
|
557
|
+
const consumed = new Set();
|
|
558
|
+
const quantityKey = quantityKeyForComponent(component.kind);
|
|
559
|
+
if (quantityKey !== null) {
|
|
560
|
+
for (const name of VALUE_PROPERTY_NAMES[quantityKey]) {
|
|
561
|
+
consumed.add(name);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
for (const name of MODEL_PROPERTY_NAMES) {
|
|
565
|
+
consumed.add(name);
|
|
566
|
+
}
|
|
567
|
+
consumed.add('ftype');
|
|
568
|
+
if (canCapGroupProperty(component) !== null) {
|
|
569
|
+
consumed.add('CanCapGroupId');
|
|
570
|
+
consumed.add('CanCapPackageName');
|
|
571
|
+
consumed.add('PackageName');
|
|
572
|
+
}
|
|
573
|
+
return consumed;
|
|
574
|
+
}
|
|
575
|
+
function quantityKeyForComponent(kind) {
|
|
576
|
+
switch (kind) {
|
|
577
|
+
case 'resistor':
|
|
578
|
+
case 'variable-resistor':
|
|
579
|
+
case 'potentiometer':
|
|
580
|
+
return 'resistance';
|
|
581
|
+
case 'capacitor':
|
|
582
|
+
return 'capacitance';
|
|
583
|
+
case 'inductor':
|
|
584
|
+
return 'inductance';
|
|
585
|
+
case 'voltage-source':
|
|
586
|
+
case 'battery':
|
|
587
|
+
case 'rail':
|
|
588
|
+
return 'voltage';
|
|
589
|
+
case 'current-source':
|
|
590
|
+
return 'current';
|
|
591
|
+
default:
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function sourcePortElement(component, terminalName) {
|
|
662
596
|
const voltage = component.kind === 'rail' ? quantity(component, 'voltage') : null;
|
|
663
597
|
return {
|
|
664
598
|
type: 'source_port',
|
|
@@ -671,15 +605,8 @@ function sourcePortElement(component: Component, terminalName: string): CircuitJ
|
|
|
671
605
|
...(voltage !== null ? { provides_voltage: voltage.value.value } : {}),
|
|
672
606
|
};
|
|
673
607
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
connectivity: Connectivity,
|
|
677
|
-
names: ReadonlyMap<NodeId, string>,
|
|
678
|
-
sourcePortIdsByNode: ReadonlyMap<NodeId, readonly string[]>,
|
|
679
|
-
warnings: string[],
|
|
680
|
-
): readonly CircuitJsonSourceTrace[] {
|
|
681
|
-
const traces: CircuitJsonSourceTrace[] = [];
|
|
682
|
-
|
|
608
|
+
function sourceTraceElements(connectivity, names, sourcePortIdsByNode, warnings) {
|
|
609
|
+
const traces = [];
|
|
683
610
|
for (let nodeId = 0; nodeId < connectivity.nodeCount; nodeId += 1) {
|
|
684
611
|
const sourcePortIds = sourcePortIdsByNode.get(nodeId) ?? [];
|
|
685
612
|
if (sourcePortIds.length === 0) {
|
|
@@ -694,17 +621,11 @@ function sourceTraceElements(
|
|
|
694
621
|
display_name: names.get(nodeId) ?? (connectivity.groundNodeId === nodeId ? 'GND' : `N${nodeId}`),
|
|
695
622
|
});
|
|
696
623
|
}
|
|
697
|
-
|
|
698
624
|
return traces;
|
|
699
625
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
sourceTraces: readonly CircuitJsonSourceTrace[],
|
|
704
|
-
): AnyCircuitElement[] {
|
|
705
|
-
const elements: AnyCircuitElement[] = [];
|
|
706
|
-
const schematicPortBySourcePortId = new Map<string, string>();
|
|
707
|
-
|
|
626
|
+
function schematicElements(doc, sourceTraces) {
|
|
627
|
+
const elements = [];
|
|
628
|
+
const schematicPortBySourcePortId = new Map();
|
|
708
629
|
for (const component of doc.components) {
|
|
709
630
|
if (SOURCE_ONLY_NET_NAME_KINDS.has(component.kind)) {
|
|
710
631
|
continue;
|
|
@@ -738,7 +659,6 @@ function schematicElements(
|
|
|
738
659
|
});
|
|
739
660
|
}
|
|
740
661
|
}
|
|
741
|
-
|
|
742
662
|
for (const trace of sourceTraces) {
|
|
743
663
|
const sourcePortIds = trace.connected_source_port_ids;
|
|
744
664
|
if (sourcePortIds.length < 2) {
|
|
@@ -762,7 +682,6 @@ function schematicElements(
|
|
|
762
682
|
edges,
|
|
763
683
|
});
|
|
764
684
|
}
|
|
765
|
-
|
|
766
685
|
for (const [index, directive] of doc.directives.entries()) {
|
|
767
686
|
elements.push({
|
|
768
687
|
type: 'schematic_text',
|
|
@@ -775,19 +694,16 @@ function schematicElements(
|
|
|
775
694
|
anchor: 'left',
|
|
776
695
|
});
|
|
777
696
|
}
|
|
778
|
-
|
|
779
697
|
return elements;
|
|
780
698
|
}
|
|
781
|
-
|
|
782
|
-
function normalizeCircuitJsonElements(elements: readonly unknown[]): CircuitJson {
|
|
699
|
+
function normalizeCircuitJsonElements(elements) {
|
|
783
700
|
const result = validateCircuitJsonDocument(elements);
|
|
784
701
|
if (!result.valid) {
|
|
785
702
|
throw new Error(`generated invalid Circuit JSON: ${result.errors.map((error) => `${error.path ?? '<root>'}: ${error.message}`).join('; ')}`);
|
|
786
703
|
}
|
|
787
704
|
return result.elements;
|
|
788
705
|
}
|
|
789
|
-
|
|
790
|
-
function schematicSymbolName(component: Component): string {
|
|
706
|
+
function schematicSymbolName(component) {
|
|
791
707
|
switch (component.kind) {
|
|
792
708
|
case 'resistor':
|
|
793
709
|
case 'variable-resistor':
|
|
@@ -843,8 +759,7 @@ function schematicSymbolName(component: Component): string {
|
|
|
843
759
|
return 'testpoint_right';
|
|
844
760
|
}
|
|
845
761
|
}
|
|
846
|
-
|
|
847
|
-
function schematicComponentSize(component: Component): { readonly width: number; readonly height: number } {
|
|
762
|
+
function schematicComponentSize(component) {
|
|
848
763
|
switch (component.kind) {
|
|
849
764
|
case 'resistor':
|
|
850
765
|
case 'variable-resistor':
|
|
@@ -872,13 +787,11 @@ function schematicComponentSize(component: Component): { readonly width: number;
|
|
|
872
787
|
return DEFAULT_SCHEMATIC_COMPONENT_SIZE;
|
|
873
788
|
}
|
|
874
789
|
}
|
|
875
|
-
|
|
876
|
-
function schematicDisplayValue(component: Component): string | undefined {
|
|
790
|
+
function schematicDisplayValue(component) {
|
|
877
791
|
const value = quantity(component, 'resistance') ?? quantity(component, 'capacitance') ?? quantity(component, 'inductance') ?? quantity(component, 'voltage') ?? quantity(component, 'current');
|
|
878
792
|
return value?.value.raw;
|
|
879
793
|
}
|
|
880
|
-
|
|
881
|
-
function schematicPortFacingDirection(origin: Point, terminalPosition: Point): CircuitJsonSchematicPortDirection {
|
|
794
|
+
function schematicPortFacingDirection(origin, terminalPosition) {
|
|
882
795
|
const dx = terminalPosition.x - origin.x;
|
|
883
796
|
const dy = terminalPosition.y - origin.y;
|
|
884
797
|
if (Math.abs(dx) >= Math.abs(dy)) {
|
|
@@ -886,13 +799,11 @@ function schematicPortFacingDirection(origin: Point, terminalPosition: Point): C
|
|
|
886
799
|
}
|
|
887
800
|
return dy < 0 ? 'up' : 'down';
|
|
888
801
|
}
|
|
889
|
-
|
|
890
|
-
function isPolarizedCapacitor(component: Component): boolean {
|
|
802
|
+
function isPolarizedCapacitor(component) {
|
|
891
803
|
const text = searchablePropertyText(component);
|
|
892
804
|
return text.includes('electrolytic') || text.includes('polar');
|
|
893
805
|
}
|
|
894
|
-
|
|
895
|
-
function diodeSymbolName(component: Component): string {
|
|
806
|
+
function diodeSymbolName(component) {
|
|
896
807
|
const text = searchablePropertyText(component);
|
|
897
808
|
if (text.includes('zener')) {
|
|
898
809
|
return 'zener_diode_horz';
|
|
@@ -902,8 +813,7 @@ function diodeSymbolName(component: Component): string {
|
|
|
902
813
|
}
|
|
903
814
|
return 'diode_right';
|
|
904
815
|
}
|
|
905
|
-
|
|
906
|
-
function mosfetSymbolName(component: Component): string {
|
|
816
|
+
function mosfetSymbolName(component) {
|
|
907
817
|
const channel = inferMosfetChannel(component);
|
|
908
818
|
const mode = inferMosfetMode(component);
|
|
909
819
|
if (channel === 'p') {
|
|
@@ -911,9 +821,8 @@ function mosfetSymbolName(component: Component): string {
|
|
|
911
821
|
}
|
|
912
822
|
return mode === 'depletion' ? 'n_channel_d_mosfet_transistor_horz' : 'n_channel_e_mosfet_transistor_horz';
|
|
913
823
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
const names = new Map<NodeId, string>();
|
|
824
|
+
function netNames(doc, connectivity) {
|
|
825
|
+
const names = new Map();
|
|
917
826
|
for (const component of doc.components) {
|
|
918
827
|
if (component.terminals.length === 0) {
|
|
919
828
|
continue;
|
|
@@ -938,9 +847,8 @@ function netNames(doc: CircuitDocument, connectivity: Connectivity): ReadonlyMap
|
|
|
938
847
|
}
|
|
939
848
|
return names;
|
|
940
849
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
const ids = new Set<NodeId>();
|
|
850
|
+
function railNodeIds(doc, connectivity) {
|
|
851
|
+
const ids = new Set();
|
|
944
852
|
for (const component of doc.components) {
|
|
945
853
|
if (component.kind !== 'rail') {
|
|
946
854
|
continue;
|
|
@@ -957,12 +865,8 @@ function railNodeIds(doc: CircuitDocument, connectivity: Connectivity): Readonly
|
|
|
957
865
|
}
|
|
958
866
|
return ids;
|
|
959
867
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
doc: CircuitDocument,
|
|
963
|
-
exportedComponentIds: ReadonlySet<string>,
|
|
964
|
-
): readonly string[] {
|
|
965
|
-
const warnings: string[] = [];
|
|
868
|
+
function sourceOnlyWarnings(doc, exportedComponentIds) {
|
|
869
|
+
const warnings = [];
|
|
966
870
|
for (const component of doc.components) {
|
|
967
871
|
if (exportedComponentIds.has(component.id)) {
|
|
968
872
|
continue;
|
|
@@ -973,8 +877,7 @@ function sourceOnlyWarnings(
|
|
|
973
877
|
}
|
|
974
878
|
return warnings;
|
|
975
879
|
}
|
|
976
|
-
|
|
977
|
-
function appendSourcePort(map: Map<NodeId, string[]>, nodeId: NodeId, sourcePortIdValue: string): void {
|
|
880
|
+
function appendSourcePort(map, nodeId, sourcePortIdValue) {
|
|
978
881
|
const existing = map.get(nodeId);
|
|
979
882
|
if (existing === undefined) {
|
|
980
883
|
map.set(nodeId, [sourcePortIdValue]);
|
|
@@ -982,8 +885,7 @@ function appendSourcePort(map: Map<NodeId, string[]>, nodeId: NodeId, sourcePort
|
|
|
982
885
|
}
|
|
983
886
|
existing.push(sourcePortIdValue);
|
|
984
887
|
}
|
|
985
|
-
|
|
986
|
-
function quantity(component: Component, key: QuantityKey): QuantityLookup | null {
|
|
888
|
+
function quantity(component, key) {
|
|
987
889
|
const names = VALUE_PROPERTY_NAMES[key];
|
|
988
890
|
for (const name of names) {
|
|
989
891
|
const value = component.properties[name];
|
|
@@ -994,17 +896,10 @@ function quantity(component: Component, key: QuantityKey): QuantityLookup | null
|
|
|
994
896
|
}
|
|
995
897
|
return null;
|
|
996
898
|
}
|
|
997
|
-
|
|
998
|
-
function propertyQuantity(value: PropertyValue | undefined): ParsedQuantity | null {
|
|
899
|
+
function propertyQuantity(value) {
|
|
999
900
|
return propertyQuantityValue(value);
|
|
1000
901
|
}
|
|
1001
|
-
|
|
1002
|
-
function missingQuantityComponent(
|
|
1003
|
-
component: Component,
|
|
1004
|
-
quantityName: string,
|
|
1005
|
-
warnings: string[],
|
|
1006
|
-
manufacturerPartNumber: string | null,
|
|
1007
|
-
): CircuitJsonSourceComponent {
|
|
902
|
+
function missingQuantityComponent(component, quantityName, warnings, manufacturerPartNumber) {
|
|
1008
903
|
warnings.push(`${component.id} (${component.kind}): missing ${quantityName}; emitted opaque simple_chip source component metadata only`);
|
|
1009
904
|
return {
|
|
1010
905
|
...sourceComponentBase(component),
|
|
@@ -1012,8 +907,7 @@ function missingQuantityComponent(
|
|
|
1012
907
|
...(manufacturerPartNumber !== null ? { manufacturer_part_number: manufacturerPartNumber } : {}),
|
|
1013
908
|
};
|
|
1014
909
|
}
|
|
1015
|
-
|
|
1016
|
-
function firstStringProperty(component: Component, names: readonly string[]): string | null {
|
|
910
|
+
function firstStringProperty(component, names) {
|
|
1017
911
|
for (const name of names) {
|
|
1018
912
|
const value = component.properties[name];
|
|
1019
913
|
const text = propertyStringValue(value);
|
|
@@ -1023,29 +917,24 @@ function firstStringProperty(component: Component, names: readonly string[]): st
|
|
|
1023
917
|
}
|
|
1024
918
|
return null;
|
|
1025
919
|
}
|
|
1026
|
-
|
|
1027
|
-
function inferTransistorType(component: Component): 'npn' | 'pnp' {
|
|
920
|
+
function inferTransistorType(component) {
|
|
1028
921
|
const searchable = searchablePropertyText(component);
|
|
1029
922
|
return searchable.includes('pnp') ? 'pnp' : 'npn';
|
|
1030
923
|
}
|
|
1031
|
-
|
|
1032
|
-
function inferMosfetChannel(component: Component): 'n' | 'p' {
|
|
924
|
+
function inferMosfetChannel(component) {
|
|
1033
925
|
const searchable = searchablePropertyText(component);
|
|
1034
926
|
return searchable.includes('pmos') || searchable.includes('p-channel') || searchable.includes('p channel') ? 'p' : 'n';
|
|
1035
927
|
}
|
|
1036
|
-
|
|
1037
|
-
function inferJfetChannel(component: Component): 'n' | 'p' {
|
|
928
|
+
function inferJfetChannel(component) {
|
|
1038
929
|
const searchable = searchablePropertyText(component);
|
|
1039
930
|
return searchable.includes('pjf') || searchable.includes('p-channel') || searchable.includes('p channel') ? 'p' : 'n';
|
|
1040
931
|
}
|
|
1041
|
-
|
|
1042
|
-
function inferMosfetMode(component: Component): 'enhancement' | 'depletion' {
|
|
932
|
+
function inferMosfetMode(component) {
|
|
1043
933
|
const searchable = searchablePropertyText(component);
|
|
1044
934
|
return searchable.includes('depletion') ? 'depletion' : 'enhancement';
|
|
1045
935
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
const values: string[] = [component.name, component.sourceTypeName ?? ''];
|
|
936
|
+
function searchablePropertyText(component) {
|
|
937
|
+
const values = [component.name, component.sourceTypeName ?? ''];
|
|
1049
938
|
for (const value of Object.values(component.properties)) {
|
|
1050
939
|
const text = propertyStringValue(value);
|
|
1051
940
|
if (text !== null) {
|
|
@@ -1054,24 +943,19 @@ function searchablePropertyText(component: Component): string {
|
|
|
1054
943
|
}
|
|
1055
944
|
return values.join(' ').toLowerCase();
|
|
1056
945
|
}
|
|
1057
|
-
|
|
1058
|
-
function sourceComponentId(componentId: string): string {
|
|
946
|
+
function sourceComponentId(componentId) {
|
|
1059
947
|
return `source_component:${componentId}`;
|
|
1060
948
|
}
|
|
1061
|
-
|
|
1062
|
-
function sourcePortId(componentId: string, terminalName: string): string {
|
|
949
|
+
function sourcePortId(componentId, terminalName) {
|
|
1063
950
|
return `source_port:${componentId}:${terminalName}`;
|
|
1064
951
|
}
|
|
1065
|
-
|
|
1066
|
-
function sourceNetId(nodeId: NodeId): string {
|
|
952
|
+
function sourceNetId(nodeId) {
|
|
1067
953
|
return `source_net:${nodeId}`;
|
|
1068
954
|
}
|
|
1069
|
-
|
|
1070
|
-
function sourceTraceId(nodeId: NodeId): string {
|
|
955
|
+
function sourceTraceId(nodeId) {
|
|
1071
956
|
return `source_trace:${nodeId}`;
|
|
1072
957
|
}
|
|
1073
|
-
|
|
1074
|
-
function sourcePortPosition(doc: CircuitDocument, sourcePortIdValue: string): Point {
|
|
958
|
+
function sourcePortPosition(doc, sourcePortIdValue) {
|
|
1075
959
|
const parsed = parseSourcePortId(sourcePortIdValue);
|
|
1076
960
|
if (parsed === null) {
|
|
1077
961
|
return { x: 0, y: 0 };
|
|
@@ -1080,19 +964,16 @@ function sourcePortPosition(doc: CircuitDocument, sourcePortIdValue: string): Po
|
|
|
1080
964
|
const terminal = component?.terminals.find((candidate) => candidate.name === parsed.terminalName);
|
|
1081
965
|
return toTscircuitSchematicPoint(terminal?.position ?? component?.origin ?? { x: 0, y: 0 });
|
|
1082
966
|
}
|
|
1083
|
-
|
|
1084
|
-
function toTscircuitSchematicPoint(point: Point): Point {
|
|
967
|
+
function toTscircuitSchematicPoint(point) {
|
|
1085
968
|
return {
|
|
1086
969
|
x: roundTscircuitSchematicCoordinate(point.x * TSCIRCUIT_SCHEMATIC_COORD_SCALE),
|
|
1087
970
|
y: roundTscircuitSchematicCoordinate(point.y * TSCIRCUIT_SCHEMATIC_COORD_SCALE),
|
|
1088
971
|
};
|
|
1089
972
|
}
|
|
1090
|
-
|
|
1091
|
-
function roundTscircuitSchematicCoordinate(value: number): number {
|
|
973
|
+
function roundTscircuitSchematicCoordinate(value) {
|
|
1092
974
|
return Math.round(value * 1000) / 1000;
|
|
1093
975
|
}
|
|
1094
|
-
|
|
1095
|
-
function parseSourcePortId(sourcePortIdValue: string): Readonly<{ componentId: string; terminalName: string }> | null {
|
|
976
|
+
function parseSourcePortId(sourcePortIdValue) {
|
|
1096
977
|
const prefix = 'source_port:';
|
|
1097
978
|
if (!sourcePortIdValue.startsWith(prefix)) {
|
|
1098
979
|
return null;
|
|
@@ -1107,23 +988,16 @@ function parseSourcePortId(sourcePortIdValue: string): Readonly<{ componentId: s
|
|
|
1107
988
|
terminalName: rest.slice(separator + 1),
|
|
1108
989
|
};
|
|
1109
990
|
}
|
|
1110
|
-
|
|
1111
|
-
function summarizeSchemaIssues(
|
|
1112
|
-
issues: readonly {
|
|
1113
|
-
readonly path: readonly (string | number)[];
|
|
1114
|
-
readonly message: string;
|
|
1115
|
-
}[],
|
|
1116
|
-
): string {
|
|
991
|
+
function summarizeSchemaIssues(issues) {
|
|
1117
992
|
return issues
|
|
1118
993
|
.slice(0, 3)
|
|
1119
994
|
.map((issue) => {
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
995
|
+
const path = issue.path.length === 0 ? '<root>' : issue.path.join('.');
|
|
996
|
+
return `${path}: ${issue.message}`;
|
|
997
|
+
})
|
|
1123
998
|
.join('; ');
|
|
1124
999
|
}
|
|
1125
|
-
|
|
1126
|
-
function shallowSchemaIssue(value: unknown): string | null {
|
|
1000
|
+
function shallowSchemaIssue(value) {
|
|
1127
1001
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
1128
1002
|
return 'Circuit JSON element must be an object';
|
|
1129
1003
|
}
|
|
@@ -1134,33 +1008,61 @@ function shallowSchemaIssue(value: unknown): string | null {
|
|
|
1134
1008
|
}
|
|
1135
1009
|
return null;
|
|
1136
1010
|
}
|
|
1137
|
-
|
|
1138
|
-
function checkedRecord(value: unknown): JsonRecord {
|
|
1011
|
+
function checkedRecord(value) {
|
|
1139
1012
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
1140
1013
|
throw new Error('expected Circuit JSON element object');
|
|
1141
1014
|
}
|
|
1142
1015
|
return Object.fromEntries(Object.entries(value));
|
|
1143
1016
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1017
|
+
function vesselDspPropertySidecar(record) {
|
|
1018
|
+
const sourceComponentIdValue = stringField(record, 'source_component_id');
|
|
1019
|
+
const propertyName = stringField(record, 'property_name');
|
|
1020
|
+
const message = stringField(record, 'message');
|
|
1021
|
+
if (sourceComponentIdValue === null || propertyName === null || message === null) {
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
if (!message.startsWith(VESSEL_DSP_PROPERTY_JSON_PREFIX)) {
|
|
1025
|
+
return null;
|
|
1026
|
+
}
|
|
1027
|
+
const payload = parseJsonRecord(message.slice(VESSEL_DSP_PROPERTY_JSON_PREFIX.length));
|
|
1028
|
+
if (payload === null || payload.property !== propertyName) {
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
const value = payload.value;
|
|
1032
|
+
if (!isPropertyValue(value)) {
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
return {
|
|
1036
|
+
sourceComponentId: sourceComponentIdValue,
|
|
1037
|
+
propertyName,
|
|
1038
|
+
value,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
function parseJsonRecord(source) {
|
|
1042
|
+
try {
|
|
1043
|
+
const parsed = JSON.parse(source);
|
|
1044
|
+
return isJsonRecord(parsed) ? parsed : null;
|
|
1045
|
+
}
|
|
1046
|
+
catch {
|
|
1047
|
+
return null;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function stringField(record, key) {
|
|
1146
1051
|
const value = record[key];
|
|
1147
1052
|
return typeof value === 'string' ? value : null;
|
|
1148
1053
|
}
|
|
1149
|
-
|
|
1150
|
-
function requiredStringField(record: JsonRecord, key: string): string {
|
|
1054
|
+
function requiredStringField(record, key) {
|
|
1151
1055
|
const value = stringField(record, key);
|
|
1152
1056
|
if (value === null) {
|
|
1153
1057
|
throw new Error(`Circuit JSON element is missing string field ${key}`);
|
|
1154
1058
|
}
|
|
1155
1059
|
return value;
|
|
1156
1060
|
}
|
|
1157
|
-
|
|
1158
|
-
function numericField(record: JsonRecord, key: string): number | null {
|
|
1061
|
+
function numericField(record, key) {
|
|
1159
1062
|
const value = record[key];
|
|
1160
1063
|
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
1161
1064
|
}
|
|
1162
|
-
|
|
1163
|
-
function pointField(record: JsonRecord, key: string): Point | null {
|
|
1065
|
+
function pointField(record, key) {
|
|
1164
1066
|
const value = record[key];
|
|
1165
1067
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
1166
1068
|
return null;
|
|
@@ -1170,30 +1072,57 @@ function pointField(record: JsonRecord, key: string): Point | null {
|
|
|
1170
1072
|
const y = pointRecord.y;
|
|
1171
1073
|
return typeof x === 'number' && typeof y === 'number' ? { x, y } : null;
|
|
1172
1074
|
}
|
|
1173
|
-
|
|
1174
|
-
function stringArrayField(record: JsonRecord, key: string): readonly string[] {
|
|
1075
|
+
function stringArrayField(record, key) {
|
|
1175
1076
|
const value = record[key];
|
|
1176
|
-
return Array.isArray(value) ? value.filter((entry)
|
|
1077
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
|
|
1177
1078
|
}
|
|
1178
|
-
|
|
1179
|
-
function stripKnownPrefix(value: string, prefix: string): string | null {
|
|
1079
|
+
function stripKnownPrefix(value, prefix) {
|
|
1180
1080
|
return value.startsWith(prefix) ? value.slice(prefix.length) : null;
|
|
1181
1081
|
}
|
|
1182
|
-
|
|
1183
|
-
function sanitizeId(value: string): string {
|
|
1082
|
+
function sanitizeId(value) {
|
|
1184
1083
|
const sanitized = value.replace(/\s+/g, '-').replace(/[^A-Za-z0-9_-]/g, '');
|
|
1185
1084
|
return sanitized.length > 0 ? sanitized : 'component';
|
|
1186
1085
|
}
|
|
1187
|
-
|
|
1188
|
-
|
|
1086
|
+
function stableJsonStringify(value) {
|
|
1087
|
+
return JSON.stringify(stableJsonValue(value)) ?? 'null';
|
|
1088
|
+
}
|
|
1089
|
+
function stableJsonValue(value) {
|
|
1090
|
+
if (Array.isArray(value)) {
|
|
1091
|
+
return value.map(stableJsonValue);
|
|
1092
|
+
}
|
|
1093
|
+
if (!isJsonRecord(value)) {
|
|
1094
|
+
return value;
|
|
1095
|
+
}
|
|
1096
|
+
const out = {};
|
|
1097
|
+
for (const key of Object.keys(value).sort()) {
|
|
1098
|
+
out[key] = stableJsonValue(value[key]);
|
|
1099
|
+
}
|
|
1100
|
+
return out;
|
|
1101
|
+
}
|
|
1102
|
+
function isPropertyValue(value) {
|
|
1103
|
+
if (value === null ||
|
|
1104
|
+
typeof value === 'string' ||
|
|
1105
|
+
typeof value === 'boolean' ||
|
|
1106
|
+
(typeof value === 'number' && Number.isFinite(value))) {
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
if (Array.isArray(value)) {
|
|
1110
|
+
return value.every(isPropertyValue);
|
|
1111
|
+
}
|
|
1112
|
+
if (!isJsonRecord(value)) {
|
|
1113
|
+
return false;
|
|
1114
|
+
}
|
|
1115
|
+
return Object.values(value).every(isPropertyValue);
|
|
1116
|
+
}
|
|
1117
|
+
function isJsonRecord(value) {
|
|
1118
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1119
|
+
}
|
|
1120
|
+
function terminalNameFromSourcePortId(sourcePortIdValue) {
|
|
1189
1121
|
const parsed = parseSourcePortId(sourcePortIdValue);
|
|
1190
1122
|
return parsed?.terminalName ?? sanitizeId(sourcePortIdValue);
|
|
1191
1123
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
sourcePorts: ReadonlyMap<string, SourcePortRecord>,
|
|
1195
|
-
): ReadonlyMap<string, readonly SourcePortRecord[]> {
|
|
1196
|
-
const map = new Map<string, SourcePortRecord[]>();
|
|
1124
|
+
function groupPortsByComponent(sourcePorts) {
|
|
1125
|
+
const map = new Map();
|
|
1197
1126
|
for (const port of sourcePorts.values()) {
|
|
1198
1127
|
const existing = map.get(port.componentSourceId);
|
|
1199
1128
|
if (existing === undefined) {
|
|
@@ -1204,20 +1133,15 @@ function groupPortsByComponent(
|
|
|
1204
1133
|
}
|
|
1205
1134
|
return map;
|
|
1206
1135
|
}
|
|
1207
|
-
|
|
1208
|
-
function buildComponentFromCircuitJson(
|
|
1209
|
-
sourceComponent: SourceComponentRecord,
|
|
1210
|
-
ports: readonly SourcePortRecord[],
|
|
1211
|
-
schematicComponents: ReadonlyMap<string, SchematicComponentRecord>,
|
|
1212
|
-
schematicPorts: ReadonlyMap<string, SchematicPortRecord>,
|
|
1213
|
-
index: number,
|
|
1214
|
-
): MutableComponentBuild & { readonly component: Component } {
|
|
1136
|
+
function buildComponentFromCircuitJson(sourceComponent, ports, schematicComponents, schematicPorts, sourceGroups, sidecars, index) {
|
|
1215
1137
|
const origin = schematicComponents.get(sourceComponent.sourceComponentId)?.center ?? synthesizedOrigin(index);
|
|
1216
1138
|
const terminals = ports.map((port, portIndex) => ({
|
|
1217
1139
|
name: port.terminalName,
|
|
1218
1140
|
position: schematicPorts.get(port.sourcePortId)?.center ?? synthesizedTerminalPosition(origin, ports.length, portIndex),
|
|
1219
1141
|
}));
|
|
1220
|
-
const
|
|
1142
|
+
const sidecarProperties = propertiesFromSidecars(sidecars);
|
|
1143
|
+
const groupedProperties = canCapPropertiesFromSourceGroup(sourceComponent, sourceGroups, sidecarProperties);
|
|
1144
|
+
const component = {
|
|
1221
1145
|
id: sourceComponent.componentId,
|
|
1222
1146
|
kind: kindFromCircuitJsonFtype(sourceComponent.ftype),
|
|
1223
1147
|
name: sourceComponent.name,
|
|
@@ -1225,19 +1149,49 @@ function buildComponentFromCircuitJson(
|
|
|
1225
1149
|
rotation: 0,
|
|
1226
1150
|
flipped: false,
|
|
1227
1151
|
terminals,
|
|
1228
|
-
properties:
|
|
1152
|
+
properties: {
|
|
1153
|
+
...propertiesFromCircuitJsonComponent(sourceComponent.record),
|
|
1154
|
+
...sidecarProperties,
|
|
1155
|
+
...groupedProperties,
|
|
1156
|
+
},
|
|
1229
1157
|
sourceTypeName: sourceComponent.ftype === null ? 'circuit-json:source_component' : `circuit-json:${sourceComponent.ftype}`,
|
|
1230
1158
|
};
|
|
1231
1159
|
return { sourceComponent, ports, origin, terminals, component };
|
|
1232
1160
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1161
|
+
function propertiesFromSidecars(sidecars) {
|
|
1162
|
+
const properties = {};
|
|
1163
|
+
for (const sidecar of sidecars) {
|
|
1164
|
+
properties[sidecar.propertyName] = sidecar.value;
|
|
1165
|
+
}
|
|
1166
|
+
return properties;
|
|
1167
|
+
}
|
|
1168
|
+
function canCapPropertiesFromSourceGroup(sourceComponent, sourceGroups, sidecarProperties) {
|
|
1169
|
+
const sourceGroupId = sourceComponent.sourceGroupId;
|
|
1170
|
+
if (sourceGroupId === null) {
|
|
1171
|
+
return {};
|
|
1172
|
+
}
|
|
1173
|
+
if (kindFromCircuitJsonFtype(sourceComponent.ftype) !== 'capacitor') {
|
|
1174
|
+
return {};
|
|
1175
|
+
}
|
|
1176
|
+
const discriminatorGroupId = canCapGroupIdFromSourceGroupId(sourceGroupId);
|
|
1177
|
+
const hasCanCapSection = sidecarProperties.CanCapSection !== undefined;
|
|
1178
|
+
if (discriminatorGroupId === null && !hasCanCapSection) {
|
|
1179
|
+
return {};
|
|
1180
|
+
}
|
|
1181
|
+
const group = sourceGroups.get(sourceGroupId);
|
|
1182
|
+
const fallbackGroupId = stripKnownPrefix(sourceGroupId, 'source_group:') ?? sourceGroupId;
|
|
1183
|
+
const canCapGroupId = discriminatorGroupId ?? fallbackGroupId;
|
|
1184
|
+
return {
|
|
1185
|
+
CanCapGroupId: canCapGroupId,
|
|
1186
|
+
...(group?.name === null || group?.name === undefined ? {} : { CanCapPackageName: group.name }),
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
function synthesizedOrigin(index) {
|
|
1235
1190
|
const column = index % 4;
|
|
1236
1191
|
const row = Math.floor(index / 4);
|
|
1237
1192
|
return { x: column * 80, y: row * 60 };
|
|
1238
1193
|
}
|
|
1239
|
-
|
|
1240
|
-
function synthesizedTerminalPosition(origin: Point, terminalCount: number, index: number): Point {
|
|
1194
|
+
function synthesizedTerminalPosition(origin, terminalCount, index) {
|
|
1241
1195
|
if (terminalCount <= 1) {
|
|
1242
1196
|
return origin;
|
|
1243
1197
|
}
|
|
@@ -1249,8 +1203,7 @@ function synthesizedTerminalPosition(origin: Point, terminalCount: number, index
|
|
|
1249
1203
|
const step = terminalCount === 1 ? 0 : 40 / (terminalCount - 1);
|
|
1250
1204
|
return { x: origin.x - 20, y: origin.y - 20 + step * index };
|
|
1251
1205
|
}
|
|
1252
|
-
|
|
1253
|
-
function kindFromCircuitJsonFtype(ftype: string | null): ComponentKind {
|
|
1206
|
+
function kindFromCircuitJsonFtype(ftype) {
|
|
1254
1207
|
switch (ftype) {
|
|
1255
1208
|
case 'simple_resistor':
|
|
1256
1209
|
return 'resistor';
|
|
@@ -1293,9 +1246,8 @@ function kindFromCircuitJsonFtype(ftype: string | null): ComponentKind {
|
|
|
1293
1246
|
return 'unsupported';
|
|
1294
1247
|
}
|
|
1295
1248
|
}
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
const properties: Record<string, PropertyValue> = {};
|
|
1249
|
+
function propertiesFromCircuitJsonComponent(record) {
|
|
1250
|
+
const properties = {};
|
|
1299
1251
|
const ftype = stringField(record, 'ftype');
|
|
1300
1252
|
const displayValue = stringField(record, 'display_value');
|
|
1301
1253
|
if (displayValue !== null) {
|
|
@@ -1316,14 +1268,7 @@ function propertiesFromCircuitJsonComponent(record: JsonRecord): Readonly<Record
|
|
|
1316
1268
|
}
|
|
1317
1269
|
return properties;
|
|
1318
1270
|
}
|
|
1319
|
-
|
|
1320
|
-
function addQuantityProperty(
|
|
1321
|
-
properties: Record<string, PropertyValue>,
|
|
1322
|
-
propertyName: string,
|
|
1323
|
-
record: JsonRecord,
|
|
1324
|
-
displayKey: string | null,
|
|
1325
|
-
numericKey: string,
|
|
1326
|
-
): void {
|
|
1271
|
+
function addQuantityProperty(properties, propertyName, record, displayKey, numericKey) {
|
|
1327
1272
|
const display = displayKey === null ? null : stringField(record, displayKey);
|
|
1328
1273
|
if (display !== null) {
|
|
1329
1274
|
properties[propertyName] = parseQuantity(display) ?? display;
|
|
@@ -1334,11 +1279,8 @@ function addQuantityProperty(
|
|
|
1334
1279
|
properties[propertyName] = numeric;
|
|
1335
1280
|
}
|
|
1336
1281
|
}
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
builds: readonly (MutableComponentBuild & { readonly component: Component })[],
|
|
1340
|
-
): ReadonlyMap<string, Point> {
|
|
1341
|
-
const map = new Map<string, Point>();
|
|
1282
|
+
function terminalPositionMap(builds) {
|
|
1283
|
+
const map = new Map();
|
|
1342
1284
|
for (const build of builds) {
|
|
1343
1285
|
for (const port of build.ports) {
|
|
1344
1286
|
const terminal = build.component.terminals.find((candidate) => candidate.name === port.terminalName);
|
|
@@ -1349,12 +1291,8 @@ function terminalPositionMap(
|
|
|
1349
1291
|
}
|
|
1350
1292
|
return map;
|
|
1351
1293
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
sourceTraces: readonly JsonRecord[],
|
|
1355
|
-
terminalPositions: ReadonlyMap<string, Point>,
|
|
1356
|
-
): readonly Wire[] {
|
|
1357
|
-
const wires: Wire[] = [];
|
|
1294
|
+
function wireElementsFromSourceTraces(sourceTraces, terminalPositions) {
|
|
1295
|
+
const wires = [];
|
|
1358
1296
|
for (const trace of sourceTraces) {
|
|
1359
1297
|
const ports = stringArrayField(trace, 'connected_source_port_ids');
|
|
1360
1298
|
if (ports.length < 2) {
|
|
@@ -1378,18 +1316,14 @@ function wireElementsFromSourceTraces(
|
|
|
1378
1316
|
}
|
|
1379
1317
|
return wires;
|
|
1380
1318
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
sourceNets: ReadonlyMap<string, JsonRecord>,
|
|
1384
|
-
sourceTraces: readonly JsonRecord[],
|
|
1385
|
-
): readonly Warning[] {
|
|
1386
|
-
const referencedNetIds = new Set<string>();
|
|
1319
|
+
function warningsForUnconnectedNets(sourceNets, sourceTraces) {
|
|
1320
|
+
const referencedNetIds = new Set();
|
|
1387
1321
|
for (const trace of sourceTraces) {
|
|
1388
1322
|
for (const netId of stringArrayField(trace, 'connected_source_net_ids')) {
|
|
1389
1323
|
referencedNetIds.add(netId);
|
|
1390
1324
|
}
|
|
1391
1325
|
}
|
|
1392
|
-
const warnings
|
|
1326
|
+
const warnings = [];
|
|
1393
1327
|
for (const [netId, net] of sourceNets) {
|
|
1394
1328
|
if (!referencedNetIds.has(netId)) {
|
|
1395
1329
|
warnings.push({
|
|
@@ -1400,11 +1334,11 @@ function warningsForUnconnectedNets(
|
|
|
1400
1334
|
}
|
|
1401
1335
|
return warnings;
|
|
1402
1336
|
}
|
|
1403
|
-
|
|
1404
|
-
function filenameWithoutCircuitJsonExtension(filename: string): string {
|
|
1337
|
+
function filenameWithoutCircuitJsonExtension(filename) {
|
|
1405
1338
|
return filename
|
|
1406
1339
|
.replace(/\.circuit\.json$/i, '')
|
|
1407
1340
|
.replace(/\.json$/i, '')
|
|
1408
1341
|
.replace(/[-_]+/g, ' ')
|
|
1409
1342
|
.trim();
|
|
1410
1343
|
}
|
|
1344
|
+
//# sourceMappingURL=serializer.js.map
|