001package jmri.jmrix.mqtt;
002
003import java.awt.*;
004import java.text.DecimalFormat;
005import java.util.*;
006import java.util.List;
007
008import javax.swing.*;
009import javax.swing.border.TitledBorder;
010import javax.annotation.Nonnull;
011
012import jmri.*;
013import jmri.implementation.*;
014import jmri.jmrit.beantable.signalmast.SignalMastAddPane;
015
016import org.openide.util.lookup.ServiceProvider;
017
018/**
019 * A pane for configuring VirtualSignalMast objects.
020 *
021 * @see jmri.jmrit.beantable.signalmast.SignalMastAddPane
022 * @author Bob Jacobsen Copyright (C) 2018, 2021
023 */
024public class MqttSignalMastAddPane extends SignalMastAddPane {
025
026    public MqttSignalMastAddPane() {
027        initPanel();
028    }
029
030    final void initPanel() {
031
032        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
033        // lit/unlit controls
034        JPanel p = new JPanel();
035        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
036        p.add(new JLabel(Bundle.getMessage("AllowUnLitLabel") + ": "));
037        p.add(allowUnLit);
038        p.setAlignmentX(Component.LEFT_ALIGNMENT);
039        add(p);
040
041        // Address part
042        add(addressDataPane());
043
044        // disabled aspects controls
045        TitledBorder disableborder = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black));
046        disableborder.setTitle(Bundle.getMessage("DisableAspectsLabel"));
047        JScrollPane disabledAspectsScroll = new JScrollPane(disabledAspectsPanel);
048        disabledAspectsScroll.setBorder(disableborder);
049        add(disabledAspectsScroll);
050    }
051
052    JPanel addressDataPane() {
053        JPanel p = new JPanel();
054
055        TitledBorder border = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black));
056        //border.setTitle("Mast Number");
057        p.setBorder(border);
058
059         p.setLayout(new jmri.util.javaworld.GridLayout2(1, 2));
060
061        p.add(aspectAddressLabel);
062
063        aspectAddressField.setText(paddedNumber.format(MqttSignalMast.getLastRef() + 1));
064        aspectAddressField.setEnabled(true);
065        p.add(aspectAddressField);
066
067        return p;
068    }
069    JLabel aspectAddressLabel = new JLabel(Bundle.getMessage("TopicSuffix"));
070    JTextField aspectAddressField = new JTextField(5);
071
072    /** {@inheritDoc} */
073    @Override
074    @Nonnull public String getPaneName() {
075        return Bundle.getMessage("MqttMast");
076    }
077
078    JCheckBox allowUnLit = new JCheckBox();
079
080    LinkedHashMap<String, JCheckBox> disabledAspects = new LinkedHashMap<>(NOTIONAL_ASPECT_COUNT);
081    JPanel disabledAspectsPanel = new JPanel();
082
083    MqttSignalMast currentMast = null;
084
085    /** {@inheritDoc} */
086    @Override
087    public void setAspectNames(@Nonnull SignalAppearanceMap map, @Nonnull SignalSystem sigSystem) {
088        Enumeration<String> aspects = map.getAspects();
089        // update immediately
090        disabledAspects = new LinkedHashMap<>(NOTIONAL_ASPECT_COUNT);
091        disabledAspectsPanel.removeAll();
092        while (aspects.hasMoreElements()) {
093            String aspect = aspects.nextElement();
094            JCheckBox disabled = new JCheckBox(aspect);
095            disabledAspects.put(aspect, disabled);
096        }
097
098        for (Map.Entry<String, JCheckBox> entry : disabledAspects.entrySet()) {
099            disabledAspectsPanel.add(entry.getValue());
100        }
101
102        disabledAspectsPanel.setLayout(new jmri.util.javaworld.GridLayout2(0, 1)); // 0 means enough
103
104        disabledAspectsPanel.revalidate();
105    }
106
107    /** {@inheritDoc} */
108    @Override
109    public boolean canHandleMast(@Nonnull SignalMast mast) {
110        return mast instanceof MqttSignalMast;
111    }
112
113    /** {@inheritDoc} */
114    @Override
115    public void setMast(SignalMast mast) {
116        log.trace("setMast {} start", mast);
117        if (mast == null) {
118            currentMast = null;
119            return;
120        }
121
122        if (! (mast instanceof MqttSignalMast) ) {
123            log.error("mast was wrong type: {} {}", mast.getSystemName(), mast.getClass().getName());
124            return;
125        }
126
127        aspectAddressField.setEnabled(false);
128        String[] pieces = mast.getSystemName().split("\\(");
129        if (pieces.length == 2) {
130            String number = pieces[1].substring(1, pieces[1].length()-1); // starts with ($)
131            aspectAddressField.setText(number);
132        } else {
133            log.warn("not just one '(' in {}",mast.getSystemName());
134        }
135
136        currentMast = (MqttSignalMast) mast;
137        List<String> disabled = currentMast.getDisabledAspects();
138        if (disabled != null) {
139            for (String aspect : disabled) {
140                if (disabledAspects.containsKey(aspect)) {
141                    disabledAspects.get(aspect).setSelected(true);
142                }
143            }
144        }
145
146        allowUnLit.setSelected(currentMast.allowUnLit());
147
148        log.trace("setMast {} end", mast);
149    }
150
151    DecimalFormat paddedNumber = new DecimalFormat("0000");
152
153    /** {@inheritDoc} */
154    @Override
155    public boolean createMast(@Nonnull
156            String sigsysname, @Nonnull
157                    String mastname, @Nonnull
158                            String username) {
159        log.trace("createMast {} {} {} start with currentMast: {}", sigsysname, mastname, username, currentMast);
160        if (currentMast == null) {
161            // create a mast
162            String name = "IF$mqm:"
163                    + sigsysname
164                    + ":" + mastname.substring(11, mastname.length() - 4);
165            name += "($" + aspectAddressField.getText()+ ")";
166            currentMast = new MqttSignalMast(name);
167            if (!username.isEmpty()) {
168                currentMast.setUserName(username);
169            }
170            currentMast.setMastType(mastname.substring(11, mastname.length() - 4));
171            try {
172                InstanceManager.getDefault(SignalMastManager.class).register(currentMast);
173            } catch (jmri.NamedBean.DuplicateSystemNameException e) {
174                // clear the signal mast and rethrow
175                currentMast = null;
176                throw e;
177            }
178
179        }
180
181        // load a new or existing mast
182        for (Map.Entry<String, JCheckBox> entry : disabledAspects.entrySet()) {
183            if (entry.getValue().isSelected()) {
184                currentMast.setAspectDisabled(entry.getKey());
185            } else {
186                currentMast.setAspectEnabled(entry.getKey());
187            }
188        }
189        currentMast.setAllowUnLit(allowUnLit.isSelected());
190
191        // update to next option
192        aspectAddressField.setText((paddedNumber.format(MqttSignalMast.getLastRef() + 1)));
193
194        log.trace("createMast {} {} {} end", sigsysname, mastname, username);
195        return true;
196    }
197
198    @ServiceProvider(service = SignalMastAddPane.SignalMastAddPaneProvider.class)
199    static public class SignalMastAddPaneProvider extends SignalMastAddPane.SignalMastAddPaneProvider {
200        /**
201         * {@inheritDoc}
202         * Requires a valid MQTT connection
203         */
204        @Override
205        public boolean isAvailable() {
206            for (SystemConnectionMemo memo : InstanceManager.getList(SystemConnectionMemo.class)) {
207                if (memo instanceof jmri.jmrix.mqtt.MqttSystemConnectionMemo) {
208                    return true;
209                }
210            }
211            return false;
212        }
213
214        /** {@inheritDoc} */
215        @Override
216        @Nonnull public String getPaneName() {
217            return Bundle.getMessage("MqttMast");
218        }
219        /** {@inheritDoc} */
220        @Override
221        @Nonnull public SignalMastAddPane getNewPane() {
222            return new MqttSignalMastAddPane();
223        }
224    }
225
226    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MqttSignalMastAddPane.class);
227
228}