//////////////////////////////////////////////////////////////////////
// Copyright (C) Hiroshi SUGIMURA 2021.11.11
//////////////////////////////////////////////////////////////////////
/**
* @module mainOmron
*/
'use strict'
//////////////////////////////////////////////////////////////////////
// 基本ライブラリ
const Store = require('electron-store');
const omron = require('usb-2jcie-bu');
const cron = require('node-cron');
require('date-utils'); // for log
const { Sequelize, Op, roomEnvModel } = require('./models/localDBModels'); // DBデータと連携
const { objectSort, getNow, getToday, isObjEmpty, mergeDeeply } = require('./mainSubmodule');
let sendIPCMessage = null;
const store = new Store();
let config = {
enabled: false,
debug: false,
place: 'Room'
};
let persist = {};
//////////////////////////////////////////////////////////////////////
// mainOmron
let mainOmron = {
isRun: false,
observationJob: null,
storeJob: null,
//////////////////////////////////////////////////////////////////////
//
/**
* @func start
* @desc Omronセンサの処理開始
* @param {Object} _sendIPCMessage
* @return void
* @throw error
*/
start: function (_sendIPCMessage) {
sendIPCMessage = _sendIPCMessage;
if (mainOmron.isRun) { // 重複起動対策
sendIPCMessage("renewOmronConfigView", config);
sendIPCMessage("renewOmron", persist);
mainOmron.sendTodayRoomEnv(); // 現在のデータを送っておく
return;
}
config.enabled = store.get('config.Omron.enabled', config.enabled);
config.place = store.get('config.Omron.place', config.place);
config.debug = store.get('config.Omron.debug', config.debug);
persist = store.get('persist.Omron', persist);
sendIPCMessage("renewOmronConfigView", config);
if (config.enabled == false) {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.start() Omron is disabled.') : 0;
mainOmron.isRun = false;
return;
}
mainOmron.isRun = true;
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.start()') : 0;
try {
omron.start((sensorData, error) => {
if (error) {
switch (error) {
case 'INF: port is closed.': // ポート閉じたというのはエラーというか、正常状態でもある
sendIPCMessage('omronDisconnected', null);
break;
case 'Error: recvData is nothing.': // recvDataがないというのはよく発生する
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.omron.start() callback', '\x1b[32m', error, '\x1b[0m') : 0;
break;
case 'Error: usb-2jcie-bu.requestData(): port is not found.': // portがないというのもよくある
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.omron.start() callback', '\x1b[32m', error, '\x1b[0m') : 0;
break;
default:
// それ以外のエラーは良く知らないのでエラーとして出す
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.omron.start()', error);
}
return;
}
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.start() sensorData:', '\x1b[32m', sensorData, '\x1b[0m') : 0;
persist = sensorData;
persist.time = new Date().toFormat("YYYY-MM-DD HH24:MI:SS");
sendIPCMessage("renewOmron", persist);
});
// 3秒毎にセンサの値チェック
mainOmron.observationJob = cron.schedule('*/3 * * * * *', () => {
omron.requestData();
});
} catch (e) {
}
mainOmron.storeJob = cron.schedule('*/1 * * * *', async () => {
try {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.cron.schedule() every 1min') : 0;
let dt = new Date();
//------------------------------------------------------------
// 部屋の環境を記録、Omron
if (config.enabled && persist.length != 0) {
// config.debug ? console.log( new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| main.cron.schedule() Store Omron'):0;
let n = persist;
if (n) {
roomEnvModel.create({
dateTime: dt,
srcType: 'omron',
place: config.place ? config.place : 'MyRoom',
temperature: n.temperature,
humidity: n.humidity,
anbientLight: n.anbient_light,
pressure: n.pressure,
noise: n.noise,
TVOC: n.etvoc,
CO2: n.eco2,
discomfortIndex: n.discomfort_index,
heatStroke: n.heat_stroke
});
}
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.cron.schedule() persist:', persist) : 0;
}
mainOmron.sendTodayRoomEnv(); // 本日のデータの定期的送信
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.cron.schedule() each 1min, error:', error);
}
});
sendIPCMessage("renewOmron", persist);
mainOmron.sendTodayRoomEnv(); // 現在のデータを送っておく
mainOmron.storeJob.start();
},
/**
* @func stop
* @desc stop
* @async
* @param {void}
* @return void
* @throw error
*/
stop: async function () {
mainOmron.isRun = false;
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.stop()') : 0;
if (mainOmron.observationJob) {
await mainOmron.observationJob.stop();
mainOmron.observationJob = null;
}
await mainOmron.setConfig(config);
await store.set('persist.Omron', persist);
await omron.stop();
},
/**
* @func stopWithoutSave
* @desc stopWithoutSave
* @async
* @param {void}
* @return void
* @throw error
*/
stopWithoutSave: async function () {
mainOmron.isRun = false;
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.stopWithoutSave()') : 0;
if (mainOmron.observationJob) {
await mainOmron.observationJob.stop();
mainOmron.observationJob = null;
}
await omron.stop();
},
/**
* @func setConfig
* @desc setConfig
* @async
* @param {void}
* @return void
* @throw error
*/
setConfig: async function (_config) {
if (_config) {
config = mergeDeeply(config, _config);
}
await store.set('config.Omron', config);
sendIPCMessage("renewOmronConfigView", config);
sendIPCMessage("configSaved", 'Omron');// 保存したので画面に通知
},
/**
* @func getConfig
* @desc getConfig
* @async
* @param {void}
* @return void
* @throw error
*/
getConfig: function () {
return config;
},
/**
* @func getPersist
* @desc getPersist
* @async
* @param {void}
* @return void
* @throw error
*/
getPersist: function () {
return persist;
},
//////////////////////////////////////////////////////////////////////
// inner functions
/**
* @func getCases
* @desc getCases
* @async
* @param {void}
* @return void
* @throw error
*/
/*
getCases
input
date: Date="2023-01-06"
output
when createdAt >= "2023-01-05 23:57" and createdAt < "2023-01-06 00:00" then "00:00"
when createdAt >= "2023-01-06 00:00" and createdAt < "2023-01-06 00:03" then "00:03"
when createdAt >= "2023-01-06 00:03" and createdAt < "2023-01-06 00:06" then "00:06"
...
when createdAt >= "2023-01-06 23:54" and createdAt < "2023-01-06 23:57" then "23:57"
else "24:00"
*/
getCases: function (date) {
let T1 = new Date(date);
let T2 = new Date(date);
let T3 = new Date(date);
let T4 = new Date(date);
// UTCだがStringにて表現しているので、なんか複雑
T1.setHours(T1.getHours() - T1.getHours() - 10, 57, 0, 0); // 前日の14時57分xx秒 14:57:00 .. 15:00:00 --> 00:00
T2.setHours(T1.getHours() - T1.getHours() - 10, 58, 0, 0); // T1 + 1min
T3.setHours(T1.getHours() - T1.getHours() - 10, 59, 0, 0); // T1 + 2min
T4.setHours(T1.getHours() - T1.getHours(), 0, 0, 0); // 集約先
let ret = "";
for (let t = 0; t < 480; t += 1) { // 24h * 20 times (= 60min / 3min)
// console.log( T1.toISOString(), ':', T1.toFormat('YYYY-MM-DD HH24:MI'), ', ', T4.toFormat('HH24:MI') );
ret += `WHEN "createdAt" LIKE "${T1.toFormat('YYYY-MM-DD HH24:MI')}%" OR "createdAt" LIKE "${T2.toFormat('YYYY-MM-DD HH24:MI')}%" OR "createdAt" LIKE "${T3.toFormat('YYYY-MM-DD HH24:MI')}%" THEN "${T4.toFormat('HH24:MI')}" \n`;
T1.setMinutes(T1.getMinutes() + 3); // + 3 min
T2.setMinutes(T2.getMinutes() + 3); // + 3 min
T3.setMinutes(T3.getMinutes() + 3); // + 3 min
T4.setMinutes(T4.getMinutes() + 3); // + 3 min
}
return ret + 'ELSE "24:00"';
},
/**
* @func getRows
* @desc getRows
* @async
* @param {void}
* @return void
* @throw error
*/
// DBからテーブル取得
getRows: async function () {
try {
let now = new Date(); // 現在
let begin = new Date(now); // 現在時刻UTCで取得
begin.setHours(begin.getHours() - begin.getHours() - 1, 57, 0, 0); // 前日の23時57分0秒にする
let end = new Date(begin); // 現在時刻UTCで取得
end.setHours(begin.getHours() + 25, 0, 0, 0); // 次の日の00:00:00にする
let cases = mainOmron.getCases(now);
let subQuery = `CASE ${cases} END`;
// 3分毎データ
let rows = await roomEnvModel.findAll({
attributes: ['id',
[Sequelize.fn('AVG', Sequelize.col('temperature')), 'avgTemperature'],
[Sequelize.fn('AVG', Sequelize.col('humidity')), 'avgHumidity'],
[Sequelize.fn('AVG', Sequelize.col('anbientLight')), 'avgAnbientLight'],
[Sequelize.fn('AVG', Sequelize.col('pressure')), 'avgPressure'],
[Sequelize.fn('AVG', Sequelize.col('noise')), 'avgNoise'],
[Sequelize.fn('AVG', Sequelize.col('TVOC')), 'avgTVOC'],
[Sequelize.fn('AVG', Sequelize.col('CO2')), 'avgCO2'],
[Sequelize.fn('AVG', Sequelize.col('discomfortIndex')), 'avgDiscomfortIndex'],
[Sequelize.fn('AVG', Sequelize.col('heatStroke')), 'avgHeatStroke'],
'createdAt',
[Sequelize.literal(subQuery), 'timeunit']
],
where: {
srcType: 'omron',
dateTime: { [Op.between]: [begin.toISOString(), end.toISOString()] }
},
group: ['timeunit']
});
return rows;
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.getTodayRoomEnvOmron()', error);
}
},
/**
* @func getTodayRoomEnv
* @desc getTodayRoomEnv
* @async
* @param {void}
* @return void
* @throw error
*/
getTodayRoomEnv: async function () {
// 画面に今日のデータを送信するためのデータ作る
try {
let rows = await mainOmron.getRows();
let T1 = new Date();
T1.setHours(0, 0, 0);
let array = [];
for (let t = 0; t < 480; t += 1) {
let row = rows.find((row) => row.dataValues.timeunit == T1.toFormat('HH24:MI'));
if (row) {
array.push({
id: t,
time: T1.toISOString(),
srcType: 'omron',
temperature: row.dataValues.avgTemperature,
humidity: row.dataValues.avgHumidity,
anbientLight: row.dataValues.avgAnbientLight,
pressure: row.dataValues.avgPressure,
noise: row.dataValues.avgNoise,
TVOC: row.dataValues.avgTVOC,
CO2: row.dataValues.avgCO2,
discomfortIndex: row.dataValues.avgDiscomfortIndex,
heatStroke: row.dataValues.avgHeatStroke
});
} else {
array.push({
id: t,
time: T1.toISOString(),
srcType: 'omron',
temperature: null,
humidity: null,
anbientLight: null,
pressure: null,
noise: null,
TVOC: null,
CO2: null,
discomfortIndex: null,
heatStroke: null
});
}
T1.setMinutes(T1.getMinutes() + 3); // + 3 min
}
return array;
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.getTodayRoomEnvOmron()', error);
}
},
/**
* @func sendTodayRoomEnv
* @desc sendTodayRoomEnv
* @async
* @param {void}
* @return void
* @throw error
*/
sendTodayRoomEnv: async function () {
let arg = {};
if (config.enabled) {
arg = await mainOmron.getTodayRoomEnv();
sendIPCMessage('renewRoomEnvOmron', JSON.stringify(arg));
} else {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainOmron.sendTodayRoomEnv() config.enabled:', config.enabled);
}
}
};
module.exports = mainOmron;
//////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////