Modbus & Database from features
add last week work. Work about database & modbus. TODO: field, web & smart parts
This commit is contained in:
		
							
								
								
									
										3
									
								
								src/config.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/config.properties
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| URL    = https://influx.sdi.hevs.ch | ||||
| ORG    = SIn15 | ||||
| BUCKET = SIn15 | ||||
| @@ -1,5 +1,8 @@ | ||||
| package ch.hevs.isi; | ||||
|  | ||||
| import ch.hevs.isi.core.BooleanDataPoint; | ||||
| import ch.hevs.isi.core.FloatDataPoint; | ||||
| import ch.hevs.isi.db.DatabaseConnector; | ||||
| import ch.hevs.isi.utils.Utility; | ||||
|  | ||||
| public class MinecraftController { | ||||
| @@ -74,5 +77,20 @@ public class MinecraftController { | ||||
|  | ||||
|         // Start coding here ... | ||||
|  | ||||
|         // Initialize the database connector | ||||
|         if((dbProtocol != null) && (dbHostName != null)){ | ||||
|             DatabaseConnector.url = dbProtocol+ "://" + dbHostName; | ||||
|             DatabaseConnector.getMySelf().initialize(dbProtocol+ "://" + dbHostName); | ||||
|         } | ||||
|         if(dbName != null){ | ||||
|             DatabaseConnector.org = dbName; | ||||
|         } | ||||
|         if(dbUserName != null){ | ||||
|             DatabaseConnector.bucket = dbUserName; | ||||
|         } | ||||
|  | ||||
|         // Initialize the Modbus TCP connector | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,8 +25,8 @@ public class BooleanDataPoint extends DataPoint{ | ||||
|     public String toString(){ | ||||
|         String s; | ||||
|         s = this.getLabel(); | ||||
|         s += ": "; | ||||
|         s += this.getValue(); | ||||
|         s += "="; | ||||
|         s += this.getValue() ? "1" : "0"; | ||||
|         return s; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,7 +25,7 @@ public class FloatDataPoint extends DataPoint{ | ||||
|     public String toString(){ | ||||
|         String s; | ||||
|         s = this.getLabel(); | ||||
|         s += ": "; | ||||
|         s += "="; | ||||
|         s += this.getValue(); | ||||
|         return s; | ||||
|     } | ||||
|   | ||||
| @@ -1,32 +1,190 @@ | ||||
| package ch.hevs.isi.db; | ||||
|  | ||||
|  | ||||
| import ch.hevs.isi.MinecraftController; | ||||
| import ch.hevs.isi.core.DataPoint; | ||||
| import ch.hevs.isi.core.DataPointListener; | ||||
| import ch.hevs.isi.core.FloatDataPoint; | ||||
| import ch.hevs.isi.utils.Utility; | ||||
|  | ||||
| import java.io.FileInputStream; | ||||
| import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.io.OutputStreamWriter; | ||||
| import java.net.HttpURLConnection; | ||||
| import java.net.MalformedURLException; | ||||
| import java.net.ProtocolException; | ||||
| import java.net.URL; | ||||
| import java.util.Properties; | ||||
|  | ||||
|  | ||||
| public class DatabaseConnector implements DataPointListener { | ||||
|     private Properties properties = new Properties();           // Properties of the config.properties file | ||||
|     private String fullURL = null;                              // Full URL of the InfluxDB server | ||||
|     private URL urlForWrite = null;                             // URL of the InfluxDB server | ||||
|     private HttpURLConnection con = null;                       // Connection to the InfluxDB server | ||||
|     private boolean initialized = false;                        // Boolean to know if the database connector is initialized | ||||
|     private TimeManager _timeManager = new TimeManager(3); // Time manager to manage the time of the data points | ||||
|     private long _timestamp = 0;                                // Timestamp of the data points | ||||
|     String default_token = System.getenv("SECRET_TOKEN"); // Token to access the InfluxDB server | ||||
|     public static String url = null;                            // URL of the InfluxDB server | ||||
|     public static String org = null;                            // Organization of the InfluxDB server | ||||
|     public static String bucket = null;                         // Bucket of the InfluxDB server | ||||
|  | ||||
|  | ||||
|     private static DatabaseConnector  mySelf = null; | ||||
|  | ||||
|     /** | ||||
|      * Private constructor of the database connector | ||||
|      * Read the config.properties file and initialize the database connector | ||||
|      */ | ||||
|     private DatabaseConnector (){ | ||||
|         // Read the config.properties file | ||||
|         try (InputStream input = new FileInputStream("src/config.properties")) { | ||||
|             properties.load(input); | ||||
|         } catch (Exception e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|  | ||||
|         // Get the URL, the organization and the bucket from the config.properties file if their are null | ||||
|         if (url == null){ | ||||
|             url = properties.getProperty("URL"); | ||||
|         } | ||||
|         if (org == null){ | ||||
|             org = properties.getProperty("ORG"); | ||||
|         } | ||||
|         if (bucket == null){ | ||||
|             bucket = properties.getProperty("BUCKET"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the instance of the database connector | ||||
|      * @return The instance of the database connector | ||||
|      */ | ||||
|     public static DatabaseConnector  getMySelf(){ | ||||
|         if (mySelf == null){ | ||||
|             mySelf = new DatabaseConnector(); | ||||
|         } | ||||
|         return mySelf; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Initialize the database connector | ||||
|      * @param url URL of the database. If null take the URL from the config.properties file | ||||
|      */ | ||||
|     public void initialize(String url){ | ||||
|         try{ | ||||
|             if(urlForWrite == null){ | ||||
|                 if(url == null){ | ||||
|                     url = properties.getProperty("URL"); | ||||
|                 } | ||||
|                 fullURL = url + "/api/v2/write?org=" + org + "&bucket=" + bucket; | ||||
|                 Utility.pDebug("URL: " + fullURL); | ||||
|                 urlForWrite = new URL(fullURL); | ||||
|             } | ||||
|             initialized = true; | ||||
|         } catch (MalformedURLException e) { | ||||
|             throw new RuntimeException(e); | ||||
|         } | ||||
|  | ||||
|         // If the user want to erase the previous data in the database | ||||
|         // Delete the previous data | ||||
|         if(MinecraftController.ERASE_PREVIOUS_DATA_INB_DB){ | ||||
|             try { | ||||
|                 // Create the URL to delete the data | ||||
|                 fullURL = url + "/api/v2/delete?org=" + org + "&bucket=" + bucket; | ||||
|                 Utility.pDebug("URL: " + fullURL); | ||||
|                 URL urlForDelete = new URL(fullURL); | ||||
|                 // Create the connection to the database | ||||
|                 con = (HttpURLConnection) urlForDelete.openConnection(); | ||||
|                 con.setRequestMethod("POST"); | ||||
|                 con.setRequestProperty("Authorization", "Token " + default_token); | ||||
|                 con.setRequestProperty("Content-Type", "text/plain"); | ||||
|                 con.setRequestProperty("Accept", "application/json"); | ||||
|                 con.setDoOutput(true); | ||||
|             } catch (MalformedURLException e) { | ||||
|                 throw new RuntimeException(e); | ||||
|             } catch (ProtocolException e) { | ||||
|                 throw new RuntimeException(e); | ||||
|             } catch (IOException e) { | ||||
|                 throw new RuntimeException(e); | ||||
|             } | ||||
|  | ||||
|             // Create the data to send to delete the data in the database and send it | ||||
|             String data = "{\n"; | ||||
|             data += "\"start\": \"2000-01-01T00:00:00Z\",\n"; | ||||
|             data += "\"stop\": \"2024-04-25T10:45:00Z\",\n"; | ||||
|             data += "\"predicate\": \"_measurement=\\\"Minecraft\\\"\"\n"; | ||||
|             data += "}"; | ||||
|             Utility.pDebug(data); | ||||
|             sendDataToDatabase(data); | ||||
|             fullURL=null;   // Reset the full URL | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void pushToDatabase(DataPoint dp){ | ||||
|         System.out.println(dp.toString() + " -> Database"); | ||||
|     /** | ||||
|      * Push the data point to the database | ||||
|      * @param dp Data point to push | ||||
|      */ | ||||
|     private void pushToDatabase(DataPoint dp) { | ||||
|         // Initialize the database connector if not already done | ||||
|         if(initialized == false){ | ||||
|             initialize(null); | ||||
|         } | ||||
|  | ||||
|         // Create the data to send | ||||
|         String data = "Minecraft "; | ||||
|         data += dp.toString(); | ||||
|         data += " " + _timestamp; | ||||
|  | ||||
|         // Send the data to the database | ||||
|         sendDataToDatabase(data); | ||||
|     } | ||||
|  | ||||
|     private void sendDataToDatabase(String data){ | ||||
|         try { | ||||
|             // Create connection and set headers | ||||
|             if(con == null){ | ||||
|                 con = (HttpURLConnection) urlForWrite.openConnection(); | ||||
|                 con.setRequestMethod("POST"); | ||||
|                 con.setRequestProperty("Authorization", "Token " + default_token); | ||||
|                 con.setRequestProperty("Content-Type", "application/json"); | ||||
|                 con.setDoOutput(true); | ||||
|             } | ||||
|  | ||||
|             // Send data | ||||
|             OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream()); | ||||
|             writer.write(data); | ||||
|             writer.flush(); | ||||
|  | ||||
|             // Get response code | ||||
|             int respondCode = con.getResponseCode(); | ||||
|             if(respondCode != 204){ | ||||
|                 System.out.println("Error: " + respondCode + ", Data: " + data); | ||||
|             } else { | ||||
|                 System.out.println(data + " -> Database"); | ||||
|             } | ||||
|             con.disconnect(); | ||||
|             con = null; | ||||
|         } catch (IOException e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Push the data point to the database | ||||
|      * @param dp Data point to push | ||||
|      */ | ||||
|     @Override | ||||
|     public void onNewValue(DataPoint dp) { | ||||
|         pushToDatabase(dp); | ||||
|         if(dp.getLabel().equals("CLOAK_FLOAT")) { | ||||
|             FloatDataPoint fdp = (FloatDataPoint) dp; | ||||
|             _timeManager.setTimestamp(fdp.getValue()); | ||||
|             _timestamp = _timeManager.getNanosForDB(); | ||||
|         } | ||||
|         if(_timestamp != 0){ | ||||
|             pushToDatabase(dp); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										159
									
								
								src/main/java/ch/hevs/isi/field/ModbusAccessor.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								src/main/java/ch/hevs/isi/field/ModbusAccessor.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| package ch.hevs.isi.field; | ||||
|  | ||||
| import com.serotonin.modbus4j.ModbusFactory; | ||||
| import com.serotonin.modbus4j.ModbusMaster; | ||||
| import com.serotonin.modbus4j.code.DataType; | ||||
| import com.serotonin.modbus4j.exception.ErrorResponseException; | ||||
| import com.serotonin.modbus4j.exception.ModbusInitException; | ||||
| import com.serotonin.modbus4j.exception.ModbusTransportException; | ||||
| import com.serotonin.modbus4j.ip.IpParameters; | ||||
| import com.serotonin.modbus4j.ip.tcp.TcpMaster; | ||||
| import com.serotonin.modbus4j.locator.BaseLocator; | ||||
|  | ||||
| public class ModbusAccessor { | ||||
|  | ||||
|     private static ModbusAccessor mySelf = null;        //instance of the class | ||||
|     private ModbusMaster master = null;                 //instance of the modbus master | ||||
|  | ||||
|     /** | ||||
|      * private constructor | ||||
|      * */ | ||||
|     private ModbusAccessor(){ | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * static method to create a singleton pattern of the class | ||||
|      * checks if an instance of the class is already made | ||||
|      * if not, it creates an instance of the class ModbusAccessor | ||||
|      * @return instance of ModbusAccessor | ||||
|      */ | ||||
|     public static ModbusAccessor getMySelf(){ | ||||
|         if (mySelf == null){                    //if no instance is created | ||||
|             mySelf = new ModbusAccessor();      //create a new instance of the class | ||||
|         } | ||||
|         return mySelf;                          //if there is already an instance, return the existing instance | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      *  this method creates a ModbusFactory and a TCPMaster | ||||
|      *  it also initialize the Master | ||||
|      * @param ipAddress IP-address of the server | ||||
|      * @param port port of the server | ||||
|      */ | ||||
|     public void connect(String ipAddress, int port){ | ||||
|         ModbusFactory MF = new ModbusFactory();         //create a modbus factory | ||||
|         IpParameters IpParams = new IpParameters();     //create the ip parameters | ||||
|  | ||||
|         IpParams.setHost(ipAddress);        //set the server ip-address | ||||
|         IpParams.setPort(port);             //set the server port | ||||
|  | ||||
|         //create the TCPMaster with the correct IP parameters and keep it alive | ||||
|         this.master = MF.createTcpMaster(IpParams, true); | ||||
|         try { | ||||
|             this.master.init();         //initialize the master | ||||
|         } | ||||
|         catch(ModbusInitException e) | ||||
|         { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|     } | ||||
|     //methods to write | ||||
|     /** | ||||
|      * method to write a boolean value in the correct modbus register | ||||
|      * @param register address of the register | ||||
|      * @param value the desired boolean value | ||||
|      * @throws ModbusTransportException | ||||
|      * @throws ErrorResponseException | ||||
|      */ | ||||
|     public void writeBoolean (int register, boolean value) throws ModbusTransportException, ErrorResponseException{ | ||||
|         //first parameter = slaveID = always 1 because we only have one | ||||
|         //second parameter = register address ==> it is named offset because it starts with 0 and goes to the desired address | ||||
|         this.master.setValue(BaseLocator.coilStatus(1,register), value); //set the desired value in the correct register | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * method to write a float value in the correct modbus register | ||||
|      * @param register address of the register | ||||
|      * @param value the desired float value | ||||
|      * @throws ModbusTransportException | ||||
|      * @throws ErrorResponseException | ||||
|      */ | ||||
|     public void writeFloat (int register, float value) throws ModbusTransportException, ErrorResponseException{ | ||||
|         //first parameter = slaveID = always 1 because we only have one | ||||
|         //second parameter = register address ==> it is named offset because it starts with 0 and goes to the desired address | ||||
|         //third parameter = DataType of the value. The max value is 3 Byte Float, but the class DataType has only 2 or 4 byte | ||||
|         this.master.setValue(BaseLocator.holdingRegister(1,register,DataType.FOUR_BYTE_FLOAT), value); | ||||
|     } | ||||
|     //methods to read | ||||
|     /** | ||||
|      * method to read a boolean value in the correct modbus register | ||||
|      * get the coil status of the register. | ||||
|      * @param register address of register | ||||
|      * @return boolean value of the desired register | ||||
|      * @throws ModbusTransportException | ||||
|      * @throws ErrorResponseException | ||||
|      */ | ||||
|     public boolean readBoolean(int register) throws ModbusTransportException, ErrorResponseException { | ||||
|         //first parameter = slaveID = always 1 because we only have one | ||||
|         //second parameter = register address ==> it is named offset because it starts with 0 and goes to the desired address | ||||
|         boolean booleanValue = this.master.getValue(BaseLocator.coilStatus(1,register)); | ||||
|         return booleanValue; | ||||
|     } | ||||
|     /** | ||||
|      * method to read a boolean value in the correct modbus register | ||||
|      * get the value from the holding register | ||||
|      * @param register address of the register | ||||
|      * @return float value of the desired register | ||||
|      * @throws ModbusTransportException | ||||
|      * @throws ErrorResponseException | ||||
|      */ | ||||
|     public float readFloat(int register)throws ModbusTransportException, ErrorResponseException{ | ||||
|         //first parameter = slaveID = always 1 because we only have one | ||||
|         //second parameter = register address ==> it is named offset because it starts with 0 and goes to the desired address | ||||
|         //third parameter = DataType of the value. The max value is 3 Byte Float, but the class DataType has only 2 or 4 byte | ||||
|         float floatValue = (float) this.master.getValue(BaseLocator.holdingRegister(1,register, DataType.FOUR_BYTE_FLOAT)); | ||||
|         return floatValue; | ||||
|     } | ||||
|     /** | ||||
|      * this main method is only for testing the ModbusAccessor class | ||||
|      * @param args | ||||
|      */ | ||||
|     public static void main(String[] args) { | ||||
|         //create an instance of ModbusAccessor and connect it with the server | ||||
|         ModbusAccessor test = ModbusAccessor.getMySelf(); | ||||
|         test.connect("LocalHost", 1502); | ||||
|         //do this all the time | ||||
|         while(true){ | ||||
|             try{ | ||||
|                 //get a boolean value => solar SetPoint | ||||
|                 boolean solar_connect_st = test.readBoolean(609);           //SOLAR_CONNECT_ST | ||||
|                 //get a float value => factory SetPoint | ||||
|                 float factory_st = test.readFloat(605);                     //FACTORY_ST | ||||
|  | ||||
|                 //write a float value to the Remote_Factory_SP | ||||
|                 test.writeFloat(205,0.56F);                         //REMOTE_FACTORY_SP | ||||
|                 //write a boolean value to the Remote_Solar_SW | ||||
|                 test.writeBoolean(401,false);                       //REMOTE_SOLAR_SW | ||||
|  | ||||
|                 //check the factory SetPoint | ||||
|                 System.out.println("Factory Setpoint is: " + factory_st); | ||||
|  | ||||
|                 //check the solar SetPoint | ||||
|                 if(solar_connect_st){ | ||||
|                     System.out.println("Solar is connected"); | ||||
|                 }else{ | ||||
|                     System.out.println("Solar is disconnected"); | ||||
|                 } | ||||
|             }catch (ModbusTransportException e){ | ||||
|  | ||||
|             }catch (ErrorResponseException e){ | ||||
|  | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -21,6 +21,7 @@ import java.util.*; | ||||
| public class Utility { | ||||
|     /** Default size for the TCP input stream */ | ||||
|     public static final int	TCP_BUFFER_SIZE 	= 4096; | ||||
|     public static final boolean DEBUG_MODE = false; | ||||
|  | ||||
|     /** Object to get some random values... */ | ||||
|     public static Random rnd = new Random(1); | ||||
| @@ -419,4 +420,8 @@ public class Utility { | ||||
|             System.out.println(logMsg); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static void pDebug(String msg) { | ||||
|         if(DEBUG_MODE) System.out.println(msg); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										29
									
								
								src/test/java/Database.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/test/java/Database.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| import ch.hevs.isi.MinecraftController; | ||||
| import ch.hevs.isi.core.BooleanDataPoint; | ||||
| import ch.hevs.isi.core.DataPoint; | ||||
| import ch.hevs.isi.core.FloatDataPoint; | ||||
| import ch.hevs.isi.db.DatabaseConnector; | ||||
|  | ||||
| import java.io.FileInputStream; | ||||
| import java.io.InputStream; | ||||
| import java.util.Properties; | ||||
|  | ||||
| public class Database { | ||||
|     public static void main(String[] args) { | ||||
|         MinecraftController.ERASE_PREVIOUS_DATA_INB_DB = true; | ||||
|         FloatDataPoint clock = new FloatDataPoint("CLOAK_FLOAT", false); | ||||
|         FloatDataPoint gridVoltage = new FloatDataPoint("GRID_U_FLOAT", true); | ||||
|         BooleanDataPoint solarPanel = new BooleanDataPoint("REMOTE_SOLAR_SW", true); | ||||
|  | ||||
|         DatabaseConnector.getMySelf().initialize(null); | ||||
|  | ||||
|         clock.setValue(0f); | ||||
|  | ||||
|         for (float i = 0; i < 3; i += 0.1f) { | ||||
|             System.out.println(""); | ||||
|             clock.setValue(i); | ||||
|             gridVoltage.setValue(750 + (100*i)%100); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user