#include "Station.h"
#include "database/DAO.h"
#include "database/SQL.h"
#include "common/fields.h"
#include "app/Device.h"
#include "common/Spdlogger.h"
#include "common/Utils.h"
#include "protocol/MqttEntity.h"
#include "common/JsonN.h"
#include "app/Config.h"
#include "common/Snowflake.h"
#include "app/DataStruct.h"
#include "app/Application.h"

Station::Station() : stationId(0)
{
    mqttCli = std::make_shared<MqttClient>();
    predictStorageIn = vector<int>(144, 0);
    predictStorageOut = vector<int>(144, 0);
    predictCharge = vector<int>(144, 0);

    storage.U = vector<int>(144, 0);
    storage.I = vector<int>(144, 0);
    storage.P = vector<int>(144, 0);

    charge.U = vector<int>(144, 0);
    charge.I = vector<int>(144, 0);
    charge.P = vector<int>(144, 0);

    pv.U = vector<int>(144, 0);
    pv.I = vector<int>(144, 0);
    pv.P = vector<int>(144, 0);
}

void Station::setFields(Fields& fields)
{
    this->stationId = fields.get<int>(DMStation::STATION_ID);
    this->name = fields.value(DMStation::NAME);
    this->capacity = fields.get<double>(DMStation::CAPACITY);
    this->workMode = fields.get<int>(DMStation::WORK_MODE);
    this->code = fields.value(DMStation::CODE);
    this->status = fields.get<int>(DMStation::STATUS);
    
    this->operationDate = fields.value(DMStation::OPERATION_DATE);
    this->launchDate = fields.value("operation_date");
    //this->policy.setFields(fields);
}

void Station::addDevice(int deviceId, std::shared_ptr<Device> device)
{
    mapDevice[deviceId] = device;
}

void Station::addDevice(Fields& fields)
{
    int deviceId = fields.get<int>(DMDevice::DEVICE_ID);
    int stationId = fields.get<int>(DMDevice::STATION_ID);
    if (mapDevice.find(deviceId) != mapDevice.end())
    {
        mapDevice[deviceId]->setFields(fields);
    }
    else
    {
        auto device = Device::create(fields);
        mapDevice[deviceId] = device;
    }
}

std::shared_ptr<Device> Station::getDevice(int deviceId)
{
    auto iter = mapDevice.find(deviceId);
    if (iter!=mapDevice.end())
    {
        return iter->second;
    }
    return nullptr;
}

void Station::groupDevice()
{
    for (auto iter = mapDevice.begin(); iter!=mapDevice.end(); ++iter)
    {
        auto& device = iter->second;
        char key[20] = {};
        sprintf(key, "%03d_%03d_%04d", device->stationId, device->sortNo, device->deviceId);
        mapDeviceGroup[device->category][key] = device;
    }
}

std::shared_ptr<Device> Station::getDeviceByType(int deviceType, std::string code)
{
    for (auto iter = mapDevice.begin(); iter!=mapDevice.end(); ++iter)
    {
        auto device = iter->second;
        if (device->type == deviceType && device->code == code)
        {
            return device;
        }
    }
    return nullptr;
}

void Station::getDeviceByType(int deviceType, std::vector<std::shared_ptr<Device>>& res)
{
    for (auto iter = mapDevice.begin(); iter!=mapDevice.end(); ++iter)
    {
        auto device = iter->second;
        if (device->type == deviceType)
        {
            res.push_back(device);
        }
    }
}

int Station::getDeviceCount(int category)
{
    auto iter = mapDeviceGroup.find(category);
    if (iter != mapDeviceGroup.end())
    {
        return iter->second.size();
    }
    return 0;
}

void Station::getDeviceByCategory(int category, std::vector<std::shared_ptr<Device>>& res)
{
    auto iter = mapDeviceGroup.find(category);
    if (iter != mapDeviceGroup.end())
    {
        res.resize(iter->second.size());
        int i = 0;
        for (auto& item: iter->second)
        {
            res[i++] = item.second;
        }
    }
}

void Station::setWorkMode(int modeId)
{
    this->workMode = modeId;
    std::string sql = SQL(SQL::TYPE::update).table(DMStation::TABLENAME)
        .update(DMStation::WORK_MODE, std::to_string(modeId))
        .where(DMStation::STATION_ID + "=" + std::to_string(stationId)).str();
    Errcode err = DAO::exec(NULL, sql);
    if (err != Errcode::OK)
    {
        spdlog::error("set station work mode failed.");
    }
}



void Station::initMqtt()
{
    if (status!=0 && mqttCli)
    {
        auto& optionMqtt = Config::option.mqtt;
        mqttCli->init(optionMqtt.host, code, optionMqtt.username, optionMqtt.password);
    }
}

void Station::polling()
{
    if (status > 0 && mqttCli)
    {
        if (mqttCli->isConnected)
        {
            mqttCli->polling();
        }
        //else
        //{
        //    // 该函数检查连接状态，若已经连接，则无操作；若未连接，则进行连接操作
        //    this->initMqtt();
        //}
    }
}

int64_t Station::getPollingTS()
{
    return (mqttCli != nullptr) ? mqttCli->tsPolling : 0;
}

void Station::setGarewayWorkMode()
{
    if (!mqttCli) { return; }

    auto policy = Application::data().getPolicyByType(this->workMode);
    njson json;
    json["ts"] = Utils::time();
    json["no"] = 1; // 设备编号
    json["40001"] = this->workMode;
   
    if (policy)
    {
        if (policy->type == 1)
        {
            json["40002"] = njson::array();  // 峰谷套利
            policy->getGatewayJsonPeriods(json["40002"]);
        }
        else if (policy->type == 5)
        {
            json["40021"] = njson::array();  // 自定时段
            policy->getGatewayJsonPeriods(json["40021"]);
        }
    }

    std::string text = json.dump();
    spdlog::info("[station] set gateway workmode [Gateway_YT], stationId={}, text={}", stationId, text);
    mqttCli->publish("Gateway_YT", text);
}

void Station::setGarewayParams()
{
    if (!mqttCli) { return; }
    njson json;
    json["ts"] = Utils::time();
    json["no"] = 1; // 设备编号
    json["40038"] = {gatewayParam.socMin, gatewayParam.socMax, gatewayParam.capacity, gatewayParam.powerSafe, gatewayParam.powerDischarge, gatewayParam.powerCharge};
    json["40058"] = {gatewayParam.backflow, gatewayParam.overload};

    std::string text = json.dump();
    spdlog::info("[station] set gateway params, stationId={}, text={}", stationId, text);
    mqttCli->publish("Gateway_YT", text);
}

string Station::getGatewayMode()
{
    // 0：手动，1：峰谷套利，2：配网增容，3：应急供电，4：并网保电，5：运营支撑
    if (gatewayParam.mode == 0) { return "手动"; }
    else if (gatewayParam.mode == 1) { return "峰谷套利"; }
    else if (gatewayParam.mode == 2) { return "配网增容"; }
    else if (gatewayParam.mode == 3) { return "应急供电"; }
    else if (gatewayParam.mode == 4) { return "并网保电"; }
    else if (gatewayParam.mode == 5) { return "运营支撑"; }
    else { return "--"; };
}

static map<int, string> mapPeriodType =
{
    {0, "无"}, { 1, "尖" }, {2, "峰"}, {3, "平"}, {4, "谷"}, {5, "深谷"},
};
static map<int, string> mapPeriodOper =
{
    {0, "停"}, {1, "放"}, {2, "放"}, {3, "充"}, {4, "充"}, {5, "充"}
};

string Station::getGatewayParam()
{
    stringstream ss;
    std::string str1 = std::format("运行模式：{}<br>储能EMU状态：{}<br>充电桩状态：{}<br>台区电表状态：{}<br><br>",
        getGatewayMode(), 
        this->emuStatus == 1 ? "在线" : (this->emuStatus == 0 ? "离线" : "--"),
        this->cdzStatus == 1 ? "在线" : (this->cdzStatus == 0 ? "离线" : "--"),
        this->tamStatus == 1 ? "在线" : (this->tamStatus == 0 ? "离线" : "--"));
    str1 += "峰谷套利时段：<br>";
    {
        njson json;
        if (JSON::parse(gatewayParam.param1, json))
        {
            for (int i = 0; i<json.size(); ++i)
            {
                str1 += "&nbsp&nbsp&nbsp&nbsp" + std::to_string(i+1) + "月: ";

                ss.str("");
                for (auto& jsonitem : json[i])
                {
                    ss << "(" << setw(2) << setfill('0') << jsonitem[0].get<int>();
                    ss << ":" << setw(2) << setfill('0') << jsonitem[1].get<int>() << " ";
                    ss << mapPeriodType[jsonitem[2].get<int>()] << ")  ";
                }
                str1 += ss.str() + "<br>";
            }
        }
    }
    std::string str2 = "自定时段：<br>";
    {
        njson json;
        if (JSON::parse(gatewayParam.param2, json))
        {
            for (int i = 0; i<json.size(); ++i)
            {
                str2 += "&nbsp&nbsp&nbsp&nbsp时段" + std::to_string(i+1) + ": ";
                ss.str("");
                for (auto& jsonitem : json[i])
                {
                    ss << "(" << setw(2) << setfill('0') << jsonitem[0].get<int>();
                    ss << ":" << setw(2) << setfill('0') << jsonitem[1].get<int>() << " ";
                    ss <<  mapPeriodOper[jsonitem[2].get<int>()] << ")  ";
                }
                str2 += ss.str() + "<br>";
            }
        }
    }
    std::string str3 = "其它参数：<br>";
    {
        njson json;
        if (JSON::parse(gatewayParam.param3, json))
        {
            for (int i = 0; i<json.size(); ++i)
            {
                int val = json[i].get<int>();
                if (i==0) { str3 += "&nbsp&nbsp&nbsp&nbsp储能放电下限值SOC: " + std::to_string(val) + "<br>"; } //储能放电下限值 SOC : 40038  (%:0-99 且小于充电上限值)
                else if (i==1) { str3 += "&nbsp&nbsp&nbsp&nbsp储能充电上限值SOC: " + std::to_string(val) + "<br>"; }//储能充电上限值 SOC : 40039  (%:1-100 且大于放电下限值)
                else if (i==2) { str3 += "&nbsp&nbsp&nbsp&nbsp台区变压器容量: " + std::to_string(val) + "<br>"; }//台区变压器容量 : 40040  (KVVA 160-1600)
                else if (i==3) { str3 += "&nbsp&nbsp&nbsp&nbsp安全输入功率: " + std::to_string(val) + "<br>"; }//安全输入功率 : 40041  (KW 0-400)
                else if (i==4) { str3 += "&nbsp&nbsp&nbsp&nbsp储能最大放电功率: " + std::to_string(val) + "<br>"; }//储能最大放电功率 : 40042  (1KW0-150)
                else if (i==5) { str3 += "&nbsp&nbsp&nbsp&nbsp储能最大充电功率: " + std::to_string(val) + "<br>"; }//储能最大充电功率::40043  (1KW0-150)
                else if (i==6) { str3 += "&nbsp&nbsp&nbsp&nbsp运行状态: " + std::to_string(val) + "<br>"; }//运行状态 : 40044  (只读不写，0:无 1:高峰放电 2:低谷充电)
                else if (i==7) { str3 += "&nbsp&nbsp&nbsp&nbsp台区电表变比: " + std::to_string(val) + "<br>"; }//台区电表变比:40045
                //else if (i==8) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(年): " + std::to_string(val) + "<br>"; }//对时(年) : 40051
                //else if (i==9) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(月): " + std::to_string(val) + "<br>"; }//对时(月) : 40052
                //else if (i==10) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(日): " + std::to_string(val) + "<br>"; }//对时(日) : 40053
                //else if (i==11) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(时): " + std::to_string(val) + "<br>"; }//对时(时) : 40054
                //else if (i==12) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(分): " + std::to_string(val) + "<br>"; }//对时(分) : 40055
                //else if (i==13) { str3 += "&nbsp&nbsp&nbsp&nbsp对时(秒): " + std::to_string(val) + "<br>"; }//对时(秒) : 40056
                //else if (i==19) { str3 += "&nbsp&nbsp&nbsp&nbsp时间段月份: " + std::to_string(val) + "<br>"; }//时间段月份 : 40057(1-12 对应1月-12月)
                else if (i==20) { str3 += "&nbsp&nbsp&nbsp&nbsp防逆流回差: " + std::to_string(val) + "<br>"; }//防逆流回差 : 40058(1KW 10-300)
                else if (i==21) { str3 += "&nbsp&nbsp&nbsp&nbsp防过载回差: " + std::to_string(val) + "<br>"; }//防过载回差 : 40059(1KW 10-300)
            }
        }
    }
    return str1 + "<br/>" + str2 + "<br/>" + str3;
}

void Station::checkDevice()
{
    int err = 0;
    for (auto& item: mapDevice)
    {
        auto& device = item.second;
        if (device)
        {
            if (Utils::time() - device->ts > 60*6)
            {
                device->online = 0;
            }
            if (device->type != 5 && (device->err == 1 || device->online == 0))
            {
                err = 1;
            }
        }
    }
    this->err = err;
}

void Station::readAlert(std::shared_ptr<Device> device, std::string addr, int v, std::string text)
{
    int64_t ts = Utils::time();
    std::string alertId = std::to_string(device->deviceId) + "_" + addr;
    int tsCache = mapAlertCache[alertId];
    if (ts - tsCache > 60*30)
    {
        Fields fields;
        fields.set("log_id", Snowflake::instance().getIdStr());
        if (device) { fields.set("device_id", device->deviceId); }
        fields.set("type", int(EAlertType::DEVICE));
        fields.set("errcode", addr);
        fields.set("content", text + ":故障(" + std::to_string(v) + ")");
        fields.set("status", 0);
        auto dao = DaoEntity::create("log_alert");
        dao->insertFields(fields);
        mapAlertCache[alertId] = ts;
    }
}

void Station::readEnergyData(int deviceNo, string addr, int val)
{
    if (deviceNo == 1) // 入网电表
    {
        if (addr == "0x000B") { this->grid.voltage = val; }  // A相电压	R	uint32	1V			0x000B
        if (addr == "0x0011") { this->grid.current = val; }  // A相电流	R	int32	1A			0x0011
        if (addr == "0x0023") { this->grid.power = val; }    // 三相总有功	R	int32	1kW			0x0023
        // 功率因数 ?? 电表没有功率因数数据
    }
    else if (deviceNo == 2) // 储能电表
    {
        statData.ts = Utils::time();

        // 功率因数 ?? 电表没有功率因数数据
        if (addr == "0x002F") { statData.dayElectIn = val; }	 // 日充电电量	R	uint32	1kWh			0x002F
        else if (addr == "0x0031") { statData.dayElectOut = val; }	    //日放电电量	R	uint32	1kWh			0x0031
        else if (addr == "0x0033") { statData.dayFeeIn = val; }	        //日充电费用	R	uint32	1RMB			0x0033
        else if (addr == "0x0035") { statData.dayFeeOut = val; }	    //日放电费用	R	uint32	1RMB			0x0035
        else if (addr == "0x0037") { statData.dayIncome = val; }	    //日收益	    R	int32	1RMB			0x0037
        else if (addr == "0x004D") { statData.totalElectIn = val; }	    //总充电电量	R	uint32	1kWh			0x004D
        else if (addr == "0x004F") { statData.totalElectOut = val; }	//总放电电量	R	uint32	1kWh			0x004F
        else if (addr == "0x0051") { statData.totalFeeIn = val; }	    //总充电费用	R	uint32	1RMB			0x0051
        else if (addr == "0x0053") { statData.totalFeeOut = val; }	    //总放电费用	R	uint32	1RMB			0x0053
        else if (addr == "0x0055") { statData.totalIncome = val; }	    //总收益	    R	int32	1RMB			0x0055

        else if (addr == "0x0039") { statData.dayElectIn_J = val; }     //日正向尖有功电能	R	uint32	1kWh	0x0039
        else if (addr == "0x003B") { statData.dayElectIn_F = val; }     //日正向峰有功电能	R	uint32	1kWh	0x003B
        else if (addr == "0x003D") { statData.dayElectIn_P = val; }     //日正向平有功电能	R	uint32	1kWh	0x003D
        else if (addr == "0x003F") { statData.dayElectIn_G = val; }     //日正向谷有功电能	R	uint32	1kWh	0x003F
        else if (addr == "0x0041") { statData.dayElectIn_Total = val; } //日正向总有功电能	R	uint32	1kWh	0x0041

        else if (addr == "0x0043") { statData.dayElectOut_J = val; }        //日反向尖有功电能	R	uint32	1kWh	0x0043
        else if (addr == "0x0045") { statData.dayElectOut_F = val; }        //日反向峰有功电能	R	uint32	1kWh	0x0045
        else if (addr == "0x0047") { statData.dayElectOut_P = val; }        //日反向平有功电能	R	uint32	1kWh	0x0047
        else if (addr == "0x0049") { statData.dayElectOut_G = val; }        //日反向谷有功电能	R	uint32	1kWh	0x0049
        else if (addr == "0x004B") { statData.dayElectOut_Total = val; }    //日反向总有功电能	R	uint32	1kWh	0x004B

        else if (addr == "0x0057") { statData.totalElectIn_J = val; }       //总正向尖有功电能	R	uint32	1kWh	0x0057
        else if (addr == "0x0059") { statData.totalElectIn_F = val; }       //总正向峰有功电能	R	uint32	1kWh	0x0059
        else if (addr == "0x005B") { statData.totalElectIn_P = val; }       //总正向平有功电能	R	uint32	1kWh	0x005B
        else if (addr == "0x005D") { statData.totalElectIn_G = val; }       //总正向谷有功电能	R	uint32	1kWh	0x005D
        else if (addr == "0x005F") { statData.totalElectIn_Total = val; }   //总正向总有功电能	R	uint32	1kWh	0x005F
        else if (addr == "0x0061") { statData.totalElectOut_J = val; }      //总反向尖有功电能	R	uint32	1kWh	0x0061
        else if (addr == "0x0063") { statData.totalElectOut_F = val; }      //总反向峰有功电能	R	uint32	1kWh	0x0063
        else if (addr == "0x0065") { statData.totalElectOut_P = val; }      //总反向平有功电能	R	uint32	1kWh	0x0065
        else if (addr == "0x0067") { statData.totalElectOut_G = val; }      //总反向谷有功电能	R	uint32	1kWh	0x0067
        else if (addr == "0x0069") { statData.totalElectOut_Total = val; }  //总反向总有功电能	R	uint32	1kWh	0x0069
    }
}

void Station::readEMSData(int deviceNo, string addr, int val)
{
     // ["A相电压", "0x107E", "0.0", " V", "1"] ,
     // ["A相电流", "0x1084", "0.0", " A"],
     // ["总有功功率", "0x1096", "0.0", " kW"]
}

void Station::readBMSData(int deviceNo, string addr, int val)
{
    if (addr == "0x0003") { this->storage.voltage = val * 0.1; }      // ["电压", "0x0003", "0.0", " V", "0.1"],
    else if (addr == "0x0005") { this->storage.current = val * 0.1; } // ["电流", "0x0005", "0.0", " A", "0.1"] ,
    else if (addr == "0x000F") { this->storage.power = val; }   // ["堆功率", "0x000F", "0.0", " kW"] ,
}

// 充电桩
void Station::readChargeData(int deviceNo, string addr, int val)
{
    if (addr == "11") { charge.connector1.isCharge = val; } //枪1:状态	R	uint16				11
        //枪1 : 需求电压	R	uint16	12
        //枪1 : 需求电流	R	uint16	13
        //枪1 : 需求功率	R	uint16	14
    else if (addr == "15") { charge.connector1.voltage = val * 0.1; }   //枪1 : 输出电压	R	uint16	15
    else if (addr == "16") { charge.connector1.current = val * 0.01; }  //枪1 : 输出电流	R	uint16	16
    else if (addr == "17") { charge.connector1.power = val * 0.1; }     //枪1 : 输出功率	R	uint16	17
        //枪1 : 功率限值	R	uint16	18
    else if (addr == "21") { charge.connector2.isCharge = val; }        //枪2 : 状态	R	uint16	21
        //枪2 : 需求电压	R	uint16	22
        //枪2 : 需求电流	R	uint16	23
        //枪2 : 需求功率	R	uint16	24
    else if (addr == "25") { charge.connector2.voltage = val * 0.1; }   //枪2 : 输出电压	R	uint16	25
    else if (addr == "26") { charge.connector2.current = val * 0.01; }  //枪2 : 输出电流	R	uint16	26
    else if (addr == "27") { charge.connector2.power = val * 0.1; }     //枪2 : 输出功率	R	uint16	27
        //枪2 : 功率限值	R	uint16	28

    bool isCharge = (charge.connector1.isCharge || charge.connector2.isCharge);
    charge.status = isCharge ? 2 : 1;
}
void Station::readTHData(int deviceNo, string addr, int val)
{
    auto& unit = mapTempHumUnit[deviceNo];
    if (addr == "0x0001") { ; }         //所属通道号	R	uint16		1		0x0001
    else if (addr == "0x0002") { ; }    //所属温湿度号	R	uint16		1~10		0x0002
    else if (addr == "0x0003") //温度	R	int16	0.1℃			0x0003
    {  
        unit.temp = float(val) * 0.1; 
        if (deviceNo == 1) temperature = unit.temp;
    }     
    else if (addr == "0x0004")  //湿度	R	int16	0.1℃			0x0004
    { 
        unit.hum = float(val) * 0.1; 
        if (deviceNo == 1) humidity = unit.hum;
    }
}

void Station::readFire40Data(int deviceNo, string addr, int val)
{
    auto& unit = mapFire40Unit[deviceNo];

    if (addr == "0x0001") { ; }	 //所属通道号	R	uint16		1~10			0x0001
    else if (addr == "0x0002") { ; }   //主控数量	R	uint16		1			0x0002
    else if (addr == "0x0003") { ; }   //主控ID	R	uint16		1			0x0003
    else if (addr == "0x0004") { unit.statusMain = val; }   //主控状态	R	uint16		0：正常 1：预警 2：火警			0x0004
    else if (addr == "0x0005") { ; }   //主控硬件版本	R	uint16[2]		主控硬件版本			0x0005
    else if (addr == "0x0007") { ; }   //主控软件版本	R	uint16[2]		主控软件版本			0x0007
    else if (addr == "0x0009") { ; }   //主电状态	R	uint16		0：使用市电 1：使用备电			0x0009
    else if (addr == "0x000A") { ; }   //备电电流	R	uint32	0.1A				0x000A
    else if (addr == "0x000C") { ; }   //备电电压	R	uint32	0.1V				0x000C
    else if (addr == "0x000E") { ; }   //可用容量	R	uint32	0.01Ah				0x000E
    else if (addr == "0x0010") { ; }   //可充放容量	R	uint32	0.01Ah				0x0010
    else if (addr == "0x0012") { unit.usedAlarm = val; }   //警铃是否使用	R	uint16					0x0012
    else if (addr == "0x0013") { unit.statusAlarm = val; }   //警铃状态	R	uint16		0：无效 1：掉线 2：正常 3：启动			0x0013
    else if (addr == "0x0014") { unit.usedValve = val; }   //瓶头阀是否使用	R	uint16					0x0014
    else if (addr == "0x0015") { unit.statusValve = val; }   //瓶头阀状态	R	uint16		0：无效 1：掉线 2：正常 3：启动			0x0015
    else if (addr == "0x0016") { unit.usedMCP = val; }   //手报是否使用	R	uint16					0x0016
    else if (addr == "0x0017") { unit.statusMCP = val; }   //手报状态	R	uint16		0：无效 1：掉线 2：正常 3：启动			0x0017
    else if (addr == "0x0018") { ; }   //簇控制器数量	R	uint16					0x0018
    else if (addr == "0x0019") { ; }   //复合探测器总数量	R	uint16					0x0019
    else if (addr == "0x001A") { ; }   //烟雾探测器总数量	R	uint16					0x001A
    else if (addr == "0x001B") { ; }   //压力探测器总数量	R	uint16					0x001B
    else if (addr == "0x001C") { ; }   //吸气式探测器总数量	R	uint16					0x001C
    else if (addr == "0x001D") { ; }   //PACK探测器总数量	R	uint16					0x001D
    else if (addr == "0x001E") { ; }   //电池总数量	R	uint16					0x001E
}


void Station::readCoolingData(int deviceNo, string addr, int val)
{
    auto& unit = mapCoolingUnit[deviceNo];

    if (addr == "0x1001") { ; }	 //所属通道号	R	uint16		1		0x1001
    else if (addr == "0x1002") { ; }//    所属冷机号	R	uint16		1~10		0x1002
    else if (addr == "0x1003") { coolingStatus = unit.powerOn = val; }//    开关	R	uint16		0：关机，1：开机		0x1003
    else if (addr == "0x1004") { unit.mode = val; }//    采样模式	R	uint16	0-出水温度 1-电芯温度			0x1004
    else if (addr == "0x1005") { unit.cooling = val; }//    制冷状态	R	uint16		0：关闭, 1：启动		0x1005
    else if (addr == "0x1006") { unit.heating = val; }//    制热状态	R	uint16		0：关闭, 1：启动		0x1006
    else if (addr == "0x1007") { unit.highTemp = val; }//    高温告警	R	uint16		0：正常，1：告警		0x1007
    else if (addr == "0x1008") { unit.lowTemp = val; }//    低温告警	R	uint16		0：正常，1：告警		0x1008
    else if (addr == "0x1009") { unit.highPressure = val; }//    高压告警	R	uint16		0：正常，1：告警		0x1009
    else if (addr == "0x100A") { unit.lowPressure = val; }//    低压告警	R	uint16		0：正常，1：告警		0x100A
    else if (addr == "0x100B") { ; }//    进水温度传感器	R	uint16		0：正常，1：告警		0x100B
    else if (addr == "0x100C") { ; }//    出水温度传感器	R	uint16		0：正常，1：告警		0x100C
    else if (addr == "0x100D") { ; }//    进水压力传感器	R	uint16		0：正常，1：告警		0x100D
    else if (addr == "0x100E") { ; }//    出水压力传感器	R	uint16		0：正常，1：告警		0x100E
}

static void JSONReadArrayItem(njson& json, int i, int& v)
{
    if (json.is_array() && i < json.size())
    {
        v = json[i].get<int>();
    }
}

void Station::readGatewayMode(int deviceNo, int mode, string p1, string p2, string p3)
{
    auto device = this->getDeviceByType(int(EDeviceType::GATEWAY), Utils::toStr(deviceNo));
    if (device) 
    {
        device->online = true;
        device->ts = Utils::time();
    }
    if (mode != -1) { this->gatewayParam.mode = mode; }
    if (!p1.empty()) {  this->gatewayParam.param1 = p1; }
    if (!p2.empty()) { this->gatewayParam.param2 = p2; }
    if (!p3.empty()) { this->gatewayParam.param3 = p3; }

    njson json;
    if (JSON::parse(gatewayParam.param3, json))
    {
        JSONReadArrayItem(json, 0, gatewayParam.socMin); //储能放电下限值 SOC : 40038  (%:0-99 且小于充电上限值)
        JSONReadArrayItem(json, 1, gatewayParam.socMax); //储能充电上限值 SOC : 40039  (%:1-100 且大于放电下限值)
        JSONReadArrayItem(json, 2, gatewayParam.capacity); //台区变压器容量 : 40040  (KVVA 160-1600)
        JSONReadArrayItem(json, 3, gatewayParam.powerSafe); //安全输入功率 : 40041  (KW 0-400)
        JSONReadArrayItem(json, 4, gatewayParam.powerDischarge); //储能最大放电功率 : 40042  (1KW0-150)
        JSONReadArrayItem(json, 5, gatewayParam.powerCharge); //储能最大充电功率::40043  (1KW0-150)
        JSONReadArrayItem(json, 6, gatewayParam.status);    //运行状态 : 40044  (只读不写，0:无 1:高峰放电 2:低谷充电)
        JSONReadArrayItem(json, 7, gatewayParam.vtRatio);   //台区电表变比:40045
        JSONReadArrayItem(json, 20, gatewayParam.backflow); //防逆流回差 : 40058(1KW 10-300)
        JSONReadArrayItem(json, 21, gatewayParam.overload); //防过载回差 : 40059(1KW 10-300)
    }
}

void Station::readGatewayStatus(int deviceNo, int cdzStatus, int emuStatus, int tamStatus)
{
    auto device = this->getDeviceByType(int(EDeviceType::GATEWAY), Utils::toStr(deviceNo));
    if (device) 
    {
        device->online = true;
        device->ts = Utils::time(); 
    }

    if (cdzStatus == 0) charge.status = 0;
    else if (charge.status == 0) charge.status = 1;

    if (emuStatus == 0) storage.status = 0;
    else if (storage.status == 0) storage.status = 1;

    //充电桩 1：在线，0：离线
    if (cdzStatus >= 0)
    {
        if (cdzStatus != this->cdzStatus)
        {
            std::string text = "场站[" + name + "(" + std::to_string(stationId) + ")]充电桩状态变化：" + (cdzStatus>0 ? "在线" : "离线");
            if (this->cdzStatus < 0) { text = "系统启动，" + text; }
            DAO::insertSystemLogDevice(stationId, 0, text, cdzStatus);
            this->cdzStatus = cdzStatus;
        }
    }
    //储能 1：在线，0：离线
    if (emuStatus >= 0)
    {
        if (emuStatus != this->emuStatus)
        {
            std::string text = "场站[" + name + "(" + std::to_string(stationId) + ")]储能EMU状态变化：" + (emuStatus>0 ? "在线" : "离线");
            if (this->emuStatus < 0) { text = "系统启动，" + text; }
            DAO::insertSystemLogDevice(stationId, 0, text, emuStatus);
            this->emuStatus = emuStatus;
        }
    }

    //台区电表 1：在线，0：离线
    if (tamStatus >= 0)
    {
        if (tamStatus != this->tamStatus)
        {
            std::string text = "场站[" + name + "(" + std::to_string(stationId) + ")]台区电表状态变化：" + (tamStatus > 0 ? "在线" : "离线");
            if (this->tamStatus < 0) { text = "系统启动，" + text; }
            DAO::insertSystemLogDevice(stationId, 0, text, tamStatus);
            this->tamStatus = tamStatus;
        }
    }
}

static void DeviceReadJsonArray(std::shared_ptr<Device> device, njson& json, string key)
{
    if (json.contains(key))
    {
        vector<int> vecd = json[key].get<std::vector<int>>();
        for (int i = 0; i <= vecd.size(); ++i)
        {
            device->setParam(key + std::to_string(i+1), vecd[i]);
        }
    }
}

void Station::readTDData(int deviceNo, string str)
{
    njson json;
    bool ret = JSON::parse(str, json);
    if (!ret) 
    {
        spdlog::error("TD_YC parse error: json={}", str);
        return;
    }
    auto device = this->getDeviceByType(int(EDeviceType::E_METER_TD), Utils::toStr(deviceNo));
    if (!device)
    {
        spdlog::error("TD_YC parse error, get device failed, deviceNo={}, json={}", deviceNo, str);
        return;
    }
    DeviceReadJsonArray(device, json, "U");
    DeviceReadJsonArray(device, json, "I");
    DeviceReadJsonArray(device, json, "P");
    DeviceReadJsonArray(device, json, "Ep");   // 正向总、尖、峰、平、谷电能
    DeviceReadJsonArray(device, json, "En");   // 反向总、尖、峰、平、谷电能
    DeviceReadJsonArray(device, json, "EpTo"); // 当日正向总、尖、峰、平、谷电能
    DeviceReadJsonArray(device, json, "EnTo"); // 当日反向总、尖、峰、平、谷电能
}

static std::string MapValueToJson(int npos, std::map<int, float>& mapV)
{
    njson jsonarray = njson::array();
    for (int i = 0; i<=npos; i++)
    {
        jsonarray.push_back(int(mapV[i]));
    }
    return jsonarray.dump();
}
void Station::setCache(int datatype, std::vector<float>& vd)
{
    std::map<int, float>* mapptr = NULL;
    if (datatype == 1) { mapptr = &mapCacheElectIn; }
    else if (datatype == 2) { mapptr = &mapCacheElectOut; }
    else if (datatype == 3) { mapptr = &mapCacheElectCharger; }
    if (mapptr)
    {
        const int step = 600;
        const int N = 86400/step;
        int64_t tsSeconds = Utils::timeDaySeconds();
        int npos = tsSeconds / step;
        for (int i = 0; i<N; ++i)
        {
            if (i < vd.size()) { (*mapptr)[i] = vd[i]; }
            else if (i <= npos) { (*mapptr)[i] = 0; }
        }
    }
}
void Station::cache()
{
    int64_t tDaySeconds = Utils::timeDaySeconds();
    int npos = tDaySeconds / 600;
    int offset = tDaySeconds % 600;
    bool save = false;
    if (posCache != npos && offset <= 180) // 超过时间点3分钟内没有存储，进行兼容处理
    {
        if (npos == 0)
        {
            std::fill(storage.U.begin(), storage.U.end(), 0);
            std::fill(storage.I.begin(), storage.I.end(), 0);
            std::fill(storage.P.begin(), storage.P.end(), 0);
            std::fill(charge.U.begin(), charge.U.end(), 0);
            std::fill(charge.I.begin(), charge.I.end(), 0);
            std::fill(charge.P.begin(), charge.P.end(), 0);
            std::fill(pv.U.begin(), pv.U.end(), 0);
            std::fill(pv.I.begin(), pv.I.end(), 0);
            std::fill(pv.P.begin(), pv.P.end(), 0);
        }
        save = true;
        posCache = npos;
    }
    if (save && npos < storage.U.size())
    {
        storage.U[npos] = storage.voltage;
        storage.I[npos] = storage.current;
        storage.P[npos] = storage.power;

        charge.U[npos] = charge.connector1.voltage;
        charge.I[npos] = charge.connector1.current;
        charge.P[npos] = charge.connector1.power;

        //pv.U[npos];
        //pv.I[npos];
        //pv.P[npos];
    }
}

static njson VectorToJsonArray(vector<float>& vec, int n)
{
    //int v = Utils::random(40, 50);
    njson jsonArray = njson::array();
    for (int i = 0; i <= n && i < vec.size(); ++i)
    {
        jsonArray.push_back(int(vec[i]));
        //jsonArray.push_back(v + Utils::random(1, 6) -3);
    }
    return jsonArray;
}

void Station::writeStatistic()
{
    auto dao = DaoEntity::create("history_day");
    std::string dt = Utils::dateStr();
    int64_t tTime = Utils::time();
    int64_t tDate = Utils::date();
    int64_t tDelta = tTime - tDate;
    int npos = (tTime-tDate) / 600;
    int tOffset = tDelta % 600;

    for (auto iter = mapDevice.begin(); iter!=mapDevice.end(); ++iter)
    {
        auto device = iter->second;
        if (device->cache(npos))
        {
            Fields fields;
            fields.set("dt", dt);
            fields.set("station_id", this->stationId);
            fields.set("device_id", device->deviceId);
            fields.set("datatype", 1);
            fields.set("value", VectorToJsonArray(device->cacheU, npos));
            DAO::insertRuntimeData(dao, fields);

            fields.set("datatype", 2);
            fields.set("value", VectorToJsonArray(device->cacheI, npos));
            DAO::insertRuntimeData(dao, fields);

            fields.set("datatype", 3);
            fields.set("value", VectorToJsonArray(device->cacheP, npos));
            DAO::insertRuntimeData(dao, fields);
            //spdlog::info("[device] write runtime date to database, deviceId={}", device->deviceId);
        }
    }

    {
        // stat_total
        Fields fields;
        fields.set("dt", Utils::dateStr(statData.ts));
        fields.set("station_id", this->stationId);
        fields.set("E_in", statData.totalElectIn);
        fields.set("E_in_J", statData.totalElectIn_J);
        fields.set("E_in_F", statData.totalElectIn_F);
        fields.set("E_in_P", statData.totalElectIn_P);
        fields.set("E_in_G", statData.totalElectIn_G);
        fields.set("E_out", statData.totalElectOut);
        fields.set("E_out_J", statData.totalElectOut_J);
        fields.set("E_out_F", statData.totalElectOut_F);
        fields.set("E_out_P", statData.totalElectOut_P);
        fields.set("E_out_G", statData.totalElectOut_G);
        fields.set("fee_in", statData.totalFeeIn);
        fields.set("fee_Out", statData.totalFeeOut);
        fields.set("income", statData.totalIncome);
        dao->setTableName("stat_total");

        dao->duplicateUpdate(fields, {
            "E_in", "E_in_J", "E_in_F", "E_in_P", "E_in_G",
            "E_out", "E_out_J", "E_out_F", "E_out_P", "E_out_G",
            "fee_in", "fee_Out", "income"
            });
    }
    if (statData.ts > 0)
    {
        { 
            // stat_day
            Fields fields;
            fields.set("dt", Utils::dateStr(statData.ts));
            fields.set("station_id", this->stationId);
            fields.set("storage_elect_in", statData.dayElectIn);
            fields.set("storage_elect_out", statData.dayElectOut);
            fields.set("income_elect", statData.dayIncome);
            DAO::insertStatDay(dao, fields);
        }
        
        if (statData.dayElectIn > 0.0f || statData.dayElectOut > 0.0f)
        {
            //stat_total_day
            Fields fields;
            fields.set("dt", Utils::dateStr(statData.ts));
            fields.set("station_id", this->stationId);
            fields.set("E_in", statData.dayElectIn);
            fields.set("E_in_J", statData.dayElectIn_J);
            fields.set("E_in_F", statData.dayElectIn_F);
            fields.set("E_in_P", statData.dayElectIn_P);
            fields.set("E_in_G", statData.dayElectIn_G);
            fields.set("E_out", statData.dayElectOut);
            fields.set("E_out_J", statData.dayElectOut_J);
            fields.set("E_out_F", statData.dayElectOut_F);
            fields.set("E_out_P", statData.dayElectOut_P);
            fields.set("E_out_G", statData.dayElectOut_G);
            fields.set("fee_in", statData.dayFeeIn);
            fields.set("fee_Out", statData.dayFeeOut);
            fields.set("income", statData.dayIncome);
            dao->setTableName("stat_total_day");

            dao->duplicateUpdate(fields, {
                "E_in", "E_in_J", "E_in_F", "E_in_P", "E_in_G",
                "E_out", "E_out_J", "E_out_F", "E_out_P", "E_out_G",
                "fee_in", "fee_Out", "income"
                });
        }
    }

    {
        int pos = npos;
        if (tOffset > 20) { pos += 1; }
        mapCacheElectIn[pos] = (pos == 0) ? 0 : statData.dayElectIn;
        mapCacheElectOut[pos] = (pos == 0) ? 0 : statData.dayElectOut;
        mapCacheElectCharger[pos] = 0;

        // 预测数据源记录
        dao->setTableName("predict_day");

        Fields fields;
        fields.set("dt", dt);
        fields.set("station_id", stationId);
        fields.set("datatype", 1); // 1：储能充电，2：储能放电，3：充电桩充电，4：发电
        fields.set("value", MapValueToJson(pos, mapCacheElectIn));
        dao->duplicateUpdate(fields, {"value"});

        fields.set("datatype", 2); // 1：储能充电，2：储能放电，3：充电桩充电，4：发电
        fields.set("value", MapValueToJson(pos, mapCacheElectOut));
        dao->duplicateUpdate(fields, {"value"});

        fields.set("datatype", 3); // 1：储能充电，2：储能放电，3：充电桩充电，4：发电
        fields.set("value", MapValueToJson(pos, mapCacheElectCharger));
        dao->duplicateUpdate(fields, {"value"});
    }
    {
        std::string sql = std::format("select dt, station_id, count(1) count from ( "
            "  select DATE(la.create_time) as dt, station_id, la.device_id, count(1), la.content from log_alert la "
            "  LEFT JOIN device d ON d.device_id = la.device_id "
            "where la.create_time >= '{}' group by la.content, station_id, device_id, dt "
            ") as tmp group by tmp.dt, station_id; ", Utils::dateStr(Utils::time()- 86400 * 7));
        vector<Fields> result;
        dao->exec(sql, result);
        for (auto& item : result)
        {
            string dt = item.value("dt");
            string station_id = item.value("station_id");
            string count = item.value("count");
            std::string sqlUpdate = std::format("update stat_day set storage_num_err='{}' where dt='{}' AND station_id='{}'", count, dt, station_id);
            dao->exec(sqlUpdate);
        }
    }
}


void Station::predict()
{
    int64_t tNow = Utils::time();
    int days = 7;
    string dt1 = Utils::dateStr(tNow-86400*days);
    string dt2 = Utils::dateStr(tNow-86400);

    /// 预测实现方案：
    // 查询前7天的历史数据(不包含今天)，根据历史数据计算今日数据，每10分钟一个数据，一天共144个数据点
    string sql = "SELECT * FROM predict_day pd WHERE pd.station_id='" + std::to_string(stationId) 
        + "' AND dt>='" + dt1 + "' AND dt<='" + dt2 + "';";

    vector<Fields> result;
    DAO::exec(NULL, sql, result);

    // 数据源的每个时刻数据点的个数
    vector<int> countStorageIn(predictStorageIn.size(), 0);
    vector<int> countStorageOut(predictStorageOut.size(), 0);
    vector<int> countCharge(predictCharge.size(), 0);

    for (int row=0; row<result.size(); ++row)
    {
        // 如果数据全是0，则不参与计算
        auto& fields = result[row];

        vector<int>* vdptr = NULL;
        int datatype = fields.get<int>("datatype"); // 1：储能充电，2：储能放电，3：充电桩充电，4：发电
        if (datatype == 1) { vdptr = &predictStorageIn; }
        else if (datatype == 2) { vdptr = &predictStorageOut; }
        else if (datatype == 3) { vdptr = &predictCharge; }

        if (vdptr)
        {
            int v0 = 0;
            string& strval = fields.value("value");
            std::vector<int> vec;
            JSON::parseArray(strval, vec);
            for (int i = 0; i<vdptr->size() && i<vec.size(); ++i)
            {
                auto& v = vec[i];
                if (v < v0)
                {
                    v = v0;
                }
                else
                {
                    v0 = v;
                }
                //if (v0 > 0)
                {
                    (*vdptr)[i] += v;
                    if (datatype == 1) { countStorageIn[i]++; }
                    else if (datatype == 2) { countStorageOut[i]++; }
                    else if (datatype == 3) { countCharge[i]++; }
                }
            }
        }
    }

    for (int i = 0; i<predictStorageIn.size(); ++i)
    {
        if (countStorageIn[i] > 0) { predictStorageIn[i] = predictStorageIn[i] / countStorageIn[i]; }
        if (countStorageOut[i] > 0) { predictStorageOut[i] = predictStorageOut[i] / countStorageOut[i]; }
        if (countCharge[i] > 0) { predictCharge[i] = predictCharge[i] / countCharge[i]; }
    }

    //for (int i = 1; i < predictStorageIn.size(); ++i)
    //{
    //    int v1 = predictStorageIn[i] - predictStorageIn[i - 1];
    //    int v2 = predictStorageOut[i] - predictStorageOut[i - 1];
    //    int v3 = predictCharge[i] - predictCharge[i - 1];
    //    predictStorageIn[i] = v1 >=0 ? v1 : 0;
    //    predictStorageOut[i] = v2 >= 0 ? v2 : 0;
    //    predictCharge[i] = v3 >= 0 ? v3 : 0;
    //}
}



void Station::writeDeviceData()
{
}