001package jmri.jmrix.bidib;
002
003import java.util.HashMap;
004import java.util.Map;
005import jmri.*;
006import jmri.implementation.DefaultMeter;
007import jmri.implementation.MeterUpdateTask;
008
009import org.bidib.jbidibc.core.DefaultMessageListener;
010import org.bidib.jbidibc.core.MessageListener;
011import org.bidib.jbidibc.messages.CurrentValue;
012import org.bidib.jbidibc.messages.Node;
013import org.bidib.jbidibc.messages.message.BoostQueryMessage;
014import org.bidib.jbidibc.messages.utils.NodeUtils;
015
016import org.slf4j.Logger;
017import org.slf4j.LoggerFactory;
018
019
020/**
021 * Provide access to voltage and current readings
022 *
023 * @author Mark Underwood    Copyright (C) 2015
024 * @author Paul Bender       Copyright (C) 2017
025 * @author Daniel Bergqvist  Copyright (C) 2020
026 * @author Eckart Meyer      Copyright (C) 2021
027 */
028public class BiDiBPredefinedMeters {
029
030    private BiDiBTrafficController tc;
031    private final BiDiBSystemConnectionMemo _memo;
032    private final MeterUpdateTask updateTask;
033    private final Map<Integer, Meter> currentMeters = new HashMap<>();
034    private final Map<Integer, Meter> voltageMeters = new HashMap<>();
035
036    private boolean enabled = false;  // disable by default; prevent polling when not being used.
037
038    public BiDiBPredefinedMeters(BiDiBSystemConnectionMemo memo) {
039        
040        _memo = memo;
041        tc = _memo.getBiDiBTrafficController();
042        
043        updateTask = new UpdateTask(-1);
044        
045//        // scan nodes list for booster nodes
046        Map<Long, Node> nodes = tc.getNodeList();
047        for(Map.Entry<Long, Node> entry : nodes.entrySet()) {
048            Node node = entry.getValue();
049            if (NodeUtils.hasBoosterFunctions(node.getUniqueId())) {
050                log.trace("Booster - node addr: {}, node uid: {}", node.getAddr(), node);
051                String sysname = String.format("X%010x", node.getUniqueId() & 0xffffffffffL);
052                Meter currentMeter = new DefaultMeter.DefaultCurrentMeter(
053                        memo.getSystemPrefix() + InstanceManager.getDefault(MeterManager.class).typeLetter() + sysname + ":BoosterCurrent",
054                        Meter.Unit.Milli, 0, 20224.0, 1, updateTask);
055                currentMeters.put(NodeUtils.convertAddress(node.getAddr()), currentMeter);
056
057                Meter voltageMeter = new DefaultMeter.DefaultVoltageMeter(
058                        memo.getSystemPrefix() + InstanceManager.getDefault(MeterManager.class).typeLetter() + sysname + ":BoosterVoltage",
059                        Meter.Unit.Milli, 0, 25000.0, 100, updateTask);
060                voltageMeters.put(NodeUtils.convertAddress(node.getAddr()), voltageMeter);
061
062                InstanceManager.getDefault(MeterManager.class).register(currentMeter);
063                InstanceManager.getDefault(MeterManager.class).register(voltageMeter);
064
065                log.debug("BiDiBPredefinedMeters constructor called");
066            }
067        }
068    }
069
070    public void setBiDiBTrafficController(BiDiBTrafficController controller) {
071        tc = controller;
072    }
073
074    private void disposeMeter(Meter meter) {
075        updateTask.disable(meter);
076        InstanceManager.getDefault(MeterManager.class).deregister(meter);
077        updateTask.dispose(meter);
078    }
079    
080    public void dispose() {
081        for(Map.Entry<Integer, Meter> entry : currentMeters.entrySet()) {
082            disposeMeter(entry.getValue());
083        }
084        for(Map.Entry<Integer, Meter> entry : voltageMeters.entrySet()) {
085            disposeMeter(entry.getValue());
086        }
087//        updateTask.disable(currentMeter);
088//        updateTask.disable(voltageMeter);
089//        InstanceManager.getDefault(MeterManager.class).deregister(currentMeter);
090//        InstanceManager.getDefault(MeterManager.class).deregister(voltageMeter);
091//        updateTask.dispose(currentMeter);
092//        updateTask.dispose(voltageMeter);
093    }
094
095
096    private class UpdateTask extends MeterUpdateTask {
097    
098        MessageListener messageListener = null;
099        
100        public UpdateTask(int interval) {
101            super(interval);
102            createBoosterDiagListener();
103        }
104    
105        @Override 
106        public void enable(){
107            enabled = true;
108            // TODO: set feature to enable booster diag messages - and switch it off by default somewhere
109            tc.addMessageListener(messageListener);        
110            log.info("Enabled meter.");
111            super.enable();
112        }
113
114        @Override 
115        public void disable(){
116            if (!enabled) return;
117            super.disable();
118            enabled = false;
119            // TODO: set feature to disable booster diag messages
120            tc.removeMessageListener(messageListener);
121            log.info("Disabled meter.");
122        }
123        
124        private void setCurrent(byte[] address, double value) throws JmriException {
125            Meter meter = currentMeters.get(NodeUtils.convertAddress(address));
126            log.trace("setCurrent - addr: {}, Meter: {}, value: {}", address, meter, value);
127            if (meter != null) {
128                meter.setCommandedAnalogValue(value);
129            }
130        }
131
132        private void setVoltage(byte[] address, double value) throws JmriException {
133            Meter meter = voltageMeters.get(NodeUtils.convertAddress(address));
134            log.trace("setVoltage - addr: {}, Meter: {}, value: {}", address, meter, value);
135            if (meter != null) {
136                meter.setCommandedAnalogValue(value);
137            }
138        }
139
140        @Override
141        public void requestUpdateFromLayout() {
142            Map<Long, Node> nodes = tc.getNodeList();
143            for(Map.Entry<Long, Node> entry : nodes.entrySet()) {
144                Node node = entry.getValue();
145                if (NodeUtils.hasBoosterFunctions(node.getUniqueId())) {
146                    tc.sendBiDiBMessage(new BoostQueryMessage(), node);
147                }
148            }
149        }
150
151        private void createBoosterDiagListener() {
152            // to listen messages related to track power.
153            messageListener = new DefaultMessageListener() {
154                @Override
155                public void boosterDiag(byte[] address, int messageNum, CurrentValue current, int voltage, int temperature) {
156                    log.info("METER booster diag was signalled: node addr: {}, current: {}, voltage: {}, temperature: {}",
157                            address, current, voltage, temperature);
158                    try {
159                        setCurrent(address, current.getCurrent() * 1.0f);
160                        setVoltage(address, voltage * 100.0f); //units of 100mV
161                    } catch (JmriException e) {
162                        log.error("exception thrown by setCurrent or setVoltage", e);
163                    }
164                }
165            };
166        }
167
168
169    }
170    
171    private static final Logger log = LoggerFactory.getLogger(BiDiBPredefinedMeters.class);
172
173}