Redefined PinUsages; cleaned component JSONs
[Mograsim.git] / plugins / net.mograsim.logic.model.am2900 / src / net / mograsim / logic / model / examples / ReserializeAndVerifyJSONs.java
1 package net.mograsim.logic.model.examples;
2
3 import java.io.FileWriter;
4 import java.io.IOException;
5 import java.nio.file.Files;
6 import java.nio.file.Path;
7 import java.nio.file.Paths;
8 import java.util.Arrays;
9 import java.util.Collection;
10 import java.util.Comparator;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Map;
14 import java.util.Map.Entry;
15 import java.util.Scanner;
16 import java.util.Set;
17 import java.util.TreeMap;
18 import java.util.stream.Collectors;
19 import java.util.stream.Stream;
20
21 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
22 import net.mograsim.logic.model.am2900.Am2900Loader;
23 import net.mograsim.logic.model.model.LogicModelModifiable;
24 import net.mograsim.logic.model.model.components.ModelComponent;
25 import net.mograsim.logic.model.model.components.atomic.ModelTextComponent;
26 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
27 import net.mograsim.logic.model.model.wires.ModelWire;
28 import net.mograsim.logic.model.model.wires.ModelWireCrossPoint;
29 import net.mograsim.logic.model.model.wires.MovablePin;
30 import net.mograsim.logic.model.model.wires.Pin;
31 import net.mograsim.logic.model.model.wires.PinUsage;
32 import net.mograsim.logic.model.serializing.DeserializedSubmodelComponent;
33 import net.mograsim.logic.model.serializing.IdentifyParams;
34 import net.mograsim.logic.model.serializing.LogicModelParams.ComponentParams;
35 import net.mograsim.logic.model.serializing.LogicModelParams.WireParams;
36 import net.mograsim.logic.model.serializing.SubmodelComponentParams;
37 import net.mograsim.logic.model.serializing.SubmodelComponentSerializer;
38 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.StandardHighLevelStateHandler.StandardHighLevelStateHandlerParams;
39 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic.AtomicHighLevelStateHandler.AtomicHighLevelStateHandlerParams;
40 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic.DelegatingAtomicHighLevelStateHandler.DelegatingAtomicHighLevelStateHandlerParams;
41 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic.WireForcingAtomicHighLevelStateHandler.WireForcingAtomicHighLevelStateHandlerParams;
42 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.subcomponent.DelegatingSubcomponentHighLevelStateHandler.DelegatingSubcomponentHighLevelStateHandlerParams;
43 import net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.subcomponent.SubcomponentHighLevelStateHandler.SubcomponentHighLevelStateHandlerParams;
44 import net.mograsim.logic.model.util.JsonHandler;
45
46 public class ReserializeAndVerifyJSONs
47 {
48         public static double GRIDSIZE = 2.5;
49         public static boolean changePinUsages = false;
50         public static boolean changeComponentNames = true;
51         public static boolean forceDefaultComponentNames = true;
52         public static boolean changeWireNames = true;
53         public static boolean forceDefaultWireNames = true;
54         public static boolean snapWCPs = true;
55         public static boolean warnNonSnappedPoints = true;
56         public static boolean warnNonVertHorizLines = true;
57         public static boolean warnRedundantWires = true;
58
59         public static void main(String[] args) throws IOException
60         {
61                 Am2900Loader.setup();
62                 try (Scanner sysin = new Scanner(System.in))
63                 {
64                         System.out.print("Directory to search for JSONs in / JSON file to reserialize >");
65                         Path root = Paths.get(sysin.nextLine());
66                         if (!Files.exists(root))
67                                 throw new IllegalArgumentException("Path doesn't exist");
68                         if (Files.isRegularFile(root))
69                                 reserializeJSON(root, sysin);
70                         else
71                         {
72                                 System.out.print("Recursive? >");
73                                 boolean recursive = Boolean.valueOf(sysin.nextLine());
74                                 try (Stream<Path> jsons = recursive ? Files.walk(root) : Files.list(root))
75                                 {
76                                         jsons.filter(Files::isRegularFile).filter(p -> p.getFileName().toString().endsWith(".json"))
77                                                         .forEach(j -> reserializeJSON(j, sysin));
78                                 }
79                         }
80                 }
81         }
82
83         public static void reserializeJSON(Path componentPath, Scanner sysin)
84         {
85                 try
86                 {
87                         SubmodelComponentParams oldComponentJSON = JsonHandler.readJson(componentPath.toString(), SubmodelComponentParams.class);
88                         DeserializedSubmodelComponent comp = (DeserializedSubmodelComponent) SubmodelComponentSerializer
89                                         .deserialize(new LogicModelModifiable(), oldComponentJSON);
90                         System.out.println("Reserializing " + componentPath);
91                         LogicModelModifiable submodelModifiable = comp.getSubmodelModifiable();
92                         Map<String, String> componentNameRemapping = new HashMap<>();
93                         Map<String, String> wireNameRemapping = new HashMap<>();
94
95                         if (changePinUsages)
96                                 changePinUsages(sysin, comp);
97                         if (changeComponentNames)
98                                 changeComponentNames(sysin, submodelModifiable, componentNameRemapping);
99                         if (changeWireNames)
100                                 changeWireNames(sysin, submodelModifiable, wireNameRemapping);
101                         if (snapWCPs)
102                                 snapWCPs(submodelModifiable);
103                         if (warnNonSnappedPoints)
104                                 warnNonSnappedPoints(comp, submodelModifiable);
105                         if (warnNonVertHorizLines)
106                                 warnNonVertHorizLines(submodelModifiable);
107                         if (warnRedundantWires)
108                                 warnRedundantWires(submodelModifiable);
109
110                         SubmodelComponentParams newComponentJSON = SubmodelComponentSerializer.serialize(comp);
111
112                         if (changeComponentNames)
113                                 changeComponentNames_AfterSerialization(newComponentJSON, componentNameRemapping);
114                         if (changeWireNames)
115                                 changeWireNames_AfterSerialization(newComponentJSON, wireNameRemapping);
116                         sortAllJSONArrays(newComponentJSON);
117
118                         try (FileWriter writer = new FileWriter(componentPath.toString()))
119                         {
120                                 String json = JsonHandler.toJson(newComponentJSON);
121                                 json = json.replace("\u00b5", "\\u00b5");
122                                 writer.write(json);
123                         }
124                 }
125                 catch (Exception e)
126                 {
127                         System.err.println("An error occurred visiting " + componentPath + ":");
128                         e.printStackTrace();
129                 }
130         }
131
132         private static void warnRedundantWires(LogicModelModifiable submodelModifiable)
133         {
134                 Map<Pin, Set<Pin>> connectedPinGroups = new HashMap<>();
135                 submodelModifiable.getComponentsByName().values().stream().map(ModelComponent::getPins).map(Map::values).flatMap(Collection::stream)
136                                 .forEach(p -> connectedPinGroups.put(p, new HashSet<>(Arrays.asList(p))));
137                 submodelModifiable.getWiresByName().values().forEach(w ->
138                 {
139                         Pin pin1 = w.getPin1();
140                         Pin pin2 = w.getPin2();
141                         Set<Pin> pin1Group = connectedPinGroups.get(pin1);
142                         Set<Pin> pin2Group = connectedPinGroups.get(pin2);
143                         if (pin1Group == pin2Group)
144                                 System.out.println("  Wire " + w.name + " connecting " + pin1 + " and " + pin2 + " is redundant");
145                         else
146                         {
147                                 pin1Group.addAll(pin2Group);
148                                 pin2Group.forEach(p -> connectedPinGroups.put(p, pin1Group));
149                         }
150                 });
151         }
152
153         private static void changePinUsages(Scanner sysin, DeserializedSubmodelComponent comp)
154         {
155                 comp.getSubmodelPins().entrySet().stream().sorted(Comparator.comparing(Entry::getKey)).map(Entry::getValue).forEach(pin ->
156                 {
157                         PinUsage oldUsage = comp.getSupermodelPin(pin.name).usage;
158                         PinUsage usage = null;
159                         do
160                                 try
161                                 {
162                                         System.out.print("  Usage for interface pin " + pin.name + " (empty: " + oldUsage + ") >");
163                                         String usageStr = sysin.nextLine().toUpperCase();
164                                         // TODO replace with switch expression if we upgrade to Java 12
165                                         switch (usageStr)
166                                         {
167                                         case "":
168                                                 usage = oldUsage;
169                                                 break;
170                                         case "I":
171                                                 usage = PinUsage.INPUT;
172                                                 break;
173                                         case "O":
174                                                 usage = PinUsage.OUTPUT;
175                                                 break;
176                                         case "T":
177                                                 usage = PinUsage.TRISTATE;
178                                                 break;
179                                         default:
180                                                 usage = PinUsage.valueOf(usageStr);
181                                                 break;
182                                         }
183                                 }
184                                 catch (@SuppressWarnings("unused") IllegalArgumentException e)
185                                 {
186                                         System.err.println("  Illegal usage");
187                                 }
188                         while (usage == null);
189                         setInterfacePinUsage(comp, pin, usage);
190                 });
191         }
192
193         @SuppressWarnings("unused") // TextComponent
194         private static void changeComponentNames(Scanner sysin, LogicModelModifiable submodelModifiable,
195                         Map<String, String> componentNameRemapping)
196         {
197                 componentNameRemapping.put(SubmodelComponent.SUBMODEL_INTERFACE_NAME, SubmodelComponent.SUBMODEL_INTERFACE_NAME);
198                 LogicModelModifiable tempModel = new LogicModelModifiable();
199                 IdentifyParams iP = new IdentifyParams();
200                 submodelModifiable.getComponentsByName().entrySet().stream()
201                                 .filter(e -> !e.getKey().equals(SubmodelComponent.SUBMODEL_INTERFACE_NAME))
202                                 .sorted(Comparator.comparing(Entry::getKey, ReserializeAndVerifyJSONs::compareStringsWithIntegers)).forEach(e ->
203                                 {
204                                         String oldName = e.getKey();
205                                         ModelComponent subcomp = e.getValue();
206                                         String defaultName = tempModel.getDefaultComponentName(subcomp);
207                                         String newName = forceDefaultComponentNames ? defaultName : null;
208                                         while (newName == null)
209                                         {
210                                                 System.out.print("  New name for component " + oldName + " of type " + subcomp.getIDForSerializing(iP) + " (empty: "
211                                                                 + defaultName + ") >");
212                                                 newName = sysin.nextLine();
213                                                 if (newName.equals(""))
214                                                         newName = defaultName;
215                                                 if (tempModel.getComponentsByName().containsKey(newName))
216                                                 {
217                                                         System.err.println("  There already is a component with that name");
218                                                         newName = null;
219                                                 }
220                                         }
221                                         componentNameRemapping.put(oldName, newName);
222                                         new ModelTextComponent(tempModel, "", newName);
223                                 });
224         }
225
226         private static void changeComponentNames_AfterSerialization(SubmodelComponentParams newComponentJSON,
227                         Map<String, String> componentNameRemapping)
228         {
229                 for (ComponentParams cParams : newComponentJSON.submodel.components)
230                         cParams.name = componentNameRemapping.get(cParams.name);
231                 for (WireParams wParams : newComponentJSON.submodel.wires)
232                 {
233                         wParams.pin1.compName = componentNameRemapping.get(wParams.pin1.compName);
234                         wParams.pin2.compName = componentNameRemapping.get(wParams.pin2.compName);
235                 }
236                 if ("standard".equals(newComponentJSON.highLevelStateHandlerSnippetID))
237                 {
238                         StandardHighLevelStateHandlerParams hlshParams = JsonHandler.fromJsonTree(newComponentJSON.highLevelStateHandlerParams,
239                                         StandardHighLevelStateHandlerParams.class);
240                         for (AtomicHighLevelStateHandlerParams ahlshParams : hlshParams.atomicHighLevelStates.values())
241                                 if ("delegating".equals(ahlshParams.id))
242                                 {
243                                         DelegatingAtomicHighLevelStateHandlerParams dhlshParams = JsonHandler.fromJsonTree(ahlshParams.params,
244                                                         DelegatingAtomicHighLevelStateHandlerParams.class);
245                                         dhlshParams.delegateTarget = componentNameRemapping.get(dhlshParams.delegateTarget);
246                                         ahlshParams.params = JsonHandler.toJsonTree(dhlshParams);
247                                 }
248                         for (SubcomponentHighLevelStateHandlerParams shlshParams : hlshParams.subcomponentHighLevelStates.values())
249                                 if ("delegating".equals(shlshParams.id))
250                                 {
251                                         DelegatingSubcomponentHighLevelStateHandlerParams dhlshParams = JsonHandler.fromJsonTree(shlshParams.params,
252                                                         DelegatingSubcomponentHighLevelStateHandlerParams.class);
253                                         dhlshParams.delegateTarget = componentNameRemapping.get(dhlshParams.delegateTarget);
254                                         shlshParams.params = JsonHandler.toJsonTree(dhlshParams);
255                                 }
256                         newComponentJSON.highLevelStateHandlerParams = JsonHandler.toJsonTree(hlshParams);
257                 }
258         }
259
260         @SuppressWarnings("unused") // Wire
261         private static void changeWireNames(Scanner sysin, LogicModelModifiable submodelModifiable, Map<String, String> wireNameRemapping)
262         {
263                 LogicModelModifiable tempModel = new LogicModelModifiable();
264                 Pin p = new ModelWireCrossPoint(tempModel, 1).getPin();
265                 IdentifyParams iP = new IdentifyParams();
266                 submodelModifiable.getWiresByName().entrySet().stream()
267                                 .sorted(Comparator.comparing(Entry::getKey, ReserializeAndVerifyJSONs::compareStringsWithIntegers)).forEach(e ->
268                                 {
269                                         String oldName = e.getKey();
270                                         String defaultName = tempModel.getDefaultWireName();
271                                         String newName = forceDefaultWireNames ? defaultName : null;
272                                         while (newName == null)
273                                         {
274                                                 System.out.print("  New name for wire " + oldName + " (empty: " + defaultName + ") >");
275                                                 newName = sysin.nextLine();
276                                                 if (newName.equals(""))
277                                                         newName = defaultName;
278                                                 if (tempModel.getComponentsByName().containsKey(newName))
279                                                 {
280                                                         System.err.println("  There already is a component with that name");
281                                                         newName = null;
282                                                 }
283                                         }
284                                         wireNameRemapping.put(oldName, newName);
285                                         new ModelWire(tempModel, newName, p, p);
286                                 });
287         }
288
289         private static void changeWireNames_AfterSerialization(SubmodelComponentParams newComponentJSON, Map<String, String> wireNameRemapping)
290         {
291                 for (WireParams wParams : newComponentJSON.submodel.wires)
292                         wParams.name = wireNameRemapping.get(wParams.name);
293                 if ("standard".equals(newComponentJSON.highLevelStateHandlerSnippetID))
294                 {
295                         StandardHighLevelStateHandlerParams hlshParams = JsonHandler.fromJsonTree(newComponentJSON.highLevelStateHandlerParams,
296                                         StandardHighLevelStateHandlerParams.class);
297                         for (AtomicHighLevelStateHandlerParams ahlshParams : hlshParams.atomicHighLevelStates.values())
298                                 if ("wireForcing".equals(ahlshParams.id))
299                                 {
300                                         WireForcingAtomicHighLevelStateHandlerParams whlshParams = JsonHandler.fromJsonTree(ahlshParams.params,
301                                                         WireForcingAtomicHighLevelStateHandlerParams.class);
302                                         whlshParams.wiresToForce = whlshParams.wiresToForce.stream().map(wireNameRemapping::get).collect(Collectors.toList());
303                                         whlshParams.wiresToForceInverted = whlshParams.wiresToForceInverted.stream().map(wireNameRemapping::get)
304                                                         .collect(Collectors.toList());
305                                         ahlshParams.params = JsonHandler.toJsonTree(whlshParams);
306                                 }
307                         newComponentJSON.highLevelStateHandlerParams = JsonHandler.toJsonTree(hlshParams);
308                 }
309         }
310
311         private static void snapWCPs(LogicModelModifiable submodelModifiable)
312         {
313                 submodelModifiable.getComponentsByName().values().stream().filter(c -> c instanceof ModelWireCrossPoint).forEach(c ->
314                 {
315                         double x = c.getPosX();
316                         double y = c.getPosY();
317                         double newX = x % GRIDSIZE == 0 ? x - 1 : x;
318                         double newY = y % GRIDSIZE == 0 ? y - 1 : y;
319                         if (x != newX || y != newY)
320                         {
321                                 c.moveTo(newX, newY);
322                                 System.out.println("  Snapping WCP " + c.getName());
323                         }
324                 });
325         }
326
327         private static void warnNonSnappedPoints(DeserializedSubmodelComponent comp, LogicModelModifiable submodelModifiable)
328         {
329                 if (comp.getWidth() % GRIDSIZE != 0 || comp.getHeight() % GRIDSIZE != 0)
330                         System.out.println("  Size is not snapped to grid: " + comp.getWidth() + "," + comp.getHeight());
331                 submodelModifiable.getComponentsByName().values().forEach(c ->
332                 {
333                         double x = c.getPosX();
334                         double y = c.getPosY();
335                         if (c instanceof ModelWireCrossPoint)
336                         {
337                                 x++;
338                                 y++;
339                         }
340                         if (x % GRIDSIZE != 0 || y % GRIDSIZE != 0)
341                                 System.out.println("  Component " + c.getName() + " (type " + c.getIDForSerializing(new IdentifyParams())
342                                                 + ") is not snapped to grid: " + x + "," + y);
343                 });
344                 submodelModifiable.getWiresByName().values().forEach(w ->
345                 {
346                         Point[] p = w.getPath();
347                         if (p != null)
348                                 for (int i = 0; i < p.length; i++)
349                                         if (p[i].x % GRIDSIZE != 0 || p[i].y % GRIDSIZE != 0)
350                                                 System.out.println("  Wire " + w.name + " path point #" + i + " is not snapped to grid: " + p[i].x + "," + p[i].y);
351                 });
352                 comp.getPins().values().forEach(p ->
353                 {
354                         if (p.getRelX() % GRIDSIZE != 0 || p.getRelY() % GRIDSIZE != 0)
355                                 System.out.println("  Interface point " + p.name + " is not snapped to grid: " + p.getRelX() + "," + p.getRelY());
356                 });
357         }
358
359         private static void warnNonVertHorizLines(LogicModelModifiable submodelModifiable)
360         {
361                 submodelModifiable.getWiresByName().values().forEach(w ->
362                 {
363                         double[] p = w.getEffectivePath();
364                         for (int i = 1; i < p.length / 2; i++)
365                         {
366                                 double x1 = p[2 * i - 2];
367                                 double y1 = p[2 * i - 1];
368                                 double x2 = p[2 * i + 0];
369                                 double y2 = p[2 * i + 1];
370                                 if (x1 != x2 && y1 != y2)
371                                         System.out.println("  Wire " + w.name + " part #" + (i - 1) + " is neither vertical nor horizontal");
372                         }
373                 });
374         }
375
376         private static void sortAllJSONArrays(SubmodelComponentParams newComponentJSON)
377         {
378                 Comparator<String> c = ReserializeAndVerifyJSONs::compareStringsWithIntegers;
379                 Arrays.sort(newComponentJSON.interfacePins, Comparator.comparing(p -> p.name, c));
380                 Arrays.sort(newComponentJSON.submodel.components, Comparator.comparing(p -> p.name, c));
381                 Arrays.sort(newComponentJSON.submodel.wires, Comparator.comparing(p -> p.name, c));
382                 if ("standard".equals(newComponentJSON.highLevelStateHandlerSnippetID))
383                 {
384                         StandardHighLevelStateHandlerParams hlshP = JsonHandler.fromJsonTree(newComponentJSON.highLevelStateHandlerParams,
385                                         StandardHighLevelStateHandlerParams.class);
386                         TreeMap<String, AtomicHighLevelStateHandlerParams> tmp1 = new TreeMap<>(c);
387                         tmp1.putAll(hlshP.atomicHighLevelStates);
388                         hlshP.atomicHighLevelStates = tmp1;
389                         TreeMap<String, SubcomponentHighLevelStateHandlerParams> tmp2 = new TreeMap<>(c);
390                         tmp2.putAll(hlshP.subcomponentHighLevelStates);
391                         hlshP.subcomponentHighLevelStates = tmp2;
392                         newComponentJSON.highLevelStateHandlerParams = JsonHandler.toJsonTree(hlshP);
393                 }
394         }
395
396         private static int compareStringsWithIntegers(String a, String b)
397         {
398                 int aLoc = 0;
399                 int bLoc = 0;
400                 for (;;)
401                 {
402                         if (aLoc == a.length())
403                         {
404                                 if (bLoc == b.length())
405                                         return 0;
406                                 return -1;
407                         }
408                         if (bLoc == b.length())
409                                 return 1;
410                         int aInt = 1, bInt = 1;// 1 so a longer number is always greater (makes a difference for leading zeroes)
411                         boolean aHasNumber = false, bHasNumber = false;
412                         char nextCharA, nextCharB;
413                         for (;;)
414                         {
415                                 nextCharA = a.charAt(aLoc++);
416                                 if (nextCharA < '0' || nextCharA > '9')
417                                         break;
418                                 aHasNumber = true;
419                                 aInt = aInt * 10 + nextCharA - '0';
420                                 if (aLoc == a.length())
421                                         break;
422                         }
423                         for (;;)
424                         {
425                                 nextCharB = b.charAt(bLoc++);
426                                 if (nextCharB < '0' || nextCharB > '9')
427                                         break;
428                                 bHasNumber = true;
429                                 bInt = bInt * 10 + nextCharB - '0';
430                                 if (bLoc == b.length())
431                                         break;
432                         }
433                         if (aHasNumber)
434                         {
435                                 if (!bHasNumber)
436                                         return -1;
437                                 int comp = Integer.compare(aInt, bInt);
438                                 if (comp != 0)
439                                         return comp;
440                         } else
441                         {
442                                 if (bHasNumber)
443                                         return 1;
444                                 int comp = Character.compare(nextCharA, nextCharB);
445                                 if (comp != 0)
446                                         return comp;
447                         }
448                 }
449         }
450
451         private static void setInterfacePinUsage(DeserializedSubmodelComponent comp, Pin interfacePin, PinUsage usage)
452         {
453                 Set<ModelWire> wiresConnectedToPin1 = comp.submodel.getWiresByName().values().stream().filter(w -> w.getPin1() == interfacePin)
454                                 .collect(Collectors.toSet());
455                 Set<ModelWire> wiresConnectedToPin2 = comp.submodel.getWiresByName().values().stream().filter(w -> w.getPin2() == interfacePin)
456                                 .collect(Collectors.toSet());
457                 LogicModelModifiable submodelModifiable = comp.getSubmodelModifiable();
458                 wiresConnectedToPin1.forEach(submodelModifiable::destroyWire);
459                 wiresConnectedToPin2.forEach(submodelModifiable::destroyWire);
460                 Pin supermodelPin = comp.getSupermodelPin(interfacePin.name);
461                 comp.removeSubmodelInterface(interfacePin.name);
462                 comp.addSubmodelInterface(new MovablePin(submodelModifiable, comp, interfacePin.name, interfacePin.logicWidth, usage,
463                                 supermodelPin.getRelX(), supermodelPin.getRelY()));
464                 Pin interfacePinNew = comp.getSubmodelPin(interfacePin.name);
465                 wiresConnectedToPin1.forEach(w -> new ModelWire(submodelModifiable, interfacePinNew, w.getPin2(), w.getPath()));
466                 wiresConnectedToPin2.forEach(w -> new ModelWire(submodelModifiable, w.getPin1(), interfacePinNew, w.getPath()));
467         }
468 }