module.exports = function (io, sql, fs, brk, async, app_cfg) { // Module laden const { parse } = require('json2csv'); const nodemailer = require('nodemailer'); function waip_speichern(einsatz_daten) { // Einsatzmeldung in Datenbank speichern und verteilen sql.db_einsatz_speichern(einsatz_daten, function (waip_id) { sql.db_log('DEBUG', 'Neuen Einsatz mit der ID ' + waip_id + ' gespeichert.'); // nach dem Speichern anhand der waip_id die beteiligten Wachennummern zum Einsatz ermitteln sql.db_einsatz_get_rooms(waip_id, function (socket_rooms) { // socket_rooms muss groesser als 1 sein, da sonst nur der Standard-Raum '0' vorhanden ist if (socket_rooms.length > 1) { socket_rooms.forEach(function (rooms) { // fuer jede Wache(rooms.room) die verbundenen Sockets(Clients) ermitteln und den Einsatz verteilen var room_sockets = io.nsps['/waip'].adapter.rooms[rooms.room]; if (typeof room_sockets !== 'undefined') { Object.keys(room_sockets.sockets).forEach(function (socket_id) { var socket = io.of('/waip').connected[socket_id]; waip_verteilen(waip_id, socket, rooms.room); sql.db_log('WAIP', 'Einsatz ' + waip_id + ' wird an ' + socket.id + ' (' + rooms.room + ') gesendet'); }); }; }); } else { // wenn kein Raum (keine Wache) ausser '0' zurueckgeliefert wird, dann Einsatz direkt wieder loeschen weil keine Wachen dazu hinterlegt sql.db_log('Fehler-WAIP', 'Fehler: Keine Wache für den Einsatz mit der ID ' + waip_id + ' vorhanden! Einsatz wird gelöscht!'); sql.db_einsatz_loeschen(waip_id); }; }); // pruefen ob für die beteiligten Wachen eine Verteiler-Liste hinterlegt ist, falls ja: Rueckmeldungs-Link senden sql.db_vmtl_get_list(waip_id, function (list) { if (list) { brk.alert_vmtl_list(list, function (result) { if (!result) { sql.db_log('VMTL', 'Link zur Einsatz-Rückmeldung erfolgreich an Vermittler-Liste gesendet. ' + result); } else { sql.db_log('VMTL', 'Fehler beim senden des Links zur Einsatz-Rueckmeldung an die Vermittler-Liste: ' + result); }; }); } else { sql.db_log('VMTL', 'Keine Vermittler-Liste für Wachen im Einsatz ' + waip_id + ' hinterlegt. Rückmeldung wird nicht verteilt.'); }; }); }); }; function waip_verteilen(waip_id, socket, wachen_nr) { // Einsatzdaten für eine Wache aus Datenbank laden und an Client verteilen var user_obj = socket.request.user; sql.db_einsatz_get_by_waipid(waip_id, wachen_nr, user_obj.id, function (einsatzdaten) { if (einsatzdaten) { // Berechtigung des Users ueberpruefen sql.db_user_check_permission(user_obj, waip_id, function (valid) { // Wenn nutzer nicht angemeldet, Daten entfernen if (!valid) { einsatzdaten.objekt = ''; einsatzdaten.besonderheiten = ''; einsatzdaten.strasse = ''; einsatzdaten.wgs84_x = ''; einsatzdaten.wgs84_y = ''; }; // pruefen ob Einsatz bereits genau so beim Client angezeigt wurde (Doppelalarmierung) sql.db_einsatz_check_history(waip_id, einsatzdaten, socket.id, function (result) { if (!result) { // Einsatz an Client senden socket.emit('io.new_waip', einsatzdaten); sql.db_log('WAIP', 'Einsatz ' + waip_id + ' fuer Wache ' + wachen_nr + ' an Socket ' + socket.id + ' gesendet.'); sql.db_client_update_status(socket, waip_id); // Sound erstellen tts_erstellen(app_cfg, socket.id, einsatzdaten, function (tts) { if (tts) { // Sound-Link senden socket.emit('io.playtts', tts); sql.db_log('WAIP', 'ttsfile: ' + tts); }; }); } else { // Log das Einsatz explizit nicht an Client gesendet wurde sql.db_log('WAIP', 'Einsatz ' + waip_id + ' fuer Wache ' + wachen_nr + ' nicht an Socket ' + socket.id + ' gesendet, da bereits angezeigt (Doppelalarmierung).'); }; }); }); } else { // wenn keine Einsatzdaten, dann Standby senden socket.emit('io.standby', null); sql.db_log('WAIP', 'Kein Einsatz für Wache ' + wachen_nr + ' vorhanden, Standby an Socket ' + socket.id + ' gesendet.'); sql.db_client_update_status(socket, null); }; }); }; function rmld_verteilen_by_uuid(waip_uuid, rmld_uuid) { // Einsatz-ID mittels Einsatz-UUID ermitteln, und Rueckmelung an alle relevanten Clients verteilen sql.db_einsatz_get_waipid_by_uuid(waip_uuid, function (waip_id) { // am Einsatz beteilite Socket-Räume ermitteln sql.db_einsatz_get_rooms(waip_id, function (socket_rooms) { if (socket_rooms) { socket_rooms.forEach(function (row) { // fuer jede Wache(row.room) die verbundenen Sockets(Clients) ermitteln und Standby senden var room_sockets = io.nsps['/waip'].adapter.rooms[row.room]; if (typeof room_sockets !== 'undefined') { Object.keys(room_sockets.sockets).forEach(function (socket_id) { // wenn Raum zum Einsatz aufgerufen ist, dann Rueckmeldung aus DB laden und an diesen versenden sql.db_rmld_get_by_rmlduuid(rmld_uuid, function (rmld_obj) { if (rmld_obj) { // Rückmeldung an Clients/Räume senden, wenn richtiger Einsatz angezeigt wird sql.db_client_check_waip_id(socket_id, waip_id, function (same_id) { if (same_id) { var socket = io.of('/waip').connected[socket_id]; socket.emit('io.new_rmld', rmld_obj); sql.db_log('RMLD', 'Rückmeldung ' + rmld_uuid + ' für den Einsatz mit der ID ' + waip_id + ' an Wache ' + row.room + ' gesendet.'); sql.db_log('DEBUG', 'Rückmeldung JSON: ' + JSON.stringify(rmld_obj)); }; }); }; }); }); }; }); }; }); }); }; function rmld_verteilen_for_one_client(waip_id, socket, wachen_id) { // Rueckmeldung an einen bestimmten Client senden if (typeof socket.id !== 'undefined') { sql.db_rmld_get_fuer_wache(waip_id, wachen_id, function (rmld_obj) { if (rmld_obj) { // Rueckmeldung nur an den einen Socket senden socket.emit('io.new_rmld', rmld_obj); sql.db_log('RMLD', 'Vorhandene Rückmeldungen an Socket ' + socket.id + ' gesendet.'); sql.db_log('DEBUG', 'Rückmeldung JSON: ' + JSON.stringify(rmld_obj)); } else { sql.db_log('RMLD', 'Keine Rückmeldungen für Einsatz-ID' + waip_id + ' und Wachen-ID ' + wachen_id + ' vorhanden.'); }; }); }; }; function dbrd_verteilen(dbrd_uuid, socket) { // Einsatzdaten an Dashboard senden sql.db_einsatz_get_by_uuid(dbrd_uuid, function (einsatzdaten) { if (einsatzdaten) { sql.db_user_check_permission(socket.request.user, einsatzdaten.id, function (valid) { if (!valid) { delete einsatzdaten.objekt; delete einsatzdaten.besonderheiten; delete einsatzdaten.strasse; delete einsatzdaten.wgs84_x; delete einsatzdaten.wgs84_y; }; socket.emit('io.Einsatz', einsatzdaten); sql.db_log('DBRD', 'Einsatzdaten für Dashboard ' + dbrd_uuid + ' an Socket ' + socket.id + ' gesendet'); sql.db_client_update_status(socket, dbrd_uuid); }); } else { // Standby senden socket.emit('io.standby', null); sql.db_log('DBRD', 'Der angefragte Einsatz ' + dbrd_uuid + ' ist nicht - oder nicht mehr - vorhanden!, Standby an Socket ' + socket.id + ' gesendet.'); sql.db_client_update_status(socket, null); }; }); }; // TODO WAIP: Funktion um Clients remote "neuzustarten" (Seite neu laden), niedrige Prioritaet function tts_erstellen(app_cfg, socket_id, einsatzdaten, callback) { // unnoetige Zeichen aus socket_id entfernen, um diese als Dateinamen zu verwenden var id = socket_id.replace(/\W/g, ''); // Pfade der Sound-Dateien defeinieren var wav_tts = process.cwd() + app_cfg.global.soundpath + id + '.wav'; var mp3_tmp = process.cwd() + app_cfg.global.soundpath + id + '_tmp.mp3'; var mp3_tts = process.cwd() + app_cfg.global.soundpath + id + '.mp3'; var mp3_url = app_cfg.global.mediapath + id + '.mp3'; // unterscheiden des Alarmgongs nach Einsatzart if (einsatzdaten.einsatzart == "Brandeinsatz" || einsatzdaten.einsatzart == "Hilfeleistungseinsatz") { var mp3_bell = process.cwd() + app_cfg.global.soundpath + 'bell_long.mp3'; } else { var mp3_bell = process.cwd() + app_cfg.global.soundpath + 'bell_short.mp3'; }; // Zusammensetzen der Sprachansage async.map(JSON.parse(einsatzdaten.em_alarmiert), sql.db_tts_einsatzmittel, function (err, einsatzmittel) { // Grunddaten var tts_text = einsatzdaten.einsatzart + ', ' + einsatzdaten.stichwort; if (einsatzdaten.objekt) { var tts_text = tts_text + '. ' + einsatzdaten.objekt + ', ' + einsatzdaten.ort + ', ' + einsatzdaten.ortsteil; } else { var tts_text = tts_text + '. ' + einsatzdaten.ort + ', ' + einsatzdaten.ortsteil; }; // Einsatzmittel tts_text = tts_text + '. Für ' + einsatzmittel.join(", "); // Unterscheidung nach Sondersignal if (einsatzdaten.sondersignal == 1) { tts_text = tts_text + ', mit Sondersignal'; } else { tts_text = tts_text + ', ohne Sonderrechte'; }; // Abschluss tts_text = tts_text + '. Ende der Durchsage!'; // ungewollte zeichen aus Sprachansage entfernen tts_text = tts_text.replace(/:/g, " "); tts_text = tts_text.replace(/\//g, " "); tts_text = tts_text.replace(/-/g, " "); // Sprachansage als mp3 erstellen switch (process.platform) { // Windows case 'win32': // Powershell var proc = require('child_process'); var commands = [ // TTS-Schnittstelle von Windows ansprechen 'Add-Type -AssemblyName System.speech;' + '$speak = New-Object System.Speech.Synthesis.SpeechSynthesizer;' + // Ausgabedatei und Sprachtext '$speak.SetOutputToWaveFile(\"' + wav_tts + '\");' + '$speak.Speak(\"' + tts_text + '\");' + '$speak.Dispose();' + // speak.wav in mp3 umwandeln 'ffmpeg -nostats -hide_banner -loglevel 0 -y -i ' + wav_tts + ' -vn -ar 44100 -ac 2 -ab 128k -f mp3 ' + mp3_tmp + ';' + // Gong und Ansage zu einer mp3 zusammensetzen 'ffmpeg -nostats -hide_banner -loglevel 0 -y -i \"concat:' + mp3_bell + '|' + mp3_tmp + '\" -acodec copy ' + mp3_tts + ';' + 'rm ' + wav_tts + ';' + 'rm ' + mp3_tmp + ';' ]; var options = { shell: true }; var childD = proc.spawn('powershell', commands); childD.stdin.setEncoding('ascii'); childD.stderr.setEncoding('ascii'); childD.stderr.on('data', function (data) { sql.db_log('Fehler-TTS', data); callback && callback(null); }); childD.on('exit', function () { callback && callback(mp3_url); }); childD.stdin.end(); break; // LINUX case 'linux': // bash var proc = require('child_process'); var commands = [ // TTS-Schnittstelle SVOX PicoTTS '-c', ` pico2wave --lang=de-DE --wave=` + wav_tts + ` \"` + tts_text + `\" ffmpeg -nostats -hide_banner -loglevel 0 -y -i ` + wav_tts + ` -vn -ar 44100 -ac 2 -ab 128k -f mp3 ` + mp3_tmp + ` ffmpeg -nostats -hide_banner -loglevel 0 -y -i \"concat:` + mp3_bell + `|` + mp3_tmp + `\" -acodec copy ` + mp3_tts + ` rm ` + wav_tts + ` rm ` + mp3_tmp ]; var options = { shell: true }; if (app_cfg.global.development) { console.log(commands); }; var childD = proc.spawn('/bin/sh', commands); childD.stdin.setEncoding('ascii'); childD.stderr.setEncoding('ascii'); childD.on('exit', function (code, signal) { if (code > 0) { sql.db_log('Fehler-TTS', 'Exit-Code ' + code + '; Fehler beim erstellen der TTS-Datei'); callback && callback(null); } else { callback && callback(mp3_url); }; }); childD.stdin.end(); break; // anderes OS default: sql.db_log('Fehler-TTS', 'TTS für dieses Server-Betriebssystem nicht verfügbar!'); callback && callback(null); }; }); }; setInterval(function () { // (alle 10 Sekunden) sql.db_socket_get_all_to_standby(function (socket_ids) { // alle User-Einstellungen prüfen und ggf. Standby senden if (socket_ids) { socket_ids.forEach(function (row) { var socket = io.of('/waip').connected[row.socket_id]; socket.emit('io.standby', null); socket.emit('io.stopaudio', null); sql.db_log('WAIP', 'Standby an Socket ' + socket.id + ' gesendet'); sql.db_client_update_status(socket, null); }); }; }); sql.db_einsatz_get_old(app_cfg.global.time_to_delete_waip, function (waip) { // nach alten Einsaetzen suchen und diese ggf. loeschen if (waip) { sql.db_log('WAIP', 'Einsatz mit der ID ' + waip.id + ' ist veraltet und kann gelöscht werden.') // beteiligte Wachen zum Einsatz ermitteln sql.db_einsatz_get_rooms(waip.id, function (data) { if (data) { data.forEach(function (row) { // fuer jede Wache (row.room) die verbundenen Sockets(Clients) ermitteln und Standby senden var room_sockets = io.nsps['/waip'].adapter.rooms[row.room]; if (typeof room_sockets !== 'undefined') { Object.keys(room_sockets.sockets).forEach(function (socket_id) { // Standby senden var socket = io.of('/waip').connected[socket_id]; sql.db_client_check_waip_id(socket.id, waip.id, function (same_id) { if (same_id) { socket.emit('io.standby', null); socket.emit('io.stopaudio', null); sql.db_log('WAIP', 'Standby an Socket ' + socket.id + ' gesendet'); sql.db_client_update_status(socket, null); }; }); }); }; }); }; sql.db_socket_get_by_room(waip.uuid, function (socket_ids) { // Dashboards trennen, deren Einsatz geloescht wurde // TODO TEST: Dashboard-Trennen-Funktion testen if (socket_ids) { socket_ids.forEach(function (row) { var socket = io.of('/dbrd').connected[row.socket_id]; socket.emit('io.deleted', null); sql.db_log('DBRD', 'Dashboard mit dem Socket ' + socket.id + ' getrennt, da Einsatz gelöscht.'); sql.db_client_update_status(socket, null); }); }; }); sql.db_rmld_get_for_export(waip.einsatznummer, waip.uuid, function (full_rmld) { // beteiligte Wachen aus den Einsatz-Rueckmeldungen filtern var arry_wachen = full_rmld.map(a => a.wache_nr); sql.db_export_get_for_rmld(arry_wachen, function (export_data) { // SQL gibt ist eine Schliefe (db.each), fuer jedes Ergebnis wird eine CSV/Mail erstellt if (export_data) { // je Export eine CSV erstellen, die nur die gewuenschten Rueckmeldungen enthaelt var part_rmld = full_rmld.filter(obj => String(obj.wache_nr).startsWith(String(export_data.export_filter))); // CSV-Spalten definieren var csv_col = ['id', 'einsatznummer', 'waip_uuid', 'rmld_uuid', 'alias', 'einsatzkraft', 'maschinist', 'fuehrungskraft', 'agt', 'set_time', 'arrival_time', 'wache_id', 'wache_nr', 'wache_name']; var opts = { csv_col }; try { var csv = parse(part_rmld, opts); // CSV Dateiname und Pfad festlegen var csv_filename = export_data.export_name.replace(/[|&;$%@"<>()+,]/g, ''); csv_filename = csv_filename.replace(/ /g, "_"); csv_filename = 'einsatz_' + part_rmld[0].einsatznummer + '_export_' + csv_filename + '.csv'; csv_path = process.cwd() + app_cfg.rmld.backup_path; // CSV in Backup-Ordner speichern, falls aktiviert if (app_cfg.rmld.backup_to_file) { // Ordner erstellen fs.mkdir(csv_path, { recursive: true }, function (err) { if (err) { sql.db_log('EXPORT', 'Fehler beim Erstellen des Backup-Ordners: ' + err); }; // CSV speichern fs.writeFile(csv_path + csv_filename, csv, function (err) { if (err) { sql.db_log('EXPORT', 'Fehler beim speichern der Export-CSV: ' + err); }; }); }); }; // CSV per Mail versenden, falls aktiviert if (app_cfg.rmld.backup_to_mail) { // pruefen ob Mail plausibel ist var validmail = /\S+@\S+\.\S+/; if (validmail.test(export_data.export_recipient)) { // Mail-Server var transport = nodemailer.createTransport({ host: app_cfg.rmld.mailserver_host, port: app_cfg.rmld.mailserver_port, secure: app_cfg.rmld.secure_mail, auth: { user: app_cfg.rmld.mail_user, pass: app_cfg.rmld.mail_pass } }); var mail_message = { from: 'Wachalarm-IP-Web <' + app_cfg.rmld.mail_from + '>', to: export_data.export_recipient, subject: 'Automatischer Export Wachalarm-IP-Web - ' + export_data.export_name + ' - Einsatz ' + part_rmld[0].einsatznummer, html: 'Hallo,

anbei der automatische Export aller Einsatz-Rückmeldungen für den Einsatz ' + part_rmld[0].einsatznummer + '

Mit freundlichen Grüßen

' + app_cfg.public.company + '
', attachments: [{ filename: csv_filename, content: csv }] }; transport.sendMail(mail_message, function (err, info) { if (err) { sql.db_log('EXPORT', 'Fehler beim senden der Export-Mail an ' + export_data.export_recipient + ': ' + err); } else { sql.db_log('EXPORT', 'Mail an ' + export_data.export_recipient + ' gesendet: ' + JSON.stringify(info)); } }); } else { sql.db_log('EXPORT', 'Fehler beim versenden der Export-Mail an ' + export_data.export_recipient + ' - keine richtige Mail-Adresse!'); }; }; } catch (err) { sql.db_log('EXPORT', 'Fehler beim erstellen der Export-CSV: ' + err); }; }; }); // alte Rueckmeldungen loeschen sql.db_rmld_loeschen(waip.uuid); }); // alten Einsatz loeschen sql.db_einsatz_loeschen(waip.id); sql.db_log('WAIP', 'Einsatz-Daten zu Einsatz ' + waip.id + ' gelöscht.'); }); }; }); // loeschen alter Sounddaten nach alter (15min) und socket-id (nicht mehr verbunden) fs.readdirSync(process.cwd() + app_cfg.global.soundpath).forEach(file => { // nur die mp3s von alten clients loeschen if (file.substring(0, 4) != 'bell' && file.substring(file.length - 3) == 'mp3' && file.substring(file.length - 8) != '_tmp.mp3') { // Socket-ID aus Datei-Namen extrahieren socket_name = file.substring(0, file.length - 4); // Socket-ID anpassen, damit die SQL-Abfrage ein Ergebnis liefert socket_name.replace('waip', '/waip#'); sql.db_socket_get_by_id(socket_name, function (data) { if (!data) { fs.unlink(process.cwd() + app_cfg.global.soundpath + file, function (err) { if (err) return sql.db_log('Fehler-WAIP', err); sql.db_log('WAIP', 'Veraltete Sound-Datei ' + file + ' wurde gelöscht.'); }); }; }); }; }); }, 10000); return { waip_speichern: waip_speichern, waip_verteilen: waip_verteilen, rmld_verteilen_for_one_client: rmld_verteilen_for_one_client, rmld_verteilen_by_uuid: rmld_verteilen_by_uuid, dbrd_verteilen: dbrd_verteilen }; };