001package jmri.jmrit.logixng.expressions; 002 003import java.beans.*; 004import java.io.*; 005import java.util.*; 006 007import jmri.*; 008import jmri.jmrit.logixng.*; 009import jmri.jmrit.logixng.util.*; 010import jmri.jmrit.logixng.util.parser.*; 011import jmri.util.TimerUtil; 012 013/** 014 * Check the status of battery and power supply. 015 * 016 * @author Daniel Bergqvist Copyright (C) 2023 017 */ 018public class ExpressionLinuxLinePower extends AbstractDigitalExpression 019 implements PropertyChangeListener { 020 021 private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is; 022 private ProtectedTimerTask _timerTask; 023 private final int _delay = 5; 024 private boolean _lastResult; 025 private JmriException _thrownException; 026 027 public ExpressionLinuxLinePower(String sys, String user) 028 throws BadUserNameException, BadSystemNameException { 029 super(sys, user); 030 031 try { 032 _lastResult = internalEvaluate(); 033 } catch (JmriException e) { 034 _thrownException = e; 035 } 036 } 037 038 @Override 039 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 040 DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class); 041 String sysName = systemNames.get(getSystemName()); 042 String userName = userNames.get(getSystemName()); 043 if (sysName == null) sysName = manager.getAutoSystemName(); 044 ExpressionLinuxLinePower copy = new ExpressionLinuxLinePower(sysName, userName); 045 copy.setComment(getComment()); 046 copy.set_Is_IsNot(_is_IsNot); 047 return manager.registerExpression(copy); 048 } 049 050 public void set_Is_IsNot(Is_IsNot_Enum is_IsNot) { 051 _is_IsNot = is_IsNot; 052 } 053 054 public Is_IsNot_Enum get_Is_IsNot() { 055 return _is_IsNot; 056 } 057 058 /** {@inheritDoc} */ 059 @Override 060 public Category getCategory() { 061 return Category.LINUX; 062 } 063 064 private List<String> getLinuxPowerSupplies() throws IOException, NoPowerSuppliesException { 065 List<String> powerSupplies = new ArrayList<>(); 066 067 Process process = Runtime.getRuntime().exec(new String[]{"upower","-e"}); 068 try (BufferedReader buffer = new BufferedReader(new InputStreamReader(process.getInputStream()))) { 069 String line; 070 while ((line = buffer.readLine()) != null) { 071 powerSupplies.add(line); 072 } 073 } 074 075// powerSupplies.clear(); // For testing only 076 077 if (powerSupplies.isEmpty()) throw new NoPowerSuppliesException(); 078 079 return powerSupplies; 080 } 081 082 public boolean isLinePowerOnline() throws IOException, JmriException { 083 boolean isPowerOnline = false; 084 085 for (String powerSupply : getLinuxPowerSupplies()) { 086 Process process = Runtime.getRuntime().exec(new String[]{"upower", "-i", powerSupply}); 087 try (BufferedReader buffer = new BufferedReader(new InputStreamReader(process.getInputStream()))) { 088 String line; 089 boolean linePowerFound = false; 090 while ((line = buffer.readLine()) != null) { 091 if (line.isBlank()) continue; 092 093 if (line.startsWith(" ")) { 094 line = line.substring(2); 095 } else { 096 throw new JmriException("Unknown string. It doesn't start with two spaces."); 097 } 098 099// System.out.format("'%s'%n", line); 100 101 if ("line-power".equals(line)) { 102// System.out.format("Line power found%n"); 103 linePowerFound = true; 104 } else if (line.startsWith(" ")) { 105// System.out.format("Spaces found%n"); 106 line = line.substring(2); 107 if (linePowerFound && line.startsWith("online:")) { 108// System.out.format("Online found%n"); 109 String[] parts = line.split("\\s+"); 110// System.out.format("Line: '%s', part0: '%s', part1: '%s'%n", line, parts[0], parts[1]); 111 if ("yes".equals(parts[1])) { 112 isPowerOnline = true; 113 } 114 } 115 } else { 116// System.out.format("Other: %s%n", line); 117 linePowerFound = false; 118 } 119 } 120 } 121 } 122 123 return isPowerOnline; 124 } 125 126 private boolean internalEvaluate() throws JmriException { 127 128 try { 129 if (_is_IsNot == Is_IsNot_Enum.Is) { 130 return isLinePowerOnline(); 131 } else { 132 return !isLinePowerOnline(); 133 } 134 } catch (java.io.IOException e) { 135 throw new JmriException("IO Exception: " + e.getMessage(), e); 136 } 137 } 138 139 /** {@inheritDoc} */ 140 @Override 141 public boolean evaluate() throws JmriException { 142 143 if (_thrownException != null) { 144 JmriException e = _thrownException; 145 _thrownException = null; 146 throw e; 147 } 148 149 // Check this every ?? seconds 150 return internalEvaluate(); 151 } 152 153 @Override 154 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 155 throw new UnsupportedOperationException("Not supported."); 156 } 157 158 @Override 159 public int getChildCount() { 160 return 0; 161 } 162 163 @Override 164 public String getShortDescription(Locale locale) { 165 return Bundle.getMessage(locale, "LinuxLinePower_Short"); 166 } 167 168 @Override 169 public String getLongDescription(Locale locale) { 170 return Bundle.getMessage(locale, "LinuxLinePower_Long", _is_IsNot.toString()); 171 } 172 173 /** {@inheritDoc} */ 174 @Override 175 public void setup() { 176 // Do nothing 177 } 178 179 /** {@inheritDoc} */ 180 @Override 181 public void registerListenersForThisClass() { 182 if (!_listenersAreRegistered) { 183 _timerTask = new ProtectedTimerTask() { 184 @Override 185 public void execute() { 186 try { 187 boolean _lastLastResult = _lastResult; 188 _lastResult = internalEvaluate(); 189 if (_lastResult != _lastLastResult) { 190 getConditionalNG().execute(); 191 } 192 } catch (JmriException e) { 193 _thrownException = e; 194 } 195 } 196 }; 197 198 TimerUtil.schedule(_timerTask, _delay*1000, _delay*1000); 199 _listenersAreRegistered = true; 200 } 201 } 202 203 /** {@inheritDoc} */ 204 @Override 205 public void unregisterListenersForThisClass() { 206 if (_listenersAreRegistered) { 207 _timerTask.cancel(); 208 _listenersAreRegistered = false; 209 } 210 } 211 212 /** {@inheritDoc} */ 213 @Override 214 public void propertyChange(PropertyChangeEvent evt) { 215 getConditionalNG().execute(); 216 } 217 218 /** {@inheritDoc} */ 219 @Override 220 public void disposeMe() { 221 } 222 223 224 public static class NoPowerSuppliesException extends JmriException { 225 226 /** 227 * Creates a new instance of <code>NoPowerSuppliesException</code>. 228 */ 229 public NoPowerSuppliesException() { 230 super(Bundle.getMessage("LinuxLinePower_NoPowerSuppliesException")); 231 } 232 } 233 234// private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionLinuxLinePower.class); 235 236}