const router = require("koa-router")();
|
const mysql = require("../utils/mysql_db");
|
const jpush = require("../utils/jpush");
|
|
router.prefix("/apicloud");
|
|
let map_timeout = {}; //心跳超时检测
|
|
const multer = require("koa-multer"); //加载koa-multer模块
|
//文件上传
|
//配置
|
var storage = multer.diskStorage({
|
//文件保存路径
|
destination: function(req, file, cb) {
|
cb(null, "public/uploads/");
|
},
|
//修改文件名称
|
filename: function(req, file, cb) {
|
var fileFormat = file.originalname.split(".");
|
cb(null, Date.now() + "." + fileFormat[fileFormat.length - 1]);
|
}
|
});
|
//加载配置
|
var upload = multer({
|
storage: storage
|
});
|
|
/**
|
* 上传录音文件
|
* 传入参数 file
|
*/
|
router.post("/upload", upload.single("file"), async (ctx, next) => {
|
if (ctx.req.file) {
|
//TODO 语音识别
|
//AipSpeechClient.recognize(ctx.req.file.filename);
|
ctx.body = {
|
success: true,
|
filename: ctx.req.file.filename //返回文件名
|
};
|
} else {
|
ctx.body = {
|
success: false
|
};
|
}
|
});
|
|
//下发即时任务/apicloud/jpush?alias=123456780000432&commond=record&extra={"freq":"98","length":10000}
|
router.get("/jpush", async function(ctx, next) {
|
let params = ctx.query;
|
let devices = JSON.parse(params.devices);
|
if (params.command == "scan") {
|
//扫台
|
setDeviceStatus(devices, "2"); //更新设备状态 2:扫台
|
// let extra = JSON.parse(params.extra)
|
//生成并插入即时任务
|
let sql = `INSERT INTO fm_t_realtime_task(device_list,command,freq_range,recording_length,create_time,detail)
|
VALUES('${JSON.stringify(devices)}',
|
'扫台',
|
${JSON.stringify(params.freq_range)},
|
${params.length},
|
NOW(),
|
${
|
params.detail
|
? "".concat("'", params.detail, "'")
|
: "null"
|
}
|
);`;
|
let result = await mysql.queryNewId(sql);
|
console.log(result);
|
//下发扫台指令
|
let extra = {};
|
extra.identifier = {
|
task_type: "即时任务",
|
task_id: result[0].new_id
|
};
|
extra.title = "updateTask";
|
extra.task_id = result[0].new_id;
|
if (params.length == "0") {
|
console.log(params.length);
|
extra.isNeedRecording = false;
|
} else {
|
console.log(params.length);
|
extra.isNeedRecording = true;
|
}
|
|
extra.isPeriodTask = false;
|
extra.startTime = "04:00:00";
|
extra.endTime = "03:27:00";
|
extra.recordSeconds = params.length * 1000;
|
let freq = JSON.parse(params.freq_range);
|
extra.startFrequency = freq[0];
|
extra.endFrequenty = freq[1];
|
|
let sendDevices = [];
|
for (let i = 0; i < devices.length; i++) {
|
if (devices[i]) {
|
sendDevices.push(devices[i]);
|
}
|
}
|
|
// console.log(sendDevices);
|
|
jpush.pushWithAlias(sendDevices.join(","), "updateTask", extra);
|
params.logDes = `对编号为${params.devices}的设备下发扫台任务。`;
|
ctx.body = {
|
code: 20000,
|
task_id: result[0].new_id
|
};
|
} else if (params.command == "record") {
|
//录音
|
setDeviceStatus(devices, "3"); //更新设备状态
|
let extra = JSON.parse(params.extra);
|
//生成并插入即时任务
|
let sql = `INSERT INTO fm_t_realtime_task(device_list,command,create_time,detail,freq_range,recording_length)
|
VALUES('${JSON.stringify(devices)}',
|
'录音',
|
NOW(),
|
${
|
params.detail
|
? "".concat("'", params.detail, "'")
|
: "null"
|
},
|
${extra.freq},
|
${extra.length}
|
);`;
|
let result = await mysql.queryNewId(sql);
|
//按设备依次插入任务执行记录并下发录音指令
|
// let task_id = result[0].new_id //即时任务id
|
// sql = `INSERT INTO fm_t_task_record(task_type,task_id,device_lgid,start_time) VALUES
|
// ('即时任务', '${task_id}', '${devices[i]}', NOW());`
|
// result = await mysql.queryNewId(sql)
|
let task_record_id = result[0].new_id;
|
//添加数据标识
|
extra.identifier = {
|
task_type: "即时任务",
|
task_record_id: task_record_id,
|
result_type: 0
|
};
|
extra.title = "updateTask";
|
extra.task_id = result[0].new_id;
|
extra.isNeedRecording = true;
|
extra.isPeriodTask = false;
|
extra.startTime = "04:00:00";
|
extra.endTime = "03:27:00";
|
extra.recordSeconds = params.extra.length * 1000;
|
|
let sendDevices = [];
|
for (let i = 0; i < devices.length; i++) {
|
if (devices[i]) {
|
// let task_id = result[0].new_id //即时任务id
|
// sql = `INSERT INTO fm_t_task_record(task_type,task_id,device_lgid,start_time) VALUES
|
// ('即时任务', '${task_id}', '${devices[i]}', NOW());`
|
// result = await mysql.queryNewId(sql)
|
sendDevices.push(devices[i]);
|
}
|
}
|
// console.log(sendDevices);
|
|
jpush.pushWithAlias(sendDevices.join(","), "updateTask", extra); //extra中的录音长度单位为毫秒
|
params.logDes = `对编号为${params.devices}的设备下发录音任务。`;
|
|
ctx.body = {
|
code: 20000,
|
task_id: task_record_id
|
};
|
} else if (params.command == "reboot") {
|
//重启
|
params.logDes = `对编号为${params.devices}的设备下发重启任务。`;
|
|
let sendDevices = [];
|
for (let i = 0; i < devices.length; i++) {
|
if (devices[i]) {
|
sendDevices.push(devices[i]);
|
}
|
}
|
|
jpush.pushWithAlias(sendDevices.join(","), "reboot", params);
|
ctx.body = {
|
code: 20000
|
};
|
} else if (params.command == "periodicTasks") {
|
setDeviceStatus(devices, "2"); //更新设备状态
|
// let par = JSON.parse(params.extra)
|
//生成并插入即时任务
|
let sql = `INSERT INTO fm_t_realtime_task(device_list,command,freq_range,recording_length,create_time,detail)
|
VALUES('${JSON.stringify(devices)}',
|
'周期任务',
|
${JSON.stringify(params.freq)},
|
${params.length},
|
NOW(),
|
${
|
params.detail
|
? "".concat("'", params.detail, "'")
|
: "null"
|
}
|
);`;
|
|
let result = await mysql.queryNewId(sql);
|
//下发周期任务指令
|
let extra = {};
|
extra.identifier = {
|
task_type: "周期任务",
|
task_id: result[0].new_id
|
};
|
extra.title = "updateTask";
|
extra.task_id = result[0].new_id;
|
extra.isNeedRecording = true;
|
extra.isPeriodTask = true;
|
extra.startTime = "04:00:00";
|
extra.endTime = "03:27:00";
|
extra.recordSeconds = params.length * 1000;
|
extra.period_seconds = params.periodLength * 1000 * 60;
|
let freq = JSON.parse(params.freq);
|
extra.startFrequency = freq[0];
|
extra.endFrequenty = freq[1];
|
|
let sendDevices = [];
|
for (let i = 0; i < devices.length; i++) {
|
if (devices[i]) {
|
sendDevices.push(devices[i]);
|
}
|
}
|
// console.log(sendDevices);
|
|
jpush.pushWithAlias(sendDevices.join(","), "updateTask", extra);
|
params.logDes = `对编号为${params.devices}的设备下发周期任务。`;
|
ctx.body = {
|
code: 20000,
|
task_id: result[0].new_id
|
};
|
} else if (params.command == "installApp") {
|
//重启
|
params.logDes = `对编号为${params.devices}的设备下发更新设备。`;
|
|
let sendDevices = [];
|
for (let i = 0; i < devices.length; i++) {
|
if (devices[i]) {
|
sendDevices.push(devices[i]);
|
}
|
}
|
|
jpush.pushWithAlias(sendDevices.join(","), "installApp", params);
|
ctx.body = {
|
code: 20000
|
};
|
} else if (params.command == "restartApp") {
|
//重启
|
params.logDes = `对编号为${params.devices}的设备下发重启App。`;
|
// console.log(devices.join(','))
|
let sendDevices = [];
|
for (let i = 0; i < devices.length; i++) {
|
if (devices[i]) {
|
sendDevices.push(devices[i]);
|
}
|
}
|
|
jpush.pushWithAlias(sendDevices.join(","), "restartApp", params);
|
ctx.body = {
|
code: 20000
|
};
|
}
|
});
|
|
//设备程序启动上报接口
|
router.get("/appLoaded", async function(ctx, next) {
|
let params = ctx.query;
|
let sql = `SELECT log_id FROM fm_t_device WHERE phy_id='${params.id}';`;
|
let result = await mysql.query(sql);
|
|
if (result.length == 1) {
|
ctx.session.log_id = result[0].log_id;
|
sql = `UPDATE fm_t_device set status=1 WHERE log_id='${ctx.session.log_id}'`;
|
params.logDes = `设备${ctx.session.log_id}启动上报。`;
|
mysql.query(sql);
|
ctx.body = {
|
device_id: result[0].log_id,
|
code: 20000
|
};
|
} else {
|
ctx.body = {
|
success: false
|
};
|
}
|
});
|
|
//设备心跳接口
|
router.get("/heartBeat", async function(ctx, next) {
|
let params = ctx.query;
|
|
//插入数据库
|
let sql = `INSERT INTO fm_t_heartbeat
|
(device_lgid,battery,lon,lat,upload_time) VALUES
|
(
|
'${ctx.session.log_id}',
|
'${params.battery}',
|
${params.lon},
|
${params.lat},
|
NOW()
|
)`;
|
await mysql.query(sql);
|
|
//获取心跳间隔
|
sql = `SELECT heartbeat from fm_t_device WHERE log_id='${ctx.session.log_id}'`;
|
let result = await mysql.query(sql);
|
let interval = result[0].heartbeat;
|
params.logDes = `设备${ctx.session.log_id}设置心跳。`;
|
ctx.body = {
|
code: 20000,
|
interval: interval
|
};
|
|
//心跳超时检测
|
if (typeof map_timeout[ctx.session.log_id] != "undefined")
|
clearTimeout(map_timeout[ctx.session.log_id]);
|
map_timeout[ctx.session.log_id] = setTimeout(function() {
|
sql = `UPDATE fm_t_device set status=0 WHERE log_id='${ctx.session.log_id}'`;
|
mysql.query(sql);
|
}, interval * 60 * 1000 + 30000);
|
});
|
|
/**
|
* 设备周期任务获取接口
|
* 需要返回下一次任务执行时间 next
|
*/
|
router.get("/PeriodicTask", async function(ctx, next) {
|
let log_id = ctx.session.log_id; //设备逻辑号
|
let time = -1; //下一次执行任务时间
|
|
//获取周期任务id 和 period
|
let sql = `SELECT id,period FROM fm_t_period_task WHERE id = (SELECT task_id FROM fm_t_device WHERE log_id = '${log_id}');`;
|
let result = await mysql.query(sql);
|
|
if (result.length > 0) {
|
time = f_next(0, result[0].period * 60000); //计算任务时间
|
}
|
if (time != -1) {
|
ctx.body = {
|
success: true,
|
next: time,
|
identifier: {
|
task_type: "周期任务",
|
task_id: result[0].id
|
} // 数据标识
|
};
|
} else {
|
ctx.body = {
|
success: false
|
};
|
}
|
});
|
|
//错误上报
|
router.get("/error", async function(ctx, next) {
|
//TODO 数据库插入错误记录
|
|
//返回是否需要重启应用程序
|
ctx.body = {
|
success: true,
|
reboot: false
|
};
|
});
|
|
/**上传扫台结果
|
* 传入参数:result(扫台结果 字符串 &分割频率) start(扫台开始时间 字符串) end(扫台结束时间) lon lat ,
|
*/
|
router.get("/ReportScanResult", async function(ctx, next) {
|
/**
|
* 扫台记录保存流程:
|
* 1 将扫台结果保存到任务记录表(fm_t_task_record)中
|
* 2 如果是周期任务,则返回需要录音的频率列表;此外还需返回录音频率对应的标识,标识包括:
|
* i task_record_id,即1中插入新的任务记录后得到的主键id
|
* ii result_type,即频率的判别类型(取值为字典表fm_d_result_type中的id值)
|
*/
|
let log_id = ctx.session.log_id;
|
let params = ctx.query;
|
params.identifier = JSON.parse(params.identifier);
|
let scan_result = params.result
|
.split("&")
|
.map(freq => Math.round(Number(freq) * 10) / 10);
|
|
//保存任务执行记录及扫台结果(不需要区分周期任务和即时任务的扫台)
|
let sql = `INSERT INTO fm_t_task_record(task_type,task_id,device_lgid,start_time,finish_time,scan_result)
|
VALUES('${params.identifier.task_type}',
|
'${params.identifier.task_id}',
|
'${log_id}',
|
'${params.start}',
|
'${params.end}',
|
'${JSON.stringify(scan_result)}'
|
);`;
|
let query_result = await mysql.queryNewId(sql);
|
let task_record_id = query_result[0].new_id;
|
|
//处理即时任务结果
|
if (params.identifier.task_type == "即时任务") {
|
setDeviceStatus([log_id], "1"); //完成扫台 更新状态为 空闲
|
}
|
//处理周期任务的扫台结果
|
if (params.identifier.task_type == "周期任务") {
|
//获取任务配置
|
let sql = `SELECT freq_range,whitelist,recording_length FROM fm_t_period_task WHERE id=${params.identifier.task_id}`;
|
let result = await mysql.query(sql);
|
let freq_range = result[0].freq_range
|
? JSON.parse(result[0].freq_range)
|
: null;
|
let whitelist = result[0].whitelist
|
? JSON.parse(result[0].whitelist)
|
: null;
|
let recording_length = result[0].recording_length;
|
|
//筛选出在任务扫描频率范围内的频率
|
let freq_in_range = [];
|
for (let i = 0; i < scan_result.length; i++) {
|
const frequency = scan_result[i];
|
if (f_inRanges(frequency, freq_range)) {
|
freq_in_range.push(frequency);
|
}
|
}
|
|
//根据白名单判断频率是否合法
|
|
let freq_list = []; //需要录音的频率
|
let identifier_list = []; //录音频率对应的数据标识
|
if (whitelist && JSON.stringify(whitelist) != "{}") {
|
//当任务白名单不为空时,利用白名单判断频率的合法性
|
|
let whitelist_map = await getWhitelist(whitelist); //白名单详细数据
|
//由于任务白名单保存的是白名单id,先获取白名单频率到id的映射
|
let freq_map = {};
|
for (let whitelist_id in whitelist) {
|
freq_map[whitelist_map[whitelist_id].frequency] = whitelist_id;
|
}
|
//对扫台结果的频率进行逐个判别
|
for (let i = 0; i < freq_in_range.length; i++) {
|
const freq = freq_in_range[i];
|
if (freq_map[freq] == undefined) {
|
//当频率不在任务白名单中时
|
//直接将频率分类为非法电台
|
freq_list.push(freq);
|
identifier_list.push({
|
task_type: "周期任务",
|
task_record_id: task_record_id,
|
result_type: 2 //类型:频率非法
|
});
|
} else {
|
//当频率在任务白名单中时
|
const wid = freq_map[freq]; //频率所对应的白名单id
|
let time_legal = true; //时间合法性
|
let area_legal = true; //区域合法性
|
|
//判断时间是否合法
|
let task_time = new Date(params.start); //任务开始时间
|
let i_day = task_time.getDay(); //星期
|
i_day = i_day == 0 ? 7 : i_day;
|
if (whitelist_map[wid].runtime) {
|
//如果设置了运行时
|
const runtime_day = whitelist_map[wid].runtime[i_day];
|
if (runtime_day && runtime_day.length > 0) {
|
//如果设置了当天的运行时
|
let i_hour = task_time.getHours();
|
time_legal = false;
|
for (let index = 0; index < runtime_day.length; index++) {
|
if (i_hour == runtime_day[index]) {
|
time_legal = true;
|
break;
|
}
|
}
|
} else {
|
//如果当天的运行时未设置,则判别为非法
|
time_legal = false;
|
}
|
} else {
|
//如果没有设置运行时,则直接判别频率不合法
|
time_legal = false;
|
}
|
|
//TODO: 判断区域是否合法
|
|
//合法性判别
|
if (time_legal && area_legal) {
|
//如果时间区域均合法且设置了录音
|
if (whitelist[wid] * 1) {
|
freq_list.push(freq);
|
identifier_list.push({
|
task_type: "周期任务",
|
task_record_id: task_record_id,
|
result_type: 1 //类型:合法电台
|
});
|
}
|
} else {
|
//优先判别时间合法性(即:如果时间非法则不再判断区域非法)
|
if (!time_legal) {
|
freq_list.push(freq);
|
identifier_list.push({
|
task_type: "周期任务",
|
task_record_id: task_record_id,
|
result_type: 3 //类型:时间非法
|
});
|
} else {
|
freq_list.push(freq);
|
identifier_list.push({
|
task_type: "周期任务",
|
task_record_id: task_record_id,
|
result_type: 4 //类型:区域非法
|
});
|
}
|
}
|
}
|
}
|
} else {
|
//当任务的白名单为空时,则将全部扫描结果判定为非法电台
|
for (let i = 0; i < freq_in_range.length; i++) {
|
const freq = freq_in_range[i];
|
freq_list.push(freq);
|
identifier_list.push({
|
task_type: "周期任务",
|
task_record_id: task_record_id,
|
result_type: 2 //类型:频率非法
|
});
|
}
|
}
|
ctx.body = {
|
success: true,
|
gray_list: freq_list, //需要录音的频率数组
|
identifier_list: identifier_list, //录音命令对应的数据标识
|
recording_length: recording_length //秒
|
};
|
}
|
ctx.body = {
|
success: true,
|
gray_list: []
|
};
|
});
|
|
//录音结果上报
|
router.get("/ReportRecordResult", async function(ctx, next) {
|
let params = ctx.query;
|
const identifier = JSON.parse(params.identifier);
|
let sql = `INSERT INTO fm_t_scan_record(task_record_id,frequency,lon,lat,signal_strength,radio_file_path,result,upload_time)
|
VALUES(${identifier.task_record_id},
|
${Math.round(params.frequency * 10) / 10},
|
${params.lon},
|
${params.lat},
|
${params.rssi},
|
'/uploads/${params.file}',
|
'${identifier.result_type}',
|
'${params.start}'
|
);`;
|
mysql.query(sql);
|
//由于即时任务的录音,其task_record 和 scan_record是一对一的
|
//所以对于即时录音任务,在录音完成后可更新任务记录表中的任务结束时间
|
if (identifier.task_type == "即时任务") {
|
setDeviceStatus([ctx.session.log_id], "1"); //完成录音 更新状态为 空闲
|
sql = `UPDATE fm_t_task_record SET finish_time='${params.end}' WHERE id=${identifier.task_record_id};`;
|
mysql.query(sql);
|
}
|
ctx.body = {
|
success: true
|
};
|
});
|
|
/**
|
* 计算下一个周期任务的执行时间
|
* @param {number} start 起算点(时间戳)
|
* @param {number} period 周期
|
* @param {number} end 结束点(时间戳)
|
*/
|
function f_next(start, period, end) {
|
let t = Math.ceil((Date.now() - start) / period) * period + start;
|
if (end) {
|
t = t <= end ? t : -1;
|
}
|
return t;
|
}
|
|
/**
|
* 判断目标频率是否在频率区间内
|
* @param {number} target 目标频率
|
* @param {Array<Array<number>>} ranges 扫描频率范围
|
*/
|
function f_inRanges(target, ranges) {
|
target = target * 1;
|
for (let i = 0; i < ranges.length; i++) {
|
const range = ranges[i];
|
if (range.length == 1) {
|
if (target == range[0]) return true;
|
} else if (range.length == 2) {
|
if (target >= range[0] && target <= range[1]) return true;
|
} else {
|
console.log("警告");
|
}
|
}
|
return false;
|
}
|
|
/**
|
* 获取任务白名单的详细信息
|
* @param {JSON} whitelist_of_task 任务白名单
|
*/
|
async function getWhitelist(whitelist_of_task) {
|
let ids = [];
|
for (let id in whitelist_of_task) {
|
ids.push(id);
|
}
|
let sql = `SELECT id,frequency,runtime,area from fm_t_whitelist where id IN (${ids.join(
|
","
|
)});`;
|
let result = await mysql.query(sql);
|
whitelist_map = {};
|
for (let i = 0; i < result.length; i++) {
|
let element = result[i];
|
if (element.runtime) {
|
element.runtime = JSON.parse(element.runtime);
|
//处理运行时为空对象的情况(正常情况下是不应该有这种情况的)
|
if (JSON.stringify(element.runtime) == "{}") {
|
element.runtime = null;
|
}
|
}
|
whitelist_map[element.id] = element;
|
}
|
return whitelist_map;
|
}
|
|
/**
|
* 设置设备的状态(主要用于下发即时任务和完成任务时改变设备的状态)
|
* @param {Array<String>} device_list 设备逻辑号列表
|
* @param {String} status 设备状态
|
*/
|
function setDeviceStatus(device_list, status) {
|
let sql = `UPDATE fm_t_device SET status='${status}' WHERE log_id IN ('${device_list.join(
|
"','"
|
)}');`;
|
mysql.query(sql);
|
}
|
|
module.exports = router;
|