001package jmri.jmrix.openlcb; 002 003import jmri.*; 004import jmri.implementation.*; 005import jmri.jmrix.can.CanSystemConnectionMemo; 006 007import org.openlcb.*; 008import org.openlcb.implementations.LocationServiceUtils; 009 010import javax.annotation.Nonnull; 011 012/** 013 * Central functions for OlcbMeters. 014 * 015 * This sends a "Identify Consumers 01.02.00.00.00.00.00.00" when created to get 016 * all existing sources of Location Services info to reply. 017 * 018 * When it sees an EWP PCER for Location Services, it parses the message 019 * for current (Amperes) or voltage (Volts) analog content. If found, 020 * it first makes sure a meter exists for that quantity, then updates 021 * that meter object. 022 * 023 * System names SystemPrefix-NodeID-UnitCode-OrdinalNumber-AnyGivenText 024 * User name is (at start) "NodeNameFromSnip AnyGivenText (Unit)" 025 * 026 * @author Bob Jacobsen Copyright (C) 2025 027 */ 028public class OlcbMeterManager extends jmri.managers.AbstractMeterManager { 029 030 /** 031 * Create a new MeterManager instance. 032 * 033 * @param memo the system connection 034 */ 035 public OlcbMeterManager(@Nonnull CanSystemConnectionMemo memo) { 036 super(memo); 037 this.memo = memo; 038 this.iface = memo.get(OlcbInterface.class); 039 iface.registerMessageListener(new EWPListener()); 040 this.store = memo.get(MimicNodeStore.class); 041 } 042 043 private final OlcbInterface iface; 044 private final CanSystemConnectionMemo memo; 045 private final MimicNodeStore store; 046 047 class EWPListener extends MessageDecoder { 048 @Override 049 public void handleProducerConsumerEventReport(ProducerConsumerEventReportMessage msg, Connection sender){ 050 051 LocationServiceUtils.Content content = LocationServiceUtils.parse(msg); 052 if (content == null) return; // not location services 053 054 // process the blocks looking for an analog block 055 int ordinal = 1; 056 var scannedNode = content.getScannedDevice(); 057 String scannedName =""; 058 if (store != null) scannedName = store.findNode(scannedNode).getSimpleNodeIdent().getUserName(); 059 log.debug("Retrieved scannedNode {} scannedName {}", scannedNode, scannedName); 060 if (scannedName == null || scannedName.isEmpty()) scannedName = scannedNode.toString(); 061 062 for (LocationServiceUtils.Block block : content.getBlocks()) { 063 log.debug(" Block of type {}", block.getType()); 064 if (block instanceof LocationServiceUtils.AnalogBlock ) { 065 var analog = (LocationServiceUtils.AnalogBlock) block; 066 // analog block: find an existing meter or make a new one 067 var text = analog.getText(); 068 var unit = analog.getUnit(); 069 070 var systemLetter = memo.getSystemPrefix(); 071 var sysName = systemLetter+typeLetter()+" "+scannedNode.toString()+" "+ordinal+" "+text; 072 var userName = scannedName+" "+ordinal+" "+text; 073 074 log.debug(" Unit: {}, Text: '{}' systemName: '{}'", unit, text, sysName); 075 076 var meter = getBySystemName(sysName); 077 078 // did we get one? 079 if (meter == null) { 080 // no, create it 081 log.debug("Creating new meter '{}' of type '{}'", 082 text, unit); 083 if (unit == LocationServiceUtils.AnalogBlock.Unit.VOLTS) { 084 meter = createVoltageMeter(sysName); 085 } else if (unit == LocationServiceUtils.AnalogBlock.Unit.AMPERES) { 086 meter = createCurrentMeter(sysName); 087 } else { 088 log.warn("Meters of type {} are not supported yet", unit); 089 continue; 090 } 091 //store meter by incoming name for lookup later 092 InstanceManager.getDefault(MeterManager.class).register(meter); 093 } 094 095 // set the user name to keep it updated 096 meter.setUserName(userName); 097 098 // meter exists here - give it a value 099 ((AbstractAnalogIO)meter).setValue(analog.getValue()); 100 101 // and go on to the next one 102 ordinal++; 103 } 104 } 105 } 106 } 107 108 public static Meter createVoltageMeter(String sysName) { 109 var meter = new DefaultMeter.DefaultVoltageMeter( 110 sysName, Meter.Unit.NoPrefix, 0., 50., 0.001, null); // no updateTask 111 InstanceManager.getDefault(MeterManager.class).register(meter); 112 return meter; 113 } 114 115 public static Meter createCurrentMeter(String sysName) { 116 var meter = new DefaultMeter.DefaultCurrentMeter( 117 sysName, Meter.Unit.NoPrefix, 0., 50., 0.001, null); // no updateTask 118 InstanceManager.getDefault(MeterManager.class).register(meter); 119 return meter; 120 } 121 122 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OlcbMeterManager.class); 123 124}