From 1716bf5f83c36c31e8bf14b4c8c0992c713fb642 Mon Sep 17 00:00:00 2001 From: "S.Moldir" Date: Fri, 25 Sep 2026 10:29:07 +0000 Subject: [PATCH] =?UTF-8?q?v3:=20=D0=BA=D0=B0=D1=80=D1=82=D0=B0=20=D0=9A?= =?UTF-8?q?=D0=B0=D0=B7=D0=B0=D1=85=D1=81=D1=82=D0=B0=D0=BD=D0=B0=20=D1=81?= =?UTF-8?q?=D0=BE=20=D1=81=D1=85=D0=B5=D0=BC=D0=B0=D0=BC=D0=B8=20SSW,=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B3=D0=B8=D0=BE=D0=BD=D0=B0=D0=BC=D0=B8=20=D0=B8?= =?UTF-8?q?=20=D0=9E=D0=9F=D0=A2=D0=A1-6=20=D0=B4=D0=BB=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B7=D0=B5=D1=80=D0=B2=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script.js | 258 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 142 insertions(+), 116 deletions(-) diff --git a/script.js b/script.js index 9ec679f..a4f348a 100644 --- a/script.js +++ b/script.js @@ -4,36 +4,57 @@ const REG = [ { num: "7271UV", - type: "SIP", - station: "ГТС Алматы", + type: "BRW SIP", + station: "BRW SIP платформа (Алматы)", city: "Алматы", - desc: "Оконечный номер экстренных служб (пример схемы)", - reserve: "Есть: SIP Trunk 7272222222 (принадлежит ГТС Алматы)", + desc: "Основной оконечный номер экстренных служб", + reserve: "Есть: SIP Trunk 7272222222 (принадлежит ОПТС-6, Алматы)", }, { num: "727103", type: "BRW SIP", - station: "ГТС Алматы", + station: "BRW SIP платформа (7273130333)", city: "Алматы", desc: "Номер пересчёта принадлежит BRW SIP 7273130333", - reserve: "Есть: SIP Trunk 7272222222 (принадлежит ГТС Алматы)", + reserve: "Есть: SIP Trunk 7272222222 (принадлежит ОПТС-6, Алматы)", }, { num: "7272222222", type: "ТДМ", - station: "ГТС Алматы", + station: "ОПТС-6 (Алматы)", city: "Алматы", - desc: "Резервный номер в SIP Trunk, принадлежит ГТС Алматы", - reserve: "Резерв для 7271UV", + desc: "Резервный номер в SIP Trunk, принадлежит ОПТС-6", + reserve: "Резерв для 7271UV / 727103", }, ]; - const OWNERS = [ - { name: "ГТС Алматы (собственный номер станции)", short: "ГТС Алматы", match: function (d) { return d.owner === "own"; } }, - { name: "BRW SIP", short: "BRW SIP", match: function (d) { return d.owner === "brw-sip"; } }, - { name: "АТС", short: "АТС", match: function (d) { return d.owner === "pbx"; } }, - { name: "АТС зоны", short: "АТС зоны", match: function (d) { return d.owner === "zone"; } }, - ]; + const STATIONS = { + almaty: { + name: "Алматы", + x: 520, y: 505, + sub: { label: "ОПТС3 · ОПТС-72 · ОПТС-6", dx: 0, dy: 14, anchor: "middle" }, + op: { + id: "op6", name: "ОПТС-6", x: 578, y: 552, w: 112, h: 26, + note: "резерв 7272222222", + anchor: "start", + }, + }, + astana: { + name: "Астана", + x: 478, y: 208, + sub: { label: "SSW рег. · SSW транзит", dx: 0, dy: 14, anchor: "middle" }, + }, + aktobe: { name: "Актобе", x: 198, y: 438 }, + kostanay: { name: "Костанай", x: 352, y: 272 }, + kokshetau: { name: "Кокшетау", x: 478, y: 268 }, + petropavlovsk: { name: "Петропавловск", x: 418, y: 118 }, + taraz: { name: "Тараз", x: 438, y: 578 }, + shytkent: { name: "Шымкент", x: 468, y: 626 }, + pavlodar: { name: "Павлодар", x: 598, y: 162 }, + ustkam: { name: "Усть-Каменогорск", x: 738, y: 248 }, + }; + + const NS = "http://www.w3.org/2000/svg"; function normalize(v) { return String(v == null ? "" : v).replace(/\s+/g, "").toUpperCase(); @@ -65,28 +86,28 @@ viaStation = "неизвестна"; } - const owners = OWNERS.filter(function (o) { return o.match({ owner: owner }); }); - const ownerName = owners.length ? owners[0].name : "станция-владелец"; + const ownerName = ownerLabel(owner); const endLabel = entry ? "Оконечный номер " + entry.num : "Оконечный номер"; const mobile = [ { t: "Мобильный оператор" }, - { t: "SSW/NGN транзит" }, - { t: "SSW/NGN рег." }, + { t: "SSW транзит (ОДС / Астана)" }, + { t: "SSW рег. (регион номера)" }, { t: endLabel, active: true }, ]; const fixed = [ { t: "Станция владельца номера абонента" }, - { t: "SSW/NGN рег." + (viaStation ? " (" + viaStation + ")" : "") }, + { t: "SSW рег. (своя)" }, + { t: "SSW рег. (регион номера)" }, { t: endLabel, active: true }, ]; if (owner === "brw-sip") { - fixed.splice(2, 0, { t: "Номер пересчёта → BRW SIP", active: true }); + fixed.splice(3, 0, { t: "BRW SIP платформа (стык со всеми SSW рег.)", active: true }); } if (reserve && !/не определён/i.test(reserve) && !isReserve) { const rnum = reserve.replace(/^Есть[:\s]*/, "").split(/[\s(]/)[0]; - mobile.push({ t: "Резерв", sub: rnum + " · авт. при недоступности SIP" }); - fixed.push({ t: "Резерв", sub: rnum + " · авт. при недоступности SIP" }); + mobile.push({ t: "Резерв", sub: rnum + " · ОПТС-6, при недоступности BRW SIP" }); + fixed.push({ t: "Резерв", sub: rnum + " · ОПТС-6, при недоступности BRW SIP" }); } return { entry: entry, city: city, owner: owner, ownerName: ownerName, reserve: reserve, viaStation: viaStation, mobile: mobile, fixed: fixed }; @@ -100,6 +121,13 @@ return "zone"; } + function ownerLabel(o) { + if (o === "brw-sip") return "BRW SIP платформа"; + if (o === "pbx") return "АТС (станция)"; + if (o === "own") return "Собственный номер станции"; + return "АТС зоны"; + } + function renderChain(id, nodes) { const el = document.getElementById(id); el.innerHTML = ""; @@ -167,8 +195,8 @@ r.textContent = "Резерв: " + d.reserve; } - document.getElementById("r1src").textContent = "Мобильный оператор → SSW/NGN транзит → SSW/NGN рег. → " + (d.entry ? d.entry.num : "оконечный номер"); - document.getElementById("r2src").textContent = "Станция владельца номера → SSW/NGN рег. → " + (d.entry ? d.entry.num : "оконечный номер"); + document.getElementById("r1src").textContent = "Мобильный оператор → SSW транзит (ОДС/Астана) → SSW рег. → " + (d.entry ? d.entry.num : "оконечный номер"); + document.getElementById("r2src").textContent = "Станция → SSW рег. (своя) → SSW рег. (регион) → " + (d.entry ? d.entry.num : "оконечный номер"); renderChain("chain1", d.mobile); renderChain("chain2", d.fixed); renderScheme(d.entry ? d.entry.num : num); @@ -204,115 +232,113 @@ } function renderScheme(focusNum) { - const NS = "http://www.w3.org/2000/svg"; const svg = document.createElementNS(NS, "svg"); - svg.setAttribute("viewBox", "0 0 1100 460"); + svg.setAttribute("viewBox", "0 0 900 700"); svg.setAttribute("role", "img"); - svg.setAttribute("aria-label", "Схема маршрутизации вызовов экстренных служб"); + svg.setAttribute("aria-label", "Схема маршрутизации вызовов экстренных служб по карте Казахстана"); - const entry = findEntry(focusNum) || REG[0] || null; - const endNum = entry ? entry.num : "—"; - const hasBrw = entry ? normalize(entry.type).indexOf("BRW") === 0 : false; - - function rect(x, y, w, h, rx, fill, stroke, sw) { - const r = document.createElementNS(NS, "rect"); - r.setAttribute("x", x); r.setAttribute("y", y); - r.setAttribute("width", w); r.setAttribute("height", h); - r.setAttribute("rx", rx); - if (fill) r.setAttribute("fill", fill); - if (stroke) r.setAttribute("stroke", stroke); - r.setAttribute("stroke-width", sw || 1); - return r; + function el(tag, attrs) { + const e = document.createElementNS(NS, tag); + Object.keys(attrs || {}).forEach(function (k) { e.setAttribute(k, attrs[k]); }); + return e; } - function txt(x, y, s, size, fill, weight) { - const t = document.createElementNS(NS, "text"); - t.setAttribute("x", x); t.setAttribute("y", y); - t.setAttribute("text-anchor", "middle"); + function line(x1, y1, x2, y2, opts) { + const l = el("line", { + x1: x1, y1: y1, x2: x2, y2: y2, + stroke: opts.accent ? "var(--vibe-accent)" : "var(--kt-ai-fg-muted)", + "stroke-width": opts.width || 1.4, + }); + if (opts.dash) l.setAttribute("stroke-dasharray", opts.dash); + if (opts.marker) l.setAttribute("marker-end", "url(#arrow" + (opts.accent ? "A" : "M") + ")"); + l.setAttribute("opacity", opts.opacity || 0.8); + return l; + } + function text(x, y, s, size, fill, weight, anchor) { + const t = el("text", { x: x, y: y, "text-anchor": anchor || "middle" }); t.setAttribute("style", "font-size:" + size + "px; fill:" + fill + ";" + (weight ? " font-weight:" + weight + ";" : "")); t.textContent = s; return t; } - function edge(x1, y1, x2, y2, dashed, label, ldy) { - const l = document.createElementNS(NS, "line"); - l.setAttribute("x1", x1); l.setAttribute("y1", y1); - l.setAttribute("x2", x2); l.setAttribute("y2", y2); - l.setAttribute("stroke", dashed ? "var(--kt-ai-fg-muted)" : "var(--vibe-accent)"); - l.setAttribute("stroke-width", "1.6"); - if (dashed) l.setAttribute("stroke-dasharray", "5 4"); - l.setAttribute("marker-end", "url(#arrow" + (dashed ? "M" : "A") + ")"); - l.setAttribute("opacity", "0.85"); - svg.appendChild(l); - if (label) { - const t = document.createElementNS(NS, "text"); - t.setAttribute("x", (x1 + x2) / 2); t.setAttribute("y", ((y1 + y2) / 2) + (ldy || -7)); - t.setAttribute("text-anchor", "middle"); - t.setAttribute("style", "font-size:10px; fill:var(--kt-ai-fg-muted)"); - t.textContent = label; - svg.appendChild(t); - } - } - const defs = document.createElementNS(NS, "defs"); - defs.innerHTML = ''; + const defs = el("defs"); + defs.innerHTML = + '' + + ''; svg.appendChild(defs); - edge(215, 60, 390, 170); - edge(385, 60, 530, 170); - edge(560, 204, 620, 266); - if (hasBrw) { - edge(695, 300, 656, 334); - } - edge(620, 300, 350, 335, true, "резерв при недоступности SIP", -7); + // Стили точек городов и подписей + svg.setAttribute("style", "svg text { font-family: var(--kt-ai-font-sans); }"); - const boxFill = "var(--kt-ai-card-bg)"; - const bStroke = "var(--kt-ai-border)"; + // Контур Казахстана (упрощённый) + const outline = el("path", { + d: "M60,200 L150,115 L255,82 L368,60 L480,52 L592,62 L688,85 L772,122 L828,172 L856,252 L848,338 L808,404 L848,458 L806,528 L738,588 L656,632 L564,654 L484,668 L408,652 L344,606 L284,552 L222,496 L162,442 L114,378 L82,302 L60,252 Z", + fill: "var(--kt-ai-bg)", + stroke: "var(--kt-ai-border)", + "stroke-width": 1.5, + }); + svg.appendChild(outline); - // Мобильные операторы - svg.appendChild(rect(50, 32, 165, 56, 10, boxFill, bStroke)); - svg.appendChild(txt(132, 56, "Мобильные", 14, "var(--kt-ai-fg)", 600)); - svg.appendChild(txt(132, 73, "операторы", 14, "var(--kt-ai-fg)", 600)); - // ГТС - svg.appendChild(rect(220, 32, 165, 56, 10, boxFill, bStroke)); - svg.appendChild(txt(302, 63, "ГТС", 15, "var(--kt-ai-fg)", 600)); - // SSW/NGN транзит - svg.appendChild(rect(390, 170, 170, 56, 10, boxFill, bStroke)); - svg.appendChild(txt(475, 193, "SSW/NGN транзит", 14, "var(--kt-ai-fg)", 600)); - svg.appendChild(txt(475, 210, "транзитная шлюзовая система", 11, "var(--kt-ai-fg-muted)")); - // SSW/NGN рег. - svg.appendChild(rect(550, 266, 170, 56, 10, boxFill, bStroke)); - svg.appendChild(txt(635, 289, "SSW/NGN рег.", 14, "var(--kt-ai-fg)", 600)); - svg.appendChild(txt(635, 306, "региональная маршрутизация", 11, "var(--kt-ai-fg-muted)")); - // Резерв - svg.appendChild(rect(200, 335, 150, 56, 10, "var(--kt-ai-bg)", bStroke)); - svg.appendChild(txt(275, 358, "SIP Trunk (резерв)", 12, "var(--kt-ai-fg)", 600)); - svg.appendChild(txt(275, 375, "7272222222 · ГТС Алматы", 11, "var(--kt-ai-fg-muted)")); - // Оконечный номер - svg.appendChild(rect(700, 335, 190, 56, 10, "var(--kt-ai-bg)", hasBrw ? "var(--vibe-accent)" : bStroke, hasBrw ? 2 : 1)); - svg.appendChild(txt(795, 358, "Оконечный номер", 13, "var(--kt-ai-fg)", 600)); - svg.appendChild(txt(795, 375, endNum, 12, "var(--vibe-accent)", 600)); - // BRW SIP - if (hasBrw) { - svg.appendChild(rect(450, 335, 205, 56, 10, boxFill, bStroke)); - svg.appendChild(txt(552, 358, "BRW SIP", 14, "var(--kt-ai-fg)", 600)); - svg.appendChild(txt(552, 375, "7273130333", 12, "var(--kt-ai-fg-muted)", 600)); - const l1 = document.createElementNS(NS, "line"); - l1.setAttribute("x1", 655); l1.setAttribute("y1", 363); - l1.setAttribute("x2", 695); l1.setAttribute("y2", 363); - l1.setAttribute("stroke", "var(--vibe-accent)"); - l1.setAttribute("stroke-width", "1.6"); - l1.setAttribute("marker-end", "url(#arrowA)"); - l1.setAttribute("opacity", "0.85"); - svg.appendChild(l1); - } + // Вспомогательные линии SSW (серые) + [[198, 438, 478, 208], [352, 272, 478, 208], [478, 268, 478, 208], [418, 118, 478, 208], + [598, 162, 478, 208], [738, 248, 478, 208], [438, 578, 520, 505], [468, 626, 520, 505], + [60, 200, 478, 208], + ].forEach(function (p) { + svg.appendChild(line(p[0], p[1], p[2], p[3], { opacity: 0.5 })); + }); - const el = document.getElementById("schemeSvg"); - el.innerHTML = ""; - el.appendChild(svg); + // Основная цепочка ЭС для Алматы (акцент): мобильные/транзит Астана → SSW рег. Алматы → BRW SIP → оконечный + const almaty = STATIONS.almaty; + svg.appendChild(line(478, 208 + 28, 520, 505 - 18, { accent: true, marker: true })); + svg.appendChild(line(520, 505, 520, 588, { accent: true, marker: true })); + + // Резерв: SSW рег. Алматы → ОПТС-6 (пунктир) + const op6 = almaty.op; + svg.appendChild(line(555, 525, 578, 552, { dash: "5 4", marker: true, width: 1.4 })); + + // Узел BRW SIP + svg.appendChild(el("rect", { x: 428, y: 588, width: 184, height: 26, rx: 6, fill: "var(--vibe-tint)", stroke: "var(--vibe-accent)", "stroke-width": 1.2 })); + svg.appendChild(text(520, 605, "BRW SIP · стык со всеми SSW рег.", 11, "var(--kt-ai-fg)", 600)); + + // Узел оконечного номера (под Алматы) + svg.appendChild(el("rect", { x: 448, y: 618, width: 144, height: 24, rx: 6, fill: "var(--kt-ai-card-bg)", stroke: "var(--vibe-accent)", "stroke-width": 1.5 })); + const endNum = focusNum ? normalize(focusNum) : "7271UV"; + svg.appendChild(text(520, 634, "Оконечный: " + (endNum || "7271UV"), 11, "var(--kt-ai-fg)", 600)); + + // Точки городов + Object.keys(STATIONS).forEach(function (key) { + const c = STATIONS[key]; + const isAlmaty = key === "almaty"; + const isAstana = key === "astana"; + const circle = el("circle", { + cx: c.x, cy: c.y, r: isAlmaty ? 6 : 4.5, + fill: isAlmaty || isAstana ? "var(--vibe-accent)" : "var(--kt-ai-card-bg)", + stroke: "var(--kt-ai-border-strong)", + "stroke-width": 1.2, + }); + svg.appendChild(circle); + + let anchor = "middle", dy = -10; + if (c.x < 240) { anchor = "start"; dy = 4; } + if (c.x > 690) { anchor = "end"; dy = 4; } + svg.appendChild(text(c.x, c.y + dy, c.name, 12, "var(--kt-ai-fg)", 500, anchor)); + if (c.sub) { + svg.appendChild(text(c.x, c.y + dy + 12, c.sub.label, 10, "var(--kt-ai-fg-muted)", null, anchor)); + } + }); + + // Блок «ОПТС-6» + svg.appendChild(el("rect", { x: op6.x - 4, y: op6.y - 16, width: op6.w, height: 34, rx: 6, fill: "var(--kt-ai-card-bg)", stroke: "var(--kt-ai-border-strong)", "stroke-width": 1 })); + svg.appendChild(text(op6.x + 2, op6.y, op6.name, 12, "var(--kt-ai-fg)", 600, "start")); + svg.appendChild(text(op6.x + 2, op6.y + 14, op6.note, 10, "var(--kt-ai-fg-muted)", null, "start")); + + const elBox = document.getElementById("schemeSvg"); + elBox.innerHTML = ""; + elBox.appendChild(svg); } document.addEventListener("DOMContentLoaded", function () { renderRegister(); - renderScheme("727103"); + renderScheme("7271UV"); const input = document.getElementById("num"); const btn = document.getElementById("findBtn"); btn.addEventListener("click", function () { show(input.value); });