commit
940334af9a
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,8 @@ |
||||
{ |
||||
"dependencies": { |
||||
"dotenv": "^16.4.5", |
||||
"express": "^4.19.1", |
||||
"pg": "^8.11.3", |
||||
"sequelize": "^6.37.1" |
||||
} |
||||
} |
||||
@ -0,0 +1,367 @@ |
||||
const util = require('util'); |
||||
const express = require('express'); |
||||
const bodyParser = require('body-parser'); |
||||
const { Sequelize, DataTypes } = require('sequelize'); |
||||
require('dotenv').config(); |
||||
process.env.TZ = "Europe/Moscow"; |
||||
|
||||
const sequelize = new Sequelize('TrashSensor', process.env.DB_USER, process.env.DB_PASS, { |
||||
host: 'localhost', |
||||
dialect: 'postgres', |
||||
schema: 'sensors', |
||||
logging: false |
||||
}); |
||||
|
||||
const Sensor = sequelize.define('Sensor', { |
||||
id: { |
||||
type: DataTypes.INTEGER, |
||||
primaryKey: true, |
||||
autoIncrement: true, |
||||
}, |
||||
name: { |
||||
type: DataTypes.STRING, |
||||
allowNull: false, |
||||
}, |
||||
address: { |
||||
type: DataTypes.STRING, |
||||
allowNull: false, |
||||
}, |
||||
lat: { |
||||
type: DataTypes.FLOAT, |
||||
allowNull: false, |
||||
unique: true, |
||||
}, |
||||
lng: { |
||||
type: DataTypes.FLOAT, |
||||
allowNull: false, |
||||
unique: true, |
||||
} |
||||
}); |
||||
|
||||
const FullnessData = sequelize.define('FullnessData', { |
||||
id: { |
||||
type: DataTypes.INTEGER, |
||||
primaryKey: true, |
||||
autoIncrement: true, |
||||
}, |
||||
containerNumber: { |
||||
type: DataTypes.INTEGER, |
||||
allowNull: false, |
||||
}, |
||||
percent: { |
||||
type: DataTypes.INTEGER, |
||||
}, |
||||
batLevel: { |
||||
type: DataTypes.INTEGER, |
||||
} |
||||
}, |
||||
// {
|
||||
// timestamps: false
|
||||
// }
|
||||
); |
||||
|
||||
const DoorData = sequelize.define('DoorData', { |
||||
id: { |
||||
type: DataTypes.INTEGER, |
||||
primaryKey: true, |
||||
autoIncrement: true, |
||||
}, |
||||
containerNumber: { |
||||
type: DataTypes.INTEGER, |
||||
allowNull: false, |
||||
}, |
||||
isOpened: { |
||||
type: DataTypes.BOOLEAN, |
||||
} |
||||
}, |
||||
// {
|
||||
// timestamps: false
|
||||
// }
|
||||
); |
||||
|
||||
Sensor.hasMany(FullnessData, { as: "FullnessData" }); |
||||
FullnessData.belongsTo(Sensor, { as: "Sensor" }); |
||||
|
||||
Sensor.hasMany(DoorData, { as: "DoorData" }); |
||||
DoorData.belongsTo(Sensor, { as: "Sensor" }); |
||||
|
||||
//const app = express();
|
||||
const exp = express(); |
||||
const app = express.Router(); // Создание экземпляра маршрутизатора для API
|
||||
exp.use('/api', app); |
||||
|
||||
const port = 1778; |
||||
|
||||
const logging = (req, res, next) => { |
||||
var ip = req.ip; |
||||
console.log("------------------------------------------------------------"); |
||||
|
||||
const curData = new Date(); |
||||
//console.log(curData.toISOString())
|
||||
console.log(curData.toLocaleString("ru-RU", { timeZone: process.env.TZ })) |
||||
|
||||
console.log('Request from', ip); |
||||
console.log(`Request received for ${req.method} ${req.url}`); |
||||
console.log(`Request body: ${util.inspect(req.body)}`); |
||||
|
||||
next(); |
||||
}; |
||||
|
||||
app.use(bodyParser.urlencoded({ extended: true })); |
||||
app.use(logging); |
||||
|
||||
const authenticateToken = (req, res, next) => { |
||||
const token = req.header('Authorization'); |
||||
if (!token) return res.sendStatus(401); |
||||
|
||||
jwt.verify(token, secretKey, (err, user) => { |
||||
if (err) return res.sendStatus(403); |
||||
req.user = user; |
||||
next(); |
||||
}); |
||||
}; |
||||
|
||||
app.post('/login', (req, res) => { |
||||
const username = req.body.username; |
||||
const password = req.body.password; |
||||
|
||||
const role = getRole(username, password); |
||||
console.log(role); |
||||
const user = { username: username, role: role }; |
||||
|
||||
const token = jwt.sign(user, secretKey); |
||||
res.json({ token: token }); |
||||
}); |
||||
|
||||
app.get('/', (req, res) => { |
||||
res.send('OK'); |
||||
}); |
||||
|
||||
app.get('/sensors', async (req, res) => { |
||||
try { |
||||
const { _start = 0, _end = 100, _sort = 'createdAt', _order = 'ASC' } = req.query |
||||
|
||||
const data = await FullnessData.findAll({ |
||||
attributes: [ |
||||
'id', |
||||
[Sequelize.col('Sensor.name'), 'name'], |
||||
[Sequelize.col('Sensor.address'), 'address'], |
||||
[Sequelize.col('Sensor.lat'), 'lat'], |
||||
[Sequelize.col('Sensor.lng'), 'lng'], |
||||
'percent', |
||||
'batLevel', |
||||
], |
||||
include: [ |
||||
{ |
||||
model: Sensor, |
||||
as: "Sensor", |
||||
attributes: [], |
||||
required: true, |
||||
} |
||||
], |
||||
order: [[_sort, _order]], |
||||
offset: parseInt(_start), |
||||
limit: parseInt(_end) - parseInt(_start), |
||||
}); |
||||
|
||||
res.header('Access-Control-Allow-Origin', '*') |
||||
res.header('Access-Control-Expose-Headers', 'X-Total-Count') |
||||
res.header('X-Total-Count', data.length); |
||||
|
||||
res.json(data); |
||||
} catch (error) { |
||||
console.error('Error fetching data', error); |
||||
res.status(500).json({ error: 'Internal Server Error' }); |
||||
} |
||||
}); |
||||
|
||||
app.get('/sensors', async (req, res) => { |
||||
try { |
||||
const { _start = 0, _end = 100, _sort = 'createdAt', _order = 'ASC' } = req.query |
||||
|
||||
const data = await FullnessData.findAll({ |
||||
attributes: [ |
||||
'id', |
||||
[Sequelize.col('Sensor.name'), 'name'], |
||||
[Sequelize.col('Sensor.address'), 'address'], |
||||
[Sequelize.col('Sensor.lat'), 'lat'], |
||||
[Sequelize.col('Sensor.lng'), 'lng'], |
||||
'percent', |
||||
'batLevel', |
||||
], |
||||
include: [ |
||||
{ |
||||
model: Sensor, |
||||
as: "Sensor", |
||||
attributes: [], |
||||
required: true, |
||||
} |
||||
], |
||||
order: [[_sort, _order]], |
||||
offset: parseInt(_start), |
||||
limit: parseInt(_end) - parseInt(_start), |
||||
}); |
||||
|
||||
res.header('Access-Control-Allow-Origin', '*') |
||||
res.header('Access-Control-Expose-Headers', 'X-Total-Count') |
||||
res.header('X-Total-Count', data.length); |
||||
|
||||
res.json(data); |
||||
} catch (error) { |
||||
console.error('Error fetching data', error); |
||||
res.status(500).json({ error: 'Internal Server Error' }); |
||||
} |
||||
}); |
||||
|
||||
app.get('/now', async (req, res) => { |
||||
const sensors = await Sensor.findAll({ |
||||
order: [['id', 'ASC']] |
||||
}); |
||||
|
||||
let result = []; |
||||
for (let sens of sensors) { |
||||
for (let i = 0; i < 7; i++) { |
||||
const infoFull = await FullnessData.findOne({ |
||||
where: { SensorId: sens.id, containerNumber: i }, |
||||
order: [['createdAt', 'DESC']] |
||||
}); |
||||
|
||||
const infoDoor = await DoorData.findOne({ |
||||
where: { SensorId: sens.id, containerNumber: i }, |
||||
order: [['createdAt', 'DESC']] |
||||
}); |
||||
|
||||
const date = infoFull ? formatDateTime(new Date(infoFull.createdAt)) : '-'; |
||||
|
||||
result.push({ |
||||
id: `${sens.id}.${i}`, name: sens.name, address: sens.address, |
||||
lat: sens.lat, lng: sens.lng, percent: infoFull ? infoFull.percent : '-', |
||||
batLevel: infoFull ? infoFull.batLevel : '-', isDoorOpened: infoDoor ? infoDoor.isOpened : '-', timeAt: date |
||||
}) |
||||
} |
||||
/* for (let i = 0; i < info.length; i++) { |
||||
const date = new Date(info[i].createdAt) |
||||
result.push({ |
||||
id: `${sens.id}.${info[i].containerNumber}`, name: sens.name, address: sens.address, |
||||
lat: sens.lat, lng: sens.lng, percent: info[i].percent, |
||||
batLevel: info[i].batLevel, timeAt: formatDateTime(date) |
||||
}) |
||||
}*/ |
||||
|
||||
|
||||
} |
||||
result.sort((a, b) => b.timeAt - a.timeAt); |
||||
res.header('Access-Control-Allow-Origin', '*') |
||||
res.header('Access-Control-Expose-Headers', 'X-Total-Count') |
||||
res.header('X-Total-Count', result.length); |
||||
|
||||
res.json(result); |
||||
}); |
||||
|
||||
app.post('/data', async (req, res) => { |
||||
if (!req.body) return res.sendStatus(400); |
||||
|
||||
try { |
||||
const info = req.body.data; |
||||
console.log(info); |
||||
const fillLevelRegex = /SN\.SR\.FU\.LL\.(\w+)\.(\w+)\.(\w+)/; |
||||
const doorStatusRegex = /SN\.SR\.DO\.OR\.(\w+)\.(\w+)/; |
||||
|
||||
if (doorStatusRegex.test(info)) { |
||||
const match = info.match(doorStatusRegex); |
||||
const sensorId = parseInt(match[1], 16); |
||||
const doorStatus = match[2].split(''); |
||||
console.log(`ID датчика: ${sensorId}, Состояние дверей: ${doorStatus.join(', ') || 'N/A'}`); |
||||
console.log(doorStatus); |
||||
|
||||
for (let i = 0; i < doorStatus.length; i++) { |
||||
if (doorStatus[i] == 'x') { |
||||
continue; |
||||
} |
||||
|
||||
console.log(i, doorStatus[i]); |
||||
|
||||
const sensorReady = await Sensor.findOne({ |
||||
where: { id: sensorId }, |
||||
}); |
||||
|
||||
if (sensorReady) |
||||
DoorData.create({ isOpened: doorStatus[i] == 0 ? false : true, SensorId: sensorId, containerNumber: i }); |
||||
else |
||||
res.sendStatus(400); |
||||
} |
||||
res.status(200).json({ data: 'OK' }); |
||||
} |
||||
else if (fillLevelRegex.test(info)) { |
||||
const match = info.match(fillLevelRegex); |
||||
const sensorId = parseInt(match[1], 16); |
||||
const fillLevelsHex = match[2].split(''); |
||||
const batteryLevelHex = match[3]; |
||||
|
||||
const fillLevelsDec = fillLevelsHex.map(hex => { |
||||
if (hex === 'x') return 'x'; |
||||
if (hex === 'e') return -1; |
||||
if (hex === 'f') return 100; |
||||
return parseInt(hex, 16) * 10; |
||||
}); // Фильтрация пустых значений
|
||||
|
||||
const batteryLevelDec = parseInt(batteryLevelHex, 16); |
||||
|
||||
console.log(`ID датчика: ${sensorId}, Уровни наполненности: ${fillLevelsDec.join('%, ') || 'N/A'}, Заряд батареи: ${batteryLevelDec}%`); |
||||
|
||||
console.log(fillLevelsDec); |
||||
|
||||
const sensorReady = await Sensor.findOne({ |
||||
where: { id: sensorId }, |
||||
}); |
||||
|
||||
if (sensorReady) |
||||
for (let i = 0; i < fillLevelsDec.length; i++) { |
||||
if (fillLevelsDec[i] == 'x') { |
||||
continue; |
||||
} |
||||
const newData = await FullnessData.create({ percent: fillLevelsDec[i], batLevel: batteryLevelDec, SensorId: sensorId, containerNumber: i }); |
||||
} |
||||
else |
||||
res.sendStatus(400); |
||||
|
||||
res.header('Access-Control-Allow-Origin', '*'); |
||||
res.status(200).json({ data: 'OK' }); |
||||
|
||||
} else { |
||||
console.log('Неизвестный формат строки'); |
||||
res.sendStatus(400); |
||||
} |
||||
|
||||
} catch (error) { |
||||
console.error('Error creating', error); |
||||
res.status(500).json({ error: 'Internal Server Error' }); |
||||
} |
||||
}); |
||||
|
||||
sequelize.sync({ alter: true }).then(() => { |
||||
exp.listen(port, '0.0.0.0', () => { |
||||
console.log(`Server is running on http://localhost:${port}`); |
||||
}); |
||||
}); |
||||
|
||||
function getRole(username, password) { |
||||
if (username == 'rfid_esp32' && password == 'skbkit2024') |
||||
return 'rfid'; |
||||
|
||||
if (username == 'vladimir' && password == 'vladimir_kit2024') |
||||
return 'admin'; |
||||
|
||||
return 'user'; |
||||
} |
||||
|
||||
function formatDateTime(date) { |
||||
const hours = String(date.getHours()).padStart(2, '0'); |
||||
const minutes = String(date.getMinutes()).padStart(2, '0'); |
||||
const seconds = String(date.getSeconds()).padStart(2, '0'); |
||||
const day = String(date.getDate()).padStart(2, '0'); |
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); |
||||
const year = date.getFullYear(); |
||||
|
||||
return `${day}.${month}.${year} ${hours}:${minutes}:${seconds}`; |
||||
} |
||||
Loading…
Reference in new issue