Browse Source

update

master
zhaohe 2 years ago
parent
commit
c1cc2cf7bf
  1. 2
      iflytoplinuxsdk
  2. 22
      src/db/db_service.cpp
  3. 76
      src/main_control_service.cpp
  4. 6
      src/main_control_service.hpp
  5. 128
      src/service/data_export_service.cpp
  6. 60
      src/service/data_export_service.hpp
  7. 18
      src/service/disinfection_ctl_service.cpp

2
iflytoplinuxsdk

@ -1 +1 @@
Subproject commit 19616ea48cf115890ccd01d47c1b1fc277ffbb9b
Subproject commit 6b330dc02a70689893c404afaf5f567fcee93c42

22
src/db/db_service.cpp

@ -449,7 +449,12 @@ json DBService::getAllFormulaJson() {
j_formula["injection_pump_speed"] = f.injection_pump_speed; j_formula["injection_pump_speed"] = f.injection_pump_speed;
j_formulas.push_back(j_formula); j_formulas.push_back(j_formula);
} }
return j_formulas;
json jret;
jret["formulas"] = j_formulas;
jret["settings"] = getAllSettingJson();
return jret;
} }
void DBService::addFormula(string formula_id, string stoped_gs, string continued_gs, string stoped_satur, string continued_satur, string stoped_humi, void DBService::addFormula(string formula_id, string stoped_gs, string continued_gs, string stoped_satur, string continued_satur, string stoped_humi,
string continued_humi, string injection_pump_speed) { string continued_humi, string injection_pump_speed) {
@ -542,6 +547,21 @@ int DBService::getUserBehaviorRecordTheFirstId() {
return all[0].id; return all[0].id;
} }
list<shared_ptr<db::UserBehaviorRecord>> DBService::getAllUserBehaviorRecord() {
lock_guard<recursive_mutex> lock(lock_);
auto user_behavior_record_table = make_storage(USER_BEHAVIOR_RECORD_DB, USER_BEHAVIOR_RECORD_STRUCT);
user_behavior_record_table.sync_schema();
auto all = user_behavior_record_table.get_all<UserBehaviorRecord>(order_by(&UserBehaviorRecord::id).desc());
list<shared_ptr<db::UserBehaviorRecord>> user_behavior_records;
for (auto& u : all) {
user_behavior_records.push_back(make_shared<UserBehaviorRecord>(u));
}
return user_behavior_records;
}
json DBService::getUserBehaviorRecordDescJson(int page, int page_size) { json DBService::getUserBehaviorRecordDescJson(int page, int page_size) {
lock_guard<recursive_mutex> lock(lock_); lock_guard<recursive_mutex> lock(lock_);

76
src/main_control_service.cpp

@ -67,6 +67,10 @@ void MainControlService::initialize() {
m_deviceIoControlService->startScan(); m_deviceIoControlService->startScan();
BUILD_AND_REG_SERRVICE(DataExportService);
GET_SERVICE(DataExportService)->initialize();
GET_TO_SERVICE(m_dataExportService);
/** /**
* @brief Get the to service object * @brief Get the to service object
*/ */
@ -189,13 +193,13 @@ void MainControlService::processFrontEndMessage_userOperate(weak_ptr<WebSocket>
logger->info("user {} login success", "admin"); logger->info("user {} login success", "admin");
} else { } else {
if (!m_dbService->isUserExist(uid)) { if (!m_dbService->isUserExist(uid)) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kuser_not_exist);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kuser_not_exist, "");
receipt["ackcode"] = err::zecode(err::kuser_not_exist);
receipt["ackcodeInfo"] = err::zecode2str(err::kuser_not_exist);
return; return;
} }
if (!m_dbService->ispasswdCorrect(uid, pwd)) { if (!m_dbService->ispasswdCorrect(uid, pwd)) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kpasswd_error);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kpasswd_error, "");
receipt["ackcode"] = err::zecode(err::kpasswd_error);
receipt["ackcodeInfo"] = err::zecode2str(err::kpasswd_error);
return; return;
} }
m_deviceStateService->setLoginState(uid, m_dbService->getUser(uid)->permission_level, m_dbService->getUser(uid)->visible); m_deviceStateService->setLoginState(uid, m_dbService->getUser(uid)->permission_level, m_dbService->getUser(uid)->visible);
@ -225,13 +229,13 @@ void MainControlService::processFrontEndMessage_userOperate(weak_ptr<WebSocket>
string passwd = cmd["passwd"]; string passwd = cmd["passwd"];
logger->info("changet passwd {} {}", uid, passwd); logger->info("changet passwd {} {}", uid, passwd);
if (!m_dbService->isUserExist(uid)) { if (!m_dbService->isUserExist(uid)) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kuser_not_exist);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kuser_not_exist, "");
receipt["ackcode"] = err::zecode(err::kuser_not_exist);
receipt["ackcodeInfo"] = err::zecode2str(err::kuser_not_exist);
return; return;
} }
if (!m_dbService->ispasswdCorrect(uid, passwd)) { if (!m_dbService->ispasswdCorrect(uid, passwd)) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kpasswd_error);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kpasswd_error, "");
receipt["ackcode"] = err::zecode(err::kpasswd_error);
receipt["ackcodeInfo"] = err::zecode2str(err::kpasswd_error);
return; return;
} }
auto user = m_dbService->changePasswd(uid, newpasswd); auto user = m_dbService->changePasswd(uid, newpasswd);
@ -253,8 +257,8 @@ void MainControlService::processFrontEndMessage_userOperate(weak_ptr<WebSocket>
int id = jsonGet<int>(cmd["id"]); int id = jsonGet<int>(cmd["id"]);
auto user = m_dbService->delUser(id); auto user = m_dbService->delUser(id);
if (!user) { if (!user) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kuser_not_exist);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kuser_not_exist, "");
receipt["ackcode"] = err::zecode(err::kuser_not_exist);
receipt["ackcodeInfo"] = err::zecode2str(err::kuser_not_exist);
return; return;
} }
m_dbService->addUserBehaviorRecord(m_deviceStateService->getLoginUid(), kbehavior_del_user, fmt::format("({})", user->uid)); m_dbService->addUserBehaviorRecord(m_deviceStateService->getLoginUid(), kbehavior_del_user, fmt::format("({})", user->uid));
@ -265,8 +269,8 @@ void MainControlService::processFrontEndMessage_userOperate(weak_ptr<WebSocket>
int permission_level = jsonGet<int>(cmd["permission_level"]); int permission_level = jsonGet<int>(cmd["permission_level"]);
auto user = m_dbService->updateUserPermissionLevel(id, permission_level); auto user = m_dbService->updateUserPermissionLevel(id, permission_level);
if (!user) { if (!user) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kuser_not_exist);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kuser_not_exist, "");
receipt["ackcode"] = err::zecode(err::kuser_not_exist);
receipt["ackcodeInfo"] = err::zecode2str(err::kuser_not_exist);
return; return;
} }
m_dbService->addUserBehaviorRecord(m_deviceStateService->getLoginUid(), kbehavior_update_user_permission_level, m_dbService->addUserBehaviorRecord(m_deviceStateService->getLoginUid(), kbehavior_update_user_permission_level,
@ -279,8 +283,8 @@ void MainControlService::processFrontEndMessage_userOperate(weak_ptr<WebSocket>
string olduid; string olduid;
auto user = m_dbService->updateUserUid(id, uid, olduid); auto user = m_dbService->updateUserUid(id, uid, olduid);
if (!user) { if (!user) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kuser_not_exist);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kuser_not_exist, "");
receipt["ackcode"] = err::zecode(err::kuser_not_exist);
receipt["ackcodeInfo"] = err::zecode2str(err::kuser_not_exist);
return; return;
} }
m_dbService->addUserBehaviorRecord(m_deviceStateService->getLoginUid(), kbehavior_update_user_uid, fmt::format("({}->{})", olduid, uid)); m_dbService->addUserBehaviorRecord(m_deviceStateService->getLoginUid(), kbehavior_update_user_uid, fmt::format("({}->{})", olduid, uid));
@ -452,12 +456,41 @@ void MainControlService::processFrontEndMessage_test(weak_ptr<WebSocket> webSock
bool suc = m_zcanhost->execcmd(cancmd, receipt_str); bool suc = m_zcanhost->execcmd(cancmd, receipt_str);
receipt["receipt_str"] = receipt_str; receipt["receipt_str"] = receipt_str;
if (!suc) { if (!suc) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kfail);
receipt["ackcode"] = err::zecode(err::kfail);
} }
return; return;
} }
} }
void MainControlService::processFrontEndMessage_exportData(weak_ptr<WebSocket> webSocket, json& cmd, json& receipt) {
string cmdstr = cmd["command"];
if (cmdstr == "exportUserBehaviorRecord") {
auto errcode = m_dataExportService->exportAuditData();
if (errcode != err::ksucc) {
receipt["ackcode"] = err::zecode(errcode);
receipt["ackcodeInfo"] = err::zecode2str(errcode);
return;
}
}
if (cmdstr == "exportDisinfectionRecord") {
auto errcode = m_dataExportService->exportDisinfectionData();
if (errcode != err::ksucc) {
receipt["ackcode"] = err::zecode(errcode);
receipt["ackcodeInfo"] = err::zecode2str(errcode);
return;
}
}
if (cmdstr == "cleanDisinfectionRecord") {
dosystem("rm -rf /app/disinfection_logs/*");
}
if (cmdstr == "cleanUserBehaviorRecord") {
m_dbService->cleanUserBehaviorRecord();
return;
}
}
void MainControlService::processFrontEndMessage_setting(weak_ptr<WebSocket> webSocket, json& cmd, json& receipt) { void MainControlService::processFrontEndMessage_setting(weak_ptr<WebSocket> webSocket, json& cmd, json& receipt) {
string cmdstr = cmd["command"]; string cmdstr = cmd["command"];
if (cmdstr == "getAllSetting") { if (cmdstr == "getAllSetting") {
@ -472,8 +505,8 @@ void MainControlService::processFrontEndMessage_setting(weak_ptr<WebSocket> webS
bool suc = m_dbService->setSettingVal(settingName, settingVal); bool suc = m_dbService->setSettingVal(settingName, settingVal);
if (!suc) { if (!suc) {
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kdb_operate_error);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kdb_operate_error, "setSettingVal fail");
receipt["ackcode"] = err::zecode(err::kdb_operate_error);
receipt["ackcodeInfo"] = err::zecode2str(err::kdb_operate_error);
return; return;
} }
m_dbService->addUserBehaviorRecord(m_deviceStateService->getLoginUid(), kbehavior_set_setting_val, fmt::format("({},{})", settingName, settingVal)); m_dbService->addUserBehaviorRecord(m_deviceStateService->getLoginUid(), kbehavior_set_setting_val, fmt::format("({},{})", settingName, settingVal));
@ -490,8 +523,8 @@ void MainControlService::processFrontEndMessage_processFormulaCmd(weak_ptr<WebSo
auto formul = m_dbService->getFormula(id); auto formul = m_dbService->getFormula(id);
if (!formul) { if (!formul) {
logger->error("formula id {} not exist", id); logger->error("formula id {} not exist", id);
receipt["ackcode"] = err::error_code_get_get_ecode(err::kce, err::kdb_operate_error);
receipt["ackcodeInfo"] = err::error_code_get_desc(err::kce, err::kdb_operate_error, "");
receipt["ackcode"] = err::zecode(err::kdb_operate_error);
receipt["ackcodeInfo"] = err::zecode2str(err::kdb_operate_error);
return; return;
} }
m_disinfectionCtrlService->startDisinfection(atoi(formul->loglevel.c_str()), // m_disinfectionCtrlService->startDisinfection(atoi(formul->loglevel.c_str()), //
@ -557,10 +590,6 @@ void MainControlService::processFrontEndMessage_processBehaviorRecordCmd(weak_pt
return; return;
} }
if (cmdstr == "cleanUserBehaviorRecord") {
m_dbService->cleanUserBehaviorRecord();
return;
}
// 导入用户行为测试数据 // 导入用户行为测试数据
if (cmdstr == "importTestData") { if (cmdstr == "importTestData") {
for (size_t i = 0; i < 100; i++) { for (size_t i = 0; i < 100; i++) {
@ -598,6 +627,7 @@ void MainControlService::processFrontEndMessage(weak_ptr<WebSocket> webSocket, j
processFrontEndMessage_setting(webSocket, cmd, receipt); processFrontEndMessage_setting(webSocket, cmd, receipt);
processFrontEndMessage_processFormulaCmd(webSocket, cmd, receipt); processFrontEndMessage_processFormulaCmd(webSocket, cmd, receipt);
processFrontEndMessage_processBehaviorRecordCmd(webSocket, cmd, receipt); processFrontEndMessage_processBehaviorRecordCmd(webSocket, cmd, receipt);
processFrontEndMessage_exportData(webSocket, cmd, receipt);
/******************************************************************************* /*******************************************************************************
* getState * * getState *
*******************************************************************************/ *******************************************************************************/

6
src/main_control_service.hpp

@ -30,6 +30,7 @@
// //
#include "db/db_service.hpp" #include "db/db_service.hpp"
#include "iflytop/components/zcanreceiver/zcanhost.hpp" #include "iflytop/components/zcanreceiver/zcanhost.hpp"
#include "service/data_export_service.hpp"
#include "service/device_io_control_service.hpp" #include "service/device_io_control_service.hpp"
#include "service/disinfection_logs_manager.hpp" #include "service/disinfection_logs_manager.hpp"
#include "src/service/iflytop_can_host_device.hpp" #include "src/service/iflytop_can_host_device.hpp"
@ -61,6 +62,7 @@ class MainControlService : public enable_shared_from_this<MainControlService> {
shared_ptr<DeviceIoControlService> m_deviceIoControlService; shared_ptr<DeviceIoControlService> m_deviceIoControlService;
shared_ptr<DisinfectionLogsManager> m_disinfectionLogsManager; shared_ptr<DisinfectionLogsManager> m_disinfectionLogsManager;
shared_ptr<ZCanHost> m_zcanhost; shared_ptr<ZCanHost> m_zcanhost;
shared_ptr<DataExportService> m_dataExportService;
unique_ptr<Thread> m_reportThread; unique_ptr<Thread> m_reportThread;
@ -97,7 +99,9 @@ class MainControlService : public enable_shared_from_this<MainControlService> {
void processFrontEndMessage_processFormulaCmd(weak_ptr<WebSocket> webSocket, json& cmd, json& receipt); void processFrontEndMessage_processFormulaCmd(weak_ptr<WebSocket> webSocket, json& cmd, json& receipt);
void processFrontEndMessage_processBehaviorRecordCmd(weak_ptr<WebSocket> webSocket, json& cmd, json& receipt); void processFrontEndMessage_processBehaviorRecordCmd(weak_ptr<WebSocket> webSocket, json& cmd, json& receipt);
json createSensorDataJson();
void processFrontEndMessage_exportData(weak_ptr<WebSocket> webSocket, json& cmd, json& receipt);
json createSensorDataJson();
void dosystem(string order); void dosystem(string order);
}; };

128
src/service/data_export_service.cpp

@ -0,0 +1,128 @@
#include "data_export_service.hpp"
using namespace iflytop;
void DataExportService::loop() {
ThisThread thisThread;
while (!thisThread.getExitFlag()) {
}
}
void DataExportService::initialize() { GET_TO_SERVICE(m_dbService); }
bool DataExportService::isDetectedUDisk(string& diskpath) {
/**
* @brief /dev/sda是否存在
*/
// 检查 /dev/sda /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf /dev/sdg /dev/sdh 是否存在
for (int i = 0; i < 8; i++) {
string dev = fmt::format("/dev/sd{}", (char)('a' + i));
logger->info("check dev: {}", dev);
if (access(dev.c_str(), F_OK) == 0) {
m_ismounted = true;
diskpath = dev;
return true;
}
}
return false;
}
bool DataExportService::dosystem(string cmd) {
logger->info("do cmd: {}", cmd);
int ret = system(cmd.c_str());
if (ret == 0) {
return true;
} else {
logger->error("do cmd: {} failed,{}", cmd, ret);
return 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);
}
#define IF_ERROR_RETURN(x) \
if (!x) { \
return err::ksys_copy_file_error; \
}
err::error_t DataExportService::exportDisinfectionData() {
lock_guard<mutex> lock(lock_);
string diskpath;
if (!isDetectedUDisk(diskpath)) {
logger->error("no disk detected");
return err::kharde_unfound;
}
logger->info("diskpath: {}", diskpath);
// 创建目录
IF_ERROR_RETURN(dosystem(fmt::format("rm -rf /mnt/exportdata")));
IF_ERROR_RETURN(dosystem(fmt::format("mkdir -p /mnt/exportdata")));
// 挂载目录
IF_ERROR_RETURN(dosystem(fmt::format("mount {} /mnt/exportdata", diskpath)));
// 拷贝文件
IF_ERROR_RETURN(dosystem(fmt::format("cp -rf /app/disinfection_logs /mnt/exportdata/{}disinfection_logs", getTime())));
// 卸载目录
IF_ERROR_RETURN(dosystem(fmt::format("umount /mnt/exportdata")));
// 删除目录
IF_ERROR_RETURN(dosystem(fmt::format("rm -rf /mnt/exportdata")));
return err::ksucc;
}
err::error_t DataExportService::exportAuditData() {
lock_guard<mutex> lock(lock_);
string diskpath;
if (!isDetectedUDisk(diskpath)) {
logger->error("no disk detected");
return err::kharde_unfound;
}
logger->info("diskpath: {}", diskpath);
/**
* @brief csv
*/
auto records = m_dbService->getAllUserBehaviorRecord();
string filename = fmt::format("/tmp/{}audit.csv", getTime());
ofstream ofs(filename);
if (!ofs.is_open()) {
logger->error("open file {} failed", filename);
return err::ksys_open_file_error;
}
ofs << "date,uid,operation" << endl;
for (auto& record : records) {
ofs << fmt::format("{},{},\"{}{}\"", record->date, record->uid, db::user_behavior_to_str((user_behavior_t)record->behavior), record->behaviorinfo) << endl;
}
ofs.close();
// 文件编码转换
IF_ERROR_RETURN(dosystem(fmt::format("iconv -f UTF-8 -tgb18030 {} -o {}", filename, filename + ".1")));
dosystem(fmt::format("mv {} {}", filename + ".1", filename));
/**
* @brief U盘
*/
// 创建目录
IF_ERROR_RETURN(dosystem(fmt::format("rm -rf /mnt/exportdata")));
IF_ERROR_RETURN(dosystem(fmt::format("mkdir -p /mnt/exportdata")));
// 挂载目录
IF_ERROR_RETURN(dosystem(fmt::format("mount {} /mnt/exportdata", diskpath)));
// 拷贝文件
IF_ERROR_RETURN(dosystem(fmt::format("cp -rf {} /mnt/exportdata", filename)));
// 卸载目录
IF_ERROR_RETURN(dosystem(fmt::format("umount /mnt/exportdata")));
// 删除目录
IF_ERROR_RETURN(dosystem(fmt::format("rm -rf /mnt/exportdata")));
return err::ksucc;
}

60
src/service/data_export_service.hpp

@ -0,0 +1,60 @@
//
// Created by zwsd
//
#pragma once
#include <fstream>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <mutex>
#include "iflytop/components/zcanreceiver/zcanhost.hpp"
#include "iflytop/core/core.hpp"
#include "zservice_container/zservice_container.hpp"
// lock_guard<mutex> lock(lock_);
#include <mutex>
// std::lock_guard<std::recursive_mutex> lock(lock_);
#include "iflytop/core/error/error_code.hpp"
//
#include "db/db_service.hpp"
namespace iflytop {
class DataExportService {
ENABLE_LOGGER(DataExportService);
private:
bool m_ismounted = false;
unique_ptr<Thread> m_thread;
shared_ptr<DBService> m_dbService;
mutex lock_;
public:
DataExportService(/* args */){};
~DataExportService(){};
void initialize();
bool isDetectedUDisk(string& diskpath);
void mountDisk();
void unmountDisk();
err::error_t exportDisinfectionData();
err::error_t exportAuditData();
private:
void loop();
bool dosystem(string cmd);
};
}; // namespace iflytop

18
src/service/disinfection_ctl_service.cpp

@ -187,31 +187,17 @@ void DisinfectionCtrlService::initContext(DisinfectionContext& context,
// m_context.m_remaintime // m_context.m_remaintime
context.csvlogger->write( context.csvlogger->write(
"time," //
"date," //
"hydrogen_peroxide_volume[1],temperature[1],relative_humidity[1],h2o_h2o2_rs[1]," // "hydrogen_peroxide_volume[1],temperature[1],relative_humidity[1],h2o_h2o2_rs[1]," //
// "h2o22,temp2,humi2,saturation2," // // "h2o22,temp2,humi2,saturation2," //
// "h2o23,temp3,humi3,saturation3," // // "h2o23,temp3,humi3,saturation3," //
"dvalue,loglevel,targetloglevel," // "dvalue,loglevel,targetloglevel," //
"heating,blower,compressor,pump," // "heating,blower,compressor,pump," //
"disinfectantVolume," //
// "disinfectantVolume," //
"remaintime\n" // "remaintime\n" //
); );
} }
// logger->info("hydrogen_peroxide_volume :{}", m_hpp272_data_1.hydrogen_peroxide_volume);
// logger->info("h2o_h2o2_rs :{}", m_hpp272_data_1.h2o_h2o2_rs);
// logger->info("temperature1 :{}", m_hpp272_data_1.temperature1);
// logger->info("relative_humidity :{}", m_hpp272_data_1.relative_humidity);
// logger->info("absolute_hydrogen_peroxide :{}", m_hpp272_data_1.absolute_hydrogen_peroxide);
// logger->info("h2o_h2o2dew_point_temperature :{}", m_hpp272_data_1.h2o_h2o2dew_point_temperature);
// logger->info("reserved1 :{}", m_hpp272_data_1.reserved1);
// logger->info("water_volume :{}", m_hpp272_data_1.water_volume);
// logger->info("water_vapor_pressure :{}", m_hpp272_data_1.water_vapor_pressure);
// logger->info("absolute_humidity :{}", m_hpp272_data_1.absolute_humidity);
// logger->info("water_vapor_saturation_pressure_h2o:{}", m_hpp272_data_1.water_vapor_saturation_pressure_h2o);
// logger->info("temperature2 :{}", m_hpp272_data_1.temperature2);
// logger->info("h2o2_vapor_pressure :{}", m_hpp272_data_1.h2o2_vapor_pressure);
// logger->info("water_vapor_saturation_pressure_h2o_h2o2:{}", m_hpp272_data_1.water_vapor_saturation_pressure_h2o_h2o2);
void DisinfectionCtrlService::dumpDisinfectionLogsToCSV(DisinfectionContext& context) { void DisinfectionCtrlService::dumpDisinfectionLogsToCSV(DisinfectionContext& context) {
auto* sensors = &m_context.h2o2data.h2o2sensor_data[0]; auto* sensors = &m_context.h2o2data.h2o2sensor_data[0];

Loading…
Cancel
Save