@Override
  public String call() {

    log.info("OvenCheckThread started...");
    List<Integer> nodes = OvenState.getInstance().getNodes();
    ParamManager paramManager = new ParamManager();
    Config config = OvenState.getInstance().getConfig();

    // begin main thread loop to check oven com.heller.oven variables
    while (OvenState.getInstance().getOvenBoolVar("utility")) {

      // General Oven parameters needed in thread
      IoModule ioModule = OvenState.getInstance().getIoModule();
      double heaterOutputLimit = ioModule.getGeneralTwo().getHeaterOutputLimit();
      double cooldownTemp = ioModule.getGeneralTwo().getCooldown();

      // pause thread for specified amount
      try {
        Thread.sleep(OVEN_UPDATE_DELAY);
      } catch (InterruptedException e) {
        log.error("InterruptedException " + e.getMessage());
      }

      // get references from singleton that might change upon iteration
      List<ZoneState> zones = OvenState.getInstance().getZones();
      List<BeltState> beltStates = OvenState.getInstance().getBeltStates();
      List<RailState> railStates = OvenState.getInstance().getRailStates();
      SystemState state = OvenState.getInstance().getOvenSystemState();
      RecipeFlag recipeFlag = OvenState.getInstance().getRecipeFlag();

      // if recipe never loaded, then skip while loop
      if (OvenState.getInstance().getRecipeFlag() == RecipeFlag.NEVER_LOADED) continue;

      // If system is in COOLDOWN_DELAYED, test for boards in oven
      // If/when no boards in oven - start COOLDOWN and change System State
      if (state == SystemState.SYSTEM_STATE_RUN_COOLDOWN_DELAYED) {
        // if no boards in oven, change state to RUN_COOLDOWN
        if (!OvenManager.getInstance().isBoardsInOven()) {
          CanComm.sendSystemState(
              SystemState.SYSTEM_STATE_RUN_COOLDOWN, Can.OpId.OPER_SET.ordinal(), nodes);
        }

      } else if (state == SystemState.SYSTEM_STATE_RUN_COOLDOWN) {

        startCooldown(
            ioModule, paramManager, zones, beltStates, railStates, cooldownTemp, config, nodes);

      } else if (state.ordinal() >= LOWEST_RUN_STATE && state.ordinal() <= HIGHEST_RUN_STATE) {

        if (recipeFlag == RecipeFlag.RECIPE_UNLOADABLE) continue;
        if (recipeFlag == RecipeFlag.RECIPE_LOADING) {
          // white out heat zones on GUI
          for (ZoneState zone : zones) {
            zone.setTopColor(WHITE);
            if (zone.isHeatZone()) zone.setBottomColor(WHITE);
          }
          // white out belts on GUI
          for (BeltState beltState : beltStates) {
            beltState.setColor(WHITE);
          }
          // gray out rails on GUI
          for (RailState railState : railStates) {
            railState.setColor(WHITE);
          }
          // skip rest of loop until recipe is loaded.
          continue;
        }

        yellowFlag = false;
        // check rail position, set sysColor yellow if not ACTIVE within warning band
        checkRailPosition(railStates, state, nodes);
        // check belt speed, set sysColor yellow if belt speed out of warning band
        checkBeltSpeed(beltStates, state, nodes);
        // iterate through heaters and determine heater colors according to alarm/warning bands
        checkHeaterTemps(zones, heaterOutputLimit, state, nodes);
        // if any zone is red, Cooldown should have been called - skip rest of while loop
        if (redZone) {
          continue;
        }

        if (!yellowFlag) {
          // set light tower green and change to SYSTEM STATE RUN if in RUN PAUSE (with no warnings)
          // or RUN START
          if (state.equals(SystemState.SYSTEM_STATE_RUN_STARTUP)) {
            CanComm.sendSystemState(
                SystemState.SYSTEM_STATE_RUN, Can.OpId.OPER_SET.ordinal(), nodes);
            OvenState.getInstance().setRecipeFlag(RecipeFlag.RECIPE_RUNNING);
            CanComm.setLightTower(Equip.LightTowerState.EMPTY_OVEN.getTower());
          } else if (state.equals(SystemState.SYSTEM_STATE_RUN_PAUSE)) {
            if (OvenManager.getInstance().isAlarms() == -1
                || OvenManager.getInstance().isAlarms() == 2) {
              OvenManager.getInstance().clearAlarmState(true);
              CanComm.sendSystemState(
                  SystemState.SYSTEM_STATE_RUN, Can.OpId.OPER_SET.ordinal(), nodes);
              OvenState.getInstance().setRecipeFlag(RecipeFlag.RECIPE_RUNNING);
              CanComm.setLightTower(LightTowerState.PRODUCTS_IN_OVEN.getTower());
            }
          }
        }

        // pause thread for specified amount
        try {
          Thread.sleep(OVEN_UPDATE_DELAY);
        } catch (InterruptedException e) {
          log.error("InterruptedException " + e.getMessage());
        }
      }
    }
    log.info("OvenCheckThread closed...");
    return "thread_closed";
  }