pain.exe
This commit is contained in:
parent
db6b2a2a6e
commit
58a45463d4
38 changed files with 512 additions and 173 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,87 @@
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ThingSpeakURLGenerator {
|
||||||
|
|
||||||
|
// Constants for Room channel numbers
|
||||||
|
private static final int ROOM_39_NUMBER = 1521262;
|
||||||
|
private static final int ROOM_38_NUMBER = 1364580;
|
||||||
|
private static final int ROOM_37_NUMBER = 1521263;
|
||||||
|
|
||||||
|
// Example Dates for fetching data, dynamically generated
|
||||||
|
private static final LocalDateTime START_DATE = LocalDateTime.of(2024, 11, 1, 0, 0);
|
||||||
|
private static final LocalDateTime END_DATE = LocalDateTime.of(2024, 11, 1, 23, 59);
|
||||||
|
|
||||||
|
// Room URLs
|
||||||
|
private static final Map<Integer, String> ROOM_URLS = new HashMap<>();
|
||||||
|
static {
|
||||||
|
ROOM_URLS.put(39, createUrl(ROOM_39_NUMBER));
|
||||||
|
ROOM_URLS.put(38, createUrl(ROOM_38_NUMBER));
|
||||||
|
ROOM_URLS.put(37, createUrl(ROOM_37_NUMBER));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to create the ThingSpeak API URL dynamically
|
||||||
|
public static String createUrl(int channelNumber) {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
String startDate = encodeUrlParam(START_DATE.format(formatter));
|
||||||
|
String endDate = encodeUrlParam(END_DATE.format(formatter));
|
||||||
|
|
||||||
|
return String.format("https://api.thingspeak.com/channels/%d/feeds.csv?start=%s&end=%s",
|
||||||
|
channelNumber, startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to URL encode the date strings
|
||||||
|
private static String encodeUrlParam(String value) {
|
||||||
|
try {
|
||||||
|
return URLEncoder.encode(value, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
System.err.println("Error encoding URL parameter: " + e.getMessage());
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to calculate the average of a specific number of data points (e.g.,
|
||||||
|
// CO2 levels)
|
||||||
|
public static double calculateAverage(List<Co2Data> dataList) {
|
||||||
|
if (dataList == null || dataList.isEmpty()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double sum = 0.0;
|
||||||
|
for (Co2Data data : dataList) {
|
||||||
|
sum += data.getCo2Level(); // Assuming Co2Data has a method getCo2Level() that returns the CO2 level
|
||||||
|
}
|
||||||
|
return sum / dataList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
// Print out the generated URLs for each room
|
||||||
|
for (Map.Entry<Integer, String> entry : ROOM_URLS.entrySet()) {
|
||||||
|
System.out.println("Room " + entry.getKey() + " URL: " + entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Calculate and print the average CO2 level for each room (assuming
|
||||||
|
// Co2Data.getData() method exists)
|
||||||
|
List<Co2Data> room39Data = Co2Data.getData(ROOM_URLS.get(39));
|
||||||
|
List<Co2Data> room38Data = Co2Data.getData(ROOM_URLS.get(38));
|
||||||
|
List<Co2Data> room37Data = Co2Data.getData(ROOM_URLS.get(37));
|
||||||
|
|
||||||
|
double avgRoom39 = calculateAverage(room39Data);
|
||||||
|
double avgRoom38 = calculateAverage(room38Data);
|
||||||
|
double avgRoom37 = calculateAverage(room37Data);
|
||||||
|
|
||||||
|
System.out.println("Average CO2 Level in Room 39: " + avgRoom39);
|
||||||
|
System.out.println("Average CO2 Level in Room 38: " + avgRoom38);
|
||||||
|
System.out.println("Average CO2 Level in Room 37: " + avgRoom37);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
BIN
Code/Steiner/CO2-Daten-Projekt-V3/bin/BreakSchedule.class
Normal file
BIN
Code/Steiner/CO2-Daten-Projekt-V3/bin/BreakSchedule.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Code/Steiner/CO2-Daten-Projekt-V3/bin/TimeTable.class
Normal file
BIN
Code/Steiner/CO2-Daten-Projekt-V3/bin/TimeTable.class
Normal file
Binary file not shown.
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/App.class
Normal file
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/App.class
Normal file
Binary file not shown.
|
@ -1,78 +1,71 @@
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class App {
|
public class App {
|
||||||
// #region app constants
|
// #region app constants
|
||||||
|
private static final Scanner scanner = new Scanner(System.in);
|
||||||
private static final Teacher[] teachers = new Teacher[Teacher.nameMap.size()];
|
private static final Teacher[] teachers = new Teacher[Teacher.nameMap.size()];
|
||||||
|
|
||||||
private static final String[] START_SMALL_BREAK = {
|
|
||||||
"8:30", "10:25", "11:15", "12:05", "13:30", "14:20", "16:10", "17:00", "17:50"
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final String[] END_SMALL_BREAK = {
|
|
||||||
"8:35", "10:30", "11:20", "12:10", "13:35", "14:25", "16:15", "17:05", "17:55"
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final String[] START_LONG_BREAK = {
|
|
||||||
"9:20", "15:10"
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final String[] END_LONG_BREAK = {
|
|
||||||
"9:40", "15:25"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Room channel numbers
|
// Room channel numbers
|
||||||
private static final int ROOM_39_NUMBER = 1521262;
|
private static final int ROOM_39_NUMBER = 1521262;
|
||||||
private static final int ROOM_38_NUMBER = 1364580;
|
private static final int ROOM_38_NUMBER = 1364580;
|
||||||
private static final int ROOM_37_NUMBER = 1521263;
|
private static final int ROOM_37_NUMBER = 1521263;
|
||||||
|
|
||||||
// Date and time constants
|
// Date and time constants for Start and End Dates
|
||||||
private static final int START_YEAR = 2024;
|
private static final String START_DATE = "2024-11-04";
|
||||||
private static final int START_MONTH = 11; // November
|
private static final String END_DATE = "2024-11-08";
|
||||||
private static final int START_DAY = 4;
|
|
||||||
private static final int START_HOUR = 0;
|
|
||||||
private static final int START_MINUTE = 0;
|
|
||||||
|
|
||||||
private static final int END_YEAR = 2024;
|
|
||||||
private static final int END_MONTH = 11; // November
|
|
||||||
private static final int END_DAY = 8;
|
|
||||||
private static final int END_HOUR = 23;
|
|
||||||
private static final int END_MINUTE = 59;
|
|
||||||
|
|
||||||
// Formatting the start and end date with time
|
|
||||||
private static final String START_DATE = String.format("%d-%02d-%02d%%20%02d:%02d:00",
|
|
||||||
START_YEAR, START_MONTH, START_DAY, START_HOUR, START_MINUTE);
|
|
||||||
private static final String END_DATE = String.format("%d-%02d-%02d%%20%02d:%02d:00",
|
|
||||||
END_YEAR, END_MONTH, END_DAY, END_HOUR, END_MINUTE);
|
|
||||||
|
|
||||||
// #region Initialization
|
// #region Initialization
|
||||||
private static String createUrl(int ROOM_X_NUMBER) {
|
|
||||||
return String.format("https://api.thingspeak.com/channels/%d/feeds.csv?start=%s&end=%s",
|
public static String generateLink(int channelNumber, String date, String startTime, String endTime) {
|
||||||
ROOM_X_NUMBER, START_DATE, END_DATE);
|
String baseUrl = "https://api.thingspeak.com/channels/";
|
||||||
|
String formattedStartDateTime = date + " " + startTime;
|
||||||
|
String formattedEndDateTime = date + " " + endTime;
|
||||||
|
|
||||||
|
return baseUrl + channelNumber + "/feeds.csv?start=" + formattedStartDateTime.replace(" ", "%20")
|
||||||
|
+ "&end=" + formattedEndDateTime.replace(" ", "%20"); // %20 --> " "
|
||||||
|
|
||||||
}
|
}
|
||||||
// #endregion
|
|
||||||
|
|
||||||
private static void getDataForBreak() {
|
private static double getDataAverageForMinute(int minute, int hour, int number, String date) {
|
||||||
// Co2Data[Wochentag][Room 37, 38 or 39]
|
// get the url
|
||||||
Co2Data[][][] data = new Co2Data[5][3][11];
|
String startTime = "";
|
||||||
|
if (hour < 10) {
|
||||||
|
startTime += "0";
|
||||||
|
}
|
||||||
|
startTime += String.valueOf(hour);
|
||||||
|
startTime += ":";
|
||||||
|
if (minute < 10) {
|
||||||
|
startTime += "0";
|
||||||
|
}
|
||||||
|
startTime += String.valueOf(minute);
|
||||||
|
String calcStarttime = startTime + ":00";
|
||||||
|
String calcEndTime = startTime + "59";
|
||||||
|
|
||||||
// Placeholder logic: Retrieve data for CO2 during breaks
|
String url = generateLink(number, date, calcStarttime, calcEndTime);
|
||||||
// This should be replaced with the actual logic to get the data from ThingSpeak
|
|
||||||
for (int i = 0; i < 5; i++) {
|
double averageCO2 = 0.0;
|
||||||
for (int j = 0; j < 3; j++) {
|
List<Co2Data> co2DataList = Co2Data.getData(url);
|
||||||
for (int k = 0; k < 11; k++) {
|
for (Co2Data co2Data : co2DataList) {
|
||||||
data[i][j][k] = new Co2Data(null, k); // Placeholder CO2 data
|
averageCO2 += co2Data.getCo2Level();
|
||||||
|
}
|
||||||
|
|
||||||
|
return averageCO2 / co2DataList.size(); // Example CO2 average level
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void calculateBreakPoints(double[] minuteData, Break calcBreak) {
|
||||||
|
int duration = calcBreak.getEnd().getMinute() - calcBreak.getStart().getMinute();
|
||||||
|
int breakPoints = minuteData.length;
|
||||||
|
if (duration == minuteData.length) {
|
||||||
|
for (int i = 0; i < duration; i++) {
|
||||||
|
if (minuteData[i] < minuteData[i + 1]) {
|
||||||
|
breakPoints--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double getDataAverageForMinute(String time) {
|
} else
|
||||||
// Placeholder logic to calculate average CO2 levels
|
System.out.println("Unexpected error");
|
||||||
return 400.0; // Example CO2 average level
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initializeTeachers() {
|
private static void initializeTeachers() {
|
||||||
|
@ -85,7 +78,6 @@ public class App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Scanner scanner = new Scanner(System.in);
|
|
||||||
|
|
||||||
// #region User Interaction
|
// #region User Interaction
|
||||||
private static int getUserInput(String textOutput) {
|
private static int getUserInput(String textOutput) {
|
||||||
|
@ -104,7 +96,6 @@ public class App {
|
||||||
System.out.println("1. Up to 5 points for keeping the window open during a small pause.");
|
System.out.println("1. Up to 5 points for keeping the window open during a small pause.");
|
||||||
System.out.println("2. Up to 10 points for long pauses, depending on window usage.");
|
System.out.println("2. Up to 10 points for long pauses, depending on window usage.");
|
||||||
System.out.println("3. 5 bonus points for teacher switches in classrooms.");
|
System.out.println("3. 5 bonus points for teacher switches in classrooms.");
|
||||||
System.out.println("4. Deduct points if CO2 levels are too high.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region shutdown
|
// #region shutdown
|
||||||
|
@ -168,15 +159,22 @@ public class App {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println("Calculations in process please do not shut off...");
|
System.out.println("Calculations in process please do not shut off...");
|
||||||
initializeTeachers();
|
initializeTeachers();
|
||||||
|
|
||||||
sortTeachers();
|
sortTeachers();
|
||||||
printTeachers();
|
printTeachers();
|
||||||
|
// Loop threw each day with a specific classroom and after the weekdays are over
|
||||||
|
// go to the next of the 3 classroms
|
||||||
|
// go threw every break calculate the point and give them to the teacher
|
||||||
|
// directly
|
||||||
|
// remember the point class
|
||||||
|
// breakShedule needed
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int userInput = getUserInput(
|
int userInput = getUserInput(
|
||||||
"Do you want to see how the points were calculated? (Yes 1, No 0; anything is an error)");
|
"Do you want to see how the points were calculated? (Yes 1, No 0; anything is an error)");
|
||||||
|
|
||||||
if (userInput == 1) {
|
if (userInput == 1) {
|
||||||
printExplanation();
|
printExplanation();
|
||||||
|
// add a more detailed listing of the teacher since points can be broken down
|
||||||
|
// further
|
||||||
printShutDown();
|
printShutDown();
|
||||||
break;
|
break;
|
||||||
} else if (userInput == 0) {
|
} else if (userInput == 0) {
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
public class Break {
|
public class Break {
|
||||||
private Time start;
|
private Time start;
|
||||||
private Time end;
|
private Time end;
|
||||||
private Teacher teacher;
|
|
||||||
private int room;
|
|
||||||
|
|
||||||
public Break(Time start, Time end, Teacher teacher, int room) {
|
public Break(Time start, Time end) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.teacher = teacher;
|
|
||||||
this.room = room;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Time getStart() {
|
public Time getStart() {
|
||||||
|
@ -27,19 +23,34 @@ public class Break {
|
||||||
this.end = end;
|
this.end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Teacher getTeacher() {
|
/**
|
||||||
return teacher;
|
* Generates a Thingspeak API link based on channel number, date, and break
|
||||||
|
* period.
|
||||||
|
*
|
||||||
|
* @param channelNumber The Thingspeak channel number.
|
||||||
|
* @param date The date in format "YYYY-MM-DD".
|
||||||
|
* @param breakPeriod The Break object containing start and end times.
|
||||||
|
* @return A formatted URL for fetching data from the Thingspeak API.
|
||||||
|
*/
|
||||||
|
public static String generateLink(int channelNumber, String date, Break breakPeriod) {
|
||||||
|
String baseUrl = "https://api.thingspeak.com/channels/";
|
||||||
|
String formattedStartDateTime = date + " " + breakPeriod.getStart().toString();
|
||||||
|
String formattedEndDateTime = date + " " + breakPeriod.getEnd().toString();
|
||||||
|
|
||||||
|
return baseUrl + channelNumber + "/feeds.csv?start=" + formattedStartDateTime.replace(" ", "%20")
|
||||||
|
+ "&end=" + formattedEndDateTime.replace(" ", "%20");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTeacher(Teacher teacher) {
|
public static void main(String[] args) {
|
||||||
this.teacher = teacher;
|
// Example usage
|
||||||
}
|
Time start = Time.valueOf("00:00:00");
|
||||||
|
Time end = Time.valueOf("23:59:59");
|
||||||
|
Break breakPeriod = new Break(start, end);
|
||||||
|
|
||||||
public int getRoom() {
|
String date = "2024-11-17";
|
||||||
return room;
|
int channelNumber = 1364580;
|
||||||
}
|
|
||||||
|
|
||||||
public void setRoom(int room) {
|
String link = Break.generateLink(channelNumber, date, breakPeriod);
|
||||||
this.room = room;
|
System.out.println(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
Code/Steiner/CO2-Daten-Projekt-V3/src/BreakSchedule.java
Normal file
47
Code/Steiner/CO2-Daten-Projekt-V3/src/BreakSchedule.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
public class BreakSchedule {
|
||||||
|
private static final String[] START_SMALL_BREAK = {
|
||||||
|
"8:30", "10:25", "11:15", "12:05", "13:30", "14:20", "16:10", "17:00", "17:50"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] END_SMALL_BREAK = {
|
||||||
|
"8:35", "10:30", "11:20", "12:10", "13:35", "14:25", "16:15", "17:05", "17:55"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] START_LONG_BREAK = {
|
||||||
|
"9:20", "15:10"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] END_LONG_BREAK = {
|
||||||
|
"9:40", "15:25"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Break[] SMALL_BREAKS;
|
||||||
|
private static final Break[] LONG_BREAKS;
|
||||||
|
|
||||||
|
static {
|
||||||
|
SMALL_BREAKS = createBreaks(START_SMALL_BREAK, END_SMALL_BREAK);
|
||||||
|
LONG_BREAKS = createBreaks(START_LONG_BREAK, END_LONG_BREAK);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Break[] createBreaks(String[] startTimes, String[] endTimes) {
|
||||||
|
Break[] breaks = new Break[startTimes.length];
|
||||||
|
for (int i = 0; i < startTimes.length; i++) {
|
||||||
|
// Append ":00" to include seconds
|
||||||
|
String startTimeWithSeconds = startTimes[i];
|
||||||
|
String endTimeWithSeconds = endTimes[i];
|
||||||
|
|
||||||
|
Time start = Time.valueOf(startTimeWithSeconds);
|
||||||
|
Time end = Time.valueOf(endTimeWithSeconds);
|
||||||
|
breaks[i] = new Break(start, end);
|
||||||
|
}
|
||||||
|
return breaks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Break[] getSmallBreaks() {
|
||||||
|
return SMALL_BREAKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Break[] getLongBreaks() {
|
||||||
|
return LONG_BREAKS;
|
||||||
|
}
|
||||||
|
}
|
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/Co2Data.class
Normal file
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/Co2Data.class
Normal file
Binary file not shown.
|
@ -2,21 +2,22 @@ import java.io.BufferedReader;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Co2Data {
|
public class Co2Data {
|
||||||
// #region Fields
|
|
||||||
private Date date;
|
private Date date;
|
||||||
private int co2Level;
|
private int co2Level;
|
||||||
|
|
||||||
// #region Constructor
|
// Constructor
|
||||||
public Co2Data(Date date, int co2Level) {
|
public Co2Data(Date date, int co2Level) {
|
||||||
this.date = date;
|
this.date = date;
|
||||||
this.co2Level = co2Level;
|
this.co2Level = co2Level;
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region Getters and Setters
|
// Getters and Setters
|
||||||
public Date getDate() {
|
public Date getDate() {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
@ -33,17 +34,15 @@ public class Co2Data {
|
||||||
this.co2Level = co2Level;
|
this.co2Level = co2Level;
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region Data Fetching
|
// Fetch and parse data from a CSV URL
|
||||||
// Method to fetch and parse CO2 data from a URL
|
public static List<Co2Data> getData(String csvURL) {
|
||||||
public static List<Co2Data> getData(String csvURL, int classRoomNumber) {
|
|
||||||
List<Co2Data> dataList = new ArrayList<>();
|
List<Co2Data> dataList = new ArrayList<>();
|
||||||
Date referenceDate = new Date(8, 11, 2024, 0, 0); // Reference date: November 8, 2024
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
URL url = new URL(csvURL);
|
URL url = new URL(csvURL);
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||||
conn.setRequestMethod("GET");
|
conn.setRequestMethod("GET");
|
||||||
conn.setRequestProperty("Accept", "application/csv");
|
conn.setRequestProperty("Accept", "text/csv");
|
||||||
|
|
||||||
if (conn.getResponseCode() != 200) {
|
if (conn.getResponseCode() != 200) {
|
||||||
throw new RuntimeException("Failed : HTTP Error code : " + conn.getResponseCode());
|
throw new RuntimeException("Failed : HTTP Error code : " + conn.getResponseCode());
|
||||||
|
@ -54,66 +53,36 @@ public class Co2Data {
|
||||||
|
|
||||||
String output;
|
String output;
|
||||||
while ((output = br.readLine()) != null) {
|
while ((output = br.readLine()) != null) {
|
||||||
Co2Data data = parseData(output, classRoomNumber);
|
Co2Data data = parseData(output);
|
||||||
if (data != null && isNewerThanReferenceDate(data.getDate(), referenceDate)) {
|
if (data != null) {
|
||||||
dataList.add(data);
|
dataList.add(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println("Exception in NetClientGet: " + e);
|
System.out.println("Error in data fetching: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataList;
|
return dataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region Date Comparison
|
// Helper method to parse CSV data and create a Co2Data object
|
||||||
// Method to compare if the data date is newer than the reference date
|
private static Co2Data parseData(String line) {
|
||||||
private static boolean isNewerThanReferenceDate(Date dataDate, Date referenceDate) {
|
|
||||||
if (dataDate.getYear() > referenceDate.getYear()) {
|
|
||||||
return true;
|
|
||||||
} else if (dataDate.getYear() == referenceDate.getYear()) {
|
|
||||||
if (dataDate.getMonth() > referenceDate.getMonth()) {
|
|
||||||
return true;
|
|
||||||
} else if (dataDate.getMonth() == referenceDate.getMonth()) {
|
|
||||||
return dataDate.getDay() > referenceDate.getDay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// #region Data Parsing
|
|
||||||
// Method to parse CO2 data from a CSV line
|
|
||||||
private static Co2Data parseData(String csvLine, int classRoomNumber) {
|
|
||||||
String[] fields = csvLine.split(",");
|
|
||||||
if (fields.length < 5)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String[] dateTime = fields[0].split(" ");
|
String[] parts = line.split(",");
|
||||||
String[] dateParts = dateTime[0].split("-");
|
String dateStr = parts[0].trim(); // assuming date is in the first column
|
||||||
String[] timeParts = dateTime[1].split(":");
|
int co2Level = Integer.parseInt(parts[1].trim()); // assuming CO2 level is in the second column
|
||||||
|
|
||||||
int year = Integer.parseInt(dateParts[0]);
|
// Convert the date string into a Date object (assumes CSV date format is
|
||||||
int month = Integer.parseInt(dateParts[1]);
|
// yyyy-MM-dd HH:mm:ss)
|
||||||
int day = Integer.parseInt(dateParts[2]);
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
int hour = Integer.parseInt(timeParts[0]);
|
Date date = sdf.parse(dateStr);
|
||||||
int minute = Integer.parseInt(timeParts[1]);
|
|
||||||
Date date = new Date(day, month, year, hour, minute);
|
|
||||||
|
|
||||||
int co2Level = Integer.parseInt(fields[2]);
|
|
||||||
|
|
||||||
return new Co2Data(date, co2Level);
|
return new Co2Data(date, co2Level);
|
||||||
} catch (NumberFormatException e) {
|
} catch (Exception e) {
|
||||||
|
System.out.println("Error parsing data line: " + e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region ToString Method
|
|
||||||
// Method to return a string representation of the CO2 data
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Date: " + date + ", CO2 Level: " + co2Level;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/Date.class
Normal file
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/Date.class
Normal file
Binary file not shown.
|
@ -1,34 +1,62 @@
|
||||||
public class Date {
|
public class Date {
|
||||||
// #region Fields
|
|
||||||
private int day;
|
|
||||||
private int month;
|
|
||||||
private int year;
|
private int year;
|
||||||
|
private int month;
|
||||||
|
private int day;
|
||||||
private int hour;
|
private int hour;
|
||||||
private int minute;
|
private int minute;
|
||||||
|
|
||||||
// #region Constructor
|
// Constructor to parse date string
|
||||||
public Date(int day, int month, int year, int hour, int minute) {
|
public Date(String dateStr) {
|
||||||
this.day = day;
|
String[] dateTime = dateStr.split(" ");
|
||||||
this.month = month;
|
String[] dateParts = dateTime[0].split("-");
|
||||||
this.year = year;
|
String[] timeParts = dateTime[1].split(":");
|
||||||
this.hour = hour;
|
|
||||||
this.minute = minute;
|
|
||||||
|
|
||||||
// Automatically adjust for Switzerland time after initialization
|
this.year = Integer.parseInt(dateParts[0]);
|
||||||
|
this.month = Integer.parseInt(dateParts[1]);
|
||||||
|
this.day = Integer.parseInt(dateParts[2]);
|
||||||
|
this.hour = Integer.parseInt(timeParts[0]);
|
||||||
|
this.minute = Integer.parseInt(timeParts[1]);
|
||||||
|
|
||||||
|
// Adjust for Switzerland time
|
||||||
adjustForSwitzerlandTime();
|
adjustForSwitzerlandTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region Getters
|
// Constructor to create date with fixed numeric values
|
||||||
public int getDay() {
|
public Date(int year, int month, int day, int hour, int minute) {
|
||||||
return day;
|
this.year = year;
|
||||||
|
this.month = month;
|
||||||
|
this.day = day;
|
||||||
|
this.hour = hour;
|
||||||
|
this.minute = minute;
|
||||||
|
|
||||||
|
// Adjust for Switzerland time
|
||||||
|
adjustForSwitzerlandTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust time for Switzerland timezone (UTC+1 or UTC+2 during daylight saving
|
||||||
|
// time)
|
||||||
|
private void adjustForSwitzerlandTime() {
|
||||||
|
// Switzerland time adjustment logic (simplified version)
|
||||||
|
// For simplicity, this doesn't handle daylight saving time changes but adjusts
|
||||||
|
// based on UTC+1
|
||||||
|
this.hour += 1; // Assuming fixed UTC+1 offset for simplicity
|
||||||
|
if (this.hour >= 24) {
|
||||||
|
this.hour -= 24;
|
||||||
|
this.day += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter methods
|
||||||
|
public int getYear() {
|
||||||
|
return year;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMonth() {
|
public int getMonth() {
|
||||||
return month;
|
return month;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getYear() {
|
public int getDay() {
|
||||||
return year;
|
return day;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHour() {
|
public int getHour() {
|
||||||
|
@ -39,38 +67,7 @@ public class Date {
|
||||||
return minute;
|
return minute;
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region Adjust UTC to Switzerland Time
|
// Method to print date in a readable format
|
||||||
private void adjustForSwitzerlandTime() {
|
|
||||||
hour += 1;
|
|
||||||
|
|
||||||
// Adjust if the hour exceeds 24 or falls below 0
|
|
||||||
if (hour >= 24) {
|
|
||||||
hour -= 24;
|
|
||||||
day++;
|
|
||||||
if (day > 31) { // Simple month/day rollover, could be more sophisticated
|
|
||||||
day = 1;
|
|
||||||
month++;
|
|
||||||
if (month > 12) {
|
|
||||||
month = 1;
|
|
||||||
year++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (hour < 0) {
|
|
||||||
hour += 24;
|
|
||||||
day--;
|
|
||||||
if (day < 1) {
|
|
||||||
month--;
|
|
||||||
if (month < 1) {
|
|
||||||
month = 12;
|
|
||||||
year--;
|
|
||||||
}
|
|
||||||
day = 31; // Simple month/day rollover, could be more sophisticated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// #region toString Override
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("%04d-%02d-%02d %02d:%02d", year, month, day, hour, minute);
|
return String.format("%04d-%02d-%02d %02d:%02d", year, month, day, hour, minute);
|
||||||
}
|
}
|
||||||
|
|
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/Points.class
Normal file
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/Points.class
Normal file
Binary file not shown.
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/Teacher.class
Normal file
BIN
Code/Steiner/CO2-Daten-Projekt-V3/src/Teacher.class
Normal file
Binary file not shown.
|
@ -26,4 +26,12 @@ public class Time {
|
||||||
public void setMinute(int minute) {
|
public void setMinute(int minute) {
|
||||||
this.minute = minute;
|
this.minute = minute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Time valueOf(String time) {
|
||||||
|
String[] parts = time.split(":");
|
||||||
|
int hour = Integer.parseInt(parts[0]);
|
||||||
|
int minute = Integer.parseInt(parts[1]);
|
||||||
|
Time returnValue = new Time(hour, minute);
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
79
Code/Steiner/CO2-Daten-Projekt-V3/src/TimeTable.java
Normal file
79
Code/Steiner/CO2-Daten-Projekt-V3/src/TimeTable.java
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
public class TimeTable {
|
||||||
|
private String[][] shortTeacher;
|
||||||
|
|
||||||
|
public TimeTable(int roomIndex) {
|
||||||
|
shortTeacher = new String[11][5]; // 11 lessons, 5 weekdays
|
||||||
|
initializeTimeTable(roomIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeTimeTable(int roomIndex) {
|
||||||
|
switch (roomIndex) {
|
||||||
|
case 0: // Room 37
|
||||||
|
fillRoom37();
|
||||||
|
break;
|
||||||
|
case 1: // Room 38
|
||||||
|
fillRoom38();
|
||||||
|
break;
|
||||||
|
case 2: // Room 39
|
||||||
|
fillRoom39();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid room index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillRoom37() {
|
||||||
|
shortTeacher = new String[][] {
|
||||||
|
{ "Hm", "Ts", "Lu", "Gi", "Kp" },
|
||||||
|
{ "Hm", "Ts", "Lu", "Gi", "KP" },
|
||||||
|
{ "Hi", "Ts", "Lu", "Ba", "Or" },
|
||||||
|
{ "Hm", "Ts", "Lu", "Ba", "Vt" },
|
||||||
|
{ "Hm", "Le", "Cg", "Ba", "Vt" },
|
||||||
|
{ "Lunch", "Lunch", "Lunch", "Lunch", "Lunch" },
|
||||||
|
{ "Bd", "Lunch", "Se", "Bd", "Lunch" },
|
||||||
|
{ "Gi", "Fh", "Se", "Du", "Du" },
|
||||||
|
{ "Gi", "Fh", "Se", "Lz", "Du" },
|
||||||
|
{ "Ts", "Fh", "Se", "Lz", "Du" },
|
||||||
|
{ "Ts", "Fh", "Se", "", "" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillRoom38() {
|
||||||
|
shortTeacher = new String[][] {
|
||||||
|
{ "Bz", "Kg", "Cg", "Do", "" },
|
||||||
|
{ "Bz", "Kg", "Cg", "Do", "Hu" },
|
||||||
|
{ "Bz", "Eh", "Cg", "Gr", "Ge" },
|
||||||
|
{ "Bz", "Re", "Cg", "Gr", "Eh" },
|
||||||
|
{ "Bz", "Re", "Es", "Or", "Eh" },
|
||||||
|
{ "Lunch", "Lunch", "Lunch", "Lunch", "Lunch" },
|
||||||
|
{ "Lunch", "Lunch", "Lunch", "Lunch", "Bu" },
|
||||||
|
{ "Hn", "Bt", "Cg", "Bu", "Eh" },
|
||||||
|
{ "Hn", "Kh", "Cg", "Bu", "Eh" },
|
||||||
|
{ "Bu", "Kh", "", "Zu", "" },
|
||||||
|
{ "Hn", "", "", "", "" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillRoom39() {
|
||||||
|
shortTeacher = new String[][] {
|
||||||
|
{ "Bd", "Do", "Cg", "Bd", "Gi" },
|
||||||
|
{ "Bd", "Do", "Cg", "Bd", "Gi" },
|
||||||
|
{ "Bd", "Zu", "Cg", "Bd", "Gr" },
|
||||||
|
{ "Bd", "Zu", "Cg", "Bd", "Gr" },
|
||||||
|
{ "Bd", "Zu", "Bu", "Or", "Gi" },
|
||||||
|
{ "Lunch", "Lunch", "Lunch", "Lunch", "Lunch" },
|
||||||
|
{ "Lunch", "Lunch", "Lunch", "Lunch", "Lunch" },
|
||||||
|
{ "Lu", "Se", "Gi", "Le", "Hi" },
|
||||||
|
{ "Lu", "Se", "Gi", "Le", "Hi" },
|
||||||
|
{ "Lu", "Se", "Gi", "Le", "Hi" },
|
||||||
|
{ "Lu", "Se", "Gi", "", "" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLesson(int lesson, int day) {
|
||||||
|
if (lesson < 0 || lesson >= 11 || day < 0 || day >= 5) {
|
||||||
|
throw new IllegalArgumentException("Invalid lesson or day");
|
||||||
|
}
|
||||||
|
return shortTeacher[lesson][day];
|
||||||
|
}
|
||||||
|
}
|
7
Code/Steiner/testAAAAA/.vscode/settings.json
vendored
Normal file
7
Code/Steiner/testAAAAA/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"java.project.sourcePaths": ["src"],
|
||||||
|
"java.project.outputPath": "bin",
|
||||||
|
"java.project.referencedLibraries": [
|
||||||
|
"lib/**/*.jar"
|
||||||
|
]
|
||||||
|
}
|
18
Code/Steiner/testAAAAA/README.md
Normal file
18
Code/Steiner/testAAAAA/README.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Welcome to the VS Code Java world. Here is a guideline to help you get started to write Java code in Visual Studio Code.
|
||||||
|
|
||||||
|
## Folder Structure
|
||||||
|
|
||||||
|
The workspace contains two folders by default, where:
|
||||||
|
|
||||||
|
- `src`: the folder to maintain sources
|
||||||
|
- `lib`: the folder to maintain dependencies
|
||||||
|
|
||||||
|
Meanwhile, the compiled output files will be generated in the `bin` folder by default.
|
||||||
|
|
||||||
|
> If you want to customize the folder structure, open `.vscode/settings.json` and update the related settings there.
|
||||||
|
|
||||||
|
## Dependency Management
|
||||||
|
|
||||||
|
The `JAVA PROJECTS` view allows you to manage your dependencies. More details can be found [here](https://github.com/microsoft/vscode-java-dependency#manage-dependencies).
|
BIN
Code/Steiner/testAAAAA/bin/ThingSpeakURLGenerator.class
Normal file
BIN
Code/Steiner/testAAAAA/bin/ThingSpeakURLGenerator.class
Normal file
Binary file not shown.
22
Code/Steiner/testAAAAA/src/App.java
Normal file
22
Code/Steiner/testAAAAA/src/App.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
// Example input for generating the URL
|
||||||
|
String channelId = "123456"; // Replace with your actual channel ID
|
||||||
|
String apiKey = "YOUR_READ_API_KEY"; // Replace with your actual API key
|
||||||
|
int results = 50; // Fetch 50 records
|
||||||
|
String start = "2024-11-01 00:00:00"; // Start date-time
|
||||||
|
String end = "2024-11-01 23:59:59"; // End date-time
|
||||||
|
String timezone = "Europe/Zurich"; // Timezone
|
||||||
|
|
||||||
|
// Generate the dynamic URL using the method
|
||||||
|
String url = createThingSpeakURL(channelId, apiKey, results, start, end, timezone);
|
||||||
|
|
||||||
|
// Output the generated URL
|
||||||
|
System.out.println("Generated ThingSpeak URL: ");
|
||||||
|
System.out.println(url);
|
||||||
|
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
Code/Steiner/testAAAAA/src/ThingSpeakURLGenerator.class
Normal file
BIN
Code/Steiner/testAAAAA/src/ThingSpeakURLGenerator.class
Normal file
Binary file not shown.
87
Code/Steiner/testAAAAA/src/ThingSpeakURLGenerator.java
Normal file
87
Code/Steiner/testAAAAA/src/ThingSpeakURLGenerator.java
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ThingSpeakURLGenerator {
|
||||||
|
|
||||||
|
// Constants for Room channel numbers
|
||||||
|
private static final int ROOM_39_NUMBER = 1521262;
|
||||||
|
private static final int ROOM_38_NUMBER = 1364580;
|
||||||
|
private static final int ROOM_37_NUMBER = 1521263;
|
||||||
|
|
||||||
|
// Example Dates for fetching data, dynamically generated
|
||||||
|
private static final LocalDateTime START_DATE = LocalDateTime.of(2024, 11, 1, 0, 0);
|
||||||
|
private static final LocalDateTime END_DATE = LocalDateTime.of(2024, 11, 1, 23, 59);
|
||||||
|
|
||||||
|
// Room URLs
|
||||||
|
private static final Map<Integer, String> ROOM_URLS = new HashMap<>();
|
||||||
|
static {
|
||||||
|
ROOM_URLS.put(39, createUrl(ROOM_39_NUMBER));
|
||||||
|
ROOM_URLS.put(38, createUrl(ROOM_38_NUMBER));
|
||||||
|
ROOM_URLS.put(37, createUrl(ROOM_37_NUMBER));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to create the ThingSpeak API URL dynamically
|
||||||
|
public static String createUrl(int channelNumber) {
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
String startDate = encodeUrlParam(START_DATE.format(formatter));
|
||||||
|
String endDate = encodeUrlParam(END_DATE.format(formatter));
|
||||||
|
|
||||||
|
return String.format("https://api.thingspeak.com/channels/%d/feeds.csv?start=%s&end=%s",
|
||||||
|
channelNumber, startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to URL encode the date strings
|
||||||
|
private static String encodeUrlParam(String value) {
|
||||||
|
try {
|
||||||
|
return URLEncoder.encode(value, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
System.err.println("Error encoding URL parameter: " + e.getMessage());
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to calculate the average of a specific number of data points (e.g.,
|
||||||
|
// CO2 levels)
|
||||||
|
public static double calculateAverage(List<Co2Data> dataList) {
|
||||||
|
if (dataList == null || dataList.isEmpty()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double sum = 0.0;
|
||||||
|
for (Co2Data data : dataList) {
|
||||||
|
sum += data.getCo2Level(); // Assuming Co2Data has a method getCo2Level() that returns the CO2 level
|
||||||
|
}
|
||||||
|
return sum / dataList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
// Print out the generated URLs for each room
|
||||||
|
for (Map.Entry<Integer, String> entry : ROOM_URLS.entrySet()) {
|
||||||
|
System.out.println("Room " + entry.getKey() + " URL: " + entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: Calculate and print the average CO2 level for each room (assuming
|
||||||
|
// Co2Data.getData() method exists)
|
||||||
|
List<Co2Data> room39Data = Co2Data.getData(ROOM_URLS.get(39));
|
||||||
|
List<Co2Data> room38Data = Co2Data.getData(ROOM_URLS.get(38));
|
||||||
|
List<Co2Data> room37Data = Co2Data.getData(ROOM_URLS.get(37));
|
||||||
|
|
||||||
|
double avgRoom39 = calculateAverage(room39Data);
|
||||||
|
double avgRoom38 = calculateAverage(room38Data);
|
||||||
|
double avgRoom37 = calculateAverage(room37Data);
|
||||||
|
|
||||||
|
System.out.println("Average CO2 Level in Room 39: " + avgRoom39);
|
||||||
|
System.out.println("Average CO2 Level in Room 38: " + avgRoom38);
|
||||||
|
System.out.println("Average CO2 Level in Room 37: " + avgRoom37);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
Code/ost/JAVA-FX-PROJECT/Auftrag JavaFX.docx
Normal file
BIN
Code/ost/JAVA-FX-PROJECT/Auftrag JavaFX.docx
Normal file
Binary file not shown.
9
Code/ost/JAVA-FX-PROJECT/Notizen zum Auftrag.txt
Normal file
9
Code/ost/JAVA-FX-PROJECT/Notizen zum Auftrag.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Notizen zum Auftrag:
|
||||||
|
|
||||||
|
Das Word-Dokument ist in der Webview etwa 1 1/4 Seiten lang, während es in der Desktop-App genau eine Seite umfasst, wie von mir geplant.
|
||||||
|
|
||||||
|
Die GUI-Elemente wurden bereits erklärt.
|
||||||
|
|
||||||
|
Die GUI weist noch einige Imperfektionen auf, die während der Entwicklung in JavaFX besser behoben werden können als in Adobe XD.
|
||||||
|
|
||||||
|
Das Wireframe wurde in Adobe XD erstellt, da ich es bevorzuge, ohne aktives Internet an solchen Projekten zu arbeiten. Das Internet wurde nur für die Suche nach Büchern, deren Beschreibung und Bildern verwendet.
|
Loading…
Reference in a new issue