from __future__ import absolute_import
import sys
sys.path.append('/home/fengkai/PycharmProjects/faster-rcnn-resnet50-fpn')
import torch
import numpy as np
from torchvision.models import resnet50
from model.region_proposal_network import RegionProposalNetwork
from model.faster_rcnn import FasterRCNN
from model.roi_module import RoIPooling2D
from utils.config import opt
from utils import array_tool
from model.fpn import FPN
from torch.nn import functional

def set_bn_fix(m):   #冻结batchnorm的参数
    classname = m.__class__.__name__
    if classname.find('BatchNorm') != -1:
        for p in m.parameters(): p.requires_grad = False

def decom_resnet50():
    model = resnet50(not opt.load_path)

    C2 = torch.nn.Sequential(
        model.conv1, model.bn1, model.relu, model.maxpool,
        model.layer1)
    C3 = torch.nn.Sequential(model.layer2)
    C4 = torch.nn.Sequential(model.layer3)
    C5 = torch.nn.Sequential(model.layer4)

    del model.avgpool, model.fc

    C2.apply(set_bn_fix)
    C3.apply(set_bn_fix)
    C4.apply(set_bn_fix)
    C5.apply(set_bn_fix)

    return C2, C3, C4, C5

class FasterRCNNResNet50(FasterRCNN):

    def __init__(self,
                 n_fg_class= opt.class_num,
                 ratios=[0.5, 1, 2],
                 anchor_scales=[8],
                 feat_stride=[4, 8, 16, 32, 64]):
        C2, C3, C4, C5 = decom_resnet50()

        fpn = FPN(
            out_channels=256
        )

        rpn = RegionProposalNetwork(
            256, 256,
            ratios=ratios,
            anchor_scales=anchor_scales,
            feat_stride=feat_stride,
        )

        head = Resnet50RoIHead(
            n_class=n_fg_class + 1,
            roi_size=7,
            feat_stride=[4, 8, 16, 32, 64],
        )

        super(FasterRCNNResNet50, self).__init__(
            C2, C3, C4, C5,
            fpn,
            rpn,
            head,
        )

class Resnet50RoIHead(torch.nn.Module):
    def __init__(self, n_class, roi_size, feat_stride):
        super(Resnet50RoIHead, self).__init__()
        self.fc6 = torch.nn.Linear(7*7*256, 1024)
        self.fc7 = torch.nn.Linear(1024, 1024)
        '''
        self.rcnn_top = torch.nn.Sequential(
            torch.nn.Conv2d(256, 1024, kernel_size=7, stride=7, padding=0),
            torch.nn.ReLU(True),
            torch.nn.Conv2d(1024, 1024, kernel_size=1, stride=1, padding=0),
            torch.nn.ReLU(True))
        '''
        self.cls_loc = torch.nn.Linear(1024, n_class * 4)
        self.score = torch.nn.Linear(1024, n_class)
        normal_init(self.fc6, 0, 0.01)
        normal_init(self.fc7, 0, 0.01)
        normal_init(self.cls_loc, 0, 0.01)
        normal_init(self.score, 0, 0.01)
        #weights_init(self.rcnn_top, 0, 0.01)

        self.n_class = n_class
        self.roi_size = roi_size
        self.feat_stride = feat_stride
        self.spatial_scale = [1. / i for i in feat_stride]

    def forward(self, features_maps, rois, roi_indices):
        roi_indices = array_tool.totensor(roi_indices).float()
        rois = array_tool.totensor(rois).float()
        roi_level = self._PyramidRoI_Feat(rois)
        indices_and_rois = torch.cat([roi_indices[:, None], rois], dim=1)
        xy_indices_and_rois = indices_and_rois[:, [0, 2, 1, 4, 3]]  # yx->xy
        indices_and_rois = xy_indices_and_rois.contiguous()  # 把tensor变成在内存中连续分布的形式

        roi_pool_feats = []
        roi_to_levels = []

        for i, l in enumerate(range(2, 6)):
            if (roi_level == l).sum() == 0:
                continue
            idx_l = (roi_level == l).nonzero()
            roi_to_levels.append(idx_l)
            #if idx_l.shape[0] == 0:
             #   keep_indices_and_rois = indices_and_rois[idx_l.data]
            #else:
            keep_indices_and_rois = indices_and_rois[idx_l]
            keep_indices_and_rois = keep_indices_and_rois.view(-1, 5)
            roi_pooling = RoIPooling2D(self.roi_size, self.roi_size, self.spatial_scale[i])
            pool = roi_pooling(features_maps[i], keep_indices_and_rois)   #通过roi_pooling
            roi_pool_feats.append(pool)
        roi_pool_feats = torch.cat(roi_pool_feats, 0)
        roi_to_levels = torch.cat(roi_to_levels, 0)
        roi_to_levels = roi_to_levels.squeeze()
        idx_sorted, order = torch.sort(roi_to_levels)
        roi_pool_feats = roi_pool_feats[order]

        pool = roi_pool_feats.view(roi_pool_feats.size(0), -1)  # batch_size, CHW拉直

        fc6_out = functional.relu(self.fc6(pool))
        fc7_out = functional.relu(self.fc7(fc6_out))
        roi_cls_locs = self.cls_loc(fc7_out)  # （1000->84）每一类坐标回归
        roi_scores = self.score(fc7_out)  # （1000->21） 每一类类别预测
        #all_roi_cls_locs.append(roi_cls_locs)
        #all_roi_scores.append(roi_scores)

        #all_roi_cls_locs = torch.cat(all_roi_cls_locs, 0)
        #all_roi_scores = torch.cat(all_roi_scores, 0)

        return roi_cls_locs, roi_scores

    def _PyramidRoI_Feat(self, rois):
        roi_h = rois[:, 2] - rois[:, 0] + 1
        roi_w = rois[:, 3] - rois[:, 1] + 1
        roi_level = torch.log(torch.sqrt(roi_h*roi_w)/224.0)
        roi_level = torch.round(roi_level + 4)
        roi_level[roi_level < 2] = 2
        roi_level[roi_level > 5] = 5
        return roi_level

def normal_init(m, mean, stddev, truncated=False):
    if truncated:
        m.weight.data.normal_().fmod_(2).mul_(stddev).add_(mean)   #截断产生正态分布
    else:
        m.weight.data.normal_(mean, stddev)   #普通产生正态分布
        m.bias.data.zero_()

def weights_init(m, mean, stddev, truncated=False):
    classname = m.__class__.__name__
    if classname.find('Conv') != -1:
        m.weight.data.normal_(0.0, 0.02)
        m.bias.data.fill_(0)
    elif classname.find('BatchNorm') != -1:
        m.weight.data.normal_(1.0, 0.02)
        m.bias.data.fill_(0)

if __name__ == '__main__':
    from data.util import read_image
    import cv2
    cv_img = cv2.imread('/home/fengkai/dog.jpg')
    src_img = read_image('/home/fengkai/dog.jpg')

    from data.dataset import preprocess
    img = preprocess(array_tool.tonumpy(src_img))
    img = torch.from_numpy(img)[None]
    C2, C3, C4, C5, = decom_resnet50()
    c2_out = C2(img)
    c3_out = C3(c2_out)
    c4_out = C4(c3_out)
    c5_out = C5(c4_out)
    import numpy as np
    from model.fpn import FPN
    fpn = FPN(256)
    p2, p3, p4, p5, p6 = fpn.forward(c2_out, c3_out, c4_out, c5_out)

    rcnn_maps = [p2, p3, p4, p5]
    feat_stride = [4, 8, 16, 32, 64]
    spatial_scale = [1. / i for i in feat_stride]
    for i, l in enumerate(range(2, 6)):
        print(rcnn_maps[i].shape)
        print(spatial_scale[i])

    feature_maps = [p2, p3, p4, p5, p6]
    from model.region_proposal_network import RegionProposalNetwork
    rpn = RegionProposalNetwork()
    img_size = [src_img.shape[1], src_img.shape[2]]
    all_rpn_locs, all_rpn_scores, all_rois, all_roi_indices, all_anchors = rpn.forward(feature_maps, img_size)

    '''
    features_base, features_top, classifier = decom_resnet50()

    roihead = Resnet50RoIHead(21,14, 1. / 16, features_top, classifier)
    roihead = roihead.cuda()
    ## fake data###
    B, N, C, H, W, PH, PW = 1, 8, 1024, 32, 32, 40, 40

    bottom_data = torch.randn(B, C, H, W).cuda()
    spatial_scale = 1. / 16
    outh, outw = PH, PW
    x = bottom_data.requires_grad_()
    import numpy as np
    rois = np.ones((128, 4),dtype=np.float32)
    roi_indices = np.zeros((128,),dtype=np.int32)
    roi_cls_locs, roi_scores = roihead.forward(x, rois, roi_indices)
    roi_cls_locs = roi_cls_locs.view(128, -1, 4)
    label = np.ones((128, ),dtype=np.uint8)
    label = array_tool.totensor(label).long()
    a = torch.arange(0, 128).long().cuda()
    b = roi_cls_locs[a, label]
    '''


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
    Edge TPU image classification benchmark with OpenCV.

    Copyright (c) 2020 Nobuo Tsukamoto

    This software is released under the MIT License.
    See the LICENSE file in the project root for more information.
"""

import argparse
import collections
import operator
import os
import platform
import time

import cv2
import numpy as np
import tflite_runtime.interpreter as tflite
from utils.tflite_util import (get_output_tensor, make_interpreter,
                               set_input_tensor)

Class = collections.namedtuple("Class", ["id", "score"])

def get_output(interpreter, top_k=1, score_threshold=0.0):
    """ Returns list of detected objects.
    """
    scores = get_output_tensor(interpreter, 0)
    classes = [
        Class(i, scores[i])
        for i in np.argpartition(scores, -top_k)[-top_k:]
        if scores[i] >= score_threshold
    ]
    return sorted(classes, key=operator.itemgetter(1), reverse=True)

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--model", help="File path of Tflite model.", required=True)
    parser.add_argument("--image", help="File path of image file.", required=True)
    parser.add_argument("--thread", help="Num threads.", default=2, type=int)
    parser.add_argument("--count", help="Repeat count.", default=100, type=int)
    args = parser.parse_args()

    # Initialize TF-Lite interpreter.
    interpreter = make_interpreter(args.model, args.thread)
    interpreter.allocate_tensors()
    _, height, width, channel = interpreter.get_input_details()[0]["shape"]
    print("Interpreter: ", height, width, channel)

    # Load image.
    im = cv2.imread(args.image)
    im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
    resize_im = cv2.resize(im, (width, height))

    h, w = im.shape[:2]
    print("Input: ", h, w)

    elapsed_list = []

    for i in range(args.count + 1):
        # Run inference.
        start = time.perf_counter()

        set_input_tensor(interpreter, resize_im)
        interpreter.invoke()
        # objs = get_output(interpreter, args.threshold)
        classes = get_output(interpreter)

        inference_time = (time.perf_counter() - start) * 1000

        if i is 0:
            print("First Inference : {0:.2f} ms".format(inference_time))
        else:
            elapsed_list.append(inference_time)

    print("Inference : {0:.2f} ms".format(np.array(elapsed_list).mean()))

if __name__ == "__main__":
    main()


# AUTOGENERATED! DO NOT EDIT! File to edit: 06_clip.ipynb (unless otherwise specified).

__all__ = ['search_by_text', 'id_table']

# Cell
import pandas as pd
import numpy as np
from .tools import *
from .paths import *
from sqlalchemy import Table,BIGINT,Column
from .clipmodel import text2clip

# Cell
def search_by_text(text,topk=5,parameters={},prompt = None,prompt_factor=3):
    clip = text2clip(text).numpy()
    if prompt:
        prompt_clip = text2clip(prompt+text).numpy()
        diff = prompt_clip - clip
        clip = clip + diff*prompt_factor

    query_expr, v_one_hot = onehot.build_milvus_query(parameters)
    clip = np.hstack([clip, v_one_hot])
    results = search_by_clip(clip, topk,query_expr=query_expr)
    df = get_metadata(results[1])
    df['accuracy'] =results[0]
    try:
        df['url'] = df.apply(lambda x: f"https://away.guru/photos/{get_hash_folder(int(x['id']))}/{int(x['id'])}.jpg",axis=1)
        df['link'] = df['listing_id'].apply(lambda listing_id:f'https://www.airbnb.com/rooms/{listing_id}')
    except:
        df['url']  = np.nan
        df['link'] = np.nan
    return df.drop(columns = ['listing_id','id'])
id_table = Table('ids_meta',Base.metadata,Column('id', BIGINT),extend_existing=True)


# -*- coding: utf-8 -*-
"""

@author: abhilash
"""
#importing the required libraries
import cv2
import numpy as np
from keras.preprocessing import image
from keras.models import model_from_json
import face_recognition

#loading the image to detect
image_to_detect = cv2.imread('images/testing/trump-modi.jpg')

#load the model and load the weights
face_exp_model = model_from_json(open("dataset/facial_expression_model_structure.json","r").read())
face_exp_model.load_weights('dataset/facial_expression_model_weights.h5')
#declare the emotions label
emotions_label = ('angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral')

#detect all faces in the image
#arguments are image,no_of_times_to_upsample, model
all_face_locations = face_recognition.face_locations(image_to_detect,model='hog')

#print the number of faces detected
print('There are {} no of faces in this image'.format(len(all_face_locations)))

#looping through the face locations
for index,current_face_location in enumerate(all_face_locations):
    #splitting the tuple to get the four position values of current face
    top_pos,right_pos,bottom_pos,left_pos = current_face_location
    #printing the location of current face
    print('Found face {} at top:{},right:{},bottom:{},left:{}'.format(index+1,top_pos,right_pos,bottom_pos,left_pos))
    #slicing the current face from main image
    current_face_image = image_to_detect[top_pos:bottom_pos,left_pos:right_pos]
    #draw rectangle around the face detected
    cv2.rectangle(image_to_detect,(left_pos,top_pos),(right_pos,bottom_pos),(0,0,255),2)

    #preprocess input, convert it to an image like as the data in dataset
    #convert to grayscale
    current_face_image = cv2.cvtColor(current_face_image, cv2.COLOR_BGR2GRAY)
    #resize to 48x48 px size
    current_face_image = cv2.resize(current_face_image, (48, 48))
    #convert the PIL image into a 3d numpy array
    img_pixels = image.img_to_array(current_face_image)
    #expand the shape of an array into single row multiple columns
    img_pixels = np.expand_dims(img_pixels, axis = 0)
    #pixels are in range of [0, 255]. normalize all pixels in scale of [0, 1]
    img_pixels /= 255

    #do prodiction using model, get the prediction values for all 7 expressions
    exp_predictions = face_exp_model.predict(img_pixels)
    #find max indexed prediction value (0 till 7)
    max_index = np.argmax(exp_predictions[0])
    #get corresponding lable from emotions_label
    emotion_label = emotions_label[max_index]

    #display the name as text in the image
    font = cv2.FONT_HERSHEY_DUPLEX
    cv2.putText(image_to_detect, emotion_label, (left_pos,bottom_pos), font, 0.5, (255,255,255),1)

#showing the current face with rectangle drawn
cv2.imshow("Image Face Emotions",image_to_detect)


import numpy as np
import math
import gc

import tensorflow as tf
from keras import backend as K
from keras.preprocessing import sequence
from keras.callbacks import EarlyStopping, LearningRateScheduler

from at_toolkit.at_utils import autodl_nlp_install_download

autodl_nlp_install_download()

from log_utils import info
from at_nlp.data_manager.data_sampler import DataGenerator
from at_nlp.data_manager.sample_utils import downsampling_input_data
from at_nlp.utils import color_msg, ohe2cat
from at_nlp.model_manager.emb_utils import _load_emb
from at_nlp.data_manager.sample_config import sample_strategy
from at_nlp.generators.model_generator import ModelGenerator
from at_nlp.generators.feature_generator import FeatureGenerator
from at_nlp.generators.data_generator import DataGenerator as BatchDataGenerator
from at_nlp.data_manager.preprocess_utils import _tokenize_chinese_words
from at_nlp.evaluator import Evaluator
from at_toolkit.at_evalator import ATEvaluator

from Auto_NLP.second_stage_models.model_iter_second_stage import Model as Second_Stage_Model

INIT_BATCH_SIZE = 32
MAX_SVM_FIT_NUM = 20000
MAX_BERT_FIT_NUM = 3000
MAX_EPOCH_NUM = 120

config = tf.ConfigProto()
config.gpu_options.allow_growth = True  # dynamically grow the memory used on the GPU
config.log_device_placement = False  # to log device placement (on which device the operation ran)
config.gpu_options.per_process_gpu_memory_fraction = 0.9
sess = tf.Session(config=config)
K.set_session(sess)

class RunModel(object):
    def __init__(self, metadata, train_output_path="./", test_input_path="./"):
        self.done_training = False
        self.metadata = metadata
        self.train_output_path = train_output_path
        self.test_input_path = test_input_path

        self.multi_label = False
        self.multi_label_cnt_thred = 10
        self.fasttext_embeddings_index = None

        self.load_pretrain_emb = True
        if self.load_pretrain_emb:
            self.fasttext_embeddings_index = _load_emb(self.metadata["language"])

        self.model = None
        self.accu_nn_tokenizer_x = True

        self.second_stage_model = Second_Stage_Model(self.metadata, fasttext_emb=self.fasttext_embeddings_index)

        ################# second stage model 相关 #########################
        self.use_second_stage_model = False
        self.start_second_stage_model = False
        self.second_stage_patience = 0
        self.second_stage_patience_max_num = 8
        ################# second stage model 相关 #########################

        self.call_num = 0
        ## run model 配置有一个采样器，一个模型选择器
        self.data_sampler = None
        self.model_selector = None
        self.imbalance_level = -1
        self.start_cnn_call_num = 3

        self.split_val_x = False
        self.imbalance_flow_control = 1
        self.feature_dict = {}
        self.vocab = {}
        self.time_record = {}
        self.imbalance_level = -1
        self.avg_word_per_sample = 0
        self.use_multi_svm = True
        self.max_length = 0
        self.ft_model_check_length = 0
        self.seq_len_std = 0

        self.val_x = []
        self.clean_val_x = []
        self.val_y = np.array([])
        self.build_tokenizer_x = []
        self.build_tokenizer_y = np.array([])

        self.first_stage_done = False
        self.second_stage_done = False
        self.third_stage_done = False
        self.start_first_stage_call_num = 3
        self.tokenize_test = False

        self.switch_new_model = True
        self.switch_new_feature_mode = False
        self.cur_model_train_start = False
        self.train_epoch = 0
        self.max_train_epoch = 3
        self.model_id = 0
        self.train_model_id = -1
        self.feature_id = -1

        self.best_auc = 0.0

        self.model_lib = ['text_cnn']

        self.feature_lib = ['char-level + 300dim-embedding',
                            'word-level + pretrained embedding300dim']

        self.use_ft_model = False

        self.callbacks = []
        normal_lr = LearningRateScheduler(self.lr_decay)
        step_lr = LearningRateScheduler(self.step_decay)
        self.callbacks_ = [normal_lr, step_lr]
        self.cur_lr = None

        self.test_result_list = [0] * 30
        self.cur_model_test_res = []
        self.svm_test_result = []
        self.model_weights_list = [[]] * 20
        self.hist_test = [[]] * 20

        self.is_best = False
        self.best_call_num = 0
        self.best_svm = 0.0
        self.best_cnt = []
        self.best_svm_scores = []

        self.batch_size = INIT_BATCH_SIZE

    def _show_runtime_info(self):
        info(color_msg("********************************************************"))
        info(color_msg("current model_id is {}, model_name is {}".format(self.model_id, self.model_lib[self.model_id])))
        info(color_msg(
            "current feature_id is {}, feature_name is {}".format(self.feature_id, self.feature_lib[self.feature_id])))
        info(color_msg("train_model_id is {}".format(self.train_model_id)))
        info(color_msg("********************************************************\n"))

    def _set_sampling_strategy(self, y_train):
        strategy = sample_strategy['sample_iter_incremental_no_train_split']

        if y_train.shape[0] > 0:  # 如果当前有增量数据进样
            if self.call_num == 0 or self.call_num >= self.start_cnn_call_num:
                strategy = sample_strategy['sample_iter_incremental_no_train_split']

            elif self.call_num < self.start_cnn_call_num:
                strategy = sample_strategy["sample_iter_incremental_with_train_split"]

            if self.start_cnn_call_num == self.imbalance_flow_control and not self.split_val_x:
                strategy = sample_strategy["sample_from_full_data"]

        else:  # 当前已无增量数据
            if self.val_y.shape[0] > 0:  # 如果已经有val数据集
                strategy = sample_strategy["sample_from_full_train_data"]

            else:
                strategy = sample_strategy["sample_from_full_data"]

        return strategy

    def do_data_sampling(self, y_train):
        strategy = self._set_sampling_strategy(y_train)
        return self.data_manager.sample_dataset_iter(add_val_to_train=strategy["add_val_to_train"],
                                                     update_train=strategy["update_train"],
                                                     use_full=strategy["use_full"])

    def run_svm(self, model_name, train_x, train_y):
        self.feature_generator.tokenizer = None
        if self.metadata["language"] == "ZH" and self.call_num <= 1:
            analyzer = "char"
        else:
            analyzer = "word"
        self.feature_generator.build_tokenizer(train_x, 'svm', analyzer)
        # 后处理，将文本数据转换为 tfidf feature
        if len(train_x) > MAX_SVM_FIT_NUM:
            train_x = train_x[:MAX_SVM_FIT_NUM]
            train_y = train_y[:MAX_SVM_FIT_NUM, :]

        train_data = self.feature_generator.postprocess_data(train_x)
        classifier = self.model_manager.select_classifier(model_name=model_name, feature_mode=None,
                                                          data_feature=self.feature_generator.data_feature)

        self.svm_token = self.feature_generator.tokenizer
        if self.multi_label:
            classifier.fit(train_data, train_y)
        else:
            classifier.fit(train_data, ohe2cat(train_y))
        return classifier

    def prepare_nn_tokenizer(self, train_x):
        self.feature_generator.build_tokenizer(train_x, model_name='nn')
        self.feature_generator.set_data_feature()
        self.tokenizer = self.feature_generator.tokenizer

    def model_fit(self, train_x, train_y, model):
        num_epochs = 1
        bs_training_generator = BatchDataGenerator(train_x, train_y, batch_size=self.batch_size,
                                                   language=self.metadata["language"],
                                                   max_length=self.feature_generator.max_length
                                                   if self.feature_generator.max_length else 100,
                                                   vocab=None,
                                                   tokenizer=self.tokenizer,
                                                   num_features=self.feature_generator.num_features)

        model.fit_generator(generator=bs_training_generator, verbose=1,
                            epochs=num_epochs,
                            callbacks=self.callbacks,
                            shuffle=True)
        return model

    def init_generators(self, x_train, y_train):
        self.data_manager = DataGenerator(x_train, y_train, self.metadata, self.imbalance_level, self.multi_label)
        self.model_manager = ModelGenerator(load_pretrain_emb=self.load_pretrain_emb,
                                            fasttext_embeddings_index=self.fasttext_embeddings_index,
                                            multi_label=self.multi_label)
        # ZH 前期默认不切分词，后期需切词
        self.feature_generator = FeatureGenerator(self.metadata["language"], do_seg=False,
                                                  num_class=self.metadata["class_num"])

        self.evaluator = Evaluator(self.clean_val_x, self.val_y)

    def process_val_data(self, val_diff_x, val_diff_y):
        # 处理增量val数据
        if val_diff_x:
            clean_val_x = self.feature_generator.preprocess_data(val_diff_x)
            if self.val_y.shape[0] > 0:
                self.val_x = np.concatenate([self.val_x, val_diff_x], axis=0)
                self.val_y = np.concatenate([self.val_y, val_diff_y], axis=0)
                self.clean_val_x = np.concatenate([self.clean_val_x, clean_val_x], axis=0)

            else:
                self.val_x = val_diff_x
                self.val_y = val_diff_y
                self.clean_val_x = clean_val_x
            return clean_val_x
        else:
            return None

    def _reset_train_status(self):
        self.cur_model_train_start = False
        self.switch_new_model = True
        self.cur_lr = None
        self.train_epoch = 0
        self.best_cnt = []
        self.model_weights_list[self.train_model_id] = self.evaluator.model_weights_list
        self.cur_model_test_res = []
        self.evaluator.max_epoch = 5
        self.evaluator._reset()

    def _clear_train_space(self):
        del self.model
        gc.collect()
        K.clear_session()

    def _init_nn_train_process(self):
        self.feature_id += 1
        self.train_model_id = self.model_id + self.feature_id * len(self.model_lib)
        self._show_runtime_info()

        self.model_weights_list[self.train_model_id] = []
        self.cur_model_train_start = True
        self.switch_new_model = False
        self.train_epoch = 0
        self.callbacks = []

    def do_evaluation(self, eval_svm=True, update_val=True, update_setting=True):
        if eval_svm:
            tokenizer = self.svm_token
        else:
            tokenizer = self.tokenizer

        if update_val:
            self.evaluator.update_val_data(self.clean_val_x, self.val_y)
        if update_setting:
            self.evaluator.update_setting(self.metadata["language"],
                                          self.feature_generator.max_length,
                                          self.feature_generator.num_features,
                                          tokenizer)

        self.evaluator.valid_auc(is_svm=eval_svm, model=self.model, use_autodl_auc=True)

    def _train_nn_process(self, train_preprocessed_data, train_diff_y):
        self.model = self.model_fit(train_preprocessed_data, train_diff_y, self.model)
        if self.cur_model_train_start:  # 如果当前模型正在训练，评估每一次模型结果
            if self.call_num == self.start_first_stage_call_num:
                self.do_evaluation(eval_svm=False, update_val=True, update_setting=True)
            else:
                self.do_evaluation(eval_svm=False, update_val=False, update_setting=False)
            self.evaluator.update_model_weights(self.model, self.train_epoch)

            self.train_epoch += 1

    def _ensemble_multi_models(self):
        if self.call_num <= self.start_first_stage_call_num:
            ensemble_condition = self.evaluator.best_auc > self.best_auc
        else:
            # 对于第二个NN模型结果，要求达到前一次NN模型效果的97%
            ensemble_condition = self.evaluator.best_auc > 0.97 * self.best_auc

        if ensemble_condition:
            self.is_best = True  # 允许多模型融合
            self.best_auc = max(self.evaluator.best_auc, self.best_auc)

        else:
            self.is_best = False

        self.best_cnt.append(self.is_best)

        if self.call_num < self.start_first_stage_call_num:
            self.best_svm = self.best_auc
            self.best_svm_scores.append(self.best_svm)

    def is_stage_done(self):
        if self.model_id == len(self.model_lib) - 1:
            if self.feature_id == len(self.feature_lib) - 1:
                self.first_stage_done = True

    def meta_strategy(self):

        if self.max_length <= 156 and self.avg_word_per_sample <= 12:
            self.use_ft_model = False
            self.evaluator.max_epoch = 8
            self.feature_lib = ['char-level + 300dim-embedding',
                                'word-level + pretrained embedding300dim']

        elif self.max_length > 156:
            self.use_ft_model = False
            self.second_stage_patience_max_num = 8
            self.feature_lib = ['char-level + 300dim-embedding',
                                'word-level + pretrained embedding300dim']

        if self.imbalance_level == 2 and self.metadata["language"] == "EN":
            self.feature_lib = ['char-level + 300dim-embedding']

        if self.metadata["language"] == "ZH":
            self.feature_lib = ['word-level + pretrained embedding300dim',
                                'char-level + 300dim-embedding']
            self.second_stage_patience_max_num = 8

        if self.multi_label:
            self.feature_lib = ['char-level + 300dim-embedding']

    def _update_build_tokenizer_data(self):
        pass

    def prepare_clean_data(self, train_diff_x, val_diff_x, val_diff_y):
        # 前处理：根据给定的前处理方式 清洗文本数据, 默认default
        train_preprocessed_data = self.feature_generator.preprocess_data(train_diff_x)
        self.process_val_data(val_diff_x, val_diff_y)
        if self.call_num == 2 and self.metadata["language"] == "ZH":
            self.clean_val_x = list(map(_tokenize_chinese_words, self.clean_val_x))

        if self.accu_nn_tokenizer_x:
            if self.call_num == 2 and self.metadata["language"] == "ZH":
                self.build_tokenizer_x = train_preprocessed_data
            else:

                self.build_tokenizer_x = self.build_tokenizer_x + train_preprocessed_data

        return train_preprocessed_data

    def run_second_stage(self, remaining_time_budget):
        if not self.start_second_stage_model:
            self._clear_train_space()
            self.start_second_stage_model = True
            if self.imbalance_level == 2:
                self.second_stage_model.split_val = False

        if self.second_stage_model.model_id == len(
                self.second_stage_model.cand_models) and self.second_stage_model.data_id == self.second_stage_model.max_data:
            self.second_stage_done = True
            info("finish second stage!")
            return
        if self.second_stage_patience >= self.second_stage_patience_max_num:
            self.second_stage_model.epoch = 1
            self.second_stage_patience = 0
            do_clean = True
        else:
            do_clean = False

        if self.second_stage_model.split_val:
            self.second_stage_model.train_iter((self.data_manager.meta_data_x, self.data_manager.meta_data_y),
                                               eval_dataset=(self.val_x, self.val_y),
                                               remaining_time_budget=remaining_time_budget,
                                               do_clean=do_clean)
        else:
            self.second_stage_model.train_iter((self.data_manager.meta_train_x, self.data_manager.meta_train_y),
                                               eval_dataset=(self.val_x, self.val_y),
                                               remaining_time_budget=remaining_time_budget,
                                               do_clean=do_clean)

        second_auc = self.second_stage_model.best_sco
        self.evaluator.best_auc = second_auc
        if second_auc == -1 or second_auc == 0.02:
            second_auc = 0.0
        if second_auc >= self.best_auc * 0.97 and second_auc > 0.0:

            self.use_second_stage_model = True
            if self.second_stage_model.Xtest is None and self.second_stage_model.FIRSTROUND:
                self.second_stage_model.START = True
            elif self.second_stage_model.Xtest is None and self.second_stage_model.new_data:
                self.second_stage_model.START = False
            return
        else:

            if self.second_stage_model.START == False and self.second_stage_model.FIRSTROUND == False and self.second_stage_model.LASTROUND:
                self.second_stage_model.is_best = False
                self.second_stage_model.LASTROUND = False
            elif self.second_stage_model.START == True:  # 如果START模型没有超过当前
                self.second_stage_model.START = False

            self.use_second_stage_model = False
            return

    def run_first_stage_model(self, train_preprocessed_data, train_diff_y):
        if self.switch_new_model and not self.cur_model_train_start:  # 如果切换模型，且当前模型没有开始训练
            self._clear_train_space()
            self._init_nn_train_process()
            self.model = self.model_manager.select_classifier(model_name=self.model_lib[self.model_id],
                                                              feature_mode=self.feature_lib[self.feature_id],
                                                              data_feature=self.feature_generator.data_feature)
            info(color_msg("start new nn model training!"))

            if self.model_lib[self.model_id] == "text_cnn":
                if self.imbalance_level == 2 or self.metadata["class_num"] >= 5:
                    self.callbacks = []
                else:
                    self.callbacks = [self.callbacks_[0]]
            else:
                self.callbacks = [self.callbacks_[1]]

        self._train_nn_process(train_preprocessed_data, train_diff_y)

        if self.train_model_id >= 1:  # 训练了至少2个模型
            self._ensemble_multi_models()
        else:
            self._ensemble_multi_models()

        #  达到结束的条件
        if self.evaluator.decide_stop(train_epoch=self.train_epoch):
            self._reset_train_status()

    def train(self, x_train, y_train, remaining_time_budget=None):
        if self.done_training:
            return
        if not self.use_multi_svm and self.metadata["language"] == "EN":
            self.start_first_stage_call_num = 1
        if not self.use_multi_svm and self.metadata["language"] == "ZH":
            self.start_first_stage_call_num = 2
        if self.use_multi_svm and self.multi_label:
            self.start_first_stage_call_num = 2

        if self.call_num == 0:
            self.init_generators(x_train, y_train)
            if self.use_multi_svm:
                self.evaluator.max_epoch = 15
            if self.multi_label:
                self.evaluator.max_epoch = 18

        else:
            if self.call_num == 2 and self.metadata["language"] == "ZH":
                self.feature_generator.do_seg = True

            if y_train.shape[0] > 0:
                self.data_manager.update_meta_data(x_train, y_train)

        # 数据采样
        train_diff_x, train_diff_y, val_diff_x, val_diff_y = self.do_data_sampling(y_train)

        ########################## 设定采用的预处理方式###########################################

        ########################## 数据前处理 ####################################################
        train_preprocessed_data = self.prepare_clean_data(train_diff_x, val_diff_x, val_diff_y)

        ############################ SVM 阶段模型 ############################
        if self.call_num < self.start_first_stage_call_num:
            self.model = self.run_svm('svm', train_preprocessed_data, train_diff_y)

            if self.val_y.shape[0] > 0:
                self.do_evaluation(eval_svm=True)
                self.evaluator.update_model_weights(self.model, self.train_epoch, is_svm=True)
            self._ensemble_multi_models()

        else:

            if self.call_num == self.start_first_stage_call_num:
                # 进行模型预选择
                self.meta_strategy()
                self.feature_generator.reset_tokenizer()
                # 设定文本长度及文本长度std，影响后处理pad长度设置
                self.feature_generator.max_length = self.max_length
                self.feature_generator.seq_len_std = self.seq_len_std
                self.prepare_nn_tokenizer(train_x=self.build_tokenizer_x)
                self.accu_nn_tokenizer_x = False

            ############################ 进入第二阶段模型 ############################
            if self.first_stage_done:
                if not self.multi_label:
                    self.run_second_stage(remaining_time_budget)
                else:
                    info(color_msg("do not run second stage model when multi_label is {}".format(self.multi_label)))
                    self.second_stage_done = False
            ############################ 进入第一阶段模型 ############################
            else:
                if self.switch_new_model and not self.cur_model_train_start:
                    self.is_stage_done()
                if not self.first_stage_done:
                    self.run_first_stage_model(train_preprocessed_data, train_diff_y)

        return

    def _update_multi_model_result(self, pred):
        if self.is_best:
            self.test_result_list[self.train_model_id] = pred
            result = np.mean(self.test_result_list[:self.train_model_id + 1], axis=0)
        else:
            if isinstance(self.test_result_list[self.train_model_id], int):
                result = self.test_result_list[0]
            else:
                result = np.mean(self.test_result_list[:self.train_model_id + 1], axis=0)
        return result

    def transform_test(self):
        if self.call_num < self.start_first_stage_call_num:
            x_test_feature = self.svm_token.transform(self.x_test_clean)
            return x_test_feature

        else:
            if not self.tokenize_test:
                x_test_feature = self.tokenizer.texts_to_sequences(self.x_test_clean)
                x_test = sequence.pad_sequences(x_test_feature,
                                                maxlen=self.feature_generator.max_length,
                                                padding='post')
                self.tokenize_test = True
            else:
                x_test = self.x_test
            return x_test

    def output_svm_result(self):
        result = self.model.predict_proba(self.x_test)
        if self.is_best:
            self.svm_test_result.append(result)
        elif self.call_num > 0:
            result = self.svm_test_result[-1]
        return result

    def output_second_stage_result(self):
        info("Output in second stage!")
        # 第二阶段没有结束：只有两个选择：second_stage 模型 or 第一阶段最优模型
        if self.use_second_stage_model:
            self.second_stage_patience = 0
            info(color_msg("Use second_stage Model!!"))
            second_stage_result = self.second_stage_model.test(self.x_test_raw)

            # info(color_msg("second_stage result is {}".format(type(second_stage_result))))
            # if isinstance(second_stage_result, list):
            #     info(color_msg("second_stage result is {}".format(len(second_stage_result))))
            # if isinstance(second_stage_result, np.ndarray):
            #     info(color_msg("second_stage result is {}".format(second_stage_result.shape[0])))
            # if isinstance(second_stage_result, np.float):
            #     info(color_msg("second_stage result is {}".format(second_stage_result)))

            # 如果second_stage输出为空，返回第一个阶段结果
            if second_stage_result.shape[0] == 0:
                if isinstance(self.test_result_list[2], int):
                    result = np.mean(self.test_result_list[:2], axis=0)
                else:
                    result = np.mean(self.test_result_list[:3], axis=0)
                return result
            else:
                self.test_result_list[2] = second_stage_result
                result = np.mean(self.test_result_list[:3], axis=0)
                return result
        else:
            info(color_msg("Do Not Use second_stage Model!! second_stage_patience is {}".format(self.second_stage_patience)))
            self.second_stage_patience += 1
            if self.start_second_stage_model:
                if isinstance(self.test_result_list[2], int):
                    result = np.mean(self.test_result_list[:2], axis=0)
                else:
                    result = np.mean(self.test_result_list[:3], axis=0)
            else:
                if self.train_model_id == 0:
                    result = self.test_result_list[0]
                else:
                    result = np.mean(self.test_result_list[:2], axis=0)
            return result

    def output_first_stage_result_with_svm(self, result):
        if self.is_best:
            if np.std([np.max(self.best_svm_scores), self.best_auc]) < 0.005:
                self.svm_test_result.append(result)
                result = np.mean(self.svm_test_result, axis=0)
            else:
                self.multi_label_cnt_thred-=1
                result = result
        else:
            if np.std([np.max(self.best_svm_scores), self.best_auc]) < 0.02:
                self.svm_test_result.append(result)
                result = np.mean(self.svm_test_result, axis=0)
                self.svm_test_result.pop(-1)
            else:
                result = self.svm_test_result[-1]
        return result

    def output_first_stage_result(self):
        result = self.model.predict(self.x_test,
                                    batch_size=self.batch_size * 16)
        self.cur_model_test_res.append(result)
        if self.train_model_id == 0 and not self.cur_model_train_start:
            self.test_result_list[0] = self.cur_model_test_res[-1]

        if self.train_model_id==1 and not self.cur_model_train_start:
            if isinstance(self.test_result_list[self.train_model_id], int):

                self.test_result_list[1] = self.test_result_list[0]

        if self.train_model_id >= 1:
            result = self._update_multi_model_result(result)

        if self.call_num == self.start_first_stage_call_num:
            if self.is_best:
                if np.std([np.max(self.best_svm_scores), self.best_auc]) < 0.008:
                    self.svm_test_result.append(result)
                    result = np.mean(self.svm_test_result, axis=0)
                else:
                    result = result
            else:
                if np.std([np.max(self.best_svm_scores), self.best_auc]) < 0.02:
                    self.svm_test_result.append(result)
                    result = np.mean(self.svm_test_result, axis=0)
                    self.svm_test_result.pop(-1)
                else:
                    result = self.svm_test_result[-1]

        if self.multi_label:
            if self.train_model_id >= 1:
                result = self._update_multi_model_result(result)
            else:
                result = self.output_first_stage_result_with_svm(result)

        return result

    def test(self, x_test, remaining_time_budget):
        if self.call_num == 0:
            self.x_test_raw = x_test
            self.x_test_clean = self.feature_generator.preprocess_data(self.x_test_raw)

        if self.metadata["language"] == "ZH" and self.call_num == 2:
            # feature.do_seg 已经更新
            self.x_test_clean = self.feature_generator.preprocess_data(self.x_test_raw)

        self.x_test = self.transform_test()

        # 输出svm 结果
        if self.call_num < self.start_first_stage_call_num:
            result = self.output_svm_result()

        elif self.second_stage_done:
            result = np.mean(self.test_result_list[:3], axis=0)

        elif self.first_stage_done:
            if self.multi_label:
                if self.multi_label_cnt_thred<0:
                    result = self.cur_model_test_res[-1]
                    return result
                else:
                    result = self.svm_test_result[-1]
            else:
                result = self.output_second_stage_result()

        else:
            ## 当前为NN模型输出结果
            result = self.output_first_stage_result()

        self.done_training = False
        self.call_num += 1
        if self.call_num == MAX_EPOCH_NUM:
            self.done_training = True
            self.ft_model = None
        return result

    def lr_decay(self, epoch):
        if self.call_num == 1 or self.cur_lr is None:
            self.cur_lr = self.model_manager.lr
        if self.train_epoch % 3 == 0 and self.train_epoch > 0:
            self.cur_lr = 3 * self.cur_lr / 5
        self.cur_lr = max(self.cur_lr, 0.0001)
        lr = self.cur_lr
        return lr

    def step_decay(self, epoch):
        epoch = self.train_epoch // 3
        initial_lrate = self.model_manager.lr  # 0.016 #0.0035 #
        drop = 0.65  # 0.65
        epochs_drop = 1.0  # 2.0
        if (self.train_epoch) <= 2:
            lrate = initial_lrate
        else:
            lrate = initial_lrate * math.pow(drop, math.floor((1 + epoch) / epochs_drop))
        lrate = max(lrate, 0.0001)
        return lrate


from COMMONTOOL import TOOL, PTCureve, CSFTree, NBPlot3D
from CNS_UDP_FAST import CNS
import numpy as np
import time
import random
import copy
import matplotlib.pylab as plt

class ENVCNS(CNS):
    def __init__(self, Name, IP, PORT, Monitoring_ENV = None):
        super(ENVCNS, self).__init__(threrad_name=Name,
                                     CNS_IP=IP, CNS_Port=PORT, Remote_IP='192.168.0.29', Remote_Port=PORT, Max_len=10)
        self.Monitoring_ENV = Monitoring_ENV
        self.Name = Name # = id
        self.AcumulatedReward = 0
        self.ENVStep = 0
        self.LoggerPath = 'DB'
        self.want_tick = 100  # 20sec

        self.accident_name = ['LOCA', 'SGTR', 'MSLB'][1]

        self.Loger_txt = ''

        self.input_info = [
            # (para, x_round, x_min, x_max), (x_min=0, x_max=0 is not normalized.)
            ('ZINST98',  1, 0,   100),     # SteamDumpPos
            ('ZINST87',  1, 0,   50),      # Steam Flow 1
            ('ZINST86',  1, 0,   50),      # Steam Flow 2
            ('ZINST85',  1, 0,   50),      # Steam Flow 3
            ('KLAMPO70', 1, 0,   1),       # Charging Pump2 State
            ('BHV22',    1, 0,   1),       # SI Valve State
            ('ZINST66',  1, 0,   25),      # PZRSprayPos
            ('UAVLEG2',  1, 150, 320),     # PTTemp
            ('ZINST65',  1, 0,   160),     # PTPressure
            ('ZINST78',  1, 0,   70),      # SG1Nar
            ('ZINST77',  1, 0,   70),      # SG2Nar
            ('ZINST76',  1, 0,   70),      # SG3Nar
            ('ZINST75',  1, 0,   80),      # SG1Pres
            ('ZINST74',  1, 0,   80),      # SG2Pres
            ('ZINST73',  1, 0,   80),      # SG3Pres
            ('ZINST72',  1, 0,   100),     # SG1Wid
            ('ZINST71',  1, 0,   100),     # SG2Wid
            ('ZINST70',  1, 0,   100),     # SG3Wid
            ('UUPPPL',   1, 100, 350),     # CoreExitTemp
            ('WFWLN1',   1, 0,   25),      # SG1Feed
            ('WFWLN2',   1, 0,   25),      # SG2Feed
            ('WFWLN3',   1, 0,   25),      # SG3Feed
            ('UCOLEG1',  1, 0,   100),     # RCSColdLoop1
            ('UCOLEG2',  1, 0,   100),     # RCSColdLoop2
            ('UCOLEG3',  1, 0,   100),     # RCSColdLoop3
            ('ZINST65',  1, 0,   160),     # RCSPressure
            ('ZINST63',  1, 0,   100),     # PZRLevel
        ]

        self.action_space = 4       # TODO 1개에서 > 4개 "가압기 제어", "보조급수 제어", "SI 유량 제어", "덤프밸브"
        self.observation_space = len(self.input_info)

        # GP
        # self.pl = NBPlot3D()

    # ENV Logger
    def ENVlogging(self, s):
        cr_time = time.strftime('%c', time.localtime(time.time()))
        if self.ENVStep == 0:
            with open(f'{self.Name}.txt', 'a') as f:
                f.write('==' * 20 + '\n')
                f.write(f'[{cr_time}]\n')
                f.write('==' * 20 + '\n')
        else:
            with open(f'{self.Name}.txt', 'a') as f:
                f.write(f'[{cr_time}] {self.Loger_txt}\n')

    def normalize(self, x, x_round, x_min, x_max):
        if x_max == 0 and x_min == 0:
            # It means X value is not normalized.
            x = x / x_round
        else:
            x = x_max if x >= x_max else x
            x = x_min if x <= x_min else x
            x = (x - x_min) / (x_max - x_min)
        return x

    def get_state(self):
        state = []
        for para, x_round, x_min, x_max in self.input_info:
            if para in self.mem.keys():
                state.append(self.normalize(self.mem[para]['Val'], x_round, x_min, x_max))
            else:
                # ADD logic ----- 계산된 값을 사용하고 싶을 때
                print('Error_get_state function')
                pass
        # state = [self.mem[para]['Val'] / Round_val for para, Round_val in self.input_info]
        # self.Loger_txt += f'{state}\t'
        return np.array(state)

    def get_reward(self):
        """
        R => -20 ~ 0
        :return:
        """
        # Ver list
        self.Verlist = {
            '1': False,
            '2': False,
            '3': False,
            '4': False,
            '5': True,
        }
        # --------------------------------- NEW ----
        r = 0
        if self.ENVGetSIReset:
            V = {
                'CoolRateTemp': self.DRateFun(self.mem['KCNTOMS']['Val']),
                'CurrentTemp': self.mem['UAVLEG2']['Val'],
                'CurrentPres': self.mem['ZINST65']['Val'],
                'Dis': abs(self.DRateFun(self.mem['KCNTOMS']['Val']) - self.mem['UAVLEG2']['Val']),
                'PZRLevel': self.mem['ZINST63']['Val'],
                'SG1Nar': self.mem['ZINST78']['Val'], 'SG2Nar': self.mem['ZINST77']['Val'],
                'SG3Nar': self.mem['ZINST76']['Val'],
                'SG1Wid': self.mem['ZINST72']['Val'], 'SG2Wid': self.mem['ZINST71']['Val'],
                'SG3Wid': self.mem['ZINST70']['Val'],
                'SG1Pres': self.mem['ZINST75']['Val'], 'SG2Pres': self.mem['ZINST74']['Val'],
                'SG3Pres': self.mem['ZINST73']['Val'],
            }
            if self.Verlist['1']:
                # Cooling rate에 따라서 온도 감소
                r -= V['Dis'] / 100
                # 가압기 수위 10 아래 종료
                # if V['PZRLevel'] <= 10:  r -= 100
            if self.Verlist['2']:
                # 목표치까지 도달
                r += (29.5 - V['CurrentPres']) / 100
                r += (170 - V['CurrentTemp']) / 100
            if self.Verlist['3']:
                # Cooling rate에 따라서 온도 감소
                dis_reward = - V['Dis'] / 100 # [0.0 ~ -0.2] 동향을 보임
                # Pressure and Temp Dis
                curp = 29.5 if V['CurrentPres'] <= 29.5 else V['CurrentPres']
                curt = 170 if V['CurrentTemp'] <= 170 else V['CurrentTemp']
                dis_pres = (29.5 - V['CurrentPres']) / 100
                dis_temp = (170 - V['CurrentTemp']) / 100

                # r += (dis_pres * 0.1) + (dis_temp * 0.1) + (dis_reward * 10) # 감압 X
                r += (dis_pres * 0.1) + (dis_reward * 5)
            if self.Verlist['4']:
                # Cooling rate에 따라서 온도 감소
                dis_reward = - V['Dis'] / 100 # [0.0 ~ -0.2] 동향을 보임
                # Pressure and Temp Dis
                curp = 29.5 if V['CurrentPres'] <= 29.5 else V['CurrentPres']
                dis_pres = (29.5 - V['CurrentPres']) / 100
                PT_reward = - PTCureve().Check(Temp=V['CurrentTemp'], Pres=V['CurrentPres'])
                r += (dis_pres * 0.1) + (dis_reward * 5) + (PT_reward * 0.1)
            if self.Verlist['5']:
                r = 0
                # 1] Cooling rate에 따라서 온도 감소
                coolrate_r = - V['Dis']
                # 2] 가압기 수위 20~76% 구간 초과시 패널티
                pzrlevel_r = 0
                if 20 <= V['PZRLevel'] <= 76:
                    pass
                else:
                    if 20 > V['PZRLevel']:
                        pzrlevel_r -= (20 - V['PZRLevel'])
                    else:
                        pzrlevel_r -= (V['PZRLevel'] - 76)
                # 3] 증기 발생기 6% ~ 50% 이상 초과 시 패널티
                sg_r = 0
                for _ in range(1, 4):
                    if 6 <= V[f'SG{_}Nar'] <= 50:
                        pass
                    else:
                        if 6 > V[f'SG{_}Nar']:
                            sg_r -= (6 - V[f'SG{_}Nar'])
                        else:
                            sg_r -= (V[f'SG{_}Nar'] - 50)
                # 4] PT 커브에서 벗어나면 거리만큼 패널티
                PT_reward = - PTCureve().Check_Dis(Temp=V['CurrentTemp'], Pres=V['CurrentPres'])
                # 5] 목표치와 가까워 질 수록 +
                pres_r, temp_r = 0, 0
                pres_r = (29.5 - V['CurrentPres'])
                temp_r = (170 - V['CurrentTemp'])
                # 6] S/G 압력
                Avg_pres = (V['SG1Pres'] + V['SG2Pres'] + V['SG3Pres'])/3
                SGpres_r = 9 - Avg_pres if Avg_pres > 9 else 0
                # --------------------------------------------------------------
                w = {
                    'coolrate_r': [coolrate_r, 2],
                    'pzrlevel_r': [pzrlevel_r, 1],
                    'sg_r': [sg_r, 1.5],
                    'PT_reward': [PT_reward, 3],
                    'pres_r': [pres_r, 1],
                    'temp_r': [temp_r, 0.5],
                    'SGpres_r': [SGpres_r, 0.5]
                }

                log_txt_temp = ''
                for key in w.keys():
                    r += w[key][0] * w[key][1]
                    log_txt_temp += f'[{round(w[key][0]*w[key][1], 1)}:{w[key][0]}*{w[key][1]}]_'
                log_txt_temp = f'R:{r} = ' + log_txt_temp

                self.Loger_txt += log_txt_temp

            # self.Loger_txt += f"R:{r} = {dis_pres * 0.1}+{dis_temp * 0.1}+({dis_reward * 10})\t"
            # self.Loger_txt += f"R:{r} = {dis_pres * 0.1}+({dis_reward * 5})\t" #Verlist['3']
            # self.Loger_txt += f"R:{r} = {dis_pres * 0.1}+({dis_reward * 5})+({PT_reward * 0.1})\t"

            # --------------------------------- Send R ----
            self.AcumulatedReward += r
        # self.Loger_txt += f'{r}\t'
        self.DIS_CSF_Info += f'[R: {r}]\t'
        return r

    def get_done(self, r):
        V = {
            'CurrentTemp': self.mem['UAVLEG2']['Val'],
            'CurrentPres': self.mem['ZINST65']['Val'],
            'PZRLevel': self.mem['ZINST63']['Val'],
            'SG1Nar': self.mem['ZINST78']['Val'], 'SG2Nar': self.mem['ZINST77']['Val'],
            'SG3Nar': self.mem['ZINST76']['Val'],
            'SG1Pres': self.mem['ZINST75']['Val'], 'SG2Pres': self.mem['ZINST74']['Val'],
            'SG3Pres': self.mem['ZINST73']['Val'],
        }
        if self.Verlist['1']:
            pass
            # if self.AcumulatedReward < -100:
            #     d = True
            # elif self.mem['KCNTOMS']['Val'] == 38000:
            #     d = True
            #     r = 1
            # else:
            #     d = False
        if self.Verlist['2'] or self.Verlist['1']:
            d = False
            # 압력이 너무 아래까지 가는 것을 방지 아래가면 종료
            if V['CurrentPres'] <= 17:
                self.Loger_txt += f'PZR Pres Done\t'
                d = True
                r = -1
            # 증기발생기 수위 78 이상 시 종료
            if max(V['SG1Nar'], V['SG2Nar'], V['SG3Nar']) > 78:
                self.Loger_txt += f'SG Level Done\t'
                d = True
                r= -1
            # PT 커브 범위 초과 시 종료
            if PTCureve().Check(Temp=V['CurrentTemp'], Pres=V['CurrentPres']) == 1: # 불만족
                self.Loger_txt += f'PT Curve\t'
                d = True
                r = -1
            if self.mem['KCNTOMS']['Val'] == 35000:
                d = True
                if 17 < V['CurrentPres'] < 29.5 or V['CurrentTemp'] <= 170:
                    r = 0
                    if 17 < V['CurrentPres'] < 29.5: r += 0.5
                    if V['CurrentTemp'] <= 170: r += 0.5
                else:
                    r = -1
        if self.Verlist['3']:
            d = False
            # 압력이 너무 아래까지 가는 것을 방지 아래가면 종료
            if V['CurrentPres'] <= 17:
                self.Loger_txt += f'PZR Pres Done\t'
                d = True
                r = -1

            # PT 커브 범위 초과 시 종료
            if PTCureve().Check(Temp=V['CurrentTemp'], Pres=V['CurrentPres']) == 1:  # 불만족
                self.Loger_txt += f'PT Curve\t'
                d = True
                r = -1
            if self.mem['KCNTOMS']['Val'] == 35000:
                d = True
                if 17 < V['CurrentPres'] < 29.5 and V['CurrentTemp'] <= 170:
                    r = 1
                else:
                    r = -1
        if self.Verlist['4'] or self.Verlist['5']:
            d = False
            # 압력이 너무 아래까지 가는 것을 방지 아래가면 종료
            if self.mem['KCNTOMS']['Val'] >= 35000:
                d = True
                if 17 < V['CurrentPres'] < 29.5 and V['CurrentTemp'] <= 170:
                    r = 1
                else:
                    r = r
        # self.Loger_txt += f'{d}\t'
        return d, r

    def _send_control_save(self, zipParaVal):
        super(ENVCNS, self)._send_control_save(para=zipParaVal[0], val=zipParaVal[1])

    def send_act(self, A):
        """
        A 에 해당하는 액션을 보내고 나머지는 자동
        E.x)
            self._send_control_save(['KSWO115'], [0])
            ...
            self._send_control_to_cns()
        :param A: A 액션 [0, 0, 0] <- act space에 따라서
        :return: AMod: 수정된 액션
        """
        # -------------------------------------------------------------------------------------------------------
        # AMod = A[0]  # A는 리스트 값으로 0번째 값 추출
        AMod = A
        # -------------------------------------------------------------------------------------------------------
        # Order Book
        # -------------------------------------------------------------------------------------------------------
        # def check_CSFTree()
        V = {
            # ETC
            'Trip': self.mem['KLAMPO9']['Val'],
            'SIS': self.mem['KLAMPO6']['Val'],
            'MSI': self.mem['KLAMPO3']['Val'],

            'RCP1': self.mem['KLAMPO124']['Val'], 'RCP2': self.mem['KLAMPO125']['Val'],
            'RCP3': self.mem['KLAMPO126']['Val'], 'NetBRK': self.mem['KLAMPO224']['Val'],
            'CNSTime': self.mem['KCNTOMS']['Val'],

            'SteamDumpManAuto': self.mem['KLAMPO150']['Val'],  'SteamDumpPos': self.mem['ZINST98']['Val'],
            'SteamLine1': self.mem['BHV108']['Val'],
            'SteamLine2': self.mem['BHV208']['Val'],
            'SteamLine3': self.mem['BHV308']['Val'],

            'ChargingManAUto': self.mem['KLAMPO95']['Val'],
            'ChargingValvePos': self.mem['BFV122']['Val'],
            'ChargingPump2State': self.mem['KLAMPO70']['Val'],
            'SIValve': self.mem['BHV22']['Val'],

            'TurningGear': self.mem['KLAMPO165']['Val'],
            'OilSys': self.mem['KLAMPO164']['Val'],
            'BHV311': self.mem['BHV311']['Val'],

            # 강화학습을 위한 감시 변수
            'PZRSprayManAuto': self.mem['KLAMPO119']['Val'],
            'PZRSprayPos': self.mem['ZINST66']['Val'],
            'PZRBackHeaterOnOff': self.mem['KLAMPO118']['Val'],
            'PZRProHeaterManAuto': self.mem['KLAMPO117']['Val'],
            'PZRProHeaterPos': self.mem['BHV22']['Val'],

            # CSF 1 Value 미임계 상태 추적도
            'PowerRange': self.mem['ZINST1']['Val'], 'IntermediateRange': self.mem['ZINST2']['Val'],
            'SourceRange': self.mem['ZINST3']['Val'],
            # CSF 2 Value 노심냉각 상태 추적도
            'CoreExitTemp': self.mem['UUPPPL']['Val'],
            'PTCurve': PTCureve().Check(Temp=self.mem['UAVLEG2']['Val'], Pres=self.mem['ZINST65']['Val']),
            # CSF 3 Value 열제거원 상태 추적도
            'SG1Nar': self.mem['ZINST78']['Val'], 'SG2Nar': self.mem['ZINST77']['Val'],
            'SG3Nar': self.mem['ZINST76']['Val'],
            'SG1Pres': self.mem['ZINST75']['Val'], 'SG2Pres': self.mem['ZINST74']['Val'],
            'SG3Pres': self.mem['ZINST73']['Val'],
            'SG1Feed': self.mem['WFWLN1']['Val'], 'SG2Feed': self.mem['WFWLN2']['Val'],
            'SG3Feed': self.mem['WFWLN3']['Val'],

            'AllSGFeed': self.mem['WFWLN1']['Val'] +
                         self.mem['WFWLN2']['Val'] +
                         self.mem['WFWLN3']['Val'],
            'SG1Wid': self.mem['ZINST72']['Val'], 'SG2Wid': self.mem['ZINST71']['Val'],
            'SG3Wid': self.mem['ZINST70']['Val'],
            'SG123Wid': [self.mem['ZINST72']['Val'], self.mem['ZINST71']['Val'], self.mem['ZINST70']['Val']],

            # CSF 4 Value RCS 건전성 상태 추적도
            'RCSColdLoop1': self.mem['UCOLEG1']['List'], 'RCSColdLoop2': self.mem['UCOLEG2']['List'],
            'RCSColdLoop3': self.mem['UCOLEG3']['List'], 'RCSPressure': self.mem['ZINST65']['Val'],
            'CNSTimeL': self.mem['KCNTOMS']['List'],  # PTCurve: ...
            # CSF 5 Value 격납용기 건전성 상태 추적도
            'CTMTPressre': self.mem['ZINST26']['Val'], 'CTMTSumpLevel': self.mem['ZSUMP']['Val'],
            'CTMTRad': self.mem['ZINST22']['Val'],
            # CSF 6 Value RCS 재고량 상태 추적도
            'PZRLevel': self.mem['ZINST63']['Val']
        }
        CSF = {
            'CSF1': CSFTree.CSF1(V['Trip'], V['PowerRange'], V['IntermediateRange'], V['SourceRange']),
            'CSF2': CSFTree.CSF2(V['Trip'], V['CoreExitTemp'], V['PTCurve']),
            'CSF3': CSFTree.CSF3(V['Trip'], V['SG1Nar'], V['SG2Nar'], V['SG3Nar'],
                                 V['SG1Pres'], V['SG2Pres'], V['SG3Pres'],
                                 V['SG1Feed'], V['SG2Feed'], V['SG3Feed']),
            'CSF4': CSFTree.CSF4(V['Trip'], V['RCSColdLoop1'], V['RCSColdLoop2'], V['RCSColdLoop3'],
                                 V['RCSPressure'], V['PTCurve'], V['CNSTimeL']),
            'CSF5': CSFTree.CSF5(V['Trip'], V['CTMTPressre'], V['CTMTSumpLevel'], V['CTMTRad']),
            'CSF6': CSFTree.CSF6(V['Trip'], V['PZRLevel'])
        }
        ActOrderBook = {
            'StopAllRCP': (['KSWO132', 'KSWO133', 'KSWO134'], [0, 0, 0]),
            'StopRCP1': (['KSWO132'], [0]),
            'StopRCP2': (['KSWO133'], [0]),
            'StopRCP3': (['KSWO134'], [0]),
            'NetBRKOpen': (['KSWO244'], [0]),
            'OilSysOff': (['KSWO190'], [0]),
            'TurningGearOff': (['KSWO191'], [0]),
            'CutBHV311': (['BHV311', 'FKAFWPI'], [0, 0]),

            # 강화학습을 위한 제어 변수
            'PZRSprayMan': (['KSWO128'], [1]), 'PZRSprayAuto': (['KSWO128'], [0]),

            'PZRSprayClose': (['BPRZSP'], [self.mem['BPRZSP']['Val'] + 0.015 * -1]),
            'PZRSprayOpen': (['BPRZSP'], [self.mem['BPRZSP']['Val'] + 0.015 * 1]),

            'PZRBackHeaterOff': (['KSWO125'], [0]), 'PZRBackHeaterOn': (['KSWO125'], [1]),

            'PZRProHeaterMan': (['KSWO120'], [1]), 'PZRProHeaterAuto': (['KSWO120'], [0]),
            'PZRProHeaterDown': (['KSWO121', 'KSWO122'], [1, 0]),
            'PZRProHeaterUp': (['KSWO121', 'KSWO122'], [0, 1]),

            'DecreaseAux1Flow': (['KSWO142', 'KSWO143'], [1, 0]),
            'IncreaseAux1Flow': (['KSWO142', 'KSWO143'], [0, 1]),
            'DecreaseAux2Flow': (['KSWO151', 'KSWO152'], [1, 0]),
            'IncreaseAux2Flow': (['KSWO151', 'KSWO152'], [0, 1]),
            'DecreaseAux3Flow': (['KSWO154', 'KSWO155'], [1, 0]),
            'IncreaseAux3Flow': (['KSWO154', 'KSWO155'], [0, 1]),

            'UpAllAux': (['KSWO142', 'KSWO143', 'KSWO151', 'KSWO152', 'KSWO154', 'KSWO155'], [0, 1, 0, 1, 0, 1]),
            'DownAllAux': (['KSWO142', 'KSWO143', 'KSWO151', 'KSWO152', 'KSWO154', 'KSWO155'], [1, 0, 1, 0, 1, 0]),

            # WAFWS1
            'RL_IncreaseAux1Flow': (['WAFWS1'], [self.mem['WAFWS1']['Val'] + 0.04 * 1]),
            'RL_DecreaseAux1Flow': (['WAFWS1'], [self.mem['WAFWS1']['Val'] + 0.04 * (-1)]),
            'RL_IncreaseAux2Flow': (['WAFWS2'], [self.mem['WAFWS2']['Val'] + 0.04 * 1]),
            'RL_DecreaseAux2Flow': (['WAFWS2'], [self.mem['WAFWS2']['Val'] + 0.04 * (-1)]),
            'RL_IncreaseAux3Flow': (['WAFWS3'], [self.mem['WAFWS3']['Val'] + 0.04 * 1]),
            'RL_DecreaseAux3Flow': (['WAFWS3'], [self.mem['WAFWS3']['Val'] + 0.04 * (-1)]),

            'SteamDumpMan': (['KSWO176'], [1]), 'SteamDumpAuto': (['KSWO176'], [0]),
            'SteamDumpUp': (['PMSS'], [self.mem['PMSS']['Val'] + 2.0E5 * 1 * 0.2]),
            'SteamDumpDown': (['PMSS'], [self.mem['PMSS']['Val'] + 2.0E5 * (-1) * 0.2]),

            'IFLOGIC_SteamDumpUp': (['PMSS'], [self.mem['PMSS']['Val'] + 2.0E5 * 3 * 0.2]),
            'IFLOGIC_SteamDumpDown': (['PMSS'], [self.mem['PMSS']['Val'] + 2.0E5 * (-3) * 0.2]),

            'SteamLine1Open': (['KSWO148', 'KSWO149'], [1, 0]),
            'SteamLine2Open': (['KSWO146', 'KSWO147'], [1, 0]),
            'SteamLine3Open': (['KSWO144', 'KSWO145'], [1, 0]),

            'ChargingValveMan': (['KSWO100'], [1]), 'ChargingValveAUto': (['KSWO100'], [0]),
            'ChargingValveDown': (['KSWO101', 'KSWO102'], [1, 0]),
            'ChargingValveUp': (['KSWO101', 'KSWO102'], [0, 1]),

            'RunRCP2': (['KSWO130', 'KSWO133'], [1, 1]),
            'RunCHP2': (['KSWO70'], [1]), 'StopCHP2': (['KSWO70'], [0]),
            'OpenSI': (['KSWO81', 'KSWO82'], [1, 0]), 'CloseSI': (['KSWO81', 'KSWO82'], [0, 1]),

            'ResetSI': (['KSWO7', 'KSWO5'], [1, 1]),
        }
        CSF_level = [CSF[_]['L'] for _ in CSF.keys()]
        # self.Loger_txt += f'{CSF}\t'
        # self.Loger_txt += f'{CSF_level}\t'
        self.DIS_CSF_Info = f"[{V['CNSTime']}]\t"
        # -------------------------------------------------------------------------------------------------------
        # 자동 액션
        if V['Trip'] == 1:
            # 1] RCP 97 압력 이하에서 정지
            if V['RCSPressure'] < 97 and V['CNSTime'] < 15 * 60 * 5:
                if V['RCP1'] == 1: self._send_control_save(ActOrderBook['StopRCP1'])
                if V['RCP2'] == 1: self._send_control_save(ActOrderBook['StopRCP2'])
                if V['RCP3'] == 1: self._send_control_save(ActOrderBook['StopRCP3'])
            if V['NetBRK'] == 1: self._send_control_save(ActOrderBook['NetBRKOpen'])
            if V['TurningGear'] == 1: self._send_control_save(ActOrderBook['TurningGearOff'])
            if V['OilSys'] == 1: self._send_control_save(ActOrderBook['OilSysOff'])
            if V['BHV311'] > 0: self._send_control_save(ActOrderBook['CutBHV311'])

            # 1.1] Setup 현재 최대 압력 기준으로 세팅.
            if V['SIS'] != 0 and V['MSI'] != 0:
                if max(V['SG1Pres'], V['SG2Pres'], V['SG3Pres']) < V['SteamDumpPos']:
                    self._send_control_save(ActOrderBook['IFLOGIC_SteamDumpDown'])
            # 1.2] SI reset 전에 Aux 평균화 [검증 완료 20200903]
                if V['SG1Feed'] == V['SG2Feed'] and V['SG1Feed'] == V['SG3Feed'] and \
                        V['SG2Feed'] == V['SG1Feed'] and V['SG2Feed'] == V['SG3Feed'] and \
                        V['SG3Feed'] == V['SG1Feed'] and V['SG3Feed'] == V['SG2Feed']:
                    pass
                else:
                    # 1.2.1] 급수 일정화 수행
                    # 1.2.1.1] 가장 큰 급수 찾기
                    SGFeedList = [V['SG1Feed'], V['SG2Feed'], V['SG3Feed']]
                    MaxSGFeed = SGFeedList.index(max(SGFeedList))  # 0, 1, 2
                    MinSGFeed = SGFeedList.index(min(SGFeedList))  # 0, 1, 2
                    self._send_control_save(ActOrderBook[f'DecreaseAux{MaxSGFeed + 1}Flow'])
                    self._send_control_save(ActOrderBook[f'IncreaseAux{MinSGFeed + 1}Flow'])

            # 1.3] 2000부터 SI reset
            if V['CNSTime'] == 3000:                self._send_control_save(ActOrderBook['ResetSI'])

            # 2] SI reset 발생 시 (냉각 운전 시작)
            if V['SIS'] == 0 and V['MSI'] == 0 and V['CNSTime'] > 5 * 60 * 5:
                # 2.0] Build Cooling rate function
                if self.ENVGetSIReset == False:
                    rate = - 50 / (60 * 60 * 5) # 55
                    self.FixedTemp = copy.deepcopy(self.mem['UAVLEG2']['Val'])  # 변수의 Follow up 방지
                    self.FixedTime = copy.deepcopy(self.mem['KCNTOMS']['Val'])  # 변수의 Follow up 방지
                    self.DRateFun = lambda t: rate * (t - self.FixedTime) + self.FixedTemp
                    self.ENVGetSIReset = True

                # 2.0] Press set-point 를 현재 최대 압력 기준까지 조절
                # Steam dump Auto
                if V['SteamDumpManAuto'] == 0:      self._send_control_save(ActOrderBook['SteamDumpMan'])
                # 변수 업!
                # if max(V['SG1Pres'], V['SG2Pres'], V['SG3Pres']) < V['SteamDumpPos']:
                #     self._send_control_save(ActOrderBook['SteamDumpDown'])

                # Steam Line up
                if V['SteamLine1'] == 0:            self._send_control_save(ActOrderBook['SteamLine1Open'])
                if V['SteamLine2'] == 0:            self._send_control_save(ActOrderBook['SteamLine2Open'])
                if V['SteamLine3'] == 0:            self._send_control_save(ActOrderBook['SteamLine3Open'])

                # 2.1] Charging flow 최소화
                if V['ChargingManAUto'] == 0:       self._send_control_save(ActOrderBook['ChargingValveMan'])
                if V['ChargingValvePos'] != 0:      self._send_control_save(ActOrderBook['ChargingValveDown'])
                # 2.2] PZR spray 동작 [감압]
                if V['PZRSprayManAuto'] == 0:       self._send_control_save(ActOrderBook['PZRSprayMan'])
                if V['RCP2'] == 0:                  self._send_control_save(ActOrderBook['RunRCP2'])
                # 2.3] PZR 감압을 위한 Heater 종료
                if V['PZRProHeaterManAuto'] == 0:   self._send_control_save(ActOrderBook['PZRProHeaterMan'])
                if V['PZRProHeaterPos'] >= 0:       self._send_control_save(ActOrderBook['PZRProHeaterDown'])
                if V['PZRBackHeaterOnOff'] == 1:    self._send_control_save(ActOrderBook['PZRBackHeaterOff'])

                # -------------------------------------------------------------------------------------------
                # 강화학습 제어 파트
                # 2.4] TEST 모듈
                TEST = False
                if TEST:
                    # Check ACT input
                    while True:
                        ACT = input("Want act:")
                        if ACT == '' or ACT == '0':
                            # print("[SEND] No Act")
                            ACT = int(0)
                            break
                        if ACT.isdigit():
                            # print(f"[SEND] Act {int(ACT)}")
                            ACT = int(ACT)
                            break
                    # Done ACT input
                    # --------------
                    # Start ACT Code
                    if ACT == 0: pass
                    if ACT == 1: self._send_control_save(ActOrderBook['PZRSprayOpen'])
                    if ACT == 2: self._send_control_save(ActOrderBook['PZRSprayClose'])
                    if ACT == 3: self._send_control_save(ActOrderBook['UpAllAux'])
                    if ACT == 4: self._send_control_save(ActOrderBook['DownAllAux'])
                    if ACT == 5: self._send_control_save(ActOrderBook['RunCHP2'])
                    if ACT == 6: self._send_control_save(ActOrderBook['StopCHP2'])
        # 강화학습 제어 파트 ---------------------------------------------------------------------------------------
        V6_2_1 = False
        V6_2_2 = False
        V6_2_3 = False
        V6_2_4 = True
        if V6_2_1:  # TODO 돌리려면 AMod = Act[0] 하고 마지막 return [AMod] 써야함.
            if V['Trip'] == 1 and V['SIS'] == 0 and V['MSI'] == 0 and V['CNSTime'] > 5 * 60 * 5:
                # 1] 선택한 액션의 적합성을 판단하고 램덤으로 재 샘플링
                if AMod == 0:
                    pass
                else:
                    while True:
                        AMod = random.randint(0, 6)/10      # [0 ... 0.6]
                        if AMod == 0: break
                        if AMod == 0.1 and V['PZRSprayPos'] < 25.0: break
                        if AMod == 0.2 and V['PZRSprayPos'] != 0: break
                        if AMod == 0.3 and V['AllSGFeed'] < 73: break
                        if AMod == 0.4:
                            if not V['AllSGFeed'] == 0:
                                if V['SG1Nar'] > 6 and V['SG2Nar'] > 6 and V['SG3Nar'] > 6:
                                    break
                        if AMod == 0.5:
                            if V['PZRLevel'] > 40: pass
                            else:
                                if V['ChargingPump2State'] == 1 and V['SIValve'] == 1: pass
                                elif V['ChargingPump2State'] == 1 and V['SIValve'] == 0: break
                                elif V['ChargingPump2State'] == 0 and V['SIValve'] == 0: break
                        if AMod == 0.6:
                            if V['PZRLevel'] < 18: pass
                            else:
                                if V['ChargingPump2State'] == 0 and V['SIValve'] == 0: pass
                                elif V['ChargingPump2State'] == 0 and V['SIValve'] == 1: break
                                elif V['ChargingPump2State'] == 1 and V['SIValve'] == 1: break
                # 2] 최종적으로 제어 보내기
                if AMod == 0: pass
                if AMod == 0.1: self._send_control_save(ActOrderBook['PZRSprayOpen'])
                if AMod == 0.2: self._send_control_save(ActOrderBook['PZRSprayClose'])
                if AMod == 0.3: self._send_control_save(ActOrderBook['UpAllAux'])
                if AMod == 0.4: self._send_control_save(ActOrderBook['DownAllAux'])
                if AMod == 0.5:
                    if V['PZRLevel'] > 40:
                        AMod = 0
                    else:
                        if V['ChargingPump2State'] == 1 and V['SIValve'] == 1:
                            AMod = 0
                        elif V['ChargingPump2State'] == 1 and V['SIValve'] == 0:
                            self._send_control_save(ActOrderBook['RunCHP2'])
                        elif V['ChargingPump2State'] == 0 and V['SIValve'] == 0:
                            self._send_control_save(ActOrderBook['OpenSI'])
                if AMod == 0.6:
                    if V['PZRLevel'] < 18:
                        AMod = 0
                    else:
                        if V['ChargingPump2State'] == 0 and V['SIValve'] == 0:
                            AMod = 0
                        elif V['ChargingPump2State'] == 0 and V['SIValve'] == 1:
                            self._send_control_save(ActOrderBook['CloseSI'])
                        elif V['ChargingPump2State'] == 1 and V['SIValve'] == 1:
                            self._send_control_save(ActOrderBook['StopCHP2'])
            else:
                AMod = 0
        if V6_2_2:
            if V['Trip'] == 1 and V['SIS'] == 0 and V['MSI'] == 0 and V['CNSTime'] > 5 * 60 * 5:
                # 1] Continuous 값을 Discrete 값으로 변경
                AMod_ = [] # [1, 0, 0] 형식으로 저장됨
                for A in AMod:
                    if A >= 0.4:
                        AMod_.append(1)
                    elif -0.4 <= A < 0.4:
                        AMod_.append(0)
                    else:
                        AMod_.append(-1)
                # AMod = AMod_
                # 1.1] 가압기 10 아래면 무조건 CHP 2 on, HV Open
                if V['PZRLevel'] <= 10:
                    AMod[2] = 1

                # 2] 액션 스페이스는 줄이지 않고 수행해보기
                if AMod[0] == 1:
                    if V['PZRSprayPos'] >= 35:
                        AMod[0] = 0
                    else:
                        self._send_control_save(ActOrderBook['PZRSprayOpen'])
                if AMod[0] == 0: pass
                if AMod[0] == -1:
                    if V['PZRSprayPos'] == 0:
                        AMod[0] = 0
                    else:
                        self._send_control_save(ActOrderBook['PZRSprayClose'])

                if AMod[1] == 1:
                    if max(V['SG1Nar'], V['SG2Nar'], V['SG3Nar']) > 50:
                        AMod[1] = -1
                    else:
                        self._send_control_save(ActOrderBook['UpAllAux'])
                if AMod[1] == 0: pass
                if AMod[1] == -1:
                    if V['AllSGFeed'] == 0:
                        AMod[1] = 0
                    else:
                        self._send_control_save(ActOrderBook['DownAllAux'])

                if AMod[2] == 1:
                    if V['PZRLevel'] > 76:  ## 가압기 만수위 방지용.
                        AMod[2] = -1
                    else:
                        if V['ChargingPump2State'] == 1 and V['SIValve'] == 1:
                            AMod[2] = 0
                        elif V['ChargingPump2State'] == 0 and V['SIValve'] == 1:
                            self._send_control_save(ActOrderBook['RunCHP2'])
                        elif V['ChargingPump2State'] == 0 and V['SIValve'] == 0:
                            self._send_control_save(ActOrderBook['OpenSI'])
                if AMod[2] == 0: pass
                if AMod[2] == -1:
                    if V['ChargingPump2State'] == 0 and V['SIValve'] == 0:
                        AMod[2] = 0
                    elif V['ChargingPump2State'] == 0 and V['SIValve'] == 1:
                        self._send_control_save(ActOrderBook['CloseSI'])
                    elif V['ChargingPump2State'] == 1 and V['SIValve'] == 1:
                        self._send_control_save(ActOrderBook['StopCHP2'])

                if AMod[3] == 1:
                    if max(V['SG1Pres'], V['SG2Pres'], V['SG3Pres']) < V['SteamDumpPos']:
                        self._send_control_save(ActOrderBook['SteamDumpDown'])
                        AMod[3] == -1
                    else:
                        self._send_control_save(ActOrderBook['SteamDumpUp'])
                if AMod[3] == 0: pass
                if AMod[3] == -1: self._send_control_save(ActOrderBook['SteamDumpDown'])
            else:
                AMod = [0, 0, 0, 0]
        if V6_2_3:
            if V['Trip'] == 1 and V['SIS'] == 0 and V['MSI'] == 0 and V['CNSTime'] > 5 * 60 * 5:
                # 1] Continuous 값을 Discrete 값으로 변경
                # AMod_ = []  # [1, 0, 0] 형식으로 저장됨
                # for A in AMod:
                #     if A >= 0.4:
                #         AMod_.append(1)
                #     elif -0.4 <= A < 0.4:
                #         AMod_.append(0)
                #     else:
                #         AMod_.append(-1)
                # AMod = AMod_
                # 1.1] 가압기 10 아래면 무조건 CHP 2 on, HV Open
                if V['PZRLevel'] <= 10:
                    if AMod[2] < 0.4:  AMod[2] = 0.8
                if V['PZRLevel'] > 76:
                    if -0.4 <= AMod[2]: AMod[2] = -0.8

                # 2] 액션 스페이스는 줄이지 않고 수행해보기
                if 0.4 < AMod[0]:
                    if V['PZRSprayPos'] >= 35:
                        AMod[0] = 0
                    else:
                        self._send_control_save(ActOrderBook['PZRSprayOpen'])
                if -0.4 <= AMod[0] <= 0.4: pass
                if AMod[0] < -0.4:
                    if V['PZRSprayPos'] == 0:
                        AMod[0] = 0
                    else:
                        self._send_control_save(ActOrderBook['PZRSprayClose'])

                if 0.4 < AMod[1]:
                    if max(V['SG1Nar'], V['SG2Nar'], V['SG3Nar']) > 50:
                        AMod[1] = -0.8
                    else:
                        self._send_control_save(ActOrderBook['UpAllAux'])
                if -0.4 <= AMod[1] <= 0.4: pass
                if AMod[1] < -0.4:
                    if V['AllSGFeed'] == 0:
                        AMod[1] = 0
                    else:
                        self._send_control_save(ActOrderBook['DownAllAux'])

                if 0.4 < AMod[2]:
                    if V['ChargingPump2State'] == 1 and V['SIValve'] == 1:
                        AMod[2] = 0
                    elif V['ChargingPump2State'] == 0 and V['SIValve'] == 1:
                        self._send_control_save(ActOrderBook['RunCHP2'])
                    elif V['ChargingPump2State'] == 0 and V['SIValve'] == 0:
                        self._send_control_save(ActOrderBook['OpenSI'])
                if -0.4 <= AMod[2] <= 0.4: pass
                if AMod[2] < -0.4:
                    if V['ChargingPump2State'] == 0 and V['SIValve'] == 0:
                        AMod[2] = 0
                    elif V['ChargingPump2State'] == 0 and V['SIValve'] == 1:
                        self._send_control_save(ActOrderBook['CloseSI'])
                    elif V['ChargingPump2State'] == 1 and V['SIValve'] == 1:
                        self._send_control_save(ActOrderBook['StopCHP2'])

                if 0.4 < AMod[3]:
                    self._send_control_save(ActOrderBook['SteamDumpUp'])
                if -0.4 <= AMod[3] <= 0.4: pass
                if AMod[3] < -0.4:
                    self._send_control_save(ActOrderBook['SteamDumpDown'])
            else:
                AMod = [0, 0, 0, 0]
        if V6_2_4:
            if V['Trip'] == 1 and V['SIS'] == 0 and V['MSI'] == 0 and V['CNSTime'] > 5 * 60 * 5:

                # 1] Spray Control
                pos = self.mem['BPRZSP']['Val'] + 0.015 * np.clip(AMod[0] * 2, -2, 2)
                zip_spray_pos = (['BPRZSP'], [pos])
                self._send_control_save(zip_spray_pos)

                # 2] Aux Feed
                if AMod[1] < -0.8:
                    self._send_control_save(ActOrderBook['RL_DecreaseAux1Flow'])
                elif -0.8 <= AMod[1] < -0.6:
                    self._send_control_save(ActOrderBook['RL_DecreaseAux2Flow'])
                elif -0.6 <= AMod[1] < -0.4:
                    self._send_control_save(ActOrderBook['RL_DecreaseAux3Flow'])
                elif -0.4 <= AMod[1] < 0.4:
                    pass
                elif 0.4 <= AMod[1] < 0.6:
                    self._send_control_save(ActOrderBook['RL_IncreaseAux3Flow'])
                elif 0.6 <= AMod[1] < 0.8:
                    self._send_control_save(ActOrderBook['RL_IncreaseAux2Flow'])
                elif 0.8 <= AMod[1]:
                    self._send_control_save(ActOrderBook['RL_IncreaseAux1Flow'])

                # 3] SI Supply water
                if AMod[2] < -0.8:
                    self._send_control_save(ActOrderBook['CloseSI'])
                elif -0.8 <= AMod[2] < -0.6:
                    self._send_control_save(ActOrderBook['StopCHP2'])
                elif -0.6 <= AMod[2] < 0.6:
                    pass
                elif 0.6 <= AMod[2] < 0.8:
                    self._send_control_save(ActOrderBook['RunCHP2'])
                elif 0.8 <= AMod[2]:
                    self._send_control_save(ActOrderBook['OpenSI'])

                # 4] Steam Dump
                SteamDumpRate = 4
                DumpPos = self.mem['PMSS']['Val'] + 2.0E5 * np.clip(AMod[3] * SteamDumpRate,
                                                                    - SteamDumpRate, SteamDumpRate) * 0.2
                zip_Dump_pos = (['PMSS'],[DumpPos])
                self._send_control_save(zip_Dump_pos)

            else:
                pass #AMod = [0, 0, 0, 0]
        self.DIS_CSF_Info += f'[A:{AMod}]\t'
        self.Loger_txt += f'{AMod}\t'
        # -------------------------------------------------------------------------------------------------------
        # CSF 1 Act
        if CSF_level[0] != 0: self.DIS_CSF_Info += f'1: {CSF_level[0]} \t'
        # -------------------------------------------------------------------------------------------------------
        # CSF 2 Act
        if CSF_level[1] != 0: self.DIS_CSF_Info += f'2: {CSF_level[1]} \t'
        # -------------------------------------------------------------------------------------------------------
        # CSF 3 Act
        if CSF_level[2] != 0:
            self.DIS_CSF_Info += f'3: {CSF_level[2]} \t'
            if CSF_level[2] == 3:  # All Aux <= 33
                if V['AllSGFeed'] <= 33:
                    # 1] Find width low SG nub
                    LowSGNub = V['SG123Wid'].index(min(V['SG123Wid']))  # 0: SG1, 1:SG2, 2:SG3
                    # 2] Supply water to the low SG
                    if LowSGNub == 0: self._send_control_save(ActOrderBook['IncreaseAux1Flow'])
                    if LowSGNub == 1: self._send_control_save(ActOrderBook['IncreaseAux2Flow'])
                    if LowSGNub == 2: self._send_control_save(ActOrderBook['IncreaseAux3Flow'])
        # -------------------------------------------------------------------------------------------------------
        # CSF 4 Act
        if CSF_level[3] != 0: self.DIS_CSF_Info += f'4: {CSF_level[3]} \t'
        # -------------------------------------------------------------------------------------------------------
        # CSF 5 Act
        if CSF_level[4] != 0: self.DIS_CSF_Info += f'5: {CSF_level[4]} \t'
        # -------------------------------------------------------------------------------------------------------
        # CSF 6 Act
        if CSF_level[5] != 0: self.DIS_CSF_Info += f'6: {CSF_level[5]} \t'
        # -------------------------------------------------------------------------------------------------------
        # CSF info DIS
        # print(self.DIS_CSF_Info)
        # -------------------------------------------------------------------------------------------------------
        # Done Act
        self._send_control_to_cns()
        return AMod

    def step(self, A):
        """
        A를 받고 1 step 전진
        :param A: [Act], numpy.ndarry, Act는 numpy.float32
        :return: 최신 state와 reward done 반환
        """
        # Old Data (time t) ---------------------------------------
        # self.check_CSFTree()
        AMod = self.send_act(A)
        V = {
            # ETC
            'Trip': self.mem['KLAMPO9']['Val'],
            'SIS': self.mem['KLAMPO6']['Val'],
            'MSI': self.mem['KLAMPO3']['Val'],

            'RCP1': self.mem['KLAMPO124']['Val'], 'RCP2': self.mem['KLAMPO125']['Val'],
            'RCP3': self.mem['KLAMPO126']['Val'], 'NetBRK': self.mem['KLAMPO224']['Val'],
            'CNSTime': self.mem['KCNTOMS']['Val'],

            'SteamDumpManAuto': self.mem['KLAMPO150']['Val'], 'SteamDumpPos': self.mem['ZINST98']['Val'],
            'SteamLine1': self.mem['BHV108']['Val'],
            'SteamLine2': self.mem['BHV208']['Val'],
            'SteamLine3': self.mem['BHV308']['Val'],

            'ChargingManAUto': self.mem['KLAMPO95']['Val'],
            'ChargingValvePos': self.mem['BFV122']['Val'],
            'ChargingPump2State': self.mem['KLAMPO70']['Val'],
            'SIValve': self.mem['BHV22']['Val'],

            # 강화학습을 위한 감시 변수
            'PZRSprayManAuto': self.mem['KLAMPO119']['Val'],
            'PZRSprayPos': self.mem['ZINST66']['Val'],
            'PZRBackHeaterOnOff': self.mem['KLAMPO118']['Val'],
            'PZRProHeaterManAuto': self.mem['KLAMPO117']['Val'],
            'PZRProHeaterPos': self.mem['BHV22']['Val'],

            # CSF 1 Value 미임계 상태 추적도
            'PowerRange': self.mem['ZINST1']['Val'], 'IntermediateRange': self.mem['ZINST2']['Val'],
            'SourceRange': self.mem['ZINST3']['Val'],
            # CSF 2 Value 노심냉각 상태 추적도
            'CoreExitTemp': self.mem['UUPPPL']['Val'],
            'PTCurve': PTCureve().Check(Temp=self.mem['UAVLEG2']['Val'], Pres=self.mem['ZINST65']['Val']),
            # CSF 3 Value 열제거원 상태 추적도
            'SG1Nar': self.mem['ZINST78']['Val'], 'SG2Nar': self.mem['ZINST77']['Val'],
            'SG3Nar': self.mem['ZINST76']['Val'],
            'SG1Pres': self.mem['ZINST75']['Val'], 'SG2Pres': self.mem['ZINST74']['Val'],
            'SG3Pres': self.mem['ZINST73']['Val'],
            'SG1Feed': self.mem['WFWLN1']['Val'], 'SG2Feed': self.mem['WFWLN2']['Val'],
            'SG3Feed': self.mem['WFWLN3']['Val'],

            'AllSGFeed': self.mem['WFWLN1']['Val'] +
                         self.mem['WFWLN2']['Val'] +
                         self.mem['WFWLN3']['Val'],
            'SG1Wid': self.mem['ZINST72']['Val'], 'SG2Wid': self.mem['ZINST71']['Val'],
            'SG3Wid': self.mem['ZINST70']['Val'],
            'SG123Wid': [self.mem['ZINST72']['Val'], self.mem['ZINST71']['Val'], self.mem['ZINST70']['Val']],

            # CSF 4 Value RCS 건전성 상태 추적도
            'RCSColdLoop1': self.mem['UCOLEG1']['List'], 'RCSColdLoop2': self.mem['UCOLEG2']['List'],
            'RCSColdLoop3': self.mem['UCOLEG3']['List'], 'RCSPressure': self.mem['ZINST65']['Val'],
            'CNSTimeL': self.mem['KCNTOMS']['List'],  # PTCurve: ...
            # CSF 5 Value 격납용기 건전성 상태 추적도
            'CTMTPressre': self.mem['ZINST26']['Val'], 'CTMTSumpLevel': self.mem['ZSUMP']['Val'],
            'CTMTRad': self.mem['ZINST22']['Val'],
            # CSF 6 Value RCS 재고량 상태 추적도
            'PZRLevel': self.mem['ZINST63']['Val']
        }
        if V['Trip'] == 1 and V['SIS'] == 0 and V['MSI'] == 0 and V['CNSTime'] > 5 * 60 * 5:

        # GOTICK = input("Want Tick : ")
            self.want_tick = int(200)

        self.Monitoring_ENV.push_ENV_val(i=self.Name,
                                         Dict_val={f'{Para}': self.mem[f'{Para}']['Val'] for Para in
                                                   ['UAVLEG2', 'KCNTOMS', 'ZINST65', 'KLAMPO6', 'KLAMPO9',
                                                    'ZINST63', 'WFWLN1', 'WFWLN2', 'WFWLN3']}
                                         )
        # New Data (time t+1) -------------------------------------
        super(ENVCNS, self).step()
        self._append_val_to_list()
        self.ENVStep += 1

        reward = self.get_reward()
        done, reward = self.get_done(reward)
        self.Monitoring_ENV.push_ENV_reward(i=self.Name,
                                            Dict_val={'R': reward, 'AcuR': self.AcumulatedReward, 'Done': done})

        next_state = self.get_state()
        # ----------------------------------------------------------
        self.ENVlogging(s=self.Loger_txt)
        # self.Loger_txt = f'{next_state}\t'
        self.Loger_txt = ''
        # -------------------------------------------------------------------------------------------------------
        print(self.DIS_CSF_Info)
        # -------------------------------------------------------------------------------------------------------
        return next_state, reward, done, AMod

    def reset(self, file_name):
        # 1] CNS 상태 초기화 및 초기화된 정보 메모리에 업데이트
        super(ENVCNS, self).reset(initial_nub=1, mal=True, mal_case=12, mal_opt=10005, mal_time=30, file_name=file_name)
        # 2] 업데이트된 'Val'를 'List'에 추가 및 ENVLogging 초기화
        self._append_val_to_list()
        self.ENVlogging('')
        # 3] 'Val'을 상태로 제작후 반환
        state = self.get_state()
        # 4] 보상 누적치 및 ENVStep 초기화
        self.AcumulatedReward = 0
        self.ENVStep = 0
        self.ENVGetSIReset = False
        self.Monitoring_ENV.init_ENV_val(self.Name)
        # 5] FIX RADVAL
        self.FixedRad = random.randint(0, 20) * 5
        self.FixedTime = 0
        self.FixedTemp = 0
        # 6] 초반에 제어의 의미가 없어 보임. 실제 냉각이 필요한 제어부분까지 진행
        while True:
            if self.mem['KLAMPO9']['Val'] == 1 and self.mem['KLAMPO6']['Val'] == 0 \
                    and self.mem['KLAMPO3']['Val'] == 0 and self.mem['KCNTOMS']['Val'] > 5 * 60 * 5:
                break
            else:
                state, _, _, _ = self.step([0, 0, 0])
        return state

if __name__ == '__main__':
    # ENVCNS TEST
    env = ENVCNS(Name='Env1', IP='192.168.0.103', PORT=int(f'7101'))
    # Modify input
    env.input_info = [
        ('ZINST78', 1000),
        ('ZINST77', 200),
        ('ZINST76', 100),
    ]
    env.observation_space = 3

    for _ in range(1, 4):
        env.reset(file_name=f'Ep{_}')
        for __ in range(3):
            A = 0
            env.step(int(A))

    # while True:
    #     # A = input(f'{env.ENVStep}A:')
    #     A = 0
    #     env.step(int(A))


# -*- coding: utf-8 -*-
from mxnet import gluon
import numpy as np

class CenterLoss(gluon.HybridBlock):
    """
    Center loss block, not a ndarray matrix.
    It can save feature array and calculate center loss.
    使用gluon的HybridBlock实现的center loss类，能存储feature特征并计算center loss。
    """
    def __init__(self, num_classes, feature_dim, lmbd, **kwargs):
        '''
        :param num_classes: number of actress class
        :param feature_dim: feature dimension
        :param lmbd: lambda factor of center loss
        '''
        super(CenterLoss, self).__init__(**kwargs)
        self._lmda = lmbd
        self.centers = self.params.get('centers', shape=(num_classes, feature_dim)) # 参数矩阵

    def hybrid_forward(self, F, feature, label, centers):
        '''
        :param feature: output feature of mini batch data
        :param label: label of mini batch data
        :param centers: center features of all classes
        '''
        # 计算label的统计分布（0:counter0, 1:counter1, maximun_value:count_m）
        # 从零开始统计到最大值区间内label出现的频次
        hist = F.array(np.bincount(label.asnumpy().astype(int)))
        # 取出label对应的频次（在本batch中），用于对label的损失降权
        # 从而保证所有的向量都得到同等程度的更新
        centers_count = F.take(hist, label)
        # 取出label对应位置的特征向量（和np.take略有不同，dimension kept）
        centers_selected = F.take(centers, label)
        # 计算输出特征和特征矩阵的差值
        diff = feature - centers_selected
        # loss = loss_weight * 0.5 * sum(diff**2, axis=1) / label_weight
        loss = self._lmda * 0.5 * F.sum(F.square(diff), axis=1) / centers_count
        # mean along axis beside 0?
        return F.mean(loss, axis=0, exclude=True)

def _make_conv_block(block_index, num_channel=32, num_layer=2, kernel_size=5, strides=1, padding=2):
    '''
    Input some variable, output a convolution block,
    which contains (convolution, activation, maxpooling) * 2.
    输入一些变量，得到一个包含(卷积，激活，池化)×2的卷积模块。
    :param block_index: block index to name block
    :param num_channel: number of convolution channel
    :param num_layer: number of convolution layer
    :param kernel_size: kernel size in convolution layer
    :param strides: striders size in convolution layer
    :param padding: padding size in convolution layer
    :return out: convolution block
    '''
    out = gluon.nn.HybridSequential(prefix='block_%d_' % block_index)
    with out.name_scope():
        for _ in range(num_layer):
            out.add(gluon.nn.Conv2D(num_channel, kernel_size=kernel_size,
                                    strides=strides, padding=padding, activation='relu'))
            #out.add(gluon.nn.LeakyReLU(alpha=0.01))
        out.add(gluon.nn.MaxPool2D())
    return out

class LeNetPlus(gluon.nn.HybridBlock):
    '''
    A modified version of LeNet.
    LeNet的变形版本。
    '''
    def __init__(self, classes=10, feature_dim=2, **kwargs):
        super(LeNetPlus, self).__init__(**kwargs) # initi with super class function
        '''
        :param classes: class number of LeNetPlus
        :param feature_dim: feature dimension
        '''
        num_channels = [32, 64, 128]
        with self.name_scope():
            self.features = gluon.nn.HybridSequential(prefix='')

            for i, num_channel in enumerate(num_channels):
                self.features.add(_make_conv_block(i, num_channel=num_channel))

            self.features.add(gluon.nn.Flatten()) # flatten layer
            self.features.add(gluon.nn.Dense(feature_dim)) # feature layer
            self.output = gluon.nn.Dense(classes) # output layer

    def hybrid_forward(self, F, x, *args, **kwargs):
        feature = self.features(x)
        output = self.output(feature)
        return output, feature


'''Example script to generate text from Nietzsche's writings.

At least 20 epochs are required before the generated text
starts sounding coherent.

It is recommended to run this script on GPU, as recurrent
networks are quite computationally intensive.

If you try this script on new data, make sure your corpus
has at least ~100k characters. ~1M is better.
'''

from __future__ import print_function
from keras.callbacks import LambdaCallback
from keras.models import Sequential
from keras.models import load_model
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import LSTM
from keras.optimizers import RMSprop
from keras.utils.data_utils import get_file
import numpy as np
import random
import sys
import io
from pathlib import Path

# parameters
dataset = "./datasets/harry-potter-1-2-4.txt"
load_path = "./checkpoint.h5"
sample_length = 800

# Reads the Files and stuff
with io.open(dataset, encoding='utf-8') as f:
    text = f.read().lower()
print('corpus length:', len(text))

chars = sorted(list(set(text)))
print('total chars:', len(chars))
char_indices = dict((c, i) for i, c in enumerate(chars))
indices_char = dict((i, c) for i, c in enumerate(chars))

my_file = Path()
if my_file.is_file():
    print("Found Checkpoint. Loading Saved model")
    model = load_model(load_path)
else:
    print("NO MODEL FOUND")

def sample(preds, temperature=1.0):
    # helper function to sample an index from a probability array
    preds = np.asarray(preds).astype('float64')
    preds = np.log(preds) / temperature
    exp_preds = np.exp(preds)
    preds = exp_preds / np.sum(exp_preds)
    probas = np.random.multinomial(1, preds, 1)
    return np.argmax(probas)

# Generates the Text
print()

start_index = random.randint(0, len(text) - maxlen - 1)
for diversity in [0.2, 0.5, 1.0, 1.2]:
    print('\n----- diversity:', diversity)

    generated = ''
    sentence = text[start_index: start_index + maxlen]
    generated += sentence
    print('----- Generating with seed: "' + sentence + '"')
    sys.stdout.write(generated)

    for i in range(sample_length):
        x_pred = np.zeros((1, maxlen, len(chars)))
        for t, char in enumerate(sentence):
            x_pred[0, t, char_indices[char]] = 1.

        preds = model.predict(x_pred, verbose=0)[0]
        next_index = sample(preds, diversity)
        next_char = indices_char[next_index]

        generated += next_char
        sentence = sentence[1:] + next_char

        sys.stdout.write(next_char)
        sys.stdout.flush()
    print()