KHNP OPEN API¶

발전소 구분 WS : 월성 KR : 고리 YK : 한빛 UJ : 한울

월단위 원자력발전소 상태 : NuclearPlantStates

분단위 실시간 폐수 수질현황: WasteWater

10분단위 실시간 주변 방사선 량: RadioRate

10분단위 실시간 온배수 현황: ThermalWasteWater

본부 구분(2100:고리본부,2200:월성본부,2300:한빛본부,2400:한울본부,2800:새울본부)

024년 10월 이후 월단위 방사성 폐기물 발생량: RadioActiveWaste

In [1]:
%use dataframe
%use datetime
In [2]:
%use kandy
%use lets-plot
In [3]:
import kotlinx.datetime.*
import kotlin.time.Duration.Companion.minutes
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.format
import kotlinx.datetime.format.FormatStringsInDatetimeFormats
import kotlinx.datetime.format.byUnicodePattern
import kotlinx.datetime.minus
import kotlinx.datetime.plus
import kotlinx.datetime.toLocalDateTime

분단위 실시간 폐수 수질현황: WasteWater¶

In [4]:
val url_Wastewater = "http://192.168.35.107:7788/khnp/wastewater"
val df_Wastewater = DataFrame.readJson(url_Wastewater)
In [5]:
import kotlin.math.round

val convertValue_Wastewater = df_Wastewater
    .convert { tm001 and tm002 }.with {
        val value = it?.toString()?.toDoubleOrNull() ?: 0.0
        round(value * 10) / 10.0
    }.convert {
        time and tm001_time and tm002_time
    }.with { 
        LocalDateTime.parse(it.toString().replace(" ", "T")).toInstant(TimeZone.of("Asia/Seoul"))
    }

convertValue_Wastewater.describe()
Out[5]:

DataFrame: rowsCount = 6, columnsCount = 12

nametypecountuniquenullstopfreqmeanstdminmedianmax
timekotlinx.datetime.Instant1896302026-04-22T21:54:00Z3nullnull2026-04-22T21:54:00Z2026-04-23T00:50:00Z2026-04-23T03:45:00Z
genNameString18930KR63nullnullKRWSYK
tm001Double1895700.00000011222.77619036.3720410.0000000.000000110.000000
tm001_timekotlinx.datetime.Instant18911202026-04-22T10:55:00Z63nullnull2026-04-22T10:55:00Z2026-04-22T23:17:00Z2026-04-23T03:44:00Z
tm002Double189500.000000634.7248683.3505580.0000007.0000007.200000
tm002_timekotlinx.datetime.Instant1899402026-04-22T10:55:00Z63nullnull2026-04-22T10:55:00Z2026-04-22T23:17:00Z2026-04-23T03:44:00Z
In [6]:
val checkTime_Wastewater = 10.minutes

val checked_Wastewater = convertValue_Wastewater.filter {
    (time - tm001_time).absoluteValue <= checkTime_Wastewater &&
    (time - tm002_time).absoluteValue <= checkTime_Wastewater 
}
In [7]:
val filterTime_Wastewater = Clock.System.now().minus(6, DateTimeUnit.HOUR)

val final_Wastewater = checked_Wastewater
    .filter{ time >= filterTime_Wastewater }
    .convert {
        time and tm001_time and tm002_time
    }.with { 
        it.toLocalDateTime(TimeZone.of("Asia/Seoul")).format(LocalDateTime.Format{byUnicodePattern("yyyy-MM-dd HH:mm")})
    }
final_Wastewater.describe()
Out[7]:

DataFrame: rowsCount = 6, columnsCount = 12

nametypecountuniquenullstopfreqmeanstdminmedianmax
timeString1266302026-04-23 06:542nullnull2026-04-23 06:542026-04-23 09:502026-04-23 12:45
genNameString12620KR63nullnullKRKRWS
tm001Double1265700.0000004934.16428639.9687510.00000033.050000110.000000
tm001_timeString12611102026-04-23 06:532nullnull2026-04-23 06:532026-04-23 09:482026-04-23 12:44
tm002Double126407.100000637.0873020.0838906.9000007.1000007.200000
tm002_timeString1269302026-04-23 06:532nullnull2026-04-23 06:532026-04-23 09:492026-04-23 12:44
In [8]:
val data = final_Wastewater.toMap()
In [9]:
letsPlot( data ) +
labs( title = "분단위 실시간 폐수 수질현황", y="Wastewater Quality (PH)", x="수집시간", color="발전소") +     
geomPoint( alpha = 0.6){
    x = "time"
    y = "tm002"
    color="genName"
    size="tm001"
} +
scaleYContinuous(limits=Pair(6.0, 8.0)) +
// %Y: 년, %m: 월, %d: 일, %H: 시, %M: 분
//scaleXDateTime(format = "%m-%d %H:%M" ) +
scaleSize(range=Pair(0, 15), name="유량") +
theme(  axisTextX= elementText( angle=45, hjust = 1.0)) +    
ggsize( width = 2400, height = 450)    
Out[9]:

10분단위 실시간 온배수 현황: ThermalWasteWater¶

In [10]:
val url_ThermalWasteWater = "http://192.168.35.107:7788/khnp/thermalwastewater"
val df_ThermalWasteWater = DataFrame.readJson(url_ThermalWasteWater)
df_ThermalWasteWater.describe()
Out[10]:

DataFrame: rowsCount = 10, columnsCount = 10

nametypecountuniquenullstopfreqminmedianmax
timeString2526302026-04-23 06:5442026-04-23 06:542026-04-23 09:502026-04-23 12:45
genNameString25240KR63KRUJYK
rm001String252200063011.616.600000381469727
rm001_timeString25212002026-02-04 18:37632026-02-04 18:372026-04-23 08:452026-04-23 12:45
rm002String252130063032.534.20000076293945
rm002_timeString25212202026-02-04 18:37632026-02-04 18:372026-04-23 08:452026-04-23 12:45
rm005String252200063013.724.3
rm005_timeString25212002026-02-04 18:37632026-02-04 18:372026-04-23 08:452026-04-23 12:45
rm006String252150063032.40000152587890633.8
rm006_timeString25210702026-02-04 18:37632026-02-04 18:372026-04-23 08:452026-04-23 12:45
In [11]:
val converted_ThermalWasteWater = df_ThermalWasteWater
    .convert {
        time and rm001_time and rm002_time and rm005_time and rm006_time
    }.with { 
        LocalDateTime.parse(it.toString().replace(" ", "T")).toInstant(TimeZone.of("Asia/Seoul"))
    }.convert{ rm001 and rm002 and rm005 and rm006}.with {
        val value = it?.toString()?.toDoubleOrNull() ?: 0.0
        round(value * 10) / 10.0
    }

converted_ThermalWasteWater.describe()
Out[11]:

DataFrame: rowsCount = 10, columnsCount = 12

nametypecountuniquenullstopfreqmeanstdminmedianmax
timekotlinx.datetime.Instant2526302026-04-22T21:54:00Z4nullnull2026-04-22T21:54:00Z2026-04-23T00:50:00Z2026-04-23T03:45:00Z
genNameString25240KR63nullnullKRUJYK
rm001Double2521600.0000006310.2996036.1974070.00000012.55000016.600000
rm001_timekotlinx.datetime.Instant25212002026-02-04T09:37:00Z63nullnull2026-02-04T09:37:00Z2026-04-22T23:45:00Z2026-04-23T03:45:00Z
rm002Double252900.0000006325.05277814.5080100.00000033.00000034.200000
rm002_timekotlinx.datetime.Instant25212202026-02-04T09:37:00Z63nullnull2026-02-04T09:37:00Z2026-04-22T23:45:00Z2026-04-23T03:45:00Z
rm005Double2521800.0000006313.5777788.7198940.00000015.10000024.300000
rm005_timekotlinx.datetime.Instant25212002026-02-04T09:37:00Z63nullnull2026-02-04T09:37:00Z2026-04-22T23:45:00Z2026-04-23T03:45:00Z
rm006Double2521200.0000006324.68690514.2866170.00000032.65000033.800000
rm006_timekotlinx.datetime.Instant25210702026-02-04T09:37:00Z63nullnull2026-02-04T09:37:00Z2026-04-22T23:45:00Z2026-04-23T03:45:00Z
In [12]:
val checkTime_ThermalWasteWater = 20.minutes

val checked_ThermalWasteWater = converted_ThermalWasteWater.filter {
    (time - rm001_time).absoluteValue <= checkTime_ThermalWasteWater &&
    (time - rm002_time).absoluteValue <= checkTime_ThermalWasteWater &&
    (time - rm005_time).absoluteValue <= checkTime_ThermalWasteWater &&
    (time - rm006_time).absoluteValue <= checkTime_ThermalWasteWater
}
In [13]:
val filterTime_ThermalWasteWater = Clock.System.now().minus(6, DateTimeUnit.HOUR)

val final_ThermalWasteWater = checked_ThermalWasteWater.filter{ time >= filterTime_ThermalWasteWater }
.convert {
    time and rm001_time and rm002_time and rm005_time and rm006_time
}.with { 
     it.toLocalDateTime(TimeZone.of("Asia/Seoul")).format(LocalDateTime.Format{byUnicodePattern("yyyy-MM-dd HH:mm")})
}
final_ThermalWasteWater.describe()
Out[13]:

DataFrame: rowsCount = 10, columnsCount = 12

nametypecountuniquenullstopfreqmeanstdminmedianmax
timeString1866302026-04-23 06:543nullnull2026-04-23 06:542026-04-23 09:442026-04-23 12:45
genNameString18630UJ63nullnullUJWSYK
rm001Double18615013.6000003813.7365591.98553111.30000013.60000016.600000
rm001_timeString18611902026-04-23 10:105nullnull2026-04-23 06:452026-04-23 09:402026-04-23 12:45
rm002Double1868034.2000005833.3908600.76134532.30000033.60000034.200000
rm002_timeString18612102026-04-23 10:105nullnull2026-04-23 06:402026-04-23 09:402026-04-23 12:45
rm005Double18617013.5000003118.1779574.37107213.50000016.80000024.300000
rm005_timeString18611902026-04-23 10:105nullnull2026-04-23 06:452026-04-23 09:402026-04-23 12:45
rm006Double18611032.9000005932.9161290.45161332.30000032.90000033.800000
rm006_timeString18610602026-04-23 10:105nullnull2026-04-23 06:402026-04-23 09:402026-04-23 12:45
In [14]:
val data_ThermalWasteWater = final_ThermalWasteWater.toMap() 
In [15]:
letsPlot(data_ThermalWasteWater) +
geomRibbon(
    alpha = 0.2,
    tooltips = layerTooltips()
        .title("@genName")              // 툴팁 제목에 발전소 이름 표시
        .line("시간|@time")             // '라벨|값' 형식으로 표시
        .line("취수 수온|@rm001 °C")
        .line("배수 수온|@rm005 °C")
        .format("@rm001", ".1f")        // 소수점 첫째 자리까지 표시
        .format("@rm005", ".1f")
){
    x="time"
    ymin = "rm001"
    ymax = "rm005"
    fill = "genName"
} +
scaleYContinuous(limits=Pair(5.0, 30.0)) +    
labs( title = "발전소별 실시간 취수/배수 수온 현황", y="수온(°C)", x="수집시간", fill="발전소") +  
theme(
    axisTextX= elementText( angle=45),
    panelGridMajorX = elementLine(color = "#e0e0e0", size = 0.5)
) +
ggsize( width = 2400, height = 450)   
Out[15]:
In [ ]: