Posts: 200
Threads: 60
Joined: Jun 2015
Reputation:
7
Hi,
Ok so I have room of lights. These objects ( group addresses ) which are either switch ( boolean ) or value based ( scale ) they are tagged as "Liivng". I want to show the average value of these lights. All of the status objects have been tagged with the tag called "Living Feedback". An event based script is placed on the "Living Feedback" tag. I created a two virtual objects. The first allows me to set the value of tag objects. The second represents the feedback, the average value of the objects. The problem is that there is always a varying number of lights and it can that only one light is changed or all of them are changed. The feedback value can jump up and down whilst every light returns its status. I have tried to overcome this by not recalculating the every time by checking when the average value status object has just been updated. Which can lead to the value not always being correct. Is there a better way to do this?
Thanks,
Roger
Code: if (event.type=='groupwrite') then
local groupstatus = grp.find('5/3/0') -- Status address of living
local delta = os.time()-groupstatus.updatetime
if(delta>5) then
local currentV = round(groupstatus.value,0)
local sobj = grp.tag('Living Feedback')
local sTotal = 0
local counter = 0
for key, obj in ipairs(sobj) do
if(sobj[key].data==true)then
elseif(sobj[key].data==false)then
else
sTotal= sTotal + sobj[key].data
counter=counter+1
end
end
local value= round((sTotal / counter),0)
if(currentV~=value) then
grp.update('5/3/0', value, dt.scale)
end
end
end
Posts: 200
Threads: 60
Joined: Jun 2015
Reputation:
7
Posts: 1764
Threads: 6
Joined: Jul 2015
Reputation:
117
06.05.2016, 09:04
(This post was last modified: 06.05.2016, 09:37 by Erwin van der Zwart.)
Hi Roger,
You could do it directly with a sql query avg(),
Make a selection in table objects where tag contains TAG name with avg on
value.
This way you get back direct the avg of all your objects and send result to knx object
when ~= old value or delay to keep bus traffic low.
BR,
Erwin
Hi Roger,
You could do it directly with a sql query avg(),
Make a selection in table objects where tag contains TAG name with avg on
value.
This way you get back direct the avg of all your objects and send result to knx object
when ~= old value or delay to keep bus traffic low.
BR,
Erwin
Posts: 7758
Threads: 42
Joined: Jun 2015
Reputation:
447
You cannot do a SQL query on object values as only raw value is stored. The best approach would be to monitor the bus and calculate the average once there's no status telegram for several seconds. This example can be used a starting point:
http://forum.logicmachine.net/showthread...65#pid1265
Posts: 200
Threads: 60
Joined: Jun 2015
Reputation:
7
Hi,
That's my question. How can we determine when to make the avg calculation? Given effectively the avg status should only update once all the lights have given their feedback. Because in a group of the tagged objects, all could change or just one. The group can consist of 5 objects or it could be 100 objects. Selecting a fixed amount of time doesn't work all the time.
Thx,
Roger
Posts: 1764
Threads: 6
Joined: Jul 2015
Reputation:
117
Hi Edgars,
I think we can but i don't know how to convert hex to dec in a sql query
If i use decimal it works perfect:
result = db:getall('SELECT AVG(id) FROM objects WHERE tagcache LIKE "%AVG%"')
log(result[1]['AVG(id)'])
but for hex yow would do some extra like:
result = db:getall('SELECT AVG(CONVERT(INT,datahex)) FROM objects WHERE tagcache LIKE "%AVG%"')
log(result[1]['AVG(datahex)'])
But above is not correct...
BR,
Erwin
Posts: 7758
Threads: 42
Joined: Jun 2015
Reputation:
447
Each group consists of tagged objects so count does not matter, you have to monitor all objects anyhow. The idea is to have a timer for each group, where each status update resets/reloads this timer. Only once all status updates are received you can calculate and send the average. I'll try to prepare an example next week.
Posts: 200
Threads: 60
Joined: Jun 2015
Reputation:
7
Is there any update to this?
Posts: 7758
Threads: 42
Joined: Jun 2015
Reputation:
447
Sorry for the delay, here's a universal resident script which can be used for Average / AND / OR calculation.
Code: if not client then
-- each group has 3 fields:
-- tag - status object tag
-- output - status output object
-- mode - calculate mode (and/or/avg)
groups = {
{ tag = 'my_group_and', output = '1/1/3', mode = 'and' },
{ tag = 'my_group_or', output = '1/1/4', mode = 'or' },
{ tag = 'my_group_avg', output = '1/1/5', mode = 'avg' },
}
-- time to wait between last telegram from any status in group and update
updatedelay = 0.5
-- object value cache
values = {}
-- object datatype cache
datatypes = {}
-- send update only when value changes
grp.checkupdate = function(alias, value)
if values[ alias ] ~= value then
grp.update(alias, value)
end
end
calc = {}
-- AVERAGE value
calc['avg'] = function(group)
local result, count, value = 0, 0
for _, address in ipairs(group.objects) do
value = values[ address ]
-- number must be in [0..100] range
if type(value) == 'number' then
result = result + value
count = count + 1
-- boolean true is 100%, false is 0%
elseif type(value) == 'boolean' then
if toboolean(value) then
result = result + 100
end
count = count + 1
end
end
if count > 0 then
result = math.floor(result / count + 0.5)
grp.checkupdate(group.output, result)
end
end
-- AND gate
calc['and'] = function(group)
local result = true
for _, address in ipairs(group.objects) do
result = result and toboolean(values[ address ])
end
grp.checkupdate(group.output, result)
end
-- OR gate
calc['or'] = function(group)
local result = false
for _, address in ipairs(group.objects) do
result = result or toboolean(values[ address ])
end
grp.checkupdate(group.output, result)
end
-- prepare each group
for _, group in ipairs(groups) do
object = grp.find(group.output)
-- cache output status object value and datatype
values[ object.address ] = object.data
datatypes[ object.address ] = object.datatype
group.output = object.address
-- group input status object list
group.objects = {}
-- find all status objects and cache values and datatypes
objects = grp.tag(group.tag)
for _, object in ipairs(objects) do
values[ object.address ] = object.data
datatypes[ object.address ] = object.datatype
table.insert(group.objects, object.address)
end
-- force update on first run
group.timer = 0
-- calc function reference
group.fn = calc[ group.mode ]
end
-- handle group writes
function eventhandler(event)
local dst, datatype
dst = event.dst
datatype = datatypes[ event.dst ]
-- unknown object, stop
if not datatype then
return
end
values[ dst ] = dpt.decode(event.datahex, datatype)
-- check if any group needs to be updated
for _, group in ipairs(groups) do
for _, address in ipairs(group.objects) do
if address == dst then
group.timer = updatedelay
end
end
end
end
require('genohm-scada.eibdgm')
client = eibdgm:new({ timeout = 0.25 })
client:sethandler('groupwrite', eventhandler)
end
tsec, tusec = os.microtime()
client:step()
delta = os.udifftime(tsec, tusec)
-- check if any group has an active timer
for _, group in ipairs(groups) do
timer = group.timer
if timer then
timer = timer - delta
-- timer expired, run calc function
if timer <= 0 then
group.fn(group)
timer = nil
end
group.timer = timer
end
end
Posts: 940
Threads: 161
Joined: Jul 2015
Reputation:
33
14.06.2016, 08:09
(This post was last modified: 14.06.2016, 08:31 by buuuudzik.)
Perfect solution admin
Do I must use 1 script for 1 central status e.g. Room and another script for another status? Or I can add in this script a few groups of statuses e.g. Room1, Room2, Room3, Floor1 etc...
I tried to add:
Code: groups = {
{ tag = 'my_group_and', output = '4/0/0', mode = 'and' },
{ tag = 'my_group_or', output = '4/0/1', mode = 'or' },
{ tag = 'my_group_avg', output = '4/0/2', mode = 'avg' },
{ tag = 'new_group_and', output = '4/0/20', mode = 'and' },
{ tag = 'new_group_or', output = '4/0/21', mode = 'or' },
{ tag = 'new_group_avg', output = '4/0/22', mode = 'avg' },
}
but new_group_xxx is not calculated.
Posts: 7758
Threads: 42
Joined: Jun 2015
Reputation:
447
You should do full script reload after adding new group via disable / enable. You should also use only one script for all groups, otherwise there's unnecessary resource usage increase.
Posts: 940
Threads: 161
Joined: Jul 2015
Reputation:
33
(14.06.2016, 08:49)admin Wrote: You should do full script reload after adding new group via disable / enable. You should also use only one script for all groups, otherwise there's unnecessary resource usage increase.
You're right It works. Perfect solution.
Posts: 200
Threads: 60
Joined: Jun 2015
Reputation:
7
This is cool, thank you. Yes it works.
Posts: 8
Threads: 4
Joined: Jan 2016
Reputation:
0
HI,
I've tried this script also for obtaining an average value of some temperature values obtained from a temperature sensor that send values at 5-10 second.
The script should work at each 10 min(scheduled script), and calculate the average value for the elapsed time from the last run
Maybe something I make wrong, or I just miss something.
13/2/9 - is the GA where the sensor sends the values
13/2/11 - should be the average value of received value.
This is script:
Code: if not client then
-- each group has 3 fields:
-- tag - status object tag
-- output - status output object
-- mode - calculate mode (and/or/avg)
groups = {
{ tag = '13/2/9', output = '13/2/11', mode = 'avg' },
}
-- time to wait between last telegram from any status in group and update
updatedelay = 0.5
-- object value cache
values = {}
-- object datatype cache
datatypes = {}
-- send update only when value changes
grp.checkupdate = function(alias, value)
if values[ alias ] ~= value then
grp.update(alias, value)
end
end
calc = {}
-- AVERAGE value
calc['avg'] = function(group)
local result, count, value = 0, 0
for _, address in ipairs(group.objects) do
value = values[ address ]
-- number must be in [0..100] range
if type(value) == 'number' then
result = result + value
count = count + 1
-- boolean true is 100%, false is 0%
elseif type(value) == 'boolean' then
if toboolean(value) then
result = result + 100
end
count = count + 1
end
end
if count > 0 then
result = math.floor(result / count + 0.5)
grp.checkupdate(group.output, result)
end
end
-- AND gate
calc['and'] = function(group)
local result = true
for _, address in ipairs(group.objects) do
result = result and toboolean(values[ address ])
end
grp.checkupdate(group.output, result)
end
-- OR gate
calc['or'] = function(group)
local result = false
for _, address in ipairs(group.objects) do
result = result or toboolean(values[ address ])
end
grp.checkupdate(group.output, result)
end
-- prepare each group
for _, group in ipairs(groups) do
object = grp.find(group.output)
-- cache output status object value and datatype
values[ object.address ] = object.data
datatypes[ object.address ] = object.datatype
group.output = object.address
-- group input status object list
group.objects = {}
-- find all status objects and cache values and datatypes
objects = grp.tag(group.tag)
for _, object in ipairs(objects) do
values[ object.address ] = object.data
datatypes[ object.address ] = object.datatype
table.insert(group.objects, object.address)
end
-- force update on first run
group.timer = 0
-- calc function reference
group.fn = calc[ group.mode ]
end
-- handle group writes
function eventhandler(event)
local dst, datatype
dst = event.dst
datatype = datatypes[ event.dst ]
-- unknown object, stop
if not datatype then
return
end
values[ dst ] = dpt.decode(event.datahex, datatype)
-- check if any group needs to be updated
for _, group in ipairs(groups) do
for _, address in ipairs(group.objects) do
if address == dst then
group.timer = updatedelay
end
end
end
end
require('genohm-scada.eibdgm')
client = eibdgm:new({ timeout = 0.25 })
client:sethandler('groupwrite', eventhandler)
end
tsec, tusec = os.microtime()
client:step()
delta = os.udifftime(tsec, tusec)
-- check if any group has an active timer
for _, group in ipairs(groups) do
timer = group.timer
if timer then
timer = timer - delta
-- timer expired, run calc function
if timer <= 0 then
group.fn(group)
timer = nil
end
group.timer = timer
end
end
Posts: 7758
Threads: 42
Joined: Jun 2015
Reputation:
447
No, this script cannot be used to calculate averages for a single group address because it does not store any historical info.
Since your sensor sends data periodically, you can map an event script to 13/2/9 which will store values and calculate average once 10 minutes pass.
Code: -- storage key
key = 'temp_average'
-- time between average value send (in seconds)
maxtime = 10 * 60
-- resulting average value
result = '13/2/11'
time = os.time()
data = storage.get(key)
if not data then
data = { values = {}, time = time }
end
value = event.getvalue()
table.insert(data.values, value)
delta = time - data.time
if delta >= maxtime or delta < 0 then
storage.delete(key)
avg = 0
count = #data.values
for _, value in ipairs(data.values) do
avg = avg + value
end
grp.write(result, avg / count)
else
storage.set(key, data)
end
Posts: 8
Threads: 4
Joined: Jan 2016
Reputation:
0
Thanks for the script. It works like a charm
Posts: 32
Threads: 14
Joined: Mar 2016
Reputation:
0
Hello Admin,
I copied the script, added the tags and only used the 'and' function
groups = {
{ tag = 'GROEP_Route_Wasplaats', output = '2/1/70', mode = 'and' },
{ tag = 'GROEP_Route_Mudroom', output = '2/1/71', mode = 'and' }
}
I get an error in line 84 : Resident script:84: attempt to index global 'object' (a nil value)
stack traceback:
Assigned output GA's don't update
I do not have anything in the common functions
I truly hope you have any Ideas?
Regards,
Hendrik
Posts: 7758
Threads: 42
Joined: Jun 2015
Reputation:
447
Have you created the output objects?
Posts: 32
Threads: 14
Joined: Mar 2016
Reputation:
0
Ha, I thought so but I missed one.
Thanks!
Posts: 44
Threads: 8
Joined: Mar 2022
Reputation:
0
Quote:groups = {
{ tag = 'my_group_and', output = '4/0/0', mode = 'and' },
{ tag = 'my_group_or', output = '4/0/1', mode = 'or' },
{ tag = 'my_group_avg', output = '4/0/2', mode = 'avg' },
{ tag = 'new_group_and', output = '4/0/20', mode = 'and' },
{ tag = 'new_group_or', output = '4/0/21', mode = 'or' },
{ tag = 'new_group_avg', output = '4/0/22', mode = 'avg' },
}
Hello, I don´t understand how to create OR group of few inputs to one output.
Can you help please?
|