mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
feat(region-routing): unified per-family routing cards + region-scoped family→channel routing (#87)
* feat(region-routing): P1 tagging + region_routes primitive + read/write API + preview launcher - config.py: add Coverage.region_tagging (bool=False); add RegionRouteMatrix dataclass (enabled, cells) above NotificationsConfig; add region_routes field to NotificationsConfig; add explicit hydration branch for region_routes in _dict_to_dataclass mirroring destinations pattern. - coverage_area.py: add MonitoringArea.name (str|None=None, frozen); update areas_from_config to preserve name; refactor inline geom extraction from classify_event_areas into shared _event_geom_json helper; add matching_area_names(geom_json, areas)->list[str] (additive, all named matches, config-order, deduped; gate unchanged); add event_region_names convenience wrapper. - coverage_filter.py: add region_tagging ctor kwarg; stamp event.region/ regions before the gate when region_tagging=True and areas non-empty and not event.regions (never clobbers satpass preset). - pipeline/__init__.py: wire region_tagging into CoverageFilter construction. - notification_routes.py: add GET /notifications/regions (named coverage area names, config-order, deduped); GET /notifications/region-routing (matrix as JSON); POST /notifications/region-routing (explicit RMW — only region_routes changes, toggles/rules/destinations survive). - scripts/preview_dashboard.py: mesh-free launcher — dashboard API only, no mesh connector, no broadcast loop; vite runs separately. All 87 coverage tests pass; 300 total pass; 6 pre-existing failures unchanged (adapter config count mismatch + MeshCore EventType.NEW_CONTACT). * feat(region-routing): manual region x family matrix editor page Adds RegionRoutingMatrix.tsx — a plain editor over the region_routes config primitive. Rows = families (via useFamilies()), cols = regions (from GET /api/notifications/regions). Each cell exposes MT channel (ChannelPicker single + includeDisabled), MC channel name (text input), min_severity select (routine/priority/critical/immediate), and an enabled checkbox. Only cells where MT or MC is set are included in the sparse POST payload. Master enable toggle maps to top-level enabled. MT budget guard warns when more than 7 distinct MT indices are in use. Sticky family column; horizontal scroll for wide region sets. Registers route /region-routing in App.tsx and adds "Region Routing" nav entry (Map icon) under the Meshtastic section in Layout.tsx, immediately after Routing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(region-routing): regions endpoint reads saved (disk) coverage so routing columns are dynamic without a bot restart; preview reloads config after writes * feat(routing): unify MT/MC routing into per-family cards; region routing as an in-card expand; remove rules/destinations UI + standalone page * refactor(routing): move Meshtastic Routing from /notifications to /meshtastic/routing (mirror /meshcore/routing); redirect legacy path * feat(region-routing): dispatcher honors region_routes matrix (authoritative-on-match, per-region cooldown, per-channel dedup); non-matrix path unchanged Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(region-routing): matrix dedup key must match boot-restore 2-tuple form (prevents restart re-broadcast flood); regression test --------- Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ceb95fb80e
commit
2c46c9104d
8638 changed files with 1589821 additions and 2380 deletions
13
work/dashboard-frontend/node_modules/d3-path/LICENSE
generated
vendored
Normal file
13
work/dashboard-frontend/node_modules/d3-path/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
Copyright 2015-2022 Mike Bostock
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
94
work/dashboard-frontend/node_modules/d3-path/README.md
generated
vendored
Normal file
94
work/dashboard-frontend/node_modules/d3-path/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# d3-path
|
||||
|
||||
Say you have some code that draws to a 2D canvas:
|
||||
|
||||
```js
|
||||
function drawCircle(context, radius) {
|
||||
context.moveTo(radius, 0);
|
||||
context.arc(0, 0, radius, 0, 2 * Math.PI);
|
||||
}
|
||||
```
|
||||
|
||||
The d3-path module lets you take this exact code and additionally render to [SVG](http://www.w3.org/TR/SVG/paths.html). It works by [serializing](#path_toString) [CanvasPathMethods](http://www.w3.org/TR/2dcontext/#canvaspathmethods) calls to [SVG path data](http://www.w3.org/TR/SVG/paths.html#PathData). For example:
|
||||
|
||||
```js
|
||||
const context = d3.path();
|
||||
drawCircle(context, 40);
|
||||
pathElement.setAttribute("d", context.toString());
|
||||
```
|
||||
|
||||
Now code you write once can be used with both Canvas (for performance) and SVG (for convenience). For a practical example, see [d3-shape](https://github.com/d3/d3-shape).
|
||||
|
||||
## Installing
|
||||
|
||||
If you use npm, `npm install d3-path`. You can also download the [latest release on GitHub](https://github.com/d3/d3-path/releases/latest). In modern browsers, you can import d3-path from jsDelivr:
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
|
||||
import {path} from "https://cdn.jsdelivr.net/npm/d3-path@3/+esm";
|
||||
|
||||
const p = path();
|
||||
p.moveTo(1, 2);
|
||||
p.lineTo(3, 4);
|
||||
p.closePath();
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
For legacy environments, you can load d3-path’s UMD bundle from an npm-based CDN such as jsDelivr; a `d3` global is exported:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/d3-path@3"></script>
|
||||
<script>
|
||||
|
||||
const path = d3.path();
|
||||
|
||||
</script>
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
<a name="path" href="#path">#</a> d3.<b>path</b>() · [Source](https://github.com/d3/d3-path/blob/master/src/path.js), [Examples](https://observablehq.com/@d3/d3-path)
|
||||
|
||||
Constructs a new path serializer that implements [CanvasPathMethods](http://www.w3.org/TR/2dcontext/#canvaspathmethods).
|
||||
|
||||
<a name="path_moveTo" href="#path_moveTo">#</a> <i>path</i>.<b>moveTo</b>(<i>x</i>, <i>y</i>)
|
||||
|
||||
Move to the specified point ⟨*x*, *y*⟩. Equivalent to [*context*.moveTo](http://www.w3.org/TR/2dcontext/#dom-context-2d-moveto) and SVG’s [“moveto” command](http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands).
|
||||
|
||||
<a name="path_closePath" href="#path_closePath">#</a> <i>path</i>.<b>closePath</b>()
|
||||
|
||||
Ends the current subpath and causes an automatic straight line to be drawn from the current point to the initial point of the current subpath. Equivalent to [*context*.closePath](http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath) and SVG’s [“closepath” command](http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand).
|
||||
|
||||
<a name="path_lineTo" href="#path_lineTo">#</a> <i>path</i>.<b>lineTo</b>(<i>x</i>, <i>y</i>)
|
||||
|
||||
Draws a straight line from the current point to the specified point ⟨*x*, *y*⟩. Equivalent to [*context*.lineTo](http://www.w3.org/TR/2dcontext/#dom-context-2d-lineto) and SVG’s [“lineto” command](http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands).
|
||||
|
||||
<a name="path_quadraticCurveTo" href="#path_quadraticCurveTo">#</a> <i>path</i>.<b>quadraticCurveTo</b>(<i>cpx</i>, <i>cpy</i>, <i>x</i>, <i>y</i>)
|
||||
|
||||
Draws a quadratic Bézier segment from the current point to the specified point ⟨*x*, *y*⟩, with the specified control point ⟨*cpx*, *cpy*⟩. Equivalent to [*context*.quadraticCurveTo](http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto) and SVG’s [quadratic Bézier curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands).
|
||||
|
||||
<a name="path_bezierCurveTo" href="#path_bezierCurveTo">#</a> <i>path</i>.<b>bezierCurveTo</b>(<i>cpx1</i>, <i>cpy1</i>, <i>cpx2</i>, <i>cpy2</i>, <i>x</i>, <i>y</i>)
|
||||
|
||||
Draws a cubic Bézier segment from the current point to the specified point ⟨*x*, *y*⟩, with the specified control points ⟨*cpx1*, *cpy1*⟩ and ⟨*cpx2*, *cpy2*⟩. Equivalent to [*context*.bezierCurveTo](http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto) and SVG’s [cubic Bézier curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands).
|
||||
|
||||
<a name="path_arcTo" href="#path_arcTo">#</a> <i>path</i>.<b>arcTo</b>(<i>x1</i>, <i>y1</i>, <i>x2</i>, <i>y2</i>, <i>radius</i>)
|
||||
|
||||
Draws a circular arc segment with the specified *radius* that starts tangent to the line between the current point and the specified point ⟨*x1*, *y1*⟩ and ends tangent to the line between the specified points ⟨*x1*, *y1*⟩ and ⟨*x2*, *y2*⟩. If the first tangent point is not equal to the current point, a straight line is drawn between the current point and the first tangent point. Equivalent to [*context*.arcTo](http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto) and uses SVG’s [elliptical arc curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands).
|
||||
|
||||
<a name="path_arc" href="#path_arc">#</a> <i>path</i>.<b>arc</b>(<i>x</i>, <i>y</i>, <i>radius</i>, <i>startAngle</i>, <i>endAngle</i>[, <i>anticlockwise</i>])
|
||||
|
||||
Draws a circular arc segment with the specified center ⟨*x*, *y*⟩, *radius*, *startAngle* and *endAngle*. If *anticlockwise* is true, the arc is drawn in the anticlockwise direction; otherwise, it is drawn in the clockwise direction. If the current point is not equal to the starting point of the arc, a straight line is drawn from the current point to the start of the arc. Equivalent to [*context*.arc](http://www.w3.org/TR/2dcontext/#dom-context-2d-arc) and uses SVG’s [elliptical arc curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands).
|
||||
|
||||
<a name="path_rect" href="#path_rect">#</a> <i>path</i>.<b>rect</b>(<i>x</i>, <i>y</i>, <i>w</i>, <i>h</i>)
|
||||
|
||||
Creates a new subpath containing just the four points ⟨*x*, *y*⟩, ⟨*x* + *w*, *y*⟩, ⟨*x* + *w*, *y* + *h*⟩, ⟨*x*, *y* + *h*⟩, with those four points connected by straight lines, and then marks the subpath as closed. Equivalent to [*context*.rect](http://www.w3.org/TR/2dcontext/#dom-context-2d-rect) and uses SVG’s [“lineto” commands](http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands).
|
||||
|
||||
<a name="path_toString" href="#path_toString">#</a> <i>path</i>.<b>toString</b>()
|
||||
|
||||
Returns the string representation of this *path* according to SVG’s [path data specification](http://www.w3.org/TR/SVG/paths.html#PathData).
|
||||
|
||||
<a name="pathRound" href="#pathRound">#</a> d3.<b>pathRound</b>(*digits* = 3) · [Source](https://github.com/d3/d3-path/blob/master/src/path.js), [Examples](https://observablehq.com/@d3/d3-path)
|
||||
|
||||
Like [d3.path](#path), except limits the digits after the decimal to the specified number of *digits*.
|
||||
54
work/dashboard-frontend/node_modules/d3-path/package.json
generated
vendored
Normal file
54
work/dashboard-frontend/node_modules/d3-path/package.json
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "d3-path",
|
||||
"version": "3.1.0",
|
||||
"description": "Serialize Canvas path commands to SVG.",
|
||||
"homepage": "https://d3js.org/d3-path/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/d3/d3-path.git"
|
||||
},
|
||||
"keywords": [
|
||||
"d3",
|
||||
"d3-module",
|
||||
"canvas",
|
||||
"path",
|
||||
"svg",
|
||||
"graphics",
|
||||
"CanvasRenderingContext2D",
|
||||
"CanvasPathMethods",
|
||||
"Path2D"
|
||||
],
|
||||
"license": "ISC",
|
||||
"author": {
|
||||
"name": "Mike Bostock",
|
||||
"url": "http://bost.ocks.org/mike"
|
||||
},
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist/**/*.js",
|
||||
"src/**/*.js"
|
||||
],
|
||||
"module": "src/index.js",
|
||||
"main": "src/index.js",
|
||||
"jsdelivr": "dist/d3-path.min.js",
|
||||
"unpkg": "dist/d3-path.min.js",
|
||||
"exports": {
|
||||
"umd": "./dist/d3-path.min.js",
|
||||
"default": "./src/index.js"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"devDependencies": {
|
||||
"eslint": "8",
|
||||
"mocha": "10",
|
||||
"rollup": "3",
|
||||
"rollup-plugin-terser": "7"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha 'test/**/*-test.js' && eslint src test",
|
||||
"prepublishOnly": "rm -rf dist && yarn test && rollup -c",
|
||||
"postpublish": "git push && git push --tags && cd ../d3.github.com && git pull && cp ../${npm_package_name}/dist/${npm_package_name}.js ${npm_package_name}.v${npm_package_version%%.*}.js && cp ../${npm_package_name}/dist/${npm_package_name}.min.js ${npm_package_name}.v${npm_package_version%%.*}.min.js && git add ${npm_package_name}.v${npm_package_version%%.*}.js ${npm_package_name}.v${npm_package_version%%.*}.min.js && git commit -m \"${npm_package_name} ${npm_package_version}\" && git push && cd -"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
}
|
||||
1
work/dashboard-frontend/node_modules/d3-path/src/index.js
generated
vendored
Normal file
1
work/dashboard-frontend/node_modules/d3-path/src/index.js
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export {Path, path, pathRound} from "./path.js";
|
||||
156
work/dashboard-frontend/node_modules/d3-path/src/path.js
generated
vendored
Normal file
156
work/dashboard-frontend/node_modules/d3-path/src/path.js
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
const pi = Math.PI,
|
||||
tau = 2 * pi,
|
||||
epsilon = 1e-6,
|
||||
tauEpsilon = tau - epsilon;
|
||||
|
||||
function append(strings) {
|
||||
this._ += strings[0];
|
||||
for (let i = 1, n = strings.length; i < n; ++i) {
|
||||
this._ += arguments[i] + strings[i];
|
||||
}
|
||||
}
|
||||
|
||||
function appendRound(digits) {
|
||||
let d = Math.floor(digits);
|
||||
if (!(d >= 0)) throw new Error(`invalid digits: ${digits}`);
|
||||
if (d > 15) return append;
|
||||
const k = 10 ** d;
|
||||
return function(strings) {
|
||||
this._ += strings[0];
|
||||
for (let i = 1, n = strings.length; i < n; ++i) {
|
||||
this._ += Math.round(arguments[i] * k) / k + strings[i];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export class Path {
|
||||
constructor(digits) {
|
||||
this._x0 = this._y0 = // start of current subpath
|
||||
this._x1 = this._y1 = null; // end of current subpath
|
||||
this._ = "";
|
||||
this._append = digits == null ? append : appendRound(digits);
|
||||
}
|
||||
moveTo(x, y) {
|
||||
this._append`M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}`;
|
||||
}
|
||||
closePath() {
|
||||
if (this._x1 !== null) {
|
||||
this._x1 = this._x0, this._y1 = this._y0;
|
||||
this._append`Z`;
|
||||
}
|
||||
}
|
||||
lineTo(x, y) {
|
||||
this._append`L${this._x1 = +x},${this._y1 = +y}`;
|
||||
}
|
||||
quadraticCurveTo(x1, y1, x, y) {
|
||||
this._append`Q${+x1},${+y1},${this._x1 = +x},${this._y1 = +y}`;
|
||||
}
|
||||
bezierCurveTo(x1, y1, x2, y2, x, y) {
|
||||
this._append`C${+x1},${+y1},${+x2},${+y2},${this._x1 = +x},${this._y1 = +y}`;
|
||||
}
|
||||
arcTo(x1, y1, x2, y2, r) {
|
||||
x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;
|
||||
|
||||
// Is the radius negative? Error.
|
||||
if (r < 0) throw new Error(`negative radius: ${r}`);
|
||||
|
||||
let x0 = this._x1,
|
||||
y0 = this._y1,
|
||||
x21 = x2 - x1,
|
||||
y21 = y2 - y1,
|
||||
x01 = x0 - x1,
|
||||
y01 = y0 - y1,
|
||||
l01_2 = x01 * x01 + y01 * y01;
|
||||
|
||||
// Is this path empty? Move to (x1,y1).
|
||||
if (this._x1 === null) {
|
||||
this._append`M${this._x1 = x1},${this._y1 = y1}`;
|
||||
}
|
||||
|
||||
// Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
|
||||
else if (!(l01_2 > epsilon));
|
||||
|
||||
// Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
|
||||
// Equivalently, is (x1,y1) coincident with (x2,y2)?
|
||||
// Or, is the radius zero? Line to (x1,y1).
|
||||
else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {
|
||||
this._append`L${this._x1 = x1},${this._y1 = y1}`;
|
||||
}
|
||||
|
||||
// Otherwise, draw an arc!
|
||||
else {
|
||||
let x20 = x2 - x0,
|
||||
y20 = y2 - y0,
|
||||
l21_2 = x21 * x21 + y21 * y21,
|
||||
l20_2 = x20 * x20 + y20 * y20,
|
||||
l21 = Math.sqrt(l21_2),
|
||||
l01 = Math.sqrt(l01_2),
|
||||
l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
|
||||
t01 = l / l01,
|
||||
t21 = l / l21;
|
||||
|
||||
// If the start tangent is not coincident with (x0,y0), line to.
|
||||
if (Math.abs(t01 - 1) > epsilon) {
|
||||
this._append`L${x1 + t01 * x01},${y1 + t01 * y01}`;
|
||||
}
|
||||
|
||||
this._append`A${r},${r},0,0,${+(y01 * x20 > x01 * y20)},${this._x1 = x1 + t21 * x21},${this._y1 = y1 + t21 * y21}`;
|
||||
}
|
||||
}
|
||||
arc(x, y, r, a0, a1, ccw) {
|
||||
x = +x, y = +y, r = +r, ccw = !!ccw;
|
||||
|
||||
// Is the radius negative? Error.
|
||||
if (r < 0) throw new Error(`negative radius: ${r}`);
|
||||
|
||||
let dx = r * Math.cos(a0),
|
||||
dy = r * Math.sin(a0),
|
||||
x0 = x + dx,
|
||||
y0 = y + dy,
|
||||
cw = 1 ^ ccw,
|
||||
da = ccw ? a0 - a1 : a1 - a0;
|
||||
|
||||
// Is this path empty? Move to (x0,y0).
|
||||
if (this._x1 === null) {
|
||||
this._append`M${x0},${y0}`;
|
||||
}
|
||||
|
||||
// Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
|
||||
else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {
|
||||
this._append`L${x0},${y0}`;
|
||||
}
|
||||
|
||||
// Is this arc empty? We’re done.
|
||||
if (!r) return;
|
||||
|
||||
// Does the angle go the wrong way? Flip the direction.
|
||||
if (da < 0) da = da % tau + tau;
|
||||
|
||||
// Is this a complete circle? Draw two arcs to complete the circle.
|
||||
if (da > tauEpsilon) {
|
||||
this._append`A${r},${r},0,1,${cw},${x - dx},${y - dy}A${r},${r},0,1,${cw},${this._x1 = x0},${this._y1 = y0}`;
|
||||
}
|
||||
|
||||
// Is this arc non-empty? Draw an arc!
|
||||
else if (da > epsilon) {
|
||||
this._append`A${r},${r},0,${+(da >= pi)},${cw},${this._x1 = x + r * Math.cos(a1)},${this._y1 = y + r * Math.sin(a1)}`;
|
||||
}
|
||||
}
|
||||
rect(x, y, w, h) {
|
||||
this._append`M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}h${w = +w}v${+h}h${-w}Z`;
|
||||
}
|
||||
toString() {
|
||||
return this._;
|
||||
}
|
||||
}
|
||||
|
||||
export function path() {
|
||||
return new Path;
|
||||
}
|
||||
|
||||
// Allow instanceof d3.path
|
||||
path.prototype = Path.prototype;
|
||||
|
||||
export function pathRound(digits = 3) {
|
||||
return new Path(+digits);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue