Background information of the logic used to control simple signals
This page describes the logic used by the JMRI Simple Signal panel to control signals.
We display the actual code, so there's no ambiguity about what it's doing. This is from JMRI test release 2.9.1.
This signal protects one end of a straight through block, with no signaled turnouts.
    void doSingleBlock() {
        int appearance = SignalHead.GREEN;
        int oldAppearance = ((SignalHead)outputs[0]).getAppearance();
        // check for yellow, flashing yellow overriding green
        if (protectWithFlashing && fastestColor1()==SignalHead.YELLOW)
            appearance = SignalHead.FLASHYELLOW;
        if (fastestColor1()==SignalHead.RED || fastestColor1()==SignalHead.FLASHRED)
            appearance = SignalHead.YELLOW;
        // if distant signal, show exactly what the home signal does
        if (distantSignal)
            appearance = fastestColor1();
        // if limited speed and green, reduce to yellow
        if (limitSpeed1)
            appearance = slowerOf(appearance, SignalHead.YELLOW);
        // check for red overriding yellow or green
        if (watchSensor1!=null && watchSensor1.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor2!=null && watchSensor2.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor3!=null && watchSensor3.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor4!=null && watchSensor4.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        // check if signal if held, forcing a red aspect by this calculation
        if (((SignalHead)outputs[0]).getHeld())
            appearance = SignalHead.RED;
        // handle approach lighting
        doApproach();
        // show result if changed
        if (appearance != oldAppearance) {
            ((SignalHead)outputs[0]).setAppearance(appearance);
            if (log.isDebugEnabled()) log.debug("Change appearance of "+name+" to "+appearance);
        }
    }
        This signal is along the main route through a turnout, which is defined as the direction taken by trains when the turnout is closed. It's protecting the frog of the turnout so that trains will stop before running through a turnout set against them.
    void doTrailingMain() {
        int appearance = SignalHead.GREEN;
        int oldAppearance = ((SignalHead)outputs[0]).getAppearance();
        // check for yellow, flashing yellow overriding green
        if (protectWithFlashing && fastestColor1()==SignalHead.YELLOW)
            appearance = SignalHead.FLASHYELLOW;
        if (fastestColor1()==SignalHead.RED || fastestColor1()==SignalHead.FLASHRED)
            appearance = SignalHead.YELLOW;
        // if distant signal, show exactly what the home signal does
        if (distantSignal)
            appearance = fastestColor1();
        // if limited speed and green, reduce to yellow
        if (limitSpeed1)
            appearance = slowerOf(appearance, SignalHead.YELLOW);
        // check for red overriding yellow or green
        if (watchSensor1!=null && watchSensor1.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor2!=null && watchSensor2.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor3!=null && watchSensor3.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor4!=null && watchSensor4.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchTurnout!=null && watchTurnout.getBean().getKnownState() != Turnout.CLOSED)
            appearance = SignalHead.RED;
        if (watchTurnout!=null && watchTurnout.getBean().getCommandedState() != Turnout.CLOSED)
            appearance = SignalHead.RED;
        // check if signal if held, forcing a red aspect by this calculation
        if (((SignalHead)outputs[0]).getHeld())
            appearance = SignalHead.RED;
        // handle approach lighting
        doApproach();
        // show result if changed
        if (appearance != oldAppearance) {
            ((SignalHead)outputs[0]).setAppearance(appearance);
            log.debug("Change appearance of {} to {}", name, appearance);
        }
    }
        This signal is along the diverging route through a turnout, which is defined as the direction taken by trains when the turnout is set to "thrown". It's protecting the frog of the turnout so that trains will stop before running through a turnout set against them.
    void doTrailingDiverging() {
        int appearance = SignalHead.GREEN;
        int oldAppearance = ((SignalHead)outputs[0]).getAppearance();
        // check for yellow, flashing yellow overriding green
        if (protectWithFlashing && fastestColor1()==SignalHead.YELLOW)
            appearance = SignalHead.FLASHYELLOW;
        if (fastestColor1()==SignalHead.RED || fastestColor1()==SignalHead.FLASHRED)
            appearance = SignalHead.YELLOW;
        // if distant signal, show exactly what the home signal does
        if (distantSignal)
            appearance = fastestColor1();
        // if limited speed and green, reduce to yellow
        if (limitSpeed2)
            appearance = slowerOf(appearance, SignalHead.YELLOW);
        // check for red overriding yellow or green
        if (watchSensor1!=null && watchSensor1.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor2!=null && watchSensor2.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor3!=null && watchSensor3.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor4!=null && watchSensor4.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchTurnout!=null && watchTurnout.getBean().getKnownState() != Turnout.THROWN)
            appearance = SignalHead.RED;
        if (watchTurnout!=null && watchTurnout.getBean().getCommandedState() != Turnout.THROWN)
            appearance = SignalHead.RED;
        // check if signal if held, forcing a red aspect by this calculation
        if (((SignalHead)outputs[0]).getHeld())
            appearance = SignalHead.RED;
        // handle approach lighting
        doApproach();
        // show result if changed
        if (appearance != oldAppearance) {
            ((SignalHead)outputs[0]).setAppearance(appearance);
            if (log.isDebugEnabled()) log.debug("Change appearance of "+name+" to "+appearance);
        }
    }
        This signal is protecting the points-end of a turnout. Depending on whether the turnout is thrown or closed, the train will take two different routes, and the signal will protect different next blocks.
    void doFacing() {
        int appearance = SignalHead.GREEN;
        int oldAppearance = ((SignalHead)outputs[0]).getAppearance();
        // find downstream appearance, being pessimistic if we're not sure of the state
        int s = SignalHead.GREEN;
        if (watchTurnout!=null && watchTurnout.getBean().getKnownState() != Turnout.THROWN)
            s = slowerOf(s, fastestColor1());
        if (watchTurnout!=null && watchTurnout.getBean().getKnownState() != Turnout.CLOSED)
            s = slowerOf(s, fastestColor2());
        // check for yellow, flashing yellow overriding green
        if (protectWithFlashing && s==SignalHead.YELLOW)
            appearance = SignalHead.FLASHYELLOW;
        if (s==SignalHead.RED  || s==SignalHead.FLASHRED)
            appearance = SignalHead.YELLOW;
        // if distant signal, show exactly what the home signal does
        if (distantSignal)
            appearance = s;
        // if limited speed and green or flashing yellow, reduce to yellow
        if (watchTurnout!=null && limitSpeed1 && watchTurnout.getBean().getKnownState()!=Turnout.THROWN)
            appearance = slowerOf(appearance, SignalHead.YELLOW);
        if (watchTurnout!=null && limitSpeed2 && watchTurnout.getBean().getKnownState()!=Turnout.CLOSED)
            appearance = slowerOf(appearance, SignalHead.YELLOW);
        // check for red overriding yellow or green
        if (watchSensor1!=null && watchSensor1.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor2!=null && watchSensor2.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor3!=null && watchSensor3.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor4!=null && watchSensor4.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if ((watchTurnout!=null && watchTurnout.getBean().getKnownState() == Turnout.CLOSED)
                && ((watchedSensor1!=null && watchedSensor1.getBean().getKnownState() != Sensor.INACTIVE)))
            appearance = SignalHead.RED;
        if ((watchTurnout!=null && watchTurnout.getBean().getKnownState() == Turnout.CLOSED) && ((watchedSensor1Alt!=null && watchedSensor1Alt.getBean().getKnownState() != Sensor.INACTIVE)))
            appearance = SignalHead.RED;
        if ((watchTurnout!=null && watchTurnout.getBean().getKnownState() == Turnout.THROWN) && ((watchedSensor2!=null && watchedSensor2.getBean().getKnownState() != Sensor.INACTIVE)))
            appearance = SignalHead.RED;
        if ((watchTurnout!=null && watchTurnout.getBean().getKnownState() == Turnout.THROWN) && ((watchedSensor2Alt!=null && watchedSensor2Alt.getBean().getKnownState() != Sensor.INACTIVE)))
            appearance = SignalHead.RED;
        // check if turnout in motion, if so force red
        if (watchTurnout!=null && (watchTurnout.getBean().getKnownState() != watchTurnout.getBean().getCommandedState()) )
            appearance = SignalHead.RED;
        if (watchTurnout!=null && (watchTurnout.getBean().getKnownState() != Turnout.THROWN) && (watchTurnout.getBean().getKnownState() != Turnout.CLOSED) )  // checking for other states
            appearance = SignalHead.RED;
        // check if signal if held, forcing a red aspect by this calculation
        if (((SignalHead)outputs[0]).getHeld())
            appearance = SignalHead.RED;
        // handle approach lighting
        doApproach();
        // show result if changed
        if (appearance != oldAppearance)
            ((SignalHead)outputs[0]).setAppearance(appearance);
    }