This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

the average value of tagged objects
#1
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:
12345678910111213141516171819202122
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
Reply
#2
Any suggestions?
Reply
#3
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
Reply
#4
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
Reply
#5
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
Reply
#6
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
Reply
#7
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.
Reply
#8
Is there any update to this?
Reply
#9
Sorry for the delay, here's a universal resident script which can be used for Average / AND / OR calculation.

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
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
Reply
#10
Perfect solution adminWink

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:
123456789
 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.
Reply
#11
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.
Reply
#12
(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 rightSmile It works. Perfect solution.
Reply
#13
This is cool, thank you. Yes it works.
Reply
#14
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:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
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
Reply
#15
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:
12345678910111213141516171819202122232425262728293031
-- 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
Reply
#16
Thanks for the script. It works like a charm Wink
Reply
#17
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
Reply
#18
Have you created the output objects?
Reply
#19
Ha, I thought so but I missed one.

Thanks!
Reply
#20
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?
Reply


Forum Jump: