public List <byte[]> PackDown(List <UserLevelObject> userLevelObjects, ApplicationFunctionCodes functionCode, bool isRequest, bool isMaster)
        {
            if (userLevelObjects.Count == 0)
            {
                return(null);
            }

            List <byte[]> fragments = new List <byte[]>();

            byte[] tempFragment       = new byte[maxFragmentSize];
            byte[] finalFragment      = null;
            int    index              = 0;
            bool   fragmentInProgress = true;

            Header header = null;

            if (functionCode == ApplicationFunctionCodes.RESPONSE)
            {
                header = new ResponseHeader();

                ((ResponseHeader)header).InternalIndications[0]  = false;
                ((ResponseHeader)header).InternalIndications[1]  = false;
                ((ResponseHeader)header).InternalIndications[2]  = false;
                ((ResponseHeader)header).InternalIndications[3]  = false;
                ((ResponseHeader)header).InternalIndications[4]  = false;
                ((ResponseHeader)header).InternalIndications[5]  = false;
                ((ResponseHeader)header).InternalIndications[6]  = false;
                ((ResponseHeader)header).InternalIndications[7]  = false;
                ((ResponseHeader)header).InternalIndications[8]  = false;
                ((ResponseHeader)header).InternalIndications[9]  = false;
                ((ResponseHeader)header).InternalIndications[10] = false;
                ((ResponseHeader)header).InternalIndications[11] = false;
                ((ResponseHeader)header).InternalIndications[12] = false;
                ((ResponseHeader)header).InternalIndications[13] = false;
                ((ResponseHeader)header).InternalIndications[14] = false;
                ((ResponseHeader)header).InternalIndications[15] = false;

                lock (lockObject)
                {
                    if (sequence == -1)
                    {
                        sequence = 0;
                    }
                    header.ApplicationControl = new BitArray(new byte[1] {
                        BitConverter.GetBytes(sequence)[0]
                    });
                }
            }
            else
            {
                header = new RequestHeader();

                lock (lockObject)
                {
                    //if (++sequence > 15)
                    //{
                    //    sequence = 0;
                    //}
                    header.ApplicationControl = new BitArray(new byte[1] {
                        BitConverter.GetBytes(sequence)[0]
                    });
                }
            }

            header.ApplicationControl[7] = false;    // FIR
            header.ApplicationControl[6] = false;    // FIN
            header.ApplicationControl[5] = false;    // CON
            header.ApplicationControl[4] = false;    // UNS

            header.FunctionCode = functionCode;

            byte[] tempHeader = header.ToBytes();
            tempHeader.CopyTo(tempFragment, index);
            index += tempHeader.Count();

            byte[] tempObjHeader = null;

            foreach (UserLevelObject userLevelObject in userLevelObjects)
            {
                ObjectHeader objectHeader = new ObjectHeader();

                switch (userLevelObject.PointType)
                {
                case PointType.ANALOG_INPUT:
                    objectHeader.Group = 30;

                    switch (userLevelObject.Variation)
                    {
                    case Variations.BIT_32_NO_FLAG:
                        objectHeader.Variation = 3;
                        break;
                    }
                    break;
                }

                objectHeader.QualifierField[7] = false;
                if (userLevelObject.IndicesPresent)
                {
                    objectHeader.QualifierField[6] = false;
                    objectHeader.QualifierField[5] = false;
                    objectHeader.QualifierField[4] = true;
                }
                else if (userLevelObject.ObjectSizePresent)
                {
                    // TO DO
                }
                else
                {
                    objectHeader.QualifierField[6] = false;
                    objectHeader.QualifierField[5] = false;
                    objectHeader.QualifierField[4] = false;
                }

                if (userLevelObject.RangeFieldPresent)
                {
                    if (userLevelObject.RangePresent)
                    {
                        objectHeader.RangeField    = new byte[2];
                        objectHeader.RangeField[0] = (byte)userLevelObject.StartIndex;
                        objectHeader.RangeField[1] = (byte)userLevelObject.StopIndex;
                    }
                    else if (userLevelObject.ObjectCountPresent)
                    {
                        objectHeader.RangeField = new byte[2];
                        byte[] temp = BitConverter.GetBytes(userLevelObject.ObjectCount);
                        objectHeader.RangeField[0] = temp[0];
                        objectHeader.RangeField[1] = temp[1];
                    }
                }

                tempObjHeader = objectHeader.ToBytes();

                if ((index += tempObjHeader.Count()) > maxFragmentSize)
                {
                    finalFragment = new byte[index];
                    for (int i = 0; i < index; i++)
                    {
                        finalFragment[i] = tempFragment[i];
                    }

                    fragments.Add(finalFragment);

                    index = objectHeader.ToBytes().Count();
                }

                int startIndex, stopIndex;
                startIndex = userLevelObject.StartIndex;
                stopIndex  = userLevelObject.StopIndex;

                byte[] tempQualifier = new byte[1];
                objectHeader.QualifierField.CopyTo(tempQualifier, 0);
                byte qualifierByte = tempQualifier[0];

                switch (qualifierByte)
                {
                case 0x00:
                case 0x01:

                    int counter = -1;
                    foreach (byte[] value in userLevelObject.Values)
                    {
                        counter++;
                        if ((index + value.Count()) <= maxFragmentSize)
                        {
                            value.CopyTo(tempFragment, index);
                            index += value.Count();

                            if (userLevelObject.Values.Count() != 1)
                            {
                                stopIndex = userLevelObject.Indices[counter];
                            }
                            else
                            {
                                stopIndex = startIndex;
                            }

                            fragmentInProgress = true;
                        }
                        else
                        {
                            objectHeader.RangeField[0] = (byte)startIndex;
                            objectHeader.RangeField[1] = (byte)stopIndex;

                            tempObjHeader = objectHeader.ToBytes();
                            tempObjHeader.CopyTo(tempFragment, tempHeader.Count());

                            finalFragment = new byte[index];
                            for (int i = 0; i < index; i++)
                            {
                                finalFragment[i] = tempFragment[i];
                            }

                            fragments.Add(finalFragment);

                            tempHeader = header.ToBytes();
                            tempHeader.CopyTo(tempFragment, index);
                            index = tempHeader.Count();

                            index += objectHeader.ToBytes().Count();

                            value.CopyTo(tempFragment, index);
                            index     += value.Count();
                            startIndex = stopIndex = userLevelObject.Indices[counter];

                            fragmentInProgress = false;
                        }
                    }

                    if (fragmentInProgress)
                    {
                        objectHeader.RangeField[0] = (byte)startIndex;
                        objectHeader.RangeField[1] = (byte)stopIndex;

                        tempObjHeader = objectHeader.ToBytes();
                        tempObjHeader.CopyTo(tempFragment, tempHeader.Count());
                    }

                    break;

                case 0x07:
                case 0x08:
                    break;

                case 0x06:
                    break;

                case 0x17:
                    if (functionCode == ApplicationFunctionCodes.RESPONSE ||
                        functionCode == ApplicationFunctionCodes.WRITE)
                    {
                        for (int i = 0; i < userLevelObject.Values.Count(); i++)
                        {
                            tempFragment[index++] = (byte)userLevelObject.Indices[i];
                            byte[] tempValue = userLevelObject.Values[i];
                            tempValue.CopyTo(tempFragment, index);
                            index += tempValue.Count();
                        }
                    }
                    else
                    {
                        for (int i = 0; i < userLevelObject.Indices.Count(); i++)
                        {
                            tempFragment[index++] = (byte)userLevelObject.Indices[i];
                        }
                    }

                    break;

                case 0x28:
                    if (functionCode == ApplicationFunctionCodes.RESPONSE ||
                        functionCode == ApplicationFunctionCodes.WRITE)
                    {
                        for (int i = 0; i < userLevelObject.Values.Count(); i++)
                        {
                            byte[] tempIndices = BitConverter.GetBytes(userLevelObject.Indices[i]);
                            tempFragment[index++] = tempIndices[0];
                            tempFragment[index++] = tempIndices[1];
                            byte[] tempValue = userLevelObject.Values[i];
                            tempValue.CopyTo(tempFragment, index);
                            index += tempValue.Count();
                        }
                    }
                    else
                    {
                        for (int i = 0; i < userLevelObject.Indices.Count(); i++)
                        {
                            byte[] tempIndices = BitConverter.GetBytes(userLevelObject.Indices[i]);
                            tempFragment[index++] = tempIndices[0];
                            tempFragment[index++] = tempIndices[1];
                        }
                    }

                    break;

                case 0x5b:

                    break;
                }
            }

            finalFragment = new byte[index];
            for (int i = 0; i < index; i++)
            {
                finalFragment[i] = tempFragment[i];
            }

            fragments.Add(finalFragment);
            byte[] tempControl = new byte[1];

            if (fragments.Count() == 1)
            {
                BitArray appControl = new BitArray(new byte[1] {
                    fragments.First()[0]
                });
                appControl[7] = true;
                appControl[6] = true;
                appControl.CopyTo(tempControl, 0);
                fragments.First()[0] = tempControl[0];
            }
            else
            {
                BitArray appControl = new BitArray(new byte[1] {
                    fragments.First()[0]
                });
                appControl[7] = true;
                appControl.CopyTo(tempControl, 0);
                fragments.First()[0] = tempControl[0];

                appControl = new BitArray(new byte[1] {
                    fragments.Last()[0]
                });
                appControl[6]       = true;
                fragments.Last()[0] = tempControl[0];
            }

            List <byte[]> returnValue = new List <byte[]>();

            DNP3TransportFunctionHandler = new TransportFunctionHandler();

            foreach (byte[] fragment in fragments)
            {
                List <byte[]> segments = DNP3TransportFunctionHandler.PackDown(fragment, isRequest, isMaster);

                foreach (byte[] segment in segments)
                {
                    returnValue.Add(segment);
                }
            }

            return(returnValue);
        }
        public List <UserLevelObject> PackUp(byte[] data)
        {
            int length = data.Length;

            BitArray applicationCtrl     = new BitArray(new byte[] { data[0] });
            BitArray tempApplicationCtrl = new BitArray(applicationCtrl);

            tempApplicationCtrl[7] = false;
            tempApplicationCtrl[6] = false;
            tempApplicationCtrl[5] = false;
            tempApplicationCtrl[4] = false;

            byte[] tempSeq = new byte[1];
            tempApplicationCtrl.CopyTo(tempSeq, 0);

            ApplicationFunctionCodes functionCode = (ApplicationFunctionCodes)data[1];
            byte newSeq = tempSeq[0];

            if (applicationCtrl[7] == true)
            {
                sequence = newSeq;
                //Fir = true;
                //if (sequence == -1)
                //{
                //    sequence = newSeq;
                //}
                //else if (functionCode != ApplicationFunctionCodes.RESPONSE)
                //{
                //    if (newSeq != sequence + 1)
                //    {
                //        // log error
                //    }

                //    lock (lockObject)
                //    {
                //        sequence = newSeq;
                //    }
                //}

                ObjectsUpProcess.Clear();
            }
            else if (!Fir)
            {
                // LOG ERROR
                //return null;
            }
            else if (functionCode != ApplicationFunctionCodes.RESPONSE && newSeq != sequence + 1)
            {
                // LOG ERROR
                //return null;
            }

            Fin = applicationCtrl[6];

            int index = 0;

            if (functionCode == ApplicationFunctionCodes.RESPONSE)
            {
                length -= 4;
                index   = 4;
            }
            else
            {
                length -= 2;
                index   = 2;
            }

            while (length > 0)
            {
                length -= 3;

                UserLevelObject userLevelObject = new UserLevelObject();
                userLevelObject.FunctionCode = functionCode;

                ObjectHeader objectHeader = new ObjectHeader();
                objectHeader.Group          = data[index++];
                objectHeader.Variation      = data[index++];
                objectHeader.QualifierField = new BitArray(new byte[] { data[index++] });

                int valueSize = 0;
                switch (objectHeader.Group)
                {
                case 30:
                    userLevelObject.PointType = PointType.ANALOG_INPUT;

                    switch (objectHeader.Variation)
                    {
                    case 3:
                        userLevelObject.Variation = Variations.BIT_32_NO_FLAG;
                        valueSize = 4;
                        break;
                    }
                    break;
                }

                byte[] tempQualifier = new byte[1];
                objectHeader.QualifierField.CopyTo(tempQualifier, 0);
                byte qualifierByte = tempQualifier[0];

                int count = 0;

                switch (qualifierByte)
                {
                case 0x00:
                    userLevelObject.IndicesPresent     = false;
                    userLevelObject.RangeFieldPresent  = true;
                    userLevelObject.RangePresent       = true;
                    userLevelObject.ObjectCountPresent = false;

                    count = 2;
                    objectHeader.RangeField = new byte[count];
                    for (int i = 0; i < count; i++)
                    {
                        objectHeader.RangeField[i] = data[index++];
                    }

                    length -= count;

                    userLevelObject.StartIndex = objectHeader.RangeField[0];
                    userLevelObject.StopIndex  = objectHeader.RangeField[1];

                    if (functionCode == ApplicationFunctionCodes.RESPONSE ||
                        functionCode == ApplicationFunctionCodes.WRITE)
                    {
                        byte[] value = new byte[valueSize];
                        for (int i = userLevelObject.StartIndex; i <= userLevelObject.StopIndex; i++)
                        {
                            for (int j = 0; j < valueSize; j++)
                            {
                                value[j] = data[index++];
                                --length;
                            }
                        }

                        userLevelObject.Values.Add(value);
                    }

                    break;

                case 0x01:
                    userLevelObject.IndicesPresent     = false;
                    userLevelObject.RangeFieldPresent  = true;
                    userLevelObject.RangePresent       = true;
                    userLevelObject.ObjectCountPresent = false;

                    count = 4;
                    objectHeader.RangeField = new byte[count];
                    for (int i = 0; i < count; i++)
                    {
                        objectHeader.RangeField[i] = data[index++];
                    }

                    length -= count;

                    byte[] tempStart = new byte[2];
                    byte[] tempStop  = new byte[2];
                    for (int i = 0; i < 2; i++)
                    {
                        tempStart[i] = objectHeader.RangeField[i];
                    }
                    for (int i = 0; i < 2; i++)
                    {
                        tempStop[i] = objectHeader.RangeField[i + 2];
                    }

                    short[] start = new short[1];
                    short[] stop  = new short[1];
                    tempStart.CopyTo(start, 0);
                    tempStop.CopyTo(stop, 0);

                    userLevelObject.StartIndex = start[0];
                    userLevelObject.StopIndex  = stop[0];

                    if (functionCode == ApplicationFunctionCodes.RESPONSE ||
                        functionCode == ApplicationFunctionCodes.WRITE)
                    {
                        byte[] value = new byte[valueSize];
                        for (int i = userLevelObject.StartIndex; i < userLevelObject.StopIndex; i++)
                        {
                            for (int j = 0; j < valueSize; j++)
                            {
                                value[j] = data[index++];
                                --length;
                            }
                        }

                        userLevelObject.Values.Add(value);
                    }

                    break;

                case 0x06:
                    userLevelObject.IndicesPresent     = false;
                    userLevelObject.RangeFieldPresent  = false;
                    userLevelObject.RangePresent       = false;
                    userLevelObject.ObjectCountPresent = false;
                    break;

                case 0x07:
                    userLevelObject.IndicesPresent     = false;
                    userLevelObject.ObjectSizePresent  = false;
                    userLevelObject.RangeFieldPresent  = true;
                    userLevelObject.RangePresent       = false;
                    userLevelObject.ObjectCountPresent = true;

                    count = 1;
                    objectHeader.RangeField = new byte[count];
                    for (int i = 0; i < count; i++)
                    {
                        objectHeader.RangeField[i] = data[index++];
                    }

                    length -= count;

                    userLevelObject.ObjectCount = objectHeader.RangeField[0];

                    if (functionCode == ApplicationFunctionCodes.RESPONSE ||
                        functionCode == ApplicationFunctionCodes.WRITE)
                    {
                        byte[] value = new byte[valueSize];
                        for (int i = 0; i < userLevelObject.ObjectCount; i++)
                        {
                            for (int j = 0; j < valueSize; j++)
                            {
                                value[j] = data[index++];
                                --length;
                            }
                        }

                        userLevelObject.Values.Add(value);
                    }

                    break;

                case 0x08:
                    userLevelObject.IndicesPresent     = false;
                    userLevelObject.ObjectSizePresent  = false;
                    userLevelObject.RangeFieldPresent  = true;
                    userLevelObject.RangePresent       = false;
                    userLevelObject.ObjectCountPresent = true;

                    count = 2;
                    objectHeader.RangeField = new byte[count];
                    for (int i = 0; i < count; i++)
                    {
                        objectHeader.RangeField[i] = data[index++];
                    }

                    length -= count;

                    short[] tempCount = new short[1];
                    objectHeader.RangeField.CopyTo(tempCount, 0);

                    userLevelObject.ObjectCount = tempCount[0];

                    if (functionCode == ApplicationFunctionCodes.RESPONSE ||
                        functionCode == ApplicationFunctionCodes.WRITE)
                    {
                        byte[] value = new byte[valueSize];
                        for (int i = 0; i < userLevelObject.ObjectCount; i++)
                        {
                            for (int j = 0; j < valueSize; j++)
                            {
                                value[j] = data[index++];
                                --length;
                            }
                        }

                        userLevelObject.Values.Add(value);
                    }

                    break;

                case 0x17:
                    userLevelObject.IndicesPresent     = true;
                    userLevelObject.ObjectSizePresent  = false;
                    userLevelObject.RangeFieldPresent  = true;
                    userLevelObject.RangePresent       = false;
                    userLevelObject.ObjectCountPresent = true;

                    count = 1;
                    objectHeader.RangeField = new byte[count];
                    for (int i = 0; i < count; i++)
                    {
                        objectHeader.RangeField[i] = data[index++];
                    }

                    length -= count;

                    userLevelObject.ObjectCount = objectHeader.RangeField[0];

                    if (functionCode == ApplicationFunctionCodes.RESPONSE ||
                        functionCode == ApplicationFunctionCodes.WRITE)
                    {
                        byte[] value = new byte[valueSize];
                        for (int i = 0; i < userLevelObject.ObjectCount; i++)
                        {
                            byte[] tempIndex = new byte[1];
                            tempIndex[0] = data[index++];
                            userLevelObject.Indices.Add(BitConverter.ToInt32(tempIndex, 0));
                            length -= 1;
                            for (int j = 0; j < valueSize; j++)
                            {
                                value[j] = data[index++];
                                --length;
                            }
                        }

                        userLevelObject.Values.Add(value);
                    }
                    else
                    {
                        for (int i = 0; i < userLevelObject.ObjectCount; i++)
                        {
                            byte[] tempIndex = new byte[1];
                            tempIndex[0] = data[index++];
                            userLevelObject.Indices.Add(BitConverter.ToInt32(tempIndex, 0));
                            length -= 1;
                        }
                    }

                    break;

                case 0x28:
                    userLevelObject.IndicesPresent     = true;
                    userLevelObject.ObjectSizePresent  = false;
                    userLevelObject.RangeFieldPresent  = true;
                    userLevelObject.RangePresent       = false;
                    userLevelObject.ObjectCountPresent = true;

                    count = 2;
                    objectHeader.RangeField = new byte[count];
                    for (int i = 0; i < count; i++)
                    {
                        objectHeader.RangeField[i] = data[index++];
                    }

                    length -= count;

                    tempCount = new short[1];
                    objectHeader.RangeField.CopyTo(tempCount, 0);

                    userLevelObject.ObjectCount = tempCount[0];

                    if (functionCode == ApplicationFunctionCodes.RESPONSE ||
                        functionCode == ApplicationFunctionCodes.WRITE)
                    {
                        byte[] value = new byte[valueSize];
                        for (int i = 0; i < userLevelObject.ObjectCount; i++)
                        {
                            byte[] tempIndex = new byte[2];
                            tempIndex[0] = data[index++];
                            tempIndex[1] = data[index++];
                            userLevelObject.Indices.Add(BitConverter.ToInt32(tempIndex, 0));
                            length -= 1;
                            for (int j = 0; j < valueSize; j++)
                            {
                                value[j] = data[index++];
                                --length;
                            }
                        }

                        userLevelObject.Values.Add(value);
                    }
                    else
                    {
                        byte[] tempIndex = new byte[2];
                        tempIndex[0] = data[index++];
                        tempIndex[1] = data[index++];
                        userLevelObject.Indices.Add(BitConverter.ToInt32(tempIndex, 0));
                        length -= 2;
                    }

                    break;

                case 0x5b:
                    userLevelObject.IndicesPresent     = false;
                    userLevelObject.ObjectSizePresent  = true;
                    userLevelObject.RangeFieldPresent  = false;
                    userLevelObject.RangePresent       = false;
                    userLevelObject.ObjectCountPresent = false;

                    count = 1;
                    objectHeader.RangeField = new byte[count];
                    for (int i = 0; i < count; i++)
                    {
                        objectHeader.RangeField[i] = data[index++];
                    }

                    break;
                }

                ObjectsUpProcess.Add(userLevelObject);
            }

            if (Fin)
            {
                Fir = false;
                Fin = false;

                List <UserLevelObject> returnVal = new List <UserLevelObject>(ObjectsUpProcess);
                ObjectsUpProcess.Clear();
                return(returnVal);
            }
            else
            {
                return(null);
            }
        }