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}