#include "disinfection_ctl_service.hpp" using namespace iflytop; using namespace std; /** * @brief * 流程 * 预热: * * 风机开 * 1s * 空压机 * 1s * 蠕动泵 * 1s * * * 关蠕动泵 * 1s * 关空压机 * 1s * 关风机 * */ // #define PRE_HEAT_TIME (2) // #define PRE_HEAT_TIME (5 * 60) #define DVALUE_COMPUTEPERIOD_TIME_S (10.0) #define MAX_VOLUME (5000) namespace iflytop { extern bool g_in_test; } DisinfectionCtrlService::DisinfectionCtrlService() {} void DisinfectionCtrlService::initialize() { GET_TO_SERVICE(m_zcanHost); GET_TO_SERVICE(m_deviceIoControlService); GET_TO_SERVICE(m_dbService); GET_TO_SERVICE(m_disinfectionLogsManager); m_deviceIoControlService->drainingPump_close(); m_deviceIoControlService->replenishingFluidsPump_close(); m_deviceIoControlService->sprayLiquidPump_close(); m_deviceIoControlService->heartingPlate_setPower(false); m_deviceIoControlService->airBlower_setState(false); m_deviceIoControlService->airCompressor_setState(false); } static string getTime() { struct tm tm = {0}; time_t t = time(nullptr); struct tm* tmp = localtime_r(&t, &tm); return fmt::format("{:0>4}-{:0>2}-{:0>2} {:0>2}:{:0>2}:{:0>2}", tm.tm_year + 1900, // tm.tm_mon + 1, // tm.tm_mday, // tm.tm_hour, // tm.tm_min, tm.tm_sec); } string DisinfectionCtrlService::createDisinfectionID() { struct tm tm = {0}; time_t t = time(nullptr); if (t == -1) { logger->error("time(nullptr) failed"); exit(-1); } struct tm* tmp = localtime_r(&t, &tm); if (!tmp) { logger->error("localtime_r failed"); exit(-1); } // tm = *utctime::tm_increment_hour(&tm, 8); // logger->info("trace sendmsg_startCapture {}:{}", __FILE__, __LINE__); return fmt::format("{:0>4}-{:0>2}{:0>2}-{:0>2}{:0>2}{:0>2}", tm.tm_year + 1900, // tm.tm_mon + 1, // tm.tm_mday, // tm.tm_hour, // tm.tm_min, tm.tm_sec); } static bool zfeq(float a, float b, float eps = 0.01) { if (fabs(a - b) < eps) { return true; } return false; } float DisinfectionCtrlService::getDisinfectionDValue(float ppm) { /** * @brief * * D值的计算公式是根据美国竞品的数据记录计算得来的 * * 浓度小于150时,y=-0.5269X+97.868 * 浓度大于150时,y=-0.1405X+40.369 */ if (zfeq(ppm, 0)) { return -1; } float dvalue = 0; if (ppm < 150) { dvalue = -0.5251 * ppm + 98.154; } else if (ppm >= 150 && ppm < 240) { dvalue = -0.125 * ppm + 38.913; } else if (ppm >= 240) { // 240 -> 8.913 // 1400 -> 2 // y = -0.00603x + 10.4472 dvalue = -0.00596 * ppm + 10.3434; } if (dvalue < 2) { dvalue = 2; } return dvalue; } float DisinfectionCtrlService::computeNowLogLevel(DisinfectionContext& context) { float dvalue = context.dvalue; if (dvalue > 0) { /** * @brief 计算 m_nowLoglevel */ return context.m_nowLoglevel + DVALUE_COMPUTEPERIOD_TIME_S / (dvalue * 60); } return context.m_nowLoglevel; } void DisinfectionCtrlService::computeRemainTime(DisinfectionContext& context) { /** * @brief 计算Dvalue */ float dvalue = context.dvalue; if (dvalue > 0) { /** * @brief 计算 m_nowLoglevel */ if (context.m_targetLoglevel >= context.m_nowLoglevel) { context.m_remaintime = (context.m_targetLoglevel - context.m_nowLoglevel) * (dvalue * 60); } else { context.m_remaintime = 0; } } else { // } logger->info("computeRemainTime minh2o2 {} dvalue {}", context.h2o2data.min_h2o2, dvalue); } void DisinfectionCtrlService::initContext(DisinfectionContext& context, // int loglevel, // float injection_pump_speed, // float stoped_gs, // float continued_gs, // float stoped_satur, // float continued_satur, // float stoped_humi, // float continued_humi // ) { context.m_disinfectionID = createDisinfectionID(); context.pre_heat_time_s = m_dbService->getSettingVal("pre_heat_time_s"); context.stoped_gs = stoped_gs; context.continued_gs = continued_gs; context.stoped_satur = stoped_satur; context.continued_satur = continued_satur; context.stoped_humi = stoped_humi; context.continued_humi = continued_humi; context.injection_pump_speed = injection_pump_speed; context.injection_pump_speed_changed = true; if (g_in_test) { logger->warn("in test mode, pre_heat_time_s = 5"); context.pre_heat_time_s = 5; } logger->info("startDisinfection {} {}", m_context.m_targetLoglevel, m_context.m_disinfectionID); logger->info(" stoped_gs {}", context.stoped_gs); logger->info(" continued_gs {}", context.continued_gs); logger->info(" stoped_satur {}", context.stoped_satur); logger->info(" continued_satur {}", context.continued_satur); logger->info(" stoped_humi {}", context.stoped_humi); logger->info(" continued_humi {}", context.continued_humi); logger->info(" pre_heat_time_s {}", context.pre_heat_time_s); logger->info(""); // m_remaintime = loglevel * 20 * 60; // 计算总的加热时间 context.m_remaintime = context.pre_heat_time_s + loglevel * 90 * 60; // 计算总的加热时间 m_disinfectionWorkState = 1; context.m_targetLoglevel = loglevel; context.m_nowLoglevel = 0; // m_context.m_preheatFlag = true; m_context.dvalue = 0; // m_context.stopedflag = false; m_context.m_state = kpreheat; m_context.m_starttp = zsteady_clock().now(); m_zcanHost->warning_light_ctrl_c1002(1, 0, 0, 1, 0); m_deviceIoControlService->heartingPlate_setPower(true); context.csvlogger = m_disinfectionLogsManager->createNewLogger(context.m_disinfectionID); // zsteady_clock().elapsedTimeS(m_context.m_starttp), // // sensors[0].h2o2, sensors[0].temp, sensors[0].humid, sensors[0].saturation, // // sensors[1].h2o2, sensors[1].temp, sensors[1].humid, sensors[1].saturation, // // sensors[2].h2o2, sensors[2].temp, sensors[2].humid, sensors[2].saturation, // // m_context.dvalue, m_context.m_nowLoglevel, (int)m_context.m_targetLoglevel, // // ds->heatingStrip_getstate(), ds->airBlower_getstate(), ds->airCompressor_getstate(), ds->sprayLiquidPump_getRPM(), // // ds->getPressureSensorData(1), ds->getPressureSensorData(2), ds->getPressureSensorData(3), ds->getPressureSensorData(4), // // m_deviceIoControlService->getDisinfectantVolume_g(), // // m_context.m_remaintime context.csvlogger->write( "Date," // "Hydrogen peroxide volume(ppm),Temperature(C),Relative humidity(%RH),H2O H2O2 RS(%RS)," // // "h2o22,temp2,humi2,saturation2," // // "h2o23,temp3,humi3,saturation3," // "Dvalue,Loglevel,Targetloglevel," // "Heating,Blower,Compressor,Pump(g/min)," // "Disinfectant Volume(g)," // "Remaining time (s)\n" // ); } static string formattimeS(int sec) { if (sec >= 0) { return fmt::format("{:0>2}:{:0>2}:{:0>2}", sec / 3600, sec % 3600 / 60, sec % 60); } else { return fmt::format("--:--:--"); } } void DisinfectionCtrlService::dumpDisinfectionLogsToCSV(DisinfectionContext& context) { auto* sensors = &m_context.h2o2data.h2o2sensor_data[0]; auto ds = m_deviceIoControlService; float dvalue = 0; if (m_context.dvalue <= 0) { dvalue = 0; } else { dvalue = m_context.dvalue; } int remaintime = getEstimatedRemainingTimeS(); context.csvlogger->write( fmt::format("{}," // "{},{},{},{}," // // "{},{},{},{}," // // "{},{},{},{}," // "{:.2f},{:.2f},{:.2f}," // "{},{},{},{}," // // "{},{},{},{}," // "{}," // "{}\n" // , getTime(), // sensors[0].h2o2, sensors[0].temp, sensors[0].humid, sensors[0].saturation, // // sensors[1].h2o2, sensors[1].temp, sensors[1].humid, sensors[1].saturation, // // sensors[2].h2o2, sensors[2].temp, sensors[2].humid, sensors[2].saturation, // dvalue, m_context.m_nowLoglevel, m_context.m_targetLoglevel, // ds->heatingStrip_getstate(), ds->airBlower_getstate(), ds->airCompressor_getstate(), ds->sprayLiquidPump_getGPM(), // m_deviceIoControlService->getDisinfectantVolume_g(), // formattimeS(remaintime))); } void DisinfectionCtrlService::finishDisinfection(DisinfectionContext& context) { context.m_remaintime = 0; logger->info("stop disinfection {}", context.m_disinfectionID); // sprayLiquidPump_close(); m_deviceIoControlService->sprayLiquidPump_close(); usleep(1000 * 1000); // airCompressor(false); m_deviceIoControlService->airCompressor_setState(false); usleep(1000 * 1000); // blower_setPower(false); m_deviceIoControlService->airBlower_setState(false); usleep(1000 * 1000); // heartingPlate_setPower(false); m_deviceIoControlService->heartingPlate_setPower(false); m_disinfectionWorkState = 3; m_zcanHost->warning_light_ctrl_c1002(1, 0, 0, 0, 0); context.csvlogger = nullptr; } void DisinfectionCtrlService::processPreheatState(DisinfectionContext& context) { int hasstarttime = zsteady_clock().elapsedTimeS(context.m_starttp); // logger->info("preheat {}", context.m_disinfectionID); if ((context.m_state == kpreheat && hasstarttime > m_context.pre_heat_time_s)) { logger->info("preheat finished {}", context.m_disinfectionID); // blower_setPower(true); m_deviceIoControlService->airBlower_setState(true); usleep(1000 * 1000); // airCompressor(true); m_deviceIoControlService->airCompressor_setState(true); usleep(1000 * 1000); // sprayLiquidPump_open(); m_deviceIoControlService->sprayLiquidPump_open(context.injection_pump_speed); context.m_state = kdisinfection; // context.m_preheatFlag = false; // context.sprayLiquidFlag = true; } else { logger->info("{}: preheat {}", context.m_disinfectionID, m_context.pre_heat_time_s - hasstarttime); } } void DisinfectionCtrlService::dumpDisinfectionLogs(DisinfectionContext& context) { // float h2o2_g = m_deviceIoControlService->getDisinfectantVolume_g(); auto* sensors = &m_context.h2o2data.h2o2sensor_data[0]; auto ds = m_deviceIoControlService; logger->info( "T:{}," // "s1:({},{},{},{})," // "s2:({},{},{},{})," // "s3:({},{},{},{})," // "D:{},log:({}:{})," // "io:({},{},{},{})," // "p:({},{},{},{})," // "h2o2g:{}," // "rt:{}" // , zsteady_clock().elapsedTimeS(m_context.m_starttp), // sensors[0].h2o2, sensors[0].temp, sensors[0].humid, sensors[0].saturation, // sensors[1].h2o2, sensors[1].temp, sensors[1].humid, sensors[1].saturation, // sensors[2].h2o2, sensors[2].temp, sensors[2].humid, sensors[2].saturation, // m_context.dvalue, m_context.m_nowLoglevel, (int)m_context.m_targetLoglevel, // ds->heatingStrip_getstate(), ds->airBlower_getstate(), ds->airCompressor_getstate(), ds->sprayLiquidPump_getRPM(), // ds->getPressureSensorData(1), ds->getPressureSensorData(2), ds->getPressureSensorData(3), ds->getPressureSensorData(4), // m_deviceIoControlService->getDisinfectantVolume_g(), // getEstimatedRemainingTimeS()); } /** * @brief * 消毒中状态处理 */ void DisinfectionCtrlService::processDisinfectionState(DisinfectionContext& context) { ZCHECK(context.m_state == kdisinfection || context.m_state == kdisinfection_take_a_break, "state error"); /** * @brief 周期性计算剩余时间 */ /** * @brief 根据湿度启停喷雾 */ if (m_context.m_state == kdisinfection) { /** * @brief 检查当前 */ float nowSatur = m_context.h2o2data.max_saturation; float nowh2o2 = m_context.h2o2data.max_h2o2; float humid = m_context.h2o2data.max_humid; if (m_context.injection_pump_speed_changed) { m_deviceIoControlService->sprayLiquidPump_open(context.injection_pump_speed); m_context.injection_pump_speed_changed = false; } // humid > m_context.stoped_satur if (nowSatur > m_context.stoped_satur || nowh2o2 > m_context.stoped_gs || humid > m_context.stoped_humi) { logger->info("stop sprayLiquid"); m_deviceIoControlService->sprayLiquidPump_close(); usleep(1000 * 1000); m_deviceIoControlService->airCompressor_setState(false); // m_context.sprayLiquidFlag = false; m_context.m_state = kdisinfection_take_a_break; } } else { float nowSatur = m_context.h2o2data.max_saturation; float nowh2o2 = m_context.h2o2data.max_h2o2; float humid = m_context.h2o2data.max_humid; // && humid < m_context.continued_satur if (nowSatur < m_context.continued_satur && nowh2o2 < m_context.continued_gs && humid < context.continued_humi) { logger->info("start sprayLiquid"); m_deviceIoControlService->sprayLiquidPump_open(context.injection_pump_speed); usleep(1000 * 1000); m_deviceIoControlService->airCompressor_setState(true); m_context.m_state = kdisinfection; } } } void DisinfectionCtrlService::disinfectionLoop(bool& breakflag) { // logger->info("disinfection running {} {}s preheatFlag:{}", m_context.m_disinfectionID, m_context.m_remaintime, m_context.m_preheatFlag); m_context.m_remaintime--; bool forcelog = false; if (m_context.m_remaintime < 0) { m_context.m_remaintime = 0; } /** * @brief 更新传感器信息 */ m_deviceIoControlService->getAllSensorData(m_context.h2o2data); /** * @brief 计算D值 */ m_context.dvalue = getDisinfectionDValue(m_context.h2o2data.min_h2o2); if (zsteady_clock().elapsedTimeS(m_context.m_lastComputeDvalueTp) > DVALUE_COMPUTEPERIOD_TIME_S) { m_context.m_lastComputeDvalueTp = zsteady_clock().now(); // if (m_context.m_state == kdisinfection || m_context.m_state == kdisinfection_take_a_break) { m_context.m_nowLoglevel = computeNowLogLevel(m_context); computeRemainTime(m_context); } } if (m_context.m_state == kpreheat) { /** * @brief 预热中 */ processPreheatState(m_context); } else if (m_context.m_state == kdisinfection || m_context.m_state == kdisinfection_take_a_break) { /** * @brief 消毒中 */ processDisinfectionState(m_context); // if (m_context.m_remaintime <= 0 && m_context.m_nowLoglevel > (m_context.m_targetLoglevel + 0.01)) { m_context.m_remaintime = 0; m_context.m_nowLoglevel = m_context.m_targetLoglevel + 0.01; logger->info("disinfection finished {},but waitting for h2o2 to safe", m_context.m_disinfectionID); m_deviceIoControlService->sprayLiquidPump_close(); usleep(1000 * 1000); m_deviceIoControlService->airCompressor_setState(false); usleep(1000 * 1000); m_deviceIoControlService->heartingPlate_setPower(false); m_context.m_state = kwait_for_h2o2_down; forcelog = true; } } else if (m_context.m_state == kwait_for_h2o2_down) { /** * @brief 等待h2o2浓度下降 */ logger->info("waitting for h2o2 concentration to safe value {}=>{}", m_context.h2o2data.min_h2o2, 1); if (m_context.h2o2data.min_h2o2 < 1) { logger->info("h2o2 concentration to safe value"); breakflag = true; forcelog = true; m_context.m_state = kfinished; } } else { ZCHECK(false, "state error"); } if (forcelog || zsteady_clock().elapsedTimeS(m_context.m_lastlogTp) > DVALUE_COMPUTEPERIOD_TIME_S) { m_context.m_lastlogTp = zsteady_clock().now(); dumpDisinfectionLogs(m_context); dumpDisinfectionLogsToCSV(m_context); } } void DisinfectionCtrlService::changeDisinfectionParameter(int injection_pump_speed, // int stoped_gs, // int continued_gs, // int stoped_satur, // int continued_satur, // int stoped_humi, // int continued_humi) { lock_guard lock(lock_); // logger->info("changeInjectionPumpSpeed {}=>{}", m_context.injection_pump_speed, speed); // m_context.injection_pump_speed = speed; m_context.injection_pump_speed = injection_pump_speed; m_context.stoped_gs = stoped_gs; m_context.continued_gs = continued_gs; m_context.stoped_satur = stoped_satur; m_context.continued_satur = continued_satur; m_context.stoped_humi = stoped_humi; m_context.continued_humi = continued_humi; m_context.injection_pump_speed_changed = true; logger->info("changeDisinfectionParameter {} {} {} {} {} {} {}", injection_pump_speed, stoped_gs, continued_gs, stoped_satur, continued_satur, stoped_humi, continued_humi); } void DisinfectionCtrlService::startDisinfection(int loglevel, // int injection_pump_speed, // int stoped_gs, // int continued_gs, // int stoped_satur, // int continued_satur, // int stoped_humi, // int continued_humi // ) { lock_guard lock(lock_); /** * @TODO * 检查当前环境湿度,湿度过大,不允许消毒 */ if (m_disinfectionThread) { stopDisinfection(); } initContext(m_context, loglevel, injection_pump_speed, stoped_gs, continued_gs, stoped_satur, continued_satur, stoped_humi, continued_humi); m_disinfectionThread.reset(new Thread("m_disinfectionThread", [this]() { ThisThread thisThread; while (!thisThread.getExitFlag()) { thisThread.sleepForMs(1000); bool breakflag = false; disinfectionLoop(breakflag); if (breakflag) { break; } } // finishDisinfection(m_context); })); // } int DisinfectionCtrlService::getDisinfectionWorkState() { return m_context.m_state; } void DisinfectionCtrlService::stopDisinfection() { lock_guard lock(lock_); if (m_disinfectionThread) { m_disinfectionThread->join(); m_disinfectionThread = nullptr; } m_context.m_state = kfinished; m_disinfectionWorkState = 0; } int32_t DisinfectionCtrlService::getEstimatedRemainingTimeS() { lock_guard lock(lock_); if (m_context.m_state == kpreheat) { return getPreHeatRaminTimeS(); } else if (m_context.m_state == kdisinfection || m_context.m_state == kdisinfection_take_a_break) { if (m_context.dvalue > 0) { return m_context.m_remaintime; } else { return -1; } } else { return 0; } } int32_t DisinfectionCtrlService::getPreHeatRaminTimeS() { lock_guard lock(lock_); int32_t remaintime = 0; if (m_context.m_state == kpreheat) { remaintime = m_context.pre_heat_time_s - zsteady_clock().elapsedTimeS(m_context.m_starttp); if (remaintime < 0) { remaintime = 0; } return remaintime; } else { return 0; } } string DisinfectionCtrlService::getDisinfectionID() { lock_guard lock(lock_); return m_context.m_disinfectionID; } bool DisinfectionCtrlService::isPreheatState() { lock_guard lock(lock_); return m_context.m_state == kpreheat; } int DisinfectionCtrlService::getReplenishingFluidsWorkState() { return m_replenishingFluidsWorkState; } int DisinfectionCtrlService::getDrainingWorkState() { return m_drainingWorkState; } /******************************************************************************* * // 加液 * *******************************************************************************/ /** * @brief 开始加液 * * @param stopatg */ void DisinfectionCtrlService::startReplenishingFluids(int stopatg) { lock_guard lock(lock_); if (m_disinfectionThread) { m_disinfectionThread->join(); m_disinfectionThread = nullptr; } int32_t nowvolume = m_deviceIoControlService->getDisinfectantVolume_g(); if (nowvolume > stopatg) { logger->warn("start Replenishing fail, nowvolume {} > stopatg {}", nowvolume, stopatg); return; } m_disinfectionThread.reset(new Thread("disinfectionThread", [this, stopatg]() { ThisThread thisThread; m_deviceIoControlService->replenishingFluidsPump_open(); logger->info("startReplenishingFluids {}g", stopatg); while (!thisThread.getExitFlag()) { int32_t nowvolume = m_deviceIoControlService->getDisinfectantVolume_g(); logger->info("replenishingFluids {}g", nowvolume); if (nowvolume > stopatg) { break; } if (nowvolume > MAX_VOLUME) { logger->warn("replenishingFluids reach full level {}g", nowvolume); break; } thisThread.sleepForMs(1000); } logger->info("stopReplenishingFluids"); // replenishingFluidsPump_close(); m_deviceIoControlService->replenishingFluidsPump_close(); m_replenishingFluidsWorkState = 0; })); // m_replenishingFluidsWorkState = 1; logger->info("startReplenishingFluids "); } /** * @brief 停止加液 * */ void DisinfectionCtrlService::stopReplenishingFluids() { lock_guard lock(lock_); if (m_disinfectionThread) { m_disinfectionThread->join(); m_disinfectionThread = nullptr; } logger->info("stopReplenishingFluids"); // replenishingFluidsPump_close(); m_deviceIoControlService->replenishingFluidsPump_close(); m_replenishingFluidsWorkState = 0; } /******************************************************************************* * 排液 * *******************************************************************************/ /** * @brief 开始排液 * */ void DisinfectionCtrlService::startDraining() { lock_guard lock(lock_); if (m_disinfectionThread) { m_disinfectionThread->join(); m_disinfectionThread = nullptr; } m_disinfectionThread.reset(new Thread("disinfectionThread", [this]() { ThisThread thisThread; m_deviceIoControlService->drainingPump_open(); logger->info("startDraining "); auto startdrainingtime = zsteady_clock().now(); zsteady_tp volumeReachZeroTime; bool volumeReachZeroFlag = false; while (!thisThread.getExitFlag()) { int32_t nowvolume = m_deviceIoControlService->getDisinfectantVolume_g(); logger->info("draining remain {} g", nowvolume); if (!volumeReachZeroFlag && nowvolume == 0) { volumeReachZeroTime = zsteady_clock().now(); volumeReachZeroFlag = true; } if (volumeReachZeroFlag) { logger->info("stopDraining after {} s", 30 - zsteady_clock().elapsedTimeS(volumeReachZeroTime)); if (zsteady_clock().elapsedTimeS(volumeReachZeroTime) > 30) { break; } } thisThread.sleepForMs(1000); } logger->info("stopDraining"); // replenishingFluidsPump_close(); m_deviceIoControlService->drainingPump_close(); m_drainingWorkState = 0; })); logger->info("startDraining"); // drainingPump_open(); m_drainingWorkState = 1; } /** * @brief 停止排液体 */ void DisinfectionCtrlService::stopDraining() { lock_guard lock(lock_); if (m_disinfectionThread) { m_disinfectionThread->join(); m_disinfectionThread = nullptr; } logger->info("stopDraining"); m_drainingWorkState = 0; // drainingPump_close(); m_deviceIoControlService->drainingPump_close(); }