瀏覽代碼

First commit

First commit. Working version.
RoelGo 9 年之前
父節點
當前提交
ee23d448e3

+ 0 - 0
lib/lib_location.txt


+ 91 - 0
src/camsucks/CAMSucks.java

@@ -0,0 +1,91 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package camsucks;
+
+import java.awt.Toolkit;
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import javafx.stage.Stage;
+
+/**
+ *  This Project aims to replace the default software made for the GRID+ Controller; CAM
+ * <p>
+ * Software used for this project
+ * <p>
+ * <ul>
+ * <li> The reverse engineering of the GRID+ communication was done by rizvanrp, their site is no longer available but here is a screenshot of their article on the GRID+ http://research.domaintools.com/research/screenshot-history/rizvanrp.com/#0
+ * <li> The aim is to be able to control the fan speed of the fans connected to a GRID controller according to the temperature of the CPU Packages
+ * <li> The project uses a Communicator class that has been created by Henry Poon @ https://blog.henrypoon.com/ 
+ * <li> With the help of this class the communication with the GRID+ controller is handled
+ * <li> The sensor data is read with the help of the jWMI class made by Henry Ranch @ http://henryranch.net
+ * <li> This class communicates with an external program called openhardwaremonitor @ http://openhardwaremonitor.org/
+ * </ul>
+ * <p>
+ * 
+ *  Currently monitoring is a bit bugged and is disabled by default but can be turned on with a checkbox.
+ * <p>
+ *  Future plans and TODOs:
+ * <p>
+ * <ul>
+ * <li>     Make it possible to control fans according to GPU or CPU  or Both temperatures (seems easy enough).
+ * <li>     Add Integral control to achieve full PI control (Before this can happen a the time constant of the system must be defined reliably .
+ * <li>     Make program not crash after/during system sleep/hibernation.
+ * <li>     Find a way to compile program and not get security warnings (because of the filewriter in the jWMI class).
+ * <li>     Make a config file to save user setting in.
+ * </ul>
+ * <p>
+ * 
+ * 
+ * @author Roel
+ */
+public class CAMSucks extends Application {
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        
+        
+        // Model
+        ComputerModel model = new ComputerModel("COM3");
+        
+        // View
+
+        FXMLLoader loader = new FXMLLoader();
+        loader.setLocation(getClass().getResource("FXMLView.fxml"));
+        Parent root = loader.load();
+        
+        
+        // Controller
+        FXMLViewController controller = loader.getController();
+        controller.setModel(model);
+ 
+        Scene scene = new Scene(root);
+        setUserAgentStylesheet(STYLESHEET_CASPIAN);
+        stage.setResizable(false);
+        stage.setTitle("CAM Sucks!");
+        stage.getIcons().add(new Image(getClass().getResourceAsStream("NoCAMIcon.jpg")));
+        stage.setScene(scene);
+        stage.show();
+        
+        stage.setOnCloseRequest(event -> {
+            model.getGrid().disconnect();
+            System.exit(0);
+        });
+        
+    }
+
+    /**
+     * 
+     * @param args the command line arguments
+     */
+    public static void main(String[] args)  {
+        launch(args);
+
+    }
+
+}

+ 346 - 0
src/camsucks/Communicator.java

@@ -0,0 +1,346 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package camsucks;
+
+import gnu.io.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import static java.lang.Thread.sleep;
+import java.util.Enumeration;
+import java.util.HashMap;
+
+/**
+ * This class communicates with a serial com device
+ * @author Henry Poon  @ https://blog.henrypoon.com/ 
+ * Changes by Roel:     Removed all references to the GUI class from Henry Poon's original project.
+ *                      Changed the event based serial read to a synchronised read after each write. 
+ *                      Added a Buffer for the serial read so that the read data can easily be accessed by other classes
+ *                      Completely rewritten the writeData method so that it works with the GRID+ controller
+ *                      Added some getters and setters for the buffer
+ *                      added a getter for the portMap so that the controller and view can access it
+ */
+public class Communicator {
+
+    //for containing the ports that will be found
+    private Enumeration ports = null;
+    //map the port names to CommPortIdentifiers
+    private HashMap portMap = new HashMap();
+    //this is the object that contains the opened port
+    private String selectedPort;
+    private CommPortIdentifier selectedPortIdentifier = null;
+    private SerialPort serialPort = null;
+
+    //input and output streams for sending and receiving data
+    private InputStream input = null;
+    private OutputStream output = null;
+
+    //just a boolean flag that i use for enabling
+    //and disabling buttons depending on whether the program
+    //is connected to a serial port or not
+    private boolean bConnected = false;
+
+    //the timeout value for connecting with the port
+    final static int TIMEOUT = 2000;
+
+    //some ascii values for for certain things
+    final static int SPACE_ASCII = 32;
+    final static int DASH_ASCII = 45;
+    final static int NEW_LINE_ASCII = 10;
+
+    //Buffer
+    private int leng;
+    private byte[] buffer;
+
+    //a string for recording what goes on in the program
+    String logText = "";
+
+    /**
+     *
+     * @param selectedPort This parameter is the string name of the port the communicator object should connect to
+     */
+    public Communicator(String selectedPort) {
+        this.selectedPort = selectedPort;
+    }
+
+    //search for all the serial ports
+    //pre: none
+    //post: adds all the found ports to a combo box on the GUI
+
+    /**
+     *
+     * This method searches for COM ports on the system and saves their Identifier in the hashmap portMap with their name as key.
+     * 
+     */
+    public void searchForPorts() {
+        ports = CommPortIdentifier.getPortIdentifiers();
+
+        while (ports.hasMoreElements()) {
+            CommPortIdentifier curPort = (CommPortIdentifier) ports.nextElement();
+
+            //get only serial ports
+            if (curPort.getPortType() == CommPortIdentifier.PORT_SERIAL) {
+                getPortMap().put(curPort.getName(), curPort);
+            }
+        }
+        //System.out.println(portMap);
+    }
+
+    //connect to the selected port in the combo box
+    //pre: ports are already found by using the searchForPorts method
+    //post: the connected comm port is stored in commPort, otherwise,
+    //an exception is generated
+
+    /**
+     *This method opens the COM port with port parameters: Baudrate: 4800; databits: 8; Stopbit: 1; parity: none; 
+     */
+    public void connect() {
+        selectedPortIdentifier = (CommPortIdentifier) getPortMap().get(selectedPort);
+
+        CommPort commPort = null;
+
+        try {
+            //the method below returns an object of type CommPort
+            commPort = selectedPortIdentifier.open("TigerControlPanel", TIMEOUT);
+            //the CommPort object can be casted to a SerialPort object
+            serialPort = (SerialPort) commPort;
+            serialPort.setSerialPortParams(4800, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
+            //for controlling GUI elements
+            setConnected(true);
+
+            //logging
+            logText = selectedPort + " opened successfully.";
+            System.out.println(logText);
+            //CODE ON SETTING BAUD RATE ETC OMITTED
+            //XBEE PAIR ASSUMED TO HAVE SAME SETTINGS ALREADY
+
+        } catch (PortInUseException e) {
+            logText = selectedPort + " is in use. (" + e.toString() + ")";
+
+            System.out.println(logText);
+        } catch (Exception e) {
+            logText = "Failed to open " + selectedPort + "(" + e.toString() + ")";
+            System.out.println(logText);
+        }
+    }
+
+    //open the input and output streams
+    //pre: an open port
+    //post: initialized intput and output streams for use to communicate data
+
+    /**
+     * This method initializes the serial IO stream,
+     * after the init is complete the method sends an initialize command to the GRID+ controller
+     * @return successful Boolean value which indicates whether the method was completed succesfuly
+     */
+    public boolean initIOStream() {
+        //return value for whather opening the streams is successful or not
+        boolean successful = false;
+
+        try {
+            //
+            input = serialPort.getInputStream();
+            output = serialPort.getOutputStream();
+
+            writeData(0Xc0);
+            successful = true;
+            return successful;
+        } catch (IOException e) {
+            logText = "I/O Streams failed to open. (" + e.toString() + ")";
+            System.out.println(logText);
+            return successful;
+        }
+    }
+
+    
+    /**
+     *This method disconnects the serial communication by first closing the serialPort and then closing the IOStreams
+     * 
+     * 
+     */
+    public void disconnect() {
+        //close the serial port
+        try {
+           
+            
+            //serialPort.removeEventListener();
+            serialPort.close();
+            input.close();
+            output.close();
+            setConnected(false);
+
+            logText = "Disconnected.";
+            System.out.println(logText);
+        } catch (IOException e) {
+            logText = "Failed to close " + serialPort.getName() + "(" + e.toString() + ")";
+            System.out.println(logText);
+        }
+    }
+
+    /**
+     *  Returns true if the connection is open
+     *  False if closed
+     * @return boolean value for the connection status
+     */
+    final public boolean getConnected() {
+        return bConnected;
+    }
+
+    /**
+     *Setter for the  boolean value for the connection status
+     * @param bConnected boolean value for the connection status
+     */
+    public void setConnected(boolean bConnected) {
+        this.bConnected = bConnected;
+    }
+
+    //what happens when data is received
+    //pre: serial event is triggered
+    //post: processing on the data it reads
+
+    /**
+     * This method reads data from the input stream and puts it in a buffer 
+     * after the data is read the method waits 50 msec to make sure a new command doesn't follow up to quickly
+     * This promotes stability in the program.
+     * 
+     * 
+     */
+    public void serialRead() throws InterruptedException {
+        try {
+            if (input.available() > 0) {
+                try {
+
+                    setBuffer(new byte[1024]);
+
+                    setLeng(input.read(getBuffer()));
+
+                    // Debug.
+                    /*System.out.println("Length: " + getLeng());
+            
+                    for (int itera = 0; itera < leng; itera++) {
+                    System.out.println("byte " + itera + ": " + Integer.toHexString(getBuffer(itera) & 0xFF));
+                    }*/
+                } catch (Exception e) {
+                    logText = "Failed to read data. (" + e.toString() + ")";
+                    System.out.println(logText);
+                }
+            } else {
+                logText = "Failed to read data. No data to read";
+                //System.out.println(logText);
+            }
+
+        } catch (IOException e) {
+            logText = "Failed to read data. (" + e.toString() + ")";
+            System.out.println(logText);
+        }
+        //This prevents commands from being send too soon  
+        sleep(50);
+    }
+
+    //method that can be called to send data
+    //pre: open serial port
+    //post: data sent to the other device
+
+    /**
+     * This method sends a single byte command over the serial communication afterwards the method calls the read method
+     * @param command a single byte command
+     */
+    public void writeData(int command) {
+        try {
+            output.write(command);
+            sleep(20);
+            serialRead();
+        } catch (Exception e) {
+            logText = "Failed to write data. (" + e.toString() + ")";
+            System.out.println(logText);
+
+        }
+    }
+
+    /**
+     *  This method sends a byte array command over the serial communication afterwards the method calls the read method
+     * @param command an array of bytes as a command
+     */
+    public void writeData(byte[] command) {
+        try {
+            output.write(command);
+            sleep(50);
+            serialRead();
+        } catch (Exception e) {
+            logText = "Failed to write data. (" + e.toString() + ")";
+            System.out.println(logText);
+
+        }
+    }
+
+    /**
+     * @return the leng
+     */
+    public int getLeng() {
+        return leng;
+    }
+
+    /**
+     * @param leng the leng to set
+     */
+    public void setLeng(int leng) {
+        this.leng = leng;
+    }
+
+    /**
+     * @return the buffer
+     */
+    public byte[] getBuffer() {
+        return buffer;
+    }
+
+    /**
+     *
+     * @param i 
+     * @return The value of the buffer at i
+     */
+    public byte getBuffer(int i) {
+        return buffer[i];
+    }
+
+    /**
+     *
+     * @return  The second to last byte of the buffer
+     */
+    public byte getSecondToLast() {
+        if (leng >= 2) {
+            return buffer[leng - 2];
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     *
+     * @return The last byte of the buffer
+     */
+    public byte getlast() {
+        if (leng >= 2) {
+            return buffer[leng - 1];
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * @param buffer the buffer to set
+     */
+    public void setBuffer(byte[] buffer) {
+        this.buffer = buffer;
+    }
+
+    /**
+     * @return the portMap
+     */
+    public HashMap getPortMap() {
+        return portMap;
+    }
+
+}

+ 316 - 0
src/camsucks/ComputerModel.java

@@ -0,0 +1,316 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package camsucks;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This is the model of this project
+ *
+ * This model contains two main data members as well as some data members used
+ * to configure the calculations and two checks
+ *
+ * Along with all the getters and setters this model has two methods used for
+ * polling and calculating
+ *
+ * @author Roel
+ */
+public class ComputerModel {
+
+    // The main components of the Computermodel
+    private Sensor sensor;
+    private GRID grid;
+
+    // The variables used to calculate the percentage to send
+    private double maxCPUTemp;
+    private double maxGPUTemp;
+    private double targetCPUTemp;
+    private double targetGPUTemp;
+    private double targetRPM;
+    // a global minimum percentage, this is used to prevent the controller to constantly turn the fans on and off
+    private int minRPM;
+
+    //Boolean Checks used to know whether to run certain pieces of code
+    private boolean extraPoll;
+    private boolean manual;
+    // private boolean GRIDset;
+    // The percentage to send at the end of the loop.   
+    private int percentageToSend;
+    private int ticks;
+
+    /**
+     *
+     *
+     * All members get initialised here.
+     *
+     * @param selectedPort
+     */
+    public ComputerModel(String selectedPort) {
+        targetRPM = 35;
+        targetCPUTemp = 50;
+        maxCPUTemp = 80;
+        targetGPUTemp = 50;
+        maxGPUTemp = 80;
+        minRPM = 35;
+        extraPoll = true;
+        manual = false;
+        ticks = 0;
+
+        grid = new GRID(selectedPort);
+
+        try {
+            sensor = new Sensor();
+        } catch (Exception ex) {
+            Logger.getLogger(ComputerModel.class.getName()).log(Level.SEVERE, null, ex);
+        }
+    }
+
+    /**
+     *
+     * Currently only the pollCPUPackageTemp method of the sensor object is
+     * called This can change in the future
+     *
+     */
+    public void poll() {
+        //System.out.println("Polling...");
+
+        if (isExtraPoll()) {
+            ticks++;
+            if (!getGrid().isSame() || ticks > 10) {
+                //System.out.println("polling some extra shit ");
+                ticks = 0;
+
+                getGrid().pollVoltage();
+                
+
+                for (int i = 1; i < 7; i++) {
+                    getGrid().pollFanAMP(i);
+                    getGrid().pollFanRPM(i);
+
+                }
+            }
+
+        }
+
+        try {
+            getSensor().pollCPUTemp();
+            getSensor().pollCPUMax();
+            getSensor().pollGPUTemp();
+            getSensor().pollGPUMax();
+            
+            
+        } catch (Exception ex) {
+            System.out.println("Polling Failed");
+        }
+
+    }
+
+    /**
+     *
+     * In this method the percentage to sent is computed. The calculation aims
+     * to act as a proportional controller. A later step could be to add an
+     * integral controller to the calculation to get a better calculated fan
+     * curve
+     *
+     */
+    public void compute() {
+
+        double currentCPUTemp = getSensor().getCPUTemp();
+
+        double CPUerror = (currentCPUTemp - getTargetCPUTemp());
+
+        double CPUkFactor = ((100 - getTargetRPM()) / (getMaxCPUTemp() - getTargetCPUTemp()));
+
+        int tempCPUPercentage = (int) (getTargetRPM() + (CPUkFactor * CPUerror));
+
+        if (tempCPUPercentage < minRPM) {
+            tempCPUPercentage = minRPM;
+        }
+        
+        double currentGPUTemp = getSensor().getGPUTemp();
+
+        double GPUerror = (currentGPUTemp - getTargetGPUTemp());
+
+        double GPUkFactor = ((100 - getTargetRPM()) / (getMaxGPUTemp() - getTargetGPUTemp()));
+
+        int tempGPUPercentage = (int) (getTargetRPM() + (GPUkFactor * GPUerror));
+
+        if (tempGPUPercentage < minRPM) {
+            tempGPUPercentage = minRPM;
+        }
+        
+        if(tempCPUPercentage > tempGPUPercentage){
+            setPercentageToSend(tempCPUPercentage);
+        }   else    {
+            setPercentageToSend(tempGPUPercentage);
+        }
+
+    }
+
+    /**
+     * A getter for the Sensor Object to make the methods of the object
+     * available
+     *
+     * @return the sensor
+     */
+    public Sensor getSensor() {
+        return sensor;
+    }
+
+    /**
+     * A getter for the GRID Object to make the methods of the object available
+     *
+     * @return the grid
+     */
+    public GRID getGrid() {
+        return grid;
+    }
+
+    /**
+     * This setter overwrites the old GRID with a new Object with the
+     * selectedport as the COM port to connect to
+     *
+     * @param selectedPort The com port the GRID controller is located at
+     */
+    public void setGrid(String selectedPort) {
+        grid = new GRID(selectedPort);
+    }
+
+    /**
+     * Used to check if the extra polling has to be exicuted
+     *
+     * @return the extraPoll
+     */
+    public boolean isExtraPoll() {
+        return extraPoll;
+    }
+
+    /**
+     * Used to define whether the extra polling must be done
+     *
+     * @param extraPoll the extraPoll to set
+     */
+    public void setExtraPoll(boolean extraPoll) {
+        this.extraPoll = extraPoll;
+    }
+
+    /**
+     *
+     * @return the targetRPM
+     */
+    public double getTargetRPM() {
+        return targetRPM;
+    }
+
+    /**
+     * @param targetRPM the targetRPM to set
+     */
+    public void setTargetRPM(double targetRPM) {
+        this.targetRPM = targetRPM;
+    }
+
+    /**
+     * @return the targetTemp
+     */
+    public double getTargetCPUTemp() {
+        return targetCPUTemp;
+    }
+
+    /**
+     * @param targetTemp the targetTemp to set
+     */
+    public void setTargetCPUTemp(double targetTemp) {
+        this.targetCPUTemp = targetTemp;
+    }
+
+    /**
+     * @return the maxTemp
+     */
+    public double getMaxCPUTemp() {
+        return maxCPUTemp;
+    }
+
+    /**
+     * @param maxTemp the maxTemp to set
+     */
+    public void setMaxCPUTemp(double maxTemp) {
+        this.maxCPUTemp = maxTemp;
+    }
+    /**
+     * @return the targetTemp
+     */
+    public double getTargetGPUTemp() {
+        return targetGPUTemp;
+    }
+
+    /**
+     * @param targetTemp the targetTemp to set
+     */
+    public void setTargetGPUTemp(double targetTemp) {
+        this.targetGPUTemp = targetTemp;
+    }
+
+    /**
+     * @return the maxTemp
+     */
+    public double getMaxGPUTemp() {
+        return maxGPUTemp;
+    }
+
+    /**
+     * @param maxTemp the maxTemp to set
+     */
+    public void setMaxGPUTemp(double maxTemp) {
+        this.maxGPUTemp = maxTemp;
+    }
+
+    /**
+     * A check to see whether the percentage to send is manually set or has to
+     * be calculated
+     *
+     * @return the manual
+     */
+    public boolean isManual() {
+        return manual;
+    }
+
+    /**
+     * @param manual the manual to set
+     */
+    public void setManual(boolean manual) {
+        this.manual = manual;
+    }
+
+    /**
+     * @return the minRPM
+     */
+    public double getMinRPM() {
+        return minRPM;
+    }
+
+    /**
+     *
+     * @param minRPM
+     */
+    public void setMinRPM(int minRPM) {
+        this.minRPM = minRPM;
+    }
+
+    /**
+     * @return the percentageToSend
+     */
+    public int getPercentageToSend() {
+        return percentageToSend;
+    }
+
+    /**
+     * @param percentageToSend the percentageToSend to set
+     */
+    public void setPercentageToSend(int percentageToSend) {
+        this.percentageToSend = percentageToSend;
+    }
+}

+ 96 - 0
src/camsucks/FXMLView.fxml

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import java.lang.*?>
+<?import java.util.*?>
+<?import javafx.scene.*?>
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+
+<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="212.0" prefWidth="231.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="camsucks.FXMLViewController">
+   <children>
+      <AnchorPane>
+         <children>
+            <TabPane fx:id="TapPane" prefHeight="211.0" prefWidth="231.0" tabClosingPolicy="UNAVAILABLE">
+              <tabs>
+                <Tab fx:id="ConfigureTab" text="Configure">
+                  <content>
+                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="164.0" prefWidth="335.0">
+                           <children>
+                              <TextField fx:id="targetRPM" layoutX="161.0" layoutY="4.0" prefHeight="25.0" prefWidth="66.0" />
+                              <Label layoutX="8.0" layoutY="8.0" text="Target Fan Percentage" />
+                              <TextField fx:id="targetCPUTemp" layoutX="161.0" layoutY="53.0" prefHeight="25.0" prefWidth="65.0" />
+                              <Label layoutX="8.0" layoutY="57.0" text="Target Temperature" />
+                              <TextField fx:id="maxCPUTemp" layoutX="161.0" layoutY="103.0" prefHeight="25.0" prefWidth="65.0" />
+                              <Label layoutX="8.0" layoutY="107.0" text="Preferred Maximum" />
+                              <Label layoutX="8.0" layoutY="158.0" text="COM Port" />
+                              <ChoiceBox fx:id="portMap" layoutX="161.0" layoutY="153.0" prefHeight="25.0" prefWidth="65.0" />
+                              <TextField fx:id="maxGPUTemp" layoutX="161.0" layoutY="128.0" prefHeight="25.0" prefWidth="65.0" />
+                              <TextField fx:id="targetGPUTemp" layoutX="161.0" layoutY="78.0" prefHeight="25.0" prefWidth="65.0" />
+                              <Label layoutX="127.0" layoutY="57.0" text="CPU" />
+                              <Label layoutX="127.0" layoutY="107.0" text="CPU" />
+                              <Label layoutX="127.0" layoutY="82.0" text="GPU" />
+                              <Label layoutX="127.0" layoutY="132.0" text="GPU" />
+                              <TextField fx:id="minRPM" layoutX="161.0" layoutY="28.0" prefHeight="25.0" prefWidth="65.0" />
+                              <Label layoutX="8.0" layoutY="29.0" text="Minimum Fan Percentage" />
+                           </children>
+                        </AnchorPane>
+                  </content>
+                </Tab>
+                  <Tab closable="false" text="Manual">
+                    <content>
+                      <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
+                           <children>
+                              <CheckBox fx:id="manualCheck" layoutX="14.0" layoutY="14.0" mnemonicParsing="false" text="Manual" />
+                              <Slider fx:id="manualSlider" layoutX="15.0" layoutY="86.0" prefHeight="13.0" prefWidth="200.0" AnchorPane.leftAnchor="15.0" />
+                              <Label layoutX="13.0" layoutY="56.0" text="Fan Speed" />
+                              <Label layoutX="14.0" layoutY="72.0" text="0%" />
+                              <Label layoutX="103.0" layoutY="72.0" text="50%" />
+                              <Label layoutX="193.0" layoutY="72.0" text="100%" />
+                              <Label layoutX="11.0" layoutY="114.0" prefHeight="42.0" prefWidth="205.0" text="This is free software. If you paid for this software you got scammed." wrapText="true" />
+                           </children></AnchorPane>
+                    </content>
+                  </Tab>
+                <Tab fx:id="MonitorTab" text="Monitor">
+                  <content>
+                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="370.0" prefWidth="613.0">
+                           <children>
+                              <Label layoutX="14.0" layoutY="37.0" prefHeight="17.0" prefWidth="63.0" text="FAN #1" />
+                              <Label layoutX="14.0" layoutY="54.0" prefHeight="17.0" prefWidth="63.0" text="FAN #2" />
+                              <Label layoutX="14.0" layoutY="71.0" prefHeight="17.0" prefWidth="63.0" text="FAN #2" />
+                              <Label layoutX="14.0" layoutY="88.0" prefHeight="17.0" prefWidth="63.0" text="FAN #4" />
+                              <Label layoutX="14.0" layoutY="105.0" prefHeight="17.0" prefWidth="63.0" text="FAN #5" />
+                              <Label layoutX="14.0" layoutY="122.0" prefHeight="17.0" prefWidth="63.0" text="FAN #6" />
+                              <Label fx:id="RPMLabel1" layoutX="93.0" layoutY="37.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="RPMLabel2" layoutX="93.0" layoutY="54.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="RPMLabel3" layoutX="93.0" layoutY="71.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="RPMLabel4" layoutX="93.0" layoutY="88.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="RPMLabel5" layoutX="93.0" layoutY="105.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="RPMLabel6" layoutX="93.0" layoutY="122.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="AMPLabel1" layoutX="156.0" layoutY="37.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="AMPLabel2" layoutX="156.0" layoutY="54.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="AMPLabel3" layoutX="156.0" layoutY="71.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="AMPLabel4" layoutX="156.0" layoutY="88.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="AMPLabel5" layoutX="156.0" layoutY="105.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="AMPLabel6" layoutX="156.0" layoutY="122.0" prefHeight="17.0" prefWidth="63.0" />
+                              <CheckBox fx:id="monitorCheck" layoutX="12.0" layoutY="15.0" mnemonicParsing="false" selected="true" text="Monitor" />
+                              <Label fx:id="RPMLabel11" layoutX="103.0" layoutY="47.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="AMPLabel11" layoutX="166.0" layoutY="47.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label layoutX="91.0" layoutY="15.0" prefHeight="17.0" prefWidth="63.0" text="Voltage" />
+                              <Label fx:id="VoltageLabel" layoutX="154.0" layoutY="15.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label layoutX="14.0" layoutY="137.0" prefHeight="17.0" prefWidth="63.0" text="CPU" />
+                              <Label fx:id="CPULabel" layoutX="93.0" layoutY="137.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="CPULabelMax" layoutX="156.0" layoutY="137.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label layoutX="14.0" layoutY="152.0" prefHeight="17.0" prefWidth="63.0" text="GPU" />
+                              <Label fx:id="GPULabel" layoutX="93.0" layoutY="152.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label fx:id="GPULabelMax" layoutX="156.0" layoutY="152.0" prefHeight="17.0" prefWidth="63.0" />
+                              <Label layoutX="14.0" layoutY="167.0" prefHeight="17.0" prefWidth="63.0" text="Power" />
+                              <Label fx:id="PowerLabel" layoutX="93.0" layoutY="167.0" prefHeight="17.0" prefWidth="63.0" />
+                           </children></AnchorPane>
+                  </content>
+                </Tab>
+              </tabs>
+            </TabPane>
+         </children>
+      </AnchorPane>
+   </children>
+</AnchorPane>

+ 371 - 0
src/camsucks/FXMLViewController.java

@@ -0,0 +1,371 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package camsucks;
+
+import java.net.URL;
+import java.text.DecimalFormat;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javafx.application.Platform;
+import javafx.beans.binding.Bindings;
+import javafx.event.ActionEvent;
+import javafx.event.Event;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.Slider;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TextField;
+import javafx.scene.input.MouseEvent;
+
+/**
+ * This class is the controller of this Project. It initialises the Interactive
+ * UI elements When its model is set it adds values to certain UI elements and
+ * starts a pollAndCompute thread
+ *
+ *
+ * @author Roel
+ */
+public class FXMLViewController implements Initializable, Runnable {
+
+    //<editor-fold defaultstate="collapsed" desc="Data members">
+    private Thread t;
+    
+    
+    
+    private ComputerModel model;
+
+    @FXML
+    private TextField targetRPM;
+    @FXML
+    private TextField targetCPUTemp;
+    @FXML
+    private TextField maxCPUTemp;
+    @FXML
+    private TextField targetGPUTemp;
+    @FXML
+    private TextField maxGPUTemp;
+    @FXML
+    private TextField minRPM;
+    @FXML
+    private ChoiceBox portMap;
+
+    @FXML
+    private CheckBox manualCheck;
+    @FXML
+    private Slider manualSlider;
+
+    @FXML
+    private Tab MonitorTab;
+
+    @FXML
+    private CheckBox monitorCheck;
+    @FXML
+    private Label VoltageLabel;
+    @FXML
+    private Label CPULabel;
+    @FXML
+    private Label CPULabelMax;
+    @FXML
+    private Label GPULabel;
+    @FXML
+    private Label GPULabelMax;
+    @FXML
+    private Label PowerLabel;
+    @FXML
+    private Label AMPLabel1;
+    @FXML
+    private Label AMPLabel2;
+    @FXML
+    private Label AMPLabel3;
+    @FXML
+    private Label AMPLabel4;
+    @FXML
+    private Label AMPLabel5;
+    @FXML
+    private Label AMPLabel6;
+
+    @FXML
+    private Label RPMLabel1;
+    @FXML
+    private Label RPMLabel2;
+    @FXML
+    private Label RPMLabel3;
+    @FXML
+    private Label RPMLabel4;
+    @FXML
+    private Label RPMLabel5;
+    @FXML
+    private Label RPMLabel6;
+//</editor-fold>
+
+    /*private void closeMonitor(Event event) {
+    
+    if (MonitorTab.isSelected() && monitorCheck.isSelected() ) {
+    //System.out.println(" opened monitor");
+    model.setExtraPoll(true);
+    
+    } else {
+    
+    //System.out.println(" open configure");
+    model.setExtraPoll(false);
+    }
+    
+    }*/
+    private void setTargetRPM(ActionEvent event) {
+        double dTargetRPM = Double.parseDouble(targetRPM.getText());
+
+        model.setTargetRPM(dTargetRPM);
+
+        //System.out.println(model.getTargetRPM());
+    }
+
+    private void setMonitor(ActionEvent event) {
+
+        if (monitorCheck.isSelected()) {
+
+            model.setExtraPoll(true);
+
+        } else {
+
+            model.setExtraPoll(false);
+
+        }
+
+    }
+
+    private void setManual(ActionEvent event) {
+
+        if (manualCheck.isSelected()) {
+
+            model.setManual(true);
+
+            model.setPercentageToSend((int) manualSlider.getValue());
+
+            //System.out.println("manual ");
+        } else {
+
+            model.setManual(false);
+            // System.out.println("auto");
+
+        }
+
+    }
+
+    private void manualSpeed(MouseEvent event) {
+
+        //System.out.println("miuse event");
+        if (model.isManual()) {
+
+            try {
+                model.setPercentageToSend((int) manualSlider.getValue());
+                Thread.sleep(50);
+            } catch (InterruptedException ex) {
+                Logger.getLogger(FXMLViewController.class.getName()).log(Level.SEVERE, null, ex);
+            }
+
+        }
+
+    }
+
+    private void setTargetCPUTemp(ActionEvent event) {
+        double dTargetTemp = Double.parseDouble(targetCPUTemp.getText());
+
+        model.setTargetCPUTemp(dTargetTemp);
+
+        //System.out.println(model.getTargetTemp());
+    }
+    
+    private void setTargetGPUTemp(ActionEvent event) {
+        double dTargetTemp = Double.parseDouble(targetGPUTemp.getText());
+
+        model.setTargetGPUTemp(dTargetTemp);
+
+        //System.out.println(model.getTargetTemp());
+    }
+
+    private void setMinRPM(ActionEvent event) {
+        int dMinRPM = Integer.parseInt(minRPM.getText());
+
+        model.setMinRPM(dMinRPM);
+
+        // System.out.println(model.getMaxTemp());
+    }
+    
+    private void setMaxCPUTemp(ActionEvent event) {
+        double dMaxTemp = Double.parseDouble(maxCPUTemp.getText());
+
+        model.setMaxCPUTemp(dMaxTemp);
+
+        // System.out.println(model.getMaxTemp());
+    }
+    
+    private void setMaxGPUTemp(ActionEvent event) {
+        double dMaxTemp = Double.parseDouble(maxGPUTemp.getText());
+
+        model.setMaxGPUTemp(dMaxTemp);
+
+        // System.out.println(model.getMaxTemp());
+    }
+
+    private void setPort(Event event) {
+
+        String selectedPort = portMap.getSelectionModel().selectedItemProperty().getValue().toString();
+
+        model.getGrid().disconnect();
+
+        model.setGrid(selectedPort);
+
+    }
+
+    @Override
+    public void initialize(URL url, ResourceBundle rb) {
+        //MonitorTab.setOnSelectionChanged(this::closeMonitor);
+
+        monitorCheck.setOnAction(this::setMonitor);
+
+        targetRPM.setOnAction(this::setTargetRPM);
+
+        targetCPUTemp.setOnAction(this::setTargetCPUTemp);
+
+        maxCPUTemp.setOnAction(this::setMaxCPUTemp);
+        
+        targetGPUTemp.setOnAction(this::setTargetGPUTemp);
+
+        maxGPUTemp.setOnAction(this::setMaxGPUTemp);
+        
+        minRPM.setOnAction(this::setMinRPM);
+
+        manualCheck.setOnAction(this::setManual);
+
+        manualSlider.setMax(100);
+
+        manualSlider.setMin(0);
+
+        manualSlider.setOnMouseReleased(this::manualSpeed);
+
+        portMap.setOnAction(this::setPort);
+
+    }
+
+    /**
+     * This method sets the model for this controller. After the model is set
+     * certain UI elements are updated Finally a pollAndCompute Thread is
+     * started
+     *
+     * @param model the model to set
+     */
+    public void setModel(ComputerModel model) {
+
+        this.model = model;
+        //this.model.setGrid("COM5");
+
+        for (Object key : model.getGrid().getCommunicator().getPortMap().keySet()) {
+            portMap.getItems().add(key);
+        }
+
+        maxCPUTemp.setText(Double.toString(model.getMaxCPUTemp()));
+        maxGPUTemp.setText(Double.toString(model.getMaxGPUTemp()));
+
+        targetRPM.setText(Double.toString(model.getTargetRPM()));
+
+        targetCPUTemp.setText(Double.toString(model.getTargetCPUTemp()));
+        targetGPUTemp.setText(Double.toString(model.getTargetGPUTemp()));
+
+        minRPM.setText(Double.toString(model.getMinRPM()));
+
+        updateProperties();
+
+        //pollAndCompute poll = new pollAndCompute(model);
+        t = new Thread(this);
+        t.setDaemon(true);
+        t.start();
+    }
+
+    /**
+     * This method updates the values of some UI elements and binds properties
+     * to others
+     *
+     */
+    public void updateProperties() {
+        
+        DecimalFormat df = new DecimalFormat("#.##");      
+        
+        CPULabel.setText(df.format(model.getSensor().getCPUTemp()) + " °C");
+        
+        PowerLabel.setText(df.format(model.getGrid().getTotalWattage()) + " W");;
+        
+        CPULabelMax.setText(df.format(model.getSensor().getCpuMax()) + " °C Max");
+        
+        GPULabel.setText(df.format(model.getSensor().getGPUTemp()) + " °C");
+        
+        GPULabelMax.setText(df.format(model.getSensor().getGpuMax()) + " °C Max");
+        
+        VoltageLabel.setText(df.format(model.getGrid().getVoltage()) + " V");
+        
+        (RPMLabel1).setText(df.format(model.getGrid().getFanRPM(1)) + " RPM");
+
+        (RPMLabel2).setText(df.format(model.getGrid().getFanRPM(2)) + " RPM");
+
+        (RPMLabel3).setText(df.format(model.getGrid().getFanRPM(3)) + " RPM");
+
+        (RPMLabel4).setText(df.format(model.getGrid().getFanRPM(4)) + " RPM");
+
+        (RPMLabel5).setText(df.format(model.getGrid().getFanRPM(5)) + " RPM");
+
+        (RPMLabel6).setText(df.format(model.getGrid().getFanRPM(6)) + " RPM");
+
+        (AMPLabel1).setText(df.format(model.getGrid().getFanAMP(1)) + " A");
+
+        (AMPLabel2).setText(df.format(model.getGrid().getFanAMP(2)) + " A");
+
+        (AMPLabel3).setText(df.format(model.getGrid().getFanAMP(3)) + " A");
+
+        (AMPLabel4).setText(df.format(model.getGrid().getFanAMP(4)) + " A");
+
+        (AMPLabel5).setText(df.format(model.getGrid().getFanAMP(5)) + " A");
+
+        (AMPLabel6).setText(df.format(model.getGrid().getFanAMP(6)) + " A");
+
+    }
+
+    @Override
+    public void run() {
+
+        while (true) {
+
+            long milis = System.currentTimeMillis();
+
+            //System.out.println("Polling...");
+
+            model.poll();
+
+            Platform.runLater(() -> {
+                updateProperties();
+            });
+
+            //System.out.println("Computing...");
+            if (!model.isManual()) {
+                model.compute();
+            }
+
+            model.getGrid().setFanSpeed(model.getPercentageToSend());
+
+            try {
+                while (System.currentTimeMillis() - milis <= 1000) {
+                    Thread.sleep(50);
+                }
+            } catch (InterruptedException ex) {
+                System.out.println("Thread got interrupted");
+            }
+        }
+
+    }
+
+}

+ 290 - 0
src/camsucks/GRID.java

@@ -0,0 +1,290 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package camsucks;
+
+import java.util.HashMap;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+
+/**
+ *
+ * This is a model for the GRID controller 
+ * This class uses the communicator class to communicate to the GRID+ controller
+ * This class contains a few data members that contain data that can be gathered from the GRID+
+ * This class has some getters for these members 
+ * To update their values they each have poll functions instead of setters which send a command to the GRID and read the response
+ * To set the fan speed this class has a setFanSpeed method which sets the voltage of the GRID+ according to a percentage input.
+ * 
+ * This class also has a boolean check that checks if the voltage command is the same as the previous voltage command, this is to prevent pointless serial communication
+ * This check also has a getter to make it usable for other classes 
+ * 
+ * @author Roel
+ */
+public class GRID {
+    
+    private HashMap portMap = new HashMap();
+
+    private double voltage;
+
+    private int[] fanRPM;
+
+    private double[] fanAMP;
+    
+    private double totalWattage;
+
+    private byte[] lastCommand;
+
+    private Communicator communicator;
+    
+    private boolean same;
+
+    /**
+     * This constructor initialises  all members
+     * afterwards it opens a communicator at the selected port 
+     * @param selectedPort the com port to connect to
+     */
+    public GRID(String selectedPort) {
+        
+        totalWattage = 0;
+        lastCommand = new byte[7];
+        lastCommand[5] = 1;
+        lastCommand[6] = 2;
+        voltage = 0;
+        fanAMP = new double[7];
+        fanRPM = new int[7];
+        
+        /*for (int i = 0; i < fanRPM.length; i++) {
+        fanRPM[i] = new SimpleIntegerProperty(0);
+        }
+        
+        for (int i = 0; i < fanAMP.length; i++) {
+        fanAMP[i] = new SimpleDoubleProperty(0);
+        }*/
+        
+        communicator = new Communicator(selectedPort);
+        
+        communicator.searchForPorts();
+        
+        communicator.connect();
+
+        if (communicator.getConnected() == true) {
+           communicator.initIOStream();
+        }
+        
+    }
+
+    /**
+     *This method simply runs the disconnect method of the communicator
+     */
+    public void disconnect() {
+
+        if (getCommunicator().getConnected() == true) {
+            getCommunicator().disconnect();
+        }
+
+    }
+
+    /**
+     * @return the voltage
+     */
+    public double getVoltage() {
+        return voltage;
+    }
+
+    /**
+     * @return the fanRPM
+     */
+    public int[] getFanRPM() {
+        return fanRPM;
+    }
+
+    /**
+     * @return the fanAMP
+     */
+    public double[] getFanAMP() {
+        return fanAMP;
+    }
+
+    /**
+     * @param fan the index of the fan
+     * @return the fanRPM of the fan with index fan
+     */
+    public int getFanRPM(int fan) {
+        return fanRPM[fan - 1];
+    }
+
+    /**
+     * @param fan the index of the fan
+     * @return the fanAMP of the fan with index fan
+     */
+    public double getFanAMP(int fan) {
+        return fanAMP[fan - 1];
+    }
+
+    /**
+     * This method polls the Fan Amperage of the fan with index fan
+     * it first send the poll command and reads the byte data from the buffer
+     * this byte data is then converted to a double
+     * @param fan the index of the fan
+     */
+    public void pollFanAMP(int fan) {
+        if (getCommunicator().getConnected() == true) {
+            double tempAMP;
+            // 0x85 = -123
+            byte[] command = {-123, (byte) fan};
+
+            getCommunicator().writeData(command);
+            
+            tempAMP = (double) (((int) getCommunicator().getSecondToLast() & 0xFF) + (((double) (getCommunicator().getlast() & 0xff) / 100)));
+
+            fanAMP[fan - 1] = tempAMP;
+
+        } else {
+            fanAMP[fan - 1] =  0;
+        }
+    }
+
+    /**
+     * This method polls the Fan RPM of the fan with index fan
+     * it first send the poll command and reads the byte data from the buffer
+     * this byte data is then converted to an int
+     * @param fan the index of the fan
+     */
+    public void pollFanRPM(int fan) {
+        if (getCommunicator().getConnected() == true) {
+            // 0x8A = -118            
+
+            byte[] command = {-118, (byte) fan};
+
+            getCommunicator().writeData(command);
+
+            fanRPM[fan - 1] = (((int) (getCommunicator().getSecondToLast() & 0xFF) << 8) | ((getCommunicator().getlast() & 0xFF)));
+
+        } else {
+
+            fanRPM[fan - 1] =  0;
+
+        }
+    }
+
+    /**
+     * This method polls the voltage of all the fans 
+     * it first send the poll command and reads the byte data from the buffer
+     * this byte data is then converted to a double
+     */
+    public void pollVoltage() {
+        if (getCommunicator().getConnected() == true) {
+            // 0x84 = -124
+            byte[] command = {-124, 0x00};
+
+            getCommunicator().writeData(command);
+
+            voltage = (double) (((int) getCommunicator().getSecondToLast() & 0xFF) + (((double) (getCommunicator().getlast() & 0xff) / 100)));
+        } else {
+            voltage = 0;
+        }
+    }
+
+    /**
+     * this method calculates the voltage to set the fans at according to a percentage of the maximum voltage 12V
+     * then it send the command to set that voltage
+     * the voltages between 0 and 4 are not recognised so these are converted to 4V
+     * the comma value of the commands are always rounded to .50 and .0
+     * @param percent
+     */
+    public void setFanSpeed(int percent) {
+
+        if (getCommunicator().getConnected() == true) {
+            int firstByte, lastByte, wantedVoltage;
+            
+            //The voltages between 0 and 4 are not recognised by the grid so any voltage under 4 will still be 4 and from 0 it will be 0
+            if (percent <= 0) {
+                firstByte = 0;
+                lastByte = 0;
+            
+            } else if (percent < 34) {
+                firstByte = 4;
+                lastByte = 0;
+            }else{
+
+                wantedVoltage = (1200 * percent) / 100;
+                firstByte = wantedVoltage / 100;
+                lastByte = (wantedVoltage - (firstByte * 100));
+
+                if (lastByte < 50) {
+                    lastByte = 0x00;
+                } else {
+                    lastByte = 0x50;
+                }
+
+            }
+
+            byte[] command = {0x44, 0x00, -64, 0x00, 0x00, (byte) firstByte, (byte) lastByte};
+            
+            if ((lastCommand[5] != command[5] || lastCommand[6] != command[6])) {
+                getCommunicator().writeData(command);
+                lastCommand = command;
+                setSame(false);
+            } else {
+                setSame(true);
+            }
+            
+            
+
+        }
+
+    }
+
+    /*@Override
+    protected void finalize() throws Throwable {
+    
+    try {
+    disconnect();
+    } finally {
+    super.finalize();
+    }
+    
+    }*/
+    
+    /**
+     * @return the communicator
+     */
+    public Communicator getCommunicator() {
+        return communicator;
+    }
+
+    /**
+     * 
+     * @return A boolean value that is true if the last command is the same as the new
+     */
+    public boolean isSame() {
+        return same;
+    }
+
+    /**
+     * @param same boolean value that is true if the last command is the same as the new
+     */
+    public void setSame(boolean same) {
+        this.same = same;
+    }
+
+    /**
+     * @return the totalWattage
+     */
+    public double getTotalWattage() {
+        totalWattage = 0;
+        for(double tempAMP : fanAMP ){
+            totalWattage = totalWattage + tempAMP*getVoltage();
+            
+        }
+        
+        
+        
+        
+        return totalWattage;
+    }
+
+}

二進制
src/camsucks/NoCAMIcon.jpg


+ 156 - 0
src/camsucks/Sensor.java

@@ -0,0 +1,156 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package camsucks;
+
+
+/**
+ *
+ * This class is a model for a collection of sensors
+ * It contains a datamembers for some specific sensors as well as members for lists of sensors of a specific type.
+ * each member has a getter but no setter.
+ * Instead of the setter the members (except for the lists) have a poll function which polls the value of the member using the jWMI class 
+ * 
+ * @author Roel
+ */
+public class Sensor {
+    private String[] temperatureSensorList;
+    private String[] loadSensorList;
+    private int cpuCoreNumber;
+    private double cpuLoad;
+    private double cpuPackageTemp;
+    private double cpuMax;
+    private double gpuTemp;
+    private double gpuMax;
+
+    /**
+     * The constructor for this class
+     * At the start the lists of sensors are made with the help of the jWMI class
+     * Then the number of cores of the system is calculated
+     * 
+     * @throws Exception when the WMI value can't be obtained
+     */
+    public Sensor() throws Exception {
+        
+        temperatureSensorList = jWMI.getWMISensorList("Temperature").split(", ");
+        loadSensorList = jWMI.getWMISensorList("Load").split(", ");
+        
+        //Init for cpuCoreNumber
+        cpuCoreNumber = 0;
+        for (String temperatureSensorList1 : loadSensorList) {
+            if (temperatureSensorList1.contains("CPU Core #")) {
+                int tempNumber;
+                // gets the int value of the number after #
+                tempNumber = Integer.parseInt(temperatureSensorList1.substring(10, 11).trim());
+                if (tempNumber > cpuCoreNumber) {
+                    cpuCoreNumber = tempNumber;
+                }
+            }
+        }
+
+    }
+
+    /**
+     *  This method polls the value of the CPU Load sensor
+     * @throws Exception
+     */
+    public void pollCPULoad() throws Exception {
+        cpuLoad = Double.parseDouble(jWMI.getWMIValue("Load", "CPU Total"));
+
+    }
+    
+    /**
+     *  This method polls the value of the CPU Load sensor
+     * @throws Exception
+     */
+    public void pollCPUMax() throws Exception {
+        cpuMax = Double.parseDouble(jWMI.getWMIValue("Temperature", "CPU Core", "Max"));
+
+    }
+    
+    /**
+     *  This method polls the value of the CPU Load sensor
+     * @throws Exception
+     */
+    public void pollGPUMax() throws Exception {
+        gpuMax = Double.parseDouble(jWMI.getWMIValue("Temperature", "GPU Core", "Max"));
+
+    }
+    
+    /**
+     * This method polls the value of the GPU Temperature sensor
+     * @throws Exception
+     */
+    public void pollGPUTemp() throws Exception {
+        gpuTemp = Double.parseDouble(jWMI.getWMIValue("Temperature", "GPU Core"));
+
+    }
+
+    /**
+     * This method polls the value of the CPU Temperature sensor
+     * @throws Exception
+     */
+    public void pollCPUTemp() throws Exception {
+        cpuPackageTemp = Double.parseDouble(jWMI.getWMIValue("Temperature", "CPU Core"));
+    }
+
+    /**
+     * @return the temperatureSensorList
+     */
+    public String[] getTemperatureSensorList() {
+        return temperatureSensorList;
+    }
+
+    /**
+     * @return the loadSensorList
+     */
+    public String[] getLoadSensorList() {
+        return loadSensorList;
+    }
+
+    /**
+     * @return the cpuCoreNumber
+     */
+    public int getCpuCoreNumber() {
+        return cpuCoreNumber;
+    }
+
+   
+    /**
+     * @return the cpuLoad
+     */
+    public double getCpuLoad() {
+        return cpuLoad;
+    }
+
+    /**
+     * @return the cpuPackageTemp
+     */
+    public double getCPUTemp() {
+        return cpuPackageTemp;
+    }
+
+    /**
+     * @return the gpuTemp
+     */
+    public double getGPUTemp() {
+        return gpuTemp;
+    }
+
+    /**
+     * @return the cpuMax
+     */
+    public double getCpuMax() {
+        return cpuMax;
+    }
+    
+     /**
+     * @return the cpuMax
+     */
+    public double getGpuMax() {
+        return gpuMax;
+    }
+
+}

+ 295 - 0
src/camsucks/jWMI.java

@@ -0,0 +1,295 @@
+package camsucks;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+/**
+ * File: jWMI.java Date: 12/21/09 Author: Shaun Henry Copyright Henry Ranch LLC
+ * 2009-2010. All rights reserved. http://www.henryranch.net
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * + You must provide a link back to www.henryranch.net in any software or
+ * website which uses this software. + Redistributions of source code must
+ * retain the above copyright notice, this list of conditions and the following
+ * disclaimer. + Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution. +
+ * Neither the name of the HenryRanch LCC nor the names of its contributors nor
+ * authors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS, OWNERS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * A java bridge for querying the WMI interface.
+ *
+ * @author Copyright 2009-2010 HenryRanch LLC. Author Shaun Henry, 2009-2010.
+ * @version 1.0
+ * 
+ * Changes by Roel:     rewritten the getVBScript function and added two variants getVBScriptList and getVBScriptValue to work with the OpenHardwareMonitor WMI Classes
+ *                      rewritten the getWMIValue and added another variant getWMISensorList
+ *                      Changed some demoqueries for testing purposes.
+ *                      
+ *                 
+ *
+ */
+public class jWMI {
+
+    private static final String CRLF = "\r\n";
+
+    /**
+     * Generate a VBScript string capable of querying the desired WMI
+     * information.
+     *  In this case a list of sensors of a certain type
+     * @param SensorType The sensorType to get a list form.
+     * <br>i.e. "Load"
+     * @return the vbscript string.
+     *
+     */
+    private static String getVBScriptList(String SensorType) {
+        String vbs = "strComputer = \".\"" + CRLF;
+        vbs += "strNameSpace = \"root\\OpenHardwareMonitor\"" + CRLF;
+        vbs += "Dim oWMI : Set oWMI = GetObject(\"winmgmts:{impersonationLevel=impersonate}!\\\\\" & strComputer & \"\\\" & strNameSpace )" + CRLF;
+        vbs += "Dim classComponent : Set classComponent = oWMI.ExecQuery(\"Select * from Sensor\")" + CRLF;
+        vbs += "Dim obj, strData" + CRLF;
+        vbs += "For Each obj in classComponent" + CRLF;
+        vbs += "    If obj.SensorType = \"" + SensorType + "\" then" + CRLF;
+        vbs += "            strData = strData & obj.Name & \", \" " + CRLF;
+        vbs += "    End If" + CRLF;
+        vbs += "Next" + CRLF;
+        vbs += "wscript.echo strData" + CRLF;
+        return vbs;
+    }
+
+    
+    
+    /**
+     * Generate a VBScript string capable of querying the desired WMI
+     * information.
+     * 
+     * in this case the the Value of a sensor with name name and sensor type SensorType
+     *
+     * @param SensorType The type of the sensor
+     * <br>i.e. "Temperature"
+     * @param name the name of the sensor
+     * <br>i.e. "CPU Package"
+     * @return the vbscript string.
+     *
+     */
+    private static String getVBScriptValue(String SensorType, String name) {
+        String vbs = "strComputer = \".\"" + CRLF;
+        vbs += "strNameSpace = \"root\\OpenHardwareMonitor\"" + CRLF;
+        vbs += "Dim oWMI : Set oWMI = GetObject(\"winmgmts:{impersonationLevel=impersonate}!\\\\\" & strComputer & \"\\\" & strNameSpace )" + CRLF;
+        vbs += "Dim classComponent : Set classComponent = oWMI.ExecQuery(\"Select * from Sensor\")" + CRLF;
+        vbs += "Dim obj, strData" + CRLF;
+        vbs += "For Each obj in classComponent" + CRLF;
+        vbs += "    If obj.SensorType = \"" + SensorType + "\" then" + CRLF;
+        vbs += "        If obj.Name = \"" + name + "\" then" + CRLF;
+        vbs += "            strData = strData & obj.Value" + CRLF;
+        vbs += "        End If" + CRLF;
+        vbs += "    End If" + CRLF;
+        vbs += "Next" + CRLF;
+        vbs += "wscript.echo strData" + CRLF;
+        return vbs;
+    }
+    
+    /**
+     * Generate a VBScript string capable of querying the desired WMI
+     * information.
+     * 
+     * in this case the the Value of a sensor with name name and sensor type SensorType
+     *
+     * @param SensorType The type of the sensor
+     * <br>i.e. "Temperature"
+     * @param name the name of the sensor
+     * <br>i.e. "CPU Package"
+     * @return the vbscript string.
+     *
+     */
+    
+    private static String getVBScriptValue(String SensorType, String name, String value) {
+        String vbs = "strComputer = \".\"" + CRLF;
+        vbs += "strNameSpace = \"root\\OpenHardwareMonitor\"" + CRLF;
+        vbs += "Dim oWMI : Set oWMI = GetObject(\"winmgmts:{impersonationLevel=impersonate}!\\\\\" & strComputer & \"\\\" & strNameSpace )" + CRLF;
+        vbs += "Dim classComponent : Set classComponent = oWMI.ExecQuery(\"Select * from Sensor\")" + CRLF;
+        vbs += "Dim obj, strData" + CRLF;
+        vbs += "For Each obj in classComponent" + CRLF;
+        vbs += "    If obj.SensorType = \"" + SensorType + "\" then" + CRLF;
+        vbs += "        If obj.Name = \"" + name + "\" then" + CRLF;
+        vbs += "            strData = strData & obj." + value + CRLF;
+        vbs += "        End If" + CRLF;
+        vbs += "    End If" + CRLF;
+        vbs += "Next" + CRLF;
+        vbs += "wscript.echo strData" + CRLF;
+        return vbs;
+    }
+    
+    /**
+     * Get an environment variable from the windows OS
+     *
+     * @param envVarName the name of the env var to get
+     * @return the value of the env var
+     * @throws Exception if the given envVarName does not exist
+     *
+     */
+    private static String getEnvVar(String envVarName) throws Exception {
+        String varName = "%" + envVarName + "%";
+        String envVarValue = execute(new String[]{"cmd.exe", "/C", "echo " + varName});
+        if (envVarValue.equals(varName)) {
+            throw new Exception("Environment variable '" + envVarName + "' does not exist!");
+        }
+        return envVarValue;
+    }
+
+    /**
+     * Write the given data string to the given file
+     *
+     * @param filename the file to write the data to
+     * @param data a String ofdata to be written into the file
+     * @throws Exception if the output file cannot be written
+     *
+     */
+    private static void writeStrToFile(String filename, String data) throws Exception {
+        FileWriter output = new FileWriter(filename);
+        output.write(data);
+        output.flush();
+        output.close();
+        output = null;
+    }
+
+    /**
+     * Get the given List of WMI names of sensors of sensor type SensorType
+     *
+     * @param SensorType The type of sensor to get a list of
+     * @return a comma separated list of the sensors
+     * @throws Exception if there is a problem obtaining the value
+     *
+     */
+    public static String getWMISensorList(String SensorType) throws Exception {
+        String vbScript = getVBScriptList(SensorType);
+        String tmpDirName = getEnvVar("TEMP").trim();
+        String tmpFileName = tmpDirName + File.separator + "jwmi.vbs";
+        writeStrToFile(tmpFileName, vbScript);
+        String output = execute(new String[]{"cmd.exe", "/C", "cscript.exe", tmpFileName});
+        new File(tmpFileName).delete();
+
+        return output.trim();
+    }
+     /**
+     * Get the given WMI value from sensor with name nam and type SensorType
+     *
+     * @param SensorType the type of the sensor
+     * @param name the name of the sensor
+     * @return the value of the sensor
+     * @throws Exception if there is a problem obtaining the value
+     *
+     */
+    public static String getWMIValue(String SensorType, String name) throws Exception {
+        String vbScript = getVBScriptValue(SensorType, name);
+        String tmpDirName = getEnvVar("TEMP").trim();
+        String tmpFileName = tmpDirName + File.separator + "jwmi.vbs";
+        writeStrToFile(tmpFileName, vbScript);
+        String output = execute(new String[]{"cmd.exe", "/C", "cscript.exe", tmpFileName});
+        new File(tmpFileName).delete();
+
+        return output.trim();
+    }
+    
+    /**
+     * Get the given WMI value from sensor with name nam and type SensorType
+     *
+     * @param SensorType the type of the sensor
+     * @param name the name of the sensor
+     * @return the value of the sensor
+     * @throws Exception if there is a problem obtaining the value
+     *
+     */
+    public static String getWMIValue(String SensorType, String name, String value) throws Exception {
+        String vbScript = getVBScriptValue(SensorType, name, value);
+        String tmpDirName = getEnvVar("TEMP").trim();
+        String tmpFileName = tmpDirName + File.separator + "jwmi.vbs";
+        writeStrToFile(tmpFileName, vbScript);
+        String output = execute(new String[]{"cmd.exe", "/C", "cscript.exe", tmpFileName});
+        new File(tmpFileName).delete();
+
+        return output.trim();
+    }
+
+    /**
+     * Execute the application with the given command line parameters.
+     *
+     * @param cmdArray an array of the command line params
+     * @return the output as gathered from stdout of the process
+     * @throws an Exception upon encountering a problem
+     *
+     */
+    private static String execute(String[] cmdArray) throws Exception {
+        Process process = Runtime.getRuntime().exec(cmdArray);
+        BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
+        String output = "";
+        String line = "";
+        while ((line = input.readLine()) != null) {
+            //need to filter out lines that don't contain our desired output
+            if (!line.contains("Microsoft") && !line.equals("")) {
+                output += line + CRLF;
+            }
+        }
+        process.destroy();
+        process = null;
+        return output.trim();
+    }
+
+    /**
+     *
+     */
+    public static void executeDemoQueries() {
+        try {
+            System.out.println(Float.parseFloat(getWMIValue("Temperature", "CPU Core #3")));
+            System.out.println(getWMISensorList("Temperature"));
+            //System.out.println(getWMIValue("Select Description from Win32_PnPEntity", "Description"));
+            //System.out.println(getWMIValue("Select Description, Manufacturer from Win32_PnPEntity", "Description,Manufacturer"));
+            //System.out.println(getWMIValue("Select * from Win32_Service WHERE State = 'Stopped'", "Name"));
+            //this will return everything since the field is incorrect and was not used to a filter
+            //System.out.println(getWMIValue("Select * from Win32_Service", "Name"));
+            //this will return nothing since there is no field specified
+            //System.out.println(getWMIValue("Select Name from Win32_ComputerSystem", ""));
+            //this is a failing case where the Win32_Service class does not contain the 'Name' field
+            //System.out.println(getWMIValue("Select * from Win32_Service", "Name"));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     *
+     * @param argv
+     */
+    public static void main(String[] argv) {
+        try {
+            if (argv.length == 0) {
+                executeDemoQueries();
+            } else if (argv.length == 2) {
+                System.out.println(getWMIValue(argv[0], argv[1]));
+            } else {
+                System.out.println("Usage: jWMI <WmiQuery> <desiredFields>");
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
+//WMI class definitions below here: 
+

+ 81 - 0
src/camsucks/pollAndCompute.java

@@ -0,0 +1,81 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package camsucks;
+
+import javafx.application.Platform;
+
+/**
+ *
+ * This class, when run, enters an infinite loop. This loop will if the right
+ * conditions are met first poll the FAN RPM and FAN AMP of each fan Then it
+ * will poll the CPU temperature After that if the program isn't set to manual
+ * the loop will compute the percentage to send according to the cpu temperature
+ * as last step this loop will send the desired percentage to the fans (this
+ * could be the computed or the manually chosen percentage
+ *
+ * @author Roel
+ */
+class pollAndCompute implements Runnable {
+
+    private ComputerModel model;
+
+    private Thread t;
+
+    private int ticks;
+
+    public pollAndCompute(ComputerModel model) {
+        ticks = 0;
+        this.model = model;
+    }
+
+    @Override
+    public void run() {
+
+        while (true) {
+
+            //System.out.println("Polling...");
+            if (model.isExtraPoll()) {
+                ticks++;
+                if (!model.getGrid().isSame() || ticks > 2) {
+                    //System.out.println("polling some extra shit ");
+                    ticks = 0;
+                    Platform.runLater(() -> {
+                        for (int i = 1; i < 7; i++) {
+                            model.getGrid().pollFanAMP(i);
+                            model.getGrid().pollFanRPM(i);
+
+                        }
+                    });
+                }
+
+            }
+
+            model.poll();
+
+            //System.out.println("Computing...");
+            if (!model.isManual()) {
+                model.compute();
+            }
+
+            model.getGrid().setFanSpeed(model.getPercentageToSend());
+            
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException ex) {
+                System.out.println("Thread got interrupted");
+            }
+        }
+
+    }
+
+    /*public void start(){
+        System.out.println("Starting");
+        if(t == null){
+            t = new Thread(this, "Poll and Compute");
+            t.start();
+        }
+    }*/
+}