001package jmri.jmrit.symbolicprog;
002
003import java.awt.event.ActionEvent;
004import java.io.*;
005
006import javax.swing.AbstractAction;
007import javax.swing.JFileChooser;
008import jmri.jmrit.roster.RosterEntry;
009import jmri.jmrit.symbolicprog.tabbedframe.PaneProgFrame;
010
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014/**
015 * Action to export the RosterEntry values to a TCS-format data file.
016 * <p>
017 * TODO: Note: This first does an update of the RosterEntry from the GUI,
018 * then writes out from the Roster entry.  This means that they (RE and GUI)
019 * now agree, which has the side effect of erasing the dirty state.  Better
020 * would be to do the export directly from the GUI.
021 *
022 * @author Bob Jacobsen Copyright (C) 2003, 2023
023 */
024public class TcsExportAction extends AbstractAction {
025
026    public TcsExportAction(String actionName, CvTableModel mModel, VariableTableModel vModel, RosterEntry rosterEntry, PaneProgFrame pParent) {
027        super(actionName);
028        this.mModel = mModel;
029        this.vModel = vModel;
030        frame = pParent;
031        this.rosterEntry = rosterEntry;
032    }
033
034    JFileChooser fileChooser;
035    PaneProgFrame frame;
036    RosterEntry rosterEntry;
037
038    /**
039     * CvTableModel to load
040     */
041    CvTableModel mModel;
042
043    /**
044     * VariableTableModel to load
045     */
046    VariableTableModel vModel;
047
048    @Override
049    public void actionPerformed(ActionEvent e) {
050
051        if (fileChooser == null) {
052            fileChooser = new jmri.util.swing.JmriJFileChooser();
053        }
054
055        int retVal = fileChooser.showSaveDialog(frame);
056
057        if (retVal == JFileChooser.APPROVE_OPTION) {
058            File file = fileChooser.getSelectedFile();
059            if (log.isDebugEnabled()) {
060                log.debug("start to export to TCS file {}", file);
061            }
062
063            // update the RosterEntry from the GUI
064            frame.getRosterPane().update(rosterEntry);
065            frame.getFnLabelPane().update(rosterEntry);
066
067            try ( PrintStream str = new PrintStream(new FileOutputStream(file)); ) {
068                formatTcsVirtualNodeDefinition(str, rosterEntry, mModel, vModel);
069            } catch (IOException ex) {
070                log.error("Error writing file", ex);
071            }
072        }
073    }
074
075    /**
076     * Format the contents of the locomotive definition.
077     * This method is public static so it can be used elsewhere for e.g.
078     * direct writing to a node.
079     * @param str receives the formatted definition String
080     * @param rosterEntry defines the information to store
081     * @param model provides CV contents as available
082     * @param vModel provides variable contents as available
083     */
084    public static void formatTcsVirtualNodeDefinition(PrintStream str, RosterEntry rosterEntry, CvTableModel model, VariableTableModel vModel) {
085        str.println("Train.Name="+rosterEntry.getId());
086        str.println("Train.User Description="+rosterEntry.getComment());
087        str.println("Train.Address="+rosterEntry.getDccAddress());
088
089        // set "forced long address 128 steps" or "forced short address 128 steps"
090        str.println("Train.Speed Step Mode="+(rosterEntry.isLongAddress() ? "15" : "14"));
091
092        // Skip Consist, Directional and MU switch to allow round-trip
093
094        for (int i = 0; i <= 27; i++) { // TCS sample file went to 27
095            String label = rosterEntry.getFunctionLabel(i+1);
096            if (label == null) label = "";
097            int displayValue = intFromFunctionString(label);
098
099            str.println("Train.Functions("+i+").Display="+displayValue);
100            str.println("Train.Functions("+i+").Momentary="+(rosterEntry.getFunctionLockable(i+1) ? "0" : "1")); // Momentary == not locking
101
102            // check for CV21/CV22 variable value, otherwise skip (leave unchanged)
103            var variable = vModel.findVar("Consist Address Active For F"+(i+1));
104            if (variable != null) {
105                var value = variable.getIntValue();
106                log.trace("For index {} found consist value {}", i, value);
107                str.println("Train.Functions("+i+").Consist Behavior="+value);
108            }
109
110            str.println("Train.Functions("+i+").Description="+(displayValue != 0 ? "" : label) );
111        }
112
113        str.println("Train.Delete From Roster?=0");
114
115        str.flush();
116        str.close();
117    }
118
119    static int intFromFunctionString(String fn) {
120        if (fn == null) return 0;
121
122        switch (fn.toLowerCase().strip()) {
123
124            case "unassigned": return 0;
125
126            case "headlight": return 1;
127            case "bell": return 13;
128            case "horn": return 14;
129            case "whistle": return 15;
130            case "pantograph": return 11;
131            case "smoke": return 10;
132            case "engine": return 4;
133            case "light": return 74;
134            case "coupler clank": return 28;
135            case "couple": return 122;
136            case "uncouple": return 9;
137
138            case "shunting mode": return 7;
139            case "momentum": return 8;
140
141            case "brake": return 57;
142            case "brake release": return 200;
143            case "dynamic brake": return 41;
144            case "manual notch down": return 31;
145            case "manual notch up": return 30;
146            case "reverser": return 69;
147            case "mute": return 100;
148
149            case "far light": return 12;
150            case "cab light": return 3;
151            case "ditch lights": return 48;
152            case "step lights": return 98;
153            case "tail lights": return 62;
154            case "switching lights": return 58;
155            case "dimmer": return 51;
156            case "interior lights": return 2;
157
158            case "air compressor": return 42;
159            case "air pump": return 45;
160            case "injector": return 60;
161            case "exhaust fan": return 108;
162            case "radiator fan": return 17;
163            case "steam generator": return 66;
164            case "blower": return 105;
165            case "blow down": return 56;
166            case "safety": return 38;
167            case "sanding": return 55;
168            case "ash dump": return 88;
169            case "shoveling": return 18;
170            case "water fill": return 35;
171
172            case "long whistle": return 103;
173            case "short whistle": return 64;
174            case "doppler horn": return 63;
175
176            case "curve squeal": return 36;
177            case "brake squeal": return 21;
178            case "announce": return 6;
179            case "cab chatter": return 27;
180
181            default: return 0;
182        }
183}
184
185    private final static Logger log = LoggerFactory.getLogger(TcsExportAction.class);
186}