Skip to content

Commit c959153

Browse files
committed
mra/plots/ConfigIniPlot: Visualization to see vehicle configuration.
1 parent 426434c commit c959153

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/*
2+
* Copyright (c) 2004-2025 Universidade do Porto - Faculdade de Engenharia
3+
* Laboratório de Sistemas e Tecnologia Subaquática (LSTS)
4+
* All rights reserved.
5+
* Rua Dr. Roberto Frias s/n, sala I203, 4200-465 Porto, Portugal
6+
*
7+
* This file is part of Neptus, Command and Control Framework.
8+
*
9+
* Commercial Licence Usage
10+
* Licencees holding valid commercial Neptus licences may use this file
11+
* in accordance with the commercial licence agreement provided with the
12+
* Software or, alternatively, in accordance with the terms contained in a
13+
* written agreement between you and Universidade do Porto. For licensing
14+
* terms, conditions, and further information contact lsts@fe.up.pt.
15+
*
16+
* Modified European Union Public Licence - EUPL v.1.1 Usage
17+
* Alternatively, this file may be used under the terms of the Modified EUPL,
18+
* Version 1.1 only (the "Licence"), appearing in the file LICENSE.md
19+
* included in the packaging of this file. You may not use this work
20+
* except in compliance with the Licence. Unless required by applicable
21+
* law or agreed to in writing, software distributed under the Licence is
22+
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
23+
* ANY KIND, either express or implied. See the Licence for the specific
24+
* language governing permissions and limitations at
25+
* https://github.com/LSTS/neptus/blob/develop/LICENSE.md
26+
* and http://ec.europa.eu/idabc/eupl.html.
27+
*
28+
* For more information please see <http://lsts.fe.up.pt/neptus>.
29+
*
30+
* Author: jcordeiro
31+
* November 20, 2025
32+
*/
33+
package pt.lsts.neptus.mra.plots;
34+
35+
import com.l2fprod.common.propertysheet.DefaultProperty;
36+
import com.l2fprod.common.propertysheet.Property;
37+
import com.l2fprod.common.propertysheet.PropertySheet;
38+
import com.l2fprod.common.propertysheet.PropertySheetPanel;
39+
import com.l2fprod.common.propertysheet.PropertySheetTableModel.Item;
40+
import net.miginfocom.swing.MigLayout;
41+
import pt.lsts.neptus.mra.MRAPanel;
42+
import pt.lsts.neptus.mra.importers.IMraLogGroup;
43+
import pt.lsts.neptus.mra.visualizations.SimpleMRAVisualization;
44+
import pt.lsts.neptus.plugins.PluginDescription;
45+
46+
import javax.swing.JPanel;
47+
import javax.swing.JScrollPane;
48+
import javax.swing.JLabel;
49+
import javax.swing.JComponent;
50+
import javax.swing.JButton;
51+
52+
import java.io.File;
53+
import java.io.IOException;
54+
import java.nio.charset.StandardCharsets;
55+
import java.nio.file.Files;
56+
import java.util.ArrayList;
57+
import java.util.LinkedHashMap;
58+
import java.util.Map;
59+
import java.util.List;
60+
import java.util.stream.Collectors;
61+
import java.util.stream.Stream;
62+
63+
/**
64+
* @author jcordeiro
65+
*
66+
*/
67+
@PluginDescription(author = "jcordeiro", name = "Vehicle Configuration")
68+
public class ConfigIniPlot extends SimpleMRAVisualization {
69+
70+
private File iniFile;
71+
private final Map<String, List<Property>> sectionMap = new LinkedHashMap<>();
72+
PropertySheetPanel sheet = new PropertySheetPanel();
73+
74+
75+
public ConfigIniPlot(MRAPanel panel) {
76+
super(panel);
77+
}
78+
79+
@Override
80+
public boolean canBeApplied(IMraLogGroup source) {
81+
File logDir = source.getFile("");
82+
if (logDir == null || !logDir.isDirectory()) {
83+
return false;
84+
}
85+
86+
// check for a .ini file in the directory
87+
File[] iniFiles = logDir.listFiles((dir, name) ->
88+
name.toLowerCase().endsWith(".ini")
89+
);
90+
91+
if (iniFiles != null) {
92+
iniFile = iniFiles[0];
93+
return true;
94+
}
95+
return false;
96+
}
97+
98+
@Override
99+
public Type getType() {
100+
return Type.VISUALIZATION;
101+
}
102+
103+
@Override
104+
public JComponent getVisualization(IMraLogGroup source, double timestep) {
105+
JPanel mainPanel = new JPanel(new MigLayout("fill, insets 0"));
106+
107+
JLabel titleLabel = new JLabel("<html><h2>Vehicle Configuration</h2></html>");
108+
mainPanel.add(titleLabel, "w 100%, wrap");
109+
110+
// expand/collapse buttons
111+
JButton collapseButton = new JButton("Collapse All");
112+
collapseButton.addActionListener(e -> collapseAllCategories());
113+
mainPanel.add(collapseButton, "split 2, gapright 5");
114+
115+
JButton expandButton = new JButton("Expand All");
116+
expandButton.addActionListener(e -> expandAllCategories());
117+
mainPanel.add(expandButton, "wrap");
118+
119+
sheet.removeAll();
120+
sheet.setMode(PropertySheet.VIEW_AS_CATEGORIES);
121+
sheet.setRestoreToggleStates(false);
122+
sheet.setSortingCategories(false);
123+
sheet.setSortingProperties(false);
124+
sheet.setDescriptionVisible(false);
125+
sheet.setToolBarVisible(false);
126+
127+
if (iniFile != null && iniFile.exists()) {
128+
try {
129+
Map<String, List<Property>> sections = parseIniFile(iniFile);
130+
131+
for (Map.Entry<String, List<Property>> entry : sections.entrySet()) {
132+
133+
134+
for (Property p : entry.getValue()) {
135+
sheet.addProperty(p);
136+
}
137+
}
138+
139+
} catch (IOException e) {
140+
mainPanel.add(new JLabel("Error reading INI file: " + e.getMessage()));
141+
}
142+
}
143+
144+
JScrollPane scroll = new JScrollPane(sheet);
145+
scroll.setBorder(null);
146+
mainPanel.add(scroll, "grow, push");
147+
148+
return mainPanel;
149+
}
150+
151+
private Map<String, List<Property>> parseIniFile(File iniFile) throws IOException {
152+
153+
String currentSectionName = null;
154+
List<Property> currentSection = null;
155+
156+
try (Stream<String> lines = Files.lines(iniFile.toPath(), StandardCharsets.UTF_8)) {
157+
158+
for (String rawLine : lines.collect(Collectors.toList())) {
159+
String line = rawLine.trim();
160+
161+
if (line.isEmpty() || line.startsWith(";") || line.startsWith("#"))
162+
continue;
163+
164+
// new section
165+
if (line.startsWith("[") && line.endsWith("]")) {
166+
currentSectionName = line.substring(1, line.length() - 1).trim();
167+
currentSection = new ArrayList<>();
168+
sectionMap.put(currentSectionName, currentSection);
169+
continue;
170+
}
171+
172+
// key=value entry
173+
int idx = line.indexOf('=');
174+
if (idx > 0) {
175+
String key = line.substring(0, idx).trim();
176+
String value = line.substring(idx + 1).trim();
177+
178+
// removes possible comments
179+
int commentIdx = value.indexOf(';');
180+
if (commentIdx >= 0)
181+
value = value.substring(0, commentIdx).trim();
182+
183+
currentSection.add(createProperty(key, value, currentSectionName));
184+
}
185+
}
186+
}
187+
188+
// sort map alphabetically, first "General" then rest
189+
Map<String, List<Property>> sorted = new LinkedHashMap<>();
190+
191+
if (sectionMap.containsKey("General"))
192+
sorted.put("General", sectionMap.get("General"));
193+
194+
sectionMap.entrySet().stream()
195+
.filter(e -> !e.getKey().equals("General"))
196+
.sorted(Map.Entry.comparingByKey(String.CASE_INSENSITIVE_ORDER))
197+
.forEachOrdered(e -> sorted.put(e.getKey(), e.getValue()));
198+
199+
return sorted;
200+
}
201+
202+
private Property createProperty(String key, String value, String category) {
203+
DefaultProperty prop = new DefaultProperty();
204+
prop.setName(key);
205+
prop.setDisplayName(key);
206+
prop.setShortDescription(""); // TODO get description from XML
207+
prop.setType(String.class);
208+
prop.setEditable(false);
209+
prop.setValue(value);
210+
211+
prop.setCategory(category);
212+
213+
return prop;
214+
}
215+
216+
private void collapseAllCategories() {
217+
for (int i = 0; i < sheet.getTable().getSheetModel().getRowCount(); i++) {
218+
Item o = (Item) sheet.getTable().getSheetModel().getObject(i);
219+
if (o.isVisible() && !o.hasToggle()) {
220+
o.getParent().toggle();
221+
}
222+
}
223+
}
224+
225+
private void expandAllCategories() {
226+
for (int i = 0; i < sheet.getTable().getSheetModel().getRowCount(); i++) {
227+
Item o = (Item) sheet.getTable().getSheetModel().getObject(i);
228+
if (!o.isVisible()) {
229+
if (o.hasToggle() && !o.isVisible()) {
230+
o.toggle();
231+
} else if (!o.hasToggle() && o.getParent() != null && !o.getParent().isVisible()) {
232+
o.getParent().toggle();
233+
}
234+
}
235+
}
236+
}
237+
}
238+

0 commit comments

Comments
 (0)