var fs = require("fs"); const { CredentialManager } = require('@strychnine-labs/windows-credential-manager'); const credManager = new CredentialManager(); // Only reading existing stored credentials const cred = credManager.getCredential('opc-connect'); const API_AUTH_USERNAME = cred.username; const API_AUTH_PASSWORD = cred.password.replace("opc-connect", ''); var exec = require("child_process").exec; const { config } = require("process"); const propertiesReader = require("properties-reader"); const properties = propertiesReader(__dirname + "\\config.properties"); let restart_time = Number(properties.get("RESTART_TIME")); var responseTime = 0; let network_status = 1; let restart = 0; let mq = null; if (isNaN(restart_time) || restart_time === 0) { restart_time = 1; } let global_header = null; console.info(properties.get("MQTT_URL")); console.info(__dirname + "\config.properties"); function statusCodesToCause(code) { const statusCodes = { 0: "Connecting", 1: "Success", 2: "Unable to upload to database", 3: "Program Stopped", 4: "Data Stopped Receiving, Restarting Client in sometime" }; return statusCodes[code.toString()]; } // --- Place this function outside your controller for reusability --- function checkNetworkSpeed($http, properties) { var epochTime1 = Date.now(); var CONFIG_URL_PREFIX = properties.get("CONFIG_URL_PREFIX"); var userApiUrl = `${CONFIG_URL_PREFIX}/ingestconfigs?q=${epochTime1}`; var startTime = Date.now(); $http .get(userApiUrl, { responseType: "arraybuffer" }) .then(function (response) { var endTime = Date.now(); var requestTime = endTime - startTime; var downloadSpeedMbps = (response.data.byteLength * 8) / (requestTime / 1000) / 1024 / 1024; var networkSpeedElement = document.getElementById("network-speed-mbps"); if (networkSpeedElement) { networkSpeedElement.style.color = downloadSpeedMbps < 2 ? "red" : "green"; networkSpeedElement.textContent = downloadSpeedMbps.toFixed(2) + " Mbps"; } }) .catch(function (error) { console.error("Error making request:", error); var networkSpeedElement = document.getElementById("network-speed-mbps"); if (networkSpeedElement) { networkSpeedElement.textContent = "Speed test failed"; networkSpeedElement.style.color = "red"; } }); } function validateStatusOfAllConfigs(arr) { return arr.every((val, i, arr) => val === 1); } function isStaleTimestamp(ts, configId) { console.info("time: ", restart_time); var tmp = (+new Date() - new Date(ts)) / 60000; console.info(configId, ts, tmp); return tmp > restart_time ? true : false; } function csvJSON(csv) { var lines = csv.split("\n"); var result = []; var headers = lines[0].split(","); for (var i = 1; i < lines.length; i++) { var obj = {}; var currentline = lines[i].split(","); for (var j = 0; j < headers.length; j++) { obj[headers[j].replace("\r", "")] = currentline[j].replace("\r", ""); } result.push(obj); } return JSON.stringify(result); } function hasTagsColumnAndNonEmpty(tags) { return tags.filter(function (tag) { if (tag["tags"]) { tag["dataTagId"] = tag["tags"]; delete tag["tags"]; return tag; } }); } // Function to check the health endpoint and update global_header async function updateAuthToken() { try { const healthResponse = await fetch(properties.get("CONFIG_URL_PREFIX").replace("/exactapi", "") + "/device/health"); if (healthResponse.status === 200) { console.log(`[${new Date().toISOString()}] Health check successful, using device ID as authToken`); // $scope.displayForm(); let clientId = localStorage.getItem("clientId") console.log("clientId update", clientId); const myHeaders = new Headers(); myHeaders.append("Authorization", clientId); const requestOptions = { method: "POST", headers: myHeaders, redirect: "follow" }; const registerResponse = await fetch(properties.get("CONFIG_URL_PREFIX").replace("/exactapi", "") + "/device/register", requestOptions); const data = await registerResponse.json(); global_header = data.id; // Use the ID from device registration as the auth token return global_header; } else { console.log(`[${new Date().toISOString()}] Health check failed, using login endpoint for authToken`); // Fallback to using the login endpoint await getAuthToken(); } } catch (error) { console.error("Error updating auth token:", error); } } // Function to get auth token from login endpoint async function getAuthToken() { const authHeaders = new Headers(); authHeaders.append("Content-Type", "application/json"); const authRaw = JSON.stringify({ email: API_AUTH_USERNAME, password: API_AUTH_PASSWORD }); const authRequestOptions = { method: "POST", headers: authHeaders, body: authRaw, redirect: "follow" }; try { const response = await fetch(`${properties.get("CONFIG_URL_PREFIX")}/Users/login`, authRequestOptions); const result = await response.json(); // console.log("result.id", result.id); global_header = result.id; // Update global header with login token return global_header; } catch (error) { console.error("Failed to retrieve global header from login:", error); throw new Error("Failed to retrieve global header"); } } // Function to check /opc-network and update header if 401 async function checkAndUpdateHeader() { console.log(`checkAndUpdateHeader called at ${new Date().toLocaleTimeString()}`); const myHeaders = new Headers(); if (global_header) { myHeaders.append("Authorization", `Bearer ${global_header}`); } const requestOptions = { method: "GET", headers: myHeaders, redirect: "follow" }; try { const response = await fetch(properties.get("CONFIG_URL_PREFIX").replace("/exactapi", "") + "/opc-network", requestOptions); console.log("opc-network status", response.status); if (response.status === 401) { console.warn("Received 401, updating global_header..."); await updateAuthToken(); // Update the global header if 401 } else { const result = await response.text(); console.log(result); } } catch (error) { console.error("Error in checkAndUpdateHeader:", error); } } // Initialize the auth token on startup updateAuthToken(); // Call the function to check and update header every 60 seconds setInterval(checkAndUpdateHeader, 60000); let startFlag = true; let restart_interval = Number(properties.get("RESTART_INTERVAL")); if (isNaN(restart_interval) || restart_interval === 0) { restart_interval = 1; } let max_attempts = Number(properties.get("MAX_ATTEMPTS")); if (isNaN(max_attempts) || max_attempts === 0) { max_attempts = 5; } let backoff_interval = Number(properties.get("BACKOFF_INTERVAL")); if (isNaN(backoff_interval) || backoff_interval === 0) { backoff_interval = 0.01; } let START_INTERVAL = Number(properties.get("START_INTERVAL")); if (isNaN(START_INTERVAL) || START_INTERVAL === 0) { START_INTERVAL = 1000; } var attempts = 1; var inBackoffPeriod = false; var backoffEndTime = 0; function manageRestart($http, $scope, configId, index, form) { var clientId = localStorage.getItem("clientId") || $scope.clientId; var currentTime = Date.now(); var lastRestartTime = parseInt(localStorage.getItem(configId + "_last_restart_time")) || 0; // $scope.forms.forEach(function (form) { // if (form.id === configId) { // var unitsId = form.unitsId; // var divElement = document.getElementById(`${configId}+unit`); // if (unitsId) { // divElement.textContent = "unitId: " + unitsId; // divElement.style.color = "black"; // } else { // divElement.textContent = "Please link this to unitsId from pulse"; // divElement.style.color = "red"; // } // } // }); // $scope.forms.forEach(function (form) { // if (form.id === configId) { // var divElement = document.getElementById(`${configId}+config`); // divElement.textContent = "configId: " + configId; // divElement.style.color = "black"; // } // }); if (inBackoffPeriod && currentTime < backoffEndTime) { console.info("Backoff period time"); return; } else if (inBackoffPeriod && currentTime >= backoffEndTime) { console.info("Backoff period over, resetting attempts"); attempts = 1; inBackoffPeriod = false; } if (attempts > max_attempts && !inBackoffPeriod) { console.info( "Maximum restart attempts reached. Backing off for " + backoff_interval + " minutes." ); inBackoffPeriod = true; backoffEndTime = currentTime + backoff_interval * 60 * 1000; return; } if (currentTime - lastRestartTime < restart_interval * 60 * 1000) { console.info("Restart interval not elapsed yet."); return; } $http .get( properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + configId + "/statuses" ) .then(function (res) { if (res.status == 200) { console.info( "configId", configId, "requiredRestart", res.data["requireRestart"], "status", res.data["status"], "url", properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + configId + "/statuses" ); // status check if (res.data["status"] === 1) { network_status = 1; } else network_status = 0; // requireRestart check if (res.data["requireRestart"]) { restart = 1; console.info("Required Restart Detected"); $http .post( properties.get("CONFIG_URL_PREFIX") + '/statuses/update?where={"ingestconfigId":"' + configId + '"}', (json = { requireRestart: false }) ) .then(function (response) { if (response.status == 200) { console.info("Required Restart Requested"); $scope.restart(form); console.info("Restarted-Attempts:", attempts); localStorage.setItem( configId + "_last_restart_time", Date.now() ); attempts++; } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); } else { $http .post( properties.get("CONFIG_URL_PREFIX") + '/statuses/update?where={"ingestconfigId":"' + configId + '"}', (json = { requireRestart: false }) ) .then(function (response) { if (response.status == 200) { console.info("required restart key updated"); } }) .catch(function (response) { console.error(response); }); } if (res.data["stopAll"]) { console.info("stopAll Detected"); $http .post( properties.get("CONFIG_URL_PREFIX") + '/statuses/update?where={"ingestconfigId":"' + configId + '"}', (json = { stopAll: false, opcConnect: true }) ) .then(function (response) { if (response.status == 200) { console.info("stopAll Requested"); $scope.stopAll($scope.forms); } else { console.warn( "stopAll failed with response code", response.status ); } }) .catch(function (response) { console.error("stopAll failed with error", response); }); } else { $http .post( properties.get("CONFIG_URL_PREFIX") + '/statuses/update?where={"ingestconfigId":"' + configId + '"}', (json = { stopAll: false, opcConnect: true }) ) .then(function (response) { // console.log("log...",response); if (response.status == 200) { console.info("stopAll key updated"); } }) .catch(function (response) { console.error(response); }); } if (res.data["startAll"]) { console.info("startAll Detected"); $http .post( properties.get("CONFIG_URL_PREFIX") + '/statuses/update?where={"ingestconfigId":"' + configId + '"}', (json = { startAll: false }) ) .then(function (response) { if (response.status == 200) { console.info("startAll Requested"); $scope.startAll($scope.forms); } else { console.warn( "startAll failed with response code", response.status ); } }) .catch(function (response) { console.error("startAll failed with error", response); }); } else { $http .post( properties.get("CONFIG_URL_PREFIX") + '/statuses/update?where={"ingestconfigId":"' + configId + '"}', (json = { startAll: false }) ) .then(function (response) { if (response.status == 200) { console.info("startAll key updated"); } }) .catch(function (response) { console.error(response); }); } if (res.data["time"] && isStaleTimestamp(res.data["time"], configId)) { console.info("Stale condition detected"); if (attempts <= max_attempts) { console.info("Restarting"); $scope.restart(form); console.info("Restarted- Attempts:", attempts); localStorage.setItem(configId + "_last_restart_time", Date.now()); attempts++; } else { console.warn( "Maximum restart attempts reached. Backing off for " + backoff_interval + " minutes." ); inBackoffPeriod = true; backoffEndTime = currentTime + backoff_interval * 60 * 1000; } } else { restart = 0; console.warn("No restart required."); } var d = new Date(); var epochTime = d.getTime(); console.info( clientId + "/" + configId + "/" + configId + "_network_status" ); console.info(clientId + "/" + configId + "/" + configId + "_restart"); function safePublish(topic, message, options = {}) { if (typeof mq !== 'undefined' && mq && mq.connected) { mq.publish(topic, message, options); } else { console.warn(`⚠️ MQTT not connected. Cannot publish to topic: ${topic}`); } } safePublish( `${clientId}/${configId}/${configId}_network_status`, JSON.stringify({ t: epochTime, v: network_status }) ); safePublish( `${clientId}/${configId}/${configId}_restart`, JSON.stringify({ t: epochTime, v: restart }) ); $scope.forms[index]["status"] = statusCodesToCause(res.data["status"]); $scope.forms[index].status_class = res.data["status"] == 1 ? "success" : "error"; } }) .catch(function (e2) { console.error(e2); }); } angular .module("desktopApp", []) .factory('authInterceptor', function () { return { request: function (config) { config.headers = config.headers || {}; config.headers.Authorization = `Bearer ${global_header}`; return config; } }; }) .config(function ($httpProvider) { $httpProvider.interceptors.push('authInterceptor'); }) .controller( "indexController", function ($scope, $sce, $http, $interval, $window) { $scope.network = false; $scope.registration = true; $scope.config = false; $scope.dataTagIds = []; $scope.TagsRender = {}; $scope.TsRender = {}; $http.defaults.headers.common["Authorization"] = $scope.global_header; $scope.onMessageCallback = function (topic, msg) { var dataTagId = topic.split("/")[2]; let d = new Date(JSON.parse(msg.toString())["t"]); $scope.TagsRender[dataTagId] = JSON.parse(msg.toString())["v"]; $scope.TsRender[dataTagId] = d.toLocaleString("en-US"); }; let reconnecting = false; // 🌟 Connect to MQTT Broker const connectMQTT = async () => { if (!global_header) { console.error("MQTT authorization token is missing."); return; } // Disconnect previous connection if exists if (mq && mq.connected) { try { mq.end(true); // Clean disconnect } catch (e) { console.warn("Error closing previous MQTT connection:", e); } } const mqttUrl = `${properties.get("MQTT_URL")}?access_token=${global_header}`; const options = { keepalive: 60, // ✅ Broker friendly interval clean: true, protocol: 'ws', reconnectPeriod: 5000 // ✅ Automatic reconnect every 5s if disconnected }; mq = mqtt.connect(mqttUrl, options); mq.on("message", $scope.onMessageCallback); mq.on("connect", () => { reconnecting = false; console.log(`[${new Date().toISOString()}] ✅ MQTT Connection established`); }); mq.on("error", async (err) => { console.error(`[${new Date().toISOString()}] ❌ MQTT Connection error:`, err); if (!reconnecting && err.message && err.message.includes("Authentication failed")) { reconnecting = true; await updateAuthToken(); connectMQTT(); // 🔄 Reconnect with new token } }); mq.on("close", () => { console.warn(`[${new Date().toISOString()}] ⚠️ MQTT Connection closed`); }); mq.on("disconnect", () => { console.warn(`[${new Date().toISOString()}] ⚠️ MQTT Connection disconnected`); }); mq.on("reconnect", () => { console.warn(`[${new Date().toISOString()}] 🔄 MQTT Connection reconnecting`); }); }; // 🌟 Update Token + Connect async function updateAuthTokenAndConnectMQTT() { await updateAuthToken(); // ✅ This should set global_header if (global_header) { connectMQTT(); } else { console.error("Failed to retrieve global header for MQTT connection."); } } // 🌟 One week (or slightly earlier) dynamic token refresh function scheduleWeeklyTokenRefresh() { const SAFETY_WEEK_MS = (7 * 24 * 60 * 60 * 1000) - (2 * 60 * 60 * 1000); // Refresh after 6d 22h setTimeout(async () => { await updateAuthToken(); console.log(`[${new Date().toISOString()}] 🔑 MQTT token refreshed after 1 week`); scheduleWeeklyTokenRefresh(); // 🔄 Schedule next refresh }, SAFETY_WEEK_MS); } // 🌟 Start everything updateAuthTokenAndConnectMQTT(); scheduleWeeklyTokenRefresh(); function safePublish(topic, message, options = {}) { if (mq && mq.connected) { mq.publish(topic, message, options); } else { console.warn(`⚠️ MQTT not connected. Can't publish to ${topic}`); } } $scope.start = function (form) { start(form); localStorage.setItem(form.id, true); }; $scope.startAll = function (forms) { console.info("Calling startAll"); forms.forEach(function (form, index) { setTimeout(function () { start(form); localStorage.setItem(form.id, true); }, index * START_INTERVAL); // Delay of 5 seconds * index }); }; $scope.stop = function (form) { stop(form); localStorage.removeItem(form.id); $http .post( properties.get("CONFIG_URL_PREFIX") + '/statuses/update?where={"ingestconfigId":"' + form.id + '"}', (json = { status: 3 }) ) .then(function (response) { if (response.status == 200) { console.info("status went to stop"); if (response.data.count == 0 || response.status != 200) { console.info("Not found configs, in post statuses update"); $http .post( properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + form.id + "/statuses", (json = { status: 0 }) ) .then(function (response) { if (response.status == 200) { console.info("created status"); } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); } } else { console.warn(response.status); } }) .catch(function (e2) { console.error(e2); }); }; $scope.restart = function (form) { console.info("restart configId:", form); console.info("Calling stop"); stop(form); localStorage.removeItem(form.id); $http .post( properties.get("CONFIG_URL_PREFIX") + '/statuses/update?where={"ingestconfigId":"' + form.id + '"}', (json = { status: 3 }) ) .then(function (response) { if (response.status == 200) { console.info("updated status for stop"); console.info("created status"); $scope.start(form); } else if (response.data.count == 0 || response.status != 200) { console.info("Not found configs, in post statuses update"); $http .post( properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + form.id + "/statuses", (json = { status: 0 }) ) .then(function (response) { if (response.status == 200) { console.info(response.status); } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); } else { console.warn(response.status); } }) .catch(function (e2) { console.error(e2); }); }; $scope.stopAll = function (forms) { console.info("stopping all"); var clientId = localStorage.getItem("clientId") || $scope.clientId; $http .get( properties.get("CONFIG_URL_PREFIX") + "/clients/" + clientId + "/ingestconfigs" ) .then(function (response) { if (response.status == 200) { console.info("got all configs to delete!"); for (var i = 0; i < response.data.length; i++) { localStorage.removeItem(response.data[i]["id"]); } stopAll(forms); } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); }; $scope.addCSV = function (form) { if (document.getElementById("file").files.length == 0) { console.info("no files"); return; } console.info("fileame"); var f = document.getElementById("file").files[0], r = new FileReader(); console.info(f.name); if (!f.name.endsWith(".csv")) { alert(f.name + " is not a CSV file"); return; } r.onloadend = function (e) { var data = e.target.result; try { var taglist = JSON.parse(csvJSON(data)); taglist = hasTagsColumnAndNonEmpty(taglist); if (taglist.length > 0) { $http .post( properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + form.id + "/tags", (json = taglist) ) .then(function (response) { if (response.status == 200) { console.info("saved!"); form.tags.concat(taglist); } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); } else { alert( "CSV must contain `tags` column and value should not be empty" ); } } catch (err) { console.error("Internal error: CSV Parsing"); console.error(err); } }; r.readAsBinaryString(f); }; $scope.lget = function (arg) { return localStorage.getItem(arg); }; $scope.test = function (form) { console.info(form); test(form); }; $scope.loadTags = function (form) { $scope.tagSearchQuery = ""; var clientId = localStorage.getItem("clientId") || $scope.clientId; const local_config = form; console.info(clientId + "/" + local_config["id"]); $http .get( properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + local_config["id"] + "/tags?ts=" + (+new Date()).toString() ) .then(function (res) { if (res.status == 200) { form.tags = res.data; var prefix = form.TAG_PREFIX; form.tags.forEach((tag_el) => { mq.subscribe( clientId + "/" + local_config["id"] + "/" + prefix + tag_el.dataTagId ); }); } }); }; $scope.filteredTags = function (form, query) { if (!form || !Array.isArray(form.tags)) { return []; } var filteredTags = []; if (!query) { form.tags.forEach(function (tag) { tag.highlighted = $sce.trustAsHtml(tag.dataTagId); filteredTags.push(tag); }); } else { var regex = new RegExp("(" + preg_quote(query) + ")", "gi"); form.tags.forEach(function (tag) { if (tag.dataTagId.toLowerCase().includes(query.toLowerCase())) { var highlighted = tag.dataTagId.replace( regex, '$1' ); tag.highlighted = $sce.trustAsHtml(highlighted); filteredTags.push(tag); } }); } return filteredTags; }; function preg_quote(str) { return (str + "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } $scope.loadConfig = function ($http) { var clientId = localStorage.getItem("clientId") || $scope.clientId; $http .get( properties.get("CONFIG_URL_PREFIX") + "/clients/" + clientId + "/ingestconfigs" ) .then(function (response) { if (response.status == 200) { if (!angular.equals($scope.forms, response.data)) { var urlprefix = properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/"; // var urlsuffix = "/statuses?ts=" + (+new Date()).toString(); var urlsuffix = "/statuses"; Promise.all( response.data.map((u) => $http.get(urlprefix + u["id"] + urlsuffix) ) ) .then((responses) => Promise.all(responses.map((res) => res.data)) ) .then((texts) => { var statuses = texts.map((x) => x["status"]); if (statuses.length == 0) { $scope.config_color = "red"; } else if (validateStatusOfAllConfigs(statuses)) { console.info("all valid status"); $scope.config_color = "green"; } else { $scope.config_color = "red"; } }); } if ($scope.forms && $scope.forms.length > 0) { console.log("startFlag", startFlag); if (startFlag) { setTimeout(function () { $scope.stopAll($scope.forms); }, 2000); setTimeout(function () { $scope.startAll($scope.forms); }, 4000); startFlag = false; } for (let index = 0; index < $scope.forms.length; index++) { setTimeout(function () { manageRestart( $http, $scope, $scope.forms[index]["id"], index, $scope.forms[index] ); }, index * START_INTERVAL); } } else { console.warn(response.status); } } }) .catch(function (e) { console.error(e); }); }; $scope.searchInLogs = function (configId, typedWords) { var logsElement = angular.element(document.getElementById(configId)); var logsContent = logsElement.text(); logsContent = logsContent.replace(/Logs:/g, "
Logs:"); if (typedWords) { var escapedTypedWords = typedWords.replace( /[-\/\\^$*+?.()|[\]{}]/g, "\\$&" ); var highlightedLogsContent = logsContent.replace( new RegExp("(" + escapedTypedWords + ")", "gi"), '$1' ); highlightedLogsContent = highlightedLogsContent.replace( /Logs:/g, "Logs:" ); logsElement.html("" + highlightedLogsContent + ""); } else { logsContent = logsContent.replace(/
Logs:/g, "
Logs:"); logsElement.html(logsContent); } }; $scope.copyLogs = function (configId) { var logsContent = angular .element(document.getElementById(configId)) .text(); navigator.clipboard .writeText(logsContent) .then(function () { alert("Logs copied to clipboard!"); }) .catch(function (error) { console.error("Error copying text: ", error); }); }; $scope.copyToClipboard = function (configId) { var dynamicPathElement = angular.element( document.getElementById("dynamic-path_" + configId) ); var textArea = document.createElement("textarea"); textArea.value = dynamicPathElement.text(); document.body.appendChild(textArea); textArea.select(); document.execCommand("Copy"); textArea.remove(); alert("Path copied to clipboard!"); }; $scope.fetchDynamicPath = function (configId) { var username = window.process && window.process.env && window.process.env.USERPROFILE ? window.process.env.USERPROFILE.split("\\")[2] : ""; var path = `C:\\Users\\${username}\\AppData\\Roaming\\node-opcua-default-nodejs\\Config\\PKI\\trusted\\certs`; var dynamicPathElement = document.getElementById( "dynamic-path_" + configId ); dynamicPathElement.textContent = path; dynamicPathElement.style.display = "block"; }; $scope.togglePasswordVisibility = function (configId) { var passwordInput = document.getElementById("password_" + configId); var eyeIcon = document.getElementById("eyeIcon_" + configId); $scope.passwordVisible = !$scope.passwordVisible; if ($scope.passwordVisible) { passwordInput.type = "text"; } else { passwordInput.type = "password"; } }; $scope.toggleCertificatePathVisibility = function (configId) { var checkbox = document.getElementById( "certificates-checkbox_" + configId ); var getCertificateButton = document.getElementById( "get-certificate-button_" + configId ); var copyPathButton = document.getElementById( "copy-path-button_" + configId ); var dynamicPathElement = document.getElementById( "dynamic-path_" + configId ); if (checkbox.checked) { getCertificateButton.style.display = "block"; copyPathButton.style.display = "block"; dynamicPathElement.style.display = "block"; } else { getCertificateButton.style.display = "none"; copyPathButton.style.display = "none"; dynamicPathElement.style.display = "none"; } }; $scope.submitClientRegistration = function () { if ($scope.clientId) { localStorage.setItem("clientId", $scope.clientId); $http .get( properties.get("CONFIG_URL_PREFIX") + "/clients/" + $scope.clientId + "/ingestconfigs" ) .then(function (response) { if (response.status == 200) { $scope.registration_color = "green"; $scope.registration_msg = "Valid Client"; $scope.registration_class = "success"; localStorage.setItem("clientId", $scope.clientId); localStorage.setItem("registration_color", "green"); $scope.displayForm(); } else { $scope.registration_color = "red"; localStorage.setItem("registration_color", "red"); $scope.registration_msg = "Cloud error"; $scope.registration_class = "error"; } }) .catch(function (response) { $scope.registration_color = "red"; localStorage.setItem("registration_color", "red"); $scope.registration_msg = "ClientID doesn't exist on remote machine"; $scope.registration_class = "error"; }); } }; var formObj = { name: "name", OPC_SERVER_USER: "", OPC_SERVER_PASS: "", OPC_SERVER_HOST: "localhost", OPC_SERVER_PROGID: "", OPC_SERVER_NAMESPACE: "", TAG_PREFIX: "", SUBSCRIBE_INTERVAL: 60000, }; $scope.addForm = function () { $scope.forms.push(formObj); }; $scope.addTag = function (form) { if (form.unitsId) { var unitId = form.unitsId; console.info("unitId form:", form.name, unitId); if (form.newTag) { var capturedNewTag = form.newTag; var equipment_url = properties.get("CONFIG_URL_PREFIX") + "/units/" + unitId + "/equipment?filter={%22where%22:{%22name%22:%22Unassigned%22}}"; $http .get(equipment_url) .then(function (response) { var equipmentId; console.log("unitId:", form.unitsId); if (response.status == 200 && response.data.length == 0) { $http .post( properties.get("CONFIG_URL_PREFIX") + "/units/" + form.unitsId + "/equipment", (json = { name: "Unassigned", equipment: "Unassigned" }) ) .then(function (response) { equipmentId = response.data.id; console.info("EquipmentId created: ", equipmentId); }); } else if ( response.status == 200 && response.data.length >= 1 ) { equipmentId = response.data[0].id; console.info("EquipmentId Found: ", equipmentId); } if (equipmentId) { $http .post( properties.get("CONFIG_URL_PREFIX") + "/units/" + unitId + "/tagmeta", (json = { dataTagId: form.TAG_PREFIX + capturedNewTag, description: "Unassigned", equipmentName: "Unassigned", equipmentType: "Unassigned", equipmentId: equipmentId, }) ) .then(function (response) { console.info("meta response: ", response.data); console.info("tag saved! to the meta"); }); } }) .catch(function (response) { console.error(response); form.newTag = ""; }); } } else { $window.alert( "Please assign unitId from pulse and reload OPCConnect by CTRL+R" ); } if (form.newTag) { $http .post( properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + form.id + "/tags", (json = { dataTagId: form.newTag.trim(), ingestconfigId: form.id, address: "", scalefactor: "", }) ) .then(function (response) { if (response.status == 200) { console.info("tag saved!"); console.info(form.newTag); if (form.newTag) { console.info("in inf"); if (form.hasOwnProperty("tags")) { form.tags.push({ dataTagId: form.newTag, ingestconfigId: form.id, }); } else { form.tags = [ { dataTagId: form.newTag, ingestconfigId: form.id }, ]; } form.newTag = ""; } } else { console.error(response.status); form.newTag = ""; } }) .catch(function (response) { console.error(response); form.newTag = ""; }); } }; $scope.saveTag = function (form, index) { if (form.tags[index].id && form.tags[index].dataTagId) { var updateUrl = properties.get("CONFIG_URL_PREFIX") + "/tags/update?where=%7B%22id%22:%22" + form.tags[index].id + "%22%7D"; var dataToUpdate = { dataTagId: form.tags[index].dataTagId.trim(), address: form.tags[index].address.trim(), }; console.info("dataToUpdate:", dataToUpdate); $http .post(updateUrl, dataToUpdate) .then(function (response) { if (response.status === 200) { console.info("Updated data:", response.data); } else { console.warn("Update failed with status:", response.status); } }) .catch(function (error) { console.error("Update error:", error); }); } }; $scope.deleteTag = function (form, index) { if (form.tags[index].dataTagId) { var delete_url = properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + form.tags[index].ingestconfigId + "/tags/" + form.tags[index].id; $http .delete(delete_url) .then(function (response) { if (response.status == 204) { console.info("tag deleted: ", form.tags[index].dataTagId); form.tags.splice(index, 1); } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); } }; $scope.editTag = function (tag) { tag.editMode = true; }; $scope.displayForm = function () { var clientId = localStorage.getItem("clientId") || $scope.clientId; $http .get( properties.get("CONFIG_URL_PREFIX") + "/clients/" + clientId + "/ingestconfigs?ts=" + (+new Date()).toString() ) .then(function (response) { if (response.status == 200) { if (!angular.equals($scope.forms, response.data)) { $scope.forms = response.data; } $scope.loadConfig($http); } else { console.warn(response.status); } }) .catch(function (e) { console.error(e); }); }; $scope.deleteForm = function (index) { console.info(index); console.info("Calling delete"); var configId = $scope.forms[index]["id"]; if (confirm("Do you really want to delete this OPC server?")) { if (!configId) { $scope.forms.splice(index, 1); } else { $http .delete( properties.get("CONFIG_URL_PREFIX") + "/ingestconfigs/" + configId ) .then(function (response) { console.info(response.status); if (response.status == 200) { $scope.forms.splice(index, 1); console.info("deleted!"); } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); } } }; $scope.classConvert = function (x) { if (x == "success") { return "greencircle"; } else { return "redcircle"; } }; $scope.setForm = function (configId, formindex) { var clientId = localStorage.getItem("clientId") || $scope.clientId; var formcopy = angular.copy($scope.forms[formindex]); delete formcopy["id"]; delete formcopy["$$hashKey"]; delete formcopy["status"]; delete formcopy["status_class"]; delete formcopy["config_msg"]; delete formcopy["taglist"]; formcopy["TAG_PREFIX"] = formcopy["TAG_PREFIX"]; var tags = formcopy["tags"]; delete formcopy["tags"]; if (configId) { console.info("update form"); $http .post( properties.get("CONFIG_URL_PREFIX") + '/ingestconfigs/update?where={"id":"' + configId + '"}', (json = formcopy) ) .then(function (response) { if (response.status == 200) { console.info("saved!"); console.info("formcopy", formcopy); fs.writeFileSync( __dirname + "\\" + configId + ".json", JSON.stringify(formcopy, null, 2) ); } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); } else { console.info("in new form"); $http .post( properties.get("CONFIG_URL_PREFIX") + "/clients/" + clientId + "/ingestconfigs", (json = formcopy) ) .then(function (response) { if (response.status == 200) { console.info("saved!"); console.info(response.data); $scope.forms[formindex] = response.data; } else { console.warn(response.status); } }) .catch(function (response) { console.error(response); }); } }; $scope.unsubscribeTags = function (form) { if (form.hasOwnProperty("tags")) { console.info("unsubs"); var tags = form["tags"]; var prefix = form["TAG_PREFIX"]; var configId = form.id; var clientId = localStorage.getItem("clientId") || $scope.clientId; if (prefix && configId && clientId) { tags.forEach((tag_el) => { mq.unsubscribe( clientId + "/" + configId + "/" + prefix + tag_el["dataTagId"] ); }); } else { console.warn("Empty form or tags"); } } }; $scope.registration_color = "red"; $scope.config_color = "red"; $scope.network_color = "red"; $scope.network_class = "error"; $scope.network_msg = "Connecting"; $scope.loadNetwork = function () { var clientId = localStorage.getItem("clientId") || "empty"; const start = performance.now(); $http .get( properties.get("CONFIG_URL_PREFIX").replace("/exactapi", "") + "/opc-network") .then(function (response) { const end = performance.now(); responseTime = (end - start) / 10; console.info( "status:" + response.status + ", response time: " + responseTime.toFixed(1) ); $scope.network_color = "green"; $scope.network_class = "success"; $scope.network_msg = "Connected"; }) .catch(function (e) { const end = performance.now(); responseTime = end - start; if (e.status === 401) { $scope.network_msg = "Authenticating..."; updateAuthToken().then(() => { $http.defaults.headers.common.Authorization = global_header; $scope.network_color = "green"; $scope.network_class = "success"; $scope.network_msg = "Connected"; }) .catch((err) => { if (e.status !== 404) { $scope.network_msg = "Wrong credentials"; } else if (e.status !== 500) { $scope.network_msg = "Internal Server Error"; } else if (e.status !== 502) { $scope.network_msg = "Bad Gateway"; } }); } else { $scope.network_color = "red"; $scope.network_class = "error"; $scope.network_msg = "Unable to contact cloud"; } console.info( "status:" + e.status + ", response time: " + responseTime.toFixed(1) ); }); }; setTimeout(function () { $scope.displayForm(); $scope.loadNetwork(); }, 6000); $scope.loadClientRegistration = function () { var clientId = localStorage.getItem("clientId") || $scope.clientId; $scope.clientId = clientId; $scope.registration_color = localStorage.getItem("registration_color") || "red"; }; $scope.loadClientRegistration(); $scope.displayForm(); $scope.loadNetwork(); $interval(function () { $scope.loadConfig($http); var clientId = localStorage.getItem("clientId") || $scope.clientId; var d1 = new Date(); var epochTime1 = d1.getTime(); $scope.loadNetwork(); console.info(clientId + "/response_time"); // mq.publish( // clientId + "/response_time", // JSON.stringify({ t: epochTime1, v: responseTime.toFixed(1) }) // ); safePublish( `${clientId}/response_time`, JSON.stringify({ t: epochTime1, v: responseTime.toFixed(1) }) ); }, 60000); // --- Place this function outside your controller for reusability --- function checkNetworkSpeed($http, properties) { var epochTime1 = Date.now(); var CONFIG_URL_PREFIX = properties.get("CONFIG_URL_PREFIX"); var userApiUrl = `${CONFIG_URL_PREFIX}/ingestconfigs?q=${epochTime1}`; var startTime = Date.now(); $http .get(userApiUrl, { responseType: "arraybuffer" }) .then(function (response) { var endTime = Date.now(); var requestTime = endTime - startTime; var downloadSpeedMbps = (response.data.byteLength * 8) / (requestTime / 1000) / 1024 / 1024; var networkSpeedElement = document.getElementById("network-speed-mbps"); if (networkSpeedElement) { networkSpeedElement.style.color = downloadSpeedMbps < 2 ? "red" : "green"; networkSpeedElement.textContent = downloadSpeedMbps.toFixed(2) + " Mbps"; } }) .catch(function (error) { console.error("Error making request:", error); var networkSpeedElement = document.getElementById("network-speed-mbps"); if (networkSpeedElement) { networkSpeedElement.textContent = "calculating ..."; networkSpeedElement.style.color = "red"; } }); } // Run speed test immediately on page load for quick UI update checkNetworkSpeed($http, properties); // Then run it every 60 seconds $interval(function () { checkNetworkSpeed($http, properties); }, 60000); $interval(function () { $scope.forms.forEach(function (form) { if (form.clientId === "660cdb4511499e000783f5ff") { return; } var command_to_run = form.OPC_SERVER_DRIVER; if (!command_to_run) { command_to_run = "java -jar OPCClient_NEW.jar"; } var firstword = command_to_run.split(" ")[0]; // console.log(command_to_run); const findPidCommand = `tasklist | find "${firstword}"`; console.log("findPidCommand: ", findPidCommand); exec(findPidCommand, (error, stdout, stderr) => { if (error && error.code !== 1) { console.error(`Error executing command: ${error}`); return; } if (stderr) { console.error(`Error: ${stderr}`); return; } const processes = stdout .split("\n") .filter((line) => line.trim() !== ""); if (processes.length === 0) { console.log(`No processes found with name "${firstword}".`); return; } console.log(`Processes with name containing "${firstword}":`); processes.forEach((process) => console.log(process)); // Create a list of PIDs const pids = processes .map((process) => process.split(/\s+/)[1]) .filter((pid) => pid !== ""); console.log("List of PIDs:", pids); console.log("no of pid in form:", $scope.forms.length); if (pids.length <= 2 * $scope.forms.length) { console.log(`Only ${pids.length} process found. Not killing.`); } else { const pidsToKill = pids.slice($scope.forms.length); const killCommands = pidsToKill.map( (pid) => `taskkill /f /pid ${pid}` ); // Execute each taskkill command separately killCommands.forEach((killCommand, index) => { exec(killCommand, (killError, killStdout, killStderr) => { if (killError) { console.error( `Error killing process with PID ${pidsToKill[index]}: ${killError}` ); return; } if (killStderr) { console.error(`Error: ${killStderr}`); return; } console.log( `Process with PID ${pidsToKill[index]} killed successfully.` ); }); }); } }); }); }, 60000); $interval(function () { const jsonString = fs.readFileSync( "C:\\OPCConnect\\resources\\app\\version.json", "utf8" ); const jsonData = JSON.parse(jsonString); const version = jsonData.version; $http .post( properties.get("CONFIG_URL_PREFIX") + '/clients/update?where={"id":"' + $scope.clientId + '"}', (json = { "opcconnectversion": version }) ) .then(function (response) { if (response.status == 200) { console.info("version update success:", version); } else { console.warn("version status update issue", response.status, version); } }); try { var divElement = document.getElementById("version-value"); divElement.textContent = version; } catch (error) { var divElement = document.getElementById("version-value"); divElement.textContent = "ERROR"; } }, 60000); } );