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