#include "haccp.h" #include #include #include #include #include "unistd.h" #include "stringer.h" #include "oven.h" #include "config.h" #include "udphandler.h" namespace { enum Type { Invalid, Manual, Auto, Multi, Wash, Engineering }; struct Stamp { QDateTime time; int remainingTime; int targetTemp; int curTemp; int targetCoreTemp; int curCoreTemp; int targetHumidity; int waterLevel; int heater; QString caption; bool door; bool ssv; }; struct AutoCookConfig { Define::CookConfigType type; int level; }; struct Data { QDateTime startedAt; Type type; QString autoRoot; QList autoConfig; int washType; QList records; } data; int processCount; QTimer checkTimer; QTime lastStampedTime; Define::Mode lastMode; int lastFan; int lastWaterLevel; bool lastDoor; bool lastDamper; bool lastSideNozzle; bool lastCoreTempEnabled; int figureProcessCount(); void initData(Type type); void start(); void stamp(QString caption = ""); void stampStart(); void stampMode(Define::Mode mode); void stampDamper(); void stampSideNozzle(); void stampEnd(); void stampError(QString error); void saveData(); void check(); } namespace HACCP { void init() { // make the directory if it doesn't exist QDir().mkpath("/prime/haccp"); processCount = figureProcessCount() + 1; checkTimer.setInterval(100); QObject::connect(&checkTimer, &QTimer::timeout, check); Oven *oven = Oven::getInstance(); QObject::connect(oven, &Oven::changed, check); } void autoStart(const QString &path) { initData(Auto); data.autoRoot = path; start(); } void manualStart() { initData(Manual); start(); } void multiStart() { initData(Multi); start(); } void washStart(int type) { initData(Wash); data.washType = type; } void engineeringStart() { initData(Engineering); } void done() { if (data.type == Invalid) return; stampEnd(); saveData(); } void error(QString code) { if (data.type == Invalid) return; if (data.type == Engineering) stampError(code); else { stampError(code); saveData(); } } void setConfig(Define::CookConfigType type, int level) { data.autoConfig.append(AutoCookConfig{type, level}); } void setStep(Define::StepType type) { switch (type) { case Define::Preheat: stamp("preheat"); break; case Define::Load: stamp("load"); break; case Define::PutThermometer: stamp("therometer"); break; case Define::Cut: stamp("cut"); break; case Define::Pour: stamp("pour"); break; case Define::Bake: stamp("bake"); break; case Define::Dry: stamp("dry"); break; case Define::Ferment: stamp("ferment"); break; case Define::BlowSteam: stamp("steaming"); break; case Define::CoolDown: stamp("cooldown"); break; case Define::Steam: stamp("steam"); break; case Define::Roast: stamp("roasting"); break; case Define::Boil: stamp("boil"); break; case Define::Thicken: stamp("thicken"); break; case Define::WarmUp: stamp("warmup"); break; case Define::MakeCrispy: stamp("crispy2"); break; case Define::Finish: stamp("finish"); break; case Define::Damp: stamp("damp"); break; case Define::Defer: stamp("defer"); break; case Define::Grill: stamp("grill"); break; case Define::End: stamp("end"); break; case Define::Burn: stamp("burn"); break; case Define::Fry: stamp("fry"); break; case Define::HeatUp: stamp("heatup"); break; case Define::Ripen: stamp("ripen"); break; case Define::RipenKeep: stamp("ripenkeep"); break; case Define::BoilSteadily: stamp("boilsteadily"); break; case Define::CookGratin: stamp("cookgratin"); break; case Define::Brown: stamp("brown"); break; case Define::Simmer: stamp("simmer"); break; case Define::Moisten: stamp("moisten2"); break; } } void setProcess(Define::Process type) { switch (type) { case Define::CookAgain: stamp("again"); break; case Define::MakeCrisper: stamp("crispy3"); break; case Define::KeepWarm: stamp("heatreserve"); break; } } } namespace { QString path() { return "/prime/haccp"; } QString path(int year) { return QString("/prime/haccp/%1").arg(year, 4, 10, QLatin1Char('0')); } QString path(int year, int month) { return QString("/prime/haccp/%1/%2") .arg(year, 4, 10, QLatin1Char('0')) .arg(month, 2, 10, QLatin1Char('0')); } QString path(int year, int month, int day) { return QString("/prime/haccp/%1/%2/%3") .arg(year, 4, 10, QLatin1Char('0')) .arg(month, 2, 10, QLatin1Char('0')) .arg(day, 2, 10, QLatin1Char('0')); } QString path(int year, int month, int day, int proc) { return QString("/prime/haccp/%1/%2/%3/%4") .arg(year, 4, 10, QLatin1Char('0')) .arg(month, 2, 10, QLatin1Char('0')) .arg(day, 2, 10, QLatin1Char('0')) .arg(proc); } int figureLatest(const QString &path, QDir::Filter filter) { QDir root(path); if (!root.exists()) return -1; QStringList list = root.entryList(filter); if (list.empty()) return -1; int latest = -1; foreach (QString e, list) { bool ok; int num = e.toInt(&ok); if (!ok) continue; if (num > latest) latest = num; } return latest; } int figureLatestDir(const QString &path) { return figureLatest(path, QDir::AllDirs); } int figureLatestFile(const QString &path) { return figureLatest(path, QDir::Files); } int figureProcessCount() { int year = figureLatestDir(path()); if (year < 0) return 0; int month = figureLatestDir(path(year)); if (month < 0) return 0; int day = figureLatestDir(path(year, month)); if (day < 0) return 0; int proc = figureLatestFile(path(year, month, day)); if (proc < 0) return 0; return proc; } void initData(Type type) { data.type = type; data.autoRoot = QString(); data.autoConfig.clear(); data.washType = 0; data.records.clear(); data.startedAt = QDateTime::currentDateTime(); } void start() { lastMode = Define::InvalidMode; lastFan = -1; lastWaterLevel = -1; lastDoor = false; lastDamper = false; lastSideNozzle = false; lastCoreTempEnabled = false; Oven *oven = Oven::getInstance(); stampMode(oven->mode()); checkTimer.start(); } void stamp(QString caption) { if (data.type == Invalid) return; Oven *oven = Oven::getInstance(); UdpHandler *udp = UdpHandler::getInstance(); Stamp s; s.time = QDateTime::currentDateTime(); s.remainingTime = oven->time(); s.targetTemp = oven->temp(); s.curTemp = oven->currentTemp(); if (oven->interTempEnabled()) { s.targetCoreTemp = oven->interTemp(); s.curCoreTemp = oven->currentInterTemp(); } else { s.targetCoreTemp = 0; s.curCoreTemp = 0; } s.targetHumidity = oven->humidity(); s.door = oven->door(); s.caption = caption; s.waterLevel = udp->waterLevel(); s.ssv = udp->ssv(); s.heater = udp->heater(); data.records.append(s); lastDoor = oven->door(); lastWaterLevel = udp->waterLevel(); lastStampedTime.start(); } void stampStart() { stamp("START (POWER ON)"); } void stampMode(Define::Mode mode) { switch (mode) { case Define::SteamMode: stamp("Mode STEAM"); break; case Define::CombiMode: stamp("Mode COMBI"); break; case Define::DryMode: stamp("Mode DRY"); break; } lastMode = mode; } void stampFan(int fan) { lastFan = fan; stamp(QString("FAN = %1").arg(fan)); } void stampDamper() { stamp("DAMPER"); } void stampSideNozzle() { stamp("SIDE NOZZLE"); } void stampEnd() { stamp("END"); } void stampError(QString error) { if (data.type == Engineering) stamp(QString("ERROR (%1)").arg(error)); else stamp(QString("END (%1)").arg(error)); } void saveData() { if (data.records.isEmpty()) { data.type = Invalid; return; } QDate date = data.startedAt.date(); QDir().mkpath(path(date.year(), date.month(), date.day())); QString p = path(date.year(), date.month(), date.day(), processCount); QFile file(p); if (!file.open(QIODevice::Truncate | QIODevice::WriteOnly)) { qDebug() << "Failed to open"; return; } QTextStream stream(&file); stream.setCodec("UTF-8"); stream << "No.," << processCount << "\n"; stream << "Typ," << Config::getInstance()->getProductModelName() << "\n"; stream << "Serial No.," << Config::getInstance()->getProductSerial() << "\n"; stream << "Version," << Config::getInstance()->getProductSWVersion() << "\n"; stream << "Time," << data.startedAt.toString("yyyy.MM.dd HH:mm:ss") << "\n"; stream << "Progr,"; switch (data.type) { case Manual: stream << 1; break; case Auto: stream << data.autoRoot; break; case Multi: stream << 4; break; case Wash: stream << 6 << "-" << data.washType; break; case Engineering: stream << 999; break; } stream << "\n"; stream << "\n" << "#1,Time\n" << "#2,Inner Tank Temp\n" << "#3,Food Temp (Target)\n" << "#4,Food Temp\n" << "#5,Door Open / Closed\n" << "#6,Humidity (Target)\n" << "#7,Temp (Target)\n" << "#8,Time (Target)\n" << "#9,Water Level\n" << "#10,Valve\n" << "#11,Heater\n"; stream << "\n"; stream << "#1,#2,#3,#4,#5,#6,#7,#8,#9,#10,#11\n"; for (int i = 0; i < data.records.size(); i++) { const Stamp &s = data.records.at(i); if (!s.caption.isEmpty() && i+1 < data.records.size()) { Stamp &n = data.records[i+1]; int secs = data.startedAt.secsTo(s.time); int nextSecs = data.startedAt.secsTo(n.time); if (secs == nextSecs && s.curTemp == n.curTemp && s.targetCoreTemp == n.targetCoreTemp && s.curCoreTemp == n.curCoreTemp && s.door == n.door) { if (n.caption.isEmpty()) n.caption = s.caption; else n.caption = QString("%1\n%2").arg(s.caption).arg(n.caption); continue; } } if (!s.caption.isEmpty()) stream << s.caption << "\n"; stream << Stringer::timeSecs(data.startedAt.secsTo(s.time)) << "," << s.curTemp << "," << s.targetCoreTemp << "," << s.curCoreTemp << "," << (s.door ? "O," : "C,") << s.targetHumidity << "," << s.targetTemp << "," << Stringer::timeSecs(s.remainingTime) << "," << (s.waterLevel == 3 ? "H," : s.waterLevel == 1 ? "C," : s.waterLevel == 0 ? "L," : "E,") << (s.ssv ? "O," : "C,") << (s.heater & 0x01 ? "1" : "0") << (s.heater & 0x02 ? "1" : "0") << (s.heater & 0x04 ? "1" : "0") << (s.heater & 0x08 ? "1" : "0") << "\n"; } if (!data.autoConfig.isEmpty()) { foreach (AutoCookConfig c, data.autoConfig) stream << Define::name(c.type) << " = " << c.level << "\n"; } stream.flush(); file.close(); sync(); processCount++; data.type = Invalid; checkTimer.stop(); } void check() { switch (data.type) { case Invalid: case Wash: case Engineering: return; default: break; } Oven *oven = Oven::getInstance(); UdpHandler *udp = UdpHandler::getInstance(); if (oven->mode() != lastMode) stampMode(oven->mode()); if (oven->fan() != lastFan) stampFan(oven->fan()); if (oven->damper() != lastDamper) { lastDamper = oven->damper(); if (lastDamper) stampDamper(); } if (oven->humidification() != lastSideNozzle) { lastSideNozzle = oven->humidification(); if (lastSideNozzle) stampSideNozzle(); } if (oven->interTempEnabled() != lastCoreTempEnabled) { lastCoreTempEnabled = oven->interTempEnabled(); stamp(); } if (oven->door() != lastDoor) stamp(); #if WATER_LEVEL_HACCP_STAMP if (udp->waterLevel() != lastWaterLevel) stamp(); #endif if (lastStampedTime.elapsed() > 30 * 1000 - 100) stamp(); } }