From ba6d55e408dce923fc22263f410fbfbceb845595 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Wed, 13 Nov 2013 16:21:00 +0100 Subject: beta 2013.11.13 16:21 --- doc/context/scripts/mkiv/mtx-youless.html | 56 +++++ doc/context/scripts/mkiv/mtx-youless.man | 42 ++++ doc/context/scripts/mkiv/mtx-youless.xml | 29 +++ scripts/context/lua/mtx-youless.lua | 270 +++++++++++++++++++++ tex/context/base/cont-new.mkiv | 2 +- tex/context/base/context-version.pdf | Bin 4112 -> 4107 bytes tex/context/base/context.mkiv | 2 +- tex/context/base/core-con.lua | 2 +- tex/context/base/core-con.mkiv | 14 +- tex/context/base/s-youless.mkiv | 201 +++++++++++++++ tex/context/base/status-files.pdf | Bin 24519 -> 24516 bytes tex/context/base/status-lua.pdf | Bin 222448 -> 222369 bytes tex/generic/context/luatex/luatex-fonts-merged.lua | 2 +- 13 files changed, 609 insertions(+), 11 deletions(-) create mode 100644 doc/context/scripts/mkiv/mtx-youless.html create mode 100644 doc/context/scripts/mkiv/mtx-youless.man create mode 100644 doc/context/scripts/mkiv/mtx-youless.xml create mode 100644 scripts/context/lua/mtx-youless.lua create mode 100644 tex/context/base/s-youless.mkiv diff --git a/doc/context/scripts/mkiv/mtx-youless.html b/doc/context/scripts/mkiv/mtx-youless.html new file mode 100644 index 000000000..1889367d0 --- /dev/null +++ b/doc/context/scripts/mkiv/mtx-youless.html @@ -0,0 +1,56 @@ + + + + + + + + + + + youless Fetcher + + + + + +
+
youless Fetcher
+
+
+
+
wiki: http://contextgarden.net | mail: ntg-context@ntg.nl | website: http://www.pragma-ade.nl
+
+
+
+ +
+
+

Command line options

+ + + + + + + + + +
flagvaluedescription
--collectcollect data from device
--nobackupdon't backup old datafile
--nofiledon't write data to file (for checking)
--kwhsummative kwk data
--wattcollected watt data
--hostip address of device
+
+

Example

+mtxrun --script youless --collect --host=192.168.2.50 --kwk +
mtxrun --script youless --collect --host=192.168.2.50 --watt somefile.lua +

+
+ + diff --git a/doc/context/scripts/mkiv/mtx-youless.man b/doc/context/scripts/mkiv/mtx-youless.man new file mode 100644 index 000000000..bce5219c3 --- /dev/null +++ b/doc/context/scripts/mkiv/mtx-youless.man @@ -0,0 +1,42 @@ +.TH "mtx-youless" "1" "01-01-2013" "version 1.00" "youless Fetcher" +.SH NAME +.B mtx-youless +.SH SYNOPSIS +.B mtxrun --script youless [ +.I OPTIONS ... +.B ] [ +.I FILENAMES +.B ] +.SH DESCRIPTION +.B youless Fetcher +.SH OPTIONS +.TP +.B --collect +collect data from device +.TP +.B --nobackup +don't backup old datafile +.TP +.B --nofile +don't write data to file (for checking) +.TP +.B --kwh +summative kwk data +.TP +.B --watt +collected watt data +.TP +.B --host +ip address of device +.SH AUTHOR +More information about ConTeXt and the tools that come with it can be found at: + + +.B "maillist:" +ntg-context@ntg.nl / http://www.ntg.nl/mailman/listinfo/ntg-context + +.B "webpage:" +http://www.pragma-ade.nl / http://tex.aanhet.net + +.B "wiki:" +http://contextgarden.net diff --git a/doc/context/scripts/mkiv/mtx-youless.xml b/doc/context/scripts/mkiv/mtx-youless.xml new file mode 100644 index 000000000..9a34efce5 --- /dev/null +++ b/doc/context/scripts/mkiv/mtx-youless.xml @@ -0,0 +1,29 @@ + + + + mtx-youless + youless Fetcher + 1.00 + + + + + collect data from device + don't backup old datafile + don't write data to file (for checking) + summative kwk data + collected watt data + ip address of device + + + + + + Example + + mtxrun --script youless --collect --host=192.168.2.50 --kwk + mtxrun --script youless --collect --host=192.168.2.50 --watt somefile.lua + + + + diff --git a/scripts/context/lua/mtx-youless.lua b/scripts/context/lua/mtx-youless.lua new file mode 100644 index 000000000..18304571b --- /dev/null +++ b/scripts/context/lua/mtx-youless.lua @@ -0,0 +1,270 @@ +if not modules then modules = { } end modules ['mtx-youless'] = { + version = 1.002, + comment = "script tp fetch data from kwk meter polling device", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE", + license = "see context related readme files" +} + +-- This script can fetch data from a youless device (http://www.youless.nl/) where data +-- is merged into a file. The data concerns energy consumption (current wattage as well +-- as kwh usage). There is an accompanying module to generate graphics. + +require("util-jsn") + +-- the library variant: + +local youless = { } +utilities.youless = youless + +local lpegmatch = lpeg.match +local formatters = string.formatters + +local http = socket.http + +-- maybe just a special parser but who cares about speed here + +local function fetch(url,what,i) + local url = formatters["http://%s/V?%s=%i&f=j"](url,what,i) + local data = http.request(url) + local result = data and utilities.json.tolua(data) + return result +end + +-- "123" " 1,234" + +local tovalue = lpeg.Cs((lpeg.R("09") + lpeg.P(1)/"")^1) / tonumber + +-- "2013-11-12T06:40:00" + +local totime = (lpeg.C(4) / tonumber) * lpeg.P("-") + * (lpeg.C(2) / tonumber) * lpeg.P("-") + * (lpeg.C(2) / tonumber) * lpeg.P("T") + * (lpeg.C(2) / tonumber) * lpeg.P(":") + * (lpeg.C(2) / tonumber) * lpeg.P(":") + * (lpeg.C(2) / tonumber) + +local function get(url,what,i,data,average) + if not data then + data = { } + end + while true do + local d = fetch(url,what,i) + if d and next(d) then + local c_year, c_month, c_day, c_hour, c_minute, c_seconds = lpegmatch(totime,d.tm) + if c_year and c_seconds then + local delta = tonumber(d.dt) + local tnum = os.time { year = c_year, month = c_month, day = c_day, hour = c_hour, minute = c_minute } + local v = d.val + for i=1,#v do + local newvalue = lpegmatch(tovalue,v[i]) + if newvalue then + local t = tnum + (i-1)*delta + local current = os.date("%Y-%m-%dT%H:%M:%S",t) + local c_year, c_month, c_day, c_hour, c_minute, c_seconds = lpegmatch(totime,current) + if c_year and c_seconds then + local years = data.years if not years then years = { } data.years = years end + local d_year = years[c_year] if not d_year then d_year = { } years[c_year] = d_year end + local months = d_year.months if not months then months = { } d_year.months = months end + local d_month = months[c_month] if not d_month then d_month = { } months[c_month] = d_month end + local days = d_month.days if not days then days = { } d_month.days = days end + local d_day = days[c_day] if not d_day then d_day = { } days[c_day] = d_day end + if average then + d_day.average = newvalue + else + local hours = d_day.hours if not hours then hours = { } d_day.hours = hours end + local d_hour = hours[c_hour] if not d_hour then d_hour = { } hours[c_hour] = d_hour end + d_hour[c_minute] = newvalue + end + end + end + end + end + else + return data + end + i = i + 1 + end + return data +end + +-- day of month (kwh) +-- url = http://192.168.1.14/V?m=2 +-- m = the number of month (jan = 1, feb = 2, ..., dec = 12) + +-- hour of day (watt) +-- url = http://192.168.1.14/V?d=1 +-- d = the number of days ago (today = 0, yesterday = 1, etc.) + +-- 10 minutes (watt) +-- url = http://192.168.1.14/V?w=1 +-- w = 1 for the interval now till 8 hours ago. +-- w = 2 for the interval 8 till 16 hours ago. +-- w = 3 for the interval 16 till 24 hours ago. + +-- 1 minute (watt) +-- url = http://192.168.1.14/V?h=1 +-- h = 1 for the interval now till 30 minutes ago. +-- h = 2 for the interval 30 till 60 minutes ago + +function youless.collect(specification) + if type(specification) ~= "table" then + return + end + local host = specification.host or "" + local data = specification.data or { } + local filename = specification.filename or "" + local variant = specification.variant or "kwh" + local detail = specification.detail or false + local nobackup = specification.nobackup or false + if host == "" then + return + end + if name then + data = table.load(name) or data + end + if variant == "kwh" then + get(host,"m",1,data,true) + elseif variant == "watt" then + get(host,"d",0,data,true) + get(host,"w",1,data) + if detail then + get(host,"h",1,data) + end + end + if filename == "" then + return + end + local path = file.dirname(filename) + local base = file.basename(filename) + if nobackup then + -- saved but with checking + local tempname = file.join(path,"youless.tmp") + table.save(tempname,data) + local check = table.load(tempname) + if type(check) == "table" then + local keepname = file.replacesuffix(filename,"old") + os.remove(keepname) + if not lfs.isfile(keepname) then + os.rename(filename,keepname) + os.rename(tempname,filename) + end + end + else + local keepname = file.join(path,formatters["%s-%s"](os.date("%Y-%m-%d-%H-%M-%S",os.time()),base)) + os.rename(filename,keepname) + if not lfs.isfile(filename) then + table.save(filename,data) + end + end + return data +end + +-- local data = youless.collect { +-- host = "192.168.2.50", +-- variant = "watt", +-- filename = "youless-watt.lua" +-- } + +-- inspect(data) + +-- local data = youless.collect { +-- host = "192.168.2.50", +-- variant = "kwh", +-- filename = "youless-kwh.lua" +-- } + +-- inspect(data) + +-- the script + +local helpinfo = [[ + + + + mtx-youless + youless Fetcher + 1.00 + + + + + collect data from device + don't backup old datafile + don't write data to file (for checking) + summative kwk data + collected watt data + ip address of device + + + + + + Example + + mtxrun --script youless --collect --host=192.168.2.50 --kwk + mtxrun --script youless --collect --host=192.168.2.50 --watt somefile.lua + + + + +]] + +local application = logs.application { + name = "mtx-youless", + banner = "youless Fetcher", + helpinfo = helpinfo, +} + +local report = application.report + +scripts = scripts or { } +scripts.youless = scripts.youless or { } + +function scripts.youless.collect() + local host = environment.arguments.host + local variant = environment.arguments.kwh and "kwh" or environment.arguments.watt and "watt" + local nobackup = environment.arguments.nobackup + local nofile = environment.arguments.nofile + local filename = environment.files[1] + if not variant then + report("provide variant --kwh or --watt") + return + else + report("using variant %a",variant) + end + if not host then + host = "192.168.2.50" + report("using default host %a",host) + else + report("using host %a",host) + end + if nobackup then + report("not backing up data file") + end + if not filename and not nofile then + filename = formatters["youless-%s.lua"](variant) + end + if filename ~= "" then + report("using file %a",filename) + end + local data = youless.collect { + filename = filename, + host = host, + variant = variant, + nobackup = nobackup, + } + if type(data) ~= "table" then + report("no data collected") + elseif filename == "" then + report("data collected but not saved") + end +end + +if environment.argument("collect") then + scripts.youless.collect() +elseif environment.argument("exporthelp") then + application.export(environment.argument("exporthelp"),environment.files[1]) +else + application.help() +end diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv index 0506dce5d..f535e1628 100644 --- a/tex/context/base/cont-new.mkiv +++ b/tex/context/base/cont-new.mkiv @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2013.11.13 12:28} +\newcontextversion{2013.11.13 16:21} %D This file is loaded at runtime, thereby providing an excellent place for %D hacks, patches, extensions and new features. diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf index 853950cd3..10a349799 100644 Binary files a/tex/context/base/context-version.pdf and b/tex/context/base/context-version.pdf differ diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv index f945319dd..355879aa1 100644 --- a/tex/context/base/context.mkiv +++ b/tex/context/base/context.mkiv @@ -25,7 +25,7 @@ %D up and the dependencies are more consistent. \edef\contextformat {\jobname} -\edef\contextversion{2013.11.13 12:28} +\edef\contextversion{2013.11.13 16:21} \edef\contextkind {beta} %D For those who want to use this: diff --git a/tex/context/base/core-con.lua b/tex/context/base/core-con.lua index 315a34f39..dad24a7d4 100644 --- a/tex/context/base/core-con.lua +++ b/tex/context/base/core-con.lua @@ -971,7 +971,7 @@ local days = { -- not variables.sunday "saturday", } -local months = { -- not variables.januari +local months = { -- not variables.january "january", "february", "march", diff --git a/tex/context/base/core-con.mkiv b/tex/context/base/core-con.mkiv index 375d77072..a43473ced 100644 --- a/tex/context/base/core-con.mkiv +++ b/tex/context/base/core-con.mkiv @@ -206,8 +206,8 @@ %D %D Anyhow, the conversion looks like: -\def\monthlong #1{\ctxcommand{monthname(#1)}} -\def\monthshort#1{\ctxcommand{monthmnem(#1)}} +\unexpanded\def\monthlong #1{\ctxcommand{monthname(#1)}} +\unexpanded\def\monthshort#1{\ctxcommand{monthmnem(#1)}} \let\convertmonth\monthlong % for old times sake @@ -218,9 +218,9 @@ \let\month \monthlong -\def\MONTH #1{\WORD{\month {#1}}} -\def\MONTHLONG #1{\WORD{\monthlong {#1}}} -\def\MONTHSHORT#1{\WORD{\monthshort{#1}}} +\unexpanded\def\MONTH #1{\WORD{\month {#1}}} +\unexpanded\def\MONTHLONG #1{\WORD{\monthlong {#1}}} +\unexpanded\def\MONTHSHORT#1{\WORD{\monthshort{#1}}} %D We never explicitly needed this, but Tobias Burnus pointed out that it would be %D handy to convert to the day of the week. In doing so, we have to calculate the @@ -240,8 +240,8 @@ %D \showsetup{weekday} %D \showsetup{WEEKDAY} -\def\weekday#1{\ctxcommand{day(#1)}} -\def\WEEKDAY#1{\WORD{\weekday{#1}}} +\unexpanded\def\weekday#1{\ctxcommand{day(#1)}} +\unexpanded\def\WEEKDAY#1{\WORD{\weekday{#1}}} %D \macros %D {getdayoftheweek, dayoftheweek} diff --git a/tex/context/base/s-youless.mkiv b/tex/context/base/s-youless.mkiv new file mode 100644 index 000000000..fbc60ba47 --- /dev/null +++ b/tex/context/base/s-youless.mkiv @@ -0,0 +1,201 @@ +%D \module +%D [ file=s-youless, +%D version=2013.11.12, +%D title=\CONTEXT\ Style File, +%D subtitle=Youless Graphics, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D This is experimental code. When I have collected enough data I will make the +%D graphics nicer and provide some more. +%D +%D The Jouless can serve web pages but there is not much detail in them. They also are +%D somewhat bad \HTML, like unquoted attributes and so. We don't need this anyway as we +%D can also fetch data directly. The data is collected using a dedicated helper script +%D (of course we could just call it as module too). The data is fetched from the Jouless +%D device using queries (currently we use json, but a more direct parsing of data might +%D be more efficient). The data is converted into a proper \LUA\ table and saved (merged). + +% mtxrun --script youless --collect --kwk +% mtxrun --script youless --collect --watt + +\startluacode + + moduledata.youless = { } + + function moduledata.youless.enhance(data) + if data.years then + for y, year in next, data.years do + if year.months then + local a_year, n_year, m_year = 0, 0, 0 + for m, month in next, year.months do + if month.days then + n_year = n_year + 1 + local a_month, n_month = 0, 0 + for d, day in next, month.days do + if day.hours then + n_month = n_month + 1 + local a_day, n_day = 0, 0 + for h, hour in next, day.hours do + local a_hour, n_hour, m_hour = 0, 0, 0 + for k, v in next, hour do + if type(k) == "number" then + a_hour = a_hour + v + n_hour = n_hour + 1 + if v > m_hour then + m_hour = v + end + end + end + n_day = n_day + n_hour + a_day = a_day + a_hour + hour.maxwatt = m_hour + hour.watt = a_hour / n_hour + if m_hour > m_year then + m_year = m_hour + end + end + if n_day > 0 then + a_month = a_month + a_day + n_month = n_month + n_day + day.watt = a_day / n_day + else + day.watt = 0 + end + end + end + if n_month > 0 then + a_year = a_year + a_month + n_year = n_year + n_month + month.watt = a_month / n_month + else + month.watt = 0 + end + end + end + if n_year > 0 then + year.watt = a_year / n_year + year.maxwatt = m_year + else + year.watt = 0 + year.maxwatt = 0 + end + end + end + end + end + + function moduledata.youless.kwh(specification) + -- todo + end + + function moduledata.youless.watt(specification) + + local y = tonumber(specification.year) or os.today().year + local data = table.load(specification.filename or "youless-watt.lua") + + if not data then + return + end + + moduledata.youless.enhance(data) + + local year = data.years[y] + local scale = 10 + + for m=1,12 do + local month = year.months[m] + if month then + context.startMPpage { offset = "10pt" } + context("linecap := butt; pickup pencircle scaled .5") + + for i=0,(math.div(year.maxwatt,1000)+1)*1000,100 do + context("draw (%s,%s) -- (%s,%s) withcolor .6white ;",0,i/scale,31 * 24,i/scale) + end + + context("draw (0,%s) -- (31 * 24,%s) dashed dashpattern(on 3 off 3) withcolor darkgreen withpen pencircle scaled 1 ;",year.watt /scale,year.watt /scale) + context("draw (0,%s) -- (31 * 24,%s) dashed dashpattern(off 3 on 3) withcolor darkred withpen pencircle scaled 1 ;",month.watt/scale,month.watt/scale) + + local days = month.days + if days then + local nd = os.nofdays(y,m) + for d=1,nd do + local day = days[d] + local xoffset = (d-1) * 24 + local wd = os.weekday(d,m,y) + local weekend = wd == 1 or wd == 2 or wd == 7 + if weekend then + context("draw (%s,%s) -- (%s,%s); ",xoffset,-12,xoffset,weekend and -30) + context("draw (%s,%s) -- (%s,%s); ",xoffset+24,-12,xoffset+24,weekend and -30) + end + context([[draw textext("%s") shifted (%s,%s) ; ]],d,xoffset + 12,-20) + if day then + for h=0,23 do + local hours = day.hours + if hours then + local hour = hours[h] + if hour then + local dx = xoffset + h + local dy = hour.watt/scale + local dm = hour.maxwatt/scale + context("draw (%s,%s) -- (%s,%s) withcolor darkblue ; ",dx,0,dx,dy) + context("draw (%s,%s) -- (%s,%s) withcolor darkgray ; ",dx,dy,dx,dm) + end + end + end + end + end + for d=1,31 do + local xoffset = d * 24 + context("draw (%s,%s) -- (%s,%s) withcolor darkgray ; ",xoffset,0,xoffset,-10) + end + end + + local max = (math.div(year.maxwatt,1000)+1) + + for i=0,max*1000,1000 do + context([[draw textext.lft("%s") shifted (%s,%s) ; ]],i,-10,i/scale) + context("draw (%s,%s) -- (%s,%s) withcolor .2white ;",0,i/scale,31 * 24,i/scale) + end + + context([[draw textext("\strut\month{%s}\enspace%s") shifted (%s,%s) ; ]],m, y, 31 * 24 / 2, -50) + context([[draw textext("watt") rotated 90 shifted (%s,%s) ; ]],-15,50) + + context.stopMPpage() + else + -- maybe placeholder + end + end + + end + +\stopluacode + +\continueifinputfile{s-youless.mkiv} + +\setupbodyfont[dejavu] + +% printer (oce) : > 3000 W startup (900 W idle, 2000 W printing) +% coffeemaker : 1500 W when heating + +% baseline day : 2250 W (servers, airco, workstations, routers, switches, heating, etc) +% baseline night : 1750 W + +\starttext + + \startluacode + + os.execute([[mtxrun --script youless --collect --watt "c:/data/system/youless/data/youless-watt.lua"]]) + + -- os.execute([[mtxrun --script youless --collect --watt --nobackup "c:/data/system/youless/data/youless-watt.lua"]]) + + moduledata.youless.watt { year = 2013, filename = "c:/data/system/youless/data/youless-watt.lua" } + + \stopluacode + +\stoptext diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf index ce269173a..34d4875da 100644 Binary files a/tex/context/base/status-files.pdf and b/tex/context/base/status-files.pdf differ diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf index 3ac4ca292..9eb5052cc 100644 Binary files a/tex/context/base/status-lua.pdf and b/tex/context/base/status-lua.pdf differ diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index f6e95a000..202c24994 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 11/13/13 12:28:55 +-- merge date : 11/13/13 16:21:08 do -- begin closure to overcome local limits and interference -- cgit v1.2.3