Commit ac23dc7f authored by Patrick Ade's avatar Patrick Ade
Browse files

Add data generation scripts and frontend charting components

- Implement MQTT publisher for CO2, temperature, and humidity data simulation
- Create Chart.vue component for displaying data using Chart.js
- Update NavBar.vue to include a link to the new charts page
- Add ChartView.vue to serve as the view for charting data
- Modify router to include a route for the charts page
- Update package.json and package-lock.json to include Chart.js and vue-chartjs dependencies
- Add .python-version and pyproject.toml for Python environment setup
parent 69440d7b
import paho.mqtt.client as mqtt
import time
import json
import random
from datetime import datetime
from pathlib import Path
# MQTT Publisher to simulate CO2, temperature, and humidity data for multiple MAC addresses
def on_connect(client, userdata, flags, rc, properties=None):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print(f"Failed to connect, return code {rc}")
def get_formatted_time():
now = datetime.now().astimezone()
formatted = now.strftime("%Y-%m-%d %H:%M:%S%z")
return formatted[:-2] + ":" + formatted[-2:]
def load_mac_addresses_from_json(filepath: Path):
try:
with filepath.open("r") as file:
data = json.load(file)
return list(data.keys())
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Error loading MAC address file '{filepath}': {e}")
return []
def publish_loop(
broker: str, port: int, topic: str, mac_file: Path, delay: float = 1.0
):
mac_addresses = load_mac_addresses_from_json(mac_file)
if not mac_addresses:
print("No MAC addresses loaded. Exiting.")
return
client = mqtt.Client()
client.on_connect = on_connect
try:
client.connect(broker, port, 60)
client.loop_start()
while True:
random.shuffle(mac_addresses)
for mac in mac_addresses:
dictionary = {
"metadata": {
"mac-address": mac,
"time": get_formatted_time(),
},
"co2": random.randint(400, 3000),
"temp": round(random.uniform(18.0, 35.0), 1),
"rh": round(random.uniform(20.0, 80.0), 1),
}
message = json.dumps(dictionary)
result = client.publish(topic, message)
status = result[0]
if status == 0:
print(f"Message sent: {message}")
else:
print("Failed to send message")
time.sleep(delay)
time.sleep(5)
except KeyboardInterrupt:
print("\nPublishing stopped by user.")
except Exception as e:
print(f"An error occurred: {e}")
finally:
client.loop_stop()
client.disconnect()
if __name__ == "__main__":
broker = "localhost"
port = 1883
topic = "co2/x"
delay = 2.0
# Change this path as needed:
macs_json_path = (
Path(__file__).parent / ".." / "stream_processing" / "mac_to_room.json"
)
publish_loop(broker, port, topic, macs_json_path, delay)
[project]
name = "data-generation"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"paho-mqtt>=2.1.0",
]
version = 1
revision = 1
requires-python = ">=3.13"
[[package]]
name = "data-generation"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "paho-mqtt" },
]
[package.metadata]
requires-dist = [{ name = "paho-mqtt", specifier = ">=2.1.0" }]
[[package]]
name = "paho-mqtt"
version = "2.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/39/15/0a6214e76d4d32e7f663b109cf71fb22561c2be0f701d67f93950cd40542/paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834", size = 148848 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c4/cb/00451c3cf31790287768bb12c6bec834f5d292eaf3022afc88e14b8afc94/paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee", size = 67219 },
]
...@@ -8,8 +8,10 @@ ...@@ -8,8 +8,10 @@
"name": "frontend", "name": "frontend",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"chart.js": "^4.4.9",
"pinia": "^3.0.1", "pinia": "^3.0.1",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-chartjs": "^5.3.2",
"vue-router": "^4.5.0" "vue-router": "^4.5.0"
}, },
"devDependencies": { "devDependencies": {
...@@ -1258,6 +1260,12 @@ ...@@ -1258,6 +1260,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
"license": "MIT"
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
...@@ -2499,6 +2507,18 @@ ...@@ -2499,6 +2507,18 @@
"url": "https://github.com/chalk/chalk?sponsor=1" "url": "https://github.com/chalk/chalk?sponsor=1"
} }
}, },
"node_modules/chart.js": {
"version": "4.4.9",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz",
"integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==",
"license": "MIT",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
...@@ -4951,6 +4971,16 @@ ...@@ -4951,6 +4971,16 @@
} }
} }
}, },
"node_modules/vue-chartjs": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz",
"integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==",
"license": "MIT",
"peerDependencies": {
"chart.js": "^4.1.1",
"vue": "^3.0.0-0 || ^2.7.0"
}
},
"node_modules/vue-eslint-parser": { "node_modules/vue-eslint-parser": {
"version": "10.1.3", "version": "10.1.3",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.1.3.tgz", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.1.3.tgz",
......
...@@ -13,8 +13,10 @@ ...@@ -13,8 +13,10 @@
"format": "prettier --write src/" "format": "prettier --write src/"
}, },
"dependencies": { "dependencies": {
"chart.js": "^4.4.9",
"pinia": "^3.0.1", "pinia": "^3.0.1",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-chartjs": "^5.3.2",
"vue-router": "^4.5.0" "vue-router": "^4.5.0"
}, },
"devDependencies": { "devDependencies": {
......
<template>
<Line :data="chartData" :options="chartOptions" />
</template>
<script setup lang="ts">
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
PointElement,
LinearScale,
CategoryScale,
} from 'chart.js'
import { Line } from 'vue-chartjs'
ChartJS.register(Title, Tooltip, Legend, LineElement, PointElement, LinearScale, CategoryScale)
const chartData = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [
{
label: 'Page Views',
data: [120, 190, 300, 500, 250, 400],
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
tension: 0.3,
fill: true,
},
],
}
const chartOptions = {
responsive: true,
plugins: {
legend: {
position: 'top' as const,
},
title: {
display: true,
text: 'Website Traffic Over Time',
},
},
scales: {
y: {
beginAtZero: true,
},
},
}
</script>
...@@ -3,63 +3,63 @@ import { RouterLink, RouterView } from 'vue-router' ...@@ -3,63 +3,63 @@ import { RouterLink, RouterView } from 'vue-router'
</script> </script>
<template> <template>
<header> <header>
<div class="container"> <div class="container">
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="100" height="100" /> <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="100" height="100" />
<nav> <nav>
<RouterLink to="/">Home</RouterLink> <RouterLink to="/">Home</RouterLink>
<RouterLink to="/login">Login</RouterLink> <RouterLink to="/login">Login</RouterLink>
<RouterLink to="/about">About</RouterLink> <RouterLink to="/about">About</RouterLink>
<RouterLink to="/buildingsView">Gebäude</RouterLink> <RouterLink to="/buildingsView">Gebäude</RouterLink>
<RouterLink to="/register">Register</RouterLink> <RouterLink to="/charts">Charts</RouterLink>
</nav> <RouterLink to="/register">Registrieren</RouterLink>
</div> </nav>
</header> </div>
</header>
<RouterView /> <RouterView />
</template> </template>
<style scoped> <style scoped>
header { header {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
background: white; background: white;
z-index: 1000; z-index: 1000;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
} }
.container { .container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.5rem 2rem; padding: 0.5rem 2rem;
} }
.logo { .logo {
height: 60px; height: 60px;
width: auto; width: auto;
} }
nav a { nav a {
margin-left: 1rem; margin-left: 1rem;
color: red; color: red;
text-decoration: none; text-decoration: none;
font-weight: 500; font-weight: 500;
} }
nav a.router-link-exact-active { nav a.router-link-exact-active {
font-weight: bold; font-weight: bold;
color: darkred; color: darkred;
} }
nav a:hover { nav a:hover {
color: darkred; color: darkred;
} }
main { main {
padding-top: 80px; /* Abstand für den festen Header */ padding-top: 80px; /* Abstand für den festen Header */
} }
</style>
</style>
\ No newline at end of file
...@@ -4,6 +4,7 @@ import LoginView from '../views/LoginView.vue' ...@@ -4,6 +4,7 @@ import LoginView from '../views/LoginView.vue'
import RegisterView from '../views/RegisterView.vue' import RegisterView from '../views/RegisterView.vue'
import RoomNavView from '../views/RoomNavView.vue' import RoomNavView from '../views/RoomNavView.vue'
import BuildingsView from '../views/BuildingsView.vue' import BuildingsView from '../views/BuildingsView.vue'
import ChartView from '../views/ChartView.vue'
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
...@@ -33,7 +34,11 @@ const router = createRouter({ ...@@ -33,7 +34,11 @@ const router = createRouter({
name: 'buildingsView', name: 'buildingsView',
component: BuildingsView, component: BuildingsView,
}, },
{
path: '/charts',
name: 'charts',
component: ChartView
},
{ {
path: '/about', path: '/about',
name: 'about', name: 'about',
......
<template>
<MyChart />
</template>
<script setup lang="ts">
import MyChart from '@/components/Chart.vue'
</script>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment