001package apps.util.issuereporter;
002
003import java.awt.*;
004import java.io.File;
005import java.net.NetworkInterface;
006import java.net.SocketException;
007import java.util.*;
008import java.util.List;
009
010import javax.annotation.Nonnull;
011import javax.swing.filechooser.FileSystemView;
012
013import jmri.*;
014import jmri.configurexml.LoadXmlUserAction;
015import jmri.jmrit.audio.AudioFactory;
016import jmri.jmrit.operations.OperationsXml;
017import jmri.jmrit.roster.Roster;
018import jmri.util.gui.GuiLafPreferencesManager;
019import jmri.jmrix.ConnectionConfigManager;
020import jmri.profile.Profile;
021import jmri.profile.ProfileManager;
022import jmri.util.FileUtil;
023import jmri.util.PortNameMapper;
024import jmri.util.PortNameMapper.SerialPortFriendlyName;
025import jmri.util.node.NodeIdentity;
026import jmri.util.zeroconf.ZeroConfServiceManager;
027
028/**
029 * Provide the JMRI context info.
030 *
031 * @author Bob Jacobsen Copyright (C) 2007, 2009
032 * @author Matt Harris Copyright (C) 2008, 2009
033 */
034public class SystemInfo {
035
036    private final boolean inBody;
037    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SystemInfo.class);
038
039    SystemInfo(boolean inBody) {
040        this.inBody = inBody;
041    }
042
043    @Override
044    @Nonnull
045    public String toString() {
046        StringBuilder builder = new StringBuilder();
047        if (inBody) {
048            builder.append("<details>\n<summary>System Info</summary>\n\n");
049        }
050        asList().forEach(l -> builder.append(l).append("\n"));
051        if (inBody) {
052            builder.append("</details>\n");
053        }
054        return builder.toString();
055    }
056
057    public List<String> asList() {
058        List<String> list = new ArrayList<>();
059        list.add("|Item|Value|");
060        list.add("|---|---|");
061
062        addLine(list, "JMRI Version", Version.name());
063        addLine(list, "Application", Application.getApplicationName());
064
065        Profile profile = ProfileManager.getDefault().getActiveProfile();
066        if (profile != null) {
067            addLine(list, "Active profile", profile.getName());
068            addLine(list, "Profile location", profile.getPath().getPath());
069            addLine(list, "Profile ID", profile.getId());
070        } else {
071            addLine(list, "Active profile", "");
072        }
073
074        addLine(list, "JMRI Network ID", NodeIdentity.networkIdentity());
075        addLine(list, "JMRI Storage ID", NodeIdentity.storageIdentity(profile));
076
077        addLine(list, "Preferences directory", FileUtil.getUserFilesPath());
078        addLine(list, "Program directory", FileUtil.getProgramPath());
079        addLine(list, "Roster index", Roster.getDefault().getRosterIndexPath());
080
081        File panel = LoadXmlUserAction.getCurrentFile();
082        addLine(list, "Panel file", panel != null ? panel.getPath() : "");
083
084        addLine(list, "Locale", InstanceManager.getDefault(GuiLafPreferencesManager.class).getLocale().toString());
085
086        addLine(list, "Operations location", OperationsXml.getFileLocation());
087
088        InstanceManager.getOptionalDefault(AudioManager.class).ifPresent(am -> {
089            AudioFactory af = am.getActiveAudioFactory();
090            addLine(list, "Audio factory", af != null ? af.toString() : "");
091        });
092
093        InstanceManager.getOptionalDefault(ConnectionConfigManager.class)
094                .ifPresent(ccm -> Arrays.stream(ccm.getConnections())
095                .forEach(c -> addLine(list,
096                "Connection " + c.getConnectionName(),
097                c.getManufacturer() + " connected via " + c.name() + " on " + c.getInfo() + (c.getDisabled() ? " (disabled)" : " (enabled)"))));
098
099        addComPortInfo(list);
100
101        System.getProperties().stringPropertyNames().stream().sorted()
102                .filter(n -> !n.equals("line.separator"))
103                .forEach(n -> addLine(list, n, System.getProperty(n)));
104
105        if (!GraphicsEnvironment.isHeadless()) {
106            addLine(list, "FileSystemView#getDefaultDirectory()", FileSystemView.getFileSystemView().getDefaultDirectory().getPath());
107            addLine(list, "FileSystemView#getHomeDirectory()", FileSystemView.getFileSystemView().getHomeDirectory().getPath());
108            addLine(list, "Default JFileChooser()", new jmri.util.swing.JmriJFileChooser().getCurrentDirectory().getPath());
109        }
110        addDisplayDimensions(list);
111
112        addNetworkInfo(list);
113
114        return list;
115    }
116
117    private void addDisplayDimensions(List<String> list) {
118        if (!GraphicsEnvironment.isHeadless()) {
119            Arrays.stream(GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices())
120                    .forEachOrdered(device -> {
121                        Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(device.getDefaultConfiguration());
122                        addLine(list,
123                                "Display " + device.getIDstring(),
124                                " size: width " + device.getDisplayMode().getWidth() + " height " + device.getDisplayMode().getHeight()
125                                + " insets: top " + insets.top + " right " + insets.right + " bottom " + insets.bottom + " left " + insets.left);
126                    });
127        } else {
128            addLine(list, "Display", "headless");
129        }
130    }
131
132    private void addNetworkInfo(List<String> list) {
133        try {
134            Collections.list(NetworkInterface.getNetworkInterfaces()).stream().forEach(ni
135                    -> ni.getInterfaceAddresses().forEach(ia
136                            -> addLine(list,
137                            "Network Interface " + ni.getDisplayName(),
138                            ia.getAddress().getHostAddress())));
139        } catch (SocketException ex) {
140            log.error("Unable to enumerate network interfaces", ex);
141        }
142        InstanceManager.getDefault(ZeroConfServiceManager.class).allServices().forEach(zcs
143                -> addLine(list,
144                        "ZeroConf service " + zcs.getKey(),
145                        zcs.getServiceInfo().getNiceTextString()));
146    }
147
148    private void addComPortInfo(List<String> list) {
149        var portNames = jmri.jmrix.AbstractSerialPortController.getActualPortNames();
150
151        // now output the details
152        for (String name : portNames) {
153            // output details
154            SerialPortFriendlyName port = PortNameMapper.getPortNameMap().get(name);
155            if (port == null) {
156                port = new SerialPortFriendlyName(name, null);
157                PortNameMapper.getPortNameMap().put(name, port);
158            }
159            addLine(list, " Port: " + name, "");
160        }
161    }
162
163    private void addLine(List<String> list, String item, String value) {
164        list.add(String.format("|%s|%s|", escapePipes(item), escapePipes(value)));
165    }
166
167    private String escapePipes(String input) {
168        return input.replace("|", "\\|");
169    }
170}