001package jmri.jmrix.loconet.pr2; 002 003import jmri.DccLocoAddress; 004import jmri.InstanceManager; 005import jmri.JmriException; 006import jmri.jmrix.loconet.LnConstants; 007import jmri.jmrix.loconet.LnOpsModeProgrammer; 008import jmri.jmrix.loconet.LnPowerManager; 009import jmri.jmrix.loconet.LnPr2ThrottleManager; 010import jmri.jmrix.loconet.LnTrafficController; 011import jmri.jmrix.loconet.LocoNetMessage; 012import jmri.jmrix.loconet.LocoNetSystemConnectionMemo; 013 014/** 015 * PowerManager implementation for controlling layout power via PR2. 016 * <p> 017 * Some of the message formats used in this class are Copyright Digitrax, Inc. 018 * and used with permission as part of the JMRI project. That permission does 019 * not extend to uses in other software products. If you wish to use this code, 020 * algorithm or these message formats outside of JMRI, please contact Digitrax 021 * Inc for separate permission. 022 * 023 * @author Bob Jacobsen Copyright (C) 2001 024 */ 025public class LnPr2PowerManager extends LnPowerManager { 026 027 public LnPr2PowerManager(LocoNetSystemConnectionMemo memo) { 028 super(memo); 029 this.tc = memo.getLnTrafficController(); 030 } 031 032 LnTrafficController tc; 033 034 @Override 035 public void setPower(int v) throws JmriException { 036 int old = power; 037 power = UNKNOWN; 038 039 // Instead of GPON/GPOFF, PR2 uses ops-mode writes to CV 128 for control 040 if (v == ON) { 041 // get current active address 042 DccLocoAddress activeAddress = ((LnPr2ThrottleManager) InstanceManager.throttleManagerInstance()).getActiveAddress(); 043 if (activeAddress != null) { 044 pm = new LnOpsModeProgrammer(memo, activeAddress.getNumber(), activeAddress.isLongAddress()); 045 checkOpsProg(); 046 047 // set bit 1 in CV 128 048 pm.writeCV("128", 1, null); 049 power = ON; 050 firePowerPropertyChange(old, power); 051 // start making sure that the power is refreshed 052 if (timer == null) { 053 timer = new javax.swing.Timer(2 * 1000, e -> refresh()); 054 timer.setInitialDelay(2 * 1000); 055 timer.setRepeats(true); // in case we run by 056 } 057 timer.start(); 058 } 059 } else if (v == OFF) { 060 if (timer != null) { 061 timer.stop(); 062 } 063 064 // get current active address 065 DccLocoAddress activeAddress = ((LnPr2ThrottleManager) InstanceManager.throttleManagerInstance()).getActiveAddress(); 066 if (activeAddress != null) { 067 pm = new LnOpsModeProgrammer(memo, activeAddress.getNumber(), activeAddress.isLongAddress()); 068 checkOpsProg(); 069 070 // reset bit 1 in CV 128 071 pm.writeCV("128", 0, null); 072 power = OFF; 073 } 074 } 075 // notify of change 076 firePowerPropertyChange(old, power); 077 } 078 079 void refresh() { 080 // send inquiry message to keep power alive 081 LocoNetMessage msg = new LocoNetMessage(2); 082 msg.setOpCode(LnConstants.OPC_GPBUSY); 083 tc.sendLocoNetMessage(msg); 084 } 085 086 LnOpsModeProgrammer pm = null; 087 088 private void checkOpsProg() throws JmriException { 089 if (pm == null) { 090 throw new JmriException("Use PR2 power manager after dispose"); // NOI18N 091 } 092 } 093 094 // to listen for status changes from LocoNet 095 @Override 096 public void message(LocoNetMessage m) { 097 int old = power; 098 if (m.getOpCode() == LnConstants.OPC_GPON) { 099 power = ON; 100 } else if (m.getOpCode() == LnConstants.OPC_GPOFF) { 101 power = OFF; 102 if (timer != null) { 103 // Protect against uninitialized timer, for case where some other 104 // LocoNet agent issues OPC_GPOFF before JMRI initializes its timer. 105 // A NPE was seen, before protected added, with the DCS52. 106 timer.stop(); 107 } 108 } else if (m.getOpCode() == LnConstants.OPC_WR_SL_DATA) { 109 // if this is a service mode write, drop out of power on mode 110 if ((m.getElement(1) == 0x0E) 111 && (m.getElement(2) == 0x7C) 112 && ((m.getElement(3) & 0x04) == 0x00)) { 113 // go to power off due to service mode op 114 if (power == ON) { 115 power = OFF; 116 if (timer != null) { 117 timer.stop(); 118 } 119 } 120 } 121 } else if ( // check for status showing going off 122 (m.getOpCode() == LnConstants.OPC_PEER_XFER) 123 && (m.getElement(1) == 0x10) 124 && (m.getElement(2) == 0x22) 125 && (m.getElement(3) == 0x22) 126 && (m.getElement(4) == 0x01)) { // PR2 form 127 int[] data = m.getPeerXfrData(); 128 if ((data[2] & 0x40) != 0x40) { 129 // dropped off 130 if (power == ON) { 131 power = OFF; 132 if (timer != null) { 133 timer.stop(); 134 } 135 } 136 } 137 } 138 firePowerPropertyChange(old, power); 139 } 140 141 /** 142 * Returns false to indicate PR2 does not implement an "IDLE" power state. 143 * @return false 144 */ 145 @Override 146 public boolean implementsIdle() { 147 return false; 148 } 149 150 // timer support to send updates & keep power alive 151 javax.swing.Timer timer = null; 152} 153