001package jmri.jmrix.powerline.dmx512; 002 003import jmri.jmrix.powerline.SerialSystemConnectionMemo; 004import jmri.jmrix.powerline.SerialTrafficController; 005 006import java.io.IOException; 007 008import jmri.jmrix.SerialPort; 009 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013/** 014 * Converts Stream-based I/O to/from messages. The "SerialInterface" side 015 * sends/receives message objects. 016 * <p> 017 * The connection to a SerialPortController is via a pair of *Streams, which 018 * then carry sequences of characters for transmission. Note that this 019 * processing is handled in an independent thread. 020 * 021 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2005, 2006, 2008 Converted to 022 * multiple connection 023 * @author Ken Cameron Copyright (C) 2023 024 */ 025public class SpecificTrafficController extends SerialTrafficController { 026 027 public SpecificTrafficController(SerialSystemConnectionMemo memo) { 028 super(); 029 this.memo = memo; 030 logDebug = log.isDebugEnabled(); 031 032 // not polled at all, so allow unexpected messages, and 033 // use poll delay just to spread out startup 034 setAllowUnexpectedReply(true); 035 mWaitBeforePoll = 1000; // can take a long time to send 036 } 037 038 private boolean oneTimeLog = true; 039 public byte[] dmxArray = new byte[513]; 040 private int intensitySteps = 255; 041 private SerialPort activePort = null; 042 043 /** 044 * set value in dmxArray 045 * @param unitId offset in dmxArray 046 * @param intensityValue value to put in dmxArray 047 * @return true when values ok 048 */ 049 public boolean setDmxIntensity(int unitId, byte intensityValue) { 050 if ((unitId > 0) && (unitId <= 512)) { 051 dmxArray[unitId] = intensityValue; 052 return(true); 053 } 054 return(false); 055 } 056 057 @Override 058 protected void transmitLoop() { 059 if (oneTimeLog) { 060 oneTimeLog = false; 061 for (int i = 0; i < dmxArray.length; i++) { 062 dmxArray[i] = 0; 063 } 064 dmxArray[0] = (byte) 0; // type of buffer going out 065 /** 066 * deal with thread sync of main tc still getting setup by time of 067 * first call in the transmit loop. Give it time and retry 068 */ 069 int tryLimit = 0; 070 while ((activePort == null) && (tryLimit <= 10)) { 071 try { 072 Thread.sleep(5000); 073 } catch (InterruptedException ignore) { 074 Thread.currentThread().interrupt(); 075 } 076 tryLimit++; 077 activePort = memo.getActiveSerialPort(); 078 if (activePort == null) { 079 log.info("try {} to get activePort", tryLimit); 080 } 081 } 082 } 083 084 // loop forever 085 while (!connectionError && !threadStopRequest) { 086 try { 087 if (ostream != null) { 088 // break should be for at least 176 uSec 089 if (activePort != null) { 090 //log.info("Start Break"); 091 activePort.setBreak(); 092 try { 093 Thread.sleep(10); 094 } catch (InterruptedException e) { 095 log.warn("transmitLoop did not expected to be interrupted during break"); 096 break; 097 } 098 activePort.clearBreak(); 099 //log.info("Break Sent"); 100 // wait at least 8 usec (not msec, usec) 101 try { 102 Thread.sleep(1); 103 } catch (InterruptedException e) { 104 log.warn("transmitLoop did not expected to be interrupted during clear"); 105 break; 106 } 107 } 108 /** 109 * send the buffer of data 110 */ 111 try { 112 ostream.write(dmxArray); 113 } catch (com.fazecast.jSerialComm.SerialPortTimeoutException ex) { 114 if (!threadStopRequest) { 115 log.warn("DMX512 write operation ended early"); 116 } 117 return; 118 } 119 /** 120 * wait 25 mSec, then repeat 121 */ 122 try { 123 Thread.sleep(25); 124 } catch (InterruptedException ignore) { 125 Thread.currentThread().interrupt(); 126 } 127 } else { 128 connectionError = true; 129 } 130 } catch (IOException | RuntimeException e) { 131 // TODO Currently there's no port recovery if an exception occurs 132 // must restart JMRI to clear xmtException. 133 xmtException = true; 134 portWarn(e); 135 } 136 } 137 } 138 139 // not used, no reback 140 @Override 141 public void receiveLoop() { 142 try { 143 Thread.sleep(10000); 144 } catch (InterruptedException ignore) { 145 Thread.currentThread().interrupt(); 146 } 147 } 148 149 /** 150 * This system provides 256 dim steps 151 */ 152 @Override 153 public int getNumberOfIntensitySteps() { 154 return intensitySteps; 155 } 156 157 /** 158 * Send a sequence of Dmx messages 159 * <p> 160 * Makes call to update array 161 */ 162 @Override 163 public boolean sendDmxSequence(int unitid, byte newStep) { 164 // log.info("Unit {} value {}", unitid, (int) newStep); 165 boolean didIt = setDmxIntensity(unitid, newStep); 166 if (!didIt) { 167 log.error("Invalid Dmx Message for unit {} value {}", unitid, newStep); 168 } 169 return didIt; 170 } 171 private final static Logger log = LoggerFactory.getLogger(SpecificTrafficController.class); 172 173} 174