using Dapper;
using Repository.BackendRepository.Interface;
using Repository.Helper;
using System;
using System.Text;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
using Repository.Models;
using System.Data.SqlTypes;
using Microsoft.Extensions.Primitives;
using System.Web;
using Org.BouncyCastle.Utilities.Collections;
using System.Reflection;
using System.Drawing;
using Org.BouncyCastle.Asn1.Mozilla;
using System.IO;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Repository.BackendRepository.Implement
{
    public class NiagaraDataSynchronizeRepository : BackendRepository, INiagaraDataSynchronizeRepository
    {
        private Stopwatch stopwatch = new Stopwatch();
        private Stopwatch stopwatchSection = new Stopwatch();
        public NiagaraDataSynchronizeRepository(IDatabaseHelper databaseHelper) : base(databaseHelper)
        {
        }

        /// <summary>
        /// 更新import_niagara_tag資料表
        /// </summary>
        /// <param name="ds"></param>
        /// <param name="building"></param> 
        /// <returns></returns>
        public async Task InsertNiagaraTagList(List<Device_value> ds, List<string> building, string tag_quantity)
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                StringBuilder sb = new StringBuilder();
                try
                {
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    foreach (var b in building)
                    {
                        string sql = @"CREATE TABLE IF NOT EXISTS `import_niagara_tag` (
                                    `id` int(11) NOT NULL AUTO_INCREMENT,
                                    `db_tags` varchar(50) DEFAULT NULL,
                                    `niagara_tags` varchar(50) DEFAULT NULL,
                                    `device_area_tag` varchar(50) DEFAULT NULL,
                                    `device_building_tag` varchar(50) DEFAULT NULL,
                                    `device_system_tag` varchar(50) DEFAULT NULL,
                                    `device_name_tag` varchar(50) DEFAULT NULL,
                                    `device_floor_tag` varchar(50) DEFAULT NULL,
                                    `device_master_tag` varchar(50) DEFAULT NULL,
                                    `device_last_name_tag` varchar(50) DEFAULT NULL,
                                    `device_serial_tag` varchar(50) DEFAULT NULL,
                                    `atDateTime` datetime(1) DEFAULT NULL,
                                    `device_full_name` varchar(100) DEFAULT NULL,
                                    `is_used` smallint(1) DEFAULT 0,
                                    PRIMARY KEY (`id`)
                                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;";
                        using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                        {
                            await conn.ExecuteAsync(sql);
                        }
                        sql = "delete from import_niagara_tag where device_building_tag = '" + b + "' limit 10000";
                        using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                        {
                            await conn.ExecuteAsync(sql);
                        }
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("Create or delete related building import_niagara_tag", stopwatchSection.ElapsedMilliseconds);

                    //N4資料groupBy後放入import_niagara_tag資料表
                    var ds2 = ds.GroupBy(x => new
                    {
                        tag_name2 = x.tag_name,
                        displayName2 = x.displayName
                    }).Select(x => new Device_value
                    {
                        tag_name = x.Key.tag_name2,
                        displayName = x.Key.displayName2
                    }); 
                       
                    // StringBuilder sb = new StringBuilder();
                    bool isDome = false; //是否為巨蛋案
                    int count  = 0;
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    foreach (var row in ds2)
                    {
                        if (string.IsNullOrEmpty(row.tag_name)) continue;
                        string[] arrTag = row.tag_name.Split('_');

                        if (int.Parse(tag_quantity) == 8)
                        {
                            if (arrTag.Length == 8)
                            {
                                sb.Append($@" insert into import_niagara_tag(niagara_tags, device_area_tag, device_building_tag, device_system_tag,
                                device_name_tag, device_floor_tag, device_master_tag, device_last_name_tag, device_serial_tag, device_full_name, atDateTime) values('" +
                                    row.tag_name + "', '" +   //niagara_tags
                                    arrTag[0] + "', '" +  //device_area_tag
                                    arrTag[1] + "', '" +  //device_building_tag
                                    arrTag[2] + "', '" +  //device_system_tag
                                    arrTag[3] + "', '" +  //device_name_tag
                                    arrTag[4] + "', '" +  //device_floor_tag
                                    arrTag[5] + "', '" +  //device_master_tag
                                    arrTag[6] + "', '" +  //device_last_name_tag
                                    arrTag[7] + "', '" +  //device_serial_tag
                                    row.displayName + "', " + //device_full_name
                                    "now());"); //atDateTime
                            }
                        }
                        else
                        {
                            if (arrTag.Length == 5)
                            {
                                isDome= true;
                                sb.Append($@" insert into import_niagara_tag(niagara_tags, device_area_tag, device_building_tag, device_system_tag,
                                            device_name_tag, device_floor_tag, device_master_tag, device_last_name_tag, device_serial_tag, device_model_tag, device_full_name, atDateTime) values('" +
                                    row.tag_name + "', '" +   //niagara_tags
                                    "', '" +  //device_area_tag
                                    arrTag[0] + "', '" +  //device_building_tag
                                    arrTag[1] + "', '" +  //device_system_tag  巨蛋沒有大類,後面再用關聯的方式補上
                                    arrTag[3] + "', '" +  //device_name_tag
                                    arrTag[2] + "', '" +  //device_floor_tag
                                    "', '" +  //device_master_tag
                                    "', '" +  //device_last_name_tag
                                    arrTag[4] + "', '" +  //device_serial_tag
                                    arrTag[3] + "', '" +  //device_model_tag
                                    row.displayName + "', " + //device_full_name
                                    "now());"); //atDateTime
                                //await conn.ExecuteAsync(sb.ToString());
                                //sb.Clear();
                                count += 1;
                                if (count >= 100)
                                {
                                    count = 0;
                                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                                    {
                                        await conn.ExecuteAsync(sb.ToString());
                                    }
                                    sb.Clear();
                                }
                            }
                        }
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("Insert into import_niagara_tag", stopwatchSection.ElapsedMilliseconds);

                    if (sb.Length > 0)
                    {
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        var temp = sb.ToString();
                        using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                        {
                            await conn.ExecuteAsync(temp);
                        }
                        sb.Clear();
                        stopwatchSection.Stop();
                        await KeepTimeLog("Insert into import_niagara_tag", stopwatchSection.ElapsedMilliseconds);
                    }

                    //巨蛋特殊處理
                    if (isDome)
                    {
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        sb.Append($@" -- tag 第二碼為小類(除了安全系統之外)
                            update import_niagara_tag set device_name_tag = device_system_tag
                            where (device_system_tag <> 'S' and device_system_tag <> 'CWB' and device_system_tag <> 'B' and 
                                    (device_system_tag <> 'FM' or device_system_tag <> 'APS' or device_system_tag <> 'VICL'));

                            update import_niagara_tag a 
                            join variable b on a.device_name_tag COLLATE utf8mb4_general_ci = b.system_value and b.deleted = 0 and b.system_type = 'device_system_category_layer3'
                            join variable v1 on b.system_parent_id = v1.id
                            set device_system_tag = v1.system_value -- tag 第二碼為小類(除了安全系統之外)
                            where (device_system_tag <> 'S' and device_system_tag <> 'CWB' and device_system_tag <> 'B' and 
                                    (device_system_tag <> 'FM' or device_system_tag <> 'APS' or device_system_tag <> 'VICL'));  -- 安全系統 第二段大類 第四段小類");
                        using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                        {
                            await conn.ExecuteAsync(sb.ToString());
                        }
                        stopwatchSection.Stop();
                        await KeepTimeLog("巨蛋特殊處理", stopwatchSection.ElapsedMilliseconds);
                    }
                    stopwatch.Stop();
                    await KeepTimeLog("InsertNiagaraTagList", stopwatch.ElapsedMilliseconds);
                }
                catch (Exception exception)
                {
                    Console.WriteLine(sb.ToString());
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }
            }
        }
        
        /// <summary>
        /// 獲取照明開關 是否在 device_node 層
        /// </summary>
        /// <returns></returns>
        public async Task<string> getLightSwitchLevel()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open(); 
                var sql = "select system_value from variable where system_type = 'module_light_switch' ";
                var module_light_switch = conn.QueryAsync<string>(sql).Result.FirstOrDefault(); 
                conn.Close();
                stopwatch.Stop();
                await KeepTimeLog("getLightSwitchLevel", stopwatch.ElapsedMilliseconds);
                return module_light_switch;
            }
        }

        /// <summary>
        /// 更新import_niagara_tag資料表
        /// </summary>
        /// <param name="ds"></param>
        /// <param name="building"></param>
        /// <returns></returns>
        public async Task InsertItemFromNiagara(List<ImpNiaItem> ds, List<string> building, bool isFirstAsync)
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    #region 刪除 import_niagara_item資料表中選取的棟別
                    if (isFirstAsync)
                    {
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        foreach(var b in building)
                        {
                            string sql = @"CREATE TABLE IF NOT EXISTS `import_niagara_item` (
                                    `id` int(11) NOT NULL AUTO_INCREMENT,
                                    `device_area_tag` varchar(50) DEFAULT NULL,
                                    `device_building_tag` varchar(50) DEFAULT NULL,
                                    `device_system_tag` varchar(50) DEFAULT NULL,
                                    `device_name_tag` varchar(50) DEFAULT NULL,
                                    `device_point_name` varchar(50) DEFAULT NULL,
                                    `check_status` varchar(50) DEFAULT NULL,
                                    `parent_path` varchar(50) DEFAULT NULL,
                                    `full_name` varchar(50) DEFAULT NULL,
                                    PRIMARY KEY (`id`)
                                ) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;";
                            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sql);
                            }
                            sql = "delete from import_niagara_item where device_building_tag = '" + b + "' limit 10000;";
                            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sql);
                            }
                        }
                        stopwatchSection.Stop();
                        await KeepTimeLog("Create or delete related building import_niagara_tag", stopwatchSection.ElapsedMilliseconds);
                    }
                    #endregion

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    ds = ds.GroupBy(x => new {
                        device_area_tag = x.device_area_tag,
                        device_building_tag = x.device_building_tag,
                        device_system_tag = x.device_system_tag,
                        device_name_tag = x.device_name_tag,
                        device_point_name = x.device_point_name,
                        full_name = x.full_name
                    })
                    .Select(x => new ImpNiaItem
                    {
                        device_area_tag = x.Key.device_area_tag,
                        device_building_tag = x.Key.device_building_tag,
                        device_system_tag = x.Key.device_system_tag,
                        device_name_tag = x.Key.device_name_tag,
                        device_point_name = x.Key.device_point_name,
                        full_name = x.Key.full_name
                    }).ToList();

                    StringBuilder sb = new StringBuilder();
                    #region 放入import_niagara_item資料表
                    foreach (var row in ds)
                    {
                        sb.Append($@" insert import_niagara_item(device_area_tag, device_building_tag, device_system_tag, device_name_tag, device_point_name, parent_path, full_name, is_history)
                                    values('"+
                                        row.device_area_tag + "', '" +
                                        row.device_building_tag + "', '" +
                                        row.device_system_tag + "', '" +
                                        row.device_name_tag + "', '" +
                                        row.device_point_name + "', '" +
                                        "" + "', '" +
                                        row.full_name + "'," +
                                        "0" +
                                        ");");

                        if (sb.Length >= 100)
                        {
                            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sb.ToString());
                            }

                            sb.Clear();
                        }
                    }

                    var isDome = await GetOneAsync<string>("select system_value from variable where deleted = 0 and system_type = 'project_name';");
                    if (sb.Length > 0)
                    {
                        using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                        {
                            await conn.ExecuteAsync(sb.ToString());
                        }
                    }

                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("Insert into import_niagara_item", stopwatchSection.ElapsedMilliseconds);

                    if (isDome.Contains("ibms_dome"))
                    {
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        sb.Append($@" update import_niagara_item a 
                                        join variable b on a.device_name_tag COLLATE utf8mb4_0900_ai_ci = b.system_value COLLATE utf8mb4_0900_ai_ci and b.deleted = 0 and b.system_type = 'device_system_category_layer3'
                                        join variable v1 on b.system_parent_id = v1.id
                                    set device_system_tag = v1.system_value
                                    where (device_system_tag <> 'S' and device_system_tag <> 'CWB' and device_system_tag <> 'B');");

                        using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                        {
                            await conn.ExecuteAsync(sb.ToString());
                        }
                        stopwatchSection.Stop();
                        await KeepTimeLog("Insert into import_niagara_item", stopwatchSection.ElapsedMilliseconds);
                    }
                    sb.Clear();

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@" update import_niagara_item set full_name = device_point_name where full_name = '' or full_name is null;");
                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("update device point name where full name is empty or null", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    await RecoverSysTag("import_niagara_item");
                    stopwatchSection.Stop();
                    await KeepTimeLog("update import_niagara_item system tag", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@"SET SQL_SAFE_UPDATES = 0;
                                update import_niagara_item ini
                                join (
	                                select device_building_tag, device_system_tag, device_name_tag, device_point_name
	                                from import_niagara_item_history 
	                                group by device_building_tag, device_system_tag, device_name_tag, device_point_name
                                ) as inih on ini.device_building_tag = inih.device_building_tag and ini.device_system_tag = inih.device_system_tag and ini.device_name_tag = inih.device_name_tag
                                and ini.device_point_name = inih.device_point_name
                                set ini.is_history = 1");

                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("update device_item history", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@"SET SQL_SAFE_UPDATES = 0;
                                update import_niagara_item ini
                                join (
	                                select distinct device_building_tag, parent_path from import_niagara_item_history
                                ) inih on ini.device_building_tag = inih.device_building_tag
                                set ini.parent_path = inih.parent_path");

                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("update device_item parent_path", stopwatchSection.ElapsedMilliseconds);

                    stopwatch.Stop();
                    await KeepTimeLog("InsertItemFromNiagara", stopwatch.ElapsedMilliseconds);
                    #endregion
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }
                
            }
        }

        /// <summary>
        /// 比對 device
        /// </summary>
        /// <returns></returns>
        public async Task DeviceComparison(string LightSwitchLevel)
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    List<NiagaraTags> result;
                    StringBuilder sb = new StringBuilder();
                    StringBuilder sb2 = new StringBuilder();
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@"SELECT m.*
                                FROM import_niagara_tag m
                                WHERE NOT EXISTS (
                                    SELECT 1
                                    FROM device d
                                    WHERE m.niagara_tags = d.device_number
                                );");
                    result = (await conn.QueryAsync<NiagaraTags>(sb.ToString())).ToList<NiagaraTags>();
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("filter device item from import_niagara_tag", stopwatchSection.ElapsedMilliseconds);

                    #region device, device_kind 新增
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    int count = 0;
                    //新增至device, is_link = 1
                    if (result.Count > 0)
                    {
                        var result2 = result.GroupBy(x => new
                        {
                            device_building_tag2 = x.device_building_tag,
                            device_system_tag2 = x.device_system_tag,
                            device_name_tag2 = x.device_name_tag
                        }).Select(x => new Device_item8
                        {
                            device_building_tag = x.Key.device_building_tag2,
                            device_system_tag = x.Key.device_system_tag2,
                            device_name_tag = x.Key.device_name_tag2
                        });

                        foreach (var data in result)
                        {
                            //開關控制在個別燈具(device_node層) and 小類為二線式照明系統 and tag第八段開頭不是 G
                            if (LightSwitchLevel == "node" && data.device_name_tag == "L1" && data.device_serial_tag.Substring(0, 1) != "G")
                            {
                                // 燈具新增 device_node
                                continue;
                            }
                            else { 
                                    sb.Append($@" insert device(device_guid, deleted, status, priority, is_link, device_area_tag, 
                                    device_building_tag, device_system_tag, device_name_tag, full_name, device_floor_tag, device_master, 
                                    device_last_name, device_serial_tag, device_model_tag, device_number, device_system_category_layer3, visible, created_at, updated_at)
                                    values(uuid(), 0, 1, 0, 1, '" +
                                        data.device_area_tag + "', '" +
                                        data.device_building_tag + "', '" +
                                        data.device_system_tag + "', '" +
                                        data.device_name_tag + "', '" +
                                        data.device_full_name + "', '" + 
                                        data.device_floor_tag + "', '" +
                                        data.device_master_tag + "', '" +
                                        data.device_last_name_tag + "', '" +
                                        data.device_serial_tag + "', '" +
                                        data.device_model_tag + "', '" +
                                        data.niagara_tags + "', '" +
                                        data.device_system_tag + "', 1, now(), now() );");
                                count += 1;
                                if (count >= 100)
                                {
                                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                                    {
                                        await conn.ExecuteAsync(sb.ToString());
                                    }
                                    sb.Clear();
                                    count = 0;
                                }
                            }                               
                        }

                        if (sb.Length > 0)
                        {
                            using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sb.ToString());
                            }

                            sb.Clear();
                        }
                        stopwatchSection.Stop();
                        await KeepTimeLog("insert into device", stopwatchSection.ElapsedMilliseconds);

                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        foreach (var data in result2)
                        {
                            var sqlString = new StringBuilder();
                            sqlString.Append("select * from device_kind where device_building_tag = '" + data.device_building_tag + "' and device_system_tag = '" + data.device_system_tag + "' and device_name_tag = '" + data.device_name_tag + "'");
                            var dk = (await conn.QueryAsync<device_kind>(sqlString.ToString())).ToList<device_kind>();

                            if (dk.Count == 0)
                            {
                                sb2.Append($@"INSERT device_kind (device_kind_guid, device_building_tag, device_system_tag, device_name_tag, 
                                    device_normal_flashing, device_close_flashing, device_error_flashing, device_error_independent, 
                                    created_by, created_at, is_link)
                                    VALUES (uuid(), '" + data.device_building_tag + "', '" + data.device_system_tag + "', '" + data.device_name_tag +
                                            "', 0, 0, 1, 0, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), 1);");
                                count += 1;
                                if (count >= 100)
                                {
                                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                                    {
                                        await conn.ExecuteAsync(sb2.ToString());
                                    }
                                    sb2.Clear();
                                    count = 0;
                                }
                            }
                        }

                        if (sb2.Length > 0)
                        {
                            using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sb2.ToString());
                            }

                            sb2.Clear();
                        }
                        stopwatchSection.Stop();
                        await KeepTimeLog("insert into device_kind", stopwatchSection.ElapsedMilliseconds);
                    }
                    #endregion

                    #region device_kind process for cctv
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(@"SET SQL_SAFE_UPDATES = 0;
                                                        update device d join import_niagara_tag n on d.device_number = n.niagara_tags set d.device_model_tag = n.device_model_tag
                                                        where d.device_system_tag = 'S' and d.device_name_tag = 'C' and (d.device_model_tag is null or d.device_model_tag = '');");
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("update cctv model is null or empty", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    var resultDeviceItem = await conn.QueryAsync<NiagaraTags>($@"select * from import_niagara_tag where device_system_tag = 'S' and device_name_tag = 'C';");
                    var result3 = resultDeviceItem.GroupBy(x => new
                    {
                        device_building_tag2 = x.device_building_tag,
                        device_system_tag2 = x.device_system_tag,
                        device_name_tag2 = x.device_name_tag,
                        device_model_tag2 = x.device_model_tag
                    }).Select(x => new Device_item8
                    {
                        device_building_tag = x.Key.device_building_tag2,
                        device_system_tag = x.Key.device_system_tag2,
                        device_name_tag = x.Key.device_name_tag2,
                        device_model_tag = x.Key.device_model_tag2
                    });

                    foreach (var data in result3)
                    {
                        var sqlString = new StringBuilder();
                        sqlString.Append("select * from device_kind where device_building_tag = '" + data.device_building_tag + "' and device_system_tag = '" + data.device_system_tag + "' and device_name_tag = '" + data.device_name_tag + "' and device_model_tag = '" + data.device_model_tag + "'");
                        var dk = (await conn.QueryAsync<device_kind>(sqlString.ToString())).ToList<device_kind>();

                        if (dk.Count == 0)
                        {
                            sb2.Append($@"INSERT device_kind (device_kind_guid, device_building_tag, device_system_tag, device_name_tag, 
                                    device_normal_flashing, device_close_flashing, device_error_flashing, device_error_independent, 
                                    created_by, created_at, device_model_tag, is_link)
                                    VALUES (uuid(), '" + data.device_building_tag + "', '" + data.device_system_tag + "', '" + data.device_name_tag +
                                        "', 0, 0, 1, 0, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), '" + data.device_model_tag + "', 1);");
                            count += 1;
                            if (count >= 100)
                            {
                                using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                                {
                                    await conn.ExecuteAsync(sb2.ToString());
                                }
                                sb2.Clear();
                                count = 0;
                            }
                        }
                    }

                    if (sb2.Length > 0)
                    {
                        using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                        {
                            await conn.ExecuteAsync(sb2.ToString());
                        }

                        sb2.Clear();
                    }

                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync($@"SET SQL_SAFE_UPDATES = 0;
                                                UPDATE device_kind dk
                                                left JOIN (
		                                                SELECT device_system_tag, device_name_tag, device_model_tag
		                                                FROM import_niagara_tag
		                                                LIMIT 100000
                                                ) i ON dk.device_system_tag = i.device_system_tag 
                                                and dk.device_name_tag = i.device_name_tag
                                                and dk.device_model_tag = i.device_model_tag
                                                SET dk.is_link = 0
                                                WHERE dk.is_link = 1 and dk.device_system_tag = 'S' and dk.device_name_tag = 'C' and i.device_system_tag is null;");
                    }

                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync($@"SET SQL_SAFE_UPDATES = 0;
                                                UPDATE device_kind dk
                                                left JOIN (
		                                                SELECT device_system_tag, device_name_tag, device_model_tag
		                                                FROM import_niagara_tag
		                                                LIMIT 100000
                                                ) i ON dk.device_system_tag = i.device_system_tag 
                                                and dk.device_name_tag = i.device_name_tag
                                                and dk.device_model_tag = i.device_model_tag
                                                SET dk.is_link = 1
                                                WHERE dk.is_link = 0 and dk.device_system_tag = 'S' and dk.device_name_tag = 'C' and i.device_system_tag is not null;");
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("insert/update cctv model is_link", stopwatchSection.ElapsedMilliseconds);
                    #endregion

                    #region device_node 新增 
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@" SELECT m.*
                                    FROM import_niagara_tag m
                                    WHERE NOT EXISTS (
                                        SELECT 1
                                        FROM device_node d
                                        WHERE m.niagara_tags COLLATE utf8mb4_0900_ai_ci = d.device_number COLLATE utf8mb4_0900_ai_ci
                                    );");
                    result = (await conn.QueryAsync<NiagaraTags>(sb.ToString())).ToList<NiagaraTags>();
                    sb.Clear();

                    if (result.Count > 0)
                    {
                        var result2 = result.GroupBy(x => new
                        {
                            device_building_tag2 = x.device_building_tag,
                            device_system_tag2 = x.device_system_tag,
                            device_name_tag2 = x.device_name_tag
                        }).Select(x => new Device_item8
                        {
                            device_building_tag = x.Key.device_building_tag2,
                            device_system_tag = x.Key.device_system_tag2,
                            device_name_tag = x.Key.device_name_tag2
                        });

                        count = 0;
                        foreach (var data in result)
                        {
                            //開關控制在個別燈具(device_node層) and 小類為二線式照明系統 and tag第八段開頭不是 G
                            if (LightSwitchLevel == "node" && data.device_name_tag == "L1" && data.device_serial_tag.Substring(0, 1) != "G")
                            {
                                // 燈具新增 device_node
                                sb.Append($@"INSERT INTO  device_node(device_node_guid, deleted, device_guid, device_number, full_name, created_by, created_at, updated_at) 
                                    VALUES (uuid(), 0, '' " + //device_guid 父層需要 forge 那邊提供
                                        ",'" + data.niagara_tags + // device_number
                                    "', '" + data.device_full_name + //full_name
                                    "','B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), now());");
                                count += 1;
                                if (count >= 100)
                                {
                                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                                    {
                                        await conn.ExecuteAsync(sb.ToString());
                                    }
                                    sb.Clear();
                                    count = 0;
                                }
                            }
                        }

                        if (sb.Length > 0)
                        {
                            using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sb.ToString()); 
                            }
                            sb.Clear(); 
                        }
                        stopwatchSection.Stop();
                        await KeepTimeLog("insert into device_node", stopwatchSection.ElapsedMilliseconds);
                    }
                    #endregion

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    //device有,niagara有,is_link 更新成 1
                    sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                        UPDATE device d
                        JOIN (
		                        SELECT niagara_tags
		                        FROM import_niagara_tag
		                        LIMIT 100000
                        ) i ON d.device_number = i.niagara_tags 
                        SET d.is_link = 1, d.deleted = 0");
                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("update device is_link = 1, deleted = 0", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    //device有,niagara沒有,is_link 更新成 0
                    sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                        UPDATE device d
                        left JOIN (
		                        SELECT niagara_tags
		                        FROM import_niagara_tag
		                        LIMIT 100000
                        ) i ON d.device_number = i.niagara_tags 
                        SET d.is_link = 0
                        WHERE d.is_link = 1 and i.niagara_tags is null;");
                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("update device is_link = 0", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    // device_node 有, niagara沒有, is_link 更新成 0
                    sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                            UPDATE device_node dn
                            left JOIN (
                                SELECT niagara_tags
                                FROM import_niagara_tag
                                LIMIT 100000
                            ) i ON dn.device_number COLLATE utf8mb4_0900_ai_ci = i.niagara_tags COLLATE utf8mb4_0900_ai_ci
                            SET dn.is_link = 0 
                            WHERE dn.is_link = 1 and i.niagara_tags is null;");

                    using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("update device_node is_link = 0", stopwatchSection.ElapsedMilliseconds);

                    stopwatch.Stop();
                    await KeepTimeLog("DeviceComparison", stopwatch.ElapsedMilliseconds);
                }
                catch (Exception exception)
                {
                    //throw exception;
                    throw;
                }
                finally
                {
                    conn.Close();
                }
                
            }
        }

        /// <summary>
        /// 比對 device_item
        /// </summary>
        /// <returns></returns>
        public async Task DeviceItemComparison()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    List<NiagaraTagsForItem> result;
                    StringBuilder sb = new StringBuilder();
                    sb.Append($@" SELECT m.device_point_name, m.is_history, m.device_building_tag, m.device_system_tag, m.device_name_tag, m.parent_path, m.full_name
                                    FROM import_niagara_item m
                                    left JOIN (
	                                    select device_building_tag, device_system_tag, device_name_tag, points from device_item where deleted = 0
                                    ) d
                                    ON m.device_system_tag = d.device_system_tag 
	                                    and m.device_name_tag = d.device_name_tag 
                                        and m.device_point_name = d.points 
                                        and m.device_building_tag = d.device_building_tag
                                    where d.points is null;");
                    result = (await conn.QueryAsync<NiagaraTagsForItem>(sb.ToString())).ToList<NiagaraTagsForItem>();
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("filter device_item from import_niagara_item", stopwatchSection.ElapsedMilliseconds);

                    #region 新增至device, is_link = 1
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    if (result.Count > 0)
                    {
                        foreach (var data in result)
                        {
                            var isControll = 0;
                            var isBool = 0;
                            if (data.device_point_name == "ER" || data.device_point_name == "AL" || data.device_point_name == "ST")
                            {
                                isControll = 1;
                            }
                            if (data.device_point_name == "ER" || data.device_point_name == "ST")
                            {
                                isBool = 1;
                            }
                            sb.Append($@"insert device_item(deleted, points, is_show, is_show_riserDiagram, is_controll, is_bool, is_show_history, is_link, 
                                device_system_tag, device_name_tag, device_building_tag, full_name, parent_path, created_at, updated_at)
                                VALUES (0, '" +
                                        data.device_point_name + "', 1, 0, " +
                                        isControll + "," +
                                        isBool + "," +
                                        data.is_history +
                                        ", 1, '" +
                                        data.device_system_tag + "', '" +
                                        data.device_name_tag + "', '" +
                                        data.device_building_tag + "', '" +
                                        data.full_name + "', '" +
                                        data.parent_path + "', " +
                                        "now(), now());");
                        }
                        if (sb.Length > 0)
                        {
                            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sb.ToString());
                            }
                            sb.Clear();
                        }
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("insert into device_item", stopwatchSection.ElapsedMilliseconds);
                    #endregion

                    //device有,niagara沒有,is_link 更新成 0
                    //sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                    //      UPDATE device_item d LEFT JOIN import_niagara_item m 
                    //      ON d.device_system_tag = m.device_system_tag and d.device_name_tag = m.device_name_tag and d.points = m.device_point_name
                    //      SET d.is_link = 0, d.is_show_history = 0
                    //      WHERE m.device_point_name IS NULL");

                    //本次有匯入的 isLink 改為 1, 沒有的 isLink = 0
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                            UPDATE device_item d
                            LEFT JOIN (
                                SELECT device_system_tag, device_name_tag, device_point_name, full_name, device_building_tag
                                FROM import_niagara_item
                                LIMIT 100000 -- Specify your desired limit here
                            ) m
                            ON d.device_system_tag = m.device_system_tag
                            AND d.device_name_tag = m.device_name_tag
                            AND d.points = m.device_point_name
                            AND d.device_building_tag = m.device_building_tag
                            SET d.is_link = CASE WHEN m.device_point_name IS NULL THEN 0 ELSE 1 END;");

                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("update device_item is_link", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                            UPDATE device_item d
                            LEFT JOIN (
                                SELECT m.device_system_tag, m.device_name_tag, m.device_point_name, m.full_name, device_building_tag
                                FROM import_niagara_item m
                                WHERE (m.is_history = 1)
                                LIMIT 100000 -- Specify your desired limit here
                            ) AS subquery
                            ON d.device_system_tag = subquery.device_system_tag
                            AND d.device_name_tag = subquery.device_name_tag
                            AND d.points = subquery.device_point_name
                            AND d.device_building_tag = subquery.device_building_tag
                            SET d.is_show_history = CASE WHEN subquery.device_point_name IS NULL THEN 0 ELSE 1 END;");

                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("update device_item is_show_history", stopwatchSection.ElapsedMilliseconds);

                    stopwatch.Stop();
                    await KeepTimeLog("DeviceItemComparison", stopwatch.ElapsedMilliseconds);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }
            }
        }

        /// <summary>
        /// 新增資料至 buildingMenu
        /// </summary>
        /// <returns></returns>
        public async Task InsertBuildingMenu()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    List<NiagaraTags> result;
                    StringBuilder sb = new StringBuilder();
                    #region comparison building_menu and import_niagara_tag
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@" select a.* from (
                            select  device_building_tag, device_system_tag, device_name_tag
                            from import_niagara_tag
                            group by device_building_tag, device_system_tag, device_name_tag 
                            ) AS a
                            LEFT JOIN building_menu b
                            ON a.device_building_tag COLLATE utf8mb4_0900_ai_ci = b.device_building_tag COLLATE utf8mb4_0900_ai_ci and 
                            a.device_system_tag COLLATE utf8mb4_0900_ai_ci = b.device_system_tag COLLATE utf8mb4_0900_ai_ci and 
                            a.device_name_tag COLLATE utf8mb4_0900_ai_ci = b.sub_system_tag COLLATE utf8mb4_0900_ai_ci
                            WHERE b.device_building_tag IS NULL");
                    result = (await conn.QueryAsync<NiagaraTags>(sb.ToString())).ToList<NiagaraTags>();
                    #endregion
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("filter building_menu from import_niagara_tag", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    if (result.Count > 0)
                    {
                        foreach (var data in result)
                        {
                            StringBuilder sqlString = new StringBuilder();
                            sqlString.Append(@"select * from building_menu where building_tag = '" + data.device_building_tag + "' and main_system_tag = '" + data.device_system_tag + "' and sub_system_tag = '" + data.device_name_tag + "'");
                            var bm = (await conn.QueryAsync<building_menu>(sqlString.ToString())).ToList<building_menu>();

                            #region insert building_menu
                            if (bm.Count == 0)
                            {
                                sb.Append(@$"insert building_menu(building_tag, main_system_tag, sub_system_tag, device_building_tag, device_system_tag,
                                left_drawing, left_icon_click, left_icon_click_url_width, left_icon_click_url_height, left_planimetric_click,
                                is_link, created_by, created_at, updated_by, updated_at)
                                values('{data.device_building_tag}', '{data.device_system_tag}', '{data.device_name_tag}', '{data.device_building_tag}', '{data.device_system_tag}',
                                        4, 1, 0, 0, 1,                                            
                                        1, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now());");
                            }
                            #endregion
                        }
                        if (sb.Length > 0)
                        {
                            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sb.ToString());
                            }
                            sb.Clear();
                        }
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("insert into building_menu", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    //building_menu有,import_niagara_tag沒有,is_link 更新成 0
                    sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                            UPDATE building_menu b LEFT JOIN (
                            select  device_building_tag, device_system_tag, device_name_tag
                            from import_niagara_tag
                            group by device_building_tag, device_system_tag, device_name_tag
                            ) AS a ON b.building_tag COLLATE utf8mb4_0900_ai_ci = a.device_building_tag COLLATE utf8mb4_0900_ai_ci
                            and a.device_system_tag COLLATE utf8mb4_0900_ai_ci = b.main_system_tag COLLATE utf8mb4_0900_ai_ci and a.device_name_tag COLLATE utf8mb4_0900_ai_ci = b.sub_system_tag COLLATE utf8mb4_0900_ai_ci
                            SET b.is_link = 0
                            WHERE a.device_building_tag IS NULL and b.is_link < 2");
                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("update building_menu is_link = 0", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@"SET SQL_SAFE_UPDATES = 0;
                            UPDATE building_menu b LEFT JOIN (
                            select  device_building_tag, device_system_tag, device_name_tag
                            from import_niagara_tag
                            group by device_building_tag, device_system_tag, device_name_tag
                            ) AS a ON b.building_tag COLLATE utf8mb4_0900_ai_ci = a.device_building_tag COLLATE utf8mb4_0900_ai_ci
                            and a.device_system_tag COLLATE utf8mb4_0900_ai_ci = b.main_system_tag COLLATE utf8mb4_0900_ai_ci and a.device_name_tag COLLATE utf8mb4_0900_ai_ci = b.sub_system_tag COLLATE utf8mb4_0900_ai_ci
                            SET b.is_link = 1
                            WHERE a.device_building_tag IS NOT NULL and b.is_link < 2");

                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("update building_menu is_link = 1", stopwatchSection.ElapsedMilliseconds);

                    stopwatch.Stop();
                    await KeepTimeLog("InsertBuildingMenu", stopwatch.ElapsedMilliseconds);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }
            }
        }

        /// <summary>
        /// 新增資料至 subSystemFloor
        /// </summary>
        /// <returns></returns>
        public async Task InsertSubSystemFloor()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    List<NiagaraTags> result;
                    StringBuilder sb = new StringBuilder();
                    #region comparison sub_system_floor and import_niagara_tag
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@" select a.* from (
                            select  device_building_tag, device_system_tag, device_name_tag, device_floor_tag
                            from import_niagara_tag
                            group by device_building_tag, device_system_tag, device_name_tag, device_floor_tag
                            ) AS a
                            LEFT JOIN sub_system_floor b
                            ON a.device_building_tag COLLATE utf8mb4_0900_ai_ci = b.building_tag COLLATE utf8mb4_0900_ai_ci and 
                                a.device_system_tag COLLATE utf8mb4_0900_ai_ci = b.main_system_tag COLLATE utf8mb4_0900_ai_ci and 
                                a.device_name_tag COLLATE utf8mb4_0900_ai_ci = b.sub_system_tag COLLATE utf8mb4_0900_ai_ci and 
                                a.device_floor_tag COLLATE utf8mb4_0900_ai_ci = b.floor_tag COLLATE utf8mb4_0900_ai_ci
                            WHERE b.building_tag IS NULL");
                    result = (await conn.QueryAsync<NiagaraTags>(sb.ToString())).ToList<NiagaraTags>();
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("filter subsystem floor from import_niagara_tag", stopwatchSection.ElapsedMilliseconds);
                    #endregion

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    if (result.Count > 0)
                    {
                        foreach (var data in result)
                        {
                            #region insert building_menu
                            sb.Append(@"insert sub_system_floor(building_tag, main_system_tag, sub_system_tag, floor_tag,  
                                is_link, created_by, created_at, updated_by, updated_at)
                                VALUES ('" +
                                        data.device_building_tag + "', '" +
                                        data.device_system_tag + "', '" +
                                        data.device_name_tag + "', '" +
                                        data.device_floor_tag + "', " +
                                        "1, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now());");
                            #endregion
                        }
                        if (sb.Length > 0)
                        {
                            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sb.ToString());
                            }
                            sb.Clear();
                        }
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("insert into subsystem floor", stopwatchSection.ElapsedMilliseconds);

                    //building_menu有,import_niagara_tag沒有,is_link 更新成 0
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                            UPDATE sub_system_floor b LEFT JOIN (
                            select  device_building_tag, device_system_tag, device_name_tag, device_floor_tag
                            from import_niagara_tag
                            group by device_building_tag, device_system_tag, device_name_tag, device_floor_tag
                            ) AS a ON b.building_tag COLLATE utf8mb4_0900_ai_ci = a.device_building_tag COLLATE utf8mb4_0900_ai_ci
                            and b.main_system_tag COLLATE utf8mb4_0900_ai_ci = a.device_system_tag COLLATE utf8mb4_0900_ai_ci 
                            and b.sub_system_tag COLLATE utf8mb4_0900_ai_ci = a.device_name_tag COLLATE utf8mb4_0900_ai_ci 
                            and b.floor_tag COLLATE utf8mb4_0900_ai_ci = a.device_floor_tag COLLATE utf8mb4_0900_ai_ci
                            SET b.is_link = 0
                            WHERE a.device_building_tag IS NULL");

                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    sb.Clear();
                    stopwatchSection.Stop();
                    await KeepTimeLog("update subsystem floor is_link = 0", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sb.Append($@" SET SQL_SAFE_UPDATES = 0;
                            UPDATE sub_system_floor b LEFT JOIN (
                            select  device_building_tag, device_system_tag, device_name_tag, device_floor_tag
                            from import_niagara_tag
                            group by device_building_tag, device_system_tag, device_name_tag, device_floor_tag
                            ) AS a ON b.building_tag COLLATE utf8mb4_0900_ai_ci = a.device_building_tag COLLATE utf8mb4_0900_ai_ci
                            and b.main_system_tag COLLATE utf8mb4_0900_ai_ci = a.device_system_tag COLLATE utf8mb4_0900_ai_ci 
                            and b.sub_system_tag COLLATE utf8mb4_0900_ai_ci = a.device_name_tag COLLATE utf8mb4_0900_ai_ci 
                            and b.floor_tag COLLATE utf8mb4_0900_ai_ci = a.device_floor_tag COLLATE utf8mb4_0900_ai_ci
                            SET b.is_link = 1
                            WHERE a.device_building_tag IS NOT NULL");

                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("update subsystem floor is_link = 1", stopwatchSection.ElapsedMilliseconds);

                    stopwatch.Stop();
                    await KeepTimeLog("InsertSubSystemFloor", stopwatch.ElapsedMilliseconds);
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {  
                    conn.Close();
                }
            }
        }
        /// <summary>
        /// 檢查device內FullName為空的值,以device_Name取代
        /// </summary>
        /// <returns></returns>
        public async Task CheckFullNameEmptyReplaceByDeviceName()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    try
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("update device set full_name=device_number where full_Name='';");
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
            stopwatch.Stop();
            await KeepTimeLog("CheckFullNameEmptyReplaceByDeviceName", stopwatch.ElapsedMilliseconds);
        }

        /// <summary>
        /// 檢查device_item內FullName為空的值,以points取代
        /// </summary>
        /// <returns></returns>
        public async Task ItemCheckFullNameEmptyReplaceByDeviceName()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            //檢查是否有空值
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    try
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("update device_item set full_name = points where full_Name = '''''';");
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
            stopwatch.Stop();
            await KeepTimeLog("ItemCheckFullNameEmptyReplaceByDeviceName", stopwatch.ElapsedMilliseconds);
        }

        /// <summary>
        /// 檢查device的full_name與niagara device_full_name不同者 覆蓋
        /// </summary>
        /// <returns></returns>
        public async Task CheckDiffFullNameAndCover()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled, new TimeSpan (0, 0, 200)))
                {
                    try
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append(@"SET SQL_SAFE_UPDATES = 0;
                                    update device d
                                    JOIN ( 
	                                    select niagara_tags, device_full_name from import_niagara_tag limit 100000
                                    ) m ON m.niagara_tags  = d.device_number 
                                    LEFT JOIN (
		                                    SELECT niagara_tags, device_full_name
		                                    FROM import_niagara_tag
		                                    LIMIT 100000
                                    ) m2 ON m2.niagara_tags = d.device_number AND m2.device_full_name = d.full_name
                                    SET d.full_name = m.device_full_name
                                    WHERE m2.niagara_tags IS NULL;");
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
            stopwatch.Stop();
            await KeepTimeLog("CheckDiffFullNameAndCover", stopwatch.ElapsedMilliseconds);
        }


        /// <summary>
        /// 比對 device_item full_name 與 import_niagara_item full_name是否相同
        /// 不同則覆蓋
        /// </summary>
        /// <returns></returns>
        public async Task CheckItemDiffFullNameAndCover()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            //檢查
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    try
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append($@"UPDATE device_item d
                                    INNER JOIN (
		                                    SELECT m.device_system_tag, m.device_name_tag, m.device_point_name, m.full_name
		                                    FROM import_niagara_item m
		                                    join device_item d 
		                                    LIMIT 100000 -- Specify your desired limit here
                                    ) AS subquery
                                    ON d.device_system_tag = subquery.device_system_tag
                                    AND d.device_name_tag = subquery.device_name_tag
                                    AND d.points = subquery.device_point_name
                                    SET d.full_name = subquery.full_name
                                    WHERE d.full_name <> subquery.full_name");
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
            stopwatch.Stop();
            await KeepTimeLog("CheckItemDiffFullNameAndCover", stopwatch.ElapsedMilliseconds);
        }

        /// <summary>
        /// 先將device_item 的is_show_history全部關掉
        /// 根據niagara輸入資料來決定要開哪些點
        /// </summary>
        /// <returns></returns>
        public async Task CheckItemIsShowHistory()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            //先將所有is_show_history關閉,niagara有的才開啟
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    StringBuilder sb = new StringBuilder();
                    //先全部關閉
                    sb.Append("update device_item set is_show_history=0;");
                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }

                    sb.Clear();

                    sb.Append($@"SET SQL_SAFE_UPDATES = 0;
                                update device_item di 
                                JOIN (
                                    select distinct device_system_tag, device_name_tag, device_point_name, full_name, device_building_tag from import_niagara_item where is_history = 1 limit 100000
                                ) niagara
                                ON di.device_system_tag = niagara.device_system_tag
                                    AND di.device_name_tag = niagara.device_name_tag
                                    AND di.points = niagara.device_point_name
                                    AND di.full_name = niagara.full_name
                                    AND di.device_building_tag = niagara.device_building_tag
                                set di.is_show_history = 1");
                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sb.ToString());
                    }
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }
            }
            stopwatch.Stop();
            await KeepTimeLog("getLightSwitchLevel", stopwatch.ElapsedMilliseconds);
        }

        public async Task DeviceDisasterAysnc(List<Device_value_disaster> dv)
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                StringBuilder sb = new StringBuilder();
                List<DeviceDisasterOutput> deviceDisaster = new List<DeviceDisasterOutput>();
                List<DeviceDisasterOutput> device = new List<DeviceDisasterOutput>();
                using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
                {
                    try
                    {
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        sb.Append("select * from device_disaster;");
                        deviceDisaster = (await conn.QueryAsync<DeviceDisasterOutput>(sb.ToString())).ToList();
                        sb.Clear();
                        stopwatchSection.Stop();
                        await KeepTimeLog("select all device_disaster", stopwatchSection.ElapsedMilliseconds);

                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        sb.Append("select * from device where deleted = 0;");
                        device = (await conn.QueryAsync<DeviceDisasterOutput>(sb.ToString())).ToList();
                        stopwatchSection.Stop();
                        await KeepTimeLog("select all device", stopwatchSection.ElapsedMilliseconds);
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }

                stopwatchSection = new Stopwatch();
                stopwatchSection.Start();
                dv = dv.Where(x => device.Any(d => d.device_number == x.value.Split('/')[6])).ToList();
                sb.Clear();
                var updateList = dv.Where(x => deviceDisaster.Any(dd => dd.device_number == x.value.Split('/')[6] && dd.device_system_value != x.disasterValue)).ToList();
                foreach (var d in updateList)
                {
                    string device_number = d.value.Split('/')[6];
                    sb.Append($"update device_disaster set device_system_value = '{d.disasterValue}' where device_number = '{device_number}'; ");
                }

                var insertList = dv.Where(x => !deviceDisaster.Any(dd => dd.device_number == x.value.Split('/')[6])).ToList();
                foreach (var d in insertList)
                {
                    string device_number = d.value.Split('/')[6];
                    string device_guid = device.Where(x => x.device_number == device_number).Select(x => x.device_guid).FirstOrDefault();
                    string device_system_tag = device.Where(x => x.device_number == device_number).Select(x => x.device_system_tag).FirstOrDefault();
                    string device_name_tag = device.Where(x => x.device_number == device_number).Select(x => x.device_name_tag).FirstOrDefault();
                    var check = (await conn.QueryAsync<string>($"select device_guid from device_disaster where device_guid = '{device_guid}' and device_system_value = '{d.disasterValue}'")).FirstOrDefault();
                    if (string.IsNullOrEmpty(check))
                    {
                        sb.Append($@"insert into device_disaster 
                                    (device_guid, device_system_value, device_building_tag, device_system_tag, device_name_tag, device_floor_tag, device_serial_tag, device_number)
                                    values ('{device_guid}', '{d.disasterValue}', '{device_number.Split('_')[0]}', '{device_system_tag}', '{device_name_tag}', '{device_number.Split('_')[2]}',
                                    '{device_number.Split('_')[4]}', '{device_number}'); ");

                        await conn.ExecuteAsync(sb.ToString());
                        sb.Clear();
                    }
                }

                using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    try
                    {
                        if (!string.IsNullOrEmpty(sb.ToString()))
                            await conn.ExecuteAsync(sb.ToString());
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
                stopwatchSection.Stop();
                await KeepTimeLog("update or insert into device_disaster", stopwatchSection.ElapsedMilliseconds);

                stopwatch.Stop();
                await KeepTimeLog("DeviceDisasterAysnc", stopwatch.ElapsedMilliseconds);
            }
        }

        /// <summary>
        /// InsertFloor
        /// </summary>
        /// <returns></returns>
        public async Task InsertFloor()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    try
                    {
                        List<NiagaraTags> result;
                        StringBuilder sb = new StringBuilder();
                        #region comparison floor and sub_system_floor
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        sb.Append($@" select tag.*
                                        from (
	                                        select  device_building_tag, device_floor_tag
	                                        from import_niagara_tag
	                                        group by device_building_tag, device_floor_tag
                                        ) AS tag
                                        LEFT JOIN floor f
                                        ON tag.device_building_tag COLLATE utf8mb4_0900_ai_ci = f.building_tag COLLATE utf8mb4_0900_ai_ci 
                                            and tag.device_floor_tag COLLATE utf8mb4_0900_ai_ci = f.full_name COLLATE utf8mb4_0900_ai_ci 
                                            and f.deleted = 0
                                        WHERE f.building_tag IS NULL;");
                        result = (await conn.QueryAsync<NiagaraTags>(sb.ToString())).ToList<NiagaraTags>();
                        sb.Clear();
                        stopwatchSection.Stop();
                        await KeepTimeLog("filter floor from import_niagara_tag", stopwatchSection.ElapsedMilliseconds);
                        #endregion

                        #region insert floor
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        if (result.Count > 0)
                        {
                            Dictionary<string, int> floorPriority = new Dictionary<string, int>();
                            foreach (var data in result)
                            {
                                FloorPriority fp = (await conn.QueryAsync<FloorPriority>($@"select building_tag, max(priority) as priority from floor 
                                            where building_tag = '{data.device_building_tag}' and deleted = 0 group by building_tag;")).FirstOrDefault();
                                int priority = 0;

                                if (fp == null && floorPriority.GetValueOrDefault(data.device_building_tag) == 0)
                                    floorPriority.Add(data.device_building_tag, 0);
                                else if (fp != null && floorPriority.GetValueOrDefault(fp.building_tag) == 0)
                                    floorPriority.Add(fp.building_tag, fp.Priority);

                                priority = ++floorPriority[data.device_building_tag];

                                sb.Append($@"insert into floor(floor_guid, building_tag, deleted, status, full_name, priority, created_by, created_at)
                                    VALUES (uuid(), '{data.device_building_tag}', 0, 1, '{data.device_floor_tag}', {priority}, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now());");
                            }
                            if (sb.Length > 0)
                            {
                                await conn.ExecuteAsync(sb.ToString());
                                sb.Clear();
                            }
                        }
                        stopwatchSection.Stop();
                        await KeepTimeLog("insert into floor", stopwatchSection.ElapsedMilliseconds);
                        #endregion

                        #region update unuse floor with niagara data
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        var floor = (await conn.QueryAsync<BuildFloor>($@"select f1.*
                                                from floor as f1
                                                left join (
	                                                select f.floor_guid
	                                                from (
		                                                select  device_building_tag, device_floor_tag
		                                                from import_niagara_tag
		                                                group by device_building_tag, device_floor_tag
	                                                ) AS tag
	                                                LEFT JOIN floor f
	                                                ON tag.device_building_tag COLLATE utf8mb4_0900_ai_ci = f.building_tag COLLATE utf8mb4_0900_ai_ci 
                                                        and tag.device_floor_tag COLLATE utf8mb4_0900_ai_ci = f.full_name COLLATE utf8mb4_0900_ai_ci 
                                                        and f.deleted = 0
                                                ) as f2 on f1.floor_guid = f2.floor_guid
                                                where f2.floor_guid is null and f1.deleted = 0;")).ToList<BuildFloor>();

                        if (floor.Count > 0)
                        {
                            sb.Clear();
                            foreach (var f in floor)
                            {
                                sb.Append($"update floor set deleted = 1 where floor_guid = '{f.Floor_guid}';");
                            }

                            await conn.ExecuteAsync(sb.ToString());
                        }
                        stopwatchSection.Stop();
                        await KeepTimeLog("update floor deleted = 1", stopwatchSection.ElapsedMilliseconds);

                        stopwatch.Stop();
                        await KeepTimeLog("InsertFloor", stopwatch.ElapsedMilliseconds);
                        #endregion
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
        }

        public async Task InsertItemFromNiagara_History(List<ImpNiaItem> ds, List<string> building)
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                try
                {
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    //改成每次都新增 
                    string sql = @"SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for import_niagara_item_history
-- ----------------------------
DROP TABLE IF EXISTS `import_niagara_item_history`;
CREATE TABLE `import_niagara_item_history`  (
`id` int(11) NOT NULL AUTO_INCREMENT,
`device_area_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`device_building_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`device_system_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`device_name_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`device_floor_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`device_master_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`device_last_name_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`device_serial_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`device_point_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`parent_path` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`is_history` bit(1) NULL DEFAULT b'0',
`full_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`check_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 271 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;";
                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sql);
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("recreate import_niagara_item_history", stopwatchSection.ElapsedMilliseconds);

                    #region 刪除 import_niagara_item資料表中選取的棟別
                    //foreach (var b in building)
                    //{

                    //    sql = "delete from import_niagara_item_history where device_building_tag = '" + b + "'";
                    //    await conn.ExecuteAsync(sql);
                    //}
                    #endregion

                    StringBuilder sb = new StringBuilder();
                    int count = 0;
                    #region 放入import_niagara_item資料表
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    string ss = string.Empty;
                    foreach (var row in ds)
                    {
                        if (count == 0)
                        {
                            ss = @"insert import_niagara_item_history(device_area_tag, device_building_tag, device_system_tag, device_name_tag, device_floor_tag,  device_master_tag,
                                    device_last_name_tag, device_serial_tag, device_point_name, parent_path, full_name, is_history, created_at) values";
                        }

                        ss += $@" ('" +
                                row.device_area_tag + "', '" +
                                row.device_building_tag + "', '" +
                                row.device_system_tag + "', '" +
                                row.device_name_tag + "', '" +
                                row.device_floor_tag + "', '" +
                                row.device_master_tag + "', '" +
                                row.device_last_name_tag + "', '" +
                                row.device_serial_tag + "', '" +
                                row.device_point_name + "', '" +
                                row.parent_path + "', '" +
                                row.full_name + "'," +
                                row.isHistory + ", now() " +
                                ")";

                        count++;
                        if (count == 100)
                        {
                            ss += ";";
                            sb.Append(ss);
                            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                            {
                                await conn.ExecuteAsync(sb.ToString());
                            }
                            sb.Clear();
                            ss = string.Empty;
                            count = 0;
                        }
                        else
                            ss += ",";
                    }

                    sb.Append(ss);
                    if (sb.Length > 0)
                    {
                        sb.Clear();
                        ss = ss.Remove(ss.Length - 1);
                        ss += ";";
                        sb.Append(ss);
                        using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                        {
                            await conn.ExecuteAsync(sb.ToString());
                        }
                        sb.Clear();
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("insert into import_niagara_item_history", stopwatchSection.ElapsedMilliseconds);
                    #endregion
                }
                catch (Exception exception)
                {
                    throw exception;
                }
                finally
                {
                    conn.Close();
                }
            }
            stopwatch.Stop();
            await KeepTimeLog("InsertItemFromNiagara_History", stopwatch.ElapsedMilliseconds);
        }

        /// <summary>
        /// Recover device_system_tag
        /// </summary>
        /// <returns></returns>
        public async Task RecoverSysTag(string tableName)
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            //檢查
            using (IDbConnection conn = GetDbConnection())
            {
                conn.Open();
                using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                {
                    try
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append($@"SET SQL_SAFE_UPDATES = 0;
                                    update {tableName} a
                                    join variable v2 on v2.system_value = a.device_name_tag and v2.deleted = 0 and v2.system_type = 'device_system_category_layer3'
                                    join variable v1 on v1.id = v2.system_parent_id 
                                    set a.device_system_tag = v1.system_value
                                    where a.device_system_tag is null or a.device_system_tag = '';");
                        await conn.ExecuteAsync(sb.ToString());
                    }
                    catch (Exception exception)
                    {
                        throw exception;
                    }
                    finally
                    {
                        conn.Close();
                    }
                }
            }
            stopwatch.Stop();
            await KeepTimeLog("RecoverSysTag", stopwatch.ElapsedMilliseconds);
        }

        /// <summary>
        /// record all device control point
        /// </summary>
        /// <param name="dcp"></param>
        /// <returns></returns>
        public async Task DeviceControlPoint (List<DeviceControlPoint> dcp)
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
            using (IDbConnection conn = GetDbConnection())
            {
                try
                {
                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    string sql = @"CREATE TABLE IF NOT EXISTS `import_niagara_device_control_point` (
                                    `id` int(11) NOT NULL AUTO_INCREMENT,
                                    `name` varchar(1000) DEFAULT NULL,
                                    `displayName` varchar(50) DEFAULT NULL,
                                    `device_area_tag` varchar(50) DEFAULT NULL,
                                    `device_building_tag` varchar(50) DEFAULT NULL,
                                    `device_system_tag` varchar(50) DEFAULT NULL,
                                    `device_name_tag` varchar(50) DEFAULT NULL,
                                    `device_floor_tag` varchar(50) DEFAULT NULL,
                                    `device_number` varchar(50) DEFAULT NULL,
                                    `device_point_name` varchar(50) DEFAULT NULL,
                                    PRIMARY KEY (`id`)
                                ) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;";
                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sql);
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("check and create import_niagara_device_control_point", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    sql = @"truncate table import_niagara_device_control_point";
                    using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
                    {
                        await conn.ExecuteAsync(sql);
                    }
                    stopwatchSection.Stop();
                    await KeepTimeLog("truncate import_niagara_device_control_point", stopwatchSection.ElapsedMilliseconds);

                    stopwatchSection = new Stopwatch();
                    stopwatchSection.Start();
                    StringBuilder sb = new StringBuilder();
                    int count = 0;
                    sql = string.Empty;
                    foreach (var data in dcp)
                    {
                        if (count == 0)
                        {
                            sql = @"insert into import_niagara_device_control_point(name, displayName, device_area_tag, device_building_tag, device_system_tag, device_name_tag, device_floor_tag,
                                    device_number, device_point_name) values";
                        }
                        sql += $@"  ('{data.name}', '{data.displayName}', '{data.device_area_tag}', '{data.device_building_tag}', '{data.device_system_tag}', '{data.device_name_tag}', 
                                                '{data.device_floor_tag}', '{data.device_number}', '{data.device_point_name}')";
                        count++;
                        if (count.Equals(100))
                        {
                            sql += ";";
                            sb.Append(sql);
                            await conn.ExecuteAsync(sb.ToString());
                            sb.Clear();
                            sql = string.Empty;
                            count = 0;
                        }
                        else
                            sql += ",";
                    }

                    sb.Append(sql);
                    if (sb.Length > 0)
                    {
                        sb.Clear();
                        sql = sql.Remove(sql.Length - 1);
                        sql += ";";
                        sb.Append(sql);
                        await conn.ExecuteAsync(sb.ToString());
                        sb.Clear();
                    }

                    var isDome = await conn.QueryFirstAsync<string>("select system_value from variable where system_type = 'project_name'");
                    //巨蛋特殊處理
                    if (!string.IsNullOrEmpty(isDome) && isDome.Contains("ibms_dome"))
                    {
                        stopwatchSection = new Stopwatch();
                        stopwatchSection.Start();
                        sb.Clear();
                        // tag 第二碼為小類(除了安全系統之外)安全系統 第二段大類 第四段小類
                        sb.Append($@"update import_niagara_device_control_point a 
                                    join variable b on a.device_name_tag = b.system_value COLLATE utf8mb4_0900_ai_ci and b.deleted = 0 and b.system_type = 'device_system_category_layer3'
                                    join variable v1 on b.system_parent_id = v1.id
                                    set a.device_system_tag = v1.system_value
                                    where a.device_system_tag not in ('S', 'CWB', 'B') and not (a.device_system_tag in ('FM', 'APS', 'VICL'));");
                        await conn.ExecuteAsync(sb.ToString());
                        stopwatchSection.Stop();
                        await KeepTimeLog("巨蛋特殊處理", stopwatchSection.ElapsedMilliseconds);
                    }

                    stopwatchSection.Stop();
                    await KeepTimeLog("insert into import_niagara_device_control_point", stopwatchSection.ElapsedMilliseconds);
                }
                catch (Exception e)
                {
                    throw e;
                }
                finally
                {
                    conn.Close();
                }
            }
            stopwatch.Stop();
            await KeepTimeLog("DeviceControlPoint", stopwatch.ElapsedMilliseconds);
        }

        public async Task KeepTimeLog(string functionName, long millisecond, [CallerLineNumber] int line = 0, [CallerMemberName] string name = "")
        {
            string path = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Logs");
            string filepath = System.IO.Path.Combine(path, "SyncLog" + DateTime.Now.ToString("yyyyMMdd") + ".txt");

            using (FileStream fs = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write))
            {
                StreamWriter sw = new StreamWriter(fs);
                sw.BaseStream.Seek(0, SeekOrigin.End);
                sw.WriteLine($"Log current time {DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")}, Log function name: {functionName}, Log function usage time: {TimeSpan.FromMilliseconds(millisecond)}, Log function record line: {line}, Log called function name: {name}");
                sw.WriteLine(Environment.NewLine);
                sw.Flush();
                sw.Close();
                fs.Close();
            }
        }
    }
}