001package jmri.jmrix.ecos;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.util.ArrayList;
007import java.util.Enumeration;
008import java.util.Hashtable;
009import java.util.List;
010import java.util.ResourceBundle;
011
012import javax.annotation.Nonnull;
013import javax.swing.BorderFactory;
014import javax.swing.BoxLayout;
015import javax.swing.JButton;
016import javax.swing.JCheckBox;
017import javax.swing.JDialog;
018import javax.swing.JLabel;
019import javax.swing.JPanel;
020
021import jmri.*;
022import jmri.jmrix.ecos.utilities.GetEcosObjectNumber;
023import jmri.jmrix.ecos.utilities.RemoveObjectFromEcos;
024import jmri.util.swing.JmriJOptionPane;
025
026/**
027 * Implement turnout manager for Ecos systems.
028 * <p>
029 * System names are "UTnnn", where U is the user configurable system prefix,
030 * nnn is the turnout number without padding.
031 *
032 * @author Bob Jacobsen Copyright (C) 2001, 2008
033 */
034public class EcosTurnoutManager extends jmri.managers.AbstractTurnoutManager
035        implements EcosListener {
036
037    public EcosTurnoutManager(EcosSystemConnectionMemo memo) {
038        super(memo);
039        initEtm();
040    }
041    
042    private void initEtm() {
043        tc = getMemo().getTrafficController();
044
045        // listen for turnout creation
046        // connect to the TrafficManager
047        tc.addEcosListener(this);
048
049        // ask to be notified about newly created turnouts on the layout.
050        EcosMessage m = new EcosMessage("request(11, view)");
051        tc.sendEcosMessage(m, this);
052
053        // get initial state
054        m = new EcosMessage("queryObjects(11, addrext)");
055        tc.sendEcosMessage(m, this);
056        this.addPropertyChangeListener(this);
057    }
058
059    EcosTrafficController tc;
060
061    // The hash table simply holds the object number against the EcosTurnout ref.
062    private final Hashtable<Integer, EcosTurnout> _tecos = new Hashtable<>(); // stores known Ecos Object ids to DCC
063
064    /**
065     * {@inheritDoc}
066     */
067    @Override
068    @Nonnull
069    public EcosSystemConnectionMemo getMemo() {
070        return (EcosSystemConnectionMemo) memo;
071    }
072
073    /**
074     * {@inheritDoc}
075     */
076    @Nonnull
077    @Override
078    protected Turnout createNewTurnout(@Nonnull String systemName, String userName) throws IllegalArgumentException {
079        int addr;
080        try {
081            addr = Integer.parseInt(systemName.substring(getSystemNamePrefix().length()));
082        } catch (NumberFormatException e) {
083            throw new IllegalArgumentException("failed to convert systemName '"+systemName+"' to an Ecos turnout address");
084        }
085        Turnout t = new EcosTurnout(addr, getSystemPrefix(), tc, this);
086        t.setUserName(userName);
087        t.setFeedbackMode("MONITORING");
088        return t;
089    }
090
091    @Override
092    public boolean allowMultipleAdditions(@Nonnull String systemName) {
093        return true;
094    }
095
096    // to listen for status changes from Ecos system
097    @Override
098    public void reply(EcosReply m) {
099        log.debug("reply {}", m);
100        // is this a list of turnouts?
101        EcosTurnout et;
102
103        if (m.getResultCode() == 0) {
104            int ecosObjectId = m.getEcosObjectId();
105            if ((ecosObjectId != 11) && ((ecosObjectId < 20000) || (ecosObjectId > 30000))) {
106                log.debug("message received that is not within the valid turnout object range");
107                return;
108            }
109            List<String> headerDetails = m.getReplyHeaderDetails();
110            String[] msgContents = m.getContents();
111            //log.info("Initial Header " + headerDetails);
112            if (m.isUnsolicited()) {
113                if (ecosObjectId == 11) {
114                    //Creation or removal of a turnout from the Ecos.
115                    if (msgContents[0].contains("msg[LIST_CHANGED]")) {
116                        log.debug("We have received notification of a change in the Turnout list");
117                        EcosMessage mout = new EcosMessage("queryObjects(11)");
118                        tc.sendEcosMessage(mout, this);
119                    }
120                    //Creation or removal of a turnout from the Ecos.
121                } else {
122                    log.debug("Forwarding on State change for {}", ecosObjectId);
123                    et = _tecos.get(ecosObjectId);
124                    if (et != null) {
125                        et.reply(m);
126                        //As the event will come from one object, we shall check to see if it is an extended address,
127                        // if it is we also forward the message onto the slaved address.
128                        if (et.getExtended() != 0) {
129                            log.debug("This is also an extended turnout so forwarding on change to {}", et.getSlaveAddress());
130                            EcosTurnout etx = (EcosTurnout) provideTurnout(et.getSlaveAddress());
131                            etx.reply(m);
132                        }
133                    }
134                }
135
136            } else {
137                String replyType = m.getReplyType();
138                if (replyType.equals("queryObjects")) {
139                    if (ecosObjectId == 11 && headerDetails.size() == 0) {
140                        //if (lines[0].startsWith("<REPLY queryObjects(11)>")) {
141                        log.debug("No sub details");
142                        checkTurnoutList(msgContents);
143                    } else if (headerDetails.contains("addr")) {
144                        // yes, make sure TOs exist
145                        //log.debug("found "+(lines.length-2)+" turnout objects");
146                        for (String item : m.getContents()) {
147                            log.debug("header {}", item);
148                            //for (int i = 1; i<lines.length-1; i++) {
149                            if (item.contains("addr")) { // skip odd lines
150                                int object = GetEcosObjectNumber.getEcosObjectNumber(item, null, " ");
151                                if ((20000 <= object) && (object < 30000)) { // only physical turnouts
152                                    int addr = GetEcosObjectNumber.getEcosObjectNumber(item, "[", "]");
153                                    log.debug("Found turnout object {} addr {}", object, addr);
154
155                                    if (addr > 0) {
156                                        Turnout t = getTurnout(getSystemNamePrefix() + addr);
157                                        if (t == null) {
158                                            et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + addr);
159                                            et.setObjectNumber(object);
160                                            _tecos.put(object, et);
161                                        }
162                                    }
163                                } else if ((30000 <= object) && (object < 40000)) {  //This is a ecos route
164                                    log.debug("Found route object {}", object);
165
166                                    Turnout t = getTurnout(getSystemNamePrefix() + object);
167                                    if (t == null) {
168                                        et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + object);
169                                        et.setObjectNumber(object);
170                                        _tecos.put(object, et);
171                                    }
172                                }
173                                if ((20000 <= object) && (object < 40000)) {
174                                    EcosMessage em = new EcosMessage("request(" + object + ",view)");
175                                    tc.sendEcosMessage(em, this);
176                                    em = new EcosMessage("get(" + object + ",state)");
177                                    tc.sendEcosMessage(em, this);
178                                }
179                            }
180                        }
181                    } else if (headerDetails.contains("addrext")) {
182                        //log.info("Extended");
183                        for (String item : m.getContents()) {
184                            //log.info(item);
185                            if (item.contains("addrext")) { // skip odd lines
186                                turnoutAddressDetails(item);
187                            }
188                        }
189                    }
190                } else if (replyType.equals("get")) {
191                    et = (EcosTurnout) getByEcosObject(ecosObjectId);
192                    if (headerDetails.contains("state")) {
193                        //As this is in response to a change in state we shall forward
194                        //it straight on to the ecos turnout to deal with.
195                        et.reply(m);
196                        //As the event will come from one object, we shall check to see if it is an extended address,
197                        // if it is we also forward the message onto the slaved address.
198                        if (et.getExtended() != 0) {
199                            EcosTurnout etx = (EcosTurnout) provideTurnout(et.getSlaveAddress());
200                            etx.reply(m);
201                        }
202
203                    } else if (headerDetails.contains("symbol")) {
204                        //Extract symbol number and set on turnout.
205                        int symbol = GetEcosObjectNumber.getEcosObjectNumber(msgContents[0], "[", "]");
206                        et.setExtended(symbol);
207                        et.setTurnoutOperation(jmri.InstanceManager.getDefault(TurnoutOperationManager.class).getOperation("NoFeedback"));
208                        if ((symbol == 2) || (symbol == 4)) {
209
210                            EcosTurnout etx = (EcosTurnout) provideTurnout(et.getSlaveAddress());
211                            etx.setExtended(symbol);
212                            etx.setTurnoutOperation(jmri.InstanceManager.getDefault(TurnoutOperationManager.class).getOperation("NoFeedback"));
213                            switch (symbol) {
214                                case 2:
215                                    et.setComment("Three Way Point with " + et.getSlaveAddress());
216                                    break;
217                                case 4:
218                                    et.setComment("Double Slip with " + et.getSlaveAddress());
219                                    break;
220                                default:
221                                    break;
222                            }
223                        }
224                        // get initial state
225                        EcosMessage em = new EcosMessage("get(" + ecosObjectId + ",state)");
226                        tc.sendEcosMessage(em, this);
227
228                    } else if (headerDetails.contains("addrext")) {
229                        turnoutAddressDetails(msgContents[0]);
230                    } else {
231                        String name = null;
232                        for (String li : msgContents) {
233                            if (li.contains("name")) {
234                                //start=li.indexOf("[")+2;
235                                //end=li.indexOf("]")-1;
236                                if ((name != null) /*&& (start!=end)*/) {
237                                    name = name + EcosReply.getContentDetail(li); /*" " + li.substring(start, end);*/
238
239                                } else {
240                                    name = EcosReply.getContentDetail(li); /*li.substring(start, end);*/
241
242                                }
243                            }
244                        }
245                        if (name != null) {
246                            et.setUserName(name);
247                        }
248                    }
249                } else if (ecosObjectId >= 20000) { // ecosObjectId <= 30000 is always true at this point (Spotbugs)
250                    log.debug("Reply for specific turnout");
251                    et = _tecos.get(ecosObjectId);
252                    if (et != null) {
253                        et.reply(m);
254                        //As the event will come from one object, we shall check to see if it is an extended address,
255                        // if it is we also forward the message onto the slaved address.
256                        if (et.getExtended() != 0) {
257                            log.debug("This is also an extended turnout so forwarding on change to {}", et.getSlaveAddress());
258                            EcosTurnout etx = (EcosTurnout) provideTurnout(et.getSlaveAddress());
259                            etx.reply(m);
260                        }
261                    }
262                }
263            }
264        } else {
265            log.debug("Message received from Ecos is in error");
266        }
267    }
268
269    protected boolean addingTurnouts = false;
270
271    private void turnoutAddressDetails(String lines) {
272        addingTurnouts = true;
273        EcosTurnout et;
274        int start;
275        int end;
276        int object = GetEcosObjectNumber.getEcosObjectNumber(lines, null, " ");
277        if ((20000 <= object) && (object < 30000)) {
278            start = lines.indexOf('[') + 1;
279            end = lines.indexOf(']');
280            String turnoutadd = stripChar(lines.substring(start, end));
281            String[] straddr = turnoutadd.split(",");
282            log.debug("Number of Address for this device is {}", straddr.length);
283            if (straddr.length <= 2) {
284                if (straddr.length == 2) {
285                    if (!straddr[0].equals(straddr[1])) {
286                        log.debug("Addresses are not the same, we shall use the first address listed.");
287                    }
288                }
289                int addr = Integer.parseInt(straddr[0]);
290                if (addr > 0) {
291                    Turnout t = getTurnout(getSystemNamePrefix() + addr);
292                    if (t == null) {
293                        et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + addr);
294                        et.setObjectNumber(object);
295                        _tecos.put(object, et);
296                        // listen for changes
297                        EcosMessage em = new EcosMessage("request(" + object + ",view)");
298                        tc.sendEcosMessage(em, this);
299
300                        // get initial state
301                        em = new EcosMessage("get(" + object + ",state)");
302                        tc.sendEcosMessage(em, this);
303
304                        em = new EcosMessage("get(" + object + ", name1, name2, name3)");
305                        tc.sendEcosMessage(em, this);
306                    }
307                }
308
309            } else if (straddr.length == 4) {
310                log.debug("We have a two address object.");
311                //The first two addresses should be the same
312                if (!straddr[0].equals(straddr[1])) {
313                    log.debug("First Pair of Addresses are not the same, we shall use the first address");
314                }
315                if (!straddr[2].equals(straddr[3])) {
316                    log.debug("Second Pair of Addresses are not the same, we shall use the first address");
317                }
318                int addr = Integer.parseInt(straddr[0]);
319                int addr2 = Integer.parseInt(straddr[2]);
320                if (addr > 0) {
321                    //addr = straddr[0];
322                    Turnout t = getTurnout(getSystemNamePrefix() + addr);
323                    if (t == null) {
324                        et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + addr);
325                        et.setObjectNumber(object);
326                        et.setSlaveAddress(addr2);
327                        _tecos.put(object, et);
328
329                        //Get the type of accessory...
330                        EcosMessage em = new EcosMessage("get(" + object + ",symbol)");
331                        tc.sendEcosMessage(em, this);
332
333                        // listen for changes
334                        em = new EcosMessage("request(" + object + ",view)");
335                        tc.sendEcosMessage(em, this);
336
337                        em = new EcosMessage("get(" + object + ", name1, name2, name3)");
338                        tc.sendEcosMessage(em, this);
339                    }
340                }
341
342                if (addr2 > 0) {
343                    Turnout t = getTurnout(getSystemNamePrefix() + addr2);
344                    if (t == null) {
345                        et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + addr2);
346                        et.setMasterObjectNumber(false);
347                        et.setObjectNumber(object);
348                        et.setComment("Extended address linked with turnout " + getSystemPrefix() + "T" + straddr[0]);
349                    }
350                }
351            }
352
353        } else if ((30000 <= object) && (object < 40000)) {  //This is a ecos route
354
355            log.debug("Found route object {}", object);
356
357            Turnout t = getTurnout(getSystemNamePrefix() + object);
358            if (t == null) {
359                et = (EcosTurnout) provideTurnout(getSystemNamePrefix() + object);
360                et.setObjectNumber(object);
361                _tecos.put(object, et);
362
363                // get initial state
364                EcosMessage em = new EcosMessage("get(" + object + ",state)");
365                tc.sendEcosMessage(em, this);
366                //Need to do some more work on routes on the ecos.
367
368                // listen for changes
369                // em = new EcosMessage("request("+object+",view)");
370                // tc.sendEcosMessage(em, null);
371                // get the name from the ecos to set as Username
372                em = new EcosMessage("get(" + object + ", name1, name2, name3)");
373                tc.sendEcosMessage(em, this);
374            }
375        }
376        addingTurnouts = false;
377    }
378
379    /* This is used after an event update form the ecos informing us of a change in the 
380     * turnout list, we have to determine if it is an addition or delete.
381     * We should only ever do either a remove or an add in one go.
382     */
383    void checkTurnoutList(String[] ecoslines) {
384        final EcosPreferences p = getMemo().getPreferenceManager();
385
386        String[] jmrilist = getEcosObjectArray();
387        boolean nomatch = true;
388        int intTurnout = 0;
389        String strECOSTurnout = null;
390        for (int i = 0; i < jmrilist.length; i++) {
391            nomatch = true;
392            String strJMRITurnout = jmrilist[i];
393            intTurnout = Integer.parseInt(strJMRITurnout);
394            for (String li : ecoslines) {
395                strECOSTurnout = li.replaceAll("[\\n\\r]", "");
396                if (strECOSTurnout.equals(strJMRITurnout)) {
397                    nomatch = false;
398                    break;
399                }
400            }
401
402            if (nomatch) {
403                final EcosTurnout et = (EcosTurnout) getByEcosObject(intTurnout);
404                _tecos.remove(intTurnout);
405                if (p.getRemoveTurnoutsFromJMRI() == 0x02) {
406                    //Remove turnout
407                    _tecos.remove(et.getObject());
408                    deregister(et);
409                } else if (p.getRemoveTurnoutsFromJMRI() == 0x00) {
410                    final JDialog dialog = new JDialog();
411                    dialog.setTitle(Bundle.getMessage("DeleteTurnoutTitle"));
412                    dialog.setLocationRelativeTo(null);
413                    dialog.setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
414                    JPanel container = new JPanel();
415                    container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
416                    container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
417
418                    JLabel question = new JLabel(Bundle.getMessage("RemoveTurnoutLine1", et.getDisplayName()));
419                    question.setAlignmentX(Component.CENTER_ALIGNMENT);
420                    container.add(question);
421                    question = new JLabel(Bundle.getMessage("RemoveTurnoutLine2"));
422                    question.setAlignmentX(Component.CENTER_ALIGNMENT);
423                    container.add(question);
424                    final JCheckBox remember = new JCheckBox(Bundle.getMessage("MessageRememberSetting"));
425                    remember.setFont(remember.getFont().deriveFont(10f));
426                    remember.setAlignmentX(Component.CENTER_ALIGNMENT);
427
428                    JButton yesButton = new JButton(Bundle.getMessage("ButtonYes"));
429                    JButton noButton = new JButton(Bundle.getMessage("ButtonNo"));
430                    JPanel button = new JPanel();
431                    button.setAlignmentX(Component.CENTER_ALIGNMENT);
432                    button.add(yesButton);
433                    button.add(noButton);
434                    container.add(button);
435
436                    noButton.addActionListener(new ActionListener() {
437                        @Override
438                        public void actionPerformed(ActionEvent e) {
439                            if (remember.isSelected()) {
440                                p.setRemoveTurnoutsFromJMRI(0x01);
441                            }
442                            dialog.dispose();
443                        }
444                    });
445
446                    yesButton.addActionListener(new ActionListener() {
447                        final ResourceBundle rb = ResourceBundle.getBundle("jmri.jmrit.beantable.BeanTableBundle");
448
449                        @Override
450                        public void actionPerformed(ActionEvent e) {
451                            if (remember.isSelected()) {
452                                p.setRemoveTurnoutsFromJMRI(0x02);
453                            }
454                            int count = et.getNumPropertyChangeListeners() - 1; // one is this table
455                            if (log.isDebugEnabled()) {
456                                log.debug("Delete with {}", count);
457                            }
458                            if ((!noWarnDelete) && (count > 0)) {
459                                String msg = java.text.MessageFormat.format(
460                                        rb.getString("DeletePrompt") + "\n"
461                                        + rb.getString("ReminderInUse"),
462                                        new Object[]{et.getSystemName(), "" + count});
463                                // verify deletion
464                                int val = JmriJOptionPane.showOptionDialog(null,
465                                        msg, Bundle.getMessage("WarningTitle"),
466                                        JmriJOptionPane.DEFAULT_OPTION, JmriJOptionPane.QUESTION_MESSAGE, null,
467                                        new Object[]{Bundle.getMessage("ButtonYes"),
468                                            rb.getString("ButtonYesPlus"),
469                                                Bundle.getMessage("ButtonNo")},
470                                        Bundle.getMessage("ButtonNo"));
471                                if (val == 2 || val == JmriJOptionPane.CLOSED_OPTION ) { // array position 2
472                                    _tecos.remove(et.getObject());
473                                    deregister(et);
474                                    dialog.dispose();
475                                    return;  // return without deleting
476                                }
477                                if (val == 1) { // suppress future warnings
478                                    noWarnDelete = true;
479                                }
480                            }
481                            // finally OK, do the actual delete
482                            deleteEcosTurnout(et);
483                            dialog.dispose();
484                        }
485                    });
486                    container.add(remember);
487                    container.setAlignmentX(Component.CENTER_ALIGNMENT);
488                    container.setAlignmentY(Component.CENTER_ALIGNMENT);
489                    dialog.getContentPane().add(container);
490                    dialog.pack();
491                    dialog.setModal(true);
492                    dialog.setVisible(true);
493                } else {
494                    //We will need to remove the turnout from our list as it no longer exists on the ecos.
495                    _tecos.remove(et.getObject());
496                }
497            }
498        }
499        int turnout;
500        for (String li : ecoslines) {
501            String tmpturn = li.replaceAll("[\\n\\r]", "");
502            turnout = Integer.parseInt(tmpturn);
503            if (getByEcosObject(turnout) == null) {
504                EcosMessage mout = new EcosMessage("get(" + turnout + ", addrext)");
505                tc.sendEcosMessage(mout, this);
506            }
507        }
508    }
509
510    private boolean noWarnDelete = false;
511
512    public String stripChar(String s) {
513        String allowed
514                = ",0123456789";
515        StringBuilder result = new StringBuilder();
516        for (int i = 0; i < s.length(); i++) {
517            if (allowed.indexOf(s.charAt(i)) >= 0) {
518                result.append(s.charAt(i));
519            }
520        }
521
522        return result.toString();
523    }
524
525    @Override
526    public void message(EcosMessage m) {
527        // messages are ignored
528    }
529
530    @Override
531    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "UCF_USELESS_CONTROL_FLOW", 
532        justification = "OK to compare floats, as even tiny differences should trigger update")
533    public void propertyChange(java.beans.PropertyChangeEvent e) {
534        if ((e.getPropertyName().equals("length")) && (!addingTurnouts)) {
535            final EcosPreferences p = getMemo().getPreferenceManager();
536            EcosTurnout et;
537            String[] ecoslist = this.getEcosObjectArray();
538            
539             for (Turnout turnout : getNamedBeanSet()) {
540                if (turnout.getSystemName().startsWith(getSystemNamePrefix())) {
541                    et = (EcosTurnout) turnout;
542                    if (et.getObject() == 0) {
543                        //We do not support this yet at there are many parameters
544                        // when creating a turnout on the ecos.
545                    }
546                }
547            }
548
549            for (int i = 0; i < ecoslist.length; i++) {
550                et = (EcosTurnout) getByEcosObject(Integer.parseInt(ecoslist[i]));
551                int address = et.getNumber();
552                if (getBySystemName(getSystemNamePrefix() + address) == null) {
553                    if (p.getRemoveTurnoutsFromEcos() == 0x02) {
554                        RemoveObjectFromEcos removeObjectFromEcos = new RemoveObjectFromEcos();
555                        removeObjectFromEcos.removeObjectFromEcos("" + et.getObject(), tc);
556                        deleteEcosTurnout(et);
557                    } else {
558                        final EcosTurnout etd = et;
559                        final JDialog dialog = new JDialog();
560                        dialog.setTitle(Bundle.getMessage("RemoveTurnoutTitle"));
561                        dialog.setLocation(300, 200);
562                        dialog.setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
563                        JPanel container = new JPanel();
564                        container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
565                        container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
566
567                        JLabel question = new JLabel(Bundle.getMessage("RemoveTurnoutX", etd.getSystemName()));
568                        question.setAlignmentX(Component.CENTER_ALIGNMENT);
569                        container.add(question);
570                        final JCheckBox remember = new JCheckBox(Bundle.getMessage("MessageRememberSetting"));
571                        remember.setFont(remember.getFont().deriveFont(10f));
572                        remember.setAlignmentX(Component.CENTER_ALIGNMENT);
573                        remember.setVisible(true);
574                        JButton yesButton = new JButton(Bundle.getMessage("ButtonYes"));
575                        JButton noButton = new JButton(Bundle.getMessage("ButtonNo"));
576                        JPanel button = new JPanel();
577                        button.setAlignmentX(Component.CENTER_ALIGNMENT);
578                        button.add(yesButton);
579                        button.add(noButton);
580                        container.add(button);
581
582                        noButton.addActionListener(new ActionListener() {
583                            @Override
584                            public void actionPerformed(ActionEvent e) {
585                                if (remember.isSelected()) {
586                                    p.setRemoveTurnoutsFromEcos(0x01);
587                                }
588                                dialog.dispose();
589                            }
590                        });
591
592                        yesButton.addActionListener(new ActionListener() {
593                            @Override
594                            public void actionPerformed(ActionEvent e) {
595                                if (remember.isSelected()) {
596                                    p.setRemoveTurnoutsFromEcos(0x02);
597                                }
598                                RemoveObjectFromEcos removeObjectFromEcos = new RemoveObjectFromEcos();
599                                removeObjectFromEcos.removeObjectFromEcos("" + etd.getObject(), tc);
600                                deleteEcosTurnout(etd);
601                                dialog.dispose();
602                            }
603                        });
604                        container.add(remember);
605                        container.setAlignmentX(Component.CENTER_ALIGNMENT);
606                        container.setAlignmentY(Component.CENTER_ALIGNMENT);
607                        dialog.getContentPane().add(container);
608                        dialog.pack();
609                        dialog.setModal(true);
610                        dialog.setVisible(true);
611                    }
612                }
613            }
614        }
615        super.propertyChange(e);
616    }
617
618    public void deleteEcosTurnout(EcosTurnout et) {
619        addingTurnouts = true;
620        deregister(et);
621        et.dispose();
622        EcosMessage em = new EcosMessage("release(" + et.getObject() + ",view)");
623        tc.sendEcosMessage(em, this);
624        _tecos.remove(Integer.valueOf(et.getObject()));
625        addingTurnouts = false;
626    }
627
628    @Override
629    public void dispose() {
630        Enumeration<Integer> en = _tecos.keys();
631        EcosMessage em;
632        while (en.hasMoreElements()) {
633            int ecosObject = en.nextElement();
634            em = new EcosMessage("release(" + ecosObject + ",view)");
635            tc.sendEcosMessage(em, this);
636        }
637
638        if (InstanceManager.getNullableDefault(ConfigureManager.class) != null) {
639            InstanceManager.getDefault(ConfigureManager.class).deregister(this);
640        }
641        _tecos.clear();
642        tc.removeEcosListener(this); // disconnect from tc
643        super.dispose(); // remove SensorManager and SystemConnectionMemo change listeners
644    }
645
646    public List<String> getEcosObjectList() {
647        String[] arr = new String[_tecos.size()];
648        List<String> out = new ArrayList<String>();
649        Enumeration<Integer> en = _tecos.keys();
650        int i = 0;
651        while (en.hasMoreElements()) {
652            arr[i] = "" + en.nextElement();
653            i++;
654        }
655        java.util.Arrays.sort(arr);
656        for (i = 0; i < arr.length; i++) {
657            out.add(arr[i]);
658        }
659        return out;
660    }
661
662    public String[] getEcosObjectArray() {
663        String[] arr = new String[_tecos.size()];
664        Enumeration<Integer> en = _tecos.keys();
665        int i = 0;
666        while (en.hasMoreElements()) {
667            arr[i] = "" + en.nextElement();
668            i++;
669        }
670        java.util.Arrays.sort(arr);
671        return arr;
672    }
673
674    public Turnout getByEcosObject(int ecosObject) {
675        return _tecos.get(Integer.valueOf(ecosObject));
676    }
677
678    public void refreshItems() {
679        /*ask to be notified about newly created turnouts on the layout.
680         Doing the request to view the list, will also kick off a request to 
681         view on each individual turnout*/
682        EcosMessage m = new EcosMessage("request(11, view)");
683        tc.sendEcosMessage(m, this);
684        for (Integer ecosObjectId : _tecos.keySet()) {
685            EcosMessage em = new EcosMessage("release(" + ecosObjectId + ",view)");
686            tc.sendEcosMessage(em, this);
687            em = new EcosMessage("get(" + ecosObjectId + ",state)");
688            tc.sendEcosMessage(em, this);
689            em = new EcosMessage("request(" + ecosObjectId + ",view)");
690            tc.sendEcosMessage(em, this);
691        }
692    }
693    
694    @Override
695    @Nonnull
696    public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException {
697        int iName;
698        try {
699                iName = Integer.parseInt(curAddress);
700            } catch (NumberFormatException ex) {
701                throw new JmriException("Hardware Address passed "+curAddress+" should be a number.");
702            }
703        return prefix + typeLetter() + iName;
704    }
705    
706    /**
707     * Validates to contain at least 1 number . . .
708     * <p>
709     * TODO: Custom validation for EcosTurnoutManager could be improved.
710     * {@inheritDoc}
711     */
712    @Override
713    @Nonnull
714    public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale) throws jmri.NamedBean.BadSystemNameException {
715        return validateSystemNameFormatOnlyNumeric(name, locale);
716    }
717
718    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EcosTurnoutManager.class);
719
720}