Initial commit

master
Kuba Orlik 2 years ago
commit 067dc38d38

1
.gitignore vendored

@ -0,0 +1 @@
/solar-android.pro.user

@ -0,0 +1,41 @@
import QtQuick 2.0
Item {
property var data;
property var day_cache;
signal loaded();
property bool is_loaded;
id: root
function measure(title, fn){
const ts = new Date().getTime();
fn();
const te = new Date().getTime();
console.log(title, te-ts, "ms");
}
function refresh(callback){
log.text = "Wczytywanie..."
is_loaded = false;
data = [];
day_cache = {}
console.log("making request...");
http.get("https://solar.kuba-orlik.name/data.csv", function(text){
console.log("got response!");
background.send("newdata", text, function(data){
is_loaded = true;
const update_time = new Date(data[data.length-2]);
log.text = "Aktualna produkcja: " + data[data.length-2][1] + "W\nDane z " + update_time.getHours() + ":" + (update_time.getMinutes() < 10 ? "0" : "") + update_time.getMinutes();
root.loaded();
callback();
})
})
}
function getForDay(day, month, year, callback){
background.send("get-for-day", {day, month, year}, callback)
}
}

@ -0,0 +1,183 @@
import QtQuick 2.0
import QtCharts 2.3
Item {
property int year;
property int day;
property int month;
property bool isToday: false;
Component.onCompleted: {
refresh();
cache.loaded.connect(refresh)
}
Component.onDestruction: {
cache.loaded.disconnect(refresh);
}
function setTimeout(func, interval, ...params) {
console.log("Setting timeout!", interval)
return setTimeoutComponent.createObject(parent, { func, interval} );
}
function clearTimeout(timerObj) {
timerObj.stop();
timerObj.destroy();
}
Component {
id: setTimeoutComponent
Timer {
property var func
running: true
repeat: false
onTriggered: {
func();
destroy();
}
}
}
function draw(series, data, shifttime, index, ymax, xmin, xmax){
const ts = new Date().getTime();
var xa = xaxis;
var ya = yaxis;
if(index === undefined){
index = 0;
}
if(ymax === undefined){
ymax= 0;
xmin= new Date("2222-01-01");
xmax= new Date("1990-01-01");
}
if(index >= data.length){
xa.max = xmax;
xa.min = xmin;
ya.max = ymax;
return;
}
var step = 50;
for(var i=0; i<step; i+=2){
var coords = data[index+i];
if(!coords){ break};
var x = new Date(coords[3].getTime()+shifttime);
var y = coords[1]
if(y>ymax){
ymax = y
}
if(x>xmin){
xmax = x
}
if(x<xmin || xmin === 0){
xmin = x
}
series.append(x, y)
}
xa.max = xmax;
xa.min = xmin;
ya.max = ymax;
const te = new Date().getTime();
// console.log("drew", step, "points in ", te-ts, "ms")
background.setTimeout(function(){draw(series, data, shifttime, index+step, ymax, xmin, xmax)}, 0)
}
function refresh(){
var ts = new Date().getTime();
// chart.removeAllSeries();
const DAY =1000*60*60*24;
var s = series;
var ys = yesterdaySeries;
s.clear();
ys.clear();
cache.getForDay(day, month, year, function(data){draw(s, data, 0)})
if(isToday){
cache.getForDay(day-1, month, year, function(data){draw(ys, data, DAY)})
}
// const xa = xaxis;
// const ya = yaxis;
// cache.getForDay(day, month, year)
// .forEach(function(coords){
// var x = coords[3];
// var y = coords[1]
// if(y>ymax){
// ymax = y
// }
// if(x>xmin){
// xmax = x
// }
// if(x<xmin || xmin === 0){
// xmin = x
// }
// s.append(x, y)
// })
// cache.getForDay(day-1, month, year)
// .forEach(function(coords){
// var x = coords[3];
// var y = coords[1]
// if(y>ymax){
// ymax = y
// }
// if(x>xmin){
// xmax = x
// }
// if(x<xmin || xmin === 0){
// xmin = x
// }
// ys.append(x, y)
// })
// xa.max = xmax;
// xa.min = xmin;
// ya.max = ymax;
// var te = new Date().getTime();
// console.log("rendering graph: ", te-ts)
}
ChartView {
title: year.toString() + "-" + month.toString() + "-" + day.toString()
anchors.fill: parent
antialiasing: true
id: chart
DateTimeAxis{
id: xaxis
format: "hh:mm"
}
ValuesAxis {
id: yaxis
min: 0
}
AreaSeries{
useOpenGL: true
name: "wczoraj"
axisX: xaxis
axisY: yaxis
color: "#ccc"
visible: isToday
upperSeries: LineSeries {
id: yesterdaySeries
}
}
AreaSeries {
useOpenGL: true
name: isToday? "dzisiaj" : year + "-" + month + "-" + day
color: "#0074D9"
borderColor: "#0074D9"
axisX: xaxis
axisY: yaxis
borderWidth: 0
upperSeries: LineSeries{
id: series
}
}
}
}

@ -0,0 +1,15 @@
import QtQuick 2.4
Item {
function get(url, callback){
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
callback(xhr.responseText);
}};
xhr.send();
}
}

@ -0,0 +1,66 @@
var data = [];
const day_cache = {};
var is_loaded = false;
function getForDay(day, month, year){
if(!is_loaded) return [];
var key = day + "-" + month + "-" + year;
if(day_cache[key]) return day_cache[key];
const ts = new Date().getTime();
const targetDateStart = new Date(`${year}-${month<10?"0":""}${month}-${day<10? "0":""}${day}`)
const targetDateEnd = new Date(`${year}-${month<10?"0":""}${month}-${day<10? "0":""}${day}`);
targetDateEnd.setDate(targetDateEnd.getDate() + 1);
const timestamp_start = targetDateStart.getTime();
const timestamp_end = targetDateEnd.getTime();
var result = (data)
.filter(function(coords){
if(coords[1]>6000){return false}
const timestamp = coords[0]
//console.log(timestamp_start, timestamp, timestamp_end, timestamp <=timestamp_end && timestamp >= timestamp_start);
return timestamp <=timestamp_end && timestamp >= timestamp_start
})
const te = new Date().getTime();
day_cache[key] = result;
return result;
}
function newData(text){
const lines = text.split("\n");
// measure("map to floats", ()=>lines.map(function(line){
// var coords = line.split(",").map(parseFloat)
// var d = new Date(coords[0]);
// //(coords[0] / 3600) % 24
// //dateParser.hours(coords[0])
// //var hour = d.getHours() + d.getMinutes()/60;
// //coords.push(hour)
// }));
data = text.split("\n")
.map(function(line){
// maps to [timestamp, production, hour, datetime]
var coords = line.split(",").map(parseFloat)
var d = new Date(coords[0]);
//var hour = d.getHours() + d.getMinutes()/60;
//coords.push(hour)
coords.push(0);
// d.setDate(1);
// d.setMonth(1);
// d.setYear(1990); // so two dates always nicely overlap in comparison view
coords.push(d)
return coords;
})
is_loaded = true;
}
WorkerScript.onMessage = function(message) {
if(message.type === "newdata"){
newData(message.data);
WorkerScript.sendMessage({id: message.id, data});
}
if(message.type === "get-for-day"){
WorkerScript.sendMessage({id: message.id, data: getForDay(message.data.day, message.data.month, message.data.year)})
}
if(message.type === "set-timeout"){
WorkerScript.sendMessage({id: message.id})
}
}

@ -0,0 +1,40 @@
#include <QtWidgets/QApplication>
#include <QtQuick/QQuickView>
#include <QtCore/QDir>
#include <QtQml/QQmlEngine>
#include <QSslSocket>
#include <QApplication>
#include "date.h"
#include <QtQml>
int main(int argc, char *argv[])
{
QSurfaceFormat fmt;
fmt.setVersion(3, 1);
fmt.setDepthBufferSize(24);
QSurfaceFormat::setDefaultFormat(fmt);
qmlRegisterType<DateParser>("DateParser", 1, 0, "DateParser");
qDebug() << "Device supports OpenSSL: " << QSslSocket::supportsSsl();
// Qt Charts uses Qt Graphics View Framework for drawing, therefore QApplication must be used.
QApplication app(argc, argv);
QQuickView viewer;
// The following are needed to make examples run without having to install the module
// in desktop environments.
#ifdef Q_OS_WIN
QString extraImportPath(QStringLiteral("%1/../../../../%2"));
#else
QString extraImportPath(QStringLiteral("%1/../../../%2"));
#endif
viewer.engine()->addImportPath(extraImportPath.arg(QGuiApplication::applicationDirPath(),
QString::fromLatin1("qml")));
QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close);
viewer.setTitle(QStringLiteral("QML Chart"));
viewer.setSource(QUrl("qrc:/solar-android/main.qml"));
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
viewer.show();
return app.exec();
}

@ -0,0 +1,156 @@
import QtQuick 2.15
import QtQuick.Controls 6.3
Item {
width: 720
height: 480
visible: true
id: main
property int offset: 0;
XHR{id: http}
Data{id: cache}
Component.onCompleted: {
offset = 0;
init();
addDays(4);
}
function init(){
log.text = "Pobieranie danych..."
//daysModel.clear();
cache.refresh(function(){
offset = 0;
days.currentIndex = 0;
//addDays(4);
});
}
function addDays(n){
for(var i=0; i<=n; i++){
addDay();
}
}
function addDay(){
const d = new Date();
d.setDate(d.getDate() - offset);
daysModel.append({_day: d.getDate(), _month: d.getMonth()+1, _year: d.getFullYear(), _isToday: offset==0})
offset = offset +1;
}
NumberAnimation { id: anim; target: days; property: "contentX"; duration: 500 }
function gotoIndex(idx) {
anim.running = false;
var pos = days.contentX;
var destPos;
days.positionViewAtIndex(idx, ListView.Contain);
destPos = days.contentX;
anim.from = pos;
anim.to = destPos;
anim.running = true;
}
ListView {
id: days
anchors.fill: parent
highlightFollowsCurrentItem: true
anchors.topMargin: 54
snapMode: ListView.SnapToItem
cacheBuffer: 5000
layoutDirection: Qt.RightToLeft
orientation: ListView.Horizontal
focus: true
interactive: true
highlightMoveDuration: 50
currentIndex: 0
onAtXBeginningChanged: {
if (days.atXBeginning) {
addDays(5);
}
}
model: ListModel{
id: daysModel
}
delegate:
Day {
width: days.width
height: days.height
day: _day
month: _month
year: _year
isToday: _isToday
}
}
Text{id: log}
WorkerScript {
id: background
source: "background.mjs"
property var callback_number: 0;
property var callbacks: ({});
function send(type, data, cb){
background.callback_number++;
callbacks[callback_number] = cb
const message = {type, data, id: callback_number}
background.sendMessage(message)
}
function handleMessage(message){
const id = message.id;
if(callbacks[id]){
callbacks[id](message && message.data);
delete callbacks[id];
}
}
function setTimeout(callback){ // delay is assumed to be 0
send("set-timeout", 0, callback);
}
Component.onCompleted: {
background.onMessage.connect(handleMessage)
}
}
Row {
id: row
x: 184
width: 448
height: 40
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 8
anchors.rightMargin: 8
spacing: 12
layoutDirection: Qt.RightToLeft
Button {
id: button
text: qsTr("Odśwież")
onClicked:{
init();
}
}
Button {
id: button1
text: qsTr("Dzisiaj")
visible: !days.atXEnd;
onClicked: {
gotoIndex(0)
}
}
}
}

@ -0,0 +1,38 @@
QT += quick qml widgets charts
SOURCES += \
main.cpp
resources.files = main.qml XHR.qml Data.qml Day.qml background.mjs
resources.prefix = /$${TARGET}
RESOURCES += resources
QT_DEBUG_PLUGINS=1
QML_IMPORT_TRACE=1
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH = /home/kuba/Downloads/kquickcharts/lib/qml
QML2_IMPORT_PATH = /home/kuba/Downloads/kquickcharts/lib/qml
LIBS += -L/home/kuba/Downloads/kquickcharts/lib/qml
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES += \
../../../Downloads/kquickcharts/lib/qml/org/kde/quickcharts/qmldir \
Data.qml \
Day.qml \
XHR.qml main.qml \
background.mjs
android: include(../android_openssl/openssl.pri)
HEADERS += \
date.h
Loading…
Cancel
Save