/// <summary>
        /// DT_PARENT_CHILD_CONNECTテーブルからDtParentChildConnectを取得する
        /// </summary>
        /// <returns>取得したデータ</returns>
        public IEnumerable <DtParentChildConnect> ReadDtParentChildConnect()
        {
            IEnumerable <DtParentChildConnect> models = null;

            try
            {
                _logger.Enter();

                _dbPolly.Execute(() =>
                {
                    using (DBAccessor.Models.RmsDbContext db = new DBAccessor.Models.RmsDbContext(_appSettings))
                    {
                        if (!db.DtParentChildConnect.Any())
                        {
                            // 親子間通信データテーブルにデータがない場合は正常系
                            models = new List <DtParentChildConnect>();
                        }
                        else
                        {
                            // インベントリデータテーブルは更新ではなく新規追加なので端末SID毎に最新のデータを取得して結合する
                            var inventoryLatests = db.DtInventory.GroupBy(x => x.DeviceSid).Select(y => y.OrderByDescending(z => z.CreateDatetime).FirstOrDefault());

                            var joinEntities = db.DtParentChildConnect
                                               .Join(
                                db.DtDevice,
                                x => x.ChildDeviceSid,
                                y => y.Sid,
                                (parentChildConnet, device) => new
                            {
                                ParentChildConnet         = parentChildConnet,
                                DeviceTableSid            = device.Sid,
                                DeviceTableEquipmentUid   = device.EquipmentUid,
                                DeviceTableInstallTypeSid = device.InstallTypeSid
                            })
                                               .Join(
                                db.MtInstallType,
                                x => x.DeviceTableInstallTypeSid,
                                y => y.Sid,
                                (joinTable, installType) => new
                            {
                                joinTable.ParentChildConnet,
                                joinTable.DeviceTableSid,
                                joinTable.DeviceTableEquipmentUid,
                                InstallTypeTableCode = installType.Code,
                            })
                                               .Join(
                                inventoryLatests,
                                x => x.ParentChildConnet.Sid,
                                y => y.DeviceSid,
                                (joinTable, inventory) => new
                            {
                                joinTable.ParentChildConnet,
                                joinTable.DeviceTableEquipmentUid,
                                joinTable.InstallTypeTableCode,
                                InventoryDetailInfo = inventory.DetailInfo
                            }).ToList();

                            if (!joinEntities.Any())
                            {
                                throw new RmsException("JoinによりDT_PARENT_CHILD_CONNECTテーブルのデータ抽出数が0になりました。");
                            }
                            else
                            {
                                models = new List <DtParentChildConnect>();

                                foreach (var entity in joinEntities)
                                {
                                    var detailInfo = JsonConvert.DeserializeObject <JObject>(entity.InventoryDetailInfo);

                                    // アラーム判定・アラーム情報の生成に必要な属性のみ取得
                                    var model = new DtParentChildConnect
                                    {
                                        EquipmentUid      = entity.DeviceTableEquipmentUid,
                                        TypeCode          = entity.InstallTypeTableCode,
                                        TemperatureSensor = (string)detailInfo[Utility.Const.InventoryAlSoftwareName][Utility.Const.InventorySettingInfoName][Utility.Const.InventoryOptionName][Utility.Const.InventoryTemperatureSensorName],
                                        Dxa = (string)detailInfo[Utility.Const.InventoryAlSoftwareName][Utility.Const.InventorySettingInfoName][Utility.Const.InventoryOptionName][Utility.Const.InventoryDxaName],
                                        ParentLastConnectDatetime = entity.ParentChildConnet.ParentLastConnectDatetime,
                                        ChildLastConnectDatetime  = entity.ParentChildConnet.ChildLastConnectDatetime
                                    };
                                    models.Append(model);
                                }
                            }
                        }
                    }
                });

                return(models);
            }
            catch (Exception e)
            {
                throw new RmsException("DT_PARENT_CHILD_CONNECTテーブルのSelectに失敗しました。", e);
            }
            finally
            {
                _logger.LeaveJson("{0}", models);
            }
        }
        /// <summary>
        /// DT_PARENT_CHILD_CONNECTテーブルにメッセージを追加またはメッセージの内容で更新処理を行う(親フラグ=trueの場合)
        /// </summary>
        /// <param name="inData">更新データ</param>
        /// <returns>追加または更新したデータ。「確認日時」が既存レコードより古く更新しなかった場合には、nullを返す</returns>
        public DtParentChildConnect Save(DtParentChildConnectFromParent inData)
        {
            DtParentChildConnect model = null;

            try
            {
                _dbPolly.Execute(() =>
                {
                    using (DBAccessor.Models.RmsDbContext db = new DBAccessor.Models.RmsDbContext(_appSettings))
                        using (var tran = db.Database.BeginTransaction())
                        {
                            // 現在時刻を取得
                            var now = _timePrivder.Now;

                            // メッセージクラス→Modelクラス変換時に、通信が成功だった場合は最終接続日時にデータが格納される
                            // ここで初期値だった場合には通信失敗と判断できる
                            if (inData.ParentLastConnectDatetime == null || inData.ParentLastConnectDatetime == default(DateTime))
                            {
                                inData.ParentLastConnectDatetime = now;
                            }

                            // DB格納データクラス
                            DBAccessor.Models.DtParentChildConnect entity = CreateEntityFromParent(inData, db);

                            // 親機器 - 子機器 - 親機器確認結果の組み合わせをチェックする
                            // 3つの組み合わせでレコードを一意に決めることができる
                            var sids = CreateSqlParameterSet(entity.ParentDeviceSid, entity.ChildDeviceSid);
                            DBAccessor.Models.DtParentChildConnect targetRecord
                                = db.DtParentChildConnect.FromSql(SelectParentSqlCommand, sids).AsNoTracking().FirstOrDefault();

                            if (targetRecord == null || targetRecord.Sid == 0)
                            {
                                // 作成日時+更新日時
                                // 通常はSaveChangesにTimeProvideを渡し、DBが自動的に作成日時と更新日時を設定する。
                                // しかし親子間通信データについては、
                                // 通信失敗のケースにおいて最終更新日時にはレコード作成日時を格納する
                                // (nullを回避しつつ、通信失敗ケースであったことを後から判断できるようにするため)。
                                // DBが自動的に時刻を設定した場合に、リポジトリ内で取得した時刻と時間差が発生する可能性があるため、
                                // リポジトリ内で明示的に時刻を取得してDBにレコードを挿入する。
                                entity.CreateDatetime = now;
                                entity.UpdateDatetime = now;

                                // レコード追加
                                var dbdata = db.DtParentChildConnect.Add(entity).Entity;

                                // 最終日時更新データと作成日時・更新日時を一致させたいので、TimeProviderは渡さずに明示的な日時データを使う
                                db.SaveChanges();
                                model = dbdata.ToModel();
                            }
                            else
                            {
                                // データの時刻チェック: データがDBに格納されたデータよりも新しい場合のみ更新処理を行う
                                // DBよりも古い確認時刻だった場合には更新せずにnullを返す
                                DateTime?dbTime = targetRecord.ParentConfirmDatetime;

                                if (dbTime == null || inData.ParentConfirmDatetime.Value.CompareTo(dbTime.Value) > 0)
                                {
                                    // 更新対象のレコードのSIDを設定
                                    entity.Sid = targetRecord.Sid;

                                    db.DtParentChildConnect.Attach(entity);

                                    // 更新のあったデータのみを更新する
                                    db.Entry(entity).Property(x => x.ParentConfirmDatetime).IsModified = true;
                                    db.Entry(entity).Property(x => x.ParentResult).IsModified          = true;
                                    if (entity.ParentResult != null && entity.ParentResult.Value)
                                    {
                                        db.Entry(entity).Property(x => x.ParentLastConnectDatetime).IsModified = true;
                                    }

                                    db.SaveChanges(_timePrivder);
                                    model = entity.ToModel();
                                }
                            }

                            // トランザクション終了
                            tran.Commit();
                        }
                });

                return(model);
            }
            catch (ValidationException e)
            {
                throw new RmsParameterException(e.ValidationResult.ErrorMessage, e);
            }
            catch (Exception e)
            {
                throw new RmsException("DT_PARENT_CHILD_CONNECTテーブルの更新処理に失敗しました。", e);
            }
            finally
            {
                _logger.LeaveJson("{0}", model);
            }
        }
        /// <summary>
        /// アラーム生成対象データを作成する
        /// </summary>
        /// <param name="alarmJudgementTargets">アラーム判定対象データ</param>
        /// <param name="alarmCreationTargets">アラーム生成対象データ</param>
        /// <returns>成功した場合true、失敗した場合falseを返す</returns>
        public bool CreateAlarmCreationTarget(IReadOnlyCollection <Tuple <DtParentChildConnect, DtAlarmDefConnectionMonitor> > alarmJudgementTargets, out List <Tuple <DtParentChildConnect, DtAlarmDefConnectionMonitor> > alarmCreationTargets)
        {
            bool result = true;

            _logger.EnterJson("{0}", new { alarmJudgementTargets });

            alarmCreationTargets = new List <Tuple <DtParentChildConnect, DtAlarmDefConnectionMonitor> >();

            foreach (var judgementTarget in alarmJudgementTargets)
            {
                try
                {
                    (var parentChildConnect, var alarmDef) = judgementTarget;

                    DateTime?lastConnectDateTime = null;

                    var compResult = Nullable.Compare <DateTime>(parentChildConnect.ParentLastConnectDatetime, parentChildConnect.ChildLastConnectDatetime);

                    // 親子の最終通信日時の新しい方を取得
                    if (compResult == 1)
                    {
                        lastConnectDateTime = parentChildConnect.ParentLastConnectDatetime;
                    }
                    else if (compResult == -1)
                    {
                        lastConnectDateTime = parentChildConnect.ChildLastConnectDatetime;
                    }
                    else
                    {
                        lastConnectDateTime = parentChildConnect.ParentLastConnectDatetime;
                    }

                    if (lastConnectDateTime == null)
                    {
                        // Dispatacherで親子どちらかの最終通信日時に必ず値を設定するのでともにnullという事はあり得ない
                        throw new RmsException("LastConnectDateTime is null");
                    }
                    else
                    {
                        TimeSpan disconnectedTime  = _timeProvider.UtcNow - (DateTime)lastConnectDateTime;
                        int      disconnectionDays = disconnectedTime.Days;

                        if (JudgeAlarm(disconnectionDays, alarmDef))
                        {
                            var alarmTargetParentChildConnect = new DtParentChildConnect();
                            alarmTargetParentChildConnect = parentChildConnect;
                            alarmTargetParentChildConnect.DisconnectionDays   = disconnectionDays;
                            alarmTargetParentChildConnect.LastConnectDateTime = lastConnectDateTime;
                            alarmTargetParentChildConnect.AlarmJudgementTime  = _timeProvider.UtcNow.ToString();

                            var alarmTarget = new Tuple <DtParentChildConnect, DtAlarmDefConnectionMonitor>(alarmTargetParentChildConnect, alarmDef);
                            alarmCreationTargets.Add(alarmTarget);
                        }
                    }
                }
                catch (Exception e)
                {
                    // アラーム生成エラー(基本設計書 5.2.1.1 エラー処理)
                    _logger.Error(e, nameof(Resources.UT_PCM_PCM_005), new object[] { judgementTarget?.Item1?.Sid });
                    result = false;
                }
            }

            _logger.LeaveJson("{0}", new { alarmCreationTargets });
            return(result);
        }