001package jmri.jmrit.ussctc;
002
003import java.util.*;
004import jmri.*;
005/**
006 * Drive the code line communications on a USS CTC panel.
007 * <p>
008 * Primary interactions are with a group of {@link Station} objects
009 * that make up the panel.  Can also work with external
010 * hardware via Turnout/Sensor interfaces.
011 *
012 * @author Bob Jacobsen Copyright (C) 2007, 2017
013 */
014public class CodeLine {
015
016    /**
017     * Create and configure
018     *
019     * @param startIndicateTO  Name for turnout that starts indication operations on the layout
020     * @param startSendTO  Name for turnout that starts send operations on the layout
021     * @param output1TO  Turnout name for 1st channel of code information
022     * @param output2TO  Turnout name for 2nd channel of code information
023     * @param output3TO  Turnout name for 3rd channel of code information
024     * @param output4TO  Turnout name for 4th channel of code information
025     */
026    public CodeLine(String startIndicateTO, String startSendTO, String output1TO, String output2TO, String output3TO, String output4TO) {
027        NamedBeanHandleManager hm = InstanceManager.getDefault(NamedBeanHandleManager.class);
028        TurnoutManager tm = InstanceManager.getDefault(TurnoutManager.class);
029
030        logMemory = InstanceManager.getDefault(MemoryManager.class).provideMemory(
031                        Constants.commonNamePrefix+"CODELINE"+Constants.commonNameSuffix+"LOG");  // NOI18N
032        log.debug("log memory name is {}", logMemory.getSystemName());  // NOI18N
033
034        hStartIndicateTO = hm.getNamedBeanHandle(startIndicateTO, tm.provideTurnout(startIndicateTO));
035        hStartSendTO = hm.getNamedBeanHandle(startSendTO, tm.provideTurnout(startSendTO));
036
037        hOutput1TO = hm.getNamedBeanHandle(output1TO, tm.provideTurnout(output1TO));
038        hOutput2TO = hm.getNamedBeanHandle(output2TO, tm.provideTurnout(output2TO));
039        hOutput3TO = hm.getNamedBeanHandle(output3TO, tm.provideTurnout(output3TO));
040        hOutput4TO = hm.getNamedBeanHandle(output4TO, tm.provideTurnout(output4TO));
041    }
042
043    final Memory logMemory;
044
045    final NamedBeanHandle<Turnout> hStartIndicateTO;
046    final NamedBeanHandle<Turnout> hStartSendTO;
047
048    final NamedBeanHandle<Turnout> hOutput1TO;
049    final NamedBeanHandle<Turnout> hOutput2TO;
050    final NamedBeanHandle<Turnout> hOutput3TO;
051    final NamedBeanHandle<Turnout> hOutput4TO;
052
053    public static int START_PULSE_LENGTH = 500; // mSec
054    public static int CODE_SEND_DELAY = 2500; // mSec
055    public static int INTER_INDICATION_DELAY = 500; // mSec
056
057    volatile Deque<Station<?,?>> codeQueue = new ArrayDeque<>();
058    volatile Deque<Station<?,?>> indicationQueue = new ArrayDeque<>();
059
060    volatile boolean active = false;
061
062    synchronized void endAndCheckNext() {
063        if (!active) log.error("endAndCheckNext with active false");
064        log.debug("active set false");
065        active = false;
066        checkForWork();
067    }
068
069    synchronized void checkForWork() {
070        log.debug("checkForWork with active == {}", active);
071        if (active) return;
072        log.debug("active set true");
073        active = true;
074
075        // indications have priority over code sends
076        final Station<?,?> indicatorStation = indicationQueue.pollFirst();
077        if (indicatorStation != null) {
078            // go inactive for just a bit before starting indication cycles
079            jmri.util.ThreadingUtil.runOnGUIDelayed( ()->{
080                    startSendIndication(indicatorStation);
081                }, INTER_INDICATION_DELAY);
082            return;
083        }
084        Station<?,?> codeStation = codeQueue.pollFirst();
085        if (codeStation != null) {
086            startSendCode(codeStation);
087            return;
088        }
089        log.debug("active set false");
090        active = false;
091        logMemory.setValue("");
092        log.debug("CodeLine goes inactive");  // NOI18N
093    }
094
095    /**
096     * Request processing of an indication from the field
097     * @param station Station being addressed.
098     */
099    synchronized void requestSendCode(Station<?,?> station) {
100        log.debug("requestSendCode queued from Station {}", station.getName());
101        // remove if present
102        while (codeQueue.contains(station)) {
103            codeQueue.remove(station);
104            log.debug("     removed previous request");
105        }
106        codeQueue.addLast(station);
107        checkForWork();
108    }
109
110    void startSendCode(Station<?,?> station) {
111        final Station<?,?> s = station;
112        log.debug("CodeLine startSendCode - Tell hardware to start sending code");  // NOI18N
113        logMemory.setValue("Sending Code: Station "+station.getName());  // NOI18N
114        startSendExternalCodeLine();
115
116        // Wait time for sequence complete, then proceed to end of code send
117        // ToDo: Allow an input to end this too
118        jmri.util.ThreadingUtil.runOnGUIDelayed( ()->{
119                    s.codeValueDelivered();
120                    // and see if anything else needs to be done
121                    log.debug("end of codeValueDelivered");  // NOI18N
122                    endAndCheckNext();
123                }, CODE_SEND_DELAY);
124    }
125
126    void startSendExternalCodeLine() {
127        hStartSendTO.getBean().setCommandedState(Turnout.THROWN);
128        jmri.util.TimerUtil.schedule(new TimerTask() {
129            @Override
130            public void run() {
131                hStartSendTO.getBean().setCommandedState(Turnout.CLOSED);
132            }
133        }, START_PULSE_LENGTH);
134    }
135
136    @Override
137    public String toString() {
138        return "Codeline "+active;
139    }
140    /**
141     * Request processing of an indication from the field.
142     * @param station Station being addressed.
143     */
144    synchronized void requestIndicationStart(Station<?,?> station) {
145        log.debug("requestIndicationStart queued from Station {}", station.getName());
146        // remove if present
147        while (indicationQueue.contains(station)) {
148            indicationQueue.remove(station);
149            log.debug("     removed previous request");
150        }
151        indicationQueue.addLast(station);
152        checkForWork();
153    }
154
155    void startSendIndication(Station<?,?> station) {
156        final Station<?,?> s = station;
157        log.debug("CodeLine startSendIndication - process indication from field");
158
159        // light code light and gather values
160        station.indicationStart();
161
162        log.debug("Tell hardware to start sending indication");
163        logMemory.setValue("Receiving Indication: Station "+station.getName());  // NOI18N
164        startIndicationExternalCodeLine();
165
166        // Wait time for sequence complete, then proceed to end of indication send
167        // ToDo: Allow an input to end this too
168        jmri.util.ThreadingUtil.runOnGUIDelayed( ()->{
169                    log.debug("hardware delay done, receiving indication");
170                    s.indicationComplete();
171                    log.debug("end of indicationComplete");
172                    // and see if anything else needs to be done
173                    endAndCheckNext();
174                }, CODE_SEND_DELAY);
175    }
176
177    void startIndicationExternalCodeLine() {
178        hStartIndicateTO.getBean().setCommandedState(Turnout.THROWN);
179        jmri.util.TimerUtil.schedule(new TimerTask() {
180            @Override
181            public void run() {
182                hStartIndicateTO.getBean().setCommandedState(Turnout.CLOSED);
183            }
184        }, START_PULSE_LENGTH);
185    }
186
187
188    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CodeLine.class);
189}