package com.sb.elsinore; import com.sb.elsinore.devices.I2CDevice; import com.sb.util.MathUtil; import javax.annotation.Nonnull; import jGPIO.GPIO.Direction; import jGPIO.InPin; import jGPIO.InvalidGPIOException; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.text.DecimalFormat; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.owfs.jowfsclient.OwfsException; /** * Temp class is used to monitor temperature on the one wire system. * @author Doug Edey * */ public final class Temp implements Runnable, Comparable<Temp> { /** * Strings for the Nodes. */ public static final String PROBE_SIZE = "ProbeSize"; public static final String PROBE_ELEMENT = "probe"; public static final String POSITION = "position"; /** * Valid sizes for the probes */ public static int SIZE_SMALL = 0; public static int SIZE_MEDIUM = 1; public static int SIZE_LARGE = 2; /** * Magic numbers. * F_TO_C_MULT: Multiplier to convert F to C. * C_TO_F_MULT: Multiplier to convert C to F. * FREEZING: The freezing point of Fahrenheit. */ public MathContext context = new MathContext(2, RoundingMode.HALF_DOWN); public static BigDecimal FREEZING = new BigDecimal(32); public static BigDecimal ERROR_TEMP = new BigDecimal(-999); private boolean badTemp = false; private boolean keepalive = true; private boolean hidden = false; public boolean cutoffEnabled = false; /** * Base path for BBB System Temp. */ private final String bbbSystemTemp = "/sys/class/hwmon/hwmon0/device/temp1_input"; /** * Base path for RPi System Temp. */ private final String rpiSystemTemp = "/sys/class/thermal/thermal_zone0/temp"; /** * Match the temperature regexp. */ private final Pattern tempRegexp = Pattern.compile("^(-?)([0-9]+)(.[0-9]{1,2})?$"); private int size = SIZE_LARGE; public I2CDevice i2cDevice = null; public int i2cChannel = -1; /** * Save the current object to the configuration using LaunchControl. */ public void save() { if (name != null && !name.equals("")) { LaunchControl.addTempToConfig(this); } } /** * Standard constructor. * @param name The probe name or input name. * Use "system" to create a system temperature probe * @param inProbe The address of this temperature probe. */ public Temp(String name, String inProbe) { String aName = inProbe; BrewServer.LOG.info("Adding" + aName); if (name.equalsIgnoreCase("system")) { aName = "System"; File tempFile = new File(rpiSystemTemp); if (tempFile.exists()) { fProbe = rpiSystemTemp; } else { tempFile = new File(bbbSystemTemp); if (tempFile.exists()) { fProbe = bbbSystemTemp; } else { BrewServer.LOG.warning( "Couldn't find a valid system temperature probe"); return; } } } else if (name.equalsIgnoreCase("Blank")){ // This is a special case for no temp probe. fProbe = null; } else { fProbe = "/sys/bus/w1/devices/" + aName + "/w1_slave"; File probePath = new File(fProbe); // Lets assume that OWFS has "." separated names if (!probePath.exists() && aName.contains(".")) { String[] newAddress = aName.split("\\.|-"); if (newAddress.length == 2) { String devFamily = newAddress[0]; String devAddress = ""; // Byte swap! devAddress += newAddress[1].subSequence(10, 12); devAddress += newAddress[1].subSequence(8, 10); devAddress += newAddress[1].subSequence(6, 8); devAddress += newAddress[1].subSequence(4, 6); devAddress += newAddress[1].subSequence(2, 4); devAddress += newAddress[1].subSequence(0, 2); String fixedAddress = devFamily + "-" + devAddress.toLowerCase(); BrewServer.LOG.info("Converted address: " + fixedAddress); this.fProbe = "/sys/bus/w1/devices/" + fixedAddress + "/w1_slave"; probePath = new File(fProbe); if (!probePath.exists()) { // Try OWFS fProbe = null; } } } } this.probeName = aName; this.name = name; BrewServer.LOG.info(this.probeName + " added."); } /** * The Runnable loop. */ public void run() { while (keepalive) { if (updateTemp().equals(ERROR_TEMP)) { if (fProbe != null && fProbe.equals( "/sys/class/thermal/thermal_zone0/temp")) { return; } // Uh(oh no file found, disable output to prevent logging floods loggingOn = false; } else { loggingOn = true; } if (volumeMeasurement) { updateVolume(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * @param n The name to set this Temp to. */ public void setName(final String n) { this.name = n; } /** * @return The name of this probe */ public String getName() { return name.replaceAll(" ", "_"); } /** * @return The address of this probe */ public String getProbe() { return probeName; } /****** * Method to take a cutoff value, parse it to the correct scale * and then update the cutoffTemp. * @param cutoffInput String describing the temperature */ public void setCutoffTemp(final String cutoffInput) { Matcher tempMatcher = tempRegexp.matcher(cutoffInput); if (tempMatcher.find()) { // We have matched against the TEMP_REGEXP String number = tempMatcher.group(1); if (number == null) { number = "+"; } // Get the integer if (tempMatcher.group(2) != null) { number += tempMatcher.group(2); } // Do we have a decimal? if (tempMatcher.group(3) != null) { number += tempMatcher.group(3); } // Create the temp this.cutoffTemp= new BigDecimal(number); } else { BrewServer.LOG.severe(cutoffTemp + " doesn't match " + tempRegexp.pattern()); } } // PRIVATE //// /** * Setup strings for the probe. */ private String fProbe, name, probeName; /** * Turn on and off logging. */ private boolean loggingOn = true; /** * Hold the current error string. */ public String currentError = null; /** * The current temp. */ private BigDecimal currentTemp = new BigDecimal(0), currentVolume = new BigDecimal(0), cutoffTemp = new BigDecimal(-999.0), volumeConstant = new BigDecimal(0), volumeMultiplier = new BigDecimal(0.0), gravity = new BigDecimal(1.000); /** * The current timestamp. */ private long currentTime = 0; /** * Other strings, obviously named. */ private String scale = "C", volumeAddress = "", volumeOffset = "", volumeUnit = ""; /** * Are we measuring volume? */ private boolean volumeMeasurement = false; /** * Volume analog input number. */ private int volumeAIN = -1; /** * The baselist of volume measurements. */ private ConcurrentHashMap<BigDecimal, BigDecimal> volumeBase = null; /** * The input pin to read. */ private InPin volumePin = null; private boolean stopVolumeLogging; private BigDecimal calibration = BigDecimal.ZERO; private TriggerControl triggerControl = null; private int position = -1; /** * @return Get the current temperature */ public BigDecimal getTemp() { // set up the reader if (scale.equals("F")) { return getTempF(); } return getTempC(); } /** * @return The temp unit/Scale */ public String getScale() { return scale; } /** * @param s Value to set the temperature unit to. */ public void setScale(final String s) { BrewServer.LOG.warning("Cut off is: " + this.cutoffTemp); if (s.equalsIgnoreCase("F")) { // Do we need to convert the cutoff temp if (cutoffTemp.compareTo(ERROR_TEMP) != 0 && !scale.equalsIgnoreCase(s)) { this.cutoffTemp = cToF(cutoffTemp); } this.calibration = this.calibration.multiply(new BigDecimal(1.8)); this.scale = s; } if (s.equalsIgnoreCase("C")) { // Do we need to convert the cutoff temp if (cutoffTemp.compareTo(ERROR_TEMP) != 0 && !scale.equalsIgnoreCase(s)) { this.cutoffTemp = fToC(cutoffTemp); } this.calibration = this.calibration.divide(new BigDecimal(1.8), context); this.scale = s; } BrewServer.LOG.warning("Cut off is now: " + this.cutoffTemp); } /** * @return The current temperature in fahrenheit. */ public BigDecimal getTempF() { if (scale.equals("F")) { return currentTemp.add(this.calibration); } return cToF(currentTemp.add(this.calibration)); } /** * @return The current temperature in celsius. */ public BigDecimal getTempC() { if (scale.equals("C")) { return currentTemp.add(this.calibration); } return fToC(currentTemp.add(this.calibration)); } /** * @param currentTemp temperature to convert in Fahrenheit * @return Temperature in celsius */ public static BigDecimal fToC(final BigDecimal currentTemp) { BigDecimal t = currentTemp.subtract(FREEZING); t = MathUtil.divide(MathUtil.multiply(t, 5),9); return t; } /** * @param currentTemp temperature to convert in Celsius * @return Temperature in Fahrenheit */ public static BigDecimal cToF(final BigDecimal currentTemp) { BigDecimal t = MathUtil.divide(MathUtil.multiply(currentTemp, 9), 5); t = t.add(FREEZING); return t; } /** * @return The current timestamp. */ public long getTime() { return currentTime; } /** * @return The current cutoff temp. */ public String getCutoff() { return cutoffTemp.toPlainString(); } /** * @return The current temperature as read. -999 if it's bad. */ public BigDecimal updateTemp() { BigDecimal result; if (badTemp && currentError != null && currentError.equals("")) { BrewServer.LOG.warning("Trying to recover " + this.getName()); } if (fProbe == null) { result = updateTempFromOWFS(); } else { result = updateTempFromFile(); } if (result.equals(ERROR_TEMP)) { badTemp = true; return result; } if (badTemp) { badTemp = false; BrewServer.LOG.warning("Recovered temperature reading for " + this.getName()); if (this.currentError.startsWith("Could")) { this.currentError = ""; } } // OWFS/One wire always uses Celsius if (scale.equals("F")) { result = cToF(result); } currentTemp = result; currentTime = System.currentTimeMillis(); currentError = null; if (cutoffEnabled && currentTemp.compareTo(cutoffTemp) >= 0) { BrewServer.LOG.log(Level.SEVERE, currentTemp + ": ****** CUT OFF TEMPERATURE (" + cutoffTemp + ") EXCEEDED *****"); System.exit(-1); } return result; } /** * @return Get the current temperature from the OWFS server */ public BigDecimal updateTempFromOWFS() { // Use the OWFS connection if (probeName.equals("Blank")) { return new BigDecimal(0.0);} BigDecimal temp = ERROR_TEMP; String rawTemp = ""; try { rawTemp = LaunchControl.readOWFSPath(probeName + "/temperature"); if (rawTemp.equals("")) { BrewServer.LOG.severe( "Couldn't find the probe " + probeName + " for " + name); LaunchControl.setupOWFS(); } else { temp = new BigDecimal(rawTemp); } } catch (IOException e) { currentError = "Couldn't read " + probeName; BrewServer.LOG.log(Level.SEVERE, currentError, e); } catch (OwfsException e) { currentError = "Couldn't read " + probeName; BrewServer.LOG.log(Level.SEVERE, currentError, e); LaunchControl.setupOWFS(); } catch (NumberFormatException e) { currentError = "Couldn't parse" + rawTemp; BrewServer.LOG.log(Level.SEVERE, currentError, e); } return temp; } /** * @return The current temperature read directly from the file system. */ public BigDecimal updateTempFromFile() { BufferedReader br = null; String temp = null; BigDecimal newTemperature = null; try { br = new BufferedReader(new FileReader(fProbe)); String line = br.readLine(); if (line == null || line.contains("NO")) { // bad CRC, do nothing this.currentError = "Bad CRC from " + fProbe; } else if (line.contains("YES")) { // good CRC line = br.readLine(); // last value should be t= int t = line.indexOf("t="); temp = line.substring(t + 2); BigDecimal tTemp = new BigDecimal(temp); newTemperature = MathUtil.divide(tTemp, 1000); this.currentError = null; } else { // System Temperature BigDecimal tTemp = new BigDecimal(line); newTemperature = MathUtil.divide(tTemp, 1000); } } catch (IOException ie) { if (loggingOn) { this.currentError = "Couldn't find the device under: " + fProbe; BrewServer.LOG.warning(currentError); if (fProbe.equals(rpiSystemTemp)) { fProbe = bbbSystemTemp; } } return ERROR_TEMP; } catch (NumberFormatException nfe) { this.currentError = "Couldn't parse " + temp + " as a double"; nfe.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException ie) { BrewServer.LOG.warning(ie.getLocalizedMessage()); } } } if( newTemperature == null ) { newTemperature = getTempC(); } return newTemperature; } /** * Setup the volume reading. * @param address One Wire device address. * @param offset Device offset input. * @param unit The volume unit to read as. */ public boolean setupVolumes(final String address, final String offset, final String unit) { // start a volume measurement at the same time if (unit == null || (!unit.equals(VolumeUnits.LITRES) && !unit.equals(VolumeUnits.UK_GALLONS) && !unit.equals(VolumeUnits.US_GALLONS))) { return false; } this.volumeMeasurement = true; this.volumeUnit = unit; this.volumeAddress = address.replace("-", "."); this.volumeOffset = offset.toUpperCase(); try { BrewServer.LOG.log(Level.INFO, "Volume ADC at: " + volumeAddress + " - " + offset); String temp = LaunchControl.readOWFSPath(volumeAddress + "/volt." + offset); // Check to make sure we can read OK if (temp.equals("")) { BrewServer.LOG.severe( "Couldn't read the Volume from " + volumeAddress + "/volt." + offset); return false; } else { BrewServer.LOG.log(Level.INFO, "Volume reads " + temp); } } catch (IOException e) { BrewServer.LOG.log(Level.SEVERE, "IOException when access the ADC over 1wire", e); return false; } catch (OwfsException e) { BrewServer.LOG.log(Level.SEVERE, "OWFSException when access the ADC over 1wire", e); return false; } return true; } /** * Setup volumes for AIN pins. * @param analogPin The AIN pin number * @param unit The volume unit * @return True is setup OK * @throws InvalidGPIOException If the Pin cannot be setup */ public boolean setupVolumes(final int analogPin, final String unit) throws InvalidGPIOException { // start a volume measurement at the same time if (unit == null || (!unit.equals(VolumeUnits.LITRES) && !unit.equals(VolumeUnits.UK_GALLONS) && !unit.equals(VolumeUnits.US_GALLONS))) { return false; } this.volumeMeasurement = true; this.volumeUnit = unit; try { this.volumePin = new InPin(analogPin, Direction.ANALOGUE); } catch (InvalidGPIOException e) { this.volumeMeasurement = false; BrewServer.LOG.warning("Invalid Analog GPIO specified " + analogPin); throw(e); } setupVolume(); if (volumeConstant.compareTo(BigDecimal.ZERO) >= 0 || volumeMultiplier.compareTo(BigDecimal.ZERO) >= 0) { this.volumeMeasurement = false; } return this.volumeMeasurement; } /** * Calculate volume reading maths. */ public void setupVolume() { if (volumeBase == null) { return; } // Calculate the values of b*value + c = volume // get the value of c this.volumeMultiplier = new BigDecimal(0); this.volumeConstant = new BigDecimal(0); // for the rest of the values Iterator<Entry<BigDecimal, BigDecimal>> it = volumeBase.entrySet().iterator(); Entry<BigDecimal, BigDecimal> prevPair = null; while (it.hasNext()) { Entry<BigDecimal, BigDecimal> pairs = it.next(); if (prevPair != null) { // diff the pair value and dive by the diff of the key BigDecimal keyDiff = pairs.getKey().subtract(prevPair.getKey()); BigDecimal valueDiff = pairs.getValue().subtract(prevPair.getValue()); BigDecimal newMultiplier = MathUtil.divide(valueDiff, keyDiff); BigDecimal newConstant = pairs.getValue().subtract(valueDiff.multiply(keyDiff)); if (volumeMultiplier.compareTo(BigDecimal.ZERO) != 0) { if (newMultiplier.equals(volumeMultiplier)) { BrewServer.LOG.info( "The newMultiplier isn't the same as the old one," + " if this is a big difference, be careful!" + " You may need a quadratic!"); BrewServer.LOG.info("New: " + newMultiplier + ". Old: " + volumeMultiplier); } } else { this.volumeMultiplier = newMultiplier; } if (volumeConstant.compareTo(BigDecimal.ZERO) != 0) { if (newConstant.equals(volumeConstant)) { BrewServer.LOG.info("The new constant " + "isn't the same as the old one, if this is a big" + " difference, be careful!" + " You may need a quadratic!"); BrewServer.LOG.info("New: " + newConstant + ". Old: " + volumeConstant); } } else { this.volumeConstant = newConstant; } } prevPair = pairs; } // we should be done now } /** * @return The latest volume reading */ public BigDecimal updateVolume() { try { BigDecimal pinValue; if (volumeAIN != -1) { pinValue = new BigDecimal(volumePin.readValue()); } else if (volumeAddress != null && volumeOffset != null) { try { pinValue = new BigDecimal( LaunchControl.readOWFSPath( volumeAddress + "/volt." + volumeOffset)); if (this.stopVolumeLogging) { BrewServer.LOG.log(Level.SEVERE, "Recovered volume level reading for " + this.name); this.stopVolumeLogging = false; } } catch (Exception e) { if (!this.stopVolumeLogging) { BrewServer.LOG.log(Level.SEVERE, "Could not update the volume reading from OWFS"); this.stopVolumeLogging = true; } BrewServer.LOG.info("Reconnecting OWFS"); LaunchControl.setupOWFS(); return BigDecimal.ZERO; } } else { return BigDecimal.ZERO; } // Are we outside of the known range? BigDecimal curKey = null, prevKey = null; BigDecimal curValue = null, prevValue = null; SortedSet<BigDecimal> keys = null; try { keys = Collections.synchronizedSortedSet( new TreeSet<>(volumeBase.keySet())); } catch (NullPointerException npe) { // No VolumeBase setup, so we're probably calibrating return pinValue; } catch (Exception e) { BrewServer.LOG.log(Level.SEVERE, "Uncaught exception when creating the volume set", e); System.exit(-1); } BigDecimal tVolume = null; try { for (BigDecimal key: keys) { if (prevKey == null) { prevKey = key; prevValue = volumeBase.get(key); continue; } else if (curKey != null) { prevKey = curKey; prevValue = curValue; } curKey = key; curValue = volumeBase.get(key); if (pinValue.compareTo(prevValue) >= 0 && pinValue.compareTo(curValue) <= 0) { // We have a set encompassing the values! // assume it's linear BigDecimal volRange = curKey.subtract(prevKey); BigDecimal readingRange = curValue.subtract(prevValue); BigDecimal ratio = MathUtil.divide(pinValue.subtract(prevValue),readingRange); BigDecimal volDiff = ratio.multiply(volRange); tVolume = volDiff.add(prevKey); } } if (tVolume == null && curKey != null) { // Try to extrapolate BigDecimal volRange = curKey.subtract(prevKey); BigDecimal readingRange = curValue.subtract(prevValue); BigDecimal ratio = MathUtil.divide(pinValue.subtract(prevValue),readingRange); BigDecimal volDiff = ratio.multiply(volRange); tVolume = volDiff.add(prevKey); } } catch (NoSuchElementException e) { // no more elements BrewServer.LOG.info("Finished reading Volume Elements"); } if (tVolume == null) { // try to assume the value this.currentVolume = pinValue.subtract(volumeConstant) .multiply(volumeMultiplier); } else { this.currentVolume = tVolume; } this.currentVolume = this.currentVolume.multiply(this.gravity); return pinValue; } catch (RuntimeException | IOException e) { e.printStackTrace(); } return BigDecimal.ZERO; } /** * @param unit Unit to set the volume units to. */ @SuppressWarnings("unused") public void setVolumeUnit(final String unit) { this.volumeUnit = unit; } /** * Append a volume measurement to the current list of calibrated values. * @param volume Volume measurement to record. * @return True if added OK. */ public boolean addVolumeMeasurement(final BigDecimal volume) { // record 10 readings and average it BigDecimal maxReads = BigDecimal.TEN; BigDecimal total = new BigDecimal(0); for (int i = 0; i < maxReads.intValue(); i++) { try { try { if (this.volumePin != null) { total = total.add(new BigDecimal( this.volumePin.readValue())); } else { try { total = total.add(new BigDecimal( LaunchControl.readOWFSPath( volumeAddress + "/volt." + volumeOffset))); } catch (OwfsException e) { e.printStackTrace(); } } } catch (RuntimeException re) { re.printStackTrace(); return false; } catch (IOException e) { e.printStackTrace(); return false; } } catch (NumberFormatException e) { BrewServer.LOG.warning("Bad Analog input value!"); return false; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } // read in ten values BigDecimal avgValue = MathUtil.divide(total, maxReads); BrewServer.LOG.info("Read " + avgValue + " for " + volume + " " + volumeUnit); this.addVolumeMeasurement(volume, avgValue); System.out.println(this.name + ": Added volume data point " + volume); return true; } /** * Add a volume measurement at a specific key. * @param key The key to overwrite/set * @param value The value to overwrite/set */ public void addVolumeMeasurement( final BigDecimal key, final BigDecimal value) { BrewServer.LOG.info("Adding " + key + " with value " + value); if (volumeBase == null) { this.volumeBase = new ConcurrentHashMap<>(); } this.volumeBase.put(key, value); } /** * @return The current volume measurement, * -1.0 is there is no measurement enabled. */ public BigDecimal getVolume() { if (this.volumeMeasurement) { return this.currentVolume; } return BigDecimal.ONE.negate(); } /** * @return The current volume unit */ public String getVolumeUnit() { return this.volumeUnit; } /** * @return The analogue input */ public String getVolumeAIN() { if (this.volumeAIN == -1) { return ""; } return Integer.toString(this.volumeAIN); } /** * @return Get the volume address */ public String getVolumeAddress() { return this.volumeAddress; } /** * @return The current volume offset */ public String getVolumeOffset() { return this.volumeOffset; } /** * @return The current volume base map */ public ConcurrentHashMap<BigDecimal, BigDecimal> getVolumeBase() { return this.volumeBase; } /** * Check to see if this object has a valid volume input. * @return true if there's a valid volume input on this class */ public boolean hasVolume() { return (this.volumeAddress != null && !this.volumeAddress.equals("") && this.volumeOffset != null && !this.volumeOffset.equals("")) || (this.volumeAIN != -1); } /***** * Helper function to return a map of the current status. * @return The current status of the temperature probe. */ public Map<String, Object> getMapStatus() { Map<String, Object> statusMap = new HashMap<>(); statusMap.put("hidden", isHidden()); statusMap.put("temp", getTemp()); statusMap.put("elapsed", getTime()); statusMap.put("scale", getScale()); statusMap.put("cutoff", getCutoff()); statusMap.put("cutoffEnabled", cutoffEnabled); statusMap.put("calibration", getCalibration()); statusMap.put("gravity", gravity); statusMap.put("position", this.position); statusMap.put("size", this.size); if (currentError != null) { statusMap.put("error", currentError); } return statusMap; } public void shutdown() { // Graceful shutdown. keepalive = false; BrewServer.LOG.warning(this.getName() + " is shutting down"); Thread.currentThread().interrupt(); } public void setCalibration(String calibration) { // Lock the calibration temp calibration = calibration.replace(",", "."); Matcher tempMatcher = tempRegexp.matcher(calibration); if (tempMatcher.find()) { // We have matched against the TEMP_REGEXP String number = tempMatcher.group(1); if (number == null) { number = "+"; } // Get the integer if (tempMatcher.group(2) != null) { number += tempMatcher.group(2); } // Do we have a decimal? if (tempMatcher.group(3) != null) { number += tempMatcher.group(3); } // Create the temp BigDecimal temperature = new BigDecimal(number); this.calibration = temperature.setScale(2, BigDecimal.ROUND_DOWN); } else { BrewServer.LOG.severe(calibration + " doesn't match " + tempRegexp.pattern()); } } public String getCalibration() { DecimalFormat df = new DecimalFormat("#.##"); return df.format(this.calibration); } public boolean isSetup() { return !this.getName().equals(this.getProbe()); } public void toggleVisibility() { if (this.hidden) { this.show(); } else { this.hide(); } } public void hide() { this.hidden = true; } public void show() { this.hidden = false; } public boolean isHidden() { return this.hidden; } public void setGravity(BigDecimal newGravity) { this.gravity = newGravity; } public BigDecimal getGravity() { return this.gravity; } @Override public String toString() { return this.getName(); } /** * Get the current TriggerControl object, create a new one if it doesn't * exist. * @return the TriggerControl. */ public TriggerControl getTriggerControl() { if (this.triggerControl == null) { this.triggerControl = new TriggerControl(); this.triggerControl.setOutputControl(this.getName()); } return triggerControl; } /** * @return The position of this temp probe in the list. */ public int getPosition() { return this.position; } /** * Set the position of this temp probe. * @param newPos The new position. */ public void setPosition(final int newPos) { this.position = newPos; } @Override public int compareTo(@Nonnull final Temp o) { if (o.getPosition() == this.position) { return o.getName().compareTo(this.name); } if (this.position == -1) { return 1; } return this.position - o.getPosition(); } public BigDecimal convertF(BigDecimal temp) { if (this.scale.equals("C")) { return cToF(temp); } return temp; } /** * Get the current Size. * @return {@value #SIZE_SMALL} {@value #SIZE_MEDIUM} or {@value #SIZE_LARGE} */ public int getSize() { return size; } /** * Set the new size of this render. * @param newSize {@value #SIZE_SMALL} {@value #SIZE_MEDIUM} or {@value #SIZE_LARGE} */ public void setSize(int newSize) { if (size == -1) { return; } size = newSize; } public String getI2CDevNumberString() { if (i2cDevice == null) { return ""; } return i2cDevice.getDevNumberString(); } public String getI2CDevAddressString() { if (i2cDevice == null) { return ""; } return Integer.toString(i2cDevice.getAddress()); } public String geti2cChannel() { if (i2cChannel == -1) { return ""; } return Integer.toString(i2cChannel); } public String getI2CDevType() { if (i2cDevice == null) { return ""; } return i2cDevice.getDevName(); } public boolean setupVolumeI2C(String i2c_device, String i2c_address, String i2c_channel, String i2c_type, String units) { return setupVolumeI2C(LaunchControl.getI2CDevice(i2c_device, i2c_address, i2c_type), i2c_channel, units); } public boolean setupVolumeI2C(I2CDevice i2c_device, String i2c_channel, String volumeUnits) { this.i2cDevice = i2c_device; this.i2cChannel = Integer.parseInt(i2c_channel); this.setVolumeUnit(volumeUnits); return (i2cDevice != null); } }