-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathloader.py
More file actions
187 lines (160 loc) · 8.12 KB
/
loader.py
File metadata and controls
187 lines (160 loc) · 8.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import componentLibrary
import re
import pickle
import traceback
from ursina import *
def parseKicadNetlist(file_path):
components = {}
connections = {}
with open(file_path, "r", encoding="utf-8") as file:
content = file.read()
# Extract components
comp_pattern = re.findall(r'\(comp \(ref "([^"]+)"\).*?\(value "([^"]+)"\)', content, re.DOTALL)
for ref, value in comp_pattern:
components[ref] = value
# Extract connections
net_pattern = re.findall(r'(\(net \(code "[^"]+"\) \(name "[^"]+"\)(.|\n)*?\)\)\)(?!(net )))', content, re.DOTALL)
for net_groups in net_pattern:
if len(net_groups) > 1:
net_groups = net_groups[0]
netname = re.findall(r'\(name "([^"]+)', net_groups)
net_nodes = re.findall(r'\(node \(ref "([^"]+)"\) \(pin "([^"]+)"\)', net_groups)
connections[netname[0]] = {}
for designator, pinnumber in net_nodes:
connections[netname[0]][designator] = pinnumber
return components, connections
# main loading function
def loadComponents(filename, clickFunction={}):
# detect fileending if it is a netlist or a save file
if filename[-5:] == ".ffps":
return _loadProjectFile(filename, clickFunction)
elif filename[-4:] == ".net":
return _loadNetlist(filename, clickFunction)
# parses and loads a project file
def _loadProjectFile(filename, clickFunction):
# first read in the file and unpickle it
loadStore = {}
with open(filename, "br") as file:
loadStore = pickle.load(file)
# print("loadStore", loadStore)
components = loadStore['components']
dataStore = {}
# instantiate all components from their respective class and add them to the components subdict
dataStore["components"] = {}
for designator in list(components.keys()):
saved_value = components[designator]['value']
if saved_value is None or saved_value == 'None':
print(f"Skipping {designator}: saved value is '{saved_value}' (no valid component class)")
continue
try:
componentClass = getattr(componentLibrary, saved_value)
footprint = components[designator]['footprint']
dataStore["components"][designator] = componentClass(clickFunction, footprint, designator) # add the actual component
dataStore["components"][designator].footprint.rotation = components[designator]['rotation']
dataStore["components"][designator].footprint.position = components[designator]['position']
except:
print(traceback.format_exc())
print("No component candidate found for", saved_value, "skipping")
# print('dataStore', dataStore)
# add all nets to the nets subdict
dataStore['nets'] = {}
connections = loadStore['nets']
for netname in list(connections.keys()):
nets = connections[netname]
if "unconnected" not in netname:
dataStore['nets'][netname] = nets
# add airwires
# print(connections)
dataStore['airwires'] = {}
# iterate over every netname
for netname in list(connections.keys()):
if "unconnected" not in netname:
# prepare
dataStore['airwires'][netname] = {}
# Get pins sorted by part name
pin_list = sorted(connections[netname].items(), key=lambda x: x[0])
pins = []
for part, pin in pin_list:
try:
pos = dataStore['components'][part].getPinPos(int(pin))
pins.append((part, int(pin), pos))
except:
print(f"Skipping pin {part} {pin} in net {netname}")
# For each pin, create airwire to closest other pin
for idx in range(len(pins)):
if len(pins) > 1:
closest_j = min((j for j in range(len(pins)) if j != idx), key=lambda j: distance(pins[idx][2], pins[j][2]))
start_part, start_pin, start_pos = pins[idx]
end_part, end_pin, end_pos = pins[closest_j]
if clickFunction != {}:
dataStore['airwires'][netname][str(idx+1)] = componentLibrary.AIRWIRE(start_pos, end_pos, clickFunction, netname, start_part, end_part)
else:
dataStore['airwires'][netname][str(idx+1)] = "wire" + str(idx+1)
return dataStore
# parses and loads netlists
def _loadNetlist(filename, clickFunction={}, dataStore={}):
# first get all components and connections from the netlist
components, connections = parseKicadNetlist(filename)
# instantiate all components from their respective class and add them to the components subdict
dataStore["components"] = {}
for designator in list(components.keys()):
try:
componentClass = getattr(componentLibrary, components[designator])
if clickFunction != {}:
dataStore["components"][designator] = componentClass(clickFunction, 0, designator) # add the actual component
else:
dataStore["components"][str(designator)] = components[designator] # only add name for testing purposes
except:
print(traceback.format_exc())
print("No component candidate found for", components[designator], "skipping")
# add all nets to the nets subdict
dataStore['nets'] = {}
for netname in list(connections.keys()):
nets = connections[netname]
if "unconnected" not in netname:
dataStore['nets'][netname] = nets
# add airwires
# print(connections)
dataStore['airwires'] = {}
validDesignators = dataStore['components'].keys()
# iterate over every netname
for netname in list(connections.keys()):
if "unconnected" not in netname:
# prepare
dataStore['airwires'][netname] = {}
# print(netname)
for i in range(1, len(connections[netname])):
try:
startPart = list(connections[netname].keys())[i-1]
startPin = int(list(connections[netname].values())[i-1])
endPart = list(connections[netname].keys())[i]
endPin = int(list(connections[netname].values())[i])
startPosition = dataStore['components'][startPart].getPinPos(startPin)
endPosition = dataStore['components'][endPart].getPinPos(endPin)
# print("net", netname, "wire", i, startPart, startPin, endPart, endPin, startPosition, endPosition)
if clickFunction != {}:
dataStore['airwires'][netname][str(i)] = componentLibrary.AIRWIRE(startPosition, endPosition, clickFunction, netname, startPart, endPart)
else:
dataStore['airwires'][netname][str(i)] = "wire" + str(i)
except:
print(traceback.format_exc())
print("skipping Airwire, because part does not exist.")
if dataStore['airwires'][netname] == {}:
del(dataStore['airwires'][netname])
return dataStore
def makeSaveStore(dataStore, debug=1):
saveStore = {}
saveStore["components"]= {}
saveStore['nets'] = dataStore['nets']
for designatorName in dataStore['components'].keys():
designatorObject = dataStore['components'][designatorName]
class_name = type(designatorObject).__name__
saveStore['components'][designatorName] = {'value': class_name, 'rotation': designatorObject.footprint.rotation, 'position': designatorObject.footprint.position, 'footprint': designatorObject.current_footprint}
if debug:
print("saveStore", saveStore)
return pickle.dumps(saveStore, protocol=pickle.HIGHEST_PROTOCOL)
if __name__ == "__main__":
dataStore = _loadNetlist("transistor_oscillator.net")
print(dataStore)
with open("testoutput.ffps", 'wb') as file:
file.write(makeSaveStore(dataStore, debug=1))