# Create your tasks here
from __future__ import absolute_import, unicode_literals
from apps.utils.tasks import CustomTask
from celery import shared_task, group, chain
from apps.utils.sql import DbConnection
from server.settings import get_sysconfig, update_sysconfig
import importlib
from django.core.cache import cache
from apps.enm.models import MpLog, Mpoint, MpointStat, EnStat, EnStat2
from apps.wpm.models import SfLog, StLog
import datetime
from django.db.models import Sum, Avg
from dateutil import tz
from django.conf import settings
from apps.wpm.services import get_sflog
from apps.mtm.models import Mgroup, Material
from apps.fim.services import get_cost_unit, get_price_unit
from apps.fim.models import Fee
from django.core.cache import cache
from apps.enm.services import translate_eval_formula
import logging
from django.db.models import F
from apps.wpm.services import get_pcoal_heat
import traceback
from django.utils import timezone
myLogger = logging.getLogger('log')


def get_current_and_previous_time():
    now = datetime.datetime.now()
    pre = now - datetime.timedelta(hours=1)
    return now, pre


@shared_task(base=CustomTask)
def get_tag_val():
    config = get_sysconfig()
    with DbConnection(config['enm']['db_host'], config['enm']['db_user'], config['enm']['db_password'], config['enm']['db_database']) as cursor:
        last_tag_id = config['enm'].get('last_tag_id', None)
        if not last_tag_id:
            mr = MpLog.objects.all().order_by('-tag_update', 'tag_id').first()
            if mr is None:
                last_tag_id = 0
            else:
                last_tag_id = mr.tag_id
                update_sysconfig({'enm': {'last_tag_id': last_tag_id}})
        cursor.execute(
            "select id, val, tag_code, update_time from tag_value where id > %s order by update_time, id", (last_tag_id))
        results = cursor.fetchall()  # 获取数据后保存至本地
        need_func = {}  # 需要执行测点函数的字典， 此种情况只执行一次
        for row in results:
            mr_one = MpLog()
            mr_one.tag_id, mr_one.tag_val, mr_one.tag_code, mr_one.tag_update = row
            mpoint, _ = Mpoint.objects.get_or_create(code=mr_one.tag_code, defaults={
                                                     'name': mr_one.tag_code, 'code': mr_one.tag_code, 'unit': 'unknown'})
            mr_one.mpoint = mpoint
            mr_one.save()
            last_tag_id = mr_one.tag_id
            if mpoint.func_on_change:
                need_func[mpoint.id] = mr_one.id
        # 执行测点函数
        for key in need_func:
            mpoint_val_on_change.delay(need_func[key])
        update_sysconfig({'enm': {'last_tag_id': last_tag_id}})


@shared_task(base=CustomTask)
def mpoint_val_on_change(mpLogId: str):
    """测点值变化的时候执行任务
    """
    mplog = MpLog.objects.get(id=mpLogId)
    mpoint = mplog.mpoint
    module, func = mpoint.func_on_change.rsplit(".", 1)
    m = importlib.import_module(module)
    f = getattr(m, func)
    f(mplog)  # 同步执行


@shared_task(base=CustomTask)
def cal_mpointstats_duration(start_time: str, end_time: str):
    """
    重跑某一段时间的任务
    """
    start_time = datetime.datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
    end_time = datetime.datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')
    current_time = start_time
    while current_time <= end_time:
        year, month, day, hour = current_time.year, current_time.month, current_time.day, current_time.hour
        cal_mpointstats(0, year, month, day, hour)
        current_time += datetime.timedelta(hours=1)


@shared_task(base=CustomTask)
def cal_mpointstat_hour(mpointId: str, year: int, month: int, day: int, hour: int):
    """
    计算某一测点, 某一时间点某一小时的统计值
    """
    mpoint = Mpoint.objects.get(id=mpointId)
    mytz = tz.gettz(settings.TIME_ZONE)
    dt = datetime.datetime(year=year, month=month,
                           day=day, hour=hour, tzinfo=mytz)
    if mpoint.material:  # 如果计量的是物料
        params = {'mpoint': mpoint, 'type': 'hour'}
        params['year'], params['month'], params['day'], params['hour'] = year, month, day, hour
        val = 0
        if mpoint.formula:
            formula = mpoint.formula
            try:
                val = translate_eval_formula(formula, year, month, day, hour)
            except:
                myLogger.error(
                    '公式执行错误:{}-{}'.format(mpoint.id, formula), exc_info=True)
                return
        else:
            mrs = MpLog.objects.filter(
                mpoint=mpoint,
                tag_update__year=params['year'],
                tag_update__month=params['month'],
                tag_update__day=params['day'],
                tag_update__hour=params['hour']).order_by('tag_update')
            if mrs.exists():
                val = mrs.last().tag_val - mrs.first().tag_val
        ms, _ = MpointStat.objects.get_or_create(**params, defaults=params)
        ms.val = val
        ms.save()

        # 更新更高级别的值
        sum_dict_day = MpointStat.objects.filter(
            type='hour', mpoint=mpoint, year=year, month=month, day=day).aggregate(sum=Sum('val'))
        params_day = {'type': 'day', 'mpoint': mpoint,
                      'year': year, 'month': month, 'day': day}
        ms_day, _ = MpointStat.objects.get_or_create(
            **params_day, defaults=params_day)
        ms_day.val = sum_dict_day['sum']
        ms_day.save()

        sum_dict_month = MpointStat.objects.filter(
            type='day', mpoint=mpoint, year=year, month=month).aggregate(sum=Sum('val'))
        params_month = {'type': 'month', 'mpoint': mpoint,
                        'year': year, 'month': month}
        ms_month, _ = MpointStat.objects.get_or_create(
            **params_month, defaults=params_month)
        ms_month.val = sum_dict_month['sum']
        ms_month.save()

        sum_dict_year = MpointStat.objects.filter(
            type='month', mpoint=mpoint, year=year).aggregate(sum=Sum('val'))
        params_year = {'type': 'year', 'mpoint': mpoint, 'year': year}
        ms_year, _ = MpointStat.objects.get_or_create(
            **params_year, defaults=params_year)
        ms_year.val = sum_dict_year['sum']
        ms_year.save()

        if mpoint.mgroups_allocate:  # 如果有分配系数
            for allocate in mpoint.mgroups_allocate:
                mgroup = Mgroup.objects.get(id=allocate['mgroup'])
                ratio = allocate['ratio']
                # 查找并绑定值班记录
                sflog = get_sflog(mgroup, dt)
                year_s, month_s, day_s = sflog.get_ymd

                params_hour_s = {'type': 'hour_s', 'mpoint': mpoint, 'sflog': sflog,  'mgroup': mgroup, 'year': year,
                                 'month': month, 'day': day, 'year_s': year_s, 'month_s': month_s, 'day_s': day_s, 'hour': hour}
                ms_hour_s, _ = MpointStat.objects.get_or_create(
                    **params_hour_s, defaults=params_hour_s)
                ms_hour_s.val = ms.val*ratio
                ms_hour_s.save()

                # 开始往上计算
                sum_dict_sflog_s = MpointStat.objects.filter(
                    type='hour_s', mpoint=mpoint, year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum('val'))
                params_sflog_s = {'type': 'sflog', 'mpoint': mpoint, 'sflog': sflog,
                                  'year_s': year_s, 'month_s': month_s, 'day_s': day_s, 'mgroup': mgroup}
                ms_sflog_s, _ = MpointStat.objects.get_or_create(
                    **params_sflog_s, defaults=params_sflog_s)
                ms_sflog_s.val = sum_dict_sflog_s['sum']
                ms_sflog_s.save()

                # next_cal_dict = [mpoint.material.id, sflog.id, year, month, day, hour, year_s, month_s, day_s]
                # if next_cal_dict == cache.get('enm_cal_dict', None):
                #     next_cal = 0
                # else:
                #     next_cal = 1
                #     cache.set('enm_cal_dict', next_cal_dict, 60)
                cal_mpointstat_manual(
                    mpoint.id, sflog.id, mgroup.id, year, month, day, hour, year_s, month_s, day_s, 0)


@shared_task(base=CustomTask)
def cal_mpointstats(is_now=1, year=None,  month=None, day=None, hour=None):
    """
    计算所有自动采集测点的统计值，默认当前小时, 可手动传入时间
    """
    if year and month and day and hour is not None:
        pass
    else:
        now, pre = get_current_and_previous_time()
        if is_now:
            year, month, day, hour = now.year, now.month, now.day, now.hour
        else:
            year, month, day, hour = pre.year, pre.month, pre.day, pre.hour

    # 先统计不带公式的测点
    mpoints_without_formula = Mpoint.objects.filter(
        is_auto=True, formula='', func_on_change='')
    # mpoints_without_formula_group = []
    for item in mpoints_without_formula:
        # mpoints_without_formula_group.append(cal_mpointstat_hour.s(item.id, year, month, day, hour))
        cal_mpointstat_hour(item.id, year, month, day, hour)

    # 再统计其他测点
    mpoints_other = Mpoint.objects.filter(
        is_auto=True, func_on_change='').exclude(formula='')
    # mpoints_other_group = []
    for item in mpoints_other:
        # mpoints_other_group.append(cal_mpointstat_hour.s(item.id, year, month, day, hour))
        cal_mpointstat_hour(item.id, year, month, day, hour)

    # 开始计算enstat
    mgroups = Mgroup.objects.filter(need_enm=True).order_by('sort')
    # mgroups_group = []
    year_s, month_s, day_s = 0, 0, 0
    for mgroup in mgroups:
        # mgroups_group.append(cal_enstat.s('hour_s', None, mgroup.id, year, month, day, hour, None, None, None, True, ['material', 'run_hour']))
        year_s, month_s, day_s = cal_enstat(
            'hour_s', None, mgroup.id, year, month, day, hour, None, None, None, True)

    # mgroups_t = mgroups.filter(name__in=['回转窑', '水泥磨'])
    # mgroups_t_group = []
    # for mgroup in mgroups_t:
    #     mgroups_t_group.append(cal_enstat.s('hour_s', None, mgroup.id, year, month, day, hour, None, None, None, True, ['pcoal']))

    # 最后计算enstat2
    cal_enstat2(type="day_s", year_s=year_s, month_s=month_s, day_s=day_s)
    # task_chain = chain(group(mpoints_without_formula_group), group(mpoints_other_group), group(mgroups_group), group(mgroups_t_group), group([cal_enstat2.s(year_s=year, month_s=month)]))
    # task_chain.delay()
    if hour == 21:
        enm_alarm.delay(year_s, month_s, day_s)


@shared_task(base=CustomTask)
def cal_mpointstat_manual(mpointId: str, sflogId: str, mgroupId: str, year: int, month: int, day: int, hour: int, year_s: int, month_s: int, day_s: int, next_cal=0):
    """
    手动录入的测点数据进行往上统计，一级一级往上
    """
    mpoint = Mpoint.objects.get(id=mpointId)
    mgroup = Mgroup.objects.get(id=mgroupId)
    sum_dict_day_s = MpointStat.objects.filter(
        type='sflog', mpoint=mpoint, year_s=year_s, month_s=month_s, day_s=day_s, mgroup=mgroup).aggregate(sum=Sum('val'))
    params_day_s = {'type': 'day_s', 'mpoint': mpoint, 'year_s': year_s,
                    'month_s': month_s, 'day_s': day_s, 'mgroup': mgroup}
    ms_day_s, _ = MpointStat.objects.get_or_create(
        **params_day_s, defaults=params_day_s)
    ms_day_s.val = sum_dict_day_s['sum']
    ms_day_s.save()

    sum_dict_month_s = MpointStat.objects.filter(
        type='day_s', mpoint=mpoint, year_s=year_s, month_s=month_s, mgroup=mgroup).aggregate(sum=Sum('val'))
    params_month_s = {'type': 'month_s', 'mpoint': mpoint,
                      'year_s': year_s, 'month_s': month_s, 'mgroup': mgroup}
    ms_month_s, _ = MpointStat.objects.get_or_create(
        **params_month_s, defaults=params_month_s)
    ms_month_s.val = sum_dict_month_s['sum']
    ms_month_s.save()

    sum_dict_year_s = MpointStat.objects.filter(
        type='month_s', mpoint=mpoint, year_s=year_s, mgroup=mgroup).aggregate(sum=Sum('val'))
    params_year_s = {'type': 'year_s', 'mpoint': mpoint,
                     'year_s': year_s, 'mgroup': mgroup}
    ms_year_s, _ = MpointStat.objects.get_or_create(
        **params_year_s, defaults=params_year_s)
    ms_year_s.val = sum_dict_year_s['sum']
    ms_year_s.save()

    if next_cal:  # 二次计算
        if hour:
            cal_enstat('hour_s', sflogId, mgroupId, year,
                       month, day, hour, year_s, month_s, day_s)
        else:
            cal_enstat('sflog', sflogId, mgroupId, year, month,
                       day, hour, year_s, month_s, day_s)


types_list = ['hour_s', 'sflog', 'day_s', 'month_st', 'month_s', 'year_s']


@shared_task(base=CustomTask)
def cal_enstat(type, sflogId, mgroupId, year, month, day, hour, year_s, month_s, day_s, cascade=True, cal_attrs=[]):
    """
    计算能源数据统计
    """
    if cascade:
        if type in types_list:
            start_index = types_list.index(type)
            new_types_list = types_list[start_index:]
            for type in new_types_list:
                year_s, month_s, day_s = cal_enstat(
                    type, sflogId, mgroupId, year, month, day, hour, year_s, month_s, day_s, False)
        return year_s, month_s, day_s
    if not cal_attrs:
        this_cal_attrs = ['material', 'pcoal', 'run_hour']
    else:
        this_cal_attrs = cal_attrs[:]

    mgroup = Mgroup.objects.get(id=mgroupId)
    if sflogId:
        sflog = SfLog.objects.get(id=sflogId)
    elif year and month and day and hour is not None:
        mytz = tz.gettz(settings.TIME_ZONE)
        dt = datetime.datetime(year=year, month=month,
                               day=day, hour=hour, tzinfo=mytz)
        sflog = SfLog.objects.get(
            start_time__lt=dt, end_time__gte=dt, mgroup=mgroup)
    if sflog:
        year_s, month_s, day_s = sflog.get_ymd
    team = sflog.team
    if team is None and type == 'month_st':
        return year_s, month_s, day_s
    if type == 'hour_s':
        enstat, _ = EnStat.objects.get_or_create(type="hour_s", mgroup=mgroup, year=year, month=month, day=day, hour=hour,
                                                 defaults={'type': 'hour_s', 'mgroup': mgroup, 'year_s': year_s, 'month_s': month_s, 'day_s': day_s,
                                                           'year': year, 'month': month, 'day': day, 'hour': hour, 'sflog': sflog})
    elif type == 'sflog':
        enstat, _ = EnStat.objects.get_or_create(type="sflog", sflog=sflog,
                                                 defaults={'type': 'sflog', 'sflog': sflog, 'mgroup': mgroup, 'year_s': year_s, 'month_s': month_s, 'day_s': day_s})
    elif type == 'day_s':
        enstat, _ = EnStat.objects.get_or_create(type="day_s", mgroup=mgroup, year_s=year_s, month_s=month_s, day_s=day_s,
                                                 defaults={'type': 'day_s', 'mgroup': mgroup, 'year_s': year_s, 'month_s': month_s, 'day_s': day_s})
    elif type == 'month_st':
        enstat, _ = EnStat.objects.get_or_create(type="month_st", mgroup=mgroup, team=team, year_s=year_s, month_s=month_s,
                                                 defaults={'type': 'month_st', 'mgroup': mgroup, 'year_s': year_s, 'month_s': month_s, 'team': team})
    elif type == 'month_s':
        enstat, _ = EnStat.objects.get_or_create(type="month_s", mgroup=mgroup, year_s=year_s, month_s=month_s,
                                                 defaults={'type': 'month_s', 'mgroup': mgroup, 'year_s': year_s, 'month_s': month_s})
    elif type == 'year_s':
        enstat, _ = EnStat.objects.get_or_create(type="year_s", mgroup=mgroup, year_s=year_s,
                                                 defaults={'type': 'year_s', 'mgroup': mgroup, 'year_s': year_s})

    if 'material' in this_cal_attrs:
        # 消耗物料统计(包括电耗)
        input_materials = []
        has_product = False
        input_materials = mgroup.input_materials
        if mgroup.product:
            input_materials.insert(0, mgroup.product.id)
            has_product = True
        imaterial_cost_unit = 0
        imaterial_data = []
        for ind, mid in enumerate(input_materials):
            material = Material.objects.get(id=mid)
            if type == 'hour_s':
                mps = MpointStat.objects.filter(
                    type='hour_s', mgroup=mgroup, year_s=year_s, month_s=month_s, day_s=day_s, hour=hour, mpoint__material=material)
            elif type == 'sflog':
                mps = MpointStat.objects.filter(
                    type='sflog', sflog=sflog, mpoint__material=material)
            elif type == 'day_s':
                mps = MpointStat.objects.filter(
                    type='day_s', mgroup=mgroup, year_s=year_s, month_s=month_s, day_s=day_s, mpoint__material=material)
            elif type == 'month_st':
                mps = MpointStat.objects.filter(
                    type='sflog', mgroup=mgroup, sflog__team=team, year_s=year_s, month_s=month_s, mpoint__material=material)
            elif type == 'month_s':
                mps = MpointStat.objects.filter(
                    type='month_s', mgroup=mgroup, year_s=year_s, month_s=month_s, mpoint__material=material)
            elif type == 'year_s':
                mps = MpointStat.objects.filter(
                    type='year_s', mgroup=mgroup, year_s=year_s, mpoint__material=material)
            if mps.filter(mpoint__is_all=True).exists():
                mps = mps.filter(mpoint__is_all=True)
            amount_consume = mps.aggregate(sum=Sum('val'))['sum']
            if amount_consume is None:
                amount_consume = 0
            if ind == 0 and has_product:  # 如果是产量
                enstat.total_production = amount_consume
            else:
                if material.code in ['pcoal', 'cair', 'steam']:
                    price_unit = 0
                else:
                    price_unit = get_price_unit(material, year_s, month_s)
                cost = amount_consume * price_unit
                try:
                    cost_unit = cost / enstat.total_production
                except Exception as e:
                    cost_unit = 0
                imaterial_cost_unit = imaterial_cost_unit + cost_unit
                if material.code == 'elec':
                    enstat.elec_consume = amount_consume
                    enstat.elec_coal_consume = enstat.elec_consume*0.1229/1000
                    try:
                        enstat.elec_consume_unit = enstat.elec_consume/enstat.total_production
                    except Exception as e:
                        pass
                elif material.code == 'water':
                    enstat.water = amount_consume
                elif material.code == 'pcoal':
                    enstat.pcoal_consume = amount_consume
                elif material.code == 'cair':
                    enstat.cair_consume = amount_consume
                elif material.code == 'steam':
                    enstat.out_steam = amount_consume
                    enstat.out_steam_coal = enstat.out_steam * 128.6 / 1000
                elif material.code == 'ccr':
                    enstat.ccr_consume = amount_consume
                    enstat.kiln_end_heat = enstat.total_production - enstat.ccr_consume

                imaterial_item = {'material': mid, 'material_name': material.name, 'material_code': material.code,
                                  'material_type': material.type, 'price_unit': price_unit, 'amount_consume': amount_consume, 'cost': cost, 'cost_unit': cost_unit}
                imaterial_data.append(imaterial_item)
        enstat.imaterial_data = imaterial_data
        # 其他成本数据
        other_cost_data = []
        other_cost_unit = 0
        fee_qs = Fee.objects.order_by('sort')
        for fee in fee_qs:
            item = {'element': fee.element, 'cate': fee.cate,
                    'name': fee.name, 'id': fee.id}
            item['cost_unit'] = get_cost_unit(mgroup, fee, year_s, month_s)
            other_cost_unit = other_cost_unit + item['cost_unit']
            other_cost_data.append(item)
        enstat.other_cost_data = other_cost_data
        enstat.production_cost_unit = imaterial_cost_unit + other_cost_unit
        enstat.save()
    if enstat.total_production:
        MpointStat.objects.filter(mgroup=enstat.mgroup, mpoint__material__code='elec').exclude(mpoint__ep_monitored=None).update(total_production=enstat.total_production,
                                                                                                                                 elec_consume_unit=F('val')/enstat.total_production)
    if enstat.mgroup.cate == 'section':
        if 'material' in this_cal_attrs:
            # 算能耗
            if enstat.mgroup.name != '回转窑':
                try:
                    enstat.en_consume_unit = enstat.elec_coal_consume / enstat.total_production
                except Exception as e:
                    pass
            enstat.save()
        # 计算一些其他数据
        if type == 'month_st' and 'material' in this_cal_attrs:  # 如果计算的是班月，把主要设备电耗数据拉过来, 方便查询
            # res = MpointStat.objects.filter(type='sflog', year_s=year_s, month_s=month_s, sflog__team=enstat.team, mpoint__ep_monitored__power_kw__gte=100).values(
            #     equipment=F('mpoint__ep_monitored__id'), equipment_name=F('mpoint__ep_monitored__name')).annotate(consume=Sum('val'))
            # res = list(res)
            # for item in res:
            #     try:
            #         item['consume_unit'] = item['consume'] / enstat.total_production
            #     except ZeroDivisionError:
            #         item['consume_unit'] = None
            res = MpointStat.objects.filter(type='sflog', year_s=year_s, month_s=month_s, sflog__team=enstat.team, mpoint__ep_monitored__power_kw__gte=100).values(
                'elec_consume_unit', equipment=F('mpoint__ep_monitored__id'), equipment_name=F('mpoint__ep_monitored__name'))
            enstat.equip_elec_data = list(res)
            enstat.save()
        if enstat.mgroup.name == '回转窑':  # 算单位产品(综合电耗/标煤耗/综合能耗)
            # 综合电耗
            if enstat.type in ['hour_s', 'day_s', 'year_s', 'month_s']:
                pre_enstat = EnStat.objects.filter(
                    type=enstat.type, year_s=enstat.year_s, month_s=enstat.month_s, day_s=enstat.day_s, hour=enstat.hour, mgroup__name='原料磨').first()
                if pre_enstat:
                    try:
                        enstat.celec_consume_unit = enstat.elec_consume_unit + \
                            1.45*pre_enstat.elec_consume_unit
                        enstat.save()
                    except Exception as e:
                        pass

            # 算总煤耗
            if 'pcoal' in this_cal_attrs:
                if type in ['hour_s', 'sflog', 'day_s']:
                    enstat.pcoal_heat = get_pcoal_heat(
                        enstat.year_s, enstat.month_s, enstat.day_s)
                    enstat.pcoal_coal_consume = enstat.pcoal_consume * enstat.pcoal_heat/29307
                elif type == 'month_st':
                    enstat.pcoal_coal_consume = EnStat.objects.filter(
                        type='sflog', mgroup=enstat.mgroup, year_s=year_s, month_s=month_s, sflog__team=enstat.team).aggregate(sum=Sum('pcoal_coal_consume'))['sum']
                elif type == 'month_s':
                    enstat.pcoal_coal_consume = EnStat.objects.filter(
                        type='sflog', mgroup=enstat.mgroup, year_s=year_s, month_s=month_s).aggregate(sum=Sum('pcoal_coal_consume'))['sum']
                elif type == 'year_s':
                    enstat.pcoal_coal_consume = EnStat.objects.filter(
                        type='sflog', mgroup=enstat.mgroup, year_s=year_s).aggregate(sum=Sum('pcoal_coal_consume'))['sum']

                if enstat.pcoal_coal_consume is None:
                    enstat.pcoal_coal_consume = 0

                # 算单位产品标煤耗
                try:
                    enstat.coal_consume_unit = enstat.pcoal_coal_consume / enstat.total_production
                except ZeroDivisionError:
                    pass

                # 综合能耗
                enstat.cen_consume_unit = enstat.coal_consume_unit + \
                    0.1229 * enstat.elec_consume_unit
                enstat.save()
        if enstat.mgroup.name == '水泥磨' and enstat.type not in ['month_st', 'sflog'] and 'pcoal' in this_cal_attrs:
            pre_enstat = EnStat.objects.filter(type=enstat.type, year_s=enstat.year_s, month_s=enstat.month_s,
                                               day_s=enstat.day_s, hour=enstat.hour, mgroup__name='回转窑').first()
            if pre_enstat:
                # 综合能耗
                enstat.cen_consume_unit = enstat.elec_consume_unit * \
                    0.1229 + 0.7*pre_enstat.cen_consume_unit
            enstat.save()
        # 运转时长相关
        if type != 'hour_s' and 'run_hour' in this_cal_attrs:
            enstat.total_hour_now, enstat.shut_hour = get_total_hour_now_and_shut_hour(
                enstat)
            enstat.run_hour = enstat.total_hour_now - enstat.shut_hour
            try:
                enstat.run_rate = (enstat.run_hour / enstat.total_hour_now)*100
                enstat.production_hour = enstat.total_production / enstat.total_hour_now
            except ZeroDivisionError:
                pass
            enstat.save()
    enstat.save()
    return year_s, month_s, day_s  # 返回这个值主要为了全厂数据计算而用


def get_total_hour_now_and_shut_hour(enstat: EnStat):
    from apps.wpm.models import SfLog
    # if enstat.type == 'hour_s':
    #     # 找到停机记录，并划分到该小时
    #     end_time = datetime.datetime(enstat.year, enstat.month, enstat.day, enstat.hour)
    #     start_time = end_time - datetime.timedelta(hours=1)
    #     sts = StLog.objects.filter(mgroup=enstat.mgroup)
    #     sts = (sts.filter(start_time__gt=start_time, start_time__lt=end_time)|sts.filter(start_time__lt=start_time, end_time=None)|sts.filter(end_time__gt=start_time, end_time__lt=end_time)).distinct()
    #     shut_hour = 0
    #     for i in sts:
    #         if i.end_time is None:
    #             run_hour = 0
    #             if i.start_time > start_time:
    #                 run_hour = (i.start_time - start_time).total_seconds/3600
    #             shut_hour = 1 - run_hour
    #         shut_hour = shut_hour +
    #     return 1, 0
    now = datetime.datetime.now().replace(tzinfo=tz.gettz(settings.TIME_ZONE))
    if enstat.type == 'sflog':
        sflog = enstat.sflog
        return sflog.total_hour_now, sflog.shut_hour
    elif enstat.type == 'day_s':
        res = SfLog.objects.filter(end_time__year=enstat.year_s, end_time__month=enstat.month_s, end_time__day=enstat.day_s,
                                   mgroup=enstat.mgroup, end_time__lt=now).aggregate(
            sum1=Sum('total_hour_now'),
            sum2=Sum('shut_hour')
        )
        return res['sum1'] if res['sum1'] else 0, res['sum2'] if res['sum2'] else 0
    elif enstat.type == 'month_st':
        res = SfLog.objects.filter(end_time__year=enstat.year_s, end_time__month=enstat.month_s, mgroup=enstat.mgroup, team=enstat.team, end_time__lt=now).aggregate(
            sum1=Sum('total_hour_now'),
            sum2=Sum('shut_hour')
        )
        return res['sum1'] if res['sum1'] else 0, res['sum2'] if res['sum2'] else 0
    elif enstat.type == 'month_s':
        res = SfLog.objects.filter(end_time__year=enstat.year_s, end_time__month=enstat.month_s, mgroup=enstat.mgroup, end_time__lt=now).aggregate(
            sum1=Sum('total_hour_now'),
            sum2=Sum('shut_hour')
        )
        return res['sum1'] if res['sum1'] else 0, res['sum2'] if res['sum2'] else 0
    elif enstat.type == 'year_s':
        res = SfLog.objects.filter(end_time__year=enstat.year_s, mgroup=enstat.mgroup, end_time__lt=now).aggregate(
            sum1=Sum('total_hour_now'),
            sum2=Sum('shut_hour')
        )
        return res['sum1'] if res['sum1'] else 0, res['sum2'] if res['sum2'] else 0


@shared_task(base=CustomTask)
def cal_enstat2(type: str, year_s: int, month_s: int, day_s: int, cascade=True):
    if cascade:
        if type == 'day_s':
            cal_enstat2('day_s', year_s, month_s, day_s, False)
            cal_enstat2('month_s', year_s, month_s, day_s, False)
        elif type == 'month_s':
            cal_enstat2('month_s', year_s, month_s, day_s, False)
        else:
            return
    if type == 'month_s':
        enstat2, _ = EnStat2.objects.get_or_create(type="month_s", year_s=year_s, month_s=month_s, defaults={
                                                   'year_s': year_s, 'month_s': month_s, 'type': 'month_s'})
    elif type == 'day_s':
        enstat2, _ = EnStat2.objects.get_or_create(type="day_s", year_s=year_s, month_s=month_s, day_s=day_s, defaults={
                                                   'year_s': year_s, 'month_s': month_s, 'day_s': day_s, 'type': 'day_s'})
    # enstat2 = EnStat2.objects.select_for_update().get(id=enstat2.id)  # 加锁
    material_cement = Material.objects.get(code='cement')
    material_clinker = Material.objects.get(code='clinker')
    material_bulk_cement = Material.objects.get(code='bulk_cement')
    material_bag_cement = Material.objects.get(code='bag_cement')

    enstat2.bulk_cement_price = get_price_unit(
        material_bulk_cement, year_s, month_s)
    enstat2.clinker_price = get_price_unit(material_clinker, year_s, month_s)
    enstat2.bag_cement_price = get_price_unit(
        material_bag_cement, year_s, month_s)
    if type == 'month_s':
        enstat2.bulk_cement_val = MpointStat.objects.filter(
            type='month_s', mpoint__material=material_bulk_cement, year_s=year_s, month_s=month_s).aggregate(sum=Sum('val'))['sum']
    elif type == 'day_s':
        enstat2.bulk_cement_val = MpointStat.objects.filter(
            type='day_s', mpoint__material=material_bulk_cement, year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum('val'))['sum']
    if enstat2.bulk_cement_val is None:
        enstat2.bulk_cement_val = 0
    if type == 'month_s':
        enstat2.bag_cement_val = MpointStat.objects.filter(
            type='month_s', mpoint__material=material_bag_cement, year_s=year_s, month_s=month_s).aggregate(sum=Sum('val'))['sum']
    elif type == 'day_s':
        enstat2.bag_cement_val = MpointStat.objects.filter(
            type='day_s', mpoint__material=material_bag_cement, year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum('val'))['sum']
    if enstat2.bag_cement_val is None:
        enstat2.bag_cement_val = 0
    if type == 'month_s':
        enstat2.clinker_val = MpointStat.objects.filter(
            type='month_s', mpoint__material=material_cement, year_s=year_s, month_s=month_s).aggregate(sum=Sum('val'))['sum']
    elif type == 'day_s':
        enstat2.clinker_val = MpointStat.objects.filter(
            type='day_s', mpoint__material=material_cement, year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum('val'))['sum']
    if enstat2.clinker_val is None:
        enstat2.clinker_val = 0

    enstat2.industry_total_val = (enstat2.bulk_cement_val*enstat2.bulk_cement_price+enstat2.bag_cement_val *
                                  enstat2.bag_cement_price+enstat2.clinker_val*enstat2.clinker_price)/10000

    if type == 'month_s':
        res = EnStat.objects.filter(mgroup__product__code='cement', type='month_s', year_s=year_s, month_s=month_s).aggregate(
            sum=Sum('total_production'), avg=Avg('production_cost_unit'))
    elif type == 'day_s':
        res = EnStat.objects.filter(mgroup__product__code='cement', type='day_s', year_s=year_s, month_s=month_s, day_s=day_s).aggregate(
            sum=Sum('total_production'), avg=Avg('production_cost_unit'))

    enstat2.cement_val = res['sum'] if res['sum'] else 0
    enstat2.cement_cost_unit = res['avg'] if res['avg'] else 0
    enstat2.industry_add_val = enstat2.industry_total_val - \
        enstat2.cement_val * enstat2.cement_cost_unit / 10000

    # 全厂电量
    if type == 'month_s':
        enstat_qs = EnStat.objects.filter(
            type='month_s', year_s=year_s, month_s=month_s)
    elif type == 'day_s':
        enstat_qs = EnStat.objects.filter(
            type='day_s', year_s=year_s, month_s=month_s, day_s=day_s)
    res_elec_pcoal = enstat_qs.aggregate(sum1=Sum('elec_consume'), sum2=Sum('elec_coal_consume'), sum3=Sum('pcoal_consume'), sum4=Sum('pcoal_coal_consume'),
                                         sum5=Sum('water_consume'), sum6=Sum('cair_consume'))
    enstat2.elec_consume = res_elec_pcoal['sum1'] if res_elec_pcoal['sum1'] else 0
    enstat2.elec_coal_consume = enstat2.elec_consume*0.1229/1000
    enstat2.pcoal_consume = res_elec_pcoal['sum3'] if res_elec_pcoal['sum3'] else 0
    enstat2.pcoal_coal_consume = res_elec_pcoal['sum4'] if res_elec_pcoal['sum4'] else 0
    enstat2.water_consume = res_elec_pcoal['sum5'] if res_elec_pcoal['sum5'] else 0
    enstat2.cair_consume = res_elec_pcoal['sum6'] if res_elec_pcoal['sum6'] else 0
    enstat2.en_consume = enstat2.pcoal_coal_consume + enstat2.elec_coal_consume
    try:
        enstat2.en_consume_unit = enstat2.en_consume/enstat2.industry_total_val
    except ZeroDivisionError:
        pass
    try:
        enstat2.en_add_consume_unit = enstat2.en_consume/enstat2.industry_add_val
    except ZeroDivisionError:
        pass
    enstat2.save()


def cal_enstat_pcoal_change(enstat, new_pcoal_heat):
    type = enstat.type
    if type in ['hour_s', 'sflog', 'day_s']:
        enstat.pcoal_heat = new_pcoal_heat
        enstat.pcoal_coal_consume = enstat.pcoal_consume * enstat.pcoal_heat/29307
    elif type == 'month_st':
        enstat.pcoal_coal_consume = EnStat.objects.filter(type='sflog', mgroup=enstat.mgroup, year_s=enstat.year_s,
                                                          month_s=enstat.month_s, sflog__team=enstat.team).aggregate(sum=Sum('pcoal_coal_consume'))['sum']
    elif type == 'month_s':
        enstat.pcoal_coal_consume = EnStat.objects.filter(
            type='sflog', mgroup=enstat.mgroup, year_s=enstat.year_s, month_s=enstat.month_s).aggregate(sum=Sum('pcoal_coal_consume'))['sum']
    elif type == 'year_s':
        enstat.pcoal_coal_consume = EnStat.objects.filter(
            type='sflog', mgroup=enstat.mgroup, year_s=enstat.year_s).aggregate(sum=Sum('pcoal_coal_consume'))['sum']

    if enstat.pcoal_coal_consume is None:
        enstat.pcoal_coal_consume = 0
    # 算单位产品标煤耗
    try:
        enstat.coal_consume_unit = enstat.pcoal_coal_consume / enstat.total_production
    except ZeroDivisionError:
        pass

    # 综合能耗
    enstat.cen_consume_unit = enstat.coal_consume_unit + \
        0.1229 * enstat.elec_consume_unit

    enstat.save(update_fields=[
                'pcoal_heat', 'pcoal_coal_consume', 'coal_consume_unit', 'cen_consume_unit'])
    # 同步更新水泥磨的综合能耗，这步有可能不成功，因为水泥磨是后算的, 但是当pcoal_change时这个就有用了
    if type not in ['month_st', 'sflog']:
        next_enstat = EnStat.objects.filter(type=enstat.type, year_s=enstat.year_s, month_s=enstat.month_s,
                                            day_s=enstat.day_s, hour=enstat.hour, mgroup__name='水泥磨').first()
        if next_enstat:
            next_enstat.cen_consume_unit = next_enstat.elec_consume_unit * \
                0.1229 + 0.7*enstat.cen_consume_unit
            next_enstat.save(update_fields=['cen_consume_unit'])


enm_alarms_list = [
    ['回转窑', 'celec_consume_unit', '单位产品综合电耗'],
    ['回转窑', 'coal_consume_unit', '单位产品标煤耗'],
    ['水泥磨', 'elec_consume_unit', '单位产品分布电耗']
]


@shared_task(base=CustomTask)
def enm_alarm(year_s: int, month_s: int, day_s: int):
    """
    enm报警任务
    """
    from apps.ecm.models import Event, EventCate,  Eventdo
    from apps.mtm.models import Goal
    from apps.ecm.service import notify_event
    now = timezone.now()
    event_cate, _ = EventCate.objects.get_or_create(code='consume_exceed', defaults={
                                                    'name': '能耗超过目标值', 'code': 'consume_exceed', 'trigger': 30})
    for item in enm_alarms_list:
        mgroups = Mgroup.objects.filter(name=item[0])
        for mgroup in mgroups:
            enstat = EnStat.objects.filter(
                mgroup=mgroup, type='day_s', year_s=year_s, month_s=month_s, day_s=day_s).first()
            if enstat:
                mgroup_name = item[0]
                goal_cate_str = item[1]
                real_val = getattr(enstat, goal_cate_str, None)
                goal = Goal.objects.filter(
                    goal_cate__code=goal_cate_str, year=year_s, mgroup=mgroup).first()
                if goal:
                    goal_val = getattr(goal, f'goal_val_{month_s}', None)
                    if goal_val and real_val and real_val > goal_val:  # 触发事件
                        event = Event()
                        event.obj_cate = 'enm'
                        event.happen_time = now
                        event.voice_msg = f'{mgroup_name}{item[2]}超过设定目标值'
                        event.enm_data = {'mgroup': mgroup.id, 'mgroup_name': mgroup.name, 'type': f'{goal_cate_str}.exceed',
                                          'year_s': year_s, 'month_s': month_s, 'day_s': day_s, 'val': real_val, 'goal_val': goal_val, 'enstat': enstat.id}
                        event.save()
                        Eventdo.objects.get_or_create(cate=event_cate, event=event, defaults={
                            'cate': event_cate,
                            'event': event
                        })
                        notify_event(event)
