public static void Create(IEnumerable <IBlock> blocks, string path, PortalPlc parentPlc, WinccExportType exportType, TiaPortalVersion portalVersion)
        {
            if (blocks == null)
            {
                throw new ArgumentNullException(nameof(blocks));
            }
            IEnumerable <DataBlock> dataBlocks;

            if ((dataBlocks = blocks.OfType <DataBlock>())?.Count() <= 0)
            {
                throw new ArgumentException("Blocks does not contain any valid DataBlocks.", nameof(blocks));
            }

            WinccConfiguration.CreateInternal(dataBlocks, path, parentPlc, exportType, portalVersion);
        }
        private static void CreateInternal(IEnumerable <DataBlock> dataBlocks, string path, PortalPlc parentPlc, WinccExportType exportType, TiaPortalVersion portalVersion)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }
            if (!FileHelpers.IsValidFilePath(path, ".xlsx"))
            {
                throw new ArgumentException(path + " is not a valid path.", nameof(path));
            }

            var    currentAlarmRow = 2;
            var    currentTagRow   = 2;
            string dataBlockName;
            var    proTags = new List <string>();

            using (var document = new ExcelPackage())
            {
                var tagWorksheet   = document.Workbook.Worksheets.Add(TagWorksheetName);
                var alarmWorksheet = document.Workbook.Worksheets.Add(AlarmWorksheetName);

                WinccConfiguration.WriteXlsxHeaders(document.Workbook, exportType, portalVersion);

                foreach (var db in dataBlocks)
                {
                    dataBlockName = db.Name;
                    db.CalcluateAddresses(parentPlc);

                    foreach (var entry in db.Children)
                    {
                        ProcessDataEntry(alarmWorksheet, entry, string.Empty, new Address(), db.Name);
                    }

                    if (exportType != WinccExportType.Professional)
                    {
                        WriteComfortAdvancedTagRow(tagWorksheet, db, "HMI_Connection_1");
                    }
                }

                if (exportType == WinccExportType.Professional)
                {
                    foreach (var tag in proTags)
                    {
                        WriteProfessionalTagRow(tagWorksheet, tag, "HMI_Connection_1");
                    }
                }

                using (Stream file = new FileStream(path, FileMode.Create))
                {
                    document.SaveAs(file);
                }
            }

            void ProcessDataEntry(ExcelWorksheet worksheet, DataEntry entry, string prependText, Address addressOffset, string prependTag = "")
            {
                string stackedComment;

                if (string.IsNullOrWhiteSpace(prependText))
                {
                    stackedComment = entry.Comment;
                }
                else if (prependText.EndsWith(" - "))
                {
                    stackedComment = prependText + entry.Comment;
                }
                else
                {
                    stackedComment = prependText + " - " + entry.Comment;
                }

                string stackedTag;

                if (string.IsNullOrWhiteSpace(prependTag))
                {
                    stackedTag = entry.Name;
                }
                else if (prependText.EndsWith("."))
                {
                    stackedTag = prependTag + entry.Name;
                }
                else
                {
                    stackedTag = prependTag + "." + entry.Name;
                }

                switch (entry.DataType)
                {
                case DataType.BOOL:
                    var alarmNumber = currentAlarmRow - 1;

                    var row = WinccConstants.GetAlarmRow(exportType, portalVersion);
                    row[WinccExportField.Id]        = alarmNumber.ToString();
                    row[WinccExportField.Name]      = $"Discrete_alarm_{alarmNumber}";
                    row[WinccExportField.AlarmText] = stackedComment;
                    row[WinccExportField.InfoText]  = stackedComment;

                    if (exportType == WinccExportType.Professional)
                    {
                        row[WinccExportField.TriggerTag] = stackedTag.Replace('.', '_');

                        proTags.Add(stackedTag);
                    }
                    else                             // WinCC Comfort/Advanced
                    {
                        row[WinccExportField.TriggerTag] = dataBlockName;
                        row[WinccExportField.TriggerBit] = AddressToTriggerBit(addressOffset + entry.Address.Value).ToString();
                    }

                    var column = 1;
                    foreach (var item in row)
                    {
                        worksheet.Cells[currentAlarmRow, column].Style.Numberformat.Format = "@";
                        worksheet.Cells[currentAlarmRow, column++].Value = item;
                    }

                    currentAlarmRow++;
                    break;

                case DataType.UDT:
                case DataType.STRUCT:
                    entry.CalcluateAddresses(parentPlc);
                    foreach (var newEntry in entry.Children)
                    {
                        ProcessDataEntry(worksheet, newEntry, stackedComment, addressOffset + entry.Address.Value, stackedTag);
                    }
                    break;

                case DataType.ARRAY:
                    TagHelper.ResolveArrayChildren(entry, parentPlc);

                    // write a new entry for each of the children
                    foreach (var child in entry.Children)
                    {
                        ProcessDataEntry(worksheet, child, prependText, entry.Address.Value + addressOffset, stackedTag);
                    }
                    break;

                default:
                    throw new SiemensException("Unsupported data type for WinCC alarms: " + entry.Name + ", " + entry.DataType.ToString());
                }
            }

            void WriteComfortAdvancedTagRow(ExcelWorksheet worksheet, DataBlock dataBlock, string connectionName)
            {
                string dataType, address;

                var dbLength = dataBlock.CalculateSize(parentPlc).Byte;

                var wordLength = (int)Math.Ceiling(dbLength / 2.0);

                if (dbLength > 2)
                {
                    dataType = $"Array [0..{wordLength - 1}] of Word";
                    address  = $"%DB{dataBlock.Number}.DBX0.0";
                }
                else
                {
                    dataType   = "Word";
                    address    = $"%DB{dataBlock.Number}.DBW0";
                    wordLength = 0;
                }

                var row = WinccConstants.GetTagRow(exportType, portalVersion);

                row[WinccExportField.Name]       = dataBlock.Name;
                row[WinccExportField.Connection] = connectionName;
                row[WinccExportField.DataType]   = dataType;
                row[WinccExportField.Length]     = ((wordLength + 1) * 2).ToString();
                row[WinccExportField.Address]    = address;

                var column = 1;

                foreach (var item in row)
                {
                    worksheet.Cells[currentTagRow, column].Style.Numberformat.Format = "@";
                    worksheet.Cells[currentTagRow, column++].Value = item;
                }

                currentTagRow++;
            }

            void WriteProfessionalTagRow(ExcelWorksheet worksheet, string tag, string connectionName)
            {
                var row = WinccConstants.GetTagRow(exportType, portalVersion);

                row[WinccExportField.Name]       = tag.Replace('.', '_');
                row[WinccExportField.Connection] = connectionName;
                row[WinccExportField.PlcTag]     = tag;

                var column = 1;

                foreach (var item in row)
                {
                    worksheet.Cells[currentTagRow, column].Style.Numberformat.Format = "@";
                    worksheet.Cells[currentTagRow, column++].Value = item;
                }

                currentTagRow++;
            }

            int AddressToTriggerBit(Address address)
            {
                if (address.Byte % 2 == 0)
                {
                    return((address.Byte + 1) * 8 + address.Bit);
                }
                else
                {
                    return((address.Byte - 1) * 8 + address.Bit);
                }
            }
        }
 public static void Create(IBlock block, string path, PortalPlc parentPlc, WinccExportType exportType, TiaPortalVersion portalVersion)
 {
     WinccConfiguration.Create(new[] { block }, path, parentPlc, exportType, portalVersion);
 }