From 20f2053a86fbb838558a9d14745bd7f8bfe935e5 Mon Sep 17 00:00:00 2001 From: Ivan Date: Mon, 21 Aug 2023 06:28:58 +0300 Subject: [PATCH] archive, bug fixes --- server.js | 214 ++++++-- static/img/left.svg | 11 + static/img/play-circle.svg | 11 + static/img/right.svg | 11 + static/styles/main.css | 269 ++++++++- static/templates/devices/drivers.html | 2 +- static/templates/devices/index.html | 2 +- static/templates/devices/update.html | 2 +- static/templates/index.html | 2 +- static/templates/live.html | 134 ++++- static/templates/reports/index.html | 2 +- static/templates/reports/report.html | 12 +- static/templates/videos/see.html | 760 ++++++++++++++++++++++++++ 13 files changed, 1351 insertions(+), 81 deletions(-) create mode 100644 static/img/left.svg create mode 100644 static/img/play-circle.svg create mode 100644 static/img/right.svg create mode 100644 static/templates/videos/see.html diff --git a/server.js b/server.js index 9ad32d5..b12ac3f 100644 --- a/server.js +++ b/server.js @@ -30,6 +30,7 @@ app.get("/reports", reports); app.get("/devices", devices); app.get("/devices/drivers", drivers); app.get("/devices/update", update); +app.get("/videos", videos); // const DB_User = process.env.DB_USER; @@ -64,7 +65,7 @@ async function index(req, res) { port: DB_Port, }); const client = await pool.connect(); - // Выполняем запрос и получаем результат + const query = ` SELECT COUNT(*) AS count FROM registrars @@ -74,40 +75,40 @@ async function index(req, res) { templateData.Count = registrars.rows[0].count; const last11DaysQuery = ` - SELECT COUNT(DISTINCT evtuuid) AS count - FROM alarms - WHERE time >= NOW() - INTERVAL '10 days' + INTERVAL '3 hours' - AND time <= NOW() + INTERVAL '1 day' + INTERVAL '3 hours' - AND st IS NOT NULL - GROUP BY DATE_TRUNC('day', time) - ORDER BY DATE_TRUNC('day', time) + WITH date_sequence AS ( + SELECT DATE_TRUNC('day', NOW() - INTERVAL '10 days') + (generate_series(0, 10) || ' days')::interval AS day + ) + SELECT + date_sequence.day AS day, + COALESCE(COUNT(DISTINCT evtuuid), 0) AS count + FROM date_sequence + LEFT JOIN alarms ON DATE_TRUNC('day', alarms.time) = date_sequence.day + AND alarms.time >= NOW() - INTERVAL '10 days' + INTERVAL '3 hours' + AND alarms.time <= NOW() + INTERVAL '1 day' + INTERVAL '3 hours' + AND alarms.st IS NOT NULL + GROUP BY date_sequence.day + ORDER BY date_sequence.day DESC `; const last11DaysAlarms = await client.query(last11DaysQuery); - const last11DaysCounts = last11DaysAlarms.rows.map(row => parseInt(row.count, 10)); - for (let i = 0; i < last11DaysCounts.length; i++) { - if (last11DaysCounts[i] > 0) { - templateData.AlarmsLast11Days[i] = last11DaysCounts[i]; - } - } const daysBeforeQuery = ` - SELECT COUNT(DISTINCT evtuuid) AS count - FROM alarms - WHERE time >= NOW() - INTERVAL '21 days' + INTERVAL '3 hours' - AND time <= NOW() - INTERVAL '10 days' + INTERVAL '3 hours' - AND st IS NOT NULL - GROUP BY DATE_TRUNC('day', time) - ORDER BY DATE_TRUNC('day', time) - `; - const daysBeforeAlarms = await client.query(daysBeforeQuery); - const daysBeforeCounts = daysBeforeAlarms.rows.map(row => parseInt(row.count, 10)); - for (let i = 0; i < daysBeforeCounts.length; i++) { - if (daysBeforeCounts[i] > 0) { - templateData.Alarms11DaysBefore[i] = daysBeforeCounts[i]; - } - } + WITH date_sequence AS ( + SELECT DATE_TRUNC('day', NOW() - INTERVAL '21 days') + (generate_series(0, 10) || ' days')::interval AS day + ) + SELECT + date_sequence.day AS day, + COALESCE(COUNT(DISTINCT evtuuid), 0) AS count + FROM date_sequence + LEFT JOIN alarms ON DATE_TRUNC('day', alarms.time) = date_sequence.day + AND alarms.time >= NOW() - INTERVAL '21 days' + INTERVAL '3 hours' + AND alarms.time <= NOW() - INTERVAL '10 days' + INTERVAL '3 hours' + AND alarms.st IS NOT NULL + GROUP BY date_sequence.day + ORDER BY date_sequence.day DESC +`; + + const daysBeforeAlarms = await client.query(daysBeforeQuery); - // Создание массива дат в формате "dd.mm" за последние 11 дней const currentDate = new Date(); const dates = []; for (let i = 10; i >= 0; i--) { @@ -118,22 +119,50 @@ async function index(req, res) { templateData.Dates = dates; const positionsLast11DaysQuery = ` - SELECT COUNT(*) AS count - FROM geo - WHERE time >= NOW() - INTERVAL '10 days' + INTERVAL '3 hours' - AND time <= NOW() + INTERVAL '1 day' + INTERVAL '3 hours' - GROUP BY DATE_TRUNC('day', time) - ORDER BY DATE_TRUNC('day', time) + SELECT + COUNT(*) AS count, + DATE_TRUNC('day', time) AS day, + CASE WHEN COUNT(*) = 0 THEN 0 ELSE 1 END AS sort_value + FROM geo + WHERE time >= NOW() - INTERVAL '10 days' + INTERVAL '3 hours' + AND time <= NOW() + INTERVAL '1 day' + INTERVAL '3 hours' + GROUP BY DATE_TRUNC('day', time) + ORDER BY sort_value DESC, day DESC `; const positionsLast11Days = await client.query(positionsLast11DaysQuery); - const positionsLast11DaysCounts = positionsLast11Days.rows.map(row => parseInt(row.count, 10)); - for (let i = 0; i < positionsLast11DaysCounts.length; i++) { - if (positionsLast11DaysCounts[i] > 0) { - templateData.PositionsLast11Days[i] = positionsLast11DaysCounts[i]; - } - } + // console.log(positionsLast11Days.rows) + // const positionsLast11DaysCounts = positionsLast11Days.rows.map(row => parseInt(row.count, 10)); + // for (let i = 0; i < positionsLast11DaysCounts.length; i++) { + // if (positionsLast11DaysCounts[i] > 0) { + // templateData.PositionsLast11Days[i] = positionsLast11DaysCounts[i]; + // } + // } - // console.log(templateData); + templateData.Dates.reverse(); + +const last11DaysMap = new Map(last11DaysAlarms.rows.map(row => [row.day.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit' }), parseInt(row.count, 10)])); + +for (let i = 0; i < dates.length; i++) { + const dateKey = dates[i]; + templateData.AlarmsLast11Days[i] = last11DaysMap.has(dateKey) ? last11DaysMap.get(dateKey) : 0; +} + +const beforeDaysMap = new Map(daysBeforeAlarms.rows.map(row => [row.day.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit' }), parseInt(row.count, 10)])); + +for (let i = 0; i < dates.length; i++) { + const dateKey = dates[i]; + templateData.Alarms11DaysBefore[i] = beforeDaysMap.has(dateKey) ? beforeDaysMap.get(dateKey) : 0; +} + +const positionsMap = new Map(positionsLast11Days.rows.map(row => [row.day.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit' }), parseInt(row.count, 10)])); + +for (let i = 0; i < dates.length; i++) { + const dateKey = dates[i]; + templateData.PositionsLast11Days[i] = positionsMap.has(dateKey) ? positionsMap.get(dateKey) : 0; +} + + + console.log(templateData); const source = fs.readFileSync("static/templates/index.html", "utf8"); @@ -1437,6 +1466,105 @@ function update(req, res) { res.sendFile(path.join(__dirname, "static/templates/devices/update.html")); } +async function videos(req, res) { + let templateData = { + Organisation: "Название организации", + User: "Тестовое Имя", + ifDBError: false, + Registrars: [], + }; + + try { + const pool = new Pool({ + user: DB_User, + host: DB_Host, + database: DB_Name, + password: DB_Password, + port: DB_Port, + }); + const client = await pool.connect(); + + const minuteInMillis = 60 * 1000; + + const query = ` + SELECT id, serial, lastkeepalive FROM registrars ORDER BY id ASC + `; + const registrars = await client.query(query); + + templateData.Registrars = registrars.rows.map((row) => ({ + id: row.id, + serial: row.serial, + status: Date.now() - Date.parse(row.lastkeepalive) <= minuteInMillis, + })); + + console.log(templateData); + + const source = fs.readFileSync("static/templates/videos/see.html", "utf8"); + const template = handlebars.compile(source); + const resultHTML = template(templateData); + res.send(resultHTML); + + client.release(); + } catch (error) { + console.error(error); + templateData.ifDBError = true; + + const source = fs.readFileSync("static/templates/videos/see.html", "utf8"); + const template = handlebars.compile(source); + const resultT = template(templateData); + res.send(resultT); + } +} + + +app.post("/getspeedarchive", async (req, res) => { + const { serial, datetime } = req.body; + + const formattedDateTime = new Date(datetime).toISOString(); + const pool = new Pool({ + user: DB_User, + host: DB_Host, + database: DB_Name, + password: DB_Password, + port: DB_Port, + }); + const client = await pool.connect(); + const sqlQuery = ` + SELECT speed, latitude, longitude + FROM geo + WHERE serial = $1 + AND time >= $2 + AND time < $3; + `; + + const startTime = new Date(formattedDateTime); + startTime.setMinutes(0, 0, 0); // Округление до начала часа + const endTime = new Date(startTime); + endTime.setHours(endTime.getHours() + 1); + + pool.query(sqlQuery, [serial, startTime, endTime], (error, results) => { + if (error) { + console.error("Ошибка при выполнении SQL-запроса:", error); + res.status(500).json({ error: "Ошибка на сервере" }); + } else { + const speeds = results.rows.map((row) => row.speed); + const transformedSpeeds = speeds.map((speed) => { + if (speed > 150) { + return speed / 100; + } else { + return speed; + } + }); + + const geoData = results.rows.map((row) => ({ + latitude: row.latitude, + longitude: row.longitude, + })); + res.json({ speeds: transformedSpeeds, geo: geoData }); + } + }); +}); + const port = 8081; app.listen(port, () => { console.log(`Server is running on port ${port}`); diff --git a/static/img/left.svg b/static/img/left.svg new file mode 100644 index 0000000..a9bd69f --- /dev/null +++ b/static/img/left.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/static/img/play-circle.svg b/static/img/play-circle.svg new file mode 100644 index 0000000..bd9f83b --- /dev/null +++ b/static/img/play-circle.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/static/img/right.svg b/static/img/right.svg new file mode 100644 index 0000000..1f1ffb9 --- /dev/null +++ b/static/img/right.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/static/styles/main.css b/static/styles/main.css index e8dd9ed..52f8b41 100644 --- a/static/styles/main.css +++ b/static/styles/main.css @@ -478,6 +478,7 @@ header h2 span { border: 2px solid rgba(245, 245, 250, 1); border-radius: 30px; overflow: hidden; + position: relative; } .table h1 { @@ -1401,17 +1402,6 @@ input[type="datetime-local"] { padding: 12px 33px 26px 33px; } -.video-container { - position: relative; - width: 500px; -} - -video { - width: 450px; - display: inline-block; - border-right: 2px solid rgba(245, 245, 250, 1); -} - .controls { width: 100%; display: flex; @@ -1476,7 +1466,7 @@ video { transition: 1s; position: relative; float: left; - z-index: 10; + z-index: 3; } .signals-list.hide { @@ -1612,12 +1602,24 @@ video { background: rgba(0, 0, 0, 0.10); } -.map { +.stream-map { /* background-color: #ff443a2f; */ width: 100%; height: 100%; display: block; - /* float: right; */ + position: absolute; + bottom: 0; + right: 0; + +} + +.map { + height: 310px; + width: 25%; + display: block; + position: absolute; + bottom: 0; + left: 0; } #map { @@ -1631,7 +1633,7 @@ video { position: absolute; top: 10px; left: 50%; - z-index: 10; + z-index: 3; background-color: white; padding: 9px 11px; border-radius: 10px; @@ -1674,7 +1676,7 @@ video { font-weight: bold; } -.cameras { +.stream-cameras { background: #F5F5FA; position: absolute; bottom: 0; @@ -1682,6 +1684,16 @@ video { width: calc(100% - 345px - 2px); z-index: 3; } +.cameras { + background: #F5F5FA; + position: absolute; + top: 0; + right: 0; + width: 100%; + height: calc(100% - 310px); + z-index: 3; + display: inline-flex; +} .cameras-swipe { display: flex; @@ -1700,13 +1712,13 @@ video { background: #dcdce2; } -.video-container { +.stream-video-container { display: flex; flex-wrap: wrap; width: 100%; } -.video-container video { +.stream-video-container video { height: auto; box-sizing: border-box; width: 25%; @@ -1714,6 +1726,79 @@ video { border: 1px solid white; } +.video-container { + display: flex; + flex-wrap: wrap; + width: 80%; + float: left; +} + +.video-container div { + height: auto; + box-sizing: border-box; + width: 25%; + padding: 0; + height: 33.4%; + border: 1px solid white; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + justify-content: center; +} + +.video-container div, +.video-container-right div { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + justify-content: center; + cursor: pointer; + transition: 0.1s; +} + +.video-container div:hover, +.video-container-right div:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.video-container div img, +.video-container-right div img { + height: 32px; + width: 32px; +} + +.video-container div span, +.video-container-right div span { + font-size: 16px; + font-weight: 500; + margin-top: 15px; +} + +.video-container-right { + display: flex; + flex-wrap: wrap; + float: right; + width: 20%; + float: right; +} + +.video-container-right div { + height: auto; + box-sizing: border-box; + width: 100%; + height: 25%; + padding: 0; + border: 1px solid white; + +} + +.report video { + width: 450px; + border-right: 2px solid rgba(245, 245, 250, 1); +} + .edit-container { position: fixed; @@ -1806,6 +1891,154 @@ video { margin-bottom: 0; } +/* Стили для вашего календаря */ +.calendar { + width: 250px; + height: 300px; + margin: 5px; + padding: 0 25px; + border-radius: 15px; + overflow: hidden; + position: absolute; + bottom: 0; + left: 25%; + background-color:white; + margin: 4px; +} + +.calendar-header { + display: flex; + justify-content: space-between; + align-items: center; + /* background-color: #f0f0f0; */ + padding: 20px 5px; + border-bottom: 1px solid #ccc; +} + +.calendar-header h2 { + font-size: 16px; + font-weight: 600; + margin: 0; + color: rgba(0, 0, 0, 0.90); +} + +#prevMonth { + background-image: url(../img/left.svg); +} + +#nextMonth { + background-image: url(../img/right.svg); +} + +#prevMonth, #nextMonth { + background-color: transparent; + border: none; + height: 18px; + cursor: pointer; + background-repeat: no-repeat; +} + +.daysOfWeek { + display: flex; + justify-content: space-around; + padding: 15px 0; + font-size: 14px; + color: #7E818C; +} + +.dates { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 10px; +} + +.date { + width: 26px; + height: 26px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + border-radius: 50%; + font-size: 14px; + color: rgba(0, 0, 0, 0.75); +} + +.date:hover { + background-color: #8086F939; +} + +.date.selected { + background-color: #8086F9; + color: white; +} + +.video-popup { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + /* background-color: rgba(0, 0, 0, 0.6); */ + z-index: 999; +} + +.video-popup-content { + position: absolute; + /* top: 10%; */ + /* left: 50%; */ + transform: translate(-50%, -50%); + /* background-color: white; */ + padding: 20px; + /* box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); */ + text-align: center; + z-index: 101; +} + +.close-popup { + position: absolute; + top: 10px; + right: 10px; + font-size: 24px; + cursor: pointer; +} + +.popup-video-container { + width: 80%; + max-width: 800px; + margin: 0 auto; +} + +.video-time { + width: calc(75% - 300px - 10px - 5px); + height: 50px; + position: absolute; + right: 7px; + bottom: 7px; + background-color:white; + border-radius: 15px; +} +input[type="time"]::-webkit-calendar-picker-indicator { + display: none; +} + +.video-time input[type="time"] { + padding: 6px; + border-radius: 10px; + border: 1px solid rgba(0, 0, 0, 0.10); + background: #FFF; + font-size: 16px; + outline: none; + cursor: text; + margin: 8px !important; +} + +input[type="time"]:hover, +input[type="time"]:focus { + border: 1px solid rgba(0, 0, 0, 0.30); +} + @media (max-width: 1950px) { /* при разрешении монитора до 1950 пикселей */ diff --git a/static/templates/devices/drivers.html b/static/templates/devices/drivers.html index 4bafed0..2e30eb1 100644 --- a/static/templates/devices/drivers.html +++ b/static/templates/devices/drivers.html @@ -40,7 +40,7 @@
Трансляция
- +
Записи
diff --git a/static/templates/devices/index.html b/static/templates/devices/index.html index dfdaee1..46da604 100644 --- a/static/templates/devices/index.html +++ b/static/templates/devices/index.html @@ -40,7 +40,7 @@
Трансляция
- +
Записи
diff --git a/static/templates/devices/update.html b/static/templates/devices/update.html index a94e368..d7411df 100644 --- a/static/templates/devices/update.html +++ b/static/templates/devices/update.html @@ -40,7 +40,7 @@
Трансляция
- +
Записи
diff --git a/static/templates/index.html b/static/templates/index.html index b145335..7ecc38e 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -42,7 +42,7 @@
Трансляция
- +
Записи
diff --git a/static/templates/live.html b/static/templates/live.html index f72535e..5b20d8f 100644 --- a/static/templates/live.html +++ b/static/templates/live.html @@ -45,7 +45,7 @@
Трансляция
- +
Записи
@@ -115,6 +115,15 @@ +
+
+ × + +
+
+
@@ -128,7 +137,8 @@