001package jmri.jmrix.loconet; 002 003import jmri.*; 004import jmri.implementation.DefaultMeter; 005import jmri.implementation.MeterUpdateTask; 006import jmri.jmrix.loconet.duplexgroup.swing.LnIPLImplementation; 007 008/** 009 * Provide access to current and voltage meter from some LocoNet command stations 010 * 011 * @author Steve G Copyright (C) 2019 012 * @author Bob Jacobsen Copyright (C) 2019 013 * @author Egbert Boerse Copyright (C) 2019 014 * @author Daniel Bergqvist Copyright (C) 2020 015 * @author B. Milhaupt Copyright (C) 2020 016 */ 017public class LnPredefinedMeters implements LocoNetListener, Disposable { 018 019 private SlotManager sm = null; 020 private LnTrafficController tc = null; 021 private final MeterUpdateTask updateTask; 022 private final LnMeterInitTask initializationTask; 023 024 /** 025 * Create a LnPredefinedMeters object 026 * 027 * @param scm connection memo 028 */ 029 public LnPredefinedMeters(LocoNetSystemConnectionMemo scm) { 030 this.sm = scm.getSlotManager(); 031 this.tc = scm.getLnTrafficController(); 032 033 updateTask = new MeterUpdateTask(LnConstants.METER_INTERVAL_MS) { 034 @Override 035 public void requestUpdateFromLayout() { 036 sm.sendReadSlot(249); 037 } 038 }; 039 040 tc.addLocoNetListener(~0, this); 041 042 updateTask.initTimer(); 043 044 // a work-around to ensure that the LocoNet transmit path is established 045 // before making an initial query-mode request 046 initializationTask = new LnMeterInitTask(sm.tc, 85); 047 initializationTask.initTimer(); 048 initializationTask.enable(); 049 } 050 051 @Override 052 public void message(LocoNetMessage msg) { 053 if (msg.getNumDataElements() != 21 054 || msg.getOpCode() != LnConstants.OPC_EXP_RD_SL_DATA 055 || msg.getElement(1) != 21 056 || msg.getElement(2) != 1 057 || msg.getElement(3) != 0x79) { 058 return; 059 } 060 int srcDeviceType = msg.getElement(16); 061 if ((srcDeviceType == LnConstants.RE_IPL_DIGITRAX_HOST_BXP88) 062 || (srcDeviceType == LnConstants.RE_IPL_DIGITRAX_HOST_LNWI) 063 || (srcDeviceType == LnConstants.RE_IPL_DIGITRAX_HOST_BXPA1)) { 064 // these devices support Query Mode but always return 0s for 065 // voltage/current data 066 return; 067 } 068 069 float valAmps = msg.getElement(6)/10.0f; 070 float valVolts = msg.getElement(4)*2.0f/10.0f; 071 072 int srcSerNum = msg.getElement(18)+128*msg.getElement(19); 073 074 String voltSysName = createSystemName(srcDeviceType, srcSerNum, "Voltage"); // NOI18N 075 Meter m = InstanceManager.getDefault(MeterManager.class).getBySystemName(voltSysName); 076 updateAddMeter(m, voltSysName, valVolts, true); 077 078 String ampsSysName = createSystemName(srcDeviceType, srcSerNum, "InputCurrent"); // NOI18N 079 m = InstanceManager.getDefault(MeterManager.class).getBySystemName(ampsSysName); 080 updateAddMeter(m, ampsSysName, valAmps, false); 081 } 082 083 /** 084 * Dispose of the instance. 085 * Removes TC Listener. 086 * Disposes initialization task and individual update tasks. 087 */ 088 @Override 089 public void dispose() { 090 tc.removeLocoNetListener(~0, this); 091 initializationTask.dispose(); 092 var meters = new java.util.HashSet<>(InstanceManager.getDefault(MeterManager.class).getNamedBeanSet()); 093 for (Meter m: meters) { 094 if (m.getSystemName().startsWith(sm.getSystemPrefix()+"V")) { // NOI18N 095 updateTask.disable(m); 096 InstanceManager.getDefault(MeterManager.class).deregister(m); 097 updateTask.dispose(m); 098 } 099 } 100 } 101 102 public void requestUpdateFromLayout() { 103 log.debug("sending request for voltmeter/ammeter information"); 104 sm.sendReadSlot(249); 105 } 106 107 private String createSystemName(int device, int sn, String typeString) { 108 String devName = LnIPLImplementation.getDeviceName(0, device,0,0); 109 if (devName == null) { 110 devName="["+device+"]"; // NOI18N 111 } 112 return sm.getSystemPrefix()+"V"+ devName + "(s/n"+sn+")"+typeString; // NOI18N 113 } 114 115 private void updateAddMeter(Meter m, String sysName, float value, boolean typeVolt ) { 116 if (m == null) { 117 Meter newMeter; 118 if (typeVolt) { 119 // voltMeter not (yet) registered 120 newMeter = new DefaultMeter.DefaultVoltageMeter(sysName, 121 Meter.Unit.NoPrefix, 0, 25.4, 0.2, updateTask); 122 } else { 123 // ammeter not (yet) registered 124 newMeter = new DefaultMeter.DefaultCurrentMeter(sysName, 125 Meter.Unit.NoPrefix, 0, 12.7, 0.1, updateTask); 126 } 127 try { 128 newMeter.setCommandedAnalogValue(value); 129 } catch (JmriException e) { 130 log.debug("Exception setting {}Meter {} to value {}", 131 (typeVolt?"volt":"current"), // NOI18N 132 sysName, value, e); 133 } 134 InstanceManager.getDefault(MeterManager.class).register(newMeter); 135 log.debug("Added new {}Meter {} with value {}", 136 (typeVolt?"volt":"current"), // NOI18N 137 sysName, value); 138 } else { 139 try { 140 m.setCommandedAnalogValue(value); 141 } catch (JmriException e) { 142 log.debug("Exception setting {}Meter {} to value {}", 143 (typeVolt?"volt":"current"), // NOI18N 144 sysName, value, e); 145 } 146 log.debug("Updating currentMeter {} with value {}", 147 sysName, value); 148 } 149 } 150 151 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LnPredefinedMeters.class); 152}