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