Merge branch 'main' of https://gitlab.fdmci.hva.nl/propedeuse-hbo-ict/onderwijs/2023-2024/out-a-se-ti/blok-3/qaajeeqiinii59
This commit is contained in:
@@ -20,15 +20,18 @@ nav:
|
||||
- Websockets: rpi-documentation/websockets
|
||||
- Reverse Proxy: rpi-documentation/Reverse-Proxy
|
||||
- Db - Ws connection: rpi-documentation/Databaseconnection
|
||||
- Put-request: rpi-documentation/Put-Request
|
||||
- 🧠 Brainstorm:
|
||||
- Ideeën: brainstorm/ideeën
|
||||
- Database design: brainstorm/Database
|
||||
- Feedback: brainstorm/Feedback
|
||||
- Problem: brainstorm/Problem
|
||||
- Infrastructure: brainstorm/UML-infrastructure
|
||||
- Infrastructure: brainstorm/UML-infrastructureV2
|
||||
- Taskflow: brainstorm/Taskflow
|
||||
- Design: Sp1SchetsProject/FirstDesign
|
||||
- Interview facility manager: brainstorm/bebouwBeheer.md
|
||||
- Interview facility manager: brainstorm/gebouwBeheer
|
||||
- Questions enquete: brainstorm/QuestionsEnquete
|
||||
- 🖨️ Software:
|
||||
- Dev page: brainstorm/SoftwareDocumentatie/Dev_page
|
||||
- Graph classes: brainstorm/SoftwareDocumentatie/classes
|
||||
|
77
docs/brainstorm/UML-infrastructureV2.md
Normal file
77
docs/brainstorm/UML-infrastructureV2.md
Normal file
@@ -0,0 +1,77 @@
|
||||
```mermaid
|
||||
classDiagram
|
||||
setup --> websocketSetup
|
||||
loop --> screenButtonHandler
|
||||
screenButtonHandler --> DisplayText
|
||||
screenButtonHandler --> sendData
|
||||
sendData --> Server : Websocket
|
||||
setup --> loop
|
||||
python --> Server
|
||||
Server --> website : Websocket
|
||||
|
||||
|
||||
namespace ESP32Questionbox {
|
||||
class setup {
|
||||
+int questionID
|
||||
+char*[] Question
|
||||
+char*[] Answer
|
||||
+DisplayText displayText
|
||||
+void websocketSetup()
|
||||
}
|
||||
|
||||
class loop {
|
||||
+void screenButtonHandler()
|
||||
+void sendData(int question, String answer)
|
||||
+void hexdump(const void* mem, uint32_t len, uint8_t cols)
|
||||
}
|
||||
|
||||
class websocketClient {
|
||||
+loop()
|
||||
+begin()
|
||||
+sendTXT()
|
||||
}
|
||||
|
||||
class websocketSetup {
|
||||
+connectWifi()
|
||||
+websocketConnect()
|
||||
}
|
||||
|
||||
class screenButtonHandler {
|
||||
-bool redButton
|
||||
-bool greenButton
|
||||
-bool whiteButton
|
||||
+displayText.writeText()
|
||||
+sendData()
|
||||
}
|
||||
class DisplayText {
|
||||
+void writeText(char* text, int size, int posX, int posY, int screenTime, bool center, bool bottom)
|
||||
-int centerText(char* text)
|
||||
-void printWordsFull(char* text, bool bottom)
|
||||
}
|
||||
class sendData{
|
||||
+webSocket.sendTXT
|
||||
}
|
||||
}
|
||||
namespace server {
|
||||
class python {
|
||||
+databaseScript()
|
||||
+websocketScript()
|
||||
+flaskScript()
|
||||
}
|
||||
class Server {
|
||||
+websocket()
|
||||
+flask()
|
||||
+mariaDB()
|
||||
}
|
||||
}
|
||||
|
||||
namespace user {
|
||||
class website {
|
||||
+ getLiveData()
|
||||
+ getHistoricalData()
|
||||
+ showData()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@@ -64,14 +64,12 @@ flowchart LR
|
||||
|
||||
The wiring diagram for the node is as follows:
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
### Fritsing Diagram
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## Future Improvements
|
||||
|
||||
|
@@ -237,7 +237,7 @@ async def getNodeID(macAdress):
|
||||
|
||||
This function is alot like the original one, with the only 2 changes being that it now also commits the nodeID and that the code to make a new node is now in a different function.
|
||||
|
||||
[Link to code](server\data.py)
|
||||
[link to code](../../server/web-data-connection/data.py)
|
||||
|
||||
## The function to commit the data from the enqueteNodes
|
||||
|
||||
|
@@ -13,6 +13,8 @@ def get_query(node, dataType, MAC, questionID, replies, dateStart, dateEnd):
|
||||
WHERE TimeStamp BETWEEN '{dateStart}' AND '{dateEnd}';'''
|
||||
elif node and dataType:
|
||||
query = f"SELECT * FROM Measurement WHERE NodeID = {node} AND Type = '{dataType}'"
|
||||
elif MAC == "*":
|
||||
query = "SELECT * FROM Node"
|
||||
elif node:
|
||||
query = f"SELECT * FROM Measurement WHERE NodeID = {node}"
|
||||
elif dataType:
|
||||
|
22
server/web-data-connection/PythonMermaid.md
Normal file
22
server/web-data-connection/PythonMermaid.md
Normal file
@@ -0,0 +1,22 @@
|
||||
```mermaid
|
||||
classDiagram
|
||||
Node <|-- SensorNode
|
||||
Node <|-- EnqueteNode
|
||||
Node: + String macAddress
|
||||
Node: +getNodeID()
|
||||
class SensorNode{
|
||||
+ Float temp
|
||||
+ Float Eco2
|
||||
+ Float Tvoc
|
||||
+ Float humi
|
||||
- String query
|
||||
+processSensorNodeData()
|
||||
}
|
||||
|
||||
class EnqueteNode{
|
||||
+ Int result
|
||||
+ Int questionID
|
||||
- String query
|
||||
+ ProcessEnqueteNodeData()
|
||||
}
|
||||
```
|
@@ -1,64 +1,19 @@
|
||||
import asyncio
|
||||
import websockets
|
||||
import mysql.connector
|
||||
import json
|
||||
from class_SensorNode import SensorNode
|
||||
from class_enqueteNode import EnqueteNode
|
||||
from classes_data import dbLogin
|
||||
|
||||
sensorNodeArray = []
|
||||
enqueteNodeArray = []
|
||||
|
||||
async def processSensorNodeData(data, nodeID):
|
||||
try:
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
|
||||
query = "INSERT INTO `Measurement` (NodeID, Type, Value) VALUES (%s, %s, %s)"
|
||||
processedData = json.loads(data)
|
||||
|
||||
processedTemp = (processedData['Temp'])
|
||||
processedHumi = (processedData['Humi'])
|
||||
processedeCO2 = (processedData['eCO2'])
|
||||
processedTVOC = (processedData['TVOC'])
|
||||
processedMAC = (processedData['node'])
|
||||
|
||||
pushingDataArray = [(nodeID, "Temp", processedTemp), (nodeID, "Humi", processedHumi), (nodeID, "eCO2", processedeCO2), (nodeID, "TVOC", processedTVOC)]
|
||||
for i in pushingDataArray:
|
||||
print(query ,i)
|
||||
cursor.execute(query, i)
|
||||
mydb.commit()
|
||||
|
||||
except mysql.connector.Error as err:
|
||||
print("MySQL Error:", err)
|
||||
finally:
|
||||
cursor.close()
|
||||
mydb.close()
|
||||
|
||||
async def processEnqueteNodeData(data, nodeID):
|
||||
try:
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
|
||||
query = "INSERT INTO `Reply` (Result, Node_NodeID, Question_QuestionID) VALUES (%s, %s, %s)"
|
||||
processedData = json.loads(data)
|
||||
|
||||
processedQuestionID = (processedData['QuestionID'])
|
||||
processedResponse = (processedData['Response'])
|
||||
|
||||
pushingDataArray = [(processedResponse, nodeID, processedQuestionID)]
|
||||
|
||||
for i in pushingDataArray:
|
||||
cursor.execute(query, i)
|
||||
mydb.commit()
|
||||
except mysql.connector.Error as err:
|
||||
print("MySQL Error:", err)
|
||||
finally:
|
||||
cursor.close()
|
||||
mydb.close()
|
||||
|
||||
async def receive_data():
|
||||
uri = "ws://145.92.8.114/ws"
|
||||
try:
|
||||
async with websockets.connect(uri) as websocket:
|
||||
while True:
|
||||
print("true")
|
||||
data = await websocket.recv()
|
||||
print(f"Received data: {data}")
|
||||
|
||||
@@ -73,14 +28,13 @@ async def receive_data():
|
||||
await getNodeInfo('sensor')
|
||||
await getNodeInfo('enquete')
|
||||
|
||||
print(sensorNodeArray)
|
||||
|
||||
if macAdress in sensorNodeArray:
|
||||
nodeID = await getNodeID(macAdress)
|
||||
await processSensorNodeData(data, nodeID)
|
||||
await SensorNode.processSensorNodeData(data, nodeID)
|
||||
elif macAdress in enqueteNodeArray:
|
||||
nodeID = await getNodeID(macAdress)
|
||||
await processEnqueteNodeData(data, nodeID)
|
||||
await EnqueteNode.processEnqueteNodeData(data, nodeID)
|
||||
else:
|
||||
await newNode(macAdress, type)
|
||||
except websockets.ConnectionClosedError as e:
|
||||
@@ -89,17 +43,8 @@ async def receive_data():
|
||||
async def main():
|
||||
await receive_data()
|
||||
|
||||
def dbLogin():
|
||||
mydb = mysql.connector.connect(
|
||||
host="localhost",
|
||||
user="root",
|
||||
password="Dingleberries69!",
|
||||
database="NodeData"
|
||||
)
|
||||
|
||||
return mydb
|
||||
|
||||
async def getNodeInfo(type):
|
||||
print("getNodeINfo")
|
||||
global sensorNodeArray
|
||||
global enqueteNodeArray
|
||||
|
||||
@@ -114,14 +59,12 @@ async def getNodeInfo(type):
|
||||
for tuples in nodeInfo:
|
||||
for item in tuples:
|
||||
nodeInfoArray.append(item)
|
||||
print(nodeInfoArray)
|
||||
|
||||
cursor.close()
|
||||
mydb.close()
|
||||
|
||||
if type == 'sensor':
|
||||
sensorNodeArray = nodeInfoArray
|
||||
print(sensorNodeArray)
|
||||
return sensorNodeArray
|
||||
|
||||
elif type == 'enquete':
|
||||
@@ -143,7 +86,7 @@ async def getNodeID(macAdress):
|
||||
return nodeID
|
||||
|
||||
async def newNode(mac, type):
|
||||
id = (mac, type,)
|
||||
id = (mac, type)
|
||||
mydb = dbLogin()
|
||||
|
||||
cursor = mydb.cursor()
|
26
server/web-data-connection/databaseGeneralClass.py
Normal file
26
server/web-data-connection/databaseGeneralClass.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import mysql.connector
|
||||
|
||||
def dbLogin():
|
||||
mydb = mysql.connector.connect(
|
||||
host="localhost",
|
||||
user="root",
|
||||
password="Dingleberries69!",
|
||||
database="NodeData"
|
||||
)
|
||||
return mydb
|
||||
|
||||
class Node():
|
||||
def __init__(self, macAdress):
|
||||
self.macAdress = macAdress
|
||||
self.id = None
|
||||
|
||||
def getNodeId(self):
|
||||
id = (self.macAdress,)
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
cursor.execute("""SELECT nodeID FROM Node WHERE MAC = %s""", id)
|
||||
data = cursor.fetchall()
|
||||
|
||||
for tuples in data:
|
||||
for item in tuples:
|
||||
self.id = item
|
155
server/web-data-connection/datatransfer.md
Normal file
155
server/web-data-connection/datatransfer.md
Normal file
@@ -0,0 +1,155 @@
|
||||
## The websocket -> database connection classes.
|
||||
I have made several classes to make the database connection more clear and easyer to overlook.
|
||||
This here is the main file where the data for each function is collected and ready to be sent to the database.
|
||||
|
||||
In the file : "data.py" the primary connections are made to the websocket and the data recieved is split off to see which type of node came back.
|
||||
|
||||
These types can be the "sensorNode"(the nodes that are located around the school) and the "enqueteNode"(a questionaire node which also collects data.).
|
||||
|
||||
```py
|
||||
#Importing all different files from all the nodes which are on different pages
|
||||
import asyncio
|
||||
import websockets
|
||||
import json
|
||||
from class_SensorNode import SensorNode
|
||||
from class_enqueteNode import EnqueteNode
|
||||
from classes_data import dbLogin
|
||||
|
||||
#Making global variables
|
||||
sensorNodeArray = []
|
||||
enqueteNodeArray = []
|
||||
```
|
||||
These array's need to be global because of the several uses later in the code. These cannot be bound to a singular function.
|
||||
|
||||
The following function is meant to connect to the websocket and after this, process the gained Json data from the socket.
|
||||
|
||||
Once this is done get the info if the data comming from the websocket is from a "sensor-node" or a "questionairre-node".
|
||||
|
||||
once this information is gained, decide if this new node is a not yet existing connection with the database or if it was already inserted.
|
||||
|
||||
In the case that the node didn't connect to the database, a new node is made with this new MAC address.
|
||||
|
||||
These functions are put in different classes for a better overview of the whole project and a better understanding of what function is supposed to do what.
|
||||
```py
|
||||
#Connection making with the websocket
|
||||
async def receive_data():
|
||||
uri = "ws://145.92.8.114/ws"
|
||||
try:
|
||||
async with websockets.connect(uri) as websocket:
|
||||
while True:
|
||||
print("true")
|
||||
data = await websocket.recv()
|
||||
print(f"Received data: {data}")
|
||||
|
||||
processedData = json.loads(data)
|
||||
macAdress = processedData['node']
|
||||
|
||||
#A function to see if the node is one of two types.
|
||||
if "Temp" in processedData:
|
||||
type = 'sensor'
|
||||
else:
|
||||
type = 'enquete'
|
||||
|
||||
await getNodeInfo('sensor')
|
||||
await getNodeInfo('enquete')
|
||||
|
||||
#Get the node id and use it in functions seperate from this file.
|
||||
if macAdress in sensorNodeArray:
|
||||
nodeID = await getNodeID(macAdress)
|
||||
await SensorNode.processSensorNodeData(data, nodeID)
|
||||
elif macAdress in enqueteNodeArray:
|
||||
nodeID = await getNodeID(macAdress)
|
||||
await EnqueteNode.processEnqueteNodeData(data, nodeID)
|
||||
else:
|
||||
await newNode(macAdress, type)
|
||||
# Error message if smth went wrong
|
||||
except websockets.ConnectionClosedError as e:
|
||||
print("WebSocket connection closed:", e)
|
||||
|
||||
#Wait for data to come in.
|
||||
async def main():
|
||||
await receive_data()
|
||||
```
|
||||
The following function is made to set the different node types appart, this is done by putting down these global variables.
|
||||
These variables are doen this way because the python-scope could not reach inside some parts of the files.
|
||||
|
||||
A bit further a array is made to holde the node info. this is so the information of the node can be sepperated and held.
|
||||
|
||||
After the array, a type tuple is made. (A tuple is a type of info which acts in a way like a array. For more info visit https://www.w3schools.com/python/python_tuples.asp)
|
||||
|
||||
Then another connection to the database is made to gather all existing mac-addresses.
|
||||
|
||||
Then a for-loop is made to see if the incomming MAC is existing, if this isn't the case, add it to the array.
|
||||
|
||||
After, if the given type from the previous function is a sensor-, or questionaire-node
|
||||
```py
|
||||
#By python's scuffed we had to use global variables.
|
||||
async def getNodeInfo(type):
|
||||
print("getNodeINfo")
|
||||
global sensorNodeArray
|
||||
global enqueteNodeArray
|
||||
|
||||
#New array which is needed.
|
||||
nodeInfoArray = []
|
||||
|
||||
# make a connection to the databasse, then gather all MAC-adresses from it.
|
||||
id = (type,)
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
cursor.execute("""SELECT MAC FROM Node WHERE Type = %s""", id)
|
||||
#Fetch all info(get all info)
|
||||
nodeInfo = cursor.fetchall()
|
||||
|
||||
#Go along each tuple in nodeinfo and each item in tuple, append(item)
|
||||
for tuples in nodeInfo:
|
||||
for item in tuples:
|
||||
nodeInfoArray.append(item)
|
||||
|
||||
cursor.close()
|
||||
mydb.close()
|
||||
|
||||
#If the type is a sensor do this,
|
||||
if type == 'sensor':
|
||||
sensorNodeArray = nodeInfoArray
|
||||
return sensorNodeArray
|
||||
|
||||
#Else, this if statement
|
||||
elif type == 'enquete':
|
||||
enqueteNodeArray = nodeInfoArray
|
||||
return enqueteNodeArray
|
||||
```
|
||||
The next function acts as a node ID fetcher, it searches the database for information regarding the nodeID's.
|
||||
|
||||
Like the previous function, It adds the new ID id this is not yet existent.
|
||||
```py
|
||||
async def getNodeID(macAdress):
|
||||
id = (macAdress,)
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
cursor.execute("""SELECT nodeID FROM Node WHERE MAC = %s""", id)
|
||||
data = cursor.fetchall()
|
||||
|
||||
#Again, all tuples in data, all items for all tuples, nodeID
|
||||
for tuples in data:
|
||||
for item in tuples:
|
||||
nodeID = item
|
||||
|
||||
return nodeID
|
||||
```
|
||||
The following function will take the previous information and process it and push it to the database.
|
||||
|
||||
First the connection, then the query, then insert the data into the query and send it off.
|
||||
```py
|
||||
async def newNode(mac, type):
|
||||
id = (mac, type)
|
||||
mydb = dbLogin()
|
||||
|
||||
cursor = mydb.cursor()
|
||||
#Insert will insert it into the given location in the database.
|
||||
cursor.execute("INSERT INTO `Node` (MAC, Type) VALUES (%s, %s)", id)
|
||||
#A simple print to show that the process has been executed succesfully.
|
||||
print("new node assigned")
|
||||
mydb.commit()
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
78
server/web-data-connection/enqueteClassFile.md
Normal file
78
server/web-data-connection/enqueteClassFile.md
Normal file
@@ -0,0 +1,78 @@
|
||||
## Questionaire class
|
||||
This File and class are dedicated to storing/using data that is related to the questionaire. This class is primairly used as a gateway for pushing data and loading data.
|
||||
|
||||
By doing this a lot of space is saved on the main file and the readability wil increase.
|
||||
|
||||
By doing this, it also solves the issues with the very precise naming and the often similar types of names.
|
||||
This way it ensures no confusion on what the purpous of each segement is.
|
||||
|
||||
First up this page imports different types of information, like the library's and the needed files and/or Node.
|
||||
|
||||
|
||||
```py
|
||||
#Importing different librarys.
|
||||
import mysql.connector
|
||||
import json
|
||||
#Importing different classes.
|
||||
from classes_data import Node
|
||||
from classes_data import dbLogin
|
||||
```
|
||||
Here a Class is made as a child-class of the parent-class: "Node".
|
||||
|
||||
This clas first makes a private variable, this being: "__query".
|
||||
This is done so no other outside sources can interfere with this query and potentially cause problems down the line.
|
||||
|
||||
After this the "__init__"function is called.
|
||||
(In javascript this is known as the "constructor".
|
||||
For further information visit https://www.w3schools.com/python/gloss_python_class_init.asp)
|
||||
|
||||
Because this class is a child class, we want to use some functionality from the parent class. That is where the "super" comes in. This makes it so the class uses the values and propperties of the parent class. (for more information visit https://www.w3schools.com/python/python_inheritance.asp)
|
||||
|
||||
The rest of the class contains a function in which the gatherd data
|
||||
gets put into the database using the query and the gatherd data.
|
||||
```py
|
||||
#Node is between brackets to show that this class is a child class from the parent class "Node"
|
||||
class EnqueteNode(Node):
|
||||
#A private query to use later in a function.
|
||||
__query = "INSERT INTO `Reply` (Result, Node_NodeID, Question_QuestionID) VALUES (%s, %s, %s)"
|
||||
#use a super to get info from the parent class.
|
||||
def __init__(self, macAdress, response, questionID):
|
||||
super().__init__(macAdress)
|
||||
self.response = response
|
||||
self.questionID = questionID
|
||||
```
|
||||
The following function is meant to make a database connection, get the data from the websocket (this data will only be from the questionaire-node)
|
||||
and send the gotten data to the database.
|
||||
|
||||
The function starts with the database connection and calls uppon the websocket data.
|
||||
It then creates variables with the data to put it in an array.
|
||||
|
||||
This array then gets sorted and pushed thogether with the query to correctly sort it and push it to the database.
|
||||
|
||||
In case of an error, it also asks for errors and prints it.
|
||||
```py
|
||||
#making a database connection to then load in the processed data.
|
||||
async def processEnqueteNodeData(data, nodeID):
|
||||
try:
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
#Getting the websocket data.
|
||||
processedData = json.loads(data)
|
||||
#Making variables of the different types of data.
|
||||
EnqueteNode.questionID = (processedData['QuestionID'])
|
||||
EnqueteNode.response = (processedData['Response'])
|
||||
|
||||
#An array with the data to push.
|
||||
pushingDataArray = [(EnqueteNode.questionID, nodeID, EnqueteNode.response)]
|
||||
|
||||
#Push the data according to the query to the database.
|
||||
for i in pushingDataArray:
|
||||
cursor.execute(EnqueteNode.__query, i)
|
||||
mydb.commit()
|
||||
#print an error.
|
||||
except mysql.connector.Error as err:
|
||||
print("MySQL Error:", err)
|
||||
finally:
|
||||
cursor.close()
|
||||
mydb.close()
|
||||
```
|
36
server/web-data-connection/enqueteNodeClass.py
Normal file
36
server/web-data-connection/enqueteNodeClass.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import mysql.connector
|
||||
import json
|
||||
|
||||
from classes_data import Node
|
||||
from classes_data import dbLogin
|
||||
|
||||
class EnqueteNode(Node):
|
||||
__query = "INSERT INTO `Reply` (Result, Node_NodeID, Question_QuestionID) VALUES (%s, %s, %s)"
|
||||
|
||||
|
||||
def __init__(self, macAdress, response, questionID):
|
||||
super().__init__(macAdress)
|
||||
self.response = response
|
||||
self.questionID = questionID
|
||||
|
||||
async def processEnqueteNodeData(data, nodeID):
|
||||
try:
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
|
||||
processedData = json.loads(data)
|
||||
|
||||
EnqueteNode.questionID = (processedData['QuestionID'])
|
||||
EnqueteNode.response = (processedData['Response'])
|
||||
|
||||
pushingDataArray = [(EnqueteNode.questionID, nodeID, EnqueteNode.response)]
|
||||
|
||||
for i in pushingDataArray:
|
||||
print(EnqueteNode.__query, i)
|
||||
cursor.execute(EnqueteNode.__query, i)
|
||||
mydb.commit()
|
||||
except mysql.connector.Error as err:
|
||||
print("MySQL Error:", err)
|
||||
finally:
|
||||
cursor.close()
|
||||
mydb.close()
|
69
server/web-data-connection/generalDatabaseFile.md
Normal file
69
server/web-data-connection/generalDatabaseFile.md
Normal file
@@ -0,0 +1,69 @@
|
||||
## General node file.
|
||||
This File includes several main (verry important) components:
|
||||
The Node parent class and the database log- function.
|
||||
|
||||
The database funcion is used in almost every class and almost every function, so I put it here in a centeral location.
|
||||
|
||||
The reason it isn't in the main file is because the main file imports all classes and this function, but once the classes are called, this function can't be called up. So it needed to be in a file where all other files take information from and not put information in.
|
||||
|
||||
The file begings with importing a library that handles the database connection.
|
||||
```py
|
||||
#Importing a database library to connect to the database.
|
||||
import mysql.connector
|
||||
```
|
||||
Here the database log-in function is made, this allows functions to call for it and make a connection.
|
||||
```py
|
||||
def dbLogin():
|
||||
#This variable is used as a latch point to connect to the database with the correct log-in.
|
||||
mydb = mysql.connector.connect(
|
||||
host="localhost",
|
||||
user="root",
|
||||
password="**********",
|
||||
database="NodeData"
|
||||
)
|
||||
return mydb
|
||||
```
|
||||
After the function, a central class is made to act as a parent class for every other class that has something to do with the nodes and the data.
|
||||
|
||||
The class has some interchangable variables and primairly asks the incomming data for its mac adress and looks for any similarities.
|
||||
|
||||
It primairly gives the main MAC-address over to the child-classes so these can be used.
|
||||
|
||||
This class might seem small, but is not to be underestimated.
|
||||
|
||||
At the start of every class the "__init__" function is called.
|
||||
(In javascript this is known as the "constructor".
|
||||
For further information visit https://www.w3schools.com/python/gloss_python_class_init.asp)
|
||||
|
||||
The parent node is able to be further evolved, but for now is able do always provide the node ID to all child-classes.
|
||||
and a variable with the MAC-addresses.
|
||||
```py
|
||||
# A general class which acts as a database connector and a node identifier
|
||||
class Node():
|
||||
|
||||
def __init__(self, macAdress):
|
||||
self.macAdress = macAdress
|
||||
self.id = None
|
||||
```
|
||||
The function below uses the infromation from the database to find the corresponding node ID from the gotten MAc-address.
|
||||
|
||||
It searches inside of the database and finds a match with the given MAC-address.
|
||||
|
||||
It first makes a connection with the database by calling the previous function, then it executes the given querry, and searches for the node ID corresponding with the inserted MAC-address.
|
||||
|
||||
After this search, it inserts that given id into the ID variable.
|
||||
```py
|
||||
def getNodeId(self):
|
||||
id = (self.macAdress,)
|
||||
#loging in
|
||||
mydb = dbLogin()
|
||||
#make a cursor.
|
||||
cursor = mydb.cursor()
|
||||
cursor.execute("""SELECT nodeID FROM Node WHERE MAC = %s""", id)
|
||||
#get all the data
|
||||
data = cursor.fetchall()
|
||||
#make a for-loop to go along all the tuples in the data, and then all items in the tupe and those items get put into the ID
|
||||
for tuples in data:
|
||||
for item in tuples:
|
||||
self.id = item
|
||||
```
|
41
server/web-data-connection/sensorNodeClass.py
Normal file
41
server/web-data-connection/sensorNodeClass.py
Normal file
@@ -0,0 +1,41 @@
|
||||
import mysql.connector
|
||||
import json
|
||||
|
||||
from classes_data import Node
|
||||
from classes_data import dbLogin
|
||||
|
||||
class SensorNode(Node):
|
||||
__query = "INSERT INTO `Measurement` (NodeID, Type, Value) VALUES (%s, %s, %s)"
|
||||
|
||||
def __init__(self, macAdress, temp, humi, eCO2, TVOC):
|
||||
super().__init__(macAdress)
|
||||
self.temperature = temp
|
||||
self.humidity = humi
|
||||
self.eCO2 = eCO2
|
||||
self.TVOC = TVOC
|
||||
|
||||
async def processSensorNodeData(data, nodeID):
|
||||
try:
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
|
||||
processedData = json.loads(data)
|
||||
|
||||
SensorNode.temperature = (processedData['Temp'])
|
||||
SensorNode.humidity = (processedData['Humi'])
|
||||
SensorNode.eCO2 = (processedData['eCO2'])
|
||||
SensorNode.TVOC = (processedData['TVOC'])
|
||||
|
||||
|
||||
|
||||
pushingDataArray = [(nodeID, "Temp", SensorNode.temperature), (nodeID, "Humi", SensorNode.humidity), (nodeID, "eCO2", SensorNode.eCO2), (nodeID, "TVOC", SensorNode.TVOC)]
|
||||
for i in pushingDataArray:
|
||||
cursor.execute(SensorNode.__query, i)
|
||||
mydb.commit()
|
||||
|
||||
|
||||
except mysql.connector.Error as err:
|
||||
print("MySQL Error:", err)
|
||||
finally:
|
||||
cursor.close()
|
||||
mydb.close()
|
77
server/web-data-connection/sensorNodeClassFile.md
Normal file
77
server/web-data-connection/sensorNodeClassFile.md
Normal file
@@ -0,0 +1,77 @@
|
||||
## Sensor Node class
|
||||
This class is made with a intention to store/transfer the data collected from the several node's that are going to be placed around school.
|
||||
|
||||
This is done to reduce confusion on what every function is meant to do.
|
||||
|
||||
This file imports the needed files/classes to function.
|
||||
Aswell as the important librarys.I
|
||||
```py
|
||||
#Import library's
|
||||
import mysql.connector
|
||||
import json
|
||||
#Import classes and functions from different files.
|
||||
from classes_data import Node
|
||||
from classes_data import dbLogin
|
||||
```
|
||||
The class then (being a child class) creates a private query.
|
||||
This is done this way because no other files need this query so it is only logical to private this.
|
||||
|
||||
After this the "__init__"function is called.
|
||||
(In javascript this is known as the "constructor".
|
||||
For further information visit https://www.w3schools.com/python/gloss_python_class_init.asp)
|
||||
|
||||
The "__init__" here makes variables for all the data the node needs to collect and sends to the database.
|
||||
|
||||
After this a function to send the gotten data to the databse is made.
|
||||
|
||||
The function gets all the data, puts this in an array to then send it to the database according to the given query so it gets placed correctly.
|
||||
```py
|
||||
#A class to send data to the database. child class of "Node"
|
||||
class SensorNode(Node):
|
||||
#A private query only to be used in this class.
|
||||
__query = "INSERT INTO `Measurement` (NodeID, Type, Value) VALUES (%s, %s, %s)"
|
||||
|
||||
#All variables to be used.
|
||||
def __init__(self, macAdress, temp, humi, eCO2, TVOC):
|
||||
super().__init__(macAdress)
|
||||
self.temperature = temp
|
||||
self.humidity = humi
|
||||
self.eCO2 = eCO2
|
||||
self.TVOC = TVOC
|
||||
```
|
||||
The function starts with getting the database connection with the "dblogin" function and gains the the data from the websocket in the variable "processedData".
|
||||
|
||||
This data is then split up into different types, these are then inserted into an array.
|
||||
|
||||
The newly made array then gets pulled appart and the query is then combined with each segment, thereby succesfully inserting the data into it and then pushing it to the database following its guidline.
|
||||
|
||||
In case of a database error, a function is made to show said error.
|
||||
This helps with identifying problems and potentially fixing it.
|
||||
```py
|
||||
#A function to connect to the database, grab the info which is given, and push this to the database.
|
||||
async def processSensorNodeData(data, nodeID):
|
||||
try:
|
||||
mydb = dbLogin()
|
||||
cursor = mydb.cursor()
|
||||
|
||||
processedData = json.loads(data)
|
||||
#The variables to give to the database.
|
||||
SensorNode.temperature = (processedData['Temp'])
|
||||
SensorNode.humidity = (processedData['Humi'])
|
||||
SensorNode.eCO2 = (processedData['eCO2'])
|
||||
SensorNode.TVOC = (processedData['TVOC'])
|
||||
|
||||
#A array of the info to be given to the database in the correct format.
|
||||
pushingDataArray = [(nodeID, "Temp", SensorNode.temperature), (nodeID, "Humi", SensorNode.humidity), (nodeID, "eCO2", SensorNode.eCO2), (nodeID, "TVOC", SensorNode.TVOC)]
|
||||
#Go along all files of the array, and push it out to the database following the query.
|
||||
for i in pushingDataArray:
|
||||
cursor.execute(SensorNode.__query, i)
|
||||
mydb.commit()
|
||||
|
||||
|
||||
except mysql.connector.Error as err:
|
||||
print("MySQL Error:", err)
|
||||
finally:
|
||||
cursor.close()
|
||||
mydb.close()
|
||||
```
|
BIN
server/web-data-connection/umlpython.png
Normal file
BIN
server/web-data-connection/umlpython.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
@@ -8,11 +8,16 @@ class Graph {
|
||||
this.tvocArray = [];
|
||||
}
|
||||
|
||||
createDiv() {
|
||||
let div = document.createElement("div");
|
||||
let graphDiv = document.createElement("div");
|
||||
div.setAttribute("class", "graphBody");
|
||||
graphDiv.setAttribute("id", this.id);
|
||||
div.appendChild(graphDiv);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
// Function to create a graph
|
||||
makeGraph(line, lineColor, name) {
|
||||
let div = document.createElement("div");
|
||||
div.setAttribute("id", this.id);
|
||||
document.body.appendChild(div);
|
||||
let lineArray;
|
||||
switch (line) {
|
||||
case "temp":
|
||||
@@ -132,10 +137,11 @@ class DataProcessor {
|
||||
|
||||
makeGraph() {
|
||||
this.graph = new Graph(1);
|
||||
this.graph.createDiv();
|
||||
this.graph.makeGraph("temp", "red", "Temperature");
|
||||
this.graph.makeGraph("humi", "blue", "Humidity");
|
||||
this.graph.makeGraph("eco2", "green", "eCO2");
|
||||
this.graph.makeGraph("tvoc", "#F5G644", "TVOC");
|
||||
this.graph.makeGraph("tvoc", "black", "TVOC");
|
||||
}
|
||||
|
||||
updateGraph() {
|
||||
|
@@ -2,6 +2,7 @@
|
||||
const data = [];
|
||||
processor = new DataProcessor();
|
||||
let link;
|
||||
nodeDataArray = {};
|
||||
|
||||
// Function to create checkbox with label
|
||||
function createCheckBox(id, label) {
|
||||
@@ -16,7 +17,41 @@ function createCheckBox(id, label) {
|
||||
|
||||
return { checkbox, checkboxLabel };
|
||||
}
|
||||
fetch("http://145.92.8.114/getNodeInfo?macAdress=*")
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
nodeInputs(data);
|
||||
})
|
||||
|
||||
function nodeInputs(JSONdata) {
|
||||
select = document.getElementById('node-input');
|
||||
|
||||
console.log(JSONdata);
|
||||
for (var i = 0; i < JSONdata.length; i++) {
|
||||
console.log(JSONdata[i]);
|
||||
console.log(option)
|
||||
var node = JSONdata[i].NodeID;
|
||||
var name = JSONdata[i].Name;
|
||||
var location = JSONdata[i].Location;
|
||||
nodeDataArray[node] = { name: name, location: location };
|
||||
// Create new option element
|
||||
var option = document.createElement('option');
|
||||
|
||||
// Set the value of the option
|
||||
option.value = node;
|
||||
|
||||
// Set the text of the option
|
||||
option.text = name;
|
||||
|
||||
// Add the option to the select
|
||||
select.add(option);
|
||||
}
|
||||
}
|
||||
// Create HTML input elements for user input
|
||||
const container = document.createElement("div");
|
||||
container.setAttribute("class", "container");
|
||||
@@ -102,11 +137,10 @@ container.appendChild(dateFilter);
|
||||
const nodeFilter = document.createElement("div");
|
||||
nodeFilter.setAttribute("class", "node-filter");
|
||||
|
||||
const nodeInput = document.createElement("input");
|
||||
nodeInput.setAttribute("type", "text");
|
||||
const nodeInput = document.createElement("select");
|
||||
nodeInput.setAttribute("type", "select");
|
||||
nodeInput.setAttribute("placeholder", "Enter Node Name (* for all)");
|
||||
nodeInput.setAttribute("id", "node-input");
|
||||
nodeInput.setAttribute("class", "input-field");
|
||||
|
||||
nodeFilter.appendChild(nodeInput);
|
||||
container.appendChild(nodeFilter);
|
||||
|
@@ -2,6 +2,7 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="styles/graph-styles.css">
|
||||
@@ -20,16 +21,15 @@
|
||||
<li class="nav-item">
|
||||
<a href="graphs.html" class="nav-link">Graphs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="settings.html" class="nav-link">Settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="questions-dashboard.html" class="nav-link">Questions</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<body>
|
||||
<script src="graph-classes.js"></script>
|
||||
<script src="graph-main.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -9,33 +9,45 @@
|
||||
<script src="https://cdn.plot.ly/plotly-latest.min.js" charset="utf-8"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
|
||||
rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<nav class="navbar">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a href="index.html" class="nav-link">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="graphs.html" class="nav-link">Graphs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="questions-dashboard.html" class="nav-link">Questions</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<button class="nav-button" onclick="settings()">Change node names ⌄</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="editNode">
|
||||
<p id="text">Status updating</p>
|
||||
<select id="mySelect"></select>
|
||||
<textarea maxlength="44" id="inputName"></textarea>
|
||||
<textarea maxlength="44" id="inputLocation"></textarea>
|
||||
<button id="button" onclick="changeText()">Change Information</button>
|
||||
</div>
|
||||
|
||||
|
||||
<body>
|
||||
<nav class="navbar">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a href="index.html" class="nav-link">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="graphs.html" class="nav-link">Graphs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="settings.html" class="nav-link">Settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="questions-dashboard.html" class="nav-link">Questions</a>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<script src="https://cdn.plot.ly/plotly-latest.min.js" charset="utf-8"></script>
|
||||
<script src="GaugGroup.js"></script>
|
||||
<script src="graph-classes.js"></script>
|
||||
<script src="main.js"></script>
|
||||
<script src="text.js"></script>
|
||||
<script src="liveGraph.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@@ -141,4 +141,4 @@ function toggleGraph(nodeId) {
|
||||
} else {
|
||||
console.error('No element found with id: liveGraph' + nodeId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,14 +11,14 @@ class QuestionCreationClass {
|
||||
label: 'Responses',
|
||||
data: this.data,
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
'rgba(255, 206, 86, 0.2)'
|
||||
'rgba(255, 206, 86, 0.2)',
|
||||
'rgba(255, 99, 132, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(255, 99, 132, 1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)'
|
||||
'rgba(255, 206, 86, 1)',
|
||||
'rgba(255, 99, 132, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
|
@@ -20,9 +20,6 @@
|
||||
<li class="nav-item">
|
||||
<a href="graphs.html" class="nav-link">Graphs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="settings.html" class="nav-link">Settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="questions-dashboard.html" class="nav-link">Questions</a>
|
||||
</ul>
|
||||
|
@@ -1,18 +1,55 @@
|
||||
//For now create dummy data to show on the website.
|
||||
let dummydata1 = [40, 30, 20];
|
||||
let questionOptionsDummy1 = ['disgusting','clean', 'fine'];
|
||||
let awa;
|
||||
|
||||
let dummydata2 = [25, 35, 40];
|
||||
let questionOptionsDummy2 = ['disgusting', 'clean', 'normal'];
|
||||
data();
|
||||
|
||||
let dummydata3 = [30, 20, 20];
|
||||
let questionOptionsDummy3 = ['cold', 'perfect', 'hot'];
|
||||
|
||||
let dummydata4 = [30, 20, 20];
|
||||
let questionOptionsDummy4 = ['really crowded','not at all', 'its fine', ];
|
||||
async function data() {
|
||||
fetch("http://145.92.8.114/getQuestionData")
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
|
||||
let dummydata5 = [30, 20, 20];
|
||||
let questionOptionsDummy5 = ['no','yes', 'decently'];
|
||||
// Initialize an array to hold the counts for each question
|
||||
let questionCounts = [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]];
|
||||
|
||||
// Iterate over the data
|
||||
for (let item of data) {
|
||||
// Increment the count for the appropriate question and result
|
||||
questionCounts[item.Question_QuestionID - 1][item.Result]++;
|
||||
}
|
||||
|
||||
// Log the counts for each question
|
||||
for (let i = 0; i < questionCounts.length; i++) {
|
||||
console.log(`Question ${i + 1} counts: ${questionCounts[i]}`);
|
||||
}
|
||||
|
||||
// Update the dummydata arrays
|
||||
dummydata1 = questionCounts[0];
|
||||
dummydata2 = questionCounts[1];
|
||||
dummydata3 = questionCounts[2];
|
||||
dummydata4 = questionCounts[3];
|
||||
dummydata5 = questionCounts[4];
|
||||
|
||||
graph();
|
||||
})
|
||||
}
|
||||
// for each(Result == 0) in
|
||||
async function graph() {
|
||||
let questionOptionsDummy1 = ['clean','fine', 'disgusting'];
|
||||
|
||||
let questionOptionsDummy2 = ['clean', 'normal', 'disgusting'];
|
||||
|
||||
let questionOptionsDummy3 = ['hot', 'perfect', 'cold'];
|
||||
|
||||
let questionOptionsDummy4 = ['not at all', 'its fine', 'really crowded', ];
|
||||
|
||||
let questionOptionsDummy5 = ['no','decently', 'yes'];
|
||||
|
||||
//make arrays to store data.
|
||||
let chartConfigArray = [];
|
||||
@@ -52,3 +89,4 @@ const myChart4 = new Chart(ctx4, chartConfigArray[3].chartConfig);
|
||||
|
||||
const ctx5 = document.getElementById('chart5').getContext('2d');
|
||||
const myChart5 = new Chart(ctx5, chartConfigArray[4].chartConfig);
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="styles/settings-styles.css">
|
||||
<title>Gauges</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<nav class="navbar">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a href="index.html" class="nav-link">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="graphs.html" class="nav-link">Graphs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="settings.html" class="nav-link">Settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="questions-dashboard.html" class="nav-link">Questions</a>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
|
||||
<body>
|
||||
<script src="main.js"></script>
|
||||
<script src="GaugGroup.js"></script>
|
||||
</body>
|
||||
</html>
|
@@ -1,202 +1,249 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
font-family: "Roboto", sans-serif;
|
||||
|
||||
|
||||
box-sizing: border-box;
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
/*
|
||||
.editNodeInformation{
|
||||
margin-bottom: 60%;
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
} */
|
||||
|
||||
.nav-button {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
#editNode {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
margin-top: 8vh;
|
||||
background-color: #f0f0f0;
|
||||
flex-direction: column;
|
||||
background-color: #afafaf;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
.gaugeGroup {
|
||||
margin-top: 1%;
|
||||
width: 98vw;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column; /* Keep as column */
|
||||
justify-content: flex-start;
|
||||
height: 20vh;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 50px;
|
||||
border: 2px solid #333;
|
||||
clear: both;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
margin-bottom: 2vh;
|
||||
/* position: relative; */
|
||||
/* float: top; */
|
||||
}
|
||||
|
||||
#mySelect {
|
||||
margin-left: 45vh;
|
||||
}
|
||||
|
||||
#text {
|
||||
font-size: 20px;
|
||||
margin-left: 25vh;
|
||||
}
|
||||
|
||||
#inputName {
|
||||
margin-left: 5vh;
|
||||
margin-right: 5vh;
|
||||
}
|
||||
|
||||
#inputLocation {
|
||||
margin-right: 5vh;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
margin-top: 8vh;
|
||||
background-color: #f0f0f0;
|
||||
flex-direction: column;
|
||||
background-color: #afafaf;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gaugeGroup {
|
||||
margin-top: 2vh;
|
||||
width: 98vw;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column; /* Keep as column */
|
||||
justify-content: flex-start;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 50px;
|
||||
border: 2px solid #333;
|
||||
clear: both;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.groupTitle {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.Node {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Sensorvalues {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
width: 15%;
|
||||
height: 110%;
|
||||
background-color: #ddd;
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding-top: 3vh; /* Increase bottom padding */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
flex-wrap: wrap;
|
||||
width: 15%;
|
||||
height: 110%;
|
||||
background-color: #ddd;
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding-top: 3vh; /* Increase bottom padding */
|
||||
}
|
||||
|
||||
.gaugeContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 80%; /* Increase the height from 70% to 80% */
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
height: 5vh;
|
||||
margin-bottom: 1vh;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 80%; /* Increase the height from 70% to 80% */
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
height: 5vh;
|
||||
margin-bottom: 1vh;
|
||||
}
|
||||
|
||||
.gaugeImage {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 120%;
|
||||
object-fit: contain;
|
||||
position: absolute; /* Make the image position absolute */
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1; /* Make the image display below the needle */
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 120%;
|
||||
object-fit: contain;
|
||||
position: absolute; /* Make the image position absolute */
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1; /* Make the image display below the needle */
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.gaugeValue, .gaugeText {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
z-index: 2;
|
||||
.gaugeValue,
|
||||
.gaugeText {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.gaugeText {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 1.4vw;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: -1.4vw; /* Adjust this value to move the text further down */
|
||||
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 1.4vw;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
top: -1.4vw; /* Adjust this value to move the text further down */
|
||||
}
|
||||
|
||||
.arrowimg {
|
||||
width: 3vh;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
position: absolute;
|
||||
top: 0.5vw;
|
||||
right: 1.2vw;
|
||||
z-index: 2;
|
||||
width: 3vh;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
position: absolute;
|
||||
top: 0.5vw;
|
||||
right: 1.2vw;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.valueContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#valueText {
|
||||
font-size: 20px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.needle {
|
||||
position: absolute;
|
||||
bottom: -10%; /* Lower the needle to the bottom */
|
||||
left: 50%;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
transform-origin: bottom;
|
||||
z-index: 3; /* Make the needle display above the image */
|
||||
transition: transform 0.1s ease-out;
|
||||
|
||||
position: absolute;
|
||||
bottom: -10%; /* Lower the needle to the bottom */
|
||||
left: 50%;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
transform-origin: bottom;
|
||||
z-index: 3; /* Make the needle display above the image */
|
||||
transition: transform 0.1s ease-out;
|
||||
}
|
||||
|
||||
.contentContainer {
|
||||
display: flex;
|
||||
flex-direction: row; /* Layout children side by side */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row; /* Layout children side by side */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #333;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
position: fixed; /* Fix the navbar at the top of the page */
|
||||
top: 0; /* Position it at the very top */
|
||||
width: 100%; /* Make it span the full width of the page */
|
||||
z-index: 1000; /* Make sure it's above all other elements */
|
||||
background-color: #333;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
position: fixed; /* Fix the navbar at the top of the page */
|
||||
top: 0; /* Position it at the very top */
|
||||
width: 100%; /* Make it span the full width of the page */
|
||||
z-index: 1000; /* Make sure it's above all other elements */
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center; /* Center the links horizontally */
|
||||
height: 100%;
|
||||
width: 100%; /* Make it span the full width of the navbar */
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center; /* Center the links horizontally */
|
||||
height: 100%;
|
||||
width: 100%; /* Make it span the full width of the navbar */
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin-right: 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.plotly-container {
|
||||
width: 100%;
|
||||
float: bottom;
|
||||
padding: 1vw;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
float: bottom;
|
||||
padding: 1vw;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.js-plotly-plot {
|
||||
width: 90%;
|
||||
height: 100%;
|
||||
width: 90%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
|
||||
}
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
@@ -46,11 +46,12 @@ body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border: 2px solid #ccc;
|
||||
align-self: center;
|
||||
border: 3px solid #ccc;
|
||||
border-radius: 10px;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
width: 90;
|
||||
width: 95%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -108,9 +109,20 @@ body {
|
||||
}
|
||||
|
||||
.js-plotly-plot {
|
||||
align-self: center;
|
||||
width: 95%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 10px;
|
||||
align-self: center center;
|
||||
}
|
||||
/* Additional styling as needed */
|
||||
|
||||
.graphBody {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
border: 3px solid #ccc;
|
||||
border-radius: 10px;
|
||||
justify-content: center;
|
||||
width: 95%;
|
||||
height: 100%;
|
||||
align-content: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
@@ -65,6 +65,7 @@
|
||||
border: 3px solid #000;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
canvas {
|
||||
margin-bottom: 20px;
|
||||
}
|
25
web/newWebsite/text.html
Normal file
25
web/newWebsite/text.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<button id="settings" onclick="settings()">Settings</button>
|
||||
<div id="editNode">
|
||||
<p id="text">Status updating</p>
|
||||
<select id="mySelect"></select>
|
||||
<textarea id="inputName"></textarea>
|
||||
<textarea id="inputLocation"></textarea>
|
||||
<button id="button" onclick="changeText()">Change Information</button>
|
||||
</div>
|
||||
</body>
|
||||
<header>
|
||||
<script src="text.js"></script>
|
||||
</header>
|
||||
|
||||
</html>
|
96
web/newWebsite/text.js
Normal file
96
web/newWebsite/text.js
Normal file
@@ -0,0 +1,96 @@
|
||||
apiGetAllNode = "http://145.92.8.114/getNodeInfo?macAdress=*"
|
||||
|
||||
nodeDataArray = {};
|
||||
|
||||
var updateNode = document.getElementById('editNode');
|
||||
var locationInput = document.getElementById("inputLocation");
|
||||
var nameInput = document.getElementById("inputName");
|
||||
var select = document.getElementById('mySelect');
|
||||
|
||||
document.getElementById("inputName").placeholder = "Type new name here..";
|
||||
document.getElementById("inputLocation").placeholder = "Type new location here..";
|
||||
|
||||
updateNode.style.display = "none";
|
||||
|
||||
function settings() {
|
||||
if (updateNode.style.display === "none") {
|
||||
updateNode.style.display = "block";
|
||||
locationInput.value = "";
|
||||
nameInput.value = "";
|
||||
getInfoNode();
|
||||
|
||||
} else {
|
||||
updateNode.style.display = "none";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getInfoNode() {
|
||||
fetch(apiGetAllNode)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
document.getElementById('text').innerHTML = "Error: Network response was not ok";
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
document.getElementById('text').innerHTML = "Fetching data";
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
document.getElementById('text').innerHTML = "Data fetched";
|
||||
handleData(data);
|
||||
})
|
||||
}
|
||||
|
||||
function handleData(JSONdata) {
|
||||
var i, L = select.options.length - 1;
|
||||
for (i = L; i >= 0; i--) {
|
||||
select.remove(i);
|
||||
}
|
||||
|
||||
for (var i = 0; i < JSONdata.length; i++) {
|
||||
var node = JSONdata[i].NodeID;
|
||||
var name = JSONdata[i].Name;
|
||||
var location = JSONdata[i].Location;
|
||||
nodeDataArray[node] = { name: name, location: location };
|
||||
// Create new option element
|
||||
var option = document.createElement('option');
|
||||
|
||||
// Set the value of the option
|
||||
option.value = node;
|
||||
|
||||
// Set the text of the option
|
||||
option.text = name;
|
||||
|
||||
// Add the option to the select
|
||||
select.add(option);
|
||||
}
|
||||
}
|
||||
|
||||
function changeText() {
|
||||
var nodeName = encodeURIComponent(document.getElementById('inputName').value);
|
||||
var nodeLocation = encodeURIComponent(document.getElementById('inputLocation').value);
|
||||
|
||||
updateNodeInfo(select.value, nodeName, nodeLocation);
|
||||
|
||||
var text = document.getElementById('text');
|
||||
|
||||
text.innerHTML = "Changes made"
|
||||
getInfoNode();
|
||||
}
|
||||
|
||||
function updateNodeInfo(node, newNodeName, newNodeLocation) {
|
||||
apiUrl = "http://145.92.8.114/updateData?node=" + node + "&name=" + newNodeName + "&location=" + newNodeLocation;
|
||||
fetch(apiUrl)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@@ -72,8 +72,6 @@ p1 {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.nodeData {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
|
Reference in New Issue
Block a user