Initial commit
commit
067dc38d38
@ -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…
Reference in New Issue