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}