diff --git a/assets/js/co.js b/assets/js/co.js index dad433b5..9ecce4e7 100644 --- a/assets/js/co.js +++ b/assets/js/co.js @@ -618,6 +618,14 @@ var urlCtrl = { "#define." : {title:'TAG MAP ', icon : 'map-marker', action:function( hash ){ showDefinition("explain"+hash.split('.')[1]) } }, "#data.index" : {title:'OPEN DATA FOR ALL', icon : 'fa-folder-open-o'}, "#opendata" : {"alias":"#data.index"}, + "#thing.graph" : {title:'GRAPH IOT', icon : 'fa-line-chart'}, + "#thing.manage" : {title:'MANAGE IOT', icon : 'fa-database'}, + "#thing.updatesckdevices" : {title:'UPDATE SCK', icon : 'fa-database'}, + "#thing.scklastestreadings" : {title:'LASTEST READING SCK', icon : 'fa-database'}, + "#thing.getsckdataincodb" : {title:'DATA RECORD SCK', icon : 'fa-database'}, + "#thing.synthetizesckdata" : {title:'SYNTHETIZE RECORD SCK', icon : 'fa-database'}, + "#search" : { "title":'SEARCH AND FIND', "icon" : 'map-search', "hash" : "#default.directory", "preaction":function( hash ){ return searchByHash(hash);} }, + }, shortVal : ["p","poi","s","o","e","pr","c","cl"/* "s","v","a", "r",*/], shortKey : [ "citoyens","poi" ,"siteurl","organizations","events","projects" ,"cities" ,"classified"/*"entry","vote" ,"action" ,"rooms" */], diff --git a/assets/js/sig/map_initializer.js b/assets/js/sig/map_initializer.js index bf148de0..ed3e69d2 100755 --- a/assets/js/sig/map_initializer.js +++ b/assets/js/sig/map_initializer.js @@ -337,6 +337,7 @@ "poi.openScene" : "poi-marker-default", "poi.stand" : "poi-marker-default", "poi.parking" : "poi-marker-default", + "poi.smartCitizen" : "poi-marker-default", "entry" : "entry-marker-default", "action" : "action-marker-default", diff --git a/assets/js/thing/graph.js b/assets/js/thing/graph.js new file mode 100644 index 00000000..90a86eb2 --- /dev/null +++ b/assets/js/thing/graph.js @@ -0,0 +1,313 @@ +/** +*@author: Danzal +* +*/ +function showAllGraph(){ $(".graphs").show(); } +function showGraph(figGraph){ $("#"+figGraph).show(); } +function hideAllGraph(){ $(".graphs").hide(); } +function hideGraph(figGraph){ $("#"+figGraph).hide(); } + +function hideAllChart(sourcedb){ $(".chart_"+sourcedb).hide(); } +function showAllChart(sourcedb){ $(".chart_"+sourcedb).show(); } + +function showHideChart(valuebtn){ + //var sourcedb = $(this).data("sourcedb") ; // amélioration pour un eselection direct + var sourceText=""; + var toShow=""; + switch (valuebtn) { + case 0 : + if(!graphReady.dCOdb){ + sourceText="la base de données de Communecter"; + setGraphFromCoDB(); + } + hideAllChart("dAPIsc"); + toShow="dCOdb"; + break; + case 1: + if(!graphReady.dAPIsc){ + sourceText="l'API de smartcitizen"; + setGraphFromAPIsmartcitizen(); + } + hideAllChart("dCOdb"); + toShow="dAPIsc"; + break; + /*case 2: + //TODO : redrawBothChartGraph(); + showAllChart("dCOdb"); + showAllChart("dAPIsc"); + break;*/ + } + if((!graphReady.dCOdb && valuebtn==0) || ( !graphReady.dAPIsc && valuebtn==1) ){ + $.blockUI({ message : ' Chargement des données depuis '+sourceText+' en cours ... ' }); + }else{ + graphesRedraw(toShow); + } + showAllChart(toShow); + $("#btnTempAndHum").click(); + $("#legend-graph").removeClass("hidden"); + +} + +function setSVGForSensor(sensor,sensorKey) { + + var svgId = "sensor"+sensor; + var figGraph = "graphe_"+sensor; + var gId = svgId+"_g"; + + var svgObj = d3.select("#"+figGraph) + .append("svg").attr("width",svgwidth).attr("height",svgheight) + .attr("viewBox","0 0 "+svgwidth+" "+svgheight) + .attr("preserveAspectRatio","xMidYMid meet") + .attr("class","col-sm-12 svggraph") + .attr("id", svgId); + + var g = svgObj + .append("g") + .attr("transform", "translate(" + gmargin.left + "," + gmargin.top + ")") + .attr("id", gId); + + var captionSensor = d3.select("#"+figGraph) + .append("figcaption") + .text("Graph of sensor "+infoSensors[svgId].name+" ("+infoSensors[svgId].description+")"); + + multiGraphe[sensorKey] = {svgid : svgId, + svg : svgObj, + mesure : {description : "", unit : "" }, + dimension : { width : +gwidth, + height : +gheight, + margin : gmargin }, + gid : gId , + domain : {Yn : null, Ym : null, Xn : vXn, Xm : vXm , xDomainInitialized : false}, + data : {}, + divgraphid : figGraph, + urlReqApi : "" + }; + + mylog.log('multiGraphe['+sensorKey+'] : '); + mylog.log(multiGraphe[sensorKey]); +} + +function setLegend(deviceId,strkCol){ + + var idIM = 'icnmin_'+deviceId; + var idTL = 'textdevice_'+deviceId; + + var legendILSC=" SCK device "+deviceId+""; + // var iconMinus = ""; + var dLegend = d3.select("#legend").append("div").attr("id","legend_"+deviceId).attr("class","col-sm-12 col-xs-12"); + dLegend.append("span").html(legendILSC); +} + +function setStrokeColorAndLegendForDevice(device) { + //var stId = "sCol_"+device; + if(strockeColorArray[device] == null ){ + var strockeColor = "rgb("+Math.floor((Math.random()*220)+1)+","+Math.floor((Math.random()*220)+1)+","+Math.floor((Math.random()*220)+1)+")"; + strockeColorArray[device]=strockeColor; + setLegend(device,strockeColor); + } + //return strockeColor; +} + +function setGraphDomain(data, sensorkey,redraw){ + mylog.log("setGraphDomain"); +if(redraw==true){ + multiGraphe[sensorkey].domain.Yn == null; + multiGraphe[sensorkey].domain.Ym == null; + min[sensorkey]=null; + max[sensorkey]=null; +} + + for(id in data){ + var dDS = data[id][sensorkey]; + //mylog.log("dDS : " ); + //mylog.log(dDS); + if(dDS.length>=1){ + min[sensorkey]=d3.min( dDS, function(d){ return d.values;}); + max[sensorkey]=d3.max( dDS, function(d){ return d.values;}); + //mylog.log("- sensorkey :"+ sensorkey+ ": min : "+ min +" -- max: "+max); + if (multiGraphe[sensorkey].domain.Yn == null || multiGraphe[sensorkey].domain.Yn > min[sensorkey]) { + multiGraphe[sensorkey].domain.Yn = min[sensorkey]; + //mylog.log("multiGraphe["+sensorkey+"].domain.Yn : " + multiGraphe[sensorkey].domain.Yn); + } + if (multiGraphe[sensorkey].domain.Ym == null || multiGraphe[sensorkey].domain.Ym < max[sensorkey]) { + multiGraphe[sensorkey].domain.Ym = max[sensorkey]; + //mylog.log("multiGraphe["+sensorkey+"].domain.Ym : "+multiGraphe[sensorkey].domain.Ym); + } + } + } +} + + +function setAxisXY(sensorKey){ + setAxisX(sensorKey); + setAxisY(sensorKey); +} + + +function setAxisX(sensorKey){ + mylog.log(" --- setAxisX --- "); + var gId = multiGraphe[sensorKey].gid; + var g = d3.select("#"+gId); + var xAxisId="xAxis"+multiGraphe[sensorKey].svgid; + //var height = multiGraphe[sensorKey].dimension.height; + + d3.select("#"+xAxisId).remove(); // TODO refaire la selection sur le graphe sensor + + g.append("g") + .attr("id", xAxisId) + .attr("class", "theAxis") + .attr("transform", "translate(0," + gheight + ")") + .call(d3.axisBottom(x)) + /// * + //.append("text") + //.attr("fill","#000") + //.attr("x", gwidth) + //.attr("text-anchor","end") + //.text("time") + ; +} + +function setAxisY(sensorKey){ + mylog.log(" --- setAxisY --- "); + + var gId = multiGraphe[sensorKey].gid; + var g = d3.select("#"+gId); + var yAxisId="yAxis"+ multiGraphe[sensorKey].svgid; + + d3.select("#"+yAxisId).remove(); // TODO refaire la selection sur le graphe sensor + + var sensorkunit = sensorKey+" "+infoSensors[multiGraphe[sensorKey].svgid].unit ; // mettre dans le text + + g.append("g") + .attr("id", yAxisId) + .attr("class", "theAxis") + .call(d3.axisLeft(y)) + .append("text") + .attr("fill","#000") + .attr("transform", "rotate(-90)") + .attr("y", 8) + .attr("dy", "0.71em") + .attr("text-anchor","end") + .text(sensorkunit) + ; + +} + +function tracer(da,device,sensorKey,strokeColor="blue",source, strokeWidth=1.5){ + mylog.log("da in tracer"); + mylog.log(da); + if (da.length>0){ + mylog.log("----------- da.length > 0 : tracer ! -------- "); + mylog.log("-- tracer - device : "+device+" sensor "+sensorKey); + var g = d3.select("#"+multiGraphe[sensorKey].gid); + + var gpathId = source+"_gpId_"+device+multiGraphe[sensorKey].svgid; //ex : [source]_gpId_4162sensor17 + var graphClassSensor = "gcs_"+dataSensors[sensorKey].id+" "+source+" chart_"+source+" "+device +" chart_graph" ; + g.append("path") + .datum(da) + .attr("fill", "none") + .attr("class", graphClassSensor) + .attr("id", gpathId) + .attr("stroke", strokeColor) + .attr("stroke-linejoin", "round") + .attr("stroke-linecap", "round") + .attr("stroke-width", strokeWidth) + .attr("d", line); + /* TODO afficher text du device lors d'un survol avec la souris + g.append("text") + .datum(function(d){ return {value: d.values[d.values.length - 1]}; }) + .attr("transform", function(d) { return "translate(" + x(d.values) + "," + y(d.timestamps) + ")"; }) + .attr("x", 3) + .attr("dy", "0.35em") + .style("font", "10px sans-serif") + .text(function(d) { return d.values; });*/ + } + $.unblockUI(); + +} + +function graphesRedraw(source){ + for(keySens in multiGraphe){ + grapheOneSensor(keySens,source, true); + } +} + +function grapheOneSensor(keySens,source, redraw) { + $.unblockUI(); + var data = (source=="dCOdb")? dCOdb : dAPIsc; + + setGraphDomain(data, keySens, redraw); +//TODO Prendre un domaine 10% plus grand que les data en Y 5%top et 5 bottom + y.domain([(multiGraphe[keySens].domain.Yn-1), (multiGraphe[keySens].domain.Ym+1)]); + setAxisY(keySens); + + if(multiGraphe[keySens].domain.xDomainInitialized==false || redraw==true){ + x.domain([multiGraphe[keySens].domain.Xn, multiGraphe[keySens].domain.Xm]); + setAxisX(keySens); + multiGraphe[keySens].domain.xDomainInitialized=true; + } + + if(redraw==false){ + for (dId in data){ + tracer(data[dId][keySens], dId, keySens, strockeColorArray[dId],source); + } + } + if(graphReady[source]==false){ + $(".svggraph").show(); + $("#legend-graph").show(); + graphReady[source]=true; + } +} + +function fillArrayWithObjectTimestampsAndValues(readings, deviceid, sensorkey){ + mylog.log("- fillArrayWithObjectTimestampsAndValues - "); + + dAPIsc[deviceid][sensorkey] = readings.map( + function(item){ + var ts = new Date(); + ts.setTime(Date.parse(item[0])); + ts.setSeconds(0); + item[1] = +item[1]; + return {timestamps : ts, values : item[1]}; + } + ); + //multiGraphe[sensorkey].data[deviceid] = dAPIsc[deviceid][sensorkey]; +} + +function dataSensorAdaptorTimestampsAndValues(convertedDataRecord,device){ + mylog.log("--dataSensorAdaptorTimestampsAndValues--"); + var dataCOdb={temp : [], hum : [], bat: [], panel : [], no2 : [], panel : [], co : [], noise : [], nets : [], light : []}; + convertedDataRecord.forEach( function(item){ + var ts = new Date(); + ts.setTime(Date.parse(item.timestamp)); + ts.setSeconds(0); + + item.temp =+item.temp; + item.hum =+item.hum; + item.bat =+item.bat; + item.panel=+item.panel; + item.co =+item.co; + item.no2 =+item.no2; + item.light=+item.light; + item.nets =+item.nets; + item.noise=+item.noise; + + dataCOdb.temp.push({timestamps : ts, values : item.temp}); + dataCOdb.hum.push({timestamps : ts, values : item.hum}); + dataCOdb.bat.push({timestamps : ts, values : item.bat}); + dataCOdb.panel.push({timestamps : ts, values : item.panel}); + dataCOdb.no2.push({timestamps : ts, values : item.no2}); + dataCOdb.co.push({timestamps : ts, values : item.co}); + dataCOdb.noise.push({timestamps : ts, values : item.noise}); + dataCOdb.nets.push({timestamps : ts, values : item.nets}); + dataCOdb.light.push({timestamps : ts, values : item.light}); + + } + ); + dCOdb[device]=dataCOdb; + + for (ks in multiGraphe){ + multiGraphe[ks].data[device]=dataCOdb[ks]; + } + //return dataCOdb; +} diff --git a/components/CommunecterController.php b/components/CommunecterController.php index 024492cb..e4b2dec5 100755 --- a/components/CommunecterController.php +++ b/components/CommunecterController.php @@ -485,10 +485,20 @@ class CommunecterController extends Controller "checkurlexists" => array('href' => "/ph/communecter/app/checkurlexists", "public" => true), "rooms" => array('href' => "/ph/communecter/app/rooms", "public" => true), "survey" => array('href' => "/ph/communecter/app/survey", "public" => true), + "thing" => array('href' => "/ph/communecter/app/thing", "public" => true), ), "siteurl" => array( "incnbclick" => array('href' => "ph/communecter/siteurl/incnbclick") ), + "thing" => array( + //"index" => array('href' => "/ph/communecter/thing/index", "public" => true ), + "graph" => array('href' => "/ph/communecter/thing/graph", "public" => true ), + "scklastestreadings" => array('href' => "/ph/communecter/thing/scklastestreadings", "public" => true ), + "updatesckdevices" => array('href' => "/ph/communecter/thing/updatesckdevices", "public" => true ), + "manage" => array('href' => "/ph/communecter/thing/manage","public"=>true), + "getsckdataincodb" => array('href' => "/ph/communecter/thing/getsckdataincodb","public"=>true), + "synthetizesckdata" => array('href' => "/ph/communecter/thing/synthetizesckdata","public"=>true), + ), ); function initPage(){ diff --git a/config/CO2/params.json b/config/CO2/params.json index 279aedf2..d637ec54 100644 --- a/config/CO2/params.json +++ b/config/CO2/params.json @@ -175,7 +175,19 @@ "icon" : "university", "mainTitle" : "Partie Admin Public", "placeholderMainSearch" : "Admin Public ..." - } + }, + + "#thing": { + "inMenu" : true, + "useHeader" : false, + "open" : true, + "subdomain" : "thing", + "subdomainName" : "Objets communectés", + "hash" : "#app.thing", + "icon" : "database", + "mainTitle" : "Les objets communectés", + "placeholderMainSearch" : "Les objets communectés ..." + } } diff --git a/config/CO2/thing.json b/config/CO2/thing.json new file mode 100644 index 00000000..69b28b30 --- /dev/null +++ b/config/CO2/thing.json @@ -0,0 +1,58 @@ +{ + "sections" : { + "smartcitizen" : { + "label":"Smart-Citizen-Kit", + "labelshort" : "SCK", + "key" : "smartCitizen", + "icon" : "thermometer-three-quarters", + "section":1, + "color":"azure", + "filter" : "sckFilters", + "active" : true + + }, + "CO PI": { + "label":"CO PI", + "key" : "copi", + "icon" : "clone", + "section":2, + "color":"azure", + "filter" : "copiFilters", + "active": false + } + }, + "sckFilters": { + "Dernieres-Mesures" : { + "page" : "scklastestreadings", + "label" :"Dernières Mesures", + "icon" : "clock-o", + "key" : "smartCitizen", + "subcat" : [ "Temperature et humidité" , "Gaz", "Lumière" , "Bruit" , "Nets", "Énergie", "Tous" ] + }, + "Graphes" : { + "page" : "graph", + "icon" : "line-chart", + "label" :"Graphes", + "key" : "smartCitizen", + "subcat" : [ "Données Communecter", "Données API SmartCitizen" ] + }, + "Gestion-SCK" : { + "page" : "manage", + "icon" : "database", + "label" :"Gestion SCK", + "key" : "smartCitizen", + "forAdmin" : true, + "subcat" : [ "Avec compte smartcitizen", "Sans compte smartcitizen" ] + } + }, + "copiFilters": { + "Voir-tous" : { + "page": "copi", + "icon" : "cloud", + "label" :"Copi All", + "key" : "copi", + "subcat" : [ ] + } + } + +} \ No newline at end of file diff --git a/controllers/AppController.php b/controllers/AppController.php index 1d04200a..19227b45 100755 --- a/controllers/AppController.php +++ b/controllers/AppController.php @@ -348,5 +348,8 @@ function leftShift32($number, $steps) { exit; } - + public function actionThing(){ + CO2Stat::incNbLoad("co2-thing"); + echo $this->renderPartial("thing",array(), true); + } } \ No newline at end of file diff --git a/controllers/ThingController.php b/controllers/ThingController.php new file mode 100644 index 00000000..de2d3910 --- /dev/null +++ b/controllers/ThingController.php @@ -0,0 +1,30 @@ + +* Date: 26/01/2017 rapatrié dans CO2 le 22/03/17 +*/ + +class ThingController extends CommunecterController { + + const moduleTitle = "Thing"; + protected function beforeAction($action) { + parent::initPage(); + return parent::beforeAction($action); + } + + public function actions(){ + return array( + //'index' => 'citizenToolKit.controllers.thing.IndexAction', + 'graph' => 'citizenToolKit.controllers.thing.GetGraphAction', + 'scklastestreadings' => 'citizenToolKit.controllers.thing.GetLastestReadingAction', + 'updatesckdevices' => 'citizenToolKit.controllers.thing.UpdateSckDevicesAction', + 'manage' => 'citizenToolKit.controllers.thing.ManageAction', + 'getsckdataincodb' => 'citizenToolKit.controllers.thing.GetSCKDataInCODBAction', + 'synthetizesckdata' => 'citizenToolKit.controllers.thing.SynthetizeSCKDataInCODBAction', + + ); + } + +} diff --git a/docs/bugs.org b/docs/bugs.org index 172fecf5..0dea511c 100755 --- a/docs/bugs.org +++ b/docs/bugs.org @@ -2,8 +2,8 @@ * Correction de bugs CO2 * ************************************************************ - - +[ ] agenda ajout d'evenement impossible le datepicker bloque la date de fin, même si c'est bien une date ultérieur à la date de début. +[ ] Les annonces ne s'enregistre pas si on ajoute une adresse (passage par la carte). diff --git a/docs/specs/Thing.md b/docs/specs/Thing.md new file mode 100644 index 00000000..329fa43e --- /dev/null +++ b/docs/specs/Thing.md @@ -0,0 +1,65 @@ +# Thing +Au delà des Smart-Citizen-Kit, thing pourrrait permettre d'intégrer les objets connecté à CO2, COPI. + +Un point d'entrée est disponible à coté du bouton "actus", il est nommé "Objets communectés" (on peut renommer en IoT), l'icone est un database. + +## Smart-Citizen-Kit +La view index permet d'accéder + * à deux vue différentre : + * Les graphes (encore du developpement à finir) + * Les dernières valeurs enregistrés + * à une page de gestion (travaux en cours): + * première fonctionnalité de la page ajouter l'adresse mac correspondant au device (en cours de finalisation) + +### Double push + +Le kit pourrait être utilisé exclusivement avec Communecter, un serveur de temps pour sck est ajouter dans l'API, la reception simple fonctionne déjà, et la réception batch (plusieurs enregistrements en même temps) est en cours de développement. Ces fonctionnalités peuvent permettre une redondance, si on développe tous les fonctions nécessaire. + +#### Firmware +Le firmware sck double push a été développé par Nicolas Grondin en s'inspirant des débuts de travaux de Jean Daniel CAZAL (Stagiaire). +Nicolas Grondin a par la suite amélioré la gestion de la FIFO (écriture et lecture en EEPROM) et la stabilité du firmware. +Avec le double push nous devons choisir une période d'envoi des enregistrements adéquat pour ne pas surcharger la base de données, pour l'instant ce "time update" est de 5 minutes (300 secondes). + +#### Coté serveur +Un outil datetime dans l'API de communecter, permet de fournir l'heure au SCK. +Pour les enregistrements on passe par element/save, avec un traitement grace au fonction dans le model thing. + + +### Graphe (#thing.graph) + +Les données sont récupérer via l'API de Smartcitizen (api.smartcitizen.me/v0), la version de l'api est succeptible de changer, il faudra alors changer la constante Thing::URL_API_SC. +Les devices possèdent 9 sensors, pour obtenir les enregistrements (Température, Humidité, CO, NO2, Lumière, Bruit, Nombre de réseaux WLAN environnant, Batterie, et Panneau Photo-Voltaïque). +Le nombre de réseaux WIFI environnant n'est pas utilisé. + +#### Les requetes sur l'API + +Il faut faire une requete par sensor avec un rollup (temps séparant 2 enregistrement), une date de début et une date de fin. +La date de fin est l'heure actuelle en GMT. +La date de début est calculer, si le paramètre nbDays est utilisé dans la requète pour afficher la page graphe (par exemple : #thing.graphe?nbDays=20 , il y a une limite à 31 jours), sinon c'est un graphe sur une journée. C'est une fonctionnalité de substitution aux datepickers. + + +La page graphe utilise D3.js pour construire les graphes. Chaque sensor a son SVG dédié, si plusieurs devices sont dans la même zone, les données sont afficher sur le mème graphe. Pour l'instant la zone c'est même code postale et même country, on devra faire un filtrage par niveau par la suite (Région département). + +#### Bouton de selection de graphe +Tous les graphes sont construit une seul fois au chargement de la page. Les boutons cache les graphes et montre ceux qui sont selectionnés, le dernier bouton affiche tous les graphes. + +#### Légende +La légende permet d'être redirigé vers le device sur site officiel des SCK : smartcitizen.me/kits/[deviceId] + + +### Page des dernières valeur enregistré +Affiche les dernièrs enregistrement du SCK. + +Fonctionnalité qui pourra permettre d'ajouter valeurs dans des POD. + + +### Page de gestion (#thing.manage) + +Cette page à pour but de faire la liaison entre les enregistrements reçu dans notre base, et le device qui les envois. En effet, les devices n'ont pas connaissance de leur deviceId. Sur smartcitizen, cette liaison est faite lors de l'enregistrement du kit sur la plateforme. +Nous avons besoin de la même liaison dans notre base de données, celà permet au utilisateurs ou nous même (si on limite l'accès au administrateurs) de renseigné l'adresse mac du kit. + + +### Base de données + +[Une estimation de la capacité nécessaire](https://docs.google.com/spreadsheets/d/1E5_lm-dEw28Vq176nwduC9KgFOfZzYbgjU2mWfJxYRo/edit?usp=sharing) + diff --git a/views/app/annonces.php b/views/app/annonces.php index fee6afdc..aa07a20e 100755 --- a/views/app/annonces.php +++ b/views/app/annonces.php @@ -21,7 +21,7 @@ $this->renderPartial($layoutPath.'header', array( "layoutPath"=>$layoutPath , "type" => @$type, - "page" => page) ); + "page" => $page) ); ?> + +
Si vous avez un compte smartcitizen.me vous pourrez mettre à jours les adresse mac de vos kit qui sont communectés (double push). Utiliser le token smarticitizen, pour plus d'information sur l'authentification et le token voir la page developper.smartcitizen.me. +
+ + + ++ +