This page last changed on Nov 10, 2010 by trondk.

As explained in other posts I have with some success been able to get some basic OR functionality working with my KNX installation, hence have been able to play around with it a bit.

At the moment it seems like the status objects (sensors) are kept up to date by means of polling, once a second as far as I can see.

When scaling up from my simple tests (with one dimmer and one switch) this would mean that for lighting control in my system with (soon to have) between 30-50 dimmer channels, I am looking at 120-200 KNX telegrams per second on the KNX bus (read request and response for switch + dimmer for every dimmer channel.)

I am not sure I think that is the sanest approach.

For one thing, even if the KNX bus as such may have no problems with it (I don't know really, have not looked at the specs), it will for sure create a lot of "noise" in my telegram monitoring log (I log all traffic on the bus, to be able to do visualisation for stuff like heating control etc.)

I would think that an alternative would be to do the status objects by means of listening on the bus to any telegrams to the relevant group addresses, and update the status based on that?

Is there already plans for exploring such an approach? Would anybody be adversed to me hacking a bit in that area?

Also, if you agree that it would be nice to have this as an alternative way of handling status (e.g. configurable), do you think that this would be best done as a general mechanism for all protocols, or do you prefer a solution that is KNX only?

Anyone with opinions on this subject?

/Trond

Yes, start hacking.

It is actually already based on listening on the bus, in the KNXConnectionManager (https://openremote.svn.sourceforge.net/svnroot/openremote/trunk/Controller/src/org/openremote/controller/protocol/knx/KNXConnectionManager.java)

A lot of it is unfinished, I put it in place quickly and was distracted to other things.

What you will want to do is to throttle the protocol implementation, i.e. even if the system does request status updates (once a second, or whatever else you configure for it – this config also needs to be externalized), it doesn't mean that it has to translate to a request on the bus – it could return the status from its 'internalState' where we have the latest info received from the KNX bus.

Further, adding callbacks to the API for protocols to push events to status cache instead of using polling is on the list of things to do. If you want to start hacking on that, go ahead. It involves API changes so send stuff to review where you can.

Thanks for taking the initiative.

  /**
   * TODO
   *
   */
  private class KNXBusListener implements KNXListener
  {

    private KNXnetIPTunnel connection = null;
    private Map<GroupAddress, ApplicationProtocolDataUnit.ResponseAPDU> internalState =
        new ConcurrentHashMap<GroupAddress, ApplicationProtocolDataUnit.ResponseAPDU>(1000);



    private KNXBusListener(KNXnetIPTunnel connection)
    {
      this.connection = connection;

    }

    public void frameReceived(FrameEvent event)
    {
      try
      {
        log.debug("RECEIVED: " + event.getFrame());

        byte[] frame = event.getFrame().toByteArray();

        // TODO : properly handle AdditionalInfo field when AddInfo is present (currently breaks this impl.)

        if (DataLink.isDataIndicateFrame(frame[KNXCommand.CEMI_MESSAGECODE_OFFSET]))
        {
          GroupAddress address = new GroupAddress(
              frame[KNXCommand.CEMI_DESTADDR_HIGH_OFFSET],
              frame[KNXCommand.CEMI_DESTADDR_LOW_OFFSET]
          );

          byte dataLen    = frame[KNXCommand.CEMI_DATALEN_OFFSET];
          byte apciHi     = frame[KNXCommand.CEMI_TPCI_APCI_OFFSET];
          byte apciLoData = frame[KNXCommand.CEMI_APCI_DATA_OFFSET];

          // sanity checks -- is a response?

          if (!ApplicationProtocolDataUnit.isGroupValueResponse(new byte[] { apciHi, apciLoData }))
          {
            log.debug("Ignoring frame: " + event.getFrame());

            // TODO : should handle write requests coming to gateway, e.g. motion sensors

            return;
          }
          
          ApplicationProtocolDataUnit.ResponseAPDU apdu = null;

          if (dataLen == 1)
          {
            apdu = ApplicationProtocolDataUnit.ResponseAPDU.create6BitResponse
            (
                new byte[] { apciHi, apciLoData }
            );
          }

          else if (dataLen == 2)
          {
            apdu = ApplicationProtocolDataUnit.ResponseAPDU.create8BitResponse
            (
                new byte[] { apciHi, apciLoData, frame[KNXCommand.CEMI_DATA1_OFFSET] }
            );
          }

          else if (dataLen == 3)
          {
            apdu = ApplicationProtocolDataUnit.ResponseAPDU.createTwoByteResponse
            (
                new byte[] {
                    apciHi, apciLoData,
                    KNXCommand.CEMI_DATA1_OFFSET,
                    KNXCommand.CEMI_DATA2_OFFSET
                }
            );
          }

          else if (dataLen == 4)
          {
            apdu = ApplicationProtocolDataUnit.ResponseAPDU.createThreeByteResponse
            (
                new byte[] {
                    apciHi, apciLoData,
                    KNXCommand.CEMI_DATA1_OFFSET,
                    KNXCommand.CEMI_DATA2_OFFSET,
                    KNXCommand.CEMI_DATA3_OFFSET
                }
            );
          }

          else if (dataLen == 5)
          {
            apdu = ApplicationProtocolDataUnit.ResponseAPDU.createFourByteResponse
            (
                new byte[] {
                    apciHi, apciLoData,
                    KNXCommand.CEMI_DATA1_OFFSET,
                    KNXCommand.CEMI_DATA2_OFFSET,
                    KNXCommand.CEMI_DATA3_OFFSET,
                    KNXCommand.CEMI_DATA4_OFFSET
                }
            );
          }
          
          else
          {
            byte[] data = new byte[dataLen];
            System.arraycopy(frame, KNXCommand.CEMI_DATA1_OFFSET, data, 0, data.length);

            apdu = ApplicationProtocolDataUnit.ResponseAPDU.createStringResponse(data);
          }

          log.debug("Adding to internal state " + event.getFrame());

          internalState.put(address, apdu);
        }
Posted by juha at Nov 11, 2010 04:47

Thanks for the pointer!

I have barely started to dig into this, so it may take a while before I have something generic.

Howver, I managed to cook up a "workaround" patch that seems to work fairly well for my KNX setup. It works by basically storing everything received from the KNX bus in the internal cache, and then just avoid sending read-requests if there already is a value in the cache for the relevant address.

Regards,
Trond

Index: src/org/openremote/controller/protocol/knx/KNXConnectionManager.java   
===================================================================           
--- src/org/openremote/controller/protocol/knx/KNXConnectionManager.java        (revision 3159)
+++ src/org/openremote/controller/protocol/knx/KNXConnectionManager.java        (working copy) 
@@ -925,11 +925,11 @@                                                                          
                                                                                               
           if (!ApplicationProtocolDataUnit.isGroupValueResponse(new byte[] { apciHi, apciLoData }))
           {                                                                                        
-            log.debug("Ignoring frame: " + event.getFrame());                                      
+            //log.warn("Ignoring frame: " + event.getFrame());                                     
                                                                                                    
             // TODO : should handle write requests coming to gateway, e.g. motion sensors          
                                                                                                    
-            return;                                                                                
+            //return;                                                                              
           }                                                                                        
                                                                                                    
           ApplicationProtocolDataUnit.ResponseAPDU apdu = null;                                    
@@ -997,8 +997,15 @@                                                                                
             apdu = ApplicationProtocolDataUnit.ResponseAPDU.createStringResponse(data);            
           }                                                                                        
                                                                                                    
-          log.debug("Adding to internal state " + event.getFrame());                               
+          String sourceAddr = IndividualAddress.formatToAreaLineDevice(                            
+            new byte[] { frame[4/*CEMI_SOURCEADDR_HIGH_OFFSET*/],                                  
+                         frame[5/*CEMI_SOURCEADDR_LOW_OFFSET*/] }                                  
+            );                                                                                     
+                                                                                                   
+          if( !sourceAddr.equals("0.0.0") )                                                        
+          {                                                                                        
-          internalState.put(address, apdu);                                                        
+            internalState.put(address, apdu);
+          }
         }

 /*
@@ -1093,9 +1100,22 @@

     public ApplicationProtocolDataUnit read(GroupValueRead command)
     {
-      this.sendInternal(command);
-
+      // First check if we already have a cached value for this
+      // address...
       ApplicationProtocolDataUnit.ResponseAPDU response = busListener.internalState.get(command.getAddress());

+      if (response != null)
+      {
+        log.debug("############## Using cached value for address "
+                  + command.getAddress() + ": "
+                  + response.resolve(command.getDataPointType()));
+      }
+      else
+      {
+        this.sendInternal(command);
+
+        response = busListener.internalState.get(command.getAddress());
+      }
+
       if (response == null)
       {
Index: src/org/openremote/controller/statuscache/PollingMachineThread.java
===================================================================
--- src/org/openremote/controller/statuscache/PollingMachineThread.java (revision 3159)
+++ src/org/openremote/controller/statuscache/PollingMachineThread.java (working copy)
@@ -32,7 +32,7 @@

   private Sensor sensor;
        private StatusCacheService statusCacheService;
-       private static final long INTERVAL = 500;
+       private static final long INTERVAL = 200;
        private boolean alive = true;

        /** milliseconds */
Index: src/org/openremote/controller/statuscache/StatusCache.java
===================================================================
--- src/org/openremote/controller/statuscache/StatusCache.java  (revision 3159)
+++ src/org/openremote/controller/statuscache/StatusCache.java  (working copy)
@@ -65,8 +65,8 @@
    public synchronized void saveOrUpdateStatus(Integer componentID, String status) {
       String oldStatus = sensorStatus.get(componentID);
       if (status == null || "".equals(status)) {
-         logger.info("Status is null or \"\" while calling saveOrUpdateStatus in statusCache.");
-         return;
+        //logger.info("Status is null or \"\" while calling saveOrUpdateStatus in statusCache.");
+        //return;
       }

       boolean needNotify = false;
Posted by trondk at Nov 11, 2010 20:46

Hi Trond,

could you explain me how you solved it?

i want to use your code as well on my raspberry pi. But i don't know how to start.

your help will really be appreciated.

thx a lot

Posted by wuytens at Dec 04, 2014 19:03
Document generated by Confluence on Jun 05, 2016 09:30