001package jmri.jmrit.symbolicprog; 002 003import java.io.File; 004import java.io.FileInputStream; 005import java.io.InputStream; 006import java.io.IOException; 007import java.util.Properties; 008 009import jmri.jmrit.roster.RosterEntry; 010 011/** 012 * Import CV values from a TCS backup file (from a CDI backup) 013 * directly into a RosterEntry. 014 *<p> 015 * Note that this does not update any GUI that's showing the 016 * RosterEntry, e.g. a RosterPane or FunctionLabelPane. Those 017 * must have their updates triggered elsewhere, e.g. TcsImportAction. 018 * 019 * @author Alex Shepherd Copyright (C) 2003 (original Pr1Importer) 020 * *author Bob Jacobsen Copyright (C) 2023 021 */ 022public class TcsImporter { 023 024 Properties tcsProperties; 025 026 /** 027 * The import process starts upon creation of a TcsImporter 028 * @param file The File object to be read 029 * @param cvModel Model used to look up CV values (not used) 030 * @param model Variable model used to look up function bits (retained for later use) 031 * @throws IOException from underlying file operations 032 */ 033 public TcsImporter(File file, CvTableModel cvModel, VariableTableModel model) throws IOException { 034 this.model = model; 035 tcsProperties = new Properties(); 036 FileInputStream fileStream = new FileInputStream(file); 037 try { 038 tcsProperties.load(fileStream); 039 } finally { 040 fileStream.close(); 041 } 042 } 043 044 VariableTableModel model; 045 046 public TcsImporter(InputStream stream) throws IOException { 047 tcsProperties = new Properties(); 048 tcsProperties.load(stream); 049 } 050 051 public void setRosterEntry(RosterEntry rosterEntry) { 052 // TODO: How to handle name? Check for mismatch with ID? (Don't change ID?) 053 log.debug("found name {}", tcsProperties.get("Train.Name")); 054 // TODO: Confirm that address is correct? How to handle that? 055 log.debug("found address {}", tcsProperties.get("Train.Address")); 056 log.debug("found step {}", tcsProperties.get("Train.Speed")); // note key truncated as space 057 058 // TODO: move the following to a static method ot allow reuse from elsewhere 059 060 // copy User Description to comment 061 var userDescription = tcsProperties.get("Train.User").toString(); // note key truncated as space 062 // remove the "Description=" at front (due to space in key=value pair in input) 063 userDescription = userDescription.substring("Description=".length()); 064 rosterEntry.setComment(userDescription); 065 066 for (int i=0; i < 27; i++) { 067 var momentaryObj = tcsProperties.get("Train.Functions("+i+").Momentary"); 068 log.trace("Found Momentary {} as {}", i, momentaryObj); 069 if (momentaryObj == null) continue; // no change if null 070 var momentary = momentaryObj.toString(); 071 072 var displayObj = tcsProperties.get("Train.Functions("+i+").Display"); 073 log.trace("Found Display {} as {}", i, displayObj); 074 if (displayObj == null) continue; // no change if null 075 var display = displayObj.toString(); 076 077 var descriptionObj = tcsProperties.get("Train.Functions("+i+").Description"); 078 log.trace("Found Description {} as {}", i, descriptionObj); 079 if (descriptionObj == null) continue; // no change if null 080 var description = descriptionObj.toString(); 081 082 // Handle non-zero Display values by updating description value 083 description = unpackDescription(description, display); 084 085 // Here, we copy Description to the label 086 rosterEntry.setFunctionLabel(i+1, description); 087 // and momentary to the "locked" status 088 rosterEntry.setFunctionLockable(i+1, momentary.equals("1")); 089 090 // process consist bit 091 // first, see if function variable exists 092 var variable = model.findVar("Consist Address Active For F"+(i+1)); 093 if (variable != null) { 094 // it exists, so we can't ignore the consist info. 095 // retrieve it 096 var consistObj = tcsProperties.get("Train.Functions("+i+").Consist"); 097 log.debug("Found {} as \'{}\'", "Train.Functions("+i+").Consist", consistObj); 098 if (consistObj != null) { 099 if (consistObj.equals("Behavior=1")) { // "Current Cab Only" 100 variable.setIntValue(1); 101 } else { 102 variable.setIntValue(0); // respond to the consist address 103 } 104 log.trace("result is value {}", variable.getIntValue()); 105 } 106 } else { 107 log.debug("Variable {} not found", "Consist Address Active For F"+(i+1) ); 108 } 109 } 110 } 111 112 static String unpackDescription(String description, String display) { 113 // if there is a description value, that wins 114 if (!description.isEmpty()) return description; 115 116 // there must be a value in display, unpack it. 117 // We do string switch in case of non-numerically-parseable garbage 118 switch (display) { 119 120 case "0": return ""; 121 122 case "1": return "Headlight"; 123 case "13": return "Bell"; 124 case "14": return "Horn"; 125 case "15": return "Whistle"; 126 case "11": return "Pantograph"; 127 case "10": return "Smoke"; 128 case "4": return "Engine"; 129 case "74": return "Light"; 130 case "28": return "Coupler Clank"; 131 case "122": return "Couple"; 132 case "9": return "Uncouple"; 133 134 case "7": return "Shunting Mode"; 135 case "8": return "Momentum"; 136 137 case "57": return "Brake"; 138 case "200": return "Brake Release"; 139 case "41": return "Dynamic Brake"; 140 case "31": return "Manual Notch Down"; 141 case "30": return "Manual Notch Up"; 142 case "69": return "Reverser"; 143 case "100": return "Mute"; 144 145 case "12": return "Far Light"; 146 case "3": return "Cab Light"; 147 case "48": return "Ditch Lights"; 148 case "98": return "Step Lights"; 149 case "62": return "Tail Lights"; 150 case "58": return "Switching Lights"; 151 case "51": return "Dimmer"; 152 case "2": return "Interior Lights"; 153 154 case "42": return "Air Compressor"; 155 case "45": return "Air Pump"; 156 case "60": return "Injector"; 157 case "108": return "Exhaust Fan"; 158 case "17": return "Radiator Fan"; 159 case "66": return "Steam Generator"; 160 case "105": return "Blower"; 161 case "56": return "Blow Down"; 162 case "38": return "Safety"; 163 case "55": return "Sanding"; 164 case "88": return "Ash Dump"; 165 case "18": return "Shoveling"; 166 case "35": return "Water Fill"; 167 168 case "103": return "Long Whistle"; 169 case "64": return "Short Whistle"; 170 case "63": return "Doppler Horn"; 171 172 case "36": return "Curve Squeal"; 173 case "21": return "Brake Squeal"; 174 case "6": return "Announce"; 175 case "27": return "Cab Chatter"; 176 177 case "255": return "Unavailable_"; 178 179 default: return "<entry error \""+display+"\">"; 180 } 181 } 182 183 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TcsImporter.class); 184}