Kanggu Park il y a 4 ans
commit
d4f535cdcb
4 fichiers modifiés avec 2681 ajouts et 0 suppressions
  1. 5 0
      Config.ini
  2. 433 0
      RealTimeDataAccumulator.py
  3. 1604 0
      RealTimeSimulator_HeatStorageSystem.py
  4. 639 0
      RealTimeSimulator_Load_Forecasting.py

+ 5 - 0
Config.ini

@@ -0,0 +1,5 @@
+[LocalDB_Info]
+ip_address = 192.168.100.159
+user_id = sa
+user_password = icontrols123.,
+db_name = iBems_Forecasting

+ 433 - 0
RealTimeDataAccumulator.py

@@ -0,0 +1,433 @@
+# # Data accumulator 
+# 
+# 참고 : https://signing.tistory.com/22
+# 공공데이터포털 : https://www.data.go.kr/
+# 본 파일은 동네예보 조회서비스 중 동네예보조회에 대한 내용임
+# 공공데이터포털에서 제공하는 동네예보 조회서비스 API는 최근 1일까지의 데이터만 제공하고 있음
+
+from urllib.request import urlopen
+from urllib.parse import urlencode, unquote, quote_plus
+from datetime import datetime, timedelta
+import urllib
+import requests
+import json
+import pandas as pd
+import pymssql
+import configparser
+import time
+import numpy as np
+
+def get_WF_Temperature_Humidity_info(data):
+	HumWF=[]
+	TempWF=[]
+	try: 
+		weather_info = data['response']['body']['items']['item'] 
+		for i in range(len(weather_info)):
+			if weather_info[i]['category'] == 'T3H':
+				TempWF.append([weather_info[i]['baseDate'], weather_info[i]['baseTime'], weather_info[i]['fcstDate'], weather_info[i]['fcstTime'],weather_info[i]['fcstValue']])
+			elif weather_info[i]['category'] == 'REH':
+				HumWF.append([weather_info[i]['baseDate'], weather_info[i]['baseTime'], weather_info[i]['fcstDate'], weather_info[i]['fcstTime'],weather_info[i]['fcstValue']])
+		return TempWF, HumWF
+	except KeyError:
+		print('API 호출 실패! (Weather forecast data)')
+
+def get_base_time(hour): 
+    hour = int(hour)
+    if hour < 3: 
+        temp_hour = '20' 
+    elif hour < 6: 
+        temp_hour = '23' 
+    elif hour < 9: 
+        temp_hour = '02' 
+    elif hour < 12: 
+        temp_hour = '05' 
+    elif hour < 15: 
+        temp_hour = '08'
+    elif hour < 18:
+        temp_hour = '11' 
+    elif hour < 20: 
+        temp_hour = '14' 
+    elif hour < 24: 
+        temp_hour = '17' 
+    return temp_hour + '00'
+
+def get_weather_forecast(n_x, n_y): 
+	now = datetime.now()
+	now_date = now.strftime('%Y%m%d')
+	now_hour = int(now.strftime('%H'))
+	if now_hour < 6: 
+		base_date = str(int(now_date) - 1)
+	else: 
+		base_date = now_date
+	base_hour = get_base_time(now_hour)
+
+	num_of_rows = '90'
+	base_date = base_date
+	base_time = base_hour
+#	base_date = '20200622'
+#	base_time = '1700'
+	# 해당 지역에 맞는 죄표 입력
+		
+	# Setting for URL parsing
+	CallBackURL = 'http://apis.data.go.kr/1360000/VilageFcstInfoService/getVilageFcst'		# 맨 마지막 명칭에 따라 상세기능에 대한 정보가 변경될 수 있음
+																							# getUltraSrtNcst: 초단기실황조회, getUltraSrtFcst: 초단기예보조회, getVilageFcst: 동네예보조회, getFcstVersion: 예보버전조회
+	params = '?' + urlencode({
+		quote_plus("serviceKey"): "sOVGUogWTbCCmzCn10iCEI0pb9VqKHfiBv8PKYnxJLdz4n63U2uSO5Y2TDjS3lez%2BMNT1TVaH4sCkgsctj2xVg%3D%3D",  # 인증키 (2년마다 갱신 필요)  # 반드시 본인이 신청한 인증키를 입력해야함 (IP 불일치로 인한 오류 발생 가능)
+		quote_plus("numOfRows"): num_of_rows,          # 한 페이지 결과 수 // default : 10
+		quote_plus("pageNo"): "1",              # 페이지 번호 // default : 1
+		quote_plus("dataType"): "JSON",         # 응답자료형식 : XML, JSON
+		quote_plus("base_date"): base_date,    # 발표일자 // yyyymmdd
+		quote_plus("base_time"): base_time,        # 발표시각 // HHMM, 매 시각 40분 이후 호출
+		quote_plus("nx"): n_x,                # 예보지점 X 좌표
+		quote_plus("ny"): n_y                 # 예보지점 Y 좌표
+	})
+	# URL parsing
+	req = urllib.request.Request(CallBackURL + unquote(params))
+
+	# Get Data from API
+	response_body = urlopen(req).read() # get bytes data
+
+	# Convert bytes to json
+	json_data = json.loads(response_body)
+	# Every result
+	res = pd.DataFrame(json_data['response']['body']['items']['item'])
+	print('\n============================== Result ==============================')
+	print(res)
+	print('=====================================================================\n')
+	TemperatureWF, HumidityWF = get_WF_Temperature_Humidity_info(json_data) 
+	
+	return TemperatureWF, HumidityWF
+
+
+def get_Temperature_Humidity_info(data, len):
+    temperature=[]
+    humidity=[]	
+    try:     
+        weather_info = data['response']['body']['items']['item'] 
+        for i in range(int(len)):
+            temperature.append(weather_info[i]['ta'])
+            humidity.append(weather_info[i]['hm'])
+        return temperature, humidity
+    except KeyError:
+        print('API 호출 실패! (Actual weather data)')
+
+def get_weather(start_day, end_day):
+	if end_day.hour > start_day.hour:
+		hour_term = (end_day - start_day).seconds/3600
+		day_term = (end_day - start_day).days
+		UnkonwDataLen = day_term*24 + hour_term
+	else:
+		hour_term = (start_day - end_day).seconds/3600
+		day_term = (end_day - start_day).days +1
+		UnkonwDataLen = day_term*24 - hour_term
+		
+	sYear = str(start_day.year)
+	if start_day.month < 10:
+		sMonth = "0" + str(start_day.month)
+	else:
+		sMonth=str(start_day.month)
+	if start_day.day < 10:
+		sDay = "0" + str(start_day.day)
+	else:
+		sDay = str(start_day.day)    
+	start_date = sYear + sMonth + sDay
+	if start_day.hour < 10:
+		sTime = "0" + str(start_day.hour)
+	else:
+		sTime = str(start_day.hour)        
+	start_time = sTime
+
+	eYear = str(end_day.year)
+	if end_day.month < 10:
+		eMonth = "0" + str(end_day.month)
+	else:
+		eMonth=str(end_day.month)
+	if end_day.day < 10:
+		eDay = "0" + str(end_day.day)
+	else:
+		eDay = str(end_day.day)    
+	end_date = eYear + eMonth + eDay
+	if end_day.hour < 10:
+		eTime = "0" + str(end_day.hour)
+	else:
+		eTime = str(end_day.hour)
+	end_time = eTime
+
+	# Setting for URL parsing
+	CallBackURL = 'http://apis.data.go.kr/1360000/AsosHourlyInfoService/getWthrDataList'      # 맨 마지막 명칭에 따라 상세기능에 대한 정보가 변경될 수 있음
+	#   parameter for request
+	params = '?' + urlencode({
+		quote_plus("serviceKey"): "sOVGUogWTbCCmzCn10iCEI0pb9VqKHfiBv8PKYnxJLdz4n63U2uSO5Y2TDjS3lez%2BMNT1TVaH4sCkgsctj2xVg%3D%3D",     # 인증키  # 반드시 본인이 신청한 인증키를 입력해야함 (IP 불일치로 인한 오류 발생 가능)
+		quote_plus("numOfRows"): str(int(UnkonwDataLen)),          # 한 페이지 결과 수 // default : 10
+		quote_plus("pageNo"): "1",              # 페이지 번호 // default : 1
+		quote_plus("dataType"): "JSON",         # 응답자료형식 : XML, JSON
+		quote_plus("dataCd"): "ASOS",
+		quote_plus("dateCd"): "HR",         # 날짜 분류 코드: DAY, HR
+		quote_plus("startDt"): start_date,    # 시작일 // yyyymmdd
+		quote_plus("startHh"): start_time,        # 시작시 // HH
+		quote_plus("endDt"): end_date,    # 종료일 // yyyymmdd
+		quote_plus("endHh"): end_time,        # 종료시 // HH
+		quote_plus("stnIds"): "143",                # 지점번호 대구: 143
+		quote_plus("schListCnt"): "10"
+	})
+	# URL parsing
+	req = urllib.request.Request(CallBackURL + unquote(params))
+	print('result length : ', UnkonwDataLen)
+	# Get Data from API
+	response_body = urlopen(req).read() # get bytes data
+	# Convert bytes to json
+	json_data = json.loads(response_body)
+	# Every result
+	res = pd.DataFrame(json_data['response']['body']['items']['item'])
+	print('\n============================== Result ==============================')
+	print(res)
+	print('=====================================================================\n')
+	Temperature, Humidity = get_Temperature_Humidity_info(json_data, UnkonwDataLen)
+	return Temperature, Humidity
+	
+def Check_Restoring_Unknown_past_data(targetDB_IP, targetDB_UserID, targetDB_UserPW, targetDB_Name, n_x, n_y):
+	# MSSQL Access
+	conn = pymssql.connect(host = targetDB_IP, user = targetDB_UserID, password = targetDB_UserPW, database = targetDB_Name)
+	# Create Cursor from Connection
+	cursor = conn.cursor()
+	
+	now = datetime.now()
+	InitialForecastDayforCheck = now - timedelta(days=30)	# 최대 약 1개월 전까지 데이터 확인 (데이터 요청은 한번에 최대 1000건을 넘길 수 없음)
+	# API가 전날 데이터까지 제공함 (오늘 예보데이터가 없다면 다음날 채워야 함)
+	FinalForecastDayforCheck = now
+	
+	## Temperature 가져오기
+	cursor.execute("SELECT * FROM "+targetDBName+".dbo.BemsMonitoringPointWeatherForecasted where SiteId = 1 and Category="+"'"+"Temperature"+"'"+" and ForecastedDateTime >= "+"'"+str(InitialForecastDayforCheck.year)+"-"+str(InitialForecastDayforCheck.month)+"-"+str(InitialForecastDayforCheck.day)+"'"+"and ForecastedDateTime < "+"'"+str(FinalForecastDayforCheck.year)+"-"+str(FinalForecastDayforCheck.month)+"-"+str(FinalForecastDayforCheck.day)+"' order by ForecastedDateTime asc")
+	
+	# 데이타 하나씩 Fetch하여 출력
+	row = cursor.fetchone()
+	TemperatureRawData = [row]
+	while row:
+		row = cursor.fetchone()
+		if row == None:
+			break
+		TemperatureRawData.append(row)
+	
+	## Humidity 가져오기
+	cursor.execute("SELECT * FROM "+targetDBName+".dbo.BemsMonitoringPointWeatherForecasted where SiteId = 1 and Category="+"'"+"Humidity"+"'"+" and ForecastedDateTime >= "+"'"+str(InitialForecastDayforCheck.year)+"-"+str(InitialForecastDayforCheck.month)+"-"+str(InitialForecastDayforCheck.day)+"'"+"and ForecastedDateTime < "+"'"+str(FinalForecastDayforCheck.year)+"-"+str(FinalForecastDayforCheck.month)+"-"+str(FinalForecastDayforCheck.day)+"' order by ForecastedDateTime asc")
+	
+	# 데이타 하나씩 Fetch하여 출력
+	row = cursor.fetchone()
+	HumidityRawData = [row]
+	while row:
+		row = cursor.fetchone()
+		if row == None:
+			break
+		HumidityRawData.append(row)
+	
+	conn.close()
+	
+	TimeIdx_3h_Interval = [datetime(InitialForecastDayforCheck.year, InitialForecastDayforCheck.month, InitialForecastDayforCheck.day, 0, 0, 0)]
+	TimeIdx_Final = datetime(FinalForecastDayforCheck.year, FinalForecastDayforCheck.month, FinalForecastDayforCheck.day, 0, 0, 0)
+	while TimeIdx_3h_Interval[-1] < TimeIdx_Final:
+		TimeIdx_3h_Interval.append(TimeIdx_3h_Interval[-1]+timedelta(hours=3))
+	TimeIdx_3h_Interval = TimeIdx_3h_Interval[0:-1]
+	
+	### DB에 비어있는 값 찾기
+	idx_tem = 0								# TemperatureRawData 인덱스
+	idx_hum = 0								# HumidityRawData 인덱스
+	InitialDay_UnknownData_Tem = []			# 기온 unkown data 초기 일시
+	FinalDay_UnkownDate_Tem = []			# 기온 unkown data 연속 종료 일시
+	isContinue_Tem = False	
+	InitialDay_UnknownData_Hum = []			# 습도 unkown data 초기 일시
+	FinalDay_UnkownDate_Hum = []			# 습도 unkown data 연속 종료 일시
+	isContinue_Hum = False
+	
+	
+	for i in range(len(TimeIdx_3h_Interval)):
+		# DB에 데이터가 없는 경우
+		if len(TemperatureRawData) == 1:
+			InitialDay_UnknownData_Tem.append(TimeIdx_3h_Interval[0])
+			FinalDay_UnkownDate_Tem.append(TimeIdx_3h_Interval[-1])
+			break
+		# DB 마지막 일시의 데이터와 복원하고자하는 일시의 마지막 데이터가 일치하지 않는 경우(기온)
+		elif i >= len(TemperatureRawData):	
+			if idx_tem >= len(TemperatureRawData):				## 복원하고자하는 데이터의 마지막 일시 할당 후 for문 종료
+				InitialDay_UnknownData_Tem.append(TimeIdx_3h_Interval[i])
+				FinalDay_UnkownDate_Tem.append(TimeIdx_3h_Interval[-1])		
+				break
+			elif TimeIdx_3h_Interval[i] == TemperatureRawData[idx_tem][4]:
+				idx_tem += 1
+				if isContinue_Tem == True:
+					FinalDay_UnkownDate_Tem.append(TimeIdx_3h_Interval[i] - timedelta(hours=3))
+				isContinue_Tem = False
+			else:
+				if isContinue_Tem == False:
+					InitialDay_UnknownData_Tem.append(TimeIdx_3h_Interval[i])
+				isContinue_Tem = True
+			#####
+		# DB에 최근 데이터가 있는 경우(기온)
+		else:								
+			if TimeIdx_3h_Interval[i] == TemperatureRawData[idx_tem][4]:
+				idx_tem += 1
+				if isContinue_Tem == True:
+					FinalDay_UnkownDate_Tem.append(TimeIdx_3h_Interval[i] - timedelta(hours=3))
+				isContinue_Tem = False
+			else:
+				if isContinue_Tem == False:
+					InitialDay_UnknownData_Tem.append(TimeIdx_3h_Interval[i])
+				isContinue_Tem = True
+				
+	for i in range(len(TimeIdx_3h_Interval)):
+		# DB 마지막 일시의 데이터와 복원하고자하는 일시의 마지막 데이터가 일치하지 않는 경우(습도)
+		if len(HumidityRawData) == 1:
+			InitialDay_UnknownData_Hum.append(TimeIdx_3h_Interval[0])
+			FinalDay_UnkownDate_Hum.append(TimeIdx_3h_Interval[-1])
+			break
+		elif i >= len(HumidityRawData):	
+			if idx_hum >= len(HumidityRawData):				## 복원하고자하는 데이터의 마지막 일시 할당 후 for문 종료
+				InitialDay_UnknownData_Hum.append(TimeIdx_3h_Interval[i])
+				FinalDay_UnkownDate_Hum.append(TimeIdx_3h_Interval[-1])		
+				break
+			elif TimeIdx_3h_Interval[i] == HumidityRawData[idx_hum][4]:
+				idx_hum += 1
+				if isContinue_Hum == True:
+					FinalDay_UnkownDate_Hum.append(TimeIdx_3h_Interval[i] - timedelta(hours=3))
+				isContinue_Hum = False
+			else:
+				if isContinue_Hum == False:
+					InitialDay_UnknownData_Hum.append(TimeIdx_3h_Interval[i])
+				isContinue_Hum = True
+		#####
+		
+		# DB에 복원하고자하는 일시의 마지막 데이터가 있는 경우(습도)
+		else:
+			if TimeIdx_3h_Interval[i] == HumidityRawData[idx_hum][4]:
+				idx_hum += 1
+				if isContinue_Hum == True:
+					FinalDay_UnkownDate_Hum.append(TimeIdx_3h_Interval[i] - timedelta(hours=3))
+				isContinue_Hum = False
+			else:
+				if isContinue_Hum == False:
+					InitialDay_UnknownData_Hum.append(TimeIdx_3h_Interval[i])
+				isContinue_Hum = True
+	
+	### Restoring unknown data from actual past weather data
+	# MSSQL Access
+	conn = pymssql.connect(host = targetDB_IP, user = targetDB_UserID, password = targetDB_UserPW, database = targetDB_Name)
+	# Create Cursor from Connection
+	cursor = conn.cursor()	
+	for i in range(len(FinalDay_UnkownDate_Tem)):
+		Tem, Hum = get_weather(InitialDay_UnknownData_Tem[i], FinalDay_UnkownDate_Tem[i] + timedelta(hours=1))  	## API 특징 end_time은 포함하지않으므로
+		tem_date = InitialDay_UnknownData_Tem[i]
+		for j in range(0,len(Tem),3):
+			try:
+				cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointWeatherForecasted (SiteId, CreatedDateTime, Category, BaseDateTime, ForecastedDateTime, ForecastedValue, nx, ny) VALUES(1," + "'" + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','"+ "Temperature" + "','"+ str(tem_date) + "','" + str(tem_date) + "'," + str(Tem[j]) + "," + n_x + "," + n_y + ")")
+				conn.commit()				
+			except:
+				print('There is an issue in the progress of restoring unknown weather forecast data to actual past weather data. (Temperature)')						
+			tem_date += timedelta(hours=3)
+	
+	for i in range(len(FinalDay_UnkownDate_Hum)):
+		Tem, Hum = get_weather(InitialDay_UnknownData_Hum[i], FinalDay_UnkownDate_Hum[i] + timedelta(hours=1))  	## API 특징 end_time은 포함하지않으므로
+		hum_date = InitialDay_UnknownData_Hum[i]
+		for j in range(0,len(Hum),3):
+			try:
+				cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointWeatherForecasted (SiteId, CreatedDateTime, Category, BaseDateTime, ForecastedDateTime, ForecastedValue, nx, ny) VALUES(1," + "'" + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','"+ "Humidity" + "','"+ str(hum_date) + "','" + str(hum_date) + "'," + str(Hum[j]) + "," + n_x + "," + n_y + ")")
+				conn.commit()				
+			except:
+				print('There is an issue in the progress of restoring unknown weather forecast data to actual past weather data. (Humidity)')						
+			hum_date += timedelta(hours=3)
+	conn.close()
+	
+	if len(FinalDay_UnkownDate_Tem) == 0:
+		print('There is no unknown data before (Temperature)')
+	else:
+		for i in range(len(InitialDay_UnknownData_Tem)):
+			print('Initial and final date of unknown data (Tempearure) : ', InitialDay_UnknownData_Tem[i], FinalDay_UnkownDate_Tem[i])
+	if len(FinalDay_UnkownDate_Hum) == 0:
+		print('There is no unknown data before (Humidity)')
+	else:
+		for i in range(len(InitialDay_UnknownData_Hum)):
+			print('Initial and final date of unknown data (Humidity) : ', InitialDay_UnknownData_Hum[i], FinalDay_UnkownDate_Hum[i])
+	
+	
+if __name__ == "__main__" :
+	## Loading .ini file
+	myINI = configparser.ConfigParser()
+	myINI.read("Config.ini", "utf-8" )
+	# MSSQL Access
+	conn = pymssql.connect(host=myINI.get('LocalDB_Info','ip_address'), user=myINI.get('LocalDB_Info','user_id'), password=myINI.get('LocalDB_Info','user_password'), database=myINI.get('LocalDB_Info','db_name'))
+	# Create Cursor from Connection
+	cursor = conn.cursor()			
+
+	# Execute SQL (Config data)
+	cursor.execute('SELECT * FROM BemsConfigData where SiteId = 1')
+	rowDB_info = cursor.fetchone()	
+	conn.close()
+
+	targetDBIP = rowDB_info[5]
+	targetDBUserID = rowDB_info[6]
+	targetDBUserPW = rowDB_info[7]
+	targetDBName = rowDB_info[8]
+		
+	nx = str(89) 			# 예보지점 x 좌표
+	ny = str(91) 			# 예보지점 y 좌표
+	
+	#### Check Unknown past data when starting program ####	
+	Check_Restoring_Unknown_past_data(targetDBIP, targetDBUserID, targetDBUserPW, targetDBName, nx, ny)
+	
+	# Accumulate weather forecast data
+	while True:		
+		now = datetime.now()
+		if now.hour == 1 and now.minute <= 15:				## 하루에 한번 오전 1시 이후에 과거 데이터 체크 (오전 1시는 임의로 정한 시각)
+			Check_Restoring_Unknown_past_data(targetDBIP, targetDBUserID, targetDBUserPW, targetDBName, nx, ny)		## 과거 실 데이터가 어제까지만 제공되기 때문에 매일 어제 데이터가 있는지 체크
+		if now.hour == 20 and now.minute >= 45 and now.minute <= 59:
+			AccumulationActive = True
+		else:
+			AccumulationActive = False
+			if now.minute > 55:
+				print("[ Current Time -", now.hour,":", now.minute,":", now.second,"], " "Sleeping for 10 minutes... Accumulate weather forecasted data at 8:45 ~ 9:00 p.m. every day")
+				time.sleep(60*10)
+			else:
+				print("[ Current Time -", now.hour,":", now.minute,":", now.second,"], " "Sleeping for 15 minutes... Accumulate weather forecasted data at 8:45 ~ 9:00 p.m. every day")
+				time.sleep(60*15)
+		
+		if AccumulationActive:
+							
+			TempWF, HumWF = get_weather_forecast(nx, ny) 
+			
+			# MSSQL Access
+			conn = pymssql.connect(host = targetDBIP, user = targetDBUserID, password = targetDBUserPW, database = targetDBName)
+			# Create Cursor from Connection
+			cursor = conn.cursor()	
+			
+			for i in range(1, len(TempWF)):
+				baseDate = TempWF[i][0]
+				baseTime = TempWF[i][1]
+				FcstDate = TempWF[i][2]
+				FcstTime = TempWF[i][3]
+				baseDateTime = datetime(int(baseDate[0:4]), int(baseDate[4:6]), int(baseDate[6:]), int(baseTime[0:2]), int(baseTime[2:]))
+				FcstDateTime = datetime(int(FcstDate[0:4]), int(FcstDate[4:6]), int(FcstDate[6:]), int(FcstTime[0:2]), int(FcstTime[2:]))
+				try:
+					cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointWeatherForecasted (SiteId, CreatedDateTime, Category, BaseDateTime, ForecastedDateTime, ForecastedValue, nx, ny) VALUES(1," + "'" + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','"+ "Temperature" + "','"+ str(baseDateTime) + "','" + str(FcstDateTime) + "'," + TempWF[i][4] + "," + nx + "," + ny + ")")
+					conn.commit()
+				except:
+					print('Weather forecasted temperature data already exists! (ForecastDateTime : '+str(FcstDateTime)+')')
+			for i in range(1, len(HumWF)):
+				baseDate = HumWF[i][0]
+				baseTime = HumWF[i][1]
+				FcstDate = HumWF[i][2]
+				FcstTime = HumWF[i][3]
+				baseDateTime = datetime(int(baseDate[0:4]), int(baseDate[4:6]), int(baseDate[6:]), int(baseTime[0:2]), int(baseTime[2:]))
+				FcstDateTime = datetime(int(FcstDate[0:4]), int(FcstDate[4:6]), int(FcstDate[6:]), int(FcstTime[0:2]), int(FcstTime[2:]))
+				try:
+					cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointWeatherForecasted (SiteId, CreatedDateTime, Category, BaseDateTime, ForecastedDateTime, ForecastedValue, nx, ny) VALUES(1," + "'" + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','"+ "Humidity" + "','"+ str(baseDateTime) + "','" + str(FcstDateTime) + "'," + HumWF[i][4] + "," + nx + "," + ny + ")")
+					conn.commit()
+				except:					
+					print('Weather forecasted humidity data already exists! (ForecastDateTime : '+str(FcstDateTime)+')')
+				
+			conn.close()
+
+			print("Sleeping for 15 minutes ...")
+			time.sleep(60*15)
+			
+			
+			
+			
+			

+ 1604 - 0
RealTimeSimulator_HeatStorageSystem.py

@@ -0,0 +1,1604 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+import time
+import datetime
+import numpy as np
+import math
+from korean_lunar_calendar import KoreanLunarCalendar
+import configparser
+import pymssql
+
+from sklearn import ensemble
+from sklearn.model_selection import train_test_split
+
+
+## Measure
+def MAPE(y_observed, y_pred):
+    return np.mean(np.abs((y_observed - y_pred) / y_observed)) * 100
+def MAE(y_observed, y_pred):
+    return np.mean(np.abs(y_observed - y_pred))
+def MBE(y_observed, y_pred):
+    return (np.sum((y_observed - y_pred))/(len(y_observed)*np.mean(y_observed)))*100
+def CVRMSE(y_observed, y_pred):
+    return (np.sqrt(np.mean((y_observed - y_pred)*(y_observed - y_pred)))/np.mean(y_observed))*100
+
+
+def Check_AlivedTimeStamp(RawData, ComparedData, idx_raw, idx_comp, unit):
+    if unit == 'daily':
+        if datetime.date(RawData[idx_raw].year, RawData[idx_raw].month, RawData[idx_raw].day) == datetime.date(ComparedData[idx_comp].year, ComparedData[idx_comp].month, ComparedData[idx_comp].day):
+            isAlived = True
+        else:
+            isAlived = False
+    elif unit == 'quarterly':
+        if datetime.datetime(RawData[idx_raw][4].year,RawData[idx_raw][4].month,RawData[idx_raw][4].day,RawData[idx_raw][4].hour,RawData[idx_raw][4].minute) == datetime.datetime(ComparedData[idx_comp].year, ComparedData[idx_comp].month, ComparedData[idx_comp].day,ComparedData[idx_comp].hour, ComparedData[idx_comp].minute):
+            isAlived = True
+        else:
+            isAlived = False
+    return isAlived
+
+def detect_unknown_duplicated_zero_data_for_faciilty(raw_Data, startday, lastday, Day_Period, OrgDataRes, isRecent):
+	CumTime = datetime.datetime(int(startday.strftime('%Y')), int(startday.strftime('%m')), int(startday.strftime('%d')), 0, 0, 0)
+	StandardTimeStamp_DayUnit = [CumTime]
+	StandardTimeStamp_QuarterUnit = [CumTime]
+	# Create intact time stamp 
+	for idx_day in range(Day_Period):
+		StandardTimeStamp_DayUnit.append(startday + datetime.timedelta(days=idx_day))
+		if isRecent and idx_day == Day_Period-1:
+			tmp_len = now.hour*4 + int(now.minute/15)
+			for idx_time in range(tmp_len):
+				CumTime += datetime.timedelta(minutes = 15)
+				StandardTimeStamp_QuarterUnit.append(CumTime)
+		else:
+			for idx_time in range(OrgDataRes):
+				CumTime += datetime.timedelta(minutes = 15)
+				StandardTimeStamp_QuarterUnit.append(CumTime)
+							
+			
+	### Extract data within day period
+	Raw_Date=[]     # raw data (date)
+	Raw_Value=[]    # raw data (value)
+	for i in range(len(raw_Data)):
+		if datetime.date(raw_Data[i][4].year,raw_Data[i][4].month,raw_Data[i][4].day) >= startday:
+			if datetime.date(raw_Data[i][4].year,raw_Data[i][4].month,raw_Data[i][4].day) <= lastday:
+				Raw_Date.append(raw_Data[i][4])
+				Raw_Value.append(raw_Data[i][5])
+			if datetime.date(raw_Data[i][4].year,raw_Data[i][4].month,raw_Data[i][4].day) > lastday:
+				break
+				
+	Data_len=len(Raw_Date)
+	if isRecent:
+		DataAct_len = (Day_Period-1)*OrgDataRes + now.hour*4 + int(now.minute/15)+1
+	else:
+		DataAct_len = Day_Period*OrgDataRes
+		
+	### Unknown/duplicated data counts
+	DataCount=[]
+	for i in range(len(StandardTimeStamp_DayUnit)):
+		cnt_unk=0   # Unknown data count
+		for j in range(Data_len-1):
+			if StandardTimeStamp_DayUnit[i] == datetime.date(Raw_Date[j].year,Raw_Date[j].month,Raw_Date[j].day):
+				cnt_unk += 1
+		if isRecent and i==len(StandardTimeStamp_DayUnit)-1:
+			DataCount.append([StandardTimeStamp_DayUnit[i], now.hour*4 + int(now.minute/15) - cnt_unk])        
+		else:
+			DataCount.append([StandardTimeStamp_DayUnit[i], OrgDataRes-cnt_unk])
+									
+	DataCountMat=np.matrix(DataCount)
+
+	######## 현재 DB 특성상 값이 중복되거나 시간테이블의 행 자체가 없는 경우가 있고, 이 데이터 1 step 앞뒤로 데이터가 비정상일 확률이 높으므로 비정상데이터 뿐만 아니라 앞뒤 1 step까지 NaN으로 처리함
+	data_w_nan=[]
+	idx=0
+	idx2=0
+	isBadData = False
+	for i in range(DataAct_len): 
+		if datetime.date(raw_Data[idx][4].year,raw_Data[idx][4].month,raw_Data[idx][4].day) >= startday and datetime.date(raw_Data[idx][4].year,raw_Data[idx][4].month,raw_Data[idx][4].day) <= lastday:
+			if isBadData == True:
+				data_w_nan.append(np.nan)
+				isBadData=False
+			elif Check_AlivedTimeStamp(raw_Data, StandardTimeStamp_QuarterUnit, idx, idx2, 'quarterly'):
+				data_w_nan.append(raw_Data[idx][5])
+			else:
+				if i > 1:
+					data_w_nan[-1]=np.nan
+				data_w_nan.append(np.nan)
+				#data_w_nan.append(np.nan)
+				if raw_Data[idx+1][5] > 0 and Check_AlivedTimeStamp(raw_Data, StandardTimeStamp_QuarterUnit, idx+1, idx2+1, 'quarterly'):
+					isBadData = True
+				idx -= 1
+			idx2 += 1
+		idx += 1
+	return StandardTimeStamp_QuarterUnit, data_w_nan, DataCountMat
+
+
+### 예보데이터는 내일 데이터까지 확보해야하기때문에 리스트 수가 설비 데이터에 비해 하루 치가 더 많다
+def detect_unknown_duplicated_zero_data_for_WeatherForecast3h(raw_Data, startday, lastday, Day_Period):
+	StandardTimeStamp_DayUnit = []
+	# Create intact time stamp 
+	for idx_day in range(Day_Period+1):
+		StandardTimeStamp_DayUnit.append(startday + datetime.timedelta(days=idx_day))
+		
+	### Extract data within day period
+	Raw_Value_max=[]    # raw data (value)
+	Raw_Value_min=[]
+	Raw_Value_mean=[]
+	Raw_Date=[]     # raw data (date)
+	tmp_data=[raw_Data[0][5]]
+	for i in range(len(raw_Data)):        
+		if i == len(raw_Data)-1:
+			Raw_Date.append(raw_Data[i][4])
+			Raw_Value_max.append(max(tmp_data))
+			Raw_Value_min.append(min(tmp_data))
+			Raw_Value_mean.append(np.mean(tmp_data))
+		elif datetime.date(raw_Data[i][4].year,raw_Data[i][4].month,raw_Data[i][4].day) >= startday:
+			if datetime.date(raw_Data[i][4].year,raw_Data[i][4].month,raw_Data[i][4].day) <= lastday + datetime.timedelta(days=1):
+				if datetime.date(raw_Data[i][4].year,raw_Data[i][4].month,raw_Data[i][4].day) != datetime.date(raw_Data[i+1][4].year,raw_Data[i+1][4].month,raw_Data[i+1][4].day):
+					Raw_Date.append(raw_Data[i][4])
+					Raw_Value_max.append(max(tmp_data))
+					Raw_Value_min.append(min(tmp_data))
+					Raw_Value_mean.append(np.mean(tmp_data))
+					tmp_data=[]
+				tmp_data.append(raw_Data[i+1][5])
+			if datetime.date(raw_Data[i][4].year,raw_Data[i][4].month,raw_Data[i][4].day) > lastday + datetime.timedelta(days=1):
+				break
+
+	Data_len=len(Raw_Date)
+	### Unknown/duplicated data counts
+	DataCount=[]
+	for i in range(len(StandardTimeStamp_DayUnit)):
+		cnt_unk=0   # Unknown data count
+		for j in range(Data_len-1):
+			if StandardTimeStamp_DayUnit[i] == datetime.date(Raw_Date[j].year,Raw_Date[j].month,Raw_Date[j].day):
+				cnt_unk += 1
+		DataCount.append([StandardTimeStamp_DayUnit[i], 1-cnt_unk])
+	DataCountMat=np.matrix(DataCount)
+	######## 현재 DB 특성상 값이 중복되거나 시간테이블의 행 자체가 없는 경우가 있고, 이 데이터 1 step 앞뒤로 데이터가 비정상일 확률이 높으므로 비정상데이터 뿐만 아니라 앞뒤 1 step까지 NaN으로 처리함
+
+	MaxData_w_nan=[]
+	MinData_w_nan=[]
+	MeanData_w_nan=[]
+	for i in range(len(StandardTimeStamp_DayUnit)):
+		for j in range(len(Raw_Date)):
+			if Check_AlivedTimeStamp(Raw_Date, StandardTimeStamp_DayUnit, j, i, 'daily'):
+				MaxData_w_nan.append(Raw_Value_max[j])
+				MinData_w_nan.append(Raw_Value_min[j])
+				MeanData_w_nan.append(Raw_Value_mean[j])
+				break
+			elif j == len(Raw_Date)-1:
+				MaxData_w_nan.append(np.nan)
+				MinData_w_nan.append(np.nan)
+				MeanData_w_nan.append(np.nan)
+				
+	return StandardTimeStamp_DayUnit, MaxData_w_nan, MinData_w_nan, MeanData_w_nan, DataCountMat
+
+
+### Define day-type 
+def getDayName(year, month, day):
+    return ['MON','TUE','WED','THU','FRI','SAT','SUN'][datetime.date(year, month, day).weekday()]
+def getDayType(DateinDay, Period, SpecialHoliday):
+    DoW=[];    # Day of Week
+    for i in range(Period):
+        if DateinDay[i].year==2019 and DateinDay[i].month==5 and DateinDay[i].day==18:
+            DoW.append([5, DateinDay[i]])
+        elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'MON':
+            DoW.append([1, DateinDay[i]])
+        elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'TUE':
+            DoW.append([2, DateinDay[i]])
+        elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'WED':
+            DoW.append([3, DateinDay[i]])
+        elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'THU':
+            DoW.append([4, DateinDay[i]])
+        elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'FRI':
+            DoW.append([5, DateinDay[i]])
+        elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'SAT':
+            DoW.append([6, DateinDay[i]])
+        elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'SUN':
+            DoW.append([7, DateinDay[i]])
+
+        for j in range(len(SpecialHoliday)):
+            if SpecialHoliday[j] == datetime.date(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day):
+                DoW[-1][0] = 8
+                break
+    
+    ### W-W:1, N-W:2, W-N:3, N-N:4 ###
+    DayType=[]
+    for i in range(Period):
+        if i==0:
+            if DoW[i][0] <= 5:
+                DayType.append([1, DateinDay[i]])
+            elif DoW[i][0] > 5:
+                DayType.append([3, DateinDay[i]])
+        else:
+            if DoW[i-1][0] <= 5 and  DoW[i][0] <= 5:
+                DayType.append([1, DateinDay[i]])
+            elif DoW[i-1][0] > 5 and DoW[i][0] <= 5:
+                DayType.append([2, DateinDay[i]])
+            elif DoW[i-1][0] <= 5 and DoW[i][0] > 5:
+                DayType.append([3, DateinDay[i]])
+            elif DoW[i-1][0] > 5 and DoW[i][0] > 5:
+                DayType.append([4, DateinDay[i]])
+    return DoW, DayType
+
+
+if __name__ == "__main__" :
+	Init = True
+	## Check every 15min. in the infinite loop
+	while True:
+		now = datetime.datetime.now().now()
+		## distinguish real time update and specific day
+		## 자정에 생기는 인덱싱 문제로 0시에는 16분에 업데이트, 나머지는 15분에 한 번씩 업데이트
+		if Init:
+			prev_time_minute = now.minute - 1		## 알고리즘 중복 수행 방지 (알고리즘 수행시 1분이 안걸리기에 한타임에 알고리즘 한번만 동작시키기 위함)
+		if (now.hour != 0 and now.minute%15 == 1 and now.second > 0 and now.second < 5) and prev_time_minute != now.minute:
+			ActiveAlgorithm = True
+			prev_time_minute = now.minute
+		else:
+			ActiveAlgorithm = False
+			
+		if ActiveAlgorithm or Init:
+			
+			## Loading .ini file
+			myINI = configparser.ConfigParser()
+			myINI.read("Config.ini", "utf-8" )
+			# MSSQL Access
+			conn = pymssql.connect(host=myINI.get('LocalDB_Info','ip_address'), user=myINI.get('LocalDB_Info','user_id'), password=myINI.get('LocalDB_Info','user_password'), database = myINI.get('LocalDB_Info','db_name'), autocommit=True)
+			# Create Cursor from Connection
+			cursor = conn.cursor()			
+
+			# Execute SQL (Electric consumption)
+			cursor.execute('SELECT * FROM BemsConfigData where SiteId = 1')
+			
+			rowDB_info = cursor.fetchone()
+			
+			conn.close()
+			
+			loadDBIP = rowDB_info[1]
+			loadDBUserID = rowDB_info[2]
+			loadDBUserPW = rowDB_info[3]
+			loadDBName = rowDB_info[4]
+			targetDBIP = rowDB_info[5]
+			targetDBUserID = rowDB_info[6]
+			targetDBUserPW = rowDB_info[7]
+			targetDBName = rowDB_info[8]
+			
+			startday = datetime.date(int(rowDB_info[9].year), int(rowDB_info[9].month), int(rowDB_info[9].day))
+			
+			now=datetime.datetime.now().now()
+			lastday = datetime.date(now.year, now.month, now.day)
+			isRecent = True
+
+			if startday < datetime.date(2020,4,8):
+				print('[ERROR] 데이터 최소 시작 시점은 2020.04.08 입니다')
+				startday = datetime.date(2020,4,9)
+			elif startday > lastday:
+				print('[ERROR] 예측 타깃 시작시점이 데이터 시작 시점보다 작을 수 없습니다')
+			
+			
+			##############################################################################################
+			## 기온, 습도 예보 데이터 로드
+			# MSSQL 접속
+			conn = pymssql.connect(host = targetDBIP, user = targetDBUserID, password = targetDBUserPW, database = targetDBName, autocommit=True)
+			# Connection 으로부터 Cursor 생성
+			cursor = conn.cursor()
+
+			# SQL문 실행 (기온 예보)
+			cursor.execute('SELECT * FROM BemsMonitoringPointWeatherForecasted where SiteId = 1 and Category = '+"'"+'Temperature'+"'"+' order by ForecastedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawWFTemperature = [row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawWFTemperature.append(row)
+			rawWFTemperature.reverse()
+
+			# SQL문 실행 (습도 예보)
+			cursor.execute('SELECT * FROM BemsMonitoringPointWeatherForecasted where SiteId = 1 and Category = '+"'"+'Humidity'+"'"+' order by ForecastedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawWFHumidity = [row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawWFHumidity.append(row)
+			rawWFHumidity.reverse()
+			##############################################################################################
+
+			startday = datetime.date(rawWFHumidity[0][4].year, rawWFHumidity[0][4].month, rawWFHumidity[0][4].day)		## 데이터 불러오는 DB가 선구축된다고 가정하여 예보데이터 기준으로 startday define
+			DayPeriod = (lastday - startday).days + 1
+			print('* StartDay :',startday,',', 'LastDay :', lastday,',','Current Time :', now, ',','Day period :', DayPeriod)
+
+
+			# MSSQL 접속
+			conn = pymssql.connect(host = loadDBIP, user = loadDBUserID, password = loadDBUserPW, database = loadDBName, autocommit=True)
+			
+			# Connection 으로부터 Cursor 생성
+			cursor = conn.cursor()
+
+			DataRes_96=96
+			DataRes_24=24
+
+			print('************ (Start) Load & pre-processing data !! ************')			
+			# SQL문 실행 (축열조 축열량)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 2 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawChillerCalAmount=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawChillerCalAmount.append(row)
+			rawChillerCalAmount.reverse()
+
+			# SQL문 실행 (축열조 제빙운전상태)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 16 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawChillerStatusIcing=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawChillerStatusIcing.append(row)
+			rawChillerStatusIcing.reverse()
+			
+			# SQL문 실행 (축열조 축단운전상태)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 17 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawChillerStatusDeicing=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawChillerStatusDeicing.append(row)
+			rawChillerStatusDeicing.reverse()
+
+			# SQL문 실행 (축열조 병렬운전상태)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 18 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawChillerStatusParallel=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawChillerStatusParallel.append(row)
+			rawChillerStatusParallel.reverse()
+
+			# SQL문 실행 (축열조 냉단운전상태)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 19 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawChillerStatusRefOnly=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawChillerStatusRefOnly.append(row)
+			rawChillerStatusRefOnly.reverse()
+
+
+			## 현재 2019, 2020년 냉동기 전력량 데이터가 없으므로 2년 전 데이터를 활용
+			# SQL문 실행 (냉동기1 전력량)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 11 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawRefPowerConsume1=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawRefPowerConsume1.append(row)
+			rawRefPowerConsume1.reverse()
+
+			# SQL문 실행 (냉동기1 운전상태)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 15 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawRefStatus1=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawRefStatus1.append(row)
+			rawRefStatus1.reverse()
+
+			# SQL문 실행 (냉동기2 전력량)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 11 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawRefPowerConsume2=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawRefPowerConsume2.append(row)
+			rawRefPowerConsume2.reverse()
+
+			# SQL문 실행 (냉동기2 운전상태)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 15 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawRefStatus2=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawRefStatus2.append(row)
+			rawRefStatus2.reverse()
+
+			# SQL문 실행 (브라인 입구온도)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 4 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawBrineInletTemperature=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawBrineInletTemperature.append(row)
+			rawBrineInletTemperature.reverse()
+
+			# SQL문 실행 (브라인 출구온도)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 3 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawBrineOutletTemperature=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawBrineOutletTemperature.append(row)
+			rawBrineOutletTemperature.reverse()
+
+			# SQL문 실행 (브라인 혼합온도)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 22 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawBrineMixedTemperature=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawBrineMixedTemperature.append(row)
+			rawBrineMixedTemperature.reverse()
+
+			# SQL문 실행 (브라인 통과유량)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 5 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawBrineFlowAmount=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawBrineFlowAmount.append(row)
+			rawBrineFlowAmount.reverse()
+
+
+			# SQL문 실행 (정기휴일)
+			cursor.execute('SELECT * FROM CmHoliday where SiteId = 1 and IsUse = 1')
+			
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			regularHolidayData = [row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				regularHolidayData.append(row)
+			regularHolidayData = regularHolidayData[0:-1]
+
+			# SQL문 실행 (비정기휴일)
+			cursor.execute('SELECT * FROM CmHolidayCustom where SiteId = 1 and IsUse = 1')
+			
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			suddenHolidayData = [row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				suddenHolidayData.append(row)
+			suddenHolidayData = suddenHolidayData[0:-1]
+
+			##############################################################################################
+			## 현재 2019, 2020년 냉동기 전력량 데이터가 없으므로 2년 전 데이터를 활용
+			# SQL문 실행 (냉동기1 전력량), 2018
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 11 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawRefPowerConsume1_2018=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < datetime.date(2018,1,1):
+					break
+				rawRefPowerConsume1_2018.append(row)
+			rawRefPowerConsume1_2018.reverse()
+
+			# SQL문 실행 (냉동기1 운전상태)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 15 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawRefStatus1_2018=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < datetime.date(2018,1,1):
+					break
+				rawRefStatus1_2018.append(row)
+			rawRefStatus1_2018.reverse()
+
+			# SQL문 실행 (냉동기2 전력량)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 11 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawRefPowerConsume2_2018=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < datetime.date(2018,1,1):
+					break
+				rawRefPowerConsume2_2018.append(row)
+			rawRefPowerConsume2_2018.reverse()
+
+			# SQL문 실행 (냉동기2 운전상태)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId=1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 15 order by CreatedDateTime desc')
+			
+			row = cursor.fetchone()
+			rawRefStatus2_2018=[row]
+			while row:
+				row = cursor.fetchone()
+				if row == None:
+					break
+				if datetime.date(row[4].year,row[4].month,row[4].day) < datetime.date(2018,1,1):
+					break
+				rawRefStatus2_2018.append(row)
+			rawRefStatus2_2018.reverse()
+			
+			##############################################################################################
+
+			# 연결 끊기
+			conn.close()
+
+			## 휴일 데이터 DB에서 호출
+
+			# 공휴일의 음력 계산
+			calendar_convert = KoreanLunarCalendar()
+			SpecialHoliday = []
+			for i in range(lastday.year-startday.year+1):
+				for j in range(len(regularHolidayData)):
+					if regularHolidayData[j][3] == 1:
+						if regularHolidayData[j][1] == 12 and regularHolidayData[j][2] == 30: ## 설 하루 전 연휴 계산을 위함
+							calendar_convert.setLunarDate(startday.year+i-1, regularHolidayData[j][1], regularHolidayData[j][2], False)
+							SpecialHoliday.append(datetime.date(int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[0]), int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[1]), int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[2])))
+						else:
+							calendar_convert.setLunarDate(startday.year+i, regularHolidayData[j][1], regularHolidayData[j][2], False)
+							SpecialHoliday.append(datetime.date(int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[0]), int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[1]), int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[2])))
+					else:
+						SpecialHoliday.append(datetime.date(startday.year+i,regularHolidayData[j][1],regularHolidayData[j][2]))
+
+			for i in range(len(suddenHolidayData)):
+				if suddenHolidayData[i][1].year >= startday.year:
+					SpecialHoliday.append(datetime.date(suddenHolidayData[i][1].year, suddenHolidayData[i][1].month, suddenHolidayData[i][1].day))
+
+			SpecialHoliday=list(set(SpecialHoliday))
+
+			##############################################################################################
+
+			ChillerCalAmount_Date, ChillerCalAmount_w_nan, DataCountMat_ChillerCalAmount = detect_unknown_duplicated_zero_data_for_faciilty(rawChillerCalAmount, startday, lastday, DayPeriod, DataRes_96, isRecent)
+
+			BrineMixedTemperature_Date, BrineMixedTemperature_w_nan, DataCountMat_BrineMixedTemperature = detect_unknown_duplicated_zero_data_for_faciilty(rawBrineMixedTemperature, startday, lastday, DayPeriod, DataRes_96, isRecent)
+			BrineInletTemperature_Date, BrineInletTemperature_w_nan, DataCountMat_BrineInletTemperature = detect_unknown_duplicated_zero_data_for_faciilty(rawBrineInletTemperature, startday, lastday, DayPeriod, DataRes_96, isRecent)
+			BrineOutletTemperature_Date, BrineOutletTemperature_w_nan, DataCountMat_BrineOutletTemperature = detect_unknown_duplicated_zero_data_for_faciilty(rawBrineOutletTemperature, startday, lastday, DayPeriod, DataRes_96, isRecent)
+
+			BrineFlowAmount_Date, BrineFlowAmount_w_nan, DataCountMat_BrineFlowAmount = detect_unknown_duplicated_zero_data_for_faciilty(rawBrineFlowAmount, startday, lastday, DayPeriod, DataRes_96, isRecent)
+						
+			ChStatusIcing_Date, ChStatusIcing_w_nan, DataCountMat_ChStatusIcing = detect_unknown_duplicated_zero_data_for_faciilty(rawChillerStatusIcing, startday, lastday, DayPeriod, DataRes_96, isRecent)
+			ChStatusDeicing_Date, ChStatusDeicing_w_nan, DataCountMat_ChStatusDeicing = detect_unknown_duplicated_zero_data_for_faciilty(rawChillerStatusDeicing, startday, lastday, DayPeriod, DataRes_96, isRecent)
+			ChStatusParallel_Date, ChStatusParallel_w_nan, DataCountMat_ChStatusParallel = detect_unknown_duplicated_zero_data_for_faciilty(rawChillerStatusParallel, startday, lastday, DayPeriod, DataRes_96, isRecent)
+			ChStatusRefOnly_Date, ChStatusRefOnly_w_nan, DataCountMat_ChStatusRefOnly = detect_unknown_duplicated_zero_data_for_faciilty(rawChillerStatusRefOnly, startday, lastday, DayPeriod, DataRes_96, isRecent)
+
+			RefPowerConsume1_Date, RefPowerConsume1_w_nan, DataCountMat_RefPowerConsume1 = detect_unknown_duplicated_zero_data_for_faciilty(rawRefPowerConsume1, startday, lastday, DayPeriod, DataRes_96, isRecent)
+			RefPowerConsume2_Date, RefPowerConsume2_w_nan, DataCountMat_RefPowerConsume2 = detect_unknown_duplicated_zero_data_for_faciilty(rawRefPowerConsume2, startday, lastday, DayPeriod, DataRes_96, isRecent)
+
+			RefStatus1_Date, RefStatus1_w_nan, DataCountMat_RefStatus1 = detect_unknown_duplicated_zero_data_for_faciilty(rawRefStatus1, startday, lastday, DayPeriod, DataRes_96, isRecent)
+			RefStatus2_Date, RefStatus2_w_nan, DataCountMat_RefStatus2 = detect_unknown_duplicated_zero_data_for_faciilty(rawRefStatus2, startday, lastday, DayPeriod, DataRes_96, isRecent)
+
+			##############################################################################################
+			## 2019, 2020년 냉동기 전력량이 없어서 2018년 데이터로 대체
+			DayPeriod_2018 = (datetime.date(2018,12,31) - datetime.date(2018,1,1)).days + 1
+			
+			RefPowerConsume1_2018_Date, RefPowerConsume1_2018_w_nan, DataCountMat_RefPowerConsume1_2018 = detect_unknown_duplicated_zero_data_for_faciilty(rawRefPowerConsume1_2018, datetime.date(2018,1,1), datetime.date(2018,12,31), DayPeriod_2018, DataRes_96, isRecent)
+			RefPowerConsume2_2018_Date, RefPowerConsume2_2018_w_nan, DataCountMat_RefPowerConsume2_2018 = detect_unknown_duplicated_zero_data_for_faciilty(rawRefPowerConsume2_2018, datetime.date(2018,1,1), datetime.date(2018,12,31), DayPeriod_2018, DataRes_96, isRecent)
+
+			RefStatus1_Date_2018, RefStatus1_2018_w_nan, DataCountMat_RefStatus1_2018 = detect_unknown_duplicated_zero_data_for_faciilty(rawRefStatus1_2018, datetime.date(2018,1,1), datetime.date(2018,12,31), DayPeriod_2018, DataRes_96, isRecent)
+			RefStatus2_2018_Date, RefStatus2_2018_w_nan, DataCountMat_RefStatus2_2018 = detect_unknown_duplicated_zero_data_for_faciilty(rawRefStatus2_2018, datetime.date(2018,1,1), datetime.date(2018,12,31), DayPeriod_2018, DataRes_96, isRecent)
+				
+			################# Using the power Consumption of Refrigerator in 2018 instead of 2020 #################
+			#### 전력 소비량 계산
+			_st=90*96
+			_end=195*96
+			period_2018=(_end-_st)/96
+			RefStatus1_2018_w_nan_tmp=RefStatus1_2018_w_nan[_st:_end]
+			RefPowerConsume1_2018_w_nan_tmp=RefPowerConsume1_2018_w_nan[_st:_end]
+
+			RefStatus2_2018_w_nan_tmp=RefStatus2_2018_w_nan[_st:_end]
+			RefPowerConsume2_2018_w_nan_tmp=RefPowerConsume2_2018_w_nan[_st:_end]
+			
+			### Estimation based on Statistical method
+			X1 = []
+			X2 = []
+			Y1 = []
+			Y2 = []
+			TermNum = 96
+			for i in range(TermNum, len(RefStatus1_2018_w_nan_tmp),TermNum):
+				X1.append(RefStatus1_2018_w_nan_tmp[i-TermNum:i])
+				X2.append(RefStatus2_2018_w_nan_tmp[i-TermNum:i])
+				Y1.append(RefPowerConsume1_2018_w_nan_tmp[i-TermNum:i])
+				Y2.append(RefPowerConsume2_2018_w_nan_tmp[i-TermNum:i])
+
+			xTrain1, xTest1, yTrain1, yTest1 = train_test_split(X1, Y1, test_size=0.1, shuffle =False)
+			xTrain2, xTest2, yTrain2, yTest2 = train_test_split(X2, Y2, test_size=0.1, shuffle =False)
+
+			Y_tmp1=[]
+			Y_tmp2=[]
+			for i in range(len(xTrain1)):
+				for j in range(TermNum):
+					if xTrain1[i][j] == 1:
+						Y_tmp1.append(yTrain1[i][j])
+					if xTrain2[i][j] == 1:
+						Y_tmp2.append(yTrain2[i][j])
+
+			mean_RefConsume1=np.mean(Y_tmp1)      # 냉동기1 전력량 평균
+			mean_RefConsume2=np.mean(Y_tmp2)      # 냉동기2 전력량 평균
+			
+			##############################################################################################
+			##############################################################################################
+
+			WFTemperature_Date, WFTemperatureMax_w_nan, WFTemperatureMin_w_nan, WFTemperatureMean_w_nan, DataCountMat_WFTemperature = detect_unknown_duplicated_zero_data_for_WeatherForecast3h(rawWFTemperature, startday, lastday, DayPeriod)
+			WFHumidity_Date, WFHumidityMax_w_nan, WFHumidityMin_w_nan, WFHumidityMean_w_nan, DataCountMat_WFHumidity = detect_unknown_duplicated_zero_data_for_WeatherForecast3h(rawWFHumidity, startday, lastday, DayPeriod)
+
+			RawDate = ChillerCalAmount_Date
+			
+			## 축열조 상태 변수 - 제빙운전:10, 축단운전:20, 병렬운전:30, 냉단운전:40, OFF:0
+			Icing=10
+			StorageOnly=20
+			Parallel=30
+			ChillerOnly=40
+			Off=0
+			ChillerStatus=[]
+			for i in range(len(ChStatusIcing_Date)):
+				if ChStatusIcing_w_nan[i]==1:
+					ChillerStatus.append(Icing)
+				elif ChStatusDeicing_w_nan[i]==1:
+					ChillerStatus.append(StorageOnly)
+				elif ChStatusParallel_w_nan[i]==1:
+					ChillerStatus.append(Parallel)
+				elif ChStatusRefOnly_w_nan[i]==1:
+					ChillerStatus.append(ChillerOnly)
+				elif ChStatusIcing_w_nan[i]==0 or ChStatusDeicing_w_nan[i]==0 or ChStatusParallel_w_nan[i]==0 or ChStatusRefOnly_w_nan[i]==0:
+					ChillerStatus.append(Off)
+				else:
+					ChillerStatus.append(np.nan)
+
+			## 축/방열량에 대해서 두가지 변수를 생성한다.
+			## 첫번쨰는 사용자에게 상대적 열량을 보여주기 위해 0 < Q < max(Q) 사이의 값으로 구성된 열량
+			## 두번쨰는 실질적 계산을 위해서 NaN이 포함된 날은 제외하고 학습하므로 NaN 구간의 축/방열량은 0으로 가정하고 산출
+			## 축적 열량의 최대치 (정격용량) = 3060 USRT (=10,924.2 kW)일 때 100%
+			max_q_accum_kWh = 3060*3.57
+			q_accum_kWh=[0]
+			nan_cnt=0
+			nan_point=[]
+			for i in range(len(ChillerStatus)):
+				if math.isnan(ChillerStatus[i]):    # Nan의 경우 축열량을 0이라고 가정하고 진행
+					q_accum_kWh.append(q_accum_kWh[-1])
+					nan_cnt += 1
+					nan_point.append(i)
+				else:
+					if ChillerStatus[i] == Icing and BrineInletTemperature_w_nan[i] < BrineMixedTemperature_w_nan[i]:
+						q_accum_kWh.append(q_accum_kWh[-1] + (BrineFlowAmount_w_nan[i]*0.06*1.042*3.14*0.28*0.25)*(BrineMixedTemperature_w_nan[i]-BrineInletTemperature_w_nan[i]))
+					elif ChillerStatus[i] == StorageOnly and BrineInletTemperature_w_nan[i] > BrineMixedTemperature_w_nan[i]:
+						q_accum_kWh.append(q_accum_kWh[-1] - (BrineFlowAmount_w_nan[i]*0.06*1.042*3.14*0.28*0.25)*(BrineInletTemperature_w_nan[i]-BrineMixedTemperature_w_nan[i]))
+					elif ChillerStatus[i] == Parallel and BrineInletTemperature_w_nan[i] > BrineMixedTemperature_w_nan[i]:
+						q_accum_kWh.append(q_accum_kWh[-1] - (BrineFlowAmount_w_nan[i]*0.06*1.042*3.14*0.28*0.25)*(BrineInletTemperature_w_nan[i]-BrineMixedTemperature_w_nan[i]))
+					else: #ChillerStatus[i] == Off or ChillerStatus[i] == ChillerOnly:
+						q_accum_kWh.append(q_accum_kWh[-1])
+										
+					if q_accum_kWh[-1] < 0:
+						q_accum_kWh[-1] = 0
+					elif q_accum_kWh[-1] > max_q_accum_kWh:
+						q_accum_kWh[-1] = max_q_accum_kWh
+									
+					if nan_cnt > 48:
+						print('[Warning] Too many nan points exist (48 points sequentially)')
+					nan_cnt = 0
+
+			q_accum_kWh = q_accum_kWh[1:len(q_accum_kWh)]
+			q_accum_percent=[]
+			for i in range(len(q_accum_kWh)):
+				q_accum_percent.append((q_accum_kWh[i]/max_q_accum_kWh)*100)
+
+			CalAmount_prev = q_accum_percent[:len(q_accum_percent)-96]		## DB에 비어있는 이전 축열량이 있다면 채워주기 위함
+			
+			#################### Calculate the Gradient on Each Operation Mode ########################
+			cnt_nan=0
+			CalAmount_wo_nan=[]
+			ChillerStatus_wo_nan=[]
+			RefStatus1_wo_nan=[]
+			RefStatus2_wo_nan=[]
+			RefStatus_wo_nan=[]
+
+			## 1: off,off, 2: on,off, 3: on,on
+			for i in range(len(q_accum_percent)):
+				if not np.isnan(q_accum_percent[i]) and not np.isnan(ChillerStatus[i]) and not np.isnan(RefStatus1_w_nan[i]) and not np.isnan(RefStatus2_w_nan[i]):
+					CalAmount_wo_nan.append(q_accum_percent[i])
+					ChillerStatus_wo_nan.append(ChillerStatus[i])
+					RefStatus1_wo_nan.append(RefStatus1_w_nan[i])
+					RefStatus2_wo_nan.append(RefStatus2_w_nan[i])
+					RefStatus_wo_nan.append(RefStatus1_w_nan[i]+RefStatus2_w_nan[i])
+					cnt_nan=0
+				else:
+					CalAmount_wo_nan.append(CalAmount_wo_nan[-1])
+					ChillerStatus_wo_nan.append(0)
+					RefStatus1_wo_nan.append(0)
+					RefStatus2_wo_nan.append(0)
+					RefStatus_wo_nan.append(0)
+					cnt_nan+=1
+					if cnt_nan>12:
+						cnt_nan=0
+						# print('There are many unknown data!')
+			
+			# 학습용 데이터로 사용
+			train_size = int(len(ChillerStatus_wo_nan))
+			## 나머지를 검증용 데이터로 사용
+			## test_size = len(ChillerStatus_wo_nan) - train_size
+
+			trainStatus = np.array(ChillerStatus_wo_nan[0:train_size])
+			trainCalAmount = np.array(CalAmount_wo_nan[0:train_size])
+			trainRefStatus1 = np.array(RefStatus1_wo_nan[0:train_size])
+			trainRefStatus2 = np.array(RefStatus2_wo_nan[0:train_size])
+			
+
+			GradientCalAmount_mode_Icing = []
+			GradientCalAmount_mode_StorageOnly = []
+			GradientCalAmount_mode_Parallel = []
+			GradientCalAmount_mode_ChillerOnly = []
+			isNan_Point = False
+			for i in range(len(trainStatus)):
+				for j in range(len(nan_point)):
+					if i == nan_point[j]:
+						isNan_Point=True
+						break
+				if not isNan_Point:
+					if trainStatus[i] == Icing and trainCalAmount[i] > trainCalAmount[i-1] and trainRefStatus1[i] == 1 and trainRefStatus2[i] == 1:
+						GradientCalAmount_mode_Icing.append(trainCalAmount[i]-trainCalAmount[i-1])
+					elif trainStatus[i] == StorageOnly and trainCalAmount[i] < trainCalAmount[i-1]:
+						GradientCalAmount_mode_StorageOnly.append(trainCalAmount[i]-trainCalAmount[i-1])
+					elif trainStatus[i] == Parallel and trainCalAmount[i] < trainCalAmount[i-1] and (trainRefStatus1[i] == 1 or trainRefStatus2[i] == 1):
+						GradientCalAmount_mode_Parallel.append(trainCalAmount[i]-trainCalAmount[i-1])
+					elif trainStatus[i] == ChillerOnly and trainRefStatus1[i] == 1 and trainRefStatus2[i] == 1:
+						GradientCalAmount_mode_ChillerOnly.append(trainCalAmount[i]-trainCalAmount[i-1])
+				isNan_Point = False
+				
+			GradientCalAmount_w3sigma_mode_Icing = []
+			if len(GradientCalAmount_mode_Icing) != 0:
+				max3sigma_mode_Icing = np.mean(GradientCalAmount_mode_Icing)+np.std(GradientCalAmount_mode_Icing)*3
+				min3sigma_mode_Icing = np.mean(GradientCalAmount_mode_Icing)-np.std(GradientCalAmount_mode_Icing)*3
+
+			GradientCalAmount_w3sigma_mode_StorageOnly = []
+			if len(GradientCalAmount_mode_StorageOnly) != 0:
+				max3sigma_mode_StorageOnly = np.mean(GradientCalAmount_mode_StorageOnly)+np.std(GradientCalAmount_mode_StorageOnly)*3
+				min3sigma_mode_StorageOnly = np.mean(GradientCalAmount_mode_StorageOnly)-np.std(GradientCalAmount_mode_StorageOnly)*3
+
+			GradientCalAmount_w3sigma_mode_Parallel = []
+			if len(GradientCalAmount_mode_Parallel) != 0:
+				max3sigma_mode_Parallel = np.mean(GradientCalAmount_mode_Parallel)+np.std(GradientCalAmount_mode_Parallel)*3
+				min3sigma_mode_Parallel = np.mean(GradientCalAmount_mode_Parallel)-np.std(GradientCalAmount_mode_Parallel)*3
+
+			GradientCalAmount_w3sigma_mode_ChillerOnly = []
+			if len(GradientCalAmount_mode_ChillerOnly) != 0:
+				max3sigma_mode_ChillerOnly = np.mean(GradientCalAmount_mode_ChillerOnly)+np.std(GradientCalAmount_mode_ChillerOnly)*3
+				min3sigma_mode_ChillerOnly = np.mean(GradientCalAmount_mode_ChillerOnly)-np.std(GradientCalAmount_mode_ChillerOnly)*3
+
+				
+			for i in range(len(GradientCalAmount_mode_Icing)):
+				if GradientCalAmount_mode_Icing[i] <= max3sigma_mode_Icing and GradientCalAmount_mode_Icing[i] >= min3sigma_mode_Icing:
+					GradientCalAmount_w3sigma_mode_Icing.append(GradientCalAmount_mode_Icing[i])
+					
+			for i in range(len(GradientCalAmount_mode_StorageOnly)):
+				if GradientCalAmount_mode_StorageOnly[i] <= max3sigma_mode_StorageOnly and GradientCalAmount_mode_StorageOnly[i] >= min3sigma_mode_StorageOnly:
+					GradientCalAmount_w3sigma_mode_StorageOnly.append(GradientCalAmount_mode_StorageOnly[i])
+					
+			for i in range(len(GradientCalAmount_mode_Parallel)):
+				if GradientCalAmount_mode_Parallel[i] <= max3sigma_mode_Parallel and GradientCalAmount_mode_Parallel[i] >= min3sigma_mode_Parallel:
+					GradientCalAmount_w3sigma_mode_Parallel.append(GradientCalAmount_mode_Parallel[i])
+					
+			for i in range(len(GradientCalAmount_mode_ChillerOnly)):
+				if GradientCalAmount_mode_ChillerOnly[i] <= max3sigma_mode_ChillerOnly and GradientCalAmount_mode_ChillerOnly[i] >= min3sigma_mode_ChillerOnly:
+					GradientCalAmount_w3sigma_mode_ChillerOnly.append(GradientCalAmount_mode_ChillerOnly[i])
+						
+			#print(np.mean(GradientCalAmount_w3sigma_mode_Icing), np.mean(GradientCalAmount_w3sigma_mode_StorageOnly), np.mean(GradientCalAmount_w3sigma_mode_Parallel), np.mean(GradientCalAmount_w3sigma_mode_ChillerOnly))
+			#print(np.std(GradientCalAmount_w3sigma_mode_Icing), np.std(GradientCalAmount_w3sigma_mode_StorageOnly), np.std(GradientCalAmount_w3sigma_mode_Parallel), np.std(GradientCalAmount_w3sigma_mode_ChillerOnly))
+
+			print('************ (Finish) Load & pre-processing data !! ************')
+			print('****************************************************************')
+			#######################################################################################
+
+
+			############################################################################################################
+			#################### Prediction for the Degree of Daily Deicing ############################################
+			## 	매일 21시~21시 15분 사이에 산출 및 DB 삽입
+			
+			if (now.hour == 21 and (now.minute > 0 or now.minute < 16)) or Init:
+
+				print('************ (Start) The Degree of Daily Deicing is being predicted!! ************')
+				DailyDeicingAmount = []
+				DailyDeicingAmount_kWh = []
+				idx = 0
+				
+				if isRecent and now.hour < 21:	## 21시를 전, 후로 익일 예상 방냉량이 업데이트
+					_DayPeriod = DayPeriod-1
+				else:
+					_DayPeriod = DayPeriod
+				for i in range(_DayPeriod):
+					tmpAmount = []
+					tmpAmount_kWh = []
+					
+					if i == 0:
+						time_length = 4*21 # 첫번째 날은 저녁 9시까지 방냉량만 산출
+					else:
+						time_length = 96
+					for time_idx in range(time_length):    
+						if q_accum_percent[idx] > q_accum_percent[idx+1]:
+							tmpAmount.append(q_accum_percent[idx]-q_accum_percent[idx+1])
+							tmpAmount_kWh.append(q_accum_kWh[idx]-q_accum_kWh[idx+1])
+						idx += 1
+					if len(tmpAmount) > 0:
+						DailyDeicingAmount.append(sum(tmpAmount))
+						DailyDeicingAmount_kWh.append(sum(tmpAmount_kWh))
+					else:
+						DailyDeicingAmount.append(0)
+						DailyDeicingAmount_kWh.append(0)
+
+				DateinDay=[]
+				for k in range(_DayPeriod):
+					DateinDay.append(RawDate[k*DataRes_96])
+				DoW, DayType = getDayType(DateinDay, _DayPeriod, SpecialHoliday)
+
+				# Collect the normal data
+				X = []
+				Y = []
+				_isnan = False
+
+				for i in range(_DayPeriod):
+					if DayType[i][0] < 3 and DailyDeicingAmount[i] > 0:     ## 평일이면서 축열조를 가동하고 결측값이 없는 날만 추출
+						if i == _DayPeriod-1:
+							time_len = int(len(ChillerStatus)%96)
+						else:							
+							time_len = DataRes_96
+						for j in range(time_len):
+							if math.isnan(ChillerStatus[i*DataRes_96+j]):
+								_isnan = True
+						if not _isnan:
+							X.append([WFTemperatureMax_w_nan[i], WFTemperatureMin_w_nan[i], WFTemperatureMean_w_nan[i], WFHumidityMax_w_nan[i], WFHumidityMin_w_nan[i], WFHumidityMean_w_nan[i]])
+							Y.append(DailyDeicingAmount[i])
+						_isnan = False
+					
+				xTrain, xVal, yTrain, yVal = train_test_split(X, Y, test_size=0.001, shuffle = False)
+				xTomorrow_WF = [WFTemperatureMax_w_nan[_DayPeriod], WFTemperatureMin_w_nan[_DayPeriod],WFTemperatureMean_w_nan[_DayPeriod], WFHumidityMax_w_nan[_DayPeriod], WFHumidityMin_w_nan[_DayPeriod], WFHumidityMean_w_nan[_DayPeriod]]
+				#MSE의 변화를 확인하기 위하여 앙상블의 크기 범위에서 랜덤 포레스트 트레이닝
+				maeOos = []
+				Acc_CVRMSE = []
+				Acc_MBE = []
+				nTreeList = range(100, 200, 50)
+				for iTrees in nTreeList:
+					depth = None
+					maxFeat = np.matrix(X).shape[1] #조정해볼 것
+					DailyDeicing_RFModel = ensemble.RandomForestRegressor(n_estimators=iTrees,
+									max_depth=depth, max_features=maxFeat,
+									oob_score=False, random_state=42)
+					DailyDeicing_RFModel.fit(xTrain, yTrain)
+					#데이터 세트에 대한 MSE 누적
+					prediction = DailyDeicing_RFModel.predict(xVal)
+					
+					maeOos.append(MAE(yVal, prediction))
+					Acc_MBE.append(MBE(yVal, prediction))
+					Acc_CVRMSE.append(CVRMSE(np.array(yVal), np.array(prediction)))
+				#print('prediction', prediction)
+				#print('yVal', yVal)
+					
+				#print("Validation Set of MAE : ",maeOos[-1])
+				#print("Validation Set of CVRMSE : ", CVRMSE(yVal, prediction))
+				#print("Validation Set of Aver. CVRMSE : ", np.mean(Acc_CVRMSE))
+
+				PredictedDeIcingAmount = DailyDeicing_RFModel.predict([xTomorrow_WF])    ## 학습모델을 통한 익일 방냉량 예측
+				PredictedDeIcingAmount_Tomorrow = round(PredictedDeIcingAmount[0],6)
+				print('####################################################')
+				print('## Estimated daily Deicing amount = ', PredictedDeIcingAmount_Tomorrow, ' % ##')
+				print('####################################################')
+				
+				#### 익일 방냉량 DB 삽입
+				### Day-ahead deicing amount is updated everyday
+				# MSSQL Access
+				conn = pymssql.connect(host = targetDBIP, user = targetDBUserID, password = targetDBUserPW, database = targetDBName, autocommit=True)
+				# Create Cursor from Connection
+				cursor = conn.cursor()
+						
+				if now.hour >= 21:			
+					TargetDate = datetime.datetime(now.year,now.month,now.day,21,0,0) + datetime.timedelta(days=1)
+				else:
+					TargetDate = datetime.datetime(now.year,now.month,now.day,21,0,0)
+				
+				## Storage deicing amount
+				cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsMonitoringPointForecastingDayAhead where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 0 and TargetDateTime = '" + TargetDate.strftime('%Y-%m-%d %H:00:00') + "' order by CreatedDateTime desc")	
+								
+				# 데이타 하나씩 Fetch하여 출력
+				row = cursor.fetchone()
+				rawData=[]
+				while row:
+					row = cursor.fetchone()
+					rawData.append(row)	
+				if rawData:			
+					try:
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsMonitoringPointForecastingDayAhead set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', ForecastedValue = " + str(PredictedDeIcingAmount_Tomorrow) + " where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 0 and TargetDateTime = '"+ TargetDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+						
+						print("* The prediction of Daily deicing amount was updated!! (Recommend)")
+					except:
+						print("[ERROR] There is an update error!! (Daily deicing amount)")
+				else:
+					try:
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointForecastingDayAhead (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,ForecastedValue) VALUES(1,3,4478,0,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TargetDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(PredictedDeIcingAmount_Tomorrow) + ")" )
+						
+						print("* The prediction of daily deicing amount was inserted!! (Recommend)")
+					except:
+						print("[ERROR] There is an insert error!! (Daily deicing amount)")
+				
+								
+				print('************ (Finish) The Degree of Daily Deicing is being predicted!! ************')
+				print('***********************************************************************************')
+				#######################################################################################
+
+
+			##################################################################################################################################################
+			################# Find Optimal Operating Schedule for predicted daily deicing amount #############################################################
+			## 	15분 주기로 현상태 반영하여 업데이트
+			
+			print('************ (Start) Recommended operating schedule is being found!! ************')
+			
+			if now.hour >= 0 and now.hour < 21:
+				simul_lth = 24*4 - (now.hour*4 + int(now.minute/15)) - 3*4 	## (15분 단위 카운트)
+			else:
+				simul_lth = 24*4 - (now.hour*4 +int(now.minute/15)) + 21*4
+			# 이미 지난 시간(전날 9 pm 이후)에 대한 데이터 정리
+			inputX_prev = ChillerStatus_wo_nan[len(ChillerStatus_wo_nan)-(96-simul_lth):len(ChillerStatus_wo_nan)]
+			inputX_REF1_prev = RefStatus1_wo_nan[len(RefStatus1_wo_nan)-(96-simul_lth):len(RefStatus1_wo_nan)]
+			inputX_REF2_prev = RefStatus2_wo_nan[len(RefStatus2_wo_nan)-(96-simul_lth):len(RefStatus2_wo_nan)]
+			RecommendedCalAmount_prev = CalAmount_wo_nan[len(CalAmount_wo_nan)-(96-simul_lth):len(CalAmount_wo_nan)]
+			
+			print('* Current Amount : ', CalAmount_wo_nan[-1], '[%], ', 'Estimated Deicing Amount : ', PredictedDeIcingAmount_Tomorrow, '[%]')
+			idx = 0
+			TermNum = 96
+			RecommendedCalAmount = [CalAmount_wo_nan[-1]]
+			
+			if now.hour >= 21 or now.hour < 6:
+				while RecommendedCalAmount[-1] < PredictedDeIcingAmount_Tomorrow:
+					idx += 1
+					if idx >= simul_lth:
+						print("* It should be fully operated")
+						break
+					inputX = []
+					inputX_REF1 = []
+					inputX_REF2 = []
+					## 단순히 심야 운전만 고려하고 축냉량 시 제빙모드와 OFF만 고려하여 시뮬레이션 (다른 모드를 추가하여 구성할 수 있음) 
+					## Off=0, Icing = 10, StorageOnly = 20, Parallel = 30, ChillerOnly = 40
+					## 추천 방냉은 저녁 9시 이후부터 아침 6시 사이까지.... 중간에 사용하고 있는 부분에 대한 것은 어떻게 처리할지...고민해야함...낮에 축단운전을 하기에....
+					for i in range(idx):
+						inputX.append(Icing)
+						inputX_REF1.append(1)
+						inputX_REF2.append(1)
+					for i in range(simul_lth-len(inputX)):
+						inputX.append(0)
+						inputX_REF1.append(0)
+						inputX_REF2.append(0)
+						
+					RecommendedCalAmount = [CalAmount_wo_nan[-1]]
+					for i in range(len(inputX)):
+						if i == 1:
+							RecommendedCalAmount = RecommendedCalAmount[-1]
+						if inputX[i]==Icing:
+							if inputX_REF1[i] + inputX_REF2[i]==2:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_Icing))
+							elif inputX_REF1[i] + inputX_REF2[i]==1:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_Icing)/2)
+							else:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1])
+
+						elif inputX[i]==StorageOnly:
+							RecommendedCalAmount.append(RecommendedCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_StorageOnly))
+
+						elif inputX[i]==Parallel:
+							if inputX_REF1[i] + inputX_REF2[i]==2:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_Parallel)*2)
+							elif inputX_REF1[i] + inputX_REF2[i]==1:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_Parallel))
+							else:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1])
+
+						elif inputX[i]==ChillerOnly:
+							if inputX_REF1[i] + inputX_REF2[i]==2:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_ChillerOnly))
+							elif inputX_REF1[i] + inputX_REF2[i]==1:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_ChillerOnly)/2)
+							else:
+								RecommendedCalAmount.append(RecommendedCalAmount[-1])
+
+						elif inputX[i]==0:
+							RecommendedCalAmount.append(RecommendedCalAmount[-1])
+
+						## 0이나 100을 넘어갔을 경우 보정 (현재 데이터에서 축열량은 % 단위이기 때문에)
+						if RecommendedCalAmount[-1] >= 100:
+							RecommendedCalAmount[-1] = 100
+						elif RecommendedCalAmount[-1] <= 0:
+							RecommendedCalAmount[-1] = 0
+					#print('max.',np.max(RecommendedCalAmount[-1]))
+					
+			else:
+				print("************ It is not time to operate the storage in icing mode ")
+				
+			if idx == 0:
+				inputX = []
+				inputX_REF1 = []
+				inputX_REF2 = []
+				RecommendedCalAmount = []
+				for i in range(simul_lth):
+					inputX.append(0)
+					inputX_REF1.append(0)
+					inputX_REF2.append(0)
+					RecommendedCalAmount.append(CalAmount_wo_nan[-1])
+			inputX = inputX_prev + inputX
+			inputX_REF1 = inputX_REF1_prev + inputX_REF1
+			inputX_REF2 = inputX_REF2_prev + inputX_REF2
+			RecommendedCalAmount = RecommendedCalAmount_prev + RecommendedCalAmount
+					
+			#### 실제 및 추천 운전 스케쥴 DB 삽입 
+			#### Recommended operating schedule is updated everyday
+			# MSSQL Access
+			conn = pymssql.connect(host = targetDBIP, user = targetDBUserID, password = targetDBUserPW, database = targetDBName, autocommit=True)
+			# Create Cursor from Connection
+			cursor = conn.cursor()
+			
+			# Execute SQL
+			if now.hour >= 21:
+				InitDate = datetime.datetime(now.year,now.month,now.day,21,0,0)
+			else:
+				InitDate = datetime.datetime(now.year,now.month,now.day,21,0,0)-datetime.timedelta(days=1)
+				
+			## Storage mode
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 16 and SimulationCase = 0 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:00:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by CreatedDateTime desc")
+			
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			rawData=[]
+			while row:
+				row = cursor.fetchone()
+				rawData.append(row)
+			if rawData:			
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(inputX[i]) + " where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 16 and SimulationCase = 0 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+						
+					print("* The storage operating schedule was updated!! (Recommend)")
+				except:
+					print("[ERROR] There is an update error!! (Ice storage mode)")
+			else:
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,3,4478,16,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(inputX[i]) + ", 0)" )
+						
+					print("* The storage operating schedule was inserted!! (Recommend)")
+				except:
+					print("[ERROR] There is an insert error!! (Ice storage mode)")
+			
+			## REF1 status
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 15 and SimulationCase = 0 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:00:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by CreatedDateTime desc")		
+			
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			rawData=[]
+			while row:
+				row = cursor.fetchone()
+				rawData.append(row)	
+			if rawData:			
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(inputX_REF1[i]) + " where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 15 and SimulationCase = 0 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+									
+					print("* The refrigerator1 status was updated!! (Recommend)")
+				except:
+					print("[Error] There is an update error!! (Recommended refrigerator1 status)")
+			else:
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,2,4479,15,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(inputX_REF1[i]) + ", 0)" )
+						
+					print("* The refrigerator1 status was inserted!! (Recommend)")
+				except:
+					print("[Error] There is an insert error!! (Recommended refrigerator1 status)")
+			
+			## REF1 power consume
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 11 and SimulationCase = 0 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:00:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by CreatedDateTime desc")		
+			
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			rawData=[]
+			while row:
+				row = cursor.fetchone()
+				rawData.append(row)	
+			if rawData:			
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						if inputX_REF1[i]==1:
+							TmpComsume = mean_RefConsume1
+						else:
+							TmpComsume = 0
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(TmpComsume) + " where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 11 and SimulationCase = 0 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+										
+					print("* The recommended refrigerator1 power was updated!! (Recommend)")
+				except:
+					print("[ERROR] There is an update error!! (Recommended refrigerator1 power)")
+			else:
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						if inputX_REF1[i]==1:
+							TmpComsume = mean_RefConsume1
+						else:
+							TmpComsume = 0
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,2,4479,11,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(TmpComsume) + ", 0)" )
+						
+					print("* The recommended refrigerator1 power was inserted!! (Recommend)")
+				except:
+					print("[ERROR] There is an insert error!! (Recommended refrigerator1 power)")
+					
+			## REF2 status
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 15 and SimulationCase = 0 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:00:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by CreatedDateTime desc")	
+						
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			rawData=[]
+			while row:
+				row = cursor.fetchone()
+				rawData.append(row)	
+			if rawData:			
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(inputX_REF2[i]) + " where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 15 and SimulationCase = 0 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+						
+					print("* The refrigerator2 status was updated!! (Recommend)")
+				except:
+					print("[ERROR] There is an update error!! (Recommended refrigerator2 status)")
+			else:
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,2,4480,15,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(inputX_REF2[i]) + ", 0)" )
+						
+					print("* The refrigerator2 status was inserted!! (Recommend)")
+				except:
+					print("[ERROR] There is an insert error!! (Recommended refrigerator2 status)")
+				
+			## REF2 power consume
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 11 and SimulationCase = 0 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:00:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by CreatedDateTime desc")		
+			
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			rawData=[]
+			while row:
+				row = cursor.fetchone()
+				rawData.append(row)	
+			if rawData:			
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						if inputX_REF2[i]==1:
+							TmpComsume = mean_RefConsume2
+						else:
+							TmpComsume = 0
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(TmpComsume) + " where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 11 and SimulationCase = 0 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+						
+					print("* The recommended refrigerator2 power was updated!! (Recommend)")
+				except:
+					print("[ERROR] There is an update error!! (Recommended Refrigerator2 power)")
+			else:
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						if inputX_REF2[i]==1:
+							TmpComsume = mean_RefConsume2
+						else:
+							TmpComsume = 0
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,2,4480,11,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(TmpComsume) + ", 0)" )
+						
+					print("* The refrigerator2 power was inserted!! (Recommend)")
+				except:
+					print("[ERROR] There is an insert error!! (Recommended Refrigerator2 power)")
+			
+			## Thermal energy amount
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 2 and SimulationCase = 0 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:00:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by CreatedDateTime desc")
+			
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			rawData=[]
+			while row:
+				row = cursor.fetchone()
+				rawData.append(row)
+			if rawData:			
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(RecommendedCalAmount[i]) + " where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 2 and SimulationCase = 0 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+						
+					print("* Thermal energy amount was updated!! (Recommend)")
+				except:
+					print("[ERROR] There is an update error!! (Recommended thermal energy amount)")
+			else:
+				try:
+					for i in range(TermNum):
+						TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,3,4478,2,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(RecommendedCalAmount[i]) + ", 0)" )
+						
+					print("* Thermal energy amount was inserted!! (Recommend)")
+				except:
+					print("[ERROR] There is an insert error!! (Recommended thermal energy amount)")
+			
+			## 첫 실행시에만 동작
+			if Init:
+				## Thermal energy amount (과거 확인 후 축열량이 공백인 경우 채워주기)
+				CalAmount_prev_tmp = CalAmount_prev[len(CalAmount_prev)-TermNum*5:]
+				for d in range(5, 0, -1):		# 5일전까지
+					InitDate_tmp = InitDate-datetime.timedelta(days=d)
+					
+					for m in range(TermNum):	# 1열씩 업데이트 (중간중간 공백인 경우를 고려)
+						TmpDate = InitDate_tmp + datetime.timedelta(minutes=m*15)
+						cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 2 and SimulationCase = 0 and TargetDateTime = '" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "' order by CreatedDateTime desc")
+						
+						# 데이타 하나씩 Fetch하여 출력
+						row = cursor.fetchone()
+						if row:			
+							try:
+								cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ (datetime.datetime.now()-datetime.timedelta(minutes=d)).strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(CalAmount_prev_tmp[(5-d)*TermNum+m]) + " where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 2 and SimulationCase = 0 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+								
+							except:
+								print("[ERROR] There is an update error!! (Recommended thermal energy amount)")
+						else:
+							try:
+								cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,3,4478,2,'" + (datetime.datetime.now()-datetime.timedelta(minutes=d)).strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(CalAmount_prev_tmp[(5-d)*TermNum+m]) + ", 0)" )
+								
+							except:
+								print("[ERROR] There is an insert error!! (Recommended thermal energy amount)")
+									
+			conn.close()
+			
+			print('************ (Finish) Recommended operating schedule is being found!! ************')
+			print('**********************************************************************************')
+			#######################################################################################
+
+
+		##################################################################################################################################################
+		################# Stochastic method for estimating the Variation of Ice Thermal Storage based on Operation Mode "for Simulation" #################
+		#### 사용자 정의 데이터를 데이터 로드
+		### 계속 체킹
+		
+		while True:
+			now_ = datetime.datetime.now().now()
+			## sleep 매분 2,6,10,... 초에만 동작
+			if now_.second%4==2:
+				break
+			time.sleep(1)
+							
+		#time.sleep(2)
+		#print('start time : ', now_)	
+		# MSSQL Access
+		conn = pymssql.connect(host = targetDBIP, user = targetDBUserID, password = targetDBUserPW, database = targetDBName, autocommit=True)
+		# Create Cursor from Connection
+		cursor = conn.cursor()
+
+		# Execute SQL 
+		cursor.execute('SELECT TOP 1 * FROM '+targetDBName+'.dbo.BemsIceThermalStorageSimulation where SiteId=1 and FacilityCode=4478 and PropertyId=16 and SimulationCase=1 order by CreatedDateTime desc')
+		
+		
+		row = cursor.fetchone()
+		conn.close()
+		#print('end time : ', now_)	
+		if Init:
+			if row != None:				
+				recentDateTime = row[4]
+			else:
+				recentDateTime = now_
+			Init = False
+			ActiveSimulator = False
+		if row != None:
+			if recentDateTime < row[4]:
+				recentDateTime =  row[4]
+				ActiveSimulator = True
+			else:
+				ActiveSimulator = False
+				
+		now_ = datetime.datetime.now().now()
+		if now_.second%30 > 0 and now_.second%30 < 2:
+			print('* Keep an eye on updating DB table every 2 seconds ... (This message appears every 30 seconds)')
+		
+		if ActiveSimulator:	
+			print('************ (Start) Simulator! ************')
+			time.sleep(2)
+			# MSSQL Access
+			conn = pymssql.connect(host = targetDBIP, user = targetDBUserID, password = targetDBUserPW, database = targetDBName, autocommit=True)
+			# Create Cursor from Connection
+			cursor = conn.cursor()
+			# Execute SQL
+			InitDate = datetime.datetime(now.year, now.month, now.day, now.hour, int(int(now.minute/15)*15),0)
+			
+			## Storage mode
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 16 and SimulationCase = 1 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:%M:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by TargetDateTime asc")			
+			# 데이타 한꺼번에 Fetch
+			rows = cursor.fetchall()			
+			rawData_StorageMode = []
+			for i in rows:
+				rawData_StorageMode.append(i)
+				
+			time.sleep(1)	
+			## REF1 status
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 15 and SimulationCase = 1 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:%M:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by TargetDateTime asc")			
+			# 데이타 한꺼번에 Fetch
+			rows = cursor.fetchall()			
+			rawData_RefStatus1 = []
+			for i in rows:
+				rawData_RefStatus1.append(i)
+							
+			# rawData_RefStatus1=rawData_RefStatus1[:len(rawData_RefStatus1)-1]
+			#rawData_RefStatus1=rawData_RefStatus1[:-2]
+						
+			time.sleep(1)
+			## REF2 status
+			cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 15 and SimulationCase = 1 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:%M:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by TargetDateTime asc")
+			# 데이타 한꺼번에 Fetch
+			rows = cursor.fetchall()
+			rawData_RefStatus2 = []
+			for i in rows:
+				rawData_RefStatus2.append(i)
+			# rawData_RefStatus2=rawData_RefStatus2[:len(rawData_RefStatus2)-1]
+			# rawData_RefStatus2=rawData_RefStatus2[:-2]
+							
+			CustomizedStatus=[]
+			for i in range(len(rawData_StorageMode)):
+				CustomizedStatus.append(rawData_StorageMode[i][6])
+			
+			CustomizedRefStatus1=[]
+			for i in range(len(rawData_RefStatus1)):
+				CustomizedRefStatus1.append(rawData_RefStatus1[i][6])
+			
+			CustomizedRefStatus2 = []
+			for i in range(len(rawData_RefStatus2)):
+				CustomizedRefStatus2.append(rawData_RefStatus2[i][6])
+				
+			# 한번 더 데이터 불러오기 (가끔 제대로 로드 안되는 경우 있음)
+			time.sleep(0.5)
+			if len(CustomizedStatus) != len(CustomizedRefStatus1):
+				## REF1 status
+				cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 15 and SimulationCase = 1 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:%M:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by TargetDateTime asc")			
+				# 데이타 한꺼번에 Fetch
+				rows = cursor.fetchall()			
+				rawData_RefStatus1 = []
+				for i in rows:
+					rawData_RefStatus1.append(i)
+				
+				CustomizedRefStatus1=[]
+				for i in range(len(rawData_RefStatus1)):
+					CustomizedRefStatus1.append(rawData_RefStatus1[i][6])
+				
+			time.sleep(0.5)
+			if len(CustomizedStatus) != len(CustomizedRefStatus2):
+				## REF2 status
+				cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 15 and SimulationCase = 1 and TargetDateTime >= '" + InitDate.strftime('%Y-%m-%d %H:%M:00') + "' and TargetDateTime < '" + (InitDate + datetime.timedelta(days=1)).strftime('%Y-%m-%d %H:00:00') + "'  order by TargetDateTime asc")
+				# 데이타 한꺼번에 Fetch
+				rows = cursor.fetchall()
+				rawData_RefStatus2 = []
+				for i in rows:
+					rawData_RefStatus2.append(i)
+				
+				CustomizedRefStatus2 = []
+				for i in range(len(rawData_RefStatus2)):
+					CustomizedRefStatus2.append(rawData_RefStatus2[i][6])
+			
+			
+			SimulCalAmount=[CalAmount_wo_nan[-1]]
+			for i in range(len(CustomizedStatus)):
+				if i == 1:
+					SimulCalAmount = [SimulCalAmount[-1]]
+				## 제빙운전은 두대로 운영되었으므로 평균값은 2대 운전 기준
+				if CustomizedStatus[i] == Icing:
+					if len(GradientCalAmount_w3sigma_mode_Icing) == 0:
+						print('[Warning] There is no enough data (Icing)')
+						SimulCalAmount.append(SimulCalAmount[-1])
+					else:
+						if CustomizedRefStatus1[i] + CustomizedRefStatus2[i] == 2:
+							SimulCalAmount.append(SimulCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_Icing))
+						elif CustomizedRefStatus1[i] + CustomizedRefStatus2[i] == 1:
+							SimulCalAmount.append(SimulCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_Icing)/2)
+						else:
+							SimulCalAmount.append(SimulCalAmount[-1])
+				## 축단운전은 냉동기가 운영되지 않음
+				elif CustomizedStatus[i] == StorageOnly:
+					if len(GradientCalAmount_w3sigma_mode_StorageOnly) == 0:
+						print('[Warning] There is no enough data (Storage Only)')
+						SimulCalAmount.append(SimulCalAmount[-1])
+					else:
+						SimulCalAmount.append(SimulCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_StorageOnly))
+				## 병렬운전에서 축열조 변화량은 냉동기 상태와 상관없음
+				elif CustomizedStatus[i] == Parallel:
+					if len(GradientCalAmount_w3sigma_mode_Parallel) == 0:
+						print('[Warning] There is no enough data (Parallel)')
+						SimulCalAmount.append(SimulCalAmount[-1])
+					else:
+						SimulCalAmount.append(SimulCalAmount[-1]+np.mean(GradientCalAmount_w3sigma_mode_Parallel))
+				## 냉단운전은 냉동기 두대로 운영되었으므로 축열량은 그대로
+				elif CustomizedStatus[i] == ChillerOnly:
+					if len(GradientCalAmount_w3sigma_mode_ChillerOnly) == 0:
+						print('[Warning] There is no enough data (Chiller Only)')
+						SimulCalAmount.append(SimulCalAmount[-1])
+					else:
+						SimulCalAmount.append(SimulCalAmount[-1])
+				elif CustomizedStatus[i]==0:
+					SimulCalAmount.append(SimulCalAmount[-1])
+
+				if SimulCalAmount[-1] > 100:
+					SimulCalAmount[-1] = 100
+					CustomizedRefStatus1[i] = 0
+					CustomizedRefStatus2[i] = 0
+				elif SimulCalAmount[-1] < 0:
+					SimulCalAmount[-1] = 0
+					CustomizedRefStatus1[i] = 0
+					CustomizedRefStatus2[i] = 0
+							
+			
+			#### 시뮬레이션 결과 데이터 DB 삽입 	
+			## Thermal energy amount
+			for i in range(len(CustomizedStatus)):
+				TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+				cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 2 and SimulationCase = 1 and TargetDateTime = '" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "' ")
+				
+				# 데이타 하나씩 Fetch하여 출력
+				row = cursor.fetchone()
+				if row:
+					try:
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(SimulCalAmount[i]) + " where SiteId = 1 and FacilityTypeId = 3 and FacilityCode = 4478 and PropertyId = 2 and SimulationCase = 1 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+						
+						if i == len(CustomizedStatus)-1:
+							print("* Thermal energy amount was updated!! (Simul)")
+					except:
+						print("[ERROR] There is an update error!! (Simulated thermal energy amount)")
+					
+				else:
+					try:
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,3,4478,2,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(SimulCalAmount[i]) + ", 1)" )
+						
+						if i == len(CustomizedStatus)-1:
+							print("* Thermal energy amount was inserted!! (Simul)")
+					except:
+						print("[ERROR] There is an insert error!! (Simulated thermal energy amount)")
+					
+				
+			
+			## REF1 power consume			
+			for i in range(len(CustomizedStatus)):
+				TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+				cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 11 and SimulationCase = 1 and TargetDateTime = '" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "' ")	
+				
+				if CustomizedRefStatus1[i]==1:
+					TmpComsume = mean_RefConsume1
+				else:
+					TmpComsume = 0	
+				# 데이타 하나씩 Fetch하여 출력
+				row = cursor.fetchone()
+				if row:
+					try:
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(TmpComsume) + " where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4479 and PropertyId = 11 and SimulationCase = 1 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+						
+						if i == len(CustomizedStatus)-1:
+							print("* The REF1 power comsumption was updated!! (Simul)")
+					except:
+						print("[ERROR] There is an update error!! (Simulated refrigerator1 power)")
+					
+				else:
+					try:
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,2,4479,11,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(TmpComsume) + ", 1)" )
+						
+						if i == len(CustomizedStatus)-1:
+							print("* The REF1 power comsumption was inserted!! (Simul)")
+					except:
+						print("[ERROR] There is an insert error!! (Simulated refrigerator1 power)")		
+			
+			## REF2 power consume	
+					
+			for i in range(len(CustomizedStatus)):
+				TmpDate = InitDate + datetime.timedelta(minutes=i*15)
+				cursor.execute("SELECT * FROM " + targetDBName + ".dbo.BemsIceThermalStorageSimulation where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 11 and SimulationCase = 1 and TargetDateTime = '" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "' ")
+				
+				if CustomizedRefStatus2[i]==1:
+					TmpComsume = mean_RefConsume2
+				else:
+					TmpComsume = 0	
+				# 데이타 하나씩 Fetch하여 출력
+				row = cursor.fetchone()
+				if row:
+					try:
+						cursor.execute("UPDATE " + targetDBName + ".dbo.BemsIceThermalStorageSimulation set CreatedDateTime = '"+ datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') +"', SimulationValue = " + str(TmpComsume) + " where SiteId = 1 and FacilityTypeId = 2 and FacilityCode = 4480 and PropertyId = 11 and SimulationCase = 1 and TargetDateTime = '"+ TmpDate.strftime('%Y-%m-%d %H:%M:00')+"'")
+						
+						if i == len(CustomizedStatus)-1:
+							print("* The REF2 power comsumption was updated!! (Simul)")
+					except:
+						print("[ERROR] There is an update error!! (Simulated refrigerator2 power)")
+					
+				else:
+					try:
+						cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsIceThermalStorageSimulation (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,SimulationValue,SimulationCase) VALUES(1,2,4480,11,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + TmpDate.strftime('%Y-%m-%d %H:%M:00') + "', "+ str(TmpComsume) + ", 1)" )
+						
+						if i == len(CustomizedStatus)-1:
+							print("* The REF2 power comsumption was inserted!! (Simul)")					
+					except:
+						print("[ERROR] There is an insert error!! (Simulated refrigerator2 power)")
+			
+			conn.close()
+			print('************ (Finish) Simulator! ************')
+			print('*********************************************')
+			#######################################################################################
+			
+		
+
+
+

+ 639 - 0
RealTimeSimulator_Load_Forecasting.py

@@ -0,0 +1,639 @@
+# # Day-ahead load forecasting
+# 
+# DB : MS SQL
+# 
+# Program Language : Python
+#
+# kgpark@hdc-icontrols.com
+# April 10, 2020
+
+# ### BEMS 데이터 수집 메카니즘
+# #### 데이터 별로 수집 타입에 따라 다르지만, Raw 테이블에 적산 값으로 저장이 되고 15min 테이블에서 해당 시간대와 그 전 시간대의 차이 값을 입력한다.
+# #### DGW 혹은 시스템에 이상이 생겼을 때, 데이터가 들어오지 않거나 0으로 입력된다.
+# #### 1시간 테이블은 15분 테이블에서 각 15분, 30분, 45분, 60분의 데이터 합산 값이 나왔다.
+# #### 합산 값으로 저장되다보니 4개 포인트 중 적어도 하나만 있어도 1시간 데이터로 저장이 된다.
+# #### 따라서, 15분 데이터를 전처리하는 것이 주효하고 데이터가 없거나 0값을 검출하여 비정상 데이터로 추정하는 것을 추천한다.
+# #### 또한, 1시간 단위로 데이터 주기를 변환한다면 15분 테이블의 4개 포인트 중 하나라도 값을 모른다면 그 시간의 데이터가 비정상이라고 가정하는 것을 추천한다.
+
+
+import matplotlib.pyplot as plt
+import pymssql
+import datetime
+import numpy as np
+import math
+from korean_lunar_calendar import KoreanLunarCalendar
+import calendar
+import configparser
+import sys
+import time
+
+
+# ## Define functions
+### Define day-type 
+def getDayName(year, month, day):
+	return ['MON','TUE','WED','THU','FRI','SAT','SUN'][datetime.date(year, month, day).weekday()]
+def getDayType(DateinDay, Period, SpecialHoliday):
+	DoW=[];    # Day of Week
+	for i in range(Period):
+		if DateinDay[i].year==2019 and DateinDay[i].month==5 and DateinDay[i].day==18:
+			DoW.append([5, DateinDay[i]])
+		elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'MON':
+			DoW.append([1, DateinDay[i]])
+		elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'TUE':
+			DoW.append([2, DateinDay[i]])
+		elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'WED':
+			DoW.append([3, DateinDay[i]])
+		elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'THU':
+			DoW.append([4, DateinDay[i]])
+		elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'FRI':
+			DoW.append([5, DateinDay[i]])
+		elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'SAT':
+			DoW.append([6, DateinDay[i]])
+		elif getDayName(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day) == 'SUN':
+			DoW.append([7, DateinDay[i]])
+
+		for j in range(len(SpecialHoliday)):
+			if SpecialHoliday[j] == datetime.date(DateinDay[i].year,DateinDay[i].month,DateinDay[i].day):
+				DoW[-1][0] = 8
+				break
+    ### W:1, N:2, ### W: Workday, N: Non-workday
+	DayType=[]
+	for i in range(Period):
+		if DoW[i][0] <= 5:
+			DayType.append([1, DateinDay[i]])
+		elif DoW[i][0] > 5:
+			DayType.append([2, DateinDay[i]])
+	return DoW, DayType  
+
+
+def Reconstruction(DayType, DatainHour, mark, DataRes, isRecent):
+	ReconstructedData=[]    
+	DayType1h=[]
+	Day_len = len(DayType)
+
+	# Rearrange data in hour unit
+	for i in range(Day_len):
+		if i == Day_len-1 and isRecent:
+			Time_len = len(DatainHour) - i*DataRes
+		else:
+			Time_len=DataRes
+		for j in range(Time_len):
+			DayType1h.append([DatainHour[i*DataRes + j], DayType[i][0], datetime.datetime(DayType[i][1].year, DayType[i][1].month, DayType[i][1].day, j, 0)])       ## data, daytype, time
+		
+	# 비정상 데이터보다 앞선 시간의 데이터 중 DayType이 같고 시간이 같은 5개 날 데이터의 평균으로 복원함
+	for i in reversed(range(len(DayType1h))):
+		AccData=[]
+		cnt=0
+		if math.isnan(DayType1h[i][0]):
+			for j in range(len(DayType1h)):
+				if cnt > 5:    
+					break
+				if i < j and DayType1h[j][1] == DayType1h[i][1] and DayType1h[j][2].hour == DayType1h[i][2].hour and (not math.isnan(DayType1h[j][0])):
+					AccData.append(DayType1h[j][0])
+					cnt += 1
+			DayType1h[i][0] = np.mean(AccData)
+		ReconstructedData.append(DayType1h[i][0])
+	ReconstructedData.reverse()
+
+	### Double-checking for the data which is not reconstructed, especially in front
+	for i in range(len(DayType1h)):
+		AccData=[]
+		cnt=0
+		if math.isnan(DayType1h[i][0]):
+			#print('Here is NaN!!',ReconstructedData[i],i,DayType1h[i][2].hour, DayType1h[i][1])
+			for j in reversed(range(len(DayType1h))):
+				if cnt > 5:    
+					break
+				if i > j and DayType1h[j][1] == DayType1h[i][1] and DayType1h[j][2].hour == DayType1h[i][2].hour and (not math.isnan(DayType1h[j][0])):
+					AccData.append(DayType1h[j][0])
+					cnt += 1
+			ReconstructedData[i] = np.mean(AccData)
+	return ReconstructedData, DayType1h
+
+## For day-ahead linear prediction
+def lpc_pred_DayAhead(Data_trn, DayType_trn, cov_lth, DayType_tst, DataRes):
+	# Calculating the filter bank for each hour and day-type using traing set
+	for c_w in range(1,3):
+		DayType_trn[0,0]=0
+		CP_pred_fb=np.zeros(Data_trn.shape)
+		lpc_fb=np.zeros([cov_lth[c_w-1],DataRes])
+		Prv_A=[]
+		Prv_A=np.transpose(Data_trn[DataRes-cov_lth[c_w-1]:DataRes,np.where(DayType_trn == c_w)[0]-1])
+		for hr_i in range(24):
+			lpc_fb[:,hr_i]=np.dot(np.linalg.pinv(Prv_A), np.transpose(Data_trn[hr_i,np.where(DayType_trn == c_w)[0]]))
+
+		if c_w == 1:
+			lpc_fb1=lpc_fb
+		elif c_w == 2:
+			lpc_fb2=lpc_fb
+
+	## For testing
+	if DayType_tst[0,0] == 1:
+		lpc_t=lpc_fb1
+	elif DayType_tst[0,0] == 2:
+		lpc_t=lpc_fb2
+
+	Data_tt=Data_trn[:,-1]
+	# Load prediction for test day based on the filter bank
+	CP_pred=np.transpose(np.dot(np.transpose(Data_tt[DataRes-cov_lth[DayType_tst[0,0]-1]:DataRes+1]),lpc_t))
+	return CP_pred
+
+## For step-ahead linear prediction
+def lpc_pred_OneStepAhead(Data_trn, DayType_trn, cov_lth, DayType_tst, DataRes):
+	for c_w in range(1,3):
+		DayType_trn[0,0]=0
+		lpc_fb=np.zeros([cov_lth[c_w-1],DataRes])
+		Prv_A=[]
+		Prv_A=np.transpose(Data_trn[DataRes-cov_lth[c_w-1]:DataRes,np.where(DayType_trn == c_w)[0]-1])
+		lpc_fb=np.dot(np.linalg.pinv(Prv_A), np.transpose(Data_trn[0,np.where(DayType_trn == c_w)[0]]))
+
+		if c_w == 1:
+			lpc_fb1=lpc_fb
+		elif c_w == 2:
+			lpc_fb2=lpc_fb
+	## Testing
+	if DayType_tst[0,0] == 1:
+		lpc_t=lpc_fb1
+	elif DayType_tst[0,0] == 2:
+		lpc_t=lpc_fb2
+		
+	Data_tt=Data_trn[:,-1]
+	CP_pred=np.transpose(np.dot(np.transpose(Data_tt[DataRes-cov_lth[DayType_tst[0,0]-1]:DataRes+1]),lpc_t))
+	return CP_pred
+
+## Measure
+def MAPE(y_observed, y_pred):
+	return np.mean(np.abs((y_observed - y_pred) / y_observed)) * 100
+def MAE(y_observed, y_pred):
+	return np.mean(np.abs(y_observed - y_pred))
+def MBE(y_observed, y_pred):
+	return (np.sum((y_observed - y_pred))/(len(y_observed)*np.mean(y_observed)))*100
+def CVRMSE(y_observed, y_pred):
+	return (np.sqrt(np.mean((y_observed - y_pred)*(y_observed - y_pred)))/np.mean(y_observed))*100
+
+## Check for normal time stamp
+def Check_AlivedTimeStamp(RawData, ComparedData, idx_raw, idx_comp):
+	if datetime.date(RawData[idx_raw][4].year,RawData[idx_raw][4].month,RawData[idx_raw][4].day) == datetime.date(ComparedData[idx_comp].year, ComparedData[idx_comp].month, ComparedData[idx_comp].day) and datetime.time(RawData[idx_raw][4].hour,RawData[idx_raw][4].minute) == datetime.time(ComparedData[idx_comp].hour, ComparedData[idx_comp].minute):
+		isAlived = True
+	else:
+		isAlived = False
+	return isAlived
+
+if __name__ == "__main__" :
+
+	## Check every hour on the hour operating infinite loop
+	while True:
+		now = datetime.datetime.now().now()
+		
+		## distinguish real time update and specific day
+		## 자정에 생기는 인덱싱 문제로 0시에는 16분에 업데이트
+		if (now.hour != 0 and now.minute == 1) or (now.hour == 0 and now.minute == 16):
+			PredctionActive = True
+		else:
+			PredctionActive = False
+			if now.second > 55:
+				print("[ Current Time -", now.hour,":", now.minute,":", now.second,"], " "Sleeping for 30 seconds... Prediction starts every hour")
+				time.sleep(30)
+			else:
+				print("[ Current Time -", now.hour,":", now.minute,":", now.second,"], " "Sleeping for 60 seconds... Prediction starts every hour")
+				time.sleep(60)
+		
+		if PredctionActive:
+		
+			## Loading .ini file
+			myINI = configparser.ConfigParser()
+			myINI.read("Config.ini", "utf-8" )
+			# MSSQL Access
+			conn = pymssql.connect(host=myINI.get('LocalDB_Info','ip_address'), user=myINI.get('LocalDB_Info','user_id'), password=myINI.get('LocalDB_Info','user_password'), database=myINI.get('LocalDB_Info','db_name'))
+			# Create Cursor from Connection
+			cursor = conn.cursor()			
+
+			# Execute SQL (Electric consumption)
+			cursor.execute('SELECT * FROM BemsConfigData where SiteId = 1')
+			rowDB_info = cursor.fetchone()
+			
+			conn.close()
+			
+			loadDBIP = rowDB_info[1]
+			loadDBUserID = rowDB_info[2]
+			loadDBUserPW = rowDB_info[3]
+			loadDBName = rowDB_info[4]
+			targetDBIP = rowDB_info[5]
+			targetDBUserID = rowDB_info[6]
+			targetDBUserPW = rowDB_info[7]
+			targetDBName = rowDB_info[8]
+			linearFilterLength = rowDB_info[10]
+			
+			print("=================== Prediction start! ===================")
+			
+			startday = datetime.date(int(rowDB_info[9].year), int(rowDB_info[9].month), int(rowDB_info[9].day))
+			
+			# ## Data accumulation
+			isRecent = True
+			lastday = datetime.date(now.year, now.month, now.day)
+			if startday < datetime.date(2017,1,1):
+				print('[ERROR] 데이터 최소 시작 시점은 2017.01.01 입니다')
+			elif startday > lastday:
+				print('[ERROR] 예측 타깃 시작시점이 데이터 시작 시점보다 작을 수 없습니다')
+				
+			now_ = datetime.date(now.year, now.month, now.day)
+			# 학습데이터의 기간은 최대 2년으로 한정
+			if (startday-now_).days > 730:
+				Ago_2year = now_ + timedelta(days=-730)
+				startday = datetime.date(Ago_2year.year, Ago_2year.month, Ago_2year.day)
+			
+			# MSSQL Access
+			conn = pymssql.connect(host = loadDBIP, user = loadDBUserID, password = loadDBUserPW, database = loadDBName)
+
+			# Create Cursor from Connection
+			cursor = conn.cursor()
+
+			# Execute SQL (Electric consumption)
+			cursor.execute('SELECT * FROM BemsMonitoringPointHistory15min where SiteId = 1 and FacilityTypeId = 99 and FacilityCode = 4863 and PropertyId = 1 order by CreatedDateTime desc')
+
+			# 데이타 하나씩 Fetch하여 출력
+			row = cursor.fetchone()
+			DataRes_org=96
+			DataRes_24=24
+
+			rawData=[]
+			while row:
+				row = cursor.fetchone()
+				if datetime.date(row[4].year,row[4].month,row[4].day) < startday:
+					break
+				rawData.append(row)
+			rawData.reverse()   # 오름차순 정렬   
+			
+			# 연결 끊기
+			conn.close()
+			print('rawData',rawData[0],rawData[-1])
+			# 현장 데이터가 없을 경우 예외처리
+			if now.hour == 0:
+				hour_calib = 0
+			else:
+				hour_calib = 1
+			if datetime.datetime(now.year, now.month, now.day, now.hour, 0, 0) - datetime.timedelta(hours=hour_calib) == datetime.datetime(rawData[-1][4].year, rawData[-1][4].month, rawData[-1][4].day, rawData[-1][4].hour, 0, 0):
+			
+				# MSSQL Access
+				conn = pymssql.connect(host = loadDBIP, user = loadDBUserID, password = loadDBUserPW, database = loadDBName)
+				# Create Cursor from Connection
+				cursor = conn.cursor()
+				# SQL문 실행 (정기휴일)
+				cursor.execute('SELECT * FROM CmHoliday where SiteId = 1 and IsUse = 1')
+
+				# 데이타 하나씩 Fetch하여 출력
+				row = cursor.fetchone()
+				regularHolidayData = [row]
+				while row:
+					row = cursor.fetchone()
+					regularHolidayData.append(row)
+				regularHolidayData = regularHolidayData[0:-1]
+
+				# SQL문 실행 (비정기휴일)
+				cursor.execute('SELECT * FROM CmHolidayCustom where SiteId = 1 and IsUse = 1')
+
+				# 데이타 하나씩 Fetch하여 출력
+				row = cursor.fetchone()
+				suddenHolidayData = [row]
+				while row:
+					row = cursor.fetchone()
+					suddenHolidayData.append(row)
+				suddenHolidayData = suddenHolidayData[0:-1]
+				
+				# 연결 끊기
+				conn.close()
+				
+				# 공휴일의 음력 계산 
+				calendar_convert = KoreanLunarCalendar()
+				SpecialHoliday = []
+				for i in range(lastday.year-startday.year+1):
+					for j in range(len(regularHolidayData)):
+						if regularHolidayData[j][3] == 1:
+							if regularHolidayData[j][1] == 12 and regularHolidayData[j][2] == 30: ## 설 하루 전 연휴 계산을 위함
+								calendar_convert.setLunarDate(startday.year+i-1, regularHolidayData[j][1], regularHolidayData[j][2], False)
+								SpecialHoliday.append(datetime.date(int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[0]), int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[1]), int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[2])))
+							else:
+								calendar_convert.setLunarDate(startday.year+i, regularHolidayData[j][1], regularHolidayData[j][2], False)
+								SpecialHoliday.append(datetime.date(int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[0]), int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[1]), int(calendar_convert.SolarIsoFormat().split(' ')[0].split('-')[2])))
+						else:
+							SpecialHoliday.append(datetime.date(startday.year+i,regularHolidayData[j][1],regularHolidayData[j][2]))
+
+				for i in range(len(suddenHolidayData)):
+					if suddenHolidayData[i][1].year >= startday.year:
+						SpecialHoliday.append(datetime.date(suddenHolidayData[i][1].year, suddenHolidayData[i][1].month, suddenHolidayData[i][1].day))
+
+				SpecialHoliday=list(set(SpecialHoliday))
+				DayPeriod = (lastday - startday).days + 1
+				print('First day:',startday,',', 'Last Day:', lastday,',','Current Time:', now)
+				print('Day period :', DayPeriod)
+				
+				# ## Find unkown/zero data (Bad data)
+
+				StartTime = datetime.datetime(int(startday.strftime('%Y')), int(startday.strftime('%m')), int(startday.strftime('%d')), 0, 0, 0)
+				TimeStamp_DayUnit = []
+				StandardTimeStamp = []
+				# Create normal time stamp 
+				for idx_day in range(DayPeriod):
+					TimeStamp_DayUnit.append(startday + datetime.timedelta(days=idx_day))
+					if isRecent and idx_day == DayPeriod-1:
+						if now.hour == 0:		# 예외처리용 (자정에 Day count가 안되는 현상)
+							tmp_len = 1
+						else:
+							tmp_len = now.hour*4 + int(now.minute/15)
+						for idx_time in range(tmp_len):
+							StandardTimeStamp.append(StartTime)
+							StartTime += datetime.timedelta(minutes = 15)
+					else:
+						for idx_time in range(DataRes_org):
+							StandardTimeStamp.append(StartTime)
+							StartTime += datetime.timedelta(minutes = 15)
+
+				RawDate=[]         # raw data (date)
+				RawElectricLoad=[]    # raw data (electric load)
+				for i in range(len(rawData)):
+					if datetime.date(rawData[i][4].year,rawData[i][4].month,rawData[i][4].day) >= startday:
+						if datetime.date(rawData[i][4].year,rawData[i][4].month,rawData[i][4].day) <= lastday:
+							RawDate.append(rawData[i][4])
+							RawElectricLoad.append(rawData[i][5])
+						if datetime.date(rawData[i][4].year,rawData[i][4].month,rawData[i][4].day) > lastday:
+							break
+
+				Data_len=len(RawDate)
+				if isRecent:
+					DataAct_len = (DayPeriod-1)*DataRes_org + now.hour*4 + int(now.minute/15)
+				else:
+					DataAct_len = DayPeriod*DataRes_org
+				### Unknown/zero data counts
+				DataCount=[]
+				for i in range(len(TimeStamp_DayUnit)):
+					cnt_unk=0   # For Unknown data count
+					cnt_zero=0   # zero data count
+					for j in range(Data_len):
+						if TimeStamp_DayUnit[i] == datetime.date(RawDate[j].year,RawDate[j].month,RawDate[j].day):
+							cnt_unk += 1
+							if RawElectricLoad[j] == 0:
+								cnt_zero += 1
+					if isRecent and i==len(TimeStamp_DayUnit)-1:
+						DataCount.append([TimeStamp_DayUnit[i], now.hour*4 + int(now.minute/15) - cnt_unk, cnt_zero])        
+					else:
+						DataCount.append([TimeStamp_DayUnit[i], DataRes_org-cnt_unk, cnt_zero])
+						
+				## Visualization
+				## 월 인덱스 설정 ##
+				idxCal=[]
+				idxCalName=[]
+				idxCal.append(0)
+				for y_idx in range(lastday.year - startday.year + 1):
+					if startday.year == lastday.year:
+						for m_idx in range(lastday.month - startday.month + 1):
+							month = startday.month + m_idx
+							idxCal.append(idxCal[-1] + calendar.monthrange(startday.year, month)[1])
+							idxCalName.append(calendar.month_name[month])
+					else:
+						if y_idx == 0:  ## 첫번째 해
+							for m_idx in range(13-startday.month):
+								month = startday.month + m_idx
+								idxCal.append(idxCal[-1] + calendar.monthrange(startday.year, month)[1])
+								idxCalName.append(calendar.month_name[month])
+						elif y_idx !=0 and y_idx == lastday.year - startday.year: ## 마지막 해        
+							for m_idx in range(lastday.month):
+								month = m_idx + 1
+								idxCal.append(idxCal[-1] + calendar.monthrange(lastday.year, month)[1])
+								idxCalName.append(calendar.month_name[month])
+						else: 
+							for m_idx in range(12):
+								month = m_idx + 1
+								idxCal.append(idxCal[-1] + calendar.monthrange(startday.year+y_idx, month)[1])
+								idxCalName.append(calendar.month_name[month])      
+
+				DataCountMat=np.matrix(DataCount)
+				
+				print("The number of unknown data:",sum(DataCountMat[:,1]), ", The number of zero data:", sum(DataCountMat[:,2]))
+				
+				plt.figure(figsize=(16,9))
+				plt.subplot(311)
+				plt.plot(DataCountMat[:,1],label='Unknown data', linewidth = 2)
+				plt.plot(DataCountMat[:,2],label='Zero data', linewidth = 2)
+	#			plt.xlabel('Months', fontsize = 16)
+				plt.ylabel('Data counts', fontsize = 14)
+				plt.legend(loc='upper left', fontsize = 14)
+				plt.title("Unknown/zero electric load data per 15min. unit ("+str(startday.year)+"."+str(startday.month)+"."+str(startday.day)+" - "+str(lastday.year)+"."+str(lastday.month)+"."+str(lastday.day)+")", fontsize = 14)
+				plt.xlim(idxCal[0], idxCal[-1])
+				plt.xticks(idxCal, idxCalName, fontsize=6.5)
+				plt.yticks(fontsize=14)
+				
+				print("Bad data detection complete!")
+
+				### NaN-padding after finding unknown data
+				######## 현재 DB 특성상 값이 0으로 찍히거나 시간테이블의 행 자체가 없는 경우가 있고, 이 데이터 1 step 앞뒤로 데이터가 비정상일 확률이 높으므로 비정상데이터 뿐만 아니라 앞뒤 1 step까지 nan으로 처리함
+
+				ElectricLoad_Un_ZP=[]
+				RawDate=[]
+				idx=0
+				idx2=0
+				isBadData = False
+				
+				for i in range(DataAct_len): 
+					if datetime.date(rawData[idx][4].year,rawData[idx][4].month,rawData[idx][4].day) >= startday and datetime.date(rawData[idx][4].year,rawData[idx][4].month,rawData[idx][4].day) <= lastday:
+						RawDate.append(StandardTimeStamp[idx2])
+						if isBadData == True:
+							ElectricLoad_Un_ZP.append(np.nan)        
+							isBadData=False
+						elif rawData[idx][5]==0:
+							ElectricLoad_Un_ZP[-1]=np.nan
+							ElectricLoad_Un_ZP.append(np.nan)
+							if rawData[idx+1][5] > 0 and Check_AlivedTimeStamp(rawData, StandardTimeStamp, idx+1, idx2+1):
+								isBadData = True
+						elif Check_AlivedTimeStamp(rawData, StandardTimeStamp, idx, idx2):
+							ElectricLoad_Un_ZP.append(rawData[idx][5])
+						else:            
+							ElectricLoad_Un_ZP[-1]=np.nan
+							ElectricLoad_Un_ZP.append(np.nan)
+							if rawData[idx+1][5] > 0 and Check_AlivedTimeStamp(rawData, StandardTimeStamp, idx+1, idx2+1):
+								isBadData = True
+							idx -= 1
+						idx2 += 1
+					idx += 1
+					
+				print('NaN-padding complete!')
+
+
+			# ## Decimation to 1-hour period
+				ElectricLoad_1h = []
+				for i in range(DayPeriod):
+					if i == DayPeriod-1 and isRecent:
+						Time_len = DataAct_len - i*DataRes_org + 1
+					else:
+						Time_len = DataRes_org
+					isNaN=False
+					for j in range(Time_len):
+						if ElectricLoad_Un_ZP[i*4 + j] == np.nan:
+							isNaN=True
+						if j%4==3:
+							if isNaN:
+								ElectricLoad_1h.append(np.nan)
+							else:
+								ElectricLoad_1h.append(sum(ElectricLoad_Un_ZP[i*DataRes_org + j-3:i*DataRes_org + j+1]))
+
+				print('Decimation to 1hour complete!')
+				
+				# ## Data reconstruction using similar-day approach
+				DateinDay=[]
+				for k in range(DayPeriod):
+					DateinDay.append(RawDate[k*DataRes_org])
+
+				DoW, DayType = getDayType(DateinDay, DayPeriod, SpecialHoliday)
+
+			# Find the similar-day and reconstructed data
+				marking=np.nan
+				ReconstructedData, DayType1h = Reconstruction(DayType, ElectricLoad_1h, marking, DataRes_24, isRecent)
+				
+				plt.subplot(312)
+				plt.plot(ReconstructedData, '*-', label='Reconstructed data',linewidth=3)
+				plt.plot(ElectricLoad_1h, '--',  label='Raw data',linewidth=3)
+				plt.legend(loc='upper right', fontsize = 14)
+				plt.ylabel('Power [kW]', fontsize = 14)
+				plt.yticks(fontsize=14)
+				plt.xticks([0],fontsize=14)
+				plt.xlim((DayPeriod-10)*24, DayPeriod*24)
+				plt.title('Raw & reconstructed data in the latest 10 days',fontsize=14)
+				
+				print('Reconstruct complete!')
+
+
+			# ## Day-ahead load forecasting
+			####### Convert to matrix
+				ReconstructedData_Arr=np.zeros((DataRes_24, DayPeriod))
+				for i in range(DayPeriod):
+					if isRecent and i==DayPeriod-1:
+						for j in range(len(ReconstructedData)%DataRes_24):
+							ReconstructedData_Arr[j,i]=ReconstructedData[i*DataRes_24+j]        
+					else:
+						for j in range(DataRes_24):
+							ReconstructedData_Arr[j,i]=ReconstructedData[i*DataRes_24+j]
+
+				trn_period=DayPeriod - 1
+				DayType_m=np.matrix(DayType)
+				Data_trn=ReconstructedData_Arr[:,0:trn_period]
+				Data_tst=ReconstructedData_Arr[:,trn_period]
+				DayType_trn=DayType_m[0:trn_period,:]
+				DayType_tst=DayType_m[trn_period,:]
+				cov_lth=np.array([int(linearFilterLength.split(',')[0]),int(linearFilterLength.split(',')[1])])
+				y_pred_dayAhead = lpc_pred_DayAhead(Data_trn, DayType_trn, cov_lth, DayType_tst, DataRes_24)
+				print('-------------------------Day-ahead prediction result-------------------------')
+				if isRecent:
+					if now.hour == 0:
+						print('MAPE :', MAPE(Data_tst[0],y_pred_dayAhead[0]), 'MAE :', MAE(Data_tst[0],y_pred_dayAhead[0]))
+					else:
+						print('MAPE :', MAPE(Data_tst[0:now.hour],y_pred_dayAhead[0:now.hour]), 'MAE :', MAE(Data_tst[0:now.hour],y_pred_dayAhead[0:now.hour]))
+				else:
+					print('MAPE :', MAPE(Data_tst,y_pred_dayAhead),'MAE :', MAE(Data_tst,y_pred_dayAhead))
+					print('MBE :', MBE(Data_tst,y_pred_dayAhead), 'CVRMSE :', CVRMSE(Data_tst,y_pred_dayAhead)) 
+				print('-------------------------------------------------------------------------------')
+
+			# ## One-step-ahead load forecasting
+				y_pred_oneStep=[]
+				Data_tst_oneStep=[]
+				if isRecent:
+					dayHour = now.hour + 1        
+				else:
+					dayHour = DataRes_24
+				for i in range(dayHour):
+					####### Convert to matrix
+					ReconstructedData_tmp=ReconstructedData[i:]
+					if isRecent:
+						for ii in range(DataRes_24-i):
+							ReconstructedData_tmp.append(np.nan)    
+					for ii in range(i):
+						ReconstructedData_tmp.append(np.nan)
+					ReconstructedData_Arr_oneStep=np.zeros((DataRes_24, DayPeriod))
+					for j in range(DayPeriod):
+						for k in range(DataRes_24):
+							ReconstructedData_Arr_oneStep[k,j]=ReconstructedData_tmp[j*DataRes_24+k]
+					Data_trn=ReconstructedData_Arr_oneStep[:,0:trn_period]
+					if isRecent:
+						Data_tst_oneStep.append(ReconstructedData_Arr_oneStep[i,trn_period])
+					else:
+						Data_tst_oneStep=ReconstructedData_Arr[:,trn_period]
+					y_pred_oneStep.append(lpc_pred_OneStepAhead(Data_trn, DayType_trn, cov_lth, DayType_tst, DataRes_24))
+					
+				print('-------------------------OneStep-ahead prediction result-------------------------')
+				if isRecent:
+					if now.hour == 0:
+						print('MAPE :', MAPE(Data_tst[0],y_pred_oneStep[0]), 'MAE :', MAE(Data_tst[0],y_pred_oneStep[0]))
+					else:
+						print('MAPE :', MAPE(Data_tst[0:now.hour],y_pred_oneStep[0:now.hour]), 'MAE :', MAE(Data_tst[0:now.hour],y_pred_oneStep[0:now.hour]))
+				else:
+					print('MAPE :', MAPE(Data_tst_oneStep,y_pred_oneStep),'MAE :', MAE(Data_tst_oneStep,y_pred_oneStep))
+				print('-------------------------------------------------------------------------------')
+
+				plt.subplot(313)
+				plt.grid(b=True, which='both',axis='y')
+				if isRecent:
+					plt.plot(ReconstructedData_Arr[0:now.hour,trn_period], label='Observed data', linewidth=3)
+				else:
+					plt.plot(ReconstructedData_Arr[:,trn_period], label='Observed data', linewidth=3)    
+				plt.plot(y_pred_dayAhead, '--', label='Day-ahead Prediction', linewidth=3)
+				plt.plot(y_pred_oneStep, '*-.', label='OneStep-ahead Prediction', MarkerSize=10, linewidth=3)
+				plt.xlabel('Time [hour]', fontsize = 14)
+				plt.ylabel('Power [kW]', fontsize = 14)
+				plt.legend(loc='upper right', fontsize = 14)
+				plt.xticks([6,12,18,24],['6','12','18','24'], fontsize = 14)
+				plt.yticks(fontsize = 14)
+				plt.ylim(min(ReconstructedData)*0.9,max(ReconstructedData)*1.1)
+				if isRecent:
+					plt.title("Electric load forecasting on "+str(now.year)+"."+str(now.month)+"."+str(now.day)+" (Updated every hour) - DGB 2nd branch", fontsize=14)
+				else:
+					plt.title("Electric load forecasting on "+str(lastday.year)+"."+str(lastday.month)+"."+str(lastday.day)+" (Updated every hour) - DGB 2nd branch", fontsize=14)
+				#plt.show()
+				print("=================== Prediction was successfully finished! ===================")
+				fig = plt.gcf()
+				if isRecent:
+					# Save the figure file of result
+					# fig.savefig("Result of electric load forecasting on "+str(now.year)+"."+str(now.month)+"."+str(now.day)+" "+str(now.hour)+"h"+str(now.minute)+"m - DGB 2nd branch.png", dpi=fig.dpi)
+						
+					### One-hour-ahead load forecasting updated every 1 minute
+					# MSSQL Access
+					conn = pymssql.connect(host = targetDBIP, user = targetDBUserID, password = targetDBUserPW, database = targetDBName)
+					# Create Cursor from Connection
+					cursor = conn.cursor()				
+					cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointForecastingHourAhead (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,ForecastedValue) VALUES(1,99,4863,1,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','"+ datetime.datetime(now.year,now.month,now.day,now.hour,0,0).strftime('%Y-%m-%d %H:00:00') + "',"+str(y_pred_oneStep[-1])+")")
+					
+					## Insert data temporary 
+					if now.hour==0:
+						try:
+							cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointHistoryHourly (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,CurrentValue) VALUES(1,99,4863,1,'" + (datetime.datetime(now.year,now.month,now.day,now.hour,0,0) - datetime.timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00') + "',"+str(ReconstructedData_Arr[23,trn_period-1])+")")
+							conn.commit()
+						except:
+							print('Hour-ahead forecasted data already exists! (' + (datetime.datetime(now.year,now.month,now.day,now.hour,0,0) - datetime.timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00') + ')')
+					
+					else:
+						try:
+							cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointHistoryHourly (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,CurrentValue) VALUES(1,99,4863,1,'" + (datetime.datetime(now.year,now.month,now.day,now.hour,0,0) - datetime.timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00') + "',"+str(ReconstructedData_Arr[now.hour-1,trn_period])+")")
+							conn.commit()
+						except:
+							print('Hour-ahead forecasted data already exists! (' + (datetime.datetime(now.year,now.month,now.day,now.hour,0,0) - datetime.timedelta(hours=1)).strftime('%Y-%m-%d %H:00:00') + ')')
+																
+					### Day-ahead load forecasting updated every midnight
+					if now.hour == 0:
+						# Create Cursor from Connection
+						cursor = conn.cursor()				
+						for i in range(len(y_pred_dayAhead)):
+							try:
+								cursor.execute("INSERT INTO " + targetDBName + ".dbo.BemsMonitoringPointForecastingDayAhead (SiteId,FacilityTypeId,FacilityCode,PropertyId,CreatedDateTime,TargetDateTime,ForecastedValue) VALUES(1,99,4863,1,'" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "','" + (datetime.datetime(now.year,now.month,now.day,0,0,0) + datetime.timedelta(hours=i)).strftime('%Y-%m-%d %H:00:00') + "'," + str(y_pred_dayAhead[i]) + ")")
+								conn.commit()
+							except:
+								print('Day-ahead forecasted data already exists! ('+(datetime.datetime(now.year,now.month,now.day,0,0,0) + datetime.timedelta(hours=i)).strftime('%Y-%m-%d %H:00:00')+')')
+							
+					conn.close()
+					print("The result was saved!")
+				else:
+					fig.savefig("Result of electric load forecasting on "+str(lastday.year)+"."+str(lastday.month)+"."+str(lastday.day)+ "- DGB 2nd branch.png", dpi=fig.dpi)
+					plt.show()
+					
+				print("Sleeping for 60 seconds ...")
+				
+			else:
+				print("No data ... Sleeping for 60 seconds ...")
+				
+			time.sleep(60)