diff --git a/.gitignore b/.gitignore index f147edf..c75f867 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,10 @@ compile_commands.json # QtCreator local machine specific files for imported projects *creator.user* + +# Gradle and Java +.gradle +.settings + +# VSCode +.vscode \ No newline at end of file diff --git a/NodeRoid.pro b/NodeRoid.pro new file mode 100644 index 0000000..0b62aad --- /dev/null +++ b/NodeRoid.pro @@ -0,0 +1,39 @@ +QT += core gui +QT += network + + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++11 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + mainwindow.cpp \ + notificationclient.cpp \ + syncdata.cpp + +HEADERS += \ + mainwindow.h \ + notificationclient.h \ + syncdata.h + +FORMS += \ + mainwindow.ui + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +DISTFILES += \ + android/AndroidManifest.xml \ + android/src/cc/akashic/noderoid/NotificationClient.java + +contains(ANDROID_TARGET_ARCH,arm64-v8a) { + ANDROID_PACKAGE_SOURCE_DIR = \ + $$PWD/android +} diff --git a/android/.project b/android/.project new file mode 100644 index 0000000..91e13ef --- /dev/null +++ b/android/.project @@ -0,0 +1,28 @@ + + + android + Project android created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + + + 1634916704863 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml new file mode 100644 index 0000000..f4490c3 --- /dev/null +++ b/android/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..310b710 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,77 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.3' + } +} + +repositories { + google() + jcenter() +} + +apply plugin: 'com.android.application' + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) +} + +android { + /******************************************************* + * The following variables: + * - androidBuildToolsVersion, + * - androidCompileSdkVersion + * - qt5AndroidDir - holds the path to qt android files + * needed to build any Qt application + * on Android. + * + * are defined in gradle.properties file. This file is + * updated by QtCreator and androiddeployqt tools. + * Changing them manually might break the compilation! + *******************************************************/ + + compileSdkVersion androidCompileSdkVersion.toInteger() + + buildToolsVersion androidBuildToolsVersion + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] + aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] + res.srcDirs = [qt5AndroidDir + '/res', 'res'] + resources.srcDirs = ['resources'] + renderscript.srcDirs = ['src'] + assets.srcDirs = ['assets'] + jniLibs.srcDirs = ['libs'] + } + } + + tasks.withType(JavaCompile) { + options.incremental = true + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + abortOnError false + } + + // Do not compress Qt binary resources file + aaptOptions { + noCompress 'rcc' + } + + defaultConfig { + resConfig "en" + minSdkVersion qtMinSdkVersion + targetSdkVersion qtTargetSdkVersion + } +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..fded106 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,11 @@ +# Project-wide Gradle settings. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m + +# Gradle caching allows reusing the build artifacts from a previous +# build with the same inputs. However, over time, the cache size will +# grow. Uncomment the following line to enable it. +#org.gradle.caching=true diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..442d913 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/res/drawable-hdpi/icon.png b/android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..22385a6 Binary files /dev/null and b/android/res/drawable-hdpi/icon.png differ diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..629813c Binary files /dev/null and b/android/res/drawable-ldpi/icon.png differ diff --git a/android/res/drawable-mdpi/icon.png b/android/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..05d00ea Binary files /dev/null and b/android/res/drawable-mdpi/icon.png differ diff --git a/android/res/drawable-xhdpi/icon.png b/android/res/drawable-xhdpi/icon.png new file mode 100644 index 0000000..72b3cad Binary files /dev/null and b/android/res/drawable-xhdpi/icon.png differ diff --git a/android/res/drawable-xxhdpi/icon.png b/android/res/drawable-xxhdpi/icon.png new file mode 100644 index 0000000..5f7de0c Binary files /dev/null and b/android/res/drawable-xxhdpi/icon.png differ diff --git a/android/res/drawable-xxxhdpi/icon.png b/android/res/drawable-xxxhdpi/icon.png new file mode 100644 index 0000000..1b608ed Binary files /dev/null and b/android/res/drawable-xxxhdpi/icon.png differ diff --git a/android/res/values/libs.xml b/android/res/values/libs.xml new file mode 100644 index 0000000..beb15ca --- /dev/null +++ b/android/res/values/libs.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/cc/akashic/noderoid/NotificationClient.java b/android/src/cc/akashic/noderoid/NotificationClient.java new file mode 100644 index 0000000..9b0305b --- /dev/null +++ b/android/src/cc/akashic/noderoid/NotificationClient.java @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package cc.akashic.noderoid; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.BitmapFactory; +import android.app.NotificationChannel; + +public class NotificationClient +{ + public static void notify(Context context, String message) { + try { + NotificationManager m_notificationManager = (NotificationManager) + context.getSystemService(Context.NOTIFICATION_SERVICE); + + Notification.Builder m_builder; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + int importance = NotificationManager.IMPORTANCE_DEFAULT; + NotificationChannel notificationChannel; + notificationChannel = new NotificationChannel("NodeRoid", "NodeRoid Notifier", importance); + m_notificationManager.createNotificationChannel(notificationChannel); + m_builder = new Notification.Builder(context, notificationChannel.getId()); + } else { + m_builder = new Notification.Builder(context); + } + + Bitmap icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon); + m_builder.setSmallIcon(R.drawable.icon) + .setLargeIcon(icon) + .setContentTitle("NodeRoid") + .setContentText(message) + .setDefaults(Notification.DEFAULT_SOUND) + .setColor(Color.GREEN) + .setAutoCancel(true); + + m_notificationManager.notify(0, m_builder.build()); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..c0d6d89 --- /dev/null +++ b/main.cpp @@ -0,0 +1,18 @@ +/** + * @file main.cpp + * @author Myth + * @version 1.0 + * @date 2021.10.23 + */ + +#include "mainwindow.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..616ba35 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,119 @@ +/** + * @file mainwindows.cpp + * @author Myth + * @version 1.0 + * @date 2021.10.23 + */ + +#include "ui_mainwindow.h" + +#include "notificationclient.h" +#include "syncdata.h" + +#include "mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + syncdata = new SyncData(this); + connect(syncdata, SIGNAL(timeUpdated(QString)), + this, SLOT(dispUpdateTime(QString))); + connect(syncdata, SIGNAL(nodeDataUpdated(int, float, float, float)), + this, SLOT(dispNodeData(int, float, float, float))); + connect(syncdata, SIGNAL(nodeDataUpdated(int, float, float, float)), + this, SLOT(sendNodeWarn(int, float, float, float))); + + temp_max = 27.0; + connect(this->ui->doubleSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(updateTempMax(double))); +} + +/** + * @brief 槽函数:更新高温报警温度 + */ +void MainWindow::updateTempMax(double temp_value) +{ + temp_max = temp_value; +} + +/** + * @brief 槽函数:显示节点数据 + */ +void MainWindow::dispNodeData(int seq, float humi, float temp, float light) +{ + QString line_str; + + line_str = "节点" + + QString::number(seq) + + " 湿度:" + + QString::number((double)humi) + + "% 温度:" + + QString::number((double)temp) + + "°C\n 光照度:" + + QString::number((double)light) + + "lx"; + + switch (seq) + { + case 2: + ui->label_node2->setText(line_str); + break; + case 3: + ui->label_node3->setText(line_str); + break; + case 4: + ui->label_node4->setText(line_str); + break; + case 5: + ui->label_node5->setText(line_str); + } +} + +/** + * @brief 槽函数:显示更新时间 + */ +void MainWindow::dispUpdateTime(QString time) +{ + QString line_str; + + line_str = "更新时间:" + time; + ui->label_time->setText(line_str); +} + +/** + * @brief 槽函数:发送系统通知 + */ +void MainWindow::sendNodeWarn(int seq, float humi, float temp, float light) +{ + static bool is_first_run = true; + static QString last_warn_str; + if (is_first_run) + { + NotificationClient().setNotification("成功获取数据!"); + is_first_run = false; + } + + if (temp > temp_max) + { + QString warn_str = "节点" + + QString::number(seq) + + "温度过高:" + + QString::number((double)temp) + + "°C"; + + if (QString::compare(warn_str, last_warn_str) != 0) + { + NotificationClient().setNotification(warn_str); + last_warn_str.clear(); + last_warn_str.append(warn_str); + } + } +} + +MainWindow::~MainWindow() +{ + delete ui; + delete syncdata; +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..7e18f6d --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,41 @@ +/** + * @file mainwindows.h + * @author Myth + * @version 1.0 + * @date 2021.10.23 + */ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +#include "syncdata.h" + +QT_BEGIN_NAMESPACE +namespace Ui +{ + class MainWindow; +} +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui; + SyncData *syncdata; + double temp_max; + +private slots: + void updateTempMax(double temp_value); + void dispNodeData(int seq, float humi, float temp, float light); + void dispUpdateTime(QString time); + void sendNodeWarn(int seq, float humi, float temp, float light); +}; +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..7c804ff --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,275 @@ + + + MainWindow + + + + 0 + 0 + 1080 + 1920 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + Arial + 9 + + + + MainWindow + + + true + + + + + + 10 + 0 + 351 + 41 + + + + + Arial + 30 + 75 + true + + + + NodeRoid v1.0 + + + + + + 0 + 40 + 1081 + 16 + + + + false + + + false + + + Qt::Horizontal + + + + + + 10 + 60 + 1071 + 101 + + + + + Arial + 25 + 75 + true + + + + 等待中 + + + + + + 10 + 160 + 1071 + 101 + + + + + Arial + 25 + 75 + true + + + + 等待中 + + + + + + 10 + 260 + 1071 + 101 + + + + + Arial + 25 + 75 + true + + + + 等待中 + + + + + + 10 + 360 + 1071 + 101 + + + + + Arial + 25 + 75 + true + + + + 等待中 + + + + + + 10 + 460 + 1071 + 41 + + + + + Arial + 25 + 75 + true + + + + 等待中 + + + + + + 90 + 550 + 231 + 71 + + + + + Arial + 25 + + + + -100.000000000000000 + + + 100.000000000000000 + + + 27.000000000000000 + + + + + + + 0 + 0 + 1080 + 23 + + + + + + + + diff --git a/notificationclient.cpp b/notificationclient.cpp new file mode 100644 index 0000000..b5c32b8 --- /dev/null +++ b/notificationclient.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "notificationclient.h" + +#include +#include + +NotificationClient::NotificationClient(QObject *parent) + : QObject(parent) +{ + connect(this, &NotificationClient::notificationChanged, + this, &NotificationClient::updateAndroidNotification); +} + +void NotificationClient::setNotification(const QString ¬ification) +{ + if (m_notification == notification) + return; + + //! [notification changed signal] + m_notification = notification; + emit notificationChanged(); + //! [notification changed signal] +} + +QString NotificationClient::notification() const +{ + return m_notification; +} + +//! [Send notification message to Java] +void NotificationClient::updateAndroidNotification() +{ + QJniObject javaNotification = QJniObject::fromString(m_notification); + QJniObject::callStaticMethod( + "cc/akashic/noderoid/NotificationClient", + "notify", + "(Landroid/content/Context;Ljava/lang/String;)V", + QNativeInterface::QAndroidApplication::context(), + javaNotification.object()); +} +//! [Send notification message to Java] diff --git a/notificationclient.h b/notificationclient.h new file mode 100644 index 0000000..f821dbf --- /dev/null +++ b/notificationclient.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NOTIFICATIONCLIENT_H +#define NOTIFICATIONCLIENT_H + +#include + +//! [Qt Notification Class] +class NotificationClient : public QObject +{ + Q_OBJECT +public: + explicit NotificationClient(QObject *parent = 0); + + void setNotification(const QString ¬ification); + QString notification() const; + +signals: + void notificationChanged(); + +private slots: + void updateAndroidNotification(); + +private: + QString m_notification; +}; +//! [Qt Notification Class] +#endif // NOTIFICATIONCLIENT_H diff --git a/syncdata.cpp b/syncdata.cpp new file mode 100644 index 0000000..1b1b4d2 --- /dev/null +++ b/syncdata.cpp @@ -0,0 +1,132 @@ +/** + * @file syncdata.cpp + * @author Myth + * @version 1.0 + * @date 2021.10.23 + */ + +#include +#include +#include +#include +#include + +#include "syncdata.h" + +//数据源链接,数据由其他程序生成 +#define DATA_URL "http://node.akashic.cc/data.json" + +//每 100ms 更新一次数据 +#define UPDATE_INTERVAL 100 + +SyncData::SyncData(QObject *parent) : QObject(parent) +{ + data_manager = new QNetworkAccessManager(this); + //每次请求返回结果后调用槽函数 dataReplyFinished + connect(data_manager, SIGNAL(finished(QNetworkReply *)), + this, SLOT(dataReplyFinished(QNetworkReply *))); + + timer = new QTimer(this); + //定时器触发后调用槽函数 updateNodeData + connect(timer, SIGNAL(timeout()), + this, SLOT(updateNodeData())); + timer->start(UPDATE_INTERVAL); +} + +/** + * @brief 槽函数:请求新的数据 + */ +void SyncData::updateNodeData(void) +{ + data_manager->get(QNetworkRequest(QUrl(DATA_URL))); +} + +/** + * @brief 槽函数:处理收到的数据 + */ +void SyncData::dataReplyFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError networkerror = reply->error(); + + if (networkerror == QNetworkReply::NoError) + { + // JSON 解析 + QByteArray databuff = reply->readAll(); + QString result = QString::fromStdString(databuff.toStdString()).toUtf8(); + QJsonDocument document; + QJsonParseError parseerror; + document = QJsonDocument::fromJson(result.toUtf8(), &parseerror); + + QString time; + double humi; + double temp; + double light; + + if (!document.isNull() && parseerror.error == QJsonParseError::NoError) + { + if (document.isObject()) + { + QJsonObject Object = document.object(); + if (!Object.isEmpty()) + { + if (Object.contains("time")) + { + QJsonValue time_val = Object.value("time"); + if (time_val.isString()) + { + time = time_val.toString(); + emit timeUpdated(time); //信号:时间更新 + } + } + + //遍历 2 ~ 5 节点数据 + for (int seq = 2; seq <= 5; seq++) + { + if (Object.contains("node" + QString::number(seq))) + { + QJsonValue dataObj = Object.value("node" + QString::number(seq)); + if (dataObj.isObject()) + { + QJsonObject ObjectData = dataObj.toObject(); + if (ObjectData.contains("humi")) + { + QJsonValue humi_val = ObjectData.value("humi"); + if (humi_val.isDouble()) + { + humi = humi_val.toDouble(); + } + } + + if (ObjectData.contains("temp")) + { + QJsonValue temp_val = ObjectData.value("temp"); + if (temp_val.isDouble()) + { + temp = temp_val.toDouble(); + } + } + + if (ObjectData.contains("light")) + { + QJsonValue light_val = ObjectData.value("light"); + if (light_val.isDouble()) + { + light = light_val.toDouble(); + } + } + + emit nodeDataUpdated(seq, humi, temp, light); //信号:数据更新 + } + } + } + } + } + } + } +} + +SyncData::~SyncData() +{ + delete data_manager; + delete timer; +} diff --git a/syncdata.h b/syncdata.h new file mode 100644 index 0000000..e7efdf0 --- /dev/null +++ b/syncdata.h @@ -0,0 +1,36 @@ +/** + * @file syncdata.h + * @author Myth + * @version 1.0 + * @date 2021.10.23 + */ + +#ifndef SYNCDATA_H +#define SYNCDATA_H + +#include +#include +#include + +class SyncData : public QObject +{ + Q_OBJECT + +public: + SyncData(QObject *parent = nullptr); + ~SyncData(); + +private: + QNetworkAccessManager *data_manager; + QTimer *timer; + +signals: + void timeUpdated(QString time); + void nodeDataUpdated(int seq, float humi, float temp, float light); + +private slots: + void updateNodeData(void); + void dataReplyFinished(QNetworkReply *reply); +}; + +#endif // SYNCDATA_H