001package jmri.jmrix.can.cbus;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import javax.annotation.Nonnull;
007
008import jmri.*;
009import jmri.jmrix.can.CanSystemConnectionMemo;
010import jmri.managers.AbstractReporterManager;
011
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015/**
016 * Implement ReporterManager for CAN CBUS systems.
017 * <p>
018 * System names are "MRnnnnn", where M is the user-configurable system getSystemPrefix(),
019 * nnnnn is the reporter number without padding.
020 * <p>
021 * CBUS Reporters are NOT automatically created.
022 *
023 * @author Mark Riddoch Copyright (C) 2015
024 * @author Steve Young Copyright (C) 2019
025 */
026public class CbusReporterManager extends AbstractReporterManager {
027
028    public CbusReporterManager(CanSystemConnectionMemo memo) {
029        super(memo);
030    }
031    
032    /**
033     * {@inheritDoc}
034     */
035    @Override
036    @Nonnull
037    public CanSystemConnectionMemo getMemo() {
038        return (CanSystemConnectionMemo) memo;
039    }
040
041    /**
042     * {@inheritDoc}
043     */
044    @Override
045    @Nonnull
046    protected Reporter createNewReporter(@Nonnull String systemName, String userName) throws IllegalArgumentException {
047        log.debug("ReporterManager create new CbusReporter: {}", systemName);
048        String addr = systemName.substring(getSystemNamePrefix().length());
049        Reporter t = new CbusReporter(addr, getMemo());
050        t.setUserName(userName);
051        t.addPropertyChangeListener(this);
052        return t;
053    }
054
055    /**
056     * {@inheritDoc}
057     * Checks for reporter number between 0 and 65535
058     */
059    @Override
060    public NameValidity validSystemNameFormat(@Nonnull String systemName) {
061        // name must be in the MSnnnnn format (M is user configurable); no + or ; or - for Reporter address
062        log.debug("Checking system name: {}", systemName);
063        try {
064            // try to parse the string; success returns true
065            int testnum = Integer.parseInt(systemName.substring(getSystemPrefix().length() + 1, systemName.length()));
066            if ( testnum < 0 ) {
067                log.debug("Number field cannot be negative in system name: {}", systemName);
068                return NameValidity.INVALID;
069            }
070            if ( testnum > 65535  ) {
071                log.debug("Number field cannot be greater than 65535 in system name: {}", systemName);
072                return NameValidity.INVALID;
073            }
074        }
075        catch (NumberFormatException e) {
076            log.debug("Illegal character in number field of system name: {}", systemName);
077            return NameValidity.INVALID;
078        }
079        catch (StringIndexOutOfBoundsException e) {
080            log.debug("Wrong length ( missing MR? ) for system name: {}", systemName);
081            return NameValidity.INVALID;
082        }
083        log.debug("Valid system name: {}", systemName);
084        return NameValidity.VALID;
085    }
086    
087    /**
088     * {@inheritDoc}
089     */
090    @Override
091    public boolean allowMultipleAdditions(@Nonnull String systemName) {
092        return true;
093    }
094
095    /**
096     * {@inheritDoc}
097     */
098    @Override
099    public String getEntryToolTip() {
100        return Bundle.getMessage("AddReporterEntryToolTip");
101    }
102    
103    /** 
104     * Validates to only numeric system names.
105     * {@inheritDoc}
106     */
107    @Override
108    @Nonnull
109    public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale) throws jmri.NamedBean.BadSystemNameException {
110        return validateSystemNameFormatOnlyNumeric(name,locale);
111    }
112    
113    protected final static String CBUS_REPORTER_DESCRIPTOR_KEY = "CBUS Reporter Type"; // NOI18N
114    
115    protected final static String CBUS_REPORTER_TYPE_CLASSIC = "Classic RFID"; // NOI18N
116    
117    protected final static String CBUS_REPORTER_TYPE_DDES_DESCRIBING = "CANRC522 / CANRCOM"; // NOI18N
118    
119    protected final static String[] CBUS_REPORTER_TYPES = {
120        CBUS_REPORTER_TYPE_CLASSIC,CBUS_REPORTER_TYPE_DDES_DESCRIBING};
121    
122    protected final static String[] CBUS_REPORTER_TYPE_TIPS = {
123        "DDES / ACDAT 5 byte unique tag.","DDES self-describing ( Writeable CANRC522 / Railcom )"}; // NOI18N
124    
125    protected final static String CBUS_DEFAULT_REPORTER_TYPE = CBUS_REPORTER_TYPES[0];
126    
127    protected final static String CBUS_MAINTAIN_SENSOR_DESCRIPTOR_KEY = "Maintain CBUS Sensor"; // NOI18N
128    
129    @Override
130    @Nonnull
131    public List<NamedBeanPropertyDescriptor<?>> getKnownBeanProperties() {
132        List<NamedBeanPropertyDescriptor<?>> l = new ArrayList<>();
133        l.add(new SelectionPropertyDescriptor(
134            CBUS_REPORTER_DESCRIPTOR_KEY, CBUS_REPORTER_TYPES, CBUS_REPORTER_TYPE_TIPS, CBUS_DEFAULT_REPORTER_TYPE) {
135            @Override
136            public String getColumnHeaderText() {
137                return CBUS_REPORTER_DESCRIPTOR_KEY;
138            }
139            @Override
140            public boolean isEditable(NamedBean bean) {
141                return (bean instanceof CbusReporter);
142            }
143        });
144        l.add(new BooleanPropertyDescriptor(
145            CBUS_MAINTAIN_SENSOR_DESCRIPTOR_KEY, false) {
146            @Override
147            public String getColumnHeaderText() {
148                return CBUS_MAINTAIN_SENSOR_DESCRIPTOR_KEY;
149            }
150            @Override
151            public boolean isEditable(NamedBean bean) {
152                return (bean instanceof CbusReporter);
153            }
154        });
155        return l;
156    }
157    
158    private int _timeout=2000; // same default as TimeoutReporter
159    
160    /**
161     * Set the Reporter timeout.
162     * @param timeout time in milliseconds that CbusReporters stay at IdTag.SEEN after hearing an ID Tag.
163     */
164    public void setTimeout(int timeout){
165        _timeout = timeout;
166    }
167    
168    /**
169     * Get the Reporter Timeout.
170     * @return milliseconds for CbusReporters to return to IdTag.UNSEEN
171     */
172    public int getTimeout(){
173        return _timeout;
174    }
175
176    private static final Logger log = LoggerFactory.getLogger(CbusReporterManager.class);
177
178}