001package jmri.jmrix.maple;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Utility Class supporting output to Maple HMI's
008 * <p>
009 * All of the Maple HMI panels receive the same output bits. This keeps them
010 * synchronized. Output is sent in "broadcast" mode to Station Address 0. All
011 * HMI's receive the output. Output is sent at the end of each polling cycle,
012 * whether or not anything has changed. That way, if an HMI panel is plugged in,
013 * it will be up-to-date within one polling cycle. Serial systems with unique
014 * output bits for each node keep their output array in each node. That code has
015 * been moved to this utility class for Maple Systems because all nodes share
016 * the same output bits. Coil bits within Maple Systems HMI's are divided into
017 * input (1-1000) and output (1001-9000), so input bits are read starting from
018 * HMI address 1, and output bits are written starting at HMI address 1001.
019 *
020 * @author Dave Duchamp, Copyright (C) 2009
021 */
022public class OutputBits {
023
024    public OutputBits(SerialTrafficController _tc) {
025        // clear all output bits
026        for (int i = 0; i < 256; i++) {
027            outputArray[i] = 0;
028        }
029    }
030
031    // operational variables
032// private int mPulseWidth = 500;
033    private static int mSendDelay = 200;
034    private static int mNumOutputBits = 98;
035    protected byte[] outputArray = new byte[256]; // current values of the output bits
036
037    // access routines
038    public static void setNumOutputBits(int n) {
039        mNumOutputBits = n;
040    }
041
042    public static int getNumOutputBits() {
043        return mNumOutputBits;
044    }
045// public int getPulseWidth() {return (mPulseWidth);}
046// public void setPulseWidth(int width) {mPulseWidth = width;}
047
048    public static void setSendDelay(int n) {
049        mSendDelay = n;
050    }
051
052    public static int getSendDelay() {
053        return mSendDelay;
054    }
055
056    /**
057     * Set an output bit.
058     * <p>
059     * Note: state = 'true' for 0, 'false' for 1.
060     * Bits are numbered from 1 (not 0)
061     * @param bitNumber bit index number starting from 1.
062     * @param state true for 0, false for 1.
063     */
064    public void setOutputBit(int bitNumber, boolean state) {
065        // validate that this bitNumber is defined
066        if (bitNumber > mNumOutputBits) {
067            log.warn("Output bit out-of-range for configured bits");
068            return;
069        }
070        // locate in the outputArray
071        int byteNumber = (bitNumber - 1) / 8;
072        // update the byte
073        byte bit = (byte) (1 << ((bitNumber - 1) % 8));
074        if (state) {
075            outputArray[byteNumber] &= (~bit);
076        } else {
077            outputArray[byteNumber] |= bit;
078        }
079    }
080
081    /**
082     * Get the current state of an output bit.
083     * <p>
084     * Bits are numbered from 1 (not 0).
085     *
086     * @param bitNumber bit number to check, index starts at 1.
087     * @return 'true' for 0, 'false' for 1
088     */
089    public boolean getOutputBit(int bitNumber) {
090        // locate in the outputArray
091        int byteNumber = (bitNumber - 1) / 8;
092        // validate that this byte number is defined
093        if (byteNumber >= 256) {
094            byteNumber = 255;
095        }
096        // update the byte
097        byte bit = (byte) (1 << ((bitNumber - 1) % 8));
098        byte testByte = outputArray[byteNumber];
099        testByte &= bit;
100        if (testByte == 0) {
101            return (true);
102        } else {
103            return (false);
104        }
105    }
106
107    /**
108     * Create a Transmit packet (SerialMessage).
109     * @param startBitNum start bit number.
110     * @param endBitNum end bit number.
111     * @return serial message.
112     */
113    public SerialMessage createOutPacket(int startBitNum, int endBitNum) {
114        int nBits = endBitNum - startBitNum + 1;
115        if (nBits > 99) {
116            log.error("Number of bits for this packet greater than 99 - {}", nBits);
117            return null;
118        }
119        int sAdd = 1000 + startBitNum;  // write starting at 1001 for output 1 in HMI's  
120        // create message
121        SerialMessage m = new SerialMessage(14 + nBits);
122        m.setElement(0, 02);
123        m.setElement(1, '0');  // send in broadcast mode so all devices update      
124        m.setElement(2, '0');
125        m.setElement(3, 'W');
126        m.setElement(4, 'C');
127        m.setElement(5, '0' + (sAdd / 1000));
128        m.setElement(6, '0' + ((sAdd - ((sAdd / 1000) * 1000)) / 100));
129        m.setElement(7, '0' + ((sAdd - ((sAdd / 100) * 100)) / 10));
130        m.setElement(8, '0' + (sAdd - ((sAdd / 10) * 10)));
131        m.setElement(9, '0' + (nBits / 10));
132        m.setElement(10, '0' + (nBits - ((nBits / 10) * 10)));
133        for (int i = 0; i < nBits; i++) {
134            int j = i - ((i / 8) * 8);
135            int val = outputArray[i / 8];
136            if (j == 0) {
137                m.setElement(11 + i, ((val & 0x01) != 0) ? '1' : '0');
138            } else if (j == 1) {
139                m.setElement(11 + i, ((val & 0x02) != 0) ? '1' : '0');
140            } else if (j == 2) {
141                m.setElement(11 + i, ((val & 0x04) != 0) ? '1' : '0');
142            } else if (j == 3) {
143                m.setElement(11 + i, ((val & 0x08) != 0) ? '1' : '0');
144            } else if (j == 4) {
145                m.setElement(11 + i, ((val & 0x10) != 0) ? '1' : '0');
146            } else if (j == 5) {
147                m.setElement(11 + i, ((val & 0x20) != 0) ? '1' : '0');
148            } else if (j == 6) {
149                m.setElement(11 + i, ((val & 0x40) != 0) ? '1' : '0');
150            } else if (j == 7) {
151                m.setElement(11 + i, ((val & 0x80) != 0) ? '1' : '0');
152            }
153        }
154        m.setElement(11 + nBits, 03);
155        m.setChecksum(12 + nBits);
156        m.setTimeout(mSendDelay);
157        m.setNoReply();
158        return m;
159    }
160
161    private final static Logger log = LoggerFactory.getLogger(OutputBits.class);
162
163}