Come anticipato in fase di progettazione, si è pensato di realizzare una classe di messaggi che conterrà le differenti tipologie di contenuti. In questo modo il formato scambiato dai componenti sarà univoco e potrà essere ricostruito mediante funzioni di utilità che consentono la ricostruzione degli oggetti dai byte ricevuti. La classe generica che verrà trasmessa sarà composta da un descrittore del tipo di dati inviati definito tramite l’enumerazione delle possibili tipologie, e dai dati stessi codificati come stringa JSON. La classe metterà a disposizione i metodi getter e setter per configurare ed ottenere il valore di ogni campo dell’oggetto e consentire al parser JSON di ricostruirlo.
Listato A.1: Message.java
public class Message {
public enum MessageType{ANNOUNCE, INFO_GW, SENSOR_DATA};
private MessageType type;
private String content;
public Message(MessageType type, String content){
this.type = type;
this.content = content; }
public Message(){}
public void setType(MessageType type){
this.type = type; }
public MessageType getType(){
return type; }
public String getContent(){
return content; }
public void setContent(String content){
this.content = content; }
A.1.1
I contenuti
Il campo content della classe message potrà contenere tre differenti formati di contenuti:
InfoGatewayContent definisce il messaggio che i gateway invieranno ai client che riterranno utili. Conterrà gli attributi necessari ai client per connettere la rete WiFi generata dal gateway, e connettersi al broker locale: ID del gateway, distanza dal dispositivo che ha inviato il messaggio di presentazione, i sensori richiesti, i parametri per connettersi alla rete WiFi (SSID, chiave d’accesso e tipo di sicurezza) e l’URI del broker.
Listato A.2: InfoGatewayContent.java
public class InfoGatewayContent {
public static enum Security{OPEN, WEP, WPA};
private double distance;
private String ssid;
private String passphrase;
private Security security;
private String brokerURI;
private List<String> sensorsRequested;
private String deviceId;
public InfoGatewayContent() {}
public InfoGatewayContent(double distance, String ssid, String passphrase, Security security, String brokerURI, List<String> sensorRequested, String deviceId){
this.distance = distance;
this.ssid = ssid;
this.passphrase = passphrase;
this.security = security;
this.brokerURI = brokerURI;
this.sensorsRequested = sensorRequested;
this.deviceId = deviceId; }
public double getDistance() {
return distance; }
public void setDistance(double distance) {
this.distance = distance; }
public String getSSID() {
return ssid; }
public void setSSID(String ssid) {
this.ssid = ssid; }
public String getPassphrase() {
return passphrase; }
this.passphrase = passphrase; }
public Security getSecurity(){
return security; }
public void setSecurity(Security security){
this.security = security; }
public String getBrokerURI() {
return brokerURI; }
public void setBrokerURI(String brokerURI) {
this.brokerURI = brokerURI; }
public List<String> getSensorsRequested() {
return sensorsRequested; }
public void setSensorsRequested(List<String> sensorsRequested) {
this.sensorsRequested = sensorsRequested; }
public String getDeviceId() {
return deviceId; }
public void setDeviceId(String deviceName) {
this.deviceId = deviceName; }
}
AnnounceContent è la classe che definisce il contenuto di un messaggio con cui i client annunciano la loro presenza in una specifica località. La classe conterrà tutte le informazioni necessarie al gateway per capire se il dispositivo può risultare utile alle applicazioni in esecuzione o meno, ed eventualmente per contattarlo: l’identificativo, le coordinate geografiche e la lista di sensori messi a disposizione.
Listato A.3: AnnounceContent.java
public class AnnounceContent{
private String devId;
private List<String> availableSensors;
private double latitude;
private double longitude;
public AnnounceContent(){}
public AnnounceContent(String id, List<String> sensors, double latitude, double longitude){
this.devId = id;
this.availableSensors = sensors;
this.latitude = latitude;
this.longitude = longitude; }
public List<String> getAvailableSensors() {
return availableSensors; }
public double getLatitude() {
return latitude; }
public double getLongitude() {
return longitude; }
public String getDevId() {
return devId; }
public void setDevId(String devId) {
this.devId = devId; }
}
SensorData è invece una rappresentazione dei dati dei sensori. Consente di poter inviare le informazioni raccolte in un formato generico e indipendente dal dispositivo. Quando il cliente invierà dati in questo formato sarà trasmessa al cliente una lista di SensorData, in cui ogni elemento della lista conterrà nel campo value l’ultimo aggiornamento del sensore descritto dal campo type, ottenuto nell’istante indicato dal timestamp.
Listato A.4: SensorData.java
public class SensorData {
private String type;
private long timestamp;
private float[] value = new float[3];
public SensorData() {}
public SensorData(String type, long timestamp, float[] value){
this.type = type;
this.timestamp = timestamp;
this.value = value; }
public String getType() {
return type; }
public void setType(String type) {
this.type = type; }
public long getTimestamp() {
return timestamp; }
public void setTimestamp(long timestamp) {
this.timestamp = timestamp; }
public float[] getValue() {
return value; }
public void setValue(float[] value) {
this.value = value; }
}
A.1.2
Serializzazione e deserializzazione del payload
MQTT trasmette messaggi in formato binario, risulta quindi necessario che i payload siano espressi in un formato serializzabile, per questo motivo si è deciso di utilizzare una rappresentazione in formato JSON che offre un parsing più veloce ed un overhead minore rispetto ad XML. Le operazioni di serializzazione e deserializzazione dei dati sono realizzate dalla classe MessageUtils che, con l’ausilio delle librerie Gson di Google, offre due metodi statici:
String getJsonString(Object o) restituisce la rappresentazione dell’oggetto passato come pa- rametro in formato JSON.
Object fromJson(String json) ricevuta in input una stringa JSON provvede alla ricostruzione del messaggio e ne restituisce il contenuto deserializzato. Esegue un primo parsing per ottenere l’oggetto di tipo Message, quindi a seconda del tipo di messaggio esegue un secondo parsing per la deserializzazione della stringa JSON che rappresenta il contenuto.
Listato A.5: MessageUtils.java
public class MessageUtils {
protected static Gson gson = new Gson();
public static Object fromJson(String json){ Message m = gson.fromJson(json, Message.class);
switch(m.getType()){
case ANNOUNCE:
return(AnnounceContent)gson.fromJson(m.getContent(), AnnounceContent.class);
case INFO_GW:
return(InfoGatewayContent) gson.fromJson(m.getContent(), InfoGatewayContent.class);
case SENSOR_DATA:
Type sensorDataType = new TypeToken<ArrayList<SensorData>>(){}.getType();
returngson.fromJson(m.getContent(), sensorDataType); }
return null; }
public static String getJsonString(Object o) {
return gson.toJson(o); }