Partials.Cooler = function (params) { var dateBoxValue = ko.observable(new Date((new Date).getTime() + (24 * 60 * 60 * 1000))); var initialized = false; var dataSource; var rawData = ko.observable(); function createDataSource() { return new DevExpress.data.DataSource({ store: Partials.db.BemsDeviceRunHourly, paginate: false, select: ['RunDate', 'Temperature', 'Loads', 'BemsDeviceRunHourlyCombine', 'BemsDeviceRunHourlyCost'], expand: ['BemsDeviceRunHourlyCombine', 'BemsDeviceRunHourlyCost', 'BemsDeviceRunHourlyCombine/BemsDevice', 'BemsDeviceRunHourlyCombine/BemsDevice/BemsDeviceType'], map: function (item) { item.Hour = moment(item.RunDate).format("H"); item.Loads = parseInt(item.Loads); /** * 시설가동 및 온도 그래프를 위해 */ item.Centrífugal = item.BemsDeviceRunHourlyCombine.reduce(function (p, c) { return (c.BemsDevice.DeviceTypeId == 1) ? (c.BemsDevice.Calorie * c.BemsDevice.Capacity) + p : p; }, 0); item.Absorption = item.BemsDeviceRunHourlyCombine.reduce(function (p, c) { return (c.BemsDevice.DeviceTypeId == 2) ? (c.BemsDevice.Calorie * c.BemsDevice.Capacity) + p : p; }, 0); item.Congeal = item.BemsDeviceRunHourlyCombine.reduce(function (p, c) { return (c.BemsDevice.DeviceTypeId == 3) ? (c.BemsDevice.Calorie * c.BemsDevice.Capacity) + p : p; }, 0); item.Thaw = item.BemsDeviceRunHourlyCombine.reduce(function (p, c) { return (c.BemsDevice.DeviceTypeId == 4) ? (c.BemsDevice.Calorie * c.BemsDevice.Capacity) + p : p; }, 0); /** * 생산열량 및 사용금액 그래프를 위해 */ item.Calories = item.Centrífugal + item.Absorption + item.Thaw + item.Congeal; item.Electricity = item.BemsDeviceRunHourlyCost.reduce(function (p, c) { return (c.FuelTypeId === 1) ? parseInt(c.Cost) + p : p; }, 0); item.Gas = item.BemsDeviceRunHourlyCost.reduce(function (p, c) { return (c.FuelTypeId === 2) ? parseInt(c.Cost) + p : p; }, 0); item.Water = item.BemsDeviceRunHourlyCost.reduce(function (p, c) { return (c.FuelTypeId === 3) ? parseInt(c.Cost) + p : p; }, 0); item.Chemical = item.BemsDeviceRunHourlyCost.reduce(function (p, c) { return (c.FuelTypeId === 4) ? parseInt(c.Cost) + p : p; }, 0); return item; }, filter: [ ["SiteId", "=", 1], ["BuildingId", "=", 1], ["ServiceTypeId", "=", 1], ["year(RunDate)", "=", dateBoxValue().getFullYear()], ["month(RunDate)", "=", dateBoxValue().getMonth() + 1], ["day(RunDate)", "=", dateBoxValue().getDate()], ] }); } dateBoxValue.subscribe(function (e) { dataSource = createDataSource(); handleViewShown(); }); function Temperature(result, initial) { $.extend(initial, { cellTemplate: function (e, v) { $("
").text(v.text).appendTo(e); } }); return result.reduce(function (p, c) { var o = new Object(); o[c.Hour] = c.Temperature.toFixed(1); $.extend(p, o); return p; }, initial); } function Loads(result, initial) { $.extend(initial, { cellTemplate: function (e, v) { $("
").text(v.text).appendTo(e); } }); return result.reduce(function (p, c) { var o = new Object(); o[c.Hour] = c.Loads; $.extend(p, o); return p; }, initial); } function Devices(index, result, initial) { $.extend(initial, { cellTemplate: function (e, v) { if (v.value) { $("
").text(v.value.BemsDevice.BemsDeviceType.Name).appendTo(e); $("
").text(v.value.BemsDevice.Name).appendTo(e); $("
").text(Globalize.format(v.value.BemsDevice.Calorie * v.value.BemsDevice.Capacity, "n0")).appendTo(e); } else { $("
").appendTo(e); } } }); return result.reduce(function (p, c) { if (c.BemsDeviceRunHourlyCombine[0]) { var o = new Object(); o[c.Hour] = c.BemsDeviceRunHourlyCombine[index]; $.extend(p, o); } return p; }, initial); } function DevicesSummary(result, initial) { $.extend(initial, { cellTemplate: function (e, v) { if (v.value) { var sum = v.value.reduce(function(prev ,current) { return (current.BemsDevice.DeviceTypeId == 3) ? prev : (current.BemsDevice.Calorie * current.BemsDevice.Capacity) + prev; }, 0 ); $("
").text(Globalize.format(sum)).appendTo(e); } } }); return result.reduce(function (p, c) { if (c.BemsDeviceRunHourlyCombine) { var o = new Object(); o[c.Hour] = c.BemsDeviceRunHourlyCombine; $.extend(p, o); } return p; }, initial); } function Cost(fuelTypeId, result, initial) { $.extend(initial, { cellTemplate: function (e, v) { if (v.value) { $("
").text(Globalize.format(parseInt(v.value.Consume))).appendTo(e); $("
").text(Globalize.format(parseInt(v.value.Cost), 'c')).appendTo(e); } else { $("
").appendTo(e); } } }); return result.reduce(function (p, c) { if (c.BemsDeviceRunHourlyCost) { var cost = c.BemsDeviceRunHourlyCost.filter(function (v) { return v.FuelTypeId === fuelTypeId; })[0]; if (cost) { var o = new Object(); o[c.Hour] = cost; $.extend(p, o); } } return p; }, initial); } function load(result) { rawData(result); var data = new Array(); data.push(Temperature(result, { Group: 0, Item: '온도(℃)' })); data.push(Loads(result, { Group: 0, Item: '부하량(kcal)' })); data.push(Devices(0, result, { Group: 1, Item: '1' })); data.push(Devices(1, result, { Group: 1, Item: '2' })); data.push(Devices(2, result, { Group: 1, Item: '3' })); data.push(Devices(3, result, { Group: 1, Item: '4' })); data.push(DevicesSummary(result, { Group: 1, Item: '합계(kcal)' })); data.push(Cost(1, result, { Group: 2, Item: '전기 (kWh)' })); data.push(Cost(2, result, { Group: 2, Item: '가스 (㎥/h)' })); data.push(Cost(3, result, { Group: 2, Item: '수도 (㎥/h)' })); data.push(Cost(4, result, { Group: 2, Item: '약품 (ℓ)' })); $("#gridContainer").dxDataGrid({ dataSource: data, showBorders: true, showRowLines: true, columnFixing: { enabled: true }, columns: [ { dataField: "Group", caption: '', groupIndex: 0, customizeText: function (cellinfo) { if (cellinfo.value === 0) return '예측'; if (cellinfo.value === 1) return '가동설비 및 생산열량 (kcal)'; if (cellinfo.value === 2) return '에너지 사용량 및 비용'; return ''; } }, { dataField: 'Item', caption: '시간', width: 110, alignment: 'center', fixed: true }, { dataField: '0', caption: '00시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '1', caption: '01시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '2', caption: '02시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '3', caption: '03시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '4', caption: '04시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '5', caption: '05시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '6', caption: '06시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '7', caption: '07시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '8', caption: '08시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '9', caption: '09시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '10', caption: '10시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '11', caption: '11시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '12', caption: '12시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '13', caption: '13시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '14', caption: '14시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '15', caption: '15시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '16', caption: '16시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '17', caption: '17시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '18', caption: '18시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '19', caption: '19시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '20', caption: '20시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '21', caption: '21시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '22', caption: '22시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, { dataField: '23', caption: '23시', width: 140, alignment: 'center', format: 'fixedPoint', cellTemplate: function (e, v) { v.data.cellTemplate(e, v); } }, ] }); } var devicesChartOption = { dataSource: rawData, commonSeriesSettings: { argumentField: "Hour", type: "stackedBar", hoverMode: "allArgumentPoints", selectionMode: "allArgumentPoints", }, argumentAxis: { label: { customizeText: function () { return this.value + '시'; } } }, valueAxis: [ { name: 'valueAxis', label: { format: 'fixedPoint millions', customizeText: function () { return this.valueText + ' kcal'; } } }, { name: 'tempAxis', position: 'right', label: { precision: 0, customizeText: function () { return this.value + ' ℃'; }, }, maxValueMargin: 0.1, minValueMargin: 0.1, min: -10, max: 30 } ], series: [ { name: "흡수식", color: "#70cbed", valueField: "Absorption" }, { name: "원심식", color: "#bad5a4", valueField: "Centrífugal" }, { name: "빙축열(해빙)", color: "#c0222f", valueField: "Thaw" }, { name: "빙축열(생산)", color: "#f2c899", valueField: "Congeal" }, { name: "온도", type: "spline", color: "#9dD5ad", valueField: "Temperature", axis: 'tempAxis' }, ], legend: { verticalAlignment: "top", horizontalAlignment: "right", position: "inside", border: { visible: true } }, onPointClick: function (e) { e.target.select(); }, tooltip: { enabled: true, location: "edge", format: 'fixedPoint', customizeTooltip: function (point) { return point.seriesName + " : " + point.valueText; } } }; var pricesChartOption = { dataSource: rawData, commonSeriesSettings: { argumentField: "Hour", type: "Bar", hoverMode: "allArgumentPoints", selectionMode: "allArgumentPoints", }, argumentAxis: { label: { customizeText: function () { return this.value + '시'; } } }, valueAxis: [ { name: 'valueAxis', label: { format: 'currency', customizeText: function () { return this.valueText; } } }, { name: 'caloriesAxis', position: 'right', label: { precision: 0, format: 'fixedPoint millions', customizeText: function () { return this.valueText + 'kcal'; }, }, maxValueMargin: 0.1, minValueMargin: 1, } ], series: [ { name: "전기", color: "#70cbed", valueField: "Electricity" }, { name: "가스", color: "#bad5a4", valueField: "Gas" }, { name: "수도", color: "#c0222f", valueField: "Water" }, { name: "약품", color: "#f2c899", valueField: "Chemical" }, { name: "생산열량", type: "line", color: "#9da5ad", valueField: "Calories", axis: 'caloriesAxis' }, ], legend: { verticalAlignment: "top", horizontalAlignment: "right", position: "inside", border: { visible: true } }, onPointClick: function (e) { e.target.select(); }, tooltip: { enabled: true, location: "edge", format: 'fixedPoint', customizeTooltip: function (point) { return point.seriesName + " : " + point.valueText; } } }; var operateGridOption = { dataSource: new DevExpress.data.DataSource({ store: Partials.db.BemsDevice, select: ['SiteId', 'BuildingId', 'DeviceId', 'Name', 'BemsDeviceType/Name', 'BemsZone/Name', 'Capacity', 'Calorie', 'IsUse'], expand: ['BemsDeviceType', 'BemsZone'], map: function (item) { return item; }, filter: [ ["SiteId", "=", 1], ["BuildingId", "=", 1], ["DeviceTypeId", "<", 5], ] }), paging: { enabled: false }, showColumnLines: false, showRowLines: true, hoverStateEnabled: true, editing: { editMode: 'batch', editEnabled: true, }, rowUpdating: function (rowInfo) { Partials.db.BemsDevice.update(rowInfo.key, rowInfo.newData); }, columns: [ { dataField: 'Name', caption: '설비명칭', alignment: 'right', allowEditing: false }, { dataField: 'BemsDeviceType.Name', caption: '방식', width: 100, alignment: 'right', allowEditing: false }, { dataField: 'BemsZone.Name', caption: '장소', width: 220, alignment: 'right', allowEditing: false }, { dataField: 'Capacity', caption: '용량(RT)', format: 'fixedPoint', allowEditing: false }, { dataField: 'Calorie', caption: '열량(kcal)', format: 'fixedPoint', allowEditing: false }, { dataField: 'IsUse', caption: '사용', alignment: 'center', allowEditing: true } ] }; var graphViewPopup = { width: 1200, height: 600, showTitle: true, title: ko.computed({ read: function () { return moment(dateBoxValue()).format("YYYY년 MM월 D일"); } }), visible: ko.observable(false), dragEnabled: false, closeOnOutsideClick: false, show: function () { graphViewPopup.visible(true); }, shownAction: function () { } }; var predictPopup = { width: 800, height: 'auto', showTitle: true, weatherDataSource: ko.observable(), zoneDataSource: ko.observable(), buttons: [ { toolbar: 'bottom', location: 'after', widget: 'button', options: { text: '예측', clickAction: function () { var tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); if (moment(tomorrow).format('YYYYMMDD') === moment(dateBoxValue()).format('YYYYMMDD')) { Partials.api.post('ExecuteLoadsHourly', { SiteId: 1, ServiceTypeId:1, CreatedDate: dateBoxValue() }).done(function (result) { viewModel.predictPopup.visible(false); viewModel.viewShown(); }); } else { DevExpress.ui.dialog.alert('익일 부하예측만 가능합니다.', '알림'); } } } } ], title: ko.computed({ read: function () { return moment(dateBoxValue()).format("YYYY년 MM월 D일"); } }), visible: ko.observable(false), dragEnabled: false, closeOnOutsideClick: false, show: function () { predictPopup.visible(true); }, showingAction: function () { predictPopup.weatherDataSource = new DevExpress.data.DataSource({ store: Partials.db.BemsWeatherHourly, paginate: false, map: function (item) { return item; }, filter: [ ["SiteId", "=", 1], ["year(CreatedDate)", "=", dateBoxValue().getFullYear()], ["month(CreatedDate)", "=", dateBoxValue().getMonth() + 1], ["day(CreatedDate)", "=", dateBoxValue().getDate()], ] }); predictPopup.zoneDataSource = new DevExpress.data.DataSource({ store: Partials.db.BemsZoneActivateDaily, expand: ['BemsZoneActivate', 'BemsZoneActivate/BemsZone'], paginate: false, map: function (item) { return item; }, filter: [ ["SiteId", "=", 1], ["BuildingId", "=", 1], ["ServiceTypeId", "=", 1], ["year(CreatedDate)", "=", dateBoxValue().getFullYear()], ["month(CreatedDate)", "=", dateBoxValue().getMonth() + 1], ["day(CreatedDate)", "=", dateBoxValue().getDate()], ] }); }, shownAction: function () { predictPopup.weatherDataSource.load().done(function (result) { var dataGrid = $("#weatherGrid").dxDataGrid("instance"); dataGrid.option({ dataSource: result, editing: { editMode: 'batch', editEnabled: true, }, rowUpdating: function (rowInfo) { var key = new Partials.BemsWeatherHourlyViewModel(rowInfo.key); Partials.db.BemsWeatherHourly.update(key.toJS(), rowInfo.newData); }, columns: [ { dataField: 'CreatedDate', caption: '시간', width: '10%', alignment: 'right', allowEditing: false, customizeText: function (cellInfo) { return moment(cellInfo.value).format("H"); } }, { dataField: 'Temperature', caption: '온도(℃)', format: 'fixedPoint', precision: 1, }, { dataField: 'Humidity', caption: '습도(%)', format: 'fixedPoint', precision: 0, }, { dataField: 'Wind', caption: '풍속(m/s)', format: 'fixedPoint', precision: 2, }, { dataField: 'Rainfall', caption: '강수량(mm)', format: 'fixedPoint', precision: 2, }, ] }); }); predictPopup.zoneDataSource.load().done(function (result) { var dataGrid = $("#zoneGrid").dxDataGrid("instance"); dataGrid.option({ dataSource: result, editing: { editMode: 'batch', editEnabled: true, }, columnFixing: { enabled: true }, rowUpdating: function (rowInfo) { Partials.db.BemsZoneActivateDaily.update(rowInfo.key, rowInfo.newData); }, columns: [ { dataField: 'BemsZoneActivate.BemsZone.Name', caption: '시설명', fixed: true, width: 150, allowEditing: false }, { dataField: 'BemsZoneActivate.Loads', caption: '부하(cal)', fixed: true, format: 'fixedPoint', width: 110, alignment: 'right', allowEditing: false }, { dataField: 'BemsZoneActivate.RateOfWork', caption: '가동율(%)', fixed: true, format: 'fixedPoint', precision: 2, width: 100, alignment: 'right', allowEditing: false }, { dataField: 'H00', caption: '00시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H01', caption: '01시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H02', caption: '02시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H03', caption: '03시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H04', caption: '04시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H05', caption: '05시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H06', caption: '06시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H07', caption: '07시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H08', caption: '08시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H09', caption: '09시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H10', caption: '10시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H11', caption: '11시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H12', caption: '12시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H13', caption: '13시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H14', caption: '14시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H15', caption: '15시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H16', caption: '16시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H17', caption: '17시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H18', caption: '18시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H19', caption: '19시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H20', caption: '20시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H21', caption: '21시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H22', caption: '22시', width: 50, alignment: 'center', dataType: 'boolean', }, { dataField: 'H23', caption: '23시', width: 50, alignment: 'center', dataType: 'boolean', }, ] }); }); } }; var operatePopup = { width: 700, height: 'auto', showTitle: true, title: ko.computed({ read: function () { return moment(dateBoxValue()).format("YYYY년 MM월 D일"); } }), visible: ko.observable(false), dragEnabled: false, closeOnOutsideClick: false, buttons: [ { toolbar: 'bottom', location: 'after', widget: 'button', options: { text: '산출하기', clickAction: function () { var tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); if (moment(tomorrow).format('YYYYMMDD') === moment(dateBoxValue()).format('YYYYMMDD')) { Partials.api.post('OptimalDeviceRunHourly', { SiteId: 1, BuildingId: 1, ServiceTypeId: 1, RunDate: dateBoxValue() }).done(function (result) { viewModel.operatePopup.visible(false); viewModel.viewShown(); }); } else { DevExpress.ui.dialog.alert('익일 최적운전 산출이 가능합니다.', '알림'); } } } } ], show: function () { operatePopup.visible(true); }, shownAction: function () { } }; var handleViewShown = function () { if (initialized == false) { initialized = true; dataSource = createDataSource(); } dataSource.load().done(function (result) { load(result); }); }; var viewModel = { dateBoxValue: dateBoxValue, devicesChartOption: devicesChartOption, pricesChartOption: pricesChartOption, operateGridOption: operateGridOption, viewShown: handleViewShown, graphViewPopup: graphViewPopup, operatePopup: operatePopup, predictPopup: predictPopup, onPrevious: function (e) { var date = new Date(dateBoxValue()); date.setDate(date.getDate() - 1); dateBoxValue(date); }, onNext: function (e) { var date = new Date(dateBoxValue()); date.setDate(date.getDate() + 1) dateBoxValue(date); } }; return viewModel; };