diff --git a/ScenariMicroservizio/1657636848310-scenari-tcplocalhost1883/.lck b/ScenariMicroservizio/1657636848310-scenari-tcplocalhost1883/.lck new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ScenariMicroservizio/1657636876590-scenari-tcplocalhost1883/.lck b/ScenariMicroservizio/1657636876590-scenari-tcplocalhost1883/.lck new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ScenariMicroservizio/java-commandline.txt b/ScenariMicroservizio/java-commandline.txt new file mode 100644 index 0000000000000000000000000000000000000000..e0cb82743a61731d93bbcc63c7e77cd379ccaeb0 --- /dev/null +++ b/ScenariMicroservizio/java-commandline.txt @@ -0,0 +1,6 @@ +compilare: +javac -cp .:./org.eclipse.paho.client.mqttv3_1.2.5.jar:./org.json-1.0.0.v201011060100.jar -d bin src/utility/* src/scenari/* + +partire: +cd bin +java -classpath .:../org.eclipse.paho.client.mqttv3_1.2.5.jar:../org.json-1.0.0.v201011060100.jar scenari.Scenari diff --git a/ScenariMicroservizio/org.eclipse.paho.client.mqttv3_1.2.5.jar b/ScenariMicroservizio/org.eclipse.paho.client.mqttv3_1.2.5.jar new file mode 100644 index 0000000000000000000000000000000000000000..f75a141b10d88b1fa98a90519c826e690b62beab Binary files /dev/null and b/ScenariMicroservizio/org.eclipse.paho.client.mqttv3_1.2.5.jar differ diff --git a/ScenariMicroservizio/org.json-1.0.0.v201011060100.jar b/ScenariMicroservizio/org.json-1.0.0.v201011060100.jar new file mode 100644 index 0000000000000000000000000000000000000000..2aa70fdf80f4cc14a2063a8f118c847506c6c579 Binary files /dev/null and b/ScenariMicroservizio/org.json-1.0.0.v201011060100.jar differ diff --git a/ScenariMicroservizio/src/scenari.json b/ScenariMicroservizio/src/scenari.json new file mode 100644 index 0000000000000000000000000000000000000000..bd43971ae01e7596921e6278d126d2c2f3dabe58 --- /dev/null +++ b/ScenariMicroservizio/src/scenari.json @@ -0,0 +1,16 @@ +{"stato-attuale":0, +"automa":[{"stato":0, +"learn-off":0, +"learn-on":1, +"antifurto-on":2, +"antifurto-off":0}, +{"stato":1, +"learn-off":0, +"learn-on":1, +"antifurto-on":1, +"antifurto-off":1}, +{"stato":2, +"learn-off":2, +"learn-on":2, +"antifurto-on":2, +"antifurto-off":0}]} \ No newline at end of file diff --git a/ScenariMicroservizio/src/scenari/Automa.java b/ScenariMicroservizio/src/scenari/Automa.java new file mode 100644 index 0000000000000000000000000000000000000000..2c725c747cd28661ef18900628066ad5c969a71e --- /dev/null +++ b/ScenariMicroservizio/src/scenari/Automa.java @@ -0,0 +1,167 @@ +package scenari; + +import java.io.IOException; +import java.util.Iterator; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import utility.Helper; + +public class Automa { +// 0: learn = off, antifurto = off +// 1: learn = on, antifurto = off +// 2: learn = off, antifurto = on => scenari = on + + private int stato; + private JSONObject jsonObject; + private boolean learn; + private boolean antifurto; + private final String DATABASE_PATH = "./scenari.json"; + + public Automa() throws JSONException, IOException { + this.jsonObject = new JSONObject(Helper.leggiFile(DATABASE_PATH)); + setCollaterals(this.jsonObject.getInt("stato-attuale")); + } + + public synchronized void setStato(String command) { + setStato(command,false); + } + + public synchronized void setStato(String command,boolean fromDatabase) { + if (command == null || !Helper.checkJSON(command) + || (!command.contains("\"learn\":") && !command.contains("\"antifurto\":")) + || (!command.contains(":\"on\"") && !command.contains(":\"off\""))) + return; + String[] cmd = command.split(":"); + String cosa = cmd[0].split("\"", 3)[1]; + String come = cmd[1].split("\"", 3)[1]; + + if (Helper.compareText(cosa, "learn")) { + if (Helper.compareText(come, "on") && this.stato == 0) { + setCollaterals(1,!fromDatabase); + } else if (Helper.compareText(come, "off") && this.stato == 1) { + setCollaterals(0,!fromDatabase); + } + return; + } + if (Helper.compareText(cosa, "antifurto")) { + if (Helper.compareText(come, "on") && this.stato == 0) { + setCollaterals(2,!fromDatabase); + } else if (Helper.compareText(come, "off") && this.stato == 2) { + setCollaterals(0,!fromDatabase); + } + return; + } + } + + public String findPossibleCommand(String which) throws JSONException{ + Boolean learn = null; + if(!(learn=Helper.compareText(which, "learn")) && !Helper.compareText(which, "antifurto")) + return null; +// devo cercare nel jsonObject lo stato attuale e trovare la stringa corrispondente al which, poi discriminare in base a cio' che mi permette di cambiare stato + @SuppressWarnings("unchecked") + Iterator<String> keys = jsonObject.keys(); + while(keys.hasNext()) { + String key = keys.next(); + try { + JSONArray array = jsonObject.getJSONArray(key); + for(int i =0; i<array.length(); i++) { + JSONObject a = array.getJSONObject(i); + int stato = a.getInt("stato"); + if(stato != this.stato) + continue; + if(learn) { + int learnOff = a.getInt("learn-off"); + if(learnOff != stato) + return "off"; + int learnOn = a.getInt("learn-on"); + if(learnOn != stato) + return "on"; + } else { + int antifurtoOff = a.getInt("antifurto-off"); + if(antifurtoOff != stato) + return "off"; + int antifurtoOn = a.getInt("antifurto-on"); + if(antifurtoOn != stato) + return "on"; + } + } + } catch (JSONException e) { + if(!e.getMessage().contains("stato-attuale")) + System.out.println(e.getMessage()); + } +// if(riga.getInt("stato")==statoAttuale) { +// this.statoAttuale = riga.getInt("cmd"+newStatoInterruttore); +// aggiornaInterruttoreESuono(); +// } + } + return null; + } + + private synchronized void setCollaterals(int stato) { + setCollaterals(stato,false); + } + + private synchronized void setCollaterals(int stato,boolean write) { + switch (stato) { + case 0: { + this.stato = 0; + this.learn = false; + this.antifurto = false; + break; + } + case 1: { + this.stato = 1; + this.learn = true; + this.antifurto = false; + break; + } + case 2: { + this.stato = 2; + this.learn = false; + this.antifurto = true; + break; + } + } + notifyAll(); + if(write) + writeStatoOnDatabase(); + } + + private synchronized void writeStatoOnDatabase() { + try { + this.jsonObject.put("stato-attuale", this.stato); + Helper.scriviFile(jsonObject,DATABASE_PATH); + } catch (JSONException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public int getStato() { + return this.stato; + } + + public boolean getLearn() { + return this.learn; + } + + public boolean getAntifurto() { + return this.antifurto; + } + + public synchronized boolean waitForAntifurto() { + while(!this.antifurto) { + try { + wait(); + } catch(InterruptedException ex) { + System.out.println("Interrupted exception"); + } + } + return this.antifurto; + } + +} diff --git a/ScenariMicroservizio/src/scenari/Scenari.java b/ScenariMicroservizio/src/scenari/Scenari.java new file mode 100644 index 0000000000000000000000000000000000000000..63f8c0e65f5bb5c2487140c9a9e628e27e24bc8d --- /dev/null +++ b/ScenariMicroservizio/src/scenari/Scenari.java @@ -0,0 +1,175 @@ +package scenari; + + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; + +import org.eclipse.paho.client.mqttv3.IMqttToken; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttTopic; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.json.JSONException; +import org.json.JSONObject; + +import utility.Esecutore; +import utility.Helper; +import utility.MyQueue; +import utility.Pair; +import utility.Publisher; +import utility.SubscribeCallback; + +public class Scenari { + + private static String brokerUrl; + private String learnTrigger; + private String attivaScenari; + private static ArrayList<String> topicsSub; + private String clientId = Long.toString(new Date().getTime()) + "-scenari"; // unique client id + private Automa automa; + + public MqttClient mqttClient; + + public Scenari(Automa automa) throws JSONException, IOException, MqttException { + this.automa = automa; + + JSONObject config = new JSONObject(Helper.leggiFile("./CONF/conf.json")); + brokerUrl = config.getString("protocol") + "://" + config.getString("broker") + ":" + config.getInt("port"); + + topicsSub = new ArrayList<String>(); + topicsSub.add("to/all"); //mandare la mia descrizione json + topicsSub.add("rpc/gruppo2/luci/scenari"); //mandare lo stato attuale + + // inutile aggiungere i topic, perche subbo gia' tutto gpio/# + config = new JSONObject(Helper.leggiFile("./CONF/zona.json")); + learnTrigger = config.getString("learn-trigger"); +// topicsSub.add("from/gruppo2/luci/gpio/" + learnTrigger); // Sottoscrivo i messaggi che notificano il cambiamento di stato dell'interruttore + attivaScenari = config.getString("attiva-scenari"); +// topicsSub.add("from/gruppo2/luci/gpio/"+attivaScenari); + +// interruttoreOutputSuono = config.getString("outputSuono"); +// topicsSub.add("from/gruppo2/luci/gpio/" + interruttoreOutputSuono); // Sottoscrivo i messaggi che notificano il cambiamento di stato dell'interruttore +// topicsSub.add("to/gruppo2/luci/scenari/learn"); + topicsSub.add("from/gruppo2/luci/gpio/#"); + + this.mqttClient = new MqttClient(brokerUrl, clientId, new MemoryPersistence()); + //memory persistence serve per non avere errori in console + // https://github.com/eclipse/paho.mqtt.java/issues/794 + } + + public void startClient(Esecutore esec, Publisher publisher) throws MqttException { + MqttConnectOptions options = new MqttConnectOptions(); + options.setUserName("gruppo2"); + options.setPassword("funziona".toCharArray()); + options.setCleanSession(false); + mqttClient.setCallback(new SubscribeCallback(this, publisher, learnTrigger, attivaScenari, esec, automa)); + + mqttClient.connect(options); + + for(String t: topicsSub) + mqttClient.subscribe(t); + } + + + + + /* + * Abbiamo 3 tipi di eventi: + * to Command Events - mi viene richiesto di eseguire un comando --> sottoscrivo + * from Status Events - io annuncio un cambiamento di stato + * (inviato a tutti i client che hanno sottoscritto il topic su cui faccio la publish) + * rpc Query Events - richiesta di inviare uno stato a un client (o microservizio). --> sottoscrivo + * Query implementati come coppia di eventi: <query event, status event> + * RICORDIAMO COME ABBIAMO IMPLEMENTATO LA RPC SU MQTT + * + * + * + * Il cuore della nostra business logic è un metodo in cui mi metto in attesa su mqtt e: + * se arriva un comando, chiamo il metodo responsabile per eseguire quel comando; + * se arriva un evento di stato, chiamo il metodo responsabile per gestire quell'evento di stato; + * se arriva una remote procedure call, attivo la risposta sulla remote procedure call + * + * + * + * Capire come gestire sistema a regole (vedi slide "Esempio" in "Applicazioni IoT "Cloud Based"") + * + * + * + * Sicurezza gestita con TLS + * + * + * + * FILE DA FARE per un servizio: (?) + * file che contiene le informazioni per connettersi al mosquitto locale + * file che dice qual'è la porta che lui deve esporre + * file che mi dice tutte le cose che lo configurano, ad esempio quanti device deve guardare + * ... + * + * I file di configurazione sono in JSON o in XML + * I dati persistiti si troveranno nella sottodirectory (chiamata con il nome del servizio) di Home + * + * + * Nella fase di configurazione potremo ancora modificare i file di configurazione (ad esempio aggiungendo device, + * regole). Dopodiché quando si fa partire il servizio si inizierà a usare l'altra parte di file system (ovvero il Local + * Dynamic File System) dove si andranno a salvare gli stati per renderli persistenti. + * + * + * + * Ho un processo principale che come unico compito ha quello di fare una fork() e creare un processo figlio. + * Sarà questo processo figlio a eseguire effettivamente il programma. Se il processo figlio termina (PER UN QUALCHE + * PROBLEMA), allora il processo padre se ne accorge e fa ripartire un nuovo processo figlio. + * + * + * Devo avere una thread di configurazione che obbedisce ai miei comandi (dati sulla HAT interface andando su + * 192.168.0.101:9001/configure.html (dove 192.168.0.101 è l'indirizzo della beaglebone) + * + * + * Ho una classe che implementa l'automa a stati finiti + * + * + */ + + public static void main(String args[]) throws JSONException, IOException, MqttException { + System.out.println("started"); + //PER TESTARE PRIMA TOGLI IL TRY CATCH !!!!! POI QUANDO TUTTO FUNZIONA, METTI IL TRY CATCH, togli le throws E IMPLEMENTA LA RIPARTENZA! + +// while(true) { +// try { + startSystem(); +// } +// catch(Exception e) { +// // DA FARE: +// // qui metto il codice per far ripartire il processo: rileggo lo stato dei sensori e dell'interruttore, +// // capisco di conseguenza in quale stato dell'automa mi trovo e riparto. +// startSystem(); +// } +// } +// Scenari antifurto = +// new Scenari(new Automa()); + } + + + private static void startSystem() throws JSONException, IOException, MqttException { + +// MyQueue<Integer> codaVal = new MyQueue<Integer>(); +// MyQueue<Pair> codaMsg = ; + Automa automa = new Automa(); + Scenari scenari = new Scenari(automa); + Publisher publisher = new Publisher(new MyQueue<Pair>(), scenari); +// Esecutore esec = new Esecutore(publisher, codaVal, automa, antifurto.interruttoreOutputSuono); + Esecutore esec = new Esecutore(publisher,automa); +// Timer timer = new Timer(30000,-5,esec,automa); + + scenari.startClient(esec,publisher);//, publisher); + publisher.start(); + esec.start(); +// timer.start(); + } + + + + + +} diff --git a/ScenariMicroservizio/src/test/AutomaTest.java b/ScenariMicroservizio/src/test/AutomaTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fe8cb0c344df074c53539995c01a7046296fa142 --- /dev/null +++ b/ScenariMicroservizio/src/test/AutomaTest.java @@ -0,0 +1,106 @@ +package test; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +import scenari.Automa; +import scenari.Scenari; +import utility.Esecutore; +import utility.SubscribeCallback; + +public class AutomaTest { + + +// @Test + void test01() throws JSONException, IOException { + Automa a = new Automa(); + JSONObject loff = new JSONObject().put("learn","off"); + JSONObject lon = new JSONObject().put("learn","on"); + JSONObject aoff = new JSONObject().put("antifurto","off"); + JSONObject aon = new JSONObject().put("antifurto","on"); + final int stato = a.getStato(); + a.setStato(loff.toString()); + a.setStato(aoff.toString()); +// valid commands + assertEquals(0,a.getStato()); + a.setStato(lon.toString()); + assertEquals(1,a.getStato()); + a.setStato(loff.toString()); + assertEquals(0,a.getStato()); + a.setStato(aon.toString()); + assertEquals(2,a.getStato()); + a.setStato(aoff.toString()); + assertEquals(0,a.getStato()); +// invalid commands + a.setStato(loff.toString()); + assertEquals(0,a.getStato()); + a.setStato(aoff.toString()); + assertEquals(0,a.getStato()); + a.setStato(lon.toString()); + assertEquals(1,a.getStato()); + a.setStato(lon.toString()); + assertEquals(1,a.getStato()); + a.setStato(aoff.toString()); + assertEquals(1,a.getStato()); + a.setStato(aon.toString());//secondo me questo dovrebbe essere valido + assertEquals(1,a.getStato()); + a.setStato(loff.toString()); + a.setStato(aon.toString()); + assertEquals(2,a.getStato()); + a.setStato(lon.toString()); + assertEquals(2,a.getStato()); + a.setStato(loff.toString()); + assertEquals(2,a.getStato()); + a.setStato(aon.toString()); + assertEquals(2,a.getStato()); + a.setStato(null); + assertEquals(2,a.getStato()); + a.setStato("\"antifurto\":\"on\""); + assertEquals(2,a.getStato()); + a.setStato("{\"dati\":\"on\"}"); + assertEquals(2,a.getStato()); + a.setStato("{\"antifurto\":true}"); + assertEquals(2,a.getStato()); + + resetStato(stato,a,loff,aoff,lon,aon); + } + + private void resetStato(int stato,Automa a, JSONObject loff, JSONObject aoff, JSONObject lon, JSONObject aon) { + switch (stato) { + case 0: { + a.setStato(loff.toString()); + a.setStato(aoff.toString()); + break; + } + case 1: { + resetStato(0,a, aon, aon, aon, aon); + a.setStato(lon.toString()); + break; + } + case 2: { + resetStato(0,a, aon, aon, aon, aon); + a.setStato(aon.toString()); + break; + } + } + } + +// @Test +// void test02() throws JSONException, IOException, MqttException { +// Automa a = new Automa(); +// a.setStato("{\"learn\":\"on\"}"); +// SubscribeCallback sb = new SubscribeCallback(new Scenari(a), "IN4", "IN5", new Esecutore(a), a); +//// sb.handleEvent(1, "IN4"); +// MqttMessage mes = new MqttMessage(); +// mes.setPayload("{\"event\":0}".getBytes()); +// sb.messageArrived("from/gruppo2/luci/gpio/OUT3", mes); +// } + +} diff --git a/ScenariMicroservizio/src/utility/Esecutore.java b/ScenariMicroservizio/src/utility/Esecutore.java new file mode 100644 index 0000000000000000000000000000000000000000..1d1dd3cc854a22f4831f2c3a890345bea7f66e05 --- /dev/null +++ b/ScenariMicroservizio/src/utility/Esecutore.java @@ -0,0 +1,148 @@ +package utility; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Arrays; + +import org.json.JSONException; +import org.json.JSONObject; + +import scenari.Automa; + +public class Esecutore extends Thread { + +// private MyQueue<Integer> codaVal; // coda in cui vengono mantenuti i valori da sommare alla variabile valore +// private int valore; // variabile numerica cumulativa inizialmente impostata a 0 +// public final int SOGLIA = 100; + private Publisher publisher; + private Automa automa; +// private String outputSuono; // nome logico dell'interruttore che fa scattare il suono dell'allarme + + public Esecutore( Publisher publisher, /*MyQueue<Integer> codaVal, */ Automa automa) { +// this.codaVal = codaVal; +// this.valore = 0; + this.automa = automa; + this.publisher = publisher; +// this.outputSuono = outputSuono; + } + +// private int numeroRighe = 0; + + public void run() { +// qui ci va la business logic + try { + while (true) { + LocalTime ora = LocalTime.now(); +// if(!automa.waitForAntifurto()) + if(!automa.getAntifurto()) + continue; + // l’antifurto è acceso + FileReader fr = randomFileScenari(); + if(fr == null )//|| numeroRighe == 0) + continue; + BufferedReader br = new BufferedReader(fr);// selezioniamo un file contenente i comandi + String line = br.readLine();//su ogni linea si trova un nuovo comando, in ordine di tempo dal meno recente al piu' recente +// numeroRighe--; + while(line.isBlank()) { + line = br.readLine(); +// numeroRighe--; + } + LocalTime tempoCmd = LocalTime.now(); + boolean startedPublishing = false; +// String nomeLuce; +// String cmd; + while(line != null && ora.isBefore(tempoCmd)/*&& numeroRighe > 0*//*&& tempoCmd.isBefore(LocalTime.now())*/) { + ora = LocalTime.now(); +// System.out.println("\tsecondo ciclo"); + + JSONObject jline = new JSONObject(line); + String[] time = jline.getString("tempo").split("-"); +// System.out.println("using: "+line+" + "+time.length); + if(time.length != 3) { +// System.out.println(jline.getString("tempo")); + continue; + } + tempoCmd = LocalTime.of(Integer.valueOf(time[0]), Integer.valueOf(time[1]), Integer.valueOf(time[2]));// otteniamo [la data e] l’ora in cui è stato inviato quel comando + + LocalTime tempoAttuale = LocalTime.now(); + try { + long waitingTime = Helper.timeDifference(tempoAttuale, tempoCmd)*1000; + if(waitingTime > 1000) { +// System.out.println(tempoAttuale); +// System.out.println(tempoCmd); +// System.out.println("Waiting to execute command"); +// long offset = ((Double) (Math.random() * 10)).intValue()*1000;//trova un valore tra 1 e 10 (*1000) + //ThreadLocalRandom.current().nextLong(10000); +// if(offset%2 == 0) { +// waitingTime += offset;// + 1000; +// } else waitingTime -= offset; +// waitingTime += 1000; +// System.out.println(waitingTime); +// if(waitingTime > 1000) + Thread.sleep(waitingTime); + +// System.out.println("running..."); + startedPublishing = true; + } //else System.out.println("useless"); + if(startedPublishing) { +// System.out.println("\tpublishing"); + String nomeLuce = jline.getString("luce"); + String cmd = jline.getString("cmd"); +// System.out.println("to/gruppo2/luci/gpio/"+nomeLuce); +// System.out.println("{cmd:"+cmd+"}"); + publisher.aggiungiComando("to/gruppo2/luci/gpio/"+nomeLuce, "{cmd:"+cmd+"}"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + line = br.readLine(); +// numeroRighe--; + if(line != null) + while(line.isBlank()) { + line = br.readLine(); + // numeroRighe--; + } +// System.out.println("should restart? "+line != null+" ; "+ora.isBefore(tempoCmd)); +// System.out.println(line != null && ora.isBefore(tempoCmd)); + } +// now line == null so I'm done reading the file +// TODO something about this + br.close(); + fr.close(); +// numeroRighe = 0; + } + } catch (IOException | JSONException e) { + e.printStackTrace(); + } + } + + private synchronized FileReader randomFileScenari() throws IOException { + // Creating a File object for directory + File directoryPath = new File("./db-scenari/"); + // List of all files and directories + final ArrayList<File> filesListRaw = new ArrayList<File>(Arrays.asList(directoryPath.listFiles())); + final ArrayList<File> filesList = new ArrayList<File>(); + for(File f : filesListRaw) { + if(f.toString().endsWith(".json")) + filesList.add(f); + } + int maxFiles = filesList.size(); + if(maxFiles == 0) + return null; + if(maxFiles == 1) { + File f = filesList.get(0); +// numeroRighe = ((Long)(Files.lines(f.toPath()).count())).intValue(); + return new FileReader(f); + } + File path = null; + do { + path = filesList.get(((Double) (Math.random() * maxFiles)).intValue()); + } while(path == null); + return new FileReader(path); + } + +} diff --git a/ScenariMicroservizio/src/utility/Helper.java b/ScenariMicroservizio/src/utility/Helper.java new file mode 100644 index 0000000000000000000000000000000000000000..67cbba14bced3a2d6e78d597fcc22ea871be3df7 --- /dev/null +++ b/ScenariMicroservizio/src/utility/Helper.java @@ -0,0 +1,149 @@ +package utility; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.time.temporal.Temporal; + +import org.json.JSONException; +import org.json.JSONObject; + +public class Helper { + + /*public static void sendResponse(String response, HttpExchange exchange) throws IOException { + System.out.println(response); + exchange.sendResponseHeaders(200, response.getBytes().length); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + + public static void badRequest(HttpExchange exchange) throws IOException { + System.out.println("Errors in the request!"); +// exchange.getResponseHeaders().remove("content-type"); + String response = "{\"message\":\"Errors in the request!\"}"; + exchange.sendResponseHeaders(400, response.getBytes().length); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + + public static void pageNotFound(HttpExchange exchange) throws IOException { + System.out.println("Page not found!"); +// exchange.getResponseHeaders().remove("content-type"); + String response = "{\"message\":\"Page not found!\"}"; + exchange.sendResponseHeaders(404, response.getBytes().length); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } + + public static void methodNotAllowed(HttpExchange exchange) throws IOException { + System.out.println("Method not allowed!"); +// exchange.getResponseHeaders().remove("content-type"); + String response = "{\"message\":\"Method not allowed!\"}"; + exchange.sendResponseHeaders(405, response.getBytes().length); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + }*/ + + public static boolean compareText(String a, String b){ + return a.compareToIgnoreCase(b) == 0; + } + + public static String readBody(InputStream requestBody) { + int req; + StringBuffer sb = new StringBuffer(); + try { + while((req = requestBody.read()) != -1) + sb.append(Character.toString((char)req)); + } catch (IOException e) { + e.printStackTrace(); + } + return sb.toString(); + } + + public static boolean checkJSON(String body) { + if(!body.startsWith("{") || !body.endsWith("}")) + return false; + try { + new JSONObject(body); + return true; + } catch (JSONException e) { + return false; + } + } + + public static String leggiFile(String path) throws IOException { + String line; + StringBuffer answer = new StringBuffer(); + BufferedReader bufferedReader = null; + try { + bufferedReader = new BufferedReader(new FileReader(path)); + while((line = bufferedReader.readLine()) != null) { + answer.append(line).append("\n"); + } + } catch (FileNotFoundException ex) { +// System.out.println("Unable to open file '" + path + "'"); +// scriviFile("sono qui",path); +// System.out.println("file creato al path corrispondente"); + } finally { + if(bufferedReader != null) + bufferedReader.close(); + } + return answer.toString(); + } + + public synchronized static void scriviFile(String text, String path) throws IOException { + BufferedWriter b = new BufferedWriter(new FileWriter(path)); + String[] lines = text.split("\n"); + for(String l : lines) { + b.write(l); + b.newLine(); + } + b.close(); + } + + public synchronized static void scriviFile(JSONObject job, String path) throws IOException { + scriviFile(convertiJSON(job),path); + } + + private static String convertiJSON(JSONObject job) throws IOException { + String[] strings = job.toString().split(","); + StringBuffer text = new StringBuffer(); + int index = 1; + for(String s : strings) + if(strings.length != index) { + text.append(s).append(",\n"); + index++; + } else text.append(s); + return text.toString(); + } + + public static void appendiFile(String text, String path) throws IOException { + String fileContents = leggiFile(path); + StringBuffer s = new StringBuffer(); + if(!fileContents.isBlank()) + s.append(fileContents).append("\n"); + s.append(text); + scriviFile(s.toString(),path); + } + + public static void appendiFile(JSONObject job, String path, boolean splitLines) throws IOException { + appendiFile(splitLines ? convertiJSON(job) : job.toString() ,path); + } + + public static void appendiFile(JSONObject job, String path) throws IOException { + appendiFile(job,path,true); + } + + public static long timeDifference(Temporal older, Temporal newer) { + return Duration.between(older, newer).toSeconds(); + } +} diff --git a/ScenariMicroservizio/src/utility/MyQueue.java b/ScenariMicroservizio/src/utility/MyQueue.java new file mode 100644 index 0000000000000000000000000000000000000000..da7359c5a76a2c1749360e9336ea727d46ce4cf2 --- /dev/null +++ b/ScenariMicroservizio/src/utility/MyQueue.java @@ -0,0 +1,35 @@ +package utility; +import java.util.ArrayList; + +public class MyQueue<T> { + + private ArrayList<T> queue; + + public MyQueue() { + this.queue = new ArrayList<T>(); + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + public synchronized void send(T n) { + queue.add(n); + notifyAll(); + } + + public synchronized T receive() { + while(isEmpty()) { + try { + wait(); + } catch(InterruptedException ex) { + System.out.println("Interrupted exception"); + } + } + return queue.remove(0); + } + + public synchronized void removeAll() { + queue.clear(); + } +} diff --git a/ScenariMicroservizio/src/utility/Pair.java b/ScenariMicroservizio/src/utility/Pair.java new file mode 100644 index 0000000000000000000000000000000000000000..5faf4f718bdde1b499ac822a2388ec5ba409c802 --- /dev/null +++ b/ScenariMicroservizio/src/utility/Pair.java @@ -0,0 +1,21 @@ +package utility; + +public class Pair { + + private String topic; + private String msg; + + + public Pair(String topic, String msg) { + this.topic = topic; + this.msg = msg; + } + + public String getTopic() { + return topic; + } + + public String getMsg() { + return msg; + } +} diff --git a/ScenariMicroservizio/src/utility/Publisher.java b/ScenariMicroservizio/src/utility/Publisher.java new file mode 100644 index 0000000000000000000000000000000000000000..a5c8b89907c85213a3cfa5444a672add5dfbb0b9 --- /dev/null +++ b/ScenariMicroservizio/src/utility/Publisher.java @@ -0,0 +1,38 @@ +package utility; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.eclipse.paho.client.mqttv3.MqttTopic; + +import scenari.Scenari; + +public class Publisher extends Thread { + + private MyQueue<Pair> coda; + private Scenari scenari; + + public Publisher(MyQueue<Pair> coda, Scenari scenari) { + this.coda = coda; + this.scenari = scenari; + } + + public void aggiungiComando(String topic, String msg) { + coda.send(new Pair(topic, msg)); + } + + + public void run() { + while(true) { + Pair p = coda.receive(); + try { + sendMqttMessage(p.getTopic(), p.getMsg()); + } catch (MqttException e) { + e.printStackTrace(); + } + } + } + + private void sendMqttMessage(String topic, String msg) throws MqttException { + final MqttTopic msgTopic = scenari.mqttClient.getTopic(topic); + msgTopic.publish(new MqttMessage(msg.getBytes())); + } +} diff --git a/ScenariMicroservizio/src/utility/SubscribeCallback.java b/ScenariMicroservizio/src/utility/SubscribeCallback.java new file mode 100644 index 0000000000000000000000000000000000000000..0fd6f35b6c5e4e302302a9cbd89fee48f69fbb9f --- /dev/null +++ b/ScenariMicroservizio/src/utility/SubscribeCallback.java @@ -0,0 +1,224 @@ +package utility; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.json.JSONException; +import org.json.JSONObject; + +import scenari.Automa; +import scenari.Scenari; + +public class SubscribeCallback implements MqttCallback{ + + private Scenari scenari; + private Publisher publisher; + private String learnTrigger; + private String attivaScenari; + private Esecutore esec; + private Automa automa; + + public SubscribeCallback(Scenari scenari, Publisher publisher, String learnTrigger, String attivaScenari, Esecutore esec, Automa automa) { + this.scenari = scenari; + this.publisher = publisher; + this.learnTrigger = learnTrigger; + this.attivaScenari = attivaScenari; + this.esec = esec; + this.automa = automa; + } + + @Override + public void connectionLost(Throwable arg0) { + System.out.println("connessione persa\t"+arg0); +// boolean retry = true; + final Date d = new Date(); + Date d2 = new Date(); + long time = Math.abs(d2.getTime()-d.getTime()); + + while(/*retry ||*/ (time<600000)) { + try { + System.out.println("retrying..."); + + Scenari.main(null); + return; +// esec.interrupt(); +// publisher.interrupt(); +// publisher = new Publisher(new MyQueue<Pair>(), scenari); +// esec = new Esecutore(publisher,automa); + +// scenari.startClient(esec, publisher); +// retry = false; + } catch (MqttException | JSONException | IOException e) { + System.out.println("non sono riusciuto"); + d2 = new Date(); + time = Math.abs(d2.getTime()-d.getTime()); + } + } + System.out.println("fuori dal while"); + if(time>=600000) { + System.out.println("Tentativo di riconnessione fallito"); + System.exit(1); + } + } + + + @Override + public void deliveryComplete(IMqttDeliveryToken arg0) { + // Nessuna operazione + System.out.println("inviato"); + } + + + @Override + public void messageArrived(String topic, MqttMessage message) throws MqttException, JSONException, IOException { +// topic = topic.toLowerCase(); + System.out.println("new message: "+topic+" "+message.toString()); + if(topic.equals("to/all")) { + System.out.println("invio lo stato dell'automa"); + sendRequested("from/gruppo2/luci/scenari/description",false); + return; + } + if(topic.equals("rpc/gruppo2/luci/scenari")) { + System.out.println("invio la mia configurazione"); + sendRequested("from/gruppo2/luci/scenari",true); // CONTROLLA SE VA BENE PUBBLICARE LO STATO SU QUESTO TOPIC O SE IL TOPIC DEVE ESSERE UN'ALTRO. NOTA: NON POSSO PUBBLICARE SULLO STESSO TOPIC CHE HO SOTTOSCRITTO + return; + } + + + JSONObject msgJson = new JSONObject(message.toString()); + // CONTROLLA SULLA BEAGLEBONE CHE IL MESSAGGIO MANDATO SIA UN INT. CONTROLLA ANCHE IL TOPIC SU CUI MANDA I MESSAGGI (da aver sottoscritto i topic correttamente) + +// gli event sono 0 o 1; i trigger quando sono attivati/premuti inviano uno 0, che e' l'unico elemento che mi interessa + if(topic.equals("from/gruppo2/luci/gpio/"+learnTrigger)) { + System.out.println("trigger per learn\t"+!automa.getAntifurto()); + if(automa.getAntifurto())//se l'antifurto e' attivo, non posso attivare gli scenari + return; + int newStatus = msgJson.getInt("event"); + if(newStatus != 1) + return; + handleEvent(newStatus,learnTrigger); + return; + } + if(topic.equals("from/gruppo2/luci/gpio/"+attivaScenari)) { + System.out.println("trigger per antifurto\t"+!automa.getLearn()); + if(automa.getLearn())//se l'apprendimento e' attivo, non posso attivare l'antifurto + return; + int newStatus = msgJson.getInt("event"); + if(newStatus != 1) + return; + handleEvent(newStatus,attivaScenari); + return; + } + if(topic.startsWith("from/gruppo2/luci/gpio/IN")) { + System.out.println("totalmente inutile per me!"); + return;//non mi interessa altro da IN + } +// if(topic.equals("to/gruppo2/luci/scenari/learn")) {//mi aspetto un {learn:true/false}, scritto da handleEvent +// System.out.println("messaggio per iniziare/terminare a imparare"); +// boolean val = msgJson.getBoolean("learn"); +// handleLearn(val); +// return; +// } + if(topic.startsWith("from/gruppo2/luci/gpio/OUT")) { + if(message.toString().contains("event")) { + System.out.println("se learn==true, salvo questo event!"); + System.out.println(automa.getLearn()+"\t"+msgJson.toString()); + } + if(!automa.getLearn() || !message.toString().contains("event")) + return; + if(scenarioName == null) { + handleLearn(true); + } + int val = msgJson.getInt("event"); + String luce = topic.substring(23); + handleLearnData(luce,val); + return; + } + + System.out.println("Impossibile"); + } + + private void handleEvent(int newStatus,String which) throws JSONException, MqttException{ +// if(newStatus != 1) +// return; + String cosa = which == learnTrigger ? "learn" : "antifurto"; + String cmd = automa.findPossibleCommand(cosa); + if(cmd == null) + return; + System.out.println("handling event: "+cosa+"\t"+cmd); + automa.setStato(new JSONObject().put(cosa,cmd).toString()); + System.out.println("stato\t"+automa.getStato()); + if(Helper.compareText(cosa, "learn")) { +// JSONObject j = new JSONObject().put("learn", Helper.compareText(cmd, "on")); +// Helper.sendMqttMessage(scenari.mqttClient, "to/gruppo2/luci/scenari/learn", j.toString()); +// publisher.aggiungiComando("to/gruppo2/luci/scenari/learn", j.toString());//NON posso inviare ad un topic che subbo + boolean val = Helper.compareText(cmd, "on"); + handleLearn(val); + } +// if(newStatus!=-1) { +// String statusOnOff = automa.converter(newStatus); // lo stato e' della forma "on" oppure "off" +// if(! statusOnOff.equals(automa.getStatoSuono()) ) { +// automa.aggiornaStatoSuono(statusOnOff); +// } + } + + private String scenarioName = null;//"scenario"+LocalTime.now(); +// private LocalTime lastTime = null; + + private void handleLearn(boolean shouldLearn) { + System.out.println("handleLearn "+shouldLearn+" ; "+automa.getLearn()); + automa.setStato("{\"learn\":"+ (shouldLearn ? "\"on\"" : "\"off\"")+"}"); + System.out.println("\t"+automa.getLearn()); + if(automa.getLearn()) { + if(/*lastTime == null &&*/ scenarioName == null) { +// lastTime = LocalTime.now(); + scenarioName = "scenario-"+LocalDateTime.now().toString().replace(':', '-')+".json"; + } + } else { +// lastTime = null; + scenarioName = null; +// TODO tell antifurto to turn off if shouldLearn == true + } + } + + private void handleLearnData(String luce, int val) throws MqttException, IOException, JSONException { + JSONObject j = new JSONObject(); +// tempo passato dallo scorso comando + LocalTime now = LocalTime.now(); +// long diff = Helper.timeDifference(lastTime,now); + j.put("tempo", now.format(DateTimeFormatter.ofPattern("HH-mm-ss"))); +// lastTime = now; +// quale luce + j.put("luce", luce); +// accesa o spenta? + j.put("cmd", val); + Helper.appendiFile(j, "./db-scenari/"+scenarioName,false); + } + + private void sendRequested(String topic,boolean moreInfo) throws MqttException, JSONException { + JSONObject j = new JSONObject(); + j.put("stato", automa.getStato()); + if(moreInfo) { + j.put("learn", automa.getLearn()); + j.put("antifurto", automa.getAntifurto()); + j.put("learn-trigger", learnTrigger); + j.put("attiva-scenari", attivaScenari); + } +// Helper.sendMqttMessage(scenari.mqttClient, topic, j.toString()); + publisher.aggiungiComando(topic,j.toString()); + } + +// private void sendDescription(String topic) throws JSONException, MqttException { + // DA FARE: guardare quale descrizione manda netmon, archiver, meter,... e implementare di conseguenza questo metodo +// String msg = +// publisher.aggiungiComando(topic, msg); +// } + + +}