001package jmri.web.servlet.panel; 002 003import java.util.ArrayList; 004import java.util.Comparator; 005import java.util.List; 006import javax.servlet.annotation.WebServlet; 007import javax.servlet.http.HttpServlet; 008import javax.swing.JFrame; 009 010import jmri.jmrit.display.IndicatorTrackIcon; 011import jmri.jmrit.display.IndicatorTurnoutIcon; 012import jmri.jmrit.display.Positionable; 013import jmri.jmrit.display.controlPanelEditor.ControlPanelEditor; 014import org.jdom2.Document; 015import org.jdom2.Element; 016import org.jdom2.output.Format; 017import org.jdom2.output.XMLOutputter; 018import org.openide.util.lookup.ServiceProvider; 019import org.slf4j.Logger; 020import org.slf4j.LoggerFactory; 021 022/** 023 * Return xml (for specified ControlPanel) suitable for use by external clients. 024 * <p> 025 * See JMRI Web Server - Panel Servlet Help in help/en/html/web/PanelServlet.shtml for an example description of 026 * the interaction between the Web Servlets, the Web Browser and the JMRI application. 027 * 028 * @author Randall Wood (C) 2016 029 */ 030@WebServlet(name = "ControlPanelServlet", 031 urlPatterns = {"/panel/ControlPanel"}) 032@ServiceProvider(service = HttpServlet.class) 033public class ControlPanelServlet extends AbstractPanelServlet { 034 035 @Override 036 protected String getPanelType() { 037 return "ControlPanel"; 038 } 039 040 @Override 041 protected String getXmlPanel(String name) { 042 log.debug("Getting {} for {}", getPanelType(), name); 043 ControlPanelEditor editor = (ControlPanelEditor) getEditor(name); 044 if (editor == null) { 045 log.warn("Requested ControlPanel [{}] does not exist.", name); 046 return "ERROR Requested panel [" + name + "] does not exist."; 047 } 048 049 Element panel = new Element("panel"); 050 051 JFrame frame = editor.getTargetFrame(); 052 053 panel.setAttribute("name", name); 054 panel.setAttribute("height", Integer.toString(frame.getContentPane().getHeight())); 055 panel.setAttribute("width", Integer.toString(frame.getContentPane().getWidth())); 056 panel.setAttribute("panelheight", Integer.toString(editor.getTargetPanel().getHeight())); 057 panel.setAttribute("panelwidth", Integer.toString(editor.getTargetPanel().getWidth())); 058 059 panel.setAttribute("showtooltips", (editor.showToolTip()) ? "yes" : "no"); 060 panel.setAttribute("controlling", (editor.allControlling()) ? "yes" : "no"); 061 if (editor.getBackgroundColor() != null) { 062 Element color = new Element("backgroundColor"); 063 color.setAttribute("red", Integer.toString(editor.getBackgroundColor().getRed())); 064 color.setAttribute("green", Integer.toString(editor.getBackgroundColor().getGreen())); 065 color.setAttribute("blue", Integer.toString(editor.getBackgroundColor().getBlue())); 066 panel.addContent(color); 067 } 068 069 // include contents, sorted by display level (z-order) so the web client, 070 // which draws elements in document order onto a single canvas, reproduces 071 // the desktop stacking order. Lower-level elements are emitted first and 072 // higher-level elements last. A stable sort preserves the existing relative 073 // order of elements that share a display level. See JMRI/JMRI#12794. 074 List<Positionable> contents = new ArrayList<>(editor.getContents()); 075 contents.sort(Comparator.comparingInt(Positionable::getDisplayLevel)); 076 log.debug("N elements: {}", contents.size()); 077 for (Positionable sub : contents) { 078 if (sub != null) { 079 Element e = new Element("temp"); 080 try { 081 e = positionableElement(sub); 082 } catch (Exception ex) { 083 log.error("Error storing panel element: {}", ex, ex); 084 } 085 // where required, add special stuff to positionable here to use in Web Server 086 switch (e.getName()) { 087 case "indicatorturnouticon" : 088 // if separate occ.sensor was set on icon, names for sensor plus the turnout were 089 // already copied to e as part of 'contents' 090 Element elem = new Element("oblocksysname"); 091 if (((IndicatorTurnoutIcon) sub).getOccBlock() != null) { // optional for CPE, not read on load 092 String itoioblock = ((IndicatorTurnoutIcon) sub).getOccBlock().getSystemName(); 093 elem.addContent(itoioblock); 094 log.debug("CP-SERVLET ITOI = {}", itoioblock); 095 } else { 096 elem.addContent("none"); // NOI18N 097 log.debug("no oblocksensor configured on ITOI {}", sub.getNameString()); 098 } 099 e.addContent(elem); 100 break; 101 case "indicatortrackicon" : 102 // if separate occ.sensor was set on icon, its name was already copied to e as part of 'contents' 103 elem = new Element("oblocksysname"); 104 if (((IndicatorTrackIcon) sub).getOccBlock() != null) { // optional for CPE, not read on load 105 String itioblock = ((IndicatorTrackIcon) sub).getOccBlock().getSystemName(); 106 elem.addContent(itioblock); 107 log.debug("CP-SERVLET ITI = {}", itioblock); 108 } else { 109 elem.addContent("none"); // NOI18N 110 log.debug("no oblocksensor configured on ITI {}", sub.getNameString()); 111 } 112 e.addContent(elem); 113 break; 114 case "" : 115 default : 116 // nothing extra 117 } 118 panel.addContent(e); 119 } 120 } 121 122 Document doc = new Document(panel); 123 XMLOutputter out = new XMLOutputter(); 124 out.setFormat(Format.getPrettyFormat() 125 .setLineSeparator(System.getProperty("line.separator")) 126 .setTextMode(Format.TextMode.TRIM)); 127 128 return out.outputString(doc); 129 } 130 131 @Override 132 protected String getJsonPanel(String name) { 133 // TODO Auto-generated method stub 134 return "ERROR JSON support not implemented"; 135 } 136 137 private static final Logger log = LoggerFactory.getLogger(ControlPanelServlet.class); 138 139}