f34bf342c7730e4f717bd25b8d0fc476b416d7c5
[Mograsim.git] / 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.IOException;
4 import java.nio.file.Files;
5 import java.nio.file.Path;
6 import java.nio.file.Paths;
7 import java.util.Arrays;
8 import java.util.Collection;
9 import java.util.Comparator;
10 import java.util.HashMap;
11 import java.util.HashSet;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.Optional;
15 import java.util.Scanner;
16 import java.util.Set;
17 import java.util.function.Function;
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.submodels.SubmodelComponent;
26 import net.mograsim.logic.model.model.components.submodels.SubmodelInterface;
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.IndirectModelComponentCreator;
35 import net.mograsim.logic.model.serializing.SubmodelComponentSerializer;
36 import net.mograsim.logic.model.snippets.highlevelstatehandlers.DefaultHighLevelStateHandler;
37
38 public class ReserializeAndVerifyJSONs
39 {
40         public static double GRIDSIZE = 2.5;
41         public static boolean changePinUsages = false;
42         public static boolean changeComponentNames = false;
43         public static boolean snapWCPs = true;
44         public static boolean warnNonSnappedPoints = true;
45         public static boolean warnNonVertHorizLines = true;
46         public static boolean warnRedundantWires = true;
47
48         public static void main(String[] args) throws IOException
49         {
50                 Am2900Loader.setup();
51                 try (Scanner sysin = new Scanner(System.in))
52                 {
53                         System.out.print("Directory to search for JSONs in / JSON file to reserialize >");
54                         Path root = Paths.get(sysin.nextLine());
55                         if (!Files.exists(root))
56                                 throw new IllegalArgumentException("Path doesn't exist");
57                         if (Files.isRegularFile(root))
58                                 reserializeJSON(root, sysin);
59                         else
60                         {
61                                 System.out.print("Recursive? >");
62                                 boolean recursive = Boolean.valueOf(sysin.nextLine());
63                                 try (Stream<Path> jsons = recursive ? Files.walk(root) : Files.list(root))
64                                 {
65                                         jsons.filter(Files::isRegularFile).filter(p -> p.getFileName().toString().endsWith(".json"))
66                                                         .forEach(j -> reserializeJSON(j, sysin));
67                                 }
68                         }
69                 }
70         }
71
72         public static void reserializeJSON(Path componentPath, Scanner sysin)
73         {
74                 try
75                 {
76                         DeserializedSubmodelComponent comp = (DeserializedSubmodelComponent) IndirectModelComponentCreator
77                                         .createComponent(new LogicModelModifiable(), "jsonfile:" + componentPath.toString());
78                         System.out.println("Reserializing " + componentPath);
79                         LogicModelModifiable submodelModifiable = comp.getSubmodelModifiable();
80                         Map<String, String> componentNameRemapping = new HashMap<>();
81
82                         if (changePinUsages)
83                                 changePinUsages(sysin, comp);
84                         if (changeComponentNames)
85                                 changeComponentNames(sysin, submodelModifiable, componentNameRemapping);
86                         if (snapWCPs)
87                                 snapWCPs(submodelModifiable);
88                         if (warnNonSnappedPoints)
89                                 warnNonSnappedPoints(comp, submodelModifiable);
90                         if (warnNonVertHorizLines)
91                                 warnNonVertHorizLines(submodelModifiable);
92                         if (warnRedundantWires)
93                                 warnRedundantWires(submodelModifiable);
94
95                         SubmodelComponentSerializer.serialize(comp, componentPath.toString());
96
97                         if (changeComponentNames)
98                                 changeComponentNames_AfterSerialization(sysin, comp, componentNameRemapping);
99                 }
100                 catch (Exception e)
101                 {
102                         System.err.println("An error occurred visiting " + componentPath + ":");
103                         e.printStackTrace();
104                 }
105         }
106
107         private static void warnRedundantWires(LogicModelModifiable submodelModifiable)
108         {
109                 Map<Pin, Set<Pin>> connectedPinGroups = new HashMap<>();
110                 submodelModifiable.getComponentsByName().values().stream().map(ModelComponent::getPins).map(Map::values).flatMap(Collection::stream)
111                                 .forEach(p -> connectedPinGroups.put(p, new HashSet<>(Arrays.asList(p))));
112                 submodelModifiable.getWiresByName().values().forEach(w ->
113                 {
114                         Pin pin1 = w.getPin1();
115                         Pin pin2 = w.getPin2();
116                         Set<Pin> pin1Group = connectedPinGroups.get(pin1);
117                         Set<Pin> pin2Group = connectedPinGroups.get(pin2);
118                         if (pin1Group == pin2Group)
119                                 System.out.println("  Wire " + w.name + " connecting " + pin1 + " and " + pin2 + " is redundant");
120                         else
121                         {
122                                 pin1Group.addAll(pin2Group);
123                                 pin2Group.forEach(p -> connectedPinGroups.put(p, pin1Group));
124                         }
125                 });
126         }
127
128         private static void changePinUsages(Scanner sysin, DeserializedSubmodelComponent comp)
129         {
130                 comp.getSupermodelPins().entrySet().stream().sorted(Comparator.comparing(Entry::getKey)).map(Entry::getValue).forEach(pin ->
131                 {
132                         PinUsage usage = null;
133                         while (usage == null)
134                                 try
135                                 {
136                                         System.out.print("  Usage for interface pin " + pin.name + " (empty: " + pin.usage + ") >");
137                                         String usageStr = sysin.nextLine().toUpperCase();
138                                         usage = usageStr.equals("") ? pin.usage
139                                                         : usageStr.equals("I") ? PinUsage.INPUT
140                                                                         : usageStr.equals("O") ? PinUsage.OUTPUT
141                                                                                         : usageStr.equals("T") ? PinUsage.TRISTATE : PinUsage.valueOf(usageStr);
142                                 }
143                                 catch (@SuppressWarnings("unused") IllegalArgumentException e)
144                                 {
145                                         System.err.println("  Illegal usage");
146                                 }
147                         setInterfacePinUsage(comp, pin, usage);
148                 });
149         }
150
151         private static void changeComponentNames(Scanner sysin, LogicModelModifiable submodelModifiable,
152                         Map<String, String> componentNameRemapping)
153         {
154                 componentNameRemapping.put(SubmodelComponent.SUBMODEL_INTERFACE_NAME, SubmodelComponent.SUBMODEL_INTERFACE_NAME);
155                 LogicModelModifiable tempModel = new LogicModelModifiable();
156                 IdentifyParams iP = new IdentifyParams();
157                 submodelModifiable.getComponentsByName().entrySet().stream()
158                                 .filter(e -> !e.getKey().equals(SubmodelComponent.SUBMODEL_INTERFACE_NAME))
159                                 .sorted(Comparator.comparing(Entry::getKey, ReserializeAndVerifyJSONs::compareStringsWithIntegers)).forEach(e ->
160                                 {
161                                         String oldName = e.getKey();
162                                         ModelComponent subcomp = e.getValue();
163                                         String defaultName = tempModel.getDefaultComponentName(subcomp);
164                                         String newName = null;
165                                         while (newName == null)
166                                         {
167                                                 System.out.print("  New name for component " + oldName + " of type " + subcomp.getIDForSerializing(iP) + " (empty: "
168                                                                 + defaultName + ") >");
169                                                 newName = sysin.nextLine();
170                                                 if (newName.equals(""))
171                                                         newName = defaultName;
172                                                 if (tempModel.getComponentsByName().containsKey(newName))
173                                                 {
174                                                         System.err.println("  There already is a component with that name");
175                                                         newName = null;
176                                                 }
177                                         }
178                                         componentNameRemapping.put(oldName, newName);
179                                         IndirectModelComponentCreator
180                                                         .createComponent(tempModel, subcomp.getIDForSerializing(iP), subcomp.getParamsForSerializingJSON(iP), newName)
181                                                         .moveTo(subcomp.getPosX(), subcomp.getPosY());
182                                 });
183                 SubmodelInterface tempSubmodelInterface = new SubmodelInterface(tempModel);
184                 for (Pin p : submodelModifiable.getComponentsByName().get(SubmodelComponent.SUBMODEL_INTERFACE_NAME).getPins().values())
185                         tempSubmodelInterface
186                                         .addPin(new Pin(tempModel, tempSubmodelInterface, p.name, p.logicWidth, p.usage, p.getRelX(), p.getRelY()));
187                 for (ModelWire w : submodelModifiable.getWiresByName().values())
188                         createWire(componentNameRemapping::get, tempModel, w);
189
190                 Optional<ModelComponent> o;
191                 while ((o = submodelModifiable.getComponentsByName().values().stream()
192                                 .filter(c -> !c.getName().equals(SubmodelComponent.SUBMODEL_INTERFACE_NAME)).findAny()).isPresent())
193                         submodelModifiable.destroyComponent(o.get());
194
195                 tempModel.getComponentsByName().values().stream().filter(c -> !c.getName().equals(SubmodelComponent.SUBMODEL_INTERFACE_NAME))
196                                 .forEach(c -> IndirectModelComponentCreator
197                                                 .createComponent(submodelModifiable, c.getIDForSerializing(iP), c.getParamsForSerializingJSON(iP), c.getName())
198                                                 .moveTo(c.getPosX(), c.getPosY()));
199                 for (ModelWire w : tempModel.getWiresByName().values())
200                         createWire(Function.identity(), submodelModifiable, w);
201         }
202
203         private static void changeComponentNames_AfterSerialization(Scanner sysin, DeserializedSubmodelComponent comp,
204                         Map<String, String> componentNameRemapping)
205         {
206                 if (comp.getHighLevelStateHandler() == null || !(comp.getHighLevelStateHandler() instanceof DefaultHighLevelStateHandler))
207                 {
208                         System.out.println("  A non-default HighLevelStateHandler was detected. Check for changes there manually.");
209                         System.out.print("  Empty line to continue to next component, old component name to get new component name >");
210                         for (String line = sysin.nextLine(); !line.equals(""); line = sysin.nextLine())
211                                 System.out.println("  " + line + "->" + componentNameRemapping.get(line) + " >");
212                 }
213         }
214
215         private static void snapWCPs(LogicModelModifiable submodelModifiable)
216         {
217                 submodelModifiable.getComponentsByName().values().stream().filter(c -> c instanceof ModelWireCrossPoint).forEach(c ->
218                 {
219                         double x = c.getPosX();
220                         double y = c.getPosY();
221                         double newX = x % GRIDSIZE == 0 ? x - 1 : x;
222                         double newY = y % GRIDSIZE == 0 ? y - 1 : y;
223                         if (x != newX || y != newY)
224                         {
225                                 c.moveTo(newX, newY);
226                                 System.out.println("  Snapping WCP " + c.getName());
227                         }
228                 });
229         }
230
231         private static void warnNonSnappedPoints(DeserializedSubmodelComponent comp, LogicModelModifiable submodelModifiable)
232         {
233                 if (comp.getWidth() % GRIDSIZE != 0 || comp.getHeight() % GRIDSIZE != 0)
234                         System.out.println("  Size is not snapped to grid: " + comp.getWidth() + "," + comp.getHeight());
235                 submodelModifiable.getComponentsByName().values().forEach(c ->
236                 {
237                         double x = c.getPosX();
238                         double y = c.getPosY();
239                         if (c instanceof ModelWireCrossPoint)
240                         {
241                                 x++;
242                                 y++;
243                         }
244                         if (x % GRIDSIZE != 0 || y % GRIDSIZE != 0)
245                                 System.out.println("  Component " + c.getName() + " (type " + c.getIDForSerializing(new IdentifyParams())
246                                                 + ") is not snapped to grid: " + x + "," + y);
247                 });
248                 submodelModifiable.getWiresByName().values().forEach(w ->
249                 {
250                         Point[] p = w.getPath();
251                         if (p != null)
252                                 for (int i = 0; i < p.length; i++)
253                                         if (p[i].x % GRIDSIZE != 0 || p[i].y % GRIDSIZE != 0)
254                                                 System.out.println("  Wire " + w.name + " path point #" + i + " is not snapped to grid: " + p[i].x + "," + p[i].y);
255                 });
256                 comp.getPins().values().forEach(p ->
257                 {
258                         if (p.getRelX() % GRIDSIZE != 0 || p.getRelY() % GRIDSIZE != 0)
259                                 System.out.println("  Interface point " + p.name + " is not snapped to grid: " + p.getRelX() + "," + p.getRelY());
260                 });
261         }
262
263         private static void warnNonVertHorizLines(LogicModelModifiable submodelModifiable)
264         {
265                 submodelModifiable.getWiresByName().values().forEach(w ->
266                 {
267                         double[] p = w.getEffectivePath();
268                         for (int i = 1; i < p.length / 2; i++)
269                         {
270                                 double x1 = p[2 * i - 2];
271                                 double y1 = p[2 * i - 1];
272                                 double x2 = p[2 * i + 0];
273                                 double y2 = p[2 * i + 1];
274                                 if (x1 != x2 && y1 != y2)
275                                         System.out.println("  Wire " + w.name + " part #" + (i - 1) + " is neither vertical nor horizontal");
276                         }
277                 });
278         }
279
280         private static ModelWire createWire(Function<String, String> componentNameRemapping, LogicModelModifiable tempModelForDefaultNames,
281                         ModelWire w)
282         {
283                 return new ModelWire(tempModelForDefaultNames, w.name,
284                                 getRemappedPin(componentNameRemapping, tempModelForDefaultNames, w.getPin1()),
285                                 getRemappedPin(componentNameRemapping, tempModelForDefaultNames, w.getPin2()), w.getPath());
286         }
287
288         private static Pin getRemappedPin(Function<String, String> componentNameRemapping, LogicModelModifiable tempModelForDefaultNames,
289                         Pin pin)
290         {
291                 return tempModelForDefaultNames.getComponentsByName().get(componentNameRemapping.apply(pin.component.getName())).getPin(pin.name);
292         }
293
294         private static int compareStringsWithIntegers(String a, String b)
295         {
296                 int aLoc = 0;
297                 int bLoc = 0;
298                 for (;;)
299                 {
300                         if (aLoc == a.length())
301                         {
302                                 if (bLoc == b.length())
303                                         return 0;
304                                 return -1;
305                         }
306                         if (bLoc == b.length())
307                                 return 1;
308                         int aInt = 0;
309                         int aIntLen = 0;
310                         char nextCharA;
311                         for (;;)
312                         {
313                                 nextCharA = a.charAt(aLoc++);
314                                 if (nextCharA < '0' || nextCharA > '9')
315                                         break;
316                                 aIntLen++;
317                                 aInt = aInt * 10 + nextCharA - '0';
318                                 if (aLoc == a.length())
319                                         break;
320                         }
321                         int bInt = 0;
322                         int bIntLen = 0;
323                         char nextCharB;
324                         for (;;)
325                         {
326                                 nextCharB = b.charAt(bLoc++);
327                                 if (nextCharB < '0' || nextCharB > '9')
328                                         break;
329                                 bIntLen++;
330                                 bInt = bInt * 10 + nextCharB - '0';
331                                 if (bLoc == b.length())
332                                         break;
333                         }
334                         if (aIntLen != 0)
335                         {
336                                 if (bIntLen == 0)
337                                         return -1;
338                                 int comp = Integer.compare(aInt, bInt);
339                                 if (comp != 0)
340                                         return comp;
341                         } else
342                         {
343                                 if (bIntLen != 0)
344                                         return 1;
345                                 int comp = Character.compare(nextCharA, nextCharB);
346                                 if (comp != 0)
347                                         return comp;
348                         }
349                 }
350         }
351
352         private static void setInterfacePinUsage(DeserializedSubmodelComponent comp, Pin interfacePin, PinUsage usage)
353         {
354                 Set<ModelWire> wiresConnectedToPin = comp.submodel.getWiresByName().values().stream()
355                                 .filter(w -> w.getPin1() == interfacePin || w.getPin2() == interfacePin).collect(Collectors.toSet());
356                 LogicModelModifiable submodelModifiable = comp.getSubmodelModifiable();
357                 wiresConnectedToPin.forEach(submodelModifiable::destroyWire);
358                 comp.removeSubmodelInterface(interfacePin.name);
359                 comp.addSubmodelInterface(new MovablePin(submodelModifiable, comp, interfacePin.name, interfacePin.logicWidth, usage,
360                                 interfacePin.getRelX(), interfacePin.getRelY()));
361                 wiresConnectedToPin.forEach(w -> new ModelWire(submodelModifiable, w.getPin1(), w.getPin2()));
362         }
363 }