mirror of
https://github.com/IoTManagerProject/IoTManagerWeb.git
synced 2026-03-26 15:02:21 +03:00
рефакторинг
This commit is contained in:
@@ -49,7 +49,7 @@ export default {
|
||||
module: true,
|
||||
toplevel: true,
|
||||
unsafe_arrows: true,
|
||||
drop_console: true,
|
||||
drop_console: false,
|
||||
drop_debugger: true,
|
||||
},
|
||||
output: { quote_style: 1 },
|
||||
|
||||
1235
src/App.svelte
1235
src/App.svelte
File diff suppressed because it is too large
Load Diff
572
src/WebSocketManager.js
Normal file
572
src/WebSocketManager.js
Normal file
@@ -0,0 +1,572 @@
|
||||
class WebSocketManager {
|
||||
constructor(deviceList, debug = true) {
|
||||
this.deviceList = deviceList;
|
||||
this.debug = debug;
|
||||
this.socket = [];
|
||||
this.selectedWs = 0;
|
||||
this.currentPageName = undefined;
|
||||
this.reconnectTimeout = 60;
|
||||
this.remainingTimeout = this.reconnectTimeout;
|
||||
this.preventReconnect = false;
|
||||
this.waitingAckTimeout = 18000;
|
||||
this.rebootOrUpdateProcess = false;
|
||||
this.showAwaitingCircle = false;
|
||||
this.socketConnected = false;
|
||||
this.ackTimeoutsArr = [];
|
||||
this.startMillis = [];
|
||||
this.ping = [];
|
||||
this.layoutJson = [];
|
||||
this.paramsJson = {};
|
||||
this.itemsJson = {};
|
||||
this.widgetsJson = {};
|
||||
this.configJson = {};
|
||||
this.scenarioTxt = "";
|
||||
this.settingsJson = {};
|
||||
this.ssidJson = {};
|
||||
this.errorsJson = {};
|
||||
this.incDeviceList = [];
|
||||
this.flashProfileJson = {};
|
||||
this.otaJson = {};
|
||||
this.firstDevListRequest = true;
|
||||
this.parsed = this.initializeParsedFlags();
|
||||
this.pageReady = this.initializePageReadyFlags();
|
||||
this.pages = [];
|
||||
}
|
||||
|
||||
initializeParsedFlags() {
|
||||
return {
|
||||
itemsJson: false,
|
||||
widgetsJson: false,
|
||||
configJson: false,
|
||||
settingsJson: false,
|
||||
ssidJson: false,
|
||||
errorsJson: false,
|
||||
incDeviceList: false,
|
||||
flashProfileJson: false,
|
||||
otaJson: false,
|
||||
};
|
||||
}
|
||||
|
||||
initializePageReadyFlags() {
|
||||
return {
|
||||
dash: false,
|
||||
config: false,
|
||||
connection: false,
|
||||
list: false,
|
||||
system: false,
|
||||
dev: false,
|
||||
};
|
||||
}
|
||||
|
||||
connectToAllDevices() {
|
||||
this.deviceList.forEach((device, i) => {
|
||||
device.ws = i;
|
||||
if (!device.status) {
|
||||
this.wsConnect(i);
|
||||
this.wsEventAdd(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async initDevList() {
|
||||
if (this.firstDevListRequest) {
|
||||
this.devListOverride();
|
||||
} else {
|
||||
this.devListCombine();
|
||||
}
|
||||
this.firstDevListRequest = false;
|
||||
this.parsed.deviceListJson = true;
|
||||
this.debug && console.log("[✔]", "deviceList parsed");
|
||||
this.onParsed();
|
||||
this.selectedDeviceDataRefresh();
|
||||
this.connectToAllDevices();
|
||||
}
|
||||
|
||||
wsConnect(ws) {
|
||||
const ip = this.getIP(ws);
|
||||
if (ip === "error") {
|
||||
this.debug && console.log("[e]", "device list wrong");
|
||||
} else {
|
||||
this.socket[ws] = new WebSocket(`ws://${ip}:81`);
|
||||
this.socket[ws].binaryType = "blob";
|
||||
this.debug && console.log("[i]", ip, ws, "started connecting...");
|
||||
}
|
||||
}
|
||||
|
||||
getIP(ws) {
|
||||
const device = this.deviceList.find((device) => device.ws === ws);
|
||||
return device ? device.ip : "error";
|
||||
}
|
||||
|
||||
wsEventAdd(ws) {
|
||||
if (!this.socket[ws]) {
|
||||
this.debug && console.log("[e]", "socket not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
const ip = this.getIP(ws);
|
||||
this.socket[ws].addEventListener("open", () => this.handleOpen(ws, ip));
|
||||
this.socket[ws].addEventListener("message", (event) => this.handleMessage(event, ws));
|
||||
this.socket[ws].addEventListener("close", () => this.handleClose(ws, ip));
|
||||
this.socket[ws].addEventListener("error", () => this.handleError(ws, ip));
|
||||
}
|
||||
|
||||
handleOpen(ws, ip) {
|
||||
this.markDeviceStatus(ws, true);
|
||||
if (this.firstDevListRequest && ws === 0) this.wsSendMsg(ws, "/devlist|");
|
||||
if (this.currentPageName === "/|") {
|
||||
this.wsSendMsg(ws, this.currentPageName);
|
||||
} else if (ws === this.selectedWs) {
|
||||
this.sendCurrentPageNameToSelectedWs();
|
||||
}
|
||||
}
|
||||
|
||||
handleMessage(event, ws) {
|
||||
if (typeof event.data === "string" && event.data === "/tstr|") {
|
||||
this.ack(ws, true);
|
||||
} else if (event.data instanceof Blob) {
|
||||
if (ws === this.selectedWs) {
|
||||
this.parseBlob(event.data, ws);
|
||||
}
|
||||
if (this.currentPageName === "/|") {
|
||||
this.parseAllBlob(event.data, ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleClose(ws, ip) {
|
||||
this.debug && console.log("[e]", ip, "connection closed");
|
||||
this.markDeviceStatus(ws, false);
|
||||
}
|
||||
|
||||
handleError(ws, ip) {
|
||||
this.debug && console.log("[e]", ip, "connection error");
|
||||
this.markDeviceStatus(ws, false);
|
||||
}
|
||||
|
||||
markDeviceStatus(ws, status) {
|
||||
this.deviceList.forEach((device) => {
|
||||
if (device.ws === ws) {
|
||||
device.status = status;
|
||||
device.ping = 0;
|
||||
this.debug && console.log("[i]", device.ip, ws, `status ${status ? "online" : "offline"}`);
|
||||
if (!status) {
|
||||
this.deleteWidget(ws);
|
||||
this.sortingLayout(ws);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.selectedDeviceDataRefresh();
|
||||
}
|
||||
|
||||
deleteWidget(ws) {
|
||||
this.layoutJson = this.layoutJson.filter((item) => item.ws !== ws);
|
||||
}
|
||||
|
||||
async parseBlob(blob, ws) {
|
||||
const header = await blob.slice(0, 6).text();
|
||||
const size = await blob.slice(7, 11).text();
|
||||
|
||||
const handlers = {
|
||||
itemsj: this.handleItemsJson,
|
||||
widget: this.handleWidgetsJson,
|
||||
config: this.handleConfigJson,
|
||||
scenar: this.handleScenarioTxt,
|
||||
settin: this.handleSettingsJson,
|
||||
ssidli: this.handleSsidJson,
|
||||
errors: this.handleErrorsJson,
|
||||
devlis: this.handleDeviceList,
|
||||
prfile: this.handleFlashProfileJson,
|
||||
otaupd: this.handleOtaJson,
|
||||
corelg: this.handleCoreLog,
|
||||
};
|
||||
|
||||
if (handlers[header]) {
|
||||
await handlers[header].call(this, blob, size, ws);
|
||||
}
|
||||
|
||||
await this.onParsed();
|
||||
}
|
||||
|
||||
async handleItemsJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "itemsJson");
|
||||
}
|
||||
|
||||
async handleWidgetsJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "widgetsJson");
|
||||
}
|
||||
|
||||
async handleConfigJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "configJson");
|
||||
}
|
||||
|
||||
async handleScenarioTxt(blob, size) {
|
||||
this.scenarioTxt = await this.getPayloadAsTxt(blob, size);
|
||||
this.debug && console.log("[i]", "scenarioTxt: ", this.scenarioTxt);
|
||||
}
|
||||
|
||||
async handleSettingsJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "settingsJson");
|
||||
}
|
||||
|
||||
async handleSsidJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "ssidJson");
|
||||
}
|
||||
|
||||
async handleErrorsJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "errorsJson");
|
||||
}
|
||||
|
||||
async handleDeviceList(blob, size, ws) {
|
||||
await this.parseJsonPayload(blob, size, "incDeviceList");
|
||||
await this.initDevList();
|
||||
}
|
||||
|
||||
async handleFlashProfileJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "flashProfileJson");
|
||||
}
|
||||
|
||||
async handleOtaJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "otaJson");
|
||||
}
|
||||
|
||||
async handleCoreLog(blob, size) {
|
||||
const txt = await this.getPayloadAsTxt(blob, size);
|
||||
this.addCoreMsg(txt);
|
||||
}
|
||||
|
||||
async parseJsonPayload(blob, size, jsonKey) {
|
||||
const out = {};
|
||||
if (await this.getPayloadAsJson(blob, size, out)) {
|
||||
this[jsonKey] = out.json;
|
||||
this.parsed[jsonKey] = true;
|
||||
this.debug && console.log("[✔]", `${jsonKey}: `, this[jsonKey]);
|
||||
} else {
|
||||
this.parsed[jsonKey] = false;
|
||||
this.debug && console.log("[e]", `${jsonKey} parse error`);
|
||||
}
|
||||
}
|
||||
|
||||
async parseAllBlob(blob, ws) {
|
||||
const header = await blob.slice(0, 6).text();
|
||||
const size = await blob.slice(7, 11).text();
|
||||
|
||||
const handlers = {
|
||||
status: this.handleStatusJson,
|
||||
layout: this.handleLayoutJson,
|
||||
params: this.handleParamsJson,
|
||||
charta: this.handleChartAJson,
|
||||
chartb: this.handleChartBJson,
|
||||
};
|
||||
|
||||
if (handlers[header]) {
|
||||
await handlers[header].call(this, blob, size, ws);
|
||||
}
|
||||
}
|
||||
|
||||
async handleStatusJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "statusJson");
|
||||
}
|
||||
|
||||
async handleLayoutJson(blob, size, ws) {
|
||||
const out = {};
|
||||
if (await this.getPayloadAsJson(blob, size, out)) {
|
||||
this.combineLayoutsInOne(ws, out.json);
|
||||
this.debug && console.log("[✔]", "devLayout: ", out.json);
|
||||
} else {
|
||||
this.debug && console.log("[e]", "devLayout parse error");
|
||||
}
|
||||
}
|
||||
|
||||
async handleParamsJson(blob, size, ws) {
|
||||
const out = {};
|
||||
if (await this.getPayloadAsJson(blob, size, out)) {
|
||||
this.paramsJson = { ...this.paramsJson, ...out.json };
|
||||
this.updateAllStatuses(ws);
|
||||
this.onParsed();
|
||||
this.debug && console.log("[✔]", "devParams: ", out.json);
|
||||
} else {
|
||||
this.debug && console.log("[e]", "devParams parse error");
|
||||
}
|
||||
}
|
||||
|
||||
async handleChartAJson(blob, size) {
|
||||
const txt = await this.getPayloadAsTxt(blob, size);
|
||||
const chartJson = JSON.parse(`[${txt.substring(0, txt.length - 1)}]`);
|
||||
const out = {};
|
||||
if (await this.getJsonAsJson(blob, size, out)) {
|
||||
const finalDataJson = { status: chartJson, ...out.json };
|
||||
this.updateWidgetByArray(finalDataJson);
|
||||
this.debug && console.log("[✔]", "chartJson: ", finalDataJson);
|
||||
} else {
|
||||
this.debug && console.log("[e]", "chart json add-ns parse error");
|
||||
}
|
||||
}
|
||||
|
||||
async handleChartBJson(blob, size) {
|
||||
await this.parseJsonPayload(blob, size, "chartStatus");
|
||||
}
|
||||
|
||||
sendCurrentPageNameToSelectedWs() {
|
||||
if (this.selectedWs !== undefined) {
|
||||
this.wsSendMsg(this.selectedWs, this.currentPageName);
|
||||
}
|
||||
}
|
||||
|
||||
wsSendMsg(ws, msg) {
|
||||
if (this.socket[ws]?.readyState === 1) {
|
||||
this.socket[ws].send(msg);
|
||||
this.debug && console.log("[i]", this.getIP(ws), ws, "msg send success", msg);
|
||||
} else {
|
||||
this.debug && console.log("[e]", this.getIP(ws), ws, "msg not send");
|
||||
}
|
||||
}
|
||||
|
||||
ack(ws, st) {
|
||||
if (!st) {
|
||||
this.startMillis[ws] = Date.now();
|
||||
this.ackTimeoutsArr[ws] = setTimeout(() => {
|
||||
this.markDeviceStatus(ws, false);
|
||||
}, this.waitingAckTimeout);
|
||||
} else {
|
||||
clearTimeout(this.ackTimeoutsArr[ws]);
|
||||
this.ping[ws] = Date.now() - this.startMillis[ws];
|
||||
this.deviceList.forEach((device) => {
|
||||
if (device.ws === ws) {
|
||||
device.ping = this.ping[ws];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
selectedDeviceDataRefresh() {
|
||||
const selectedDevice = this.deviceList.find((device) => device.ws === this.selectedWs);
|
||||
this.socketConnected = selectedDevice?.status || false;
|
||||
}
|
||||
|
||||
sortingLayout(ws) {
|
||||
this.layoutJson.sort((a, b) => a.descr.localeCompare(b.descr));
|
||||
this.wsSendMsg(ws, "/params|");
|
||||
}
|
||||
|
||||
wsTestMsgTask() {
|
||||
setTimeout(() => this.wsTestMsgTask(), 1000);
|
||||
if (!this.preventReconnect) {
|
||||
this.remainingTimeout--;
|
||||
if (this.rebootOrUpdateProcess && this.socketConnected) {
|
||||
this.rebootOrUpdateProcess = false;
|
||||
this.showAwaitingCircle = false;
|
||||
this.reconnectTimeout = 60;
|
||||
this.remainingTimeout = this.reconnectTimeout;
|
||||
}
|
||||
if (this.remainingTimeout <= 0) {
|
||||
this.debug && console.log("[i]", "----timer tick----");
|
||||
this.remainingTimeout = this.reconnectTimeout;
|
||||
this.deviceList.forEach((device) => {
|
||||
if (!device.status) {
|
||||
this.wsConnect(device.ws);
|
||||
this.wsEventAdd(device.ws);
|
||||
} else {
|
||||
this.wsSendMsg(device.ws, "/tst|");
|
||||
this.ack(device.ws, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getPayloadAsJson(blob, size, out) {
|
||||
const partBlob = blob.slice(size, blob.length);
|
||||
const txt = await partBlob.text();
|
||||
try {
|
||||
out.json = JSON.parse(txt);
|
||||
out.parse = true;
|
||||
} catch (e) {
|
||||
this.debug && console.log("[e]", "json parse error: ", txt);
|
||||
out.parse = false;
|
||||
}
|
||||
return out.parse;
|
||||
}
|
||||
|
||||
async getPayloadAsTxt(blob, size) {
|
||||
const partBlob = blob.slice(size, blob.length);
|
||||
return await partBlob.text();
|
||||
}
|
||||
|
||||
async getJsonAsJson(blob, size, out) {
|
||||
const partBlob = blob.slice(size, blob.length);
|
||||
const txt = await partBlob.text();
|
||||
try {
|
||||
out.json = JSON.parse(txt);
|
||||
out.parse = true;
|
||||
} catch (e) {
|
||||
this.debug && console.log("[e]", "json parse error: ", txt);
|
||||
out.parse = false;
|
||||
}
|
||||
return out.parse;
|
||||
}
|
||||
|
||||
async devListOverride() {
|
||||
this.deviceList = this.incDeviceList;
|
||||
this.sortList(this.deviceList);
|
||||
this.deviceList[0].status = true;
|
||||
this.debug && console.log("[i]", "[devlist]", "devlist overridden");
|
||||
}
|
||||
|
||||
async onParsed() {
|
||||
const pageHandlers = {
|
||||
"/|": () => (this.pageReady.dash = true),
|
||||
"/config|": () => this.handleConfigPage(),
|
||||
"/connection|": () => this.handleConnectionPage(),
|
||||
"/list|": () => this.handleListPage(),
|
||||
"/system|": () => this.handleSystemPage(),
|
||||
"/profile|": () => this.handleProfilePage(),
|
||||
};
|
||||
|
||||
if (pageHandlers[this.currentPageName]) {
|
||||
await pageHandlers[this.currentPageName].call(this);
|
||||
}
|
||||
}
|
||||
|
||||
handleConfigPage() {
|
||||
if (this.parsed.itemsJson && this.parsed.widgetsJson && this.parsed.configJson && this.parsed.settingsJson) {
|
||||
this.clearParsedFlags();
|
||||
this.pageReady.config = true;
|
||||
this.debug && console.log("✔✔", "config page parsed");
|
||||
}
|
||||
}
|
||||
|
||||
handleConnectionPage() {
|
||||
if (this.parsed.ssidJson && this.parsed.settingsJson && this.parsed.errorsJson) {
|
||||
this.clearParsedFlags();
|
||||
this.pageReady.connection = true;
|
||||
this.debug && console.log("✔✔", "connection page parsed");
|
||||
}
|
||||
}
|
||||
|
||||
handleListPage() {
|
||||
if (this.parsed.settingsJson) {
|
||||
this.clearParsedFlags();
|
||||
this.pageReady.list = true;
|
||||
this.debug && console.log("✔✔", "list page parsed");
|
||||
}
|
||||
}
|
||||
|
||||
handleSystemPage() {
|
||||
if (this.parsed.errorsJson && this.parsed.settingsJson) {
|
||||
this.clearParsedFlags();
|
||||
this.getVersionsList();
|
||||
this.pageReady.system = true;
|
||||
this.debug && console.log("✔✔", "system page parsed");
|
||||
}
|
||||
}
|
||||
|
||||
async handleProfilePage() {
|
||||
if (this.parsed.flashProfileJson) {
|
||||
this.clearParsedFlags();
|
||||
this.pageReady.profile = true;
|
||||
this.debug && console.log("✔✔", "profile page parsed");
|
||||
await this.getModInfo();
|
||||
await this.getProfile();
|
||||
}
|
||||
}
|
||||
|
||||
async devListCombine() {
|
||||
this.deviceList = this.combineArrays(this.deviceList, this.incDeviceList);
|
||||
this.sortList(this.deviceList);
|
||||
this.debug && console.log("[i]", "[devlist]", "devlist combined");
|
||||
}
|
||||
|
||||
combineArrays(A, B) {
|
||||
const ids = new Set(A.map((d) => d.ip));
|
||||
return [...A, ...B.filter((d) => !ids.has(d.ip))];
|
||||
}
|
||||
|
||||
getSelectedDeviceData(ws) {
|
||||
this.selectedDeviceData = this.deviceList.find((device) => device.ws === ws);
|
||||
}
|
||||
|
||||
sortList(list) {
|
||||
const firstDev = list.shift();
|
||||
list.sort((a, b) => a.name.localeCompare(b.name));
|
||||
list.unshift(firstDev);
|
||||
}
|
||||
|
||||
sendToAllDevices(msg) {
|
||||
this.deviceList.forEach((device) => {
|
||||
if (device.status === true) {
|
||||
this.wsSendMsg(device.ws, msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//слияние layout-ов всех устройств в общий layout
|
||||
async combineLayoutsInOne(ws, devLayout) {
|
||||
for (let i = 0; i < devLayout.length; i++) {
|
||||
devLayout[i].ws = ws;
|
||||
}
|
||||
this.layoutJson = this.layoutJson.concat(devLayout);
|
||||
console.log("[2]", ws, "devLayout pushed to layout");
|
||||
this.sortingLayout(ws);
|
||||
}
|
||||
|
||||
updateAllStatuses(ws) {
|
||||
for (const [key, value] of Object.entries(this.paramsJson)) {
|
||||
for (let i = 0; i < this.layoutJson.length; i++) {
|
||||
let topic = this.layoutJson[i].topic;
|
||||
if (topic) {
|
||||
//this.layoutJson[i].ws = ws;
|
||||
topic = topic.substring(topic.lastIndexOf("/") + 1, topic.length);
|
||||
if (key === topic) {
|
||||
console.log("[i]", "updated =>" + topic, value);
|
||||
this.layoutJson[i].status = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.wsSendMsg(ws, "/charts|");
|
||||
}
|
||||
|
||||
sortingLayout(ws) {
|
||||
//сортируем весь layout по алфавиту
|
||||
this.layoutJson.sort(function (a, b) {
|
||||
if (a.descr < b.descr) {
|
||||
return -1;
|
||||
}
|
||||
if (a.descr > b.descr) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
//формируем json всех карточек
|
||||
this.pages = [];
|
||||
const newPage = Array.from(new Set(Array.from(this.layoutJson, ({ page }) => page)));
|
||||
newPage.forEach(function (item, i, arr) {
|
||||
this.pages = [
|
||||
...this.pages,
|
||||
JSON.parse(
|
||||
JSON.stringify({
|
||||
page: item,
|
||||
})
|
||||
),
|
||||
];
|
||||
});
|
||||
//сортируем карточки по алфавиту
|
||||
this.pages.sort(function (a, b) {
|
||||
if (a.page < b.page) {
|
||||
return -1;
|
||||
}
|
||||
if (a.page > b.page) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
this.layoutJson = this.layoutJson;
|
||||
console.log("[3]", ws, "layout sort, requested params...");
|
||||
this.wsSendMsg(ws, "/params|");
|
||||
}
|
||||
}
|
||||
|
||||
export default WebSocketManager;
|
||||
@@ -7,7 +7,7 @@
|
||||
import Anydata from "../widgets/Anydata.svelte";
|
||||
import Alarm from "../components/Alarm.svelte";
|
||||
|
||||
export let layoutJson;
|
||||
export let layoutJson = [];
|
||||
|
||||
$: layoutJson.length, timeOut();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user