001package jmri.jmrit.symbolicprog;
002
003import java.awt.event.ActionEvent;
004import java.beans.PropertyChangeEvent;
005import java.beans.PropertyChangeListener;
006
007import javax.swing.AbstractAction;
008
009import jmri.jmrit.roster.RosterEntry;
010import jmri.jmrit.symbolicprog.tabbedframe.PaneProgFrame;
011import jmri.jmrix.can.CanSystemConnectionMemo;
012import jmri.util.swing.JmriJOptionPane;
013
014import org.openlcb.OlcbInterface;
015import org.openlcb.NodeID;
016import org.openlcb.cdi.impl.ConfigRepresentation;
017/**
018 * Action to upload the function labels from a roster entry to a TCS CS-105.
019 *
020 * @author Bob Jacobsen Copyright (C) 2003
021 */
022public class TcsUploadAction extends AbstractAction implements PropertyChangeListener {
023
024    public TcsUploadAction(String actionName, CvTableModel pModel, VariableTableModel vModel, RosterEntry rosterEntry, PaneProgFrame pParent) {
025        super(actionName);
026        cvTable = pModel;
027        this.vModel = vModel;
028        frame = pParent;
029        this.rosterEntry = rosterEntry;
030    }
031
032    // will this be enabled if created?
033    static public boolean willBeEnabled() {
034        // see if there's an openlcb connection
035        var cscm = getSystemConnectionMemo();
036        if (cscm == null) {
037            return false;
038        }
039        if (cscm.get(org.openlcb.NodeID.class) == null) {
040            return false;
041        }
042        return true;
043    }
044
045    static CanSystemConnectionMemo getSystemConnectionMemo() {
046        return jmri.InstanceManager.getNullableDefault(CanSystemConnectionMemo.class);
047    }
048
049    PaneProgFrame frame;
050    RosterEntry rosterEntry;
051    CvTableModel cvTable;
052    VariableTableModel vModel;
053    ConfigRepresentation configRep;
054
055    @Override
056    public void actionPerformed(ActionEvent e) {
057        boolean isLong = cvTable.holdsLongAddress();
058        int addr = cvTable.holdsAddress();
059        log.debug("computed address is {} long: {}", addr, isLong);
060
061        // Create the train's node ID
062        byte upperAddressByte = (byte) (isLong ? (192+(addr>>8)) : 0);
063        byte lowerAddressByte = (byte) (addr & 0xFF);
064        var nodeID = new NodeID(new byte[]{6,1,0,0,upperAddressByte, lowerAddressByte});
065        log.debug("node ID {}", nodeID);
066
067        // check for Node ID already known
068        var nodeStore = getSystemConnectionMemo().get(org.openlcb.MimicNodeStore.class);
069        var nodeMemo = nodeStore.findNode(nodeID);
070        if (nodeMemo == null) {
071            JmriJOptionPane.showMessageDialog(frame, "Entry "+addr+" not found in CS-105, canceling");
072            return;
073        }
074
075        // get the CD/CDI information
076        configRep = new ConfigRepresentation(getSystemConnectionMemo().get(OlcbInterface.class),nodeID);
077        configRep.addPropertyChangeListener(this);
078
079    }
080
081    @Override
082    public void propertyChange(PropertyChangeEvent event) {
083        switch (event.getPropertyName()) {
084            case ConfigRepresentation.UPDATE_STATE :
085                // Normal. Indicates that the load is proceeding.
086            case ConfigRepresentation.UPDATE_REP :
087                // Normal, CDI is read in, loading caches next
088                return;
089            case ConfigRepresentation.UPDATE_CACHE_COMPLETE :
090                log.debug("CDI read done");
091
092                // look for values
093                processValuesFromGUI();
094                return;
095            default:
096                log.error("Unexpected PropertyChangeEvent {}", event);
097                return;
098        }
099    }
100
101    /**
102     * Construct and execute a listener that sets
103     * the appropriate values from the GUI elements.
104     */
105    void processValuesFromGUI() {
106        log.trace("processValuesFromGUI");
107        configRep.visit(new ConfigRepresentation.Visitor() {
108            @Override
109            public void visitString(ConfigRepresentation.StringEntry e) {
110                log.trace("String entry {} is {}", e.key, e.getValue());
111
112                if (e.key.startsWith("Train.User Description")) {
113                    e.setValue(frame.getRosterPane().getComment());
114                 } else if (e.key.startsWith("Train.Functions")) {
115                    int index = getNumberField(e.key);
116                    if (index == -1) {
117                        log.warn("Unexpected format \"{}\"", e.key);
118                        return;
119                    }
120                    if (e.key.endsWith("Description")) {
121                        String value = frame.getFnLabelPane().getLabel(index+1).getText();
122                        if (value==null) {
123                            value = "";
124                        }
125                        log.debug(".Description found function {} roster description \"{}\"", index, value);
126                        log.trace("   mapping gives {}", TcsExportAction.intFromFunctionString(
127                                        rosterEntry.getFunctionLabel(index+1)) );
128                        if (TcsExportAction.intFromFunctionString(
129                                        frame.getFnLabelPane().getLabel(index+1).getText()
130                                    ) == 0) {
131                            e.setValue(value);
132                        }
133                    } else {
134                        log.warn("Unexpected content \"{}\"", e.key);
135                    }
136                }
137            }
138
139            @Override
140            public void visitInt(ConfigRepresentation.IntegerEntry e) {
141                log.trace("Integer entry {} is {}", e.key, e.getValue());
142
143                // is this the last entry?
144                if (e.key.startsWith("Train.Delete From Roster")) {
145                    // TODO: This is firing much too soon
146                    JmriJOptionPane.showMessageDialog(frame, "Upload complete.");
147                } else if (e.key.startsWith("Train.Functions")) {
148                    int index = getNumberField(e.key);
149                    if (index == -1) {
150                        log.warn("Unexpected format \"{}\"", e.key);
151                        return;
152                    }
153                    if (e.key.endsWith(".Momentary")) {
154                        long value = 1;
155                        if (frame.getFnLabelPane().getLockable(index+1).isSelected()) {
156                            value = 0;  // lockable is not Momentary
157                        }
158                        e.setValue(value);
159                    } else if (e.key.endsWith(".Consist Behavior")) {
160
161                        // process consist bit
162                        // first, see if function variable exists
163                        var variable = vModel.findVar("Consist Address Active For F"+(index+1));
164                        if (variable != null) {
165                            // it exists, so we transfer that to the consist info
166                            int value = variable.getIntValue();
167                            e.setValue(value);
168                        } else {
169                            log.debug("Variable {} not found", "Consist Address Active For F"+(index+1) );
170                        }
171
172                    } else if (e.key.endsWith(".Display")) {
173                        // do a reverse lookup and store
174                        int value = TcsExportAction.intFromFunctionString(
175                                        frame.getFnLabelPane().getLabel(index+1).getText()
176                                    );
177                        e.setValue(value);
178                        log.debug(".display found function {} roster description \"{}\"", index, value);
179                    } else {
180                        log.warn("Unexpected content \"{}\"", e.key);
181                    }
182                }
183            }
184
185            @Override
186            public void visitEvent(ConfigRepresentation.EventEntry e) {
187                log.trace("Event entry {} is {}", e.key, e.getValue());
188            }
189        });
190    }
191
192    // Extract the number from e.g. Train.Functions(25).Momentary
193    static int getNumberField(String value) {
194        int first = value.indexOf("(");
195        int last = value.indexOf(")");
196        if (first > 0 && last > 0 && last > first + 1) {
197            var digits = value.substring(first+1, last);
198            return Integer.parseInt(digits);
199        }
200        return -1;
201    }
202
203    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TcsUploadAction.class);
204}