Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Group Address as of ETS4 #52

Open
juanter opened this issue Jan 29, 2019 · 0 comments
Open

Group Address as of ETS4 #52

juanter opened this issue Jan 29, 2019 · 0 comments

Comments

@juanter
Copy link

juanter commented Jan 29, 2019

I have needed to communicate with a group address 24/4/1..250 and I have verified that the KnxHelper class was not ready and with a few small modifications and got it working.
I share my modifications in case you find them interesting.

KnxHelper.cs
`
using System;
using System.Linq;
using KNXLib.Exceptions;

namespace KNXLib
{
internal class KnxHelper
{
#region Address Processing
// +-----------------------------------------------+
// 16 bits | INDIVIDUAL ADDRESS |
// +-----------------------+-----------------------+
// | OCTET 0 (high byte) | OCTET 1 (low byte) |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// bits | 7| 6| 5| 4| 3| 2| 1| 0| 7| 6| 5| 4| 3| 2| 1| 0|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | Subnetwork Address | |
// +-----------+-----------+ Device Address |
// |(Area Adrs)|(Line Adrs)| |
// +-----------------------+-----------------------+
// -- up to ETS3 --------------------------------------------+
// +-----------------------------------------------+
// 16 bits | GROUP ADDRESS (3 level) |
// +-----------------------+-----------------------+
// | OCTET 0 (high byte) | OCTET 1 (low byte) |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// bits | 7| 6| 5| 4| 3| 2| 1| 0| 7| 6| 5| 4| 3| 2| 1| 0|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// up to ETS3| | Main Grp | Midd G | Sub Group |
// as of ETS4| Main Grp | Midd G | Sub Group |
// +--+--------------------+-----------------------+

    //           +-----------------------------------------------+
    // 16 bits   |             GROUP ADDRESS (2 level)           |
    //           +-----------------------+-----------------------+
    //           | OCTET 0 (high byte)   |  OCTET 1 (low byte)   |
    //           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    //    bits   | 7| 6| 5| 4| 3| 2| 1| 0| 7| 6| 5| 4| 3| 2| 1| 0|
    //           +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    // up to ETS3|  | Main Grp  |            Sub Group           |
    // as of ETS4|  Main Grp    |            Sub Group           |
    //           +--+--------------------+-----------------------+
    // These tables clearly show why ETS3 can only visualize 16 MaGs (0...15). 
    //  The reason is that the MaGs are coded in 4 bits. As of ETS4, 
    //  the entire range of usable GAs is doubled by using one more bit. 
    //  The 15th bit increases the number of group addresses by 32768 entries (2^16 - 2^15).

    public static bool IsAddressIndividual(string address)
    {
        return address.Contains('.');
    }

    public static string GetIndividualAddress(byte[] addr)
    {
        return GetAddress(addr, '.', false);
    }

    public static string GetGroupAddress(byte[] addr, bool threeLevelAddressing)
    {
        return GetAddress(addr, '/', threeLevelAddressing);
    }

    private static string GetAddress(byte[] addr, char separator, bool threeLevelAddressing)
    {
        var group = separator.Equals('/');
        string address;

        if (group && !threeLevelAddressing)
        {
            // 2 level group
            address = (addr[0] >> 3).ToString();
            address += separator;
            address += (((addr[0] & 0x07) << 8) + addr[1]).ToString(); // this may not work, must be checked
        }
        else
        {
            // 3 level individual or group
            // up to ETS3
            // address = group
            //    ? ((addr[0] & 0x7F) >> 3).ToString()
            //    : (addr[0] >> 4).ToString();
            // as of ETS4
            address = group
                ? (addr[0] >> 3).ToString()
                : (addr[0] >> 4).ToString();

            address += separator;

            if (group)
                address += (addr[0] & 0x07).ToString();
            else
                address += (addr[0] & 0x0F).ToString();

            address += separator;
            address += addr[1].ToString();
        }

        return address;
    }

    public static byte[] GetAddress(string address)
    {
        try
        {
            var addr = new byte[2];
            var threeLevelAddressing = true;
            string[] parts;
            var group = address.Contains('/');

            if (!group)
            {
                // individual address
                parts = address.Split('.');
                if (parts.Length != 3 || parts[0].Length > 2 || parts[1].Length > 2 || parts[2].Length > 3)
                    throw new InvalidKnxAddressException(address);
            }
            else
            {
                // group address
                parts = address.Split('/');
                if (parts.Length != 3 || parts[0].Length > 2 || parts[1].Length > 1 || parts[2].Length > 3)
                {
                    if (parts.Length != 2 || parts[0].Length > 2 || parts[1].Length > 4)
                        throw new InvalidKnxAddressException(address);

                    threeLevelAddressing = false;
                }
            }

            if (!threeLevelAddressing)
            {
                var part = int.Parse(parts[0]);
                // -- up to ETS3
                // if (part > 15)
                // -- as of ETS4
                if (part > 31)
                    throw new InvalidKnxAddressException(address);

                addr[0] = (byte)(part << 3);
                part = int.Parse(parts[1]);
                if (part > 2047)
                    throw new InvalidKnxAddressException(address);

                var part2 = BitConverter.GetBytes(part);
                if (part2.Length > 2)
                    throw new InvalidKnxAddressException(address);

                addr[0] = (byte)(addr[0] | part2[0]);
                addr[1] = part2[1];
            }
            else
            {
                var part = int.Parse(parts[0]);
                // -- up to ETS3
                // if (part > 15)
                // -- as of ETS4
                if (part > 31)
                    throw new InvalidKnxAddressException(address);

                addr[0] = group
                    ? (byte)(part << 3)
                    : (byte)(part << 4);

                part = int.Parse(parts[1]);
                if ((group && part > 7) || (!group && part > 15))
                    throw new InvalidKnxAddressException(address);

                addr[0] = (byte)(addr[0] | part);
                part = int.Parse(parts[2]);
                if (part > 255)
                    throw new InvalidKnxAddressException(address);

                addr[1] = (byte)part;
            }

            return addr;
        }
        catch (Exception)
        {
            throw new InvalidKnxAddressException(address);
        }
    }
    #endregion

    #region Control Fields
    // Bit order
    // +---+---+---+---+---+---+---+---+
    // | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
    // +---+---+---+---+---+---+---+---+

    //  Control Field 1

    //   Bit  |
    //  ------+---------------------------------------------------------------
    //    7   | Frame Type  - 0x0 for extended frame
    //        |               0x1 for standard frame
    //  ------+---------------------------------------------------------------
    //    6   | Reserved
    //        |
    //  ------+---------------------------------------------------------------
    //    5   | Repeat Flag - 0x0 repeat frame on medium in case of an error
    //        |               0x1 do not repeat
    //  ------+---------------------------------------------------------------
    //    4   | System Broadcast - 0x0 system broadcast
    //        |                    0x1 broadcast
    //  ------+---------------------------------------------------------------
    //    3   | Priority    - 0x0 system
    //        |               0x1 normal (also called alarm priority)
    //  ------+               0x2 urgent (also called high priority)
    //    2   |               0x3 low
    //        |
    //  ------+---------------------------------------------------------------
    //    1   | Acknowledge Request - 0x0 no ACK requested
    //        | (L_Data.req)          0x1 ACK requested
    //  ------+---------------------------------------------------------------
    //    0   | Confirm      - 0x0 no error
    //        | (L_Data.con) - 0x1 error
    //  ------+---------------------------------------------------------------


    //  Control Field 2

    //   Bit  |
    //  ------+---------------------------------------------------------------
    //    7   | Destination Address Type - 0x0 individual address
    //        |                          - 0x1 group address
    //  ------+---------------------------------------------------------------
    //   6-4  | Hop Count (0-7)
    //  ------+---------------------------------------------------------------
    //   3-0  | Extended Frame Format - 0x0 standard frame
    //  ------+---------------------------------------------------------------
    public enum KnxDestinationAddressType
    {
        INDIVIDUAL = 0,
        GROUP = 1
    }

    public static KnxDestinationAddressType GetKnxDestinationAddressType(byte control_field_2)
    {
        return (0x80 & control_field_2) != 0
            ? KnxDestinationAddressType.GROUP
            : KnxDestinationAddressType.INDIVIDUAL;
    }

    #endregion

    #region Data Processing
    // In the Common EMI frame, the APDU payload is defined as follows:

    // +--------+--------+--------+--------+--------+
    // | TPCI + | APCI + |  Data  |  Data  |  Data  |
    // |  APCI  |  Data  |        |        |        |
    // +--------+--------+--------+--------+--------+
    //   byte 1   byte 2  byte 3     ...     byte 16

    // For data that is 6 bits or less in length, only the first two bytes are used in a Common EMI
    // frame. Common EMI frame also carries the information of the expected length of the Protocol
    // Data Unit (PDU). Data payload can be at most 14 bytes long.  <p>

    // The first byte is a combination of transport layer control information (TPCI) and application
    // layer control information (APCI). First 6 bits are dedicated for TPCI while the two least
    // significant bits of first byte hold the two most significant bits of APCI field, as follows:

    //   Bit 1    Bit 2    Bit 3    Bit 4    Bit 5    Bit 6    Bit 7    Bit 8      Bit 1   Bit 2
    // +--------+--------+--------+--------+--------+--------+--------+--------++--------+----....
    // |        |        |        |        |        |        |        |        ||        |
    // |  TPCI  |  TPCI  |  TPCI  |  TPCI  |  TPCI  |  TPCI  | APCI   |  APCI  ||  APCI  |
    // |        |        |        |        |        |        |(bit 1) |(bit 2) ||(bit 3) |
    // +--------+--------+--------+--------+--------+--------+--------+--------++--------+----....
    // +                            B  Y  T  E    1                            ||       B Y T E  2
    // +-----------------------------------------------------------------------++-------------....

    //Total number of APCI control bits can be either 4 or 10. The second byte bit structure is as follows:

    //   Bit 1    Bit 2    Bit 3    Bit 4    Bit 5    Bit 6    Bit 7    Bit 8      Bit 1   Bit 2
    // +--------+--------+--------+--------+--------+--------+--------+--------++--------+----....
    // |        |        |        |        |        |        |        |        ||        |
    // |  APCI  |  APCI  | APCI/  |  APCI/ |  APCI/ |  APCI/ | APCI/  |  APCI/ ||  Data  |  Data
    // |(bit 3) |(bit 4) | Data   |  Data  |  Data  |  Data  | Data   |  Data  ||        |
    // +--------+--------+--------+--------+--------+--------+--------+--------++--------+----....
    // +                            B  Y  T  E    2                            ||       B Y T E  3
    // +-----------------------------------------------------------------------++-------------....
    public static string GetData(int dataLength, byte[] apdu)
    {

        switch (dataLength)
        {
            case 0:
                return string.Empty;
            case 1:
                return Convert.ToChar(0x3F & apdu[1]).ToString();
            case 2:
                return Convert.ToChar(apdu[2]).ToString();
            default:
                var data = string.Empty;
                for (var i = 2; i < apdu.Length; i++)
                    data += Convert.ToChar(apdu[i]);

                return data;
        }
    }

    public static int GetDataLength(byte[] data)
    {
        if (data.Length <= 0)
            return 0;

        if (data.Length == 1 && data[0] < 0x3F)
            return 1;

        if (data[0] < 0x3F)
            return data.Length;

        return data.Length + 1;
    }

    public static void WriteData(byte[] datagram, byte[] data, int dataStart)
    {
        if (data.Length == 1)
        {
            if (data[0] < 0x3F)
            {
                datagram[dataStart] = (byte)(datagram[dataStart] | data[0]);
            }
            else
            {
                datagram[dataStart + 1] = data[0];
            }
        }
        else if (data.Length > 1)
        {
            if (data[0] < 0x3F)
            {
                datagram[dataStart] = (byte)(datagram[dataStart] | data[0]);

                for (var i = 1; i < data.Length; i++)
                {
                    datagram[dataStart + i] = data[i];
                }
            }
            else
            {
                for (var i = 0; i < data.Length; i++)
                {
                    datagram[dataStart + 1 + i] = data[i];
                }
            }
        }
    }
    #endregion

    #region Service Type

    public enum SERVICE_TYPE
    {
        //0x0201
        SEARCH_REQUEST,
        //0x0202
        SEARCH_RESPONSE,
        //0x0203
        DESCRIPTION_REQUEST,
        //0x0204
        DESCRIPTION_RESPONSE,
        //0x0205
        CONNECT_REQUEST,
        //0x0206
        CONNECT_RESPONSE,
        //0x0207
        CONNECTIONSTATE_REQUEST,
        //0x0208
        CONNECTIONSTATE_RESPONSE,
        //0x0209
        DISCONNECT_REQUEST,
        //0x020A
        DISCONNECT_RESPONSE,
        //0x0310
        DEVICE_CONFIGURATION_REQUEST,
        //0x0311
        DEVICE_CONFIGURATION_ACK,
        //0x0420
        TUNNELLING_REQUEST,
        //0x0421
        TUNNELLING_ACK,
        //0x0530
        ROUTING_INDICATION,
        //0x0531
        ROUTING_LOST_MESSAGE,
        // UNKNOWN
        UNKNOWN
    }

    public static SERVICE_TYPE GetServiceType(byte[] datagram)
    {
        switch (datagram[2])
        {
            case (0x02):
                {
                    switch (datagram[3])
                    {
                        case (0x06):
                            return SERVICE_TYPE.CONNECT_RESPONSE;
                        case (0x09):
                            return SERVICE_TYPE.DISCONNECT_REQUEST;
                        case (0x0a):
                            return SERVICE_TYPE.DISCONNECT_RESPONSE;
                        case (0x08):
                            return SERVICE_TYPE.CONNECTIONSTATE_RESPONSE;
                    }
                }
                break;
            case (0x04):
                {
                    switch (datagram[3])
                    {
                        case (0x20):
                            return SERVICE_TYPE.TUNNELLING_REQUEST;
                        case (0x21):
                            return SERVICE_TYPE.TUNNELLING_ACK;
                    }
                }
                break;
        }
        return SERVICE_TYPE.UNKNOWN;
    }

    public static int GetChannelId(byte[] datagram)
    {
        if (datagram.Length > 6)
            return datagram[6];

        return -1;
    }

    #endregion
}

}

`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant