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}