001package jmri.implementation;
002
003import java.util.TimerTask;
004
005import javax.annotation.Nonnull;
006
007import jmri.*;
008import jmri.util.TimerUtil;
009
010/**
011 * Abstract base class for average meter objects.
012 *
013 * @author Mark Underwood    (C) 2015
014 * @author Daniel Bergqvist  (C) 2025
015 */
016public class DefaultAverageMeter extends AbstractAnalogIO implements AverageMeter {
017
018    private static final int DELAY = 250;
019    private final int SIZE = 100;
020    private final double[] _measurements = new double[SIZE];
021
022    private int _numMeasurements = 1;
023    private TimerTask _updateTask;
024    private Meter _meter;
025    private int _time = 1000;   // 1 second
026
027    public DefaultAverageMeter(@Nonnull String sys) {
028        this(sys, null);
029    }
030
031    public DefaultAverageMeter(@Nonnull String sys, String user) {
032        super(sys, user, false);
033        DefaultAverageMeter.this.setTime(1000);
034    }
035
036    /** {@inheritDoc} */
037    @Override
038    public void enable() {
039        log.debug("Enabling meter.");
040    }
041
042    /** {@inheritDoc} */
043    @Override
044    public void disable() {
045        log.debug("Disabling meter.");
046    }
047
048    /**
049     * {@inheritDoc}
050     * <p>
051     * Override this if the meter can send value to the layout.
052     */
053    @Override
054    protected void sendValueToLayout(double value) throws JmriException {
055        // Do nothing
056    }
057
058    /** {@inheritDoc} */
059    @Override
060    protected boolean cutOutOfBoundsValues() {
061        return true;
062    }
063
064    /** {@inheritDoc} */
065    @Override
066    public void setState(int s) throws JmriException {
067        throw new UnsupportedOperationException("Not supported.");
068    }
069
070    /** {@inheritDoc} */
071    @Override
072    public int getState() {
073        throw new UnsupportedOperationException("Not supported.");
074    }
075
076    /** {@inheritDoc} */
077    @Override
078    public String getBeanType() {
079        return Bundle.getMessage("BeanNameMeter");
080    }
081
082    /** {@inheritDoc} */
083    @Override
084    public Unit getUnit() {
085        if (_meter != null) {
086            return _meter.getUnit();
087        } else {
088            return Unit.NoPrefix;
089        }
090    }
091
092    /** {@inheritDoc} */
093    @Override
094    public double getMin() {
095        if (_meter != null) {
096            return _meter.getMin();
097        } else {
098            return 0.0;
099        }
100    }
101
102    /** {@inheritDoc} */
103    @Override
104    public double getMax() {
105        if (_meter != null) {
106            return _meter.getMax();
107        } else {
108            return 0.0;
109        }
110    }
111
112    /** {@inheritDoc} */
113    @Override
114    public double getResolution() {
115        if (_meter != null) {
116            return _meter.getResolution();
117        } else {
118            return 1.0;
119        }
120    }
121
122    /** {@inheritDoc} */
123    @Override
124    public AbsoluteOrRelative getAbsoluteOrRelative() {
125        if (_meter != null) {
126            return _meter.getAbsoluteOrRelative();
127        } else {
128            return AbsoluteOrRelative.RELATIVE;
129        }
130    }
131
132    /**
133     * Request an update from the layout.
134     */
135    @Override
136    public void requestUpdateFromLayout() {
137        _meter.requestUpdateFromLayout();
138    }
139
140    @Override
141    public int getTime() {
142        return _time;
143    }
144
145    @Override
146    public void setTime(int time) {
147        this._time = time;
148        _numMeasurements = (int) Math.round(((double)_time) / DELAY);
149    }
150
151    @Override
152    public Meter getMeter() {
153        return _meter;
154    }
155
156    @Override
157    public void setMeter(Meter meter) {
158        if (meter == this._meter) {
159            return;
160        }
161        if (_meter != null) {
162            _updateTask.cancel();
163        }
164        this._meter = meter;
165        if (_meter != null) {
166            _updateTask = new MyTimerTask();
167            TimerUtil.scheduleOnGUIThread(_updateTask, DELAY, DELAY);
168        }
169    }
170
171    /** {@inheritDoc} */
172    @Override
173    public double getKnownAnalogValue() {
174        double sum = 0.0;
175        int start = SIZE - _numMeasurements;
176        for (int i=start; i < SIZE; i++) {
177            sum += _measurements[i];
178        }
179        return sum / _numMeasurements;
180    }
181
182    /** {@inheritDoc} */
183    @Override
184    public void dispose() {
185        if (_meter != null) {
186            _updateTask.cancel();
187            _meter.dispose();
188        }
189        super.dispose();
190    }
191
192
193    private class MyTimerTask extends TimerTask {
194
195        @Override
196        public void run() {
197            System.arraycopy(_measurements, 1, _measurements, 0, SIZE-1);
198            _measurements[SIZE-1] = _meter.getKnownAnalogValue();
199        }
200
201    }
202
203
204    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultAverageMeter.class);
205
206}