re commit

This commit is contained in:
Jay 2017-03-22 13:35:45 +08:00
commit 8947715671
109 changed files with 161369 additions and 0 deletions

8
.babelrc Normal file
View File

@ -0,0 +1,8 @@
{
"presets":[
"es2015",
"react",
"stage-1"
],
"plugins": []
}

24
.eslintrc.json Normal file
View File

@ -0,0 +1,24 @@
{
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"rules": {
"no-const-assign": "warn",
"no-this-before-super": "warn",
"no-undef": "warn",
"no-unreachable": "warn",
"no-unused-vars": "warn",
"constructor-super": "warn",
"valid-typeof": "warn"
},
"parser": "babel-eslint"
}

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

50
app.js Normal file
View File

@ -0,0 +1,50 @@
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const cors = require('cors');
const logger = require('morgan');
const path = require('path');
const config = require('./config');
const so = require('./includes/storeObject');
const app = express();
const api_route = require('./route/api');
// storeObject interval clear
const clearStore = setInterval(() =>{
so.clear();
}, 30000)
app.set('port', process.env.PORT || config.system.port);
app.use(logger('short'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
resave: false,
saveUninitialized: true,
secret: '6520833345e05e0dcfce'
}));
app.use(cors());
app.use(express.static(path.resolve(__dirname, 'public')));
app.use('/semantic', express.static(path.resolve(__dirname, 'node_modules', 'semantic-ui-css')));
app.use('/react-datetime', express.static(path.resolve(__dirname, 'node_modules', 'react-datetime', 'css')));
const server = app.listen(app.get('port'), () => {
console.log(`Server start on port ${server.address().port}`);
});
// use route
app.use('/api', api_route);
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'views', 'index.html'));
});
app.get(['/admin','/admin/*'], (req,res) => {
res.sendFile(path.resolve(__dirname, 'views', 'admin.html'));
});

44
config.json Normal file
View File

@ -0,0 +1,44 @@
{
"system": {
"port": 3000
},
"uni_token": "webiounitoken",
"perpage": 10,
"leone_limie": 40,
"db": {
"user": "webio",
"pass": "16055536",
"host": "192.168.98.227",
"port": "3306",
"db1": "jcwebioc",
"db2": "jciocer",
"db3": "jciochtr",
"db4": "jcles",
"db5": "jcmbset",
"db6": "jcmbrt",
"db7": "jcioclc",
"db8": "jciocln"
},
"permission": {
"dio": true,
"log": true,
"leone": true,
"iogroup": true,
"iocmd": true,
"schedule": true,
"modbus": true,
"link": true
},
"cmdpath":{
"manualip": "/home/www/cmd/manualip",
"dhcpip": "/home/www/cmd/dhcpip",
"sysinfo": "/home/www/sysinfo",
"settime": "/home/www/cmd/chd",
"scanleone": "/home/www/cmd/scanleone",
"scanleone_end": "/home/www/scan_end",
"iocmd": "/home/www/cmd/cmdio",
"leonert": "/home/www/tmp/rtles",
"htsrt": "/home/www/tmp/rthts",
"version": "/factory/webioa_version"
}
}

87
includes/apiTool.js Normal file
View File

@ -0,0 +1,87 @@
const so = require('./storeObject');
const config = require('../config');
const fs = require('fs');
const checkPermission = (req) => {
let id = req.headers['x-auth-token'];
if (id) {
let obj = so.get(id);
if (config.uni_token.length > 0 && id == config.uni_token) return true;
if (obj != null) {
if ('user' in obj && obj.user.write_privilege == '1') return true;
}
}
return false;
}
const checkArray = (obj) => {
if (Array.isArray(obj)) return obj;
return [];
}
const getLeoneRT = (cb) => {
if (!cb || typeof cb != 'function') return;
fs.exists(config.cmdpath.leonert, exists => {
if (!exists) return cb([]);
fs.readFile(config.cmdpath.leonert, (err, data) => {
if (err) return cb([]);
let str = data.toString();
let tmp = str.split(/\n/);
let rt = [];
for (var i in tmp) {
if (!tmp[i].trim()) continue;
let arr = tmp[i].split(' ');
if (arr.length != 5) continue;
let [ip, ts, hs, mode, mtime] = arr;
rt.push({ ip, ts, hs, mode, mtime });
}
return cb(rt);
});
});
}
const promiseQuery = (res, query, param, key = '') => {
return new Promise((resolve, reject) => {
res.db.query(query, param, (err, row) => {
if (err) return reject(err);
resolve({ data: row, key });
});
});
}
const getMode = (req) => {
let lngs = req.headers['accept-language'].split(',');
let lng = null;
if (lngs.length > 0) {
lng = lngs[0].substring(0, 2);
} else {
lng = 'zh';
}
if (!fs.existsSync(`../public/locales/${lng}.json`)) lng = 'zh';
let json = require(`../public/locales/${lng}.json`);
return json[lng].translation.leone_stats;
}
const getCmd = (req) => {
let lngs = req.headers['accept-language'].split(',');
let lng = null;
if (lngs.length > 0) {
lng = lngs[0].substring(0, 2);
} else {
lng = 'zh';
}
if (!fs.existsSync(`../public/locales/${lng}.json`)) lng = 'zh';
let json = require(`../public/locales/${lng}.json`);
return json[lng].translation.action_list;
}
module.exports = {
checkPermission,
checkArray,
getLeoneRT,
promiseQuery,
getCmd,
getMode
}

9
includes/errorManager.js Normal file
View File

@ -0,0 +1,9 @@
const manager = (errCode = '', lang = 'zh') => {
if(typeof lang != 'string' || !lang.trim()) lang = 'zh';
lang = lang.toLowerCase();
let errors = require(`./language/${lang}.json`);
if(errCode in errors) return errors[errCode];
else return 'Error Code not found';
}
module.exports = manager;

65
includes/language/zh.json Normal file
View File

@ -0,0 +1,65 @@
{
"ERR0000": "資料輸入錯誤",
"ERR0001": "PIN輸入錯誤",
"ERR0002": "value輸入錯誤",
"ERR0003": "DI資料查詢錯誤",
"ERR0004": "DO資料查詢錯誤",
"ERR0005": "DI資料輸入錯誤",
"ERR0006": "DO資料輸入錯誤",
"ERR0007": "DO資訊寫入失敗",
"ERR0008": "DI資訊寫入失敗",
"ERR0009": "類型輸入錯誤",
"ERR0010": "IP輸入錯誤",
"ERR0011": "Netmask輸入錯誤",
"ERR0012": "Gateway輸入錯誤",
"ERR0013": "DNS輸入錯誤",
"ERR0014": "網路資訊取得失敗",
"ERR0015": "時間輸入錯誤",
"ERR0016": "帳號輸入錯誤",
"ERR0017": "密碼輸入錯誤",
"ERR0018": "使用者資料取得失敗",
"ERR0019": "帳號或密碼錯誤",
"ERR0020": "刪除使用者失敗",
"ERR0021": "使用者資料更新失敗",
"ERR0022": "使用者資料新增失敗",
"ERR0023": "數量計算錯誤",
"ERR0024": "數量取得失敗",
"ERR0025": "IP格式錯誤",
"ERR0026": "名稱輸入錯誤",
"ERR0027": "此IP裝置已存在",
"ERR0028": "ID輸入錯誤",
"ERR0029": "裝置輸入錯誤",
"ERR0030": "動作輸入錯誤",
"ERR0031": "溫度請介於16-30間",
"ERR0032": "啟用狀態輸入錯誤",
"ERR0033": "分鐘輸入錯誤",
"ERR0034": "小時輸入錯誤",
"ERR0035": "日輸入錯誤",
"ERR0036": "月輸入錯誤",
"ERR0037": "admin 不能被刪除",
"ERR0038": "裝置節點輸入錯誤",
"ERR0039": "裝置Di數量輸入錯誤",
"ERR0040": "裝置Di起始位址輸入錯誤",
"ERR0041": "裝置Do數量輸入錯誤",
"ERR0042": "裝置Do起始位址輸入錯誤",
"ERR0043": "裝置Ai數量輸入錯誤",
"ERR0044": "裝置Ai起始位址輸入錯誤",
"ERR0045": "裝置Ao數量輸入錯誤",
"ERR0046": "裝置Ao起始位址輸入錯誤",
"ERR0047": "裝置節點編號已存在",
"ERR0048": "位址輸入錯誤",
"ERR0049": "數值輸入錯誤",
"ERR0050": "最小值輸入錯誤",
"ERR0051": "最大值輸入錯誤",
"ERR0052": "ScaleMin輸入錯誤",
"ERR0053": "ScaleMax輸入錯誤",
"ERR0054": "此位址設定已存在",
"ERR7000": "命令執行失敗",
"ERR8000": "資料查詢失敗",
"ERR8001": "資料新增失敗",
"ERR8002": "資料更新失敗",
"ERR8003": "資料刪除失敗",
"ERR9000": "操作權限不足"
}

68
includes/storeObject.js Normal file
View File

@ -0,0 +1,68 @@
const StoreObj = (() => {
let memStore = {};
let maxAge = 3600000;
/**
* @param {string} uuid
* @param {object} obj
*/
const set = (uuid, obj) => {
memStore[uuid] = {
obj,
age: Date.now() + maxAge
}
}
/**
* @param {string} uuid
*/
const get = (uuid) => {
if (uuid in memStore) {
let s = memStore[uuid];
if (Date.now() < s.age) {
s.age = Date.now() + maxAge;
return s.obj;
} else {
delete memStore[uuid];
}
}
return null;
}
/**
* @param {string} uuid
*/
const chkKey = (uuid) => {
if (uuid in memStore) return true;
return false;
}
/**
* @param {string} uuid
*/
const del = (uuid) => {
if (uuid in memStore) delete memStore[uuid];
}
const clear = () => {
let t = Date.now();
for (var i in memStore) {
let s = memStore[i];
if (s.age < t) delete memStore[i];
}
}
/**
* @param {string} uuid if not input return all
*/
const show = (uuid) => {
if (uuid && uuid in memStore) return memStore[uuid];
return memStore;
}
return {set, get, chkKey, clear, del, show };
})()
module.exports = StoreObj;

36
libs/confLoader.js Normal file
View File

@ -0,0 +1,36 @@
var fs = require('fs');
var path = require('path');
var confLoader = {
_conf: null,
_path: null,
load: (p) => {
var self = this;
if (!p || p == undefined) {
p = path.resolve(__dirname, '..', 'config.json');
}
self._path = p;
if (!fs.existsSync(p)) {
throw `Config file not exists , file location : ${p}`;
}
var filecontent = fs.readFileSync(p);
if (!filecontent) {
throw `Config file read fail`;
}
filecontent = filecontent.toString();
var conf = null;
try {
conf = JSON.parse(filecontent);
self._conf = conf;
} catch (e) {
throw e;
}
return self._conf;
},
get: (key) => {
return this._conf[key];
}
};
module.exports = confLoader;

40
libs/confLoader_cls.js Normal file
View File

@ -0,0 +1,40 @@
var fs = require('fs');
var path = require('path');
class confLoader {
constructor() {
this._conf = null;
this._path = null;
}
load(p) {
var self = this;
if (!p || p == undefined) {
p = path.resolve(__dirname, '..', 'config.json');
}
self._path = p;
if (!fs.existsSync(p)) {
throw `Config file not exists , file location : ${p}`;
}
var filecontent = fs.readFileSync(p);
if (!filecontent) {
throw `Config file read fail`;
}
filecontent = filecontent.toString();
var conf = null;
try {
conf = JSON.parse(filecontent);
self._conf = conf;
} catch (e) {
throw e;
}
return self;
}
get(key) {
return this._conf[key];
}
}
module.exports = confLoader;

31
libs/crypto.js Normal file
View File

@ -0,0 +1,31 @@
var crypto = require('crypto');
var random = (len = 32) => {
var buf = crypto.randomBytes(len);
return buf.toString("hex");
}
var sha256 = (str) => {
return crypto.createHash("sha256").update(str).digest('base64');
}
var genPassHash = (str) => {
var hash = random(16);
var pass = sha256(str + hash);
return `$${hash}$${pass}`;
}
var comparePass = (plain, hash) => {
var match = hash.match(/^\$(.+?)\$(.+)$/);
if (match == null || match.length < 3 || !match[1] || !match[2]) return false;
var pass = sha256(plain + match[1]);
if (pass == match[2]) return true;
return false;
}
module.exports = {
random: random,
sha256: sha256,
genPassHash: genPassHash,
comparePass: comparePass
}

30
libs/mysql.js Normal file
View File

@ -0,0 +1,30 @@
var mysql = require('mysql');
var config = require('./confLoader.js');
config.load();
var dbConf = config.get('db');
var database = function(){
this.conf = dbConf;
}
database.prototype.connect = function(){
var self = this;
self._con = mysql.createConnection(self.conf);
}
database.prototype.formatQuery = function(str, arr){
return mysql.format(str, arr);
}
database.prototype.query = function(query, cb){
var self = this;
self._con.query(query,cb);
}
database.prototype.close = function(){
this._con.end();
}
module.exports = database;

102
libs/mysql_cls.js Normal file
View File

@ -0,0 +1,102 @@
var mysql = require('mysql');
class database {
constructor() {
this._user = '';
this._password = '';
this._host = '';
this._port = 3306;
this._database = '';
this._con = null;
this.autoclose = false;
}
connect() {
if (!this._user || !this._host || !this._password || !this._database) throw 'mysql connect arg is empty';
this._con = mysql.createConnection({
user: this._user,
password: this._password,
host: this._host,
database: this._database,
port: this._port
});
}
checkConnect() {
if (!this._con) {
this.connect();
}
}
formatQuery(query, arg) {
return mysql.format(query, arg);
}
escape(val) {
return mysql.escape(val);
}
query(query, params, cb) {
this.checkConnect();
let self = this;
if (typeof params == 'function') {
cb = params;
params = [];
};
if (typeof params == 'object' && !Array.isArray(params)) params = [];
this._con.query(query, params, (err, rows, field) => {
if (self.autoclose) {
self.close(() => {
cb.apply(this, [err, rows, field]);
});
} else {
cb.apply(this, [err, rows, field]);
}
});
}
recordPage(rows, page, maxPage) {
if (!page || !isFinite(page) || page < 1) page = 1;
let totalpage = Math.ceil(rows / maxPage);
let prevpage = page - 1;
let nextpage = page + 1;
if (prevpage < 1) prevpage = 1;
if (nextpage > totalpage) nextpage = totalpage;
let rec_start = (page - 1) * maxPage + 1
let rec_end = (rec_start + maxPage - 1);
if (rec_end > rows) rec_end = rows;
let json = {
rec_start,
rec_end,
total: rows,
prevpage,
nextpage,
totalpage,
page
};
return json;
}
close(cb) {
let self = this;
this._con.end((err) => {
self._con = null;
if (cb) cb();
});
}
set user(str) { this._user = str; }
set host(str) { this._host = str; }
set password(str) { this._password = str; }
set port(str) { this._port = str; }
set database(str) { this._database = str; }
}
module.exports = database;

55
package.json Normal file
View File

@ -0,0 +1,55 @@
{
"name": "ci3",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"test": "node node_modules/webpack-dev-server/bin/webpack-dev-server",
"build": "NODE_ENV=production webpack -p --progress --colors",
"build-dev": "NODE_ENV=development webpack --watch --progress"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"body-parser": "^1.16.1",
"cookie-parser": "^1.4.3",
"cors": "^2.8.1",
"express": "^4.14.1",
"express-session": "^1.15.1",
"i18next": "^7.0.0",
"moment": "^2.17.1",
"morgan": "^1.8.1",
"mysql": "^2.13.0",
"react": "^15.4.2",
"react-datetime": "^2.8.8",
"react-dom": "^15.4.2",
"react-redux": "^5.0.2",
"react-router": "^3.0.2",
"redux": "^3.6.0",
"redux-thunk": "^2.2.0",
"semantic-ui-css": "^2.2.7",
"semantic-ui-react": "^0.65.0"
},
"devDependencies": {
"babel-core": "^6.23.1",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.3.1",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.23.0",
"babel-preset-stage-1": "^6.22.0",
"eslint": "^3.15.0",
"eslint-config-airbnb": "^14.1.0",
"eslint-loader": "^1.6.1",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.9.0",
"html-webpack-plugin": "^2.28.0",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.3.0"
},
"repository": {
"type": "git",
"url": "http://git.trj.tw/jcnet/webio-node.git"
},
"description": ""
}

24
public/css/main.css Normal file
View File

@ -0,0 +1,24 @@
html,
body,
div#app {
height: 100%;
width: 100%;
}
.login-grid {
height: 100%;
}
.login-column {
max-width: 450px;
}
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.hide-element {
display: 'none' !important;
}

90109
public/js/admin_bundle.js Normal file

File diff suppressed because it is too large Load Diff

62361
public/js/index_bundle.js Normal file

File diff suppressed because it is too large Load Diff

356
public/locales/zh.json Normal file
View File

@ -0,0 +1,356 @@
{
"zh": {
"translation": {
"page": {
"login": {
"input": {
"placeholder": {
"account": "請輸入帳號",
"password": "請輸入密碼"
},
"label": {
}
},
"button": {
"login": "登入"
}
},
"system_info": {
"title": {
"sysinfo": "系統資訊",
"timeinfo": "時間資訊"
},
"form": {
"input": {
},
"label": {
"ip": "IP",
"netmask": "Netmask",
"gateway": "Gateway",
"dns": "DNS",
"sysdate": "系統日期"
},
"button": {
"dhcpip": "使用DHCP",
"manualip": "手動設定",
"update_network": "更新網路資訊",
"update_time": "更新時間資訊"
}
}
},
"userlist": {
"title": {
"userlist": "使用者管理"
},
"table": {
"account": "帳號",
"privilege": "權限",
"operate": "操作",
"button": {
"edit": "修改",
"del": "刪除",
"add": "新增使用者"
}
},
"form": {
"title": {
"add": "新增資料",
"edit": "修改資料"
},
"label": {
"account": "帳號",
"password": "密碼",
"read_privilege": "讀取權限",
"write_privilege": "控制權限"
},
"button": {
"submit": "送出",
"cancel": "取消"
}
}
},
"dio": {
"title": {
"di": "DigitInput",
"do": "DigitOutput"
},
"form": {
"label": {
"logic": "邏輯",
"do_ctrl": "DO控制",
"di_status": "狀態",
"di_triggered": "觸發",
"di_not_triggered": "未觸發"
},
"button": {
"update_setting": "更新IO設定"
}
}
},
"log": {
"table": {
"datetime": "時間",
"username": "使用者",
"status": "狀態",
"event": "事件",
"description": "說明",
"button": {
"prevpage": "上一頁",
"nextpage": "下一頁"
}
},
"description": {
"dio": "$io_label$ $io_name$ 邏輯 $io_logic$ 在 $io_trigger_time$ 改變為 $io_trigger_status$",
"leone": "$io_label$ $io_name$ 在 $io_trigger_time$ 執行 $io_trigger_status$",
"iogroup": "$io_label$ $io_name$ 在 $io_trigger_time$ 執行 $io_trigger_status$"
}
},
"leone": {
"form": {
"title": {
"add": "新增資料",
"edit": "修改資料",
"select-dev": "選擇裝置"
},
"label": {
"ip": "IP",
"password": "密碼",
"name": "名稱",
"run_command": "執行命令",
"temp": "請輸入溫度",
"select_num": "已選數量"
},
"button": {
"scan": "掃描",
"submit": "送出",
"cancel": "取消",
"prevpage": "上一頁",
"nextpage": "下一頁"
}
},
"table": {
"operate": "操作",
"name": "名稱",
"ip": "IP",
"hsts": "溫度 / 濕度",
"mode": "模式",
"password": "密碼",
"update_time": "更新時間",
"control": "控制",
"button": {
"add": "新增",
"edit": "修改",
"del": "刪除"
}
},
"loader": {
"scan": "Scanning",
"load": "Loading"
}
},
"iogroup": {
"table": {
"operate": "操作",
"name": "名稱",
"groups": "群組內容",
"button": {
"showgroup": "顯示群組裝置",
"add": "新增",
"edit": "修改",
"del": "刪除"
}
},
"form": {
"title": {
"add": "新增資料",
"edit": "修改資料"
},
"label": {
"name": "名稱",
"select_device": "選擇裝置",
"selected_device": "已選裝置列表"
},
"button": {
"join": "加入",
"remove": "移除",
"submit": "送出",
"cancel": "取消"
}
}
},
"iocmd": {
"form": {
"label": {
"action": "動作",
"select_device": "選擇裝置",
"selected_device": "已選裝置列表",
"temp": "請輸入溫度"
},
"button": {
"join": "加入",
"remove": "移除",
"submit": "送出"
}
}
},
"schedule": {
"table": {
"operate": "操作",
"name": "名稱",
"schedule_time": "排程時間",
"action": "動作",
"active_status": "啟用狀態",
"device": "裝置",
"button": {
"add": "新增",
"del": "刪除",
"edit": "修改",
"enable": "啟用",
"disable": "停用",
"showgroup": "顯示群組裝置"
}
},
"form": {
"title": {
"add": "新增資料",
"edit": "修改資料"
},
"label": {
"enable": "啟用排程",
"name": "名稱",
"action": "動作",
"time": "時間",
"date": "請輸入日期",
"week": "請選擇週次",
"select_device": "選擇裝置",
"selected_device": "已選裝置列表",
"datetype": "時間類型",
"type_week": "週次",
"type_date": "日期",
"temp": "溫度"
},
"button": {
"join": "加入",
"remove": "移除",
"submit": "送出",
"cancel": "取消",
"remove": "移除"
}
}
}
},
"dashboard": {
"systime": "系統時間",
"sysip": "系統IP",
"sysinfo": "系統資訊",
"hsts": "溫濕度資訊",
"devstats": "裝置狀態",
"label": {
"status": "狀態",
"name": "名稱"
},
"status": {
"leone": "無回應(網路問題或是IP/密碼錯誤)",
"digitinput": "觸發"
}
},
"menu": {
"title": "選單",
"item": {
"systeminfo": "系統資訊",
"userlist": "使用者管理",
"dio": "DIO控制面板",
"log": "系統記錄",
"leone": "LeOne",
"iogroup": "IOGroup",
"iocmd": "IO CMD",
"schedule": "排程控制",
"modbus": "Modbus",
"link": "連動",
"logout": "登出"
}
},
"tip": {
"input_empty": "輸入欄位請勿空白",
"input_empty_password": "請輸入密碼",
"input_empty_ip": "請輸入IP",
"input_empty_temp": "請輸入溫度",
"input_empty_groupname": "請填寫群組名稱",
"input_empty_name": "請輸入名稱",
"input_empty_time": "請輸入時間",
"input_empty_date": "請輸入日期",
"date_format": "日期格式錯誤 m-d",
"date_range": "日期範圍輸入錯誤",
"time_format": "時間格式錯誤 H:M",
"time_range": "時間範圍輸入錯誤",
"temp_format": "溫度請介於16-30間",
"ip_format": "IP格式錯誤數值請介於0-255間",
"datetime_format": "請輸入時間日期 yyyy-mm-dd MM:ss",
"update_success": "更新成功",
"delete_success": "刪除完成",
"userdata_get_fail": "使用者資料取得失敗",
"select_action": "請選擇動作",
"select_device": "請選擇裝置",
"select_week": "請選擇週次",
"device_parser_error": "裝置列表解析錯誤",
"command_run_complete": "命令執行完成",
"action_error": "動作輸入錯誤",
"mb_io_type_dup": "相同類型已存在",
"scan_empty": "無新增裝置"
},
"action_list": [
{ "cmd": "0 0", "name": "關閉" },
{ "cmd": "0 1", "name": "開啟" },
{ "cmd": "1 1", "name": "冷氣功能 - 自動" },
{ "cmd": "1 2", "name": "冷氣功能 - 冷氣" },
{ "cmd": "1 3", "name": "冷氣功能 - 除濕" },
{ "cmd": "1 4", "name": "冷氣功能 - 送風" },
{ "cmd": "1 5", "name": "冷氣功能 - 暖氣" },
{ "cmd": "2", "name": "冷氣溫度" }
],
"leone_stats": [
{ "mode": "9999", "name": "無回應" },
{ "mode": "1", "name": "自動" },
{ "mode": "2", "name": "冷氣" },
{ "mode": "3", "name": "除濕" },
{ "mode": "4", "name": "送風" },
{ "mode": "5", "name": "暖氣" },
{ "mode": "99", "name": "關機" }
],
"time_unit": {
"sec": "秒",
"min": "分",
"hour": "小時",
"day": "天",
"ago": "前"
},
"week": {
"mon": "週一",
"tue": "週二",
"wed": "週三",
"thu": "週四",
"fri": "週五",
"sat": "週六",
"sun": "週日"
},
"porttype": [
{"code": "1", "name":"DigitOutput"},
{"code": "2", "name":"DigitInput"},
{"code": "3", "name":"AnalogyOutput"},
{"code": "4", "name":"AnalogyInput"}
],
"select": {
"select_action": "請選擇動作",
"dev_type": "請選擇裝置類型",
"dev_name": "請選擇裝置",
"action": "請選擇動作",
"digitoutput": "DigitOutput",
"leone": "LeOne",
"iogroup": "IOGroup"
}
}
}
}

36
route/ResTool.js Normal file
View File

@ -0,0 +1,36 @@
const errList = require('../includes/errorManager');
function send(req, res) {
if ('db' in res && typeof res.db == 'object' && 'close' in res.db && typeof res.db.close == 'function') res.db.close();
let lngs = req.headers['accept-language'].split(',');
let lng = null;
if (lngs.length > 0) {
lng = lngs[0].substring(0, 2);
}
let json = {};
if (!('api_res' in res)) {
json = {
errorCode: 'ERR9999',
message: 'api not found',
status: 0
};
} else if (typeof res.api_res != 'object') {
json = {
errorCode: res.api_res,
message: errList(res.api_res, lng),
status: 0
};
} else {
json = {
data: res.api_res,
status: 1
};
}
res.send(json);
}
module.exports = { send: send };

199
route/api/dio.js Normal file
View File

@ -0,0 +1,199 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const fs = require('fs');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
const exec = require('child_process').exec;
const so = require('../../includes/storeObject');
const crypt = require('../../libs/crypto');
router
.get('/', (req, res) => {
res.send({ name: 'WebIO DIO Api' });
})
.post('/getio', (req, res, n) => {
if (!config.permission.dio) return n('ERR9000');
res.api_res = {
record: [],
rt: {
di: {},
do: {}
}
};
let pdi = new Promise((resolve, reject) => {
let id = 1;
let dis = {};
! function dichk(i) {
if (i > 8) return resolve({ data: dis, key: 'di' });
dis[i] = 0;
exec(`ditchk ${i}`, (err, sout, serr) => {
if (err) return dichk(++i);
dis[i] = sout.replace(/\n/, '');
dichk(++i);
});
}(id)
});
let pdo = new Promise((resolve, reject) => {
let id = 1;
let dos = {};
! function dochk(i) {
if (i > 8) return resolve({ data: dos, key: 'do' });
dos[i] = 0;
exec(`dotchk ${i}`, (err, sout, serr) => {
if (err) return dochk(++i);
dos[i] = sout.replace(/\n/, '');
dochk(++i);
});
}(id)
});
Promise.all([pdi, pdo])
.then(r => {
for (let i in r) {
if (r[i].key == 'di') {
res.api_res.rt.di = r[i].data;
} else if (r[i].key == 'do') {
res.api_res.rt.do = r[i].data;
}
}
return n();
})
.catch(e => n());
})
.post('/dotrun', (req, res, n) => {
if (!config.permission.dio) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.pin) return n('ERR0001');
if (!('value' in arr.data)) return n('ERR0002');
exec(`dotrun ${arr.data.pin} ${arr.data.value}`, (err, sout, serr) => {
res.api_res = {
record: []
};
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let obj = so.get(req.headers['x-auth-token']);
let u = '';
if (obj != null && 'user' in obj && 'account' in obj.user) u = obj.user.account;
let query = "select * from ??.?? where `douid` = ?";
let param = [config.db.db1, 'dolist', arr.data.pin];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n();
let q = "insert into ??.?? (`eventtag`, `iolabel`, `ioname`, `iosetting1`, `iosetting2`, `iosetting3`, `ioevent`, `ioeventtst`) values ";
let p = [config.db.db2, 'jciocert'];
let doStat = sout.replace(/\n/, '') == 1 ? 1 : 0;
let str = `('WEB', 'DO${row[0].douid}', '${row[0].doname}', '${row[0].dologic}', '${doStat}', '${u}', '${doStat}', unix_timestamp())`;
res.db.query(q + str, p, (err, row) => n());
});
});
})
.post('/getdioinfo', (req, res, n) => {
if (!config.permission.dio) return n('ERR9000');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let q = "select * from ??.??";
let pdi = tool.promiseQuery(res, q, [config.db.db1, 'dilist'], 'di');
let pdo = tool.promiseQuery(res, q, [config.db.db1, 'dolist'], 'do');
res.api_res = {
record:[],
rt: {
di: [],
do: []
}
};
Promise.all([pdi,pdo])
.then(r => {
for(let i in r){
if(r[i].key == 'di') res.api_res.rt.di = r[i].data;
else if(r[i].key == 'do') res.api_res.rt.do = r[i].data;
}
return n();
})
.catch(e => n('ERR8000'));
})
.post('/setdioinfo', (req,res,n) => {
if(!config.permission.dio) return n('ERR9000');
// if(!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if(!arr.data) return n('ERR0000');
if(!arr.data.di || typeof (arr.data.di) != 'object' || Object.keys(arr.data.di).length == 0) return n('ERR0005');
if(!arr.data.do || typeof (arr.data.do) != 'object' || Object.keys(arr.data.do).length == 0) return n('ERR0006');
res.api_res = {
record: []
}
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "update ??.?? set ?? = unix_timestamp() , ";
let sub = " ?? = case ?? ";
let dos = [];
let dis = [];
let di_logic = "";
let di_name = "";
let do_logic = "";
let do_name = "";
let where = " where ?? in (?)";
for(let i=1; i<=8; i++){
dos.push(`do${i}`);
dis.push(`di${i}`);
if(arr.data.do[`do${i}`] && 'logic' in arr.data.do[`do${i}`]){
do_logic += ` when 'do${i}' then ${arr.data.do[`do${i}`].logic == 1 ? 1 : 0} `;
}
if(arr.data.do[`do${i}`] && 'name' in arr.data.do[`do${i}`]){
do_name += ` when 'do${i}' then ${res.db.escape(arr.data.do[`do${i}`].name)} `;
}
if(arr.data.di[`di${i}`] && 'logic' in arr.data.di[`di${i}`]){
di_logic += ` when 'di${i}' then ${arr.data.di[`di${i}`].logic == 1 ? 1 : 0} `;
}
if(arr.data.di[`di${i}`] && 'name' in arr.data.di[`di${i}`]){
di_name += ` when 'di${i}' then ${res.db.escape(arr.data.di[`di${i}`].name)} `;
}
}
do_logic += " end ";
do_name += " end ";
di_logic += " end ";
di_name += " end ";
let diq = `${query} ${sub} ${di_logic} , ${sub} ${di_name} ${where}`;
let doq = `${query} ${sub} ${do_logic} , ${sub} ${do_name} ${where}`;
let pdi = tool.promiseQuery(res, diq, [config.db.db1, 'dilist', 'di_modify_date', 'dilogic', 'diid', 'diname', 'diid', 'diid', dis]);
let pdo = tool.promiseQuery(res, doq, [config.db.db1, 'dolist', 'do_modify_date', 'dologic', 'doid', 'doname', 'doid', 'doid', dos]);
Promise.all([pdi, pdo])
.then(r => n())
.catch(e => n('ERR8002'))
})
.all('*', rt.send);
module.exports = router;

35
route/api/index.js Normal file
View File

@ -0,0 +1,35 @@
const express = require('express');
const router = express.Router();
const errMng = require('../../includes/errorManager');
router
.get('/', (req, res) => {
res.send({ name: 'WebIO API System' });
})
.use('/system', require('./system.js'))
.use('/log', require('./log.js'))
.use('/leone', require('./leone.js'))
.use('/iogroup', require('./iogroup.js'))
.use('/iocmd', require('./iocmd.js'))
.use('/schedule', require('./schedule.js'))
.use('/dio', require('./dio.js'))
.use('/link', require('./link.js'))
.use('/modbus', require('./modbus.js'));
// api error handler
router.use((err, req, res, n) => {
if ('db' in res && typeof res.db == 'object' && 'close' in res.db && typeof res.db.close == 'function') res.db.close();
let lngs = req.headers['accept-language'].split(',');
let lng = null;
if (lngs.length > 0) {
lng = lngs[0].substring(0, 2);
}
res.send({
errorCode: (typeof err != 'string' ? err.toString() : err),
message: errMng(err, lng),
status: 0
});
})
module.exports = router;

188
route/api/iocmd.js Normal file
View File

@ -0,0 +1,188 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
const exec = require('child_process').exec;
const execSync = require('child_process').execSync;
const so = require('../../includes/storeObject');
router
.get('/', (req, res) => {
res.send({ name: 'WebIO IOCmd API' });
})
.post('/iocmd', (req, res, n) => {
if (!config.permission.iocmd) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.cmd) return n('ERR0030');
if (!arr.data.devs) return n('ERR0029');
let cmds = arr.data.cmd.split(' ');
if (cmds.length != 2) return n('ERR0030');
if (cmds[0] == 2 && !(cmds[1] > 16 && cmds[1] < 30)) return n('ERR0031');
let cmd = `echo '${cmds[0]} ${cmds[1]} ${arr.data.devs}' > ${config.cmdpath.iocmd}`;
exec(cmd, (err, sout, serr) => {
let data = {};
data.record = [];
res.api_res = data;
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let dos = [];
let les = [];
let ios = [];
let devs = arr.data.devs.split(',');
let regex_do = /^do([0-9]+)$/;
let regex_le = /^le([0-9]+)$/;
let regex_io = /^iogroup([0-9]+)$/;
for (let i in devs) {
let s = devs[i];
if (!s.trim()) continue;
if (regex_do.test(s)) {
dos.push(s.replace(regex_do, '$1'));
} else if (regex_le.test(s)) {
les.push(s.replace(regex_le, '$1'));
} else if (regex_io.test(s)) {
ios.push(s.replace(regex_io, '$1'));
}
}
let obj = so.get(req.headers['x-auth-token']);
let u = '';
if (obj != null && 'user' in obj && 'account' in obj.user) u = obj.user.account;
let pros = [];
if (dos.length > 0) {
let query = "select * from ??.?? where `douid` in (?)";
let param = [config.db.db1, 'dolist', dos];
pros.push(tool.promiseQuery(res, query, param, 'do'));
}
if (les.length > 0) {
let query = "select * from ??.?? where `leonelistuid` in (?)";
let param = [config.db.db1, 'leonelist', les];
pros.push(tool.promiseQuery(res, query, param, 'leone'));
}
if (ios.length > 0) {
let query = "select * from ??.?? where `iogroupuid` in (?)";
let param = [config.db.db1, 'iogroup', ios];
pros.push(tool.promiseQuery(res, query, param, 'iogroup'));
}
Promise.all(pros)
.then(r => {
let c = r.length;
let q = "insert into ??.?? (`eventtag`, `iolabel`, `ioname`, `iosetting1`, `iosetting2`, `iosetting3`, `ioevent`, `ioeventtst`) values ";
let p = [config.db.db2, 'jciocert'];
// for (let i in r) {
! function runLog(json) {
if (!json) return;
if (json.key == 'do') {
let d = json.data;
let qs = [];
for (let j in d) {
let doStat = execSync(`dotchk ${d[j].douid}`) == 1 ? 'HIGH' : 'LOW';
let str = `('WEB', 'DO${d[j].douid}', '${d[j].doname}', '${d[j].dologic}', '${doStat}', '${u}', '${doStat}', unix_timestamp())`;
qs.push(str);
}
tool.promiseQuery(res, q + qs.join(','), p)
.then(r => {
c--;
})
.catch(err => {
c--;
});
runLog(r.pop());
} else if (json.key == 'leone') {
let d = json.data;
let qs = [];
let mm = tool.getMode(req);
let act = tool.getCmd(req);
tool.getLeoneRT((rts) => {
for (let j in d) {
let st = '';
for (var k in rts) {
if (rts[k].ip == d[j].leoneip) {
for (let o in mm) {
if (rts[k].mode == mm[o].mode) {
st = mm[o].name;
break;
}
}
break;
}
}
let cn = '';
for (let k in act) {
if (act[k].cmd == `${cmds[0]} ${cmds[1]}`) {
cn = act[k].name;
break;
} else if (act[k].cmd == cmds[0]) {
cn = act[k].name;
break;
}
}
let str = `('WEB', 'LeOne', '${d[j].leonename}', '-', '${st}', '${u}', '${cn}', unix_timestamp())`;
qs.push(str);
}
tool.promiseQuery(res, q + qs.join(','), p)
.then(r => {
c--;
})
.catch(err => {
c--;
});
runLog(r.pop());
});
} else if (json.key == 'iogroup') {
let d = json.data;
let qs = [];
let act = tool.getCmd(req);
for (let j in d) {
let cn = '';
for (let k in act) {
if (act[k].cmd == `${cmds[0]} ${cmds[1]}`) {
cn = act[k].name;
break;
} else if (act[k].cmd == cmds[0]) {
cn = act[k].name;
break;
}
}
let str = `('WEB', 'IOGroup', '${d[j].iogroupname}', '-', '-', '${u}', '${cn}', unix_timestamp())`;
qs.push(str);
}
tool.promiseQuery(res, q + qs.join(','), p)
.then(r => {
c--;
})
.catch(err => {
c--;
});
runLog(r.pop());
}
}(r.pop())
! function chkFin() {
if (c <= 0) return n();
setTimeout(chkFin, 1000);
}()
})
.catch(err => {
return n();
});
});
})
.all('*', rt.send);
module.exports = router;

183
route/api/iogroup.js Normal file
View File

@ -0,0 +1,183 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
router
.get('/', (req, res) => {
res.send({ name: 'WebIO IOGroup API' });
})
.post(['/getiogrouplist', '/getiogroup'], (req, res, n) => {
if (!config.permission.iogroup) return n('ERR9000');
let s = false;
let arr = req.body;
if (req.url == '/getiogroup') {
s = true;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
}
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "select * from ??.?? ";
let order = " order by `iogroupuid` desc ";
let param = [config.db.db1, 'iogroup'];
if (s) {
query += " where `iogroupuid` = ? ";
param.push(arr.data.id);
}
res.db.query(query + order, param, (err, row) => {
if (err) return n('ERR8000');
let dos = [];
let les = [];
for (var i in row) {
let str = row[i].iogroupid;
let tmp = str.split(',');
if (tmp.length == 0) continue;
for (var j in tmp) {
if (/^do([0-9]+)$/.test(tmp[j])) {
let n = tmp[j].replace(/^do([0-9]+)$/, '$1');
dos.push(n);
} else if (/^le([0-9]+)$/.test(tmp[j])) {
let n = tmp[j].replace(/^le([0-9]+)$/, '$1');
les.push(n);
}
}
}
let doarr = [];
let leonearr = [];
let pros = [];
if (dos.length > 0) {
let query = "select * from ??.?? where `douid` in (?)";
let param = [config.db.db1, 'dolist', dos];
pros.push(tool.promiseQuery(res, query, param, 'do'));
}
if (les.length > 0) {
let query = "select * from ??.?? where `leonelistuid` in (?)";
let param = [config.db.db1, 'leonelist', les];
pros.push(tool.promiseQuery(res, query, param, 'leone'));
}
Promise.all(pros)
.then(r => {
for (var i in r) {
if (r[i].key == 'do') doarr = r[i].data;
else if (r[i].key == 'leone') leonearr = r[i].data;
}
let data = {};
data.record = tool.checkArray(row);
data.rt = {};
data.rt.do = tool.checkArray(doarr);
data.rt.leone = tool.checkArray(leonearr);
res.api_res = data;
return n();
})
.catch(err => {
if (err) return n('ERR8000');
});
});
})
.post('/addiogroup', (req, res, n) => {
if (!config.permission.iogroup) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.name) return n('ERR0026');
if (!arr.data.devs) return n('ERR0029');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "insert into ??.?? (`iogroupname`,`iogroupid`,`iogroup_add_date`) values (?, ?, unix_timestamp())";
let param = [config.db.db1, 'iogroup', arr.data.name, arr.data.devs];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8001');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.post('/editiogroup', (req, res, n) => {
if (!config.permission.iogroup) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
if (!arr.data.name) return n('ERR0026');
if (!arr.data.devs) return n('ERR0029');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "update ??.?? set \
`iogroupname` = ?, \
`iogroupid` = ?, \
`iogroup_modify_date` = unix_timestamp() \
where \
`iogroupuid` = ?";
let param = [config.db.db1, 'iogroup', arr.data.name, arr.data.devs, arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.post('/deliogroup', (req, res, n) => {
if (!config.permission.iogroup) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "delete from ??.?? where `iogroupuid` = ? ";
let param = [config.db.db1, 'iogroup', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8003');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.all('*', rt.send);
module.exports = router;

284
route/api/leone.js Normal file
View File

@ -0,0 +1,284 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const fs = require('fs');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
const exec = require('child_process').exec;
router
.get('/', (req, res) => {
res.send({name: 'WebIO Leone API'});
})
.post('/scanleone', (req, res, n) => {
if (!config.permission.leone) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.ip || !/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/.test(arr.data.ip)) return n('ERR0010');
if (!arr.data.password) return n('ERR0017');
let ips = arr.data.ip.trim().split('.');
for (var i in ips) {
if (ips[i] < 0 || ips[i] > 255) return n('ERR0025');
}
if (fs.existsSync(config.cmdpath.scanleone)) {
fs.unlinkSync(config.cmdpath.scanleone);
}
if (fs.existsSync(config.cmdpath.scanleone_end)) {
fs.unlinkSync(config.cmdpath.scanleone_end);
}
let cmd = `echo '${arr.data.ip} ${arr.data.password}' > ${config.cmdpath.scanleone}`;
exec(cmd, (err, sout, serr) => {
if (err) return n('ERR7000');
! function chkEnd() {
fs.exists(config.cmdpath.scanleone_end, exists => {
if (exists) {
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "select `leonename`, `leoneip`, `leonelistuid` from ??.?? where `temporary` = '1' ";
let param = [config.db.db1, 'leonelist'];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
let data = {};
data.record = tool.checkArray(row);
res.api_res = data;
return n();
});
}else{
setTimeout(chkEnd, 1000);
}
});
}()
});
})
.post(['/getleonelist', '/getleone'], (req, res, n) => {
if (!config.permission.leone) return n('ERR9000');
let s = false;
let arr = req.body;
if (req.url == '/getleone') {
s = true;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
}
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "select * from ??.?? where `temporary` = '0' ";
let param = [config.db.db1, 'leonelist'];
if (s) {
query += " and `leonelistuid` = ? ";
param.push(arr.data.id);
}
let order = " order by `leonelistuid` desc ";
res.db.query(query + order, param, (err, row) => {
if (err) return n('ERR8000');
tool.getLeoneRT(rts => {
let data = {};
data.record = tool.checkArray(row);
data.rt = {};
data.rt.status = tool.checkArray(rts);
res.api_res = data;
return n();
});
});
})
.post('/addleone', (req, res, n) => {
let arr = req.body;
if (!config.permission.leone) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
if (!arr.data) return n('ERR0000');
if (!arr.data.name) return n('ERR0026');
if (!arr.data.ip) return n('ERR0010');
if (!arr.data.password) return n('ERR0017');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "select count(*) as num from ??.?? where `temporary` = '0' ";
let param = [config.db.db1, 'leonelist'];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
if (row[0].num >= config.leone_limit) return n('ERR0048');
let query = "select count(*) from ??.?? where `leoneip` = ? ";
let p = [...param, arr.data.ip];
res.db.query(query, p, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
if (row[0].num > 0) return n('ERR0027');
let query = "insert into ??.?? (`leoneip`,`leonename`,`leonepassword`,`leone_add_date`,`leone_modify_date`) values (?,?,?,unix_timestamp(),unix_timestamp())";
let p = [...param, arr.data.ip, arr.data.name, arr.data.password];
res.db.query(query, p, (err, row) => {
if (err) return n('ERR8001');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
});
})
})
.post('/delleone', (req, res, n) => {
if (!config.permission.leone) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "delete from ??.?? where `leonelistuid` = ? ";
let param = [config.db.db1, 'leonelist', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8003');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.post('/editleone', (req, res, n) => {
if (!config.permission.leone) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
if (!arr.data.name) return n("ERR0026");
if (!arr.data.password) return n('ERR0017');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "update ??.?? set \
`leonename` = ?,\
`leonepassword` = ?,\
`leone_modify_date` = unix_timestamp() \
where \
`leonelistuid` = ? ";
let param = [config.db.db1, 'leonelist', arr.data.name, arr.data.password, arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
let data = {};
data.record = [];
res.api_res = data;
let query = "select * from ??.?? where `leonelistuid` = ? ";
let param = [config.db.db1, 'leonelist', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n();
if (row.length == 0) return n();
let { leoneip, leonename, leonepassword } = row[0];
let cmd = `sledn ${leonepassword} ${leoneip} "${leonename}"`;
exec(cmd, (err, sout, serr) => {
return n();
});
});
});
})
.post('/addscanleone', (req, res, n) => {
if (!config.permission.leone) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id || !Array.isArray(arr.data.id)) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let ids = [];
for (var i in arr.data.id) {
let t = arr.data.id[i];
if (typeof t == 'string' && t.length == 0) continue;
ids.push(t);
}
if (ids.length == 0) {
let data = {};
data.record = [];
res.api_res = data;
return n();
}
let query = "select count(*) as num from ??.?? where `temporary` = '0' or `leonelistuid` in (?)";
let param = [config.db.db1, 'leonelist', ids];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
if (row[0].num >= config.leone_limit) return n('ERR0048');
let query = "update ??.?? set `temporary` = '0', `leone_modify_date` = unix_timestamp() where `leonelistuid` in (?)";
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
});
})
.post('/clearscanleone', (req, res, n) => {
if (!config.permission.leone) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "delete from ??.?? where `temporary` = '1'";
let param = [config.db.db1, 'leonelist'];
res.db.query(query, param, (err, row) => {
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.all('*', rt.send);
module.exports = router;

287
route/api/link.js Normal file
View File

@ -0,0 +1,287 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
router
.get('/', (req, res) => {
res.send({ name: 'WebIO Link API' });
})
.post('/getlinklist', (req, res, n) => {
if (!config.permission.link) return n('ERR9000');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db8;
res.db.connect();
let query = "select * from ??.?? \
where \
`jcioclntuid` not in ( \
select n2.jcioclntuid \
from ??.?? n1 \
left join \
??.?? n2\
on \
n1.`lnlcid1` = concat('ln', n2.`jcioclntuid`) \
or n1.`lnlcid2` = concat('ln', n2.`jcioclntuid`) \
where n2.`jcioclntuid` is not null \
)";
let param = [config.db.db8, 'jcioclnt', config.db.db8, 'jcioclnt', config.db.db8, 'jcioclnt'];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row)
};
return n();
});
})
.post('/getlink', (req, res, n) => {
if (!config.permission.link) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db8;
res.db.connect();
res.api_res = {
record: [{ root: arr.data.id }],
rt: {
ln: [],
lc: []
}
}
let query = "select ??.??(?) as ids";
let param = [config.db.db8, 'getLink', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
let ids = row[0].ids;
let id = ids.split(',');
id.splice(0, 1);
let ida = id.filter(t => {
if (t != '') return t;
});
let pros = [];
let lnq = "select * from ??.?? where `jcioclntuid` in (?)";
let lnp = [config.db.db8, 'jcioclnt', ida];
pros.push(tool.promiseQuery(res, lnq, lnp, 'ln'));
let lcq = "select c.* from ??.?? n \
left join ??.?? c \
on \
c.`jcioclctuid` = substr(n.`lnlcid1`, 3) \
or c.`jcioclctuid` = substr(n.`lnlcid2`, 3) \
where \
n.`jcioclntuid` in ( ? ) \
and ( \
substr(n.`lnlcid1`, 1,2) = 'lc' \
or \
substr(n.`lnlcid2`, 2,2) = 'lc' \
) \
and c.`jcioclctuid` is not null \
group by c.`jcioclctuid`";
let lcp = [config.db.db8, 'jcioclnt', config.db.db7, 'jcioclct', ida];
pros.push(tool.promiseQuery(res, lcq, lcp, 'lc'));
Promise.all(pros)
.then(r => {
for (let i in r) {
if (r[i].key == 'lc') res.api_res.rt.lc = r[i].data;
if (r[i].key == 'ln') res.api_res.rt.ln = r[i].data;
}
return n();
})
.catch(err => n('ERR8000'));
});
})
.post('/addlink', (req, res, n) => {
if (!config.permission.link) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.link || Object.keys(arr.data.link).length == 0) return n('ERR0049');
if (!('active' in arr.data)) return n('ERR0032');
if (!arr.data.action) return n('ERR0030');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db8;
res.db.connect();
! function runLoop(unit, cb) {
if (typeof unit != 'object' || Object.keys(unit).length == 0) return cb(0);
if (unit.type == 'ln') {
let p1 = new Promise((resolve, reject) => {
runLoop(unit.id1, id => {
resolve({ id, key: 'id1' });
});
});
let p2 = new Promise((resolve, reject) => {
runLoop(unit.id2, id => {
resolve({ id, key: 'id2' });
});
});
Promise.all([p1, p2])
.then(r => {
let id1 = 0;
let id2 = 0;
for (let i in r) {
if (r[i].key == 'id1') id1 = r[i].id;
if (r[i].key == 'id2') id2 = r[i].id;
}
let q = "insert into ??.?? \
(`lnname`, `lnlcid1`, `lnlcid2`, `lnlcop`, `lnactive`, `lnaddtst`, `lnmodtst`) values (?,?,?,?,'1',unix_timestamp(),unix_timestamp())";
let p = [config.db.db8, 'jcioclnt', unit.name, id1, id2, unit.op];
res.db.query(q, p, (err, row) => {
if (err) return cb(0);
return cb(`ln${row.insertId}`);
});
})
.catch(err => {
return cb(0);
});
} else if (unit.type == 'lc') {
let q = "insert into ??.?? \
(`lcname`, `lcioid`, `lciovalue`, `lcioop`, `lcactive`, `lcaddtst`, `lcmodtst`) values ('-', ?, ?, ?, '1', unix_timestamp(), unix_timestamp())";
let p = [config.db.db7, 'jcioclct', unit.id, unit.value, unit.op];
res.db.query(q, p, (err, row) => {
if (err) return cb(0);
return cb(`lc${row.insertId}`);
});
} else {
return cb(0);
}
}(arr.data.link, (id) => {
if (id == 0) return n('ERR8001');
if (typeof id != 'string') id = id.toString();
let query = "update ??.?? set \
`lnactive` = ?, \
`lnaction` = ? \
where \
`jcioclntuid` = ?";
let param = [config.db.db8, 'jcioclnt', (arr.data.active == 1 ? 1 : 0), arr.data.action, id.substr(2)];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
res.api_res = {
record: []
};
return n();
});
});
})
.post('/swlinkactive', (req, res, n) => {
if (!config.permission.link) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db8;
res.db.connect();
let query = "update ??.?? set \
`lnactive` = (case when `lnactive` = 1 then 0 else 1 end), \
`lnmodtst` = unix_timestamp() \
where \
`jcioclntuid` = ?";
let param = [config.db.db8, 'jcioclnt', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
res.api_res = {
record: []
};
return n();
});
})
.post('/dellink', (req, res, n) => {
if (!config.permission.link) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db8;
res.db.connect();
let query = "select ??.??(?) as ids";
let param = [config.db.db8, 'getLink', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
let ids = row[0].ids;
let id = ids.split(',');
id.splice(0, 1);
let ida = id.filter(t => {
if (t != '') return t;
});
let query = "delete c,n from ??.?? n \
left join ??.?? c \
on \
( \
c.`jcioclctuid` = substr(n.`lnlcid1`, 3) \
or c.`jcioclctuid` = substr(n.`lnlcid2`, 3)\
) \
and ( \
substr(n.`lnlcid1`, 1,2) = 'lc' \
or substr(n.`lnlcid2` ,1,2) = 'lc'\
) \
where \
n.`jcioclntuid` in (?)";
let param = [config.db.db8, 'jcioclnt', config.db.db7, 'jcioclct', ida];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8003');
res.api_res = {
record: []
};
return n();
});
});
})
.all('*', rt.send);
module.exports = router;

46
route/api/log.js Normal file
View File

@ -0,0 +1,46 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
router
.get('/', (req, res) => {
res.send({ name: 'WebIO Log API' })
})
.post('/getlog', (req, res, n) => {
if(!config.permission.log) return n('ERR9000');
let arr = req.body;
let p = arr.data && arr.data.p && isFinite(arr.data.p) && arr.data.p > 0 ? arr.data.p : 1;
let per = arr.data && arr.data.perpage && isFinite(arr.data.perpage) && arr.data.perpage > 0 ? arr.data.perpage : config.perpage;
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db2;
res.db.connect();
let query = "select count(*) as num from ??.??";
let param = [config.db.db2, 'jciocert'];
res.db.query(query, param, (err, row) => {
if(err || row.length == 0) return n('ERR0023');
let page = res.db.recordPage(row[0].num, p, per);
let query = "select * from ??.?? order by `ioeventtst` desc ";
let limit = ` limit ${page.rec_start} , ${per} `;
res.db.query(query + limit, param, (err, row) => {
if(err) return n('ERR8000');
let data = {};
data.page = page;
data.record = tool.checkArray(row);
res.api_res = data;
return n();
});
})
})
.all('*', rt.send);
module.exports = router;

588
route/api/modbus.js Normal file
View File

@ -0,0 +1,588 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const fs = require('fs');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
const exec = require('child_process').exec;
const so = require('../../includes/storeObject');
const crypt = require('../../libs/crypto');
router
.get('/', (req, res) => {
res.send({ name: 'WebIO Modbus API' });
})
.post('/getmodbuslist', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select * from ??.?? order by `uid` desc";
let param = [config.db.db5, 'device'];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row)
};
return n();
})
})
.post('/getmodbus', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
// if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select * from ??.?? where `uid` = ?";
let param = [config.db.db5, 'device', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row),
rt: {
iolist: [],
aioset: []
}
};
let pros = [];
let iq = "select * from ??.?? where `devuid` = ?";
let ip = [config.db.db5, 'iolist', row[0].uid];
pros.push(tool.promiseQuery(res, iq, ip, 'iolist'));
let aq = "select a.* from ??.?? a\
left join ??.?? i\
on \
a.`iouid`= i.`uid` \
where \
i.`devuid` = ?";
let ap = [config.db.db5, 'aioset', config.db.db5, 'iolist', row[0].uid];
pros.push(tool.promiseQuery(res, aq, ap, 'aioset'));
Promise.all(pros)
.then(r => {
for (let i in r) {
if (r[i].key == 'iolist') res.api_res.rt.iolist = r[i].data;
if (r[i].key == 'aioset') res.api_res.rt.aioset = r[i].data;
}
return n();
})
.then(err => n('ERR8000'));
});
})
.post('/getporttype', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select * from ??.?? order by `uid` ";
let param = [config.db.db5, 'porttype'];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row)
};
return n();
});
})
.post('/addmodbus', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.name) return n('ERR0026');
if (!('node' in arr.data)) return n('ERR0038');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let u = '';
let obj = so.get(req.headers['x-auth-token']);
if (obj != null && 'user' in obj && 'account' in obj.user) u = obj.user.account;
let query = "insert into ??.?? (`name`, `node`, `adduser`, `moduser`, `ctime`, `mtime`) values (?,?,?,?, unix_timestamp(), unix_timestamp())";
let param = [config.db.db5, 'device', arr.data.name, arr.data.node, u, u];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8001');
res.api_res = {
record: []
};
return n();
});
})
.post('/editmodbus', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
if (!arr.data.name) return n('ERR0026');
if (!('node' in arr.data) || !('original_node' in arr.data)) return n('ERR0038');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select count(*) as num from ??.?? where `node` = ? and `uid` != ?";
let param = [config.db.db5, 'device', arr.data.node, arr.data.id];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
if (row[0].num > 0) return n('ERR0047');
let u = '';
let obj = so.get(req.headers['x-auth-token']);
if (obj != null && 'user' in obj && 'account' in obj.user) u = obj.user.account;
let query = "update ??.?? set \
`name` = ?, \
`node` = ?, \
`moduser` = ?, \
`mtime` = unix_timestamp() \
where \
`uid` = ?";
let param = [config.db.db5, 'device', arr.data.name, arr.data.node, u, arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
let q = "delete from ??.?? where `node` in (?)";
let p = [config.db.db6, 'jcmbrt', [arr.data.node, arr.data.original_node]];
res.db.query(q, p, (err, row) => {
res.api_res = {
record: []
};
return n();
});
})
})
})
.post('/delmodbus', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "delete d,i,s,rt from ??.?? d \
left join ??.?? i \
on d.`uid` = i.`devuid` \
left join ??.?? s \
on i.`uid` = s.`iouid` \
left join ??.?? rt \
on d.`node` = rt.`node` \
where \
d.`uid` = ?";
let param = [config.db.db5, 'device', config.db.db5, 'iolist', config.db.db5, 'aioset', config.db.db6, 'jcmbrt', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8003');
res.api_res = {
record: []
};
return n();
});
})
.post('/getiostatus', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
if (!arr.data.type) return n('ERR0009');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select rt.* from ??.?? rt \
left join ??.?? d \
on d.`node` = rt.`node` \
left join ??.?? i \
on i.`devuid` = d.`uid` and i.`type` = ? \
where \
d.`uid` = ? \
and rt.`type` = ? \
and i.`uid` is not null ";
let param = [config.db.db6, 'jcmbrt', config.db.db5, 'device', config.db.db5, 'iolist', arr.data.type, arr.data.id, arr.data.type];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row)
};
return n();
});
})
.post('/getiolist', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select * from ??.?? where `uid` = ? ";
let param = [config.db.db5, 'iolist', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row)
};
return n();
});
})
.post('/addiolist', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
if (!arr.data.type) return n('ERR0009');
if (!arr.data.addr) return n('ERR0048');
if (!arr.data.num) return n('ERR0049');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select count(*) as c from ??.?? \
where \
`devuid` = ? \
and `type` = ? \
and `addr` = ?";
let param = [config.db.db5, 'iolist', arr.data.id, arr.data.type, arr.data.addr];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
if (row[0].c > 0) return n('ERR0054');
let query = "insert into ??.?? (`devuid`,`type`,`addr`,`num`,`ctime`,`mtime`) values (?, ?, ?, ?, unix_timestamp(), unix_timestamp())";
let param = [config.db.db5, 'iolist', arr.data.id, arr.data.type, arr.data.addr, arr.data.num];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8001');
res.api_res = {
record: []
};
return n();
});
});
})
.post('/editiolist', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
if (!arr.data.addr) return n('ERR0048');
if (!arr.data.num) return n('ERR0049');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select count(*) sa c from ??.?? i \
left join ??.?? i2 \
on i2.`type` = i.`type` \
and i2.`addr` = ? \
and i2.`devuid` = i.`devuid` \
and i2.`uid` != ? \
where \
i.`uid` = ? \
and i2.`uid` is not null ";
let param = [config.db.db5, 'iolist', config.db.db5, 'iolist', arr.data.addr, arr.data.id, arr.data.id];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
if (row[0].c > 0) return n('ERR0054');
let q = "update ??.?? set \
`addr` = ?, \
`num` = ?, \
`mtime` = unix_timestamp() \
where \
`uid` = ?";
let p = [config.db.db5, 'iolist', arr.data.addr, arr.data.num, arr.data.id];
res.db.query(q, p, (err, row) => {
if (err) return n('ERR8002');
res.api_res = {
record: []
};
return n();
});
});
})
.post('/deliolist', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "delete i, a from ??.?? i \
left join ??.?? a \
on a.`iouid` = i.`uid` \
where \
i.`uid` = ?";
let param = [config.db.db5, 'iolist', config.db.db5, 'aioset', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8003');
res.api_res = {
record: []
};
return n();
});
})
.post('/getaiosetlist', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select a.* from ??.?? a \
left join ??.?? i \
on a.`iouid` = i.`uid` \
where \
i.`uid` = ?";
let param = [config.db.db5, 'aioset', config.db.db5, 'iolist', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row)
};
return n();
});
})
.post('/getaioset', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select * from ??.?? where `uid` = ?";
let param = [config.db.db5, 'aioset', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row)
};
return n();
});
})
.post('/delaioset', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "delete from ??.?? where `uid` = ?";
let param = [config.db.db5, 'aioset', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8003');
res.api_res = {
record: []
};
return n();
});
})
.post('/addaioset', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.iouid) return n('ERR0028');
if (!arr.data.name) return n('ERR0026');
if (!('portnum' in arr.data)) return n('ERR0048');
if (!('range_min' in arr.data)) return n('ERR0050');
if (!('range_max' in arr.data)) return n('ERR0051');
if (!('scale_min' in arr.data)) return n('ERR0052');
if (!('scale_max' in arr.data)) return n('ERR0053');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select count(*) as count from ??.?? where `iouid` = ? and `portnum` = ?";
let param = [config.db.db5, 'aioset', arr.data.iouid, arr.data.portnum];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
if (row[0].count > 0) return n('ERR0054');
let q = "insert into ??.?? (`iouid`, `name`, `portnum`, `range_min`, `range_max`, `scale_min`, `scale_max`, `ctime`, `mtime`) values \
(?, ?, ?, ?, ?, ?, ?, unix_timestamp(), unix_timestamp())";
let p = [config.db.db5, 'aioset', arr.data.iouid, arr.data.name, arr.data.portnum, arr.data.range_min, arr.data.range_max, arr.data.scale_min, arr.data.scale_max];
res.db.query(q, p, (err, row) => {
if (err) return n('ERR8001');
res.api_res = {
record: []
};
return n();
});
});
})
.post('/editaioset', (req, res, n) => {
if (!config.permission.modbus) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
if (!arr.data.iouid) return n('ERR0028');
if (!arr.data.name) return n('ERR0026');
if (!arr.data.portnum) return n('ERR0048');
if (!('range_min' in arr.data)) return n('ERR0050');
if (!('range_max' in arr.data)) return n('ERR0051');
if (!('scale_min' in arr.data)) return n('ERR0052');
if (!('scale_max' in arr.data)) return n('ERR0053');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db5;
res.db.connect();
let query = "select count(*) as count from ??.?? \
where \
`iouid` = ? \
and `portnum` = ? \
and `uid` != ?";
let param = [config.db.db5, 'aioset', arr.data.iouid, arr.data.portnum, arr.data.id];
res.db.query(query, param, (err, row) => {
if (err || row.length == 0) return n('ERR8000');
if (row[0].length > 0) return n('ERR0054');
let query = "update ??.?? set \
`name` = ?, \
`portnum` = ?, \
`range_min` = ?, \
`range_max` = ?, \
`scale_min` = ?, \
`scale_max` = ?, \
`mtime` = unix_timestamp() \
where `uid` = ?";
let param = [config.db.db5, 'aioset', arr.data.name, arr.data.portnum, arr.data.range_min, arr.data.range_max, arr.data.scale_min, arr.data.scale_max, arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
res.api_res = {
record: []
};
return n();
});
});
})
.all('*', rt.send);
module.exports = router;

301
route/api/schedule.js Normal file
View File

@ -0,0 +1,301 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const fs = require('fs');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
const exec = require('child_process').exec;
const so = require('../../includes/storeObject');
const crypt = require('../../libs/crypto');
router
.get('/', (req, res) => {
res.send({ name: 'WebIO Schedule API' });
})
.post(['/getschedulelist', '/getschedule'], (req, res, n) => {
if (!config.permission.schedule) return n('ERR9000');
let s = false;
let arr = req.body;
if (req.url == '/getschedule') {
s = true;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
}
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "select * from ??.?? ";
let order = " order by `ioscheduleuid` desc ";
let param = [config.db.db1, 'ioschedulet'];
if (s) {
query += " where `ioschedulet` = ? ";
param.push(arr.data.id);
}
res.db.query(query + order, param, (err, row) => {
if (err) return n('ERR8000');
res.api_res = {
record: tool.checkArray(row),
rt: {
'do': [],
'leone': [],
'iogroup': []
}
};
let dos = [];
let les = [];
let ios = [];
let regex = {
do: /^do([0-9]+)$/,
leone: /^leone([0-9]+)$/,
iogroup: /^iogroup([0-9]+)$/
}
for (let i in row) {
let str = row[i].ioscheduleio;
let t = str.split(',');
if (t.length == 0) continue;
for (let j in t) {
if (regex.do.test(t[j])) {
dos.push(t[j].replace(regex.do, '$1'));
} else if (regex.leone.test(t[j])) {
les.push(t[j].replace(regex.leone, '$1'));
} else if (regex.iogroup.test(t[j])) {
ios.push(t[j].replace(regex.iogroup, '$1'));
}
}
}
let pros = [];
if (dos.length > 0) {
let query = "select * from ??.?? where `douid` in (?)";
let param = [config.db.db1, 'dolist', dos];
pros.push(tool.promiseQuery(res, query, param, 'do'));
}
if (les.length > 0) {
let query = "select * from ??.?? where `leonelistuid` in (?)";
let param = [config.db.db1, 'leonelist', les];
pros.push(tool.promiseQuery(res, query, param, 'leone'));
}
if (ios.length > 0) {
let query = "select * from ??.?? where `iogroupuid` in (?)";
let param = [config.db.db1, 'iogroup', ios];
pros.push(tool.promiseQuery(res, query, param, 'iogroup'));
}
Promise.all(pros)
.then(r => {
for (let i in r) {
if (r[i].key == 'do') {
res.api_res.rt.do = r[i].data;
} else if (r[i].key == 'leone') {
res.api_res.rt.leone = r[i].data;
} else if (r[i].key == 'iogroup') {
res.api_res.rt.iogroup = r[i].data;
}
}
return n();
})
.catch(err => {
return n();
})
});
})
.post('/delschedule', (req, res, n) => {
if (!config.permission.schedule) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "delete from ??.?? where `ioscheduleuid` = ? ";
let param = [config.db.db1, 'ioschedulet', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8003');
res.api_res = {
record: []
};
return n();
});
})
.post('/swschedule', (req, res, n) => {
if (!config.permission.schedule) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.id) return n('ERR0028');
// let active = arr.data.active == 1 ? 1 : 0;
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "update ??.?? set `ioscheduleactive` = (case when `ioscheduleactive` = '1' then '0' else '1' end) where `ioscheduleuid` = ? ";
let param = [config.db.db1, 'ioschedulet', arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
res.api_res = {
record: []
};
return n();
});
})
.post('/addschedule', (req, res, n) => {
if (!config.permission.schedule) return n('ERR9000');
if (!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.name) return n('ERR0026');
if (!('active' in arr.data)) return n('ERR0032');
if (!arr.data.devs) return n('ERR0029');
if (!arr.data.action) return n('ERR0030');
if (!('min' in arr.data) || !isFinite(arr.data.min) || !(arr.data.min >= 0 && arr.data.min < 60)) return n('ERR0033');
if (!('hour' in arr.data) || !isFinite(arr.data.hour) || !(arr.data.hour >= 0 && arr.data.hour < 24)) return n('ERR0034');
if (!arr.data.week) {
if (!arr.data.day || !isFinite(arr.data.day) || !(arr.data.day > 0 && arr.data.day <= 31)) return n('ERR0035');
if (!arr.data.month || !isFinite(arr.data.month) || !(arr.data.month > 0 && arr.data.month <= 12)) return n('ERR0036');
}
let tmp = arr.data.action.split(',');
if (tmp.length != 2) return n('ERR0030');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let active = arr.data.active == 1 ? 1 : 0;
let d = '';
let m = '';
let w = '';
if (arr.data.week) {
w = arr.data.week;
d = '-';
m = '-';
} else {
w = '-';
d = arr.data.day;
m = arr.data.month;
}
let query = "insert into ??.?? (\
`ioschedulename`, \
`ioscheduleactive`, \
`ioscheduleio`, \
`ioschedulecmd`, \
`ioscheduleparam1`, \
`ioscheduleparam2`, \
`ioscheduleparam3`, \
`ioscheduleparam4`, \
`ioscheduleparam5`, \
`ioschedule_add_date`) values (?,?,?,?,?,?,?,?,?,unix_timestamp())";
let param = [config.db.db1, 'ioschedulet', arr.data.name, active, arr.data.devs, arr.data.action, arr.data.min, arr.data.hour, d, m, w];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8001');
res.api_res = {
record: []
};
return n();
});
})
.post('/editschedule', (req,res,n) => {
if(!config.permission.schedule) return n('ERR9000');
if(!tool.checkPermission(req)) return n('ERR9000');
let arr = req.body;
if (!arr.data) return n('ERR0000');
if(!arr.data.id) return n('ERR0028');
if (!arr.data.name) return n('ERR0026');
if (!('active' in arr.data)) return n('ERR0032');
if (!arr.data.devs) return n('ERR0029');
if (!arr.data.action) return n('ERR0030');
if (!('min' in arr.data) || !isFinite(arr.data.min) || !(arr.data.min >= 0 && arr.data.min < 60)) return n('ERR0033');
if (!('hour' in arr.data) || !isFinite(arr.data.hour) || !(arr.data.hour >= 0 && arr.data.hour < 24)) return n('ERR0034');
if (!arr.data.week) {
if (!arr.data.day || !isFinite(arr.data.day) || !(arr.data.day > 0 && arr.data.day <= 31)) return n('ERR0035');
if (!arr.data.month || !isFinite(arr.data.month) || !(arr.data.month > 0 && arr.data.month <= 12)) return n('ERR0036');
}
let tmp = arr.data.action.split(',');
if (tmp.length != 2) return n('ERR0030');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let active = arr.data.active == 1 ? 1 : 0;
let d = '';
let m = '';
let w = '';
if (arr.data.week) {
w = arr.data.week;
d = '-';
m = '-';
} else {
w = '-';
d = arr.data.day;
m = arr.data.month;
}
let query = "update ??.?? set \
`ioschedulename` = ? , \
`ioscheduleactive` = ? , \
`ioscheduleio` = ? , \
`ioschedulecmd` = ? , \
`ioscheduleparam1` = ? , \
`ioscheduleparam2` = ? , \
`ioscheduleparam3` = ? , \
`ioscheduleparam4` = ? , \
`ioscheduleparam5` = ? , \
`ioschedule_add_date` = unix_timestamp() \
where \
`ioscheduleuid` = ?";
let param = [config.db.db1, 'ioschedulet', arr.data.name, active, arr.data.devs, arr.data.action, arr.data.min, arr.data.hour, d, m, w, arr.data.id];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8002');
res.api_res = {
record: []
};
return n();
});
})
.all('*', rt.send);
module.exports = router;

452
route/api/system.js Normal file
View File

@ -0,0 +1,452 @@
const express = require('express');
const router = express.Router();
const rt = require('../ResTool');
const config = require('../../config.json');
const fs = require('fs');
const mysql = require('../../libs/mysql_cls');
const tool = require('../../includes/apiTool');
const exec = require('child_process').exec;
const so = require('../../includes/storeObject');
const crypt = require('../../libs/crypto');
router
.get('/', (req, res, n) => {
// res.db = new mysql();
// res.db.user = config.db.user;
// res.db.password = config.db.pass;
// res.db.host = config.db.host;
// res.db.port = config.db.port;
// res.db.database = config.db.db1;
// res.db.connect();
res.send({ name: 'WebIO System API' });
})
.post('/getnetwork', (req, res, n) => {
fs.exists(config.cmdpath.sysinfo, (exists) => {
if (!exists) return n('ERR0014');
fs.readFile(config.cmdpath.sysinfo, (err, d) => {
if (err) return n('ERR0014');
let str = d.toString().split(/\n/);
let arr = {};
for (var i in str) {
if (!str[i].trim()) continue;
let t = str[i].split(' ');
if (t.langth < 2) continue;
arr[t[0]] = t[1];
}
let data = {};
data.record = [arr];
res.api_res = data;
return n();
});
});
})
.post('/updatenetwork', (req, res, n) => {
let arr = req.body;
if (!tool.checkPermission(req)) return n('ERR9000');
if (!arr.data) return n('ERR0000');
if (!arr.data.type) return n('ERR0009');
if (arr.data.type == 'manual') {
if (!arr.data.ip) return n('ERR0010');
if (!arr.data.netmask) return n('ERR0011');
if (!arr.data.gateway) return n('ERR0012');
if (!arr.data.dns) return n('ERR0013');
}
let cmd = '';
if (arr.data.type == 'manual') {
cmd = `echo "${arr.data.ip}" "${arr.data.gateway}" "${arr.data.netmask}" "${arr.data.dns}" > ${config.cmdpath.manualip}`;
} else {
cmd = `touch ${config.cmdpath.dhcpip}`;
}
if (cmd.length > 0) {
exec(cmd, (err, sout, serr) => {
let data = {};
data.record = [];
res.api_res = data;
return n();
});
}
})
.post('/gettime', (req, res, n) => {
let cmd = 'date +%s';
exec(cmd, (err, sout, serr) => {
let time = parseInt(sout);
let data = {};
data.record = [{ time }];
res.api_res = data;
return n();
});
})
.post('/updatetime', (req, res, n) => {
let arr = req.body;
if (!tool.checkPermission(req)) return n('ERR9000');
if (!arr.data) return n('ERR0000');
if (!arr.data.time || !/^[0-9]{12}$/.test(arr.data.time)) return n('ERR0015');
let cmd = `echo "${arr.data.time}" > ${config.cmdpath.settime}`;
exec(cmd, (err, sout, serr) => {
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.post('/login', (req, res, n) => {
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.account) return n('ERR0016');
if (!arr.data.password) return n('ERR0017');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "select * from ??.?? where `account` = ? and `user_password` = ?";
res.db.query(query, [config.db.db1, 'userlist', arr.data.account, arr.data.password], (err, row) => {
if (err) return n('ERR8000');
if (row.length == 0) return n('ERR0019');
delete row[0]['user_password'];
let token = '';
while (true) {
token = crypt.random(15);
if (!so.chkKey(token)) break;
}
so.set(token, { user: row[0] });
let data = {};
data.record = row;
data.rt = {}
data.rt.permission = [];
let tmp = {};
for(let i in config.permission) {
if(config.permission[i]){
tmp[i] = true;
}
}
data.rt.permission.push(tmp);
data.token = token;
res.api_res = data;
return n();
});
})
.post('/logout', (req, res, n) => {
let token = req.headers['x-auth-token'];
if (token) {
so.del(token);
}
let data = {};
data.record = [];
res.api_res = data;
return n();
})
.post(['/getuserlist', '/getuser'], (req, res, n) => {
let s = false;
let arr = req.body;
if (req.url == '/getuser') {
s = true;
if (!arr.data) return n('ERR0000');
if (!arr.data.account) return n('ERR0016');
}
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "select * from ??.??";
let param = [config.db.db1, 'userlist']
if (s) {
query += " where `account` = ?";
param.push(arr.data.account);
}
res.db.query(query, param, (err, row) => {
if (err) return n('ERR8000');
for (var i in row) {
delete row[i]['user_password'];
}
let data = {};
data.record = row;
res.api_res = data;
return n();
})
})
.post('/deluser', (req, res, n) => {
let arr = req.body;
if (!tool.checkPermission(req)) return n('ERR9000');
if (!arr.data) return n('ERR0000');
if (!arr.data.account) return n('ERR0016');
if (arr.data.account == 'admin') return n('ERR0037');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "delete from ??.?? where `account` = ?";
let param = [config.db.db1, 'userlist', arr.data.account];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR0020');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.post('/edituser', (req, res, n) => {
let arr = req.body;
if (!tool.checkPermission(req)) return n('ERR9000');
if (!arr.data) return n('ERR0000');
if (!arr.data.account) return n('ERR0016');
let w = arr.data.write_privilege && arr.data.write_privilege == '1' ? 1 : 0;
let r = arr.data.read_privilege && arr.data.read_privilege == '1' ? 1 : 0;
let pass = typeof arr.data.password == 'string' && arr.data.password.length > 0 ? arr.data.password : '';
if(arr.data.account == 'admin') {
w = 1;
r = 1;
}
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "update ??.?? set `write_privilege` = ? , `read_privilege` = ? " +
(pass.length > 0 ? " , `user_password` = ? " : "") + " where `account` = ? ";
let param = [config.db.db1, 'userlist', w.toString(), r.toString()];
if (pass.length > 0) param.push(pass);
param.push(arr.data.account);
res.db.query(query, param, (err, row) => {
if (err) return n('ERR0021');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.post('/adduser', (req, res, n) => {
let arr = req.body;
if (!tool.checkPermission(req)) return n('ERR9000');
if (!arr.data) return n('ERR0000');
if (!arr.data.account) return n('ERR0016');
if (!arr.data.password) return n('ERR0017');
let w = arr.data.write_privilege && arr.data.write_privilege == '1' ? 1 : 0;
let r = arr.data.read_privilege && arr.data.read_privilege == '1' ? 1 : 0;
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let query = "insert into ??.?? (`account`,`user_password`,`write_privilege`,`read_privilege`,`user_add_date`) values (?,?,?,?,unix_timestamp())";
let param = [config.db.db1, 'userlist', arr.data.account, arr.data.password, w.toString(), r.toString()];
res.db.query(query, param, (err, row) => {
if (err) return n('ERR0022');
let data = {};
data.record = [];
res.api_res = data;
return n();
});
})
.post('/dashboard', (req, res, n) => {
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
let data = {
record: [],
rt: {}
};
data.rt['time'] = [{
time: Date.now()
}];
res.api_res = data;
let pros = [];
pros.push(new Promise((resolve, reject) => {
fs.exists(config.cmdpath.sysinfo, exists => {
if (!exists) return resolve({ data: [], key: 'sysinfo' });
fs.readFile(config.cmdpath.sysinfo, (err, d) => {
if (err) return resolve({ data: [], key: 'sysinfo' });
let s = d.toString();
let tmp = s.split(/\n/);
for (let i in tmp) {
if (!tmp[i].trim()) continue;
let tt = tmp[i].split(' ');
if (tt.length > 1 && /^ip$/i.test(tt[0])) {
return resolve({ data: [{ ip: tt[1] }], key: 'sysinfo' });
}
}
});
});
}));
pros.push(new Promise((resolve, reject) => {
fs.exists(config.cmdpath.version, exists => {
if (!exists) return resolve({ data: [], key: 'version' });
fs.readFile(config.cmdpath.version, (err, d) => {
if (err) return resolve({ data: [], key: 'version' });
return resolve({ data: [{ version: d.toString().replace(/\n/, '') }], key: 'version' });
});
});
}));
if (config.permission.dio) {
pros.push(new Promise((resolve, reject) => {
let q = "select `diname`, `diid`, `diuid` from ??.?? ";
let p = [config.db.db1, 'dilist'];
res.db.query(q, p, (err, row) => {
if (err) return resolve({ data: [], key: 'di' });
let c = row.length;
let td = [];
! function chkdi(json) {
if (!json) return;
exec(`ditchk ${json.diid.replace(/^di([0-9]+)$/, '$1')}`, (err, sout, serr) => {
if (err) {
chkdi(row.pop());
if (!--c) return resolve({ data: td, key: 'di' });
return;
}
if (sout == 1) td.push(json);
chkdi(row.pop());
if (!--c) return resolve({ data: td, key: 'di' });
return;
});
}(row.pop());
});
}));
}
if (config.permission.leone) {
pros.push(new Promise((resolve, reject) => {
tool.getLeoneRT(rts => {
let ips = []
for (let i in rts) {
if (rts[i].mode == '9999') {
ips.push(rts[i].ip);
}
}
let q = "select * from ??.?? where `leoneip` in (?) order by `leonelistuid` desc ";
let p = [config.db.db1, 'leonelist', ips];
res.db.query(q, p, (err, row) => {
if (err) return resolve({ data: [], key: 'leone' });
return resolve({ data: row, key: 'leone' });
});
});
}));
}
Promise.all(pros)
.then(r => {
for (let i in r) {
if (r[i].key == 'di') {
data.rt.di = r[i].data;
} else if (r[i].key == 'leone') {
data.rt.leone = r[i].data;
} else if (r[i].key == 'sysinfo') {
data.rt.ip = r[i].data;
} else if (r[i].key == 'version') {
data.rt.version = r[i].data;
}
}
return n();
})
.catch(e => {
return n();
});
})
.post('/getselectlist', (req, res, n) => {
let arr = req.body;
if (!arr.data) return n('ERR0000');
if (!arr.data.type) return n('ERR0009');
res.db = new mysql();
res.db.user = config.db.user;
res.db.password = config.db.pass;
res.db.host = config.db.host;
res.db.port = config.db.port;
res.db.database = config.db.db1;
res.db.connect();
res.api_res = {
record: []
};
let pro = null;
let q, p;
switch (arr.data.type) {
case 'do':
q = "select `doname` as name, `douid` as id from ??.??";
p = [config.db.db1, 'dolist'];
pro = tool.promiseQuery(res, q, p, '');
break;
case 'di':
q = "select `diname` as name, `diuid` as id from ??.??";
p = [config.db.db1, 'dilist'];
pro = tool.promiseQuery(res, q, p, '');
break;
case 'leone':
q = "select `leonename` as name, `leonelistuid` as id from ??.??";
p = [config.db.db1, 'leonelist'];
pro = tool.promiseQuery(res, q, p, '');
break;
case 'iogroup':
q = "select `iogroupname` as name, `iogroupuid` as id from ??.??";
p = [config.db.db1, 'iogroup'];
pro = tool.promiseQuery(res, q, p, '');
break;
default:
return n();
}
pro.then(r => {
if('data' in r) {
res.api_res.record = tool.checkArray(r.data);
}
return n();
}).catch(e => {
return n();
})
})
.all('*', rt.send);
module.exports = router;

623
src/actions/index.js Normal file
View File

@ -0,0 +1,623 @@
/**
* add message to dialog queue
* @param {string} msg
* @param {function} act
*/
export const add_dialog_msg =(msg, act) => ({
type: 'dialog_addmsg',
msg: msg,
act: act || null
});
/**
* remove dialog queue first message
*/
export const remove_dialog_msg = () => ({
type: 'dialog_remove_first'
});
/**
* set i18next object
* @param {object} i18n i18next object
*/
export const set_i18n = (i18n) => ({
type: 'i18n_set_ctx',
i18n: i18n
});
/**
* Toggle Menu open/close
* @param {bool} flag switch menu open/close
*/
export const toggle_menu = (flag) => ({
type: flag ? 'ui_show_menu' : 'ui_hide_menu'
});
/**
* ToGGle Loading open/close
* @param {bool} flag
*/
export const toggle_loading = (flag) => ({
type: flag ? 'ui_show_loading' : 'ui_hide_loading'
});
/**
*
* @param {object} data
*/
export const getRequest = (data = {}) => {
let json = {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
mode:'cors'
};
let token = sessionStorage.getItem('token');
if(token) json.headers['x-auth-token'] = token;
json.body = JSON.stringify({data});
return json;
}
export const get_network_info = () => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/system/getnetwork', getRequest())
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1){
return dispatch(add_dialog_msg(json.message));
}
let j = {};
if('data' in json && 'record' in json.data && json.data.record.length > 0) j = json.data.record[0];
return dispatch(network_info(j));
});
}
const network_info = (json) => ({
type: 'network_info',
network: json
});
export const set_network_info = (dhcpMode, ip, netmask, gateway, dns) => dispatch => {
dispatch(toggle_loading(1));
let type = dhcpMode ? 'dhcp' : 'manual';
let req = getRequest();
let json = {
data:{
type,
ip,
netmask,
gateway,
dns
}
}
req.body = JSON.stringify(json);
return fetch('/api/system/updatenetwork', req)
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(get_network_info());
});
}
export const get_system_time = () => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/system/gettime', getRequest())
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1){
return dispatch(add_dialog_msg(json.message));
}
let t = '';
if('data' in json && 'record' in json.data && json.data.record.length > 0) t = json.data.record[0].time;
return dispatch(system_time(t));
});
}
const system_time = (time) => ({
type: 'system_time',
time
});
export const set_system_time = time => dispatch => {
dispatch(toggle_loading(1));
let req = getRequest({time});
return fetch('/api/system/updatetime', req)
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(get_system_time());
});
}
export const get_user_list = () => dispatch => {
dispatch(toggle_loading(1));
let req = getRequest();
return fetch('/api/system/getuserlist', req)
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(user_list(json.data.record));
});
}
const user_list = (user) => ({
type: 'user_list',
user
})
const userApi = (url, data = {}) => dispatch => {
dispatch(toggle_loading(1));
let req = getRequest(data);
return fetch(url, req)
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(get_user_list());
});
}
export const edit_user = (data) => dispatch => {
return dispatch(userApi('/api/system/edituser', data));
}
export const add_user = (data) => dispatch => {
return dispatch(userApi('/api/system/adduser', data));
}
export const del_user = (data) => dispatch => {
return dispatch(userApi('/api/system/deluser', data));
}
export const get_dio_info = () => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/dio/getdioinfo', getRequest())
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(dioinfo(json.data.rt.do || [], json.data.rt.di || []));
});
}
export const get_dio_status = () => dispatch => {
return fetch('/api/dio/getio', getRequest())
.then(response => response.json())
.then(json => {
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(diostatus(json.data.rt.do, json.data.rt.di));
});
}
export const set_do_status = (data) => dispatch => {
return fetch('/api/dio/dotrun', getRequest(data))
.then(response => response.json())
.then(json => {
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
})
}
export const set_dio_info = (data) => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/dio/setdioinfo', getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(get_dio_info());
})
}
const diostatus = (doSt, diSt) => ({
type: 'dio_status',
doSt,
diSt
})
const dioinfo = (dod, did) => ({
type: 'dio_list',
do: dod,
di: did
});
export const get_log_list = (p = 1) => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/log/getlog', getRequest({p}))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(loglist(json.data.record, json.data.page));
});
}
const loglist = (list, page) => ({
type: 'log_list',
list,
page
});
export const scan_leone = (data) => (dispatch, getState) => {
dispatch(toggle_loading(1));
dispatch(set_scanning());
return fetch('/api/leone/scanleone', getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
let {i18n} = getState();
if(json.data.record.length == 0) return dispatch(add_dialog_msg(i18n&&i18n.t? i18n.t('tip.scan_empty') : ''));
return dispatch(show_scan_leone(json.data.record));
})
}
export const clear_scan_leone = (clrRemote = false) => dispatch => {
dispatch(clear_scan());
return fetch('/api/leone/clearscanleone', getRequest())
.then(response => response.json())
.then(json => {
return ;
});
}
export const add_scan_leone = (data) => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/leone/addscanleone', getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
dispatch(clear_scan());
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(get_leone_list());
})
}
export const get_leone_list = (showLoading = true) => dispatch => {
if(showLoading) dispatch(toggle_loading(1));
return fetch('/api/leone/getleonelist', getRequest())
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(leone_list(json.data.record, json.data.rt.status));
});
}
export const del_leone = (data) => dispatch => {
dispatch(leone_api('/api/leone/delleone', data));
}
export const add_leone = data => dispatch => {
dispatch(leone_api('/api/leone/addleone', data));
}
export const edit_leone = data => dispatch => {
dispatch(leone_api('/api/leone/editleone', data));
}
const leone_api = (url, data) => dispatch => {
dispatch(toggle_loading(1));
return fetch(url, getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(get_leone_list());
});
}
const clear_scan = () => ({
type: 'leone_clear_scan'
})
const set_scanning = () => ({
type: 'leone_scanning'
})
const show_scan_leone = (list) => ({
type: 'leone_scan',
list
});
const leone_list = (list, status) => ({
type: 'leone_list',
list,
status
})
export const io_cmd = (data, cb = null) => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/iocmd/iocmd', getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
if(cb && typeof cb == 'function') return cb();
});
}
export const get_iogroup_list = () => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/iogroup/getiogrouplist', getRequest())
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(iogroup_list(json.data.record, json.data.rt.do, json.data.rt.leone));
});
}
const iogroup_list = (list, doa, leone) => ({
type: 'iogroup_list',
list,
do: doa,
leone
});
const group_api = (url, data) => dispatch => {
dispatch(toggle_loading(1));
return fetch(url, getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(get_iogroup_list());
});
}
export const del_iogroup = (data) => dispatch => {
dispatch(group_api('/api/iogroup/deliogroup', data));
}
export const add_iogroup = (data) => dispatch => {
dispatch(group_api('/api/iogroup/addiogroup', data));
}
export const edit_iogroup = (data) => dispatch => {
dispatch(group_api('/api/iogroup/editiogroup', data));
}
export const get_select_list = (data) => dispatch => {
return fetch('/api/system/getselectlist', getRequest(data))
.then(response => response.json())
.then(json => {
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(select_list(json.data.record || []));
})
}
export const clear_select_list = () => ({
type: 'clear_select_list'
})
const select_list = (list) => ({
type: 'select_list',
list
})
const schedule_api = (url, data) => dispatch => {
dispatch(toggle_loading(1));
return fetch(url, getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(get_schedule_list());
})
}
const schedule_list = (list, dos, les, ios) => ({
type: 'schedule_list',
list,
dos,
les,
ios
});
export const get_schedule_list = () => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/schedule/getschedulelist', getRequest())
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
return dispatch(schedule_list(json.data.record || [], json.data.rt.do || [], json.data.rt.leone || [], json.data.rt.iogroup || []));
})
}
export const add_schedule = (data) => dispatch => {
dispatch(schedule_api('/api/schedule/addschedule', data));
}
export const del_schedule = (data) => dispatch => {
dispatch(schedule_api('/api/schedule/delschedule', data));
}
export const edit_schedule = (data) => dispatch => {
dispatch(schedule_api('/api/schedule/editschedule', data));
}
export const sw_schedule = (data) => dispatch => {
dispatch(schedule_api('/api/schedule/swschedule', data));
}
export const clear_userlist = () => ({
type: 'clear_userlist'
});
export const clear_dio = () => ({
type: 'clear_dio'
});
export const clear_leone =() => ({
type: 'clear_leone'
});
export const clear_log = () => ({
type: 'clear_log'
});
export const clear_iogroup = () => ({
type: 'clear_iogroup'
});
export const clear_schedule = () => ({
type: 'clear_schedule'
});
export const get_modbus_list = () => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/modbus/getmodbuslist', getRequest())
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1 ) return dispatch(add_dialog_msg(json.message));
dispatch(modbus_list(json.data.record || []));
});
}
const modbus_list = list => ({
type: 'modbus_list',
list
});
export const clear_modbus = () => ({
type: 'clear_modbus'
})
const modbus_api = (url, data) => dispatch => {
dispatch(toggle_loading(1));
return fetch(url, getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
dispatch(get_modbus_list());
})
}
export const del_modbus = (data) => dispatch => {
dispatch(modbus_api('/api/modbus/delmodbus', data));
}
export const add_modbus = data => dispatch => {
dispatch(modbus_api('/api/modbus/addmodbus', data));
}
export const edit_modbus = data => dispatch => {
dispatch(modbus_api('/api/modbus/editmodbus', data));
}
export const get_mb_data = data => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/modbus/getmodbus', getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
dispatch(mb_data(data.id, json.data.rt.iolist || [], json.data.rt.aioset || []));
})
}
const mb_data = (id, iolist, aioset) => ({
type: 'mb_data',
id,
iolist,
aioset
})
export const get_mb_iostatus = data => dispatch => {
return fetch('/api/modbus/getiostatus', getRequest(data))
.then(response => response.json())
.then(json => {
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
dispatch(mb_iostatus(data.id, data.type, json.data.record || []));
})
}
const mb_iostatus = (id, iotype, list) => ({
type: 'mb_iostatus',
id,
iotype,
list
});
const mb_iolist_api = (url, data) => dispatch => {
dispatch(toggle_loading(1))
return fetch(url, getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
dispatch(get_mb_data({id: data.devuid}));
});
}
const mb_aio_api = (url, data) => dispatch => {
dispatch(toggle_loading(1))
return fetch(url, getRequest(data))
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
dispatch(get_mb_data({id: data.devuid}));
});
}
export const edit_mb_iolist = data => dispatch => {
return dispatch(mb_iolist_api('/api/modbus/editiolist', data));
}
export const add_mb_iolist = data => dispatch => {
return dispatch(mb_iolist_api('/api/modbus/addiolist', data));
}
export const del_mb_iolist = data => dispatch => {
return dispatch(mb_iolist_api('/api/modbus/deliolist', data));
}
export const add_mb_aio = data => dispatch => {
return dispatch(mb_aio_api('/api/modbus/addaioset', data));
}
export const edit_mb_aio = data => dispatch => {
return dispatch(mb_aio_api('/api/modbus/editaioset', data));
}
export const del_mb_aio = data => dispatch => {
return dispatch(mb_aio_api('/api/modbus/delaioset', data));
}
export const get_link_list = () => dispatch => {
dispatch(toggle_loading(1));
return fetch('/api/link/getlinklist', getRequest())
.then(response => response.json())
.then(json => {
dispatch(toggle_loading(0));
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
dispatch(link_list(json.data.record || []))
})
}
const link_list = (list) => ({
type: 'link_list',
list
})
export const clear_link = () => ({
type: 'clear_link'
})
const link_api = (url, data) => dispatch => {
return fetch(url, getRequest(data))
.then(response => response.json())
.then(json => {
if(json.status != 1) return dispatch(add_dialog_msg(json.message));
dispatch(get_link_list());
})
}
export const add_link = (data) => dispatch => {
dispatch(link_api('/api/link/addlink', data));
}
export const del_link = (data) => dispatch => {
dispatch(link_api('/api/link/dellink', data));
}
export const sw_link_active = (data) => dispatch => {
dispatch(link_api('/api/link/swlinkactive', data));
}

66
src/admin.js Normal file
View File

@ -0,0 +1,66 @@
import React from 'react';
import ReactDOM from 'react-dom';
import {Router, Route, browserHistory, IndexRoute} from 'react-router';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware, compose} from 'redux';
import reducers from './reducers';
import routes from './routes';
import thunk from 'redux-thunk';
import {set_i18n} from './actions';
import i18next from 'i18next';
const middleware = [thunk];
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extensions options like name, actionsBlacklist, actionsCreators, serialize...
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(...middleware),
// other store enhancers if any
);
let {NODE_ENV} = process.env;
const store = createStore(reducers, NODE_ENV && NODE_ENV == 'production' ? applyMiddleware(...middleware) : enhancer );
// store.subscribe(() => {
// console.log(store.getState().dialog);
// });
class PageRoot extends React.Component {
componentDidMount(){
let lang = navigator
.language
.substring(0, 2);
fetch(`/locales/${lang}.json`).then(response => {
if (response.status == 200)
return response.json();
return {}
}).then(json => {
i18next.init({
lng: lang,
resources: json
}, () => {
store.dispatch(set_i18n(i18next));
})
})
}
render() {
return (
<Provider store={store}>
<Router history={browserHistory} routes={routes}/>
</Provider>
)
}
}
ReactDOM.render(
<PageRoot />,
document.getElementById('app')
);

View File

@ -0,0 +1,123 @@
import React from 'react';
import {Modal, Grid, Divider} from 'semantic-ui-react';
function randomColor() {
var colors = [
'red',
'orange',
'yellow',
'olive',
'green',
'teal',
'blue',
'violet',
'purple',
'pink',
'brown',
'grey'
];
colors.sort(function () {
return 0.5 - Math.random();
});
return colors.shift();
}
function ParseTree(props) {
let {root, ln, lc} = props;
let ops = {
"0": "等於",
"1": "大於",
"2": "小於",
"3": "大於等於",
"4": "小於等於",
"5": "不等於",
"8": "AND",
"9": "OR"
};
function GenLN(props) {
let {data, isRoot} = props;
return (
<Grid.Column color={randomColor()} width={isRoot
? 16
: 8}>
<div>{`ID:${data.jcioclntuid || ''} / Name:${data.lnname || ''} / Action:${data.lnaction || ''}`}</div>
<Divider horizontal={true}>{ops[data.lnlcop]}</Divider>
<GenNode ids={[data.lnlcid1, data.lnlcid2]}/>
</Grid.Column>
)
}
function GenLC(props) {
let {data} = props;
return (
<Grid.Column color={randomColor()} width={8}>
<div>{`ID:${data.jcioclctuid || ''} / Name:${data.lcname || ''} / Action:${data.lcaction || ''}`}</div>
<hr/>
<div>{`${data.lcioid} ${ops[data.lcioop]} ${data.lciovalue}`}</div>
</Grid.Column>
)
}
function GenNode(props) {
let {ids} = props;
let isRoot = ids.length == 1 && ids[0].substr(2) == root
? true
: false;
let arr = [];
for (let i in ids) {
let id = ids[i];
if (/^ln/.test(id)) {
let idn = id.match(/^ln(\d+)$/);
if (!idn || idn.length < 1)
continue;
let num = idn[1];
for (let j in ln) {
if (ln[j].jcioclntuid == num) {
arr.push(<GenLN key={`ln${ln[j].kcioclntuid}`} data={ln[j]} isRoot={isRoot}/>)
}
}
} else if (/^lc/.test(id)) {
let idn = id.match(/^lc(\d+)$/);
if (!idn || idn.length < 1)
continue;
let num = idn[1];
for (let j in lc) {
if (lc[j].jcioclctuid == num) {
arr.push(<GenLC key={`lc${lc[j].jcioclctuid}`} data={lc[j]}/>)
}
}
}
}
return (
<Grid>
{arr.map(t => t)
}
</Grid>
)
}
return <GenNode ids={[`ln${root}`]}/>
}
const LinkInfo = ({
i18n,
open,
root,
ln,
lc,
onClose
}) => {
return (
<Modal size="fullscreen" open={open} onClose={() => {
onClose()
}}>
<Modal.Content>
<ParseTree root={root} ln={ln} lc={lc}/>
</Modal.Content>
</Modal>
)
}
export default LinkInfo;

View File

@ -0,0 +1,22 @@
import React from 'react';
import {Table, Button} from 'semantic-ui-react';
const ListItem = ({i18n, data, swActive, delItem, showInfo}) => {
return (
<Table.Row>
<Table.Cell>
<Button type="button" size="tiny" basic content="Delete" onClick={()=>{delItem(data.jcioclntuid || '')}}/>
<Button type="button" size="tiny" basic content={data.lnactive == 1 ? 'Disable' : 'Enable'} onClick={()=>{swActive(data.jcioclntuid || '')}} />
</Table.Cell>
<Table.Cell>{data.jcioclntuid || ''}</Table.Cell>
<Table.Cell>{data.lnname || ''}</Table.Cell>
<Table.Cell>{data.lnactive == 1 ? '啟用' : '停用'}</Table.Cell>
<Table.Cell>
<Button type="button" size="tiny" basic content="顯示資訊" onClick={()=>{showInfo(data.jcioclntuid)}} />
</Table.Cell>
</Table.Row>
)
}
export default ListItem ;

View File

@ -0,0 +1,100 @@
import React from 'react';
import {Container, Segment, Table, Button} from 'semantic-ui-react';
import {getRequest} from '../../../actions';
import {Link} from 'react-router';
import ListItem from './ListItem';
import LinkInfoModal from './LinkInfoModal';
const defState = {
infoModal: {
open: false,
ln: [],
lc: [],
root: ''
}
}
class ActionLinkPage extends React.Component {
state = {
...defState
}
componentDidMount(){
this.props.getList();
this.props.router.setRouteLeaveHook(this.props.route, () => {
this.props.clearList();
})
}
swLinkActive = (id) => {
if(!id) return ;
this.props.swLink({id});
}
delLink = (id) => {
if(!id) return ;
this.props.delLink({id});
}
openInfoModal = (id) => {
if(!id) return ;
fetch('/api/link/getlink', getRequest({id}))
.then(response=>response.json())
.then(json => {
if(json.status != 1 ) return this.props.showDialog(json.message);
this.setState({
infoModal:{
open: true,
root: id,
ln: json.data.rt.ln || [],
lc: json.data.rt.lc || []
}
})
})
}
closeInfoModal = () => {
this.setState({
infoModal:{
...defState.infoModal
}
})
}
render(){
let {i18n, list} = this.props;
return (
<Container>
<Segment className="clearfix">
<Button as={Link} to="/admin/addlink" basic color="green" floated="right" icon="plus" content="新增" style={{marginBottom: '10px'}} />
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>操作</Table.HeaderCell>
<Table.HeaderCell>ID</Table.HeaderCell>
<Table.HeaderCell>名稱</Table.HeaderCell>
<Table.HeaderCell>啟用狀態</Table.HeaderCell>
<Table.HeaderCell>觸發內容</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
list.map((t,idx) => (
<ListItem key={idx} i18n={i18n} data={t} swActive={this.swLinkActive} delItem={this.delLink} showInfo={this.openInfoModal} />
))
}
</Table.Body>
</Table>
</Segment>
<LinkInfoModal i18n={i18n}
open={this.state.infoModal.open}
root={this.state.infoModal.root}
ln={this.state.infoModal.ln}
lc={this.state.infoModal.lc}
onClose={this.closeInfoModal} />
</Container>
)
}
}
export default ActionLinkPage;

View File

@ -0,0 +1,132 @@
import React from 'react';
import {Grid, Form, Segment, Input} from 'semantic-ui-react';
const ops = [
{
"code": "0",
"name": "等於"
}, {
"code": "1",
"name": "大於"
}, {
"code": "2",
"name": "小於"
}, {
"code": "3",
"name": "大於等於"
}, {
"code": "4",
"name": "小於等於"
}, {
"code": "5",
"name": "不等於"
}, {
"code": "8",
"name": "AND"
}, {
"code": "9",
"name": "OR"
}
]
class UnitItem extends React.Component {
render() {
let {unit, data, getList} = this.props;
return (
<Grid.Column>
<Segment>
<Form.Field>
<label>選擇元件</label>
<select
value={data.st}
onChange={e => {
getList(unit, e.target.value);
}}>
<option value="">請選擇元件</option>
<option value="leone">LeOne</option>
<option value="di">DigitInput</option>
<option value="time">時間</option>
<option value="unit">已建立群組</option>
</select>
</Form.Field>
{
!data.st
? null
: (<UnitPanel data={data}/>)
}
</Segment>
</Grid.Column>
)
}
}
const UnitPanel = ({data}) => {
if (data.st == 'di') {
return (
<div>
<LC_List data={data} />
<select>
<option value="">選擇狀態</option>
<option value="0">0</option>
<option value="1">1</option>
</select>
</div>
)
}
if(data.st == 'leone') {
return (
<div>
<LC_List data={data} />
<select>
<option value="">選擇感測器</option>
<option value="leone_ttrigger1">溫度</option>
<option value="leone_htrigger1">濕度</option>
</select>
<Input size="mini" placeholder="請輸入數值" />
</div>
)
}
if(data.st == 'unit') {
return (
<div>
<select>
<option value="">選擇群組</option>
{
data.list.map((t,idx) => (
<option key={idx} value={t.id}>{t.name}</option>
))
}
</select>
</div>
)
}
return null;
}
const LC_List = ({data}) => {
return (
<div>
<select>
<option value="">選擇裝置</option>
{
data.list.map((t, idx) => (
<option key={idx} value={t.id}>{t.name}</option>
))
}
</select>
<select>
<option value="">選擇條件</option>
{
ops.map((t,idx) => {
if(t.code == 8 || t.code == 9) return ;
return (<option key={idx} value={t.code}>{t.name}</option>)
})
}
</select>
</div>
)
}
export default UnitItem;

View File

@ -0,0 +1,138 @@
import React from 'react';
import {
Container,
Segment,
Form,
Input,
Button,
Checkbox,
Grid
} from 'semantic-ui-react';
import {getRequest} from '../../../actions';
import UnitItem from './UnitItem';
const defState = {
groups: {},
edit: {
active: false,
name: '',
type: 'ln',
op: '',
id1: {
st: '',
list: [],
data: {}
},
id2: {
st: '',
list: [],
data: {}
}
}
}
const gtype = {
lc: {
type: 'lc',
id: '',
op: '',
value: ''
},
ln: {
type: 'ln'
}
}
class ActionLinkAdd extends React.Component {
state = {
...defState
}
getSelectList = (unit, type = '') => {
if (!unit) {
return;
}
let edit = {
...this.state.edit
};
if (!(unit in edit)) {
return;
}
edit[unit].st = type;
edit[unit].data = {
...gtype.lc
}
if (type != 'di' && type != 'leone') {
return this.setState({edit});
}
if (type == 'di' || type == 'leone') {
this.props.toggleLoading(1);
let json = {
type
};
fetch('/api/system/getselectlist', getRequest(json))
.then(response => response.json())
.then(json => {
if (json.status != 1)
return this.props.showDialog(json.message);
edit[unit].list = json.data.record || [];
this.setState({edit}, () => {
this.props.toggleLoading(0);
});
});
}
if(type == 'unit') {
let list = [];
for(let i in this.state.groups){
list.push({id: i, name: this.state.groups[i].name});
}
edit[unit].list = list;
edit[unit].data = {
...gtype.ln
}
this.setState({edit})
}
}
render() {
return (
<Container>
<Form as={Segment}>
<Form.Field>
<Checkbox label="啟用連動"/>
</Form.Field>
<Form.Field>
<label>建立條件群組</label>
<Segment color="red">
<Form.Field>
<Input label="節點名稱"/>
</Form.Field>
<Form.Field>
<label>觸發條件</label>
<select>
<option value="">請選擇觸發條件</option>
<option value="8">AND</option>
<option value="9">OR</option>
</select>
</Form.Field>
<Grid columns={2} padded>
<UnitItem unit="id1" data={this.state.edit.id1} getList={this.getSelectList}/>
<UnitItem unit="id2" data={this.state.edit.id2} getList={this.getSelectList}/>
</Grid>
<div
style={{
textAlign: 'right'
}}>
<Button type="button" basic size="tiny" color="blue" content="加入"/>
<Button type="button" basic size="tiny" color="green" content="清除"/>
</div>
</Segment>
</Form.Field>
</Form>
</Container>
)
}
}
export default ActionLinkAdd;

View File

@ -0,0 +1,28 @@
import React from 'react';
import {Table, Input, Checkbox, Label} from 'semantic-ui-react';
const DiItem = ({i18n, cusKey, data, onNameChange, onLogicChange, status}) => {
return (
<Table.Row>
<Table.Cell>
<Label content={cusKey}/>
</Table.Cell>
<Table.Cell width={4}>
<Input fluid name="diname" value={data.diname || ''} onChange={(e) => onNameChange(cusKey, e.target.value)} />
</Table.Cell>
<Table.Cell>
<Checkbox toggle={true} label={i18n&&i18n.t ? i18n.t('page.dio.form.label.logic') : ''} checked={data.dilogic == 1 ? true : false} onChange={(e, d) => onLogicChange(cusKey, d.checked)}/>
</Table.Cell>
<Table.Cell>
<span>{i18n&&i18n.t ? i18n.t('page.dio.form.label.di_status') : ''} <Label color={status == 0 ? 'green' : 'red'} size="tiny" content={
i18n&&i18n.t ?
(status == 0 ? i18n.t('page.dio.form.label.di_not_triggered') : i18n.t('page.dio.form.label.di_triggered')) :
"Loading..."
}/></span>
</Table.Cell>
</Table.Row>
)
}
export default DiItem;

View File

@ -0,0 +1,24 @@
import React from 'react';
import {Table, Input, Checkbox, Label} from 'semantic-ui-react';
const DoItem = ({i18n, cusKey, data, onNameChange, onLogicChange, status, onDoRun}) => {
return (
<Table.Row>
<Table.Cell>
<Label content={cusKey}/>
</Table.Cell>
<Table.Cell width={4}>
<Input fluid name="doname" value={data.doname || ''} onChange={(e) => {onNameChange(cusKey, e.target.value)}} />
</Table.Cell>
<Table.Cell>
<Checkbox toggle={true} label={i18n&&i18n.t ? i18n.t('page.dio.form.label.logic') : ''} checked={data.dologic == 1 ? true : false } onChange={(e, d)=>{onLogicChange(cusKey, d.checked)}} />
</Table.Cell>
<Table.Cell>
<Checkbox toggle={true} label={i18n&&i18n.t ? i18n.t('page.dio.form.label.do_ctrl') : ''} checked={status == 1} onChange={(e,d) => { onDoRun(cusKey, d.checked) }} />
</Table.Cell>
</Table.Row>
)
}
export default DoItem;

View File

@ -0,0 +1,175 @@
import React from 'react';
import {Container, Grid, Segment, Header, CheckBox, Table, Input, Button} from 'semantic-ui-react';
import DoItem from './DoItem';
import DiItem from './DiItem';
class DIO extends React.Component {
state = {
do: [],
di: [],
doSt: {},
diSt: {}
}
tick = null
onNameChange = (key, name) => {
let reg = new RegExp(`^${key}$`, 'i');
if(/^do/i.test(key)) {
let dos = [...this.state.do];
for(let i in dos){
if(reg.test(dos[i].doid)){
dos[i].doname = name;
this.setState({
do: dos
});
break;
}
}
}else{
let dis = [...this.state.di];
for(let i in dis){
if(reg.test(dis[i].diid)){
dis[i].diname = name;
this.setState({
di: dis
});
break;
}
}
}
}
onLogicChange = (key, st) => {
let reg = new RegExp(`^${key}$`, 'i');
if(/^do/i.test(key)) {
let dos = [...this.state.do];
for(let i in dos){
if(reg.test(dos[i].doid)){
dos[i].dologic = st ? 1 : 0;
this.setState({
do: dos
});
break;
}
}
}else{
let dis = [...this.state.di];
for(let i in dis){
if(reg.test(dis[i].diid)){
dis[i].dilogic = st ? 1 : 0;
this.setState({
di: dis
});
break;
}
}
}
}
updateSetting = () => {
let dos = [...this.state.do];
let dis = [...this.state.di];
let json = {};
json.do = {};
json.di = {};
for(let i in dos) {
json.do[dos[i].doid] = {
name: dos[i].doname,
logic: dos[i].dologic
}
}
for(let i in dis){
json.di[dis[i].diid] ={
name: dis[i].diname,
logic: dis[i].dilogic
}
}
this.props.setDIOInfo(json);
}
doRun = (key, val) => {
let reg = /^do([0-9]+)$/i;
let m = key.match(reg);
if(!m || !m[1]) return ;
let pin = m[1];
if(pin in this.state.doSt){
let tmp = {...this.state.doSt};
tmp[pin] = val ? 1 : 0;
this.setState({
doSt: tmp
})
}
this.props.dotRun(pin, val ? 1 : 0);
}
componentWillReceiveProps(nextProps) {
this.setState({
do: nextProps.dio.do,
di: nextProps.dio.di,
diSt: nextProps.dio.diSt,
doSt: nextProps.dio.doSt
});
}
updateTick = () => {
this.props.getIO();
}
componentDidMount() {
this.props.getDIOInfo();
this.props.getIO();
this.tick = setInterval(this.updateTick, 5000);
this.props.router.setRouteLeaveHook(this.props.route, () => {
this.props.clearList();
})
}
componentWillUnmount() {
clearInterval(this.tick);
}
render () {
let {i18n} = this.props;
return (
<Container>
<Grid columns={2}>
<Grid.Column>
<Header as="h4" color="blue" content={i18n&&i18n.t ? i18n.t('page.dio.title.do'): ''}/>
<Table>
<Table.Body>
{
[1,2,3,4,5,6,7,8].map(t => {
let tmp = this.state.do.filter(tt => tt.doid == `do${t}`);
let dost = this.state.doSt[t] || 0;
return <DoItem i18n={i18n} key={t} cusKey={`Do${t}`} data={tmp[0] || {}} onLogicChange={this.onLogicChange} onNameChange={this.onNameChange} status={dost} onDoRun={this.doRun}/>
})
}
</Table.Body>
</Table>
</Grid.Column>
<Grid.Column>
<Header as="h4" color="blue" content={i18n&&i18n.t ? i18n.t('page.dio.title.di'): ''}/>
<Table>
<Table.Body>
{
[1,2,3,4,5,6,7,8].map(t => {
let tmp = this.state.di.filter(tt => tt.diid == `di${t}`);
let dist = this.state.diSt[t] || 0;
return <DiItem i18n={i18n} key={t} cusKey={`Di${t}`} data={tmp[0] || {}} onLogicChange={this.onLogicChange} onNameChange={this.onNameChange} status={dist} />
})
}
</Table.Body>
</Table>
</Grid.Column>
</Grid>
<Button type="button" style={{marginTop: '10px'}} fluid content={i18n&&i18n.t ? i18n.t('page.dio.form.button.update_setting') : ''} onClick={() => {this.updateSetting()}} />
</Container>
)
}
}
export default DIO;

View File

@ -0,0 +1,17 @@
import React from 'react';
import {List, Button, Label} from 'semantic-ui-react';
const SelectedItem = ({i18n, data, idx, removeSelect}) => {
return (
<List.Item>
<List.Content floated="right">
<Button type="button" size="mini" content={i18n&&i18n.t ? i18n.t('page.iogroup.form.button.remove') : ''} onClick={() => {removeSelect(idx)}} />
</List.Content>
<Label content={data.type || ''} />
{data.name || ''}
</List.Item>
)
}
export default SelectedItem;

View File

@ -0,0 +1,129 @@
import React from 'react';
import {Container, Segment, Form, Button, List, Input} from 'semantic-ui-react';
import DeviceSelect from '../../Common/DeviceSelect';
import SelectedItem from './SelectedItem';
class IOCmdPage extends React.Component {
state = {
showTemp: false,
selectItem: []
}
querySelectList = (type) => {
this.props.clearSelectList();
if(!type) return ;
this.props.getSelectList({type});
}
checkShowTemp = (cmd) => {
this.setState({
showTemp: cmd == 2 ? true : false
})
}
addSelect = (data) => {
let tmp = this.state.selectItem.filter(t => t.id == data.id);
if(tmp.length > 0) return ;
this.setState({
selectItem: [...this.state.selectItem, data]
});
}
removeSelect = (idx) => {
this.state.selectItem.splice(idx, 1);
this.setState({
selectItem: this.state.selectItem
})
}
runCmd = (data) => {
let {i18n} = this.props;
if(!data.cmd) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.select_action') : '');
if(data.cmd == 2){
if(!data.temp) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty_temp') : '');
if(!(data.temp > 16 && data.temp < 30)) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.temp_format') : '');
data.cmd = `${data.cmd} ${data.temp}`;
}
let ids = this.state.selectItem.map(t => t.id);
let json = {
...data,
devs: ids.join(',')
}
this.props.runCmd(json);
this.setState({
showTemp: false,
selectItem: []
}, ()=>{
this.props.clearSelectList();
})
}
componentDidMount() {
this.props.clearSelectList();
}
render() {
let {i18n,permissions,devs} = this.props;
let actlist = i18n&&i18n.getResource&&i18n.language ? i18n.getResource(i18n.language + '.translation.action_list') : [];
return (
<Container>
<Segment>
<Form onSubmit={(e,d) => {
e.preventDefault();
this.runCmd(d.formData);
}} serializer={e => {
let json = {
cmd: '',
temp: ''
};
let sel = e.querySelector('select[name="cmd"]');
if(sel && 'value' in sel) json.cmd = sel.value;
let temp = e.querySelector('input[name="temp"]');
if(temp && 'value' in temp) json.temp = temp.value;
if(e.reset) e.reset();
return json;
}}>
<Form.Field>
<label>{i18n&&i18n.t ? i18n.t('page.iocmd.form.label.action') : ''}</label>
<select name="cmd" onChange={e => {
this.checkShowTemp(e.target.value);
}}>
<option value="">{i18n&&i18n.t ? i18n.t('select.action') : ''}</option>
{
actlist.map((t,idx) => <option key={idx} value={t.cmd}>{t.name}</option>)
}
</select>
</Form.Field>
{
this.state.showTemp ?
(<Form.Field>
<Input name="temp" label={i18n&&i18n.t ? i18n.t('page.iocmd.form.label.temp') : ''} />
</Form.Field>) : null
}
<DeviceSelect i18n={i18n} devs={devs} page="iocmd" showGroup={true} permissions={permissions} querySelectList={this.querySelectList} addSelect={this.addSelect} />
<Form.Field>
<label>{i18n&&i18n.t ? i18n.t('page.iocmd.form.label.selected_device') : ''}</label>
<Segment>
<List divided relaxed={true}>
{
this.state.selectItem.map((t,idx) => <SelectedItem key={idx} i18n={i18n} data={t} idx={idx} removeSelect={this.removeSelect} />)
}
</List>
</Segment>
</Form.Field>
<Button type="submit" fluid content={i18n&&i18n.t ? i18n.t('page.iocmd.form.button.submit') : ''} />
</Form>
</Segment>
</Container>
)
}
}
export default IOCmdPage;

View File

@ -0,0 +1,51 @@
import React from 'react';
import {Modal, Form, Input, Segment, Grid, Button, Header, List} from 'semantic-ui-react';
import SelectedItem from './SelectedItem';
import DeviceSelect from '../../Common/DeviceSelect';
const GroupModal = ({i18n, open, type, data, devs, permissions, onClose, onSubmit, selected, removeSelect, addSelect, querySelectList}) => {
return (
<Modal open={open}>
<Modal.Header content={i18n&&i18n.t ? (type == 1 ? i18n.t('page.iogroup.form.title.edit') : i18n.t('page.iogroup.form.title.add')) : ''} />
<Modal.Content>
<Form onSubmit={(e,d) => {
e.preventDefault();
onSubmit(type, d.formData);
}} serializer={e => {
let json = {
name: '',
id: data.iogroupuid || 0
};
let n = e.querySelector('input[name="name"]');
if(n && 'value' in n) json.name = n.value;
return json;
}}>
<Form.Field>
<Input label={i18n&&i18n.t?i18n.t('page.iogroup.form.label.name') : ''} name="name" defaultValue={data.iogroupname || ''} />
</Form.Field>
<DeviceSelect i18n={i18n} devs={devs} page="iogroup" showGroup={false} permissions={permissions} addSelect={addSelect} querySelectList={querySelectList} />
<Segment>
<Header as="h4" content={i18n&&i18n.t ? i18n.t('page.iogroup.form.label.selected_device') : ''} />
<List divided verticalAlign="middle">
{
selected.map((t, idx) => <SelectedItem i18n={i18n} key={idx} data={t} idx={idx} removeSelect={removeSelect}/>)
}
</List>
</Segment>
<Grid columns={2}>
<Grid.Column>
<Button fluid type="submit" content={i18n&&i18n.t?i18n.t('page.leone.form.button.submit'):''}/>
</Grid.Column>
<Grid.Column>
<Button fluid type="button" content={i18n&&i18n.t?i18n.t('page.leone.form.button.cancel'):''} onClick={() => {onClose()}}/>
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
</Modal>
)
}
export default GroupModal;

View File

@ -0,0 +1,55 @@
import React from 'react';
import {Table, Button, Label, Item} from 'semantic-ui-react';
const ListItem = ({i18n, data, dos, les, openItemModal, onDelete, openModal}) => {
let devs = data.iogroupid || '';
let dev = devs.split(',');
let items = [];
if(dev.length > 0){
for(let i in dev){
if(/^do/i.test(dev[i])){
for(let j in dos){
if(`do${dos[j].douid}` == dev[i]) {
items.push({
type: 'DigitOutput',
name: dos[j].doname,
id: dev[i]
});
break;
}
}
}else if(/^le/i.test(dev[i])){
for(let j in les){
if(`le${les[j].leonelistuid}` == dev[i]) {
items.push({
type: 'LeOne',
name: les[j].leonename,
id: dev[i]
});
break;
}
}
}
}
}
return (
<Table.Row>
<Table.Cell>
<Button type="button" basic size="tiny" color="red" content={i18n&&i18n.t ? i18n.t('page.iogroup.table.button.del') : ''} onClick={()=>{onDelete(data.iogroupuid)}} />
<Button type="button" basic size="tiny" color="blue" content={i18n&&i18n.t ? i18n.t('page.iogroup.table.button.edit') : ''} onClick={() => {openModal(1, data, items)}}/>
</Table.Cell>
<Table.Cell>{data.iogroupname}</Table.Cell>
<Table.Cell>
{
items.length > 0 ?
(items.length == 1 ?
<Item><Label content={items[0].type} />{items[0].name}</Item>:
<Button size="tiny" basic content={i18n&&i18n.t ? i18n.t('page.iogroup.table.button.showgroup') : ''} onClick={()=>{openItemModal(items)}}/> ) :
''
}
</Table.Cell>
</Table.Row>
)
}
export default ListItem;

View File

@ -0,0 +1,17 @@
import React from 'react';
import {List, Button, Label} from 'semantic-ui-react';
const SelectedItem = ({i18n, data, idx, removeSelect}) => {
return (
<List.Item>
<List.Content floated="right">
<Button type="button" size="mini" content={i18n&&i18n.t ? i18n.t('page.iogroup.form.button.remove') : ''} onClick={() => {removeSelect(idx)}} />
</List.Content>
<Label content={data.type || ''} />
{data.name || ''}
</List.Item>
)
}
export default SelectedItem;

View File

@ -0,0 +1,152 @@
import React from 'react';
import {Container, Table, Button, Segment, Modal, Item, Label} from 'semantic-ui-react';
import ListItem from './ListItem';
import GroupModal from './GroupModal';
class IOGroupPage extends React.Component {
state = {
modal: false,
modalType: 0,
modalData: {},
modalSelect: [],
showItem: false,
items: []
}
showGroupItems = (items = []) => {
this.setState({
showItem: true,
items
})
}
closeItemModal = () => {
this.setState({
showItem: false,
items: []
})
}
openModal = (type, data= {}, items=[]) => {
this.props.clearSelectList();
this.setState({
modal: true,
modalType: type,
modalData: data,
modalSelect: items
})
}
closeModal = () => {
this.setState({
modal: false,
modalData: {},
modalSelect: []
})
}
submitModal = (type, data ={}) => {
let {i18n} = this.props;
if(!data.name || (type == 1 && !data.id)) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
let ids = this.state.modalSelect.map(t => t.id);
let json = {
...data,
devs: ids.join(',')
};
if(type == 1) {
this.props.editIOGroup(json);
}else {
this.props.addIOGroup(json);
}
this.closeModal()
}
querySelectList = (type) => {
this.props.clearSelectList();
if(!type) return ;
this.props.getSelectList({type});
}
removeSelect = (idx) => {
this.state.modalSelect.splice(idx, 1);
this.setState({
modalSelect: this.state.modalSelect
})
}
addSelect = (data = {}) => {
let tmp = this.state.modalSelect.filter(t => t.id == data.id);
if(tmp.length > 0) return ;
this.setState({
modalSelect: [...this.state.modalSelect, data]
})
}
delIOGroup = (id) => {
if(!id) return ;
this.props.delIOGroup({id});
}
componentDidMount() {
this.props.getIOGroupList();
this.props.router.setRouteLeaveHook(this.props.route, () => {
this.props.clearList();
})
}
render () {
let {i18n, list, dos, leones, permissions, devs} = this.props;
return (
<Container>
<Segment className="clearfix">
<Button basic type="button" color="green" floated="right" style={{marginBottom: '15px'}} icon="plus" content={i18n&&i18n.t ? i18n.t('page.iogroup.table.button.add') : ''} onClick={() => {this.openModal(0)}} />
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.iogroup.table.operate') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.iogroup.table.name') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.iogroup.table.groups') : ''}</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
list.map((t, idx) => <ListItem i18n={i18n} key={idx} data={t} dos={dos} les={leones} openItemModal={this.showGroupItems} onDelete={this.delIOGroup} openModal={this.openModal}/>)
}
</Table.Body>
</Table>
</Segment>
<ShowGroupItem open={this.state.showItem} items={this.state.items} onClose={this.closeItemModal} />
<GroupModal i18n={i18n}
open={this.state.modal}
data={this.state.modalData}
type={this.state.modalType}
onClose={this.closeModal}
onSubmit={this.submitModal}
permissions={permissions}
devs={devs}
selected={this.state.modalSelect}
removeSelect={this.removeSelect}
addSelect={this.addSelect}
querySelectList={this.querySelectList} />
</Container>
)
}
}
const ShowGroupItem = ({i18n,open,items, onClose}) => {
return (
<Modal open={open} onClose={() => {onClose()}} size="small">
<Modal.Content>
{
items.map((t, idx) => <Item key={idx}><Label content={t.type}/>{t.name}</Item>)
}
</Modal.Content>
</Modal>
)
}
export default IOGroupPage;

View File

@ -0,0 +1,52 @@
import React from 'react';
import {Modal, Form, Header, Grid, Button, Input} from 'semantic-ui-react';
const CmdModal = ({i18n, open, id, devname, cmd, onSubmit, onClose}) => {
let actlist = i18n&&i18n.getResource&&i18n.language ? i18n.getResource(i18n.language + '.translation.action_list') : [];
let act = actlist.filter(t => t.cmd == cmd);
return (
<Modal open={open} closeOnDimmerClick={false}>
<Modal.Content>
<Form onSubmit={(e, d) => {
e.preventDefault();
onSubmit(d.formData);
}} serializer={e => {
let json = {
devs: `le${id}`
};
json.cmd = cmd;
if(json.cmd == 2){
let el = e.querySelector('input[name="temp"]');
if(el && 'value' in el){
json.cmd = `${json.cmd} ${el.value}`
}
}
return json;
}}>
<Header as="h4" content={devname || ''}/>
<Header as="h4" content={`${i18n&&i18n.t?i18n.t('page.leone.form.label.run_command'):''} ${act[0] ? act[0].name : ''}`} />
{
cmd && cmd == '2' ?
(<Form.Field>
<Input fluid name="temp" label={i18n&&i18n.t ? i18n.t('page.leone.form.label.temp') : ''}/>
</Form.Field>) :
null
}
<Grid columns={2} style={{marginTop: '15px'}}>
<Grid.Column>
<Button fluid type="submit" content={i18n&&i18n.t?i18n.t('page.leone.form.button.submit'):''}/>
</Grid.Column>
<Grid.Column>
<Button fluid type="button" content={i18n&&i18n.t?i18n.t('page.leone.form.button.cancel'):''} onClick={() => {onClose()}}/>
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
</Modal>
)
}
export default CmdModal;

View File

@ -0,0 +1,46 @@
import React from 'react';
import {Modal, Form, Input, Grid, Button} from 'semantic-ui-react';
const LeOneModal = ({i18n, open, type, data, onSubmit, onClose}) => {
return (
<Modal open={open}>
<Modal.Header content={i18n&&i18n.t ? (type == 1 ? i18n.t('page.leone.form.title.edit') : i18n.t('page.leone.form.title.add')) : '' } />
<Modal.Content>
<Form onSubmit={(e,d) => {
e.preventDefault();
onSubmit(type, d.formData);
}} serializer={e => {
let json = {
id: data.leonelistuid || 0,
name: e.querySelector('input[name="name"]').value,
ip: e.querySelector('input[name="ip"]').value,
password: e.querySelector('input[name="password"]').value
};
return json ;
}}>
<Form.Field>
<Input label={i18n&&i18n.t?i18n.t('page.leone.form.label.name'):''} name="name" defaultValue={data.leonename || ''} />
</Form.Field>
<Form.Field>
<Input label={i18n&&i18n.t?i18n.t('page.leone.form.label.ip'):''} name="ip" disabled={type == 1} defaultValue={data.leoneip || ''} />
</Form.Field>
<Form.Field>
<Input label={i18n&&i18n.t?i18n.t('page.leone.form.label.password'):''} name="password" defaultValue={data.leonepassword || ''} />
</Form.Field>
<Grid columns={2}>
<Grid.Column>
<Button fluid type="submit" content={i18n&&i18n.t?i18n.t('page.leone.form.button.submit'):''}/>
</Grid.Column>
<Grid.Column>
<Button fluid type="button" content={i18n&&i18n.t?i18n.t('page.leone.form.button.cancel'):''} onClick={() => {onClose()}}/>
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
</Modal>
)
}
export default LeOneModal;

View File

@ -0,0 +1,46 @@
import React from 'react';
import {Table, Button} from 'semantic-ui-react';
const ListItem = ({i18n, data, status, delLeone, editLeone, runCmd}) => {
let stats = i18n&&i18n.getResource&&i18n.language ? i18n.getResource(i18n.language + '.translation.leone_stats') : [];
let actlist = i18n&&i18n.getResource&&i18n.language ? i18n.getResource(i18n.language + '.translation.action_list') : [];
let st = stats.filter(t => t.mode == status.mode);
let tunit = i18n&&i18n.t ? [i18n.t('time_unit.sec'), i18n.t('time_unit.min'), i18n.t('time_unit.hour'), i18n.t('time_unit.day')] : [];
let idx = 0;
let mtime = Math.floor(Date.now() / 1000) - status.mtime;
while(idx < 3){
if(mtime >= 60){
mtime = Math.floor(mtime / 60);
idx++;
}else{
break;
}
}
return (
<Table.Row>
<Table.Cell>
<Button type="button" basic size="tiny" color="red" content={i18n&&i18n.t ? i18n.t('page.leone.table.button.del') : ''} onClick={() => {delLeone(data.leonelistuid)}} />
<Button type="button" basic size="tiny" color="blue" content={i18n&&i18n.t ? i18n.t('page.leone.table.button.edit') : ''} onClick={() => {editLeone(1, data)}} />
</Table.Cell>
<Table.Cell>{data.leonename}</Table.Cell>
<Table.Cell>{data.leoneip}</Table.Cell>
<Table.Cell>{`${status.ts == '9999' || !isFinite(status.ts) ? '-' : `${status.ts} ${String.fromCharCode(8451)}`} / ${status.hs == '9999' || !isFinite(status.hs) ? '-' : `${status.hs} %`}`}</Table.Cell>
<Table.Cell>{st[0]&&st[0].name ? st[0].name : status.mode}</Table.Cell>
<Table.Cell>{data.leonepassword}</Table.Cell>
<Table.Cell>{`${mtime}${tunit[idx]}${i18n&&i18n.t? i18n.t('time_unit.ago') : ''}`}</Table.Cell>
<Table.Cell>
<select onChange={(e)=>{
runCmd(data.leonelistuid, data.leonename, e.target.value);
e.target.selectedIndex = 0;
}}>
<option value="">{i18n&&i18n.t ? i18n.t('tip.select_action') : ''}</option>
{
actlist.map((t, idx) => <option key={idx} value={t.cmd}>{t.name}</option>)
}
</select>
</Table.Cell>
</Table.Row>
)
}
export default ListItem;

View File

@ -0,0 +1,52 @@
import React from 'react';
import {Container, Table, Segment, Form, Grid, Button, Input} from 'semantic-ui-react';
const ScanForm = ({i18n, onSubmit}) => {
return (
<Form onSubmit={(e, data) => {
e.preventDefault();
onSubmit(data.formData);
}} serializer={e => {
let json = {
ip1: e.querySelector('input[name="ip1"]').value,
ip2: e.querySelector('input[name="ip2"]').value,
ip3: e.querySelector('input[name="ip3"]').value,
password: e.querySelector('input[name="pass"]').value
};
return json;
}}>
<Grid verticalAlign="middle">
<Grid.Column computer={8} mobile={16}>
<Form.Group inline={true}>
<Form.Field>
<label>{i18n&&i18n.t ? i18n.t('page.leone.form.label.ip') : ''}</label>
<Input style={{width: '60px'}} defaultValue="192" name="ip1"/>
</Form.Field>
<Form.Field>
<Input style={{width: '60px'}} defaultValue="168" name="ip2"/>
</Form.Field>
<Form.Field>
<Input style={{width: '60px'}} defaultValue="1" name="ip3"/>
</Form.Field>
<Form.Field>
<Input style={{width: '60px'}} defaultValue="*" disabled/>
</Form.Field>
</Form.Group>
<Form.Group inline={true}>
<Form.Field>
<label>{i18n&&i18n.t ? i18n.t('page.leone.form.label.password') : ''}</label>
<Input name="pass" />
</Form.Field>
</Form.Group>
</Grid.Column>
<Grid.Column computer={8} mobile={16}>
<Button type="submit" fluid icon="search" content={i18n&&i18n.t ? i18n.t('page.leone.form.button.scan') : ''} />
</Grid.Column>
</Grid>
</Form>
)
}
export default ScanForm;

View File

@ -0,0 +1,123 @@
import React from 'react';
import {Modal, Grid, Label, Button, Table, Checkbox} from 'semantic-ui-react';
import ScanItem from './SelectScanItem';
class SelectScan extends React.Component {
state = {
list: [],
page: 1,
per: 10,
totalpage: 1
}
componentWillReceiveProps(nextProps) {
if(this.state.list.length !== nextProps.list) {
let totalpage = Math.ceil( nextProps.list.length / this.state.per ) || 1;
this.setState({
list: nextProps.list,
totalpage
});
}
}
onItemSelect = (idx, checked) => {
let list = [...this.state.list];
if(list[idx]){
list[idx].checked = checked;
this.setState({
list
});
}
}
changePage = (p = 1) => {
p = p < 1 ? 1 : p;
p = p > this.state.totalpage ? this.state.totalpage : p;
this.setState({
page: p
});
}
calSelect = () => {
let a = this.state.list.filter(t => t.checked);
return a.length;
}
checkAllSelect = (s, e) => {
let arr = this.state.list.slice(s,e);
let tmp = arr.filter(t => !t.checked);
if(tmp.length > 0) return false;
return true;
}
changeAllCheck = (s, e, chk) => {
let arr = [...this.state.list];
for(let i=s; i<e; i++){
arr[i].checked = chk;
}
this.setState({
list: arr
});
}
submitData = () => {
let json = {
id: []
};
for(let i in this.state.list) {
if(this.state.list[i].leonelistuid) json.id.push(this.state.list[i].leonelistuid)
}
this.props.onSubmit(json);
}
render () {
let {i18n, onClose} = this.props;
let sIdx = this.state.per * (this.state.page - 1);
let eIdx = sIdx + this.state.per;
eIdx = eIdx > this.state.list.length ? this.state.list.length : eIdx;
let arr = this.state.list.slice(sIdx, eIdx);
return (
<Modal open={this.state.list.length > 0 ? true : false} closeOnDimmerClick={false} >
<Modal.Header content={i18n&&i18n.t ? i18n.t('page.leone.form.title.select-dev') : ''} />
<Modal.Content>
<div className="clearfix">
<Label content={`${i18n&&i18n.t?i18n.t('page.leone.form.label.select_num') : ''} ${this.calSelect()}`} basic color="blue"/>
<div style={{float: 'right'}}>
<Button type="button" content={i18n&&i18n.t?i18n.t('page.leone.form.button.prevpage') : ''} onClick={() => {this.changePage(this.state.page - 1)}} size="tiny" basic color="blue"/>
<Label basic content={`${this.state.page} / ${this.state.totalpage}`}/>
<Button type="button" content={i18n&&i18n.t?i18n.t('page.leone.form.button.nextpage') : ''} onClick={() => {this.changePage(this.state.page + 1)}} size="tiny" basic color="blue"/>
</div>
</div>
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell><Checkbox checked={this.checkAllSelect(sIdx, eIdx)} onChange={(e,d)=>{ this.changeAllCheck(sIdx, eIdx, d.checked) }} /></Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.form.label.name') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.form.label.name') : ''}</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
arr.map((t, idx) => <ScanItem key={idx} i18n={i18n} data={t} idx={idx} onChange={this.onItemSelect}/>)
}
</Table.Body>
</Table>
<Grid columns={2}>
<Grid.Column>
<Button content={i18n&&i18n.t ? i18n.t('page.leone.form.button.submit') : ''} fluid onClick={() => {this.submitData()}}/>
</Grid.Column>
<Grid.Column>
<Button content={i18n&&i18n.t ? i18n.t('page.leone.form.button.cancel') : ''} fluid onClick={() => {onClose()}} />
</Grid.Column>
</Grid>
</Modal.Content>
</Modal>
)
}
}
export default SelectScan;

View File

@ -0,0 +1,12 @@
import React from 'react';
import {Table, Checkbox} from 'semantic-ui-react';
const ScanItem = ({i18n, data, idx, onChange}) => (
<Table.Row>
<Table.Cell><Checkbox onChange={(e,d) => {onChange(idx, d.checked)}} checked={data.checked} /></Table.Cell>
<Table.Cell>{data.leonename}</Table.Cell>
<Table.Cell>{data.leoneip}</Table.Cell>
</Table.Row>
)
export default ScanItem;

View File

@ -0,0 +1,174 @@
import React from 'react';
import {Container, Table, Segment, Form, Grid, Button} from 'semantic-ui-react';
import ScanForm from './ScanForm';
import {add_dialog_msg} from '../../../actions';
import SelectScan from './SelectScan';
import ListItem from './ListItem';
import LeOneModal from './LeOneModal';
import CmdModal from './CmdModal';
class LeOnePage extends React.Component {
state = {
modal: false,
modalType: 0,
modalData: {},
cmd: false,
cmdData: {
id: 0,
name: '',
cmd: ''
}
}
scanSubmit = (data) => {
let {i18n} = this.props;
if(!data.ip1.trim() || !data.ip2.trim() || !data.ip3.trim() || !data.password.trim())
return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
// send to scan
let json = {
ip: `${data.ip1}.${data.ip2}.${data.ip3}`,
password: data.password
};
this.props.scanLeOne(json);
}
doDelLeOne = (id) => {
let {i18n} = this.props;
if(!id) return;
this.props.delLeOne({id});
}
openModal = (type, data = {}) => {
this.setState({
modal: true,
modalType: type == 1 ? 1 : 0,
modalData: data
});
}
closeModal = () => {
this.setState({
modal: false,
modalData: {}
});
}
submitModal = (type, data ={}) => {
let {i18n} = this.props;
if((type == 1 && !data.id) || !data.name || !data.password || (type == 0 && !data.ip)) return this.props.showDialog(i18n&&i18n.t?i18n.t('tip.input_empty') : '')
if(type == 0){
this.props.addLeOne(data);
}else{
this.props.editLeOne(data);
}
this.closeModal();
}
openCmdModal = (id, name, cmd) => {
this.setState({
cmd: true,
cmdData: {
id,name,cmd
}
})
}
closeCmdModal = () => {
this.setState({
cmd: false,
cmdData: {
id: 0,
name: '',
cmd: ''
}
})
}
submitCmdModal = (data) => {
let {i18n} = this.props;
if(!data.devs || !data.cmd) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(data.cmd.trim() == 2) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty_temp') : '');
let cmds = data.cmd.split(' ');
if(cmds.length != 2) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(cmds[0] == 2 && (cmds[1] <16 || cmds[1] > 30) ) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.temp_format') : '');
this.props.runCmd(data);
this.closeCmdModal()
}
tick = null;
runTick = () => {
if(!this.props.scanning){
this.props.getLeOneList(0);
}
}
componentDidMount() {
this.props.getLeOneList();
this.tick = setInterval(this.runTick, 10000);
this.props.router.setRouteLeaveHook(this.props.route, () => {
this.props.clearList();
})
}
componentWillUnmount() {
clearInterval(this.tick);
}
componentWillReceiveProps(nextProps) {
}
render () {
let {i18n, scanList, clearScan, addScanLeOne, list, status} = this.props;
return (
<Container>
<Segment>
<ScanForm i18n={i18n} onSubmit={this.scanSubmit} />
</Segment>
<Segment className="clearfix">
<Button type="button" basic color="green" content={i18n&&i18n.t ? i18n.t('page.leone.table.button.add') : ''} style={{marginBottom: '10px'}} icon="plus" floated="right" onClick={() => {this.openModal(0)}} />
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.table.operate') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.table.name') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.table.ip') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.table.hsts') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.table.mode') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.table.password') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.table.update_time') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.leone.table.control') : ''}</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
list.map(t => {
let st = {};
for(let i in status){
if(status[i].ip == t.leoneip) {
st = status[i];
break;
}
}
return <ListItem i18n={i18n} key={t.leonelistuid} status={st} data={t} delLeone={this.doDelLeOne} editLeone={this.openModal} runCmd={this.openCmdModal}/>
})
}
</Table.Body>
</Table>
</Segment>
<SelectScan i18n={i18n} list={scanList} onClose={clearScan} onSubmit={addScanLeOne}/>
<LeOneModal i18n={i18n} open={this.state.modal} type={this.state.modalType} data={this.state.modalData} onSubmit={this.submitModal} onClose={this.closeModal} />
<CmdModal i18n={i18n} open={this.state.cmd} id={this.state.cmdData.id} devname={this.state.cmdData.name} cmd={this.state.cmdData.cmd} onSubmit={this.submitCmdModal} onClose={this.closeCmdModal} />
</Container>
)
}
}
export default LeOnePage;

View File

@ -0,0 +1,32 @@
import React from 'react';
import {Table} from 'semantic-ui-react';
import {convertTime} from '../../../tools';
const LogItem = ({i18n, data}) => {
if(!(i18n && i18n.t)) return null;
let defMsg = '';
if(/^(do|di)/i.test(data.iolabel)){
defMsg = i18n.t('page.log.description.dio');
}else if(/^leone/i.test(data.iolabel)){
defMsg = i18n.t('page.log.description.leone');
}else if(/^iogroup/i.test(data.iolabel)){
defMsg = i18n.t('page.log.description.iogroup');
}
let msg = defMsg
.replace(/\$io_label\$/, data.iolabel)
.replace(/\$io_name\$/, data.ioname)
.replace(/\$io_logic\$/, data.iosetting1)
.replace(/\$io_trigger_time\$/, convertTime(data.ioeventtst))
.replace(/\$io_trigger_status\$/, data.ioevent);
return (
<Table.Row>
<Table.Cell>{convertTime(data.ioeventtst)}</Table.Cell>
<Table.Cell>{data.iosetting3}</Table.Cell>
<Table.Cell>{data.iosetting2}</Table.Cell>
<Table.Cell>{data.ioevent}</Table.Cell>
<Table.Cell>{msg}</Table.Cell>
</Table.Row>
)
}
export default LogItem;

View File

@ -0,0 +1,46 @@
import React from 'react';
import {Container, Table, Button, Label} from 'semantic-ui-react';
import LogItem from './LogItem';
class LogPage extends React.Component {
componentDidMount(){
this.props.getList();
this.props.router.setRouteLeaveHook(this.props.route, () => {
this.props.clearList();
})
}
loadPage = (p = 1) => {
this.props.getList(p);
}
render () {
let {i18n, list, page} = this.props;
return (
<Container>
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.log.table.datetime') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.log.table.username') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.log.table.status') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.log.table.event') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.log.table.description') : ''}</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{ list.map(t => <LogItem key={t.jciocertuid} i18n={i18n} data={t} />) }
</Table.Body>
</Table>
<div style={{textAlign: 'center'}}>
<Button content={i18n&&i18n.t ? i18n.t('page.log.table.button.prevpage') : ''} size="small" labelPosition="left" basic={true} color="black" icon="arrow left" onClick={() => {this.loadPage(page.prevpage)}} />
<Label basic={true} color="blue" content={`${page.page || 1} / ${page.totalpage || 1}`} />
<Button content={i18n&&i18n.t ? i18n.t('page.log.table.button.nextpage') : ''} size="small" labelPosition="right" basic={true} color="black" icon="arrow right" onClick={() => {this.loadPage(page.nextpage)}} />
</div>
</Container>
)
}
}
export default LogPage;

View File

@ -0,0 +1,40 @@
import React from 'react';
import {Table, Input, Form, Button} from 'semantic-ui-react';
const AIOForm = ({i18n, open, type, data, onSubmit, onClose}) => {
if(!open) return null;
let input = {
name: data.name || '',
portnum: data.portnum || '',
scale_min: data.scale_min || 0,
scale_max: data.scale_max || 0,
range_min: data.range_min || 0,
range_max: data.range_max || 0
}
return (
<Table.Row>
<Table.Cell><Input name="name" onChange={(e,d)=>{input.name = d.value}} defaultValue={data.name || ''}/></Table.Cell>
<Table.Cell><Input name="portnum" onChange={(e,d)=>{input.portnum = d.value}} defaultValue={data.portnum || ''}/></Table.Cell>
<Table.Cell>
<Input name="range_min" size="small" label="Min" onChange={(e,d)=>{input.range_min = d.value}} defaultValue={data.range_min || '0'}/>
<Input name="range_max" size="small" label="Max" onChange={(e,d)=>{input.range_max = d.value}} defaultValue={data.range_max || '0'}/>
</Table.Cell>
<Table.Cell>
<Input name="scale_min" size="small" label="Min" onChange={(e,d)=>{input.scale_min = d.value}} defaultValue={data.scale_min || '0'}/>
<Input name="scale_max" size="small" label="Max" onChange={(e,d)=>{input.scale_max = d.value}} defaultValue={data.scale_max || '0'}/>
</Table.Cell>
<Table.Cell>
<Button basic size="tiny" content="Submit" type="button" onClick={()=>{
let json = {
...input,
id: data.uid || ''
}
onSubmit(type, json);
}} />
<Button basic size="tiny" content="Cancel" type="button" onClick={()=>{onClose()}} />
</Table.Cell>
</Table.Row>
)
}
export default AIOForm;

View File

@ -0,0 +1,20 @@
import React from 'react';
import {Table, Button} from 'semantic-ui-react';
const AIOListItem = ({i18n, data, editAIO, delAIO}) => {
return (
<Table.Row>
<Table.Cell>{data.name || ''}</Table.Cell>
<Table.Cell>{data.portnum || ''}</Table.Cell>
<Table.Cell>{data.range_min || 0} ~ {data.range_max || 0}</Table.Cell>
<Table.Cell>{data.scale_min || 0} ~ {data.scale_max || 0}</Table.Cell>
<Table.Cell>
<Button type="button" basic size="tiny" content="Edit" onClick={()=>{editAIO(1, data)}} />
<Button type="button" basic size="tiny" content="Delete" onClick={()=>{delAIO(data.uid || '')}} />
</Table.Cell>
</Table.Row>
)
}
export default AIOListItem;

View File

@ -0,0 +1,72 @@
import React from 'react';
import {Modal, Table, Button, Input, Form} from 'semantic-ui-react';
import ListItem from './AIOListItem';
import AIOForm from './AIOForm';
class AIOModal extends React.Component {
state = {
editMode: false,
data: {},
type: 0
}
openEditField = (type, data = {}) => {
this.closeEditField(()=>{
this.setState({
editMode: true,
data,
type
});
});
}
closeEditField = (cb) => {
this.setState({
editMode: false,
data: {}
}, ()=>{
if(cb && typeof cb == 'function') cb()
});
}
submitAIO = (type, data) => {
data.iouid = this.props.iouid;
this.props.onSubmit(type, data);
this.closeEditField();
}
render() {
let {i18n, iouid, open, list, onClose, delAIO} = this.props;
return (
<Modal size="large" open={open} onClose={()=>{onClose()}}>
<Modal.Content className="clearfix">
<Button basic size="tiny" color="green" floated="right" style={{marginBottom: '10px'}} icon="plus" content="Add Set" onClick={()=>{
this.openEditField(0);
}}/>
<Table size="small">
<Table.Header>
<Table.Row>
<Table.HeaderCell>名稱</Table.HeaderCell>
<Table.HeaderCell>接口號碼</Table.HeaderCell>
<Table.HeaderCell>Range(Min-Max)</Table.HeaderCell>
<Table.HeaderCell>Scale(Min-Max)</Table.HeaderCell>
<Table.HeaderCell>操作</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
list.map((t,idx) => (
<ListItem key={idx} i18n={i18n} data={t} editAIO={this.openEditField} delAIO={delAIO}/>
))
}
</Table.Body>
<Table.Footer>
<AIOForm i18n={i18n} open={this.state.editMode} type={this.state.type} data={this.state.data} onSubmit={this.submitAIO} onClose={this.closeEditField}/>
</Table.Footer>
</Table>
</Modal.Content>
</Modal>
)
}
}
export default AIOModal;

View File

@ -0,0 +1,25 @@
import React from 'react';
import {Menu, Header} from 'semantic-ui-react';
import DevListItem from './DeviceListItem';
const DeviceList = ({i18n, list, delModbus, editModbus, showDev, selectDevToShow}) => {
return (
<Menu vertical={true}>
<Menu.Item>
<Menu.Header content="裝置列表" />
<Menu.Menu>
{
list.map((t, idx) => {
return (
<DevListItem key={idx} i18n={i18n} idx={idx} data={t} delModbus={delModbus} editModbus={editModbus} showDev={showDev} selectDevToShow={selectDevToShow}/>
)
})
}
</Menu.Menu>
</Menu.Item>
</Menu>
)
}
export default DeviceList;

View File

@ -0,0 +1,19 @@
import React from 'react';
import {List, Button, Icon} from 'semantic-ui-react';
const DeviceListItem = ({i18n, idx, data, delModbus, editModbus, showDev, selectDevToShow}) =>{
return (
<List.Item active={data.uid == showDev}>
<span style={{cursor: 'pointer'}} onClick={() => {
selectDevToShow(data.uid);
}}>
{data.name} / Node:{data.node}
</span>
<Icon style={{cursor: 'pointer'}} name="trash" onClick={()=>{delModbus(data.uid || '')}}/>
<Icon style={{cursor: 'pointer'}} name="write" onClick={()=>{editModbus(1, data)}}/>
</List.Item>
)
}
export default DeviceListItem;

View File

@ -0,0 +1,63 @@
import React from 'react';
import {Modal, Form, Button, Grid, Input } from 'semantic-ui-react';
const IOModal = ({i18n, open, type, data, onSubmit, onClose}) => {
let iotype = i18n&&i18n.getResource&&i18n.language ? i18n.getResource(i18n.language + '.translation.porttype') : [];
return (
<Modal open={open}>
<Modal.Header content={type == 1 ? '修改資料' : '新增資料'} />
<Modal.Content>
<Form onSubmit={(e,d)=>{
e.preventDefault();
onSubmit(type, d.formData);
}} serializer={e=>{
let json = {
id: data.uid || '',
addr: '',
num: '',
type: ''
};
let addr = e.querySelector('input[name="addr"]');
if(addr && 'value' in addr) json.addr = addr.value;
let num = e.querySelector('input[name="num"]');
if(num && 'value' in num) json.num = num.value;
let type = e.querySelector('select[name="io_type"]');
if(type && 'value' in type) json.type = type.value;
return json;
}}>
<Form.Field>
<label>接口類型</label>
<select name="io_type" defaultValue={data.type || ''} disabled={type == 1}>
<option value="">請選擇類型</option>
{
iotype.map((t,idx) => (
<option key={idx} value={t.code}>{t.name}</option>
))
}
</select>
</Form.Field>
<Form.Field>
<Input name="addr" label="起始位址" defaultValue={data.addr || ''} />
</Form.Field>
<Form.Field>
<Input name="num" label="數量" defaultValue={data.num || ''} />
</Form.Field>
<Grid columns={2}>
<Grid.Column>
<Button type="submit" fluid content="Submit" />
</Grid.Column>
<Grid.Column>
<Button type="button" fluid content="Cancel" onClick={()=>{onClose()}}/>
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
</Modal>
)
}
export default IOModal ;

View File

@ -0,0 +1,89 @@
import React from 'react';
import {Menu, Segment, Table, Header, Label} from 'semantic-ui-react';
import {convertTime} from '../../../tools';
import IOPanelListItem from './IOPanelListItem';
class IOPanel extends React.Component {
state = {
tabIdx: '1'
}
tabItemClick = (e, el) => {
this.setState({
tabIdx: el.name
}, ()=>{
this.props.getStatus({id: this.props.data.uid || '', type: this.state.tabIdx});
});
}
componentWillReceiveProps(nprops) {
if(nprops.data && nprops.data.uid) {
if(this.props.data && this.props.data.uid){
if(this.props.data.uid != nprops.data.uid) {
this.props.getStatus({id: nprops.data.uid, type: this.state.tabIdx});
}
}else{
this.props.getStatus({id: nprops.data.uid, type: this.state.tabIdx});
}
}
}
render(){
let {i18n, show, data, ioModal, delIOList, showAIOSet} = this.props;
if(!show) return null;
let iolist = data.iolist || [];
let ios = iolist.filter(t => {
if(t.type == this.state.tabIdx) return t;
});
let status = data.status || {};
let ss = status[this.state.tabIdx] || [];
return (
<div>
<Menu attached="top" tabular>
<Menu.Item content="DigitOutput" name="1" active={this.state.tabIdx == "1"} onClick={this.tabItemClick} />
<Menu.Item content="DigitInput" name="2" active={this.state.tabIdx == "2"} onClick={this.tabItemClick} />
<Menu.Item content="AnalogyOutput" name="3" active={this.state.tabIdx == "3"} onClick={this.tabItemClick} />
<Menu.Item content="AnalogyInput" name="4" active={this.state.tabIdx == "4"} onClick={this.tabItemClick} />
<Menu.Menu position="right">
<Menu.Item content="AddIO" icon="plus" onClick={()=>{ioModal(0)}}/>
</Menu.Menu>
</Menu>
<Segment attached="bottom">
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>起始位置</Table.HeaderCell>
<Table.HeaderCell>接口數量</Table.HeaderCell>
<Table.HeaderCell>操作</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
ios.map((t,idx) => (
<IOPanelListItem key={idx} i18n={i18n} data={t} ioModal={ioModal} delIOList={delIOList} showAIOSet={showAIOSet} />
))
}
</Table.Body>
</Table>
<Segment>
<Header as="h3" content="接口資訊"/>
{
ss.map((t,idx) => (
<div key={idx}>
<Label basic color="blue" content={`PortNum:${t.port}`}/>
<Label basic color="blue" content={`原始數值:${t.value}`}/>
<Label basic color="blue" content={`轉換數值:${t.value2}`}/>
<Label basic color="green" content={`最後更新於:${convertTime(t.tst, true)}`}/>
</div>
))
}
</Segment>
</Segment>
</div>
)
}
}
export default IOPanel;

View File

@ -0,0 +1,24 @@
import React from 'react';
import {Table, Button} from 'semantic-ui-react';
const IOPanelListItem = ({i18n, data, ioModal, delIOList, showAIOSet}) => {
return (
<Table.Row>
<Table.Cell>{data.addr || ''}</Table.Cell>
<Table.Cell>{data.num || ''}</Table.Cell>
<Table.Cell>
<Button type="button" basic content="修改" onClick={()=>{ioModal(1, data)}}/>
<Button type="button" basic content="刪除" onClick={()=>{delIOList(data.uid || '')}}/>
{
data.type == 3 || data.type == 4 ?
(
<Button type="button" basic content="顯示設定" onClick={()=>{showAIOSet(data.uid)}} />
):null
}
</Table.Cell>
</Table.Row>
)
}
export default IOPanelListItem;

View File

@ -0,0 +1,49 @@
import React from 'react';
import {Modal, Form, Input, Grid, Button} from 'semantic-ui-react';
const ModbusModal = ({i18n, open, type, data, onSubmit, onClose}) => {
if(!i18n || Object.keys(i18n).length == 0) return null;
return (
<Modal open={open}>
<Modal.Header content={type == 1 ? '修改裝置' : '新增裝置'} />
<Modal.Content>
<Form onSubmit={(e, d) => {
e.preventDefault();
onSubmit(type, d.formData);
}} serializer={e => {
let json = {
name: '',
node: '',
id: data.uid || '',
original_node: data.node || ''
};
let n = e.querySelector('input[name="name"]');
if(n && 'value' in n) json.name = n.value;
let nn = e.querySelector('input[name="node"]');
if(nn && 'value' in nn) json.node = nn.value;
return json ;
}}>
<Form.Field>
<Input name="name" defaultValue={data.name || ''} label="Name"/>
</Form.Field>
<Form.Field>
<Input name="node" defaultValue={data.node || ''} label="Node"/>
</Form.Field>
<Grid columns={2}>
<Grid.Column>
<Button content="Submit" fluid type="submit" />
</Grid.Column>
<Grid.Column>
<Button content="Cancel" fluid type="button" onClick={()=>{onClose()}} />
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
</Modal>
)
}
export default ModbusModal;

View File

@ -0,0 +1,210 @@
import React from 'react';
import {Container, Segment, List, Menu, Item, Grid} from 'semantic-ui-react';
import DeviceList from './DeviceList';
import ModbusModal from './ModbusModal';
import IOPanel from './IOPanel';
import IOModal from './IOModal';
import AIOModal from './AIOModal';
const defIOModalState = {
open: false,
data: {},
type: 0
}
const defAIOModalState = {
open: false,
list: [],
iouid: 0
}
class ModbusPage extends React.Component {
state = {
mbModal: false,
mbType: 0,
mbData: {},
showDev: "",
ioModal:{
...defIOModalState
},
aioModal:{
...defAIOModalState
}
}
componentDidMount() {
this.props.getList();
this.props.router.setRouteLeaveHook(this.props.route, () => {
this.props.clearList();
});
}
componentWillReceiveProps(nprop) {
if(this.state.aioModal.open) {
this.openAIOModal(this.state.aioModal.iouid, nprop.list);
}
}
delModbus = id => {
if(!id) return ;
this.props.delModbus({id});
}
openModbusModal = (type, data = {}) => {
this.setState({
mbModal: true,
mbType: type,
mbData: data
});
}
closeModbusModal = () => {
this.setState({
mbModal: false,
mbData: {}
})
}
submitModbusModal = (type, data = {}) => {
let {i18n} = this.props;
if(type == 1 && (!data.id)) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(!data.name || !('node' in data) || data.node.length == 0) return this.props.showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(type == 1){
this.props.editModbus(data);
}else{
this.props.addModbus(data);
}
this.closeModbusModal();
}
selectDevToShow = (uid) => {
this.setState({
showDev: uid
});
this.props.getMBData({id: uid});
}
openIOModal = (type, data = {}) => {
this.setState({
ioModal: {
open: true,
type,
data
}
});
}
closeIOModal = () =>{
this.setState({
ioModal: {
...defIOModalState
}
})
}
submitIOModal = (type, data) => {
let {i18n} = this.props;
if((type == 1 && !data.id) || !('addr' in data) || !('num' in data) || !('type' in data)) return this.props.showDialog(i18n.t('tip.input_empty'));
data.devuid = this.state.showDev;
if(type == 1){
this.props.editIOList(data);
}else{
data.id = this.state.showDev;
this.props.addIOList(data);
}
this.closeIOModal();
}
delIOList = (id) => {
if(!id) return ;
this.props.delIOList({id, devuid: this.state.showDev});
}
openAIOModal = (id, nlist = []) => {
if(!id) return ;
let slist = nlist.length == 0 ? this.props.list : nlist;
let dev = slist.filter(t => {
if(t.uid == this.state.showDev) return t;
});
if(dev.length == 0) return ;
let aio = dev[0].aioset || [];
let list = aio.filter(t => {
if(t.iouid == id) return t;
});
this.setState({
aioModal:{
open: true,
list,
iouid: id
}
});
}
closeAIOModal = () => {
this.setState({
aioModal:{
...defAIOModalState
}
});
}
submitAIO = (type, data) => {
data.devuid = this.state.showDev;
if(type == 1){
this.props.editAIO(data);
}else{
this.props.addAIO(data);
}
// this.closeAIOModal();
}
delAIO = (id) => {
if(!id) return ;
this.props.delAIO({id, devuid: this.state.showDev});
}
render() {
let {i18n, list} = this.props;
let dev = list.filter(t => {
if(t.uid == this.state.showDev) return t;
});
return (
<Container>
<Menu>
<Menu.Menu position="right">
<Menu.Item name="新增裝置" icon="plus" onClick={()=>{this.openModbusModal(0)}}/>
</Menu.Menu>
</Menu>
<Grid>
<Grid.Column width={4}>
<DeviceList i18n={i18n}
list={list}
delModbus={this.delModbus}
editModbus={this.openModbusModal}
showDev={this.state.showDev}
selectDevToShow={this.selectDevToShow}/>
</Grid.Column>
<Grid.Column width={12}>
<IOPanel i18n={i18n}
show={dev.length > 0}
data={dev[0]}
ioModal={this.openIOModal}
delIOList={this.delIOList}
getStatus={this.props.getMBIOStatus}
showAIOSet={this.openAIOModal} />
</Grid.Column>
</Grid>
<ModbusModal i18n={i18n} open={this.state.mbModal} data={this.state.mbData} type={this.state.mbType} onSubmit={this.submitModbusModal} onClose={this.closeModbusModal} />
<IOModal i18n={i18n}
open={this.state.ioModal.open}
type={this.state.ioModal.type}
data={this.state.ioModal.data}
onClose={this.closeIOModal}
onSubmit={this.submitIOModal} />
<AIOModal i18n={i18n}
open={this.state.aioModal.open}
iouid={this.state.aioModal.iouid}
list={this.state.aioModal.list}
onSubmit={this.submitAIO}
delAIO={this.delAIO}
onClose={this.closeAIOModal} />
</Container>
)
}
}
export default ModbusPage;

View File

@ -0,0 +1,120 @@
import React from 'react';
import {Table, Button, Item, Label} from 'semantic-ui-react';
const ListItem = ({i18n, idx, data, dos, les, ios, changeActive, openModal, delItem, showGroup}) => {
let actlist = i18n&&i18n.getResource&&i18n.language ? i18n.getResource(i18n.language + '.translation.action_list') : [];
let weekArr = i18n&&i18n.t ? [
i18n.t('week.mon'),
i18n.t('week.tue'),
i18n.t('week.wed'),
i18n.t('week.thu'),
i18n.t('week.fri'),
i18n.t('week.sat'),
i18n.t('week.sun')
] : [];
let timeStr = '';
let min = data.ioscheduleparam1 || '';
let hour = data.ioscheduleparam2 || '';
let day = data.ioscheduleparam3 || '';
let month = data.ioscheduleparam4 || '';
let week = data.ioscheduleparam5 || '';
if(week != '-'){
let w = week.split(',');
let ws = [];
w.sort();
for(let i in w){
ws.push(weekArr[w[i] - 1] || '');
}
timeStr = `${ws.join(',')} ${hour}:${min}`;
}else{
timeStr = `${month}/${day} ${hour}:${min}`;
}
let act = data.ioschedulecmd || '';
act = act.split(',');
let actcmd = '';
let actname = '';
if(act.length == 2){
if(act[0] == 2){
actcmd = '2'
}else{
actcmd = act.join(' ');
}
}
if(actcmd.length > 0){
for(let i in actlist){
if(actlist[i].cmd == actcmd){
actname = actlist[i].name;
}
}
}
let devs = data.ioscheduleio || '';
let dev = devs.split(',');
let items = [];
if(dev.length > 0){
for(let i in dev){
if(/^do/i.test(dev[i])){
for(let j in dos){
if(`do${dos[j].douid}` == dev[i]) {
items.push({
type: 'DigitOutput',
name: dos[j].doname,
id: dev[i]
});
break;
}
}
}else if(/^le/i.test(dev[i])){
for(let j in les){
if(`le${les[j].leonelistuid}` == dev[i]) {
items.push({
type: 'LeOne',
name: les[j].leonename,
id: dev[i]
});
break;
}
}
}else if(/^iogroup/i.test(dev[i])){
for(let j in ios){
if(`iogroup${ios[j].iogroupuid}` == dev[i]){
items.push({
type: 'IOGroup',
name: ios[j].iogroupname,
id: dev[i]
});
}
}
}
}
}
return (
<Table.Row>
<Table.Cell>
<Button basic size="tiny" color={data.ioscheduleactive == 1 ? 'orange' : 'olive'}
content={i18n&&i18n.t ? (data.ioscheduleactive == 1 ? i18n.t('page.schedule.table.button.disable') : i18n.t('page.schedule.table.button.enable')) : ''}
onClick={()=>{ changeActive(data.ioscheduleuid || ''); }} />
<Button basic size="tiny" color="red" content={i18n&&i18n.t ? i18n.t('page.schedule.table.button.del') : ''} onClick={() => {delItem(data.ioscheduleuid || '')}} />
<Button basic size="tiny" color="blue" content={i18n&&i18n.t ? i18n.t('page.schedule.table.button.edit') : ''} onClick={() => {openModal(1, data, items);}} />
</Table.Cell>
<Table.Cell>{data.ioschedulename || ''}</Table.Cell>
<Table.Cell>{timeStr}</Table.Cell>
<Table.Cell>{actname}</Table.Cell>
<Table.Cell>{i18n&&i18n.t ? (data.ioscheduleactive == 1 ? i18n.t('page.schedule.table.button.enable') : i18n.t('page.schedule.table.button.disable')) : ''}</Table.Cell>
<Table.Cell>
{
items.length > 0 ?
(items.length == 1 ?
<Item><Label content={items[0].type} />{items[0].name}</Item>:
<Button size="tiny" basic content={i18n&&i18n.t ? i18n.t('page.schedule.table.button.showgroup') : ''} onClick={()=>{showGroup(items)}}/> ) :
''
}
</Table.Cell>
</Table.Row>
)
}
export default ListItem;

View File

@ -0,0 +1,158 @@
import React from 'react';
import {Modal, Form, Checkbox, Input, Radio, Segment, Header, List, Grid, Button} from 'semantic-ui-react';
import DateTime from 'react-datetime';
import DeviceSelect from '../../Common/DeviceSelect';
import SelectedItem from './SelectedItem';
class ScheduleModal extends React.Component {
render(){
let {i18n, open, data, type, devs, permissions, querySelectList, onSubmit, onClose} = this.props;
let {showTemp, week, cmd, temp, dateType, selected, addSelect, removeSelected, changeDateType, checkShowTemp, changeWeek} = this.props;
let actlist = i18n&&i18n.getResource&&i18n.language ? i18n.getResource(i18n.language + '.translation.action_list') : [];
let act = data.ioschedulecmd || '';
act = act.split(',');
let defcmd = '';
let deftemp = '';
if(act.length == 2){
if(act[0] == '2') {
defcmd = act[0];
deftemp = act[1];
}else {
defcmd = act.join(' ');
}
}
return (
<Modal open={open}>
<Modal.Header content={i18n&&i18n.t ? (type == 1 ? i18n.t('page.schedule.form.title.edut') : i18n.t('page.schedule.form.title.add')) : ''}/>
<Modal.Content>
<Form onSubmit={(e, d) => {
e.preventDefault();
onSubmit(type, d.formData);
}} serializer={e => {
let json = {
active: false,
name: '',
cmd: '',
temp: '',
time: '',
date: '',
id: data.ioscheduleuid || ''
};
let active = e.querySelector('input[name="active"]');
if(active && 'checked' in active) json.active = active.checked;
let name = e.querySelector('input[name="name"]');
if(name && 'value' in name) json.name = name.value;
let cmd = e.querySelector('select[name="act"]');
if(cmd && 'value' in cmd) json.cmd = cmd.value;
let time = e.querySelector('#timeDiv input');
if(time && 'value' in time) json.time = time.value;
let date = e.querySelector('#dateDiv input');
if(date && 'value' in date) json.date = date.value;
let temp = e.querySelector('input[name="temp"]');
if(temp && 'value' in temp) json.temp = temp.value;
return json;
}}>
<Form.Field>
{/*<label>{i18n&&i18n.t ? i18n.t('page.schedule.form.label.enable') : ''}</label>*/}
<Checkbox defaultChecked={data.ioscheduleactive == 1 ? true : false} name="active" label={i18n&&i18n.t ? i18n.t('page.schedule.form.label.enable') : ''} />
</Form.Field>
<Form.Field>
<Input label={i18n&&i18n.t ? i18n.t('page.schedule.form.label.name') : ''} name="name" defaultValue={data.ioschedulename} />
</Form.Field>
<Form.Field>
<label>{i18n&&i18n.t?i18n.t('page.schedule.form.label.action') : ''}</label>
<select name="act" value={cmd} onChange={(e) => { checkShowTemp(e.target.value); }}>
<option value="">{i18n&&i18n.t ? i18n.t('select.select_action') : ''}</option>
{
actlist.map((t, idx) => {
{/*let selected = defcmd == t.cmd ? 'selected' : 'false';*/}
return (
<option key={idx} value={t.cmd} >{t.name}</option>
)
})
}
</select>
</Form.Field>
{
showTemp ?
(<Form.Field>
<Input label={i18n&&i18n.t ? i18n.t('page.schedule.form.label.temp') : ''} name="temp" defaultValue={temp} />
</Form.Field>)
: null
}
<Form.Field id="timeDiv">
<label>{i18n&&i18n.t?i18n.t('page.schedule.form.label.time') : ''}</label>
<DateTime dateFormat={false} timeFormat="HH:mm" defaultValue={data.ioscheduleparam1&&data.ioscheduleparam2 ? `${data.ioscheduleparam2}:${data.ioscheduleparam1}` : ''} />
</Form.Field>
<Form.Field>
<label>{i18n&&i18n.t ? i18n.t('page.schedule.form.label.datetype') : ''}</label>
<Radio label={i18n&&i18n.t ? i18n.t('page.schedule.form.label.type_week') : ''} name="dtRadio" value="w" checked={dateType == 'w'} onChange={(e, {value}) => {changeDateType(value)}} />
<Radio label={i18n&&i18n.t ? i18n.t('page.schedule.form.label.type_date') : ''} name="dtRadio" value="d" checked={dateType == 'd'} onChange={(e, {value}) => {changeDateType(value)}} />
</Form.Field>
{
dateType == 'w' ?
(
<Form.Group inline>
<label>{i18n&&i18n.t ? i18n.t('page.schedule.form.label.week') : ''}</label>
<Form.Field>
<Checkbox label={i18n&&i18n.t ? i18n.t('week.mon') : ''} value="1" checked={week.find(t => t == 1) ? true : false} onChange={(e, {value, checked}) => {changeWeek(checked, value)}}/>
</Form.Field>
<Form.Field>
<Checkbox label={i18n&&i18n.t ? i18n.t('week.tue') : ''} value="2" checked={week.find(t => t == 2) ? true : false} onChange={(e, {value, checked}) => {changeWeek(checked, value)}}/>
</Form.Field>
<Form.Field>
<Checkbox label={i18n&&i18n.t ? i18n.t('week.wed') : ''} value="3" checked={week.find(t => t == 3) ? true : false} onChange={(e, {value, checked}) => {changeWeek(checked, value)}}/>
</Form.Field>
<Form.Field>
<Checkbox label={i18n&&i18n.t ? i18n.t('week.thu') : ''} value="4" checked={week.find(t => t == 4) ? true : false} onChange={(e, {value, checked}) => {changeWeek(checked, value)}}/>
</Form.Field>
<Form.Field>
<Checkbox label={i18n&&i18n.t ? i18n.t('week.fri') : ''} value="5" checked={week.find(t => t == 5) ? true : false} onChange={(e, {value, checked}) => {changeWeek(checked, value)}}/>
</Form.Field>
<Form.Field>
<Checkbox label={i18n&&i18n.t ? i18n.t('week.sat') : ''} value="6" checked={week.find(t => t == 6) ? true : false} onChange={(e, {value, checked}) => {changeWeek(checked, value)}}/>
</Form.Field>
<Form.Field>
<Checkbox label={i18n&&i18n.t ? i18n.t('week.sun') : ''} value="7" checked={week.find(t => t == 7) ? true : false} onChange={(e, {value, checked}) => {changeWeek(checked, value)}}/>
</Form.Field>
</Form.Group>
) :
(
<Form.Field id="dateDiv">
<label>{i18n&&i18n.t?i18n.t('page.schedule.form.label.date'):''}</label>
<DateTime timeFormat={false} dateFormat="MM-DD" defaultValue={`${data.ioscheduleparam4}-${data.ioscheduleparam3}`} />
</Form.Field>
)
}
<DeviceSelect i18n={i18n} devs={devs} addSelect={addSelect} showGroup={true} permissions={permissions} querySelectList={querySelectList} page="schedule" />
<Segment>
<Header as="h4" content={i18n&&i18n.t ? i18n.t('page.schedule.form.label.selected_device') : ''} />
<List verticalAlign="middle" divided>
{
selected.map((t, idx) => (
<SelectedItem key={idx} i18n={i18n} data={t} idx={idx} removeSelected={removeSelected} />
))
}
</List>
</Segment>
<Grid columns={2}>
<Grid.Column>
<Button fluid type="submit" content={i18n&&i18n.t?i18n.t('page.leone.form.button.submit'):''}/>
</Grid.Column>
<Grid.Column>
<Button fluid type="button" content={i18n&&i18n.t?i18n.t('page.leone.form.button.cancel'):''} onClick={() => {onClose()}}/>
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
</Modal>
)
}
}
export default ScheduleModal;

View File

@ -0,0 +1,17 @@
import React from 'react';
import {List, Button, Label} from 'semantic-ui-react';
const SelectedItem = ({i18n, idx, data, removeSelected}) => {
return(
<List.Item>
<List.Content floated="right">
<Button type="button" size="mini" content={i18n&&i18n.t ? i18n.t('page.schedule.form.button.remove') : ''} onClick={() => {removeSelected(idx)}} />
</List.Content>
<Label content={data.type || ''} />
{data.name || ''}
</List.Item>
)
}
export default SelectedItem;

View File

@ -0,0 +1,297 @@
import React from 'react';
import {Container, Segment, Table, Button, Modal, Item, Label} from 'semantic-ui-react';
import ListItem from './ListItem';
import ScheduleModal from './ScheduleModal';
class SchedulePage extends React.Component {
state ={
modal: false,
modalType: 0,
modalData: {},
modalWeek: [],
modalShowTemp: false,
modalDateType: 'w',
modalSelected: [],
modalCmd: '',
modalTemp: '',
itemModal: false,
itemModalData: []
}
componentDidMount() {
this.props.getScheduleList();
this.props.router.setRouteLeaveHook(this.props.route, () => {
this.props.clearList();
})
}
changeActive = (id) => {
if(!id) return ;
this.props.swSchedule({id});
}
delItem = (id) => {
if(!id) return ;
this.props.delSchedule({id});
}
openModal = (type, data = {}, items = []) => {
let json = {
modal: true,
modalType: type,
modalData: data,
modalSelected: [...items]
}
if(type == 1){
let dtype = '';
if(data.ioscheduleparam5 && data.ioscheduleparam5 != '-'){
let ws = data.ioscheduleparam5.split(',');
let arr = [];
dtype = 'w';
for(let i in ws){
if(isFinite(ws[i]) && ws[i] >= 1 && ws[i] <= 7){
arr = [...arr, ws[i]];
}
}
json = {
...json,
modalWeek: [...arr]
};
// this.setState({
// week: [...arr]
// });
}else{
dtype = 'd';
}
let act = data.ioschedulecmd || '';
act = act.split(',');
if(act.length == 2){
if(act[0] == '2') {
json = {
...json,
modalShowTemp: true,
modalCmd: act[0],
modalTemp: act[1]
}
}else {
json ={
...json,
modalShowTemp: false,
modalCmd: act.join(' '),
modalTemp: ''
}
}
}
json = {
...json,
modalDateType: dtype
}
// this.setState({
// modalDateType: dtype
// })
}
this.setState({
...json
});
}
closeModal = () => {
this.setState({
modal: false,
modalType: 0,
modalData: {},
modalWeek: [],
modalShowTemp: false,
modalDateType: 'w',
modalSelected: [],
modalCmd: '',
modalTemp: ''
})
}
submitModal = (type, data) => {
let {i18n, showDialog} = this.props;
let json = {};
if(type == 1 && !data.id) return showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if(!data.name || !data.time || !data.cmd || (data.cmd == 2 && !data.temp) || (this.state.modalDateType == 'd' && !data.date))
return showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty') : '');
if((this.state.modalDateType == 'w' && this.state.modalWeek.length == 0)) return showDialog(i18n&&i18n.t ? i18n.t('tip.select_week') : '');
json.name = data.name;
json.active = data.active ? 1 : 0;
if(!/^\d{1,2}:\d{1,2}$/.test(data.time)) return showDialog(i18n&&i18n.t ? i18n.t('tip.input_format') : '');
let tarr = data.time.split(':');
if(tarr[0] < 0 || tarr[0] > 23 || tarr[1] < 0 || tarr[1] > 59) return showDialog(i18n&&i18n.t ? i18n.t('tip.time_range') : '');
json.min = tarr[1];
json.hour = tarr[0];
if(this.state.modalDateType == 'w'){
json.week = this.state.modalWeek.join(',');
}else{
json.day = '';
json.month = '';
if(!/^\d{1,2}\-\d{1,2}$/.test(data.date)) return showDialog(i18n&&i18n.t ? i18n.t('tip.date_format') : '');
let darr = data.date.split('-');
if(darr[0] < 1 || darr[0] > 12 || darr[1] < 1 || darr[1] > 31) return showDialog(i18n&&i18n.t ? i18n.t('tip.date_range') : '');
json.day = darr[1];
json.month = darr[0];
}
let tmp = this.state.modalSelected.map(t => t.id);
json.devs = tmp.join(',');
if(data.cmd == 2){
if(!data.temp) return showDialog(i18n&&i18n.t ? i18n.t('tip.input_empty_temp') : '');
if(!(data.temp >= 16 && data.temp <= 30)) return showDialog(i18n&&i18n.t ? i18n.t('tip.temp_format') : '');
json.action = `${data.cmd},${data.temp}`;
}else{
json.action = data.cmd.replace(' ', ',');
}
json.id = data.id;
if(type == 0){
this.props.addSchedule(json);
}else{
this.props.editSchedule(json);
}
this.closeModal();
}
showGroup = (items) => {
this.setState({
itemModal: true,
itemModalData: [...items]
});
}
closeGroupModal = () => {
this.setState({
itemModal: false,
itemModalData: []
});
}
querySelectList = (type) => {
this.props.getSelectList({type});
}
// ================================
addSelect = (data) => {
if(!data) return;
this.setState({
modalSelected: [...this.state.modalSelected, data]
})
}
removeSelected = (idx) => {
let items = [...this.state.modalSelected];
items.splice(idx, 1);
this.setState({
modalSelected: [...items]
});
}
checkShowTemp = (val) => {
this.setState({
modalShowTemp: val == 2 ? true : false,
modalCmd: val
});
}
changeDateType = (type) => {
if(!type) return;
this.setState({
modalDateType: type
})
}
changeWeek = (checked, value) => {
let week = this.state.modalWeek;
if(checked){
if(week.indexOf(value) == -1){
week.push(value);
}
}else{
if(week.indexOf(value) != -1){
week.splice(week.indexOf(value), 1);
}
}
this.setState({
modalWeek: [...week]
});
}
// ================================
render() {
let {i18n, devs, list, dos, les, ios, permissions} = this.props;
return (
<Container>
<Segment className="clearfix">
<Button type="button" floated="right" icon="plus" basic color="green" content={i18n&&i18n.t ? i18n.t('page.schedule.table.button.add') : ''} style={{marginBottom: '15px'}} onClick={() => {this.openModal(0)}}/>
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.schedule.table.operate') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.schedule.table.name') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.schedule.table.schedule_time') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.schedule.table.action') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.schedule.table.active_status') : ''}</Table.HeaderCell>
<Table.HeaderCell>{i18n&&i18n.t ? i18n.t('page.schedule.table.device') : ''}</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{
list.map((t,idx) => (<ListItem key={idx} i18n={i18n} data={t} idx={idx} dos={dos} ios={ios} les={les} openModal={this.openModal} changeActive={this.changeActive} delItem={this.delItem} showGroup={this.showGroup} />))
}
</Table.Body>
</Table>
</Segment>
<ShowGroupItem i18n={i18n} open={this.state.itemModal} items={this.state.itemModalData} onClose={this.closeGroupModal} />
<ScheduleModal i18n={i18n}
open={this.state.modal}
data={this.state.modalData}
type={this.state.modalType}
querySelectList={this.querySelectList}
permissions={permissions}
devs={devs}
selected={this.state.modalSelected}
week={this.state.modalWeek}
showTemp={this.state.modalShowTemp}
dateType={this.state.modalDateType}
cmd={this.state.modalCmd}
temp={this.state.modalTemp}
addSelect={this.addSelect}
checkShowTemp={this.checkShowTemp}
removeSelected={this.removeSelected}
changeDateType={this.changeDateType}
changeWeek={this.changeWeek}
onClose={this.closeModal}
onSubmit={this.submitModal} />
</Container>
)
}
}
const ShowGroupItem = ({i18n,open,items, onClose}) => {
return (
<Modal open={open} onClose={() => {onClose()}} size="small">
<Modal.Content>
{
items.map((t, idx) => <Item key={idx}><Label content={t.type}/>{t.name}</Item>)
}
</Modal.Content>
</Modal>
)
}
export default SchedulePage;

View File

@ -0,0 +1,159 @@
import React from 'react';
import {} from '../../../actions';
import Datetime from 'react-datetime';
import {Container, Segment, Form, Header, Menu, Grid, Table, Input, Button} from 'semantic-ui-react';
class NetForm extends React.Component {
state = {
input: this.props.network && 'NETWORKMODE' in this.props.network && this.props.network['NETWORKMODE'] == 1 ? false : true,
ip: '',
netmask: '',
gateway: '',
dns: ''
}
changeActive = (active) => {
this.setState({input: active});
}
setInputState = (name, val) => {
let json = {};
json[name] = val;
this.setState(json);
}
componentWillReceiveProps(nextProps) {
// if(this.props.network['IP'] != nextProps.network['IP']){
// this.setState({ip: nextProps.network.ip});
this.setInputState('ip', nextProps.network['IP']);
// }
// if(this.props.network['NETMASK'] != nextProps.network['NETMASK']){
// this.setState({netmask: nextProps.network.netmask});
this.setInputState('netmask', nextProps.network['NETMASK']);
// }
// if(this.props.network['GATEWAY'] != nextProps.network['GATEWAY']){
// this.setState({gateway: nextProps.network['GATEWAY]});
this.setInputState('gateway', nextProps.network['GATEWAY']);
// }
// if(this.props.network['DNS'] !== nextProps.network['DNS']){
// this.setState({dns: nextProps.network['DNS]});
this.setInputState('dns', nextProps.network['DNS']);
// }
}
render() {
let {i18n, network, onSubmit} = this.props;
return (
<div>
<Menu widths={2}>
<Menu.Item active={this.state.input} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.button.dhcpip') : ''} onClick={()=>{this.changeActive(true)}}/>
<Menu.Item active={!this.state.input} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.button.manualip') : ''} onClick={()=>{this.changeActive(false)}}/>
</Menu>
<Form onSubmit={(e,data) => {
e.preventDefault();
onSubmit(data.formData);
}} serializer={e => {
let json = {
ip: e.querySelector('input[name="ip"]').value,
netmask: e.querySelector('input[name="netmask"]').value,
gateway: e.querySelector('input[name="gateway"]').value,
dns: e.querySelector('input[name="dns"]').value,
dhcpMode: this.state.input
}
return json;
}}>
<Table>
<Table.Body>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.ip') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Input fluid name="ip" value={this.state.ip} disabled={this.state.input} onChange={e=>{this.setInputState('ip', e.target.value)}}/>
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.netmask') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Input fluid name="netmask" value={this.state.netmask} disabled={this.state.input} onChange={e=>{this.setInputState('netmask', e.target.value)}}/>
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.gateway') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Input fluid name="gateway" value={this.state.gateway} disabled={this.state.input} onChange={e=>{this.setInputState('gateway', e.target.value)}}/>
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.dns') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Input fluid name="dns" value={this.state.dns} disabled={this.state.input} onChange={e=>{this.setInputState('dns', e.target.value)}}/>
</Table.Cell>
</Table.Row>
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.Cell colSpan="2">
<Button type="submit" fluid content={i18n && 't' in i18n ? i18n.t('page.system_info.form.button.update_network') :''}/>
</Table.Cell>
</Table.Row>
</Table.Footer>
</Table>
</Form>
</div>
)
}
}
/*const NetForm = ({i18n, onSubmit, network}) => (
<Form onSubmit={(e,data) => {
e.preventDefault();
onSubmit(data.formData);
}} serializer={e => {
let json = {
ip: e.querySelector('input[name="ip"]').value,
netmask: e.querySelector('input[name="netmask"]').value,
gateway: e.querySelector('input[name="gateway"]').value,
dns: e.querySelector('input[name="dns"]').value
}
return json;
}}>
<Table>
<Table.Body>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.ip') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Input fluid name="ip" value={network['IP'] || ''} />
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.netmask') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Input fluid name="netmask" value={network['NETMASK'] || ''} />
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.gateway') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Input fluid name="gateway" value={network['GATEWAY'] || ''} />
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.dns') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Input fluid name="dns" value={network['DNS'] || ''} />
</Table.Cell>
</Table.Row>
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.Cell colSpan="2">
<Button type="submit" fluid content={i18n && 't' in i18n ? i18n.t('page.system_info.form.button.update_network') :''}/>
</Table.Cell>
</Table.Row>
</Table.Footer>
</Table>
</Form>
)*/
export default NetForm;

View File

@ -0,0 +1,40 @@
import React from 'react';
import {} from '../../../actions';
import Datetime from 'react-datetime';
import {Container, Segment, Form, Header, Menu, Grid, Table, Input, Button} from 'semantic-ui-react';
import {convertTime} from '../../../tools';
const TimeForm = ({i18n, time, onSubmit}) => {
return (
<Form onSubmit={(e,data) => {
e.preventDefault();
onSubmit(data.formData);
}} serializer={e => {
let json = {};
json.time = e.querySelector('input').value || '';
return json;
}}>
<Table>
<Table.Body>
<Table.Row>
<Table.Cell width={8} content={i18n && 't' in i18n ? i18n.t('page.system_info.form.label.sysdate') : ''} textAlign="center"/>
<Table.Cell width={8} textAlign="center" >
<Datetime dateFormat="YYYY-MM-DD" timeFormat="HH:mm" value={convertTime(time)} input={true} />
</Table.Cell>
</Table.Row>
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.Cell colSpan="2">
<Button type="submit" fluid content={i18n && 't' in i18n ? i18n.t('page.system_info.form.button.update_time') :''} />
</Table.Cell>
</Table.Row>
</Table.Footer>
</Table>
</Form>
)
}
export default TimeForm;

View File

@ -0,0 +1,57 @@
import React from 'react';
import {toggle_loading, get_network_info, get_system_time, add_dialog_msg,set_network_info, set_system_time} from '../../../actions';
import Datetime from 'react-datetime';
import {Container, Segment, Form, Header, Menu, Grid, Table, Input} from 'semantic-ui-react';
import NetForm from './NetForm';
import TimeForm from './TimeForm';
import {convertTime,padding} from '../../../tools';
class SysInfo extends React.Component {
componentDidMount(){
let {dispatch} = this.props;
// get network
dispatch(get_network_info());
//get time
dispatch(get_system_time());
}
netSubmit = (data) => {
let {dispatch, i18n} = this.props;
if(!data.dhcpMode){
if(!data.ip || !data.netmask || !data.gateway || !data.dns) return dispatch(add_dialog_msg(i18n && 't' in i18n ? i18n.t('tip.input_empty') : ''));
}
dispatch(set_network_info(data.dhcpMode, data.ip, data.netmask, data.gateway, data.dns));
}
timeSubmit = (data) => {
let {dispatch, i18n} = this.props;
let regex_date = /^([0-9]{4})\-([0-9]{2})\-([0-9]{2})\s([0-9]{1,2}):([0-9]{1,2})$/;
if(!('time' in data) || !data.time.trim() || !regex_date.test(data.time.trim())) return dispatch(add_dialog_msg(i18n && 't' in i18n ? i18n.t('tip.datetime_format') : ''));
let m = data.time.trim().match(regex_date);
if(!m) return dispatch(add_dialog_msg(i18n && 't' in i18n ? i18n.t('tip.datetime_format') : ''));
// MMDDHHmmYYYY
let dstr = `${m[2]}${m[3]}${padding(m[4], 2)}${padding(m[5], 2)}${m[1]}`;
dispatch(set_system_time(dstr));
}
render() {
let {i18n, network, time} = this.props;
return (
<Container>
<Segment>
<Header as="h2" content={i18n && 't' in i18n ? i18n.t('page.system_info.title.sysinfo') : ''} />
<NetForm i18n={i18n} network={network} onSubmit={this.netSubmit}/>
</Segment>
<Segment style={{marginBottom: '100px'}}>
<Header as="h2" content={i18n && 't' in i18n ? i18n.t('page.system_info.title.timeinfo') : ''} />
<TimeForm i18n={i18n} time={time} onSubmit={this.timeSubmit}/>
</Segment>
</Container>
)
}
}
export default SysInfo;

View File

@ -0,0 +1,26 @@
import React from 'react';
import {Table, Button} from 'semantic-ui-react';
const ListItem = ({i18n, user, openModal, delUser}) => {
let write = sessionStorage.getItem('write_privilege');
let account = sessionStorage.getItem('account');
return (
<Table.Row>
<Table.Cell>{user.account}</Table.Cell>
<Table.Cell>
{user.write_privilege == 1 ? 'W' : ''}{user.write_privilege == 1 && user.read_privilege == 1 ? ' / ' : ''}{user.read_privilege == 1 ? 'R' : ''}
</Table.Cell>
<Table.Cell>
{
(write == 1 && (user.account != 'admin' || (account == 'admin') ) ) || account == user.account ?
<Button type="button" content={i18n && i18n.t ? i18n.t('page.userlist.table.button.edit') : ''} onClick={() => openModal(user.account)}/> :
null
}
{write == 1 && user.account != 'admin' ? <Button type="button" content={i18n && i18n.t ? i18n.t('page.userlist.table.button.del') : ''} onClick={()=>delUser(user.account)}/> : null }
</Table.Cell>
</Table.Row>
)
}
export default ListItem;

View File

@ -0,0 +1,50 @@
import React from 'react';
import {Modal, Form, Input, Grid, Button, Checkbox} from 'semantic-ui-react';
const UModal = ({i18n, open, type, data, closeModal, onSubmit}) => {
return (
<Modal closeOnDimmerClick={false} open={open}>
<Modal.Content>
<Form onSubmit={(e, data) => {
e.preventDefault();
onSubmit(data.formData);
}}
serializer={e=>{
let json = {
type,
account: e.querySelector('input[name="account"]').value,
password: e.querySelector('input[name="password"]').value,
read_privilege: e.querySelector('input[name="read_privilege"]').checked ? 1 : 0,
write_privilege: e.querySelector('input[name="write_privilege"]').checked ? 1 : 0
};
return json;
}}>
<Form.Field>
<Input label={i18n && i18n.t ? i18n.t('page.userlist.form.label.account') : ''} name="account" disabled={type == 1} defaultValue={type == 1 ? data.account : ''} />
</Form.Field>
<Form.Field>
<Input type="password" label={i18n && i18n.t ? i18n.t('page.userlist.form.label.password') : ''} name="password" />
</Form.Field>
<Form.Field>
<Checkbox label={i18n && i18n.t ? i18n.t('page.userlist.form.label.read_privilege') : ''} defaultChecked={data.read_privilege == 1} name="read_privilege" />
</Form.Field>
<Form.Field>
<Checkbox label={i18n && i18n.t ? i18n.t('page.userlist.form.label.write_privilege') : ''} defaultChecked={data.write_privilege == 1} name="write_privilege" />
</Form.Field>
<Grid>
<Grid.Column width={8}>
<Button type="submit" fluid content={i18n && i18n.t ? i18n.t('page.userlist.form.button.submit') : ''} />
</Grid.Column>
<Grid.Column width={8}>
<Button type="button" fluid content={i18n && i18n.t ? i18n.t('page.userlist.form.button.cancel') : ''} onClick={() => closeModal()} />
</Grid.Column>
</Grid>
</Form>
</Modal.Content>
</Modal>
)
}
export default UModal;

View File

@ -0,0 +1,89 @@
import React from 'react';
import {Container, Segment, Header, Table, Button, Modal} from 'semantic-ui-react';
import ListItem from './ListItem';
import {get_user_list, add_dialog_msg, edit_user, add_user, del_user} from '../../../actions';
import UModal from './UserModal';
class UList extends React.Component {
state ={
edit: false,
// 0 is add , 1 is edit
editType: 0,
editData: {}
}
openModal = (acc) => {
if(!acc) return this.setState({edit: true, editType: 0});
let {users} = this.props;
let user = users.filter(t => t.account == acc);
this.setState({
edit: true,
editType: 1,
editData: user[0] || {}
});
}
closeModal = () => {
this.setState({edit: false, editData: {}});
}
onModalSubmit = (data) => {
let {i18n} = this.props;
if(data.type == 0 && (!data.account || !data.password)) return this.props.showDialog(i18n && i18n.t ? i18n.t('tip.input_empty') : '');
this.setState({edit: false, editData: {}});
if(data.type == 1){
this.props.editUser(data);
}else{
this.props.addUser(data);
}
}
delUser = (acc) => {
if(!acc) return ;
this.props.delUser({account: acc});
}
componentDidMount(){
this.props.getList();
this.props.router.setRouteLeaveHook(this.props.route, () => {
this.props.clearList();
})
}
render () {
let {i18n, users} = this.props;
return (
<Container>
<Segment>
<Header as="h2" content={i18n && 't' in i18n ? i18n.t('page.userlist.title.userlist') : ''} />
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell width={6} content={i18n && i18n.t ? i18n.t('page.userlist.table.account') : ''} />
<Table.HeaderCell width={6} content={i18n && i18n.t ? i18n.t('page.userlist.table.privilege') : ''} />
<Table.HeaderCell width={4} content={i18n && i18n.t ? i18n.t('page.userlist.table.operate') : ''} />
</Table.Row>
</Table.Header>
<Table.Body>
{
users.map(t => <ListItem key={t.account} i18n={i18n} user={t} openModal={this.openModal} delUser={this.delUser}/>)
}
</Table.Body>
<Table.Footer>
<Table.Row>
<Table.Cell colSpan="3">
<Button type="button" fluid content={i18n && i18n.t ? i18n.t('page.userlist.table.button.add') : ''} onClick={()=>this.openModal()}/>
</Table.Cell>
</Table.Row>
</Table.Footer>
</Table>
</Segment>
<UModal i18n={i18n} open={this.state.edit} type={this.state.editType} data={this.state.editData} closeModal={this.closeModal} onSubmit={this.onModalSubmit}/>
</Container>
)
}
}
export default UList;

View File

@ -0,0 +1,64 @@
import React from 'react';
import {Form, Button} from 'semantic-ui-react';
const DeviceSelect = ({i18n, querySelectList, page, permissions, devs, addSelect, showGroup}) => {
let devName = null;
let devType = null;
return (
<Form.Group inline>
<Form.Field>
<label>{i18n&&i18n.t?i18n.t(`page.${page || ''}.form.label.select_device`) : ''}</label>
<select ref={node => devType = node} onChange={(e) => {
querySelectList(e.target.value);
}}>
<option value="">{i18n&&i18n.t?i18n.t('select.dev_type') : ''}</option>
{
permissions.dio ?
<option value="do">{i18n&&i18n.t ? i18n.t('select.digitoutput') : ''}</option> : null
}
{
permissions.leone ?
<option value="leone">{i18n&&i18n.t ? i18n.t('select.leone') : ''}</option> : null
}
{
permissions.iogroup && showGroup ?
<option value="iogroup">{i18n&&i18n.t ? i18n.t('select.iogroup') : ''}</option> : null
}
</select>
</Form.Field>
<Form.Field>
<select ref={node => devName = node}>
{
devs.map((t, idx) => <option key={idx} value={t.id || ''}>{t.name || ''}</option>)
}
</select>
</Form.Field>
<Form.Field>
<Button type="button" basic size="mini" content={i18n&&i18n.t?i18n.t(`page.${page || ''}.form.button.join`) : ''} onClick={()=>{
let type = '';
let devname = '';
let devid = '';
let typev = '';
if(devType != null) {
type = devType.options[devType.selectedIndex].innerHTML;
typev = devType.value == 'leone' ? 'le' : devType.value;
}
if(devName != null) {
if(devName.options.length == 0) return ;
devname = devName.options[devName.selectedIndex].innerHTML;
devid = `${typev}${devName.value}`;
}
let json = {
type,
name: devname,
id: devid
};
if(!devType.value || !type || !devname) return ;
addSelect(json);
}} />
</Form.Field>
</Form.Group>
)
}
export default DeviceSelect;

14
src/components/Dialog.js Normal file
View File

@ -0,0 +1,14 @@
import React from 'react';
import {Modal, Button} from 'semantic-ui-react';
const Dialog = ({obj, getNext}) => (
<Modal open={obj && obj.msg != '' ? true : false} onClose={() => getNext(obj.act)} style={{zIndex: "2001"}}>
<Modal.Content>{obj && obj.msg ? obj.msg : ''}</Modal.Content>
<Modal.Actions>
<Button onClick={() => getNext(obj.act)} content="OK" />
</Modal.Actions>
</Modal>
);
export default Dialog;

10
src/components/Loading.js Normal file
View File

@ -0,0 +1,10 @@
import React from 'react';
import {Loader, Dimmer} from 'semantic-ui-react';
const Loading = ({active}) => (
<Dimmer active={active} style={{zIndex: '2000'}}>
<Loader size="large" />
</Dimmer>
)
export default Loading;

View File

@ -0,0 +1,29 @@
import React from 'react';
import {Form, Segment, Input, Button} from 'semantic-ui-react';
const loginForm = ({i18n, page, onSubmit}) => {
let account;
let password;
return (
<Form size="large" onSubmit={(e, data) => {
e.preventDefault();
onSubmit(data.formData);
}} serializer={e => {
let acc = e.querySelector('input[name="acc"]').value;
let pass = e.querySelector('input[name="pass"]').value;
return {account: acc, password: pass};
}}>
<Segment stacked={true} >
<Form.Field>
<Input icon="user" iconPosition="left" name="acc" placeholder={typeof i18n.t == 'function' ? i18n.t(`${page}.input.placeholder.account`) : ''} />
</Form.Field>
<Form.Field>
<Input icon="lock" iconPosition="left" name="pass" type="password" placeholder={typeof i18n.t == 'function' ? i18n.t(`${page}.input.placeholder.password`) : ''} />
</Form.Field>
<Button type="submit" color="teal" size="large" fluid={true} content={typeof i18n.t == 'function' ? i18n.t(`${page}.button.login`) : ''} />
</Segment>
</Form>
);
}
export default loginForm;

View File

@ -0,0 +1,99 @@
import React from 'react';
import i18next from 'i18next';
import {Grid, Header} from 'semantic-ui-react';
import LoginForm from './Form.js';
import Dialog from '../../containers/DialogControl';
import {add_dialog_msg, toggle_loading} from '../../actions';
import {connect} from 'react-redux';
import Loading from '../../containers/LoadingControl';
class Root extends React.Component {
constructor(props) {
super(props)
this.state = {
i18n: {}
};
}
handleSubmit = (data) => {
let {i18n} = this.state;
if (typeof data.account != 'string' || !data.account.trim() || typeof data.password != 'string' || !data.password.trim()) {
// show dialog
this.props.dispatch(add_dialog_msg(i18n && 't' in i18n ? i18n.t('tip.input_empty') : ''));
return;
}
this.props.dispatch(toggle_loading(1));
fetch('/api/system/login', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({data})
})
.then(res => res.json())
.then(json => {
this.props.dispatch(toggle_loading(0));
if(json.status != 1) {
this.props.dispatch(add_dialog_msg(json.message));
return ;
}
sessionStorage.setItem('write_privilege', json.data.record[0].write_privilege);
sessionStorage.setItem('read_privilege', json.data.record[0].read_privilege);
sessionStorage.setItem('account', json.data.record[0].account);
sessionStorage.setItem('token', json.data.token);
if(json.data.rt.permission && json.data.rt.permission.length > 0) {
sessionStorage.setItem('permissions', JSON.stringify(json.data.rt.permission[0]));
}
// this.props.dispatch(add_dialog_msg('登入成功', () => {
location.replace('/admin');
// }))
})
.catch(err => this.props.dispatch(add_dialog_msg('登入失敗')));
}
componentDidMount() {
let lang = navigator
.language
.substring(0, 2);
fetch(`/locales/${lang}.json`).then(response => {
if (response.status == 200)
return response.json();
return {}
}).then(json => {
i18next.init({
lng: lang,
resources: json
}, () => {
this.setState({i18n: i18next});
})
})
}
render() {
const {i18n} = this.state;
const page = 'page.login';
return (
<div style={{
height: '100%'
}}>
<Loading />
<Dialog/>
<Grid verticalAlign="middle" centered className="login-grid">
<Grid.Column className="login-column">
<Header textAlign="center" content={'JCNet WebIO'} as="h2" color="teal"/>
<LoginForm i18n={this.state.i18n} page={page} onSubmit={this.handleSubmit}/>
</Grid.Column>
</Grid>
</div>
);
}
}
export default connect()(Root)

View File

@ -0,0 +1,14 @@
import React from 'react';
import {Sidebar, Menu, Segment, Icon} from 'semantic-ui-react';
import {Link} from 'react-router';
const MItem = ({toLink, txt, onClick, permission}) => {
if(permission == 1) {
return (
<Menu.Item as={Link} to={toLink} content={txt} onClick={onClick} />
)
}
return null;
}
export default MItem;

View File

@ -0,0 +1,40 @@
import React from 'react';
import {Sidebar, Menu, Segment, Icon, Container} from 'semantic-ui-react';
import {Link} from 'react-router';
import MItem from './Item';
const MainMenu = ({i18n, show, toggleMenu, children, permissions}) => (
<div style={{height: '100%'}}>
<Sidebar.Pushable as={Segment} style={{zIndex: 100}}>
<Sidebar as={Menu} animation="push" width="thin" visible={show} icon="labeled" vertical inverted>
<MItem toLink="/admin" txt={i18n && 't' in i18n ? i18n.t('menu.item.systeminfo') : ''} permission={true} onClick={() => toggleMenu()} />
<MItem toLink="/admin/user" txt={i18n && 't' in i18n ? i18n.t('menu.item.userlist') : ''} permission={true} onClick={() => toggleMenu()} />
<MItem toLink="/admin/dio" txt={i18n && 't' in i18n ? i18n.t('menu.item.dio') : ''} permission={permissions.dio} onClick={() => toggleMenu()} />
<MItem toLink="/admin/log" txt={i18n && 't' in i18n ? i18n.t('menu.item.log') : ''} permission={permissions.log} onClick={()=>toggleMenu()} />
<MItem toLink="/admin/leone" txt={i18n && 't'in i18n ? i18n.t('menu.item.leone') : ''} permission={permissions.leone} onClick={()=>toggleMenu()} />
<MItem toLink="/admin/iogroup" txt={i18n && 't' in i18n ? i18n.t('menu.item.iogroup') : ''} permission={permissions.iogroup} onClick={()=>toggleMenu()} />
<MItem toLink="/admin/iocmd" txt={i18n && 't' in i18n ? i18n.t('menu.item.iocmd') : ''} permission={permissions.iocmd} onClick={()=>toggleMenu()} />
<MItem toLink="/admin/schedule" txt={i18n && 't' in i18n ? i18n.t('menu.item.schedule') : ''} permission={permissions.schedule} onClick={()=>toggleMenu()} />
<MItem toLink="/admin/modbus" txt={i18n && 't' in i18n ? i18n.t('menu.item.modbus') : ''} permission={permissions.modbus} onClick={()=>toggleMenu()} />
<MItem toLink="/admin/link" txt={i18n && 't' in i18n ? i18n.t('menu.item.link') : ''} permission={permissions.link} onClick={()=>toggleMenu()} />
<MItem toLink="/admin" txt={i18n && 't' in i18n ? i18n.t('menu.item.logout') : ''} permission={true} onClick={()=>{
sessionStorage.clear();
location.replace('/');
}} />
</Sidebar>
<Sidebar.Pusher style={{backgroundColor: '#eee'}}>
<Menu fixed="top" >
<Menu.Item onClick={() => toggleMenu(show ? false : true)} >
<Icon name="sidebar" />
{i18n && 't' in i18n ? i18n.t('menu.title') : ''}
</Menu.Item>
</Menu>
<Container style={{paddingTop: '45px', paddingBottom: '40px'}}>
{children}
</Container>
</Sidebar.Pusher>
</Sidebar.Pushable>
</div>
);
export default MainMenu;

View File

@ -0,0 +1,34 @@
import {connect} from 'react-redux';
import {add_dialog_msg, toggle_loading, get_link_list, clear_link, add_link, del_link, sw_link_active} from '../../actions';
import ActionLinkPage from '../../components/AdminPage/ActionLink';
const mapStateToProps = (state) => ({
i18n: state.i18n,
list: state.lists.link.list || []
});
const mapDispatchToProps = (dispatch, ownProps) => ({
showDialog: (msg) => {
dispatch(add_dialog_msg(msg));
},
toggleLoading: (active = false) => {
dispatch(toggle_loading(active));
},
getList: () => {
dispatch(get_link_list());
},
clearList: () => {
dispatch(clear_link());
},
delLink: (data) => {
dispatch(del_link(data));
},
addLink: (data) => {
dispatch(add_link(data));
},
swLink: (data) => {
dispatch(sw_link_active(data));
}
})
export default connect(mapStateToProps, mapDispatchToProps)(ActionLinkPage);

View File

@ -0,0 +1,18 @@
import {connect} from 'react-redux';
import {add_dialog_msg, toggle_loading} from '../../actions';
import ActionLinkAdd from '../../components/AdminPage/ActionLinkAdd';
const mapStateToProps = (state) => ({
i18n: state.i18n
});
const mapDispatchToProps = (dispatch, ownProps) => ({
showDialog: (msg) => {
dispatch(add_dialog_msg(msg));
},
toggleLoading: (active = false) => {
dispatch(toggle_loading(active));
}
})
export default connect(mapStateToProps, mapDispatchToProps)(ActionLinkAdd);

View File

@ -0,0 +1,31 @@
import {connect} from 'react-redux';
import DIO from '../../components/AdminPage/DIO';
import {get_dio_info, get_dio_status, set_do_status, set_dio_info, clear_dio} from '../../actions';
const mapStateToProps = (state) => ({
dio: state.lists.dio,
i18n: state.i18n
});
const mapDispatchToProps = (dispatch, ownProps) => ({
clearList: () => {
dispatch(clear_dio());
},
getDIOInfo: () => {
dispatch(get_dio_info());
},
getIO: () => {
dispatch(get_dio_status());
},
dotRun: (pin, value) => {
dispatch(set_do_status({
pin,
value
}));
},
setDIOInfo: (data) => {
dispatch(set_dio_info(data));
}
})
export default connect(mapStateToProps, mapDispatchToProps)(DIO);

View File

@ -0,0 +1,36 @@
import {connect} from 'react-redux';
import {add_dialog_msg, get_select_list, clear_select_list, io_cmd} from '../../actions';
import IOCmdPage from '../../components/AdminPage/IOCmd'
const mapStateToProps = (state) => ({
i18n: state.i18n,
permissions: (function() {
let p = sessionStorage.getItem('permissions');
if (!p) return {};
let json = {};
try {
json = JSON.parse(p);
} catch (e) {
return {};
}
return json
}()),
devs: state.lists.selectdev
});
const mapDispatchToProps = (dispatch, ownProps) => ({
showDialog: (msg) => {
dispatch(add_dialog_msg(msg));
},
getSelectList: (data) => {
dispatch(get_select_list(data));
},
clearSelectList: () => {
dispatch(clear_select_list());
},
runCmd: (data) => {
dispatch(io_cmd(data))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(IOCmdPage);

View File

@ -0,0 +1,51 @@
import {connect} from 'react-redux';
import {add_dialog_msg, get_iogroup_list, del_iogroup, edit_iogroup, add_iogroup, get_select_list,clear_iogroup, clear_select_list} from '../../actions';
import IOGroupPage from '../../components/AdminPage/IOGroup';
const mapStateToProps = (state) => ({
i18n: state.i18n,
list: state.lists.iogroup.list,
dos: state.lists.iogroup.do,
leones: state.lists.iogroup.leone,
permissions: (function() {
let p = sessionStorage.getItem('permissions');
if (!p) return {};
let json = {};
try {
json = JSON.parse(p);
} catch (e) {
return {};
}
return json
}()),
devs: state.lists.selectdev
});
const mapDispatchToProps = (dispatch, ownProps) => ({
showDialog: (msg) => {
dispatch(add_dialog_msg(msg));
},
clearList: () => {
dispatch(clear_iogroup());
},
getIOGroupList: () => {
dispatch(get_iogroup_list());
},
addIOGroup: (data) => {
dispatch(add_iogroup(data));
},
editIOGroup: (data) => {
dispatch(edit_iogroup(data));
},
delIOGroup: (data) => {
dispatch(del_iogroup(data));
},
getSelectList: (data) => {
dispatch(get_select_list(data));
},
clearSelectList: () => {
dispatch(clear_select_list());
}
})
export default connect(mapStateToProps, mapDispatchToProps)(IOGroupPage);

View File

@ -0,0 +1,46 @@
import {connect} from 'react-redux';
import LeOnePage from '../../components/AdminPage/LeOne';
import {add_dialog_msg, scan_leone, clear_scan_leone, get_leone_list,add_scan_leone,del_leone, add_leone, edit_leone, io_cmd, clear_leone} from '../../actions';
const mapStateToProps = (state) => ({
i18n: state.i18n,
scanList: state.lists.leone.scanList,
list: state.lists.leone.list,
status: state.lists.leone.status,
scanning: state.lists.leone.scanning
});
const mapDispatchToProps = (dispatch, ownProps) => ({
showDialog: (msg = '') => {
dispatch(add_dialog_msg(msg));
},
clearList: () => {
dispatch(clear_leone);
},
scanLeOne: (data) => {
dispatch(scan_leone(data));
},
clearScan: () => {
dispatch(clear_scan_leone())
},
addScanLeOne: (data) => {
dispatch(add_scan_leone(data));
},
getLeOneList: (showLoading = true) => {
dispatch(get_leone_list(showLoading));
},
delLeOne: (data) => {
dispatch(del_leone(data));
},
addLeOne: (data) => {
dispatch(add_leone(data));
},
editLeOne: (data) => {
dispatch(edit_leone(data));
},
runCmd: (data) => {
dispatch(io_cmd(data));
}
})
export default connect(mapStateToProps, mapDispatchToProps)(LeOnePage);

View File

@ -0,0 +1,20 @@
import {connect} from 'react-redux';
import LogPage from '../../components/AdminPage/Log';
import {get_log_list, clear_log} from '../../actions';
const mapStateToProps = (state) => ({
i18n: state.i18n,
list: state.lists.log.list || [],
page: state.lists.log.page || []
});
const mapDispatchToProps = (dispatch, ownProps) => ({
getList: (p = 1) => {
dispatch(get_log_list(p));
},
clearList: () => {
dispatch(clear_log());
}
})
export default connect(mapStateToProps, mapDispatchToProps)(LogPage);

View File

@ -0,0 +1,68 @@
import {connect} from 'react-redux';
import {add_dialog_msg,
clear_modbus,
get_modbus_list,
del_modbus,
add_modbus,
edit_modbus,
get_mb_data,
get_mb_iostatus,
del_mb_iolist,
add_mb_iolist,
edit_mb_iolist,
add_mb_aio,
edit_mb_aio,
del_mb_aio} from '../../actions';
import ModbusPage from '../../components/AdminPage/Modbus';
const mapStateToProps = (state) => ({
i18n: state.i18n,
list: state.lists.modbus.list || []
});
const mapDispatchToProps = (dispatch, ownProps) => ({
showDialog: (msg) => {
dispatch(add_dialog_msg(msg));
},
clearList: () => {
dispatch(clear_modbus());
},
getList: () => {
dispatch(get_modbus_list());
},
delModbus: (data) => {
dispatch(del_modbus(data));
},
addModbus: data => {
dispatch(add_modbus(data));
},
editModbus: data=>{
dispatch(edit_modbus(data));
},
getMBData: data => {
dispatch(get_mb_data(data));
},
getMBIOStatus: data => {
dispatch(get_mb_iostatus(data));
},
addIOList: data => {
dispatch(add_mb_iolist(data));
},
editIOList: data=>{
dispatch(edit_mb_iolist(data));
},
delIOList: data => {
dispatch(del_mb_iolist(data));
},
addAIO: data => {
dispatch(add_mb_aio(data));
},
editAIO: data => {
dispatch(edit_mb_aio(data));
},
delAIO: data => {
dispatch(del_mb_aio(data));
}
})
export default connect(mapStateToProps, mapDispatchToProps)(ModbusPage);

View File

@ -0,0 +1,55 @@
import {connect} from 'react-redux';
import {add_dialog_msg, get_select_list, clear_select_list, get_schedule_list, add_schedule, del_schedule, edit_schedule, sw_schedule, clear_schedule} from '../../actions';
import SchedulePage from '../../components/AdminPage/Schedule';
const mapStateToProps = (state) => ({
i18n: state.i18n,
devs: state.lists.selectdev,
list: state.lists.schedule.list,
dos: state.lists.schedule.do,
les: state.lists.schedule.leone,
ios: state.lists.schedule.iogroup,
permissions: (function() {
let p = sessionStorage.getItem('permissions');
if (!p) return {};
let json = {};
try {
json = JSON.parse(p);
} catch (e) {
return {};
}
return json
}())
});
const mapDispatchToProps = (dispatch, ownProps) => ({
showDialog: (msg) => {
dispatch(add_dialog_msg(msg));
},
clearList: () => {
dispatch(clear_schedule());
},
getSelectList: (data) => {
dispatch(get_select_list(data));
},
clearSelectList: () => {
dispatch(clear_select_list());
},
getScheduleList: () => {
dispatch(get_schedule_list());
},
addSchedule: (data) => {
dispatch(add_schedule(data));
},
editSchedule: (data) => {
dispatch(edit_schedule(data));
},
delSchedule: (data) => {
dispatch(del_schedule(data));
},
swSchedule: (data) => {
dispatch(sw_schedule(data));
}
});
export default connect(mapStateToProps, mapDispatchToProps)(SchedulePage);

View File

@ -0,0 +1,11 @@
import React from 'react';
import {connect} from 'react-redux';
import SysInfo from '../../components/AdminPage/SystemInfo'
const mapStateToProps = (state) => ({
i18n: state.i18n,
network: 'network' in state.sysinfo ? state.sysinfo.network : {},
time: 'time' in state.sysinfo ? state.sysinfo.time : 0
})
export default connect(mapStateToProps)(SysInfo);

View File

@ -0,0 +1,32 @@
import React from 'react';
import {connect} from 'react-redux';
import UList from '../../components/AdminPage/UserList';
import {add_dialog_msg, edit_user, get_user_list, add_user, del_user,clear_userlist} from '../../actions';
const mapStateToProps = (state) => ({
i18n: state.i18n,
users: state.lists.user || []
});
const mapDispatchToProps = (dispatch, ownProps) => ({
showDialog: (msg) => {
dispatch(add_dialog_msg(msg));
},
clearList: () => {
dispatch(clear_userlist())
},
getList: () => {
dispatch(get_user_list());
},
addUser: data => {
dispatch(add_user(data));
},
editUser: data => {
dispatch(edit_user(data));
},
delUser: data=>{
dispatch(del_user(data));
}
})
export default connect(mapStateToProps, mapDispatchToProps)(UList);

View File

@ -0,0 +1,33 @@
import React from 'react';
import {connect} from 'react-redux';
import Datetime from 'react-datetime';
import MainMenu from '../../containers/MenuControl';
import Loading from '../../containers/LoadingControl';
import Dialog from '../DialogControl';
class AdmPage extends React.Component {
constructor(props) {
super(props);
}
render() {
let {i18n, children} = this.props;
if(!i18n || Object.keys(i18n).length == 0 || !i18n.t || !i18n.getResource) return null;
return (
<div style={{height: '100%'}}>
<Loading />
<Dialog />
<MainMenu i18n={i18n} >
{children}
</MainMenu>
</div>
)
}
}
const mapStateToProps = (state) => ({
i18n: state.i18n
})
export default connect(mapStateToProps)(AdmPage);

View File

@ -0,0 +1,17 @@
import {connect} from 'react-redux';
import Dialog from '../components/Dialog';
import {remove_dialog_msg} from '../actions'
const mapStateToProps = (state) => ({
obj: state.dialog[0] || null
});
const mapDispatchToProps = (dispatch, ownProps) => ({
getNext : (act) => {
//get next dialog message
if(typeof act == 'function') act();
dispatch(remove_dialog_msg());
}
});
export default connect(mapStateToProps, mapDispatchToProps)(Dialog);

View File

@ -0,0 +1,8 @@
import {connect} from 'react-redux';
import Loading from '../components/Loading';
const mapStateToProps = (state) => ({
active: state.ui.showLoading
});
export default connect(mapStateToProps)(Loading);

View File

@ -0,0 +1,26 @@
import { connect } from 'react-redux';
import MainMenu from '../components/MainMenu';
import { toggle_menu } from '../actions'
const mapStateToProps = (state) => ({
show: state.ui.showMenu,
permissions: (function() {
let p = sessionStorage.getItem('permissions');
if (!p) return {};
let json = {};
try {
json = JSON.parse(p);
} catch (e) {
return {};
}
return json
}())
});
const mapDispatchToProps = (dispatch, ownProps) => ({
toggleMenu: (flag) => {
dispatch(toggle_menu(flag));
}
});
export default connect(mapStateToProps, mapDispatchToProps)(MainMenu);

25
src/index.js Normal file
View File

@ -0,0 +1,25 @@
import React from 'react';
import ReactDOM from 'react-dom';
// import {Router, Route, browserHistory, IndexRoute} from 'react-router';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import reducers from './reducers';
// import routes from './routes';
import Login from './components/Login';
import thunk from 'redux-thunk';
const middleware = [thunk];
const store = createStore(reducers, applyMiddleware(...middleware));
store.subscribe(() => {
console.log(store.getState());
});
ReactDOM.render(
<Provider store={store}>
<Login />
{/*<Router history={browserHistory} routes={routes} />*/}
</Provider>,
document.getElementById('app')
);

15
src/reducers/dialog.js Normal file
View File

@ -0,0 +1,15 @@
const dialogReducer = (state = [], action) => {
switch(action.type) {
case 'dialog_addmsg':
return [...state, {
msg: action.msg,
act: action.act || null
}];
case 'dialog_remove_first':
return state.slice(1);
default:
return state;
}
}
export default dialogReducer;

10
src/reducers/i18n.js Normal file
View File

@ -0,0 +1,10 @@
const i18nReducer = (state = {}, action) => {
switch(action.type) {
case 'i18n_set_ctx':
return action.i18n;
default:
return state;
}
}
export default i18nReducer;

Some files were not shown because too many files have changed in this diff Show More